From 85c0fe7dc086f89f958302b7fd0f5fe8103db441 Mon Sep 17 00:00:00 2001 From: Ed Santiago Date: Thu, 3 Feb 2022 14:48:35 -0700 Subject: System tests: revert emergency skip of checkpoint tests Revert #13049. criu-3.16.1-6.fc36 fixes the problem and is now in fc36 stable: https://bodhi.fedoraproject.org/updates/FEDORA-2022-183b337712 (Yes, I confirmed that tests pass on a rawhide vm) Signed-off-by: Ed Santiago --- test/system/520-checkpoint.bats | 4 ---- 1 file changed, 4 deletions(-) (limited to 'test/system') diff --git a/test/system/520-checkpoint.bats b/test/system/520-checkpoint.bats index fcb7fbb84..046dfd126 100644 --- a/test/system/520-checkpoint.bats +++ b/test/system/520-checkpoint.bats @@ -15,10 +15,6 @@ function setup() { skip "FIXME: checkpointing broken in Ubuntu 2004, 2104, 2110, ..." fi - if [[ "$(uname -r)" =~ "5.17" ]]; then - skip "FIXME: checkpointing broken on kernel 5.17 (#12949)" - fi - # None of these tests work rootless.... if is_rootless; then # ...however, is that a genuine cast-in-stone limitation, or one -- cgit v1.2.3-54-g00ecf From b1bf91a22a8d5d4d676e48efa2073944baff14c1 Mon Sep 17 00:00:00 2001 From: cdoern Date: Tue, 18 Jan 2022 15:46:11 -0500 Subject: Podman pod create --share-parent vs --share=cgroup separated cgroupNS sharing from setting the pod as the cgroup parent, made a new flag --share-parent which sets the pod as the cgroup parent for all containers entering the pod remove cgroup from the default kernel namespaces since we want the same default behavior as before which is just the cgroup parent. resolves #12765 Signed-off-by: cdoern Signed-off-by: cdoern Signed-off-by: cdoern --- cmd/podman/pods/create.go | 9 ++++++ docs/source/markdown/podman-pod-create.1.md | 8 +++++- libpod/options.go | 2 +- pkg/api/handlers/libpod/pods.go | 4 +++ pkg/domain/entities/pods.go | 2 ++ pkg/specgen/generate/namespaces.go | 2 +- pkg/specgen/generate/pod_create.go | 3 ++ pkg/specgen/namespaces.go | 2 +- pkg/specgen/podspecgen.go | 2 ++ test/e2e/pod_create_test.go | 43 +++++++++++++++++++++++++++++ test/system/200-pod.bats | 2 +- 11 files changed, 74 insertions(+), 5 deletions(-) (limited to 'test/system') diff --git a/cmd/podman/pods/create.go b/cmd/podman/pods/create.go index 4b32e7bb7..1cd36008e 100644 --- a/cmd/podman/pods/create.go +++ b/cmd/podman/pods/create.go @@ -17,6 +17,7 @@ import ( "github.com/containers/podman/v4/cmd/podman/parse" "github.com/containers/podman/v4/cmd/podman/registry" "github.com/containers/podman/v4/cmd/podman/validate" + "github.com/containers/podman/v4/libpod/define" "github.com/containers/podman/v4/pkg/domain/entities" "github.com/containers/podman/v4/pkg/errorhandling" "github.com/containers/podman/v4/pkg/specgen" @@ -52,6 +53,7 @@ var ( podIDFile string replace bool share string + shareParent bool ) func init() { @@ -88,6 +90,9 @@ func init() { flags.StringVar(&share, shareFlagName, specgen.DefaultKernelNamespaces, "A comma delimited list of kernel namespaces the pod will share") _ = createCommand.RegisterFlagCompletionFunc(shareFlagName, common.AutocompletePodShareNamespace) + shareParentFlagName := "share-parent" + flags.BoolVar(&shareParent, shareParentFlagName, true, "Set the pod's cgroup as the cgroup parent for all containers joining the pod") + flags.SetNormalizeFunc(aliasNetworkFlag) } @@ -147,7 +152,11 @@ func create(cmd *cobra.Command, args []string) error { if err != nil { return err } + if strings.Contains(share, "cgroup") && shareParent { + return errors.Wrapf(define.ErrInvalidArg, "cannot define the pod as the cgroup parent at the same time as joining the infra container's cgroupNS") + } createOptions.Share = strings.Split(share, ",") + createOptions.ShareParent = &shareParent if cmd.Flag("infra-command").Changed { // Only send content to server side if user changed defaults cmdIn, err := cmd.Flags().GetString("infra-command") diff --git a/docs/source/markdown/podman-pod-create.1.md b/docs/source/markdown/podman-pod-create.1.md index 58d3b9d44..8088e1d62 100644 --- a/docs/source/markdown/podman-pod-create.1.md +++ b/docs/source/markdown/podman-pod-create.1.md @@ -265,7 +265,7 @@ Note: Labeling can be disabled for all containers by setting label=false in the #### **--share**=*namespace* -A comma-separated list of kernel namespaces to share. If none or "" is specified, no namespaces will be shared. The namespaces to choose from are ipc, net, pid, uts. +A comma-separated list of kernel namespaces to share. If none or "" is specified, no namespaces will be shared. The namespaces to choose from are cgroup, ipc, net, pid, uts. The operator can identify a pod in three ways: UUID long identifier (“f78375b1c487e03c9438c729345e54db9d20cfa2ac1fc3494b6eb60872e74778”) @@ -276,6 +276,12 @@ podman generates a UUID for each pod, and if a name is not assigned to the container with **--name** then a random string name will be generated for it. The name is useful any place you need to identify a pod. +#### **--share-parent** + +This boolean determines whether or not all containers entering the pod will use the pod as their cgroup parent. The default value of this flag is true. If you are looking to share the cgroup namespace rather than a cgroup parent in a pod, use **--share** + +Note: This options conflict with **--share=cgroup** since that would set the pod as the cgroup parent but enter the container into the same cgroupNS as the infra container. + #### **--sysctl**=_name_=_value_ Configure namespace kernel parameters for all containers in the pod. diff --git a/libpod/options.go b/libpod/options.go index 4f9e49d0f..e0502a72d 100644 --- a/libpod/options.go +++ b/libpod/options.go @@ -1865,7 +1865,7 @@ func WithPodCgroupParent(path string) PodCreateOption { // this pod. // This can still be overridden at the container level by explicitly specifying // a Cgroup parent. -func WithPodCgroups() PodCreateOption { +func WithPodParent() PodCreateOption { return func(pod *Pod) error { if pod.valid { return define.ErrPodFinalized diff --git a/pkg/api/handlers/libpod/pods.go b/pkg/api/handlers/libpod/pods.go index afbdf0e5f..d522631b7 100644 --- a/pkg/api/handlers/libpod/pods.go +++ b/pkg/api/handlers/libpod/pods.go @@ -45,6 +45,10 @@ func PodCreate(w http.ResponseWriter, r *http.Request) { infraOptions.Net = &entities.NetOptions{} infraOptions.Devices = psg.Devices infraOptions.SecurityOpt = psg.SecurityOpt + if psg.ShareParent == nil { + t := true + psg.ShareParent = &t + } err = specgenutil.FillOutSpecGen(psg.InfraContainerSpec, &infraOptions, []string{}) // necessary for default values in many cases (userns, idmappings) if err != nil { utils.Error(w, http.StatusInternalServerError, errors.Wrap(err, "error filling out specgen")) diff --git a/pkg/domain/entities/pods.go b/pkg/domain/entities/pods.go index aeccc82b4..7922db4e6 100644 --- a/pkg/domain/entities/pods.go +++ b/pkg/domain/entities/pods.go @@ -132,6 +132,7 @@ type PodCreateOptions struct { Name string `json:"name,omitempty"` Net *NetOptions `json:"net,omitempty"` Share []string `json:"share,omitempty"` + ShareParent *bool `json:"share_parent,omitempty"` Pid string `json:"pid,omitempty"` Cpus float64 `json:"cpus,omitempty"` CpusetCpus string `json:"cpuset_cpus,omitempty"` @@ -324,6 +325,7 @@ func ToPodSpecGen(s specgen.PodSpecGenerator, p *PodCreateOptions) (*specgen.Pod } s.InfraImage = p.InfraImage s.SharedNamespaces = p.Share + s.ShareParent = p.ShareParent s.PodCreateCommand = p.CreateCommand s.VolumesFrom = p.VolumesFrom diff --git a/pkg/specgen/generate/namespaces.go b/pkg/specgen/generate/namespaces.go index 93d9caf4c..3f77cbe76 100644 --- a/pkg/specgen/generate/namespaces.go +++ b/pkg/specgen/generate/namespaces.go @@ -482,7 +482,7 @@ func GetNamespaceOptions(ns []string, netnsIsHost bool) ([]libpod.PodCreateOptio for _, toShare := range ns { switch toShare { case "cgroup": - options = append(options, libpod.WithPodCgroups()) + options = append(options, libpod.WithPodCgroup()) case "net": // share the netns setting with other containers in the pod only when it is not set to host if !netnsIsHost { diff --git a/pkg/specgen/generate/pod_create.go b/pkg/specgen/generate/pod_create.go index 03829e8cf..68fda3ad7 100644 --- a/pkg/specgen/generate/pod_create.go +++ b/pkg/specgen/generate/pod_create.go @@ -166,6 +166,9 @@ func createPodOptions(p *specgen.PodSpecGenerator, rt *libpod.Runtime, infraSpec ) if !p.NoInfra { //&& infraSpec != nil { options = append(options, libpod.WithInfraContainer()) + if p.ShareParent == nil || (p.ShareParent != nil && *p.ShareParent) { + options = append(options, libpod.WithPodParent()) + } nsOptions, err := GetNamespaceOptions(p.SharedNamespaces, p.InfraContainerSpec.NetNS.IsHost()) if err != nil { return nil, err diff --git a/pkg/specgen/namespaces.go b/pkg/specgen/namespaces.go index f61937078..e672bc65f 100644 --- a/pkg/specgen/namespaces.go +++ b/pkg/specgen/namespaces.go @@ -57,7 +57,7 @@ const ( // DefaultKernelNamespaces is a comma-separated list of default kernel // namespaces. - DefaultKernelNamespaces = "cgroup,ipc,net,uts" + DefaultKernelNamespaces = "ipc,net,uts" ) // Namespace describes the namespace diff --git a/pkg/specgen/podspecgen.go b/pkg/specgen/podspecgen.go index 91b2599cc..759caa0c0 100644 --- a/pkg/specgen/podspecgen.go +++ b/pkg/specgen/podspecgen.go @@ -63,6 +63,8 @@ type PodBasicConfig struct { // also be used by some tools that wish to recreate the pod // (e.g. `podman generate systemd --new`). // Optional. + // ShareParent determines if all containers in the pod will share the pod's cgroup as the cgroup parent + ShareParent *bool `json:"share_parent,omitempty"` PodCreateCommand []string `json:"pod_create_command,omitempty"` // Pid sets the process id namespace of the pod // Optional (defaults to private if unset). This sets the PID namespace of the infra container diff --git a/test/e2e/pod_create_test.go b/test/e2e/pod_create_test.go index f0abfd80c..cd7f72ac0 100644 --- a/test/e2e/pod_create_test.go +++ b/test/e2e/pod_create_test.go @@ -1068,4 +1068,47 @@ ENTRYPOINT ["sleep","99999"] }) + It("podman pod create --share-parent test", func() { + SkipIfRootlessCgroupsV1("rootless cannot use cgroups with cgroupsv1") + podCreate := podmanTest.Podman([]string{"pod", "create", "--share-parent=false"}) + podCreate.WaitWithDefaultTimeout() + Expect(podCreate).Should(Exit(0)) + + ctrCreate := podmanTest.Podman([]string{"run", "-dt", "--pod", podCreate.OutputToString(), ALPINE}) + ctrCreate.WaitWithDefaultTimeout() + Expect(ctrCreate).Should(Exit(0)) + + inspectPod := podmanTest.Podman([]string{"pod", "inspect", podCreate.OutputToString()}) + inspectPod.WaitWithDefaultTimeout() + Expect(inspectPod).Should(Exit(0)) + data := inspectPod.InspectPodToJSON() + + inspect := podmanTest.InspectContainer(ctrCreate.OutputToString()) + Expect(data.CgroupPath).To(HaveLen(0)) + if podmanTest.CgroupManager == "cgroupfs" || !rootless.IsRootless() { + Expect(inspect[0].HostConfig.CgroupParent).To(HaveLen(0)) + } else if podmanTest.CgroupManager == "systemd" { + Expect(inspect[0].HostConfig.CgroupParent).To(Equal("user.slice")) + } + + podCreate2 := podmanTest.Podman([]string{"pod", "create", "--share", "cgroup,ipc,net,uts", "--share-parent=false", "--infra-name", "cgroupCtr"}) + podCreate2.WaitWithDefaultTimeout() + Expect(podCreate2).Should(Exit(0)) + + ctrCreate2 := podmanTest.Podman([]string{"run", "-dt", "--pod", podCreate2.OutputToString(), ALPINE}) + ctrCreate2.WaitWithDefaultTimeout() + Expect(ctrCreate2).Should(Exit(0)) + + inspectInfra := podmanTest.InspectContainer("cgroupCtr") + + inspect2 := podmanTest.InspectContainer(ctrCreate2.OutputToString()) + + Expect(inspect2[0].HostConfig.CgroupMode).To(ContainSubstring(inspectInfra[0].ID)) + + podCreate3 := podmanTest.Podman([]string{"pod", "create", "--share", "cgroup"}) + podCreate3.WaitWithDefaultTimeout() + Expect(podCreate3).ShouldNot(Exit(0)) + + }) + }) diff --git a/test/system/200-pod.bats b/test/system/200-pod.bats index bccd04e8d..34dfaa8f6 100644 --- a/test/system/200-pod.bats +++ b/test/system/200-pod.bats @@ -340,7 +340,7 @@ EOF run_podman 125 pod create --share bogus --name $pod_name is "$output" ".*Invalid kernel namespace to share: bogus. Options are: cgroup, ipc, net, pid, uts or none" \ "pod test for bogus --share option" - run_podman pod create --share cgroup,ipc --name $pod_name + run_podman pod create --share ipc --name $pod_name run_podman run --rm --pod $pod_name --hostname foobar $IMAGE hostname is "$output" "foobar" "--hostname should work with non share UTS namespace" } -- cgit v1.2.3-54-g00ecf From bcd5f5ead7b432f8a5f6c5438b5aa81134718c12 Mon Sep 17 00:00:00 2001 From: Paul Holzinger Date: Fri, 4 Feb 2022 15:48:41 +0100 Subject: append podman dns search domain Append the podman dns seach domain to the host search domains when we use the dnsname/aardvark server. Previously it would only use podman seach domains and discard the host domains. Fixes #13103 Signed-off-by: Paul Holzinger --- libpod/container_internal_linux.go | 22 +++++++++++----------- test/system/500-networking.bats | 24 +++++++++++++++++++++--- 2 files changed, 32 insertions(+), 14 deletions(-) (limited to 'test/system') diff --git a/libpod/container_internal_linux.go b/libpod/container_internal_linux.go index 86d8586d0..95f1634a8 100644 --- a/libpod/container_internal_linux.go +++ b/libpod/container_internal_linux.go @@ -2099,38 +2099,38 @@ func (c *Container) generateResolvConf() (string, error) { } dnsServers := append(dns, c.config.DNSServer...) // If the user provided dns, it trumps all; then dns masq; then resolv.conf + var search []string switch { case len(dnsServers) > 0: - // We store DNS servers as net.IP, so need to convert to string for _, server := range dnsServers { nameservers = append(nameservers, server.String()) } - case len(networkNameServers) > 0: - nameservers = append(nameservers, networkNameServers...) default: // Make a new resolv.conf - nameservers = resolvconf.GetNameservers(resolv.Content) - // slirp4netns has a built in DNS server. + // first add the nameservers from the networks status + nameservers = append(nameservers, networkNameServers...) + // when we add network dns server we also have to add the search domains + search = networkSearchDomains + // slirp4netns has a built in DNS forwarder. if c.config.NetMode.IsSlirp4netns() { slirp4netnsDNS, err := GetSlirp4netnsDNS(c.slirp4netnsSubnet) if err != nil { logrus.Warn("Failed to determine Slirp4netns DNS: ", err.Error()) } else { - nameservers = append([]string{slirp4netnsDNS.String()}, nameservers...) + nameservers = append(nameservers, slirp4netnsDNS.String()) } } + nameservers = append(nameservers, resolvconf.GetNameservers(resolv.Content)...) } - var search []string - if len(c.config.DNSSearch) > 0 || len(c.runtime.config.Containers.DNSSearches) > 0 || len(networkSearchDomains) > 0 { + if len(c.config.DNSSearch) > 0 || len(c.runtime.config.Containers.DNSSearches) > 0 { if !util.StringInSlice(".", c.config.DNSSearch) { - search = c.runtime.config.Containers.DNSSearches + search = append(search, c.runtime.config.Containers.DNSSearches...) search = append(search, c.config.DNSSearch...) - search = append(search, networkSearchDomains...) } } else { - search = resolvconf.GetSearchDomains(resolv.Content) + search = append(search, resolvconf.GetSearchDomains(resolv.Content)...) } var options []string diff --git a/test/system/500-networking.bats b/test/system/500-networking.bats index 9f70c1c6c..b49f141dc 100644 --- a/test/system/500-networking.bats +++ b/test/system/500-networking.bats @@ -605,9 +605,27 @@ load helpers "8.8.8.8", ] EOF - CONTAINERS_CONF=$containersconf run_podman run --rm $IMAGE grep "example.com" /etc/resolv.conf - CONTAINERS_CONF=$containersconf run_podman run --rm $IMAGE grep $searchIP /etc/resolv.conf - is "$output" "nameserver $searchIP" "Should only be one $searchIP not multiple" + + local nl=" +" + + CONTAINERS_CONF=$containersconf run_podman run --rm $IMAGE cat /etc/resolv.conf + is "$output" "search example.com$nl.*" "correct seach domain" + is "$output" ".*nameserver 1.1.1.1${nl}nameserver $searchIP${nl}nameserver 1.0.0.1${nl}nameserver 8.8.8.8" "nameserver order is correct" + + # create network with dns + local netname=testnet-$(random_string 10) + local subnet=$(random_rfc1918_subnet) + run_podman network create --subnet "$subnet.0/24" $netname + # custom server overwrites the network dns server + CONTAINERS_CONF=$containersconf run_podman run --network $netname --rm $IMAGE cat /etc/resolv.conf + is "$output" "search example.com$nl.*" "correct seach domain" + is "$output" ".*nameserver 1.1.1.1${nl}nameserver $searchIP${nl}nameserver 1.0.0.1${nl}nameserver 8.8.8.8" "nameserver order is correct" + + # we should use the integrated dns server + run_podman run --network $netname --rm $IMAGE cat /etc/resolv.conf + is "$output" "search dns.podman.*" "correct seach domain" + is "$output" ".*nameserver $subnet.1.*" "integrated dns nameserver is set" } # vim: filetype=sh -- cgit v1.2.3-54-g00ecf From b1b9a0d7a2a5c0ad789bd5537a081e33f0b0f7cb Mon Sep 17 00:00:00 2001 From: Aditya R Date: Fri, 4 Feb 2022 17:02:00 +0530 Subject: tests: retrofit healthcheck system tests All the healthcheck return output now but systems tests is written to expect empty output which seems wrong. Modify jq output to contain newline character rather than actual newline Signed-off-by: Aditya R --- test/system/220-healthcheck.bats | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'test/system') diff --git a/test/system/220-healthcheck.bats b/test/system/220-healthcheck.bats index 1d4a2ea7e..6159a2807 100644 --- a/test/system/220-healthcheck.bats +++ b/test/system/220-healthcheck.bats @@ -18,7 +18,7 @@ function _check_health { # (kludge to deal with parse_table and empty strings) if [ "$expect" = "''" ]; then expect=""; fi - actual=$(jq -r ".$field" <<<"$output") + actual=$(jq ".$field" <<<"$output") is "$actual" "$expect" "$testname - .State.Healthcheck.$field" done } @@ -77,10 +77,10 @@ EOF is "$output" "" "output from 'podman healthcheck run'" _check_health "All healthy" " -Status | healthy +Status | \"healthy\" FailingStreak | 0 Log[-1].ExitCode | 0 -Log[-1].Output | +Log[-1].Output | \"Life is Good on stdout\\\nLife is Good on stderr\" " # Force a failure @@ -88,19 +88,19 @@ Log[-1].Output | sleep 2 _check_health "First failure" " -Status | healthy +Status | \"healthy\" FailingStreak | [123] Log[-1].ExitCode | 1 -Log[-1].Output | +Log[-1].Output | \"Uh-oh on stdout!\\\nUh-oh on stderr!\" " # After three successive failures, container should no longer be healthy sleep 5 _check_health "Three or more failures" " -Status | unhealthy +Status | \"unhealthy\" FailingStreak | [3456] Log[-1].ExitCode | 1 -Log[-1].Output | +Log[-1].Output | \"Uh-oh on stdout!\\\nUh-oh on stderr!\" " # healthcheck should now fail, with exit status 1 and 'unhealthy' output -- cgit v1.2.3-54-g00ecf From 9aee36b768fbbf58dd8550380e6cb67e33af3a22 Mon Sep 17 00:00:00 2001 From: Ed Santiago Date: Mon, 7 Feb 2022 06:58:19 -0700 Subject: Cleanup: remove obsolete/misleading bug workaround Followup to #13129: remove a no-longer-necessary workaround for a healthcheck bug. Signed-off-by: Ed Santiago --- test/system/220-healthcheck.bats | 3 --- 1 file changed, 3 deletions(-) (limited to 'test/system') diff --git a/test/system/220-healthcheck.bats b/test/system/220-healthcheck.bats index 6159a2807..c502ad669 100644 --- a/test/system/220-healthcheck.bats +++ b/test/system/220-healthcheck.bats @@ -15,9 +15,6 @@ function _check_health { run_podman inspect --format "{{json .State.Healthcheck}}" healthcheck_c parse_table "$tests" | while read field expect;do - # (kludge to deal with parse_table and empty strings) - if [ "$expect" = "''" ]; then expect=""; fi - actual=$(jq ".$field" <<<"$output") is "$actual" "$expect" "$testname - .State.Healthcheck.$field" done -- cgit v1.2.3-54-g00ecf From ed60f8908672a251a6cd366c42152c100dbf68f7 Mon Sep 17 00:00:00 2001 From: Paul Holzinger Date: Mon, 7 Feb 2022 15:04:04 +0100 Subject: move rootless netns slirp4netns process to systemd user.slice When running podman inside systemd user units, it is possible that systemd kills the rootless netns slirp4netns process because it was started in the default unit cgroup. When the unit is stopped all processes in that cgroup are killed. Since the slirp4netns process is run once for all containers it should not be killed. To make sure systemd will not kill the process we move it to the user.slice. Fixes #13153 Signed-off-by: Paul Holzinger --- libpod/networking_linux.go | 7 +++++++ test/system/250-systemd.bats | 30 ++++++++++++++++++++++++++++++ utils/utils.go | 22 ++++++++++++++++++---- 3 files changed, 55 insertions(+), 4 deletions(-) (limited to 'test/system') diff --git a/libpod/networking_linux.go b/libpod/networking_linux.go index f490ac626..e55e9d114 100644 --- a/libpod/networking_linux.go +++ b/libpod/networking_linux.go @@ -28,6 +28,7 @@ import ( "github.com/containers/podman/v4/pkg/resolvconf" "github.com/containers/podman/v4/pkg/rootless" "github.com/containers/podman/v4/pkg/util" + "github.com/containers/podman/v4/utils" "github.com/containers/storage/pkg/lockfile" "github.com/opencontainers/selinux/go-selinux/label" "github.com/pkg/errors" @@ -495,6 +496,12 @@ func (r *Runtime) GetRootlessNetNs(new bool) (*RootlessNetNS, error) { return nil, err } + // move to systemd scope to prevent systemd from killing it + err = utils.MoveRootlessNetnsSlirpProcessToUserSlice(cmd.Process.Pid) + if err != nil { + logrus.Errorf("failed to move the rootless netns slirp4netns process to the systemd user.slice: %v", err) + } + // build a new resolv.conf file which uses the slirp4netns dns server address resolveIP, err := GetSlirp4netnsDNS(nil) if err != nil { diff --git a/test/system/250-systemd.bats b/test/system/250-systemd.bats index c47679904..3847d9510 100644 --- a/test/system/250-systemd.bats +++ b/test/system/250-systemd.bats @@ -281,4 +281,34 @@ LISTEN_FDNAMES=listen_fdnames" | sort) is "$output" "" "output should be empty" } +# https://github.com/containers/podman/issues/13153 +@test "podman rootless-netns slirp4netns process should be in different cgroup" { + is_rootless || skip "only meaningful for rootless" + + cname=$(random_string) + local netname=testnet-$(random_string 10) + + # create network and container with network + run_podman network create $netname + run_podman create --name $cname --network $netname $IMAGE top + + # run container in systemd unit + service_setup + + # run second container with network + cname2=$(random_string) + run_podman run -d --name $cname2 --network $netname $IMAGE top + + # stop systemd container + service_cleanup + + # now check that the rootless netns slirp4netns process is still alive and working + run_podman unshare --rootless-netns ip addr + is "$output" ".*tap0.*" "slirp4netns interface exists in the netns" + run_podman exec $cname2 nslookup google.com + + run_podman rm -f -t0 $cname2 + run_podman network rm -f $netname +} + # vim: filetype=sh diff --git a/utils/utils.go b/utils/utils.go index 52586b937..22f0cb12f 100644 --- a/utils/utils.go +++ b/utils/utils.go @@ -174,7 +174,7 @@ func RunsOnSystemd() bool { return runsOnSystemd } -func moveProcessToScope(pidPath, slice, scope string) error { +func moveProcessPIDFileToScope(pidPath, slice, scope string) error { data, err := ioutil.ReadFile(pidPath) if err != nil { // do not raise an error if the file doesn't exist @@ -187,18 +187,32 @@ func moveProcessToScope(pidPath, slice, scope string) error { if err != nil { return errors.Wrapf(err, "cannot parse pid file %s", pidPath) } - err = RunUnderSystemdScope(int(pid), slice, scope) + return moveProcessToScope(int(pid), slice, scope) +} + +func moveProcessToScope(pid int, slice, scope string) error { + err := RunUnderSystemdScope(int(pid), slice, scope) // If the PID is not valid anymore, do not return an error. if dbusErr, ok := err.(dbus.Error); ok { if dbusErr.Name == "org.freedesktop.DBus.Error.UnixProcessIdUnknown" { return nil } } - return err } +// MoveRootlessNetnsSlirpProcessToUserSlice moves the slirp4netns process for the rootless netns +// into a different scope so that systemd does not kill it with a container. +func MoveRootlessNetnsSlirpProcessToUserSlice(pid int) error { + randBytes := make([]byte, 4) + _, err := rand.Read(randBytes) + if err != nil { + return err + } + return moveProcessToScope(pid, "user.slice", fmt.Sprintf("rootless-netns-%x.scope", randBytes)) +} + // MovePauseProcessToScope moves the pause process used for rootless mode to keep the namespaces alive to // a separate scope. func MovePauseProcessToScope(pausePidPath string) { @@ -211,7 +225,7 @@ func MovePauseProcessToScope(pausePidPath string) { logrus.Errorf("failed to read random bytes: %v", err) continue } - err = moveProcessToScope(pausePidPath, "user.slice", fmt.Sprintf("podman-pause-%x.scope", randBytes)) + err = moveProcessPIDFileToScope(pausePidPath, "user.slice", fmt.Sprintf("podman-pause-%x.scope", randBytes)) if err == nil { return } -- cgit v1.2.3-54-g00ecf