opts.go 34 KB

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