diff options
Diffstat (limited to 'cmd/podman')
36 files changed, 1417 insertions, 3035 deletions
diff --git a/cmd/podman/common/completion.go b/cmd/podman/common/completion.go index 9a4524b46..e925fb4f1 100644 --- a/cmd/podman/common/completion.go +++ b/cmd/podman/common/completion.go @@ -223,7 +223,7 @@ func getSecrets(cmd *cobra.Command, toComplete string) ([]string, cobra.ShellCom cobra.CompErrorln(err.Error()) return nil, cobra.ShellCompDirectiveNoFileComp } - secrets, err := engine.SecretList(registry.GetContext()) + secrets, err := engine.SecretList(registry.GetContext(), entities.SecretListRequest{}) if err != nil { cobra.CompErrorln(err.Error()) return nil, cobra.ShellCompDirectiveNoFileComp @@ -985,40 +985,14 @@ func AutocompleteFormat(o interface{}) func(cmd *cobra.Command, args []string, t f = f.Elem() } - // // the only supported type is struct + // the only supported type is struct if f.Kind() != reflect.Struct { return nil, cobra.ShellCompDirectiveNoFileComp } // last field get all names to suggest if i == len(fields)-1 { - suggestions := []string{} - for j := 0; j < f.NumField(); j++ { - fname := f.Type().Field(j).Name - suffix := "}}" - kind := f.Type().Field(j).Type.Kind() - if kind == reflect.Ptr { - // make sure to read the actual type when it is a pointer - kind = f.Type().Field(j).Type.Elem().Kind() - } - // when we have a nested struct do not append braces instead append a dot - if kind == reflect.Struct { - suffix = "." - } - if strings.HasPrefix(fname, fields[i]) { - // add field name with closing braces - suggestions = append(suggestions, fname+suffix) - } - } - - for j := 0; j < f.NumMethod(); j++ { - fname := f.Type().Method(j).Name - if strings.HasPrefix(fname, fields[i]) { - // add method name with closing braces - suggestions = append(suggestions, fname+"}}") - } - } - + suggestions := getStructFields(f, fields[i]) // add the current toComplete value in front so that the shell can complete this correctly toCompArr := strings.Split(toComplete, ".") toCompArr[len(toCompArr)-1] = "" @@ -1032,6 +1006,52 @@ func AutocompleteFormat(o interface{}) func(cmd *cobra.Command, args []string, t } } +// getStructFields reads all struct field names and method names and returns them. +func getStructFields(f reflect.Value, prefix string) []string { + suggestions := []string{} + // follow the pointer first + if f.Kind() == reflect.Ptr { + f = f.Elem() + } + // we only support structs + if f.Kind() != reflect.Struct { + return nil + } + // loop over all field names + for j := 0; j < f.NumField(); j++ { + field := f.Type().Field(j) + fname := field.Name + suffix := "}}" + kind := field.Type.Kind() + if kind == reflect.Ptr { + // make sure to read the actual type when it is a pointer + kind = field.Type.Elem().Kind() + } + // when we have a nested struct do not append braces instead append a dot + if kind == reflect.Struct { + suffix = "." + } + if strings.HasPrefix(fname, prefix) { + // add field name with suffix + suggestions = append(suggestions, fname+suffix) + } + // if field is anonymous add the child fields as well + if field.Anonymous { + suggestions = append(suggestions, getStructFields(f.FieldByIndex([]int{j}), prefix)...) + } + } + + for j := 0; j < f.NumMethod(); j++ { + fname := f.Type().Method(j).Name + if strings.HasPrefix(fname, prefix) { + // add method name with closing braces + suggestions = append(suggestions, fname+"}}") + } + } + + return suggestions +} + // AutocompleteEventFilter - Autocomplete event filter flag options. // -> "container=", "event=", "image=", "pod=", "volume=", "type=" func AutocompleteEventFilter(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { diff --git a/cmd/podman/common/completion_test.go b/cmd/podman/common/completion_test.go index 5bd627b85..84b3c1132 100644 --- a/cmd/podman/common/completion_test.go +++ b/cmd/podman/common/completion_test.go @@ -17,6 +17,10 @@ type Car struct { Extras map[string]string } +type Anonymous struct { + Hello string +} + func (c Car) Type() string { return "" } @@ -30,7 +34,10 @@ func TestAutocompleteFormat(t *testing.T) { Name string Age int Car *Car - }{} + *Anonymous + }{ + Anonymous: &Anonymous{}, + } testStruct.Car = &Car{} testStruct.Car.Extras = map[string]string{"test": "1"} @@ -73,12 +80,12 @@ func TestAutocompleteFormat(t *testing.T) { { "fist level struct field name", "{{.", - []string{"{{.Name}}", "{{.Age}}", "{{.Car."}, + []string{"{{.Name}}", "{{.Age}}", "{{.Car.", "{{.Anonymous.", "{{.Hello}}"}, }, { "fist level struct field name", "{{ .", - []string{"{{ .Name}}", "{{ .Age}}", "{{ .Car."}, + []string{"{{ .Name}}", "{{ .Age}}", "{{ .Car.", "{{ .Anonymous.", "{{ .Hello}}"}, }, { "fist level struct field name", diff --git a/cmd/podman/common/create.go b/cmd/podman/common/create.go index 401cf2e09..325c1dc69 100644 --- a/cmd/podman/common/create.go +++ b/cmd/podman/common/create.go @@ -7,6 +7,7 @@ import ( "github.com/containers/common/pkg/completion" "github.com/containers/podman/v3/cmd/podman/registry" "github.com/containers/podman/v3/libpod/define" + "github.com/containers/podman/v3/pkg/domain/entities" "github.com/spf13/cobra" ) @@ -14,663 +15,714 @@ const sizeWithUnitFormat = "(format: `<number>[<unit>]`, where unit = b (bytes), var containerConfig = registry.PodmanConfig() -func DefineCreateFlags(cmd *cobra.Command, cf *ContainerCLIOpts) { +func DefineCreateFlags(cmd *cobra.Command, cf *entities.ContainerCreateOptions, isInfra bool) { createFlags := cmd.Flags() - annotationFlagName := "annotation" - createFlags.StringSliceVar( - &cf.Annotation, - annotationFlagName, []string{}, - "Add annotations to container (key:value)", - ) - _ = cmd.RegisterFlagCompletionFunc(annotationFlagName, completion.AutocompleteNone) + if !isInfra { + annotationFlagName := "annotation" + createFlags.StringSliceVar( + &cf.Annotation, + annotationFlagName, []string{}, + "Add annotations to container (key:value)", + ) + _ = cmd.RegisterFlagCompletionFunc(annotationFlagName, completion.AutocompleteNone) - attachFlagName := "attach" - createFlags.StringSliceVarP( - &cf.Attach, - attachFlagName, "a", []string{}, - "Attach to STDIN, STDOUT or STDERR", - ) - _ = cmd.RegisterFlagCompletionFunc(attachFlagName, AutocompleteCreateAttach) + attachFlagName := "attach" + createFlags.StringSliceVarP( + &cf.Attach, + attachFlagName, "a", []string{}, + "Attach to STDIN, STDOUT or STDERR", + ) + _ = cmd.RegisterFlagCompletionFunc(attachFlagName, AutocompleteCreateAttach) - authfileFlagName := "authfile" - createFlags.StringVar( - &cf.Authfile, - authfileFlagName, auth.GetDefaultAuthFile(), - "Path of the authentication file. Use REGISTRY_AUTH_FILE environment variable to override", - ) - _ = cmd.RegisterFlagCompletionFunc(authfileFlagName, completion.AutocompleteDefault) + authfileFlagName := "authfile" + createFlags.StringVar( + &cf.Authfile, + authfileFlagName, auth.GetDefaultAuthFile(), + "Path of the authentication file. Use REGISTRY_AUTH_FILE environment variable to override", + ) + _ = cmd.RegisterFlagCompletionFunc(authfileFlagName, completion.AutocompleteDefault) - blkioWeightFlagName := "blkio-weight" - createFlags.StringVar( - &cf.BlkIOWeight, - blkioWeightFlagName, "", - "Block IO weight (relative weight) accepts a weight value between 10 and 1000.", - ) - _ = cmd.RegisterFlagCompletionFunc(blkioWeightFlagName, completion.AutocompleteNone) + blkioWeightFlagName := "blkio-weight" + createFlags.StringVar( + &cf.BlkIOWeight, + blkioWeightFlagName, "", + "Block IO weight (relative weight) accepts a weight value between 10 and 1000.", + ) + _ = cmd.RegisterFlagCompletionFunc(blkioWeightFlagName, completion.AutocompleteNone) - blkioWeightDeviceFlagName := "blkio-weight-device" - createFlags.StringSliceVar( - &cf.BlkIOWeightDevice, - blkioWeightDeviceFlagName, []string{}, - "Block IO weight (relative device weight, format: `DEVICE_NAME:WEIGHT`)", - ) - _ = cmd.RegisterFlagCompletionFunc(blkioWeightDeviceFlagName, completion.AutocompleteDefault) + blkioWeightDeviceFlagName := "blkio-weight-device" + createFlags.StringSliceVar( + &cf.BlkIOWeightDevice, + blkioWeightDeviceFlagName, []string{}, + "Block IO weight (relative device weight, format: `DEVICE_NAME:WEIGHT`)", + ) + _ = cmd.RegisterFlagCompletionFunc(blkioWeightDeviceFlagName, completion.AutocompleteDefault) - capAddFlagName := "cap-add" - createFlags.StringSliceVar( - &cf.CapAdd, - capAddFlagName, []string{}, - "Add capabilities to the container", - ) - _ = cmd.RegisterFlagCompletionFunc(capAddFlagName, completion.AutocompleteCapabilities) + capAddFlagName := "cap-add" + createFlags.StringSliceVar( + &cf.CapAdd, + capAddFlagName, []string{}, + "Add capabilities to the container", + ) + _ = cmd.RegisterFlagCompletionFunc(capAddFlagName, completion.AutocompleteCapabilities) - capDropFlagName := "cap-drop" - createFlags.StringSliceVar( - &cf.CapDrop, - capDropFlagName, []string{}, - "Drop capabilities from the container", - ) - _ = cmd.RegisterFlagCompletionFunc(capDropFlagName, completion.AutocompleteCapabilities) + capDropFlagName := "cap-drop" + createFlags.StringSliceVar( + &cf.CapDrop, + capDropFlagName, []string{}, + "Drop capabilities from the container", + ) + _ = cmd.RegisterFlagCompletionFunc(capDropFlagName, completion.AutocompleteCapabilities) - cgroupnsFlagName := "cgroupns" - createFlags.String( - cgroupnsFlagName, "", - "cgroup namespace to use", - ) - _ = cmd.RegisterFlagCompletionFunc(cgroupnsFlagName, AutocompleteNamespace) + cgroupnsFlagName := "cgroupns" + createFlags.String( + cgroupnsFlagName, "", + "cgroup namespace to use", + ) + _ = cmd.RegisterFlagCompletionFunc(cgroupnsFlagName, AutocompleteNamespace) - cgroupsFlagName := "cgroups" - createFlags.StringVar( - &cf.CGroupsMode, - cgroupsFlagName, cgroupConfig(), - `control container cgroup configuration ("enabled"|"disabled"|"no-conmon"|"split")`, - ) - _ = cmd.RegisterFlagCompletionFunc(cgroupsFlagName, AutocompleteCgroupMode) + cgroupsFlagName := "cgroups" + createFlags.StringVar( + &cf.CGroupsMode, + cgroupsFlagName, cgroupConfig(), + `control container cgroup configuration ("enabled"|"disabled"|"no-conmon"|"split")`, + ) + _ = cmd.RegisterFlagCompletionFunc(cgroupsFlagName, AutocompleteCgroupMode) - cgroupParentFlagName := "cgroup-parent" - createFlags.StringVar( - &cf.CGroupParent, - cgroupParentFlagName, "", - "Optional parent cgroup for the container", - ) - _ = cmd.RegisterFlagCompletionFunc(cgroupParentFlagName, completion.AutocompleteDefault) + cpuPeriodFlagName := "cpu-period" + createFlags.Uint64Var( + &cf.CPUPeriod, + cpuPeriodFlagName, 0, + "Limit the CPU CFS (Completely Fair Scheduler) period", + ) + _ = cmd.RegisterFlagCompletionFunc(cpuPeriodFlagName, completion.AutocompleteNone) - cidfileFlagName := "cidfile" - createFlags.StringVar( - &cf.CIDFile, - cidfileFlagName, "", - "Write the container ID to the file", - ) - _ = cmd.RegisterFlagCompletionFunc(cidfileFlagName, completion.AutocompleteDefault) + cpuQuotaFlagName := "cpu-quota" + createFlags.Int64Var( + &cf.CPUQuota, + cpuQuotaFlagName, 0, + "Limit the CPU CFS (Completely Fair Scheduler) quota", + ) + _ = cmd.RegisterFlagCompletionFunc(cpuQuotaFlagName, completion.AutocompleteNone) - conmonPidfileFlagName := "conmon-pidfile" - createFlags.StringVar( - &cf.ConmonPIDFile, - conmonPidfileFlagName, "", - "Path to the file that will receive the PID of conmon", - ) - _ = cmd.RegisterFlagCompletionFunc(conmonPidfileFlagName, completion.AutocompleteDefault) + cpuRtPeriodFlagName := "cpu-rt-period" + createFlags.Uint64Var( + &cf.CPURTPeriod, + cpuRtPeriodFlagName, 0, + "Limit the CPU real-time period in microseconds", + ) + _ = cmd.RegisterFlagCompletionFunc(cpuRtPeriodFlagName, completion.AutocompleteNone) - cpuPeriodFlagName := "cpu-period" - createFlags.Uint64Var( - &cf.CPUPeriod, - cpuPeriodFlagName, 0, - "Limit the CPU CFS (Completely Fair Scheduler) period", - ) - _ = cmd.RegisterFlagCompletionFunc(cpuPeriodFlagName, completion.AutocompleteNone) + cpuRtRuntimeFlagName := "cpu-rt-runtime" + createFlags.Int64Var( + &cf.CPURTRuntime, + cpuRtRuntimeFlagName, 0, + "Limit the CPU real-time runtime in microseconds", + ) + _ = cmd.RegisterFlagCompletionFunc(cpuRtRuntimeFlagName, completion.AutocompleteNone) - cpuQuotaFlagName := "cpu-quota" - createFlags.Int64Var( - &cf.CPUQuota, - cpuQuotaFlagName, 0, - "Limit the CPU CFS (Completely Fair Scheduler) quota", - ) - _ = cmd.RegisterFlagCompletionFunc(cpuQuotaFlagName, completion.AutocompleteNone) + cpuSharesFlagName := "cpu-shares" + createFlags.Uint64Var( + &cf.CPUShares, + cpuSharesFlagName, 0, + "CPU shares (relative weight)", + ) + _ = cmd.RegisterFlagCompletionFunc(cpuSharesFlagName, completion.AutocompleteNone) + cidfileFlagName := "cidfile" + createFlags.StringVar( + &cf.CIDFile, + cidfileFlagName, "", + "Write the container ID to the file", + ) + _ = cmd.RegisterFlagCompletionFunc(cidfileFlagName, completion.AutocompleteDefault) + cpusetMemsFlagName := "cpuset-mems" + createFlags.StringVar( + &cf.CPUSetMems, + cpusetMemsFlagName, "", + "Memory nodes (MEMs) in which to allow execution (0-3, 0,1). Only effective on NUMA systems.", + ) + _ = cmd.RegisterFlagCompletionFunc(cpusetMemsFlagName, completion.AutocompleteNone) - cpuRtPeriodFlagName := "cpu-rt-period" - createFlags.Uint64Var( - &cf.CPURTPeriod, - cpuRtPeriodFlagName, 0, - "Limit the CPU real-time period in microseconds", - ) - _ = cmd.RegisterFlagCompletionFunc(cpuRtPeriodFlagName, completion.AutocompleteNone) + deviceFlagName := "device" + createFlags.StringSliceVar( + &cf.Devices, + deviceFlagName, devices(), + "Add a host device to the container", + ) + _ = cmd.RegisterFlagCompletionFunc(deviceFlagName, completion.AutocompleteDefault) - cpuRtRuntimeFlagName := "cpu-rt-runtime" - createFlags.Int64Var( - &cf.CPURTRuntime, - cpuRtRuntimeFlagName, 0, - "Limit the CPU real-time runtime in microseconds", - ) - _ = cmd.RegisterFlagCompletionFunc(cpuRtRuntimeFlagName, completion.AutocompleteNone) + deviceCgroupRuleFlagName := "device-cgroup-rule" + createFlags.StringSliceVar( + &cf.DeviceCGroupRule, + deviceCgroupRuleFlagName, []string{}, + "Add a rule to the cgroup allowed devices list", + ) + _ = cmd.RegisterFlagCompletionFunc(deviceCgroupRuleFlagName, completion.AutocompleteNone) - cpuSharesFlagName := "cpu-shares" - createFlags.Uint64Var( - &cf.CPUShares, - cpuSharesFlagName, 0, - "CPU shares (relative weight)", - ) - _ = cmd.RegisterFlagCompletionFunc(cpuSharesFlagName, completion.AutocompleteNone) + deviceReadBpsFlagName := "device-read-bps" + createFlags.StringSliceVar( + &cf.DeviceReadBPs, + deviceReadBpsFlagName, []string{}, + "Limit read rate (bytes per second) from a device (e.g. --device-read-bps=/dev/sda:1mb)", + ) + _ = cmd.RegisterFlagCompletionFunc(deviceReadBpsFlagName, completion.AutocompleteDefault) - cpusFlagName := "cpus" - createFlags.Float64Var( - &cf.CPUS, - cpusFlagName, 0, - "Number of CPUs. The default is 0.000 which means no limit", - ) - _ = cmd.RegisterFlagCompletionFunc(cpusFlagName, completion.AutocompleteNone) + deviceReadIopsFlagName := "device-read-iops" + createFlags.StringSliceVar( + &cf.DeviceReadIOPs, + deviceReadIopsFlagName, []string{}, + "Limit read rate (IO per second) from a device (e.g. --device-read-iops=/dev/sda:1000)", + ) + _ = cmd.RegisterFlagCompletionFunc(deviceReadIopsFlagName, completion.AutocompleteDefault) - cpusetCpusFlagName := "cpuset-cpus" - createFlags.StringVar( - &cf.CPUSetCPUs, - cpusetCpusFlagName, "", - "CPUs in which to allow execution (0-3, 0,1)", - ) - _ = cmd.RegisterFlagCompletionFunc(cpusetCpusFlagName, completion.AutocompleteNone) + deviceWriteBpsFlagName := "device-write-bps" + createFlags.StringSliceVar( + &cf.DeviceWriteBPs, + deviceWriteBpsFlagName, []string{}, + "Limit write rate (bytes per second) to a device (e.g. --device-write-bps=/dev/sda:1mb)", + ) + _ = cmd.RegisterFlagCompletionFunc(deviceWriteBpsFlagName, completion.AutocompleteDefault) - cpusetMemsFlagName := "cpuset-mems" - createFlags.StringVar( - &cf.CPUSetMems, - cpusetMemsFlagName, "", - "Memory nodes (MEMs) in which to allow execution (0-3, 0,1). Only effective on NUMA systems.", - ) - _ = cmd.RegisterFlagCompletionFunc(cpusetMemsFlagName, completion.AutocompleteNone) + deviceWriteIopsFlagName := "device-write-iops" + createFlags.StringSliceVar( + &cf.DeviceWriteIOPs, + deviceWriteIopsFlagName, []string{}, + "Limit write rate (IO per second) to a device (e.g. --device-write-iops=/dev/sda:1000)", + ) + _ = cmd.RegisterFlagCompletionFunc(deviceWriteIopsFlagName, completion.AutocompleteDefault) - deviceFlagName := "device" - createFlags.StringSliceVar( - &cf.Devices, - deviceFlagName, devices(), - "Add a host device to the container", - ) - _ = cmd.RegisterFlagCompletionFunc(deviceFlagName, completion.AutocompleteDefault) + createFlags.Bool( + "disable-content-trust", false, + "This is a Docker specific option and is a NOOP", + ) - deviceCgroupRuleFlagName := "device-cgroup-rule" - createFlags.StringSliceVar( - &cf.DeviceCGroupRule, - deviceCgroupRuleFlagName, []string{}, - "Add a rule to the cgroup allowed devices list", - ) - _ = cmd.RegisterFlagCompletionFunc(deviceCgroupRuleFlagName, completion.AutocompleteNone) + envFlagName := "env" + createFlags.StringArrayP( + envFlagName, "e", env(), + "Set environment variables in container", + ) + _ = cmd.RegisterFlagCompletionFunc(envFlagName, completion.AutocompleteNone) + + if !registry.IsRemote() { + createFlags.BoolVar( + &cf.EnvHost, + "env-host", false, "Use all current host environment variables in container", + ) + } + + envFileFlagName := "env-file" + createFlags.StringSliceVar( + &cf.EnvFile, + envFileFlagName, []string{}, + "Read in a file of environment variables", + ) + _ = cmd.RegisterFlagCompletionFunc(envFileFlagName, completion.AutocompleteDefault) - deviceReadBpsFlagName := "device-read-bps" - createFlags.StringSliceVar( - &cf.DeviceReadBPs, - deviceReadBpsFlagName, []string{}, - "Limit read rate (bytes per second) from a device (e.g. --device-read-bps=/dev/sda:1mb)", - ) - _ = cmd.RegisterFlagCompletionFunc(deviceReadBpsFlagName, completion.AutocompleteDefault) + exposeFlagName := "expose" + createFlags.StringSliceVar( + &cf.Expose, + exposeFlagName, []string{}, + "Expose a port or a range of ports", + ) + _ = cmd.RegisterFlagCompletionFunc(exposeFlagName, completion.AutocompleteNone) - deviceReadIopsFlagName := "device-read-iops" - createFlags.StringSliceVar( - &cf.DeviceReadIOPs, - deviceReadIopsFlagName, []string{}, - "Limit read rate (IO per second) from a device (e.g. --device-read-iops=/dev/sda:1000)", - ) - _ = cmd.RegisterFlagCompletionFunc(deviceReadIopsFlagName, completion.AutocompleteDefault) + groupAddFlagName := "group-add" + createFlags.StringSliceVar( + &cf.GroupAdd, + groupAddFlagName, []string{}, + "Add additional groups to the primary container process. 'keep-groups' allows container processes to use supplementary groups.", + ) + _ = cmd.RegisterFlagCompletionFunc(groupAddFlagName, completion.AutocompleteNone) - deviceWriteBpsFlagName := "device-write-bps" - createFlags.StringSliceVar( - &cf.DeviceWriteBPs, - deviceWriteBpsFlagName, []string{}, - "Limit write rate (bytes per second) to a device (e.g. --device-write-bps=/dev/sda:1mb)", - ) - _ = cmd.RegisterFlagCompletionFunc(deviceWriteBpsFlagName, completion.AutocompleteDefault) + healthCmdFlagName := "health-cmd" + createFlags.StringVar( + &cf.HealthCmd, + healthCmdFlagName, "", + "set a healthcheck command for the container ('none' disables the existing healthcheck)", + ) + _ = cmd.RegisterFlagCompletionFunc(healthCmdFlagName, completion.AutocompleteNone) - deviceWriteIopsFlagName := "device-write-iops" - createFlags.StringSliceVar( - &cf.DeviceWriteIOPs, - deviceWriteIopsFlagName, []string{}, - "Limit write rate (IO per second) to a device (e.g. --device-write-iops=/dev/sda:1000)", - ) - _ = cmd.RegisterFlagCompletionFunc(deviceWriteIopsFlagName, completion.AutocompleteDefault) + healthIntervalFlagName := "health-interval" + createFlags.StringVar( + &cf.HealthInterval, + healthIntervalFlagName, DefaultHealthCheckInterval, + "set an interval for the healthchecks (a value of disable results in no automatic timer setup)", + ) + _ = cmd.RegisterFlagCompletionFunc(healthIntervalFlagName, completion.AutocompleteNone) - createFlags.Bool( - "disable-content-trust", false, - "This is a Docker specific option and is a NOOP", - ) + healthRetriesFlagName := "health-retries" + createFlags.UintVar( + &cf.HealthRetries, + healthRetriesFlagName, DefaultHealthCheckRetries, + "the number of retries allowed before a healthcheck is considered to be unhealthy", + ) + _ = cmd.RegisterFlagCompletionFunc(healthRetriesFlagName, completion.AutocompleteNone) - entrypointFlagName := "entrypoint" - createFlags.String(entrypointFlagName, "", - "Overwrite the default ENTRYPOINT of the image", - ) - _ = cmd.RegisterFlagCompletionFunc(entrypointFlagName, completion.AutocompleteNone) + healthStartPeriodFlagName := "health-start-period" + createFlags.StringVar( + &cf.HealthStartPeriod, + healthStartPeriodFlagName, DefaultHealthCheckStartPeriod, + "the initialization time needed for a container to bootstrap", + ) + _ = cmd.RegisterFlagCompletionFunc(healthStartPeriodFlagName, completion.AutocompleteNone) - envFlagName := "env" - createFlags.StringArrayP( - envFlagName, "e", env(), - "Set environment variables in container", - ) - _ = cmd.RegisterFlagCompletionFunc(envFlagName, completion.AutocompleteNone) + healthTimeoutFlagName := "health-timeout" + createFlags.StringVar( + &cf.HealthTimeout, + healthTimeoutFlagName, DefaultHealthCheckTimeout, + "the maximum time allowed to complete the healthcheck before an interval is considered failed", + ) + _ = cmd.RegisterFlagCompletionFunc(healthTimeoutFlagName, completion.AutocompleteNone) - if !registry.IsRemote() { createFlags.BoolVar( - &cf.EnvHost, - "env-host", false, "Use all current host environment variables in container", + &cf.HTTPProxy, + "http-proxy", containerConfig.Containers.HTTPProxy, + "Set proxy environment variables in the container based on the host proxy vars", ) - } - - envFileFlagName := "env-file" - createFlags.StringSliceVar( - &cf.EnvFile, - envFileFlagName, []string{}, - "Read in a file of environment variables", - ) - _ = cmd.RegisterFlagCompletionFunc(envFileFlagName, completion.AutocompleteDefault) - - exposeFlagName := "expose" - createFlags.StringSliceVar( - &cf.Expose, - exposeFlagName, []string{}, - "Expose a port or a range of ports", - ) - _ = cmd.RegisterFlagCompletionFunc(exposeFlagName, completion.AutocompleteNone) - gidmapFlagName := "gidmap" - createFlags.StringSliceVar( - &cf.GIDMap, - gidmapFlagName, []string{}, - "GID map to use for the user namespace", - ) - _ = cmd.RegisterFlagCompletionFunc(gidmapFlagName, completion.AutocompleteNone) + imageVolumeFlagName := "image-volume" + createFlags.StringVar( + &cf.ImageVolume, + imageVolumeFlagName, DefaultImageVolume, + `Tells podman how to handle the builtin image volumes ("bind"|"tmpfs"|"ignore")`, + ) + _ = cmd.RegisterFlagCompletionFunc(imageVolumeFlagName, AutocompleteImageVolume) - groupAddFlagName := "group-add" - createFlags.StringSliceVar( - &cf.GroupAdd, - groupAddFlagName, []string{}, - "Add additional groups to the primary container process. 'keep-groups' allows container processes to use supplementary groups.", - ) - _ = cmd.RegisterFlagCompletionFunc(groupAddFlagName, completion.AutocompleteNone) + createFlags.BoolVar( + &cf.Init, + "init", false, + "Run an init binary inside the container that forwards signals and reaps processes", + ) - createFlags.Bool( - "help", false, "", - ) + initPathFlagName := "init-path" + createFlags.StringVar( + &cf.InitPath, + initPathFlagName, initPath(), + // Do not use the Value field for setting the default value to determine user input (i.e., non-empty string) + "Path to the container-init binary", + ) + _ = cmd.RegisterFlagCompletionFunc(initPathFlagName, completion.AutocompleteDefault) - healthCmdFlagName := "health-cmd" - createFlags.StringVar( - &cf.HealthCmd, - healthCmdFlagName, "", - "set a healthcheck command for the container ('none' disables the existing healthcheck)", - ) - _ = cmd.RegisterFlagCompletionFunc(healthCmdFlagName, completion.AutocompleteNone) + createFlags.BoolVarP( + &cf.Interactive, + "interactive", "i", false, + "Keep STDIN open even if not attached", + ) + ipcFlagName := "ipc" + createFlags.String( + ipcFlagName, "", + "IPC namespace to use", + ) + _ = cmd.RegisterFlagCompletionFunc(ipcFlagName, AutocompleteNamespace) - healthIntervalFlagName := "health-interval" - createFlags.StringVar( - &cf.HealthInterval, - healthIntervalFlagName, DefaultHealthCheckInterval, - "set an interval for the healthchecks (a value of disable results in no automatic timer setup)", - ) - _ = cmd.RegisterFlagCompletionFunc(healthIntervalFlagName, completion.AutocompleteNone) + kernelMemoryFlagName := "kernel-memory" + createFlags.StringVar( + &cf.KernelMemory, + kernelMemoryFlagName, "", + "Kernel memory limit "+sizeWithUnitFormat, + ) + _ = cmd.RegisterFlagCompletionFunc(kernelMemoryFlagName, completion.AutocompleteNone) + logDriverFlagName := "log-driver" + createFlags.StringVar( + &cf.LogDriver, + logDriverFlagName, logDriver(), + "Logging driver for the container", + ) + _ = cmd.RegisterFlagCompletionFunc(logDriverFlagName, AutocompleteLogDriver) - healthRetriesFlagName := "health-retries" - createFlags.UintVar( - &cf.HealthRetries, - healthRetriesFlagName, DefaultHealthCheckRetries, - "the number of retries allowed before a healthcheck is considered to be unhealthy", - ) - _ = cmd.RegisterFlagCompletionFunc(healthRetriesFlagName, completion.AutocompleteNone) + logOptFlagName := "log-opt" + createFlags.StringSliceVar( + &cf.LogOptions, + logOptFlagName, []string{}, + "Logging driver options", + ) + _ = cmd.RegisterFlagCompletionFunc(logOptFlagName, AutocompleteLogOpt) - healthStartPeriodFlagName := "health-start-period" - createFlags.StringVar( - &cf.HealthStartPeriod, - healthStartPeriodFlagName, DefaultHealthCheckStartPeriod, - "the initialization time needed for a container to bootstrap", - ) - _ = cmd.RegisterFlagCompletionFunc(healthStartPeriodFlagName, completion.AutocompleteNone) + memoryFlagName := "memory" + createFlags.StringVarP( + &cf.Memory, + memoryFlagName, "m", "", + "Memory limit "+sizeWithUnitFormat, + ) + _ = cmd.RegisterFlagCompletionFunc(memoryFlagName, completion.AutocompleteNone) - healthTimeoutFlagName := "health-timeout" - createFlags.StringVar( - &cf.HealthTimeout, - healthTimeoutFlagName, DefaultHealthCheckTimeout, - "the maximum time allowed to complete the healthcheck before an interval is considered failed", - ) - _ = cmd.RegisterFlagCompletionFunc(healthTimeoutFlagName, completion.AutocompleteNone) + memoryReservationFlagName := "memory-reservation" + createFlags.StringVar( + &cf.MemoryReservation, + memoryReservationFlagName, "", + "Memory soft limit "+sizeWithUnitFormat, + ) + _ = cmd.RegisterFlagCompletionFunc(memoryReservationFlagName, completion.AutocompleteNone) - hostnameFlagName := "hostname" - createFlags.StringVarP( - &cf.Hostname, - hostnameFlagName, "h", "", - "Set container hostname", - ) - _ = cmd.RegisterFlagCompletionFunc(hostnameFlagName, completion.AutocompleteNone) + memorySwapFlagName := "memory-swap" + createFlags.StringVar( + &cf.MemorySwap, + memorySwapFlagName, "", + "Swap limit equal to memory plus swap: '-1' to enable unlimited swap", + ) + _ = cmd.RegisterFlagCompletionFunc(memorySwapFlagName, completion.AutocompleteNone) - createFlags.BoolVar( - &cf.HTTPProxy, - "http-proxy", containerConfig.Containers.HTTPProxy, - "Set proxy environment variables in the container based on the host proxy vars", - ) + memorySwappinessFlagName := "memory-swappiness" + createFlags.Int64Var( + &cf.MemorySwappiness, + memorySwappinessFlagName, -1, + "Tune container memory swappiness (0 to 100, or -1 for system default)", + ) + _ = cmd.RegisterFlagCompletionFunc(memorySwappinessFlagName, completion.AutocompleteNone) - imageVolumeFlagName := "image-volume" - createFlags.StringVar( - &cf.ImageVolume, - imageVolumeFlagName, DefaultImageVolume, - `Tells podman how to handle the builtin image volumes ("bind"|"tmpfs"|"ignore")`, - ) - _ = cmd.RegisterFlagCompletionFunc(imageVolumeFlagName, AutocompleteImageVolume) + createFlags.BoolVar( + &cf.NoHealthCheck, + "no-healthcheck", false, + "Disable healthchecks on container", + ) + createFlags.BoolVar( + &cf.OOMKillDisable, + "oom-kill-disable", false, + "Disable OOM Killer", + ) - createFlags.BoolVar( - &cf.Init, - "init", false, - "Run an init binary inside the container that forwards signals and reaps processes", - ) + oomScoreAdjFlagName := "oom-score-adj" + createFlags.IntVar( + &cf.OOMScoreAdj, + oomScoreAdjFlagName, 0, + "Tune the host's OOM preferences (-1000 to 1000)", + ) + _ = cmd.RegisterFlagCompletionFunc(oomScoreAdjFlagName, completion.AutocompleteNone) - initPathFlagName := "init-path" - createFlags.StringVar( - &cf.InitPath, - initPathFlagName, initPath(), - // Do not use the Value field for setting the default value to determine user input (i.e., non-empty string) - "Path to the container-init binary", - ) - _ = cmd.RegisterFlagCompletionFunc(initPathFlagName, completion.AutocompleteDefault) + archFlagName := "arch" + createFlags.StringVar( + &cf.Arch, + archFlagName, "", + "use `ARCH` instead of the architecture of the machine for choosing images", + ) + _ = cmd.RegisterFlagCompletionFunc(archFlagName, completion.AutocompleteArch) - createFlags.BoolVarP( - &cf.Interactive, - "interactive", "i", false, - "Keep STDIN open even if not attached", - ) + osFlagName := "os" + createFlags.StringVar( + &cf.OS, + osFlagName, "", + "use `OS` instead of the running OS for choosing images", + ) + _ = cmd.RegisterFlagCompletionFunc(osFlagName, completion.AutocompleteOS) - ipcFlagName := "ipc" - createFlags.String( - ipcFlagName, "", - "IPC namespace to use", - ) - _ = cmd.RegisterFlagCompletionFunc(ipcFlagName, AutocompleteNamespace) + variantFlagName := "variant" + createFlags.StringVar( + &cf.Variant, + variantFlagName, "", + "Use _VARIANT_ instead of the running architecture variant for choosing images", + ) + _ = cmd.RegisterFlagCompletionFunc(variantFlagName, completion.AutocompleteNone) - kernelMemoryFlagName := "kernel-memory" - createFlags.StringVar( - &cf.KernelMemory, - kernelMemoryFlagName, "", - "Kernel memory limit "+sizeWithUnitFormat, - ) - _ = cmd.RegisterFlagCompletionFunc(kernelMemoryFlagName, completion.AutocompleteNone) + pidsLimitFlagName := "pids-limit" + createFlags.Int64( + pidsLimitFlagName, pidsLimit(), + "Tune container pids limit (set 0 for unlimited, -1 for server defaults)", + ) + _ = cmd.RegisterFlagCompletionFunc(pidsLimitFlagName, completion.AutocompleteNone) - labelFlagName := "label" - createFlags.StringArrayVarP( - &cf.Label, - labelFlagName, "l", []string{}, - "Set metadata on container", - ) - _ = cmd.RegisterFlagCompletionFunc(labelFlagName, completion.AutocompleteNone) + platformFlagName := "platform" + createFlags.StringVar( + &cf.Platform, + platformFlagName, "", + "Specify the platform for selecting the image. (Conflicts with --arch and --os)", + ) + _ = cmd.RegisterFlagCompletionFunc(platformFlagName, completion.AutocompleteNone) - labelFileFlagName := "label-file" - createFlags.StringSliceVar( - &cf.LabelFile, - labelFileFlagName, []string{}, - "Read in a line delimited file of labels", - ) - _ = cmd.RegisterFlagCompletionFunc(labelFileFlagName, completion.AutocompleteDefault) + podFlagName := "pod" + createFlags.StringVar( + &cf.Pod, + podFlagName, "", + "Run container in an existing pod", + ) + _ = cmd.RegisterFlagCompletionFunc(podFlagName, AutocompletePods) - logDriverFlagName := "log-driver" - createFlags.StringVar( - &cf.LogDriver, - logDriverFlagName, logDriver(), - "Logging driver for the container", - ) - _ = cmd.RegisterFlagCompletionFunc(logDriverFlagName, AutocompleteLogDriver) + podIDFileFlagName := "pod-id-file" + createFlags.StringVar( + &cf.PodIDFile, + podIDFileFlagName, "", + "Read the pod ID from the file", + ) + _ = cmd.RegisterFlagCompletionFunc(podIDFileFlagName, completion.AutocompleteDefault) + createFlags.BoolVar( + &cf.Privileged, + "privileged", false, + "Give extended privileges to container", + ) + createFlags.BoolVarP( + &cf.PublishAll, + "publish-all", "P", false, + "Publish all exposed ports to random ports on the host interface", + ) - logOptFlagName := "log-opt" - createFlags.StringSliceVar( - &cf.LogOptions, - logOptFlagName, []string{}, - "Logging driver options", - ) - _ = cmd.RegisterFlagCompletionFunc(logOptFlagName, AutocompleteLogOpt) + pullFlagName := "pull" + createFlags.StringVar( + &cf.Pull, + pullFlagName, policy(), + `Pull image before creating ("always"|"missing"|"never")`, + ) + _ = cmd.RegisterFlagCompletionFunc(pullFlagName, AutocompletePullOption) - memoryFlagName := "memory" - createFlags.StringVarP( - &cf.Memory, - memoryFlagName, "m", "", - "Memory limit "+sizeWithUnitFormat, - ) - _ = cmd.RegisterFlagCompletionFunc(memoryFlagName, completion.AutocompleteNone) + createFlags.BoolVarP( + &cf.Quiet, + "quiet", "q", false, + "Suppress output information when pulling images", + ) + createFlags.BoolVar( + &cf.ReadOnly, + "read-only", false, + "Make containers root filesystem read-only", + ) + createFlags.BoolVar( + &cf.ReadOnlyTmpFS, + "read-only-tmpfs", true, + "When running containers in read-only mode mount a read-write tmpfs on /run, /tmp and /var/tmp", + ) + requiresFlagName := "requires" + createFlags.StringSliceVar( + &cf.Requires, + requiresFlagName, []string{}, + "Add one or more requirement containers that must be started before this container will start", + ) + _ = cmd.RegisterFlagCompletionFunc(requiresFlagName, AutocompleteContainers) - memoryReservationFlagName := "memory-reservation" - createFlags.StringVar( - &cf.MemoryReservation, - memoryReservationFlagName, "", - "Memory soft limit "+sizeWithUnitFormat, - ) - _ = cmd.RegisterFlagCompletionFunc(memoryReservationFlagName, completion.AutocompleteNone) + restartFlagName := "restart" + createFlags.StringVar( + &cf.Restart, + restartFlagName, "", + `Restart policy to apply when a container exits ("always"|"no"|"on-failure"|"unless-stopped")`, + ) + _ = cmd.RegisterFlagCompletionFunc(restartFlagName, AutocompleteRestartOption) - memorySwapFlagName := "memory-swap" - createFlags.StringVar( - &cf.MemorySwap, - memorySwapFlagName, "", - "Swap limit equal to memory plus swap: '-1' to enable unlimited swap", - ) - _ = cmd.RegisterFlagCompletionFunc(memorySwapFlagName, completion.AutocompleteNone) + createFlags.BoolVar( + &cf.Rm, + "rm", false, + "Remove container (and pod if created) after exit", + ) + createFlags.BoolVar( + &cf.RootFS, + "rootfs", false, + "The first argument is not an image but the rootfs to the exploded container", + ) - memorySwappinessFlagName := "memory-swappiness" - createFlags.Int64Var( - &cf.MemorySwappiness, - memorySwappinessFlagName, -1, - "Tune container memory swappiness (0 to 100, or -1 for system default)", - ) - _ = cmd.RegisterFlagCompletionFunc(memorySwappinessFlagName, completion.AutocompleteNone) + sdnotifyFlagName := "sdnotify" + createFlags.StringVar( + &cf.SdNotifyMode, + sdnotifyFlagName, define.SdNotifyModeContainer, + `control sd-notify behavior ("container"|"conmon"|"ignore")`, + ) + _ = cmd.RegisterFlagCompletionFunc(sdnotifyFlagName, AutocompleteSDNotify) - nameFlagName := "name" - createFlags.StringVar( - &cf.Name, - nameFlagName, "", - "Assign a name to the container", - ) - _ = cmd.RegisterFlagCompletionFunc(nameFlagName, completion.AutocompleteNone) + secretFlagName := "secret" + createFlags.StringArrayVar( + &cf.Secrets, + secretFlagName, []string{}, + "Add secret to container", + ) + _ = cmd.RegisterFlagCompletionFunc(secretFlagName, AutocompleteSecrets) - createFlags.BoolVar( - &cf.NoHealthCheck, - "no-healthcheck", false, - "Disable healthchecks on container", - ) - createFlags.BoolVar( - &cf.OOMKillDisable, - "oom-kill-disable", false, - "Disable OOM Killer", - ) + securityOptFlagName := "security-opt" + createFlags.StringArrayVar( + &cf.SecurityOpt, + securityOptFlagName, []string{}, + "Security Options", + ) + _ = cmd.RegisterFlagCompletionFunc(securityOptFlagName, AutocompleteSecurityOption) - oomScoreAdjFlagName := "oom-score-adj" - createFlags.IntVar( - &cf.OOMScoreAdj, - oomScoreAdjFlagName, 0, - "Tune the host's OOM preferences (-1000 to 1000)", - ) - _ = cmd.RegisterFlagCompletionFunc(oomScoreAdjFlagName, completion.AutocompleteNone) + shmSizeFlagName := "shm-size" + createFlags.String( + shmSizeFlagName, shmSize(), + "Size of /dev/shm "+sizeWithUnitFormat, + ) + _ = cmd.RegisterFlagCompletionFunc(shmSizeFlagName, completion.AutocompleteNone) - archFlagName := "arch" - createFlags.StringVar( - &cf.Arch, - archFlagName, "", - "use `ARCH` instead of the architecture of the machine for choosing images", - ) - _ = cmd.RegisterFlagCompletionFunc(archFlagName, completion.AutocompleteArch) + stopSignalFlagName := "stop-signal" + createFlags.StringVar( + &cf.SignaturePolicy, + "signature-policy", "", + "`Pathname` of signature policy file (not usually used)", + ) + createFlags.StringVar( + &cf.StopSignal, + stopSignalFlagName, "", + "Signal to stop a container. Default is SIGTERM", + ) + _ = cmd.RegisterFlagCompletionFunc(stopSignalFlagName, AutocompleteStopSignal) - osFlagName := "os" - createFlags.StringVar( - &cf.OS, - osFlagName, "", - "use `OS` instead of the running OS for choosing images", - ) - _ = cmd.RegisterFlagCompletionFunc(osFlagName, completion.AutocompleteOS) + stopTimeoutFlagName := "stop-timeout" + createFlags.UintVar( + &cf.StopTimeout, + stopTimeoutFlagName, containerConfig.Engine.StopTimeout, + "Timeout (in seconds) that containers stopped by user command have to exit. If exceeded, the container will be forcibly stopped via SIGKILL.", + ) + _ = cmd.RegisterFlagCompletionFunc(stopTimeoutFlagName, completion.AutocompleteNone) - variantFlagName := "variant" - createFlags.StringVar( - &cf.Variant, - variantFlagName, "", - "Use _VARIANT_ instead of the running architecture variant for choosing images", - ) - _ = cmd.RegisterFlagCompletionFunc(variantFlagName, completion.AutocompleteNone) + sysctlFlagName := "sysctl" + createFlags.StringSliceVar( + &cf.Sysctl, + sysctlFlagName, []string{}, + "Sysctl options", + ) + //TODO: Add function for sysctl completion. + _ = cmd.RegisterFlagCompletionFunc(sysctlFlagName, completion.AutocompleteNone) + + systemdFlagName := "systemd" + createFlags.StringVar( + &cf.Systemd, + systemdFlagName, "true", + `Run container in systemd mode ("true"|"false"|"always")`, + ) + _ = cmd.RegisterFlagCompletionFunc(systemdFlagName, AutocompleteSystemdFlag) - personalityFlagName := "personality" - createFlags.StringVar( - &cf.Personality, - personalityFlagName, "", - "Configure execution domain using personality (e.g., LINUX/LINUX32)", - ) - _ = cmd.RegisterFlagCompletionFunc(personalityFlagName, AutocompleteNamespace) + personalityFlagName := "personality" + createFlags.StringVar( + &cf.Personality, + personalityFlagName, "", + "Configure execution domain using personality (e.g., LINUX/LINUX32)", + ) + _ = cmd.RegisterFlagCompletionFunc(personalityFlagName, AutocompleteNamespace) - pidFlagName := "pid" - createFlags.String( - pidFlagName, "", - "PID namespace to use", - ) - _ = cmd.RegisterFlagCompletionFunc(pidFlagName, AutocompleteNamespace) + timeoutFlagName := "timeout" + createFlags.UintVar( + &cf.Timeout, + timeoutFlagName, 0, + "Maximum length of time a container is allowed to run. The container will be killed automatically after the time expires.", + ) + _ = cmd.RegisterFlagCompletionFunc(timeoutFlagName, completion.AutocompleteNone) - pidsLimitFlagName := "pids-limit" - createFlags.Int64( - pidsLimitFlagName, pidsLimit(), - "Tune container pids limit (set 0 for unlimited, -1 for server defaults)", - ) - _ = cmd.RegisterFlagCompletionFunc(pidsLimitFlagName, completion.AutocompleteNone) + // Flag for TLS verification, so that `run` and `create` commands can make use of it. + // Make sure to use `=` while using this flag i.e `--tls-verify=false/true` + tlsVerifyFlagName := "tls-verify" + createFlags.BoolVar( + &cf.TLSVerify, + tlsVerifyFlagName, true, + "Require HTTPS and verify certificates when contacting registries for pulling images", + ) - platformFlagName := "platform" - createFlags.StringVar( - &cf.Platform, - platformFlagName, "", - "Specify the platform for selecting the image. (Conflicts with --arch and --os)", - ) - _ = cmd.RegisterFlagCompletionFunc(platformFlagName, completion.AutocompleteNone) + tmpfsFlagName := "tmpfs" + createFlags.StringArrayVar( + &cf.TmpFS, + tmpfsFlagName, []string{}, + "Mount a temporary filesystem (`tmpfs`) into a container", + ) + _ = cmd.RegisterFlagCompletionFunc(tmpfsFlagName, completion.AutocompleteDefault) - podFlagName := "pod" - createFlags.StringVar( - &cf.Pod, - podFlagName, "", - "Run container in an existing pod", - ) - _ = cmd.RegisterFlagCompletionFunc(podFlagName, AutocompletePods) + createFlags.BoolVarP( + &cf.TTY, + "tty", "t", false, + "Allocate a pseudo-TTY for container", + ) - podIDFileFlagName := "pod-id-file" - createFlags.StringVar( - &cf.PodIDFile, - podIDFileFlagName, "", - "Read the pod ID from the file", - ) - _ = cmd.RegisterFlagCompletionFunc(podIDFileFlagName, completion.AutocompleteDefault) - - // Flag for TLS verification, so that `run` and `create` commands can make use of it. - // Make sure to use `=` while using this flag i.e `--tls-verify=false/true` - tlsVerifyFlagName := "tls-verify" - createFlags.BoolVar( - &cf.TLSVerify, - tlsVerifyFlagName, true, - "Require HTTPS and verify certificates when contacting registries for pulling images", - ) + timezoneFlagName := "tz" + createFlags.StringVar( + &cf.Timezone, + timezoneFlagName, containerConfig.TZ(), + "Set timezone in container", + ) + _ = cmd.RegisterFlagCompletionFunc(timezoneFlagName, completion.AutocompleteNone) //TODO: add timezone completion - createFlags.BoolVar( - &cf.Privileged, - "privileged", false, - "Give extended privileges to container", - ) - createFlags.BoolVarP( - &cf.PublishAll, - "publish-all", "P", false, - "Publish all exposed ports to random ports on the host interface", - ) + umaskFlagName := "umask" + createFlags.StringVar( + &cf.Umask, + umaskFlagName, containerConfig.Umask(), + "Set umask in container", + ) + _ = cmd.RegisterFlagCompletionFunc(umaskFlagName, completion.AutocompleteNone) - pullFlagName := "pull" - createFlags.StringVar( - &cf.Pull, - pullFlagName, policy(), - `Pull image before creating ("always"|"missing"|"never")`, - ) - _ = cmd.RegisterFlagCompletionFunc(pullFlagName, AutocompletePullOption) + ulimitFlagName := "ulimit" + createFlags.StringSliceVar( + &cf.Ulimit, + ulimitFlagName, ulimits(), + "Ulimit options", + ) + _ = cmd.RegisterFlagCompletionFunc(ulimitFlagName, completion.AutocompleteNone) - createFlags.BoolVarP( - &cf.Quiet, - "quiet", "q", false, - "Suppress output information when pulling images", - ) - createFlags.BoolVar( - &cf.ReadOnly, - "read-only", false, - "Make containers root filesystem read-only", - ) - createFlags.BoolVar( - &cf.ReadOnlyTmpFS, - "read-only-tmpfs", true, - "When running containers in read-only mode mount a read-write tmpfs on /run, /tmp and /var/tmp", - ) - createFlags.BoolVar( - &cf.Replace, - "replace", false, - `If a container with the same name exists, replace it`, - ) + userFlagName := "user" + createFlags.StringVarP( + &cf.User, + userFlagName, "u", "", + "Username or UID (format: <name|uid>[:<group|gid>])", + ) + _ = cmd.RegisterFlagCompletionFunc(userFlagName, AutocompleteUserFlag) - requiresFlagName := "requires" - createFlags.StringSliceVar( - &cf.Requires, - requiresFlagName, []string{}, - "Add one or more requirement containers that must be started before this container will start", - ) - _ = cmd.RegisterFlagCompletionFunc(requiresFlagName, AutocompleteContainers) + utsFlagName := "uts" + createFlags.String( + utsFlagName, "", + "UTS namespace to use", + ) + _ = cmd.RegisterFlagCompletionFunc(utsFlagName, AutocompleteNamespace) - restartFlagName := "restart" - createFlags.StringVar( - &cf.Restart, - restartFlagName, "", - `Restart policy to apply when a container exits ("always"|"no"|"on-failure"|"unless-stopped")`, - ) - _ = cmd.RegisterFlagCompletionFunc(restartFlagName, AutocompleteRestartOption) + mountFlagName := "mount" + createFlags.StringArrayVar( + &cf.Mount, + mountFlagName, []string{}, + "Attach a filesystem mount to the container", + ) + _ = cmd.RegisterFlagCompletionFunc(mountFlagName, AutocompleteMountFlag) + + volumeDesciption := "Bind mount a volume into the container" + if registry.IsRemote() { + volumeDesciption = "Bind mount a volume into the container. Volume src will be on the server machine, not the client" + } + volumeFlagName := "volume" + createFlags.StringArrayVarP( + &cf.Volume, + volumeFlagName, "v", volumes(), + volumeDesciption, + ) + _ = cmd.RegisterFlagCompletionFunc(volumeFlagName, AutocompleteVolumeFlag) - createFlags.BoolVar( - &cf.Rm, - "rm", false, - "Remove container (and pod if created) after exit", - ) - createFlags.BoolVar( - &cf.RootFS, - "rootfs", false, - "The first argument is not an image but the rootfs to the exploded container", - ) + volumesFromFlagName := "volumes-from" + createFlags.StringArrayVar( + &cf.VolumesFrom, + volumesFromFlagName, []string{}, + "Mount volumes from the specified container(s)", + ) + _ = cmd.RegisterFlagCompletionFunc(volumesFromFlagName, AutocompleteContainers) - sdnotifyFlagName := "sdnotify" - createFlags.StringVar( - &cf.SdNotifyMode, - sdnotifyFlagName, define.SdNotifyModeContainer, - `control sd-notify behavior ("container"|"conmon"|"ignore")`, - ) - _ = cmd.RegisterFlagCompletionFunc(sdnotifyFlagName, AutocompleteSDNotify) + workdirFlagName := "workdir" + createFlags.StringVarP( + &cf.Workdir, + workdirFlagName, "w", "", + "Working directory inside the container", + ) + _ = cmd.RegisterFlagCompletionFunc(workdirFlagName, completion.AutocompleteDefault) - secretFlagName := "secret" - createFlags.StringArrayVar( - &cf.Secrets, - secretFlagName, []string{}, - "Add secret to container", - ) - _ = cmd.RegisterFlagCompletionFunc(secretFlagName, AutocompleteSecrets) + seccompPolicyFlagName := "seccomp-policy" + createFlags.StringVar( + &cf.SeccompPolicy, + seccompPolicyFlagName, "default", + "Policy for selecting a seccomp profile (experimental)", + ) + _ = cmd.RegisterFlagCompletionFunc(seccompPolicyFlagName, completion.AutocompleteDefault) - securityOptFlagName := "security-opt" - createFlags.StringArrayVar( - &cf.SecurityOpt, - securityOptFlagName, []string{}, - "Security Options", - ) - _ = cmd.RegisterFlagCompletionFunc(securityOptFlagName, AutocompleteSecurityOption) + cgroupConfFlagName := "cgroup-conf" + createFlags.StringSliceVar( + &cf.CgroupConf, + cgroupConfFlagName, []string{}, + "Configure cgroup v2 (key=value)", + ) + _ = cmd.RegisterFlagCompletionFunc(cgroupConfFlagName, completion.AutocompleteNone) - shmSizeFlagName := "shm-size" - createFlags.String( - shmSizeFlagName, shmSize(), - "Size of /dev/shm "+sizeWithUnitFormat, - ) - _ = cmd.RegisterFlagCompletionFunc(shmSizeFlagName, completion.AutocompleteNone) + pidFileFlagName := "pidfile" + createFlags.StringVar( + &cf.PidFile, + pidFileFlagName, "", + "Write the container process ID to the file") + _ = cmd.RegisterFlagCompletionFunc(pidFileFlagName, completion.AutocompleteDefault) - stopSignalFlagName := "stop-signal" - createFlags.StringVar( - &cf.SignaturePolicy, - "signature-policy", "", - "`Pathname` of signature policy file (not usually used)", - ) - createFlags.StringVar( - &cf.StopSignal, - stopSignalFlagName, "", - "Signal to stop a container. Default is SIGTERM", - ) - _ = cmd.RegisterFlagCompletionFunc(stopSignalFlagName, AutocompleteStopSignal) + _ = createFlags.MarkHidden("signature-policy") + if registry.IsRemote() { + _ = createFlags.MarkHidden("env-host") + _ = createFlags.MarkHidden("http-proxy") + } - stopTimeoutFlagName := "stop-timeout" - createFlags.UintVar( - &cf.StopTimeout, - stopTimeoutFlagName, containerConfig.Engine.StopTimeout, - "Timeout (in seconds) that containers stopped by user command have to exit. If exceeded, the container will be forcibly stopped via SIGKILL.", - ) - _ = cmd.RegisterFlagCompletionFunc(stopTimeoutFlagName, completion.AutocompleteNone) + createFlags.BoolVar( + &cf.Replace, + "replace", false, + `If a container with the same name exists, replace it`, + ) + } subgidnameFlagName := "subgidname" createFlags.StringVar( @@ -688,60 +740,13 @@ func DefineCreateFlags(cmd *cobra.Command, cf *ContainerCLIOpts) { ) _ = cmd.RegisterFlagCompletionFunc(subuidnameFlagName, completion.AutocompleteSubuidName) - sysctlFlagName := "sysctl" + gidmapFlagName := "gidmap" createFlags.StringSliceVar( - &cf.Sysctl, - sysctlFlagName, []string{}, - "Sysctl options", - ) - //TODO: Add function for sysctl completion. - _ = cmd.RegisterFlagCompletionFunc(sysctlFlagName, completion.AutocompleteNone) - - systemdFlagName := "systemd" - createFlags.StringVar( - &cf.Systemd, - systemdFlagName, "true", - `Run container in systemd mode ("true"|"false"|"always")`, - ) - _ = cmd.RegisterFlagCompletionFunc(systemdFlagName, AutocompleteSystemdFlag) - - timeoutFlagName := "timeout" - createFlags.UintVar( - &cf.Timeout, - timeoutFlagName, 0, - "Maximum length of time a container is allowed to run. The container will be killed automatically after the time expires.", - ) - _ = cmd.RegisterFlagCompletionFunc(timeoutFlagName, completion.AutocompleteNone) - - tmpfsFlagName := "tmpfs" - createFlags.StringArrayVar( - &cf.TmpFS, - tmpfsFlagName, []string{}, - "Mount a temporary filesystem (`tmpfs`) into a container", - ) - _ = cmd.RegisterFlagCompletionFunc(tmpfsFlagName, completion.AutocompleteDefault) - - createFlags.BoolVarP( - &cf.TTY, - "tty", "t", false, - "Allocate a pseudo-TTY for container", - ) - - timezoneFlagName := "tz" - createFlags.StringVar( - &cf.Timezone, - timezoneFlagName, containerConfig.TZ(), - "Set timezone in container", - ) - _ = cmd.RegisterFlagCompletionFunc(timezoneFlagName, completion.AutocompleteNone) //TODO: add timezone completion - - umaskFlagName := "umask" - createFlags.StringVar( - &cf.Umask, - umaskFlagName, containerConfig.Umask(), - "Set umask in container", + &cf.GIDMap, + gidmapFlagName, []string{}, + "GID map to use for the user namespace", ) - _ = cmd.RegisterFlagCompletionFunc(umaskFlagName, completion.AutocompleteNone) + _ = cmd.RegisterFlagCompletionFunc(gidmapFlagName, completion.AutocompleteNone) uidmapFlagName := "uidmap" createFlags.StringSliceVar( @@ -751,22 +756,6 @@ func DefineCreateFlags(cmd *cobra.Command, cf *ContainerCLIOpts) { ) _ = cmd.RegisterFlagCompletionFunc(uidmapFlagName, completion.AutocompleteNone) - ulimitFlagName := "ulimit" - createFlags.StringSliceVar( - &cf.Ulimit, - ulimitFlagName, ulimits(), - "Ulimit options", - ) - _ = cmd.RegisterFlagCompletionFunc(ulimitFlagName, completion.AutocompleteNone) - - userFlagName := "user" - createFlags.StringVarP( - &cf.User, - userFlagName, "u", "", - "Username or UID (format: <name|uid>[:<group|gid>])", - ) - _ = cmd.RegisterFlagCompletionFunc(userFlagName, AutocompleteUserFlag) - usernsFlagName := "userns" createFlags.String( usernsFlagName, os.Getenv("PODMAN_USERNS"), @@ -774,75 +763,106 @@ func DefineCreateFlags(cmd *cobra.Command, cf *ContainerCLIOpts) { ) _ = cmd.RegisterFlagCompletionFunc(usernsFlagName, AutocompleteUserNamespace) - utsFlagName := "uts" - createFlags.String( - utsFlagName, "", - "UTS namespace to use", + cgroupParentFlagName := "cgroup-parent" + createFlags.StringVar( + &cf.CGroupParent, + cgroupParentFlagName, "", + "Optional parent cgroup for the container", ) - _ = cmd.RegisterFlagCompletionFunc(utsFlagName, AutocompleteNamespace) + _ = cmd.RegisterFlagCompletionFunc(cgroupParentFlagName, completion.AutocompleteDefault) - mountFlagName := "mount" - createFlags.StringArrayVar( - &cf.Mount, - mountFlagName, []string{}, - "Attach a filesystem mount to the container", + conmonPidfileFlagName := "" + if !isInfra { + conmonPidfileFlagName = "conmon-pidfile" + } else { + conmonPidfileFlagName = "infra-conmon-pidfile" + } + createFlags.StringVar( + &cf.ConmonPIDFile, + conmonPidfileFlagName, "", + "Path to the file that will receive the PID of conmon", ) - _ = cmd.RegisterFlagCompletionFunc(mountFlagName, AutocompleteMountFlag) + _ = cmd.RegisterFlagCompletionFunc(conmonPidfileFlagName, completion.AutocompleteDefault) - volumeDesciption := "Bind mount a volume into the container" - if registry.IsRemote() { - volumeDesciption = "Bind mount a volume into the container. Volume src will be on the server machine, not the client" - } - volumeFlagName := "volume" - createFlags.StringArrayVarP( - &cf.Volume, - volumeFlagName, "v", volumes(), - volumeDesciption, + cpusFlagName := "cpus" + createFlags.Float64Var( + &cf.CPUS, + cpusFlagName, 0, + "Number of CPUs. The default is 0.000 which means no limit", + ) + _ = cmd.RegisterFlagCompletionFunc(cpusFlagName, completion.AutocompleteNone) + + cpusetCpusFlagName := "cpuset-cpus" + createFlags.StringVar( + &cf.CPUSetCPUs, + cpusetCpusFlagName, "", + "CPUs in which to allow execution (0-3, 0,1)", ) - _ = cmd.RegisterFlagCompletionFunc(volumeFlagName, AutocompleteVolumeFlag) + _ = cmd.RegisterFlagCompletionFunc(cpusetCpusFlagName, completion.AutocompleteNone) + + entrypointFlagName := "" + if !isInfra { + entrypointFlagName = "entrypoint" + } else { + entrypointFlagName = "infra-command" + } - volumesFromFlagName := "volumes-from" - createFlags.StringArrayVar( - &cf.VolumesFrom, - volumesFromFlagName, []string{}, - "Mount volumes from the specified container(s)", + createFlags.String(entrypointFlagName, "", + "Overwrite the default ENTRYPOINT of the image", ) - _ = cmd.RegisterFlagCompletionFunc(volumesFromFlagName, AutocompleteContainers) + _ = cmd.RegisterFlagCompletionFunc(entrypointFlagName, completion.AutocompleteNone) - workdirFlagName := "workdir" + hostnameFlagName := "hostname" createFlags.StringVarP( - &cf.Workdir, - workdirFlagName, "w", "", - "Working directory inside the container", + &cf.Hostname, + hostnameFlagName, "h", "", + "Set container hostname", ) - _ = cmd.RegisterFlagCompletionFunc(workdirFlagName, completion.AutocompleteDefault) + _ = cmd.RegisterFlagCompletionFunc(hostnameFlagName, completion.AutocompleteNone) - seccompPolicyFlagName := "seccomp-policy" - createFlags.StringVar( - &cf.SeccompPolicy, - seccompPolicyFlagName, "default", - "Policy for selecting a seccomp profile (experimental)", + labelFlagName := "label" + createFlags.StringArrayVarP( + &cf.Label, + labelFlagName, "l", []string{}, + "Set metadata on container", ) - _ = cmd.RegisterFlagCompletionFunc(seccompPolicyFlagName, completion.AutocompleteDefault) + _ = cmd.RegisterFlagCompletionFunc(labelFlagName, completion.AutocompleteNone) - cgroupConfFlagName := "cgroup-conf" + labelFileFlagName := "label-file" createFlags.StringSliceVar( - &cf.CgroupConf, - cgroupConfFlagName, []string{}, - "Configure cgroup v2 (key=value)", + &cf.LabelFile, + labelFileFlagName, []string{}, + "Read in a line delimited file of labels", ) - _ = cmd.RegisterFlagCompletionFunc(cgroupConfFlagName, completion.AutocompleteNone) + _ = cmd.RegisterFlagCompletionFunc(labelFileFlagName, completion.AutocompleteDefault) - pidFileFlagName := "pidfile" - createFlags.StringVar( - &cf.PidFile, - pidFileFlagName, "", - "Write the container process ID to the file") - _ = cmd.RegisterFlagCompletionFunc(pidFileFlagName, completion.AutocompleteDefault) - - _ = createFlags.MarkHidden("signature-policy") - if registry.IsRemote() { - _ = createFlags.MarkHidden("env-host") - _ = createFlags.MarkHidden("http-proxy") + nameFlagName := "" + if !isInfra { + nameFlagName = "name" + createFlags.StringVar( + &cf.Name, + nameFlagName, "", + "Assign a name to the container", + ) + } else { + nameFlagName = "infra-name" + createFlags.StringVar( + &cf.Name, + nameFlagName, "", + "Assign a name to the container", + ) } + _ = cmd.RegisterFlagCompletionFunc(nameFlagName, completion.AutocompleteNone) + + createFlags.Bool( + "help", false, "", + ) + + pidFlagName := "pid" + createFlags.StringVar( + &cf.PID, + pidFlagName, "", + "PID namespace to use", + ) + _ = cmd.RegisterFlagCompletionFunc(pidFlagName, AutocompleteNamespace) } diff --git a/cmd/podman/common/create_opts.go b/cmd/podman/common/create_opts.go index c94f46cf2..09ac61f2e 100644 --- a/cmd/podman/common/create_opts.go +++ b/cmd/podman/common/create_opts.go @@ -16,125 +16,10 @@ import ( "github.com/containers/podman/v3/pkg/domain/entities" "github.com/containers/podman/v3/pkg/rootless" "github.com/containers/podman/v3/pkg/specgen" + "github.com/docker/docker/api/types/mount" "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 +30,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 @@ -210,18 +95,30 @@ func ContainerCreateToContainerCLIOpts(cc handlers.CreateContainerConfig, rtc *c expose = append(expose, fmt.Sprintf("%s/%s", p.Port(), p.Proto())) } - // mounts type=tmpfs/bind,source=,dest=,opt=val - // TODO options + // mounts type=tmpfs/bind,source=...,target=...=,opt=val mounts := make([]string, 0, len(cc.HostConfig.Mounts)) + var builder strings.Builder for _, m := range cc.HostConfig.Mounts { - mount := fmt.Sprintf("type=%s", m.Type) - if len(m.Source) > 0 { - mount += fmt.Sprintf(",source=%s", m.Source) + addField(&builder, "type", string(m.Type)) + addField(&builder, "source", m.Source) + addField(&builder, "target", m.Target) + addField(&builder, "ro", strconv.FormatBool(m.ReadOnly)) + addField(&builder, "consistency", string(m.Consistency)) + + // Map any specialized mount options that intersect between *Options and cli options + switch m.Type { + case mount.TypeBind: + addField(&builder, "bind-propagation", string(m.BindOptions.Propagation)) + addField(&builder, "bind-nonrecursive", strconv.FormatBool(m.BindOptions.NonRecursive)) + case mount.TypeTmpfs: + addField(&builder, "tmpfs-size", strconv.FormatInt(m.TmpfsOptions.SizeBytes, 10)) + addField(&builder, "tmpfs-mode", strconv.FormatUint(uint64(m.TmpfsOptions.Mode), 10)) + case mount.TypeVolume: + // All current VolumeOpts are handled above + // See vendor/github.com/containers/common/pkg/parse/parse.go:ValidateVolumeOpts() } - if len(m.Target) > 0 { - mount += fmt.Sprintf(",dst=%s", m.Target) - } - mounts = append(mounts, mount) + mounts = append(mounts, builder.String()) + builder.Reset() } // dns @@ -341,7 +238,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...), @@ -622,3 +519,17 @@ func logDriver() string { } return "" } + +// addField is a helper function to populate mount options +func addField(b *strings.Builder, name string, value string) { + if value == "" { + return + } + + if b.Len() > 0 { + b.WriteRune(',') + } + b.WriteString(name) + b.WriteRune('=') + b.WriteString(value) +} diff --git a/cmd/podman/common/createparse.go b/cmd/podman/common/createparse.go deleted file mode 100644 index dcef1a151..000000000 --- a/cmd/podman/common/createparse.go +++ /dev/null @@ -1,29 +0,0 @@ -package common - -import ( - "github.com/containers/common/pkg/config" - "github.com/pkg/errors" -) - -// validate determines if the flags and values given by the user are valid. things checked -// by validate must not need any state information on the flag (i.e. changed) -func (c *ContainerCLIOpts) validate() error { - var () - if c.Rm && (c.Restart != "" && c.Restart != "no" && c.Restart != "on-failure") { - return errors.Errorf(`the --rm option conflicts with --restart, when the restartPolicy is not "" and "no"`) - } - - if _, err := config.ParsePullPolicy(c.Pull); err != nil { - return err - } - - var imageVolType = map[string]string{ - "bind": "", - "tmpfs": "", - "ignore": "", - } - if _, ok := imageVolType[c.ImageVolume]; !ok { - return errors.Errorf("invalid image-volume type %q. Pick one of bind, tmpfs, or ignore", c.ImageVolume) - } - return nil -} diff --git a/cmd/podman/common/netflags.go b/cmd/podman/common/netflags.go index aa8714b50..d11f3c9d2 100644 --- a/cmd/podman/common/netflags.go +++ b/cmd/podman/common/netflags.go @@ -8,8 +8,10 @@ import ( "github.com/containers/podman/v3/libpod/define" "github.com/containers/podman/v3/pkg/domain/entities" "github.com/containers/podman/v3/pkg/specgen" + "github.com/containers/podman/v3/pkg/specgenutil" "github.com/pkg/errors" "github.com/spf13/cobra" + "github.com/spf13/pflag" ) func DefineNetFlags(cmd *cobra.Command) { @@ -87,12 +89,15 @@ func DefineNetFlags(cmd *cobra.Command) { // NetFlagsToNetOptions parses the network flags for the given cmd. // The netnsFromConfig bool is used to indicate if the --network flag // should always be parsed regardless if it was set on the cli. -func NetFlagsToNetOptions(cmd *cobra.Command, netnsFromConfig bool) (*entities.NetOptions, error) { +func NetFlagsToNetOptions(opts *entities.NetOptions, flags pflag.FlagSet, netnsFromConfig bool) (*entities.NetOptions, error) { var ( err error ) - opts := entities.NetOptions{} - opts.AddHosts, err = cmd.Flags().GetStringSlice("add-host") + if opts == nil { + opts = &entities.NetOptions{} + } + + opts.AddHosts, err = flags.GetStringSlice("add-host") if err != nil { return nil, err } @@ -103,56 +108,50 @@ func NetFlagsToNetOptions(cmd *cobra.Command, netnsFromConfig bool) (*entities.N } } - if cmd.Flags().Changed("dns") { - servers, err := cmd.Flags().GetStringSlice("dns") - if err != nil { - return nil, err - } - for _, d := range servers { - if d == "none" { - opts.UseImageResolvConf = true - if len(servers) > 1 { - return nil, errors.Errorf("%s is not allowed to be specified with other DNS ip addresses", d) - } - break - } - dns := net.ParseIP(d) - if dns == nil { - return nil, errors.Errorf("%s is not an ip address", d) + servers, err := flags.GetStringSlice("dns") + if err != nil { + return nil, err + } + for _, d := range servers { + if d == "none" { + opts.UseImageResolvConf = true + if len(servers) > 1 { + return nil, errors.Errorf("%s is not allowed to be specified with other DNS ip addresses", d) } - opts.DNSServers = append(opts.DNSServers, dns) + break + } + dns := net.ParseIP(d) + if dns == nil { + return nil, errors.Errorf("%s is not an ip address", d) } + opts.DNSServers = append(opts.DNSServers, dns) } - if cmd.Flags().Changed("dns-opt") { - options, err := cmd.Flags().GetStringSlice("dns-opt") - if err != nil { - return nil, err - } - opts.DNSOptions = options + options, err := flags.GetStringSlice("dns-opt") + if err != nil { + return nil, err } + opts.DNSOptions = options - if cmd.Flags().Changed("dns-search") { - dnsSearches, err := cmd.Flags().GetStringSlice("dns-search") - if err != nil { - return nil, err - } - // Validate domains are good - for _, dom := range dnsSearches { - if dom == "." { - if len(dnsSearches) > 1 { - return nil, errors.Errorf("cannot pass additional search domains when also specifying '.'") - } - continue - } - if _, err := parse.ValidateDomain(dom); err != nil { - return nil, err + dnsSearches, err := flags.GetStringSlice("dns-search") + if err != nil { + return nil, err + } + // Validate domains are good + for _, dom := range dnsSearches { + if dom == "." { + if len(dnsSearches) > 1 { + return nil, errors.Errorf("cannot pass additional search domains when also specifying '.'") } + continue + } + if _, err := parse.ValidateDomain(dom); err != nil { + return nil, err } - opts.DNSSearch = dnsSearches } + opts.DNSSearch = dnsSearches - m, err := cmd.Flags().GetString("mac-address") + m, err := flags.GetString("mac-address") if err != nil { return nil, err } @@ -164,18 +163,18 @@ func NetFlagsToNetOptions(cmd *cobra.Command, netnsFromConfig bool) (*entities.N opts.StaticMAC = &mac } - inputPorts, err := cmd.Flags().GetStringSlice("publish") + inputPorts, err := flags.GetStringSlice("publish") if err != nil { return nil, err } if len(inputPorts) > 0 { - opts.PublishPorts, err = CreatePortBindings(inputPorts) + opts.PublishPorts, err = specgenutil.CreatePortBindings(inputPorts) if err != nil { return nil, err } } - ip, err := cmd.Flags().GetString("ip") + ip, err := flags.GetString("ip") if err != nil { return nil, err } @@ -190,15 +189,15 @@ func NetFlagsToNetOptions(cmd *cobra.Command, netnsFromConfig bool) (*entities.N opts.StaticIP = &staticIP } - opts.NoHosts, err = cmd.Flags().GetBool("no-hosts") + opts.NoHosts, err = flags.GetBool("no-hosts") if err != nil { return nil, err } // parse the --network value only when the flag is set or we need to use // the netns config value, e.g. when --pod is not used - if netnsFromConfig || cmd.Flag("network").Changed { - network, err := cmd.Flags().GetString("network") + if netnsFromConfig || flags.Changed("network") { + network, err := flags.GetString("network") if err != nil { return nil, err } @@ -215,12 +214,13 @@ func NetFlagsToNetOptions(cmd *cobra.Command, netnsFromConfig bool) (*entities.N opts.CNINetworks = cniNets } - aliases, err := cmd.Flags().GetStringSlice("network-alias") + aliases, err := flags.GetStringSlice("network-alias") if err != nil { return nil, err } if len(aliases) > 0 { opts.Aliases = aliases } - return &opts, err + + return opts, err } diff --git a/cmd/podman/common/ports.go b/cmd/podman/common/ports.go deleted file mode 100644 index 2092bbe53..000000000 --- a/cmd/podman/common/ports.go +++ /dev/null @@ -1,22 +0,0 @@ -package common - -import ( - "github.com/docker/go-connections/nat" - "github.com/pkg/errors" -) - -func verifyExpose(expose []string) error { - // add the expose ports from the user (--expose) - // can be single or a range - for _, expose := range expose { - // support two formats for expose, original format <portnum>/[<proto>] or <startport-endport>/[<proto>] - _, port := nat.SplitProtoPort(expose) - // parse the start and end port and create a sequence of ports to expose - // if expose a port, the start and end port are the same - _, _, err := nat.ParsePortRange(port) - if err != nil { - return errors.Wrapf(err, "invalid range format for --expose: %s", expose) - } - } - return nil -} diff --git a/cmd/podman/common/specgen.go b/cmd/podman/common/specgen.go deleted file mode 100644 index 59d32f568..000000000 --- a/cmd/podman/common/specgen.go +++ /dev/null @@ -1,972 +0,0 @@ -package common - -import ( - "fmt" - "os" - "strconv" - "strings" - "time" - - "github.com/containers/image/v5/manifest" - "github.com/containers/podman/v3/cmd/podman/parse" - "github.com/containers/podman/v3/libpod/define" - ann "github.com/containers/podman/v3/pkg/annotations" - envLib "github.com/containers/podman/v3/pkg/env" - ns "github.com/containers/podman/v3/pkg/namespaces" - "github.com/containers/podman/v3/pkg/specgen" - systemdDefine "github.com/containers/podman/v3/pkg/systemd/define" - "github.com/containers/podman/v3/pkg/util" - "github.com/docker/go-units" - "github.com/opencontainers/runtime-spec/specs-go" - "github.com/pkg/errors" -) - -func getCPULimits(c *ContainerCLIOpts) *specs.LinuxCPU { - cpu := &specs.LinuxCPU{} - hasLimits := false - - if c.CPUS > 0 { - period, quota := util.CoresToPeriodAndQuota(c.CPUS) - - cpu.Period = &period - cpu.Quota = "a - hasLimits = true - } - if c.CPUShares > 0 { - cpu.Shares = &c.CPUShares - hasLimits = true - } - if c.CPUPeriod > 0 { - cpu.Period = &c.CPUPeriod - hasLimits = true - } - if c.CPUSetCPUs != "" { - cpu.Cpus = c.CPUSetCPUs - hasLimits = true - } - if c.CPUSetMems != "" { - cpu.Mems = c.CPUSetMems - hasLimits = true - } - if c.CPUQuota > 0 { - cpu.Quota = &c.CPUQuota - hasLimits = true - } - if c.CPURTPeriod > 0 { - cpu.RealtimePeriod = &c.CPURTPeriod - hasLimits = true - } - if c.CPURTRuntime > 0 { - cpu.RealtimeRuntime = &c.CPURTRuntime - hasLimits = true - } - - if !hasLimits { - return nil - } - return cpu -} - -func getIOLimits(s *specgen.SpecGenerator, c *ContainerCLIOpts) (*specs.LinuxBlockIO, error) { - var err error - io := &specs.LinuxBlockIO{} - hasLimits := false - if b := c.BlkIOWeight; len(b) > 0 { - u, err := strconv.ParseUint(b, 10, 16) - if err != nil { - return nil, errors.Wrapf(err, "invalid value for blkio-weight") - } - nu := uint16(u) - io.Weight = &nu - hasLimits = true - } - - if len(c.BlkIOWeightDevice) > 0 { - if err := parseWeightDevices(s, c.BlkIOWeightDevice); err != nil { - return nil, err - } - hasLimits = true - } - - if bps := c.DeviceReadBPs; len(bps) > 0 { - if s.ThrottleReadBpsDevice, err = parseThrottleBPSDevices(bps); err != nil { - return nil, err - } - hasLimits = true - } - - if bps := c.DeviceWriteBPs; len(bps) > 0 { - if s.ThrottleWriteBpsDevice, err = parseThrottleBPSDevices(bps); err != nil { - return nil, err - } - hasLimits = true - } - - if iops := c.DeviceReadIOPs; len(iops) > 0 { - if s.ThrottleReadIOPSDevice, err = parseThrottleIOPsDevices(iops); err != nil { - return nil, err - } - hasLimits = true - } - - if iops := c.DeviceWriteIOPs; len(iops) > 0 { - if s.ThrottleWriteIOPSDevice, err = parseThrottleIOPsDevices(iops); err != nil { - return nil, err - } - hasLimits = true - } - - if !hasLimits { - return nil, nil - } - return io, nil -} - -func getMemoryLimits(s *specgen.SpecGenerator, c *ContainerCLIOpts) (*specs.LinuxMemory, error) { - var err error - memory := &specs.LinuxMemory{} - hasLimits := false - if m := c.Memory; len(m) > 0 { - ml, err := units.RAMInBytes(m) - if err != nil { - return nil, errors.Wrapf(err, "invalid value for memory") - } - memory.Limit = &ml - if c.MemorySwap == "" { - limit := 2 * ml - memory.Swap = &(limit) - } - hasLimits = true - } - if m := c.MemoryReservation; len(m) > 0 { - mr, err := units.RAMInBytes(m) - if err != nil { - return nil, errors.Wrapf(err, "invalid value for memory") - } - memory.Reservation = &mr - hasLimits = true - } - if m := c.MemorySwap; len(m) > 0 { - var ms int64 - // only set memory swap if it was set - // -1 indicates unlimited - if m != "-1" { - ms, err = units.RAMInBytes(m) - memory.Swap = &ms - if err != nil { - return nil, errors.Wrapf(err, "invalid value for memory") - } - hasLimits = true - } - } - if m := c.KernelMemory; len(m) > 0 { - mk, err := units.RAMInBytes(m) - if err != nil { - return nil, errors.Wrapf(err, "invalid value for kernel-memory") - } - memory.Kernel = &mk - hasLimits = true - } - if c.MemorySwappiness >= 0 { - swappiness := uint64(c.MemorySwappiness) - memory.Swappiness = &swappiness - hasLimits = true - } - if c.OOMKillDisable { - memory.DisableOOMKiller = &c.OOMKillDisable - hasLimits = true - } - if !hasLimits { - return nil, nil - } - return memory, nil -} - -func setNamespaces(s *specgen.SpecGenerator, c *ContainerCLIOpts) error { - var err error - - if c.PID != "" { - s.PidNS, err = specgen.ParseNamespace(c.PID) - if err != nil { - return err - } - } - if c.IPC != "" { - s.IpcNS, err = specgen.ParseNamespace(c.IPC) - if err != nil { - return err - } - } - if c.UTS != "" { - s.UtsNS, err = specgen.ParseNamespace(c.UTS) - if err != nil { - return err - } - } - if c.CgroupNS != "" { - s.CgroupNS, err = specgen.ParseNamespace(c.CgroupNS) - if err != nil { - return err - } - } - // userns must be treated differently - if c.UserNS != "" { - s.UserNS, err = specgen.ParseUserNamespace(c.UserNS) - if err != nil { - return err - } - } - if c.Net != nil { - s.NetNS = c.Net.Network - } - return nil -} - -func FillOutSpecGen(s *specgen.SpecGenerator, c *ContainerCLIOpts, args []string) error { - var ( - err error - ) - - // validate flags as needed - if err := c.validate(); err != nil { - return err - } - - s.User = c.User - inputCommand := args[1:] - if len(c.HealthCmd) > 0 { - if c.NoHealthCheck { - return errors.New("Cannot specify both --no-healthcheck and --health-cmd") - } - s.HealthConfig, err = makeHealthCheckFromCli(c.HealthCmd, c.HealthInterval, c.HealthRetries, c.HealthTimeout, c.HealthStartPeriod) - if err != nil { - return err - } - } else if c.NoHealthCheck { - s.HealthConfig = &manifest.Schema2HealthConfig{ - Test: []string{"NONE"}, - } - } - - userNS := ns.UsernsMode(c.UserNS) - s.IDMappings, err = util.ParseIDMapping(userNS, c.UIDMap, c.GIDMap, c.SubUIDName, c.SubGIDName) - if err != nil { - return err - } - // If some mappings are specified, assume a private user namespace - if userNS.IsDefaultValue() && (!s.IDMappings.HostUIDMapping || !s.IDMappings.HostGIDMapping) { - s.UserNS.NSMode = specgen.Private - } else { - s.UserNS.NSMode = specgen.NamespaceMode(userNS) - } - - s.Terminal = c.TTY - - if err := verifyExpose(c.Expose); err != nil { - return err - } - // We are not handling the Expose flag yet. - // s.PortsExpose = c.Expose - s.PortMappings = c.Net.PublishPorts - s.PublishExposedPorts = c.PublishAll - s.Pod = c.Pod - - if len(c.PodIDFile) > 0 { - if len(s.Pod) > 0 { - return errors.New("Cannot specify both --pod and --pod-id-file") - } - podID, err := ReadPodIDFile(c.PodIDFile) - if err != nil { - return err - } - s.Pod = podID - } - - expose, err := createExpose(c.Expose) - if err != nil { - return err - } - s.Expose = expose - - if err := setNamespaces(s, c); err != nil { - return err - } - - if sig := c.StopSignal; len(sig) > 0 { - stopSignal, err := util.ParseSignal(sig) - if err != nil { - return err - } - s.StopSignal = &stopSignal - } - - // ENVIRONMENT VARIABLES - // - // Precedence order (higher index wins): - // 1) containers.conf (EnvHost, EnvHTTP, Env) 2) image data, 3 User EnvHost/EnvHTTP, 4) env-file, 5) env - // containers.conf handled and image data handled on the server side - // user specified EnvHost and EnvHTTP handled on Server Side relative to Server - // env-file and env handled on client side - var env map[string]string - - // First transform the os env into a map. We need it for the labels later in - // any case. - osEnv, err := envLib.ParseSlice(os.Environ()) - if err != nil { - return errors.Wrap(err, "error parsing host environment variables") - } - - s.EnvHost = c.EnvHost - s.HTTPProxy = c.HTTPProxy - - // env-file overrides any previous variables - for _, f := range c.EnvFile { - fileEnv, err := envLib.ParseFile(f) - if err != nil { - return err - } - // File env is overridden by env. - env = envLib.Join(env, fileEnv) - } - - parsedEnv, err := envLib.ParseSlice(c.Env) - if err != nil { - return err - } - - s.Env = envLib.Join(env, parsedEnv) - - // LABEL VARIABLES - labels, err := parse.GetAllLabels(c.LabelFile, c.Label) - if err != nil { - return errors.Wrapf(err, "unable to process labels") - } - - if systemdUnit, exists := osEnv[systemdDefine.EnvVariable]; exists { - labels[systemdDefine.EnvVariable] = systemdUnit - } - - s.Labels = labels - - // ANNOTATIONS - annotations := make(map[string]string) - - // First, add our default annotations - annotations[ann.TTY] = "false" - if c.TTY { - annotations[ann.TTY] = "true" - } - - // Last, add user annotations - for _, annotation := range c.Annotation { - splitAnnotation := strings.SplitN(annotation, "=", 2) - if len(splitAnnotation) < 2 { - return errors.Errorf("Annotations must be formatted KEY=VALUE") - } - annotations[splitAnnotation[0]] = splitAnnotation[1] - } - s.Annotations = annotations - - s.WorkDir = c.Workdir - if c.Entrypoint != nil { - entrypoint := []string{} - if ep := *c.Entrypoint; len(ep) > 0 { - // Check if entrypoint specified is json - if err := json.Unmarshal([]byte(*c.Entrypoint), &entrypoint); err != nil { - entrypoint = append(entrypoint, ep) - } - } - s.Entrypoint = entrypoint - } - - // Include the command used to create the container. - s.ContainerCreateCommand = os.Args - - if len(inputCommand) > 0 { - s.Command = inputCommand - } - - // SHM Size - if c.ShmSize != "" { - shmSize, err := units.FromHumanSize(c.ShmSize) - if err != nil { - return errors.Wrapf(err, "unable to translate --shm-size") - } - s.ShmSize = &shmSize - } - s.CNINetworks = c.Net.CNINetworks - - // Network aliases - if len(c.Net.Aliases) > 0 { - // build a map of aliases where key=cniName - aliases := make(map[string][]string, len(s.CNINetworks)) - for _, cniNetwork := range s.CNINetworks { - aliases[cniNetwork] = c.Net.Aliases - } - s.Aliases = aliases - } - - s.HostAdd = c.Net.AddHosts - s.UseImageResolvConf = c.Net.UseImageResolvConf - s.DNSServers = c.Net.DNSServers - s.DNSSearch = c.Net.DNSSearch - s.DNSOptions = c.Net.DNSOptions - s.StaticIP = c.Net.StaticIP - s.StaticMAC = c.Net.StaticMAC - s.NetworkOptions = c.Net.NetworkOptions - s.UseImageHosts = c.Net.NoHosts - - s.ImageVolumeMode = c.ImageVolume - if s.ImageVolumeMode == "bind" { - s.ImageVolumeMode = "anonymous" - } - - s.Systemd = c.Systemd - s.SdNotifyMode = c.SdNotifyMode - if s.ResourceLimits == nil { - s.ResourceLimits = &specs.LinuxResources{} - } - s.ResourceLimits.Memory, err = getMemoryLimits(s, c) - if err != nil { - return err - } - s.ResourceLimits.BlockIO, err = getIOLimits(s, c) - if err != nil { - return err - } - if c.PIDsLimit != nil { - pids := specs.LinuxPids{ - Limit: *c.PIDsLimit, - } - - s.ResourceLimits.Pids = &pids - } - s.ResourceLimits.CPU = getCPULimits(c) - - unifieds := make(map[string]string) - for _, unified := range c.CgroupConf { - splitUnified := strings.SplitN(unified, "=", 2) - if len(splitUnified) < 2 { - return errors.Errorf("--cgroup-conf must be formatted KEY=VALUE") - } - unifieds[splitUnified[0]] = splitUnified[1] - } - if len(unifieds) > 0 { - s.ResourceLimits.Unified = unifieds - } - - if s.ResourceLimits.CPU == nil && s.ResourceLimits.Pids == nil && s.ResourceLimits.BlockIO == nil && s.ResourceLimits.Memory == nil && s.ResourceLimits.Unified == nil { - s.ResourceLimits = nil - } - - if s.LogConfiguration == nil { - s.LogConfiguration = &specgen.LogConfig{} - } - - if ld := c.LogDriver; len(ld) > 0 { - s.LogConfiguration.Driver = ld - } - s.CgroupParent = c.CGroupParent - s.CgroupsMode = c.CGroupsMode - s.Groups = c.GroupAdd - - s.Hostname = c.Hostname - sysctl := map[string]string{} - if ctl := c.Sysctl; len(ctl) > 0 { - sysctl, err = util.ValidateSysctls(ctl) - if err != nil { - return err - } - } - s.Sysctl = sysctl - - s.CapAdd = c.CapAdd - s.CapDrop = c.CapDrop - s.Privileged = c.Privileged - s.ReadOnlyFilesystem = c.ReadOnly - s.ConmonPidFile = c.ConmonPIDFile - - s.DependencyContainers = c.Requires - - // TODO - // outside of specgen and oci though - // defaults to true, check spec/storage - // s.readonly = c.ReadOnlyTmpFS - // TODO convert to map? - // check if key=value and convert - sysmap := make(map[string]string) - for _, ctl := range c.Sysctl { - splitCtl := strings.SplitN(ctl, "=", 2) - if len(splitCtl) < 2 { - return errors.Errorf("invalid sysctl value %q", ctl) - } - sysmap[splitCtl[0]] = splitCtl[1] - } - s.Sysctl = sysmap - - if c.CIDFile != "" { - s.Annotations[define.InspectAnnotationCIDFile] = c.CIDFile - } - - for _, opt := range c.SecurityOpt { - if opt == "no-new-privileges" { - s.ContainerSecurityConfig.NoNewPrivileges = true - } else { - con := strings.SplitN(opt, "=", 2) - if len(con) != 2 { - return fmt.Errorf("invalid --security-opt 1: %q", opt) - } - switch con[0] { - case "apparmor": - s.ContainerSecurityConfig.ApparmorProfile = con[1] - s.Annotations[define.InspectAnnotationApparmor] = con[1] - case "label": - // TODO selinux opts and label opts are the same thing - s.ContainerSecurityConfig.SelinuxOpts = append(s.ContainerSecurityConfig.SelinuxOpts, con[1]) - s.Annotations[define.InspectAnnotationLabel] = strings.Join(s.ContainerSecurityConfig.SelinuxOpts, ",label=") - case "mask": - s.ContainerSecurityConfig.Mask = append(s.ContainerSecurityConfig.Mask, strings.Split(con[1], ":")...) - case "proc-opts": - s.ProcOpts = strings.Split(con[1], ",") - case "seccomp": - s.SeccompProfilePath = con[1] - s.Annotations[define.InspectAnnotationSeccomp] = con[1] - // this option is for docker compatibility, it is the same as unmask=ALL - case "systempaths": - if con[1] == "unconfined" { - s.ContainerSecurityConfig.Unmask = append(s.ContainerSecurityConfig.Unmask, []string{"ALL"}...) - } else { - return fmt.Errorf("invalid systempaths option %q, only `unconfined` is supported", con[1]) - } - case "unmask": - s.ContainerSecurityConfig.Unmask = append(s.ContainerSecurityConfig.Unmask, con[1:]...) - default: - return fmt.Errorf("invalid --security-opt 2: %q", opt) - } - } - } - - s.SeccompPolicy = c.SeccompPolicy - - s.VolumesFrom = c.VolumesFrom - - // Only add read-only tmpfs mounts in case that we are read-only and the - // read-only tmpfs flag has been set. - mounts, volumes, overlayVolumes, imageVolumes, err := parseVolumes(c.Volume, c.Mount, c.TmpFS, c.ReadOnlyTmpFS && c.ReadOnly) - if err != nil { - return err - } - s.Mounts = mounts - s.Volumes = volumes - s.OverlayVolumes = overlayVolumes - s.ImageVolumes = imageVolumes - - for _, dev := range c.Devices { - s.Devices = append(s.Devices, specs.LinuxDevice{Path: dev}) - } - - for _, rule := range c.DeviceCGroupRule { - dev, err := parseLinuxResourcesDeviceAccess(rule) - if err != nil { - return err - } - s.DeviceCGroupRule = append(s.DeviceCGroupRule, dev) - } - - s.Init = c.Init - s.InitPath = c.InitPath - s.Stdin = c.Interactive - // quiet - // DeviceCgroupRules: c.StringSlice("device-cgroup-rule"), - - // Rlimits/Ulimits - for _, u := range c.Ulimit { - if u == "host" { - s.Rlimits = nil - break - } - ul, err := units.ParseUlimit(u) - if err != nil { - return errors.Wrapf(err, "ulimit option %q requires name=SOFT:HARD, failed to be parsed", u) - } - rl := specs.POSIXRlimit{ - Type: ul.Name, - Hard: uint64(ul.Hard), - Soft: uint64(ul.Soft), - } - s.Rlimits = append(s.Rlimits, rl) - } - - logOpts := make(map[string]string) - for _, o := range c.LogOptions { - split := strings.SplitN(o, "=", 2) - if len(split) < 2 { - return errors.Errorf("invalid log option %q", o) - } - switch strings.ToLower(split[0]) { - case "driver": - s.LogConfiguration.Driver = split[1] - case "path": - s.LogConfiguration.Path = split[1] - case "max-size": - logSize, err := units.FromHumanSize(split[1]) - if err != nil { - return err - } - s.LogConfiguration.Size = logSize - default: - logOpts[split[0]] = split[1] - } - } - s.LogConfiguration.Options = logOpts - s.Name = c.Name - s.PreserveFDs = c.PreserveFDs - - s.OOMScoreAdj = &c.OOMScoreAdj - if c.Restart != "" { - splitRestart := strings.Split(c.Restart, ":") - switch len(splitRestart) { - case 1: - // No retries specified - case 2: - if strings.ToLower(splitRestart[0]) != "on-failure" { - return errors.Errorf("restart policy retries can only be specified with on-failure restart policy") - } - retries, err := strconv.Atoi(splitRestart[1]) - if err != nil { - return errors.Wrapf(err, "error parsing restart policy retry count") - } - if retries < 0 { - return errors.Errorf("must specify restart policy retry count as a number greater than 0") - } - var retriesUint = uint(retries) - s.RestartRetries = &retriesUint - default: - return errors.Errorf("invalid restart policy: may specify retries at most once") - } - s.RestartPolicy = splitRestart[0] - } - - s.Secrets, s.EnvSecrets, err = parseSecrets(c.Secrets) - if err != nil { - return err - } - - if c.Personality != "" { - s.Personality = &specs.LinuxPersonality{} - s.Personality.Domain = specs.LinuxPersonalityDomain(c.Personality) - } - - s.Remove = c.Rm - s.StopTimeout = &c.StopTimeout - s.Timeout = c.Timeout - s.Timezone = c.Timezone - s.Umask = c.Umask - s.PidFile = c.PidFile - s.Volatile = c.Rm - - // Initcontainers - s.InitContainerType = c.InitContainerType - return nil -} - -func makeHealthCheckFromCli(inCmd, interval string, retries uint, timeout, startPeriod string) (*manifest.Schema2HealthConfig, error) { - cmdArr := []string{} - isArr := true - err := json.Unmarshal([]byte(inCmd), &cmdArr) // array unmarshalling - if err != nil { - cmdArr = strings.SplitN(inCmd, " ", 2) // default for compat - isArr = false - } - // Every healthcheck requires a command - if len(cmdArr) == 0 { - return nil, errors.New("Must define a healthcheck command for all healthchecks") - } - concat := "" - if cmdArr[0] == "CMD" || cmdArr[0] == "none" { // this is for compat, we are already split properly for most compat cases - cmdArr = strings.Fields(inCmd) - } else if cmdArr[0] != "CMD-SHELL" { // this is for podman side of things, won't contain the keywords - if isArr && len(cmdArr) > 1 { // an array of consecutive commands - cmdArr = append([]string{"CMD"}, cmdArr...) - } else { // one singular command - if len(cmdArr) == 1 { - concat = cmdArr[0] - } else { - concat = strings.Join(cmdArr[0:], " ") - } - cmdArr = append([]string{"CMD-SHELL"}, concat) - } - } - - if cmdArr[0] == "none" { // if specified to remove healtcheck - cmdArr = []string{"NONE"} - } - - // healthcheck is by default an array, so we simply pass the user input - hc := manifest.Schema2HealthConfig{ - Test: cmdArr, - } - - if interval == "disable" { - interval = "0" - } - intervalDuration, err := time.ParseDuration(interval) - if err != nil { - return nil, errors.Wrapf(err, "invalid healthcheck-interval") - } - - hc.Interval = intervalDuration - - if retries < 1 { - return nil, errors.New("healthcheck-retries must be greater than 0") - } - hc.Retries = int(retries) - timeoutDuration, err := time.ParseDuration(timeout) - if err != nil { - return nil, errors.Wrapf(err, "invalid healthcheck-timeout") - } - if timeoutDuration < time.Duration(1) { - return nil, errors.New("healthcheck-timeout must be at least 1 second") - } - hc.Timeout = timeoutDuration - - startPeriodDuration, err := time.ParseDuration(startPeriod) - if err != nil { - return nil, errors.Wrapf(err, "invalid healthcheck-start-period") - } - if startPeriodDuration < time.Duration(0) { - return nil, errors.New("healthcheck-start-period must be 0 seconds or greater") - } - hc.StartPeriod = startPeriodDuration - - return &hc, nil -} - -func parseWeightDevices(s *specgen.SpecGenerator, weightDevs []string) error { - for _, val := range weightDevs { - split := strings.SplitN(val, ":", 2) - if len(split) != 2 { - return fmt.Errorf("bad format: %s", val) - } - if !strings.HasPrefix(split[0], "/dev/") { - return fmt.Errorf("bad format for device path: %s", val) - } - weight, err := strconv.ParseUint(split[1], 10, 0) - if err != nil { - return fmt.Errorf("invalid weight for device: %s", val) - } - if weight > 0 && (weight < 10 || weight > 1000) { - return fmt.Errorf("invalid weight for device: %s", val) - } - w := uint16(weight) - s.WeightDevice[split[0]] = specs.LinuxWeightDevice{ - Weight: &w, - LeafWeight: nil, - } - } - return nil -} - -func parseThrottleBPSDevices(bpsDevices []string) (map[string]specs.LinuxThrottleDevice, error) { - td := make(map[string]specs.LinuxThrottleDevice) - for _, val := range bpsDevices { - split := strings.SplitN(val, ":", 2) - if len(split) != 2 { - return nil, fmt.Errorf("bad format: %s", val) - } - if !strings.HasPrefix(split[0], "/dev/") { - return nil, fmt.Errorf("bad format for device path: %s", val) - } - rate, err := units.RAMInBytes(split[1]) - if err != nil { - return nil, fmt.Errorf("invalid rate for device: %s. The correct format is <device-path>:<number>[<unit>]. Number must be a positive integer. Unit is optional and can be kb, mb, or gb", val) - } - if rate < 0 { - return nil, fmt.Errorf("invalid rate for device: %s. The correct format is <device-path>:<number>[<unit>]. Number must be a positive integer. Unit is optional and can be kb, mb, or gb", val) - } - td[split[0]] = specs.LinuxThrottleDevice{Rate: uint64(rate)} - } - return td, nil -} - -func parseThrottleIOPsDevices(iopsDevices []string) (map[string]specs.LinuxThrottleDevice, error) { - td := make(map[string]specs.LinuxThrottleDevice) - for _, val := range iopsDevices { - split := strings.SplitN(val, ":", 2) - if len(split) != 2 { - return nil, fmt.Errorf("bad format: %s", val) - } - if !strings.HasPrefix(split[0], "/dev/") { - return nil, fmt.Errorf("bad format for device path: %s", val) - } - rate, err := strconv.ParseUint(split[1], 10, 64) - if err != nil { - return nil, fmt.Errorf("invalid rate for device: %s. The correct format is <device-path>:<number>. Number must be a positive integer", val) - } - td[split[0]] = specs.LinuxThrottleDevice{Rate: rate} - } - return td, nil -} - -func parseSecrets(secrets []string) ([]specgen.Secret, map[string]string, error) { - secretParseError := errors.New("error parsing secret") - var mount []specgen.Secret - envs := make(map[string]string) - for _, val := range secrets { - // mount only tells if user has set an option that can only be used with mount secret type - mountOnly := false - source := "" - secretType := "" - target := "" - var uid, gid uint32 - // default mode 444 octal = 292 decimal - var mode uint32 = 292 - split := strings.Split(val, ",") - - // --secret mysecret - if len(split) == 1 { - mountSecret := specgen.Secret{ - Source: val, - UID: uid, - GID: gid, - Mode: mode, - } - mount = append(mount, mountSecret) - continue - } - // --secret mysecret,opt=opt - if !strings.Contains(split[0], "=") { - source = split[0] - split = split[1:] - } - - for _, val := range split { - kv := strings.SplitN(val, "=", 2) - if len(kv) < 2 { - return nil, nil, errors.Wrapf(secretParseError, "option %s must be in form option=value", val) - } - switch kv[0] { - case "source": - source = kv[1] - case "type": - if secretType != "" { - return nil, nil, errors.Wrap(secretParseError, "cannot set more tha one secret type") - } - if kv[1] != "mount" && kv[1] != "env" { - return nil, nil, errors.Wrapf(secretParseError, "type %s is invalid", kv[1]) - } - secretType = kv[1] - case "target": - target = kv[1] - case "mode": - mountOnly = true - mode64, err := strconv.ParseUint(kv[1], 8, 32) - if err != nil { - return nil, nil, errors.Wrapf(secretParseError, "mode %s invalid", kv[1]) - } - mode = uint32(mode64) - case "uid", "UID": - mountOnly = true - uid64, err := strconv.ParseUint(kv[1], 10, 32) - if err != nil { - return nil, nil, errors.Wrapf(secretParseError, "UID %s invalid", kv[1]) - } - uid = uint32(uid64) - case "gid", "GID": - mountOnly = true - gid64, err := strconv.ParseUint(kv[1], 10, 32) - if err != nil { - return nil, nil, errors.Wrapf(secretParseError, "GID %s invalid", kv[1]) - } - gid = uint32(gid64) - - default: - return nil, nil, errors.Wrapf(secretParseError, "option %s invalid", val) - } - } - - if secretType == "" { - secretType = "mount" - } - if source == "" { - return nil, nil, errors.Wrapf(secretParseError, "no source found %s", val) - } - if secretType == "mount" { - if target != "" { - return nil, nil, errors.Wrapf(secretParseError, "target option is invalid for mounted secrets") - } - mountSecret := specgen.Secret{ - Source: source, - UID: uid, - GID: gid, - Mode: mode, - } - mount = append(mount, mountSecret) - } - if secretType == "env" { - if mountOnly { - return nil, nil, errors.Wrap(secretParseError, "UID, GID, Mode options cannot be set with secret type env") - } - if target == "" { - target = source - } - envs[target] = source - } - } - return mount, envs, nil -} - -var cgroupDeviceType = map[string]bool{ - "a": true, // all - "b": true, // block device - "c": true, // character device -} - -var cgroupDeviceAccess = map[string]bool{ - "r": true, //read - "w": true, //write - "m": true, //mknod -} - -// parseLinuxResourcesDeviceAccess parses the raw string passed with the --device-access-add flag -func parseLinuxResourcesDeviceAccess(device string) (specs.LinuxDeviceCgroup, error) { - var devType, access string - var major, minor *int64 - - value := strings.Split(device, " ") - if len(value) != 3 { - return specs.LinuxDeviceCgroup{}, fmt.Errorf("invalid device cgroup rule requires type, major:Minor, and access rules: %q", device) - } - - devType = value[0] - if !cgroupDeviceType[devType] { - return specs.LinuxDeviceCgroup{}, fmt.Errorf("invalid device type in device-access-add: %s", devType) - } - - number := strings.SplitN(value[1], ":", 2) - i, err := strconv.ParseInt(number[0], 10, 64) - if err != nil { - return specs.LinuxDeviceCgroup{}, err - } - major = &i - if len(number) == 2 && number[1] != "*" { - i, err := strconv.ParseInt(number[1], 10, 64) - if err != nil { - return specs.LinuxDeviceCgroup{}, err - } - minor = &i - } - access = value[2] - for _, c := range strings.Split(access, "") { - if !cgroupDeviceAccess[c] { - return specs.LinuxDeviceCgroup{}, fmt.Errorf("invalid device access in device-access-add: %s", c) - } - } - return specs.LinuxDeviceCgroup{ - Allow: true, - Type: devType, - Major: major, - Minor: minor, - Access: access, - }, nil -} diff --git a/cmd/podman/common/util.go b/cmd/podman/common/util.go deleted file mode 100644 index cdfff9d6f..000000000 --- a/cmd/podman/common/util.go +++ /dev/null @@ -1,274 +0,0 @@ -package common - -import ( - "io/ioutil" - "net" - "strconv" - "strings" - - "github.com/containers/podman/v3/libpod/network/types" - "github.com/pkg/errors" - "github.com/sirupsen/logrus" -) - -// ReadPodIDFile reads the specified file and returns its content (i.e., first -// line). -func ReadPodIDFile(path string) (string, error) { - content, err := ioutil.ReadFile(path) - if err != nil { - return "", errors.Wrap(err, "error reading pod ID file") - } - return strings.Split(string(content), "\n")[0], nil -} - -// ReadPodIDFiles reads the specified files and returns their content (i.e., -// first line). -func ReadPodIDFiles(files []string) ([]string, error) { - ids := []string{} - for _, file := range files { - id, err := ReadPodIDFile(file) - if err != nil { - return nil, err - } - ids = append(ids, id) - } - return ids, nil -} - -// ParseFilters transforms one filter format to another and validates input -func ParseFilters(filter []string) (map[string][]string, error) { - // TODO Remove once filter refactor is finished and url.Values done. - filters := map[string][]string{} - for _, f := range filter { - t := strings.SplitN(f, "=", 2) - filters = make(map[string][]string) - if len(t) < 2 { - return map[string][]string{}, errors.Errorf("filter input must be in the form of filter=value: %s is invalid", f) - } - filters[t[0]] = append(filters[t[0]], t[1]) - } - return filters, nil -} - -// createExpose parses user-provided exposed port definitions and converts them -// into SpecGen format. -// TODO: The SpecGen format should really handle ranges more sanely - we could -// be massively inflating what is sent over the wire with a large range. -func createExpose(expose []string) (map[uint16]string, error) { - toReturn := make(map[uint16]string) - - for _, e := range expose { - // Check for protocol - proto := "tcp" - splitProto := strings.Split(e, "/") - if len(splitProto) > 2 { - return nil, errors.Errorf("invalid expose format - protocol can only be specified once") - } else if len(splitProto) == 2 { - proto = splitProto[1] - } - - // Check for a range - start, len, err := parseAndValidateRange(splitProto[0]) - if err != nil { - return nil, err - } - - var index uint16 - for index = 0; index < len; index++ { - portNum := start + index - protocols, ok := toReturn[portNum] - if !ok { - toReturn[portNum] = proto - } else { - newProto := strings.Join(append(strings.Split(protocols, ","), strings.Split(proto, ",")...), ",") - toReturn[portNum] = newProto - } - } - } - - return toReturn, nil -} - -// CreatePortBindings iterates ports mappings into SpecGen format. -func CreatePortBindings(ports []string) ([]types.PortMapping, error) { - // --publish is formatted as follows: - // [[hostip:]hostport[-endPort]:]containerport[-endPort][/protocol] - toReturn := make([]types.PortMapping, 0, len(ports)) - - for _, p := range ports { - var ( - ctrPort string - proto, hostIP, hostPort *string - ) - - splitProto := strings.Split(p, "/") - switch len(splitProto) { - case 1: - // No protocol was provided - case 2: - proto = &(splitProto[1]) - default: - return nil, errors.Errorf("invalid port format - protocol can only be specified once") - } - - remainder := splitProto[0] - haveV6 := false - - // Check for an IPv6 address in brackets - splitV6 := strings.Split(remainder, "]") - switch len(splitV6) { - case 1: - // Do nothing, proceed as before - case 2: - // We potentially have an IPv6 address - haveV6 = true - if !strings.HasPrefix(splitV6[0], "[") { - return nil, errors.Errorf("invalid port format - IPv6 addresses must be enclosed by []") - } - if !strings.HasPrefix(splitV6[1], ":") { - return nil, errors.Errorf("invalid port format - IPv6 address must be followed by a colon (':')") - } - ipNoPrefix := strings.TrimPrefix(splitV6[0], "[") - hostIP = &ipNoPrefix - remainder = strings.TrimPrefix(splitV6[1], ":") - default: - return nil, errors.Errorf("invalid port format - at most one IPv6 address can be specified in a --publish") - } - - splitPort := strings.Split(remainder, ":") - switch len(splitPort) { - case 1: - if haveV6 { - return nil, errors.Errorf("invalid port format - must provide host and destination port if specifying an IP") - } - ctrPort = splitPort[0] - case 2: - hostPort = &(splitPort[0]) - ctrPort = splitPort[1] - case 3: - if haveV6 { - return nil, errors.Errorf("invalid port format - when v6 address specified, must be [ipv6]:hostPort:ctrPort") - } - hostIP = &(splitPort[0]) - hostPort = &(splitPort[1]) - ctrPort = splitPort[2] - default: - return nil, errors.Errorf("invalid port format - format is [[hostIP:]hostPort:]containerPort") - } - - newPort, err := parseSplitPort(hostIP, hostPort, ctrPort, proto) - if err != nil { - return nil, err - } - - toReturn = append(toReturn, newPort) - } - - return toReturn, nil -} - -// parseSplitPort parses individual components of the --publish flag to produce -// a single port mapping in SpecGen format. -func parseSplitPort(hostIP, hostPort *string, ctrPort string, protocol *string) (types.PortMapping, error) { - newPort := types.PortMapping{} - if ctrPort == "" { - return newPort, errors.Errorf("must provide a non-empty container port to publish") - } - ctrStart, ctrLen, err := parseAndValidateRange(ctrPort) - if err != nil { - return newPort, errors.Wrapf(err, "error parsing container port") - } - newPort.ContainerPort = ctrStart - newPort.Range = ctrLen - - if protocol != nil { - if *protocol == "" { - return newPort, errors.Errorf("must provide a non-empty protocol to publish") - } - newPort.Protocol = *protocol - } - if hostIP != nil { - if *hostIP == "" { - return newPort, errors.Errorf("must provide a non-empty container host IP to publish") - } else if *hostIP != "0.0.0.0" { - // If hostIP is 0.0.0.0, leave it unset - CNI treats - // 0.0.0.0 and empty differently, Docker does not. - testIP := net.ParseIP(*hostIP) - if testIP == nil { - return newPort, errors.Errorf("cannot parse %q as an IP address", *hostIP) - } - newPort.HostIP = testIP.String() - } - } - if hostPort != nil { - if *hostPort == "" { - // Set 0 as a placeholder. The server side of Specgen - // will find a random, open, unused port to use. - newPort.HostPort = 0 - } else { - hostStart, hostLen, err := parseAndValidateRange(*hostPort) - if err != nil { - return newPort, errors.Wrapf(err, "error parsing host port") - } - if hostLen != ctrLen { - return newPort, errors.Errorf("host and container port ranges have different lengths: %d vs %d", hostLen, ctrLen) - } - newPort.HostPort = hostStart - } - } - - hport := newPort.HostPort - logrus.Debugf("Adding port mapping from %d to %d length %d protocol %q", hport, newPort.ContainerPort, newPort.Range, newPort.Protocol) - - return newPort, nil -} - -// Parse and validate a port range. -// Returns start port, length of range, error. -func parseAndValidateRange(portRange string) (uint16, uint16, error) { - splitRange := strings.Split(portRange, "-") - if len(splitRange) > 2 { - return 0, 0, errors.Errorf("invalid port format - port ranges are formatted as startPort-stopPort") - } - - if splitRange[0] == "" { - return 0, 0, errors.Errorf("port numbers cannot be negative") - } - - startPort, err := parseAndValidatePort(splitRange[0]) - if err != nil { - return 0, 0, err - } - - var rangeLen uint16 = 1 - if len(splitRange) == 2 { - if splitRange[1] == "" { - return 0, 0, errors.Errorf("must provide ending number for port range") - } - endPort, err := parseAndValidatePort(splitRange[1]) - if err != nil { - return 0, 0, err - } - if endPort <= startPort { - return 0, 0, errors.Errorf("the end port of a range must be higher than the start port - %d is not higher than %d", endPort, startPort) - } - // Our range is the total number of ports - // involved, so we need to add 1 (8080:8081 is - // 2 ports, for example, not 1) - rangeLen = endPort - startPort + 1 - } - - return startPort, rangeLen, nil -} - -// Turn a single string into a valid U16 port. -func parseAndValidatePort(port string) (uint16, error) { - num, err := strconv.Atoi(port) - if err != nil { - return 0, errors.Wrapf(err, "invalid port number") - } - if num < 1 || num > 65535 { - return 0, errors.Errorf("port numbers must be between 1 and 65535 (inclusive), got %d", num) - } - return uint16(num), nil -} diff --git a/cmd/podman/common/volumes.go b/cmd/podman/common/volumes.go deleted file mode 100644 index 883d604da..000000000 --- a/cmd/podman/common/volumes.go +++ /dev/null @@ -1,630 +0,0 @@ -package common - -import ( - "fmt" - "path/filepath" - "strings" - - "github.com/containers/common/pkg/parse" - "github.com/containers/podman/v3/libpod/define" - "github.com/containers/podman/v3/pkg/specgen" - "github.com/containers/podman/v3/pkg/util" - spec "github.com/opencontainers/runtime-spec/specs-go" - "github.com/pkg/errors" -) - -var ( - errDuplicateDest = errors.Errorf("duplicate mount destination") - optionArgError = errors.Errorf("must provide an argument for option") - noDestError = errors.Errorf("must set volume destination") - errInvalidSyntax = errors.Errorf("incorrect mount format: should be --mount type=<bind|tmpfs|volume>,[src=<host-dir|volume-name>,]target=<ctr-dir>[,options]") -) - -// Parse all volume-related options in the create config into a set of mounts -// and named volumes to add to the container. -// Handles --volumes, --mount, and --tmpfs flags. -// Does not handle image volumes, init, and --volumes-from flags. -// Can also add tmpfs mounts from read-only tmpfs. -// TODO: handle options parsing/processing via containers/storage/pkg/mount -func parseVolumes(volumeFlag, mountFlag, tmpfsFlag []string, addReadOnlyTmpfs bool) ([]spec.Mount, []*specgen.NamedVolume, []*specgen.OverlayVolume, []*specgen.ImageVolume, error) { - // Get mounts from the --mounts flag. - unifiedMounts, unifiedVolumes, unifiedImageVolumes, err := getMounts(mountFlag) - if err != nil { - return nil, nil, nil, nil, err - } - - // Next --volumes flag. - volumeMounts, volumeVolumes, overlayVolumes, err := specgen.GenVolumeMounts(volumeFlag) - if err != nil { - return nil, nil, nil, nil, err - } - - // Next --tmpfs flag. - tmpfsMounts, err := getTmpfsMounts(tmpfsFlag) - if err != nil { - return nil, nil, nil, nil, err - } - - // Unify mounts from --mount, --volume, --tmpfs. - // Start with --volume. - for dest, mount := range volumeMounts { - if _, ok := unifiedMounts[dest]; ok { - return nil, nil, nil, nil, errors.Wrapf(errDuplicateDest, dest) - } - unifiedMounts[dest] = mount - } - for dest, volume := range volumeVolumes { - if _, ok := unifiedVolumes[dest]; ok { - return nil, nil, nil, nil, errors.Wrapf(errDuplicateDest, dest) - } - unifiedVolumes[dest] = volume - } - // Now --tmpfs - for dest, tmpfs := range tmpfsMounts { - if _, ok := unifiedMounts[dest]; ok { - return nil, nil, nil, nil, errors.Wrapf(errDuplicateDest, dest) - } - unifiedMounts[dest] = tmpfs - } - - // If requested, add tmpfs filesystems for read-only containers. - if addReadOnlyTmpfs { - readonlyTmpfs := []string{"/tmp", "/var/tmp", "/run"} - options := []string{"rw", "rprivate", "nosuid", "nodev", "tmpcopyup"} - for _, dest := range readonlyTmpfs { - if _, ok := unifiedMounts[dest]; ok { - continue - } - if _, ok := unifiedVolumes[dest]; ok { - continue - } - unifiedMounts[dest] = spec.Mount{ - Destination: dest, - Type: define.TypeTmpfs, - Source: "tmpfs", - Options: options, - } - } - } - - // Check for conflicts between named volumes, overlay & image volumes, - // and mounts - allMounts := make(map[string]bool) - testAndSet := func(dest string) error { - if _, ok := allMounts[dest]; ok { - return errors.Wrapf(errDuplicateDest, "conflict at mount destination %v", dest) - } - allMounts[dest] = true - return nil - } - for dest := range unifiedMounts { - if err := testAndSet(dest); err != nil { - return nil, nil, nil, nil, err - } - } - for dest := range unifiedVolumes { - if err := testAndSet(dest); err != nil { - return nil, nil, nil, nil, err - } - } - for dest := range overlayVolumes { - if err := testAndSet(dest); err != nil { - return nil, nil, nil, nil, err - } - } - for dest := range unifiedImageVolumes { - if err := testAndSet(dest); err != nil { - return nil, nil, nil, nil, err - } - } - - // Final step: maps to arrays - finalMounts := make([]spec.Mount, 0, len(unifiedMounts)) - for _, mount := range unifiedMounts { - if mount.Type == define.TypeBind { - absSrc, err := filepath.Abs(mount.Source) - if err != nil { - return nil, nil, nil, nil, errors.Wrapf(err, "error getting absolute path of %s", mount.Source) - } - mount.Source = absSrc - } - finalMounts = append(finalMounts, mount) - } - finalVolumes := make([]*specgen.NamedVolume, 0, len(unifiedVolumes)) - for _, volume := range unifiedVolumes { - finalVolumes = append(finalVolumes, volume) - } - finalOverlayVolume := make([]*specgen.OverlayVolume, 0) - for _, volume := range overlayVolumes { - finalOverlayVolume = append(finalOverlayVolume, volume) - } - finalImageVolumes := make([]*specgen.ImageVolume, 0, len(unifiedImageVolumes)) - for _, volume := range unifiedImageVolumes { - finalImageVolumes = append(finalImageVolumes, volume) - } - - return finalMounts, finalVolumes, finalOverlayVolume, finalImageVolumes, nil -} - -// findMountType parses the input and extracts the type of the mount type and -// the remaining non-type tokens. -func findMountType(input string) (mountType string, tokens []string, err error) { - // Split by comma, iterate over the slice and look for - // "type=$mountType". Everything else is appended to tokens. - found := false - for _, s := range strings.Split(input, ",") { - kv := strings.Split(s, "=") - if found || !(len(kv) == 2 && kv[0] == "type") { - tokens = append(tokens, s) - continue - } - mountType = kv[1] - found = true - } - if !found { - err = errInvalidSyntax - } - return -} - -// getMounts takes user-provided input from the --mount flag and creates OCI -// spec mounts and Libpod named volumes. -// podman run --mount type=bind,src=/etc/resolv.conf,target=/etc/resolv.conf ... -// podman run --mount type=tmpfs,target=/dev/shm ... -// podman run --mount type=volume,source=test-volume, ... -func getMounts(mountFlag []string) (map[string]spec.Mount, map[string]*specgen.NamedVolume, map[string]*specgen.ImageVolume, error) { - finalMounts := make(map[string]spec.Mount) - finalNamedVolumes := make(map[string]*specgen.NamedVolume) - finalImageVolumes := make(map[string]*specgen.ImageVolume) - - for _, mount := range mountFlag { - // TODO: Docker defaults to "volume" if no mount type is specified. - mountType, tokens, err := findMountType(mount) - if err != nil { - return nil, nil, nil, err - } - switch mountType { - case define.TypeBind: - mount, err := getBindMount(tokens) - if err != nil { - return nil, nil, nil, err - } - if _, ok := finalMounts[mount.Destination]; ok { - return nil, nil, nil, errors.Wrapf(errDuplicateDest, mount.Destination) - } - finalMounts[mount.Destination] = mount - case define.TypeTmpfs: - mount, err := getTmpfsMount(tokens) - if err != nil { - return nil, nil, nil, err - } - if _, ok := finalMounts[mount.Destination]; ok { - return nil, nil, nil, errors.Wrapf(errDuplicateDest, mount.Destination) - } - finalMounts[mount.Destination] = mount - case define.TypeDevpts: - mount, err := getDevptsMount(tokens) - if err != nil { - return nil, nil, nil, err - } - if _, ok := finalMounts[mount.Destination]; ok { - return nil, nil, nil, errors.Wrapf(errDuplicateDest, mount.Destination) - } - finalMounts[mount.Destination] = mount - case "image": - volume, err := getImageVolume(tokens) - if err != nil { - return nil, nil, nil, err - } - if _, ok := finalImageVolumes[volume.Destination]; ok { - return nil, nil, nil, errors.Wrapf(errDuplicateDest, volume.Destination) - } - finalImageVolumes[volume.Destination] = volume - case "volume": - volume, err := getNamedVolume(tokens) - if err != nil { - return nil, nil, nil, err - } - if _, ok := finalNamedVolumes[volume.Dest]; ok { - return nil, nil, nil, errors.Wrapf(errDuplicateDest, volume.Dest) - } - finalNamedVolumes[volume.Dest] = volume - default: - return nil, nil, nil, errors.Errorf("invalid filesystem type %q", mountType) - } - } - - return finalMounts, finalNamedVolumes, finalImageVolumes, nil -} - -// Parse a single bind mount entry from the --mount flag. -func getBindMount(args []string) (spec.Mount, error) { - newMount := spec.Mount{ - Type: define.TypeBind, - } - - var setSource, setDest, setRORW, setSuid, setDev, setExec, setRelabel bool - - for _, val := range args { - kv := strings.SplitN(val, "=", 2) - switch kv[0] { - case "bind-nonrecursive": - newMount.Options = append(newMount.Options, "bind") - case "readonly", "ro", "rw": - if setRORW { - return newMount, errors.Wrapf(optionArgError, "cannot pass 'readonly', 'ro', or 'rw' options more than once") - } - setRORW = true - // Can be formatted as one of: - // readonly - // readonly=[true|false] - // ro - // ro=[true|false] - // rw - // rw=[true|false] - if kv[0] == "readonly" { - kv[0] = "ro" - } - switch len(kv) { - case 1: - newMount.Options = append(newMount.Options, kv[0]) - case 2: - switch strings.ToLower(kv[1]) { - case "true": - newMount.Options = append(newMount.Options, kv[0]) - case "false": - // Set the opposite only for rw - // ro's opposite is the default - if kv[0] == "rw" { - newMount.Options = append(newMount.Options, "ro") - } - default: - return newMount, errors.Wrapf(optionArgError, "'readonly', 'ro', or 'rw' must be set to true or false, instead received %q", kv[1]) - } - default: - return newMount, errors.Wrapf(optionArgError, "badly formatted option %q", val) - } - case "nosuid", "suid": - if setSuid { - return newMount, errors.Wrapf(optionArgError, "cannot pass 'nosuid' and 'suid' options more than once") - } - setSuid = true - newMount.Options = append(newMount.Options, kv[0]) - case "nodev", "dev": - if setDev { - return newMount, errors.Wrapf(optionArgError, "cannot pass 'nodev' and 'dev' options more than once") - } - setDev = true - newMount.Options = append(newMount.Options, kv[0]) - case "noexec", "exec": - if setExec { - return newMount, errors.Wrapf(optionArgError, "cannot pass 'noexec' and 'exec' options more than once") - } - setExec = true - newMount.Options = append(newMount.Options, kv[0]) - case "shared", "rshared", "private", "rprivate", "slave", "rslave", "unbindable", "runbindable", "Z", "z": - newMount.Options = append(newMount.Options, kv[0]) - case "bind-propagation": - if len(kv) == 1 { - return newMount, errors.Wrapf(optionArgError, kv[0]) - } - newMount.Options = append(newMount.Options, kv[1]) - case "src", "source": - if len(kv) == 1 { - return newMount, errors.Wrapf(optionArgError, kv[0]) - } - if len(kv[1]) == 0 { - return newMount, errors.Wrapf(optionArgError, "host directory cannot be empty") - } - newMount.Source = kv[1] - setSource = true - case "target", "dst", "destination": - if len(kv) == 1 { - return newMount, errors.Wrapf(optionArgError, kv[0]) - } - if err := parse.ValidateVolumeCtrDir(kv[1]); err != nil { - return newMount, err - } - newMount.Destination = filepath.Clean(kv[1]) - setDest = true - case "relabel": - if setRelabel { - return newMount, errors.Wrapf(optionArgError, "cannot pass 'relabel' option more than once") - } - setRelabel = true - if len(kv) != 2 { - return newMount, errors.Wrapf(util.ErrBadMntOption, "%s mount option must be 'private' or 'shared'", kv[0]) - } - switch kv[1] { - case "private": - newMount.Options = append(newMount.Options, "Z") - case "shared": - newMount.Options = append(newMount.Options, "z") - default: - return newMount, errors.Wrapf(util.ErrBadMntOption, "%s mount option must be 'private' or 'shared'", kv[0]) - } - case "consistency": - // Often used on MACs and mistakenly on Linux platforms. - // Since Docker ignores this option so shall we. - continue - default: - return newMount, errors.Wrapf(util.ErrBadMntOption, kv[0]) - } - } - - if !setDest { - return newMount, noDestError - } - - if !setSource { - newMount.Source = newMount.Destination - } - - options, err := parse.ValidateVolumeOpts(newMount.Options) - if err != nil { - return newMount, err - } - newMount.Options = options - return newMount, nil -} - -// Parse a single tmpfs mount entry from the --mount flag -func getTmpfsMount(args []string) (spec.Mount, error) { - newMount := spec.Mount{ - Type: define.TypeTmpfs, - Source: define.TypeTmpfs, - } - - var setDest, setRORW, setSuid, setDev, setExec, setTmpcopyup bool - - for _, val := range args { - kv := strings.SplitN(val, "=", 2) - switch kv[0] { - case "tmpcopyup", "notmpcopyup": - if setTmpcopyup { - return newMount, errors.Wrapf(optionArgError, "cannot pass 'tmpcopyup' and 'notmpcopyup' options more than once") - } - setTmpcopyup = true - newMount.Options = append(newMount.Options, kv[0]) - case "ro", "rw": - if setRORW { - return newMount, errors.Wrapf(optionArgError, "cannot pass 'ro' and 'rw' options more than once") - } - setRORW = true - newMount.Options = append(newMount.Options, kv[0]) - case "nosuid", "suid": - if setSuid { - return newMount, errors.Wrapf(optionArgError, "cannot pass 'nosuid' and 'suid' options more than once") - } - setSuid = true - newMount.Options = append(newMount.Options, kv[0]) - case "nodev", "dev": - if setDev { - return newMount, errors.Wrapf(optionArgError, "cannot pass 'nodev' and 'dev' options more than once") - } - setDev = true - newMount.Options = append(newMount.Options, kv[0]) - case "noexec", "exec": - if setExec { - return newMount, errors.Wrapf(optionArgError, "cannot pass 'noexec' and 'exec' options more than once") - } - setExec = true - newMount.Options = append(newMount.Options, kv[0]) - case "tmpfs-mode": - if len(kv) == 1 { - return newMount, errors.Wrapf(optionArgError, kv[0]) - } - newMount.Options = append(newMount.Options, fmt.Sprintf("mode=%s", kv[1])) - case "tmpfs-size": - if len(kv) == 1 { - return newMount, errors.Wrapf(optionArgError, kv[0]) - } - newMount.Options = append(newMount.Options, fmt.Sprintf("size=%s", kv[1])) - case "src", "source": - return newMount, errors.Errorf("source is not supported with tmpfs mounts") - case "target", "dst", "destination": - if len(kv) == 1 { - return newMount, errors.Wrapf(optionArgError, kv[0]) - } - if err := parse.ValidateVolumeCtrDir(kv[1]); err != nil { - return newMount, err - } - newMount.Destination = filepath.Clean(kv[1]) - setDest = true - case "consistency": - // Often used on MACs and mistakenly on Linux platforms. - // Since Docker ignores this option so shall we. - continue - default: - return newMount, errors.Wrapf(util.ErrBadMntOption, kv[0]) - } - } - - if !setDest { - return newMount, noDestError - } - - return newMount, nil -} - -// Parse a single devpts mount entry from the --mount flag -func getDevptsMount(args []string) (spec.Mount, error) { - newMount := spec.Mount{ - Type: define.TypeDevpts, - Source: define.TypeDevpts, - } - - var setDest bool - - for _, val := range args { - kv := strings.SplitN(val, "=", 2) - switch kv[0] { - case "target", "dst", "destination": - if len(kv) == 1 { - return newMount, errors.Wrapf(optionArgError, kv[0]) - } - if err := parse.ValidateVolumeCtrDir(kv[1]); err != nil { - return newMount, err - } - newMount.Destination = filepath.Clean(kv[1]) - setDest = true - default: - return newMount, errors.Wrapf(util.ErrBadMntOption, kv[0]) - } - } - - if !setDest { - return newMount, noDestError - } - - return newMount, nil -} - -// Parse a single volume mount entry from the --mount flag. -// Note that the volume-label option for named volumes is currently NOT supported. -// TODO: add support for --volume-label -func getNamedVolume(args []string) (*specgen.NamedVolume, error) { - newVolume := new(specgen.NamedVolume) - - var setSource, setDest, setRORW, setSuid, setDev, setExec bool - - for _, val := range args { - kv := strings.SplitN(val, "=", 2) - switch kv[0] { - case "ro", "rw": - if setRORW { - return nil, errors.Wrapf(optionArgError, "cannot pass 'ro' and 'rw' options more than once") - } - setRORW = true - newVolume.Options = append(newVolume.Options, kv[0]) - case "nosuid", "suid": - if setSuid { - return nil, errors.Wrapf(optionArgError, "cannot pass 'nosuid' and 'suid' options more than once") - } - setSuid = true - newVolume.Options = append(newVolume.Options, kv[0]) - case "nodev", "dev": - if setDev { - return nil, errors.Wrapf(optionArgError, "cannot pass 'nodev' and 'dev' options more than once") - } - setDev = true - newVolume.Options = append(newVolume.Options, kv[0]) - case "noexec", "exec": - if setExec { - return nil, errors.Wrapf(optionArgError, "cannot pass 'noexec' and 'exec' options more than once") - } - setExec = true - newVolume.Options = append(newVolume.Options, kv[0]) - case "volume-label": - return nil, errors.Errorf("the --volume-label option is not presently implemented") - case "src", "source": - if len(kv) == 1 { - return nil, errors.Wrapf(optionArgError, kv[0]) - } - newVolume.Name = kv[1] - setSource = true - case "target", "dst", "destination": - if len(kv) == 1 { - return nil, errors.Wrapf(optionArgError, kv[0]) - } - if err := parse.ValidateVolumeCtrDir(kv[1]); err != nil { - return nil, err - } - newVolume.Dest = filepath.Clean(kv[1]) - setDest = true - case "consistency": - // Often used on MACs and mistakenly on Linux platforms. - // Since Docker ignores this option so shall we. - continue - default: - return nil, errors.Wrapf(util.ErrBadMntOption, kv[0]) - } - } - - if !setSource { - return nil, errors.Errorf("must set source volume") - } - if !setDest { - return nil, noDestError - } - - return newVolume, nil -} - -// Parse the arguments into an image volume. An image volume is a volume based -// on a container image. The container image is first mounted on the host and -// is then bind-mounted into the container. An ImageVolume is always mounted -// read only. -func getImageVolume(args []string) (*specgen.ImageVolume, error) { - newVolume := new(specgen.ImageVolume) - - for _, val := range args { - kv := strings.SplitN(val, "=", 2) - switch kv[0] { - case "src", "source": - if len(kv) == 1 { - return nil, errors.Wrapf(optionArgError, kv[0]) - } - newVolume.Source = kv[1] - case "target", "dst", "destination": - if len(kv) == 1 { - return nil, errors.Wrapf(optionArgError, kv[0]) - } - if err := parse.ValidateVolumeCtrDir(kv[1]); err != nil { - return nil, err - } - newVolume.Destination = filepath.Clean(kv[1]) - case "rw", "readwrite": - switch kv[1] { - case "true": - newVolume.ReadWrite = true - case "false": - // Nothing to do. RO is default. - default: - return nil, errors.Wrapf(util.ErrBadMntOption, "invalid rw value %q", kv[1]) - } - case "consistency": - // Often used on MACs and mistakenly on Linux platforms. - // Since Docker ignores this option so shall we. - continue - default: - return nil, errors.Wrapf(util.ErrBadMntOption, kv[0]) - } - } - - if len(newVolume.Source)*len(newVolume.Destination) == 0 { - return nil, errors.Errorf("must set source and destination for image volume") - } - - return newVolume, nil -} - -// GetTmpfsMounts creates spec.Mount structs for user-requested tmpfs mounts -func getTmpfsMounts(tmpfsFlag []string) (map[string]spec.Mount, error) { - m := make(map[string]spec.Mount) - for _, i := range tmpfsFlag { - // Default options if nothing passed - var options []string - spliti := strings.Split(i, ":") - destPath := spliti[0] - if err := parse.ValidateVolumeCtrDir(spliti[0]); err != nil { - return nil, err - } - if len(spliti) > 1 { - options = strings.Split(spliti[1], ",") - } - - if _, ok := m[destPath]; ok { - return nil, errors.Wrapf(errDuplicateDest, destPath) - } - - mount := spec.Mount{ - Destination: filepath.Clean(destPath), - Type: string(define.TypeTmpfs), - Options: options, - Source: string(define.TypeTmpfs), - } - m[destPath] = mount - } - return m, nil -} diff --git a/cmd/podman/containers/create.go b/cmd/podman/containers/create.go index 7583a024e..8b27de53e 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/logs.go b/cmd/podman/containers/logs.go index 00a8d4b52..1548c6c24 100644 --- a/cmd/podman/containers/logs.go +++ b/cmd/podman/containers/logs.go @@ -120,7 +120,7 @@ func logsFlags(cmd *cobra.Command) { func logs(_ *cobra.Command, args []string) error { if logsOptions.SinceRaw != "" { // parse time, error out if something is wrong - since, err := util.ParseInputTime(logsOptions.SinceRaw) + since, err := util.ParseInputTime(logsOptions.SinceRaw, true) if err != nil { return errors.Wrapf(err, "error parsing --since %q", logsOptions.SinceRaw) } @@ -128,7 +128,7 @@ func logs(_ *cobra.Command, args []string) error { } if logsOptions.UntilRaw != "" { // parse time, error out if something is wrong - until, err := util.ParseInputTime(logsOptions.UntilRaw) + until, err := util.ParseInputTime(logsOptions.UntilRaw, false) if err != nil { return errors.Wrapf(err, "error parsing --until %q", logsOptions.UntilRaw) } diff --git a/cmd/podman/containers/prune.go b/cmd/podman/containers/prune.go index e55bd8a53..e13b9e7f6 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 3b6f74efa..05214f32c 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 830d1de7f..d14961829 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/generate/systemd.go b/cmd/podman/generate/systemd.go index 5461f1f6a..2ab33c26b 100644 --- a/cmd/podman/generate/systemd.go +++ b/cmd/podman/generate/systemd.go @@ -12,15 +12,22 @@ import ( "github.com/containers/podman/v3/cmd/podman/registry" "github.com/containers/podman/v3/cmd/podman/utils" "github.com/containers/podman/v3/pkg/domain/entities" + systemDefine "github.com/containers/podman/v3/pkg/systemd/define" "github.com/pkg/errors" "github.com/sirupsen/logrus" "github.com/spf13/cobra" ) +const ( + restartPolicyFlagName = "restart-policy" + timeFlagName = "time" +) + var ( files bool format string systemdTimeout uint + systemdRestart string systemdOptions = entities.GenerateSystemdOptions{} systemdDescription = `Generate systemd units for a pod or container. The generated units can later be controlled via systemctl(1).` @@ -47,10 +54,9 @@ func init() { flags.BoolVarP(&systemdOptions.Name, "name", "n", false, "Use container/pod names instead of IDs") flags.BoolVarP(&files, "files", "f", false, "Generate .service files instead of printing to stdout") - timeFlagName := "time" flags.UintVarP(&systemdTimeout, timeFlagName, "t", containerConfig.Engine.StopTimeout, "Stop timeout override") _ = systemdCmd.RegisterFlagCompletionFunc(timeFlagName, completion.AutocompleteNone) - flags.BoolVarP(&systemdOptions.New, "new", "", false, "Create a new container instead of starting an existing one") + flags.BoolVarP(&systemdOptions.New, "new", "", false, "Create a new container or pod instead of starting an existing one") flags.BoolVarP(&systemdOptions.NoHeader, "no-header", "", false, "Skip header generation") containerPrefixFlagName := "container-prefix" @@ -65,8 +71,7 @@ func init() { flags.StringVar(&systemdOptions.Separator, separatorFlagName, "-", "Systemd unit name separator between name/id and prefix") _ = systemdCmd.RegisterFlagCompletionFunc(separatorFlagName, completion.AutocompleteNone) - restartPolicyFlagName := "restart-policy" - flags.StringVar(&systemdOptions.RestartPolicy, restartPolicyFlagName, "on-failure", "Systemd restart-policy") + flags.StringVar(&systemdRestart, restartPolicyFlagName, systemDefine.DefaultRestartPolicy, "Systemd restart-policy") _ = systemdCmd.RegisterFlagCompletionFunc(restartPolicyFlagName, common.AutocompleteSystemdRestartOptions) formatFlagName := "format" @@ -77,9 +82,12 @@ func init() { } func systemd(cmd *cobra.Command, args []string) error { - if cmd.Flags().Changed("time") { + if cmd.Flags().Changed(timeFlagName) { systemdOptions.StopTimeout = &systemdTimeout } + if cmd.Flags().Changed(restartPolicyFlagName) { + systemdOptions.RestartPolicy = &systemdRestart + } if registry.IsRemote() { logrus.Warnln("The generated units should be placed on your remote system") diff --git a/cmd/podman/images/import.go b/cmd/podman/images/import.go index bc80417cc..d4bc0f610 100644 --- a/cmd/podman/images/import.go +++ b/cmd/podman/images/import.go @@ -30,7 +30,7 @@ var ( RunE: importCon, Args: cobra.RangeArgs(1, 2), ValidArgsFunction: common.AutocompleteDefaultOneArg, - Example: `podman import http://example.com/ctr.tar url-image + Example: `podman import https://example.com/ctr.tar url-image cat ctr.tar | podman -q import --message "importing the ctr.tar tarball" - image-imported cat ctr.tar | podman import -`, } @@ -42,7 +42,7 @@ var ( RunE: importCommand.RunE, Args: importCommand.Args, ValidArgsFunction: importCommand.ValidArgsFunction, - Example: `podman image import http://example.com/ctr.tar url-image + Example: `podman image import https://example.com/ctr.tar url-image cat ctr.tar | podman -q image import --message "importing the ctr.tar tarball" - image-imported cat ctr.tar | podman image import -`, } diff --git a/cmd/podman/images/prune.go b/cmd/podman/images/prune.go index 6ecf4f2aa..8a484495a 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/machine/init.go b/cmd/podman/machine/init.go index f4133dbde..ec44a707d 100644 --- a/cmd/podman/machine/init.go +++ b/cmd/podman/machine/init.go @@ -1,4 +1,4 @@ -// +build amd64,linux arm64,linux amd64,darwin arm64,darwin +// +build amd64,!windows arm64,!windows package machine @@ -34,12 +34,13 @@ func init() { Parent: machineCmd, }) flags := initCmd.Flags() + cfg := registry.PodmanConfig() cpusFlagName := "cpus" flags.Uint64Var( &initOpts.CPUS, cpusFlagName, 1, - "Number of CPUs. The default is 1.", + "Number of CPUs", ) _ = initCmd.RegisterFlagCompletionFunc(cpusFlagName, completion.AutocompleteNone) @@ -56,12 +57,12 @@ func init() { flags.Uint64VarP( &initOpts.Memory, memoryFlagName, "m", 2048, - "Memory (in MB)", + "Memory in MB", ) _ = initCmd.RegisterFlagCompletionFunc(memoryFlagName, completion.AutocompleteNone) ImagePathFlagName := "image-path" - flags.StringVar(&initOpts.ImagePath, ImagePathFlagName, "", "Path to qcow image") + flags.StringVar(&initOpts.ImagePath, ImagePathFlagName, cfg.Engine.MachineImage, "Path to qcow image") _ = initCmd.RegisterFlagCompletionFunc(ImagePathFlagName, completion.AutocompleteDefault) IgnitionPathFlagName := "ignition-path" diff --git a/cmd/podman/machine/list.go b/cmd/podman/machine/list.go index 134a081ab..d4360bb9b 100644 --- a/cmd/podman/machine/list.go +++ b/cmd/podman/machine/list.go @@ -1,4 +1,4 @@ -// +build amd64,linux arm64,linux amd64,darwin arm64,darwin +// +build amd64,!windows arm64,!windows package machine diff --git a/cmd/podman/machine/machine.go b/cmd/podman/machine/machine.go index b059afc38..8ff9055f0 100644 --- a/cmd/podman/machine/machine.go +++ b/cmd/podman/machine/machine.go @@ -1,4 +1,4 @@ -// +build amd64,linux arm64,linux amd64,darwin arm64,darwin +// +build amd64,!windows arm64,!windows package machine diff --git a/cmd/podman/machine/rm.go b/cmd/podman/machine/rm.go index 02e3dfeb8..c17399c78 100644 --- a/cmd/podman/machine/rm.go +++ b/cmd/podman/machine/rm.go @@ -1,4 +1,4 @@ -// +build amd64,linux arm64,linux amd64,darwin arm64,darwin +// +build amd64,!windows arm64,!windows package machine diff --git a/cmd/podman/machine/ssh.go b/cmd/podman/machine/ssh.go index b52a48faf..85101a641 100644 --- a/cmd/podman/machine/ssh.go +++ b/cmd/podman/machine/ssh.go @@ -1,4 +1,4 @@ -// +build amd64,linux arm64,linux amd64,darwin arm64,darwin +// +build amd64,!windows arm64,!windows package machine diff --git a/cmd/podman/machine/start.go b/cmd/podman/machine/start.go index f8f0eed09..a5ba74599 100644 --- a/cmd/podman/machine/start.go +++ b/cmd/podman/machine/start.go @@ -1,4 +1,4 @@ -// +build amd64,linux arm64,linux amd64,darwin arm64,darwin +// +build amd64,!windows arm64,!windows package machine diff --git a/cmd/podman/machine/stop.go b/cmd/podman/machine/stop.go index 2d5aa7b95..76ba85601 100644 --- a/cmd/podman/machine/stop.go +++ b/cmd/podman/machine/stop.go @@ -1,4 +1,4 @@ -// +build amd64,linux arm64,linux amd64,darwin arm64,darwin +// +build amd64,!windows arm64,!windows package machine diff --git a/cmd/podman/networks/create.go b/cmd/podman/networks/create.go index 1f3b321ba..b5ddd215f 100644 --- a/cmd/podman/networks/create.go +++ b/cmd/podman/networks/create.go @@ -11,6 +11,7 @@ import ( "github.com/containers/podman/v3/libpod/define" "github.com/containers/podman/v3/pkg/domain/entities" "github.com/pkg/errors" + "github.com/sirupsen/logrus" "github.com/spf13/cobra" ) @@ -56,7 +57,8 @@ func networkCreateFlags(cmd *cobra.Command) { macvlanFlagName := "macvlan" flags.StringVar(&networkCreateOptions.MacVLAN, macvlanFlagName, "", "create a Macvlan connection based on this device") - _ = cmd.RegisterFlagCompletionFunc(macvlanFlagName, completion.AutocompleteNone) + // This option is deprecated + flags.MarkHidden(macvlanFlagName) labelFlagName := "label" flags.StringArrayVar(&labels, labelFlagName, nil, "set metadata on a network") @@ -100,6 +102,11 @@ func networkCreate(cmd *cobra.Command, args []string) error { if err != nil { return errors.Wrapf(err, "unable to process options") } + + if networkCreateOptions.MacVLAN != "" { + logrus.Warn("The --macvlan option is deprecated, use `--driver macvlan --opt parent=<device>` instead") + } + response, err := registry.ContainerEngine().NetworkCreate(registry.Context(), name, networkCreateOptions) if err != nil { return err diff --git a/cmd/podman/networks/prune.go b/cmd/podman/networks/prune.go index e6b779ded..311d098cd 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/play/kube.go b/cmd/podman/play/kube.go index 2eebd9f86..9308371d2 100644 --- a/cmd/podman/play/kube.go +++ b/cmd/podman/play/kube.go @@ -86,6 +86,9 @@ func init() { flags.StringVar(&kubeOptions.Authfile, authfileFlagName, auth.GetDefaultAuthFile(), "Path of the authentication file. Use REGISTRY_AUTH_FILE environment variable to override") _ = kubeCmd.RegisterFlagCompletionFunc(authfileFlagName, completion.AutocompleteDefault) + downFlagName := "down" + flags.BoolVar(&kubeOptions.Down, downFlagName, false, "Stop pods defined in the YAML file") + if !registry.IsRemote() { certDirFlagName := "cert-dir" flags.StringVar(&kubeOptions.CertDir, certDirFlagName, "", "`Pathname` of a directory containing TLS certificates and keys") @@ -144,12 +147,55 @@ func kube(cmd *cobra.Command, args []string) error { } kubeOptions.StaticMACs = append(kubeOptions.StaticMACs, m) } + if kubeOptions.Down { + return teardown(yamlfile) + } + return playkube(yamlfile) +} - report, err := registry.ContainerEngine().PlayKube(registry.GetContext(), yamlfile, kubeOptions.PlayKubeOptions) +func teardown(yamlfile string) error { + var ( + podStopErrors utils.OutputErrors + podRmErrors utils.OutputErrors + ) + options := new(entities.PlayKubeDownOptions) + reports, err := registry.ContainerEngine().PlayKubeDown(registry.GetContext(), yamlfile, *options) if err != nil { return err } + // Output stopped pods + fmt.Println("Pods stopped:") + for _, stopped := range reports.StopReport { + if len(stopped.Errs) == 0 { + fmt.Println(stopped.Id) + } else { + podStopErrors = append(podStopErrors, stopped.Errs...) + } + } + // Dump any stop errors + lastStopError := podStopErrors.PrintErrors() + if lastStopError != nil { + fmt.Fprintf(os.Stderr, "Error: %s\n", lastStopError) + } + + // Output rm'd pods + fmt.Println("Pods removed:") + for _, removed := range reports.RmReport { + if removed.Err == nil { + fmt.Println(removed.Id) + } else { + podRmErrors = append(podRmErrors, removed.Err) + } + } + return podRmErrors.PrintErrors() +} + +func playkube(yamlfile string) error { + report, err := registry.ContainerEngine().PlayKube(registry.GetContext(), yamlfile, kubeOptions.PlayKubeOptions) + if err != nil { + return err + } // Print volumes report for i, volume := range report.Volumes { if i == 0 { diff --git a/cmd/podman/pods/create.go b/cmd/podman/pods/create.go index bf5b9e350..b3f84dcd8 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/logs.go b/cmd/podman/pods/logs.go new file mode 100644 index 000000000..fe5205669 --- /dev/null +++ b/cmd/podman/pods/logs.go @@ -0,0 +1,140 @@ +package pods + +import ( + "os" + + "github.com/containers/common/pkg/completion" + "github.com/containers/podman/v3/cmd/podman/common" + "github.com/containers/podman/v3/cmd/podman/registry" + "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/util" + "github.com/pkg/errors" + "github.com/spf13/cobra" +) + +// logsOptionsWrapper wraps entities.LogsOptions and prevents leaking +// CLI-only fields into the API types. +type logsOptionsWrapper struct { + entities.PodLogsOptions + + SinceRaw string + + UntilRaw string +} + +var ( + logsPodOptions logsOptionsWrapper + logsPodDescription = `Displays logs for pod with one or more containers.` + logsPodCommand = &cobra.Command{ + Use: "logs [options] POD", + Short: "Fetch logs for pod with one or more containers", + Long: logsPodDescription, + // We dont want users to invoke latest and pod togather + Args: func(cmd *cobra.Command, args []string) error { + switch { + case registry.IsRemote() && logsPodOptions.Latest: + return errors.New(cmd.Name() + " does not support 'latest' when run remotely") + case len(args) > 1: + return errors.New("requires exactly 1 arg") + case logsPodOptions.Latest && len(args) > 0: + return errors.New("--latest and pods cannot be used together") + case !logsPodOptions.Latest && len(args) < 1: + return errors.New("specify at least one pod name or ID to log") + } + return nil + }, + RunE: logs, + ValidArgsFunction: common.AutocompletePods, + Example: `podman pod logs podID + podman pod logs -c ctrname podName + podman pod logs --tail 2 mywebserver + podman pod logs --follow=true --since 10m podID + podman pod logs mywebserver`, + } + + containerLogsCommand = &cobra.Command{ + Use: logsPodCommand.Use, + Short: logsPodCommand.Short, + Long: logsPodCommand.Long, + Args: logsPodCommand.Args, + RunE: logsPodCommand.RunE, + ValidArgsFunction: logsPodCommand.ValidArgsFunction, + Example: `podman pod logs podId + podman pod logs -c ctrname podName + podman pod logs --tail 2 mywebserver + podman pod logs --follow=true --since 10m podID`, + } +) + +func init() { + registry.Commands = append(registry.Commands, registry.CliCommand{ + Command: logsPodCommand, + }) + logsFlags(logsPodCommand) + validate.AddLatestFlag(logsPodCommand, &logsPodOptions.Latest) + + // container logs + registry.Commands = append(registry.Commands, registry.CliCommand{ + Command: containerLogsCommand, + Parent: podCmd, + }) + logsFlags(containerLogsCommand) + validate.AddLatestFlag(containerLogsCommand, &logsPodOptions.Latest) +} + +func logsFlags(cmd *cobra.Command) { + flags := cmd.Flags() + + flags.BoolVar(&logsPodOptions.Details, "details", false, "Show extra details provided to the logs") + flags.BoolVarP(&logsPodOptions.Follow, "follow", "f", false, "Follow log output.") + + containerNameFlag := "container" + flags.StringVarP(&logsPodOptions.ContainerName, containerNameFlag, "c", "", "Filter logs by container name or id which belongs to pod") + _ = cmd.RegisterFlagCompletionFunc(containerNameFlag, common.AutocompleteContainers) + + sinceFlagName := "since" + flags.StringVar(&logsPodOptions.SinceRaw, sinceFlagName, "", "Show logs since TIMESTAMP") + _ = cmd.RegisterFlagCompletionFunc(sinceFlagName, completion.AutocompleteNone) + + untilFlagName := "until" + flags.StringVar(&logsPodOptions.UntilRaw, untilFlagName, "", "Show logs until TIMESTAMP") + _ = cmd.RegisterFlagCompletionFunc(untilFlagName, completion.AutocompleteNone) + + tailFlagName := "tail" + flags.Int64Var(&logsPodOptions.Tail, tailFlagName, -1, "Output the specified number of LINES at the end of the logs.") + _ = cmd.RegisterFlagCompletionFunc(tailFlagName, completion.AutocompleteNone) + + flags.BoolVarP(&logsPodOptions.Timestamps, "timestamps", "t", false, "Output the timestamps in the log") + flags.SetInterspersed(false) + _ = flags.MarkHidden("details") +} + +func logs(_ *cobra.Command, args []string) error { + if logsPodOptions.SinceRaw != "" { + // parse time, error out if something is wrong + since, err := util.ParseInputTime(logsPodOptions.SinceRaw, true) + if err != nil { + return errors.Wrapf(err, "error parsing --since %q", logsPodOptions.SinceRaw) + } + logsPodOptions.Since = since + } + if logsPodOptions.UntilRaw != "" { + // parse time, error out if something is wrong + until, err := util.ParseInputTime(logsPodOptions.UntilRaw, false) + if err != nil { + return errors.Wrapf(err, "error parsing --until %q", logsPodOptions.UntilRaw) + } + logsPodOptions.Until = until + } + + // Remote can only process one container at a time + if registry.IsRemote() && logsPodOptions.ContainerName == "" { + return errors.Wrapf(define.ErrInvalidArg, "-c or --container cannot be empty") + } + + logsPodOptions.StdoutWriter = os.Stdout + logsPodOptions.StderrWriter = os.Stderr + return registry.ContainerEngine().PodLogs(registry.GetContext(), args[0], logsPodOptions.PodLogsOptions) +} diff --git a/cmd/podman/pods/ps.go b/cmd/podman/pods/ps.go index 14e3e2ea9..60aadf224 100644 --- a/cmd/podman/pods/ps.go +++ b/cmd/podman/pods/ps.go @@ -57,7 +57,7 @@ func init() { formatFlagName := "format" flags.StringVar(&psInput.Format, formatFlagName, "", "Pretty-print pods to JSON or using a Go template") - _ = psCmd.RegisterFlagCompletionFunc(formatFlagName, common.AutocompleteFormat(ListPodReporter{})) + _ = psCmd.RegisterFlagCompletionFunc(formatFlagName, common.AutocompleteFormat(ListPodReporter{ListPodsReport: &entities.ListPodsReport{}})) flags.Bool("noheading", false, "Do not print headers") flags.BoolVar(&psInput.Namespace, "namespace", false, "Display namespace information of the pod") diff --git a/cmd/podman/pods/rm.go b/cmd/podman/pods/rm.go index fbaf64c1f..dc4c7eb83 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 e39891a9b..e5f9eaa84 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 bcc054b8e..41325649f 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/cmd/podman/secrets/list.go b/cmd/podman/secrets/list.go index e64990c6f..f136de4ab 100644 --- a/cmd/podman/secrets/list.go +++ b/cmd/podman/secrets/list.go @@ -48,7 +48,7 @@ func init() { } func ls(cmd *cobra.Command, args []string) error { - responses, err := registry.ContainerEngine().SecretList(context.Background()) + responses, err := registry.ContainerEngine().SecretList(context.Background(), entities.SecretListRequest{}) if err != nil { return err } diff --git a/cmd/podman/volumes/import.go b/cmd/podman/volumes/import.go new file mode 100644 index 000000000..441bd0fe4 --- /dev/null +++ b/cmd/podman/volumes/import.go @@ -0,0 +1,97 @@ +package volumes + +import ( + "fmt" + "os" + + "github.com/containers/podman/v3/cmd/podman/common" + "github.com/containers/podman/v3/cmd/podman/inspect" + "github.com/containers/podman/v3/cmd/podman/parse" + "github.com/containers/podman/v3/cmd/podman/registry" + "github.com/containers/podman/v3/pkg/domain/entities" + "github.com/containers/podman/v3/utils" + "github.com/pkg/errors" + "github.com/spf13/cobra" +) + +var ( + importDescription = `Imports contents into a podman volume from specified tarball (.tar, .tar.gz, .tgz, .bzip, .tar.xz, .txz).` + importCommand = &cobra.Command{ + Annotations: map[string]string{registry.EngineMode: registry.ABIMode}, + Use: "import VOLUME [SOURCE]", + Short: "Import a tarball contents into a podman volume", + Long: importDescription, + RunE: importVol, + Args: cobra.ExactArgs(2), + ValidArgsFunction: common.AutocompleteVolumes, + Example: `podman volume import my_vol /home/user/import.tar + cat ctr.tar | podman import volume my_vol -`, + } +) + +func init() { + registry.Commands = append(registry.Commands, registry.CliCommand{ + Command: importCommand, + Parent: volumeCmd, + }) +} + +func importVol(cmd *cobra.Command, args []string) error { + var inspectOpts entities.InspectOptions + var tarFile *os.File + containerEngine := registry.ContainerEngine() + ctx := registry.Context() + // create a slice of volumes since inspect expects slice as arg + volumes := []string{args[0]} + tarPath := args[1] + + if tarPath != "-" { + err := parse.ValidateFileName(tarPath) + if err != nil { + return err + } + + // open tar file + tarFile, err = os.Open(tarPath) + if err != nil { + return err + } + } else { + tarFile = os.Stdin + } + + inspectOpts.Type = inspect.VolumeType + volumeData, _, err := containerEngine.VolumeInspect(ctx, volumes, inspectOpts) + if err != nil { + return err + } + if len(volumeData) < 1 { + return errors.New("no volume data found") + } + mountPoint := volumeData[0].VolumeConfigResponse.Mountpoint + driver := volumeData[0].VolumeConfigResponse.Driver + volumeOptions := volumeData[0].VolumeConfigResponse.Options + volumeMountStatus, err := containerEngine.VolumeMounted(ctx, args[0]) + if err != nil { + return err + } + if mountPoint == "" { + return errors.New("volume is not mounted anywhere on host") + } + // Check if volume is using external plugin and export only if volume is mounted + if driver != "" && driver != "local" { + if !volumeMountStatus.Value { + return fmt.Errorf("volume is using a driver %s and volume is not mounted on %s", driver, mountPoint) + } + } + // Check if volume is using `local` driver and has mount options type other than tmpfs + if driver == "local" { + if mountOptionType, ok := volumeOptions["type"]; ok { + if mountOptionType != "tmpfs" && !volumeMountStatus.Value { + return fmt.Errorf("volume is using a driver %s and volume is not mounted on %s", driver, mountPoint) + } + } + } + // dont care if volume is mounted or not we are gonna import everything to mountPoint + return utils.UntarToFileSystem(mountPoint, tarFile, nil) +} |