Skip to content

Commit

Permalink
Implement signal for core plugin entries
Browse files Browse the repository at this point in the history
Signed-off-by: Enis Inan <[email protected]>
  • Loading branch information
ekinanp committed Nov 12, 2019
1 parent 01bbc67 commit 3c939fd
Show file tree
Hide file tree
Showing 6 changed files with 130 additions and 7 deletions.
3 changes: 3 additions & 0 deletions plugin/action.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,9 @@ func SupportedActionsOf(entry Entry) []string {
if _, ok := entry.(Deletable); ok {
actions = append(actions, DeleteAction().Name)
}
if _, ok := entry.(Signalable); ok {
actions = append(actions, SignalAction().Name)
}

return actions
}
Expand Down
42 changes: 37 additions & 5 deletions plugin/aws/ec2Instance.go
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,12 @@ func (inst *ec2Instance) Schema() *plugin.EntrySchema {
return plugin.
NewEntrySchema(inst, "instance").
SetDescription(ec2InstanceDescription).
SetMetaAttributeSchema(ec2InstanceMetadata{})
SetMetaAttributeSchema(ec2InstanceMetadata{}).
AddSignal("start", "Starts the EC2 instance").
AddSignal("stop", "Stops the EC2 instance").
AddSignal("hibernate", "Hibernates the EC2 instance").
AddSignal("restart", "Reboots the EC2 instance").
AddSignal("terminate", "Terminates the EC2 instance")
}

func (inst *ec2Instance) ChildSchemas() []*plugin.EntrySchema {
Expand Down Expand Up @@ -255,10 +260,7 @@ func (inst *ec2Instance) checkLatestConsoleOutput(ctx context.Context) (*ec2Inst
}

func (inst *ec2Instance) Delete(ctx context.Context) (bool, error) {
_, err := inst.client.TerminateInstancesWithContext(ctx, &ec2Client.TerminateInstancesInput{
InstanceIds: awsSDK.StringSlice([]string{inst.id}),
})
return false, err
return false, inst.Signal(ctx, "terminate")
}

func (inst *ec2Instance) Exec(ctx context.Context, cmd string, args []string, opts plugin.ExecOptions) (plugin.ExecCommand, error) {
Expand Down Expand Up @@ -309,6 +311,36 @@ func (inst *ec2Instance) Exec(ctx context.Context, cmd string, args []string, op
return transport.ExecSSH(ctx, transport.Identity{Host: hostname, FallbackUser: fallbackuser, IdentityFile: identityfile}, append([]string{cmd}, args...), opts)
}

func (inst *ec2Instance) Signal(ctx context.Context, signal string) error {
var err error
switch signal {
case "start":
_, err = inst.client.StartInstancesWithContext(ctx, &ec2Client.StartInstancesInput{
InstanceIds: awsSDK.StringSlice([]string{inst.id}),
})
case "stop":
_, err = inst.client.StopInstancesWithContext(ctx, &ec2Client.StopInstancesInput{
InstanceIds: awsSDK.StringSlice([]string{inst.id}),
})
case "hibernate":
_, err = inst.client.StopInstancesWithContext(ctx, &ec2Client.StopInstancesInput{
InstanceIds: awsSDK.StringSlice([]string{inst.id}),
Hibernate: awsSDK.Bool(true),
})
case "restart":
_, err = inst.client.RebootInstancesWithContext(ctx, &ec2Client.RebootInstancesInput{
InstanceIds: awsSDK.StringSlice([]string{inst.id}),
})
case "terminate":
_, err = inst.client.TerminateInstancesWithContext(ctx, &ec2Client.TerminateInstancesInput{
InstanceIds: awsSDK.StringSlice([]string{inst.id}),
})
default:
err = fmt.Errorf("unknown signal %v", signal)
}
return err
}

const ec2InstanceDescription = `
This is an EC2 instance. Its Exec action uses SSH. It will look up port, user,
and other configuration by exact hostname match from default SSH config files.
Expand Down
28 changes: 27 additions & 1 deletion plugin/docker/container.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,13 @@ func (c *container) Schema() *plugin.EntrySchema {
return plugin.
NewEntrySchema(c, "container").
SetMetaAttributeSchema(types.Container{}).
SetMetadataSchema(types.ContainerJSON{})
SetMetadataSchema(types.ContainerJSON{}).
AddSignal("start", "Starts the container. Equivalent to 'docker start <container>'").
AddSignal("stop", "Stops the container. Equivalent to 'docker stop <container>'").
AddSignal("pause", "Suspends all processes in the container. Equivalent to 'docker pause <container>'").
AddSignal("resume", "Un-suspends all processes in the container. Equivalent to 'docker unpause <container>'").
AddSignal("restart", "Restarts the container. Equivalent to 'docker restart <container>'").
AddSignalGroup("linux", `\Asig.+`, "Consists of all the supported Linux signals like SIGHUP, SIGKILL. Equivalent to\n'docker kill <container> --signal <signal>'")
}

func (c *container) ChildSchemas() []*plugin.EntrySchema {
Expand Down Expand Up @@ -161,3 +167,23 @@ func (c *container) Exec(ctx context.Context, cmd string, args []string, opts pl
}()
return execCmd, nil
}

func (c *container) Signal(ctx context.Context, signal string) error {
var err error
switch signal {
case "start":
err = c.client.ContainerStart(ctx, c.id, types.ContainerStartOptions{})
case "stop":
err = c.client.ContainerStop(ctx, c.id, nil)
case "pause":
err = c.client.ContainerPause(ctx, c.id)
case "resume":
err = c.client.ContainerUnpause(ctx, c.id)
case "restart":
err = c.client.ContainerRestart(ctx, c.id, nil)
default:
// linux signal
err = c.client.ContainerKill(ctx, c.id, signal)
}
return err
}
37 changes: 37 additions & 0 deletions plugin/entrySchema.go
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,43 @@ func (s *EntrySchema) IsSingleton() *EntrySchema {
return s
}

// AddSignal adds the given signal to s' supported signals
func (s *EntrySchema) AddSignal(name string, description string) *EntrySchema {
return s.addSignalSchema(name, "", description)

}

// AddSignalGroup adds the given signal group to s' supported signals
func (s *EntrySchema) AddSignalGroup(name string, regex string, description string) *EntrySchema {
if len(regex) <= 0 {
panic("s.AddSignalGroup: received empty regex")
}
return s.addSignalSchema(name, regex, description)
}

func (s *EntrySchema) addSignalSchema(name string, regex string, description string) *EntrySchema {
if len(name) <= 0 {
panic("s.addSignalSchema: received empty name")
}
if len(description) <= 0 {
panic("s.addSignalSchema: received empty description")
}
schema := SignalSchema{
signalSchema: signalSchema{
Name: name,
Regex: regex,
Description: description,
},
}
err := schema.normalize()
if err != nil {
msg := fmt.Sprintf("s.addSignalSchema: received invalid regex: %v", err)
panic(msg)
}
s.Signals = append(s.Signals, schema)
return s
}

// SetMetaAttributeSchema sets the meta attribute's schema. obj is an empty struct
// that will be marshalled into a JSON schema. SetMetaSchema will panic
// if obj is not a struct.
Expand Down
20 changes: 19 additions & 1 deletion plugin/gcp/computeInst.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,10 @@ func (c *computeInstance) Schema() *plugin.EntrySchema {
return plugin.
NewEntrySchema(c, "instance").
SetDescription(computeInstDescription).
SetMetaAttributeSchema(compute.Instance{})
SetMetaAttributeSchema(compute.Instance{}).
AddSignal("start", "Starts the instance").
AddSignal("stop", "Stops the instance").
AddSignal("reset", "Resets the instance, similar to doing a hard-reset on your computer")
}

func (c *computeInstance) ChildSchemas() []*plugin.EntrySchema {
Expand All @@ -81,6 +84,21 @@ func (c *computeInstance) ChildSchemas() []*plugin.EntrySchema {
}
}

func (c *computeInstance) Signal(ctx context.Context, signal string) error {
var err error
switch signal {
case "start":
_, err = c.service.Instances.Start(c.service.projectID, getZone(c.instance), c.Name()).Do()
case "stop":
_, err = c.service.Instances.Stop(c.service.projectID, getZone(c.instance), c.Name()).Do()
case "reset":
_, err = c.service.Instances.Reset(c.service.projectID, getZone(c.instance), c.Name()).Do()
default:
err = fmt.Errorf("unsupported signal %v", signal)
}
return err
}

func (c *computeInstance) Exec(ctx context.Context, cmd string, args []string,
opts plugin.ExecOptions) (plugin.ExecCommand, error) {
conf, err := gceSSHFiles()
Expand Down
7 changes: 7 additions & 0 deletions plugin/signalSchema.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,14 @@ func (s *SignalSchema) UnmarshalJSON(bytes []byte) error {
if err != nil {
return err
}
return s.normalize()
}

// normalizes s by downcasing its name, making its regex case-insensitive,
// and compiling the latter. Returns an error if the regex fails compilation.
func (s *SignalSchema) normalize() error {
s.signalSchema.Name = strings.ToLower(s.signalSchema.Name)
var err error
if len(s.signalSchema.Regex) > 0 {
// (?i) tells Go that the regex is case-insensitive
s.signalSchema.Regex = "(?i)" + s.signalSchema.Regex
Expand Down

0 comments on commit 3c939fd

Please sign in to comment.