Skip to content

Commit 4bbe2ee

Browse files
Merge pull request #17068 from ygalblum/quadlet-kube-publish-ports
Quadlet Kube - add support for PublishPort key
2 parents 67305ce + b10a906 commit 4bbe2ee

File tree

7 files changed

+186
-80
lines changed

7 files changed

+186
-80
lines changed

docs/source/markdown/podman-systemd.unit.5.md

+17
Original file line numberDiff line numberDiff line change
@@ -365,6 +365,23 @@ it may be absolute or relative to the location of the unit file.
365365

366366
This key may be used multiple times
367367

368+
#### `PublishPort=`
369+
370+
Exposes a port, or a range of ports (e.g. `50-59`), from the container to the host. Equivalent
371+
to the `podman kube play`'s `--publish` option. The format is similar to the Podman options, which is of
372+
the form `ip:hostPort:containerPort`, `ip::containerPort`, `hostPort:containerPort` or
373+
`containerPort`, where the number of host and container ports must be the same (in the case
374+
of a range).
375+
376+
If the IP is set to 0.0.0.0 or not set at all, the port will be bound on all IPv4 addresses on
377+
the host; use [::] for IPv6.
378+
379+
The list of published ports specified in the unit file will be merged with the list of ports specified
380+
in the Kubernetes YAML file. If the same container port and protocol is specified in both, the
381+
entry from the unit file will take precedence
382+
383+
This key can be listed multiple times.
384+
368385
### Volume units
369386

370387
Volume files are named with a `.volume` extension and contain a section `[Volume]` describing the

pkg/systemd/quadlet/quadlet.go

+71-57
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,7 @@ var (
141141
KeyRemapUIDSize: true,
142142
KeyNetwork: true,
143143
KeyConfigMap: true,
144+
KeyPublishPort: true,
144145
}
145146
)
146147

@@ -454,63 +455,8 @@ func ConvertContainer(container *parser.UnitFile, isUser bool) (*parser.UnitFile
454455
podman.addf("--expose=%s", exposedPort)
455456
}
456457

457-
publishPorts := container.LookupAll(ContainerGroup, KeyPublishPort)
458-
for _, publishPort := range publishPorts {
459-
publishPort = strings.TrimSpace(publishPort) // Allow whitespace after
460-
461-
// IP address could have colons in it. For example: "[::]:8080:80/tcp, so use custom splitter
462-
parts := splitPorts(publishPort)
463-
464-
var containerPort string
465-
ip := ""
466-
hostPort := ""
467-
468-
// format (from podman run):
469-
// ip:hostPort:containerPort | ip::containerPort | hostPort:containerPort | containerPort
470-
//
471-
// ip could be IPv6 with minimum of these chars "[::]"
472-
// containerPort can have a suffix of "/tcp" or "/udp"
473-
//
474-
475-
switch len(parts) {
476-
case 1:
477-
containerPort = parts[0]
478-
479-
case 2:
480-
hostPort = parts[0]
481-
containerPort = parts[1]
482-
483-
case 3:
484-
ip = parts[0]
485-
hostPort = parts[1]
486-
containerPort = parts[2]
487-
488-
default:
489-
return nil, fmt.Errorf("invalid published port '%s'", publishPort)
490-
}
491-
492-
if ip == "0.0.0.0" {
493-
ip = ""
494-
}
495-
496-
if len(hostPort) > 0 && !isPortRange(hostPort) {
497-
return nil, fmt.Errorf("invalid port format '%s'", hostPort)
498-
}
499-
500-
if len(containerPort) > 0 && !isPortRange(containerPort) {
501-
return nil, fmt.Errorf("invalid port format '%s'", containerPort)
502-
}
503-
504-
switch {
505-
case len(ip) > 0 && len(hostPort) > 0:
506-
podman.addf("-p=%s:%s:%s", ip, hostPort, containerPort)
507-
case len(ip) > 0:
508-
podman.addf("-p=%s::%s", ip, containerPort)
509-
case len(hostPort) > 0:
510-
podman.addf("-p=%s:%s", hostPort, containerPort)
511-
default:
512-
podman.addf("-p=%s", containerPort)
513-
}
458+
if err := handlePublishPorts(container, ContainerGroup, podman); err != nil {
459+
return nil, err
514460
}
515461

516462
podman.addEnv(podmanEnv)
@@ -775,6 +721,10 @@ func ConvertKube(kube *parser.UnitFile, isUser bool) (*parser.UnitFile, error) {
775721
execStart.add("--configmap", configMapPath)
776722
}
777723

724+
if err := handlePublishPorts(kube, KubeGroup, execStart); err != nil {
725+
return nil, err
726+
}
727+
778728
execStart.add(yamlPath)
779729

780730
service.AddCmdline(ServiceGroup, "ExecStart", execStart.Args)
@@ -876,3 +826,67 @@ func getAbsolutePath(quadletUnitFile *parser.UnitFile, filePath string) (string,
876826
}
877827
return filePath, nil
878828
}
829+
830+
func handlePublishPorts(unitFile *parser.UnitFile, groupName string, podman *PodmanCmdline) error {
831+
publishPorts := unitFile.LookupAll(groupName, KeyPublishPort)
832+
for _, publishPort := range publishPorts {
833+
publishPort = strings.TrimSpace(publishPort) // Allow whitespace after
834+
835+
// IP address could have colons in it. For example: "[::]:8080:80/tcp, so use custom splitter
836+
parts := splitPorts(publishPort)
837+
838+
var containerPort string
839+
ip := ""
840+
hostPort := ""
841+
842+
// format (from podman run):
843+
// ip:hostPort:containerPort | ip::containerPort | hostPort:containerPort | containerPort
844+
//
845+
// ip could be IPv6 with minimum of these chars "[::]"
846+
// containerPort can have a suffix of "/tcp" or "/udp"
847+
//
848+
849+
switch len(parts) {
850+
case 1:
851+
containerPort = parts[0]
852+
853+
case 2:
854+
hostPort = parts[0]
855+
containerPort = parts[1]
856+
857+
case 3:
858+
ip = parts[0]
859+
hostPort = parts[1]
860+
containerPort = parts[2]
861+
862+
default:
863+
return fmt.Errorf("invalid published port '%s'", publishPort)
864+
}
865+
866+
if ip == "0.0.0.0" {
867+
ip = ""
868+
}
869+
870+
if len(hostPort) > 0 && !isPortRange(hostPort) {
871+
return fmt.Errorf("invalid port format '%s'", hostPort)
872+
}
873+
874+
if len(containerPort) > 0 && !isPortRange(containerPort) {
875+
return fmt.Errorf("invalid port format '%s'", containerPort)
876+
}
877+
878+
podman.add("--publish")
879+
switch {
880+
case len(ip) > 0 && len(hostPort) > 0:
881+
podman.addf("%s:%s:%s", ip, hostPort, containerPort)
882+
case len(ip) > 0:
883+
podman.addf("%s::%s", ip, containerPort)
884+
case len(hostPort) > 0:
885+
podman.addf("%s:%s", hostPort, containerPort)
886+
default:
887+
podman.addf("%s", containerPort)
888+
}
889+
}
890+
891+
return nil
892+
}

test/e2e/quadlet/ports.container

+14-14
Original file line numberDiff line numberDiff line change
@@ -5,46 +5,46 @@ ExposeHostPort=1000
55
## assert-podman-args --expose=2000-3000
66
ExposeHostPort=2000-3000
77

8-
## assert-podman-args -p=127.0.0.1:80:90
8+
## assert-podman-args --publish 127.0.0.1:80:90
99
PublishPort=127.0.0.1:80:90
1010

11-
## assert-podman-args -p=80:91
11+
## assert-podman-args --publish 80:91
1212
PublishPort=0.0.0.0:80:91
1313

14-
## assert-podman-args -p=80:92
14+
## assert-podman-args --publish 80:92
1515
PublishPort=:80:92
1616

17-
## assert-podman-args -p=127.0.0.1::93
17+
## assert-podman-args --publish 127.0.0.1::93
1818
PublishPort=127.0.0.1::93
1919

20-
## assert-podman-args -p=94
20+
## assert-podman-args --publish 94
2121
PublishPort=0.0.0.0::94
2222

23-
## assert-podman-args -p=95
23+
## assert-podman-args --publish 95
2424
PublishPort=::95
2525

26-
## assert-podman-args -p=80:96
26+
## assert-podman-args --publish 80:96
2727
PublishPort=80:96
2828

29-
## assert-podman-args -p=97
29+
## assert-podman-args --publish 97
3030
PublishPort=97
3131

32-
## assert-podman-args -p=1234/udp
32+
## assert-podman-args --publish 1234/udp
3333
PublishPort=1234/udp
3434

35-
## assert-podman-args -p=1234:1234/udp
35+
## assert-podman-args --publish 1234:1234/udp
3636
PublishPort=1234:1234/udp
3737

38-
## assert-podman-args -p=127.0.0.1:1234:1234/udp
38+
## assert-podman-args --publish 127.0.0.1:1234:1234/udp
3939
PublishPort=127.0.0.1:1234:1234/udp
4040

41-
## assert-podman-args -p=1234/tcp
41+
## assert-podman-args --publish 1234/tcp
4242
PublishPort=1234/tcp
4343

44-
## assert-podman-args -p=1234:1234/tcp
44+
## assert-podman-args --publish 1234:1234/tcp
4545
PublishPort=1234:1234/tcp
4646

47-
## assert-podman-args -p=127.0.0.1:1234:1234/tcp
47+
## assert-podman-args --publish 127.0.0.1:1234:1234/tcp
4848
PublishPort=127.0.0.1:1234:1234/tcp
4949

5050
## assert-podman-args --expose=2000-3000/udp

test/e2e/quadlet/ports.kube

+44
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
[Kube]
2+
Yaml=/opt/k8s/deployment.yml
3+
4+
## assert-podman-args --publish 127.0.0.1:80:90
5+
PublishPort=127.0.0.1:80:90
6+
7+
## assert-podman-args --publish 80:91
8+
PublishPort=0.0.0.0:80:91
9+
10+
## assert-podman-args --publish 80:92
11+
PublishPort=:80:92
12+
13+
## assert-podman-args --publish 127.0.0.1::93
14+
PublishPort=127.0.0.1::93
15+
16+
## assert-podman-args --publish 94
17+
PublishPort=0.0.0.0::94
18+
19+
## assert-podman-args --publish 95
20+
PublishPort=::95
21+
22+
## assert-podman-args --publish 80:96
23+
PublishPort=80:96
24+
25+
## assert-podman-args --publish 97
26+
PublishPort=97
27+
28+
## assert-podman-args --publish 1234/udp
29+
PublishPort=1234/udp
30+
31+
## assert-podman-args --publish 1234:1234/udp
32+
PublishPort=1234:1234/udp
33+
34+
## assert-podman-args --publish 127.0.0.1:1234:1234/udp
35+
PublishPort=127.0.0.1:1234:1234/udp
36+
37+
## assert-podman-args --publish 1234/tcp
38+
PublishPort=1234/tcp
39+
40+
## assert-podman-args --publish 1234:1234/tcp
41+
PublishPort=1234:1234/tcp
42+
43+
## assert-podman-args --publish 127.0.0.1:1234:1234/tcp
44+
PublishPort=127.0.0.1:1234:1234/tcp

test/e2e/quadlet/ports_ipv6.container

+9-9
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,28 @@
11
[Container]
22
Image=localhost/imagename
3-
## assert-podman-args -p=[::1]:80:90
3+
## assert-podman-args --publish [::1]:80:90
44
PublishPort=[::1]:80:90
55

6-
## assert-podman-args -p=[::]:80:91
6+
## assert-podman-args --publish [::]:80:91
77
PublishPort=[::]:80:91
88

9-
## assert-podman-args -p=[2001:DB8::23]:80:91
9+
## assert-podman-args --publish [2001:DB8::23]:80:91
1010
PublishPort=[2001:DB8::23]:80:91
1111

12-
## assert-podman-args -p=[::1]::93
12+
## assert-podman-args --publish [::1]::93
1313
PublishPort=[::1]::93
1414

15-
## assert-podman-args -p=[::]::94
15+
## assert-podman-args --publish [::]::94
1616
PublishPort=[::]::94
1717

18-
## assert-podman-args -p=[2001:db8::42]::94
18+
## assert-podman-args --publish [2001:db8::42]::94
1919
PublishPort=[2001:db8::42]::94
2020

21-
## assert-podman-args -p=[::1]:1234:1234/udp
21+
## assert-podman-args --publish [::1]:1234:1234/udp
2222
PublishPort=[::1]:1234:1234/udp
2323

24-
## assert-podman-args -p=[::1]:1234:1234/tcp
24+
## assert-podman-args --publish [::1]:1234:1234/tcp
2525
PublishPort=[::1]:1234:1234/tcp
2626

27-
## assert-podman-args -p=[2001:db8:c0:ff:ee::1]:1234:1234/udp
27+
## assert-podman-args --publish [2001:db8:c0:ff:ee::1]:1234:1234/udp
2828
PublishPort=[2001:db8:c0:ff:ee::1]:1234:1234/udp

test/e2e/quadlet/ports_ipv6.kube

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
[Kube]
2+
Yaml=/opt/k8s/deployment.yml
3+
4+
## assert-podman-args --publish [::1]:80:90
5+
PublishPort=[::1]:80:90
6+
7+
## assert-podman-args --publish [::]:80:91
8+
PublishPort=[::]:80:91
9+
10+
## assert-podman-args --publish [2001:DB8::23]:80:91
11+
PublishPort=[2001:DB8::23]:80:91
12+
13+
## assert-podman-args --publish [::1]::93
14+
PublishPort=[::1]::93
15+
16+
## assert-podman-args --publish [::]::94
17+
PublishPort=[::]::94
18+
19+
## assert-podman-args --publish [2001:db8::42]::94
20+
PublishPort=[2001:db8::42]::94
21+
22+
## assert-podman-args --publish [::1]:1234:1234/udp
23+
PublishPort=[::1]:1234:1234/udp
24+
25+
## assert-podman-args --publish [::1]:1234:1234/tcp
26+
PublishPort=[::1]:1234:1234/tcp
27+
28+
## assert-podman-args --publish [2001:db8:c0:ff:ee::1]:1234:1234/udp
29+
PublishPort=[2001:db8:c0:ff:ee::1]:1234:1234/udp

test/e2e/quadlet_test.go

+2
Original file line numberDiff line numberDiff line change
@@ -492,6 +492,8 @@ var _ = Describe("quadlet system generator", func() {
492492
Entry("Kube - Network", "network.kube"),
493493
Entry("Kube - Quadlet Network", "network.quadlet.kube"),
494494
Entry("Kube - ConfigMap", "configmap.kube"),
495+
Entry("Kube - Publish IPv4 ports", "ports.kube"),
496+
Entry("Kube - Publish IPv6 ports", "ports_ipv6.kube"),
495497

496498
Entry("Network - Basic", "basic.network"),
497499
Entry("Network - Label", "label.network"),

0 commit comments

Comments
 (0)