From 8b3181271d89cf8328786dc1d85a77357a07e2ea Mon Sep 17 00:00:00 2001
From: Philipp Matthes
Date: Wed, 11 Mar 2026 11:11:10 +0100
Subject: [PATCH] Document initial ideas to route multicluster resources
---
helm/bundles/cortex-nova/values.yaml | 18 +++++++++++
pkg/multicluster/client.go | 46 ++++++++++++++++++++++------
2 files changed, 55 insertions(+), 9 deletions(-)
diff --git a/helm/bundles/cortex-nova/values.yaml b/helm/bundles/cortex-nova/values.yaml
index b2dbba788..138cddcf0 100644
--- a/helm/bundles/cortex-nova/values.yaml
+++ b/helm/bundles/cortex-nova/values.yaml
@@ -117,6 +117,24 @@ cortex-scheduling-controllers:
enabledTasks:
- nova-decisions-cleanup-task
+ # List: iterate over all api servers with the gvk and return a combined list.
+ # Get: iterate over all api server with the gvk and return the first result that is found.
+ # Create/Update/Delete: based on the content of the resource, match api server with labels and execute op only there.
+ # WatchesMulticluster: watch resource in all clusters with the gvk.
+ multicluster:
+ # These resources will be written into the home cluster if no match is found.
+ fallbacks:
+ - gvk: "cortex.cloud/v1alpha1/Decision"
+ - # ...
+ apiservers:
+ - host: "https://api.cc-b0-qa-de-1.ccloud.external.a-qa-de-200.soil-garden.qa-de-1.cloud.sap"
+ gvks: # Used for List, Get, Watches
+ - "cortex.cloud/v1alpha1/Decision"
+ - "cortex.cloud/v1alpha1/DecisionList"
+ # ...
+ labels: # Used for Create/Update/Delete
+ az: "qa-de-1b"
+
cortex-knowledge-controllers:
<<: *cortex
namePrefix: cortex-nova-knowledge
diff --git a/pkg/multicluster/client.go b/pkg/multicluster/client.go
index d3bd88ae6..82e94582c 100644
--- a/pkg/multicluster/client.go
+++ b/pkg/multicluster/client.go
@@ -8,6 +8,7 @@ import (
"errors"
"sync"
+ hv1 "github.com/cobaltcore-dev/openstack-hypervisor-operator/api/v1"
"k8s.io/apimachinery/pkg/api/meta"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
@@ -17,7 +18,40 @@ import (
"sigs.k8s.io/controller-runtime/pkg/cluster"
)
+type ResourceRouter interface {
+ Match(obj any, labels map[string]string) (bool, error)
+}
+
+type HypervisorResourceRouter struct{}
+
+func (h HypervisorResourceRouter) Match(obj any, labels map[string]string) (bool, error) {
+ hv, ok := obj.(hv1.Hypervisor)
+ if !ok {
+ return false, errors.New("object is not a Hypervisor")
+ }
+ // Match by hypervisor availability zone label.
+ az, ok := labels["az"]
+ if !ok {
+ return false, errors.New("object does not have availability zone label")
+ }
+ hvAZ, ok := hv.Labels["topology.kubernetes.io/zone"]
+ if !ok {
+ return false, errors.New("hypervisor does not have availability zone label")
+ }
+ return hvAZ == az, nil
+}
+
+func main() {
+ _ = Client{
+ ResourceRouters: map[schema.GroupVersionKind]ResourceRouter{
+ hv1.GroupVersion.WithKind("Hypervisor"): HypervisorResourceRouter{},
+ },
+ }
+}
+
type Client struct {
+ ResourceRouters map[schema.GroupVersionKind]ResourceRouter
+
// The cluster in which cortex is deployed.
HomeCluster cluster.Cluster
// The REST config for the home cluster in which cortex is deployed.
@@ -134,17 +168,11 @@ func (c *Client) GVKFromHomeScheme(obj runtime.Object) (gvk schema.GroupVersionK
return gvks[0], nil
}
-// Get the cluster for the given group version kind.
-//
-// If this object kind does not have a remote cluster configured,
-// the home cluster is returned.
-func (c *Client) ClusterForResource(gvk schema.GroupVersionKind) cluster.Cluster {
+func (c *Client) ClusterForResource(routable Routable) cluster.Cluster {
c.remoteClustersMu.RLock()
defer c.remoteClustersMu.RUnlock()
- cl, ok := c.remoteClusters[gvk]
- if ok {
- return cl
- }
+ // TODO: Match the approprite cluster.
+
return c.HomeCluster
}