play kube add support for multiple networks

Allow the same --network options for play kube as for podman run/create.

Signed-off-by: Paul Holzinger <pholzing@redhat.com>
This commit is contained in:
Paul Holzinger
2021-12-13 15:56:20 +01:00
parent 535818414c
commit 3e9af2029f
11 changed files with 108 additions and 50 deletions

View File

@ -69,7 +69,7 @@ func init() {
_ = kubeCmd.RegisterFlagCompletionFunc(staticMACFlagName, completion.AutocompleteNone)
networkFlagName := "network"
flags.StringVar(&kubeOptions.Network, networkFlagName, "", "Connect pod to CNI network(s)")
flags.StringArrayVar(&kubeOptions.Networks, networkFlagName, nil, "Connect pod to network(s) or network mode")
_ = kubeCmd.RegisterFlagCompletionFunc(networkFlagName, common.AutocompleteNetworkFlag)
staticIPFlagName := "ip"

View File

@ -142,6 +142,7 @@ removed. Any volumes created are left intact.
#### **--ip**=*IP address*
Assign a static ip address to the pod. This option can be specified several times when play kube creates more than one pod.
Note: When joining multiple networks you should use the **--network name:ip=\<ip\>** syntax.
#### **--log-driver**=driver
@ -167,15 +168,24 @@ This option is currently supported only by the **journald** log driver.
#### **--mac-address**=*MAC address*
Assign a static mac address to the pod. This option can be specified several times when play kube creates more than one pod.
Note: When joining multiple networks you should use the **--network name:mac=\<mac\>** syntax.
#### **--network**=*mode*, **--net**
Change the network mode of the pod. The host and bridge network mode should be configured in the yaml file.
Change the network mode of the pod. The host network mode should be configured in the YAML file.
Valid _mode_ values are:
- **bridge[:OPTIONS,...]**: Create a network stack on the default bridge. This is the default for rootfull containers. It is possible to specify these additional options:
- **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.
For example to set a static ipv4 address and a static mac address, use `--network bridge:ip=10.88.0.10,mac=44:33:22:11:00:99`.
- \<network name or ID\>[:OPTIONS,...]: Connect to a user-defined network; this is the network name or ID from a network created by **[podman network create](podman-network-create.1.md)**. Using the network name implies the bridge network mode. It is possible to specify the same options described under the bridge mode above. You can use the **--network** option multiple times to specify additional networks.
- **none**: Create a network namespace for the container but do not configure network interfaces for it, thus the container has no network connectivity.
- **container:**_id_: Reuse another container's network stack.
- **network**: Connect to a user-defined network, multiple networks should be comma-separated.
- **ns:**_path_: Path to a network namespace to join.
- **private**: Create a new namespace for the container. This will use the **bridge** mode for rootfull containers and **slirp4netns** for rootless ones.
- **slirp4netns[:OPTIONS,...]**: use **slirp4netns**(1) to create a user network stack. This is the default for rootless containers. It is possible to specify these additional options:
@ -253,9 +263,9 @@ $ podman play kube demo.yml --configmap configmap-foo.yml --configmap configmap-
52182811df2b1e73f36476003a66ec872101ea59034ac0d4d3a7b40903b955a6
```
CNI network(s) can be specified as comma-separated list using ``--network``
Create a pod connected to two networks (called net1 and net2) with a static ip
```
$ podman play kube demo.yml --network cni1,cni2
$ podman play kube demo.yml --network net1:ip=10.89.1.5 --network net2:ip=10.89.10.10
52182811df2b1e73f36476003a66ec872101ea59034ac0d4d3a7b40903b955a6
```

View File

@ -23,7 +23,7 @@ func PlayKube(w http.ResponseWriter, r *http.Request) {
runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime)
decoder := r.Context().Value(api.DecoderKey).(*schema.Decoder)
query := struct {
Network string `schema:"network"`
Network []string `schema:"network"`
TLSVerify bool `schema:"tlsVerify"`
LogDriver string `schema:"logDriver"`
LogOptions []string `schema:"logOptions"`
@ -103,7 +103,7 @@ func PlayKube(w http.ResponseWriter, r *http.Request) {
Authfile: authfile,
Username: username,
Password: password,
Network: query.Network,
Networks: query.Network,
NoHosts: query.NoHosts,
Quiet: true,
LogDriver: query.LogDriver,

View File

@ -18,8 +18,10 @@ func (s *APIServer) registerPlayHandlers(r *mux.Router) error {
// parameters:
// - in: query
// name: network
// type: array
// description: USe the network mode or specify an array of networks.
// items:
// type: string
// description: Connect the pod to this network.
// - in: query
// name: tlsVerify
// type: boolean

View File

@ -15,8 +15,8 @@ type KubeOptions struct {
Username *string
// Password for authenticating against the registry.
Password *string
// Network - name of the CNI network to connect to.
Network *string
// Network - name of the networks to connect to.
Network *[]string
// NoHosts - do not generate /etc/hosts file in pod's containers
NoHosts *bool
// Quiet - suppress output when pulling images.

View File

@ -79,15 +79,15 @@ func (o *KubeOptions) GetPassword() string {
}
// WithNetwork set field Network to given value
func (o *KubeOptions) WithNetwork(value string) *KubeOptions {
func (o *KubeOptions) WithNetwork(value []string) *KubeOptions {
o.Network = &value
return o
}
// GetNetwork returns value of field Network
func (o *KubeOptions) GetNetwork() string {
func (o *KubeOptions) GetNetwork() []string {
if o.Network == nil {
var z string
var z []string
return z
}
return *o.Network

View File

@ -26,8 +26,8 @@ type PlayKubeOptions struct {
Username string
// Password for authenticating against the registry.
Password string
// Network - name of the CNI network to connect to.
Network string
// Networks - name of the network to connect to.
Networks []string
// Quiet - suppress output when pulling images.
Quiet bool
// SignaturePolicy - path to a signature-policy file.

View File

@ -17,6 +17,7 @@ import (
"github.com/containers/image/v5/types"
"github.com/containers/podman/v3/libpod"
"github.com/containers/podman/v3/libpod/define"
nettypes "github.com/containers/podman/v3/libpod/network/types"
"github.com/containers/podman/v3/pkg/autoupdate"
"github.com/containers/podman/v3/pkg/domain/entities"
"github.com/containers/podman/v3/pkg/specgen"
@ -195,8 +196,7 @@ func (ic *ContainerEngine) playKubePod(ctx context.Context, podName string, podY
return nil, err
}
if options.Network != "" {
ns, networks, netOpts, err := specgen.ParseNetworkFlag([]string{options.Network})
ns, networks, netOpts, err := specgen.ParseNetworkFlag(options.Networks)
if err != nil {
return nil, err
}
@ -206,28 +206,41 @@ func (ic *ContainerEngine) playKubePod(ctx context.Context, podName string, podY
}
podOpt.Net.Network = ns
if len(networks) > 0 {
podOpt.Net.Networks = networks
}
if len(netOpts) > 0 {
podOpt.Net.NetworkOptions = netOpts
}
}
// FIXME This is very hard to support properly
// if len(options.StaticIPs) > *ipIndex {
// podOpt.Net.StaticIP = &options.StaticIPs[*ipIndex]
// } else if len(options.StaticIPs) > 0 {
// // only warn if the user has set at least one ip
// logrus.Warn("No more static ips left using a random one")
// }
// if len(options.StaticMACs) > *ipIndex {
// podOpt.Net.StaticMAC = &options.StaticMACs[*ipIndex]
// } else if len(options.StaticIPs) > 0 {
// // only warn if the user has set at least one mac
// logrus.Warn("No more static macs left using a random one")
// }
// *ipIndex++
// FIXME This is very hard to support properly with a good ux
if len(options.StaticIPs) > *ipIndex {
if !podOpt.Net.Network.IsBridge() {
errors.Wrap(define.ErrInvalidArg, "static ip addresses can only be set when the network mode is bridge")
}
if len(podOpt.Net.Networks) != 1 {
return nil, errors.Wrap(define.ErrInvalidArg, "cannot set static ip addresses for more than network, use netname:ip=<ip> syntax to specify ips for more than network")
}
for name, netOpts := range podOpt.Net.Networks {
netOpts.StaticIPs = append(netOpts.StaticIPs, options.StaticIPs[*ipIndex])
podOpt.Net.Networks[name] = netOpts
}
} else if len(options.StaticIPs) > 0 {
// only warn if the user has set at least one ip
logrus.Warn("No more static ips left using a random one")
}
if len(options.StaticMACs) > *ipIndex {
if !podOpt.Net.Network.IsBridge() {
errors.Wrap(define.ErrInvalidArg, "static mac address can only be set when the network mode is bridge")
}
if len(podOpt.Net.Networks) != 1 {
return nil, errors.Wrap(define.ErrInvalidArg, "cannot set static mac address for more than network, use netname:mac=<mac> syntax to specify mac for more than network")
}
for name, netOpts := range podOpt.Net.Networks {
netOpts.StaticMAC = nettypes.HardwareAddr(options.StaticMACs[*ipIndex])
podOpt.Net.Networks[name] = netOpts
}
} else if len(options.StaticIPs) > 0 {
// only warn if the user has set at least one mac
logrus.Warn("No more static macs left using a random one")
}
*ipIndex++
p := specgen.NewPodSpecGenerator()
if err != nil {

View File

@ -11,7 +11,7 @@ import (
func (ic *ContainerEngine) PlayKube(ctx context.Context, path string, opts entities.PlayKubeOptions) (*entities.PlayKubeReport, error) {
options := new(play.KubeOptions).WithAuthfile(opts.Authfile).WithUsername(opts.Username).WithPassword(opts.Password)
options.WithCertDir(opts.CertDir).WithQuiet(opts.Quiet).WithSignaturePolicy(opts.SignaturePolicy).WithConfigMaps(opts.ConfigMaps)
options.WithLogDriver(opts.LogDriver).WithNetwork(opts.Network).WithSeccompProfileRoot(opts.SeccompProfileRoot)
options.WithLogDriver(opts.LogDriver).WithNetwork(opts.Networks).WithSeccompProfileRoot(opts.SeccompProfileRoot)
options.WithStaticIPs(opts.StaticIPs).WithStaticMACs(opts.StaticMACs)
if len(opts.LogOptions) > 0 {
options.WithLogOptions(opts.LogOptions)

View File

@ -2136,6 +2136,41 @@ spec:
}
})
It("podman play kube with multiple networks", func() {
ctr := getCtr(withImage(ALPINE))
pod := getPod(withCtr(ctr))
err := generateKubeYaml("pod", pod, kubeYaml)
Expect(err).To(BeNil())
net1 := "net1" + stringid.GenerateNonCryptoID()
net2 := "net2" + stringid.GenerateNonCryptoID()
net := podmanTest.Podman([]string{"network", "create", "--subnet", "10.0.11.0/24", net1})
net.WaitWithDefaultTimeout()
defer podmanTest.removeCNINetwork(net1)
Expect(net).Should(Exit(0))
net = podmanTest.Podman([]string{"network", "create", "--subnet", "10.0.12.0/24", net2})
net.WaitWithDefaultTimeout()
defer podmanTest.removeCNINetwork(net2)
Expect(net).Should(Exit(0))
ip1 := "10.0.11.5"
ip2 := "10.0.12.10"
kube := podmanTest.Podman([]string{"play", "kube", "--network", net1 + ":ip=" + ip1, "--network", net2 + ":ip=" + ip2, kubeYaml})
kube.WaitWithDefaultTimeout()
Expect(kube).Should(Exit(0))
inspect := podmanTest.Podman([]string{"exec", getCtrNameInPod(pod), "ip", "addr"})
inspect.WaitWithDefaultTimeout()
Expect(inspect).Should(Exit(0))
Expect(inspect.OutputToString()).To(ContainSubstring(ip1))
Expect(inspect.OutputToString()).To(ContainSubstring(ip2))
Expect(inspect.OutputToString()).To(ContainSubstring("eth0"))
Expect(inspect.OutputToString()).To(ContainSubstring("eth1"))
})
It("podman play kube test with network portbindings", func() {
ip := "127.0.0.100"
port := "5000"

View File

@ -104,8 +104,6 @@ RELABEL="system_u:object_r:container_file_t:s0"
TESTDIR=$PODMAN_TMPDIR/testdir
mkdir -p $TESTDIR
echo "$testYaml" | sed "s|TESTDIR|${TESTDIR}|g" > $PODMAN_TMPDIR/test.yaml
run_podman 125 play kube --network bridge $PODMAN_TMPDIR/test.yaml
is "$output" ".*invalid value passed to --network: bridge or host networking must be configured in YAML" "podman plan-network should fail with --network host"
run_podman 125 play kube --network host $PODMAN_TMPDIR/test.yaml
is "$output" ".*invalid value passed to --network: bridge or host networking must be configured in YAML" "podman plan-network should fail with --network host"
run_podman play kube --network slirp4netns:port_handler=slirp4netns $PODMAN_TMPDIR/test.yaml