-
Notifications
You must be signed in to change notification settings - Fork 119
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
chore(operator): Support Progressing state in every phase + refactori…
…ng + speed improvements (#236) Signed-off-by: odubajDT <ondrej.dubaj@dynatrace.com>
- Loading branch information
Showing
20 changed files
with
529 additions
and
397 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
package common | ||
|
||
import ( | ||
klcv1alpha1 "github.com/keptn/lifecycle-controller/operator/api/v1alpha1" | ||
apicommon "github.com/keptn/lifecycle-controller/operator/api/v1alpha1/common" | ||
"k8s.io/apimachinery/pkg/types" | ||
) | ||
|
||
func GetTaskStatus(taskName string, instanceStatus []klcv1alpha1.TaskStatus) klcv1alpha1.TaskStatus { | ||
for _, status := range instanceStatus { | ||
if status.TaskDefinitionName == taskName { | ||
return status | ||
} | ||
} | ||
return klcv1alpha1.TaskStatus{ | ||
TaskDefinitionName: taskName, | ||
Status: apicommon.StatePending, | ||
TaskName: "", | ||
} | ||
} | ||
|
||
func GetEvaluationStatus(evaluationName string, instanceStatus []klcv1alpha1.EvaluationStatus) klcv1alpha1.EvaluationStatus { | ||
for _, status := range instanceStatus { | ||
if status.EvaluationDefinitionName == evaluationName { | ||
return status | ||
} | ||
} | ||
return klcv1alpha1.EvaluationStatus{ | ||
EvaluationDefinitionName: evaluationName, | ||
Status: apicommon.StatePending, | ||
EvaluationName: "", | ||
} | ||
} | ||
|
||
func GetAppVersionName(namespace string, appName string, version string) types.NamespacedName { | ||
return types.NamespacedName{Namespace: namespace, Name: appName + "-" + version} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
package common | ||
|
||
import ( | ||
"errors" | ||
|
||
"github.com/keptn/lifecycle-controller/operator/api/v1alpha1/common" | ||
"go.opentelemetry.io/otel/attribute" | ||
"sigs.k8s.io/controller-runtime/pkg/client" | ||
) | ||
|
||
//go:generate moq -pkg common_mock --skip-ensure -out ./fake/phaseitem_mock.go . PhaseItem | ||
type PhaseItem interface { | ||
GetState() common.KeptnState | ||
SetState(common.KeptnState) | ||
GetCurrentPhase() string | ||
SetCurrentPhase(string) | ||
GetVersion() string | ||
GetMetricsAttributes() []attribute.KeyValue | ||
GetSpanName(phase string) string | ||
Complete() | ||
} | ||
|
||
type PhaseItemWrapper struct { | ||
Obj PhaseItem | ||
} | ||
|
||
func NewPhaseItemWrapperFromClientObject(object client.Object) (*PhaseItemWrapper, error) { | ||
pi, ok := object.(PhaseItem) | ||
if !ok { | ||
return nil, errors.New("provided object does not implement PhaseItem interface") | ||
} | ||
return &PhaseItemWrapper{Obj: pi}, nil | ||
} | ||
|
||
func (pw PhaseItemWrapper) GetState() common.KeptnState { | ||
return pw.Obj.GetState() | ||
} | ||
|
||
func (pw *PhaseItemWrapper) SetState(state common.KeptnState) { | ||
pw.Obj.SetState(state) | ||
} | ||
|
||
func (pw PhaseItemWrapper) GetCurrentPhase() string { | ||
return pw.Obj.GetCurrentPhase() | ||
} | ||
|
||
func (pw *PhaseItemWrapper) SetCurrentPhase(phase string) { | ||
pw.Obj.SetCurrentPhase(phase) | ||
} | ||
|
||
func (pw PhaseItemWrapper) GetMetricsAttributes() []attribute.KeyValue { | ||
return pw.Obj.GetMetricsAttributes() | ||
} | ||
|
||
func (pw *PhaseItemWrapper) Complete() { | ||
pw.Obj.Complete() | ||
} | ||
|
||
func (pw PhaseItemWrapper) GetVersion() string { | ||
return pw.Obj.GetVersion() | ||
} | ||
|
||
func (pw PhaseItemWrapper) GetSpanName(phase string) string { | ||
return pw.Obj.GetSpanName(phase) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
package common | ||
|
||
import ( | ||
"github.com/keptn/lifecycle-controller/operator/api/v1alpha1" | ||
"github.com/keptn/lifecycle-controller/operator/api/v1alpha1/common" | ||
"github.com/stretchr/testify/require" | ||
"testing" | ||
) | ||
|
||
func TestPhaseItemWrapper_GetState(t *testing.T) { | ||
appVersion := &v1alpha1.KeptnAppVersion{ | ||
Status: v1alpha1.KeptnAppVersionStatus{ | ||
Status: common.StateFailed, | ||
CurrentPhase: "test", | ||
}, | ||
} | ||
|
||
object, err := NewPhaseItemWrapperFromClientObject(appVersion) | ||
require.Nil(t, err) | ||
|
||
require.Equal(t, "test", object.GetCurrentPhase()) | ||
|
||
object.Complete() | ||
|
||
require.NotZero(t, appVersion.Status.EndTime) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
package common | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"time" | ||
|
||
"github.com/go-logr/logr" | ||
"github.com/keptn/lifecycle-controller/operator/api/v1alpha1/common" | ||
"go.opentelemetry.io/otel/codes" | ||
"go.opentelemetry.io/otel/trace" | ||
"k8s.io/client-go/tools/record" | ||
ctrl "sigs.k8s.io/controller-runtime" | ||
"sigs.k8s.io/controller-runtime/pkg/client" | ||
) | ||
|
||
type PhaseHandler struct { | ||
client.Client | ||
Recorder record.EventRecorder | ||
Log logr.Logger | ||
SpanHandler SpanHandler | ||
} | ||
|
||
type PhaseResult struct { | ||
Continue bool | ||
ctrl.Result | ||
} | ||
|
||
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, ctxAppTrace context.Context, tracer trace.Tracer, reconcileObject client.Object, phase common.KeptnPhaseType, span trace.Span, reconcilePhase func() (common.KeptnState, error)) (*PhaseResult, error) { | ||
requeueResult := ctrl.Result{Requeue: true, RequeueAfter: 5 * time.Second} | ||
piWrapper, err := NewPhaseItemWrapperFromClientObject(reconcileObject) | ||
if err != nil { | ||
return &PhaseResult{Continue: false, Result: ctrl.Result{Requeue: true}}, err | ||
} | ||
oldStatus := piWrapper.GetState() | ||
oldPhase := piWrapper.GetCurrentPhase() | ||
piWrapper.SetCurrentPhase(phase.ShortName) | ||
|
||
r.Log.Info(phase.LongName + " not finished") | ||
ctxAppTrace, spanAppTrace, err := r.SpanHandler.GetSpan(ctxAppTrace, tracer, reconcileObject, phase.ShortName) | ||
if err != nil { | ||
r.Log.Error(err, "could not get span") | ||
} | ||
|
||
state, err := reconcilePhase() | ||
if err != nil { | ||
spanAppTrace.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 | ||
} | ||
|
||
if state.IsPending() { | ||
state = common.StateProgressing | ||
} | ||
|
||
defer func(oldStatus common.KeptnState, oldPhase string, reconcileObject client.Object) { | ||
piWrapper, _ := NewPhaseItemWrapperFromClientObject(reconcileObject) | ||
if oldStatus != piWrapper.GetState() || oldPhase != piWrapper.GetCurrentPhase() { | ||
ctx, spanAppTrace, err = r.SpanHandler.GetSpan(ctxAppTrace, 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) | ||
|
||
if state.IsCompleted() { | ||
if state.IsFailed() { | ||
piWrapper.Complete() | ||
piWrapper.SetState(common.StateFailed) | ||
spanAppTrace.AddEvent(phase.LongName + " has failed") | ||
spanAppTrace.SetStatus(codes.Error, "Failed") | ||
spanAppTrace.End() | ||
if err := r.SpanHandler.UnbindSpan(reconcileObject, phase.ShortName); err != nil { | ||
r.Log.Error(err, "cannot unbind span") | ||
} | ||
RecordEvent(r.Recorder, phase, "Warning", reconcileObject, "Failed", "has failed", piWrapper.GetVersion()) | ||
return &PhaseResult{Continue: false, Result: ctrl.Result{}}, nil | ||
} | ||
|
||
piWrapper.SetState(common.StateSucceeded) | ||
spanAppTrace.AddEvent(phase.LongName + " has succeeded") | ||
spanAppTrace.SetStatus(codes.Ok, "Succeeded") | ||
spanAppTrace.End() | ||
if err := r.SpanHandler.UnbindSpan(reconcileObject, phase.ShortName); err != nil { | ||
r.Log.Error(err, "cannot unbind span") | ||
} | ||
RecordEvent(r.Recorder, phase, "Normal", reconcileObject, "Succeeded", "has succeeded", piWrapper.GetVersion()) | ||
|
||
return &PhaseResult{Continue: true, Result: requeueResult}, nil | ||
} | ||
|
||
piWrapper.SetState(common.StateProgressing) | ||
RecordEvent(r.Recorder, phase, "Warning", reconcileObject, "NotFinished", "has not finished", piWrapper.GetVersion()) | ||
|
||
return &PhaseResult{Continue: false, Result: requeueResult}, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
package common | ||
|
||
import ( | ||
"context" | ||
|
||
"go.opentelemetry.io/otel/trace" | ||
"sigs.k8s.io/controller-runtime/pkg/client" | ||
) | ||
|
||
type SpanHandler struct { | ||
bindCRDSpan map[string]trace.Span | ||
} | ||
|
||
func (r SpanHandler) GetSpan(ctx context.Context, tracer trace.Tracer, reconcileObject client.Object, phase string) (context.Context, trace.Span, error) { | ||
piWrapper, err := NewPhaseItemWrapperFromClientObject(reconcileObject) | ||
if err != nil { | ||
return nil, nil, err | ||
} | ||
appvName := piWrapper.GetSpanName(phase) | ||
if r.bindCRDSpan == nil { | ||
r.bindCRDSpan = make(map[string]trace.Span) | ||
} | ||
if span, ok := r.bindCRDSpan[appvName]; ok { | ||
return ctx, span, nil | ||
} | ||
ctx, span := tracer.Start(ctx, phase, trace.WithSpanKind(trace.SpanKindConsumer)) | ||
r.bindCRDSpan[appvName] = span | ||
return ctx, span, nil | ||
} | ||
|
||
func (r SpanHandler) UnbindSpan(reconcileObject client.Object, phase string) error { | ||
piWrapper, err := NewPhaseItemWrapperFromClientObject(reconcileObject) | ||
if err != nil { | ||
return err | ||
} | ||
delete(r.bindCRDSpan, piWrapper.GetSpanName(phase)) | ||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.