From 6b2f48129e172d1bea6d174647e21f94a1c63aff Mon Sep 17 00:00:00 2001 From: Ygal Blum Date: Thu, 23 Nov 2023 18:26:17 +0200 Subject: [PATCH] Quadlet - Add support for .pod units Add support for .pod unit files with only PodmanArgs, GlobalArgs, ContainersConfModule and PodName Add support for linking .container units with .pod ones Add e2e and system tests Add to man page Signed-off-by: Ygal Blum --- cmd/quadlet/main.go | 25 +++- docs/source/markdown/podman-systemd.unit.5.md | 97 ++++++++++++- pkg/systemd/quadlet/quadlet.go | 135 +++++++++++++++++- test/e2e/quadlet/basic.pod | 9 ++ test/e2e/quadlet/name.pod | 4 + test/e2e/quadlet/pod.non-quadlet.container | 6 + test/e2e/quadlet/pod.not-found.container | 6 + test/e2e/quadlet/podmanargs.pod | 13 ++ test/e2e/quadlet_test.go | 68 +++++++++ test/system/252-quadlet.bats | 58 ++++++++ test/system/helpers.bash | 28 ++++ test/system/helpers.systemd.bash | 2 + 12 files changed, 444 insertions(+), 7 deletions(-) create mode 100644 test/e2e/quadlet/basic.pod create mode 100644 test/e2e/quadlet/name.pod create mode 100644 test/e2e/quadlet/pod.non-quadlet.container create mode 100644 test/e2e/quadlet/pod.not-found.container create mode 100644 test/e2e/quadlet/podmanargs.pod diff --git a/cmd/quadlet/main.go b/cmd/quadlet/main.go index b36997b32a..491d8eb3cb 100644 --- a/cmd/quadlet/main.go +++ b/cmd/quadlet/main.go @@ -54,6 +54,7 @@ var ( ".kube": 3, ".network": 2, ".image": 1, + ".pod": 4, } ) @@ -389,6 +390,23 @@ func warnIfAmbiguousName(unit *parser.UnitFile, group string) { } } +func generatePodsInfoMap(units []*parser.UnitFile) map[string]*quadlet.PodInfo { + podsInfoMap := make(map[string]*quadlet.PodInfo) + for _, unit := range units { + if !strings.HasSuffix(unit.Filename, ".pod") { + continue + } + + serviceName := quadlet.GetPodServiceName(unit) + podsInfoMap[unit.Filename] = &quadlet.PodInfo{ + ServiceName: serviceName, + Containers: make([]string, 0), + } + } + + return podsInfoMap +} + func main() { if err := process(); err != nil { Logf("%s", err.Error()) @@ -478,6 +496,9 @@ func process() error { return getOrder(i) < getOrder(j) }) + // Generate the PodsInfoMap to allow containers to link to their pods and add themselves to the pod's containers list + podsInfoMap := generatePodsInfoMap(units) + // A map of network/volume unit file-names, against their calculated names, as needed by Podman. var resourceNames = make(map[string]string) @@ -489,7 +510,7 @@ func process() error { switch { case strings.HasSuffix(unit.Filename, ".container"): warnIfAmbiguousName(unit, quadlet.ContainerGroup) - service, err = quadlet.ConvertContainer(unit, resourceNames, isUserFlag) + service, err = quadlet.ConvertContainer(unit, resourceNames, isUserFlag, podsInfoMap) case strings.HasSuffix(unit.Filename, ".volume"): warnIfAmbiguousName(unit, quadlet.VolumeGroup) service, name, err = quadlet.ConvertVolume(unit, unit.Filename, resourceNames) @@ -500,6 +521,8 @@ func process() error { case strings.HasSuffix(unit.Filename, ".image"): warnIfAmbiguousName(unit, quadlet.ImageGroup) service, name, err = quadlet.ConvertImage(unit) + case strings.HasSuffix(unit.Filename, ".pod"): + service, err = quadlet.ConvertPod(unit, unit.Filename, podsInfoMap) default: Logf("Unsupported file type %q", unit.Filename) continue diff --git a/docs/source/markdown/podman-systemd.unit.5.md b/docs/source/markdown/podman-systemd.unit.5.md index 79659ded24..7f0e7d3f82 100644 --- a/docs/source/markdown/podman-systemd.unit.5.md +++ b/docs/source/markdown/podman-systemd.unit.5.md @@ -6,7 +6,7 @@ podman\-systemd.unit - systemd units using Podman Quadlet ## SYNOPSIS -*name*.container, *name*.volume, *name*.network, *name*.kube *name*.image +*name*.container, *name*.volume, *name*.network, *name*.kube *name*.image, *name*.pod ### Podman unit search path @@ -35,13 +35,11 @@ the [Service] table and [Install] tables pass directly to systemd and are handle See systemd.unit(5) man page for more information. The Podman generator reads the search paths above and reads files with the extensions `.container` -`.volume` and `*.kube`, and for each file generates a similarly named `.service` file. Be aware that +`.volume`, `.network`, `.pod` and `.kube`, and for each file generates a similarly named `.service` file. Be aware that existing vendor services (i.e., in `/usr/`) are replaced if they have the same name. The generated unit files can be started and managed with `systemctl` like any other systemd service. `systemctl {--user} list-unit-files` lists existing unit files on the system. -Files with the `.network` extension are only read if they are mentioned in a `.container` file. See the `Network=` key. - The Podman files use the same format as [regular systemd unit files](https://www.freedesktop.org/software/systemd/man/systemd.syntax.html). Each file type has a custom section (for example, `[Container]`) that is handled by Podman, and all other sections are passed on untouched, allowing the use of any normal systemd configuration options @@ -72,7 +70,8 @@ Quadlet requires the use of cgroup v2, use `podman info --format {{.Host.Cgroups ### Service Type By default, the `Type` field of the `Service` section of the Quadlet file does not need to be set. -Quadlet will set it to `notify` for `.container` and `.kube` files and to `oneshot` for `.volume`, `.network` and `.image` files. +Quadlet will set it to `notify` for `.container` and `.kube` files, +`forking` for `.pod` files, and `oneshot` for `.volume`, `.network` and `.image` files. However, `Type` may be explicitly set to `oneshot` for `.container` and `.kube` files when no containers are expected to run once `podman` exits. @@ -190,6 +189,7 @@ Valid options for `[Container]` are listed below: | Rootfs=/var/lib/rootfs | --rootfs /var/lib/rootfs | | Notify=true | --sdnotify container | | PidsLimit=10000 | --pids-limit 10000 | +| Pod=pod-name | --pod=pod-name | | PodmanArgs=--add-host foobar | --add-host foobar | | PublishPort=50-59 | --publish 50-59 | | Pull=never | --pull=never | @@ -501,6 +501,14 @@ of startup on its own. Tune the container's pids limit. This is equivalent to the Podman `--pids-limit` option. +### `Pod=` + +Specify a Quadlet `.pod` unit to link the container to. +The value must take the form of `.pod` and the `.pod` unit must exist. + +Quadlet will add all the necessary parameters to link between the container and the pod and between their corresponding services. + + ### `PodmanArgs=` This key contains a list of arguments passed directly to the end of the `podman run` command @@ -658,6 +666,69 @@ Working directory inside the container. The default working directory for running binaries within a container is the root directory (/). The image developer can set a different default with the WORKDIR instruction. This option overrides the working directory by using the -w option. +## Pod units [Pod] + +Pod units are named with a `.pod` extension and contain a `[Pod]` section describing +the pod that is created and run as a service. The resulting service file contains a line like +`ExecStartPre=podman pod create …`, and most of the keys in this section control the command-line +options passed to Podman. + +By default, the Podman pod has the same name as the unit, but with a `systemd-` prefix, i.e. +a `$name.pod` file creates a `$name-pod.service` unit and a `systemd-$name` Podman pod. The +`PodName` option allows for overriding this default name with a user-provided one. + +Valid options for `[Container]` are listed below: + +| **[Pod] options** | **podman container create equivalent** | +|-------------------------------------|----------------------------------------| +| ContainersConfModule=/etc/nvd\.conf | --module=/etc/nvd\.conf | +| GlobalArgs=--log-level=debug | --log-level=debug | +| PodmanArgs=\-\-cpus=2 | --cpus=2 | +| PodName=name | --name=name | + +Supported keys in the `[Pod]` section are: + +### `ContainersConfModule=` + +Load the specified containers.conf(5) module. Equivalent to the Podman `--module` option. + +This key can be listed multiple times. + +### `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. + +### `PodmanArgs=` + +This key contains a list of arguments passed directly to the end of the `podman kube play` command +in the generated file (right before the path to the yaml file 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, 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. + +### `PodName=` + +The (optional) name of the Podman pod. If this is not specified, the default value +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. + +Please note that pods and containers cannot have the same name. +So, if PodName is set, it must not conflict with any container. + ## Kube units [Kube] Kube units are named with a `.kube` extension and contain a `[Kube]` section describing @@ -1295,6 +1366,22 @@ IPRange=172.16.0.0/28 Label=org.test.Key=value ``` +Example for Container in a Pod: + +`test.pod` +``` +[Pod] +PodName=test +``` + +`centos.container` +``` +[Container] +Image=quay.io/centos/centos:latest +Exec=sh -c "sleep inf" +Pod=test.pod +``` + ## SEE ALSO **[systemd.unit(5)](https://www.freedesktop.org/software/systemd/man/systemd.unit.html)**, **[systemd.service(5)](https://www.freedesktop.org/software/systemd/man/systemd.service.html)**, diff --git a/pkg/systemd/quadlet/quadlet.go b/pkg/systemd/quadlet/quadlet.go index 26e1745b1e..93ae66398b 100644 --- a/pkg/systemd/quadlet/quadlet.go +++ b/pkg/systemd/quadlet/quadlet.go @@ -29,6 +29,7 @@ const ( InstallGroup = "Install" KubeGroup = "Kube" NetworkGroup = "Network" + PodGroup = "Pod" ServiceGroup = "Service" UnitGroup = "Unit" VolumeGroup = "Volume" @@ -36,6 +37,7 @@ const ( XContainerGroup = "X-Container" XKubeGroup = "X-Kube" XNetworkGroup = "X-Network" + XPodGroup = "X-Pod" XVolumeGroup = "X-Volume" XImageGroup = "X-Image" ) @@ -114,6 +116,8 @@ const ( KeyOS = "OS" KeyPidsLimit = "PidsLimit" KeyPodmanArgs = "PodmanArgs" + KeyPodName = "PodName" + KeyPod = "Pod" KeyPublishPort = "PublishPort" KeyPull = "Pull" KeyReadOnly = "ReadOnly" @@ -153,6 +157,11 @@ const ( KeyYaml = "Yaml" ) +type PodInfo struct { + ServiceName string + Containers []string +} + var ( validPortRange = regexp.Delayed(`\d+(-\d+)?(/udp|/tcp)?$`) @@ -199,6 +208,7 @@ var ( KeyNoNewPrivileges: true, KeyNotify: true, KeyPidsLimit: true, + KeyPod: true, KeyPodmanArgs: true, KeyPublishPort: true, KeyPull: true, @@ -307,6 +317,13 @@ var ( KeyTLSVerify: true, KeyVariant: true, } + + supportedPodKeys = map[string]bool{ + KeyContainersConfModule: true, + KeyGlobalArgs: true, + KeyPodmanArgs: true, + KeyPodName: true, + } ) func replaceExtension(name string, extension string, extraPrefix string, extraSuffix string) string { @@ -382,7 +399,7 @@ func usernsOpts(kind string, opts []string) string { // service file (unit file with Service group) based on the options in the // Container group. // The original Container group is kept around as X-Container. -func ConvertContainer(container *parser.UnitFile, names map[string]string, isUser bool) (*parser.UnitFile, error) { +func ConvertContainer(container *parser.UnitFile, names map[string]string, isUser bool, podsInfoMap map[string]*PodInfo) (*parser.UnitFile, error) { service := container.Dup() service.Filename = replaceExtension(container.Filename, ".service", "", "") @@ -767,6 +784,10 @@ func ConvertContainer(container *parser.UnitFile, names map[string]string, isUse podman.add("--pull", pull) } + if err := handlePod(container, service, ContainerGroup, podsInfoMap, podman); err != nil { + return nil, err + } + handlePodmanArgs(container, ContainerGroup, podman) if len(image) > 0 { @@ -1225,6 +1246,95 @@ func ConvertImage(image *parser.UnitFile) (*parser.UnitFile, string, error) { return service, imageName, nil } +func GetPodServiceName(podUnit *parser.UnitFile) string { + return replaceExtension(podUnit.Filename, "", "", "-pod") +} + +func ConvertPod(podUnit *parser.UnitFile, name string, podsInfoMap map[string]*PodInfo) (*parser.UnitFile, error) { + podInfo, ok := podsInfoMap[podUnit.Filename] + if !ok { + return nil, fmt.Errorf("internal error while processing pod %s", podUnit.Filename) + } + + service := podUnit.Dup() + service.Filename = replaceExtension(podInfo.ServiceName, ".service", "", "") + + if podUnit.Path != "" { + service.Add(UnitGroup, "SourcePath", podUnit.Path) + } + + if err := checkForUnknownKeys(podUnit, PodGroup, supportedPodKeys); err != nil { + return nil, err + } + + // Derive pod name from unit name (with added prefix), or use user-provided name. + podName, ok := podUnit.Lookup(PodGroup, KeyPodName) + if !ok || len(podName) == 0 { + podName = replaceExtension(name, "", "systemd-", "") + } + + /* Rename old Pod group to x-Pod so that systemd ignores it */ + service.RenameGroup(PodGroup, XPodGroup) + + // Need the containers filesystem mounted to start podman + service.Add(UnitGroup, "RequiresMountsFor", "%t/containers") + + for _, containerService := range podInfo.Containers { + service.Add(UnitGroup, "Wants", containerService) + service.Add(UnitGroup, "Before", containerService) + } + + if !podUnit.HasKey(ServiceGroup, "SyslogIdentifier") { + service.Set(ServiceGroup, "SyslogIdentifier", "%N") + } + + execStart := createBasePodmanCommand(podUnit, PodGroup) + execStart.add("pod", "start", "--pod-id-file=%t/%N.pod-id") + service.AddCmdline(ServiceGroup, "ExecStart", execStart.Args) + + execStop := createBasePodmanCommand(podUnit, PodGroup) + execStop.add("pod", "stop") + execStop.add( + "--pod-id-file=%t/%N.pod-id", + "--ignore", + "--time=10", + ) + service.AddCmdline(ServiceGroup, "ExecStop", execStop.Args) + + execStopPost := createBasePodmanCommand(podUnit, PodGroup) + execStopPost.add("pod", "rm") + execStopPost.add( + "--pod-id-file=%t/%N.pod-id", + "--ignore", + "--force", + ) + service.AddCmdline(ServiceGroup, "ExecStopPost", execStopPost.Args) + + execStartPre := createBasePodmanCommand(podUnit, PodGroup) + execStartPre.add("pod", "create") + execStartPre.add( + "--infra-conmon-pidfile=%t/%N.pid", + "--pod-id-file=%t/%N.pod-id", + "--exit-policy=stop", + "--replace", + ) + + execStartPre.addf("--name=%s", podName) + + handlePodmanArgs(podUnit, PodGroup, execStartPre) + + service.AddCmdline(ServiceGroup, "ExecStartPre", execStartPre.Args) + + service.Setv(ServiceGroup, + "Environment", "PODMAN_SYSTEMD_UNIT=%n", + "Type", "forking", + "Restart", "on-failure", + "PIDFile", "%t/%N.pid", + ) + + return service, nil +} + func handleUser(unitFile *parser.UnitFile, groupName string, podman *PodmanCmdline) error { user, hasUser := unitFile.Lookup(groupName, KeyUser) okUser := hasUser && len(user) > 0 @@ -1685,3 +1795,26 @@ func createBasePodmanCommand(unitFile *parser.UnitFile, groupName string) *Podma return podman } + +func handlePod(quadletUnitFile, serviceUnitFile *parser.UnitFile, groupName string, podsInfoMap map[string]*PodInfo, podman *PodmanCmdline) error { + pod, ok := quadletUnitFile.Lookup(groupName, KeyPod) + if ok && len(pod) > 0 { + if !strings.HasSuffix(pod, ".pod") { + return fmt.Errorf("pod %s is not Quadlet based", pod) + } + + podInfo, ok := podsInfoMap[pod] + if !ok { + return fmt.Errorf("quadlet pod unit %s does not exist", pod) + } + + podman.add("--pod-id-file", fmt.Sprintf("%%t/%s.pod-id", podInfo.ServiceName)) + + podServiceName := fmt.Sprintf("%s.service", podInfo.ServiceName) + serviceUnitFile.Add(UnitGroup, "BindsTo", podServiceName) + serviceUnitFile.Add(UnitGroup, "After", podServiceName) + + podInfo.Containers = append(podInfo.Containers, serviceUnitFile.Filename) + } + return nil +} diff --git a/test/e2e/quadlet/basic.pod b/test/e2e/quadlet/basic.pod new file mode 100644 index 0000000000..2b8a7bb1e4 --- /dev/null +++ b/test/e2e/quadlet/basic.pod @@ -0,0 +1,9 @@ +## assert-key-is Unit RequiresMountsFor "%t/containers" +## assert-key-is Service Type forking +## assert-key-is Service SyslogIdentifier "%N" +## assert-key-is-regex Service ExecStartPre ".*/podman pod create --infra-conmon-pidfile=%t/%N.pid --pod-id-file=%t/%N.pod-id --exit-policy=stop --replace --name=systemd-basic" +## assert-key-is-regex Service ExecStart ".*/podman pod start --pod-id-file=%t/%N.pod-id" +## assert-key-is-regex Service ExecStop ".*/podman pod stop --pod-id-file=%t/%N.pod-id --ignore --time=10" +## assert-key-is-regex Service ExecStopPost ".*/podman pod rm --pod-id-file=%t/%N.pod-id --ignore --force" + +[Pod] diff --git a/test/e2e/quadlet/name.pod b/test/e2e/quadlet/name.pod new file mode 100644 index 0000000000..33c528d7bc --- /dev/null +++ b/test/e2e/quadlet/name.pod @@ -0,0 +1,4 @@ +## assert-podman-pre-args "--name=test-pod" + +[Pod] +PodName=test-pod diff --git a/test/e2e/quadlet/pod.non-quadlet.container b/test/e2e/quadlet/pod.non-quadlet.container new file mode 100644 index 0000000000..cde0459fb9 --- /dev/null +++ b/test/e2e/quadlet/pod.non-quadlet.container @@ -0,0 +1,6 @@ +## assert-failed +## assert-stderr-contains "pod test-pod is not Quadlet based" + +[Container] +Image=localhost/imagename +Pod=test-pod diff --git a/test/e2e/quadlet/pod.not-found.container b/test/e2e/quadlet/pod.not-found.container new file mode 100644 index 0000000000..13c64fefe1 --- /dev/null +++ b/test/e2e/quadlet/pod.not-found.container @@ -0,0 +1,6 @@ +## assert-failed +## assert-stderr-contains "quadlet pod unit not-found.pod does not exist" + +[Container] +Image=localhost/imagename +Pod=not-found.pod diff --git a/test/e2e/quadlet/podmanargs.pod b/test/e2e/quadlet/podmanargs.pod new file mode 100644 index 0000000000..4e35532fb7 --- /dev/null +++ b/test/e2e/quadlet/podmanargs.pod @@ -0,0 +1,13 @@ +## assert-podman-pre-args "--foo" +## assert-podman-pre-args "--bar" +## assert-podman-pre-args "--also" +## assert-podman-pre-args "--with-key=value" +## assert-podman-pre-args "--with-space" "yes" + + +[Pod] +PodmanArgs="--foo" \ + --bar +PodmanArgs=--also +PodmanArgs=--with-key=value +PodmanArgs=--with-space yes diff --git a/test/e2e/quadlet_test.go b/test/e2e/quadlet_test.go index 34de882930..a436199f9f 100644 --- a/test/e2e/quadlet_test.go +++ b/test/e2e/quadlet_test.go @@ -39,6 +39,8 @@ func loadQuadletTestcase(path string) *quadletTestcase { service += "-network" case ".image": service += "-image" + case ".pod": + service += "-pod" } service += ".service" @@ -331,6 +333,46 @@ func (t *quadletTestcase) assertStartPodmanFinalArgsRegex(args []string, unit *p return t.assertPodmanFinalArgsRegex(args, unit, "ExecStart") } +func (t *quadletTestcase) assertStartPrePodmanArgs(args []string, unit *parser.UnitFile) bool { + return t.assertPodmanArgs(args, unit, "ExecStartPre", false, false) +} + +func (t *quadletTestcase) assertStartPrePodmanArgsRegex(args []string, unit *parser.UnitFile) bool { + return t.assertPodmanArgs(args, unit, "ExecStartPre", true, false) +} + +func (t *quadletTestcase) assertStartPrePodmanGlobalArgs(args []string, unit *parser.UnitFile) bool { + return t.assertPodmanArgs(args, unit, "ExecStartPre", false, true) +} + +func (t *quadletTestcase) assertStartPrePodmanGlobalArgsRegex(args []string, unit *parser.UnitFile) bool { + return t.assertPodmanArgs(args, unit, "ExecStartPre", true, true) +} + +func (t *quadletTestcase) assertStartPrePodmanArgsKeyVal(args []string, unit *parser.UnitFile) bool { + return t.assertPodmanArgsKeyVal(args, unit, "ExecStartPre", false, false) +} + +func (t *quadletTestcase) assertStartPrePodmanArgsKeyValRegex(args []string, unit *parser.UnitFile) bool { + return t.assertPodmanArgsKeyVal(args, unit, "ExecStartPre", true, false) +} + +func (t *quadletTestcase) assertStartPrePodmanGlobalArgsKeyVal(args []string, unit *parser.UnitFile) bool { + return t.assertPodmanArgsKeyVal(args, unit, "ExecStartPre", false, true) +} + +func (t *quadletTestcase) assertStartPrePodmanGlobalArgsKeyValRegex(args []string, unit *parser.UnitFile) bool { + return t.assertPodmanArgsKeyVal(args, unit, "ExecStartPre", true, true) +} + +func (t *quadletTestcase) assertStartPrePodmanFinalArgs(args []string, unit *parser.UnitFile) bool { + return t.assertPodmanFinalArgs(args, unit, "ExecStartPre") +} + +func (t *quadletTestcase) assertStartPrePodmanFinalArgsRegex(args []string, unit *parser.UnitFile) bool { + return t.assertPodmanFinalArgsRegex(args, unit, "ExecStartPre") +} + func (t *quadletTestcase) assertStopPodmanArgs(args []string, unit *parser.UnitFile) bool { return t.assertPodmanArgs(args, unit, "ExecStop", false, false) } @@ -440,6 +482,26 @@ func (t *quadletTestcase) doAssert(check []string, unit *parser.UnitFile, sessio ok = t.assertStartPodmanFinalArgs(args, unit) case "assert-podman-final-args-regex": ok = t.assertStartPodmanFinalArgsRegex(args, unit) + case "assert-podman-pre-args": + ok = t.assertStartPrePodmanArgs(args, unit) + case "assert-podman-pre-args-regex": + ok = t.assertStartPrePodmanArgsRegex(args, unit) + case "assert-podman-pre-args-key-val": + ok = t.assertStartPrePodmanArgsKeyVal(args, unit) + case "assert-podman-pre-args-key-val-regex": + ok = t.assertStartPrePodmanArgsKeyValRegex(args, unit) + case "assert-podman-pre-global-args": + ok = t.assertStartPrePodmanGlobalArgs(args, unit) + case "assert-podman-pre-global-args-regex": + ok = t.assertStartPrePodmanGlobalArgsRegex(args, unit) + case "assert-podman-pre-global-args-key-val": + ok = t.assertStartPrePodmanGlobalArgsKeyVal(args, unit) + case "assert-podman-pre-global-args-key-val-regex": + ok = t.assertStartPrePodmanGlobalArgsKeyValRegex(args, unit) + case "assert-podman-pre-final-args": + ok = t.assertStartPrePodmanFinalArgs(args, unit) + case "assert-podman-pre-final-args-regex": + ok = t.assertStartPrePodmanFinalArgsRegex(args, unit) case "assert-symlink": ok = t.assertSymlink(args, unit) case "assert-podman-stop-args": @@ -714,6 +776,8 @@ BOGUS=foo Entry("notify.container", "notify.container", 0, ""), Entry("oneshot.container", "oneshot.container", 0, ""), Entry("other-sections.container", "other-sections.container", 0, ""), + Entry("pod.non-quadlet.container", "pod.non-quadlet.container", 1, "converting \"pod.non-quadlet.container\": pod test-pod is not Quadlet based"), + Entry("pod.not-found.container", "pod.not-found.container", 1, "converting \"pod.not-found.container\": quadlet pod unit not-found.pod does not exist"), Entry("podmanargs.container", "podmanargs.container", 0, ""), Entry("ports.container", "ports.container", 0, ""), Entry("ports_ipv6.container", "ports_ipv6.container", 0, ""), @@ -821,6 +885,10 @@ BOGUS=foo Entry("Image - Arch and OS", "arch-os.image", 0, ""), Entry("Image - global args", "globalargs.image", 0, ""), Entry("Image - Containers Conf Modules", "containersconfmodule.image", 0, ""), + + Entry("basic.pod", "basic.pod", 0, ""), + Entry("name.pod", "name.pod", 0, ""), + Entry("podmanargs.pod", "podmanargs.pod", 0, ""), ) }) diff --git a/test/system/252-quadlet.bats b/test/system/252-quadlet.bats index 2b57e58edd..6780af194f 100644 --- a/test/system/252-quadlet.bats +++ b/test/system/252-quadlet.bats @@ -1412,4 +1412,62 @@ EOF run_podman rmi --ignore $(pause_image) } +@test "quadlet - pod simple" { + local quadlet_tmpdir=$PODMAN_TMPDIR/quadlets + + local test_pod_name=pod_test_$(random_string) + local quadlet_pod_unit=$test_pod_name.pod + local quadlet_pod_file=$PODMAN_TMPDIR/$quadlet_pod_unit + cat > $quadlet_pod_file < $quadlet_container_file <