Tizen_4.0 base
[platform/upstream/docker-engine.git] / vendor / github.com / docker / cli / cli / command / container / opts.go
1 package container
2
3 import (
4         "bytes"
5         "encoding/json"
6         "fmt"
7         "io/ioutil"
8         "path"
9         "regexp"
10         "strconv"
11         "strings"
12         "time"
13
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"
24 )
25
26 var (
27         deviceCgroupRuleRegexp = regexp.MustCompile(`^[acb] ([0-9]+|\*):([0-9]+|\*) [rwm]{1,3}$`)
28 )
29
30 // containerOptions is a data object with all the options for creating a container
31 type containerOptions struct {
32         attach             opts.ListOpts
33         volumes            opts.ListOpts
34         tmpfs              opts.ListOpts
35         mounts             opts.MountOpt
36         blkioWeightDevice  opts.WeightdeviceOpt
37         deviceReadBps      opts.ThrottledeviceOpt
38         deviceWriteBps     opts.ThrottledeviceOpt
39         links              opts.ListOpts
40         aliases            opts.ListOpts
41         linkLocalIPs       opts.ListOpts
42         deviceReadIOps     opts.ThrottledeviceOpt
43         deviceWriteIOps    opts.ThrottledeviceOpt
44         env                opts.ListOpts
45         labels             opts.ListOpts
46         deviceCgroupRules  opts.ListOpts
47         devices            opts.ListOpts
48         ulimits            *opts.UlimitOpt
49         sysctls            *opts.MapOpts
50         publish            opts.ListOpts
51         expose             opts.ListOpts
52         dns                opts.ListOpts
53         dnsSearch          opts.ListOpts
54         dnsOptions         opts.ListOpts
55         extraHosts         opts.ListOpts
56         volumesFrom        opts.ListOpts
57         envFile            opts.ListOpts
58         capAdd             opts.ListOpts
59         capDrop            opts.ListOpts
60         groupAdd           opts.ListOpts
61         securityOpt        opts.ListOpts
62         storageOpt         opts.ListOpts
63         labelsFile         opts.ListOpts
64         loggingOpts        opts.ListOpts
65         privileged         bool
66         pidMode            string
67         utsMode            string
68         usernsMode         string
69         publishAll         bool
70         stdin              bool
71         tty                bool
72         oomKillDisable     bool
73         oomScoreAdj        int
74         containerIDFile    string
75         entrypoint         string
76         hostname           string
77         memory             opts.MemBytes
78         memoryReservation  opts.MemBytes
79         memorySwap         opts.MemSwapBytes
80         kernelMemory       opts.MemBytes
81         user               string
82         workingDir         string
83         cpuCount           int64
84         cpuShares          int64
85         cpuPercent         int64
86         cpuPeriod          int64
87         cpuRealtimePeriod  int64
88         cpuRealtimeRuntime int64
89         cpuQuota           int64
90         cpus               opts.NanoCPUs
91         cpusetCpus         string
92         cpusetMems         string
93         blkioWeight        uint16
94         ioMaxBandwidth     opts.MemBytes
95         ioMaxIOps          uint64
96         swappiness         int64
97         netMode            string
98         macAddress         string
99         ipv4Address        string
100         ipv6Address        string
101         ipcMode            string
102         pidsLimit          int64
103         restartPolicy      string
104         readonlyRootfs     bool
105         loggingDriver      string
106         cgroupParent       string
107         volumeDriver       string
108         stopSignal         string
109         stopTimeout        int
110         isolation          string
111         shmSize            opts.MemBytes
112         noHealthcheck      bool
113         healthCmd          string
114         healthInterval     time.Duration
115         healthTimeout      time.Duration
116         healthStartPeriod  time.Duration
117         healthRetries      int
118         runtime            string
119         autoRemove         bool
120         init               bool
121
122         Image string
123         Args  []string
124 }
125
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),
161         }
162
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")
186
187         // Security
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")
193
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")
219
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")
229
230         // Health-checking
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")
238
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)")
274
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")
283
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"})
286         return copts
287 }
288
289 type containerConfig struct {
290         Config           *container.Config
291         HostConfig       *container.HostConfig
292         NetworkingConfig *networktypes.NetworkingConfig
293 }
294
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.
298 // nolint: gocyclo
299 func parse(flags *pflag.FlagSet, copts *containerOptions) (*containerConfig, error) {
300         var (
301                 attachStdin  = copts.attach.Get("stdin")
302                 attachStdout = copts.attach.Get("stdout")
303                 attachStderr = copts.attach.Get("stderr")
304         )
305
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)
310                 }
311         }
312         if copts.stdin {
313                 attachStdin = true
314         }
315         // If -a is not set, attach to stdout and stderr
316         if copts.attach.Len() == 0 {
317                 attachStdout = true
318                 attachStderr = true
319         }
320
321         var err error
322
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)
326         }
327
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.")
331         }
332         var binds []string
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)
344                 }
345         }
346
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]
352                 } else {
353                         tmpfs[arr[0]] = ""
354                 }
355         }
356
357         var (
358                 runCmd     strslice.StrSlice
359                 entrypoint strslice.StrSlice
360         )
361
362         if len(copts.Args) > 0 {
363                 runCmd = strslice.StrSlice(copts.Args)
364         }
365
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{""}
371         }
372
373         ports, portBindings, err := nat.ParsePortSpecs(copts.publish.GetAll())
374         if err != nil {
375                 return nil, err
376         }
377
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)
382                 }
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)
388                 if err != nil {
389                         return nil, errors.Errorf("invalid range format for --expose: %s, error: %s", e, err)
390                 }
391                 for i := start; i <= end; i++ {
392                         p, err := nat.NewPort(proto, strconv.FormatUint(i, 10))
393                         if err != nil {
394                                 return nil, err
395                         }
396                         if _, exists := ports[p]; !exists {
397                                 ports[p] = struct{}{}
398                         }
399                 }
400         }
401
402         // parse device mappings
403         deviceMappings := []container.DeviceMapping{}
404         for _, device := range copts.devices.GetAll() {
405                 deviceMapping, err := parseDevice(device)
406                 if err != nil {
407                         return nil, err
408                 }
409                 deviceMappings = append(deviceMappings, deviceMapping)
410         }
411
412         // collect all the environment variables for the container
413         envVariables, err := opts.ReadKVStrings(copts.envFile.GetAll(), copts.env.GetAll())
414         if err != nil {
415                 return nil, err
416         }
417
418         // collect all the labels for the container
419         labels, err := opts.ReadKVStrings(copts.labelsFile.GetAll(), copts.labels.GetAll())
420         if err != nil {
421                 return nil, err
422         }
423
424         ipcMode := container.IpcMode(copts.ipcMode)
425         if !ipcMode.Valid() {
426                 return nil, errors.Errorf("--ipc: invalid IPC mode")
427         }
428
429         pidMode := container.PidMode(copts.pidMode)
430         if !pidMode.Valid() {
431                 return nil, errors.Errorf("--pid: invalid PID mode")
432         }
433
434         utsMode := container.UTSMode(copts.utsMode)
435         if !utsMode.Valid() {
436                 return nil, errors.Errorf("--uts: invalid UTS mode")
437         }
438
439         usernsMode := container.UsernsMode(copts.usernsMode)
440         if !usernsMode.Valid() {
441                 return nil, errors.Errorf("--userns: invalid USER mode")
442         }
443
444         restartPolicy, err := opts.ParseRestartPolicy(copts.restartPolicy)
445         if err != nil {
446                 return nil, err
447         }
448
449         loggingOpts, err := parseLoggingOpts(copts.loggingDriver, copts.loggingOpts.GetAll())
450         if err != nil {
451                 return nil, err
452         }
453
454         securityOpts, err := parseSecurityOpts(copts.securityOpt.GetAll())
455         if err != nil {
456                 return nil, err
457         }
458
459         storageOpts, err := parseStorageOpts(copts.storageOpt.GetAll())
460         if err != nil {
461                 return nil, err
462         }
463
464         // Healthcheck
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")
474                 }
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)
482                 }
483                 if copts.healthInterval < 0 {
484                         return nil, errors.Errorf("--health-interval cannot be negative")
485                 }
486                 if copts.healthTimeout < 0 {
487                         return nil, errors.Errorf("--health-timeout cannot be negative")
488                 }
489                 if copts.healthRetries < 0 {
490                         return nil, errors.Errorf("--health-retries cannot be negative")
491                 }
492                 if copts.healthStartPeriod < 0 {
493                         return nil, fmt.Errorf("--health-start-period cannot be negative")
494                 }
495
496                 healthConfig = &container.HealthConfig{
497                         Test:        probe,
498                         Interval:    copts.healthInterval,
499                         Timeout:     copts.healthTimeout,
500                         StartPeriod: copts.healthStartPeriod,
501                         Retries:     copts.healthRetries,
502                 }
503         }
504
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,
535         }
536
537         config := &container.Config{
538                 Hostname:     copts.hostname,
539                 ExposedPorts: ports,
540                 User:         copts.user,
541                 Tty:          copts.tty,
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,
550                 Env:             envVariables,
551                 Cmd:             runCmd,
552                 Image:           copts.Image,
553                 Volumes:         volumes,
554                 MacAddress:      copts.macAddress,
555                 Entrypoint:      entrypoint,
556                 WorkingDir:      copts.workingDir,
557                 Labels:          opts.ConvertKVStringsToMap(labels),
558                 Healthcheck:     healthConfig,
559         }
560         if flags.Changed("stop-signal") {
561                 config.StopSignal = copts.stopSignal
562         }
563         if flags.Changed("stop-timeout") {
564                 config.StopTimeout = &copts.stopTimeout
565         }
566
567         hostConfig := &container.HostConfig{
568                 Binds:           binds,
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),
587                 IpcMode:        ipcMode,
588                 PidMode:        pidMode,
589                 UTSMode:        utsMode,
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,
603                 Tmpfs:          tmpfs,
604                 Sysctls:        copts.sysctls.GetAll(),
605                 Runtime:        copts.runtime,
606                 Mounts:         mounts,
607         }
608
609         if copts.autoRemove && !hostConfig.RestartPolicy.IsNone() {
610                 return nil, errors.Errorf("Conflicting options: --restart and --rm")
611         }
612
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
616         }
617
618         // When allocating stdin in attached mode, close stdin at client disconnect
619         if config.OpenStdin && config.AttachStdin {
620                 config.StdinOnce = true
621         }
622
623         networkingConfig := &networktypes.NetworkingConfig{
624                 EndpointsConfig: make(map[string]*networktypes.EndpointSettings),
625         }
626
627         if copts.ipv4Address != "" || copts.ipv6Address != "" || copts.linkLocalIPs.Len() > 0 {
628                 epConfig := &networktypes.EndpointSettings{}
629                 networkingConfig.EndpointsConfig[string(hostConfig.NetworkMode)] = epConfig
630
631                 epConfig.IPAMConfig = &networktypes.EndpointIPAMConfig{
632                         IPv4Address: copts.ipv4Address,
633                         IPv6Address: copts.ipv6Address,
634                 }
635
636                 if copts.linkLocalIPs.Len() > 0 {
637                         epConfig.IPAMConfig.LinkLocalIPs = make([]string, copts.linkLocalIPs.Len())
638                         copy(epConfig.IPAMConfig.LinkLocalIPs, copts.linkLocalIPs.GetAll())
639                 }
640         }
641
642         if hostConfig.NetworkMode.IsUserDefined() && len(hostConfig.Links) > 0 {
643                 epConfig := networkingConfig.EndpointsConfig[string(hostConfig.NetworkMode)]
644                 if epConfig == nil {
645                         epConfig = &networktypes.EndpointSettings{}
646                 }
647                 epConfig.Links = make([]string, len(hostConfig.Links))
648                 copy(epConfig.Links, hostConfig.Links)
649                 networkingConfig.EndpointsConfig[string(hostConfig.NetworkMode)] = epConfig
650         }
651
652         if copts.aliases.Len() > 0 {
653                 epConfig := networkingConfig.EndpointsConfig[string(hostConfig.NetworkMode)]
654                 if epConfig == nil {
655                         epConfig = &networktypes.EndpointSettings{}
656                 }
657                 epConfig.Aliases = make([]string, copts.aliases.Len())
658                 copy(epConfig.Aliases, copts.aliases.GetAll())
659                 networkingConfig.EndpointsConfig[string(hostConfig.NetworkMode)] = epConfig
660         }
661
662         return &containerConfig{
663                 Config:           config,
664                 HostConfig:       hostConfig,
665                 NetworkingConfig: networkingConfig,
666         }, nil
667 }
668
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)
673         }
674         return loggingOptsMap, nil
675 }
676
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)
684                         } else {
685                                 return securityOpts, errors.Errorf("Invalid --security-opt: %q", opt)
686                         }
687                 }
688                 if con[0] == "seccomp" && con[1] != "unconfined" {
689                         f, err := ioutil.ReadFile(con[1])
690                         if err != nil {
691                                 return securityOpts, errors.Errorf("opening seccomp profile (%s) failed: %v", con[1], err)
692                         }
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)
696                         }
697                         securityOpts[key] = fmt.Sprintf("seccomp=%s", b.Bytes())
698                 }
699         }
700
701         return securityOpts, nil
702 }
703
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)
710                         m[opt[0]] = opt[1]
711                 } else {
712                         return nil, errors.Errorf("invalid storage option")
713                 }
714         }
715         return m, nil
716 }
717
718 // parseDevice parses a device mapping string to a container.DeviceMapping struct
719 func parseDevice(device string) (container.DeviceMapping, error) {
720         src := ""
721         dst := ""
722         permissions := "rwm"
723         arr := strings.Split(device, ":")
724         switch len(arr) {
725         case 3:
726                 permissions = arr[2]
727                 fallthrough
728         case 2:
729                 if validDeviceMode(arr[1]) {
730                         permissions = arr[1]
731                 } else {
732                         dst = arr[1]
733                 }
734                 fallthrough
735         case 1:
736                 src = arr[0]
737         default:
738                 return container.DeviceMapping{}, errors.Errorf("invalid device specification: %s", device)
739         }
740
741         if dst == "" {
742                 dst = src
743         }
744
745         deviceMapping := container.DeviceMapping{
746                 PathOnHost:        src,
747                 PathInContainer:   dst,
748                 CgroupPermissions: permissions,
749         }
750         return deviceMapping, nil
751 }
752
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) {
758                 return val, nil
759         }
760
761         return val, errors.Errorf("invalid device cgroup format '%s'", val)
762 }
763
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{
768                 'r': true,
769                 'w': true,
770                 'm': true,
771         }
772         if mode == "" {
773                 return false
774         }
775         for _, c := range mode {
776                 if !legalDeviceMode[c] {
777                         return false
778                 }
779                 legalDeviceMode[c] = false
780         }
781         return true
782 }
783
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)
790 }
791
792 func validatePath(val string, validator func(string) bool) (string, error) {
793         var containerPath string
794         var mode string
795
796         if strings.Count(val, ":") > 2 {
797                 return val, errors.Errorf("bad format for path: %s", val)
798         }
799
800         split := strings.SplitN(val, ":", 3)
801         if split[0] == "" {
802                 return val, errors.Errorf("bad format for path: %s", val)
803         }
804         switch len(split) {
805         case 1:
806                 containerPath = split[0]
807                 val = path.Clean(containerPath)
808         case 2:
809                 if isValid := validator(split[1]); isValid {
810                         containerPath = split[0]
811                         mode = split[1]
812                         val = fmt.Sprintf("%s:%s", path.Clean(containerPath), mode)
813                 } else {
814                         containerPath = split[1]
815                         val = fmt.Sprintf("%s:%s", split[0], path.Clean(containerPath))
816                 }
817         case 3:
818                 containerPath = split[1]
819                 mode = split[2]
820                 if isValid := validator(split[2]); !isValid {
821                         return val, errors.Errorf("bad mode specified: %s", mode)
822                 }
823                 val = fmt.Sprintf("%s:%s:%s", split[0], containerPath, mode)
824         }
825
826         if !path.IsAbs(containerPath) {
827                 return val, errors.Errorf("%s is not an absolute path", containerPath)
828         }
829         return val, nil
830 }
831
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"} {
836                 if s == str {
837                         return s, nil
838                 }
839         }
840         return val, errors.Errorf("valid streams are STDIN, STDOUT and STDERR")
841 }