14 "github.com/Sirupsen/logrus"
15 "github.com/docker/cli/cli/compose/loader"
16 "github.com/docker/cli/opts"
17 "github.com/docker/docker/api/types/container"
18 networktypes "github.com/docker/docker/api/types/network"
19 "github.com/docker/docker/api/types/strslice"
20 "github.com/docker/docker/pkg/signal"
21 "github.com/docker/go-connections/nat"
22 "github.com/pkg/errors"
23 "github.com/spf13/pflag"
27 deviceCgroupRuleRegexp = regexp.MustCompile(`^[acb] ([0-9]+|\*):([0-9]+|\*) [rwm]{1,3}$`)
30 // containerOptions is a data object with all the options for creating a container
31 type containerOptions struct {
36 blkioWeightDevice opts.WeightdeviceOpt
37 deviceReadBps opts.ThrottledeviceOpt
38 deviceWriteBps opts.ThrottledeviceOpt
41 linkLocalIPs opts.ListOpts
42 deviceReadIOps opts.ThrottledeviceOpt
43 deviceWriteIOps opts.ThrottledeviceOpt
46 deviceCgroupRules opts.ListOpts
48 ulimits *opts.UlimitOpt
53 dnsSearch opts.ListOpts
54 dnsOptions opts.ListOpts
55 extraHosts opts.ListOpts
56 volumesFrom opts.ListOpts
60 groupAdd opts.ListOpts
61 securityOpt opts.ListOpts
62 storageOpt opts.ListOpts
63 labelsFile opts.ListOpts
64 loggingOpts opts.ListOpts
74 containerIDFile string
78 memoryReservation opts.MemBytes
79 memorySwap opts.MemSwapBytes
80 kernelMemory opts.MemBytes
87 cpuRealtimePeriod int64
88 cpuRealtimeRuntime int64
94 ioMaxBandwidth opts.MemBytes
111 shmSize opts.MemBytes
114 healthInterval time.Duration
115 healthTimeout time.Duration
116 healthStartPeriod time.Duration
126 // addFlags adds all command line flags that will be used by parse to the FlagSet
127 func addFlags(flags *pflag.FlagSet) *containerOptions {
128 copts := &containerOptions{
129 aliases: opts.NewListOpts(nil),
130 attach: opts.NewListOpts(validateAttach),
131 blkioWeightDevice: opts.NewWeightdeviceOpt(opts.ValidateWeightDevice),
132 capAdd: opts.NewListOpts(nil),
133 capDrop: opts.NewListOpts(nil),
134 dns: opts.NewListOpts(opts.ValidateIPAddress),
135 dnsOptions: opts.NewListOpts(nil),
136 dnsSearch: opts.NewListOpts(opts.ValidateDNSSearch),
137 deviceCgroupRules: opts.NewListOpts(validateDeviceCgroupRule),
138 deviceReadBps: opts.NewThrottledeviceOpt(opts.ValidateThrottleBpsDevice),
139 deviceReadIOps: opts.NewThrottledeviceOpt(opts.ValidateThrottleIOpsDevice),
140 deviceWriteBps: opts.NewThrottledeviceOpt(opts.ValidateThrottleBpsDevice),
141 deviceWriteIOps: opts.NewThrottledeviceOpt(opts.ValidateThrottleIOpsDevice),
142 devices: opts.NewListOpts(validateDevice),
143 env: opts.NewListOpts(opts.ValidateEnv),
144 envFile: opts.NewListOpts(nil),
145 expose: opts.NewListOpts(nil),
146 extraHosts: opts.NewListOpts(opts.ValidateExtraHost),
147 groupAdd: opts.NewListOpts(nil),
148 labels: opts.NewListOpts(opts.ValidateEnv),
149 labelsFile: opts.NewListOpts(nil),
150 linkLocalIPs: opts.NewListOpts(nil),
151 links: opts.NewListOpts(opts.ValidateLink),
152 loggingOpts: opts.NewListOpts(nil),
153 publish: opts.NewListOpts(nil),
154 securityOpt: opts.NewListOpts(nil),
155 storageOpt: opts.NewListOpts(nil),
156 sysctls: opts.NewMapOpts(nil, opts.ValidateSysctl),
157 tmpfs: opts.NewListOpts(nil),
158 ulimits: opts.NewUlimitOpt(nil),
159 volumes: opts.NewListOpts(nil),
160 volumesFrom: opts.NewListOpts(nil),
163 // General purpose flags
164 flags.VarP(&copts.attach, "attach", "a", "Attach to STDIN, STDOUT or STDERR")
165 flags.Var(&copts.deviceCgroupRules, "device-cgroup-rule", "Add a rule to the cgroup allowed devices list")
166 flags.Var(&copts.devices, "device", "Add a host device to the container")
167 flags.VarP(&copts.env, "env", "e", "Set environment variables")
168 flags.Var(&copts.envFile, "env-file", "Read in a file of environment variables")
169 flags.StringVar(&copts.entrypoint, "entrypoint", "", "Overwrite the default ENTRYPOINT of the image")
170 flags.Var(&copts.groupAdd, "group-add", "Add additional groups to join")
171 flags.StringVarP(&copts.hostname, "hostname", "h", "", "Container host name")
172 flags.BoolVarP(&copts.stdin, "interactive", "i", false, "Keep STDIN open even if not attached")
173 flags.VarP(&copts.labels, "label", "l", "Set meta data on a container")
174 flags.Var(&copts.labelsFile, "label-file", "Read in a line delimited file of labels")
175 flags.BoolVar(&copts.readonlyRootfs, "read-only", false, "Mount the container's root filesystem as read only")
176 flags.StringVar(&copts.restartPolicy, "restart", "no", "Restart policy to apply when a container exits")
177 flags.StringVar(&copts.stopSignal, "stop-signal", signal.DefaultStopSignal, "Signal to stop a container")
178 flags.IntVar(&copts.stopTimeout, "stop-timeout", 0, "Timeout (in seconds) to stop a container")
179 flags.SetAnnotation("stop-timeout", "version", []string{"1.25"})
180 flags.Var(copts.sysctls, "sysctl", "Sysctl options")
181 flags.BoolVarP(&copts.tty, "tty", "t", false, "Allocate a pseudo-TTY")
182 flags.Var(copts.ulimits, "ulimit", "Ulimit options")
183 flags.StringVarP(&copts.user, "user", "u", "", "Username or UID (format: <name|uid>[:<group|gid>])")
184 flags.StringVarP(&copts.workingDir, "workdir", "w", "", "Working directory inside the container")
185 flags.BoolVar(&copts.autoRemove, "rm", false, "Automatically remove the container when it exits")
188 flags.Var(&copts.capAdd, "cap-add", "Add Linux capabilities")
189 flags.Var(&copts.capDrop, "cap-drop", "Drop Linux capabilities")
190 flags.BoolVar(&copts.privileged, "privileged", false, "Give extended privileges to this container")
191 flags.Var(&copts.securityOpt, "security-opt", "Security Options")
192 flags.StringVar(&copts.usernsMode, "userns", "", "User namespace to use")
194 // Network and port publishing flag
195 flags.Var(&copts.extraHosts, "add-host", "Add a custom host-to-IP mapping (host:ip)")
196 flags.Var(&copts.dns, "dns", "Set custom DNS servers")
197 // We allow for both "--dns-opt" and "--dns-option", although the latter is the recommended way.
198 // This is to be consistent with service create/update
199 flags.Var(&copts.dnsOptions, "dns-opt", "Set DNS options")
200 flags.Var(&copts.dnsOptions, "dns-option", "Set DNS options")
201 flags.MarkHidden("dns-opt")
202 flags.Var(&copts.dnsSearch, "dns-search", "Set custom DNS search domains")
203 flags.Var(&copts.expose, "expose", "Expose a port or a range of ports")
204 flags.StringVar(&copts.ipv4Address, "ip", "", "IPv4 address (e.g., 172.30.100.104)")
205 flags.StringVar(&copts.ipv6Address, "ip6", "", "IPv6 address (e.g., 2001:db8::33)")
206 flags.Var(&copts.links, "link", "Add link to another container")
207 flags.Var(&copts.linkLocalIPs, "link-local-ip", "Container IPv4/IPv6 link-local addresses")
208 flags.StringVar(&copts.macAddress, "mac-address", "", "Container MAC address (e.g., 92:d0:c6:0a:29:33)")
209 flags.VarP(&copts.publish, "publish", "p", "Publish a container's port(s) to the host")
210 flags.BoolVarP(&copts.publishAll, "publish-all", "P", false, "Publish all exposed ports to random ports")
211 // We allow for both "--net" and "--network", although the latter is the recommended way.
212 flags.StringVar(&copts.netMode, "net", "default", "Connect a container to a network")
213 flags.StringVar(&copts.netMode, "network", "default", "Connect a container to a network")
214 flags.MarkHidden("net")
215 // We allow for both "--net-alias" and "--network-alias", although the latter is the recommended way.
216 flags.Var(&copts.aliases, "net-alias", "Add network-scoped alias for the container")
217 flags.Var(&copts.aliases, "network-alias", "Add network-scoped alias for the container")
218 flags.MarkHidden("net-alias")
220 // Logging and storage
221 flags.StringVar(&copts.loggingDriver, "log-driver", "", "Logging driver for the container")
222 flags.StringVar(&copts.volumeDriver, "volume-driver", "", "Optional volume driver for the container")
223 flags.Var(&copts.loggingOpts, "log-opt", "Log driver options")
224 flags.Var(&copts.storageOpt, "storage-opt", "Storage driver options for the container")
225 flags.Var(&copts.tmpfs, "tmpfs", "Mount a tmpfs directory")
226 flags.Var(&copts.volumesFrom, "volumes-from", "Mount volumes from the specified container(s)")
227 flags.VarP(&copts.volumes, "volume", "v", "Bind mount a volume")
228 flags.Var(&copts.mounts, "mount", "Attach a filesystem mount to the container")
231 flags.StringVar(&copts.healthCmd, "health-cmd", "", "Command to run to check health")
232 flags.DurationVar(&copts.healthInterval, "health-interval", 0, "Time between running the check (ms|s|m|h) (default 0s)")
233 flags.IntVar(&copts.healthRetries, "health-retries", 0, "Consecutive failures needed to report unhealthy")
234 flags.DurationVar(&copts.healthTimeout, "health-timeout", 0, "Maximum time to allow one check to run (ms|s|m|h) (default 0s)")
235 flags.DurationVar(&copts.healthStartPeriod, "health-start-period", 0, "Start period for the container to initialize before starting health-retries countdown (ms|s|m|h) (default 0s)")
236 flags.SetAnnotation("health-start-period", "version", []string{"1.29"})
237 flags.BoolVar(&copts.noHealthcheck, "no-healthcheck", false, "Disable any container-specified HEALTHCHECK")
239 // Resource management
240 flags.Uint16Var(&copts.blkioWeight, "blkio-weight", 0, "Block IO (relative weight), between 10 and 1000, or 0 to disable (default 0)")
241 flags.Var(&copts.blkioWeightDevice, "blkio-weight-device", "Block IO weight (relative device weight)")
242 flags.StringVar(&copts.containerIDFile, "cidfile", "", "Write the container ID to the file")
243 flags.StringVar(&copts.cpusetCpus, "cpuset-cpus", "", "CPUs in which to allow execution (0-3, 0,1)")
244 flags.StringVar(&copts.cpusetMems, "cpuset-mems", "", "MEMs in which to allow execution (0-3, 0,1)")
245 flags.Int64Var(&copts.cpuCount, "cpu-count", 0, "CPU count (Windows only)")
246 flags.SetAnnotation("cpu-count", "ostype", []string{"windows"})
247 flags.Int64Var(&copts.cpuPercent, "cpu-percent", 0, "CPU percent (Windows only)")
248 flags.SetAnnotation("cpu-percent", "ostype", []string{"windows"})
249 flags.Int64Var(&copts.cpuPeriod, "cpu-period", 0, "Limit CPU CFS (Completely Fair Scheduler) period")
250 flags.Int64Var(&copts.cpuQuota, "cpu-quota", 0, "Limit CPU CFS (Completely Fair Scheduler) quota")
251 flags.Int64Var(&copts.cpuRealtimePeriod, "cpu-rt-period", 0, "Limit CPU real-time period in microseconds")
252 flags.SetAnnotation("cpu-rt-period", "version", []string{"1.25"})
253 flags.Int64Var(&copts.cpuRealtimeRuntime, "cpu-rt-runtime", 0, "Limit CPU real-time runtime in microseconds")
254 flags.SetAnnotation("cpu-rt-runtime", "version", []string{"1.25"})
255 flags.Int64VarP(&copts.cpuShares, "cpu-shares", "c", 0, "CPU shares (relative weight)")
256 flags.Var(&copts.cpus, "cpus", "Number of CPUs")
257 flags.SetAnnotation("cpus", "version", []string{"1.25"})
258 flags.Var(&copts.deviceReadBps, "device-read-bps", "Limit read rate (bytes per second) from a device")
259 flags.Var(&copts.deviceReadIOps, "device-read-iops", "Limit read rate (IO per second) from a device")
260 flags.Var(&copts.deviceWriteBps, "device-write-bps", "Limit write rate (bytes per second) to a device")
261 flags.Var(&copts.deviceWriteIOps, "device-write-iops", "Limit write rate (IO per second) to a device")
262 flags.Var(&copts.ioMaxBandwidth, "io-maxbandwidth", "Maximum IO bandwidth limit for the system drive (Windows only)")
263 flags.SetAnnotation("io-maxbandwidth", "ostype", []string{"windows"})
264 flags.Uint64Var(&copts.ioMaxIOps, "io-maxiops", 0, "Maximum IOps limit for the system drive (Windows only)")
265 flags.SetAnnotation("io-maxiops", "ostype", []string{"windows"})
266 flags.Var(&copts.kernelMemory, "kernel-memory", "Kernel memory limit")
267 flags.VarP(&copts.memory, "memory", "m", "Memory limit")
268 flags.Var(&copts.memoryReservation, "memory-reservation", "Memory soft limit")
269 flags.Var(&copts.memorySwap, "memory-swap", "Swap limit equal to memory plus swap: '-1' to enable unlimited swap")
270 flags.Int64Var(&copts.swappiness, "memory-swappiness", -1, "Tune container memory swappiness (0 to 100)")
271 flags.BoolVar(&copts.oomKillDisable, "oom-kill-disable", false, "Disable OOM Killer")
272 flags.IntVar(&copts.oomScoreAdj, "oom-score-adj", 0, "Tune host's OOM preferences (-1000 to 1000)")
273 flags.Int64Var(&copts.pidsLimit, "pids-limit", 0, "Tune container pids limit (set -1 for unlimited)")
275 // Low-level execution (cgroups, namespaces, ...)
276 flags.StringVar(&copts.cgroupParent, "cgroup-parent", "", "Optional parent cgroup for the container")
277 flags.StringVar(&copts.ipcMode, "ipc", "", "IPC namespace to use")
278 flags.StringVar(&copts.isolation, "isolation", "", "Container isolation technology")
279 flags.StringVar(&copts.pidMode, "pid", "", "PID namespace to use")
280 flags.Var(&copts.shmSize, "shm-size", "Size of /dev/shm")
281 flags.StringVar(&copts.utsMode, "uts", "", "UTS namespace to use")
282 flags.StringVar(&copts.runtime, "runtime", "", "Runtime to use for this container")
284 flags.BoolVar(&copts.init, "init", false, "Run an init inside the container that forwards signals and reaps processes")
285 flags.SetAnnotation("init", "version", []string{"1.25"})
289 type containerConfig struct {
290 Config *container.Config
291 HostConfig *container.HostConfig
292 NetworkingConfig *networktypes.NetworkingConfig
295 // parse parses the args for the specified command and generates a Config,
296 // a HostConfig and returns them with the specified command.
297 // If the specified args are not valid, it will return an error.
299 func parse(flags *pflag.FlagSet, copts *containerOptions) (*containerConfig, error) {
301 attachStdin = copts.attach.Get("stdin")
302 attachStdout = copts.attach.Get("stdout")
303 attachStderr = copts.attach.Get("stderr")
306 // Validate the input mac address
307 if copts.macAddress != "" {
308 if _, err := opts.ValidateMACAddress(copts.macAddress); err != nil {
309 return nil, errors.Errorf("%s is not a valid mac address", copts.macAddress)
315 // If -a is not set, attach to stdout and stderr
316 if copts.attach.Len() == 0 {
323 swappiness := copts.swappiness
324 if swappiness != -1 && (swappiness < 0 || swappiness > 100) {
325 return nil, errors.Errorf("invalid value: %d. Valid memory swappiness range is 0-100", swappiness)
328 mounts := copts.mounts.Value()
329 if len(mounts) > 0 && copts.volumeDriver != "" {
330 logrus.Warn("`--volume-driver` is ignored for volumes specified via `--mount`. Use `--mount type=volume,volume-driver=...` instead.")
333 volumes := copts.volumes.GetMap()
334 // add any bind targets to the list of container volumes
335 for bind := range copts.volumes.GetMap() {
336 parsed, _ := loader.ParseVolume(bind)
337 if parsed.Source != "" {
338 // after creating the bind mount we want to delete it from the copts.volumes values because
339 // we do not want bind mounts being committed to image configs
340 binds = append(binds, bind)
341 // We should delete from the map (`volumes`) here, as deleting from copts.volumes will not work if
342 // there are duplicates entries.
343 delete(volumes, bind)
347 // Can't evaluate options passed into --tmpfs until we actually mount
348 tmpfs := make(map[string]string)
349 for _, t := range copts.tmpfs.GetAll() {
350 if arr := strings.SplitN(t, ":", 2); len(arr) > 1 {
351 tmpfs[arr[0]] = arr[1]
358 runCmd strslice.StrSlice
359 entrypoint strslice.StrSlice
362 if len(copts.Args) > 0 {
363 runCmd = strslice.StrSlice(copts.Args)
366 if copts.entrypoint != "" {
367 entrypoint = strslice.StrSlice{copts.entrypoint}
368 } else if flags.Changed("entrypoint") {
369 // if `--entrypoint=` is parsed then Entrypoint is reset
370 entrypoint = []string{""}
373 ports, portBindings, err := nat.ParsePortSpecs(copts.publish.GetAll())
378 // Merge in exposed ports to the map of published ports
379 for _, e := range copts.expose.GetAll() {
380 if strings.Contains(e, ":") {
381 return nil, errors.Errorf("invalid port format for --expose: %s", e)
383 //support two formats for expose, original format <portnum>/[<proto>] or <startport-endport>/[<proto>]
384 proto, port := nat.SplitProtoPort(e)
385 //parse the start and end port and create a sequence of ports to expose
386 //if expose a port, the start and end port are the same
387 start, end, err := nat.ParsePortRange(port)
389 return nil, errors.Errorf("invalid range format for --expose: %s, error: %s", e, err)
391 for i := start; i <= end; i++ {
392 p, err := nat.NewPort(proto, strconv.FormatUint(i, 10))
396 if _, exists := ports[p]; !exists {
397 ports[p] = struct{}{}
402 // parse device mappings
403 deviceMappings := []container.DeviceMapping{}
404 for _, device := range copts.devices.GetAll() {
405 deviceMapping, err := parseDevice(device)
409 deviceMappings = append(deviceMappings, deviceMapping)
412 // collect all the environment variables for the container
413 envVariables, err := opts.ReadKVStrings(copts.envFile.GetAll(), copts.env.GetAll())
418 // collect all the labels for the container
419 labels, err := opts.ReadKVStrings(copts.labelsFile.GetAll(), copts.labels.GetAll())
424 ipcMode := container.IpcMode(copts.ipcMode)
425 if !ipcMode.Valid() {
426 return nil, errors.Errorf("--ipc: invalid IPC mode")
429 pidMode := container.PidMode(copts.pidMode)
430 if !pidMode.Valid() {
431 return nil, errors.Errorf("--pid: invalid PID mode")
434 utsMode := container.UTSMode(copts.utsMode)
435 if !utsMode.Valid() {
436 return nil, errors.Errorf("--uts: invalid UTS mode")
439 usernsMode := container.UsernsMode(copts.usernsMode)
440 if !usernsMode.Valid() {
441 return nil, errors.Errorf("--userns: invalid USER mode")
444 restartPolicy, err := opts.ParseRestartPolicy(copts.restartPolicy)
449 loggingOpts, err := parseLoggingOpts(copts.loggingDriver, copts.loggingOpts.GetAll())
454 securityOpts, err := parseSecurityOpts(copts.securityOpt.GetAll())
459 storageOpts, err := parseStorageOpts(copts.storageOpt.GetAll())
465 var healthConfig *container.HealthConfig
466 haveHealthSettings := copts.healthCmd != "" ||
467 copts.healthInterval != 0 ||
468 copts.healthTimeout != 0 ||
469 copts.healthStartPeriod != 0 ||
470 copts.healthRetries != 0
471 if copts.noHealthcheck {
472 if haveHealthSettings {
473 return nil, errors.Errorf("--no-healthcheck conflicts with --health-* options")
475 test := strslice.StrSlice{"NONE"}
476 healthConfig = &container.HealthConfig{Test: test}
477 } else if haveHealthSettings {
478 var probe strslice.StrSlice
479 if copts.healthCmd != "" {
480 args := []string{"CMD-SHELL", copts.healthCmd}
481 probe = strslice.StrSlice(args)
483 if copts.healthInterval < 0 {
484 return nil, errors.Errorf("--health-interval cannot be negative")
486 if copts.healthTimeout < 0 {
487 return nil, errors.Errorf("--health-timeout cannot be negative")
489 if copts.healthRetries < 0 {
490 return nil, errors.Errorf("--health-retries cannot be negative")
492 if copts.healthStartPeriod < 0 {
493 return nil, fmt.Errorf("--health-start-period cannot be negative")
496 healthConfig = &container.HealthConfig{
498 Interval: copts.healthInterval,
499 Timeout: copts.healthTimeout,
500 StartPeriod: copts.healthStartPeriod,
501 Retries: copts.healthRetries,
505 resources := container.Resources{
506 CgroupParent: copts.cgroupParent,
507 Memory: copts.memory.Value(),
508 MemoryReservation: copts.memoryReservation.Value(),
509 MemorySwap: copts.memorySwap.Value(),
510 MemorySwappiness: &copts.swappiness,
511 KernelMemory: copts.kernelMemory.Value(),
512 OomKillDisable: &copts.oomKillDisable,
513 NanoCPUs: copts.cpus.Value(),
514 CPUCount: copts.cpuCount,
515 CPUPercent: copts.cpuPercent,
516 CPUShares: copts.cpuShares,
517 CPUPeriod: copts.cpuPeriod,
518 CpusetCpus: copts.cpusetCpus,
519 CpusetMems: copts.cpusetMems,
520 CPUQuota: copts.cpuQuota,
521 CPURealtimePeriod: copts.cpuRealtimePeriod,
522 CPURealtimeRuntime: copts.cpuRealtimeRuntime,
523 PidsLimit: copts.pidsLimit,
524 BlkioWeight: copts.blkioWeight,
525 BlkioWeightDevice: copts.blkioWeightDevice.GetList(),
526 BlkioDeviceReadBps: copts.deviceReadBps.GetList(),
527 BlkioDeviceWriteBps: copts.deviceWriteBps.GetList(),
528 BlkioDeviceReadIOps: copts.deviceReadIOps.GetList(),
529 BlkioDeviceWriteIOps: copts.deviceWriteIOps.GetList(),
530 IOMaximumIOps: copts.ioMaxIOps,
531 IOMaximumBandwidth: uint64(copts.ioMaxBandwidth),
532 Ulimits: copts.ulimits.GetList(),
533 DeviceCgroupRules: copts.deviceCgroupRules.GetAll(),
534 Devices: deviceMappings,
537 config := &container.Config{
538 Hostname: copts.hostname,
542 // TODO: deprecated, it comes from -n, --networking
543 // it's still needed internally to set the network to disabled
544 // if e.g. bridge is none in daemon opts, and in inspect
545 NetworkDisabled: false,
546 OpenStdin: copts.stdin,
547 AttachStdin: attachStdin,
548 AttachStdout: attachStdout,
549 AttachStderr: attachStderr,
554 MacAddress: copts.macAddress,
555 Entrypoint: entrypoint,
556 WorkingDir: copts.workingDir,
557 Labels: opts.ConvertKVStringsToMap(labels),
558 Healthcheck: healthConfig,
560 if flags.Changed("stop-signal") {
561 config.StopSignal = copts.stopSignal
563 if flags.Changed("stop-timeout") {
564 config.StopTimeout = &copts.stopTimeout
567 hostConfig := &container.HostConfig{
569 ContainerIDFile: copts.containerIDFile,
570 OomScoreAdj: copts.oomScoreAdj,
571 AutoRemove: copts.autoRemove,
572 Privileged: copts.privileged,
573 PortBindings: portBindings,
574 Links: copts.links.GetAll(),
575 PublishAllPorts: copts.publishAll,
576 // Make sure the dns fields are never nil.
577 // New containers don't ever have those fields nil,
578 // but pre created containers can still have those nil values.
579 // See https://github.com/docker/docker/pull/17779
580 // for a more detailed explanation on why we don't want that.
581 DNS: copts.dns.GetAllOrEmpty(),
582 DNSSearch: copts.dnsSearch.GetAllOrEmpty(),
583 DNSOptions: copts.dnsOptions.GetAllOrEmpty(),
584 ExtraHosts: copts.extraHosts.GetAll(),
585 VolumesFrom: copts.volumesFrom.GetAll(),
586 NetworkMode: container.NetworkMode(copts.netMode),
590 UsernsMode: usernsMode,
591 CapAdd: strslice.StrSlice(copts.capAdd.GetAll()),
592 CapDrop: strslice.StrSlice(copts.capDrop.GetAll()),
593 GroupAdd: copts.groupAdd.GetAll(),
594 RestartPolicy: restartPolicy,
595 SecurityOpt: securityOpts,
596 StorageOpt: storageOpts,
597 ReadonlyRootfs: copts.readonlyRootfs,
598 LogConfig: container.LogConfig{Type: copts.loggingDriver, Config: loggingOpts},
599 VolumeDriver: copts.volumeDriver,
600 Isolation: container.Isolation(copts.isolation),
601 ShmSize: copts.shmSize.Value(),
602 Resources: resources,
604 Sysctls: copts.sysctls.GetAll(),
605 Runtime: copts.runtime,
609 if copts.autoRemove && !hostConfig.RestartPolicy.IsNone() {
610 return nil, errors.Errorf("Conflicting options: --restart and --rm")
613 // only set this value if the user provided the flag, else it should default to nil
614 if flags.Changed("init") {
615 hostConfig.Init = &copts.init
618 // When allocating stdin in attached mode, close stdin at client disconnect
619 if config.OpenStdin && config.AttachStdin {
620 config.StdinOnce = true
623 networkingConfig := &networktypes.NetworkingConfig{
624 EndpointsConfig: make(map[string]*networktypes.EndpointSettings),
627 if copts.ipv4Address != "" || copts.ipv6Address != "" || copts.linkLocalIPs.Len() > 0 {
628 epConfig := &networktypes.EndpointSettings{}
629 networkingConfig.EndpointsConfig[string(hostConfig.NetworkMode)] = epConfig
631 epConfig.IPAMConfig = &networktypes.EndpointIPAMConfig{
632 IPv4Address: copts.ipv4Address,
633 IPv6Address: copts.ipv6Address,
636 if copts.linkLocalIPs.Len() > 0 {
637 epConfig.IPAMConfig.LinkLocalIPs = make([]string, copts.linkLocalIPs.Len())
638 copy(epConfig.IPAMConfig.LinkLocalIPs, copts.linkLocalIPs.GetAll())
642 if hostConfig.NetworkMode.IsUserDefined() && len(hostConfig.Links) > 0 {
643 epConfig := networkingConfig.EndpointsConfig[string(hostConfig.NetworkMode)]
645 epConfig = &networktypes.EndpointSettings{}
647 epConfig.Links = make([]string, len(hostConfig.Links))
648 copy(epConfig.Links, hostConfig.Links)
649 networkingConfig.EndpointsConfig[string(hostConfig.NetworkMode)] = epConfig
652 if copts.aliases.Len() > 0 {
653 epConfig := networkingConfig.EndpointsConfig[string(hostConfig.NetworkMode)]
655 epConfig = &networktypes.EndpointSettings{}
657 epConfig.Aliases = make([]string, copts.aliases.Len())
658 copy(epConfig.Aliases, copts.aliases.GetAll())
659 networkingConfig.EndpointsConfig[string(hostConfig.NetworkMode)] = epConfig
662 return &containerConfig{
664 HostConfig: hostConfig,
665 NetworkingConfig: networkingConfig,
669 func parseLoggingOpts(loggingDriver string, loggingOpts []string) (map[string]string, error) {
670 loggingOptsMap := opts.ConvertKVStringsToMap(loggingOpts)
671 if loggingDriver == "none" && len(loggingOpts) > 0 {
672 return map[string]string{}, errors.Errorf("invalid logging opts for driver %s", loggingDriver)
674 return loggingOptsMap, nil
677 // takes a local seccomp daemon, reads the file contents for sending to the daemon
678 func parseSecurityOpts(securityOpts []string) ([]string, error) {
679 for key, opt := range securityOpts {
680 con := strings.SplitN(opt, "=", 2)
681 if len(con) == 1 && con[0] != "no-new-privileges" {
682 if strings.Contains(opt, ":") {
683 con = strings.SplitN(opt, ":", 2)
685 return securityOpts, errors.Errorf("Invalid --security-opt: %q", opt)
688 if con[0] == "seccomp" && con[1] != "unconfined" {
689 f, err := ioutil.ReadFile(con[1])
691 return securityOpts, errors.Errorf("opening seccomp profile (%s) failed: %v", con[1], err)
693 b := bytes.NewBuffer(nil)
694 if err := json.Compact(b, f); err != nil {
695 return securityOpts, errors.Errorf("compacting json for seccomp profile (%s) failed: %v", con[1], err)
697 securityOpts[key] = fmt.Sprintf("seccomp=%s", b.Bytes())
701 return securityOpts, nil
704 // parses storage options per container into a map
705 func parseStorageOpts(storageOpts []string) (map[string]string, error) {
706 m := make(map[string]string)
707 for _, option := range storageOpts {
708 if strings.Contains(option, "=") {
709 opt := strings.SplitN(option, "=", 2)
712 return nil, errors.Errorf("invalid storage option")
718 // parseDevice parses a device mapping string to a container.DeviceMapping struct
719 func parseDevice(device string) (container.DeviceMapping, error) {
723 arr := strings.Split(device, ":")
729 if validDeviceMode(arr[1]) {
738 return container.DeviceMapping{}, errors.Errorf("invalid device specification: %s", device)
745 deviceMapping := container.DeviceMapping{
747 PathInContainer: dst,
748 CgroupPermissions: permissions,
750 return deviceMapping, nil
753 // validateDeviceCgroupRule validates a device cgroup rule string format
754 // It will make sure 'val' is in the form:
755 // 'type major:minor mode'
756 func validateDeviceCgroupRule(val string) (string, error) {
757 if deviceCgroupRuleRegexp.MatchString(val) {
761 return val, errors.Errorf("invalid device cgroup format '%s'", val)
764 // validDeviceMode checks if the mode for device is valid or not.
765 // Valid mode is a composition of r (read), w (write), and m (mknod).
766 func validDeviceMode(mode string) bool {
767 var legalDeviceMode = map[rune]bool{
775 for _, c := range mode {
776 if !legalDeviceMode[c] {
779 legalDeviceMode[c] = false
784 // validateDevice validates a path for devices
785 // It will make sure 'val' is in the form:
786 // [host-dir:]container-path[:mode]
787 // It also validates the device mode.
788 func validateDevice(val string) (string, error) {
789 return validatePath(val, validDeviceMode)
792 func validatePath(val string, validator func(string) bool) (string, error) {
793 var containerPath string
796 if strings.Count(val, ":") > 2 {
797 return val, errors.Errorf("bad format for path: %s", val)
800 split := strings.SplitN(val, ":", 3)
802 return val, errors.Errorf("bad format for path: %s", val)
806 containerPath = split[0]
807 val = path.Clean(containerPath)
809 if isValid := validator(split[1]); isValid {
810 containerPath = split[0]
812 val = fmt.Sprintf("%s:%s", path.Clean(containerPath), mode)
814 containerPath = split[1]
815 val = fmt.Sprintf("%s:%s", split[0], path.Clean(containerPath))
818 containerPath = split[1]
820 if isValid := validator(split[2]); !isValid {
821 return val, errors.Errorf("bad mode specified: %s", mode)
823 val = fmt.Sprintf("%s:%s:%s", split[0], containerPath, mode)
826 if !path.IsAbs(containerPath) {
827 return val, errors.Errorf("%s is not an absolute path", containerPath)
832 // validateAttach validates that the specified string is a valid attach option.
833 func validateAttach(val string) (string, error) {
834 s := strings.ToLower(val)
835 for _, str := range []string{"stdin", "stdout", "stderr"} {
840 return val, errors.Errorf("valid streams are STDIN, STDOUT and STDERR")