From 535818414c2a6bdcf6434e36c33775ea1a43f1cf Mon Sep 17 00:00:00 2001 From: Paul Holzinger Date: Fri, 10 Dec 2021 15:22:09 +0100 Subject: support advanced network configuration via cli Rework the --network parse logic to support multiple networks with specific network configuration settings. --network can now be set multiple times. For bridge network mode the following options have been added: - **alias=name**: Add network-scoped alias for the container. - **ip=IPv4**: Specify a static ipv4 address for this container. - **ip=IPv6**: Specify a static ipv6 address for this container. - **mac=MAC**: Specify a static mac address address for this container. - **interface_name**: Specify a name for the created network interface inside the container. So now you can set --network bridge:ip=10.88.0.10,mac=44:33:22:11:00:99 for the default bridge network as well as for network names. This is better than using --ip because we can set the ip per network without any confusion which network the ip address should be assigned to. The --ip, --mac-address and --network-alias options are still supported but --ip or --mac-address can only be set when only one network is set. This limitation already existed previously. The ability to specify a custom network interface name is new Fixes #11534 Signed-off-by: Paul Holzinger --- cmd/podman/common/create_opts.go | 9 +-- cmd/podman/common/netflags.go | 131 ++++++++++++++++++++++----------------- cmd/podman/containers/create.go | 2 +- cmd/podman/containers/run.go | 2 +- cmd/podman/pods/create.go | 4 +- 5 files changed, 80 insertions(+), 68 deletions(-) (limited to 'cmd') diff --git a/cmd/podman/common/create_opts.go b/cmd/podman/common/create_opts.go index 8f0cd1be6..990c1c063 100644 --- a/cmd/podman/common/create_opts.go +++ b/cmd/podman/common/create_opts.go @@ -156,18 +156,11 @@ func ContainerCreateToContainerCLIOpts(cc handlers.CreateContainerConfig, rtc *c } // netMode - nsmode, networks, err := specgen.ParseNetworkNamespace(string(cc.HostConfig.NetworkMode), true) + nsmode, networks, netOpts, err := specgen.ParseNetworkFlag([]string{string(cc.HostConfig.NetworkMode)}) if err != nil { return nil, nil, err } - var netOpts map[string][]string - parts := strings.SplitN(string(cc.HostConfig.NetworkMode), ":", 2) - if len(parts) > 1 { - netOpts = make(map[string][]string) - netOpts[parts[0]] = strings.Split(parts[1], ",") - } - // network // Note: we cannot emulate compat exactly here. we only allow specifics of networks to be // defined when there is only one network. diff --git a/cmd/podman/common/netflags.go b/cmd/podman/common/netflags.go index f79a9fd88..ba8ab7a8b 100644 --- a/cmd/podman/common/netflags.go +++ b/cmd/podman/common/netflags.go @@ -61,8 +61,8 @@ func DefineNetFlags(cmd *cobra.Command) { _ = cmd.RegisterFlagCompletionFunc(macAddressFlagName, completion.AutocompleteNone) networkFlagName := "network" - netFlags.String( - networkFlagName, containerConfig.NetNS(), + netFlags.StringArray( + networkFlagName, nil, "Connect a container to a network", ) _ = cmd.RegisterFlagCompletionFunc(networkFlagName, AutocompleteNetworkFlag) @@ -88,9 +88,7 @@ func DefineNetFlags(cmd *cobra.Command) { } // NetFlagsToNetOptions parses the network flags for the given cmd. -// The netnsFromConfig bool is used to indicate if the --network flag -// should always be parsed regardless if it was set on the cli. -func NetFlagsToNetOptions(opts *entities.NetOptions, flags pflag.FlagSet, netnsFromConfig bool) (*entities.NetOptions, error) { +func NetFlagsToNetOptions(opts *entities.NetOptions, flags pflag.FlagSet) (*entities.NetOptions, error) { var ( err error ) @@ -168,79 +166,100 @@ func NetFlagsToNetOptions(opts *entities.NetOptions, flags pflag.FlagSet, netnsF return nil, err } - // parse the --network value only when the flag is set or we need to use - // the netns config value, e.g. when --pod is not used - if netnsFromConfig || flags.Changed("network") { - network, err := flags.GetString("network") + // parse the network only when network was changed + // otherwise we send default to server so that the server + // can pick the correct default instead of the client + if flags.Changed("network") { + network, err := flags.GetStringArray("network") if err != nil { return nil, err } - ns, networks, options, err := specgen.ParseNetworkString(network) + ns, networks, options, err := specgen.ParseNetworkFlag(network) if err != nil { return nil, err } - if len(options) > 0 { - opts.NetworkOptions = options - } + opts.NetworkOptions = options opts.Network = ns opts.Networks = networks } - ip, err := flags.GetString("ip") - if err != nil { - return nil, err - } - if ip != "" { - staticIP := net.ParseIP(ip) - if staticIP == nil { - return nil, errors.Errorf("%s is not an ip address", ip) - } - if !opts.Network.IsBridge() { - return nil, errors.Wrap(define.ErrInvalidArg, "--ip can only be set when the network mode is bridge") - } - if len(opts.Networks) != 1 { - return nil, errors.Wrap(define.ErrInvalidArg, "--ip can only be set for a single network") - } - for name, netOpts := range opts.Networks { - netOpts.StaticIPs = append(netOpts.StaticIPs, staticIP) - opts.Networks[name] = netOpts + if flags.Changed("ip") || flags.Changed("mac-address") || flags.Changed("network-alias") { + // if there is no network we add the default + if len(opts.Networks) == 0 { + opts.Networks = map[string]types.PerNetworkOptions{ + "default": {}, + } } - } - m, err := flags.GetString("mac-address") - if err != nil { - return nil, err - } - if len(m) > 0 { - mac, err := net.ParseMAC(m) + ip, err := flags.GetString("ip") if err != nil { return nil, err } - if !opts.Network.IsBridge() { - return nil, errors.Wrap(define.ErrInvalidArg, "--mac-address can only be set when the network mode is bridge") + if ip != "" { + // if pod create --infra=false + if infra, err := flags.GetBool("infra"); err == nil && !infra { + return nil, errors.Wrap(define.ErrInvalidArg, "cannot set --ip without infra container") + } + + staticIP := net.ParseIP(ip) + if staticIP == nil { + return nil, errors.Errorf("%s is not an ip address", ip) + } + if !opts.Network.IsBridge() && !opts.Network.IsDefault() { + return nil, errors.Wrap(define.ErrInvalidArg, "--ip can only be set when the network mode is bridge") + } + if len(opts.Networks) != 1 { + return nil, errors.Wrap(define.ErrInvalidArg, "--ip can only be set for a single network") + } + for name, netOpts := range opts.Networks { + netOpts.StaticIPs = append(netOpts.StaticIPs, staticIP) + opts.Networks[name] = netOpts + } } - if len(opts.Networks) != 1 { - return nil, errors.Wrap(define.ErrInvalidArg, "--mac-address can only be set for a single network") + + m, err := flags.GetString("mac-address") + if err != nil { + return nil, err } - for name, netOpts := range opts.Networks { - netOpts.StaticMAC = types.HardwareAddr(mac) - opts.Networks[name] = netOpts + if len(m) > 0 { + // if pod create --infra=false + if infra, err := flags.GetBool("infra"); err == nil && !infra { + return nil, errors.Wrap(define.ErrInvalidArg, "cannot set --mac without infra container") + } + mac, err := net.ParseMAC(m) + if err != nil { + return nil, err + } + if !opts.Network.IsBridge() && !opts.Network.IsDefault() { + return nil, errors.Wrap(define.ErrInvalidArg, "--mac-address can only be set when the network mode is bridge") + } + if len(opts.Networks) != 1 { + return nil, errors.Wrap(define.ErrInvalidArg, "--mac-address can only be set for a single network") + } + for name, netOpts := range opts.Networks { + netOpts.StaticMAC = types.HardwareAddr(mac) + opts.Networks[name] = netOpts + } } - } - aliases, err := flags.GetStringSlice("network-alias") - if err != nil { - return nil, err - } - if len(aliases) > 0 { - if !opts.Network.IsBridge() { - return nil, errors.Wrap(define.ErrInvalidArg, "--network-alias can only be set when the network mode is bridge") + aliases, err := flags.GetStringSlice("network-alias") + if err != nil { + return nil, err } - for name, netOpts := range opts.Networks { - netOpts.Aliases = aliases - opts.Networks[name] = netOpts + if len(aliases) > 0 { + // if pod create --infra=false + if infra, err := flags.GetBool("infra"); err == nil && !infra { + return nil, errors.Wrap(define.ErrInvalidArg, "cannot set --network-alias without infra container") + } + if !opts.Network.IsBridge() && !opts.Network.IsDefault() { + return nil, errors.Wrap(define.ErrInvalidArg, "--network-alias can only be set when the network mode is bridge") + } + for name, netOpts := range opts.Networks { + netOpts.Aliases = aliases + opts.Networks[name] = netOpts + } } } diff --git a/cmd/podman/containers/create.go b/cmd/podman/containers/create.go index a17fcaa1c..e004f4ab2 100644 --- a/cmd/podman/containers/create.go +++ b/cmd/podman/containers/create.go @@ -105,7 +105,7 @@ func create(cmd *cobra.Command, args []string) error { err error ) flags := cmd.Flags() - cliVals.Net, err = common.NetFlagsToNetOptions(nil, *flags, cliVals.Pod == "" && cliVals.PodIDFile == "") + cliVals.Net, err = common.NetFlagsToNetOptions(nil, *flags) if err != nil { return err } diff --git a/cmd/podman/containers/run.go b/cmd/podman/containers/run.go index 9d1b040cc..cfb89ce57 100644 --- a/cmd/podman/containers/run.go +++ b/cmd/podman/containers/run.go @@ -120,7 +120,7 @@ func run(cmd *cobra.Command, args []string) error { } flags := cmd.Flags() - cliVals.Net, err = common.NetFlagsToNetOptions(nil, *flags, cliVals.Pod == "" && cliVals.PodIDFile == "") + cliVals.Net, err = common.NetFlagsToNetOptions(nil, *flags) if err != nil { return err } diff --git a/cmd/podman/pods/create.go b/cmd/podman/pods/create.go index 7399dd029..f844812c2 100644 --- a/cmd/podman/pods/create.go +++ b/cmd/podman/pods/create.go @@ -116,7 +116,7 @@ func create(cmd *cobra.Command, args []string) error { return fmt.Errorf("cannot specify no-hosts without an infra container") } flags := cmd.Flags() - createOptions.Net, err = common.NetFlagsToNetOptions(nil, *flags, createOptions.Infra) + createOptions.Net, err = common.NetFlagsToNetOptions(nil, *flags) if err != nil { return err } @@ -133,7 +133,7 @@ func create(cmd *cobra.Command, args []string) error { } else { // reassign certain options for lbpod api, these need to be populated in spec flags := cmd.Flags() - infraOptions.Net, err = common.NetFlagsToNetOptions(nil, *flags, createOptions.Infra) + infraOptions.Net, err = common.NetFlagsToNetOptions(nil, *flags) if err != nil { return err } -- cgit v1.2.3-54-g00ecf