Fix all ports exposed by kube play

Container ports defined with containerPort were exposed by default
even though kubernetes interprets them as mostly informative.
Closes #17028

Signed-off-by: Peter Werner <wpw.peter@gmail.com>
This commit is contained in:
Peter Werner
2023-08-31 21:38:33 +02:00
parent c3ab75ca45
commit f52b02f406
11 changed files with 86 additions and 7 deletions

View File

@ -158,6 +158,9 @@ func playFlags(cmd *cobra.Command) {
flags.StringSliceVar(&playOptions.PublishPorts, publishPortsFlagName, []string{}, "Publish a container's port, or a range of ports, to the host")
_ = cmd.RegisterFlagCompletionFunc(publishPortsFlagName, completion.AutocompleteNone)
publishAllPortsFlagName := "publish-all"
flags.BoolVar(&playOptions.PublishAllPorts, publishAllPortsFlagName, false, "Whether to publish all ports defined in the K8S YAML file (containerPort, hostPort), if false only hostPort will be published")
waitFlagName := "wait"
flags.BoolVarP(&playOptions.Wait, waitFlagName, "w", false, "Clean up all objects created when a SIGTERM is received or pods exit")

View File

@ -221,6 +221,16 @@ 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.
#### **--publish-all**
Setting this option to `true` will expose all ports to the host,
even if only specified via **containerPort** in the K8 YAML.
In terms of which port will be exposed, **--publish** has higher priority than **hostPort**, has higher priority than
**containerPort**.
If set to `false` (which is the default), only ports defined via **hostPort**
or **--publish** are published on the host.
#### **--quiet**, **-q**
Suppress output information when pulling images

View File

@ -53,6 +53,9 @@ type PlayOptions struct {
Force *bool
// PublishPorts - configure how to expose ports configured inside the K8S YAML file
PublishPorts []string
// PublishAllPorts - whether to publish all ports defined in the K8S YAML file
// (containerPort, hostPort) otherwise only hostPort will be published
PublishAllPorts *bool
// Wait - indicates whether to return after having created the pods
Wait *bool
ServiceContainer *bool

View File

@ -333,6 +333,21 @@ func (o *PlayOptions) GetPublishPorts() []string {
return o.PublishPorts
}
// WithPublishAllPorts set field PublishAllPorts to given value
func (o *PlayOptions) WithPublishAllPorts(value bool) *PlayOptions {
o.PublishAllPorts = &value
return o
}
// GetPublishAllPorts returns value of field PublishAllPorts
func (o *PlayOptions) GetPublishAllPorts() bool {
if o.PublishAllPorts == nil {
var z bool
return z
}
return *o.PublishAllPorts
}
// WithWait set field Wait to given value
func (o *PlayOptions) WithWait(value bool) *PlayOptions {
o.Wait = &value

View File

@ -70,6 +70,9 @@ type PlayKubeOptions struct {
Force bool
// PublishPorts - configure how to expose ports configured inside the K8S YAML file
PublishPorts []string
// PublishAllPorts - whether to publish all ports defined in the K8S YAML file
// (containerPort, hostPort) otherwise only hostPort will be published
PublishAllPorts bool
// Wait - indicates whether to return after having created the pods
Wait bool
}

View File

@ -473,7 +473,7 @@ func (ic *ContainerEngine) playKubePod(ctx context.Context, podName string, podY
Net: &entities.NetOptions{NoHosts: options.NoHosts},
ExitPolicy: string(config.PodExitPolicyStop),
}
podOpt, err = kube.ToPodOpt(ctx, podName, podOpt, podYAML)
podOpt, err = kube.ToPodOpt(ctx, podName, podOpt, options.PublishAllPorts, podYAML)
if err != nil {
return nil, nil, err
}

View File

@ -73,6 +73,7 @@ func (ic *ContainerEngine) PlayKube(ctx context.Context, body io.Reader, opts en
options.WithStart(start == types.OptionalBoolTrue)
}
options.WithPublishPorts(opts.PublishPorts)
options.WithPublishAllPorts(opts.PublishAllPorts)
options.WithNoTrunc(opts.UseLongAnnotations)
return play.KubeWithBody(ic.ClientCtx, body, options)
}

View File

@ -43,7 +43,7 @@ import (
"sigs.k8s.io/yaml"
)
func ToPodOpt(ctx context.Context, podName string, p entities.PodCreateOptions, podYAML *v1.PodTemplateSpec) (entities.PodCreateOptions, error) {
func ToPodOpt(ctx context.Context, podName string, p entities.PodCreateOptions, publishAllPorts bool, podYAML *v1.PodTemplateSpec) (entities.PodCreateOptions, error) {
p.Net = &entities.NetOptions{NoHosts: p.Net.NoHosts}
p.Name = podName
@ -82,7 +82,7 @@ func ToPodOpt(ctx context.Context, podName string, p entities.PodCreateOptions,
}
p.Net.AddHosts = hosts
}
podPorts := getPodPorts(podYAML.Spec.Containers)
podPorts := getPodPorts(podYAML.Spec.Containers, publishAllPorts)
p.Net.PublishPorts = podPorts
if dnsConfig := podYAML.Spec.DNSConfig; dnsConfig != nil {
@ -1143,14 +1143,14 @@ 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, publishAll bool) []types.PortMapping {
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 {
if p.HostPort == 0 && p.ContainerPort != 0 && publishAll {
p.HostPort = p.ContainerPort
}
if p.Protocol == "" {

View File

@ -44,6 +44,39 @@ func TestParseMountPathRO(t *testing.T) {
assert.NotContains(t, options, "ro")
}
func TestGetPodPorts(t *testing.T) {
c1 := v1.Container{
Name: "container1",
Ports: []v1.ContainerPort{{
ContainerPort: 5000,
}, {
ContainerPort: 5001,
HostPort: 5002,
}},
}
c2 := v1.Container{
Name: "container2",
Ports: []v1.ContainerPort{{
HostPort: 5004,
}},
}
r := getPodPorts([]v1.Container{c1, c2}, false)
assert.Equal(t, 2, len(r))
assert.Equal(t, uint16(5001), r[0].ContainerPort)
assert.Equal(t, uint16(5002), r[0].HostPort)
assert.Equal(t, uint16(5004), r[1].ContainerPort)
assert.Equal(t, uint16(5004), r[1].HostPort)
r = getPodPorts([]v1.Container{c1, c2}, true)
assert.Equal(t, 3, len(r))
assert.Equal(t, uint16(5000), r[0].ContainerPort)
assert.Equal(t, uint16(5000), r[0].HostPort)
assert.Equal(t, uint16(5001), r[1].ContainerPort)
assert.Equal(t, uint16(5002), r[1].HostPort)
assert.Equal(t, uint16(5004), r[2].ContainerPort)
assert.Equal(t, uint16(5004), r[2].HostPort)
}
func TestGetPortNumber(t *testing.T) {
portSpec := intstr.IntOrString{Type: intstr.Int, IntVal: 3000, StrVal: "myport"}
cp1 := v1.ContainerPort{Name: "myport", ContainerPort: 4000}

View File

@ -5425,7 +5425,7 @@ spec:
err := writeYaml(publishPortsPodWithContainerPort, kubeYaml)
Expect(err).ToNot(HaveOccurred())
kube := podmanTest.Podman([]string{"kube", "play", kubeYaml})
kube := podmanTest.Podman([]string{"kube", "play", "--publish-all=true", kubeYaml})
kube.WaitWithDefaultTimeout()
Expect(kube).Should(Exit(125))
// The error message is printed only on local call
@ -5434,6 +5434,17 @@ spec:
}
})
It("podman play kube should not publish containerPort by default", func() {
err := writeYaml(publishPortsPodWithContainerPort, kubeYaml)
Expect(err).ToNot(HaveOccurred())
kube := podmanTest.Podman([]string{"kube", "play", kubeYaml})
kube.WaitWithDefaultTimeout()
Expect(kube).Should(Exit(0))
testHTTPServer("80", true, "connection refused")
})
It("with privileged containers ports and publish in command line - curl should succeed", func() {
err := writeYaml(publishPortsPodWithContainerPort, kubeYaml)
Expect(err).ToNot(HaveOccurred())

View File

@ -532,7 +532,7 @@ EOF
image: $IMAGE
ports:
- name: hostp
containerPort: $HOST_PORT
hostPort: $HOST_PORT
EOF
run_podman kube play $PODMAN_TMPDIR/test.yaml