Skip to content

Commit

Permalink
feat(lifecycle-operator): implement promotion task (#3057)
Browse files Browse the repository at this point in the history
Signed-off-by: Florian Bacher <florian.bacher@dynatrace.com>
  • Loading branch information
bacherfl committed Feb 19, 2024
1 parent aa2c72c commit e165600
Show file tree
Hide file tree
Showing 15 changed files with 366 additions and 20 deletions.
1 change: 1 addition & 0 deletions .github/actions/deploy-keptn-on-cluster/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ metricsOperator:
tag: $TAG

lifecycleOperator:
promotionTasksEnabled: true
lifecycleOperator:
imagePullPolicy: Never
image:
Expand Down
1 change: 1 addition & 0 deletions lifecycle-operator/apis/lifecycle/v1beta1/common/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,7 @@ type CheckType string

const PreDeploymentCheckType CheckType = "pre"
const PostDeploymentCheckType CheckType = "post"
const PromotionCheckType CheckType = "promotion"
const PreDeploymentEvaluationCheckType CheckType = "pre-eval"
const PostDeploymentEvaluationCheckType CheckType = "post-eval"

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -337,6 +337,16 @@ func (w KeptnWorkloadVersion) GetPostDeploymentEvaluationTaskStatus() []ItemStat
return w.Status.PostDeploymentEvaluationTaskStatus
}

func (w KeptnWorkloadVersion) GetPromotionTasks() []string {
// promotion tasks are not included in Workloads, but we need the implementation of this method to fulfil the PhaseItem interface
return []string{}
}

func (w KeptnWorkloadVersion) GetPromotionTaskStatus() []ItemStatus {
// promotion tasks are not included in Workloads, but we need the implementation of this method to fulfil the PhaseItem interface
return []ItemStatus{}
}

func (w KeptnWorkloadVersion) GetAppName() string {
return w.Spec.AppName
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,16 @@ func TestKeptnWorkloadVersion(t *testing.T) {
"workloadVersion": "version",
"workloadVersionName": "workload",
}, workload.GetEventAnnotations())

require.Equal(t,
[]string{},
workload.GetPromotionTasks(),
)

require.Equal(t,
[]ItemStatus{},
workload.GetPromotionTaskStatus(),
)
}

//nolint:dupl
Expand Down
3 changes: 3 additions & 0 deletions lifecycle-operator/controllers/common/task/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,9 @@ func (r Handler) setupTasks(taskCreateAttributes CreateTaskAttributes, piWrapper
case apicommon.PostDeploymentCheckType:
tasks = piWrapper.GetPostDeploymentTasks()
statuses = piWrapper.GetPostDeploymentTaskStatus()
case apicommon.PromotionCheckType:
tasks = piWrapper.GetPromotionTasks()
statuses = piWrapper.GetPromotionTaskStatus()
}
return tasks, statuses
}
Expand Down
54 changes: 54 additions & 0 deletions lifecycle-operator/controllers/common/task/handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -376,6 +376,60 @@ func TestTaskHandler(t *testing.T) {
getSpanCalls: 1,
unbindSpanCalls: 1,
},
{
name: "succeeded promotion task",
object: &v1beta1.KeptnAppVersion{
ObjectMeta: v1.ObjectMeta{
Namespace: "namespace",
},
Spec: v1beta1.KeptnAppVersionSpec{
KeptnAppContextSpec: v1beta1.KeptnAppContextSpec{
DeploymentTaskSpec: v1beta1.DeploymentTaskSpec{
PromotionTasks: []string{"task-def"},
},
},
},
Status: v1beta1.KeptnAppVersionStatus{
PromotionStatus: apicommon.StateSucceeded,
PromotionTaskStatus: []v1beta1.ItemStatus{
{
DefinitionName: "task-def",
Status: apicommon.StateProgressing,
Name: "prom-task-def-",
},
},
},
},
taskObj: v1beta1.KeptnTask{
ObjectMeta: v1.ObjectMeta{
Namespace: "namespace",
Name: "prom-task-def-",
},
Status: v1beta1.KeptnTaskStatus{
Status: apicommon.StateSucceeded,
},
},
createAttr: CreateTaskAttributes{
SpanName: "",
Definition: v1beta1.KeptnTaskDefinition{
ObjectMeta: v1.ObjectMeta{
Name: "task-def",
},
},
CheckType: apicommon.PromotionCheckType,
},
wantStatus: []v1beta1.ItemStatus{
{
DefinitionName: "task-def",
Status: apicommon.StateSucceeded,
Name: "prom-task-def-",
},
},
wantSummary: apicommon.StatusSummary{Total: 1, Succeeded: 1},
wantErr: nil,
getSpanCalls: 1,
unbindSpanCalls: 1,
},
}
config.Instance().SetDefaultNamespace(testcommon.KeptnNamespace)

Expand Down

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

10 changes: 10 additions & 0 deletions lifecycle-operator/controllers/lifecycle/interfaces/phaseitem.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,10 @@ type PhaseItem interface {
GetAppName() string
GetPreDeploymentTasks() []string
GetPostDeploymentTasks() []string
GetPromotionTasks() []string
GetPreDeploymentTaskStatus() []klcv1beta1.ItemStatus
GetPostDeploymentTaskStatus() []klcv1beta1.ItemStatus
GetPromotionTaskStatus() []klcv1beta1.ItemStatus
GetPreDeploymentEvaluations() []string
GetPostDeploymentEvaluations() []string
GetPreDeploymentEvaluationTaskStatus() []klcv1beta1.ItemStatus
Expand Down Expand Up @@ -158,3 +160,11 @@ func (pw PhaseItemWrapper) GetSpanAttributes() []attribute.KeyValue {
func (pw PhaseItemWrapper) DeprecateRemainingPhases(phase apicommon.KeptnPhaseType) {
pw.Obj.DeprecateRemainingPhases(phase)
}

func (pw PhaseItemWrapper) GetPromotionTasks() []string {
return pw.Obj.GetPromotionTasks()
}

func (pw PhaseItemWrapper) GetPromotionTaskStatus() []klcv1beta1.ItemStatus {
return pw.Obj.GetPromotionTaskStatus()
}
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,12 @@ func TestPhaseItem(t *testing.T) {
GetPostDeploymentEvaluationTaskStatusFunc: func() []v1beta1.ItemStatus {
return nil
},
GetPromotionTasksFunc: func() []string {
return []string{}
},
GetPromotionTaskStatusFunc: func() []v1beta1.ItemStatus {
return []v1beta1.ItemStatus{}
},
GenerateTaskFunc: func(taskDefinition v1beta1.KeptnTaskDefinition, checkType apicommon.CheckType) v1beta1.KeptnTask {
return v1beta1.KeptnTask{}
},
Expand Down Expand Up @@ -187,4 +193,10 @@ func TestPhaseItem(t *testing.T) {
wrapper.DeprecateRemainingPhases(apicommon.PhaseAppDeployment)
require.Len(t, phaseItemMock.DeprecateRemainingPhasesCalls(), 1)

_ = wrapper.GetPromotionTaskStatus()
require.Len(t, phaseItemMock.GetPromotionTaskStatusCalls(), 1)

_ = wrapper.GetPromotionTasks()
require.Len(t, phaseItemMock.GetPromotionTasksCalls(), 1)

}
Original file line number Diff line number Diff line change
Expand Up @@ -52,13 +52,14 @@ const traceComponentName = "keptn/lifecycle-operator/appversion"
type KeptnAppVersionReconciler struct {
Scheme *runtime.Scheme
client.Client
Log logr.Logger
EventSender eventsender.IEvent
TracerFactory telemetry.TracerFactory
Meters apicommon.KeptnMeters
SpanHandler telemetry.ISpanHandler
EvaluationHandler evaluation.IEvaluationHandler
PhaseHandler phase.IHandler
Log logr.Logger
EventSender eventsender.IEvent
TracerFactory telemetry.TracerFactory
Meters apicommon.KeptnMeters
SpanHandler telemetry.ISpanHandler
EvaluationHandler evaluation.IEvaluationHandler
PhaseHandler phase.IHandler
PromotionTasksEnabled bool
}

// +kubebuilder:rbac:groups=lifecycle.keptn.sh,resources=keptnappversions,verbs=get;list;watch;create;update;patch;delete
Expand All @@ -76,7 +77,7 @@ type KeptnAppVersionReconciler struct {
// For more details, check Reconcile and its Result here:
// - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.13.0/pkg/reconcile
//
//nolint:gocyclo
//nolint:gocyclo,gocognit
func (r *KeptnAppVersionReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
requestInfo := controllercommon.GetRequestInfo(req)
r.Log.Info("Searching for Keptn App Version", "requestInfo", requestInfo)
Expand Down Expand Up @@ -114,7 +115,7 @@ func (r *KeptnAppVersionReconciler) Reconcile(ctx context.Context, req ctrl.Requ

if !appVersion.IsPreDeploymentSucceeded() {
reconcilePreDep := func(phaseCtx context.Context) (apicommon.KeptnState, error) {
return r.reconcilePrePostDeployment(ctx, phaseCtx, appVersion, apicommon.PreDeploymentCheckType)
return r.reconcilePhase(ctx, phaseCtx, appVersion, apicommon.PreDeploymentCheckType)
}
result, err := r.PhaseHandler.HandlePhase(ctx, ctxAppTrace, r.getTracer(), appVersion, currentPhase, reconcilePreDep)
if !result.Continue {
Expand Down Expand Up @@ -147,7 +148,7 @@ func (r *KeptnAppVersionReconciler) Reconcile(ctx context.Context, req ctrl.Requ
currentPhase = apicommon.PhaseAppPostDeployment
if !appVersion.IsPostDeploymentSucceeded() {
reconcilePostDep := func(phaseCtx context.Context) (apicommon.KeptnState, error) {
return r.reconcilePrePostDeployment(ctx, phaseCtx, appVersion, apicommon.PostDeploymentCheckType)
return r.reconcilePhase(ctx, phaseCtx, appVersion, apicommon.PostDeploymentCheckType)
}
result, err := r.PhaseHandler.HandlePhase(ctx, ctxAppTrace, r.getTracer(), appVersion, currentPhase, reconcilePostDep)
if !result.Continue {
Expand All @@ -166,6 +167,17 @@ func (r *KeptnAppVersionReconciler) Reconcile(ctx context.Context, req ctrl.Requ
}
}

if r.PromotionTasksEnabled && !appVersion.IsPromotionCompleted() {
currentPhase = apicommon.PhasePromotion
reconcilePromotionFunc := func(phaseCtx context.Context) (apicommon.KeptnState, error) {
return r.reconcilePhase(ctx, phaseCtx, appVersion, apicommon.PromotionCheckType)
}
result, err := r.PhaseHandler.HandlePhase(ctx, ctxAppTrace, r.getTracer(), appVersion, currentPhase, reconcilePromotionFunc)
if !result.Continue {
return result.Result, err
}
}

// AppVersion is completed at this place
return r.finishKeptnAppVersionReconcile(ctx, appVersion, spanAppTrace)
}
Expand Down
Loading

0 comments on commit e165600

Please sign in to comment.