Skip to content

Commit 6098772

Browse files
authored
fix GetActiveClustersForNamespaceInRequestedZones to return correct set of active clusters for the namespace (#3580)
1 parent 948b754 commit 6098772

File tree

2 files changed

+143
-3
lines changed

2 files changed

+143
-3
lines changed

pkg/csi/service/common/commonco/k8sorchestrator/topology.go

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1727,9 +1727,14 @@ func (c *K8sOrchestrator) GetActiveClustersForNamespaceInRequestedZones(ctx cont
17271727
}
17281728
// check if zone belong to namespace specified with targetNS and belong to
17291729
// volume requirement specified with requestedZones
1730-
if zoneObjUnstructured.GetNamespace() != targetNS && !slices.Contains(requestedZones, zoneObjUnstructured.GetName()) {
1731-
log.Debugf("skipping zone: %q as it does not match requested targetNS: %q and requestedZones: %v requirement",
1732-
zoneObjUnstructured.GetName(), targetNS, requestedZones)
1730+
if zoneObjUnstructured.GetNamespace() != targetNS {
1731+
log.Debugf("skipping zone:%q as it is not assgiend to namespace: %q",
1732+
zoneObjUnstructured.GetName(), targetNS)
1733+
continue
1734+
}
1735+
if !slices.Contains(requestedZones, zoneObjUnstructured.GetName()) {
1736+
log.Debugf("skipping zone:%q as it does not belong to requestedZones: %v",
1737+
zoneObjUnstructured.GetName(), requestedZones)
17331738
continue
17341739
}
17351740
// capture active cluster on the namespace from zone CR instance
@@ -1743,6 +1748,13 @@ func (c *K8sOrchestrator) GetActiveClustersForNamespaceInRequestedZones(ctx cont
17431748
return nil, logger.LogNewErrorf(log, "clusterMoIDs not found in zone instance :%q",
17441749
zoneObj.(*unstructured.Unstructured).GetName())
17451750
}
1751+
log.Infof("zone name=%q ns=%q uid=%q rv=%q active clusters: %v",
1752+
zoneObjUnstructured.GetName(),
1753+
zoneObjUnstructured.GetNamespace(),
1754+
zoneObjUnstructured.GetUID(),
1755+
zoneObjUnstructured.GetResourceVersion(),
1756+
clusters,
1757+
)
17461758
activeClusters = append(activeClusters, clusters...)
17471759
}
17481760
if len(activeClusters) == 0 {

pkg/csi/service/common/commonco/k8sorchestrator/topology_test.go

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,17 @@
11
package k8sorchestrator
22

33
import (
4+
"context"
45
"encoding/json"
56
"testing"
7+
"time"
68

9+
. "github.com/onsi/ginkgo/v2"
10+
. "github.com/onsi/gomega"
711
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
12+
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
13+
"k8s.io/apimachinery/pkg/runtime/schema"
14+
"k8s.io/client-go/tools/cache"
815
csinodetopologyv1alpha1 "sigs.k8s.io/vsphere-csi-driver/v3/pkg/internalapis/csinodetopology/v1alpha1"
916
)
1017

@@ -44,3 +51,124 @@ func TestPatchNodeTopology(t *testing.T) {
4451
t.Errorf("ResourceVersion should be %s, got %s", csiNodeTopology.ResourceVersion, resourceVersion)
4552
}
4653
}
54+
55+
// ---------------------- Fake Informer ----------------------
56+
type fakeInformer struct {
57+
cache.SharedIndexInformer
58+
store cache.Store
59+
}
60+
61+
func (f *fakeInformer) GetStore() cache.Store {
62+
return f.store
63+
}
64+
65+
// ---------------------- Test Helpers ----------------------
66+
func makeZone(name, ns string, deleted bool, clusterMoIDs []string, causeNestedError bool) *unstructured.Unstructured {
67+
zone := &unstructured.Unstructured{}
68+
zone.SetGroupVersionKind(schema.GroupVersionKind{
69+
Group: "cns.vmware.com",
70+
Version: "v1alpha1",
71+
Kind: "Zone",
72+
})
73+
zone.SetName(name)
74+
zone.SetNamespace(ns)
75+
76+
if deleted {
77+
now := metav1.NewTime(time.Now())
78+
zone.SetDeletionTimestamp(&now)
79+
}
80+
81+
if causeNestedError {
82+
zone.Object["spec"] = "not-a-map"
83+
} else if clusterMoIDs != nil {
84+
_ = unstructured.SetNestedStringSlice(zone.Object, clusterMoIDs, "spec", "namespace", "clusterMoIDs")
85+
}
86+
87+
return zone
88+
}
89+
90+
func setupFakeStore(zones ...*unstructured.Unstructured) cache.Store {
91+
store := cache.NewStore(cache.DeletionHandlingMetaNamespaceKeyFunc)
92+
for _, z := range zones {
93+
_ = store.Add(z)
94+
}
95+
return store
96+
}
97+
98+
// ---------------------- Tests ----------------------
99+
var _ = Describe("GetActiveClustersForNamespaceInRequestedZones", func() {
100+
var (
101+
ctx context.Context
102+
orch *K8sOrchestrator
103+
)
104+
105+
BeforeEach(func() {
106+
ctx = context.Background()
107+
orch = &K8sOrchestrator{}
108+
})
109+
110+
It("returns clusters when valid zones exist", func() {
111+
z1 := makeZone("zone-A", "ns1", false, []string{"cluster-1"}, false)
112+
zoneInformer = &fakeInformer{store: setupFakeStore(z1)}
113+
114+
clusters, err := orch.GetActiveClustersForNamespaceInRequestedZones(ctx, "ns1", []string{"zone-A"})
115+
Expect(err).NotTo(HaveOccurred())
116+
Expect(clusters).To(ConsistOf("cluster-1"))
117+
})
118+
119+
It("skips zones with deletion timestamp", func() {
120+
z := makeZone("zone-A", "ns1", true, []string{"cluster-1"}, false)
121+
zoneInformer = &fakeInformer{store: setupFakeStore(z)}
122+
123+
clusters, err := orch.GetActiveClustersForNamespaceInRequestedZones(ctx, "ns1", []string{"zone-A"})
124+
Expect(err).To(HaveOccurred())
125+
Expect(err.Error()).To(ContainSubstring("could not find active cluster"))
126+
Expect(clusters).To(BeNil())
127+
})
128+
129+
It("skips zones not in requestedZones", func() {
130+
z := makeZone("zone-A", "ns1", false, []string{"cluster-1"}, false)
131+
zoneInformer = &fakeInformer{store: setupFakeStore(z)}
132+
133+
clusters, err := orch.GetActiveClustersForNamespaceInRequestedZones(ctx, "ns1", []string{"zone-B"})
134+
Expect(err).To(HaveOccurred())
135+
Expect(err.Error()).To(ContainSubstring("could not find active cluster"))
136+
Expect(clusters).To(BeNil())
137+
})
138+
139+
It("returns error when clusterMoIDs not found", func() {
140+
z := makeZone("zone-A", "ns1", false, nil, false)
141+
zoneInformer = &fakeInformer{store: setupFakeStore(z)}
142+
143+
clusters, err := orch.GetActiveClustersForNamespaceInRequestedZones(ctx, "ns1", []string{"zone-A"})
144+
Expect(err).To(HaveOccurred())
145+
Expect(err.Error()).To(ContainSubstring("clusterMoIDs not found"))
146+
Expect(clusters).To(BeNil())
147+
})
148+
149+
It("returns error when NestedStringSlice returns error", func() {
150+
z := makeZone("zone-A", "ns1", false, nil, true)
151+
zoneInformer = &fakeInformer{store: setupFakeStore(z)}
152+
153+
clusters, err := orch.GetActiveClustersForNamespaceInRequestedZones(ctx, "ns1", []string{"zone-A"})
154+
Expect(err).To(HaveOccurred())
155+
Expect(err.Error()).To(ContainSubstring("failed to get clusterMoIDs"))
156+
Expect(clusters).To(BeNil())
157+
})
158+
159+
It("aggregates clusters from multiple zones", func() {
160+
z1 := makeZone("zone-A", "ns1", false, []string{"cluster-1"}, false)
161+
z2 := makeZone("zone-B", "ns1", false, []string{"cluster-2", "cluster-3"}, false)
162+
zoneInformer = &fakeInformer{store: setupFakeStore(z1, z2)}
163+
164+
clusters, err := orch.GetActiveClustersForNamespaceInRequestedZones(ctx, "ns1", []string{"zone-A", "zone-B"})
165+
Expect(err).NotTo(HaveOccurred())
166+
Expect(clusters).To(ConsistOf("cluster-1", "cluster-2", "cluster-3"))
167+
})
168+
})
169+
170+
// ---------------------- Ginkgo Entry ----------------------
171+
func TestGetActiveClusters_Ginkgo(t *testing.T) {
172+
RegisterFailHandler(Fail)
173+
RunSpecs(t, "K8sOrchestrator GetActiveClusters Suite")
174+
}

0 commit comments

Comments
 (0)