Skip to content

Commit

Permalink
Merge pull request #17068 from ygalblum/quadlet-kube-publish-ports
Browse files Browse the repository at this point in the history
Quadlet Kube - add support for PublishPort key
  • Loading branch information
openshift-merge-robot authored Jan 12, 2023
2 parents 67305ce + b10a906 commit 4bbe2ee
Show file tree
Hide file tree
Showing 7 changed files with 186 additions and 80 deletions.
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

0 comments on commit 4bbe2ee

Please sign in to comment.