diff --git a/docs/modules/ROOT/pages/running/dry-build.adoc b/docs/modules/ROOT/pages/running/dry-build.adoc index 9093bb2cea..00748d0ec0 100644 --- a/docs/modules/ROOT/pages/running/dry-build.adoc +++ b/docs/modules/ROOT/pages/running/dry-build.adoc @@ -24,12 +24,12 @@ As the patching on an Integration custom resource can be boring, we've introduce [[undeploy]] == Undeploy an application -Specular to that, we have the **undeploy** operation. If at any point in time you want to bring the Integration back to its original status, then just patch its phase to an empty string (which represent the `Initialization` phase). The operator will take care to revert it and to clean all the resources associated. +Specular to that, we have the **undeploy** operation. If at any point in time you want to bring the Integration back to its original status, then just patch its phase to the `Undeploying` phase. The operator will take care to revert it and to clean all the resources associated. It would be something like: ```bash -kubectl patch it my-app --type=merge --subresource=status -p '{"status":{"phase":""}}' +kubectl patch it my-app --type=merge --subresource=status -p '{"status":{"phase":"Undeploying"}}' ``` Also here, you will find handy the `kamel undeploy` CLI command. It also expects one ore more Integration names you want to undeploy. The `kamel undeploy` and the patch to "" are equivalent. diff --git a/pkg/apis/camel/v1/integration_types.go b/pkg/apis/camel/v1/integration_types.go index 554de5cfbb..4fb96bbc3d 100644 --- a/pkg/apis/camel/v1/integration_types.go +++ b/pkg/apis/camel/v1/integration_types.go @@ -177,6 +177,8 @@ const ( IntegrationPhaseBuildComplete IntegrationPhase = "Build Complete" // IntegrationPhaseDeploying --. IntegrationPhaseDeploying IntegrationPhase = "Deploying" + // IntegrationPhaseUnDeploying --. + IntegrationPhaseUnDeploying IntegrationPhase = "Undeploying" // IntegrationPhaseRunning --. IntegrationPhaseRunning IntegrationPhase = "Running" // IntegrationPhaseError --. diff --git a/pkg/apis/camel/v1/integration_types_support.go b/pkg/apis/camel/v1/integration_types_support.go index 2e7fa71c70..48a717070f 100644 --- a/pkg/apis/camel/v1/integration_types_support.go +++ b/pkg/apis/camel/v1/integration_types_support.go @@ -370,16 +370,18 @@ func (in *Integration) IsSynthetic() bool { return in.Annotations[IntegrationSyntheticLabel] == "true" } -// SetBuildOrDeploymentPhase set the proper building phase and the related timestamps. -func (in *Integration) SetBuildOrDeploymentPhase() { +// SetBuildCompletePhase set the proper building phase and the related timestamps. +func (in *Integration) SetBuildCompletePhase() { now := metav1.Now().Rfc3339Copy() in.Status.BuildTimestamp = &now - if in.Annotations[IntegrationDontRunAfterBuildAnnotation] == IntegrationDontRunAfterBuildAnnotationTrueValue { - in.Status.Phase = IntegrationPhaseBuildComplete - } else { - in.Status.DeploymentTimestamp = &now - in.Status.Phase = IntegrationPhaseDeploying - } + in.Status.Phase = IntegrationPhaseBuildComplete +} + +// SetDeployingPhase set the proper deploy phase and the related timestamps. +func (in *Integration) SetDeployingPhase() { + now := metav1.Now().Rfc3339Copy() + in.Status.DeploymentTimestamp = &now + in.Status.Phase = IntegrationPhaseDeploying } // GetCondition returns the condition with the provided type. diff --git a/pkg/cmd/deploy.go b/pkg/cmd/deploy.go index f63d36b2c9..cc775efb6b 100644 --- a/pkg/cmd/deploy.go +++ b/pkg/cmd/deploy.go @@ -23,7 +23,6 @@ import ( v1 "github.com/apache/camel-k/v2/pkg/apis/camel/v1" "github.com/spf13/cobra" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ctrl "sigs.k8s.io/controller-runtime/pkg/client" ) @@ -74,10 +73,7 @@ func (o *deployCmdOptions) run(cmd *cobra.Command, args []string) error { } integration := existing.DeepCopy() - // Set DeploymentTimestamp to track when deployment was initiated - now := metav1.Now().Rfc3339Copy() - integration.Status.DeploymentTimestamp = &now - integration.Status.Phase = v1.IntegrationPhaseDeploying + integration.SetDeployingPhase() patch := ctrl.MergeFrom(existing) d, err := patch.Data(integration) diff --git a/pkg/cmd/undeploy.go b/pkg/cmd/undeploy.go index fcb7495da4..4d989ad022 100644 --- a/pkg/cmd/undeploy.go +++ b/pkg/cmd/undeploy.go @@ -98,7 +98,7 @@ func (o *undeployCmdOptions) undeployIntegrations(cmd *cobra.Command, c k8sclien continue } it := i - it.Status.Phase = v1.IntegrationPhaseInitialization + it.Status.Phase = v1.IntegrationPhaseUnDeploying if err := c.Status().Update(o.Context, &it); err != nil { return undeployed, fmt.Errorf("could not undeploy %s in namespace %s: %w", it.Name, o.Namespace, err) } diff --git a/pkg/controller/integration/build.go b/pkg/controller/integration/build.go index 2d1971de5e..a82dcc20f5 100644 --- a/pkg/controller/integration/build.go +++ b/pkg/controller/integration/build.go @@ -228,7 +228,7 @@ func (action *buildAction) handleBuildRunning(ctx context.Context, it *v1.Integr } it.Status.Image = fmt.Sprintf("%s@%s", image, build.Status.Digest) } - it.SetBuildOrDeploymentPhase() + it.SetBuildCompletePhase() case v1.BuildPhaseError, v1.BuildPhaseInterrupted, v1.BuildPhaseFailed: it.Status.Phase = v1.IntegrationPhaseError reason := fmt.Sprintf("Build%s", build.Status.Phase) diff --git a/pkg/controller/integration/build_complete.go b/pkg/controller/integration/build_complete.go new file mode 100644 index 0000000000..edc1fa17f5 --- /dev/null +++ b/pkg/controller/integration/build_complete.go @@ -0,0 +1,67 @@ +/* +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 integration + +import ( + "context" + + v1 "github.com/apache/camel-k/v2/pkg/apis/camel/v1" + "github.com/apache/camel-k/v2/pkg/trait" + corev1 "k8s.io/api/core/v1" +) + +// NewBuildCompleteAction creates a new build complete action. +func NewBuildCompleteAction() Action { + return &buildCompleteAction{} +} + +type buildCompleteAction struct { + baseAction +} + +// Name returns a common name of the action. +func (action *buildCompleteAction) Name() string { + return "build-complete" +} + +// CanHandle tells whether this action can handle the integration. +func (action *buildCompleteAction) CanHandle(integration *v1.Integration) bool { + return integration.Status.Phase == v1.IntegrationPhaseBuildComplete +} + +// Handle handles the integrations. +func (action *buildCompleteAction) Handle(ctx context.Context, integration *v1.Integration) (*v1.Integration, error) { + // Run traits that are enabled for the "Build Complete" phase (ie, gitops) + _, err := trait.Apply(ctx, action.client, integration, nil) + if err != nil { + integration.Status.Phase = v1.IntegrationPhaseError + integration.SetReadyCondition( + corev1.ConditionFalse, + v1.IntegrationConditionInitializationFailedReason, + err.Error(), + ) + + return integration, err + } + if integration.Annotations[v1.IntegrationDontRunAfterBuildAnnotation] != v1.IntegrationDontRunAfterBuildAnnotationTrueValue { + // We only move to Deploying phase if the Integration is not marked as "build only" + integration.SetDeployingPhase() + } + + return integration, nil +} diff --git a/pkg/controller/integration/build_complete_test.go b/pkg/controller/integration/build_complete_test.go new file mode 100644 index 0000000000..a0fe9cf375 --- /dev/null +++ b/pkg/controller/integration/build_complete_test.go @@ -0,0 +1,89 @@ +/* +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 integration + +import ( + "context" + "testing" + + v1 "github.com/apache/camel-k/v2/pkg/apis/camel/v1" + "github.com/apache/camel-k/v2/pkg/internal" + "github.com/apache/camel-k/v2/pkg/util/log" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +func TestIntegrationBuildCompleteToDeployDrift(t *testing.T) { + it := &v1.Integration{ + TypeMeta: metav1.TypeMeta{ + APIVersion: v1.SchemeGroupVersion.String(), + Kind: v1.IntegrationKind, + }, + ObjectMeta: metav1.ObjectMeta{ + Namespace: "ns", + Name: "my-it", + }, + Status: v1.IntegrationStatus{ + Phase: v1.IntegrationPhaseBuildComplete, + }, + } + c, err := internal.NewFakeClient(it) + require.NoError(t, err) + + a := buildCompleteAction{} + a.InjectLogger(log.Log) + a.InjectClient(c) + assert.Equal(t, "build-complete", a.Name()) + assert.True(t, a.CanHandle(it)) + handledIt, err := a.Handle(context.TODO(), it) + require.NoError(t, err) + require.NotNil(t, handledIt) + assert.Equal(t, v1.IntegrationPhaseDeploying, handledIt.Status.Phase) +} + +func TestIntegrationBuildCompleteDontDeploy(t *testing.T) { + it := &v1.Integration{ + TypeMeta: metav1.TypeMeta{ + APIVersion: v1.SchemeGroupVersion.String(), + Kind: v1.IntegrationKind, + }, + ObjectMeta: metav1.ObjectMeta{ + Namespace: "ns", + Name: "my-it", + Annotations: map[string]string{ + v1.IntegrationDontRunAfterBuildAnnotation: "true", + }, + }, + Status: v1.IntegrationStatus{ + Phase: v1.IntegrationPhaseBuildComplete, + }, + } + c, err := internal.NewFakeClient(it) + require.NoError(t, err) + + a := buildCompleteAction{} + a.InjectLogger(log.Log) + a.InjectClient(c) + assert.Equal(t, "build-complete", a.Name()) + assert.True(t, a.CanHandle(it)) + handledIt, err := a.Handle(context.TODO(), it) + require.NoError(t, err) + require.NotNil(t, handledIt) + assert.Equal(t, v1.IntegrationPhaseBuildComplete, handledIt.Status.Phase) +} diff --git a/pkg/controller/integration/build_kit.go b/pkg/controller/integration/build_kit.go index 5bafcc1847..8a4006e3de 100644 --- a/pkg/controller/integration/build_kit.go +++ b/pkg/controller/integration/build_kit.go @@ -141,12 +141,13 @@ kits: } if integrationKit != nil { - action.L.Debug("Setting integration kit for integration", "integration", integration.Name, "namespace", integration.Namespace, "integration kit", integrationKit.Name) + action.L.Debug("Setting integration kit for integration", "integration", integration.Name, + "namespace", integration.Namespace, "integration kit", integrationKit.Name) // Set the kit name so the next handle loop, will fall through the // same path as integration with a user defined kit integration.SetIntegrationKit(integrationKit) if integrationKit.Status.Phase == v1.IntegrationKitPhaseReady { - integration.SetBuildOrDeploymentPhase() + integration.SetBuildCompletePhase() } } else { action.L.Debug("Not yet able to assign an integration kit to integration", @@ -203,7 +204,7 @@ func (action *buildKitAction) checkIntegrationKit(ctx context.Context, integrati } if kit.Status.Phase == v1.IntegrationKitPhaseReady { - integration.SetBuildOrDeploymentPhase() + integration.SetBuildCompletePhase() integration.SetIntegrationKit(kit) return integration, nil diff --git a/pkg/controller/integration/build_kit_test.go b/pkg/controller/integration/build_kit_test.go index 06500aa446..1253a149a3 100644 --- a/pkg/controller/integration/build_kit_test.go +++ b/pkg/controller/integration/build_kit_test.go @@ -125,7 +125,7 @@ func TestCamelBuildKitKitSetOnIntegration(t *testing.T) { a.InjectClient(c) handledIt, err = a.Handle(context.TODO(), it) require.NoError(t, err) - assert.Equal(t, v1.IntegrationPhaseDeploying, handledIt.Status.Phase) + assert.Equal(t, v1.IntegrationPhaseBuildComplete, handledIt.Status.Phase) // Move IntegrationKit phase to ready status it.Status.Phase = v1.IntegrationPhaseBuildingKit @@ -243,7 +243,7 @@ func TestCamelBuildKitKitLookupExistingKit(t *testing.T) { handledIt, err = a.Handle(context.TODO(), it) require.NoError(t, err) assert.NotNil(t, handledIt) - assert.Equal(t, v1.IntegrationPhaseDeploying, handledIt.Status.Phase) + assert.Equal(t, v1.IntegrationPhaseBuildComplete, handledIt.Status.Phase) assert.Equal(t, ik.Name, it.Status.IntegrationKit.Name) assert.Equal(t, ik.Namespace, it.Status.IntegrationKit.Namespace) // Found a matching kit (error) diff --git a/pkg/controller/integration/build_test.go b/pkg/controller/integration/build_test.go index 20c6b36b75..81bd808adb 100644 --- a/pkg/controller/integration/build_test.go +++ b/pkg/controller/integration/build_test.go @@ -204,7 +204,7 @@ func TestIntegrationBuildRunningBuildSucceeded(t *testing.T) { handledIt, err := a.Handle(context.TODO(), it) require.NoError(t, err) require.NotNil(t, handledIt) - assert.Equal(t, v1.IntegrationPhaseDeploying, handledIt.Status.Phase) + assert.Equal(t, v1.IntegrationPhaseBuildComplete, handledIt.Status.Phase) assert.Equal(t, "my-image@123456", handledIt.Status.Image) assert.Equal(t, "/deploy/my.jar", handledIt.Status.Jar) } diff --git a/pkg/controller/integration/initialize.go b/pkg/controller/integration/initialize.go index 0fdbd65790..275f97d0e5 100644 --- a/pkg/controller/integration/initialize.go +++ b/pkg/controller/integration/initialize.go @@ -47,7 +47,8 @@ func (action *initializeAction) Name() string { // CanHandle tells whether this action can handle the integration. func (action *initializeAction) CanHandle(integration *v1.Integration) bool { - return integration.Status.Phase == v1.IntegrationPhaseInitialization + return integration.Status.Phase == v1.IntegrationPhaseInitialization || + integration.Status.Phase == v1.IntegrationPhaseUnDeploying } // Handle handles the integrations. @@ -58,12 +59,19 @@ func (action *initializeAction) Handle(ctx context.Context, integration *v1.Inte return action.importFromExternalApp(integration) } - if integration.Spec.Git != nil { + // Only move to the build submitted when we're initializing, never on undeploying + if integration.Status.Phase == v1.IntegrationPhaseInitialization && integration.Spec.Git != nil { integration.Status.Phase = v1.IntegrationPhaseBuildSubmitted return integration, nil } + // We must clear deployment conditions when undeploying. + if integration.Status.Phase == v1.IntegrationPhaseUnDeploying { + integration.Status.Replicas = nil + integration.Status.RemoveCondition(v1.IntegrationConditionReady) + } + if _, err := trait.Apply(ctx, action.client, integration, nil); err != nil { integration.Status.Phase = v1.IntegrationPhaseError integration.SetReadyCondition(corev1.ConditionFalse, @@ -72,12 +80,6 @@ func (action *initializeAction) Handle(ctx context.Context, integration *v1.Inte return integration, err } - if integration.Status.Image != "" { - integration.SetBuildOrDeploymentPhase() - - return integration, nil - } - if integration.Status.IntegrationKit == nil { ikt, err := action.lookupIntegrationKit(ctx, integration) if err != nil { @@ -87,6 +89,12 @@ func (action *initializeAction) Handle(ctx context.Context, integration *v1.Inte integration.SetIntegrationKit(ikt) } + if integration.Status.Image != "" { + integration.SetBuildCompletePhase() + + return integration, nil + } + integration.Status.Phase = v1.IntegrationPhaseBuildingKit integration.Status.Version = defaults.Version if timestamp := integration.Status.InitializationTimestamp; timestamp == nil || timestamp.IsZero() { diff --git a/pkg/controller/integration/integration_controller.go b/pkg/controller/integration/integration_controller.go index 56f9115b61..14f7a67511 100644 --- a/pkg/controller/integration/integration_controller.go +++ b/pkg/controller/integration/integration_controller.go @@ -505,6 +505,7 @@ func (r *reconcileIntegration) Reconcile(ctx context.Context, request reconcile. NewInitializeAction(), NewBuildAction(), newBuildKitAction(), + NewBuildCompleteAction(), } if instance.IsSynthetic() { diff --git a/pkg/controller/integration/monitor.go b/pkg/controller/integration/monitor.go index e88d90af30..890c66cb30 100644 --- a/pkg/controller/integration/monitor.go +++ b/pkg/controller/integration/monitor.go @@ -63,8 +63,7 @@ func (action *monitorAction) Name() string { func (action *monitorAction) CanHandle(integration *v1.Integration) bool { return integration.Status.Phase == v1.IntegrationPhaseDeploying || integration.Status.Phase == v1.IntegrationPhaseRunning || - integration.Status.Phase == v1.IntegrationPhaseError || - integration.Status.Phase == v1.IntegrationPhaseBuildComplete + integration.Status.Phase == v1.IntegrationPhaseError } //nolint:nestif @@ -156,15 +155,6 @@ func (action *monitorAction) Handle(ctx context.Context, integration *v1.Integra } action.checkTraitAnnotationsDeprecatedNotice(integration) - if integration.Status.Phase == v1.IntegrationPhaseBuildComplete { - // The following status fields are only filled during execution. - // We must remove them to clear any previous execution status. - integration.Status.Replicas = nil - integration.Status.RemoveCondition(v1.IntegrationConditionReady) - - return integration, nil - } - return action.monitorPods(ctx, environment, integration) } diff --git a/pkg/trait/gc.go b/pkg/trait/gc.go index 0b3e64d9e8..ca4a9a1f64 100644 --- a/pkg/trait/gc.go +++ b/pkg/trait/gc.go @@ -115,23 +115,14 @@ func (t *gcTrait) Configure(e *Environment) (bool, *TraitCondition, error) { // We need to execute this trait only when all resources have been created and // deployed with a new generation if there is was any change during the Integration drift. - return e.IntegrationInRunningPhases() || e.IntegrationInPhase(v1.IntegrationPhaseBuildComplete), nil, nil + return e.IntegrationInRunningPhases() || e.IntegrationInPhase(v1.IntegrationPhaseUnDeploying), nil, nil } func (t *gcTrait) Apply(e *Environment) error { // Garbage collection runs when: // 1. Generation > 1: resource was updated, clean up old generation resources - // 2. BuildComplete phase AND integration has previously been deployed: undeploy scenario - shouldRunGC := e.Integration.GetGeneration() > 1 - - if !shouldRunGC && e.IntegrationInPhase(v1.IntegrationPhaseBuildComplete) { - // Only run GC if integration was previously deployed (undeploy case) - if !hasNeverDeployed(e.Integration) { - shouldRunGC = true - } - } - - if shouldRunGC { + // 2. undeploy scenario + if e.Integration.GetGeneration() > 1 || e.IntegrationInPhase(v1.IntegrationPhaseUnDeploying) { // Register a post action that deletes the existing resources that are labelled // with the previous integration generation(s). // We make the assumption generation is a monotonically increasing strictly positive integer, @@ -204,8 +195,7 @@ func (t *gcTrait) garbageCollectResources(e *Environment) error { // On undeploy, delete all resources regardless of generation. // On generation upgrade, filter to only delete old resources. - isUndeploying := e.IntegrationInPhase(v1.IntegrationPhaseBuildComplete) && !hasNeverDeployed(e.Integration) - if !isUndeploying { + if !e.IntegrationInPhase(v1.IntegrationPhaseUnDeploying) { selector = selector.Add(*generation) } @@ -265,23 +255,6 @@ func canBeDeleted(it *v1.Integration, u unstructured.Unstructured) bool { return false } -// hasNeverDeployed returns true if the integration has never been deployed. -// Checks both DeploymentTimestamp and Ready condition for reliability. -func hasNeverDeployed(integration *v1.Integration) bool { - // Primary check: DeploymentTimestamp is set when deployment is triggered - if integration.Status.DeploymentTimestamp != nil && !integration.Status.DeploymentTimestamp.IsZero() { - return false // has been deployed - } - - // Secondary check: Ready condition becomes true only after successful deployment - readyCond := integration.Status.GetCondition(v1.IntegrationConditionReady) - if readyCond != nil && readyCond.FirstTruthyTime != nil && !readyCond.FirstTruthyTime.IsZero() { - return false - } - - return true -} - // getDeletableTypes returns the list of deletable types resources, inspecting the rules for which the operator SA is allowed in the // Integration namespace. func (t *gcTrait) getDeletableTypes(e *Environment) (map[schema.GroupVersionKind]struct{}, error) { diff --git a/pkg/trait/gc_test.go b/pkg/trait/gc_test.go index f81d216f6c..0b0e16843c 100644 --- a/pkg/trait/gc_test.go +++ b/pkg/trait/gc_test.go @@ -162,7 +162,7 @@ func TestGarbageCollectPreserveResourcesWithSameGeneration(t *testing.T) { func TestGarbageCollectUndeploying(t *testing.T) { gcTrait, environment := createNominalGCTest() - environment.Integration.Status.Phase = v1.IntegrationPhaseBuildComplete + environment.Integration.Status.Phase = v1.IntegrationPhaseUnDeploying // Simulate undeploy scenario: DeploymentTimestamp set means integration was previously deployed now := metav1.Now() @@ -393,54 +393,6 @@ func TestCanResourceBeDeleted(t *testing.T) { assert.True(t, canBeDeleted(it, resThisItOwner)) } -func TestHasNeverDeployed(t *testing.T) { - tests := []struct { - name string - deploymentTimestamp *metav1.Time - readyCondition *v1.IntegrationCondition - expected bool - }{ - { - name: "never deployed - both checks nil", - deploymentTimestamp: nil, - readyCondition: nil, - expected: true, - }, - { - name: "deployed - DeploymentTimestamp set", - deploymentTimestamp: ptr.To(metav1.Now()), - readyCondition: nil, - expected: false, - }, - { - name: "deployed - Ready FirstTruthyTime set", - deploymentTimestamp: nil, - readyCondition: &v1.IntegrationCondition{ - Type: v1.IntegrationConditionReady, - Status: corev1.ConditionTrue, - FirstTruthyTime: ptr.To(metav1.Now()), - }, - expected: false, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - integration := &v1.Integration{ - Status: v1.IntegrationStatus{ - DeploymentTimestamp: tt.deploymentTimestamp, - }, - } - if tt.readyCondition != nil { - integration.Status.Conditions = []v1.IntegrationCondition{*tt.readyCondition} - } - - result := hasNeverDeployed(integration) - assert.Equal(t, tt.expected, result) - }) - } -} - func TestGarbageCollectDryBuildSkipsGC(t *testing.T) { gcTrait, environment := createNominalGCTest() environment.Integration.Status.Phase = v1.IntegrationPhaseBuildComplete diff --git a/pkg/trait/gitops.go b/pkg/trait/gitops.go index 667455e3dd..f177f5ec9f 100644 --- a/pkg/trait/gitops.go +++ b/pkg/trait/gitops.go @@ -59,7 +59,7 @@ func (t *gitOpsTrait) Configure(e *Environment) (bool, *TraitCondition, error) { return false, nil, nil } - return e.IntegrationInPhase(v1.IntegrationPhaseDeploying, v1.IntegrationPhaseBuildComplete), nil, nil + return e.IntegrationInPhase(v1.IntegrationPhaseBuildComplete), nil, nil } func (t *gitOpsTrait) Apply(e *Environment) error { diff --git a/pkg/trait/gitops_test.go b/pkg/trait/gitops_test.go index beae0563f5..7d7c978ee7 100644 --- a/pkg/trait/gitops_test.go +++ b/pkg/trait/gitops_test.go @@ -42,7 +42,7 @@ func TestGitOpsAddAction(t *testing.T) { env := &Environment{ Integration: &v1.Integration{ Status: v1.IntegrationStatus{ - Phase: v1.IntegrationPhaseDeploying, + Phase: v1.IntegrationPhaseBuildComplete, }, }, PostActions: []func(*Environment) error{},