Skip to content
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
36 changes: 32 additions & 4 deletions pkg/multicluster/routers.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,11 @@ import (
// for the multicluster client that cortex supports by default. This is used to
// route resources to the correct cluster in a multicluster setup.
var DefaultResourceRouters = map[schema.GroupVersionKind]ResourceRouter{
{Group: "kvm.cloud.sap", Version: "v1", Kind: "Hypervisor"}: HypervisorResourceRouter{},
{Group: "cortex.cloud", Version: "v1alpha1", Kind: "Reservation"}: ReservationsResourceRouter{},
{Group: "cortex.cloud", Version: "v1alpha1", Kind: "History"}: HistoryResourceRouter{},
{Group: "cortex.cloud", Version: "v1alpha1", Kind: "CommittedResource"}: CommittedResourceRouter{},
{Group: "kvm.cloud.sap", Version: "v1", Kind: "Hypervisor"}: HypervisorResourceRouter{},
{Group: "cortex.cloud", Version: "v1alpha1", Kind: "Reservation"}: ReservationsResourceRouter{},
{Group: "cortex.cloud", Version: "v1alpha1", Kind: "History"}: HistoryResourceRouter{},
{Group: "cortex.cloud", Version: "v1alpha1", Kind: "CommittedResource"}: CommittedResourceRouter{},
{Group: "cortex.cloud", Version: "v1alpha1", Kind: "FlavorGroupCapacity"}: FlavorGroupCapacityResourceRouter{},
}

// ResourceRouter determines which remote cluster a resource should be written to
Expand Down Expand Up @@ -111,6 +112,33 @@ func (c CommittedResourceRouter) Match(obj any, labels map[string]string) (bool,
return cr.Spec.AvailabilityZone == availabilityZone, nil
}

// FlavorGroupCapacityResourceRouter routes flavor group capacity CRDs to clusters based on availability zone.
type FlavorGroupCapacityResourceRouter struct{}

func (f FlavorGroupCapacityResourceRouter) Match(obj any, labels map[string]string) (bool, error) {
var fgc v1alpha1.FlavorGroupCapacity

switch v := obj.(type) {
case *v1alpha1.FlavorGroupCapacity:
if v == nil {
return false, errors.New("object is nil")
}
fgc = *v
case v1alpha1.FlavorGroupCapacity:
fgc = v
default:
return false, errors.New("object is not a FlavorGroupCapacity")
}
availabilityZone, ok := labels["availabilityZone"]
if !ok {
return false, errors.New("cluster does not have availabilityZone label")
}
if fgc.Spec.AvailabilityZone == "" {
return false, errors.New("flavor group capacity does not have availability zone in spec")
}
return fgc.Spec.AvailabilityZone == availabilityZone, nil
}

// HistoryResourceRouter routes histories to clusters based on availability zone.
type HistoryResourceRouter struct{}

Expand Down
95 changes: 95 additions & 0 deletions pkg/multicluster/routers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,101 @@ func TestHistoryResourceRouter_Match(t *testing.T) {
}
}

func TestFlavorGroupCapacityResourceRouter_Match(t *testing.T) {
router := FlavorGroupCapacityResourceRouter{}

tests := []struct {
name string
obj any
labels map[string]string
wantMatch bool
wantErr bool
}{
{
name: "matching AZ",
obj: v1alpha1.FlavorGroupCapacity{
Spec: v1alpha1.FlavorGroupCapacitySpec{
AvailabilityZone: "qa-de-1b",
},
},
labels: map[string]string{"availabilityZone": "qa-de-1b"},
wantMatch: true,
},
{
name: "matching AZ pointer",
obj: &v1alpha1.FlavorGroupCapacity{
Spec: v1alpha1.FlavorGroupCapacitySpec{
AvailabilityZone: "qa-de-1b",
},
},
labels: map[string]string{"availabilityZone": "qa-de-1b"},
wantMatch: true,
},
{
name: "non-matching AZ",
obj: v1alpha1.FlavorGroupCapacity{
Spec: v1alpha1.FlavorGroupCapacitySpec{
AvailabilityZone: "qa-de-1b",
},
},
labels: map[string]string{"availabilityZone": "qa-de-1a"},
wantMatch: false,
},
{
name: "not a FlavorGroupCapacity",
obj: "not-a-flavor-group-capacity",
labels: map[string]string{"availabilityZone": "qa-de-1b"},
wantErr: true,
},
{
name: "cluster missing availabilityZone label",
obj: v1alpha1.FlavorGroupCapacity{
Spec: v1alpha1.FlavorGroupCapacitySpec{
AvailabilityZone: "qa-de-1b",
},
},
labels: map[string]string{},
wantErr: true,
},
{
name: "FlavorGroupCapacity missing availability zone",
obj: v1alpha1.FlavorGroupCapacity{
Spec: v1alpha1.FlavorGroupCapacitySpec{},
},
labels: map[string]string{"availabilityZone": "qa-de-1b"},
wantErr: true,
},
{
name: "typed nil pointer doesn't panic",
obj: (*v1alpha1.FlavorGroupCapacity)(nil),
labels: map[string]string{"availabilityZone": "qa-de-1b"},
wantErr: true,
},
{
name: "nil object doesn't panic",
obj: nil,
labels: map[string]string{"availabilityZone": "qa-de-1b"},
wantErr: true,
wantMatch: false,
},
}

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)
}
})
}
}

func TestReservationsResourceRouter_Match(t *testing.T) {
router := ReservationsResourceRouter{}

Expand Down
Loading