opts.go 34 KB

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