diff --git a/docs/source/markdown/podman-systemd.unit.5.md b/docs/source/markdown/podman-systemd.unit.5.md index eec0a97799..1c35c997ea 100644 --- a/docs/source/markdown/podman-systemd.unit.5.md +++ b/docs/source/markdown/podman-systemd.unit.5.md @@ -333,6 +333,8 @@ Valid options for `[Container]` are listed below: | Pull=never | --pull never | | ReadOnly=true | --read-only | | ReadOnlyTmpfs=true | --read-only-tmpfs | +| ReloadCmd=/usr/bin/command | Add ExecReload and run exec with the value | +| ReloadSignal=SIGHUP | Add ExecReload and run kill with the signal | | Retry=5 | --retry=5 | | RetryDelay=5s | --retry-delay=5s | | Rootfs=/var/lib/rootfs | --rootfs /var/lib/rootfs | @@ -784,6 +786,22 @@ If enabled, makes the image read-only. If ReadOnly is set to `true`, mount a read-write tmpfs on /dev, /dev/shm, /run, /tmp, and /var/tmp. +### `ReloadCmd=` + +Add `ExecReload` line to the `Service` that runs ` podman exec` with this command in this container. + +In order to execute the reload run `systemctl reload ` + +Mutually exclusive with `ReloadSignal` + +### `ReloadSignal=` + +Add `ExecReload` line to the `Service` that runs `podman kill` with this signal which sends the signal to the main container process. + +In order to execute the reload run `systemctl reload ` + +Mutually exclusive with `ReloadCmd` + ### `Retry=` Number of times to retry the image pull when a HTTP error occurs. Equivalent to the Podman `--retry` option. diff --git a/pkg/systemd/quadlet/quadlet.go b/pkg/systemd/quadlet/quadlet.go index 0ed87134b3..2c12a2212a 100644 --- a/pkg/systemd/quadlet/quadlet.go +++ b/pkg/systemd/quadlet/quadlet.go @@ -139,6 +139,8 @@ const ( KeyPull = "Pull" KeyReadOnly = "ReadOnly" KeyReadOnlyTmpfs = "ReadOnlyTmpfs" + KeyReloadCmd = "ReloadCmd" + KeyReloadSignal = "ReloadSignal" KeyRemapGid = "RemapGid" // deprecated KeyRemapUid = "RemapUid" // deprecated KeyRemapUidSize = "RemapUidSize" // deprecated @@ -256,6 +258,8 @@ var ( KeyPull: true, KeyReadOnly: true, KeyReadOnlyTmpfs: true, + KeyReloadCmd: true, + KeyReloadSignal: true, KeyRemapGid: true, KeyRemapUid: true, KeyRemapUidSize: true, @@ -578,6 +582,10 @@ func ConvertContainer(container *parser.UnitFile, isUser bool, unitsInfoMap map[ serviceStopCmd.Args[0] = fmt.Sprintf("-%s", serviceStopCmd.Args[0]) service.AddCmdline(ServiceGroup, "ExecStopPost", serviceStopCmd.Args) + if err := handleExecReload(container, service, ContainerGroup); err != nil { + return nil, warnings, err + } + podman := createBasePodmanCommand(container, ContainerGroup) podman.add("run") @@ -2226,3 +2234,29 @@ func addDefaultDependencies(service *parser.UnitFile, isUser bool) { service.PrependUnitLine(UnitGroup, "Wants", networkUnit) } } + +func handleExecReload(quadletUnitFile, serviceUnitFile *parser.UnitFile, groupName string) error { + reloadSignal, signalOk := quadletUnitFile.Lookup(groupName, KeyReloadSignal) + signalOk = signalOk && len(reloadSignal) > 0 + reloadcmd, cmdOk := quadletUnitFile.LookupLastArgs(groupName, KeyReloadCmd) + cmdOk = cmdOk && len(reloadcmd) > 0 + + if !cmdOk && !signalOk { + return nil + } + + if cmdOk && signalOk { + return fmt.Errorf("%s and %s are mutually exclusive but both are set", KeyReloadCmd, KeyReloadSignal) + } + + serviceReloadCmd := createBasePodmanCommand(quadletUnitFile, groupName) + if cmdOk { + serviceReloadCmd.add("exec", "--cidfile=%t/%N.cid") + serviceReloadCmd.add(reloadcmd...) + } else { + serviceReloadCmd.add("kill", "--cidfile=%t/%N.cid", "--signal", reloadSignal) + } + serviceUnitFile.AddCmdline(ServiceGroup, "ExecReload", serviceReloadCmd.Args) + + return nil +} diff --git a/test/e2e/quadlet/reloadboth.container b/test/e2e/quadlet/reloadboth.container new file mode 100644 index 0000000000..f3a1dde72c --- /dev/null +++ b/test/e2e/quadlet/reloadboth.container @@ -0,0 +1,7 @@ +## assert-failed +## assert-stderr-contains "ReloadCmd and ReloadSignal are mutually exclusive but both are set" + +[Container] +Image=localhost/imagename +ReloadCmd=/usr/bin/command +ReloadSignal=SIGHUP diff --git a/test/e2e/quadlet/reloadcmd.container b/test/e2e/quadlet/reloadcmd.container new file mode 100644 index 0000000000..cb3643663a --- /dev/null +++ b/test/e2e/quadlet/reloadcmd.container @@ -0,0 +1,8 @@ +## assert-podman-reload-args "exec" +## assert-podman-reload-args "--cidfile=%t/%N.cid" +## assert-podman-reload-final-args "/some/binary file" "--arg1" "arg 2" + +[Container] +Image=localhost/imagename +ReloadCmd="/some/binary file" --arg1 \ + "arg 2" diff --git a/test/e2e/quadlet/reloadsignal.container b/test/e2e/quadlet/reloadsignal.container new file mode 100644 index 0000000000..60b8220e99 --- /dev/null +++ b/test/e2e/quadlet/reloadsignal.container @@ -0,0 +1,7 @@ +## assert-podman-reload-args "kill" +## assert-podman-reload-args "--cidfile=%t/%N.cid" +## assert-podman-reload-args "--signal" "SIGHUP" + +[Container] +Image=localhost/imagename +ReloadSignal=SIGHUP diff --git a/test/e2e/quadlet_test.go b/test/e2e/quadlet_test.go index b716441c6c..a621b6e361 100644 --- a/test/e2e/quadlet_test.go +++ b/test/e2e/quadlet_test.go @@ -477,6 +477,30 @@ func (t *quadletTestcase) assertStopPostPodmanArgsKeyValRegex(args []string, uni return t.assertPodmanArgsKeyVal(args, unit, "ExecStopPost", true, false) } +func (t *quadletTestcase) assertReloadPodmanArgs(args []string, unit *parser.UnitFile) bool { + return t.assertPodmanArgs(args, unit, "ExecReload", false, false) +} + +func (t *quadletTestcase) assertReloadPodmanGlobalArgs(args []string, unit *parser.UnitFile) bool { + return t.assertPodmanArgs(args, unit, "ExecReload", false, true) +} + +func (t *quadletTestcase) assertReloadPodmanFinalArgs(args []string, unit *parser.UnitFile) bool { + return t.assertPodmanFinalArgs(args, unit, "ExecReload") +} + +func (t *quadletTestcase) assertReloadPodmanFinalArgsRegex(args []string, unit *parser.UnitFile) bool { + return t.assertPodmanFinalArgsRegex(args, unit, "ExecReload") +} + +func (t *quadletTestcase) assertReloadPodmanArgsKeyVal(args []string, unit *parser.UnitFile) bool { + return t.assertPodmanArgsKeyVal(args, unit, "ExecReload", false, false) +} + +func (t *quadletTestcase) assertReloadPodmanArgsKeyValRegex(args []string, unit *parser.UnitFile) bool { + return t.assertPodmanArgsKeyVal(args, unit, "ExecReload", true, false) +} + func (t *quadletTestcase) assertSymlink(args []string, unit *parser.UnitFile) bool { Expect(args).To(HaveLen(2)) symlink := args[0] @@ -590,6 +614,18 @@ func (t *quadletTestcase) doAssert(check []string, unit *parser.UnitFile, sessio ok = t.assertStopPostPodmanArgsKeyVal(args, unit) case "assert-podman-stop-post-args-key-val-regex": ok = t.assertStopPostPodmanArgsKeyValRegex(args, unit) + case "assert-podman-reload-args": + ok = t.assertReloadPodmanArgs(args, unit) + case "assert-podman-reload-global-args": + ok = t.assertReloadPodmanGlobalArgs(args, unit) + case "assert-podman-reload-final-args": + ok = t.assertReloadPodmanFinalArgs(args, unit) + case "assert-podman-reload-final-args-regex": + ok = t.assertReloadPodmanFinalArgsRegex(args, unit) + case "assert-podman-reload-args-key-val": + ok = t.assertReloadPodmanArgsKeyVal(args, unit) + case "assert-podman-reload-args-key-val-regex": + ok = t.assertReloadPodmanArgsKeyValRegex(args, unit) default: return fmt.Errorf("Unsupported assertion %s", op) @@ -926,6 +962,8 @@ BOGUS=foo Entry("CgroupMode", "cgroups-mode.container"), Entry("Container - No Default Dependencies", "no_deps.container"), Entry("retry.container", "retry.container"), + Entry("reloadcmd.container", "reloadcmd.container"), + Entry("reloadsignal.container", "reloadsignal.container"), Entry("basic.volume", "basic.volume"), Entry("device-copy.volume", "device-copy.volume"), @@ -1063,6 +1101,7 @@ BOGUS=foo Entry("pod.not-found.container", "pod.not-found.container", "converting \"pod.not-found.container\": quadlet pod unit not-found.pod does not exist"), Entry("subidmapping-with-remap.container", "subidmapping-with-remap.container", "converting \"subidmapping-with-remap.container\": deprecated Remap keys are set along with explicit mapping keys"), Entry("userns-with-remap.container", "userns-with-remap.container", "converting \"userns-with-remap.container\": deprecated Remap keys are set along with explicit mapping keys"), + Entry("reloadboth.container", "reloadboth.container", "converting \"reloadboth.container\": ReloadCmd and ReloadSignal are mutually exclusive but both are set"), Entry("image-no-image.volume", "image-no-image.volume", "converting \"image-no-image.volume\": the key Image is mandatory when using the image driver"), Entry("Volume - Quadlet image (.build) not found", "build-not-found.quadlet.volume", "converting \"build-not-found.quadlet.volume\": requested Quadlet image not-found.build was not found"),