From a0cae65c13c8f30703cf2122c5f3e926218d84c7 Mon Sep 17 00:00:00 2001 From: John Schug Date: Wed, 26 Mar 2025 16:49:23 -0700 Subject: [PATCH] quadlet: add support for the UpheldBy option in the Install section This adds support for the UpheldBy option in quadlet files. The UpheldBy option is the counterpart to the Upholds option added in systemd v249 and is similar to the existing WantedBy and RequiredBy options. See https://www.freedesktop.org/software/systemd/man/latest/systemd.unit.html#Upholds=. Signed-off-by: John Schug --- cmd/quadlet/main.go | 30 +++++++++---------- docs/source/markdown/podman-systemd.unit.5.md | 2 +- test/e2e/quadlet/install.container | 5 ++++ 3 files changed, 21 insertions(+), 16 deletions(-) diff --git a/cmd/quadlet/main.go b/cmd/quadlet/main.go index 88483b78d9..ff113499da 100644 --- a/cmd/quadlet/main.go +++ b/cmd/quadlet/main.go @@ -449,6 +449,18 @@ func generateServiceFile(service *parser.UnitFile) error { return nil } +func gatherDependentSymlinks(service *parser.UnitFile, key, dir, filename string) []string { + symlinks := make([]string, 0) + groupBy := service.LookupAllStrv(quadlet.InstallGroup, key) + for _, groupByUnit := range groupBy { + // Only allow filenames, not paths + if !strings.Contains(groupByUnit, "/") { + symlinks = append(symlinks, fmt.Sprintf("%s.%s/%s", groupByUnit, dir, filename)) + } + } + return symlinks +} + // This parses the `Install` group of the unit file and creates the required // symlinks to get systemd to start the newly generated file as needed. // In a traditional setup this is done by "systemctl enable", but that doesn't @@ -476,21 +488,9 @@ func enableServiceFile(outputPath string, service *parser.UnitFile) { } if serviceFilename != "" { - wantedBy := service.LookupAllStrv(quadlet.InstallGroup, "WantedBy") - for _, wantedByUnit := range wantedBy { - // Only allow filenames, not paths - if !strings.Contains(wantedByUnit, "/") { - symlinks = append(symlinks, fmt.Sprintf("%s.wants/%s", wantedByUnit, serviceFilename)) - } - } - - requiredBy := service.LookupAllStrv(quadlet.InstallGroup, "RequiredBy") - for _, requiredByUnit := range requiredBy { - // Only allow filenames, not paths - if !strings.Contains(requiredByUnit, "/") { - symlinks = append(symlinks, fmt.Sprintf("%s.requires/%s", requiredByUnit, serviceFilename)) - } - } + symlinks = append(symlinks, gatherDependentSymlinks(service, "WantedBy", "wants", serviceFilename)...) + symlinks = append(symlinks, gatherDependentSymlinks(service, "RequiredBy", "requires", serviceFilename)...) + symlinks = append(symlinks, gatherDependentSymlinks(service, "UpheldBy", "upholds", serviceFilename)...) } for _, symlinkRel := range symlinks { diff --git a/docs/source/markdown/podman-systemd.unit.5.md b/docs/source/markdown/podman-systemd.unit.5.md index b5dfd8970e..1eb05d522f 100644 --- a/docs/source/markdown/podman-systemd.unit.5.md +++ b/docs/source/markdown/podman-systemd.unit.5.md @@ -129,7 +129,7 @@ For example, to start a container on boot, add something like this to the file: WantedBy=default.target ``` -Currently, only the `Alias`, `WantedBy` and `RequiredBy` keys are supported. +Currently, only the `Alias`, `WantedBy`, `RequiredBy`, and `UpheldBy` keys are supported. The Install section can be part of the main file, or it can be in a separate drop-in file as described above. The latter allows you to diff --git a/test/e2e/quadlet/install.container b/test/e2e/quadlet/install.container index 80c1cd19a1..2d79fcffca 100644 --- a/test/e2e/quadlet/install.container +++ b/test/e2e/quadlet/install.container @@ -7,6 +7,9 @@ ## assert-symlink req1.service.requires/install.service ../install.service ## assert-symlink req2.service.requires/install.service ../install.service ## assert-symlink req3.service.requires/install.service ../install.service +## assert-symlink up1.service.upholds/install.service ../install.service +## assert-symlink up2.service.upholds/install.service ../install.service +## assert-symlink up3.service.upholds/install.service ../install.service [Container] Image=localhost/imagename @@ -19,3 +22,5 @@ WantedBy=want1.service want2.service WantedBy=want3.service RequiredBy=req1.service req2.service RequiredBy=req3.service +UpheldBy=up1.service up2.service +UpheldBy=up3.service