diff options
126 files changed, 2107 insertions, 2470 deletions
diff --git a/.cirrus.yml b/.cirrus.yml index 7da409ffb..628b74f72 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -431,7 +431,7 @@ alt_build_task: - env: ALT_NAME: 'Build Without CGO' - env: - ALT_NAME: 'Test build RPM' + ALT_NAME: 'Test build podman-next Copr RPM' - env: ALT_NAME: 'Alt Arch. Cross' # This task cannot make use of the shared repo.tbz artifact. @@ -285,8 +285,9 @@ vendor: .PHONY: vendor-in-container vendor-in-container: - podman run --privileged --rm --env HOME=/root \ + podman run --rm --env HOME=/root \ -v $(CURDIR):/src -w /src \ + --security-opt label=disable \ docker.io/library/golang:1.17 \ make vendor diff --git a/cmd/podman/common/create.go b/cmd/podman/common/create.go index 1e573cc2d..a2bc45b9e 100644 --- a/cmd/podman/common/create.go +++ b/cmd/podman/common/create.go @@ -28,10 +28,10 @@ func ContainerToPodOptions(containerCreate *entities.ContainerCreateOptions, pod } // DefineCreateFlags declares and instantiates the container create flags -func DefineCreateFlags(cmd *cobra.Command, cf *entities.ContainerCreateOptions, isInfra bool, clone bool) { +func DefineCreateFlags(cmd *cobra.Command, cf *entities.ContainerCreateOptions, mode entities.ContainerMode) { createFlags := cmd.Flags() - if !isInfra && !clone { // regular create flags + if mode == entities.CreateMode { // regular create flags annotationFlagName := "annotation" createFlags.StringSliceVar( &cf.Annotation, @@ -103,22 +103,6 @@ func DefineCreateFlags(cmd *cobra.Command, cf *entities.ContainerCreateOptions, ) _ = cmd.RegisterFlagCompletionFunc(deviceCgroupRuleFlagName, 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) - - 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) - createFlags.Bool( "disable-content-trust", false, "This is a Docker specific option and is a NOOP", @@ -597,7 +581,7 @@ func DefineCreateFlags(cmd *cobra.Command, cf *entities.ContainerCreateOptions, `If a container with the same name exists, replace it`, ) } - if isInfra || (!clone && !isInfra) { // infra container flags, create should also pick these up + if mode == entities.InfraMode || (mode == entities.CreateMode) { // infra container flags, create should also pick these up shmSizeFlagName := "shm-size" createFlags.String( shmSizeFlagName, shmSize(), @@ -677,7 +661,7 @@ func DefineCreateFlags(cmd *cobra.Command, cf *entities.ContainerCreateOptions, ) _ = cmd.RegisterFlagCompletionFunc(cgroupParentFlagName, completion.AutocompleteDefault) var conmonPidfileFlagName string - if !isInfra { + if mode == entities.CreateMode { conmonPidfileFlagName = "conmon-pidfile" } else { conmonPidfileFlagName = "infra-conmon-pidfile" @@ -690,7 +674,7 @@ func DefineCreateFlags(cmd *cobra.Command, cf *entities.ContainerCreateOptions, _ = cmd.RegisterFlagCompletionFunc(conmonPidfileFlagName, completion.AutocompleteDefault) var entrypointFlagName string - if !isInfra { + if mode == entities.CreateMode { entrypointFlagName = "entrypoint" } else { entrypointFlagName = "infra-command" @@ -725,7 +709,7 @@ func DefineCreateFlags(cmd *cobra.Command, cf *entities.ContainerCreateOptions, ) _ = cmd.RegisterFlagCompletionFunc(labelFileFlagName, completion.AutocompleteDefault) - if isInfra { + if mode == entities.InfraMode { nameFlagName := "infra-name" createFlags.StringVar( &cf.Name, @@ -775,7 +759,8 @@ func DefineCreateFlags(cmd *cobra.Command, cf *entities.ContainerCreateOptions, ) _ = cmd.RegisterFlagCompletionFunc(volumesFromFlagName, AutocompleteContainers) } - if clone || !isInfra { // clone and create only flags, we need this level of separation so clone does not pick up all of the flags + + if mode == entities.CloneMode || mode == entities.CreateMode { nameFlagName := "name" createFlags.StringVar( &cf.Name, @@ -791,7 +776,8 @@ func DefineCreateFlags(cmd *cobra.Command, cf *entities.ContainerCreateOptions, "Run container in an existing pod", ) _ = cmd.RegisterFlagCompletionFunc(podFlagName, AutocompletePods) - + } + if mode != entities.InfraMode { // clone create and update only flags, we need this level of separation so clone does not pick up all of the flags cpuPeriodFlagName := "cpu-period" createFlags.Uint64Var( &cf.CPUPeriod, @@ -840,8 +826,24 @@ func DefineCreateFlags(cmd *cobra.Command, cf *entities.ContainerCreateOptions, ) _ = cmd.RegisterFlagCompletionFunc(memorySwappinessFlagName, completion.AutocompleteNone) } - // anyone can use these + if mode == entities.CreateMode || mode == entities.UpdateMode { + 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) + 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) + } + // anyone can use these cpusFlagName := "cpus" createFlags.Float64Var( &cf.CPUS, diff --git a/cmd/podman/containers/clone.go b/cmd/podman/containers/clone.go index 9881a791c..62bc90a2c 100644 --- a/cmd/podman/containers/clone.go +++ b/cmd/podman/containers/clone.go @@ -41,7 +41,7 @@ func cloneFlags(cmd *cobra.Command) { flags.BoolVarP(&ctrClone.Force, forceFlagName, "f", false, "force the existing container to be destroyed") common.DefineCreateDefaults(&ctrClone.CreateOpts) - common.DefineCreateFlags(cmd, &ctrClone.CreateOpts, false, true) + common.DefineCreateFlags(cmd, &ctrClone.CreateOpts, entities.CloneMode) } func init() { registry.Commands = append(registry.Commands, registry.CliCommand{ diff --git a/cmd/podman/containers/create.go b/cmd/podman/containers/create.go index 455127fd7..b854ff4b2 100644 --- a/cmd/podman/containers/create.go +++ b/cmd/podman/containers/create.go @@ -72,7 +72,7 @@ func createFlags(cmd *cobra.Command) { flags.SetInterspersed(false) common.DefineCreateDefaults(&cliVals) - common.DefineCreateFlags(cmd, &cliVals, false, false) + common.DefineCreateFlags(cmd, &cliVals, entities.CreateMode) common.DefineNetFlags(cmd) flags.SetNormalizeFunc(utils.AliasFlags) diff --git a/cmd/podman/containers/run.go b/cmd/podman/containers/run.go index ef13ea95e..f66d4d4d3 100644 --- a/cmd/podman/containers/run.go +++ b/cmd/podman/containers/run.go @@ -61,7 +61,7 @@ func runFlags(cmd *cobra.Command) { flags.SetInterspersed(false) common.DefineCreateDefaults(&cliVals) - common.DefineCreateFlags(cmd, &cliVals, false, false) + common.DefineCreateFlags(cmd, &cliVals, entities.CreateMode) common.DefineNetFlags(cmd) flags.SetNormalizeFunc(utils.AliasFlags) diff --git a/cmd/podman/containers/update.go b/cmd/podman/containers/update.go new file mode 100644 index 000000000..33aa25f8e --- /dev/null +++ b/cmd/podman/containers/update.go @@ -0,0 +1,83 @@ +package containers + +import ( + "context" + "fmt" + + "github.com/containers/podman/v4/cmd/podman/common" + "github.com/containers/podman/v4/cmd/podman/registry" + "github.com/containers/podman/v4/pkg/domain/entities" + "github.com/containers/podman/v4/pkg/specgen" + "github.com/containers/podman/v4/pkg/specgenutil" + "github.com/opencontainers/runtime-spec/specs-go" + "github.com/spf13/cobra" +) + +var ( + updateDescription = `Updates the cgroup configuration of a given container` + + updateCommand = &cobra.Command{ + Use: "update [options] CONTAINER", + Short: "update an existing container", + Long: updateDescription, + RunE: update, + Args: cobra.ExactArgs(1), + ValidArgsFunction: common.AutocompleteContainers, + Example: `podman update --cpus=5 foobar_container`, + } + + containerUpdateCommand = &cobra.Command{ + Args: updateCommand.Args, + Use: updateCommand.Use, + Short: updateCommand.Short, + Long: updateCommand.Long, + RunE: updateCommand.RunE, + ValidArgsFunction: updateCommand.ValidArgsFunction, + Example: `podman container update --cpus=5 foobar_container`, + } +) +var ( + updateOpts entities.ContainerCreateOptions +) + +func updateFlags(cmd *cobra.Command) { + common.DefineCreateDefaults(&updateOpts) + common.DefineCreateFlags(cmd, &updateOpts, entities.UpdateMode) +} + +func init() { + registry.Commands = append(registry.Commands, registry.CliCommand{ + Command: updateCommand, + }) + updateFlags(updateCommand) + + registry.Commands = append(registry.Commands, registry.CliCommand{ + Command: containerUpdateCommand, + Parent: containerCmd, + }) + updateFlags(containerUpdateCommand) +} + +func update(cmd *cobra.Command, args []string) error { + var err error + // use a specgen since this is the easiest way to hold resource info + s := &specgen.SpecGenerator{} + s.ResourceLimits = &specs.LinuxResources{} + + // we need to pass the whole specgen since throttle devices are parsed later due to cross compat. + s.ResourceLimits, err = specgenutil.GetResources(s, &updateOpts) + if err != nil { + return err + } + + opts := &entities.ContainerUpdateOptions{ + NameOrID: args[0], + Specgen: s, + } + rep, err := registry.ContainerEngine().ContainerUpdate(context.Background(), opts) + if err != nil { + return err + } + fmt.Println(rep) + return nil +} diff --git a/cmd/podman/machine/init.go b/cmd/podman/machine/init.go index def3334e8..0848147a8 100644 --- a/cmd/podman/machine/init.go +++ b/cmd/podman/machine/init.go @@ -42,7 +42,6 @@ func init() { }) flags := initCmd.Flags() cfg := registry.PodmanConfig() - initOpts.Username = cfg.Config.Machine.User cpusFlagName := "cpus" flags.Uint64Var( @@ -89,6 +88,10 @@ func init() { ) _ = flags.MarkHidden("reexec") + UsernameFlagName := "username" + flags.StringVar(&initOpts.Username, UsernameFlagName, cfg.Machine.User, "Username used in qcow image") + _ = initCmd.RegisterFlagCompletionFunc(UsernameFlagName, completion.AutocompleteDefault) + ImagePathFlagName := "image-path" flags.StringVar(&initOpts.ImagePath, ImagePathFlagName, cfg.Machine.Image, "Path to qcow image") _ = initCmd.RegisterFlagCompletionFunc(ImagePathFlagName, completion.AutocompleteDefault) diff --git a/cmd/podman/pods/clone.go b/cmd/podman/pods/clone.go index 9558c6aed..bf7d10c44 100644 --- a/cmd/podman/pods/clone.go +++ b/cmd/podman/pods/clone.go @@ -44,7 +44,7 @@ func cloneFlags(cmd *cobra.Command) { _ = podCloneCommand.RegisterFlagCompletionFunc(nameFlagName, completion.AutocompleteNone) common.DefineCreateDefaults(&podClone.InfraOptions) - common.DefineCreateFlags(cmd, &podClone.InfraOptions, true, false) + common.DefineCreateFlags(cmd, &podClone.InfraOptions, entities.InfraMode) podClone.InfraOptions.MemorySwappiness = -1 // this is not implemented for pods yet, need to set -1 default manually diff --git a/cmd/podman/pods/create.go b/cmd/podman/pods/create.go index 4f1f66ad6..d30f4782a 100644 --- a/cmd/podman/pods/create.go +++ b/cmd/podman/pods/create.go @@ -65,7 +65,7 @@ func init() { flags := createCommand.Flags() flags.SetInterspersed(false) common.DefineCreateDefaults(&infraOptions) - common.DefineCreateFlags(createCommand, &infraOptions, true, false) + common.DefineCreateFlags(createCommand, &infraOptions, entities.InfraMode) common.DefineNetFlags(createCommand) flags.BoolVar(&createOptions.Infra, "infra", true, "Create an infra container associated with the pod to share namespaces with") diff --git a/contrib/cirrus/lib.sh b/contrib/cirrus/lib.sh index e7ea05867..b03a3da3e 100644 --- a/contrib/cirrus/lib.sh +++ b/contrib/cirrus/lib.sh @@ -158,9 +158,9 @@ setup_rootless() { msg "************************************************************" cd $GOSRC || exit 1 # Guarantee independence from specific values - rootless_uid=$[RANDOM+1000] + rootless_uid=$((1500 + RANDOM % 5000)) ROOTLESS_UID=$rootless_uid - rootless_gid=$[RANDOM+1000] + rootless_gid=$((1500 + RANDOM % 5000)) msg "creating $rootless_uid:$rootless_gid $ROOTLESS_USER user" groupadd -g $rootless_gid $ROOTLESS_USER useradd -g $rootless_gid -u $rootless_uid --no-user-group --create-home $ROOTLESS_USER diff --git a/docs/source/markdown/.gitignore b/docs/source/markdown/.gitignore index 8a0d553ba..74e7fc075 100644 --- a/docs/source/markdown/.gitignore +++ b/docs/source/markdown/.gitignore @@ -15,6 +15,7 @@ podman-manifest-push.1.md podman-pause.1.md podman-pod-clone.1.md podman-pod-create.1.md +podman-pod-kill.1.md podman-pod-logs.1.md podman-pod-rm.1.md podman-pod-start.1.md @@ -26,3 +27,4 @@ podman-run.1.md podman-search.1.md podman-stop.1.md podman-unpause.1.md +podman-update.1.md diff --git a/docs/source/markdown/links/podman-container-update.1 b/docs/source/markdown/links/podman-container-update.1 new file mode 100644 index 000000000..e903b5c06 --- /dev/null +++ b/docs/source/markdown/links/podman-container-update.1 @@ -0,0 +1 @@ +.so man1/podman-update.1 diff --git a/docs/source/markdown/options/device-read-bps.md b/docs/source/markdown/options/device-read-bps.md new file mode 100644 index 000000000..e0c610061 --- /dev/null +++ b/docs/source/markdown/options/device-read-bps.md @@ -0,0 +1,5 @@ +#### **--device-read-bps**=*path:rate* + +Limit read rate (in bytes per second) from a device (e.g. **--device-read-bps=/dev/sda:1mb**). + +This option is not supported on cgroups V1 rootless systems. diff --git a/docs/source/markdown/options/device-read-iops.md b/docs/source/markdown/options/device-read-iops.md new file mode 100644 index 000000000..9cd0f9030 --- /dev/null +++ b/docs/source/markdown/options/device-read-iops.md @@ -0,0 +1,5 @@ +#### **--device-read-iops**=*path:rate* + +Limit read rate (in IO operations per second) from a device (e.g. **--device-read-iops=/dev/sda:1000**). + +This option is not supported on cgroups V1 rootless systems. diff --git a/docs/source/markdown/options/device-write-bps.md b/docs/source/markdown/options/device-write-bps.md new file mode 100644 index 000000000..3dacc4515 --- /dev/null +++ b/docs/source/markdown/options/device-write-bps.md @@ -0,0 +1,5 @@ +#### **--device-write-bps**=*path:rate* + +Limit write rate (in bytes per second) to a device (e.g. **--device-write-bps=/dev/sda:1mb**). + +This option is not supported on cgroups V1 rootless systems. diff --git a/docs/source/markdown/options/device-write-iops.md b/docs/source/markdown/options/device-write-iops.md new file mode 100644 index 000000000..cf5ce3859 --- /dev/null +++ b/docs/source/markdown/options/device-write-iops.md @@ -0,0 +1,5 @@ +#### **--device-write-iops**=*path:rate* + +Limit write rate (in IO operations per second) to a device (e.g. **--device-write-iops=/dev/sda:1000**). + +This option is not supported on cgroups V1 rootless systems. diff --git a/docs/source/markdown/options/http-proxy.md b/docs/source/markdown/options/http-proxy.md new file mode 100644 index 000000000..f83dcde4a --- /dev/null +++ b/docs/source/markdown/options/http-proxy.md @@ -0,0 +1,15 @@ +#### **--http-proxy** + +By default proxy environment variables are passed into the container if set +for the Podman process. This can be disabled by setting the value to **false**. +The environment variables passed in include **http_proxy**, +**https_proxy**, **ftp_proxy**, **no_proxy**, and also the upper case versions of +those. This option is only needed when the host system must use a proxy but +the container should not use any proxy. Proxy environment variables specified +for the container in any other way will override the values that would have +been passed through from the host. (Other ways to specify the proxy for the +container include passing the values with the **--env** flag, or hard coding the +proxy environment at container build time.) +(This option is not available with the remote Podman client, including Mac and Windows (excluding WSL2) machines) + +Defaults to **true**. diff --git a/docs/source/markdown/options/memory-reservation.md b/docs/source/markdown/options/memory-reservation.md new file mode 100644 index 000000000..410f1dd7c --- /dev/null +++ b/docs/source/markdown/options/memory-reservation.md @@ -0,0 +1,11 @@ +#### **--memory-reservation**=*number[unit]* + +Memory soft limit. A _unit_ can be **b** (bytes), **k** (kibibytes), **m** (mebibytes), or **g** (gibibytes). + +After setting memory reservation, when the system detects memory contention +or low memory, containers are forced to restrict their consumption to their +reservation. So you should always set the value below **--memory**, otherwise the +hard limit will take precedence. By default, memory reservation will be the same +as memory limit. + +This option is not supported on cgroups V1 rootless systems. diff --git a/docs/source/markdown/options/memory-swap.md b/docs/source/markdown/options/memory-swap.md new file mode 100644 index 000000000..08ee8b1a0 --- /dev/null +++ b/docs/source/markdown/options/memory-swap.md @@ -0,0 +1,13 @@ +#### **--memory-swap**=*number[unit]* + +A limit value equal to memory plus swap. +A _unit_ can be **b** (bytes), **k** (kibibytes), **m** (mebibytes), or **g** (gibibytes). + +Must be used with the **-m** (**--memory**) flag. +The argument value should always be larger than that of + **-m** (**--memory**) By default, it is set to double +the value of **--memory**. + +Set _number_ to **-1** to enable unlimited swap. + +This option is not supported on cgroups V1 rootless systems. diff --git a/docs/source/markdown/options/memory.md b/docs/source/markdown/options/memory.md new file mode 100644 index 000000000..1be9159c3 --- /dev/null +++ b/docs/source/markdown/options/memory.md @@ -0,0 +1,11 @@ +#### **--memory**, **-m**=*number[unit]* + +Memory limit. A _unit_ can be **b** (bytes), **k** (kibibytes), **m** (mebibytes), or **g** (gibibytes). + +Allows the memory available to a container to be constrained. If the host +supports swap memory, then the **-m** memory setting can be larger than physical +RAM. If a limit of 0 is specified (not using **-m**), the container's memory is +not limited. The actual limit may be rounded up to a multiple of the operating +system's page size (the value would be very large, that's millions of trillions). + +This option is not supported on cgroups V1 rootless systems. diff --git a/docs/source/markdown/options/name.container.md b/docs/source/markdown/options/name.container.md new file mode 100644 index 000000000..0c3df6ef1 --- /dev/null +++ b/docs/source/markdown/options/name.container.md @@ -0,0 +1,14 @@ +#### **--name**=*name* + +Assign a name to the container. + +The operator can identify a container in three ways: + +- UUID long identifier (“f78375b1c487e03c9438c729345e54db9d20cfa2ac1fc3494b6eb60872e74778”); +- UUID short identifier (“f78375b1c487”); +- Name (“jonah”). + +Podman generates a UUID for each container, and if a name is not assigned +to the container with **--name** then it will generate a random +string name. The name is useful any place you need to identify a container. +This works for both background and foreground containers. diff --git a/docs/source/markdown/options/restart.md b/docs/source/markdown/options/restart.md new file mode 100644 index 000000000..825ae613f --- /dev/null +++ b/docs/source/markdown/options/restart.md @@ -0,0 +1,15 @@ +#### **--restart**=*policy* + +Restart policy to follow when containers exit. +Restart policy will not take effect if a container is stopped via the **podman kill** or **podman stop** commands. + +Valid _policy_ values are: + +- `no` : Do not restart containers on exit +- `on-failure[:max_retries]` : Restart containers when they exit with a non-zero exit code, retrying indefinitely or until the optional *max_retries* count is hit +- `always` : Restart containers when they exit, regardless of status, retrying indefinitely +- `unless-stopped` : Identical to **always** + +Please note that restart will not restart containers after a system reboot. +If this functionality is required in your environment, you can invoke Podman from a **systemd.unit**(5) file, or create an init script for whichever init system is in use. +To generate systemd unit files, please see **podman generate systemd**. diff --git a/docs/source/markdown/options/signal.md b/docs/source/markdown/options/signal.md new file mode 100644 index 000000000..6e6c03657 --- /dev/null +++ b/docs/source/markdown/options/signal.md @@ -0,0 +1,4 @@ +#### **--signal**, **-s**=**signal** + +Signal to send to the container<<|s in the pod>>. For more information on Linux signals, refer to *signal(7)*. +The default is **SIGKILL**. diff --git a/docs/source/markdown/options/subgidname.md b/docs/source/markdown/options/subgidname.md new file mode 100644 index 000000000..1ca0496d0 --- /dev/null +++ b/docs/source/markdown/options/subgidname.md @@ -0,0 +1,5 @@ +#### **--subgidname**=*name* + +Run the container in a new user namespace using the map with _name_ in the _/etc/subgid_ file. +If running rootless, the user needs to have the right to use the mapping. See **subgid**(5). +This flag conflicts with **--userns** and **--gidmap**. diff --git a/docs/source/markdown/options/subuidname.md b/docs/source/markdown/options/subuidname.md new file mode 100644 index 000000000..bd7b61575 --- /dev/null +++ b/docs/source/markdown/options/subuidname.md @@ -0,0 +1,5 @@ +#### **--subuidname**=*name* + +Run the container in a new user namespace using the map with _name_ in the _/etc/subuid_ file. +If running rootless, the user needs to have the right to use the mapping. See **subuid**(5). +This flag conflicts with **--userns** and **--uidmap**. diff --git a/docs/source/markdown/options/userns.container.md b/docs/source/markdown/options/userns.container.md new file mode 100644 index 000000000..8f96892df --- /dev/null +++ b/docs/source/markdown/options/userns.container.md @@ -0,0 +1,47 @@ +#### **--userns**=*mode* + +Set the user namespace mode for the container. It defaults to the **PODMAN_USERNS** environment variable. An empty value ("") means user namespaces are disabled unless an explicit mapping is set with the **--uidmap** and **--gidmap** options. + +This option is incompatible with **--gidmap**, **--uidmap**, **--subuidname** and **--subgidname**. + +Rootless user --userns=Key mappings: + +Key | Host User | Container User +----------|---------------|--------------------- +"" |$UID |0 (Default User account mapped to root user in container.) +keep-id |$UID |$UID (Map user account to same UID within container.) +auto |$UID | nil (Host User UID is not mapped into container.) +nomap |$UID | nil (Host User UID is not mapped into container.) + +Valid _mode_ values are: + +**auto**[:_OPTIONS,..._]: automatically create a unique user namespace. + +The `--userns=auto` flag, requires that the user name `containers` and a range of subordinate user ids that the Podman container is allowed to use be specified in the /etc/subuid and /etc/subgid files. + +Example: `containers:2147483647:2147483648`. + +Podman allocates unique ranges of UIDs and GIDs from the `containers` subordinate user ids. The size of the ranges is based on the number of UIDs required in the image. The number of UIDs and GIDs can be overridden with the `size` option. + +The rootless option `--userns=keep-id` uses all the subuids and subgids of the user. Using `--userns=auto` when starting new containers will not work as long as any containers exist that were started with `--userns=keep-id`. + + Valid `auto` options: + + - *gidmapping*=_CONTAINER_GID:HOST_GID:SIZE_: to force a GID mapping to be present in the user namespace. + - *size*=_SIZE_: to specify an explicit size for the automatic user namespace. e.g. `--userns=auto:size=8192`. If `size` is not specified, `auto` will estimate a size for the user namespace. + - *uidmapping*=_CONTAINER_UID:HOST_UID:SIZE_: to force a UID mapping to be present in the user namespace. + +**container:**_id_: join the user namespace of the specified container. + +**host**: run in the user namespace of the caller. The processes running in the container will have the same privileges on the host as any other process launched by the calling user (default). + +**keep-id**: creates a user namespace where the current rootless user's UID:GID are mapped to the same values in the container. This option is not allowed for containers created by the root user. + + Valid `keep-id` options: + + - *uid*=UID: override the UID inside the container that will be used to map the current rootless user to. + - *gid*=GID: override the GID inside the container that will be used to map the current rootless user to. + +**nomap**: creates a user namespace where the current rootless user's UID:GID are not mapped into the container. This option is not allowed for containers created by the root user. + +**ns:**_namespace_: run the <<container|pod>> in the given existing user namespace. diff --git a/docs/source/markdown/podman-container-clone.1.md.in b/docs/source/markdown/podman-container-clone.1.md.in index 26f414b62..3e31389d2 100644 --- a/docs/source/markdown/podman-container-clone.1.md.in +++ b/docs/source/markdown/podman-container-clone.1.md.in @@ -52,36 +52,18 @@ If none are specified, the original container's CPU memory nodes are used. @@option destroy -#### **--device-read-bps**=*path* +@@option device-read-bps -Limit read rate (bytes per second) from a device (e.g. --device-read-bps=/dev/sda:1mb). - -This option is not supported on cgroups V1 rootless systems. - -#### **--device-write-bps**=*path* - -Limit write rate (bytes per second) to a device (e.g. --device-write-bps=/dev/sda:1mb) - -This option is not supported on cgroups V1 rootless systems. +@@option device-write-bps #### **--force**, **-f** Force removal of the original container that we are cloning. Can only be used in conjunction with **--destroy**. -#### **--memory**, **-m**=*limit* - -Memory limit (format: `<number>[<unit>]`, where unit = b (bytes), k (kibibytes), m (mebibytes), or g (gibibytes)) - -Allows the memory available to a container to be constrained. If the host -supports swap memory, then the **-m** memory setting can be larger than physical -RAM. If a limit of 0 is specified (not using **-m**), the container's memory is -not limited. The actual limit may be rounded up to a multiple of the operating -system's page size (the value would be very large, that's millions of trillions). +@@option memory If no memory limits are specified, the original container's will be used. -This option is not supported on cgroups V1 rootless systems. - #### **--memory-reservation**=*limit* Memory soft limit (format: `<number>[<unit>]`, where unit = b (bytes), k (kibibytes), m (mebibytes), or g (gibibytes)) @@ -92,8 +74,6 @@ reservation. So you should always set the value below **--memory**, otherwise th hard limit will take precedence. By default, memory reservation will be the same as memory limit from the container being cloned. -This option is not supported on cgroups V1 rootless systems. - #### **--memory-swap**=*limit* A limit value equal to memory plus swap. Must be used with the **-m** diff --git a/docs/source/markdown/podman-container.1.md b/docs/source/markdown/podman-container.1.md index a66e2789d..593cd3c42 100644 --- a/docs/source/markdown/podman-container.1.md +++ b/docs/source/markdown/podman-container.1.md @@ -46,6 +46,7 @@ The container command allows you to manage containers | top | [podman-top(1)](podman-top.1.md) | Display the running processes of a container. | | unmount | [podman-unmount(1)](podman-unmount.1.md) | Unmount a working container's root filesystem.(Alias unmount) | | unpause | [podman-unpause(1)](podman-unpause.1.md) | Unpause one or more containers. | +| update | [podman-update(1)](podman-update.1.md) | Updates the cgroup configuration of a given container. | | wait | [podman-wait(1)](podman-wait.1.md) | Wait on one or more containers to stop and print their exit codes. | ## SEE ALSO diff --git a/docs/source/markdown/podman-create.1.md.in b/docs/source/markdown/podman-create.1.md.in index 0a880951d..987f10181 100644 --- a/docs/source/markdown/podman-create.1.md.in +++ b/docs/source/markdown/podman-create.1.md.in @@ -87,9 +87,7 @@ each of stdin, stdout, and stderr. @@option blkio-weight -#### **--blkio-weight-device**=*device:weight* - -Block IO relative device weight. +@@option blkio-weight-device @@option cap-add @@ -148,27 +146,11 @@ device. The devices that podman will load modules when necessary are: #### **--device-read-bps**=*path* -Limit read rate (bytes per second) from a device (e.g. --device-read-bps=/dev/sda:1mb) - -This option is not supported on cgroups V1 rootless systems. - -#### **--device-read-iops**=*path* - -Limit read rate (IO per second) from a device (e.g. --device-read-iops=/dev/sda:1000) - -This option is not supported on cgroups V1 rootless systems. - -#### **--device-write-bps**=*path* - -Limit write rate (bytes per second) to a device (e.g. --device-write-bps=/dev/sda:1mb) +@@option device-read-iops -This option is not supported on cgroups V1 rootless systems. +@@option device-write-bps -#### **--device-write-iops**=*path* - -Limit write rate (IO per second) to a device (e.g. --device-write-iops=/dev/sda:1000) - -This option is not supported on cgroups V1 rootless systems. +@@option device-write-iops @@option disable-content-trust @@ -230,25 +212,7 @@ Print usage statement @@option hostuser -#### **--http-proxy** - -By default proxy environment variables are passed into the container if set -for the Podman process. This can be disabled by setting the `--http-proxy` -option to `false`. The environment variables passed in include `http_proxy`, -`https_proxy`, `ftp_proxy`, `no_proxy`, and also the upper case versions of -those. This option is only needed when the host system must use a proxy but -the container should not use any proxy. Proxy environment variables specified -for the container in any other way will override the values that would have -been passed through from the host. (Other ways to specify the proxy for the -container include passing the values with the `--env` flag, or hard coding the -proxy environment at container build time.) (This option is not available with the remote Podman client, including Mac and Windows (excluding WSL2) machines) - -For example, to disable passing these environment variables from host to -container: - -`--http-proxy=false` - -Defaults to `true` +@@option http-proxy @@option image-volume @@ -325,60 +289,17 @@ This option is currently supported only by the **journald** log driver. @@option mac-address -#### **--memory**, **-m**=*limit* +@@option memory -Memory limit (format: `<number>[<unit>]`, where unit = b (bytes), k (kibibytes), m (mebibytes), or g (gibibytes)) +@@option memory-reservation -Allows you to constrain the memory available to a container. If the host -supports swap memory, then the **-m** memory setting can be larger than physical -RAM. If a limit of 0 is specified (not using **-m**), the container's memory is -not limited. The actual limit may be rounded up to a multiple of the operating -system's page size (the value would be very large, that's millions of trillions). - -This option is not supported on cgroups V1 rootless systems. - -#### **--memory-reservation**=*limit* - -Memory soft limit (format: `<number>[<unit>]`, where unit = b (bytes), k (kibibytes), m (mebibytes), or g (gibibytes)) - -After setting memory reservation, when the system detects memory contention -or low memory, containers are forced to restrict their consumption to their -reservation. So you should always set the value below **--memory**, otherwise the -hard limit will take precedence. By default, memory reservation will be the same -as memory limit. - -This option is not supported on cgroups V1 rootless systems. - -#### **--memory-swap**=*limit* - -A limit value equal to memory plus swap. Must be used with the **-m** -(**--memory**) flag. The swap `LIMIT` should always be larger than **-m** -(**--memory**) value. By default, the swap `LIMIT` will be set to double -the value of --memory. - -The format of `LIMIT` is `<number>[<unit>]`. Unit can be `b` (bytes), -`k` (kibibytes), `m` (mebibytes), or `g` (gibibytes). If you don't specify a -unit, `b` is used. Set LIMIT to `-1` to enable unlimited swap. - -This option is not supported on cgroups V1 rootless systems. +@@option memory-swap @@option memory-swappiness @@option mount -#### **--name**=*name* - -Assign a name to the container - -The operator can identify a container in three ways: -UUID long identifier (“f78375b1c487e03c9438c729345e54db9d20cfa2ac1fc3494b6eb60872e74778”) -UUID short identifier (“f78375b1c487”) -Name (“jonah”) - -podman generates a UUID for each container, and if a name is not assigned -to the container with **--name** then it will generate a random -string name. The name is useful any place you need to identify a container. -This works for both background and foreground containers. +@@option name.container #### **--network**=*mode*, **--net** @@ -518,21 +439,7 @@ Suppress output information when pulling images @@option requires -#### **--restart**=*policy* - -Restart policy to follow when containers exit. -Restart policy will not take effect if a container is stopped via the `podman kill` or `podman stop` commands. - -Valid values are: - -- `no` : Do not restart containers on exit -- `on-failure[:max_retries]` : Restart containers when they exit with a non-0 exit code, retrying indefinitely or until the optional max_retries count is hit -- `always` : Restart containers when they exit, regardless of status, retrying indefinitely -- `unless-stopped` : Identical to **always** - -Please note that restart will not restart containers after a system reboot. -If this functionality is required in your environment, you can invoke Podman from a systemd unit file, or create an init script for whichever init system is in use. -To generate systemd unit files, please see *podman generate systemd* +@@option restart #### **--rm** @@ -604,13 +511,9 @@ When size is `0`, there is no limit on the amount of memory used for IPC by the @@option stop-timeout -#### **--subgidname**=*name* - -Name for GID map from the `/etc/subgid` file. Using this flag will run the container with user namespace enabled. This flag conflicts with `--userns` and `--gidmap`. - -#### **--subuidname**=*name* +@@option subgidname -Name for UID map from the `/etc/subuid` file. Using this flag will run the container with user namespace enabled. This flag conflicts with `--userns` and `--uidmap`. +@@option subuidname #### **--sysctl**=*SYSCTL* @@ -670,48 +573,7 @@ The following examples are all valid: Without this argument the command will be run as root in the container. -#### **--userns**=*mode* - -Set the user namespace mode for the container. It defaults to the **PODMAN_USERNS** environment variable. An empty value ("") means user namespaces are disabled unless an explicit mapping is set with the **--uidmap** and **--gidmap** options. - -Rootless user --userns=Key mappings: - -Key | Host User | Container User -----------|---------------|--------------------- -"" |$UID |0 (Default User account mapped to root user in container.) -keep-id |$UID |$UID (Map user account to same UID within container.) -auto |$UID | nil (Host User UID is not mapped into container.) -nomap |$UID | nil (Host User UID is not mapped into container.) - -Valid _mode_ values are: - -**auto**[:_OPTIONS,..._]: automatically create a unique user namespace. - -The `--userns=auto` flag, requires that the user name `containers` and a range of subordinate user ids that the Podman container is allowed to use be specified in the /etc/subuid and /etc/subgid files. - -Example: `containers:2147483647:2147483648`. - -Podman allocates unique ranges of UIDs and GIDs from the `containers` subordinate user ids. The size of the ranges is based on the number of UIDs required in the image. The number of UIDs and GIDs can be overridden with the `size` option. The `auto` options currently does not work in rootless mode - - Valid `auto` options: - - - *gidmapping*=_CONTAINER_GID:HOST_GID:SIZE_: to force a GID mapping to be present in the user namespace. - - *size*=_SIZE_: to specify an explicit size for the automatic user namespace. e.g. `--userns=auto:size=8192`. If `size` is not specified, `auto` will estimate a size for the user namespace. - - *uidmapping*=_CONTAINER_UID:HOST_UID:SIZE_: to force a UID mapping to be present in the user namespace. - -**container:**_id_: join the user namespace of the specified container. - -**host**: run in the user namespace of the caller. The processes running in the container will have the same privileges on the host as any other process launched by the calling user (default). - -**keep-id**: creates a user namespace where the current rootless user's UID:GID are mapped to the same values in the container. This option is not allowed for containers created by the root user. - -**nomap**: creates a user namespace where the current rootless user's UID:GID are not mapped into the container. This option is not allowed for containers created by the root user. - -**ns:**_namespace_: run the container in the given existing user namespace. - -**private**: create a new namespace for the container. - -This option is incompatible with **--gidmap**, **--uidmap**, **--subuidname** and **--subgidname**. +@@option userns.container @@option uts.container diff --git a/docs/source/markdown/podman-kill.1.md.in b/docs/source/markdown/podman-kill.1.md.in index 2788cc694..46d7f5c6b 100644 --- a/docs/source/markdown/podman-kill.1.md.in +++ b/docs/source/markdown/podman-kill.1.md.in @@ -23,10 +23,7 @@ Signal all running and paused containers. Instead of providing the container name or ID, use the last created container. If you use methods other than Podman to run containers such as CRI-O, the last started container could be from either of those methods. (This option is not available with the remote Podman client, including Mac and Windows (excluding WSL2) machines) -#### **--signal**, **-s** - -Signal to send to the container. For more information on Linux signals, refer to *man signal(7)*. - +@@option signal ## EXAMPLE diff --git a/docs/source/markdown/podman-kube-play.1.md.in b/docs/source/markdown/podman-kube-play.1.md.in index f0b404057..bcd5687ca 100644 --- a/docs/source/markdown/podman-kube-play.1.md.in +++ b/docs/source/markdown/podman-kube-play.1.md.in @@ -21,7 +21,7 @@ Currently, the supported Kubernetes kinds are: `Kubernetes Pods or Deployments` -Only two volume types are supported by kube play, the *hostPath* and *persistentVolumeClaim* volume types. For the *hostPath* volume type, only the *default (empty)*, *DirectoryOrCreate*, *Directory*, *FileOrCreate*, *File*, *Socket*, *CharDevice* and *BlockDevice* subtypes are supported. Podman interprets the value of *hostPath* *path* as a file path when it contains at least one forward slash, otherwise Podman treats the value as the name of a named volume. When using a *persistentVolumeClaim*, the value for *claimName* is the name for the Podman named volume. +Only three volume types are supported by kube play, the *hostPath*, *emptyDir*, and *persistentVolumeClaim* volume types. For the *hostPath* volume type, only the *default (empty)*, *DirectoryOrCreate*, *Directory*, *FileOrCreate*, *File*, *Socket*, *CharDevice* and *BlockDevice* subtypes are supported. Podman interprets the value of *hostPath* *path* as a file path when it contains at least one forward slash, otherwise Podman treats the value as the name of a named volume. When using a *persistentVolumeClaim*, the value for *claimName* is the name for the Podman named volume. When using an *emptyDir* volume, podman creates an anonymous volume that is attached the containers running inside the pod and is deleted once the pod is removed. Note: When playing a kube YAML with init containers, the init container will be created with init type value `once`. To change the default type, use the `io.podman.annotations.init.container.type` annotation to set the type to `always`. @@ -225,45 +225,7 @@ Require HTTPS and verify certificates when contacting registries (default: true) then TLS verification will be used. If set to false, then TLS verification will not be used. If not specified, TLS verification will be used unless the target registry is listed as an insecure registry in registries.conf. -#### **--userns**=*mode* - -Set the user namespace mode for the container. It defaults to the **PODMAN_USERNS** environment variable. An empty value ("") means user namespaces are disabled unless an explicit mapping is set with the **--uidmap** and **--gidmap** options. - -Rootless user --userns=Key mappings: - -Key | Host User | Container User -----------|---------------|--------------------- -"" |$UID |0 (Default User account mapped to root user in container.) -keep-id |$UID |$UID (Map user account to same UID within container.) -auto |$UID | nil (Host User UID is not mapped into container.) -nomap |$UID | nil (Host User UID is not mapped into container.) - -Valid _mode_ values are: - -**auto**[:_OPTIONS,..._]: automatically create a unique user namespace. - -The `--userns=auto` flag, requires that the user name `containers` and a range of subordinate user ids that the Podman container is allowed to use be specified in the /etc/subuid and /etc/subgid files. - -Example: `containers:2147483647:2147483648`. - -Podman allocates unique ranges of UIDs and GIDs from the `containers` subordinate user ids. The size of the ranges is based on the number of UIDs required in the image. The number of UIDs and GIDs can be overridden with the `size` option. The `auto` options currently does not work in rootless mode - - Valid `auto` options: - - - *gidmapping*=_CONTAINER_GID:HOST_GID:SIZE_: to force a GID mapping to be present in the user namespace. - - *size*=_SIZE_: to specify an explicit size for the automatic user namespace. e.g. `--userns=auto:size=8192`. If `size` is not specified, `auto` will estimate a size for the user namespace. - - *uidmapping*=_CONTAINER_UID:HOST_UID:SIZE_: to force a UID mapping to be present in the user namespace. - -**container:**_id_: join the user namespace of the specified container. - -**host**: create a new namespace for the container. - -**keep-id**: creates a user namespace where the current rootless user's UID:GID are mapped to the same values in the container. This option is not allowed for containers created by the root user. - -**nomap**: creates a user namespace where the current rootless user's UID:GID are not mapped into the container. This option is not allowed for containers created by the root user. - -**ns:**_namespace_: run the pod in the given existing user namespace. - +@@option userns.container ## EXAMPLES Recreate the pod and containers as described in a file called `demo.yml` diff --git a/docs/source/markdown/podman-machine-init.1.md b/docs/source/markdown/podman-machine-init.1.md index 07273a111..cf2eeca0b 100644 --- a/docs/source/markdown/podman-machine-init.1.md +++ b/docs/source/markdown/podman-machine-init.1.md @@ -76,6 +76,12 @@ Set the timezone for the machine and containers. Valid values are `local` or a `timezone` such as `America/Chicago`. A value of `local`, which is the default, means to use the timezone of the machine host. +#### **--username** + +Username to use for executing commands in remote VM. Default value is `core` +for FCOS and `user` for Fedora (default on Windows hosts). Should match the one +used inside the resulting VM image. + #### **--volume**, **-v**=*source:target[:options]* Mounts a volume from source to target. diff --git a/docs/source/markdown/podman-pod-clone.1.md.in b/docs/source/markdown/podman-pod-clone.1.md.in index c040f1c27..24edc44ec 100644 --- a/docs/source/markdown/podman-pod-clone.1.md.in +++ b/docs/source/markdown/podman-pod-clone.1.md.in @@ -48,13 +48,9 @@ Podman may load kernel modules required for using the specified device. The devices that Podman will load modules for when necessary are: /dev/fuse. -#### **--device-read-bps**=*path* +@@option device-read-bps -Limit read rate (bytes per second) from a device (e.g. --device-read-bps=/dev/sda:1mb). - -#### **--device-write-bps**=*path* - -Limit write rate (bytes per second) to a device (e.g. --device-write-bps=/dev/sda:1mb) +@@option device-write-bps @@option gidmap.pod @@ -146,13 +142,9 @@ When size is `0`, there is no limit on the amount of memory used for IPC by the When set to true, this flag starts the newly created pod after the clone process has completed. All containers within the pod are started. -#### **--subgidname**=*name* - -Name for GID map from the `/etc/subgid` file. Using this flag will run the container with user namespace enabled. This flag conflicts with `--userns` and `--gidmap`. - -#### **--subuidname**=*name* +@@option subgidname -Name for UID map from the `/etc/subuid` file. Using this flag will run the container with user namespace enabled. This flag conflicts with `--userns` and `--uidmap`. +@@option subuidname #### **--sysctl**=*name=value* diff --git a/docs/source/markdown/podman-pod-create.1.md.in b/docs/source/markdown/podman-pod-create.1.md.in index 702780c65..35d06fa00 100644 --- a/docs/source/markdown/podman-pod-create.1.md.in +++ b/docs/source/markdown/podman-pod-create.1.md.in @@ -65,13 +65,9 @@ Podman may load kernel modules required for using the specified device. The devices that Podman will load modules for when necessary are: /dev/fuse. -#### **--device-read-bps**=*path* +@@option device-read-bps -Limit read rate (bytes per second) from a device (e.g. --device-read-bps=/dev/sda:1mb) - -#### **--device-write-bps**=*path* - -Limit write rate (bytes per second) to a device (e.g. --device-write-bps=/dev/sda:1mb) +@@option device-write-bps #### **--dns**=*ipaddr* @@ -287,14 +283,9 @@ Size of `/dev/shm` (format: `<number>[<unit>]`, where unit = b (bytes), k (kibib If the unit is omitted, the system uses bytes. If the size is omitted, the system uses `64m`. When size is `0`, there is no limit on the amount of memory used for IPC by the pod. This option conflicts with **--ipc=host** when running containers. -#### **--subgidname**=*name* - -Name for GID map from the `/etc/subgid` file. Using this flag will run the container with user namespace enabled. This flag conflicts with `--userns` and `--gidmap`. - -#### **--subuidname**=*name* - -Name for UID map from the `/etc/subuid` file. Using this flag will run the container with user namespace enabled. This flag conflicts with `--userns` and `--uidmap`. +@@option subgidname +@@option subuidname #### **--sysctl**=*name=value* diff --git a/docs/source/markdown/podman-pod-kill.1.md b/docs/source/markdown/podman-pod-kill.1.md.in index 96ced68a7..7f37661b0 100644 --- a/docs/source/markdown/podman-pod-kill.1.md +++ b/docs/source/markdown/podman-pod-kill.1.md.in @@ -19,10 +19,7 @@ Sends signal to all containers associated with a pod. Instead of providing the pod name or ID, use the last created pod. If you use methods other than Podman to run pods such as CRI-O, the last started pod could be from either of those methods. (This option is not available with the remote Podman client, including Mac and Windows (excluding WSL2) machines) -#### **--signal**, **-s** - -Signal to send to the containers in the pod. For more information on Linux signals, refer to *man signal(7)*. - +@@option signal ## EXAMPLE diff --git a/docs/source/markdown/podman-run.1.md.in b/docs/source/markdown/podman-run.1.md.in index 6798c65da..64affa238 100644 --- a/docs/source/markdown/podman-run.1.md.in +++ b/docs/source/markdown/podman-run.1.md.in @@ -180,29 +180,13 @@ device. The devices that Podman will load modules when necessary are: @@option device-cgroup-rule -#### **--device-read-bps**=*path:rate* +@@option device-read-bps -Limit read rate (in bytes per second) from a device (e.g. **--device-read-bps=/dev/sda:1mb**). +@@option device-read-iops -This option is not supported on cgroups V1 rootless systems. +@@option device-write-bps -#### **--device-read-iops**=*path:rate* - -Limit read rate (in IO operations per second) from a device (e.g. **--device-read-iops=/dev/sda:1000**). - -This option is not supported on cgroups V1 rootless systems. - -#### **--device-write-bps**=*path:rate* - -Limit write rate (in bytes per second) to a device (e.g. **--device-write-bps=/dev/sda:1mb**). - -This option is not supported on cgroups V1 rootless systems. - -#### **--device-write-iops**=*path:rate* - -Limit write rate (in IO operations per second) to a device (e.g. **--device-write-iops=/dev/sda:1000**). - -This option is not supported on cgroups V1 rootless systems. +@@option device-write-iops @@option disable-content-trust @@ -264,20 +248,7 @@ Print usage statement @@option hostuser -#### **--http-proxy** - -By default proxy environment variables are passed into the container if set -for the Podman process. This can be disabled by setting the value to **false**. -The environment variables passed in include **http_proxy**, -**https_proxy**, **ftp_proxy**, **no_proxy**, and also the upper case versions of -those. This option is only needed when the host system must use a proxy but -the container should not use any proxy. Proxy environment variables specified -for the container in any other way will override the values that would have -been passed through from the host. (Other ways to specify the proxy for the -container include passing the values with the **--env** flag, or hard coding the -proxy environment at container build time.) (This option is not available with the remote Podman client, including Mac and Windows (excluding WSL2) machines) - -Defaults to **true**. +@@option http-proxy @@option image-volume @@ -348,52 +319,15 @@ RAM. If a limit of 0 is specified (not using **-m**), the container's memory is not limited. The actual limit may be rounded up to a multiple of the operating system's page size (the value would be very large, that's millions of trillions). -This option is not supported on cgroups V1 rootless systems. - -#### **--memory-reservation**=*number[unit]* - -Memory soft limit. A _unit_ can be **b** (bytes), **k** (kibibytes), **m** (mebibytes), or **g** (gibibytes). +@@option memory-reservation -After setting memory reservation, when the system detects memory contention -or low memory, containers are forced to restrict their consumption to their -reservation. So you should always set the value below **--memory**, otherwise the -hard limit will take precedence. By default, memory reservation will be the same -as memory limit. - -This option is not supported on cgroups V1 rootless systems. - -#### **--memory-swap**=*number[unit]* - -A limit value equal to memory plus swap. -A _unit_ can be **b** (bytes), **k** (kibibytes), **m** (mebibytes), or **g** (gibibytes). - -Must be used with the **-m** (**--memory**) flag. -The argument value should always be larger than that of - **-m** (**--memory**) By default, it is set to double -the value of **--memory**. - -Set _number_ to **-1** to enable unlimited swap. - -This option is not supported on cgroups V1 rootless systems. +@@option memory-swap @@option memory-swappiness @@option mount -#### **--name**=*name* - -Assign a name to the container. - -The operator can identify a container in three ways: - -- UUID long identifier (“f78375b1c487e03c9438c729345e54db9d20cfa2ac1fc3494b6eb60872e74778”); -- UUID short identifier (“f78375b1c487”); -- Name (“jonah”). - -Podman generates a UUID for each container, and if a name is not assigned -to the container with **--name** then it will generate a random -string name. The name is useful any place you need to identify a container. -This works for both background and foreground containers. +@@option name.container #### **--network**=*mode*, **--net** @@ -544,21 +478,7 @@ Suppress output information when pulling images @@option requires -#### **--restart**=*policy* - -Restart policy to follow when containers exit. -Restart policy will not take effect if a container is stopped via the **podman kill** or **podman stop** commands. - -Valid _policy_ values are: - -- `no` : Do not restart containers on exit -- `on-failure[:max_retries]` : Restart containers when they exit with a non-zero exit code, retrying indefinitely or until the optional *max_retries* count is hit -- `always` : Restart containers when they exit, regardless of status, retrying indefinitely -- `unless-stopped` : Identical to **always** - -Please note that restart will not restart containers after a system reboot. -If this functionality is required in your environment, you can invoke Podman from a **systemd.unit**(5) file, or create an init script for whichever init system is in use. -To generate systemd unit files, please see **podman generate systemd**. +@@option restart #### **--rm** @@ -641,17 +561,9 @@ Sets whether the signals sent to the **podman run** command are proxied to the c @@option stop-timeout -#### **--subgidname**=*name* +@@option subgidname -Run the container in a new user namespace using the map with _name_ in the _/etc/subgid_ file. -If calling **podman run** as an unprivileged user, the user needs to have the right to use the mapping. See **subgid**(5). -This flag conflicts with **--userns** and **--gidmap**. - -#### **--subuidname**=*name* - -Run the container in a new user namespace using the map with _name_ in the _/etc/subuid_ file. -If calling **podman run** as an unprivileged user, the user needs to have the right to use the mapping. See **subuid**(5). -This flag conflicts with **--userns** and **--uidmap**. +@@option subuidname #### **--sysctl**=*name=value* @@ -721,49 +633,7 @@ Without this argument, the command will run as the user specified in the contain When a user namespace is not in use, the UID and GID used within the container and on the host will match. When user namespaces are in use, however, the UID and GID in the container may correspond to another UID and GID on the host. In rootless containers, for example, a user namespace is always used, and root in the container will by default correspond to the UID and GID of the user invoking Podman. -#### **--userns**=*mode* - -Set the user namespace mode for the container. It defaults to the **PODMAN_USERNS** environment variable. An empty value ("") means user namespaces are disabled unless an explicit mapping is set with the **--uidmap** and **--gidmap** options. - -Rootless user --userns=Key mappings: - -Key | Host User | Container User -----------|---------------|--------------------- -"" |$UID |0 (Default User account mapped to root user in container.) -keep-id |$UID |$UID (Map user account to same UID within container.) -auto |$UID | nil (Host User UID is not mapped into container.) -nomap |$UID | nil (Host User UID is not mapped into container.) - -Valid _mode_ values are: - -**auto**[:_OPTIONS,..._]: automatically create a unique user namespace. - -The `--userns=auto` flag, requires that the user name `containers` and a range of subordinate user ids that the Podman container is allowed to use be specified in the /etc/subuid and /etc/subgid files. - -Example: `containers:2147483647:2147483648`. - -Podman allocates unique ranges of UIDs and GIDs from the `containers` subordinate user ids. The size of the ranges is based on the number of UIDs required in the image. The number of UIDs and GIDs can be overridden with the `size` option. - -The rootless option `--userns=keep-id` uses all the subuids and subgids of the user. Using `--userns=auto` when starting new containers will not work as long as any containers exist that were started with `--userns=keep-id`. - - Valid `auto` options: - - - *gidmapping*=_CONTAINER_GID:HOST_GID:SIZE_: to force a GID mapping to be present in the user namespace. - - *size*=_SIZE_: to specify an explicit size for the automatic user namespace. e.g. `--userns=auto:size=8192`. If `size` is not specified, `auto` will estimate a size for the user namespace. - - *uidmapping*=_CONTAINER_UID:HOST_UID:SIZE_: to force a UID mapping to be present in the user namespace. - -**container:**_id_: join the user namespace of the specified container. - -**host**: run in the user namespace of the caller. The processes running in the container will have the same privileges on the host as any other process launched by the calling user (default). - -**keep-id**: creates a user namespace where the current rootless user's UID:GID are mapped to the same values in the container. This option is not allowed for containers created by the root user. - -**nomap**: creates a user namespace where the current rootless user's UID:GID are not mapped into the container. This option is not allowed for containers created by the root user. - -**ns:**_namespace_: run the container in the given existing user namespace. - -**private**: create a new namespace for the container. -This option is incompatible with **--gidmap**, **--uidmap**, **--subuidname** and **--subgidname**. +@@option userns.container @@option uts.container diff --git a/docs/source/markdown/podman-update.1.md.in b/docs/source/markdown/podman-update.1.md.in new file mode 100644 index 000000000..2928379f3 --- /dev/null +++ b/docs/source/markdown/podman-update.1.md.in @@ -0,0 +1,78 @@ +% podman-update(1) + +## NAME +podman\-update - Updates the cgroup configuration of a given container + +## SYNOPSIS +**podman update** [*options*] *container* + +**podman container update** [*options*] *container* + +## DESCRIPTION + +Updates the cgroup configuration of an already existing container. The currently supported options are a subset of the +podman create/run resource limits options. These new options are non-persistent and only last for the current execution of the container; the configuration will be honored on its next run. +This means that this command can only be executed on an already running container and the changes made will be erased the next time the container is stopped and restarted, this is to ensure immutability. +This command takes one argument, a container name or ID, alongside the resource flags to modify the cgroup. + +## OPTIONS + +@@option blkio-weight + +@@option blkio-weight-device + +@@option cpu-period + +@@option cpu-quota + +@@option cpu-rt-period + +@@option cpu-rt-runtime + +@@option cpu-shares + +@@option cpus.container + +@@option cpuset-cpus + +@@option cpuset-mems + +@@option device-read-bps + +@@option device-read-iops + +@@option device-write-bps + +@@option device-write-iops + +@@option memory + +@@option memory-reservation + +@@option memory-swap + +@@option memory-swappiness + + +## EXAMPLEs + +update a container with a new cpu quota and period +``` +podman update --cpus=5 myCtr +``` + +update a container with all available options for cgroups v2 +``` +podman update --cpus 5 --cpuset-cpus 0 --cpu-shares 123 --cpuset-mems 0 --memory 1G --memory-swap 2G --memory-reservation 2G --blkio-weight-device /dev/zero:123 --blkio-weight 123 --device-read-bps /dev/zero:10mb --device-write-bps /dev/zero:10mb --device-read-iops /dev/zero:1000 --device-write-iops /dev/zero:1000 ctrID +``` + +update a container with all available options for cgroups v1 +``` +podman update --cpus 5 --cpuset-cpus 0 --cpu-shares 123 --cpuset-mems 0 --memory 1G --memory-swap 2G --memory-reservation 2G --memory-swappiness 50 ctrID +``` + +## SEE ALSO +**[podman(1)](podman.1.md)**, **[podman-create(1)](podman-create.1.md)**, **[podman-run(1)](podman-run.1.md)** + +## HISTORY +August 2022, Originally written by Charlie Doern <cdoern@redhat.com> diff --git a/docs/source/markdown/podman.1.md b/docs/source/markdown/podman.1.md index d1192b6d2..8c3af2561 100644 --- a/docs/source/markdown/podman.1.md +++ b/docs/source/markdown/podman.1.md @@ -355,6 +355,7 @@ the exit codes follow the `chroot` standard, see below: | [podman-unpause(1)](podman-unpause.1.md) | Unpause one or more containers. | | [podman-unshare(1)](podman-unshare.1.md) | Run a command inside of a modified user namespace. | | [podman-untag(1)](podman-untag.1.md) | Removes one or more names from a locally-stored image. | +| [podman-update(1)](podman-update.1.md) | Updates the cgroup configuration of a given container. | | [podman-version(1)](podman-version.1.md) | Display the Podman version information. | | [podman-volume(1)](podman-volume.1.md) | Simple management tool for volumes. | | [podman-wait(1)](podman-wait.1.md) | Wait on one or more containers to stop and print their exit codes. | @@ -12,7 +12,7 @@ require ( github.com/containernetworking/cni v1.1.2 github.com/containernetworking/plugins v1.1.1 github.com/containers/buildah v1.27.0 - github.com/containers/common v0.49.2-0.20220823130605-72a7da3358ac + github.com/containers/common v0.49.2-0.20220826180622-c2dcb4e70340 github.com/containers/conmon v2.0.20+incompatible github.com/containers/image/v5 v5.22.0 github.com/containers/ocicrypt v1.1.5 @@ -70,6 +70,4 @@ require ( gopkg.in/yaml.v3 v3.0.1 ) -require github.com/docker/libnetwork v0.8.0-dev.2.0.20190625141545-5a177b73e316 // indirect - replace github.com/opencontainers/runc => github.com/opencontainers/runc v1.1.1-0.20220617142545-8b9452f75cbc @@ -395,8 +395,8 @@ github.com/containernetworking/plugins v1.1.1/go.mod h1:Sr5TH/eBsGLXK/h71HeLfX19 github.com/containers/buildah v1.27.0 h1:LJ1ks7vKxwPzJGr5BWVvigbtVL9w7XeHtNEmiIOPJqI= github.com/containers/buildah v1.27.0/go.mod h1:anH3ExvDXRNP9zLQCrOc1vWb5CrhqLF/aYFim4tslvA= github.com/containers/common v0.49.1/go.mod h1:ueM5hT0itKqCQvVJDs+EtjornAQtrHYxQJzP2gxeGIg= -github.com/containers/common v0.49.2-0.20220823130605-72a7da3358ac h1:rLbTzosxPKrQd+EgMRxfC1WYm3azPiQfig+Lr7mCQ4k= -github.com/containers/common v0.49.2-0.20220823130605-72a7da3358ac/go.mod h1:xC4qkLfW9R+YSDknlT9xU+NDNxIw017U8AyohGtr9Ec= +github.com/containers/common v0.49.2-0.20220826180622-c2dcb4e70340 h1:Qg3LBb6sp5clQBF9OPqumvlCJGsMl6N2b5hEDttRbWA= +github.com/containers/common v0.49.2-0.20220826180622-c2dcb4e70340/go.mod h1:xC4qkLfW9R+YSDknlT9xU+NDNxIw017U8AyohGtr9Ec= github.com/containers/conmon v2.0.20+incompatible h1:YbCVSFSCqFjjVwHTPINGdMX1F6JXHGTUje2ZYobNrkg= github.com/containers/conmon v2.0.20+incompatible/go.mod h1:hgwZ2mtuDrppv78a/cOBNiCm6O0UMWGx1mu7P00nu5I= github.com/containers/image/v5 v5.22.0 h1:KemxPmD4D2YYOFZN2SgoTk7nBFcnwPiPW0MqjYtknSE= @@ -502,8 +502,6 @@ github.com/docker/go-plugins-helpers v0.0.0-20211224144127-6eecb7beb651 h1:YcvzL github.com/docker/go-plugins-helpers v0.0.0-20211224144127-6eecb7beb651/go.mod h1:LFyLie6XcDbyKGeVK6bHe+9aJTYCxWLBg5IrJZOaXKA= github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw= github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= -github.com/docker/libnetwork v0.8.0-dev.2.0.20190625141545-5a177b73e316 h1:moehPjPiGUaWdwgOl92xRyFHJyaqXDHcCyW9M6nmCK4= -github.com/docker/libnetwork v0.8.0-dev.2.0.20190625141545-5a177b73e316/go.mod h1:93m0aTqz6z+g32wla4l4WxTrdtvBRmVzYRkYvasA5Z8= github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1/go.mod h1:cyGadeNEkKy96OOhEzfZl+yxihPEzKnqJwvfuSUqbZE= github.com/docker/libtrust v0.0.0-20160708172513-aabc10ec26b7 h1:UhxFibDNY/bfvqU5CAUmr9zpesgbU6SWc8/B4mflAE4= github.com/docker/libtrust v0.0.0-20160708172513-aabc10ec26b7/go.mod h1:cyGadeNEkKy96OOhEzfZl+yxihPEzKnqJwvfuSUqbZE= diff --git a/libpod/container.go b/libpod/container.go index 6c05b1084..44a8669fd 100644 --- a/libpod/container.go +++ b/libpod/container.go @@ -237,6 +237,9 @@ type ContainerNamedVolume struct { Dest string `json:"dest"` // Options are fstab style mount options Options []string `json:"options,omitempty"` + // IsAnonymous sets the named volume as anonymous even if it has a name + // This is used for emptyDir volumes from a kube yaml + IsAnonymous bool `json:"setAnonymous,omitempty"` } // ContainerOverlayVolume is a overlay volume that will be mounted into the diff --git a/libpod/container_api.go b/libpod/container_api.go index 2ff4bfe08..f88e38ce1 100644 --- a/libpod/container_api.go +++ b/libpod/container_api.go @@ -16,6 +16,7 @@ import ( "github.com/containers/podman/v4/libpod/events" "github.com/containers/podman/v4/pkg/signal" "github.com/containers/storage/pkg/archive" + spec "github.com/opencontainers/runtime-spec/specs-go" "github.com/sirupsen/logrus" ) @@ -98,6 +99,15 @@ func (c *Container) Start(ctx context.Context, recursive bool) error { return c.start() } +// Update updates the given container. +// only the cgroup config can be updated and therefore only a linux resource spec is passed. +func (c *Container) Update(res *spec.LinuxResources) error { + if err := c.syncContainer(); err != nil { + return err + } + return c.update(res) +} + // StartAndAttach starts a container and attaches to it. // This acts as a combination of the Start and Attach APIs, ensuring proper // ordering of the two such that no output from the container is lost (e.g. the diff --git a/libpod/container_internal.go b/libpod/container_internal.go index 60fb29607..32674235a 100644 --- a/libpod/container_internal.go +++ b/libpod/container_internal.go @@ -27,6 +27,7 @@ import ( cutil "github.com/containers/common/pkg/util" "github.com/containers/podman/v4/libpod/define" "github.com/containers/podman/v4/libpod/events" + "github.com/containers/podman/v4/libpod/shutdown" "github.com/containers/podman/v4/pkg/ctime" "github.com/containers/podman/v4/pkg/lookup" "github.com/containers/podman/v4/pkg/rootless" @@ -1038,6 +1039,13 @@ func (c *Container) init(ctx context.Context, retainRetries bool) error { } } + // To ensure that we don't lose track of Conmon if hit by a SIGTERM + // in the middle of setting up the container, inhibit shutdown signals + // until after we save Conmon's PID to the state. + // TODO: This can likely be removed once conmon-rs support merges. + shutdown.Inhibit() + defer shutdown.Uninhibit() + // With the spec complete, do an OCI create if _, err = c.ociRuntime.CreateContainer(c, nil); err != nil { return err @@ -1073,6 +1081,7 @@ func (c *Container) init(ctx context.Context, retainRetries bool) error { if err := c.save(); err != nil { return err } + if c.config.HealthCheckConfig != nil { if err := c.createTimer(); err != nil { logrus.Error(err) @@ -2343,3 +2352,12 @@ func (c *Container) extractSecretToCtrStorage(secr *ContainerSecret) error { } return nil } + +// update calls the ociRuntime update function to modify a cgroup config after container creation +func (c *Container) update(resources *spec.LinuxResources) error { + if err := c.ociRuntime.UpdateContainer(c, resources); err != nil { + return err + } + logrus.Debugf("updated container %s", c.ID()) + return nil +} diff --git a/libpod/kube.go b/libpod/kube.go index a0fb52973..a70782d69 100644 --- a/libpod/kube.go +++ b/libpod/kube.go @@ -468,12 +468,15 @@ func newPodObject(podName string, annotations map[string]string, initCtrs, conta CreationTimestamp: v12.Now(), Annotations: annotations, } + // Set enableServiceLinks to false as podman doesn't use the service port environment variables + enableServiceLinks := false ps := v1.PodSpec{ - Containers: containers, - Hostname: hostname, - HostNetwork: hostNetwork, - InitContainers: initCtrs, - Volumes: volumes, + Containers: containers, + Hostname: hostname, + HostNetwork: hostNetwork, + InitContainers: initCtrs, + Volumes: volumes, + EnableServiceLinks: &enableServiceLinks, } if dnsOptions != nil && (len(dnsOptions.Nameservers)+len(dnsOptions.Searches)+len(dnsOptions.Options) > 0) { ps.DNSConfig = dnsOptions diff --git a/libpod/oci.go b/libpod/oci.go index 70053db1b..e5b9a0dcd 100644 --- a/libpod/oci.go +++ b/libpod/oci.go @@ -5,6 +5,7 @@ import ( "github.com/containers/common/pkg/resize" "github.com/containers/podman/v4/libpod/define" + "github.com/opencontainers/runtime-spec/specs-go" ) // OCIRuntime is an implementation of an OCI runtime. @@ -148,6 +149,9 @@ type OCIRuntime interface { // RuntimeInfo returns verbose information about the runtime. RuntimeInfo() (*define.ConmonInfo, *define.OCIRuntimeInfo, error) + + // UpdateContainer updates the given container's cgroup configuration. + UpdateContainer(ctr *Container, res *specs.LinuxResources) error } // AttachOptions are options used when attached to a container or an exec diff --git a/libpod/oci_conmon_common.go b/libpod/oci_conmon_common.go index b96f92d3a..cc65e1261 100644 --- a/libpod/oci_conmon_common.go +++ b/libpod/oci_conmon_common.go @@ -307,6 +307,52 @@ func (r *ConmonOCIRuntime) StartContainer(ctr *Container) error { return nil } +// UpdateContainer updates the given container's cgroup configuration +func (r *ConmonOCIRuntime) UpdateContainer(ctr *Container, resources *spec.LinuxResources) error { + runtimeDir, err := util.GetRuntimeDir() + if err != nil { + return err + } + env := []string{fmt.Sprintf("XDG_RUNTIME_DIR=%s", runtimeDir)} + if path, ok := os.LookupEnv("PATH"); ok { + env = append(env, fmt.Sprintf("PATH=%s", path)) + } + args := r.runtimeFlags + args = append(args, "update") + tempFile, additionalArgs, err := generateResourceFile(resources) + if err != nil { + return err + } + defer os.Remove(tempFile) + + args = append(args, additionalArgs...) + return utils.ExecCmdWithStdStreams(os.Stdin, os.Stdout, os.Stderr, env, r.path, append(args, ctr.ID())...) +} + +func generateResourceFile(res *spec.LinuxResources) (string, []string, error) { + flags := []string{} + if res == nil { + return "", flags, nil + } + + f, err := ioutil.TempFile("", "podman") + if err != nil { + return "", nil, err + } + + j, err := json.Marshal(res) + if err != nil { + return "", nil, err + } + _, err = f.WriteString(string(j)) + if err != nil { + return "", nil, err + } + + flags = append(flags, "--resources="+f.Name()) + return f.Name(), flags, nil +} + // KillContainer sends the given signal to the given container. // If all is set, send to all PIDs in the container. // All is only supported if the container created cgroups. diff --git a/libpod/oci_conmon_freebsd.go b/libpod/oci_conmon_freebsd.go index 6f7ac7fc6..d74f2af01 100644 --- a/libpod/oci_conmon_freebsd.go +++ b/libpod/oci_conmon_freebsd.go @@ -19,6 +19,9 @@ func (r *ConmonOCIRuntime) withContainerSocketLabel(ctr *Container, closure func // moveConmonToCgroupAndSignal gets a container's cgroupParent and moves the conmon process to that cgroup // it then signals for conmon to start by sending nonce data down the start fd func (r *ConmonOCIRuntime) moveConmonToCgroupAndSignal(ctr *Container, cmd *exec.Cmd, startFd *os.File) error { - // No equivalent on FreeBSD + // No equivalent to cgroup on FreeBSD, just signal conmon to start + if err := writeConmonPipeData(startFd); err != nil { + return err + } return nil } diff --git a/libpod/oci_missing.go b/libpod/oci_missing.go index 2ab2b4577..bbf2957ff 100644 --- a/libpod/oci_missing.go +++ b/libpod/oci_missing.go @@ -8,6 +8,7 @@ import ( "github.com/containers/common/pkg/resize" "github.com/containers/podman/v4/libpod/define" + spec "github.com/opencontainers/runtime-spec/specs-go" "github.com/sirupsen/logrus" ) @@ -80,6 +81,11 @@ func (r *MissingRuntime) StartContainer(ctr *Container) error { return r.printError() } +// UpdateContainer is not available as the runtime is missing +func (r *MissingRuntime) UpdateContainer(ctr *Container, resources *spec.LinuxResources) error { + return r.printError() +} + // KillContainer is not available as the runtime is missing // TODO: We could attempt to unix.Kill() the PID as recorded in the state if we // really want to smooth things out? Won't be perfect, but if the container has diff --git a/libpod/options.go b/libpod/options.go index d31741094..56d5265d2 100644 --- a/libpod/options.go +++ b/libpod/options.go @@ -1413,9 +1413,10 @@ func WithNamedVolumes(volumes []*ContainerNamedVolume) CtrCreateOption { } ctr.config.NamedVolumes = append(ctr.config.NamedVolumes, &ContainerNamedVolume{ - Name: vol.Name, - Dest: vol.Dest, - Options: mountOpts, + Name: vol.Name, + Dest: vol.Dest, + Options: mountOpts, + IsAnonymous: vol.IsAnonymous, }) } diff --git a/libpod/runtime_ctr.go b/libpod/runtime_ctr.go index 703ae5cbe..b43114fab 100644 --- a/libpod/runtime_ctr.go +++ b/libpod/runtime_ctr.go @@ -474,6 +474,11 @@ func (r *Runtime) setupContainer(ctx context.Context, ctr *Container) (_ *Contai return nil, fmt.Errorf("error retrieving named volume %s for new container: %w", vol.Name, err) } } + if vol.IsAnonymous { + // If SetAnonymous is true, make this an anonymous volume + // this is needed for emptyDir volumes from kube yamls + isAnonymous = true + } logrus.Debugf("Creating new volume %s for container", vol.Name) @@ -814,11 +819,11 @@ func (r *Runtime) removeContainer(ctx context.Context, c *Container, force, remo // Ignore error, since podman will report original error volumesFrom, _ := c.volumesFrom() if len(volumesFrom) > 0 { - logrus.Debugf("Cleaning up volume not possible since volume is in use (%s)", v) + logrus.Debugf("Cleaning up volume not possible since volume is in use (%s)", v.Name) continue } } - logrus.Errorf("Cleaning up volume (%s): %v", v, err) + logrus.Errorf("Cleaning up volume (%s): %v", v.Name, err) } } } @@ -968,7 +973,7 @@ func (r *Runtime) evictContainer(ctx context.Context, idOrName string, removeVol continue } if err := r.removeVolume(ctx, volume, false, timeout, false); err != nil && err != define.ErrNoSuchVolume && err != define.ErrVolumeBeingUsed { - logrus.Errorf("Cleaning up volume (%s): %v", v, err) + logrus.Errorf("Cleaning up volume (%s): %v", v.Name, err) } } } diff --git a/pkg/api/handlers/compat/containers.go b/pkg/api/handlers/compat/containers.go index 0b82c48f6..61d6fc86d 100644 --- a/pkg/api/handlers/compat/containers.go +++ b/pkg/api/handlers/compat/containers.go @@ -407,7 +407,7 @@ func convertSecondaryIPPrefixLen(input *define.InspectNetworkSettings, output *t } func LibpodToContainerJSON(l *libpod.Container, sz bool) (*types.ContainerJSON, error) { - _, imageName := l.Image() + imageID, imageName := l.Image() inspect, err := l.Inspect(sz) if err != nil { return nil, err @@ -488,7 +488,7 @@ func LibpodToContainerJSON(l *libpod.Container, sz bool) (*types.ContainerJSON, Path: inspect.Path, Args: inspect.Args, State: &state, - Image: imageName, + Image: "sha256:" + imageID, ResolvConfPath: inspect.ResolvConfPath, HostnamePath: inspect.HostnamePath, HostsPath: inspect.HostsPath, diff --git a/pkg/api/handlers/libpod/containers.go b/pkg/api/handlers/libpod/containers.go index 5d85d4009..d1460569f 100644 --- a/pkg/api/handlers/libpod/containers.go +++ b/pkg/api/handlers/libpod/containers.go @@ -1,6 +1,7 @@ package libpod import ( + "encoding/json" "errors" "fmt" "io/ioutil" @@ -10,6 +11,7 @@ import ( "github.com/containers/podman/v4/libpod" "github.com/containers/podman/v4/libpod/define" + "github.com/containers/podman/v4/pkg/api/handlers" "github.com/containers/podman/v4/pkg/api/handlers/compat" "github.com/containers/podman/v4/pkg/api/handlers/utils" api "github.com/containers/podman/v4/pkg/api/types" @@ -17,6 +19,7 @@ import ( "github.com/containers/podman/v4/pkg/domain/infra/abi" "github.com/containers/podman/v4/pkg/util" "github.com/gorilla/schema" + "github.com/opencontainers/runtime-spec/specs-go" "github.com/sirupsen/logrus" ) @@ -391,6 +394,28 @@ func InitContainer(w http.ResponseWriter, r *http.Request) { utils.WriteResponse(w, http.StatusNoContent, "") } +func UpdateContainer(w http.ResponseWriter, r *http.Request) { + name := utils.GetName(r) + runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) + ctr, err := runtime.LookupContainer(name) + if err != nil { + utils.ContainerNotFound(w, name, err) + return + } + + options := &handlers.UpdateEntities{Resources: &specs.LinuxResources{}} + if err := json.NewDecoder(r.Body).Decode(&options.Resources); err != nil { + utils.Error(w, http.StatusInternalServerError, fmt.Errorf("decode(): %w", err)) + return + } + err = ctr.Update(options.Resources) + if err != nil { + utils.InternalServerError(w, err) + return + } + utils.WriteResponse(w, http.StatusCreated, ctr.ID()) +} + func ShouldRestart(w http.ResponseWriter, r *http.Request) { runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) // Now use the ABI implementation to prevent us from having duplicate diff --git a/pkg/api/handlers/swagger/responses.go b/pkg/api/handlers/swagger/responses.go index 93a508b39..3de9b06e9 100644 --- a/pkg/api/handlers/swagger/responses.go +++ b/pkg/api/handlers/swagger/responses.go @@ -313,6 +313,11 @@ type containerCreateResponse struct { Body entities.ContainerCreateResponse } +type containerUpdateResponse struct { + // in:body + ID string +} + // Wait container // swagger:response type containerWaitResponse struct { diff --git a/pkg/api/handlers/types.go b/pkg/api/handlers/types.go index aab905878..bb416d9f4 100644 --- a/pkg/api/handlers/types.go +++ b/pkg/api/handlers/types.go @@ -11,6 +11,7 @@ import ( dockerContainer "github.com/docker/docker/api/types/container" dockerNetwork "github.com/docker/docker/api/types/network" "github.com/docker/go-connections/nat" + "github.com/opencontainers/runtime-spec/specs-go" ) type AuthConfig struct { @@ -64,6 +65,12 @@ type LibpodContainersRmReport struct { RmError string `json:"Err,omitempty"` } +// UpdateEntities used to wrap the oci resource spec in a swagger model +// swagger:model +type UpdateEntities struct { + Resources *specs.LinuxResources +} + type Info struct { docker.Info BuildahVersion string diff --git a/pkg/api/server/register_containers.go b/pkg/api/server/register_containers.go index 8aba4ea05..41baf5418 100644 --- a/pkg/api/server/register_containers.go +++ b/pkg/api/server/register_containers.go @@ -1626,5 +1626,33 @@ func (s *APIServer) registerContainersHandlers(r *mux.Router) error { // 500: // $ref: "#/responses/internalError" r.HandleFunc(VersionedPath("/libpod/containers/{name}/rename"), s.APIHandler(compat.RenameContainer)).Methods(http.MethodPost) + // swagger:operation POST /libpod/containers/{name}/update libpod ContainerUpdateLibpod + // --- + // tags: + // - containers + // summary: Update an existing containers cgroup configuration + // description: Update an existing containers cgroup configuration. + // parameters: + // - in: path + // name: name + // type: string + // required: true + // description: Full or partial ID or full name of the container to update + // - in: body + // name: resources + // description: attributes for updating the container + // schema: + // $ref: "#/definitions/UpdateEntities" + // produces: + // - application/json + // responses: + // responses: + // 201: + // $ref: "#/responses/containerUpdateResponse" + // 404: + // $ref: "#/responses/containerNotFound" + // 500: + // $ref: "#/responses/internalError" + r.HandleFunc(VersionedPath("/libpod/containers/{name}/update"), s.APIHandler(libpod.UpdateContainer)).Methods(http.MethodPost) return nil } diff --git a/pkg/bindings/containers/update.go b/pkg/bindings/containers/update.go new file mode 100644 index 000000000..7cda7c306 --- /dev/null +++ b/pkg/bindings/containers/update.go @@ -0,0 +1,31 @@ +package containers + +import ( + "context" + "net/http" + "strings" + + "github.com/containers/podman/v4/pkg/bindings" + "github.com/containers/podman/v4/pkg/domain/entities" + jsoniter "github.com/json-iterator/go" +) + +func Update(ctx context.Context, options *entities.ContainerUpdateOptions) (string, error) { + conn, err := bindings.GetClient(ctx) + if err != nil { + return "", err + } + + resources, err := jsoniter.MarshalToString(options.Specgen.ResourceLimits) + if err != nil { + return "", err + } + stringReader := strings.NewReader(resources) + response, err := conn.DoRequest(ctx, stringReader, http.MethodPost, "/containers/%s/update", nil, nil, options.NameOrID) + if err != nil { + return "", err + } + defer response.Body.Close() + + return options.NameOrID, response.Process(nil) +} diff --git a/pkg/bindings/images/images.go b/pkg/bindings/images/images.go index bb7867c4e..ea7d445db 100644 --- a/pkg/bindings/images/images.go +++ b/pkg/bindings/images/images.go @@ -282,9 +282,9 @@ func Search(ctx context.Context, term string, options *SearchOptions) ([]entitie } params.Set("term", term) - // Note: we have to verify if skipped is false. + // SkipTLSVerify is special. It's not being serialized by ToParams() + // because we need to flip the boolean. if options.SkipTLSVerify != nil { - params.Del("SkipTLSVerify") params.Set("tlsVerify", strconv.FormatBool(!options.GetSkipTLSVerify())) } diff --git a/pkg/bindings/images/pull.go b/pkg/bindings/images/pull.go index 109981c63..8caf45c0e 100644 --- a/pkg/bindings/images/pull.go +++ b/pkg/bindings/images/pull.go @@ -35,9 +35,9 @@ func Pull(ctx context.Context, rawImage string, options *PullOptions) ([]string, } params.Set("reference", rawImage) + // SkipTLSVerify is special. It's not being serialized by ToParams() + // because we need to flip the boolean. if options.SkipTLSVerify != nil { - params.Del("SkipTLSVerify") - // Note: we have to verify if skipped is false. params.Set("tlsVerify", strconv.FormatBool(!options.GetSkipTLSVerify())) } diff --git a/pkg/bindings/images/push.go b/pkg/bindings/images/push.go index f1e059f8c..0e1309e91 100644 --- a/pkg/bindings/images/push.go +++ b/pkg/bindings/images/push.go @@ -38,10 +38,9 @@ func Push(ctx context.Context, source string, destination string, options *PushO if err != nil { return err } - // SkipTLSVerify is special. We need to delete the param added by - // toparams and change the key and flip the bool + // SkipTLSVerify is special. It's not being serialized by ToParams() + // because we need to flip the boolean. if options.SkipTLSVerify != nil { - params.Del("SkipTLSVerify") params.Set("tlsVerify", strconv.FormatBool(!options.GetSkipTLSVerify())) } params.Set("destination", destination) diff --git a/pkg/bindings/images/types.go b/pkg/bindings/images/types.go index 3ecfb9e09..f8630926e 100644 --- a/pkg/bindings/images/types.go +++ b/pkg/bindings/images/types.go @@ -136,9 +136,9 @@ type PushOptions struct { // ProgressWriter is a writer where push progress are sent. // Since API handler for image push is quiet by default, WithQuiet(false) is necessary for // the writer to receive progress messages. - ProgressWriter *io.Writer + ProgressWriter *io.Writer `schema:"-"` // SkipTLSVerify to skip HTTPS and certificate verification. - SkipTLSVerify *bool + SkipTLSVerify *bool `schema:"-"` // RemoveSignatures Discard any pre-existing signatures in the image. RemoveSignatures *bool // Username for authenticating against the registry. @@ -158,7 +158,7 @@ type SearchOptions struct { // Limit the number of results. Limit *int // SkipTLSVerify to skip HTTPS and certificate verification. - SkipTLSVerify *bool + SkipTLSVerify *bool `schema:"-"` // ListTags search the available tags of the repository ListTags *bool } @@ -183,12 +183,12 @@ type PullOptions struct { // Password for authenticating against the registry. Password *string // ProgressWriter is a writer where pull progress are sent. - ProgressWriter *io.Writer + ProgressWriter *io.Writer `schema:"-"` // Quiet can be specified to suppress pull progress when pulling. Ignored // for remote calls. Quiet *bool // SkipTLSVerify to skip HTTPS and certificate verification. - SkipTLSVerify *bool + SkipTLSVerify *bool `schema:"-"` // Username for authenticating against the registry. Username *string // Variant will overwrite the local variant for image pulls. diff --git a/pkg/bindings/internal/util/util.go b/pkg/bindings/internal/util/util.go index f8f99d6c1..52ce14738 100644 --- a/pkg/bindings/internal/util/util.go +++ b/pkg/bindings/internal/util/util.go @@ -74,6 +74,9 @@ func ToParams(o interface{}) (url.Values, error) { } paramName := fieldName if pn, ok := sType.Field(i).Tag.Lookup("schema"); ok { + if pn == "-" { + continue + } paramName = pn } switch { diff --git a/pkg/bindings/kube/kube.go b/pkg/bindings/kube/kube.go index e727439cf..1b9f888ef 100644 --- a/pkg/bindings/kube/kube.go +++ b/pkg/bindings/kube/kube.go @@ -40,8 +40,10 @@ func PlayWithBody(ctx context.Context, body io.Reader, options *PlayOptions) (*e if err != nil { return nil, err } + // SkipTLSVerify is special. It's not being serialized by ToParams() + // because we need to flip the boolean. if options.SkipTLSVerify != nil { - params.Set("tlsVerify", strconv.FormatBool(options.GetSkipTLSVerify())) + params.Set("tlsVerify", strconv.FormatBool(!options.GetSkipTLSVerify())) } if options.Start != nil { params.Set("start", strconv.FormatBool(options.GetStart())) diff --git a/pkg/bindings/kube/types.go b/pkg/bindings/kube/types.go index 783d1912a..279a9f8f3 100644 --- a/pkg/bindings/kube/types.go +++ b/pkg/bindings/kube/types.go @@ -27,7 +27,7 @@ type PlayOptions struct { SignaturePolicy *string // SkipTLSVerify - skip https and certificate validation when // contacting container registries. - SkipTLSVerify *bool + SkipTLSVerify *bool `schema:"-"` // SeccompProfileRoot - path to a directory containing seccomp // profiles. SeccompProfileRoot *string diff --git a/pkg/bindings/manifests/manifests.go b/pkg/bindings/manifests/manifests.go index 0163d21a0..752366937 100644 --- a/pkg/bindings/manifests/manifests.go +++ b/pkg/bindings/manifests/manifests.go @@ -165,10 +165,9 @@ func Push(ctx context.Context, name, destination string, options *images.PushOpt if err != nil { return "", err } - // SkipTLSVerify is special. We need to delete the param added by - // ToParams() and change the key and flip the bool + // SkipTLSVerify is special. It's not being serialized by ToParams() + // because we need to flip the boolean. if options.SkipTLSVerify != nil { - params.Del("SkipTLSVerify") params.Set("tlsVerify", strconv.FormatBool(!options.GetSkipTLSVerify())) } @@ -246,10 +245,9 @@ func Modify(ctx context.Context, name string, images []string, options *ModifyOp if err != nil { return "", err } - // SkipTLSVerify is special. We need to delete the param added by - // ToParams() and change the key and flip the bool + // SkipTLSVerify is special. It's not being serialized by ToParams() + // because we need to flip the boolean. if options.SkipTLSVerify != nil { - params.Del("SkipTLSVerify") params.Set("tlsVerify", strconv.FormatBool(!options.GetSkipTLSVerify())) } diff --git a/pkg/bindings/manifests/types.go b/pkg/bindings/manifests/types.go index 5f2557fe1..fec3f9d13 100644 --- a/pkg/bindings/manifests/types.go +++ b/pkg/bindings/manifests/types.go @@ -32,7 +32,7 @@ type AddOptions struct { Authfile *string Password *string Username *string - SkipTLSVerify *bool + SkipTLSVerify *bool `schema:"-"` } //go:generate go run ../generator/generator.go RemoveOptions @@ -60,5 +60,5 @@ type ModifyOptions struct { Authfile *string Password *string Username *string - SkipTLSVerify *bool + SkipTLSVerify *bool `schema:"-"` } diff --git a/pkg/bindings/test/types_test.go b/pkg/bindings/test/types_test.go new file mode 100644 index 000000000..bc98c8b7d --- /dev/null +++ b/pkg/bindings/test/types_test.go @@ -0,0 +1,66 @@ +package bindings_test + +import ( + "bytes" + + "github.com/containers/podman/v4/pkg/bindings/images" + "github.com/containers/podman/v4/pkg/bindings/kube" + "github.com/containers/podman/v4/pkg/bindings/manifests" + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" +) + +var _ = Describe("Binding types", func() { + It("serialize image pull options", func() { + var writer bytes.Buffer + opts := new(images.PullOptions).WithOS("foo").WithProgressWriter(&writer).WithSkipTLSVerify(true) + params, err := opts.ToParams() + Expect(err).ToNot(HaveOccurred()) + Expect(params.Get("os")).To(Equal("foo")) + Expect(params.Has("progresswriter")).To(BeFalse()) + Expect(params.Has("skiptlsverify")).To(BeFalse()) + }) + + It("serialize image push options", func() { + var writer bytes.Buffer + opts := new(images.PushOptions).WithAll(true).WithProgressWriter(&writer).WithSkipTLSVerify(true) + params, err := opts.ToParams() + Expect(err).ToNot(HaveOccurred()) + Expect(params.Get("all")).To(Equal("true")) + Expect(params.Has("progresswriter")).To(BeFalse()) + Expect(params.Has("skiptlsverify")).To(BeFalse()) + }) + + It("serialize image search options", func() { + opts := new(images.SearchOptions).WithLimit(123).WithSkipTLSVerify(true) + params, err := opts.ToParams() + Expect(err).ToNot(HaveOccurred()) + Expect(params.Get("limit")).To(Equal("123")) + Expect(params.Has("skiptlsverify")).To(BeFalse()) + }) + + It("serialize manifest modify options", func() { + opts := new(manifests.ModifyOptions).WithOS("foo").WithSkipTLSVerify(true) + params, err := opts.ToParams() + Expect(err).ToNot(HaveOccurred()) + Expect(params.Get("os")).To(Equal("foo")) + Expect(params.Has("skiptlsverify")).To(BeFalse()) + }) + + It("serialize manifest add options", func() { + opts := new(manifests.AddOptions).WithAll(true).WithOS("foo").WithSkipTLSVerify(true) + params, err := opts.ToParams() + Expect(err).ToNot(HaveOccurred()) + Expect(params.Get("all")).To(Equal("true")) + Expect(params.Get("os")).To(Equal("foo")) + Expect(params.Has("skiptlsverify")).To(BeFalse()) + }) + + It("serialize kube play options", func() { + opts := new(kube.PlayOptions).WithQuiet(true).WithSkipTLSVerify(true) + params, err := opts.ToParams() + Expect(err).ToNot(HaveOccurred()) + Expect(params.Get("quiet")).To(Equal("true")) + Expect(params.Has("skiptlsverify")).To(BeFalse()) + }) +}) diff --git a/pkg/domain/entities/containers.go b/pkg/domain/entities/containers.go index 91ccdc2b2..47225f25c 100644 --- a/pkg/domain/entities/containers.go +++ b/pkg/domain/entities/containers.go @@ -495,3 +495,9 @@ type ContainerCloneOptions struct { Run bool Force bool } + +// ContainerUpdateOptions containers options for updating an existing containers cgroup configuration +type ContainerUpdateOptions struct { + NameOrID string + Specgen *specgen.SpecGenerator +} diff --git a/pkg/domain/entities/engine_container.go b/pkg/domain/entities/engine_container.go index 6a766eb84..69adc9732 100644 --- a/pkg/domain/entities/engine_container.go +++ b/pkg/domain/entities/engine_container.go @@ -51,6 +51,7 @@ type ContainerEngine interface { ContainerTop(ctx context.Context, options TopOptions) (*StringSliceReport, error) ContainerUnmount(ctx context.Context, nameOrIDs []string, options ContainerUnmountOptions) ([]*ContainerUnmountReport, error) ContainerUnpause(ctx context.Context, namesOrIds []string, options PauseUnPauseOptions) ([]*PauseUnpauseReport, error) + ContainerUpdate(ctx context.Context, options *ContainerUpdateOptions) (string, error) ContainerWait(ctx context.Context, namesOrIds []string, options WaitOptions) ([]WaitReport, error) Diff(ctx context.Context, namesOrIds []string, options DiffOptions) (*DiffReport, error) Events(ctx context.Context, opts EventsOptions) error diff --git a/pkg/domain/entities/pods.go b/pkg/domain/entities/pods.go index 33ca2c807..b672434d8 100644 --- a/pkg/domain/entities/pods.go +++ b/pkg/domain/entities/pods.go @@ -164,6 +164,15 @@ type PodCloneOptions struct { Start bool } +type ContainerMode string + +const ( + InfraMode = ContainerMode("infra") + CloneMode = ContainerMode("clone") + UpdateMode = ContainerMode("update") + CreateMode = ContainerMode("create") +) + type ContainerCreateOptions struct { Annotation []string Attach []string diff --git a/pkg/domain/infra/abi/containers.go b/pkg/domain/infra/abi/containers.go index 0a8e5bc2f..dfa3c5ba0 100644 --- a/pkg/domain/infra/abi/containers.go +++ b/pkg/domain/infra/abi/containers.go @@ -1715,3 +1715,27 @@ func (ic *ContainerEngine) ContainerClone(ctx context.Context, ctrCloneOpts enti return &entities.ContainerCreateReport{Id: ctr.ID()}, nil } + +// ContainerUpdate finds and updates the given container's cgroup config with the specified options +func (ic *ContainerEngine) ContainerUpdate(ctx context.Context, updateOptions *entities.ContainerUpdateOptions) (string, error) { + err := specgen.WeightDevices(updateOptions.Specgen) + if err != nil { + return "", err + } + err = specgen.FinishThrottleDevices(updateOptions.Specgen) + if err != nil { + return "", err + } + ctrs, err := getContainersByContext(false, false, []string{updateOptions.NameOrID}, ic.Libpod) + if err != nil { + return "", err + } + if len(ctrs) != 1 { + return "", fmt.Errorf("container not found") + } + + if err = ctrs[0].Update(updateOptions.Specgen.ResourceLimits); err != nil { + return "", err + } + return ctrs[0].ID(), nil +} diff --git a/pkg/domain/infra/abi/play.go b/pkg/domain/infra/abi/play.go index faa89cc26..6ea20a4f2 100644 --- a/pkg/domain/infra/abi/play.go +++ b/pkg/domain/infra/abi/play.go @@ -436,7 +436,7 @@ func (ic *ContainerEngine) playKubePod(ctx context.Context, podName string, podY } // Go through the volumes and create a podman volume for all volumes that have been - // defined by a configmap + // defined by a configmap or secret for _, v := range volumes { if (v.Type == kube.KubeVolumeTypeConfigMap || v.Type == kube.KubeVolumeTypeSecret) && !v.Optional { vol, err := ic.Libpod.NewVolume(ctx, libpod.WithVolumeName(v.Source)) diff --git a/pkg/domain/infra/runtime_libpod.go b/pkg/domain/infra/runtime_libpod.go index f76fab4ea..a23a23653 100644 --- a/pkg/domain/infra/runtime_libpod.go +++ b/pkg/domain/infra/runtime_libpod.go @@ -294,57 +294,6 @@ func ParseIDMapping(mode namespaces.UsernsMode, uidMapSlice, gidMapSlice []strin options.AutoUserNsOpts = *opts return &options, nil } - if mode.IsKeepID() { - if len(uidMapSlice) > 0 || len(gidMapSlice) > 0 { - return nil, errors.New("cannot specify custom mappings with --userns=keep-id") - } - if len(subUIDMap) > 0 || len(subGIDMap) > 0 { - return nil, errors.New("cannot specify subuidmap or subgidmap with --userns=keep-id") - } - if !rootless.IsRootless() { - return nil, errors.New("keep-id is only supported in rootless mode") - } - min := func(a, b int) int { - if a < b { - return a - } - return b - } - - uid := rootless.GetRootlessUID() - gid := rootless.GetRootlessGID() - - uids, gids, err := rootless.GetConfiguredMappings() - if err != nil { - return nil, fmt.Errorf("cannot read mappings: %w", err) - } - maxUID, maxGID := 0, 0 - for _, u := range uids { - maxUID += u.Size - } - for _, g := range gids { - maxGID += g.Size - } - - options.UIDMap, options.GIDMap = nil, nil - - options.UIDMap = append(options.UIDMap, idtools.IDMap{ContainerID: 0, HostID: 1, Size: min(uid, maxUID)}) - options.UIDMap = append(options.UIDMap, idtools.IDMap{ContainerID: uid, HostID: 0, Size: 1}) - if maxUID > uid { - options.UIDMap = append(options.UIDMap, idtools.IDMap{ContainerID: uid + 1, HostID: uid + 1, Size: maxUID - uid}) - } - - options.GIDMap = append(options.GIDMap, idtools.IDMap{ContainerID: 0, HostID: 1, Size: min(gid, maxGID)}) - options.GIDMap = append(options.GIDMap, idtools.IDMap{ContainerID: gid, HostID: 0, Size: 1}) - if maxGID > gid { - options.GIDMap = append(options.GIDMap, idtools.IDMap{ContainerID: gid + 1, HostID: gid + 1, Size: maxGID - gid}) - } - - options.HostUIDMapping = false - options.HostGIDMapping = false - // Simply ignore the setting and do not set up an inner namespace for root as it is a no-op - return &options, nil - } if subGIDMap == "" && subUIDMap != "" { subGIDMap = subUIDMap diff --git a/pkg/domain/infra/tunnel/containers.go b/pkg/domain/infra/tunnel/containers.go index 023bee430..68ca788b8 100644 --- a/pkg/domain/infra/tunnel/containers.go +++ b/pkg/domain/infra/tunnel/containers.go @@ -1024,3 +1024,16 @@ func (ic *ContainerEngine) ContainerRename(ctx context.Context, nameOrID string, func (ic *ContainerEngine) ContainerClone(ctx context.Context, ctrCloneOpts entities.ContainerCloneOptions) (*entities.ContainerCreateReport, error) { return nil, errors.New("cloning a container is not supported on the remote client") } + +// ContainerUpdate finds and updates the given container's cgroup config with the specified options +func (ic *ContainerEngine) ContainerUpdate(ctx context.Context, updateOptions *entities.ContainerUpdateOptions) (string, error) { + err := specgen.WeightDevices(updateOptions.Specgen) + if err != nil { + return "", err + } + err = specgen.FinishThrottleDevices(updateOptions.Specgen) + if err != nil { + return "", err + } + return containers.Update(ic.ClientCtx, updateOptions) +} diff --git a/pkg/k8s.io/api/core/v1/types.go b/pkg/k8s.io/api/core/v1/types.go index 384965769..d47178878 100644 --- a/pkg/k8s.io/api/core/v1/types.go +++ b/pkg/k8s.io/api/core/v1/types.go @@ -58,6 +58,10 @@ type VolumeSource struct { ConfigMap *ConfigMapVolumeSource `json:"configMap,omitempty"` // Secret represents a secret that should be mounted as a volume Secret *SecretVolumeSource `json:"secret,omitempty"` + // emptyDir represents a temporary directory that shares a pod's lifetime. + // More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir + // +optional + EmptyDir *EmptyDirVolumeSource `json:"emptyDir,omitempty"` } // PersistentVolumeClaimVolumeSource references the user's PVC in the same namespace. diff --git a/pkg/machine/e2e/config_init_test.go b/pkg/machine/e2e/config_init_test.go index d6c7990b0..305d101a3 100644 --- a/pkg/machine/e2e/config_init_test.go +++ b/pkg/machine/e2e/config_init_test.go @@ -9,6 +9,7 @@ type initMachine struct { --cpus uint Number of CPUs (default 1) --disk-size uint Disk size in GB (default 100) --ignition-path string Path to ignition file + --username string Username of the remote user (default "core" for FCOS, "user" for Fedora) --image-path string Path to qcow image (default "testing") -m, --memory uint Memory in MB (default 2048) --now Start machine now @@ -21,6 +22,7 @@ type initMachine struct { cpus *uint diskSize *uint ignitionPath string + username string imagePath string memory *uint now bool @@ -42,6 +44,9 @@ func (i *initMachine) buildCmd(m *machineTestBuilder) []string { if l := len(i.ignitionPath); l > 0 { cmd = append(cmd, "--ignition-path", i.ignitionPath) } + if l := len(i.username); l > 0 { + cmd = append(cmd, "--username", i.username) + } if l := len(i.imagePath); l > 0 { cmd = append(cmd, "--image-path", i.imagePath) } @@ -76,6 +81,11 @@ func (i *initMachine) withIgnitionPath(path string) *initMachine { //nolint:unus return i } +func (i *initMachine) withUsername(username string) *initMachine { + i.username = username + return i +} + func (i *initMachine) withImagePath(path string) *initMachine { i.imagePath = path return i diff --git a/pkg/machine/e2e/init_test.go b/pkg/machine/e2e/init_test.go index 859a3ca46..c298d3b14 100644 --- a/pkg/machine/e2e/init_test.go +++ b/pkg/machine/e2e/init_test.go @@ -77,6 +77,26 @@ var _ = Describe("podman machine init", func() { Expect(inspectAfter[0].State).To(Equal(machine.Running)) }) + It("simple init with username", func() { + i := new(initMachine) + remoteUsername := "remoteuser" + session, err := mb.setCmd(i.withImagePath(mb.imagePath).withUsername(remoteUsername)).run() + Expect(err).To(BeNil()) + Expect(session).To(Exit(0)) + + inspectBefore, ec, err := mb.toQemuInspectInfo() + Expect(err).To(BeNil()) + Expect(ec).To(BeZero()) + + Expect(len(inspectBefore)).To(BeNumerically(">", 0)) + testMachine := inspectBefore[0] + Expect(testMachine.Name).To(Equal(mb.names[0])) + Expect(testMachine.Resources.CPUs).To(Equal(uint64(1))) + Expect(testMachine.Resources.Memory).To(Equal(uint64(2048))) + Expect(testMachine.SSHConfig.RemoteUsername).To((Equal(remoteUsername))) + + }) + It("machine init with cpus, disk size, memory, timezone", func() { name := randomString() i := new(initMachine) diff --git a/pkg/namespaces/namespaces.go b/pkg/namespaces/namespaces.go index 8eacb8da7..6dd576ea5 100644 --- a/pkg/namespaces/namespaces.go +++ b/pkg/namespaces/namespaces.go @@ -21,6 +21,14 @@ const ( slirpType = "slirp4netns" ) +// KeepIDUserNsOptions defines how to keepIDmatically create a user namespace. +type KeepIDUserNsOptions struct { + // UID is the target uid in the user namespace. + UID *uint32 + // GID is the target uid in the user namespace. + GID *uint32 +} + // CgroupMode represents cgroup mode in the container. type CgroupMode string @@ -93,7 +101,8 @@ func (n UsernsMode) IsHost() bool { // IsKeepID indicates whether container uses a mapping where the (uid, gid) on the host is kept inside of the namespace. func (n UsernsMode) IsKeepID() bool { - return n == "keep-id" + parts := strings.Split(string(n), ":") + return parts[0] == "keep-id" } // IsNoMap indicates whether container uses a mapping where the (uid, gid) on the host is not present in the namespace. @@ -154,6 +163,44 @@ func (n UsernsMode) GetAutoOptions() (*types.AutoUserNsOptions, error) { return &options, nil } +// GetKeepIDOptions returns a KeepIDUserNsOptions with the settings to keepIDmatically set up +// a user namespace. +func (n UsernsMode) GetKeepIDOptions() (*KeepIDUserNsOptions, error) { + parts := strings.SplitN(string(n), ":", 2) + if parts[0] != "keep-id" { + return nil, fmt.Errorf("wrong user namespace mode") + } + options := KeepIDUserNsOptions{} + if len(parts) == 1 { + return &options, nil + } + for _, o := range strings.Split(parts[1], ",") { + v := strings.SplitN(o, "=", 2) + if len(v) != 2 { + return nil, fmt.Errorf("invalid option specified: %q", o) + } + switch v[0] { + case "uid": + s, err := strconv.ParseUint(v[1], 10, 32) + if err != nil { + return nil, err + } + v := uint32(s) + options.UID = &v + case "gid": + s, err := strconv.ParseUint(v[1], 10, 32) + if err != nil { + return nil, err + } + v := uint32(s) + options.GID = &v + default: + return nil, fmt.Errorf("unknown option specified: %q", v[0]) + } + } + return &options, nil +} + // IsPrivate indicates whether the container uses the a private userns. func (n UsernsMode) IsPrivate() bool { return !(n.IsHost() || n.IsContainer()) diff --git a/pkg/specgen/generate/config_unsupported.go b/pkg/specgen/generate/config_unsupported.go deleted file mode 100644 index a97ae0709..000000000 --- a/pkg/specgen/generate/config_unsupported.go +++ /dev/null @@ -1,29 +0,0 @@ -//go:build !linux -// +build !linux - -package generate - -import ( - "errors" - - "github.com/containers/common/libimage" - "github.com/containers/podman/v4/pkg/specgen" - spec "github.com/opencontainers/runtime-spec/specs-go" - "github.com/opencontainers/runtime-tools/generate" -) - -// DevicesFromPath computes a list of devices -func DevicesFromPath(g *generate.Generator, devicePath string) error { - return errors.New("unsupported DevicesFromPath") -} - -func BlockAccessToKernelFilesystems(privileged, pidModeIsHost bool, mask, unmask []string, g *generate.Generator) { -} - -func supportAmbientCapabilities() bool { - return false -} - -func getSeccompConfig(s *specgen.SpecGenerator, configSpec *spec.Spec, img *libimage.Image) (*spec.LinuxSeccomp, error) { - return nil, errors.New("not implemented getSeccompConfig") -} diff --git a/pkg/specgen/generate/container.go b/pkg/specgen/generate/container.go index d57efa0d1..46b7a2dc2 100644 --- a/pkg/specgen/generate/container.go +++ b/pkg/specgen/generate/container.go @@ -21,7 +21,6 @@ import ( spec "github.com/opencontainers/runtime-spec/specs-go" "github.com/openshift/imagebuilder" "github.com/sirupsen/logrus" - "golang.org/x/sys/unix" ) func getImageFromSpec(ctx context.Context, r *libpod.Runtime, s *specgen.SpecGenerator) (*libimage.Image, string, *libimage.ImageData, error) { @@ -518,75 +517,6 @@ func mapSecurityConfig(c *libpod.ContainerConfig, s *specgen.SpecGenerator) { s.HostUsers = c.HostUsers } -// FinishThrottleDevices takes the temporary representation of the throttle -// devices in the specgen and looks up the major and major minors. it then -// sets the throttle devices proper in the specgen -func FinishThrottleDevices(s *specgen.SpecGenerator) error { - if s.ResourceLimits == nil { - s.ResourceLimits = &spec.LinuxResources{} - } - if bps := s.ThrottleReadBpsDevice; len(bps) > 0 { - if s.ResourceLimits.BlockIO == nil { - s.ResourceLimits.BlockIO = &spec.LinuxBlockIO{} - } - for k, v := range bps { - statT := unix.Stat_t{} - if err := unix.Stat(k, &statT); err != nil { - return fmt.Errorf("could not parse throttle device at %s: %w", k, err) - } - v.Major = (int64(unix.Major(uint64(statT.Rdev)))) //nolint: unconvert - v.Minor = (int64(unix.Minor(uint64(statT.Rdev)))) //nolint: unconvert - if s.ResourceLimits.BlockIO == nil { - s.ResourceLimits.BlockIO = new(spec.LinuxBlockIO) - } - s.ResourceLimits.BlockIO.ThrottleReadBpsDevice = append(s.ResourceLimits.BlockIO.ThrottleReadBpsDevice, v) - } - } - if bps := s.ThrottleWriteBpsDevice; len(bps) > 0 { - if s.ResourceLimits.BlockIO == nil { - s.ResourceLimits.BlockIO = &spec.LinuxBlockIO{} - } - for k, v := range bps { - statT := unix.Stat_t{} - if err := unix.Stat(k, &statT); err != nil { - return fmt.Errorf("could not parse throttle device at %s: %w", k, err) - } - v.Major = (int64(unix.Major(uint64(statT.Rdev)))) //nolint: unconvert - v.Minor = (int64(unix.Minor(uint64(statT.Rdev)))) //nolint: unconvert - s.ResourceLimits.BlockIO.ThrottleWriteBpsDevice = append(s.ResourceLimits.BlockIO.ThrottleWriteBpsDevice, v) - } - } - if iops := s.ThrottleReadIOPSDevice; len(iops) > 0 { - if s.ResourceLimits.BlockIO == nil { - s.ResourceLimits.BlockIO = &spec.LinuxBlockIO{} - } - for k, v := range iops { - statT := unix.Stat_t{} - if err := unix.Stat(k, &statT); err != nil { - return fmt.Errorf("could not parse throttle device at %s: %w", k, err) - } - v.Major = (int64(unix.Major(uint64(statT.Rdev)))) //nolint: unconvert - v.Minor = (int64(unix.Minor(uint64(statT.Rdev)))) //nolint: unconvert - s.ResourceLimits.BlockIO.ThrottleReadIOPSDevice = append(s.ResourceLimits.BlockIO.ThrottleReadIOPSDevice, v) - } - } - if iops := s.ThrottleWriteIOPSDevice; len(iops) > 0 { - if s.ResourceLimits.BlockIO == nil { - s.ResourceLimits.BlockIO = &spec.LinuxBlockIO{} - } - for k, v := range iops { - statT := unix.Stat_t{} - if err := unix.Stat(k, &statT); err != nil { - return fmt.Errorf("could not parse throttle device at %s: %w", k, err) - } - v.Major = (int64(unix.Major(uint64(statT.Rdev)))) //nolint: unconvert - v.Minor = (int64(unix.Minor(uint64(statT.Rdev)))) //nolint: unconvert - s.ResourceLimits.BlockIO.ThrottleWriteIOPSDevice = append(s.ResourceLimits.BlockIO.ThrottleWriteIOPSDevice, v) - } - } - return nil -} - // Check name looks for existing containers/pods with the same name, and modifies the given string until a new name is found func CheckName(rt *libpod.Runtime, n string, kind bool) string { switch { diff --git a/pkg/specgen/generate/container_create.go b/pkg/specgen/generate/container_create.go index e9cec2873..4d5ac22ad 100644 --- a/pkg/specgen/generate/container_create.go +++ b/pkg/specgen/generate/container_create.go @@ -56,7 +56,7 @@ func MakeContainer(ctx context.Context, rt *libpod.Runtime, s *specgen.SpecGener } } - if err := FinishThrottleDevices(s); err != nil { + if err := specgen.FinishThrottleDevices(s); err != nil { return nil, nil, nil, err } @@ -387,9 +387,10 @@ func createContainerOptions(rt *libpod.Runtime, s *specgen.SpecGenerator, pod *l var vols []*libpod.ContainerNamedVolume for _, v := range volumes { vols = append(vols, &libpod.ContainerNamedVolume{ - Name: v.Name, - Dest: v.Dest, - Options: v.Options, + Name: v.Name, + Dest: v.Dest, + Options: v.Options, + IsAnonymous: v.IsAnonymous, }) } options = append(options, libpod.WithNamedVolumes(vols)) diff --git a/pkg/specgen/generate/kube/kube.go b/pkg/specgen/generate/kube/kube.go index e9abf419b..375b719d3 100644 --- a/pkg/specgen/generate/kube/kube.go +++ b/pkg/specgen/generate/kube/kube.go @@ -406,8 +406,15 @@ func ToSpecGen(ctx context.Context, opts *CtrSpecGenOptions) (*specgen.SpecGener Name: volumeSource.Source, Options: options, } - s.Volumes = append(s.Volumes, &secretVolume) + case KubeVolumeTypeEmptyDir: + emptyDirVolume := specgen.NamedVolume{ + Dest: volume.MountPath, + Name: volumeSource.Source, + Options: options, + IsAnonymous: true, + } + s.Volumes = append(s.Volumes, &emptyDirVolume) default: return nil, errors.New("unsupported volume source type") } diff --git a/pkg/specgen/generate/kube/volume.go b/pkg/specgen/generate/kube/volume.go index c12adadd8..230521ec6 100644 --- a/pkg/specgen/generate/kube/volume.go +++ b/pkg/specgen/generate/kube/volume.go @@ -32,6 +32,7 @@ const ( KubeVolumeTypeBlockDevice KubeVolumeTypeCharDevice KubeVolumeTypeSecret + KubeVolumeTypeEmptyDir ) //nolint:revive @@ -219,8 +220,13 @@ func VolumeFromConfigMap(configMapVolumeSource *v1.ConfigMapVolumeSource, config return kv, nil } +// Create a kubeVolume for an emptyDir volume +func VolumeFromEmptyDir(emptyDirVolumeSource *v1.EmptyDirVolumeSource, name string) (*KubeVolume, error) { + return &KubeVolume{Type: KubeVolumeTypeEmptyDir, Source: name}, nil +} + // Create a KubeVolume from one of the supported VolumeSource -func VolumeFromSource(volumeSource v1.VolumeSource, configMaps []v1.ConfigMap, secretsManager *secrets.SecretsManager) (*KubeVolume, error) { +func VolumeFromSource(volumeSource v1.VolumeSource, configMaps []v1.ConfigMap, secretsManager *secrets.SecretsManager, volName string) (*KubeVolume, error) { switch { case volumeSource.HostPath != nil: return VolumeFromHostPath(volumeSource.HostPath) @@ -230,8 +236,10 @@ func VolumeFromSource(volumeSource v1.VolumeSource, configMaps []v1.ConfigMap, s return VolumeFromConfigMap(volumeSource.ConfigMap, configMaps) case volumeSource.Secret != nil: return VolumeFromSecret(volumeSource.Secret, secretsManager) + case volumeSource.EmptyDir != nil: + return VolumeFromEmptyDir(volumeSource.EmptyDir, volName) default: - return nil, errors.New("HostPath, ConfigMap, and PersistentVolumeClaim are currently the only supported VolumeSource") + return nil, errors.New("HostPath, ConfigMap, EmptyDir, and PersistentVolumeClaim are currently the only supported VolumeSource") } } @@ -240,7 +248,7 @@ func InitializeVolumes(specVolumes []v1.Volume, configMaps []v1.ConfigMap, secre volumes := make(map[string]*KubeVolume) for _, specVolume := range specVolumes { - volume, err := VolumeFromSource(specVolume.VolumeSource, configMaps, secretsManager) + volume, err := VolumeFromSource(specVolume.VolumeSource, configMaps, secretsManager, specVolume.Name) if err != nil { return nil, fmt.Errorf("failed to create volume %q: %w", specVolume.Name, err) } diff --git a/pkg/specgen/generate/namespaces.go b/pkg/specgen/generate/namespaces.go index f0d4e9153..f57b6c23c 100644 --- a/pkg/specgen/generate/namespaces.go +++ b/pkg/specgen/generate/namespaces.go @@ -3,7 +3,6 @@ package generate import ( "errors" "fmt" - "os" "strings" "github.com/containers/common/libimage" @@ -11,11 +10,11 @@ import ( "github.com/containers/common/pkg/config" "github.com/containers/podman/v4/libpod" "github.com/containers/podman/v4/libpod/define" + "github.com/containers/podman/v4/pkg/namespaces" "github.com/containers/podman/v4/pkg/rootless" "github.com/containers/podman/v4/pkg/specgen" "github.com/containers/podman/v4/pkg/util" spec "github.com/opencontainers/runtime-spec/specs-go" - "github.com/opencontainers/runtime-tools/generate" "github.com/sirupsen/logrus" ) @@ -198,12 +197,18 @@ func namespaceOptions(s *specgen.SpecGenerator, rt *libpod.Runtime, pod *libpod. if !rootless.IsRootless() { return nil, errors.New("keep-id is only supported in rootless mode") } - toReturn = append(toReturn, libpod.WithAddCurrentUserPasswdEntry()) + opts, err := namespaces.UsernsMode(s.UserNS.String()).GetKeepIDOptions() + if err != nil { + return nil, err + } + if opts.UID == nil && opts.GID == nil { + toReturn = append(toReturn, libpod.WithAddCurrentUserPasswdEntry()) + } // If user is not overridden, set user in the container // to user running Podman. if s.User == "" { - _, uid, gid, err := util.GetKeepIDMapping() + _, uid, gid, err := util.GetKeepIDMapping(opts) if err != nil { return nil, err } @@ -357,153 +362,6 @@ func namespaceOptions(s *specgen.SpecGenerator, rt *libpod.Runtime, pod *libpod. return toReturn, nil } -func specConfigureNamespaces(s *specgen.SpecGenerator, g *generate.Generator, rt *libpod.Runtime, pod *libpod.Pod) error { - // PID - switch s.PidNS.NSMode { - case specgen.Path: - if _, err := os.Stat(s.PidNS.Value); err != nil { - return fmt.Errorf("cannot find specified PID namespace path: %w", err) - } - if err := g.AddOrReplaceLinuxNamespace(string(spec.PIDNamespace), s.PidNS.Value); err != nil { - return err - } - case specgen.Host: - if err := g.RemoveLinuxNamespace(string(spec.PIDNamespace)); err != nil { - return err - } - case specgen.Private: - if err := g.AddOrReplaceLinuxNamespace(string(spec.PIDNamespace), ""); err != nil { - return err - } - } - - // IPC - switch s.IpcNS.NSMode { - case specgen.Path: - if _, err := os.Stat(s.IpcNS.Value); err != nil { - return fmt.Errorf("cannot find specified IPC namespace path: %w", err) - } - if err := g.AddOrReplaceLinuxNamespace(string(spec.IPCNamespace), s.IpcNS.Value); err != nil { - return err - } - case specgen.Host: - if err := g.RemoveLinuxNamespace(string(spec.IPCNamespace)); err != nil { - return err - } - case specgen.Private: - if err := g.AddOrReplaceLinuxNamespace(string(spec.IPCNamespace), ""); err != nil { - return err - } - } - - // UTS - switch s.UtsNS.NSMode { - case specgen.Path: - if _, err := os.Stat(s.UtsNS.Value); err != nil { - return fmt.Errorf("cannot find specified UTS namespace path: %w", err) - } - if err := g.AddOrReplaceLinuxNamespace(string(spec.UTSNamespace), s.UtsNS.Value); err != nil { - return err - } - case specgen.Host: - if err := g.RemoveLinuxNamespace(string(spec.UTSNamespace)); err != nil { - return err - } - case specgen.Private: - if err := g.AddOrReplaceLinuxNamespace(string(spec.UTSNamespace), ""); err != nil { - return err - } - } - - hostname := s.Hostname - if hostname == "" { - switch { - case s.UtsNS.NSMode == specgen.FromPod: - hostname = pod.Hostname() - case s.UtsNS.NSMode == specgen.FromContainer: - utsCtr, err := rt.LookupContainer(s.UtsNS.Value) - if err != nil { - return fmt.Errorf("error looking up container to share uts namespace with: %w", err) - } - hostname = utsCtr.Hostname() - case (s.NetNS.NSMode == specgen.Host && hostname == "") || s.UtsNS.NSMode == specgen.Host: - tmpHostname, err := os.Hostname() - if err != nil { - return fmt.Errorf("unable to retrieve hostname of the host: %w", err) - } - hostname = tmpHostname - default: - logrus.Debug("No hostname set; container's hostname will default to runtime default") - } - } - - g.RemoveHostname() - if s.Hostname != "" || s.UtsNS.NSMode != specgen.Host { - // Set the hostname in the OCI configuration only if specified by - // the user or if we are creating a new UTS namespace. - // TODO: Should we be doing this for pod or container shared - // namespaces? - g.SetHostname(hostname) - } - if _, ok := s.Env["HOSTNAME"]; !ok && s.Hostname != "" { - g.AddProcessEnv("HOSTNAME", hostname) - } - - // User - if _, err := specgen.SetupUserNS(s.IDMappings, s.UserNS, g); err != nil { - return err - } - - // Cgroup - switch s.CgroupNS.NSMode { - case specgen.Path: - if _, err := os.Stat(s.CgroupNS.Value); err != nil { - return fmt.Errorf("cannot find specified cgroup namespace path: %w", err) - } - if err := g.AddOrReplaceLinuxNamespace(string(spec.CgroupNamespace), s.CgroupNS.Value); err != nil { - return err - } - case specgen.Host: - if err := g.RemoveLinuxNamespace(string(spec.CgroupNamespace)); err != nil { - return err - } - case specgen.Private: - if err := g.AddOrReplaceLinuxNamespace(string(spec.CgroupNamespace), ""); err != nil { - return err - } - } - - // Net - switch s.NetNS.NSMode { - case specgen.Path: - if _, err := os.Stat(s.NetNS.Value); err != nil { - return fmt.Errorf("cannot find specified network namespace path: %w", err) - } - if err := g.AddOrReplaceLinuxNamespace(string(spec.NetworkNamespace), s.NetNS.Value); err != nil { - return err - } - case specgen.Host: - if err := g.RemoveLinuxNamespace(string(spec.NetworkNamespace)); err != nil { - return err - } - case specgen.Private, specgen.NoNetwork: - if err := g.AddOrReplaceLinuxNamespace(string(spec.NetworkNamespace), ""); err != nil { - return err - } - } - - if g.Config.Annotations == nil { - g.Config.Annotations = make(map[string]string) - } - if s.PublishExposedPorts { - g.Config.Annotations[define.InspectAnnotationPublishAll] = define.InspectResponseTrue - } else { - g.Config.Annotations[define.InspectAnnotationPublishAll] = define.InspectResponseFalse - } - - return nil -} - // GetNamespaceOptions transforms a slice of kernel namespaces // into a slice of pod create options. Currently, not all // kernel namespaces are supported, and they will be returned in an error diff --git a/pkg/specgen/generate/namespaces_freebsd.go b/pkg/specgen/generate/namespaces_freebsd.go new file mode 100644 index 000000000..d821d9daa --- /dev/null +++ b/pkg/specgen/generate/namespaces_freebsd.go @@ -0,0 +1,51 @@ +package generate + +import ( + "fmt" + "os" + + "github.com/containers/podman/v4/libpod" + "github.com/containers/podman/v4/pkg/specgen" + "github.com/opencontainers/runtime-tools/generate" + "github.com/sirupsen/logrus" +) + +func specConfigureNamespaces(s *specgen.SpecGenerator, g *generate.Generator, rt *libpod.Runtime, pod *libpod.Pod) error { + // UTS + + hostname := s.Hostname + if hostname == "" { + switch { + case s.UtsNS.NSMode == specgen.FromPod: + hostname = pod.Hostname() + case s.UtsNS.NSMode == specgen.FromContainer: + utsCtr, err := rt.LookupContainer(s.UtsNS.Value) + if err != nil { + return fmt.Errorf("error looking up container to share uts namespace with: %w", err) + } + hostname = utsCtr.Hostname() + case (s.NetNS.NSMode == specgen.Host && hostname == "") || s.UtsNS.NSMode == specgen.Host: + tmpHostname, err := os.Hostname() + if err != nil { + return fmt.Errorf("unable to retrieve hostname of the host: %w", err) + } + hostname = tmpHostname + default: + logrus.Debug("No hostname set; container's hostname will default to runtime default") + } + } + + g.RemoveHostname() + if s.Hostname != "" || s.UtsNS.NSMode != specgen.Host { + // Set the hostname in the OCI configuration only if specified by + // the user or if we are creating a new UTS namespace. + // TODO: Should we be doing this for pod or container shared + // namespaces? + g.SetHostname(hostname) + } + if _, ok := s.Env["HOSTNAME"]; !ok && s.Hostname != "" { + g.AddProcessEnv("HOSTNAME", hostname) + } + + return nil +} diff --git a/pkg/specgen/generate/namespaces_linux.go b/pkg/specgen/generate/namespaces_linux.go new file mode 100644 index 000000000..5c056e52c --- /dev/null +++ b/pkg/specgen/generate/namespaces_linux.go @@ -0,0 +1,160 @@ +package generate + +import ( + "fmt" + "os" + + "github.com/containers/podman/v4/libpod" + "github.com/containers/podman/v4/libpod/define" + "github.com/containers/podman/v4/pkg/specgen" + spec "github.com/opencontainers/runtime-spec/specs-go" + "github.com/opencontainers/runtime-tools/generate" + "github.com/sirupsen/logrus" +) + +func specConfigureNamespaces(s *specgen.SpecGenerator, g *generate.Generator, rt *libpod.Runtime, pod *libpod.Pod) error { + // PID + switch s.PidNS.NSMode { + case specgen.Path: + if _, err := os.Stat(s.PidNS.Value); err != nil { + return fmt.Errorf("cannot find specified PID namespace path: %w", err) + } + if err := g.AddOrReplaceLinuxNamespace(string(spec.PIDNamespace), s.PidNS.Value); err != nil { + return err + } + case specgen.Host: + if err := g.RemoveLinuxNamespace(string(spec.PIDNamespace)); err != nil { + return err + } + case specgen.Private: + if err := g.AddOrReplaceLinuxNamespace(string(spec.PIDNamespace), ""); err != nil { + return err + } + } + + // IPC + switch s.IpcNS.NSMode { + case specgen.Path: + if _, err := os.Stat(s.IpcNS.Value); err != nil { + return fmt.Errorf("cannot find specified IPC namespace path: %w", err) + } + if err := g.AddOrReplaceLinuxNamespace(string(spec.IPCNamespace), s.IpcNS.Value); err != nil { + return err + } + case specgen.Host: + if err := g.RemoveLinuxNamespace(string(spec.IPCNamespace)); err != nil { + return err + } + case specgen.Private: + if err := g.AddOrReplaceLinuxNamespace(string(spec.IPCNamespace), ""); err != nil { + return err + } + } + + // UTS + switch s.UtsNS.NSMode { + case specgen.Path: + if _, err := os.Stat(s.UtsNS.Value); err != nil { + return fmt.Errorf("cannot find specified UTS namespace path: %w", err) + } + if err := g.AddOrReplaceLinuxNamespace(string(spec.UTSNamespace), s.UtsNS.Value); err != nil { + return err + } + case specgen.Host: + if err := g.RemoveLinuxNamespace(string(spec.UTSNamespace)); err != nil { + return err + } + case specgen.Private: + if err := g.AddOrReplaceLinuxNamespace(string(spec.UTSNamespace), ""); err != nil { + return err + } + } + + hostname := s.Hostname + if hostname == "" { + switch { + case s.UtsNS.NSMode == specgen.FromPod: + hostname = pod.Hostname() + case s.UtsNS.NSMode == specgen.FromContainer: + utsCtr, err := rt.LookupContainer(s.UtsNS.Value) + if err != nil { + return fmt.Errorf("error looking up container to share uts namespace with: %w", err) + } + hostname = utsCtr.Hostname() + case (s.NetNS.NSMode == specgen.Host && hostname == "") || s.UtsNS.NSMode == specgen.Host: + tmpHostname, err := os.Hostname() + if err != nil { + return fmt.Errorf("unable to retrieve hostname of the host: %w", err) + } + hostname = tmpHostname + default: + logrus.Debug("No hostname set; container's hostname will default to runtime default") + } + } + + g.RemoveHostname() + if s.Hostname != "" || s.UtsNS.NSMode != specgen.Host { + // Set the hostname in the OCI configuration only if specified by + // the user or if we are creating a new UTS namespace. + // TODO: Should we be doing this for pod or container shared + // namespaces? + g.SetHostname(hostname) + } + if _, ok := s.Env["HOSTNAME"]; !ok && s.Hostname != "" { + g.AddProcessEnv("HOSTNAME", hostname) + } + + // User + if _, err := specgen.SetupUserNS(s.IDMappings, s.UserNS, g); err != nil { + return err + } + + // Cgroup + switch s.CgroupNS.NSMode { + case specgen.Path: + if _, err := os.Stat(s.CgroupNS.Value); err != nil { + return fmt.Errorf("cannot find specified cgroup namespace path: %w", err) + } + if err := g.AddOrReplaceLinuxNamespace(string(spec.CgroupNamespace), s.CgroupNS.Value); err != nil { + return err + } + case specgen.Host: + if err := g.RemoveLinuxNamespace(string(spec.CgroupNamespace)); err != nil { + return err + } + case specgen.Private: + if err := g.AddOrReplaceLinuxNamespace(string(spec.CgroupNamespace), ""); err != nil { + return err + } + } + + // Net + switch s.NetNS.NSMode { + case specgen.Path: + if _, err := os.Stat(s.NetNS.Value); err != nil { + return fmt.Errorf("cannot find specified network namespace path: %w", err) + } + if err := g.AddOrReplaceLinuxNamespace(string(spec.NetworkNamespace), s.NetNS.Value); err != nil { + return err + } + case specgen.Host: + if err := g.RemoveLinuxNamespace(string(spec.NetworkNamespace)); err != nil { + return err + } + case specgen.Private, specgen.NoNetwork: + if err := g.AddOrReplaceLinuxNamespace(string(spec.NetworkNamespace), ""); err != nil { + return err + } + } + + if g.Config.Annotations == nil { + g.Config.Annotations = make(map[string]string) + } + if s.PublishExposedPorts { + g.Config.Annotations[define.InspectAnnotationPublishAll] = define.InspectResponseTrue + } else { + g.Config.Annotations[define.InspectAnnotationPublishAll] = define.InspectResponseFalse + } + + return nil +} diff --git a/pkg/specgen/generate/namespaces_unsupported.go b/pkg/specgen/generate/namespaces_unsupported.go new file mode 100644 index 000000000..c4a9c22d8 --- /dev/null +++ b/pkg/specgen/generate/namespaces_unsupported.go @@ -0,0 +1,16 @@ +//go:build !linux && !freebsd +// +build !linux,!freebsd + +package generate + +import ( + "errors" + + "github.com/containers/podman/v4/libpod" + "github.com/containers/podman/v4/pkg/specgen" + "github.com/opencontainers/runtime-tools/generate" +) + +func specConfigureNamespaces(s *specgen.SpecGenerator, g *generate.Generator, rt *libpod.Runtime, pod *libpod.Pod) error { + return errors.New("unsupported specConfigureNamespaces") +} diff --git a/pkg/specgen/generate/oci.go b/pkg/specgen/generate/oci.go index a531494c9..3ac1a9b3f 100644 --- a/pkg/specgen/generate/oci.go +++ b/pkg/specgen/generate/oci.go @@ -1,37 +1,19 @@ package generate import ( - "context" - "encoding/json" "fmt" - "path" "strings" "github.com/containers/common/libimage" - "github.com/containers/common/pkg/cgroups" "github.com/containers/common/pkg/config" - "github.com/containers/podman/v4/libpod" "github.com/containers/podman/v4/libpod/define" "github.com/containers/podman/v4/pkg/rootless" "github.com/containers/podman/v4/pkg/specgen" - spec "github.com/opencontainers/runtime-spec/specs-go" "github.com/opencontainers/runtime-tools/generate" "github.com/sirupsen/logrus" "golang.org/x/sys/unix" ) -func setProcOpts(s *specgen.SpecGenerator, g *generate.Generator) { - if s.ProcOpts == nil { - return - } - for i := range g.Config.Mounts { - if g.Config.Mounts[i].Destination == "/proc" { - g.Config.Mounts[i].Options = s.ProcOpts - return - } - } -} - func addRlimits(s *specgen.SpecGenerator, g *generate.Generator) { var ( isRootless = rootless.IsRootless() @@ -133,302 +115,3 @@ func makeCommand(s *specgen.SpecGenerator, imageData *libimage.ImageData, rtc *c return finalCommand, nil } - -// canMountSys is a best-effort heuristic to detect whether mounting a new sysfs is permitted in the container -func canMountSys(isRootless, isNewUserns bool, s *specgen.SpecGenerator) bool { - if s.NetNS.IsHost() && (isRootless || isNewUserns) { - return false - } - if isNewUserns { - switch s.NetNS.NSMode { - case specgen.Slirp, specgen.Private, specgen.NoNetwork, specgen.Bridge: - return true - default: - return false - } - } - return true -} - -func getCgroupPermissons(unmask []string) string { - ro := "ro" - rw := "rw" - cgroup := "/sys/fs/cgroup" - - cgroupv2, _ := cgroups.IsCgroup2UnifiedMode() - if !cgroupv2 { - return ro - } - - if unmask != nil && unmask[0] == "ALL" { - return rw - } - - for _, p := range unmask { - if path.Clean(p) == cgroup { - return rw - } - } - return ro -} - -// SpecGenToOCI returns the base configuration for the container. -func SpecGenToOCI(ctx context.Context, s *specgen.SpecGenerator, rt *libpod.Runtime, rtc *config.Config, newImage *libimage.Image, mounts []spec.Mount, pod *libpod.Pod, finalCmd []string, compatibleOptions *libpod.InfraInherit) (*spec.Spec, error) { - cgroupPerm := getCgroupPermissons(s.Unmask) - - g, err := generate.New("linux") - if err != nil { - return nil, err - } - // Remove the default /dev/shm mount to ensure we overwrite it - g.RemoveMount("/dev/shm") - g.HostSpecific = true - addCgroup := true - - isRootless := rootless.IsRootless() - isNewUserns := s.UserNS.IsContainer() || s.UserNS.IsPath() || s.UserNS.IsPrivate() - - canMountSys := canMountSys(isRootless, isNewUserns, s) - - if s.Privileged && canMountSys { - cgroupPerm = "rw" - g.RemoveMount("/sys") - sysMnt := spec.Mount{ - Destination: "/sys", - Type: "sysfs", - Source: "sysfs", - Options: []string{"rprivate", "nosuid", "noexec", "nodev", "rw"}, - } - g.AddMount(sysMnt) - } - if !canMountSys { - addCgroup = false - g.RemoveMount("/sys") - r := "ro" - if s.Privileged { - r = "rw" - } - sysMnt := spec.Mount{ - Destination: "/sys", - Type: "bind", // should we use a constant for this, like createconfig? - Source: "/sys", - Options: []string{"rprivate", "nosuid", "noexec", "nodev", r, "rbind"}, - } - g.AddMount(sysMnt) - if !s.Privileged && isRootless { - g.AddLinuxMaskedPaths("/sys/kernel") - } - } - gid5Available := true - if isRootless { - nGids, err := rootless.GetAvailableGids() - if err != nil { - return nil, err - } - gid5Available = nGids >= 5 - } - // When using a different user namespace, check that the GID 5 is mapped inside - // the container. - if gid5Available && (s.IDMappings != nil && len(s.IDMappings.GIDMap) > 0) { - mappingFound := false - for _, r := range s.IDMappings.GIDMap { - if r.ContainerID <= 5 && 5 < r.ContainerID+r.Size { - mappingFound = true - break - } - } - if !mappingFound { - gid5Available = false - } - } - if !gid5Available { - // If we have no GID mappings, the gid=5 default option would fail, so drop it. - g.RemoveMount("/dev/pts") - devPts := spec.Mount{ - Destination: "/dev/pts", - Type: "devpts", - Source: "devpts", - Options: []string{"rprivate", "nosuid", "noexec", "newinstance", "ptmxmode=0666", "mode=0620"}, - } - g.AddMount(devPts) - } - - inUserNS := isRootless || isNewUserns - - if inUserNS && s.IpcNS.IsHost() { - g.RemoveMount("/dev/mqueue") - devMqueue := spec.Mount{ - Destination: "/dev/mqueue", - Type: "bind", // constant ? - Source: "/dev/mqueue", - Options: []string{"bind", "nosuid", "noexec", "nodev"}, - } - g.AddMount(devMqueue) - } - if inUserNS && s.PidNS.IsHost() { - g.RemoveMount("/proc") - procMount := spec.Mount{ - Destination: "/proc", - Type: define.TypeBind, - Source: "/proc", - Options: []string{"rbind", "nosuid", "noexec", "nodev"}, - } - g.AddMount(procMount) - } - - if addCgroup { - cgroupMnt := spec.Mount{ - Destination: "/sys/fs/cgroup", - Type: "cgroup", - Source: "cgroup", - Options: []string{"rprivate", "nosuid", "noexec", "nodev", "relatime", cgroupPerm}, - } - g.AddMount(cgroupMnt) - } - - g.Config.Linux.Personality = s.Personality - - g.SetProcessCwd(s.WorkDir) - - g.SetProcessArgs(finalCmd) - - g.SetProcessTerminal(s.Terminal) - - for key, val := range s.Annotations { - g.AddAnnotation(key, val) - } - - if s.ResourceLimits != nil { - out, err := json.Marshal(s.ResourceLimits) - if err != nil { - return nil, err - } - err = json.Unmarshal(out, g.Config.Linux.Resources) - if err != nil { - return nil, err - } - g.Config.Linux.Resources = s.ResourceLimits - } - - weightDevices, err := WeightDevices(s.WeightDevice) - if err != nil { - return nil, err - } - if len(weightDevices) > 0 { - for _, dev := range weightDevices { - g.AddLinuxResourcesBlockIOWeightDevice(dev.Major, dev.Minor, *dev.Weight) - } - } - - // Devices - // set the default rule at the beginning of device configuration - if !inUserNS && !s.Privileged { - g.AddLinuxResourcesDevice(false, "", nil, nil, "rwm") - } - - var userDevices []spec.LinuxDevice - - if !s.Privileged { - // add default devices from containers.conf - for _, device := range rtc.Containers.Devices { - if err = DevicesFromPath(&g, device); err != nil { - return nil, err - } - } - if len(compatibleOptions.HostDeviceList) > 0 && len(s.Devices) == 0 { - userDevices = compatibleOptions.HostDeviceList - } else { - userDevices = s.Devices - } - // add default devices specified by caller - for _, device := range userDevices { - if err = DevicesFromPath(&g, device.Path); err != nil { - return nil, err - } - } - } - s.HostDeviceList = userDevices - - // set the devices cgroup when not running in a user namespace - if !inUserNS && !s.Privileged { - for _, dev := range s.DeviceCgroupRule { - g.AddLinuxResourcesDevice(true, dev.Type, dev.Major, dev.Minor, dev.Access) - } - } - - BlockAccessToKernelFilesystems(s.Privileged, s.PidNS.IsHost(), s.Mask, s.Unmask, &g) - - g.ClearProcessEnv() - for name, val := range s.Env { - g.AddProcessEnv(name, val) - } - - addRlimits(s, &g) - - // NAMESPACES - if err := specConfigureNamespaces(s, &g, rt, pod); err != nil { - return nil, err - } - configSpec := g.Config - - if err := securityConfigureGenerator(s, &g, newImage, rtc); err != nil { - return nil, err - } - - // BIND MOUNTS - configSpec.Mounts = SupersedeUserMounts(mounts, configSpec.Mounts) - // Process mounts to ensure correct options - if err := InitFSMounts(configSpec.Mounts); err != nil { - return nil, err - } - - // Add annotations - if configSpec.Annotations == nil { - configSpec.Annotations = make(map[string]string) - } - - if s.Remove { - configSpec.Annotations[define.InspectAnnotationAutoremove] = define.InspectResponseTrue - } else { - configSpec.Annotations[define.InspectAnnotationAutoremove] = define.InspectResponseFalse - } - - if len(s.VolumesFrom) > 0 { - configSpec.Annotations[define.InspectAnnotationVolumesFrom] = strings.Join(s.VolumesFrom, ",") - } - - if s.Privileged { - configSpec.Annotations[define.InspectAnnotationPrivileged] = define.InspectResponseTrue - } else { - configSpec.Annotations[define.InspectAnnotationPrivileged] = define.InspectResponseFalse - } - - if s.Init { - configSpec.Annotations[define.InspectAnnotationInit] = define.InspectResponseTrue - } else { - configSpec.Annotations[define.InspectAnnotationInit] = define.InspectResponseFalse - } - - if s.OOMScoreAdj != nil { - g.SetProcessOOMScoreAdj(*s.OOMScoreAdj) - } - setProcOpts(s, &g) - - return configSpec, nil -} - -func WeightDevices(wtDevices map[string]spec.LinuxWeightDevice) ([]spec.LinuxWeightDevice, error) { - devs := []spec.LinuxWeightDevice{} - for k, v := range wtDevices { - statT := unix.Stat_t{} - if err := unix.Stat(k, &statT); err != nil { - return nil, fmt.Errorf("failed to inspect '%s' in --blkio-weight-device: %w", k, err) - } - dev := new(spec.LinuxWeightDevice) - dev.Major = (int64(unix.Major(uint64(statT.Rdev)))) //nolint: unconvert - dev.Minor = (int64(unix.Minor(uint64(statT.Rdev)))) //nolint: unconvert - dev.Weight = v.Weight - devs = append(devs, *dev) - } - return devs, nil -} diff --git a/pkg/specgen/generate/oci_freebsd.go b/pkg/specgen/generate/oci_freebsd.go new file mode 100644 index 000000000..71c926fd2 --- /dev/null +++ b/pkg/specgen/generate/oci_freebsd.go @@ -0,0 +1,96 @@ +//go:build freebsd + +package generate + +import ( + "context" + "strings" + + "github.com/containers/common/libimage" + "github.com/containers/common/pkg/config" + "github.com/containers/podman/v4/libpod" + "github.com/containers/podman/v4/libpod/define" + "github.com/containers/podman/v4/pkg/specgen" + spec "github.com/opencontainers/runtime-spec/specs-go" + "github.com/opencontainers/runtime-tools/generate" +) + +// SpecGenToOCI returns the base configuration for the container. +func SpecGenToOCI(ctx context.Context, s *specgen.SpecGenerator, rt *libpod.Runtime, rtc *config.Config, newImage *libimage.Image, mounts []spec.Mount, pod *libpod.Pod, finalCmd []string, compatibleOptions *libpod.InfraInherit) (*spec.Spec, error) { + g, err := generate.New("freebsd") + if err != nil { + return nil, err + } + + g.SetProcessCwd(s.WorkDir) + + g.SetProcessArgs(finalCmd) + + g.SetProcessTerminal(s.Terminal) + + for key, val := range s.Annotations { + g.AddAnnotation(key, val) + } + + g.ClearProcessEnv() + for name, val := range s.Env { + g.AddProcessEnv(name, val) + } + + addRlimits(s, &g) + + // NAMESPACES + if err := specConfigureNamespaces(s, &g, rt, pod); err != nil { + return nil, err + } + configSpec := g.Config + + if err := securityConfigureGenerator(s, &g, newImage, rtc); err != nil { + return nil, err + } + + // BIND MOUNTS + configSpec.Mounts = SupersedeUserMounts(mounts, configSpec.Mounts) + // Process mounts to ensure correct options + if err := InitFSMounts(configSpec.Mounts); err != nil { + return nil, err + } + + // Add annotations + if configSpec.Annotations == nil { + configSpec.Annotations = make(map[string]string) + } + + if s.Remove { + configSpec.Annotations[define.InspectAnnotationAutoremove] = define.InspectResponseTrue + } else { + configSpec.Annotations[define.InspectAnnotationAutoremove] = define.InspectResponseFalse + } + + if len(s.VolumesFrom) > 0 { + configSpec.Annotations[define.InspectAnnotationVolumesFrom] = strings.Join(s.VolumesFrom, ",") + } + + if s.Privileged { + configSpec.Annotations[define.InspectAnnotationPrivileged] = define.InspectResponseTrue + } else { + configSpec.Annotations[define.InspectAnnotationPrivileged] = define.InspectResponseFalse + } + + if s.Init { + configSpec.Annotations[define.InspectAnnotationInit] = define.InspectResponseTrue + } else { + configSpec.Annotations[define.InspectAnnotationInit] = define.InspectResponseFalse + } + + if s.OOMScoreAdj != nil { + g.SetProcessOOMScoreAdj(*s.OOMScoreAdj) + } + + return configSpec, nil +} + +func WeightDevices(wtDevices map[string]spec.LinuxWeightDevice) ([]spec.LinuxWeightDevice, error) { + devs := []spec.LinuxWeightDevice{} + return devs, nil +} diff --git a/pkg/specgen/generate/oci_linux.go b/pkg/specgen/generate/oci_linux.go new file mode 100644 index 000000000..341853de5 --- /dev/null +++ b/pkg/specgen/generate/oci_linux.go @@ -0,0 +1,331 @@ +package generate + +import ( + "context" + "encoding/json" + "fmt" + "path" + "strings" + + "github.com/containers/common/libimage" + "github.com/containers/common/pkg/cgroups" + "github.com/containers/common/pkg/config" + "github.com/containers/podman/v4/libpod" + "github.com/containers/podman/v4/libpod/define" + "github.com/containers/podman/v4/pkg/rootless" + "github.com/containers/podman/v4/pkg/specgen" + spec "github.com/opencontainers/runtime-spec/specs-go" + "github.com/opencontainers/runtime-tools/generate" + "golang.org/x/sys/unix" +) + +func setProcOpts(s *specgen.SpecGenerator, g *generate.Generator) { + if s.ProcOpts == nil { + return + } + for i := range g.Config.Mounts { + if g.Config.Mounts[i].Destination == "/proc" { + g.Config.Mounts[i].Options = s.ProcOpts + return + } + } +} + +// canMountSys is a best-effort heuristic to detect whether mounting a new sysfs is permitted in the container +func canMountSys(isRootless, isNewUserns bool, s *specgen.SpecGenerator) bool { + if s.NetNS.IsHost() && (isRootless || isNewUserns) { + return false + } + if isNewUserns { + switch s.NetNS.NSMode { + case specgen.Slirp, specgen.Private, specgen.NoNetwork, specgen.Bridge: + return true + default: + return false + } + } + return true +} + +func getCgroupPermissons(unmask []string) string { + ro := "ro" + rw := "rw" + cgroup := "/sys/fs/cgroup" + + cgroupv2, _ := cgroups.IsCgroup2UnifiedMode() + if !cgroupv2 { + return ro + } + + if unmask != nil && unmask[0] == "ALL" { + return rw + } + + for _, p := range unmask { + if path.Clean(p) == cgroup { + return rw + } + } + return ro +} + +// SpecGenToOCI returns the base configuration for the container. +func SpecGenToOCI(ctx context.Context, s *specgen.SpecGenerator, rt *libpod.Runtime, rtc *config.Config, newImage *libimage.Image, mounts []spec.Mount, pod *libpod.Pod, finalCmd []string, compatibleOptions *libpod.InfraInherit) (*spec.Spec, error) { + cgroupPerm := getCgroupPermissons(s.Unmask) + + g, err := generate.New("linux") + if err != nil { + return nil, err + } + // Remove the default /dev/shm mount to ensure we overwrite it + g.RemoveMount("/dev/shm") + g.HostSpecific = true + addCgroup := true + + isRootless := rootless.IsRootless() + isNewUserns := s.UserNS.IsContainer() || s.UserNS.IsPath() || s.UserNS.IsPrivate() + + canMountSys := canMountSys(isRootless, isNewUserns, s) + + if s.Privileged && canMountSys { + cgroupPerm = "rw" + g.RemoveMount("/sys") + sysMnt := spec.Mount{ + Destination: "/sys", + Type: "sysfs", + Source: "sysfs", + Options: []string{"rprivate", "nosuid", "noexec", "nodev", "rw"}, + } + g.AddMount(sysMnt) + } + if !canMountSys { + addCgroup = false + g.RemoveMount("/sys") + r := "ro" + if s.Privileged { + r = "rw" + } + sysMnt := spec.Mount{ + Destination: "/sys", + Type: "bind", // should we use a constant for this, like createconfig? + Source: "/sys", + Options: []string{"rprivate", "nosuid", "noexec", "nodev", r, "rbind"}, + } + g.AddMount(sysMnt) + if !s.Privileged && isRootless { + g.AddLinuxMaskedPaths("/sys/kernel") + } + } + gid5Available := true + if isRootless { + nGids, err := rootless.GetAvailableGids() + if err != nil { + return nil, err + } + gid5Available = nGids >= 5 + } + // When using a different user namespace, check that the GID 5 is mapped inside + // the container. + if gid5Available && (s.IDMappings != nil && len(s.IDMappings.GIDMap) > 0) { + mappingFound := false + for _, r := range s.IDMappings.GIDMap { + if r.ContainerID <= 5 && 5 < r.ContainerID+r.Size { + mappingFound = true + break + } + } + if !mappingFound { + gid5Available = false + } + } + if !gid5Available { + // If we have no GID mappings, the gid=5 default option would fail, so drop it. + g.RemoveMount("/dev/pts") + devPts := spec.Mount{ + Destination: "/dev/pts", + Type: "devpts", + Source: "devpts", + Options: []string{"rprivate", "nosuid", "noexec", "newinstance", "ptmxmode=0666", "mode=0620"}, + } + g.AddMount(devPts) + } + + inUserNS := isRootless || isNewUserns + + if inUserNS && s.IpcNS.IsHost() { + g.RemoveMount("/dev/mqueue") + devMqueue := spec.Mount{ + Destination: "/dev/mqueue", + Type: "bind", // constant ? + Source: "/dev/mqueue", + Options: []string{"bind", "nosuid", "noexec", "nodev"}, + } + g.AddMount(devMqueue) + } + if inUserNS && s.PidNS.IsHost() { + g.RemoveMount("/proc") + procMount := spec.Mount{ + Destination: "/proc", + Type: define.TypeBind, + Source: "/proc", + Options: []string{"rbind", "nosuid", "noexec", "nodev"}, + } + g.AddMount(procMount) + } + + if addCgroup { + cgroupMnt := spec.Mount{ + Destination: "/sys/fs/cgroup", + Type: "cgroup", + Source: "cgroup", + Options: []string{"rprivate", "nosuid", "noexec", "nodev", "relatime", cgroupPerm}, + } + g.AddMount(cgroupMnt) + } + + g.Config.Linux.Personality = s.Personality + + g.SetProcessCwd(s.WorkDir) + + g.SetProcessArgs(finalCmd) + + g.SetProcessTerminal(s.Terminal) + + for key, val := range s.Annotations { + g.AddAnnotation(key, val) + } + + if s.ResourceLimits != nil { + out, err := json.Marshal(s.ResourceLimits) + if err != nil { + return nil, err + } + err = json.Unmarshal(out, g.Config.Linux.Resources) + if err != nil { + return nil, err + } + g.Config.Linux.Resources = s.ResourceLimits + } + + weightDevices, err := WeightDevices(s.WeightDevice) + if err != nil { + return nil, err + } + if len(weightDevices) > 0 { + for _, dev := range weightDevices { + g.AddLinuxResourcesBlockIOWeightDevice(dev.Major, dev.Minor, *dev.Weight) + } + } + + // Devices + // set the default rule at the beginning of device configuration + if !inUserNS && !s.Privileged { + g.AddLinuxResourcesDevice(false, "", nil, nil, "rwm") + } + + var userDevices []spec.LinuxDevice + + if !s.Privileged { + // add default devices from containers.conf + for _, device := range rtc.Containers.Devices { + if err = DevicesFromPath(&g, device); err != nil { + return nil, err + } + } + if len(compatibleOptions.HostDeviceList) > 0 && len(s.Devices) == 0 { + userDevices = compatibleOptions.HostDeviceList + } else { + userDevices = s.Devices + } + // add default devices specified by caller + for _, device := range userDevices { + if err = DevicesFromPath(&g, device.Path); err != nil { + return nil, err + } + } + } + s.HostDeviceList = userDevices + + // set the devices cgroup when not running in a user namespace + if !inUserNS && !s.Privileged { + for _, dev := range s.DeviceCgroupRule { + g.AddLinuxResourcesDevice(true, dev.Type, dev.Major, dev.Minor, dev.Access) + } + } + + BlockAccessToKernelFilesystems(s.Privileged, s.PidNS.IsHost(), s.Mask, s.Unmask, &g) + + g.ClearProcessEnv() + for name, val := range s.Env { + g.AddProcessEnv(name, val) + } + + addRlimits(s, &g) + + // NAMESPACES + if err := specConfigureNamespaces(s, &g, rt, pod); err != nil { + return nil, err + } + configSpec := g.Config + + if err := securityConfigureGenerator(s, &g, newImage, rtc); err != nil { + return nil, err + } + + // BIND MOUNTS + configSpec.Mounts = SupersedeUserMounts(mounts, configSpec.Mounts) + // Process mounts to ensure correct options + if err := InitFSMounts(configSpec.Mounts); err != nil { + return nil, err + } + + // Add annotations + if configSpec.Annotations == nil { + configSpec.Annotations = make(map[string]string) + } + + if s.Remove { + configSpec.Annotations[define.InspectAnnotationAutoremove] = define.InspectResponseTrue + } else { + configSpec.Annotations[define.InspectAnnotationAutoremove] = define.InspectResponseFalse + } + + if len(s.VolumesFrom) > 0 { + configSpec.Annotations[define.InspectAnnotationVolumesFrom] = strings.Join(s.VolumesFrom, ",") + } + + if s.Privileged { + configSpec.Annotations[define.InspectAnnotationPrivileged] = define.InspectResponseTrue + } else { + configSpec.Annotations[define.InspectAnnotationPrivileged] = define.InspectResponseFalse + } + + if s.Init { + configSpec.Annotations[define.InspectAnnotationInit] = define.InspectResponseTrue + } else { + configSpec.Annotations[define.InspectAnnotationInit] = define.InspectResponseFalse + } + + if s.OOMScoreAdj != nil { + g.SetProcessOOMScoreAdj(*s.OOMScoreAdj) + } + setProcOpts(s, &g) + + return configSpec, nil +} + +func WeightDevices(wtDevices map[string]spec.LinuxWeightDevice) ([]spec.LinuxWeightDevice, error) { + devs := []spec.LinuxWeightDevice{} + for k, v := range wtDevices { + statT := unix.Stat_t{} + if err := unix.Stat(k, &statT); err != nil { + return nil, fmt.Errorf("failed to inspect '%s' in --blkio-weight-device: %w", k, err) + } + dev := new(spec.LinuxWeightDevice) + dev.Major = (int64(unix.Major(uint64(statT.Rdev)))) //nolint: unconvert + dev.Minor = (int64(unix.Minor(uint64(statT.Rdev)))) //nolint: unconvert + dev.Weight = v.Weight + devs = append(devs, *dev) + } + return devs, nil +} diff --git a/pkg/specgen/generate/oci_unsupported.go b/pkg/specgen/generate/oci_unsupported.go new file mode 100644 index 000000000..7e1b8c42c --- /dev/null +++ b/pkg/specgen/generate/oci_unsupported.go @@ -0,0 +1,24 @@ +//go:build !linux && !freebsd +// +build !linux,!freebsd + +package generate + +import ( + "context" + "errors" + + "github.com/containers/common/libimage" + "github.com/containers/common/pkg/config" + "github.com/containers/podman/v4/libpod" + "github.com/containers/podman/v4/pkg/specgen" + spec "github.com/opencontainers/runtime-spec/specs-go" +) + +// SpecGenToOCI returns the base configuration for the container. +func SpecGenToOCI(ctx context.Context, s *specgen.SpecGenerator, rt *libpod.Runtime, rtc *config.Config, newImage *libimage.Image, mounts []spec.Mount, pod *libpod.Pod, finalCmd []string, compatibleOptions *libpod.InfraInherit) (*spec.Spec, error) { + return nil, errors.New("unsupported SpecGenToOCI") +} + +func WeightDevices(wtDevices map[string]spec.LinuxWeightDevice) ([]spec.LinuxWeightDevice, error) { + return []spec.LinuxWeightDevice{}, errors.New("unsupported WeightDevices") +} diff --git a/pkg/specgen/generate/pod_create.go b/pkg/specgen/generate/pod_create.go index d6063b9a0..14d390e49 100644 --- a/pkg/specgen/generate/pod_create.go +++ b/pkg/specgen/generate/pod_create.go @@ -45,7 +45,7 @@ func MakePod(p *entities.PodSpec, rt *libpod.Runtime) (*libpod.Pod, error) { } if !p.PodSpecGen.NoInfra { - err := FinishThrottleDevices(p.PodSpecGen.InfraContainerSpec) + err := specgen.FinishThrottleDevices(p.PodSpecGen.InfraContainerSpec) if err != nil { return nil, err } @@ -53,17 +53,11 @@ func MakePod(p *entities.PodSpec, rt *libpod.Runtime) (*libpod.Pod, error) { p.PodSpecGen.ResourceLimits.BlockIO = p.PodSpecGen.InfraContainerSpec.ResourceLimits.BlockIO } - weightDevices, err := WeightDevices(p.PodSpecGen.InfraContainerSpec.WeightDevice) + err = specgen.WeightDevices(p.PodSpecGen.InfraContainerSpec) if err != nil { return nil, err } - - if p.PodSpecGen.ResourceLimits != nil && len(weightDevices) > 0 { - if p.PodSpecGen.ResourceLimits.BlockIO == nil { - p.PodSpecGen.ResourceLimits.BlockIO = &specs.LinuxBlockIO{} - } - p.PodSpecGen.ResourceLimits.BlockIO.WeightDevice = weightDevices - } + p.PodSpecGen.ResourceLimits = p.PodSpecGen.InfraContainerSpec.ResourceLimits } options, err := createPodOptions(&p.PodSpecGen) diff --git a/pkg/specgen/generate/security_freebsd.go b/pkg/specgen/generate/security_freebsd.go new file mode 100644 index 000000000..5fd66c769 --- /dev/null +++ b/pkg/specgen/generate/security_freebsd.go @@ -0,0 +1,19 @@ +package generate + +import ( + "github.com/containers/common/libimage" + "github.com/containers/common/pkg/config" + "github.com/containers/podman/v4/libpod" + "github.com/containers/podman/v4/pkg/specgen" + "github.com/opencontainers/runtime-tools/generate" +) + +// setLabelOpts sets the label options of the SecurityConfig according to the +// input. +func setLabelOpts(s *specgen.SpecGenerator, runtime *libpod.Runtime, pidConfig specgen.Namespace, ipcConfig specgen.Namespace) error { + return nil +} + +func securityConfigureGenerator(s *specgen.SpecGenerator, g *generate.Generator, newImage *libimage.Image, rtc *config.Config) error { + return nil +} diff --git a/pkg/specgen/generate/security.go b/pkg/specgen/generate/security_linux.go index aacefcbac..aacefcbac 100644 --- a/pkg/specgen/generate/security.go +++ b/pkg/specgen/generate/security_linux.go diff --git a/pkg/specgen/generate/security_unsupported.go b/pkg/specgen/generate/security_unsupported.go new file mode 100644 index 000000000..d0f937e44 --- /dev/null +++ b/pkg/specgen/generate/security_unsupported.go @@ -0,0 +1,24 @@ +//go:build !linux && !freebsd +// +build !linux,!freebsd + +package generate + +import ( + "errors" + + "github.com/containers/common/libimage" + "github.com/containers/common/pkg/config" + "github.com/containers/podman/v4/libpod" + "github.com/containers/podman/v4/pkg/specgen" + "github.com/opencontainers/runtime-tools/generate" +) + +// setLabelOpts sets the label options of the SecurityConfig according to the +// input. +func setLabelOpts(s *specgen.SpecGenerator, runtime *libpod.Runtime, pidConfig specgen.Namespace, ipcConfig specgen.Namespace) error { + return errors.New("unsupported setLabelOpts") +} + +func securityConfigureGenerator(s *specgen.SpecGenerator, g *generate.Generator, newImage *libimage.Image, rtc *config.Config) error { + return errors.New("unsupported securityConfigureGenerator") +} diff --git a/pkg/specgen/namespaces.go b/pkg/specgen/namespaces.go index 03a2049f6..8cc0fe6a9 100644 --- a/pkg/specgen/namespaces.go +++ b/pkg/specgen/namespaces.go @@ -11,6 +11,7 @@ import ( "github.com/containers/common/pkg/cgroups" cutil "github.com/containers/common/pkg/util" "github.com/containers/podman/v4/libpod/define" + "github.com/containers/podman/v4/pkg/namespaces" "github.com/containers/podman/v4/pkg/util" "github.com/containers/storage" spec "github.com/opencontainers/runtime-spec/specs-go" @@ -308,6 +309,14 @@ func ParseUserNamespace(ns string) (Namespace, error) { case ns == "keep-id": toReturn.NSMode = KeepID return toReturn, nil + case strings.HasPrefix(ns, "keep-id:"): + split := strings.SplitN(ns, ":", 2) + if len(split) != 2 { + return toReturn, errors.New("invalid setting for keep-id: mode") + } + toReturn.NSMode = KeepID + toReturn.Value = split[1] + return toReturn, nil case ns == "nomap": toReturn.NSMode = NoMap return toReturn, nil @@ -490,7 +499,11 @@ func SetupUserNS(idmappings *storage.IDMappingOptions, userns Namespace, g *gene return user, err } case KeepID: - mappings, uid, gid, err := util.GetKeepIDMapping() + opts, err := namespaces.UsernsMode(userns.String()).GetKeepIDOptions() + if err != nil { + return user, err + } + mappings, uid, gid, err := util.GetKeepIDMapping(opts) if err != nil { return user, err } diff --git a/pkg/specgen/utils.go b/pkg/specgen/utils.go new file mode 100644 index 000000000..dc9127bb3 --- /dev/null +++ b/pkg/specgen/utils.go @@ -0,0 +1,14 @@ +//go:build !linux +// +build !linux + +package specgen + +// FinishThrottleDevices cannot be called on non-linux OS' due to importing unix functions +func FinishThrottleDevices(s *SpecGenerator) error { + return nil +} + +// WeightDevices cannot be called on non-linux OS' due to importing unix functions +func WeightDevices(s *SpecGenerator) error { + return nil +} diff --git a/pkg/specgen/utils_linux.go b/pkg/specgen/utils_linux.go new file mode 100644 index 000000000..d8e4cbae3 --- /dev/null +++ b/pkg/specgen/utils_linux.go @@ -0,0 +1,103 @@ +//go:build linux +// +build linux + +package specgen + +import ( + "fmt" + + spec "github.com/opencontainers/runtime-spec/specs-go" + "golang.org/x/sys/unix" +) + +// FinishThrottleDevices takes the temporary representation of the throttle +// devices in the specgen and looks up the major and major minors. it then +// sets the throttle devices proper in the specgen +func FinishThrottleDevices(s *SpecGenerator) error { + if s.ResourceLimits == nil { + s.ResourceLimits = &spec.LinuxResources{} + } + if bps := s.ThrottleReadBpsDevice; len(bps) > 0 { + if s.ResourceLimits.BlockIO == nil { + s.ResourceLimits.BlockIO = &spec.LinuxBlockIO{} + } + for k, v := range bps { + statT := unix.Stat_t{} + if err := unix.Stat(k, &statT); err != nil { + return fmt.Errorf("could not parse throttle device at %s: %w", k, err) + } + v.Major = (int64(unix.Major(uint64(statT.Rdev)))) //nolint: unconvert + v.Minor = (int64(unix.Minor(uint64(statT.Rdev)))) //nolint: unconvert + if s.ResourceLimits.BlockIO == nil { + s.ResourceLimits.BlockIO = new(spec.LinuxBlockIO) + } + s.ResourceLimits.BlockIO.ThrottleReadBpsDevice = append(s.ResourceLimits.BlockIO.ThrottleReadBpsDevice, v) + } + } + if bps := s.ThrottleWriteBpsDevice; len(bps) > 0 { + if s.ResourceLimits.BlockIO == nil { + s.ResourceLimits.BlockIO = &spec.LinuxBlockIO{} + } + for k, v := range bps { + statT := unix.Stat_t{} + if err := unix.Stat(k, &statT); err != nil { + return fmt.Errorf("could not parse throttle device at %s: %w", k, err) + } + v.Major = (int64(unix.Major(uint64(statT.Rdev)))) //nolint: unconvert + v.Minor = (int64(unix.Minor(uint64(statT.Rdev)))) //nolint: unconvert + s.ResourceLimits.BlockIO.ThrottleWriteBpsDevice = append(s.ResourceLimits.BlockIO.ThrottleWriteBpsDevice, v) + } + } + if iops := s.ThrottleReadIOPSDevice; len(iops) > 0 { + if s.ResourceLimits.BlockIO == nil { + s.ResourceLimits.BlockIO = &spec.LinuxBlockIO{} + } + for k, v := range iops { + statT := unix.Stat_t{} + if err := unix.Stat(k, &statT); err != nil { + return fmt.Errorf("could not parse throttle device at %s: %w", k, err) + } + v.Major = (int64(unix.Major(uint64(statT.Rdev)))) //nolint: unconvert + v.Minor = (int64(unix.Minor(uint64(statT.Rdev)))) //nolint: unconvert + s.ResourceLimits.BlockIO.ThrottleReadIOPSDevice = append(s.ResourceLimits.BlockIO.ThrottleReadIOPSDevice, v) + } + } + if iops := s.ThrottleWriteIOPSDevice; len(iops) > 0 { + if s.ResourceLimits.BlockIO == nil { + s.ResourceLimits.BlockIO = &spec.LinuxBlockIO{} + } + for k, v := range iops { + statT := unix.Stat_t{} + if err := unix.Stat(k, &statT); err != nil { + return fmt.Errorf("could not parse throttle device at %s: %w", k, err) + } + v.Major = (int64(unix.Major(uint64(statT.Rdev)))) //nolint: unconvert + v.Minor = (int64(unix.Minor(uint64(statT.Rdev)))) //nolint: unconvert + s.ResourceLimits.BlockIO.ThrottleWriteIOPSDevice = append(s.ResourceLimits.BlockIO.ThrottleWriteIOPSDevice, v) + } + } + return nil +} + +func WeightDevices(specgen *SpecGenerator) error { + devs := []spec.LinuxWeightDevice{} + if specgen.ResourceLimits == nil { + specgen.ResourceLimits = &spec.LinuxResources{} + } + for k, v := range specgen.WeightDevice { + statT := unix.Stat_t{} + if err := unix.Stat(k, &statT); err != nil { + return fmt.Errorf("failed to inspect '%s' in --blkio-weight-device: %w", k, err) + } + dev := new(spec.LinuxWeightDevice) + dev.Major = (int64(unix.Major(uint64(statT.Rdev)))) //nolint: unconvert + dev.Minor = (int64(unix.Minor(uint64(statT.Rdev)))) //nolint: unconvert + dev.Weight = v.Weight + devs = append(devs, *dev) + if specgen.ResourceLimits.BlockIO == nil { + specgen.ResourceLimits.BlockIO = &spec.LinuxBlockIO{} + } + specgen.ResourceLimits.BlockIO.WeightDevice = devs + } + return nil +} diff --git a/pkg/specgen/volumes.go b/pkg/specgen/volumes.go index 84de4fdd1..e70ed5b13 100644 --- a/pkg/specgen/volumes.go +++ b/pkg/specgen/volumes.go @@ -23,6 +23,9 @@ type NamedVolume struct { Dest string // Options are options that the named volume will be mounted with. Options []string + // IsAnonymous sets the named volume as anonymous even if it has a name + // This is used for emptyDir volumes from a kube yaml + IsAnonymous bool } // OverlayVolume holds information about a overlay volume that will be mounted into diff --git a/pkg/specgenutil/specgen.go b/pkg/specgenutil/specgen.go index 8c2c59fed..439a13385 100644 --- a/pkg/specgenutil/specgen.go +++ b/pkg/specgenutil/specgen.go @@ -20,7 +20,6 @@ import ( "github.com/containers/podman/v4/pkg/specgen" systemdDefine "github.com/containers/podman/v4/pkg/systemd/define" "github.com/containers/podman/v4/pkg/util" - "github.com/docker/docker/opts" "github.com/docker/go-units" "github.com/opencontainers/runtime-spec/specs-go" ) @@ -461,11 +460,12 @@ func FillOutSpecGen(s *specgen.SpecGenerator, c *entities.ContainerCreateOptions // SHM Size if c.ShmSize != "" { - var m opts.MemBytes - if err := m.Set(c.ShmSize); err != nil { + val, err := units.RAMInBytes(c.ShmSize) + + if err != nil { return fmt.Errorf("unable to translate --shm-size: %w", err) } - val := m.Value() + s.ShmSize = &val } @@ -507,44 +507,9 @@ func FillOutSpecGen(s *specgen.SpecGenerator, c *entities.ContainerCreateOptions s.ResourceLimits = &specs.LinuxResources{} } - if s.ResourceLimits.Memory == nil || (len(c.Memory) != 0 || len(c.MemoryReservation) != 0 || len(c.MemorySwap) != 0 || c.MemorySwappiness != 0) { - s.ResourceLimits.Memory, err = getMemoryLimits(c) - if err != nil { - return err - } - } - if s.ResourceLimits.BlockIO == nil || (len(c.BlkIOWeight) != 0 || len(c.BlkIOWeightDevice) != 0 || len(c.DeviceReadBPs) != 0 || len(c.DeviceWriteBPs) != 0) { - 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 - } - - if s.ResourceLimits.CPU == nil || (c.CPUPeriod != 0 || c.CPUQuota != 0 || c.CPURTPeriod != 0 || c.CPURTRuntime != 0 || c.CPUS != 0 || len(c.CPUSetCPUs) != 0 || len(c.CPUSetMems) != 0 || c.CPUShares != 0) { - 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.New("--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 + s.ResourceLimits, err = GetResources(s, c) + if err != nil { + return err } if s.LogConfiguration == nil { @@ -1171,3 +1136,47 @@ func parseLinuxResourcesDeviceAccess(device string) (specs.LinuxDeviceCgroup, er Access: access, }, nil } + +func GetResources(s *specgen.SpecGenerator, c *entities.ContainerCreateOptions) (*specs.LinuxResources, error) { + var err error + if s.ResourceLimits.Memory == nil || (len(c.Memory) != 0 || len(c.MemoryReservation) != 0 || len(c.MemorySwap) != 0 || c.MemorySwappiness != 0) { + s.ResourceLimits.Memory, err = getMemoryLimits(c) + if err != nil { + return nil, err + } + } + if s.ResourceLimits.BlockIO == nil || (len(c.BlkIOWeight) != 0 || len(c.BlkIOWeightDevice) != 0 || len(c.DeviceReadBPs) != 0 || len(c.DeviceWriteBPs) != 0) { + s.ResourceLimits.BlockIO, err = getIOLimits(s, c) + if err != nil { + return nil, err + } + } + if c.PIDsLimit != nil { + pids := specs.LinuxPids{ + Limit: *c.PIDsLimit, + } + + s.ResourceLimits.Pids = &pids + } + + if s.ResourceLimits.CPU == nil || (c.CPUPeriod != 0 || c.CPUQuota != 0 || c.CPURTPeriod != 0 || c.CPURTRuntime != 0 || c.CPUS != 0 || len(c.CPUSetCPUs) != 0 || len(c.CPUSetMems) != 0 || c.CPUShares != 0) { + 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 nil, errors.New("--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 + } + return s.ResourceLimits, nil +} diff --git a/pkg/util/utils.go b/pkg/util/utils.go index 33c11d611..87e403986 100644 --- a/pkg/util/utils.go +++ b/pkg/util/utils.go @@ -342,7 +342,7 @@ func ParseSignal(rawSignal string) (syscall.Signal, error) { } // GetKeepIDMapping returns the mappings and the user to use when keep-id is used -func GetKeepIDMapping() (*stypes.IDMappingOptions, int, int, error) { +func GetKeepIDMapping(opts *namespaces.KeepIDUserNsOptions) (*stypes.IDMappingOptions, int, int, error) { if !rootless.IsRootless() { return nil, -1, -1, errors.New("keep-id is only supported in rootless mode") } @@ -359,6 +359,12 @@ func GetKeepIDMapping() (*stypes.IDMappingOptions, int, int, error) { uid := rootless.GetRootlessUID() gid := rootless.GetRootlessGID() + if opts.UID != nil { + uid = int(*opts.UID) + } + if opts.GID != nil { + gid = int(*opts.GID) + } uids, gids, err := rootless.GetConfiguredMappings() if err != nil { diff --git a/test/apiv2/20-containers.at b/test/apiv2/20-containers.at index 655462f16..9ace46b8b 100644 --- a/test/apiv2/20-containers.at +++ b/test/apiv2/20-containers.at @@ -309,7 +309,9 @@ t POST containers/create Image=${MultiTagName} 201 \ .Id~[0-9a-f]\\{64\\} cid=$(jq -r '.Id' <<<"$output") t GET containers/$cid/json 200 \ - .Image=${MultiTagName} + .Config.Image=${MultiTagName} \ + .Image~sha256:[0-9a-f]\\{64\\} + t DELETE containers/$cid 204 t DELETE images/${MultiTagName} 200 # vim: filetype=sh @@ -545,6 +547,21 @@ t GET libpod/containers/$cname/json 200 \ .ImageName=$IMAGE \ .Name=$cname +if root; then + podman run -dt --name=updateCtr alpine + echo '{"Memory":{"Limit":500000}, "CPU":{"Shares":123}}' >${TMPD}/update.json + t POST libpod/containers/updateCtr/update ${TMPD}/update.json 201 + + # Verify + echo '{ "AttachStdout":true,"Cmd":["cat","/sys/fs/cgroup/cpu.weight"]}' >${TMPD}/exec.json + t POST containers/updateCtr/exec ${TMPD}/exec.json 201 .Id~[0-9a-f]\\{64\\} + eid=$(jq -r '.Id' <<<"$output") + # 002 is the byte length + t POST exec/$eid/start 200 $'\001\0025' + + podman rm -f updateCtr +fi + rm -rf $TMPD podman container rm -fa diff --git a/test/apiv2/70-short-names.at b/test/apiv2/70-short-names.at index 95cf0e1d0..952dd2ad1 100644 --- a/test/apiv2/70-short-names.at +++ b/test/apiv2/70-short-names.at @@ -9,7 +9,7 @@ t POST "images/create?fromImage=quay.io/libpod/alpine:latest" 200 .error~null .s # 14291 - let a short-name resolve to a *local* non Docker-Hub image. t POST containers/create Image=alpine 201 .Id~[0-9a-f]\\{64\\} cid=$(jq -r '.Id' <<<"$output") -t GET containers/$cid/json 200 .Image="quay.io/libpod/alpine:latest" +t GET containers/$cid/json 200 .Config.Image="quay.io/libpod/alpine:latest" .Image~sha256:[0-9a-f]\\{64\\} podman rm -f $cid ########## TAG diff --git a/test/apiv2/python/rest_api/test_v2_0_0_container.py b/test/apiv2/python/rest_api/test_v2_0_0_container.py index a6cd93a1a..25596a9b7 100644 --- a/test/apiv2/python/rest_api/test_v2_0_0_container.py +++ b/test/apiv2/python/rest_api/test_v2_0_0_container.py @@ -359,8 +359,6 @@ class ContainerTestCase(APITestCase): self.assertEqual(2000, out["HostConfig"]["MemorySwap"]) self.assertEqual(1000, out["HostConfig"]["Memory"]) - - def execute_process(cmd): return subprocess.run( cmd, diff --git a/test/e2e/generate_kube_test.go b/test/e2e/generate_kube_test.go index e7ceaf2d2..6a73d8ab6 100644 --- a/test/e2e/generate_kube_test.go +++ b/test/e2e/generate_kube_test.go @@ -71,6 +71,8 @@ var _ = Describe("Podman generate kube", func() { Expect(pod.Spec.Containers[0]).To(HaveField("WorkingDir", "")) Expect(pod.Spec.Containers[0].Env).To(BeNil()) Expect(pod).To(HaveField("Name", "top-pod")) + enableServiceLinks := false + Expect(pod.Spec).To(HaveField("EnableServiceLinks", &enableServiceLinks)) numContainers := 0 for range pod.Spec.Containers { @@ -165,6 +167,8 @@ var _ = Describe("Podman generate kube", func() { err := yaml.Unmarshal(kube.Out.Contents(), pod) Expect(err).To(BeNil()) Expect(pod.Spec).To(HaveField("HostNetwork", false)) + enableServiceLinks := false + Expect(pod.Spec).To(HaveField("EnableServiceLinks", &enableServiceLinks)) numContainers := 0 for range pod.Spec.Containers { @@ -550,6 +554,11 @@ var _ = Describe("Podman generate kube", func() { It("podman generate kube on pod with ports", func() { podName := "test" + + lock4 := GetPortLock("4000") + defer lock4.Unlock() + lock5 := GetPortLock("5000") + defer lock5.Unlock() podSession := podmanTest.Podman([]string{"pod", "create", "--name", podName, "-p", "4000:4000", "-p", "5000:5000"}) podSession.WaitWithDefaultTimeout() Expect(podSession).Should(Exit(0)) diff --git a/test/e2e/play_kube_test.go b/test/e2e/play_kube_test.go index 1b4eefd45..d1eb960cd 100644 --- a/test/e2e/play_kube_test.go +++ b/test/e2e/play_kube_test.go @@ -509,6 +509,9 @@ spec: volumes: {{ range . }} - name: {{ .Name }} + {{- if (eq .VolumeType "EmptyDir") }} + emptyDir: {} + {{- end }} {{- if (eq .VolumeType "HostPath") }} hostPath: path: {{ .HostPath.Path }} @@ -1242,12 +1245,15 @@ type ConfigMap struct { Optional bool } +type EmptyDir struct{} + type Volume struct { VolumeType string Name string HostPath PersistentVolumeClaim ConfigMap + EmptyDir } // getHostPathVolume takes a type and a location for a HostPath @@ -1289,6 +1295,14 @@ func getConfigMapVolume(vName string, items []map[string]string, optional bool) } } +func getEmptyDirVolume() *Volume { + return &Volume{ + VolumeType: "EmptyDir", + Name: defaultVolName, + EmptyDir: EmptyDir{}, + } +} + type Env struct { Name string Value string @@ -2482,7 +2496,7 @@ spec: It("podman play kube test with network portbindings", func() { ip := "127.0.0.100" - port := "5000" + port := "8087" ctr := getCtr(withHostIP(ip, port), withImage(BB)) pod := getPod(withCtr(ctr)) @@ -2496,7 +2510,7 @@ spec: inspect := podmanTest.Podman([]string{"port", getCtrNameInPod(pod)}) inspect.WaitWithDefaultTimeout() Expect(inspect).Should(Exit(0)) - Expect(inspect.OutputToString()).To(Equal("5000/tcp -> 127.0.0.100:5000")) + Expect(inspect.OutputToString()).To(Equal("8087/tcp -> 127.0.0.100:8087")) }) It("podman play kube test with nonexistent empty HostPath type volume", func() { @@ -2762,6 +2776,43 @@ VOLUME %s`, ALPINE, hostPathDir+"/") Expect(kube).Should(Exit(0)) }) + It("podman play kube with emptyDir volume", func() { + podName := "test-pod" + ctrName1 := "vol-test-ctr" + ctrName2 := "vol-test-ctr-2" + ctr1 := getCtr(withVolumeMount("/test-emptydir", false), withImage(BB), withName(ctrName1)) + ctr2 := getCtr(withVolumeMount("/test-emptydir-2", false), withImage(BB), withName(ctrName2)) + pod := getPod(withPodName(podName), withVolume(getEmptyDirVolume()), withCtr(ctr1), withCtr(ctr2)) + err = generateKubeYaml("pod", pod, kubeYaml) + Expect(err).To(BeNil()) + + kube := podmanTest.Podman([]string{"play", "kube", kubeYaml}) + kube.WaitWithDefaultTimeout() + Expect(kube).Should(Exit(0)) + + emptyDirCheck1 := podmanTest.Podman([]string{"exec", podName + "-" + ctrName1, "ls", "/test-emptydir"}) + emptyDirCheck1.WaitWithDefaultTimeout() + Expect(emptyDirCheck1).Should(Exit(0)) + + emptyDirCheck2 := podmanTest.Podman([]string{"exec", podName + "-" + ctrName2, "ls", "/test-emptydir-2"}) + emptyDirCheck2.WaitWithDefaultTimeout() + Expect(emptyDirCheck2).Should(Exit(0)) + + volList1 := podmanTest.Podman([]string{"volume", "ls", "-q"}) + volList1.WaitWithDefaultTimeout() + Expect(volList1).Should(Exit(0)) + Expect(volList1.OutputToString()).To(Equal(defaultVolName)) + + remove := podmanTest.Podman([]string{"pod", "rm", "-f", podName}) + remove.WaitWithDefaultTimeout() + Expect(remove).Should(Exit(0)) + + volList2 := podmanTest.Podman([]string{"volume", "ls", "-q"}) + volList2.WaitWithDefaultTimeout() + Expect(volList2).Should(Exit(0)) + Expect(volList2.OutputToString()).To(Equal("")) + }) + It("podman play kube applies labels to pods", func() { var numReplicas int32 = 5 expectedLabelKey := "key1" diff --git a/test/e2e/run_userns_test.go b/test/e2e/run_userns_test.go index 62e512d3a..016f67bf6 100644 --- a/test/e2e/run_userns_test.go +++ b/test/e2e/run_userns_test.go @@ -113,6 +113,16 @@ var _ = Describe("Podman UserNS support", func() { Expect(session).Should(Exit(0)) uid := fmt.Sprintf("%d", os.Geteuid()) Expect(session.OutputToString()).To(ContainSubstring(uid)) + + session = podmanTest.Podman([]string{"run", "--userns=keep-id:uid=10,gid=12", "alpine", "sh", "-c", "echo $(id -u):$(id -g)"}) + session.WaitWithDefaultTimeout() + if os.Geteuid() == 0 { + Expect(session).Should(Exit(125)) + return + } + + Expect(session).Should(Exit(0)) + Expect(session.OutputToString()).To(ContainSubstring("10:12")) }) It("podman --userns=keep-id check passwd", func() { diff --git a/test/e2e/save_test.go b/test/e2e/save_test.go index 94c363dd4..afb723a63 100644 --- a/test/e2e/save_test.go +++ b/test/e2e/save_test.go @@ -153,6 +153,9 @@ var _ = Describe("Podman save", func() { defer os.Setenv("GNUPGHOME", origGNUPGHOME) port := 5000 + portlock := GetPortLock(strconv.Itoa(port)) + defer portlock.Unlock() + session := podmanTest.Podman([]string{"run", "-d", "--name", "registry", "-p", strings.Join([]string{strconv.Itoa(port), strconv.Itoa(port)}, ":"), REGISTRY_IMAGE}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) diff --git a/test/e2e/update_test.go b/test/e2e/update_test.go new file mode 100644 index 000000000..97dadd04c --- /dev/null +++ b/test/e2e/update_test.go @@ -0,0 +1,200 @@ +package integration + +import ( + "github.com/containers/common/pkg/cgroupv2" + . "github.com/containers/podman/v4/test/utils" + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + . "github.com/onsi/gomega/gexec" +) + +var _ = Describe("Podman update", func() { + var ( + tempdir string + err error + podmanTest *PodmanTestIntegration + ) + + BeforeEach(func() { + tempdir, err = CreateTempDirInTempDir() + Expect(err).To(BeNil()) + podmanTest = PodmanTestCreate(tempdir) + podmanTest.Setup() + }) + + AfterEach(func() { + podmanTest.Cleanup() + f := CurrentGinkgoTestDescription() + processTestResult(f) + + }) + + It("podman update container all options v1", func() { + SkipIfCgroupV2("testing flags that only work in cgroup v1") + SkipIfRootless("many of these handlers are not enabled while rootless in CI") + session := podmanTest.Podman([]string{"run", "-dt", ALPINE}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + + ctrID := session.OutputToString() + + commonArgs := []string{ + "update", + "--cpus", "5", + "--cpuset-cpus", "0", + "--cpu-shares", "123", + "--cpuset-mems", "0", + "--memory", "1G", + "--memory-swap", "2G", + "--memory-reservation", "2G", + "--memory-swappiness", "50", ctrID} + + session = podmanTest.Podman(commonArgs) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + + // checking cpu quota from --cpus + session = podmanTest.Podman([]string{"exec", "-it", ctrID, "cat", "/sys/fs/cgroup/cpu/cpu.cfs_quota_us"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + Expect(session.OutputToString()).Should(ContainSubstring("500000")) + + // checking cpuset-cpus + session = podmanTest.Podman([]string{"exec", "-it", ctrID, "cat", "/sys/fs/cgroup/cpuset/cpuset.cpus"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + Expect(session.OutputToString()).Should(Equal("0")) + + // checking cpuset-mems + session = podmanTest.Podman([]string{"exec", "-it", ctrID, "cat", "/sys/fs/cgroup/cpuset/cpuset.mems"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + Expect(session.OutputToString()).Should(Equal("0")) + + // checking memory limit + session = podmanTest.Podman([]string{"exec", "-it", ctrID, "cat", "/sys/fs/cgroup/memory/memory.limit_in_bytes"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + Expect(session.OutputToString()).Should(ContainSubstring("1073741824")) + + // checking memory-swap + session = podmanTest.Podman([]string{"exec", "-it", ctrID, "cat", "/sys/fs/cgroup/memory/memory.memsw.limit_in_bytes"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + Expect(session.OutputToString()).Should(ContainSubstring("2147483648")) + + // checking cpu-shares + session = podmanTest.Podman([]string{"exec", "-it", ctrID, "cat", "/sys/fs/cgroup/cpu/cpu.shares"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + Expect(session.OutputToString()).Should(ContainSubstring("123")) + + }) + + It("podman update container all options v2", func() { + SkipIfCgroupV1("testing flags that only work in cgroup v2") + SkipIfRootless("many of these handlers are not enabled while rootless in CI") + session := podmanTest.Podman([]string{"run", "-dt", ALPINE}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + + ctrID := session.OutputToString() + + commonArgs := []string{ + "update", + "--cpus", "5", + "--cpuset-cpus", "0", + "--cpu-shares", "123", + "--cpuset-mems", "0", + "--memory", "1G", + "--memory-swap", "2G", + "--memory-reservation", "2G", + "--blkio-weight", "123", + "--device-read-bps", "/dev/zero:10mb", + "--device-write-bps", "/dev/zero:10mb", + "--device-read-iops", "/dev/zero:1000", + "--device-write-iops", "/dev/zero:1000", + ctrID} + + session = podmanTest.Podman(commonArgs) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + + ctrID = session.OutputToString() + + // checking cpu quota and period + session = podmanTest.Podman([]string{"exec", "-it", ctrID, "cat", "/sys/fs/cgroup/cpu.max"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + Expect(session.OutputToString()).Should(ContainSubstring("500000")) + + // checking blkio weight + session = podmanTest.Podman([]string{"exec", "-it", ctrID, "cat", "/sys/fs/cgroup/io.bfq.weight"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + Expect(session.OutputToString()).Should(ContainSubstring("123")) + + // checking device-read/write-bps/iops + session = podmanTest.Podman([]string{"exec", "-it", ctrID, "cat", "/sys/fs/cgroup/io.max"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + Expect(session.OutputToString()).Should(ContainSubstring("rbps=10485760 wbps=10485760 riops=1000 wiops=1000")) + + // checking cpuset-cpus + session = podmanTest.Podman([]string{"exec", "-it", ctrID, "cat", "/sys/fs/cgroup/cpuset.cpus"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + Expect(session.OutputToString()).Should(Equal("0")) + + // checking cpuset-mems + session = podmanTest.Podman([]string{"exec", "-it", ctrID, "cat", "/sys/fs/cgroup/cpuset.mems"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + Expect(session.OutputToString()).Should(Equal("0")) + + // checking memory limit + session = podmanTest.Podman([]string{"exec", "-it", ctrID, "cat", "/sys/fs/cgroup/memory.max"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + Expect(session.OutputToString()).Should(ContainSubstring("1073741824")) + + // checking memory-swap + session = podmanTest.Podman([]string{"exec", "-it", ctrID, "cat", "/sys/fs/cgroup/memory.swap.max"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + Expect(session.OutputToString()).Should(ContainSubstring("1073741824")) + + // checking cpu-shares + session = podmanTest.Podman([]string{"exec", "-it", ctrID, "cat", "/sys/fs/cgroup/cpu.weight"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + Expect(session.OutputToString()).Should(ContainSubstring("5")) + }) + + It("podman update keep original resources if not overridden", func() { + SkipIfRootless("many of these handlers are not enabled while rootless in CI") + session := podmanTest.Podman([]string{"run", "-dt", "--cpus", "5", ALPINE}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + + session = podmanTest.Podman([]string{ + "update", + "--memory", "1G", + session.OutputToString(), + }) + + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + + ctrID := session.OutputToString() + + if v2, _ := cgroupv2.Enabled(); v2 { + session = podmanTest.Podman([]string{"exec", "-it", ctrID, "cat", "/sys/fs/cgroup/cpu.max"}) + } else { + session = podmanTest.Podman([]string{"exec", "-it", ctrID, "cat", "/sys/fs/cgroup/cpu/cpu.cfs_quota_us"}) + } + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + Expect(session.OutputToString()).Should(ContainSubstring("500000")) + }) +}) diff --git a/test/system/090-events.bats b/test/system/090-events.bats index ceb53ae73..cee0e23b0 100644 --- a/test/system/090-events.bats +++ b/test/system/090-events.bats @@ -64,7 +64,7 @@ load helpers run_podman --events-backend=file tag $IMAGE $tag run_podman --events-backend=file untag $IMAGE $tag run_podman --events-backend=file tag $IMAGE $tag - run_podman --events-backend=file rmi $tag + run_podman --events-backend=file rmi -f $imageID run_podman --events-backend=file events --stream=false --filter type=image --since $t0 is "$output" ".*image push $imageID dir:$pushedDir @@ -74,7 +74,8 @@ load helpers .*image tag $imageID $tag .*image untag $imageID $tag:latest .*image tag $imageID $tag -.*image remove $imageID $tag.*" \ +.*image untag $imageID $tag:latest +.*image remove $imageID $imageID" \ "podman events" } diff --git a/test/system/250-systemd.bats b/test/system/250-systemd.bats index 9a91501dd..8f4471f91 100644 --- a/test/system/250-systemd.bats +++ b/test/system/250-systemd.bats @@ -73,6 +73,11 @@ function service_cleanup() { # These tests can fail in dev. environment because of SELinux. # quick fix: chcon -t container_runtime_exec_t ./bin/podman @test "podman generate - systemd - basic" { + # Flakes with "ActiveState=failed (expected =inactive)" + if is_ubuntu; then + skip "FIXME: 2022-09-01: requires conmon-2.1.4, ubuntu has 2.1.3" + fi + cname=$(random_string) # See #7407 for --pull=always. run_podman create --pull=always --name $cname --label "io.containers.autoupdate=registry" $IMAGE \ diff --git a/test/system/710-kube.bats b/test/system/710-kube.bats index 58e42148a..c446ff65f 100644 --- a/test/system/710-kube.bats +++ b/test/system/710-kube.bats @@ -78,11 +78,6 @@ status | = | null assert "$actual" $op "$expect" ".$key" done < <(parse_table "$expect") - if ! is_remote; then - count=$(egrep -c "$kubernetes_63" <<<"$output") - assert "$count" = 1 "1 instance of the Kubernetes-63-char warning" - fi - run_podman rm $cname } @@ -157,12 +152,6 @@ status | = | {} assert "$actual" $op "$expect" ".$key" done < <(parse_table "$expect") - # Why 4? Maybe two for each container? - if ! is_remote; then - count=$(egrep -c "$kubernetes_63" <<<"$output") - assert "$count" = 4 "instances of the Kubernetes-63-char warning" - fi - run_podman rm $cname1 $cname2 run_podman pod rm $pname run_podman rmi $(pause_image) diff --git a/vendor/github.com/containers/common/libimage/image.go b/vendor/github.com/containers/common/libimage/image.go index d1548eb23..3cc843ed3 100644 --- a/vendor/github.com/containers/common/libimage/image.go +++ b/vendor/github.com/containers/common/libimage/image.go @@ -475,7 +475,11 @@ func (i *Image) removeRecursive(ctx context.Context, rmMap map[string]*RemoveIma } return processedIDs, err } + report.Untagged = append(report.Untagged, i.Names()...) + for _, name := range i.Names() { + i.runtime.writeEvent(&Event{ID: i.ID(), Name: name, Time: time.Now(), Type: EventTypeImageUntag}) + } if !hasChildren { report.Removed = true diff --git a/vendor/github.com/docker/docker/opts/address_pools.go b/vendor/github.com/docker/docker/opts/address_pools.go deleted file mode 100644 index fa15c24b9..000000000 --- a/vendor/github.com/docker/docker/opts/address_pools.go +++ /dev/null @@ -1,84 +0,0 @@ -package opts - -import ( - "encoding/csv" - "encoding/json" - "fmt" - "strconv" - "strings" - - types "github.com/docker/libnetwork/ipamutils" -) - -// PoolsOpt is a Value type for parsing the default address pools definitions -type PoolsOpt struct { - Values []*types.NetworkToSplit -} - -// UnmarshalJSON fills values structure info from JSON input -func (p *PoolsOpt) UnmarshalJSON(raw []byte) error { - return json.Unmarshal(raw, &(p.Values)) -} - -// Set predefined pools -func (p *PoolsOpt) Set(value string) error { - csvReader := csv.NewReader(strings.NewReader(value)) - fields, err := csvReader.Read() - if err != nil { - return err - } - - poolsDef := types.NetworkToSplit{} - - for _, field := range fields { - parts := strings.SplitN(field, "=", 2) - if len(parts) != 2 { - return fmt.Errorf("invalid field '%s' must be a key=value pair", field) - } - - key := strings.ToLower(parts[0]) - value := strings.ToLower(parts[1]) - - switch key { - case "base": - poolsDef.Base = value - case "size": - size, err := strconv.Atoi(value) - if err != nil { - return fmt.Errorf("invalid size value: %q (must be integer): %v", value, err) - } - poolsDef.Size = size - default: - return fmt.Errorf("unexpected key '%s' in '%s'", key, field) - } - } - - p.Values = append(p.Values, &poolsDef) - - return nil -} - -// Type returns the type of this option -func (p *PoolsOpt) Type() string { - return "pool-options" -} - -// String returns a string repr of this option -func (p *PoolsOpt) String() string { - var pools []string - for _, pool := range p.Values { - repr := fmt.Sprintf("%s %d", pool.Base, pool.Size) - pools = append(pools, repr) - } - return strings.Join(pools, ", ") -} - -// Value returns the mounts -func (p *PoolsOpt) Value() []*types.NetworkToSplit { - return p.Values -} - -// Name returns the flag name of this option -func (p *PoolsOpt) Name() string { - return "default-address-pools" -} diff --git a/vendor/github.com/docker/docker/opts/env.go b/vendor/github.com/docker/docker/opts/env.go deleted file mode 100644 index 97e1a8c8a..000000000 --- a/vendor/github.com/docker/docker/opts/env.go +++ /dev/null @@ -1,30 +0,0 @@ -package opts // import "github.com/docker/docker/opts" - -import ( - "os" - "strings" - - "github.com/pkg/errors" -) - -// ValidateEnv validates an environment variable and returns it. -// If no value is specified, it obtains its value from the current environment -// -// As on ParseEnvFile and related to #16585, environment variable names -// are not validate whatsoever, it's up to application inside docker -// to validate them or not. -// -// The only validation here is to check if name is empty, per #25099 -func ValidateEnv(val string) (string, error) { - arr := strings.SplitN(val, "=", 2) - if arr[0] == "" { - return "", errors.New("invalid environment variable: " + val) - } - if len(arr) > 1 { - return val, nil - } - if envVal, ok := os.LookupEnv(arr[0]); ok { - return arr[0] + "=" + envVal, nil - } - return val, nil -} diff --git a/vendor/github.com/docker/docker/opts/hosts.go b/vendor/github.com/docker/docker/opts/hosts.go deleted file mode 100644 index a3123adef..000000000 --- a/vendor/github.com/docker/docker/opts/hosts.go +++ /dev/null @@ -1,183 +0,0 @@ -package opts // import "github.com/docker/docker/opts" - -import ( - "fmt" - "net" - "net/url" - "path/filepath" - "strconv" - "strings" - - "github.com/docker/docker/pkg/homedir" -) - -const ( - // DefaultHTTPPort Default HTTP Port used if only the protocol is provided to -H flag e.g. dockerd -H tcp:// - // These are the IANA registered port numbers for use with Docker - // see http://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.xhtml?search=docker - DefaultHTTPPort = 2375 // Default HTTP Port - // DefaultTLSHTTPPort Default HTTP Port used when TLS enabled - DefaultTLSHTTPPort = 2376 // Default TLS encrypted HTTP Port - // DefaultUnixSocket Path for the unix socket. - // Docker daemon by default always listens on the default unix socket - DefaultUnixSocket = "/var/run/docker.sock" - // DefaultTCPHost constant defines the default host string used by docker on Windows - DefaultTCPHost = "tcp://" + DefaultHTTPHost + ":2375" - // DefaultTLSHost constant defines the default host string used by docker for TLS sockets - DefaultTLSHost = "tcp://" + DefaultHTTPHost + ":2376" - // DefaultNamedPipe defines the default named pipe used by docker on Windows - DefaultNamedPipe = `//./pipe/docker_engine` - // HostGatewayName is the string value that can be passed - // to the IPAddr section in --add-host that is replaced by - // the value of HostGatewayIP daemon config value - HostGatewayName = "host-gateway" -) - -// ValidateHost validates that the specified string is a valid host and returns it. -func ValidateHost(val string) (string, error) { - host := strings.TrimSpace(val) - // The empty string means default and is not handled by parseDaemonHost - if host != "" { - _, err := parseDaemonHost(host) - if err != nil { - return val, err - } - } - // Note: unlike most flag validators, we don't return the mutated value here - // we need to know what the user entered later (using ParseHost) to adjust for TLS - return val, nil -} - -// ParseHost and set defaults for a Daemon host string. -// defaultToTLS is preferred over defaultToUnixXDG. -func ParseHost(defaultToTLS, defaultToUnixXDG bool, val string) (string, error) { - host := strings.TrimSpace(val) - if host == "" { - if defaultToTLS { - host = DefaultTLSHost - } else if defaultToUnixXDG { - runtimeDir, err := homedir.GetRuntimeDir() - if err != nil { - return "", err - } - socket := filepath.Join(runtimeDir, "docker.sock") - host = "unix://" + socket - } else { - host = DefaultHost - } - } else { - var err error - host, err = parseDaemonHost(host) - if err != nil { - return val, err - } - } - return host, nil -} - -// parseDaemonHost parses the specified address and returns an address that will be used as the host. -// Depending of the address specified, this may return one of the global Default* strings defined in hosts.go. -func parseDaemonHost(addr string) (string, error) { - addrParts := strings.SplitN(addr, "://", 2) - if len(addrParts) == 1 && addrParts[0] != "" { - addrParts = []string{"tcp", addrParts[0]} - } - - switch addrParts[0] { - case "tcp": - return ParseTCPAddr(addrParts[1], DefaultTCPHost) - case "unix": - return parseSimpleProtoAddr("unix", addrParts[1], DefaultUnixSocket) - case "npipe": - return parseSimpleProtoAddr("npipe", addrParts[1], DefaultNamedPipe) - case "fd": - return addr, nil - default: - return "", fmt.Errorf("Invalid bind address format: %s", addr) - } -} - -// parseSimpleProtoAddr parses and validates that the specified address is a valid -// socket address for simple protocols like unix and npipe. It returns a formatted -// socket address, either using the address parsed from addr, or the contents of -// defaultAddr if addr is a blank string. -func parseSimpleProtoAddr(proto, addr, defaultAddr string) (string, error) { - addr = strings.TrimPrefix(addr, proto+"://") - if strings.Contains(addr, "://") { - return "", fmt.Errorf("Invalid proto, expected %s: %s", proto, addr) - } - if addr == "" { - addr = defaultAddr - } - return fmt.Sprintf("%s://%s", proto, addr), nil -} - -// ParseTCPAddr parses and validates that the specified address is a valid TCP -// address. It returns a formatted TCP address, either using the address parsed -// from tryAddr, or the contents of defaultAddr if tryAddr is a blank string. -// tryAddr is expected to have already been Trim()'d -// defaultAddr must be in the full `tcp://host:port` form -func ParseTCPAddr(tryAddr string, defaultAddr string) (string, error) { - if tryAddr == "" || tryAddr == "tcp://" { - return defaultAddr, nil - } - addr := strings.TrimPrefix(tryAddr, "tcp://") - if strings.Contains(addr, "://") || addr == "" { - return "", fmt.Errorf("Invalid proto, expected tcp: %s", tryAddr) - } - - defaultAddr = strings.TrimPrefix(defaultAddr, "tcp://") - defaultHost, defaultPort, err := net.SplitHostPort(defaultAddr) - if err != nil { - return "", err - } - // url.Parse fails for trailing colon on IPv6 brackets on Go 1.5, but - // not 1.4. See https://github.com/golang/go/issues/12200 and - // https://github.com/golang/go/issues/6530. - if strings.HasSuffix(addr, "]:") { - addr += defaultPort - } - - u, err := url.Parse("tcp://" + addr) - if err != nil { - return "", err - } - host, port, err := net.SplitHostPort(u.Host) - if err != nil { - // try port addition once - host, port, err = net.SplitHostPort(net.JoinHostPort(u.Host, defaultPort)) - } - if err != nil { - return "", fmt.Errorf("Invalid bind address format: %s", tryAddr) - } - - if host == "" { - host = defaultHost - } - if port == "" { - port = defaultPort - } - p, err := strconv.Atoi(port) - if err != nil && p == 0 { - return "", fmt.Errorf("Invalid bind address format: %s", tryAddr) - } - - return fmt.Sprintf("tcp://%s%s", net.JoinHostPort(host, port), u.Path), nil -} - -// ValidateExtraHost validates that the specified string is a valid extrahost and returns it. -// ExtraHost is in the form of name:ip where the ip has to be a valid ip (IPv4 or IPv6). -func ValidateExtraHost(val string) (string, error) { - // allow for IPv6 addresses in extra hosts by only splitting on first ":" - arr := strings.SplitN(val, ":", 2) - if len(arr) != 2 || len(arr[0]) == 0 { - return "", fmt.Errorf("bad format for add-host: %q", val) - } - // Skip IPaddr validation for special "host-gateway" string - if arr[1] != HostGatewayName { - if _, err := ValidateIPAddress(arr[1]); err != nil { - return "", fmt.Errorf("invalid IP address in add-host: %q", arr[1]) - } - } - return val, nil -} diff --git a/vendor/github.com/docker/docker/opts/hosts_unix.go b/vendor/github.com/docker/docker/opts/hosts_unix.go deleted file mode 100644 index 4b1c8512e..000000000 --- a/vendor/github.com/docker/docker/opts/hosts_unix.go +++ /dev/null @@ -1,12 +0,0 @@ -//go:build !windows -// +build !windows - -package opts // import "github.com/docker/docker/opts" - -const ( - // DefaultHTTPHost Default HTTP Host used if only port is provided to -H flag e.g. dockerd -H tcp://:8080 - DefaultHTTPHost = "localhost" - - // DefaultHost constant defines the default host string used by docker on other hosts than Windows - DefaultHost = "unix://" + DefaultUnixSocket -) diff --git a/vendor/github.com/docker/docker/opts/hosts_windows.go b/vendor/github.com/docker/docker/opts/hosts_windows.go deleted file mode 100644 index 576236ba4..000000000 --- a/vendor/github.com/docker/docker/opts/hosts_windows.go +++ /dev/null @@ -1,60 +0,0 @@ -package opts // import "github.com/docker/docker/opts" - -const ( - // TODO Windows. Identify bug in GOLang 1.5.1+ and/or Windows Server 2016 TP5. - // - // On Windows, this mitigates a problem with the default options of running - // a docker client against a local docker daemon on TP5. - // - // What was found that if the default host is "localhost", even if the client - // (and daemon as this is local) is not physically on a network, and the DNS - // cache is flushed (ipconfig /flushdns), then the client will pause for - // exactly one second when connecting to the daemon for calls. For example - // using docker run windowsservercore cmd, the CLI will send a create followed - // by an attach. You see the delay between the attach finishing and the attach - // being seen by the daemon. - // - // Here's some daemon debug logs with additional debug spew put in. The - // AfterWriteJSON log is the very last thing the daemon does as part of the - // create call. The POST /attach is the second CLI call. Notice the second - // time gap. - // - // time="2015-11-06T13:38:37.259627400-08:00" level=debug msg="After createRootfs" - // time="2015-11-06T13:38:37.263626300-08:00" level=debug msg="After setHostConfig" - // time="2015-11-06T13:38:37.267631200-08:00" level=debug msg="before createContainerPl...." - // time="2015-11-06T13:38:37.271629500-08:00" level=debug msg=ToDiskLocking.... - // time="2015-11-06T13:38:37.275643200-08:00" level=debug msg="loggin event...." - // time="2015-11-06T13:38:37.277627600-08:00" level=debug msg="logged event...." - // time="2015-11-06T13:38:37.279631800-08:00" level=debug msg="In defer func" - // time="2015-11-06T13:38:37.282628100-08:00" level=debug msg="After daemon.create" - // time="2015-11-06T13:38:37.286651700-08:00" level=debug msg="return 2" - // time="2015-11-06T13:38:37.289629500-08:00" level=debug msg="Returned from daemon.ContainerCreate" - // time="2015-11-06T13:38:37.311629100-08:00" level=debug msg="After WriteJSON" - // ... 1 second gap here.... - // time="2015-11-06T13:38:38.317866200-08:00" level=debug msg="Calling POST /v1.22/containers/984758282b842f779e805664b2c95d563adc9a979c8a3973e68c807843ee4757/attach" - // time="2015-11-06T13:38:38.326882500-08:00" level=info msg="POST /v1.22/containers/984758282b842f779e805664b2c95d563adc9a979c8a3973e68c807843ee4757/attach?stderr=1&stdin=1&stdout=1&stream=1" - // - // We suspect this is either a bug introduced in GOLang 1.5.1, or that a change - // in GOLang 1.5.1 (from 1.4.3) is exposing a bug in Windows. In theory, - // the Windows networking stack is supposed to resolve "localhost" internally, - // without hitting DNS, or even reading the hosts file (which is why localhost - // is commented out in the hosts file on Windows). - // - // We have validated that working around this using the actual IPv4 localhost - // address does not cause the delay. - // - // This does not occur with the docker client built with 1.4.3 on the same - // Windows build, regardless of whether the daemon is built using 1.5.1 - // or 1.4.3. It does not occur on Linux. We also verified we see the same thing - // on a cross-compiled Windows binary (from Linux). - // - // Final note: This is a mitigation, not a 'real' fix. It is still susceptible - // to the delay if a user were to do 'docker run -H=tcp://localhost:2375...' - // explicitly. - - // DefaultHTTPHost Default HTTP Host used if only port is provided to -H flag e.g. dockerd -H tcp://:8080 - DefaultHTTPHost = "127.0.0.1" - - // DefaultHost constant defines the default host string used by docker on Windows - DefaultHost = "npipe://" + DefaultNamedPipe -) diff --git a/vendor/github.com/docker/docker/opts/ip.go b/vendor/github.com/docker/docker/opts/ip.go deleted file mode 100644 index cfbff3a9f..000000000 --- a/vendor/github.com/docker/docker/opts/ip.go +++ /dev/null @@ -1,47 +0,0 @@ -package opts // import "github.com/docker/docker/opts" - -import ( - "fmt" - "net" -) - -// IPOpt holds an IP. It is used to store values from CLI flags. -type IPOpt struct { - *net.IP -} - -// NewIPOpt creates a new IPOpt from a reference net.IP and a -// string representation of an IP. If the string is not a valid -// IP it will fallback to the specified reference. -func NewIPOpt(ref *net.IP, defaultVal string) *IPOpt { - o := &IPOpt{ - IP: ref, - } - o.Set(defaultVal) - return o -} - -// Set sets an IPv4 or IPv6 address from a given string. If the given -// string is not parsable as an IP address it returns an error. -func (o *IPOpt) Set(val string) error { - ip := net.ParseIP(val) - if ip == nil { - return fmt.Errorf("%s is not an ip address", val) - } - *o.IP = ip - return nil -} - -// String returns the IP address stored in the IPOpt. If stored IP is a -// nil pointer, it returns an empty string. -func (o *IPOpt) String() string { - if *o.IP == nil { - return "" - } - return o.IP.String() -} - -// Type returns the type of the option -func (o *IPOpt) Type() string { - return "ip" -} diff --git a/vendor/github.com/docker/docker/opts/opts.go b/vendor/github.com/docker/docker/opts/opts.go deleted file mode 100644 index 60a093f28..000000000 --- a/vendor/github.com/docker/docker/opts/opts.go +++ /dev/null @@ -1,348 +0,0 @@ -package opts // import "github.com/docker/docker/opts" - -import ( - "fmt" - "net" - "path" - "regexp" - "strings" - - units "github.com/docker/go-units" -) - -var ( - alphaRegexp = regexp.MustCompile(`[a-zA-Z]`) - domainRegexp = regexp.MustCompile(`^(:?(:?[a-zA-Z0-9]|(:?[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9]))(:?\.(:?[a-zA-Z0-9]|(:?[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])))*)\.?\s*$`) -) - -// ListOpts holds a list of values and a validation function. -type ListOpts struct { - values *[]string - validator ValidatorFctType -} - -// NewListOpts creates a new ListOpts with the specified validator. -func NewListOpts(validator ValidatorFctType) ListOpts { - var values []string - return *NewListOptsRef(&values, validator) -} - -// NewListOptsRef creates a new ListOpts with the specified values and validator. -func NewListOptsRef(values *[]string, validator ValidatorFctType) *ListOpts { - return &ListOpts{ - values: values, - validator: validator, - } -} - -func (opts *ListOpts) String() string { - if len(*opts.values) == 0 { - return "" - } - return fmt.Sprintf("%v", *opts.values) -} - -// Set validates if needed the input value and adds it to the -// internal slice. -func (opts *ListOpts) Set(value string) error { - if opts.validator != nil { - v, err := opts.validator(value) - if err != nil { - return err - } - value = v - } - *opts.values = append(*opts.values, value) - return nil -} - -// Delete removes the specified element from the slice. -func (opts *ListOpts) Delete(key string) { - for i, k := range *opts.values { - if k == key { - *opts.values = append((*opts.values)[:i], (*opts.values)[i+1:]...) - return - } - } -} - -// GetMap returns the content of values in a map in order to avoid -// duplicates. -func (opts *ListOpts) GetMap() map[string]struct{} { - ret := make(map[string]struct{}) - for _, k := range *opts.values { - ret[k] = struct{}{} - } - return ret -} - -// GetAll returns the values of slice. -func (opts *ListOpts) GetAll() []string { - return *opts.values -} - -// GetAllOrEmpty returns the values of the slice -// or an empty slice when there are no values. -func (opts *ListOpts) GetAllOrEmpty() []string { - v := *opts.values - if v == nil { - return make([]string, 0) - } - return v -} - -// Get checks the existence of the specified key. -func (opts *ListOpts) Get(key string) bool { - for _, k := range *opts.values { - if k == key { - return true - } - } - return false -} - -// Len returns the amount of element in the slice. -func (opts *ListOpts) Len() int { - return len(*opts.values) -} - -// Type returns a string name for this Option type -func (opts *ListOpts) Type() string { - return "list" -} - -// WithValidator returns the ListOpts with validator set. -func (opts *ListOpts) WithValidator(validator ValidatorFctType) *ListOpts { - opts.validator = validator - return opts -} - -// NamedOption is an interface that list and map options -// with names implement. -type NamedOption interface { - Name() string -} - -// NamedListOpts is a ListOpts with a configuration name. -// This struct is useful to keep reference to the assigned -// field name in the internal configuration struct. -type NamedListOpts struct { - name string - ListOpts -} - -var _ NamedOption = &NamedListOpts{} - -// NewNamedListOptsRef creates a reference to a new NamedListOpts struct. -func NewNamedListOptsRef(name string, values *[]string, validator ValidatorFctType) *NamedListOpts { - return &NamedListOpts{ - name: name, - ListOpts: *NewListOptsRef(values, validator), - } -} - -// Name returns the name of the NamedListOpts in the configuration. -func (o *NamedListOpts) Name() string { - return o.name -} - -// MapOpts holds a map of values and a validation function. -type MapOpts struct { - values map[string]string - validator ValidatorFctType -} - -// Set validates if needed the input value and add it to the -// internal map, by splitting on '='. -func (opts *MapOpts) Set(value string) error { - if opts.validator != nil { - v, err := opts.validator(value) - if err != nil { - return err - } - value = v - } - vals := strings.SplitN(value, "=", 2) - if len(vals) == 1 { - (opts.values)[vals[0]] = "" - } else { - (opts.values)[vals[0]] = vals[1] - } - return nil -} - -// GetAll returns the values of MapOpts as a map. -func (opts *MapOpts) GetAll() map[string]string { - return opts.values -} - -func (opts *MapOpts) String() string { - return fmt.Sprintf("%v", opts.values) -} - -// Type returns a string name for this Option type -func (opts *MapOpts) Type() string { - return "map" -} - -// NewMapOpts creates a new MapOpts with the specified map of values and a validator. -func NewMapOpts(values map[string]string, validator ValidatorFctType) *MapOpts { - if values == nil { - values = make(map[string]string) - } - return &MapOpts{ - values: values, - validator: validator, - } -} - -// NamedMapOpts is a MapOpts struct with a configuration name. -// This struct is useful to keep reference to the assigned -// field name in the internal configuration struct. -type NamedMapOpts struct { - name string - MapOpts -} - -var _ NamedOption = &NamedMapOpts{} - -// NewNamedMapOpts creates a reference to a new NamedMapOpts struct. -func NewNamedMapOpts(name string, values map[string]string, validator ValidatorFctType) *NamedMapOpts { - return &NamedMapOpts{ - name: name, - MapOpts: *NewMapOpts(values, validator), - } -} - -// Name returns the name of the NamedMapOpts in the configuration. -func (o *NamedMapOpts) Name() string { - return o.name -} - -// ValidatorFctType defines a validator function that returns a validated string and/or an error. -type ValidatorFctType func(val string) (string, error) - -// ValidatorFctListType defines a validator function that returns a validated list of string and/or an error -type ValidatorFctListType func(val string) ([]string, error) - -// ValidateIPAddress validates an Ip address. -func ValidateIPAddress(val string) (string, error) { - var ip = net.ParseIP(strings.TrimSpace(val)) - if ip != nil { - return ip.String(), nil - } - return "", fmt.Errorf("%s is not an ip address", val) -} - -// ValidateDNSSearch validates domain for resolvconf search configuration. -// A zero length domain is represented by a dot (.). -func ValidateDNSSearch(val string) (string, error) { - if val = strings.Trim(val, " "); val == "." { - return val, nil - } - return validateDomain(val) -} - -func validateDomain(val string) (string, error) { - if alphaRegexp.FindString(val) == "" { - return "", fmt.Errorf("%s is not a valid domain", val) - } - ns := domainRegexp.FindSubmatch([]byte(val)) - if len(ns) > 0 && len(ns[1]) < 255 { - return string(ns[1]), nil - } - return "", fmt.Errorf("%s is not a valid domain", val) -} - -// ValidateLabel validates that the specified string is a valid label, -// it does not use the reserved namespaces com.docker.*, io.docker.*, org.dockerproject.* -// and returns it. -// Labels are in the form on key=value. -func ValidateLabel(val string) (string, error) { - if strings.Count(val, "=") < 1 { - return "", fmt.Errorf("bad attribute format: %s", val) - } - - lowered := strings.ToLower(val) - if strings.HasPrefix(lowered, "com.docker.") || strings.HasPrefix(lowered, "io.docker.") || - strings.HasPrefix(lowered, "org.dockerproject.") { - return "", fmt.Errorf( - "label %s is not allowed: the namespaces com.docker.*, io.docker.*, and org.dockerproject.* are reserved for internal use", - val) - } - - return val, nil -} - -// ValidateSingleGenericResource validates that a single entry in the -// generic resource list is valid. -// i.e 'GPU=UID1' is valid however 'GPU:UID1' or 'UID1' isn't -func ValidateSingleGenericResource(val string) (string, error) { - if strings.Count(val, "=") < 1 { - return "", fmt.Errorf("invalid node-generic-resource format `%s` expected `name=value`", val) - } - return val, nil -} - -// ParseLink parses and validates the specified string as a link format (name:alias) -func ParseLink(val string) (string, string, error) { - if val == "" { - return "", "", fmt.Errorf("empty string specified for links") - } - arr := strings.Split(val, ":") - if len(arr) > 2 { - return "", "", fmt.Errorf("bad format for links: %s", val) - } - if len(arr) == 1 { - return val, val, nil - } - // This is kept because we can actually get a HostConfig with links - // from an already created container and the format is not `foo:bar` - // but `/foo:/c1/bar` - if strings.HasPrefix(arr[0], "/") { - _, alias := path.Split(arr[1]) - return arr[0][1:], alias, nil - } - return arr[0], arr[1], nil -} - -// MemBytes is a type for human readable memory bytes (like 128M, 2g, etc) -type MemBytes int64 - -// String returns the string format of the human readable memory bytes -func (m *MemBytes) String() string { - // NOTE: In spf13/pflag/flag.go, "0" is considered as "zero value" while "0 B" is not. - // We return "0" in case value is 0 here so that the default value is hidden. - // (Sometimes "default 0 B" is actually misleading) - if m.Value() != 0 { - return units.BytesSize(float64(m.Value())) - } - return "0" -} - -// Set sets the value of the MemBytes by passing a string -func (m *MemBytes) Set(value string) error { - val, err := units.RAMInBytes(value) - *m = MemBytes(val) - return err -} - -// Type returns the type -func (m *MemBytes) Type() string { - return "bytes" -} - -// Value returns the value in int64 -func (m *MemBytes) Value() int64 { - return int64(*m) -} - -// UnmarshalJSON is the customized unmarshaler for MemBytes -func (m *MemBytes) UnmarshalJSON(s []byte) error { - if len(s) <= 2 || s[0] != '"' || s[len(s)-1] != '"' { - return fmt.Errorf("invalid size: %q", s) - } - val, err := units.RAMInBytes(string(s[1 : len(s)-1])) - *m = MemBytes(val) - return err -} diff --git a/vendor/github.com/docker/docker/opts/quotedstring.go b/vendor/github.com/docker/docker/opts/quotedstring.go deleted file mode 100644 index 6c889070e..000000000 --- a/vendor/github.com/docker/docker/opts/quotedstring.go +++ /dev/null @@ -1,37 +0,0 @@ -package opts // import "github.com/docker/docker/opts" - -// QuotedString is a string that may have extra quotes around the value. The -// quotes are stripped from the value. -type QuotedString struct { - value *string -} - -// Set sets a new value -func (s *QuotedString) Set(val string) error { - *s.value = trimQuotes(val) - return nil -} - -// Type returns the type of the value -func (s *QuotedString) Type() string { - return "string" -} - -func (s *QuotedString) String() string { - return *s.value -} - -func trimQuotes(value string) string { - lastIndex := len(value) - 1 - for _, char := range []byte{'\'', '"'} { - if value[0] == char && value[lastIndex] == char { - return value[1:lastIndex] - } - } - return value -} - -// NewQuotedString returns a new quoted string option -func NewQuotedString(value *string) *QuotedString { - return &QuotedString{value: value} -} diff --git a/vendor/github.com/docker/docker/opts/runtime.go b/vendor/github.com/docker/docker/opts/runtime.go deleted file mode 100644 index 4b9babf0a..000000000 --- a/vendor/github.com/docker/docker/opts/runtime.go +++ /dev/null @@ -1,79 +0,0 @@ -package opts // import "github.com/docker/docker/opts" - -import ( - "fmt" - "strings" - - "github.com/docker/docker/api/types" -) - -// RuntimeOpt defines a map of Runtimes -type RuntimeOpt struct { - name string - stockRuntimeName string - values *map[string]types.Runtime -} - -// NewNamedRuntimeOpt creates a new RuntimeOpt -func NewNamedRuntimeOpt(name string, ref *map[string]types.Runtime, stockRuntime string) *RuntimeOpt { - if ref == nil { - ref = &map[string]types.Runtime{} - } - return &RuntimeOpt{name: name, values: ref, stockRuntimeName: stockRuntime} -} - -// Name returns the name of the NamedListOpts in the configuration. -func (o *RuntimeOpt) Name() string { - return o.name -} - -// Set validates and updates the list of Runtimes -func (o *RuntimeOpt) Set(val string) error { - parts := strings.SplitN(val, "=", 2) - if len(parts) != 2 { - return fmt.Errorf("invalid runtime argument: %s", val) - } - - parts[0] = strings.TrimSpace(parts[0]) - parts[1] = strings.TrimSpace(parts[1]) - if parts[0] == "" || parts[1] == "" { - return fmt.Errorf("invalid runtime argument: %s", val) - } - - parts[0] = strings.ToLower(parts[0]) - if parts[0] == o.stockRuntimeName { - return fmt.Errorf("runtime name '%s' is reserved", o.stockRuntimeName) - } - - if _, ok := (*o.values)[parts[0]]; ok { - return fmt.Errorf("runtime '%s' was already defined", parts[0]) - } - - (*o.values)[parts[0]] = types.Runtime{Path: parts[1]} - - return nil -} - -// String returns Runtime values as a string. -func (o *RuntimeOpt) String() string { - var out []string - for k := range *o.values { - out = append(out, k) - } - - return fmt.Sprintf("%v", out) -} - -// GetMap returns a map of Runtimes (name: path) -func (o *RuntimeOpt) GetMap() map[string]types.Runtime { - if o.values != nil { - return *o.values - } - - return map[string]types.Runtime{} -} - -// Type returns the type of the option -func (o *RuntimeOpt) Type() string { - return "runtime" -} diff --git a/vendor/github.com/docker/docker/opts/ulimit.go b/vendor/github.com/docker/docker/opts/ulimit.go deleted file mode 100644 index 61cc58d4d..000000000 --- a/vendor/github.com/docker/docker/opts/ulimit.go +++ /dev/null @@ -1,81 +0,0 @@ -package opts // import "github.com/docker/docker/opts" - -import ( - "fmt" - - units "github.com/docker/go-units" -) - -// UlimitOpt defines a map of Ulimits -type UlimitOpt struct { - values *map[string]*units.Ulimit -} - -// NewUlimitOpt creates a new UlimitOpt -func NewUlimitOpt(ref *map[string]*units.Ulimit) *UlimitOpt { - if ref == nil { - ref = &map[string]*units.Ulimit{} - } - return &UlimitOpt{ref} -} - -// Set validates a Ulimit and sets its name as a key in UlimitOpt -func (o *UlimitOpt) Set(val string) error { - l, err := units.ParseUlimit(val) - if err != nil { - return err - } - - (*o.values)[l.Name] = l - - return nil -} - -// String returns Ulimit values as a string. -func (o *UlimitOpt) String() string { - var out []string - for _, v := range *o.values { - out = append(out, v.String()) - } - - return fmt.Sprintf("%v", out) -} - -// GetList returns a slice of pointers to Ulimits. -func (o *UlimitOpt) GetList() []*units.Ulimit { - var ulimits []*units.Ulimit - for _, v := range *o.values { - ulimits = append(ulimits, v) - } - - return ulimits -} - -// Type returns the option type -func (o *UlimitOpt) Type() string { - return "ulimit" -} - -// NamedUlimitOpt defines a named map of Ulimits -type NamedUlimitOpt struct { - name string - UlimitOpt -} - -var _ NamedOption = &NamedUlimitOpt{} - -// NewNamedUlimitOpt creates a new NamedUlimitOpt -func NewNamedUlimitOpt(name string, ref *map[string]*units.Ulimit) *NamedUlimitOpt { - if ref == nil { - ref = &map[string]*units.Ulimit{} - } - return &NamedUlimitOpt{ - name: name, - UlimitOpt: *NewUlimitOpt(ref), - } -} - -// Name returns the option name -func (o *NamedUlimitOpt) Name() string { - return o.name -} diff --git a/vendor/github.com/docker/libnetwork/LICENSE b/vendor/github.com/docker/libnetwork/LICENSE deleted file mode 100644 index e06d20818..000000000 --- a/vendor/github.com/docker/libnetwork/LICENSE +++ /dev/null @@ -1,202 +0,0 @@ -Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "{}" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright {yyyy} {name of copyright owner} - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - diff --git a/vendor/github.com/docker/libnetwork/ipamutils/utils.go b/vendor/github.com/docker/libnetwork/ipamutils/utils.go deleted file mode 100644 index 3fd37cd88..000000000 --- a/vendor/github.com/docker/libnetwork/ipamutils/utils.go +++ /dev/null @@ -1,135 +0,0 @@ -// Package ipamutils provides utility functions for ipam management -package ipamutils - -import ( - "fmt" - "net" - "sync" -) - -var ( - // PredefinedLocalScopeDefaultNetworks contains a list of 31 IPv4 private networks with host size 16 and 12 - // (172.17-31.x.x/16, 192.168.x.x/20) which do not overlap with the networks in `PredefinedGlobalScopeDefaultNetworks` - PredefinedLocalScopeDefaultNetworks []*net.IPNet - // PredefinedGlobalScopeDefaultNetworks contains a list of 64K IPv4 private networks with host size 8 - // (10.x.x.x/24) which do not overlap with the networks in `PredefinedLocalScopeDefaultNetworks` - PredefinedGlobalScopeDefaultNetworks []*net.IPNet - mutex sync.Mutex - localScopeDefaultNetworks = []*NetworkToSplit{{"172.17.0.0/16", 16}, {"172.18.0.0/16", 16}, {"172.19.0.0/16", 16}, - {"172.20.0.0/14", 16}, {"172.24.0.0/14", 16}, {"172.28.0.0/14", 16}, - {"192.168.0.0/16", 20}} - globalScopeDefaultNetworks = []*NetworkToSplit{{"10.0.0.0/8", 24}} -) - -// NetworkToSplit represent a network that has to be split in chunks with mask length Size. -// Each subnet in the set is derived from the Base pool. Base is to be passed -// in CIDR format. -// Example: a Base "10.10.0.0/16 with Size 24 will define the set of 256 -// 10.10.[0-255].0/24 address pools -type NetworkToSplit struct { - Base string `json:"base"` - Size int `json:"size"` -} - -func init() { - var err error - if PredefinedGlobalScopeDefaultNetworks, err = splitNetworks(globalScopeDefaultNetworks); err != nil { - //we are going to panic in case of error as we should never get into this state - panic("InitAddressPools failed to initialize the global scope default address pool") - } - - if PredefinedLocalScopeDefaultNetworks, err = splitNetworks(localScopeDefaultNetworks); err != nil { - //we are going to panic in case of error as we should never get into this state - panic("InitAddressPools failed to initialize the local scope default address pool") - } -} - -// configDefaultNetworks configures local as well global default pool based on input -func configDefaultNetworks(defaultAddressPool []*NetworkToSplit, result *[]*net.IPNet) error { - mutex.Lock() - defer mutex.Unlock() - defaultNetworks, err := splitNetworks(defaultAddressPool) - if err != nil { - return err - } - *result = defaultNetworks - return nil -} - -// GetGlobalScopeDefaultNetworks returns PredefinedGlobalScopeDefaultNetworks -func GetGlobalScopeDefaultNetworks() []*net.IPNet { - mutex.Lock() - defer mutex.Unlock() - return PredefinedGlobalScopeDefaultNetworks -} - -// GetLocalScopeDefaultNetworks returns PredefinedLocalScopeDefaultNetworks -func GetLocalScopeDefaultNetworks() []*net.IPNet { - mutex.Lock() - defer mutex.Unlock() - return PredefinedLocalScopeDefaultNetworks -} - -// ConfigGlobalScopeDefaultNetworks configures global default pool. -// Ideally this will be called from SwarmKit as part of swarm init -func ConfigGlobalScopeDefaultNetworks(defaultAddressPool []*NetworkToSplit) error { - if defaultAddressPool == nil { - defaultAddressPool = globalScopeDefaultNetworks - } - return configDefaultNetworks(defaultAddressPool, &PredefinedGlobalScopeDefaultNetworks) -} - -// ConfigLocalScopeDefaultNetworks configures local default pool. -// Ideally this will be called during libnetwork init -func ConfigLocalScopeDefaultNetworks(defaultAddressPool []*NetworkToSplit) error { - if defaultAddressPool == nil { - return nil - } - return configDefaultNetworks(defaultAddressPool, &PredefinedLocalScopeDefaultNetworks) -} - -// splitNetworks takes a slice of networks, split them accordingly and returns them -func splitNetworks(list []*NetworkToSplit) ([]*net.IPNet, error) { - localPools := make([]*net.IPNet, 0, len(list)) - - for _, p := range list { - _, b, err := net.ParseCIDR(p.Base) - if err != nil { - return nil, fmt.Errorf("invalid base pool %q: %v", p.Base, err) - } - ones, _ := b.Mask.Size() - if p.Size <= 0 || p.Size < ones { - return nil, fmt.Errorf("invalid pools size: %d", p.Size) - } - localPools = append(localPools, splitNetwork(p.Size, b)...) - } - return localPools, nil -} - -func splitNetwork(size int, base *net.IPNet) []*net.IPNet { - one, bits := base.Mask.Size() - mask := net.CIDRMask(size, bits) - n := 1 << uint(size-one) - s := uint(bits - size) - list := make([]*net.IPNet, 0, n) - - for i := 0; i < n; i++ { - ip := copyIP(base.IP) - addIntToIP(ip, uint(i<<s)) - list = append(list, &net.IPNet{IP: ip, Mask: mask}) - } - return list -} - -func copyIP(from net.IP) net.IP { - ip := make([]byte, len(from)) - copy(ip, from) - return ip -} - -func addIntToIP(array net.IP, ordinal uint) { - for i := len(array) - 1; i >= 0; i-- { - array[i] |= (byte)(ordinal & 0xff) - ordinal >>= 8 - } -} diff --git a/vendor/modules.txt b/vendor/modules.txt index db7e9b5bb..d80f64177 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -114,7 +114,7 @@ github.com/containers/buildah/pkg/rusage github.com/containers/buildah/pkg/sshagent github.com/containers/buildah/pkg/util github.com/containers/buildah/util -# github.com/containers/common v0.49.2-0.20220823130605-72a7da3358ac +# github.com/containers/common v0.49.2-0.20220826180622-c2dcb4e70340 ## explicit github.com/containers/common/libimage github.com/containers/common/libimage/define @@ -371,7 +371,6 @@ github.com/docker/docker/api/types/versions github.com/docker/docker/api/types/volume github.com/docker/docker/client github.com/docker/docker/errdefs -github.com/docker/docker/opts github.com/docker/docker/pkg/archive github.com/docker/docker/pkg/fileutils github.com/docker/docker/pkg/homedir @@ -401,9 +400,6 @@ github.com/docker/go-plugins-helpers/volume # github.com/docker/go-units v0.4.0 ## explicit github.com/docker/go-units -# github.com/docker/libnetwork v0.8.0-dev.2.0.20190625141545-5a177b73e316 -## explicit -github.com/docker/libnetwork/ipamutils # github.com/felixge/httpsnoop v1.0.1 github.com/felixge/httpsnoop # github.com/fsnotify/fsnotify v1.5.4 |