github.com/flavio/docker@v0.1.3-0.20170117145210-f63d1a6eec47/cli/command/container/opts.go (about)

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