diff options
Diffstat (limited to 'pkg/specgen')
-rw-r--r-- | pkg/specgen/container_validate.go | 22 | ||||
-rw-r--r-- | pkg/specgen/generate/container_create.go | 4 | ||||
-rw-r--r-- | pkg/specgen/generate/namespaces.go | 40 | ||||
-rw-r--r-- | pkg/specgen/generate/pod_create.go | 18 | ||||
-rw-r--r-- | pkg/specgen/namespaces.go | 177 | ||||
-rw-r--r-- | pkg/specgen/namespaces_test.go | 265 | ||||
-rw-r--r-- | pkg/specgen/pod_validate.go | 23 | ||||
-rw-r--r-- | pkg/specgen/podspecgen.go | 35 | ||||
-rw-r--r-- | pkg/specgen/specgen.go | 24 |
9 files changed, 498 insertions, 110 deletions
diff --git a/pkg/specgen/container_validate.go b/pkg/specgen/container_validate.go index caea51ea8..d06a047c1 100644 --- a/pkg/specgen/container_validate.go +++ b/pkg/specgen/container_validate.go @@ -29,25 +29,10 @@ func exclusiveOptions(opt1, opt2 string) error { // Validate verifies that the given SpecGenerator is valid and satisfies required // input for creating a container. func (s *SpecGenerator) Validate() error { - if rootless.IsRootless() && len(s.CNINetworks) == 0 { - if s.StaticIP != nil || s.StaticIPv6 != nil { - return ErrNoStaticIPRootless - } - if s.StaticMAC != nil { - return ErrNoStaticMACRootless - } - } - // Containers being added to a pod cannot have certain network attributes // associated with them because those should be on the infra container. if len(s.Pod) > 0 && s.NetNS.NSMode == FromPod { - if s.StaticIP != nil || s.StaticIPv6 != nil { - return errors.Wrap(define.ErrNetworkOnPodContainer, "static ip addresses must be defined when the pod is created") - } - if s.StaticMAC != nil { - return errors.Wrap(define.ErrNetworkOnPodContainer, "MAC addresses must be defined when the pod is created") - } - if len(s.CNINetworks) > 0 { + if len(s.Networks) > 0 { return errors.Wrap(define.ErrNetworkOnPodContainer, "networks must be defined when the pod is created") } if len(s.PortMappings) > 0 || s.PublishExposedPorts { @@ -204,5 +189,10 @@ func (s *SpecGenerator) Validate() error { if err := validateNetNS(&s.NetNS); err != nil { return err } + if s.NetNS.NSMode != Bridge && len(s.Networks) > 0 { + // Note that we also get the ip and mac in the networks map + return errors.New("Networks and static ip/mac address can only be used with Bridge mode networking") + } + return nil } diff --git a/pkg/specgen/generate/container_create.go b/pkg/specgen/generate/container_create.go index df5d2e8ff..331c9393a 100644 --- a/pkg/specgen/generate/container_create.go +++ b/pkg/specgen/generate/container_create.go @@ -160,10 +160,6 @@ func MakeContainer(ctx context.Context, rt *libpod.Runtime, s *specgen.SpecGener } options = append(options, opts...) - if len(s.Aliases) > 0 { - options = append(options, libpod.WithNetworkAliases(s.Aliases)) - } - if containerType := s.InitContainerType; len(containerType) > 0 { options = append(options, libpod.WithInitCtrType(containerType)) } diff --git a/pkg/specgen/generate/namespaces.go b/pkg/specgen/generate/namespaces.go index 7d63fc10f..a2bc37e34 100644 --- a/pkg/specgen/generate/namespaces.go +++ b/pkg/specgen/generate/namespaces.go @@ -10,6 +10,7 @@ import ( "github.com/containers/common/pkg/config" "github.com/containers/podman/v3/libpod" "github.com/containers/podman/v3/libpod/define" + "github.com/containers/podman/v3/libpod/network/types" "github.com/containers/podman/v3/pkg/rootless" "github.com/containers/podman/v3/pkg/specgen" "github.com/containers/podman/v3/pkg/util" @@ -66,7 +67,7 @@ func GetDefaultNamespaceMode(nsType string, cfg *config.Config, pod *libpod.Pod) case "cgroup": return specgen.ParseCgroupNamespace(cfg.Containers.CgroupNS) case "net": - ns, _, err := specgen.ParseNetworkNamespace(cfg.Containers.NetNS, cfg.Containers.RootlessNetworking == "cni") + ns, _, _, err := specgen.ParseNetworkFlag(nil) return ns, err } @@ -250,7 +251,7 @@ func namespaceOptions(ctx context.Context, s *specgen.SpecGenerator, rt *libpod. if s.NetNS.Value != "" { val = fmt.Sprintf("slirp4netns:%s", s.NetNS.Value) } - toReturn = append(toReturn, libpod.WithNetNS(portMappings, expose, postConfigureNetNS, val, s.CNINetworks)) + toReturn = append(toReturn, libpod.WithNetNS(portMappings, expose, postConfigureNetNS, val, nil)) case specgen.Private: fallthrough case specgen.Bridge: @@ -258,7 +259,34 @@ func namespaceOptions(ctx context.Context, s *specgen.SpecGenerator, rt *libpod. if err != nil { return nil, err } - toReturn = append(toReturn, libpod.WithNetNS(portMappings, expose, postConfigureNetNS, "bridge", s.CNINetworks)) + + rtConfig, err := rt.GetConfigNoCopy() + if err != nil { + return nil, err + } + // if no network was specified use add the default + if len(s.Networks) == 0 { + // backwards config still allow the old cni networks list and convert to new format + if len(s.CNINetworks) > 0 { + logrus.Warn(`specgen "cni_networks" option is deprecated use the "networks" map instead`) + networks := make(map[string]types.PerNetworkOptions, len(s.CNINetworks)) + for _, net := range s.CNINetworks { + networks[net] = types.PerNetworkOptions{} + } + s.Networks = networks + } else { + // no networks given but bridge is set so use default network + s.Networks = map[string]types.PerNetworkOptions{ + rtConfig.Network.DefaultNetwork: {}, + } + } + } + // rename the "default" network to the correct default name + if opts, ok := s.Networks["default"]; ok { + s.Networks[rtConfig.Network.DefaultNetwork] = opts + delete(s.Networks, "default") + } + toReturn = append(toReturn, libpod.WithNetNS(portMappings, expose, postConfigureNetNS, "bridge", s.Networks)) } if s.UseImageHosts { @@ -281,12 +309,6 @@ func namespaceOptions(ctx context.Context, s *specgen.SpecGenerator, rt *libpod. if len(s.DNSOptions) > 0 { toReturn = append(toReturn, libpod.WithDNSOption(s.DNSOptions)) } - if s.StaticIP != nil { - toReturn = append(toReturn, libpod.WithStaticIP(*s.StaticIP)) - } - if s.StaticMAC != nil { - toReturn = append(toReturn, libpod.WithStaticMAC(*s.StaticMAC)) - } if s.NetworkOptions != nil { toReturn = append(toReturn, libpod.WithNetworkOptions(s.NetworkOptions)) } diff --git a/pkg/specgen/generate/pod_create.go b/pkg/specgen/generate/pod_create.go index 72dd249e7..0a797c571 100644 --- a/pkg/specgen/generate/pod_create.go +++ b/pkg/specgen/generate/pod_create.go @@ -218,9 +218,7 @@ func MapSpec(p *specgen.PodSpecGenerator) (*specgen.SpecGenerator, error) { case specgen.Host: logrus.Debugf("Pod will use host networking") if len(p.InfraContainerSpec.PortMappings) > 0 || - p.InfraContainerSpec.StaticIP != nil || - p.InfraContainerSpec.StaticMAC != nil || - len(p.InfraContainerSpec.CNINetworks) > 0 || + len(p.InfraContainerSpec.Networks) > 0 || p.InfraContainerSpec.NetNS.NSMode == specgen.NoNetwork { return nil, errors.Wrapf(define.ErrInvalidArg, "cannot set host network if network-related configuration is specified") } @@ -234,9 +232,7 @@ func MapSpec(p *specgen.PodSpecGenerator) (*specgen.SpecGenerator, error) { case specgen.NoNetwork: logrus.Debugf("Pod will not use networking") if len(p.InfraContainerSpec.PortMappings) > 0 || - p.InfraContainerSpec.StaticIP != nil || - p.InfraContainerSpec.StaticMAC != nil || - len(p.InfraContainerSpec.CNINetworks) > 0 || + len(p.InfraContainerSpec.Networks) > 0 || p.InfraContainerSpec.NetNS.NSMode == "host" { return nil, errors.Wrapf(define.ErrInvalidArg, "cannot disable pod network if network-related configuration is specified") } @@ -264,15 +260,13 @@ func MapSpec(p *specgen.PodSpecGenerator) (*specgen.SpecGenerator, error) { if len(p.DNSSearch) > 0 { p.InfraContainerSpec.DNSSearch = p.DNSSearch } - if p.StaticIP != nil { - p.InfraContainerSpec.StaticIP = p.StaticIP - } - if p.StaticMAC != nil { - p.InfraContainerSpec.StaticMAC = p.StaticMAC - } if p.NoManageResolvConf { p.InfraContainerSpec.UseImageResolvConf = true } + if len(p.Networks) > 0 { + p.InfraContainerSpec.Networks = p.Networks + } + // deprecated cni networks for api users if len(p.CNINetworks) > 0 { p.InfraContainerSpec.CNINetworks = p.CNINetworks } diff --git a/pkg/specgen/namespaces.go b/pkg/specgen/namespaces.go index bb5385ef1..15a8ece17 100644 --- a/pkg/specgen/namespaces.go +++ b/pkg/specgen/namespaces.go @@ -2,10 +2,13 @@ package specgen import ( "fmt" + "net" "os" "strings" "github.com/containers/common/pkg/cgroups" + "github.com/containers/podman/v3/libpod/define" + "github.com/containers/podman/v3/libpod/network/types" "github.com/containers/podman/v3/pkg/rootless" "github.com/containers/podman/v3/pkg/util" "github.com/containers/storage" @@ -271,9 +274,9 @@ func ParseUserNamespace(ns string) (Namespace, error) { // ParseNetworkNamespace parses a network namespace specification in string // form. // Returns a namespace and (optionally) a list of CNI networks to join. -func ParseNetworkNamespace(ns string, rootlessDefaultCNI bool) (Namespace, []string, error) { +func ParseNetworkNamespace(ns string, rootlessDefaultCNI bool) (Namespace, map[string]types.PerNetworkOptions, error) { toReturn := Namespace{} - var cniNetworks []string + networks := make(map[string]types.PerNetworkOptions) // Net defaults to Slirp on rootless switch { case ns == string(Slirp), strings.HasPrefix(ns, string(Slirp)+":"): @@ -313,28 +316,174 @@ func ParseNetworkNamespace(ns string, rootlessDefaultCNI bool) (Namespace, []str default: // Assume we have been given a list of CNI networks. // Which only works in bridge mode, so set that. - cniNetworks = strings.Split(ns, ",") + networkList := strings.Split(ns, ",") + for _, net := range networkList { + networks[net] = types.PerNetworkOptions{} + } + toReturn.NSMode = Bridge } - return toReturn, cniNetworks, nil + return toReturn, networks, nil } -func ParseNetworkString(network string) (Namespace, []string, map[string][]string, error) { +// ParseNetworkFlag parses a network string slice into the network options +// If the input is nil or empty it will use the default setting from containers.conf +func ParseNetworkFlag(networks []string) (Namespace, map[string]types.PerNetworkOptions, map[string][]string, error) { var networkOptions map[string][]string - parts := strings.SplitN(network, ":", 2) + // by default we try to use the containers.conf setting + // if we get at least one value use this instead + ns := containerConfig.Containers.NetNS + if len(networks) > 0 { + ns = networks[0] + } - ns, cniNets, err := ParseNetworkNamespace(network, containerConfig.Containers.RootlessNetworking == "cni") - if err != nil { - return Namespace{}, nil, nil, err + toReturn := Namespace{} + podmanNetworks := make(map[string]types.PerNetworkOptions) + + switch { + case ns == string(Slirp), strings.HasPrefix(ns, string(Slirp)+":"): + parts := strings.SplitN(ns, ":", 2) + if len(parts) > 1 { + networkOptions = make(map[string][]string) + networkOptions[parts[0]] = strings.Split(parts[1], ",") + } + toReturn.NSMode = Slirp + case ns == string(FromPod): + toReturn.NSMode = FromPod + case ns == "" || ns == string(Default) || ns == string(Private): + // Net defaults to Slirp on rootless + if rootless.IsRootless() && containerConfig.Containers.RootlessNetworking != "cni" { + toReturn.NSMode = Slirp + break + } + // if not slirp we use bridge + fallthrough + case ns == string(Bridge), strings.HasPrefix(ns, string(Bridge)+":"): + toReturn.NSMode = Bridge + parts := strings.SplitN(ns, ":", 2) + netOpts := types.PerNetworkOptions{} + if len(parts) > 1 { + var err error + netOpts, err = parseBridgeNetworkOptions(parts[1]) + if err != nil { + return toReturn, nil, nil, err + } + } + // we have to set the special default network name here + podmanNetworks["default"] = netOpts + + case ns == string(NoNetwork): + toReturn.NSMode = NoNetwork + case ns == string(Host): + toReturn.NSMode = Host + case strings.HasPrefix(ns, "ns:"): + split := strings.SplitN(ns, ":", 2) + if len(split) != 2 { + return toReturn, nil, nil, errors.Errorf("must provide a path to a namespace when specifying ns:") + } + toReturn.NSMode = Path + toReturn.Value = split[1] + case strings.HasPrefix(ns, string(FromContainer)+":"): + split := strings.SplitN(ns, ":", 2) + if len(split) != 2 { + return toReturn, nil, nil, errors.Errorf("must provide name or ID or a container when specifying container:") + } + toReturn.NSMode = FromContainer + toReturn.Value = split[1] + default: + // we should have a normal network + parts := strings.SplitN(ns, ":", 2) + if len(parts) == 1 { + // Assume we have been given a comma separated list of networks for backwards compat. + networkList := strings.Split(ns, ",") + for _, net := range networkList { + podmanNetworks[net] = types.PerNetworkOptions{} + } + } else { + if parts[0] == "" { + return toReturn, nil, nil, errors.New("network name cannot be empty") + } + netOpts, err := parseBridgeNetworkOptions(parts[1]) + if err != nil { + return toReturn, nil, nil, errors.Wrapf(err, "invalid option for network %s", parts[0]) + } + podmanNetworks[parts[0]] = netOpts + } + + // networks need bridge mode + toReturn.NSMode = Bridge + } + + if len(networks) > 1 { + if !toReturn.IsBridge() { + return toReturn, nil, nil, errors.Wrapf(define.ErrInvalidArg, "cannot set multiple networks without bridge network mode, selected mode %s", toReturn.NSMode) + } + + for _, network := range networks[1:] { + parts := strings.SplitN(network, ":", 2) + if parts[0] == "" { + return toReturn, nil, nil, errors.Wrapf(define.ErrInvalidArg, "network name cannot be empty") + } + if util.StringInSlice(parts[0], []string{string(Bridge), string(Slirp), string(FromPod), string(NoNetwork), + string(Default), string(Private), string(Path), string(FromContainer), string(Host)}) { + return toReturn, nil, nil, errors.Wrapf(define.ErrInvalidArg, "can only set extra network names, selected mode %s conflicts with bridge", parts[0]) + } + netOpts := types.PerNetworkOptions{} + if len(parts) > 1 { + var err error + netOpts, err = parseBridgeNetworkOptions(parts[1]) + if err != nil { + return toReturn, nil, nil, errors.Wrapf(err, "invalid option for network %s", parts[0]) + } + } + podmanNetworks[parts[0]] = netOpts + } + } + + return toReturn, podmanNetworks, networkOptions, nil +} + +func parseBridgeNetworkOptions(opts string) (types.PerNetworkOptions, error) { + netOpts := types.PerNetworkOptions{} + if len(opts) == 0 { + return netOpts, nil } + allopts := strings.Split(opts, ",") + for _, opt := range allopts { + split := strings.SplitN(opt, "=", 2) + switch split[0] { + case "ip", "ip6": + ip := net.ParseIP(split[1]) + if ip == nil { + return netOpts, errors.Errorf("invalid ip address %q", split[1]) + } + netOpts.StaticIPs = append(netOpts.StaticIPs, ip) - if len(parts) > 1 { - networkOptions = make(map[string][]string) - networkOptions[parts[0]] = strings.Split(parts[1], ",") - cniNets = nil + case "mac": + mac, err := net.ParseMAC(split[1]) + if err != nil { + return netOpts, err + } + netOpts.StaticMAC = types.HardwareAddr(mac) + + case "alias": + if split[1] == "" { + return netOpts, errors.New("alias cannot be empty") + } + netOpts.Aliases = append(netOpts.Aliases, split[1]) + + case "interface_name": + if split[1] == "" { + return netOpts, errors.New("interface_name cannot be empty") + } + netOpts.InterfaceName = split[1] + + default: + return netOpts, errors.Errorf("unknown bridge network option: %s", split[0]) + } } - return ns, cniNets, networkOptions, nil + return netOpts, nil } func SetupUserNS(idmappings *storage.IDMappingOptions, userns Namespace, g *generate.Generator) (string, error) { diff --git a/pkg/specgen/namespaces_test.go b/pkg/specgen/namespaces_test.go new file mode 100644 index 000000000..4f69e6b98 --- /dev/null +++ b/pkg/specgen/namespaces_test.go @@ -0,0 +1,265 @@ +package specgen + +import ( + "net" + "testing" + + "github.com/containers/podman/v3/libpod/network/types" + "github.com/containers/podman/v3/pkg/rootless" + "github.com/stretchr/testify/assert" +) + +func parsMacNoErr(mac string) types.HardwareAddr { + m, _ := net.ParseMAC(mac) + return types.HardwareAddr(m) +} + +func TestParseNetworkFlag(t *testing.T) { + // root and rootless have different defaults + defaultNetName := "default" + defaultNetworks := map[string]types.PerNetworkOptions{ + defaultNetName: {}, + } + defaultNsMode := Namespace{NSMode: Bridge} + if rootless.IsRootless() { + defaultNsMode = Namespace{NSMode: Slirp} + defaultNetworks = map[string]types.PerNetworkOptions{} + } + + tests := []struct { + name string + args []string + nsmode Namespace + networks map[string]types.PerNetworkOptions + options map[string][]string + err string + }{ + { + name: "empty input", + args: nil, + nsmode: defaultNsMode, + networks: defaultNetworks, + }, + { + name: "empty string as input", + args: []string{}, + nsmode: defaultNsMode, + networks: defaultNetworks, + }, + { + name: "default mode", + args: []string{"default"}, + nsmode: defaultNsMode, + networks: defaultNetworks, + }, + { + name: "private mode", + args: []string{"private"}, + nsmode: defaultNsMode, + networks: defaultNetworks, + }, + { + name: "bridge mode", + args: []string{"bridge"}, + nsmode: Namespace{NSMode: Bridge}, + networks: map[string]types.PerNetworkOptions{ + defaultNetName: {}, + }, + }, + { + name: "slirp4netns mode", + args: []string{"slirp4netns"}, + nsmode: Namespace{NSMode: Slirp}, + networks: map[string]types.PerNetworkOptions{}, + }, + { + name: "from pod mode", + args: []string{"pod"}, + nsmode: Namespace{NSMode: FromPod}, + networks: map[string]types.PerNetworkOptions{}, + }, + { + name: "no network mode", + args: []string{"none"}, + nsmode: Namespace{NSMode: NoNetwork}, + networks: map[string]types.PerNetworkOptions{}, + }, + { + name: "container mode", + args: []string{"container:abc"}, + nsmode: Namespace{NSMode: FromContainer, Value: "abc"}, + networks: map[string]types.PerNetworkOptions{}, + }, + { + name: "ns path mode", + args: []string{"ns:/path"}, + nsmode: Namespace{NSMode: Path, Value: "/path"}, + networks: map[string]types.PerNetworkOptions{}, + }, + { + name: "slirp4netns mode with options", + args: []string{"slirp4netns:cidr=10.0.0.0/24"}, + nsmode: Namespace{NSMode: Slirp}, + networks: map[string]types.PerNetworkOptions{}, + options: map[string][]string{ + "slirp4netns": {"cidr=10.0.0.0/24"}, + }, + }, + { + name: "bridge mode with options 1", + args: []string{"bridge:ip=10.0.0.1,mac=11:22:33:44:55:66"}, + nsmode: Namespace{NSMode: Bridge}, + networks: map[string]types.PerNetworkOptions{ + defaultNetName: { + StaticIPs: []net.IP{net.ParseIP("10.0.0.1")}, + StaticMAC: parsMacNoErr("11:22:33:44:55:66"), + }, + }, + }, + { + name: "bridge mode with options 2", + args: []string{"bridge:ip=10.0.0.1,ip=10.0.0.5"}, + nsmode: Namespace{NSMode: Bridge}, + networks: map[string]types.PerNetworkOptions{ + defaultNetName: { + StaticIPs: []net.IP{net.ParseIP("10.0.0.1"), net.ParseIP("10.0.0.5")}, + }, + }, + }, + { + name: "bridge mode with ip6 option", + args: []string{"bridge:ip6=fd10::"}, + nsmode: Namespace{NSMode: Bridge}, + networks: map[string]types.PerNetworkOptions{ + defaultNetName: { + StaticIPs: []net.IP{net.ParseIP("fd10::")}, + }, + }, + }, + { + name: "bridge mode with alias option", + args: []string{"bridge:alias=myname,alias=myname2"}, + nsmode: Namespace{NSMode: Bridge}, + networks: map[string]types.PerNetworkOptions{ + defaultNetName: { + Aliases: []string{"myname", "myname2"}, + }, + }, + }, + { + name: "bridge mode with alias option", + args: []string{"bridge:alias=myname,alias=myname2"}, + nsmode: Namespace{NSMode: Bridge}, + networks: map[string]types.PerNetworkOptions{ + defaultNetName: { + Aliases: []string{"myname", "myname2"}, + }, + }, + }, + { + name: "bridge mode with interface option", + args: []string{"bridge:interface_name=eth123"}, + nsmode: Namespace{NSMode: Bridge}, + networks: map[string]types.PerNetworkOptions{ + defaultNetName: { + InterfaceName: "eth123", + }, + }, + }, + { + name: "bridge mode with invalid option", + args: []string{"bridge:abc=123"}, + nsmode: Namespace{NSMode: Bridge}, + err: "unknown bridge network option: abc", + }, + { + name: "bridge mode with invalid ip", + args: []string{"bridge:ip=10..1"}, + nsmode: Namespace{NSMode: Bridge}, + err: "invalid ip address \"10..1\"", + }, + { + name: "bridge mode with invalid mac", + args: []string{"bridge:mac=123"}, + nsmode: Namespace{NSMode: Bridge}, + err: "address 123: invalid MAC address", + }, + { + name: "network name", + args: []string{"someName"}, + nsmode: Namespace{NSMode: Bridge}, + networks: map[string]types.PerNetworkOptions{ + "someName": {}, + }, + }, + { + name: "network name with options", + args: []string{"someName:ip=10.0.0.1"}, + nsmode: Namespace{NSMode: Bridge}, + networks: map[string]types.PerNetworkOptions{ + "someName": {StaticIPs: []net.IP{net.ParseIP("10.0.0.1")}}, + }, + }, + { + name: "multiple networks", + args: []string{"someName", "net2"}, + nsmode: Namespace{NSMode: Bridge}, + networks: map[string]types.PerNetworkOptions{ + "someName": {}, + "net2": {}, + }, + }, + { + name: "multiple networks with options", + args: []string{"someName:ip=10.0.0.1", "net2:ip=10.10.0.1"}, + nsmode: Namespace{NSMode: Bridge}, + networks: map[string]types.PerNetworkOptions{ + "someName": {StaticIPs: []net.IP{net.ParseIP("10.0.0.1")}}, + "net2": {StaticIPs: []net.IP{net.ParseIP("10.10.0.1")}}, + }, + }, + { + name: "multiple networks with bridge mode first should map to default net", + args: []string{"bridge", "net2"}, + nsmode: Namespace{NSMode: Bridge}, + networks: map[string]types.PerNetworkOptions{ + defaultNetName: {}, + "net2": {}, + }, + }, + { + name: "conflicting network modes should error", + args: []string{"bridge", "host"}, + nsmode: Namespace{NSMode: Bridge}, + err: "can only set extra network names, selected mode host conflicts with bridge: invalid argument", + }, + { + name: "multiple networks empty name should error", + args: []string{"someName", ""}, + nsmode: Namespace{NSMode: Bridge}, + err: "network name cannot be empty: invalid argument", + }, + { + name: "multiple networks on invalid mode should error", + args: []string{"host", "net2"}, + nsmode: Namespace{NSMode: Host}, + err: "cannot set multiple networks without bridge network mode, selected mode host: invalid argument", + }, + } + + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + got, got1, got2, err := ParseNetworkFlag(tt.args) + if tt.err != "" { + assert.EqualError(t, err, tt.err, tt.name) + } else { + assert.NoError(t, err, tt.name) + } + + assert.Equal(t, tt.nsmode, got, tt.name) + assert.Equal(t, tt.networks, got1, tt.name) + assert.Equal(t, tt.options, got2, tt.name) + }) + } +} diff --git a/pkg/specgen/pod_validate.go b/pkg/specgen/pod_validate.go index bca7b6dbe..c5a66189c 100644 --- a/pkg/specgen/pod_validate.go +++ b/pkg/specgen/pod_validate.go @@ -1,7 +1,6 @@ package specgen import ( - "github.com/containers/podman/v3/pkg/rootless" "github.com/containers/podman/v3/pkg/util" "github.com/pkg/errors" ) @@ -19,15 +18,6 @@ func exclusivePodOptions(opt1, opt2 string) error { // Validate verifies the input is valid func (p *PodSpecGenerator) Validate() error { - if rootless.IsRootless() && len(p.CNINetworks) == 0 { - if p.StaticIP != nil { - return ErrNoStaticIPRootless - } - if p.StaticMAC != nil { - return ErrNoStaticMACRootless - } - } - // PodBasicConfig if p.NoInfra { if len(p.InfraCommand) > 0 { @@ -52,11 +42,10 @@ func (p *PodSpecGenerator) Validate() error { if p.NetNS.NSMode != Default && p.NetNS.NSMode != "" { return errors.New("NoInfra and network modes cannot be used together") } - if p.StaticIP != nil { - return exclusivePodOptions("NoInfra", "StaticIP") - } - if p.StaticMAC != nil { - return exclusivePodOptions("NoInfra", "StaticMAC") + // Note that networks might be set when --ip or --mac was set + // so we need to check that no networks are set without the infra + if len(p.Networks) > 0 { + return errors.New("cannot set networks options without infra container") } if len(p.DNSOption) > 0 { return exclusivePodOptions("NoInfra", "DNSOption") @@ -78,10 +67,8 @@ func (p *PodSpecGenerator) Validate() error { if len(p.PortMappings) > 0 { return errors.New("PortMappings can only be used with Bridge or slirp4netns networking") } - if len(p.CNINetworks) > 0 { - return errors.New("CNINetworks can only be used with Bridge mode networking") - } } + if p.NoManageResolvConf { if len(p.DNSServer) > 0 { return exclusivePodOptions("NoManageResolvConf", "DNSServer") diff --git a/pkg/specgen/podspecgen.go b/pkg/specgen/podspecgen.go index 948fb990c..e59d11c0a 100644 --- a/pkg/specgen/podspecgen.go +++ b/pkg/specgen/podspecgen.go @@ -86,33 +86,26 @@ type PodNetworkConfig struct { // Defaults to Bridge as root and Slirp as rootless. // Mandatory. NetNS Namespace `json:"netns,omitempty"` - // StaticIP sets a static IP for the infra container. As the infra - // container's network is used for the entire pod by default, this will - // thus be a static IP for the whole pod. - // Only available if NetNS is set to Bridge (the default for root). - // As such, conflicts with NoInfra=true by proxy. - // Optional. - StaticIP *net.IP `json:"static_ip,omitempty"` - // StaticMAC sets a static MAC for the infra container. As the infra - // container's network is used for the entire pod by default, this will - // thus be a static MAC for the entire pod. - // Only available if NetNS is set to Bridge (the default for root). - // As such, conflicts with NoInfra=true by proxy. - // Optional. - // swagger:strfmt string - StaticMAC *types.HardwareAddr `json:"static_mac,omitempty"` // PortMappings is a set of ports to map into the infra container. // As, by default, containers share their network with the infra // container, this will forward the ports to the entire pod. // Only available if NetNS is set to Bridge or Slirp. // Optional. PortMappings []types.PortMapping `json:"portmappings,omitempty"` - // CNINetworks is a list of CNI networks that the infra container will - // join. As, by default, containers share their network with the infra - // container, these networks will effectively be joined by the - // entire pod. - // Only available when NetNS is set to Bridge, the default for root. - // Optional. + // Map of networks names ot ids the container should join to. + // You can request additional settings for each network, you can + // set network aliases, static ips, static mac address and the + // network interface name for this container on the specifc network. + // If the map is empty and the bridge network mode is set the container + // will be joined to the default network. + Networks map[string]types.PerNetworkOptions + // CNINetworks is a list of CNI networks to join the container to. + // If this list is empty, the default CNI network will be joined + // instead. If at least one entry is present, we will not join the + // default network (unless it is part of this list). + // Only available if NetNS is set to bridge. + // Optional. + // Deprecated: as of podman 4.0 use "Networks" instead. CNINetworks []string `json:"cni_networks,omitempty"` // NoManageResolvConf indicates that /etc/resolv.conf should not be // managed by the pod. Instead, each container will create and manage a diff --git a/pkg/specgen/specgen.go b/pkg/specgen/specgen.go index 0e257ad4c..e650c1966 100644 --- a/pkg/specgen/specgen.go +++ b/pkg/specgen/specgen.go @@ -394,26 +394,10 @@ type ContainerCgroupConfig struct { // ContainerNetworkConfig contains information on a container's network // configuration. type ContainerNetworkConfig struct { - // Aliases are a list of network-scoped aliases for container - // Optional - Aliases map[string][]string `json:"aliases"` // NetNS is the configuration to use for the container's network // namespace. // Mandatory. NetNS Namespace `json:"netns,omitempty"` - // StaticIP is the a IPv4 address of the container. - // Only available if NetNS is set to Bridge. - // Optional. - StaticIP *net.IP `json:"static_ip,omitempty"` - // StaticIPv6 is a static IPv6 address to set in the container. - // Only available if NetNS is set to Bridge. - // Optional. - StaticIPv6 *net.IP `json:"static_ipv6,omitempty"` - // StaticMAC is a static MAC address to set in the container. - // Only available if NetNS is set to bridge. - // Optional. - // swagger:strfmt string - StaticMAC *nettypes.HardwareAddr `json:"static_mac,omitempty"` // PortBindings is a set of ports to map into the container. // Only available if NetNS is set to bridge or slirp. // Optional. @@ -434,12 +418,20 @@ type ContainerNetworkConfig struct { // PublishExposedPorts is set. // Optional. Expose map[uint16]string `json:"expose,omitempty"` + // Map of networks names ot ids the container should join to. + // You can request additional settings for each network, you can + // set network aliases, static ips, static mac address and the + // network interface name for this container on the specifc network. + // If the map is empty and the bridge network mode is set the container + // will be joined to the default network. + Networks map[string]nettypes.PerNetworkOptions // CNINetworks is a list of CNI networks to join the container to. // If this list is empty, the default CNI network will be joined // instead. If at least one entry is present, we will not join the // default network (unless it is part of this list). // Only available if NetNS is set to bridge. // Optional. + // Deprecated: as of podman 4.0 use "Networks" instead. CNINetworks []string `json:"cni_networks,omitempty"` // UseImageResolvConf indicates that resolv.conf should not be managed // by Podman, but instead sourced from the image. |