opts.go 34 KB

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