parse.go 36 KB

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