Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Quadlet Kube - add support for PublishPort key #17068

Merged
Merged
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
17 changes: 17 additions & 0 deletions docs/source/markdown/podman-systemd.unit.5.md
Original file line number Diff line number Diff line change
Expand Up @@ -365,6 +365,23 @@ it may be absolute or relative to the location of the unit file.

This key may be used multiple times

#### `PublishPort=`

Exposes a port, or a range of ports (e.g. `50-59`), from the container to the host. Equivalent
to the `podman kube play`'s `--publish` option. The format is similar to the Podman options, which is of
the form `ip:hostPort:containerPort`, `ip::containerPort`, `hostPort:containerPort` or
`containerPort`, where the number of host and container ports must be the same (in the case
of a range).

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
the host; use [::] for IPv6.

The list of published ports specified in the unit file will be merged with the list of ports specified
in the Kubernetes YAML file. If the same container port and protocol is specified in both, the
entry from the unit file will take precedence

This key can be listed multiple times.

### Volume units

Volume files are named with a `.volume` extension and contain a section `[Volume]` describing the
Expand Down
128 changes: 71 additions & 57 deletions pkg/systemd/quadlet/quadlet.go
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@ var (
KeyRemapUIDSize: true,
KeyNetwork: true,
KeyConfigMap: true,
KeyPublishPort: true,
}
)

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

publishPorts := container.LookupAll(ContainerGroup, KeyPublishPort)
for _, publishPort := range publishPorts {
publishPort = strings.TrimSpace(publishPort) // Allow whitespace after

// IP address could have colons in it. For example: "[::]:8080:80/tcp, so use custom splitter
parts := splitPorts(publishPort)

var containerPort string
ip := ""
hostPort := ""

// format (from podman run):
// ip:hostPort:containerPort | ip::containerPort | hostPort:containerPort | containerPort
//
// ip could be IPv6 with minimum of these chars "[::]"
// containerPort can have a suffix of "/tcp" or "/udp"
//

switch len(parts) {
case 1:
containerPort = parts[0]

case 2:
hostPort = parts[0]
containerPort = parts[1]

case 3:
ip = parts[0]
hostPort = parts[1]
containerPort = parts[2]

default:
return nil, fmt.Errorf("invalid published port '%s'", publishPort)
}

if ip == "0.0.0.0" {
ip = ""
}

if len(hostPort) > 0 && !isPortRange(hostPort) {
return nil, fmt.Errorf("invalid port format '%s'", hostPort)
}

if len(containerPort) > 0 && !isPortRange(containerPort) {
return nil, fmt.Errorf("invalid port format '%s'", containerPort)
}

switch {
case len(ip) > 0 && len(hostPort) > 0:
podman.addf("-p=%s:%s:%s", ip, hostPort, containerPort)
case len(ip) > 0:
podman.addf("-p=%s::%s", ip, containerPort)
case len(hostPort) > 0:
podman.addf("-p=%s:%s", hostPort, containerPort)
default:
podman.addf("-p=%s", containerPort)
}
if err := handlePublishPorts(container, ContainerGroup, podman); err != nil {
return nil, err
}

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

if err := handlePublishPorts(kube, KubeGroup, execStart); err != nil {
return nil, err
}

execStart.add(yamlPath)

service.AddCmdline(ServiceGroup, "ExecStart", execStart.Args)
Expand Down Expand Up @@ -876,3 +826,67 @@ func getAbsolutePath(quadletUnitFile *parser.UnitFile, filePath string) (string,
}
return filePath, nil
}

func handlePublishPorts(unitFile *parser.UnitFile, groupName string, podman *PodmanCmdline) error {
publishPorts := unitFile.LookupAll(groupName, KeyPublishPort)
for _, publishPort := range publishPorts {
publishPort = strings.TrimSpace(publishPort) // Allow whitespace after

// IP address could have colons in it. For example: "[::]:8080:80/tcp, so use custom splitter
parts := splitPorts(publishPort)

var containerPort string
ip := ""
hostPort := ""

// format (from podman run):
// ip:hostPort:containerPort | ip::containerPort | hostPort:containerPort | containerPort
//
// ip could be IPv6 with minimum of these chars "[::]"
// containerPort can have a suffix of "/tcp" or "/udp"
//

switch len(parts) {
case 1:
containerPort = parts[0]

case 2:
hostPort = parts[0]
containerPort = parts[1]

case 3:
ip = parts[0]
hostPort = parts[1]
containerPort = parts[2]

default:
return fmt.Errorf("invalid published port '%s'", publishPort)
}

if ip == "0.0.0.0" {
ip = ""
}

if len(hostPort) > 0 && !isPortRange(hostPort) {
return fmt.Errorf("invalid port format '%s'", hostPort)
}

if len(containerPort) > 0 && !isPortRange(containerPort) {
return fmt.Errorf("invalid port format '%s'", containerPort)
}

podman.add("--publish")
switch {
case len(ip) > 0 && len(hostPort) > 0:
podman.addf("%s:%s:%s", ip, hostPort, containerPort)
case len(ip) > 0:
podman.addf("%s::%s", ip, containerPort)
case len(hostPort) > 0:
podman.addf("%s:%s", hostPort, containerPort)
default:
podman.addf("%s", containerPort)
}
}

return nil
}
28 changes: 14 additions & 14 deletions test/e2e/quadlet/ports.container
Original file line number Diff line number Diff line change
Expand Up @@ -5,46 +5,46 @@ ExposeHostPort=1000
## assert-podman-args --expose=2000-3000
ExposeHostPort=2000-3000

## assert-podman-args -p=127.0.0.1:80:90
## assert-podman-args --publish 127.0.0.1:80:90
PublishPort=127.0.0.1:80:90

## assert-podman-args -p=80:91
## assert-podman-args --publish 80:91
PublishPort=0.0.0.0:80:91

## assert-podman-args -p=80:92
## assert-podman-args --publish 80:92
PublishPort=:80:92

## assert-podman-args -p=127.0.0.1::93
## assert-podman-args --publish 127.0.0.1::93
PublishPort=127.0.0.1::93

## assert-podman-args -p=94
## assert-podman-args --publish 94
PublishPort=0.0.0.0::94

## assert-podman-args -p=95
## assert-podman-args --publish 95
PublishPort=::95

## assert-podman-args -p=80:96
## assert-podman-args --publish 80:96
PublishPort=80:96

## assert-podman-args -p=97
## assert-podman-args --publish 97
PublishPort=97

## assert-podman-args -p=1234/udp
## assert-podman-args --publish 1234/udp
PublishPort=1234/udp

## assert-podman-args -p=1234:1234/udp
## assert-podman-args --publish 1234:1234/udp
PublishPort=1234:1234/udp

## assert-podman-args -p=127.0.0.1:1234:1234/udp
## assert-podman-args --publish 127.0.0.1:1234:1234/udp
PublishPort=127.0.0.1:1234:1234/udp

## assert-podman-args -p=1234/tcp
## assert-podman-args --publish 1234/tcp
PublishPort=1234/tcp

## assert-podman-args -p=1234:1234/tcp
## assert-podman-args --publish 1234:1234/tcp
PublishPort=1234:1234/tcp

## assert-podman-args -p=127.0.0.1:1234:1234/tcp
## assert-podman-args --publish 127.0.0.1:1234:1234/tcp
PublishPort=127.0.0.1:1234:1234/tcp

## assert-podman-args --expose=2000-3000/udp
Expand Down
44 changes: 44 additions & 0 deletions test/e2e/quadlet/ports.kube
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
[Kube]
Yaml=/opt/k8s/deployment.yml

## assert-podman-args --publish 127.0.0.1:80:90
PublishPort=127.0.0.1:80:90

## assert-podman-args --publish 80:91
PublishPort=0.0.0.0:80:91

## assert-podman-args --publish 80:92
PublishPort=:80:92

## assert-podman-args --publish 127.0.0.1::93
PublishPort=127.0.0.1::93

## assert-podman-args --publish 94
PublishPort=0.0.0.0::94

## assert-podman-args --publish 95
PublishPort=::95

## assert-podman-args --publish 80:96
PublishPort=80:96

## assert-podman-args --publish 97
PublishPort=97

## assert-podman-args --publish 1234/udp
PublishPort=1234/udp

## assert-podman-args --publish 1234:1234/udp
PublishPort=1234:1234/udp

## assert-podman-args --publish 127.0.0.1:1234:1234/udp
PublishPort=127.0.0.1:1234:1234/udp

## assert-podman-args --publish 1234/tcp
PublishPort=1234/tcp

## assert-podman-args --publish 1234:1234/tcp
PublishPort=1234:1234/tcp

## assert-podman-args --publish 127.0.0.1:1234:1234/tcp
PublishPort=127.0.0.1:1234:1234/tcp
18 changes: 9 additions & 9 deletions test/e2e/quadlet/ports_ipv6.container
Original file line number Diff line number Diff line change
@@ -1,28 +1,28 @@
[Container]
Image=localhost/imagename
## assert-podman-args -p=[::1]:80:90
## assert-podman-args --publish [::1]:80:90
PublishPort=[::1]:80:90

## assert-podman-args -p=[::]:80:91
## assert-podman-args --publish [::]:80:91
PublishPort=[::]:80:91

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

## assert-podman-args -p=[::1]::93
## assert-podman-args --publish [::1]::93
PublishPort=[::1]::93

## assert-podman-args -p=[::]::94
## assert-podman-args --publish [::]::94
PublishPort=[::]::94

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

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

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

## assert-podman-args -p=[2001:db8:c0:ff:ee::1]:1234:1234/udp
## assert-podman-args --publish [2001:db8:c0:ff:ee::1]:1234:1234/udp
PublishPort=[2001:db8:c0:ff:ee::1]:1234:1234/udp
29 changes: 29 additions & 0 deletions test/e2e/quadlet/ports_ipv6.kube
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
[Kube]
Yaml=/opt/k8s/deployment.yml

## assert-podman-args --publish [::1]:80:90
PublishPort=[::1]:80:90

## assert-podman-args --publish [::]:80:91
PublishPort=[::]:80:91

## assert-podman-args --publish [2001:DB8::23]:80:91
PublishPort=[2001:DB8::23]:80:91

## assert-podman-args --publish [::1]::93
PublishPort=[::1]::93

## assert-podman-args --publish [::]::94
PublishPort=[::]::94

## assert-podman-args --publish [2001:db8::42]::94
PublishPort=[2001:db8::42]::94

## assert-podman-args --publish [::1]:1234:1234/udp
PublishPort=[::1]:1234:1234/udp

## assert-podman-args --publish [::1]:1234:1234/tcp
PublishPort=[::1]:1234:1234/tcp

## assert-podman-args --publish [2001:db8:c0:ff:ee::1]:1234:1234/udp
PublishPort=[2001:db8:c0:ff:ee::1]:1234:1234/udp
2 changes: 2 additions & 0 deletions test/e2e/quadlet_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -492,6 +492,8 @@ var _ = Describe("quadlet system generator", func() {
Entry("Kube - Network", "network.kube"),
Entry("Kube - Quadlet Network", "network.quadlet.kube"),
Entry("Kube - ConfigMap", "configmap.kube"),
Entry("Kube - Publish IPv4 ports", "ports.kube"),
Entry("Kube - Publish IPv6 ports", "ports_ipv6.kube"),

Entry("Network - Basic", "basic.network"),
Entry("Network - Label", "label.network"),
Expand Down