165 lines
4.7 KiB
Go
165 lines
4.7 KiB
Go
package client
|
|
|
|
import (
|
|
"crypto/tls"
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"net/http"
|
|
"net/url"
|
|
"os"
|
|
"strings"
|
|
|
|
"github.com/docker/docker/cli"
|
|
"github.com/docker/docker/cliconfig"
|
|
"github.com/docker/docker/opts"
|
|
"github.com/docker/docker/pkg/sockets"
|
|
"github.com/docker/docker/pkg/term"
|
|
"github.com/docker/docker/pkg/tlsconfig"
|
|
)
|
|
|
|
// DockerCli represents the docker command line client.
|
|
// Instances of the client can be returned from NewDockerCli.
|
|
type DockerCli struct {
|
|
// initializing closure
|
|
init func() error
|
|
|
|
// proto holds the client protocol i.e. unix.
|
|
proto string
|
|
// addr holds the client address.
|
|
addr string
|
|
// basePath holds the path to prepend to the requests
|
|
basePath string
|
|
|
|
// configFile has the client configuration file
|
|
configFile *cliconfig.ConfigFile
|
|
// in holds the input stream and closer (io.ReadCloser) for the client.
|
|
in io.ReadCloser
|
|
// out holds the output stream (io.Writer) for the client.
|
|
out io.Writer
|
|
// err holds the error stream (io.Writer) for the client.
|
|
err io.Writer
|
|
// keyFile holds the key file as a string.
|
|
keyFile string
|
|
// tlsConfig holds the TLS configuration for the client, and will
|
|
// set the scheme to https in NewDockerCli if present.
|
|
tlsConfig *tls.Config
|
|
// scheme holds the scheme of the client i.e. https.
|
|
scheme string
|
|
// inFd holds the file descriptor of the client's STDIN (if valid).
|
|
inFd uintptr
|
|
// outFd holds file descriptor of the client's STDOUT (if valid).
|
|
outFd uintptr
|
|
// isTerminalIn indicates whether the client's STDIN is a TTY
|
|
isTerminalIn bool
|
|
// isTerminalOut indicates whether the client's STDOUT is a TTY
|
|
isTerminalOut bool
|
|
// transport holds the client transport instance.
|
|
transport *http.Transport
|
|
}
|
|
|
|
// Initialize calls the init function that will setup the configuration for the client
|
|
// such as the TLS, tcp and other parameters used to run the client.
|
|
func (cli *DockerCli) Initialize() error {
|
|
if cli.init == nil {
|
|
return nil
|
|
}
|
|
return cli.init()
|
|
}
|
|
|
|
// CheckTtyInput checks if we are trying to attach to a container tty
|
|
// from a non-tty client input stream, and if so, returns an error.
|
|
func (cli *DockerCli) CheckTtyInput(attachStdin, ttyMode bool) error {
|
|
// In order to attach to a container tty, input stream for the client must
|
|
// be a tty itself: redirecting or piping the client standard input is
|
|
// incompatible with `docker run -t`, `docker exec -t` or `docker attach`.
|
|
if ttyMode && attachStdin && !cli.isTerminalIn {
|
|
return errors.New("cannot enable tty mode on non tty input")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// PsFormat returns the format string specified in the configuration.
|
|
// String contains columns and format specification, for example {{ID}\t{{Name}}.
|
|
func (cli *DockerCli) PsFormat() string {
|
|
return cli.configFile.PsFormat
|
|
}
|
|
|
|
// NewDockerCli returns a DockerCli instance with IO output and error streams set by in, out and err.
|
|
// The key file, protocol (i.e. unix) and address are passed in as strings, along with the tls.Config. If the tls.Config
|
|
// is set the client scheme will be set to https.
|
|
// The client will be given a 32-second timeout (see https://github.com/docker/docker/pull/8035).
|
|
func NewDockerCli(in io.ReadCloser, out, err io.Writer, clientFlags *cli.ClientFlags) *DockerCli {
|
|
cli := &DockerCli{
|
|
in: in,
|
|
out: out,
|
|
err: err,
|
|
keyFile: clientFlags.Common.TrustKey,
|
|
}
|
|
|
|
cli.init = func() error {
|
|
|
|
clientFlags.PostParse()
|
|
|
|
hosts := clientFlags.Common.Hosts
|
|
|
|
switch len(hosts) {
|
|
case 0:
|
|
hosts = []string{os.Getenv("DOCKER_HOST")}
|
|
case 1:
|
|
// only accept one host to talk to
|
|
default:
|
|
return errors.New("Please specify only one -H")
|
|
}
|
|
|
|
var e error
|
|
if hosts[0], e = opts.ParseHost(hosts[0]); e != nil {
|
|
return e
|
|
}
|
|
|
|
protoAddrParts := strings.SplitN(hosts[0], "://", 2)
|
|
cli.proto, cli.addr = protoAddrParts[0], protoAddrParts[1]
|
|
|
|
if cli.proto == "tcp" {
|
|
// error is checked in pkg/parsers already
|
|
parsed, _ := url.Parse("tcp://" + cli.addr)
|
|
cli.addr = parsed.Host
|
|
cli.basePath = parsed.Path
|
|
}
|
|
|
|
if clientFlags.Common.TLSOptions != nil {
|
|
cli.scheme = "https"
|
|
var e error
|
|
cli.tlsConfig, e = tlsconfig.Client(*clientFlags.Common.TLSOptions)
|
|
if e != nil {
|
|
return e
|
|
}
|
|
} else {
|
|
cli.scheme = "http"
|
|
}
|
|
|
|
if cli.in != nil {
|
|
cli.inFd, cli.isTerminalIn = term.GetFdInfo(cli.in)
|
|
}
|
|
if cli.out != nil {
|
|
cli.outFd, cli.isTerminalOut = term.GetFdInfo(cli.out)
|
|
}
|
|
|
|
// The transport is created here for reuse during the client session.
|
|
cli.transport = &http.Transport{
|
|
TLSClientConfig: cli.tlsConfig,
|
|
}
|
|
sockets.ConfigureTCPTransport(cli.transport, cli.proto, cli.addr)
|
|
|
|
configFile, e := cliconfig.Load(cliconfig.ConfigDir())
|
|
if e != nil {
|
|
fmt.Fprintf(cli.err, "WARNING: Error loading config file:%v\n", e)
|
|
}
|
|
cli.configFile = configFile
|
|
|
|
return nil
|
|
}
|
|
|
|
return cli
|
|
}
|