opts.go 35 KB

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