Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions cmd/podman/kube/play.go
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,9 @@ func playFlags(cmd *cobra.Command) {
noPodPrefix := "no-pod-prefix"
flags.BoolVar(&playOptions.NoPodPrefix, noPodPrefix, false, "Do not prefix container name with pod name")

replicas := "replicas"
flags.BoolVar(&playOptions.Replicas, replicas, false, "Replicas allows multiple Pods creation")

if !registry.IsRemote() {
certDirFlagName := "cert-dir"
flags.StringVar(&playOptions.CertDir, certDirFlagName, "", "`Pathname` of a directory containing TLS certificates and keys")
Expand Down
6 changes: 6 additions & 0 deletions docs/source/markdown/podman-kube-play.1.md.in
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,8 @@ Define or override a port definition in the YAML file.
The lists of ports in the YAML file and the command line are merged. Matching is done by using the **containerPort** field.
If **containerPort** exists in both the YAML file and the option, the latter takes precedence.

In case this options is used with **--replicas** option then number of published ports should be qual to the number of replicas in container spec, e.g. Deployment.spec.replicas is 3, then **--publish** is set to "8080:80,8081:80,8082:80"

#### **--publish-all**

Setting this option to `true` will expose all ports to the host,
Expand All @@ -289,6 +291,10 @@ Suppress output information when pulling images

Tears down the pods created by a previous run of `kube play` and recreates the pods. This option is used to keep the existing pods up to date based upon the Kubernetes YAML.

#### **--replicas**

Only one Pod replica is created by default, despite the desired number of Pods in the Kubernetes Deployment resource. This setting overrides the default behavior, allowing the desired number of Pods to be created.

#### **--seccomp-profile-root**=*path*

Directory path for seccomp profiles (default: "/var/lib/kubelet/seccomp"). (This option is not available with the remote Podman client, including Mac and Windows (excluding WSL2) machines)
Expand Down
2 changes: 2 additions & 0 deletions pkg/api/handlers/libpod/kube.go
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ func KubePlay(w http.ResponseWriter, r *http.Request) {
Wait bool `schema:"wait"`
Build bool `schema:"build"`
NoPodPrefix bool `schema:"noPodPrefix"`
Replicas bool `schema:"replicas"`
}{
TLSVerify: true,
Start: true,
Expand Down Expand Up @@ -200,6 +201,7 @@ func KubePlay(w http.ResponseWriter, r *http.Request) {
Wait: query.Wait,
ContextDir: contextDirectory,
NoPodPrefix: query.NoPodPrefix,
Replicas: query.Replicas,
}
if _, found := r.URL.Query()["build"]; found {
options.Build = types.NewOptionalBool(query.Build)
Expand Down
2 changes: 2 additions & 0 deletions pkg/bindings/kube/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@ type PlayOptions struct {
Wait *bool
ServiceContainer *bool
NoPodPrefix *bool
// Replicas allows multiple Pods creation
Replicas *bool
}

// ApplyOptions are optional options for applying kube YAML files to a k8s cluster
Expand Down
15 changes: 15 additions & 0 deletions pkg/bindings/kube/types_play_options.go

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

4 changes: 4 additions & 0 deletions pkg/domain/entities/play.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,10 @@ type PlayKubeOptions struct {
SystemContext *types.SystemContext
// Do not prefix container name with pod name
NoPodPrefix bool
// MultiplePods - allows creating of multiple Pods
MultiplePods bool
// Replicas allows multiple Pods creation
Replicas bool
}

// PlayKubePod represents a single pod and associated containers created by play kube
Expand Down
56 changes: 40 additions & 16 deletions pkg/domain/infra/abi/play.go
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,7 @@ func (ic *ContainerEngine) PlayKube(ctx context.Context, body io.Reader, options
ipIndex := 0

var configMaps []v1.ConfigMap
var numReplicas int32

ranContainers := false
// set the ranContainers bool to true if at least one container was successfully started.
Expand Down Expand Up @@ -404,15 +405,46 @@ func (ic *ContainerEngine) PlayKube(ctx context.Context, body io.Reader, options
return nil, fmt.Errorf("unable to read YAML as Kube Deployment: %w", err)
}

r, proxies, err := ic.playKubeDeployment(ctx, &deploymentYAML, options, &ipIndex, configMaps, serviceContainer)
if err != nil {
return nil, err
numReplicas = 1
if deploymentYAML.Spec.Replicas != nil {
numReplicas = *deploymentYAML.Spec.Replicas
}
notifyProxies = append(notifyProxies, proxies...)

report.Pods = append(report.Pods, r.Pods...)
if numReplicas > 1 && options.Replicas {
if len(options.PublishPorts) > 0 && len(options.PublishPorts) != int(numReplicas) {
return nil, fmt.Errorf("number of Pod replics aren't equal to the number of published ports: %d replicas, %d published ports", numReplicas, len(options.PublishPorts))
}

var portToPublish = make([]string, numReplicas)
copy(portToPublish, options.PublishPorts)
options.PublishPorts = make([]string, 1)
for i := range numReplicas {
options.PublishPorts[0] = portToPublish[i]
podName := fmt.Sprintf("%s-pod-%d", deploymentYAML.ObjectMeta.Name, i)
r, proxies, err := ic.playKubeDeployment(ctx, &deploymentYAML, options, &ipIndex, configMaps, serviceContainer, podName)
if err != nil {
return nil, err
}
notifyProxies = append(notifyProxies, proxies...)

report.Pods = append(report.Pods, r.Pods...)
setRanContainers(r)
}
} else {
if numReplicas > 1 {
logrus.Warnf("Limiting replica count to 1, use `--replicas` to enable more than one replica")
}
podName := fmt.Sprintf("%s-pod", deploymentYAML.ObjectMeta.Name)
r, proxies, err := ic.playKubeDeployment(ctx, &deploymentYAML, options, &ipIndex, configMaps, serviceContainer, podName)
if err != nil {
return nil, err
}
notifyProxies = append(notifyProxies, proxies...)

report.Pods = append(report.Pods, r.Pods...)
setRanContainers(r)
}
validKinds++
setRanContainers(r)
case "Job":
var jobYAML v1.Job

Expand Down Expand Up @@ -563,28 +595,20 @@ func (ic *ContainerEngine) playKubeDaemonSet(ctx context.Context, daemonSetYAML
return &report, proxies, nil
}

func (ic *ContainerEngine) playKubeDeployment(ctx context.Context, deploymentYAML *v1apps.Deployment, options entities.PlayKubeOptions, ipIndex *int, configMaps []v1.ConfigMap, serviceContainer *libpod.Container) (*entities.PlayKubeReport, []*notifyproxy.NotifyProxy, error) {
func (ic *ContainerEngine) playKubeDeployment(ctx context.Context, deploymentYAML *v1apps.Deployment, options entities.PlayKubeOptions, ipIndex *int, configMaps []v1.ConfigMap, serviceContainer *libpod.Container, podName string) (*entities.PlayKubeReport, []*notifyproxy.NotifyProxy, error) {
var (
deploymentName string
podSpec v1.PodTemplateSpec
numReplicas int32
report entities.PlayKubeReport
)

deploymentName = deploymentYAML.ObjectMeta.Name
if deploymentName == "" {
return nil, nil, errors.New("deployment does not have a name")
}
numReplicas = 1
if deploymentYAML.Spec.Replicas != nil {
numReplicas = *deploymentYAML.Spec.Replicas
}
if numReplicas > 1 {
logrus.Warnf("Limiting replica count to 1, more than one replica is not supported by Podman")
}

podSpec = deploymentYAML.Spec.Template

podName := fmt.Sprintf("%s-pod", deploymentName)
podReport, proxies, err := ic.playKubePod(ctx, podName, &podSpec, options, ipIndex, deploymentYAML.Annotations, configMaps, serviceContainer)
if err != nil {
return nil, nil, fmt.Errorf("encountered while bringing up pod %s: %w", podName, err)
Expand Down
1 change: 1 addition & 0 deletions pkg/domain/infra/tunnel/kube.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ func (ic *ContainerEngine) PlayKube(_ context.Context, body io.Reader, opts enti
options.WithPublishAllPorts(opts.PublishAllPorts)
options.WithNoTrunc(opts.UseLongAnnotations)
options.WithNoPodPrefix(opts.NoPodPrefix)
options.WithReplicas(opts.Replicas)
return play.KubeWithBody(ic.ClientCtx, body, options)
}

Expand Down
44 changes: 39 additions & 5 deletions test/e2e/play_kube_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -507,6 +507,28 @@ spec:
periodSeconds: 1
`

var replicasPodYaml = `
apiVersion: apps/v1
kind: Deployment
metadata:
name: replicas-pods-test
labels:
app: testimage
spec:
replicas: 2
selector:
matchLabels:
app: testimage
template:
metadata:
labels:
app: testimage
spec:
containers:
- name: testimage
image: ` + NGINX_IMAGE + `
`

var selinuxLabelPodYaml = `
apiVersion: v1
kind: Pod
Expand Down Expand Up @@ -3629,7 +3651,7 @@ spec:
if IsRemote() {
Expect(kube.ErrorToString()).To(BeEmpty())
} else {
Expect(kube.ErrorToString()).To(ContainSubstring("Limiting replica count to 1, more than one replica is not supported by Podman"))
Expect(kube.ErrorToString()).To(ContainSubstring("Limiting replica count to 1, use `--replicas` to enable more than one replica"))
}

podName := getPodNameInDeployment(deployment)
Expand Down Expand Up @@ -3687,7 +3709,7 @@ spec:
if IsRemote() {
Expect(kube.ErrorToString()).To(BeEmpty())
} else {
Expect(kube.ErrorToString()).To(ContainSubstring("Limiting replica count to 1, more than one replica is not supported by Podman"))
Expect(kube.ErrorToString()).To(ContainSubstring("Limiting replica count to 1, use `--replicas` to enable more than one replica"))
}

podName := getPodNameInDeployment(deployment)
Expand Down Expand Up @@ -4170,7 +4192,7 @@ spec:
if IsRemote() {
Expect(kube.ErrorToString()).To(BeEmpty())
} else {
Expect(kube.ErrorToString()).To(ContainSubstring("Limiting replica count to 1, more than one replica is not supported by Podman"))
Expect(kube.ErrorToString()).To(ContainSubstring("Limiting replica count to 1, use `--replicas` to enable more than one replica"))
}

correctLabels := expectedLabelKey + ":" + expectedLabelValue
Expand Down Expand Up @@ -4211,7 +4233,7 @@ spec:
if IsRemote() {
Expect(kube.ErrorToString()).To(BeEmpty())
} else {
Expect(kube.ErrorToString()).To(ContainSubstring("Limiting replica count to 1, more than one replica is not supported by Podman"))
Expect(kube.ErrorToString()).To(ContainSubstring("Limiting replica count to 1, use `--replicas` to enable more than one replica"))
}

pod := getPodNameInDeployment(deployment)
Expand Down Expand Up @@ -5811,7 +5833,7 @@ spec:

// warnings are only propagated to local clients
if !IsRemote() {
Expect(kube.ErrorToString()).Should(ContainSubstring("Limiting replica count to 1, more than one replica is not supported by Podman"))
Expect(kube.ErrorToString()).Should(ContainSubstring("Limiting replica count to 1, use `--replicas` to enable more than one replica"))
}

Expect(strings.Count(kube.OutputToString(), "Pod:")).To(Equal(1))
Expand Down Expand Up @@ -6481,4 +6503,16 @@ spec:
inspect := podmanTest.PodmanExitCleanly("inspect", "simpleWithoutPodPrefix")
Expect(inspect.InspectContainerToJSON()[0].Name).Should(Equal("simpleWithoutPodPrefix"))
})

It("multiple Pod replicas", func() {
err := writeYaml(replicasPodYaml, kubeYaml)
Expect(err).ToNot(HaveOccurred())

kube := podmanTest.Podman([]string{"kube", "play", "--replicas", "--publish", "8080:80,8081:80", kubeYaml})
kube.WaitWithDefaultTimeout()

podsCount := podmanTest.PodmanExitCleanly("pod", "ps", "-n")
podsCount.WaitWithDefaultTimeout()
Expect(podsCount.OutputToStringArray()).To(HaveLen(2))
})
})