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"),