| /* |
| 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 event |
| |
| import ( |
| "context" |
| "fmt" |
| |
| corev1 "k8s.io/api/core/v1" |
| "k8s.io/apimachinery/pkg/runtime" |
| "k8s.io/client-go/tools/record" |
| |
| ctrl "sigs.k8s.io/controller-runtime/pkg/client" |
| |
| v1 "github.com/apache/camel-k/pkg/apis/camel/v1" |
| "github.com/apache/camel-k/pkg/apis/camel/v1alpha1" |
| "github.com/apache/camel-k/pkg/client" |
| "github.com/apache/camel-k/pkg/util/kubernetes" |
| "github.com/apache/camel-k/pkg/util/log" |
| ) |
| |
| const ( |
| // ReasonIntegrationPhaseUpdated --. |
| ReasonIntegrationPhaseUpdated = "IntegrationPhaseUpdated" |
| // ReasonIntegrationConditionChanged --. |
| ReasonIntegrationConditionChanged = "IntegrationConditionChanged" |
| // ReasonIntegrationError --. |
| ReasonIntegrationError = "IntegrationError" |
| |
| // ReasonIntegrationKitPhaseUpdated --. |
| ReasonIntegrationKitPhaseUpdated = "IntegrationKitPhaseUpdated" |
| // ReasonIntegrationKitConditionChanged --. |
| ReasonIntegrationKitConditionChanged = "IntegrationKitConditionChanged" |
| // ReasonIntegrationKitError --. |
| ReasonIntegrationKitError = "IntegrationKitError" |
| |
| // ReasonIntegrationPlatformPhaseUpdated --. |
| ReasonIntegrationPlatformPhaseUpdated = "IntegrationPlatformPhaseUpdated" |
| // ReasonIntegrationPlatformConditionChanged --. |
| ReasonIntegrationPlatformConditionChanged = "IntegrationPlatformConditionChanged" |
| // ReasonIntegrationPlatformError --. |
| ReasonIntegrationPlatformError = "IntegrationPlatformError" |
| |
| // ReasonBuildPhaseUpdated --. |
| ReasonBuildPhaseUpdated = "BuildPhaseUpdated" |
| // ReasonBuildConditionChanged --. |
| ReasonBuildConditionChanged = "BuildConditionChanged" |
| // ReasonBuildError --. |
| ReasonBuildError = "BuildError" |
| |
| // ReasonKameletError --. |
| ReasonKameletError = "KameletError" |
| // ReasonKameletConditionChanged --. |
| ReasonKameletConditionChanged = "KameletConditionChanged" |
| // ReasonKameletPhaseUpdated --. |
| ReasonKameletPhaseUpdated = "KameletPhaseUpdated" |
| |
| // ReasonKameletBindingError --. |
| ReasonKameletBindingError = "KameletBindingError" |
| // ReasonKameletBindingConditionChanged --. |
| ReasonKameletBindingConditionChanged = "KameletBindingConditionChanged" |
| // ReasonKameletBindingPhaseUpdated --. |
| ReasonKameletBindingPhaseUpdated = "KameletBindingPhaseUpdated" |
| |
| // ReasonRelatedObjectChanged --. |
| ReasonRelatedObjectChanged = "ReasonRelatedObjectChanged" |
| ) |
| |
| // NotifyIntegrationError automatically generates error events when the integration reconcile cycle phase has an error. |
| func NotifyIntegrationError(ctx context.Context, c client.Client, recorder record.EventRecorder, old, newResource *v1.Integration, err error) { |
| it := old |
| if newResource != nil { |
| it = newResource |
| } |
| if it == nil { |
| return |
| } |
| recorder.Eventf(it, corev1.EventTypeWarning, ReasonIntegrationError, "Cannot reconcile Integration %s: %v", it.Name, err) |
| } |
| |
| // NotifyIntegrationUpdated automatically generates events when the integration changes. |
| func NotifyIntegrationUpdated(ctx context.Context, c client.Client, recorder record.EventRecorder, old, newResource *v1.Integration) { |
| if newResource == nil { |
| return |
| } |
| oldPhase := "" |
| var oldConditions []v1.ResourceCondition |
| if old != nil { |
| oldPhase = string(old.Status.Phase) |
| oldConditions = old.Status.GetConditions() |
| } |
| if newResource.Status.Phase != v1.IntegrationPhaseNone { |
| notifyIfConditionUpdated(recorder, newResource, oldConditions, newResource.Status.GetConditions(), "Integration", newResource.Name, ReasonIntegrationConditionChanged) |
| } |
| notifyIfPhaseUpdated(ctx, c, recorder, newResource, oldPhase, string(newResource.Status.Phase), "Integration", newResource.Name, ReasonIntegrationPhaseUpdated, "") |
| } |
| |
| // NotifyIntegrationKitUpdated automatically generates events when an integration kit changes. |
| func NotifyIntegrationKitUpdated(ctx context.Context, c client.Client, recorder record.EventRecorder, old, newResource *v1.IntegrationKit) { |
| if newResource == nil { |
| return |
| } |
| oldPhase := "" |
| var oldConditions []v1.ResourceCondition |
| if old != nil { |
| oldPhase = string(old.Status.Phase) |
| oldConditions = old.Status.GetConditions() |
| } |
| if newResource.Status.Phase != v1.IntegrationKitPhaseNone { |
| notifyIfConditionUpdated(recorder, newResource, oldConditions, newResource.Status.GetConditions(), "Integration Kit", newResource.Name, ReasonIntegrationKitConditionChanged) |
| } |
| notifyIfPhaseUpdated(ctx, c, recorder, newResource, oldPhase, string(newResource.Status.Phase), "Integration Kit", newResource.Name, ReasonIntegrationKitPhaseUpdated, "") |
| } |
| |
| // NotifyIntegrationKitError automatically generates error events when the integration kit reconcile cycle phase has an error. |
| func NotifyIntegrationKitError(ctx context.Context, c client.Client, recorder record.EventRecorder, old, newResource *v1.IntegrationKit, err error) { |
| kit := old |
| if newResource != nil { |
| kit = newResource |
| } |
| if kit == nil { |
| return |
| } |
| recorder.Eventf(kit, corev1.EventTypeWarning, ReasonIntegrationKitError, "Cannot reconcile Integration Kit %s: %v", kit.Name, err) |
| } |
| |
| // NotifyIntegrationPlatformUpdated automatically generates events when an integration platform changes. |
| func NotifyIntegrationPlatformUpdated(ctx context.Context, c client.Client, recorder record.EventRecorder, old, newResource *v1.IntegrationPlatform) { |
| if newResource == nil { |
| return |
| } |
| oldPhase := "" |
| var oldConditions []v1.ResourceCondition |
| if old != nil { |
| oldPhase = string(old.Status.Phase) |
| oldConditions = old.Status.GetConditions() |
| } |
| if newResource.Status.Phase != v1.IntegrationPlatformPhaseNone { |
| notifyIfConditionUpdated(recorder, newResource, oldConditions, newResource.Status.GetConditions(), "Integration Platform", newResource.Name, ReasonIntegrationPlatformConditionChanged) |
| } |
| notifyIfPhaseUpdated(ctx, c, recorder, newResource, oldPhase, string(newResource.Status.Phase), "Integration Platform", newResource.Name, ReasonIntegrationPlatformPhaseUpdated, "") |
| } |
| |
| // NotifyIntegrationPlatformError automatically generates error events when the integration Platform reconcile cycle phase has an error. |
| func NotifyIntegrationPlatformError(ctx context.Context, c client.Client, recorder record.EventRecorder, old, newResource *v1.IntegrationPlatform, err error) { |
| p := old |
| if newResource != nil { |
| p = newResource |
| } |
| if p == nil { |
| return |
| } |
| recorder.Eventf(p, corev1.EventTypeWarning, ReasonIntegrationPlatformError, "Cannot reconcile Integration Platform %s: %v", p.Name, err) |
| } |
| |
| // NotifyKameletUpdated automatically generates events when a Kamelet changes. |
| func NotifyKameletUpdated(ctx context.Context, c client.Client, recorder record.EventRecorder, old, newResource *v1alpha1.Kamelet) { |
| if newResource == nil { |
| return |
| } |
| oldPhase := "" |
| var oldConditions []v1.ResourceCondition |
| if old != nil { |
| oldPhase = string(old.Status.Phase) |
| oldConditions = old.Status.GetConditions() |
| } |
| if newResource.Status.Phase != v1alpha1.KameletPhaseNone { |
| notifyIfConditionUpdated(recorder, newResource, oldConditions, newResource.Status.GetConditions(), "Kamelet", newResource.Name, ReasonKameletConditionChanged) |
| } |
| notifyIfPhaseUpdated(ctx, c, recorder, newResource, oldPhase, string(newResource.Status.Phase), "Kamelet", newResource.Name, ReasonKameletPhaseUpdated, "") |
| } |
| |
| // NotifyKameletError automatically generates error events when the kamelet reconcile cycle phase has an error. |
| func NotifyKameletError(ctx context.Context, c client.Client, recorder record.EventRecorder, old, newResource *v1alpha1.Kamelet, err error) { |
| k := old |
| if newResource != nil { |
| k = newResource |
| } |
| if k == nil { |
| return |
| } |
| recorder.Eventf(k, corev1.EventTypeWarning, ReasonKameletError, "Cannot reconcile Kamelet %s: %v", k.Name, err) |
| } |
| |
| // NotifyKameletBindingUpdated automatically generates events when a KameletBinding changes. |
| func NotifyKameletBindingUpdated(ctx context.Context, c client.Client, recorder record.EventRecorder, old, newResource *v1alpha1.KameletBinding) { |
| if newResource == nil { |
| return |
| } |
| oldPhase := "" |
| var oldConditions []v1.ResourceCondition |
| if old != nil { |
| oldPhase = string(old.Status.Phase) |
| oldConditions = old.Status.GetConditions() |
| } |
| if newResource.Status.Phase != v1alpha1.KameletBindingPhaseNone { |
| notifyIfConditionUpdated(recorder, newResource, oldConditions, newResource.Status.GetConditions(), "KameletBinding", newResource.Name, ReasonKameletBindingConditionChanged) |
| } |
| notifyIfPhaseUpdated(ctx, c, recorder, newResource, oldPhase, string(newResource.Status.Phase), "KameletBinding", newResource.Name, ReasonKameletBindingPhaseUpdated, "") |
| } |
| |
| // NotifyKameletBindingError automatically generates error events when the kameletBinding reconcile cycle phase has an error. |
| func NotifyKameletBindingError(ctx context.Context, c client.Client, recorder record.EventRecorder, old, newResource *v1alpha1.KameletBinding, err error) { |
| k := old |
| if newResource != nil { |
| k = newResource |
| } |
| if k == nil { |
| return |
| } |
| recorder.Eventf(k, corev1.EventTypeWarning, ReasonKameletError, "Cannot reconcile KameletBinding %s: %v", k.Name, err) |
| } |
| |
| // NotifyBuildUpdated automatically generates events when a build changes. |
| func NotifyBuildUpdated(ctx context.Context, c client.Client, recorder record.EventRecorder, old, newResource *v1.Build) { |
| if newResource == nil { |
| return |
| } |
| oldPhase := "" |
| var oldConditions []v1.ResourceCondition |
| if old != nil { |
| oldPhase = string(old.Status.Phase) |
| oldConditions = old.Status.GetConditions() |
| } |
| if newResource.Status.Phase != v1.BuildPhaseNone { |
| notifyIfConditionUpdated(recorder, newResource, oldConditions, newResource.Status.GetConditions(), "Build", newResource.Name, ReasonBuildConditionChanged) |
| } |
| info := "" |
| if newResource.Status.Failure != nil { |
| attempt := newResource.Status.Failure.Recovery.Attempt |
| attemptMax := newResource.Status.Failure.Recovery.AttemptMax |
| info = fmt.Sprintf(" (recovery %d of %d)", attempt, attemptMax) |
| } |
| notifyIfPhaseUpdated(ctx, c, recorder, newResource, oldPhase, string(newResource.Status.Phase), "Build", newResource.Name, ReasonBuildPhaseUpdated, info) |
| } |
| |
| // NotifyBuildError automatically generates error events when the build reconcile cycle phase has an error. |
| func NotifyBuildError(ctx context.Context, c client.Client, recorder record.EventRecorder, old, newResource *v1.Build, err error) { |
| p := old |
| if newResource != nil { |
| p = newResource |
| } |
| if p == nil { |
| return |
| } |
| recorder.Eventf(p, corev1.EventTypeWarning, ReasonBuildError, "Cannot reconcile Build %s: %v", p.Name, err) |
| } |
| |
| // nolint:lll |
| func notifyIfPhaseUpdated(ctx context.Context, c client.Client, recorder record.EventRecorder, newResource ctrl.Object, oldPhase, newPhase string, resourceType, name, reason, info string) { |
| if oldPhase == newPhase { |
| return |
| } |
| |
| // Update information about phase changes |
| phase := newPhase |
| if phase == "" { |
| phase = "[none]" |
| } |
| recorder.Eventf(newResource, corev1.EventTypeNormal, reason, "%s %q in phase %q%s", resourceType, name, phase, info) |
| |
| if creatorRef, creator := getCreatorObject(ctx, c, newResource); creatorRef != nil && creator != nil { |
| if namespace := newResource.GetNamespace(); namespace == creatorRef.Namespace { |
| recorder.Eventf(creator, corev1.EventTypeNormal, ReasonRelatedObjectChanged, "%s %q, created by %s %q, changed phase to %q%s", resourceType, name, creatorRef.Kind, creatorRef.Name, phase, info) |
| } else { |
| recorder.Eventf(creator, corev1.EventTypeNormal, ReasonRelatedObjectChanged, "%s \"%s/%s\", created by %s %q, changed phase to %q%s", resourceType, namespace, name, creatorRef.Kind, creatorRef.Name, phase, info) |
| } |
| } |
| } |
| |
| func notifyIfConditionUpdated(recorder record.EventRecorder, newResource runtime.Object, oldConditions, newConditions []v1.ResourceCondition, resourceType, name, reason string) { |
| // Update information about changes in conditions |
| for _, cond := range getCommonChangedConditions(oldConditions, newConditions) { |
| tail := "" |
| if cond.GetMessage() != "" { |
| tail = fmt.Sprintf(": %s", cond.GetMessage()) |
| } |
| recorder.Eventf(newResource, corev1.EventTypeNormal, reason, "Condition %q is %q for %s %s%s", cond.GetType(), cond.GetStatus(), resourceType, name, tail) |
| } |
| } |
| |
| func getCommonChangedConditions(oldConditions, newConditions []v1.ResourceCondition) []v1.ResourceCondition { |
| oldState := make(map[string]v1.ResourceCondition) |
| for _, c := range oldConditions { |
| oldState[c.GetType()] = c |
| } |
| |
| var res []v1.ResourceCondition |
| for _, newCond := range newConditions { |
| oldCond := oldState[newCond.GetType()] |
| if oldCond == nil || oldCond.GetStatus() != newCond.GetStatus() || oldCond.GetMessage() != newCond.GetMessage() { |
| res = append(res, newCond) |
| } |
| } |
| return res |
| } |
| |
| func getCreatorObject(ctx context.Context, c client.Client, obj runtime.Object) (*corev1.ObjectReference, runtime.Object) { |
| if ref := kubernetes.GetCamelCreator(obj); ref != nil { |
| if ref.Kind == "Integration" { |
| it := v1.NewIntegration(ref.Namespace, ref.Name) |
| if err := c.Get(ctx, ctrl.ObjectKeyFromObject(&it), &it); err != nil { |
| log.Infof("Cannot get information about the creator Integration %v: %v", ref, err) |
| return nil, nil |
| } |
| return ref, &it |
| } |
| } |
| return nil, nil |
| } |