From fb7d16c7a841bbe2c02cb97b2820334b617e7f44 Mon Sep 17 00:00:00 2001 From: Paul Holzinger Date: Tue, 4 May 2021 17:06:57 +0200 Subject: add --mac-address to podman play kube Add a new --mac-address flag to podman play kube. This is used to specify a static MAC address which should be used for the pod. This option can be specified several times because play kube can create more than one pod. Fixes #9731 Signed-off-by: Paul Holzinger --- cmd/podman/play/kube.go | 15 ++++++++++++ docs/source/markdown/podman-play-kube.1.md | 4 ++++ pkg/api/handlers/libpod/play.go | 37 ++++++++++++++++++++---------- pkg/api/server/register_play.go | 6 +++++ pkg/bindings/play/types.go | 2 ++ pkg/bindings/play/types_kube_options.go | 16 +++++++++++++ pkg/domain/entities/play.go | 2 ++ pkg/domain/infra/abi/play.go | 10 ++++++-- pkg/domain/infra/tunnel/play.go | 3 ++- test/e2e/play_kube_test.go | 13 ++++++++++- 10 files changed, 92 insertions(+), 16 deletions(-) diff --git a/cmd/podman/play/kube.go b/cmd/podman/play/kube.go index 30d6d86f0..fe382bdfb 100644 --- a/cmd/podman/play/kube.go +++ b/cmd/podman/play/kube.go @@ -2,6 +2,7 @@ package pods import ( "fmt" + "net" "os" "github.com/containers/common/pkg/auth" @@ -27,6 +28,7 @@ type playKubeOptionsWrapper struct { } var ( + macs []string // https://kubernetes.io/docs/reference/command-line-tools-reference/kubelet/ defaultSeccompRoot = "/var/lib/kubelet/seccomp" kubeOptions = playKubeOptionsWrapper{} @@ -61,6 +63,10 @@ func init() { flags.StringVar(&kubeOptions.CredentialsCLI, credsFlagName, "", "`Credentials` (USERNAME:PASSWORD) to use for authenticating to a registry") _ = kubeCmd.RegisterFlagCompletionFunc(credsFlagName, completion.AutocompleteNone) + staticMACFlagName := "mac-address" + flags.StringSliceVar(&macs, staticMACFlagName, nil, "Static MAC addresses to assign to the pods") + _ = kubeCmd.RegisterFlagCompletionFunc(staticMACFlagName, completion.AutocompleteNone) + networkFlagName := "network" flags.StringVar(&kubeOptions.Network, networkFlagName, "", "Connect pod to CNI network(s)") _ = kubeCmd.RegisterFlagCompletionFunc(networkFlagName, common.AutocompleteNetworkFlag) @@ -128,6 +134,15 @@ func kube(cmd *cobra.Command, args []string) error { if yamlfile == "-" { yamlfile = "/dev/stdin" } + + for _, mac := range macs { + m, err := net.ParseMAC(mac) + if err != nil { + return err + } + kubeOptions.StaticMACs = append(kubeOptions.StaticMACs, m) + } + report, err := registry.ContainerEngine().PlayKube(registry.GetContext(), yamlfile, kubeOptions.PlayKubeOptions) if err != nil { return err diff --git a/docs/source/markdown/podman-play-kube.1.md b/docs/source/markdown/podman-play-kube.1.md index 1074c27f8..ab2019139 100644 --- a/docs/source/markdown/podman-play-kube.1.md +++ b/docs/source/markdown/podman-play-kube.1.md @@ -70,6 +70,10 @@ Assign a static ip address to the pod. This option can be specified several time Set logging driver for all created containers. +#### **\-\-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. + #### **\-\-network**=*networks*, **\-\-net** A comma-separated list of the names of CNI networks the pod should join. diff --git a/pkg/api/handlers/libpod/play.go b/pkg/api/handlers/libpod/play.go index 96f572a8b..90332924c 100644 --- a/pkg/api/handlers/libpod/play.go +++ b/pkg/api/handlers/libpod/play.go @@ -21,11 +21,12 @@ func PlayKube(w http.ResponseWriter, r *http.Request) { runtime := r.Context().Value("runtime").(*libpod.Runtime) decoder := r.Context().Value("decoder").(*schema.Decoder) query := struct { - Network string `schema:"network"` - TLSVerify bool `schema:"tlsVerify"` - LogDriver string `schema:"logDriver"` - Start bool `schema:"start"` - StaticIPs []string `schema:"staticIPs"` + Network string `schema:"network"` + TLSVerify bool `schema:"tlsVerify"` + LogDriver string `schema:"logDriver"` + Start bool `schema:"start"` + StaticIPs []string `schema:"staticIPs"` + StaticMACs []string `schema:"staticMACs"` }{ TLSVerify: true, Start: true, @@ -48,6 +49,17 @@ func PlayKube(w http.ResponseWriter, r *http.Request) { staticIPs = append(staticIPs, ip) } + staticMACs := make([]net.HardwareAddr, 0, len(query.StaticMACs)) + for _, macString := range query.StaticMACs { + mac, err := net.ParseMAC(macString) + if err != nil { + utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest, + err) + return + } + staticMACs = append(staticMACs, mac) + } + // Fetch the K8s YAML file from the body, and copy it to a temp file. tmpfile, err := ioutil.TempFile("", "libpod-play-kube.yml") if err != nil { @@ -78,13 +90,14 @@ func PlayKube(w http.ResponseWriter, r *http.Request) { containerEngine := abi.ContainerEngine{Libpod: runtime} options := entities.PlayKubeOptions{ - Authfile: authfile, - Username: username, - Password: password, - Network: query.Network, - Quiet: true, - LogDriver: query.LogDriver, - StaticIPs: staticIPs, + Authfile: authfile, + Username: username, + Password: password, + Network: query.Network, + Quiet: true, + LogDriver: query.LogDriver, + StaticIPs: staticIPs, + StaticMACs: staticMACs, } if _, found := r.URL.Query()["tlsVerify"]; found { options.SkipTLSVerify = types.NewOptionalBool(!query.TLSVerify) diff --git a/pkg/api/server/register_play.go b/pkg/api/server/register_play.go index da37abb70..c51301aa8 100644 --- a/pkg/api/server/register_play.go +++ b/pkg/api/server/register_play.go @@ -40,6 +40,12 @@ func (s *APIServer) registerPlayHandlers(r *mux.Router) error { // description: Static IPs used for the pods. // items: // type: string + // - in: query + // name: staticMACs + // type: array + // description: Static MACs used for the pods. + // items: + // type: string // - in: body // name: request // description: Kubernetes YAML file. diff --git a/pkg/bindings/play/types.go b/pkg/bindings/play/types.go index 6598ec3c2..52a72c7b6 100644 --- a/pkg/bindings/play/types.go +++ b/pkg/bindings/play/types.go @@ -27,6 +27,8 @@ type KubeOptions struct { SeccompProfileRoot *string // StaticIPs - Static IP address used by the pod(s). StaticIPs *[]net.IP + // StaticMACs - Static MAC address used by the pod(s). + StaticMACs *[]net.HardwareAddr // ConfigMaps - slice of pathnames to kubernetes configmap YAMLs. ConfigMaps *[]string // LogDriver for the container. For example: journald diff --git a/pkg/bindings/play/types_kube_options.go b/pkg/bindings/play/types_kube_options.go index a1786f553..4cc7d6f21 100644 --- a/pkg/bindings/play/types_kube_options.go +++ b/pkg/bindings/play/types_kube_options.go @@ -181,6 +181,22 @@ func (o *KubeOptions) GetStaticIPs() []net.IP { return *o.StaticIPs } +// WithStaticMACs +func (o *KubeOptions) WithStaticMACs(value []net.HardwareAddr) *KubeOptions { + v := &value + o.StaticMACs = v + return o +} + +// GetStaticMACs +func (o *KubeOptions) GetStaticMACs() []net.HardwareAddr { + var staticMACs []net.HardwareAddr + if o.StaticMACs == nil { + return staticMACs + } + return *o.StaticMACs +} + // WithConfigMaps func (o *KubeOptions) WithConfigMaps(value []string) *KubeOptions { v := &value diff --git a/pkg/domain/entities/play.go b/pkg/domain/entities/play.go index c69bb0867..89dfc08e9 100644 --- a/pkg/domain/entities/play.go +++ b/pkg/domain/entities/play.go @@ -30,6 +30,8 @@ type PlayKubeOptions struct { SeccompProfileRoot string // StaticIPs - Static IP address used by the pod(s). StaticIPs []net.IP + // StaticMACs - Static MAC address used by the pod(s). + StaticMACs []net.HardwareAddr // ConfigMaps - slice of pathnames to kubernetes configmap YAMLs. ConfigMaps []string // LogDriver for the container. For example: journald diff --git a/pkg/domain/infra/abi/play.go b/pkg/domain/infra/abi/play.go index 6ddd4a042..4b8be6c35 100644 --- a/pkg/domain/infra/abi/play.go +++ b/pkg/domain/infra/abi/play.go @@ -199,11 +199,17 @@ func (ic *ContainerEngine) playKubePod(ctx context.Context, podName string, podY } if len(options.StaticIPs) > *ipIndex { p.StaticIP = &options.StaticIPs[*ipIndex] - *ipIndex++ } else if len(options.StaticIPs) > 0 { - // only warn if the user has set at least one ip ip + // 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 { + p.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++ // Create the Pod pod, err := generate.MakePod(p, ic.Libpod) diff --git a/pkg/domain/infra/tunnel/play.go b/pkg/domain/infra/tunnel/play.go index e52e1a1f7..e66ff0308 100644 --- a/pkg/domain/infra/tunnel/play.go +++ b/pkg/domain/infra/tunnel/play.go @@ -11,7 +11,8 @@ 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).WithStaticIPs(opts.StaticIPs) + options.WithLogDriver(opts.LogDriver).WithNetwork(opts.Network).WithSeccompProfileRoot(opts.SeccompProfileRoot) + options.WithStaticIPs(opts.StaticIPs).WithStaticMACs(opts.StaticMACs) if s := opts.SkipTLSVerify; s != types.OptionalBoolUndefined { options.WithSkipTLSVerify(s == types.OptionalBoolTrue) diff --git a/test/e2e/play_kube_test.go b/test/e2e/play_kube_test.go index f89da4c05..836fbe1ee 100644 --- a/test/e2e/play_kube_test.go +++ b/test/e2e/play_kube_test.go @@ -1717,7 +1717,7 @@ spec: } }) - It("podman play kube --ip", func() { + It("podman play kube --ip and --mac-address", func() { var i, numReplicas int32 numReplicas = 3 deployment := getDeployment(withReplicas(numReplicas)) @@ -1735,6 +1735,10 @@ spec: for _, ip := range ips { playArgs = append(playArgs, "--ip", ip) } + macs := []string{"e8:d8:82:c9:80:40", "e8:d8:82:c9:80:50", "e8:d8:82:c9:80:60"} + for _, mac := range macs { + playArgs = append(playArgs, "--mac-address", mac) + } kube := podmanTest.Podman(append(playArgs, kubeYaml)) kube.WaitWithDefaultTimeout() @@ -1747,6 +1751,13 @@ spec: Expect(inspect.ExitCode()).To(Equal(0)) Expect(inspect.OutputToString()).To(Equal(ips[i])) } + + for i = 0; i < numReplicas; i++ { + inspect := podmanTest.Podman([]string{"inspect", getCtrNameInPod(&podNames[i]), "--format", "{{ .NetworkSettings.Networks." + net + ".MacAddress }}"}) + inspect.WaitWithDefaultTimeout() + Expect(inspect.ExitCode()).To(Equal(0)) + Expect(inspect.OutputToString()).To(Equal(macs[i])) + } }) It("podman play kube test with network portbindings", func() { -- cgit v1.2.3-54-g00ecf