diff --git a/pkg/controller/integration/integration_controller.go b/pkg/controller/integration/integration_controller.go index 3e28398841..730368b44e 100644 --- a/pkg/controller/integration/integration_controller.go +++ b/pkg/controller/integration/integration_controller.go @@ -19,8 +19,6 @@ package integration import ( "context" - "github.com/pkg/errors" - appsv1 "k8s.io/api/apps/v1" k8serrors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/runtime" @@ -257,7 +255,7 @@ func (r *ReconcileIntegration) Reconcile(request reconcile.Request) (reconcile.R target.SetIntegrationPlatform(pl) } - return r.update(ctx, targetLog, target) + return r.update(ctx, &instance, target) } return reconcile.Result{}, err @@ -280,21 +278,11 @@ func (r *ReconcileIntegration) Reconcile(request reconcile.Request) (reconcile.R newTarget, err := a.Handle(ctx, target) if err != nil { - // Some traits, like the deployment and knative service ones, - // update owned resources in the running phase, so it's better - // handling update conflicts gracefully, consistently with the - // primary integration update requests. - if cause := errors.Cause(err); k8serrors.IsConflict(cause) { - log.Error(cause, "conflict") - return reconcile.Result{ - Requeue: true, - }, nil - } return reconcile.Result{}, err } if newTarget != nil { - if r, err := r.update(ctx, targetLog, newTarget); err != nil { + if r, err := r.update(ctx, &instance, newTarget); err != nil { return r, err } @@ -316,8 +304,7 @@ func (r *ReconcileIntegration) Reconcile(request reconcile.Request) (reconcile.R return reconcile.Result{}, nil } -// Update -- -func (r *ReconcileIntegration) update(ctx context.Context, log log.Logger, target *v1alpha1.Integration) (reconcile.Result, error) { +func (r *ReconcileIntegration) update(ctx context.Context, base *v1alpha1.Integration, target *v1alpha1.Integration) (reconcile.Result, error) { dgst, err := digest.ComputeForIntegration(target) if err != nil { return reconcile.Result{}, err @@ -325,16 +312,7 @@ func (r *ReconcileIntegration) update(ctx context.Context, log log.Logger, targe target.Status.Digest = dgst - err = r.client.Status().Update(ctx, target) - if err != nil { - if k8serrors.IsConflict(err) { - log.Error(err, "conflict") - - return reconcile.Result{ - Requeue: true, - }, nil - } - } + err = r.client.Status().Patch(ctx, target, k8sclient.MergeFrom(base)) return reconcile.Result{}, err } diff --git a/pkg/controller/integration/monitor.go b/pkg/controller/integration/monitor.go index 85d5a5a34b..1cad996068 100644 --- a/pkg/controller/integration/monitor.go +++ b/pkg/controller/integration/monitor.go @@ -62,8 +62,7 @@ func (action *monitorAction) Handle(ctx context.Context, integration *v1alpha1.I return integration, nil } - // Run traits that are enabled for the running phase, - // such as the deployment, garbage collector and Knative service traits. + // Run traits that are enabled for the running phase _, err = trait.Apply(ctx, action.client, integration, nil) if err != nil { return nil, err diff --git a/pkg/trait/affinity.go b/pkg/trait/affinity.go index c285c6e8b6..1c921e4e92 100644 --- a/pkg/trait/affinity.go +++ b/pkg/trait/affinity.go @@ -66,7 +66,7 @@ func (t *affinityTrait) Configure(e *Environment) (bool, error) { return false, fmt.Errorf("both pod affinity and pod anti-affinity can't be set simultaneously") } - return e.IntegrationInPhase(v1alpha1.IntegrationPhaseDeploying), nil + return e.IntegrationInPhase(v1alpha1.IntegrationPhaseDeploying, v1alpha1.IntegrationPhaseRunning), nil } func (t *affinityTrait) Apply(e *Environment) (err error) { diff --git a/pkg/trait/classpath.go b/pkg/trait/classpath.go index 14814f0802..1a2a643880 100644 --- a/pkg/trait/classpath.go +++ b/pkg/trait/classpath.go @@ -49,7 +49,8 @@ func (t *classpathTrait) Configure(e *Environment) (bool, error) { return false, nil } - return e.InPhase(v1alpha1.IntegrationKitPhaseReady, v1alpha1.IntegrationPhaseDeploying), nil + return e.InPhase(v1alpha1.IntegrationKitPhaseReady, v1alpha1.IntegrationPhaseDeploying) || + e.InPhase(v1alpha1.IntegrationKitPhaseReady, v1alpha1.IntegrationPhaseRunning), nil } func (t *classpathTrait) Apply(e *Environment) error { diff --git a/pkg/trait/container.go b/pkg/trait/container.go index c37775cc93..6bd3ae68ef 100644 --- a/pkg/trait/container.go +++ b/pkg/trait/container.go @@ -82,7 +82,7 @@ func (t *containerTrait) Configure(e *Environment) (bool, error) { return false, nil } - if !e.IntegrationInPhase(v1alpha1.IntegrationPhaseDeploying) { + if !e.IntegrationInPhase(v1alpha1.IntegrationPhaseDeploying, v1alpha1.IntegrationPhaseRunning) { return false, nil } diff --git a/pkg/trait/debug.go b/pkg/trait/debug.go index 7dcc4302a8..d9d30bfd6d 100644 --- a/pkg/trait/debug.go +++ b/pkg/trait/debug.go @@ -40,7 +40,7 @@ func newDebugTrait() *debugTrait { func (t *debugTrait) Configure(e *Environment) (bool, error) { if t.Enabled != nil && *t.Enabled { - return e.IntegrationInPhase(v1alpha1.IntegrationPhaseDeploying), nil + return e.IntegrationInPhase(v1alpha1.IntegrationPhaseDeploying, v1alpha1.IntegrationPhaseRunning), nil } return false, nil diff --git a/pkg/trait/deployer.go b/pkg/trait/deployer.go index b5a8bebaa9..a996f5b00f 100644 --- a/pkg/trait/deployer.go +++ b/pkg/trait/deployer.go @@ -17,6 +17,23 @@ limitations under the License. package trait +import ( + "reflect" + + "github.com/pkg/errors" + + jsonpatch "github.com/evanphx/json-patch" + + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/types" + "k8s.io/apimachinery/pkg/util/json" + + "sigs.k8s.io/controller-runtime/pkg/client" + + "github.com/apache/camel-k/pkg/apis/camel/v1alpha1" + "github.com/apache/camel-k/pkg/util/kubernetes" +) + // The deployer trait can be used to explicitly select the kind of high level resource that // will deploy the integration. // @@ -34,10 +51,57 @@ func newDeployerTrait() *deployerTrait { } func (t *deployerTrait) Configure(e *Environment) (bool, error) { - return true, nil + return e.IntegrationInPhase( + v1alpha1.IntegrationPhaseInitialization, + v1alpha1.IntegrationPhaseDeploying, + v1alpha1.IntegrationPhaseRunning, + ), nil } func (t *deployerTrait) Apply(e *Environment) error { + switch e.Integration.Status.Phase { + + case v1alpha1.IntegrationPhaseInitialization, v1alpha1.IntegrationPhaseDeploying: + // Register a post action that updates the resources generated by the traits + e.PostActions = append(e.PostActions, func(env *Environment) error { + if err := kubernetes.ReplaceResources(env.C, env.Client, env.Resources.Items()); err != nil { + return errors.Wrap(err, "error during replace resource") + } + return nil + }) + + case v1alpha1.IntegrationPhaseRunning: + // Register a post action that patches the resources generated by the traits + e.PostActions = append(e.PostActions, func(env *Environment) error { + for _, resource := range env.Resources.Items() { + key, err := client.ObjectKeyFromObject(resource) + if err != nil { + return err + } + + object := resource.DeepCopyObject() + err = env.Client.Get(env.C, key, object) + if err != nil { + return err + } + + patch, err := positiveMergePatch(object, resource) + if err != nil { + return err + } else if len(patch) == 0 { + // Avoid triggering a patch request for nothing + continue + } + + err = env.Client.Patch(env.C, resource, client.ConstantPatch(types.MergePatchType, patch)) + if err != nil { + return errors.Wrap(err, "error during patch resource") + } + } + return nil + }) + } + return nil } @@ -45,3 +109,67 @@ func (t *deployerTrait) Apply(e *Environment) error { func (t *deployerTrait) IsPlatformTrait() bool { return true } + +func positiveMergePatch(source runtime.Object, target runtime.Object) ([]byte, error) { + sourceJSON, err := json.Marshal(source) + if err != nil { + return nil, err + } + + targetJSON, err := json.Marshal(target) + if err != nil { + return nil, err + } + + mergePatch, err := jsonpatch.CreateMergePatch(sourceJSON, targetJSON) + if err != nil { + return nil, err + } + + var positivePatch map[string]interface{} + err = json.Unmarshal(mergePatch, &positivePatch) + if err != nil { + return nil, err + } + + // The following is a work-around to remove null fields from the JSON merge patch, + // so that values defaulted by controllers server-side are not deleted. + // It's generally acceptable as these values are orthogonal to the values managed + // by the traits. + removeNilValues(reflect.ValueOf(positivePatch), reflect.Value{}) + + // Return an empty patch if no keys remain + if len(positivePatch) == 0 { + return make([]byte, 0), nil + } + + return json.Marshal(positivePatch) +} + +func removeNilValues(v reflect.Value, parent reflect.Value) { + for v.Kind() == reflect.Ptr || v.Kind() == reflect.Interface { + v = v.Elem() + } + switch v.Kind() { + case reflect.Array, reflect.Slice: + for i := 0; i < v.Len(); i++ { + removeNilValues(v.Index(i), v) + } + case reflect.Map: + for _, k := range v.MapKeys() { + switch c := v.MapIndex(k); { + case !c.IsValid(): + // Skip keys previously deleted + continue + case c.IsNil(), c.Elem().Kind() == reflect.Map && len(c.Elem().MapKeys()) == 0: + v.SetMapIndex(k, reflect.Value{}) + default: + removeNilValues(c, v) + } + } + // Back process the parent map in case it has been emptied so that it's deleted as well + if len(v.MapKeys()) == 0 && parent.Kind() == reflect.Map { + removeNilValues(parent, reflect.Value{}) + } + } +} diff --git a/pkg/trait/deployment.go b/pkg/trait/deployment.go index daf3f09b5e..818fc9f042 100644 --- a/pkg/trait/deployment.go +++ b/pkg/trait/deployment.go @@ -18,14 +18,10 @@ limitations under the License. package trait import ( - "context" - appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "sigs.k8s.io/controller-runtime/pkg/client" - "github.com/apache/camel-k/pkg/apis/camel/v1alpha1" ) @@ -97,7 +93,6 @@ func (t *deploymentTrait) Configure(e *Environment) (bool, error) { func (t *deploymentTrait) Apply(e *Environment) error { if e.IntegrationKitInPhase(v1alpha1.IntegrationKitPhaseReady) && e.IntegrationInPhase(v1alpha1.IntegrationPhaseBuildingKit, v1alpha1.IntegrationPhaseResolvingKit) { - e.PostProcessors = append(e.PostProcessors, func(environment *Environment) error { // trigger integration deploy e.Integration.Status.Phase = v1alpha1.IntegrationPhaseDeploying @@ -107,44 +102,33 @@ func (t *deploymentTrait) Apply(e *Environment) error { return nil } - if e.InPhase(v1alpha1.IntegrationKitPhaseReady, v1alpha1.IntegrationPhaseDeploying) { + if e.InPhase(v1alpha1.IntegrationKitPhaseReady, v1alpha1.IntegrationPhaseDeploying) || + e.InPhase(v1alpha1.IntegrationKitPhaseReady, v1alpha1.IntegrationPhaseRunning) { maps := e.ComputeConfigMaps() - depl := t.getDeploymentFor(e) + deployment := t.getDeploymentFor(e) e.Resources.AddAll(maps) - e.Resources.Add(depl) + e.Resources.Add(deployment) e.Integration.Status.SetCondition( v1alpha1.IntegrationConditionDeploymentAvailable, corev1.ConditionTrue, v1alpha1.IntegrationConditionDeploymentAvailableReason, - depl.Name, + deployment.Name, ) - return nil - } - - if e.IntegrationInPhase(v1alpha1.IntegrationPhaseRunning) { - // Reconcile the deployment replicas - deployment := &appsv1.Deployment{} - err := t.client.Get(context.TODO(), client.ObjectKey{Namespace: e.Integration.Namespace, Name: e.Integration.Name}, deployment) - if err != nil { - return err - } - replicas := e.Integration.Spec.Replicas - // Deployment replicas defaults to 1, so we avoid forcing - // an update to nil that will result to another update cycle - // back to that default value by the Deployment controller. - if replicas == nil && *deployment.Spec.Replicas != 1 || - replicas != nil && *deployment.Spec.Replicas != *replicas { - deployment.Spec.Replicas = replicas - err := t.client.Update(context.TODO(), deployment) - if err != nil { - return err + if e.IntegrationInPhase(v1alpha1.IntegrationPhaseRunning) { + // Reconcile the deployment replicas + replicas := e.Integration.Spec.Replicas + // Deployment replicas defaults to 1, so we avoid forcing + // an update to nil that will result to another update cycle + // back to that default value by the Deployment controller. + if replicas == nil { + one := int32(1) + replicas = &one } + deployment.Spec.Replicas = replicas } - - return nil } return nil diff --git a/pkg/trait/deployment_test.go b/pkg/trait/deployment_test.go index 7fa84507ed..15a30df420 100644 --- a/pkg/trait/deployment_test.go +++ b/pkg/trait/deployment_test.go @@ -24,9 +24,8 @@ import ( "github.com/apache/camel-k/pkg/apis/camel/v1alpha1" "github.com/apache/camel-k/pkg/util/kubernetes" "github.com/apache/camel-k/pkg/util/test" - "github.com/stretchr/testify/assert" - "sigs.k8s.io/controller-runtime/pkg/client" + "github.com/stretchr/testify/assert" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" @@ -132,14 +131,13 @@ func TestApplyDeploymentTraitWhileRunningIntegrationDoesSucceed(t *testing.T) { assert.Nil(t, err) - deployment := &appsv1.Deployment{} - err = deploymentTrait.client.Get(context.TODO(), client.ObjectKey{Namespace: "namespace", Name: "integration-name"}, deployment) - assert.Nil(t, err) + deployment := environment.Resources.GetDeployment(func(deployment *appsv1.Deployment) bool { return true }) + assert.NotNil(t, deployment) + assert.Equal(t, "integration-name", deployment.Name) assert.Equal(t, int32(3), *deployment.Spec.Replicas) } func createNominalDeploymentTest() (*deploymentTrait, *Environment) { - trait := newDeploymentTrait() enabled := true trait.Enabled = &enabled diff --git a/pkg/trait/environment.go b/pkg/trait/environment.go index 137b3ff132..04774f7711 100644 --- a/pkg/trait/environment.go +++ b/pkg/trait/environment.go @@ -53,7 +53,7 @@ func newEnvironmentTrait() *environmentTrait { func (t *environmentTrait) Configure(e *Environment) (bool, error) { if t.Enabled == nil || *t.Enabled { - return e.IntegrationInPhase(v1alpha1.IntegrationPhaseDeploying), nil + return e.IntegrationInPhase(v1alpha1.IntegrationPhaseDeploying, v1alpha1.IntegrationPhaseRunning), nil } return false, nil diff --git a/pkg/trait/gc.go b/pkg/trait/gc.go index 11b8ccb0ba..b35a4a87f9 100644 --- a/pkg/trait/gc.go +++ b/pkg/trait/gc.go @@ -90,35 +90,35 @@ func (t *garbageCollectorTrait) Configure(e *Environment) (bool, error) { } func (t *garbageCollectorTrait) Apply(e *Environment) error { - if e.IntegrationInPhase(v1alpha1.IntegrationPhaseInitialization, v1alpha1.IntegrationPhaseDeploying) { + switch e.Integration.Status.Phase { + + case v1alpha1.IntegrationPhaseRunning: + // Register a post action that deletes the existing resources that are labelled + // with the previous integration generations. + // TODO: this should be refined so that it's run when all the replicas for the newer generation + // are ready. This is to be added when the integration scale status is refined with ready replicas + e.PostActions = append(e.PostActions, func(env *Environment) error { + // The collection and deletion are performed asynchronously to avoid blocking + // the reconcile loop. + go t.garbageCollectResources(env) + return nil + }) + fallthrough + + default: // Register a post processor that adds the required labels to the new resources e.PostProcessors = append(e.PostProcessors, func(env *Environment) error { + generation := strconv.FormatInt(env.Integration.GetGeneration(), 10) env.Resources.VisitMetaObject(func(resource metav1.Object) { labels := resource.GetLabels() - if labels == nil { - labels = map[string]string{} - } // Label the resource with the current integration generation - labels["camel.apache.org/generation"] = strconv.FormatInt(env.Integration.GetGeneration(), 10) + labels["camel.apache.org/generation"] = generation // Make sure the integration label is set labels["camel.apache.org/integration"] = env.Integration.Name resource.SetLabels(labels) }) return nil }) - } else if e.IntegrationInPhase(v1alpha1.IntegrationPhaseRunning) { - // Let's run garbage collection during the integration running phase - // TODO: this should be refined so that it's run when all the replicas for the newer generation - // are ready. This is to be added when the integration scale status is refined with ready replicas - - // Register a post action that deletes the existing resources that are labelled - // with the previous integration generations. - e.PostActions = append(e.PostActions, func(environment *Environment) error { - // The collection and deletion are performed asynchronously to avoid blocking - // the reconcile loop. - go t.garbageCollectResources(e) - return nil - }) } return nil diff --git a/pkg/trait/gc_test.go b/pkg/trait/gc_test.go index 776663801a..5a49058867 100644 --- a/pkg/trait/gc_test.go +++ b/pkg/trait/gc_test.go @@ -51,7 +51,7 @@ func TestApplyGarbageCollectorTraitDoesSucceed(t *testing.T) { err := gcTrait.Apply(environment) assert.Nil(t, err) - assert.Len(t, environment.PostProcessors, 0) + assert.Len(t, environment.PostProcessors, 1) assert.Len(t, environment.PostActions, 1) } diff --git a/pkg/trait/ingress.go b/pkg/trait/ingress.go index 0b99cdd37d..75af6f94ee 100644 --- a/pkg/trait/ingress.go +++ b/pkg/trait/ingress.go @@ -62,7 +62,7 @@ func (t *ingressTrait) Configure(e *Environment) (bool, error) { return false, nil } - if !e.IntegrationInPhase(v1alpha1.IntegrationPhaseDeploying) { + if !e.IntegrationInPhase(v1alpha1.IntegrationPhaseDeploying, v1alpha1.IntegrationPhaseRunning) { return false, nil } diff --git a/pkg/trait/istio.go b/pkg/trait/istio.go index abe3f81f6a..4b8a9046e1 100644 --- a/pkg/trait/istio.go +++ b/pkg/trait/istio.go @@ -20,9 +20,11 @@ package trait import ( "strconv" - "github.com/apache/camel-k/pkg/apis/camel/v1alpha1" appsv1 "k8s.io/api/apps/v1" + servingv1 "knative.dev/serving/pkg/apis/serving/v1" + + "github.com/apache/camel-k/pkg/apis/camel/v1alpha1" ) // The Istio trait allows to configure properties related to the Istio service mesh, @@ -51,7 +53,7 @@ func newIstioTrait() *istioTrait { func (t *istioTrait) Configure(e *Environment) (bool, error) { if t.Enabled != nil && *t.Enabled { - return e.IntegrationInPhase(v1alpha1.IntegrationPhaseDeploying), nil + return e.IntegrationInPhase(v1alpha1.IntegrationPhaseDeploying, v1alpha1.IntegrationPhaseRunning), nil } return false, nil diff --git a/pkg/trait/jolokia.go b/pkg/trait/jolokia.go index 90c590dad6..8a8b80dcf0 100644 --- a/pkg/trait/jolokia.go +++ b/pkg/trait/jolokia.go @@ -96,7 +96,7 @@ func (t *jolokiaTrait) Configure(e *Environment) (bool, error) { setDefaultJolokiaOption(options, &t.UseSslClientAuthentication, "useSslClientAuthentication", true) } - return e.IntegrationInPhase(v1alpha1.IntegrationPhaseDeploying), nil + return e.IntegrationInPhase(v1alpha1.IntegrationPhaseDeploying, v1alpha1.IntegrationPhaseRunning), nil } func (t *jolokiaTrait) Apply(e *Environment) (err error) { diff --git a/pkg/trait/jolokia_test.go b/pkg/trait/jolokia_test.go index 5474fde098..abf43f9717 100644 --- a/pkg/trait/jolokia_test.go +++ b/pkg/trait/jolokia_test.go @@ -55,7 +55,7 @@ func TestConfigureJolokiaTraitInWrongPhaseDoesNotSucceed(t *testing.T) { configured, err := trait.Configure(environment) assert.Nil(t, err) - assert.False(t, configured) + assert.True(t, configured) } func TestConfigureJolokiaTraitWithUnparseableOptionsDoesNotSucceed(t *testing.T) { diff --git a/pkg/trait/knative.go b/pkg/trait/knative.go index 368d6f8bf0..13f36ffbc5 100644 --- a/pkg/trait/knative.go +++ b/pkg/trait/knative.go @@ -95,7 +95,7 @@ func (t *knativeTrait) Configure(e *Environment) (bool, error) { return false, nil } - if !e.IntegrationInPhase(v1alpha1.IntegrationPhaseDeploying) { + if !e.IntegrationInPhase(v1alpha1.IntegrationPhaseDeploying, v1alpha1.IntegrationPhaseRunning) { return false, nil } diff --git a/pkg/trait/knative_service.go b/pkg/trait/knative_service.go index 9fa8089977..b57f76f770 100644 --- a/pkg/trait/knative_service.go +++ b/pkg/trait/knative_service.go @@ -18,11 +18,8 @@ limitations under the License. package trait import ( - "context" "strconv" - "sigs.k8s.io/controller-runtime/pkg/client" - corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -170,18 +167,24 @@ func (t *knativeServiceTrait) Configure(e *Environment) (bool, error) { } func (t *knativeServiceTrait) Apply(e *Environment) error { - if e.IntegrationInPhase(v1alpha1.IntegrationPhaseRunning) { - // Reconcile the Knative scale annotations - service := &serving.Service{} - err := t.client.Get(context.TODO(), client.ObjectKey{Namespace: e.Integration.Namespace, Name: e.Integration.Name}, service) - if err != nil { - return err - } + ksvc := t.getServiceFor(e) + maps := e.ComputeConfigMaps() + e.Resources.AddAll(maps) + e.Resources.Add(ksvc) + + e.Integration.Status.SetCondition( + v1alpha1.IntegrationConditionKnativeServiceAvailable, + corev1.ConditionTrue, + v1alpha1.IntegrationConditionKnativeServiceAvailableReason, + ksvc.Name, + ) + + if e.IntegrationInPhase(v1alpha1.IntegrationPhaseRunning) { replicas := e.Integration.Spec.Replicas isUpdateRequired := false - minScale, ok := service.Spec.Template.Annotations[knativeServingMinScaleAnnotation] + minScale, ok := ksvc.Spec.Template.Annotations[knativeServingMinScaleAnnotation] if ok { min, err := strconv.Atoi(minScale) if err != nil { @@ -194,7 +197,7 @@ func (t *knativeServiceTrait) Apply(e *Environment) error { isUpdateRequired = true } - maxScale, ok := service.Spec.Template.Annotations[knativeServingMaxScaleAnnotation] + maxScale, ok := ksvc.Spec.Template.Annotations[knativeServingMaxScaleAnnotation] if ok { max, err := strconv.Atoi(maxScale) if err != nil { @@ -210,42 +213,23 @@ func (t *knativeServiceTrait) Apply(e *Environment) error { if isUpdateRequired { if replicas == nil { if t.MinScale != nil && *t.MinScale > 0 { - service.Spec.Template.Annotations[knativeServingMinScaleAnnotation] = strconv.Itoa(*t.MinScale) + ksvc.Spec.Template.Annotations[knativeServingMinScaleAnnotation] = strconv.Itoa(*t.MinScale) } else { - delete(service.Spec.Template.Annotations, knativeServingMinScaleAnnotation) + delete(ksvc.Spec.Template.Annotations, knativeServingMinScaleAnnotation) } if t.MaxScale != nil && *t.MaxScale > 0 { - service.Spec.Template.Annotations[knativeServingMaxScaleAnnotation] = strconv.Itoa(*t.MaxScale) + ksvc.Spec.Template.Annotations[knativeServingMaxScaleAnnotation] = strconv.Itoa(*t.MaxScale) } else { - delete(service.Spec.Template.Annotations, knativeServingMaxScaleAnnotation) + delete(ksvc.Spec.Template.Annotations, knativeServingMaxScaleAnnotation) } } else { scale := strconv.Itoa(int(*replicas)) - service.Spec.Template.Annotations[knativeServingMinScaleAnnotation] = scale - service.Spec.Template.Annotations[knativeServingMaxScaleAnnotation] = scale - } - err := t.client.Update(context.TODO(), service) - if err != nil { - return err + ksvc.Spec.Template.Annotations[knativeServingMinScaleAnnotation] = scale + ksvc.Spec.Template.Annotations[knativeServingMaxScaleAnnotation] = scale } } - - return nil } - ksvc := t.getServiceFor(e) - maps := e.ComputeConfigMaps() - - e.Resources.AddAll(maps) - e.Resources.Add(ksvc) - - e.Integration.Status.SetCondition( - v1alpha1.IntegrationConditionKnativeServiceAvailable, - corev1.ConditionTrue, - v1alpha1.IntegrationConditionKnativeServiceAvailableReason, - ksvc.Name, - ) - return nil } diff --git a/pkg/trait/owner.go b/pkg/trait/owner.go index 37c7d64663..3b1ff118b1 100644 --- a/pkg/trait/owner.go +++ b/pkg/trait/owner.go @@ -54,7 +54,7 @@ func (t *ownerTrait) Configure(e *Environment) (bool, error) { return false, nil } - return e.IntegrationInPhase(v1alpha1.IntegrationPhaseDeploying), nil + return e.IntegrationInPhase(v1alpha1.IntegrationPhaseDeploying, v1alpha1.IntegrationPhaseRunning), nil } func (t *ownerTrait) Apply(e *Environment) error { diff --git a/pkg/trait/probes.go b/pkg/trait/probes.go index 5731d6f6f6..8ab2ba3b31 100644 --- a/pkg/trait/probes.go +++ b/pkg/trait/probes.go @@ -73,7 +73,11 @@ func newProbesTrait() *probesTrait { func (t *probesTrait) Configure(e *Environment) (bool, error) { if t.Enabled != nil && *t.Enabled { - return e.IntegrationInPhase(v1alpha1.IntegrationPhaseInitialization) || e.IntegrationInPhase(v1alpha1.IntegrationPhaseDeploying), nil + return e.IntegrationInPhase( + v1alpha1.IntegrationPhaseInitialization, + v1alpha1.IntegrationPhaseDeploying, + v1alpha1.IntegrationPhaseRunning, + ), nil } return false, nil @@ -100,7 +104,7 @@ func (t *probesTrait) Apply(e *Environment) error { ) } - if e.IntegrationInPhase(v1alpha1.IntegrationPhaseDeploying) { + if e.IntegrationInPhase(v1alpha1.IntegrationPhaseDeploying, v1alpha1.IntegrationPhaseRunning) { e.Resources.VisitDeployment(func(deployment *appsv1.Deployment) { if len(deployment.Spec.Template.Spec.Containers) != 1 { return diff --git a/pkg/trait/prometheus.go b/pkg/trait/prometheus.go index 84a8a73c3e..8cc6ad0d36 100644 --- a/pkg/trait/prometheus.go +++ b/pkg/trait/prometheus.go @@ -64,7 +64,7 @@ func newPrometheusTrait() *prometheusTrait { } func (t *prometheusTrait) Configure(e *Environment) (bool, error) { - return e.IntegrationInPhase(v1alpha1.IntegrationPhaseDeploying), nil + return e.IntegrationInPhase(v1alpha1.IntegrationPhaseDeploying, v1alpha1.IntegrationPhaseRunning), nil } func (t *prometheusTrait) Apply(e *Environment) (err error) { diff --git a/pkg/trait/route.go b/pkg/trait/route.go index adc8c378c1..7ca59e176f 100644 --- a/pkg/trait/route.go +++ b/pkg/trait/route.go @@ -21,12 +21,13 @@ import ( "fmt" "reflect" - "github.com/apache/camel-k/pkg/apis/camel/v1alpha1" - - routev1 "github.com/openshift/api/route/v1" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/util/intstr" + + routev1 "github.com/openshift/api/route/v1" + + "github.com/apache/camel-k/pkg/apis/camel/v1alpha1" ) // The Route trait can be used to configure the creation of OpenShift routes for the integration. @@ -86,7 +87,7 @@ func (t *routeTrait) Configure(e *Environment) (bool, error) { return false, nil } - if !e.IntegrationInPhase(v1alpha1.IntegrationPhaseDeploying) { + if !e.IntegrationInPhase(v1alpha1.IntegrationPhaseDeploying, v1alpha1.IntegrationPhaseRunning) { return false, nil } @@ -115,11 +116,14 @@ func (t *routeTrait) Apply(e *Environment) error { route := routev1.Route{ TypeMeta: metav1.TypeMeta{ Kind: "Route", - APIVersion: routev1.SchemeGroupVersion.String(), + APIVersion: routev1.GroupVersion.String(), }, ObjectMeta: metav1.ObjectMeta{ Name: t.service.Name, Namespace: t.service.Namespace, + Labels: map[string]string{ + "camel.apache.org/integration": e.Integration.Name, + }, }, Spec: routev1.RouteSpec{ Port: &routev1.RoutePort{ diff --git a/pkg/trait/service.go b/pkg/trait/service.go index e58ff68776..8511dd9509 100644 --- a/pkg/trait/service.go +++ b/pkg/trait/service.go @@ -64,7 +64,7 @@ func (t *serviceTrait) Configure(e *Environment) (bool, error) { return false, nil } - if !e.IntegrationInPhase(v1alpha1.IntegrationPhaseDeploying) { + if !e.IntegrationInPhase(v1alpha1.IntegrationPhaseDeploying, v1alpha1.IntegrationPhaseRunning) { return false, nil } @@ -102,8 +102,8 @@ func (t *serviceTrait) Apply(e *Environment) error { // add a new service if not already created if svc == nil { svc = getServiceFor(e) - e.Resources.Add(svc) } + e.Resources.Add(svc) return nil } diff --git a/pkg/trait/trait.go b/pkg/trait/trait.go index 8e50c2ce29..8d691bcc1c 100644 --- a/pkg/trait/trait.go +++ b/pkg/trait/trait.go @@ -47,13 +47,6 @@ func Apply(ctx context.Context, c client.Client, integration *v1alpha1.Integrati return nil, errors.Wrap(err, "error during trait customization") } - // replace resources created by the trait - if environment.Resources != nil { - if err := kubernetes.ReplaceResources(ctx, c, environment.Resources.Items()); err != nil { - return nil, errors.Wrap(err, "error during replace resource") - } - } - // execute post actions registered by traits for _, postAction := range environment.PostActions { err := postAction(environment) diff --git a/pkg/trait/trait_catalog.go b/pkg/trait/trait_catalog.go index ee7f698692..87316407c3 100644 --- a/pkg/trait/trait_catalog.go +++ b/pkg/trait/trait_catalog.go @@ -140,7 +140,6 @@ func (c *Catalog) TraitsForProfile(profile v1alpha1.TraitProfile) []Trait { case v1alpha1.TraitProfileOpenShift: return []Trait{ c.tCamel, - c.tGarbageCollector, c.tDebug, c.tRestDsl, c.tDependencies, @@ -149,6 +148,7 @@ func (c *Catalog) TraitsForProfile(profile v1alpha1.TraitProfile) []Trait { c.tEnvironment, c.tDeployer, c.tDeployment, + c.tGarbageCollector, c.tAffinity, c.tService, c.tContainer, @@ -163,7 +163,6 @@ func (c *Catalog) TraitsForProfile(profile v1alpha1.TraitProfile) []Trait { case v1alpha1.TraitProfileKubernetes: return []Trait{ c.tCamel, - c.tGarbageCollector, c.tDebug, c.tRestDsl, c.tDependencies, @@ -172,6 +171,7 @@ func (c *Catalog) TraitsForProfile(profile v1alpha1.TraitProfile) []Trait { c.tEnvironment, c.tDeployer, c.tDeployment, + c.tGarbageCollector, c.tAffinity, c.tService, c.tContainer, @@ -186,7 +186,6 @@ func (c *Catalog) TraitsForProfile(profile v1alpha1.TraitProfile) []Trait { case v1alpha1.TraitProfileKnative: return []Trait{ c.tCamel, - c.tGarbageCollector, c.tDebug, c.tRestDsl, c.tKnative, @@ -196,6 +195,7 @@ func (c *Catalog) TraitsForProfile(profile v1alpha1.TraitProfile) []Trait { c.tEnvironment, c.tDeployer, c.tDeployment, + c.tGarbageCollector, c.tAffinity, c.tKnativeService, c.tContainer, diff --git a/pkg/util/kubernetes/collection.go b/pkg/util/kubernetes/collection.go index eb6a00d45f..a56f4d0789 100644 --- a/pkg/util/kubernetes/collection.go +++ b/pkg/util/kubernetes/collection.go @@ -35,12 +35,12 @@ type Collection struct { } // NewCollection creates a new empty collection -func NewCollection(objcts ...runtime.Object) *Collection { +func NewCollection(objects ...runtime.Object) *Collection { collection := Collection{ - items: make([]runtime.Object, 0, len(objcts)), + items: make([]runtime.Object, 0, len(objects)), } - collection.items = append(collection.items, objcts...) + collection.items = append(collection.items, objects...) return &collection }