Skip to content

Commit

Permalink
Add container signal delivery
Browse files Browse the repository at this point in the history
Port layer now uses the vSphere API to send the "kill" command to the
container VM guest toolbox.

Closes vmware#1267
  • Loading branch information
dougm committed Aug 3, 2016
1 parent 63d1aec commit 7da75a3
Show file tree
Hide file tree
Showing 7 changed files with 164 additions and 5 deletions.
10 changes: 10 additions & 0 deletions lib/apiservers/portlayer/restapi/handlers/containers_handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,16 @@ func (handler *ContainersHandlersImpl) GetContainerListHandler(params containers
func (handler *ContainersHandlersImpl) ContainerSignalHandler(params containers.ContainerSignalParams) middleware.Responder {
defer trace.End(trace.Begin("Containers.ContainerSignal"))

h := exec.GetContainer(exec.ParseID(params.ID))
if h == nil {
return containers.NewContainerSignalNotFound().WithPayload(&models.Error{Message: fmt.Sprintf("container %s not found", params.ID)})
}

err := h.Container.Signal(context.Background(), params.Signal)
if err != nil {
return containers.NewContainerSignalInternalServerError().WithPayload(&models.Error{Message: err.Error()})
}

return containers.NewContainerSignalOK()
}

Expand Down
8 changes: 8 additions & 0 deletions lib/apiservers/portlayer/swagger.yml
Original file line number Diff line number Diff line change
Expand Up @@ -842,6 +842,14 @@ paths:
format: int64
required: true
responses:
'500':
description: "Failed to signal container"
schema:
$ref: "#/definitions/Error"
'404':
description: "Container not found"
schema:
$ref: "#/definitions/Error"
'200':
description: "OK"
/containers/{id}/logs:
Expand Down
32 changes: 32 additions & 0 deletions lib/portlayer/exec/container.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
"time"

log "github.com/Sirupsen/logrus"
"github.com/vmware/govmomi/guest"
"github.com/vmware/govmomi/object"
"github.com/vmware/govmomi/vim25/mo"
"github.com/vmware/govmomi/vim25/types"
Expand Down Expand Up @@ -240,6 +241,37 @@ func (c *Container) stop(ctx context.Context) error {
return c.vm.WaitForPowerState(ctx, types.VirtualMachinePowerStatePoweredOff)
}

func (c *Container) startGuestProgram(ctx context.Context, name string, args string) error {
o := guest.NewOperationsManager(c.vm.Client.Client, c.vm.Reference())
m, err := o.ProcessManager(ctx)
if err != nil {
return err
}

spec := types.GuestProgramSpec{
ProgramPath: name,
Arguments: args,
}

auth := types.NamePasswordAuthentication{
Username: c.ExecConfig.ID,
}

_, err = m.StartProgram(ctx, &auth, &spec)

return err
}

func (c *Container) Signal(ctx context.Context, num int64) error {
defer trace.End(trace.Begin("Container.Signal"))

if c.vm == nil {
return fmt.Errorf("vm not set")
}

return c.startGuestProgram(ctx, "kill", fmt.Sprintf("%d", num))
}

type RemovePowerError struct {
err error
}
Expand Down
27 changes: 27 additions & 0 deletions lib/tether/toolbox.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package tether

import (
"errors"
"fmt"
"net"
"syscall"
Expand Down Expand Up @@ -70,6 +71,10 @@ func (t *Toolbox) Reload(config *ExecutorConfig) error {
func (t *Toolbox) InContainer() *Toolbox {
t.PowerCommand.Halt.Handler = t.halt

vix := t.Service.VixCommand
vix.Authenticate = t.containerAuthenticate
vix.ProcessStartCommand = t.containerStartCommand

return t
}

Expand Down Expand Up @@ -97,6 +102,28 @@ func (t *Toolbox) kill(name string) error {
return nil
}

func (t *Toolbox) containerAuthenticate(_ toolbox.VixCommandRequestHeader, data []byte) error {
var c toolbox.VixUserCredentialNamePassword
if err := c.UnmarshalBinary(data); err != nil {
return err
}
// no authentication yet, just using container ID as a sanity check for now
if c.Name != t.config.ID {
return errors.New("failed to verify container ID")
}

return nil
}

func (t *Toolbox) containerStartCommand(r *toolbox.VixMsgStartProgramRequest) (int, error) {
switch r.ProgramPath {
case "kill":
return -1, t.kill(r.Arguments)
default:
return -1, fmt.Errorf("unknown command %q", r.ProgramPath)
}
}

func (t *Toolbox) halt() error {
session := t.config.Sessions[t.config.ID]
log.Infof("stopping %s", session.ID)
Expand Down
10 changes: 5 additions & 5 deletions tests/test-cases/Group1-Docker-Commands/1-14-Docker-Kill.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,23 +12,23 @@ This test requires that a vSphere server is running and available

#Test Steps:
1. Deploy VIC appliance to vSphere server
2. Issue docker create busybox sleep 30 to the VIC appliance
2. Issue docker create busybox sleep 300 to the VIC appliance
3. Issue docker start <containerID> to the VIC appliance
4. Issue docker kill <containerID> to the VIC appliance
5. Issue docker start <containerID> to the VIC appliance
6. Issue docker kill -s 2 <containerID> to the VIC appliance
7. Issue docker kill -s 9 <containerID> to the VIC appliance
6. Issue docker kill -s HUP <containerID> to the VIC appliance
7. Issue docker kill -s TERM <containerID> to the VIC appliance
8. Issue docker kill fakeContainer to the VIC appliance

#Expected Outcome:
* Steps 2-7 should all return without error and provide the container ID in the response
* Step 4 should result in the container stopping immediately
* Step 6 should result in the container continuing to run
* Step 7 should result in the container stopping immediately
* Step 8 should result in an error and the following message:
* Step 8 should result in an error and the following message:
```
Failed to kill container (fakeContainer): Error response from daemon: Cannot kill container fakeContainer: No such container: fakeContainer
```

#Possible Problems:
None
None
66 changes: 66 additions & 0 deletions tests/test-cases/Group1-Docker-Commands/1-14-Docker-Kill.robot
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
*** Settings ***
Documentation Test 1-14 - Docker Kill
Resource ../../resources/Util.robot
Suite Setup Install VIC Appliance To Test Server
Suite Teardown Cleanup VIC Appliance On Test Server

*** Keywords ***
Trap Signal Command
# Container command runs an infinite loop, trapping and logging the given signal name
[Arguments] ${sig}
[Return] busybox sh -c "trap 'echo KillSignal${sig}' ${sig}; while true; do date && sleep 1; done"

Assert Kill Signal
# Assert the docker kill signal was trapped by checking the container output log file
[Arguments] ${id} ${sig}
${rc}= Run And Return Rc govc datastore.download ${id}/${id}.log ${TEMPDIR}/${id}.log
Should Be Equal As Integers ${rc} 0
${output}= OperatingSystem.Get File ${TEMPDIR}/${id}.log
Remove File ${TEMPDIR}/${id}.log
Should Contain ${output} KillSignal${sig}

Inspect State Running
[Arguments] ${id} ${expected}
${rc} ${state}= Run And Return Rc And Output docker ${params} inspect --format="{{ .State.Running }}" ${id}
Should Be Equal As Integers ${rc} 0
Should Be Equal ${state} ${expected}

*** Test Cases ***
Signal a container with default kill signal
${rc}= Run And Return Rc docker ${params} pull busybox
Should Be Equal As Integers ${rc} 0
${rc} ${id}= Run And Return Rc And Output docker ${params} create busybox sleep 300
Should Be Equal As Integers ${rc} 0
${rc}= Run And Return Rc docker ${params} start ${id}
Should Be Equal As Integers ${rc} 0
Inspect State Running ${id} true
${rc}= Run And Return Rc docker ${params} kill ${id}
Should Be Equal As Integers ${rc} 0
# Wait for container VM to stop/powerOff
Wait Until Keyword Succeeds 5x 200 milliseconds Inspect State Running ${id} false
# Cannot send signal to a powered off container VM
${rc} ${output}= Run And Return Rc And Output docker ${params} kill ${id}
Should Be Equal As Integers ${rc} 1
Should Contain ${output} Cannot kill container ${id}

Signal a container with SIGHUP
${rc}= Run And Return Rc docker ${params} pull busybox
Should Be Equal As Integers ${rc} 0
${trap}= Trap Signal Command HUP
${rc} ${id}= Run And Return Rc And Output docker ${params} run -d ${trap}
Should Be Equal As Integers ${rc} 0
# Expect failure with unknown signal name
${rc}= Run And Return Rc docker ${params} kill -s NOPE ${id}
Should Be Equal As Integers ${rc} 1
${rc}= Run And Return Rc docker ${params} kill -s HUP ${id}
Should Be Equal As Integers ${rc} 0
Wait Until Keyword Succeeds 5x 1 seconds Assert Kill Signal ${id} HUP
Inspect State Running ${id} true
${rc}= Run And Return Rc docker ${params} kill -s TERM ${id}
Should Be Equal As Integers ${rc} 0
Wait Until Keyword Succeeds 5x 200 milliseconds Inspect State Running ${id} false

Signal a non-existent container
${rc} ${output}= Run And Return Rc And Output docker ${params} kill fakeContainer
Should Be Equal As Integers ${rc} 1
Should Contain ${output} No such container: fakeContainer
16 changes: 16 additions & 0 deletions tests/test-cases/Group8-vSphere-Integration/8-1-GuestTools.robot
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,19 @@ Stop container VM using guest shutdown
Run govc vm.ip ${id}
${rc}= Run And Return Rc govc vm.power -s ${id}
Should Be Equal As Integers ${rc} 0

Signal container VM using vix command
${rc}= Run And Return Rc docker ${params} pull busybox
Should Be Equal As Integers ${rc} 0
${rc} ${id}= Run And Return Rc And Output docker ${params} run -d busybox /bin/top
Should Be Equal As Integers ${rc} 0
Run govc vm.ip ${id}
# Invalid command
${rc}= Run And Return Rc govc guest.start -vm ${id} -l ${id} hello world
Should Be Equal As Integers ${rc} 1
# Invalid id (via auth user)
${rc}= Run And Return Rc govc guest.start -vm ${id} kill USR1
Should Be Equal As Integers ${rc} 1
# OK
${rc}= Run And Return Rc govc guest.start -vm ${id} -l ${id} kill USR1
Should Be Equal As Integers ${rc} 0

0 comments on commit 7da75a3

Please sign in to comment.