diff --git a/docs/source/markdown/podman-systemd.unit.5.md b/docs/source/markdown/podman-systemd.unit.5.md index ddbff23ca7..a032696425 100644 --- a/docs/source/markdown/podman-systemd.unit.5.md +++ b/docs/source/markdown/podman-systemd.unit.5.md @@ -121,67 +121,69 @@ There is only one required key, `Image`, which defines the container image the s Valid options for `[Container]` are listed below: -| **[Container] options** | **podman run equivalent** | -|--------------------------------|------------------------------------------------------| -| AddCapability=CAP | --cap-add CAP | -| AddDevice=/dev/foo | --device /dev/foo | -| Annotation="XYZ" | --annotation "XYZ" | -| AutoUpdate=registry | --label "io.containers.autoupdate=registry" | -| ContainerName=name | --name name | -| DNS=192.168.55.1 | --dns=192.168.55.1 | -| DNSSearch=foo.com | --dns-search=foo.com | -| DNSOption=ndots:1 | --dns-option=ndots:1 | -| DropCapability=CAP | --cap-drop=CAP | -| Environment=foo=bar | --env foo=bar | -| EnvironmentFile=/tmp/env | --env-file /tmp/env | -| EnvironmentHost=true | --env-host | -| Exec=/usr/bin/command | Command after image specification - /usr/bin/command | -| ExposeHostPort=50-59 | --expose 50-59 | -| Group=1234 | --user UID:1234 | -| HealthCmd="/usr/bin/command" | --health-cmd="/usr/bin/command" | -| HealthInterval=2m | --health-interval=2m | -| HealthOnFailure=kill | --health-on-failure=kill | -| HealthRetries=5 | --health-retries=5 | -| HealthStartPeriod=1m | --health-start-period=period=1m | -| HealthStartupCmd="command" | --health-startup-cmd="command" | -| HealthStartupInterval=1m | --health-startup-interval=1m | -| HealthStartupRetries=8 | --health-startup-retries=8 | -| HealthStartupSuccess=2 | --health-startup-success=2 | -| HealthStartupTimeout=1m33s | --health-startup-timeout=1m33s | -| HealthTimeout=20s | --health-timeout=20s | -| HostName=new-host-name | --hostname="new-host-name" | -| Image=ubi8 | Image specification - ubi8 | -| IP=192.5.0.1 | --ip 192.5.0.1 | -| IP6=2001:db8::1 | --ip6 2001:db8::1 | -| Label="XYZ" | --label "XYZ" | -| LogDriver=journald | --log-driver journald | -| Mount=type=... | --mount type=... | -| Network=host | --net host | -| NoNewPrivileges=true | --security-opt no-new-privileges | -| Rootfs=/var/lib/rootfs | --rootfs /var/lib/rootfs | -| Notify=true | --sdnotify container | -| PidsLimit=10000 | --pids-limit 10000 | -| PodmanArgs=--add-host foobar | --add-host foobar | -| PublishPort=50-59 | --publish 50-59 | -| Pull=never | --pull=never | -| ReadOnly=true | --read-only | -| RunInit=true | --init | -| SeccompProfile=/tmp/s.json | --security-opt seccomp=/tmp/s.json | -| SecurityLabelDisable=true | --security-opt label=disable | -| SecurityLabelFileType=usr_t | --security-opt label=filetype:usr_t | -| SecurityLabelLevel=s0:c1,c2 | --security-opt label=level:s0:c1,c2 | -| SecurityLabelNested=true | --security-opt label=nested | -| SecurityLabelType=spc_t | --security-opt label=type:spc_t | -| ShmSize=100m | --shm-size=100m | -| Sysctl=name=value | --sysctl=name=value | -| Timezone=local | --tz local | -| Tmpfs=/work | --tmpfs /work | -| Ulimit=nofile:1000:10000 | --ulimit nofile:1000:10000 | -| User=bin | --user bin | -| UserNS=keep-id:uid=200,gid=210 | --userns keep-id:uid=200,gid=210 | -| VolatileTmp=true | --tmpfs /tmp | -| Volume=/source:/dest | --volume /source:/dest | -| WorkingDir=$HOME | --workdir $HOME | +| **[Container] options** | **podman run equivalent** | +|--------------------------------------|------------------------------------------------------| +| AddCapability=CAP | --cap-add CAP | +| AddDevice=/dev/foo | --device /dev/foo | +| Annotation="XYZ" | --annotation "XYZ" | +| AutoUpdate=registry | --label "io.containers.autoupdate=registry" | +| ContainerName=name | --name name | +| ContainersConfModule=/etc/nvd\.conf | --module=/etc/nvd\.conf | +| DNS=192.168.55.1 | --dns=192.168.55.1 | +| DNSSearch=foo.com | --dns-search=foo.com | +| DNSOption=ndots:1 | --dns-option=ndots:1 | +| DropCapability=CAP | --cap-drop=CAP | +| Environment=foo=bar | --env foo=bar | +| EnvironmentFile=/tmp/env | --env-file /tmp/env | +| EnvironmentHost=true | --env-host | +| Exec=/usr/bin/command | Command after image specification - /usr/bin/command | +| ExposeHostPort=50-59 | --expose 50-59 | +| Group=1234 | --user UID:1234 | +| GlobalArgs=--log-level=debug | --log-level=debug | +| HealthCmd="/usr/bin/command" | --health-cmd="/usr/bin/command" | +| HealthInterval=2m | --health-interval=2m | +| HealthOnFailure=kill | --health-on-failure=kill | +| HealthRetries=5 | --health-retries=5 | +| HealthStartPeriod=1m | --health-start-period=period=1m | +| HealthStartupCmd="command" | --health-startup-cmd="command" | +| HealthStartupInterval=1m | --health-startup-interval=1m | +| HealthStartupRetries=8 | --health-startup-retries=8 | +| HealthStartupSuccess=2 | --health-startup-success=2 | +| HealthStartupTimeout=1m33s | --health-startup-timeout=1m33s | +| HealthTimeout=20s | --health-timeout=20s | +| HostName=new-host-name | --hostname="new-host-name" | +| Image=ubi8 | Image specification - ubi8 | +| IP=192.5.0.1 | --ip 192.5.0.1 | +| IP6=2001:db8::1 | --ip6 2001:db8::1 | +| Label="XYZ" | --label "XYZ" | +| LogDriver=journald | --log-driver journald | +| Mount=type=... | --mount type=... | +| Network=host | --net host | +| NoNewPrivileges=true | --security-opt no-new-privileges | +| Rootfs=/var/lib/rootfs | --rootfs /var/lib/rootfs | +| Notify=true | --sdnotify container | +| PidsLimit=10000 | --pids-limit 10000 | +| PodmanArgs=--add-host foobar | --add-host foobar | +| PublishPort=50-59 | --publish 50-59 | +| Pull=never | --pull=never | +| ReadOnly=true | --read-only | +| RunInit=true | --init | +| SeccompProfile=/tmp/s.json | --security-opt seccomp=/tmp/s.json | +| SecurityLabelDisable=true | --security-opt label=disable | +| SecurityLabelFileType=usr_t | --security-opt label=filetype:usr_t | +| SecurityLabelLevel=s0:c1,c2 | --security-opt label=level:s0:c1,c2 | +| SecurityLabelNested=true | --security-opt label=nested | +| SecurityLabelType=spc_t | --security-opt label=type:spc_t | +| ShmSize=100m | --shm-size=100m | +| Sysctl=name=value | --sysctl=name=value | +| Timezone=local | --tz local | +| Tmpfs=/work | --tmpfs /work | +| Ulimit=nofile:1000:10000 | --ulimit nofile:1000:10000 | +| User=bin | --user bin | +| UserNS=keep-id:uid=200,gid=210 | --userns keep-id:uid=200,gid=210 | +| VolatileTmp=true | --tmpfs /tmp | +| Volume=/source:/dest | --volume /source:/dest | +| WorkingDir=$HOME | --workdir $HOME | Description of `[Container]` section are: @@ -228,6 +230,12 @@ The (optional) name of the Podman container. If this is not specified, the defau of `systemd-%N` is used, which is the same as the service name but with a `systemd-` prefix to avoid conflicts with user-managed containers. +### `ContainersConfModule=` + +Load the specified containers.conf(5) module. Equivalent to the Podman `--module` option. + +This key can be listed multiple times. + ### `DNS=` Set network-scoped DNS resolver/nameserver for containers in this network. @@ -286,6 +294,19 @@ to the Podman `--expose` option. This key can be listed multiple times. +### `GlobalArgs=` + +This key contains a list of arguments passed directly between `podman` and `run` +in the generated file (right before the image name in the command line). It can be used to +access Podman features otherwise unsupported by the generator. Since the generator is unaware +of what unexpected interactions can be caused by these arguments, it is not recommended to use +this option. + +The format of this is a space separated list of arguments, which can optionally be individually +escaped to allow inclusion of whitespace and other control characters. + +This key can be listed multiple times. + ### `Group=` The (numeric) GID to run as inside the container. This does not need to match the GID on the host, @@ -598,9 +619,11 @@ There is only one required key, `Yaml`, which defines the path to the Kubernetes Valid options for `[Kube]` are listed below: | **[Kube] options** | **podman kube play equivalent** | -| ----------------------------------- | -----------------------------------------------------------------| +| ------------------------------------| -----------------------------------------------------------------| | AutoUpdate=registry | --annotation "io.containers.autoupdate=registry" | | ConfigMap=/tmp/config.map | --config-map /tmp/config.map | +| ContainersConfModule=/etc/nvd\.conf | --module=/etc/nvd\.conf | +| GlobalArgs=--log-level=debug | --log-level=debug | | LogDriver=journald | --log-driver journald | | Network=host | --net host | | PodmanArgs=\-\-annotation=key=value | --annotation=key=value | @@ -629,6 +652,12 @@ it may be absolute or relative to the location of the unit file. This key may be used multiple times +### `ContainersConfModule=` + +Load the specified containers.conf(5) module. Equivalent to the Podman `--module` option. + +This key can be listed multiple times. + ### `ExitCodePropagation=` Control how the main PID of the systemd service should exit. The following values are supported: @@ -638,6 +667,20 @@ Control how the main PID of the systemd service should exit. The following value The current default value is `none`. +### `GlobalArgs=` + +This key contains a list of arguments passed directly between `podman` and `kube` +in the generated file (right before the image name in the command line). It can be used to +access Podman features otherwise unsupported by the generator. Since the generator is unaware +of what unexpected interactions can be caused by these arguments, it is not recommended to use +this option. + +The format of this is a space separated list of arguments, which can optionally be individually +escaped to allow inclusion of whitespace and other control characters. + +This key can be listed multiple times. + + ### `LogDriver=` Set the log-driver Podman uses when running the container. @@ -739,24 +782,32 @@ particularly interesting when using special options to control network creation, Valid options for `[Network]` are listed below: -| **[Network] options** | **podman network create equivalent** | -|-------------------------------|--------------------------------------| -| DisableDNS=true | --disable-dns | -| DNS=192.168.55.1 | --dns=192.168.55.1 | -| Driver=bridge | --driver bridge | -| Gateway=192.168.55.3 | --gateway 192.168.55.3 | -| Internal=true | --internal | -| IPAMDriver=dhcp | --ipam-driver dhcp | -| IPRange=192.168.55.128/25 | --ip-range 192.168.55.128/25 | -| IPv6=true | --ipv6 | -| Label="XYZ" | --label "XYZ" | -| NetworkName=foo | podman network create foo | -| Options=isolate | --opt isolate | -| PodmanArgs=--dns=192.168.55.1 | --dns=192.168.55.1 | -| Subnet=192.5.0.0/16 | --subnet 192.5.0.0/16 | +| **[Network] options** | **podman network create equivalent** | +|-------------------------------------|--------------------------------------| +| ContainersConfModule=/etc/nvd\.conf | --module=/etc/nvd\.conf | +| DisableDNS=true | --disable-dns | +| DNS=192.168.55.1 | --dns=192.168.55.1 | +| Driver=bridge | --driver bridge | +| Gateway=192.168.55.3 | --gateway 192.168.55.3 | +| GlobalArgs=--log-level=debug | --log-level=debug | +| Internal=true | --internal | +| IPAMDriver=dhcp | --ipam-driver dhcp | +| IPRange=192.168.55.128/25 | --ip-range 192.168.55.128/25 | +| IPv6=true | --ipv6 | +| Label="XYZ" | --label "XYZ" | +| NetworkName=foo | podman network create foo | +| Options=isolate | --opt isolate | +| PodmanArgs=--dns=192.168.55.1 | --dns=192.168.55.1 | +| Subnet=192.5.0.0/16 | --subnet 192.5.0.0/16 | Supported keys in `[Network]` section are: +### `ContainersConfModule=` + +Load the specified containers.conf(5) module. Equivalent to the Podman `--module` option. + +This key can be listed multiple times. + ### `DisableDNS=` (defaults to `no`) If enabled, disables the DNS plugin for this network. @@ -783,6 +834,19 @@ This is equivalent to the Podman `--gateway` option This key can be listed multiple times. +### `GlobalArgs=` + +This key contains a list of arguments passed directly between `podman` and `network` +in the generated file (right before the image name in the command line). It can be used to +access Podman features otherwise unsupported by the generator. Since the generator is unaware +of what unexpected interactions can be caused by these arguments, it is not recommended to use +this option. + +The format of this is a space separated list of arguments, which can optionally be individually +escaped to allow inclusion of whitespace and other control characters. + +This key can be listed multiple times. + ### `Internal=` (defaults to `no`) Restrict external access of this network. @@ -870,9 +934,11 @@ Valid options for `[Volume]` are listed below: | **[Volume] options** | **podman volume create equivalent** | |-------------------------------------|-------------------------------------------| +| ContainersConfModule=/etc/nvd\.conf | --module=/etc/nvd\.conf | +| Copy=true | --opt copy | | Device=tmpfs | --opt device=tmpfs | | Driver=image | --driver=image | -| Copy=true | --opt copy | +| GlobalArgs=--log-level=debug | --log-level=debug | | Group=192 | --opt group=192 | | Image=quay.io/centos/centos\:latest | --opt image=quay.io/centos/centos\:latest | | Label="foo=bar" | --label "foo=bar" | @@ -882,6 +948,12 @@ Valid options for `[Volume]` are listed below: Supported keys in `[Volume]` section are: +### `ContainersConfModule=` + +Load the specified containers.conf(5) module. Equivalent to the Podman `--module` option. + +This key can be listed multiple times. + ### `Copy=` (default to `yes`) If enabled, the content of the image located at the mountpoint of the volume is copied into the @@ -897,6 +969,19 @@ Specify the volume driver name. When set to `image`, the `Image` key must also b This is equivalent to the Podman `--driver` option. +### `GlobalArgs=` + +This key contains a list of arguments passed directly between `podman` and `volume` +in the generated file (right before the image name in the command line). It can be used to +access Podman features otherwise unsupported by the generator. Since the generator is unaware +of what unexpected interactions can be caused by these arguments, it is not recommended to use +this option. + +The format of this is a space separated list of arguments, which can optionally be individually +escaped to allow inclusion of whitespace and other control characters. + +This key can be listed multiple times. + ### `Group=` The host (numeric) GID, or group name to use as the group for the volume @@ -970,8 +1055,10 @@ Valid options for `[Image]` are listed below: | Arch=aarch64 | --arch=aarch64 | | AuthFile=/etc/registry/auth\.json | --authfile=/etc/registry/auth\.json | | CertDir=/etc/registery/certs | --cert-dir=/etc/registery/certs | +| ContainersConfModule=/etc/nvd\.conf | --module=/etc/nvd\.conf | | Creds=myname\:mypassword | --creds=myname\:mypassword | | DecryptionKey=/etc/registery\.key | --decryption-key=/etc/registery\.key | +| GlobalArgs=--log-level=debug | --log-level=debug | | Image=quay.io/centos/centos\:latest | podman image pull quay.io/centos/centos\:latest | | OS=windows | --os=windows | | PodmanArgs=--os=linux | --os=linux | @@ -1002,6 +1089,12 @@ Use certificates at path (*.crt, *.cert, *.key) to connect to the registry. This is equivalent to the Podman `--cert-dir` option. +### `ContainersConfModule=` + +Load the specified containers.conf(5) module. Equivalent to the Podman `--module` option. + +This key can be listed multiple times. + ### `Creds=` The `[username[:password]]` to use to authenticate with the registry, if required. @@ -1014,6 +1107,19 @@ The `[key[:passphrase]]` to be used for decryption of images. This is equivalent to the Podman `--decryption-key` option. +### `GlobalArgs=` + +This key contains a list of arguments passed directly between `podman` and `image` +in the generated file (right before the image name in the command line). It can be used to +access Podman features otherwise unsupported by the generator. Since the generator is unaware +of what unexpected interactions can be caused by these arguments, it is not recommended to use +this option. + +The format of this is a space separated list of arguments, which can optionally be individually +escaped to allow inclusion of whitespace and other control characters. + +This key can be listed multiple times. + ### `Image=` The image to pull. diff --git a/pkg/systemd/quadlet/quadlet.go b/pkg/systemd/quadlet/quadlet.go index 31e863baaa..73fdab4b3f 100644 --- a/pkg/systemd/quadlet/quadlet.go +++ b/pkg/systemd/quadlet/quadlet.go @@ -59,6 +59,7 @@ const ( KeyDecryptionKey = "DecryptionKey" KeyConfigMap = "ConfigMap" KeyContainerName = "ContainerName" + KeyContainersConfModule = "ContainersConfModule" KeyCopy = "Copy" KeyDevice = "Device" KeyDNS = "DNS" @@ -72,6 +73,7 @@ const ( KeyExec = "Exec" KeyExitCodePropagation = "ExitCodePropagation" KeyExposeHostPort = "ExposeHostPort" + KeyGlobalArgs = "GlobalArgs" KeyGroup = "Group" KeyHealthCmd = "HealthCmd" KeyHealthInterval = "HealthInterval" @@ -154,6 +156,7 @@ var ( KeyAnnotation: true, KeyAutoUpdate: true, KeyContainerName: true, + KeyContainersConfModule: true, KeyDNS: true, KeyDNSOption: true, KeyDNSSearch: true, @@ -163,6 +166,7 @@ var ( KeyEnvironmentHost: true, KeyExec: true, KeyExposeHostPort: true, + KeyGlobalArgs: true, KeyGroup: true, KeyHealthCmd: true, KeyHealthInterval: true, @@ -219,67 +223,75 @@ var ( // Supported keys in "Volume" group supportedVolumeKeys = map[string]bool{ - KeyCopy: true, - KeyDevice: true, - KeyDriver: true, - KeyGroup: true, - KeyImage: true, - KeyLabel: true, - KeyOptions: true, - KeyPodmanArgs: true, - KeyType: true, - KeyUser: true, - KeyVolumeName: true, + KeyContainersConfModule: true, + KeyCopy: true, + KeyDevice: true, + KeyDriver: true, + KeyGlobalArgs: true, + KeyGroup: true, + KeyImage: true, + KeyLabel: true, + KeyOptions: true, + KeyPodmanArgs: true, + KeyType: true, + KeyUser: true, + KeyVolumeName: true, } // Supported keys in "Network" group supportedNetworkKeys = map[string]bool{ - KeyLabel: true, - KeyDNS: true, - KeyNetworkDisableDNS: true, - KeyNetworkDriver: true, - KeyNetworkGateway: true, - KeyNetworkIPAMDriver: true, - KeyNetworkIPRange: true, - KeyNetworkIPv6: true, - KeyNetworkInternal: true, - KeyNetworkName: true, - KeyNetworkOptions: true, - KeyNetworkSubnet: true, - KeyPodmanArgs: true, + KeyLabel: true, + KeyDNS: true, + KeyContainersConfModule: true, + KeyGlobalArgs: true, + KeyNetworkDisableDNS: true, + KeyNetworkDriver: true, + KeyNetworkGateway: true, + KeyNetworkIPAMDriver: true, + KeyNetworkIPRange: true, + KeyNetworkIPv6: true, + KeyNetworkInternal: true, + KeyNetworkName: true, + KeyNetworkOptions: true, + KeyNetworkSubnet: true, + KeyPodmanArgs: true, } // Supported keys in "Kube" group supportedKubeKeys = map[string]bool{ - KeyAutoUpdate: true, - KeyConfigMap: true, - KeyExitCodePropagation: true, - KeyLogDriver: true, - KeyNetwork: true, - KeyPodmanArgs: true, - KeyPublishPort: true, - KeyRemapGID: true, - KeyRemapUID: true, - KeyRemapUIDSize: true, - KeyRemapUsers: true, - KeySetWorkingDirectory: true, - KeyUserNS: true, - KeyYaml: true, + KeyAutoUpdate: true, + KeyConfigMap: true, + KeyContainersConfModule: true, + KeyExitCodePropagation: true, + KeyGlobalArgs: true, + KeyLogDriver: true, + KeyNetwork: true, + KeyPodmanArgs: true, + KeyPublishPort: true, + KeyRemapGID: true, + KeyRemapUID: true, + KeyRemapUIDSize: true, + KeyRemapUsers: true, + KeySetWorkingDirectory: true, + KeyUserNS: true, + KeyYaml: true, } // Supported keys in "Image" group supportedImageKeys = map[string]bool{ - KeyAllTags: true, - KeyArch: true, - KeyAuthFile: true, - KeyCertDir: true, - KeyCreds: true, - KeyDecryptionKey: true, - KeyImage: true, - KeyOS: true, - KeyPodmanArgs: true, - KeyTLSVerify: true, - KeyVariant: true, + KeyAllTags: true, + KeyArch: true, + KeyAuthFile: true, + KeyCertDir: true, + KeyContainersConfModule: true, + KeyCreds: true, + KeyDecryptionKey: true, + KeyGlobalArgs: true, + KeyImage: true, + KeyOS: true, + KeyPodmanArgs: true, + KeyTLSVerify: true, + KeyVariant: true, } ) @@ -416,14 +428,19 @@ func ConvertContainer(container *parser.UnitFile, names map[string]string, isUse // If conmon exited uncleanly it may not have removed the container, so // force it, -i makes it ignore non-existing files. - service.Add(ServiceGroup, "ExecStop", podmanBinary()+" rm -v -f -i --cidfile=%t/%N.cid") + serviceStopCmd := createBasePodmanCommand(container, ContainerGroup) + serviceStopCmd.add("rm", "-v", "-f", "-i", "--cidfile=%t/%N.cid") + service.AddCmdline(ServiceGroup, "ExecStop", serviceStopCmd.Args) // The ExecStopPost is needed when the main PID (i.e., conmon) gets killed. // In that case, ExecStop is not executed but *Post only. If both are // fired in sequence, *Post will exit when detecting that the --cidfile // has already been removed by the previous `rm`.. - service.Add(ServiceGroup, "ExecStopPost", "-"+podmanBinary()+" rm -v -f -i --cidfile=%t/%N.cid") + serviceStopCmd.Args[0] = fmt.Sprintf("-%s", serviceStopCmd.Args[0]) + service.AddCmdline(ServiceGroup, "ExecStopPost", serviceStopCmd.Args) - podman := NewPodmanCmdline("run") + podman := createBasePodmanCommand(container, ContainerGroup) + + podman.add("run") podman.addf("--name=%s", containerName) @@ -794,7 +811,9 @@ func ConvertNetwork(network *parser.UnitFile, name string) (*parser.UnitFile, st // Need the containers filesystem mounted to start podman service.Add(UnitGroup, "RequiresMountsFor", "%t/containers") - podman := NewPodmanCmdline("network", "create", "--ignore") + podman := createBasePodmanCommand(network, NetworkGroup) + + podman.add("network", "create", "--ignore") if disableDNS := network.LookupBooleanWithDefault(NetworkGroup, KeyNetworkDisableDNS, false); disableDNS { podman.add("--disable-dns") @@ -898,7 +917,9 @@ func ConvertVolume(volume *parser.UnitFile, name string, names map[string]string labels := volume.LookupAllKeyVal(VolumeGroup, "Label") - podman := NewPodmanCmdline("volume", "create", "--ignore") + podman := createBasePodmanCommand(volume, VolumeGroup) + + podman.add("volume", "create", "--ignore") driver, ok := volume.Lookup(VolumeGroup, KeyDriver) if ok { @@ -1050,7 +1071,9 @@ func ConvertKube(kube *parser.UnitFile, names map[string]string, isUser bool) (* service.Set(ServiceGroup, "SyslogIdentifier", "%N") } - execStart := NewPodmanCmdline("kube", "play") + execStart := createBasePodmanCommand(kube, KubeGroup) + + execStart.add("kube", "play") execStart.add( // Replace any previous container with the same name, not fail @@ -1107,8 +1130,8 @@ func ConvertKube(kube *parser.UnitFile, names map[string]string, isUser bool) (* // Use `ExecStopPost` to make sure cleanup happens even in case of // errors; otherwise containers, pods, etc. would be left behind. - execStop := NewPodmanCmdline("kube", "down") - execStop.add(yamlPath) + execStop := createBasePodmanCommand(kube, KubeGroup) + execStop.add("kube", "down", yamlPath) service.AddCmdline(ServiceGroup, "ExecStopPost", execStop.Args) err = handleSetWorkingDirectory(kube, service) @@ -1142,7 +1165,9 @@ func ConvertImage(image *parser.UnitFile) (*parser.UnitFile, string, error) { // Need the containers filesystem mounted to start podman service.Add(UnitGroup, "RequiresMountsFor", "%t/containers") - podman := NewPodmanCmdline("image", "pull") + podman := createBasePodmanCommand(image, ImageGroup) + + podman.add("image", "pull") stringKeys := map[string]string{ KeyArch: "--arch", @@ -1571,3 +1596,19 @@ func convertToCSV(s []string) (string, error) { return ret, nil } + +func createBasePodmanCommand(unitFile *parser.UnitFile, groupName string) *PodmanCmdline { + podman := NewPodmanCmdline() + + containersConfModules := unitFile.LookupAll(groupName, KeyContainersConfModule) + for _, containersConfModule := range containersConfModules { + podman.addf("--module=%s", containersConfModule) + } + + globalArgs := unitFile.LookupAllArgs(groupName, KeyGlobalArgs) + if len(globalArgs) > 0 { + podman.add(globalArgs...) + } + + return podman +} diff --git a/test/e2e/quadlet/basic.container b/test/e2e/quadlet/basic.container index 4e33fc8621..3a3a98f695 100644 --- a/test/e2e/quadlet/basic.container +++ b/test/e2e/quadlet/basic.container @@ -12,7 +12,7 @@ ## assert-key-is "Service" "Type" "notify" ## assert-key-is "Service" "NotifyAccess" "all" ## assert-key-is "Service" "SyslogIdentifier" "%N" -## assert-key-is-regex "Service" "ExecStopPost" "-.*/podman rm -v -f -i --cidfile=%t/%N.cid" +## assert-key-is-regex "Service" "ExecStopPost" "-[/S].*/podman rm -v -f -i --cidfile=%t/%N.cid" ## assert-key-is-regex "Service" "ExecStop" ".*/podman rm -v -f -i --cidfile=%t/%N.cid" ## assert-key-is "Service" "Environment" "PODMAN_SYSTEMD_UNIT=%n" diff --git a/test/e2e/quadlet/containersconfmodule.container b/test/e2e/quadlet/containersconfmodule.container new file mode 100644 index 0000000000..58c39962de --- /dev/null +++ b/test/e2e/quadlet/containersconfmodule.container @@ -0,0 +1,7 @@ +## assert-podman-global-args "run" "--module=/etc/container/1.conf" +## assert-podman-global-args "run" "--module=/etc/container/2.conf" + +[Container] +Image=image +ContainersConfModule=/etc/container/1.conf +ContainersConfModule=/etc/container/2.conf diff --git a/test/e2e/quadlet/containersconfmodule.image b/test/e2e/quadlet/containersconfmodule.image new file mode 100644 index 0000000000..b5337ba299 --- /dev/null +++ b/test/e2e/quadlet/containersconfmodule.image @@ -0,0 +1,7 @@ +## assert-podman-global-args "image" "--module=/etc/container/1.conf" +## assert-podman-global-args "image" "--module=/etc/container/2.conf" + +[Image] +Image=image:latest +ContainersConfModule=/etc/container/1.conf +ContainersConfModule=/etc/container/2.conf diff --git a/test/e2e/quadlet/containersconfmodule.kube b/test/e2e/quadlet/containersconfmodule.kube new file mode 100644 index 0000000000..2570c6861d --- /dev/null +++ b/test/e2e/quadlet/containersconfmodule.kube @@ -0,0 +1,7 @@ +## assert-podman-global-args "kube" "--module=/etc/container/1.conf" +## assert-podman-global-args "kube" "--module=/etc/container/2.conf" + +[Kube] +Yaml=file.yml +ContainersConfModule=/etc/container/1.conf +ContainersConfModule=/etc/container/2.conf diff --git a/test/e2e/quadlet/containersconfmodule.network b/test/e2e/quadlet/containersconfmodule.network new file mode 100644 index 0000000000..237f7842e4 --- /dev/null +++ b/test/e2e/quadlet/containersconfmodule.network @@ -0,0 +1,6 @@ +## assert-podman-global-args "network" "--module=/etc/container/1.conf" +## assert-podman-global-args "network" "--module=/etc/container/2.conf" + +[Network] +ContainersConfModule=/etc/container/1.conf +ContainersConfModule=/etc/container/2.conf diff --git a/test/e2e/quadlet/containersconfmodule.volume b/test/e2e/quadlet/containersconfmodule.volume new file mode 100644 index 0000000000..b442201bf0 --- /dev/null +++ b/test/e2e/quadlet/containersconfmodule.volume @@ -0,0 +1,6 @@ +## assert-podman-global-args "volume" "--module=/etc/container/1.conf" +## assert-podman-global-args "volume" "--module=/etc/container/2.conf" + +[Volume] +ContainersConfModule=/etc/container/1.conf +ContainersConfModule=/etc/container/2.conf diff --git a/test/e2e/quadlet/globalargs.container b/test/e2e/quadlet/globalargs.container new file mode 100644 index 0000000000..9ddc482b5a --- /dev/null +++ b/test/e2e/quadlet/globalargs.container @@ -0,0 +1,16 @@ +## assert-podman-global-args "run" "--conmon=/usr/bin/somewhere" +## assert-podman-global-args "run" "--imagestore=/var/lib/somestore" +## assert-podman-global-args "run" "--log-level=debug" + +## assert-podman-stop-global-args "rm" "--conmon=/usr/bin/somewhere" +## assert-podman-stop-global-args "rm" "--imagestore=/var/lib/somestore" +## assert-podman-stop-global-args "rm" "--log-level=debug" + +## assert-podman-stop-post-global-args "rm" "--conmon=/usr/bin/somewhere" +## assert-podman-stop-post-global-args "rm" "--imagestore=/var/lib/somestore" +## assert-podman-stop-post-global-args "rm" "--log-level=debug" + +[Container] +Image=image +GlobalArgs=--conmon=/usr/bin/somewhere +GlobalArgs=--imagestore=/var/lib/somestore --log-level=debug diff --git a/test/e2e/quadlet/globalargs.image b/test/e2e/quadlet/globalargs.image new file mode 100644 index 0000000000..9d07a05392 --- /dev/null +++ b/test/e2e/quadlet/globalargs.image @@ -0,0 +1,8 @@ +## assert-podman-global-args "image" "--identity=path=/etc/identity" +## assert-podman-global-args "image" "--syslog" +## assert-podman-global-args "image" "--log-level=debug" + +[Image] +Image=image:latest +GlobalArgs=--identity=path=/etc/identity +GlobalArgs=--syslog --log-level=debug diff --git a/test/e2e/quadlet/globalargs.kube b/test/e2e/quadlet/globalargs.kube new file mode 100644 index 0000000000..3234d0a9ae --- /dev/null +++ b/test/e2e/quadlet/globalargs.kube @@ -0,0 +1,12 @@ +## assert-podman-global-args "kube" "--conmon=/usr/bin/somewhere" +## assert-podman-global-args "kube" "--imagestore=/var/lib/somestore" +## assert-podman-global-args "kube" "--log-level=debug" + +## assert-podman-stop-post-global-args "kube" "--conmon=/usr/bin/somewhere" +## assert-podman-stop-post-global-args "kube" "--imagestore=/var/lib/somestore" +## assert-podman-stop-post-global-args "kube" "--log-level=debug" + +[Kube] +Yaml=file.yml +GlobalArgs=--conmon=/usr/bin/somewhere +GlobalArgs=--imagestore=/var/lib/somestore --log-level=debug diff --git a/test/e2e/quadlet/globalargs.network b/test/e2e/quadlet/globalargs.network new file mode 100644 index 0000000000..ebb74c7915 --- /dev/null +++ b/test/e2e/quadlet/globalargs.network @@ -0,0 +1,7 @@ +## assert-podman-global-args "network" "--network-cmd-path=/usr/bin/network-cmd" +## assert-podman-global-args "network" "--network-config-dir=/etc/network-config" +## assert-podman-global-args "network" "--log-level=debug" + +[Network] +GlobalArgs=--network-cmd-path=/usr/bin/network-cmd +GlobalArgs=--network-config-dir=/etc/network-config --log-level=debug diff --git a/test/e2e/quadlet/globalargs.volume b/test/e2e/quadlet/globalargs.volume new file mode 100644 index 0000000000..60fb34d872 --- /dev/null +++ b/test/e2e/quadlet/globalargs.volume @@ -0,0 +1,7 @@ +## assert-podman-global-args "volume" "--volumepath=/var/lib/somewhere" +## assert-podman-global-args "volume" "--imagestore=/var/lib/store" +## assert-podman-global-args "volume" "--log-level=debug" + +[Volume] +GlobalArgs=--volumepath=/var/lib/somewhere +GlobalArgs=--imagestore=/var/lib/store --log-level=debug diff --git a/test/e2e/quadlet_test.go b/test/e2e/quadlet_test.go index 0a5af5db27..99266d9be3 100644 --- a/test/e2e/quadlet_test.go +++ b/test/e2e/quadlet_test.go @@ -169,14 +169,26 @@ func (t *quadletTestcase) assertKeyContains(args []string, unit *parser.UnitFile return ok && strings.Contains(realValue, value) } -func (t *quadletTestcase) assertPodmanArgs(args []string, unit *parser.UnitFile, key string) bool { +func (t *quadletTestcase) assertPodmanArgs(args []string, unit *parser.UnitFile, key string, allowRegex, globalOnly bool) bool { podmanArgs, _ := unit.LookupLastArgs("Service", key) - return findSublist(podmanArgs, args) != -1 -} + if globalOnly { + podmanCmdLocation := findSublist(podmanArgs, []string{args[0]}) + if podmanCmdLocation == -1 { + return false + } -func (t *quadletTestcase) assertPodmanArgsRegex(args []string, unit *parser.UnitFile, key string) bool { - podmanArgs, _ := unit.LookupLastArgs("Service", key) - return findSublistRegex(podmanArgs, args) != -1 + podmanArgs = podmanArgs[:podmanCmdLocation] + args = args[1:] + } + + var location int + if allowRegex { + location = findSublistRegex(podmanArgs, args) + } else { + location = findSublist(podmanArgs, args) + } + + return location != -1 } func keyValueStringToMap(keyValueString, separator string) (map[string]string, error) { @@ -216,9 +228,19 @@ func keyValMapEqualRegex(expectedKeyValMap, actualKeyValMap map[string]string) b return true } -func (t *quadletTestcase) assertPodmanArgsKeyVal(args []string, unit *parser.UnitFile, key string, allowRegex bool) bool { +func (t *quadletTestcase) assertPodmanArgsKeyVal(args []string, unit *parser.UnitFile, key string, allowRegex, globalOnly bool) bool { podmanArgs, _ := unit.LookupLastArgs("Service", key) + if globalOnly { + podmanCmdLocation := findSublist(podmanArgs, []string{args[0]}) + if podmanCmdLocation == -1 { + return false + } + + podmanArgs = podmanArgs[:podmanCmdLocation] + args = args[1:] + } + expectedKeyValMap, err := keyValueStringToMap(args[2], args[1]) if err != nil { return false @@ -270,19 +292,35 @@ func (t *quadletTestcase) assertPodmanFinalArgsRegex(args []string, unit *parser } func (t *quadletTestcase) assertStartPodmanArgs(args []string, unit *parser.UnitFile) bool { - return t.assertPodmanArgs(args, unit, "ExecStart") + return t.assertPodmanArgs(args, unit, "ExecStart", false, false) } func (t *quadletTestcase) assertStartPodmanArgsRegex(args []string, unit *parser.UnitFile) bool { - return t.assertPodmanArgsRegex(args, unit, "ExecStart") + return t.assertPodmanArgs(args, unit, "ExecStart", true, false) +} + +func (t *quadletTestcase) assertStartPodmanGlobalArgs(args []string, unit *parser.UnitFile) bool { + return t.assertPodmanArgs(args, unit, "ExecStart", false, true) +} + +func (t *quadletTestcase) assertStartPodmanGlobalArgsRegex(args []string, unit *parser.UnitFile) bool { + return t.assertPodmanArgs(args, unit, "ExecStart", true, true) } func (t *quadletTestcase) assertStartPodmanArgsKeyVal(args []string, unit *parser.UnitFile) bool { - return t.assertPodmanArgsKeyVal(args, unit, "ExecStart", false) + return t.assertPodmanArgsKeyVal(args, unit, "ExecStart", false, false) } func (t *quadletTestcase) assertStartPodmanArgsKeyValRegex(args []string, unit *parser.UnitFile) bool { - return t.assertPodmanArgsKeyVal(args, unit, "ExecStart", true) + return t.assertPodmanArgsKeyVal(args, unit, "ExecStart", true, false) +} + +func (t *quadletTestcase) assertStartPodmanGlobalArgsKeyVal(args []string, unit *parser.UnitFile) bool { + return t.assertPodmanArgsKeyVal(args, unit, "ExecStart", false, true) +} + +func (t *quadletTestcase) assertStartPodmanGlobalArgsKeyValRegex(args []string, unit *parser.UnitFile) bool { + return t.assertPodmanArgsKeyVal(args, unit, "ExecStart", true, true) } func (t *quadletTestcase) assertStartPodmanFinalArgs(args []string, unit *parser.UnitFile) bool { @@ -294,7 +332,11 @@ func (t *quadletTestcase) assertStartPodmanFinalArgsRegex(args []string, unit *p } func (t *quadletTestcase) assertStopPodmanArgs(args []string, unit *parser.UnitFile) bool { - return t.assertPodmanArgs(args, unit, "ExecStop") + return t.assertPodmanArgs(args, unit, "ExecStop", false, false) +} + +func (t *quadletTestcase) assertStopPodmanGlobalArgs(args []string, unit *parser.UnitFile) bool { + return t.assertPodmanArgs(args, unit, "ExecStop", false, true) } func (t *quadletTestcase) assertStopPodmanFinalArgs(args []string, unit *parser.UnitFile) bool { @@ -305,8 +347,20 @@ func (t *quadletTestcase) assertStopPodmanFinalArgsRegex(args []string, unit *pa return t.assertPodmanFinalArgsRegex(args, unit, "ExecStop") } +func (t *quadletTestcase) assertStopPodmanArgsKeyVal(args []string, unit *parser.UnitFile) bool { + return t.assertPodmanArgsKeyVal(args, unit, "ExecStop", false, false) +} + +func (t *quadletTestcase) assertStopPodmanArgsKeyValRegex(args []string, unit *parser.UnitFile) bool { + return t.assertPodmanArgsKeyVal(args, unit, "ExecStop", true, false) +} + func (t *quadletTestcase) assertStopPostPodmanArgs(args []string, unit *parser.UnitFile) bool { - return t.assertPodmanArgs(args, unit, "ExecStopPost") + return t.assertPodmanArgs(args, unit, "ExecStopPost", false, false) +} + +func (t *quadletTestcase) assertStopPostPodmanGlobalArgs(args []string, unit *parser.UnitFile) bool { + return t.assertPodmanArgs(args, unit, "ExecStopPost", false, true) } func (t *quadletTestcase) assertStopPostPodmanFinalArgs(args []string, unit *parser.UnitFile) bool { @@ -317,6 +371,14 @@ func (t *quadletTestcase) assertStopPostPodmanFinalArgsRegex(args []string, unit return t.assertPodmanFinalArgsRegex(args, unit, "ExecStopPost") } +func (t *quadletTestcase) assertStopPostPodmanArgsKeyVal(args []string, unit *parser.UnitFile) bool { + return t.assertPodmanArgsKeyVal(args, unit, "ExecStopPost", false, false) +} + +func (t *quadletTestcase) assertStopPostPodmanArgsKeyValRegex(args []string, unit *parser.UnitFile) bool { + return t.assertPodmanArgsKeyVal(args, unit, "ExecStopPost", true, false) +} + func (t *quadletTestcase) assertSymlink(args []string, unit *parser.UnitFile) bool { Expect(args).To(HaveLen(2)) symlink := args[0] @@ -366,6 +428,14 @@ func (t *quadletTestcase) doAssert(check []string, unit *parser.UnitFile, sessio ok = t.assertStartPodmanArgsKeyVal(args, unit) case "assert-podman-args-key-val-regex": ok = t.assertStartPodmanArgsKeyValRegex(args, unit) + case "assert-podman-global-args": + ok = t.assertStartPodmanGlobalArgs(args, unit) + case "assert-podman-global-args-regex": + ok = t.assertStartPodmanGlobalArgsRegex(args, unit) + case "assert-podman-global-args-key-val": + ok = t.assertStartPodmanGlobalArgsKeyVal(args, unit) + case "assert-podman-global-args-key-val-regex": + ok = t.assertStartPodmanGlobalArgsKeyValRegex(args, unit) case "assert-podman-final-args": ok = t.assertStartPodmanFinalArgs(args, unit) case "assert-podman-final-args-regex": @@ -374,16 +444,28 @@ func (t *quadletTestcase) doAssert(check []string, unit *parser.UnitFile, sessio ok = t.assertSymlink(args, unit) case "assert-podman-stop-args": ok = t.assertStopPodmanArgs(args, unit) + case "assert-podman-stop-global-args": + ok = t.assertStopPodmanGlobalArgs(args, unit) case "assert-podman-stop-final-args": ok = t.assertStopPodmanFinalArgs(args, unit) case "assert-podman-stop-final-args-regex": ok = t.assertStopPodmanFinalArgsRegex(args, unit) + case "assert-podman-stop-args-key-val": + ok = t.assertStopPodmanArgsKeyVal(args, unit) + case "assert-podman-stop-args-key-val-regex": + ok = t.assertStopPodmanArgsKeyValRegex(args, unit) case "assert-podman-stop-post-args": ok = t.assertStopPostPodmanArgs(args, unit) + case "assert-podman-stop-post-global-args": + ok = t.assertStopPostPodmanGlobalArgs(args, unit) case "assert-podman-stop-post-final-args": ok = t.assertStopPostPodmanFinalArgs(args, unit) case "assert-podman-stop-post-final-args-regex": ok = t.assertStopPostPodmanFinalArgsRegex(args, unit) + case "assert-podman-stop-post-args-key-val": + ok = t.assertStopPostPodmanArgsKeyVal(args, unit) + case "assert-podman-stop-post-args-key-val-regex": + ok = t.assertStopPostPodmanArgsKeyValRegex(args, unit) default: return fmt.Errorf("Unsupported assertion %s", op) @@ -647,6 +729,8 @@ BOGUS=foo Entry("user.container", "user.container", 0, ""), Entry("volume.container", "volume.container", 0, ""), Entry("workingdir.container", "workingdir.container", 0, ""), + Entry("Container - global args", "globalargs.container", 0, ""), + Entry("Container - Containers Conf Modules", "containersconfmodule.container", 0, ""), Entry("basic.volume", "basic.volume", 0, ""), Entry("device-copy.volume", "device-copy.volume", 0, ""), @@ -657,6 +741,8 @@ BOGUS=foo Entry("uid.volume", "uid.volume", 0, ""), Entry("image.volume", "image.volume", 0, ""), Entry("image-no-image.volume", "image-no-image.volume", 1, "converting \"image-no-image.volume\": the key Image is mandatory when using the image driver"), + Entry("Volume - global args", "globalargs.volume", 0, ""), + Entry("Volume - Containers Conf Modules", "containersconfmodule.volume", 0, ""), Entry("Absolute Path", "absolute.path.kube", 0, ""), Entry("Basic kube", "basic.kube", 0, ""), @@ -676,6 +762,8 @@ BOGUS=foo Entry("Kube - Working Directory YAML Relative Path", "workingdir-yaml-rel.kube", 0, ""), Entry("Kube - Working Directory Unit", "workingdir-unit.kube", 0, ""), Entry("Kube - Working Directory already in Service", "workingdir-service.kube", 0, ""), + Entry("Kube - global args", "globalargs.kube", 0, ""), + Entry("Kube - Containers Conf Modules", "containersconfmodule.kube", 0, ""), Entry("Network - Basic", "basic.network", 0, ""), Entry("Network - Disable DNS", "disable-dns.network", 0, ""), @@ -698,6 +786,8 @@ BOGUS=foo Entry("Network - Subnets", "subnets.network", 0, ""), Entry("Network - multiple subnet, gateway and range", "subnet-trio.multiple.network", 0, ""), Entry("Network - subnet, gateway and range", "subnet-trio.network", 0, ""), + Entry("Network - global args", "globalargs.network", 0, ""), + Entry("Network - Containers Conf Modules", "containersconfmodule.network", 0, ""), Entry("Image - Basic", "basic.image", 0, ""), Entry("Image - No Image", "no-image.image", 1, "converting \"no-image.image\": no Image key specified"), @@ -711,6 +801,8 @@ BOGUS=foo Entry("Image - All Tags", "all-tags.image", 0, ""), Entry("Image - TLS Verify", "tls-verify.image", 0, ""), Entry("Image - Arch and OS", "arch-os.image", 0, ""), + Entry("Image - global args", "globalargs.image", 0, ""), + Entry("Image - Containers Conf Modules", "containersconfmodule.image", 0, ""), ) })