Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions docs/modules/ROOT/pages/running/dry-build.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
2 changes: 2 additions & 0 deletions pkg/apis/camel/v1/integration_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,8 @@ const (
IntegrationPhaseBuildComplete IntegrationPhase = "Build Complete"
// IntegrationPhaseDeploying --.
IntegrationPhaseDeploying IntegrationPhase = "Deploying"
// IntegrationPhaseUnDeploying --.
IntegrationPhaseUnDeploying IntegrationPhase = "Undeploying"
// IntegrationPhaseRunning --.
IntegrationPhaseRunning IntegrationPhase = "Running"
// IntegrationPhaseError --.
Expand Down
18 changes: 10 additions & 8 deletions pkg/apis/camel/v1/integration_types_support.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
6 changes: 1 addition & 5 deletions pkg/cmd/deploy.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
)

Expand Down Expand Up @@ -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)
Expand Down
2 changes: 1 addition & 1 deletion pkg/cmd/undeploy.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand Down
2 changes: 1 addition & 1 deletion pkg/controller/integration/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
67 changes: 67 additions & 0 deletions pkg/controller/integration/build_complete.go
Original file line number Diff line number Diff line change
@@ -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
}
89 changes: 89 additions & 0 deletions pkg/controller/integration/build_complete_test.go
Original file line number Diff line number Diff line change
@@ -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)
}
7 changes: 4 additions & 3 deletions pkg/controller/integration/build_kit.go
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions pkg/controller/integration/build_kit_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand Down
2 changes: 1 addition & 1 deletion pkg/controller/integration/build_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand Down
24 changes: 16 additions & 8 deletions pkg/controller/integration/initialize.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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,
Expand All @@ -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 {
Expand All @@ -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() {
Expand Down
1 change: 1 addition & 0 deletions pkg/controller/integration/integration_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -505,6 +505,7 @@ func (r *reconcileIntegration) Reconcile(ctx context.Context, request reconcile.
NewInitializeAction(),
NewBuildAction(),
newBuildKitAction(),
NewBuildCompleteAction(),
}

if instance.IsSynthetic() {
Expand Down
12 changes: 1 addition & 11 deletions pkg/controller/integration/monitor.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)
}

Expand Down
Loading
Loading