mirror of
https://github.com/weaveworks/scope.git
synced 2026-07-28 09:41:57 +00:00
@@ -20,6 +20,7 @@ const (
|
||||
RemoveContainer = "docker_remove_container"
|
||||
AttachContainer = "docker_attach_container"
|
||||
ExecContainer = "docker_exec_container"
|
||||
ResizeExecTTY = "docker_resize_exec_tty"
|
||||
|
||||
waitTime = 10
|
||||
)
|
||||
@@ -122,6 +123,7 @@ func (r *registry) execContainer(containerID string, req xfer.Request) xfer.Resp
|
||||
if err != nil {
|
||||
return xfer.ResponseError(err)
|
||||
}
|
||||
|
||||
local, _ := pipe.Ends()
|
||||
cw, err := r.client.StartExecNonBlocking(exec.ID, docker_client.StartExecOptions{
|
||||
Tty: true,
|
||||
@@ -133,11 +135,19 @@ func (r *registry) execContainer(containerID string, req xfer.Request) xfer.Resp
|
||||
if err != nil {
|
||||
return xfer.ResponseError(err)
|
||||
}
|
||||
|
||||
r.Lock()
|
||||
r.pipeIDToexecID[id] = exec.ID
|
||||
r.Unlock()
|
||||
|
||||
pipe.OnClose(func() {
|
||||
if err := cw.Close(); err != nil {
|
||||
log.Errorf("Error closing exec in container %s: %v", containerID, err)
|
||||
return
|
||||
}
|
||||
r.Lock()
|
||||
delete(r.pipeIDToexecID, id)
|
||||
r.Unlock()
|
||||
})
|
||||
go func() {
|
||||
if err := cw.Wait(); err != nil {
|
||||
@@ -146,11 +156,30 @@ func (r *registry) execContainer(containerID string, req xfer.Request) xfer.Resp
|
||||
pipe.Close()
|
||||
}()
|
||||
return xfer.Response{
|
||||
Pipe: id,
|
||||
RawTTY: true,
|
||||
Pipe: id,
|
||||
RawTTY: true,
|
||||
ResizeTTYControl: ResizeExecTTY,
|
||||
}
|
||||
}
|
||||
|
||||
func (r *registry) resizeExecTTY(pipeID string, height, width uint) xfer.Response {
|
||||
r.Lock()
|
||||
execID, ok := r.pipeIDToexecID[pipeID]
|
||||
r.Unlock()
|
||||
|
||||
if !ok {
|
||||
return xfer.ResponseErrorf("Unknown pipeID (%q)", pipeID)
|
||||
}
|
||||
|
||||
if err := r.client.ResizeExecTTY(execID, int(height), int(width)); err != nil {
|
||||
return xfer.ResponseErrorf(
|
||||
"Error setting terminal size (%d, %d) of pipe %s: %v",
|
||||
height, width, pipeID, err)
|
||||
}
|
||||
|
||||
return xfer.Response{}
|
||||
}
|
||||
|
||||
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)
|
||||
@@ -171,6 +200,7 @@ func (r *registry) registerControls() {
|
||||
RemoveContainer: captureContainerID(r.removeContainer),
|
||||
AttachContainer: captureContainerID(r.attachContainer),
|
||||
ExecContainer: captureContainerID(r.execContainer),
|
||||
ResizeExecTTY: xfer.ResizeTTYControlWrapper(r.resizeExecTTY),
|
||||
}
|
||||
r.handlerRegistry.Batch(nil, controls)
|
||||
}
|
||||
@@ -185,6 +215,7 @@ func (r *registry) deregisterControls() {
|
||||
RemoveContainer,
|
||||
AttachContainer,
|
||||
ExecContainer,
|
||||
ResizeExecTTY,
|
||||
}
|
||||
r.handlerRegistry.Batch(controls, nil)
|
||||
}
|
||||
|
||||
@@ -66,20 +66,33 @@ func TestPipes(t *testing.T) {
|
||||
return ok
|
||||
})
|
||||
|
||||
for _, tc := range []string{
|
||||
docker.AttachContainer,
|
||||
docker.ExecContainer,
|
||||
for _, want := range []struct {
|
||||
control string
|
||||
response xfer.Response
|
||||
}{
|
||||
{
|
||||
control: docker.AttachContainer,
|
||||
response: xfer.Response{
|
||||
Pipe: "pipeid",
|
||||
RawTTY: true,
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
control: docker.ExecContainer,
|
||||
response: xfer.Response{
|
||||
Pipe: "pipeid",
|
||||
RawTTY: true,
|
||||
ResizeTTYControl: docker.ResizeExecTTY,
|
||||
},
|
||||
},
|
||||
} {
|
||||
result := hr.HandleControlRequest(xfer.Request{
|
||||
Control: tc,
|
||||
Control: want.control,
|
||||
NodeID: report.MakeContainerNodeID("ping"),
|
||||
})
|
||||
want := xfer.Response{
|
||||
Pipe: "pipeid",
|
||||
RawTTY: true,
|
||||
}
|
||||
if !reflect.DeepEqual(result, want) {
|
||||
t.Errorf("diff %s: %s", tc, test.Diff(want, result))
|
||||
if !reflect.DeepEqual(result, want.response) {
|
||||
t.Errorf("diff %s: %s", want.control, test.Diff(want, result))
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
@@ -65,6 +65,7 @@ type registry struct {
|
||||
containersByPID map[int]Container
|
||||
images map[string]docker_client.APIImages
|
||||
networks []docker_client.Network
|
||||
pipeIDToexecID map[string]string
|
||||
}
|
||||
|
||||
// Client interface for mocking.
|
||||
@@ -86,6 +87,7 @@ type Client interface {
|
||||
CreateExec(docker_client.CreateExecOptions) (*docker_client.Exec, error)
|
||||
StartExecNonBlocking(string, docker_client.StartExecOptions) (docker_client.CloseWaiter, error)
|
||||
Stats(docker_client.StatsOptions) error
|
||||
ResizeExecTTY(id string, height, width int) error
|
||||
}
|
||||
|
||||
func newDockerClient(endpoint string) (Client, error) {
|
||||
@@ -103,6 +105,7 @@ func NewRegistry(interval time.Duration, pipes controls.PipeClient, collectStats
|
||||
containers: radix.New(),
|
||||
containersByPID: map[int]Container{},
|
||||
images: map[string]docker_client.APIImages{},
|
||||
pipeIDToexecID: map[string]string{},
|
||||
|
||||
client: client,
|
||||
pipes: pipes,
|
||||
|
||||
@@ -167,6 +167,10 @@ func (m *mockDockerClient) Stats(_ client.StatsOptions) error {
|
||||
return fmt.Errorf("stats")
|
||||
}
|
||||
|
||||
func (m *mockDockerClient) ResizeExecTTY(id string, height, width int) error {
|
||||
return fmt.Errorf("resizeExecTTY")
|
||||
}
|
||||
|
||||
type mockCloseWaiter struct{}
|
||||
|
||||
func (mockCloseWaiter) Close() error { return nil }
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"os/exec"
|
||||
|
||||
log "github.com/Sirupsen/logrus"
|
||||
"github.com/docker/docker/pkg/term"
|
||||
"github.com/kr/pty"
|
||||
|
||||
"github.com/weaveworks/scope/common/xfer"
|
||||
@@ -12,15 +13,18 @@ import (
|
||||
|
||||
// Control IDs used by the host integration.
|
||||
const (
|
||||
ExecHost = "host_exec"
|
||||
ExecHost = "host_exec"
|
||||
ResizeExecTTY = "host_resize_exec_tty"
|
||||
)
|
||||
|
||||
func (r *Reporter) registerControls() {
|
||||
r.handlerRegistry.Register(ExecHost, r.execHost)
|
||||
r.handlerRegistry.Register(ResizeExecTTY, xfer.ResizeTTYControlWrapper(r.resizeExecTTY))
|
||||
}
|
||||
|
||||
func (r *Reporter) deregisterControls() {
|
||||
r.handlerRegistry.Rm(ExecHost)
|
||||
r.handlerRegistry.Rm(ResizeExecTTY)
|
||||
}
|
||||
|
||||
func (r *Reporter) execHost(req xfer.Request) xfer.Response {
|
||||
@@ -35,6 +39,11 @@ func (r *Reporter) execHost(req xfer.Request) xfer.Response {
|
||||
if err != nil {
|
||||
return xfer.ResponseError(err)
|
||||
}
|
||||
|
||||
r.Lock()
|
||||
r.pipeIDToTTY[id] = ptyPipe.Fd()
|
||||
r.Unlock()
|
||||
|
||||
pipe.OnClose(func() {
|
||||
if err := cmd.Process.Kill(); err != nil {
|
||||
log.Errorf("Error stopping host shell: %v", err)
|
||||
@@ -42,6 +51,9 @@ func (r *Reporter) execHost(req xfer.Request) xfer.Response {
|
||||
if err := ptyPipe.Close(); err != nil {
|
||||
log.Errorf("Error closing host shell's pty: %v", err)
|
||||
}
|
||||
r.Lock()
|
||||
delete(r.pipeIDToTTY, id)
|
||||
r.Unlock()
|
||||
log.Info("Host shell closed.")
|
||||
})
|
||||
go func() {
|
||||
@@ -52,7 +64,32 @@ func (r *Reporter) execHost(req xfer.Request) xfer.Response {
|
||||
}()
|
||||
|
||||
return xfer.Response{
|
||||
Pipe: id,
|
||||
RawTTY: true,
|
||||
Pipe: id,
|
||||
RawTTY: true,
|
||||
ResizeTTYControl: ResizeExecTTY,
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Reporter) resizeExecTTY(pipeID string, height, width uint) xfer.Response {
|
||||
r.Lock()
|
||||
fd, ok := r.pipeIDToTTY[pipeID]
|
||||
r.Unlock()
|
||||
|
||||
if !ok {
|
||||
return xfer.ResponseErrorf("Unknown pipeID (%q)", pipeID)
|
||||
}
|
||||
|
||||
size := term.Winsize{
|
||||
Height: uint16(height),
|
||||
Width: uint16(width),
|
||||
}
|
||||
|
||||
if err := term.SetWinsize(fd, &size); err != nil {
|
||||
return xfer.ResponseErrorf(
|
||||
"Error setting terminal size (%d, %d) of pipe %s: %v",
|
||||
height, width, pipeID, err)
|
||||
}
|
||||
|
||||
return xfer.Response{}
|
||||
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package host
|
||||
import (
|
||||
"net"
|
||||
"runtime"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/weaveworks/scope/common/mtime"
|
||||
@@ -52,6 +53,7 @@ var (
|
||||
|
||||
// Reporter generates Reports containing the host topology.
|
||||
type Reporter struct {
|
||||
sync.RWMutex
|
||||
hostID string
|
||||
hostName string
|
||||
probeID string
|
||||
@@ -59,6 +61,7 @@ type Reporter struct {
|
||||
pipes controls.PipeClient
|
||||
hostShellCmd []string
|
||||
handlerRegistry *controls.HandlerRegistry
|
||||
pipeIDToTTY map[string]uintptr
|
||||
}
|
||||
|
||||
// NewReporter returns a Reporter which produces a report containing host
|
||||
@@ -72,13 +75,14 @@ func NewReporter(hostID, hostName, probeID, version string, pipes controls.PipeC
|
||||
version: version,
|
||||
hostShellCmd: getHostShellCmd(),
|
||||
handlerRegistry: handlerRegistry,
|
||||
pipeIDToTTY: map[string]uintptr{},
|
||||
}
|
||||
r.registerControls()
|
||||
return r
|
||||
}
|
||||
|
||||
// Name of this reporter, for metrics gathering
|
||||
func (Reporter) Name() string { return "Host" }
|
||||
func (*Reporter) Name() string { return "Host" }
|
||||
|
||||
// GetLocalNetworks is exported for mocking
|
||||
var GetLocalNetworks = func() ([]*net.IPNet, error) {
|
||||
|
||||
Reference in New Issue
Block a user