Move container util methods to the container package.

Signed-off-by: Daniel Nephin <dnephin@docker.com>
This commit is contained in:
Daniel Nephin 2016-08-29 12:23:15 -04:00
parent bec81075bf
commit 31d8c27e70
6 changed files with 136 additions and 130 deletions

View File

@ -46,8 +46,9 @@ func NewAttachCommand(dockerCli *client.DockerCli) *cobra.Command {
func runAttach(dockerCli *client.DockerCli, opts *attachOptions) error {
ctx := context.Background()
client := dockerCli.Client()
c, err := dockerCli.Client().ContainerInspect(ctx, opts.container)
c, err := client.ContainerInspect(ctx, opts.container)
if err != nil {
return err
}
@ -82,11 +83,11 @@ func runAttach(dockerCli *client.DockerCli, opts *attachOptions) error {
}
if opts.proxy && !c.Config.Tty {
sigc := dockerCli.ForwardAllSignals(ctx, opts.container)
sigc := ForwardAllSignals(ctx, dockerCli, opts.container)
defer signal.StopCatch(sigc)
}
resp, errAttach := dockerCli.Client().ContainerAttach(ctx, opts.container, options)
resp, errAttach := client.ContainerAttach(ctx, opts.container, options)
if errAttach != nil && errAttach != httputil.ErrPersistEOF {
// ContainerAttach returns an ErrPersistEOF (connection closed)
// means server met an error and put it in Hijacked connection
@ -101,11 +102,11 @@ func runAttach(dockerCli *client.DockerCli, opts *attachOptions) error {
// terminal, the only way to get the shell prompt to display for attaches 2+ is to artificially
// resize it, then go back to normal. Without this, every attach after the first will
// require the user to manually resize or hit enter.
dockerCli.ResizeTtyTo(ctx, opts.container, height+1, width+1, false)
resizeTtyTo(ctx, client, opts.container, height+1, width+1, false)
// After the above resizing occurs, the call to MonitorTtySize below will handle resetting back
// to the actual size.
if err := dockerCli.MonitorTtySize(ctx, opts.container, false); err != nil {
if err := MonitorTtySize(ctx, dockerCli, opts.container, false); err != nil {
logrus.Debugf("Error monitoring TTY size: %s", err)
}
}

View File

@ -11,6 +11,8 @@ import (
"github.com/docker/docker/api/types"
"github.com/docker/docker/cli"
"github.com/docker/docker/pkg/promise"
apiclient "github.com/docker/docker/client"
"github.com/docker/engine-api/types"
"github.com/spf13/cobra"
)
@ -66,8 +68,9 @@ func runExec(dockerCli *client.DockerCli, opts *execOptions, container string, e
execConfig.DetachKeys = dockerCli.ConfigFile().DetachKeys
ctx := context.Background()
client := dockerCli.Client()
response, err := dockerCli.Client().ContainerExecCreate(ctx, container, *execConfig)
response, err := client.ContainerExecCreate(ctx, container, *execConfig)
if err != nil {
return err
}
@ -89,7 +92,7 @@ func runExec(dockerCli *client.DockerCli, opts *execOptions, container string, e
Tty: execConfig.Tty,
}
if err := dockerCli.Client().ContainerExecStart(ctx, execID, execStartCheck); err != nil {
if err := client.ContainerExecStart(ctx, execID, execStartCheck); err != nil {
return err
}
// For now don't print this - wait for when we support exec wait()
@ -118,7 +121,7 @@ func runExec(dockerCli *client.DockerCli, opts *execOptions, container string, e
}
}
resp, err := dockerCli.Client().ContainerExecAttach(ctx, execID, *execConfig)
resp, err := client.ContainerExecAttach(ctx, execID, *execConfig)
if err != nil {
return err
}
@ -128,7 +131,7 @@ func runExec(dockerCli *client.DockerCli, opts *execOptions, container string, e
})
if execConfig.Tty && dockerCli.In().IsTerminal() {
if err := dockerCli.MonitorTtySize(ctx, execID, true); err != nil {
if err := MonitorTtySize(ctx, dockerCli, execID, true); err != nil {
fmt.Fprintf(dockerCli.Err(), "Error monitoring TTY size: %s\n", err)
}
}
@ -139,7 +142,7 @@ func runExec(dockerCli *client.DockerCli, opts *execOptions, container string, e
}
var status int
if _, status, err = dockerCli.GetExecExitCode(ctx, execID); err != nil {
if _, status, err = getExecExitCode(ctx, client, execID); err != nil {
return err
}
@ -150,6 +153,21 @@ func runExec(dockerCli *client.DockerCli, opts *execOptions, container string, e
return nil
}
// getExecExitCode perform an inspect on the exec command. It returns
// the running state and the exit code.
func getExecExitCode(ctx context.Context, client apiclient.ContainerAPIClient, execID string) (bool, int, error) {
resp, err := client.ContainerExecInspect(ctx, execID)
if err != nil {
// If we can't connect, then the daemon probably died.
if err != apiclient.ErrConnectionFailed {
return false, -1, err
}
return false, -1, nil
}
return resp.Running, resp.ExitCode, nil
}
// parseExec parses the specified args for the specified command and generates
// an ExecConfig from it.
func parseExec(opts *execOptions, container string, execCmd []string) (*types.ExecConfig, error) {

View File

@ -146,7 +146,7 @@ func runRun(dockerCli *client.DockerCli, flags *pflag.FlagSet, opts *runOptions,
return runStartContainerErr(err)
}
if opts.sigProxy {
sigc := dockerCli.ForwardAllSignals(ctx, createResponse.ID)
sigc := ForwardAllSignals(ctx, dockerCli, createResponse.ID)
defer signal.StopCatch(sigc)
}
var (
@ -235,7 +235,7 @@ func runRun(dockerCli *client.DockerCli, flags *pflag.FlagSet, opts *runOptions,
}
if (config.AttachStdin || config.AttachStdout || config.AttachStderr) && config.Tty && dockerCli.Out().IsTerminal() {
if err := dockerCli.MonitorTtySize(ctx, createResponse.ID, false); err != nil {
if err := MonitorTtySize(ctx, dockerCli, createResponse.ID, false); err != nil {
fmt.Fprintf(stderr, "Error monitoring TTY size: %s\n", err)
}
}

View File

@ -64,7 +64,7 @@ func runStart(dockerCli *client.DockerCli, opts *startOptions) error {
// We always use c.ID instead of container to maintain consistency during `docker start`
if !c.Config.Tty {
sigc := dockerCli.ForwardAllSignals(ctx, c.ID)
sigc := ForwardAllSignals(ctx, dockerCli, c.ID)
defer signal.StopCatch(sigc)
}
@ -119,7 +119,7 @@ func runStart(dockerCli *client.DockerCli, opts *startOptions) error {
// 5. Wait for attachment to break.
if c.Config.Tty && dockerCli.Out().IsTerminal() {
if err := dockerCli.MonitorTtySize(ctx, c.ID, false); err != nil {
if err := MonitorTtySize(ctx, dockerCli, c.ID, false); err != nil {
fmt.Fprintf(dockerCli.Err(), "Error monitoring TTY size: %s\n", err)
}
}

103
api/client/container/tty.go Normal file
View File

@ -0,0 +1,103 @@
package container
import (
"fmt"
"os"
gosignal "os/signal"
"runtime"
"time"
"github.com/Sirupsen/logrus"
"github.com/docker/docker/api/client"
"github.com/docker/docker/pkg/signal"
apiclient "github.com/docker/engine-api/client"
"github.com/docker/engine-api/types"
"golang.org/x/net/context"
)
// ResizeTtyTo resizes tty to specific height and width
func resizeTtyTo(ctx context.Context, client apiclient.ContainerAPIClient, id string, height, width int, isExec bool) {
if height == 0 && width == 0 {
return
}
options := types.ResizeOptions{
Height: height,
Width: width,
}
var err error
if isExec {
err = client.ContainerExecResize(ctx, id, options)
} else {
err = client.ContainerResize(ctx, id, options)
}
if err != nil {
logrus.Debugf("Error resize: %s", err)
}
}
// MonitorTtySize updates the container tty size when the terminal tty changes size
func MonitorTtySize(ctx context.Context, cli *client.DockerCli, id string, isExec bool) error {
resizeTty := func() {
height, width := cli.Out().GetTtySize()
resizeTtyTo(ctx, cli.Client(), id, height, width, isExec)
}
resizeTty()
if runtime.GOOS == "windows" {
go func() {
prevH, prevW := cli.Out().GetTtySize()
for {
time.Sleep(time.Millisecond * 250)
h, w := cli.Out().GetTtySize()
if prevW != w || prevH != h {
resizeTty()
}
prevH = h
prevW = w
}
}()
} else {
sigchan := make(chan os.Signal, 1)
gosignal.Notify(sigchan, signal.SIGWINCH)
go func() {
for range sigchan {
resizeTty()
}
}()
}
return nil
}
// ForwardAllSignals forwards signals to the container
func ForwardAllSignals(ctx context.Context, cli *client.DockerCli, cid string) chan os.Signal {
sigc := make(chan os.Signal, 128)
signal.CatchAll(sigc)
go func() {
for s := range sigc {
if s == signal.SIGCHLD || s == signal.SIGPIPE {
continue
}
var sig string
for sigStr, sigN := range signal.SignalMap {
if sigN == s {
sig = sigStr
break
}
}
if sig == "" {
fmt.Fprintf(cli.Err(), "Unsupported signal: %v. Discarding.\n", s)
continue
}
if err := cli.Client().ContainerKill(ctx, cid, sig); err != nil {
logrus.Debugf("Error sending signal: %s", err)
}
}
}()
return sigc
}

View File

@ -5,95 +5,10 @@ import (
"io"
"io/ioutil"
"os"
gosignal "os/signal"
"path/filepath"
"runtime"
"strings"
"time"
"golang.org/x/net/context"
"github.com/Sirupsen/logrus"
"github.com/docker/docker/api/types"
"github.com/docker/docker/client"
"github.com/docker/docker/pkg/signal"
"github.com/docker/engine-api/types"
)
func (cli *DockerCli) resizeTty(ctx context.Context, id string, isExec bool) {
height, width := cli.Out().GetTtySize()
cli.ResizeTtyTo(ctx, id, height, width, isExec)
}
// ResizeTtyTo resizes tty to specific height and width
// TODO: this can be unexported again once all container related commands move to package container
func (cli *DockerCli) ResizeTtyTo(ctx context.Context, id string, height, width int, isExec bool) {
if height == 0 && width == 0 {
return
}
options := types.ResizeOptions{
Height: height,
Width: width,
}
var err error
if isExec {
err = cli.client.ContainerExecResize(ctx, id, options)
} else {
err = cli.client.ContainerResize(ctx, id, options)
}
if err != nil {
logrus.Debugf("Error resize: %s", err)
}
}
// GetExecExitCode perform an inspect on the exec command. It returns
// the running state and the exit code.
func (cli *DockerCli) GetExecExitCode(ctx context.Context, execID string) (bool, int, error) {
resp, err := cli.client.ContainerExecInspect(ctx, execID)
if err != nil {
// If we can't connect, then the daemon probably died.
if err != client.ErrConnectionFailed {
return false, -1, err
}
return false, -1, nil
}
return resp.Running, resp.ExitCode, nil
}
// MonitorTtySize updates the container tty size when the terminal tty changes size
func (cli *DockerCli) MonitorTtySize(ctx context.Context, id string, isExec bool) error {
cli.resizeTty(ctx, id, isExec)
if runtime.GOOS == "windows" {
go func() {
prevH, prevW := cli.Out().GetTtySize()
for {
time.Sleep(time.Millisecond * 250)
h, w := cli.Out().GetTtySize()
if prevW != w || prevH != h {
cli.resizeTty(ctx, id, isExec)
}
prevH = h
prevW = w
}
}()
} else {
sigchan := make(chan os.Signal, 1)
gosignal.Notify(sigchan, signal.SIGWINCH)
go func() {
for range sigchan {
cli.resizeTty(ctx, id, isExec)
}
}()
}
return nil
}
// CopyToFile writes the content of the reader to the specified file
func CopyToFile(outfile string, r io.Reader) error {
tmpFile, err := ioutil.TempFile(filepath.Dir(outfile), ".docker_temp_")
@ -119,37 +34,6 @@ func CopyToFile(outfile string, r io.Reader) error {
return nil
}
// ForwardAllSignals forwards signals to the container
// TODO: this can be unexported again once all container commands are under
// api/client/container
func (cli *DockerCli) ForwardAllSignals(ctx context.Context, cid string) chan os.Signal {
sigc := make(chan os.Signal, 128)
signal.CatchAll(sigc)
go func() {
for s := range sigc {
if s == signal.SIGCHLD || s == signal.SIGPIPE {
continue
}
var sig string
for sigStr, sigN := range signal.SignalMap {
if sigN == s {
sig = sigStr
break
}
}
if sig == "" {
fmt.Fprintf(cli.err, "Unsupported signal: %v. Discarding.\n", s)
continue
}
if err := cli.client.ContainerKill(ctx, cid, sig); err != nil {
logrus.Debugf("Error sending signal: %s", err)
}
}
}()
return sigc
}
// capitalizeFirst capitalizes the first character of string
func capitalizeFirst(s string) string {
switch l := len(s); l {