From b4584ea8546be227c74174130ec3a04a93996d28 Mon Sep 17 00:00:00 2001
From: Aditya R <arajan@redhat.com>
Date: Wed, 24 Aug 2022 11:22:33 +0530
Subject: run,create: add support for --env-merge for preprocessing vars

Allow end users to preprocess default environment variables before
injecting them into container using `--env-merge`

Usage
```
podman run -it --rm --env-merge some=${some}-edit --env-merge
some2=${some2}-edit2 myimage sh
```

Closes: https://github.com/containers/podman/issues/15288

Signed-off-by: Aditya R <arajan@redhat.com>
---
 pkg/api/handlers/compat/containers_create.go |  1 +
 pkg/api/handlers/types.go                    |  1 +
 pkg/domain/entities/pods.go                  |  1 +
 pkg/specgen/generate/container.go            | 12 ++++++++++++
 pkg/specgen/specgen.go                       |  3 +++
 pkg/specgenutil/specgen.go                   |  3 +++
 6 files changed, 21 insertions(+)

(limited to 'pkg')

diff --git a/pkg/api/handlers/compat/containers_create.go b/pkg/api/handlers/compat/containers_create.go
index 9fff8b4c8..d4f5d5f36 100644
--- a/pkg/api/handlers/compat/containers_create.go
+++ b/pkg/api/handlers/compat/containers_create.go
@@ -408,6 +408,7 @@ func cliOpts(cc handlers.CreateContainerConfig, rtc *config.Config) (*entities.C
 		Systemd:           "true", // podman default
 		TmpFS:             parsedTmp,
 		TTY:               cc.Config.Tty,
+		EnvMerge:          cc.EnvMerge,
 		UnsetEnv:          cc.UnsetEnv,
 		UnsetEnvAll:       cc.UnsetEnvAll,
 		User:              cc.Config.User,
diff --git a/pkg/api/handlers/types.go b/pkg/api/handlers/types.go
index b533e131c..ebbc5f63a 100644
--- a/pkg/api/handlers/types.go
+++ b/pkg/api/handlers/types.go
@@ -127,6 +127,7 @@ type CreateContainerConfig struct {
 	dockerContainer.Config                                // desired container configuration
 	HostConfig             dockerContainer.HostConfig     // host dependent configuration for container
 	NetworkingConfig       dockerNetwork.NetworkingConfig // network configuration for container
+	EnvMerge               []string                       // preprocess env variables from image before injecting into containers
 	UnsetEnv               []string                       // unset specified default environment variables
 	UnsetEnvAll            bool                           // unset all default environment variables
 }
diff --git a/pkg/domain/entities/pods.go b/pkg/domain/entities/pods.go
index 14ce370c1..33ca2c807 100644
--- a/pkg/domain/entities/pods.go
+++ b/pkg/domain/entities/pods.go
@@ -263,6 +263,7 @@ type ContainerCreateOptions struct {
 	TTY               bool
 	Timezone          string
 	Umask             string
+	EnvMerge          []string
 	UnsetEnv          []string
 	UnsetEnvAll       bool
 	UIDMap            []string
diff --git a/pkg/specgen/generate/container.go b/pkg/specgen/generate/container.go
index 85cd8f5ca..e293ce010 100644
--- a/pkg/specgen/generate/container.go
+++ b/pkg/specgen/generate/container.go
@@ -19,6 +19,7 @@ import (
 	"github.com/containers/podman/v4/pkg/signal"
 	"github.com/containers/podman/v4/pkg/specgen"
 	spec "github.com/opencontainers/runtime-spec/specs-go"
+	"github.com/openshift/imagebuilder"
 	"github.com/sirupsen/logrus"
 	"golang.org/x/sys/unix"
 )
@@ -131,6 +132,17 @@ func CompleteSpec(ctx context.Context, r *libpod.Runtime, s *specgen.SpecGenerat
 		defaultEnvs = envLib.Join(envLib.DefaultEnvVariables(), envLib.Join(defaultEnvs, envs))
 	}
 
+	for _, e := range s.EnvMerge {
+		processedWord, err := imagebuilder.ProcessWord(e, envLib.Slice(defaultEnvs))
+		if err != nil {
+			return nil, fmt.Errorf("unable to process variables for --env-merge %s: %w", e, err)
+		}
+		splitWord := strings.Split(processedWord, "=")
+		if _, ok := defaultEnvs[splitWord[0]]; ok {
+			defaultEnvs[splitWord[0]] = splitWord[1]
+		}
+	}
+
 	for _, e := range s.UnsetEnv {
 		delete(defaultEnvs, e)
 	}
diff --git a/pkg/specgen/specgen.go b/pkg/specgen/specgen.go
index b90f07ef8..51b6736a9 100644
--- a/pkg/specgen/specgen.go
+++ b/pkg/specgen/specgen.go
@@ -204,6 +204,9 @@ type ContainerBasicConfig struct {
 	// The execution domain system allows Linux to provide limited support
 	// for binaries compiled under other UNIX-like operating systems.
 	Personality *spec.LinuxPersonality `json:"personality,omitempty"`
+	// EnvMerge takes the specified environment variables from image and preprocess them before injecting them into the
+	// container.
+	EnvMerge []string `json:"envmerge,omitempty"`
 	// UnsetEnv unsets the specified default environment variables from the image or from buildin or containers.conf
 	// Optional.
 	UnsetEnv []string `json:"unsetenv,omitempty"`
diff --git a/pkg/specgenutil/specgen.go b/pkg/specgenutil/specgen.go
index 7392e7b44..aab2eebd5 100644
--- a/pkg/specgenutil/specgen.go
+++ b/pkg/specgenutil/specgen.go
@@ -839,6 +839,9 @@ func FillOutSpecGen(s *specgen.SpecGenerator, c *entities.ContainerCreateOptions
 	if !s.Volatile {
 		s.Volatile = c.Rm
 	}
+	if len(s.EnvMerge) == 0 || len(c.EnvMerge) != 0 {
+		s.EnvMerge = c.EnvMerge
+	}
 	if len(s.UnsetEnv) == 0 || len(c.UnsetEnv) != 0 {
 		s.UnsetEnv = c.UnsetEnv
 	}
-- 
cgit v1.2.3-54-g00ecf