Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(operator): Do not proceed with WLI if no AppVersion containing it is available #377

Merged
merged 5 commits into from
Nov 14, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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