mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
Convert 'docker run' to a cobra command and to use pflags
Move container options into a struct so that tests should pass. Remove unused FlagSet arg from Parse Disable interspersed args on docker run Signed-off-by: Daniel Nephin <dnephin@docker.com>
This commit is contained in:
parent
3db23a4eaf
commit
a77f2450c7
18 changed files with 609 additions and 420 deletions
|
@ -66,7 +66,7 @@ func (cli *DockerCli) CmdAttach(args ...string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
if *proxy && !c.Config.Tty {
|
if *proxy && !c.Config.Tty {
|
||||||
sigc := cli.forwardAllSignals(ctx, container)
|
sigc := cli.ForwardAllSignals(ctx, container)
|
||||||
defer signal.StopCatch(sigc)
|
defer signal.StopCatch(sigc)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -80,20 +80,20 @@ func (cli *DockerCli) CmdAttach(args ...string) error {
|
||||||
defer resp.Close()
|
defer resp.Close()
|
||||||
|
|
||||||
if c.Config.Tty && cli.isTerminalOut {
|
if c.Config.Tty && cli.isTerminalOut {
|
||||||
height, width := cli.getTtySize()
|
height, width := cli.GetTtySize()
|
||||||
// To handle the case where a user repeatedly attaches/detaches without resizing their
|
// To handle the case where a user repeatedly attaches/detaches without resizing their
|
||||||
// terminal, the only way to get the shell prompt to display for attaches 2+ is to artificially
|
// 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
|
// resize it, then go back to normal. Without this, every attach after the first will
|
||||||
// require the user to manually resize or hit enter.
|
// require the user to manually resize or hit enter.
|
||||||
cli.resizeTtyTo(ctx, cmd.Arg(0), height+1, width+1, false)
|
cli.resizeTtyTo(ctx, cmd.Arg(0), height+1, width+1, false)
|
||||||
|
|
||||||
// After the above resizing occurs, the call to monitorTtySize below will handle resetting back
|
// After the above resizing occurs, the call to MonitorTtySize below will handle resetting back
|
||||||
// to the actual size.
|
// to the actual size.
|
||||||
if err := cli.monitorTtySize(ctx, cmd.Arg(0), false); err != nil {
|
if err := cli.MonitorTtySize(ctx, cmd.Arg(0), false); err != nil {
|
||||||
logrus.Debugf("Error monitoring TTY size: %s", err)
|
logrus.Debugf("Error monitoring TTY size: %s", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if err := cli.holdHijackedConnection(ctx, c.Config.Tty, in, cli.out, cli.err, resp); err != nil {
|
if err := cli.HoldHijackedConnection(ctx, c.Config.Tty, in, cli.out, cli.err, resp); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -101,7 +101,7 @@ func (cli *DockerCli) CmdAttach(args ...string) error {
|
||||||
return errAttach
|
return errAttach
|
||||||
}
|
}
|
||||||
|
|
||||||
_, status, err := cli.getExitCode(ctx, container)
|
_, status, err := cli.GetExitCode(ctx, container)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -75,6 +75,21 @@ func (cli *DockerCli) Err() io.Writer {
|
||||||
return cli.err
|
return cli.err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// In returns the reader used for stdin
|
||||||
|
func (cli *DockerCli) In() io.ReadCloser {
|
||||||
|
return cli.in
|
||||||
|
}
|
||||||
|
|
||||||
|
// ConfigFile returns the ConfigFile
|
||||||
|
func (cli *DockerCli) ConfigFile() *configfile.ConfigFile {
|
||||||
|
return cli.configFile
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsTerminalOut returns true if the clients stdin is a TTY
|
||||||
|
func (cli *DockerCli) IsTerminalOut() bool {
|
||||||
|
return cli.isTerminalOut
|
||||||
|
}
|
||||||
|
|
||||||
// CheckTtyInput checks if we are trying to attach to a container tty
|
// 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.
|
// from a non-tty client input stream, and if so, returns an error.
|
||||||
func (cli *DockerCli) CheckTtyInput(attachStdin, ttyMode bool) error {
|
func (cli *DockerCli) CheckTtyInput(attachStdin, ttyMode bool) error {
|
||||||
|
|
|
@ -38,7 +38,6 @@ func (cli *DockerCli) Command(name string) func(...string) error {
|
||||||
"restart": cli.CmdRestart,
|
"restart": cli.CmdRestart,
|
||||||
"rm": cli.CmdRm,
|
"rm": cli.CmdRm,
|
||||||
"rmi": cli.CmdRmi,
|
"rmi": cli.CmdRmi,
|
||||||
"run": cli.CmdRun,
|
|
||||||
"save": cli.CmdSave,
|
"save": cli.CmdSave,
|
||||||
"start": cli.CmdStart,
|
"start": cli.CmdStart,
|
||||||
"stats": cli.CmdStats,
|
"stats": cli.CmdStats,
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package client
|
package container
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
@ -11,13 +11,16 @@ import (
|
||||||
"golang.org/x/net/context"
|
"golang.org/x/net/context"
|
||||||
|
|
||||||
"github.com/Sirupsen/logrus"
|
"github.com/Sirupsen/logrus"
|
||||||
Cli "github.com/docker/docker/cli"
|
"github.com/docker/docker/api/client"
|
||||||
"github.com/docker/docker/opts"
|
"github.com/docker/docker/cli"
|
||||||
|
opttypes "github.com/docker/docker/opts"
|
||||||
"github.com/docker/docker/pkg/promise"
|
"github.com/docker/docker/pkg/promise"
|
||||||
"github.com/docker/docker/pkg/signal"
|
"github.com/docker/docker/pkg/signal"
|
||||||
runconfigopts "github.com/docker/docker/runconfig/opts"
|
runconfigopts "github.com/docker/docker/runconfig/opts"
|
||||||
"github.com/docker/engine-api/types"
|
"github.com/docker/engine-api/types"
|
||||||
"github.com/docker/libnetwork/resolvconf/dns"
|
"github.com/docker/libnetwork/resolvconf/dns"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
"github.com/spf13/pflag"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -25,74 +28,74 @@ const (
|
||||||
errCmdCouldNotBeInvoked = "could not be invoked"
|
errCmdCouldNotBeInvoked = "could not be invoked"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (cid *cidFile) Close() error {
|
type runOptions struct {
|
||||||
cid.file.Close()
|
autoRemove bool
|
||||||
|
detach bool
|
||||||
if !cid.written {
|
sigProxy bool
|
||||||
if err := os.Remove(cid.path); err != nil {
|
name string
|
||||||
return fmt.Errorf("failed to remove the CID file '%s': %s \n", cid.path, err)
|
detachKeys string
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cid *cidFile) Write(id string) error {
|
// NewRunCommand create a new `docker run` command
|
||||||
if _, err := cid.file.Write([]byte(id)); err != nil {
|
func NewRunCommand(dockerCli *client.DockerCli) *cobra.Command {
|
||||||
return fmt.Errorf("Failed to write the container ID to the file: %s", err)
|
var opts runOptions
|
||||||
}
|
var copts *runconfigopts.ContainerOptions
|
||||||
cid.written = true
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// if container start fails with 'command not found' error, return 127
|
cmd := &cobra.Command{
|
||||||
// if container start fails with 'command cannot be invoked' error, return 126
|
Use: "run [OPTIONS] IMAGE [COMMAND] [ARG...]",
|
||||||
// return 125 for generic docker daemon failures
|
Short: "Run a command in a new container",
|
||||||
func runStartContainerErr(err error) error {
|
Args: cli.RequiresMinArgs(1),
|
||||||
trimmedErr := strings.TrimPrefix(err.Error(), "Error response from daemon: ")
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
statusError := Cli.StatusError{StatusCode: 125}
|
copts.Image = args[0]
|
||||||
if strings.HasPrefix(trimmedErr, "Container command") {
|
if len(args) > 1 {
|
||||||
if strings.Contains(trimmedErr, errCmdNotFound) {
|
copts.Args = args[1:]
|
||||||
statusError = Cli.StatusError{StatusCode: 127}
|
}
|
||||||
} else if strings.Contains(trimmedErr, errCmdCouldNotBeInvoked) {
|
return runRun(dockerCli, cmd.Flags(), &opts, copts)
|
||||||
statusError = Cli.StatusError{StatusCode: 126}
|
},
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return statusError
|
flags := cmd.Flags()
|
||||||
}
|
flags.SetInterspersed(false)
|
||||||
|
|
||||||
// CmdRun runs a command in a new container.
|
|
||||||
//
|
|
||||||
// Usage: docker run [OPTIONS] IMAGE [COMMAND] [ARG...]
|
|
||||||
func (cli *DockerCli) CmdRun(args ...string) error {
|
|
||||||
cmd := Cli.Subcmd("run", []string{"IMAGE [COMMAND] [ARG...]"}, Cli.DockerCommands["run"].Description, true)
|
|
||||||
addTrustedFlags(cmd, true)
|
|
||||||
|
|
||||||
// These are flags not stored in Config/HostConfig
|
// These are flags not stored in Config/HostConfig
|
||||||
var (
|
flags.BoolVar(&opts.autoRemove, "rm", false, "Automatically remove the container when it exits")
|
||||||
flAutoRemove = cmd.Bool([]string{"-rm"}, false, "Automatically remove the container when it exits")
|
flags.BoolVarP(&opts.detach, "detach", "d", false, "Run container in background and print container ID")
|
||||||
flDetach = cmd.Bool([]string{"d", "-detach"}, false, "Run container in background and print container ID")
|
flags.BoolVar(&opts.sigProxy, "sig-proxy", true, "Proxy received signals to the process")
|
||||||
flSigProxy = cmd.Bool([]string{"-sig-proxy"}, true, "Proxy received signals to the process")
|
flags.StringVar(&opts.name, "name", "", "Assign a name to the container")
|
||||||
flName = cmd.String([]string{"-name"}, "", "Assign a name to the container")
|
flags.StringVar(&opts.detachKeys, "detach-keys", "", "Override the key sequence for detaching a container")
|
||||||
flDetachKeys = cmd.String([]string{"-detach-keys"}, "", "Override the key sequence for detaching a container")
|
|
||||||
flAttach *opts.ListOpts
|
|
||||||
|
|
||||||
|
// Add an explicit help that doesn't have a `-h` to prevent the conflict
|
||||||
|
// with hostname
|
||||||
|
flags.Bool("help", false, "Print usage")
|
||||||
|
|
||||||
|
client.AddTrustedFlags(flags, true)
|
||||||
|
copts = runconfigopts.AddFlags(flags)
|
||||||
|
return cmd
|
||||||
|
}
|
||||||
|
|
||||||
|
func runRun(dockerCli *client.DockerCli, flags *pflag.FlagSet, opts *runOptions, copts *runconfigopts.ContainerOptions) error {
|
||||||
|
stdout, stderr, stdin := dockerCli.Out(), dockerCli.Err(), dockerCli.In()
|
||||||
|
client := dockerCli.Client()
|
||||||
|
// TODO: pass this as an argument
|
||||||
|
cmdPath := "run"
|
||||||
|
|
||||||
|
var (
|
||||||
|
flAttach *opttypes.ListOpts
|
||||||
ErrConflictAttachDetach = fmt.Errorf("Conflicting options: -a and -d")
|
ErrConflictAttachDetach = fmt.Errorf("Conflicting options: -a and -d")
|
||||||
ErrConflictRestartPolicyAndAutoRemove = fmt.Errorf("Conflicting options: --restart and --rm")
|
ErrConflictRestartPolicyAndAutoRemove = fmt.Errorf("Conflicting options: --restart and --rm")
|
||||||
ErrConflictDetachAutoRemove = fmt.Errorf("Conflicting options: --rm and -d")
|
ErrConflictDetachAutoRemove = fmt.Errorf("Conflicting options: --rm and -d")
|
||||||
)
|
)
|
||||||
|
|
||||||
config, hostConfig, networkingConfig, cmd, err := runconfigopts.Parse(cmd, args)
|
config, hostConfig, networkingConfig, err := runconfigopts.Parse(flags, copts)
|
||||||
|
|
||||||
// just in case the Parse does not exit
|
// just in case the Parse does not exit
|
||||||
if err != nil {
|
if err != nil {
|
||||||
cmd.ReportError(err.Error(), true)
|
reportError(stderr, cmdPath, err.Error(), true)
|
||||||
os.Exit(125)
|
os.Exit(125)
|
||||||
}
|
}
|
||||||
|
|
||||||
if hostConfig.OomKillDisable != nil && *hostConfig.OomKillDisable && hostConfig.Memory == 0 {
|
if hostConfig.OomKillDisable != nil && *hostConfig.OomKillDisable && hostConfig.Memory == 0 {
|
||||||
fmt.Fprintf(cli.err, "WARNING: Disabling the OOM killer on containers without setting a '-m/--memory' limit may be dangerous.\n")
|
fmt.Fprintf(stderr, "WARNING: Disabling the OOM killer on containers without setting a '-m/--memory' limit may be dangerous.\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(hostConfig.DNS) > 0 {
|
if len(hostConfig.DNS) > 0 {
|
||||||
|
@ -101,30 +104,26 @@ func (cli *DockerCli) CmdRun(args ...string) error {
|
||||||
// set a DNS to a localhost address
|
// set a DNS to a localhost address
|
||||||
for _, dnsIP := range hostConfig.DNS {
|
for _, dnsIP := range hostConfig.DNS {
|
||||||
if dns.IsLocalhost(dnsIP) {
|
if dns.IsLocalhost(dnsIP) {
|
||||||
fmt.Fprintf(cli.err, "WARNING: Localhost DNS setting (--dns=%s) may fail in containers.\n", dnsIP)
|
fmt.Fprintf(stderr, "WARNING: Localhost DNS setting (--dns=%s) may fail in containers.\n", dnsIP)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if config.Image == "" {
|
|
||||||
cmd.Usage()
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
config.ArgsEscaped = false
|
config.ArgsEscaped = false
|
||||||
|
|
||||||
if !*flDetach {
|
if !opts.detach {
|
||||||
if err := cli.CheckTtyInput(config.AttachStdin, config.Tty); err != nil {
|
if err := dockerCli.CheckTtyInput(config.AttachStdin, config.Tty); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if fl := cmd.Lookup("-attach"); fl != nil {
|
if fl := flags.Lookup("attach"); fl != nil {
|
||||||
flAttach = fl.Value.(*opts.ListOpts)
|
flAttach = fl.Value.(*opttypes.ListOpts)
|
||||||
if flAttach.Len() != 0 {
|
if flAttach.Len() != 0 {
|
||||||
return ErrConflictAttachDetach
|
return ErrConflictAttachDetach
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if *flAutoRemove {
|
if opts.autoRemove {
|
||||||
return ErrConflictDetachAutoRemove
|
return ErrConflictDetachAutoRemove
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -134,28 +133,27 @@ func (cli *DockerCli) CmdRun(args ...string) error {
|
||||||
config.StdinOnce = false
|
config.StdinOnce = false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Disable flSigProxy when in TTY mode
|
// Disable sigProxy when in TTY mode
|
||||||
sigProxy := *flSigProxy
|
|
||||||
if config.Tty {
|
if config.Tty {
|
||||||
sigProxy = false
|
opts.sigProxy = false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Telling the Windows daemon the initial size of the tty during start makes
|
// Telling the Windows daemon the initial size of the tty during start makes
|
||||||
// a far better user experience rather than relying on subsequent resizes
|
// a far better user experience rather than relying on subsequent resizes
|
||||||
// to cause things to catch up.
|
// to cause things to catch up.
|
||||||
if runtime.GOOS == "windows" {
|
if runtime.GOOS == "windows" {
|
||||||
hostConfig.ConsoleSize[0], hostConfig.ConsoleSize[1] = cli.getTtySize()
|
hostConfig.ConsoleSize[0], hostConfig.ConsoleSize[1] = dockerCli.GetTtySize()
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx, cancelFun := context.WithCancel(context.Background())
|
ctx, cancelFun := context.WithCancel(context.Background())
|
||||||
|
|
||||||
createResponse, err := cli.createContainer(ctx, config, hostConfig, networkingConfig, hostConfig.ContainerIDFile, *flName)
|
createResponse, err := dockerCli.CreateContainer(ctx, config, hostConfig, networkingConfig, hostConfig.ContainerIDFile, opts.name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
cmd.ReportError(err.Error(), true)
|
reportError(stderr, cmdPath, err.Error(), true)
|
||||||
return runStartContainerErr(err)
|
return runStartContainerErr(err)
|
||||||
}
|
}
|
||||||
if sigProxy {
|
if opts.sigProxy {
|
||||||
sigc := cli.forwardAllSignals(ctx, createResponse.ID)
|
sigc := dockerCli.ForwardAllSignals(ctx, createResponse.ID)
|
||||||
defer signal.StopCatch(sigc)
|
defer signal.StopCatch(sigc)
|
||||||
}
|
}
|
||||||
var (
|
var (
|
||||||
|
@ -167,34 +165,34 @@ func (cli *DockerCli) CmdRun(args ...string) error {
|
||||||
waitDisplayID = make(chan struct{})
|
waitDisplayID = make(chan struct{})
|
||||||
go func() {
|
go func() {
|
||||||
defer close(waitDisplayID)
|
defer close(waitDisplayID)
|
||||||
fmt.Fprintf(cli.out, "%s\n", createResponse.ID)
|
fmt.Fprintf(stdout, "%s\n", createResponse.ID)
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
if *flAutoRemove && (hostConfig.RestartPolicy.IsAlways() || hostConfig.RestartPolicy.IsOnFailure()) {
|
if opts.autoRemove && (hostConfig.RestartPolicy.IsAlways() || hostConfig.RestartPolicy.IsOnFailure()) {
|
||||||
return ErrConflictRestartPolicyAndAutoRemove
|
return ErrConflictRestartPolicyAndAutoRemove
|
||||||
}
|
}
|
||||||
attach := config.AttachStdin || config.AttachStdout || config.AttachStderr
|
attach := config.AttachStdin || config.AttachStdout || config.AttachStderr
|
||||||
if attach {
|
if attach {
|
||||||
var (
|
var (
|
||||||
out, stderr io.Writer
|
out, cerr io.Writer
|
||||||
in io.ReadCloser
|
in io.ReadCloser
|
||||||
)
|
)
|
||||||
if config.AttachStdin {
|
if config.AttachStdin {
|
||||||
in = cli.in
|
in = stdin
|
||||||
}
|
}
|
||||||
if config.AttachStdout {
|
if config.AttachStdout {
|
||||||
out = cli.out
|
out = stdout
|
||||||
}
|
}
|
||||||
if config.AttachStderr {
|
if config.AttachStderr {
|
||||||
if config.Tty {
|
if config.Tty {
|
||||||
stderr = cli.out
|
cerr = stdout
|
||||||
} else {
|
} else {
|
||||||
stderr = cli.err
|
cerr = stderr
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if *flDetachKeys != "" {
|
if opts.detachKeys != "" {
|
||||||
cli.configFile.DetachKeys = *flDetachKeys
|
dockerCli.ConfigFile().DetachKeys = opts.detachKeys
|
||||||
}
|
}
|
||||||
|
|
||||||
options := types.ContainerAttachOptions{
|
options := types.ContainerAttachOptions{
|
||||||
|
@ -202,10 +200,10 @@ func (cli *DockerCli) CmdRun(args ...string) error {
|
||||||
Stdin: config.AttachStdin,
|
Stdin: config.AttachStdin,
|
||||||
Stdout: config.AttachStdout,
|
Stdout: config.AttachStdout,
|
||||||
Stderr: config.AttachStderr,
|
Stderr: config.AttachStderr,
|
||||||
DetachKeys: cli.configFile.DetachKeys,
|
DetachKeys: dockerCli.ConfigFile().DetachKeys,
|
||||||
}
|
}
|
||||||
|
|
||||||
resp, errAttach := cli.client.ContainerAttach(ctx, createResponse.ID, options)
|
resp, errAttach := client.ContainerAttach(ctx, createResponse.ID, options)
|
||||||
if errAttach != nil && errAttach != httputil.ErrPersistEOF {
|
if errAttach != nil && errAttach != httputil.ErrPersistEOF {
|
||||||
// ContainerAttach returns an ErrPersistEOF (connection closed)
|
// ContainerAttach returns an ErrPersistEOF (connection closed)
|
||||||
// means server met an error and put it in Hijacked connection
|
// means server met an error and put it in Hijacked connection
|
||||||
|
@ -215,7 +213,7 @@ func (cli *DockerCli) CmdRun(args ...string) error {
|
||||||
defer resp.Close()
|
defer resp.Close()
|
||||||
|
|
||||||
errCh = promise.Go(func() error {
|
errCh = promise.Go(func() error {
|
||||||
errHijack := cli.holdHijackedConnection(ctx, config.Tty, in, out, stderr, resp)
|
errHijack := dockerCli.HoldHijackedConnection(ctx, config.Tty, in, out, cerr, resp)
|
||||||
if errHijack == nil {
|
if errHijack == nil {
|
||||||
return errAttach
|
return errAttach
|
||||||
}
|
}
|
||||||
|
@ -223,18 +221,18 @@ func (cli *DockerCli) CmdRun(args ...string) error {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
if *flAutoRemove {
|
if opts.autoRemove {
|
||||||
defer func() {
|
defer func() {
|
||||||
// Explicitly not sharing the context as it could be "Done" (by calling cancelFun)
|
// Explicitly not sharing the context as it could be "Done" (by calling cancelFun)
|
||||||
// and thus the container would not be removed.
|
// and thus the container would not be removed.
|
||||||
if err := cli.removeContainer(context.Background(), createResponse.ID, true, false, true); err != nil {
|
if err := dockerCli.RemoveContainer(context.Background(), createResponse.ID, true, false, true); err != nil {
|
||||||
fmt.Fprintf(cli.err, "%v\n", err)
|
fmt.Fprintf(stderr, "%v\n", err)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
//start the container
|
//start the container
|
||||||
if err := cli.client.ContainerStart(ctx, createResponse.ID, types.ContainerStartOptions{}); err != nil {
|
if err := client.ContainerStart(ctx, createResponse.ID, types.ContainerStartOptions{}); err != nil {
|
||||||
// If we have holdHijackedConnection, we should notify
|
// If we have holdHijackedConnection, we should notify
|
||||||
// holdHijackedConnection we are going to exit and wait
|
// holdHijackedConnection we are going to exit and wait
|
||||||
// to avoid the terminal are not restored.
|
// to avoid the terminal are not restored.
|
||||||
|
@ -243,13 +241,13 @@ func (cli *DockerCli) CmdRun(args ...string) error {
|
||||||
<-errCh
|
<-errCh
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd.ReportError(err.Error(), false)
|
reportError(stderr, cmdPath, err.Error(), false)
|
||||||
return runStartContainerErr(err)
|
return runStartContainerErr(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (config.AttachStdin || config.AttachStdout || config.AttachStderr) && config.Tty && cli.isTerminalOut {
|
if (config.AttachStdin || config.AttachStdout || config.AttachStderr) && config.Tty && dockerCli.IsTerminalOut() {
|
||||||
if err := cli.monitorTtySize(ctx, createResponse.ID, false); err != nil {
|
if err := dockerCli.MonitorTtySize(ctx, createResponse.ID, false); err != nil {
|
||||||
fmt.Fprintf(cli.err, "Error monitoring TTY size: %s\n", err)
|
fmt.Fprintf(stderr, "Error monitoring TTY size: %s\n", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -270,32 +268,58 @@ func (cli *DockerCli) CmdRun(args ...string) error {
|
||||||
var status int
|
var status int
|
||||||
|
|
||||||
// Attached mode
|
// Attached mode
|
||||||
if *flAutoRemove {
|
if opts.autoRemove {
|
||||||
// Autoremove: wait for the container to finish, retrieve
|
// Autoremove: wait for the container to finish, retrieve
|
||||||
// the exit code and remove the container
|
// the exit code and remove the container
|
||||||
if status, err = cli.client.ContainerWait(ctx, createResponse.ID); err != nil {
|
if status, err = client.ContainerWait(ctx, createResponse.ID); err != nil {
|
||||||
return runStartContainerErr(err)
|
return runStartContainerErr(err)
|
||||||
}
|
}
|
||||||
if _, status, err = cli.getExitCode(ctx, createResponse.ID); err != nil {
|
if _, status, err = dockerCli.GetExitCode(ctx, createResponse.ID); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// No Autoremove: Simply retrieve the exit code
|
// No Autoremove: Simply retrieve the exit code
|
||||||
if !config.Tty {
|
if !config.Tty {
|
||||||
// In non-TTY mode, we can't detach, so we must wait for container exit
|
// In non-TTY mode, we can't detach, so we must wait for container exit
|
||||||
if status, err = cli.client.ContainerWait(ctx, createResponse.ID); err != nil {
|
if status, err = client.ContainerWait(ctx, createResponse.ID); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// In TTY mode, there is a race: if the process dies too slowly, the state could
|
// In TTY mode, there is a race: if the process dies too slowly, the state could
|
||||||
// be updated after the getExitCode call and result in the wrong exit code being reported
|
// be updated after the getExitCode call and result in the wrong exit code being reported
|
||||||
if _, status, err = cli.getExitCode(ctx, createResponse.ID); err != nil {
|
if _, status, err = dockerCli.GetExitCode(ctx, createResponse.ID); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if status != 0 {
|
if status != 0 {
|
||||||
return Cli.StatusError{StatusCode: status}
|
return cli.StatusError{StatusCode: status}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// reportError is a utility method that prints a user-friendly message
|
||||||
|
// containing the error that occurred during parsing and a suggestion to get help
|
||||||
|
func reportError(stderr io.Writer, name string, str string, withHelp bool) {
|
||||||
|
if withHelp {
|
||||||
|
str += ".\nSee '" + os.Args[0] + " " + name + " --help'"
|
||||||
|
}
|
||||||
|
fmt.Fprintf(stderr, "%s: %s.\n", os.Args[0], str)
|
||||||
|
}
|
||||||
|
|
||||||
|
// if container start fails with 'command not found' error, return 127
|
||||||
|
// if container start fails with 'command cannot be invoked' error, return 126
|
||||||
|
// return 125 for generic docker daemon failures
|
||||||
|
func runStartContainerErr(err error) error {
|
||||||
|
trimmedErr := strings.TrimPrefix(err.Error(), "Error response from daemon: ")
|
||||||
|
statusError := cli.StatusError{StatusCode: 125}
|
||||||
|
if strings.HasPrefix(trimmedErr, "Container command") {
|
||||||
|
if strings.Contains(trimmedErr, errCmdNotFound) {
|
||||||
|
statusError = cli.StatusError{StatusCode: 127}
|
||||||
|
} else if strings.Contains(trimmedErr, errCmdCouldNotBeInvoked) {
|
||||||
|
statusError = cli.StatusError{StatusCode: 126}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return statusError
|
||||||
|
}
|
|
@ -12,7 +12,7 @@ import (
|
||||||
// FIXME migrate to docker/distribution/reference
|
// FIXME migrate to docker/distribution/reference
|
||||||
"github.com/docker/docker/reference"
|
"github.com/docker/docker/reference"
|
||||||
"github.com/docker/docker/registry"
|
"github.com/docker/docker/registry"
|
||||||
runconfigopts "github.com/docker/docker/runconfig/opts"
|
//runconfigopts "github.com/docker/docker/runconfig/opts"
|
||||||
"github.com/docker/engine-api/client"
|
"github.com/docker/engine-api/client"
|
||||||
"github.com/docker/engine-api/types"
|
"github.com/docker/engine-api/types"
|
||||||
"github.com/docker/engine-api/types/container"
|
"github.com/docker/engine-api/types/container"
|
||||||
|
@ -56,6 +56,26 @@ type cidFile struct {
|
||||||
written bool
|
written bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (cid *cidFile) Close() error {
|
||||||
|
cid.file.Close()
|
||||||
|
|
||||||
|
if !cid.written {
|
||||||
|
if err := os.Remove(cid.path); err != nil {
|
||||||
|
return fmt.Errorf("failed to remove the CID file '%s': %s \n", cid.path, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cid *cidFile) Write(id string) error {
|
||||||
|
if _, err := cid.file.Write([]byte(id)); err != nil {
|
||||||
|
return fmt.Errorf("Failed to write the container ID to the file: %s", err)
|
||||||
|
}
|
||||||
|
cid.written = true
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func newCIDFile(path string) (*cidFile, error) {
|
func newCIDFile(path string) (*cidFile, error) {
|
||||||
if _, err := os.Stat(path); err == nil {
|
if _, err := os.Stat(path); err == nil {
|
||||||
return nil, fmt.Errorf("Container ID file found, make sure the other container isn't running or delete %s", path)
|
return nil, fmt.Errorf("Container ID file found, make sure the other container isn't running or delete %s", path)
|
||||||
|
@ -69,7 +89,10 @@ func newCIDFile(path string) (*cidFile, error) {
|
||||||
return &cidFile{path: path, file: f}, nil
|
return &cidFile{path: path, file: f}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cli *DockerCli) createContainer(ctx context.Context, config *container.Config, hostConfig *container.HostConfig, networkingConfig *networktypes.NetworkingConfig, cidfile, name string) (*types.ContainerCreateResponse, error) {
|
// CreateContainer creates a container from a config
|
||||||
|
// TODO: this can be unexported again once all container commands are under
|
||||||
|
// api/client/container
|
||||||
|
func (cli *DockerCli) CreateContainer(ctx context.Context, config *container.Config, hostConfig *container.HostConfig, networkingConfig *networktypes.NetworkingConfig, cidfile, name string) (*types.ContainerCreateResponse, error) {
|
||||||
var containerIDFile *cidFile
|
var containerIDFile *cidFile
|
||||||
if cidfile != "" {
|
if cidfile != "" {
|
||||||
var err error
|
var err error
|
||||||
|
@ -143,25 +166,26 @@ func (cli *DockerCli) CmdCreate(args ...string) error {
|
||||||
cmd := Cli.Subcmd("create", []string{"IMAGE [COMMAND] [ARG...]"}, Cli.DockerCommands["create"].Description, true)
|
cmd := Cli.Subcmd("create", []string{"IMAGE [COMMAND] [ARG...]"}, Cli.DockerCommands["create"].Description, true)
|
||||||
addTrustedFlags(cmd, true)
|
addTrustedFlags(cmd, true)
|
||||||
|
|
||||||
|
// TODO: tmp disable for PoC, convert to cobra and pflag later
|
||||||
// These are flags not stored in Config/HostConfig
|
// These are flags not stored in Config/HostConfig
|
||||||
var (
|
// var (
|
||||||
flName = cmd.String([]string{"-name"}, "", "Assign a name to the container")
|
// flName = cmd.String([]string{"-name"}, "", "Assign a name to the container")
|
||||||
)
|
// )
|
||||||
|
|
||||||
config, hostConfig, networkingConfig, cmd, err := runconfigopts.Parse(cmd, args)
|
// config, hostConfig, networkingConfig, cmd, err := runconfigopts.Parse(cmd, args)
|
||||||
|
//
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
cmd.ReportError(err.Error(), true)
|
// cmd.ReportError(err.Error(), true)
|
||||||
os.Exit(1)
|
// os.Exit(1)
|
||||||
}
|
// }
|
||||||
if config.Image == "" {
|
// if config.Image == "" {
|
||||||
cmd.Usage()
|
// cmd.Usage()
|
||||||
return nil
|
// return nil
|
||||||
}
|
// }
|
||||||
response, err := cli.createContainer(context.Background(), config, hostConfig, networkingConfig, hostConfig.ContainerIDFile, *flName)
|
// response, err := cli.CreateContainer(context.Background(), config, hostConfig, networkingConfig, hostConfig.ContainerIDFile, *flName)
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
return err
|
// return err
|
||||||
}
|
// }
|
||||||
fmt.Fprintf(cli.out, "%s\n", response.ID)
|
// fmt.Fprintf(cli.out, "%s\n", response.ID)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -93,11 +93,11 @@ func (cli *DockerCli) CmdExec(args ...string) error {
|
||||||
}
|
}
|
||||||
defer resp.Close()
|
defer resp.Close()
|
||||||
errCh = promise.Go(func() error {
|
errCh = promise.Go(func() error {
|
||||||
return cli.holdHijackedConnection(ctx, execConfig.Tty, in, out, stderr, resp)
|
return cli.HoldHijackedConnection(ctx, execConfig.Tty, in, out, stderr, resp)
|
||||||
})
|
})
|
||||||
|
|
||||||
if execConfig.Tty && cli.isTerminalIn {
|
if execConfig.Tty && cli.isTerminalIn {
|
||||||
if err := cli.monitorTtySize(ctx, execID, true); err != nil {
|
if err := cli.MonitorTtySize(ctx, execID, true); err != nil {
|
||||||
fmt.Fprintf(cli.err, "Error monitoring TTY size: %s\n", err)
|
fmt.Fprintf(cli.err, "Error monitoring TTY size: %s\n", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,8 @@ import (
|
||||||
"github.com/docker/engine-api/types"
|
"github.com/docker/engine-api/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (cli *DockerCli) holdHijackedConnection(ctx context.Context, tty bool, inputStream io.ReadCloser, outputStream, errorStream io.Writer, resp types.HijackedResponse) error {
|
// HoldHijackedConnection ... TODO docstring
|
||||||
|
func (cli *DockerCli) HoldHijackedConnection(ctx context.Context, tty bool, inputStream io.ReadCloser, outputStream, errorStream io.Writer, resp types.HijackedResponse) error {
|
||||||
var (
|
var (
|
||||||
err error
|
err error
|
||||||
restoreOnce sync.Once
|
restoreOnce sync.Once
|
||||||
|
|
|
@ -32,7 +32,7 @@ func (cli *DockerCli) CmdRm(args ...string) error {
|
||||||
}
|
}
|
||||||
name = strings.Trim(name, "/")
|
name = strings.Trim(name, "/")
|
||||||
|
|
||||||
if err := cli.removeContainer(ctx, name, *v, *link, *force); err != nil {
|
if err := cli.RemoveContainer(ctx, name, *v, *link, *force); err != nil {
|
||||||
errs = append(errs, err.Error())
|
errs = append(errs, err.Error())
|
||||||
} else {
|
} else {
|
||||||
fmt.Fprintf(cli.out, "%s\n", name)
|
fmt.Fprintf(cli.out, "%s\n", name)
|
||||||
|
@ -44,7 +44,10 @@ func (cli *DockerCli) CmdRm(args ...string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cli *DockerCli) removeContainer(ctx context.Context, container string, removeVolumes, removeLinks, force bool) error {
|
// RemoveContainer removes a container
|
||||||
|
// TODO: this can be unexported again once all container commands are under
|
||||||
|
// api/client/container
|
||||||
|
func (cli *DockerCli) RemoveContainer(ctx context.Context, container string, removeVolumes, removeLinks, force bool) error {
|
||||||
options := types.ContainerRemoveOptions{
|
options := types.ContainerRemoveOptions{
|
||||||
RemoveVolumes: removeVolumes,
|
RemoveVolumes: removeVolumes,
|
||||||
RemoveLinks: removeLinks,
|
RemoveLinks: removeLinks,
|
||||||
|
|
|
@ -17,7 +17,10 @@ import (
|
||||||
"github.com/docker/engine-api/types"
|
"github.com/docker/engine-api/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (cli *DockerCli) forwardAllSignals(ctx context.Context, cid string) chan os.Signal {
|
// ForwardAllSignals forwards signals to the contianer
|
||||||
|
// 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)
|
sigc := make(chan os.Signal, 128)
|
||||||
signal.CatchAll(sigc)
|
signal.CatchAll(sigc)
|
||||||
go func() {
|
go func() {
|
||||||
|
@ -74,7 +77,7 @@ func (cli *DockerCli) CmdStart(args ...string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
if !c.Config.Tty {
|
if !c.Config.Tty {
|
||||||
sigc := cli.forwardAllSignals(ctx, container)
|
sigc := cli.ForwardAllSignals(ctx, container)
|
||||||
defer signal.StopCatch(sigc)
|
defer signal.StopCatch(sigc)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -105,7 +108,7 @@ func (cli *DockerCli) CmdStart(args ...string) error {
|
||||||
}
|
}
|
||||||
defer resp.Close()
|
defer resp.Close()
|
||||||
cErr := promise.Go(func() error {
|
cErr := promise.Go(func() error {
|
||||||
errHijack := cli.holdHijackedConnection(ctx, c.Config.Tty, in, cli.out, cli.err, resp)
|
errHijack := cli.HoldHijackedConnection(ctx, c.Config.Tty, in, cli.out, cli.err, resp)
|
||||||
if errHijack == nil {
|
if errHijack == nil {
|
||||||
return errAttach
|
return errAttach
|
||||||
}
|
}
|
||||||
|
@ -121,14 +124,14 @@ func (cli *DockerCli) CmdStart(args ...string) error {
|
||||||
|
|
||||||
// 4. Wait for attachment to break.
|
// 4. Wait for attachment to break.
|
||||||
if c.Config.Tty && cli.isTerminalOut {
|
if c.Config.Tty && cli.isTerminalOut {
|
||||||
if err := cli.monitorTtySize(ctx, container, false); err != nil {
|
if err := cli.MonitorTtySize(ctx, container, false); err != nil {
|
||||||
fmt.Fprintf(cli.err, "Error monitoring TTY size: %s\n", err)
|
fmt.Fprintf(cli.err, "Error monitoring TTY size: %s\n", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if attchErr := <-cErr; attchErr != nil {
|
if attchErr := <-cErr; attchErr != nil {
|
||||||
return attchErr
|
return attchErr
|
||||||
}
|
}
|
||||||
_, status, err := cli.getExitCode(ctx, container)
|
_, status, err := cli.GetExitCode(ctx, container)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,6 +37,7 @@ import (
|
||||||
"github.com/docker/notary/tuf/data"
|
"github.com/docker/notary/tuf/data"
|
||||||
"github.com/docker/notary/tuf/signed"
|
"github.com/docker/notary/tuf/signed"
|
||||||
"github.com/docker/notary/tuf/store"
|
"github.com/docker/notary/tuf/store"
|
||||||
|
"github.com/spf13/pflag"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -44,7 +45,20 @@ var (
|
||||||
untrusted bool
|
untrusted bool
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// TODO: tmp workaround to get this PoC working, change everything to use
|
||||||
|
// exported version
|
||||||
func addTrustedFlags(fs *flag.FlagSet, verify bool) {
|
func addTrustedFlags(fs *flag.FlagSet, verify bool) {
|
||||||
|
trusted, message := setupTrustedFlag(verify)
|
||||||
|
fs.BoolVar(&untrusted, []string{"-disable-content-trust"}, !trusted, message)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddTrustedFlags adds the trust flags to a FlagSet
|
||||||
|
func AddTrustedFlags(fs *pflag.FlagSet, verify bool) {
|
||||||
|
trusted, message := setupTrustedFlag(verify)
|
||||||
|
fs.BoolVar(&untrusted, "disable-content-trust", !trusted, message)
|
||||||
|
}
|
||||||
|
|
||||||
|
func setupTrustedFlag(verify bool) (bool, string) {
|
||||||
var trusted bool
|
var trusted bool
|
||||||
if e := os.Getenv("DOCKER_CONTENT_TRUST"); e != "" {
|
if e := os.Getenv("DOCKER_CONTENT_TRUST"); e != "" {
|
||||||
if t, err := strconv.ParseBool(e); t || err != nil {
|
if t, err := strconv.ParseBool(e); t || err != nil {
|
||||||
|
@ -56,7 +70,7 @@ func addTrustedFlags(fs *flag.FlagSet, verify bool) {
|
||||||
if verify {
|
if verify {
|
||||||
message = "Skip image verification"
|
message = "Skip image verification"
|
||||||
}
|
}
|
||||||
fs.BoolVar(&untrusted, []string{"-disable-content-trust"}, !trusted, message)
|
return trusted, message
|
||||||
}
|
}
|
||||||
|
|
||||||
func isTrusted() bool {
|
func isTrusted() bool {
|
||||||
|
|
|
@ -61,7 +61,7 @@ func (cli *DockerCli) RegistryAuthenticationPrivilegedFunc(index *registrytypes.
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cli *DockerCli) resizeTty(ctx context.Context, id string, isExec bool) {
|
func (cli *DockerCli) resizeTty(ctx context.Context, id string, isExec bool) {
|
||||||
height, width := cli.getTtySize()
|
height, width := cli.GetTtySize()
|
||||||
cli.resizeTtyTo(ctx, id, height, width, isExec)
|
cli.resizeTtyTo(ctx, id, height, width, isExec)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -87,9 +87,9 @@ func (cli *DockerCli) resizeTtyTo(ctx context.Context, id string, height, width
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// getExitCode perform an inspect on the container. It returns
|
// GetExitCode perform an inspect on the container. It returns
|
||||||
// the running state and the exit code.
|
// the running state and the exit code.
|
||||||
func (cli *DockerCli) getExitCode(ctx context.Context, containerID string) (bool, int, error) {
|
func (cli *DockerCli) GetExitCode(ctx context.Context, containerID string) (bool, int, error) {
|
||||||
c, err := cli.client.ContainerInspect(ctx, containerID)
|
c, err := cli.client.ContainerInspect(ctx, containerID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// If we can't connect, then the daemon probably died.
|
// If we can't connect, then the daemon probably died.
|
||||||
|
@ -117,15 +117,16 @@ func (cli *DockerCli) getExecExitCode(ctx context.Context, execID string) (bool,
|
||||||
return resp.Running, resp.ExitCode, nil
|
return resp.Running, resp.ExitCode, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cli *DockerCli) monitorTtySize(ctx context.Context, id string, isExec bool) error {
|
// 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)
|
cli.resizeTty(ctx, id, isExec)
|
||||||
|
|
||||||
if runtime.GOOS == "windows" {
|
if runtime.GOOS == "windows" {
|
||||||
go func() {
|
go func() {
|
||||||
prevH, prevW := cli.getTtySize()
|
prevH, prevW := cli.GetTtySize()
|
||||||
for {
|
for {
|
||||||
time.Sleep(time.Millisecond * 250)
|
time.Sleep(time.Millisecond * 250)
|
||||||
h, w := cli.getTtySize()
|
h, w := cli.GetTtySize()
|
||||||
|
|
||||||
if prevW != w || prevH != h {
|
if prevW != w || prevH != h {
|
||||||
cli.resizeTty(ctx, id, isExec)
|
cli.resizeTty(ctx, id, isExec)
|
||||||
|
@ -146,7 +147,8 @@ func (cli *DockerCli) monitorTtySize(ctx context.Context, id string, isExec bool
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cli *DockerCli) getTtySize() (int, int) {
|
// GetTtySize returns the height and width in characters of the tty
|
||||||
|
func (cli *DockerCli) GetTtySize() (int, int) {
|
||||||
if !cli.isTerminalOut {
|
if !cli.isTerminalOut {
|
||||||
return 0, 0
|
return 0, 0
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/docker/docker/api/client"
|
"github.com/docker/docker/api/client"
|
||||||
|
"github.com/docker/docker/api/client/container"
|
||||||
"github.com/docker/docker/api/client/image"
|
"github.com/docker/docker/api/client/image"
|
||||||
"github.com/docker/docker/api/client/volume"
|
"github.com/docker/docker/api/client/volume"
|
||||||
"github.com/docker/docker/cli"
|
"github.com/docker/docker/cli"
|
||||||
|
@ -34,8 +35,9 @@ func NewCobraAdaptor(clientFlags *cliflags.ClientFlags) CobraAdaptor {
|
||||||
rootCmd.SetFlagErrorFunc(flagErrorFunc)
|
rootCmd.SetFlagErrorFunc(flagErrorFunc)
|
||||||
rootCmd.SetOutput(stdout)
|
rootCmd.SetOutput(stdout)
|
||||||
rootCmd.AddCommand(
|
rootCmd.AddCommand(
|
||||||
volume.NewVolumeCommand(dockerCli),
|
container.NewRunCommand(dockerCli),
|
||||||
image.NewSearchCommand(dockerCli),
|
image.NewSearchCommand(dockerCli),
|
||||||
|
volume.NewVolumeCommand(dockerCli),
|
||||||
)
|
)
|
||||||
|
|
||||||
rootCmd.PersistentFlags().BoolP("help", "h", false, "Print usage")
|
rootCmd.PersistentFlags().BoolP("help", "h", false, "Print usage")
|
||||||
|
@ -52,7 +54,7 @@ func NewCobraAdaptor(clientFlags *cliflags.ClientFlags) CobraAdaptor {
|
||||||
func (c CobraAdaptor) Usage() []cli.Command {
|
func (c CobraAdaptor) Usage() []cli.Command {
|
||||||
cmds := []cli.Command{}
|
cmds := []cli.Command{}
|
||||||
for _, cmd := range c.rootCmd.Commands() {
|
for _, cmd := range c.rootCmd.Commands() {
|
||||||
cmds = append(cmds, cli.Command{Name: cmd.Use, Description: cmd.Short})
|
cmds = append(cmds, cli.Command{Name: cmd.Name(), Description: cmd.Short})
|
||||||
}
|
}
|
||||||
return cmds
|
return cmds
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,7 +37,6 @@ var DockerCommandUsage = []Command{
|
||||||
{"restart", "Restart a container"},
|
{"restart", "Restart a container"},
|
||||||
{"rm", "Remove one or more containers"},
|
{"rm", "Remove one or more containers"},
|
||||||
{"rmi", "Remove one or more images"},
|
{"rmi", "Remove one or more images"},
|
||||||
{"run", "Run a command in a new container"},
|
|
||||||
{"save", "Save one or more images to a tar archive"},
|
{"save", "Save one or more images to a tar archive"},
|
||||||
{"start", "Start one or more stopped containers"},
|
{"start", "Start one or more stopped containers"},
|
||||||
{"stats", "Display a live stream of container(s) resource usage statistics"},
|
{"stats", "Display a live stream of container(s) resource usage statistics"},
|
||||||
|
|
|
@ -8,160 +8,245 @@ import (
|
||||||
"path"
|
"path"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/docker/docker/opts"
|
"github.com/docker/docker/opts"
|
||||||
flag "github.com/docker/docker/pkg/mflag"
|
|
||||||
"github.com/docker/docker/pkg/mount"
|
"github.com/docker/docker/pkg/mount"
|
||||||
"github.com/docker/docker/pkg/signal"
|
"github.com/docker/docker/pkg/signal"
|
||||||
"github.com/docker/engine-api/types/container"
|
"github.com/docker/engine-api/types/container"
|
||||||
networktypes "github.com/docker/engine-api/types/network"
|
networktypes "github.com/docker/engine-api/types/network"
|
||||||
"github.com/docker/engine-api/types/strslice"
|
"github.com/docker/engine-api/types/strslice"
|
||||||
"github.com/docker/go-connections/nat"
|
"github.com/docker/go-connections/nat"
|
||||||
"github.com/docker/go-units"
|
units "github.com/docker/go-units"
|
||||||
|
"github.com/spf13/pflag"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Parse parses the specified args for the specified command and generates a Config,
|
// ContainerOptions is a data object with all the options for creating a container
|
||||||
// a HostConfig and returns them with the specified command.
|
// TODO: remove fl prefix
|
||||||
// If the specified args are not valid, it will return an error.
|
type ContainerOptions struct {
|
||||||
func Parse(cmd *flag.FlagSet, args []string) (*container.Config, *container.HostConfig, *networktypes.NetworkingConfig, *flag.FlagSet, error) {
|
flAttach opts.ListOpts
|
||||||
var (
|
flVolumes opts.ListOpts
|
||||||
// FIXME: use utils.ListOpts for attach and volumes?
|
flTmpfs opts.ListOpts
|
||||||
flAttach = opts.NewListOpts(ValidateAttach)
|
flBlkioWeightDevice WeightdeviceOpt
|
||||||
flVolumes = opts.NewListOpts(nil)
|
flDeviceReadBps ThrottledeviceOpt
|
||||||
flTmpfs = opts.NewListOpts(nil)
|
flDeviceWriteBps ThrottledeviceOpt
|
||||||
flBlkioWeightDevice = NewWeightdeviceOpt(ValidateWeightDevice)
|
flLinks opts.ListOpts
|
||||||
flDeviceReadBps = NewThrottledeviceOpt(ValidateThrottleBpsDevice)
|
flAliases opts.ListOpts
|
||||||
flDeviceWriteBps = NewThrottledeviceOpt(ValidateThrottleBpsDevice)
|
flDeviceReadIOps ThrottledeviceOpt
|
||||||
flLinks = opts.NewListOpts(ValidateLink)
|
flDeviceWriteIOps ThrottledeviceOpt
|
||||||
flAliases = opts.NewListOpts(nil)
|
flEnv opts.ListOpts
|
||||||
flDeviceReadIOps = NewThrottledeviceOpt(ValidateThrottleIOpsDevice)
|
flLabels opts.ListOpts
|
||||||
flDeviceWriteIOps = NewThrottledeviceOpt(ValidateThrottleIOpsDevice)
|
flDevices opts.ListOpts
|
||||||
flEnv = opts.NewListOpts(ValidateEnv)
|
flUlimits *UlimitOpt
|
||||||
flLabels = opts.NewListOpts(ValidateEnv)
|
flSysctls *opts.MapOpts
|
||||||
flDevices = opts.NewListOpts(ValidateDevice)
|
flPublish opts.ListOpts
|
||||||
|
flExpose opts.ListOpts
|
||||||
|
flDNS opts.ListOpts
|
||||||
|
flDNSSearch opts.ListOpts
|
||||||
|
flDNSOptions opts.ListOpts
|
||||||
|
flExtraHosts opts.ListOpts
|
||||||
|
flVolumesFrom opts.ListOpts
|
||||||
|
flEnvFile opts.ListOpts
|
||||||
|
flCapAdd opts.ListOpts
|
||||||
|
flCapDrop opts.ListOpts
|
||||||
|
flGroupAdd opts.ListOpts
|
||||||
|
flSecurityOpt opts.ListOpts
|
||||||
|
flStorageOpt opts.ListOpts
|
||||||
|
flLabelsFile opts.ListOpts
|
||||||
|
flLoggingOpts opts.ListOpts
|
||||||
|
flPrivileged *bool
|
||||||
|
flPidMode *string
|
||||||
|
flUTSMode *string
|
||||||
|
flUsernsMode *string
|
||||||
|
flPublishAll *bool
|
||||||
|
flStdin *bool
|
||||||
|
flTty *bool
|
||||||
|
flOomKillDisable *bool
|
||||||
|
flOomScoreAdj *int
|
||||||
|
flContainerIDFile *string
|
||||||
|
flEntrypoint *string
|
||||||
|
flHostname *string
|
||||||
|
flMemoryString *string
|
||||||
|
flMemoryReservation *string
|
||||||
|
flMemorySwap *string
|
||||||
|
flKernelMemory *string
|
||||||
|
flUser *string
|
||||||
|
flWorkingDir *string
|
||||||
|
flCPUShares *int64
|
||||||
|
flCPUPercent *int64
|
||||||
|
flCPUPeriod *int64
|
||||||
|
flCPUQuota *int64
|
||||||
|
flCpusetCpus *string
|
||||||
|
flCpusetMems *string
|
||||||
|
flBlkioWeight *uint16
|
||||||
|
flIOMaxBandwidth *string
|
||||||
|
flIOMaxIOps *uint64
|
||||||
|
flSwappiness *int64
|
||||||
|
flNetMode *string
|
||||||
|
flMacAddress *string
|
||||||
|
flIPv4Address *string
|
||||||
|
flIPv6Address *string
|
||||||
|
flIpcMode *string
|
||||||
|
flPidsLimit *int64
|
||||||
|
flRestartPolicy *string
|
||||||
|
flReadonlyRootfs *bool
|
||||||
|
flLoggingDriver *string
|
||||||
|
flCgroupParent *string
|
||||||
|
flVolumeDriver *string
|
||||||
|
flStopSignal *string
|
||||||
|
flIsolation *string
|
||||||
|
flShmSize *string
|
||||||
|
flNoHealthcheck *bool
|
||||||
|
flHealthCmd *string
|
||||||
|
flHealthInterval *time.Duration
|
||||||
|
flHealthTimeout *time.Duration
|
||||||
|
flHealthRetries *int
|
||||||
|
|
||||||
flUlimits = NewUlimitOpt(nil)
|
Image string
|
||||||
flSysctls = opts.NewMapOpts(nil, opts.ValidateSysctl)
|
Args []string
|
||||||
|
}
|
||||||
|
|
||||||
flPublish = opts.NewListOpts(nil)
|
// AddFlags adds all command line flags that will be used by Parse to the FlagSet
|
||||||
flExpose = opts.NewListOpts(nil)
|
func AddFlags(flags *pflag.FlagSet) *ContainerOptions {
|
||||||
flDNS = opts.NewListOpts(opts.ValidateIPAddress)
|
copts := &ContainerOptions{
|
||||||
flDNSSearch = opts.NewListOpts(opts.ValidateDNSSearch)
|
flAttach: opts.NewListOpts(ValidateAttach),
|
||||||
flDNSOptions = opts.NewListOpts(nil)
|
flVolumes: opts.NewListOpts(nil),
|
||||||
flExtraHosts = opts.NewListOpts(ValidateExtraHost)
|
flTmpfs: opts.NewListOpts(nil),
|
||||||
flVolumesFrom = opts.NewListOpts(nil)
|
flBlkioWeightDevice: NewWeightdeviceOpt(ValidateWeightDevice),
|
||||||
flEnvFile = opts.NewListOpts(nil)
|
flDeviceReadBps: NewThrottledeviceOpt(ValidateThrottleBpsDevice),
|
||||||
flCapAdd = opts.NewListOpts(nil)
|
flDeviceWriteBps: NewThrottledeviceOpt(ValidateThrottleBpsDevice),
|
||||||
flCapDrop = opts.NewListOpts(nil)
|
flLinks: opts.NewListOpts(ValidateLink),
|
||||||
flGroupAdd = opts.NewListOpts(nil)
|
flAliases: opts.NewListOpts(nil),
|
||||||
flSecurityOpt = opts.NewListOpts(nil)
|
flDeviceReadIOps: NewThrottledeviceOpt(ValidateThrottleIOpsDevice),
|
||||||
flStorageOpt = opts.NewListOpts(nil)
|
flDeviceWriteIOps: NewThrottledeviceOpt(ValidateThrottleIOpsDevice),
|
||||||
flLabelsFile = opts.NewListOpts(nil)
|
flEnv: opts.NewListOpts(ValidateEnv),
|
||||||
flLoggingOpts = opts.NewListOpts(nil)
|
flLabels: opts.NewListOpts(ValidateEnv),
|
||||||
flPrivileged = cmd.Bool([]string{"-privileged"}, false, "Give extended privileges to this container")
|
flDevices: opts.NewListOpts(ValidateDevice),
|
||||||
flPidMode = cmd.String([]string{"-pid"}, "", "PID namespace to use")
|
|
||||||
flUTSMode = cmd.String([]string{"-uts"}, "", "UTS namespace to use")
|
|
||||||
flUsernsMode = cmd.String([]string{"-userns"}, "", "User namespace to use")
|
|
||||||
flPublishAll = cmd.Bool([]string{"P", "-publish-all"}, false, "Publish all exposed ports to random ports")
|
|
||||||
flStdin = cmd.Bool([]string{"i", "-interactive"}, false, "Keep STDIN open even if not attached")
|
|
||||||
flTty = cmd.Bool([]string{"t", "-tty"}, false, "Allocate a pseudo-TTY")
|
|
||||||
flOomKillDisable = cmd.Bool([]string{"-oom-kill-disable"}, false, "Disable OOM Killer")
|
|
||||||
flOomScoreAdj = cmd.Int([]string{"-oom-score-adj"}, 0, "Tune host's OOM preferences (-1000 to 1000)")
|
|
||||||
flContainerIDFile = cmd.String([]string{"-cidfile"}, "", "Write the container ID to the file")
|
|
||||||
flEntrypoint = cmd.String([]string{"-entrypoint"}, "", "Overwrite the default ENTRYPOINT of the image")
|
|
||||||
flHostname = cmd.String([]string{"h", "-hostname"}, "", "Container host name")
|
|
||||||
flMemoryString = cmd.String([]string{"m", "-memory"}, "", "Memory limit")
|
|
||||||
flMemoryReservation = cmd.String([]string{"-memory-reservation"}, "", "Memory soft limit")
|
|
||||||
flMemorySwap = cmd.String([]string{"-memory-swap"}, "", "Swap limit equal to memory plus swap: '-1' to enable unlimited swap")
|
|
||||||
flKernelMemory = cmd.String([]string{"-kernel-memory"}, "", "Kernel memory limit")
|
|
||||||
flUser = cmd.String([]string{"u", "-user"}, "", "Username or UID (format: <name|uid>[:<group|gid>])")
|
|
||||||
flWorkingDir = cmd.String([]string{"w", "-workdir"}, "", "Working directory inside the container")
|
|
||||||
flCPUShares = cmd.Int64([]string{"c", "-cpu-shares"}, 0, "CPU shares (relative weight)")
|
|
||||||
flCPUPercent = cmd.Int64([]string{"-cpu-percent"}, 0, "CPU percent (Windows only)")
|
|
||||||
flCPUPeriod = cmd.Int64([]string{"-cpu-period"}, 0, "Limit CPU CFS (Completely Fair Scheduler) period")
|
|
||||||
flCPUQuota = cmd.Int64([]string{"-cpu-quota"}, 0, "Limit CPU CFS (Completely Fair Scheduler) quota")
|
|
||||||
flCpusetCpus = cmd.String([]string{"-cpuset-cpus"}, "", "CPUs in which to allow execution (0-3, 0,1)")
|
|
||||||
flCpusetMems = cmd.String([]string{"-cpuset-mems"}, "", "MEMs in which to allow execution (0-3, 0,1)")
|
|
||||||
flBlkioWeight = cmd.Uint16([]string{"-blkio-weight"}, 0, "Block IO (relative weight), between 10 and 1000")
|
|
||||||
flIOMaxBandwidth = cmd.String([]string{"-io-maxbandwidth"}, "", "Maximum IO bandwidth limit for the system drive (Windows only)")
|
|
||||||
flIOMaxIOps = cmd.Uint64([]string{"-io-maxiops"}, 0, "Maximum IOps limit for the system drive (Windows only)")
|
|
||||||
flSwappiness = cmd.Int64([]string{"-memory-swappiness"}, -1, "Tune container memory swappiness (0 to 100)")
|
|
||||||
flNetMode = cmd.String([]string{"-net"}, "default", "Connect a container to a network")
|
|
||||||
flMacAddress = cmd.String([]string{"-mac-address"}, "", "Container MAC address (e.g. 92:d0:c6:0a:29:33)")
|
|
||||||
flIPv4Address = cmd.String([]string{"-ip"}, "", "Container IPv4 address (e.g. 172.30.100.104)")
|
|
||||||
flIPv6Address = cmd.String([]string{"-ip6"}, "", "Container IPv6 address (e.g. 2001:db8::33)")
|
|
||||||
flIpcMode = cmd.String([]string{"-ipc"}, "", "IPC namespace to use")
|
|
||||||
flPidsLimit = cmd.Int64([]string{"-pids-limit"}, 0, "Tune container pids limit (set -1 for unlimited)")
|
|
||||||
flRestartPolicy = cmd.String([]string{"-restart"}, "no", "Restart policy to apply when a container exits")
|
|
||||||
flReadonlyRootfs = cmd.Bool([]string{"-read-only"}, false, "Mount the container's root filesystem as read only")
|
|
||||||
flLoggingDriver = cmd.String([]string{"-log-driver"}, "", "Logging driver for container")
|
|
||||||
flCgroupParent = cmd.String([]string{"-cgroup-parent"}, "", "Optional parent cgroup for the container")
|
|
||||||
flVolumeDriver = cmd.String([]string{"-volume-driver"}, "", "Optional volume driver for the container")
|
|
||||||
flStopSignal = cmd.String([]string{"-stop-signal"}, signal.DefaultStopSignal, fmt.Sprintf("Signal to stop a container, %v by default", signal.DefaultStopSignal))
|
|
||||||
flIsolation = cmd.String([]string{"-isolation"}, "", "Container isolation technology")
|
|
||||||
flShmSize = cmd.String([]string{"-shm-size"}, "", "Size of /dev/shm, default value is 64MB")
|
|
||||||
// Healthcheck
|
|
||||||
flNoHealthcheck = cmd.Bool([]string{"-no-healthcheck"}, false, "Disable any container-specified HEALTHCHECK")
|
|
||||||
flHealthCmd = cmd.String([]string{"-health-cmd"}, "", "Command to run to check health")
|
|
||||||
flHealthInterval = cmd.Duration([]string{"-health-interval"}, 0, "Time between running the check")
|
|
||||||
flHealthTimeout = cmd.Duration([]string{"-health-timeout"}, 0, "Maximum time to allow one check to run")
|
|
||||||
flHealthRetries = cmd.Int([]string{"-health-retries"}, 0, "Consecutive failures needed to report unhealthy")
|
|
||||||
)
|
|
||||||
|
|
||||||
cmd.Var(&flAttach, []string{"a", "-attach"}, "Attach to STDIN, STDOUT or STDERR")
|
flUlimits: NewUlimitOpt(nil),
|
||||||
cmd.Var(&flBlkioWeightDevice, []string{"-blkio-weight-device"}, "Block IO weight (relative device weight)")
|
flSysctls: opts.NewMapOpts(nil, opts.ValidateSysctl),
|
||||||
cmd.Var(&flDeviceReadBps, []string{"-device-read-bps"}, "Limit read rate (bytes per second) from a device")
|
|
||||||
cmd.Var(&flDeviceWriteBps, []string{"-device-write-bps"}, "Limit write rate (bytes per second) to a device")
|
|
||||||
cmd.Var(&flDeviceReadIOps, []string{"-device-read-iops"}, "Limit read rate (IO per second) from a device")
|
|
||||||
cmd.Var(&flDeviceWriteIOps, []string{"-device-write-iops"}, "Limit write rate (IO per second) to a device")
|
|
||||||
cmd.Var(&flVolumes, []string{"v", "-volume"}, "Bind mount a volume")
|
|
||||||
cmd.Var(&flTmpfs, []string{"-tmpfs"}, "Mount a tmpfs directory")
|
|
||||||
cmd.Var(&flLinks, []string{"-link"}, "Add link to another container")
|
|
||||||
cmd.Var(&flAliases, []string{"-net-alias"}, "Add network-scoped alias for the container")
|
|
||||||
cmd.Var(&flDevices, []string{"-device"}, "Add a host device to the container")
|
|
||||||
cmd.Var(&flLabels, []string{"l", "-label"}, "Set meta data on a container")
|
|
||||||
cmd.Var(&flLabelsFile, []string{"-label-file"}, "Read in a line delimited file of labels")
|
|
||||||
cmd.Var(&flEnv, []string{"e", "-env"}, "Set environment variables")
|
|
||||||
cmd.Var(&flEnvFile, []string{"-env-file"}, "Read in a file of environment variables")
|
|
||||||
cmd.Var(&flPublish, []string{"p", "-publish"}, "Publish a container's port(s) to the host")
|
|
||||||
cmd.Var(&flExpose, []string{"-expose"}, "Expose a port or a range of ports")
|
|
||||||
cmd.Var(&flDNS, []string{"-dns"}, "Set custom DNS servers")
|
|
||||||
cmd.Var(&flDNSSearch, []string{"-dns-search"}, "Set custom DNS search domains")
|
|
||||||
cmd.Var(&flDNSOptions, []string{"-dns-opt"}, "Set DNS options")
|
|
||||||
cmd.Var(&flExtraHosts, []string{"-add-host"}, "Add a custom host-to-IP mapping (host:ip)")
|
|
||||||
cmd.Var(&flVolumesFrom, []string{"-volumes-from"}, "Mount volumes from the specified container(s)")
|
|
||||||
cmd.Var(&flCapAdd, []string{"-cap-add"}, "Add Linux capabilities")
|
|
||||||
cmd.Var(&flCapDrop, []string{"-cap-drop"}, "Drop Linux capabilities")
|
|
||||||
cmd.Var(&flGroupAdd, []string{"-group-add"}, "Add additional groups to join")
|
|
||||||
cmd.Var(&flSecurityOpt, []string{"-security-opt"}, "Security Options")
|
|
||||||
cmd.Var(&flStorageOpt, []string{"-storage-opt"}, "Set storage driver options per container")
|
|
||||||
cmd.Var(flUlimits, []string{"-ulimit"}, "Ulimit options")
|
|
||||||
cmd.Var(flSysctls, []string{"-sysctl"}, "Sysctl options")
|
|
||||||
cmd.Var(&flLoggingOpts, []string{"-log-opt"}, "Log driver options")
|
|
||||||
|
|
||||||
cmd.Require(flag.Min, 1)
|
flPublish: opts.NewListOpts(nil),
|
||||||
|
flExpose: opts.NewListOpts(nil),
|
||||||
|
flDNS: opts.NewListOpts(opts.ValidateIPAddress),
|
||||||
|
flDNSSearch: opts.NewListOpts(opts.ValidateDNSSearch),
|
||||||
|
flDNSOptions: opts.NewListOpts(nil),
|
||||||
|
flExtraHosts: opts.NewListOpts(ValidateExtraHost),
|
||||||
|
flVolumesFrom: opts.NewListOpts(nil),
|
||||||
|
flEnvFile: opts.NewListOpts(nil),
|
||||||
|
flCapAdd: opts.NewListOpts(nil),
|
||||||
|
flCapDrop: opts.NewListOpts(nil),
|
||||||
|
flGroupAdd: opts.NewListOpts(nil),
|
||||||
|
flSecurityOpt: opts.NewListOpts(nil),
|
||||||
|
flStorageOpt: opts.NewListOpts(nil),
|
||||||
|
flLabelsFile: opts.NewListOpts(nil),
|
||||||
|
flLoggingOpts: opts.NewListOpts(nil),
|
||||||
|
|
||||||
if err := cmd.ParseFlags(args, true); err != nil {
|
flPrivileged: flags.Bool("privileged", false, "Give extended privileges to this container"),
|
||||||
return nil, nil, nil, cmd, err
|
flPidMode: flags.String("pid", "", "PID namespace to use"),
|
||||||
|
flUTSMode: flags.String("uts", "", "UTS namespace to use"),
|
||||||
|
flUsernsMode: flags.String("userns", "", "User namespace to use"),
|
||||||
|
flPublishAll: flags.BoolP("publish-all", "P", false, "Publish all exposed ports to random ports"),
|
||||||
|
flStdin: flags.BoolP("interactive", "i", false, "Keep STDIN open even if not attached"),
|
||||||
|
flTty: flags.BoolP("tty", "t", false, "Allocate a pseudo-TTY"),
|
||||||
|
flOomKillDisable: flags.Bool("oom-kill-disable", false, "Disable OOM Killer"),
|
||||||
|
flOomScoreAdj: flags.Int("oom-score-adj", 0, "Tune host's OOM preferences (-1000 to 1000)"),
|
||||||
|
flContainerIDFile: flags.String("cidfile", "", "Write the container ID to the file"),
|
||||||
|
flEntrypoint: flags.String("entrypoint", "", "Overwrite the default ENTRYPOINT of the image"),
|
||||||
|
flHostname: flags.StringP("hostname", "h", "", "Container host name"),
|
||||||
|
flMemoryString: flags.StringP("memory", "m", "", "Memory limit"),
|
||||||
|
flMemoryReservation: flags.String("memory-reservation", "", "Memory soft limit"),
|
||||||
|
flMemorySwap: flags.String("memory-swap", "", "Swap limit equal to memory plus swap: '-1' to enable unlimited swap"),
|
||||||
|
flKernelMemory: flags.String("kernel-memory", "", "Kernel memory limit"),
|
||||||
|
flUser: flags.StringP("user", "u", "", "Username or UID (format: <name|uid>[:<group|gid>])"),
|
||||||
|
flWorkingDir: flags.StringP("workdir", "w", "", "Working directory inside the container"),
|
||||||
|
flCPUShares: flags.Int64P("cpu-shares", "c", 0, "CPU shares (relative weight)"),
|
||||||
|
flCPUPercent: flags.Int64("cpu-percent", 0, "CPU percent (Windows only)"),
|
||||||
|
flCPUPeriod: flags.Int64("cpu-period", 0, "Limit CPU CFS (Completely Fair Scheduler) period"),
|
||||||
|
flCPUQuota: flags.Int64("cpu-quota", 0, "Limit CPU CFS (Completely Fair Scheduler) quota"),
|
||||||
|
flCpusetCpus: flags.String("cpuset-cpus", "", "CPUs in which to allow execution (0-3, 0,1)"),
|
||||||
|
flCpusetMems: flags.String("cpuset-mems", "", "MEMs in which to allow execution (0-3, 0,1)"),
|
||||||
|
flBlkioWeight: flags.Uint16("blkio-weight", 0, "Block IO (relative weight), between 10 and 1000"),
|
||||||
|
flIOMaxBandwidth: flags.String("io-maxbandwidth", "", "Maximum IO bandwidth limit for the system drive (Windows only)"),
|
||||||
|
flIOMaxIOps: flags.Uint64("io-maxiops", 0, "Maximum IOps limit for the system drive (Windows only)"),
|
||||||
|
flSwappiness: flags.Int64("memory-swappiness", -1, "Tune container memory swappiness (0 to 100)"),
|
||||||
|
flNetMode: flags.String("net", "default", "Connect a container to a network"),
|
||||||
|
flMacAddress: flags.String("mac-address", "", "Container MAC address (e.g. 92:d0:c6:0a:29:33)"),
|
||||||
|
flIPv4Address: flags.String("ip", "", "Container IPv4 address (e.g. 172.30.100.104)"),
|
||||||
|
flIPv6Address: flags.String("ip6", "", "Container IPv6 address (e.g. 2001:db8::33)"),
|
||||||
|
flIpcMode: flags.String("ipc", "", "IPC namespace to use"),
|
||||||
|
flPidsLimit: flags.Int64("pids-limit", 0, "Tune container pids limit (set -1 for unlimited)"),
|
||||||
|
flRestartPolicy: flags.String("restart", "no", "Restart policy to apply when a container exits"),
|
||||||
|
flReadonlyRootfs: flags.Bool("read-only", false, "Mount the container's root filesystem as read only"),
|
||||||
|
flLoggingDriver: flags.String("log-driver", "", "Logging driver for container"),
|
||||||
|
flCgroupParent: flags.String("cgroup-parent", "", "Optional parent cgroup for the container"),
|
||||||
|
flVolumeDriver: flags.String("volume-driver", "", "Optional volume driver for the container"),
|
||||||
|
flStopSignal: flags.String("stop-signal", signal.DefaultStopSignal, fmt.Sprintf("Signal to stop a container, %v by default", signal.DefaultStopSignal)),
|
||||||
|
flIsolation: flags.String("isolation", "", "Container isolation technology"),
|
||||||
|
flShmSize: flags.String("shm-size", "", "Size of /dev/shm, default value is 64MB"),
|
||||||
|
flNoHealthcheck: cmd.Bool([]string{"-no-healthcheck"}, false, "Disable any container-specified HEALTHCHECK"),
|
||||||
|
flHealthCmd: cmd.String([]string{"-health-cmd"}, "", "Command to run to check health"),
|
||||||
|
flHealthInterval: cmd.Duration([]string{"-health-interval"}, 0, "Time between running the check"),
|
||||||
|
flHealthTimeout: cmd.Duration([]string{"-health-timeout"}, 0, "Maximum time to allow one check to run"),
|
||||||
|
flHealthRetries: cmd.Int([]string{"-health-retries"}, 0, "Consecutive failures needed to report unhealthy"),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
flags.VarP(&copts.flAttach, "attach", "a", "Attach to STDIN, STDOUT or STDERR")
|
||||||
|
flags.Var(&copts.flBlkioWeightDevice, "blkio-weight-device", "Block IO weight (relative device weight)")
|
||||||
|
flags.Var(&copts.flDeviceReadBps, "device-read-bps", "Limit read rate (bytes per second) from a device")
|
||||||
|
flags.Var(&copts.flDeviceWriteBps, "device-write-bps", "Limit write rate (bytes per second) to a device")
|
||||||
|
flags.Var(&copts.flDeviceReadIOps, "device-read-iops", "Limit read rate (IO per second) from a device")
|
||||||
|
flags.Var(&copts.flDeviceWriteIOps, "device-write-iops", "Limit write rate (IO per second) to a device")
|
||||||
|
flags.VarP(&copts.flVolumes, "volume", "v", "Bind mount a volume")
|
||||||
|
flags.Var(&copts.flTmpfs, "tmpfs", "Mount a tmpfs directory")
|
||||||
|
flags.Var(&copts.flLinks, "link", "Add link to another container")
|
||||||
|
flags.Var(&copts.flAliases, "net-alias", "Add network-scoped alias for the container")
|
||||||
|
flags.Var(&copts.flDevices, "device", "Add a host device to the container")
|
||||||
|
flags.VarP(&copts.flLabels, "label", "l", "Set meta data on a container")
|
||||||
|
flags.Var(&copts.flLabelsFile, "label-file", "Read in a line delimited file of labels")
|
||||||
|
flags.VarP(&copts.flEnv, "env", "e", "Set environment variables")
|
||||||
|
flags.Var(&copts.flEnvFile, "env-file", "Read in a file of environment variables")
|
||||||
|
flags.VarP(&copts.flPublish, "publish", "p", "Publish a container's port(s) to the host")
|
||||||
|
flags.Var(&copts.flExpose, "expose", "Expose a port or a range of ports")
|
||||||
|
flags.Var(&copts.flDNS, "dns", "Set custom DNS servers")
|
||||||
|
flags.Var(&copts.flDNSSearch, "dns-search", "Set custom DNS search domains")
|
||||||
|
flags.Var(&copts.flDNSOptions, "dns-opt", "Set DNS options")
|
||||||
|
flags.Var(&copts.flExtraHosts, "add-host", "Add a custom host-to-IP mapping (host:ip)")
|
||||||
|
flags.Var(&copts.flVolumesFrom, "volumes-from", "Mount volumes from the specified container(s)")
|
||||||
|
flags.Var(&copts.flCapAdd, "cap-add", "Add Linux capabilities")
|
||||||
|
flags.Var(&copts.flCapDrop, "cap-drop", "Drop Linux capabilities")
|
||||||
|
flags.Var(&copts.flGroupAdd, "group-add", "Add additional groups to join")
|
||||||
|
flags.Var(&copts.flSecurityOpt, "security-opt", "Security Options")
|
||||||
|
flags.Var(&copts.flStorageOpt, "storage-opt", "Set storage driver options per container")
|
||||||
|
flags.Var(copts.flUlimits, "ulimit", "Ulimit options")
|
||||||
|
flags.Var(copts.flSysctls, "sysctl", "Sysctl options")
|
||||||
|
flags.Var(&copts.flLoggingOpts, "log-opt", "Log driver options")
|
||||||
|
|
||||||
|
return copts
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse parses the args for the specified command and generates a Config,
|
||||||
|
// a HostConfig and returns them with the specified command.
|
||||||
|
// If the specified args are not valid, it will return an error.
|
||||||
|
func Parse(flags *pflag.FlagSet, copts *ContainerOptions) (*container.Config, *container.HostConfig, *networktypes.NetworkingConfig, error) {
|
||||||
|
|
||||||
var (
|
var (
|
||||||
attachStdin = flAttach.Get("stdin")
|
attachStdin = copts.flAttach.Get("stdin")
|
||||||
attachStdout = flAttach.Get("stdout")
|
attachStdout = copts.flAttach.Get("stdout")
|
||||||
attachStderr = flAttach.Get("stderr")
|
attachStderr = copts.flAttach.Get("stderr")
|
||||||
)
|
)
|
||||||
|
|
||||||
// Validate the input mac address
|
// Validate the input mac address
|
||||||
if *flMacAddress != "" {
|
if *copts.flMacAddress != "" {
|
||||||
if _, err := ValidateMACAddress(*flMacAddress); err != nil {
|
if _, err := ValidateMACAddress(*copts.flMacAddress); err != nil {
|
||||||
return nil, nil, nil, cmd, fmt.Errorf("%s is not a valid mac address", *flMacAddress)
|
return nil, nil, nil, fmt.Errorf("%s is not a valid mac address", *copts.flMacAddress)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if *flStdin {
|
if *copts.flStdin {
|
||||||
attachStdin = true
|
attachStdin = true
|
||||||
}
|
}
|
||||||
// If -a is not set, attach to stdout and stderr
|
// If -a is not set, attach to stdout and stderr
|
||||||
if flAttach.Len() == 0 {
|
if copts.flAttach.Len() == 0 {
|
||||||
attachStdout = true
|
attachStdout = true
|
||||||
attachStderr = true
|
attachStderr = true
|
||||||
}
|
}
|
||||||
|
@ -169,83 +254,83 @@ func Parse(cmd *flag.FlagSet, args []string) (*container.Config, *container.Host
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
var flMemory int64
|
var flMemory int64
|
||||||
if *flMemoryString != "" {
|
if *copts.flMemoryString != "" {
|
||||||
flMemory, err = units.RAMInBytes(*flMemoryString)
|
flMemory, err = units.RAMInBytes(*copts.flMemoryString)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, nil, cmd, err
|
return nil, nil, nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var MemoryReservation int64
|
var MemoryReservation int64
|
||||||
if *flMemoryReservation != "" {
|
if *copts.flMemoryReservation != "" {
|
||||||
MemoryReservation, err = units.RAMInBytes(*flMemoryReservation)
|
MemoryReservation, err = units.RAMInBytes(*copts.flMemoryReservation)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, nil, cmd, err
|
return nil, nil, nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var memorySwap int64
|
var memorySwap int64
|
||||||
if *flMemorySwap != "" {
|
if *copts.flMemorySwap != "" {
|
||||||
if *flMemorySwap == "-1" {
|
if *copts.flMemorySwap == "-1" {
|
||||||
memorySwap = -1
|
memorySwap = -1
|
||||||
} else {
|
} else {
|
||||||
memorySwap, err = units.RAMInBytes(*flMemorySwap)
|
memorySwap, err = units.RAMInBytes(*copts.flMemorySwap)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, nil, cmd, err
|
return nil, nil, nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var KernelMemory int64
|
var KernelMemory int64
|
||||||
if *flKernelMemory != "" {
|
if *copts.flKernelMemory != "" {
|
||||||
KernelMemory, err = units.RAMInBytes(*flKernelMemory)
|
KernelMemory, err = units.RAMInBytes(*copts.flKernelMemory)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, nil, cmd, err
|
return nil, nil, nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
swappiness := *flSwappiness
|
swappiness := *copts.flSwappiness
|
||||||
if swappiness != -1 && (swappiness < 0 || swappiness > 100) {
|
if swappiness != -1 && (swappiness < 0 || swappiness > 100) {
|
||||||
return nil, nil, nil, cmd, fmt.Errorf("invalid value: %d. Valid memory swappiness range is 0-100", swappiness)
|
return nil, nil, nil, fmt.Errorf("invalid value: %d. Valid memory swappiness range is 0-100", swappiness)
|
||||||
}
|
}
|
||||||
|
|
||||||
var shmSize int64
|
var shmSize int64
|
||||||
if *flShmSize != "" {
|
if *copts.flShmSize != "" {
|
||||||
shmSize, err = units.RAMInBytes(*flShmSize)
|
shmSize, err = units.RAMInBytes(*copts.flShmSize)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, nil, cmd, err
|
return nil, nil, nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO FIXME units.RAMInBytes should have a uint64 version
|
// TODO FIXME units.RAMInBytes should have a uint64 version
|
||||||
var maxIOBandwidth int64
|
var maxIOBandwidth int64
|
||||||
if *flIOMaxBandwidth != "" {
|
if *copts.flIOMaxBandwidth != "" {
|
||||||
maxIOBandwidth, err = units.RAMInBytes(*flIOMaxBandwidth)
|
maxIOBandwidth, err = units.RAMInBytes(*copts.flIOMaxBandwidth)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, nil, cmd, err
|
return nil, nil, nil, err
|
||||||
}
|
}
|
||||||
if maxIOBandwidth < 0 {
|
if maxIOBandwidth < 0 {
|
||||||
return nil, nil, nil, cmd, fmt.Errorf("invalid value: %s. Maximum IO Bandwidth must be positive", *flIOMaxBandwidth)
|
return nil, nil, nil, fmt.Errorf("invalid value: %s. Maximum IO Bandwidth must be positive", *copts.flIOMaxBandwidth)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var binds []string
|
var binds []string
|
||||||
// add any bind targets to the list of container volumes
|
// add any bind targets to the list of container volumes
|
||||||
for bind := range flVolumes.GetMap() {
|
for bind := range copts.flVolumes.GetMap() {
|
||||||
if arr := volumeSplitN(bind, 2); len(arr) > 1 {
|
if arr := volumeSplitN(bind, 2); len(arr) > 1 {
|
||||||
// after creating the bind mount we want to delete it from the flVolumes values because
|
// after creating the bind mount we want to delete it from the copts.flVolumes values because
|
||||||
// we do not want bind mounts being committed to image configs
|
// we do not want bind mounts being committed to image configs
|
||||||
binds = append(binds, bind)
|
binds = append(binds, bind)
|
||||||
flVolumes.Delete(bind)
|
copts.flVolumes.Delete(bind)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Can't evaluate options passed into --tmpfs until we actually mount
|
// Can't evaluate options passed into --tmpfs until we actually mount
|
||||||
tmpfs := make(map[string]string)
|
tmpfs := make(map[string]string)
|
||||||
for _, t := range flTmpfs.GetAll() {
|
for _, t := range copts.flTmpfs.GetAll() {
|
||||||
if arr := strings.SplitN(t, ":", 2); len(arr) > 1 {
|
if arr := strings.SplitN(t, ":", 2); len(arr) > 1 {
|
||||||
if _, _, err := mount.ParseTmpfsOptions(arr[1]); err != nil {
|
if _, _, err := mount.ParseTmpfsOptions(arr[1]); err != nil {
|
||||||
return nil, nil, nil, cmd, err
|
return nil, nil, nil, err
|
||||||
}
|
}
|
||||||
tmpfs[arr[0]] = arr[1]
|
tmpfs[arr[0]] = arr[1]
|
||||||
} else {
|
} else {
|
||||||
|
@ -254,27 +339,25 @@ func Parse(cmd *flag.FlagSet, args []string) (*container.Config, *container.Host
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
parsedArgs = cmd.Args()
|
|
||||||
runCmd strslice.StrSlice
|
runCmd strslice.StrSlice
|
||||||
entrypoint strslice.StrSlice
|
entrypoint strslice.StrSlice
|
||||||
image = cmd.Arg(0)
|
|
||||||
)
|
)
|
||||||
if len(parsedArgs) > 1 {
|
if len(copts.Args) > 0 {
|
||||||
runCmd = strslice.StrSlice(parsedArgs[1:])
|
runCmd = strslice.StrSlice(copts.Args)
|
||||||
}
|
}
|
||||||
if *flEntrypoint != "" {
|
if *copts.flEntrypoint != "" {
|
||||||
entrypoint = strslice.StrSlice{*flEntrypoint}
|
entrypoint = strslice.StrSlice{*copts.flEntrypoint}
|
||||||
}
|
}
|
||||||
|
|
||||||
ports, portBindings, err := nat.ParsePortSpecs(flPublish.GetAll())
|
ports, portBindings, err := nat.ParsePortSpecs(copts.flPublish.GetAll())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, nil, cmd, err
|
return nil, nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Merge in exposed ports to the map of published ports
|
// Merge in exposed ports to the map of published ports
|
||||||
for _, e := range flExpose.GetAll() {
|
for _, e := range copts.flExpose.GetAll() {
|
||||||
if strings.Contains(e, ":") {
|
if strings.Contains(e, ":") {
|
||||||
return nil, nil, nil, cmd, fmt.Errorf("invalid port format for --expose: %s", e)
|
return nil, nil, nil, fmt.Errorf("invalid port format for --expose: %s", e)
|
||||||
}
|
}
|
||||||
//support two formats for expose, original format <portnum>/[<proto>] or <startport-endport>/[<proto>]
|
//support two formats for expose, original format <portnum>/[<proto>] or <startport-endport>/[<proto>]
|
||||||
proto, port := nat.SplitProtoPort(e)
|
proto, port := nat.SplitProtoPort(e)
|
||||||
|
@ -282,12 +365,12 @@ func Parse(cmd *flag.FlagSet, args []string) (*container.Config, *container.Host
|
||||||
//if expose a port, the start and end port are the same
|
//if expose a port, the start and end port are the same
|
||||||
start, end, err := nat.ParsePortRange(port)
|
start, end, err := nat.ParsePortRange(port)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, nil, cmd, fmt.Errorf("invalid range format for --expose: %s, error: %s", e, err)
|
return nil, nil, nil, fmt.Errorf("invalid range format for --expose: %s, error: %s", e, err)
|
||||||
}
|
}
|
||||||
for i := start; i <= end; i++ {
|
for i := start; i <= end; i++ {
|
||||||
p, err := nat.NewPort(proto, strconv.FormatUint(i, 10))
|
p, err := nat.NewPort(proto, strconv.FormatUint(i, 10))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, nil, cmd, err
|
return nil, nil, nil, err
|
||||||
}
|
}
|
||||||
if _, exists := ports[p]; !exists {
|
if _, exists := ports[p]; !exists {
|
||||||
ports[p] = struct{}{}
|
ports[p] = struct{}{}
|
||||||
|
@ -297,64 +380,64 @@ func Parse(cmd *flag.FlagSet, args []string) (*container.Config, *container.Host
|
||||||
|
|
||||||
// parse device mappings
|
// parse device mappings
|
||||||
deviceMappings := []container.DeviceMapping{}
|
deviceMappings := []container.DeviceMapping{}
|
||||||
for _, device := range flDevices.GetAll() {
|
for _, device := range copts.flDevices.GetAll() {
|
||||||
deviceMapping, err := ParseDevice(device)
|
deviceMapping, err := ParseDevice(device)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, nil, cmd, err
|
return nil, nil, nil, err
|
||||||
}
|
}
|
||||||
deviceMappings = append(deviceMappings, deviceMapping)
|
deviceMappings = append(deviceMappings, deviceMapping)
|
||||||
}
|
}
|
||||||
|
|
||||||
// collect all the environment variables for the container
|
// collect all the environment variables for the container
|
||||||
envVariables, err := readKVStrings(flEnvFile.GetAll(), flEnv.GetAll())
|
envVariables, err := readKVStrings(copts.flEnvFile.GetAll(), copts.flEnv.GetAll())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, nil, cmd, err
|
return nil, nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// collect all the labels for the container
|
// collect all the labels for the container
|
||||||
labels, err := readKVStrings(flLabelsFile.GetAll(), flLabels.GetAll())
|
labels, err := readKVStrings(copts.flLabelsFile.GetAll(), copts.flLabels.GetAll())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, nil, cmd, err
|
return nil, nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
ipcMode := container.IpcMode(*flIpcMode)
|
ipcMode := container.IpcMode(*copts.flIpcMode)
|
||||||
if !ipcMode.Valid() {
|
if !ipcMode.Valid() {
|
||||||
return nil, nil, nil, cmd, fmt.Errorf("--ipc: invalid IPC mode")
|
return nil, nil, nil, fmt.Errorf("--ipc: invalid IPC mode")
|
||||||
}
|
}
|
||||||
|
|
||||||
pidMode := container.PidMode(*flPidMode)
|
pidMode := container.PidMode(*copts.flPidMode)
|
||||||
if !pidMode.Valid() {
|
if !pidMode.Valid() {
|
||||||
return nil, nil, nil, cmd, fmt.Errorf("--pid: invalid PID mode")
|
return nil, nil, nil, fmt.Errorf("--pid: invalid PID mode")
|
||||||
}
|
}
|
||||||
|
|
||||||
utsMode := container.UTSMode(*flUTSMode)
|
utsMode := container.UTSMode(*copts.flUTSMode)
|
||||||
if !utsMode.Valid() {
|
if !utsMode.Valid() {
|
||||||
return nil, nil, nil, cmd, fmt.Errorf("--uts: invalid UTS mode")
|
return nil, nil, nil, fmt.Errorf("--uts: invalid UTS mode")
|
||||||
}
|
}
|
||||||
|
|
||||||
usernsMode := container.UsernsMode(*flUsernsMode)
|
usernsMode := container.UsernsMode(*copts.flUsernsMode)
|
||||||
if !usernsMode.Valid() {
|
if !usernsMode.Valid() {
|
||||||
return nil, nil, nil, cmd, fmt.Errorf("--userns: invalid USER mode")
|
return nil, nil, nil, fmt.Errorf("--userns: invalid USER mode")
|
||||||
}
|
}
|
||||||
|
|
||||||
restartPolicy, err := ParseRestartPolicy(*flRestartPolicy)
|
restartPolicy, err := ParseRestartPolicy(*copts.flRestartPolicy)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, nil, cmd, err
|
return nil, nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
loggingOpts, err := parseLoggingOpts(*flLoggingDriver, flLoggingOpts.GetAll())
|
loggingOpts, err := parseLoggingOpts(*copts.flLoggingDriver, copts.flLoggingOpts.GetAll())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, nil, cmd, err
|
return nil, nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
securityOpts, err := parseSecurityOpts(flSecurityOpt.GetAll())
|
securityOpts, err := parseSecurityOpts(copts.flSecurityOpt.GetAll())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, nil, cmd, err
|
return nil, nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
storageOpts, err := parseStorageOpts(flStorageOpt.GetAll())
|
storageOpts, err := parseStorageOpts(copts.flStorageOpt.GetAll())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, nil, cmd, err
|
return nil, nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Healthcheck
|
// Healthcheck
|
||||||
|
@ -391,96 +474,96 @@ func Parse(cmd *flag.FlagSet, args []string) (*container.Config, *container.Host
|
||||||
}
|
}
|
||||||
|
|
||||||
resources := container.Resources{
|
resources := container.Resources{
|
||||||
CgroupParent: *flCgroupParent,
|
CgroupParent: *copts.flCgroupParent,
|
||||||
Memory: flMemory,
|
Memory: flMemory,
|
||||||
MemoryReservation: MemoryReservation,
|
MemoryReservation: MemoryReservation,
|
||||||
MemorySwap: memorySwap,
|
MemorySwap: memorySwap,
|
||||||
MemorySwappiness: flSwappiness,
|
MemorySwappiness: copts.flSwappiness,
|
||||||
KernelMemory: KernelMemory,
|
KernelMemory: KernelMemory,
|
||||||
OomKillDisable: flOomKillDisable,
|
OomKillDisable: copts.flOomKillDisable,
|
||||||
CPUPercent: *flCPUPercent,
|
CPUPercent: *copts.flCPUPercent,
|
||||||
CPUShares: *flCPUShares,
|
CPUShares: *copts.flCPUShares,
|
||||||
CPUPeriod: *flCPUPeriod,
|
CPUPeriod: *copts.flCPUPeriod,
|
||||||
CpusetCpus: *flCpusetCpus,
|
CpusetCpus: *copts.flCpusetCpus,
|
||||||
CpusetMems: *flCpusetMems,
|
CpusetMems: *copts.flCpusetMems,
|
||||||
CPUQuota: *flCPUQuota,
|
CPUQuota: *copts.flCPUQuota,
|
||||||
PidsLimit: *flPidsLimit,
|
PidsLimit: *copts.flPidsLimit,
|
||||||
BlkioWeight: *flBlkioWeight,
|
BlkioWeight: *copts.flBlkioWeight,
|
||||||
BlkioWeightDevice: flBlkioWeightDevice.GetList(),
|
BlkioWeightDevice: copts.flBlkioWeightDevice.GetList(),
|
||||||
BlkioDeviceReadBps: flDeviceReadBps.GetList(),
|
BlkioDeviceReadBps: copts.flDeviceReadBps.GetList(),
|
||||||
BlkioDeviceWriteBps: flDeviceWriteBps.GetList(),
|
BlkioDeviceWriteBps: copts.flDeviceWriteBps.GetList(),
|
||||||
BlkioDeviceReadIOps: flDeviceReadIOps.GetList(),
|
BlkioDeviceReadIOps: copts.flDeviceReadIOps.GetList(),
|
||||||
BlkioDeviceWriteIOps: flDeviceWriteIOps.GetList(),
|
BlkioDeviceWriteIOps: copts.flDeviceWriteIOps.GetList(),
|
||||||
IOMaximumIOps: *flIOMaxIOps,
|
IOMaximumIOps: *copts.flIOMaxIOps,
|
||||||
IOMaximumBandwidth: uint64(maxIOBandwidth),
|
IOMaximumBandwidth: uint64(maxIOBandwidth),
|
||||||
Ulimits: flUlimits.GetList(),
|
Ulimits: copts.flUlimits.GetList(),
|
||||||
Devices: deviceMappings,
|
Devices: deviceMappings,
|
||||||
}
|
}
|
||||||
|
|
||||||
config := &container.Config{
|
config := &container.Config{
|
||||||
Hostname: *flHostname,
|
Hostname: *copts.flHostname,
|
||||||
ExposedPorts: ports,
|
ExposedPorts: ports,
|
||||||
User: *flUser,
|
User: *copts.flUser,
|
||||||
Tty: *flTty,
|
Tty: *copts.flTty,
|
||||||
// TODO: deprecated, it comes from -n, --networking
|
// TODO: deprecated, it comes from -n, --networking
|
||||||
// it's still needed internally to set the network to disabled
|
// it's still needed internally to set the network to disabled
|
||||||
// if e.g. bridge is none in daemon opts, and in inspect
|
// if e.g. bridge is none in daemon opts, and in inspect
|
||||||
NetworkDisabled: false,
|
NetworkDisabled: false,
|
||||||
OpenStdin: *flStdin,
|
OpenStdin: *copts.flStdin,
|
||||||
AttachStdin: attachStdin,
|
AttachStdin: attachStdin,
|
||||||
AttachStdout: attachStdout,
|
AttachStdout: attachStdout,
|
||||||
AttachStderr: attachStderr,
|
AttachStderr: attachStderr,
|
||||||
Env: envVariables,
|
Env: envVariables,
|
||||||
Cmd: runCmd,
|
Cmd: runCmd,
|
||||||
Image: image,
|
Image: copts.Image,
|
||||||
Volumes: flVolumes.GetMap(),
|
Volumes: copts.flVolumes.GetMap(),
|
||||||
MacAddress: *flMacAddress,
|
MacAddress: *copts.flMacAddress,
|
||||||
Entrypoint: entrypoint,
|
Entrypoint: entrypoint,
|
||||||
WorkingDir: *flWorkingDir,
|
WorkingDir: *copts.flWorkingDir,
|
||||||
Labels: ConvertKVStringsToMap(labels),
|
Labels: ConvertKVStringsToMap(labels),
|
||||||
Healthcheck: healthConfig,
|
Healthcheck: healthConfig,
|
||||||
}
|
}
|
||||||
if cmd.IsSet("-stop-signal") {
|
if flags.Changed("stop-signal") {
|
||||||
config.StopSignal = *flStopSignal
|
config.StopSignal = *copts.flStopSignal
|
||||||
}
|
}
|
||||||
|
|
||||||
hostConfig := &container.HostConfig{
|
hostConfig := &container.HostConfig{
|
||||||
Binds: binds,
|
Binds: binds,
|
||||||
ContainerIDFile: *flContainerIDFile,
|
ContainerIDFile: *copts.flContainerIDFile,
|
||||||
OomScoreAdj: *flOomScoreAdj,
|
OomScoreAdj: *copts.flOomScoreAdj,
|
||||||
Privileged: *flPrivileged,
|
Privileged: *copts.flPrivileged,
|
||||||
PortBindings: portBindings,
|
PortBindings: portBindings,
|
||||||
Links: flLinks.GetAll(),
|
Links: copts.flLinks.GetAll(),
|
||||||
PublishAllPorts: *flPublishAll,
|
PublishAllPorts: *copts.flPublishAll,
|
||||||
// Make sure the dns fields are never nil.
|
// Make sure the dns fields are never nil.
|
||||||
// New containers don't ever have those fields nil,
|
// New containers don't ever have those fields nil,
|
||||||
// but pre created containers can still have those nil values.
|
// but pre created containers can still have those nil values.
|
||||||
// See https://github.com/docker/docker/pull/17779
|
// See https://github.com/docker/docker/pull/17779
|
||||||
// for a more detailed explanation on why we don't want that.
|
// for a more detailed explanation on why we don't want that.
|
||||||
DNS: flDNS.GetAllOrEmpty(),
|
DNS: copts.flDNS.GetAllOrEmpty(),
|
||||||
DNSSearch: flDNSSearch.GetAllOrEmpty(),
|
DNSSearch: copts.flDNSSearch.GetAllOrEmpty(),
|
||||||
DNSOptions: flDNSOptions.GetAllOrEmpty(),
|
DNSOptions: copts.flDNSOptions.GetAllOrEmpty(),
|
||||||
ExtraHosts: flExtraHosts.GetAll(),
|
ExtraHosts: copts.flExtraHosts.GetAll(),
|
||||||
VolumesFrom: flVolumesFrom.GetAll(),
|
VolumesFrom: copts.flVolumesFrom.GetAll(),
|
||||||
NetworkMode: container.NetworkMode(*flNetMode),
|
NetworkMode: container.NetworkMode(*copts.flNetMode),
|
||||||
IpcMode: ipcMode,
|
IpcMode: ipcMode,
|
||||||
PidMode: pidMode,
|
PidMode: pidMode,
|
||||||
UTSMode: utsMode,
|
UTSMode: utsMode,
|
||||||
UsernsMode: usernsMode,
|
UsernsMode: usernsMode,
|
||||||
CapAdd: strslice.StrSlice(flCapAdd.GetAll()),
|
CapAdd: strslice.StrSlice(copts.flCapAdd.GetAll()),
|
||||||
CapDrop: strslice.StrSlice(flCapDrop.GetAll()),
|
CapDrop: strslice.StrSlice(copts.flCapDrop.GetAll()),
|
||||||
GroupAdd: flGroupAdd.GetAll(),
|
GroupAdd: copts.flGroupAdd.GetAll(),
|
||||||
RestartPolicy: restartPolicy,
|
RestartPolicy: restartPolicy,
|
||||||
SecurityOpt: securityOpts,
|
SecurityOpt: securityOpts,
|
||||||
StorageOpt: storageOpts,
|
StorageOpt: storageOpts,
|
||||||
ReadonlyRootfs: *flReadonlyRootfs,
|
ReadonlyRootfs: *copts.flReadonlyRootfs,
|
||||||
LogConfig: container.LogConfig{Type: *flLoggingDriver, Config: loggingOpts},
|
LogConfig: container.LogConfig{Type: *copts.flLoggingDriver, Config: loggingOpts},
|
||||||
VolumeDriver: *flVolumeDriver,
|
VolumeDriver: *copts.flVolumeDriver,
|
||||||
Isolation: container.Isolation(*flIsolation),
|
Isolation: container.Isolation(*copts.flIsolation),
|
||||||
ShmSize: shmSize,
|
ShmSize: shmSize,
|
||||||
Resources: resources,
|
Resources: resources,
|
||||||
Tmpfs: tmpfs,
|
Tmpfs: tmpfs,
|
||||||
Sysctls: flSysctls.GetAll(),
|
Sysctls: copts.flSysctls.GetAll(),
|
||||||
}
|
}
|
||||||
|
|
||||||
// When allocating stdin in attached mode, close stdin at client disconnect
|
// When allocating stdin in attached mode, close stdin at client disconnect
|
||||||
|
@ -492,11 +575,11 @@ func Parse(cmd *flag.FlagSet, args []string) (*container.Config, *container.Host
|
||||||
EndpointsConfig: make(map[string]*networktypes.EndpointSettings),
|
EndpointsConfig: make(map[string]*networktypes.EndpointSettings),
|
||||||
}
|
}
|
||||||
|
|
||||||
if *flIPv4Address != "" || *flIPv6Address != "" {
|
if *copts.flIPv4Address != "" || *copts.flIPv6Address != "" {
|
||||||
networkingConfig.EndpointsConfig[string(hostConfig.NetworkMode)] = &networktypes.EndpointSettings{
|
networkingConfig.EndpointsConfig[string(hostConfig.NetworkMode)] = &networktypes.EndpointSettings{
|
||||||
IPAMConfig: &networktypes.EndpointIPAMConfig{
|
IPAMConfig: &networktypes.EndpointIPAMConfig{
|
||||||
IPv4Address: *flIPv4Address,
|
IPv4Address: *copts.flIPv4Address,
|
||||||
IPv6Address: *flIPv6Address,
|
IPv6Address: *copts.flIPv6Address,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -511,17 +594,17 @@ func Parse(cmd *flag.FlagSet, args []string) (*container.Config, *container.Host
|
||||||
networkingConfig.EndpointsConfig[string(hostConfig.NetworkMode)] = epConfig
|
networkingConfig.EndpointsConfig[string(hostConfig.NetworkMode)] = epConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
if flAliases.Len() > 0 {
|
if copts.flAliases.Len() > 0 {
|
||||||
epConfig := networkingConfig.EndpointsConfig[string(hostConfig.NetworkMode)]
|
epConfig := networkingConfig.EndpointsConfig[string(hostConfig.NetworkMode)]
|
||||||
if epConfig == nil {
|
if epConfig == nil {
|
||||||
epConfig = &networktypes.EndpointSettings{}
|
epConfig = &networktypes.EndpointSettings{}
|
||||||
}
|
}
|
||||||
epConfig.Aliases = make([]string, flAliases.Len())
|
epConfig.Aliases = make([]string, copts.flAliases.Len())
|
||||||
copy(epConfig.Aliases, flAliases.GetAll())
|
copy(epConfig.Aliases, copts.flAliases.GetAll())
|
||||||
networkingConfig.EndpointsConfig[string(hostConfig.NetworkMode)] = epConfig
|
networkingConfig.EndpointsConfig[string(hostConfig.NetworkMode)] = epConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
return config, hostConfig, networkingConfig, cmd, nil
|
return config, hostConfig, networkingConfig, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// reads a file of line terminated key=value pairs, and overrides any keys
|
// reads a file of line terminated key=value pairs, and overrides any keys
|
||||||
|
|
|
@ -11,22 +11,27 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
flag "github.com/docker/docker/pkg/mflag"
|
|
||||||
"github.com/docker/docker/runconfig"
|
"github.com/docker/docker/runconfig"
|
||||||
"github.com/docker/engine-api/types/container"
|
"github.com/docker/engine-api/types/container"
|
||||||
networktypes "github.com/docker/engine-api/types/network"
|
networktypes "github.com/docker/engine-api/types/network"
|
||||||
"github.com/docker/go-connections/nat"
|
"github.com/docker/go-connections/nat"
|
||||||
|
"github.com/spf13/pflag"
|
||||||
)
|
)
|
||||||
|
|
||||||
func parseRun(args []string) (*container.Config, *container.HostConfig, *networktypes.NetworkingConfig, *flag.FlagSet, error) {
|
// TODO: drop FlagSet from return value
|
||||||
cmd := flag.NewFlagSet("run", flag.ContinueOnError)
|
func parseRun(args []string) (*container.Config, *container.HostConfig, *networktypes.NetworkingConfig, error) {
|
||||||
cmd.SetOutput(ioutil.Discard)
|
flags := pflag.NewFlagSet("run", pflag.ContinueOnError)
|
||||||
cmd.Usage = nil
|
flags.SetOutput(ioutil.Discard)
|
||||||
return Parse(cmd, args)
|
flags.Usage = nil
|
||||||
|
copts := AddFlags(flags)
|
||||||
|
if err := flags.Parse(args); err != nil {
|
||||||
|
return nil, nil, nil, err
|
||||||
|
}
|
||||||
|
return Parse(flags, copts)
|
||||||
}
|
}
|
||||||
|
|
||||||
func parse(t *testing.T, args string) (*container.Config, *container.HostConfig, error) {
|
func parse(t *testing.T, args string) (*container.Config, *container.HostConfig, error) {
|
||||||
config, hostConfig, _, _, err := parseRun(strings.Split(args+" ubuntu bash", " "))
|
config, hostConfig, _, err := parseRun(strings.Split(args+" ubuntu bash", " "))
|
||||||
return config, hostConfig, err
|
return config, hostConfig, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -351,7 +356,7 @@ func setupPlatformVolume(u []string, w []string) ([]string, string) {
|
||||||
func TestParseWithMacAddress(t *testing.T) {
|
func TestParseWithMacAddress(t *testing.T) {
|
||||||
invalidMacAddress := "--mac-address=invalidMacAddress"
|
invalidMacAddress := "--mac-address=invalidMacAddress"
|
||||||
validMacAddress := "--mac-address=92:d0:c6:0a:29:33"
|
validMacAddress := "--mac-address=92:d0:c6:0a:29:33"
|
||||||
if _, _, _, _, err := parseRun([]string{invalidMacAddress, "img", "cmd"}); err != nil && err.Error() != "invalidMacAddress is not a valid mac address" {
|
if _, _, _, err := parseRun([]string{invalidMacAddress, "img", "cmd"}); err != nil && err.Error() != "invalidMacAddress is not a valid mac address" {
|
||||||
t.Fatalf("Expected an error with %v mac-address, got %v", invalidMacAddress, err)
|
t.Fatalf("Expected an error with %v mac-address, got %v", invalidMacAddress, err)
|
||||||
}
|
}
|
||||||
if config, _ := mustParse(t, validMacAddress); config.MacAddress != "92:d0:c6:0a:29:33" {
|
if config, _ := mustParse(t, validMacAddress); config.MacAddress != "92:d0:c6:0a:29:33" {
|
||||||
|
@ -362,7 +367,7 @@ func TestParseWithMacAddress(t *testing.T) {
|
||||||
func TestParseWithMemory(t *testing.T) {
|
func TestParseWithMemory(t *testing.T) {
|
||||||
invalidMemory := "--memory=invalid"
|
invalidMemory := "--memory=invalid"
|
||||||
validMemory := "--memory=1G"
|
validMemory := "--memory=1G"
|
||||||
if _, _, _, _, err := parseRun([]string{invalidMemory, "img", "cmd"}); err != nil && err.Error() != "invalid size: 'invalid'" {
|
if _, _, _, err := parseRun([]string{invalidMemory, "img", "cmd"}); err != nil && err.Error() != "invalid size: 'invalid'" {
|
||||||
t.Fatalf("Expected an error with '%v' Memory, got '%v'", invalidMemory, err)
|
t.Fatalf("Expected an error with '%v' Memory, got '%v'", invalidMemory, err)
|
||||||
}
|
}
|
||||||
if _, hostconfig := mustParse(t, validMemory); hostconfig.Memory != 1073741824 {
|
if _, hostconfig := mustParse(t, validMemory); hostconfig.Memory != 1073741824 {
|
||||||
|
@ -374,7 +379,7 @@ func TestParseWithMemorySwap(t *testing.T) {
|
||||||
invalidMemory := "--memory-swap=invalid"
|
invalidMemory := "--memory-swap=invalid"
|
||||||
validMemory := "--memory-swap=1G"
|
validMemory := "--memory-swap=1G"
|
||||||
anotherValidMemory := "--memory-swap=-1"
|
anotherValidMemory := "--memory-swap=-1"
|
||||||
if _, _, _, _, err := parseRun([]string{invalidMemory, "img", "cmd"}); err == nil || err.Error() != "invalid size: 'invalid'" {
|
if _, _, _, err := parseRun([]string{invalidMemory, "img", "cmd"}); err == nil || err.Error() != "invalid size: 'invalid'" {
|
||||||
t.Fatalf("Expected an error with '%v' MemorySwap, got '%v'", invalidMemory, err)
|
t.Fatalf("Expected an error with '%v' MemorySwap, got '%v'", invalidMemory, err)
|
||||||
}
|
}
|
||||||
if _, hostconfig := mustParse(t, validMemory); hostconfig.MemorySwap != 1073741824 {
|
if _, hostconfig := mustParse(t, validMemory); hostconfig.MemorySwap != 1073741824 {
|
||||||
|
@ -427,12 +432,12 @@ func TestParseWithExpose(t *testing.T) {
|
||||||
"8080-8082/tcp": {"8080/tcp", "8081/tcp", "8082/tcp"},
|
"8080-8082/tcp": {"8080/tcp", "8081/tcp", "8082/tcp"},
|
||||||
}
|
}
|
||||||
for expose, expectedError := range invalids {
|
for expose, expectedError := range invalids {
|
||||||
if _, _, _, _, err := parseRun([]string{fmt.Sprintf("--expose=%v", expose), "img", "cmd"}); err == nil || err.Error() != expectedError {
|
if _, _, _, err := parseRun([]string{fmt.Sprintf("--expose=%v", expose), "img", "cmd"}); err == nil || err.Error() != expectedError {
|
||||||
t.Fatalf("Expected error '%v' with '--expose=%v', got '%v'", expectedError, expose, err)
|
t.Fatalf("Expected error '%v' with '--expose=%v', got '%v'", expectedError, expose, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for expose, exposedPorts := range valids {
|
for expose, exposedPorts := range valids {
|
||||||
config, _, _, _, err := parseRun([]string{fmt.Sprintf("--expose=%v", expose), "img", "cmd"})
|
config, _, _, err := parseRun([]string{fmt.Sprintf("--expose=%v", expose), "img", "cmd"})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -446,7 +451,7 @@ func TestParseWithExpose(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Merge with actual published port
|
// Merge with actual published port
|
||||||
config, _, _, _, err := parseRun([]string{"--publish=80", "--expose=80-81/tcp", "img", "cmd"})
|
config, _, _, err := parseRun([]string{"--publish=80", "--expose=80-81/tcp", "img", "cmd"})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -485,7 +490,7 @@ func TestParseDevice(t *testing.T) {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
for device, deviceMapping := range valids {
|
for device, deviceMapping := range valids {
|
||||||
_, hostconfig, _, _, err := parseRun([]string{fmt.Sprintf("--device=%v", device), "img", "cmd"})
|
_, hostconfig, _, err := parseRun([]string{fmt.Sprintf("--device=%v", device), "img", "cmd"})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -501,11 +506,11 @@ func TestParseDevice(t *testing.T) {
|
||||||
|
|
||||||
func TestParseModes(t *testing.T) {
|
func TestParseModes(t *testing.T) {
|
||||||
// ipc ko
|
// ipc ko
|
||||||
if _, _, _, _, err := parseRun([]string{"--ipc=container:", "img", "cmd"}); err == nil || err.Error() != "--ipc: invalid IPC mode" {
|
if _, _, _, err := parseRun([]string{"--ipc=container:", "img", "cmd"}); err == nil || err.Error() != "--ipc: invalid IPC mode" {
|
||||||
t.Fatalf("Expected an error with message '--ipc: invalid IPC mode', got %v", err)
|
t.Fatalf("Expected an error with message '--ipc: invalid IPC mode', got %v", err)
|
||||||
}
|
}
|
||||||
// ipc ok
|
// ipc ok
|
||||||
_, hostconfig, _, _, err := parseRun([]string{"--ipc=host", "img", "cmd"})
|
_, hostconfig, _, err := parseRun([]string{"--ipc=host", "img", "cmd"})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -513,11 +518,11 @@ func TestParseModes(t *testing.T) {
|
||||||
t.Fatalf("Expected a valid IpcMode, got %v", hostconfig.IpcMode)
|
t.Fatalf("Expected a valid IpcMode, got %v", hostconfig.IpcMode)
|
||||||
}
|
}
|
||||||
// pid ko
|
// pid ko
|
||||||
if _, _, _, _, err := parseRun([]string{"--pid=container:", "img", "cmd"}); err == nil || err.Error() != "--pid: invalid PID mode" {
|
if _, _, _, err := parseRun([]string{"--pid=container:", "img", "cmd"}); err == nil || err.Error() != "--pid: invalid PID mode" {
|
||||||
t.Fatalf("Expected an error with message '--pid: invalid PID mode', got %v", err)
|
t.Fatalf("Expected an error with message '--pid: invalid PID mode', got %v", err)
|
||||||
}
|
}
|
||||||
// pid ok
|
// pid ok
|
||||||
_, hostconfig, _, _, err = parseRun([]string{"--pid=host", "img", "cmd"})
|
_, hostconfig, _, err = parseRun([]string{"--pid=host", "img", "cmd"})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -525,11 +530,11 @@ func TestParseModes(t *testing.T) {
|
||||||
t.Fatalf("Expected a valid PidMode, got %v", hostconfig.PidMode)
|
t.Fatalf("Expected a valid PidMode, got %v", hostconfig.PidMode)
|
||||||
}
|
}
|
||||||
// uts ko
|
// uts ko
|
||||||
if _, _, _, _, err := parseRun([]string{"--uts=container:", "img", "cmd"}); err == nil || err.Error() != "--uts: invalid UTS mode" {
|
if _, _, _, err := parseRun([]string{"--uts=container:", "img", "cmd"}); err == nil || err.Error() != "--uts: invalid UTS mode" {
|
||||||
t.Fatalf("Expected an error with message '--uts: invalid UTS mode', got %v", err)
|
t.Fatalf("Expected an error with message '--uts: invalid UTS mode', got %v", err)
|
||||||
}
|
}
|
||||||
// uts ok
|
// uts ok
|
||||||
_, hostconfig, _, _, err = parseRun([]string{"--uts=host", "img", "cmd"})
|
_, hostconfig, _, err = parseRun([]string{"--uts=host", "img", "cmd"})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -537,11 +542,11 @@ func TestParseModes(t *testing.T) {
|
||||||
t.Fatalf("Expected a valid UTSMode, got %v", hostconfig.UTSMode)
|
t.Fatalf("Expected a valid UTSMode, got %v", hostconfig.UTSMode)
|
||||||
}
|
}
|
||||||
// shm-size ko
|
// shm-size ko
|
||||||
if _, _, _, _, err = parseRun([]string{"--shm-size=a128m", "img", "cmd"}); err == nil || err.Error() != "invalid size: 'a128m'" {
|
if _, _, _, err = parseRun([]string{"--shm-size=a128m", "img", "cmd"}); err == nil || err.Error() != "invalid size: 'a128m'" {
|
||||||
t.Fatalf("Expected an error with message 'invalid size: a128m', got %v", err)
|
t.Fatalf("Expected an error with message 'invalid size: a128m', got %v", err)
|
||||||
}
|
}
|
||||||
// shm-size ok
|
// shm-size ok
|
||||||
_, hostconfig, _, _, err = parseRun([]string{"--shm-size=128m", "img", "cmd"})
|
_, hostconfig, _, err = parseRun([]string{"--shm-size=128m", "img", "cmd"})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -570,12 +575,12 @@ func TestParseRestartPolicy(t *testing.T) {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
for restart, expectedError := range invalids {
|
for restart, expectedError := range invalids {
|
||||||
if _, _, _, _, err := parseRun([]string{fmt.Sprintf("--restart=%s", restart), "img", "cmd"}); err == nil || err.Error() != expectedError {
|
if _, _, _, err := parseRun([]string{fmt.Sprintf("--restart=%s", restart), "img", "cmd"}); err == nil || err.Error() != expectedError {
|
||||||
t.Fatalf("Expected an error with message '%v' for %v, got %v", expectedError, restart, err)
|
t.Fatalf("Expected an error with message '%v' for %v, got %v", expectedError, restart, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for restart, expected := range valids {
|
for restart, expected := range valids {
|
||||||
_, hostconfig, _, _, err := parseRun([]string{fmt.Sprintf("--restart=%v", restart), "img", "cmd"})
|
_, hostconfig, _, err := parseRun([]string{fmt.Sprintf("--restart=%v", restart), "img", "cmd"})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -626,11 +631,11 @@ func TestParseHealth(t *testing.T) {
|
||||||
|
|
||||||
func TestParseLoggingOpts(t *testing.T) {
|
func TestParseLoggingOpts(t *testing.T) {
|
||||||
// logging opts ko
|
// logging opts ko
|
||||||
if _, _, _, _, err := parseRun([]string{"--log-driver=none", "--log-opt=anything", "img", "cmd"}); err == nil || err.Error() != "invalid logging opts for driver none" {
|
if _, _, _, err := parseRun([]string{"--log-driver=none", "--log-opt=anything", "img", "cmd"}); err == nil || err.Error() != "invalid logging opts for driver none" {
|
||||||
t.Fatalf("Expected an error with message 'invalid logging opts for driver none', got %v", err)
|
t.Fatalf("Expected an error with message 'invalid logging opts for driver none', got %v", err)
|
||||||
}
|
}
|
||||||
// logging opts ok
|
// logging opts ok
|
||||||
_, hostconfig, _, _, err := parseRun([]string{"--log-driver=syslog", "--log-opt=something", "img", "cmd"})
|
_, hostconfig, _, err := parseRun([]string{"--log-driver=syslog", "--log-opt=something", "img", "cmd"})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -645,18 +650,18 @@ func TestParseEnvfileVariables(t *testing.T) {
|
||||||
e = "open nonexistent: The system cannot find the file specified."
|
e = "open nonexistent: The system cannot find the file specified."
|
||||||
}
|
}
|
||||||
// env ko
|
// env ko
|
||||||
if _, _, _, _, err := parseRun([]string{"--env-file=nonexistent", "img", "cmd"}); err == nil || err.Error() != e {
|
if _, _, _, err := parseRun([]string{"--env-file=nonexistent", "img", "cmd"}); err == nil || err.Error() != e {
|
||||||
t.Fatalf("Expected an error with message '%s', got %v", e, err)
|
t.Fatalf("Expected an error with message '%s', got %v", e, err)
|
||||||
}
|
}
|
||||||
// env ok
|
// env ok
|
||||||
config, _, _, _, err := parseRun([]string{"--env-file=fixtures/valid.env", "img", "cmd"})
|
config, _, _, err := parseRun([]string{"--env-file=fixtures/valid.env", "img", "cmd"})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
if len(config.Env) != 1 || config.Env[0] != "ENV1=value1" {
|
if len(config.Env) != 1 || config.Env[0] != "ENV1=value1" {
|
||||||
t.Fatalf("Expected a config with [ENV1=value1], got %v", config.Env)
|
t.Fatalf("Expected a config with [ENV1=value1], got %v", config.Env)
|
||||||
}
|
}
|
||||||
config, _, _, _, err = parseRun([]string{"--env-file=fixtures/valid.env", "--env=ENV2=value2", "img", "cmd"})
|
config, _, _, err = parseRun([]string{"--env-file=fixtures/valid.env", "--env=ENV2=value2", "img", "cmd"})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -671,18 +676,18 @@ func TestParseLabelfileVariables(t *testing.T) {
|
||||||
e = "open nonexistent: The system cannot find the file specified."
|
e = "open nonexistent: The system cannot find the file specified."
|
||||||
}
|
}
|
||||||
// label ko
|
// label ko
|
||||||
if _, _, _, _, err := parseRun([]string{"--label-file=nonexistent", "img", "cmd"}); err == nil || err.Error() != e {
|
if _, _, _, err := parseRun([]string{"--label-file=nonexistent", "img", "cmd"}); err == nil || err.Error() != e {
|
||||||
t.Fatalf("Expected an error with message '%s', got %v", e, err)
|
t.Fatalf("Expected an error with message '%s', got %v", e, err)
|
||||||
}
|
}
|
||||||
// label ok
|
// label ok
|
||||||
config, _, _, _, err := parseRun([]string{"--label-file=fixtures/valid.label", "img", "cmd"})
|
config, _, _, err := parseRun([]string{"--label-file=fixtures/valid.label", "img", "cmd"})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
if len(config.Labels) != 1 || config.Labels["LABEL1"] != "value1" {
|
if len(config.Labels) != 1 || config.Labels["LABEL1"] != "value1" {
|
||||||
t.Fatalf("Expected a config with [LABEL1:value1], got %v", config.Labels)
|
t.Fatalf("Expected a config with [LABEL1:value1], got %v", config.Labels)
|
||||||
}
|
}
|
||||||
config, _, _, _, err = parseRun([]string{"--label-file=fixtures/valid.label", "--label=LABEL2=value2", "img", "cmd"})
|
config, _, _, err = parseRun([]string{"--label-file=fixtures/valid.label", "--label=LABEL2=value2", "img", "cmd"})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -692,7 +697,7 @@ func TestParseLabelfileVariables(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestParseEntryPoint(t *testing.T) {
|
func TestParseEntryPoint(t *testing.T) {
|
||||||
config, _, _, _, err := parseRun([]string{"--entrypoint=anything", "cmd", "img"})
|
config, _, _, err := parseRun([]string{"--entrypoint=anything", "cmd", "img"})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -106,3 +106,8 @@ func (opt *ThrottledeviceOpt) GetList() []*blkiodev.ThrottleDevice {
|
||||||
|
|
||||||
return throttledevice
|
return throttledevice
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Type returns the option type
|
||||||
|
func (opt *ThrottledeviceOpt) Type() string {
|
||||||
|
return "throttled-device"
|
||||||
|
}
|
||||||
|
|
|
@ -50,3 +50,8 @@ func (o *UlimitOpt) GetList() []*units.Ulimit {
|
||||||
|
|
||||||
return ulimits
|
return ulimits
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Type returns the option type
|
||||||
|
func (o *UlimitOpt) Type() string {
|
||||||
|
return "ulimit"
|
||||||
|
}
|
||||||
|
|
|
@ -82,3 +82,8 @@ func (opt *WeightdeviceOpt) GetList() []*blkiodev.WeightDevice {
|
||||||
|
|
||||||
return weightdevice
|
return weightdevice
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Type returns the option type
|
||||||
|
func (opt *WeightdeviceOpt) Type() string {
|
||||||
|
return "weighted-device"
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue