diff --git a/pkg/controller/nginxingress/nginx_ingress_controller.go b/pkg/controller/nginxingress/nginx_ingress_controller.go index 47435d20..56ca7126 100644 --- a/pkg/controller/nginxingress/nginx_ingress_controller.go +++ b/pkg/controller/nginxingress/nginx_ingress_controller.go @@ -121,7 +121,7 @@ func (n *nginxIngressControllerReconciler) Reconcile(ctx context.Context, req ct } defer func() { // defer is before checking err so that we can update status even if there is an error lgr.Info("updating status") - n.updateStatus(ctx, &nginxIngressController, controllerDeployment, ingressClass, managedRes, collisionCountErr) + n.updateStatus(&nginxIngressController, controllerDeployment, ingressClass, managedRes, collisionCountErr) if statusErr := n.client.Status().Update(ctx, &nginxIngressController); statusErr != nil { lgr.Error(statusErr, "unable to update NginxIngressController status") if err == nil { @@ -321,16 +321,42 @@ func (n *nginxIngressControllerReconciler) collides(ctx context.Context, nic *ap } // updateStatus updates the status of the NginxIngressController resource. If a nil controller Deployment or IngressClass is passed, the status is defaulted for those fields if they are not already set. -func (n *nginxIngressControllerReconciler) updateStatus(ctx context.Context, nic *approutingv1alpha1.NginxIngressController, controllerDeployment *appsv1.Deployment, ic *netv1.IngressClass, managedResourceRefs []approutingv1alpha1.ManagedObjectReference, err error) { - lgr := log.FromContext(ctx) +func (n *nginxIngressControllerReconciler) updateStatus(nic *approutingv1alpha1.NginxIngressController, controllerDeployment *appsv1.Deployment, ic *netv1.IngressClass, managedResourceRefs []approutingv1alpha1.ManagedObjectReference, err error) { + n.updateStatusManagedResourceRefs(nic, managedResourceRefs) + + n.updateStatusIngressClass(nic, ic) - if managedResourceRefs != nil { - lgr.Info("updating managed resource refs status") - nic.Status.ManagedResourceRefs = managedResourceRefs + // default conditions + if controllerDeployment == nil || controllerDeployment.CreationTimestamp.IsZero() { + n.updateStatusNilDeployment(nic) + } else { + for _, cond := range controllerDeployment.Status.Conditions { + switch cond.Type { + case appsv1.DeploymentProgressing: + n.updateStatusControllerProgressing(nic, cond) + case appsv1.DeploymentAvailable: + n.updateStatusControllerAvailable(nic, cond) + } + } } + n.updateStatusControllerReplicas(nic, controllerDeployment) + n.updateStatusAvailable(nic) + + // error checking at end to take precedence over other conditions + n.updateStatusFromError(nic, err) +} + +func (n *nginxIngressControllerReconciler) updateStatusManagedResourceRefs(nic *approutingv1alpha1.NginxIngressController, managedResourceRefs []approutingv1alpha1.ManagedObjectReference) { + if managedResourceRefs == nil { + return + } + + nic.Status.ManagedResourceRefs = managedResourceRefs +} + +func (n *nginxIngressControllerReconciler) updateStatusIngressClass(nic *approutingv1alpha1.NginxIngressController, ic *netv1.IngressClass) { if ic == nil || ic.CreationTimestamp.IsZero() { - lgr.Info("adding IngressClassUnknown condition") nic.SetCondition(metav1.Condition{ Type: approutingv1alpha1.ConditionTypeIngressClassReady, Status: metav1.ConditionUnknown, @@ -338,7 +364,6 @@ func (n *nginxIngressControllerReconciler) updateStatus(ctx context.Context, nic Message: "IngressClass is unknown", }) } else { - lgr.Info("adding IngressClassIsReady condition") nic.SetCondition(metav1.Condition{ Type: approutingv1alpha1.ConditionTypeIngressClassReady, Status: "True", @@ -346,44 +371,38 @@ func (n *nginxIngressControllerReconciler) updateStatus(ctx context.Context, nic Message: "Ingress Class is up-to-date ", }) } +} - // default conditions - if controllerDeployment == nil || controllerDeployment.CreationTimestamp.IsZero() { - lgr.Info("adding ControllerDeploymentUnknown to ControllerAvailable condition") - nic.SetCondition(metav1.Condition{ - Type: approutingv1alpha1.ConditionTypeControllerAvailable, - Status: metav1.ConditionUnknown, - Reason: "ControllerDeploymentUnknown", - Message: "Controller deployment is unknown", - }) - lgr.Info("adding ControllerDeploymentUnknown to Progressing condition") - nic.SetCondition(metav1.Condition{ - Type: approutingv1alpha1.ConditionTypeProgressing, - Status: metav1.ConditionUnknown, - Reason: "ControllerDeploymentUnknown", - Message: "Controller deployment is unknown", - }) - } else { - lgr.Info("updating controller replicas status") - nic.Status.ControllerReadyReplicas = controllerDeployment.Status.ReadyReplicas - nic.Status.ControllerAvailableReplicas = controllerDeployment.Status.AvailableReplicas - nic.Status.ControllerUnavailableReplicas = controllerDeployment.Status.UnavailableReplicas - nic.Status.ControllerReplicas = controllerDeployment.Status.Replicas +func (n *nginxIngressControllerReconciler) updateStatusNilDeployment(nic *approutingv1alpha1.NginxIngressController) { + nic.SetCondition(metav1.Condition{ + Type: approutingv1alpha1.ConditionTypeControllerAvailable, + Status: metav1.ConditionUnknown, + Reason: "ControllerDeploymentUnknown", + Message: "Controller deployment is unknown", + }) + nic.SetCondition(metav1.Condition{ + Type: approutingv1alpha1.ConditionTypeProgressing, + Status: metav1.ConditionUnknown, + Reason: "ControllerDeploymentUnknown", + Message: "Controller deployment is unknown", + }) +} - for _, cond := range controllerDeployment.Status.Conditions { - switch cond.Type { - case appsv1.DeploymentProgressing: - n.updateStatusControllerProgressing(nic, cond) - case appsv1.DeploymentAvailable: - n.updateStatusControllerAvailable(nic, cond) - } - } +func (n *nginxIngressControllerReconciler) updateStatusControllerReplicas(nic *approutingv1alpha1.NginxIngressController, deployment *appsv1.Deployment) { + if deployment == nil { + return } + nic.Status.ControllerReadyReplicas = deployment.Status.ReadyReplicas + nic.Status.ControllerAvailableReplicas = deployment.Status.AvailableReplicas + nic.Status.ControllerUnavailableReplicas = deployment.Status.UnavailableReplicas + nic.Status.ControllerReplicas = deployment.Status.Replicas +} + +func (n *nginxIngressControllerReconciler) updateStatusAvailable(nic *approutingv1alpha1.NginxIngressController) { controllerAvailable := nic.GetCondition(approutingv1alpha1.ConditionTypeControllerAvailable) icAvailable := nic.GetCondition(approutingv1alpha1.ConditionTypeIngressClassReady) if controllerAvailable != nil && icAvailable != nil && controllerAvailable.Status == metav1.ConditionTrue && icAvailable.Status == metav1.ConditionTrue { - lgr.Info("adding ControllerIsAvailable condition") nic.SetCondition(metav1.Condition{ Type: approutingv1alpha1.ConditionTypeAvailable, Status: metav1.ConditionTrue, @@ -391,7 +410,6 @@ func (n *nginxIngressControllerReconciler) updateStatus(ctx context.Context, nic Message: "Controller Deployment has minimum availability and IngressClass is up-to-date", }) } else { - lgr.Info("adding ControllerIsNotAvailable condition") nic.SetCondition(metav1.Condition{ Type: approutingv1alpha1.ConditionTypeAvailable, Status: metav1.ConditionFalse, @@ -399,10 +417,10 @@ func (n *nginxIngressControllerReconciler) updateStatus(ctx context.Context, nic Message: "Controller Deployment does not have minimum availability or IngressClass is not up-to-date", }) } +} - // error checking at end to take precedence over other conditions +func (n *nginxIngressControllerReconciler) updateStatusFromError(nic *approutingv1alpha1.NginxIngressController, err error) { if errors.Is(err, icCollisionErr) { - lgr.Info("adding IngressClassCollision condition") nic.SetCondition(metav1.Condition{ Type: approutingv1alpha1.ConditionTypeProgressing, Status: metav1.ConditionFalse, @@ -412,7 +430,6 @@ func (n *nginxIngressControllerReconciler) updateStatus(ctx context.Context, nic n.events.Event(nic, corev1.EventTypeWarning, "IngressClassCollision", "IngressClass already exists and is not owned by this controller. Change the spec.IngressClassName to an unused IngressClass name in a new NginxIngressController.") } if errors.Is(err, maxCollisionsErr) { - lgr.Info("adding TooManyCollisions condition") nic.SetCondition(metav1.Condition{ Type: approutingv1alpha1.ConditionTypeProgressing, Status: metav1.ConditionFalse, diff --git a/pkg/controller/nginxingress/nginx_ingress_controller_test.go b/pkg/controller/nginxingress/nginx_ingress_controller_test.go new file mode 100644 index 00000000..9c42de5c --- /dev/null +++ b/pkg/controller/nginxingress/nginx_ingress_controller_test.go @@ -0,0 +1,455 @@ +package nginxingress + +import ( + "errors" + "fmt" + "testing" + + approutingv1alpha1 "github.com/Azure/aks-app-routing-operator/api/v1alpha1" + "github.com/stretchr/testify/require" + appsv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" + netv1 "k8s.io/api/networking/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/tools/record" +) + +func TestUpdateStatusManagedResourceRefs(t *testing.T) { + t.Run("nil managed resource refs", func(t *testing.T) { + nic := &approutingv1alpha1.NginxIngressController{} + n := &nginxIngressControllerReconciler{} + n.updateStatusManagedResourceRefs(nic, nil) + + require.Nil(t, nic.Status.ManagedResourceRefs) + }) + + t.Run("managed resource refs", func(t *testing.T) { + nic := &approutingv1alpha1.NginxIngressController{} + n := &nginxIngressControllerReconciler{} + refs := []approutingv1alpha1.ManagedObjectReference{ + { + Name: "name", + Namespace: "namespace", + Kind: "kind", + APIGroup: "group", + }, + } + n.updateStatusManagedResourceRefs(nic, refs) + require.Equal(t, refs, nic.Status.ManagedResourceRefs) + + }) +} + +func TestUpdateStatusIngressClass(t *testing.T) { + t.Run("nil ingress class", func(t *testing.T) { + nic := &approutingv1alpha1.NginxIngressController{} + n := &nginxIngressControllerReconciler{} + n.updateStatusIngressClass(nic, nil) + + got := nic.GetCondition(approutingv1alpha1.ConditionTypeIngressClassReady) + require.NotNil(t, got) + require.Equal(t, metav1.ConditionUnknown, got.Status) + }) + + t.Run(("ingress clas with no creation timestamp"), func(t *testing.T) { + nic := &approutingv1alpha1.NginxIngressController{} + n := &nginxIngressControllerReconciler{} + n.updateStatusIngressClass(nic, &netv1.IngressClass{}) + + got := nic.GetCondition(approutingv1alpha1.ConditionTypeIngressClassReady) + require.NotNil(t, got) + require.Equal(t, metav1.ConditionUnknown, got.Status) + }) + + t.Run("ingress class with creation timestamp", func(t *testing.T) { + nic := &approutingv1alpha1.NginxIngressController{} + n := &nginxIngressControllerReconciler{} + n.updateStatusIngressClass(nic, &netv1.IngressClass{ + ObjectMeta: metav1.ObjectMeta{ + CreationTimestamp: metav1.Now(), + }, + Spec: netv1.IngressClassSpec{}, + }) + + got := nic.GetCondition(approutingv1alpha1.ConditionTypeIngressClassReady) + require.NotNil(t, got) + require.Equal(t, metav1.ConditionTrue, got.Status) + + }) +} + +func TestUpdateStatusNilDeployment(t *testing.T) { + nic := &approutingv1alpha1.NginxIngressController{} + n := &nginxIngressControllerReconciler{} + n.updateStatusNilDeployment(nic) + + controllerAvailable := nic.GetCondition(approutingv1alpha1.ConditionTypeControllerAvailable) + require.NotNil(t, controllerAvailable) + require.Equal(t, metav1.ConditionUnknown, controllerAvailable.Status) + + controllerProgressing := nic.GetCondition(approutingv1alpha1.ConditionTypeProgressing) + require.NotNil(t, controllerProgressing) + require.Equal(t, metav1.ConditionUnknown, controllerProgressing.Status) +} + +func TestUpdateStatusControllerReplicas(t *testing.T) { + t.Run("nil deployment", func(t *testing.T) { + nic := &approutingv1alpha1.NginxIngressController{} + n := &nginxIngressControllerReconciler{} + n.updateStatusControllerReplicas(nic, nil) + require.Equal(t, int32(0), nic.Status.ControllerReplicas) + require.Equal(t, int32(0), nic.Status.ControllerReadyReplicas) + require.Equal(t, int32(0), nic.Status.ControllerAvailableReplicas) + require.Equal(t, int32(0), nic.Status.ControllerUnavailableReplicas) + }) + + t.Run("deployment with replicas", func(t *testing.T) { + nic := &approutingv1alpha1.NginxIngressController{} + n := &nginxIngressControllerReconciler{} + deployment := &appsv1.Deployment{ + Status: appsv1.DeploymentStatus{ + Replicas: 1, + ReadyReplicas: 2, + AvailableReplicas: 1, + UnavailableReplicas: 5, + }, + } + n.updateStatusControllerReplicas(nic, deployment) + require.Equal(t, int32(1), nic.Status.ControllerReplicas) + require.Equal(t, int32(2), nic.Status.ControllerReadyReplicas) + require.Equal(t, int32(1), nic.Status.ControllerAvailableReplicas) + require.Equal(t, int32(5), nic.Status.ControllerUnavailableReplicas) + }) +} + +func TestUpdateStatusAvailable(t *testing.T) { + cases := []struct { + name string + controllerAvailable *metav1.Condition + icAvailable *metav1.Condition + expected *metav1.Condition + }{ + { + name: "controller available, ic available", + controllerAvailable: &metav1.Condition{ + Type: approutingv1alpha1.ConditionTypeControllerAvailable, + Status: metav1.ConditionTrue, + }, + icAvailable: &metav1.Condition{ + Type: approutingv1alpha1.ConditionTypeIngressClassReady, + Status: metav1.ConditionTrue, + }, + expected: &metav1.Condition{ + Type: approutingv1alpha1.ConditionTypeAvailable, + Status: metav1.ConditionTrue, + }, + }, + { + name: "controller not available, ic available", + controllerAvailable: &metav1.Condition{ + Type: approutingv1alpha1.ConditionTypeControllerAvailable, + Status: metav1.ConditionFalse, + }, + icAvailable: &metav1.Condition{ + Type: approutingv1alpha1.ConditionTypeIngressClassReady, + Status: metav1.ConditionTrue, + }, + expected: &metav1.Condition{ + Type: approutingv1alpha1.ConditionTypeAvailable, + Status: metav1.ConditionFalse, + }, + }, + { + name: "controller available, ic not available", + controllerAvailable: &metav1.Condition{ + Type: approutingv1alpha1.ConditionTypeControllerAvailable, + Status: metav1.ConditionTrue, + }, + icAvailable: &metav1.Condition{ + Type: approutingv1alpha1.ConditionTypeIngressClassReady, + Status: metav1.ConditionFalse, + }, + expected: &metav1.Condition{ + Type: approutingv1alpha1.ConditionTypeAvailable, + Status: metav1.ConditionFalse, + }, + }, + { + name: "controller not available, ic not available", + controllerAvailable: &metav1.Condition{ + Type: approutingv1alpha1.ConditionTypeControllerAvailable, + Status: metav1.ConditionFalse, + }, + icAvailable: &metav1.Condition{ + Type: approutingv1alpha1.ConditionTypeIngressClassReady, + Status: metav1.ConditionFalse, + }, + expected: &metav1.Condition{ + Type: approutingv1alpha1.ConditionTypeAvailable, + Status: metav1.ConditionFalse, + }, + }, + { + name: "nil controller condition, ic available", + controllerAvailable: nil, + icAvailable: &metav1.Condition{ + Type: approutingv1alpha1.ConditionTypeIngressClassReady, + Status: metav1.ConditionTrue, + }, + expected: &metav1.Condition{ + Type: approutingv1alpha1.ConditionTypeAvailable, + Status: metav1.ConditionFalse, + }, + }, + { + name: "nil ic condition, controller available", + controllerAvailable: &metav1.Condition{ + Type: approutingv1alpha1.ConditionTypeControllerAvailable, + Status: metav1.ConditionTrue, + }, + icAvailable: nil, + expected: &metav1.Condition{ + Type: approutingv1alpha1.ConditionTypeAvailable, + Status: metav1.ConditionFalse, + }, + }, + } + + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + nic := &approutingv1alpha1.NginxIngressController{ + Status: approutingv1alpha1.NginxIngressControllerStatus{ + Conditions: []metav1.Condition{}, + }, + } + + if c.controllerAvailable != nil { + nic.Status.Conditions = append(nic.Status.Conditions, *c.controllerAvailable) + } + if c.icAvailable != nil { + nic.Status.Conditions = append(nic.Status.Conditions, *c.icAvailable) + } + + n := &nginxIngressControllerReconciler{} + n.updateStatusAvailable(nic) + + got := nic.GetCondition(approutingv1alpha1.ConditionTypeAvailable) + if got == nil && c.expected == nil { + return + } + + require.Equal(t, c.expected.Status, got.Status) + }) + } +} + +func TestUpdateStatusFromError(t *testing.T) { + t.Run("nil error", func(t *testing.T) { + recorder := record.NewFakeRecorder(10) + n := &nginxIngressControllerReconciler{ + events: recorder, + } + nic := &approutingv1alpha1.NginxIngressController{} + n.updateStatusFromError(nic, nil) + require.True(t, len(nic.Status.Conditions) == 0) + select { + case <-recorder.Events: + require.Fail(t, "unexpected event") + default: + // no events, we're good + } + }) + + t.Run("non nil, non handled error", func(t *testing.T) { + recorder := record.NewFakeRecorder(10) + n := &nginxIngressControllerReconciler{ + events: recorder, + } + nic := &approutingv1alpha1.NginxIngressController{} + n.updateStatusFromError(nic, errors.New("test error")) + require.True(t, len(nic.Status.Conditions) == 0) + select { + case <-recorder.Events: + require.Fail(t, "unexpected event") + default: + // no events, we're good + } + }) + + t.Run("ingressClass collision error", func(t *testing.T) { + recorder := record.NewFakeRecorder(10) + n := &nginxIngressControllerReconciler{ + events: recorder, + } + nic := &approutingv1alpha1.NginxIngressController{} + nic.Generation = 1 + n.updateStatusFromError(nic, icCollisionErr) + got := nic.GetCondition(approutingv1alpha1.ConditionTypeProgressing) + require.True(t, got.Status == metav1.ConditionFalse) + require.True(t, got.ObservedGeneration == nic.Generation) + + event := <-recorder.Events + require.Equal(t, event, "Warning IngressClassCollision IngressClass already exists and is not owned by this controller. Change the spec.IngressClassName to an unused IngressClass name in a new NginxIngressController.") + }) + + t.Run("max collision error", func(t *testing.T) { + recorder := record.NewFakeRecorder(10) + n := &nginxIngressControllerReconciler{ + events: recorder, + } + nic := &approutingv1alpha1.NginxIngressController{} + nic.Generation = 1 + n.updateStatusFromError(nic, maxCollisionsErr) + got := nic.GetCondition(approutingv1alpha1.ConditionTypeProgressing) + require.True(t, got.Status == metav1.ConditionFalse) + require.True(t, got.ObservedGeneration == nic.Generation) + + event := <-recorder.Events + require.Equal(t, event, "Warning TooManyCollisions Too many collisions with existing resources. Change the spec.ControllerNamePrefix to something more unique in a new NginxIngressController.") + }) +} + +func TestUpdateStatusControllerAvailable(t *testing.T) { + t.Run("non deployment available condition", func(t *testing.T) { + n := &nginxIngressControllerReconciler{} + nic := &approutingv1alpha1.NginxIngressController{} + n.updateStatusControllerAvailable(nic, appsv1.DeploymentCondition{}) + require.True(t, len(nic.Status.Conditions) == 0) + }) + + t.Run("deployment available true condition", func(t *testing.T) { + n := &nginxIngressControllerReconciler{} + nic := &approutingv1alpha1.NginxIngressController{} + nic.Generation = 1 + n.updateStatusControllerAvailable(nic, appsv1.DeploymentCondition{ + Type: appsv1.DeploymentAvailable, + Status: corev1.ConditionTrue, + }) + + got := nic.GetCondition(approutingv1alpha1.ConditionTypeControllerAvailable) + require.True(t, got.Status == metav1.ConditionTrue) + require.True(t, got.ObservedGeneration == nic.Generation) + }) + + t.Run("deployment available false condition", func(t *testing.T) { + n := &nginxIngressControllerReconciler{} + nic := &approutingv1alpha1.NginxIngressController{} + nic.Generation = 2 + n.updateStatusControllerAvailable(nic, appsv1.DeploymentCondition{ + Type: appsv1.DeploymentAvailable, + Status: corev1.ConditionFalse, + }) + + got := nic.GetCondition(approutingv1alpha1.ConditionTypeControllerAvailable) + require.True(t, got.Status == metav1.ConditionFalse) + require.True(t, got.ObservedGeneration == nic.Generation) + }) + + t.Run("deployment available unknown condition", func(t *testing.T) { + n := &nginxIngressControllerReconciler{} + nic := &approutingv1alpha1.NginxIngressController{} + nic.Generation = 3 + n.updateStatusControllerAvailable(nic, appsv1.DeploymentCondition{ + Type: appsv1.DeploymentAvailable, + Status: corev1.ConditionUnknown, + }) + + got := nic.GetCondition(approutingv1alpha1.ConditionTypeControllerAvailable) + require.True(t, got.Status == metav1.ConditionUnknown) + require.True(t, got.ObservedGeneration == nic.Generation) + }) +} + +func TestUpdateStatusControllerProgressing(t *testing.T) { + t.Run("non deployment progressing condition", func(t *testing.T) { + n := &nginxIngressControllerReconciler{} + nic := &approutingv1alpha1.NginxIngressController{} + n.updateStatusControllerProgressing(nic, appsv1.DeploymentCondition{}) + require.True(t, len(nic.Status.Conditions) == 0) + }) + + t.Run("deployment progressing true condition", func(t *testing.T) { + n := &nginxIngressControllerReconciler{} + nic := &approutingv1alpha1.NginxIngressController{} + nic.Generation = 1 + n.updateStatusControllerProgressing(nic, appsv1.DeploymentCondition{ + Type: appsv1.DeploymentProgressing, + Status: corev1.ConditionTrue, + }) + + got := nic.GetCondition(approutingv1alpha1.ConditionTypeProgressing) + require.True(t, got.Status == metav1.ConditionTrue) + require.True(t, got.ObservedGeneration == nic.Generation) + }) + + t.Run("deployment progressing false condition", func(t *testing.T) { + n := &nginxIngressControllerReconciler{} + nic := &approutingv1alpha1.NginxIngressController{} + nic.Generation = 2 + n.updateStatusControllerProgressing(nic, appsv1.DeploymentCondition{ + Type: appsv1.DeploymentProgressing, + Status: corev1.ConditionFalse, + }) + + got := nic.GetCondition(approutingv1alpha1.ConditionTypeProgressing) + require.True(t, got.Status == metav1.ConditionFalse) + require.True(t, got.ObservedGeneration == nic.Generation) + }) + + t.Run("deployment progressing unknown condition", func(t *testing.T) { + n := &nginxIngressControllerReconciler{} + nic := &approutingv1alpha1.NginxIngressController{} + nic.Generation = 3 + n.updateStatusControllerProgressing(nic, appsv1.DeploymentCondition{ + Type: appsv1.DeploymentProgressing, + Status: corev1.ConditionUnknown, + }) + + got := nic.GetCondition(approutingv1alpha1.ConditionTypeProgressing) + require.True(t, got.Status == metav1.ConditionUnknown) + require.True(t, got.ObservedGeneration == nic.Generation) + }) +} + +func TestIsUnreconcilableError(t *testing.T) { + cases := []struct { + name string + err error + want bool + }{ + { + name: "nil error", + err: nil, + want: false, + }, + { + name: "non unreconcilable error", + err: errors.New("some error"), + want: false, + }, + { + name: "unreconcilable error", + err: icCollisionErr, + want: true, + }, + { + name: "another unreconcilable error", + err: maxCollisionsErr, + want: true, + }, + { + name: "wrapped unreconcilable error", + err: fmt.Errorf("wrapped: %w", icCollisionErr), + want: true, + }, + } + + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + got := isUnreconcilableError(c.err) + if got != c.want { + t.Errorf("got %v, want %v", got, c.want) + } + }) + } +} diff --git a/pkg/manifests/common_test.go b/pkg/manifests/common_test.go index 53e58ad5..12a4a911 100644 --- a/pkg/manifests/common_test.go +++ b/pkg/manifests/common_test.go @@ -10,6 +10,9 @@ import ( "github.com/Azure/aks-app-routing-operator/pkg/util" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + appsv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "sigs.k8s.io/controller-runtime/pkg/client" ) @@ -58,6 +61,77 @@ func TestHasTopLevelLabels(t *testing.T) { } } +func TestGetOwnerRefs(t *testing.T) { + cases := []struct { + Name string + Owner client.Object + Controller bool + Outcome []metav1.OwnerReference + }{ + { + Name: "non-controller", + Owner: &corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-namespace", + UID: "test-uid", + }, + TypeMeta: metav1.TypeMeta{ + APIVersion: "v1", + Kind: "Namespace", + }, + }, + Controller: false, + Outcome: []metav1.OwnerReference{ + { + APIVersion: "v1", + Kind: "Namespace", + Name: "test-namespace", + UID: "test-uid", + Controller: util.ToPtr(false), + }, + }, + }, + { + Name: "controller", + Owner: &appsv1.Deployment{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-deployment", + UID: "test-uid2", + }, + TypeMeta: metav1.TypeMeta{ + APIVersion: "v1", + Kind: "Deployment", + }, + }, + Controller: true, + Outcome: []metav1.OwnerReference{ + { + APIVersion: "v1", + Kind: "Deployment", + Name: "test-deployment", + UID: "test-uid2", + Controller: util.ToPtr(true), + }, + }, + }, + } + + for _, c := range cases { + t.Run(c.Name, func(t *testing.T) { + ret := GetOwnerRefs(c.Owner, c.Controller) + + require.Equal(t, len(c.Outcome), len(ret)) + for i, ref := range ret { + require.Equal(t, ref.APIVersion, c.Outcome[i].APIVersion) + require.Equal(t, ref.Kind, c.Outcome[i].Kind) + require.Equal(t, ref.Name, c.Outcome[i].Name) + require.Equal(t, ref.UID, c.Outcome[i].UID) + require.Equal(t, ref.Controller, c.Outcome[i].Controller) + } + }) + } +} + // AssertFixture checks the fixture path and compares it to the provided objects, failing if they are not equal func AssertFixture(t *testing.T, fixturePath string, objs []client.Object) { t.Logf("Testing fixture %s", fixturePath)