From 360a491358c25f41e61b0e3520d6cb14250d4908 Mon Sep 17 00:00:00 2001 From: Markus Wieland Date: Thu, 19 Mar 2026 10:02:33 +0100 Subject: [PATCH 1/7] Add ReservationsResourceRouter for routing reservations based on availability zone --- cmd/main.go | 4 +++- pkg/multicluster/routers.go | 25 +++++++++++++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/cmd/main.go b/cmd/main.go index f8188f93a..389c980f7 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -275,12 +275,14 @@ func main() { os.Exit(1) } hvGVK := schema.GroupVersionKind{Group: "kvm.cloud.sap", Version: "v1", Kind: "Hypervisor"} + reservationGVK := schema.GroupVersionKind{Group: "cortex.cloud", Version: "v1alpha1", Kind: "Reservation"} multiclusterClient := &multicluster.Client{ HomeCluster: homeCluster, HomeRestConfig: restConfig, HomeScheme: scheme, ResourceRouters: map[schema.GroupVersionKind]multicluster.ResourceRouter{ - hvGVK: multicluster.HypervisorResourceRouter{}, + hvGVK: multicluster.HypervisorResourceRouter{}, + reservationGVK: multicluster.ReservationsResourceRouter{}, }, } multiclusterClientConfig := conf.GetConfigOrDie[multicluster.ClientConfig]() diff --git a/pkg/multicluster/routers.go b/pkg/multicluster/routers.go index 1e0496791..a5322088a 100644 --- a/pkg/multicluster/routers.go +++ b/pkg/multicluster/routers.go @@ -6,6 +6,7 @@ package multicluster import ( "errors" + "github.com/cobaltcore-dev/cortex/api/v1alpha1" hv1 "github.com/cobaltcore-dev/openstack-hypervisor-operator/api/v1" corev1 "k8s.io/api/core/v1" ) @@ -40,4 +41,28 @@ func (h HypervisorResourceRouter) Match(obj any, labels map[string]string) (bool return hvAZ == az, nil } +// ReservationsResourceRouter routes reservations to clusters based on availability zone. +type ReservationsResourceRouter struct{} + +func (r ReservationsResourceRouter) Match(obj any, labels map[string]string) (bool, error) { + var res v1alpha1.Reservation + switch v := obj.(type) { + case *v1alpha1.Reservation: + res = *v + case v1alpha1.Reservation: + res = v + default: + return false, errors.New("object is not a Reservation") + } + az, ok := labels["az"] + if !ok { + return false, errors.New("cluster does not have availability zone label") + } + resAZ := res.Spec.AvailabilityZone + if resAZ == "" { + return false, errors.New("reservation does not have availability zone label") + } + return resAZ == az, nil +} + // TODO: Add router for Decision CRD and reservations after their refactoring is done. From aec01f643d868e482fdebc43fea5f0bd50c321ce Mon Sep 17 00:00:00 2001 From: Markus Wieland Date: Thu, 19 Mar 2026 10:08:38 +0100 Subject: [PATCH 2/7] Add unit tests for ReservationsResourceRouter matching logic --- pkg/multicluster/routers_test.go | 85 ++++++++++++++++++++++++++++++++ 1 file changed, 85 insertions(+) diff --git a/pkg/multicluster/routers_test.go b/pkg/multicluster/routers_test.go index d9598b767..d3b732827 100644 --- a/pkg/multicluster/routers_test.go +++ b/pkg/multicluster/routers_test.go @@ -6,6 +6,7 @@ package multicluster import ( "testing" + "github.com/cobaltcore-dev/cortex/api/v1alpha1" hv1 "github.com/cobaltcore-dev/openstack-hypervisor-operator/api/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) @@ -93,3 +94,87 @@ func TestHypervisorResourceRouter_Match(t *testing.T) { }) } } + +func TestReservationsResourceRouter_Match(t *testing.T) { + router := ReservationsResourceRouter{} + + tests := []struct { + name string + obj any + labels map[string]string + wantMatch bool + wantErr bool + }{ + { + name: "matching AZ", + obj: v1alpha1.Reservation{ + Spec: v1alpha1.ReservationSpec{ + AvailabilityZone: "qa-de-1a", + }, + }, + labels: map[string]string{"az": "qa-de-1a"}, + wantMatch: true, + }, + { + name: "matching AZ pointer", + obj: &v1alpha1.Reservation{ + Spec: v1alpha1.ReservationSpec{ + AvailabilityZone: "qa-de-1a", + }, + }, + labels: map[string]string{"az": "qa-de-1a"}, + wantMatch: true, + }, + { + name: "non-matching AZ", + obj: v1alpha1.Reservation{ + Spec: v1alpha1.ReservationSpec{ + AvailabilityZone: "qa-de-1a", + }, + }, + labels: map[string]string{"az": "qa-de-1b"}, + wantMatch: false, + }, + { + name: "not a Reservation", + obj: "not-a-reservation", + labels: map[string]string{"az": "qa-de-1a"}, + wantErr: true, + }, + { + name: "cluster missing az label", + obj: v1alpha1.Reservation{ + Spec: v1alpha1.ReservationSpec{ + AvailabilityZone: "qa-de-1a", + }, + }, + labels: map[string]string{}, + wantErr: true, + }, + { + name: "reservation missing availability zone", + obj: v1alpha1.Reservation{ + Spec: v1alpha1.ReservationSpec{ + AvailabilityZone: "", + }, + }, + labels: map[string]string{"az": "qa-de-1a"}, + wantErr: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + match, err := router.Match(tt.obj, tt.labels) + if tt.wantErr && err == nil { + t.Fatal("expected error, got nil") + } + if !tt.wantErr && err != nil { + t.Fatalf("unexpected error: %v", err) + } + if match != tt.wantMatch { + t.Errorf("expected match=%v, got %v", tt.wantMatch, match) + } + }) + } +} From cb994fe0688c1179f3ce2f341d336964f16d811d Mon Sep 17 00:00:00 2001 From: Markus Wieland Date: Thu, 19 Mar 2026 11:07:32 +0100 Subject: [PATCH 3/7] Refactor availability zone label handling in ResourceRouter and update tests --- pkg/multicluster/routers.go | 18 ++++++++-------- pkg/multicluster/routers_test.go | 36 +++++++++++++++++++++----------- 2 files changed, 33 insertions(+), 21 deletions(-) diff --git a/pkg/multicluster/routers.go b/pkg/multicluster/routers.go index a5322088a..97990157b 100644 --- a/pkg/multicluster/routers.go +++ b/pkg/multicluster/routers.go @@ -30,15 +30,15 @@ func (h HypervisorResourceRouter) Match(obj any, labels map[string]string) (bool default: return false, errors.New("object is not a Hypervisor") } - az, ok := labels["az"] + availabilityZone, ok := labels["availability_zone"] if !ok { return false, errors.New("cluster does not have availability zone label") } - hvAZ, ok := hv.Labels[corev1.LabelTopologyZone] + hvAvailabilityZone, ok := hv.Labels[corev1.LabelTopologyZone] if !ok { return false, errors.New("hypervisor does not have availability zone label") } - return hvAZ == az, nil + return hvAvailabilityZone == availabilityZone, nil } // ReservationsResourceRouter routes reservations to clusters based on availability zone. @@ -54,15 +54,15 @@ func (r ReservationsResourceRouter) Match(obj any, labels map[string]string) (bo default: return false, errors.New("object is not a Reservation") } - az, ok := labels["az"] + availabilityZone, ok := labels["availability_zone"] if !ok { - return false, errors.New("cluster does not have availability zone label") + return false, errors.New("cluster does not have availability zone in spec") } - resAZ := res.Spec.AvailabilityZone - if resAZ == "" { - return false, errors.New("reservation does not have availability zone label") + reservationAvailabilityZone := res.Spec.AvailabilityZone + if reservationAvailabilityZone == "" { + return false, errors.New("reservation does not have availability zone in spec") } - return resAZ == az, nil + return reservationAvailabilityZone == availabilityZone, nil } // TODO: Add router for Decision CRD and reservations after their refactoring is done. diff --git a/pkg/multicluster/routers_test.go b/pkg/multicluster/routers_test.go index d3b732827..4de43e8d6 100644 --- a/pkg/multicluster/routers_test.go +++ b/pkg/multicluster/routers_test.go @@ -28,7 +28,7 @@ func TestHypervisorResourceRouter_Match(t *testing.T) { Labels: map[string]string{"topology.kubernetes.io/zone": "qa-de-1a"}, }, }, - labels: map[string]string{"az": "qa-de-1a"}, + labels: map[string]string{"availability_zone": "qa-de-1a"}, wantMatch: true, }, { @@ -38,7 +38,7 @@ func TestHypervisorResourceRouter_Match(t *testing.T) { Labels: map[string]string{"topology.kubernetes.io/zone": "qa-de-1a"}, }, }, - labels: map[string]string{"az": "qa-de-1a"}, + labels: map[string]string{"availability_zone": "qa-de-1a"}, wantMatch: true, }, { @@ -48,17 +48,17 @@ func TestHypervisorResourceRouter_Match(t *testing.T) { Labels: map[string]string{"topology.kubernetes.io/zone": "qa-de-1a"}, }, }, - labels: map[string]string{"az": "qa-de-1b"}, + labels: map[string]string{"availability_zone": "qa-de-1b"}, wantMatch: false, }, { name: "not a Hypervisor", obj: "not-a-hypervisor", - labels: map[string]string{"az": "qa-de-1a"}, + labels: map[string]string{"availability_zone": "qa-de-1a"}, wantErr: true, }, { - name: "cluster missing az label", + name: "cluster missing availability_zone label", obj: hv1.Hypervisor{ ObjectMeta: metav1.ObjectMeta{ Labels: map[string]string{"topology.kubernetes.io/zone": "qa-de-1a"}, @@ -74,7 +74,13 @@ func TestHypervisorResourceRouter_Match(t *testing.T) { Labels: map[string]string{}, }, }, - labels: map[string]string{"az": "qa-de-1a"}, + labels: map[string]string{"availability_zone": "qa-de-1a"}, + wantErr: true, + }, + { + name: "nil pointer doesn't dereference", + obj: nil, + labels: map[string]string{"availability_zone": "qa-de-1a"}, wantErr: true, }, } @@ -112,7 +118,7 @@ func TestReservationsResourceRouter_Match(t *testing.T) { AvailabilityZone: "qa-de-1a", }, }, - labels: map[string]string{"az": "qa-de-1a"}, + labels: map[string]string{"availability_zone": "qa-de-1a"}, wantMatch: true, }, { @@ -122,7 +128,7 @@ func TestReservationsResourceRouter_Match(t *testing.T) { AvailabilityZone: "qa-de-1a", }, }, - labels: map[string]string{"az": "qa-de-1a"}, + labels: map[string]string{"availability_zone": "qa-de-1a"}, wantMatch: true, }, { @@ -132,17 +138,17 @@ func TestReservationsResourceRouter_Match(t *testing.T) { AvailabilityZone: "qa-de-1a", }, }, - labels: map[string]string{"az": "qa-de-1b"}, + labels: map[string]string{"availability_zone": "qa-de-1b"}, wantMatch: false, }, { name: "not a Reservation", obj: "not-a-reservation", - labels: map[string]string{"az": "qa-de-1a"}, + labels: map[string]string{"availability_zone": "qa-de-1a"}, wantErr: true, }, { - name: "cluster missing az label", + name: "cluster missing availability_zone label", obj: v1alpha1.Reservation{ Spec: v1alpha1.ReservationSpec{ AvailabilityZone: "qa-de-1a", @@ -158,7 +164,13 @@ func TestReservationsResourceRouter_Match(t *testing.T) { AvailabilityZone: "", }, }, - labels: map[string]string{"az": "qa-de-1a"}, + labels: map[string]string{"availability_zone": "qa-de-1a"}, + wantErr: true, + }, + { + name: "nil pointer doesn't dereference", + obj: nil, + labels: map[string]string{"availability_zone": "qa-de-1a"}, wantErr: true, }, } From e3b10bd8ea2577d478f8424362b33d74dd403e47 Mon Sep 17 00:00:00 2001 From: Markus Wieland Date: Thu, 19 Mar 2026 11:30:37 +0100 Subject: [PATCH 4/7] Add nil object checks to ResourceRouter Match methods and update tests --- pkg/multicluster/routers.go | 10 ++++++++++ pkg/multicluster/routers_test.go | 31 +++++++++++++++++++++++-------- 2 files changed, 33 insertions(+), 8 deletions(-) diff --git a/pkg/multicluster/routers.go b/pkg/multicluster/routers.go index 97990157b..699d617bd 100644 --- a/pkg/multicluster/routers.go +++ b/pkg/multicluster/routers.go @@ -22,6 +22,11 @@ type HypervisorResourceRouter struct{} func (h HypervisorResourceRouter) Match(obj any, labels map[string]string) (bool, error) { var hv hv1.Hypervisor + + if obj == nil { + return false, errors.New("object is nil") + } + switch v := obj.(type) { case *hv1.Hypervisor: hv = *v @@ -46,6 +51,11 @@ type ReservationsResourceRouter struct{} func (r ReservationsResourceRouter) Match(obj any, labels map[string]string) (bool, error) { var res v1alpha1.Reservation + + if obj == nil { + return false, errors.New("object is nil") + } + switch v := obj.(type) { case *v1alpha1.Reservation: res = *v diff --git a/pkg/multicluster/routers_test.go b/pkg/multicluster/routers_test.go index 4de43e8d6..220556269 100644 --- a/pkg/multicluster/routers_test.go +++ b/pkg/multicluster/routers_test.go @@ -7,6 +7,7 @@ import ( "testing" "github.com/cobaltcore-dev/cortex/api/v1alpha1" + testlib "github.com/cobaltcore-dev/cortex/pkg/testing" hv1 "github.com/cobaltcore-dev/openstack-hypervisor-operator/api/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) @@ -33,11 +34,11 @@ func TestHypervisorResourceRouter_Match(t *testing.T) { }, { name: "matching AZ pointer", - obj: &hv1.Hypervisor{ + obj: testlib.Ptr(hv1.Hypervisor{ ObjectMeta: metav1.ObjectMeta{ Labels: map[string]string{"topology.kubernetes.io/zone": "qa-de-1a"}, }, - }, + }), labels: map[string]string{"availability_zone": "qa-de-1a"}, wantMatch: true, }, @@ -78,11 +79,18 @@ func TestHypervisorResourceRouter_Match(t *testing.T) { wantErr: true, }, { - name: "nil pointer doesn't dereference", - obj: nil, + name: "typed nil pointer doesn't panic", + obj: (*hv1.Hypervisor)(nil), labels: map[string]string{"availability_zone": "qa-de-1a"}, wantErr: true, }, + { + name: "nil object doesn't panic", + obj: nil, + labels: map[string]string{"availability_zone": "qa-de-1a"}, + wantErr: true, + wantMatch: false, + }, } for _, tt := range tests { @@ -123,11 +131,11 @@ func TestReservationsResourceRouter_Match(t *testing.T) { }, { name: "matching AZ pointer", - obj: &v1alpha1.Reservation{ + obj: testlib.Ptr(v1alpha1.Reservation{ Spec: v1alpha1.ReservationSpec{ AvailabilityZone: "qa-de-1a", }, - }, + }), labels: map[string]string{"availability_zone": "qa-de-1a"}, wantMatch: true, }, @@ -168,11 +176,18 @@ func TestReservationsResourceRouter_Match(t *testing.T) { wantErr: true, }, { - name: "nil pointer doesn't dereference", - obj: nil, + name: "typed nil pointer doesn't panic", + obj: (*v1alpha1.Reservation)(nil), labels: map[string]string{"availability_zone": "qa-de-1a"}, wantErr: true, }, + { + name: "nil object doesn't panic", + obj: nil, + labels: map[string]string{"availability_zone": "qa-de-1a"}, + wantErr: true, + wantMatch: false, + }, } for _, tt := range tests { From fa0d7b96d49f5cab6573bb11416cef23b4a629e1 Mon Sep 17 00:00:00 2001 From: Markus Wieland Date: Thu, 19 Mar 2026 11:36:45 +0100 Subject: [PATCH 5/7] Add nil object checks in Match methods of HypervisorResourceRouter and ReservationsResourceRouter --- pkg/multicluster/routers.go | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/pkg/multicluster/routers.go b/pkg/multicluster/routers.go index 699d617bd..334cb35e8 100644 --- a/pkg/multicluster/routers.go +++ b/pkg/multicluster/routers.go @@ -23,12 +23,11 @@ type HypervisorResourceRouter struct{} func (h HypervisorResourceRouter) Match(obj any, labels map[string]string) (bool, error) { var hv hv1.Hypervisor - if obj == nil { - return false, errors.New("object is nil") - } - switch v := obj.(type) { case *hv1.Hypervisor: + if v == nil { + return false, errors.New("object is nil") + } hv = *v case hv1.Hypervisor: hv = v @@ -52,12 +51,11 @@ type ReservationsResourceRouter struct{} func (r ReservationsResourceRouter) Match(obj any, labels map[string]string) (bool, error) { var res v1alpha1.Reservation - if obj == nil { - return false, errors.New("object is nil") - } - switch v := obj.(type) { case *v1alpha1.Reservation: + if v == nil { + return false, errors.New("object is nil") + } res = *v case v1alpha1.Reservation: res = v From 5e27f34660bdd1e61b3cc35e8e80e980753037d5 Mon Sep 17 00:00:00 2001 From: Markus Wieland Date: Thu, 19 Mar 2026 11:42:05 +0100 Subject: [PATCH 6/7] Update error message for missing availability_zone label in ReservationsResourceRouter --- pkg/multicluster/routers.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/multicluster/routers.go b/pkg/multicluster/routers.go index 334cb35e8..04533ea0b 100644 --- a/pkg/multicluster/routers.go +++ b/pkg/multicluster/routers.go @@ -64,7 +64,7 @@ func (r ReservationsResourceRouter) Match(obj any, labels map[string]string) (bo } availabilityZone, ok := labels["availability_zone"] if !ok { - return false, errors.New("cluster does not have availability zone in spec") + return false, errors.New("cluster does not have availability_zone label") } reservationAvailabilityZone := res.Spec.AvailabilityZone if reservationAvailabilityZone == "" { From b19f119aaba806d327d442acd6e78ec116fd828d Mon Sep 17 00:00:00 2001 From: Markus Wieland Date: Thu, 19 Mar 2026 11:53:06 +0100 Subject: [PATCH 7/7] Update availability zone label to use camelCase in ResourceRouter and tests --- pkg/multicluster/routers.go | 8 ++++---- pkg/multicluster/routers_test.go | 32 ++++++++++++++++---------------- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/pkg/multicluster/routers.go b/pkg/multicluster/routers.go index 04533ea0b..c5b9036ad 100644 --- a/pkg/multicluster/routers.go +++ b/pkg/multicluster/routers.go @@ -34,9 +34,9 @@ func (h HypervisorResourceRouter) Match(obj any, labels map[string]string) (bool default: return false, errors.New("object is not a Hypervisor") } - availabilityZone, ok := labels["availability_zone"] + availabilityZone, ok := labels["availabilityZone"] if !ok { - return false, errors.New("cluster does not have availability zone label") + return false, errors.New("cluster does not have availabilityZone label") } hvAvailabilityZone, ok := hv.Labels[corev1.LabelTopologyZone] if !ok { @@ -62,9 +62,9 @@ func (r ReservationsResourceRouter) Match(obj any, labels map[string]string) (bo default: return false, errors.New("object is not a Reservation") } - availabilityZone, ok := labels["availability_zone"] + availabilityZone, ok := labels["availabilityZone"] if !ok { - return false, errors.New("cluster does not have availability_zone label") + return false, errors.New("cluster does not have availability zone label") } reservationAvailabilityZone := res.Spec.AvailabilityZone if reservationAvailabilityZone == "" { diff --git a/pkg/multicluster/routers_test.go b/pkg/multicluster/routers_test.go index 220556269..06b7a5d5c 100644 --- a/pkg/multicluster/routers_test.go +++ b/pkg/multicluster/routers_test.go @@ -29,7 +29,7 @@ func TestHypervisorResourceRouter_Match(t *testing.T) { Labels: map[string]string{"topology.kubernetes.io/zone": "qa-de-1a"}, }, }, - labels: map[string]string{"availability_zone": "qa-de-1a"}, + labels: map[string]string{"availabilityZone": "qa-de-1a"}, wantMatch: true, }, { @@ -39,7 +39,7 @@ func TestHypervisorResourceRouter_Match(t *testing.T) { Labels: map[string]string{"topology.kubernetes.io/zone": "qa-de-1a"}, }, }), - labels: map[string]string{"availability_zone": "qa-de-1a"}, + labels: map[string]string{"availabilityZone": "qa-de-1a"}, wantMatch: true, }, { @@ -49,17 +49,17 @@ func TestHypervisorResourceRouter_Match(t *testing.T) { Labels: map[string]string{"topology.kubernetes.io/zone": "qa-de-1a"}, }, }, - labels: map[string]string{"availability_zone": "qa-de-1b"}, + labels: map[string]string{"availabilityZone": "qa-de-1b"}, wantMatch: false, }, { name: "not a Hypervisor", obj: "not-a-hypervisor", - labels: map[string]string{"availability_zone": "qa-de-1a"}, + labels: map[string]string{"availabilityZone": "qa-de-1a"}, wantErr: true, }, { - name: "cluster missing availability_zone label", + name: "cluster missing availabilityZone label", obj: hv1.Hypervisor{ ObjectMeta: metav1.ObjectMeta{ Labels: map[string]string{"topology.kubernetes.io/zone": "qa-de-1a"}, @@ -75,19 +75,19 @@ func TestHypervisorResourceRouter_Match(t *testing.T) { Labels: map[string]string{}, }, }, - labels: map[string]string{"availability_zone": "qa-de-1a"}, + labels: map[string]string{"availabilityZone": "qa-de-1a"}, wantErr: true, }, { name: "typed nil pointer doesn't panic", obj: (*hv1.Hypervisor)(nil), - labels: map[string]string{"availability_zone": "qa-de-1a"}, + labels: map[string]string{"availabilityZone": "qa-de-1a"}, wantErr: true, }, { name: "nil object doesn't panic", obj: nil, - labels: map[string]string{"availability_zone": "qa-de-1a"}, + labels: map[string]string{"availabilityZone": "qa-de-1a"}, wantErr: true, wantMatch: false, }, @@ -126,7 +126,7 @@ func TestReservationsResourceRouter_Match(t *testing.T) { AvailabilityZone: "qa-de-1a", }, }, - labels: map[string]string{"availability_zone": "qa-de-1a"}, + labels: map[string]string{"availabilityZone": "qa-de-1a"}, wantMatch: true, }, { @@ -136,7 +136,7 @@ func TestReservationsResourceRouter_Match(t *testing.T) { AvailabilityZone: "qa-de-1a", }, }), - labels: map[string]string{"availability_zone": "qa-de-1a"}, + labels: map[string]string{"availabilityZone": "qa-de-1a"}, wantMatch: true, }, { @@ -146,17 +146,17 @@ func TestReservationsResourceRouter_Match(t *testing.T) { AvailabilityZone: "qa-de-1a", }, }, - labels: map[string]string{"availability_zone": "qa-de-1b"}, + labels: map[string]string{"availabilityZone": "qa-de-1b"}, wantMatch: false, }, { name: "not a Reservation", obj: "not-a-reservation", - labels: map[string]string{"availability_zone": "qa-de-1a"}, + labels: map[string]string{"availabilityZone": "qa-de-1a"}, wantErr: true, }, { - name: "cluster missing availability_zone label", + name: "cluster missing availabilityZone label", obj: v1alpha1.Reservation{ Spec: v1alpha1.ReservationSpec{ AvailabilityZone: "qa-de-1a", @@ -172,19 +172,19 @@ func TestReservationsResourceRouter_Match(t *testing.T) { AvailabilityZone: "", }, }, - labels: map[string]string{"availability_zone": "qa-de-1a"}, + labels: map[string]string{"availabilityZone": "qa-de-1a"}, wantErr: true, }, { name: "typed nil pointer doesn't panic", obj: (*v1alpha1.Reservation)(nil), - labels: map[string]string{"availability_zone": "qa-de-1a"}, + labels: map[string]string{"availabilityZone": "qa-de-1a"}, wantErr: true, }, { name: "nil object doesn't panic", obj: nil, - labels: map[string]string{"availability_zone": "qa-de-1a"}, + labels: map[string]string{"availabilityZone": "qa-de-1a"}, wantErr: true, wantMatch: false, },