mirror of
https://github.com/weaveworks/scope.git
synced 2026-03-06 03:31:00 +00:00
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.
191 lines
5.4 KiB
Go
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)
|
|
}
|