diff --git a/operator/api/v1alpha1/common/common.go b/operator/api/v1alpha1/common/common.go index 182975445b..a2173b6078 100644 --- a/operator/api/v1alpha1/common/common.go +++ b/operator/api/v1alpha1/common/common.go @@ -64,47 +64,47 @@ func (k KeptnState) IsPending() bool { type StatusSummary struct { Total int - progressing int - failed int - succeeded int - pending int - unknown int - cancelled int + Progressing int + Failed int + Succeeded int + Pending int + Unknown int + Cancelled int } func UpdateStatusSummary(status KeptnState, summary StatusSummary) StatusSummary { switch status { case StateFailed: - summary.failed++ + summary.Failed++ case StateCancelled: - summary.cancelled++ + summary.Cancelled++ case StateSucceeded: - summary.succeeded++ + summary.Succeeded++ case StateProgressing: - summary.progressing++ + summary.Progressing++ case StatePending, "": - summary.pending++ + summary.Pending++ case StateUnknown: - summary.unknown++ + summary.Unknown++ } return summary } func (s StatusSummary) GetTotalCount() int { - return s.failed + s.succeeded + s.progressing + s.pending + s.unknown + s.cancelled + return s.Failed + s.Succeeded + s.Progressing + s.Pending + s.Unknown + s.Cancelled } func GetOverallState(s StatusSummary) KeptnState { - if s.failed > 0 || s.cancelled > 0 { + if s.Failed > 0 || s.Cancelled > 0 { return StateFailed } - if s.progressing > 0 { + if s.Progressing > 0 { return StateProgressing } - if s.pending > 0 { + if s.Pending > 0 { return StatePending } - if s.unknown > 0 || s.GetTotalCount() != s.Total { + if s.Unknown > 0 || s.GetTotalCount() != s.Total { return StateUnknown } return StateSucceeded diff --git a/operator/api/v1alpha1/keptnappversion_types.go b/operator/api/v1alpha1/keptnappversion_types.go index 3138d1538c..e01ffe9de9 100644 --- a/operator/api/v1alpha1/keptnappversion_types.go +++ b/operator/api/v1alpha1/keptnappversion_types.go @@ -299,12 +299,11 @@ func (a KeptnAppVersion) GetVersion() string { return a.Spec.Version } -func (a KeptnAppVersion) GenerateTask(traceContextCarrier propagation.MapCarrier, taskDefinition string, checkType common.CheckType) KeptnTask { +func (a KeptnAppVersion) GenerateTask(taskDefinition string, checkType common.CheckType) KeptnTask { return KeptnTask{ ObjectMeta: metav1.ObjectMeta{ - Name: common.GenerateTaskName(checkType, taskDefinition), - Namespace: a.Namespace, - Annotations: traceContextCarrier, + Name: common.GenerateTaskName(checkType, taskDefinition), + Namespace: a.Namespace, }, Spec: KeptnTaskSpec{ AppVersion: a.GetVersion(), @@ -317,12 +316,11 @@ func (a KeptnAppVersion) GenerateTask(traceContextCarrier propagation.MapCarrier } } -func (a KeptnAppVersion) GenerateEvaluation(traceContextCarrier propagation.MapCarrier, evaluationDefinition string, checkType common.CheckType) KeptnEvaluation { +func (a KeptnAppVersion) GenerateEvaluation(evaluationDefinition string, checkType common.CheckType) KeptnEvaluation { return KeptnEvaluation{ ObjectMeta: metav1.ObjectMeta{ - Name: common.GenerateEvaluationName(checkType, evaluationDefinition), - Namespace: a.Namespace, - Annotations: traceContextCarrier, + Name: common.GenerateEvaluationName(checkType, evaluationDefinition), + Namespace: a.Namespace, }, Spec: KeptnEvaluationSpec{ AppVersion: a.Spec.Version, diff --git a/operator/api/v1alpha1/keptnevaluation_types.go b/operator/api/v1alpha1/keptnevaluation_types.go index 4f05e1d248..00dbcef7cb 100644 --- a/operator/api/v1alpha1/keptnevaluation_types.go +++ b/operator/api/v1alpha1/keptnevaluation_types.go @@ -21,6 +21,7 @@ import ( "github.com/keptn/lifecycle-toolkit/operator/api/v1alpha1/common" "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/propagation" "go.opentelemetry.io/otel/trace" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "sigs.k8s.io/controller-runtime/pkg/client" @@ -175,3 +176,15 @@ func (e KeptnEvaluation) GetSpanAttributes() []attribute.KeyValue { common.EvaluationType.String(string(e.Spec.Type)), } } + +func (e *KeptnEvaluation) SetPhaseTraceID(phase string, carrier propagation.MapCarrier) { + // present due to SpanItem interface +} + +func (e KeptnEvaluation) GetSpanKey(phase string) string { + return e.Name +} + +func (e KeptnEvaluation) GetSpanName(phase string) string { + return e.Name +} diff --git a/operator/api/v1alpha1/keptntask_types.go b/operator/api/v1alpha1/keptntask_types.go index dfda41757c..e38ad41817 100644 --- a/operator/api/v1alpha1/keptntask_types.go +++ b/operator/api/v1alpha1/keptntask_types.go @@ -21,6 +21,7 @@ import ( "github.com/keptn/lifecycle-toolkit/operator/api/v1alpha1/common" "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/propagation" "go.opentelemetry.io/otel/trace" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "sigs.k8s.io/controller-runtime/pkg/client" @@ -183,3 +184,15 @@ func (t KeptnTask) GetSpanAttributes() []attribute.KeyValue { common.TaskType.String(string(t.Spec.Type)), } } + +func (t *KeptnTask) SetPhaseTraceID(phase string, carrier propagation.MapCarrier) { + // present due to SpanItem interface +} + +func (t KeptnTask) GetSpanKey(phase string) string { + return t.Name +} + +func (t KeptnTask) GetSpanName(phase string) string { + return t.Name +} diff --git a/operator/api/v1alpha1/keptnworkloadinstance_types.go b/operator/api/v1alpha1/keptnworkloadinstance_types.go index 774b03e1e9..58618843bb 100644 --- a/operator/api/v1alpha1/keptnworkloadinstance_types.go +++ b/operator/api/v1alpha1/keptnworkloadinstance_types.go @@ -336,12 +336,11 @@ func (w KeptnWorkloadInstance) GetVersion() string { return w.Spec.Version } -func (w KeptnWorkloadInstance) GenerateTask(traceContextCarrier propagation.MapCarrier, taskDefinition string, checkType common.CheckType) KeptnTask { +func (w KeptnWorkloadInstance) GenerateTask(taskDefinition string, checkType common.CheckType) KeptnTask { return KeptnTask{ ObjectMeta: metav1.ObjectMeta{ - Name: common.GenerateTaskName(checkType, taskDefinition), - Namespace: w.Namespace, - Annotations: traceContextCarrier, + Name: common.GenerateTaskName(checkType, taskDefinition), + Namespace: w.Namespace, }, Spec: KeptnTaskSpec{ AppName: w.GetAppName(), @@ -355,12 +354,11 @@ func (w KeptnWorkloadInstance) GenerateTask(traceContextCarrier propagation.MapC } } -func (w KeptnWorkloadInstance) GenerateEvaluation(traceContextCarrier propagation.MapCarrier, evaluationDefinition string, checkType common.CheckType) KeptnEvaluation { +func (w KeptnWorkloadInstance) GenerateEvaluation(evaluationDefinition string, checkType common.CheckType) KeptnEvaluation { return KeptnEvaluation{ ObjectMeta: metav1.ObjectMeta{ - Name: common.GenerateEvaluationName(checkType, evaluationDefinition), - Namespace: w.Namespace, - Annotations: traceContextCarrier, + Name: common.GenerateEvaluationName(checkType, evaluationDefinition), + Namespace: w.Namespace, }, Spec: KeptnEvaluationSpec{ AppName: w.GetAppName(), diff --git a/operator/api/v1alpha1/tests/keptnappversion_test.go b/operator/api/v1alpha1/tests/keptnappversion_test.go index 31a436ff4f..7c05b573e5 100644 --- a/operator/api/v1alpha1/tests/keptnappversion_test.go +++ b/operator/api/v1alpha1/tests/keptnappversion_test.go @@ -179,7 +179,7 @@ func TestKeptnAppVersion(t *testing.T) { require.Equal(t, "trace1.appname.version.phase", app.GetSpanKey("phase")) - task := app.GenerateTask(map[string]string{}, "taskdef", common.PostDeploymentCheckType) + task := app.GenerateTask("taskdef", common.PostDeploymentCheckType) require.Equal(t, v1alpha1.KeptnTaskSpec{ AppVersion: app.GetVersion(), AppName: app.GetParentName(), @@ -189,7 +189,7 @@ func TestKeptnAppVersion(t *testing.T) { Type: common.PostDeploymentCheckType, }, task.Spec) - evaluation := app.GenerateEvaluation(map[string]string{}, "taskdef", common.PostDeploymentCheckType) + evaluation := app.GenerateEvaluation("taskdef", common.PostDeploymentCheckType) require.Equal(t, v1alpha1.KeptnEvaluationSpec{ AppVersion: app.GetVersion(), AppName: app.GetParentName(), diff --git a/operator/api/v1alpha1/tests/keptnevaluation_test.go b/operator/api/v1alpha1/tests/keptnevaluation_test.go index 991fd4b7d3..0597c3cc21 100644 --- a/operator/api/v1alpha1/tests/keptnevaluation_test.go +++ b/operator/api/v1alpha1/tests/keptnevaluation_test.go @@ -25,6 +25,24 @@ func TestKeptnEvaluation(t *testing.T) { }, } + evaluation.SetPhaseTraceID("", nil) + require.Equal(t, v1alpha1.KeptnEvaluation{ + ObjectMeta: metav1.ObjectMeta{ + Name: "evaluation", + }, + Spec: v1alpha1.KeptnEvaluationSpec{ + AppName: "app", + AppVersion: "appversion", + Type: common.PostDeploymentCheckType, + }, + Status: v1alpha1.KeptnEvaluationStatus{ + OverallStatus: common.StateFailed, + }, + }, *evaluation) + + require.Equal(t, "evaluation", evaluation.GetSpanKey("")) + require.Equal(t, "evaluation", evaluation.GetSpanName("")) + require.False(t, evaluation.IsEndTimeSet()) require.False(t, evaluation.IsStartTimeSet()) diff --git a/operator/api/v1alpha1/tests/keptntask_test.go b/operator/api/v1alpha1/tests/keptntask_test.go index 9958f5f8e8..fdb6a4af8c 100644 --- a/operator/api/v1alpha1/tests/keptntask_test.go +++ b/operator/api/v1alpha1/tests/keptntask_test.go @@ -25,6 +25,24 @@ func TestKeptnTask(t *testing.T) { }, } + task.SetPhaseTraceID("", nil) + require.Equal(t, v1alpha1.KeptnTask{ + ObjectMeta: metav1.ObjectMeta{ + Name: "task", + }, + Spec: v1alpha1.KeptnTaskSpec{ + AppName: "app", + AppVersion: "appversion", + Type: common.PostDeploymentCheckType, + }, + Status: v1alpha1.KeptnTaskStatus{ + Status: common.StateFailed, + }, + }, *task) + + require.Equal(t, "task", task.GetSpanKey("")) + require.Equal(t, "task", task.GetSpanName("")) + require.False(t, task.IsEndTimeSet()) require.False(t, task.IsStartTimeSet()) diff --git a/operator/api/v1alpha1/tests/keptnworkloadinstance_test.go b/operator/api/v1alpha1/tests/keptnworkloadinstance_test.go index 017d5d4213..5741e24082 100644 --- a/operator/api/v1alpha1/tests/keptnworkloadinstance_test.go +++ b/operator/api/v1alpha1/tests/keptnworkloadinstance_test.go @@ -183,7 +183,7 @@ func TestKeptnWorkloadInstance(t *testing.T) { require.Equal(t, "trace1.workloadname.version.phase", workload.GetSpanKey("phase")) - task := workload.GenerateTask(map[string]string{}, "taskdef", common.PostDeploymentCheckType) + task := workload.GenerateTask("taskdef", common.PostDeploymentCheckType) require.Equal(t, v1alpha1.KeptnTaskSpec{ AppName: workload.GetAppName(), WorkloadVersion: workload.GetVersion(), @@ -194,7 +194,7 @@ func TestKeptnWorkloadInstance(t *testing.T) { Type: common.PostDeploymentCheckType, }, task.Spec) - evaluation := workload.GenerateEvaluation(map[string]string{}, "taskdef", common.PostDeploymentCheckType) + evaluation := workload.GenerateEvaluation("taskdef", common.PostDeploymentCheckType) require.Equal(t, v1alpha1.KeptnEvaluationSpec{ AppName: workload.GetAppName(), WorkloadVersion: workload.GetVersion(), diff --git a/operator/controllers/common/errors.go b/operator/controllers/common/errors.go index 5505980539..19bc35ec89 100644 --- a/operator/controllers/common/errors.go +++ b/operator/controllers/common/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 ErrCannotWrapToSpanItem = fmt.Errorf("provided object does not implement SpanItem interface") var ErrRetryCountExceeded = fmt.Errorf("retryCount for evaluation exceeded") var ErrNoValues = fmt.Errorf("no values") var ErrInvalidOperator = fmt.Errorf("invalid operator") @@ -21,3 +22,4 @@ var ErrNoLabelsFoundTask = "no labels found for task: %s" var ErrNoConfigMapMsg = "No ConfigMap specified or HTTP source specified in TaskDefinition) / Namespace: %s, Name: %s" var ErrCannotGetFunctionConfigMap = "could not get function configMap: %w" var ErrCannotFetchAppVersionForWorkloadInstanceMsg = "could not fetch AppVersion for KeptnWorkloadInstance: " +var ErrCouldNotUnbindSpan = "could not unbind span for %s" diff --git a/operator/controllers/common/evaluationhandler.go b/operator/controllers/common/evaluationhandler.go index 6e881e3431..3e115b0d53 100644 --- a/operator/controllers/common/evaluationhandler.go +++ b/operator/controllers/common/evaluationhandler.go @@ -3,12 +3,12 @@ package common import ( "context" "fmt" + "time" "github.com/go-logr/logr" klcv1alpha1 "github.com/keptn/lifecycle-toolkit/operator/api/v1alpha1" apicommon "github.com/keptn/lifecycle-toolkit/operator/api/v1alpha1/common" - "go.opentelemetry.io/otel" - "go.opentelemetry.io/otel/propagation" + "go.opentelemetry.io/otel/codes" "go.opentelemetry.io/otel/trace" "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/runtime" @@ -20,10 +20,11 @@ import ( type EvaluationHandler struct { client.Client - Recorder record.EventRecorder - Log logr.Logger - Tracer trace.Tracer - Scheme *runtime.Scheme + Recorder record.EventRecorder + Log logr.Logger + Tracer trace.Tracer + Scheme *runtime.Scheme + SpanHandler ISpanHandler } type EvaluationCreateAttributes struct { @@ -32,7 +33,7 @@ type EvaluationCreateAttributes struct { CheckType apicommon.CheckType } -func (r EvaluationHandler) ReconcileEvaluations(ctx context.Context, reconcileObject client.Object, evaluationCreateAttributes EvaluationCreateAttributes) ([]klcv1alpha1.EvaluationStatus, apicommon.StatusSummary, error) { +func (r EvaluationHandler) ReconcileEvaluations(ctx context.Context, phaseCtx context.Context, reconcileObject client.Object, evaluationCreateAttributes EvaluationCreateAttributes) ([]klcv1alpha1.EvaluationStatus, apicommon.StatusSummary, error) { piWrapper, err := NewPhaseItemWrapperFromClientObject(reconcileObject) if err != nil { return nil, apicommon.StatusSummary{}, err @@ -101,10 +102,30 @@ func (r EvaluationHandler) ReconcileEvaluations(ctx context.Context, reconcileOb } evaluationStatus.EvaluationName = evaluationName evaluationStatus.SetStartTime() + _, _, err = r.SpanHandler.GetSpan(phaseCtx, r.Tracer, evaluation, "") + if err != nil { + r.Log.Error(err, "could not get span") + } } else { + _, spanEvaluationTrace, err := r.SpanHandler.GetSpan(phaseCtx, r.Tracer, evaluation, "") + if err != nil { + r.Log.Error(err, "could not get span") + } // Update state of Evaluation if it is already created evaluationStatus.Status = evaluation.Status.OverallStatus if evaluationStatus.Status.IsCompleted() { + if evaluationStatus.Status.IsSucceeded() { + spanEvaluationTrace.AddEvent(evaluation.Name + " has finished") + spanEvaluationTrace.SetStatus(codes.Ok, "Finished") + } else { + spanEvaluationTrace.AddEvent(evaluation.Name + " has failed") + r.setEvaluationFailureEvents(evaluation, spanEvaluationTrace) + spanEvaluationTrace.SetStatus(codes.Error, "Failed") + } + spanEvaluationTrace.End() + if err := r.SpanHandler.UnbindSpan(evaluation, ""); err != nil { + r.Log.Error(err, ErrCouldNotUnbindSpan, evaluation.Name) + } evaluationStatus.SetEndTime() } } @@ -127,22 +148,12 @@ func (r EvaluationHandler) CreateKeptnEvaluation(ctx context.Context, namespace return "", err } - ctx, span := r.Tracer.Start(ctx, evaluationCreateAttributes.SpanName, trace.WithSpanKind(trace.SpanKindProducer)) - defer span.End() - - piWrapper.SetSpanAttributes(span) - - // create TraceContext - // follow up with a Keptn propagator that JSON-encoded the OTel map into our own key - traceContextCarrier := propagation.MapCarrier{} - otel.GetTextMapPropagator().Inject(ctx, traceContextCarrier) - phase := apicommon.KeptnPhaseType{ ShortName: "KeptnEvaluationCreate", LongName: "Keptn Evaluation Create", } - newEvaluation := piWrapper.GenerateEvaluation(traceContextCarrier, evaluationCreateAttributes.EvaluationDefinition, evaluationCreateAttributes.CheckType) + newEvaluation := piWrapper.GenerateEvaluation(evaluationCreateAttributes.EvaluationDefinition, evaluationCreateAttributes.CheckType) err = controllerutil.SetControllerReference(reconcileObject, &newEvaluation, r.Scheme) if err != nil { r.Log.Error(err, "could not set controller reference:") @@ -157,3 +168,11 @@ func (r EvaluationHandler) CreateKeptnEvaluation(ctx context.Context, namespace return newEvaluation.Name, nil } + +func (r EvaluationHandler) setEvaluationFailureEvents(evaluation *klcv1alpha1.KeptnEvaluation, spanTrace trace.Span) { + for k, v := range evaluation.Status.EvaluationStatus { + if v.Status == apicommon.StateFailed { + spanTrace.AddEvent(fmt.Sprintf("evaluation of '%s' failed with value: '%s' and reason: '%s'", k, v.Value, v.Message), trace.WithTimestamp(time.Now().UTC())) + } + } +} diff --git a/operator/controllers/common/evaluationhandler_test.go b/operator/controllers/common/evaluationhandler_test.go new file mode 100644 index 0000000000..a84f005719 --- /dev/null +++ b/operator/controllers/common/evaluationhandler_test.go @@ -0,0 +1,313 @@ +package common + +import ( + "context" + "strings" + "testing" + + "github.com/keptn/lifecycle-toolkit/operator/api/v1alpha1" + "github.com/keptn/lifecycle-toolkit/operator/api/v1alpha1/common" + kltfake "github.com/keptn/lifecycle-toolkit/operator/controllers/common/fake" + "github.com/stretchr/testify/require" + "go.opentelemetry.io/otel/trace" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/kubernetes/scheme" + "k8s.io/client-go/tools/record" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/client/fake" +) + +func TestEvaluationHandler(t *testing.T) { + tests := []struct { + name string + object client.Object + createAttr EvaluationCreateAttributes + wantStatus []v1alpha1.EvaluationStatus + wantSummary common.StatusSummary + evalObj v1alpha1.KeptnEvaluation + wantErr error + getSpanCalls int + unbindSpanCalls int + }{ + { + name: "cannot unwrap object", + object: &v1alpha1.KeptnEvaluation{}, + evalObj: v1alpha1.KeptnEvaluation{}, + createAttr: EvaluationCreateAttributes{}, + wantStatus: nil, + wantSummary: common.StatusSummary{}, + wantErr: ErrCannotWrapToPhaseItem, + getSpanCalls: 0, + unbindSpanCalls: 0, + }, + { + name: "no evaluations", + object: &v1alpha1.KeptnAppVersion{}, + evalObj: v1alpha1.KeptnEvaluation{}, + createAttr: EvaluationCreateAttributes{ + SpanName: "", + EvaluationDefinition: "", + CheckType: common.PreDeploymentEvaluationCheckType, + }, + wantStatus: []v1alpha1.EvaluationStatus(nil), + wantSummary: common.StatusSummary{}, + wantErr: nil, + getSpanCalls: 0, + unbindSpanCalls: 0, + }, + { + name: "evaluation not started", + object: &v1alpha1.KeptnAppVersion{ + Spec: v1alpha1.KeptnAppVersionSpec{ + KeptnAppSpec: v1alpha1.KeptnAppSpec{ + PreDeploymentEvaluations: []string{"eval-def"}, + }, + }, + }, + evalObj: v1alpha1.KeptnEvaluation{}, + createAttr: EvaluationCreateAttributes{ + SpanName: "", + EvaluationDefinition: "eval-def", + CheckType: common.PreDeploymentEvaluationCheckType, + }, + wantStatus: []v1alpha1.EvaluationStatus{ + { + EvaluationDefinitionName: "eval-def", + Status: common.StatePending, + EvaluationName: "pre-eval-eval-def-", + }, + }, + wantSummary: common.StatusSummary{Total: 1, Pending: 1}, + wantErr: nil, + getSpanCalls: 1, + unbindSpanCalls: 0, + }, + { + name: "already done evaluation", + object: &v1alpha1.KeptnAppVersion{ + Spec: v1alpha1.KeptnAppVersionSpec{ + KeptnAppSpec: v1alpha1.KeptnAppSpec{ + PreDeploymentEvaluations: []string{"eval-def"}, + }, + }, + Status: v1alpha1.KeptnAppVersionStatus{ + PreDeploymentEvaluationStatus: common.StateSucceeded, + PreDeploymentEvaluationTaskStatus: []v1alpha1.EvaluationStatus{ + { + EvaluationDefinitionName: "eval-def", + Status: common.StateSucceeded, + EvaluationName: "pre-eval-eval-def-", + }, + }, + }, + }, + evalObj: v1alpha1.KeptnEvaluation{}, + createAttr: EvaluationCreateAttributes{ + SpanName: "", + EvaluationDefinition: "eval-def", + CheckType: common.PreDeploymentEvaluationCheckType, + }, + wantStatus: []v1alpha1.EvaluationStatus{ + { + EvaluationDefinitionName: "eval-def", + Status: common.StateSucceeded, + EvaluationName: "pre-eval-eval-def-", + }, + }, + wantSummary: common.StatusSummary{Total: 1, Succeeded: 1}, + wantErr: nil, + getSpanCalls: 0, + unbindSpanCalls: 0, + }, + { + name: "failed evaluation", + object: &v1alpha1.KeptnAppVersion{ + ObjectMeta: v1.ObjectMeta{ + Namespace: "namespace", + }, + Spec: v1alpha1.KeptnAppVersionSpec{ + KeptnAppSpec: v1alpha1.KeptnAppSpec{ + PreDeploymentEvaluations: []string{"eval-def"}, + }, + }, + Status: v1alpha1.KeptnAppVersionStatus{ + PreDeploymentEvaluationStatus: common.StateSucceeded, + PreDeploymentEvaluationTaskStatus: []v1alpha1.EvaluationStatus{ + { + EvaluationDefinitionName: "eval-def", + Status: common.StateProgressing, + EvaluationName: "pre-eval-eval-def-", + }, + }, + }, + }, + evalObj: v1alpha1.KeptnEvaluation{ + ObjectMeta: v1.ObjectMeta{ + Namespace: "namespace", + Name: "pre-eval-eval-def-", + }, + Status: v1alpha1.KeptnEvaluationStatus{ + OverallStatus: common.StateFailed, + }, + }, + createAttr: EvaluationCreateAttributes{ + SpanName: "", + EvaluationDefinition: "eval-def", + CheckType: common.PreDeploymentEvaluationCheckType, + }, + wantStatus: []v1alpha1.EvaluationStatus{ + { + EvaluationDefinitionName: "eval-def", + Status: common.StateFailed, + EvaluationName: "pre-eval-eval-def-", + }, + }, + wantSummary: common.StatusSummary{Total: 1, Failed: 1}, + wantErr: nil, + getSpanCalls: 1, + unbindSpanCalls: 1, + }, + { + name: "succeeded evaluation", + object: &v1alpha1.KeptnAppVersion{ + ObjectMeta: v1.ObjectMeta{ + Namespace: "namespace", + }, + Spec: v1alpha1.KeptnAppVersionSpec{ + KeptnAppSpec: v1alpha1.KeptnAppSpec{ + PreDeploymentEvaluations: []string{"eval-def"}, + }, + }, + Status: v1alpha1.KeptnAppVersionStatus{ + PreDeploymentEvaluationStatus: common.StateSucceeded, + PreDeploymentEvaluationTaskStatus: []v1alpha1.EvaluationStatus{ + { + EvaluationDefinitionName: "eval-def", + Status: common.StateProgressing, + EvaluationName: "pre-eval-eval-def-", + }, + }, + }, + }, + evalObj: v1alpha1.KeptnEvaluation{ + ObjectMeta: v1.ObjectMeta{ + Namespace: "namespace", + Name: "pre-eval-eval-def-", + }, + Status: v1alpha1.KeptnEvaluationStatus{ + OverallStatus: common.StateSucceeded, + }, + }, + createAttr: EvaluationCreateAttributes{ + SpanName: "", + EvaluationDefinition: "eval-def", + CheckType: common.PreDeploymentEvaluationCheckType, + }, + wantStatus: []v1alpha1.EvaluationStatus{ + { + EvaluationDefinitionName: "eval-def", + Status: common.StateSucceeded, + EvaluationName: "pre-eval-eval-def-", + }, + }, + wantSummary: common.StatusSummary{Total: 1, Succeeded: 1}, + wantErr: nil, + getSpanCalls: 1, + unbindSpanCalls: 1, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := v1alpha1.AddToScheme(scheme.Scheme) + require.Nil(t, err) + spanHandlerMock := kltfake.ISpanHandlerMock{ + GetSpanFunc: func(ctx context.Context, tracer trace.Tracer, reconcileObject client.Object, phase string) (context.Context, trace.Span, error) { + return context.TODO(), trace.SpanFromContext(context.TODO()), nil + }, + UnbindSpanFunc: func(reconcileObject client.Object, phase string) error { + return nil + }, + } + handler := EvaluationHandler{ + SpanHandler: &spanHandlerMock, + Log: ctrl.Log.WithName("controller"), + Recorder: record.NewFakeRecorder(100), + Client: fake.NewClientBuilder().WithObjects(&tt.evalObj).Build(), + Tracer: trace.NewNoopTracerProvider().Tracer("tracer"), + Scheme: scheme.Scheme, + } + status, summary, err := handler.ReconcileEvaluations(context.TODO(), context.TODO(), tt.object, tt.createAttr) + if len(tt.wantStatus) == len(status) { + for j, item := range status { + require.Equal(t, tt.wantStatus[j].EvaluationDefinitionName, item.EvaluationDefinitionName) + require.True(t, strings.Contains(item.EvaluationName, tt.wantStatus[j].EvaluationName)) + require.Equal(t, tt.wantStatus[j].Status, item.Status) + } + } else { + t.Errorf("unexpected result, want %+v, got %+v", tt.wantStatus, status) + } + require.Equal(t, tt.wantSummary, summary) + require.Equal(t, tt.wantErr, err) + require.Equal(t, tt.getSpanCalls, len(spanHandlerMock.GetSpanCalls())) + require.Equal(t, tt.unbindSpanCalls, len(spanHandlerMock.UnbindSpanCalls())) + }) + } +} + +func TestEvaluationHandler_createEvaluation(t *testing.T) { + tests := []struct { + name string + object client.Object + createAttr EvaluationCreateAttributes + wantName string + wantErr error + }{ + { + name: "cannot unwrap object", + object: &v1alpha1.KeptnEvaluation{}, + createAttr: EvaluationCreateAttributes{}, + wantName: "", + wantErr: ErrCannotWrapToPhaseItem, + }, + { + name: "created evaluation", + object: &v1alpha1.KeptnAppVersion{ + ObjectMeta: v1.ObjectMeta{ + Namespace: "namespace", + }, + Spec: v1alpha1.KeptnAppVersionSpec{ + KeptnAppSpec: v1alpha1.KeptnAppSpec{ + PreDeploymentEvaluations: []string{"eval-def"}, + }, + }, + }, + createAttr: EvaluationCreateAttributes{ + SpanName: "", + EvaluationDefinition: "eval-def", + CheckType: common.PreDeploymentEvaluationCheckType, + }, + wantName: "pre-eval-eval-def-", + wantErr: nil, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := v1alpha1.AddToScheme(scheme.Scheme) + require.Nil(t, err) + handler := EvaluationHandler{ + SpanHandler: &kltfake.ISpanHandlerMock{}, + Log: ctrl.Log.WithName("controller"), + Recorder: record.NewFakeRecorder(100), + Client: fake.NewClientBuilder().Build(), + Tracer: trace.NewNoopTracerProvider().Tracer("tracer"), + Scheme: scheme.Scheme, + } + name, err := handler.CreateKeptnEvaluation(context.TODO(), "namespace", tt.object, tt.createAttr) + require.True(t, strings.Contains(name, tt.wantName)) + require.Equal(t, tt.wantErr, err) + }) + } +} diff --git a/operator/controllers/common/fake/phaseitem_mock.go b/operator/controllers/common/fake/phaseitem_mock.go index beacdfbf2a..ab94650fcb 100644 --- a/operator/controllers/common/fake/phaseitem_mock.go +++ b/operator/controllers/common/fake/phaseitem_mock.go @@ -7,7 +7,6 @@ import ( lfcv1alpha1 "github.com/keptn/lifecycle-toolkit/operator/api/v1alpha1" keptncommon "github.com/keptn/lifecycle-toolkit/operator/api/v1alpha1/common" "go.opentelemetry.io/otel/attribute" - "go.opentelemetry.io/otel/propagation" "go.opentelemetry.io/otel/trace" "sync" "time" @@ -25,10 +24,10 @@ import ( // CompleteFunc: func() { // panic("mock out the Complete method") // }, -// GenerateEvaluationFunc: func(traceContextCarrier propagation.MapCarrier, evaluationDefinition string, checkType keptncommon.CheckType) lfcv1alpha1.KeptnEvaluation { +// GenerateEvaluationFunc: func(evaluationDefinition string, checkType keptncommon.CheckType) lfcv1alpha1.KeptnEvaluation { // panic("mock out the GenerateEvaluation method") // }, -// GenerateTaskFunc: func(traceContextCarrier propagation.MapCarrier, taskDefinition string, checkType keptncommon.CheckType) lfcv1alpha1.KeptnTask { +// GenerateTaskFunc: func(taskDefinition string, checkType keptncommon.CheckType) lfcv1alpha1.KeptnTask { // panic("mock out the GenerateTask method") // }, // GetAppNameFunc: func() string { @@ -76,12 +75,6 @@ import ( // GetSpanAttributesFunc: func() []attribute.KeyValue { // panic("mock out the GetSpanAttributes method") // }, -// GetSpanKeyFunc: func(phase string) string { -// panic("mock out the GetSpanKey method") -// }, -// GetSpanNameFunc: func(phase string) string { -// panic("mock out the GetSpanName method") -// }, // GetStartTimeFunc: func() time.Time { // panic("mock out the GetStartTime method") // }, @@ -97,9 +90,6 @@ import ( // SetCurrentPhaseFunc: func(s string) { // panic("mock out the SetCurrentPhase method") // }, -// SetPhaseTraceIDFunc: func(phase string, carrier propagation.MapCarrier) { -// panic("mock out the SetPhaseTraceID method") -// }, // SetSpanAttributesFunc: func(span trace.Span) { // panic("mock out the SetSpanAttributes method") // }, @@ -120,10 +110,10 @@ type PhaseItemMock struct { CompleteFunc func() // GenerateEvaluationFunc mocks the GenerateEvaluation method. - GenerateEvaluationFunc func(traceContextCarrier propagation.MapCarrier, evaluationDefinition string, checkType keptncommon.CheckType) lfcv1alpha1.KeptnEvaluation + GenerateEvaluationFunc func(evaluationDefinition string, checkType keptncommon.CheckType) lfcv1alpha1.KeptnEvaluation // GenerateTaskFunc mocks the GenerateTask method. - GenerateTaskFunc func(traceContextCarrier propagation.MapCarrier, taskDefinition string, checkType keptncommon.CheckType) lfcv1alpha1.KeptnTask + GenerateTaskFunc func(taskDefinition string, checkType keptncommon.CheckType) lfcv1alpha1.KeptnTask // GetAppNameFunc mocks the GetAppName method. GetAppNameFunc func() string @@ -170,12 +160,6 @@ type PhaseItemMock struct { // GetSpanAttributesFunc mocks the GetSpanAttributes method. GetSpanAttributesFunc func() []attribute.KeyValue - // GetSpanKeyFunc mocks the GetSpanKey method. - GetSpanKeyFunc func(phase string) string - - // GetSpanNameFunc mocks the GetSpanName method. - GetSpanNameFunc func(phase string) string - // GetStartTimeFunc mocks the GetStartTime method. GetStartTimeFunc func() time.Time @@ -191,9 +175,6 @@ type PhaseItemMock struct { // SetCurrentPhaseFunc mocks the SetCurrentPhase method. SetCurrentPhaseFunc func(s string) - // SetPhaseTraceIDFunc mocks the SetPhaseTraceID method. - SetPhaseTraceIDFunc func(phase string, carrier propagation.MapCarrier) - // SetSpanAttributesFunc mocks the SetSpanAttributes method. SetSpanAttributesFunc func(span trace.Span) @@ -212,8 +193,6 @@ type PhaseItemMock struct { } // GenerateEvaluation holds details about calls to the GenerateEvaluation method. GenerateEvaluation []struct { - // TraceContextCarrier is the traceContextCarrier argument value. - TraceContextCarrier propagation.MapCarrier // EvaluationDefinition is the evaluationDefinition argument value. EvaluationDefinition string // CheckType is the checkType argument value. @@ -221,8 +200,6 @@ type PhaseItemMock struct { } // GenerateTask holds details about calls to the GenerateTask method. GenerateTask []struct { - // TraceContextCarrier is the traceContextCarrier argument value. - TraceContextCarrier propagation.MapCarrier // TaskDefinition is the taskDefinition argument value. TaskDefinition string // CheckType is the checkType argument value. @@ -273,16 +250,6 @@ type PhaseItemMock struct { // GetSpanAttributes holds details about calls to the GetSpanAttributes method. GetSpanAttributes []struct { } - // GetSpanKey holds details about calls to the GetSpanKey method. - GetSpanKey []struct { - // Phase is the phase argument value. - Phase string - } - // GetSpanName holds details about calls to the GetSpanName method. - GetSpanName []struct { - // Phase is the phase argument value. - Phase string - } // GetStartTime holds details about calls to the GetStartTime method. GetStartTime []struct { } @@ -300,13 +267,6 @@ type PhaseItemMock struct { // S is the s argument value. S string } - // SetPhaseTraceID holds details about calls to the SetPhaseTraceID method. - SetPhaseTraceID []struct { - // Phase is the phase argument value. - Phase string - // Carrier is the carrier argument value. - Carrier propagation.MapCarrier - } // SetSpanAttributes holds details about calls to the SetSpanAttributes method. SetSpanAttributes []struct { // Span is the span argument value. @@ -337,14 +297,11 @@ type PhaseItemMock struct { lockGetPreDeploymentTasks sync.RWMutex lockGetPreviousVersion sync.RWMutex lockGetSpanAttributes sync.RWMutex - lockGetSpanKey sync.RWMutex - lockGetSpanName sync.RWMutex lockGetStartTime sync.RWMutex lockGetState sync.RWMutex lockGetVersion sync.RWMutex lockIsEndTimeSet sync.RWMutex lockSetCurrentPhase sync.RWMutex - lockSetPhaseTraceID sync.RWMutex lockSetSpanAttributes sync.RWMutex lockSetState sync.RWMutex } @@ -407,35 +364,31 @@ func (mock *PhaseItemMock) CompleteCalls() []struct { } // GenerateEvaluation calls GenerateEvaluationFunc. -func (mock *PhaseItemMock) GenerateEvaluation(traceContextCarrier propagation.MapCarrier, evaluationDefinition string, checkType keptncommon.CheckType) lfcv1alpha1.KeptnEvaluation { +func (mock *PhaseItemMock) GenerateEvaluation(evaluationDefinition string, checkType keptncommon.CheckType) lfcv1alpha1.KeptnEvaluation { if mock.GenerateEvaluationFunc == nil { panic("PhaseItemMock.GenerateEvaluationFunc: method is nil but PhaseItem.GenerateEvaluation was just called") } callInfo := struct { - TraceContextCarrier propagation.MapCarrier EvaluationDefinition string CheckType keptncommon.CheckType }{ - TraceContextCarrier: traceContextCarrier, EvaluationDefinition: evaluationDefinition, CheckType: checkType, } mock.lockGenerateEvaluation.Lock() mock.calls.GenerateEvaluation = append(mock.calls.GenerateEvaluation, callInfo) mock.lockGenerateEvaluation.Unlock() - return mock.GenerateEvaluationFunc(traceContextCarrier, evaluationDefinition, checkType) + return mock.GenerateEvaluationFunc(evaluationDefinition, checkType) } // GenerateEvaluationCalls gets all the calls that were made to GenerateEvaluation. // Check the length with: // len(mockedPhaseItem.GenerateEvaluationCalls()) func (mock *PhaseItemMock) GenerateEvaluationCalls() []struct { - TraceContextCarrier propagation.MapCarrier EvaluationDefinition string CheckType keptncommon.CheckType } { var calls []struct { - TraceContextCarrier propagation.MapCarrier EvaluationDefinition string CheckType keptncommon.CheckType } @@ -446,37 +399,33 @@ func (mock *PhaseItemMock) GenerateEvaluationCalls() []struct { } // GenerateTask calls GenerateTaskFunc. -func (mock *PhaseItemMock) GenerateTask(traceContextCarrier propagation.MapCarrier, taskDefinition string, checkType keptncommon.CheckType) lfcv1alpha1.KeptnTask { +func (mock *PhaseItemMock) GenerateTask(taskDefinition string, checkType keptncommon.CheckType) lfcv1alpha1.KeptnTask { if mock.GenerateTaskFunc == nil { panic("PhaseItemMock.GenerateTaskFunc: method is nil but PhaseItem.GenerateTask was just called") } callInfo := struct { - TraceContextCarrier propagation.MapCarrier - TaskDefinition string - CheckType keptncommon.CheckType + TaskDefinition string + CheckType keptncommon.CheckType }{ - TraceContextCarrier: traceContextCarrier, - TaskDefinition: taskDefinition, - CheckType: checkType, + TaskDefinition: taskDefinition, + CheckType: checkType, } mock.lockGenerateTask.Lock() mock.calls.GenerateTask = append(mock.calls.GenerateTask, callInfo) mock.lockGenerateTask.Unlock() - return mock.GenerateTaskFunc(traceContextCarrier, taskDefinition, checkType) + return mock.GenerateTaskFunc(taskDefinition, checkType) } // GenerateTaskCalls gets all the calls that were made to GenerateTask. // Check the length with: // len(mockedPhaseItem.GenerateTaskCalls()) func (mock *PhaseItemMock) GenerateTaskCalls() []struct { - TraceContextCarrier propagation.MapCarrier - TaskDefinition string - CheckType keptncommon.CheckType + TaskDefinition string + CheckType keptncommon.CheckType } { var calls []struct { - TraceContextCarrier propagation.MapCarrier - TaskDefinition string - CheckType keptncommon.CheckType + TaskDefinition string + CheckType keptncommon.CheckType } mock.lockGenerateTask.RLock() calls = mock.calls.GenerateTask @@ -874,68 +823,6 @@ func (mock *PhaseItemMock) GetSpanAttributesCalls() []struct { return calls } -// GetSpanKey calls GetSpanKeyFunc. -func (mock *PhaseItemMock) GetSpanKey(phase string) string { - if mock.GetSpanKeyFunc == nil { - panic("PhaseItemMock.GetSpanKeyFunc: method is nil but PhaseItem.GetSpanKey was just called") - } - callInfo := struct { - Phase string - }{ - Phase: phase, - } - mock.lockGetSpanKey.Lock() - mock.calls.GetSpanKey = append(mock.calls.GetSpanKey, callInfo) - mock.lockGetSpanKey.Unlock() - return mock.GetSpanKeyFunc(phase) -} - -// GetSpanKeyCalls gets all the calls that were made to GetSpanKey. -// Check the length with: -// len(mockedPhaseItem.GetSpanKeyCalls()) -func (mock *PhaseItemMock) GetSpanKeyCalls() []struct { - Phase string -} { - var calls []struct { - Phase string - } - mock.lockGetSpanKey.RLock() - calls = mock.calls.GetSpanKey - mock.lockGetSpanKey.RUnlock() - return calls -} - -// GetSpanName calls GetSpanNameFunc. -func (mock *PhaseItemMock) GetSpanName(phase string) string { - if mock.GetSpanNameFunc == nil { - panic("PhaseItemMock.GetSpanNameFunc: method is nil but PhaseItem.GetSpanName was just called") - } - callInfo := struct { - Phase string - }{ - Phase: phase, - } - mock.lockGetSpanName.Lock() - mock.calls.GetSpanName = append(mock.calls.GetSpanName, callInfo) - mock.lockGetSpanName.Unlock() - return mock.GetSpanNameFunc(phase) -} - -// GetSpanNameCalls gets all the calls that were made to GetSpanName. -// Check the length with: -// len(mockedPhaseItem.GetSpanNameCalls()) -func (mock *PhaseItemMock) GetSpanNameCalls() []struct { - Phase string -} { - var calls []struct { - Phase string - } - mock.lockGetSpanName.RLock() - calls = mock.calls.GetSpanName - mock.lockGetSpanName.RUnlock() - return calls -} - // GetStartTime calls GetStartTimeFunc. func (mock *PhaseItemMock) GetStartTime() time.Time { if mock.GetStartTimeFunc == nil { @@ -1071,41 +958,6 @@ func (mock *PhaseItemMock) SetCurrentPhaseCalls() []struct { return calls } -// SetPhaseTraceID calls SetPhaseTraceIDFunc. -func (mock *PhaseItemMock) SetPhaseTraceID(phase string, carrier propagation.MapCarrier) { - if mock.SetPhaseTraceIDFunc == nil { - panic("PhaseItemMock.SetPhaseTraceIDFunc: method is nil but PhaseItem.SetPhaseTraceID was just called") - } - callInfo := struct { - Phase string - Carrier propagation.MapCarrier - }{ - Phase: phase, - Carrier: carrier, - } - mock.lockSetPhaseTraceID.Lock() - mock.calls.SetPhaseTraceID = append(mock.calls.SetPhaseTraceID, callInfo) - mock.lockSetPhaseTraceID.Unlock() - mock.SetPhaseTraceIDFunc(phase, carrier) -} - -// SetPhaseTraceIDCalls gets all the calls that were made to SetPhaseTraceID. -// Check the length with: -// len(mockedPhaseItem.SetPhaseTraceIDCalls()) -func (mock *PhaseItemMock) SetPhaseTraceIDCalls() []struct { - Phase string - Carrier propagation.MapCarrier -} { - var calls []struct { - Phase string - Carrier propagation.MapCarrier - } - mock.lockSetPhaseTraceID.RLock() - calls = mock.calls.SetPhaseTraceID - mock.lockSetPhaseTraceID.RUnlock() - return calls -} - // SetSpanAttributes calls SetSpanAttributesFunc. func (mock *PhaseItemMock) SetSpanAttributes(span trace.Span) { if mock.SetSpanAttributesFunc == nil { diff --git a/operator/controllers/common/fake/spanhandler_mock.go b/operator/controllers/common/fake/spanhandler_mock.go index 71406bd529..2efbec10b4 100644 --- a/operator/controllers/common/fake/spanhandler_mock.go +++ b/operator/controllers/common/fake/spanhandler_mock.go @@ -10,25 +10,25 @@ import ( "sync" ) -// SpanHandlerIMock is a mock implementation of common.SpanHandlerI. +// ISpanHandlerMock is a mock implementation of common.ISpanHandler. // -// func TestSomethingThatUsesSpanHandlerI(t *testing.T) { +// func TestSomethingThatUsesISpanHandler(t *testing.T) { // -// // make and configure a mocked common.SpanHandlerI -// mockedSpanHandlerI := &SpanHandlerIMock{ -// GetSpanFunc: func(ctx context.Context, tracer trace.Tracer, reconcileObject client.Object, phase string) (context.Context, trace.Span, error) { -// panic("mock out the GetSpan method") -// }, -// UnbindSpanFunc: func(reconcileObject client.Object, phase string) error { -// panic("mock out the UnbindSpan method") -// }, -// } +// // make and configure a mocked common.ISpanHandler +// mockedISpanHandler := &ISpanHandlerMock{ +// GetSpanFunc: func(ctx context.Context, tracer trace.Tracer, reconcileObject client.Object, phase string) (context.Context, trace.Span, error) { +// panic("mock out the GetSpan method") +// }, +// UnbindSpanFunc: func(reconcileObject client.Object, phase string) error { +// panic("mock out the UnbindSpan method") +// }, +// } // -// // use mockedSpanHandlerI in code that requires common.SpanHandlerI -// // and then make assertions. +// // use mockedISpanHandler in code that requires common.ISpanHandler +// // and then make assertions. // -// } -type SpanHandlerIMock struct { +// } +type ISpanHandlerMock struct { // GetSpanFunc mocks the GetSpan method. GetSpanFunc func(ctx context.Context, tracer trace.Tracer, reconcileObject client.Object, phase string) (context.Context, trace.Span, error) @@ -61,9 +61,9 @@ type SpanHandlerIMock struct { } // GetSpan calls GetSpanFunc. -func (mock *SpanHandlerIMock) GetSpan(ctx context.Context, tracer trace.Tracer, reconcileObject client.Object, phase string) (context.Context, trace.Span, error) { +func (mock *ISpanHandlerMock) GetSpan(ctx context.Context, tracer trace.Tracer, reconcileObject client.Object, phase string) (context.Context, trace.Span, error) { if mock.GetSpanFunc == nil { - panic("SpanHandlerIMock.GetSpanFunc: method is nil but SpanHandlerI.GetSpan was just called") + panic("ISpanHandlerMock.GetSpanFunc: method is nil but ISpanHandler.GetSpan was just called") } callInfo := struct { Ctx context.Context @@ -84,9 +84,8 @@ func (mock *SpanHandlerIMock) GetSpan(ctx context.Context, tracer trace.Tracer, // GetSpanCalls gets all the calls that were made to GetSpan. // Check the length with: -// -// len(mockedSpanHandlerI.GetSpanCalls()) -func (mock *SpanHandlerIMock) GetSpanCalls() []struct { +// len(mockedISpanHandler.GetSpanCalls()) +func (mock *ISpanHandlerMock) GetSpanCalls() []struct { Ctx context.Context Tracer trace.Tracer ReconcileObject client.Object @@ -105,9 +104,9 @@ func (mock *SpanHandlerIMock) GetSpanCalls() []struct { } // UnbindSpan calls UnbindSpanFunc. -func (mock *SpanHandlerIMock) UnbindSpan(reconcileObject client.Object, phase string) error { +func (mock *ISpanHandlerMock) UnbindSpan(reconcileObject client.Object, phase string) error { if mock.UnbindSpanFunc == nil { - panic("SpanHandlerIMock.UnbindSpanFunc: method is nil but SpanHandlerI.UnbindSpan was just called") + panic("ISpanHandlerMock.UnbindSpanFunc: method is nil but ISpanHandler.UnbindSpan was just called") } callInfo := struct { ReconcileObject client.Object @@ -124,9 +123,8 @@ func (mock *SpanHandlerIMock) UnbindSpan(reconcileObject client.Object, phase st // UnbindSpanCalls gets all the calls that were made to UnbindSpan. // Check the length with: -// -// len(mockedSpanHandlerI.UnbindSpanCalls()) -func (mock *SpanHandlerIMock) UnbindSpanCalls() []struct { +// len(mockedISpanHandler.UnbindSpanCalls()) +func (mock *ISpanHandlerMock) UnbindSpanCalls() []struct { ReconcileObject client.Object Phase string } { diff --git a/operator/controllers/common/fake/spanitem_mock.go b/operator/controllers/common/fake/spanitem_mock.go new file mode 100644 index 0000000000..850a692eb0 --- /dev/null +++ b/operator/controllers/common/fake/spanitem_mock.go @@ -0,0 +1,206 @@ +// Code generated by moq; DO NOT EDIT. +// github.com/matryer/moq + +package fake + +import ( + "go.opentelemetry.io/otel/propagation" + "go.opentelemetry.io/otel/trace" + "sync" +) + +// SpanItemMock is a mock implementation of common.SpanItem. +// +// func TestSomethingThatUsesSpanItem(t *testing.T) { +// +// // make and configure a mocked common.SpanItem +// mockedSpanItem := &SpanItemMock{ +// GetSpanKeyFunc: func(phase string) string { +// panic("mock out the GetSpanKey method") +// }, +// GetSpanNameFunc: func(phase string) string { +// panic("mock out the GetSpanName method") +// }, +// SetPhaseTraceIDFunc: func(phase string, carrier propagation.MapCarrier) { +// panic("mock out the SetPhaseTraceID method") +// }, +// SetSpanAttributesFunc: func(span trace.Span) { +// panic("mock out the SetSpanAttributes method") +// }, +// } +// +// // use mockedSpanItem in code that requires common.SpanItem +// // and then make assertions. +// +// } +type SpanItemMock struct { + // GetSpanKeyFunc mocks the GetSpanKey method. + GetSpanKeyFunc func(phase string) string + + // GetSpanNameFunc mocks the GetSpanName method. + GetSpanNameFunc func(phase string) string + + // SetPhaseTraceIDFunc mocks the SetPhaseTraceID method. + SetPhaseTraceIDFunc func(phase string, carrier propagation.MapCarrier) + + // SetSpanAttributesFunc mocks the SetSpanAttributes method. + SetSpanAttributesFunc func(span trace.Span) + + // calls tracks calls to the methods. + calls struct { + // GetSpanKey holds details about calls to the GetSpanKey method. + GetSpanKey []struct { + // Phase is the phase argument value. + Phase string + } + // GetSpanName holds details about calls to the GetSpanName method. + GetSpanName []struct { + // Phase is the phase argument value. + Phase string + } + // SetPhaseTraceID holds details about calls to the SetPhaseTraceID method. + SetPhaseTraceID []struct { + // Phase is the phase argument value. + Phase string + // Carrier is the carrier argument value. + Carrier propagation.MapCarrier + } + // SetSpanAttributes holds details about calls to the SetSpanAttributes method. + SetSpanAttributes []struct { + // Span is the span argument value. + Span trace.Span + } + } + lockGetSpanKey sync.RWMutex + lockGetSpanName sync.RWMutex + lockSetPhaseTraceID sync.RWMutex + lockSetSpanAttributes sync.RWMutex +} + +// GetSpanKey calls GetSpanKeyFunc. +func (mock *SpanItemMock) GetSpanKey(phase string) string { + if mock.GetSpanKeyFunc == nil { + panic("SpanItemMock.GetSpanKeyFunc: method is nil but SpanItem.GetSpanKey was just called") + } + callInfo := struct { + Phase string + }{ + Phase: phase, + } + mock.lockGetSpanKey.Lock() + mock.calls.GetSpanKey = append(mock.calls.GetSpanKey, callInfo) + mock.lockGetSpanKey.Unlock() + return mock.GetSpanKeyFunc(phase) +} + +// GetSpanKeyCalls gets all the calls that were made to GetSpanKey. +// Check the length with: +// len(mockedSpanItem.GetSpanKeyCalls()) +func (mock *SpanItemMock) GetSpanKeyCalls() []struct { + Phase string +} { + var calls []struct { + Phase string + } + mock.lockGetSpanKey.RLock() + calls = mock.calls.GetSpanKey + mock.lockGetSpanKey.RUnlock() + return calls +} + +// GetSpanName calls GetSpanNameFunc. +func (mock *SpanItemMock) GetSpanName(phase string) string { + if mock.GetSpanNameFunc == nil { + panic("SpanItemMock.GetSpanNameFunc: method is nil but SpanItem.GetSpanName was just called") + } + callInfo := struct { + Phase string + }{ + Phase: phase, + } + mock.lockGetSpanName.Lock() + mock.calls.GetSpanName = append(mock.calls.GetSpanName, callInfo) + mock.lockGetSpanName.Unlock() + return mock.GetSpanNameFunc(phase) +} + +// GetSpanNameCalls gets all the calls that were made to GetSpanName. +// Check the length with: +// len(mockedSpanItem.GetSpanNameCalls()) +func (mock *SpanItemMock) GetSpanNameCalls() []struct { + Phase string +} { + var calls []struct { + Phase string + } + mock.lockGetSpanName.RLock() + calls = mock.calls.GetSpanName + mock.lockGetSpanName.RUnlock() + return calls +} + +// SetPhaseTraceID calls SetPhaseTraceIDFunc. +func (mock *SpanItemMock) SetPhaseTraceID(phase string, carrier propagation.MapCarrier) { + if mock.SetPhaseTraceIDFunc == nil { + panic("SpanItemMock.SetPhaseTraceIDFunc: method is nil but SpanItem.SetPhaseTraceID was just called") + } + callInfo := struct { + Phase string + Carrier propagation.MapCarrier + }{ + Phase: phase, + Carrier: carrier, + } + mock.lockSetPhaseTraceID.Lock() + mock.calls.SetPhaseTraceID = append(mock.calls.SetPhaseTraceID, callInfo) + mock.lockSetPhaseTraceID.Unlock() + mock.SetPhaseTraceIDFunc(phase, carrier) +} + +// SetPhaseTraceIDCalls gets all the calls that were made to SetPhaseTraceID. +// Check the length with: +// len(mockedSpanItem.SetPhaseTraceIDCalls()) +func (mock *SpanItemMock) SetPhaseTraceIDCalls() []struct { + Phase string + Carrier propagation.MapCarrier +} { + var calls []struct { + Phase string + Carrier propagation.MapCarrier + } + mock.lockSetPhaseTraceID.RLock() + calls = mock.calls.SetPhaseTraceID + mock.lockSetPhaseTraceID.RUnlock() + return calls +} + +// SetSpanAttributes calls SetSpanAttributesFunc. +func (mock *SpanItemMock) SetSpanAttributes(span trace.Span) { + if mock.SetSpanAttributesFunc == nil { + panic("SpanItemMock.SetSpanAttributesFunc: method is nil but SpanItem.SetSpanAttributes was just called") + } + callInfo := struct { + Span trace.Span + }{ + Span: span, + } + mock.lockSetSpanAttributes.Lock() + mock.calls.SetSpanAttributes = append(mock.calls.SetSpanAttributes, callInfo) + mock.lockSetSpanAttributes.Unlock() + mock.SetSpanAttributesFunc(span) +} + +// SetSpanAttributesCalls gets all the calls that were made to SetSpanAttributes. +// Check the length with: +// len(mockedSpanItem.SetSpanAttributesCalls()) +func (mock *SpanItemMock) SetSpanAttributesCalls() []struct { + Span trace.Span +} { + var calls []struct { + Span trace.Span + } + mock.lockSetSpanAttributes.RLock() + calls = mock.calls.SetSpanAttributes + mock.lockSetSpanAttributes.RUnlock() + return calls +} diff --git a/operator/controllers/common/fake/tracer.go b/operator/controllers/common/fake/tracer_mock.go similarity index 79% rename from operator/controllers/common/fake/tracer.go rename to operator/controllers/common/fake/tracer_mock.go index 5934e8ba0b..9bdc5314dc 100644 --- a/operator/controllers/common/fake/tracer.go +++ b/operator/controllers/common/fake/tracer_mock.go @@ -11,19 +11,19 @@ import ( // ITracerMock is a mock implementation of common.ITracer. // -// func TestSomethingThatUsesITracer(t *testing.T) { +// func TestSomethingThatUsesITracer(t *testing.T) { // -// // make and configure a mocked common.ITracer -// mockedITracer := &ITracerMock{ -// StartFunc: func(ctx context.Context, spanName string, opts ...trace.SpanStartOption) (context.Context, trace.Span) { -// panic("mock out the Start method") -// }, -// } +// // make and configure a mocked common.ITracer +// mockedITracer := &ITracerMock{ +// StartFunc: func(ctx context.Context, spanName string, opts ...trace.SpanStartOption) (context.Context, trace.Span) { +// panic("mock out the Start method") +// }, +// } // -// // use mockedITracer in code that requires common.ITracer -// // and then make assertions. +// // use mockedITracer in code that requires common.ITracer +// // and then make assertions. // -// } +// } type ITracerMock struct { // StartFunc mocks the Start method. StartFunc func(ctx context.Context, spanName string, opts ...trace.SpanStartOption) (context.Context, trace.Span) @@ -65,8 +65,7 @@ func (mock *ITracerMock) Start(ctx context.Context, spanName string, opts ...tra // StartCalls gets all the calls that were made to Start. // Check the length with: -// -// len(mockedITracer.StartCalls()) +// len(mockedITracer.StartCalls()) func (mock *ITracerMock) StartCalls() []struct { Ctx context.Context SpanName string diff --git a/operator/controllers/common/phasehandler.go b/operator/controllers/common/phasehandler.go index f784a2458b..a98a8a9a26 100644 --- a/operator/controllers/common/phasehandler.go +++ b/operator/controllers/common/phasehandler.go @@ -6,11 +6,9 @@ import ( "time" "github.com/go-logr/logr" - klcv1alpha1 "github.com/keptn/lifecycle-toolkit/operator/api/v1alpha1" "github.com/keptn/lifecycle-toolkit/operator/api/v1alpha1/common" "go.opentelemetry.io/otel/codes" "go.opentelemetry.io/otel/trace" - "k8s.io/apimachinery/pkg/types" "k8s.io/client-go/tools/record" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" @@ -28,16 +26,11 @@ type PhaseResult struct { ctrl.Result } -type failedCheckReason struct { - Message string - Time time.Time -} - func RecordEvent(recorder record.EventRecorder, phase common.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 common.KeptnPhaseType, span trace.Span, reconcilePhase func() (common.KeptnState, error)) (*PhaseResult, error) { +func (r PhaseHandler) HandlePhase(ctx context.Context, ctxTrace context.Context, tracer trace.Tracer, reconcileObject client.Object, phase common.KeptnPhaseType, span trace.Span, reconcilePhase func(phaseCtx context.Context) (common.KeptnState, error)) (*PhaseResult, error) { requeueResult := ctrl.Result{Requeue: true, RequeueAfter: 5 * time.Second} piWrapper, err := NewPhaseItemWrapperFromClientObject(reconcileObject) if err != nil { @@ -51,14 +44,14 @@ func (r PhaseHandler) HandlePhase(ctx context.Context, ctxTrace context.Context, piWrapper.SetCurrentPhase(phase.ShortName) r.Log.Info(phase.LongName + " not finished") - _, spanAppTrace, err := r.SpanHandler.GetSpan(ctxTrace, tracer, reconcileObject, phase.ShortName) + spanPhaseCtx, spanPhaseTrace, err := r.SpanHandler.GetSpan(ctxTrace, tracer, reconcileObject, phase.ShortName) if err != nil { r.Log.Error(err, "could not get span") } - state, err := reconcilePhase() + state, err := reconcilePhase(spanPhaseCtx) if err != nil { - spanAppTrace.AddEvent(phase.LongName + " could not get reconciled") + spanPhaseTrace.AddEvent(phase.LongName + " could not get reconciled") RecordEvent(r.Recorder, phase, "Warning", reconcileObject, "ReconcileErrored", "could not get reconciled", piWrapper.GetVersion()) span.SetStatus(codes.Error, err.Error()) return &PhaseResult{Continue: false, Result: requeueResult}, err @@ -68,31 +61,24 @@ func (r PhaseHandler) HandlePhase(ctx context.Context, ctxTrace context.Context, state = common.StateProgressing } - defer func(oldStatus common.KeptnState, oldPhase string, reconcileObject client.Object) { + defer func(ctx context.Context, oldStatus common.KeptnState, oldPhase string, reconcileObject client.Object) { piWrapper, _ := NewPhaseItemWrapperFromClientObject(reconcileObject) if oldStatus != piWrapper.GetState() || oldPhase != piWrapper.GetCurrentPhase() { - ctx, spanAppTrace, err = r.SpanHandler.GetSpan(ctxTrace, tracer, reconcileObject, piWrapper.GetCurrentPhase()) - if err != nil { - r.Log.Error(err, "could not get span") - } if err := r.Status().Update(ctx, reconcileObject); err != nil { r.Log.Error(err, "could not update status") } } - }(oldStatus, oldPhase, reconcileObject) + }(ctx, oldStatus, oldPhase, reconcileObject) if state.IsCompleted() { if state.IsFailed() { piWrapper.Complete() piWrapper.SetState(common.StateFailed) - spanAppTrace.AddEvent(phase.LongName + " has failed") - if err := r.createFailureReasonSpanEvents(ctx, phase, piWrapper, spanAppTrace); err != nil { - r.Log.Error(err, "cannot add failure events to spans") - } - spanAppTrace.SetStatus(codes.Error, "Failed") - spanAppTrace.End() + spanPhaseTrace.AddEvent(phase.LongName + " has failed") + spanPhaseTrace.SetStatus(codes.Error, "Failed") + spanPhaseTrace.End() if err := r.SpanHandler.UnbindSpan(reconcileObject, phase.ShortName); err != nil { - r.Log.Error(err, "cannot unbind span") + r.Log.Error(err, ErrCouldNotUnbindSpan, reconcileObject.GetName()) } RecordEvent(r.Recorder, phase, "Warning", reconcileObject, "Failed", "has failed", piWrapper.GetVersion()) piWrapper.CancelRemainingPhases(phase) @@ -100,11 +86,11 @@ func (r PhaseHandler) HandlePhase(ctx context.Context, ctxTrace context.Context, } piWrapper.SetState(common.StateSucceeded) - spanAppTrace.AddEvent(phase.LongName + " has succeeded") - spanAppTrace.SetStatus(codes.Ok, "Succeeded") - spanAppTrace.End() + spanPhaseTrace.AddEvent(phase.LongName + " has succeeded") + spanPhaseTrace.SetStatus(codes.Ok, "Succeeded") + spanPhaseTrace.End() if err := r.SpanHandler.UnbindSpan(reconcileObject, phase.ShortName); err != nil { - r.Log.Error(err, "cannot unbind span") + r.Log.Error(err, ErrCouldNotUnbindSpan, reconcileObject.GetName()) } RecordEvent(r.Recorder, phase, "Normal", reconcileObject, "Succeeded", "has succeeded", piWrapper.GetVersion()) @@ -116,87 +102,3 @@ func (r PhaseHandler) HandlePhase(ctx context.Context, ctxTrace context.Context, return &PhaseResult{Continue: false, Result: requeueResult}, nil } - -func (r PhaseHandler) createFailureReasonSpanEvents(ctx context.Context, phase common.KeptnPhaseType, object *PhaseItemWrapper, spanTrace trace.Span) error { - var messageObjects []failedCheckReason - var err error - if phase.IsEvaluation() { - messageObjects, err = r.GetEvaluationFailureReasons(ctx, phase, object) - } else if phase.IsTask() { - messageObjects, err = r.GetTaskFailureReasons(ctx, phase, object) - } - - if err != nil { - return err - } - - for _, msgObject := range messageObjects { - spanTrace.AddEvent(msgObject.Message, trace.WithTimestamp(msgObject.Time)) - } - - return nil -} - -func (r PhaseHandler) GetEvaluationFailureReasons(ctx context.Context, phase common.KeptnPhaseType, object PhaseItem) ([]failedCheckReason, error) { - resultEvents := []failedCheckReason{} - var status []klcv1alpha1.EvaluationStatus - if phase.IsPreEvaluation() { - status = object.GetPreDeploymentEvaluationTaskStatus() - } else { - status = object.GetPostDeploymentEvaluationTaskStatus() - } - - // there can be only one evaluation and in this section of the code, it can only be failed - // checking length of the status only for safety reasons - if len(status) != 1 { - return nil, fmt.Errorf("evaluation status not found for %s/%s", object.GetAppName(), object.GetParentName()) - } - - evaluation := &klcv1alpha1.KeptnEvaluation{} - if err := r.Client.Get(ctx, types.NamespacedName{Name: status[0].EvaluationName, Namespace: object.GetNamespace()}, evaluation); err != nil { - return nil, fmt.Errorf("evaluation %s not found for %s/%s", status[0].EvaluationName, object.GetAppName(), object.GetParentName()) - } - - for k, v := range evaluation.Status.EvaluationStatus { - if v.Status == common.StateFailed { - obj := failedCheckReason{ - Time: evaluation.Status.EndTime.Time, - Message: fmt.Sprintf("evaluation of '%s' failed with value: '%s' and reason: '%s'", k, v.Value, v.Message), - } - resultEvents = append(resultEvents, obj) - } - } - - return resultEvents, nil -} - -func (r PhaseHandler) GetTaskFailureReasons(ctx context.Context, phase common.KeptnPhaseType, object PhaseItem) ([]failedCheckReason, error) { - resultEvents := []failedCheckReason{} - var failedTasks []klcv1alpha1.KeptnTask - var status []klcv1alpha1.TaskStatus - if phase.IsPreTask() { - status = object.GetPreDeploymentTaskStatus() - } else { - status = object.GetPostDeploymentTaskStatus() - } - - for _, item := range status { - if item.Status == common.StateFailed { - task := &klcv1alpha1.KeptnTask{} - if err := r.Client.Get(ctx, types.NamespacedName{Name: item.TaskName, Namespace: object.GetNamespace()}, task); err != nil { - return nil, fmt.Errorf("task %s not found for %s/%s", item.TaskName, object.GetAppName(), object.GetParentName()) - } - failedTasks = append(failedTasks, *task) - } - } - - for _, task := range failedTasks { - obj := failedCheckReason{ - Time: task.Status.EndTime.Time, - Message: fmt.Sprintf("task '%s' failed with reason: '%s'", task.Name, task.Status.Message), - } - resultEvents = append(resultEvents, obj) - } - - return resultEvents, nil -} diff --git a/operator/controllers/common/phasehandler_test.go b/operator/controllers/common/phasehandler_test.go index 1e90f9c200..615abf63f4 100644 --- a/operator/controllers/common/phasehandler_test.go +++ b/operator/controllers/common/phasehandler_test.go @@ -18,14 +18,13 @@ import ( ) func TestPhaseHandler(t *testing.T) { - //phase, state, endtimeset requeueResult := ctrl.Result{Requeue: true, RequeueAfter: 5 * time.Second} tests := []struct { name string handler PhaseHandler object *v1alpha1.KeptnAppVersion phase common.KeptnPhaseType - reconcilePhase func() (common.KeptnState, error) + reconcilePhase func(phaseCtx context.Context) (common.KeptnState, error) wantObject *v1alpha1.KeptnAppVersion want *PhaseResult wantErr error @@ -64,7 +63,7 @@ func TestPhaseHandler(t *testing.T) { }, }, phase: common.PhaseAppDeployment, - reconcilePhase: func() (common.KeptnState, error) { + reconcilePhase: func(phaseCtx context.Context) (common.KeptnState, error) { return "", fmt.Errorf("some err") }, want: &PhaseResult{Continue: false, Result: requeueResult}, @@ -91,7 +90,7 @@ func TestPhaseHandler(t *testing.T) { }, }, phase: common.PhaseAppDeployment, - reconcilePhase: func() (common.KeptnState, error) { + reconcilePhase: func(phaseCtx context.Context) (common.KeptnState, error) { return common.StatePending, nil }, want: &PhaseResult{Continue: false, Result: requeueResult}, @@ -118,7 +117,7 @@ func TestPhaseHandler(t *testing.T) { }, }, phase: common.PhaseAppDeployment, - reconcilePhase: func() (common.KeptnState, error) { + reconcilePhase: func(phaseCtx context.Context) (common.KeptnState, error) { return common.StateProgressing, nil }, want: &PhaseResult{Continue: false, Result: requeueResult}, @@ -145,7 +144,7 @@ func TestPhaseHandler(t *testing.T) { }, }, phase: common.PhaseAppDeployment, - reconcilePhase: func() (common.KeptnState, error) { + reconcilePhase: func(phaseCtx context.Context) (common.KeptnState, error) { return common.StateSucceeded, nil }, want: &PhaseResult{Continue: true, Result: requeueResult}, @@ -172,7 +171,7 @@ func TestPhaseHandler(t *testing.T) { }, }, phase: common.PhaseAppPreEvaluation, - reconcilePhase: func() (common.KeptnState, error) { + reconcilePhase: func(phaseCtx context.Context) (common.KeptnState, error) { return common.StateFailed, nil }, want: &PhaseResult{Continue: false, Result: ctrl.Result{}}, @@ -200,7 +199,7 @@ func TestPhaseHandler(t *testing.T) { }, }, phase: common.PhaseAppPreEvaluation, - reconcilePhase: func() (common.KeptnState, error) { + reconcilePhase: func(phaseCtx context.Context) (common.KeptnState, error) { return common.StateUnknown, nil }, want: &PhaseResult{Continue: false, Result: requeueResult}, @@ -225,249 +224,3 @@ func TestPhaseHandler(t *testing.T) { }) } } - -func TestPhaseHandler_GetEvaluationFailureReasons(t *testing.T) { - tests := []struct { - name string - handler PhaseHandler - object *v1alpha1.KeptnAppVersion - clientObject *v1alpha1.KeptnEvaluation - phase common.KeptnPhaseType - want []failedCheckReason - wantErr error - }{ - { - name: "status len is 0", - handler: PhaseHandler{ - SpanHandler: &SpanHandler{}, - Log: ctrl.Log.WithName("controller"), - Recorder: record.NewFakeRecorder(100), - }, - object: &v1alpha1.KeptnAppVersion{ - Status: v1alpha1.KeptnAppVersionStatus{ - Status: common.StateFailed, - CurrentPhase: common.PhaseAppPreEvaluation.LongName, - }, - }, - clientObject: &v1alpha1.KeptnEvaluation{}, - phase: common.PhaseAppPreEvaluation, - want: nil, - wantErr: fmt.Errorf("evaluation status not found for /"), - }, - { - name: "cannot get evaluation", - handler: PhaseHandler{ - SpanHandler: &SpanHandler{}, - Log: ctrl.Log.WithName("controller"), - Recorder: record.NewFakeRecorder(100), - }, - object: &v1alpha1.KeptnAppVersion{ - ObjectMeta: v1.ObjectMeta{ - Name: "appversion", - Namespace: "namespace", - }, - Status: v1alpha1.KeptnAppVersionStatus{ - Status: common.StateFailed, - CurrentPhase: common.PhaseAppPreEvaluation.LongName, - PreDeploymentEvaluationTaskStatus: []v1alpha1.EvaluationStatus{ - { - EvaluationName: "eval-name", - }, - }, - }, - }, - clientObject: &v1alpha1.KeptnEvaluation{}, - phase: common.PhaseAppPreEvaluation, - want: nil, - wantErr: fmt.Errorf("evaluation eval-name not found for /"), - }, - { - name: "evaluation failed", - handler: PhaseHandler{ - SpanHandler: &SpanHandler{}, - Log: ctrl.Log.WithName("controller"), - Recorder: record.NewFakeRecorder(100), - }, - object: &v1alpha1.KeptnAppVersion{ - ObjectMeta: v1.ObjectMeta{ - Name: "appversion", - Namespace: "namespace", - }, - Status: v1alpha1.KeptnAppVersionStatus{ - Status: common.StateFailed, - CurrentPhase: common.PhaseAppPreEvaluation.LongName, - PreDeploymentEvaluationTaskStatus: []v1alpha1.EvaluationStatus{ - { - EvaluationName: "eval-name", - }, - }, - }, - }, - clientObject: &v1alpha1.KeptnEvaluation{ - ObjectMeta: v1.ObjectMeta{ - Name: "eval-name", - Namespace: "namespace", - }, - Status: v1alpha1.KeptnEvaluationStatus{ - EndTime: v1.Time{Time: time.Date(1, 1, 1, 1, 1, 1, 0, time.Local)}, - EvaluationStatus: map[string]v1alpha1.EvaluationStatusItem{ - "cpu": { - Value: "10", - Status: common.StateFailed, - Message: "cpu failed", - }, - "mem": { - Value: "10", - Status: common.StateSucceeded, - Message: "mem passed", - }, - }, - }, - }, - phase: common.PhaseAppPreEvaluation, - want: []failedCheckReason{ - { - Message: "evaluation of 'cpu' failed with value: '10' and reason: 'cpu failed'", - Time: time.Date(1, 1, 1, 1, 1, 1, 0, time.Local), - }, - }, - wantErr: nil, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - err := v1alpha1.AddToScheme(scheme.Scheme) - require.Nil(t, err) - client := fake.NewClientBuilder().WithObjects(tt.clientObject).Build() - tt.handler.Client = client - result, err := tt.handler.GetEvaluationFailureReasons(context.TODO(), tt.phase, tt.object) - require.Equal(t, tt.want, result) - require.Equal(t, tt.wantErr, err) - }) - } -} - -func TestPhaseHandler_GetTaskFailureReasons(t *testing.T) { - tests := []struct { - name string - handler PhaseHandler - object *v1alpha1.KeptnAppVersion - clientObject *v1alpha1.KeptnTask - phase common.KeptnPhaseType - want []failedCheckReason - wantErr error - }{ - { - name: "no state failed", - handler: PhaseHandler{ - SpanHandler: &SpanHandler{}, - Log: ctrl.Log.WithName("controller"), - Recorder: record.NewFakeRecorder(100), - }, - object: &v1alpha1.KeptnAppVersion{ - ObjectMeta: v1.ObjectMeta{ - Name: "appversion", - Namespace: "namespace", - }, - Status: v1alpha1.KeptnAppVersionStatus{ - Status: common.StateSucceeded, - CurrentPhase: common.PhaseAppPreDeployment.LongName, - PreDeploymentTaskStatus: []v1alpha1.TaskStatus{ - { - TaskName: "task-name", - Status: common.StateSucceeded, - }, - }, - }, - }, - clientObject: &v1alpha1.KeptnTask{}, - phase: common.PhaseAppPreDeployment, - want: []failedCheckReason{}, - wantErr: nil, - }, - { - name: "cannot get task", - handler: PhaseHandler{ - SpanHandler: &SpanHandler{}, - Log: ctrl.Log.WithName("controller"), - Recorder: record.NewFakeRecorder(100), - }, - object: &v1alpha1.KeptnAppVersion{ - ObjectMeta: v1.ObjectMeta{ - Name: "appversion", - Namespace: "namespace", - }, - Status: v1alpha1.KeptnAppVersionStatus{ - Status: common.StateFailed, - CurrentPhase: common.PhaseAppPreDeployment.LongName, - PreDeploymentTaskStatus: []v1alpha1.TaskStatus{ - { - TaskName: "task-name", - Status: common.StateFailed, - }, - }, - }, - }, - clientObject: &v1alpha1.KeptnTask{}, - phase: common.PhaseAppPreDeployment, - want: nil, - wantErr: fmt.Errorf("task task-name not found for /"), - }, - { - name: "task failed", - handler: PhaseHandler{ - SpanHandler: &SpanHandler{}, - Log: ctrl.Log.WithName("controller"), - Recorder: record.NewFakeRecorder(100), - }, - object: &v1alpha1.KeptnAppVersion{ - ObjectMeta: v1.ObjectMeta{ - Name: "appversion", - Namespace: "namespace", - }, - Status: v1alpha1.KeptnAppVersionStatus{ - Status: common.StateFailed, - CurrentPhase: common.PhaseAppPreDeployment.LongName, - PreDeploymentTaskStatus: []v1alpha1.TaskStatus{ - { - TaskName: "task-name", - Status: common.StateFailed, - }, - }, - }, - }, - clientObject: &v1alpha1.KeptnTask{ - ObjectMeta: v1.ObjectMeta{ - Name: "task-name", - Namespace: "namespace", - }, - Status: v1alpha1.KeptnTaskStatus{ - Status: common.StateFailed, - Message: "task failed", - EndTime: v1.Time{Time: time.Date(1, 1, 1, 1, 1, 1, 0, time.Local)}, - }, - }, - phase: common.PhaseAppPreDeployment, - want: []failedCheckReason{ - { - Message: "task 'task-name' failed with reason: 'task failed'", - Time: time.Date(1, 1, 1, 1, 1, 1, 0, time.Local), - }, - }, - wantErr: nil, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - err := v1alpha1.AddToScheme(scheme.Scheme) - require.Nil(t, err) - client := fake.NewClientBuilder().WithObjects(tt.clientObject).Build() - tt.handler.Client = client - result, err := tt.handler.GetTaskFailureReasons(context.TODO(), tt.phase, tt.object) - require.Equal(t, tt.want, result) - require.Equal(t, tt.wantErr, err) - }) - } -} diff --git a/operator/controllers/common/phaseitem.go b/operator/controllers/common/phaseitem.go index 9812902dfb..385810cdd0 100644 --- a/operator/controllers/common/phaseitem.go +++ b/operator/controllers/common/phaseitem.go @@ -7,7 +7,6 @@ import ( "github.com/keptn/lifecycle-toolkit/operator/api/v1alpha1/common" apicommon "github.com/keptn/lifecycle-toolkit/operator/api/v1alpha1/common" "go.opentelemetry.io/otel/attribute" - "go.opentelemetry.io/otel/propagation" "go.opentelemetry.io/otel/trace" "sigs.k8s.io/controller-runtime/pkg/client" ) @@ -37,13 +36,10 @@ type PhaseItem interface { GetPostDeploymentEvaluations() []string GetPreDeploymentEvaluationTaskStatus() []klcv1alpha1.EvaluationStatus GetPostDeploymentEvaluationTaskStatus() []klcv1alpha1.EvaluationStatus - GenerateTask(traceContextCarrier propagation.MapCarrier, taskDefinition string, checkType common.CheckType) klcv1alpha1.KeptnTask - GenerateEvaluation(traceContextCarrier propagation.MapCarrier, evaluationDefinition string, checkType common.CheckType) klcv1alpha1.KeptnEvaluation + GenerateTask(taskDefinition string, checkType common.CheckType) klcv1alpha1.KeptnTask + GenerateEvaluation(evaluationDefinition string, checkType common.CheckType) klcv1alpha1.KeptnEvaluation GetSpanAttributes() []attribute.KeyValue - GetSpanKey(phase string) string - GetSpanName(phase string) string SetSpanAttributes(span trace.Span) - SetPhaseTraceID(phase string, carrier propagation.MapCarrier) CancelRemainingPhases(phase common.KeptnPhaseType) } @@ -143,12 +139,12 @@ func (pw PhaseItemWrapper) GetPostDeploymentEvaluationTaskStatus() []klcv1alpha1 return pw.Obj.GetPostDeploymentEvaluationTaskStatus() } -func (pw PhaseItemWrapper) GenerateTask(traceContextCarrier propagation.MapCarrier, taskDefinition string, checkType common.CheckType) klcv1alpha1.KeptnTask { - return pw.Obj.GenerateTask(traceContextCarrier, taskDefinition, checkType) +func (pw PhaseItemWrapper) GenerateTask(taskDefinition string, checkType common.CheckType) klcv1alpha1.KeptnTask { + return pw.Obj.GenerateTask(taskDefinition, checkType) } -func (pw PhaseItemWrapper) GenerateEvaluation(traceContextCarrier propagation.MapCarrier, evaluationDefinition string, checkType common.CheckType) klcv1alpha1.KeptnEvaluation { - return pw.Obj.GenerateEvaluation(traceContextCarrier, evaluationDefinition, checkType) +func (pw PhaseItemWrapper) GenerateEvaluation(evaluationDefinition string, checkType common.CheckType) klcv1alpha1.KeptnEvaluation { + return pw.Obj.GenerateEvaluation(evaluationDefinition, checkType) } func (pw PhaseItemWrapper) SetSpanAttributes(span trace.Span) { @@ -159,18 +155,6 @@ func (pw PhaseItemWrapper) GetSpanAttributes() []attribute.KeyValue { return pw.Obj.GetSpanAttributes() } -func (pw PhaseItemWrapper) GetSpanKey(phase string) string { - return pw.Obj.GetSpanKey(phase) -} - -func (pw PhaseItemWrapper) GetSpanName(phase string) string { - return pw.Obj.GetSpanName(phase) -} - func (pw PhaseItemWrapper) CancelRemainingPhases(phase common.KeptnPhaseType) { pw.Obj.CancelRemainingPhases(phase) } - -func (pw PhaseItemWrapper) SetPhaseTraceID(phase string, carrier propagation.MapCarrier) { - pw.Obj.SetPhaseTraceID(phase, carrier) -} diff --git a/operator/controllers/common/phaseitem_test.go b/operator/controllers/common/phaseitem_test.go index c2766da3c0..db21bed34e 100644 --- a/operator/controllers/common/phaseitem_test.go +++ b/operator/controllers/common/phaseitem_test.go @@ -9,7 +9,6 @@ import ( "github.com/keptn/lifecycle-toolkit/operator/controllers/common/fake" "github.com/stretchr/testify/require" "go.opentelemetry.io/otel/attribute" - "go.opentelemetry.io/otel/propagation" "go.opentelemetry.io/otel/trace" ) @@ -49,12 +48,6 @@ func TestPhaseItem(t *testing.T) { GetSpanAttributesFunc: func() []attribute.KeyValue { return nil }, - GetSpanKeyFunc: func(phase string) string { - return "span" - }, - GetSpanNameFunc: func(phase string) string { - return "name" - }, CompleteFunc: func() { }, IsEndTimeSetFunc: func() bool { @@ -102,18 +95,16 @@ func TestPhaseItem(t *testing.T) { GetPostDeploymentEvaluationTaskStatusFunc: func() []v1alpha1.EvaluationStatus { return nil }, - GenerateTaskFunc: func(traceContextCarrier propagation.MapCarrier, taskDefinition string, checkType common.CheckType) v1alpha1.KeptnTask { + GenerateTaskFunc: func(taskDefinition string, checkType common.CheckType) v1alpha1.KeptnTask { return v1alpha1.KeptnTask{} }, - GenerateEvaluationFunc: func(traceContextCarrier propagation.MapCarrier, evaluationDefinition string, checkType common.CheckType) v1alpha1.KeptnEvaluation { + GenerateEvaluationFunc: func(evaluationDefinition string, checkType common.CheckType) v1alpha1.KeptnEvaluation { return v1alpha1.KeptnEvaluation{} }, SetSpanAttributesFunc: func(span trace.Span) { }, CancelRemainingPhasesFunc: func(phase common.KeptnPhaseType) { }, - SetPhaseTraceIDFunc: func(phase string, carrier propagation.MapCarrier) { - }, } wrapper := PhaseItemWrapper{Obj: &phaseItemMock} @@ -136,12 +127,6 @@ func TestPhaseItem(t *testing.T) { _ = wrapper.GetSpanAttributes() require.Len(t, phaseItemMock.GetSpanAttributesCalls(), 1) - _ = wrapper.GetSpanKey("phase") - require.Len(t, phaseItemMock.GetSpanKeyCalls(), 1) - - _ = wrapper.GetSpanName("phase") - require.Len(t, phaseItemMock.GetSpanNameCalls(), 1) - wrapper.Complete() require.Len(t, phaseItemMock.CompleteCalls(), 1) @@ -190,10 +175,10 @@ func TestPhaseItem(t *testing.T) { _ = wrapper.GetPostDeploymentEvaluationTaskStatus() require.Len(t, phaseItemMock.GetPostDeploymentEvaluationTaskStatusCalls(), 1) - _ = wrapper.GenerateTask(nil, "", common.PostDeploymentCheckType) + _ = wrapper.GenerateTask("", common.PostDeploymentCheckType) require.Len(t, phaseItemMock.GenerateTaskCalls(), 1) - _ = wrapper.GenerateEvaluation(nil, "", common.PostDeploymentCheckType) + _ = wrapper.GenerateEvaluation("", common.PostDeploymentCheckType) require.Len(t, phaseItemMock.GenerateEvaluationCalls(), 1) wrapper.SetSpanAttributes(nil) @@ -202,7 +187,4 @@ func TestPhaseItem(t *testing.T) { wrapper.CancelRemainingPhases(common.PhaseAppDeployment) require.Len(t, phaseItemMock.CancelRemainingPhasesCalls(), 1) - wrapper.SetPhaseTraceID(common.PhaseAppDeployment.LongName, propagation.MapCarrier{}) - require.Len(t, phaseItemMock.SetPhaseTraceIDCalls(), 1) - } diff --git a/operator/controllers/common/spanhandler.go b/operator/controllers/common/spanhandler.go index e6ac77ab7d..4a9dac75eb 100644 --- a/operator/controllers/common/spanhandler.go +++ b/operator/controllers/common/spanhandler.go @@ -18,7 +18,7 @@ type ISpanHandler interface { type keptnSpanCtx struct { Span trace.Span - Ctx context.Context + Ctx context.Context //nolint:all } type SpanHandler struct { @@ -27,7 +27,7 @@ type SpanHandler struct { } func (r *SpanHandler) GetSpan(ctx context.Context, tracer trace.Tracer, reconcileObject client.Object, phase string) (context.Context, trace.Span, error) { - piWrapper, err := NewPhaseItemWrapperFromClientObject(reconcileObject) + piWrapper, err := NewSpanItemWrapperFromClientObject(reconcileObject) if err != nil { return nil, nil, err } @@ -56,7 +56,7 @@ func (r *SpanHandler) GetSpan(ctx context.Context, tracer trace.Tracer, reconcil } func (r *SpanHandler) UnbindSpan(reconcileObject client.Object, phase string) error { - piWrapper, err := NewPhaseItemWrapperFromClientObject(reconcileObject) + piWrapper, err := NewSpanItemWrapperFromClientObject(reconcileObject) if err != nil { return err } diff --git a/operator/controllers/common/spanitem.go b/operator/controllers/common/spanitem.go new file mode 100644 index 0000000000..da73864d5b --- /dev/null +++ b/operator/controllers/common/spanitem.go @@ -0,0 +1,44 @@ +package common + +import ( + "go.opentelemetry.io/otel/propagation" + "go.opentelemetry.io/otel/trace" + "sigs.k8s.io/controller-runtime/pkg/client" +) + +// SpanItem represents an object whose metrics are stored +// +//go:generate moq -pkg fake --skip-ensure -out ./fake/spanitem_mock.go . SpanItem +type SpanItem interface { + SetSpanAttributes(span trace.Span) + SetPhaseTraceID(phase string, carrier propagation.MapCarrier) + GetSpanKey(phase string) string + GetSpanName(phase string) string +} + +type SpanItemWrapper struct { + Obj SpanItem +} + +func NewSpanItemWrapperFromClientObject(object client.Object) (*SpanItemWrapper, error) { + mo, ok := object.(SpanItem) + if !ok { + return nil, ErrCannotWrapToSpanItem + } + return &SpanItemWrapper{Obj: mo}, nil +} + +func (pw SpanItemWrapper) SetPhaseTraceID(phase string, carrier propagation.MapCarrier) { + pw.Obj.SetPhaseTraceID(phase, carrier) +} +func (pw SpanItemWrapper) GetSpanKey(phase string) string { + return pw.Obj.GetSpanKey(phase) +} + +func (pw SpanItemWrapper) GetSpanName(phase string) string { + return pw.Obj.GetSpanName(phase) +} + +func (pw SpanItemWrapper) SetSpanAttributes(span trace.Span) { + pw.Obj.SetSpanAttributes(span) +} diff --git a/operator/controllers/common/spanitem_test.go b/operator/controllers/common/spanitem_test.go new file mode 100644 index 0000000000..b2329ade37 --- /dev/null +++ b/operator/controllers/common/spanitem_test.go @@ -0,0 +1,64 @@ +package common + +import ( + "testing" + + "github.com/keptn/lifecycle-toolkit/operator/api/v1alpha1" + "github.com/keptn/lifecycle-toolkit/operator/api/v1alpha1/common" + "github.com/keptn/lifecycle-toolkit/operator/controllers/common/fake" + "github.com/stretchr/testify/require" + "go.opentelemetry.io/otel/propagation" + "go.opentelemetry.io/otel/trace" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +func TestSpanItemWrapper(t *testing.T) { + evaluation := &v1alpha1.KeptnEvaluation{ + ObjectMeta: v1.ObjectMeta{ + Name: "evaluation", + }, + Spec: v1alpha1.KeptnEvaluationSpec{ + AppName: "app", + AppVersion: "appversion", + Type: common.PostDeploymentCheckType, + }, + Status: v1alpha1.KeptnEvaluationStatus{ + OverallStatus: common.StateFailed, + }, + } + + object, err := NewSpanItemWrapperFromClientObject(evaluation) + require.Nil(t, err) + + require.Equal(t, "evaluation", object.GetSpanKey("")) +} + +func TestSpanItem(t *testing.T) { + spanItemMock := fake.SpanItemMock{ + SetPhaseTraceIDFunc: func(phase string, carrier propagation.MapCarrier) { + }, + SetSpanAttributesFunc: func(span trace.Span) { + }, + GetSpanKeyFunc: func(phase string) string { + return "key" + }, + GetSpanNameFunc: func(phase string) string { + return "name" + }, + } + + wrapper := SpanItemWrapper{Obj: &spanItemMock} + + wrapper.SetPhaseTraceID("", nil) + require.Len(t, spanItemMock.SetPhaseTraceIDCalls(), 1) + + wrapper.SetSpanAttributes(nil) + require.Len(t, spanItemMock.SetSpanAttributesCalls(), 1) + + _ = wrapper.GetSpanKey("") + require.Len(t, spanItemMock.GetSpanKeyCalls(), 1) + + wrapper.GetSpanName("") + require.Len(t, spanItemMock.GetSpanNameCalls(), 1) + +} diff --git a/operator/controllers/common/taskhandler.go b/operator/controllers/common/taskhandler.go index de1315ba66..313badbf99 100644 --- a/operator/controllers/common/taskhandler.go +++ b/operator/controllers/common/taskhandler.go @@ -3,13 +3,13 @@ package common import ( "context" "fmt" + "time" "github.com/go-logr/logr" klcv1alpha1 "github.com/keptn/lifecycle-toolkit/operator/api/v1alpha1" "github.com/keptn/lifecycle-toolkit/operator/api/v1alpha1/common" apicommon "github.com/keptn/lifecycle-toolkit/operator/api/v1alpha1/common" - "go.opentelemetry.io/otel" - "go.opentelemetry.io/otel/propagation" + "go.opentelemetry.io/otel/codes" "go.opentelemetry.io/otel/trace" "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/runtime" @@ -21,10 +21,11 @@ import ( type TaskHandler struct { client.Client - Recorder record.EventRecorder - Log logr.Logger - Tracer trace.Tracer - Scheme *runtime.Scheme + Recorder record.EventRecorder + Log logr.Logger + Tracer trace.Tracer + Scheme *runtime.Scheme + SpanHandler ISpanHandler } type TaskCreateAttributes struct { @@ -33,7 +34,7 @@ type TaskCreateAttributes struct { CheckType common.CheckType } -func (r TaskHandler) ReconcileTasks(ctx context.Context, reconcileObject client.Object, taskCreateAttributes TaskCreateAttributes) ([]klcv1alpha1.TaskStatus, apicommon.StatusSummary, error) { +func (r TaskHandler) ReconcileTasks(ctx context.Context, phaseCtx context.Context, reconcileObject client.Object, taskCreateAttributes TaskCreateAttributes) ([]klcv1alpha1.TaskStatus, apicommon.StatusSummary, error) { piWrapper, err := NewPhaseItemWrapperFromClientObject(reconcileObject) if err != nil { return nil, apicommon.StatusSummary{}, err @@ -102,10 +103,30 @@ func (r TaskHandler) ReconcileTasks(ctx context.Context, reconcileObject client. } taskStatus.TaskName = taskName taskStatus.SetStartTime() + _, _, err = r.SpanHandler.GetSpan(phaseCtx, r.Tracer, task, "") + if err != nil { + r.Log.Error(err, "could not get span") + } } else { + _, spanTaskTrace, err := r.SpanHandler.GetSpan(phaseCtx, r.Tracer, task, "") + if err != nil { + r.Log.Error(err, "could not get span") + } // Update state of Task if it is already created taskStatus.Status = task.Status.Status if taskStatus.Status.IsCompleted() { + if taskStatus.Status.IsSucceeded() { + spanTaskTrace.AddEvent(task.Name + " has finished") + spanTaskTrace.SetStatus(codes.Ok, "Finished") + } else { + spanTaskTrace.AddEvent(task.Name + " has failed") + r.setTaskFailureEvents(task, spanTaskTrace) + spanTaskTrace.SetStatus(codes.Error, "Failed") + } + spanTaskTrace.End() + if err := r.SpanHandler.UnbindSpan(task, ""); err != nil { + r.Log.Error(err, ErrCouldNotUnbindSpan, task.Name) + } taskStatus.SetEndTime() } } @@ -128,22 +149,12 @@ func (r TaskHandler) CreateKeptnTask(ctx context.Context, namespace string, reco return "", err } - ctx, span := r.Tracer.Start(ctx, taskCreateAttributes.SpanName, trace.WithSpanKind(trace.SpanKindProducer)) - defer span.End() - - piWrapper.SetSpanAttributes(span) - - // create TraceContext - // follow up with a Keptn propagator that JSON-encoded the OTel map into our own key - traceContextCarrier := propagation.MapCarrier{} - otel.GetTextMapPropagator().Inject(ctx, traceContextCarrier) - phase := apicommon.KeptnPhaseType{ ShortName: "KeptnTaskCreate", LongName: "Keptn Task Create", } - newTask := piWrapper.GenerateTask(traceContextCarrier, taskCreateAttributes.TaskDefinition, taskCreateAttributes.CheckType) + newTask := piWrapper.GenerateTask(taskCreateAttributes.TaskDefinition, taskCreateAttributes.CheckType) err = controllerutil.SetControllerReference(reconcileObject, &newTask, r.Scheme) if err != nil { r.Log.Error(err, "could not set controller reference:") @@ -158,3 +169,7 @@ func (r TaskHandler) CreateKeptnTask(ctx context.Context, namespace string, reco return newTask.Name, nil } + +func (r TaskHandler) setTaskFailureEvents(task *klcv1alpha1.KeptnTask, spanTrace trace.Span) { + spanTrace.AddEvent(fmt.Sprintf("task '%s' failed with reason: '%s'", task.Name, task.Status.Message), trace.WithTimestamp(time.Now().UTC())) +} diff --git a/operator/controllers/common/taskhandler_test.go b/operator/controllers/common/taskhandler_test.go new file mode 100644 index 0000000000..be768595b3 --- /dev/null +++ b/operator/controllers/common/taskhandler_test.go @@ -0,0 +1,313 @@ +package common + +import ( + "context" + "strings" + "testing" + + "github.com/keptn/lifecycle-toolkit/operator/api/v1alpha1" + "github.com/keptn/lifecycle-toolkit/operator/api/v1alpha1/common" + kltfake "github.com/keptn/lifecycle-toolkit/operator/controllers/common/fake" + "github.com/stretchr/testify/require" + "go.opentelemetry.io/otel/trace" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/kubernetes/scheme" + "k8s.io/client-go/tools/record" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/client/fake" +) + +func TestTaskHandler(t *testing.T) { + tests := []struct { + name string + object client.Object + createAttr TaskCreateAttributes + wantStatus []v1alpha1.TaskStatus + wantSummary common.StatusSummary + taskObj v1alpha1.KeptnTask + wantErr error + getSpanCalls int + unbindSpanCalls int + }{ + { + name: "cannot unwrap object", + object: &v1alpha1.KeptnTask{}, + taskObj: v1alpha1.KeptnTask{}, + createAttr: TaskCreateAttributes{}, + wantStatus: nil, + wantSummary: common.StatusSummary{}, + wantErr: ErrCannotWrapToPhaseItem, + getSpanCalls: 0, + unbindSpanCalls: 0, + }, + { + name: "no tasks", + object: &v1alpha1.KeptnAppVersion{}, + taskObj: v1alpha1.KeptnTask{}, + createAttr: TaskCreateAttributes{ + SpanName: "", + TaskDefinition: "", + CheckType: common.PreDeploymentCheckType, + }, + wantStatus: []v1alpha1.TaskStatus(nil), + wantSummary: common.StatusSummary{}, + wantErr: nil, + getSpanCalls: 0, + unbindSpanCalls: 0, + }, + { + name: "task not started", + object: &v1alpha1.KeptnAppVersion{ + Spec: v1alpha1.KeptnAppVersionSpec{ + KeptnAppSpec: v1alpha1.KeptnAppSpec{ + PreDeploymentTasks: []string{"task-def"}, + }, + }, + }, + taskObj: v1alpha1.KeptnTask{}, + createAttr: TaskCreateAttributes{ + SpanName: "", + TaskDefinition: "task-def", + CheckType: common.PreDeploymentCheckType, + }, + wantStatus: []v1alpha1.TaskStatus{ + { + TaskDefinitionName: "task-def", + Status: common.StatePending, + TaskName: "pre-task-def-", + }, + }, + wantSummary: common.StatusSummary{Total: 1, Pending: 1}, + wantErr: nil, + getSpanCalls: 1, + unbindSpanCalls: 0, + }, + { + name: "already done task", + object: &v1alpha1.KeptnAppVersion{ + Spec: v1alpha1.KeptnAppVersionSpec{ + KeptnAppSpec: v1alpha1.KeptnAppSpec{ + PreDeploymentTasks: []string{"task-def"}, + }, + }, + Status: v1alpha1.KeptnAppVersionStatus{ + PreDeploymentStatus: common.StateSucceeded, + PreDeploymentTaskStatus: []v1alpha1.TaskStatus{ + { + TaskDefinitionName: "task-def", + Status: common.StateSucceeded, + TaskName: "pre-task-def-", + }, + }, + }, + }, + taskObj: v1alpha1.KeptnTask{}, + createAttr: TaskCreateAttributes{ + SpanName: "", + TaskDefinition: "task-def", + CheckType: common.PreDeploymentCheckType, + }, + wantStatus: []v1alpha1.TaskStatus{ + { + TaskDefinitionName: "task-def", + Status: common.StateSucceeded, + TaskName: "pre-task-def-", + }, + }, + wantSummary: common.StatusSummary{Total: 1, Succeeded: 1}, + wantErr: nil, + getSpanCalls: 0, + unbindSpanCalls: 0, + }, + { + name: "failed task", + object: &v1alpha1.KeptnAppVersion{ + ObjectMeta: v1.ObjectMeta{ + Namespace: "namespace", + }, + Spec: v1alpha1.KeptnAppVersionSpec{ + KeptnAppSpec: v1alpha1.KeptnAppSpec{ + PreDeploymentTasks: []string{"task-def"}, + }, + }, + Status: v1alpha1.KeptnAppVersionStatus{ + PreDeploymentStatus: common.StateSucceeded, + PreDeploymentTaskStatus: []v1alpha1.TaskStatus{ + { + TaskDefinitionName: "task-def", + Status: common.StateProgressing, + TaskName: "pre-task-def-", + }, + }, + }, + }, + taskObj: v1alpha1.KeptnTask{ + ObjectMeta: v1.ObjectMeta{ + Namespace: "namespace", + Name: "pre-task-def-", + }, + Status: v1alpha1.KeptnTaskStatus{ + Status: common.StateFailed, + }, + }, + createAttr: TaskCreateAttributes{ + SpanName: "", + TaskDefinition: "task-def", + CheckType: common.PreDeploymentCheckType, + }, + wantStatus: []v1alpha1.TaskStatus{ + { + TaskDefinitionName: "task-def", + Status: common.StateFailed, + TaskName: "pre-task-def-", + }, + }, + wantSummary: common.StatusSummary{Total: 1, Failed: 1}, + wantErr: nil, + getSpanCalls: 1, + unbindSpanCalls: 1, + }, + { + name: "succeeded task", + object: &v1alpha1.KeptnAppVersion{ + ObjectMeta: v1.ObjectMeta{ + Namespace: "namespace", + }, + Spec: v1alpha1.KeptnAppVersionSpec{ + KeptnAppSpec: v1alpha1.KeptnAppSpec{ + PreDeploymentTasks: []string{"task-def"}, + }, + }, + Status: v1alpha1.KeptnAppVersionStatus{ + PreDeploymentStatus: common.StateSucceeded, + PreDeploymentTaskStatus: []v1alpha1.TaskStatus{ + { + TaskDefinitionName: "task-def", + Status: common.StateProgressing, + TaskName: "pre-task-def-", + }, + }, + }, + }, + taskObj: v1alpha1.KeptnTask{ + ObjectMeta: v1.ObjectMeta{ + Namespace: "namespace", + Name: "pre-task-def-", + }, + Status: v1alpha1.KeptnTaskStatus{ + Status: common.StateSucceeded, + }, + }, + createAttr: TaskCreateAttributes{ + SpanName: "", + TaskDefinition: "task-def", + CheckType: common.PreDeploymentCheckType, + }, + wantStatus: []v1alpha1.TaskStatus{ + { + TaskDefinitionName: "task-def", + Status: common.StateSucceeded, + TaskName: "pre-task-def-", + }, + }, + wantSummary: common.StatusSummary{Total: 1, Succeeded: 1}, + wantErr: nil, + getSpanCalls: 1, + unbindSpanCalls: 1, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := v1alpha1.AddToScheme(scheme.Scheme) + require.Nil(t, err) + spanHandlerMock := kltfake.ISpanHandlerMock{ + GetSpanFunc: func(ctx context.Context, tracer trace.Tracer, reconcileObject client.Object, phase string) (context.Context, trace.Span, error) { + return context.TODO(), trace.SpanFromContext(context.TODO()), nil + }, + UnbindSpanFunc: func(reconcileObject client.Object, phase string) error { + return nil + }, + } + handler := TaskHandler{ + SpanHandler: &spanHandlerMock, + Log: ctrl.Log.WithName("controller"), + Recorder: record.NewFakeRecorder(100), + Client: fake.NewClientBuilder().WithObjects(&tt.taskObj).Build(), + Tracer: trace.NewNoopTracerProvider().Tracer("tracer"), + Scheme: scheme.Scheme, + } + status, summary, err := handler.ReconcileTasks(context.TODO(), context.TODO(), tt.object, tt.createAttr) + if len(tt.wantStatus) == len(status) { + for j, item := range status { + require.Equal(t, tt.wantStatus[j].TaskDefinitionName, item.TaskDefinitionName) + require.True(t, strings.Contains(item.TaskName, tt.wantStatus[j].TaskName)) + require.Equal(t, tt.wantStatus[j].Status, item.Status) + } + } else { + t.Errorf("unexpected result, want %+v, got %+v", tt.wantStatus, status) + } + require.Equal(t, tt.wantSummary, summary) + require.Equal(t, tt.wantErr, err) + require.Equal(t, tt.getSpanCalls, len(spanHandlerMock.GetSpanCalls())) + require.Equal(t, tt.unbindSpanCalls, len(spanHandlerMock.UnbindSpanCalls())) + }) + } +} + +func TestTaskHandler_createTask(t *testing.T) { + tests := []struct { + name string + object client.Object + createAttr TaskCreateAttributes + wantName string + wantErr error + }{ + { + name: "cannot unwrap object", + object: &v1alpha1.KeptnEvaluation{}, + createAttr: TaskCreateAttributes{}, + wantName: "", + wantErr: ErrCannotWrapToPhaseItem, + }, + { + name: "created task", + object: &v1alpha1.KeptnAppVersion{ + ObjectMeta: v1.ObjectMeta{ + Namespace: "namespace", + }, + Spec: v1alpha1.KeptnAppVersionSpec{ + KeptnAppSpec: v1alpha1.KeptnAppSpec{ + PreDeploymentTasks: []string{"task-def"}, + }, + }, + }, + createAttr: TaskCreateAttributes{ + SpanName: "", + CheckType: common.PreDeploymentCheckType, + TaskDefinition: "task-def", + }, + wantName: "pre-task-def-", + wantErr: nil, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := v1alpha1.AddToScheme(scheme.Scheme) + require.Nil(t, err) + handler := TaskHandler{ + SpanHandler: &kltfake.ISpanHandlerMock{}, + Log: ctrl.Log.WithName("controller"), + Recorder: record.NewFakeRecorder(100), + Client: fake.NewClientBuilder().Build(), + Tracer: trace.NewNoopTracerProvider().Tracer("tracer"), + Scheme: scheme.Scheme, + } + name, err := handler.CreateKeptnTask(context.TODO(), "namespace", tt.object, tt.createAttr) + require.True(t, strings.Contains(name, tt.wantName)) + require.Equal(t, tt.wantErr, err) + }) + } +} diff --git a/operator/controllers/common/ITracer.go b/operator/controllers/common/tracer.go similarity index 52% rename from operator/controllers/common/ITracer.go rename to operator/controllers/common/tracer.go index 4684a89928..a8964c4dc6 100644 --- a/operator/controllers/common/ITracer.go +++ b/operator/controllers/common/tracer.go @@ -2,5 +2,5 @@ package common import "go.opentelemetry.io/otel/trace" -//go:generate moq -pkg fake -skip-ensure -out ./fake/tracer.go . ITracer +//go:generate moq -pkg fake -skip-ensure -out ./fake/tracer_mock.go . ITracer type ITracer = trace.Tracer diff --git a/operator/controllers/keptnappversion/controller.go b/operator/controllers/keptnappversion/controller.go index b7583bb462..3fab763f8c 100644 --- a/operator/controllers/keptnappversion/controller.go +++ b/operator/controllers/keptnappversion/controller.go @@ -104,8 +104,8 @@ func (r *KeptnAppVersionReconciler) Reconcile(ctx context.Context, req ctrl.Requ } if !appVersion.IsPreDeploymentSucceeded() { - reconcilePreDep := func() (common.KeptnState, error) { - return r.reconcilePrePostDeployment(ctx, appVersion, common.PreDeploymentCheckType) + reconcilePreDep := func(phaseCtx context.Context) (common.KeptnState, error) { + return r.reconcilePrePostDeployment(ctx, phaseCtx, appVersion, common.PreDeploymentCheckType) } result, err := phaseHandler.HandlePhase(ctx, ctxAppTrace, r.Tracer, appVersion, phase, span, reconcilePreDep) if !result.Continue { @@ -115,8 +115,8 @@ func (r *KeptnAppVersionReconciler) Reconcile(ctx context.Context, req ctrl.Requ phase = common.PhaseAppPreEvaluation if !appVersion.IsPreDeploymentEvaluationSucceeded() { - reconcilePreEval := func() (common.KeptnState, error) { - return r.reconcilePrePostEvaluation(ctx, appVersion, common.PreDeploymentEvaluationCheckType) + reconcilePreEval := func(phaseCtx context.Context) (common.KeptnState, error) { + return r.reconcilePrePostEvaluation(ctx, phaseCtx, appVersion, common.PreDeploymentEvaluationCheckType) } result, err := phaseHandler.HandlePhase(ctx, ctxAppTrace, r.Tracer, appVersion, phase, span, reconcilePreEval) if !result.Continue { @@ -126,7 +126,7 @@ func (r *KeptnAppVersionReconciler) Reconcile(ctx context.Context, req ctrl.Requ phase = common.PhaseAppDeployment if !appVersion.AreWorkloadsSucceeded() { - reconcileAppDep := func() (common.KeptnState, error) { + reconcileAppDep := func(phaseCtx context.Context) (common.KeptnState, error) { return r.reconcileWorkloads(ctx, appVersion) } result, err := phaseHandler.HandlePhase(ctx, ctxAppTrace, r.Tracer, appVersion, phase, span, reconcileAppDep) @@ -137,8 +137,8 @@ func (r *KeptnAppVersionReconciler) Reconcile(ctx context.Context, req ctrl.Requ phase = common.PhaseAppPostDeployment if !appVersion.IsPostDeploymentSucceeded() { - reconcilePostDep := func() (common.KeptnState, error) { - return r.reconcilePrePostDeployment(ctx, appVersion, common.PostDeploymentCheckType) + reconcilePostDep := func(phaseCtx context.Context) (common.KeptnState, error) { + return r.reconcilePrePostDeployment(ctx, phaseCtx, appVersion, common.PostDeploymentCheckType) } result, err := phaseHandler.HandlePhase(ctx, ctxAppTrace, r.Tracer, appVersion, phase, span, reconcilePostDep) if !result.Continue { @@ -148,8 +148,8 @@ func (r *KeptnAppVersionReconciler) Reconcile(ctx context.Context, req ctrl.Requ phase = common.PhaseAppPostEvaluation if !appVersion.IsPostDeploymentEvaluationCompleted() { - reconcilePostEval := func() (common.KeptnState, error) { - return r.reconcilePrePostEvaluation(ctx, appVersion, common.PostDeploymentEvaluationCheckType) + reconcilePostEval := func(phaseCtx context.Context) (common.KeptnState, error) { + return r.reconcilePrePostEvaluation(ctx, phaseCtx, appVersion, common.PostDeploymentEvaluationCheckType) } result, err := phaseHandler.HandlePhase(ctx, ctxAppTrace, r.Tracer, appVersion, phase, span, reconcilePostEval) if !result.Continue { @@ -186,7 +186,7 @@ func (r *KeptnAppVersionReconciler) Reconcile(ctx context.Context, req ctrl.Requ spanAppTrace.SetStatus(codes.Ok, "Finished") spanAppTrace.End() if err := r.SpanHandler.UnbindSpan(appVersion, ""); err != nil { - r.Log.Error(err, "could not unbind span for %s", appVersion.Name) + r.Log.Error(err, controllercommon.ErrCouldNotUnbindSpan, appVersion.Name) } return ctrl.Result{}, nil diff --git a/operator/controllers/keptnappversion/controller_test.go b/operator/controllers/keptnappversion/controller_test.go index c0577c8e7c..5507c38f72 100644 --- a/operator/controllers/keptnappversion/controller_test.go +++ b/operator/controllers/keptnappversion/controller_test.go @@ -156,7 +156,7 @@ func setupReconcilerWithMeters(t *testing.T) *KeptnAppVersionReconciler { return r } -func setupReconciler(t *testing.T) (*KeptnAppVersionReconciler, chan string, *fake.ITracerMock, *fake.SpanHandlerIMock) { +func setupReconciler(t *testing.T) (*KeptnAppVersionReconciler, chan string, *fake.ITracerMock, *fake.ISpanHandlerMock) { //setup logger opts := zap.Options{ Development: true, @@ -170,7 +170,7 @@ func setupReconciler(t *testing.T) (*KeptnAppVersionReconciler, chan string, *fa //fake span handler - spanRecorder := &fake.SpanHandlerIMock{ + spanRecorder := &fake.ISpanHandlerMock{ GetSpanFunc: func(ctx context.Context, tracer trace.Tracer, reconcileObject client.Object, phase string) (context.Context, trace.Span, error) { return ctx, trace.SpanFromContext(ctx), nil }, diff --git a/operator/controllers/keptnappversion/reconcile_prepostdeployment.go b/operator/controllers/keptnappversion/reconcile_prepostdeployment.go index cdfc9d72e2..76e65ca01e 100644 --- a/operator/controllers/keptnappversion/reconcile_prepostdeployment.go +++ b/operator/controllers/keptnappversion/reconcile_prepostdeployment.go @@ -9,13 +9,14 @@ import ( controllercommon "github.com/keptn/lifecycle-toolkit/operator/controllers/common" ) -func (r *KeptnAppVersionReconciler) reconcilePrePostDeployment(ctx context.Context, appVersion *klcv1alpha1.KeptnAppVersion, checkType common.CheckType) (common.KeptnState, error) { +func (r *KeptnAppVersionReconciler) reconcilePrePostDeployment(ctx context.Context, phaseCtx context.Context, appVersion *klcv1alpha1.KeptnAppVersion, checkType common.CheckType) (common.KeptnState, error) { taskHandler := controllercommon.TaskHandler{ - Client: r.Client, - Recorder: r.Recorder, - Log: r.Log, - Tracer: r.Tracer, - Scheme: r.Scheme, + Client: r.Client, + Recorder: r.Recorder, + Log: r.Log, + Tracer: r.Tracer, + Scheme: r.Scheme, + SpanHandler: r.SpanHandler, } taskCreateAttributes := controllercommon.TaskCreateAttributes{ @@ -23,7 +24,7 @@ func (r *KeptnAppVersionReconciler) reconcilePrePostDeployment(ctx context.Conte CheckType: checkType, } - newStatus, state, err := taskHandler.ReconcileTasks(ctx, appVersion, taskCreateAttributes) + newStatus, state, err := taskHandler.ReconcileTasks(ctx, phaseCtx, appVersion, taskCreateAttributes) if err != nil { return common.StateUnknown, err } diff --git a/operator/controllers/keptnappversion/reconcile_prepostevaluation.go b/operator/controllers/keptnappversion/reconcile_prepostevaluation.go index cfdb5087da..6267065fb5 100644 --- a/operator/controllers/keptnappversion/reconcile_prepostevaluation.go +++ b/operator/controllers/keptnappversion/reconcile_prepostevaluation.go @@ -9,13 +9,14 @@ import ( controllercommon "github.com/keptn/lifecycle-toolkit/operator/controllers/common" ) -func (r *KeptnAppVersionReconciler) reconcilePrePostEvaluation(ctx context.Context, appVersion *klcv1alpha1.KeptnAppVersion, checkType common.CheckType) (common.KeptnState, error) { +func (r *KeptnAppVersionReconciler) reconcilePrePostEvaluation(ctx context.Context, phaseCtx context.Context, appVersion *klcv1alpha1.KeptnAppVersion, checkType common.CheckType) (common.KeptnState, error) { evaluationHandler := controllercommon.EvaluationHandler{ - Client: r.Client, - Recorder: r.Recorder, - Log: r.Log, - Tracer: r.Tracer, - Scheme: r.Scheme, + Client: r.Client, + Recorder: r.Recorder, + Log: r.Log, + Tracer: r.Tracer, + Scheme: r.Scheme, + SpanHandler: r.SpanHandler, } evaluationCreateAttributes := controllercommon.EvaluationCreateAttributes{ @@ -23,7 +24,7 @@ func (r *KeptnAppVersionReconciler) reconcilePrePostEvaluation(ctx context.Conte CheckType: checkType, } - newStatus, state, err := evaluationHandler.ReconcileEvaluations(ctx, appVersion, evaluationCreateAttributes) + newStatus, state, err := evaluationHandler.ReconcileEvaluations(ctx, phaseCtx, appVersion, evaluationCreateAttributes) if err != nil { return common.StateUnknown, err } diff --git a/operator/controllers/keptnworkloadinstance/controller.go b/operator/controllers/keptnworkloadinstance/controller.go index c346c47c3a..9e55c09db8 100644 --- a/operator/controllers/keptnworkloadinstance/controller.go +++ b/operator/controllers/keptnworkloadinstance/controller.go @@ -168,8 +168,8 @@ func (r *KeptnWorkloadInstanceReconciler) Reconcile(ctx context.Context, req ctr } if !workloadInstance.IsPreDeploymentSucceeded() { - reconcilePre := func() (common.KeptnState, error) { - return r.reconcilePrePostDeployment(ctx, workloadInstance, common.PreDeploymentCheckType) + reconcilePre := func(phaseCtx context.Context) (common.KeptnState, error) { + return r.reconcilePrePostDeployment(ctx, phaseCtx, workloadInstance, common.PreDeploymentCheckType) } result, err := phaseHandler.HandlePhase(ctx, ctxWorkloadTrace, r.Tracer, workloadInstance, phase, span, reconcilePre) if !result.Continue { @@ -180,8 +180,8 @@ func (r *KeptnWorkloadInstanceReconciler) Reconcile(ctx context.Context, req ctr //Wait for pre-evaluation checks of Workload phase = common.PhaseWorkloadPreEvaluation if !workloadInstance.IsPreDeploymentEvaluationSucceeded() { - reconcilePreEval := func() (common.KeptnState, error) { - return r.reconcilePrePostEvaluation(ctx, workloadInstance, common.PreDeploymentEvaluationCheckType) + reconcilePreEval := func(phaseCtx context.Context) (common.KeptnState, error) { + return r.reconcilePrePostEvaluation(ctx, phaseCtx, workloadInstance, common.PreDeploymentEvaluationCheckType) } result, err := phaseHandler.HandlePhase(ctx, ctxWorkloadTrace, r.Tracer, workloadInstance, phase, span, reconcilePreEval) if !result.Continue { @@ -192,7 +192,7 @@ func (r *KeptnWorkloadInstanceReconciler) Reconcile(ctx context.Context, req ctr //Wait for deployment of Workload phase = common.PhaseWorkloadDeployment if !workloadInstance.IsDeploymentSucceeded() { - reconcileWorkloadInstance := func() (common.KeptnState, error) { + reconcileWorkloadInstance := func(phaseCtx context.Context) (common.KeptnState, error) { return r.reconcileDeployment(ctx, workloadInstance) } result, err := phaseHandler.HandlePhase(ctx, ctxWorkloadTrace, r.Tracer, workloadInstance, phase, span, reconcileWorkloadInstance) @@ -204,8 +204,8 @@ func (r *KeptnWorkloadInstanceReconciler) Reconcile(ctx context.Context, req ctr //Wait for post-deployment checks of Workload phase = common.PhaseWorkloadPostDeployment if !workloadInstance.IsPostDeploymentSucceeded() { - reconcilePostDeployment := func() (common.KeptnState, error) { - return r.reconcilePrePostDeployment(ctx, workloadInstance, common.PostDeploymentCheckType) + reconcilePostDeployment := func(phaseCtx context.Context) (common.KeptnState, error) { + return r.reconcilePrePostDeployment(ctx, phaseCtx, workloadInstance, common.PostDeploymentCheckType) } result, err := phaseHandler.HandlePhase(ctx, ctxWorkloadTrace, r.Tracer, workloadInstance, phase, span, reconcilePostDeployment) if !result.Continue { @@ -216,8 +216,8 @@ func (r *KeptnWorkloadInstanceReconciler) Reconcile(ctx context.Context, req ctr //Wait for post-evaluation checks of Workload phase = common.PhaseWorkloadPostEvaluation if !workloadInstance.IsPostDeploymentEvaluationSucceeded() { - reconcilePostEval := func() (common.KeptnState, error) { - return r.reconcilePrePostEvaluation(ctx, workloadInstance, common.PostDeploymentEvaluationCheckType) + reconcilePostEval := func(phaseCtx context.Context) (common.KeptnState, error) { + return r.reconcilePrePostEvaluation(ctx, phaseCtx, workloadInstance, common.PostDeploymentEvaluationCheckType) } result, err := phaseHandler.HandlePhase(ctx, ctxWorkloadTrace, r.Tracer, workloadInstance, phase, span, reconcilePostEval) if !result.Continue { @@ -248,7 +248,7 @@ func (r *KeptnWorkloadInstanceReconciler) Reconcile(ctx context.Context, req ctr spanWorkloadTrace.SetStatus(codes.Ok, "Finished") spanWorkloadTrace.End() if err := r.SpanHandler.UnbindSpan(workloadInstance, ""); err != nil { - r.Log.Error(err, "could not unbind span for %s", workloadInstance.Name) + r.Log.Error(err, controllercommon.ErrCouldNotUnbindSpan, workloadInstance.Name) } controllercommon.RecordEvent(r.Recorder, phase, "Normal", workloadInstance, "Finished", "is finished", workloadInstance.GetVersion()) diff --git a/operator/controllers/keptnworkloadinstance/reconcile_prepostdeployment.go b/operator/controllers/keptnworkloadinstance/reconcile_prepostdeployment.go index df8235cfbf..76f363039f 100644 --- a/operator/controllers/keptnworkloadinstance/reconcile_prepostdeployment.go +++ b/operator/controllers/keptnworkloadinstance/reconcile_prepostdeployment.go @@ -9,13 +9,14 @@ import ( controllercommon "github.com/keptn/lifecycle-toolkit/operator/controllers/common" ) -func (r *KeptnWorkloadInstanceReconciler) reconcilePrePostDeployment(ctx context.Context, workloadInstance *klcv1alpha1.KeptnWorkloadInstance, checkType common.CheckType) (common.KeptnState, error) { +func (r *KeptnWorkloadInstanceReconciler) reconcilePrePostDeployment(ctx context.Context, phaseCtx context.Context, workloadInstance *klcv1alpha1.KeptnWorkloadInstance, checkType common.CheckType) (common.KeptnState, error) { taskHandler := controllercommon.TaskHandler{ - Client: r.Client, - Recorder: r.Recorder, - Log: r.Log, - Tracer: r.Tracer, - Scheme: r.Scheme, + Client: r.Client, + Recorder: r.Recorder, + Log: r.Log, + Tracer: r.Tracer, + Scheme: r.Scheme, + SpanHandler: r.SpanHandler, } taskCreateAttributes := controllercommon.TaskCreateAttributes{ @@ -23,7 +24,7 @@ func (r *KeptnWorkloadInstanceReconciler) reconcilePrePostDeployment(ctx context CheckType: checkType, } - newStatus, state, err := taskHandler.ReconcileTasks(ctx, workloadInstance, taskCreateAttributes) + newStatus, state, err := taskHandler.ReconcileTasks(ctx, phaseCtx, workloadInstance, taskCreateAttributes) if err != nil { return common.StateUnknown, err } diff --git a/operator/controllers/keptnworkloadinstance/reconcile_prepostevaluation.go b/operator/controllers/keptnworkloadinstance/reconcile_prepostevaluation.go index c9b09cc91c..67f88006ef 100644 --- a/operator/controllers/keptnworkloadinstance/reconcile_prepostevaluation.go +++ b/operator/controllers/keptnworkloadinstance/reconcile_prepostevaluation.go @@ -9,13 +9,14 @@ import ( controllercommon "github.com/keptn/lifecycle-toolkit/operator/controllers/common" ) -func (r *KeptnWorkloadInstanceReconciler) reconcilePrePostEvaluation(ctx context.Context, workloadInstance *klcv1alpha1.KeptnWorkloadInstance, checkType common.CheckType) (common.KeptnState, error) { +func (r *KeptnWorkloadInstanceReconciler) reconcilePrePostEvaluation(ctx context.Context, phaseCtx context.Context, workloadInstance *klcv1alpha1.KeptnWorkloadInstance, checkType common.CheckType) (common.KeptnState, error) { evaluationHandler := controllercommon.EvaluationHandler{ - Client: r.Client, - Recorder: r.Recorder, - Log: r.Log, - Tracer: r.Tracer, - Scheme: r.Scheme, + Client: r.Client, + Recorder: r.Recorder, + Log: r.Log, + Tracer: r.Tracer, + Scheme: r.Scheme, + SpanHandler: r.SpanHandler, } evaluationCreateAttributes := controllercommon.EvaluationCreateAttributes{ @@ -23,7 +24,7 @@ func (r *KeptnWorkloadInstanceReconciler) reconcilePrePostEvaluation(ctx context CheckType: checkType, } - newStatus, state, err := evaluationHandler.ReconcileEvaluations(ctx, workloadInstance, evaluationCreateAttributes) + newStatus, state, err := evaluationHandler.ReconcileEvaluations(ctx, phaseCtx, workloadInstance, evaluationCreateAttributes) if err != nil { return common.StateUnknown, err }