From 049a5d82f1fbdac6e69e171588df172cb7e9e399 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Mosler?= Date: Wed, 2 Nov 2022 15:07:59 +0100 Subject: [PATCH] Allow namespace path network option for pods. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Closes #16208. Signed-off-by: Björn Mosler --- pkg/specgen/generate/pod_create.go | 5 + pkg/specgen/generate/pod_create_test.go | 189 ++++++++++++++++++++++++ test/e2e/pod_create_test.go | 37 ++++- 3 files changed, 228 insertions(+), 3 deletions(-) create mode 100644 pkg/specgen/generate/pod_create_test.go diff --git a/pkg/specgen/generate/pod_create.go b/pkg/specgen/generate/pod_create.go index b99a5457ca..06f2e0f2e5 100644 --- a/pkg/specgen/generate/pod_create.go +++ b/pkg/specgen/generate/pod_create.go @@ -195,6 +195,11 @@ func MapSpec(p *specgen.PodSpecGenerator) (*specgen.SpecGenerator, error) { p.InfraContainerSpec.NetworkOptions = p.NetworkOptions p.InfraContainerSpec.NetNS.NSMode = specgen.Slirp } + case specgen.Path: + logrus.Debugf("Pod will use namespace path networking") + p.InfraContainerSpec.NetNS.NSMode = specgen.Path + p.InfraContainerSpec.NetNS.Value = p.PodNetworkConfig.NetNS.Value + case specgen.NoNetwork: logrus.Debugf("Pod will not use networking") if len(p.InfraContainerSpec.PortMappings) > 0 || diff --git a/pkg/specgen/generate/pod_create_test.go b/pkg/specgen/generate/pod_create_test.go new file mode 100644 index 0000000000..f6dd5d9842 --- /dev/null +++ b/pkg/specgen/generate/pod_create_test.go @@ -0,0 +1,189 @@ +package generate + +import ( + "net" + + "github.com/containers/common/libnetwork/types" + "github.com/containers/podman/v4/pkg/specgen" + "github.com/stretchr/testify/assert" + + "testing" +) + +var ( + portMappings = []types.PortMapping{{HostPort: 443, ContainerPort: 8080, Protocol: protoUDP}, {HostPort: 22, ContainerPort: 2222, Protocol: protoTCP}} + networks = map[string]types.PerNetworkOptions{"test": { + StaticIPs: nil, + Aliases: nil, + StaticMAC: nil, + InterfaceName: "eth2", + }} +) + +func TestMapSpecCopyPodSpecToInfraContainerSpec(t *testing.T) { + infraCommand := []string{"top"} + addedHosts := []string{"otherhost"} + dnsServers := []net.IP{net.IPv4(10, 0, 0, 1), net.IPv4(8, 8, 8, 8)} + dnsOptions := []string{"dns option"} + dnsSearch := []string{"dns search"} + infraImage := "someimage" + conmonPidFile := "/var/run/conmon.pid" + podSpec := specgen.PodSpecGenerator{ + PodBasicConfig: specgen.PodBasicConfig{InfraCommand: infraCommand, InfraImage: infraImage, + InfraConmonPidFile: conmonPidFile}, + PodNetworkConfig: specgen.PodNetworkConfig{ + PortMappings: portMappings, HostAdd: addedHosts, DNSServer: dnsServers, DNSOption: dnsOptions, DNSSearch: dnsSearch, + Networks: networks, NoManageResolvConf: true, NoManageHosts: true}, + PodCgroupConfig: specgen.PodCgroupConfig{}, + PodResourceConfig: specgen.PodResourceConfig{}, + PodStorageConfig: specgen.PodStorageConfig{}, + PodSecurityConfig: specgen.PodSecurityConfig{}, + InfraContainerSpec: &specgen.SpecGenerator{}, + ServiceContainerID: "", + } + + mappedSpec, err := MapSpec(&podSpec) + + assert.NoError(t, err) + + assert.Equal(t, portMappings, mappedSpec.PortMappings) + assert.Equal(t, infraCommand, mappedSpec.Entrypoint) + assert.Equal(t, addedHosts, mappedSpec.HostAdd) + assert.Equal(t, dnsServers, mappedSpec.DNSServers) + assert.Equal(t, dnsOptions, mappedSpec.DNSOptions) + assert.Equal(t, dnsSearch, mappedSpec.DNSSearch) + assert.True(t, mappedSpec.UseImageResolvConf) + assert.Equal(t, networks, mappedSpec.Networks) + assert.True(t, mappedSpec.UseImageHosts) + assert.Equal(t, conmonPidFile, mappedSpec.ConmonPidFile) + assert.Equal(t, infraImage, mappedSpec.Image) +} + +func createPodSpec(mode specgen.NamespaceMode) specgen.PodSpecGenerator { + return specgen.PodSpecGenerator{ + InfraContainerSpec: &specgen.SpecGenerator{}, + PodNetworkConfig: specgen.PodNetworkConfig{ + NetNS: specgen.Namespace{NSMode: mode}, + }, + } +} + +func createPodSpecWithNetworks(mode specgen.NamespaceMode) specgen.PodSpecGenerator { + spec := createPodSpec(mode) + spec.InfraContainerSpec.Networks = networks + return spec +} + +func createPodSpecWithPortMapping(mode specgen.NamespaceMode) specgen.PodSpecGenerator { + spec := createPodSpec(mode) + spec.InfraContainerSpec.PortMappings = portMappings + return spec +} + +func createPodSpecWithNetNsPath(path string) specgen.PodSpecGenerator { + spec := createPodSpec(specgen.Path) + spec.NetNS.Value = path + return spec +} + +func TestMapSpecNetworkOptions(t *testing.T) { + tests := []struct { + name string + podSpec specgen.PodSpecGenerator + expectedNSMode specgen.NamespaceMode + expectedNSValue string + expectedNetworkOptions map[string][]string + mustError bool + }{ + { + name: "Default", + podSpec: createPodSpec(specgen.Default), + expectedNSMode: "", + }, + { + name: "Bridge", + podSpec: createPodSpec(specgen.Bridge), + expectedNSMode: specgen.Bridge, + }, + { + name: "Private", + podSpec: createPodSpec(specgen.Private), + expectedNSMode: specgen.Private, + }, { + name: "Host", + podSpec: createPodSpec(specgen.Host), + expectedNSMode: specgen.Host, + }, + { + name: "Host but with port mappings", + podSpec: createPodSpecWithPortMapping(specgen.Host), + mustError: true, + }, { + name: "Host but with networks", + podSpec: createPodSpecWithNetworks(specgen.Host), + mustError: true, + }, + { + name: "Slirp", + podSpec: createPodSpec(specgen.Slirp), + expectedNSMode: specgen.Slirp, + }, + { + name: "Slirp but if infra spec NS mode is Host", + podSpec: specgen.PodSpecGenerator{ + InfraContainerSpec: &specgen.SpecGenerator{ + ContainerNetworkConfig: specgen.ContainerNetworkConfig{NetNS: specgen.Namespace{NSMode: host}}, + }, + PodNetworkConfig: specgen.PodNetworkConfig{ + NetNS: specgen.Namespace{NSMode: specgen.Slirp}, + }, + }, + expectedNSMode: specgen.Host, + }, + { + name: "Path", + podSpec: createPodSpecWithNetNsPath("/var/run/netns/bla"), + expectedNSMode: specgen.Path, + expectedNSValue: "/var/run/netns/bla", + }, + { + name: "NoNetwork", + podSpec: createPodSpec(specgen.NoNetwork), + expectedNSMode: specgen.NoNetwork, + }, + { + name: "NoNetwork but with networks", + podSpec: createPodSpecWithNetworks(specgen.NoNetwork), + mustError: true, + }, + { + name: "NoNetwork but with port mappings", + podSpec: createPodSpecWithPortMapping(specgen.NoNetwork), + mustError: true, + }, + { + name: "FromContainer", + podSpec: createPodSpec(specgen.FromContainer), + mustError: true, + }, { + name: "FromPod", + podSpec: createPodSpec(specgen.FromPod), + mustError: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + mappedSpec, err := MapSpec(&tt.podSpec) + + if tt.mustError { + assert.Error(t, err) + } else { + assert.NoError(t, err, "error is not nil") + assert.Equal(t, tt.expectedNSMode, mappedSpec.NetNS.NSMode) + assert.Equal(t, tt.expectedNSValue, mappedSpec.NetNS.Value) + assert.Equal(t, tt.expectedNetworkOptions, mappedSpec.NetworkOptions) + } + }) + } +} diff --git a/test/e2e/pod_create_test.go b/test/e2e/pod_create_test.go index d694efe5fa..c849f58e68 100644 --- a/test/e2e/pod_create_test.go +++ b/test/e2e/pod_create_test.go @@ -500,11 +500,42 @@ entrypoint ["/fromimage"] podCreate.WaitWithDefaultTimeout() Expect(podCreate).Should(Exit(125)) Expect(podCreate.ErrorToString()).To(ContainSubstring("pods presently do not support network mode container")) + }) + + It("podman pod create with namespace path networking", func() { + SkipIfRootless("ip netns is not supported for rootless users") + SkipIfContainerized("ip netns cannot be run within a container.") + + podName := "netnspod" + netNsName := "test1" + networkMode := fmt.Sprintf("ns:/var/run/netns/%s", netNsName) - podCreate = podmanTest.Podman([]string{"pod", "create", "--network", "ns:/does/not/matter"}) + addNetns := SystemExec("ip", []string{"netns", "add", netNsName}) + Expect(addNetns).Should(Exit(0)) + defer func() { + delNetns := SystemExec("ip", []string{"netns", "delete", netNsName}) + Expect(delNetns).Should(Exit(0)) + }() + + podCreate := podmanTest.Podman([]string{"pod", "create", "--name", podName, "--network", networkMode}) podCreate.WaitWithDefaultTimeout() - Expect(podCreate).Should(Exit(125)) - Expect(podCreate.ErrorToString()).To(ContainSubstring("pods presently do not support network mode path")) + Expect(podCreate).Should(Exit(0)) + + podStart := podmanTest.Podman([]string{"pod", "start", podName}) + podStart.WaitWithDefaultTimeout() + Expect(podStart).Should(Exit(0)) + + inspectPod := podmanTest.Podman([]string{"pod", "inspect", podName}) + inspectPod.WaitWithDefaultTimeout() + Expect(inspectPod).Should(Exit(0)) + inspectPodJSON := inspectPod.InspectPodToJSON() + + inspectInfraContainer := podmanTest.Podman([]string{"inspect", inspectPodJSON.InfraContainerID}) + inspectInfraContainer.WaitWithDefaultTimeout() + Expect(inspectInfraContainer).Should(Exit(0)) + inspectInfraContainerJSON := inspectInfraContainer.InspectContainerToJSON() + + Expect(inspectInfraContainerJSON[0].HostConfig.NetworkMode).To(Equal(networkMode)) }) It("podman pod create with --net=none", func() {