Skip to content

Commit 0928754

Browse files
authored
adjust directref relation update logic (#90)
* adjust directref relation update logic need to split to atomic reference pair to do sort and compare
1 parent 93c6a62 commit 0928754

7 files changed

Lines changed: 154 additions & 69 deletions

File tree

resourcetopo/node_info.go

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -58,8 +58,10 @@ type nodeInfo struct {
5858
// will be updated to true after the object added to cache or the manager is of virtual type.
5959
objectExisted bool
6060

61-
relationsLock sync.RWMutex
62-
relations []ResourceRelation
61+
// always call relationsLock.Lock() before relation change, so when locked, the relation is stable.
62+
// kind like a lock.RLock() in relation change scenario.
63+
relationsLock sync.RWMutex
64+
labelRelations []ResourceRelation
6365
}
6466

6567
func newNode(s *nodeStorage, cluster, namespace, name string) *nodeInfo {
@@ -129,10 +131,10 @@ func (node *nodeInfo) checkLabelUpdateForPostNode(postNode *nodeInfo) {
129131

130132
node.relationsLock.RLock()
131133
defer node.relationsLock.RUnlock()
132-
if len(node.relations) == 0 {
134+
if len(node.labelRelations) == 0 {
133135
return
134136
}
135-
for _, relation := range node.relations {
137+
for _, relation := range node.labelRelations {
136138
if !typeEqual(relation.PostMeta, postNode.storageRef.meta) {
137139
return
138140
}
@@ -181,13 +183,10 @@ func (n *nodeInfo) updateNodeMeta(obj Object) {
181183
func (n *nodeInfo) objectDeleted() {
182184
n.metaLock.Lock()
183185
defer n.metaLock.Unlock()
184-
n.relationsLock.Lock()
185-
defer n.relationsLock.Unlock()
186186

187187
n.objectExisted = false
188188
n.labels = nil
189189
n.ownerNodes = nil
190-
n.relations = nil
191190
}
192191

193192
func (n *nodeInfo) matched(selector labels.Selector) bool {

resourcetopo/node_relation_utils.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,13 @@ func deleteAllRelation(node *nodeInfo) {
9999
// we always lock preOrder before postOrder, expect in this function.
100100
// but to keep transition atomic, and in object deletion scenario,
101101
// this is okay to hold the lock in whole process, and lock preOrderNode as needed.
102+
// node.metaLock.Lock()
103+
// defer node.metaLock.Unlock()
104+
node.relationsLock.Lock()
105+
node.objectDeleted()
106+
node.labelRelations = nil
107+
defer node.relationsLock.Unlock()
108+
102109
node.lock.Lock()
103110
defer node.lock.Unlock()
104111

resourcetopo/node_topology_updater.go

Lines changed: 96 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,11 @@ import (
2424
"k8s.io/klog/v2"
2525
)
2626

27+
type nodeMeta struct {
28+
apiVersion, kind string
29+
cluster, namespace, name string
30+
}
31+
2732
var _ cache.ResourceEventHandler = &nodeStorage{}
2833

2934
func (s *nodeStorage) OnAdd(obj interface{}) {
@@ -63,23 +68,40 @@ func (s *nodeStorage) OnUpdate(oldObj, newObj interface{}) {
6368
return
6469
}
6570

66-
var resolvedRelations []ResourceRelation
71+
var resolvedLabelRelations []ResourceRelation
72+
resolvedDirectRelations := make(map[nodeMeta]interface{})
6773
for _, resolver := range s.resolvers {
6874
relations := resolver.Resolve(newTopoObj)
69-
resolvedRelations = append(resolvedRelations, relations...)
75+
for _, relation := range relations {
76+
if relation.LabelSelector != nil {
77+
resolvedLabelRelations = append(resolvedLabelRelations, relation)
78+
} else {
79+
for _, directRef := range relation.DirectRefs {
80+
resolvedDirectRelations[nodeMeta{
81+
apiVersion: relation.PostMeta.APIVersion,
82+
kind: relation.PostMeta.Kind,
83+
cluster: relation.Cluster,
84+
namespace: directRef.Namespace,
85+
name: directRef.Name,
86+
}] = nil
87+
}
88+
}
89+
}
7090
}
7191

72-
slices.SortFunc(resolvedRelations, compareResourceRelation)
92+
slices.SortFunc(resolvedLabelRelations, compareLabelResourceRelation)
7393
node.relationsLock.Lock()
74-
sortedSlicesCompare(node.relations, resolvedRelations,
94+
sortedSlicesCompare(node.labelRelations, resolvedLabelRelations,
7595
func(relation ResourceRelation) {
76-
s.removeResourceRelation(node, &relation)
96+
s.removeLabelResourceRelation(node, &relation)
7797
},
7898
func(relation ResourceRelation) {
79-
s.addResourceRelation(node, &relation)
99+
s.addLabelResourceRelation(node, &relation)
80100
},
81-
compareResourceRelation)
82-
node.relations = resolvedRelations
101+
compareLabelResourceRelation)
102+
node.labelRelations = resolvedLabelRelations
103+
104+
s.setDirectResourceRelation(node, resolvedDirectRelations)
83105
node.relationsLock.Unlock()
84106

85107
if !node.labelEqualed(newTopoObj.GetLabels()) ||
@@ -132,7 +154,6 @@ func (s *nodeStorage) OnDelete(obj interface{}) {
132154
return
133155
}
134156

135-
node.objectDeleted()
136157
deleteAllRelation(node)
137158
node.checkGC()
138159

@@ -142,18 +163,24 @@ func (s *nodeStorage) OnDelete(obj interface{}) {
142163

143164
func (s *nodeStorage) addNode(obj Object, node *nodeInfo) {
144165
node.updateNodeMeta(obj)
145-
if len(node.relations) != 0 {
146-
klog.Warningf("unexpected relations {%v}", node.relations)
147-
node.relations = nil
166+
if len(node.labelRelations) != 0 {
167+
klog.Warningf("unexpected labelRelations {%v}", node.labelRelations)
168+
node.labelRelations = nil
148169
}
149170
node.relationsLock.Lock()
150171
for _, resolver := range s.resolvers {
151172
relations := resolver.Resolve(obj)
152-
node.relations = append(node.relations, relations...)
173+
for _, relation := range relations {
174+
if relation.LabelSelector != nil {
175+
node.labelRelations = append(node.labelRelations, relation)
176+
} else {
177+
s.addDirectResourceRelation(node, &relation)
178+
}
179+
}
153180
}
154-
slices.SortFunc(node.relations, compareResourceRelation)
155-
for _, relation := range node.relations {
156-
s.addResourceRelation(node, &relation)
181+
slices.SortFunc(node.labelRelations, compareLabelResourceRelation)
182+
for _, relation := range node.labelRelations {
183+
s.addLabelResourceRelation(node, &relation)
157184
}
158185
node.relationsLock.Unlock()
159186

@@ -171,20 +198,14 @@ func (s *nodeStorage) addNode(obj Object, node *nodeInfo) {
171198
}
172199
}
173200

174-
func (s *nodeStorage) addResourceRelation(node *nodeInfo, relation *ResourceRelation) {
201+
func (s *nodeStorage) addLabelResourceRelation(node *nodeInfo, relation *ResourceRelation) {
175202
postMeta := relation.PostMeta
176203
postMetaKey := generateMetaKey(postMeta)
177204
postStorage := s.manager.getStorage(postMeta)
178205
if postStorage == nil {
179206
klog.Errorf("Failed to get node storage by meta %s, ignore this relation", postMetaKey)
180207
return
181208
}
182-
if len(relation.DirectRefs) > 0 {
183-
for _, ref := range relation.DirectRefs {
184-
postNode := postStorage.getOrCreateNode(relation.Cluster, ref.Namespace, ref.Name)
185-
rangeAndSetDirectRefRelation(node, postNode, s.manager)
186-
}
187-
}
188209

189210
if relation.LabelSelector != nil {
190211
var postNodes []*nodeInfo
@@ -199,22 +220,65 @@ func (s *nodeStorage) addResourceRelation(node *nodeInfo, relation *ResourceRela
199220
}
200221
}
201222

202-
func (s *nodeStorage) removeResourceRelation(node *nodeInfo, relation *ResourceRelation) {
203-
postStorage := s.manager.getStorage(relation.PostMeta)
223+
func (s *nodeStorage) setDirectResourceRelation(node *nodeInfo, directRefs map[nodeMeta]interface{}) {
224+
var toDel []*nodeInfo
225+
rangeNodeList(node.directReferredPostOrders, func(postNode *nodeInfo) {
226+
postNodeMeta := nodeMeta{
227+
apiVersion: postNode.storageRef.meta.APIVersion,
228+
kind: postNode.storageRef.meta.Kind,
229+
cluster: postNode.cluster,
230+
namespace: postNode.namespace,
231+
name: postNode.name,
232+
}
233+
if _, ok := directRefs[postNodeMeta]; !ok {
234+
toDel = append(toDel, postNode)
235+
} else {
236+
delete(directRefs, postNodeMeta)
237+
}
238+
})
239+
240+
for _, postNode := range toDel {
241+
if deleteDirectRelation(node, postNode) {
242+
node.noticePostOrderRelationDeleted(postNode)
243+
postNode.checkGC()
244+
}
245+
}
246+
247+
for postNodeMeta := range directRefs {
248+
postStorage := s.manager.getStorage(metav1.TypeMeta{APIVersion: postNodeMeta.apiVersion, Kind: postNodeMeta.kind})
249+
if postStorage == nil {
250+
klog.Errorf("Failed to get node storage by meta %s, ignore this relation", generateKey(postNodeMeta.apiVersion, postNodeMeta.kind))
251+
continue
252+
}
253+
postNode := postStorage.getOrCreateNode(postNodeMeta.cluster, postNodeMeta.namespace, postNodeMeta.name)
254+
rangeAndSetDirectRefRelation(node, postNode, s.manager)
255+
}
256+
}
257+
258+
func (s *nodeStorage) addDirectResourceRelation(node *nodeInfo, relation *ResourceRelation) {
259+
postMeta := relation.PostMeta
260+
postMetaKey := generateMetaKey(postMeta)
261+
postStorage := s.manager.getStorage(postMeta)
204262
if postStorage == nil {
205-
klog.Error("Failed to get node Storage by %s, ignore this delete request",
206-
generateMetaKey(relation.PostMeta))
263+
klog.Errorf("Failed to get node storage by meta %s, ignore this relation", postMetaKey)
207264
return
208265
}
266+
209267
if len(relation.DirectRefs) > 0 {
210268
for _, ref := range relation.DirectRefs {
211-
postNode := postStorage.getNode(relation.Cluster, ref.Namespace, ref.Name)
212-
if deleteDirectRelation(node, postNode) {
213-
node.noticePostOrderRelationDeleted(postNode)
214-
postNode.checkGC()
215-
}
269+
postNode := postStorage.getOrCreateNode(relation.Cluster, ref.Namespace, ref.Name)
270+
rangeAndSetDirectRefRelation(node, postNode, s.manager)
216271
}
217272
}
273+
}
274+
275+
func (s *nodeStorage) removeLabelResourceRelation(node *nodeInfo, relation *ResourceRelation) {
276+
postStorage := s.manager.getStorage(relation.PostMeta)
277+
if postStorage == nil {
278+
klog.Error("Failed to get node Storage by %s, ignore this delete request",
279+
generateMetaKey(relation.PostMeta))
280+
return
281+
}
218282

219283
if relation.LabelSelector != nil {
220284
postNodes := postStorage.getMatchedNodeList(node.cluster, node.namespace, relation.LabelSelector)

resourcetopo/resourcetopo_test.go

Lines changed: 37 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ var _ = Describe("test suite with ists config(label selector and virtual rersour
7272
Expect(podStorage).NotTo(BeNil())
7373
Expect(istsStorage).NotTo(BeNil())
7474

75-
ctx, cancel = context.WithCancel((context.Background()))
75+
ctx, cancel = context.WithCancel(context.Background())
7676
podHandler = &objecthandler{}
7777
Expect(manager.AddNodeHandler(PodMeta, podHandler)).Should(Succeed())
7878
stsHandler = &objecthandler{}
@@ -471,7 +471,7 @@ var _ = Describe("test suite with cluster role config(cluster role and direct re
471471
Expect(clusterroleStorage).NotTo(BeNil())
472472
Expect(saStorage).NotTo(BeNil())
473473

474-
ctx, cancel = context.WithCancel((context.Background()))
474+
ctx, cancel = context.WithCancel(context.Background())
475475
clusterRoleBindingHandler = &objecthandler{}
476476
Expect(manager.AddNodeHandler(ClusterRoleBindingMeta, clusterRoleBindingHandler)).NotTo(HaveOccurred())
477477
clusterRoleHandler = &objecthandler{}
@@ -804,7 +804,7 @@ var _ = Describe("test suite with svc and pod config(label selector and reverse
804804
svcStorage, _ = manager.GetTopoNodeStorage(ServiceMeta)
805805
Expect(svcStorage).NotTo(BeNil())
806806

807-
ctx, cancel = context.WithCancel((context.Background()))
807+
ctx, cancel = context.WithCancel(context.Background())
808808
podHandler = &objecthandler{}
809809
Expect(manager.AddNodeHandler(PodMeta, podHandler)).To(Succeed())
810810
svcHandler = &objecthandler{}
@@ -1143,7 +1143,7 @@ var _ = Describe("test suite with deploy config(label selector and owner referen
11431143
Expect(podStorage).NotTo(BeNil())
11441144
Expect(deployStorage).NotTo(BeNil())
11451145

1146-
ctx, cancel = context.WithCancel((context.Background()))
1146+
ctx, cancel = context.WithCancel(context.Background())
11471147
podHandler = &objecthandler{}
11481148
Expect(manager.AddNodeHandler(PodMeta, podHandler)).Should(Succeed())
11491149
replicasetHandler = &objecthandler{}
@@ -1356,7 +1356,7 @@ var _ = Describe("test suite with deploy config(label selector and owner referen
13561356
})
13571357
})
13581358

1359-
var _ = Describe("test suite with relations update", func() {
1359+
var _ = Describe("test suite with labelRelations update", func() {
13601360
var manager Manager
13611361
var fakeClient *fake.Clientset
13621362
var clusterRoleBindingHandler, clusterRoleHandler, saHandler *objecthandler
@@ -1389,7 +1389,7 @@ var _ = Describe("test suite with relations update", func() {
13891389
Expect(clusterroleStorage).NotTo(BeNil())
13901390
Expect(saStorage).NotTo(BeNil())
13911391

1392-
ctx, cancel = context.WithCancel((context.Background()))
1392+
ctx, cancel = context.WithCancel(context.Background())
13931393
clusterRoleBindingHandler = &objecthandler{}
13941394
Expect(manager.AddNodeHandler(ClusterRoleBindingMeta, clusterRoleBindingHandler)).NotTo(HaveOccurred())
13951395
clusterRoleHandler = &objecthandler{}
@@ -1552,6 +1552,35 @@ var _ = Describe("test suite with relations update", func() {
15521552
Expect(fakeClient.RbacV1().ClusterRoleBindings().Create(ctx, crb, metav1.CreateOptions{})).NotTo(BeNil())
15531553
syncStatus(checkAll)
15541554
})
1555+
It("create and update clusterRoleBinding with objects not existed", func() {
1556+
ns := "testclusterresource"
1557+
crbName := "crbtest"
1558+
crName := "crName"
1559+
saName := "saName"
1560+
saName2 := "saName2"
1561+
1562+
clusterRoleBindingHandler.addCallExpected()
1563+
crb := newClusterRoleBinding(crbName, crName,
1564+
[]types.NamespacedName{{Name: saName, Namespace: ns}})
1565+
Expect(fakeClient.RbacV1().ClusterRoleBindings().Create(ctx, crb, metav1.CreateOptions{})).NotTo(BeNil())
1566+
syncStatus(checkAll)
1567+
1568+
clusterRoleBindingHandler.updateCallExpected()
1569+
crb = newClusterRoleBinding(crbName, crName,
1570+
[]types.NamespacedName{{Name: saName, Namespace: ns}, {Name: saName2, Namespace: ns}})
1571+
Expect(fakeClient.RbacV1().ClusterRoleBindings().Update(ctx, crb, metav1.UpdateOptions{})).NotTo(BeNil())
1572+
syncStatus(checkAll)
1573+
1574+
clusterRoleBindingHandler.updateCallExpected()
1575+
crb = newClusterRoleBinding(crbName, crName, []types.NamespacedName{{Name: saName2, Namespace: ns}})
1576+
Expect(fakeClient.RbacV1().ClusterRoleBindings().Update(ctx, crb, metav1.UpdateOptions{})).NotTo(BeNil())
1577+
syncStatus(checkAll)
1578+
1579+
clusterRoleBindingHandler.updateCallExpected()
1580+
crb = newClusterRoleBinding(crbName, crName, []types.NamespacedName{})
1581+
Expect(fakeClient.RbacV1().ClusterRoleBindings().Update(ctx, crb, metav1.UpdateOptions{})).NotTo(BeNil())
1582+
syncStatus(checkAll)
1583+
})
15551584
})
15561585

15571586
var _ = Describe("test suite with mock relation for fed namespaces and local cluster pods", func() {
@@ -1579,7 +1608,7 @@ var _ = Describe("test suite with mock relation for fed namespaces and local clu
15791608
nsStorage, _ = manager.GetTopoNodeStorage(NamespaceMeta)
15801609
Expect(nsStorage).NotTo(BeNil())
15811610

1582-
ctx, cancel = context.WithCancel((context.Background()))
1611+
ctx, cancel = context.WithCancel(context.Background())
15831612
nsHandler = &objecthandler{}
15841613
Expect(manager.AddNodeHandler(NamespaceMeta, nsHandler)).NotTo(HaveOccurred())
15851614

@@ -1780,7 +1809,7 @@ var _ = Describe("test suite for multi routine", func() {
17801809
Expect(deployStorage).NotTo(BeNil())
17811810
Expect(inspectDeployStorage).NotTo(BeNil())
17821811

1783-
ctx, cancel = context.WithCancel((context.Background()))
1812+
ctx, cancel = context.WithCancel(context.Background())
17841813
podHandler = &objecthandler{needRangePostOrder: true, needRangePreOrder: true}
17851814
Expect(manager.AddNodeHandler(PodMeta, podHandler)).Should(Succeed())
17861815
replicasetHandler = &objecthandler{needRangePostOrder: true, needRangePreOrder: true}

resourcetopo/types.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,7 @@ type ResourceRelation struct {
159159
DirectRefs []types.NamespacedName
160160

161161
// LabelSelector offers a kubernetes label-selector way for svc-pod type relation, could be nil if it's empty.
162+
// If Set, will ignore DirectRefs.
162163
LabelSelector *metav1.LabelSelector
163164
}
164165

0 commit comments

Comments
 (0)