diff options
-rw-r--r-- | Makefile | 2 | ||||
-rw-r--r-- | cmd/podman/common/create.go | 16 | ||||
-rwxr-xr-x | contrib/cirrus/runner.sh | 12 | ||||
-rw-r--r-- | docs/source/markdown/podman-container-clone.1.md | 6 | ||||
-rw-r--r-- | docs/source/markdown/podman-machine-set.1.md | 4 | ||||
-rwxr-xr-x | hack/make-and-check-size | 30 | ||||
-rw-r--r-- | libpod/container_path_resolution.go | 1 | ||||
-rw-r--r-- | libpod/doc.go | 11 | ||||
-rw-r--r-- | libpod/oci_conmon_exec_linux.go | 7 | ||||
-rw-r--r-- | libpod/oci_conmon_linux.go | 6 | ||||
-rw-r--r-- | pkg/api/handlers/libpod/manifests.go | 2 | ||||
-rw-r--r-- | pkg/bindings/manifests/manifests.go | 64 | ||||
-rw-r--r-- | pkg/domain/infra/abi/containers.go | 29 | ||||
-rw-r--r-- | pkg/machine/qemu/machine.go | 22 | ||||
-rw-r--r-- | pkg/specgen/generate/container.go | 4 | ||||
-rw-r--r-- | pkg/specgen/generate/security.go | 7 | ||||
-rw-r--r-- | podman.spec.rpkg | 5 | ||||
-rw-r--r-- | test/e2e/container_clone_test.go | 37 | ||||
-rw-r--r-- | test/e2e/run_test.go | 6 |
19 files changed, 198 insertions, 73 deletions
@@ -292,7 +292,7 @@ validate: gofmt lint .gitvalidation validate.completions man-page-check swagger- .PHONY: build-all-new-commits build-all-new-commits: # Validate that all the commits build on top of $(GIT_BASE_BRANCH) - git rebase $(GIT_BASE_BRANCH) -x make + git rebase $(GIT_BASE_BRANCH) -x "$(MAKE)" .PHONY: vendor vendor: diff --git a/cmd/podman/common/create.go b/cmd/podman/common/create.go index 8d9a255ec..afaa1942e 100644 --- a/cmd/podman/common/create.go +++ b/cmd/podman/common/create.go @@ -394,14 +394,6 @@ func DefineCreateFlags(cmd *cobra.Command, cf *entities.ContainerCreateOptions, ) _ = cmd.RegisterFlagCompletionFunc(platformFlagName, completion.AutocompleteNone) - podFlagName := "pod" - createFlags.StringVar( - &cf.Pod, - podFlagName, "", - "Run container in an existing pod", - ) - _ = cmd.RegisterFlagCompletionFunc(podFlagName, AutocompletePods) - podIDFileFlagName := "pod-id-file" createFlags.StringVar( &cf.PodIDFile, @@ -837,6 +829,14 @@ func DefineCreateFlags(cmd *cobra.Command, cf *entities.ContainerCreateOptions, ) _ = cmd.RegisterFlagCompletionFunc(nameFlagName, completion.AutocompleteNone) + podFlagName := "pod" + createFlags.StringVar( + &cf.Pod, + podFlagName, "", + "Run container in an existing pod", + ) + _ = cmd.RegisterFlagCompletionFunc(podFlagName, AutocompletePods) + cpuPeriodFlagName := "cpu-period" createFlags.Uint64Var( &cf.CPUPeriod, diff --git a/contrib/cirrus/runner.sh b/contrib/cirrus/runner.sh index 1bff07203..f33c6af29 100755 --- a/contrib/cirrus/runner.sh +++ b/contrib/cirrus/runner.sh @@ -228,7 +228,17 @@ function _run_altbuild() { case "$ALT_NAME" in *Each*) git fetch origin - make build-all-new-commits GIT_BASE_BRANCH=origin/$DEST_BRANCH + # The check-size script, introduced 2022-03-22 in #13518, + # runs 'make' (the original purpose of this check) against + # each commit, then checks image sizes to make sure that + # none have grown beyond a given limit. That of course + # requires a baseline, which is why we use '^' to start + # with the *parent* commit of this PR, not the first commit. + context_dir=$(mktemp -d --tmpdir make-size-check.XXXXXXX) + make build-all-new-commits \ + GIT_BASE_BRANCH=origin/"${DEST_BRANCH}^" \ + MAKE="hack/make-and-check-size $context_dir" + rm -rf $context_dir ;; *Windows*) make podman-remote-release-windows_amd64.zip diff --git a/docs/source/markdown/podman-container-clone.1.md b/docs/source/markdown/podman-container-clone.1.md index 870bf077c..eaf330373 100644 --- a/docs/source/markdown/podman-container-clone.1.md +++ b/docs/source/markdown/podman-container-clone.1.md @@ -141,6 +141,12 @@ If no memory limits are specified, the original container's will be used. Set a custom name for the cloned container. The default if not specified is of the syntax: **<ORIGINAL_NAME>-clone** +#### **--pod**=*name* + +Clone the container in an existing pod. It is helpful to move a container to an +existing pod. The container will join the pod shared namespaces, losing its configuration +that conflicts with the shared namespaces. + #### **--run** When set to true, this flag runs the newly created container after the diff --git a/docs/source/markdown/podman-machine-set.1.md b/docs/source/markdown/podman-machine-set.1.md index ec89cfc28..a4918eacf 100644 --- a/docs/source/markdown/podman-machine-set.1.md +++ b/docs/source/markdown/podman-machine-set.1.md @@ -26,7 +26,9 @@ container execution. This option will also update the current podman remote connection default if it is currently pointing at the specified machine name (or `podman-machine-default` if no name is specified). -API forwarding, if available, will follow this setting. +Unlike [**podman system connection default**](podman-system-connection-default.1.md) +this option will also make the API socket, if available, forward to the rootful/rootless +socket in the VM. ## EXAMPLES diff --git a/hack/make-and-check-size b/hack/make-and-check-size index a6a77e8ca..71b382b44 100755 --- a/hack/make-and-check-size +++ b/hack/make-and-check-size @@ -2,28 +2,30 @@ # # make-and-check-size - wrapper around 'make' that also checks binary growth # -# This script is intended to be run via 'git rebase -x', in a Makefile rule -# such as: +# This script is intended to be run via 'git rebase -x', in a form such as: # -# build-all-new-commits: -# CONTEXT_DIR=$(shell mktemp -d --tmpdir make-size-check.XXXXXXX); \ -# git rebase $(GIT_BASE_BRANCH)^ -x "hack/make-and-check-size $$CONTEXT_DIR"; \ -# $(RM) -rf $$CONTEXT_DIR +# context_dir=$(mktemp -d --tmpdir make-size-check.XXXXXXX) +# git rebase ${GIT_BASE_BRANCH}^ -x "hack/make-and-check-size $context_dir" +# rm -rf $context_dir # -# ...which has long been a part of our usual CI, one that makes sure that -# each commit (in a multi-commit PR) can be compiled individually. By -# adding the '^' to GIT_BASE_BRANCH we establish a baseline and store +# (Carefully note the '^' next to GIT_BASE_BRANCH!) +# +# A 'git rebase -x' has long been a part of our usual CI; it guarantees +# that each commit (whether in a single- or multi-commit PR) can be +# compiled individually. +# +# By adding the '^' to GIT_BASE_BRANCH we establish a baseline and store # the binary sizes of each file (podman, podman-remote) prior to our PR. # -# CONTEXT_DIR is a temporary directory used to store the original sizes +# context_dir is a temporary directory used to store the original sizes # of each binary file under bin/ # # *IMPORTANT NOTE*: this script will leave the git checkout in a funky state! # (because we rebase onto a nonterminal commit). I believe this is OK, since -# this makefile target is used only in CI and only in a scratch VM. Running -# this in a development environment would yield unpredictable results anyway, -# by rebasing onto origin/main by default and by leaving an aborted rebase -# on failure. +# this script is only invoked in CI from runner.sh and only in a scratch VM. +# Running this in a development environment would yield unpredictable results +# anyway, by rebasing onto origin/main by default and by leaving an aborted +# rebase on failure. # ME=$(basename $0) diff --git a/libpod/container_path_resolution.go b/libpod/container_path_resolution.go index 7db23b783..80a3749f5 100644 --- a/libpod/container_path_resolution.go +++ b/libpod/container_path_resolution.go @@ -1,4 +1,3 @@ -// +linux package libpod import ( diff --git a/libpod/doc.go b/libpod/doc.go new file mode 100644 index 000000000..948153181 --- /dev/null +++ b/libpod/doc.go @@ -0,0 +1,11 @@ +// The libpod library is not stable and we do not support use cases outside of +// this repository. The API can change at any time even with patch releases. +// +// If you need a stable interface Podman provides a HTTP API which follows semver, +// please see https://docs.podman.io/en/latest/markdown/podman-system-service.1.html +// to start the api service and https://docs.podman.io/en/latest/_static/api.html +// for the API reference. +// +// We also provide stable go bindings to talk to the api service from another go +// program, see the pkg/bindings directory. +package libpod diff --git a/libpod/oci_conmon_exec_linux.go b/libpod/oci_conmon_exec_linux.go index aa970bbde..65123b37e 100644 --- a/libpod/oci_conmon_exec_linux.go +++ b/libpod/oci_conmon_exec_linux.go @@ -758,11 +758,14 @@ func prepareProcessExec(c *Container, options *ExecOptions, env []string, sessio } else { pspec.Capabilities.Bounding = ctrSpec.Process.Capabilities.Bounding } + + // Always unset the inheritable capabilities similarly to what the Linux kernel does + // They are used only when using capabilities with uid != 0. + pspec.Capabilities.Inheritable = []string{} + if execUser.Uid == 0 { pspec.Capabilities.Effective = pspec.Capabilities.Bounding - pspec.Capabilities.Inheritable = pspec.Capabilities.Bounding pspec.Capabilities.Permitted = pspec.Capabilities.Bounding - pspec.Capabilities.Ambient = pspec.Capabilities.Bounding } else { if user == c.config.User { pspec.Capabilities.Effective = ctrSpec.Process.Capabilities.Effective diff --git a/libpod/oci_conmon_linux.go b/libpod/oci_conmon_linux.go index ba4079bed..38bf85834 100644 --- a/libpod/oci_conmon_linux.go +++ b/libpod/oci_conmon_linux.go @@ -1587,11 +1587,13 @@ func readConmonPipeData(runtimeName string, pipe *os.File, ociLog string) (int, var si *syncInfo rdr := bufio.NewReader(pipe) b, err := rdr.ReadBytes('\n') - if err != nil { + // ignore EOF here, error is returned even when data was read + // if it is no valid json unmarshal will fail below + if err != nil && !errors.Is(err, io.EOF) { ch <- syncStruct{err: err} } if err := json.Unmarshal(b, &si); err != nil { - ch <- syncStruct{err: err} + ch <- syncStruct{err: fmt.Errorf("conmon bytes %q: %w", string(b), err)} return } ch <- syncStruct{si: si} diff --git a/pkg/api/handlers/libpod/manifests.go b/pkg/api/handlers/libpod/manifests.go index 250736579..ad662f32c 100644 --- a/pkg/api/handlers/libpod/manifests.go +++ b/pkg/api/handlers/libpod/manifests.go @@ -401,7 +401,7 @@ func ManifestModify(w http.ResponseWriter, r *http.Request) { case len(report.Errors) > 0 && len(report.Images) > 0: statusCode = http.StatusConflict case len(report.Errors) > 0: - statusCode = http.StatusInternalServerError + statusCode = http.StatusBadRequest } utils.WriteResponse(w, statusCode, report) } diff --git a/pkg/bindings/manifests/manifests.go b/pkg/bindings/manifests/manifests.go index 458cb913a..f7cd0d262 100644 --- a/pkg/bindings/manifests/manifests.go +++ b/pkg/bindings/manifests/manifests.go @@ -2,10 +2,9 @@ package manifests import ( "context" - "errors" "fmt" + "io/ioutil" "net/http" - "net/url" "strconv" "strings" @@ -14,8 +13,11 @@ import ( "github.com/containers/podman/v4/pkg/api/handlers" "github.com/containers/podman/v4/pkg/bindings" "github.com/containers/podman/v4/pkg/bindings/images" + "github.com/containers/podman/v4/pkg/domain/entities" + "github.com/containers/podman/v4/pkg/errorhandling" "github.com/containers/podman/v4/version" jsoniter "github.com/json-iterator/go" + "github.com/pkg/errors" ) // Create creates a manifest for the given name. Optional images to be associated with @@ -135,30 +137,8 @@ func Add(ctx context.Context, name string, options *AddOptions) (string, error) // Remove deletes a manifest entry from a manifest list. Both name and the digest to be // removed are mandatory inputs. The ID of the new manifest list is returned as a string. func Remove(ctx context.Context, name, digest string, _ *RemoveOptions) (string, error) { - if bindings.ServiceVersion(ctx).GTE(semver.MustParse("4.0.0")) { - optionsv4 := new(ModifyOptions).WithOperation("remove") - return Modify(ctx, name, []string{digest}, optionsv4) - } - - // API Version < 4.0.0 - conn, err := bindings.GetClient(ctx) - if err != nil { - return "", err - } - - headers := http.Header{} - headers.Add("API-Version", "3.4.0") - - params := url.Values{} - params.Set("digest", digest) - response, err := conn.DoRequest(ctx, nil, http.MethodDelete, "/manifests/%s", params, headers, name) - if err != nil { - return "", err - } - defer response.Body.Close() - - var idr handlers.IDResponse - return idr.ID, response.Process(&idr) + optionsv4 := new(ModifyOptions).WithOperation("remove") + return Modify(ctx, name, []string{digest}, optionsv4) } // Push takes a manifest list and pushes to a destination. If the destination is not specified, @@ -229,8 +209,36 @@ func Modify(ctx context.Context, name string, images []string, options *ModifyOp } defer response.Body.Close() - var idr handlers.IDResponse - return idr.ID, response.Process(&idr) + data, err := ioutil.ReadAll(response.Body) + if err != nil { + return "", errors.Wrap(err, "unable to process API response") + } + + if response.IsSuccess() || response.IsRedirection() { + var report entities.ManifestModifyReport + if err = jsoniter.Unmarshal(data, &report); err != nil { + return "", errors.Wrap(err, "unable to decode API response") + } + + err = errorhandling.JoinErrors(report.Errors) + if err != nil { + errModel := errorhandling.ErrorModel{ + Because: (errors.Cause(err)).Error(), + Message: err.Error(), + ResponseCode: response.StatusCode, + } + return report.ID, &errModel + } + return report.ID, nil + } + + errModel := errorhandling.ErrorModel{ + ResponseCode: response.StatusCode, + } + if err = jsoniter.Unmarshal(data, &errModel); err != nil { + return "", errors.Wrap(err, "unable to decode API response") + } + return "", &errModel } // Annotate modifies the given manifest list using options and the optional list of images diff --git a/pkg/domain/infra/abi/containers.go b/pkg/domain/infra/abi/containers.go index 1986228a6..f45bdeba5 100644 --- a/pkg/domain/infra/abi/containers.go +++ b/pkg/domain/infra/abi/containers.go @@ -1496,6 +1496,35 @@ func (ic *ContainerEngine) ContainerClone(ctx context.Context, ctrCloneOpts enti return nil, err } + if ctrCloneOpts.CreateOpts.Pod != "" { + pod, err := ic.Libpod.LookupPod(ctrCloneOpts.CreateOpts.Pod) + if err != nil { + return nil, err + } + + allNamespaces := []struct { + isShared bool + value *specgen.Namespace + }{ + {pod.SharesPID(), &spec.PidNS}, + {pod.SharesNet(), &spec.NetNS}, + {pod.SharesCgroup(), &spec.CgroupNS}, + {pod.SharesIPC(), &spec.IpcNS}, + {pod.SharesUTS(), &spec.UtsNS}, + } + + printWarning := false + for _, n := range allNamespaces { + if n.isShared && !n.value.IsDefault() { + *n.value = specgen.Namespace{NSMode: specgen.Default} + printWarning = true + } + } + if printWarning { + logrus.Warning("At least one namespace was reset to the default configuration") + } + } + err = specgenutil.FillOutSpecGen(spec, &ctrCloneOpts.CreateOpts, []string{}) if err != nil { return nil, err diff --git a/pkg/machine/qemu/machine.go b/pkg/machine/qemu/machine.go index 3b14572a6..287b93612 100644 --- a/pkg/machine/qemu/machine.go +++ b/pkg/machine/qemu/machine.go @@ -98,7 +98,7 @@ func (p *Provider) NewMachine(opts machine.InitOptions) (machine.VM, error) { return nil, err } - cmd := append([]string{execPath}) + cmd := []string{execPath} // Add memory cmd = append(cmd, []string{"-m", strconv.Itoa(int(vm.Memory))}...) // Add cpus @@ -430,13 +430,29 @@ func (v *MachineVM) Start(name string, _ machine.StartOptions) error { // Disable graphic window when not in debug mode // Done in start, so we're not suck with the debug level we used on init - if logrus.GetLevel() != logrus.DebugLevel { + if !logrus.IsLevelEnabled(logrus.DebugLevel) { cmd = append(cmd, "-display", "none") } _, err = os.StartProcess(v.CmdLine[0], cmd, attr) if err != nil { - return err + // check if qemu was not found + if !errors.Is(err, os.ErrNotExist) { + return err + } + // lookup qemu again maybe the path was changed, https://github.com/containers/podman/issues/13394 + cfg, err := config.Default() + if err != nil { + return err + } + cmd[0], err = cfg.FindHelperBinary(QemuCommand, true) + if err != nil { + return err + } + _, err = os.StartProcess(cmd[0], cmd, attr) + if err != nil { + return err + } } fmt.Println("Waiting for VM ...") socketPath, err := getRuntimeDir() diff --git a/pkg/specgen/generate/container.go b/pkg/specgen/generate/container.go index 64669f34d..0e9d33dd8 100644 --- a/pkg/specgen/generate/container.go +++ b/pkg/specgen/generate/container.go @@ -338,8 +338,8 @@ func FinishThrottleDevices(s *specgen.SpecGenerator) error { } // ConfigToSpec takes a completed container config and converts it back into a specgenerator for purposes of cloning an exisiting container -func ConfigToSpec(rt *libpod.Runtime, specg *specgen.SpecGenerator, contaierID string) (*libpod.Container, error) { - c, err := rt.LookupContainer(contaierID) +func ConfigToSpec(rt *libpod.Runtime, specg *specgen.SpecGenerator, containerID string) (*libpod.Container, error) { + c, err := rt.LookupContainer(containerID) if err != nil { return nil, err } diff --git a/pkg/specgen/generate/security.go b/pkg/specgen/generate/security.go index 9c6709905..988c29832 100644 --- a/pkg/specgen/generate/security.go +++ b/pkg/specgen/generate/security.go @@ -146,6 +146,10 @@ func securityConfigureGenerator(s *specgen.SpecGenerator, g *generate.Generator, configSpec := g.Config configSpec.Process.Capabilities.Ambient = []string{} + + // Always unset the inheritable capabilities similarly to what the Linux kernel does + // They are used only when using capabilities with uid != 0. + configSpec.Process.Capabilities.Inheritable = []string{} configSpec.Process.Capabilities.Bounding = caplist user := strings.Split(s.User, ":")[0] @@ -153,7 +157,6 @@ func securityConfigureGenerator(s *specgen.SpecGenerator, g *generate.Generator, if (user == "" && s.UserNS.NSMode != specgen.KeepID) || user == "root" || user == "0" { configSpec.Process.Capabilities.Effective = caplist configSpec.Process.Capabilities.Permitted = caplist - configSpec.Process.Capabilities.Inheritable = caplist } else { mergedCaps, err := capabilities.MergeCapabilities(nil, s.CapAdd, nil) if err != nil { @@ -175,12 +178,12 @@ func securityConfigureGenerator(s *specgen.SpecGenerator, g *generate.Generator, } configSpec.Process.Capabilities.Effective = userCaps configSpec.Process.Capabilities.Permitted = userCaps - configSpec.Process.Capabilities.Inheritable = userCaps // Ambient capabilities were added to Linux 4.3. Set ambient // capabilities only when the kernel supports them. if supportAmbientCapabilities() { configSpec.Process.Capabilities.Ambient = userCaps + configSpec.Process.Capabilities.Inheritable = userCaps } } diff --git a/podman.spec.rpkg b/podman.spec.rpkg index 41385dba6..937253918 100644 --- a/podman.spec.rpkg +++ b/podman.spec.rpkg @@ -144,10 +144,7 @@ Summary: (Experimental) Remote client for managing %{name} containers %description remote Remote client for managing %{name} containers. -This experimental remote client is under heavy development. Please do not -run %{name}-remote in production. - -%{name}-remote uses the version 2 API to connect to a %{name} client to +%{name}-remote uses the libpod REST API to connect to a %{name} client to manage pods, containers and container images. %{name}-remote supports ssh connections as well. diff --git a/test/e2e/container_clone_test.go b/test/e2e/container_clone_test.go index bebc6872b..a327bb8ed 100644 --- a/test/e2e/container_clone_test.go +++ b/test/e2e/container_clone_test.go @@ -184,4 +184,41 @@ var _ = Describe("Podman container clone", func() { Expect(ctrInspect.InspectContainerToJSON()[0].HostConfig.NetworkMode).Should(Equal(runInspect.InspectContainerToJSON()[0].HostConfig.NetworkMode)) }) + It("podman container clone to a pod", func() { + createPod := podmanTest.Podman([]string{"pod", "create", "--share", "uts", "--name", "foo-pod"}) + createPod.WaitWithDefaultTimeout() + Expect(createPod).To(Exit(0)) + + ctr := podmanTest.RunTopContainer("ctr") + ctr.WaitWithDefaultTimeout() + Expect(ctr).Should(Exit(0)) + + clone := podmanTest.Podman([]string{"container", "clone", "--name", "cloned", "--pod", "foo-pod", "ctr"}) + clone.WaitWithDefaultTimeout() + Expect(clone).To(Exit(0)) + + ctrInspect := podmanTest.Podman([]string{"inspect", "cloned"}) + ctrInspect.WaitWithDefaultTimeout() + Expect(ctrInspect).Should(Exit(0)) + + Expect(ctrInspect.InspectContainerToJSON()[0].Pod).Should(Equal(createPod.OutputToString())) + + Expect(ctrInspect.InspectContainerToJSON()[0].HostConfig.NetworkMode).Should(Not(ContainSubstring("container:"))) + + createPod = podmanTest.Podman([]string{"pod", "create", "--share", "uts,net", "--name", "bar-pod"}) + createPod.WaitWithDefaultTimeout() + Expect(createPod).To(Exit(0)) + + clone = podmanTest.Podman([]string{"container", "clone", "--name", "cloned2", "--pod", "bar-pod", "ctr"}) + clone.WaitWithDefaultTimeout() + Expect(clone).To(Exit(0)) + + ctrInspect = podmanTest.Podman([]string{"inspect", "cloned2"}) + ctrInspect.WaitWithDefaultTimeout() + Expect(ctrInspect).Should(Exit(0)) + + Expect(ctrInspect.InspectContainerToJSON()[0].Pod).Should(Equal(createPod.OutputToString())) + + Expect(ctrInspect.InspectContainerToJSON()[0].HostConfig.NetworkMode).Should(ContainSubstring("container:")) + }) }) diff --git a/test/e2e/run_test.go b/test/e2e/run_test.go index 91a2eddad..f4a6e5733 100644 --- a/test/e2e/run_test.go +++ b/test/e2e/run_test.go @@ -498,7 +498,7 @@ var _ = Describe("Podman run", func() { session = podmanTest.Podman([]string{"run", "--rm", "--user", "root", ALPINE, "grep", "CapInh", "/proc/self/status"}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) - Expect(session.OutputToString()).To(ContainSubstring("00000000a80425fb")) + Expect(session.OutputToString()).To(ContainSubstring("0000000000000000")) session = podmanTest.Podman([]string{"run", "--rm", ALPINE, "grep", "CapBnd", "/proc/self/status"}) session.WaitWithDefaultTimeout() @@ -533,7 +533,7 @@ var _ = Describe("Podman run", func() { session = podmanTest.Podman([]string{"run", "--user=0:0", "--cap-add=DAC_OVERRIDE", "--rm", ALPINE, "grep", "CapInh", "/proc/self/status"}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) - Expect(session.OutputToString()).To(ContainSubstring("00000000a80425fb")) + Expect(session.OutputToString()).To(ContainSubstring("0000000000000000")) if os.Geteuid() > 0 { if os.Getenv("SKIP_USERNS") != "" { @@ -550,7 +550,7 @@ var _ = Describe("Podman run", func() { session = podmanTest.Podman([]string{"run", "--userns=keep-id", "--privileged", "--rm", ALPINE, "grep", "CapInh", "/proc/self/status"}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) - Expect(session.OutputToString()).To(ContainSubstring("0000000000000000")) + Expect(session.OutputToString()).To(ContainSubstring("0000000000000002")) session = podmanTest.Podman([]string{"run", "--userns=keep-id", "--cap-add=DAC_OVERRIDE", "--rm", ALPINE, "grep", "CapInh", "/proc/self/status"}) session.WaitWithDefaultTimeout() |