diff options
author | OpenShift Merge Robot <openshift-merge-robot@users.noreply.github.com> | 2021-08-27 09:24:26 -0400 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-08-27 09:24:26 -0400 |
commit | 266a3892f25d8cee508f421e718ba6f135ff7123 (patch) | |
tree | 98aceda072f00ac22d3ee438bfc42d2f4aa1ff93 /cmd/podman/common | |
parent | 69cdf5d8035672e8bc7141cac0207e4b0f0e80cd (diff) | |
parent | d28e85741fedb89be48a03d4f05687e970eb71b9 (diff) | |
download | podman-266a3892f25d8cee508f421e718ba6f135ff7123.tar.gz podman-266a3892f25d8cee508f421e718ba6f135ff7123.tar.bz2 podman-266a3892f25d8cee508f421e718ba6f135ff7123.zip |
Merge pull request #11102 from cdoern/infraEnhance
InfraContainer Rework
Diffstat (limited to 'cmd/podman/common')
-rw-r--r-- | cmd/podman/common/create.go | 1416 | ||||
-rw-r--r-- | cmd/podman/common/create_opts.go | 120 | ||||
-rw-r--r-- | cmd/podman/common/createparse.go | 29 | ||||
-rw-r--r-- | cmd/podman/common/netflags.go | 102 | ||||
-rw-r--r-- | cmd/podman/common/ports.go | 22 | ||||
-rw-r--r-- | cmd/podman/common/specgen.go | 972 | ||||
-rw-r--r-- | cmd/podman/common/util.go | 274 | ||||
-rw-r--r-- | cmd/podman/common/volumes.go | 630 |
8 files changed, 771 insertions, 2794 deletions
diff --git a/cmd/podman/common/create.go b/cmd/podman/common/create.go index 401cf2e09..325c1dc69 100644 --- a/cmd/podman/common/create.go +++ b/cmd/podman/common/create.go @@ -7,6 +7,7 @@ import ( "github.com/containers/common/pkg/completion" "github.com/containers/podman/v3/cmd/podman/registry" "github.com/containers/podman/v3/libpod/define" + "github.com/containers/podman/v3/pkg/domain/entities" "github.com/spf13/cobra" ) @@ -14,663 +15,714 @@ const sizeWithUnitFormat = "(format: `<number>[<unit>]`, where unit = b (bytes), var containerConfig = registry.PodmanConfig() -func DefineCreateFlags(cmd *cobra.Command, cf *ContainerCLIOpts) { +func DefineCreateFlags(cmd *cobra.Command, cf *entities.ContainerCreateOptions, isInfra bool) { createFlags := cmd.Flags() - annotationFlagName := "annotation" - createFlags.StringSliceVar( - &cf.Annotation, - annotationFlagName, []string{}, - "Add annotations to container (key:value)", - ) - _ = cmd.RegisterFlagCompletionFunc(annotationFlagName, completion.AutocompleteNone) + if !isInfra { + annotationFlagName := "annotation" + createFlags.StringSliceVar( + &cf.Annotation, + annotationFlagName, []string{}, + "Add annotations to container (key:value)", + ) + _ = cmd.RegisterFlagCompletionFunc(annotationFlagName, completion.AutocompleteNone) - attachFlagName := "attach" - createFlags.StringSliceVarP( - &cf.Attach, - attachFlagName, "a", []string{}, - "Attach to STDIN, STDOUT or STDERR", - ) - _ = cmd.RegisterFlagCompletionFunc(attachFlagName, AutocompleteCreateAttach) + attachFlagName := "attach" + createFlags.StringSliceVarP( + &cf.Attach, + attachFlagName, "a", []string{}, + "Attach to STDIN, STDOUT or STDERR", + ) + _ = cmd.RegisterFlagCompletionFunc(attachFlagName, AutocompleteCreateAttach) - authfileFlagName := "authfile" - createFlags.StringVar( - &cf.Authfile, - authfileFlagName, auth.GetDefaultAuthFile(), - "Path of the authentication file. Use REGISTRY_AUTH_FILE environment variable to override", - ) - _ = cmd.RegisterFlagCompletionFunc(authfileFlagName, completion.AutocompleteDefault) + authfileFlagName := "authfile" + createFlags.StringVar( + &cf.Authfile, + authfileFlagName, auth.GetDefaultAuthFile(), + "Path of the authentication file. Use REGISTRY_AUTH_FILE environment variable to override", + ) + _ = cmd.RegisterFlagCompletionFunc(authfileFlagName, completion.AutocompleteDefault) - blkioWeightFlagName := "blkio-weight" - createFlags.StringVar( - &cf.BlkIOWeight, - blkioWeightFlagName, "", - "Block IO weight (relative weight) accepts a weight value between 10 and 1000.", - ) - _ = cmd.RegisterFlagCompletionFunc(blkioWeightFlagName, completion.AutocompleteNone) + blkioWeightFlagName := "blkio-weight" + createFlags.StringVar( + &cf.BlkIOWeight, + blkioWeightFlagName, "", + "Block IO weight (relative weight) accepts a weight value between 10 and 1000.", + ) + _ = cmd.RegisterFlagCompletionFunc(blkioWeightFlagName, completion.AutocompleteNone) - blkioWeightDeviceFlagName := "blkio-weight-device" - createFlags.StringSliceVar( - &cf.BlkIOWeightDevice, - blkioWeightDeviceFlagName, []string{}, - "Block IO weight (relative device weight, format: `DEVICE_NAME:WEIGHT`)", - ) - _ = cmd.RegisterFlagCompletionFunc(blkioWeightDeviceFlagName, completion.AutocompleteDefault) + blkioWeightDeviceFlagName := "blkio-weight-device" + createFlags.StringSliceVar( + &cf.BlkIOWeightDevice, + blkioWeightDeviceFlagName, []string{}, + "Block IO weight (relative device weight, format: `DEVICE_NAME:WEIGHT`)", + ) + _ = cmd.RegisterFlagCompletionFunc(blkioWeightDeviceFlagName, completion.AutocompleteDefault) - capAddFlagName := "cap-add" - createFlags.StringSliceVar( - &cf.CapAdd, - capAddFlagName, []string{}, - "Add capabilities to the container", - ) - _ = cmd.RegisterFlagCompletionFunc(capAddFlagName, completion.AutocompleteCapabilities) + capAddFlagName := "cap-add" + createFlags.StringSliceVar( + &cf.CapAdd, + capAddFlagName, []string{}, + "Add capabilities to the container", + ) + _ = cmd.RegisterFlagCompletionFunc(capAddFlagName, completion.AutocompleteCapabilities) - capDropFlagName := "cap-drop" - createFlags.StringSliceVar( - &cf.CapDrop, - capDropFlagName, []string{}, - "Drop capabilities from the container", - ) - _ = cmd.RegisterFlagCompletionFunc(capDropFlagName, completion.AutocompleteCapabilities) + capDropFlagName := "cap-drop" + createFlags.StringSliceVar( + &cf.CapDrop, + capDropFlagName, []string{}, + "Drop capabilities from the container", + ) + _ = cmd.RegisterFlagCompletionFunc(capDropFlagName, completion.AutocompleteCapabilities) - cgroupnsFlagName := "cgroupns" - createFlags.String( - cgroupnsFlagName, "", - "cgroup namespace to use", - ) - _ = cmd.RegisterFlagCompletionFunc(cgroupnsFlagName, AutocompleteNamespace) + cgroupnsFlagName := "cgroupns" + createFlags.String( + cgroupnsFlagName, "", + "cgroup namespace to use", + ) + _ = cmd.RegisterFlagCompletionFunc(cgroupnsFlagName, AutocompleteNamespace) - cgroupsFlagName := "cgroups" - createFlags.StringVar( - &cf.CGroupsMode, - cgroupsFlagName, cgroupConfig(), - `control container cgroup configuration ("enabled"|"disabled"|"no-conmon"|"split")`, - ) - _ = cmd.RegisterFlagCompletionFunc(cgroupsFlagName, AutocompleteCgroupMode) + cgroupsFlagName := "cgroups" + createFlags.StringVar( + &cf.CGroupsMode, + cgroupsFlagName, cgroupConfig(), + `control container cgroup configuration ("enabled"|"disabled"|"no-conmon"|"split")`, + ) + _ = cmd.RegisterFlagCompletionFunc(cgroupsFlagName, AutocompleteCgroupMode) - cgroupParentFlagName := "cgroup-parent" - createFlags.StringVar( - &cf.CGroupParent, - cgroupParentFlagName, "", - "Optional parent cgroup for the container", - ) - _ = cmd.RegisterFlagCompletionFunc(cgroupParentFlagName, completion.AutocompleteDefault) + cpuPeriodFlagName := "cpu-period" + createFlags.Uint64Var( + &cf.CPUPeriod, + cpuPeriodFlagName, 0, + "Limit the CPU CFS (Completely Fair Scheduler) period", + ) + _ = cmd.RegisterFlagCompletionFunc(cpuPeriodFlagName, completion.AutocompleteNone) - cidfileFlagName := "cidfile" - createFlags.StringVar( - &cf.CIDFile, - cidfileFlagName, "", - "Write the container ID to the file", - ) - _ = cmd.RegisterFlagCompletionFunc(cidfileFlagName, completion.AutocompleteDefault) + cpuQuotaFlagName := "cpu-quota" + createFlags.Int64Var( + &cf.CPUQuota, + cpuQuotaFlagName, 0, + "Limit the CPU CFS (Completely Fair Scheduler) quota", + ) + _ = cmd.RegisterFlagCompletionFunc(cpuQuotaFlagName, completion.AutocompleteNone) - conmonPidfileFlagName := "conmon-pidfile" - createFlags.StringVar( - &cf.ConmonPIDFile, - conmonPidfileFlagName, "", - "Path to the file that will receive the PID of conmon", - ) - _ = cmd.RegisterFlagCompletionFunc(conmonPidfileFlagName, completion.AutocompleteDefault) + cpuRtPeriodFlagName := "cpu-rt-period" + createFlags.Uint64Var( + &cf.CPURTPeriod, + cpuRtPeriodFlagName, 0, + "Limit the CPU real-time period in microseconds", + ) + _ = cmd.RegisterFlagCompletionFunc(cpuRtPeriodFlagName, completion.AutocompleteNone) - cpuPeriodFlagName := "cpu-period" - createFlags.Uint64Var( - &cf.CPUPeriod, - cpuPeriodFlagName, 0, - "Limit the CPU CFS (Completely Fair Scheduler) period", - ) - _ = cmd.RegisterFlagCompletionFunc(cpuPeriodFlagName, completion.AutocompleteNone) + cpuRtRuntimeFlagName := "cpu-rt-runtime" + createFlags.Int64Var( + &cf.CPURTRuntime, + cpuRtRuntimeFlagName, 0, + "Limit the CPU real-time runtime in microseconds", + ) + _ = cmd.RegisterFlagCompletionFunc(cpuRtRuntimeFlagName, completion.AutocompleteNone) - cpuQuotaFlagName := "cpu-quota" - createFlags.Int64Var( - &cf.CPUQuota, - cpuQuotaFlagName, 0, - "Limit the CPU CFS (Completely Fair Scheduler) quota", - ) - _ = cmd.RegisterFlagCompletionFunc(cpuQuotaFlagName, completion.AutocompleteNone) + cpuSharesFlagName := "cpu-shares" + createFlags.Uint64Var( + &cf.CPUShares, + cpuSharesFlagName, 0, + "CPU shares (relative weight)", + ) + _ = cmd.RegisterFlagCompletionFunc(cpuSharesFlagName, completion.AutocompleteNone) + cidfileFlagName := "cidfile" + createFlags.StringVar( + &cf.CIDFile, + cidfileFlagName, "", + "Write the container ID to the file", + ) + _ = cmd.RegisterFlagCompletionFunc(cidfileFlagName, completion.AutocompleteDefault) + cpusetMemsFlagName := "cpuset-mems" + createFlags.StringVar( + &cf.CPUSetMems, + cpusetMemsFlagName, "", + "Memory nodes (MEMs) in which to allow execution (0-3, 0,1). Only effective on NUMA systems.", + ) + _ = cmd.RegisterFlagCompletionFunc(cpusetMemsFlagName, completion.AutocompleteNone) - cpuRtPeriodFlagName := "cpu-rt-period" - createFlags.Uint64Var( - &cf.CPURTPeriod, - cpuRtPeriodFlagName, 0, - "Limit the CPU real-time period in microseconds", - ) - _ = cmd.RegisterFlagCompletionFunc(cpuRtPeriodFlagName, completion.AutocompleteNone) + deviceFlagName := "device" + createFlags.StringSliceVar( + &cf.Devices, + deviceFlagName, devices(), + "Add a host device to the container", + ) + _ = cmd.RegisterFlagCompletionFunc(deviceFlagName, completion.AutocompleteDefault) - cpuRtRuntimeFlagName := "cpu-rt-runtime" - createFlags.Int64Var( - &cf.CPURTRuntime, - cpuRtRuntimeFlagName, 0, - "Limit the CPU real-time runtime in microseconds", - ) - _ = cmd.RegisterFlagCompletionFunc(cpuRtRuntimeFlagName, completion.AutocompleteNone) + deviceCgroupRuleFlagName := "device-cgroup-rule" + createFlags.StringSliceVar( + &cf.DeviceCGroupRule, + deviceCgroupRuleFlagName, []string{}, + "Add a rule to the cgroup allowed devices list", + ) + _ = cmd.RegisterFlagCompletionFunc(deviceCgroupRuleFlagName, completion.AutocompleteNone) - cpuSharesFlagName := "cpu-shares" - createFlags.Uint64Var( - &cf.CPUShares, - cpuSharesFlagName, 0, - "CPU shares (relative weight)", - ) - _ = cmd.RegisterFlagCompletionFunc(cpuSharesFlagName, completion.AutocompleteNone) + deviceReadBpsFlagName := "device-read-bps" + createFlags.StringSliceVar( + &cf.DeviceReadBPs, + deviceReadBpsFlagName, []string{}, + "Limit read rate (bytes per second) from a device (e.g. --device-read-bps=/dev/sda:1mb)", + ) + _ = cmd.RegisterFlagCompletionFunc(deviceReadBpsFlagName, completion.AutocompleteDefault) - cpusFlagName := "cpus" - createFlags.Float64Var( - &cf.CPUS, - cpusFlagName, 0, - "Number of CPUs. The default is 0.000 which means no limit", - ) - _ = cmd.RegisterFlagCompletionFunc(cpusFlagName, completion.AutocompleteNone) + deviceReadIopsFlagName := "device-read-iops" + createFlags.StringSliceVar( + &cf.DeviceReadIOPs, + deviceReadIopsFlagName, []string{}, + "Limit read rate (IO per second) from a device (e.g. --device-read-iops=/dev/sda:1000)", + ) + _ = cmd.RegisterFlagCompletionFunc(deviceReadIopsFlagName, completion.AutocompleteDefault) - cpusetCpusFlagName := "cpuset-cpus" - createFlags.StringVar( - &cf.CPUSetCPUs, - cpusetCpusFlagName, "", - "CPUs in which to allow execution (0-3, 0,1)", - ) - _ = cmd.RegisterFlagCompletionFunc(cpusetCpusFlagName, completion.AutocompleteNone) + deviceWriteBpsFlagName := "device-write-bps" + createFlags.StringSliceVar( + &cf.DeviceWriteBPs, + deviceWriteBpsFlagName, []string{}, + "Limit write rate (bytes per second) to a device (e.g. --device-write-bps=/dev/sda:1mb)", + ) + _ = cmd.RegisterFlagCompletionFunc(deviceWriteBpsFlagName, completion.AutocompleteDefault) - cpusetMemsFlagName := "cpuset-mems" - createFlags.StringVar( - &cf.CPUSetMems, - cpusetMemsFlagName, "", - "Memory nodes (MEMs) in which to allow execution (0-3, 0,1). Only effective on NUMA systems.", - ) - _ = cmd.RegisterFlagCompletionFunc(cpusetMemsFlagName, completion.AutocompleteNone) + deviceWriteIopsFlagName := "device-write-iops" + createFlags.StringSliceVar( + &cf.DeviceWriteIOPs, + deviceWriteIopsFlagName, []string{}, + "Limit write rate (IO per second) to a device (e.g. --device-write-iops=/dev/sda:1000)", + ) + _ = cmd.RegisterFlagCompletionFunc(deviceWriteIopsFlagName, completion.AutocompleteDefault) - deviceFlagName := "device" - createFlags.StringSliceVar( - &cf.Devices, - deviceFlagName, devices(), - "Add a host device to the container", - ) - _ = cmd.RegisterFlagCompletionFunc(deviceFlagName, completion.AutocompleteDefault) + createFlags.Bool( + "disable-content-trust", false, + "This is a Docker specific option and is a NOOP", + ) - deviceCgroupRuleFlagName := "device-cgroup-rule" - createFlags.StringSliceVar( - &cf.DeviceCGroupRule, - deviceCgroupRuleFlagName, []string{}, - "Add a rule to the cgroup allowed devices list", - ) - _ = cmd.RegisterFlagCompletionFunc(deviceCgroupRuleFlagName, completion.AutocompleteNone) + envFlagName := "env" + createFlags.StringArrayP( + envFlagName, "e", env(), + "Set environment variables in container", + ) + _ = cmd.RegisterFlagCompletionFunc(envFlagName, completion.AutocompleteNone) + + if !registry.IsRemote() { + createFlags.BoolVar( + &cf.EnvHost, + "env-host", false, "Use all current host environment variables in container", + ) + } + + envFileFlagName := "env-file" + createFlags.StringSliceVar( + &cf.EnvFile, + envFileFlagName, []string{}, + "Read in a file of environment variables", + ) + _ = cmd.RegisterFlagCompletionFunc(envFileFlagName, completion.AutocompleteDefault) - deviceReadBpsFlagName := "device-read-bps" - createFlags.StringSliceVar( - &cf.DeviceReadBPs, - deviceReadBpsFlagName, []string{}, - "Limit read rate (bytes per second) from a device (e.g. --device-read-bps=/dev/sda:1mb)", - ) - _ = cmd.RegisterFlagCompletionFunc(deviceReadBpsFlagName, completion.AutocompleteDefault) + exposeFlagName := "expose" + createFlags.StringSliceVar( + &cf.Expose, + exposeFlagName, []string{}, + "Expose a port or a range of ports", + ) + _ = cmd.RegisterFlagCompletionFunc(exposeFlagName, completion.AutocompleteNone) - deviceReadIopsFlagName := "device-read-iops" - createFlags.StringSliceVar( - &cf.DeviceReadIOPs, - deviceReadIopsFlagName, []string{}, - "Limit read rate (IO per second) from a device (e.g. --device-read-iops=/dev/sda:1000)", - ) - _ = cmd.RegisterFlagCompletionFunc(deviceReadIopsFlagName, completion.AutocompleteDefault) + groupAddFlagName := "group-add" + createFlags.StringSliceVar( + &cf.GroupAdd, + groupAddFlagName, []string{}, + "Add additional groups to the primary container process. 'keep-groups' allows container processes to use supplementary groups.", + ) + _ = cmd.RegisterFlagCompletionFunc(groupAddFlagName, completion.AutocompleteNone) - deviceWriteBpsFlagName := "device-write-bps" - createFlags.StringSliceVar( - &cf.DeviceWriteBPs, - deviceWriteBpsFlagName, []string{}, - "Limit write rate (bytes per second) to a device (e.g. --device-write-bps=/dev/sda:1mb)", - ) - _ = cmd.RegisterFlagCompletionFunc(deviceWriteBpsFlagName, completion.AutocompleteDefault) + healthCmdFlagName := "health-cmd" + createFlags.StringVar( + &cf.HealthCmd, + healthCmdFlagName, "", + "set a healthcheck command for the container ('none' disables the existing healthcheck)", + ) + _ = cmd.RegisterFlagCompletionFunc(healthCmdFlagName, completion.AutocompleteNone) - deviceWriteIopsFlagName := "device-write-iops" - createFlags.StringSliceVar( - &cf.DeviceWriteIOPs, - deviceWriteIopsFlagName, []string{}, - "Limit write rate (IO per second) to a device (e.g. --device-write-iops=/dev/sda:1000)", - ) - _ = cmd.RegisterFlagCompletionFunc(deviceWriteIopsFlagName, completion.AutocompleteDefault) + healthIntervalFlagName := "health-interval" + createFlags.StringVar( + &cf.HealthInterval, + healthIntervalFlagName, DefaultHealthCheckInterval, + "set an interval for the healthchecks (a value of disable results in no automatic timer setup)", + ) + _ = cmd.RegisterFlagCompletionFunc(healthIntervalFlagName, completion.AutocompleteNone) - createFlags.Bool( - "disable-content-trust", false, - "This is a Docker specific option and is a NOOP", - ) + healthRetriesFlagName := "health-retries" + createFlags.UintVar( + &cf.HealthRetries, + healthRetriesFlagName, DefaultHealthCheckRetries, + "the number of retries allowed before a healthcheck is considered to be unhealthy", + ) + _ = cmd.RegisterFlagCompletionFunc(healthRetriesFlagName, completion.AutocompleteNone) - entrypointFlagName := "entrypoint" - createFlags.String(entrypointFlagName, "", - "Overwrite the default ENTRYPOINT of the image", - ) - _ = cmd.RegisterFlagCompletionFunc(entrypointFlagName, completion.AutocompleteNone) + healthStartPeriodFlagName := "health-start-period" + createFlags.StringVar( + &cf.HealthStartPeriod, + healthStartPeriodFlagName, DefaultHealthCheckStartPeriod, + "the initialization time needed for a container to bootstrap", + ) + _ = cmd.RegisterFlagCompletionFunc(healthStartPeriodFlagName, completion.AutocompleteNone) - envFlagName := "env" - createFlags.StringArrayP( - envFlagName, "e", env(), - "Set environment variables in container", - ) - _ = cmd.RegisterFlagCompletionFunc(envFlagName, completion.AutocompleteNone) + healthTimeoutFlagName := "health-timeout" + createFlags.StringVar( + &cf.HealthTimeout, + healthTimeoutFlagName, DefaultHealthCheckTimeout, + "the maximum time allowed to complete the healthcheck before an interval is considered failed", + ) + _ = cmd.RegisterFlagCompletionFunc(healthTimeoutFlagName, completion.AutocompleteNone) - if !registry.IsRemote() { createFlags.BoolVar( - &cf.EnvHost, - "env-host", false, "Use all current host environment variables in container", + &cf.HTTPProxy, + "http-proxy", containerConfig.Containers.HTTPProxy, + "Set proxy environment variables in the container based on the host proxy vars", ) - } - - envFileFlagName := "env-file" - createFlags.StringSliceVar( - &cf.EnvFile, - envFileFlagName, []string{}, - "Read in a file of environment variables", - ) - _ = cmd.RegisterFlagCompletionFunc(envFileFlagName, completion.AutocompleteDefault) - - exposeFlagName := "expose" - createFlags.StringSliceVar( - &cf.Expose, - exposeFlagName, []string{}, - "Expose a port or a range of ports", - ) - _ = cmd.RegisterFlagCompletionFunc(exposeFlagName, completion.AutocompleteNone) - gidmapFlagName := "gidmap" - createFlags.StringSliceVar( - &cf.GIDMap, - gidmapFlagName, []string{}, - "GID map to use for the user namespace", - ) - _ = cmd.RegisterFlagCompletionFunc(gidmapFlagName, completion.AutocompleteNone) + imageVolumeFlagName := "image-volume" + createFlags.StringVar( + &cf.ImageVolume, + imageVolumeFlagName, DefaultImageVolume, + `Tells podman how to handle the builtin image volumes ("bind"|"tmpfs"|"ignore")`, + ) + _ = cmd.RegisterFlagCompletionFunc(imageVolumeFlagName, AutocompleteImageVolume) - groupAddFlagName := "group-add" - createFlags.StringSliceVar( - &cf.GroupAdd, - groupAddFlagName, []string{}, - "Add additional groups to the primary container process. 'keep-groups' allows container processes to use supplementary groups.", - ) - _ = cmd.RegisterFlagCompletionFunc(groupAddFlagName, completion.AutocompleteNone) + createFlags.BoolVar( + &cf.Init, + "init", false, + "Run an init binary inside the container that forwards signals and reaps processes", + ) - createFlags.Bool( - "help", false, "", - ) + initPathFlagName := "init-path" + createFlags.StringVar( + &cf.InitPath, + initPathFlagName, initPath(), + // Do not use the Value field for setting the default value to determine user input (i.e., non-empty string) + "Path to the container-init binary", + ) + _ = cmd.RegisterFlagCompletionFunc(initPathFlagName, completion.AutocompleteDefault) - healthCmdFlagName := "health-cmd" - createFlags.StringVar( - &cf.HealthCmd, - healthCmdFlagName, "", - "set a healthcheck command for the container ('none' disables the existing healthcheck)", - ) - _ = cmd.RegisterFlagCompletionFunc(healthCmdFlagName, completion.AutocompleteNone) + createFlags.BoolVarP( + &cf.Interactive, + "interactive", "i", false, + "Keep STDIN open even if not attached", + ) + ipcFlagName := "ipc" + createFlags.String( + ipcFlagName, "", + "IPC namespace to use", + ) + _ = cmd.RegisterFlagCompletionFunc(ipcFlagName, AutocompleteNamespace) - healthIntervalFlagName := "health-interval" - createFlags.StringVar( - &cf.HealthInterval, - healthIntervalFlagName, DefaultHealthCheckInterval, - "set an interval for the healthchecks (a value of disable results in no automatic timer setup)", - ) - _ = cmd.RegisterFlagCompletionFunc(healthIntervalFlagName, completion.AutocompleteNone) + kernelMemoryFlagName := "kernel-memory" + createFlags.StringVar( + &cf.KernelMemory, + kernelMemoryFlagName, "", + "Kernel memory limit "+sizeWithUnitFormat, + ) + _ = cmd.RegisterFlagCompletionFunc(kernelMemoryFlagName, completion.AutocompleteNone) + logDriverFlagName := "log-driver" + createFlags.StringVar( + &cf.LogDriver, + logDriverFlagName, logDriver(), + "Logging driver for the container", + ) + _ = cmd.RegisterFlagCompletionFunc(logDriverFlagName, AutocompleteLogDriver) - healthRetriesFlagName := "health-retries" - createFlags.UintVar( - &cf.HealthRetries, - healthRetriesFlagName, DefaultHealthCheckRetries, - "the number of retries allowed before a healthcheck is considered to be unhealthy", - ) - _ = cmd.RegisterFlagCompletionFunc(healthRetriesFlagName, completion.AutocompleteNone) + logOptFlagName := "log-opt" + createFlags.StringSliceVar( + &cf.LogOptions, + logOptFlagName, []string{}, + "Logging driver options", + ) + _ = cmd.RegisterFlagCompletionFunc(logOptFlagName, AutocompleteLogOpt) - healthStartPeriodFlagName := "health-start-period" - createFlags.StringVar( - &cf.HealthStartPeriod, - healthStartPeriodFlagName, DefaultHealthCheckStartPeriod, - "the initialization time needed for a container to bootstrap", - ) - _ = cmd.RegisterFlagCompletionFunc(healthStartPeriodFlagName, completion.AutocompleteNone) + memoryFlagName := "memory" + createFlags.StringVarP( + &cf.Memory, + memoryFlagName, "m", "", + "Memory limit "+sizeWithUnitFormat, + ) + _ = cmd.RegisterFlagCompletionFunc(memoryFlagName, completion.AutocompleteNone) - healthTimeoutFlagName := "health-timeout" - createFlags.StringVar( - &cf.HealthTimeout, - healthTimeoutFlagName, DefaultHealthCheckTimeout, - "the maximum time allowed to complete the healthcheck before an interval is considered failed", - ) - _ = cmd.RegisterFlagCompletionFunc(healthTimeoutFlagName, completion.AutocompleteNone) + memoryReservationFlagName := "memory-reservation" + createFlags.StringVar( + &cf.MemoryReservation, + memoryReservationFlagName, "", + "Memory soft limit "+sizeWithUnitFormat, + ) + _ = cmd.RegisterFlagCompletionFunc(memoryReservationFlagName, completion.AutocompleteNone) - hostnameFlagName := "hostname" - createFlags.StringVarP( - &cf.Hostname, - hostnameFlagName, "h", "", - "Set container hostname", - ) - _ = cmd.RegisterFlagCompletionFunc(hostnameFlagName, completion.AutocompleteNone) + memorySwapFlagName := "memory-swap" + createFlags.StringVar( + &cf.MemorySwap, + memorySwapFlagName, "", + "Swap limit equal to memory plus swap: '-1' to enable unlimited swap", + ) + _ = cmd.RegisterFlagCompletionFunc(memorySwapFlagName, completion.AutocompleteNone) - createFlags.BoolVar( - &cf.HTTPProxy, - "http-proxy", containerConfig.Containers.HTTPProxy, - "Set proxy environment variables in the container based on the host proxy vars", - ) + memorySwappinessFlagName := "memory-swappiness" + createFlags.Int64Var( + &cf.MemorySwappiness, + memorySwappinessFlagName, -1, + "Tune container memory swappiness (0 to 100, or -1 for system default)", + ) + _ = cmd.RegisterFlagCompletionFunc(memorySwappinessFlagName, completion.AutocompleteNone) - imageVolumeFlagName := "image-volume" - createFlags.StringVar( - &cf.ImageVolume, - imageVolumeFlagName, DefaultImageVolume, - `Tells podman how to handle the builtin image volumes ("bind"|"tmpfs"|"ignore")`, - ) - _ = cmd.RegisterFlagCompletionFunc(imageVolumeFlagName, AutocompleteImageVolume) + createFlags.BoolVar( + &cf.NoHealthCheck, + "no-healthcheck", false, + "Disable healthchecks on container", + ) + createFlags.BoolVar( + &cf.OOMKillDisable, + "oom-kill-disable", false, + "Disable OOM Killer", + ) - createFlags.BoolVar( - &cf.Init, - "init", false, - "Run an init binary inside the container that forwards signals and reaps processes", - ) + oomScoreAdjFlagName := "oom-score-adj" + createFlags.IntVar( + &cf.OOMScoreAdj, + oomScoreAdjFlagName, 0, + "Tune the host's OOM preferences (-1000 to 1000)", + ) + _ = cmd.RegisterFlagCompletionFunc(oomScoreAdjFlagName, completion.AutocompleteNone) - initPathFlagName := "init-path" - createFlags.StringVar( - &cf.InitPath, - initPathFlagName, initPath(), - // Do not use the Value field for setting the default value to determine user input (i.e., non-empty string) - "Path to the container-init binary", - ) - _ = cmd.RegisterFlagCompletionFunc(initPathFlagName, completion.AutocompleteDefault) + archFlagName := "arch" + createFlags.StringVar( + &cf.Arch, + archFlagName, "", + "use `ARCH` instead of the architecture of the machine for choosing images", + ) + _ = cmd.RegisterFlagCompletionFunc(archFlagName, completion.AutocompleteArch) - createFlags.BoolVarP( - &cf.Interactive, - "interactive", "i", false, - "Keep STDIN open even if not attached", - ) + osFlagName := "os" + createFlags.StringVar( + &cf.OS, + osFlagName, "", + "use `OS` instead of the running OS for choosing images", + ) + _ = cmd.RegisterFlagCompletionFunc(osFlagName, completion.AutocompleteOS) - ipcFlagName := "ipc" - createFlags.String( - ipcFlagName, "", - "IPC namespace to use", - ) - _ = cmd.RegisterFlagCompletionFunc(ipcFlagName, AutocompleteNamespace) + variantFlagName := "variant" + createFlags.StringVar( + &cf.Variant, + variantFlagName, "", + "Use _VARIANT_ instead of the running architecture variant for choosing images", + ) + _ = cmd.RegisterFlagCompletionFunc(variantFlagName, completion.AutocompleteNone) - kernelMemoryFlagName := "kernel-memory" - createFlags.StringVar( - &cf.KernelMemory, - kernelMemoryFlagName, "", - "Kernel memory limit "+sizeWithUnitFormat, - ) - _ = cmd.RegisterFlagCompletionFunc(kernelMemoryFlagName, completion.AutocompleteNone) + pidsLimitFlagName := "pids-limit" + createFlags.Int64( + pidsLimitFlagName, pidsLimit(), + "Tune container pids limit (set 0 for unlimited, -1 for server defaults)", + ) + _ = cmd.RegisterFlagCompletionFunc(pidsLimitFlagName, completion.AutocompleteNone) - labelFlagName := "label" - createFlags.StringArrayVarP( - &cf.Label, - labelFlagName, "l", []string{}, - "Set metadata on container", - ) - _ = cmd.RegisterFlagCompletionFunc(labelFlagName, completion.AutocompleteNone) + platformFlagName := "platform" + createFlags.StringVar( + &cf.Platform, + platformFlagName, "", + "Specify the platform for selecting the image. (Conflicts with --arch and --os)", + ) + _ = cmd.RegisterFlagCompletionFunc(platformFlagName, completion.AutocompleteNone) - labelFileFlagName := "label-file" - createFlags.StringSliceVar( - &cf.LabelFile, - labelFileFlagName, []string{}, - "Read in a line delimited file of labels", - ) - _ = cmd.RegisterFlagCompletionFunc(labelFileFlagName, completion.AutocompleteDefault) + podFlagName := "pod" + createFlags.StringVar( + &cf.Pod, + podFlagName, "", + "Run container in an existing pod", + ) + _ = cmd.RegisterFlagCompletionFunc(podFlagName, AutocompletePods) - logDriverFlagName := "log-driver" - createFlags.StringVar( - &cf.LogDriver, - logDriverFlagName, logDriver(), - "Logging driver for the container", - ) - _ = cmd.RegisterFlagCompletionFunc(logDriverFlagName, AutocompleteLogDriver) + podIDFileFlagName := "pod-id-file" + createFlags.StringVar( + &cf.PodIDFile, + podIDFileFlagName, "", + "Read the pod ID from the file", + ) + _ = cmd.RegisterFlagCompletionFunc(podIDFileFlagName, completion.AutocompleteDefault) + createFlags.BoolVar( + &cf.Privileged, + "privileged", false, + "Give extended privileges to container", + ) + createFlags.BoolVarP( + &cf.PublishAll, + "publish-all", "P", false, + "Publish all exposed ports to random ports on the host interface", + ) - logOptFlagName := "log-opt" - createFlags.StringSliceVar( - &cf.LogOptions, - logOptFlagName, []string{}, - "Logging driver options", - ) - _ = cmd.RegisterFlagCompletionFunc(logOptFlagName, AutocompleteLogOpt) + pullFlagName := "pull" + createFlags.StringVar( + &cf.Pull, + pullFlagName, policy(), + `Pull image before creating ("always"|"missing"|"never")`, + ) + _ = cmd.RegisterFlagCompletionFunc(pullFlagName, AutocompletePullOption) - memoryFlagName := "memory" - createFlags.StringVarP( - &cf.Memory, - memoryFlagName, "m", "", - "Memory limit "+sizeWithUnitFormat, - ) - _ = cmd.RegisterFlagCompletionFunc(memoryFlagName, completion.AutocompleteNone) + createFlags.BoolVarP( + &cf.Quiet, + "quiet", "q", false, + "Suppress output information when pulling images", + ) + createFlags.BoolVar( + &cf.ReadOnly, + "read-only", false, + "Make containers root filesystem read-only", + ) + createFlags.BoolVar( + &cf.ReadOnlyTmpFS, + "read-only-tmpfs", true, + "When running containers in read-only mode mount a read-write tmpfs on /run, /tmp and /var/tmp", + ) + requiresFlagName := "requires" + createFlags.StringSliceVar( + &cf.Requires, + requiresFlagName, []string{}, + "Add one or more requirement containers that must be started before this container will start", + ) + _ = cmd.RegisterFlagCompletionFunc(requiresFlagName, AutocompleteContainers) - memoryReservationFlagName := "memory-reservation" - createFlags.StringVar( - &cf.MemoryReservation, - memoryReservationFlagName, "", - "Memory soft limit "+sizeWithUnitFormat, - ) - _ = cmd.RegisterFlagCompletionFunc(memoryReservationFlagName, completion.AutocompleteNone) + restartFlagName := "restart" + createFlags.StringVar( + &cf.Restart, + restartFlagName, "", + `Restart policy to apply when a container exits ("always"|"no"|"on-failure"|"unless-stopped")`, + ) + _ = cmd.RegisterFlagCompletionFunc(restartFlagName, AutocompleteRestartOption) - memorySwapFlagName := "memory-swap" - createFlags.StringVar( - &cf.MemorySwap, - memorySwapFlagName, "", - "Swap limit equal to memory plus swap: '-1' to enable unlimited swap", - ) - _ = cmd.RegisterFlagCompletionFunc(memorySwapFlagName, completion.AutocompleteNone) + createFlags.BoolVar( + &cf.Rm, + "rm", false, + "Remove container (and pod if created) after exit", + ) + createFlags.BoolVar( + &cf.RootFS, + "rootfs", false, + "The first argument is not an image but the rootfs to the exploded container", + ) - memorySwappinessFlagName := "memory-swappiness" - createFlags.Int64Var( - &cf.MemorySwappiness, - memorySwappinessFlagName, -1, - "Tune container memory swappiness (0 to 100, or -1 for system default)", - ) - _ = cmd.RegisterFlagCompletionFunc(memorySwappinessFlagName, completion.AutocompleteNone) + sdnotifyFlagName := "sdnotify" + createFlags.StringVar( + &cf.SdNotifyMode, + sdnotifyFlagName, define.SdNotifyModeContainer, + `control sd-notify behavior ("container"|"conmon"|"ignore")`, + ) + _ = cmd.RegisterFlagCompletionFunc(sdnotifyFlagName, AutocompleteSDNotify) - nameFlagName := "name" - createFlags.StringVar( - &cf.Name, - nameFlagName, "", - "Assign a name to the container", - ) - _ = cmd.RegisterFlagCompletionFunc(nameFlagName, completion.AutocompleteNone) + secretFlagName := "secret" + createFlags.StringArrayVar( + &cf.Secrets, + secretFlagName, []string{}, + "Add secret to container", + ) + _ = cmd.RegisterFlagCompletionFunc(secretFlagName, AutocompleteSecrets) - createFlags.BoolVar( - &cf.NoHealthCheck, - "no-healthcheck", false, - "Disable healthchecks on container", - ) - createFlags.BoolVar( - &cf.OOMKillDisable, - "oom-kill-disable", false, - "Disable OOM Killer", - ) + securityOptFlagName := "security-opt" + createFlags.StringArrayVar( + &cf.SecurityOpt, + securityOptFlagName, []string{}, + "Security Options", + ) + _ = cmd.RegisterFlagCompletionFunc(securityOptFlagName, AutocompleteSecurityOption) - oomScoreAdjFlagName := "oom-score-adj" - createFlags.IntVar( - &cf.OOMScoreAdj, - oomScoreAdjFlagName, 0, - "Tune the host's OOM preferences (-1000 to 1000)", - ) - _ = cmd.RegisterFlagCompletionFunc(oomScoreAdjFlagName, completion.AutocompleteNone) + shmSizeFlagName := "shm-size" + createFlags.String( + shmSizeFlagName, shmSize(), + "Size of /dev/shm "+sizeWithUnitFormat, + ) + _ = cmd.RegisterFlagCompletionFunc(shmSizeFlagName, completion.AutocompleteNone) - archFlagName := "arch" - createFlags.StringVar( - &cf.Arch, - archFlagName, "", - "use `ARCH` instead of the architecture of the machine for choosing images", - ) - _ = cmd.RegisterFlagCompletionFunc(archFlagName, completion.AutocompleteArch) + stopSignalFlagName := "stop-signal" + createFlags.StringVar( + &cf.SignaturePolicy, + "signature-policy", "", + "`Pathname` of signature policy file (not usually used)", + ) + createFlags.StringVar( + &cf.StopSignal, + stopSignalFlagName, "", + "Signal to stop a container. Default is SIGTERM", + ) + _ = cmd.RegisterFlagCompletionFunc(stopSignalFlagName, AutocompleteStopSignal) - osFlagName := "os" - createFlags.StringVar( - &cf.OS, - osFlagName, "", - "use `OS` instead of the running OS for choosing images", - ) - _ = cmd.RegisterFlagCompletionFunc(osFlagName, completion.AutocompleteOS) + stopTimeoutFlagName := "stop-timeout" + createFlags.UintVar( + &cf.StopTimeout, + stopTimeoutFlagName, containerConfig.Engine.StopTimeout, + "Timeout (in seconds) that containers stopped by user command have to exit. If exceeded, the container will be forcibly stopped via SIGKILL.", + ) + _ = cmd.RegisterFlagCompletionFunc(stopTimeoutFlagName, completion.AutocompleteNone) - variantFlagName := "variant" - createFlags.StringVar( - &cf.Variant, - variantFlagName, "", - "Use _VARIANT_ instead of the running architecture variant for choosing images", - ) - _ = cmd.RegisterFlagCompletionFunc(variantFlagName, completion.AutocompleteNone) + sysctlFlagName := "sysctl" + createFlags.StringSliceVar( + &cf.Sysctl, + sysctlFlagName, []string{}, + "Sysctl options", + ) + //TODO: Add function for sysctl completion. + _ = cmd.RegisterFlagCompletionFunc(sysctlFlagName, completion.AutocompleteNone) + + systemdFlagName := "systemd" + createFlags.StringVar( + &cf.Systemd, + systemdFlagName, "true", + `Run container in systemd mode ("true"|"false"|"always")`, + ) + _ = cmd.RegisterFlagCompletionFunc(systemdFlagName, AutocompleteSystemdFlag) - personalityFlagName := "personality" - createFlags.StringVar( - &cf.Personality, - personalityFlagName, "", - "Configure execution domain using personality (e.g., LINUX/LINUX32)", - ) - _ = cmd.RegisterFlagCompletionFunc(personalityFlagName, AutocompleteNamespace) + personalityFlagName := "personality" + createFlags.StringVar( + &cf.Personality, + personalityFlagName, "", + "Configure execution domain using personality (e.g., LINUX/LINUX32)", + ) + _ = cmd.RegisterFlagCompletionFunc(personalityFlagName, AutocompleteNamespace) - pidFlagName := "pid" - createFlags.String( - pidFlagName, "", - "PID namespace to use", - ) - _ = cmd.RegisterFlagCompletionFunc(pidFlagName, AutocompleteNamespace) + timeoutFlagName := "timeout" + createFlags.UintVar( + &cf.Timeout, + timeoutFlagName, 0, + "Maximum length of time a container is allowed to run. The container will be killed automatically after the time expires.", + ) + _ = cmd.RegisterFlagCompletionFunc(timeoutFlagName, completion.AutocompleteNone) - pidsLimitFlagName := "pids-limit" - createFlags.Int64( - pidsLimitFlagName, pidsLimit(), - "Tune container pids limit (set 0 for unlimited, -1 for server defaults)", - ) - _ = cmd.RegisterFlagCompletionFunc(pidsLimitFlagName, completion.AutocompleteNone) + // Flag for TLS verification, so that `run` and `create` commands can make use of it. + // Make sure to use `=` while using this flag i.e `--tls-verify=false/true` + tlsVerifyFlagName := "tls-verify" + createFlags.BoolVar( + &cf.TLSVerify, + tlsVerifyFlagName, true, + "Require HTTPS and verify certificates when contacting registries for pulling images", + ) - platformFlagName := "platform" - createFlags.StringVar( - &cf.Platform, - platformFlagName, "", - "Specify the platform for selecting the image. (Conflicts with --arch and --os)", - ) - _ = cmd.RegisterFlagCompletionFunc(platformFlagName, completion.AutocompleteNone) + tmpfsFlagName := "tmpfs" + createFlags.StringArrayVar( + &cf.TmpFS, + tmpfsFlagName, []string{}, + "Mount a temporary filesystem (`tmpfs`) into a container", + ) + _ = cmd.RegisterFlagCompletionFunc(tmpfsFlagName, completion.AutocompleteDefault) - podFlagName := "pod" - createFlags.StringVar( - &cf.Pod, - podFlagName, "", - "Run container in an existing pod", - ) - _ = cmd.RegisterFlagCompletionFunc(podFlagName, AutocompletePods) + createFlags.BoolVarP( + &cf.TTY, + "tty", "t", false, + "Allocate a pseudo-TTY for container", + ) - podIDFileFlagName := "pod-id-file" - createFlags.StringVar( - &cf.PodIDFile, - podIDFileFlagName, "", - "Read the pod ID from the file", - ) - _ = cmd.RegisterFlagCompletionFunc(podIDFileFlagName, completion.AutocompleteDefault) - - // Flag for TLS verification, so that `run` and `create` commands can make use of it. - // Make sure to use `=` while using this flag i.e `--tls-verify=false/true` - tlsVerifyFlagName := "tls-verify" - createFlags.BoolVar( - &cf.TLSVerify, - tlsVerifyFlagName, true, - "Require HTTPS and verify certificates when contacting registries for pulling images", - ) + timezoneFlagName := "tz" + createFlags.StringVar( + &cf.Timezone, + timezoneFlagName, containerConfig.TZ(), + "Set timezone in container", + ) + _ = cmd.RegisterFlagCompletionFunc(timezoneFlagName, completion.AutocompleteNone) //TODO: add timezone completion - createFlags.BoolVar( - &cf.Privileged, - "privileged", false, - "Give extended privileges to container", - ) - createFlags.BoolVarP( - &cf.PublishAll, - "publish-all", "P", false, - "Publish all exposed ports to random ports on the host interface", - ) + umaskFlagName := "umask" + createFlags.StringVar( + &cf.Umask, + umaskFlagName, containerConfig.Umask(), + "Set umask in container", + ) + _ = cmd.RegisterFlagCompletionFunc(umaskFlagName, completion.AutocompleteNone) - pullFlagName := "pull" - createFlags.StringVar( - &cf.Pull, - pullFlagName, policy(), - `Pull image before creating ("always"|"missing"|"never")`, - ) - _ = cmd.RegisterFlagCompletionFunc(pullFlagName, AutocompletePullOption) + ulimitFlagName := "ulimit" + createFlags.StringSliceVar( + &cf.Ulimit, + ulimitFlagName, ulimits(), + "Ulimit options", + ) + _ = cmd.RegisterFlagCompletionFunc(ulimitFlagName, completion.AutocompleteNone) - createFlags.BoolVarP( - &cf.Quiet, - "quiet", "q", false, - "Suppress output information when pulling images", - ) - createFlags.BoolVar( - &cf.ReadOnly, - "read-only", false, - "Make containers root filesystem read-only", - ) - createFlags.BoolVar( - &cf.ReadOnlyTmpFS, - "read-only-tmpfs", true, - "When running containers in read-only mode mount a read-write tmpfs on /run, /tmp and /var/tmp", - ) - createFlags.BoolVar( - &cf.Replace, - "replace", false, - `If a container with the same name exists, replace it`, - ) + userFlagName := "user" + createFlags.StringVarP( + &cf.User, + userFlagName, "u", "", + "Username or UID (format: <name|uid>[:<group|gid>])", + ) + _ = cmd.RegisterFlagCompletionFunc(userFlagName, AutocompleteUserFlag) - requiresFlagName := "requires" - createFlags.StringSliceVar( - &cf.Requires, - requiresFlagName, []string{}, - "Add one or more requirement containers that must be started before this container will start", - ) - _ = cmd.RegisterFlagCompletionFunc(requiresFlagName, AutocompleteContainers) + utsFlagName := "uts" + createFlags.String( + utsFlagName, "", + "UTS namespace to use", + ) + _ = cmd.RegisterFlagCompletionFunc(utsFlagName, AutocompleteNamespace) - restartFlagName := "restart" - createFlags.StringVar( - &cf.Restart, - restartFlagName, "", - `Restart policy to apply when a container exits ("always"|"no"|"on-failure"|"unless-stopped")`, - ) - _ = cmd.RegisterFlagCompletionFunc(restartFlagName, AutocompleteRestartOption) + mountFlagName := "mount" + createFlags.StringArrayVar( + &cf.Mount, + mountFlagName, []string{}, + "Attach a filesystem mount to the container", + ) + _ = cmd.RegisterFlagCompletionFunc(mountFlagName, AutocompleteMountFlag) + + volumeDesciption := "Bind mount a volume into the container" + if registry.IsRemote() { + volumeDesciption = "Bind mount a volume into the container. Volume src will be on the server machine, not the client" + } + volumeFlagName := "volume" + createFlags.StringArrayVarP( + &cf.Volume, + volumeFlagName, "v", volumes(), + volumeDesciption, + ) + _ = cmd.RegisterFlagCompletionFunc(volumeFlagName, AutocompleteVolumeFlag) - createFlags.BoolVar( - &cf.Rm, - "rm", false, - "Remove container (and pod if created) after exit", - ) - createFlags.BoolVar( - &cf.RootFS, - "rootfs", false, - "The first argument is not an image but the rootfs to the exploded container", - ) + volumesFromFlagName := "volumes-from" + createFlags.StringArrayVar( + &cf.VolumesFrom, + volumesFromFlagName, []string{}, + "Mount volumes from the specified container(s)", + ) + _ = cmd.RegisterFlagCompletionFunc(volumesFromFlagName, AutocompleteContainers) - sdnotifyFlagName := "sdnotify" - createFlags.StringVar( - &cf.SdNotifyMode, - sdnotifyFlagName, define.SdNotifyModeContainer, - `control sd-notify behavior ("container"|"conmon"|"ignore")`, - ) - _ = cmd.RegisterFlagCompletionFunc(sdnotifyFlagName, AutocompleteSDNotify) + workdirFlagName := "workdir" + createFlags.StringVarP( + &cf.Workdir, + workdirFlagName, "w", "", + "Working directory inside the container", + ) + _ = cmd.RegisterFlagCompletionFunc(workdirFlagName, completion.AutocompleteDefault) - secretFlagName := "secret" - createFlags.StringArrayVar( - &cf.Secrets, - secretFlagName, []string{}, - "Add secret to container", - ) - _ = cmd.RegisterFlagCompletionFunc(secretFlagName, AutocompleteSecrets) + seccompPolicyFlagName := "seccomp-policy" + createFlags.StringVar( + &cf.SeccompPolicy, + seccompPolicyFlagName, "default", + "Policy for selecting a seccomp profile (experimental)", + ) + _ = cmd.RegisterFlagCompletionFunc(seccompPolicyFlagName, completion.AutocompleteDefault) - securityOptFlagName := "security-opt" - createFlags.StringArrayVar( - &cf.SecurityOpt, - securityOptFlagName, []string{}, - "Security Options", - ) - _ = cmd.RegisterFlagCompletionFunc(securityOptFlagName, AutocompleteSecurityOption) + cgroupConfFlagName := "cgroup-conf" + createFlags.StringSliceVar( + &cf.CgroupConf, + cgroupConfFlagName, []string{}, + "Configure cgroup v2 (key=value)", + ) + _ = cmd.RegisterFlagCompletionFunc(cgroupConfFlagName, completion.AutocompleteNone) - shmSizeFlagName := "shm-size" - createFlags.String( - shmSizeFlagName, shmSize(), - "Size of /dev/shm "+sizeWithUnitFormat, - ) - _ = cmd.RegisterFlagCompletionFunc(shmSizeFlagName, completion.AutocompleteNone) + pidFileFlagName := "pidfile" + createFlags.StringVar( + &cf.PidFile, + pidFileFlagName, "", + "Write the container process ID to the file") + _ = cmd.RegisterFlagCompletionFunc(pidFileFlagName, completion.AutocompleteDefault) - stopSignalFlagName := "stop-signal" - createFlags.StringVar( - &cf.SignaturePolicy, - "signature-policy", "", - "`Pathname` of signature policy file (not usually used)", - ) - createFlags.StringVar( - &cf.StopSignal, - stopSignalFlagName, "", - "Signal to stop a container. Default is SIGTERM", - ) - _ = cmd.RegisterFlagCompletionFunc(stopSignalFlagName, AutocompleteStopSignal) + _ = createFlags.MarkHidden("signature-policy") + if registry.IsRemote() { + _ = createFlags.MarkHidden("env-host") + _ = createFlags.MarkHidden("http-proxy") + } - stopTimeoutFlagName := "stop-timeout" - createFlags.UintVar( - &cf.StopTimeout, - stopTimeoutFlagName, containerConfig.Engine.StopTimeout, - "Timeout (in seconds) that containers stopped by user command have to exit. If exceeded, the container will be forcibly stopped via SIGKILL.", - ) - _ = cmd.RegisterFlagCompletionFunc(stopTimeoutFlagName, completion.AutocompleteNone) + createFlags.BoolVar( + &cf.Replace, + "replace", false, + `If a container with the same name exists, replace it`, + ) + } subgidnameFlagName := "subgidname" createFlags.StringVar( @@ -688,60 +740,13 @@ func DefineCreateFlags(cmd *cobra.Command, cf *ContainerCLIOpts) { ) _ = cmd.RegisterFlagCompletionFunc(subuidnameFlagName, completion.AutocompleteSubuidName) - sysctlFlagName := "sysctl" + gidmapFlagName := "gidmap" createFlags.StringSliceVar( - &cf.Sysctl, - sysctlFlagName, []string{}, - "Sysctl options", - ) - //TODO: Add function for sysctl completion. - _ = cmd.RegisterFlagCompletionFunc(sysctlFlagName, completion.AutocompleteNone) - - systemdFlagName := "systemd" - createFlags.StringVar( - &cf.Systemd, - systemdFlagName, "true", - `Run container in systemd mode ("true"|"false"|"always")`, - ) - _ = cmd.RegisterFlagCompletionFunc(systemdFlagName, AutocompleteSystemdFlag) - - timeoutFlagName := "timeout" - createFlags.UintVar( - &cf.Timeout, - timeoutFlagName, 0, - "Maximum length of time a container is allowed to run. The container will be killed automatically after the time expires.", - ) - _ = cmd.RegisterFlagCompletionFunc(timeoutFlagName, completion.AutocompleteNone) - - tmpfsFlagName := "tmpfs" - createFlags.StringArrayVar( - &cf.TmpFS, - tmpfsFlagName, []string{}, - "Mount a temporary filesystem (`tmpfs`) into a container", - ) - _ = cmd.RegisterFlagCompletionFunc(tmpfsFlagName, completion.AutocompleteDefault) - - createFlags.BoolVarP( - &cf.TTY, - "tty", "t", false, - "Allocate a pseudo-TTY for container", - ) - - timezoneFlagName := "tz" - createFlags.StringVar( - &cf.Timezone, - timezoneFlagName, containerConfig.TZ(), - "Set timezone in container", - ) - _ = cmd.RegisterFlagCompletionFunc(timezoneFlagName, completion.AutocompleteNone) //TODO: add timezone completion - - umaskFlagName := "umask" - createFlags.StringVar( - &cf.Umask, - umaskFlagName, containerConfig.Umask(), - "Set umask in container", + &cf.GIDMap, + gidmapFlagName, []string{}, + "GID map to use for the user namespace", ) - _ = cmd.RegisterFlagCompletionFunc(umaskFlagName, completion.AutocompleteNone) + _ = cmd.RegisterFlagCompletionFunc(gidmapFlagName, completion.AutocompleteNone) uidmapFlagName := "uidmap" createFlags.StringSliceVar( @@ -751,22 +756,6 @@ func DefineCreateFlags(cmd *cobra.Command, cf *ContainerCLIOpts) { ) _ = cmd.RegisterFlagCompletionFunc(uidmapFlagName, completion.AutocompleteNone) - ulimitFlagName := "ulimit" - createFlags.StringSliceVar( - &cf.Ulimit, - ulimitFlagName, ulimits(), - "Ulimit options", - ) - _ = cmd.RegisterFlagCompletionFunc(ulimitFlagName, completion.AutocompleteNone) - - userFlagName := "user" - createFlags.StringVarP( - &cf.User, - userFlagName, "u", "", - "Username or UID (format: <name|uid>[:<group|gid>])", - ) - _ = cmd.RegisterFlagCompletionFunc(userFlagName, AutocompleteUserFlag) - usernsFlagName := "userns" createFlags.String( usernsFlagName, os.Getenv("PODMAN_USERNS"), @@ -774,75 +763,106 @@ func DefineCreateFlags(cmd *cobra.Command, cf *ContainerCLIOpts) { ) _ = cmd.RegisterFlagCompletionFunc(usernsFlagName, AutocompleteUserNamespace) - utsFlagName := "uts" - createFlags.String( - utsFlagName, "", - "UTS namespace to use", + cgroupParentFlagName := "cgroup-parent" + createFlags.StringVar( + &cf.CGroupParent, + cgroupParentFlagName, "", + "Optional parent cgroup for the container", ) - _ = cmd.RegisterFlagCompletionFunc(utsFlagName, AutocompleteNamespace) + _ = cmd.RegisterFlagCompletionFunc(cgroupParentFlagName, completion.AutocompleteDefault) - mountFlagName := "mount" - createFlags.StringArrayVar( - &cf.Mount, - mountFlagName, []string{}, - "Attach a filesystem mount to the container", + conmonPidfileFlagName := "" + if !isInfra { + conmonPidfileFlagName = "conmon-pidfile" + } else { + conmonPidfileFlagName = "infra-conmon-pidfile" + } + createFlags.StringVar( + &cf.ConmonPIDFile, + conmonPidfileFlagName, "", + "Path to the file that will receive the PID of conmon", ) - _ = cmd.RegisterFlagCompletionFunc(mountFlagName, AutocompleteMountFlag) + _ = cmd.RegisterFlagCompletionFunc(conmonPidfileFlagName, completion.AutocompleteDefault) - volumeDesciption := "Bind mount a volume into the container" - if registry.IsRemote() { - volumeDesciption = "Bind mount a volume into the container. Volume src will be on the server machine, not the client" - } - volumeFlagName := "volume" - createFlags.StringArrayVarP( - &cf.Volume, - volumeFlagName, "v", volumes(), - volumeDesciption, + cpusFlagName := "cpus" + createFlags.Float64Var( + &cf.CPUS, + cpusFlagName, 0, + "Number of CPUs. The default is 0.000 which means no limit", + ) + _ = cmd.RegisterFlagCompletionFunc(cpusFlagName, completion.AutocompleteNone) + + cpusetCpusFlagName := "cpuset-cpus" + createFlags.StringVar( + &cf.CPUSetCPUs, + cpusetCpusFlagName, "", + "CPUs in which to allow execution (0-3, 0,1)", ) - _ = cmd.RegisterFlagCompletionFunc(volumeFlagName, AutocompleteVolumeFlag) + _ = cmd.RegisterFlagCompletionFunc(cpusetCpusFlagName, completion.AutocompleteNone) + + entrypointFlagName := "" + if !isInfra { + entrypointFlagName = "entrypoint" + } else { + entrypointFlagName = "infra-command" + } - volumesFromFlagName := "volumes-from" - createFlags.StringArrayVar( - &cf.VolumesFrom, - volumesFromFlagName, []string{}, - "Mount volumes from the specified container(s)", + createFlags.String(entrypointFlagName, "", + "Overwrite the default ENTRYPOINT of the image", ) - _ = cmd.RegisterFlagCompletionFunc(volumesFromFlagName, AutocompleteContainers) + _ = cmd.RegisterFlagCompletionFunc(entrypointFlagName, completion.AutocompleteNone) - workdirFlagName := "workdir" + hostnameFlagName := "hostname" createFlags.StringVarP( - &cf.Workdir, - workdirFlagName, "w", "", - "Working directory inside the container", + &cf.Hostname, + hostnameFlagName, "h", "", + "Set container hostname", ) - _ = cmd.RegisterFlagCompletionFunc(workdirFlagName, completion.AutocompleteDefault) + _ = cmd.RegisterFlagCompletionFunc(hostnameFlagName, completion.AutocompleteNone) - seccompPolicyFlagName := "seccomp-policy" - createFlags.StringVar( - &cf.SeccompPolicy, - seccompPolicyFlagName, "default", - "Policy for selecting a seccomp profile (experimental)", + labelFlagName := "label" + createFlags.StringArrayVarP( + &cf.Label, + labelFlagName, "l", []string{}, + "Set metadata on container", ) - _ = cmd.RegisterFlagCompletionFunc(seccompPolicyFlagName, completion.AutocompleteDefault) + _ = cmd.RegisterFlagCompletionFunc(labelFlagName, completion.AutocompleteNone) - cgroupConfFlagName := "cgroup-conf" + labelFileFlagName := "label-file" createFlags.StringSliceVar( - &cf.CgroupConf, - cgroupConfFlagName, []string{}, - "Configure cgroup v2 (key=value)", + &cf.LabelFile, + labelFileFlagName, []string{}, + "Read in a line delimited file of labels", ) - _ = cmd.RegisterFlagCompletionFunc(cgroupConfFlagName, completion.AutocompleteNone) + _ = cmd.RegisterFlagCompletionFunc(labelFileFlagName, completion.AutocompleteDefault) - pidFileFlagName := "pidfile" - createFlags.StringVar( - &cf.PidFile, - pidFileFlagName, "", - "Write the container process ID to the file") - _ = cmd.RegisterFlagCompletionFunc(pidFileFlagName, completion.AutocompleteDefault) - - _ = createFlags.MarkHidden("signature-policy") - if registry.IsRemote() { - _ = createFlags.MarkHidden("env-host") - _ = createFlags.MarkHidden("http-proxy") + nameFlagName := "" + if !isInfra { + nameFlagName = "name" + createFlags.StringVar( + &cf.Name, + nameFlagName, "", + "Assign a name to the container", + ) + } else { + nameFlagName = "infra-name" + createFlags.StringVar( + &cf.Name, + nameFlagName, "", + "Assign a name to the container", + ) } + _ = cmd.RegisterFlagCompletionFunc(nameFlagName, completion.AutocompleteNone) + + createFlags.Bool( + "help", false, "", + ) + + pidFlagName := "pid" + createFlags.StringVar( + &cf.PID, + pidFlagName, "", + "PID namespace to use", + ) + _ = cmd.RegisterFlagCompletionFunc(pidFlagName, AutocompleteNamespace) } diff --git a/cmd/podman/common/create_opts.go b/cmd/podman/common/create_opts.go index c94f46cf2..2f592cba7 100644 --- a/cmd/podman/common/create_opts.go +++ b/cmd/podman/common/create_opts.go @@ -19,122 +19,6 @@ import ( "github.com/pkg/errors" ) -type ContainerCLIOpts struct { - Annotation []string - Attach []string - Authfile string - BlkIOWeight string - BlkIOWeightDevice []string - CapAdd []string - CapDrop []string - CgroupNS string - CGroupsMode string - CGroupParent string - CIDFile string - ConmonPIDFile string - CPUPeriod uint64 - CPUQuota int64 - CPURTPeriod uint64 - CPURTRuntime int64 - CPUShares uint64 - CPUS float64 - CPUSetCPUs string - CPUSetMems string - Devices []string - DeviceCGroupRule []string - DeviceReadBPs []string - DeviceReadIOPs []string - DeviceWriteBPs []string - DeviceWriteIOPs []string - Entrypoint *string - Env []string - EnvHost bool - EnvFile []string - Expose []string - GIDMap []string - GroupAdd []string - HealthCmd string - HealthInterval string - HealthRetries uint - HealthStartPeriod string - HealthTimeout string - Hostname string - HTTPProxy bool - ImageVolume string - Init bool - InitContainerType string - InitPath string - Interactive bool - IPC string - KernelMemory string - Label []string - LabelFile []string - LogDriver string - LogOptions []string - Memory string - MemoryReservation string - MemorySwap string - MemorySwappiness int64 - Name string - NoHealthCheck bool - OOMKillDisable bool - OOMScoreAdj int - Arch string - OS string - Variant string - Personality string - PID string - PIDsLimit *int64 - Platform string - Pod string - PodIDFile string - PreserveFDs uint - Privileged bool - PublishAll bool - Pull string - Quiet bool - ReadOnly bool - ReadOnlyTmpFS bool - Restart string - Replace bool - Requires []string - Rm bool - RootFS bool - Secrets []string - SecurityOpt []string - SdNotifyMode string - ShmSize string - SignaturePolicy string - StopSignal string - StopTimeout uint - StorageOpt []string - SubUIDName string - SubGIDName string - Sysctl []string - Systemd string - Timeout uint - TLSVerify bool - TmpFS []string - TTY bool - Timezone string - Umask string - UIDMap []string - Ulimit []string - User string - UserNS string - UTS string - Mount []string - Volume []string - VolumesFrom []string - Workdir string - SeccompPolicy string - PidFile string - - Net *entities.NetOptions - - CgroupConf []string -} - func stringMaptoArray(m map[string]string) []string { a := make([]string, 0, len(m)) for k, v := range m { @@ -145,7 +29,7 @@ func stringMaptoArray(m map[string]string) []string { // ContainerCreateToContainerCLIOpts converts a compat input struct to cliopts so it can be converted to // a specgen spec. -func ContainerCreateToContainerCLIOpts(cc handlers.CreateContainerConfig, rtc *config.Config) (*ContainerCLIOpts, []string, error) { +func ContainerCreateToContainerCLIOpts(cc handlers.CreateContainerConfig, rtc *config.Config) (*entities.ContainerCreateOptions, []string, error) { var ( capAdd []string cappDrop []string @@ -341,7 +225,7 @@ func ContainerCreateToContainerCLIOpts(cc handlers.CreateContainerConfig, rtc *c // Note: several options here are marked as "don't need". this is based // on speculation by Matt and I. We think that these come into play later // like with start. We believe this is just a difference in podman/compat - cliOpts := ContainerCLIOpts{ + cliOpts := entities.ContainerCreateOptions{ // Attach: nil, // don't need? Authfile: "", CapAdd: append(capAdd, cc.HostConfig.CapAdd...), diff --git a/cmd/podman/common/createparse.go b/cmd/podman/common/createparse.go deleted file mode 100644 index dcef1a151..000000000 --- a/cmd/podman/common/createparse.go +++ /dev/null @@ -1,29 +0,0 @@ -package common - -import ( - "github.com/containers/common/pkg/config" - "github.com/pkg/errors" -) - -// validate determines if the flags and values given by the user are valid. things checked -// by validate must not need any state information on the flag (i.e. changed) -func (c *ContainerCLIOpts) validate() error { - var () - if c.Rm && (c.Restart != "" && c.Restart != "no" && c.Restart != "on-failure") { - return errors.Errorf(`the --rm option conflicts with --restart, when the restartPolicy is not "" and "no"`) - } - - if _, err := config.ParsePullPolicy(c.Pull); err != nil { - return err - } - - var imageVolType = map[string]string{ - "bind": "", - "tmpfs": "", - "ignore": "", - } - if _, ok := imageVolType[c.ImageVolume]; !ok { - return errors.Errorf("invalid image-volume type %q. Pick one of bind, tmpfs, or ignore", c.ImageVolume) - } - return nil -} diff --git a/cmd/podman/common/netflags.go b/cmd/podman/common/netflags.go index aa8714b50..d11f3c9d2 100644 --- a/cmd/podman/common/netflags.go +++ b/cmd/podman/common/netflags.go @@ -8,8 +8,10 @@ import ( "github.com/containers/podman/v3/libpod/define" "github.com/containers/podman/v3/pkg/domain/entities" "github.com/containers/podman/v3/pkg/specgen" + "github.com/containers/podman/v3/pkg/specgenutil" "github.com/pkg/errors" "github.com/spf13/cobra" + "github.com/spf13/pflag" ) func DefineNetFlags(cmd *cobra.Command) { @@ -87,12 +89,15 @@ func DefineNetFlags(cmd *cobra.Command) { // NetFlagsToNetOptions parses the network flags for the given cmd. // The netnsFromConfig bool is used to indicate if the --network flag // should always be parsed regardless if it was set on the cli. -func NetFlagsToNetOptions(cmd *cobra.Command, netnsFromConfig bool) (*entities.NetOptions, error) { +func NetFlagsToNetOptions(opts *entities.NetOptions, flags pflag.FlagSet, netnsFromConfig bool) (*entities.NetOptions, error) { var ( err error ) - opts := entities.NetOptions{} - opts.AddHosts, err = cmd.Flags().GetStringSlice("add-host") + if opts == nil { + opts = &entities.NetOptions{} + } + + opts.AddHosts, err = flags.GetStringSlice("add-host") if err != nil { return nil, err } @@ -103,56 +108,50 @@ func NetFlagsToNetOptions(cmd *cobra.Command, netnsFromConfig bool) (*entities.N } } - if cmd.Flags().Changed("dns") { - servers, err := cmd.Flags().GetStringSlice("dns") - if err != nil { - return nil, err - } - for _, d := range servers { - if d == "none" { - opts.UseImageResolvConf = true - if len(servers) > 1 { - return nil, errors.Errorf("%s is not allowed to be specified with other DNS ip addresses", d) - } - break - } - dns := net.ParseIP(d) - if dns == nil { - return nil, errors.Errorf("%s is not an ip address", d) + servers, err := flags.GetStringSlice("dns") + if err != nil { + return nil, err + } + for _, d := range servers { + if d == "none" { + opts.UseImageResolvConf = true + if len(servers) > 1 { + return nil, errors.Errorf("%s is not allowed to be specified with other DNS ip addresses", d) } - opts.DNSServers = append(opts.DNSServers, dns) + break + } + dns := net.ParseIP(d) + if dns == nil { + return nil, errors.Errorf("%s is not an ip address", d) } + opts.DNSServers = append(opts.DNSServers, dns) } - if cmd.Flags().Changed("dns-opt") { - options, err := cmd.Flags().GetStringSlice("dns-opt") - if err != nil { - return nil, err - } - opts.DNSOptions = options + options, err := flags.GetStringSlice("dns-opt") + if err != nil { + return nil, err } + opts.DNSOptions = options - if cmd.Flags().Changed("dns-search") { - dnsSearches, err := cmd.Flags().GetStringSlice("dns-search") - if err != nil { - return nil, err - } - // Validate domains are good - for _, dom := range dnsSearches { - if dom == "." { - if len(dnsSearches) > 1 { - return nil, errors.Errorf("cannot pass additional search domains when also specifying '.'") - } - continue - } - if _, err := parse.ValidateDomain(dom); err != nil { - return nil, err + dnsSearches, err := flags.GetStringSlice("dns-search") + if err != nil { + return nil, err + } + // Validate domains are good + for _, dom := range dnsSearches { + if dom == "." { + if len(dnsSearches) > 1 { + return nil, errors.Errorf("cannot pass additional search domains when also specifying '.'") } + continue + } + if _, err := parse.ValidateDomain(dom); err != nil { + return nil, err } - opts.DNSSearch = dnsSearches } + opts.DNSSearch = dnsSearches - m, err := cmd.Flags().GetString("mac-address") + m, err := flags.GetString("mac-address") if err != nil { return nil, err } @@ -164,18 +163,18 @@ func NetFlagsToNetOptions(cmd *cobra.Command, netnsFromConfig bool) (*entities.N opts.StaticMAC = &mac } - inputPorts, err := cmd.Flags().GetStringSlice("publish") + inputPorts, err := flags.GetStringSlice("publish") if err != nil { return nil, err } if len(inputPorts) > 0 { - opts.PublishPorts, err = CreatePortBindings(inputPorts) + opts.PublishPorts, err = specgenutil.CreatePortBindings(inputPorts) if err != nil { return nil, err } } - ip, err := cmd.Flags().GetString("ip") + ip, err := flags.GetString("ip") if err != nil { return nil, err } @@ -190,15 +189,15 @@ func NetFlagsToNetOptions(cmd *cobra.Command, netnsFromConfig bool) (*entities.N opts.StaticIP = &staticIP } - opts.NoHosts, err = cmd.Flags().GetBool("no-hosts") + opts.NoHosts, err = flags.GetBool("no-hosts") if err != nil { return nil, err } // parse the --network value only when the flag is set or we need to use // the netns config value, e.g. when --pod is not used - if netnsFromConfig || cmd.Flag("network").Changed { - network, err := cmd.Flags().GetString("network") + if netnsFromConfig || flags.Changed("network") { + network, err := flags.GetString("network") if err != nil { return nil, err } @@ -215,12 +214,13 @@ func NetFlagsToNetOptions(cmd *cobra.Command, netnsFromConfig bool) (*entities.N opts.CNINetworks = cniNets } - aliases, err := cmd.Flags().GetStringSlice("network-alias") + aliases, err := flags.GetStringSlice("network-alias") if err != nil { return nil, err } if len(aliases) > 0 { opts.Aliases = aliases } - return &opts, err + + return opts, err } diff --git a/cmd/podman/common/ports.go b/cmd/podman/common/ports.go deleted file mode 100644 index 2092bbe53..000000000 --- a/cmd/podman/common/ports.go +++ /dev/null @@ -1,22 +0,0 @@ -package common - -import ( - "github.com/docker/go-connections/nat" - "github.com/pkg/errors" -) - -func verifyExpose(expose []string) error { - // add the expose ports from the user (--expose) - // can be single or a range - for _, expose := range expose { - // support two formats for expose, original format <portnum>/[<proto>] or <startport-endport>/[<proto>] - _, port := nat.SplitProtoPort(expose) - // parse the start and end port and create a sequence of ports to expose - // if expose a port, the start and end port are the same - _, _, err := nat.ParsePortRange(port) - if err != nil { - return errors.Wrapf(err, "invalid range format for --expose: %s", expose) - } - } - return nil -} diff --git a/cmd/podman/common/specgen.go b/cmd/podman/common/specgen.go deleted file mode 100644 index 59d32f568..000000000 --- a/cmd/podman/common/specgen.go +++ /dev/null @@ -1,972 +0,0 @@ -package common - -import ( - "fmt" - "os" - "strconv" - "strings" - "time" - - "github.com/containers/image/v5/manifest" - "github.com/containers/podman/v3/cmd/podman/parse" - "github.com/containers/podman/v3/libpod/define" - ann "github.com/containers/podman/v3/pkg/annotations" - envLib "github.com/containers/podman/v3/pkg/env" - ns "github.com/containers/podman/v3/pkg/namespaces" - "github.com/containers/podman/v3/pkg/specgen" - systemdDefine "github.com/containers/podman/v3/pkg/systemd/define" - "github.com/containers/podman/v3/pkg/util" - "github.com/docker/go-units" - "github.com/opencontainers/runtime-spec/specs-go" - "github.com/pkg/errors" -) - -func getCPULimits(c *ContainerCLIOpts) *specs.LinuxCPU { - cpu := &specs.LinuxCPU{} - hasLimits := false - - if c.CPUS > 0 { - period, quota := util.CoresToPeriodAndQuota(c.CPUS) - - cpu.Period = &period - cpu.Quota = "a - hasLimits = true - } - if c.CPUShares > 0 { - cpu.Shares = &c.CPUShares - hasLimits = true - } - if c.CPUPeriod > 0 { - cpu.Period = &c.CPUPeriod - hasLimits = true - } - if c.CPUSetCPUs != "" { - cpu.Cpus = c.CPUSetCPUs - hasLimits = true - } - if c.CPUSetMems != "" { - cpu.Mems = c.CPUSetMems - hasLimits = true - } - if c.CPUQuota > 0 { - cpu.Quota = &c.CPUQuota - hasLimits = true - } - if c.CPURTPeriod > 0 { - cpu.RealtimePeriod = &c.CPURTPeriod - hasLimits = true - } - if c.CPURTRuntime > 0 { - cpu.RealtimeRuntime = &c.CPURTRuntime - hasLimits = true - } - - if !hasLimits { - return nil - } - return cpu -} - -func getIOLimits(s *specgen.SpecGenerator, c *ContainerCLIOpts) (*specs.LinuxBlockIO, error) { - var err error - io := &specs.LinuxBlockIO{} - hasLimits := false - if b := c.BlkIOWeight; len(b) > 0 { - u, err := strconv.ParseUint(b, 10, 16) - if err != nil { - return nil, errors.Wrapf(err, "invalid value for blkio-weight") - } - nu := uint16(u) - io.Weight = &nu - hasLimits = true - } - - if len(c.BlkIOWeightDevice) > 0 { - if err := parseWeightDevices(s, c.BlkIOWeightDevice); err != nil { - return nil, err - } - hasLimits = true - } - - if bps := c.DeviceReadBPs; len(bps) > 0 { - if s.ThrottleReadBpsDevice, err = parseThrottleBPSDevices(bps); err != nil { - return nil, err - } - hasLimits = true - } - - if bps := c.DeviceWriteBPs; len(bps) > 0 { - if s.ThrottleWriteBpsDevice, err = parseThrottleBPSDevices(bps); err != nil { - return nil, err - } - hasLimits = true - } - - if iops := c.DeviceReadIOPs; len(iops) > 0 { - if s.ThrottleReadIOPSDevice, err = parseThrottleIOPsDevices(iops); err != nil { - return nil, err - } - hasLimits = true - } - - if iops := c.DeviceWriteIOPs; len(iops) > 0 { - if s.ThrottleWriteIOPSDevice, err = parseThrottleIOPsDevices(iops); err != nil { - return nil, err - } - hasLimits = true - } - - if !hasLimits { - return nil, nil - } - return io, nil -} - -func getMemoryLimits(s *specgen.SpecGenerator, c *ContainerCLIOpts) (*specs.LinuxMemory, error) { - var err error - memory := &specs.LinuxMemory{} - hasLimits := false - if m := c.Memory; len(m) > 0 { - ml, err := units.RAMInBytes(m) - if err != nil { - return nil, errors.Wrapf(err, "invalid value for memory") - } - memory.Limit = &ml - if c.MemorySwap == "" { - limit := 2 * ml - memory.Swap = &(limit) - } - hasLimits = true - } - if m := c.MemoryReservation; len(m) > 0 { - mr, err := units.RAMInBytes(m) - if err != nil { - return nil, errors.Wrapf(err, "invalid value for memory") - } - memory.Reservation = &mr - hasLimits = true - } - if m := c.MemorySwap; len(m) > 0 { - var ms int64 - // only set memory swap if it was set - // -1 indicates unlimited - if m != "-1" { - ms, err = units.RAMInBytes(m) - memory.Swap = &ms - if err != nil { - return nil, errors.Wrapf(err, "invalid value for memory") - } - hasLimits = true - } - } - if m := c.KernelMemory; len(m) > 0 { - mk, err := units.RAMInBytes(m) - if err != nil { - return nil, errors.Wrapf(err, "invalid value for kernel-memory") - } - memory.Kernel = &mk - hasLimits = true - } - if c.MemorySwappiness >= 0 { - swappiness := uint64(c.MemorySwappiness) - memory.Swappiness = &swappiness - hasLimits = true - } - if c.OOMKillDisable { - memory.DisableOOMKiller = &c.OOMKillDisable - hasLimits = true - } - if !hasLimits { - return nil, nil - } - return memory, nil -} - -func setNamespaces(s *specgen.SpecGenerator, c *ContainerCLIOpts) error { - var err error - - if c.PID != "" { - s.PidNS, err = specgen.ParseNamespace(c.PID) - if err != nil { - return err - } - } - if c.IPC != "" { - s.IpcNS, err = specgen.ParseNamespace(c.IPC) - if err != nil { - return err - } - } - if c.UTS != "" { - s.UtsNS, err = specgen.ParseNamespace(c.UTS) - if err != nil { - return err - } - } - if c.CgroupNS != "" { - s.CgroupNS, err = specgen.ParseNamespace(c.CgroupNS) - if err != nil { - return err - } - } - // userns must be treated differently - if c.UserNS != "" { - s.UserNS, err = specgen.ParseUserNamespace(c.UserNS) - if err != nil { - return err - } - } - if c.Net != nil { - s.NetNS = c.Net.Network - } - return nil -} - -func FillOutSpecGen(s *specgen.SpecGenerator, c *ContainerCLIOpts, args []string) error { - var ( - err error - ) - - // validate flags as needed - if err := c.validate(); err != nil { - return err - } - - s.User = c.User - inputCommand := args[1:] - if len(c.HealthCmd) > 0 { - if c.NoHealthCheck { - return errors.New("Cannot specify both --no-healthcheck and --health-cmd") - } - s.HealthConfig, err = makeHealthCheckFromCli(c.HealthCmd, c.HealthInterval, c.HealthRetries, c.HealthTimeout, c.HealthStartPeriod) - if err != nil { - return err - } - } else if c.NoHealthCheck { - s.HealthConfig = &manifest.Schema2HealthConfig{ - Test: []string{"NONE"}, - } - } - - userNS := ns.UsernsMode(c.UserNS) - s.IDMappings, err = util.ParseIDMapping(userNS, c.UIDMap, c.GIDMap, c.SubUIDName, c.SubGIDName) - if err != nil { - return err - } - // If some mappings are specified, assume a private user namespace - if userNS.IsDefaultValue() && (!s.IDMappings.HostUIDMapping || !s.IDMappings.HostGIDMapping) { - s.UserNS.NSMode = specgen.Private - } else { - s.UserNS.NSMode = specgen.NamespaceMode(userNS) - } - - s.Terminal = c.TTY - - if err := verifyExpose(c.Expose); err != nil { - return err - } - // We are not handling the Expose flag yet. - // s.PortsExpose = c.Expose - s.PortMappings = c.Net.PublishPorts - s.PublishExposedPorts = c.PublishAll - s.Pod = c.Pod - - if len(c.PodIDFile) > 0 { - if len(s.Pod) > 0 { - return errors.New("Cannot specify both --pod and --pod-id-file") - } - podID, err := ReadPodIDFile(c.PodIDFile) - if err != nil { - return err - } - s.Pod = podID - } - - expose, err := createExpose(c.Expose) - if err != nil { - return err - } - s.Expose = expose - - if err := setNamespaces(s, c); err != nil { - return err - } - - if sig := c.StopSignal; len(sig) > 0 { - stopSignal, err := util.ParseSignal(sig) - if err != nil { - return err - } - s.StopSignal = &stopSignal - } - - // ENVIRONMENT VARIABLES - // - // Precedence order (higher index wins): - // 1) containers.conf (EnvHost, EnvHTTP, Env) 2) image data, 3 User EnvHost/EnvHTTP, 4) env-file, 5) env - // containers.conf handled and image data handled on the server side - // user specified EnvHost and EnvHTTP handled on Server Side relative to Server - // env-file and env handled on client side - var env map[string]string - - // First transform the os env into a map. We need it for the labels later in - // any case. - osEnv, err := envLib.ParseSlice(os.Environ()) - if err != nil { - return errors.Wrap(err, "error parsing host environment variables") - } - - s.EnvHost = c.EnvHost - s.HTTPProxy = c.HTTPProxy - - // env-file overrides any previous variables - for _, f := range c.EnvFile { - fileEnv, err := envLib.ParseFile(f) - if err != nil { - return err - } - // File env is overridden by env. - env = envLib.Join(env, fileEnv) - } - - parsedEnv, err := envLib.ParseSlice(c.Env) - if err != nil { - return err - } - - s.Env = envLib.Join(env, parsedEnv) - - // LABEL VARIABLES - labels, err := parse.GetAllLabels(c.LabelFile, c.Label) - if err != nil { - return errors.Wrapf(err, "unable to process labels") - } - - if systemdUnit, exists := osEnv[systemdDefine.EnvVariable]; exists { - labels[systemdDefine.EnvVariable] = systemdUnit - } - - s.Labels = labels - - // ANNOTATIONS - annotations := make(map[string]string) - - // First, add our default annotations - annotations[ann.TTY] = "false" - if c.TTY { - annotations[ann.TTY] = "true" - } - - // Last, add user annotations - for _, annotation := range c.Annotation { - splitAnnotation := strings.SplitN(annotation, "=", 2) - if len(splitAnnotation) < 2 { - return errors.Errorf("Annotations must be formatted KEY=VALUE") - } - annotations[splitAnnotation[0]] = splitAnnotation[1] - } - s.Annotations = annotations - - s.WorkDir = c.Workdir - if c.Entrypoint != nil { - entrypoint := []string{} - if ep := *c.Entrypoint; len(ep) > 0 { - // Check if entrypoint specified is json - if err := json.Unmarshal([]byte(*c.Entrypoint), &entrypoint); err != nil { - entrypoint = append(entrypoint, ep) - } - } - s.Entrypoint = entrypoint - } - - // Include the command used to create the container. - s.ContainerCreateCommand = os.Args - - if len(inputCommand) > 0 { - s.Command = inputCommand - } - - // SHM Size - if c.ShmSize != "" { - shmSize, err := units.FromHumanSize(c.ShmSize) - if err != nil { - return errors.Wrapf(err, "unable to translate --shm-size") - } - s.ShmSize = &shmSize - } - s.CNINetworks = c.Net.CNINetworks - - // Network aliases - if len(c.Net.Aliases) > 0 { - // build a map of aliases where key=cniName - aliases := make(map[string][]string, len(s.CNINetworks)) - for _, cniNetwork := range s.CNINetworks { - aliases[cniNetwork] = c.Net.Aliases - } - s.Aliases = aliases - } - - s.HostAdd = c.Net.AddHosts - s.UseImageResolvConf = c.Net.UseImageResolvConf - s.DNSServers = c.Net.DNSServers - s.DNSSearch = c.Net.DNSSearch - s.DNSOptions = c.Net.DNSOptions - s.StaticIP = c.Net.StaticIP - s.StaticMAC = c.Net.StaticMAC - s.NetworkOptions = c.Net.NetworkOptions - s.UseImageHosts = c.Net.NoHosts - - s.ImageVolumeMode = c.ImageVolume - if s.ImageVolumeMode == "bind" { - s.ImageVolumeMode = "anonymous" - } - - s.Systemd = c.Systemd - s.SdNotifyMode = c.SdNotifyMode - if s.ResourceLimits == nil { - s.ResourceLimits = &specs.LinuxResources{} - } - s.ResourceLimits.Memory, err = getMemoryLimits(s, c) - if err != nil { - return err - } - s.ResourceLimits.BlockIO, err = getIOLimits(s, c) - if err != nil { - return err - } - if c.PIDsLimit != nil { - pids := specs.LinuxPids{ - Limit: *c.PIDsLimit, - } - - s.ResourceLimits.Pids = &pids - } - s.ResourceLimits.CPU = getCPULimits(c) - - unifieds := make(map[string]string) - for _, unified := range c.CgroupConf { - splitUnified := strings.SplitN(unified, "=", 2) - if len(splitUnified) < 2 { - return errors.Errorf("--cgroup-conf must be formatted KEY=VALUE") - } - unifieds[splitUnified[0]] = splitUnified[1] - } - if len(unifieds) > 0 { - s.ResourceLimits.Unified = unifieds - } - - if s.ResourceLimits.CPU == nil && s.ResourceLimits.Pids == nil && s.ResourceLimits.BlockIO == nil && s.ResourceLimits.Memory == nil && s.ResourceLimits.Unified == nil { - s.ResourceLimits = nil - } - - if s.LogConfiguration == nil { - s.LogConfiguration = &specgen.LogConfig{} - } - - if ld := c.LogDriver; len(ld) > 0 { - s.LogConfiguration.Driver = ld - } - s.CgroupParent = c.CGroupParent - s.CgroupsMode = c.CGroupsMode - s.Groups = c.GroupAdd - - s.Hostname = c.Hostname - sysctl := map[string]string{} - if ctl := c.Sysctl; len(ctl) > 0 { - sysctl, err = util.ValidateSysctls(ctl) - if err != nil { - return err - } - } - s.Sysctl = sysctl - - s.CapAdd = c.CapAdd - s.CapDrop = c.CapDrop - s.Privileged = c.Privileged - s.ReadOnlyFilesystem = c.ReadOnly - s.ConmonPidFile = c.ConmonPIDFile - - s.DependencyContainers = c.Requires - - // TODO - // outside of specgen and oci though - // defaults to true, check spec/storage - // s.readonly = c.ReadOnlyTmpFS - // TODO convert to map? - // check if key=value and convert - sysmap := make(map[string]string) - for _, ctl := range c.Sysctl { - splitCtl := strings.SplitN(ctl, "=", 2) - if len(splitCtl) < 2 { - return errors.Errorf("invalid sysctl value %q", ctl) - } - sysmap[splitCtl[0]] = splitCtl[1] - } - s.Sysctl = sysmap - - if c.CIDFile != "" { - s.Annotations[define.InspectAnnotationCIDFile] = c.CIDFile - } - - for _, opt := range c.SecurityOpt { - if opt == "no-new-privileges" { - s.ContainerSecurityConfig.NoNewPrivileges = true - } else { - con := strings.SplitN(opt, "=", 2) - if len(con) != 2 { - return fmt.Errorf("invalid --security-opt 1: %q", opt) - } - switch con[0] { - case "apparmor": - s.ContainerSecurityConfig.ApparmorProfile = con[1] - s.Annotations[define.InspectAnnotationApparmor] = con[1] - case "label": - // TODO selinux opts and label opts are the same thing - s.ContainerSecurityConfig.SelinuxOpts = append(s.ContainerSecurityConfig.SelinuxOpts, con[1]) - s.Annotations[define.InspectAnnotationLabel] = strings.Join(s.ContainerSecurityConfig.SelinuxOpts, ",label=") - case "mask": - s.ContainerSecurityConfig.Mask = append(s.ContainerSecurityConfig.Mask, strings.Split(con[1], ":")...) - case "proc-opts": - s.ProcOpts = strings.Split(con[1], ",") - case "seccomp": - s.SeccompProfilePath = con[1] - s.Annotations[define.InspectAnnotationSeccomp] = con[1] - // this option is for docker compatibility, it is the same as unmask=ALL - case "systempaths": - if con[1] == "unconfined" { - s.ContainerSecurityConfig.Unmask = append(s.ContainerSecurityConfig.Unmask, []string{"ALL"}...) - } else { - return fmt.Errorf("invalid systempaths option %q, only `unconfined` is supported", con[1]) - } - case "unmask": - s.ContainerSecurityConfig.Unmask = append(s.ContainerSecurityConfig.Unmask, con[1:]...) - default: - return fmt.Errorf("invalid --security-opt 2: %q", opt) - } - } - } - - s.SeccompPolicy = c.SeccompPolicy - - s.VolumesFrom = c.VolumesFrom - - // Only add read-only tmpfs mounts in case that we are read-only and the - // read-only tmpfs flag has been set. - mounts, volumes, overlayVolumes, imageVolumes, err := parseVolumes(c.Volume, c.Mount, c.TmpFS, c.ReadOnlyTmpFS && c.ReadOnly) - if err != nil { - return err - } - s.Mounts = mounts - s.Volumes = volumes - s.OverlayVolumes = overlayVolumes - s.ImageVolumes = imageVolumes - - for _, dev := range c.Devices { - s.Devices = append(s.Devices, specs.LinuxDevice{Path: dev}) - } - - for _, rule := range c.DeviceCGroupRule { - dev, err := parseLinuxResourcesDeviceAccess(rule) - if err != nil { - return err - } - s.DeviceCGroupRule = append(s.DeviceCGroupRule, dev) - } - - s.Init = c.Init - s.InitPath = c.InitPath - s.Stdin = c.Interactive - // quiet - // DeviceCgroupRules: c.StringSlice("device-cgroup-rule"), - - // Rlimits/Ulimits - for _, u := range c.Ulimit { - if u == "host" { - s.Rlimits = nil - break - } - ul, err := units.ParseUlimit(u) - if err != nil { - return errors.Wrapf(err, "ulimit option %q requires name=SOFT:HARD, failed to be parsed", u) - } - rl := specs.POSIXRlimit{ - Type: ul.Name, - Hard: uint64(ul.Hard), - Soft: uint64(ul.Soft), - } - s.Rlimits = append(s.Rlimits, rl) - } - - logOpts := make(map[string]string) - for _, o := range c.LogOptions { - split := strings.SplitN(o, "=", 2) - if len(split) < 2 { - return errors.Errorf("invalid log option %q", o) - } - switch strings.ToLower(split[0]) { - case "driver": - s.LogConfiguration.Driver = split[1] - case "path": - s.LogConfiguration.Path = split[1] - case "max-size": - logSize, err := units.FromHumanSize(split[1]) - if err != nil { - return err - } - s.LogConfiguration.Size = logSize - default: - logOpts[split[0]] = split[1] - } - } - s.LogConfiguration.Options = logOpts - s.Name = c.Name - s.PreserveFDs = c.PreserveFDs - - s.OOMScoreAdj = &c.OOMScoreAdj - if c.Restart != "" { - splitRestart := strings.Split(c.Restart, ":") - switch len(splitRestart) { - case 1: - // No retries specified - case 2: - if strings.ToLower(splitRestart[0]) != "on-failure" { - return errors.Errorf("restart policy retries can only be specified with on-failure restart policy") - } - retries, err := strconv.Atoi(splitRestart[1]) - if err != nil { - return errors.Wrapf(err, "error parsing restart policy retry count") - } - if retries < 0 { - return errors.Errorf("must specify restart policy retry count as a number greater than 0") - } - var retriesUint = uint(retries) - s.RestartRetries = &retriesUint - default: - return errors.Errorf("invalid restart policy: may specify retries at most once") - } - s.RestartPolicy = splitRestart[0] - } - - s.Secrets, s.EnvSecrets, err = parseSecrets(c.Secrets) - if err != nil { - return err - } - - if c.Personality != "" { - s.Personality = &specs.LinuxPersonality{} - s.Personality.Domain = specs.LinuxPersonalityDomain(c.Personality) - } - - s.Remove = c.Rm - s.StopTimeout = &c.StopTimeout - s.Timeout = c.Timeout - s.Timezone = c.Timezone - s.Umask = c.Umask - s.PidFile = c.PidFile - s.Volatile = c.Rm - - // Initcontainers - s.InitContainerType = c.InitContainerType - return nil -} - -func makeHealthCheckFromCli(inCmd, interval string, retries uint, timeout, startPeriod string) (*manifest.Schema2HealthConfig, error) { - cmdArr := []string{} - isArr := true - err := json.Unmarshal([]byte(inCmd), &cmdArr) // array unmarshalling - if err != nil { - cmdArr = strings.SplitN(inCmd, " ", 2) // default for compat - isArr = false - } - // Every healthcheck requires a command - if len(cmdArr) == 0 { - return nil, errors.New("Must define a healthcheck command for all healthchecks") - } - concat := "" - if cmdArr[0] == "CMD" || cmdArr[0] == "none" { // this is for compat, we are already split properly for most compat cases - cmdArr = strings.Fields(inCmd) - } else if cmdArr[0] != "CMD-SHELL" { // this is for podman side of things, won't contain the keywords - if isArr && len(cmdArr) > 1 { // an array of consecutive commands - cmdArr = append([]string{"CMD"}, cmdArr...) - } else { // one singular command - if len(cmdArr) == 1 { - concat = cmdArr[0] - } else { - concat = strings.Join(cmdArr[0:], " ") - } - cmdArr = append([]string{"CMD-SHELL"}, concat) - } - } - - if cmdArr[0] == "none" { // if specified to remove healtcheck - cmdArr = []string{"NONE"} - } - - // healthcheck is by default an array, so we simply pass the user input - hc := manifest.Schema2HealthConfig{ - Test: cmdArr, - } - - if interval == "disable" { - interval = "0" - } - intervalDuration, err := time.ParseDuration(interval) - if err != nil { - return nil, errors.Wrapf(err, "invalid healthcheck-interval") - } - - hc.Interval = intervalDuration - - if retries < 1 { - return nil, errors.New("healthcheck-retries must be greater than 0") - } - hc.Retries = int(retries) - timeoutDuration, err := time.ParseDuration(timeout) - if err != nil { - return nil, errors.Wrapf(err, "invalid healthcheck-timeout") - } - if timeoutDuration < time.Duration(1) { - return nil, errors.New("healthcheck-timeout must be at least 1 second") - } - hc.Timeout = timeoutDuration - - startPeriodDuration, err := time.ParseDuration(startPeriod) - if err != nil { - return nil, errors.Wrapf(err, "invalid healthcheck-start-period") - } - if startPeriodDuration < time.Duration(0) { - return nil, errors.New("healthcheck-start-period must be 0 seconds or greater") - } - hc.StartPeriod = startPeriodDuration - - return &hc, nil -} - -func parseWeightDevices(s *specgen.SpecGenerator, weightDevs []string) error { - for _, val := range weightDevs { - split := strings.SplitN(val, ":", 2) - if len(split) != 2 { - return fmt.Errorf("bad format: %s", val) - } - if !strings.HasPrefix(split[0], "/dev/") { - return fmt.Errorf("bad format for device path: %s", val) - } - weight, err := strconv.ParseUint(split[1], 10, 0) - if err != nil { - return fmt.Errorf("invalid weight for device: %s", val) - } - if weight > 0 && (weight < 10 || weight > 1000) { - return fmt.Errorf("invalid weight for device: %s", val) - } - w := uint16(weight) - s.WeightDevice[split[0]] = specs.LinuxWeightDevice{ - Weight: &w, - LeafWeight: nil, - } - } - return nil -} - -func parseThrottleBPSDevices(bpsDevices []string) (map[string]specs.LinuxThrottleDevice, error) { - td := make(map[string]specs.LinuxThrottleDevice) - for _, val := range bpsDevices { - split := strings.SplitN(val, ":", 2) - if len(split) != 2 { - return nil, fmt.Errorf("bad format: %s", val) - } - if !strings.HasPrefix(split[0], "/dev/") { - return nil, fmt.Errorf("bad format for device path: %s", val) - } - rate, err := units.RAMInBytes(split[1]) - if err != nil { - return nil, fmt.Errorf("invalid rate for device: %s. The correct format is <device-path>:<number>[<unit>]. Number must be a positive integer. Unit is optional and can be kb, mb, or gb", val) - } - if rate < 0 { - return nil, fmt.Errorf("invalid rate for device: %s. The correct format is <device-path>:<number>[<unit>]. Number must be a positive integer. Unit is optional and can be kb, mb, or gb", val) - } - td[split[0]] = specs.LinuxThrottleDevice{Rate: uint64(rate)} - } - return td, nil -} - -func parseThrottleIOPsDevices(iopsDevices []string) (map[string]specs.LinuxThrottleDevice, error) { - td := make(map[string]specs.LinuxThrottleDevice) - for _, val := range iopsDevices { - split := strings.SplitN(val, ":", 2) - if len(split) != 2 { - return nil, fmt.Errorf("bad format: %s", val) - } - if !strings.HasPrefix(split[0], "/dev/") { - return nil, fmt.Errorf("bad format for device path: %s", val) - } - rate, err := strconv.ParseUint(split[1], 10, 64) - if err != nil { - return nil, fmt.Errorf("invalid rate for device: %s. The correct format is <device-path>:<number>. Number must be a positive integer", val) - } - td[split[0]] = specs.LinuxThrottleDevice{Rate: rate} - } - return td, nil -} - -func parseSecrets(secrets []string) ([]specgen.Secret, map[string]string, error) { - secretParseError := errors.New("error parsing secret") - var mount []specgen.Secret - envs := make(map[string]string) - for _, val := range secrets { - // mount only tells if user has set an option that can only be used with mount secret type - mountOnly := false - source := "" - secretType := "" - target := "" - var uid, gid uint32 - // default mode 444 octal = 292 decimal - var mode uint32 = 292 - split := strings.Split(val, ",") - - // --secret mysecret - if len(split) == 1 { - mountSecret := specgen.Secret{ - Source: val, - UID: uid, - GID: gid, - Mode: mode, - } - mount = append(mount, mountSecret) - continue - } - // --secret mysecret,opt=opt - if !strings.Contains(split[0], "=") { - source = split[0] - split = split[1:] - } - - for _, val := range split { - kv := strings.SplitN(val, "=", 2) - if len(kv) < 2 { - return nil, nil, errors.Wrapf(secretParseError, "option %s must be in form option=value", val) - } - switch kv[0] { - case "source": - source = kv[1] - case "type": - if secretType != "" { - return nil, nil, errors.Wrap(secretParseError, "cannot set more tha one secret type") - } - if kv[1] != "mount" && kv[1] != "env" { - return nil, nil, errors.Wrapf(secretParseError, "type %s is invalid", kv[1]) - } - secretType = kv[1] - case "target": - target = kv[1] - case "mode": - mountOnly = true - mode64, err := strconv.ParseUint(kv[1], 8, 32) - if err != nil { - return nil, nil, errors.Wrapf(secretParseError, "mode %s invalid", kv[1]) - } - mode = uint32(mode64) - case "uid", "UID": - mountOnly = true - uid64, err := strconv.ParseUint(kv[1], 10, 32) - if err != nil { - return nil, nil, errors.Wrapf(secretParseError, "UID %s invalid", kv[1]) - } - uid = uint32(uid64) - case "gid", "GID": - mountOnly = true - gid64, err := strconv.ParseUint(kv[1], 10, 32) - if err != nil { - return nil, nil, errors.Wrapf(secretParseError, "GID %s invalid", kv[1]) - } - gid = uint32(gid64) - - default: - return nil, nil, errors.Wrapf(secretParseError, "option %s invalid", val) - } - } - - if secretType == "" { - secretType = "mount" - } - if source == "" { - return nil, nil, errors.Wrapf(secretParseError, "no source found %s", val) - } - if secretType == "mount" { - if target != "" { - return nil, nil, errors.Wrapf(secretParseError, "target option is invalid for mounted secrets") - } - mountSecret := specgen.Secret{ - Source: source, - UID: uid, - GID: gid, - Mode: mode, - } - mount = append(mount, mountSecret) - } - if secretType == "env" { - if mountOnly { - return nil, nil, errors.Wrap(secretParseError, "UID, GID, Mode options cannot be set with secret type env") - } - if target == "" { - target = source - } - envs[target] = source - } - } - return mount, envs, nil -} - -var cgroupDeviceType = map[string]bool{ - "a": true, // all - "b": true, // block device - "c": true, // character device -} - -var cgroupDeviceAccess = map[string]bool{ - "r": true, //read - "w": true, //write - "m": true, //mknod -} - -// parseLinuxResourcesDeviceAccess parses the raw string passed with the --device-access-add flag -func parseLinuxResourcesDeviceAccess(device string) (specs.LinuxDeviceCgroup, error) { - var devType, access string - var major, minor *int64 - - value := strings.Split(device, " ") - if len(value) != 3 { - return specs.LinuxDeviceCgroup{}, fmt.Errorf("invalid device cgroup rule requires type, major:Minor, and access rules: %q", device) - } - - devType = value[0] - if !cgroupDeviceType[devType] { - return specs.LinuxDeviceCgroup{}, fmt.Errorf("invalid device type in device-access-add: %s", devType) - } - - number := strings.SplitN(value[1], ":", 2) - i, err := strconv.ParseInt(number[0], 10, 64) - if err != nil { - return specs.LinuxDeviceCgroup{}, err - } - major = &i - if len(number) == 2 && number[1] != "*" { - i, err := strconv.ParseInt(number[1], 10, 64) - if err != nil { - return specs.LinuxDeviceCgroup{}, err - } - minor = &i - } - access = value[2] - for _, c := range strings.Split(access, "") { - if !cgroupDeviceAccess[c] { - return specs.LinuxDeviceCgroup{}, fmt.Errorf("invalid device access in device-access-add: %s", c) - } - } - return specs.LinuxDeviceCgroup{ - Allow: true, - Type: devType, - Major: major, - Minor: minor, - Access: access, - }, nil -} diff --git a/cmd/podman/common/util.go b/cmd/podman/common/util.go deleted file mode 100644 index cdfff9d6f..000000000 --- a/cmd/podman/common/util.go +++ /dev/null @@ -1,274 +0,0 @@ -package common - -import ( - "io/ioutil" - "net" - "strconv" - "strings" - - "github.com/containers/podman/v3/libpod/network/types" - "github.com/pkg/errors" - "github.com/sirupsen/logrus" -) - -// ReadPodIDFile reads the specified file and returns its content (i.e., first -// line). -func ReadPodIDFile(path string) (string, error) { - content, err := ioutil.ReadFile(path) - if err != nil { - return "", errors.Wrap(err, "error reading pod ID file") - } - return strings.Split(string(content), "\n")[0], nil -} - -// ReadPodIDFiles reads the specified files and returns their content (i.e., -// first line). -func ReadPodIDFiles(files []string) ([]string, error) { - ids := []string{} - for _, file := range files { - id, err := ReadPodIDFile(file) - if err != nil { - return nil, err - } - ids = append(ids, id) - } - return ids, nil -} - -// ParseFilters transforms one filter format to another and validates input -func ParseFilters(filter []string) (map[string][]string, error) { - // TODO Remove once filter refactor is finished and url.Values done. - filters := map[string][]string{} - for _, f := range filter { - t := strings.SplitN(f, "=", 2) - filters = make(map[string][]string) - if len(t) < 2 { - return map[string][]string{}, errors.Errorf("filter input must be in the form of filter=value: %s is invalid", f) - } - filters[t[0]] = append(filters[t[0]], t[1]) - } - return filters, nil -} - -// createExpose parses user-provided exposed port definitions and converts them -// into SpecGen format. -// TODO: The SpecGen format should really handle ranges more sanely - we could -// be massively inflating what is sent over the wire with a large range. -func createExpose(expose []string) (map[uint16]string, error) { - toReturn := make(map[uint16]string) - - for _, e := range expose { - // Check for protocol - proto := "tcp" - splitProto := strings.Split(e, "/") - if len(splitProto) > 2 { - return nil, errors.Errorf("invalid expose format - protocol can only be specified once") - } else if len(splitProto) == 2 { - proto = splitProto[1] - } - - // Check for a range - start, len, err := parseAndValidateRange(splitProto[0]) - if err != nil { - return nil, err - } - - var index uint16 - for index = 0; index < len; index++ { - portNum := start + index - protocols, ok := toReturn[portNum] - if !ok { - toReturn[portNum] = proto - } else { - newProto := strings.Join(append(strings.Split(protocols, ","), strings.Split(proto, ",")...), ",") - toReturn[portNum] = newProto - } - } - } - - return toReturn, nil -} - -// CreatePortBindings iterates ports mappings into SpecGen format. -func CreatePortBindings(ports []string) ([]types.PortMapping, error) { - // --publish is formatted as follows: - // [[hostip:]hostport[-endPort]:]containerport[-endPort][/protocol] - toReturn := make([]types.PortMapping, 0, len(ports)) - - for _, p := range ports { - var ( - ctrPort string - proto, hostIP, hostPort *string - ) - - splitProto := strings.Split(p, "/") - switch len(splitProto) { - case 1: - // No protocol was provided - case 2: - proto = &(splitProto[1]) - default: - return nil, errors.Errorf("invalid port format - protocol can only be specified once") - } - - remainder := splitProto[0] - haveV6 := false - - // Check for an IPv6 address in brackets - splitV6 := strings.Split(remainder, "]") - switch len(splitV6) { - case 1: - // Do nothing, proceed as before - case 2: - // We potentially have an IPv6 address - haveV6 = true - if !strings.HasPrefix(splitV6[0], "[") { - return nil, errors.Errorf("invalid port format - IPv6 addresses must be enclosed by []") - } - if !strings.HasPrefix(splitV6[1], ":") { - return nil, errors.Errorf("invalid port format - IPv6 address must be followed by a colon (':')") - } - ipNoPrefix := strings.TrimPrefix(splitV6[0], "[") - hostIP = &ipNoPrefix - remainder = strings.TrimPrefix(splitV6[1], ":") - default: - return nil, errors.Errorf("invalid port format - at most one IPv6 address can be specified in a --publish") - } - - splitPort := strings.Split(remainder, ":") - switch len(splitPort) { - case 1: - if haveV6 { - return nil, errors.Errorf("invalid port format - must provide host and destination port if specifying an IP") - } - ctrPort = splitPort[0] - case 2: - hostPort = &(splitPort[0]) - ctrPort = splitPort[1] - case 3: - if haveV6 { - return nil, errors.Errorf("invalid port format - when v6 address specified, must be [ipv6]:hostPort:ctrPort") - } - hostIP = &(splitPort[0]) - hostPort = &(splitPort[1]) - ctrPort = splitPort[2] - default: - return nil, errors.Errorf("invalid port format - format is [[hostIP:]hostPort:]containerPort") - } - - newPort, err := parseSplitPort(hostIP, hostPort, ctrPort, proto) - if err != nil { - return nil, err - } - - toReturn = append(toReturn, newPort) - } - - return toReturn, nil -} - -// parseSplitPort parses individual components of the --publish flag to produce -// a single port mapping in SpecGen format. -func parseSplitPort(hostIP, hostPort *string, ctrPort string, protocol *string) (types.PortMapping, error) { - newPort := types.PortMapping{} - if ctrPort == "" { - return newPort, errors.Errorf("must provide a non-empty container port to publish") - } - ctrStart, ctrLen, err := parseAndValidateRange(ctrPort) - if err != nil { - return newPort, errors.Wrapf(err, "error parsing container port") - } - newPort.ContainerPort = ctrStart - newPort.Range = ctrLen - - if protocol != nil { - if *protocol == "" { - return newPort, errors.Errorf("must provide a non-empty protocol to publish") - } - newPort.Protocol = *protocol - } - if hostIP != nil { - if *hostIP == "" { - return newPort, errors.Errorf("must provide a non-empty container host IP to publish") - } else if *hostIP != "0.0.0.0" { - // If hostIP is 0.0.0.0, leave it unset - CNI treats - // 0.0.0.0 and empty differently, Docker does not. - testIP := net.ParseIP(*hostIP) - if testIP == nil { - return newPort, errors.Errorf("cannot parse %q as an IP address", *hostIP) - } - newPort.HostIP = testIP.String() - } - } - if hostPort != nil { - if *hostPort == "" { - // Set 0 as a placeholder. The server side of Specgen - // will find a random, open, unused port to use. - newPort.HostPort = 0 - } else { - hostStart, hostLen, err := parseAndValidateRange(*hostPort) - if err != nil { - return newPort, errors.Wrapf(err, "error parsing host port") - } - if hostLen != ctrLen { - return newPort, errors.Errorf("host and container port ranges have different lengths: %d vs %d", hostLen, ctrLen) - } - newPort.HostPort = hostStart - } - } - - hport := newPort.HostPort - logrus.Debugf("Adding port mapping from %d to %d length %d protocol %q", hport, newPort.ContainerPort, newPort.Range, newPort.Protocol) - - return newPort, nil -} - -// Parse and validate a port range. -// Returns start port, length of range, error. -func parseAndValidateRange(portRange string) (uint16, uint16, error) { - splitRange := strings.Split(portRange, "-") - if len(splitRange) > 2 { - return 0, 0, errors.Errorf("invalid port format - port ranges are formatted as startPort-stopPort") - } - - if splitRange[0] == "" { - return 0, 0, errors.Errorf("port numbers cannot be negative") - } - - startPort, err := parseAndValidatePort(splitRange[0]) - if err != nil { - return 0, 0, err - } - - var rangeLen uint16 = 1 - if len(splitRange) == 2 { - if splitRange[1] == "" { - return 0, 0, errors.Errorf("must provide ending number for port range") - } - endPort, err := parseAndValidatePort(splitRange[1]) - if err != nil { - return 0, 0, err - } - if endPort <= startPort { - return 0, 0, errors.Errorf("the end port of a range must be higher than the start port - %d is not higher than %d", endPort, startPort) - } - // Our range is the total number of ports - // involved, so we need to add 1 (8080:8081 is - // 2 ports, for example, not 1) - rangeLen = endPort - startPort + 1 - } - - return startPort, rangeLen, nil -} - -// Turn a single string into a valid U16 port. -func parseAndValidatePort(port string) (uint16, error) { - num, err := strconv.Atoi(port) - if err != nil { - return 0, errors.Wrapf(err, "invalid port number") - } - if num < 1 || num > 65535 { - return 0, errors.Errorf("port numbers must be between 1 and 65535 (inclusive), got %d", num) - } - return uint16(num), nil -} diff --git a/cmd/podman/common/volumes.go b/cmd/podman/common/volumes.go deleted file mode 100644 index 883d604da..000000000 --- a/cmd/podman/common/volumes.go +++ /dev/null @@ -1,630 +0,0 @@ -package common - -import ( - "fmt" - "path/filepath" - "strings" - - "github.com/containers/common/pkg/parse" - "github.com/containers/podman/v3/libpod/define" - "github.com/containers/podman/v3/pkg/specgen" - "github.com/containers/podman/v3/pkg/util" - spec "github.com/opencontainers/runtime-spec/specs-go" - "github.com/pkg/errors" -) - -var ( - errDuplicateDest = errors.Errorf("duplicate mount destination") - optionArgError = errors.Errorf("must provide an argument for option") - noDestError = errors.Errorf("must set volume destination") - errInvalidSyntax = errors.Errorf("incorrect mount format: should be --mount type=<bind|tmpfs|volume>,[src=<host-dir|volume-name>,]target=<ctr-dir>[,options]") -) - -// Parse all volume-related options in the create config into a set of mounts -// and named volumes to add to the container. -// Handles --volumes, --mount, and --tmpfs flags. -// Does not handle image volumes, init, and --volumes-from flags. -// Can also add tmpfs mounts from read-only tmpfs. -// TODO: handle options parsing/processing via containers/storage/pkg/mount -func parseVolumes(volumeFlag, mountFlag, tmpfsFlag []string, addReadOnlyTmpfs bool) ([]spec.Mount, []*specgen.NamedVolume, []*specgen.OverlayVolume, []*specgen.ImageVolume, error) { - // Get mounts from the --mounts flag. - unifiedMounts, unifiedVolumes, unifiedImageVolumes, err := getMounts(mountFlag) - if err != nil { - return nil, nil, nil, nil, err - } - - // Next --volumes flag. - volumeMounts, volumeVolumes, overlayVolumes, err := specgen.GenVolumeMounts(volumeFlag) - if err != nil { - return nil, nil, nil, nil, err - } - - // Next --tmpfs flag. - tmpfsMounts, err := getTmpfsMounts(tmpfsFlag) - if err != nil { - return nil, nil, nil, nil, err - } - - // Unify mounts from --mount, --volume, --tmpfs. - // Start with --volume. - for dest, mount := range volumeMounts { - if _, ok := unifiedMounts[dest]; ok { - return nil, nil, nil, nil, errors.Wrapf(errDuplicateDest, dest) - } - unifiedMounts[dest] = mount - } - for dest, volume := range volumeVolumes { - if _, ok := unifiedVolumes[dest]; ok { - return nil, nil, nil, nil, errors.Wrapf(errDuplicateDest, dest) - } - unifiedVolumes[dest] = volume - } - // Now --tmpfs - for dest, tmpfs := range tmpfsMounts { - if _, ok := unifiedMounts[dest]; ok { - return nil, nil, nil, nil, errors.Wrapf(errDuplicateDest, dest) - } - unifiedMounts[dest] = tmpfs - } - - // If requested, add tmpfs filesystems for read-only containers. - if addReadOnlyTmpfs { - readonlyTmpfs := []string{"/tmp", "/var/tmp", "/run"} - options := []string{"rw", "rprivate", "nosuid", "nodev", "tmpcopyup"} - for _, dest := range readonlyTmpfs { - if _, ok := unifiedMounts[dest]; ok { - continue - } - if _, ok := unifiedVolumes[dest]; ok { - continue - } - unifiedMounts[dest] = spec.Mount{ - Destination: dest, - Type: define.TypeTmpfs, - Source: "tmpfs", - Options: options, - } - } - } - - // Check for conflicts between named volumes, overlay & image volumes, - // and mounts - allMounts := make(map[string]bool) - testAndSet := func(dest string) error { - if _, ok := allMounts[dest]; ok { - return errors.Wrapf(errDuplicateDest, "conflict at mount destination %v", dest) - } - allMounts[dest] = true - return nil - } - for dest := range unifiedMounts { - if err := testAndSet(dest); err != nil { - return nil, nil, nil, nil, err - } - } - for dest := range unifiedVolumes { - if err := testAndSet(dest); err != nil { - return nil, nil, nil, nil, err - } - } - for dest := range overlayVolumes { - if err := testAndSet(dest); err != nil { - return nil, nil, nil, nil, err - } - } - for dest := range unifiedImageVolumes { - if err := testAndSet(dest); err != nil { - return nil, nil, nil, nil, err - } - } - - // Final step: maps to arrays - finalMounts := make([]spec.Mount, 0, len(unifiedMounts)) - for _, mount := range unifiedMounts { - if mount.Type == define.TypeBind { - absSrc, err := filepath.Abs(mount.Source) - if err != nil { - return nil, nil, nil, nil, errors.Wrapf(err, "error getting absolute path of %s", mount.Source) - } - mount.Source = absSrc - } - finalMounts = append(finalMounts, mount) - } - finalVolumes := make([]*specgen.NamedVolume, 0, len(unifiedVolumes)) - for _, volume := range unifiedVolumes { - finalVolumes = append(finalVolumes, volume) - } - finalOverlayVolume := make([]*specgen.OverlayVolume, 0) - for _, volume := range overlayVolumes { - finalOverlayVolume = append(finalOverlayVolume, volume) - } - finalImageVolumes := make([]*specgen.ImageVolume, 0, len(unifiedImageVolumes)) - for _, volume := range unifiedImageVolumes { - finalImageVolumes = append(finalImageVolumes, volume) - } - - return finalMounts, finalVolumes, finalOverlayVolume, finalImageVolumes, nil -} - -// findMountType parses the input and extracts the type of the mount type and -// the remaining non-type tokens. -func findMountType(input string) (mountType string, tokens []string, err error) { - // Split by comma, iterate over the slice and look for - // "type=$mountType". Everything else is appended to tokens. - found := false - for _, s := range strings.Split(input, ",") { - kv := strings.Split(s, "=") - if found || !(len(kv) == 2 && kv[0] == "type") { - tokens = append(tokens, s) - continue - } - mountType = kv[1] - found = true - } - if !found { - err = errInvalidSyntax - } - return -} - -// getMounts takes user-provided input from the --mount flag and creates OCI -// spec mounts and Libpod named volumes. -// podman run --mount type=bind,src=/etc/resolv.conf,target=/etc/resolv.conf ... -// podman run --mount type=tmpfs,target=/dev/shm ... -// podman run --mount type=volume,source=test-volume, ... -func getMounts(mountFlag []string) (map[string]spec.Mount, map[string]*specgen.NamedVolume, map[string]*specgen.ImageVolume, error) { - finalMounts := make(map[string]spec.Mount) - finalNamedVolumes := make(map[string]*specgen.NamedVolume) - finalImageVolumes := make(map[string]*specgen.ImageVolume) - - for _, mount := range mountFlag { - // TODO: Docker defaults to "volume" if no mount type is specified. - mountType, tokens, err := findMountType(mount) - if err != nil { - return nil, nil, nil, err - } - switch mountType { - case define.TypeBind: - mount, err := getBindMount(tokens) - if err != nil { - return nil, nil, nil, err - } - if _, ok := finalMounts[mount.Destination]; ok { - return nil, nil, nil, errors.Wrapf(errDuplicateDest, mount.Destination) - } - finalMounts[mount.Destination] = mount - case define.TypeTmpfs: - mount, err := getTmpfsMount(tokens) - if err != nil { - return nil, nil, nil, err - } - if _, ok := finalMounts[mount.Destination]; ok { - return nil, nil, nil, errors.Wrapf(errDuplicateDest, mount.Destination) - } - finalMounts[mount.Destination] = mount - case define.TypeDevpts: - mount, err := getDevptsMount(tokens) - if err != nil { - return nil, nil, nil, err - } - if _, ok := finalMounts[mount.Destination]; ok { - return nil, nil, nil, errors.Wrapf(errDuplicateDest, mount.Destination) - } - finalMounts[mount.Destination] = mount - case "image": - volume, err := getImageVolume(tokens) - if err != nil { - return nil, nil, nil, err - } - if _, ok := finalImageVolumes[volume.Destination]; ok { - return nil, nil, nil, errors.Wrapf(errDuplicateDest, volume.Destination) - } - finalImageVolumes[volume.Destination] = volume - case "volume": - volume, err := getNamedVolume(tokens) - if err != nil { - return nil, nil, nil, err - } - if _, ok := finalNamedVolumes[volume.Dest]; ok { - return nil, nil, nil, errors.Wrapf(errDuplicateDest, volume.Dest) - } - finalNamedVolumes[volume.Dest] = volume - default: - return nil, nil, nil, errors.Errorf("invalid filesystem type %q", mountType) - } - } - - return finalMounts, finalNamedVolumes, finalImageVolumes, nil -} - -// Parse a single bind mount entry from the --mount flag. -func getBindMount(args []string) (spec.Mount, error) { - newMount := spec.Mount{ - Type: define.TypeBind, - } - - var setSource, setDest, setRORW, setSuid, setDev, setExec, setRelabel bool - - for _, val := range args { - kv := strings.SplitN(val, "=", 2) - switch kv[0] { - case "bind-nonrecursive": - newMount.Options = append(newMount.Options, "bind") - case "readonly", "ro", "rw": - if setRORW { - return newMount, errors.Wrapf(optionArgError, "cannot pass 'readonly', 'ro', or 'rw' options more than once") - } - setRORW = true - // Can be formatted as one of: - // readonly - // readonly=[true|false] - // ro - // ro=[true|false] - // rw - // rw=[true|false] - if kv[0] == "readonly" { - kv[0] = "ro" - } - switch len(kv) { - case 1: - newMount.Options = append(newMount.Options, kv[0]) - case 2: - switch strings.ToLower(kv[1]) { - case "true": - newMount.Options = append(newMount.Options, kv[0]) - case "false": - // Set the opposite only for rw - // ro's opposite is the default - if kv[0] == "rw" { - newMount.Options = append(newMount.Options, "ro") - } - default: - return newMount, errors.Wrapf(optionArgError, "'readonly', 'ro', or 'rw' must be set to true or false, instead received %q", kv[1]) - } - default: - return newMount, errors.Wrapf(optionArgError, "badly formatted option %q", val) - } - case "nosuid", "suid": - if setSuid { - return newMount, errors.Wrapf(optionArgError, "cannot pass 'nosuid' and 'suid' options more than once") - } - setSuid = true - newMount.Options = append(newMount.Options, kv[0]) - case "nodev", "dev": - if setDev { - return newMount, errors.Wrapf(optionArgError, "cannot pass 'nodev' and 'dev' options more than once") - } - setDev = true - newMount.Options = append(newMount.Options, kv[0]) - case "noexec", "exec": - if setExec { - return newMount, errors.Wrapf(optionArgError, "cannot pass 'noexec' and 'exec' options more than once") - } - setExec = true - newMount.Options = append(newMount.Options, kv[0]) - case "shared", "rshared", "private", "rprivate", "slave", "rslave", "unbindable", "runbindable", "Z", "z": - newMount.Options = append(newMount.Options, kv[0]) - case "bind-propagation": - if len(kv) == 1 { - return newMount, errors.Wrapf(optionArgError, kv[0]) - } - newMount.Options = append(newMount.Options, kv[1]) - case "src", "source": - if len(kv) == 1 { - return newMount, errors.Wrapf(optionArgError, kv[0]) - } - if len(kv[1]) == 0 { - return newMount, errors.Wrapf(optionArgError, "host directory cannot be empty") - } - newMount.Source = kv[1] - setSource = true - case "target", "dst", "destination": - if len(kv) == 1 { - return newMount, errors.Wrapf(optionArgError, kv[0]) - } - if err := parse.ValidateVolumeCtrDir(kv[1]); err != nil { - return newMount, err - } - newMount.Destination = filepath.Clean(kv[1]) - setDest = true - case "relabel": - if setRelabel { - return newMount, errors.Wrapf(optionArgError, "cannot pass 'relabel' option more than once") - } - setRelabel = true - if len(kv) != 2 { - return newMount, errors.Wrapf(util.ErrBadMntOption, "%s mount option must be 'private' or 'shared'", kv[0]) - } - switch kv[1] { - case "private": - newMount.Options = append(newMount.Options, "Z") - case "shared": - newMount.Options = append(newMount.Options, "z") - default: - return newMount, errors.Wrapf(util.ErrBadMntOption, "%s mount option must be 'private' or 'shared'", kv[0]) - } - case "consistency": - // Often used on MACs and mistakenly on Linux platforms. - // Since Docker ignores this option so shall we. - continue - default: - return newMount, errors.Wrapf(util.ErrBadMntOption, kv[0]) - } - } - - if !setDest { - return newMount, noDestError - } - - if !setSource { - newMount.Source = newMount.Destination - } - - options, err := parse.ValidateVolumeOpts(newMount.Options) - if err != nil { - return newMount, err - } - newMount.Options = options - return newMount, nil -} - -// Parse a single tmpfs mount entry from the --mount flag -func getTmpfsMount(args []string) (spec.Mount, error) { - newMount := spec.Mount{ - Type: define.TypeTmpfs, - Source: define.TypeTmpfs, - } - - var setDest, setRORW, setSuid, setDev, setExec, setTmpcopyup bool - - for _, val := range args { - kv := strings.SplitN(val, "=", 2) - switch kv[0] { - case "tmpcopyup", "notmpcopyup": - if setTmpcopyup { - return newMount, errors.Wrapf(optionArgError, "cannot pass 'tmpcopyup' and 'notmpcopyup' options more than once") - } - setTmpcopyup = true - newMount.Options = append(newMount.Options, kv[0]) - case "ro", "rw": - if setRORW { - return newMount, errors.Wrapf(optionArgError, "cannot pass 'ro' and 'rw' options more than once") - } - setRORW = true - newMount.Options = append(newMount.Options, kv[0]) - case "nosuid", "suid": - if setSuid { - return newMount, errors.Wrapf(optionArgError, "cannot pass 'nosuid' and 'suid' options more than once") - } - setSuid = true - newMount.Options = append(newMount.Options, kv[0]) - case "nodev", "dev": - if setDev { - return newMount, errors.Wrapf(optionArgError, "cannot pass 'nodev' and 'dev' options more than once") - } - setDev = true - newMount.Options = append(newMount.Options, kv[0]) - case "noexec", "exec": - if setExec { - return newMount, errors.Wrapf(optionArgError, "cannot pass 'noexec' and 'exec' options more than once") - } - setExec = true - newMount.Options = append(newMount.Options, kv[0]) - case "tmpfs-mode": - if len(kv) == 1 { - return newMount, errors.Wrapf(optionArgError, kv[0]) - } - newMount.Options = append(newMount.Options, fmt.Sprintf("mode=%s", kv[1])) - case "tmpfs-size": - if len(kv) == 1 { - return newMount, errors.Wrapf(optionArgError, kv[0]) - } - newMount.Options = append(newMount.Options, fmt.Sprintf("size=%s", kv[1])) - case "src", "source": - return newMount, errors.Errorf("source is not supported with tmpfs mounts") - case "target", "dst", "destination": - if len(kv) == 1 { - return newMount, errors.Wrapf(optionArgError, kv[0]) - } - if err := parse.ValidateVolumeCtrDir(kv[1]); err != nil { - return newMount, err - } - newMount.Destination = filepath.Clean(kv[1]) - setDest = true - case "consistency": - // Often used on MACs and mistakenly on Linux platforms. - // Since Docker ignores this option so shall we. - continue - default: - return newMount, errors.Wrapf(util.ErrBadMntOption, kv[0]) - } - } - - if !setDest { - return newMount, noDestError - } - - return newMount, nil -} - -// Parse a single devpts mount entry from the --mount flag -func getDevptsMount(args []string) (spec.Mount, error) { - newMount := spec.Mount{ - Type: define.TypeDevpts, - Source: define.TypeDevpts, - } - - var setDest bool - - for _, val := range args { - kv := strings.SplitN(val, "=", 2) - switch kv[0] { - case "target", "dst", "destination": - if len(kv) == 1 { - return newMount, errors.Wrapf(optionArgError, kv[0]) - } - if err := parse.ValidateVolumeCtrDir(kv[1]); err != nil { - return newMount, err - } - newMount.Destination = filepath.Clean(kv[1]) - setDest = true - default: - return newMount, errors.Wrapf(util.ErrBadMntOption, kv[0]) - } - } - - if !setDest { - return newMount, noDestError - } - - return newMount, nil -} - -// Parse a single volume mount entry from the --mount flag. -// Note that the volume-label option for named volumes is currently NOT supported. -// TODO: add support for --volume-label -func getNamedVolume(args []string) (*specgen.NamedVolume, error) { - newVolume := new(specgen.NamedVolume) - - var setSource, setDest, setRORW, setSuid, setDev, setExec bool - - for _, val := range args { - kv := strings.SplitN(val, "=", 2) - switch kv[0] { - case "ro", "rw": - if setRORW { - return nil, errors.Wrapf(optionArgError, "cannot pass 'ro' and 'rw' options more than once") - } - setRORW = true - newVolume.Options = append(newVolume.Options, kv[0]) - case "nosuid", "suid": - if setSuid { - return nil, errors.Wrapf(optionArgError, "cannot pass 'nosuid' and 'suid' options more than once") - } - setSuid = true - newVolume.Options = append(newVolume.Options, kv[0]) - case "nodev", "dev": - if setDev { - return nil, errors.Wrapf(optionArgError, "cannot pass 'nodev' and 'dev' options more than once") - } - setDev = true - newVolume.Options = append(newVolume.Options, kv[0]) - case "noexec", "exec": - if setExec { - return nil, errors.Wrapf(optionArgError, "cannot pass 'noexec' and 'exec' options more than once") - } - setExec = true - newVolume.Options = append(newVolume.Options, kv[0]) - case "volume-label": - return nil, errors.Errorf("the --volume-label option is not presently implemented") - case "src", "source": - if len(kv) == 1 { - return nil, errors.Wrapf(optionArgError, kv[0]) - } - newVolume.Name = kv[1] - setSource = true - case "target", "dst", "destination": - if len(kv) == 1 { - return nil, errors.Wrapf(optionArgError, kv[0]) - } - if err := parse.ValidateVolumeCtrDir(kv[1]); err != nil { - return nil, err - } - newVolume.Dest = filepath.Clean(kv[1]) - setDest = true - case "consistency": - // Often used on MACs and mistakenly on Linux platforms. - // Since Docker ignores this option so shall we. - continue - default: - return nil, errors.Wrapf(util.ErrBadMntOption, kv[0]) - } - } - - if !setSource { - return nil, errors.Errorf("must set source volume") - } - if !setDest { - return nil, noDestError - } - - return newVolume, nil -} - -// Parse the arguments into an image volume. An image volume is a volume based -// on a container image. The container image is first mounted on the host and -// is then bind-mounted into the container. An ImageVolume is always mounted -// read only. -func getImageVolume(args []string) (*specgen.ImageVolume, error) { - newVolume := new(specgen.ImageVolume) - - for _, val := range args { - kv := strings.SplitN(val, "=", 2) - switch kv[0] { - case "src", "source": - if len(kv) == 1 { - return nil, errors.Wrapf(optionArgError, kv[0]) - } - newVolume.Source = kv[1] - case "target", "dst", "destination": - if len(kv) == 1 { - return nil, errors.Wrapf(optionArgError, kv[0]) - } - if err := parse.ValidateVolumeCtrDir(kv[1]); err != nil { - return nil, err - } - newVolume.Destination = filepath.Clean(kv[1]) - case "rw", "readwrite": - switch kv[1] { - case "true": - newVolume.ReadWrite = true - case "false": - // Nothing to do. RO is default. - default: - return nil, errors.Wrapf(util.ErrBadMntOption, "invalid rw value %q", kv[1]) - } - case "consistency": - // Often used on MACs and mistakenly on Linux platforms. - // Since Docker ignores this option so shall we. - continue - default: - return nil, errors.Wrapf(util.ErrBadMntOption, kv[0]) - } - } - - if len(newVolume.Source)*len(newVolume.Destination) == 0 { - return nil, errors.Errorf("must set source and destination for image volume") - } - - return newVolume, nil -} - -// GetTmpfsMounts creates spec.Mount structs for user-requested tmpfs mounts -func getTmpfsMounts(tmpfsFlag []string) (map[string]spec.Mount, error) { - m := make(map[string]spec.Mount) - for _, i := range tmpfsFlag { - // Default options if nothing passed - var options []string - spliti := strings.Split(i, ":") - destPath := spliti[0] - if err := parse.ValidateVolumeCtrDir(spliti[0]); err != nil { - return nil, err - } - if len(spliti) > 1 { - options = strings.Split(spliti[1], ",") - } - - if _, ok := m[destPath]; ok { - return nil, errors.Wrapf(errDuplicateDest, destPath) - } - - mount := spec.Mount{ - Destination: filepath.Clean(destPath), - Type: string(define.TypeTmpfs), - Options: options, - Source: string(define.TypeTmpfs), - } - m[destPath] = mount - } - return m, nil -} |