opts.go 34 KB

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