github.com/DaoCloud/dao@v0.0.0-20161212064103-c3dbfd13ee36/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  		flAliases:           opts.NewListOpts(nil),
   115  		flAttach:            opts.NewListOpts(ValidateAttach),
   116  		flBlkioWeightDevice: NewWeightdeviceOpt(ValidateWeightDevice),
   117  		flCapAdd:            opts.NewListOpts(nil),
   118  		flCapDrop:           opts.NewListOpts(nil),
   119  		flDNS:               opts.NewListOpts(opts.ValidateIPAddress),
   120  		flDNSOptions:        opts.NewListOpts(nil),
   121  		flDNSSearch:         opts.NewListOpts(opts.ValidateDNSSearch),
   122  		flDeviceReadBps:     NewThrottledeviceOpt(ValidateThrottleBpsDevice),
   123  		flDeviceReadIOps:    NewThrottledeviceOpt(ValidateThrottleIOpsDevice),
   124  		flDeviceWriteBps:    NewThrottledeviceOpt(ValidateThrottleBpsDevice),
   125  		flDeviceWriteIOps:   NewThrottledeviceOpt(ValidateThrottleIOpsDevice),
   126  		flDevices:           opts.NewListOpts(ValidateDevice),
   127  		flEnv:               opts.NewListOpts(ValidateEnv),
   128  		flEnvFile:           opts.NewListOpts(nil),
   129  		flExpose:            opts.NewListOpts(nil),
   130  		flExtraHosts:        opts.NewListOpts(ValidateExtraHost),
   131  		flGroupAdd:          opts.NewListOpts(nil),
   132  		flLabels:            opts.NewListOpts(ValidateEnv),
   133  		flLabelsFile:        opts.NewListOpts(nil),
   134  		flLinkLocalIPs:      opts.NewListOpts(nil),
   135  		flLinks:             opts.NewListOpts(ValidateLink),
   136  		flLoggingOpts:       opts.NewListOpts(nil),
   137  		flPublish:           opts.NewListOpts(nil),
   138  		flSecurityOpt:       opts.NewListOpts(nil),
   139  		flStorageOpt:        opts.NewListOpts(nil),
   140  		flSysctls:           opts.NewMapOpts(nil, opts.ValidateSysctl),
   141  		flTmpfs:             opts.NewListOpts(nil),
   142  		flUlimits:           NewUlimitOpt(nil),
   143  		flVolumes:           opts.NewListOpts(nil),
   144  		flVolumesFrom:       opts.NewListOpts(nil),
   145  	}
   146  
   147  	// General purpose flags
   148  	flags.VarP(&copts.flAttach, "attach", "a", "附加标准输入、标准输出和标准错误")
   149  	flags.Var(&copts.flDevices, "device", "为容器添加一个宿主机设备Add")
   150  	flags.VarP(&copts.flEnv, "env", "e", "设置容器运行时环境变量")
   151  	flags.Var(&copts.flEnvFile, "env-file", "从一个文件中为容器读取环境变量")
   152  	flags.StringVar(&copts.flEntrypoint, "entrypoint", "", "覆盖镜像默认的ENTRYPOINT")
   153  	flags.Var(&copts.flGroupAdd, "group-add", "添加容器加入的额外组")
   154  	flags.StringVarP(&copts.flHostname, "hostname", "h", "", "容器的主机名")
   155  	flags.BoolVarP(&copts.flStdin, "interactive", "i", false, "即使不被附加也保持标准输入打开")
   156  	flags.VarP(&copts.flLabels, "label", "l", "为一个容器上设置元数据")
   157  	flags.Var(&copts.flLabelsFile, "label-file", "从一个标签文件中读取标签信息")
   158  	flags.BoolVar(&copts.flReadonlyRootfs, "read-only", false, "将容器的根文件系统挂载为只读模式")
   159  	flags.StringVar(&copts.flRestartPolicy, "restart", "no", "当一个容器退出时为其采取的重启策略")
   160  	flags.StringVar(&copts.flStopSignal, "stop-signal", signal.DefaultStopSignal, fmt.Sprintf("停止一个容器的信号, 默认是 %v", signal.DefaultStopSignal))
   161  	flags.Var(copts.flSysctls, "sysctl", "系统控制 sysctl 选项")
   162  	flags.BoolVarP(&copts.flTty, "tty", "t", false, "分配一个伪终端")
   163  	flags.Var(copts.flUlimits, "ulimit", "用户限制 Ulimit 选项")
   164  	flags.StringVarP(&copts.flUser, "user", "u", "", "用户名或用户ID (格式: <用户名|用户ID>[:<组|组ID>])")
   165  	flags.StringVarP(&copts.flWorkingDir, "workdir", "w", "", "进程在容器内部的工作目录")
   166  
   167  	// Security
   168  	flags.Var(&copts.flCapAdd, "cap-add", "添加 Linux 特权")
   169  	flags.Var(&copts.flCapDrop, "cap-drop", "丢弃 Linux 特权")
   170  	flags.BoolVar(&copts.flPrivileged, "privileged", false, "授予容器所有的特权")
   171  	flags.Var(&copts.flSecurityOpt, "security-opt", "安全选项")
   172  	flags.StringVar(&copts.flUsernsMode, "userns", "", "使用的用户命名空间")
   173  
   174  	// Network and port publishing flag
   175  	flags.Var(&copts.flExtraHosts, "add-host", "为容器添加一个自定义的主机名到IP的映射(主机名:IP)")
   176  	flags.Var(&copts.flDNS, "dns", "设置自定义的DNS服务器地址")
   177  	flags.Var(&copts.flDNSOptions, "dns-opt", "设置DNS选项")
   178  	flags.Var(&copts.flDNSSearch, "dns-search", "设置自定义的DNS搜索域")
   179  	flags.Var(&copts.flExpose, "expose", "暴露一个或者指定范围的端口")
   180  	flags.StringVar(&copts.flIPv4Address, "ip", "", "容器的IPv4地址(比如172.30.100.104)")
   181  	flags.StringVar(&copts.flIPv6Address, "ip6", "", "容器的IPv6地址(比如2001:db8::33)")
   182  	flags.Var(&copts.flLinks, "link", "添加到另一个容器的连接")
   183  	flags.Var(&copts.flLinkLocalIPs, "link-local-ip", "容器 IPv4/IPv6 本地连接地址")
   184  	flags.StringVar(&copts.flMacAddress, "mac-address", "", "容器MAC地址 (e.g. 92:d0:c6:0a:29:33)")
   185  	flags.VarP(&copts.flPublish, "publish", "p", "将容器内部端口映射到宿主机的指定端口")
   186  	flags.BoolVarP(&copts.flPublishAll, "publish-all", "P", false, "映射容器内部的所有端口到宿主机上的随机端口")
   187  	// We allow for both "--net" and "--network", although the latter is the recommended way.
   188  	flags.StringVar(&copts.flNetMode, "net", "default", "为容器指定网络类型")
   189  	flags.StringVar(&copts.flNetMode, "network", "default", "为容器指定网络类型")
   190  	flags.MarkHidden("net")
   191  	// We allow for both "--net-alias" and "--network-alias", although the latter is the recommended way.
   192  	flags.Var(&copts.flAliases, "net-alias", "为容器添加网络范围内的别名")
   193  	flags.Var(&copts.flAliases, "network-alias", "为容器添加网络范围内的别名")
   194  	flags.MarkHidden("net-alias")
   195  
   196  	// Logging and storage
   197  	flags.StringVar(&copts.flLoggingDriver, "log-driver", "", "为容器指定日志驱动")
   198  	flags.StringVar(&copts.flVolumeDriver, "volume-driver", "", "为容器指定可选的存储卷驱动")
   199  	flags.Var(&copts.flLoggingOpts, "log-opt", "日志驱动选项")
   200  	flags.Var(&copts.flStorageOpt, "storage-opt", "为容器设置存储驱动选项")
   201  	flags.Var(&copts.flTmpfs, "tmpfs", "挂载一个临时文件系统目录")
   202  	flags.Var(&copts.flVolumesFrom, "volumes-from", "从指定的容器挂载存储卷")
   203  	flags.VarP(&copts.flVolumes, "volume", "v", "绑定挂载一个存储卷")
   204  
   205  	// Health-checking
   206  	flags.StringVar(&copts.flHealthCmd, "health-cmd", "", "运行健康的检查的命令")
   207  	flags.DurationVar(&copts.flHealthInterval, "health-interval", 0, "运行健康检查的时间间隔")
   208  	flags.IntVar(&copts.flHealthRetries, "health-retries", 0, "需要汇报不健康的最终错误次数")
   209  	flags.DurationVar(&copts.flHealthTimeout, "health-timeout", 0, "允许一次健康检查运行的最长时间")
   210  	flags.BoolVar(&copts.flNoHealthcheck, "no-healthcheck", false, "禁用容器内任何指定的健康检查")
   211  
   212  	// Resource management
   213  	flags.Uint16Var(&copts.flBlkioWeight, "blkio-weight", 0, "磁盘IO设置(相对值),从10到1000")
   214  	flags.Var(&copts.flBlkioWeightDevice, "blkio-weight-device", "磁盘设备IO设置(相对值),从10到1000")
   215  	flags.StringVar(&copts.flContainerIDFile, "cidfile", "", "写容器ID的文件路径地址")
   216  	flags.StringVar(&copts.flCpusetCpus, "cpuset-cpus", "", "允许容器执行的CPU核指定(0-3,0,1): 0-3代表运行运行在0,1,2,3这4个核上")
   217  	flags.StringVar(&copts.flCpusetMems, "cpuset-mems", "", "允许容器执行的CPU内存所在核指定(0-3,0,1): 0-3代表运行运行在0,1,2,3这4个核上")
   218  	flags.Int64Var(&copts.flCPUPercent, "cpu-percent", 0, "CPU百分比(只支持Windows)")
   219  	flags.Int64Var(&copts.flCPUPeriod, "cpu-period", 0, "限制CPU绝对公平调度算法(CFS)的时间周期")
   220  	flags.Int64Var(&copts.flCPUQuota, "cpu-quota", 0, "限制CPU绝对公平调度算法(CFS)的时间限额")
   221  	flags.Int64VarP(&copts.flCPUShares, "cpu-shares", "c", 0, "CPU计算资源的值(相对值)")
   222  	flags.Var(&copts.flDeviceReadBps, "device-read-bps", "限制一个设备的读速率(bps)")
   223  	flags.Var(&copts.flDeviceReadIOps, "device-read-iops", "限制一个设备的读速率(IOps)")
   224  	flags.Var(&copts.flDeviceWriteBps, "device-write-bps", "限制一个设备的写速率(bps)")
   225  	flags.Var(&copts.flDeviceWriteIOps, "device-write-iops", "限制一个设备的写速率(IOps)")
   226  	flags.StringVar(&copts.flIOMaxBandwidth, "io-maxbandwidth", "", "系统驱动的最大IO带宽限制(只支持Windows)")
   227  	flags.Uint64Var(&copts.flIOMaxIOps, "io-maxiops", 0, "系统驱动的最大IOps限制(只支持Windows)")
   228  	flags.StringVar(&copts.flKernelMemory, "kernel-memory", "", "内核内存限制")
   229  	flags.StringVarP(&copts.flMemoryString, "memory", "m", "", "内存限制")
   230  	flags.StringVar(&copts.flMemoryReservation, "memory-reservation", "", "内存软限制")
   231  	flags.StringVar(&copts.flMemorySwap, "memory-swap", "", "交换内存限制 等于 实际内存 + 交换区内存: '-1' 代表启用不受限的交换区内存")
   232  	flags.Int64Var(&copts.flSwappiness, "memory-swappiness", -1, "设置容器内存swappiness参数 (0 到 100)")
   233  	flags.BoolVar(&copts.flOomKillDisable, "oom-kill-disable", false, "禁用OOM Killer")
   234  	flags.IntVar(&copts.flOomScoreAdj, "oom-score-adj", 0, "设置OOM偏好参数 (-1000 至 1000)")
   235  	flags.Int64Var(&copts.flPidsLimit, "pids-limit", 0, "设置容器进程上限(设置-1代表没有限制)")
   236  
   237  	// Low-level execution (cgroups, namespaces, ...)
   238  	flags.StringVar(&copts.flCgroupParent, "cgroup-parent", "", "为容器设置的可选cgroup父系统")
   239  	flags.StringVar(&copts.flIpcMode, "ipc", "", "使用的IPC命名空间")
   240  	flags.StringVar(&copts.flIsolation, "isolation", "", "容器的隔离技术")
   241  	flags.StringVar(&copts.flPidMode, "pid", "", "使用的PID命名空间")
   242  	flags.StringVar(&copts.flShmSize, "shm-size", "", "内存共享文件的/dev/shm的大小, 默认值为64MB")
   243  	flags.StringVar(&copts.flUTSMode, "uts", "", "使用的UTS命名空间")
   244  	flags.StringVar(&copts.flRuntime, "runtime", "", "为容器选择的容器运行时驱动类型")
   245  	return copts
   246  }
   247  
   248  // Parse parses the args for the specified command and generates a Config,
   249  // a HostConfig and returns them with the specified command.
   250  // If the specified args are not valid, it will return an error.
   251  func Parse(flags *pflag.FlagSet, copts *ContainerOptions) (*container.Config, *container.HostConfig, *networktypes.NetworkingConfig, error) {
   252  	var (
   253  		attachStdin  = copts.flAttach.Get("stdin")
   254  		attachStdout = copts.flAttach.Get("stdout")
   255  		attachStderr = copts.flAttach.Get("stderr")
   256  	)
   257  
   258  	// Validate the input mac address
   259  	if copts.flMacAddress != "" {
   260  		if _, err := ValidateMACAddress(copts.flMacAddress); err != nil {
   261  			return nil, nil, nil, fmt.Errorf("%s is not a valid mac address", copts.flMacAddress)
   262  		}
   263  	}
   264  	if copts.flStdin {
   265  		attachStdin = true
   266  	}
   267  	// If -a is not set, attach to stdout and stderr
   268  	if copts.flAttach.Len() == 0 {
   269  		attachStdout = true
   270  		attachStderr = true
   271  	}
   272  
   273  	var err error
   274  
   275  	var flMemory int64
   276  	if copts.flMemoryString != "" {
   277  		flMemory, err = units.RAMInBytes(copts.flMemoryString)
   278  		if err != nil {
   279  			return nil, nil, nil, err
   280  		}
   281  	}
   282  
   283  	var MemoryReservation int64
   284  	if copts.flMemoryReservation != "" {
   285  		MemoryReservation, err = units.RAMInBytes(copts.flMemoryReservation)
   286  		if err != nil {
   287  			return nil, nil, nil, err
   288  		}
   289  	}
   290  
   291  	var memorySwap int64
   292  	if copts.flMemorySwap != "" {
   293  		if copts.flMemorySwap == "-1" {
   294  			memorySwap = -1
   295  		} else {
   296  			memorySwap, err = units.RAMInBytes(copts.flMemorySwap)
   297  			if err != nil {
   298  				return nil, nil, nil, err
   299  			}
   300  		}
   301  	}
   302  
   303  	var KernelMemory int64
   304  	if copts.flKernelMemory != "" {
   305  		KernelMemory, err = units.RAMInBytes(copts.flKernelMemory)
   306  		if err != nil {
   307  			return nil, nil, nil, err
   308  		}
   309  	}
   310  
   311  	swappiness := copts.flSwappiness
   312  	if swappiness != -1 && (swappiness < 0 || swappiness > 100) {
   313  		return nil, nil, nil, fmt.Errorf("invalid value: %d. Valid memory swappiness range is 0-100", swappiness)
   314  	}
   315  
   316  	var shmSize int64
   317  	if copts.flShmSize != "" {
   318  		shmSize, err = units.RAMInBytes(copts.flShmSize)
   319  		if err != nil {
   320  			return nil, nil, nil, err
   321  		}
   322  	}
   323  
   324  	// TODO FIXME units.RAMInBytes should have a uint64 version
   325  	var maxIOBandwidth int64
   326  	if copts.flIOMaxBandwidth != "" {
   327  		maxIOBandwidth, err = units.RAMInBytes(copts.flIOMaxBandwidth)
   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.flIOMaxBandwidth)
   333  		}
   334  	}
   335  
   336  	var binds []string
   337  	// add any bind targets to the list of container volumes
   338  	for bind := range copts.flVolumes.GetMap() {
   339  		if arr := volumeSplitN(bind, 2); len(arr) > 1 {
   340  			// after creating the bind mount we want to delete it from the copts.flVolumes values because
   341  			// we do not want bind mounts being committed to image configs
   342  			binds = append(binds, bind)
   343  			copts.flVolumes.Delete(bind)
   344  		}
   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.flTmpfs.GetAll() {
   350  		if arr := strings.SplitN(t, ":", 2); len(arr) > 1 {
   351  			if _, _, err := mount.ParseTmpfsOptions(arr[1]); err != nil {
   352  				return nil, nil, nil, err
   353  			}
   354  			tmpfs[arr[0]] = arr[1]
   355  		} else {
   356  			tmpfs[arr[0]] = ""
   357  		}
   358  	}
   359  
   360  	var (
   361  		runCmd     strslice.StrSlice
   362  		entrypoint strslice.StrSlice
   363  	)
   364  	if len(copts.Args) > 0 {
   365  		runCmd = strslice.StrSlice(copts.Args)
   366  	}
   367  	if copts.flEntrypoint != "" {
   368  		entrypoint = strslice.StrSlice{copts.flEntrypoint}
   369  	}
   370  
   371  	ports, portBindings, err := nat.ParsePortSpecs(copts.flPublish.GetAll())
   372  	if err != nil {
   373  		return nil, nil, nil, err
   374  	}
   375  
   376  	// Merge in exposed ports to the map of published ports
   377  	for _, e := range copts.flExpose.GetAll() {
   378  		if strings.Contains(e, ":") {
   379  			return nil, nil, nil, fmt.Errorf("invalid port format for --expose: %s", e)
   380  		}
   381  		//support two formats for expose, original format <portnum>/[<proto>] or <startport-endport>/[<proto>]
   382  		proto, port := nat.SplitProtoPort(e)
   383  		//parse the start and end port and create a sequence of ports to expose
   384  		//if expose a port, the start and end port are the same
   385  		start, end, err := nat.ParsePortRange(port)
   386  		if err != nil {
   387  			return nil, nil, nil, fmt.Errorf("invalid range format for --expose: %s, error: %s", e, err)
   388  		}
   389  		for i := start; i <= end; i++ {
   390  			p, err := nat.NewPort(proto, strconv.FormatUint(i, 10))
   391  			if err != nil {
   392  				return nil, nil, nil, err
   393  			}
   394  			if _, exists := ports[p]; !exists {
   395  				ports[p] = struct{}{}
   396  			}
   397  		}
   398  	}
   399  
   400  	// parse device mappings
   401  	deviceMappings := []container.DeviceMapping{}
   402  	for _, device := range copts.flDevices.GetAll() {
   403  		deviceMapping, err := ParseDevice(device)
   404  		if err != nil {
   405  			return nil, nil, nil, err
   406  		}
   407  		deviceMappings = append(deviceMappings, deviceMapping)
   408  	}
   409  
   410  	// collect all the environment variables for the container
   411  	envVariables, err := readKVStrings(copts.flEnvFile.GetAll(), copts.flEnv.GetAll())
   412  	if err != nil {
   413  		return nil, nil, nil, err
   414  	}
   415  
   416  	// collect all the labels for the container
   417  	labels, err := readKVStrings(copts.flLabelsFile.GetAll(), copts.flLabels.GetAll())
   418  	if err != nil {
   419  		return nil, nil, nil, err
   420  	}
   421  
   422  	ipcMode := container.IpcMode(copts.flIpcMode)
   423  	if !ipcMode.Valid() {
   424  		return nil, nil, nil, fmt.Errorf("--ipc: invalid IPC mode")
   425  	}
   426  
   427  	pidMode := container.PidMode(copts.flPidMode)
   428  	if !pidMode.Valid() {
   429  		return nil, nil, nil, fmt.Errorf("--pid: invalid PID mode")
   430  	}
   431  
   432  	utsMode := container.UTSMode(copts.flUTSMode)
   433  	if !utsMode.Valid() {
   434  		return nil, nil, nil, fmt.Errorf("--uts: invalid UTS mode")
   435  	}
   436  
   437  	usernsMode := container.UsernsMode(copts.flUsernsMode)
   438  	if !usernsMode.Valid() {
   439  		return nil, nil, nil, fmt.Errorf("--userns: invalid USER mode")
   440  	}
   441  
   442  	restartPolicy, err := ParseRestartPolicy(copts.flRestartPolicy)
   443  	if err != nil {
   444  		return nil, nil, nil, err
   445  	}
   446  
   447  	loggingOpts, err := parseLoggingOpts(copts.flLoggingDriver, copts.flLoggingOpts.GetAll())
   448  	if err != nil {
   449  		return nil, nil, nil, err
   450  	}
   451  
   452  	securityOpts, err := parseSecurityOpts(copts.flSecurityOpt.GetAll())
   453  	if err != nil {
   454  		return nil, nil, nil, err
   455  	}
   456  
   457  	storageOpts, err := parseStorageOpts(copts.flStorageOpt.GetAll())
   458  	if err != nil {
   459  		return nil, nil, nil, err
   460  	}
   461  
   462  	// Healthcheck
   463  	var healthConfig *container.HealthConfig
   464  	haveHealthSettings := copts.flHealthCmd != "" ||
   465  		copts.flHealthInterval != 0 ||
   466  		copts.flHealthTimeout != 0 ||
   467  		copts.flHealthRetries != 0
   468  	if copts.flNoHealthcheck {
   469  		if haveHealthSettings {
   470  			return nil, nil, nil, fmt.Errorf("--no-healthcheck conflicts with --health-* options")
   471  		}
   472  		test := strslice.StrSlice{"NONE"}
   473  		healthConfig = &container.HealthConfig{Test: test}
   474  	} else if haveHealthSettings {
   475  		var probe strslice.StrSlice
   476  		if copts.flHealthCmd != "" {
   477  			args := []string{"CMD-SHELL", copts.flHealthCmd}
   478  			probe = strslice.StrSlice(args)
   479  		}
   480  		if copts.flHealthInterval < 0 {
   481  			return nil, nil, nil, fmt.Errorf("--health-interval cannot be negative")
   482  		}
   483  		if copts.flHealthTimeout < 0 {
   484  			return nil, nil, nil, fmt.Errorf("--health-timeout cannot be negative")
   485  		}
   486  
   487  		healthConfig = &container.HealthConfig{
   488  			Test:     probe,
   489  			Interval: copts.flHealthInterval,
   490  			Timeout:  copts.flHealthTimeout,
   491  			Retries:  copts.flHealthRetries,
   492  		}
   493  	}
   494  
   495  	resources := container.Resources{
   496  		CgroupParent:         copts.flCgroupParent,
   497  		Memory:               flMemory,
   498  		MemoryReservation:    MemoryReservation,
   499  		MemorySwap:           memorySwap,
   500  		MemorySwappiness:     &copts.flSwappiness,
   501  		KernelMemory:         KernelMemory,
   502  		OomKillDisable:       &copts.flOomKillDisable,
   503  		CPUPercent:           copts.flCPUPercent,
   504  		CPUShares:            copts.flCPUShares,
   505  		CPUPeriod:            copts.flCPUPeriod,
   506  		CpusetCpus:           copts.flCpusetCpus,
   507  		CpusetMems:           copts.flCpusetMems,
   508  		CPUQuota:             copts.flCPUQuota,
   509  		PidsLimit:            copts.flPidsLimit,
   510  		BlkioWeight:          copts.flBlkioWeight,
   511  		BlkioWeightDevice:    copts.flBlkioWeightDevice.GetList(),
   512  		BlkioDeviceReadBps:   copts.flDeviceReadBps.GetList(),
   513  		BlkioDeviceWriteBps:  copts.flDeviceWriteBps.GetList(),
   514  		BlkioDeviceReadIOps:  copts.flDeviceReadIOps.GetList(),
   515  		BlkioDeviceWriteIOps: copts.flDeviceWriteIOps.GetList(),
   516  		IOMaximumIOps:        copts.flIOMaxIOps,
   517  		IOMaximumBandwidth:   uint64(maxIOBandwidth),
   518  		Ulimits:              copts.flUlimits.GetList(),
   519  		Devices:              deviceMappings,
   520  	}
   521  
   522  	config := &container.Config{
   523  		Hostname:     copts.flHostname,
   524  		ExposedPorts: ports,
   525  		User:         copts.flUser,
   526  		Tty:          copts.flTty,
   527  		// TODO: deprecated, it comes from -n, --networking
   528  		// it's still needed internally to set the network to disabled
   529  		// if e.g. bridge is none in daemon opts, and in inspect
   530  		NetworkDisabled: false,
   531  		OpenStdin:       copts.flStdin,
   532  		AttachStdin:     attachStdin,
   533  		AttachStdout:    attachStdout,
   534  		AttachStderr:    attachStderr,
   535  		Env:             envVariables,
   536  		Cmd:             runCmd,
   537  		Image:           copts.Image,
   538  		Volumes:         copts.flVolumes.GetMap(),
   539  		MacAddress:      copts.flMacAddress,
   540  		Entrypoint:      entrypoint,
   541  		WorkingDir:      copts.flWorkingDir,
   542  		Labels:          ConvertKVStringsToMap(labels),
   543  		Healthcheck:     healthConfig,
   544  	}
   545  	if flags.Changed("stop-signal") {
   546  		config.StopSignal = copts.flStopSignal
   547  	}
   548  
   549  	hostConfig := &container.HostConfig{
   550  		Binds:           binds,
   551  		ContainerIDFile: copts.flContainerIDFile,
   552  		OomScoreAdj:     copts.flOomScoreAdj,
   553  		Privileged:      copts.flPrivileged,
   554  		PortBindings:    portBindings,
   555  		Links:           copts.flLinks.GetAll(),
   556  		PublishAllPorts: copts.flPublishAll,
   557  		// Make sure the dns fields are never nil.
   558  		// New containers don't ever have those fields nil,
   559  		// but pre created containers can still have those nil values.
   560  		// See https://github.com/docker/docker/pull/17779
   561  		// for a more detailed explanation on why we don't want that.
   562  		DNS:            copts.flDNS.GetAllOrEmpty(),
   563  		DNSSearch:      copts.flDNSSearch.GetAllOrEmpty(),
   564  		DNSOptions:     copts.flDNSOptions.GetAllOrEmpty(),
   565  		ExtraHosts:     copts.flExtraHosts.GetAll(),
   566  		VolumesFrom:    copts.flVolumesFrom.GetAll(),
   567  		NetworkMode:    container.NetworkMode(copts.flNetMode),
   568  		IpcMode:        ipcMode,
   569  		PidMode:        pidMode,
   570  		UTSMode:        utsMode,
   571  		UsernsMode:     usernsMode,
   572  		CapAdd:         strslice.StrSlice(copts.flCapAdd.GetAll()),
   573  		CapDrop:        strslice.StrSlice(copts.flCapDrop.GetAll()),
   574  		GroupAdd:       copts.flGroupAdd.GetAll(),
   575  		RestartPolicy:  restartPolicy,
   576  		SecurityOpt:    securityOpts,
   577  		StorageOpt:     storageOpts,
   578  		ReadonlyRootfs: copts.flReadonlyRootfs,
   579  		LogConfig:      container.LogConfig{Type: copts.flLoggingDriver, Config: loggingOpts},
   580  		VolumeDriver:   copts.flVolumeDriver,
   581  		Isolation:      container.Isolation(copts.flIsolation),
   582  		ShmSize:        shmSize,
   583  		Resources:      resources,
   584  		Tmpfs:          tmpfs,
   585  		Sysctls:        copts.flSysctls.GetAll(),
   586  		Runtime:        copts.flRuntime,
   587  	}
   588  
   589  	// When allocating stdin in attached mode, close stdin at client disconnect
   590  	if config.OpenStdin && config.AttachStdin {
   591  		config.StdinOnce = true
   592  	}
   593  
   594  	networkingConfig := &networktypes.NetworkingConfig{
   595  		EndpointsConfig: make(map[string]*networktypes.EndpointSettings),
   596  	}
   597  
   598  	if copts.flIPv4Address != "" || copts.flIPv6Address != "" || copts.flLinkLocalIPs.Len() > 0 {
   599  		epConfig := &networktypes.EndpointSettings{}
   600  		networkingConfig.EndpointsConfig[string(hostConfig.NetworkMode)] = epConfig
   601  
   602  		epConfig.IPAMConfig = &networktypes.EndpointIPAMConfig{
   603  			IPv4Address: copts.flIPv4Address,
   604  			IPv6Address: copts.flIPv6Address,
   605  		}
   606  
   607  		if copts.flLinkLocalIPs.Len() > 0 {
   608  			epConfig.IPAMConfig.LinkLocalIPs = make([]string, copts.flLinkLocalIPs.Len())
   609  			copy(epConfig.IPAMConfig.LinkLocalIPs, copts.flLinkLocalIPs.GetAll())
   610  		}
   611  	}
   612  
   613  	if hostConfig.NetworkMode.IsUserDefined() && len(hostConfig.Links) > 0 {
   614  		epConfig := networkingConfig.EndpointsConfig[string(hostConfig.NetworkMode)]
   615  		if epConfig == nil {
   616  			epConfig = &networktypes.EndpointSettings{}
   617  		}
   618  		epConfig.Links = make([]string, len(hostConfig.Links))
   619  		copy(epConfig.Links, hostConfig.Links)
   620  		networkingConfig.EndpointsConfig[string(hostConfig.NetworkMode)] = epConfig
   621  	}
   622  
   623  	if copts.flAliases.Len() > 0 {
   624  		epConfig := networkingConfig.EndpointsConfig[string(hostConfig.NetworkMode)]
   625  		if epConfig == nil {
   626  			epConfig = &networktypes.EndpointSettings{}
   627  		}
   628  		epConfig.Aliases = make([]string, copts.flAliases.Len())
   629  		copy(epConfig.Aliases, copts.flAliases.GetAll())
   630  		networkingConfig.EndpointsConfig[string(hostConfig.NetworkMode)] = epConfig
   631  	}
   632  
   633  	return config, hostConfig, networkingConfig, nil
   634  }
   635  
   636  // reads a file of line terminated key=value pairs, and overrides any keys
   637  // present in the file with additional pairs specified in the override parameter
   638  func readKVStrings(files []string, override []string) ([]string, error) {
   639  	envVariables := []string{}
   640  	for _, ef := range files {
   641  		parsedVars, err := ParseEnvFile(ef)
   642  		if err != nil {
   643  			return nil, err
   644  		}
   645  		envVariables = append(envVariables, parsedVars...)
   646  	}
   647  	// parse the '-e' and '--env' after, to allow override
   648  	envVariables = append(envVariables, override...)
   649  
   650  	return envVariables, nil
   651  }
   652  
   653  // ConvertKVStringsToMap converts ["key=value"] to {"key":"value"}
   654  func ConvertKVStringsToMap(values []string) map[string]string {
   655  	result := make(map[string]string, len(values))
   656  	for _, value := range values {
   657  		kv := strings.SplitN(value, "=", 2)
   658  		if len(kv) == 1 {
   659  			result[kv[0]] = ""
   660  		} else {
   661  			result[kv[0]] = kv[1]
   662  		}
   663  	}
   664  
   665  	return result
   666  }
   667  
   668  func parseLoggingOpts(loggingDriver string, loggingOpts []string) (map[string]string, error) {
   669  	loggingOptsMap := ConvertKVStringsToMap(loggingOpts)
   670  	if loggingDriver == "none" && len(loggingOpts) > 0 {
   671  		return map[string]string{}, fmt.Errorf("invalid logging opts for driver %s", loggingDriver)
   672  	}
   673  	return loggingOptsMap, nil
   674  }
   675  
   676  // takes a local seccomp daemon, reads the file contents for sending to the daemon
   677  func parseSecurityOpts(securityOpts []string) ([]string, error) {
   678  	for key, opt := range securityOpts {
   679  		con := strings.SplitN(opt, "=", 2)
   680  		if len(con) == 1 && con[0] != "no-new-privileges" {
   681  			if strings.Index(opt, ":") != -1 {
   682  				con = strings.SplitN(opt, ":", 2)
   683  			} else {
   684  				return securityOpts, fmt.Errorf("Invalid --security-opt: %q", opt)
   685  			}
   686  		}
   687  		if con[0] == "seccomp" && con[1] != "unconfined" {
   688  			f, err := ioutil.ReadFile(con[1])
   689  			if err != nil {
   690  				return securityOpts, fmt.Errorf("opening seccomp profile (%s) failed: %v", con[1], err)
   691  			}
   692  			b := bytes.NewBuffer(nil)
   693  			if err := json.Compact(b, f); err != nil {
   694  				return securityOpts, fmt.Errorf("compacting json for seccomp profile (%s) failed: %v", con[1], err)
   695  			}
   696  			securityOpts[key] = fmt.Sprintf("seccomp=%s", b.Bytes())
   697  		}
   698  	}
   699  
   700  	return securityOpts, nil
   701  }
   702  
   703  // parses storage options per container into a map
   704  func parseStorageOpts(storageOpts []string) (map[string]string, error) {
   705  	m := make(map[string]string)
   706  	for _, option := range storageOpts {
   707  		if strings.Contains(option, "=") {
   708  			opt := strings.SplitN(option, "=", 2)
   709  			m[opt[0]] = opt[1]
   710  		} else {
   711  			return nil, fmt.Errorf("Invalid storage option.")
   712  		}
   713  	}
   714  	return m, nil
   715  }
   716  
   717  // ParseRestartPolicy returns the parsed policy or an error indicating what is incorrect
   718  func ParseRestartPolicy(policy string) (container.RestartPolicy, error) {
   719  	p := container.RestartPolicy{}
   720  
   721  	if policy == "" {
   722  		return p, nil
   723  	}
   724  
   725  	var (
   726  		parts = strings.Split(policy, ":")
   727  		name  = parts[0]
   728  	)
   729  
   730  	p.Name = name
   731  	switch name {
   732  	case "always", "unless-stopped":
   733  		if len(parts) > 1 {
   734  			return p, fmt.Errorf("maximum restart count not valid with restart policy of \"%s\"", name)
   735  		}
   736  	case "no":
   737  		// do nothing
   738  	case "on-failure":
   739  		if len(parts) > 2 {
   740  			return p, fmt.Errorf("restart count format is not valid, usage: 'on-failure:N' or 'on-failure'")
   741  		}
   742  		if len(parts) == 2 {
   743  			count, err := strconv.Atoi(parts[1])
   744  			if err != nil {
   745  				return p, err
   746  			}
   747  
   748  			p.MaximumRetryCount = count
   749  		}
   750  	default:
   751  		return p, fmt.Errorf("invalid restart policy %s", name)
   752  	}
   753  
   754  	return p, nil
   755  }
   756  
   757  // ParseDevice parses a device mapping string to a container.DeviceMapping struct
   758  func ParseDevice(device string) (container.DeviceMapping, error) {
   759  	src := ""
   760  	dst := ""
   761  	permissions := "rwm"
   762  	arr := strings.Split(device, ":")
   763  	switch len(arr) {
   764  	case 3:
   765  		permissions = arr[2]
   766  		fallthrough
   767  	case 2:
   768  		if ValidDeviceMode(arr[1]) {
   769  			permissions = arr[1]
   770  		} else {
   771  			dst = arr[1]
   772  		}
   773  		fallthrough
   774  	case 1:
   775  		src = arr[0]
   776  	default:
   777  		return container.DeviceMapping{}, fmt.Errorf("invalid device specification: %s", device)
   778  	}
   779  
   780  	if dst == "" {
   781  		dst = src
   782  	}
   783  
   784  	deviceMapping := container.DeviceMapping{
   785  		PathOnHost:        src,
   786  		PathInContainer:   dst,
   787  		CgroupPermissions: permissions,
   788  	}
   789  	return deviceMapping, nil
   790  }
   791  
   792  // ParseLink parses and validates the specified string as a link format (name:alias)
   793  func ParseLink(val string) (string, string, error) {
   794  	if val == "" {
   795  		return "", "", fmt.Errorf("empty string specified for links")
   796  	}
   797  	arr := strings.Split(val, ":")
   798  	if len(arr) > 2 {
   799  		return "", "", fmt.Errorf("bad format for links: %s", val)
   800  	}
   801  	if len(arr) == 1 {
   802  		return val, val, nil
   803  	}
   804  	// This is kept because we can actually get a HostConfig with links
   805  	// from an already created container and the format is not `foo:bar`
   806  	// but `/foo:/c1/bar`
   807  	if strings.HasPrefix(arr[0], "/") {
   808  		_, alias := path.Split(arr[1])
   809  		return arr[0][1:], alias, nil
   810  	}
   811  	return arr[0], arr[1], nil
   812  }
   813  
   814  // ValidateLink validates that the specified string has a valid link format (containerName:alias).
   815  func ValidateLink(val string) (string, error) {
   816  	if _, _, err := ParseLink(val); err != nil {
   817  		return val, err
   818  	}
   819  	return val, nil
   820  }
   821  
   822  // ValidDeviceMode checks if the mode for device is valid or not.
   823  // Valid mode is a composition of r (read), w (write), and m (mknod).
   824  func ValidDeviceMode(mode string) bool {
   825  	var legalDeviceMode = map[rune]bool{
   826  		'r': true,
   827  		'w': true,
   828  		'm': true,
   829  	}
   830  	if mode == "" {
   831  		return false
   832  	}
   833  	for _, c := range mode {
   834  		if !legalDeviceMode[c] {
   835  			return false
   836  		}
   837  		legalDeviceMode[c] = false
   838  	}
   839  	return true
   840  }
   841  
   842  // ValidateDevice validates a path for devices
   843  // It will make sure 'val' is in the form:
   844  //    [host-dir:]container-path[:mode]
   845  // It also validates the device mode.
   846  func ValidateDevice(val string) (string, error) {
   847  	return validatePath(val, ValidDeviceMode)
   848  }
   849  
   850  func validatePath(val string, validator func(string) bool) (string, error) {
   851  	var containerPath string
   852  	var mode string
   853  
   854  	if strings.Count(val, ":") > 2 {
   855  		return val, fmt.Errorf("bad format for path: %s", val)
   856  	}
   857  
   858  	split := strings.SplitN(val, ":", 3)
   859  	if split[0] == "" {
   860  		return val, fmt.Errorf("bad format for path: %s", val)
   861  	}
   862  	switch len(split) {
   863  	case 1:
   864  		containerPath = split[0]
   865  		val = path.Clean(containerPath)
   866  	case 2:
   867  		if isValid := validator(split[1]); isValid {
   868  			containerPath = split[0]
   869  			mode = split[1]
   870  			val = fmt.Sprintf("%s:%s", path.Clean(containerPath), mode)
   871  		} else {
   872  			containerPath = split[1]
   873  			val = fmt.Sprintf("%s:%s", split[0], path.Clean(containerPath))
   874  		}
   875  	case 3:
   876  		containerPath = split[1]
   877  		mode = split[2]
   878  		if isValid := validator(split[2]); !isValid {
   879  			return val, fmt.Errorf("bad mode specified: %s", mode)
   880  		}
   881  		val = fmt.Sprintf("%s:%s:%s", split[0], containerPath, mode)
   882  	}
   883  
   884  	if !path.IsAbs(containerPath) {
   885  		return val, fmt.Errorf("%s is not an absolute path", containerPath)
   886  	}
   887  	return val, nil
   888  }
   889  
   890  // volumeSplitN splits raw into a maximum of n parts, separated by a separator colon.
   891  // A separator colon is the last `:` character in the regex `[:\\]?[a-zA-Z]:` (note `\\` is `\` escaped).
   892  // In Windows driver letter appears in two situations:
   893  // a. `^[a-zA-Z]:` (A colon followed  by `^[a-zA-Z]:` is OK as colon is the separator in volume option)
   894  // b. A string in the format like `\\?\C:\Windows\...` (UNC).
   895  // Therefore, a driver letter can only follow either a `:` or `\\`
   896  // This allows to correctly split strings such as `C:\foo:D:\:rw` or `/tmp/q:/foo`.
   897  func volumeSplitN(raw string, n int) []string {
   898  	var array []string
   899  	if len(raw) == 0 || raw[0] == ':' {
   900  		// invalid
   901  		return nil
   902  	}
   903  	// numberOfParts counts the number of parts separated by a separator colon
   904  	numberOfParts := 0
   905  	// left represents the left-most cursor in raw, updated at every `:` character considered as a separator.
   906  	left := 0
   907  	// right represents the right-most cursor in raw incremented with the loop. Note this
   908  	// starts at index 1 as index 0 is already handle above as a special case.
   909  	for right := 1; right < len(raw); right++ {
   910  		// stop parsing if reached maximum number of parts
   911  		if n >= 0 && numberOfParts >= n {
   912  			break
   913  		}
   914  		if raw[right] != ':' {
   915  			continue
   916  		}
   917  		potentialDriveLetter := raw[right-1]
   918  		if (potentialDriveLetter >= 'A' && potentialDriveLetter <= 'Z') || (potentialDriveLetter >= 'a' && potentialDriveLetter <= 'z') {
   919  			if right > 1 {
   920  				beforePotentialDriveLetter := raw[right-2]
   921  				// Only `:` or `\\` are checked (`/` could fall into the case of `/tmp/q:/foo`)
   922  				if beforePotentialDriveLetter != ':' && beforePotentialDriveLetter != '\\' {
   923  					// e.g. `C:` is not preceded by any delimiter, therefore it was not a drive letter but a path ending with `C:`.
   924  					array = append(array, raw[left:right])
   925  					left = right + 1
   926  					numberOfParts++
   927  				}
   928  				// else, `C:` is considered as a drive letter and not as a delimiter, so we continue parsing.
   929  			}
   930  			// if right == 1, then `C:` is the beginning of the raw string, therefore `:` is again not considered a delimiter and we continue parsing.
   931  		} else {
   932  			// if `:` is not preceded by a potential drive letter, then consider it as a delimiter.
   933  			array = append(array, raw[left:right])
   934  			left = right + 1
   935  			numberOfParts++
   936  		}
   937  	}
   938  	// need to take care of the last part
   939  	if left < len(raw) {
   940  		if n >= 0 && numberOfParts >= n {
   941  			// if the maximum number of parts is reached, just append the rest to the last part
   942  			// left-1 is at the last `:` that needs to be included since not considered a separator.
   943  			array[n-1] += raw[left-1:]
   944  		} else {
   945  			array = append(array, raw[left:])
   946  		}
   947  	}
   948  	return array
   949  }