From 4ea7da92576d8fc16bc73ab37b711910e57859d4 Mon Sep 17 00:00:00 2001 From: odubajDT <93584209+odubajDT@users.noreply.github.com> Date: Tue, 17 Jan 2023 15:49:23 +0100 Subject: [PATCH] feat: annotate K8s Events (#589) Signed-off-by: odubajDT Signed-off-by: odubajDT <93584209+odubajDT@users.noreply.github.com> --- .../apis/lifecycle/v1alpha2/common/phases.go | 16 +- .../apis/lifecycle/v1alpha2/keptnapp_test.go | 6 + .../apis/lifecycle/v1alpha2/keptnapp_types.go | 9 + .../v1alpha2/keptnappversion_test.go | 6 + .../v1alpha2/keptnappversion_types.go | 8 + .../v1alpha2/keptnevaluation_test.go | 22 +- .../v1alpha2/keptnevaluation_types.go | 11 + .../apis/lifecycle/v1alpha2/keptntask_test.go | 23 +- .../lifecycle/v1alpha2/keptntask_types.go | 11 + .../lifecycle/v1alpha2/keptnworkload_test.go | 6 + .../lifecycle/v1alpha2/keptnworkload_types.go | 16 +- .../v1alpha2/keptnworkloadinstance_test.go | 7 + .../v1alpha2/keptnworkloadinstance_types.go | 9 + .../controllers/common/evaluationhandler.go | 1 + .../controllers/common/helperfunctions.go | 41 +++ .../common/helperfunctions_test.go | 241 ++++++++++++++++++ operator/controllers/common/phasehandler.go | 5 - operator/controllers/common/taskhandler.go | 11 +- operator/controllers/errors/errors.go | 1 + .../lifecycle/interfaces/eventobject.go | 29 +++ .../lifecycle/interfaces/eventobject_test.go | 37 +++ .../interfaces/fake/eventobject_mock.go | 62 +++++ .../lifecycle/keptnapp/controller.go | 9 +- .../lifecycle/keptnapp/controller_test.go | 8 +- .../lifecycle/keptnevaluation/controller.go | 18 +- .../lifecycle/keptntask/job_utils.go | 15 +- .../keptntaskdefinition/reconcile_function.go | 10 +- .../lifecycle/keptnworkload/controller.go | 6 +- operator/go.mod | 1 + operator/go.sum | 2 + operator/webhooks/pod_mutating_webhook.go | 17 +- 31 files changed, 595 insertions(+), 69 deletions(-) create mode 100644 operator/controllers/lifecycle/interfaces/eventobject.go create mode 100644 operator/controllers/lifecycle/interfaces/eventobject_test.go create mode 100644 operator/controllers/lifecycle/interfaces/fake/eventobject_mock.go diff --git a/operator/apis/lifecycle/v1alpha2/common/phases.go b/operator/apis/lifecycle/v1alpha2/common/phases.go index 14a691715b..35144c969e 100644 --- a/operator/apis/lifecycle/v1alpha2/common/phases.go +++ b/operator/apis/lifecycle/v1alpha2/common/phases.go @@ -24,6 +24,14 @@ var phases = []KeptnPhaseType{ PhaseAppPreEvaluation, PhaseAppPostEvaluation, PhaseAppDeployment, + PhaseReconcileEvaluation, + PhaseReconcileTask, + PhaseCreateEvaluation, + PhaseCreateTask, + PhaseCreateApp, + PhaseCreateWorkload, + PhaseCreateWorklodInstance, + PhaseCreateAppVersion, PhaseCompleted, PhaseDeprecated, } @@ -80,7 +88,13 @@ var ( PhaseAppPostEvaluation = KeptnPhaseType{LongName: "App Post-Deployment Evaluations", ShortName: "AppPostDeployEvaluations"} PhaseAppDeployment = KeptnPhaseType{LongName: "App Deployment", ShortName: "AppDeploy"} PhaseReconcileEvaluation = KeptnPhaseType{LongName: "Reconcile Evaluation", ShortName: "ReconcileEvaluation"} - PhaseCreateEvaluation = KeptnPhaseType{LongName: "Create Evaluation", ShortName: "Create Evaluation"} + PhaseReconcileTask = KeptnPhaseType{LongName: "Reconcile Task", ShortName: "ReconcileTask"} + PhaseCreateEvaluation = KeptnPhaseType{LongName: "Create Evaluation", ShortName: "CreateEvaluation"} + PhaseCreateTask = KeptnPhaseType{LongName: "Create Task", ShortName: "CreateTask"} + PhaseCreateApp = KeptnPhaseType{LongName: "Create App", ShortName: "CreateApp"} + PhaseCreateWorkload = KeptnPhaseType{LongName: "Create Workload", ShortName: "CreateWorkload"} + PhaseCreateWorklodInstance = KeptnPhaseType{LongName: "Create WorkloadInstance", ShortName: "CreateWorkloadInstance"} + PhaseCreateAppVersion = KeptnPhaseType{LongName: "Create AppVersion", ShortName: "CreateAppVersion"} PhaseCompleted = KeptnPhaseType{LongName: "Completed", ShortName: "Completed"} PhaseDeprecated = KeptnPhaseType{LongName: "Deprecated", ShortName: "Deprecated"} ) diff --git a/operator/apis/lifecycle/v1alpha2/keptnapp_test.go b/operator/apis/lifecycle/v1alpha2/keptnapp_test.go index a63608e0c0..1f9453a6d3 100644 --- a/operator/apis/lifecycle/v1alpha2/keptnapp_test.go +++ b/operator/apis/lifecycle/v1alpha2/keptnapp_test.go @@ -44,4 +44,10 @@ func TestKeptnApp(t *testing.T) { common.AppName.String("app"), common.AppVersion.String("version"), }, app.GetSpanAttributes()) + + require.Equal(t, map[string]string{ + "appName": "app", + "appVersion": "version", + "appRevision": "1", + }, app.GetEventAnnotations()) } diff --git a/operator/apis/lifecycle/v1alpha2/keptnapp_types.go b/operator/apis/lifecycle/v1alpha2/keptnapp_types.go index 982b018a8c..0d86d1334e 100644 --- a/operator/apis/lifecycle/v1alpha2/keptnapp_types.go +++ b/operator/apis/lifecycle/v1alpha2/keptnapp_types.go @@ -18,6 +18,7 @@ package v1alpha2 import ( "fmt" + "strconv" "strings" "github.com/keptn/lifecycle-toolkit/operator/apis/lifecycle/v1alpha2/common" @@ -106,3 +107,11 @@ func (a KeptnApp) GetSpanAttributes() []attribute.KeyValue { common.AppVersion.String(a.Spec.Version), } } + +func (a KeptnApp) GetEventAnnotations() map[string]string { + return map[string]string{ + "appName": a.Name, + "appVersion": a.Spec.Version, + "appRevision": strconv.FormatInt(a.Generation, 10), + } +} diff --git a/operator/apis/lifecycle/v1alpha2/keptnappversion_test.go b/operator/apis/lifecycle/v1alpha2/keptnappversion_test.go index c2b9ad075d..84837ab6b5 100644 --- a/operator/apis/lifecycle/v1alpha2/keptnappversion_test.go +++ b/operator/apis/lifecycle/v1alpha2/keptnappversion_test.go @@ -206,6 +206,12 @@ func TestKeptnAppVersion(t *testing.T) { common.AppVersion.String("version"), common.AppNamespace.String("namespace"), }, app.GetSpanAttributes()) + + require.Equal(t, map[string]string{ + "appName": "appname", + "appVersion": "version", + "appVersionName": "app", + }, app.GetEventAnnotations()) } func TestKeptnAppVersion_GetWorkloadNameOfApp(t *testing.T) { diff --git a/operator/apis/lifecycle/v1alpha2/keptnappversion_types.go b/operator/apis/lifecycle/v1alpha2/keptnappversion_types.go index 260a929204..ddc36a4fb1 100644 --- a/operator/apis/lifecycle/v1alpha2/keptnappversion_types.go +++ b/operator/apis/lifecycle/v1alpha2/keptnappversion_types.go @@ -419,3 +419,11 @@ func (a *KeptnAppVersion) SetPhaseTraceID(phase string, carrier propagation.MapC } a.Status.PhaseTraceIDs[common.GetShortPhaseName(phase)] = carrier } + +func (a KeptnAppVersion) GetEventAnnotations() map[string]string { + return map[string]string{ + "appName": a.Spec.AppName, + "appVersion": a.Spec.Version, + "appVersionName": a.Name, + } +} diff --git a/operator/apis/lifecycle/v1alpha2/keptnevaluation_test.go b/operator/apis/lifecycle/v1alpha2/keptnevaluation_test.go index 31471adae9..a980c63e73 100644 --- a/operator/apis/lifecycle/v1alpha2/keptnevaluation_test.go +++ b/operator/apis/lifecycle/v1alpha2/keptnevaluation_test.go @@ -15,9 +15,10 @@ func TestKeptnEvaluation(t *testing.T) { Name: "evaluation", }, Spec: KeptnEvaluationSpec{ - AppName: "app", - AppVersion: "appversion", - Type: common.PostDeploymentCheckType, + AppName: "app", + AppVersion: "appversion", + Type: common.PostDeploymentCheckType, + EvaluationDefinition: "def", }, Status: KeptnEvaluationStatus{ OverallStatus: common.StateFailed, @@ -30,9 +31,10 @@ func TestKeptnEvaluation(t *testing.T) { Name: "evaluation", }, Spec: KeptnEvaluationSpec{ - AppName: "app", - AppVersion: "appversion", - Type: common.PostDeploymentCheckType, + AppName: "app", + AppVersion: "appversion", + Type: common.PostDeploymentCheckType, + EvaluationDefinition: "def", }, Status: KeptnEvaluationStatus{ OverallStatus: common.StateFailed, @@ -84,6 +86,14 @@ func TestKeptnEvaluation(t *testing.T) { common.EvaluationType.String(string(common.PostDeploymentCheckType)), }, evaluation.GetSpanAttributes()) + require.Equal(t, map[string]string{ + "appName": "app", + "appVersion": "appversion", + "workloadName": "", + "workloadVersion": "", + "evaluationName": "evaluation", + "evaluationDefinitionName": "def", + }, evaluation.GetEventAnnotations()) } func TestKeptnEvaluationList(t *testing.T) { diff --git a/operator/apis/lifecycle/v1alpha2/keptnevaluation_types.go b/operator/apis/lifecycle/v1alpha2/keptnevaluation_types.go index 1a0d9d2fcd..c5170e8fb3 100644 --- a/operator/apis/lifecycle/v1alpha2/keptnevaluation_types.go +++ b/operator/apis/lifecycle/v1alpha2/keptnevaluation_types.go @@ -189,3 +189,14 @@ func (e KeptnEvaluation) GetSpanKey(phase string) string { func (e KeptnEvaluation) GetSpanName(phase string) string { return e.Name } + +func (e KeptnEvaluation) GetEventAnnotations() map[string]string { + return map[string]string{ + "appName": e.Spec.AppName, + "appVersion": e.Spec.AppVersion, + "workloadName": e.Spec.Workload, + "workloadVersion": e.Spec.WorkloadVersion, + "evaluationName": e.Name, + "evaluationDefinitionName": e.Spec.EvaluationDefinition, + } +} diff --git a/operator/apis/lifecycle/v1alpha2/keptntask_test.go b/operator/apis/lifecycle/v1alpha2/keptntask_test.go index 7f9beb00c7..f3c1e5bae5 100644 --- a/operator/apis/lifecycle/v1alpha2/keptntask_test.go +++ b/operator/apis/lifecycle/v1alpha2/keptntask_test.go @@ -15,9 +15,10 @@ func TestKeptnTask(t *testing.T) { Name: "task", }, Spec: KeptnTaskSpec{ - AppName: "app", - AppVersion: "appversion", - Type: common.PostDeploymentCheckType, + AppName: "app", + AppVersion: "appversion", + Type: common.PostDeploymentCheckType, + TaskDefinition: "def", }, Status: KeptnTaskStatus{ Status: common.StateFailed, @@ -30,9 +31,10 @@ func TestKeptnTask(t *testing.T) { Name: "task", }, Spec: KeptnTaskSpec{ - AppName: "app", - AppVersion: "appversion", - Type: common.PostDeploymentCheckType, + AppName: "app", + AppVersion: "appversion", + Type: common.PostDeploymentCheckType, + TaskDefinition: "def", }, Status: KeptnTaskStatus{ Status: common.StateFailed, @@ -95,6 +97,15 @@ func TestKeptnTask(t *testing.T) { common.TaskType.String(string(common.PostDeploymentCheckType)), }, task.GetSpanAttributes()) + require.Equal(t, map[string]string{ + "appName": "app", + "appVersion": "appversion", + "workloadName": "workload", + "workloadVersion": "workloadversion", + "taskName": "task", + "taskDefinitionName": "def", + }, task.GetEventAnnotations()) + } func TestKeptnTaskList(t *testing.T) { diff --git a/operator/apis/lifecycle/v1alpha2/keptntask_types.go b/operator/apis/lifecycle/v1alpha2/keptntask_types.go index 6c8a665cab..f11416e2d9 100644 --- a/operator/apis/lifecycle/v1alpha2/keptntask_types.go +++ b/operator/apis/lifecycle/v1alpha2/keptntask_types.go @@ -197,3 +197,14 @@ func (t KeptnTask) GetSpanKey(phase string) string { func (t KeptnTask) GetSpanName(phase string) string { return t.Name } + +func (t KeptnTask) GetEventAnnotations() map[string]string { + return map[string]string{ + "appName": t.Spec.AppName, + "appVersion": t.Spec.AppVersion, + "workloadName": t.Spec.Workload, + "workloadVersion": t.Spec.WorkloadVersion, + "taskName": t.Name, + "taskDefinitionName": t.Spec.TaskDefinition, + } +} diff --git a/operator/apis/lifecycle/v1alpha2/keptnworkload_test.go b/operator/apis/lifecycle/v1alpha2/keptnworkload_test.go index d10a526f8e..7af31a4fb3 100644 --- a/operator/apis/lifecycle/v1alpha2/keptnworkload_test.go +++ b/operator/apis/lifecycle/v1alpha2/keptnworkload_test.go @@ -46,4 +46,10 @@ func TestKeptnWorkload(t *testing.T) { common.WorkloadName.String("workload"), common.WorkloadVersion.String("version"), }, workload.GetSpanAttributes()) + + require.Equal(t, map[string]string{ + "appName": "app", + "workloadName": "workload", + "workloadVersion": "version", + }, workload.GetEventAnnotations()) } diff --git a/operator/apis/lifecycle/v1alpha2/keptnworkload_types.go b/operator/apis/lifecycle/v1alpha2/keptnworkload_types.go index 3f13be85ad..cac4b45321 100644 --- a/operator/apis/lifecycle/v1alpha2/keptnworkload_types.go +++ b/operator/apis/lifecycle/v1alpha2/keptnworkload_types.go @@ -102,10 +102,18 @@ func (w KeptnWorkload) GenerateWorkloadInstance(previousVersion string, traceCon } } -func (i KeptnWorkload) GetSpanAttributes() []attribute.KeyValue { +func (w KeptnWorkload) GetSpanAttributes() []attribute.KeyValue { return []attribute.KeyValue{ - common.AppName.String(i.Spec.AppName), - common.WorkloadName.String(i.Name), - common.WorkloadVersion.String(i.Spec.Version), + common.AppName.String(w.Spec.AppName), + common.WorkloadName.String(w.Name), + common.WorkloadVersion.String(w.Spec.Version), + } +} + +func (w KeptnWorkload) GetEventAnnotations() map[string]string { + return map[string]string{ + "appName": w.Spec.AppName, + "workloadName": w.Name, + "workloadVersion": w.Spec.Version, } } diff --git a/operator/apis/lifecycle/v1alpha2/keptnworkloadinstance_test.go b/operator/apis/lifecycle/v1alpha2/keptnworkloadinstance_test.go index 0bbd9f18ba..b045659580 100644 --- a/operator/apis/lifecycle/v1alpha2/keptnworkloadinstance_test.go +++ b/operator/apis/lifecycle/v1alpha2/keptnworkloadinstance_test.go @@ -215,6 +215,13 @@ func TestKeptnWorkloadInstance(t *testing.T) { common.WorkloadVersion.String("version"), common.WorkloadNamespace.String("namespace"), }, workload.GetSpanAttributes()) + + require.Equal(t, map[string]string{ + "appName": "appname", + "workloadName": "workloadname", + "workloadVersion": "version", + "workloadInstanceName": "workload", + }, workload.GetEventAnnotations()) } //nolint:dupl diff --git a/operator/apis/lifecycle/v1alpha2/keptnworkloadinstance_types.go b/operator/apis/lifecycle/v1alpha2/keptnworkloadinstance_types.go index 73a81acee4..fca5d86aec 100644 --- a/operator/apis/lifecycle/v1alpha2/keptnworkloadinstance_types.go +++ b/operator/apis/lifecycle/v1alpha2/keptnworkloadinstance_types.go @@ -427,3 +427,12 @@ func (w *KeptnWorkloadInstance) SetPhaseTraceID(phase string, carrier propagatio } w.Status.PhaseTraceIDs[common.GetShortPhaseName(phase)] = carrier } + +func (w KeptnWorkloadInstance) GetEventAnnotations() map[string]string { + return map[string]string{ + "appName": w.Spec.AppName, + "workloadName": w.Spec.WorkloadName, + "workloadVersion": w.Spec.Version, + "workloadInstanceName": w.Name, + } +} diff --git a/operator/controllers/common/evaluationhandler.go b/operator/controllers/common/evaluationhandler.go index 6f2a96bb1d..6f932df4c3 100644 --- a/operator/controllers/common/evaluationhandler.go +++ b/operator/controllers/common/evaluationhandler.go @@ -106,6 +106,7 @@ func (r EvaluationHandler) ReconcileEvaluations(ctx context.Context, phaseCtx co return newStatus, summary, nil } +//nolint:dupl func (r EvaluationHandler) CreateKeptnEvaluation(ctx context.Context, namespace string, reconcileObject client.Object, evaluationCreateAttributes CreateAttributes) (string, error) { piWrapper, err := interfaces.NewPhaseItemWrapperFromClientObject(reconcileObject) if err != nil { diff --git a/operator/controllers/common/helperfunctions.go b/operator/controllers/common/helperfunctions.go index fe6fd5b36c..440be47162 100644 --- a/operator/controllers/common/helperfunctions.go +++ b/operator/controllers/common/helperfunctions.go @@ -1,9 +1,15 @@ package common import ( + "fmt" + klcv1alpha2 "github.com/keptn/lifecycle-toolkit/operator/apis/lifecycle/v1alpha2" apicommon "github.com/keptn/lifecycle-toolkit/operator/apis/lifecycle/v1alpha2/common" + "github.com/keptn/lifecycle-toolkit/operator/controllers/lifecycle/interfaces" + "golang.org/x/exp/maps" "k8s.io/apimachinery/pkg/types" + "k8s.io/client-go/tools/record" + "sigs.k8s.io/controller-runtime/pkg/client" ) type CreateAttributes struct { @@ -41,3 +47,38 @@ func GetOldStatus(name string, statuses []klcv1alpha2.ItemStatus) apicommon.Kept return oldstatus } + +// RecordEvent creates k8s Event and adds it to Eventqueue +func RecordEvent(recorder record.EventRecorder, phase apicommon.KeptnPhaseType, eventType string, reconcileObject client.Object, shortReason string, longReason string, version string) { + msg := setEventMessage(phase, reconcileObject, longReason, version) + annotations := setAnnotations(reconcileObject, phase) + recorder.AnnotatedEventf(reconcileObject, annotations, eventType, fmt.Sprintf("%s%s", phase.ShortName, shortReason), msg) +} + +func setEventMessage(phase apicommon.KeptnPhaseType, reconcileObject client.Object, longReason string, version string) string { + if version == "" { + return fmt.Sprintf("%s: %s / Namespace: %s, Name: %s", phase.LongName, longReason, reconcileObject.GetNamespace(), reconcileObject.GetName()) + } + return fmt.Sprintf("%s: %s / Namespace: %s, Name: %s, Version: %s", phase.LongName, longReason, reconcileObject.GetNamespace(), reconcileObject.GetName(), version) +} + +func setAnnotations(reconcileObject client.Object, phase apicommon.KeptnPhaseType) map[string]string { + if reconcileObject == nil || reconcileObject.GetName() == "" || reconcileObject.GetNamespace() == "" { + return nil + } + annotations := map[string]string{ + "namespace": reconcileObject.GetNamespace(), + "name": reconcileObject.GetName(), + "phase": phase.ShortName, + } + + piWrapper, err := interfaces.NewEventObjectWrapperFromClientObject(reconcileObject) + if err == nil { + maps.Copy(annotations, piWrapper.GetEventAnnotations()) + } + + annotationsObject := reconcileObject.GetAnnotations() + annotations["traceparent"] = annotationsObject["traceparent"] + + return annotations +} diff --git a/operator/controllers/common/helperfunctions_test.go b/operator/controllers/common/helperfunctions_test.go index 5a3cfcb66e..2ba252972f 100644 --- a/operator/controllers/common/helperfunctions_test.go +++ b/operator/controllers/common/helperfunctions_test.go @@ -6,7 +6,9 @@ import ( klcv1alpha2 "github.com/keptn/lifecycle-toolkit/operator/apis/lifecycle/v1alpha2" apicommon "github.com/keptn/lifecycle-toolkit/operator/apis/lifecycle/v1alpha2/common" "github.com/stretchr/testify/require" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" + "sigs.k8s.io/controller-runtime/pkg/client" ) func Test_GetItemStatus(t *testing.T) { @@ -161,3 +163,242 @@ func Test_GetOldStatus(t *testing.T) { }) } } + +func Test_setEventMessage(t *testing.T) { + tests := []struct { + name string + version string + want string + }{ + { + name: "version empty", + version: "", + want: "App Deployment: longReason / Namespace: namespace, Name: app", + }, + { + name: "version set", + version: "1.0.0", + want: "App Deployment: longReason / Namespace: namespace, Name: app, Version: 1.0.0", + }, + } + + appVersion := &klcv1alpha2.KeptnAppVersion{ + ObjectMeta: v1.ObjectMeta{ + Name: "app", + Namespace: "namespace", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + require.Equal(t, setEventMessage(apicommon.PhaseAppDeployment, appVersion, "longReason", tt.version), tt.want) + }) + } +} + +func Test_setAnnotations(t *testing.T) { + tests := []struct { + name string + object client.Object + want map[string]string + }{ + { + name: "nil object", + object: nil, + want: nil, + }, + { + name: "empty object", + object: &klcv1alpha2.KeptnEvaluationDefinition{}, + want: nil, + }, + { + name: "unknown object", + object: &klcv1alpha2.KeptnEvaluationDefinition{ + ObjectMeta: v1.ObjectMeta{ + Name: "def", + Namespace: "namespace", + }, + }, + want: map[string]string{ + "namespace": "namespace", + "name": "def", + "phase": "AppDeploy", + "traceparent": "", + }, + }, + { + name: "object with traceparent", + object: &klcv1alpha2.KeptnEvaluationDefinition{ + ObjectMeta: v1.ObjectMeta{ + Name: "def", + Namespace: "namespace", + Annotations: map[string]string{ + "traceparent": "23232333", + }, + }, + }, + want: map[string]string{ + "namespace": "namespace", + "name": "def", + "phase": "AppDeploy", + "traceparent": "23232333", + }, + }, + { + name: "KeptnApp", + object: &klcv1alpha2.KeptnApp{ + ObjectMeta: v1.ObjectMeta{ + Name: "app", + Namespace: "namespace", + Generation: 1, + }, + Spec: klcv1alpha2.KeptnAppSpec{ + Version: "1.0.0", + }, + }, + want: map[string]string{ + "namespace": "namespace", + "name": "app", + "phase": "AppDeploy", + "appName": "app", + "appVersion": "1.0.0", + "appRevision": "1", + "traceparent": "", + }, + }, + { + name: "KeptnAppVersion", + object: &klcv1alpha2.KeptnAppVersion{ + ObjectMeta: v1.ObjectMeta{ + Name: "appVersion", + Namespace: "namespace", + }, + Spec: klcv1alpha2.KeptnAppVersionSpec{ + AppName: "app", + KeptnAppSpec: klcv1alpha2.KeptnAppSpec{ + Version: "1.0.0", + }, + }, + }, + want: map[string]string{ + "namespace": "namespace", + "name": "appVersion", + "phase": "AppDeploy", + "appName": "app", + "appVersion": "1.0.0", + "appVersionName": "appVersion", + "traceparent": "", + }, + }, + { + name: "KeptnWorkload", + object: &klcv1alpha2.KeptnWorkload{ + ObjectMeta: v1.ObjectMeta{ + Name: "workload", + Namespace: "namespace", + }, + Spec: klcv1alpha2.KeptnWorkloadSpec{ + AppName: "app", + Version: "1.0.0", + }, + }, + want: map[string]string{ + "namespace": "namespace", + "name": "workload", + "phase": "AppDeploy", + "appName": "app", + "workloadVersion": "1.0.0", + "workloadName": "workload", + "traceparent": "", + }, + }, + { + name: "KeptnWorkloadInstance", + object: &klcv1alpha2.KeptnWorkloadInstance{ + ObjectMeta: v1.ObjectMeta{ + Name: "workloadInstance", + Namespace: "namespace", + }, + Spec: klcv1alpha2.KeptnWorkloadInstanceSpec{ + KeptnWorkloadSpec: klcv1alpha2.KeptnWorkloadSpec{ + AppName: "app", + Version: "1.0.0", + }, + WorkloadName: "workload", + }, + }, + want: map[string]string{ + "namespace": "namespace", + "name": "workloadInstance", + "phase": "AppDeploy", + "appName": "app", + "workloadVersion": "1.0.0", + "workloadName": "workload", + "workloadInstanceName": "workloadInstance", + "traceparent": "", + }, + }, + { + name: "KeptnTask", + object: &klcv1alpha2.KeptnTask{ + ObjectMeta: v1.ObjectMeta{ + Name: "task", + Namespace: "namespace", + }, + Spec: klcv1alpha2.KeptnTaskSpec{ + AppName: "app", + AppVersion: "1.0.0", + Workload: "workload", + WorkloadVersion: "2.0.0", + TaskDefinition: "def", + }, + }, + want: map[string]string{ + "namespace": "namespace", + "name": "task", + "phase": "AppDeploy", + "appName": "app", + "appVersion": "1.0.0", + "workloadName": "workload", + "workloadVersion": "2.0.0", + "taskDefinitionName": "def", + "taskName": "task", + "traceparent": "", + }, + }, + { + name: "KeptnEvaluation", + object: &klcv1alpha2.KeptnEvaluation{ + ObjectMeta: v1.ObjectMeta{ + Name: "eval", + Namespace: "namespace", + }, + Spec: klcv1alpha2.KeptnEvaluationSpec{ + AppName: "app", + AppVersion: "1.0.0", + Workload: "workload", + WorkloadVersion: "2.0.0", + EvaluationDefinition: "def", + }, + }, + want: map[string]string{ + "namespace": "namespace", + "name": "eval", + "phase": "AppDeploy", + "appName": "app", + "appVersion": "1.0.0", + "workloadName": "workload", + "workloadVersion": "2.0.0", + "evaluationDefinitionName": "def", + "evaluationName": "eval", + "traceparent": "", + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + require.Equal(t, setAnnotations(tt.object, apicommon.PhaseAppDeployment), tt.want) + }) + } +} diff --git a/operator/controllers/common/phasehandler.go b/operator/controllers/common/phasehandler.go index 055b2daaa3..fef5b79ead 100644 --- a/operator/controllers/common/phasehandler.go +++ b/operator/controllers/common/phasehandler.go @@ -2,7 +2,6 @@ package common import ( "context" - "fmt" "time" "github.com/go-logr/logr" @@ -28,10 +27,6 @@ type PhaseResult struct { ctrl.Result } -func RecordEvent(recorder record.EventRecorder, phase apicommon.KeptnPhaseType, eventType string, reconcileObject client.Object, shortReason string, longReason string, version string) { - recorder.Event(reconcileObject, eventType, fmt.Sprintf("%s%s", phase.ShortName, shortReason), fmt.Sprintf("%s %s / Namespace: %s, Name: %s, Version: %s ", phase.LongName, longReason, reconcileObject.GetNamespace(), reconcileObject.GetName(), version)) -} - func (r PhaseHandler) HandlePhase(ctx context.Context, ctxTrace context.Context, tracer trace.Tracer, reconcileObject client.Object, phase apicommon.KeptnPhaseType, span trace.Span, reconcilePhase func(phaseCtx context.Context) (apicommon.KeptnState, error)) (*PhaseResult, error) { requeueResult := ctrl.Result{Requeue: true, RequeueAfter: 5 * time.Second} piWrapper, err := interfaces.NewPhaseItemWrapperFromClientObject(reconcileObject) diff --git a/operator/controllers/common/taskhandler.go b/operator/controllers/common/taskhandler.go index 3bf9d25fec..f02d72f6ff 100644 --- a/operator/controllers/common/taskhandler.go +++ b/operator/controllers/common/taskhandler.go @@ -36,10 +36,7 @@ func (r TaskHandler) ReconcileTasks(ctx context.Context, phaseCtx context.Contex return nil, apicommon.StatusSummary{}, err } - phase := apicommon.KeptnPhaseType{ - ShortName: "ReconcileTasks", - LongName: "Reconcile Tasks", - } + phase := apicommon.PhaseReconcileTask tasks, statuses := r.setupTasks(taskCreateAttributes, piWrapper) @@ -111,16 +108,14 @@ func (r TaskHandler) ReconcileTasks(ctx context.Context, phaseCtx context.Contex return newStatus, summary, nil } +//nolint:dupl func (r TaskHandler) CreateKeptnTask(ctx context.Context, namespace string, reconcileObject client.Object, taskCreateAttributes CreateAttributes) (string, error) { piWrapper, err := interfaces.NewPhaseItemWrapperFromClientObject(reconcileObject) if err != nil { return "", err } - phase := apicommon.KeptnPhaseType{ - ShortName: "KeptnTaskCreate", - LongName: "Keptn Task Create", - } + phase := apicommon.PhaseCreateTask newTask := piWrapper.GenerateTask(taskCreateAttributes.Definition, taskCreateAttributes.CheckType) err = controllerutil.SetControllerReference(reconcileObject, &newTask, r.Scheme) diff --git a/operator/controllers/errors/errors.go b/operator/controllers/errors/errors.go index ccbd19c9a5..12bbb4a1c4 100644 --- a/operator/controllers/errors/errors.go +++ b/operator/controllers/errors/errors.go @@ -6,6 +6,7 @@ var ErrCannotWrapToPhaseItem = fmt.Errorf("provided object does not implement Ph var ErrCannotWrapToListItem = fmt.Errorf("provided object does not implement ListItem interface") var ErrCannotWrapToMetricsObject = fmt.Errorf("provided object does not implement MetricsObject interface") var ErrCannotWrapToActiveMetricsObject = fmt.Errorf("provided object does not implement ActiveMetricsObject interface") +var ErrCannotWrapToEventObject = fmt.Errorf("provided object does not implement EventObject interface") var ErrCannotWrapToSpanItem = fmt.Errorf("provided object does not implement SpanItem interface") var ErrRetryCountExceeded = fmt.Errorf("retryCount for evaluation exceeded") var ErrNoValues = fmt.Errorf("no values") diff --git a/operator/controllers/lifecycle/interfaces/eventobject.go b/operator/controllers/lifecycle/interfaces/eventobject.go new file mode 100644 index 0000000000..43f5f8f8bc --- /dev/null +++ b/operator/controllers/lifecycle/interfaces/eventobject.go @@ -0,0 +1,29 @@ +package interfaces + +import ( + "github.com/keptn/lifecycle-toolkit/operator/controllers/errors" + "sigs.k8s.io/controller-runtime/pkg/client" +) + +// EventObject represents an object who can send k8s Events with annotations +// +//go:generate moq -pkg fake --skip-ensure -out ./fake/eventobject_mock.go . EventObject +type EventObject interface { + GetEventAnnotations() map[string]string +} + +type EventObjectWrapper struct { + Obj EventObject +} + +func NewEventObjectWrapperFromClientObject(object client.Object) (*EventObjectWrapper, error) { + eo, ok := object.(EventObject) + if !ok { + return nil, errors.ErrCannotWrapToEventObject + } + return &EventObjectWrapper{Obj: eo}, nil +} + +func (eo EventObjectWrapper) GetEventAnnotations() map[string]string { + return eo.Obj.GetEventAnnotations() +} diff --git a/operator/controllers/lifecycle/interfaces/eventobject_test.go b/operator/controllers/lifecycle/interfaces/eventobject_test.go new file mode 100644 index 0000000000..5e20334bd8 --- /dev/null +++ b/operator/controllers/lifecycle/interfaces/eventobject_test.go @@ -0,0 +1,37 @@ +package interfaces + +import ( + "testing" + + "github.com/keptn/lifecycle-toolkit/operator/apis/lifecycle/v1alpha2" + apicommon "github.com/keptn/lifecycle-toolkit/operator/apis/lifecycle/v1alpha2/common" + "github.com/keptn/lifecycle-toolkit/operator/controllers/lifecycle/interfaces/fake" + "github.com/stretchr/testify/require" +) + +func TestEventObjectWrapper(t *testing.T) { + appVersion := v1alpha2.KeptnAppVersion{ + Status: v1alpha2.KeptnAppVersionStatus{ + Status: apicommon.StateFailed, + CurrentPhase: "test", + }, + } + + object, err := NewEventObjectWrapperFromClientObject(&appVersion) + require.Nil(t, err) + + require.NotEmpty(t, object.GetEventAnnotations()) +} + +func TestEventObject(t *testing.T) { + EventObjectMock := fake.EventObjectMock{ + GetEventAnnotationsFunc: func() map[string]string { + return nil + }, + } + + wrapper := EventObjectWrapper{Obj: &EventObjectMock} + + _ = wrapper.GetEventAnnotations() + require.Len(t, EventObjectMock.GetEventAnnotationsCalls(), 1) +} diff --git a/operator/controllers/lifecycle/interfaces/fake/eventobject_mock.go b/operator/controllers/lifecycle/interfaces/fake/eventobject_mock.go new file mode 100644 index 0000000000..cbe1b6eb16 --- /dev/null +++ b/operator/controllers/lifecycle/interfaces/fake/eventobject_mock.go @@ -0,0 +1,62 @@ +// Code generated by moq; DO NOT EDIT. +// github.com/matryer/moq + +package fake + +import ( + "sync" +) + +// EventObjectMock is a mock implementation of interfaces.EventObject. +// +// func TestSomethingThatUsesEventObject(t *testing.T) { +// +// // make and configure a mocked interfaces.EventObject +// mockedEventObject := &EventObjectMock{ +// GetEventAnnotationsFunc: func() map[string]string { +// panic("mock out the GetEventAnnotations method") +// }, +// } +// +// // use mockedEventObject in code that requires interfaces.EventObject +// // and then make assertions. +// +// } +type EventObjectMock struct { + // GetEventAnnotationsFunc mocks the GetEventAnnotations method. + GetEventAnnotationsFunc func() map[string]string + + // calls tracks calls to the methods. + calls struct { + // GetEventAnnotations holds details about calls to the GetEventAnnotations method. + GetEventAnnotations []struct { + } + } + lockGetEventAnnotations sync.RWMutex +} + +// GetEventAnnotations calls GetEventAnnotationsFunc. +func (mock *EventObjectMock) GetEventAnnotations() map[string]string { + if mock.GetEventAnnotationsFunc == nil { + panic("EventObjectMock.GetEventAnnotationsFunc: method is nil but EventObject.GetEventAnnotations was just called") + } + callInfo := struct { + }{} + mock.lockGetEventAnnotations.Lock() + mock.calls.GetEventAnnotations = append(mock.calls.GetEventAnnotations, callInfo) + mock.lockGetEventAnnotations.Unlock() + return mock.GetEventAnnotationsFunc() +} + +// GetEventAnnotationsCalls gets all the calls that were made to GetEventAnnotations. +// Check the length with: +// len(mockedEventObject.GetEventAnnotationsCalls()) +func (mock *EventObjectMock) GetEventAnnotationsCalls() []struct { +} { + var calls []struct { + } + mock.lockGetEventAnnotations.RLock() + calls = mock.calls.GetEventAnnotations + mock.lockGetEventAnnotations.RUnlock() + return calls +} diff --git a/operator/controllers/lifecycle/keptnapp/controller.go b/operator/controllers/lifecycle/keptnapp/controller.go index ce93aa29c2..b058a4d3ab 100644 --- a/operator/controllers/lifecycle/keptnapp/controller.go +++ b/operator/controllers/lifecycle/keptnapp/controller.go @@ -24,6 +24,7 @@ import ( "github.com/go-logr/logr" klcv1alpha2 "github.com/keptn/lifecycle-toolkit/operator/apis/lifecycle/v1alpha2" "github.com/keptn/lifecycle-toolkit/operator/apis/lifecycle/v1alpha2/common" + controllercommon "github.com/keptn/lifecycle-toolkit/operator/controllers/common" controllererrors "github.com/keptn/lifecycle-toolkit/operator/controllers/errors" "github.com/keptn/lifecycle-toolkit/operator/controllers/lifecycle/interfaces" "go.opentelemetry.io/otel" @@ -105,10 +106,10 @@ func (r *KeptnAppReconciler) Reconcile(ctx context.Context, req ctrl.Request) (c if err != nil { r.Log.Error(err, "could not create AppVersion") span.SetStatus(codes.Error, err.Error()) - r.Recorder.Event(app, "Warning", "AppVersionNotCreated", fmt.Sprintf("Could not create KeptnAppVersion / Namespace: %s, Name: %s ", appVersion.Namespace, appVersion.Name)) + controllercommon.RecordEvent(r.Recorder, common.PhaseCreateAppVersion, "Warning", appVersion, "AppVersionNotCreated", "Could not create KeptnAppVersion", appVersion.Spec.Version) return ctrl.Result{}, err } - r.Recorder.Event(app, "Normal", "AppVersionCreated", fmt.Sprintf("Created KeptnAppVersion / Namespace: %s, Name: %s ", appVersion.Namespace, appVersion.Name)) + controllercommon.RecordEvent(r.Recorder, common.PhaseCreateAppVersion, "Normal", appVersion, "AppVersionCreated", "created KeptnAppVersion", appVersion.Spec.Version) app.Status.CurrentVersion = app.Spec.Version if err := r.Client.Status().Update(ctx, app); err != nil { @@ -172,10 +173,10 @@ func (r *KeptnAppReconciler) handleGenerationBump(ctx context.Context, app *klcv if app.Generation != 1 { if err := r.deprecateAppVersions(ctx, app); err != nil { r.Log.Error(err, "could not deprecate appVersions for appVersion %s", app.GetAppVersionName()) - r.Recorder.Event(app, "Warning", "AppVersionNotDeprecated", fmt.Sprintf("Could not deprecate KeptnAppVersions for KeptnAppVersion / Namespace: %s, Name: %s ", app.Namespace, app.GetAppVersionName())) + controllercommon.RecordEvent(r.Recorder, common.PhaseCreateAppVersion, "Warning", app, "AppVersionNotDeprecated", fmt.Sprintf("could not deprecate KeptnAppVersions for KeptnAppVersion: %s", app.GetAppVersionName()), app.Spec.Version) return err } - r.Recorder.Event(app, "Normal", "AppVersionDeprecated", fmt.Sprintf("Deprecated KeptnAppVersions for KeptnAppVersion / Namespace: %s, Name: %s ", app.Namespace, app.GetAppVersionName())) + controllercommon.RecordEvent(r.Recorder, common.PhaseCreateAppVersion, "Normal", app, "AppVersionDeprecated", fmt.Sprintf("deprecated KeptnAppVersions for KeptnAppVersion: %s", app.GetAppVersionName()), app.Spec.Version) } return nil } diff --git a/operator/controllers/lifecycle/keptnapp/controller_test.go b/operator/controllers/lifecycle/keptnapp/controller_test.go index ca5c9e0a3e..0cb2d0d4a8 100644 --- a/operator/controllers/lifecycle/keptnapp/controller_test.go +++ b/operator/controllers/lifecycle/keptnapp/controller_test.go @@ -67,7 +67,7 @@ func TestKeptnAppReconciler_reconcile(t *testing.T) { }, }, wantErr: nil, - event: `Normal AppVersionCreated Created KeptnAppVersion / Namespace: default, Name: myapp-1.0.0`, + event: `Normal CreateAppVersionAppVersionCreated Create AppVersion: created KeptnAppVersion / Namespace: default, Name: myapp-1.0.0-1, Version: 1.0.0`, }, { name: "test simple notfound should not return error nor event", @@ -144,7 +144,7 @@ func TestKeptnAppReconciler_deprecateAppVersions(t *testing.T) { require.Nil(t, err) event := <-eventChannel - assert.Matches(t, event, `Normal AppVersionCreated Created KeptnAppVersion / Namespace: default, Name: myapp-1.0.0-1`) + assert.Matches(t, event, `Normal CreateAppVersionAppVersionCreated Create AppVersion: created KeptnAppVersion / Namespace: default, Name: myapp-1.0.0-1, Version: 1.0.0`) err = controllercommon.UpdateAppRevision(r.Client, "myapp", 2) require.Nil(t, err) @@ -159,10 +159,10 @@ func TestKeptnAppReconciler_deprecateAppVersions(t *testing.T) { require.Nil(t, err) event = <-eventChannel - assert.Matches(t, event, `Normal AppVersionCreated Created KeptnAppVersion / Namespace: default, Name: myapp-1.0.0-2`) + assert.Matches(t, event, `Normal CreateAppVersionAppVersionCreated Create AppVersion: created KeptnAppVersion / Namespace: default, Name: myapp-1.0.0-2, Version: 1.0.0`) event = <-eventChannel - assert.Matches(t, event, `Normal AppVersionDeprecated Deprecated KeptnAppVersions for KeptnAppVersion / Namespace: default, Name: myapp-1.0.0-2`) + assert.Matches(t, event, `Normal CreateAppVersionAppVersionDeprecated Create AppVersion: deprecated KeptnAppVersions for KeptnAppVersion: myapp-1.0.0-2 / Namespace: default, Name: myapp, Version: 1.0.0`) } func setupReconciler(t *testing.T) (*KeptnAppReconciler, chan string, *interfacesfake.ITracerMock) { diff --git a/operator/controllers/lifecycle/keptnevaluation/controller.go b/operator/controllers/lifecycle/keptnevaluation/controller.go index 7213b2cede..2f6aa17a8c 100644 --- a/operator/controllers/lifecycle/keptnevaluation/controller.go +++ b/operator/controllers/lifecycle/keptnevaluation/controller.go @@ -18,12 +18,12 @@ package keptnevaluation import ( "context" - "fmt" "time" "github.com/go-logr/logr" klcv1alpha2 "github.com/keptn/lifecycle-toolkit/operator/apis/lifecycle/v1alpha2" apicommon "github.com/keptn/lifecycle-toolkit/operator/apis/lifecycle/v1alpha2/common" + controllercommon "github.com/keptn/lifecycle-toolkit/operator/controllers/common" controllererrors "github.com/keptn/lifecycle-toolkit/operator/controllers/errors" providers "github.com/keptn/lifecycle-toolkit/operator/controllers/lifecycle/keptnevaluation/providers" "go.opentelemetry.io/otel" @@ -108,7 +108,7 @@ func (r *KeptnEvaluationReconciler) Reconcile(ctx context.Context, req ctrl.Requ // load the provider provider, err2 := providers.NewProvider(evaluationDefinition.Spec.Source, r.Log, r.Client) if err2 != nil { - r.recordEvent("Error", evaluation, "ProviderNotFound", "evaluation provider was not found") + controllercommon.RecordEvent(r.Recorder, apicommon.PhaseReconcileEvaluation, "Error", evaluation, "ProviderNotFound", "evaluation provider was not found", "") r.Log.Error(err2, "Failed to get the correct Metric Provider") span.SetStatus(codes.Error, err2.Error()) return ctrl.Result{Requeue: false}, err2 @@ -147,17 +147,19 @@ func (r *KeptnEvaluationReconciler) handleEvaluationIncomplete(ctx context.Conte // Evaluation is uncompleted, update status anyway this avoids updating twice in case of completion err := r.Client.Status().Update(ctx, evaluation) if err != nil { - r.recordEvent("Warning", evaluation, "ReconcileErrored", "could not update status") + controllercommon.RecordEvent(r.Recorder, apicommon.PhaseReconcileEvaluation, "Warning", evaluation, "ReconcileErrored", "could not update status", "") span.SetStatus(codes.Error, err.Error()) return err } - r.recordEvent("Normal", evaluation, "NotFinished", "has not finished") + controllercommon.RecordEvent(r.Recorder, apicommon.PhaseReconcileEvaluation, "Normal", evaluation, "NotFinished", "has not finished", "") + return nil + } func (r *KeptnEvaluationReconciler) handleEvaluationExceededRetries(ctx context.Context, evaluation *klcv1alpha2.KeptnEvaluation, span trace.Span) { - r.recordEvent("Warning", evaluation, "ReconcileTimeOut", "retryCount exceeded") + controllercommon.RecordEvent(r.Recorder, apicommon.PhaseReconcileEvaluation, "Warning", evaluation, "ReconcileTimeOut", "retryCount exceeded", "") err := controllererrors.ErrRetryCountExceeded span.SetStatus(codes.Error, err.Error()) evaluation.Status.OverallStatus = apicommon.StateFailed @@ -230,7 +232,7 @@ func (r *KeptnEvaluationReconciler) updateFinishedEvaluationMetrics(ctx context. err := r.Client.Status().Update(ctx, evaluation) if err != nil { span.SetStatus(codes.Error, err.Error()) - r.recordEvent("Warning", evaluation, "ReconcileErrored", "could not update status") + controllercommon.RecordEvent(r.Recorder, apicommon.PhaseReconcileEvaluation, "Warning", evaluation, "ReconcileErrored", "could not update status", "") return err } @@ -269,7 +271,3 @@ func (r *KeptnEvaluationReconciler) fetchDefinitionAndProvider(ctx context.Conte } return evaluationDefinition, evaluationProvider, nil } - -func (r *KeptnEvaluationReconciler) recordEvent(eventType string, evaluation *klcv1alpha2.KeptnEvaluation, shortReason string, longReason string) { - r.Recorder.Event(evaluation, eventType, shortReason, fmt.Sprintf("%s / Namespace: %s, Name: %s, WorkloadVersion: %s ", longReason, evaluation.Namespace, evaluation.Name, evaluation.Spec.WorkloadVersion)) -} diff --git a/operator/controllers/lifecycle/keptntask/job_utils.go b/operator/controllers/lifecycle/keptntask/job_utils.go index 9df999a6d1..b28a96b1dd 100644 --- a/operator/controllers/lifecycle/keptntask/job_utils.go +++ b/operator/controllers/lifecycle/keptntask/job_utils.go @@ -8,6 +8,7 @@ import ( "github.com/imdario/mergo" klcv1alpha2 "github.com/keptn/lifecycle-toolkit/operator/apis/lifecycle/v1alpha2" apicommon "github.com/keptn/lifecycle-toolkit/operator/apis/lifecycle/v1alpha2/common" + controllercommon "github.com/keptn/lifecycle-toolkit/operator/controllers/common" batchv1 "k8s.io/api/batch/v1" "k8s.io/apimachinery/pkg/types" ctrl "sigs.k8s.io/controller-runtime" @@ -17,7 +18,7 @@ func (r *KeptnTaskReconciler) createJob(ctx context.Context, req ctrl.Request, t jobName := "" definition, err := r.getTaskDefinition(ctx, task.Spec.TaskDefinition, req.Namespace) if err != nil { - r.Recorder.Event(task, "Warning", "TaskDefinitionNotFound", fmt.Sprintf("Could not find KeptnTaskDefinition / Namespace: %s, Name: %s ", task.Namespace, task.Spec.TaskDefinition)) + controllercommon.RecordEvent(r.Recorder, apicommon.PhaseCreateTask, "Warning", task, "TaskDefinitionNotFound", fmt.Sprintf("could not find KeptnTaskDefinition: %s ", task.Spec.TaskDefinition), "") return err } @@ -54,7 +55,7 @@ func (r *KeptnTaskReconciler) createFunctionJob(ctx context.Context, req ctrl.Re if len(task.Spec.Parameters.Inline) > 0 { err = mergo.Merge(¶ms.Parameters, task.Spec.Parameters.Inline) if err != nil { - r.Recorder.Event(task, "Warning", "TaskDefinitionMergeFailure", fmt.Sprintf("Could not merge KeptnTaskDefinition / Namespace: %s, Name: %s ", task.Namespace, task.Spec.TaskDefinition)) + controllercommon.RecordEvent(r.Recorder, apicommon.PhaseCreateTask, "Warning", task, "TaskDefinitionMergeFailure", fmt.Sprintf("could not merge KeptnTaskDefinition: %s ", task.Spec.TaskDefinition), "") return "", err } } @@ -70,11 +71,11 @@ func (r *KeptnTaskReconciler) createFunctionJob(ctx context.Context, req ctrl.Re err = r.Client.Create(ctx, job) if err != nil { r.Log.Error(err, "could not create job") - r.Recorder.Event(task, "Warning", "JobNotCreated", fmt.Sprintf("Could not create Job / Namespace: %s, Name: %s ", task.Namespace, task.Name)) + controllercommon.RecordEvent(r.Recorder, apicommon.PhaseCreateTask, "Warning", task, "JobNotCreated", fmt.Sprintf("could not create Job: %s ", task.Name), "") return job.Name, err } - r.Recorder.Event(task, "Normal", "JobCreated", fmt.Sprintf("Created Job / Namespace: %s, Name: %s ", task.Namespace, task.Name)) + controllercommon.RecordEvent(r.Recorder, apicommon.PhaseReconcileTask, "Normal", task, "JobCreated", fmt.Sprintf("created Job: %s ", task.Name), "") return job.Name, nil } @@ -82,7 +83,7 @@ func (r *KeptnTaskReconciler) updateJob(ctx context.Context, req ctrl.Request, t job, err := r.getJob(ctx, task.Status.JobName, req.Namespace) if err != nil { task.Status.JobName = "" - r.Recorder.Event(task, "Warning", "JobReferenceRemoved", fmt.Sprintf("Removed Job Reference as Job could not be found / Namespace: %s, TaskName: %s ", task.Namespace, task.Name)) + controllercommon.RecordEvent(r.Recorder, apicommon.PhaseReconcileTask, "Warning", task, "JobReferenceRemoved", "removed Job Reference as Job could not be found", "") err = r.Client.Status().Update(ctx, task) if err != nil { r.Log.Error(err, "could not remove job reference for: "+task.Name) @@ -126,7 +127,7 @@ func (r *KeptnTaskReconciler) handleParent(ctx context.Context, req ctrl.Request var parentJobParams FunctionExecutionParams parentDefinition, err := r.getTaskDefinition(ctx, definition.Spec.Function.FunctionReference.Name, req.Namespace) if err != nil { - r.Recorder.Event(task, "Warning", "TaskDefinitionNotFound", fmt.Sprintf("Could not find KeptnTaskDefinition / Namespace: %s, Name: %s ", task.Namespace, task.Spec.TaskDefinition)) + controllercommon.RecordEvent(r.Recorder, apicommon.PhaseCreateTask, "Warning", task, "TaskDefinitionNotFound", fmt.Sprintf("could not find KeptnTaskDefinition: %s ", task.Spec.TaskDefinition), "") return err } parentJobParams, _, err = r.parseFunctionTaskDefinition(parentDefinition) @@ -135,7 +136,7 @@ func (r *KeptnTaskReconciler) handleParent(ctx context.Context, req ctrl.Request } err = mergo.Merge(¶ms, parentJobParams) if err != nil { - r.Recorder.Event(task, "Warning", "TaskDefinitionMergeFailure", fmt.Sprintf("Could not merge KeptnTaskDefinition / Namespace: %s, Name: %s ", task.Namespace, task.Spec.TaskDefinition)) + controllercommon.RecordEvent(r.Recorder, apicommon.PhaseCreateTask, "Warning", task, "TaskDefinitionMergeFailure", fmt.Sprintf("could not merge KeptnTaskDefinition: %s ", task.Spec.TaskDefinition), "") return err } return nil diff --git a/operator/controllers/lifecycle/keptntaskdefinition/reconcile_function.go b/operator/controllers/lifecycle/keptntaskdefinition/reconcile_function.go index 096f861493..96eb1feeec 100644 --- a/operator/controllers/lifecycle/keptntaskdefinition/reconcile_function.go +++ b/operator/controllers/lifecycle/keptntaskdefinition/reconcile_function.go @@ -6,6 +6,8 @@ import ( "reflect" klcv1alpha2 "github.com/keptn/lifecycle-toolkit/operator/apis/lifecycle/v1alpha2" + apicommon "github.com/keptn/lifecycle-toolkit/operator/apis/lifecycle/v1alpha2/common" + controllercommon "github.com/keptn/lifecycle-toolkit/operator/controllers/common" controllererrors "github.com/keptn/lifecycle-toolkit/operator/controllers/errors" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/errors" @@ -62,19 +64,19 @@ func (r *KeptnTaskDefinitionReconciler) reconcileFunctionInline(ctx context.Cont if cmIsNew { err := r.Client.Create(ctx, &functionCm) if err != nil { - r.Recorder.Event(definition, "Warning", "ConfigMapNotCreated", fmt.Sprintf("Could not create configmap / Namespace: %s, Name: %s ", functionCm.Namespace, functionCm.Name)) + controllercommon.RecordEvent(r.Recorder, apicommon.PhaseReconcileTask, "Warning", &functionCm, "ConfigMapNotCreated", "could not create configmap", "") return err } - r.Recorder.Event(definition, "Normal", "ConfigMapCreated", fmt.Sprintf("Created configmap / Namespace: %s, Name: %s ", functionCm.Namespace, functionCm.Name)) + controllercommon.RecordEvent(r.Recorder, apicommon.PhaseReconcileTask, "Normal", &functionCm, "ConfigMapCreated", "created configmap", "") } else { if !reflect.DeepEqual(cm, functionCm) { err := r.Client.Update(ctx, &functionCm) if err != nil { - r.Recorder.Event(definition, "Warning", "ConfigMapNotUpdated", fmt.Sprintf("Could not update configmap / Namespace: %s, Name: %s ", functionCm.Namespace, functionCm.Name)) + controllercommon.RecordEvent(r.Recorder, apicommon.PhaseReconcileTask, "Warning", &functionCm, "ConfigMapNotUpdated", "uould not update configmap", "") return err } - r.Recorder.Event(definition, "Normal", "ConfigMapUpdated", fmt.Sprintf("Updated configmap / Namespace: %s, Name: %s ", functionCm.Namespace, functionCm.Name)) + controllercommon.RecordEvent(r.Recorder, apicommon.PhaseReconcileTask, "Normal", &functionCm, "ConfigMapUpdated", "updated configmap", "") } } diff --git a/operator/controllers/lifecycle/keptnworkload/controller.go b/operator/controllers/lifecycle/keptnworkload/controller.go index d65b104501..ef343fa580 100644 --- a/operator/controllers/lifecycle/keptnworkload/controller.go +++ b/operator/controllers/lifecycle/keptnworkload/controller.go @@ -22,6 +22,8 @@ import ( "github.com/go-logr/logr" klcv1alpha2 "github.com/keptn/lifecycle-toolkit/operator/apis/lifecycle/v1alpha2" + apicommon "github.com/keptn/lifecycle-toolkit/operator/apis/lifecycle/v1alpha2/common" + controllercommon "github.com/keptn/lifecycle-toolkit/operator/controllers/common" controllererrors "github.com/keptn/lifecycle-toolkit/operator/controllers/errors" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/codes" @@ -101,10 +103,10 @@ func (r *KeptnWorkloadReconciler) Reconcile(ctx context.Context, req ctrl.Reques if err != nil { r.Log.Error(err, "could not create Workload Instance") span.SetStatus(codes.Error, err.Error()) - r.Recorder.Event(workload, "Warning", "WorkloadInstanceNotCreated", fmt.Sprintf("Could not create KeptnWorkloadInstance / Namespace: %s, Name: %s ", workloadInstance.Namespace, workloadInstance.Name)) + controllercommon.RecordEvent(r.Recorder, apicommon.PhaseCreateWorklodInstance, "Warning", workloadInstance, "WorkloadInstanceNotCreated", "could not create KeptnWorkloadInstance ", workloadInstance.Spec.Version) return ctrl.Result{}, err } - r.Recorder.Event(workload, "Normal", "WorkloadInstanceCreated", fmt.Sprintf("Created KeptnWorkloadInstance / Namespace: %s, Name: %s ", workloadInstance.Namespace, workloadInstance.Name)) + controllercommon.RecordEvent(r.Recorder, apicommon.PhaseCreateWorklodInstance, "Normal", workloadInstance, "WorkloadInstanceCreated", "created KeptnWorkloadInstance ", workloadInstance.Spec.Version) workload.Status.CurrentVersion = workload.Spec.Version if err := r.Client.Status().Update(ctx, workload); err != nil { r.Log.Error(err, "could not update Current Version of Workload") diff --git a/operator/go.mod b/operator/go.mod index 01427c4658..761b14b497 100644 --- a/operator/go.mod +++ b/operator/go.mod @@ -29,6 +29,7 @@ require ( require ( github.com/magiconair/properties v1.8.7 + golang.org/x/exp v0.0.0-20230111222715-75897c7a292a k8s.io/apiserver v0.25.5 ) diff --git a/operator/go.sum b/operator/go.sum index a2421342fa..eaa677cd96 100644 --- a/operator/go.sum +++ b/operator/go.sum @@ -374,6 +374,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= +golang.org/x/exp v0.0.0-20230111222715-75897c7a292a h1:/YWeLOBWYV5WAQORVPkZF3Pq9IppkcT72GKnWjNf5W8= +golang.org/x/exp v0.0.0-20230111222715-75897c7a292a/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= diff --git a/operator/webhooks/pod_mutating_webhook.go b/operator/webhooks/pod_mutating_webhook.go index 6564a8b2c2..2ce9c890ce 100644 --- a/operator/webhooks/pod_mutating_webhook.go +++ b/operator/webhooks/pod_mutating_webhook.go @@ -13,6 +13,7 @@ import ( klcv1alpha2 "github.com/keptn/lifecycle-toolkit/operator/apis/lifecycle/v1alpha2" apicommon "github.com/keptn/lifecycle-toolkit/operator/apis/lifecycle/v1alpha2/common" "github.com/keptn/lifecycle-toolkit/operator/apis/lifecycle/v1alpha2/semconv" + controllercommon "github.com/keptn/lifecycle-toolkit/operator/controllers/common" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/codes" "go.opentelemetry.io/otel/propagation" @@ -306,12 +307,12 @@ func (a *PodMutatingWebhook) handleWorkload(ctx context.Context, logger logr.Log err = a.Client.Create(ctx, workload) if err != nil { logger.Error(err, "Could not create Workload") - a.Recorder.Event(workload, "Warning", "WorkloadNotCreated", fmt.Sprintf("Could not create KeptnWorkload / Namespace: %s, Name: %s ", workload.Namespace, workload.Name)) + controllercommon.RecordEvent(a.Recorder, apicommon.PhaseCreateWorkload, "Warning", workload, "WorkloadNotCreated", "could not create KeptnWorkload", workload.Spec.Version) span.SetStatus(codes.Error, err.Error()) return err } - a.Recorder.Event(workload, "Normal", "WorkloadCreated", fmt.Sprintf("KeptnWorkload created / Namespace: %s, Name: %s ", workload.Namespace, workload.Name)) + controllercommon.RecordEvent(a.Recorder, apicommon.PhaseCreateWorkload, "Normal", workload, "WorkloadCreated", "created KeptnWorkload", workload.Spec.Version) return nil } @@ -331,12 +332,12 @@ func (a *PodMutatingWebhook) handleWorkload(ctx context.Context, logger logr.Log err = a.Client.Update(ctx, workload) if err != nil { logger.Error(err, "Could not update Workload") - a.Recorder.Event(workload, "Warning", "WorkloadNotUpdated", fmt.Sprintf("Could not update KeptnWorkload / Namespace: %s, Name: %s ", workload.Namespace, workload.Name)) + controllercommon.RecordEvent(a.Recorder, apicommon.PhaseCreateWorkload, "Warning", workload, "WorkloadNotUpdated", "could not update KeptnWorkload", workload.Spec.Version) span.SetStatus(codes.Error, err.Error()) return err } - a.Recorder.Event(workload, "Normal", "WorkloadUpdated", fmt.Sprintf("KeptnWorkload updated / Namespace: %s, Name: %s ", workload.Namespace, workload.Name)) + controllercommon.RecordEvent(a.Recorder, apicommon.PhaseCreateWorkload, "Normal", workload, "WorkloadUpdated", "updated KeptnWorkload", workload.Spec.Version) return nil } @@ -361,12 +362,12 @@ func (a *PodMutatingWebhook) handleApp(ctx context.Context, logger logr.Logger, err = a.Client.Create(ctx, app) if err != nil { logger.Error(err, "Could not create App") - a.Recorder.Event(app, "Warning", "AppNotCreated", fmt.Sprintf("Could not create KeptnApp / Namespace: %s, Name: %s ", app.Namespace, app.Name)) + controllercommon.RecordEvent(a.Recorder, apicommon.PhaseCreateApp, "Warning", app, "AppNotCreated", "could not create KeptnApp", app.Spec.Version) span.SetStatus(codes.Error, err.Error()) return err } - a.Recorder.Event(app, "Normal", "AppCreated", fmt.Sprintf("KeptnApp created / Namespace: %s, Name: %s ", app.Namespace, app.Name)) + controllercommon.RecordEvent(a.Recorder, apicommon.PhaseCreateApp, "Normal", app, "AppCreated", "created KeptnApp", app.Spec.Version) return nil } @@ -386,12 +387,12 @@ func (a *PodMutatingWebhook) handleApp(ctx context.Context, logger logr.Logger, err = a.Client.Update(ctx, app) if err != nil { logger.Error(err, "Could not update App") - a.Recorder.Event(app, "Warning", "AppNotUpdated", fmt.Sprintf("Could not update KeptnApp / Namespace: %s, Name: %s ", app.Namespace, app.Name)) + controllercommon.RecordEvent(a.Recorder, apicommon.PhaseCreateApp, "Warning", app, "AppNotUpdated", "could not update KeptnApp", app.Spec.Version) span.SetStatus(codes.Error, err.Error()) return err } - a.Recorder.Event(app, "Normal", "AppUpdated", fmt.Sprintf("KeptnApp updated / Namespace: %s, Name: %s ", app.Namespace, app.Name)) + controllercommon.RecordEvent(a.Recorder, apicommon.PhaseCreateApp, "Normal", app, "AppUpdated", "updated KeptnApp", app.Spec.Version) return nil }