github.com/brahmaroutu/docker@v1.2.1-0.20160809185609-eb28dde01f16/runconfig/opts/parse.go (about)

     1  package opts
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/json"
     6  	"fmt"
     7  	"io/ioutil"
     8  	"path"
     9  	"strconv"
    10  	"strings"
    11  	"time"
    12  
    13  	"github.com/docker/docker/opts"
    14  	"github.com/docker/docker/pkg/mount"
    15  	"github.com/docker/docker/pkg/signal"
    16  	"github.com/docker/engine-api/types/container"
    17  	networktypes "github.com/docker/engine-api/types/network"
    18  	"github.com/docker/engine-api/types/strslice"
    19  	"github.com/docker/go-connections/nat"
    20  	units "github.com/docker/go-units"
    21  	"github.com/spf13/pflag"
    22  )
    23  
    24  // ContainerOptions is a data object with all the options for creating a container
    25  // TODO: remove fl prefix
    26  type ContainerOptions struct {
    27  	flAttach            opts.ListOpts
    28  	flVolumes           opts.ListOpts
    29  	flTmpfs             opts.ListOpts
    30  	flBlkioWeightDevice WeightdeviceOpt
    31  	flDeviceReadBps     ThrottledeviceOpt
    32  	flDeviceWriteBps    ThrottledeviceOpt
    33  	flLinks             opts.ListOpts
    34  	flAliases           opts.ListOpts
    35  	flLinkLocalIPs      opts.ListOpts
    36  	flDeviceReadIOps    ThrottledeviceOpt
    37  	flDeviceWriteIOps   ThrottledeviceOpt
    38  	flEnv               opts.ListOpts
    39  	flLabels            opts.ListOpts
    40  	flDevices           opts.ListOpts
    41  	flUlimits           *UlimitOpt
    42  	flSysctls           *opts.MapOpts
    43  	flPublish           opts.ListOpts
    44  	flExpose            opts.ListOpts
    45  	flDNS               opts.ListOpts
    46  	flDNSSearch         opts.ListOpts
    47  	flDNSOptions        opts.ListOpts
    48  	flExtraHosts        opts.ListOpts
    49  	flVolumesFrom       opts.ListOpts
    50  	flEnvFile           opts.ListOpts
    51  	flCapAdd            opts.ListOpts
    52  	flCapDrop           opts.ListOpts
    53  	flGroupAdd          opts.ListOpts
    54  	flSecurityOpt       opts.ListOpts
    55  	flStorageOpt        opts.ListOpts
    56  	flLabelsFile        opts.ListOpts
    57  	flLoggingOpts       opts.ListOpts
    58  	flPrivileged        bool
    59  	flPidMode           string
    60  	flUTSMode           string
    61  	flUsernsMode        string
    62  	flPublishAll        bool
    63  	flStdin             bool
    64  	flTty               bool
    65  	flOomKillDisable    bool
    66  	flOomScoreAdj       int
    67  	flContainerIDFile   string
    68  	flEntrypoint        string
    69  	flHostname          string
    70  	flMemoryString      string
    71  	flMemoryReservation string
    72  	flMemorySwap        string
    73  	flKernelMemory      string
    74  	flUser              string
    75  	flWorkingDir        string
    76  	flCPUShares         int64
    77  	flCPUPercent        int64
    78  	flCPUPeriod         int64
    79  	flCPUQuota          int64
    80  	flCpusetCpus        string
    81  	flCpusetMems        string
    82  	flBlkioWeight       uint16
    83  	flIOMaxBandwidth    string
    84  	flIOMaxIOps         uint64
    85  	flSwappiness        int64
    86  	flNetMode           string
    87  	flMacAddress        string
    88  	flIPv4Address       string
    89  	flIPv6Address       string
    90  	flIpcMode           string
    91  	flPidsLimit         int64
    92  	flRestartPolicy     string
    93  	flReadonlyRootfs    bool
    94  	flLoggingDriver     string
    95  	flCgroupParent      string
    96  	flVolumeDriver      string
    97  	flStopSignal        string
    98  	flIsolation         string
    99  	flShmSize           string
   100  	flNoHealthcheck     bool
   101  	flHealthCmd         string
   102  	flHealthInterval    time.Duration
   103  	flHealthTimeout     time.Duration
   104  	flHealthRetries     int
   105  	flRuntime           string
   106  	flAutoRemove        bool
   107  
   108  	Image string
   109  	Args  []string
   110  }
   111  
   112  // AddFlags adds all command line flags that will be used by Parse to the FlagSet
   113  func AddFlags(flags *pflag.FlagSet) *ContainerOptions {
   114  	copts := &ContainerOptions{
   115  		flAliases:           opts.NewListOpts(nil),
   116  		flAttach:            opts.NewListOpts(ValidateAttach),
   117  		flBlkioWeightDevice: NewWeightdeviceOpt(ValidateWeightDevice),
   118  		flCapAdd:            opts.NewListOpts(nil),
   119  		flCapDrop:           opts.NewListOpts(nil),
   120  		flDNS:               opts.NewListOpts(opts.ValidateIPAddress),
   121  		flDNSOptions:        opts.NewListOpts(nil),
   122  		flDNSSearch:         opts.NewListOpts(opts.ValidateDNSSearch),
   123  		flDeviceReadBps:     NewThrottledeviceOpt(ValidateThrottleBpsDevice),
   124  		flDeviceReadIOps:    NewThrottledeviceOpt(ValidateThrottleIOpsDevice),
   125  		flDeviceWriteBps:    NewThrottledeviceOpt(ValidateThrottleBpsDevice),
   126  		flDeviceWriteIOps:   NewThrottledeviceOpt(ValidateThrottleIOpsDevice),
   127  		flDevices:           opts.NewListOpts(ValidateDevice),
   128  		flEnv:               opts.NewListOpts(ValidateEnv),
   129  		flEnvFile:           opts.NewListOpts(nil),
   130  		flExpose:            opts.NewListOpts(nil),
   131  		flExtraHosts:        opts.NewListOpts(ValidateExtraHost),
   132  		flGroupAdd:          opts.NewListOpts(nil),
   133  		flLabels:            opts.NewListOpts(ValidateEnv),
   134  		flLabelsFile:        opts.NewListOpts(nil),
   135  		flLinkLocalIPs:      opts.NewListOpts(nil),
   136  		flLinks:             opts.NewListOpts(ValidateLink),
   137  		flLoggingOpts:       opts.NewListOpts(nil),
   138  		flPublish:           opts.NewListOpts(nil),
   139  		flSecurityOpt:       opts.NewListOpts(nil),
   140  		flStorageOpt:        opts.NewListOpts(nil),
   141  		flSysctls:           opts.NewMapOpts(nil, opts.ValidateSysctl),
   142  		flTmpfs:             opts.NewListOpts(nil),
   143  		flUlimits:           NewUlimitOpt(nil),
   144  		flVolumes:           opts.NewListOpts(nil),
   145  		flVolumesFrom:       opts.NewListOpts(nil),
   146  	}
   147  
   148  	// General purpose flags
   149  	flags.VarP(&copts.flAttach, "attach", "a", "Attach to STDIN, STDOUT or STDERR")
   150  	flags.Var(&copts.flDevices, "device", "Add a host device to the container")
   151  	flags.VarP(&copts.flEnv, "env", "e", "Set environment variables")
   152  	flags.Var(&copts.flEnvFile, "env-file", "Read in a file of environment variables")
   153  	flags.StringVar(&copts.flEntrypoint, "entrypoint", "", "Overwrite the default ENTRYPOINT of the image")
   154  	flags.Var(&copts.flGroupAdd, "group-add", "Add additional groups to join")
   155  	flags.StringVarP(&copts.flHostname, "hostname", "h", "", "Container host name")
   156  	flags.BoolVarP(&copts.flStdin, "interactive", "i", false, "Keep STDIN open even if not attached")
   157  	flags.VarP(&copts.flLabels, "label", "l", "Set meta data on a container")
   158  	flags.Var(&copts.flLabelsFile, "label-file", "Read in a line delimited file of labels")
   159  	flags.BoolVar(&copts.flReadonlyRootfs, "read-only", false, "Mount the container's root filesystem as read only")
   160  	flags.StringVar(&copts.flRestartPolicy, "restart", "no", "Restart policy to apply when a container exits")
   161  	flags.StringVar(&copts.flStopSignal, "stop-signal", signal.DefaultStopSignal, fmt.Sprintf("Signal to stop a container, %v by default", signal.DefaultStopSignal))
   162  	flags.Var(copts.flSysctls, "sysctl", "Sysctl options")
   163  	flags.BoolVarP(&copts.flTty, "tty", "t", false, "Allocate a pseudo-TTY")
   164  	flags.Var(copts.flUlimits, "ulimit", "Ulimit options")
   165  	flags.StringVarP(&copts.flUser, "user", "u", "", "Username or UID (format: <name|uid>[:<group|gid>])")
   166  	flags.StringVarP(&copts.flWorkingDir, "workdir", "w", "", "Working directory inside the container")
   167  	flags.BoolVar(&copts.flAutoRemove, "rm", false, "Automatically remove the container when it exits")
   168  
   169  	// Security
   170  	flags.Var(&copts.flCapAdd, "cap-add", "Add Linux capabilities")
   171  	flags.Var(&copts.flCapDrop, "cap-drop", "Drop Linux capabilities")
   172  	flags.BoolVar(&copts.flPrivileged, "privileged", false, "Give extended privileges to this container")
   173  	flags.Var(&copts.flSecurityOpt, "security-opt", "Security Options")
   174  	flags.StringVar(&copts.flUsernsMode, "userns", "", "User namespace to use")
   175  
   176  	// Network and port publishing flag
   177  	flags.Var(&copts.flExtraHosts, "add-host", "Add a custom host-to-IP mapping (host:ip)")
   178  	flags.Var(&copts.flDNS, "dns", "Set custom DNS servers")
   179  	flags.Var(&copts.flDNSOptions, "dns-opt", "Set DNS options")
   180  	flags.Var(&copts.flDNSSearch, "dns-search", "Set custom DNS search domains")
   181  	flags.Var(&copts.flExpose, "expose", "Expose a port or a range of ports")
   182  	flags.StringVar(&copts.flIPv4Address, "ip", "", "Container IPv4 address (e.g. 172.30.100.104)")
   183  	flags.StringVar(&copts.flIPv6Address, "ip6", "", "Container IPv6 address (e.g. 2001:db8::33)")
   184  	flags.Var(&copts.flLinks, "link", "Add link to another container")
   185  	flags.Var(&copts.flLinkLocalIPs, "link-local-ip", "Container IPv4/IPv6 link-local addresses")
   186  	flags.StringVar(&copts.flMacAddress, "mac-address", "", "Container MAC address (e.g. 92:d0:c6:0a:29:33)")
   187  	flags.VarP(&copts.flPublish, "publish", "p", "Publish a container's port(s) to the host")
   188  	flags.BoolVarP(&copts.flPublishAll, "publish-all", "P", false, "Publish all exposed ports to random ports")
   189  	// We allow for both "--net" and "--network", although the latter is the recommended way.
   190  	flags.StringVar(&copts.flNetMode, "net", "default", "Connect a container to a network")
   191  	flags.StringVar(&copts.flNetMode, "network", "default", "Connect a container to a network")
   192  	flags.MarkHidden("net")
   193  	// We allow for both "--net-alias" and "--network-alias", although the latter is the recommended way.
   194  	flags.Var(&copts.flAliases, "net-alias", "Add network-scoped alias for the container")
   195  	flags.Var(&copts.flAliases, "network-alias", "Add network-scoped alias for the container")
   196  	flags.MarkHidden("net-alias")
   197  
   198  	// Logging and storage
   199  	flags.StringVar(&copts.flLoggingDriver, "log-driver", "", "Logging driver for the container")
   200  	flags.StringVar(&copts.flVolumeDriver, "volume-driver", "", "Optional volume driver for the container")
   201  	flags.Var(&copts.flLoggingOpts, "log-opt", "Log driver options")
   202  	flags.Var(&copts.flStorageOpt, "storage-opt", "Storage driver options for the container")
   203  	flags.Var(&copts.flTmpfs, "tmpfs", "Mount a tmpfs directory")
   204  	flags.Var(&copts.flVolumesFrom, "volumes-from", "Mount volumes from the specified container(s)")
   205  	flags.VarP(&copts.flVolumes, "volume", "v", "Bind mount a volume")
   206  
   207  	// Health-checking
   208  	flags.StringVar(&copts.flHealthCmd, "health-cmd", "", "Command to run to check health")
   209  	flags.DurationVar(&copts.flHealthInterval, "health-interval", 0, "Time between running the check")
   210  	flags.IntVar(&copts.flHealthRetries, "health-retries", 0, "Consecutive failures needed to report unhealthy")
   211  	flags.DurationVar(&copts.flHealthTimeout, "health-timeout", 0, "Maximum time to allow one check to run")
   212  	flags.BoolVar(&copts.flNoHealthcheck, "no-healthcheck", false, "Disable any container-specified HEALTHCHECK")
   213  
   214  	// Resource management
   215  	flags.Uint16Var(&copts.flBlkioWeight, "blkio-weight", 0, "Block IO (relative weight), between 10 and 1000")
   216  	flags.Var(&copts.flBlkioWeightDevice, "blkio-weight-device", "Block IO weight (relative device weight)")
   217  	flags.StringVar(&copts.flContainerIDFile, "cidfile", "", "Write the container ID to the file")
   218  	flags.StringVar(&copts.flCpusetCpus, "cpuset-cpus", "", "CPUs in which to allow execution (0-3, 0,1)")
   219  	flags.StringVar(&copts.flCpusetMems, "cpuset-mems", "", "MEMs in which to allow execution (0-3, 0,1)")
   220  	flags.Int64Var(&copts.flCPUPercent, "cpu-percent", 0, "CPU percent (Windows only)")
   221  	flags.Int64Var(&copts.flCPUPeriod, "cpu-period", 0, "Limit CPU CFS (Completely Fair Scheduler) period")
   222  	flags.Int64Var(&copts.flCPUQuota, "cpu-quota", 0, "Limit CPU CFS (Completely Fair Scheduler) quota")
   223  	flags.Int64VarP(&copts.flCPUShares, "cpu-shares", "c", 0, "CPU shares (relative weight)")
   224  	flags.Var(&copts.flDeviceReadBps, "device-read-bps", "Limit read rate (bytes per second) from a device")
   225  	flags.Var(&copts.flDeviceReadIOps, "device-read-iops", "Limit read rate (IO per second) from a device")
   226  	flags.Var(&copts.flDeviceWriteBps, "device-write-bps", "Limit write rate (bytes per second) to a device")
   227  	flags.Var(&copts.flDeviceWriteIOps, "device-write-iops", "Limit write rate (IO per second) to a device")
   228  	flags.StringVar(&copts.flIOMaxBandwidth, "io-maxbandwidth", "", "Maximum IO bandwidth limit for the system drive (Windows only)")
   229  	flags.Uint64Var(&copts.flIOMaxIOps, "io-maxiops", 0, "Maximum IOps limit for the system drive (Windows only)")
   230  	flags.StringVar(&copts.flKernelMemory, "kernel-memory", "", "Kernel memory limit")
   231  	flags.StringVarP(&copts.flMemoryString, "memory", "m", "", "Memory limit")
   232  	flags.StringVar(&copts.flMemoryReservation, "memory-reservation", "", "Memory soft limit")
   233  	flags.StringVar(&copts.flMemorySwap, "memory-swap", "", "Swap limit equal to memory plus swap: '-1' to enable unlimited swap")
   234  	flags.Int64Var(&copts.flSwappiness, "memory-swappiness", -1, "Tune container memory swappiness (0 to 100)")
   235  	flags.BoolVar(&copts.flOomKillDisable, "oom-kill-disable", false, "Disable OOM Killer")
   236  	flags.IntVar(&copts.flOomScoreAdj, "oom-score-adj", 0, "Tune host's OOM preferences (-1000 to 1000)")
   237  	flags.Int64Var(&copts.flPidsLimit, "pids-limit", 0, "Tune container pids limit (set -1 for unlimited)")
   238  
   239  	// Low-level execution (cgroups, namespaces, ...)
   240  	flags.StringVar(&copts.flCgroupParent, "cgroup-parent", "", "Optional parent cgroup for the container")
   241  	flags.StringVar(&copts.flIpcMode, "ipc", "", "IPC namespace to use")
   242  	flags.StringVar(&copts.flIsolation, "isolation", "", "Container isolation technology")
   243  	flags.StringVar(&copts.flPidMode, "pid", "", "PID namespace to use")
   244  	flags.StringVar(&copts.flShmSize, "shm-size", "", "Size of /dev/shm, default value is 64MB")
   245  	flags.StringVar(&copts.flUTSMode, "uts", "", "UTS namespace to use")
   246  	flags.StringVar(&copts.flRuntime, "runtime", "", "Runtime to use for this container")
   247  	return copts
   248  }
   249  
   250  // Parse parses the args for the specified command and generates a Config,
   251  // a HostConfig and returns them with the specified command.
   252  // If the specified args are not valid, it will return an error.
   253  func Parse(flags *pflag.FlagSet, copts *ContainerOptions) (*container.Config, *container.HostConfig, *networktypes.NetworkingConfig, error) {
   254  	var (
   255  		attachStdin  = copts.flAttach.Get("stdin")
   256  		attachStdout = copts.flAttach.Get("stdout")
   257  		attachStderr = copts.flAttach.Get("stderr")
   258  	)
   259  
   260  	// Validate the input mac address
   261  	if copts.flMacAddress != "" {
   262  		if _, err := ValidateMACAddress(copts.flMacAddress); err != nil {
   263  			return nil, nil, nil, fmt.Errorf("%s is not a valid mac address", copts.flMacAddress)
   264  		}
   265  	}
   266  	if copts.flStdin {
   267  		attachStdin = true
   268  	}
   269  	// If -a is not set, attach to stdout and stderr
   270  	if copts.flAttach.Len() == 0 {
   271  		attachStdout = true
   272  		attachStderr = true
   273  	}
   274  
   275  	var err error
   276  
   277  	var flMemory int64
   278  	if copts.flMemoryString != "" {
   279  		flMemory, err = units.RAMInBytes(copts.flMemoryString)
   280  		if err != nil {
   281  			return nil, nil, nil, err
   282  		}
   283  	}
   284  
   285  	var MemoryReservation int64
   286  	if copts.flMemoryReservation != "" {
   287  		MemoryReservation, err = units.RAMInBytes(copts.flMemoryReservation)
   288  		if err != nil {
   289  			return nil, nil, nil, err
   290  		}
   291  	}
   292  
   293  	var memorySwap int64
   294  	if copts.flMemorySwap != "" {
   295  		if copts.flMemorySwap == "-1" {
   296  			memorySwap = -1
   297  		} else {
   298  			memorySwap, err = units.RAMInBytes(copts.flMemorySwap)
   299  			if err != nil {
   300  				return nil, nil, nil, err
   301  			}
   302  		}
   303  	}
   304  
   305  	var KernelMemory int64
   306  	if copts.flKernelMemory != "" {
   307  		KernelMemory, err = units.RAMInBytes(copts.flKernelMemory)
   308  		if err != nil {
   309  			return nil, nil, nil, err
   310  		}
   311  	}
   312  
   313  	swappiness := copts.flSwappiness
   314  	if swappiness != -1 && (swappiness < 0 || swappiness > 100) {
   315  		return nil, nil, nil, fmt.Errorf("invalid value: %d. Valid memory swappiness range is 0-100", swappiness)
   316  	}
   317  
   318  	var shmSize int64
   319  	if copts.flShmSize != "" {
   320  		shmSize, err = units.RAMInBytes(copts.flShmSize)
   321  		if err != nil {
   322  			return nil, nil, nil, err
   323  		}
   324  	}
   325  
   326  	// TODO FIXME units.RAMInBytes should have a uint64 version
   327  	var maxIOBandwidth int64
   328  	if copts.flIOMaxBandwidth != "" {
   329  		maxIOBandwidth, err = units.RAMInBytes(copts.flIOMaxBandwidth)
   330  		if err != nil {
   331  			return nil, nil, nil, err
   332  		}
   333  		if maxIOBandwidth < 0 {
   334  			return nil, nil, nil, fmt.Errorf("invalid value: %s. Maximum IO Bandwidth must be positive", copts.flIOMaxBandwidth)
   335  		}
   336  	}
   337  
   338  	var binds []string
   339  	// add any bind targets to the list of container volumes
   340  	for bind := range copts.flVolumes.GetMap() {
   341  		if arr := volumeSplitN(bind, 2); len(arr) > 1 {
   342  			// after creating the bind mount we want to delete it from the copts.flVolumes values because
   343  			// we do not want bind mounts being committed to image configs
   344  			binds = append(binds, bind)
   345  			copts.flVolumes.Delete(bind)
   346  		}
   347  	}
   348  
   349  	// Can't evaluate options passed into --tmpfs until we actually mount
   350  	tmpfs := make(map[string]string)
   351  	for _, t := range copts.flTmpfs.GetAll() {
   352  		if arr := strings.SplitN(t, ":", 2); len(arr) > 1 {
   353  			if _, _, err := mount.ParseTmpfsOptions(arr[1]); err != nil {
   354  				return nil, nil, nil, err
   355  			}
   356  			tmpfs[arr[0]] = arr[1]
   357  		} else {
   358  			tmpfs[arr[0]] = ""
   359  		}
   360  	}
   361  
   362  	var (
   363  		runCmd     strslice.StrSlice
   364  		entrypoint strslice.StrSlice
   365  	)
   366  	if len(copts.Args) > 0 {
   367  		runCmd = strslice.StrSlice(copts.Args)
   368  	}
   369  	if copts.flEntrypoint != "" {
   370  		entrypoint = strslice.StrSlice{copts.flEntrypoint}
   371  	} else if flags.Changed("entrypoint") {
   372  		// if `--entrypoint=` is parsed then Entrypoint is reset
   373  		entrypoint = []string{""}
   374  	}
   375  
   376  	ports, portBindings, err := nat.ParsePortSpecs(copts.flPublish.GetAll())
   377  	if err != nil {
   378  		return nil, nil, nil, err
   379  	}
   380  
   381  	// Merge in exposed ports to the map of published ports
   382  	for _, e := range copts.flExpose.GetAll() {
   383  		if strings.Contains(e, ":") {
   384  			return nil, nil, nil, fmt.Errorf("invalid port format for --expose: %s", e)
   385  		}
   386  		//support two formats for expose, original format <portnum>/[<proto>] or <startport-endport>/[<proto>]
   387  		proto, port := nat.SplitProtoPort(e)
   388  		//parse the start and end port and create a sequence of ports to expose
   389  		//if expose a port, the start and end port are the same
   390  		start, end, err := nat.ParsePortRange(port)
   391  		if err != nil {
   392  			return nil, nil, nil, fmt.Errorf("invalid range format for --expose: %s, error: %s", e, err)
   393  		}
   394  		for i := start; i <= end; i++ {
   395  			p, err := nat.NewPort(proto, strconv.FormatUint(i, 10))
   396  			if err != nil {
   397  				return nil, nil, nil, err
   398  			}
   399  			if _, exists := ports[p]; !exists {
   400  				ports[p] = struct{}{}
   401  			}
   402  		}
   403  	}
   404  
   405  	// parse device mappings
   406  	deviceMappings := []container.DeviceMapping{}
   407  	for _, device := range copts.flDevices.GetAll() {
   408  		deviceMapping, err := ParseDevice(device)
   409  		if err != nil {
   410  			return nil, nil, nil, err
   411  		}
   412  		deviceMappings = append(deviceMappings, deviceMapping)
   413  	}
   414  
   415  	// collect all the environment variables for the container
   416  	envVariables, err := readKVStrings(copts.flEnvFile.GetAll(), copts.flEnv.GetAll())
   417  	if err != nil {
   418  		return nil, nil, nil, err
   419  	}
   420  
   421  	// collect all the labels for the container
   422  	labels, err := readKVStrings(copts.flLabelsFile.GetAll(), copts.flLabels.GetAll())
   423  	if err != nil {
   424  		return nil, nil, nil, err
   425  	}
   426  
   427  	ipcMode := container.IpcMode(copts.flIpcMode)
   428  	if !ipcMode.Valid() {
   429  		return nil, nil, nil, fmt.Errorf("--ipc: invalid IPC mode")
   430  	}
   431  
   432  	pidMode := container.PidMode(copts.flPidMode)
   433  	if !pidMode.Valid() {
   434  		return nil, nil, nil, fmt.Errorf("--pid: invalid PID mode")
   435  	}
   436  
   437  	utsMode := container.UTSMode(copts.flUTSMode)
   438  	if !utsMode.Valid() {
   439  		return nil, nil, nil, fmt.Errorf("--uts: invalid UTS mode")
   440  	}
   441  
   442  	usernsMode := container.UsernsMode(copts.flUsernsMode)
   443  	if !usernsMode.Valid() {
   444  		return nil, nil, nil, fmt.Errorf("--userns: invalid USER mode")
   445  	}
   446  
   447  	restartPolicy, err := ParseRestartPolicy(copts.flRestartPolicy)
   448  	if err != nil {
   449  		return nil, nil, nil, err
   450  	}
   451  
   452  	loggingOpts, err := parseLoggingOpts(copts.flLoggingDriver, copts.flLoggingOpts.GetAll())
   453  	if err != nil {
   454  		return nil, nil, nil, err
   455  	}
   456  
   457  	securityOpts, err := parseSecurityOpts(copts.flSecurityOpt.GetAll())
   458  	if err != nil {
   459  		return nil, nil, nil, err
   460  	}
   461  
   462  	storageOpts, err := parseStorageOpts(copts.flStorageOpt.GetAll())
   463  	if err != nil {
   464  		return nil, nil, nil, err
   465  	}
   466  
   467  	// Healthcheck
   468  	var healthConfig *container.HealthConfig
   469  	haveHealthSettings := copts.flHealthCmd != "" ||
   470  		copts.flHealthInterval != 0 ||
   471  		copts.flHealthTimeout != 0 ||
   472  		copts.flHealthRetries != 0
   473  	if copts.flNoHealthcheck {
   474  		if haveHealthSettings {
   475  			return nil, nil, nil, fmt.Errorf("--no-healthcheck conflicts with --health-* options")
   476  		}
   477  		test := strslice.StrSlice{"NONE"}
   478  		healthConfig = &container.HealthConfig{Test: test}
   479  	} else if haveHealthSettings {
   480  		var probe strslice.StrSlice
   481  		if copts.flHealthCmd != "" {
   482  			args := []string{"CMD-SHELL", copts.flHealthCmd}
   483  			probe = strslice.StrSlice(args)
   484  		}
   485  		if copts.flHealthInterval < 0 {
   486  			return nil, nil, nil, fmt.Errorf("--health-interval cannot be negative")
   487  		}
   488  		if copts.flHealthTimeout < 0 {
   489  			return nil, nil, nil, fmt.Errorf("--health-timeout cannot be negative")
   490  		}
   491  
   492  		healthConfig = &container.HealthConfig{
   493  			Test:     probe,
   494  			Interval: copts.flHealthInterval,
   495  			Timeout:  copts.flHealthTimeout,
   496  			Retries:  copts.flHealthRetries,
   497  		}
   498  	}
   499  
   500  	resources := container.Resources{
   501  		CgroupParent:         copts.flCgroupParent,
   502  		Memory:               flMemory,
   503  		MemoryReservation:    MemoryReservation,
   504  		MemorySwap:           memorySwap,
   505  		MemorySwappiness:     &copts.flSwappiness,
   506  		KernelMemory:         KernelMemory,
   507  		OomKillDisable:       &copts.flOomKillDisable,
   508  		CPUPercent:           copts.flCPUPercent,
   509  		CPUShares:            copts.flCPUShares,
   510  		CPUPeriod:            copts.flCPUPeriod,
   511  		CpusetCpus:           copts.flCpusetCpus,
   512  		CpusetMems:           copts.flCpusetMems,
   513  		CPUQuota:             copts.flCPUQuota,
   514  		PidsLimit:            copts.flPidsLimit,
   515  		BlkioWeight:          copts.flBlkioWeight,
   516  		BlkioWeightDevice:    copts.flBlkioWeightDevice.GetList(),
   517  		BlkioDeviceReadBps:   copts.flDeviceReadBps.GetList(),
   518  		BlkioDeviceWriteBps:  copts.flDeviceWriteBps.GetList(),
   519  		BlkioDeviceReadIOps:  copts.flDeviceReadIOps.GetList(),
   520  		BlkioDeviceWriteIOps: copts.flDeviceWriteIOps.GetList(),
   521  		IOMaximumIOps:        copts.flIOMaxIOps,
   522  		IOMaximumBandwidth:   uint64(maxIOBandwidth),
   523  		Ulimits:              copts.flUlimits.GetList(),
   524  		Devices:              deviceMappings,
   525  	}
   526  
   527  	config := &container.Config{
   528  		Hostname:     copts.flHostname,
   529  		ExposedPorts: ports,
   530  		User:         copts.flUser,
   531  		Tty:          copts.flTty,
   532  		// TODO: deprecated, it comes from -n, --networking
   533  		// it's still needed internally to set the network to disabled
   534  		// if e.g. bridge is none in daemon opts, and in inspect
   535  		NetworkDisabled: false,
   536  		OpenStdin:       copts.flStdin,
   537  		AttachStdin:     attachStdin,
   538  		AttachStdout:    attachStdout,
   539  		AttachStderr:    attachStderr,
   540  		Env:             envVariables,
   541  		Cmd:             runCmd,
   542  		Image:           copts.Image,
   543  		Volumes:         copts.flVolumes.GetMap(),
   544  		MacAddress:      copts.flMacAddress,
   545  		Entrypoint:      entrypoint,
   546  		WorkingDir:      copts.flWorkingDir,
   547  		Labels:          ConvertKVStringsToMap(labels),
   548  		Healthcheck:     healthConfig,
   549  	}
   550  	if flags.Changed("stop-signal") {
   551  		config.StopSignal = copts.flStopSignal
   552  	}
   553  
   554  	hostConfig := &container.HostConfig{
   555  		Binds:           binds,
   556  		ContainerIDFile: copts.flContainerIDFile,
   557  		OomScoreAdj:     copts.flOomScoreAdj,
   558  		AutoRemove:      copts.flAutoRemove,
   559  		Privileged:      copts.flPrivileged,
   560  		PortBindings:    portBindings,
   561  		Links:           copts.flLinks.GetAll(),
   562  		PublishAllPorts: copts.flPublishAll,
   563  		// Make sure the dns fields are never nil.
   564  		// New containers don't ever have those fields nil,
   565  		// but pre created containers can still have those nil values.
   566  		// See https://github.com/docker/docker/pull/17779
   567  		// for a more detailed explanation on why we don't want that.
   568  		DNS:            copts.flDNS.GetAllOrEmpty(),
   569  		DNSSearch:      copts.flDNSSearch.GetAllOrEmpty(),
   570  		DNSOptions:     copts.flDNSOptions.GetAllOrEmpty(),
   571  		ExtraHosts:     copts.flExtraHosts.GetAll(),
   572  		VolumesFrom:    copts.flVolumesFrom.GetAll(),
   573  		NetworkMode:    container.NetworkMode(copts.flNetMode),
   574  		IpcMode:        ipcMode,
   575  		PidMode:        pidMode,
   576  		UTSMode:        utsMode,
   577  		UsernsMode:     usernsMode,
   578  		CapAdd:         strslice.StrSlice(copts.flCapAdd.GetAll()),
   579  		CapDrop:        strslice.StrSlice(copts.flCapDrop.GetAll()),
   580  		GroupAdd:       copts.flGroupAdd.GetAll(),
   581  		RestartPolicy:  restartPolicy,
   582  		SecurityOpt:    securityOpts,
   583  		StorageOpt:     storageOpts,
   584  		ReadonlyRootfs: copts.flReadonlyRootfs,
   585  		LogConfig:      container.LogConfig{Type: copts.flLoggingDriver, Config: loggingOpts},
   586  		VolumeDriver:   copts.flVolumeDriver,
   587  		Isolation:      container.Isolation(copts.flIsolation),
   588  		ShmSize:        shmSize,
   589  		Resources:      resources,
   590  		Tmpfs:          tmpfs,
   591  		Sysctls:        copts.flSysctls.GetAll(),
   592  		Runtime:        copts.flRuntime,
   593  	}
   594  
   595  	// When allocating stdin in attached mode, close stdin at client disconnect
   596  	if config.OpenStdin && config.AttachStdin {
   597  		config.StdinOnce = true
   598  	}
   599  
   600  	networkingConfig := &networktypes.NetworkingConfig{
   601  		EndpointsConfig: make(map[string]*networktypes.EndpointSettings),
   602  	}
   603  
   604  	if copts.flIPv4Address != "" || copts.flIPv6Address != "" || copts.flLinkLocalIPs.Len() > 0 {
   605  		epConfig := &networktypes.EndpointSettings{}
   606  		networkingConfig.EndpointsConfig[string(hostConfig.NetworkMode)] = epConfig
   607  
   608  		epConfig.IPAMConfig = &networktypes.EndpointIPAMConfig{
   609  			IPv4Address: copts.flIPv4Address,
   610  			IPv6Address: copts.flIPv6Address,
   611  		}
   612  
   613  		if copts.flLinkLocalIPs.Len() > 0 {
   614  			epConfig.IPAMConfig.LinkLocalIPs = make([]string, copts.flLinkLocalIPs.Len())
   615  			copy(epConfig.IPAMConfig.LinkLocalIPs, copts.flLinkLocalIPs.GetAll())
   616  		}
   617  	}
   618  
   619  	if hostConfig.NetworkMode.IsUserDefined() && len(hostConfig.Links) > 0 {
   620  		epConfig := networkingConfig.EndpointsConfig[string(hostConfig.NetworkMode)]
   621  		if epConfig == nil {
   622  			epConfig = &networktypes.EndpointSettings{}
   623  		}
   624  		epConfig.Links = make([]string, len(hostConfig.Links))
   625  		copy(epConfig.Links, hostConfig.Links)
   626  		networkingConfig.EndpointsConfig[string(hostConfig.NetworkMode)] = epConfig
   627  	}
   628  
   629  	if copts.flAliases.Len() > 0 {
   630  		epConfig := networkingConfig.EndpointsConfig[string(hostConfig.NetworkMode)]
   631  		if epConfig == nil {
   632  			epConfig = &networktypes.EndpointSettings{}
   633  		}
   634  		epConfig.Aliases = make([]string, copts.flAliases.Len())
   635  		copy(epConfig.Aliases, copts.flAliases.GetAll())
   636  		networkingConfig.EndpointsConfig[string(hostConfig.NetworkMode)] = epConfig
   637  	}
   638  
   639  	return config, hostConfig, networkingConfig, nil
   640  }
   641  
   642  // reads a file of line terminated key=value pairs, and overrides any keys
   643  // present in the file with additional pairs specified in the override parameter
   644  func readKVStrings(files []string, override []string) ([]string, error) {
   645  	envVariables := []string{}
   646  	for _, ef := range files {
   647  		parsedVars, err := ParseEnvFile(ef)
   648  		if err != nil {
   649  			return nil, err
   650  		}
   651  		envVariables = append(envVariables, parsedVars...)
   652  	}
   653  	// parse the '-e' and '--env' after, to allow override
   654  	envVariables = append(envVariables, override...)
   655  
   656  	return envVariables, nil
   657  }
   658  
   659  // ConvertKVStringsToMap converts ["key=value"] to {"key":"value"}
   660  func ConvertKVStringsToMap(values []string) map[string]string {
   661  	result := make(map[string]string, len(values))
   662  	for _, value := range values {
   663  		kv := strings.SplitN(value, "=", 2)
   664  		if len(kv) == 1 {
   665  			result[kv[0]] = ""
   666  		} else {
   667  			result[kv[0]] = kv[1]
   668  		}
   669  	}
   670  
   671  	return result
   672  }
   673  
   674  func parseLoggingOpts(loggingDriver string, loggingOpts []string) (map[string]string, error) {
   675  	loggingOptsMap := ConvertKVStringsToMap(loggingOpts)
   676  	if loggingDriver == "none" && len(loggingOpts) > 0 {
   677  		return map[string]string{}, fmt.Errorf("invalid logging opts for driver %s", loggingDriver)
   678  	}
   679  	return loggingOptsMap, nil
   680  }
   681  
   682  // takes a local seccomp daemon, reads the file contents for sending to the daemon
   683  func parseSecurityOpts(securityOpts []string) ([]string, error) {
   684  	for key, opt := range securityOpts {
   685  		con := strings.SplitN(opt, "=", 2)
   686  		if len(con) == 1 && con[0] != "no-new-privileges" {
   687  			if strings.Index(opt, ":") != -1 {
   688  				con = strings.SplitN(opt, ":", 2)
   689  			} else {
   690  				return securityOpts, fmt.Errorf("Invalid --security-opt: %q", opt)
   691  			}
   692  		}
   693  		if con[0] == "seccomp" && con[1] != "unconfined" {
   694  			f, err := ioutil.ReadFile(con[1])
   695  			if err != nil {
   696  				return securityOpts, fmt.Errorf("opening seccomp profile (%s) failed: %v", con[1], err)
   697  			}
   698  			b := bytes.NewBuffer(nil)
   699  			if err := json.Compact(b, f); err != nil {
   700  				return securityOpts, fmt.Errorf("compacting json for seccomp profile (%s) failed: %v", con[1], err)
   701  			}
   702  			securityOpts[key] = fmt.Sprintf("seccomp=%s", b.Bytes())
   703  		}
   704  	}
   705  
   706  	return securityOpts, nil
   707  }
   708  
   709  // parses storage options per container into a map
   710  func parseStorageOpts(storageOpts []string) (map[string]string, error) {
   711  	m := make(map[string]string)
   712  	for _, option := range storageOpts {
   713  		if strings.Contains(option, "=") {
   714  			opt := strings.SplitN(option, "=", 2)
   715  			m[opt[0]] = opt[1]
   716  		} else {
   717  			return nil, fmt.Errorf("Invalid storage option.")
   718  		}
   719  	}
   720  	return m, nil
   721  }
   722  
   723  // ParseRestartPolicy returns the parsed policy or an error indicating what is incorrect
   724  func ParseRestartPolicy(policy string) (container.RestartPolicy, error) {
   725  	p := container.RestartPolicy{}
   726  
   727  	if policy == "" {
   728  		return p, nil
   729  	}
   730  
   731  	var (
   732  		parts = strings.Split(policy, ":")
   733  		name  = parts[0]
   734  	)
   735  
   736  	p.Name = name
   737  	switch name {
   738  	case "always", "unless-stopped":
   739  		if len(parts) > 1 {
   740  			return p, fmt.Errorf("maximum restart count not valid with restart policy of \"%s\"", name)
   741  		}
   742  	case "no":
   743  		// do nothing
   744  	case "on-failure":
   745  		if len(parts) > 2 {
   746  			return p, fmt.Errorf("restart count format is not valid, usage: 'on-failure:N' or 'on-failure'")
   747  		}
   748  		if len(parts) == 2 {
   749  			count, err := strconv.Atoi(parts[1])
   750  			if err != nil {
   751  				return p, err
   752  			}
   753  
   754  			p.MaximumRetryCount = count
   755  		}
   756  	default:
   757  		return p, fmt.Errorf("invalid restart policy %s", name)
   758  	}
   759  
   760  	return p, nil
   761  }
   762  
   763  // ParseDevice parses a device mapping string to a container.DeviceMapping struct
   764  func ParseDevice(device string) (container.DeviceMapping, error) {
   765  	src := ""
   766  	dst := ""
   767  	permissions := "rwm"
   768  	arr := strings.Split(device, ":")
   769  	switch len(arr) {
   770  	case 3:
   771  		permissions = arr[2]
   772  		fallthrough
   773  	case 2:
   774  		if ValidDeviceMode(arr[1]) {
   775  			permissions = arr[1]
   776  		} else {
   777  			dst = arr[1]
   778  		}
   779  		fallthrough
   780  	case 1:
   781  		src = arr[0]
   782  	default:
   783  		return container.DeviceMapping{}, fmt.Errorf("invalid device specification: %s", device)
   784  	}
   785  
   786  	if dst == "" {
   787  		dst = src
   788  	}
   789  
   790  	deviceMapping := container.DeviceMapping{
   791  		PathOnHost:        src,
   792  		PathInContainer:   dst,
   793  		CgroupPermissions: permissions,
   794  	}
   795  	return deviceMapping, nil
   796  }
   797  
   798  // ParseLink parses and validates the specified string as a link format (name:alias)
   799  func ParseLink(val string) (string, string, error) {
   800  	if val == "" {
   801  		return "", "", fmt.Errorf("empty string specified for links")
   802  	}
   803  	arr := strings.Split(val, ":")
   804  	if len(arr) > 2 {
   805  		return "", "", fmt.Errorf("bad format for links: %s", val)
   806  	}
   807  	if len(arr) == 1 {
   808  		return val, val, nil
   809  	}
   810  	// This is kept because we can actually get a HostConfig with links
   811  	// from an already created container and the format is not `foo:bar`
   812  	// but `/foo:/c1/bar`
   813  	if strings.HasPrefix(arr[0], "/") {
   814  		_, alias := path.Split(arr[1])
   815  		return arr[0][1:], alias, nil
   816  	}
   817  	return arr[0], arr[1], nil
   818  }
   819  
   820  // ValidateLink validates that the specified string has a valid link format (containerName:alias).
   821  func ValidateLink(val string) (string, error) {
   822  	if _, _, err := ParseLink(val); err != nil {
   823  		return val, err
   824  	}
   825  	return val, nil
   826  }
   827  
   828  // ValidDeviceMode checks if the mode for device is valid or not.
   829  // Valid mode is a composition of r (read), w (write), and m (mknod).
   830  func ValidDeviceMode(mode string) bool {
   831  	var legalDeviceMode = map[rune]bool{
   832  		'r': true,
   833  		'w': true,
   834  		'm': true,
   835  	}
   836  	if mode == "" {
   837  		return false
   838  	}
   839  	for _, c := range mode {
   840  		if !legalDeviceMode[c] {
   841  			return false
   842  		}
   843  		legalDeviceMode[c] = false
   844  	}
   845  	return true
   846  }
   847  
   848  // ValidateDevice validates a path for devices
   849  // It will make sure 'val' is in the form:
   850  //    [host-dir:]container-path[:mode]
   851  // It also validates the device mode.
   852  func ValidateDevice(val string) (string, error) {
   853  	return validatePath(val, ValidDeviceMode)
   854  }
   855  
   856  func validatePath(val string, validator func(string) bool) (string, error) {
   857  	var containerPath string
   858  	var mode string
   859  
   860  	if strings.Count(val, ":") > 2 {
   861  		return val, fmt.Errorf("bad format for path: %s", val)
   862  	}
   863  
   864  	split := strings.SplitN(val, ":", 3)
   865  	if split[0] == "" {
   866  		return val, fmt.Errorf("bad format for path: %s", val)
   867  	}
   868  	switch len(split) {
   869  	case 1:
   870  		containerPath = split[0]
   871  		val = path.Clean(containerPath)
   872  	case 2:
   873  		if isValid := validator(split[1]); isValid {
   874  			containerPath = split[0]
   875  			mode = split[1]
   876  			val = fmt.Sprintf("%s:%s", path.Clean(containerPath), mode)
   877  		} else {
   878  			containerPath = split[1]
   879  			val = fmt.Sprintf("%s:%s", split[0], path.Clean(containerPath))
   880  		}
   881  	case 3:
   882  		containerPath = split[1]
   883  		mode = split[2]
   884  		if isValid := validator(split[2]); !isValid {
   885  			return val, fmt.Errorf("bad mode specified: %s", mode)
   886  		}
   887  		val = fmt.Sprintf("%s:%s:%s", split[0], containerPath, mode)
   888  	}
   889  
   890  	if !path.IsAbs(containerPath) {
   891  		return val, fmt.Errorf("%s is not an absolute path", containerPath)
   892  	}
   893  	return val, nil
   894  }
   895  
   896  // volumeSplitN splits raw into a maximum of n parts, separated by a separator colon.
   897  // A separator colon is the last `:` character in the regex `[:\\]?[a-zA-Z]:` (note `\\` is `\` escaped).
   898  // In Windows driver letter appears in two situations:
   899  // a. `^[a-zA-Z]:` (A colon followed  by `^[a-zA-Z]:` is OK as colon is the separator in volume option)
   900  // b. A string in the format like `\\?\C:\Windows\...` (UNC).
   901  // Therefore, a driver letter can only follow either a `:` or `\\`
   902  // This allows to correctly split strings such as `C:\foo:D:\:rw` or `/tmp/q:/foo`.
   903  func volumeSplitN(raw string, n int) []string {
   904  	var array []string
   905  	if len(raw) == 0 || raw[0] == ':' {
   906  		// invalid
   907  		return nil
   908  	}
   909  	// numberOfParts counts the number of parts separated by a separator colon
   910  	numberOfParts := 0
   911  	// left represents the left-most cursor in raw, updated at every `:` character considered as a separator.
   912  	left := 0
   913  	// right represents the right-most cursor in raw incremented with the loop. Note this
   914  	// starts at index 1 as index 0 is already handle above as a special case.
   915  	for right := 1; right < len(raw); right++ {
   916  		// stop parsing if reached maximum number of parts
   917  		if n >= 0 && numberOfParts >= n {
   918  			break
   919  		}
   920  		if raw[right] != ':' {
   921  			continue
   922  		}
   923  		potentialDriveLetter := raw[right-1]
   924  		if (potentialDriveLetter >= 'A' && potentialDriveLetter <= 'Z') || (potentialDriveLetter >= 'a' && potentialDriveLetter <= 'z') {
   925  			if right > 1 {
   926  				beforePotentialDriveLetter := raw[right-2]
   927  				// Only `:` or `\\` are checked (`/` could fall into the case of `/tmp/q:/foo`)
   928  				if beforePotentialDriveLetter != ':' && beforePotentialDriveLetter != '\\' {
   929  					// e.g. `C:` is not preceded by any delimiter, therefore it was not a drive letter but a path ending with `C:`.
   930  					array = append(array, raw[left:right])
   931  					left = right + 1
   932  					numberOfParts++
   933  				}
   934  				// else, `C:` is considered as a drive letter and not as a delimiter, so we continue parsing.
   935  			}
   936  			// if right == 1, then `C:` is the beginning of the raw string, therefore `:` is again not considered a delimiter and we continue parsing.
   937  		} else {
   938  			// if `:` is not preceded by a potential drive letter, then consider it as a delimiter.
   939  			array = append(array, raw[left:right])
   940  			left = right + 1
   941  			numberOfParts++
   942  		}
   943  	}
   944  	// need to take care of the last part
   945  	if left < len(raw) {
   946  		if n >= 0 && numberOfParts >= n {
   947  			// if the maximum number of parts is reached, just append the rest to the last part
   948  			// left-1 is at the last `:` that needs to be included since not considered a separator.
   949  			array[n-1] += raw[left-1:]
   950  		} else {
   951  			array = append(array, raw[left:])
   952  		}
   953  	}
   954  	return array
   955  }