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 {
|
||||
sigc := cli.forwardAllSignals(ctx, container)
|
||||
sigc := cli.ForwardAllSignals(ctx, container)
|
||||
defer signal.StopCatch(sigc)
|
||||
}
|
||||
|
||||
|
@ -80,20 +80,20 @@ func (cli *DockerCli) CmdAttach(args ...string) error {
|
|||
defer resp.Close()
|
||||
|
||||
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
|
||||
// terminal, the only way to get the shell prompt to display for attaches 2+ is to artificially
|
||||
// resize it, then go back to normal. Without this, every attach after the first will
|
||||
// require the user to manually resize or hit enter.
|
||||
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.
|
||||
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)
|
||||
}
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
|
@ -101,7 +101,7 @@ func (cli *DockerCli) CmdAttach(args ...string) error {
|
|||
return errAttach
|
||||
}
|
||||
|
||||
_, status, err := cli.getExitCode(ctx, container)
|
||||
_, status, err := cli.GetExitCode(ctx, container)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -75,6 +75,21 @@ func (cli *DockerCli) Err() io.Writer {
|
|||
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
|
||||
// from a non-tty client input stream, and if so, returns an 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,
|
||||
"rm": cli.CmdRm,
|
||||
"rmi": cli.CmdRmi,
|
||||
"run": cli.CmdRun,
|
||||
"save": cli.CmdSave,
|
||||
"start": cli.CmdStart,
|
||||
"stats": cli.CmdStats,
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package client
|
||||
package container
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
@ -11,13 +11,16 @@ import (
|
|||
"golang.org/x/net/context"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
Cli "github.com/docker/docker/cli"
|
||||
"github.com/docker/docker/opts"
|
||||
"github.com/docker/docker/api/client"
|
||||
"github.com/docker/docker/cli"
|
||||
opttypes "github.com/docker/docker/opts"
|
||||
"github.com/docker/docker/pkg/promise"
|
||||
"github.com/docker/docker/pkg/signal"
|
||||
runconfigopts "github.com/docker/docker/runconfig/opts"
|
||||
"github.com/docker/engine-api/types"
|
||||
"github.com/docker/libnetwork/resolvconf/dns"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/pflag"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -25,74 +28,74 @@ const (
|
|||
errCmdCouldNotBeInvoked = "could not be invoked"
|
||||
)
|
||||
|
||||
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
|
||||
type runOptions struct {
|
||||
autoRemove bool
|
||||
detach bool
|
||||
sigProxy bool
|
||||
name string
|
||||
detachKeys string
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
// NewRunCommand create a new `docker run` command
|
||||
func NewRunCommand(dockerCli *client.DockerCli) *cobra.Command {
|
||||
var opts runOptions
|
||||
var copts *runconfigopts.ContainerOptions
|
||||
|
||||
// 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}
|
||||
cmd := &cobra.Command{
|
||||
Use: "run [OPTIONS] IMAGE [COMMAND] [ARG...]",
|
||||
Short: "Run a command in a new container",
|
||||
Args: cli.RequiresMinArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
copts.Image = args[0]
|
||||
if len(args) > 1 {
|
||||
copts.Args = args[1:]
|
||||
}
|
||||
return runRun(dockerCli, cmd.Flags(), &opts, copts)
|
||||
},
|
||||
}
|
||||
|
||||
return statusError
|
||||
}
|
||||
|
||||
// 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)
|
||||
flags := cmd.Flags()
|
||||
flags.SetInterspersed(false)
|
||||
|
||||
// These are flags not stored in Config/HostConfig
|
||||
var (
|
||||
flAutoRemove = cmd.Bool([]string{"-rm"}, false, "Automatically remove the container when it exits")
|
||||
flDetach = cmd.Bool([]string{"d", "-detach"}, false, "Run container in background and print container ID")
|
||||
flSigProxy = cmd.Bool([]string{"-sig-proxy"}, true, "Proxy received signals to the process")
|
||||
flName = cmd.String([]string{"-name"}, "", "Assign a name to the container")
|
||||
flDetachKeys = cmd.String([]string{"-detach-keys"}, "", "Override the key sequence for detaching a container")
|
||||
flAttach *opts.ListOpts
|
||||
flags.BoolVar(&opts.autoRemove, "rm", false, "Automatically remove the container when it exits")
|
||||
flags.BoolVarP(&opts.detach, "detach", "d", false, "Run container in background and print container ID")
|
||||
flags.BoolVar(&opts.sigProxy, "sig-proxy", true, "Proxy received signals to the process")
|
||||
flags.StringVar(&opts.name, "name", "", "Assign a name to the container")
|
||||
flags.StringVar(&opts.detachKeys, "detach-keys", "", "Override the key sequence for detaching a container")
|
||||
|
||||
// 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")
|
||||
ErrConflictRestartPolicyAndAutoRemove = fmt.Errorf("Conflicting options: --restart and --rm")
|
||||
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
|
||||
if err != nil {
|
||||
cmd.ReportError(err.Error(), true)
|
||||
reportError(stderr, cmdPath, err.Error(), true)
|
||||
os.Exit(125)
|
||||
}
|
||||
|
||||
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 {
|
||||
|
@ -101,30 +104,26 @@ func (cli *DockerCli) CmdRun(args ...string) error {
|
|||
// set a DNS to a localhost address
|
||||
for _, dnsIP := range hostConfig.DNS {
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
if config.Image == "" {
|
||||
cmd.Usage()
|
||||
return nil
|
||||
}
|
||||
|
||||
config.ArgsEscaped = false
|
||||
|
||||
if !*flDetach {
|
||||
if err := cli.CheckTtyInput(config.AttachStdin, config.Tty); err != nil {
|
||||
if !opts.detach {
|
||||
if err := dockerCli.CheckTtyInput(config.AttachStdin, config.Tty); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
if fl := cmd.Lookup("-attach"); fl != nil {
|
||||
flAttach = fl.Value.(*opts.ListOpts)
|
||||
if fl := flags.Lookup("attach"); fl != nil {
|
||||
flAttach = fl.Value.(*opttypes.ListOpts)
|
||||
if flAttach.Len() != 0 {
|
||||
return ErrConflictAttachDetach
|
||||
}
|
||||
}
|
||||
if *flAutoRemove {
|
||||
if opts.autoRemove {
|
||||
return ErrConflictDetachAutoRemove
|
||||
}
|
||||
|
||||
|
@ -134,28 +133,27 @@ func (cli *DockerCli) CmdRun(args ...string) error {
|
|||
config.StdinOnce = false
|
||||
}
|
||||
|
||||
// Disable flSigProxy when in TTY mode
|
||||
sigProxy := *flSigProxy
|
||||
// Disable sigProxy when in TTY mode
|
||||
if config.Tty {
|
||||
sigProxy = false
|
||||
opts.sigProxy = false
|
||||
}
|
||||
|
||||
// Telling the Windows daemon the initial size of the tty during start makes
|
||||
// a far better user experience rather than relying on subsequent resizes
|
||||
// to cause things to catch up.
|
||||
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())
|
||||
|
||||
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 {
|
||||
cmd.ReportError(err.Error(), true)
|
||||
reportError(stderr, cmdPath, err.Error(), true)
|
||||
return runStartContainerErr(err)
|
||||
}
|
||||
if sigProxy {
|
||||
sigc := cli.forwardAllSignals(ctx, createResponse.ID)
|
||||
if opts.sigProxy {
|
||||
sigc := dockerCli.ForwardAllSignals(ctx, createResponse.ID)
|
||||
defer signal.StopCatch(sigc)
|
||||
}
|
||||
var (
|
||||
|
@ -167,34 +165,34 @@ func (cli *DockerCli) CmdRun(args ...string) error {
|
|||
waitDisplayID = make(chan struct{})
|
||||
go func() {
|
||||
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
|
||||
}
|
||||
attach := config.AttachStdin || config.AttachStdout || config.AttachStderr
|
||||
if attach {
|
||||
var (
|
||||
out, stderr io.Writer
|
||||
out, cerr io.Writer
|
||||
in io.ReadCloser
|
||||
)
|
||||
if config.AttachStdin {
|
||||
in = cli.in
|
||||
in = stdin
|
||||
}
|
||||
if config.AttachStdout {
|
||||
out = cli.out
|
||||
out = stdout
|
||||
}
|
||||
if config.AttachStderr {
|
||||
if config.Tty {
|
||||
stderr = cli.out
|
||||
cerr = stdout
|
||||
} else {
|
||||
stderr = cli.err
|
||||
cerr = stderr
|
||||
}
|
||||
}
|
||||
|
||||
if *flDetachKeys != "" {
|
||||
cli.configFile.DetachKeys = *flDetachKeys
|
||||
if opts.detachKeys != "" {
|
||||
dockerCli.ConfigFile().DetachKeys = opts.detachKeys
|
||||
}
|
||||
|
||||
options := types.ContainerAttachOptions{
|
||||
|
@ -202,10 +200,10 @@ func (cli *DockerCli) CmdRun(args ...string) error {
|
|||
Stdin: config.AttachStdin,
|
||||
Stdout: config.AttachStdout,
|
||||
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 {
|
||||
// ContainerAttach returns an ErrPersistEOF (connection closed)
|
||||
// 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()
|
||||
|
||||
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 {
|
||||
return errAttach
|
||||
}
|
||||
|
@ -223,18 +221,18 @@ func (cli *DockerCli) CmdRun(args ...string) error {
|
|||
})
|
||||
}
|
||||
|
||||
if *flAutoRemove {
|
||||
if opts.autoRemove {
|
||||
defer func() {
|
||||
// Explicitly not sharing the context as it could be "Done" (by calling cancelFun)
|
||||
// and thus the container would not be removed.
|
||||
if err := cli.removeContainer(context.Background(), createResponse.ID, true, false, true); err != nil {
|
||||
fmt.Fprintf(cli.err, "%v\n", err)
|
||||
if err := dockerCli.RemoveContainer(context.Background(), createResponse.ID, true, false, true); err != nil {
|
||||
fmt.Fprintf(stderr, "%v\n", err)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
//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
|
||||
// holdHijackedConnection we are going to exit and wait
|
||||
// to avoid the terminal are not restored.
|
||||
|
@ -243,13 +241,13 @@ func (cli *DockerCli) CmdRun(args ...string) error {
|
|||
<-errCh
|
||||
}
|
||||
|
||||
cmd.ReportError(err.Error(), false)
|
||||
reportError(stderr, cmdPath, err.Error(), false)
|
||||
return runStartContainerErr(err)
|
||||
}
|
||||
|
||||
if (config.AttachStdin || config.AttachStdout || config.AttachStderr) && config.Tty && cli.isTerminalOut {
|
||||
if err := cli.monitorTtySize(ctx, createResponse.ID, false); err != nil {
|
||||
fmt.Fprintf(cli.err, "Error monitoring TTY size: %s\n", err)
|
||||
if (config.AttachStdin || config.AttachStdout || config.AttachStderr) && config.Tty && dockerCli.IsTerminalOut() {
|
||||
if err := dockerCli.MonitorTtySize(ctx, createResponse.ID, false); err != nil {
|
||||
fmt.Fprintf(stderr, "Error monitoring TTY size: %s\n", err)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -270,32 +268,58 @@ func (cli *DockerCli) CmdRun(args ...string) error {
|
|||
var status int
|
||||
|
||||
// Attached mode
|
||||
if *flAutoRemove {
|
||||
if opts.autoRemove {
|
||||
// Autoremove: wait for the container to finish, retrieve
|
||||
// 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)
|
||||
}
|
||||
if _, status, err = cli.getExitCode(ctx, createResponse.ID); err != nil {
|
||||
if _, status, err = dockerCli.GetExitCode(ctx, createResponse.ID); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
// No Autoremove: Simply retrieve the exit code
|
||||
if !config.Tty {
|
||||
// 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
|
||||
}
|
||||
} else {
|
||||
// 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
|
||||
if _, status, err = cli.getExitCode(ctx, createResponse.ID); err != nil {
|
||||
if _, status, err = dockerCli.GetExitCode(ctx, createResponse.ID); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
if status != 0 {
|
||||
return Cli.StatusError{StatusCode: status}
|
||||
return cli.StatusError{StatusCode: status}
|
||||
}
|
||||
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
|
||||
"github.com/docker/docker/reference"
|
||||
"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/types"
|
||||
"github.com/docker/engine-api/types/container"
|
||||
|
@ -56,6 +56,26 @@ type cidFile struct {
|
|||
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) {
|
||||
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)
|
||||
|
@ -69,7 +89,10 @@ func newCIDFile(path string) (*cidFile, error) {
|
|||
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
|
||||
if cidfile != "" {
|
||||
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)
|
||||
addTrustedFlags(cmd, true)
|
||||
|
||||
// TODO: tmp disable for PoC, convert to cobra and pflag later
|
||||
// These are flags not stored in Config/HostConfig
|
||||
var (
|
||||
flName = cmd.String([]string{"-name"}, "", "Assign a name to the container")
|
||||
)
|
||||
// var (
|
||||
// flName = cmd.String([]string{"-name"}, "", "Assign a name to the container")
|
||||
// )
|
||||
|
||||
config, hostConfig, networkingConfig, cmd, err := runconfigopts.Parse(cmd, args)
|
||||
|
||||
if err != nil {
|
||||
cmd.ReportError(err.Error(), true)
|
||||
os.Exit(1)
|
||||
}
|
||||
if config.Image == "" {
|
||||
cmd.Usage()
|
||||
return nil
|
||||
}
|
||||
response, err := cli.createContainer(context.Background(), config, hostConfig, networkingConfig, hostConfig.ContainerIDFile, *flName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Fprintf(cli.out, "%s\n", response.ID)
|
||||
// config, hostConfig, networkingConfig, cmd, err := runconfigopts.Parse(cmd, args)
|
||||
//
|
||||
// if err != nil {
|
||||
// cmd.ReportError(err.Error(), true)
|
||||
// os.Exit(1)
|
||||
// }
|
||||
// if config.Image == "" {
|
||||
// cmd.Usage()
|
||||
// return nil
|
||||
// }
|
||||
// response, err := cli.CreateContainer(context.Background(), config, hostConfig, networkingConfig, hostConfig.ContainerIDFile, *flName)
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
// fmt.Fprintf(cli.out, "%s\n", response.ID)
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -93,11 +93,11 @@ func (cli *DockerCli) CmdExec(args ...string) error {
|
|||
}
|
||||
defer resp.Close()
|
||||
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 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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,7 +11,8 @@ import (
|
|||
"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 (
|
||||
err error
|
||||
restoreOnce sync.Once
|
||||
|
|
|
@ -32,7 +32,7 @@ func (cli *DockerCli) CmdRm(args ...string) error {
|
|||
}
|
||||
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())
|
||||
} else {
|
||||
fmt.Fprintf(cli.out, "%s\n", name)
|
||||
|
@ -44,7 +44,10 @@ func (cli *DockerCli) CmdRm(args ...string) error {
|
|||
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{
|
||||
RemoveVolumes: removeVolumes,
|
||||
RemoveLinks: removeLinks,
|
||||
|
|
|
@ -17,7 +17,10 @@ import (
|
|||
"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)
|
||||
signal.CatchAll(sigc)
|
||||
go func() {
|
||||
|
@ -74,7 +77,7 @@ func (cli *DockerCli) CmdStart(args ...string) error {
|
|||
}
|
||||
|
||||
if !c.Config.Tty {
|
||||
sigc := cli.forwardAllSignals(ctx, container)
|
||||
sigc := cli.ForwardAllSignals(ctx, container)
|
||||
defer signal.StopCatch(sigc)
|
||||
}
|
||||
|
||||
|
@ -105,7 +108,7 @@ func (cli *DockerCli) CmdStart(args ...string) error {
|
|||
}
|
||||
defer resp.Close()
|
||||
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 {
|
||||
return errAttach
|
||||
}
|
||||
|
@ -121,14 +124,14 @@ func (cli *DockerCli) CmdStart(args ...string) error {
|
|||
|
||||
// 4. Wait for attachment to break.
|
||||
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)
|
||||
}
|
||||
}
|
||||
if attchErr := <-cErr; attchErr != nil {
|
||||
return attchErr
|
||||
}
|
||||
_, status, err := cli.getExitCode(ctx, container)
|
||||
_, status, err := cli.GetExitCode(ctx, container)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -37,6 +37,7 @@ import (
|
|||
"github.com/docker/notary/tuf/data"
|
||||
"github.com/docker/notary/tuf/signed"
|
||||
"github.com/docker/notary/tuf/store"
|
||||
"github.com/spf13/pflag"
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -44,7 +45,20 @@ var (
|
|||
untrusted bool
|
||||
)
|
||||
|
||||
// TODO: tmp workaround to get this PoC working, change everything to use
|
||||
// exported version
|
||||
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
|
||||
if e := os.Getenv("DOCKER_CONTENT_TRUST"); e != "" {
|
||||
if t, err := strconv.ParseBool(e); t || err != nil {
|
||||
|
@ -56,7 +70,7 @@ func addTrustedFlags(fs *flag.FlagSet, verify bool) {
|
|||
if verify {
|
||||
message = "Skip image verification"
|
||||
}
|
||||
fs.BoolVar(&untrusted, []string{"-disable-content-trust"}, !trusted, message)
|
||||
return trusted, message
|
||||
}
|
||||
|
||||
func isTrusted() bool {
|
||||
|
|
|
@ -61,7 +61,7 @@ func (cli *DockerCli) RegistryAuthenticationPrivilegedFunc(index *registrytypes.
|
|||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
|
@ -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.
|
||||
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)
|
||||
if err != nil {
|
||||
// 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
|
||||
}
|
||||
|
||||
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)
|
||||
|
||||
if runtime.GOOS == "windows" {
|
||||
go func() {
|
||||
prevH, prevW := cli.getTtySize()
|
||||
prevH, prevW := cli.GetTtySize()
|
||||
for {
|
||||
time.Sleep(time.Millisecond * 250)
|
||||
h, w := cli.getTtySize()
|
||||
h, w := cli.GetTtySize()
|
||||
|
||||
if prevW != w || prevH != h {
|
||||
cli.resizeTty(ctx, id, isExec)
|
||||
|
@ -146,7 +147,8 @@ func (cli *DockerCli) monitorTtySize(ctx context.Context, id string, isExec bool
|
|||
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 {
|
||||
return 0, 0
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ import (
|
|||
"fmt"
|
||||
|
||||
"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/volume"
|
||||
"github.com/docker/docker/cli"
|
||||
|
@ -34,8 +35,9 @@ func NewCobraAdaptor(clientFlags *cliflags.ClientFlags) CobraAdaptor {
|
|||
rootCmd.SetFlagErrorFunc(flagErrorFunc)
|
||||
rootCmd.SetOutput(stdout)
|
||||
rootCmd.AddCommand(
|
||||
volume.NewVolumeCommand(dockerCli),
|
||||
container.NewRunCommand(dockerCli),
|
||||
image.NewSearchCommand(dockerCli),
|
||||
volume.NewVolumeCommand(dockerCli),
|
||||
)
|
||||
|
||||
rootCmd.PersistentFlags().BoolP("help", "h", false, "Print usage")
|
||||
|
@ -52,7 +54,7 @@ func NewCobraAdaptor(clientFlags *cliflags.ClientFlags) CobraAdaptor {
|
|||
func (c CobraAdaptor) Usage() []cli.Command {
|
||||
cmds := []cli.Command{}
|
||||
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
|
||||
}
|
||||
|
|
|
@ -37,7 +37,6 @@ var DockerCommandUsage = []Command{
|
|||
{"restart", "Restart a container"},
|
||||
{"rm", "Remove one or more containers"},
|
||||
{"rmi", "Remove one or more images"},
|
||||
{"run", "Run a command in a new container"},
|
||||
{"save", "Save one or more images to a tar archive"},
|
||||
{"start", "Start one or more stopped containers"},
|
||||
{"stats", "Display a live stream of container(s) resource usage statistics"},
|
||||
|
|
|
@ -8,160 +8,245 @@ import (
|
|||
"path"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/docker/docker/opts"
|
||||
flag "github.com/docker/docker/pkg/mflag"
|
||||
"github.com/docker/docker/pkg/mount"
|
||||
"github.com/docker/docker/pkg/signal"
|
||||
"github.com/docker/engine-api/types/container"
|
||||
networktypes "github.com/docker/engine-api/types/network"
|
||||
"github.com/docker/engine-api/types/strslice"
|
||||
"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,
|
||||
// a HostConfig and returns them with the specified command.
|
||||
// If the specified args are not valid, it will return an error.
|
||||
func Parse(cmd *flag.FlagSet, args []string) (*container.Config, *container.HostConfig, *networktypes.NetworkingConfig, *flag.FlagSet, error) {
|
||||
var (
|
||||
// FIXME: use utils.ListOpts for attach and volumes?
|
||||
flAttach = opts.NewListOpts(ValidateAttach)
|
||||
flVolumes = opts.NewListOpts(nil)
|
||||
flTmpfs = opts.NewListOpts(nil)
|
||||
flBlkioWeightDevice = NewWeightdeviceOpt(ValidateWeightDevice)
|
||||
flDeviceReadBps = NewThrottledeviceOpt(ValidateThrottleBpsDevice)
|
||||
flDeviceWriteBps = NewThrottledeviceOpt(ValidateThrottleBpsDevice)
|
||||
flLinks = opts.NewListOpts(ValidateLink)
|
||||
flAliases = opts.NewListOpts(nil)
|
||||
flDeviceReadIOps = NewThrottledeviceOpt(ValidateThrottleIOpsDevice)
|
||||
flDeviceWriteIOps = NewThrottledeviceOpt(ValidateThrottleIOpsDevice)
|
||||
flEnv = opts.NewListOpts(ValidateEnv)
|
||||
flLabels = opts.NewListOpts(ValidateEnv)
|
||||
flDevices = opts.NewListOpts(ValidateDevice)
|
||||
// ContainerOptions is a data object with all the options for creating a container
|
||||
// TODO: remove fl prefix
|
||||
type ContainerOptions struct {
|
||||
flAttach opts.ListOpts
|
||||
flVolumes opts.ListOpts
|
||||
flTmpfs opts.ListOpts
|
||||
flBlkioWeightDevice WeightdeviceOpt
|
||||
flDeviceReadBps ThrottledeviceOpt
|
||||
flDeviceWriteBps ThrottledeviceOpt
|
||||
flLinks opts.ListOpts
|
||||
flAliases opts.ListOpts
|
||||
flDeviceReadIOps ThrottledeviceOpt
|
||||
flDeviceWriteIOps ThrottledeviceOpt
|
||||
flEnv opts.ListOpts
|
||||
flLabels opts.ListOpts
|
||||
flDevices opts.ListOpts
|
||||
flUlimits *UlimitOpt
|
||||
flSysctls *opts.MapOpts
|
||||
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)
|
||||
flSysctls = opts.NewMapOpts(nil, opts.ValidateSysctl)
|
||||
Image string
|
||||
Args []string
|
||||
}
|
||||
|
||||
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)
|
||||
flPrivileged = cmd.Bool([]string{"-privileged"}, false, "Give extended privileges to this container")
|
||||
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")
|
||||
)
|
||||
// AddFlags adds all command line flags that will be used by Parse to the FlagSet
|
||||
func AddFlags(flags *pflag.FlagSet) *ContainerOptions {
|
||||
copts := &ContainerOptions{
|
||||
flAttach: opts.NewListOpts(ValidateAttach),
|
||||
flVolumes: opts.NewListOpts(nil),
|
||||
flTmpfs: opts.NewListOpts(nil),
|
||||
flBlkioWeightDevice: NewWeightdeviceOpt(ValidateWeightDevice),
|
||||
flDeviceReadBps: NewThrottledeviceOpt(ValidateThrottleBpsDevice),
|
||||
flDeviceWriteBps: NewThrottledeviceOpt(ValidateThrottleBpsDevice),
|
||||
flLinks: opts.NewListOpts(ValidateLink),
|
||||
flAliases: opts.NewListOpts(nil),
|
||||
flDeviceReadIOps: NewThrottledeviceOpt(ValidateThrottleIOpsDevice),
|
||||
flDeviceWriteIOps: NewThrottledeviceOpt(ValidateThrottleIOpsDevice),
|
||||
flEnv: opts.NewListOpts(ValidateEnv),
|
||||
flLabels: opts.NewListOpts(ValidateEnv),
|
||||
flDevices: opts.NewListOpts(ValidateDevice),
|
||||
|
||||
cmd.Var(&flAttach, []string{"a", "-attach"}, "Attach to STDIN, STDOUT or STDERR")
|
||||
cmd.Var(&flBlkioWeightDevice, []string{"-blkio-weight-device"}, "Block IO weight (relative device weight)")
|
||||
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")
|
||||
flUlimits: NewUlimitOpt(nil),
|
||||
flSysctls: opts.NewMapOpts(nil, opts.ValidateSysctl),
|
||||
|
||||
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 {
|
||||
return nil, nil, nil, cmd, err
|
||||
flPrivileged: flags.Bool("privileged", false, "Give extended privileges to this container"),
|
||||
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 (
|
||||
attachStdin = flAttach.Get("stdin")
|
||||
attachStdout = flAttach.Get("stdout")
|
||||
attachStderr = flAttach.Get("stderr")
|
||||
attachStdin = copts.flAttach.Get("stdin")
|
||||
attachStdout = copts.flAttach.Get("stdout")
|
||||
attachStderr = copts.flAttach.Get("stderr")
|
||||
)
|
||||
|
||||
// Validate the input mac address
|
||||
if *flMacAddress != "" {
|
||||
if _, err := ValidateMACAddress(*flMacAddress); err != nil {
|
||||
return nil, nil, nil, cmd, fmt.Errorf("%s is not a valid mac address", *flMacAddress)
|
||||
if *copts.flMacAddress != "" {
|
||||
if _, err := ValidateMACAddress(*copts.flMacAddress); err != nil {
|
||||
return nil, nil, nil, fmt.Errorf("%s is not a valid mac address", *copts.flMacAddress)
|
||||
}
|
||||
}
|
||||
if *flStdin {
|
||||
if *copts.flStdin {
|
||||
attachStdin = true
|
||||
}
|
||||
// If -a is not set, attach to stdout and stderr
|
||||
if flAttach.Len() == 0 {
|
||||
if copts.flAttach.Len() == 0 {
|
||||
attachStdout = true
|
||||
attachStderr = true
|
||||
}
|
||||
|
@ -169,83 +254,83 @@ func Parse(cmd *flag.FlagSet, args []string) (*container.Config, *container.Host
|
|||
var err error
|
||||
|
||||
var flMemory int64
|
||||
if *flMemoryString != "" {
|
||||
flMemory, err = units.RAMInBytes(*flMemoryString)
|
||||
if *copts.flMemoryString != "" {
|
||||
flMemory, err = units.RAMInBytes(*copts.flMemoryString)
|
||||
if err != nil {
|
||||
return nil, nil, nil, cmd, err
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
}
|
||||
|
||||
var MemoryReservation int64
|
||||
if *flMemoryReservation != "" {
|
||||
MemoryReservation, err = units.RAMInBytes(*flMemoryReservation)
|
||||
if *copts.flMemoryReservation != "" {
|
||||
MemoryReservation, err = units.RAMInBytes(*copts.flMemoryReservation)
|
||||
if err != nil {
|
||||
return nil, nil, nil, cmd, err
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
}
|
||||
|
||||
var memorySwap int64
|
||||
if *flMemorySwap != "" {
|
||||
if *flMemorySwap == "-1" {
|
||||
if *copts.flMemorySwap != "" {
|
||||
if *copts.flMemorySwap == "-1" {
|
||||
memorySwap = -1
|
||||
} else {
|
||||
memorySwap, err = units.RAMInBytes(*flMemorySwap)
|
||||
memorySwap, err = units.RAMInBytes(*copts.flMemorySwap)
|
||||
if err != nil {
|
||||
return nil, nil, nil, cmd, err
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var KernelMemory int64
|
||||
if *flKernelMemory != "" {
|
||||
KernelMemory, err = units.RAMInBytes(*flKernelMemory)
|
||||
if *copts.flKernelMemory != "" {
|
||||
KernelMemory, err = units.RAMInBytes(*copts.flKernelMemory)
|
||||
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) {
|
||||
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
|
||||
if *flShmSize != "" {
|
||||
shmSize, err = units.RAMInBytes(*flShmSize)
|
||||
if *copts.flShmSize != "" {
|
||||
shmSize, err = units.RAMInBytes(*copts.flShmSize)
|
||||
if err != nil {
|
||||
return nil, nil, nil, cmd, err
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// TODO FIXME units.RAMInBytes should have a uint64 version
|
||||
var maxIOBandwidth int64
|
||||
if *flIOMaxBandwidth != "" {
|
||||
maxIOBandwidth, err = units.RAMInBytes(*flIOMaxBandwidth)
|
||||
if *copts.flIOMaxBandwidth != "" {
|
||||
maxIOBandwidth, err = units.RAMInBytes(*copts.flIOMaxBandwidth)
|
||||
if err != nil {
|
||||
return nil, nil, nil, cmd, err
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
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
|
||||
// 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 {
|
||||
// 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
|
||||
binds = append(binds, bind)
|
||||
flVolumes.Delete(bind)
|
||||
copts.flVolumes.Delete(bind)
|
||||
}
|
||||
}
|
||||
|
||||
// Can't evaluate options passed into --tmpfs until we actually mount
|
||||
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 _, _, err := mount.ParseTmpfsOptions(arr[1]); err != nil {
|
||||
return nil, nil, nil, cmd, err
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
tmpfs[arr[0]] = arr[1]
|
||||
} else {
|
||||
|
@ -254,27 +339,25 @@ func Parse(cmd *flag.FlagSet, args []string) (*container.Config, *container.Host
|
|||
}
|
||||
|
||||
var (
|
||||
parsedArgs = cmd.Args()
|
||||
runCmd strslice.StrSlice
|
||||
entrypoint strslice.StrSlice
|
||||
image = cmd.Arg(0)
|
||||
)
|
||||
if len(parsedArgs) > 1 {
|
||||
runCmd = strslice.StrSlice(parsedArgs[1:])
|
||||
if len(copts.Args) > 0 {
|
||||
runCmd = strslice.StrSlice(copts.Args)
|
||||
}
|
||||
if *flEntrypoint != "" {
|
||||
entrypoint = strslice.StrSlice{*flEntrypoint}
|
||||
if *copts.flEntrypoint != "" {
|
||||
entrypoint = strslice.StrSlice{*copts.flEntrypoint}
|
||||
}
|
||||
|
||||
ports, portBindings, err := nat.ParsePortSpecs(flPublish.GetAll())
|
||||
ports, portBindings, err := nat.ParsePortSpecs(copts.flPublish.GetAll())
|
||||
if err != nil {
|
||||
return nil, nil, nil, cmd, err
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
|
||||
// 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, ":") {
|
||||
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>]
|
||||
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
|
||||
start, end, err := nat.ParsePortRange(port)
|
||||
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++ {
|
||||
p, err := nat.NewPort(proto, strconv.FormatUint(i, 10))
|
||||
if err != nil {
|
||||
return nil, nil, nil, cmd, err
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
if _, exists := ports[p]; !exists {
|
||||
ports[p] = struct{}{}
|
||||
|
@ -297,64 +380,64 @@ func Parse(cmd *flag.FlagSet, args []string) (*container.Config, *container.Host
|
|||
|
||||
// parse device mappings
|
||||
deviceMappings := []container.DeviceMapping{}
|
||||
for _, device := range flDevices.GetAll() {
|
||||
for _, device := range copts.flDevices.GetAll() {
|
||||
deviceMapping, err := ParseDevice(device)
|
||||
if err != nil {
|
||||
return nil, nil, nil, cmd, err
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
deviceMappings = append(deviceMappings, deviceMapping)
|
||||
}
|
||||
|
||||
// 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 {
|
||||
return nil, nil, nil, cmd, err
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
|
||||
// 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 {
|
||||
return nil, nil, nil, cmd, err
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
|
||||
ipcMode := container.IpcMode(*flIpcMode)
|
||||
ipcMode := container.IpcMode(*copts.flIpcMode)
|
||||
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() {
|
||||
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() {
|
||||
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() {
|
||||
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 {
|
||||
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 {
|
||||
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 {
|
||||
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 {
|
||||
return nil, nil, nil, cmd, err
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
|
||||
// Healthcheck
|
||||
|
@ -391,96 +474,96 @@ func Parse(cmd *flag.FlagSet, args []string) (*container.Config, *container.Host
|
|||
}
|
||||
|
||||
resources := container.Resources{
|
||||
CgroupParent: *flCgroupParent,
|
||||
CgroupParent: *copts.flCgroupParent,
|
||||
Memory: flMemory,
|
||||
MemoryReservation: MemoryReservation,
|
||||
MemorySwap: memorySwap,
|
||||
MemorySwappiness: flSwappiness,
|
||||
MemorySwappiness: copts.flSwappiness,
|
||||
KernelMemory: KernelMemory,
|
||||
OomKillDisable: flOomKillDisable,
|
||||
CPUPercent: *flCPUPercent,
|
||||
CPUShares: *flCPUShares,
|
||||
CPUPeriod: *flCPUPeriod,
|
||||
CpusetCpus: *flCpusetCpus,
|
||||
CpusetMems: *flCpusetMems,
|
||||
CPUQuota: *flCPUQuota,
|
||||
PidsLimit: *flPidsLimit,
|
||||
BlkioWeight: *flBlkioWeight,
|
||||
BlkioWeightDevice: flBlkioWeightDevice.GetList(),
|
||||
BlkioDeviceReadBps: flDeviceReadBps.GetList(),
|
||||
BlkioDeviceWriteBps: flDeviceWriteBps.GetList(),
|
||||
BlkioDeviceReadIOps: flDeviceReadIOps.GetList(),
|
||||
BlkioDeviceWriteIOps: flDeviceWriteIOps.GetList(),
|
||||
IOMaximumIOps: *flIOMaxIOps,
|
||||
OomKillDisable: copts.flOomKillDisable,
|
||||
CPUPercent: *copts.flCPUPercent,
|
||||
CPUShares: *copts.flCPUShares,
|
||||
CPUPeriod: *copts.flCPUPeriod,
|
||||
CpusetCpus: *copts.flCpusetCpus,
|
||||
CpusetMems: *copts.flCpusetMems,
|
||||
CPUQuota: *copts.flCPUQuota,
|
||||
PidsLimit: *copts.flPidsLimit,
|
||||
BlkioWeight: *copts.flBlkioWeight,
|
||||
BlkioWeightDevice: copts.flBlkioWeightDevice.GetList(),
|
||||
BlkioDeviceReadBps: copts.flDeviceReadBps.GetList(),
|
||||
BlkioDeviceWriteBps: copts.flDeviceWriteBps.GetList(),
|
||||
BlkioDeviceReadIOps: copts.flDeviceReadIOps.GetList(),
|
||||
BlkioDeviceWriteIOps: copts.flDeviceWriteIOps.GetList(),
|
||||
IOMaximumIOps: *copts.flIOMaxIOps,
|
||||
IOMaximumBandwidth: uint64(maxIOBandwidth),
|
||||
Ulimits: flUlimits.GetList(),
|
||||
Ulimits: copts.flUlimits.GetList(),
|
||||
Devices: deviceMappings,
|
||||
}
|
||||
|
||||
config := &container.Config{
|
||||
Hostname: *flHostname,
|
||||
Hostname: *copts.flHostname,
|
||||
ExposedPorts: ports,
|
||||
User: *flUser,
|
||||
Tty: *flTty,
|
||||
User: *copts.flUser,
|
||||
Tty: *copts.flTty,
|
||||
// TODO: deprecated, it comes from -n, --networking
|
||||
// it's still needed internally to set the network to disabled
|
||||
// if e.g. bridge is none in daemon opts, and in inspect
|
||||
NetworkDisabled: false,
|
||||
OpenStdin: *flStdin,
|
||||
OpenStdin: *copts.flStdin,
|
||||
AttachStdin: attachStdin,
|
||||
AttachStdout: attachStdout,
|
||||
AttachStderr: attachStderr,
|
||||
Env: envVariables,
|
||||
Cmd: runCmd,
|
||||
Image: image,
|
||||
Volumes: flVolumes.GetMap(),
|
||||
MacAddress: *flMacAddress,
|
||||
Image: copts.Image,
|
||||
Volumes: copts.flVolumes.GetMap(),
|
||||
MacAddress: *copts.flMacAddress,
|
||||
Entrypoint: entrypoint,
|
||||
WorkingDir: *flWorkingDir,
|
||||
WorkingDir: *copts.flWorkingDir,
|
||||
Labels: ConvertKVStringsToMap(labels),
|
||||
Healthcheck: healthConfig,
|
||||
}
|
||||
if cmd.IsSet("-stop-signal") {
|
||||
config.StopSignal = *flStopSignal
|
||||
if flags.Changed("stop-signal") {
|
||||
config.StopSignal = *copts.flStopSignal
|
||||
}
|
||||
|
||||
hostConfig := &container.HostConfig{
|
||||
Binds: binds,
|
||||
ContainerIDFile: *flContainerIDFile,
|
||||
OomScoreAdj: *flOomScoreAdj,
|
||||
Privileged: *flPrivileged,
|
||||
ContainerIDFile: *copts.flContainerIDFile,
|
||||
OomScoreAdj: *copts.flOomScoreAdj,
|
||||
Privileged: *copts.flPrivileged,
|
||||
PortBindings: portBindings,
|
||||
Links: flLinks.GetAll(),
|
||||
PublishAllPorts: *flPublishAll,
|
||||
Links: copts.flLinks.GetAll(),
|
||||
PublishAllPorts: *copts.flPublishAll,
|
||||
// Make sure the dns fields are never nil.
|
||||
// New containers don't ever have those fields nil,
|
||||
// but pre created containers can still have those nil values.
|
||||
// See https://github.com/docker/docker/pull/17779
|
||||
// for a more detailed explanation on why we don't want that.
|
||||
DNS: flDNS.GetAllOrEmpty(),
|
||||
DNSSearch: flDNSSearch.GetAllOrEmpty(),
|
||||
DNSOptions: flDNSOptions.GetAllOrEmpty(),
|
||||
ExtraHosts: flExtraHosts.GetAll(),
|
||||
VolumesFrom: flVolumesFrom.GetAll(),
|
||||
NetworkMode: container.NetworkMode(*flNetMode),
|
||||
DNS: copts.flDNS.GetAllOrEmpty(),
|
||||
DNSSearch: copts.flDNSSearch.GetAllOrEmpty(),
|
||||
DNSOptions: copts.flDNSOptions.GetAllOrEmpty(),
|
||||
ExtraHosts: copts.flExtraHosts.GetAll(),
|
||||
VolumesFrom: copts.flVolumesFrom.GetAll(),
|
||||
NetworkMode: container.NetworkMode(*copts.flNetMode),
|
||||
IpcMode: ipcMode,
|
||||
PidMode: pidMode,
|
||||
UTSMode: utsMode,
|
||||
UsernsMode: usernsMode,
|
||||
CapAdd: strslice.StrSlice(flCapAdd.GetAll()),
|
||||
CapDrop: strslice.StrSlice(flCapDrop.GetAll()),
|
||||
GroupAdd: flGroupAdd.GetAll(),
|
||||
CapAdd: strslice.StrSlice(copts.flCapAdd.GetAll()),
|
||||
CapDrop: strslice.StrSlice(copts.flCapDrop.GetAll()),
|
||||
GroupAdd: copts.flGroupAdd.GetAll(),
|
||||
RestartPolicy: restartPolicy,
|
||||
SecurityOpt: securityOpts,
|
||||
StorageOpt: storageOpts,
|
||||
ReadonlyRootfs: *flReadonlyRootfs,
|
||||
LogConfig: container.LogConfig{Type: *flLoggingDriver, Config: loggingOpts},
|
||||
VolumeDriver: *flVolumeDriver,
|
||||
Isolation: container.Isolation(*flIsolation),
|
||||
ReadonlyRootfs: *copts.flReadonlyRootfs,
|
||||
LogConfig: container.LogConfig{Type: *copts.flLoggingDriver, Config: loggingOpts},
|
||||
VolumeDriver: *copts.flVolumeDriver,
|
||||
Isolation: container.Isolation(*copts.flIsolation),
|
||||
ShmSize: shmSize,
|
||||
Resources: resources,
|
||||
Tmpfs: tmpfs,
|
||||
Sysctls: flSysctls.GetAll(),
|
||||
Sysctls: copts.flSysctls.GetAll(),
|
||||
}
|
||||
|
||||
// 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),
|
||||
}
|
||||
|
||||
if *flIPv4Address != "" || *flIPv6Address != "" {
|
||||
if *copts.flIPv4Address != "" || *copts.flIPv6Address != "" {
|
||||
networkingConfig.EndpointsConfig[string(hostConfig.NetworkMode)] = &networktypes.EndpointSettings{
|
||||
IPAMConfig: &networktypes.EndpointIPAMConfig{
|
||||
IPv4Address: *flIPv4Address,
|
||||
IPv6Address: *flIPv6Address,
|
||||
IPv4Address: *copts.flIPv4Address,
|
||||
IPv6Address: *copts.flIPv6Address,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -511,17 +594,17 @@ func Parse(cmd *flag.FlagSet, args []string) (*container.Config, *container.Host
|
|||
networkingConfig.EndpointsConfig[string(hostConfig.NetworkMode)] = epConfig
|
||||
}
|
||||
|
||||
if flAliases.Len() > 0 {
|
||||
if copts.flAliases.Len() > 0 {
|
||||
epConfig := networkingConfig.EndpointsConfig[string(hostConfig.NetworkMode)]
|
||||
if epConfig == nil {
|
||||
epConfig = &networktypes.EndpointSettings{}
|
||||
}
|
||||
epConfig.Aliases = make([]string, flAliases.Len())
|
||||
copy(epConfig.Aliases, flAliases.GetAll())
|
||||
epConfig.Aliases = make([]string, copts.flAliases.Len())
|
||||
copy(epConfig.Aliases, copts.flAliases.GetAll())
|
||||
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
|
||||
|
|
|
@ -11,22 +11,27 @@ import (
|
|||
"testing"
|
||||
"time"
|
||||
|
||||
flag "github.com/docker/docker/pkg/mflag"
|
||||
"github.com/docker/docker/runconfig"
|
||||
"github.com/docker/engine-api/types/container"
|
||||
networktypes "github.com/docker/engine-api/types/network"
|
||||
"github.com/docker/go-connections/nat"
|
||||
"github.com/spf13/pflag"
|
||||
)
|
||||
|
||||
func parseRun(args []string) (*container.Config, *container.HostConfig, *networktypes.NetworkingConfig, *flag.FlagSet, error) {
|
||||
cmd := flag.NewFlagSet("run", flag.ContinueOnError)
|
||||
cmd.SetOutput(ioutil.Discard)
|
||||
cmd.Usage = nil
|
||||
return Parse(cmd, args)
|
||||
// TODO: drop FlagSet from return value
|
||||
func parseRun(args []string) (*container.Config, *container.HostConfig, *networktypes.NetworkingConfig, error) {
|
||||
flags := pflag.NewFlagSet("run", pflag.ContinueOnError)
|
||||
flags.SetOutput(ioutil.Discard)
|
||||
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) {
|
||||
config, hostConfig, _, _, err := parseRun(strings.Split(args+" ubuntu bash", " "))
|
||||
config, hostConfig, _, err := parseRun(strings.Split(args+" ubuntu bash", " "))
|
||||
return config, hostConfig, err
|
||||
}
|
||||
|
||||
|
@ -351,7 +356,7 @@ func setupPlatformVolume(u []string, w []string) ([]string, string) {
|
|||
func TestParseWithMacAddress(t *testing.T) {
|
||||
invalidMacAddress := "--mac-address=invalidMacAddress"
|
||||
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)
|
||||
}
|
||||
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) {
|
||||
invalidMemory := "--memory=invalid"
|
||||
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)
|
||||
}
|
||||
if _, hostconfig := mustParse(t, validMemory); hostconfig.Memory != 1073741824 {
|
||||
|
@ -374,7 +379,7 @@ func TestParseWithMemorySwap(t *testing.T) {
|
|||
invalidMemory := "--memory-swap=invalid"
|
||||
validMemory := "--memory-swap=1G"
|
||||
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)
|
||||
}
|
||||
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"},
|
||||
}
|
||||
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)
|
||||
}
|
||||
}
|
||||
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 {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -446,7 +451,7 @@ func TestParseWithExpose(t *testing.T) {
|
|||
}
|
||||
}
|
||||
// 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 {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -485,7 +490,7 @@ func TestParseDevice(t *testing.T) {
|
|||
},
|
||||
}
|
||||
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 {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -501,11 +506,11 @@ func TestParseDevice(t *testing.T) {
|
|||
|
||||
func TestParseModes(t *testing.T) {
|
||||
// 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)
|
||||
}
|
||||
// ipc ok
|
||||
_, hostconfig, _, _, err := parseRun([]string{"--ipc=host", "img", "cmd"})
|
||||
_, hostconfig, _, err := parseRun([]string{"--ipc=host", "img", "cmd"})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -513,11 +518,11 @@ func TestParseModes(t *testing.T) {
|
|||
t.Fatalf("Expected a valid IpcMode, got %v", hostconfig.IpcMode)
|
||||
}
|
||||
// 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)
|
||||
}
|
||||
// pid ok
|
||||
_, hostconfig, _, _, err = parseRun([]string{"--pid=host", "img", "cmd"})
|
||||
_, hostconfig, _, err = parseRun([]string{"--pid=host", "img", "cmd"})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -525,11 +530,11 @@ func TestParseModes(t *testing.T) {
|
|||
t.Fatalf("Expected a valid PidMode, got %v", hostconfig.PidMode)
|
||||
}
|
||||
// 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)
|
||||
}
|
||||
// uts ok
|
||||
_, hostconfig, _, _, err = parseRun([]string{"--uts=host", "img", "cmd"})
|
||||
_, hostconfig, _, err = parseRun([]string{"--uts=host", "img", "cmd"})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -537,11 +542,11 @@ func TestParseModes(t *testing.T) {
|
|||
t.Fatalf("Expected a valid UTSMode, got %v", hostconfig.UTSMode)
|
||||
}
|
||||
// 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)
|
||||
}
|
||||
// shm-size ok
|
||||
_, hostconfig, _, _, err = parseRun([]string{"--shm-size=128m", "img", "cmd"})
|
||||
_, hostconfig, _, err = parseRun([]string{"--shm-size=128m", "img", "cmd"})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -570,12 +575,12 @@ func TestParseRestartPolicy(t *testing.T) {
|
|||
},
|
||||
}
|
||||
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)
|
||||
}
|
||||
}
|
||||
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 {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -626,11 +631,11 @@ func TestParseHealth(t *testing.T) {
|
|||
|
||||
func TestParseLoggingOpts(t *testing.T) {
|
||||
// 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)
|
||||
}
|
||||
// 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 {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -645,18 +650,18 @@ func TestParseEnvfileVariables(t *testing.T) {
|
|||
e = "open nonexistent: The system cannot find the file specified."
|
||||
}
|
||||
// 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)
|
||||
}
|
||||
// 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 {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(config.Env) != 1 || config.Env[0] != "ENV1=value1" {
|
||||
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 {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -671,18 +676,18 @@ func TestParseLabelfileVariables(t *testing.T) {
|
|||
e = "open nonexistent: The system cannot find the file specified."
|
||||
}
|
||||
// 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)
|
||||
}
|
||||
// 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 {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(config.Labels) != 1 || config.Labels["LABEL1"] != "value1" {
|
||||
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 {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -692,7 +697,7 @@ func TestParseLabelfileVariables(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 {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
|
|
@ -106,3 +106,8 @@ func (opt *ThrottledeviceOpt) GetList() []*blkiodev.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
|
||||
}
|
||||
|
||||
// Type returns the option type
|
||||
func (o *UlimitOpt) Type() string {
|
||||
return "ulimit"
|
||||
}
|
||||
|
|
|
@ -82,3 +82,8 @@ func (opt *WeightdeviceOpt) GetList() []*blkiodev.WeightDevice {
|
|||
|
||||
return weightdevice
|
||||
}
|
||||
|
||||
// Type returns the option type
|
||||
func (opt *WeightdeviceOpt) Type() string {
|
||||
return "weighted-device"
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue