Skip to content
This repository was archived by the owner on Apr 17, 2025. It is now read-only.

Commit ef965d8

Browse files
authored
Merge pull request #121 from adrianludwin/no-ext-tree
Don't store external tree labels explicitly
2 parents bad9db5 + ced6ece commit ef965d8

File tree

5 files changed

+42
-36
lines changed

5 files changed

+42
-36
lines changed

internal/forest/namespace.go

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ package forest
22

33
import (
44
"reflect"
5+
"strconv"
6+
"strings"
57

68
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
79
"k8s.io/apimachinery/pkg/labels"
@@ -26,7 +28,8 @@ type Namespace struct {
2628
exists bool
2729
allowCascadingDeletion bool
2830

29-
// labels store the original namespaces' labels
31+
// labels store the original namespaces' labels, and are used for object propagation exceptions
32+
// and to store the tree labels of external namespaces.
3033
labels map[string]string
3134

3235
// sourceObjects store the objects created by users, identified by GVK and name.
@@ -44,15 +47,10 @@ type Namespace struct {
4447
// Anchors store a list of anchors in the namespace.
4548
Anchors []string
4649

47-
// Manager stores the manager of the namespace. The default value
48-
// "hnc.x-k8s.io" means it's managed by HNC.
50+
// Manager stores the manager of the namespace. The default value of "hnc.x-k8s.io" means it's
51+
// managed by HNC. Any other value means that the namespace is an "external" namespace, whose
52+
// metadata (e.g. labels) are set outside of HNC.
4953
Manager string
50-
51-
// ExternalTreeLabels stores external tree labels if this namespace is an external namespace.
52-
// It will be empty if the namespace is not external. External namespace will at least have one
53-
// tree label of itself. The key is the tree label without ".tree.hnc.x-k8s.io/depth" suffix.
54-
// The value is the depth.
55-
ExternalTreeLabels map[string]int
5654
}
5755

5856
// Name returns the name of the namespace, of "<none>" if the namespace is nil.
@@ -90,6 +88,19 @@ func (ns *Namespace) UnsetExists() bool {
9088
return changed
9189
}
9290

91+
// GetTreeLabels returns all the tree labels with the values converted into integers for easier
92+
// manipulation.
93+
func (ns *Namespace) GetTreeLabels() map[string]int {
94+
r := map[string]int{}
95+
for k, v := range ns.labels {
96+
if !strings.Contains(k, api.LabelTreeDepthSuffix) {
97+
continue
98+
}
99+
r[k], _ = strconv.Atoi(v)
100+
}
101+
return r
102+
}
103+
93104
func (ns *Namespace) GetLabels() labels.Set {
94105
return labels.Set(ns.labels)
95106
}
@@ -176,5 +187,5 @@ func (ns *Namespace) HasAnchor(n string) bool {
176187

177188
// IsExternal returns true if the namespace is not managed by HNC.
178189
func (ns *Namespace) IsExternal() bool {
179-
return len(ns.ExternalTreeLabels) > 0
190+
return ns.Manager != "" && ns.Manager != api.MetaGroup
180191
}

internal/hierarchyconfig/reconciler.go

Lines changed: 9 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -331,15 +331,14 @@ func (r *Reconciler) syncWithForest(log logr.Logger, nsInst *corev1.Namespace, i
331331
// syncExternalNamespace sets external tree labels to the namespace in the forest
332332
// if the namespace is an external namespace not managed by HNC.
333333
func (r *Reconciler) syncExternalNamespace(log logr.Logger, nsInst *corev1.Namespace, ns *forest.Namespace) {
334-
ns.Manager = nsInst.Annotations[api.AnnotationManagedBy]
335-
if ns.Manager == "" || ns.Manager == api.MetaGroup {
334+
mgr := nsInst.Annotations[api.AnnotationManagedBy]
335+
if mgr == "" || mgr == api.MetaGroup {
336336
// If the internal namespace is just converted from an external namespace,
337337
// enqueue the descendants to remove the propagated external tree labels.
338338
if ns.IsExternal() {
339339
r.enqueueAffected(log, "subtree root converts from external to internal", ns.DescendantNames()...)
340340
}
341341
ns.Manager = api.MetaGroup
342-
ns.ExternalTreeLabels = nil
343342
return
344343
}
345344

@@ -348,18 +347,7 @@ func (r *Reconciler) syncExternalNamespace(log logr.Logger, nsInst *corev1.Names
348347
if !ns.IsExternal() {
349348
r.enqueueAffected(log, "subtree root converts from internal to external", ns.DescendantNames()...)
350349
}
351-
352-
// Get tree labels and set them in the forest. If there's no tree labels on the
353-
// namespace, set only one tree label of itself.
354-
etls := make(map[string]int)
355-
for tl, d := range nsInst.Labels {
356-
enm := strings.TrimSuffix(tl, api.LabelTreeDepthSuffix)
357-
if enm != tl {
358-
etls[enm], _ = strconv.Atoi(d)
359-
}
360-
}
361-
etls[ns.Name()] = 0
362-
ns.ExternalTreeLabels = etls
350+
ns.Manager = mgr
363351
}
364352

365353
// syncSubnamespaceParent sets the parent to the owner and updates the SubnamespaceAnchorMissing
@@ -504,6 +492,9 @@ func (r *Reconciler) syncAnchors(log logr.Logger, ns *forest.Namespace, anms []s
504492
func (r *Reconciler) syncTreeLabels(log logr.Logger, nsInst *corev1.Namespace, ns *forest.Namespace) bool {
505493
if ns.IsExternal() {
506494
metadata.SetLabel(nsInst, nsInst.Name+api.LabelTreeDepthSuffix, "0")
495+
496+
// Set the labels so we can retrieve the external tree labels in the future if needed
497+
ns.SetLabels(nsInst.Labels)
507498
return false
508499
}
509500

@@ -530,9 +521,8 @@ func (r *Reconciler) syncTreeLabels(log logr.Logger, nsInst *corev1.Namespace, n
530521
// Note it's impossible to have an external namespace as a non-root, which is
531522
// enforced by both admission controllers and the reconciler here.
532523
if curNS.IsExternal() {
533-
for k, v := range curNS.ExternalTreeLabels {
534-
l = k + api.LabelTreeDepthSuffix
535-
metadata.SetLabel(nsInst, l, strconv.Itoa(depth+v))
524+
for k, v := range curNS.GetTreeLabels() {
525+
metadata.SetLabel(nsInst, k, strconv.Itoa(depth+v))
536526
}
537527
break
538528
}
@@ -543,7 +533,7 @@ func (r *Reconciler) syncTreeLabels(log logr.Logger, nsInst *corev1.Namespace, n
543533
// Update the labels in the forest so that we can quickly access the labels and
544534
// compare if they match the given selector
545535
if ns.SetLabels(nsInst.Labels) {
546-
log.Info("Namespace tree labels have been updated.")
536+
log.Info("Namespace managed and tree labels have been updated")
547537
return true
548538
}
549539
return false

internal/hierarchyconfig/validator_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,8 +70,8 @@ func TestChangeParentOnManagedBy(t *testing.T) {
7070
l := zap.New()
7171

7272
// Make c and d external namespaces
73-
f.Get("c").ExternalTreeLabels = map[string]int{"c" + api.LabelTreeDepthSuffix: 0}
74-
f.Get("d").ExternalTreeLabels = map[string]int{"d" + api.LabelTreeDepthSuffix: 0}
73+
f.Get("c").Manager = "external-tool"
74+
f.Get("d").Manager = "external-tool"
7575

7676
// These cases test changing parent for internal or external namespaces, described
7777
// in the table at https://bit.ly/hnc-external-hierarchy#heading=h.z9mkbslfq41g

internal/namespace/validator.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,12 @@ func (v *Validator) illegalIncludedNamespaceLabel(req *nsRequest) admission.Resp
194194
// namespace name cannot be updated.
195195
func (v *Validator) nameExistsInExternalHierarchy(req *nsRequest) admission.Response {
196196
for _, nm := range v.Forest.GetNamespaceNames() {
197-
if _, ok := v.Forest.Get(nm).ExternalTreeLabels[req.ns.Name]; ok {
197+
ns := v.Forest.Get(nm)
198+
if !ns.IsExternal() {
199+
continue
200+
}
201+
externalTreeLabels := ns.GetTreeLabels()
202+
if _, ok := externalTreeLabels[req.ns.Name]; ok {
198203
msg := fmt.Sprintf("The namespace name %q is reserved by the external hierarchy manager %q.", req.ns.Name, v.Forest.Get(nm).Manager)
199204
return webhooks.Deny(metav1.StatusReasonAlreadyExists, msg)
200205
}

internal/namespace/validator_test.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -124,10 +124,10 @@ func TestCreateNamespace(t *testing.T) {
124124
f := foresttest.Create("-")
125125
vns := &Validator{Forest: f}
126126
a := f.Get("a")
127-
a.ExternalTreeLabels = map[string]int{
128-
nm: 1,
129-
a.Name(): 0,
130-
}
127+
a.SetLabels(map[string]string{
128+
nm + api.LabelTreeDepthSuffix: "1",
129+
a.Name() + api.LabelTreeDepthSuffix: "0",
130+
})
131131

132132
// Requested namespace uses "exhier" as name.
133133
ns := &corev1.Namespace{}

0 commit comments

Comments
 (0)