Files
weave-scope/probe/docker/controls.go
Krzesimir Nowak 41193b428e Make control handlers registry an object and extend its functionality
It is not a singleton anymore. Instead it is an object with a registry
backend. The default registry backend is provided, which is equivalent
to what used to be before. Custom backend can be provided for testing
purposes.

The registry also supports batch operations to remove and add handlers
as an atomic step.
2016-08-12 17:03:42 +02:00

191 lines
5.4 KiB
Go

package docker
import (
docker_client "github.com/fsouza/go-dockerclient"
log "github.com/Sirupsen/logrus"
"github.com/weaveworks/scope/common/xfer"
"github.com/weaveworks/scope/probe/controls"
"github.com/weaveworks/scope/report"
)
// Control IDs used by the docker integration.
const (
StopContainer = "docker_stop_container"
StartContainer = "docker_start_container"
RestartContainer = "docker_restart_container"
PauseContainer = "docker_pause_container"
UnpauseContainer = "docker_unpause_container"
RemoveContainer = "docker_remove_container"
AttachContainer = "docker_attach_container"
ExecContainer = "docker_exec_container"
waitTime = 10
)
func (r *registry) stopContainer(containerID string, _ xfer.Request) xfer.Response {
log.Infof("Stopping container %s", containerID)
return xfer.ResponseError(r.client.StopContainer(containerID, waitTime))
}
func (r *registry) startContainer(containerID string, _ xfer.Request) xfer.Response {
log.Infof("Starting container %s", containerID)
return xfer.ResponseError(r.client.StartContainer(containerID, nil))
}
func (r *registry) restartContainer(containerID string, _ xfer.Request) xfer.Response {
log.Infof("Restarting container %s", containerID)
return xfer.ResponseError(r.client.RestartContainer(containerID, waitTime))
}
func (r *registry) pauseContainer(containerID string, _ xfer.Request) xfer.Response {
log.Infof("Pausing container %s", containerID)
return xfer.ResponseError(r.client.PauseContainer(containerID))
}
func (r *registry) unpauseContainer(containerID string, _ xfer.Request) xfer.Response {
log.Infof("Unpausing container %s", containerID)
return xfer.ResponseError(r.client.UnpauseContainer(containerID))
}
func (r *registry) removeContainer(containerID string, req xfer.Request) xfer.Response {
log.Infof("Removing container %s", containerID)
if err := r.client.RemoveContainer(docker_client.RemoveContainerOptions{
ID: containerID,
}); err != nil {
return xfer.ResponseError(err)
}
return xfer.Response{
RemovedNode: req.NodeID,
}
}
func (r *registry) attachContainer(containerID string, req xfer.Request) xfer.Response {
c, ok := r.GetContainer(containerID)
if !ok {
return xfer.ResponseErrorf("Not found: %s", containerID)
}
hasTTY := c.HasTTY()
id, pipe, err := controls.NewPipe(r.pipes, req.AppID)
if err != nil {
return xfer.ResponseError(err)
}
local, _ := pipe.Ends()
cw, err := r.client.AttachToContainerNonBlocking(docker_client.AttachToContainerOptions{
Container: containerID,
RawTerminal: hasTTY,
Stream: true,
Stdin: true,
Stdout: true,
Stderr: true,
InputStream: local,
OutputStream: local,
ErrorStream: local,
})
if err != nil {
return xfer.ResponseError(err)
}
pipe.OnClose(func() {
if err := cw.Close(); err != nil {
log.Errorf("Error closing attachment to container %s: %v", containerID, err)
return
}
})
go func() {
if err := cw.Wait(); err != nil {
log.Errorf("Error waiting on attachment to container %s: %v", containerID, err)
}
pipe.Close()
}()
return xfer.Response{
Pipe: id,
RawTTY: hasTTY,
}
}
func (r *registry) execContainer(containerID string, req xfer.Request) xfer.Response {
exec, err := r.client.CreateExec(docker_client.CreateExecOptions{
AttachStdin: true,
AttachStdout: true,
AttachStderr: true,
Tty: true,
Cmd: []string{"/bin/sh", "-c", "TERM=xterm exec $( (type getent > /dev/null 2>&1 && getent passwd root | cut -d: -f7 2>/dev/null) || echo /bin/sh)"},
Container: containerID,
})
if err != nil {
return xfer.ResponseError(err)
}
id, pipe, err := controls.NewPipe(r.pipes, req.AppID)
if err != nil {
return xfer.ResponseError(err)
}
local, _ := pipe.Ends()
cw, err := r.client.StartExecNonBlocking(exec.ID, docker_client.StartExecOptions{
Tty: true,
RawTerminal: true,
InputStream: local,
OutputStream: local,
ErrorStream: local,
})
if err != nil {
return xfer.ResponseError(err)
}
pipe.OnClose(func() {
if err := cw.Close(); err != nil {
log.Errorf("Error closing exec in container %s: %v", containerID, err)
return
}
})
go func() {
if err := cw.Wait(); err != nil {
log.Errorf("Error waiting on exec in container %s: %v", containerID, err)
}
pipe.Close()
}()
return xfer.Response{
Pipe: id,
RawTTY: true,
}
}
func captureContainerID(f func(string, xfer.Request) xfer.Response) func(xfer.Request) xfer.Response {
return func(req xfer.Request) xfer.Response {
containerID, ok := report.ParseContainerNodeID(req.NodeID)
if !ok {
return xfer.ResponseErrorf("Invalid ID: %s", req.NodeID)
}
return f(containerID, req)
}
}
func (r *registry) registerControls() {
controls := map[string]xfer.ControlHandlerFunc{
StopContainer: captureContainerID(r.stopContainer),
StartContainer: captureContainerID(r.startContainer),
RestartContainer: captureContainerID(r.restartContainer),
PauseContainer: captureContainerID(r.pauseContainer),
UnpauseContainer: captureContainerID(r.unpauseContainer),
RemoveContainer: captureContainerID(r.removeContainer),
AttachContainer: captureContainerID(r.attachContainer),
ExecContainer: captureContainerID(r.execContainer),
}
r.handlerRegistry.Batch(nil, controls)
}
func (r *registry) deregisterControls() {
controls := []string{
StopContainer,
StartContainer,
RestartContainer,
PauseContainer,
UnpauseContainer,
RemoveContainer,
AttachContainer,
ExecContainer,
}
r.handlerRegistry.Batch(controls, nil)
}