Repository: apache/camel-k
Pull request #2653 (2021-09-21 09:07:49)

[TEST] Global test - support for openshift
<!-- Description -->




<!--
Enter your extended release note in the below block. If the PR requires
additional action from users switching to the new release, include the string
"action required". If no release note is required, write "NONE". 

You can (optionally) mark this PR with labels "kind/bug" or "kind/feature" to make sure
the text is added to the right section of the release notes. 
-->

**Release Note**
```release-note
NONE
```


bouskaJ: @astefanutti can you review?

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 564306dc6..7fc07ea33 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -16,6 +16,8 @@
 - Release 1.6.0 [\#2591](https://github.com/apache/camel-k/issues/2591)
 - Should we move to go 1.16 / 1.17 ? [\#2579](https://github.com/apache/camel-k/issues/2579)
 - Read certificates from secrets to setup TLS config for route trait [\#2574](https://github.com/apache/camel-k/issues/2574)
+- Issue when trying to configure env vars with annotations [\#2512](https://github.com/apache/camel-k/issues/2512)
+- kamel cli output redirection doesn't work for some commands [\#2495](https://github.com/apache/camel-k/issues/2495)
 - \[Doc\] Add documentation for architecture [\#2273](https://github.com/apache/camel-k/issues/2273)
 - Document, or provide examples for, TLS configuration on OpenShift [\#1033](https://github.com/apache/camel-k/issues/1033)
 - support for quarkus in native mode [\#991](https://github.com/apache/camel-k/issues/991)
@@ -23,9 +25,12 @@
 
 **Merged pull requests:**
 
+- Make quarkus dependencies management more robust [\#2658](https://github.com/apache/camel-k/pull/2658) ([doru1004](https://github.com/doru1004))
 - chore\(ci\): Use `github.head_ref` as concurrency group key [\#2657](https://github.com/apache/camel-k/pull/2657) ([astefanutti](https://github.com/astefanutti))
+-  Output redirection Fix: \#2495 [\#2656](https://github.com/apache/camel-k/pull/2656) ([vsokolov00](https://github.com/vsokolov00))
 - chore\(build\): Upgrade Go dependencies [\#2655](https://github.com/apache/camel-k/pull/2655) ([astefanutti](https://github.com/astefanutti))
 - Fix local builds from directory for containerized and non-containerized uses. [\#2654](https://github.com/apache/camel-k/pull/2654) ([doru1004](https://github.com/doru1004))
+- feat\(trait\): environment variables [\#2650](https://github.com/apache/camel-k/pull/2650) ([squakez](https://github.com/squakez))
 - fix: Switch off color in Maven logs [\#2647](https://github.com/apache/camel-k/pull/2647) ([astefanutti](https://github.com/astefanutti))
 - Alow fixed custom labels in any pod [\#2646](https://github.com/apache/camel-k/pull/2646) ([claudio4j](https://github.com/claudio4j))
 - fix: Unify post-build integration phases reconciliation [\#2645](https://github.com/apache/camel-k/pull/2645) ([astefanutti](https://github.com/astefanutti))
diff --git a/docs/modules/traits/pages/environment.adoc b/docs/modules/traits/pages/environment.adoc
index 896d3690f..f3d9c0eb8 100755
--- a/docs/modules/traits/pages/environment.adoc
+++ b/docs/modules/traits/pages/environment.adoc
@@ -32,6 +32,10 @@ The following configuration options are available:
 | bool
 | Enables injection of `NAMESPACE` and `POD_NAME` environment variables (default `true`)
 
+| environment.vars
+| []string
+| A list of variables to be created on the Pod. Must have KEY=VALUE syntax (ie, MY_VAR="my value").
+
 |===
 
 // End of autogenerated code - DO NOT EDIT! (configuration)
diff --git a/examples/README.md b/examples/README.md
index 03b605f30..04b29b539 100644
--- a/examples/README.md
+++ b/examples/README.md
@@ -9,6 +9,7 @@ In this section you will find the most basic examples. Useful to start learning
 | Type  |  Description | Link  |
 |---|---|---|
 | Languages | Simple integrations developed in various supported languages | [see examples](./languages/)|
+| Basic | Simple integrations with basic configuration | [see examples](./basic/)|
 | Cron | How to create a `cront` integration | [see examples](./cron/)|
 | User Config | Explore how to include a `property`, `secret`, `configmap` or file `resource` in your integration | [see examples](./user-config/)|
 | Processor | Show how to include `Processor`s logic | [see examples](./processor/)|
@@ -20,7 +21,6 @@ In this section you will find the most basic examples. Useful to start learning
 
 In this section you can find a few examples of certain [`Camel` components](https://camel.apache.org/components/latest/index.html). This is a limited number of the wide variety of components supported by Apache Camel. You can also find useful examples [in this repository](https://github.com/apache/camel-k-examples).
 
-
 | Type  |  Description | Link  |
 |---|---|---|
 | AMQP | Component usage | [see examples](./amqp/)|
@@ -50,4 +50,5 @@ Traits configuration will be very helpful to fine tune your `Integration`. Here
 
 | Type  |  Description | Link  |
 |---|---|---|
-| JVM | How to use `JVM` trait| [see examples](./traits/jvm/)|
\ No newline at end of file
+| Container | How to customize with `container` trait| [see examples](./traits/container/)|
+| JVM | How to use `jvm` trait| [see examples](./traits/jvm/)|
\ No newline at end of file
diff --git a/examples/basic/Env.java b/examples/basic/Env.java
new file mode 100644
index 000000000..988da150d
--- /dev/null
+++ b/examples/basic/Env.java
@@ -0,0 +1,31 @@
+/*
+ * 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.
+ */
+
+// To run this integrations use:
+//
+// kamel run --env MY_ENV_VAR="hello world" Env.java --dev
+//
+
+import org.apache.camel.builder.RouteBuilder;
+
+public class Env extends RouteBuilder {
+  @Override
+  public void configure() throws Exception {
+	  from("timer:tick")
+        .log("${env:MY_ENV_VAR}");
+  }
+}
\ No newline at end of file
diff --git a/examples/basic/README.md b/examples/basic/README.md
new file mode 100644
index 000000000..9bdef5cd6
--- /dev/null
+++ b/examples/basic/README.md
@@ -0,0 +1,3 @@
+# Camel K basic examples
+
+Find useful examples about how to run an integration in Camel K.
\ No newline at end of file
diff --git a/examples/kamelets/kameletbindings/env-var-writer.kamelet.yaml b/examples/kamelets/kameletbindings/env-var-writer.kamelet.yaml
new file mode 100644
index 000000000..840e9feba
--- /dev/null
+++ b/examples/kamelets/kameletbindings/env-var-writer.kamelet.yaml
@@ -0,0 +1,31 @@
+# ---------------------------------------------------------------------------
+# 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.
+# ---------------------------------------------------------------------------
+
+apiVersion: camel.apache.org/v1alpha1
+kind: Kamelet
+metadata:
+  name: env-var-writer
+spec:
+  definition:
+    title: "Log some env vars"   
+  flow:
+    from:
+      uri: kamelet:source
+      steps:
+      - set-body:
+          simple: "${env:MY_ENV_VAR1} / ${env:MY_ENV_VAR2}"
+      - to: "log:bar"  
diff --git a/examples/kamelets/kameletbindings/kb-env-vars.yaml b/examples/kamelets/kameletbindings/kb-env-vars.yaml
index 7e42760a6..4ce9e5ad4 100644
--- a/examples/kamelets/kameletbindings/kb-env-vars.yaml
+++ b/examples/kamelets/kameletbindings/kb-env-vars.yaml
@@ -15,17 +15,23 @@
 # limitations under the License.
 # ---------------------------------------------------------------------------
 
+# Apply kamelet used in this binding
+# kubectl apply -f env-var-writer.kamelet.yaml
+#
+# Apply kamelet binding
+# kubect apply -f kb-env-vars.yaml
+# 
 apiVersion: camel.apache.org/v1alpha1
 kind: KameletBinding
 metadata:
   name: timer-to-log
-  namespace: default
+  annotations: 
+    trait.camel.apache.org/environment.vars: "[\"MY_ENV_VAR1 = value1\", \"MY_ENV_VAR2 = value2\"]"
 spec:
-  integration:
-    configuration:
-    - type: env
-      value: X=hello world!
-  sink:
-    uri: log:bar
   source:
-    uri: timer:foo
\ No newline at end of file
+    uri: timer:foo
+  sink:
+    ref:
+      kind: Kamelet
+      apiVersion: camel.apache.org/v1alpha1
+      name: env-var-writer
diff --git a/examples/kamelets/kameletbindings/kb-trait-annotation.yaml b/examples/kamelets/kameletbindings/kb-log-trait-annotation.yaml
similarity index 100%
rename from examples/kamelets/kameletbindings/kb-trait-annotation.yaml
rename to examples/kamelets/kameletbindings/kb-log-trait-annotation.yaml
diff --git a/pkg/cmd/run.go b/pkg/cmd/run.go
index 239259bf9..95fcefc52 100644
--- a/pkg/cmd/run.go
+++ b/pkg/cmd/run.go
@@ -621,7 +621,7 @@ func (o *runCmdOptions) createOrUpdateIntegration(cmd *cobra.Command, c client.C
 		integration.Spec.AddConfiguration("volume", item)
 	}
 	for _, item := range o.EnvVars {
-		integration.Spec.AddConfiguration("env", item)
+		o.Traits = append(o.Traits, fmt.Sprintf("environment.vars=%s", item))
 	}
 
 	if err := o.configureTraits(integration, o.Traits, catalog); err != nil {
diff --git a/pkg/trait/environment.go b/pkg/trait/environment.go
index ed5ee41e8..b4ebfa8de 100644
--- a/pkg/trait/environment.go
+++ b/pkg/trait/environment.go
@@ -20,6 +20,7 @@ package trait
 import (
 	"github.com/apache/camel-k/pkg/util/defaults"
 	"github.com/apache/camel-k/pkg/util/envvar"
+	"github.com/apache/camel-k/pkg/util/property"
 )
 
 // The environment trait is used internally to inject standard environment variables in the integration container,
@@ -30,6 +31,8 @@ type environmentTrait struct {
 	BaseTrait `property:",squash"`
 	// Enables injection of `NAMESPACE` and `POD_NAME` environment variables (default `true`)
 	ContainerMeta *bool `property:"container-meta" json:"containerMeta,omitempty"`
+	// A list of variables to be created on the Pod. Must have KEY=VALUE syntax (ie, MY_VAR="my value").
+	Vars []string `property:"vars" json:"vars,omitempty"`
 }
 
 const (
@@ -78,6 +81,13 @@ func (t *environmentTrait) Apply(e *Environment) error {
 		envvar.SetValFrom(&e.EnvVars, envVarPodName, "metadata.name")
 	}
 
+	if t.Vars != nil {
+		for _, env := range t.Vars {
+			k, v := property.SplitPropertyFileEntry(env)
+			envvar.SetVal(&e.EnvVars, k, v)
+		}
+	}
+
 	return nil
 }
 
diff --git a/resources/traits.yaml b/resources/traits.yaml
index 18cea7e51..cc0d88b87 100755
--- a/resources/traits.yaml
+++ b/resources/traits.yaml
@@ -288,6 +288,10 @@ traits:
     type: bool
     description: Enables injection of `NAMESPACE` and `POD_NAME` environment variables
       (default `true`)
+  - name: vars
+    type: '[]string'
+    description: A list of variables to be created on the Pod. Must have KEY=VALUE
+      syntax (ie, MY_VAR="my value").
 - name: error-handler
   platform: true
   profiles:


Test patch:
diff --git a/e2e/builder/global_test.go b/e2e/builder/global_test.go
index caac12501..185398141 100644
--- a/e2e/builder/global_test.go
+++ b/e2e/builder/global_test.go
@@ -24,6 +24,7 @@ package builder
 
 import (
 	"os"
+	ctrl "sigs.k8s.io/controller-runtime/pkg/client"
 	"testing"
 
 	. "github.com/onsi/gomega"
@@ -49,8 +50,8 @@ func TestRunGlobalInstall(t *testing.T) {
 		}
 	}
 
-	WithNewTestNamespace(t, func(ns string) {
-		Expect(Kamel("install", "-n", ns, "--global").Execute()).To(Succeed())
+	test := func(operatorNamespace string) {
+		Expect(Kamel("install", "-n", operatorNamespace, "--global").Execute()).To(Succeed())
 
 		t.Run("Global test on namespace with platform", func(t *testing.T) {
 			WithNewTestNamespace(t, func(ns2 string) {
@@ -60,9 +61,10 @@ func TestRunGlobalInstall(t *testing.T) {
 				Expect(Kamel("run", "-n", ns2, "files/Java.java").Execute()).To(Succeed())
 				Eventually(IntegrationPodPhase(ns2, "java"), TestTimeoutMedium).Should(Equal(corev1.PodRunning))
 				Eventually(IntegrationLogs(ns2, "java"), TestTimeoutShort).Should(ContainSubstring("Magicstring!"))
+				kit := IntegrationKit(ns2, "java")()
 				Expect(Kamel("delete", "--all", "-n", ns2).Execute()).To(Succeed())
-				Expect(Kits(ns2)()).Should(HaveLen(1))
-				Expect(Kits(ns)()).Should(HaveLen(0))
+				Expect(Kits(ns2)()).Should(WithTransform(integrationKitsToNamesTransform(), ContainElement(kit)))
+				Expect(Kits(operatorNamespace)()).Should(WithTransform(integrationKitsToNamesTransform(), Not(ContainElement(kit))))
 
 				Expect(Lease(ns2, platform.OperatorLockName)()).To(BeNil(), "No locking Leases expected")
 			})
@@ -88,9 +90,10 @@ func TestRunGlobalInstall(t *testing.T) {
 				Expect(Kamel("run", "-n", ns4, "files/Java.java").Execute()).To(Succeed())
 				Eventually(IntegrationPodPhase(ns4, "java"), TestTimeoutMedium).Should(Equal(corev1.PodRunning))
 				Eventually(IntegrationLogs(ns4, "java"), TestTimeoutShort).Should(ContainSubstring("Magicstring!"))
+				kit := IntegrationKit(ns4, "java")()
 				Expect(Kamel("delete", "--all", "-n", ns4).Execute()).To(Succeed())
-				Expect(Kits(ns4)()).Should(HaveLen(0))
-				Expect(Kits(ns)()).Should(HaveLen(1)) // Kit built globally
+				Expect(Kits(ns4)()).Should(WithTransform(integrationKitsToNamesTransform(), Not(ContainElement(kit))))
+				Expect(Kits(operatorNamespace)()).Should(WithTransform(integrationKitsToNamesTransform(), ContainElement(kit))) // Kit built globally
 
 				Expect(Lease(ns4, platform.OperatorLockName)()).To(BeNil(), "No locking Leases expected")
 			})
@@ -101,40 +104,81 @@ func TestRunGlobalInstall(t *testing.T) {
 				Expect(Kamel("run", "-n", ns5, "files/Java.java").Execute()).To(Succeed())
 				Eventually(IntegrationPodPhase(ns5, "java"), TestTimeoutMedium).Should(Equal(corev1.PodRunning))
 				Eventually(IntegrationLogs(ns5, "java"), TestTimeoutShort).Should(ContainSubstring("Magicstring!"))
+				kit := IntegrationKit(ns5, "java")()
 				Expect(Kamel("delete", "--all", "-n", ns5).Execute()).To(Succeed())
-				Expect(Kits(ns5)()).Should(HaveLen(0))
-				globalKits := Kits(ns)()
-				Expect(globalKits).Should(HaveLen(1)) // Reusing the same global kit
-				if len(globalKits) == 1 {
-					kit := globalKits[0]
-					// external kit mirroring the global one
-					externalKit := v1.IntegrationKit{
-						ObjectMeta: metav1.ObjectMeta{
-							Namespace: ns5,
-							Name:      "external",
-							Labels: map[string]string{
-								v1.IntegrationKitTypeLabel: v1.IntegrationKitTypeExternal,
-							},
+				Expect(Kits(ns5)()).Should(WithTransform(integrationKitsToNamesTransform(), Not(ContainElement(kit))))
+				globalKits := Kits(operatorNamespace)()
+				Expect(globalKits).Should(WithTransform(integrationKitsToNamesTransform(), ContainElement(kit))) // Reusing the same global kit
+
+				// external kit mirroring the global one
+				externalKit := v1.IntegrationKit{
+					ObjectMeta: metav1.ObjectMeta{
+						Namespace: ns5,
+						Name:      "external",
+						Labels: map[string]string{
+							"camel.apache.org/kit.type": v1.IntegrationKitTypeExternal,
 						},
-						Spec: v1.IntegrationKitSpec{
-							Image: kit.Status.Image,
-						},
-					}
-					Expect(TestClient().Create(TestContext, &externalKit)).Should(BeNil())
-
-					Expect(Kamel("run", "-n", ns5, "files/Java.java", "--name", "ext", "--kit", "external").Execute()).To(Succeed())
-					Eventually(IntegrationPodPhase(ns5, "ext"), TestTimeoutMedium).Should(Equal(corev1.PodRunning))
-					Eventually(IntegrationLogs(ns5, "ext"), TestTimeoutShort).Should(ContainSubstring("Magicstring!"))
-					Expect(IntegrationKit(ns5, "ext")()).Should(Equal("external"))
-					Expect(Kamel("delete", "--all", "-n", ns5).Execute()).To(Succeed())
-					Expect(Kits(ns5)()).Should(HaveLen(1)) // the external one
-					Expect(Kits(ns)()).Should(HaveLen(1))  // the global one
+					},
+					Spec: v1.IntegrationKitSpec{
+						Image: getKitImage(operatorNamespace, kit),
+					},
 				}
+				Expect(TestClient().Create(TestContext, &externalKit)).Should(BeNil())
+
+				Expect(Kamel("run", "-n", ns5, "files/Java.java", "--name", "ext", "--kit", "external").Execute()).To(Succeed())
+				Eventually(IntegrationPodPhase(ns5, "ext"), TestTimeoutMedium).Should(Equal(corev1.PodRunning))
+				Eventually(IntegrationLogs(ns5, "ext"), TestTimeoutShort).Should(ContainSubstring("Magicstring!"))
+				Expect(IntegrationKit(ns5, "ext")()).Should(Equal("external"))
+				Expect(Kamel("delete", "--all", "-n", ns5).Execute()).To(Succeed())
+				Expect(Kits(ns5)()).Should(WithTransform(integrationKitsToNamesTransform(), ContainElement("external")))        // the external one
+				Expect(Kits(operatorNamespace)()).Should(WithTransform(integrationKitsToNamesTransform(), ContainElement(kit))) // the global one
 
 				Expect(Lease(ns5, platform.OperatorLockName)()).To(BeNil(), "No locking Leases expected")
 			})
 		})
 
-		Expect(Kamel("uninstall", "-n", ns, "--skip-crd", "--skip-cluster-roles").Execute()).To(Succeed())
-	})
+		Expect(Kamel("uninstall", "-n", operatorNamespace, "--skip-crd", "--skip-cluster-roles").Execute()).To(Succeed())
+	}
+
+	ocp, err := openshift.IsOpenShift(TestClient())
+	assert.Nil(t, err)
+	if ocp {
+		// global operators are always installed in the openshift-operators namespace
+		RegisterTestingT(t)
+		test("openshift-operators")
+	}else {
+		// create new namespace for the global operator
+		WithNewTestNamespace(t,test)
+	}
+}
+
+func integrationKitsToNamesTransform() func([]v1.IntegrationKit) []string {
+	return func(iks []v1.IntegrationKit) []string {
+		var names []string
+		for _, x := range iks {
+			names = append(names, x.Name)
+		}
+		return names
+	}
+}
+
+func getKitImage(ns string, name string) string {
+	get := v1.IntegrationKit{
+		TypeMeta: metav1.TypeMeta{
+			Kind:       "IntegrationKit",
+			APIVersion: v1.SchemeGroupVersion.String(),
+		},
+		ObjectMeta: metav1.ObjectMeta{
+			Namespace: ns,
+			Name:      name,
+		},
+	}
+	key := ctrl.ObjectKey{
+		Namespace: ns,
+		Name:      name,
+	}
+	if err := TestClient().Get(TestContext, key, &get); err != nil {
+		return ""
+	}
+	return get.Status.Image
 }
diff --git a/pkg/trait/environment_test.go b/pkg/trait/environment_test.go
index 61426f167..df74cbae1 100644
--- a/pkg/trait/environment_test.go
+++ b/pkg/trait/environment_test.go
@@ -36,34 +36,7 @@ func TestDefaultEnvironment(t *testing.T) {
 	catalog, err := camel.DefaultCatalog()
 	assert.Nil(t, err)
 
-	env := Environment{
-		CamelCatalog: catalog,
-		Catalog:      NewCatalog(nil),
-		Integration: &v1.Integration{
-			Status: v1.IntegrationStatus{
-				Phase: v1.IntegrationPhaseDeploying,
-			},
-			Spec: v1.IntegrationSpec{
-				Profile: v1.TraitProfileOpenShift,
-			},
-		},
-		IntegrationKit: &v1.IntegrationKit{
-			Status: v1.IntegrationKitStatus{
-				Phase: v1.IntegrationKitPhaseReady,
-			},
-		},
-		Platform: &v1.IntegrationPlatform{
-			ObjectMeta: metav1.ObjectMeta{
-				Namespace: "ns",
-			},
-			Spec: v1.IntegrationPlatformSpec{
-				Cluster: v1.IntegrationPlatformClusterOpenShift,
-			},
-		},
-		EnvVars:        make([]corev1.EnvVar, 0),
-		ExecutedTraits: make([]Trait, 0),
-		Resources:      kubernetes.NewCollection(),
-	}
+	env := mockEnvironment(catalog)
 	env.Platform.ResyncStatusFullConfig()
 
 	err = NewEnvironmentTestCatalog().apply(&env)
@@ -107,38 +80,11 @@ func TestEnabledContainerMetaDataEnvVars(t *testing.T) {
 	c, err := camel.DefaultCatalog()
 	assert.Nil(t, err)
 
-	env := Environment{
-		CamelCatalog: c,
-		Catalog:      NewCatalog(nil),
-		Integration: &v1.Integration{
-			Status: v1.IntegrationStatus{
-				Phase: v1.IntegrationPhaseDeploying,
-			},
-			Spec: v1.IntegrationSpec{
-				Profile: v1.TraitProfileOpenShift,
-				Traits: map[string]v1.TraitSpec{
-					"environment": test.TraitSpecFromMap(t, map[string]interface{}{
-						"containerMeta": true,
-					}),
-				},
-			},
-		},
-		IntegrationKit: &v1.IntegrationKit{
-			Status: v1.IntegrationKitStatus{
-				Phase: v1.IntegrationKitPhaseReady,
-			},
-		},
-		Platform: &v1.IntegrationPlatform{
-			ObjectMeta: metav1.ObjectMeta{
-				Namespace: "ns",
-			},
-			Spec: v1.IntegrationPlatformSpec{
-				Cluster: v1.IntegrationPlatformClusterOpenShift,
-			},
-		},
-		EnvVars:        make([]corev1.EnvVar, 0),
-		ExecutedTraits: make([]Trait, 0),
-		Resources:      kubernetes.NewCollection(),
+	env := mockEnvironment(c)
+	env.Integration.Spec.Traits = map[string]v1.TraitSpec{
+		"environment": test.TraitSpecFromMap(t, map[string]interface{}{
+			"containerMeta": true,
+		}),
 	}
 	env.Platform.ResyncStatusFullConfig()
 
@@ -173,39 +119,13 @@ func TestDisabledContainerMetaDataEnvVars(t *testing.T) {
 	c, err := camel.DefaultCatalog()
 	assert.Nil(t, err)
 
-	env := Environment{
-		CamelCatalog: c,
-		Catalog:      NewCatalog(nil),
-		Integration: &v1.Integration{
-			Status: v1.IntegrationStatus{
-				Phase: v1.IntegrationPhaseDeploying,
-			},
-			Spec: v1.IntegrationSpec{
-				Profile: v1.TraitProfileOpenShift,
-				Traits: map[string]v1.TraitSpec{
-					"environment": test.TraitSpecFromMap(t, map[string]interface{}{
-						"containerMeta": false,
-					}),
-				},
-			},
-		},
-		IntegrationKit: &v1.IntegrationKit{
-			Status: v1.IntegrationKitStatus{
-				Phase: v1.IntegrationKitPhaseReady,
-			},
-		},
-		Platform: &v1.IntegrationPlatform{
-			ObjectMeta: metav1.ObjectMeta{
-				Namespace: "ns",
-			},
-			Spec: v1.IntegrationPlatformSpec{
-				Cluster: v1.IntegrationPlatformClusterOpenShift,
-			},
-		},
-		EnvVars:        make([]corev1.EnvVar, 0),
-		ExecutedTraits: make([]Trait, 0),
-		Resources:      kubernetes.NewCollection(),
+	env := mockEnvironment(c)
+	env.Integration.Spec.Traits = map[string]v1.TraitSpec{
+		"environment": test.TraitSpecFromMap(t, map[string]interface{}{
+			"containerMeta": false,
+		}),
 	}
+
 	env.Platform.ResyncStatusFullConfig()
 
 	err = NewEnvironmentTestCatalog().apply(&env)
@@ -235,6 +155,71 @@ func TestDisabledContainerMetaDataEnvVars(t *testing.T) {
 	assert.True(t, ck)
 }
 
+func TestCustomEnvVars(t *testing.T) {
+	c, err := camel.DefaultCatalog()
+	assert.Nil(t, err)
+
+	env := mockEnvironment(c)
+	env.Integration.Spec.Traits = map[string]v1.TraitSpec{
+		"environment": test.TraitSpecFromMap(t, map[string]interface{}{
+			"vars": []string{"key1=val1", "key2 = val2"},
+		}),
+	}
+	env.Platform.ResyncStatusFullConfig()
+
+	err = NewEnvironmentTestCatalog().apply(&env)
+
+	assert.Nil(t, err)
+
+	userK1 := false
+	userK2 := false
+
+	env.Resources.VisitDeployment(func(deployment *appsv1.Deployment) {
+		for _, e := range deployment.Spec.Template.Spec.Containers[0].Env {
+			if e.Name == "key1" {
+				userK1 = e.Value == "val1"
+			}
+			if e.Name == "key2" {
+				userK2 = e.Value == "val2"
+			}
+		}
+	})
+
+	assert.True(t, userK1)
+	assert.True(t, userK2)
+}
+
 func NewEnvironmentTestCatalog() *Catalog {
 	return NewCatalog(nil)
 }
+
+func mockEnvironment(catalog *camel.RuntimeCatalog) Environment {
+	return Environment{
+		CamelCatalog: catalog,
+		Catalog:      NewCatalog(nil),
+		Integration: &v1.Integration{
+			Status: v1.IntegrationStatus{
+				Phase: v1.IntegrationPhaseDeploying,
+			},
+			Spec: v1.IntegrationSpec{
+				Profile: v1.TraitProfileOpenShift,
+			},
+		},
+		IntegrationKit: &v1.IntegrationKit{
+			Status: v1.IntegrationKitStatus{
+				Phase: v1.IntegrationKitPhaseReady,
+			},
+		},
+		Platform: &v1.IntegrationPlatform{
+			ObjectMeta: metav1.ObjectMeta{
+				Namespace: "ns",
+			},
+			Spec: v1.IntegrationPlatformSpec{
+				Cluster: v1.IntegrationPlatformClusterOpenShift,
+			},
+		},
+		EnvVars:        make([]corev1.EnvVar, 0),
+		ExecutedTraits: make([]Trait, 0),
+		Resources:      kubernetes.NewCollection(),
+	}
+}