@@ -13,6 +13,7 @@ type DNSTreeNode struct {
13
13
Name string
14
14
Children []* DNSTreeNode
15
15
DataSets []DNSTreeNodeData
16
+ Parent * DNSTreeNode
16
17
}
17
18
18
19
// DNSTreeNodeData holds a data for the enpoint(s) that correspond to this node
@@ -25,6 +26,173 @@ type DNSTreeNodeData struct {
25
26
Targets []string
26
27
}
27
28
29
+ // PropagateStoppableLabel takes a propLabel (and value) to propagate throughout a tree, and a stopLabel
30
+ // whenever the label is propagated to a dataset in a node which also has the stopLabel, this node and
31
+ // all of the children of this node and any parents will have the propLabel removed.
32
+ //
33
+ // N.B. Any node with no parents is assumed to have the stopLabel, even when not present, to prevent the
34
+ // propLabel propagating to the entire tree (if this is required, use `AddLabelToBranch` on the root node).
35
+ //
36
+ // The overview of the logic of this function is as follows:
37
+ // - Spread propLabels as greedily as possible
38
+ // - Any labelled node labels all of it's children
39
+ // - Any node with all children labelled get's the propLabel too
40
+ //
41
+ // - Resolve the stopLabels
42
+ // - Any node with the stopLabel and the propLabel:
43
+ // - Has the propLabel removed from itself and all it's children
44
+ // - Has the propLabel removed from any parent (or parent's parent) that has the label
45
+
46
+ func PropagateStoppableLabel (node * DNSTreeNode , propLabel , value , stopLabel string ) {
47
+ //propagate labels regardless of stop labels
48
+ propagateLabel (node , propLabel , value )
49
+
50
+ //propagate stop labels
51
+ resolveStops (node , propLabel , value , stopLabel )
52
+ }
53
+
54
+ func resolveStops (node * DNSTreeNode , label , value , stopLabel string ) {
55
+ if isRoot (node ) && allChildrenHaveLabel (node , label , value ) {
56
+ RemoveLabelFromTree (node , label )
57
+ //entire tree cleaned so no need for any further checks
58
+ return
59
+ }
60
+
61
+ //remove label from stop labelled children
62
+ for _ , c := range node .Children {
63
+ d := findDataSetForChild (node , c .Name )
64
+ //has label and stop label = remove label from this dataset and all children
65
+ if d != nil && d .Labels [stopLabel ] != "" && d .Labels [label ] != "" {
66
+ delete (d .Labels , label )
67
+ RemoveLabelFromTree (c , label )
68
+ RemoveLabelFromParents (node , label )
69
+ } else {
70
+ resolveStops (c , label , value , stopLabel )
71
+ }
72
+
73
+ }
74
+
75
+ }
76
+
77
+ func propagateLabel (node * DNSTreeNode , label , value string ) {
78
+ for _ , c := range node .Children {
79
+ d := findDataSetForChild (node , c .Name )
80
+ if d != nil && d .Labels [label ] != "" {
81
+ //this child is labelled, indiscriminately label entire tree under this child
82
+ AddLabelToTree (c , label , value )
83
+ } else {
84
+ // this child is not labelled, continue descending to propagate label
85
+ propagateLabel (c , label , value )
86
+ }
87
+ }
88
+
89
+ // if all children are labelled, label this branch in parent node
90
+ if len (node .Children ) > 0 && allChildrenHaveLabel (node , label , value ) && node .Parent != nil {
91
+ AddLabelToBranch (node .Parent , node .Name , label , value )
92
+ }
93
+ }
94
+
95
+ func isRoot (node * DNSTreeNode ) bool {
96
+ return node .Parent == nil
97
+ }
98
+
99
+ func allChildrenHaveLabel (node * DNSTreeNode , label , value string ) bool {
100
+ for _ , c := range node .Children {
101
+ if ! HasLabelForBranch (node , c .Name , label , value ) {
102
+ return false
103
+ }
104
+ }
105
+ return true
106
+ }
107
+
108
+ func findDataSetForChild (node * DNSTreeNode , name string ) * DNSTreeNodeData {
109
+ for _ , d := range node .DataSets {
110
+ if slices .Contains (d .Targets , name ) {
111
+ return & d
112
+ }
113
+ }
114
+ return nil
115
+ }
116
+
117
+ func AddLabelToBranch (node * DNSTreeNode , branch , label , value string ) {
118
+ d := findDataSetForChild (node , branch )
119
+ if d == nil {
120
+ node .DataSets = append (node .DataSets , DNSTreeNodeData {
121
+ Labels : endpoint.Labels {
122
+ label : value ,
123
+ },
124
+ Targets : []string {
125
+ branch ,
126
+ },
127
+ })
128
+ } else {
129
+ if len (d .Targets ) == 1 {
130
+ d .Labels [label ] = value
131
+ } else {
132
+ //remove target from shared dataset and recreate uniquely
133
+ for i , t := range d .Targets {
134
+ if t == branch {
135
+ d .Targets = append (d .Targets [:i ], d .Targets [i + 1 :]... )
136
+ newDS := DNSTreeNodeData {
137
+ Labels : d .Labels .DeepCopy (),
138
+ Targets : []string {branch },
139
+ }
140
+ newDS .Labels [label ] = value
141
+ node .DataSets = append (node .DataSets , newDS )
142
+ }
143
+ }
144
+ }
145
+ }
146
+ }
147
+
148
+ func AddChild (parent * DNSTreeNode , child * DNSTreeNode ) {
149
+ parent .Children = append (parent .Children , child )
150
+ child .Parent = parent
151
+ }
152
+
153
+ func RemoveLabelFromParents (node * DNSTreeNode , label string ) {
154
+ if isRoot (node ) {
155
+ return
156
+ }
157
+
158
+ d := findDataSetForChild (node .Parent , node .Name )
159
+ if d == nil {
160
+ return
161
+ }
162
+
163
+ delete (d .Labels , label )
164
+
165
+ RemoveLabelFromParents (node .Parent , label )
166
+ }
167
+
168
+ func RemoveLabelFromTree (node * DNSTreeNode , label string ) {
169
+ for _ , d := range node .DataSets {
170
+ delete (d .Labels , label )
171
+ }
172
+
173
+ for _ , c := range node .Children {
174
+ RemoveLabelFromTree (c , label )
175
+ }
176
+ }
177
+
178
+ func AddLabelToTree (node * DNSTreeNode , label , value string ) {
179
+ for _ , c := range node .Children {
180
+ AddLabelToBranch (node , c .Name , label , value )
181
+ AddLabelToTree (c , label , value )
182
+ }
183
+ }
184
+
185
+ func HasLabelForBranch (node * DNSTreeNode , branch , label , value string ) bool {
186
+ d := findDataSetForChild (node , branch )
187
+ if d == nil {
188
+ return false
189
+ }
190
+ if v , ok := d .Labels [label ]; ok {
191
+ return value == v
192
+ }
193
+ return false
194
+ }
195
+
28
196
// RemoveNode removes a node from a tree.
29
197
// If the node was the only child of the parent node,
30
198
// the parent will be removed as well unless the parent is a root node
@@ -115,21 +283,11 @@ func ToEndpoints(node *DNSTreeNode, endpoints *[]*endpoint.Endpoint) *[]*endpoin
115
283
if isALeafNode (node ) {
116
284
return endpoints
117
285
}
118
- targets := [] string {}
286
+
119
287
for _ , child := range node .Children {
120
- targets = append (targets , child .Name )
121
288
ToEndpoints (child , endpoints )
122
289
}
123
290
124
- // this should not happen. the node is either leaf or has datasets (unless the cree was made manually)
125
- if node .DataSets == nil {
126
- * endpoints = append (* endpoints , & endpoint.Endpoint {
127
- DNSName : node .Name ,
128
- Targets : targets ,
129
- })
130
- return endpoints
131
- }
132
-
133
291
for _ , data := range node .DataSets {
134
292
* endpoints = append (* endpoints , & endpoint.Endpoint {
135
293
DNSName : node .Name ,
@@ -162,6 +320,7 @@ func populateNode(node *DNSTreeNode, record *v1alpha1.DNSRecord) {
162
320
}
163
321
164
322
for _ , c := range children {
323
+ c .Parent = node
165
324
populateNode (c , record )
166
325
}
167
326
node .Children = children
@@ -204,5 +363,5 @@ func findDataSets(name string, record *v1alpha1.DNSRecord) []DNSTreeNodeData {
204
363
// isALeafNode check if this is the last node in a tree
205
364
func isALeafNode (node * DNSTreeNode ) bool {
206
365
// no children means this is pointing to an IP or a host outside of the DNS Record
207
- return node . Children == nil || len (node .Children ) == 0
366
+ return len (node .Children ) == 0
208
367
}
0 commit comments