Repository: apache/camel-k
Pull request #705 (2019-05-29 13:49:38)

trait(route): do not set Route.Spec.TLS if not needed


Patch:
diff --git a/deploy/operator-deployment.yaml b/deploy/operator-deployment.yaml
index 21f7ed6a8..82af5323a 100644
--- a/deploy/operator-deployment.yaml
+++ b/deploy/operator-deployment.yaml
@@ -36,3 +36,8 @@ spec:
               valueFrom:
                 fieldRef:
                   fieldPath: metadata.name
+            # NAMESPACE is always the operator namespace, independently from WATCH_NAMESPACE
+            - name: NAMESPACE
+              valueFrom:
+                fieldRef:
+                  fieldPath: metadata.namespace
diff --git a/deploy/resources.go b/deploy/resources.go
index faafb198e..71f27fa3d 100644
--- a/deploy/resources.go
+++ b/deploy/resources.go
@@ -10683,6 +10683,11 @@ spec:
               valueFrom:
                 fieldRef:
                   fieldPath: metadata.name
+            # NAMESPACE is always the operator namespace, independently from WATCH_NAMESPACE
+            - name: NAMESPACE
+              valueFrom:
+                fieldRef:
+                  fieldPath: metadata.namespace
 
 `
 	Resources["operator-role-binding-knative.yaml"] =
@@ -11056,28 +11061,6 @@ spec:
     - runtime:jvm
     - runtime:kotlin
     - camel:core
-`
-	Resources["platform-integration-context-spring-boot.yaml"] =
-		`
-apiVersion: camel.apache.org/v1alpha1
-kind: IntegrationContext
-metadata:
-  name: spring-boot
-  labels:
-    app: "camel-k"
-    camel.apache.org/context.created.by.kind: Operator
-    camel.apache.org/context.created.by.name: camel-k-operator
-    camel.apache.org/context.type: platform
-spec:
-  dependencies:
-    - runtime:jvm
-    - runtime:spring-boot
-    - camel:core
-  traits:
-    springboot:
-      configuration:
-        enabled: "true"
-
 `
 	Resources["user-cluster-role.yaml"] =
 		`
diff --git a/pkg/apis/camel/v1alpha1/zz_generated.deepcopy.go b/pkg/apis/camel/v1alpha1/zz_generated.deepcopy.go
index ac2e14879..1233a9e9c 100644
--- a/pkg/apis/camel/v1alpha1/zz_generated.deepcopy.go
+++ b/pkg/apis/camel/v1alpha1/zz_generated.deepcopy.go
@@ -1,21 +1,5 @@
 // +build !ignore_autogenerated
 
-/*
-Copyright The Kubernetes Authors.
-
-Licensed 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.
-*/
-
 // Code generated by deepcopy-gen. DO NOT EDIT.
 
 package v1alpha1
@@ -799,6 +783,11 @@ func (in *IntegrationPlatformSpec) DeepCopyInto(out *IntegrationPlatformSpec) {
 			(*out)[key] = *val.DeepCopy()
 		}
 	}
+	if in.Configuration != nil {
+		in, out := &in.Configuration, &out.Configuration
+		*out = make([]ConfigurationSpec, len(*in))
+		copy(*out, *in)
+	}
 	return
 }
 
@@ -899,6 +888,11 @@ func (in *IntegrationStatus) DeepCopyInto(out *IntegrationStatus) {
 		*out = new(Failure)
 		(*in).DeepCopyInto(*out)
 	}
+	if in.Configuration != nil {
+		in, out := &in.Configuration, &out.Configuration
+		*out = make([]ConfigurationSpec, len(*in))
+		copy(*out, *in)
+	}
 	return
 }
 
diff --git a/pkg/cmd/install.go b/pkg/cmd/install.go
index 658d62373..a2ca34030 100644
--- a/pkg/cmd/install.go
+++ b/pkg/cmd/install.go
@@ -69,6 +69,7 @@ func newCmdInstall(rootCmdOptions *RootCmdOptions) *cobra.Command {
 	cmd.Flags().StringVar(&impl.camelVersion, "camel-version", "", "Set the camel version")
 	cmd.Flags().StringVar(&impl.runtimeVersion, "runtime-version", "", "Set the camel-k runtime version")
 	cmd.Flags().StringVar(&impl.baseImage, "base-image", "", "Set the base image used to run integrations")
+	cmd.Flags().StringVar(&impl.operatorImage, "operator-image", "", "Set the operator image used for the operator deployment")
 	cmd.Flags().StringSliceVar(&impl.contexts, "context", nil, "Add a camel context to build at startup, by default all known contexts are built")
 	cmd.Flags().StringVar(&impl.buildStrategy, "build-strategy", "", "Set the build strategy")
 	cmd.Flags().StringVar(&impl.buildTimeout, "build-timeout", "", "Set how long the build process can last")
@@ -96,6 +97,7 @@ type installCmdOptions struct {
 	camelVersion         string
 	runtimeVersion       string
 	baseImage            string
+	operatorImage        string
 	localRepository      string
 	buildStrategy        string
 	buildTimeout         string
@@ -139,7 +141,7 @@ func (o *installCmdOptions) install(_ *cobra.Command, _ []string) error {
 
 		namespace := o.Namespace
 
-		err = install.OperatorOrCollect(o.Context, c, namespace, collection)
+		err = install.OperatorOrCollect(o.Context, c, namespace, o.operatorImage, collection)
 		if err != nil {
 			return err
 		}
diff --git a/pkg/controller/build/schedule_pod.go b/pkg/controller/build/schedule_pod.go
index 8bbb7c920..be34557d6 100644
--- a/pkg/controller/build/schedule_pod.go
+++ b/pkg/controller/build/schedule_pod.go
@@ -21,6 +21,9 @@ import (
 	"context"
 	"sync"
 
+	"github.com/apache/camel-k/pkg/platform"
+	"github.com/apache/camel-k/pkg/util/defaults"
+
 	corev1 "k8s.io/api/core/v1"
 	k8serrors "k8s.io/apimachinery/pkg/api/errors"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -31,7 +34,6 @@ import (
 	"github.com/pkg/errors"
 
 	"github.com/apache/camel-k/pkg/apis/camel/v1alpha1"
-	"github.com/apache/camel-k/pkg/util/defaults"
 )
 
 // NewSchedulePodAction creates a new schedule action
@@ -88,10 +90,16 @@ func (action *schedulePodAction) Handle(ctx context.Context, build *v1alpha1.Bui
 		return nil
 	}
 
+	// Try to get operator image name before starting the build
+	operatorImage, err := platform.GetCurrentOperatorImage(ctx, action.client)
+	if err != nil {
+		return err
+	}
+
 	// Otherwise, let's create the build pod
 	// We may want to explicitly manage build priority as opposed to relying on
 	// the reconcile loop to handle the queuing
-	pod := newBuildPod(build)
+	pod := newBuildPod(build, operatorImage)
 
 	// Set the Build instance as the owner and controller
 	if err := controllerutil.SetControllerReference(build, pod, action.client.GetScheme()); err != nil {
@@ -115,7 +123,11 @@ func (action *schedulePodAction) Handle(ctx context.Context, build *v1alpha1.Bui
 	return action.client.Status().Update(ctx, target)
 }
 
-func newBuildPod(build *v1alpha1.Build) *corev1.Pod {
+func newBuildPod(build *v1alpha1.Build, operatorImage string) *corev1.Pod {
+	builderImage := operatorImage
+	if builderImage == "" {
+		builderImage = defaults.ImageName + ":" + defaults.Version
+	}
 	pod := &corev1.Pod{
 		TypeMeta: metav1.TypeMeta{
 			APIVersion: corev1.SchemeGroupVersion.String(),
@@ -130,7 +142,8 @@ func newBuildPod(build *v1alpha1.Build) *corev1.Pod {
 			Containers: []corev1.Container{
 				{
 					Name:  "builder",
-					Image: defaults.ImageName + ":" + defaults.Version,
+					Image: builderImage,
+					ImagePullPolicy: "IfNotPresent",
 					Args: []string{
 						"camel-k-builder",
 						build.Namespace,
diff --git a/pkg/controller/integration/initialize.go b/pkg/controller/integration/initialize.go
index 636b0f654..b2e716e98 100644
--- a/pkg/controller/integration/initialize.go
+++ b/pkg/controller/integration/initialize.go
@@ -50,6 +50,9 @@ func (action *initializeAction) CanHandle(integration *v1alpha1.Integration) boo
 // Handle handles the integrations
 func (action *initializeAction) Handle(ctx context.Context, integration *v1alpha1.Integration) error {
 	pl, err := platform.GetCurrentPlatform(ctx, action.client, integration.Namespace)
+	if err != nil {
+		return err
+	}
 
 	// The integration platform needs to be ready before starting to create integrations
 	if err != nil || pl.Status.Phase != v1alpha1.IntegrationPlatformPhaseReady {
diff --git a/pkg/controller/integrationplatform/create.go b/pkg/controller/integrationplatform/create.go
index 572fb312f..023852e05 100644
--- a/pkg/controller/integrationplatform/create.go
+++ b/pkg/controller/integrationplatform/create.go
@@ -50,7 +50,7 @@ func (action *createAction) Handle(ctx context.Context, platform *v1alpha1.Integ
 	for k := range deploy.Resources {
 		if strings.HasPrefix(k, "camel-catalog-") {
 			action.L.Infof("Installing camel catalog: %s", k)
-			err := install.Resources(ctx, action.client, platform.Namespace, k)
+			err := install.Resources(ctx, action.client, platform.Namespace, install.IdentityResourceCustomizer, k)
 			if err != nil {
 				return err
 			}
@@ -79,21 +79,21 @@ func (action *createAction) Handle(ctx context.Context, platform *v1alpha1.Integ
 
 		if len(res) > 0 {
 			action.L.Info("Installing custom platform resources")
-			err := install.Resources(ctx, action.client, platform.Namespace, res...)
+			err := install.Resources(ctx, action.client, platform.Namespace, install.IdentityResourceCustomizer, res...)
 			if err != nil {
 				return err
 			}
 		}
 	} else {
 		action.L.Info("Installing default platform resources")
-		err := install.Resources(ctx, action.client, platform.Namespace, p.DefaultContexts...)
+		err := install.Resources(ctx, action.client, platform.Namespace, install.IdentityResourceCustomizer, p.DefaultContexts...)
 		if err != nil {
 			return err
 		}
 
 		if platform.Spec.Profile == v1alpha1.TraitProfileKnative {
 			action.L.Info("Installing knative resources")
-			err := install.Resources(ctx, action.client, platform.Namespace, p.KnativeContexts...)
+			err := install.Resources(ctx, action.client, platform.Namespace, install.IdentityResourceCustomizer, p.KnativeContexts...)
 			if err != nil {
 				return err
 			}
diff --git a/pkg/install/common.go b/pkg/install/common.go
index d7a15fbb9..f8a9fbe5c 100644
--- a/pkg/install/common.go
+++ b/pkg/install/common.go
@@ -29,15 +29,23 @@ import (
 	"k8s.io/apimachinery/pkg/runtime"
 )
 
+// ResourceCustomizer can be used to inject code that changes the objects before they are created
+type ResourceCustomizer func(object runtime.Object)runtime.Object
+// IdentityResourceCustomizer is a ResourceCustomizer that does nothing
+var IdentityResourceCustomizer = func(object runtime.Object)runtime.Object {
+	return object
+}
+
+
 // Resources installs named resources from the project resource directory
-func Resources(ctx context.Context, c client.Client, namespace string, names ...string) error {
-	return ResourcesOrCollect(ctx, c, namespace, nil, names...)
+func Resources(ctx context.Context, c client.Client, namespace string, customizer ResourceCustomizer, names ...string) error {
+	return ResourcesOrCollect(ctx, c, namespace, nil, customizer, names...)
 }
 
 // ResourcesOrCollect --
-func ResourcesOrCollect(ctx context.Context, c client.Client, namespace string, collection *kubernetes.Collection, names ...string) error {
+func ResourcesOrCollect(ctx context.Context, c client.Client, namespace string, collection *kubernetes.Collection, customizer ResourceCustomizer, names ...string) error {
 	for _, name := range names {
-		if err := ResourceOrCollect(ctx, c, namespace, collection, name); err != nil {
+		if err := ResourceOrCollect(ctx, c, namespace, collection, customizer, name); err != nil {
 			return err
 		}
 	}
@@ -45,18 +53,18 @@ func ResourcesOrCollect(ctx context.Context, c client.Client, namespace string,
 }
 
 // Resource installs a single named resource from the project resource directory
-func Resource(ctx context.Context, c client.Client, namespace string, name string) error {
-	return ResourceOrCollect(ctx, c, namespace, nil, name)
+func Resource(ctx context.Context, c client.Client, namespace string, customizer ResourceCustomizer, name string) error {
+	return ResourceOrCollect(ctx, c, namespace, nil, customizer, name)
 }
 
 // ResourceOrCollect --
-func ResourceOrCollect(ctx context.Context, c client.Client, namespace string, collection *kubernetes.Collection, name string) error {
+func ResourceOrCollect(ctx context.Context, c client.Client, namespace string, collection *kubernetes.Collection, customizer ResourceCustomizer, name string) error {
 	obj, err := kubernetes.LoadResourceFromYaml(c.GetScheme(), deploy.Resources[name])
 	if err != nil {
 		return err
 	}
 
-	return RuntimeObjectOrCollect(ctx, c, namespace, collection, obj)
+	return RuntimeObjectOrCollect(ctx, c, namespace, collection, customizer(obj))
 }
 
 // RuntimeObject installs a single runtime object
diff --git a/pkg/install/operator.go b/pkg/install/operator.go
index aa2be1333..8b1c8bb70 100644
--- a/pkg/install/operator.go
+++ b/pkg/install/operator.go
@@ -21,6 +21,9 @@ import (
 	"context"
 	"errors"
 
+	v1 "k8s.io/api/apps/v1"
+	"k8s.io/apimachinery/pkg/runtime"
+
 	"github.com/apache/camel-k/deploy"
 	"github.com/apache/camel-k/pkg/apis/camel/v1alpha1"
 	"github.com/apache/camel-k/pkg/client"
@@ -31,22 +34,33 @@ import (
 )
 
 // Operator installs the operator resources in the given namespace
-func Operator(ctx context.Context, c client.Client, namespace string) error {
-	return OperatorOrCollect(ctx, c, namespace, nil)
+func Operator(ctx context.Context, c client.Client, customImage string, namespace string) error {
+	return OperatorOrCollect(ctx, c, namespace, customImage, nil)
 }
 
 // OperatorOrCollect installs the operator resources or adds them to the collector if present
-func OperatorOrCollect(ctx context.Context, c client.Client, namespace string, collection *kubernetes.Collection) error {
+func OperatorOrCollect(ctx context.Context, c client.Client, namespace string, customImage string, collection *kubernetes.Collection) error {
+	customizer := IdentityResourceCustomizer
+	if customImage != "" {
+		customizer = func(o runtime.Object) runtime.Object {
+			if d, ok := o.(*v1.Deployment); ok {
+				if d.Labels["camel.apache.org/component"] == "operator" {
+					d.Spec.Template.Spec.Containers[0].Image = customImage
+				}
+			}
+			return o
+		}
+	}
 	isOpenshift, err := openshift.IsOpenShift(c)
 	if err != nil {
 		return err
 	}
 	if isOpenshift {
-		if err := installOpenshift(ctx, c, namespace, collection); err != nil {
+		if err := installOpenshift(ctx, c, namespace, customizer, collection); err != nil {
 			return err
 		}
 	} else {
-		if err := installKubernetes(ctx, c, namespace, collection); err != nil {
+		if err := installKubernetes(ctx, c, namespace, customizer, collection); err != nil {
 			return err
 		}
 	}
@@ -61,8 +75,8 @@ func OperatorOrCollect(ctx context.Context, c client.Client, namespace string, c
 	return nil
 }
 
-func installOpenshift(ctx context.Context, c client.Client, namespace string, collection *kubernetes.Collection) error {
-	return ResourcesOrCollect(ctx, c, namespace, collection,
+func installOpenshift(ctx context.Context, c client.Client, namespace string, customizer ResourceCustomizer, collection *kubernetes.Collection) error {
+	return ResourcesOrCollect(ctx, c, namespace, collection, customizer,
 		"operator-service-account.yaml",
 		"operator-role-openshift.yaml",
 		"operator-role-binding.yaml",
@@ -70,8 +84,8 @@ func installOpenshift(ctx context.Context, c client.Client, namespace string, co
 	)
 }
 
-func installKubernetes(ctx context.Context, c client.Client, namespace string, collection *kubernetes.Collection) error {
-	return ResourcesOrCollect(ctx, c, namespace, collection,
+func installKubernetes(ctx context.Context, c client.Client, namespace string, customizer ResourceCustomizer, collection *kubernetes.Collection) error {
+	return ResourcesOrCollect(ctx, c, namespace, collection, customizer,
 		"operator-service-account.yaml",
 		"operator-role-kubernetes.yaml",
 		"operator-role-binding.yaml",
@@ -80,7 +94,7 @@ func installKubernetes(ctx context.Context, c client.Client, namespace string, c
 }
 
 func installKnative(ctx context.Context, c client.Client, namespace string, collection *kubernetes.Collection) error {
-	return ResourcesOrCollect(ctx, c, namespace, collection,
+	return ResourcesOrCollect(ctx, c, namespace, collection, IdentityResourceCustomizer,
 		"operator-role-knative.yaml",
 		"operator-role-binding-knative.yaml",
 	)
@@ -143,7 +157,7 @@ func Example(ctx context.Context, c client.Client, namespace string) error {
 
 // ExampleOrCollect --
 func ExampleOrCollect(ctx context.Context, c client.Client, namespace string, collection *kubernetes.Collection) error {
-	return ResourcesOrCollect(ctx, c, namespace, collection,
+	return ResourcesOrCollect(ctx, c, namespace, collection, IdentityResourceCustomizer,
 		"cr-example.yaml",
 	)
 }
diff --git a/pkg/platform/operator.go b/pkg/platform/operator.go
new file mode 100644
index 000000000..4431364f8
--- /dev/null
+++ b/pkg/platform/operator.go
@@ -0,0 +1,40 @@
+package platform
+
+import (
+	"context"
+	"errors"
+	"os"
+
+	v1 "k8s.io/api/core/v1"
+	"sigs.k8s.io/controller-runtime/pkg/client"
+)
+
+const operatorNamespaceEnvVariable = "NAMESPACE"
+const operatorPodNameEnvVariable = "POD_NAME"
+
+// GetCurrentOperatorImage returns the image currently used by the running operator if present (when running out of cluster, it may be absent).
+func GetCurrentOperatorImage(ctx context.Context, c client.Client) (string, error) {
+	var podNamespace string
+	var podName string
+	var envSet bool
+	if podNamespace, envSet = os.LookupEnv(operatorNamespaceEnvVariable); !envSet || podNamespace == "" {
+		return "", nil
+	}
+	if podName, envSet = os.LookupEnv(operatorPodNameEnvVariable); !envSet || podName == "" {
+		return "", nil
+	}
+
+	podKey := client.ObjectKey{
+		Namespace: podNamespace,
+		Name:      podName,
+	}
+	pod := v1.Pod{}
+
+	if err := c.Get(ctx, podKey, &pod); err != nil {
+		return "", err
+	}
+	if len(pod.Spec.Containers) == 0 {
+		return "", errors.New("no containers found in operator pod")
+	}
+	return pod.Spec.Containers[0].Image, nil
+}
diff --git a/pkg/trait/route.go b/pkg/trait/route.go
index 351d30949..41f69c80f 100644
--- a/pkg/trait/route.go
+++ b/pkg/trait/route.go
@@ -19,6 +19,7 @@ package trait
 
 import (
 	"errors"
+	"reflect"
 
 	"github.com/apache/camel-k/pkg/apis/camel/v1alpha1"
 
@@ -116,7 +117,6 @@ func (t *routeTrait) getRouteFor(service *corev1.Service) *routev1.Route {
 }
 
 func (t *routeTrait) getTLSConfig() *routev1.TLSConfig {
-
 	config := routev1.TLSConfig{
 		Termination:                   routev1.TLSTerminationType(t.TLSTermination),
 		Certificate:                   t.TLSCertificate,
@@ -126,5 +126,9 @@ func (t *routeTrait) getTLSConfig() *routev1.TLSConfig {
 		InsecureEdgeTerminationPolicy: routev1.InsecureEdgeTerminationPolicyType(t.TLSInsecureEdgeTerminationPolicy),
 	}
 
+	if reflect.DeepEqual(config, routev1.TLSConfig{}) {
+		return nil
+	}
+
 	return &config
 }


Test patch:
diff --git a/pkg/trait/route_test.go b/pkg/trait/route_test.go
index b1975d984..58ff495c0 100644
--- a/pkg/trait/route_test.go
+++ b/pkg/trait/route_test.go
@@ -34,15 +34,13 @@ import (
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 )
 
-func TestRoute_TLS(t *testing.T) {
+func createTestRouteEnvironment(t *testing.T) *Environment {
 	catalog, err := test.DefaultCatalog()
 	assert.Nil(t, err)
 
-	traitCatalog := NewCatalog(context.TODO(), nil)
-
-	environment := Environment{
+	return &Environment{
 		CamelCatalog: catalog,
-		Catalog:      traitCatalog,
+		Catalog:      NewCatalog(context.TODO(), nil),
 		Integration: &v1alpha1.Integration{
 			ObjectMeta: metav1.ObjectMeta{
 				Name:      "test-i",
@@ -51,15 +49,7 @@ func TestRoute_TLS(t *testing.T) {
 			Status: v1alpha1.IntegrationStatus{
 				Phase: v1alpha1.IntegrationPhaseDeploying,
 			},
-			Spec: v1alpha1.IntegrationSpec{
-				Traits: map[string]v1alpha1.TraitSpec{
-					"route": {
-						Configuration: map[string]string{
-							"tls-termination": string(routev1.TLSTerminationEdge),
-						},
-					},
-				},
-			},
+			Spec: v1alpha1.IntegrationSpec{},
 		},
 		IntegrationContext: &v1alpha1.IntegrationContext{
 			Status: v1alpha1.IntegrationContextStatus{
@@ -98,8 +88,39 @@ func TestRoute_TLS(t *testing.T) {
 			},
 		}),
 	}
+}
+
+func TestRoute_Default(t *testing.T) {
+	environment := createTestRouteEnvironment(t)
+	traitsCatalog := environment.Catalog
+
+	err := traitsCatalog.apply(environment)
+
+	assert.Nil(t, err)
+	assert.NotEmpty(t, environment.ExecutedTraits)
+	assert.NotNil(t, environment.GetTrait(ID("route")))
+
+	route := environment.Resources.GetRoute(func(r *routev1.Route) bool {
+		return r.ObjectMeta.Name == "test-i"
+	})
+
+	assert.NotNil(t, route)
+	assert.Nil(t, route.Spec.TLS)
+}
+
+func TestRoute_TLS(t *testing.T) {
+	environment := createTestRouteEnvironment(t)
+	traitsCatalog := environment.Catalog
+
+	environment.Integration.Spec.Traits = map[string]v1alpha1.TraitSpec{
+		"route": {
+			Configuration: map[string]string{
+				"tls-termination": string(routev1.TLSTerminationEdge),
+			},
+		},
+	}
 
-	err = traitCatalog.apply(&environment)
+	err := traitsCatalog.apply(environment)
 
 	assert.Nil(t, err)
 	assert.NotEmpty(t, environment.ExecutedTraits)
diff --git a/test/testing_env.go b/test/testing_env.go
index dbeae00ee..91763c69d 100644
--- a/test/testing_env.go
+++ b/test/testing_env.go
@@ -55,7 +55,7 @@ func init() {
 		panic(err)
 	}
 
-	err = install.Operator(testContext, testClient, getTargetNamespace())
+	err = install.Operator(testContext, testClient, "", getTargetNamespace())
 	if err != nil {
 		panic(err)
 	}