Skip to content

Commit

Permalink
(#5522) Enhance environment trait to include values from secrets/conf…
Browse files Browse the repository at this point in the history
…igmaps
  • Loading branch information
tdiesler committed Jun 18, 2024
1 parent 5d39827 commit cf56225
Show file tree
Hide file tree
Showing 6 changed files with 231 additions and 6 deletions.
53 changes: 51 additions & 2 deletions e2e/advanced/environment_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,56 @@ import (
"github.com/apache/camel-k/v2/pkg/util/defaults"
)

func TestEnvironmentTrait(t *testing.T) {
func TestEnvironmentTraitVars(t *testing.T) {
t.Parallel()

WithNewTestNamespace(t, func(ctx context.Context, g *WithT, ns string) {
operatorID := "camel-k-trait-environment"
g.Expect(CopyCamelCatalog(t, ctx, ns, operatorID)).To(Succeed())
g.Expect(CopyIntegrationKits(t, ctx, ns, operatorID)).To(Succeed())
g.Expect(KamelInstallWithID(t, ctx, operatorID, ns)).To(Succeed())

g.Eventually(SelectedPlatformPhase(t, ctx, ns, operatorID), TestTimeoutMedium).Should(Equal(v1.IntegrationPlatformPhaseReady))

// Create configmap
var cmData = make(map[string]string)
cmData["my-cm-key"] = "hello configmap"
g.Expect(CreatePlainTextConfigmap(t, ctx, ns, "my-cm", cmData)).Should(Succeed())

// Create secret
var secData = make(map[string]string)
secData["my-sec-key"] = "very top secret"
g.Expect(CreatePlainTextSecret(t, ctx, ns, "my-sec", secData)).Should(Succeed())

t.Run("Run simple env-var", func(t *testing.T) {
name := RandomizedSuffixName("envvar")
g.Expect(KamelRunWithID(t, ctx, operatorID, ns, "--name", name, "-t", "environment.vars=MY_ENV_VAR='hello world'", "files/envvar.yaml").Execute()).To(Succeed())
g.Eventually(IntegrationPodPhase(t, ctx, ns, name), TestTimeoutLong).Should(Equal(corev1.PodRunning))
g.Eventually(IntegrationConditionStatus(t, ctx, ns, name, v1.IntegrationConditionReady), TestTimeoutShort).Should(Equal(corev1.ConditionTrue))
g.Eventually(IntegrationLogs(t, ctx, ns, name), TestTimeoutShort).Should(ContainSubstring("hello world"))
})

t.Run("Run env-var from configmap", func(t *testing.T) {
name := RandomizedSuffixName("envvar-configmap")
g.Expect(KamelRunWithID(t, ctx, operatorID, ns, "--name", name, "-t", "environment.vars=MY_ENV_VAR=configmap:my-cm/my-cm-key", "files/envvar.yaml").Execute()).To(Succeed())
g.Eventually(IntegrationPodPhase(t, ctx, ns, name), TestTimeoutLong).Should(Equal(corev1.PodRunning))
g.Eventually(IntegrationConditionStatus(t, ctx, ns, name, v1.IntegrationConditionReady), TestTimeoutShort).Should(Equal(corev1.ConditionTrue))
g.Eventually(IntegrationLogs(t, ctx, ns, name), TestTimeoutShort).Should(ContainSubstring("hello configmap"))
})

t.Run("Run env-var from secret", func(t *testing.T) {
name := RandomizedSuffixName("envvar-secret")
g.Expect(KamelRunWithID(t, ctx, operatorID, ns, "--name", name, "-t", "environment.vars=MY_ENV_VAR=secret:my-sec/my-sec-key", "files/envvar.yaml").Execute()).To(Succeed())
g.Eventually(IntegrationPodPhase(t, ctx, ns, name), TestTimeoutLong).Should(Equal(corev1.PodRunning))
g.Eventually(IntegrationConditionStatus(t, ctx, ns, name, v1.IntegrationConditionReady), TestTimeoutShort).Should(Equal(corev1.ConditionTrue))
g.Eventually(IntegrationLogs(t, ctx, ns, name), TestTimeoutShort).Should(ContainSubstring("very top secret"))
})

g.Expect(Kamel(t, ctx, "delete", "--all", "-n", ns).Execute()).To(Succeed())
})
}

func TestEnvironmentTraitHttpProxy(t *testing.T) {
t.Parallel()

WithNewTestNamespace(t, func(ctx context.Context, g *WithT, ns string) {
Expand Down Expand Up @@ -67,7 +116,7 @@ func TestEnvironmentTrait(t *testing.T) {
}

// Install Camel K with the HTTP proxy environment variable
operatorID := "camel-k-trait-environment"
operatorID := "camel-k-trait-environment-http"
g.Expect(CopyCamelCatalog(t, ctx, ns, operatorID)).To(Succeed())
g.Expect(CopyIntegrationKits(t, ctx, ns, operatorID)).To(Succeed())
g.Expect(KamelInstallWithID(t, ctx, operatorID, ns, "--operator-env-vars", fmt.Sprintf("HTTP_PROXY=%s", httpProxy), "--operator-env-vars", "NO_PROXY="+strings.Join(noProxy, ","))).To(Succeed())
Expand Down
25 changes: 25 additions & 0 deletions e2e/advanced/files/envvar.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# camel-k: language=yaml

# ---------------------------------------------------------------------------
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ---------------------------------------------------------------------------

- from:
uri: "timer:tick"
steps:
- setBody:
simple: "${env:MY_ENV_VAR}"
- to: "log:info"
1 change: 1 addition & 0 deletions pkg/apis/camel/v1/trait/environment.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ type EnvironmentTrait struct {
HTTPProxy *bool `property:"http-proxy" json:"httpProxy,omitempty"`
// A list of environment variables to be added to the integration container.
// The syntax is KEY=VALUE, e.g., `MY_VAR="my value"`.
// The value may also be a reference to a configmap or secret, e.g. `MY_VAR=configmap:my-cm/my-cm-key`
// These take precedence over the previously defined environment variables.
Vars []string `property:"vars" json:"vars,omitempty"`
}
84 changes: 80 additions & 4 deletions pkg/trait/environment.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,12 @@ package trait

import (
"os"
"strings"

corev1 "k8s.io/api/core/v1"
"k8s.io/utils/pointer"

v1 "github.com/apache/camel-k/v2/pkg/apis/camel/v1"
traitv1 "github.com/apache/camel-k/v2/pkg/apis/camel/v1/trait"
"github.com/apache/camel-k/v2/pkg/util/camel"
"github.com/apache/camel-k/v2/pkg/util/defaults"
Expand All @@ -46,7 +49,7 @@ const (
envVarCamelKRuntimeVersion = "CAMEL_K_RUNTIME_VERSION"
envVarMountPathConfigMaps = "CAMEL_K_MOUNT_PATH_CONFIGMAPS"

// Disabling gosec linter as it may triggers:
// Disabling gosec linter as it may trigger:
//
// pkg/trait/environment.go:41: G101: Potential hardcoded credentials (gosec)
// envVarMountPathSecrets = "CAMEL_K_MOUNT_PATH_SECRETS"
Expand Down Expand Up @@ -99,12 +102,85 @@ func (t *environmentTrait) Apply(e *Environment) error {
}
}

if t.Vars != nil {
for _, env := range t.Vars {
k, v := property.SplitPropertyFileEntry(env)
for _, env := range t.Vars {
k, v := property.SplitPropertyFileEntry(env)
switch {
case strings.HasPrefix(v, "configmap:"):

err := setValFromConfigMapKeySelector(&e.EnvVars, k, v)
if err != nil {
return err
}
case strings.HasPrefix(v, "secret:"):
err := setValFromSecretKeySelector(&e.EnvVars, k, v)
if err != nil {
return err
}
default:
envvar.SetVal(&e.EnvVars, k, v)
}
}
return nil
}

func setValFromConfigMapKeySelector(vars *[]corev1.EnvVar, envName string, path string) error {
vs, err := v1.DecodeValueSource(path, "", "invalid configmap reference: "+path)
if err != nil {
return err
}
if envVar := envvar.Get(*vars, envName); envVar != nil {
envVar.Value = ""
envVar.ValueFrom = &corev1.EnvVarSource{
ConfigMapKeyRef: &corev1.ConfigMapKeySelector{
LocalObjectReference: corev1.LocalObjectReference{
Name: vs.ConfigMapKeyRef.Name,
},
Key: vs.ConfigMapKeyRef.Key,
},
}
} else {
*vars = append(*vars, corev1.EnvVar{
Name: envName,
ValueFrom: &corev1.EnvVarSource{
ConfigMapKeyRef: &corev1.ConfigMapKeySelector{
LocalObjectReference: corev1.LocalObjectReference{
Name: vs.ConfigMapKeyRef.Name,
},
Key: vs.ConfigMapKeyRef.Key,
},
},
})
}
return nil
}

func setValFromSecretKeySelector(vars *[]corev1.EnvVar, envName string, path string) error {
vs, err := v1.DecodeValueSource(path, "", "invalid secret reference: "+path)
if err != nil {
return err
}
if envVar := envvar.Get(*vars, envName); envVar != nil {
envVar.Value = ""
envVar.ValueFrom = &corev1.EnvVarSource{
SecretKeyRef: &corev1.SecretKeySelector{
LocalObjectReference: corev1.LocalObjectReference{
Name: vs.SecretKeyRef.Name,
},
Key: vs.SecretKeyRef.Key,
},
}
} else {
*vars = append(*vars, corev1.EnvVar{
Name: envName,
ValueFrom: &corev1.EnvVarSource{
SecretKeyRef: &corev1.SecretKeySelector{
LocalObjectReference: corev1.LocalObjectReference{
Name: vs.SecretKeyRef.Name,
},
Key: vs.SecretKeyRef.Key,
},
},
})
}
return nil
}
70 changes: 70 additions & 0 deletions pkg/trait/environment_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,76 @@ func TestCustomEnvVars(t *testing.T) {
assert.True(t, userK2)
}

func TestCustomEnvVarsFromConfigMap(t *testing.T) {
c, err := camel.DefaultCatalog()
require.NoError(t, err)

env := mockEnvironment(c)
env.Integration.Spec.Traits = v1.Traits{
Environment: &traitv1.EnvironmentTrait{
Vars: []string{"key1=configmap:my-cm/cmk1", "key2=configmap:my-cm/cmk2"},
},
}
env.Platform.ResyncStatusFullConfig()

conditions, err := NewEnvironmentTestCatalog().apply(&env)
require.NoError(t, err)
assert.NotEmpty(t, conditions)

var userK1, userK2 string

env.Resources.VisitDeployment(func(deployment *appsv1.Deployment) {
for _, e := range deployment.Spec.Template.Spec.Containers[0].Env {
if e.Name == "key1" {
assert.Equal(t, "my-cm", e.ValueFrom.ConfigMapKeyRef.Name)
userK1 = e.ValueFrom.ConfigMapKeyRef.Key
}
if e.Name == "key2" {
assert.Equal(t, "my-cm", e.ValueFrom.ConfigMapKeyRef.Name)
userK2 = e.ValueFrom.ConfigMapKeyRef.Key
}
}
})

assert.Equal(t, "cmk1", userK1)
assert.Equal(t, "cmk2", userK2)
}

func TestCustomEnvVarsFromSecret(t *testing.T) {
c, err := camel.DefaultCatalog()
require.NoError(t, err)

env := mockEnvironment(c)
env.Integration.Spec.Traits = v1.Traits{
Environment: &traitv1.EnvironmentTrait{
Vars: []string{"key1=secret:my-sec/sec1", "key2=secret:my-sec/sec2"},
},
}
env.Platform.ResyncStatusFullConfig()

conditions, err := NewEnvironmentTestCatalog().apply(&env)
require.NoError(t, err)
assert.NotEmpty(t, conditions)

var userK1, userK2 string

env.Resources.VisitDeployment(func(deployment *appsv1.Deployment) {
for _, e := range deployment.Spec.Template.Spec.Containers[0].Env {
if e.Name == "key1" {
assert.Equal(t, "my-sec", e.ValueFrom.SecretKeyRef.Name)
userK1 = e.ValueFrom.SecretKeyRef.Key
}
if e.Name == "key2" {
assert.Equal(t, "my-sec", e.ValueFrom.SecretKeyRef.Name)
userK2 = e.ValueFrom.SecretKeyRef.Key
}
}
})

assert.Equal(t, "sec1", userK1)
assert.Equal(t, "sec2", userK2)
}

func NewEnvironmentTestCatalog() *Catalog {
return NewCatalog(nil)
}
Expand Down
4 changes: 4 additions & 0 deletions script/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -397,9 +397,13 @@ endif
./script/bundle_kamelets.sh $(KAMELET_CATALOG_REPO) $(KAMELET_CATALOG_REPO_TAG)

build-compile-integration-tests:
ifndef NOTEST
@echo "####### Compiling integration tests..."
export CAMEL_K_E2E_JUST_COMPILE="true"; \
go test -run nope -tags="integration" ./e2e/...
else
@echo "####### Skipping e2e test compile..."
endif

clean:
# disable gomodules when executing go clean:
Expand Down

0 comments on commit cf56225

Please sign in to comment.