From 66b44caba182de0cddac0079aa2584a9e7671454 Mon Sep 17 00:00:00 2001 From: Christoph Deppisch Date: Thu, 4 May 2023 12:01:08 +0200 Subject: [PATCH] fix: Consistent integration platform settings hierarchy (backport #4317) - Make sure to apply global integration platform settings when creating namespace local platform - Always derive local platform from operator global platform settings - Global platform settings resist when the setting is not explicitly overwritten in the given user namespaced integration platform --- .../local_platform_test.go | 103 ++++++++++ e2e/support/test_support.go | 10 + pkg/platform/defaults.go | 161 +++++++++++++-- pkg/platform/defaults_test.go | 187 ++++++++++++++++++ 4 files changed, 442 insertions(+), 19 deletions(-) create mode 100644 e2e/commonwithcustominstall/local_platform_test.go create mode 100644 pkg/platform/defaults_test.go diff --git a/e2e/commonwithcustominstall/local_platform_test.go b/e2e/commonwithcustominstall/local_platform_test.go new file mode 100644 index 0000000000..98f99924c8 --- /dev/null +++ b/e2e/commonwithcustominstall/local_platform_test.go @@ -0,0 +1,103 @@ +//go:build integration +// +build integration + +// To enable compilation of this file in Goland, go to "Settings -> Go -> Vendoring & Build Tags -> Custom Tags" and add "integration" + +/* +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. +*/ + +package commonwithcustominstall + +import ( + "testing" + + . "github.com/onsi/gomega" + corev1 "k8s.io/api/core/v1" + + . "github.com/apache/camel-k/v2/e2e/support" + v1 "github.com/apache/camel-k/v2/pkg/apis/camel/v1" + traitv1 "github.com/apache/camel-k/v2/pkg/apis/camel/v1/trait" +) + +func TestLocalPlatform(t *testing.T) { + WithNewTestNamespace(t, func(ns string) { + operatorID := "camel-k-platform-local" + Expect(KamelInstallWithID(operatorID, ns, "--global", "--force").Execute()).To(Succeed()) + Eventually(PlatformPhase(ns), TestTimeoutMedium).Should(Equal(v1.IntegrationPlatformPhaseReady)) + + pl := Platform(ns)() + pl.Spec.Build.Maven.Properties = make(map[string]string) + pl.Spec.Build.Maven.Properties["build-global-prop1"] = "build-global-value1" + // set maximum number of running builds + pl.Spec.Build.MaxRunningBuilds = 1 + if err := TestClient().Update(TestContext, pl); err != nil { + t.Error(err) + t.FailNow() + } + + Eventually(PlatformHas(ns, func(pl *v1.IntegrationPlatform) bool { + return pl.Status.Build.MaxRunningBuilds == 1 + }), TestTimeoutMedium).Should(BeTrue()) + + WithNewTestNamespace(t, func(ns1 string) { + localPlatform := v1.NewIntegrationPlatform(ns1, operatorID) + localPlatform.Spec.Build.Maven.Properties = make(map[string]string) + localPlatform.Spec.Build.Maven.Properties["build-local-prop1"] = "build-local-value1" + localPlatform.SetOperatorID(operatorID) + + localPlatform.Spec.Traits.Container = &traitv1.ContainerTrait{ + LimitCPU: "0.1", + } + + if err := TestClient().Create(TestContext, &localPlatform); err != nil { + t.Error(err) + t.FailNow() + } + Eventually(PlatformPhase(ns1), TestTimeoutMedium).Should(Equal(v1.IntegrationPlatformPhaseReady)) + Eventually(PlatformHas(ns1, func(pl *v1.IntegrationPlatform) bool { + return pl.Status.Cluster != "" + }), TestTimeoutShort).Should(BeTrue()) + + Eventually(PlatformHas(ns1, func(pl *v1.IntegrationPlatform) bool { + return pl.Status.Build.MaxRunningBuilds == 1 + }), TestTimeoutShort).Should(BeTrue()) + + local := Platform(ns1)() + Expect(local.Status.Build.PublishStrategy).To(Equal(pl.Status.Build.PublishStrategy)) + Expect(local.Status.Build.BuildStrategy).To(Equal(pl.Status.Build.BuildStrategy)) + Expect(local.Status.Build.Maven.LocalRepository).To(Equal(pl.Status.Build.Maven.LocalRepository)) + Expect(local.Status.Build.Maven.CLIOptions).To(ContainElements(pl.Status.Build.Maven.CLIOptions)) + Expect(local.Status.Build.Maven.Extension).To(BeEmpty()) + Expect(local.Status.Build.Maven.Properties).To(HaveLen(2)) + Expect(local.Status.Build.Maven.Properties["build-global-prop1"]).To(Equal("build-global-value1")) + Expect(local.Status.Build.Maven.Properties["build-local-prop1"]).To(Equal("build-local-value1")) + + Expect(KamelRunWithID(operatorID, ns1, "--name", "local-integration", "files/yaml.yaml").Execute()).To(Succeed()) + Eventually(IntegrationPod(ns1, "local-integration"), TestTimeoutMedium).Should(Not(BeNil())) + Eventually(IntegrationPodHas(ns1, "local-integration", func(pod *corev1.Pod) bool { + if len(pod.Spec.Containers) != 1 { + return false + } + cpuLimits := pod.Spec.Containers[0].Resources.Limits.Cpu() + return cpuLimits != nil && cpuLimits.AsApproximateFloat64() > 0 + }), TestTimeoutShort).Should(BeTrue()) + + // Clean up + Expect(Kamel("delete", "--all", "-n", ns1).Execute()).To(Succeed()) + }) + }) +} diff --git a/e2e/support/test_support.go b/e2e/support/test_support.go index 5cd425710b..c70725ff04 100644 --- a/e2e/support/test_support.go +++ b/e2e/support/test_support.go @@ -1799,6 +1799,16 @@ func SelectedPlatformPhase(ns string, name string) func() v1.IntegrationPlatform } } +func PlatformHas(ns string, predicate func(pl *v1.IntegrationPlatform) bool) func() bool { + return func() bool { + pl := Platform(ns)() + if pl == nil { + return false + } + return predicate(pl) + } +} + func PlatformCondition( ns string, conditionType v1.IntegrationPlatformConditionType, diff --git a/pkg/platform/defaults.go b/pkg/platform/defaults.go index 4f7da9f57c..8dbf8a29d5 100644 --- a/pkg/platform/defaults.go +++ b/pkg/platform/defaults.go @@ -53,9 +53,14 @@ func ConfigureDefaults(ctx context.Context, c client.Client, p *v1.IntegrationPl // Reset the state to initial values p.ResyncStatusFullConfig() + // Apply settings from global integration platform that is bound to this operator + if err := applyGlobalPlatformDefaults(ctx, c, p); err != nil { + return err + } + // update missing fields in the resource if p.Status.Cluster == "" { - log.Debugf("Integration Platform [%s]: setting cluster status", p.Namespace) + log.Debugf("Integration Platform %s [%s]: setting cluster status", p.Name, p.Namespace) // determine the kind of cluster the platform is installed into isOpenShift, err := openshift.IsOpenShift(c) switch { @@ -74,12 +79,12 @@ func ConfigureDefaults(ctx context.Context, c client.Client, p *v1.IntegrationPl } else { p.Status.Build.PublishStrategy = v1.IntegrationPlatformBuildPublishStrategySpectrum } - log.Debugf("Integration Platform [%s]: setting publishing strategy %s", p.Namespace, p.Status.Build.PublishStrategy) + log.Debugf("Integration Platform %s [%s]: setting publishing strategy %s", p.Name, p.Namespace, p.Status.Build.PublishStrategy) } if p.Status.Build.BuildStrategy == "" { p.Status.Build.BuildStrategy = v1.BuildStrategyPod - log.Debugf("Integration Platform [%s]: setting build strategy %s", p.Namespace, p.Status.Build.BuildStrategy) + log.Debugf("Integration Platform %s [%s]: setting build strategy %s", p.Name, p.Namespace, p.Status.Build.BuildStrategy) } err := setPlatformDefaults(p, verbose) @@ -110,7 +115,7 @@ func ConfigureDefaults(ctx context.Context, c client.Client, p *v1.IntegrationPl } func CreateBuilderServiceAccount(ctx context.Context, client client.Client, p *v1.IntegrationPlatform) error { - log.Debugf("Integration Platform [%s]: creating build service account", p.Namespace) + log.Debugf("Integration Platform %s [%s]: creating build service account", p.Name, p.Namespace) sa := corev1.ServiceAccount{} key := ctrl.ObjectKey{ Name: BuilderServiceAccount, @@ -129,7 +134,7 @@ func configureRegistry(ctx context.Context, c client.Client, p *v1.IntegrationPl if p.Status.Cluster == v1.IntegrationPlatformClusterOpenShift && p.Status.Build.PublishStrategy != v1.IntegrationPlatformBuildPublishStrategyS2I && p.Status.Build.Registry.Address == "" { - log.Debugf("Integration Platform [%s]: setting registry address", p.Namespace) + log.Debugf("Integration Platform %s [%s]: setting registry address", p.Name, p.Namespace) // Default to using OpenShift internal container images registry when using a strategy other than S2I p.Status.Build.Registry.Address = "image-registry.openshift-image-registry.svc:5000" @@ -138,12 +143,12 @@ func configureRegistry(ctx context.Context, c client.Client, p *v1.IntegrationPl if err != nil { return err } - log.Debugf("Integration Platform [%s]: setting registry certificate authority", p.Namespace) + log.Debugf("Integration Platform %s [%s]: setting registry certificate authority", p.Name, p.Namespace) p.Status.Build.Registry.CA = cm.Name // Default to using the registry secret that's configured for the builder service account if p.Status.Build.Registry.Secret == "" { - log.Debugf("Integration Platform [%s]: setting registry secret", p.Namespace) + log.Debugf("Integration Platform %s [%s]: setting registry secret", p.Name, p.Namespace) // Bind the required role to push images to the registry err := createBuilderRegistryRoleBinding(ctx, c, p) if err != nil { @@ -179,25 +184,142 @@ func configureRegistry(ctx context.Context, c client.Client, p *v1.IntegrationPl return nil } +func applyGlobalPlatformDefaults(ctx context.Context, c client.Client, p *v1.IntegrationPlatform) error { + operatorNamespace := GetOperatorNamespace() + if operatorNamespace != "" && operatorNamespace != p.Namespace { + operatorID := defaults.OperatorID() + if operatorID != "" { + if globalPlatform, err := get(ctx, c, operatorNamespace, operatorID); err != nil && !k8serrors.IsNotFound(err) { + return err + } else if globalPlatform != nil { + applyPlatformSpec(globalPlatform, p) + return nil + } + } + + if globalPlatform, err := findLocal(ctx, c, operatorNamespace, true); err != nil && !k8serrors.IsNotFound(err) { + return err + } else if globalPlatform != nil { + applyPlatformSpec(globalPlatform, p) + } + } + + return nil +} + +func applyPlatformSpec(source *v1.IntegrationPlatform, target *v1.IntegrationPlatform) { + if target.Status.Cluster == "" { + target.Status.Cluster = source.Status.Cluster + } + if target.Status.Profile == "" { + log.Debugf("Integration Platform %s [%s]: setting profile", target.Name, target.Namespace) + target.Status.Profile = source.Status.Profile + } + + if target.Status.Build.PublishStrategy == "" { + target.Status.Build.PublishStrategy = source.Status.Build.PublishStrategy + } + if target.Status.Build.PublishStrategyOptions == nil { + log.Debugf("Integration Platform %s [%s]: setting publish strategy options", target.Name, target.Namespace) + target.Status.Build.PublishStrategyOptions = source.Status.Build.PublishStrategyOptions + } + if target.Status.Build.BuildStrategy == "" { + target.Status.Build.BuildStrategy = source.Status.Build.BuildStrategy + } + + if target.Status.Build.RuntimeVersion == "" { + log.Debugf("Integration Platform %s [%s]: setting runtime version", target.Name, target.Namespace) + target.Status.Build.RuntimeVersion = source.Status.Build.RuntimeVersion + } + if target.Status.Build.BaseImage == "" { + log.Debugf("Integration Platform %s [%s]: setting base image", target.Name, target.Namespace) + target.Status.Build.BaseImage = source.Status.Build.BaseImage + } + + if target.Status.Build.Maven.LocalRepository == "" { + log.Debugf("Integration Platform %s [%s]: setting local repository", target.Name, target.Namespace) + target.Status.Build.Maven.LocalRepository = source.Status.Build.Maven.LocalRepository + } + + if len(source.Status.Build.Maven.CLIOptions) > 0 && len(target.Status.Build.Maven.CLIOptions) == 0 { + log.Debugf("Integration Platform %s [%s]: setting CLI options", target.Name, target.Namespace) + target.Status.Build.Maven.CLIOptions = make([]string, len(source.Status.Build.Maven.CLIOptions)) + copy(target.Status.Build.Maven.CLIOptions, source.Status.Build.Maven.CLIOptions) + } + + if len(source.Status.Build.Maven.Properties) > 0 { + log.Debugf("Integration Platform %s [%s]: setting Maven properties", target.Name, target.Namespace) + if len(target.Status.Build.Maven.Properties) == 0 { + target.Status.Build.Maven.Properties = make(map[string]string, len(source.Status.Build.Maven.Properties)) + } + + for key, val := range source.Status.Build.Maven.Properties { + // only set unknown properties on target + if _, ok := target.Status.Build.Maven.Properties[key]; !ok { + target.Status.Build.Maven.Properties[key] = val + } + } + } + + if len(source.Status.Build.Maven.Extension) > 0 && len(target.Status.Build.Maven.Extension) == 0 { + log.Debugf("Integration Platform %s [%s]: setting Maven extensions", target.Name, target.Namespace) + target.Status.Build.Maven.Extension = make([]v1.MavenArtifact, len(source.Status.Build.Maven.Extension)) + copy(target.Status.Build.Maven.Extension, source.Status.Build.Maven.Extension) + } + + if target.Status.Build.Registry.Address == "" && source.Status.Build.Registry.Address != "" { + log.Debugf("Integration Platform %s [%s]: setting registry", target.Name, target.Namespace) + source.Status.Build.Registry.DeepCopyInto(&target.Status.Build.Registry) + } + + if err := target.Status.Traits.Merge(source.Status.Traits); err != nil { + log.Errorf(err, "Integration Platform %s [%s]: failed to merge traits", target.Name, target.Namespace) + } else if err := target.Status.Traits.Merge(target.Spec.Traits); err != nil { + log.Errorf(err, "Integration Platform %s [%s]: failed to merge traits", target.Name, target.Namespace) + } + + // Build timeout + if target.Status.Build.Timeout == nil { + log.Debugf("Integration Platform %s [%s]: setting build timeout", target.Name, target.Namespace) + target.Status.Build.Timeout = source.Status.Build.Timeout + } + + // Catalog tools build timeout + if target.Status.Build.BuildCatalogToolTimeout == nil { + log.Debugf("Integration Platform %s [%s]: setting build camel catalog tool timeout", target.Name, target.Namespace) + target.Status.Build.BuildCatalogToolTimeout = source.Status.Build.BuildCatalogToolTimeout + } + + if target.Status.Build.MaxRunningBuilds <= 0 { + log.Debugf("Integration Platform %s [%s]: setting max running builds", target.Name, target.Namespace) + target.Status.Build.MaxRunningBuilds = source.Status.Build.MaxRunningBuilds + } + + if len(target.Status.Kamelet.Repositories) == 0 { + log.Debugf("Integration Platform %s [%s]: setting kamelet repositories", target.Name, target.Namespace) + target.Status.Kamelet.Repositories = source.Status.Kamelet.Repositories + } +} + func setPlatformDefaults(p *v1.IntegrationPlatform, verbose bool) error { if p.Status.Build.PublishStrategyOptions == nil { - log.Debugf("Integration Platform [%s]: setting publish strategy options", p.Namespace) + log.Debugf("Integration Platform %s [%s]: setting publish strategy options", p.Name, p.Namespace) p.Status.Build.PublishStrategyOptions = map[string]string{} } if p.Status.Build.RuntimeVersion == "" { - log.Debugf("Integration Platform [%s]: setting runtime version", p.Namespace) + log.Debugf("Integration Platform %s [%s]: setting runtime version", p.Name, p.Namespace) p.Status.Build.RuntimeVersion = defaults.DefaultRuntimeVersion } if p.Status.Build.BaseImage == "" { - log.Debugf("Integration Platform [%s]: setting base image", p.Namespace) + log.Debugf("Integration Platform %s [%s]: setting base image", p.Name, p.Namespace) p.Status.Build.BaseImage = defaults.BaseImage() } if p.Status.Build.Maven.LocalRepository == "" { - log.Debugf("Integration Platform [%s]: setting local repository", p.Namespace) + log.Debugf("Integration Platform %s [%s]: setting local repository", p.Name, p.Namespace) p.Status.Build.Maven.LocalRepository = defaults.LocalRepository } if len(p.Status.Build.Maven.CLIOptions) == 0 { - log.Debugf("Integration Platform [%s]: setting CLI options", p.Namespace) + log.Debugf("Integration Platform %s [%s]: setting CLI options", p.Name, p.Namespace) p.Status.Build.Maven.CLIOptions = []string{ "-V", "--no-transfer-progress", @@ -205,7 +327,7 @@ func setPlatformDefaults(p *v1.IntegrationPlatform, verbose bool) error { } } if _, ok := p.Status.Build.PublishStrategyOptions[builder.KanikoPVCName]; !ok { - log.Debugf("Integration Platform [%s]: setting publish strategy options", p.Namespace) + log.Debugf("Integration Platform %s [%s]: setting publish strategy options", p.Name, p.Namespace) p.Status.Build.PublishStrategyOptions[builder.KanikoPVCName] = p.Name } @@ -221,7 +343,7 @@ func setPlatformDefaults(p *v1.IntegrationPlatform, verbose bool) error { log.Log.Infof("Build timeout minimum unit is sec (configured: %s, truncated: %s)", p.Status.Build.GetTimeout().Duration, d) } - log.Debugf("Integration Platform [%s]: setting build timeout", p.Namespace) + log.Debugf("Integration Platform %s [%s]: setting build timeout", p.Name, p.Namespace) p.Status.Build.Timeout = &metav1.Duration{ Duration: d, } @@ -229,7 +351,7 @@ func setPlatformDefaults(p *v1.IntegrationPlatform, verbose bool) error { // Catalog tools build timeout if p.Status.Build.GetBuildCatalogToolTimeout().Duration == 0 { - log.Debugf("Integration Platform [%s]: setting default build camel catalog tool timeout (1 minute)", p.Namespace) + log.Debugf("Integration Platform %s [%s]: setting default build camel catalog tool timeout (1 minute)", p.Name, p.Namespace) p.Status.Build.BuildCatalogToolTimeout = &metav1.Duration{ Duration: 1 * time.Minute, } @@ -240,13 +362,14 @@ func setPlatformDefaults(p *v1.IntegrationPlatform, verbose bool) error { log.Log.Infof("Build catalog tools timeout minimum unit is sec (configured: %s, truncated: %s)", p.Status.Build.GetBuildCatalogToolTimeout().Duration, d) } - log.Debugf("Integration Platform [%s]: setting build catalog tools timeout", p.Namespace) + log.Debugf("Integration Platform %s [%s]: setting build catalog tools timeout", p.Name, p.Namespace) p.Status.Build.BuildCatalogToolTimeout = &metav1.Duration{ Duration: d, } } if p.Status.Build.MaxRunningBuilds <= 0 { + log.Debugf("Integration Platform %s [%s]: setting max running builds", p.Name, p.Namespace) if p.Status.Build.BuildStrategy == v1.BuildStrategyRoutine { p.Status.Build.MaxRunningBuilds = 3 } else if p.Status.Build.BuildStrategy == v1.BuildStrategyPod { @@ -267,7 +390,7 @@ func setPlatformDefaults(p *v1.IntegrationPlatform, verbose bool) error { } if len(p.Status.Kamelet.Repositories) == 0 { - log.Debugf("Integration Platform [%s]: setting kamelet repositories", p.Namespace) + log.Debugf("Integration Platform %s [%s]: setting kamelet repositories", p.Name, p.Namespace) p.Status.Kamelet.Repositories = append(p.Status.Kamelet.Repositories, v1.IntegrationPlatformKameletRepositorySpec{ URI: repository.DefaultRemoteRepository, }) @@ -287,13 +410,13 @@ func setPlatformDefaults(p *v1.IntegrationPlatform, verbose bool) error { func setStatusAdditionalInfo(platform *v1.IntegrationPlatform) { platform.Status.Info = make(map[string]string) - log.Debugf("Integration Platform [%s]: setting build publish strategy", platform.Namespace) + log.Debugf("Integration Platform %s [%s]: setting build publish strategy", platform.Name, platform.Namespace) if platform.Spec.Build.PublishStrategy == v1.IntegrationPlatformBuildPublishStrategyBuildah { platform.Status.Info["buildahVersion"] = defaults.BuildahVersion } else if platform.Spec.Build.PublishStrategy == v1.IntegrationPlatformBuildPublishStrategyKaniko { platform.Status.Info["kanikoVersion"] = defaults.KanikoVersion } - log.Debugf("Integration Platform [%s]: setting status info", platform.Namespace) + log.Debugf("Integration Platform %s [%s]: setting status info", platform.Name, platform.Namespace) platform.Status.Info["goVersion"] = runtime.Version() platform.Status.Info["goOS"] = runtime.GOOS platform.Status.Info["gitCommit"] = defaults.GitCommit diff --git a/pkg/platform/defaults_test.go b/pkg/platform/defaults_test.go new file mode 100644 index 0000000000..d5754ba1c5 --- /dev/null +++ b/pkg/platform/defaults_test.go @@ -0,0 +1,187 @@ +/* +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. +*/ + +package platform + +import ( + "context" + "testing" + + "github.com/stretchr/testify/assert" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + v1 "github.com/apache/camel-k/v2/pkg/apis/camel/v1" + "github.com/apache/camel-k/v2/pkg/apis/camel/v1/trait" + "github.com/apache/camel-k/v2/pkg/util/test" +) + +func TestApplyGlobalPlatformSpec(t *testing.T) { + global := v1.IntegrationPlatform{ + ObjectMeta: metav1.ObjectMeta{ + Name: DefaultPlatformName, + Namespace: "ns", + }, + Spec: v1.IntegrationPlatformSpec{ + Build: v1.IntegrationPlatformBuildSpec{ + BuildStrategy: v1.BuildStrategyRoutine, + Maven: v1.MavenSpec{ + Properties: map[string]string{ + "global_prop1": "global_value1", + "global_prop2": "global_value2", + }, + }, + }, + Traits: v1.Traits{ + Logging: &trait.LoggingTrait{ + Level: "DEBUG", + }, + Container: &trait.ContainerTrait{ + ImagePullPolicy: corev1.PullAlways, + LimitCPU: "0.1", + }, + }, + Cluster: v1.IntegrationPlatformClusterOpenShift, + Profile: v1.TraitProfileOpenShift, + }, + } + + c, err := test.NewFakeClient(&global) + assert.Nil(t, err) + + err = ConfigureDefaults(context.TODO(), c, &global, false) + assert.Nil(t, err) + + ip := v1.IntegrationPlatform{ + ObjectMeta: metav1.ObjectMeta{ + Name: "local-camel-k", + Namespace: "ns", + }, + } + + ip.ResyncStatusFullConfig() + + applyPlatformSpec(&global, &ip) + + assert.Equal(t, v1.IntegrationPlatformClusterOpenShift, ip.Status.Cluster) + assert.Equal(t, v1.TraitProfileOpenShift, ip.Status.Profile) + assert.Equal(t, v1.BuildStrategyRoutine, ip.Status.Build.BuildStrategy) + + assert.True(t, ip.Status.Build.MaxRunningBuilds == 3) // default for build strategy routine + + assert.Equal(t, len(global.Status.Build.Maven.CLIOptions), len(ip.Status.Build.Maven.CLIOptions)) + assert.Equal(t, global.Status.Build.Maven.CLIOptions, ip.Status.Build.Maven.CLIOptions) + + assert.NotNil(t, ip.Status.Traits) + assert.NotNil(t, ip.Status.Traits.Logging) + assert.Equal(t, "DEBUG", ip.Status.Traits.Logging.Level) + assert.NotNil(t, ip.Status.Traits.Container) + assert.Equal(t, corev1.PullAlways, ip.Status.Traits.Container.ImagePullPolicy) + assert.Equal(t, "0.1", ip.Status.Traits.Container.LimitCPU) + + assert.Equal(t, 2, len(ip.Status.Build.Maven.Properties)) + assert.Equal(t, "global_value1", ip.Status.Build.Maven.Properties["global_prop1"]) + assert.Equal(t, "global_value2", ip.Status.Build.Maven.Properties["global_prop2"]) +} + +func TestRetainLocalPlatformSpec(t *testing.T) { + global := v1.IntegrationPlatform{ + ObjectMeta: metav1.ObjectMeta{ + Name: DefaultPlatformName, + Namespace: "ns", + }, + Spec: v1.IntegrationPlatformSpec{ + Build: v1.IntegrationPlatformBuildSpec{ + BuildStrategy: v1.BuildStrategyRoutine, + Maven: v1.MavenSpec{ + Properties: map[string]string{ + "global_prop1": "global_value1", + "global_prop2": "global_value2", + }, + }, + }, + Traits: v1.Traits{ + Logging: &trait.LoggingTrait{ + Level: "DEBUG", + }, + Container: &trait.ContainerTrait{ + ImagePullPolicy: corev1.PullIfNotPresent, + LimitCPU: "0.1", + }, + }, + Cluster: v1.IntegrationPlatformClusterOpenShift, + Profile: v1.TraitProfileOpenShift, + }, + } + + c, err := test.NewFakeClient(&global) + assert.Nil(t, err) + + err = ConfigureDefaults(context.TODO(), c, &global, false) + assert.Nil(t, err) + + ip := v1.IntegrationPlatform{ + ObjectMeta: metav1.ObjectMeta{ + Name: "local-camel-k", + Namespace: "ns", + }, + Spec: v1.IntegrationPlatformSpec{ + Build: v1.IntegrationPlatformBuildSpec{ + BuildStrategy: v1.BuildStrategyPod, + MaxRunningBuilds: 1, + Maven: v1.MavenSpec{ + Properties: map[string]string{ + "local_prop1": "local_value1", + "global_prop2": "local_value2", + }, + }, + }, + Traits: v1.Traits{ + Container: &trait.ContainerTrait{ + ImagePullPolicy: corev1.PullAlways, + }, + }, + Cluster: v1.IntegrationPlatformClusterKubernetes, + Profile: v1.TraitProfileKnative, + }, + } + + ip.ResyncStatusFullConfig() + + applyPlatformSpec(&global, &ip) + + assert.Equal(t, v1.IntegrationPlatformClusterKubernetes, ip.Status.Cluster) + assert.Equal(t, v1.TraitProfileKnative, ip.Status.Profile) + assert.Equal(t, v1.BuildStrategyPod, ip.Status.Build.BuildStrategy) + + assert.True(t, ip.Status.Build.MaxRunningBuilds == 1) + + assert.Equal(t, len(global.Status.Build.Maven.CLIOptions), len(ip.Status.Build.Maven.CLIOptions)) + assert.Equal(t, global.Status.Build.Maven.CLIOptions, ip.Status.Build.Maven.CLIOptions) + + assert.NotNil(t, ip.Status.Traits) + assert.NotNil(t, ip.Status.Traits.Logging) + assert.Equal(t, "DEBUG", ip.Status.Traits.Logging.Level) + assert.NotNil(t, ip.Status.Traits.Container) + assert.Equal(t, corev1.PullAlways, ip.Status.Traits.Container.ImagePullPolicy) + assert.Equal(t, "0.1", ip.Status.Traits.Container.LimitCPU) + + assert.Equal(t, 3, len(ip.Status.Build.Maven.Properties)) + assert.Equal(t, "global_value1", ip.Status.Build.Maven.Properties["global_prop1"]) + assert.Equal(t, "local_value2", ip.Status.Build.Maven.Properties["global_prop2"]) + assert.Equal(t, "local_value1", ip.Status.Build.Maven.Properties["local_prop1"]) +}