Skip to content

Commit

Permalink
Pick a random port on the host for play kube deployments
Browse files Browse the repository at this point in the history
podman play kube started to expose the port of containers in k8s deployments on
the same port on the host with
containers#15946. However, that breaks once
replicas are involved, as they would then bind to the same port on the host.

With this commit, we change the default behavior so that podman will pick a
random port for the container instead of using `containerPort`, but only if no
`hostPort` is set.

This fixes: containers#16765

Signed-off-by: Dan Čermák <dcermak@suse.com>
  • Loading branch information
dcermak committed Dec 15, 2022
1 parent a55bdfa commit 5afd75b
Show file tree
Hide file tree
Showing 2 changed files with 57 additions and 9 deletions.
24 changes: 18 additions & 6 deletions pkg/specgen/generate/kube/kube.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import (
"github.com/containers/podman/v4/pkg/specgen/generate"
systemdDefine "github.com/containers/podman/v4/pkg/systemd/define"
"github.com/containers/podman/v4/pkg/util"
"github.com/containers/podman/v4/utils"
"github.com/docker/docker/pkg/system"
"github.com/docker/go-units"
"github.com/ghodss/yaml"
Expand Down Expand Up @@ -71,7 +72,11 @@ func ToPodOpt(ctx context.Context, podName string, p entities.PodCreateOptions,
}
p.Net.AddHosts = hosts
}
podPorts := getPodPorts(podYAML.Spec.Containers)

podPorts, err := getPodPorts(podYAML.Spec.Containers)
if err != nil {
return entities.PodCreateOptions{}, err
}
p.Net.PublishPorts = podPorts

if dnsConfig := podYAML.Spec.DNSConfig; dnsConfig != nil {
Expand Down Expand Up @@ -950,19 +955,26 @@ func getContainerResources(container v1.Container) (v1.ResourceRequirements, err

// getPodPorts converts a slice of kube container descriptions to an
// array of portmapping
func getPodPorts(containers []v1.Container) []types.PortMapping {
func getPodPorts(containers []v1.Container) ([]types.PortMapping, error) {
var infraPorts []types.PortMapping
for _, container := range containers {
for _, p := range container.Ports {
if p.HostPort != 0 && p.ContainerPort == 0 {
p.ContainerPort = p.HostPort
}
if p.HostPort == 0 && p.ContainerPort != 0 {
p.HostPort = p.ContainerPort
}
if p.Protocol == "" {
p.Protocol = "tcp"
}
if p.HostPort == 0 && p.ContainerPort == 0 {
return nil, errors.New("Invalid port mapping, host & container port are 0")
}
if p.HostPort == 0 {
port, err := utils.GetRandomPort()
if err != nil {
return nil, err
}
p.HostPort = int32(port)
}
portBinding := types.PortMapping{
HostPort: uint16(p.HostPort),
ContainerPort: uint16(p.ContainerPort),
Expand All @@ -976,5 +988,5 @@ func getPodPorts(containers []v1.Container) []types.PortMapping {
}
}
}
return infraPorts
return infraPorts, nil
}
42 changes: 39 additions & 3 deletions test/system/700-play.bats
Original file line number Diff line number Diff line change
Expand Up @@ -468,7 +468,7 @@ spec:
run_podman kube down $PODMAN_TMPDIR/test.yaml
}

@test "podman kube play - hostport" {
@test "podman kube play - containerport" {
HOST_PORT=$(random_free_port)
echo "
apiVersion: v1
Expand All @@ -487,10 +487,46 @@ spec:
" > $PODMAN_TMPDIR/testpod.yaml

run_podman kube play $PODMAN_TMPDIR/testpod.yaml
run_podman pod inspect test_pod --format "{{.InfraConfig.PortBindings}}"
assert "$output" = "map[$HOST_PORT/tcp:[{ $HOST_PORT}]]"
run_podman pod inspect test_pod --format "{{index .InfraConfig.PortBindings \"$HOST_PORT/tcp\" | len}}"
assert "$output" = "1"
run_podman kube down $PODMAN_TMPDIR/testpod.yaml

run_podman pod rm -a -f
run_podman rm -a -f
}

@test "podman kube play - containerport and replicas" {
echo "
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: test_pod
spec:
replicas: 3
selector:
matchLabels:
app: test
template:
metadata:
labels:
app: test
spec:
containers:
- name: server
image: $IMAGE
ports:
- name: hostp
containerPort: 8080
" > "$PODMAN_TMPDIR/testpod.yaml"

run_podman kube play "$PODMAN_TMPDIR/testpod.yaml"
for i in $(seq 0 2); do
run_podman pod inspect "test_pod-pod-$i" --format '{{ index .InfraConfig.PortBindings "8080/tcp" |len}}'
assert "$output" = "1" "Expected port bindings from 8080 to exactly one container port"
done
run_podman kube down "$PODMAN_TMPDIR/testpod.yaml"

run_podman pod rm -a -f
run_podman rm -a -f
}

0 comments on commit 5afd75b

Please sign in to comment.