From 347d5372e1c6cf33ae658eb4576bf75217acbad7 Mon Sep 17 00:00:00 2001 From: Ygal Blum Date: Wed, 28 Dec 2022 13:31:46 +0200 Subject: [PATCH] Quadlet Container: Add support for EnvironmentFile and EnvironmentHost Add the new keys to the supported keys list for the Container group Pass the list of EnvironmentFile values while maintaining the order Quadlet e2e test framework: Add support for checking regex in Podman args Add relevant tests Update man Signed-off-by: Ygal Blum --- docs/source/markdown/podman-systemd.unit.5.md | 10 +++++ pkg/systemd/quadlet/quadlet.go | 45 ++++++++++++++----- test/e2e/quadlet/env-file.container | 9 ++++ test/e2e/quadlet/env-host-false.container | 6 +++ test/e2e/quadlet/env-host.container | 6 +++ test/e2e/quadlet_test.go | 29 ++++++++++++ 6 files changed, 95 insertions(+), 10 deletions(-) create mode 100644 test/e2e/quadlet/env-file.container create mode 100644 test/e2e/quadlet/env-host-false.container create mode 100644 test/e2e/quadlet/env-host.container diff --git a/docs/source/markdown/podman-systemd.unit.5.md b/docs/source/markdown/podman-systemd.unit.5.md index 0e3c90a022..aab94287ba 100644 --- a/docs/source/markdown/podman-systemd.unit.5.md +++ b/docs/source/markdown/podman-systemd.unit.5.md @@ -93,6 +93,16 @@ Set an environment variable in the container. This uses the same format as [services in systemd](https://www.freedesktop.org/software/systemd/man/systemd.exec.html#Environment=) and can be listed multiple times. +#### `EnvironmentFile=` + +Use a line-delimited file to set environment variables in the container. +The path may be absolute or relative to the location of the unit file. +This key may be used multiple times, and the order persists when passed to `podman run`. + +#### `EnvironmentHost=` (defaults to `no`) + +Use the host environment inside of the container. + #### `Exec=` If this is set then it defines what command line to run in the container. If it is not set the diff --git a/pkg/systemd/quadlet/quadlet.go b/pkg/systemd/quadlet/quadlet.go index 881070a32d..b8630c9aba 100644 --- a/pkg/systemd/quadlet/quadlet.go +++ b/pkg/systemd/quadlet/quadlet.go @@ -36,6 +36,8 @@ const ( KeyContainerName = "ContainerName" KeyImage = "Image" KeyEnvironment = "Environment" + KeyEnvironmentFile = "EnvironmentFile" + KeyEnvironmentHost = "EnvironmentHost" KeyExec = "Exec" KeyNoNewPrivileges = "NoNewPrivileges" KeyDropCapability = "DropCapability" @@ -77,6 +79,8 @@ var supportedContainerKeys = map[string]bool{ KeyContainerName: true, KeyImage: true, KeyEnvironment: true, + KeyEnvironmentFile: true, + KeyEnvironmentHost: true, KeyExec: true, KeyNoNewPrivileges: true, KeyDropCapability: true, @@ -508,6 +512,19 @@ func ConvertContainer(container *parser.UnitFile, isUser bool) (*parser.UnitFile annotations := container.LookupAllKeyVal(ContainerGroup, KeyAnnotation) podman.addAnnotations(annotations) + envFiles := container.LookupAllArgs(ContainerGroup, KeyEnvironmentFile) + for _, envFile := range envFiles { + filePath, err := getAbsolutePath(container, envFile) + if err != nil { + return nil, err + } + podman.add("--env-file", filePath) + } + + if envHost, ok := container.LookupBoolean(ContainerGroup, KeyEnvironmentHost); ok { + podman.addBool("--env-host", envHost) + } + podmanArgs := container.LookupAllArgs(ContainerGroup, KeyPodmanArgs) podman.add(podmanArgs...) @@ -694,16 +711,9 @@ func ConvertKube(kube *parser.UnitFile, isUser bool) (*parser.UnitFile, error) { return nil, fmt.Errorf("no Yaml key specified") } - if !filepath.IsAbs(yamlPath) { - if len(kube.Path) > 0 { - yamlPath = filepath.Join(filepath.Dir(kube.Path), yamlPath) - } else { - var err error - yamlPath, err = filepath.Abs(yamlPath) - if err != nil { - return nil, err - } - } + yamlPath, err := getAbsolutePath(kube, yamlPath) + if err != nil { + return nil, err } // Only allow mixed or control-group, as nothing else works well @@ -833,3 +843,18 @@ func addNetworks(quadletUnitFile *parser.UnitFile, groupName string, serviceUnit } } } + +func getAbsolutePath(quadletUnitFile *parser.UnitFile, filePath string) (string, error) { + if !filepath.IsAbs(filePath) { + if len(quadletUnitFile.Path) > 0 { + filePath = filepath.Join(filepath.Dir(quadletUnitFile.Path), filePath) + } else { + var err error + filePath, err = filepath.Abs(filePath) + if err != nil { + return "", err + } + } + } + return filePath, nil +} diff --git a/test/e2e/quadlet/env-file.container b/test/e2e/quadlet/env-file.container new file mode 100644 index 0000000000..9ae0de95ee --- /dev/null +++ b/test/e2e/quadlet/env-file.container @@ -0,0 +1,9 @@ +## assert-podman-final-args localhost/imagename +## assert-podman-args --env-file /opt/env/abs-1 --env-file /opt/env/abs-2 +## assert-podman-args-regex --env-file /.*/podman_test.*/quadlet/rel-1 + +[Container] +Image=localhost/imagename +EnvironmentFile=/opt/env/abs-1 +EnvironmentFile=/opt/env/abs-2 +EnvironmentFile=rel-1 diff --git a/test/e2e/quadlet/env-host-false.container b/test/e2e/quadlet/env-host-false.container new file mode 100644 index 0000000000..6fa17f5231 --- /dev/null +++ b/test/e2e/quadlet/env-host-false.container @@ -0,0 +1,6 @@ +## assert-podman-final-args localhost/imagename +## assert-podman-args --env-host=false + +[Container] +Image=localhost/imagename +EnvironmentHost=no diff --git a/test/e2e/quadlet/env-host.container b/test/e2e/quadlet/env-host.container new file mode 100644 index 0000000000..0de8a7b6a8 --- /dev/null +++ b/test/e2e/quadlet/env-host.container @@ -0,0 +1,6 @@ +## assert-podman-final-args localhost/imagename +## assert-podman-args --env-host + +[Container] +Image=localhost/imagename +EnvironmentHost=yes diff --git a/test/e2e/quadlet_test.go b/test/e2e/quadlet_test.go index 0129f2572f..14f5945b2f 100644 --- a/test/e2e/quadlet_test.go +++ b/test/e2e/quadlet_test.go @@ -97,6 +97,21 @@ func findSublist(full []string, sublist []string) int { return -1 } +func findSublistRegex(full []string, sublist []string) int { + if len(sublist) > len(full) { + return -1 + } + if len(sublist) == 0 { + return -1 + } + for i := 0; i < len(full)-len(sublist)+1; i++ { + if matchSublistRegexAt(full, i, sublist) { + return i + } + } + return -1 +} + func (t *quadletTestcase) assertStdErrContains(args []string, session *PodmanSessionIntegration) bool { return strings.Contains(session.ErrorToString(), args[0]) } @@ -155,6 +170,11 @@ func (t *quadletTestcase) assertPodmanArgs(args []string, unit *parser.UnitFile, return findSublist(podmanArgs, args) != -1 } +func (t *quadletTestcase) assertPodmanArgsRegex(args []string, unit *parser.UnitFile, key string) bool { + podmanArgs, _ := unit.LookupLastArgs("Service", key) + return findSublistRegex(podmanArgs, args) != -1 +} + func (t *quadletTestcase) assertPodmanFinalArgs(args []string, unit *parser.UnitFile, key string) bool { podmanArgs, _ := unit.LookupLastArgs("Service", key) if len(podmanArgs) < len(args) { @@ -175,6 +195,10 @@ func (t *quadletTestcase) assertStartPodmanArgs(args []string, unit *parser.Unit return t.assertPodmanArgs(args, unit, "ExecStart") } +func (t *quadletTestcase) assertStartPodmanArgsRegex(args []string, unit *parser.UnitFile) bool { + return t.assertPodmanArgsRegex(args, unit, "ExecStart") +} + func (t *quadletTestcase) assertStartPodmanFinalArgs(args []string, unit *parser.UnitFile) bool { return t.assertPodmanFinalArgs(args, unit, "ExecStart") } @@ -238,6 +262,8 @@ func (t *quadletTestcase) doAssert(check []string, unit *parser.UnitFile, sessio ok = t.assertKeyContains(args, unit) case "assert-podman-args": ok = t.assertStartPodmanArgs(args, unit) + case "assert-podman-args-regex": + ok = t.assertStartPodmanArgsRegex(args, unit) case "assert-podman-final-args": ok = t.assertStartPodmanFinalArgs(args, unit) case "assert-podman-final-args-regex": @@ -382,6 +408,9 @@ var _ = Describe("quadlet system generator", func() { Entry("remap-auto.container", "remap-auto.container"), Entry("remap-auto2.container", "remap-auto2.container"), Entry("volume.container", "volume.container"), + Entry("env-file.container", "env-file.container"), + Entry("env-host.container", "env-host.container"), + Entry("env-host-false.container", "env-host-false.container"), Entry("basic.volume", "basic.volume"), Entry("label.volume", "label.volume"),