opts.go 35 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928
  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 string
  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.StringVar(&copts.shmSize, "shm-size", "", "Size of /dev/shm, default value is 64MB")
  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. var shmSize int64
  330. if copts.shmSize != "" {
  331. shmSize, err = units.RAMInBytes(copts.shmSize)
  332. if err != nil {
  333. return nil, nil, nil, err
  334. }
  335. }
  336. // TODO FIXME units.RAMInBytes should have a uint64 version
  337. var maxIOBandwidth int64
  338. if copts.ioMaxBandwidth != "" {
  339. maxIOBandwidth, err = units.RAMInBytes(copts.ioMaxBandwidth)
  340. if err != nil {
  341. return nil, nil, nil, err
  342. }
  343. if maxIOBandwidth < 0 {
  344. return nil, nil, nil, fmt.Errorf("invalid value: %s. Maximum IO Bandwidth must be positive", copts.ioMaxBandwidth)
  345. }
  346. }
  347. var binds []string
  348. volumes := copts.volumes.GetMap()
  349. // add any bind targets to the list of container volumes
  350. for bind := range copts.volumes.GetMap() {
  351. if arr := volumeSplitN(bind, 2); len(arr) > 1 {
  352. // after creating the bind mount we want to delete it from the copts.volumes values because
  353. // we do not want bind mounts being committed to image configs
  354. binds = append(binds, bind)
  355. // We should delete from the map (`volumes`) here, as deleting from copts.volumes will not work if
  356. // there are duplicates entries.
  357. delete(volumes, bind)
  358. }
  359. }
  360. // Can't evaluate options passed into --tmpfs until we actually mount
  361. tmpfs := make(map[string]string)
  362. for _, t := range copts.tmpfs.GetAll() {
  363. if arr := strings.SplitN(t, ":", 2); len(arr) > 1 {
  364. tmpfs[arr[0]] = arr[1]
  365. } else {
  366. tmpfs[arr[0]] = ""
  367. }
  368. }
  369. var (
  370. runCmd strslice.StrSlice
  371. entrypoint strslice.StrSlice
  372. )
  373. if len(copts.Args) > 0 {
  374. runCmd = strslice.StrSlice(copts.Args)
  375. }
  376. if copts.entrypoint != "" {
  377. entrypoint = strslice.StrSlice{copts.entrypoint}
  378. } else if flags.Changed("entrypoint") {
  379. // if `--entrypoint=` is parsed then Entrypoint is reset
  380. entrypoint = []string{""}
  381. }
  382. ports, portBindings, err := nat.ParsePortSpecs(copts.publish.GetAll())
  383. if err != nil {
  384. return nil, nil, nil, err
  385. }
  386. // Merge in exposed ports to the map of published ports
  387. for _, e := range copts.expose.GetAll() {
  388. if strings.Contains(e, ":") {
  389. return nil, nil, nil, fmt.Errorf("invalid port format for --expose: %s", e)
  390. }
  391. //support two formats for expose, original format <portnum>/[<proto>] or <startport-endport>/[<proto>]
  392. proto, port := nat.SplitProtoPort(e)
  393. //parse the start and end port and create a sequence of ports to expose
  394. //if expose a port, the start and end port are the same
  395. start, end, err := nat.ParsePortRange(port)
  396. if err != nil {
  397. return nil, nil, nil, fmt.Errorf("invalid range format for --expose: %s, error: %s", e, err)
  398. }
  399. for i := start; i <= end; i++ {
  400. p, err := nat.NewPort(proto, strconv.FormatUint(i, 10))
  401. if err != nil {
  402. return nil, nil, nil, err
  403. }
  404. if _, exists := ports[p]; !exists {
  405. ports[p] = struct{}{}
  406. }
  407. }
  408. }
  409. // parse device mappings
  410. deviceMappings := []container.DeviceMapping{}
  411. for _, device := range copts.devices.GetAll() {
  412. deviceMapping, err := parseDevice(device)
  413. if err != nil {
  414. return nil, nil, nil, err
  415. }
  416. deviceMappings = append(deviceMappings, deviceMapping)
  417. }
  418. // collect all the environment variables for the container
  419. envVariables, err := runconfigopts.ReadKVStrings(copts.envFile.GetAll(), copts.env.GetAll())
  420. if err != nil {
  421. return nil, nil, nil, err
  422. }
  423. // collect all the labels for the container
  424. labels, err := runconfigopts.ReadKVStrings(copts.labelsFile.GetAll(), copts.labels.GetAll())
  425. if err != nil {
  426. return nil, nil, nil, err
  427. }
  428. ipcMode := container.IpcMode(copts.ipcMode)
  429. if !ipcMode.Valid() {
  430. return nil, nil, nil, fmt.Errorf("--ipc: invalid IPC mode")
  431. }
  432. pidMode := container.PidMode(copts.pidMode)
  433. if !pidMode.Valid() {
  434. return nil, nil, nil, fmt.Errorf("--pid: invalid PID mode")
  435. }
  436. utsMode := container.UTSMode(copts.utsMode)
  437. if !utsMode.Valid() {
  438. return nil, nil, nil, fmt.Errorf("--uts: invalid UTS mode")
  439. }
  440. usernsMode := container.UsernsMode(copts.usernsMode)
  441. if !usernsMode.Valid() {
  442. return nil, nil, nil, fmt.Errorf("--userns: invalid USER mode")
  443. }
  444. restartPolicy, err := runconfigopts.ParseRestartPolicy(copts.restartPolicy)
  445. if err != nil {
  446. return nil, nil, nil, err
  447. }
  448. loggingOpts, err := parseLoggingOpts(copts.loggingDriver, copts.loggingOpts.GetAll())
  449. if err != nil {
  450. return nil, nil, nil, err
  451. }
  452. securityOpts, err := parseSecurityOpts(copts.securityOpt.GetAll())
  453. if err != nil {
  454. return nil, nil, nil, err
  455. }
  456. storageOpts, err := parseStorageOpts(copts.storageOpt.GetAll())
  457. if err != nil {
  458. return nil, nil, nil, err
  459. }
  460. // Healthcheck
  461. var healthConfig *container.HealthConfig
  462. haveHealthSettings := copts.healthCmd != "" ||
  463. copts.healthInterval != 0 ||
  464. copts.healthTimeout != 0 ||
  465. copts.healthRetries != 0
  466. if copts.noHealthcheck {
  467. if haveHealthSettings {
  468. return nil, nil, nil, fmt.Errorf("--no-healthcheck conflicts with --health-* options")
  469. }
  470. test := strslice.StrSlice{"NONE"}
  471. healthConfig = &container.HealthConfig{Test: test}
  472. } else if haveHealthSettings {
  473. var probe strslice.StrSlice
  474. if copts.healthCmd != "" {
  475. args := []string{"CMD-SHELL", copts.healthCmd}
  476. probe = strslice.StrSlice(args)
  477. }
  478. if copts.healthInterval < 0 {
  479. return nil, nil, nil, fmt.Errorf("--health-interval cannot be negative")
  480. }
  481. if copts.healthTimeout < 0 {
  482. return nil, nil, nil, fmt.Errorf("--health-timeout cannot be negative")
  483. }
  484. healthConfig = &container.HealthConfig{
  485. Test: probe,
  486. Interval: copts.healthInterval,
  487. Timeout: copts.healthTimeout,
  488. Retries: copts.healthRetries,
  489. }
  490. }
  491. resources := container.Resources{
  492. CgroupParent: copts.cgroupParent,
  493. Memory: memory,
  494. MemoryReservation: memoryReservation,
  495. MemorySwap: memorySwap,
  496. MemorySwappiness: &copts.swappiness,
  497. KernelMemory: kernelMemory,
  498. OomKillDisable: &copts.oomKillDisable,
  499. NanoCPUs: copts.cpus.Value(),
  500. CPUCount: copts.cpuCount,
  501. CPUPercent: copts.cpuPercent,
  502. CPUShares: copts.cpuShares,
  503. CPUPeriod: copts.cpuPeriod,
  504. CpusetCpus: copts.cpusetCpus,
  505. CpusetMems: copts.cpusetMems,
  506. CPUQuota: copts.cpuQuota,
  507. CPURealtimePeriod: copts.cpuRealtimePeriod,
  508. CPURealtimeRuntime: copts.cpuRealtimeRuntime,
  509. PidsLimit: copts.pidsLimit,
  510. BlkioWeight: copts.blkioWeight,
  511. BlkioWeightDevice: copts.blkioWeightDevice.GetList(),
  512. BlkioDeviceReadBps: copts.deviceReadBps.GetList(),
  513. BlkioDeviceWriteBps: copts.deviceWriteBps.GetList(),
  514. BlkioDeviceReadIOps: copts.deviceReadIOps.GetList(),
  515. BlkioDeviceWriteIOps: copts.deviceWriteIOps.GetList(),
  516. IOMaximumIOps: copts.ioMaxIOps,
  517. IOMaximumBandwidth: uint64(maxIOBandwidth),
  518. Ulimits: copts.ulimits.GetList(),
  519. DeviceCgroupRules: copts.deviceCgroupRules.GetAll(),
  520. Devices: deviceMappings,
  521. }
  522. config := &container.Config{
  523. Hostname: copts.hostname,
  524. ExposedPorts: ports,
  525. User: copts.user,
  526. Tty: copts.tty,
  527. // TODO: deprecated, it comes from -n, --networking
  528. // it's still needed internally to set the network to disabled
  529. // if e.g. bridge is none in daemon opts, and in inspect
  530. NetworkDisabled: false,
  531. OpenStdin: copts.stdin,
  532. AttachStdin: attachStdin,
  533. AttachStdout: attachStdout,
  534. AttachStderr: attachStderr,
  535. Env: envVariables,
  536. Cmd: runCmd,
  537. Image: copts.Image,
  538. Volumes: volumes,
  539. MacAddress: copts.macAddress,
  540. Entrypoint: entrypoint,
  541. WorkingDir: copts.workingDir,
  542. Labels: runconfigopts.ConvertKVStringsToMap(labels),
  543. Healthcheck: healthConfig,
  544. }
  545. if flags.Changed("stop-signal") {
  546. config.StopSignal = copts.stopSignal
  547. }
  548. if flags.Changed("stop-timeout") {
  549. config.StopTimeout = &copts.stopTimeout
  550. }
  551. hostConfig := &container.HostConfig{
  552. Binds: binds,
  553. ContainerIDFile: copts.containerIDFile,
  554. OomScoreAdj: copts.oomScoreAdj,
  555. AutoRemove: copts.autoRemove,
  556. Privileged: copts.privileged,
  557. PortBindings: portBindings,
  558. Links: copts.links.GetAll(),
  559. PublishAllPorts: copts.publishAll,
  560. // Make sure the dns fields are never nil.
  561. // New containers don't ever have those fields nil,
  562. // but pre created containers can still have those nil values.
  563. // See https://github.com/docker/docker/pull/17779
  564. // for a more detailed explanation on why we don't want that.
  565. DNS: copts.dns.GetAllOrEmpty(),
  566. DNSSearch: copts.dnsSearch.GetAllOrEmpty(),
  567. DNSOptions: copts.dnsOptions.GetAllOrEmpty(),
  568. ExtraHosts: copts.extraHosts.GetAll(),
  569. VolumesFrom: copts.volumesFrom.GetAll(),
  570. NetworkMode: container.NetworkMode(copts.netMode),
  571. IpcMode: ipcMode,
  572. PidMode: pidMode,
  573. UTSMode: utsMode,
  574. UsernsMode: usernsMode,
  575. CapAdd: strslice.StrSlice(copts.capAdd.GetAll()),
  576. CapDrop: strslice.StrSlice(copts.capDrop.GetAll()),
  577. GroupAdd: copts.groupAdd.GetAll(),
  578. RestartPolicy: restartPolicy,
  579. SecurityOpt: securityOpts,
  580. StorageOpt: storageOpts,
  581. ReadonlyRootfs: copts.readonlyRootfs,
  582. LogConfig: container.LogConfig{Type: copts.loggingDriver, Config: loggingOpts},
  583. VolumeDriver: copts.volumeDriver,
  584. Isolation: container.Isolation(copts.isolation),
  585. ShmSize: shmSize,
  586. Resources: resources,
  587. Tmpfs: tmpfs,
  588. Sysctls: copts.sysctls.GetAll(),
  589. Runtime: copts.runtime,
  590. }
  591. if copts.autoRemove && !hostConfig.RestartPolicy.IsNone() {
  592. return nil, nil, nil, fmt.Errorf("Conflicting options: --restart and --rm")
  593. }
  594. // only set this value if the user provided the flag, else it should default to nil
  595. if flags.Changed("init") {
  596. hostConfig.Init = &copts.init
  597. }
  598. // When allocating stdin in attached mode, close stdin at client disconnect
  599. if config.OpenStdin && config.AttachStdin {
  600. config.StdinOnce = true
  601. }
  602. networkingConfig := &networktypes.NetworkingConfig{
  603. EndpointsConfig: make(map[string]*networktypes.EndpointSettings),
  604. }
  605. if copts.ipv4Address != "" || copts.ipv6Address != "" || copts.linkLocalIPs.Len() > 0 {
  606. epConfig := &networktypes.EndpointSettings{}
  607. networkingConfig.EndpointsConfig[string(hostConfig.NetworkMode)] = epConfig
  608. epConfig.IPAMConfig = &networktypes.EndpointIPAMConfig{
  609. IPv4Address: copts.ipv4Address,
  610. IPv6Address: copts.ipv6Address,
  611. }
  612. if copts.linkLocalIPs.Len() > 0 {
  613. epConfig.IPAMConfig.LinkLocalIPs = make([]string, copts.linkLocalIPs.Len())
  614. copy(epConfig.IPAMConfig.LinkLocalIPs, copts.linkLocalIPs.GetAll())
  615. }
  616. }
  617. if hostConfig.NetworkMode.IsUserDefined() && len(hostConfig.Links) > 0 {
  618. epConfig := networkingConfig.EndpointsConfig[string(hostConfig.NetworkMode)]
  619. if epConfig == nil {
  620. epConfig = &networktypes.EndpointSettings{}
  621. }
  622. epConfig.Links = make([]string, len(hostConfig.Links))
  623. copy(epConfig.Links, hostConfig.Links)
  624. networkingConfig.EndpointsConfig[string(hostConfig.NetworkMode)] = epConfig
  625. }
  626. if copts.aliases.Len() > 0 {
  627. epConfig := networkingConfig.EndpointsConfig[string(hostConfig.NetworkMode)]
  628. if epConfig == nil {
  629. epConfig = &networktypes.EndpointSettings{}
  630. }
  631. epConfig.Aliases = make([]string, copts.aliases.Len())
  632. copy(epConfig.Aliases, copts.aliases.GetAll())
  633. networkingConfig.EndpointsConfig[string(hostConfig.NetworkMode)] = epConfig
  634. }
  635. return config, hostConfig, networkingConfig, nil
  636. }
  637. func parseLoggingOpts(loggingDriver string, loggingOpts []string) (map[string]string, error) {
  638. loggingOptsMap := runconfigopts.ConvertKVStringsToMap(loggingOpts)
  639. if loggingDriver == "none" && len(loggingOpts) > 0 {
  640. return map[string]string{}, fmt.Errorf("invalid logging opts for driver %s", loggingDriver)
  641. }
  642. return loggingOptsMap, nil
  643. }
  644. // takes a local seccomp daemon, reads the file contents for sending to the daemon
  645. func parseSecurityOpts(securityOpts []string) ([]string, error) {
  646. for key, opt := range securityOpts {
  647. con := strings.SplitN(opt, "=", 2)
  648. if len(con) == 1 && con[0] != "no-new-privileges" {
  649. if strings.Contains(opt, ":") {
  650. con = strings.SplitN(opt, ":", 2)
  651. } else {
  652. return securityOpts, fmt.Errorf("Invalid --security-opt: %q", opt)
  653. }
  654. }
  655. if con[0] == "seccomp" && con[1] != "unconfined" {
  656. f, err := ioutil.ReadFile(con[1])
  657. if err != nil {
  658. return securityOpts, fmt.Errorf("opening seccomp profile (%s) failed: %v", con[1], err)
  659. }
  660. b := bytes.NewBuffer(nil)
  661. if err := json.Compact(b, f); err != nil {
  662. return securityOpts, fmt.Errorf("compacting json for seccomp profile (%s) failed: %v", con[1], err)
  663. }
  664. securityOpts[key] = fmt.Sprintf("seccomp=%s", b.Bytes())
  665. }
  666. }
  667. return securityOpts, nil
  668. }
  669. // parses storage options per container into a map
  670. func parseStorageOpts(storageOpts []string) (map[string]string, error) {
  671. m := make(map[string]string)
  672. for _, option := range storageOpts {
  673. if strings.Contains(option, "=") {
  674. opt := strings.SplitN(option, "=", 2)
  675. m[opt[0]] = opt[1]
  676. } else {
  677. return nil, fmt.Errorf("invalid storage option")
  678. }
  679. }
  680. return m, nil
  681. }
  682. // parseDevice parses a device mapping string to a container.DeviceMapping struct
  683. func parseDevice(device string) (container.DeviceMapping, error) {
  684. src := ""
  685. dst := ""
  686. permissions := "rwm"
  687. arr := strings.Split(device, ":")
  688. switch len(arr) {
  689. case 3:
  690. permissions = arr[2]
  691. fallthrough
  692. case 2:
  693. if validDeviceMode(arr[1]) {
  694. permissions = arr[1]
  695. } else {
  696. dst = arr[1]
  697. }
  698. fallthrough
  699. case 1:
  700. src = arr[0]
  701. default:
  702. return container.DeviceMapping{}, fmt.Errorf("invalid device specification: %s", device)
  703. }
  704. if dst == "" {
  705. dst = src
  706. }
  707. deviceMapping := container.DeviceMapping{
  708. PathOnHost: src,
  709. PathInContainer: dst,
  710. CgroupPermissions: permissions,
  711. }
  712. return deviceMapping, nil
  713. }
  714. // validateDeviceCgroupRule validates a device cgroup rule string format
  715. // It will make sure 'val' is in the form:
  716. // 'type major:minor mode'
  717. func validateDeviceCgroupRule(val string) (string, error) {
  718. if deviceCgroupRuleRegexp.MatchString(val) {
  719. return val, nil
  720. }
  721. return val, fmt.Errorf("invalid device cgroup format '%s'", val)
  722. }
  723. // validDeviceMode checks if the mode for device is valid or not.
  724. // Valid mode is a composition of r (read), w (write), and m (mknod).
  725. func validDeviceMode(mode string) bool {
  726. var legalDeviceMode = map[rune]bool{
  727. 'r': true,
  728. 'w': true,
  729. 'm': true,
  730. }
  731. if mode == "" {
  732. return false
  733. }
  734. for _, c := range mode {
  735. if !legalDeviceMode[c] {
  736. return false
  737. }
  738. legalDeviceMode[c] = false
  739. }
  740. return true
  741. }
  742. // validateDevice validates a path for devices
  743. // It will make sure 'val' is in the form:
  744. // [host-dir:]container-path[:mode]
  745. // It also validates the device mode.
  746. func validateDevice(val string) (string, error) {
  747. return validatePath(val, validDeviceMode)
  748. }
  749. func validatePath(val string, validator func(string) bool) (string, error) {
  750. var containerPath string
  751. var mode string
  752. if strings.Count(val, ":") > 2 {
  753. return val, fmt.Errorf("bad format for path: %s", val)
  754. }
  755. split := strings.SplitN(val, ":", 3)
  756. if split[0] == "" {
  757. return val, fmt.Errorf("bad format for path: %s", val)
  758. }
  759. switch len(split) {
  760. case 1:
  761. containerPath = split[0]
  762. val = path.Clean(containerPath)
  763. case 2:
  764. if isValid := validator(split[1]); isValid {
  765. containerPath = split[0]
  766. mode = split[1]
  767. val = fmt.Sprintf("%s:%s", path.Clean(containerPath), mode)
  768. } else {
  769. containerPath = split[1]
  770. val = fmt.Sprintf("%s:%s", split[0], path.Clean(containerPath))
  771. }
  772. case 3:
  773. containerPath = split[1]
  774. mode = split[2]
  775. if isValid := validator(split[2]); !isValid {
  776. return val, fmt.Errorf("bad mode specified: %s", mode)
  777. }
  778. val = fmt.Sprintf("%s:%s:%s", split[0], containerPath, mode)
  779. }
  780. if !path.IsAbs(containerPath) {
  781. return val, fmt.Errorf("%s is not an absolute path", containerPath)
  782. }
  783. return val, nil
  784. }
  785. // volumeSplitN splits raw into a maximum of n parts, separated by a separator colon.
  786. // A separator colon is the last `:` character in the regex `[:\\]?[a-zA-Z]:` (note `\\` is `\` escaped).
  787. // In Windows driver letter appears in two situations:
  788. // a. `^[a-zA-Z]:` (A colon followed by `^[a-zA-Z]:` is OK as colon is the separator in volume option)
  789. // b. A string in the format like `\\?\C:\Windows\...` (UNC).
  790. // Therefore, a driver letter can only follow either a `:` or `\\`
  791. // This allows to correctly split strings such as `C:\foo:D:\:rw` or `/tmp/q:/foo`.
  792. func volumeSplitN(raw string, n int) []string {
  793. var array []string
  794. if len(raw) == 0 || raw[0] == ':' {
  795. // invalid
  796. return nil
  797. }
  798. // numberOfParts counts the number of parts separated by a separator colon
  799. numberOfParts := 0
  800. // left represents the left-most cursor in raw, updated at every `:` character considered as a separator.
  801. left := 0
  802. // right represents the right-most cursor in raw incremented with the loop. Note this
  803. // starts at index 1 as index 0 is already handle above as a special case.
  804. for right := 1; right < len(raw); right++ {
  805. // stop parsing if reached maximum number of parts
  806. if n >= 0 && numberOfParts >= n {
  807. break
  808. }
  809. if raw[right] != ':' {
  810. continue
  811. }
  812. potentialDriveLetter := raw[right-1]
  813. if (potentialDriveLetter >= 'A' && potentialDriveLetter <= 'Z') || (potentialDriveLetter >= 'a' && potentialDriveLetter <= 'z') {
  814. if right > 1 {
  815. beforePotentialDriveLetter := raw[right-2]
  816. // Only `:` or `\\` are checked (`/` could fall into the case of `/tmp/q:/foo`)
  817. if beforePotentialDriveLetter != ':' && beforePotentialDriveLetter != '\\' {
  818. // e.g. `C:` is not preceded by any delimiter, therefore it was not a drive letter but a path ending with `C:`.
  819. array = append(array, raw[left:right])
  820. left = right + 1
  821. numberOfParts++
  822. }
  823. // else, `C:` is considered as a drive letter and not as a delimiter, so we continue parsing.
  824. }
  825. // if right == 1, then `C:` is the beginning of the raw string, therefore `:` is again not considered a delimiter and we continue parsing.
  826. } else {
  827. // if `:` is not preceded by a potential drive letter, then consider it as a delimiter.
  828. array = append(array, raw[left:right])
  829. left = right + 1
  830. numberOfParts++
  831. }
  832. }
  833. // need to take care of the last part
  834. if left < len(raw) {
  835. if n >= 0 && numberOfParts >= n {
  836. // if the maximum number of parts is reached, just append the rest to the last part
  837. // left-1 is at the last `:` that needs to be included since not considered a separator.
  838. array[n-1] += raw[left-1:]
  839. } else {
  840. array = append(array, raw[left:])
  841. }
  842. }
  843. return array
  844. }
  845. // validateAttach validates that the specified string is a valid attach option.
  846. func validateAttach(val string) (string, error) {
  847. s := strings.ToLower(val)
  848. for _, str := range []string{"stdin", "stdout", "stderr"} {
  849. if s == str {
  850. return s, nil
  851. }
  852. }
  853. return val, fmt.Errorf("valid streams are STDIN, STDOUT and STDERR")
  854. }