Repository: apache/camel-k
Pull request #1127 (2019-12-09 14:12:26)

Don't write to platform spec to allow upgrades
<!-- Description -->
Without this, upgrades to newer versions are not possible, as we used to encode even defaults in the platform specs, but defaults change in every new version (e.g. for instance the default Camel version).

I've moved the full platform config in a `fullConfig` field in the `status`, to allow it to be inspected and at the same time to allow the users to set their preference only on what matters for them.

A typical platform on Minikube now looks like:

```
apiVersion: camel.apache.org/v1alpha1
kind: IntegrationPlatform
metadata:
  creationTimestamp: "2019-12-09T13:56:32Z"
  generation: 1
  labels:
    app: camel-k
  name: camel-k
  namespace: default
  resourceVersion: "562760"
  selfLink: /apis/camel.apache.org/v1alpha1/namespaces/default/integrationplatforms/camel-k
  uid: b4b273c5-1a8b-11ea-8c9d-5cdf4ec533fd
spec:
  build:
    maven:
      settings: {}
    registry:
      address: 10.96.56.193
      insecure: true
  resources: {}
status:
  fullConfig:
    build:
      baseImage: fabric8/s2i-java:3.0-java8
      buildStrategy: pod
      camelVersion: '>=3.0.0-RC3'
      kanikoBuildCache: true
      maven:
        localRepository: /tmp/artifacts/m2
        settings:
          configMapKeyRef:
            key: settings.xml
            name: camel-k-maven-settings
        timeout: 3m45s
      persistentVolumeClaim: camel-k
      publishStrategy: Kaniko
      registry:
        address: 10.96.56.193
        insecure: true
      runtimeVersion: '>=1.0.7'
      timeout: 5m0s
    cluster: Kubernetes
    profile: Knative
  phase: Warming
  version: 1.0.0-M5-SNAPSHOT
```

Note that only the relevant information (registry address and kind) are present in spec, all other values are computed in the status.

The `fullConfig` is kept in sync by the platform controller.

<!--
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
IntegrationPlatform specification does no longer contain default values in order to ease upgrades
```


astefanutti: @nicolaferraro, just thinking out loud, would using the `kubectl.kubernetes.io/last-applied-configuration` annotation or something equivalent be a way to transport user defined configuration, e.g., when creating the platform with the `apply` sub-command:

```
apiVersion: camel.apache.org/v1alpha1
kind: IntegrationPlatform
metadata:
  annotations:
    kubectl.kubernetes.io/last-applied-configuration: |
      {"apiVersion":"camel.apache.org/v1alpha1","kind":"IntegrationPlatform","metadata":{"annotations":{},"labels":{"app":"camel-k"},"name":"camel-k","namespace":"camel-k"}}
```

nicolaferraro: Yeah, I've thought to the last-applied-config as well, but semantically in Kube it serves to a different purpose. Having it as an annotation also make it difficult to understand the content when you inspect it. 

It's also difficult, in case one changes with `k edit integrationplatform` some fields in the spec, when you get to reconcile it, to tell if a value in spec was set by the user or by our initialization code.

I think we've done a similar thing to this PR also in Integration (e.g. you see many fields, like dependencies in both `status` and `spec`). Instead of having a `fullConfig` field, which seems odd, I was thinking to move the fields one level up. So that we have e.g. spec->build->baseImage and status->build->baseImage which seems close to what we've already done in integration and feels more natural.
astefanutti: > I think we've done a similar thing to this PR also in Integration (e.g. you see many fields, like dependencies in both status and spec). Instead of having a fullConfig field, which seems odd, I was thinking to move the fields one level up. So that we have e.g. spec->build->baseImage and status->build->baseImage which seems close to what we've already done in integration and feels more natural.

Totally agree with this.
nicolaferraro: Looks much nicer now:

```
apiVersion: camel.apache.org/v1alpha1
kind: IntegrationPlatform
metadata:
  creationTimestamp: "2019-12-10T10:24:47Z"
  generation: 1
  labels:
    app: camel-k
  name: camel-k
  namespace: default
  resourceVersion: "655195"
  selfLink: /apis/camel.apache.org/v1alpha1/namespaces/default/integrationplatforms/camel-k
  uid: 4a4e7727-1b37-11ea-8c9d-5cdf4ec533fd
spec:
  build:
    maven:
      settings: {}
    registry:
      address: 10.96.56.193
      insecure: true
  resources: {}
status:
  build:
    baseImage: fabric8/s2i-java:3.0-java8
    buildStrategy: pod
    camelVersion: '>=3.0.0-RC3'
    kanikoBuildCache: true
    maven:
      localRepository: /tmp/artifacts/m2
      settings:
        configMapKeyRef:
          key: settings.xml
          name: camel-k-maven-settings
      timeout: 3m45s
    persistentVolumeClaim: camel-k
    publishStrategy: Kaniko
    registry:
      address: 10.96.56.193
      insecure: true
    runtimeVersion: '>=1.0.7'
    timeout: 5m0s
  cluster: Kubernetes
  phase: Warming
  profile: Knative
  version: 1.0.0-M5-SNAPSHOT
```
astefanutti: LGTM!

Patch:
diff --git a/pkg/apis/camel/v1alpha1/common_types.go b/pkg/apis/camel/v1alpha1/common_types.go
index f54c19c30..c3472f6bd 100644
--- a/pkg/apis/camel/v1alpha1/common_types.go
+++ b/pkg/apis/camel/v1alpha1/common_types.go
@@ -77,9 +77,9 @@ type PlatformInjectable interface {
 
 // MavenSpec --
 type MavenSpec struct {
-	LocalRepository string          `json:"localRepository,omitempty"`
-	Settings        ValueSource     `json:"settings,omitempty"`
-	Timeout         metav1.Duration `json:"timeout,omitempty"`
+	LocalRepository string           `json:"localRepository,omitempty"`
+	Settings        ValueSource      `json:"settings,omitempty"`
+	Timeout         *metav1.Duration `json:"timeout,omitempty"`
 }
 
 // ValueSource --
@@ -87,7 +87,7 @@ type ValueSource struct {
 	// Selects a key of a ConfigMap.
 	ConfigMapKeyRef *corev1.ConfigMapKeySelector `json:"configMapKeyRef,omitempty"`
 	// Selects a key of a secret.
-	SecretKeyRef *corev1.SecretKeySelector `json:"secretKeyRef,omitempty" `
+	SecretKeyRef *corev1.SecretKeySelector `json:"secretKeyRef,omitempty"`
 }
 
 const (
diff --git a/pkg/apis/camel/v1alpha1/integrationplatform_types.go b/pkg/apis/camel/v1alpha1/integrationplatform_types.go
index 72aaea779..67ebe24c1 100644
--- a/pkg/apis/camel/v1alpha1/integrationplatform_types.go
+++ b/pkg/apis/camel/v1alpha1/integrationplatform_types.go
@@ -41,6 +41,8 @@ type IntegrationPlatformResourcesSpec struct {
 
 // IntegrationPlatformStatus defines the observed state of IntegrationPlatform
 type IntegrationPlatformStatus struct {
+	IntegrationPlatformSpec `json:",inline"`
+
 	Phase      IntegrationPlatformPhase       `json:"phase,omitempty"`
 	Conditions []IntegrationPlatformCondition `json:"conditions,omitempty"`
 	Version    string                         `json:"version,omitempty"`
@@ -108,7 +110,7 @@ type IntegrationPlatformBuildSpec struct {
 	BaseImage             string                                  `json:"baseImage,omitempty"`
 	Properties            map[string]string                       `json:"properties,omitempty"`
 	Registry              IntegrationPlatformRegistrySpec         `json:"registry,omitempty"`
-	Timeout               metav1.Duration                         `json:"timeout,omitempty"`
+	Timeout               *metav1.Duration                        `json:"timeout,omitempty"`
 	PersistentVolumeClaim string                                  `json:"persistentVolumeClaim,omitempty"`
 	Maven                 MavenSpec                               `json:"maven,omitempty"`
 	HTTPProxySecret       string                                  `json:"httpProxySecret,omitempty"`
diff --git a/pkg/apis/camel/v1alpha1/integrationplatform_types_support.go b/pkg/apis/camel/v1alpha1/integrationplatform_types_support.go
index 1edbdbdfe..d85cc34c4 100644
--- a/pkg/apis/camel/v1alpha1/integrationplatform_types_support.go
+++ b/pkg/apis/camel/v1alpha1/integrationplatform_types_support.go
@@ -73,6 +73,10 @@ func (in *IntegrationPlatform) Configurations() []ConfigurationSpec {
 		return []ConfigurationSpec{}
 	}
 
+	if len(in.Status.Configuration) > 0 {
+		return in.Status.Configuration
+	}
+
 	return in.Spec.Configuration
 }
 
@@ -84,6 +88,21 @@ func (in *IntegrationPlatform) AddConfiguration(confType string, confValue strin
 	})
 }
 
+// GetActualValue can be used to extract information the platform spec or its derived config in the status
+func (in *IntegrationPlatform) GetActualValue(extractor func(spec IntegrationPlatformSpec) string) string {
+	res := extractor(in.Status.IntegrationPlatformSpec)
+	if res == "" {
+		res = extractor(in.Spec)
+	}
+	return res
+}
+
+// ResyncStatusFullConfig copies the spec configuration into the status-fullConfig field.
+func (in *IntegrationPlatform) ResyncStatusFullConfig() {
+	cl := in.Spec.DeepCopy()
+	in.Status.IntegrationPlatformSpec = *cl
+}
+
 // GetCondition returns the condition with the provided type.
 func (in *IntegrationPlatformStatus) GetCondition(condType IntegrationPlatformConditionType) *IntegrationPlatformCondition {
 	for i := range in.Conditions {
@@ -167,3 +186,19 @@ func (b IntegrationPlatformBuildSpec) IsKanikoCacheEnabled() bool {
 	}
 	return *b.KanikoBuildCache
 }
+
+// GetTimeout returns the specified duration or a default one
+func (b IntegrationPlatformBuildSpec) GetTimeout() metav1.Duration {
+	if b.Timeout == nil {
+		return metav1.Duration{}
+	}
+	return *b.Timeout
+}
+
+// GetTimeout returns the specified duration or a default one
+func (m MavenSpec) GetTimeout() metav1.Duration {
+	if m.Timeout == nil {
+		return metav1.Duration{}
+	}
+	return *m.Timeout
+}
diff --git a/pkg/builder/runtime/main.go b/pkg/builder/runtime/main.go
index 4f2cb3e9b..a191afd03 100644
--- a/pkg/builder/runtime/main.go
+++ b/pkg/builder/runtime/main.go
@@ -87,7 +87,7 @@ func computeDependencies(ctx *builder.Context) error {
 	mc := maven.NewContext(path.Join(ctx.Path, "maven"), ctx.Maven.Project)
 	mc.SettingsContent = ctx.Maven.SettingsData
 	mc.LocalRepository = ctx.Build.Platform.Build.Maven.LocalRepository
-	mc.Timeout = ctx.Build.Platform.Build.Maven.Timeout.Duration
+	mc.Timeout = ctx.Build.Platform.Build.Maven.GetTimeout().Duration
 	mc.AddArgumentf("org.apache.camel.k:camel-k-maven-plugin:%s:generate-dependency-list", ctx.Catalog.RuntimeVersion)
 
 	if err := maven.Run(mc); err != nil {
diff --git a/pkg/builder/runtime/quarkus.go b/pkg/builder/runtime/quarkus.go
index bd6d92a3e..f9c980902 100644
--- a/pkg/builder/runtime/quarkus.go
+++ b/pkg/builder/runtime/quarkus.go
@@ -107,7 +107,7 @@ func computeQuarkusDependencies(ctx *builder.Context) error {
 	mc := maven.NewContext(path.Join(ctx.Path, "maven"), ctx.Maven.Project)
 	mc.SettingsContent = ctx.Maven.SettingsData
 	mc.LocalRepository = ctx.Build.Platform.Build.Maven.LocalRepository
-	mc.Timeout = ctx.Build.Platform.Build.Maven.Timeout.Duration
+	mc.Timeout = ctx.Build.Platform.Build.Maven.GetTimeout().Duration
 
 	// Build the project
 	mc.AddArgument("package")
diff --git a/pkg/builder/s2i/publisher.go b/pkg/builder/s2i/publisher.go
index 9eadd472c..eb8c59885 100644
--- a/pkg/builder/s2i/publisher.go
+++ b/pkg/builder/s2i/publisher.go
@@ -156,7 +156,7 @@ func publisher(ctx *builder.Context) error {
 			}
 		}
 		return false, nil
-	}, ctx.Build.Platform.Build.Timeout.Duration)
+	}, ctx.Build.Platform.Build.GetTimeout().Duration)
 
 	if err != nil {
 		return err
diff --git a/pkg/cmd/describe_platform.go b/pkg/cmd/describe_platform.go
index 58e74a1fd..162a7a0b9 100644
--- a/pkg/cmd/describe_platform.go
+++ b/pkg/cmd/describe_platform.go
@@ -90,17 +90,37 @@ func (command *describePlatformCommand) describeIntegrationPlatform(platform v1a
 		describeObjectMeta(w, platform.ObjectMeta)
 		w.Write(0, "Phase:\t%s\n", platform.Status.Phase)
 		w.Write(0, "Version:\t%s\n", platform.Status.Version)
-		w.Write(0, "Base Image:\t%s\n", platform.Spec.Build.BaseImage)
-		w.Write(0, "Camel Version:\t%s\n", platform.Spec.Build.CamelVersion)
-		w.Write(0, "Local Repository:\t%s\n", platform.Spec.Build.Maven.LocalRepository)
-		w.Write(0, "Publish Strategy:\t%s\n", platform.Spec.Build.PublishStrategy)
-
-		if len(platform.Spec.Resources.Kits) > 0 {
+		w.Write(0, "Base Image:\t%s\n", platform.GetActualValue(getPlatformBaseImage))
+		w.Write(0, "Camel Version:\t%s\n", platform.GetActualValue(getPlatformCamelVersion))
+		w.Write(0, "Local Repository:\t%s\n", platform.GetActualValue(getPlatformMavenLocalRepository))
+		w.Write(0, "Publish Strategy:\t%s\n", platform.GetActualValue(getPlatformPublishStrategy))
+
+		kits := platform.Status.Resources.Kits
+		if len(kits) == 0 {
+			kits = platform.Spec.Resources.Kits
+		}
+		if len(kits) > 0 {
 			w.Write(0, "Resources:\n")
 			w.Write(1, "Kits:\n")
-			for _, kit := range platform.Spec.Resources.Kits {
+			for _, kit := range kits {
 				w.Write(2, "%s\n", kit)
 			}
 		}
 	})
 }
+
+func getPlatformBaseImage(spec v1alpha1.IntegrationPlatformSpec) string {
+	return spec.Build.BaseImage
+}
+
+func getPlatformCamelVersion(spec v1alpha1.IntegrationPlatformSpec) string {
+	return spec.Build.CamelVersion
+}
+
+func getPlatformMavenLocalRepository(spec v1alpha1.IntegrationPlatformSpec) string {
+	return spec.Build.Maven.LocalRepository
+}
+
+func getPlatformPublishStrategy(spec v1alpha1.IntegrationPlatformSpec) string {
+	return string(spec.Build.PublishStrategy)
+}
diff --git a/pkg/cmd/install.go b/pkg/cmd/install.go
index c3af624f8..56f8c33aa 100644
--- a/pkg/cmd/install.go
+++ b/pkg/cmd/install.go
@@ -25,6 +25,7 @@ import (
 
 	"github.com/apache/camel-k/pkg/util/registry"
 	"go.uber.org/multierr"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/apimachinery/pkg/runtime"
 
 	"github.com/apache/camel-k/deploy"
@@ -235,7 +236,9 @@ func (o *installCmdOptions) install(cobraCmd *cobra.Command, _ []string) error {
 				return err
 			}
 
-			platform.Spec.Build.Timeout.Duration = d
+			platform.Spec.Build.Timeout = &metav1.Duration{
+				Duration: d,
+			}
 		}
 		if o.traitProfile != "" {
 			platform.Spec.Profile = v1alpha1.TraitProfileByName(o.traitProfile)
@@ -269,12 +272,8 @@ func (o *installCmdOptions) install(cobraCmd *cobra.Command, _ []string) error {
 
 		kanikoBuildCacheFlag := cobraCmd.Flags().Lookup("kaniko-build-cache")
 
-		defaultKanikoBuildCache := true
-
 		if kanikoBuildCacheFlag.Changed {
 			platform.Spec.Build.KanikoBuildCache = &o.kanikoBuildCache
-		} else {
-			platform.Spec.Build.KanikoBuildCache = &defaultKanikoBuildCache
 		}
 
 		platform.Spec.Resources.Kits = o.kits
diff --git a/pkg/controller/integrationkit/build.go b/pkg/controller/integrationkit/build.go
index a7f2319b2..ce430689d 100644
--- a/pkg/controller/integrationkit/build.go
+++ b/pkg/controller/integrationkit/build.go
@@ -96,7 +96,7 @@ func (action *buildAction) handleBuildSubmitted(ctx context.Context, kit *v1alph
 				CamelVersion:    env.CamelCatalog.Version,
 				RuntimeVersion:  env.CamelCatalog.RuntimeVersion,
 				RuntimeProvider: env.CamelCatalog.RuntimeProvider,
-				Platform:        env.Platform.Spec,
+				Platform:        env.Platform.Status.IntegrationPlatformSpec,
 				Dependencies:    kit.Spec.Dependencies,
 				// TODO: sort for easy read
 				Steps:    builder.StepIDsFor(env.Steps...),
diff --git a/pkg/controller/integrationplatform/create.go b/pkg/controller/integrationplatform/create.go
index 5fccaa75f..6bbe14fee 100644
--- a/pkg/controller/integrationplatform/create.go
+++ b/pkg/controller/integrationplatform/create.go
@@ -56,10 +56,10 @@ func (action *createAction) Handle(ctx context.Context, platform *v1alpha1.Integ
 		}
 	}
 
-	if l := len(platform.Spec.Resources.Kits); l > 0 {
+	if l := len(platform.Status.Resources.Kits); l > 0 {
 		res := make([]string, 0, l)
 
-		for _, c := range platform.Spec.Resources.Kits {
+		for _, c := range platform.Status.Resources.Kits {
 			//
 			// Assuming that if the resource ends with a yaml extension, the full
 			// resource name is provided
diff --git a/pkg/controller/integrationplatform/initialize.go b/pkg/controller/integrationplatform/initialize.go
index 663a3b52b..ce90c2e11 100644
--- a/pkg/controller/integrationplatform/initialize.go
+++ b/pkg/controller/integrationplatform/initialize.go
@@ -19,10 +19,6 @@ package integrationplatform
 
 import (
 	"context"
-	"fmt"
-	"time"
-
-	"github.com/apache/camel-k/pkg/util/maven"
 
 	corev1 "k8s.io/api/core/v1"
 	k8serrors "k8s.io/apimachinery/pkg/api/errors"
@@ -31,10 +27,8 @@ import (
 
 	"github.com/apache/camel-k/pkg/apis/camel/v1alpha1"
 	"github.com/apache/camel-k/pkg/client"
-	"github.com/apache/camel-k/pkg/util/defaults"
-	"github.com/apache/camel-k/pkg/util/openshift"
-
 	platformutil "github.com/apache/camel-k/pkg/platform"
+	"github.com/apache/camel-k/pkg/util/defaults"
 )
 
 // NewInitializeAction returns a action that initializes the platform configuration when not provided by the user
@@ -71,62 +65,11 @@ func (action *initializeAction) Handle(ctx context.Context, platform *v1alpha1.I
 		return nil, nil
 	}
 
-	// update missing fields in the resource
-	if platform.Spec.Cluster == "" {
-		// determine the kind of cluster the platform is installed into
-		isOpenShift, err := openshift.IsOpenShift(action.client)
-		switch {
-		case err != nil:
-			return nil, err
-		case isOpenShift:
-			platform.Spec.Cluster = v1alpha1.IntegrationPlatformClusterOpenShift
-		default:
-			platform.Spec.Cluster = v1alpha1.IntegrationPlatformClusterKubernetes
-		}
-	}
-
-	if platform.Spec.Build.PublishStrategy == "" {
-		if platform.Spec.Cluster == v1alpha1.IntegrationPlatformClusterOpenShift {
-			platform.Spec.Build.PublishStrategy = v1alpha1.IntegrationPlatformBuildPublishStrategyS2I
-		} else {
-			platform.Spec.Build.PublishStrategy = v1alpha1.IntegrationPlatformBuildPublishStrategyKaniko
-		}
-	}
-
-	if platform.Spec.Build.BuildStrategy == "" {
-		// If the operator is global, a global build strategy should be used
-		if platformutil.IsCurrentOperatorGlobal() {
-			// The only global strategy we have for now
-			platform.Spec.Build.BuildStrategy = v1alpha1.IntegrationPlatformBuildStrategyPod
-		} else {
-			if platform.Spec.Build.PublishStrategy == v1alpha1.IntegrationPlatformBuildPublishStrategyKaniko {
-				// The build output has to be shared with Kaniko via a persistent volume
-				platform.Spec.Build.BuildStrategy = v1alpha1.IntegrationPlatformBuildStrategyPod
-			} else {
-				platform.Spec.Build.BuildStrategy = v1alpha1.IntegrationPlatformBuildStrategyRoutine
-			}
-		}
-	}
-
-	err = action.setDefaults(ctx, platform)
-	if err != nil {
+	if err = platformutil.ConfigureDefaults(ctx, action.client, platform, true); err != nil {
 		return nil, err
 	}
 
-	if platform.Spec.Build.PublishStrategy == v1alpha1.IntegrationPlatformBuildPublishStrategyKaniko && platform.Spec.Build.Registry.Address == "" {
-		action.L.Info("No registry specified for publishing images")
-	}
-
-	if platform.Spec.Build.Maven.Timeout.Duration != 0 {
-		action.L.Infof("Maven Timeout set to %s", platform.Spec.Build.Maven.Timeout.Duration)
-	}
-
-	err = action.client.Update(ctx, platform)
-	if err != nil {
-		return nil, err
-	}
-
-	if platform.Spec.Build.PublishStrategy == v1alpha1.IntegrationPlatformBuildPublishStrategyKaniko {
+	if platform.Status.Build.PublishStrategy == v1alpha1.IntegrationPlatformBuildPublishStrategyKaniko {
 		// Create the persistent volume claim used to coordinate build pod output
 		// with Kaniko cache and build input
 		action.L.Info("Create persistent volume claim")
@@ -135,7 +78,7 @@ func (action *initializeAction) Handle(ctx context.Context, platform *v1alpha1.I
 			return nil, err
 		}
 
-		if platform.Spec.Build.IsKanikoCacheEnabled() {
+		if platform.Status.Build.IsKanikoCacheEnabled() {
 			// Create the Kaniko warmer pod that caches the base image into the Camel K builder volume
 			action.L.Info("Create Kaniko cache warmer pod")
 			err = createKanikoCacheWarmerPod(ctx, action.client, platform)
@@ -170,97 +113,6 @@ func (action *initializeAction) isDuplicate(ctx context.Context, thisPlatform *v
 	return false, nil
 }
 
-func (action *initializeAction) setDefaults(ctx context.Context, platform *v1alpha1.IntegrationPlatform) error {
-	if platform.Spec.Profile == "" {
-		platform.Spec.Profile = platformutil.DetermineBestProfile(ctx, action.client, platform)
-	}
-	if platform.Spec.Build.CamelVersion == "" {
-		platform.Spec.Build.CamelVersion = defaults.CamelVersionConstraint
-	}
-	if platform.Spec.Build.RuntimeVersion == "" {
-		platform.Spec.Build.RuntimeVersion = defaults.RuntimeVersionConstraint
-	}
-	if platform.Spec.Build.BaseImage == "" {
-		platform.Spec.Build.BaseImage = defaults.BaseImage
-	}
-	if platform.Spec.Build.Maven.LocalRepository == "" {
-		platform.Spec.Build.Maven.LocalRepository = defaults.LocalRepository
-	}
-	if platform.Spec.Build.PersistentVolumeClaim == "" {
-		platform.Spec.Build.PersistentVolumeClaim = platform.Name
-	}
-
-	if platform.Spec.Build.Timeout.Duration != 0 {
-		d := platform.Spec.Build.Timeout.Duration.Truncate(time.Second)
-
-		if platform.Spec.Build.Timeout.Duration != d {
-			action.L.Infof("Build timeout minimum unit is sec (configured: %s, truncated: %s)", platform.Spec.Build.Timeout.Duration, d)
-		}
-
-		platform.Spec.Build.Timeout.Duration = d
-	}
-	if platform.Spec.Build.Timeout.Duration == 0 {
-		platform.Spec.Build.Timeout.Duration = 5 * time.Minute
-	}
-
-	if platform.Spec.Build.Maven.Timeout.Duration != 0 {
-		d := platform.Spec.Build.Maven.Timeout.Duration.Truncate(time.Second)
-
-		if platform.Spec.Build.Maven.Timeout.Duration != d {
-			action.L.Infof("Maven timeout minimum unit is sec (configured: %s, truncated: %s)", platform.Spec.Build.Maven.Timeout.Duration, d)
-		}
-
-		platform.Spec.Build.Maven.Timeout.Duration = d
-	}
-	if platform.Spec.Build.Maven.Timeout.Duration == 0 {
-		n := platform.Spec.Build.Timeout.Duration.Seconds() * 0.75
-		platform.Spec.Build.Maven.Timeout.Duration = (time.Duration(n) * time.Second).Truncate(time.Second)
-	}
-
-	if platform.Spec.Build.Maven.Settings.ConfigMapKeyRef == nil && platform.Spec.Build.Maven.Settings.SecretKeyRef == nil {
-		var repositories []maven.Repository
-		for i, c := range platform.Spec.Configuration {
-			if c.Type == "repository" {
-				repository := maven.NewRepository(c.Value)
-				if repository.ID == "" {
-					repository.ID = fmt.Sprintf("repository-%03d", i)
-				}
-				repositories = append(repositories, repository)
-			}
-		}
-
-		settings := maven.NewDefaultSettings(repositories)
-
-		err := createMavenSettingsConfigMap(ctx, action.client, platform, settings)
-		if err != nil {
-			return err
-		}
-
-		platform.Spec.Build.Maven.Settings.ConfigMapKeyRef = &corev1.ConfigMapKeySelector{
-			LocalObjectReference: corev1.LocalObjectReference{
-				Name: platform.Name + "-maven-settings",
-			},
-			Key: "settings.xml",
-		}
-	}
-
-	if platform.Spec.Build.PublishStrategy == v1alpha1.IntegrationPlatformBuildPublishStrategyKaniko && platform.Spec.Build.KanikoBuildCache == nil {
-		// Default to using Kaniko cache warmer
-		defaultKanikoBuildCache := true
-		platform.Spec.Build.KanikoBuildCache = &defaultKanikoBuildCache
-		action.L.Infof("Kaniko cache set to %t", *platform.Spec.Build.KanikoBuildCache)
-	}
-
-	action.L.Infof("CamelVersion set to %s", platform.Spec.Build.CamelVersion)
-	action.L.Infof("RuntimeVersion set to %s", platform.Spec.Build.RuntimeVersion)
-	action.L.Infof("BaseImage set to %s", platform.Spec.Build.BaseImage)
-	action.L.Infof("LocalRepository set to %s", platform.Spec.Build.Maven.LocalRepository)
-	action.L.Infof("Timeout set to %s", platform.Spec.Build.Timeout)
-	action.L.Infof("Maven Timeout set to %s", platform.Spec.Build.Maven.Timeout.Duration)
-
-	return nil
-}
-
 func createPersistentVolumeClaim(ctx context.Context, client client.Client, platform *v1alpha1.IntegrationPlatform) error {
 	volumeSize, err := resource.ParseQuantity("1Gi")
 	if err != nil {
@@ -274,7 +126,7 @@ func createPersistentVolumeClaim(ctx context.Context, client client.Client, plat
 		},
 		ObjectMeta: metav1.ObjectMeta{
 			Namespace: platform.Namespace,
-			Name:      platform.Spec.Build.PersistentVolumeClaim,
+			Name:      platform.Status.Build.PersistentVolumeClaim,
 			Labels: map[string]string{
 				"app": "camel-k",
 			},
@@ -299,17 +151,3 @@ func createPersistentVolumeClaim(ctx context.Context, client client.Client, plat
 
 	return nil
 }
-
-func createMavenSettingsConfigMap(ctx context.Context, client client.Client, platform *v1alpha1.IntegrationPlatform, settings maven.Settings) error {
-	cm, err := maven.CreateSettingsConfigMap(platform.Namespace, platform.Name, settings)
-	if err != nil {
-		return err
-	}
-
-	err = client.Create(ctx, cm)
-	if err != nil && !k8serrors.IsAlreadyExists(err) {
-		return err
-	}
-
-	return nil
-}
diff --git a/pkg/controller/integrationplatform/kaniko_cache.go b/pkg/controller/integrationplatform/kaniko_cache.go
index 217c50fa9..7ff25f649 100644
--- a/pkg/controller/integrationplatform/kaniko_cache.go
+++ b/pkg/controller/integrationplatform/kaniko_cache.go
@@ -59,7 +59,7 @@ func createKanikoCacheWarmerPod(ctx context.Context, client client.Client, platf
 					Image: fmt.Sprintf("gcr.io/kaniko-project/warmer:v%s", defaults.KanikoVersion),
 					Args: []string{
 						"--cache-dir=/workspace/cache",
-						"--image=" + platform.Spec.Build.BaseImage,
+						"--image=" + platform.Status.Build.BaseImage,
 					},
 					VolumeMounts: []corev1.VolumeMount{
 						{
@@ -91,7 +91,7 @@ func createKanikoCacheWarmerPod(ctx context.Context, client client.Client, platf
 					Name: "camel-k-builder",
 					VolumeSource: corev1.VolumeSource{
 						PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{
-							ClaimName: platform.Spec.Build.PersistentVolumeClaim,
+							ClaimName: platform.Status.Build.PersistentVolumeClaim,
 						},
 					},
 				},
diff --git a/pkg/controller/integrationplatform/monitor.go b/pkg/controller/integrationplatform/monitor.go
index d1e539062..380f3f1bb 100644
--- a/pkg/controller/integrationplatform/monitor.go
+++ b/pkg/controller/integrationplatform/monitor.go
@@ -21,6 +21,7 @@ import (
 	"context"
 
 	"github.com/apache/camel-k/pkg/apis/camel/v1alpha1"
+	platformutils "github.com/apache/camel-k/pkg/platform"
 	"github.com/apache/camel-k/pkg/util/defaults"
 )
 
@@ -48,5 +49,10 @@ func (action *monitorAction) Handle(ctx context.Context, platform *v1alpha1.Inte
 		action.L.Info("IntegrationPlatform version updated", "version", platform.Status.Version)
 	}
 
+	// Refresh applied configuration
+	if err := platformutils.ConfigureDefaults(ctx, action.client, platform, false); err != nil {
+		return nil, err
+	}
+
 	return platform, nil
 }
diff --git a/pkg/install/operator.go b/pkg/install/operator.go
index d4b97c88f..0d401ed08 100644
--- a/pkg/install/operator.go
+++ b/pkg/install/operator.go
@@ -191,14 +191,6 @@ func PlatformOrCollect(ctx context.Context, c client.Client, clusterType string,
 		}
 	}
 
-	var knativeInstalled bool
-	if knativeInstalled, err = knative.IsInstalled(ctx, c); err != nil {
-		return nil, err
-	}
-	if knativeInstalled {
-		pl.Spec.Profile = v1alpha1.TraitProfileKnative
-	}
-
 	return pl, nil
 }
 
diff --git a/pkg/platform/defaults.go b/pkg/platform/defaults.go
new file mode 100644
index 000000000..640e385ee
--- /dev/null
+++ b/pkg/platform/defaults.go
@@ -0,0 +1,210 @@
+/*
+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"
+	"fmt"
+	"time"
+
+	"github.com/apache/camel-k/pkg/apis/camel/v1alpha1"
+	"github.com/apache/camel-k/pkg/client"
+	"github.com/apache/camel-k/pkg/util/defaults"
+	"github.com/apache/camel-k/pkg/util/log"
+	"github.com/apache/camel-k/pkg/util/maven"
+	"github.com/apache/camel-k/pkg/util/openshift"
+	corev1 "k8s.io/api/core/v1"
+	k8serrors "k8s.io/apimachinery/pkg/api/errors"
+	v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+)
+
+// ConfigureDefaults fills with default values all missing details about the integration platform.
+// Defaults are set in the status->appliedConfiguration fields, not in the spec.
+func ConfigureDefaults(ctx context.Context, c client.Client, p *v1alpha1.IntegrationPlatform, verbose bool) error {
+	// Reset the state to initial values
+	p.ResyncStatusFullConfig()
+
+	// update missing fields in the resource
+	if p.Status.Cluster == "" {
+		// determine the kind of cluster the platform is installed into
+		isOpenShift, err := openshift.IsOpenShift(c)
+		switch {
+		case err != nil:
+			return err
+		case isOpenShift:
+			p.Status.Cluster = v1alpha1.IntegrationPlatformClusterOpenShift
+		default:
+			p.Status.Cluster = v1alpha1.IntegrationPlatformClusterKubernetes
+		}
+	}
+
+	if p.Status.Build.PublishStrategy == "" {
+		if p.Status.Cluster == v1alpha1.IntegrationPlatformClusterOpenShift {
+			p.Status.Build.PublishStrategy = v1alpha1.IntegrationPlatformBuildPublishStrategyS2I
+		} else {
+			p.Status.Build.PublishStrategy = v1alpha1.IntegrationPlatformBuildPublishStrategyKaniko
+		}
+	}
+
+	if p.Status.Build.BuildStrategy == "" {
+		// If the operator is global, a global build strategy should be used
+		if IsCurrentOperatorGlobal() {
+			// The only global strategy we have for now
+			p.Status.Build.BuildStrategy = v1alpha1.IntegrationPlatformBuildStrategyPod
+		} else {
+			if p.Status.Build.PublishStrategy == v1alpha1.IntegrationPlatformBuildPublishStrategyKaniko {
+				// The build output has to be shared with Kaniko via a persistent volume
+				p.Status.Build.BuildStrategy = v1alpha1.IntegrationPlatformBuildStrategyPod
+			} else {
+				p.Status.Build.BuildStrategy = v1alpha1.IntegrationPlatformBuildStrategyRoutine
+			}
+		}
+	}
+
+	err := setPlatformDefaults(ctx, c, p, verbose)
+	if err != nil {
+		return err
+	}
+
+	if verbose && p.Status.Build.PublishStrategy == v1alpha1.IntegrationPlatformBuildPublishStrategyKaniko && p.Status.Build.Registry.Address == "" {
+		log.Log.Info("No registry specified for publishing images")
+	}
+
+	if verbose && p.Status.Build.Maven.GetTimeout().Duration != 0 {
+		log.Log.Infof("Maven Timeout set to %s", p.Status.Build.Maven.GetTimeout().Duration)
+	}
+
+	return nil
+}
+
+func setPlatformDefaults(ctx context.Context, c client.Client, p *v1alpha1.IntegrationPlatform, verbose bool) error {
+	if p.Status.Profile == "" {
+		p.Status.Profile = DetermineBestProfile(ctx, c, p)
+	}
+	if p.Status.Build.CamelVersion == "" {
+		p.Status.Build.CamelVersion = defaults.CamelVersionConstraint
+	}
+	if p.Status.Build.RuntimeVersion == "" {
+		p.Status.Build.RuntimeVersion = defaults.RuntimeVersionConstraint
+	}
+	if p.Status.Build.BaseImage == "" {
+		p.Status.Build.BaseImage = defaults.BaseImage
+	}
+	if p.Status.Build.Maven.LocalRepository == "" {
+		p.Status.Build.Maven.LocalRepository = defaults.LocalRepository
+	}
+	if p.Status.Build.PersistentVolumeClaim == "" {
+		p.Status.Build.PersistentVolumeClaim = p.Name
+	}
+
+	if p.Status.Build.GetTimeout().Duration != 0 {
+		d := p.Status.Build.GetTimeout().Duration.Truncate(time.Second)
+
+		if verbose && p.Status.Build.GetTimeout().Duration != d {
+			log.Log.Infof("Build timeout minimum unit is sec (configured: %s, truncated: %s)", p.Status.Build.GetTimeout().Duration, d)
+		}
+
+		p.Status.Build.Timeout = &v1.Duration{
+			Duration: d,
+		}
+	}
+	if p.Status.Build.GetTimeout().Duration == 0 {
+		p.Status.Build.Timeout = &v1.Duration{
+			Duration: 5 * time.Minute,
+		}
+	}
+
+	if p.Status.Build.Maven.GetTimeout().Duration != 0 {
+		d := p.Status.Build.Maven.GetTimeout().Duration.Truncate(time.Second)
+
+		if verbose && p.Status.Build.Maven.GetTimeout().Duration != d {
+			log.Log.Infof("Maven timeout minimum unit is sec (configured: %s, truncated: %s)", p.Status.Build.Maven.GetTimeout().Duration, d)
+		}
+
+		p.Status.Build.Maven.Timeout = &v1.Duration{
+			Duration: d,
+		}
+	}
+	if p.Status.Build.Maven.GetTimeout().Duration == 0 {
+		n := p.Status.Build.GetTimeout().Duration.Seconds() * 0.75
+		p.Status.Build.Maven.Timeout = &v1.Duration{
+			Duration: (time.Duration(n) * time.Second).Truncate(time.Second),
+		}
+	}
+
+	if p.Status.Build.Maven.Settings.ConfigMapKeyRef == nil && p.Status.Build.Maven.Settings.SecretKeyRef == nil {
+		var repositories []maven.Repository
+		for i, c := range p.Status.Configuration {
+			if c.Type == "repository" {
+				repository := maven.NewRepository(c.Value)
+				if repository.ID == "" {
+					repository.ID = fmt.Sprintf("repository-%03d", i)
+				}
+				repositories = append(repositories, repository)
+			}
+		}
+
+		settings := maven.NewDefaultSettings(repositories)
+
+		err := createDefaultMavenSettingsConfigMap(ctx, c, p, settings)
+		if err != nil {
+			return err
+		}
+
+		p.Status.Build.Maven.Settings.ConfigMapKeyRef = &corev1.ConfigMapKeySelector{
+			LocalObjectReference: corev1.LocalObjectReference{
+				Name: p.Name + "-maven-settings",
+			},
+			Key: "settings.xml",
+		}
+	}
+
+	if p.Status.Build.PublishStrategy == v1alpha1.IntegrationPlatformBuildPublishStrategyKaniko && p.Status.Build.KanikoBuildCache == nil {
+		// Default to using Kaniko cache warmer
+		defaultKanikoBuildCache := true
+		p.Status.Build.KanikoBuildCache = &defaultKanikoBuildCache
+		if verbose {
+			log.Log.Infof("Kaniko cache set to %t", *p.Status.Build.KanikoBuildCache)
+		}
+	}
+
+	if verbose {
+		log.Log.Infof("CamelVersion set to %s", p.Status.Build.CamelVersion)
+		log.Log.Infof("RuntimeVersion set to %s", p.Status.Build.RuntimeVersion)
+		log.Log.Infof("BaseImage set to %s", p.Status.Build.BaseImage)
+		log.Log.Infof("LocalRepository set to %s", p.Status.Build.Maven.LocalRepository)
+		log.Log.Infof("Timeout set to %s", p.Status.Build.GetTimeout())
+		log.Log.Infof("Maven Timeout set to %s", p.Status.Build.Maven.GetTimeout().Duration)
+	}
+
+	return nil
+}
+
+func createDefaultMavenSettingsConfigMap(ctx context.Context, client client.Client, p *v1alpha1.IntegrationPlatform, settings maven.Settings) error {
+	cm, err := maven.CreateSettingsConfigMap(p.Namespace, p.Name, settings)
+	if err != nil {
+		return err
+	}
+
+	err = client.Create(ctx, cm)
+	if err != nil && !k8serrors.IsAlreadyExists(err) {
+		return err
+	}
+
+	return nil
+}
diff --git a/pkg/platform/platform.go b/pkg/platform/platform.go
index 83c2e8217..bcd92c7a0 100644
--- a/pkg/platform/platform.go
+++ b/pkg/platform/platform.go
@@ -99,8 +99,8 @@ func IsActive(p *v1alpha1.IntegrationPlatform) bool {
 
 // DetermineBestProfile tries to detect the best trait profile for the platform
 func DetermineBestProfile(ctx context.Context, c k8sclient.Reader, p *v1alpha1.IntegrationPlatform) v1alpha1.TraitProfile {
-	if p.Spec.Profile != "" {
-		return p.Spec.Profile
+	if p.Status.Profile != "" {
+		return p.Status.Profile
 	}
 	if knative.IsEnabledInNamespace(ctx, c, p.Namespace) {
 		return v1alpha1.TraitProfileKnative
@@ -110,10 +110,11 @@ func DetermineBestProfile(ctx context.Context, c k8sclient.Reader, p *v1alpha1.I
 
 // GetProfile returns the current profile of the platform (if present) or returns the default one for the cluster
 func GetProfile(p *v1alpha1.IntegrationPlatform) v1alpha1.TraitProfile {
-	if p.Spec.Profile != "" {
-		return p.Spec.Profile
+	if p.Status.Profile != "" {
+		return p.Status.Profile
 	}
-	switch p.Spec.Cluster {
+
+	switch p.Status.Cluster {
 	case v1alpha1.IntegrationPlatformClusterKubernetes:
 		return v1alpha1.TraitProfileKubernetes
 	case v1alpha1.IntegrationPlatformClusterOpenShift:
@@ -124,10 +125,10 @@ func GetProfile(p *v1alpha1.IntegrationPlatform) v1alpha1.TraitProfile {
 
 // SupportsS2iPublishStrategy --
 func SupportsS2iPublishStrategy(p *v1alpha1.IntegrationPlatform) bool {
-	return p.Spec.Build.PublishStrategy == v1alpha1.IntegrationPlatformBuildPublishStrategyS2I
+	return p.Status.Build.PublishStrategy == v1alpha1.IntegrationPlatformBuildPublishStrategyS2I
 }
 
 // SupportsKanikoPublishStrategy --
 func SupportsKanikoPublishStrategy(p *v1alpha1.IntegrationPlatform) bool {
-	return p.Spec.Build.PublishStrategy == v1alpha1.IntegrationPlatformBuildPublishStrategyKaniko && p.Spec.Build.Registry.Address != ""
+	return p.Status.Build.PublishStrategy == v1alpha1.IntegrationPlatformBuildPublishStrategyKaniko && p.Status.Build.Registry.Address != ""
 }
diff --git a/pkg/trait/camel.go b/pkg/trait/camel.go
index ca59d115b..9b5f2ee2b 100644
--- a/pkg/trait/camel.go
+++ b/pkg/trait/camel.go
@@ -105,7 +105,7 @@ func (t *camelTrait) loadOrCreateCatalog(e *Environment, camelVersion string, ru
 		// the required versions (camel and runtime) are not expressed as
 		// semver constraints
 		if exactVersionRegexp.MatchString(camelVersion) && exactVersionRegexp.MatchString(runtimeVersion) {
-			catalog, err = camel.GenerateCatalog(e.C, e.Client, ns, e.Platform.Spec.Build.Maven, camelVersion, runtimeVersion)
+			catalog, err = camel.GenerateCatalog(e.C, e.Client, ns, e.Platform.Status.Build.Maven, camelVersion, runtimeVersion)
 			if err != nil {
 				return err
 			}
@@ -148,7 +148,7 @@ func (t *camelTrait) determineCamelVersion(e *Environment) string {
 	if e.IntegrationKit != nil && e.IntegrationKit.Status.CamelVersion != "" {
 		return e.IntegrationKit.Status.CamelVersion
 	}
-	return e.Platform.Spec.Build.CamelVersion
+	return e.Platform.Status.Build.CamelVersion
 }
 
 func (t *camelTrait) determineRuntimeVersion(e *Environment) string {
@@ -161,7 +161,7 @@ func (t *camelTrait) determineRuntimeVersion(e *Environment) string {
 	if e.IntegrationKit != nil && e.IntegrationKit.Status.RuntimeVersion != "" {
 		return e.IntegrationKit.Status.RuntimeVersion
 	}
-	return e.Platform.Spec.Build.RuntimeVersion
+	return e.Platform.Status.Build.RuntimeVersion
 }
 
 // IsPlatformTrait overrides base class method
diff --git a/pkg/trait/pull_secret.go b/pkg/trait/pull_secret.go
index db7963234..86ecbc594 100644
--- a/pkg/trait/pull_secret.go
+++ b/pkg/trait/pull_secret.go
@@ -60,7 +60,7 @@ func (t *pullSecretTrait) Configure(e *Environment) (bool, error) {
 
 	if t.Auto == nil || *t.Auto {
 		if t.SecretName == "" {
-			secret := e.Platform.Spec.Build.Registry.Secret
+			secret := e.Platform.Status.Build.Registry.Secret
 			if secret != "" {
 				key := client.ObjectKey{Namespace: e.Platform.Namespace, Name: secret}
 				obj := v1.Secret{}
diff --git a/pkg/trait/quarkus.go b/pkg/trait/quarkus.go
index 8cbb165a9..72a5be703 100644
--- a/pkg/trait/quarkus.go
+++ b/pkg/trait/quarkus.go
@@ -94,7 +94,7 @@ func (t *quarkusTrait) loadOrCreateCatalog(e *Environment, camelVersion string,
 		// semver constraints
 		if exactVersionRegexp.MatchString(camelVersion) && exactVersionRegexp.MatchString(runtimeVersion) &&
 			exactVersionRegexp.MatchString(camelQuarkusVersion) && exactVersionRegexp.MatchString(quarkusVersion) {
-			catalog, err = camel.GenerateCatalogWithProvider(e.C, e.Client, ns, e.Platform.Spec.Build.Maven, camelVersion, runtimeVersion,
+			catalog, err = camel.GenerateCatalogWithProvider(e.C, e.Client, ns, e.Platform.Status.Build.Maven, camelVersion, runtimeVersion,
 				"quarkus",
 				[]maven.Dependency{
 					{
@@ -201,9 +201,9 @@ func (t *quarkusTrait) determineQuarkusVersion(e *Environment) string {
 		e.IntegrationKit.Status.RuntimeProvider.Quarkus.QuarkusVersion != "" {
 		return e.IntegrationKit.Status.RuntimeProvider.Quarkus.QuarkusVersion
 	}
-	if e.Platform.Spec.Build.RuntimeProvider != nil && e.Platform.Spec.Build.RuntimeProvider.Quarkus != nil &&
-		e.Platform.Spec.Build.RuntimeProvider.Quarkus.QuarkusVersion != "" {
-		return e.Platform.Spec.Build.RuntimeProvider.Quarkus.QuarkusVersion
+	if e.Platform.Status.Build.RuntimeProvider != nil && e.Platform.Status.Build.RuntimeProvider.Quarkus != nil &&
+		e.Platform.Status.Build.RuntimeProvider.Quarkus.QuarkusVersion != "" {
+		return e.Platform.Status.Build.RuntimeProvider.Quarkus.QuarkusVersion
 	}
 	return defaults.QuarkusVersionConstraint
 }
@@ -220,9 +220,9 @@ func (t *quarkusTrait) determineCamelQuarkusVersion(e *Environment) string {
 		e.IntegrationKit.Status.RuntimeProvider.Quarkus.CamelQuarkusVersion != "" {
 		return e.IntegrationKit.Status.RuntimeProvider.Quarkus.CamelQuarkusVersion
 	}
-	if e.Platform.Spec.Build.RuntimeProvider != nil && e.Platform.Spec.Build.RuntimeProvider.Quarkus != nil &&
-		e.Platform.Spec.Build.RuntimeProvider.Quarkus.CamelQuarkusVersion != "" {
-		return e.Platform.Spec.Build.RuntimeProvider.Quarkus.CamelQuarkusVersion
+	if e.Platform.Status.Build.RuntimeProvider != nil && e.Platform.Status.Build.RuntimeProvider.Quarkus != nil &&
+		e.Platform.Status.Build.RuntimeProvider.Quarkus.CamelQuarkusVersion != "" {
+		return e.Platform.Status.Build.RuntimeProvider.Quarkus.CamelQuarkusVersion
 	}
 	return defaults.CamelQuarkusVersionConstraint
 }
diff --git a/pkg/trait/rest-dsl.go b/pkg/trait/rest-dsl.go
index efc5ecd00..5fbb82d48 100644
--- a/pkg/trait/rest-dsl.go
+++ b/pkg/trait/rest-dsl.go
@@ -114,12 +114,12 @@ func (t *restDslTrait) Apply(e *Environment) error {
 		}
 
 		mc := maven.NewContext(tmpDir, project)
-		mc.LocalRepository = e.Platform.Spec.Build.Maven.LocalRepository
-		mc.Timeout = e.Platform.Spec.Build.Maven.Timeout.Duration
+		mc.LocalRepository = e.Platform.Status.Build.Maven.LocalRepository
+		mc.Timeout = e.Platform.Status.Build.Maven.GetTimeout().Duration
 		mc.AddArgument("-Dopenapi.spec=" + in)
 		mc.AddArgument("-Ddsl.out=" + out)
 
-		settings, err := kubernetes.ResolveValueSource(e.C, e.Client, e.Integration.Namespace, &e.Platform.Spec.Build.Maven.Settings)
+		settings, err := kubernetes.ResolveValueSource(e.C, e.Client, e.Integration.Namespace, &e.Platform.Status.Build.Maven.Settings)
 		if err != nil {
 			return err
 		}
diff --git a/pkg/trait/trait_catalog.go b/pkg/trait/trait_catalog.go
index 32d5faab3..b095c6e1e 100644
--- a/pkg/trait/trait_catalog.go
+++ b/pkg/trait/trait_catalog.go
@@ -279,8 +279,8 @@ func (c *Catalog) GetTrait(id string) Trait {
 }
 
 func (c *Catalog) configure(env *Environment) error {
-	if env.Platform != nil && env.Platform.Spec.Traits != nil {
-		if err := c.configureTraits(env.Platform.Spec.Traits); err != nil {
+	if env.Platform != nil && env.Platform.Status.Traits != nil {
+		if err := c.configureTraits(env.Platform.Status.Traits); err != nil {
 			return err
 		}
 	}
diff --git a/pkg/util/camel/catalog.go b/pkg/util/camel/catalog.go
index 4ad5d6bd1..12289e26b 100644
--- a/pkg/util/camel/catalog.go
+++ b/pkg/util/camel/catalog.go
@@ -90,7 +90,7 @@ func GenerateCatalogWithProvider(ctx context.Context, client k8sclient.Reader, n
 
 	mc := maven.NewContext(tmpDir, project)
 	mc.LocalRepository = mvn.LocalRepository
-	mc.Timeout = mvn.Timeout.Duration
+	mc.Timeout = mvn.GetTimeout().Duration
 	mc.AddSystemProperty("catalog.path", tmpDir)
 	mc.AddSystemProperty("catalog.file", "catalog.yaml")
 	if providerName != "" {
diff --git a/script/Makefile b/script/Makefile
index 5e610b339..eda1632b6 100644
--- a/script/Makefile
+++ b/script/Makefile
@@ -241,4 +241,4 @@ install-minikube:
 release-notes:
 	./script/gen_release_notes.sh $(LAST_RELEASED_VERSION) $(VERSION)
 
-.PHONY: build build-kamel build-resources build-olm unsnapshot-olm dep codegen images images-dec images-push images-push-staging test check test-integration clean release cross-compile package-examples set-version git-tag release-notes check-licenses generate-deepcopy generate-client generate-doc
+.PHONY: build build-kamel build-resources build-olm unsnapshot-olm dep codegen images images-dev images-push images-push-staging test check test-integration clean release cross-compile package-examples set-version git-tag release-notes check-licenses generate-deepcopy generate-client generate-doc


Test patch:
diff --git a/pkg/controller/integrationplatform/create_test.go b/pkg/controller/integrationplatform/create_test.go
index 2670eb729..0cfabca34 100644
--- a/pkg/controller/integrationplatform/create_test.go
+++ b/pkg/controller/integrationplatform/create_test.go
@@ -23,8 +23,8 @@ import (
 	"testing"
 
 	"github.com/apache/camel-k/deploy"
-
 	"github.com/apache/camel-k/pkg/apis/camel/v1alpha1"
+	"github.com/apache/camel-k/pkg/platform"
 	"github.com/apache/camel-k/pkg/util/log"
 	"github.com/apache/camel-k/pkg/util/test"
 	"github.com/rs/xid"
@@ -41,6 +41,9 @@ func TestCreate(t *testing.T) {
 	c, err := test.NewFakeClient(&ip)
 	assert.Nil(t, err)
 
+	err = platform.ConfigureDefaults(context.TODO(), c, &ip, false)
+	assert.Nil(t, err)
+
 	h := NewCreateAction()
 	h.InjectLogger(log.Log)
 	h.InjectClient(c)
diff --git a/pkg/controller/integrationplatform/initialize_test.go b/pkg/controller/integrationplatform/initialize_test.go
index e744a9d75..53681e2fd 100644
--- a/pkg/controller/integrationplatform/initialize_test.go
+++ b/pkg/controller/integrationplatform/initialize_test.go
@@ -22,6 +22,7 @@ import (
 	"testing"
 	"time"
 
+	"github.com/apache/camel-k/pkg/platform"
 	"github.com/rs/xid"
 
 	"github.com/apache/camel-k/pkg/apis/camel/v1alpha1"
@@ -42,6 +43,8 @@ func TestTimeouts_Default(t *testing.T) {
 	c, err := test.NewFakeClient(&ip)
 	assert.Nil(t, err)
 
+	assert.Nil(t, platform.ConfigureDefaults(context.TODO(), c, &ip, false))
+
 	h := NewInitializeAction()
 	h.InjectLogger(log.Log)
 	h.InjectClient(c)
@@ -50,11 +53,11 @@ func TestTimeouts_Default(t *testing.T) {
 	assert.Nil(t, err)
 	assert.NotNil(t, answer)
 
-	n := answer.Spec.Build.Timeout.Duration.Seconds() * 0.75
+	n := answer.Status.Build.GetTimeout().Duration.Seconds() * 0.75
 	d := (time.Duration(n) * time.Second).Truncate(time.Second)
 
-	assert.Equal(t, d, answer.Spec.Build.Maven.Timeout.Duration)
-	assert.Equal(t, 5*time.Minute, answer.Spec.Build.Timeout.Duration)
+	assert.Equal(t, d, answer.Status.Build.Maven.GetTimeout().Duration)
+	assert.Equal(t, 5*time.Minute, answer.Status.Build.GetTimeout().Duration)
 }
 
 func TestTimeouts_MavenComputedFromBuild(t *testing.T) {
@@ -66,13 +69,15 @@ func TestTimeouts_MavenComputedFromBuild(t *testing.T) {
 	timeout, err := time.ParseDuration("1m1ms")
 	assert.Nil(t, err)
 
-	ip.Spec.Build.Timeout = metav1.Duration{
+	ip.Spec.Build.Timeout = &metav1.Duration{
 		Duration: timeout,
 	}
 
 	c, err := test.NewFakeClient(&ip)
 	assert.Nil(t, err)
 
+	assert.Nil(t, platform.ConfigureDefaults(context.TODO(), c, &ip, false))
+
 	h := NewInitializeAction()
 	h.InjectLogger(log.Log)
 	h.InjectClient(c)
@@ -81,11 +86,11 @@ func TestTimeouts_MavenComputedFromBuild(t *testing.T) {
 	assert.Nil(t, err)
 	assert.NotNil(t, answer)
 
-	n := answer.Spec.Build.Timeout.Duration.Seconds() * 0.75
+	n := answer.Status.Build.GetTimeout().Duration.Seconds() * 0.75
 	d := (time.Duration(n) * time.Second).Truncate(time.Second)
 
-	assert.Equal(t, d, answer.Spec.Build.Maven.Timeout.Duration)
-	assert.Equal(t, 1*time.Minute, answer.Spec.Build.Timeout.Duration)
+	assert.Equal(t, d, answer.Status.Build.Maven.GetTimeout().Duration)
+	assert.Equal(t, 1*time.Minute, answer.Status.Build.GetTimeout().Duration)
 }
 
 func TestTimeouts_Truncated(t *testing.T) {
@@ -97,20 +102,22 @@ func TestTimeouts_Truncated(t *testing.T) {
 	bt, err := time.ParseDuration("5m1ms")
 	assert.Nil(t, err)
 
-	ip.Spec.Build.Timeout = metav1.Duration{
+	ip.Spec.Build.Timeout = &metav1.Duration{
 		Duration: bt,
 	}
 
 	mt, err := time.ParseDuration("2m1ms")
 	assert.Nil(t, err)
 
-	ip.Spec.Build.Maven.Timeout = metav1.Duration{
+	ip.Spec.Build.Maven.Timeout = &metav1.Duration{
 		Duration: mt,
 	}
 
 	c, err := test.NewFakeClient(&ip)
 	assert.Nil(t, err)
 
+	assert.Nil(t, platform.ConfigureDefaults(context.TODO(), c, &ip, false))
+
 	h := NewInitializeAction()
 	h.InjectLogger(log.Log)
 	h.InjectClient(c)
@@ -119,8 +126,8 @@ func TestTimeouts_Truncated(t *testing.T) {
 	assert.Nil(t, err)
 	assert.NotNil(t, answer)
 
-	assert.Equal(t, 2*time.Minute, answer.Spec.Build.Maven.Timeout.Duration)
-	assert.Equal(t, 5*time.Minute, answer.Spec.Build.Timeout.Duration)
+	assert.Equal(t, 2*time.Minute, answer.Status.Build.Maven.GetTimeout().Duration)
+	assert.Equal(t, 5*time.Minute, answer.Status.Build.GetTimeout().Duration)
 }
 
 func TestDefaultMavenSettingsApplied(t *testing.T) {
@@ -132,6 +139,8 @@ func TestDefaultMavenSettingsApplied(t *testing.T) {
 	c, err := test.NewFakeClient(&ip)
 	assert.Nil(t, err)
 
+	assert.Nil(t, platform.ConfigureDefaults(context.TODO(), c, &ip, false))
+
 	h := NewInitializeAction()
 	h.InjectLogger(log.Log)
 	h.InjectClient(c)
@@ -140,7 +149,8 @@ func TestDefaultMavenSettingsApplied(t *testing.T) {
 	assert.Nil(t, err)
 	assert.NotNil(t, answer)
 
-	assert.NotNil(t, answer.Spec.Build.Maven.Settings.ConfigMapKeyRef)
-	assert.Equal(t, "test-platform-maven-settings", answer.Spec.Build.Maven.Settings.ConfigMapKeyRef.Name)
-	assert.Equal(t, "settings.xml", answer.Spec.Build.Maven.Settings.ConfigMapKeyRef.Key)
+	assert.NotNil(t, answer.Status.Build.Maven.Settings.ConfigMapKeyRef)
+	assert.Nil(t, answer.Spec.Build.Maven.Settings.ConfigMapKeyRef)
+	assert.Equal(t, "test-platform-maven-settings", answer.Status.Build.Maven.Settings.ConfigMapKeyRef.Name)
+	assert.Equal(t, "settings.xml", answer.Status.Build.Maven.Settings.ConfigMapKeyRef.Key)
 }
diff --git a/pkg/controller/integrationplatform/warm_test.go b/pkg/controller/integrationplatform/warm_test.go
index 160cbc63c..d564031bc 100644
--- a/pkg/controller/integrationplatform/warm_test.go
+++ b/pkg/controller/integrationplatform/warm_test.go
@@ -21,6 +21,7 @@ import (
 	"context"
 	"testing"
 
+	"github.com/apache/camel-k/pkg/platform"
 	corev1 "k8s.io/api/core/v1"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 
@@ -54,6 +55,8 @@ func TestWarm_Succeeded(t *testing.T) {
 	c, err := test.NewFakeClient(&ip, &pod)
 	assert.Nil(t, err)
 
+	assert.Nil(t, platform.ConfigureDefaults(context.TODO(), c, &ip, false))
+
 	h := NewWarmAction()
 	h.InjectLogger(log.Log)
 	h.InjectClient(c)
@@ -86,6 +89,8 @@ func TestWarm_Failing(t *testing.T) {
 	c, err := test.NewFakeClient(&ip, &pod)
 	assert.Nil(t, err)
 
+	assert.Nil(t, platform.ConfigureDefaults(context.TODO(), c, &ip, false))
+
 	h := NewWarmAction()
 	h.InjectLogger(log.Log)
 	h.InjectClient(c)
@@ -118,6 +123,8 @@ func TestWarm_WarmingUp(t *testing.T) {
 	c, err := test.NewFakeClient(&ip, &pod)
 	assert.Nil(t, err)
 
+	assert.Nil(t, platform.ConfigureDefaults(context.TODO(), c, &ip, false))
+
 	h := NewWarmAction()
 	h.InjectLogger(log.Log)
 	h.InjectClient(c)
diff --git a/pkg/trait/builder_test.go b/pkg/trait/builder_test.go
index 9fa42bff9..a9f2c0060 100644
--- a/pkg/trait/builder_test.go
+++ b/pkg/trait/builder_test.go
@@ -46,7 +46,7 @@ func TestBuilderTraitNotAppliedBecauseOfNilKit(t *testing.T) {
 		e := e // pin
 		e.IntegrationKit = nil
 
-		t.Run(string(e.Platform.Spec.Cluster), func(t *testing.T) {
+		t.Run(string(e.Platform.Status.Cluster), func(t *testing.T) {
 			err := NewBuilderTestCatalog().apply(e)
 
 			assert.Nil(t, err)
@@ -67,7 +67,7 @@ func TestBuilderTraitNotAppliedBecauseOfNilPhase(t *testing.T) {
 		e := e // pin
 		e.IntegrationKit.Status.Phase = v1alpha1.IntegrationKitPhaseInitialization
 
-		t.Run(string(e.Platform.Spec.Cluster), func(t *testing.T) {
+		t.Run(string(e.Platform.Status.Cluster), func(t *testing.T) {
 			err := NewBuilderTestCatalog().apply(e)
 
 			assert.Nil(t, err)
@@ -124,7 +124,7 @@ func createBuilderTestEnv(cluster v1alpha1.IntegrationPlatformCluster, strategy
 		panic(err)
 	}
 
-	return &Environment{
+	res := &Environment{
 		C:            context.TODO(),
 		CamelCatalog: c,
 		Catalog:      NewCatalog(context.TODO(), nil),
@@ -157,6 +157,10 @@ func createBuilderTestEnv(cluster v1alpha1.IntegrationPlatformCluster, strategy
 		Resources:      kubernetes.NewCollection(),
 		Classpath:      strset.New(),
 	}
+
+	res.Platform.ResyncStatusFullConfig()
+
+	return res
 }
 
 func NewBuilderTestCatalog() *Catalog {
diff --git a/pkg/trait/container_test.go b/pkg/trait/container_test.go
index 90b96138a..f66c0c81e 100644
--- a/pkg/trait/container_test.go
+++ b/pkg/trait/container_test.go
@@ -72,6 +72,7 @@ func TestContainerWithDefaults(t *testing.T) {
 		Resources:      kubernetes.NewCollection(),
 		Classpath:      strset.New(),
 	}
+	environment.Platform.ResyncStatusFullConfig()
 
 	err = traitCatalog.apply(&environment)
 
@@ -134,6 +135,7 @@ func TestContainerWithCustomName(t *testing.T) {
 		Resources:      kubernetes.NewCollection(),
 		Classpath:      strset.New(),
 	}
+	environment.Platform.ResyncStatusFullConfig()
 
 	err = traitCatalog.apply(&environment)
 
diff --git a/pkg/trait/deployment_test.go b/pkg/trait/deployment_test.go
index 15a30df42..9d4532856 100644
--- a/pkg/trait/deployment_test.go
+++ b/pkg/trait/deployment_test.go
@@ -199,6 +199,7 @@ func createNominalDeploymentTest() (*deploymentTrait, *Environment) {
 		},
 		Resources: kubernetes.NewCollection(),
 	}
+	environment.Platform.ResyncStatusFullConfig()
 
 	return trait, environment
 }
diff --git a/pkg/trait/environment_test.go b/pkg/trait/environment_test.go
index 977ab4be2..e13a472e6 100644
--- a/pkg/trait/environment_test.go
+++ b/pkg/trait/environment_test.go
@@ -66,6 +66,7 @@ func TestDefaultEnvironment(t *testing.T) {
 		Resources:      kubernetes.NewCollection(),
 		Classpath:      strset.New(),
 	}
+	env.Platform.ResyncStatusFullConfig()
 
 	err = NewEnvironmentTestCatalog().apply(&env)
 
@@ -134,6 +135,7 @@ func TestEnabledContainerMetaDataEnvVars(t *testing.T) {
 		Resources:      kubernetes.NewCollection(),
 		Classpath:      strset.New(),
 	}
+	env.Platform.ResyncStatusFullConfig()
 
 	err = NewEnvironmentTestCatalog().apply(&env)
 
@@ -202,6 +204,7 @@ func TestDisabledContainerMetaDataEnvVars(t *testing.T) {
 		Resources:      kubernetes.NewCollection(),
 		Classpath:      strset.New(),
 	}
+	env.Platform.ResyncStatusFullConfig()
 
 	err = NewEnvironmentTestCatalog().apply(&env)
 
diff --git a/pkg/trait/istio_test.go b/pkg/trait/istio_test.go
index b68d374ab..92d91bfa7 100644
--- a/pkg/trait/istio_test.go
+++ b/pkg/trait/istio_test.go
@@ -64,6 +64,7 @@ func NewIstioTestEnv(t *testing.T, d *appsv1.Deployment, s *serving.Service, ena
 		EnvVars:   make([]corev1.EnvVar, 0),
 		Resources: kubernetes.NewCollection(s, d),
 	}
+	env.Platform.ResyncStatusFullConfig()
 
 	if enabled {
 		env.Integration.Spec.Traits["istio"].Configuration["enabled"] = "true"
diff --git a/pkg/trait/knative_service_test.go b/pkg/trait/knative_service_test.go
index faad1b299..5da9b5431 100644
--- a/pkg/trait/knative_service_test.go
+++ b/pkg/trait/knative_service_test.go
@@ -112,6 +112,7 @@ func TestKnativeService(t *testing.T) {
 		Resources:      kubernetes.NewCollection(),
 		Classpath:      strset.New(),
 	}
+	environment.Platform.ResyncStatusFullConfig()
 
 	err = traitCatalog.apply(&environment)
 
@@ -238,6 +239,7 @@ func TestKnativeServiceWithCustomContainerName(t *testing.T) {
 		Resources:      kubernetes.NewCollection(),
 		Classpath:      strset.New(),
 	}
+	environment.Platform.ResyncStatusFullConfig()
 
 	err = traitCatalog.apply(&environment)
 
diff --git a/pkg/trait/knative_test.go b/pkg/trait/knative_test.go
index 35961884d..716059cdc 100644
--- a/pkg/trait/knative_test.go
+++ b/pkg/trait/knative_test.go
@@ -98,6 +98,7 @@ func TestKnativeEnvConfigurationFromTrait(t *testing.T) {
 		Resources:      k8sutils.NewCollection(),
 		Classpath:      strset.New(),
 	}
+	environment.Platform.ResyncStatusFullConfig()
 
 	c, err := NewFakeClient("ns")
 	assert.Nil(t, err)
@@ -207,6 +208,7 @@ func TestKnativeEnvConfigurationFromSource(t *testing.T) {
 		Resources:      k8sutils.NewCollection(),
 		Classpath:      strset.New(),
 	}
+	environment.Platform.ResyncStatusFullConfig()
 
 	c, err := NewFakeClient("ns")
 	assert.Nil(t, err)
diff --git a/pkg/trait/route_test.go b/pkg/trait/route_test.go
index 24f489569..3a4315678 100644
--- a/pkg/trait/route_test.go
+++ b/pkg/trait/route_test.go
@@ -39,7 +39,7 @@ func createTestRouteEnvironment(t *testing.T, name string) *Environment {
 	catalog, err := camel.DefaultCatalog()
 	assert.Nil(t, err)
 
-	return &Environment{
+	res := &Environment{
 		CamelCatalog: catalog,
 		Catalog:      NewCatalog(context.TODO(), nil),
 		Integration: &v1alpha1.Integration{
@@ -92,6 +92,8 @@ func createTestRouteEnvironment(t *testing.T, name string) *Environment {
 			},
 		),
 	}
+	res.Platform.ResyncStatusFullConfig()
+	return res
 }
 
 func TestRoute_Default(t *testing.T) {
diff --git a/pkg/trait/service_test.go b/pkg/trait/service_test.go
index 5505de018..da72d7ad9 100644
--- a/pkg/trait/service_test.go
+++ b/pkg/trait/service_test.go
@@ -96,6 +96,7 @@ func TestServiceWithDefaults(t *testing.T) {
 		Resources:      kubernetes.NewCollection(),
 		Classpath:      strset.New(),
 	}
+	environment.Platform.ResyncStatusFullConfig()
 
 	err = traitCatalog.apply(&environment)
 
@@ -194,6 +195,7 @@ func TestService(t *testing.T) {
 		Resources:      kubernetes.NewCollection(),
 		Classpath:      strset.New(),
 	}
+	environment.Platform.ResyncStatusFullConfig()
 
 	err = traitCatalog.apply(&environment)
 
@@ -277,6 +279,7 @@ func TestServiceWithCustomContainerName(t *testing.T) {
 		Resources:      kubernetes.NewCollection(),
 		Classpath:      strset.New(),
 	}
+	environment.Platform.ResyncStatusFullConfig()
 
 	err = traitCatalog.apply(&environment)
 
diff --git a/pkg/trait/trait_test.go b/pkg/trait/trait_test.go
index 67faa617e..3a9f9e7e6 100644
--- a/pkg/trait/trait_test.go
+++ b/pkg/trait/trait_test.go
@@ -177,6 +177,7 @@ func TestTraitHierarchyDecode(t *testing.T) {
 			"autoscaling-target": "15",
 		},
 	}
+	env.Platform.ResyncStatusFullConfig()
 
 	env.IntegrationKit.Spec.Traits = make(map[string]v1alpha1.TraitSpec)
 	env.IntegrationKit.Spec.Traits["knative-service"] = v1alpha1.TraitSpec{
@@ -433,7 +434,7 @@ func createTestEnv(t *testing.T, cluster v1alpha1.IntegrationPlatformCluster, sc
 	catalog, err := camel.DefaultCatalog()
 	assert.Nil(t, err)
 
-	return &Environment{
+	res := &Environment{
 		CamelCatalog: catalog,
 		Catalog:      NewCatalog(context.TODO(), nil),
 		Integration: &v1alpha1.Integration{
@@ -471,6 +472,8 @@ func createTestEnv(t *testing.T, cluster v1alpha1.IntegrationPlatformCluster, sc
 		Resources:      kubernetes.NewCollection(),
 		Classpath:      strset.New(),
 	}
+	res.Platform.ResyncStatusFullConfig()
+	return res
 }
 
 func NewTraitTestCatalog() *Catalog {
diff --git a/pkg/trait/util_test.go b/pkg/trait/util_test.go
index ec2709e29..c6df916a9 100644
--- a/pkg/trait/util_test.go
+++ b/pkg/trait/util_test.go
@@ -54,6 +54,7 @@ func TestCollectConfigurationValues(t *testing.T) {
 			},
 		},
 	}
+	e.Platform.ResyncStatusFullConfig()
 
 	assert.Contains(t, e.CollectConfigurationValues("configmap"), "my-cm-integration")
 	assert.Contains(t, e.CollectConfigurationValues("secret"), "my-secret-platform")
@@ -90,6 +91,7 @@ func TestCollectConfigurationPairs(t *testing.T) {
 			},
 		},
 	}
+	e.Platform.ResyncStatusFullConfig()
 
 	pairs := e.CollectConfigurationPairs("property")
 	assert.Equal(t, "integration", pairs["p1"])