Skip to content

Commit

Permalink
fix(operator): Do not proceed with WLI if no AppVersion containing it…
Browse files Browse the repository at this point in the history
… is available (keptn#377)

Signed-off-by: Florian Bacher <florian.bacher@dynatrace.com>
  • Loading branch information
bacherfl authored and Thomas Schuetz committed Nov 15, 2022
1 parent f7286c6 commit 538935f
Show file tree
Hide file tree
Showing 6 changed files with 468 additions and 22 deletions.
4 changes: 4 additions & 0 deletions operator/api/v1alpha1/keptnappversion_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -355,3 +355,7 @@ func (a KeptnAppVersion) GetSpanAttributes() []attribute.KeyValue {
common.AppNamespace.String(a.Namespace),
}
}

func (v KeptnAppVersion) GetWorkloadNameOfApp(workloadName string) string {
return fmt.Sprintf("%s-%s", v.Spec.AppName, workloadName)
}
41 changes: 41 additions & 0 deletions operator/api/v1alpha1/keptnappversion_types_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package v1alpha1

import (
"testing"
)

func TestKeptnAppVersion_GetWorkloadNameOfApp(t *testing.T) {
type fields struct {
Spec KeptnAppVersionSpec
}
type args struct {
workloadName string
}
tests := []struct {
name string
fields fields
args args
want string
}{
{
name: "",
fields: fields{
Spec: KeptnAppVersionSpec{AppName: "my-app"},
},
args: args{
workloadName: "my-workload",
},
want: "my-app-my-workload",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
v := KeptnAppVersion{
Spec: tt.fields.Spec,
}
if got := v.GetWorkloadNameOfApp(tt.args.workloadName); got != tt.want {
t.Errorf("GetWorkloadNameOfApp() = %v, want %v", got, tt.want)
}
})
}
}
43 changes: 26 additions & 17 deletions operator/controllers/keptnworkloadinstance/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ package keptnworkloadinstance
import (
"context"
"fmt"
"reflect"
"time"

"go.opentelemetry.io/otel"
Expand Down Expand Up @@ -274,35 +273,45 @@ func (r *KeptnWorkloadInstanceReconciler) getAppVersion(ctx context.Context, app
func (r *KeptnWorkloadInstanceReconciler) getAppVersionForWorkloadInstance(ctx context.Context, wli *klcv1alpha1.KeptnWorkloadInstance) (bool, klcv1alpha1.KeptnAppVersion, error) {
apps := &klcv1alpha1.KeptnAppVersionList{}

// TODO add label selector for looking up by name?
if err := r.Client.List(ctx, apps, client.InNamespace(wli.Namespace)); err != nil {
return false, klcv1alpha1.KeptnAppVersion{}, err
}

workloadFound, latestVersion, err := getLatestAppVersion(apps, wli)
if err != nil {
r.Log.Error(err, "could not look up KeptnAppVersion for WorkloadInstance")
return false, latestVersion, err
}

if latestVersion.Spec.Version == "" || !workloadFound {
return false, klcv1alpha1.KeptnAppVersion{}, nil
}
return true, latestVersion, nil
}

func getLatestAppVersion(apps *klcv1alpha1.KeptnAppVersionList, wli *klcv1alpha1.KeptnWorkloadInstance) (bool, klcv1alpha1.KeptnAppVersion, error) {
latestVersion := klcv1alpha1.KeptnAppVersion{}
// ignore the potential error since this can not return an error with 0.0.0
oldVersion, _ := version.NewVersion("0.0.0")

workloadFound := false
for _, app := range apps.Items {
if app.Spec.AppName == wli.Spec.AppName {

for _, appWorkload := range app.Spec.Workloads {
if !reflect.DeepEqual(latestVersion, app) {
latestVersion = app
} else if appWorkload.Version == wli.Spec.Version && fmt.Sprintf("%s-%s", app.Spec.AppName, appWorkload.Name) == wli.Spec.WorkloadName {
oldVersion, err := version.NewVersion(app.Spec.Version)
if err != nil {
r.Log.Error(err, "could not parse version")
}
newVersion, err := version.NewVersion(latestVersion.Spec.Version)
if appWorkload.Version == wli.Spec.Version && app.GetWorkloadNameOfApp(appWorkload.Name) == wli.Spec.WorkloadName {
workloadFound = true
newVersion, err := version.NewVersion(app.Spec.Version)
if err != nil {
r.Log.Error(err, "could not parse version")
return false, klcv1alpha1.KeptnAppVersion{}, err
}
if oldVersion.LessThan(newVersion) {
if newVersion.GreaterThan(oldVersion) {
latestVersion = app
oldVersion = newVersion
}
}
}
}
}

if latestVersion.Spec.Version == "" {
return false, klcv1alpha1.KeptnAppVersion{}, nil
}
return true, latestVersion, nil
return workloadFound, latestVersion, nil
}
221 changes: 216 additions & 5 deletions operator/controllers/keptnworkloadinstance/controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@ package keptnworkloadinstance

import (
"context"
"testing"

"github.com/keptn/lifecycle-toolkit/operator/api/v1alpha1"
klcv1alpha1 "github.com/keptn/lifecycle-toolkit/operator/api/v1alpha1"
"github.com/stretchr/testify/require"
testrequire "github.com/stretchr/testify/require"
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
"sigs.k8s.io/controller-runtime/pkg/client/fake"
"testing"
)

func TestKeptnWorkloadInstanceReconciler_IsPodRunning(t *testing.T) {
Expand All @@ -20,7 +20,7 @@ func TestKeptnWorkloadInstanceReconciler_IsPodRunning(t *testing.T) {
r := &KeptnWorkloadInstanceReconciler{
Client: fake.NewClientBuilder().WithLists(podList).Build(),
}
isPodRunning, err := r.isPodRunning(context.TODO(), v1alpha1.ResourceReference{UID: types.UID("pod1")}, "node1")
isPodRunning, err := r.isPodRunning(context.TODO(), klcv1alpha1.ResourceReference{UID: types.UID("pod1")}, "node1")
testrequire.Nil(t, err)
if !isPodRunning {
t.Errorf("Wrong!")
Expand All @@ -29,7 +29,7 @@ func TestKeptnWorkloadInstanceReconciler_IsPodRunning(t *testing.T) {
r2 := &KeptnWorkloadInstanceReconciler{
Client: fake.NewClientBuilder().WithLists(podList2).Build(),
}
isPodRunning, err = r2.isPodRunning(context.TODO(), v1alpha1.ResourceReference{UID: types.UID("pod1")}, "node1")
isPodRunning, err = r2.isPodRunning(context.TODO(), klcv1alpha1.ResourceReference{UID: types.UID("pod1")}, "node1")
testrequire.Nil(t, err)
if isPodRunning {
t.Errorf("Wrong!")
Expand All @@ -50,3 +50,214 @@ func makeNominatedPod(podName string, nodeName string, phase v1.PodPhase) v1.Pod
},
}
}

func Test_getLatestAppVersion(t *testing.T) {
type args struct {
apps *klcv1alpha1.KeptnAppVersionList
wli *klcv1alpha1.KeptnWorkloadInstance
}
tests := []struct {
name string
args args
wantFound bool
wantAppVersion klcv1alpha1.KeptnAppVersion
wantErr bool
}{
{
name: "app version found",
args: args{
apps: &klcv1alpha1.KeptnAppVersionList{
Items: []klcv1alpha1.KeptnAppVersion{
{
ObjectMeta: metav1.ObjectMeta{
Name: "my-app",
Namespace: "default",
},
Spec: klcv1alpha1.KeptnAppVersionSpec{
KeptnAppSpec: klcv1alpha1.KeptnAppSpec{
Version: "1.0",
Workloads: []klcv1alpha1.KeptnWorkloadRef{
{
Name: "my-workload",
Version: "1.0",
},
},
},
AppName: "my-app",
},
},
{
ObjectMeta: metav1.ObjectMeta{
Name: "my-app",
Namespace: "default",
},
Spec: klcv1alpha1.KeptnAppVersionSpec{
KeptnAppSpec: klcv1alpha1.KeptnAppSpec{
Version: "2.0",
Workloads: []klcv1alpha1.KeptnWorkloadRef{
{
Name: "my-workload",
Version: "1.0",
},
},
},
AppName: "my-app",
},
},
},
},
wli: &klcv1alpha1.KeptnWorkloadInstance{
ObjectMeta: metav1.ObjectMeta{
Name: "my-workloadinstance",
Namespace: "default",
},
Spec: klcv1alpha1.KeptnWorkloadInstanceSpec{
KeptnWorkloadSpec: klcv1alpha1.KeptnWorkloadSpec{
AppName: "my-app",
Version: "1.0",
},
WorkloadName: "my-app-my-workload",
},
},
},
wantFound: true,
wantAppVersion: klcv1alpha1.KeptnAppVersion{
ObjectMeta: metav1.ObjectMeta{
Name: "my-app",
Namespace: "default",
},
Spec: klcv1alpha1.KeptnAppVersionSpec{
KeptnAppSpec: klcv1alpha1.KeptnAppSpec{
Version: "2.0",
Workloads: []klcv1alpha1.KeptnWorkloadRef{
{
Name: "my-workload",
Version: "1.0",
},
},
},
AppName: "my-app",
},
},
wantErr: false,
},
{
name: "app version not found",
args: args{
apps: &klcv1alpha1.KeptnAppVersionList{
Items: []klcv1alpha1.KeptnAppVersion{
{
ObjectMeta: metav1.ObjectMeta{
Name: "my-app",
Namespace: "default",
},
Spec: klcv1alpha1.KeptnAppVersionSpec{
KeptnAppSpec: klcv1alpha1.KeptnAppSpec{
Version: "1.0",
Workloads: []klcv1alpha1.KeptnWorkloadRef{
{
Name: "my-other-workload",
Version: "1.0",
},
},
},
AppName: "my-app",
},
},
},
},
wli: &klcv1alpha1.KeptnWorkloadInstance{
ObjectMeta: metav1.ObjectMeta{
Name: "my-workloadinstance",
Namespace: "default",
},
Spec: klcv1alpha1.KeptnWorkloadInstanceSpec{
KeptnWorkloadSpec: klcv1alpha1.KeptnWorkloadSpec{
AppName: "my-app",
Version: "1.0",
},
WorkloadName: "my-app-my-workload",
},
},
},
wantFound: false,
wantAppVersion: klcv1alpha1.KeptnAppVersion{},
wantErr: false,
},
{
name: "app version with invalid version",
args: args{
apps: &klcv1alpha1.KeptnAppVersionList{
Items: []klcv1alpha1.KeptnAppVersion{
{
ObjectMeta: metav1.ObjectMeta{
Name: "my-app",
Namespace: "default",
},
Spec: klcv1alpha1.KeptnAppVersionSpec{
KeptnAppSpec: klcv1alpha1.KeptnAppSpec{
Version: "",
Workloads: []klcv1alpha1.KeptnWorkloadRef{
{
Name: "my-workload",
Version: "1.0",
},
},
},
AppName: "my-app",
},
},
},
},
wli: &klcv1alpha1.KeptnWorkloadInstance{
ObjectMeta: metav1.ObjectMeta{
Name: "my-workloadinstance",
Namespace: "default",
},
Spec: klcv1alpha1.KeptnWorkloadInstanceSpec{
KeptnWorkloadSpec: klcv1alpha1.KeptnWorkloadSpec{
AppName: "my-app",
Version: "1.0",
},
WorkloadName: "my-app-my-workload",
},
},
},
wantFound: false,
wantAppVersion: klcv1alpha1.KeptnAppVersion{},
wantErr: true,
},
{
name: "app version list empty",
args: args{
apps: &klcv1alpha1.KeptnAppVersionList{
Items: []klcv1alpha1.KeptnAppVersion{},
},
wli: &klcv1alpha1.KeptnWorkloadInstance{
ObjectMeta: metav1.ObjectMeta{
Name: "my-workloadinstance",
Namespace: "default",
},
Spec: klcv1alpha1.KeptnWorkloadInstanceSpec{
KeptnWorkloadSpec: klcv1alpha1.KeptnWorkloadSpec{
AppName: "my-app",
Version: "1.0",
},
WorkloadName: "my-app-my-workload",
},
},
},
wantFound: false,
wantAppVersion: klcv1alpha1.KeptnAppVersion{},
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
found, gotAppVersion, err := getLatestAppVersion(tt.args.apps, tt.args.wli)
require.Equal(t, tt.wantErr, err != nil)
require.Equal(t, tt.wantFound, found)
require.Equal(t, tt.wantAppVersion, gotAppVersion)
})
}
}
Loading

0 comments on commit 538935f

Please sign in to comment.