From d28e85741fedb89be48a03d4f05687e970eb71b9 Mon Sep 17 00:00:00 2001 From: cdoern Date: Wed, 14 Jul 2021 16:30:28 -0400 Subject: [PATCH] InfraContainer Rework InfraContainer should go through the same creation process as regular containers. This change was from the cmd level down, involving new container CLI opts and specgen creating functions. What now happens is that both container and pod cli options are populated in cmd and used to create a podSpecgen and a containerSpecgen. The process then goes as follows FillOutSpecGen (infra) -> MapSpec (podOpts -> infraOpts) -> PodCreate -> MakePod -> createPodOptions -> NewPod -> CompleteSpec (infra) -> MakeContainer -> NewContainer -> newContainer -> AddInfra (to pod state) Signed-off-by: cdoern --- cmd/podman/common/create.go | 1416 +++++++++-------- cmd/podman/common/create_opts.go | 120 +- cmd/podman/common/netflags.go | 102 +- cmd/podman/containers/create.go | 211 ++- cmd/podman/containers/prune.go | 3 +- cmd/podman/containers/restore.go | 3 +- cmd/podman/containers/run.go | 19 +- cmd/podman/images/prune.go | 3 +- cmd/podman/networks/prune.go | 3 +- cmd/podman/pods/create.go | 155 +- cmd/podman/pods/rm.go | 3 +- cmd/podman/pods/start.go | 3 +- cmd/podman/pods/stop.go | 3 +- docs/source/markdown/podman-pod-create.1.md | 21 +- libpod/container_internal.go | 3 +- libpod/kube.go | 4 +- libpod/networking_linux.go | 1 - libpod/options.go | 480 +----- libpod/pod.go | 169 +- libpod/pod_api.go | 53 +- libpod/pod_internal.go | 2 +- libpod/runtime_ctr.go | 26 +- libpod/runtime_pod_infra_linux.go | 284 ---- libpod/runtime_pod_linux.go | 57 +- pkg/api/handlers/compat/containers_create.go | 3 +- pkg/api/handlers/libpod/containers_create.go | 7 +- pkg/api/handlers/libpod/pods.go | 65 +- pkg/api/handlers/types/types.go | 4 +- pkg/bindings/pods/pods.go | 12 +- pkg/bindings/test/pods_test.go | 7 +- pkg/domain/entities/engine_container.go | 2 +- pkg/domain/entities/pods.go | 183 ++- pkg/domain/entities/types.go | 38 +- pkg/domain/infra/abi/containers.go | 12 +- pkg/domain/infra/abi/generate.go | 4 +- pkg/domain/infra/abi/play.go | 250 +-- pkg/domain/infra/abi/pods.go | 9 +- pkg/domain/infra/tunnel/events.go | 1 + pkg/domain/infra/tunnel/pods.go | 7 +- pkg/specgen/generate/container_create.go | 63 +- pkg/specgen/generate/kube/kube.go | 32 +- pkg/specgen/generate/namespaces.go | 2 +- pkg/specgen/generate/oci.go | 3 +- pkg/specgen/generate/pod_create.go | 214 ++- pkg/specgen/podspecgen.go | 3 +- .../common => pkg/specgenutil}/createparse.go | 11 +- .../common => pkg/specgenutil}/ports.go | 2 +- .../common => pkg/specgenutil}/specgen.go | 104 +- .../podman/common => pkg/specgenutil}/util.go | 2 +- .../common => pkg/specgenutil}/volumes.go | 2 +- test/e2e/pod_create_test.go | 12 +- 51 files changed, 1925 insertions(+), 2273 deletions(-) delete mode 100644 libpod/runtime_pod_infra_linux.go rename {cmd/podman/common => pkg/specgenutil}/createparse.go (69%) rename {cmd/podman/common => pkg/specgenutil}/ports.go (97%) rename {cmd/podman/common => pkg/specgenutil}/specgen.go (91%) rename {cmd/podman/common => pkg/specgenutil}/util.go (99%) rename {cmd/podman/common => pkg/specgenutil}/volumes.go (99%) diff --git a/cmd/podman/common/create.go b/cmd/podman/common/create.go index 401cf2e098..325c1dc692 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: `[]`, 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: [:])", + ) + _ = 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: [:])", - ) - _ = 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 c94f46cf26..2f592cba7b 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/netflags.go b/cmd/podman/common/netflags.go index aa8714b50e..d11f3c9d29 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/containers/create.go b/cmd/podman/containers/create.go index 7583a024ed..8b27de53ed 100644 --- a/cmd/podman/containers/create.go +++ b/cmd/podman/containers/create.go @@ -17,6 +17,7 @@ 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/containers/podman/v3/pkg/util" "github.com/pkg/errors" "github.com/spf13/cobra" @@ -52,8 +53,8 @@ var ( ) var ( - cliVals common.ContainerCLIOpts InitContainerType string + cliVals entities.ContainerCreateOptions ) func createFlags(cmd *cobra.Command) { @@ -67,13 +68,18 @@ func createFlags(cmd *cobra.Command) { ) flags.SetInterspersed(false) - common.DefineCreateFlags(cmd, &cliVals) + common.DefineCreateFlags(cmd, &cliVals, false) common.DefineNetFlags(cmd) flags.SetNormalizeFunc(utils.AliasFlags) if registry.IsRemote() { - _ = flags.MarkHidden("conmon-pidfile") + if cliVals.IsInfra { + _ = flags.MarkHidden("infra-conmon-pidfile") + } else { + _ = flags.MarkHidden("conmon-pidfile") + } + _ = flags.MarkHidden("pidfile") } @@ -97,7 +103,8 @@ func create(cmd *cobra.Command, args []string) error { var ( err error ) - cliVals.Net, err = common.NetFlagsToNetOptions(cmd, cliVals.Pod == "" && cliVals.PodIDFile == "") + flags := cmd.Flags() + cliVals.Net, err = common.NetFlagsToNetOptions(nil, *flags, cliVals.Pod == "" && cliVals.PodIDFile == "") if err != nil { return err } @@ -113,22 +120,22 @@ func create(cmd *cobra.Command, args []string) error { cliVals.InitContainerType = initctr } - if err := createInit(cmd); err != nil { + cliVals, err = CreateInit(cmd, cliVals, false) + if err != nil { return err } - imageName := args[0] rawImageName := "" if !cliVals.RootFS { rawImageName = args[0] - name, err := pullImage(args[0]) + name, err := PullImage(args[0], cliVals) if err != nil { return err } imageName = name } s := specgen.NewSpecGenerator(imageName, cliVals.RootFS) - if err := common.FillOutSpecGen(s, &cliVals, args); err != nil { + if err := specgenutil.FillOutSpecGen(s, &cliVals, args); err != nil { return err } s.RawImageName = rawImageName @@ -169,100 +176,101 @@ func replaceContainer(name string) error { return removeContainers([]string{name}, rmOptions, false) } -func createInit(c *cobra.Command) error { - cliVals.StorageOpt = registry.PodmanConfig().StorageOpts - - if c.Flag("shm-size").Changed { - cliVals.ShmSize = c.Flag("shm-size").Value.String() +func CreateInit(c *cobra.Command, vals entities.ContainerCreateOptions, isInfra bool) (entities.ContainerCreateOptions, error) { + vals.UserNS = c.Flag("userns").Value.String() + // if user did not modify --userns flag and did turn on + // uid/gid mappings, set userns flag to "private" + if !c.Flag("userns").Changed && vals.UserNS == "host" { + if len(vals.UIDMap) > 0 || + len(vals.GIDMap) > 0 || + vals.SubUIDName != "" || + vals.SubGIDName != "" { + vals.UserNS = "private" + } } - if (c.Flag("dns").Changed || c.Flag("dns-opt").Changed || c.Flag("dns-search").Changed) && (cliVals.Net.Network.NSMode == specgen.NoNetwork || cliVals.Net.Network.IsContainer()) { - return errors.Errorf("conflicting options: dns and the network mode.") - } + if !isInfra { + if c.Flag("shm-size").Changed { + vals.ShmSize = c.Flag("shm-size").Value.String() + } + if c.Flag("cpu-period").Changed && c.Flag("cpus").Changed { + return vals, errors.Errorf("--cpu-period and --cpus cannot be set together") + } + if c.Flag("cpu-quota").Changed && c.Flag("cpus").Changed { + return vals, errors.Errorf("--cpu-quota and --cpus cannot be set together") + } + vals.IPC = c.Flag("ipc").Value.String() + vals.UTS = c.Flag("uts").Value.String() + vals.PID = c.Flag("pid").Value.String() + vals.CgroupNS = c.Flag("cgroupns").Value.String() + + if c.Flags().Changed("group-add") { + groups := []string{} + for _, g := range cliVals.GroupAdd { + if g == "keep-groups" { + if len(cliVals.GroupAdd) > 1 { + return vals, errors.New("the '--group-add keep-groups' option is not allowed with any other --group-add options") + } + if registry.IsRemote() { + return vals, errors.New("the '--group-add keep-groups' option is not supported in remote mode") + } + vals.Annotation = append(vals.Annotation, "run.oci.keep_original_groups=1") + } else { + groups = append(groups, g) + } + } + vals.GroupAdd = groups + } - if c.Flag("cpu-period").Changed && c.Flag("cpus").Changed { - return errors.Errorf("--cpu-period and --cpus cannot be set together") - } - if c.Flag("cpu-quota").Changed && c.Flag("cpus").Changed { - return errors.Errorf("--cpu-quota and --cpus cannot be set together") + if c.Flags().Changed("pids-limit") { + val := c.Flag("pids-limit").Value.String() + pidsLimit, err := strconv.ParseInt(val, 10, 32) + if err != nil { + return vals, err + } + vals.PIDsLimit = &pidsLimit + } + if c.Flags().Changed("env") { + env, err := c.Flags().GetStringArray("env") + if err != nil { + return vals, errors.Wrapf(err, "retrieve env flag") + } + vals.Env = env + } + if c.Flag("cgroups").Changed && vals.CGroupsMode == "split" && registry.IsRemote() { + return vals, errors.Errorf("the option --cgroups=%q is not supported in remote mode", vals.CGroupsMode) + } + + if c.Flag("pod").Changed && !strings.HasPrefix(c.Flag("pod").Value.String(), "new:") && c.Flag("userns").Changed { + return vals, errors.Errorf("--userns and --pod cannot be set together") + } } - if c.Flag("pod").Changed && !strings.HasPrefix(c.Flag("pod").Value.String(), "new:") && c.Flag("userns").Changed { - return errors.Errorf("--userns and --pod cannot be set together") + if (c.Flag("dns").Changed || c.Flag("dns-opt").Changed || c.Flag("dns-search").Changed) && vals.Net != nil && (vals.Net.Network.NSMode == specgen.NoNetwork || vals.Net.Network.IsContainer()) { + return vals, errors.Errorf("conflicting options: dns and the network mode: " + string(vals.Net.Network.NSMode)) } - noHosts, err := c.Flags().GetBool("no-hosts") if err != nil { - return err + return vals, err } if noHosts && c.Flag("add-host").Changed { - return errors.Errorf("--no-hosts and --add-host cannot be set together") - } - cliVals.UserNS = c.Flag("userns").Value.String() - // if user did not modify --userns flag and did turn on - // uid/gid mappings, set userns flag to "private" - if !c.Flag("userns").Changed && cliVals.UserNS == "host" { - if len(cliVals.UIDMap) > 0 || - len(cliVals.GIDMap) > 0 || - cliVals.SubUIDName != "" || - cliVals.SubGIDName != "" { - cliVals.UserNS = "private" - } + return vals, errors.Errorf("--no-hosts and --add-host cannot be set together") } - cliVals.IPC = c.Flag("ipc").Value.String() - cliVals.UTS = c.Flag("uts").Value.String() - cliVals.PID = c.Flag("pid").Value.String() - cliVals.CgroupNS = c.Flag("cgroupns").Value.String() - if c.Flag("entrypoint").Changed { + if !isInfra && c.Flag("entrypoint").Changed { val := c.Flag("entrypoint").Value.String() - cliVals.Entrypoint = &val - } - - if c.Flags().Changed("group-add") { - groups := []string{} - for _, g := range cliVals.GroupAdd { - if g == "keep-groups" { - if len(cliVals.GroupAdd) > 1 { - return errors.New("the '--group-add keep-groups' option is not allowed with any other --group-add options") - } - if registry.IsRemote() { - return errors.New("the '--group-add keep-groups' option is not supported in remote mode") - } - cliVals.Annotation = append(cliVals.Annotation, "run.oci.keep_original_groups=1") - } else { - groups = append(groups, g) - } - } - cliVals.GroupAdd = groups - } + vals.Entrypoint = &val + } else if isInfra && c.Flag("infra-command").Changed { - if c.Flags().Changed("pids-limit") { - val := c.Flag("pids-limit").Value.String() - pidsLimit, err := strconv.ParseInt(val, 10, 32) - if err != nil { - return err - } - cliVals.PIDsLimit = &pidsLimit - } - if c.Flags().Changed("env") { - env, err := c.Flags().GetStringArray("env") - if err != nil { - return errors.Wrapf(err, "retrieve env flag") - } - cliVals.Env = env - } - if c.Flag("cgroups").Changed && cliVals.CGroupsMode == "split" && registry.IsRemote() { - return errors.Errorf("the option --cgroups=%q is not supported in remote mode", cliVals.CGroupsMode) } // Docker-compatibility: the "-h" flag for run/create is reserved for // the hostname (see https://github.com/containers/podman/issues/1367). - return nil + return vals, nil } -func pullImage(imageName string) (string, error) { - pullPolicy, err := config.ParsePullPolicy(cliVals.Pull) +func PullImage(imageName string, cliVals entities.ContainerCreateOptions) (string, error) { + pullPolicy, err := config.ValidatePullPolicy(cliVals.Pull) if err != nil { return "", err } @@ -316,11 +324,14 @@ func createPodIfNecessary(s *specgen.SpecGenerator, netOpts *entities.NetOptions return nil, errors.Errorf("new pod name must be at least one character") } - userns, err := specgen.ParseUserNamespace(cliVals.UserNS) - if err != nil { - return nil, err + var err error + uns := specgen.Namespace{NSMode: specgen.Default} + if cliVals.UserNS != "" { + uns, err = specgen.ParseNamespace(cliVals.UserNS) + if err != nil { + return nil, err + } } - createOptions := entities.PodCreateOptions{ Name: podName, Infra: true, @@ -330,12 +341,36 @@ func createPodIfNecessary(s *specgen.SpecGenerator, netOpts *entities.NetOptions Cpus: cliVals.CPUS, CpusetCpus: cliVals.CPUSetCPUs, Pid: cliVals.PID, - Userns: userns, + Userns: uns, } // Unset config values we passed to the pod to prevent them being used twice for the container and pod. s.ContainerBasicConfig.Hostname = "" s.ContainerNetworkConfig = specgen.ContainerNetworkConfig{} s.Pod = podName - return registry.ContainerEngine().PodCreate(context.Background(), createOptions) + podSpec := entities.PodSpec{} + podGen := specgen.NewPodSpecGenerator() + podSpec.PodSpecGen = *podGen + podGen, err = entities.ToPodSpecGen(*&podSpec.PodSpecGen, &createOptions) + if err != nil { + return nil, err + } + + infraOpts := entities.ContainerCreateOptions{ImageVolume: "bind", Net: netOpts, Quiet: true} + rawImageName := config.DefaultInfraImage + name, err := PullImage(rawImageName, infraOpts) + if err != nil { + fmt.Println(err) + } + imageName := name + podGen.InfraImage = imageName + podGen.InfraContainerSpec = specgen.NewSpecGenerator(imageName, false) + podGen.InfraContainerSpec.RawImageName = rawImageName + podGen.InfraContainerSpec.NetworkOptions = podGen.NetworkOptions + err = specgenutil.FillOutSpecGen(podGen.InfraContainerSpec, &infraOpts, []string{}) + if err != nil { + return nil, err + } + podSpec.PodSpecGen = *podGen + return registry.ContainerEngine().PodCreate(context.Background(), podSpec) } diff --git a/cmd/podman/containers/prune.go b/cmd/podman/containers/prune.go index e55bd8a536..e13b9e7f62 100644 --- a/cmd/podman/containers/prune.go +++ b/cmd/podman/containers/prune.go @@ -13,6 +13,7 @@ import ( "github.com/containers/podman/v3/cmd/podman/utils" "github.com/containers/podman/v3/cmd/podman/validate" "github.com/containers/podman/v3/pkg/domain/entities" + "github.com/containers/podman/v3/pkg/specgenutil" "github.com/spf13/cobra" ) @@ -63,7 +64,7 @@ func prune(cmd *cobra.Command, args []string) error { } } - pruneOptions.Filters, err = common.ParseFilters(filter) + pruneOptions.Filters, err = specgenutil.ParseFilters(filter) if err != nil { return err } diff --git a/cmd/podman/containers/restore.go b/cmd/podman/containers/restore.go index 3b6f74efa0..05214f32c4 100644 --- a/cmd/podman/containers/restore.go +++ b/cmd/podman/containers/restore.go @@ -11,6 +11,7 @@ import ( "github.com/containers/podman/v3/cmd/podman/validate" "github.com/containers/podman/v3/pkg/domain/entities" "github.com/containers/podman/v3/pkg/rootless" + "github.com/containers/podman/v3/pkg/specgenutil" "github.com/pkg/errors" "github.com/spf13/cobra" ) @@ -106,7 +107,7 @@ func restore(cmd *cobra.Command, args []string) error { return err } if len(inputPorts) > 0 { - restoreOptions.PublishPorts, err = common.CreatePortBindings(inputPorts) + restoreOptions.PublishPorts, err = specgenutil.CreatePortBindings(inputPorts) if err != nil { return err } diff --git a/cmd/podman/containers/run.go b/cmd/podman/containers/run.go index 830d1de7f2..d149618296 100644 --- a/cmd/podman/containers/run.go +++ b/cmd/podman/containers/run.go @@ -14,6 +14,7 @@ import ( "github.com/containers/podman/v3/pkg/errorhandling" "github.com/containers/podman/v3/pkg/rootless" "github.com/containers/podman/v3/pkg/specgen" + "github.com/containers/podman/v3/pkg/specgenutil" "github.com/pkg/errors" "github.com/sirupsen/logrus" "github.com/spf13/cobra" @@ -60,7 +61,7 @@ func runFlags(cmd *cobra.Command) { flags := cmd.Flags() flags.SetInterspersed(false) - common.DefineCreateFlags(cmd, &cliVals) + common.DefineCreateFlags(cmd, &cliVals, false) common.DefineNetFlags(cmd) flags.SetNormalizeFunc(utils.AliasFlags) @@ -106,10 +107,6 @@ func init() { func run(cmd *cobra.Command, args []string) error { var err error - cliVals.Net, err = common.NetFlagsToNetOptions(cmd, cliVals.Pod == "" && cliVals.PodIDFile == "") - if err != nil { - return err - } // TODO: Breaking change should be made fatal in next major Release if cliVals.TTY && cliVals.Interactive && !terminal.IsTerminal(int(os.Stdin.Fd())) { @@ -122,11 +119,17 @@ func run(cmd *cobra.Command, args []string) error { } } + flags := cmd.Flags() + cliVals.Net, err = common.NetFlagsToNetOptions(nil, *flags, cliVals.Pod == "" && cliVals.PodIDFile == "") + if err != nil { + return err + } runOpts.CIDFile = cliVals.CIDFile runOpts.Rm = cliVals.Rm - if err := createInit(cmd); err != nil { + if cliVals, err = CreateInit(cmd, cliVals, false); err != nil { return err } + for fd := 3; fd < int(3+runOpts.PreserveFDs); fd++ { if !rootless.IsFdInherited(fd) { return errors.Errorf("file descriptor %d is not available - the preserve-fds option requires that file descriptors must be passed", fd) @@ -137,7 +140,7 @@ func run(cmd *cobra.Command, args []string) error { rawImageName := "" if !cliVals.RootFS { rawImageName = args[0] - name, err := pullImage(args[0]) + name, err := PullImage(args[0], cliVals) if err != nil { return err } @@ -178,7 +181,7 @@ func run(cmd *cobra.Command, args []string) error { } cliVals.PreserveFDs = runOpts.PreserveFDs s := specgen.NewSpecGenerator(imageName, cliVals.RootFS) - if err := common.FillOutSpecGen(s, &cliVals, args); err != nil { + if err := specgenutil.FillOutSpecGen(s, &cliVals, args); err != nil { return err } s.RawImageName = rawImageName diff --git a/cmd/podman/images/prune.go b/cmd/podman/images/prune.go index 6ecf4f2aab..8a484495a9 100644 --- a/cmd/podman/images/prune.go +++ b/cmd/podman/images/prune.go @@ -12,6 +12,7 @@ import ( "github.com/containers/podman/v3/cmd/podman/utils" "github.com/containers/podman/v3/cmd/podman/validate" "github.com/containers/podman/v3/pkg/domain/entities" + "github.com/containers/podman/v3/pkg/specgenutil" "github.com/spf13/cobra" ) @@ -59,7 +60,7 @@ func prune(cmd *cobra.Command, args []string) error { return nil } } - filterMap, err := common.ParseFilters(filter) + filterMap, err := specgenutil.ParseFilters(filter) if err != nil { return err } diff --git a/cmd/podman/networks/prune.go b/cmd/podman/networks/prune.go index e6b779ded9..311d098cd2 100644 --- a/cmd/podman/networks/prune.go +++ b/cmd/podman/networks/prune.go @@ -11,6 +11,7 @@ import ( "github.com/containers/podman/v3/cmd/podman/utils" "github.com/containers/podman/v3/cmd/podman/validate" "github.com/containers/podman/v3/pkg/domain/entities" + "github.com/containers/podman/v3/pkg/specgenutil" "github.com/spf13/cobra" "github.com/spf13/pflag" ) @@ -67,7 +68,7 @@ func networkPrune(cmd *cobra.Command, _ []string) error { return nil } } - networkPruneOptions.Filters, err = common.ParseFilters(filter) + networkPruneOptions.Filters, err = specgenutil.ParseFilters(filter) if err != nil { return err } diff --git a/cmd/podman/pods/create.go b/cmd/podman/pods/create.go index bf5b9e350c..b3f84dcd85 100644 --- a/cmd/podman/pods/create.go +++ b/cmd/podman/pods/create.go @@ -11,14 +11,17 @@ import ( "strings" "github.com/containers/common/pkg/completion" + "github.com/containers/common/pkg/config" "github.com/containers/common/pkg/sysinfo" "github.com/containers/podman/v3/cmd/podman/common" + "github.com/containers/podman/v3/cmd/podman/containers" "github.com/containers/podman/v3/cmd/podman/parse" "github.com/containers/podman/v3/cmd/podman/registry" "github.com/containers/podman/v3/cmd/podman/validate" "github.com/containers/podman/v3/pkg/domain/entities" "github.com/containers/podman/v3/pkg/errorhandling" "github.com/containers/podman/v3/pkg/specgen" + "github.com/containers/podman/v3/pkg/specgenutil" "github.com/containers/podman/v3/pkg/util" "github.com/docker/docker/pkg/parsers" "github.com/pkg/errors" @@ -44,11 +47,11 @@ var ( var ( createOptions entities.PodCreateOptions + infraOptions entities.ContainerCreateOptions labels, labelFile []string podIDFile string replace bool share string - userns string ) func init() { @@ -58,62 +61,19 @@ func init() { }) flags := createCommand.Flags() flags.SetInterspersed(false) - + infraOptions.IsInfra = true + common.DefineCreateFlags(createCommand, &infraOptions, true) common.DefineNetFlags(createCommand) - cpusetflagName := "cpuset-cpus" - flags.StringVar(&createOptions.CpusetCpus, cpusetflagName, "", "CPUs in which to allow execution") - _ = createCommand.RegisterFlagCompletionFunc(cpusetflagName, completion.AutocompleteDefault) - - cpusflagName := "cpus" - flags.Float64Var(&createOptions.Cpus, cpusflagName, 0.000, "set amount of CPUs for the pod") - _ = createCommand.RegisterFlagCompletionFunc(cpusflagName, completion.AutocompleteDefault) - - cgroupParentflagName := "cgroup-parent" - flags.StringVar(&createOptions.CGroupParent, cgroupParentflagName, "", "Set parent cgroup for the pod") - _ = createCommand.RegisterFlagCompletionFunc(cgroupParentflagName, completion.AutocompleteDefault) - - usernsFlagName := "userns" - flags.StringVar(&userns, usernsFlagName, os.Getenv("PODMAN_USERNS"), "User namespace to use") - _ = createCommand.RegisterFlagCompletionFunc(usernsFlagName, common.AutocompleteUserNamespace) - flags.BoolVar(&createOptions.Infra, "infra", true, "Create an infra container associated with the pod to share namespaces with") - infraConmonPidfileFlagName := "infra-conmon-pidfile" - flags.StringVar(&createOptions.InfraConmonPidFile, infraConmonPidfileFlagName, "", "Path to the file that will receive the POD of the infra container's conmon") - _ = createCommand.RegisterFlagCompletionFunc(infraConmonPidfileFlagName, completion.AutocompleteDefault) - - infraImageFlagName := "infra-image" - flags.String(infraImageFlagName, containerConfig.Engine.InfraImage, "The image of the infra container to associate with the pod") - _ = createCommand.RegisterFlagCompletionFunc(infraImageFlagName, common.AutocompleteImages) - - infraCommandFlagName := "infra-command" - flags.String(infraCommandFlagName, containerConfig.Engine.InfraCommand, "The command to run on the infra container when the pod is started") - _ = createCommand.RegisterFlagCompletionFunc(infraCommandFlagName, completion.AutocompleteNone) - - infraNameFlagName := "infra-name" - flags.StringVarP(&createOptions.InfraName, infraNameFlagName, "", "", "The name used as infra container name") - _ = createCommand.RegisterFlagCompletionFunc(infraNameFlagName, completion.AutocompleteNone) - - labelFileFlagName := "label-file" - flags.StringSliceVar(&labelFile, labelFileFlagName, []string{}, "Read in a line delimited file of labels") - _ = createCommand.RegisterFlagCompletionFunc(labelFileFlagName, completion.AutocompleteDefault) - - labelFlagName := "label" - flags.StringSliceVarP(&labels, labelFlagName, "l", []string{}, "Set metadata on pod (default [])") - _ = createCommand.RegisterFlagCompletionFunc(labelFlagName, completion.AutocompleteNone) - nameFlagName := "name" flags.StringVarP(&createOptions.Name, nameFlagName, "n", "", "Assign a name to the pod") _ = createCommand.RegisterFlagCompletionFunc(nameFlagName, completion.AutocompleteNone) - hostnameFlagName := "hostname" - flags.StringVarP(&createOptions.Hostname, hostnameFlagName, "", "", "Set a hostname to the pod") - _ = createCommand.RegisterFlagCompletionFunc(hostnameFlagName, completion.AutocompleteNone) - - pidFlagName := "pid" - flags.StringVar(&createOptions.Pid, pidFlagName, "", "PID namespace to use") - _ = createCommand.RegisterFlagCompletionFunc(pidFlagName, common.AutocompleteNamespace) + infraImageFlagName := "infra-image" + flags.String(infraImageFlagName, containerConfig.Engine.InfraImage, "The image of the infra container to associate with the pod") + _ = createCommand.RegisterFlagCompletionFunc(infraImageFlagName, common.AutocompleteImages) podIDFileFlagName := "pod-id-file" flags.StringVar(&podIDFile, podIDFileFlagName, "", "Write the pod ID to the file") @@ -137,25 +97,30 @@ func aliasNetworkFlag(_ *pflag.FlagSet, name string) pflag.NormalizedName { func create(cmd *cobra.Command, args []string) error { var ( - err error - podIDFD *os.File + err error + podIDFD *os.File + imageName string + rawImageName string ) + labelFile = infraOptions.LabelFile + labels = infraOptions.Label createOptions.Labels, err = parse.GetAllLabels(labelFile, labels) if err != nil { return errors.Wrapf(err, "unable to process labels") } + imageName = config.DefaultInfraImage + img := imageName if !createOptions.Infra { - logrus.Debugf("Not creating an infra container") - if cmd.Flag("infra-conmon-pidfile").Changed { - return errors.New("cannot set infra-conmon-pid without an infra container") + if cmd.Flag("no-hosts").Changed { + return fmt.Errorf("cannot specify no-hosts without an infra container") } - if cmd.Flag("infra-command").Changed { - return errors.New("cannot set infra-command without an infra container") - } - if cmd.Flag("infra-image").Changed { - return errors.New("cannot set infra-image without an infra container") + flags := cmd.Flags() + createOptions.Net, err = common.NetFlagsToNetOptions(nil, *flags, false) + if err != nil { + return err } + logrus.Debugf("Not creating an infra container") createOptions.InfraImage = "" if createOptions.InfraName != "" { return errors.New("cannot set infra-name without an infra container") @@ -166,28 +131,43 @@ func create(cmd *cobra.Command, args []string) error { } createOptions.Share = nil } else { + // reassign certain optios for lbpod api, these need to be populated in spec + createOptions.InfraConmonPidFile = infraOptions.ConmonPIDFile + createOptions.InfraName = infraOptions.Name + createOptions.Hostname = infraOptions.Hostname + createOptions.Cpus = infraOptions.CPUS + createOptions.CpusetCpus = infraOptions.CPUSetCPUs + createOptions.Pid = infraOptions.PID + flags := cmd.Flags() + infraOptions.Net, err = common.NetFlagsToNetOptions(nil, *flags, false) + if err != nil { + return err + } + infraOptions, err = containers.CreateInit(cmd, infraOptions, true) + if err != nil { + return err + } + createOptions.Net = infraOptions.Net createOptions.Share = strings.Split(share, ",") if cmd.Flag("infra-command").Changed { // Only send content to server side if user changed defaults - createOptions.InfraCommand, err = cmd.Flags().GetString("infra-command") + cmdIn, err := cmd.Flags().GetString("infra-command") + infraOptions.Entrypoint = &cmdIn + createOptions.InfraCommand = cmdIn if err != nil { return err } } if cmd.Flag("infra-image").Changed { // Only send content to server side if user changed defaults - createOptions.InfraImage, err = cmd.Flags().GetString("infra-image") + img, err = cmd.Flags().GetString("infra-image") + imageName = img if err != nil { return err } } } - createOptions.Userns, err = specgen.ParseUserNamespace(userns) - if err != nil { - return err - } - if cmd.Flag("pod-id-file").Changed { podIDFD, err = util.OpenExclusiveFile(podIDFile) if err != nil && os.IsExist(err) { @@ -200,13 +180,6 @@ func create(cmd *cobra.Command, args []string) error { defer errorhandling.SyncQuiet(podIDFD) } - createOptions.Pid = cmd.Flag("pid").Value.String() - - createOptions.Net, err = common.NetFlagsToNetOptions(cmd, createOptions.Infra) - if err != nil { - return err - } - if len(createOptions.Net.PublishPorts) > 0 { if !createOptions.Infra { return errors.Errorf("you must have an infra container to publish port bindings to the host") @@ -261,10 +234,44 @@ func create(cmd *cobra.Command, args []string) error { copy = "" + strconv.Itoa(core) } } - response, err := registry.ContainerEngine().PodCreate(context.Background(), createOptions) + podSpec := specgen.NewPodSpecGenerator() + podSpec, err = entities.ToPodSpecGen(*podSpec, &createOptions) if err != nil { return err } + if createOptions.Infra { + rawImageName = img + if !infraOptions.RootFS { + curr := infraOptions.Quiet + infraOptions.Quiet = true + name, err := containers.PullImage(imageName, infraOptions) + if err != nil { + fmt.Println(err) + } + imageName = name + infraOptions.Quiet = curr + } + podSpec.InfraImage = imageName + if infraOptions.Entrypoint != nil { + createOptions.InfraCommand = *infraOptions.Entrypoint + } + infraOptions.CPUS = createOptions.Cpus + infraOptions.CPUSetCPUs = createOptions.CpusetCpus + infraOptions.PID = createOptions.Pid + podSpec.InfraContainerSpec = specgen.NewSpecGenerator(imageName, false) + podSpec.InfraContainerSpec.RawImageName = rawImageName + podSpec.InfraContainerSpec.NetworkOptions = podSpec.NetworkOptions + err = specgenutil.FillOutSpecGen(podSpec.InfraContainerSpec, &infraOptions, []string{}) + if err != nil { + return err + } + } + PodSpec := entities.PodSpec{PodSpecGen: *podSpec} + response, err := registry.ContainerEngine().PodCreate(context.Background(), PodSpec) + if err != nil { + return err + } + if len(podIDFile) > 0 { if err = ioutil.WriteFile(podIDFile, []byte(response.Id), 0644); err != nil { return errors.Wrapf(err, "failed to write pod ID to file") diff --git a/cmd/podman/pods/rm.go b/cmd/podman/pods/rm.go index fbaf64c1fa..dc4c7eb834 100644 --- a/cmd/podman/pods/rm.go +++ b/cmd/podman/pods/rm.go @@ -12,6 +12,7 @@ import ( "github.com/containers/podman/v3/cmd/podman/validate" "github.com/containers/podman/v3/libpod/define" "github.com/containers/podman/v3/pkg/domain/entities" + "github.com/containers/podman/v3/pkg/specgenutil" "github.com/pkg/errors" "github.com/spf13/cobra" ) @@ -66,7 +67,7 @@ func init() { } func rm(_ *cobra.Command, args []string) error { - ids, err := common.ReadPodIDFiles(rmOptions.PodIDFiles) + ids, err := specgenutil.ReadPodIDFiles(rmOptions.PodIDFiles) if err != nil { return err } diff --git a/cmd/podman/pods/start.go b/cmd/podman/pods/start.go index e39891a9b0..e5f9eaa840 100644 --- a/cmd/podman/pods/start.go +++ b/cmd/podman/pods/start.go @@ -10,6 +10,7 @@ import ( "github.com/containers/podman/v3/cmd/podman/utils" "github.com/containers/podman/v3/cmd/podman/validate" "github.com/containers/podman/v3/pkg/domain/entities" + "github.com/containers/podman/v3/pkg/specgenutil" "github.com/spf13/cobra" ) @@ -64,7 +65,7 @@ func start(cmd *cobra.Command, args []string) error { errs utils.OutputErrors ) - ids, err := common.ReadPodIDFiles(startOptions.PodIDFiles) + ids, err := specgenutil.ReadPodIDFiles(startOptions.PodIDFiles) if err != nil { return err } diff --git a/cmd/podman/pods/stop.go b/cmd/podman/pods/stop.go index bcc054b8e2..41325649f6 100644 --- a/cmd/podman/pods/stop.go +++ b/cmd/podman/pods/stop.go @@ -10,6 +10,7 @@ import ( "github.com/containers/podman/v3/cmd/podman/utils" "github.com/containers/podman/v3/cmd/podman/validate" "github.com/containers/podman/v3/pkg/domain/entities" + "github.com/containers/podman/v3/pkg/specgenutil" "github.com/spf13/cobra" ) @@ -78,7 +79,7 @@ func stop(cmd *cobra.Command, args []string) error { stopOptions.Timeout = int(stopOptions.TimeoutCLI) } - ids, err := common.ReadPodIDFiles(stopOptions.PodIDFiles) + ids, err := specgenutil.ReadPodIDFiles(stopOptions.PodIDFiles) if err != nil { return err } diff --git a/docs/source/markdown/podman-pod-create.1.md b/docs/source/markdown/podman-pod-create.1.md index 4e822dca5b..fc7d397546 100644 --- a/docs/source/markdown/podman-pod-create.1.md +++ b/docs/source/markdown/podman-pod-create.1.md @@ -51,7 +51,26 @@ Set custom DNS options in the /etc/resolv.conf file that will be shared between Set custom DNS search domains in the /etc/resolv.conf file that will be shared between all containers in the pod. -#### **--help** +#### **--gidmap**=*container_gid:host_gid:amount* + +GID map for the user namespace. Using this flag will run the container with user namespace enabled. It conflicts with the `--userns` and `--subgidname` flags. + +#### **--uidmap**=*container_uid*:*from_uid*:*amount* + +Run the container in a new user namespace using the supplied mapping. This +option conflicts with the **--userns** and **--subuidname** options. This +option provides a way to map host UIDs to container UIDs. It can be passed +several times to map different ranges. + +#### **--subgidname**=*name* + +Name for GID map from the `/etc/subgid` file. Using this flag will run the container with user namespace enabled. This flag conflicts with `--userns` and `--gidmap`. + +#### **--subuidname**=*name* + +Name for UID map from the `/etc/subuid` file. Using this flag will run the container with user namespace enabled. This flag conflicts with `--userns` and `--uidmap`. + +#### **--help**, **-h** Print usage statement. diff --git a/libpod/container_internal.go b/libpod/container_internal.go index 3f7a4807d4..6717ada598 100644 --- a/libpod/container_internal.go +++ b/libpod/container_internal.go @@ -972,11 +972,12 @@ func (c *Container) checkDependenciesRunning() ([]string, error) { } // Check the status + conf := depCtr.Config() state, err := depCtr.State() if err != nil { return nil, errors.Wrapf(err, "error retrieving state of dependency %s of container %s", dep, c.ID()) } - if state != define.ContainerStateRunning { + if state != define.ContainerStateRunning && !conf.IsInfra { notRunning = append(notRunning, dep) } depCtrs[dep] = depCtr diff --git a/libpod/kube.go b/libpod/kube.go index a3f49bfe85..fff040adb8 100644 --- a/libpod/kube.go +++ b/libpod/kube.go @@ -10,6 +10,8 @@ import ( "github.com/containers/podman/v3/libpod/define" "github.com/containers/podman/v3/pkg/lookup" + "github.com/containers/podman/v3/pkg/namespaces" + "github.com/containers/podman/v3/pkg/specgen" "github.com/containers/podman/v3/pkg/util" "github.com/cri-o/ocicni/pkg/ocicni" "github.com/opencontainers/runtime-spec/specs-go" @@ -72,7 +74,7 @@ func (p *Pod) GenerateForKube() (*v1.Pod, []v1.ServicePort, error) { return nil, servicePorts, err } servicePorts = containerPortsToServicePorts(ports) - hostNetwork = p.config.InfraContainer.HostNetwork + hostNetwork = infraContainer.NetworkMode() == string(namespaces.NetworkMode(specgen.Host)) } pod, err := p.podWithContainers(allContainers, ports, hostNetwork) if err != nil { diff --git a/libpod/networking_linux.go b/libpod/networking_linux.go index dbe2274d35..5ade0849db 100644 --- a/libpod/networking_linux.go +++ b/libpod/networking_linux.go @@ -632,7 +632,6 @@ func (r *Runtime) configureNetNS(ctr *Container, ctrNS ns.NetNS) ([]*cnitypes.Re } podName := getCNIPodName(ctr) - networks, _, err := ctr.networks() if err != nil { return nil, err diff --git a/libpod/options.go b/libpod/options.go index 0bcd1e3a66..4cbd2b5e28 100644 --- a/libpod/options.go +++ b/libpod/options.go @@ -14,6 +14,7 @@ import ( "github.com/containers/image/v5/types" "github.com/containers/podman/v3/libpod/define" "github.com/containers/podman/v3/libpod/events" + netTypes "github.com/containers/podman/v3/libpod/network/types" "github.com/containers/podman/v3/pkg/namespaces" "github.com/containers/podman/v3/pkg/rootless" "github.com/containers/podman/v3/pkg/specgen" @@ -21,7 +22,6 @@ import ( "github.com/containers/storage" "github.com/containers/storage/pkg/idtools" "github.com/cri-o/ocicni/pkg/ocicni" - "github.com/opencontainers/runtime-spec/specs-go" "github.com/opencontainers/runtime-tools/generate" "github.com/pkg/errors" "github.com/sirupsen/logrus" @@ -713,7 +713,6 @@ func (r *Runtime) WithPod(pod *Pod) CtrCreateOption { if pod == nil { return define.ErrInvalidArg } - ctr.config.Pod = pod.ID() return nil @@ -1430,20 +1429,6 @@ func WithRestartRetries(tries uint) CtrCreateOption { } } -// withIsInfra sets the container to be an infra container. This means the container will be sometimes hidden -// and expected to be the first container in the pod. -func withIsInfra() CtrCreateOption { - return func(ctr *Container) error { - if ctr.valid { - return define.ErrCtrFinalized - } - - ctr.config.IsInfra = true - - return nil - } -} - // WithNamedVolumes adds the given named volumes to the container. func WithNamedVolumes(volumes []*ContainerNamedVolume) CtrCreateOption { return func(ctr *Container) error { @@ -1541,6 +1526,20 @@ func WithCreateCommand(cmd []string) CtrCreateOption { } } +// withIsInfra allows us to dfferentiate between infra containers and regular containers +// within the container config +func withIsInfra() CtrCreateOption { + return func(ctr *Container) error { + if ctr.valid { + return define.ErrCtrFinalized + } + + ctr.config.IsInfra = true + + return nil + } +} + // WithCreateWorkingDir tells Podman to create the container's working directory // if it does not exist. func WithCreateWorkingDir() CtrCreateOption { @@ -1812,45 +1811,14 @@ func WithInitCtrType(containerType string) CtrCreateOption { // Pod Creation Options -// WithInfraImage sets the infra image for libpod. -// An infra image is used for inter-container kernel -// namespace sharing within a pod. Typically, an infra -// container is lightweight and is there to reap -// zombie processes within its pid namespace. -func WithInfraImage(img string) PodCreateOption { - return func(pod *Pod) error { - if pod.valid { - return define.ErrPodFinalized - } - - pod.config.InfraContainer.InfraImage = img - - return nil - } -} - -// WithInfraCommand sets the command to -// run on pause container start up. -func WithInfraCommand(cmd []string) PodCreateOption { - return func(pod *Pod) error { - if pod.valid { - return define.ErrPodFinalized - } - - pod.config.InfraContainer.InfraCommand = cmd - return nil - } -} - -// WithInfraName sets the infra container name for a single pod. -func WithInfraName(name string) PodCreateOption { +// WithPodCreateCommand adds the full command plus arguments of the current +// process to the pod config. +func WithPodCreateCommand(createCmd []string) PodCreateOption { return func(pod *Pod) error { if pod.valid { return define.ErrPodFinalized } - - pod.config.InfraContainer.InfraName = name - + pod.config.CreateCommand = createCmd return nil } } @@ -1891,26 +1859,14 @@ func WithPodHostname(hostname string) PodCreateOption { } } -// WithPodCreateCommand adds the full command plus arguments of the current -// process to the pod config. -func WithPodCreateCommand(createCmd []string) PodCreateOption { - return func(pod *Pod) error { - if pod.valid { - return define.ErrPodFinalized - } - pod.config.CreateCommand = createCmd - return nil - } -} - // WithInfraConmonPidFile sets the path to a custom conmon PID file for the // infra container. -func WithInfraConmonPidFile(path string) PodCreateOption { +func WithInfraConmonPidFile(path string, infraSpec *specgen.SpecGenerator) PodCreateOption { return func(pod *Pod) error { if pod.valid { return define.ErrPodFinalized } - pod.config.InfraContainer.ConmonPidFile = path + infraSpec.ConmonPidFile = path return nil } } @@ -2099,320 +2055,25 @@ func WithInfraContainer() PodCreateOption { if pod.valid { return define.ErrPodFinalized } - - pod.config.InfraContainer.HasInfraContainer = true + pod.config.HasInfra = true return nil } } // WithInfraContainerPorts tells the pod to add port bindings to the pause container -func WithInfraContainerPorts(bindings []ocicni.PortMapping) PodCreateOption { - return func(pod *Pod) error { - if pod.valid { - return define.ErrPodFinalized - } - if !pod.config.InfraContainer.HasInfraContainer { - return errors.Wrapf(define.ErrInvalidArg, "cannot set pod ports as no infra container is being created") - } - pod.config.InfraContainer.PortBindings = bindings - return nil - } -} - -// WithPodStaticIP sets a static IP for the pod. -func WithPodStaticIP(ip net.IP) PodCreateOption { - return func(pod *Pod) error { - if pod.valid { - return define.ErrPodFinalized - } - - if !pod.config.InfraContainer.HasInfraContainer { - return errors.Wrapf(define.ErrInvalidArg, "cannot set pod static IP as no infra container is being created") - } - - if pod.config.InfraContainer.HostNetwork { - return errors.Wrapf(define.ErrInvalidArg, "cannot set static IP if host network is specified") - } - - if len(pod.config.InfraContainer.Networks) > 1 { - return errors.Wrapf(define.ErrInvalidArg, "cannot set a static IP if joining more than 1 CNI network") - } - - pod.config.InfraContainer.StaticIP = ip - - return nil - } -} - -// WithPodStaticMAC sets a static MAC address for the pod. -func WithPodStaticMAC(mac net.HardwareAddr) PodCreateOption { - return func(pod *Pod) error { - if pod.valid { - return define.ErrPodFinalized - } - - if !pod.config.InfraContainer.HasInfraContainer { - return errors.Wrapf(define.ErrInvalidArg, "cannot set pod static MAC as no infra container is being created") - } - - if pod.config.InfraContainer.HostNetwork { - return errors.Wrapf(define.ErrInvalidArg, "cannot set static MAC if host network is specified") - } - - if len(pod.config.InfraContainer.Networks) > 1 { - return errors.Wrapf(define.ErrInvalidArg, "cannot set a static MAC if joining more than 1 CNI network") - } - - pod.config.InfraContainer.StaticMAC = mac - - return nil - } -} - -// WithPodUseImageResolvConf sets a pod to use an image's resolv.conf and not -// create its own. -func WithPodUseImageResolvConf() PodCreateOption { - return func(pod *Pod) error { - if pod.valid { - return define.ErrPodFinalized - } - - if !pod.config.InfraContainer.HasInfraContainer { - return errors.Wrapf(define.ErrInvalidArg, "cannot configure pod DNS as no infra container is being created") - } - - if len(pod.config.InfraContainer.DNSServer) != 0 || - len(pod.config.InfraContainer.DNSSearch) != 0 || - len(pod.config.InfraContainer.DNSOption) != 0 { - return errors.Wrapf(define.ErrInvalidArg, "requested use of image resolv.conf conflicts with already-configured DNS settings") - } - - pod.config.InfraContainer.UseImageResolvConf = true - - return nil - } -} - -// WithPodDNS sets the DNS Servers for a pod. -func WithPodDNS(dnsServer []string) PodCreateOption { - return func(pod *Pod) error { - if pod.valid { - return define.ErrPodFinalized - } - - if !pod.config.InfraContainer.HasInfraContainer { - return errors.Wrapf(define.ErrInvalidArg, "cannot configure pod DNS as no infra container is being created") - } - - if pod.config.InfraContainer.UseImageResolvConf { - return errors.Wrapf(define.ErrInvalidArg, "cannot add DNS servers if pod will not create /etc/resolv.conf") - } - - pod.config.InfraContainer.DNSServer = dnsServer - - return nil - } -} - -// WithPodDNSSearch sets the DNS Search domains for a pod. -func WithPodDNSSearch(dnsSearch []string) PodCreateOption { - return func(pod *Pod) error { - if pod.valid { - return define.ErrPodFinalized - } - - if !pod.config.InfraContainer.HasInfraContainer { - return errors.Wrapf(define.ErrInvalidArg, "cannot configure pod DNS as no infra container is being created") - } - - if pod.config.InfraContainer.UseImageResolvConf { - return errors.Wrapf(define.ErrInvalidArg, "cannot add DNS search domains if pod will not create /etc/resolv.conf") - } - - pod.config.InfraContainer.DNSSearch = dnsSearch - - return nil - } -} - -// WithPodDNSOption sets DNS Options for a pod. -func WithPodDNSOption(dnsOption []string) PodCreateOption { - return func(pod *Pod) error { - if pod.valid { - return define.ErrPodFinalized - } - - if !pod.config.InfraContainer.HasInfraContainer { - return errors.Wrapf(define.ErrInvalidArg, "cannot configure pod DNS as no infra container is being created") - } - - if pod.config.InfraContainer.UseImageResolvConf { - return errors.Wrapf(define.ErrInvalidArg, "cannot add DNS options if pod will not create /etc/resolv.conf") - } - - pod.config.InfraContainer.DNSOption = dnsOption - - return nil - } -} - -// WithPodUseImageHosts tells the pod not to create /etc/hosts and instead to -// use the one provided by the image. -func WithPodUseImageHosts() PodCreateOption { - return func(pod *Pod) error { - if pod.valid { - return define.ErrPodFinalized - } - - if !pod.config.InfraContainer.HasInfraContainer { - return errors.Wrapf(define.ErrInvalidArg, "cannot configure pod hosts as no infra container is being created") - } - - if len(pod.config.InfraContainer.HostAdd) != 0 { - return errors.Wrapf(define.ErrInvalidArg, "not creating /etc/hosts conflicts with adding to the hosts file") - } - - pod.config.InfraContainer.UseImageHosts = true - - return nil - } -} - -// WithPodHosts adds additional entries to the pod's /etc/hosts -func WithPodHosts(hosts []string) PodCreateOption { - return func(pod *Pod) error { - if pod.valid { - return define.ErrPodFinalized - } - - if !pod.config.InfraContainer.HasInfraContainer { - return errors.Wrapf(define.ErrInvalidArg, "cannot configure pod hosts as no infra container is being created") - } - - if pod.config.InfraContainer.UseImageHosts { - return errors.Wrapf(define.ErrInvalidArg, "cannot add to /etc/hosts if container is using image hosts") - } - - pod.config.InfraContainer.HostAdd = hosts - - return nil - } -} - -// WithPodNetworks sets additional CNI networks for the pod to join. -func WithPodNetworks(networks []string) PodCreateOption { - return func(pod *Pod) error { - if pod.valid { - return define.ErrPodFinalized - } - - if !pod.config.InfraContainer.HasInfraContainer { - return errors.Wrapf(define.ErrInvalidArg, "cannot configure pod CNI networks as no infra container is being created") - } - - if (pod.config.InfraContainer.StaticIP != nil || pod.config.InfraContainer.StaticMAC != nil) && - len(networks) > 1 { - return errors.Wrapf(define.ErrInvalidArg, "cannot join more than one CNI network if setting a static IP or MAC address") - } - - if pod.config.InfraContainer.HostNetwork { - return errors.Wrapf(define.ErrInvalidArg, "cannot join pod to CNI networks if host network is specified") - } - - pod.config.InfraContainer.Networks = networks - - return nil - } -} - -// WithPodNoNetwork tells the pod to disable external networking. -func WithPodNoNetwork() PodCreateOption { - return func(pod *Pod) error { - if pod.valid { - return define.ErrPodFinalized - } - - if !pod.config.InfraContainer.HasInfraContainer { - return errors.Wrapf(define.ErrInvalidArg, "cannot disable pod networking as no infra container is being created") - } - - if len(pod.config.InfraContainer.PortBindings) > 0 || - pod.config.InfraContainer.StaticIP != nil || - pod.config.InfraContainer.StaticMAC != nil || - len(pod.config.InfraContainer.Networks) > 0 || - pod.config.InfraContainer.HostNetwork { - return errors.Wrapf(define.ErrInvalidArg, "cannot disable pod network if network-related configuration is specified") - } - - pod.config.InfraContainer.NoNetwork = true - - return nil - } -} - -// WithPodHostNetwork tells the pod to use the host's network namespace. -func WithPodHostNetwork() PodCreateOption { - return func(pod *Pod) error { - if pod.valid { - return define.ErrPodFinalized - } - if !pod.config.InfraContainer.HasInfraContainer { - return errors.Wrapf(define.ErrInvalidArg, "cannot configure pod host networking as no infra container is being created") - } - - if len(pod.config.InfraContainer.PortBindings) > 0 || - pod.config.InfraContainer.StaticIP != nil || - pod.config.InfraContainer.StaticMAC != nil || - len(pod.config.InfraContainer.Networks) > 0 || - pod.config.InfraContainer.NoNetwork { - return errors.Wrapf(define.ErrInvalidArg, "cannot set host network if network-related configuration is specified") - } - - pod.config.InfraContainer.HostNetwork = true - - return nil - } -} - -// WithPodInfraExitCommand sets an exit command for the pod's infra container. -// Semantics are identical to WithExitCommand() above - the ID of the container -// will be appended to the end of the provided command (note that this will -// specifically be the ID of the infra container *and not the pod's id*. -func WithPodInfraExitCommand(exitCmd []string) PodCreateOption { - return func(pod *Pod) error { - if pod.valid { - return define.ErrPodFinalized - } - - if !pod.config.InfraContainer.HasInfraContainer { - return errors.Wrapf(define.ErrInvalidArg, "cannot configure pod infra container exit command as no infra container is being created") - } - - pod.config.InfraContainer.ExitCommand = exitCmd - - return nil - } -} - -// WithPodSlirp4netns tells the pod to use slirp4netns. -func WithPodSlirp4netns(networkOptions map[string][]string) PodCreateOption { - return func(pod *Pod) error { - if pod.valid { - return define.ErrPodFinalized - } - - if !pod.config.InfraContainer.HasInfraContainer { - return errors.Wrapf(define.ErrInvalidArg, "cannot configure pod networking as no infra container is being created") - } - if pod.config.InfraContainer.HostNetwork { - return errors.Wrapf(define.ErrInvalidArg, "cannot set both HostNetwork and Slirp4netns") - } - pod.config.InfraContainer.Slirp4netns = true - pod.config.InfraContainer.NetworkOptions = networkOptions - - return nil +func WithInfraContainerPorts(bindings []ocicni.PortMapping, infraSpec *specgen.SpecGenerator) []netTypes.PortMapping { + bindingSpec := []netTypes.PortMapping{} + for _, bind := range bindings { + currBind := netTypes.PortMapping{} + currBind.ContainerPort = uint16(bind.ContainerPort) + currBind.HostIP = bind.HostIP + currBind.HostPort = uint16(bind.HostPort) + currBind.Protocol = bind.Protocol + bindingSpec = append(bindingSpec, currBind) } + infraSpec.PortMappings = bindingSpec + return infraSpec.PortMappings } // WithVolatile sets the volatile flag for the container storage. @@ -2428,78 +2089,3 @@ func WithVolatile() CtrCreateOption { return nil } } - -// WithPodUserns sets the userns for the infra container in a pod. -func WithPodUserns(userns specgen.Namespace) PodCreateOption { - return func(pod *Pod) error { - if pod.valid { - return define.ErrPodFinalized - } - - if !pod.config.InfraContainer.HasInfraContainer { - return errors.Wrapf(define.ErrInvalidArg, "cannot configure pod userns as no infra container is being created") - } - - pod.config.InfraContainer.Userns = userns - - return nil - } -} - -// WithPodCPUPAQ takes the given cpu period and quota and inserts them in the proper place. -func WithPodCPUPAQ(period uint64, quota int64) PodCreateOption { - return func(pod *Pod) error { - if pod.valid { - return define.ErrPodFinalized - } - if pod.CPUPeriod() != 0 && pod.CPUQuota() != 0 { - pod.config.InfraContainer.ResourceLimits.CPU = &specs.LinuxCPU{ - Period: &period, - Quota: "a, - } - } else { - pod.config.InfraContainer.ResourceLimits = &specs.LinuxResources{} - pod.config.InfraContainer.ResourceLimits.CPU = &specs.LinuxCPU{ - Period: &period, - Quota: "a, - } - } - return nil - } -} - -// WithPodCPUSetCPUS computes and sets the Cpus linux resource string which determines the amount of cores, from those available, we are allowed to execute on -func WithPodCPUSetCPUs(inp string) PodCreateOption { - return func(pod *Pod) error { - if pod.valid { - return define.ErrPodFinalized - } - if pod.ResourceLim().CPU.Period != nil { - pod.config.InfraContainer.ResourceLimits.CPU.Cpus = inp - } else { - pod.config.InfraContainer.ResourceLimits = &specs.LinuxResources{} - pod.config.InfraContainer.ResourceLimits.CPU = &specs.LinuxCPU{} - pod.config.InfraContainer.ResourceLimits.CPU.Cpus = inp - } - return nil - } -} - -func WithPodPidNS(inp specgen.Namespace) PodCreateOption { - return func(p *Pod) error { - if p.valid { - return define.ErrPodFinalized - } - if p.config.UsePodPID { - switch inp.NSMode { - case "container": - return errors.Wrap(define.ErrInvalidArg, "Cannot take container in a different NS as an argument") - case "host": - p.config.UsePodPID = false - } - p.config.InfraContainer.PidNS = inp - } - - return nil - } -} diff --git a/libpod/pod.go b/libpod/pod.go index 7df15df7b0..e4516b3549 100644 --- a/libpod/pod.go +++ b/libpod/pod.go @@ -2,14 +2,12 @@ package libpod import ( "context" - "net" + "fmt" "sort" "time" "github.com/containers/podman/v3/libpod/define" "github.com/containers/podman/v3/libpod/lock" - "github.com/containers/podman/v3/pkg/specgen" - "github.com/cri-o/ocicni/pkg/ocicni" "github.com/opencontainers/runtime-spec/specs-go" "github.com/pkg/errors" ) @@ -63,7 +61,7 @@ type PodConfig struct { UsePodUTS bool `json:"sharesUts,omitempty"` UsePodCgroupNS bool `json:"sharesCgroupNS,omitempty"` - InfraContainer *InfraContainerConfig `json:"infraConfig"` + HasInfra bool `json:"hasInfra,omitempty"` // Time pod was created CreatedTime time.Time `json:"created"` @@ -85,41 +83,6 @@ type podState struct { InfraContainerID string } -// InfraContainerConfig is the configuration for the pod's infra container. -// Generally speaking, these are equivalent to container configuration options -// you will find in container_config.go (and even named identically), save for -// HasInfraContainer (which determines if an infra container is even created - -// if it is false, no other options in this struct will be used) and HostNetwork -// (this involves the created OCI spec, and as such is not represented directly -// in container_config.go). -// Generally speaking, aside from those two exceptions, these options will set -// the equivalent field in the container's configuration. -type InfraContainerConfig struct { - ConmonPidFile string `json:"conmonPidFile"` - HasInfraContainer bool `json:"makeInfraContainer"` - NoNetwork bool `json:"noNetwork,omitempty"` - HostNetwork bool `json:"infraHostNetwork,omitempty"` - PidNS specgen.Namespace `json:"infraPid,omitempty"` - PortBindings []ocicni.PortMapping `json:"infraPortBindings"` - StaticIP net.IP `json:"staticIP,omitempty"` - StaticMAC net.HardwareAddr `json:"staticMAC,omitempty"` - UseImageResolvConf bool `json:"useImageResolvConf,omitempty"` - DNSServer []string `json:"dnsServer,omitempty"` - DNSSearch []string `json:"dnsSearch,omitempty"` - DNSOption []string `json:"dnsOption,omitempty"` - UseImageHosts bool `json:"useImageHosts,omitempty"` - HostAdd []string `json:"hostsAdd,omitempty"` - Networks []string `json:"networks,omitempty"` - ExitCommand []string `json:"exitCommand,omitempty"` - InfraImage string `json:"infraImage,omitempty"` - InfraCommand []string `json:"infraCommand,omitempty"` - InfraName string `json:"infraName,omitempty"` - Slirp4netns bool `json:"slirp4netns,omitempty"` - NetworkOptions map[string][]string `json:"network_options,omitempty"` - ResourceLimits *specs.LinuxResources `json:"resource_limits,omitempty"` - Userns specgen.Namespace `json:"userns,omitempty"` -} - // ID retrieves the pod's ID func (p *Pod) ID() string { return p.config.ID @@ -139,45 +102,104 @@ func (p *Pod) Namespace() string { // ResourceLim returns the cpuset resource limits for the pod func (p *Pod) ResourceLim() *specs.LinuxResources { resCopy := &specs.LinuxResources{} - if err := JSONDeepCopy(p.config.InfraContainer.ResourceLimits, resCopy); err != nil { + empty := &specs.LinuxResources{ + CPU: &specs.LinuxCPU{}, + } + infra, err := p.runtime.GetContainer(p.state.InfraContainerID) + if err != nil { + return empty + } + conf := infra.config.Spec + if err != nil { + return empty + } + if conf.Linux == nil || conf.Linux.Resources == nil { + return empty + } + if err = JSONDeepCopy(conf.Linux.Resources, resCopy); err != nil { return nil } - if resCopy != nil && resCopy.CPU != nil { + if resCopy.CPU != nil { return resCopy } - empty := &specs.LinuxResources{ - CPU: &specs.LinuxCPU{}, - } + return empty } // CPUPeriod returns the pod CPU period func (p *Pod) CPUPeriod() uint64 { - resCopy := &specs.LinuxResources{} - if err := JSONDeepCopy(p.config.InfraContainer.ResourceLimits, resCopy); err != nil { + if p.state.InfraContainerID == "" { return 0 } - if resCopy != nil && resCopy.CPU != nil && resCopy.CPU.Period != nil { - return *resCopy.CPU.Period + infra, err := p.runtime.GetContainer(p.state.InfraContainerID) + if err != nil { + return 0 + } + conf := infra.config.Spec + if conf != nil && conf.Linux != nil && conf.Linux.Resources != nil && conf.Linux.Resources.CPU != nil && conf.Linux.Resources.CPU.Period != nil { + return *conf.Linux.Resources.CPU.Period } return 0 } // CPUQuota returns the pod CPU quota func (p *Pod) CPUQuota() int64 { - resCopy := &specs.LinuxResources{} - if err := JSONDeepCopy(p.config.InfraContainer.ResourceLimits, resCopy); err != nil { + if p.state.InfraContainerID == "" { + return 0 + } + infra, err := p.runtime.GetContainer(p.state.InfraContainerID) + if err != nil { return 0 } - if resCopy != nil && resCopy.CPU != nil && resCopy.CPU.Quota != nil { - return *resCopy.CPU.Quota + conf := infra.config.Spec + if conf != nil && conf.Linux != nil && conf.Linux.Resources != nil && conf.Linux.Resources.CPU != nil && conf.Linux.Resources.CPU.Quota != nil { + return *conf.Linux.Resources.CPU.Quota } return 0 } // PidMode returns the PID mode given by the user ex: pod, private... func (p *Pod) PidMode() string { - return string(p.config.InfraContainer.PidNS.NSMode) + infra, err := p.runtime.GetContainer(p.state.InfraContainerID) + if err != nil { + return "" + } + conf := infra.Config() + ctrSpec := conf.Spec + if ctrSpec != nil && ctrSpec.Linux != nil { + for _, ns := range ctrSpec.Linux.Namespaces { + if ns.Type == specs.PIDNamespace { + if ns.Path != "" { + return fmt.Sprintf("ns:%s", ns.Path) + } + return "private" + } + } + return "host" + } + return "" +} + +// PidMode returns the PID mode given by the user ex: pod, private... +func (p *Pod) UserNSMode() string { + infra, err := p.infraContainer() + if err != nil { + return "" + } + conf := infra.Config() + ctrSpec := conf.Spec + if ctrSpec != nil && ctrSpec.Linux != nil { + for _, ns := range ctrSpec.Linux.Namespaces { + if ns.Type == specs.UserNamespace { + if ns.Path != "" { + return fmt.Sprintf("ns:%s", ns.Path) + } + return "private" + } + } + return "host" + } + return "" } // Labels returns the pod's labels @@ -263,20 +285,24 @@ func (p *Pod) CgroupPath() (string, error) { if p.state.CgroupPath != "" { return p.state.CgroupPath, nil } - if !p.HasInfraContainer() { + if p.state.InfraContainerID == "" { return "", errors.Wrap(define.ErrNoSuchCtr, "pod has no infra container") } - id := p.state.InfraContainerID + id, err := p.infraContainerID() + if err != nil { + return "", err + } if id != "" { - ctr, err := p.runtime.state.Container(id) + ctr, err := p.infraContainer() if err != nil { return "", errors.Wrapf(err, "could not get infra") } if ctr != nil { - ctr.Start(context.Background(), false) + ctr.Start(context.Background(), true) cgroupPath, err := ctr.CGroupPath() + fmt.Println(cgroupPath) if err != nil { return "", errors.Wrapf(err, "could not get container cgroup") } @@ -325,7 +351,7 @@ func (p *Pod) allContainers() ([]*Container, error) { // HasInfraContainer returns whether the pod will create an infra container func (p *Pod) HasInfraContainer() bool { - return p.config.InfraContainer.HasInfraContainer + return p.config.HasInfra } // SharesNamespaces checks if the pod has any kernel namespaces set as shared. An infra container will not be @@ -350,19 +376,26 @@ func (p *Pod) InfraContainerID() (string, error) { return p.infraContainerID() } -// InfraContainer returns the infra container. -func (p *Pod) InfraContainer() (*Container, error) { - if !p.HasInfraContainer() { - return nil, errors.Wrap(define.ErrNoSuchCtr, "pod has no infra container") - } - id, err := p.InfraContainerID() +// infraContainer is the unlocked versio of InfraContainer which returns the infra container +func (p *Pod) infraContainer() (*Container, error) { + id, err := p.infraContainerID() if err != nil { return nil, err } + if id == "" { + return nil, errors.Wrap(define.ErrNoSuchCtr, "pod has no infra container") + } return p.runtime.state.Container(id) } +// InfraContainer returns the infra container. +func (p *Pod) InfraContainer() (*Container, error) { + p.lock.Lock() + defer p.lock.Unlock() + return p.infraContainer() +} + // TODO add pod batching // Lock pod to avoid lock contention // Store and lock all containers (no RemoveContainer in batch guarantees cache will not become stale) @@ -412,13 +445,7 @@ func (p *Pod) ProcessLabel() (string, error) { if !p.HasInfraContainer() { return "", nil } - - id, err := p.InfraContainerID() - if err != nil { - return "", err - } - - ctr, err := p.runtime.state.Container(id) + ctr, err := p.infraContainer() if err != nil { return "", err } diff --git a/libpod/pod_api.go b/libpod/pod_api.go index 53fb9538f3..5f4d983b97 100644 --- a/libpod/pod_api.go +++ b/libpod/pod_api.go @@ -582,41 +582,46 @@ func (p *Pod) Inspect() (*define.InspectPodData, error) { // Infra config contains detailed information on the pod's infra // container. var infraConfig *define.InspectPodInfraConfig - if p.config.InfraContainer != nil && p.config.InfraContainer.HasInfraContainer { + if p.state.InfraContainerID != "" { + infra, err := p.runtime.GetContainer(p.state.InfraContainerID) + if err != nil { + return nil, err + } infraConfig = new(define.InspectPodInfraConfig) - infraConfig.HostNetwork = p.config.InfraContainer.HostNetwork - infraConfig.StaticIP = p.config.InfraContainer.StaticIP - infraConfig.StaticMAC = p.config.InfraContainer.StaticMAC.String() - infraConfig.NoManageResolvConf = p.config.InfraContainer.UseImageResolvConf - infraConfig.NoManageHosts = p.config.InfraContainer.UseImageHosts + infraConfig.HostNetwork = !infra.Config().ContainerNetworkConfig.UseImageHosts + infraConfig.StaticIP = infra.Config().ContainerNetworkConfig.StaticIP + infraConfig.NoManageResolvConf = infra.Config().UseImageResolvConf + infraConfig.NoManageHosts = infra.Config().UseImageHosts infraConfig.CPUPeriod = p.CPUPeriod() infraConfig.CPUQuota = p.CPUQuota() infraConfig.CPUSetCPUs = p.ResourceLim().CPU.Cpus infraConfig.PidNS = p.PidMode() - infraConfig.UserNS = p.config.InfraContainer.Userns.String() + infraConfig.UserNS = p.UserNSMode() - if len(p.config.InfraContainer.DNSServer) > 0 { - infraConfig.DNSServer = make([]string, 0, len(p.config.InfraContainer.DNSServer)) - infraConfig.DNSServer = append(infraConfig.DNSServer, p.config.InfraContainer.DNSServer...) + if len(infra.Config().ContainerNetworkConfig.DNSServer) > 0 { + infraConfig.DNSServer = make([]string, 0, len(infra.Config().ContainerNetworkConfig.DNSServer)) + for _, entry := range infra.Config().ContainerNetworkConfig.DNSServer { + infraConfig.DNSServer = append(infraConfig.DNSServer, entry.String()) + } } - if len(p.config.InfraContainer.DNSSearch) > 0 { - infraConfig.DNSSearch = make([]string, 0, len(p.config.InfraContainer.DNSSearch)) - infraConfig.DNSSearch = append(infraConfig.DNSSearch, p.config.InfraContainer.DNSSearch...) + if len(infra.Config().ContainerNetworkConfig.DNSSearch) > 0 { + infraConfig.DNSSearch = make([]string, 0, len(infra.Config().ContainerNetworkConfig.DNSSearch)) + infraConfig.DNSSearch = append(infraConfig.DNSSearch, infra.Config().ContainerNetworkConfig.DNSSearch...) } - if len(p.config.InfraContainer.DNSOption) > 0 { - infraConfig.DNSOption = make([]string, 0, len(p.config.InfraContainer.DNSOption)) - infraConfig.DNSOption = append(infraConfig.DNSOption, p.config.InfraContainer.DNSOption...) + if len(infra.Config().ContainerNetworkConfig.DNSOption) > 0 { + infraConfig.DNSOption = make([]string, 0, len(infra.Config().ContainerNetworkConfig.DNSOption)) + infraConfig.DNSOption = append(infraConfig.DNSOption, infra.Config().ContainerNetworkConfig.DNSOption...) } - if len(p.config.InfraContainer.HostAdd) > 0 { - infraConfig.HostAdd = make([]string, 0, len(p.config.InfraContainer.HostAdd)) - infraConfig.HostAdd = append(infraConfig.HostAdd, p.config.InfraContainer.HostAdd...) + if len(infra.Config().HostAdd) > 0 { + infraConfig.HostAdd = make([]string, 0, len(infra.Config().HostAdd)) + infraConfig.HostAdd = append(infraConfig.HostAdd, infra.Config().HostAdd...) } - if len(p.config.InfraContainer.Networks) > 0 { - infraConfig.Networks = make([]string, 0, len(p.config.InfraContainer.Networks)) - infraConfig.Networks = append(infraConfig.Networks, p.config.InfraContainer.Networks...) + if len(infra.Config().ContainerNetworkConfig.Networks) > 0 { + infraConfig.Networks = make([]string, 0, len(infra.Config().ContainerNetworkConfig.Networks)) + infraConfig.Networks = append(infraConfig.Networks, infra.Config().ContainerNetworkConfig.Networks...) } - infraConfig.NetworkOptions = p.config.InfraContainer.NetworkOptions - infraConfig.PortBindings = makeInspectPortBindings(p.config.InfraContainer.PortBindings, nil) + infraConfig.NetworkOptions = infra.Config().ContainerNetworkConfig.NetworkOptions + infraConfig.PortBindings = makeInspectPortBindings(infra.Config().ContainerNetworkConfig.PortMappings, nil) } inspectData := define.InspectPodData{ diff --git a/libpod/pod_internal.go b/libpod/pod_internal.go index e81bd7b16c..079b631a08 100644 --- a/libpod/pod_internal.go +++ b/libpod/pod_internal.go @@ -20,7 +20,7 @@ func newPod(runtime *Runtime) *Pod { pod.config.ID = stringid.GenerateNonCryptoID() pod.config.Labels = make(map[string]string) pod.config.CreatedTime = time.Now() - pod.config.InfraContainer = new(InfraContainerConfig) + // pod.config.InfraContainer = new(ContainerConfig) pod.state = new(podState) pod.runtime = runtime diff --git a/libpod/runtime_ctr.go b/libpod/runtime_ctr.go index 52072b0f32..7d3891f6e1 100644 --- a/libpod/runtime_ctr.go +++ b/libpod/runtime_ctr.go @@ -17,6 +17,7 @@ import ( "github.com/containers/podman/v3/pkg/cgroups" "github.com/containers/podman/v3/pkg/domain/entities/reports" "github.com/containers/podman/v3/pkg/rootless" + "github.com/containers/podman/v3/pkg/specgen" "github.com/containers/storage" "github.com/containers/storage/pkg/stringid" "github.com/docker/go-units" @@ -38,12 +39,15 @@ type CtrCreateOption func(*Container) error type ContainerFilter func(*Container) bool // NewContainer creates a new container from a given OCI config. -func (r *Runtime) NewContainer(ctx context.Context, rSpec *spec.Spec, options ...CtrCreateOption) (*Container, error) { +func (r *Runtime) NewContainer(ctx context.Context, rSpec *spec.Spec, spec *specgen.SpecGenerator, infra bool, options ...CtrCreateOption) (*Container, error) { r.lock.Lock() defer r.lock.Unlock() if !r.valid { return nil, define.ErrRuntimeStopped } + if infra { + options = append(options, withIsInfra()) + } return r.newContainer(ctx, rSpec, options...) } @@ -172,6 +176,7 @@ func (r *Runtime) initContainerVariables(rSpec *spec.Spec, config *ContainerConf } ctr.config.ShmSize = size ctr.config.StopSignal = 15 + ctr.config.StopTimeout = r.config.Engine.StopTimeout } else { // This is a restore from an imported checkpoint @@ -211,7 +216,11 @@ func (r *Runtime) initContainerVariables(rSpec *spec.Spec, config *ContainerConf } func (r *Runtime) newContainer(ctx context.Context, rSpec *spec.Spec, options ...CtrCreateOption) (*Container, error) { - ctr, err := r.initContainerVariables(rSpec, nil) + var ctr *Container + var err error + + ctr, err = r.initContainerVariables(rSpec, nil) + if err != nil { return nil, errors.Wrapf(err, "error initializing container variables") } @@ -230,7 +239,9 @@ func (r *Runtime) setupContainer(ctx context.Context, ctr *Container) (_ *Contai if err := ctr.validate(); err != nil { return nil, err } - + if ctr.config.IsInfra { + ctr.config.StopTimeout = 10 + } // normalize the networks to names // ocicni only knows about cni names so we have to make // sure we do not use ids internally @@ -327,7 +338,7 @@ func (r *Runtime) setupContainer(ctx context.Context, ctr *Container) (_ *Contai switch r.config.Engine.CgroupManager { case config.CgroupfsCgroupsManager: if ctr.config.CgroupParent == "" { - if pod != nil && pod.config.UsePodCgroup { + if pod != nil && pod.config.UsePodCgroup && !ctr.IsInfra() { podCgroup, err := pod.CgroupPath() if err != nil { return nil, errors.Wrapf(err, "error retrieving pod %s cgroup", pod.ID()) @@ -348,7 +359,7 @@ func (r *Runtime) setupContainer(ctx context.Context, ctr *Container) (_ *Contai case config.SystemdCgroupsManager: if ctr.config.CgroupParent == "" { switch { - case pod != nil && pod.config.UsePodCgroup: + case pod != nil && pod.config.UsePodCgroup && !ctr.IsInfra(): podCgroup, err := pod.CgroupPath() if err != nil { return nil, errors.Wrapf(err, "error retrieving pod %s cgroup", pod.ID()) @@ -833,7 +844,10 @@ func (r *Runtime) evictContainer(ctx context.Context, idOrName string, removeVol return id, err } - infraID := pod.state.InfraContainerID + infraID, err := pod.infraContainerID() + if err != nil { + return "", err + } if c.ID() == infraID { return id, errors.Errorf("container %s is the infra container of pod %s and cannot be removed without removing the pod", c.ID(), pod.ID()) } diff --git a/libpod/runtime_pod_infra_linux.go b/libpod/runtime_pod_infra_linux.go deleted file mode 100644 index 9236fb1f5f..0000000000 --- a/libpod/runtime_pod_infra_linux.go +++ /dev/null @@ -1,284 +0,0 @@ -// +build linux - -package libpod - -import ( - "context" - "strings" - - "github.com/containers/common/pkg/config" - "github.com/containers/podman/v3/libpod/define" - "github.com/containers/podman/v3/pkg/namespaces" - "github.com/containers/podman/v3/pkg/rootless" - "github.com/containers/podman/v3/pkg/specgen" - "github.com/containers/podman/v3/pkg/util" - v1 "github.com/opencontainers/image-spec/specs-go/v1" - spec "github.com/opencontainers/runtime-spec/specs-go" - "github.com/opencontainers/runtime-tools/generate" - "github.com/pkg/errors" - "github.com/sirupsen/logrus" -) - -const ( - // IDTruncLength is the length of the pod's id that will be used to make the - // infra container name - IDTruncLength = 12 -) - -func (r *Runtime) makeInfraContainer(ctx context.Context, p *Pod, imgName, rawImageName, imgID string, config *v1.ImageConfig) (*Container, error) { - // Set up generator for infra container defaults - g, err := generate.New("linux") - if err != nil { - return nil, err - } - - // Set Pod hostname - g.Config.Hostname = p.config.Hostname - - var options []CtrCreateOption - - // Command: If user-specified, use that preferentially. - // If not set and the config file is set, fall back to that. - var infraCtrCommand []string - if p.config.InfraContainer.InfraCommand != nil { - logrus.Debugf("User-specified infra container entrypoint %v", p.config.InfraContainer.InfraCommand) - infraCtrCommand = p.config.InfraContainer.InfraCommand - } else if r.config.Engine.InfraCommand != "" { - logrus.Debugf("Config-specified infra container entrypoint %s", r.config.Engine.InfraCommand) - infraCtrCommand = []string{r.config.Engine.InfraCommand} - } - // Only if set by the user or containers.conf, we set entrypoint for the - // infra container. - // This is only used by commit, so it shouldn't matter... But someone - // may eventually want to commit an infra container? - // TODO: Should we actually do this if set by containers.conf? - if infraCtrCommand != nil { - // Need to duplicate the array - we are going to add Cmd later - // so the current array will be changed. - newArr := make([]string, 0, len(infraCtrCommand)) - newArr = append(newArr, infraCtrCommand...) - options = append(options, WithEntrypoint(newArr)) - } - - isRootless := rootless.IsRootless() - - // I've seen circumstances where config is being passed as nil. - // Let's err on the side of safety and make sure it's safe to use. - if config != nil { - if infraCtrCommand == nil { - // If we have no entrypoint and command from the image, - // we can't go on - the infra container has no command. - if len(config.Entrypoint) == 0 && len(config.Cmd) == 0 { - return nil, errors.Errorf("infra container has no command") - } - if len(config.Entrypoint) > 0 { - infraCtrCommand = config.Entrypoint - } else { - // Use the Docker default "/bin/sh -c" - // entrypoint, as we're overriding command. - // If an image doesn't want this, it can - // override entrypoint too. - infraCtrCommand = []string{"/bin/sh", "-c"} - } - } - if len(config.Cmd) > 0 { - infraCtrCommand = append(infraCtrCommand, config.Cmd...) - } - - if len(config.Env) > 0 { - for _, nameValPair := range config.Env { - nameValSlice := strings.Split(nameValPair, "=") - if len(nameValSlice) < 2 { - return nil, errors.Errorf("Invalid environment variable structure in pause image") - } - g.AddProcessEnv(nameValSlice[0], nameValSlice[1]) - } - } - - switch { - case p.config.InfraContainer.HostNetwork: - if err := g.RemoveLinuxNamespace(string(spec.NetworkNamespace)); err != nil { - return nil, errors.Wrapf(err, "error removing network namespace from pod %s infra container", p.ID()) - } - case p.config.InfraContainer.NoNetwork: - // Do nothing - we have a network namespace by default, - // but should not configure slirp. - default: - // Since user namespace sharing is not implemented, we only need to check if it's rootless - netmode := "bridge" - if p.config.InfraContainer.Slirp4netns { - netmode = "slirp4netns" - if len(p.config.InfraContainer.NetworkOptions) != 0 { - options = append(options, WithNetworkOptions(p.config.InfraContainer.NetworkOptions)) - } - } - // FIXME allow pods to have exposed ports - options = append(options, WithNetNS(p.config.InfraContainer.PortBindings, nil, !p.config.InfraContainer.Userns.IsHost(), netmode, p.config.InfraContainer.Networks)) - } - - // For each option in InfraContainerConfig - if set, pass into - // the infra container we're creating with the appropriate - // With... option. - if p.config.InfraContainer.StaticIP != nil { - options = append(options, WithStaticIP(p.config.InfraContainer.StaticIP)) - } - if p.config.InfraContainer.StaticMAC != nil { - options = append(options, WithStaticMAC(p.config.InfraContainer.StaticMAC)) - } - if p.config.InfraContainer.UseImageResolvConf { - options = append(options, WithUseImageResolvConf()) - } - if len(p.config.InfraContainer.DNSServer) > 0 { - options = append(options, WithDNS(p.config.InfraContainer.DNSServer)) - } - if len(p.config.InfraContainer.DNSSearch) > 0 { - options = append(options, WithDNSSearch(p.config.InfraContainer.DNSSearch)) - } - if len(p.config.InfraContainer.DNSOption) > 0 { - options = append(options, WithDNSOption(p.config.InfraContainer.DNSOption)) - } - if p.config.InfraContainer.UseImageHosts { - options = append(options, WithUseImageHosts()) - } - if len(p.config.InfraContainer.HostAdd) > 0 { - options = append(options, WithHosts(p.config.InfraContainer.HostAdd)) - } - if len(p.config.InfraContainer.ExitCommand) > 0 { - options = append(options, WithExitCommand(p.config.InfraContainer.ExitCommand)) - } - - if p.config.UsePodPID && p.config.InfraContainer.PidNS.NSMode != "host" { - g.AddOrReplaceLinuxNamespace(string(spec.LinuxNamespaceType("pid")), p.config.InfraContainer.PidNS.Value) - } else if p.config.InfraContainer.PidNS.NSMode == "host" { - newNS := []spec.LinuxNamespace{} - for _, entry := range g.Config.Linux.Namespaces { - if entry.Type != spec.LinuxNamespaceType("pid") { - newNS = append(newNS, entry) - } - } - g.Config.Linux.Namespaces = newNS - } - } - - for _, ctl := range r.config.Containers.DefaultSysctls { - sysctl := strings.SplitN(ctl, "=", 2) - if len(sysctl) < 2 { - return nil, errors.Errorf("invalid default sysctl %s", ctl) - } - - // Ignore net sysctls if --net=host - if p.config.InfraContainer.HostNetwork && strings.HasPrefix(sysctl[0], "net.") { - logrus.Infof("Sysctl %s=%s ignored in containers.conf, since Network Namespace set to host", sysctl[0], sysctl[1]) - continue - } - - g.AddLinuxSysctl(sysctl[0], sysctl[1]) - } - - g.SetRootReadonly(true) - g.SetProcessArgs(infraCtrCommand) - - logrus.Debugf("Using %q as infra container command", infraCtrCommand) - - mapopt, err := util.ParseIDMapping(namespaces.UsernsMode(p.config.InfraContainer.Userns.String()), []string{}, []string{}, "", "") - if err != nil { - return nil, err - } - user, err := specgen.SetupUserNS(mapopt, p.config.InfraContainer.Userns, &g) - if err != nil { - return nil, err - } - if user != "" { - options = append(options, WithUser(user)) - } - - g.RemoveMount("/dev/shm") - if isRootless { - g.RemoveMount("/dev/pts") - devPts := spec.Mount{ - Destination: "/dev/pts", - Type: "devpts", - Source: "devpts", - Options: []string{"private", "nosuid", "noexec", "newinstance", "ptmxmode=0666", "mode=0620"}, - } - g.AddMount(devPts) - } - - // Add default sysctls from containers.conf - defaultSysctls, err := util.ValidateSysctls(r.config.Sysctls()) - if err != nil { - return nil, err - } - for sysctlKey, sysctlVal := range defaultSysctls { - // Ignore mqueue sysctls if not sharing IPC - if !p.config.UsePodIPC && strings.HasPrefix(sysctlKey, "fs.mqueue.") { - logrus.Infof("Sysctl %s=%s ignored in containers.conf, since IPC Namespace for pod is unused", sysctlKey, sysctlVal) - continue - } - - // Ignore net sysctls if host network or not sharing network - if (p.config.InfraContainer.HostNetwork || !p.config.UsePodNet) && strings.HasPrefix(sysctlKey, "net.") { - logrus.Infof("Sysctl %s=%s ignored in containers.conf, since Network Namespace for pod is unused", sysctlKey, sysctlVal) - continue - } - - // Ignore uts sysctls if not sharing UTS - if !p.config.UsePodUTS && (strings.HasPrefix(sysctlKey, "kernel.domainname") || strings.HasPrefix(sysctlKey, "kernel.hostname")) { - logrus.Infof("Sysctl %s=%s ignored in containers.conf, since UTS Namespace for pod is unused", sysctlKey, sysctlVal) - continue - } - g.AddLinuxSysctl(sysctlKey, sysctlVal) - } - - containerName := p.config.InfraContainer.InfraName - if containerName == "" { - containerName = p.ID()[:IDTruncLength] + "-infra" - } - logrus.Infof("Infra container name %s", containerName) - options = append(options, r.WithPod(p)) - options = append(options, WithRootFSFromImage(imgID, imgName, rawImageName)) - options = append(options, WithName(containerName)) - options = append(options, withIsInfra()) - options = append(options, WithIDMappings(*mapopt)) - if len(p.config.InfraContainer.ConmonPidFile) > 0 { - options = append(options, WithConmonPidFile(p.config.InfraContainer.ConmonPidFile)) - } - newRes := new(spec.LinuxResources) - newRes.CPU = new(spec.LinuxCPU) - newRes.CPU = p.ResourceLim().CPU - g.Config.Linux.Resources.CPU = newRes.CPU - - return r.newContainer(ctx, g.Config, options...) -} - -// createInfraContainer wrap creates an infra container for a pod. -// An infra container becomes the basis for kernel namespace sharing between -// containers in the pod. -func (r *Runtime) createInfraContainer(ctx context.Context, p *Pod) (*Container, error) { - if !r.valid { - return nil, define.ErrRuntimeStopped - } - imageName := p.config.InfraContainer.InfraImage - if imageName == "" { - imageName = r.config.Engine.InfraImage - } - - pulledImages, err := r.LibimageRuntime().Pull(ctx, imageName, config.PullPolicyMissing, nil) - if err != nil { - return nil, errors.Wrap(err, "error pulling infra-container image") - } - - newImage := pulledImages[0] - data, err := newImage.Inspect(ctx, false) - if err != nil { - return nil, err - } - - imageName = "none" - if len(newImage.Names()) > 0 { - imageName = newImage.Names()[0] - } - imageID := data.ID - - return r.makeInfraContainer(ctx, p, imageName, r.config.Engine.InfraImage, imageID, data.Config) -} diff --git a/libpod/runtime_pod_linux.go b/libpod/runtime_pod_linux.go index fce3f38a75..7571fdfff2 100644 --- a/libpod/runtime_pod_linux.go +++ b/libpod/runtime_pod_linux.go @@ -14,13 +14,14 @@ import ( "github.com/containers/podman/v3/libpod/events" "github.com/containers/podman/v3/pkg/cgroups" "github.com/containers/podman/v3/pkg/rootless" + "github.com/containers/podman/v3/pkg/specgen" spec "github.com/opencontainers/runtime-spec/specs-go" "github.com/pkg/errors" "github.com/sirupsen/logrus" ) // NewPod makes a new, empty pod -func (r *Runtime) NewPod(ctx context.Context, options ...PodCreateOption) (_ *Pod, deferredErr error) { +func (r *Runtime) NewPod(ctx context.Context, p specgen.PodSpecGenerator, options ...PodCreateOption) (_ *Pod, deferredErr error) { r.lock.Lock() defer r.lock.Unlock() @@ -50,8 +51,8 @@ func (r *Runtime) NewPod(ctx context.Context, options ...PodCreateOption) (_ *Po pod.config.Name = name } - if pod.config.Hostname == "" { - pod.config.Hostname = pod.config.Name + if p.InfraContainerSpec != nil && p.InfraContainerSpec.Hostname == "" { + p.InfraContainerSpec.Hostname = pod.config.Name } // Allocate a lock for the pod @@ -88,6 +89,9 @@ func (r *Runtime) NewPod(ctx context.Context, options ...PodCreateOption) (_ *Po // launch should do it for us if pod.config.UsePodCgroup { pod.state.CgroupPath = filepath.Join(pod.config.CgroupParent, pod.ID()) + if p.InfraContainerSpec != nil { + p.InfraContainerSpec.CgroupParent = pod.state.CgroupPath + } } } case config.SystemdCgroupsManager: @@ -108,6 +112,9 @@ func (r *Runtime) NewPod(ctx context.Context, options ...PodCreateOption) (_ *Po return nil, errors.Wrapf(err, "unable to create pod cgroup for pod %s", pod.ID()) } pod.state.CgroupPath = cgroupPath + if p.InfraContainerSpec != nil { + p.InfraContainerSpec.CgroupParent = pod.state.CgroupPath + } } default: return nil, errors.Wrapf(define.ErrInvalidArg, "unsupported CGroup manager: %s - cannot validate cgroup parent", r.config.Engine.CgroupManager) @@ -127,28 +134,40 @@ func (r *Runtime) NewPod(ctx context.Context, options ...PodCreateOption) (_ *Po if err := r.state.AddPod(pod); err != nil { return nil, errors.Wrapf(err, "error adding pod to state") } - defer func() { - if deferredErr != nil { - if err := r.removePod(ctx, pod, true, true); err != nil { - logrus.Errorf("Error removing pod after pause container creation failure: %v", err) - } - } - }() + return pod, nil +} - if pod.HasInfraContainer() { - ctr, err := r.createInfraContainer(ctx, pod) - if err != nil { - return nil, errors.Wrapf(err, "error adding Infra Container") - } - pod.state.InfraContainerID = ctr.ID() - if err := pod.save(); err != nil { - return nil, err - } +// AddInfra adds the created infra container to the pod state +func (r *Runtime) AddInfra(ctx context.Context, pod *Pod, infraCtr *Container) (*Pod, error) { + r.lock.Lock() + defer r.lock.Unlock() + + if !r.valid { + return nil, define.ErrRuntimeStopped + } + pod.state.InfraContainerID = infraCtr.ID() + if err := pod.save(); err != nil { + return nil, err } pod.newPodEvent(events.Create) return pod, nil } +// SavePod is a helper function to save the pod state from outside of libpod +func (r *Runtime) SavePod(pod *Pod) error { + r.lock.Lock() + defer r.lock.Unlock() + + if !r.valid { + return define.ErrRuntimeStopped + } + if err := pod.save(); err != nil { + return err + } + pod.newPodEvent(events.Create) + return nil +} + func (r *Runtime) removePod(ctx context.Context, p *Pod, removeCtrs, force bool) error { if err := p.updatePod(); err != nil { return err diff --git a/pkg/api/handlers/compat/containers_create.go b/pkg/api/handlers/compat/containers_create.go index 0b5cbd3436..9df35697a0 100644 --- a/pkg/api/handlers/compat/containers_create.go +++ b/pkg/api/handlers/compat/containers_create.go @@ -11,6 +11,7 @@ import ( "github.com/containers/podman/v3/pkg/domain/entities" "github.com/containers/podman/v3/pkg/domain/infra/abi" "github.com/containers/podman/v3/pkg/specgen" + "github.com/containers/podman/v3/pkg/specgenutil" "github.com/containers/storage" "github.com/gorilla/schema" "github.com/pkg/errors" @@ -80,7 +81,7 @@ func CreateContainer(w http.ResponseWriter, r *http.Request) { } sg := specgen.NewSpecGenerator(imgNameOrID, cliOpts.RootFS) - if err := common.FillOutSpecGen(sg, cliOpts, args); err != nil { + if err := specgenutil.FillOutSpecGen(sg, cliOpts, args); err != nil { utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "fill out specgen")) return } diff --git a/pkg/api/handlers/libpod/containers_create.go b/pkg/api/handlers/libpod/containers_create.go index 65951861bc..0e2163d5cf 100644 --- a/pkg/api/handlers/libpod/containers_create.go +++ b/pkg/api/handlers/libpod/containers_create.go @@ -28,7 +28,12 @@ func CreateContainer(w http.ResponseWriter, r *http.Request) { utils.InternalServerError(w, err) return } - ctr, err := generate.MakeContainer(context.Background(), runtime, &sg) + rtSpec, spec, opts, err := generate.MakeContainer(context.Background(), runtime, &sg) + if err != nil { + utils.InternalServerError(w, err) + return + } + ctr, err := generate.ExecuteCreate(context.Background(), runtime, rtSpec, spec, false, opts...) if err != nil { utils.InternalServerError(w, err) return diff --git a/pkg/api/handlers/libpod/pods.go b/pkg/api/handlers/libpod/pods.go index ff105bc481..3d6cf093b8 100644 --- a/pkg/api/handlers/libpod/pods.go +++ b/pkg/api/handlers/libpod/pods.go @@ -1,11 +1,15 @@ package libpod import ( + "context" "encoding/json" "fmt" "net/http" "strings" + "github.com/containers/common/libimage" + "github.com/containers/common/pkg/config" + "github.com/containers/image/v5/transports/alltransports" "github.com/containers/podman/v3/libpod" "github.com/containers/podman/v3/libpod/define" "github.com/containers/podman/v3/pkg/api/handlers" @@ -14,6 +18,7 @@ import ( "github.com/containers/podman/v3/pkg/domain/infra/abi" "github.com/containers/podman/v3/pkg/specgen" "github.com/containers/podman/v3/pkg/specgen/generate" + "github.com/containers/podman/v3/pkg/specgenutil" "github.com/containers/podman/v3/pkg/util" "github.com/gorilla/schema" "github.com/pkg/errors" @@ -25,24 +30,70 @@ func PodCreate(w http.ResponseWriter, r *http.Request) { runtime = r.Context().Value("runtime").(*libpod.Runtime) err error ) - var psg specgen.PodSpecGenerator + psg := specgen.PodSpecGenerator{InfraContainerSpec: &specgen.SpecGenerator{}} if err := json.NewDecoder(r.Body).Decode(&psg); err != nil { - utils.Error(w, "failed to decode specgen", http.StatusInternalServerError, errors.Wrap(err, "failed to decode specgen")) + utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "failed to decode specgen")) return } - // parse userns so we get the valid default value of userns - psg.Userns, err = specgen.ParseUserNamespace(psg.Userns.String()) if err != nil { - utils.Error(w, "failed to parse userns", http.StatusInternalServerError, errors.Wrap(err, "failed to parse userns")) + utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "failed to decode specgen")) return } - pod, err := generate.MakePod(&psg, runtime) + if !psg.NoInfra { + infraOptions := &entities.ContainerCreateOptions{ImageVolume: "bind", IsInfra: true, Net: &entities.NetOptions{}} // options for pulling the image and FillOutSpec + err = specgenutil.FillOutSpecGen(psg.InfraContainerSpec, infraOptions, []string{}) // necessary for default values in many cases (userns, idmappings) + if err != nil { + utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "error filling out specgen")) + return + } + out, err := json.Marshal(psg) // marshal our spec so the matching options can be unmarshaled into infra + if err != nil { + utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "failed to decode specgen")) + return + } + tempSpec := &specgen.SpecGenerator{} // temporary spec since infra cannot be decoded into + err = json.Unmarshal(out, tempSpec) // unmarhal matching options + if err != nil { + utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "failed to decode specgen")) + return + } + psg.InfraContainerSpec = tempSpec // set infra spec equal to temp + // a few extra that do not have the same json tags + psg.InfraContainerSpec.Name = psg.InfraName + psg.InfraContainerSpec.ConmonPidFile = psg.InfraConmonPidFile + psg.InfraContainerSpec.ContainerCreateCommand = psg.InfraCommand + imageName := psg.InfraImage + rawImageName := psg.InfraImage + if imageName == "" { + imageName = config.DefaultInfraImage + rawImageName = config.DefaultInfraImage + } + curr := infraOptions.Quiet + infraOptions.Quiet = true + pullOptions := &libimage.PullOptions{} + pulledImages, err := runtime.LibimageRuntime().Pull(context.Background(), imageName, config.PullPolicyMissing, pullOptions) + if err != nil { + utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "could not pull image")) + return + } + if _, err := alltransports.ParseImageName(imageName); err == nil { + if len(pulledImages) != 0 { + imageName = pulledImages[0].ID() + } + } + infraOptions.Quiet = curr + psg.InfraImage = imageName + psg.InfraContainerSpec.Image = imageName + psg.InfraContainerSpec.RawImageName = rawImageName + } + podSpecComplete := entities.PodSpec{PodSpecGen: psg} + pod, err := generate.MakePod(&podSpecComplete, runtime) if err != nil { httpCode := http.StatusInternalServerError if errors.Cause(err) == define.ErrPodExists { httpCode = http.StatusConflict } - utils.Error(w, "Something went wrong.", httpCode, err) + utils.Error(w, "Something went wrong.", httpCode, errors.Wrap(err, "failed to make pod")) return } utils.WriteResponse(w, http.StatusCreated, handlers.IDResponse{ID: pod.ID()}) diff --git a/pkg/api/handlers/types/types.go b/pkg/api/handlers/types/types.go index 71165364f4..e7920047ef 100644 --- a/pkg/api/handlers/types/types.go +++ b/pkg/api/handlers/types/types.go @@ -1,8 +1,6 @@ package types -import ( - "github.com/containers/podman/v3/pkg/domain/entities" -) +import "github.com/containers/podman/v3/pkg/domain/entities" // LibpodImagesRemoveReport is the return type for image removal via the rest // api. diff --git a/pkg/bindings/pods/pods.go b/pkg/bindings/pods/pods.go index 9d3ff322ec..a1a431a3b8 100644 --- a/pkg/bindings/pods/pods.go +++ b/pkg/bindings/pods/pods.go @@ -9,27 +9,25 @@ import ( "github.com/containers/podman/v3/pkg/api/handlers" "github.com/containers/podman/v3/pkg/bindings" "github.com/containers/podman/v3/pkg/domain/entities" - "github.com/containers/podman/v3/pkg/specgen" jsoniter "github.com/json-iterator/go" ) -func CreatePodFromSpec(ctx context.Context, s *specgen.PodSpecGenerator, options *CreateOptions) (*entities.PodCreateReport, error) { +func CreatePodFromSpec(ctx context.Context, spec *entities.PodSpec) (*entities.PodCreateReport, error) { var ( pcr entities.PodCreateReport ) - if options == nil { - options = new(CreateOptions) + if spec == nil { + spec = new(entities.PodSpec) } - _ = options conn, err := bindings.GetClient(ctx) if err != nil { return nil, err } - specgenString, err := jsoniter.MarshalToString(s) + specString, err := jsoniter.MarshalToString(spec.PodSpecGen) if err != nil { return nil, err } - stringReader := strings.NewReader(specgenString) + stringReader := strings.NewReader(specString) response, err := conn.DoRequest(stringReader, http.MethodPost, "/pods/create", nil, nil) if err != nil { return nil, err diff --git a/pkg/bindings/test/pods_test.go b/pkg/bindings/test/pods_test.go index b06ff31a2d..5331cf4398 100644 --- a/pkg/bindings/test/pods_test.go +++ b/pkg/bindings/test/pods_test.go @@ -8,6 +8,7 @@ import ( "github.com/containers/podman/v3/libpod/define" "github.com/containers/podman/v3/pkg/bindings" "github.com/containers/podman/v3/pkg/bindings/pods" + "github.com/containers/podman/v3/pkg/domain/entities" "github.com/containers/podman/v3/pkg/specgen" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" @@ -333,9 +334,9 @@ var _ = Describe("Podman pods", func() { }) It("simple create pod", func() { - ps := specgen.PodSpecGenerator{} - ps.Name = "foobar" - _, err := pods.CreatePodFromSpec(bt.conn, &ps, nil) + ps := entities.PodSpec{PodSpecGen: specgen.PodSpecGenerator{InfraContainerSpec: &specgen.SpecGenerator{}}} + ps.PodSpecGen.Name = "foobar" + _, err := pods.CreatePodFromSpec(bt.conn, &ps) Expect(err).To(BeNil()) exists, err := pods.Exists(bt.conn, "foobar", nil) diff --git a/pkg/domain/entities/engine_container.go b/pkg/domain/entities/engine_container.go index 5acf7211c3..bd011d309b 100644 --- a/pkg/domain/entities/engine_container.go +++ b/pkg/domain/entities/engine_container.go @@ -68,7 +68,7 @@ type ContainerEngine interface { NetworkRm(ctx context.Context, namesOrIds []string, options NetworkRmOptions) ([]*NetworkRmReport, error) PlayKube(ctx context.Context, path string, opts PlayKubeOptions) (*PlayKubeReport, error) PlayKubeDown(ctx context.Context, path string, opts PlayKubeDownOptions) (*PlayKubeReport, error) - PodCreate(ctx context.Context, opts PodCreateOptions) (*PodCreateReport, error) + PodCreate(ctx context.Context, specg PodSpec) (*PodCreateReport, error) PodExists(ctx context.Context, nameOrID string) (*BoolReport, error) PodInspect(ctx context.Context, options PodInspectOptions) (*PodInspectReport, error) PodKill(ctx context.Context, namesOrIds []string, options PodKillOptions) ([]*PodKillReport, error) diff --git a/pkg/domain/entities/pods.go b/pkg/domain/entities/pods.go index c66bf96fcd..10bd7e5ce6 100644 --- a/pkg/domain/entities/pods.go +++ b/pkg/domain/entities/pods.go @@ -106,6 +106,14 @@ type PodRmReport struct { Id string //nolint } +// PddSpec is an abstracted version of PodSpecGen designed to eventually accept options +// not meant to be in a specgen +type PodSpec struct { + PodSpecGen specgen.PodSpecGenerator +} + +// PodCreateOptions provides all possible options for creating a pod and its infra container +// swagger:model PodCreateOptions type PodCreateOptions struct { CGroupParent string CreateCommand []string @@ -125,6 +133,123 @@ type PodCreateOptions struct { Userns specgen.Namespace } +type ContainerCreateOptions 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 + PID string + PIDsLimit *int64 + Platform string + Pod string + PodIDFile string + Personality 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 + IsInfra bool + + Net *NetOptions + + CgroupConf []string +} + type PodCreateReport struct { Id string //nolint } @@ -149,21 +274,15 @@ func (p *PodCreateOptions) CPULimits() *specs.LinuxCPU { return cpu } -func setNamespaces(p *PodCreateOptions) ([4]specgen.Namespace, error) { - allNS := [4]specgen.Namespace{} - if p.Pid != "" { - pid, err := specgen.ParseNamespace(p.Pid) - if err != nil { - return [4]specgen.Namespace{}, err - } - allNS[0] = pid - } - return allNS, nil -} - -func (p *PodCreateOptions) ToPodSpecGen(s *specgen.PodSpecGenerator) error { +func ToPodSpecGen(s specgen.PodSpecGenerator, p *PodCreateOptions) (*specgen.PodSpecGenerator, error) { // Basic Config s.Name = p.Name + s.InfraName = p.InfraName + out, err := specgen.ParseNamespace(p.Pid) + if err != nil { + return nil, err + } + s.Pid = out s.Hostname = p.Hostname s.Labels = p.Labels s.NoInfra = !p.Infra @@ -174,32 +293,26 @@ func (p *PodCreateOptions) ToPodSpecGen(s *specgen.PodSpecGenerator) error { s.InfraConmonPidFile = p.InfraConmonPidFile } s.InfraImage = p.InfraImage - s.InfraName = p.InfraName s.SharedNamespaces = p.Share s.PodCreateCommand = p.CreateCommand // Networking config - s.NetNS = p.Net.Network - s.StaticIP = p.Net.StaticIP - s.StaticMAC = p.Net.StaticMAC - s.PortMappings = p.Net.PublishPorts - s.CNINetworks = p.Net.CNINetworks - s.NetworkOptions = p.Net.NetworkOptions - if p.Net.UseImageResolvConf { - s.NoManageResolvConf = true - } - s.DNSServer = p.Net.DNSServers - s.DNSSearch = p.Net.DNSSearch - s.DNSOption = p.Net.DNSOptions - s.NoManageHosts = p.Net.NoHosts - s.HostAdd = p.Net.AddHosts - namespaces, err := setNamespaces(p) - if err != nil { - return err - } - if !namespaces[0].IsDefault() { - s.Pid = namespaces[0] + if p.Net != nil { + s.NetNS = p.Net.Network + s.StaticIP = p.Net.StaticIP + s.StaticMAC = p.Net.StaticMAC + s.PortMappings = p.Net.PublishPorts + s.CNINetworks = p.Net.CNINetworks + s.NetworkOptions = p.Net.NetworkOptions + if p.Net.UseImageResolvConf { + s.NoManageResolvConf = true + } + s.DNSServer = p.Net.DNSServers + s.DNSSearch = p.Net.DNSSearch + s.DNSOption = p.Net.DNSOptions + s.NoManageHosts = p.Net.NoHosts + s.HostAdd = p.Net.AddHosts } // Cgroup @@ -219,7 +332,7 @@ func (p *PodCreateOptions) ToPodSpecGen(s *specgen.PodSpecGenerator) error { } } s.Userns = p.Userns - return nil + return &s, nil } type PodPruneOptions struct { diff --git a/pkg/domain/entities/types.go b/pkg/domain/entities/types.go index db4c6bb8a1..ec4d4a9028 100644 --- a/pkg/domain/entities/types.go +++ b/pkg/domain/entities/types.go @@ -31,21 +31,33 @@ type VolumeDeleteReport struct{ Report } // NetOptions reflect the shared network options between // pods and containers +type NetFlags struct { + AddHosts []string `json:"add-host,omitempty"` + DNS []string `json:"dns,omitempty"` + DNSOpt []string `json:"dns-opt,omitempty"` + DNDSearch []string `json:"dns-search,omitempty"` + MacAddr string `json:"mac-address,omitempty"` + Publish []string `json:"publish,omitempty"` + IP string `json:"ip,omitempty"` + NoHosts bool `json:"no-hosts,omitempty"` + Network string `json:"network,omitempty"` + NetworkAlias []string `json:"network-alias,omitempty"` +} type NetOptions struct { - AddHosts []string - Aliases []string - CNINetworks []string - UseImageResolvConf bool - DNSOptions []string - DNSSearch []string - DNSServers []net.IP - Network specgen.Namespace - NoHosts bool - PublishPorts []types.PortMapping - StaticIP *net.IP - StaticMAC *net.HardwareAddr + AddHosts []string `json:"hostadd,omitempty"` + Aliases []string `json:"network_alias,omitempty"` + CNINetworks []string `json:"cni_networks,omitempty"` + UseImageResolvConf bool `json:"no_manage_resolv_conf,omitempty"` + DNSOptions []string `json:"dns_option,omitempty"` + DNSSearch []string `json:"dns_search,omitempty"` + DNSServers []net.IP `json:"dns_server,omitempty"` + Network specgen.Namespace `json:"netns,omitempty"` + NoHosts bool `json:"no_manage_hosts,omitempty"` + PublishPorts []types.PortMapping `json:"portmappings,omitempty"` + StaticIP *net.IP `json:"static_ip,omitempty"` + StaticMAC *net.HardwareAddr `json:"static_mac,omitempty"` // NetworkOptions are additional options for each network - NetworkOptions map[string][]string + NetworkOptions map[string][]string `json:"network_options,omitempty"` } // All CLI inspect commands and inspect sub-commands use the same options diff --git a/pkg/domain/infra/abi/containers.go b/pkg/domain/infra/abi/containers.go index a74b65ab9a..8b2a5bfaed 100644 --- a/pkg/domain/infra/abi/containers.go +++ b/pkg/domain/infra/abi/containers.go @@ -583,7 +583,11 @@ func (ic *ContainerEngine) ContainerCreate(ctx context.Context, s *specgen.SpecG for _, w := range warn { fmt.Fprintf(os.Stderr, "%s\n", w) } - ctr, err := generate.MakeContainer(ctx, ic.Libpod, s) + rtSpec, spec, opts, err := generate.MakeContainer(context.Background(), ic.Libpod, s) + if err != nil { + return nil, err + } + ctr, err := generate.ExecuteCreate(ctx, ic.Libpod, rtSpec, spec, false, opts...) if err != nil { return nil, err } @@ -915,7 +919,11 @@ func (ic *ContainerEngine) ContainerRun(ctx context.Context, opts entities.Conta for _, w := range warn { fmt.Fprintf(os.Stderr, "%s\n", w) } - ctr, err := generate.MakeContainer(ctx, ic.Libpod, opts.Spec) + rtSpec, spec, optsN, err := generate.MakeContainer(ctx, ic.Libpod, opts.Spec) + if err != nil { + return nil, err + } + ctr, err := generate.ExecuteCreate(ctx, ic.Libpod, rtSpec, spec, false, optsN...) if err != nil { return nil, err } diff --git a/pkg/domain/infra/abi/generate.go b/pkg/domain/infra/abi/generate.go index b0853b5545..2d7bc15f54 100644 --- a/pkg/domain/infra/abi/generate.go +++ b/pkg/domain/infra/abi/generate.go @@ -60,9 +60,7 @@ func (ic *ContainerEngine) GenerateKube(ctx context.Context, nameOrIDs []string, return nil, err } } else { - if len(ctr.Dependencies()) > 0 { - return nil, errors.Wrapf(define.ErrNotImplemented, "containers with dependencies") - } + // now that infra holds NS data, we need to support dependencies. // we cannot deal with ctrs already in a pod. if len(ctr.PodID()) > 0 { return nil, errors.Errorf("container %s is associated with pod %s: use generate on the pod itself", ctr.ID(), ctr.PodID()) diff --git a/pkg/domain/infra/abi/play.go b/pkg/domain/infra/abi/play.go index f22b2dbbb5..2799df794e 100644 --- a/pkg/domain/infra/abi/play.go +++ b/pkg/domain/infra/abi/play.go @@ -6,6 +6,7 @@ import ( "fmt" "io" "io/ioutil" + "net" "os" "path/filepath" "strconv" @@ -22,6 +23,7 @@ import ( "github.com/containers/podman/v3/pkg/specgen" "github.com/containers/podman/v3/pkg/specgen/generate" "github.com/containers/podman/v3/pkg/specgen/generate/kube" + "github.com/containers/podman/v3/pkg/specgenutil" "github.com/containers/podman/v3/pkg/util" "github.com/ghodss/yaml" "github.com/pkg/errors" @@ -179,10 +181,12 @@ func (ic *ContainerEngine) playKubePod(ctx context.Context, podName string, podY } } - p, err := kube.ToPodGen(ctx, podName, podYAML) + podOpt := entities.PodCreateOptions{Infra: true, Net: &entities.NetOptions{StaticIP: &net.IP{}, StaticMAC: &net.HardwareAddr{}}} + podOpt, err = kube.ToPodOpt(ctx, podName, podOpt, podYAML) if err != nil { return nil, err } + if options.Network != "" { ns, cniNets, netOpts, err := specgen.ParseNetworkString(options.Network) if err != nil { @@ -193,42 +197,37 @@ func (ic *ContainerEngine) playKubePod(ctx context.Context, podName string, podY return nil, errors.Errorf("invalid value passed to --network: bridge or host networking must be configured in YAML") } logrus.Debugf("Pod %q joining CNI networks: %v", podName, cniNets) - p.NetNS.NSMode = specgen.Bridge - p.CNINetworks = append(p.CNINetworks, cniNets...) + podOpt.Net.Network.NSMode = specgen.Bridge + podOpt.Net.CNINetworks = append(podOpt.Net.CNINetworks, cniNets...) if len(netOpts) > 0 { - p.NetworkOptions = netOpts + podOpt.Net.NetworkOptions = netOpts } } if len(options.StaticIPs) > *ipIndex { - p.StaticIP = &options.StaticIPs[*ipIndex] + podOpt.Net.StaticIP = &options.StaticIPs[*ipIndex] } else if len(options.StaticIPs) > 0 { // only warn if the user has set at least one ip logrus.Warn("No more static ips left using a random one") } if len(options.StaticMACs) > *ipIndex { - p.StaticMAC = &options.StaticMACs[*ipIndex] + podOpt.Net.StaticMAC = &options.StaticMACs[*ipIndex] } else if len(options.StaticIPs) > 0 { // only warn if the user has set at least one mac logrus.Warn("No more static macs left using a random one") } *ipIndex++ - // Create the Pod - pod, err := generate.MakePod(p, ic.Libpod) + p := specgen.NewPodSpecGenerator() if err != nil { return nil, err } - podInfraID, err := pod.InfraContainerID() + p, err = entities.ToPodSpecGen(*p, &podOpt) if err != nil { return nil, err } - - if !options.Quiet { - writer = os.Stderr - } - + podSpec := entities.PodSpec{PodSpecGen: *p} volumes, err := kube.InitializeVolumes(podYAML.Spec.Volumes) if err != nil { return nil, err @@ -267,112 +266,146 @@ func (ic *ContainerEngine) playKubePod(ctx context.Context, podName string, podY configMaps = append(configMaps, cm) } - containers := make([]*libpod.Container, 0, len(podYAML.Spec.Containers)) - cwd, err := os.Getwd() - if err != nil { - return nil, err - } - for _, container := range podYAML.Spec.Containers { - // Contains all labels obtained from kube - labels := make(map[string]string) - var pulledImage *libimage.Image - buildFile, err := getBuildFile(container.Image, cwd) + if podOpt.Infra { + imagePull := config.DefaultInfraImage + if podOpt.InfraImage != config.DefaultInfraImage && podOpt.InfraImage != "" { + imagePull = podOpt.InfraImage + } + + pulledImages, err := pullImage(ic, writer, imagePull, options, config.PullPolicyNewer) if err != nil { return nil, err } - existsLocally, err := ic.Libpod.LibimageRuntime().Exists(container.Image) + infraOptions := entities.ContainerCreateOptions{ImageVolume: "bind"} + + podSpec.PodSpecGen.InfraImage = pulledImages[0].Names()[0] + podSpec.PodSpecGen.NoInfra = false + podSpec.PodSpecGen.InfraContainerSpec = specgen.NewSpecGenerator(pulledImages[0].Names()[0], false) + podSpec.PodSpecGen.InfraContainerSpec.NetworkOptions = p.NetworkOptions + + err = specgenutil.FillOutSpecGen(podSpec.PodSpecGen.InfraContainerSpec, &infraOptions, []string{}) if err != nil { return nil, err } - if (len(buildFile) > 0 && !existsLocally) || (len(buildFile) > 0 && options.Build) { - buildOpts := new(buildahDefine.BuildOptions) - commonOpts := new(buildahDefine.CommonBuildOptions) - buildOpts.ConfigureNetwork = buildahDefine.NetworkDefault - buildOpts.Isolation = buildahDefine.IsolationChroot - buildOpts.CommonBuildOpts = commonOpts - buildOpts.Output = container.Image - if _, _, err := ic.Libpod.Build(ctx, *buildOpts, []string{buildFile}...); err != nil { + } + + // Create the Pod + pod, err := generate.MakePod(&podSpec, ic.Libpod) + if err != nil { + return nil, err + } + + podInfraID, err := pod.InfraContainerID() + if err != nil { + return nil, err + } + + if !options.Quiet { + writer = os.Stderr + } + + containers := make([]*libpod.Container, 0, len(podYAML.Spec.Containers)) + cwd, err := os.Getwd() + if err != nil { + return nil, err + } + for _, container := range podYAML.Spec.Containers { + if !strings.Contains("infra", container.Name) { + // Contains all labels obtained from kube + labels := make(map[string]string) + var pulledImage *libimage.Image + buildFile, err := getBuildFile(container.Image, cwd) + if err != nil { return nil, err } - i, _, err := ic.Libpod.LibimageRuntime().LookupImage(container.Image, new(libimage.LookupImageOptions)) + existsLocally, err := ic.Libpod.LibimageRuntime().Exists(container.Image) if err != nil { return nil, err } - pulledImage = i - } else { - // NOTE: set the pull policy to "newer". This will cover cases - // where the "latest" tag requires a pull and will also - // transparently handle "localhost/" prefixed files which *may* - // refer to a locally built image OR an image running a - // registry on localhost. - pullPolicy := config.PullPolicyNewer - if len(container.ImagePullPolicy) > 0 { - // Make sure to lower the strings since K8s pull policy - // may be capitalized (see bugzilla.redhat.com/show_bug.cgi?id=1985905). - rawPolicy := string(container.ImagePullPolicy) - pullPolicy, err = config.ParsePullPolicy(strings.ToLower(rawPolicy)) + if (len(buildFile) > 0 && !existsLocally) || (len(buildFile) > 0 && options.Build) { + buildOpts := new(buildahDefine.BuildOptions) + commonOpts := new(buildahDefine.CommonBuildOptions) + buildOpts.ConfigureNetwork = buildahDefine.NetworkDefault + buildOpts.Isolation = buildahDefine.IsolationChroot + buildOpts.CommonBuildOpts = commonOpts + buildOpts.Output = container.Image + if _, _, err := ic.Libpod.Build(ctx, *buildOpts, []string{buildFile}...); err != nil { + return nil, err + } + i, _, err := ic.Libpod.LibimageRuntime().LookupImage(container.Image, new(libimage.LookupImageOptions)) + if err != nil { + return nil, err + } + pulledImage = i + } else { + // NOTE: set the pull policy to "newer". This will cover cases + // where the "latest" tag requires a pull and will also + // transparently handle "localhost/" prefixed files which *may* + // refer to a locally built image OR an image running a + // registry on localhost. + pullPolicy := config.PullPolicyNewer + if len(container.ImagePullPolicy) > 0 { + // Make sure to lower the strings since K8s pull policy + // may be capitalized (see bugzilla.redhat.com/show_bug.cgi?id=1985905). + rawPolicy := string(container.ImagePullPolicy) + pullPolicy, err = config.ParsePullPolicy(strings.ToLower(rawPolicy)) + if err != nil { + return nil, err + } + } + pulledImages, err := pullImage(ic, writer, container.Image, options, pullPolicy) if err != nil { return nil, err } + pulledImage = pulledImages[0] + } + + // Handle kube annotations + for k, v := range annotations { + switch k { + // Auto update annotation without container name will apply to + // all containers within the pod + case autoupdate.Label, autoupdate.AuthfileLabel: + labels[k] = v + // Auto update annotation with container name will apply only + // to the specified container + case fmt.Sprintf("%s/%s", autoupdate.Label, container.Name), + fmt.Sprintf("%s/%s", autoupdate.AuthfileLabel, container.Name): + prefixAndCtr := strings.Split(k, "/") + labels[prefixAndCtr[0]] = v + } } - // This ensures the image is the image store - pullOptions := &libimage.PullOptions{} - pullOptions.AuthFilePath = options.Authfile - pullOptions.CertDirPath = options.CertDir - pullOptions.SignaturePolicyPath = options.SignaturePolicy - pullOptions.Writer = writer - pullOptions.Username = options.Username - pullOptions.Password = options.Password - pullOptions.InsecureSkipTLSVerify = options.SkipTLSVerify - - pulledImages, err := ic.Libpod.LibimageRuntime().Pull(ctx, container.Image, pullPolicy, pullOptions) + + specgenOpts := kube.CtrSpecGenOptions{ + Container: container, + Image: pulledImage, + Volumes: volumes, + PodID: pod.ID(), + PodName: podName, + PodInfraID: podInfraID, + ConfigMaps: configMaps, + SeccompPaths: seccompPaths, + RestartPolicy: ctrRestartPolicy, + NetNSIsHost: p.NetNS.IsHost(), + SecretsManager: secretsManager, + LogDriver: options.LogDriver, + Labels: labels, + } + specGen, err := kube.ToSpecGen(ctx, &specgenOpts) if err != nil { return nil, err } - pulledImage = pulledImages[0] - } - // Handle kube annotations - for k, v := range annotations { - switch k { - // Auto update annotation without container name will apply to - // all containers within the pod - case autoupdate.Label, autoupdate.AuthfileLabel: - labels[k] = v - // Auto update annotation with container name will apply only - // to the specified container - case fmt.Sprintf("%s/%s", autoupdate.Label, container.Name), - fmt.Sprintf("%s/%s", autoupdate.AuthfileLabel, container.Name): - prefixAndCtr := strings.Split(k, "/") - labels[prefixAndCtr[0]] = v + rtSpec, spec, opts, err := generate.MakeContainer(ctx, ic.Libpod, specGen) + if err != nil { + return nil, err } + ctr, err := generate.ExecuteCreate(ctx, ic.Libpod, rtSpec, spec, false, opts...) + if err != nil { + return nil, err + } + containers = append(containers, ctr) } - - specgenOpts := kube.CtrSpecGenOptions{ - Container: container, - Image: pulledImage, - Volumes: volumes, - PodID: pod.ID(), - PodName: podName, - PodInfraID: podInfraID, - ConfigMaps: configMaps, - SeccompPaths: seccompPaths, - RestartPolicy: ctrRestartPolicy, - NetNSIsHost: p.NetNS.IsHost(), - SecretsManager: secretsManager, - LogDriver: options.LogDriver, - Labels: labels, - } - specGen, err := kube.ToSpecGen(ctx, &specgenOpts) - if err != nil { - return nil, err - } - - ctr, err := generate.MakeContainer(ctx, ic.Libpod, specGen) - if err != nil { - return nil, err - } - containers = append(containers, ctr) } if options.Start != types.OptionalBoolFalse { @@ -383,6 +416,7 @@ func (ic *ContainerEngine) playKubePod(ctx context.Context, podName string, podY } for id, err := range podStartErrors { playKubePod.ContainerErrors = append(playKubePod.ContainerErrors, errors.Wrapf(err, "error starting container %s", id).Error()) + fmt.Println(playKubePod.ContainerErrors) } } @@ -656,3 +690,21 @@ func (ic *ContainerEngine) PlayKubeDown(ctx context.Context, path string, _ enti } return reports, nil } + +// pullImage is a helper function to set up the proper pull options and pull the image for certain containers +func pullImage(ic *ContainerEngine, writer io.Writer, imagePull string, options entities.PlayKubeOptions, pullPolicy config.PullPolicy) ([]*libimage.Image, error) { + // This ensures the image is the image store + pullOptions := &libimage.PullOptions{} + pullOptions.AuthFilePath = options.Authfile + pullOptions.CertDirPath = options.CertDir + pullOptions.SignaturePolicyPath = options.SignaturePolicy + pullOptions.Writer = writer + pullOptions.Username = options.Username + pullOptions.Password = options.Password + pullOptions.InsecureSkipTLSVerify = options.SkipTLSVerify + pulledImages, err := ic.Libpod.LibimageRuntime().Pull(context.Background(), imagePull, pullPolicy, pullOptions) + if err != nil { + return nil, err + } + return pulledImages, nil +} diff --git a/pkg/domain/infra/abi/pods.go b/pkg/domain/infra/abi/pods.go index 055c495d50..98233f60d1 100644 --- a/pkg/domain/infra/abi/pods.go +++ b/pkg/domain/infra/abi/pods.go @@ -8,7 +8,6 @@ import ( "github.com/containers/podman/v3/pkg/domain/entities" dfilters "github.com/containers/podman/v3/pkg/domain/filters" "github.com/containers/podman/v3/pkg/signal" - "github.com/containers/podman/v3/pkg/specgen" "github.com/containers/podman/v3/pkg/specgen/generate" "github.com/pkg/errors" "github.com/sirupsen/logrus" @@ -248,12 +247,8 @@ func (ic *ContainerEngine) prunePodHelper(ctx context.Context) ([]*entities.PodP return reports, nil } -func (ic *ContainerEngine) PodCreate(ctx context.Context, opts entities.PodCreateOptions) (*entities.PodCreateReport, error) { - podSpec := specgen.NewPodSpecGenerator() - if err := opts.ToPodSpecGen(podSpec); err != nil { - return nil, err - } - pod, err := generate.MakePod(podSpec, ic.Libpod) +func (ic *ContainerEngine) PodCreate(ctx context.Context, specg entities.PodSpec) (*entities.PodCreateReport, error) { + pod, err := generate.MakePod(&specg, ic.Libpod) if err != nil { return nil, err } diff --git a/pkg/domain/infra/tunnel/events.go b/pkg/domain/infra/tunnel/events.go index 6e2c3f8ba8..203550c5d2 100644 --- a/pkg/domain/infra/tunnel/events.go +++ b/pkg/domain/infra/tunnel/events.go @@ -7,6 +7,7 @@ import ( "github.com/containers/podman/v3/libpod/events" "github.com/containers/podman/v3/pkg/bindings/system" "github.com/containers/podman/v3/pkg/domain/entities" + "github.com/pkg/errors" ) diff --git a/pkg/domain/infra/tunnel/pods.go b/pkg/domain/infra/tunnel/pods.go index 82f062b2c5..480adb88a3 100644 --- a/pkg/domain/infra/tunnel/pods.go +++ b/pkg/domain/infra/tunnel/pods.go @@ -6,7 +6,6 @@ import ( "github.com/containers/podman/v3/libpod/define" "github.com/containers/podman/v3/pkg/bindings/pods" "github.com/containers/podman/v3/pkg/domain/entities" - "github.com/containers/podman/v3/pkg/specgen" "github.com/containers/podman/v3/pkg/util" "github.com/pkg/errors" ) @@ -179,10 +178,8 @@ func (ic *ContainerEngine) PodPrune(ctx context.Context, opts entities.PodPruneO return pods.Prune(ic.ClientCtx, nil) } -func (ic *ContainerEngine) PodCreate(ctx context.Context, opts entities.PodCreateOptions) (*entities.PodCreateReport, error) { - podSpec := specgen.NewPodSpecGenerator() - opts.ToPodSpecGen(podSpec) - return pods.CreatePodFromSpec(ic.ClientCtx, podSpec, nil) +func (ic *ContainerEngine) PodCreate(ctx context.Context, specg entities.PodSpec) (*entities.PodCreateReport, error) { + return pods.CreatePodFromSpec(ic.ClientCtx, &specg) } func (ic *ContainerEngine) PodTop(ctx context.Context, opts entities.PodTopOptions) (*entities.StringSliceReport, error) { diff --git a/pkg/specgen/generate/container_create.go b/pkg/specgen/generate/container_create.go index 5101a6ccbd..f82b2a3c6a 100644 --- a/pkg/specgen/generate/container_create.go +++ b/pkg/specgen/generate/container_create.go @@ -22,10 +22,10 @@ import ( // MakeContainer creates a container based on the SpecGenerator. // Returns the created, container and any warnings resulting from creating the // container, or an error. -func MakeContainer(ctx context.Context, rt *libpod.Runtime, s *specgen.SpecGenerator) (*libpod.Container, error) { +func MakeContainer(ctx context.Context, rt *libpod.Runtime, s *specgen.SpecGenerator) (*spec.Spec, *specgen.SpecGenerator, []libpod.CtrCreateOption, error) { rtc, err := rt.GetConfig() if err != nil { - return nil, err + return nil, nil, nil, err } // If joining a pod, retrieve the pod for use. @@ -33,7 +33,7 @@ func MakeContainer(ctx context.Context, rt *libpod.Runtime, s *specgen.SpecGener if s.Pod != "" { pod, err = rt.LookupPod(s.Pod) if err != nil { - return nil, errors.Wrapf(err, "error retrieving pod %s", s.Pod) + return nil, nil, nil, errors.Wrapf(err, "error retrieving pod %s", s.Pod) } } @@ -41,47 +41,48 @@ func MakeContainer(ctx context.Context, rt *libpod.Runtime, s *specgen.SpecGener if s.PidNS.IsDefault() { defaultNS, err := GetDefaultNamespaceMode("pid", rtc, pod) if err != nil { - return nil, err + return nil, nil, nil, err } s.PidNS = defaultNS } if s.IpcNS.IsDefault() { defaultNS, err := GetDefaultNamespaceMode("ipc", rtc, pod) if err != nil { - return nil, err + return nil, nil, nil, err } s.IpcNS = defaultNS } if s.UtsNS.IsDefault() { defaultNS, err := GetDefaultNamespaceMode("uts", rtc, pod) if err != nil { - return nil, err + return nil, nil, nil, err } s.UtsNS = defaultNS } if s.UserNS.IsDefault() { defaultNS, err := GetDefaultNamespaceMode("user", rtc, pod) if err != nil { - return nil, err + return nil, nil, nil, err } s.UserNS = defaultNS } if s.NetNS.IsDefault() { defaultNS, err := GetDefaultNamespaceMode("net", rtc, pod) if err != nil { - return nil, err + return nil, nil, nil, err } s.NetNS = defaultNS } if s.CgroupNS.IsDefault() { defaultNS, err := GetDefaultNamespaceMode("cgroup", rtc, pod) if err != nil { - return nil, err + return nil, nil, nil, err } s.CgroupNS = defaultNS } options := []libpod.CtrCreateOption{} + if s.ContainerCreateCommand != nil { options = append(options, libpod.WithCreateCommand(s.ContainerCreateCommand)) } @@ -94,12 +95,11 @@ func MakeContainer(ctx context.Context, rt *libpod.Runtime, s *specgen.SpecGener var resolvedImageName string newImage, resolvedImageName, err = rt.LibimageRuntime().LookupImage(s.Image, nil) if err != nil { - return nil, err + return nil, nil, nil, err } - imageData, err = newImage.Inspect(ctx, false) if err != nil { - return nil, err + return nil, nil, nil, err } // If the input name changed, we could properly resolve the // image. Otherwise, it must have been an ID where we're @@ -115,29 +115,32 @@ func MakeContainer(ctx context.Context, rt *libpod.Runtime, s *specgen.SpecGener options = append(options, libpod.WithRootFSFromImage(newImage.ID(), resolvedImageName, s.RawImageName)) } if err := s.Validate(); err != nil { - return nil, errors.Wrap(err, "invalid config provided") + return nil, nil, nil, errors.Wrap(err, "invalid config provided") } finalMounts, finalVolumes, finalOverlays, err := finalizeMounts(ctx, s, rt, rtc, newImage) if err != nil { - return nil, err + return nil, nil, nil, err } command, err := makeCommand(ctx, s, imageData, rtc) if err != nil { - return nil, err + return nil, nil, nil, err } opts, err := createContainerOptions(ctx, rt, s, pod, finalVolumes, finalOverlays, imageData, command) if err != nil { - return nil, err + return nil, nil, nil, err } options = append(options, opts...) - exitCommandArgs, err := CreateExitCommandArgs(rt.StorageConfig(), rtc, logrus.IsLevelEnabled(logrus.DebugLevel), s.Remove, false) + var exitCommandArgs []string + + exitCommandArgs, err = CreateExitCommandArgs(rt.StorageConfig(), rtc, logrus.IsLevelEnabled(logrus.DebugLevel), s.Remove, false) if err != nil { - return nil, err + return nil, nil, nil, err } + options = append(options, libpod.WithExitCommand(exitCommandArgs)) if len(s.Aliases) > 0 { @@ -147,23 +150,26 @@ func MakeContainer(ctx context.Context, rt *libpod.Runtime, s *specgen.SpecGener if containerType := s.InitContainerType; len(containerType) > 0 { options = append(options, libpod.WithInitCtrType(containerType)) } - + if len(s.Name) > 0 { + logrus.Debugf("setting container name %s", s.Name) + options = append(options, libpod.WithName(s.Name)) + } if len(s.Devices) > 0 { opts = extractCDIDevices(s) options = append(options, opts...) } runtimeSpec, err := SpecGenToOCI(ctx, s, rt, rtc, newImage, finalMounts, pod, command) if err != nil { - return nil, err + return nil, nil, nil, err } - - ctr, err := rt.NewContainer(ctx, runtimeSpec, options...) + return runtimeSpec, s, options, err +} +func ExecuteCreate(ctx context.Context, rt *libpod.Runtime, runtimeSpec *spec.Spec, s *specgen.SpecGenerator, infra bool, options ...libpod.CtrCreateOption) (*libpod.Container, error) { + ctr, err := rt.NewContainer(ctx, runtimeSpec, s, infra, options...) if err != nil { return ctr, err } - // Copy the content from the underlying image into the newly created - // volume if configured to do so. return ctr, rt.PrepareVolumeOnCreateContainer(ctx, ctr) } @@ -256,11 +262,6 @@ func createContainerOptions(ctx context.Context, rt *libpod.Runtime, s *specgen. if len(s.SdNotifyMode) > 0 { options = append(options, libpod.WithSdNotifyMode(s.SdNotifyMode)) } - - if len(s.Name) > 0 { - logrus.Debugf("setting container name %s", s.Name) - options = append(options, libpod.WithName(s.Name)) - } if pod != nil { logrus.Debugf("adding container to pod %s", pod.Name()) options = append(options, rt.WithPod(pod)) @@ -379,11 +380,11 @@ func createContainerOptions(ctx context.Context, rt *libpod.Runtime, s *specgen. options = append(options, libpod.WithPrivileged(s.Privileged)) // Get namespace related options - namespaceOptions, err := namespaceOptions(ctx, s, rt, pod, imageData) + namespaceOpts, err := namespaceOptions(ctx, s, rt, pod, imageData) if err != nil { return nil, err } - options = append(options, namespaceOptions...) + options = append(options, namespaceOpts...) if len(s.ConmonPidFile) > 0 { options = append(options, libpod.WithConmonPidFile(s.ConmonPidFile)) diff --git a/pkg/specgen/generate/kube/kube.go b/pkg/specgen/generate/kube/kube.go index 04b4e5ab36..5188abc3a4 100644 --- a/pkg/specgen/generate/kube/kube.go +++ b/pkg/specgen/generate/kube/kube.go @@ -14,6 +14,7 @@ import ( "github.com/containers/image/v5/manifest" "github.com/containers/podman/v3/libpod/network/types" ann "github.com/containers/podman/v3/pkg/annotations" + "github.com/containers/podman/v3/pkg/domain/entities" "github.com/containers/podman/v3/pkg/specgen" "github.com/containers/podman/v3/pkg/specgen/generate" "github.com/containers/podman/v3/pkg/util" @@ -23,25 +24,26 @@ import ( "k8s.io/apimachinery/pkg/api/resource" ) -func ToPodGen(ctx context.Context, podName string, podYAML *v1.PodTemplateSpec) (*specgen.PodSpecGenerator, error) { - p := specgen.NewPodSpecGenerator() +func ToPodOpt(ctx context.Context, podName string, p entities.PodCreateOptions, podYAML *v1.PodTemplateSpec) (entities.PodCreateOptions, error) { + // p := specgen.NewPodSpecGenerator() + p.Net = &entities.NetOptions{} p.Name = podName p.Labels = podYAML.ObjectMeta.Labels // Kube pods must share {ipc, net, uts} by default - p.SharedNamespaces = append(p.SharedNamespaces, "ipc") - p.SharedNamespaces = append(p.SharedNamespaces, "net") - p.SharedNamespaces = append(p.SharedNamespaces, "uts") + p.Share = append(p.Share, "ipc") + p.Share = append(p.Share, "net") + p.Share = append(p.Share, "uts") // TODO we only configure Process namespace. We also need to account for Host{IPC,Network,PID} // which is not currently possible with pod create if podYAML.Spec.ShareProcessNamespace != nil && *podYAML.Spec.ShareProcessNamespace { - p.SharedNamespaces = append(p.SharedNamespaces, "pid") + p.Share = append(p.Share, "pid") } p.Hostname = podYAML.Spec.Hostname if p.Hostname == "" { p.Hostname = podName } if podYAML.Spec.HostNetwork { - p.NetNS.NSMode = specgen.Host + p.Net.Network = specgen.Namespace{NSMode: "host"} } if podYAML.Spec.HostAliases != nil { hosts := make([]string, 0, len(podYAML.Spec.HostAliases)) @@ -50,10 +52,10 @@ func ToPodGen(ctx context.Context, podName string, podYAML *v1.PodTemplateSpec) hosts = append(hosts, host+":"+hostAlias.IP) } } - p.HostAdd = hosts + p.Net.AddHosts = hosts } podPorts := getPodPorts(podYAML.Spec.Containers) - p.PortMappings = podPorts + p.Net.PublishPorts = podPorts if dnsConfig := podYAML.Spec.DNSConfig; dnsConfig != nil { // name servers @@ -62,11 +64,11 @@ func ToPodGen(ctx context.Context, podName string, podYAML *v1.PodTemplateSpec) for _, server := range dnsServers { servers = append(servers, net.ParseIP(server)) } - p.DNSServer = servers + p.Net.DNSServers = servers } // search domains if domains := dnsConfig.Searches; len(domains) > 0 { - p.DNSSearch = domains + p.Net.DNSSearch = domains } // dns options if options := dnsConfig.Options; len(options) > 0 { @@ -110,6 +112,8 @@ type CtrSpecGenOptions struct { LogDriver string // Labels define key-value pairs of metadata Labels map[string]string + // + IsInfra bool } func ToSpecGen(ctx context.Context, opts *CtrSpecGenOptions) (*specgen.SpecGenerator, error) { @@ -216,19 +220,19 @@ func ToSpecGen(ctx context.Context, opts *CtrSpecGenOptions) (*specgen.SpecGener } } // If only the yaml.Command is specified, set it as the entrypoint and drop the image Cmd - if len(opts.Container.Command) != 0 { + if !opts.IsInfra && len(opts.Container.Command) != 0 { s.Entrypoint = opts.Container.Command s.Command = []string{} } // Only override the cmd field if yaml.Args is specified // Keep the image entrypoint, or the yaml.command if specified - if len(opts.Container.Args) != 0 { + if !opts.IsInfra && len(opts.Container.Args) != 0 { s.Command = opts.Container.Args } // FIXME, // we are currently ignoring imageData.Config.ExposedPorts - if opts.Container.WorkingDir != "" { + if !opts.IsInfra && opts.Container.WorkingDir != "" { s.WorkDir = opts.Container.WorkingDir } diff --git a/pkg/specgen/generate/namespaces.go b/pkg/specgen/generate/namespaces.go index 80790dcc18..5349e224f3 100644 --- a/pkg/specgen/generate/namespaces.go +++ b/pkg/specgen/generate/namespaces.go @@ -250,7 +250,7 @@ func namespaceOptions(ctx context.Context, s *specgen.SpecGenerator, rt *libpod. if s.NetNS.Value != "" { val = fmt.Sprintf("slirp4netns:%s", s.NetNS.Value) } - toReturn = append(toReturn, libpod.WithNetNS(portMappings, expose, postConfigureNetNS, val, nil)) + toReturn = append(toReturn, libpod.WithNetNS(portMappings, expose, postConfigureNetNS, val, s.CNINetworks)) case specgen.Private: fallthrough case specgen.Bridge: diff --git a/pkg/specgen/generate/oci.go b/pkg/specgen/generate/oci.go index 1f3f9e8321..80c7f112f1 100644 --- a/pkg/specgen/generate/oci.go +++ b/pkg/specgen/generate/oci.go @@ -201,7 +201,8 @@ func SpecGenToOCI(ctx context.Context, s *specgen.SpecGenerator, rt *libpod.Runt Options: []string{"rprivate", "nosuid", "noexec", "nodev", "rw"}, } g.AddMount(sysMnt) - } else if !canMountSys { + } + if !canMountSys { addCgroup = false g.RemoveMount("/sys") r := "ro" diff --git a/pkg/specgen/generate/pod_create.go b/pkg/specgen/generate/pod_create.go index 426cf1b6de..e523aef420 100644 --- a/pkg/specgen/generate/pod_create.go +++ b/pkg/specgen/generate/pod_create.go @@ -2,53 +2,82 @@ package generate import ( "context" + "net" + "github.com/containers/common/pkg/config" "github.com/containers/podman/v3/libpod" + "github.com/containers/podman/v3/libpod/define" + "github.com/containers/podman/v3/pkg/domain/entities" "github.com/containers/podman/v3/pkg/rootless" "github.com/containers/podman/v3/pkg/specgen" "github.com/pkg/errors" "github.com/sirupsen/logrus" ) -func MakePod(p *specgen.PodSpecGenerator, rt *libpod.Runtime) (*libpod.Pod, error) { - if err := p.Validate(); err != nil { +func MakePod(p *entities.PodSpec, rt *libpod.Runtime) (*libpod.Pod, error) { + if err := p.PodSpecGen.Validate(); err != nil { return nil, err } - options, err := createPodOptions(p, rt) + if !p.PodSpecGen.NoInfra && p.PodSpecGen.InfraContainerSpec != nil { + var err error + p.PodSpecGen.InfraContainerSpec, err = MapSpec(&p.PodSpecGen) + if err != nil { + return nil, err + } + } + + options, err := createPodOptions(&p.PodSpecGen, rt, p.PodSpecGen.InfraContainerSpec) if err != nil { return nil, err } - return rt.NewPod(context.Background(), options...) + pod, err := rt.NewPod(context.Background(), p.PodSpecGen, options...) + if err != nil { + return nil, err + } + if !p.PodSpecGen.NoInfra && p.PodSpecGen.InfraContainerSpec != nil { + p.PodSpecGen.InfraContainerSpec.ContainerCreateCommand = []string{} // we do NOT want os.Args as the command, will display the pod create cmd + if p.PodSpecGen.InfraContainerSpec.Name == "" { + p.PodSpecGen.InfraContainerSpec.Name = pod.ID()[:12] + "-infra" + } + _, err = CompleteSpec(context.Background(), rt, p.PodSpecGen.InfraContainerSpec) + if err != nil { + return nil, err + } + p.PodSpecGen.InfraContainerSpec.User = "" // infraSpec user will get incorrectly assigned via the container creation process, overwrite here + rtSpec, spec, opts, err := MakeContainer(context.Background(), rt, p.PodSpecGen.InfraContainerSpec) + if err != nil { + return nil, err + } + spec.Pod = pod.ID() + opts = append(opts, rt.WithPod(pod)) + spec.CgroupParent = pod.CgroupParent() + infraCtr, err := ExecuteCreate(context.Background(), rt, rtSpec, spec, true, opts...) + if err != nil { + return nil, err + } + pod, err = rt.AddInfra(context.Background(), pod, infraCtr) + if err != nil { + return nil, err + } + } + return pod, nil } -func createPodOptions(p *specgen.PodSpecGenerator, rt *libpod.Runtime) ([]libpod.PodCreateOption, error) { +func createPodOptions(p *specgen.PodSpecGenerator, rt *libpod.Runtime, infraSpec *specgen.SpecGenerator) ([]libpod.PodCreateOption, error) { var ( options []libpod.PodCreateOption ) - if !p.NoInfra { + if !p.NoInfra { //&& infraSpec != nil { options = append(options, libpod.WithInfraContainer()) - nsOptions, err := GetNamespaceOptions(p.SharedNamespaces, p.NetNS.IsHost()) + nsOptions, err := GetNamespaceOptions(p.SharedNamespaces, p.InfraContainerSpec.NetNS.IsHost()) if err != nil { return nil, err } options = append(options, nsOptions...) // Use pod user and infra userns only when --userns is not set to host - if !p.Userns.IsHost() { + if !p.InfraContainerSpec.UserNS.IsHost() && !p.InfraContainerSpec.UserNS.IsDefault() { options = append(options, libpod.WithPodUser()) - options = append(options, libpod.WithPodUserns(p.Userns)) } - - // Make our exit command - storageConfig := rt.StorageConfig() - runtimeConfig, err := rt.GetConfig() - if err != nil { - return nil, err - } - exitCommand, err := CreateExitCommandArgs(storageConfig, runtimeConfig, logrus.IsLevelEnabled(logrus.DebugLevel), false, false) - if err != nil { - return nil, errors.Wrapf(err, "error creating infra container exit command") - } - options = append(options, libpod.WithPodInfraExitCommand(exitCommand)) } if len(p.CgroupParent) > 0 { options = append(options, libpod.WithPodCgroupParent(p.CgroupParent)) @@ -59,62 +88,27 @@ func createPodOptions(p *specgen.PodSpecGenerator, rt *libpod.Runtime) ([]libpod if len(p.Name) > 0 { options = append(options, libpod.WithPodName(p.Name)) } - if p.ResourceLimits != nil && p.ResourceLimits.CPU != nil && p.ResourceLimits.CPU.Period != nil && p.ResourceLimits.CPU.Quota != nil { - if *p.ResourceLimits.CPU.Period != 0 || *p.ResourceLimits.CPU.Quota != 0 { - options = append(options, libpod.WithPodCPUPAQ((*p.ResourceLimits.CPU.Period), (*p.ResourceLimits.CPU.Quota))) - } - } - if p.ResourceLimits != nil && p.ResourceLimits.CPU != nil && p.ResourceLimits.CPU.Cpus != "" { - options = append(options, libpod.WithPodCPUSetCPUs(p.ResourceLimits.CPU.Cpus)) + if p.PodCreateCommand != nil { + options = append(options, libpod.WithPodCreateCommand(p.PodCreateCommand)) } + if len(p.Hostname) > 0 { options = append(options, libpod.WithPodHostname(p.Hostname)) } - if len(p.HostAdd) > 0 { - options = append(options, libpod.WithPodHosts(p.HostAdd)) - } - if len(p.DNSServer) > 0 { - var dnsServers []string - for _, d := range p.DNSServer { - dnsServers = append(dnsServers, d.String()) - } - options = append(options, libpod.WithPodDNS(dnsServers)) - } - if len(p.DNSOption) > 0 { - options = append(options, libpod.WithPodDNSOption(p.DNSOption)) - } - if len(p.DNSSearch) > 0 { - options = append(options, libpod.WithPodDNSSearch(p.DNSSearch)) - } - if p.StaticIP != nil { - options = append(options, libpod.WithPodStaticIP(*p.StaticIP)) - } - if p.StaticMAC != nil { - options = append(options, libpod.WithPodStaticMAC(*p.StaticMAC)) - } - if p.NoManageResolvConf { - options = append(options, libpod.WithPodUseImageResolvConf()) - } - if len(p.CNINetworks) > 0 { - options = append(options, libpod.WithPodNetworks(p.CNINetworks)) - } - - if len(p.InfraImage) > 0 { - options = append(options, libpod.WithInfraImage(p.InfraImage)) - } - if len(p.InfraName) > 0 { - options = append(options, libpod.WithInfraName(p.InfraName)) - } - - if len(p.InfraCommand) > 0 { - options = append(options, libpod.WithInfraCommand(p.InfraCommand)) - } + return options, nil +} - if !p.Pid.IsDefault() { - options = append(options, libpod.WithPodPidNS(p.Pid)) +// MapSpec modifies the already filled Infra specgenerator, +// replacing necessary values with those specified in pod creation +func MapSpec(p *specgen.PodSpecGenerator) (*specgen.SpecGenerator, error) { + if len(p.PortMappings) > 0 { + ports, _, _, err := ParsePortMapping(p.PortMappings) + if err != nil { + return nil, err + } + p.InfraContainerSpec.PortMappings = libpod.WithInfraContainerPorts(ports, p.InfraContainerSpec) } - switch p.NetNS.NSMode { case specgen.Default, "": if p.NoInfra { @@ -123,42 +117,88 @@ func createPodOptions(p *specgen.PodSpecGenerator, rt *libpod.Runtime) ([]libpod } if rootless.IsRootless() { logrus.Debugf("Pod will use slirp4netns") - options = append(options, libpod.WithPodSlirp4netns(p.NetworkOptions)) + if p.InfraContainerSpec.NetNS.NSMode != "host" { + p.InfraContainerSpec.NetworkOptions = p.NetworkOptions + p.InfraContainerSpec.NetNS.NSMode = specgen.NamespaceMode("slirp4netns") + } } else { logrus.Debugf("Pod using bridge network mode") } case specgen.Bridge: + p.InfraContainerSpec.NetNS.NSMode = specgen.Bridge logrus.Debugf("Pod using bridge network mode") case specgen.Host: logrus.Debugf("Pod will use host networking") - options = append(options, libpod.WithPodHostNetwork()) + if len(p.InfraContainerSpec.PortMappings) > 0 || + p.InfraContainerSpec.StaticIP != nil || + p.InfraContainerSpec.StaticMAC != nil || + len(p.InfraContainerSpec.CNINetworks) > 0 || + p.InfraContainerSpec.NetNS.NSMode == specgen.NoNetwork { + return nil, errors.Wrapf(define.ErrInvalidArg, "cannot set host network if network-related configuration is specified") + } + p.InfraContainerSpec.NetNS.NSMode = specgen.Host case specgen.Slirp: logrus.Debugf("Pod will use slirp4netns") - options = append(options, libpod.WithPodSlirp4netns(p.NetworkOptions)) + if p.InfraContainerSpec.NetNS.NSMode != "host" { + p.InfraContainerSpec.NetworkOptions = p.NetworkOptions + p.InfraContainerSpec.NetNS.NSMode = specgen.NamespaceMode("slirp4netns") + } case specgen.NoNetwork: logrus.Debugf("Pod will not use networking") - options = append(options, libpod.WithPodNoNetwork()) + if len(p.InfraContainerSpec.PortMappings) > 0 || + p.InfraContainerSpec.StaticIP != nil || + p.InfraContainerSpec.StaticMAC != nil || + len(p.InfraContainerSpec.CNINetworks) > 0 || + p.InfraContainerSpec.NetNS.NSMode == "host" { + return nil, errors.Wrapf(define.ErrInvalidArg, "cannot disable pod network if network-related configuration is specified") + } + p.InfraContainerSpec.NetNS.NSMode = specgen.NoNetwork default: return nil, errors.Errorf("pods presently do not support network mode %s", p.NetNS.NSMode) } - if p.NoManageHosts { - options = append(options, libpod.WithPodUseImageHosts()) + libpod.WithPodCgroups() + if len(p.InfraCommand) > 0 { + p.InfraContainerSpec.Entrypoint = p.InfraCommand } - if len(p.PortMappings) > 0 { - ports, _, _, err := ParsePortMapping(p.PortMappings) - if err != nil { - return nil, err - } - options = append(options, libpod.WithInfraContainerPorts(ports)) + + if len(p.HostAdd) > 0 { + p.InfraContainerSpec.HostAdd = p.HostAdd } - options = append(options, libpod.WithPodCgroups()) - if p.PodCreateCommand != nil { - options = append(options, libpod.WithPodCreateCommand(p.PodCreateCommand)) + if len(p.DNSServer) > 0 { + var dnsServers []net.IP + dnsServers = append(dnsServers, p.DNSServer...) + + p.InfraContainerSpec.DNSServers = dnsServers + } + if len(p.DNSOption) > 0 { + p.InfraContainerSpec.DNSOptions = p.DNSOption + } + if len(p.DNSSearch) > 0 { + p.InfraContainerSpec.DNSSearch = p.DNSSearch + } + if p.StaticIP != nil { + p.InfraContainerSpec.StaticIP = p.StaticIP + } + if p.StaticMAC != nil { + p.InfraContainerSpec.StaticMAC = p.StaticMAC + } + if p.NoManageResolvConf { + p.InfraContainerSpec.UseImageResolvConf = true + } + if len(p.CNINetworks) > 0 { + p.InfraContainerSpec.CNINetworks = p.CNINetworks + } + if p.NoManageHosts { + p.InfraContainerSpec.UseImageHosts = p.NoManageHosts } + if len(p.InfraConmonPidFile) > 0 { - options = append(options, libpod.WithInfraConmonPidFile(p.InfraConmonPidFile)) + p.InfraContainerSpec.ConmonPidFile = p.InfraConmonPidFile } - return options, nil + if p.InfraImage != config.DefaultInfraImage { + p.InfraContainerSpec.Image = p.InfraImage + } + return p.InfraContainerSpec, nil } diff --git a/pkg/specgen/podspecgen.go b/pkg/specgen/podspecgen.go index 386571d115..8872a13219 100644 --- a/pkg/specgen/podspecgen.go +++ b/pkg/specgen/podspecgen.go @@ -67,7 +67,7 @@ type PodBasicConfig struct { // Pid sets the process id namespace of the pod // Optional (defaults to private if unset). This sets the PID namespace of the infra container // This configuration will then be shared with the entire pod if PID namespace sharing is enabled via --share - Pid Namespace `json:"pid,omitempty:"` + Pid Namespace `json:"pidns,omitempty"` // Userns is used to indicate which kind of Usernamespace to enter. // Any containers created within the pod will inherit the pod's userns settings. // Optional @@ -173,6 +173,7 @@ type PodSpecGenerator struct { PodNetworkConfig PodCgroupConfig PodResourceConfig + InfraContainerSpec *SpecGenerator `json:"-"` } type PodResourceConfig struct { diff --git a/cmd/podman/common/createparse.go b/pkg/specgenutil/createparse.go similarity index 69% rename from cmd/podman/common/createparse.go rename to pkg/specgenutil/createparse.go index dcef1a1512..b46d8fbc6b 100644 --- a/cmd/podman/common/createparse.go +++ b/pkg/specgenutil/createparse.go @@ -1,13 +1,14 @@ -package common +package specgenutil import ( "github.com/containers/common/pkg/config" + "github.com/containers/podman/v3/pkg/domain/entities" "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 { +func validate(c *entities.ContainerCreateOptions) 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"`) @@ -23,7 +24,11 @@ func (c *ContainerCLIOpts) validate() error { "ignore": "", } if _, ok := imageVolType[c.ImageVolume]; !ok { - return errors.Errorf("invalid image-volume type %q. Pick one of bind, tmpfs, or ignore", c.ImageVolume) + if c.IsInfra { + c.ImageVolume = "bind" + } else { + 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/ports.go b/pkg/specgenutil/ports.go similarity index 97% rename from cmd/podman/common/ports.go rename to pkg/specgenutil/ports.go index 2092bbe531..6cc4de1ed4 100644 --- a/cmd/podman/common/ports.go +++ b/pkg/specgenutil/ports.go @@ -1,4 +1,4 @@ -package common +package specgenutil import ( "github.com/docker/go-connections/nat" diff --git a/cmd/podman/common/specgen.go b/pkg/specgenutil/specgen.go similarity index 91% rename from cmd/podman/common/specgen.go rename to pkg/specgenutil/specgen.go index 59d32f5684..9f676db1b3 100644 --- a/cmd/podman/common/specgen.go +++ b/pkg/specgenutil/specgen.go @@ -1,6 +1,7 @@ -package common +package specgenutil import ( + "encoding/json" "fmt" "os" "strconv" @@ -11,8 +12,9 @@ import ( "github.com/containers/podman/v3/cmd/podman/parse" "github.com/containers/podman/v3/libpod/define" ann "github.com/containers/podman/v3/pkg/annotations" + "github.com/containers/podman/v3/pkg/domain/entities" envLib "github.com/containers/podman/v3/pkg/env" - ns "github.com/containers/podman/v3/pkg/namespaces" + "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" @@ -21,7 +23,7 @@ import ( "github.com/pkg/errors" ) -func getCPULimits(c *ContainerCLIOpts) *specs.LinuxCPU { +func getCPULimits(c *entities.ContainerCreateOptions) *specs.LinuxCPU { cpu := &specs.LinuxCPU{} hasLimits := false @@ -67,7 +69,7 @@ func getCPULimits(c *ContainerCLIOpts) *specs.LinuxCPU { return cpu } -func getIOLimits(s *specgen.SpecGenerator, c *ContainerCLIOpts) (*specs.LinuxBlockIO, error) { +func getIOLimits(s *specgen.SpecGenerator, c *entities.ContainerCreateOptions) (*specs.LinuxBlockIO, error) { var err error io := &specs.LinuxBlockIO{} hasLimits := false @@ -122,7 +124,7 @@ func getIOLimits(s *specgen.SpecGenerator, c *ContainerCLIOpts) (*specs.LinuxBlo return io, nil } -func getMemoryLimits(s *specgen.SpecGenerator, c *ContainerCLIOpts) (*specs.LinuxMemory, error) { +func getMemoryLimits(s *specgen.SpecGenerator, c *entities.ContainerCreateOptions) (*specs.LinuxMemory, error) { var err error memory := &specs.LinuxMemory{} hasLimits := false @@ -167,7 +169,7 @@ func getMemoryLimits(s *specgen.SpecGenerator, c *ContainerCLIOpts) (*specs.Linu memory.Kernel = &mk hasLimits = true } - if c.MemorySwappiness >= 0 { + if c.MemorySwappiness > 0 { swappiness := uint64(c.MemorySwappiness) memory.Swappiness = &swappiness hasLimits = true @@ -182,7 +184,7 @@ func getMemoryLimits(s *specgen.SpecGenerator, c *ContainerCLIOpts) (*specs.Linu return memory, nil } -func setNamespaces(s *specgen.SpecGenerator, c *ContainerCLIOpts) error { +func setNamespaces(s *specgen.SpecGenerator, c *entities.ContainerCreateOptions) error { var err error if c.PID != "" { @@ -222,18 +224,22 @@ func setNamespaces(s *specgen.SpecGenerator, c *ContainerCLIOpts) error { return nil } -func FillOutSpecGen(s *specgen.SpecGenerator, c *ContainerCLIOpts, args []string) error { +func FillOutSpecGen(s *specgen.SpecGenerator, c *entities.ContainerCreateOptions, args []string) error { var ( err error ) - // validate flags as needed - if err := c.validate(); err != nil { + if err := validate(c); err != nil { return err } - s.User = c.User - inputCommand := args[1:] + var inputCommand []string + if !c.IsInfra { + if len(args) > 1 { + inputCommand = args[1:] + } + } + if len(c.HealthCmd) > 0 { if c.NoHealthCheck { return errors.New("Cannot specify both --no-healthcheck and --health-cmd") @@ -247,12 +253,33 @@ func FillOutSpecGen(s *specgen.SpecGenerator, c *ContainerCLIOpts, args []string Test: []string{"NONE"}, } } - - userNS := ns.UsernsMode(c.UserNS) + if err := setNamespaces(s, c); err != nil { + return err + } + userNS := namespaces.UsernsMode(s.UserNS.NSMode) + tempIDMap, err := util.ParseIDMapping(namespaces.UsernsMode(c.UserNS), []string{}, []string{}, "", "") + if err != nil { + return err + } s.IDMappings, err = util.ParseIDMapping(userNS, c.UIDMap, c.GIDMap, c.SubUIDName, c.SubGIDName) if err != nil { return err } + if len(s.IDMappings.GIDMap) == 0 { + s.IDMappings.AutoUserNsOpts.AdditionalGIDMappings = tempIDMap.AutoUserNsOpts.AdditionalGIDMappings + if s.UserNS.NSMode == specgen.NamespaceMode("auto") { + s.IDMappings.AutoUserNs = true + } + } + if len(s.IDMappings.UIDMap) == 0 { + s.IDMappings.AutoUserNsOpts.AdditionalUIDMappings = tempIDMap.AutoUserNsOpts.AdditionalUIDMappings + if s.UserNS.NSMode == specgen.NamespaceMode("auto") { + s.IDMappings.AutoUserNs = true + } + } + if tempIDMap.AutoUserNsOpts.Size != 0 { + s.IDMappings.AutoUserNsOpts.Size = tempIDMap.AutoUserNsOpts.Size + } // If some mappings are specified, assume a private user namespace if userNS.IsDefaultValue() && (!s.IDMappings.HostUIDMapping || !s.IDMappings.HostGIDMapping) { s.UserNS.NSMode = specgen.Private @@ -267,7 +294,9 @@ func FillOutSpecGen(s *specgen.SpecGenerator, c *ContainerCLIOpts, args []string } // We are not handling the Expose flag yet. // s.PortsExpose = c.Expose - s.PortMappings = c.Net.PublishPorts + if c.Net != nil { + s.PortMappings = c.Net.PublishPorts + } s.PublishExposedPorts = c.PublishAll s.Pod = c.Pod @@ -288,10 +317,6 @@ func FillOutSpecGen(s *specgen.SpecGenerator, c *ContainerCLIOpts, args []string } 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 { @@ -380,6 +405,7 @@ func FillOutSpecGen(s *specgen.SpecGenerator, c *ContainerCLIOpts, args []string } // Include the command used to create the container. + s.ContainerCreateCommand = os.Args if len(inputCommand) > 0 { @@ -394,28 +420,34 @@ func FillOutSpecGen(s *specgen.SpecGenerator, c *ContainerCLIOpts, args []string } s.ShmSize = &shmSize } - s.CNINetworks = c.Net.CNINetworks + + if c.Net != nil { + 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 + if c.Net != nil { + 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.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 - + if c.Net != nil { + 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" diff --git a/cmd/podman/common/util.go b/pkg/specgenutil/util.go similarity index 99% rename from cmd/podman/common/util.go rename to pkg/specgenutil/util.go index cdfff9d6fe..15676d086a 100644 --- a/cmd/podman/common/util.go +++ b/pkg/specgenutil/util.go @@ -1,4 +1,4 @@ -package common +package specgenutil import ( "io/ioutil" diff --git a/cmd/podman/common/volumes.go b/pkg/specgenutil/volumes.go similarity index 99% rename from cmd/podman/common/volumes.go rename to pkg/specgenutil/volumes.go index 883d604da6..e9f70fc9d3 100644 --- a/cmd/podman/common/volumes.go +++ b/pkg/specgenutil/volumes.go @@ -1,4 +1,4 @@ -package common +package specgenutil import ( "fmt" diff --git a/test/e2e/pod_create_test.go b/test/e2e/pod_create_test.go index c961bfc327..7297bfc6e9 100644 --- a/test/e2e/pod_create_test.go +++ b/test/e2e/pod_create_test.go @@ -559,7 +559,7 @@ ENTRYPOINT ["sleep","99999"] It("podman pod create --cpuset-cpus", func() { podName := "testPod" ctrName := "testCtr" - numCPU := float64(sysinfo.NumCPU()) + numCPU := float64(sysinfo.NumCPU()) - 1 numCPUStr := strconv.Itoa(int(numCPU)) in := "0-" + numCPUStr podCreate := podmanTest.Podman([]string{"pod", "create", "--cpuset-cpus", in, "--name", podName}) @@ -588,20 +588,14 @@ ENTRYPOINT ["sleep","99999"] podInspect.WaitWithDefaultTimeout() Expect(podInspect).Should(Exit(0)) podJSON := podInspect.InspectPodToJSON() - Expect(podJSON.InfraConfig.PidNS).To(Equal("path")) + Expect(podJSON.InfraConfig.PidNS).To(Equal(ns)) podName = "pidPod2" ns = "pod" podCreate = podmanTest.Podman([]string{"pod", "create", "--pid", ns, "--name", podName, "--share", "pid"}) podCreate.WaitWithDefaultTimeout() - Expect(podCreate).Should(Exit(0)) - - podInspect = podmanTest.Podman([]string{"pod", "inspect", podName}) - podInspect.WaitWithDefaultTimeout() - Expect(podInspect).Should(Exit(0)) - podJSON = podInspect.InspectPodToJSON() - Expect(podJSON.InfraConfig.PidNS).To(Equal("pod")) + Expect(podCreate).Should(ExitWithError()) podName = "pidPod3" ns = "host"