Skip to content

Commit

Permalink
play kube add support for multiple networks
Browse files Browse the repository at this point in the history
Allow the same --network options for play kube as for podman run/create.

Signed-off-by: Paul Holzinger <[email protected]>
  • Loading branch information
Luap99 committed Dec 14, 2021
1 parent 5358184 commit 3e9af20
Show file tree
Hide file tree
Showing 11 changed files with 105 additions and 47 deletions.
2 changes: 1 addition & 1 deletion cmd/podman/play/kube.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
18 changes: 14 additions & 4 deletions docs/source/markdown/podman-play-kube.1.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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:
Expand Down Expand Up @@ -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
```

Expand Down
4 changes: 2 additions & 2 deletions pkg/api/handlers/libpod/play.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"`
Expand Down Expand Up @@ -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,
Expand Down
6 changes: 4 additions & 2 deletions pkg/api/server/register_play.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,10 @@ func (s *APIServer) registerPlayHandlers(r *mux.Router) error {
// parameters:
// - in: query
// name: network
// type: string
// description: Connect the pod to this network.
// type: array
// description: USe the network mode or specify an array of networks.
// items:
// type: string
// - in: query
// name: tlsVerify
// type: boolean
Expand Down
4 changes: 2 additions & 2 deletions pkg/bindings/play/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
6 changes: 3 additions & 3 deletions pkg/bindings/play/types_kube_options.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions pkg/domain/entities/play.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
69 changes: 41 additions & 28 deletions pkg/domain/infra/abi/play.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -195,39 +196,51 @@ 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})
if err != nil {
return nil, err
}
ns, networks, netOpts, err := specgen.ParseNetworkFlag(options.Networks)
if err != nil {
return nil, err
}

if (ns.IsBridge() && len(networks) == 0) || ns.IsHost() {
return nil, errors.Errorf("invalid value passed to --network: bridge or host networking must be configured in YAML")
}
if (ns.IsBridge() && len(networks) == 0) || ns.IsHost() {
return nil, errors.Errorf("invalid value passed to --network: bridge or host networking must be configured in YAML")
}

podOpt.Net.Network = ns
if len(networks) > 0 {
podOpt.Net.Networks = networks
podOpt.Net.Network = ns
podOpt.Net.Networks = networks
podOpt.Net.NetworkOptions = netOpts

// 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")
}
if len(netOpts) > 0 {
podOpt.Net.NetworkOptions = netOpts
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")
}

// 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++
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 {
Expand Down
2 changes: 1 addition & 1 deletion pkg/domain/infra/tunnel/play.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
35 changes: 35 additions & 0 deletions test/e2e/play_kube_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
2 changes: 0 additions & 2 deletions test/system/700-play.bats
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit 3e9af20

Please sign in to comment.