Skip to content

Commit

Permalink
feat(lifecycle-operator): add context metadata and traceParent of cur…
Browse files Browse the repository at this point in the history
…rent phase to tasks (#2858)

Signed-off-by: Florian Bacher <[email protected]>
  • Loading branch information
bacherfl authored Jan 26, 2024
1 parent b31ea78 commit 0798406
Show file tree
Hide file tree
Showing 36 changed files with 254 additions and 54 deletions.
6 changes: 6 additions & 0 deletions .github/scripts/.helm-tests/default/result.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3335,6 +3335,12 @@ spec:
description: AppVersion the version of the KeptnApp the KeptnTask
is being executed for.
type: string
metadata:
additionalProperties:
type: string
description: Metadata contains additional key-value pairs for
contextual information.
type: object
objectType:
description: ObjectType indicates whether the KeptnTask is being
executed for a KeptnApp or KeptnWorkload.
Expand Down
6 changes: 6 additions & 0 deletions .github/scripts/.helm-tests/lifecycle-only/result.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3281,6 +3281,12 @@ spec:
description: AppVersion the version of the KeptnApp the KeptnTask
is being executed for.
type: string
metadata:
additionalProperties:
type: string
description: Metadata contains additional key-value pairs for
contextual information.
type: object
objectType:
description: ObjectType indicates whether the KeptnTask is being
executed for a KeptnApp or KeptnWorkload.
Expand Down
6 changes: 6 additions & 0 deletions .github/scripts/.helm-tests/lifecycle-with-certs/result.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3313,6 +3313,12 @@ spec:
description: AppVersion the version of the KeptnApp the KeptnTask
is being executed for.
type: string
metadata:
additionalProperties:
type: string
description: Metadata contains additional key-value pairs for
contextual information.
type: object
objectType:
description: ObjectType indicates whether the KeptnTask is being
executed for a KeptnApp or KeptnWorkload.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1004,6 +1004,7 @@ _Appears in:_
| `workloadVersion` _string_ | WorkloadVersion the version of the KeptnWorkload the KeptnTask is being executed for. |||
| `taskType` _string_ | TaskType indicates whether the KeptnTask is part of the pre- or postDeployment phase. |||
| `objectType` _string_ | ObjectType indicates whether the KeptnTask is being executed for a KeptnApp or KeptnWorkload. |||
| `metadata` _object (keys:string, values:string)_ | Refer to Kubernetes API documentation for fields of `metadata`. |||


#### TaskParameters
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -415,7 +415,7 @@ func (a KeptnAppVersion) SetSpanAttributes(span trace.Span) {
}

func (a KeptnAppVersion) GetSpanKey(phase string) string {
return fmt.Sprintf("%s.%s.%s.%s", a.Spec.TraceId["traceparent"], a.Spec.AppName, a.Spec.Version, phase)
return fmt.Sprintf("%s.%s.%s.%s.%s", a.Spec.TraceId["traceparent"], a.Spec.AppName, a.ObjectMeta.Namespace, a.Spec.Version, phase)
}

func (v KeptnAppVersion) GetWorkloadNameOfApp(workloadName string) string {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ func TestKeptnAppVersion(t *testing.T) {

require.Equal(t, "version", app.GetVersion())

require.Equal(t, "trace1.appname.version.phase", app.GetSpanKey("phase"))
require.Equal(t, "trace1.appname.namespace.version.phase", app.GetSpanKey("phase"))

retries := int32(5)
task := app.GenerateTask(KeptnTaskDefinition{
Expand Down
3 changes: 3 additions & 0 deletions lifecycle-operator/apis/lifecycle/v1beta1/keptntask_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,9 @@ type TaskContext struct {
// ObjectType indicates whether the KeptnTask is being executed for a KeptnApp or KeptnWorkload.
// +optional
ObjectType string `json:"objectType"`
// +optional
// Metadata contains additional key-value pairs for contextual information.
Metadata map[string]string `json:"metadata,omitempty"`
}

type TaskParameters struct {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -424,7 +424,7 @@ func (w KeptnWorkloadVersion) GetSpanAttributes() []attribute.KeyValue {
}

func (w KeptnWorkloadVersion) GetSpanKey(phase string) string {
return fmt.Sprintf("%s.%s.%s.%s", w.Spec.TraceId["traceparent"], w.Spec.WorkloadName, w.Spec.Version, phase)
return fmt.Sprintf("%s.%s.%s.%s.%s", w.Spec.TraceId["traceparent"], w.Spec.WorkloadName, w.ObjectMeta.Namespace, w.Spec.Version, phase)
}

func (w KeptnWorkloadVersion) GetSpanName(phase string) string {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ func TestKeptnWorkloadVersion(t *testing.T) {

require.Equal(t, "version", workload.GetVersion())

require.Equal(t, "trace1.workloadname.version.phase", workload.GetSpanKey("phase"))
require.Equal(t, "trace1.workloadname.namespace.version.phase", workload.GetSpanKey("phase"))

retries := int32(5)
task := workload.GenerateTask(KeptnTaskDefinition{
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions lifecycle-operator/chart/templates/keptntask-crd.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -487,6 +487,12 @@ spec:
description: AppVersion the version of the KeptnApp the KeptnTask
is being executed for.
type: string
metadata:
additionalProperties:
type: string
description: Metadata contains additional key-value pairs for
contextual information.
type: object
objectType:
description: ObjectType indicates whether the KeptnTask is being
executed for a KeptnApp or KeptnWorkload.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -478,6 +478,12 @@ spec:
description: AppVersion the version of the KeptnApp the KeptnTask
is being executed for.
type: string
metadata:
additionalProperties:
type: string
description: Metadata contains additional key-value pairs for
contextual information.
type: object
objectType:
description: ObjectType indicates whether the KeptnTask is being
executed for a KeptnApp or KeptnWorkload.
Expand Down
4 changes: 3 additions & 1 deletion lifecycle-operator/controllers/common/context/context.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package context

import "context"
import (
"context"
)

type keptnAppContextKeyType string

Expand Down
12 changes: 12 additions & 0 deletions lifecycle-operator/controllers/common/helperfunctions_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -361,6 +361,18 @@ func Test_MergeMaps(t *testing.T) {
"test3": "testy3",
},
},
{
name: "one map is nil",
map1: nil,
map2: map[string]string{
"test1": "testy1",
"test3": "testy3",
},
want: map[string]string{
"test1": "testy1",
"test3": "testy3",
},
},
}

for _, tt := range tests {
Expand Down
21 changes: 19 additions & 2 deletions lifecycle-operator/controllers/common/task/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,16 @@ import (
klcv1beta1 "github.com/keptn/lifecycle-toolkit/lifecycle-operator/apis/lifecycle/v1beta1"
apicommon "github.com/keptn/lifecycle-toolkit/lifecycle-operator/apis/lifecycle/v1beta1/common"
"github.com/keptn/lifecycle-toolkit/lifecycle-operator/controllers/common"
keptncontext "github.com/keptn/lifecycle-toolkit/lifecycle-operator/controllers/common/context"
"github.com/keptn/lifecycle-toolkit/lifecycle-operator/controllers/common/eventsender"
"github.com/keptn/lifecycle-toolkit/lifecycle-operator/controllers/common/telemetry"
controllererrors "github.com/keptn/lifecycle-toolkit/lifecycle-operator/controllers/errors"
"github.com/keptn/lifecycle-toolkit/lifecycle-operator/controllers/lifecycle/interfaces"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/codes"
"go.opentelemetry.io/otel/propagation"
"go.opentelemetry.io/otel/trace"
"golang.org/x/exp/maps"
"k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
Expand Down Expand Up @@ -124,7 +128,7 @@ func (r Handler) ReconcileTasks(ctx context.Context, phaseCtx context.Context, r
}

//nolint:dupl
func (r Handler) CreateKeptnTask(ctx context.Context, namespace string, reconcileObject client.Object, taskCreateAttributes CreateTaskAttributes) (string, error) {
func (r Handler) CreateKeptnTask(ctx, phaseCtx context.Context, namespace string, reconcileObject client.Object, taskCreateAttributes CreateTaskAttributes) (string, error) {
piWrapper, err := interfaces.NewPhaseItemWrapperFromClientObject(reconcileObject)
if err != nil {
return "", err
Expand All @@ -133,6 +137,7 @@ func (r Handler) CreateKeptnTask(ctx context.Context, namespace string, reconcil
phase := apicommon.PhaseCreateTask

newTask := piWrapper.GenerateTask(taskCreateAttributes.Definition, taskCreateAttributes.CheckType)
injectKeptnContext(phaseCtx, &newTask)
err = controllerutil.SetControllerReference(reconcileObject, &newTask, r.Scheme)
if err != nil {
r.Log.Error(err, "could not set controller reference:")
Expand All @@ -147,6 +152,18 @@ func (r Handler) CreateKeptnTask(ctx context.Context, namespace string, reconcil
return newTask.Name, nil
}

func injectKeptnContext(phaseCtx context.Context, newTask *klcv1beta1.KeptnTask) {
if metadata, ok := keptncontext.GetAppMetadataFromContext(phaseCtx); ok {
traceContextCarrier := &propagation.MapCarrier{}
otel.GetTextMapPropagator().Inject(phaseCtx, traceContextCarrier)
newTask.Spec.Context.Metadata = map[string]string{}
maps.Copy(newTask.Spec.Context.Metadata, metadata)
for _, key := range traceContextCarrier.Keys() {
newTask.Spec.Context.Metadata[key] = traceContextCarrier.Get(key)
}
}
}

func (r Handler) setTaskFailureEvents(task *klcv1beta1.KeptnTask, spanTrace trace.Span) {
spanTrace.AddEvent(fmt.Sprintf("task '%s' failed with reason: '%s'", task.Name, task.Status.Message), trace.WithTimestamp(time.Now().UTC()))
}
Expand All @@ -172,7 +189,7 @@ func (r Handler) handleTaskNotExists(ctx context.Context, phaseCtx context.Conte
return controllererrors.ErrCannotGetKeptnTaskDefinition
}
taskCreateAttributes.Definition = *definition
taskName, err = r.CreateKeptnTask(ctx, piWrapper.GetNamespace(), reconcileObject, taskCreateAttributes)
taskName, err = r.CreateKeptnTask(ctx, phaseCtx, piWrapper.GetNamespace(), reconcileObject, taskCreateAttributes)
if err != nil {
return err
}
Expand Down
42 changes: 41 additions & 1 deletion lifecycle-operator/controllers/common/task/handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,23 @@ package task

import (
"context"
"fmt"
"strings"
"testing"

"github.com/keptn/lifecycle-toolkit/lifecycle-operator/apis/lifecycle/v1beta1"
apicommon "github.com/keptn/lifecycle-toolkit/lifecycle-operator/apis/lifecycle/v1beta1/common"
"github.com/keptn/lifecycle-toolkit/lifecycle-operator/controllers/common/config"
keptncontext "github.com/keptn/lifecycle-toolkit/lifecycle-operator/controllers/common/context"
"github.com/keptn/lifecycle-toolkit/lifecycle-operator/controllers/common/eventsender"
"github.com/keptn/lifecycle-toolkit/lifecycle-operator/controllers/common/telemetry"
telemetryfake "github.com/keptn/lifecycle-toolkit/lifecycle-operator/controllers/common/telemetry/fake"
"github.com/keptn/lifecycle-toolkit/lifecycle-operator/controllers/common/testcommon"
controllererrors "github.com/keptn/lifecycle-toolkit/lifecycle-operator/controllers/errors"
"github.com/stretchr/testify/require"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/propagation"
sdktrace "go.opentelemetry.io/otel/sdk/trace"
"go.opentelemetry.io/otel/trace"
"go.opentelemetry.io/otel/trace/noop"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
Expand Down Expand Up @@ -463,6 +468,7 @@ func TestTaskHandler_createTask(t *testing.T) {
t.Run(tt.name, func(t *testing.T) {
err := v1beta1.AddToScheme(scheme.Scheme)
require.Nil(t, err)

handler := Handler{
SpanHandler: &telemetryfake.ISpanHandlerMock{},
Log: ctrl.Log.WithName("controller"),
Expand All @@ -471,9 +477,43 @@ func TestTaskHandler_createTask(t *testing.T) {
Tracer: noop.NewTracerProvider().Tracer("tracer"),
Scheme: scheme.Scheme,
}
name, err := handler.CreateKeptnTask(context.TODO(), "namespace", tt.object, tt.createAttr)

// create a context with a traceParent and metadata attributes

name, err := handler.CreateKeptnTask(context.TODO(), context.TODO(), "namespace", tt.object, tt.createAttr)

require.True(t, strings.Contains(name, tt.wantName))
require.Equal(t, tt.wantErr, err)
})
}
}

func Test_injectKeptnContext(t *testing.T) {
otel.SetTextMapPropagator(propagation.NewCompositeTextMapPropagator(propagation.TraceContext{}, propagation.Baggage{}))

tp := sdktrace.NewTracerProvider()

tracer := tp.Tracer("keptn")

ctx := keptncontext.WithAppMetadata(context.TODO(), map[string]string{
"foo": "bar",
})
ctx, span := tracer.Start(ctx, "my-span")
defer span.End()

task := &v1beta1.KeptnTask{}
injectKeptnContext(ctx, task)

require.Equal(
t, map[string]string{
"foo": "bar",
"traceparent": fmt.Sprintf(
"00-%s-%s-01",
span.SpanContext().TraceID().String(),
span.SpanContext().SpanID().String(),
),
},
task.Spec.Context.Metadata,
)

}
Original file line number Diff line number Diff line change
Expand Up @@ -206,9 +206,7 @@ func (r *KeptnAppVersionReconciler) setupSpansContexts(ctx context.Context, appV

appTraceContextCarrier := propagation.MapCarrier(appVersion.Spec.TraceId)
ctxAppTrace := otel.GetTextMapPropagator().Extract(context.TODO(), appTraceContextCarrier)
ctxAppTrace = appcontext.WithAppMetadata(ctxAppTrace, appVersion.Spec.Metadata, map[string]string{
"traceParent": appVersion.Spec.TraceId["traceparent"],
})
ctxAppTrace = appcontext.WithAppMetadata(ctxAppTrace, appVersion.Spec.Metadata)
endFunc := func() {
if appVersion.IsEndTimeSet() {
r.Log.Info("Increasing app count")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -336,6 +336,14 @@ func (r *KeptnWorkloadVersionReconciler) checkPreEvaluationStatusOfApp(ctx conte
return true, nil
}

// set the App context metadata
if !reflect.DeepEqual(appVersion.Spec.Metadata, workloadVersion.Status.AppContextMetadata) {
workloadVersion.Status.AppContextMetadata = appVersion.Spec.Metadata
if err := r.Status().Update(ctx, workloadVersion); err != nil {
return true, err
}
}

// set the App trace id if not already set
if len(workloadVersion.Spec.TraceId) < 1 {
appDeploymentTraceID := appVersion.Status.PhaseTraceIDs[apicommon.PhaseAppDeployment.ShortName]
Expand All @@ -348,14 +356,6 @@ func (r *KeptnWorkloadVersionReconciler) checkPreEvaluationStatusOfApp(ctx conte
return true, err
}
}

// set the App context metadata
if !reflect.DeepEqual(appVersion.Spec.Metadata, workloadVersion.Status.AppContextMetadata) {
workloadVersion.Status.AppContextMetadata = appVersion.Spec.Metadata
if err := r.Status().Update(ctx, workloadVersion); err != nil {
return true, err
}
}
return false, nil
}

Expand Down
1 change: 1 addition & 0 deletions test/integration/imagepullsecret/00-install.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ metadata:
annotations:
container: test
spec:
ttlSecondsAfterFinished: 1000
container:
name: testy-test
image: busybox:1.36.1
Expand Down
Loading

0 comments on commit 0798406

Please sign in to comment.