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