summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cmd/podman/play/kube.go4
-rw-r--r--docs/source/markdown/podman-play-kube.1.md4
-rw-r--r--pkg/api/handlers/libpod/play.go22
-rw-r--r--pkg/api/server/register_play.go6
-rw-r--r--pkg/bindings/play/types.go4
-rw-r--r--pkg/bindings/play/types_kube_options.go17
-rw-r--r--pkg/domain/entities/play.go8
-rw-r--r--pkg/domain/infra/abi/play.go21
-rw-r--r--pkg/domain/infra/tunnel/play.go2
-rw-r--r--test/e2e/play_kube_test.go33
10 files changed, 110 insertions, 11 deletions
diff --git a/cmd/podman/play/kube.go b/cmd/podman/play/kube.go
index ddba5dc0f..30d6d86f0 100644
--- a/cmd/podman/play/kube.go
+++ b/cmd/podman/play/kube.go
@@ -65,6 +65,10 @@ func init() {
flags.StringVar(&kubeOptions.Network, networkFlagName, "", "Connect pod to CNI network(s)")
_ = kubeCmd.RegisterFlagCompletionFunc(networkFlagName, common.AutocompleteNetworkFlag)
+ staticIPFlagName := "ip"
+ flags.IPSliceVar(&kubeOptions.StaticIPs, staticIPFlagName, nil, "Static IP addresses to assign to the pods")
+ _ = kubeCmd.RegisterFlagCompletionFunc(staticIPFlagName, completion.AutocompleteNone)
+
logDriverFlagName := "log-driver"
flags.StringVar(&kubeOptions.LogDriver, logDriverFlagName, "", "Logging driver for the container")
_ = kubeCmd.RegisterFlagCompletionFunc(logDriverFlagName, common.AutocompleteLogDriver)
diff --git a/docs/source/markdown/podman-play-kube.1.md b/docs/source/markdown/podman-play-kube.1.md
index 91899a8bd..1074c27f8 100644
--- a/docs/source/markdown/podman-play-kube.1.md
+++ b/docs/source/markdown/podman-play-kube.1.md
@@ -62,6 +62,10 @@ The [username[:password]] to use to authenticate with the registry if required.
If one or both values are not supplied, a command line prompt will appear and the
value can be entered. The password is entered without echo.
+#### **\-\-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.
+
#### **\-\-log-driver**=driver
Set logging driver for all created containers.
diff --git a/pkg/api/handlers/libpod/play.go b/pkg/api/handlers/libpod/play.go
index eba5386b6..96f572a8b 100644
--- a/pkg/api/handlers/libpod/play.go
+++ b/pkg/api/handlers/libpod/play.go
@@ -3,6 +3,7 @@ package libpod
import (
"io"
"io/ioutil"
+ "net"
"net/http"
"os"
@@ -20,10 +21,11 @@ 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"`
+ Network string `schema:"network"`
+ TLSVerify bool `schema:"tlsVerify"`
+ LogDriver string `schema:"logDriver"`
+ Start bool `schema:"start"`
+ StaticIPs []string `schema:"staticIPs"`
}{
TLSVerify: true,
Start: true,
@@ -35,6 +37,17 @@ func PlayKube(w http.ResponseWriter, r *http.Request) {
return
}
+ staticIPs := make([]net.IP, 0, len(query.StaticIPs))
+ for _, ipString := range query.StaticIPs {
+ ip := net.ParseIP(ipString)
+ if ip == nil {
+ utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest,
+ errors.Errorf("Invalid IP address %s", ipString))
+ return
+ }
+ staticIPs = append(staticIPs, ip)
+ }
+
// 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 {
@@ -71,6 +84,7 @@ func PlayKube(w http.ResponseWriter, r *http.Request) {
Network: query.Network,
Quiet: true,
LogDriver: query.LogDriver,
+ StaticIPs: staticIPs,
}
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 d21029db5..da37abb70 100644
--- a/pkg/api/server/register_play.go
+++ b/pkg/api/server/register_play.go
@@ -34,6 +34,12 @@ func (s *APIServer) registerPlayHandlers(r *mux.Router) error {
// type: boolean
// default: true
// description: Start the pod after creating it.
+ // - in: query
+ // name: staticIPs
+ // type: array
+ // description: Static IPs 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 5fb9a4d41..6598ec3c2 100644
--- a/pkg/bindings/play/types.go
+++ b/pkg/bindings/play/types.go
@@ -1,5 +1,7 @@
package play
+import "net"
+
//go:generate go run ../generator/generator.go KubeOptions
// KubeOptions are optional options for replaying kube YAML files
type KubeOptions struct {
@@ -23,6 +25,8 @@ type KubeOptions struct {
// SeccompProfileRoot - path to a directory containing seccomp
// profiles.
SeccompProfileRoot *string
+ // StaticIPs - Static IP address used by the pod(s).
+ StaticIPs *[]net.IP
// 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 78396a090..a1786f553 100644
--- a/pkg/bindings/play/types_kube_options.go
+++ b/pkg/bindings/play/types_kube_options.go
@@ -1,6 +1,7 @@
package play
import (
+ "net"
"net/url"
"github.com/containers/podman/v3/pkg/bindings/internal/util"
@@ -164,6 +165,22 @@ func (o *KubeOptions) GetSeccompProfileRoot() string {
return *o.SeccompProfileRoot
}
+// WithStaticIPs
+func (o *KubeOptions) WithStaticIPs(value []net.IP) *KubeOptions {
+ v := &value
+ o.StaticIPs = v
+ return o
+}
+
+// GetStaticIPs
+func (o *KubeOptions) GetStaticIPs() []net.IP {
+ var staticIPs []net.IP
+ if o.StaticIPs == nil {
+ return staticIPs
+ }
+ return *o.StaticIPs
+}
+
// 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 cd8bb9506..c69bb0867 100644
--- a/pkg/domain/entities/play.go
+++ b/pkg/domain/entities/play.go
@@ -1,6 +1,10 @@
package entities
-import "github.com/containers/image/v5/types"
+import (
+ "net"
+
+ "github.com/containers/image/v5/types"
+)
// PlayKubeOptions controls playing kube YAML files.
type PlayKubeOptions struct {
@@ -24,6 +28,8 @@ type PlayKubeOptions struct {
// SeccompProfileRoot - path to a directory containing seccomp
// profiles.
SeccompProfileRoot string
+ // StaticIPs - Static IP address used by the pod(s).
+ StaticIPs []net.IP
// 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 6eecef2de..4a13a8029 100644
--- a/pkg/domain/infra/abi/play.go
+++ b/pkg/domain/infra/abi/play.go
@@ -16,6 +16,7 @@ import (
"github.com/containers/podman/v3/libpod/define"
"github.com/containers/podman/v3/libpod/image"
"github.com/containers/podman/v3/pkg/domain/entities"
+ "github.com/containers/podman/v3/pkg/specgen"
"github.com/containers/podman/v3/pkg/specgen/generate"
"github.com/containers/podman/v3/pkg/specgen/generate/kube"
"github.com/containers/podman/v3/pkg/util"
@@ -50,6 +51,8 @@ func (ic *ContainerEngine) PlayKube(ctx context.Context, path string, options en
return nil, errors.Wrapf(err, "unable to sort kube kinds in %q", path)
}
+ ipIndex := 0
+
// create pod on each document if it is a pod or deployment
// any other kube kind will be skipped
for _, document := range documentList {
@@ -70,7 +73,7 @@ func (ic *ContainerEngine) PlayKube(ctx context.Context, path string, options en
podTemplateSpec.ObjectMeta = podYAML.ObjectMeta
podTemplateSpec.Spec = podYAML.Spec
- r, err := ic.playKubePod(ctx, podTemplateSpec.ObjectMeta.Name, &podTemplateSpec, options)
+ r, err := ic.playKubePod(ctx, podTemplateSpec.ObjectMeta.Name, &podTemplateSpec, options, &ipIndex)
if err != nil {
return nil, err
}
@@ -84,7 +87,7 @@ func (ic *ContainerEngine) PlayKube(ctx context.Context, path string, options en
return nil, errors.Wrapf(err, "unable to read YAML %q as Kube Deployment", path)
}
- r, err := ic.playKubeDeployment(ctx, &deploymentYAML, options)
+ r, err := ic.playKubeDeployment(ctx, &deploymentYAML, options, &ipIndex)
if err != nil {
return nil, err
}
@@ -118,7 +121,7 @@ func (ic *ContainerEngine) PlayKube(ctx context.Context, path string, options en
return report, nil
}
-func (ic *ContainerEngine) playKubeDeployment(ctx context.Context, deploymentYAML *v1apps.Deployment, options entities.PlayKubeOptions) (*entities.PlayKubeReport, error) {
+func (ic *ContainerEngine) playKubeDeployment(ctx context.Context, deploymentYAML *v1apps.Deployment, options entities.PlayKubeOptions, ipIndex *int) (*entities.PlayKubeReport, error) {
var (
deploymentName string
podSpec v1.PodTemplateSpec
@@ -140,7 +143,7 @@ func (ic *ContainerEngine) playKubeDeployment(ctx context.Context, deploymentYAM
// create "replicas" number of pods
for i = 0; i < numReplicas; i++ {
podName := fmt.Sprintf("%s-pod-%d", deploymentName, i)
- podReport, err := ic.playKubePod(ctx, podName, &podSpec, options)
+ podReport, err := ic.playKubePod(ctx, podName, &podSpec, options, ipIndex)
if err != nil {
return nil, errors.Wrapf(err, "error encountered while bringing up pod %s", podName)
}
@@ -149,7 +152,7 @@ func (ic *ContainerEngine) playKubeDeployment(ctx context.Context, deploymentYAM
return &report, nil
}
-func (ic *ContainerEngine) playKubePod(ctx context.Context, podName string, podYAML *v1.PodTemplateSpec, options entities.PlayKubeOptions) (*entities.PlayKubeReport, error) {
+func (ic *ContainerEngine) playKubePod(ctx context.Context, podName string, podYAML *v1.PodTemplateSpec, options entities.PlayKubeOptions, ipIndex *int) (*entities.PlayKubeReport, error) {
var (
registryCreds *types.DockerAuthConfig
writer io.Writer
@@ -190,9 +193,17 @@ func (ic *ContainerEngine) playKubePod(ctx context.Context, podName string, podY
// networks.
networks := strings.Split(options.Network, ",")
logrus.Debugf("Pod joining CNI networks: %v", networks)
+ p.NetNS.NSMode = specgen.Bridge
p.CNINetworks = append(p.CNINetworks, networks...)
}
}
+ 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
+ logrus.Warn("No more static ips left using a random one")
+ }
// 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 9f9076114..e52e1a1f7 100644
--- a/pkg/domain/infra/tunnel/play.go
+++ b/pkg/domain/infra/tunnel/play.go
@@ -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.Network).WithSeccompProfileRoot(opts.SeccompProfileRoot).WithStaticIPs(opts.StaticIPs)
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 e479b88cc..5c25c4459 100644
--- a/test/e2e/play_kube_test.go
+++ b/test/e2e/play_kube_test.go
@@ -12,6 +12,7 @@ import (
"github.com/containers/podman/v3/pkg/util"
. "github.com/containers/podman/v3/test/utils"
+ "github.com/containers/storage/pkg/stringid"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"github.com/opencontainers/selinux/go-selinux"
@@ -1716,6 +1717,38 @@ spec:
}
})
+ It("podman play kube --ip", func() {
+ var i, numReplicas int32
+ numReplicas = 3
+ deployment := getDeployment(withReplicas(numReplicas))
+ err := generateKubeYaml("deployment", deployment, kubeYaml)
+ Expect(err).To(BeNil())
+
+ net := "playkube" + stringid.GenerateNonCryptoID()
+ session := podmanTest.Podman([]string{"network", "create", "--subnet", "10.25.31.0/24", net})
+ session.WaitWithDefaultTimeout()
+ defer podmanTest.removeCNINetwork(net)
+ Expect(session.ExitCode()).To(BeZero())
+
+ ips := []string{"10.25.31.5", "10.25.31.10", "10.25.31.15"}
+ playArgs := []string{"play", "kube", "--network", net}
+ for _, ip := range ips {
+ playArgs = append(playArgs, "--ip", ip)
+ }
+
+ kube := podmanTest.Podman(append(playArgs, kubeYaml))
+ kube.WaitWithDefaultTimeout()
+ Expect(kube.ExitCode()).To(Equal(0))
+
+ podNames := getPodNamesInDeployment(deployment)
+ for i = 0; i < numReplicas; i++ {
+ inspect := podmanTest.Podman([]string{"inspect", getCtrNameInPod(&podNames[i]), "--format", "{{ .NetworkSettings.Networks." + net + ".IPAddress }}"})
+ inspect.WaitWithDefaultTimeout()
+ Expect(inspect.ExitCode()).To(Equal(0))
+ Expect(inspect.OutputToString()).To(Equal(ips[i]))
+ }
+ })
+
It("podman play kube test with network portbindings", func() {
ip := "127.0.0.100"
port := "5000"