feat: add metadata enforcement (#1990)

* fix(controller): decode old object for delete requests

Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com>

* chore: modernize golang

Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com>

* chore: modernize golang

Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com>

* chore: modernize golang

Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com>

* fix: preserve ca-bundles injected from external providers

Signed-off-by: Oliver Baehler <oliver@sudo-i.net>

* feat: add metadata enforcement

Signed-off-by: Oliver Baehler <oliver@sudo-i.net>

* feat: add metadata enforcement

Signed-off-by: Oliver Baehler <oliver@sudo-i.net>

* fix: add resourcepoolclaim validation

Signed-off-by: Oliver Baehler <oliver@sudo-i.net>

* fix: add resourcepoolclaim validation

Signed-off-by: Oliver Baehler <oliver@sudo-i.net>

* fix: add resourcepoolclaim validation

Signed-off-by: Oliver Baehler <oliver@sudo-i.net>

* Potential fix for pull request finding

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

* fix: add resourcepoolclaim validation

Signed-off-by: Oliver Baehler <oliver@sudo-i.net>

---------

Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com>
Signed-off-by: Oliver Baehler <oliver@sudo-i.net>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
Oliver Bähler
2026-07-02 13:42:08 +02:00
committed by GitHub
co-authored by Copilot Autofix powered by AI
parent 6fbd472f27
commit 77d1810bb9
89 changed files with 12731 additions and 556 deletions
+1
View File
@@ -33,6 +33,7 @@ const (
CreatedByCapsuleLabel = "projectcapsule.dev/created-by"
CustomResourcesLabel = "projectcapsule.dev/custom-resources"
ResourceOriginLabel = "projectcapsule.dev/resource-origin"
NewManagedByCapsuleLabel = "projectcapsule.dev/managed-by"
ManagedByCapsuleLabel = "capsule.clastix.io/managed-by"
+213
View File
@@ -0,0 +1,213 @@
// Copyright 2020-2026 Project Capsule Authors
// SPDX-License-Identifier: Apache-2.0
package meta
import (
"strings"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
type ManagedMetadata struct {
labels map[string]struct{}
annotations map[string]struct{}
annotationPrefixes []string
}
func NewManagedMetadata(
labels []string,
annotations []string,
) ManagedMetadata {
m := ManagedMetadata{
labels: stringSet(
ResourcesLabel,
TenantNameLabel,
TenantLabel,
NewTenantLabel,
ResourcePoolLabel,
FreezeLabel,
OwnerPromotionLabel,
ServiceAccountPromotionLabel,
CordonedLabel,
CapsuleNameLabel,
CreatedByCapsuleLabel,
CustomResourcesLabel,
NewManagedByCapsuleLabel,
ManagedByCapsuleLabel,
LimitRangeLabel,
NetworkPolicyLabel,
ResourceQuotaLabel,
RolebindingLabel,
),
annotations: stringSet(
ReleaseAnnotation,
ReconcileAnnotation,
AvailableIngressClassesAnnotation,
AvailableIngressClassesRegexpAnnotation,
AvailableStorageClassesAnnotation,
AvailableStorageClassesRegexpAnnotation,
AllowedRegistriesAnnotation,
AllowedRegistriesRegexpAnnotation,
ForbiddenNamespaceLabelsAnnotation,
ForbiddenNamespaceLabelsRegexpAnnotation,
ForbiddenNamespaceAnnotationsAnnotation,
ForbiddenNamespaceAnnotationsRegexpAnnotation,
ProtectedTenantAnnotation,
),
annotationPrefixes: compactStrings(
ResourceQuotaAnnotationPrefix,
ResourceUsedAnnotationPrefix,
),
}
m.addLabels(labels...)
m.addAnnotations(annotations...)
return m
}
func (m ManagedMetadata) HasLabel(key string) bool {
_, ok := m.labels[key]
return ok
}
func (m ManagedMetadata) HasAnnotation(key string) bool {
if _, ok := m.annotations[key]; ok {
return true
}
for _, prefix := range m.annotationPrefixes {
if strings.HasPrefix(key, prefix) {
return true
}
}
return false
}
func (m ManagedMetadata) addLabels(values ...string) {
addStrings(m.labels, values...)
}
func (m ManagedMetadata) addAnnotations(values ...string) {
addStrings(m.annotations, values...)
}
func addStrings(set map[string]struct{}, values ...string) {
for _, value := range values {
value = strings.TrimSpace(value)
if value == "" {
continue
}
set[value] = struct{}{}
}
}
func stringSet(values ...string) map[string]struct{} {
if len(values) == 0 {
return nil
}
out := make(map[string]struct{}, len(values))
for _, value := range values {
value = strings.TrimSpace(value)
if value == "" {
continue
}
out[value] = struct{}{}
}
return out
}
func compactStrings(values ...string) []string {
if len(values) == 0 {
return nil
}
out := make([]string, 0, len(values))
for _, value := range values {
value = strings.TrimSpace(value)
if value == "" {
continue
}
out = append(out, value)
}
return out
}
type ObjectSkipRule struct {
// Labels with values which indicate a skip condition.
Labels map[string]string
// Annotations with values which indicate a skip condition.
Annotations map[string]string
}
func (s *ObjectSkipRule) ShouldSkip(
labels map[string]string,
annotations map[string]string,
) bool {
if s == nil {
return false
}
for key, expected := range s.Labels {
value, ok := labels[key]
if !ok || value != expected {
return false
}
}
for key, expected := range s.Annotations {
value, ok := annotations[key]
if !ok || value != expected {
return false
}
}
return len(s.Labels) > 0 || len(s.Annotations) > 0
}
func DefaultObjectSkipRules() []ObjectSkipRule {
return []ObjectSkipRule{
{
Labels: map[string]string{
NewManagedByCapsuleLabel: ValueController,
},
},
{
Labels: map[string]string{
NewManagedByCapsuleLabel: ValueControllerResources,
},
},
}
}
func ShouldSkipObjectByRules(
obj *metav1.PartialObjectMetadata,
rules []ObjectSkipRule,
) bool {
if obj == nil || len(rules) == 0 {
return false
}
labels := obj.GetLabels()
annotations := obj.GetAnnotations()
for _, rule := range rules {
if rule.ShouldSkip(labels, annotations) {
return true
}
}
return false
}
+850
View File
@@ -0,0 +1,850 @@
// Copyright 2020-2026 Project Capsule Authors
// SPDX-License-Identifier: Apache-2.0
package meta
import (
"reflect"
"testing"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
func TestNewManagedMetadata(t *testing.T) {
t.Parallel()
t.Run("contains default managed labels", func(t *testing.T) {
t.Parallel()
m := NewManagedMetadata(nil, nil)
for _, key := range []string{
ResourcesLabel,
TenantNameLabel,
TenantLabel,
NewTenantLabel,
ResourcePoolLabel,
FreezeLabel,
OwnerPromotionLabel,
ServiceAccountPromotionLabel,
CordonedLabel,
CapsuleNameLabel,
CreatedByCapsuleLabel,
CustomResourcesLabel,
NewManagedByCapsuleLabel,
ManagedByCapsuleLabel,
LimitRangeLabel,
NetworkPolicyLabel,
ResourceQuotaLabel,
RolebindingLabel,
} {
if !m.HasLabel(key) {
t.Fatalf("expected managed label %q", key)
}
}
})
t.Run("contains default managed annotations", func(t *testing.T) {
t.Parallel()
m := NewManagedMetadata(nil, nil)
for _, key := range []string{
ReleaseAnnotation,
ReconcileAnnotation,
AvailableIngressClassesAnnotation,
AvailableIngressClassesRegexpAnnotation,
AvailableStorageClassesAnnotation,
AvailableStorageClassesRegexpAnnotation,
AllowedRegistriesAnnotation,
AllowedRegistriesRegexpAnnotation,
ForbiddenNamespaceLabelsAnnotation,
ForbiddenNamespaceLabelsRegexpAnnotation,
ForbiddenNamespaceAnnotationsAnnotation,
ForbiddenNamespaceAnnotationsRegexpAnnotation,
ProtectedTenantAnnotation,
} {
if !m.HasAnnotation(key) {
t.Fatalf("expected managed annotation %q", key)
}
}
})
t.Run("contains default managed annotation prefixes", func(t *testing.T) {
t.Parallel()
m := NewManagedMetadata(nil, nil)
for _, key := range []string{
ResourceQuotaAnnotationPrefix + "cpu",
ResourceQuotaAnnotationPrefix + "memory",
ResourceUsedAnnotationPrefix + "cpu",
ResourceUsedAnnotationPrefix + "memory",
} {
if !m.HasAnnotation(key) {
t.Fatalf("expected managed annotation prefix match for %q", key)
}
}
})
t.Run("adds custom managed labels and annotations", func(t *testing.T) {
t.Parallel()
m := NewManagedMetadata(
[]string{
"example.corp/label",
" example.corp/trimmed-label ",
"",
" ",
},
[]string{
"example.corp/annotation",
" example.corp/trimmed-annotation ",
"",
" ",
},
)
for _, key := range []string{
"example.corp/label",
"example.corp/trimmed-label",
} {
if !m.HasLabel(key) {
t.Fatalf("expected custom managed label %q", key)
}
}
for _, key := range []string{
"example.corp/annotation",
"example.corp/trimmed-annotation",
} {
if !m.HasAnnotation(key) {
t.Fatalf("expected custom managed annotation %q", key)
}
}
if m.HasLabel("") {
t.Fatalf("empty label must not be managed")
}
if m.HasAnnotation("") {
t.Fatalf("empty annotation must not be managed")
}
})
t.Run("matching is exact and case-sensitive", func(t *testing.T) {
t.Parallel()
m := NewManagedMetadata(
[]string{
"example.corp/managed",
},
[]string{
"example.corp/managed",
},
)
if !m.HasLabel("example.corp/managed") {
t.Fatalf("expected exact label match")
}
if m.HasLabel("Example.Corp/managed") {
t.Fatalf("expected label lookup to be case-sensitive")
}
if m.HasLabel(" example.corp/managed ") {
t.Fatalf("expected label lookup not to trim lookup key")
}
if !m.HasAnnotation("example.corp/managed") {
t.Fatalf("expected exact annotation match")
}
if m.HasAnnotation("Example.Corp/managed") {
t.Fatalf("expected annotation lookup to be case-sensitive")
}
if m.HasAnnotation(" example.corp/managed ") {
t.Fatalf("expected annotation lookup not to trim lookup key")
}
})
t.Run("unknown metadata is not managed", func(t *testing.T) {
t.Parallel()
m := NewManagedMetadata(nil, nil)
if m.HasLabel("example.corp/not-managed") {
t.Fatalf("unexpected managed label")
}
if m.HasAnnotation("example.corp/not-managed") {
t.Fatalf("unexpected managed annotation")
}
})
}
func TestStringSet(t *testing.T) {
t.Parallel()
tests := []struct {
name string
in []string
want map[string]struct{}
}{
{
name: "empty input returns nil",
in: nil,
want: nil,
},
{
name: "trims values skips blanks and deduplicates",
in: []string{
"alpha",
" alpha ",
"",
" ",
"beta",
},
want: map[string]struct{}{
"alpha": {},
"beta": {},
},
},
{
name: "case sensitive",
in: []string{
"alpha",
"Alpha",
},
want: map[string]struct{}{
"alpha": {},
"Alpha": {},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
got := stringSet(tt.in...)
if !reflect.DeepEqual(got, tt.want) {
t.Fatalf("expected %#v, got %#v", tt.want, got)
}
})
}
}
func TestCompactStrings(t *testing.T) {
t.Parallel()
tests := []struct {
name string
in []string
want []string
}{
{
name: "empty input returns nil",
in: nil,
want: nil,
},
{
name: "trims values and skips blanks",
in: []string{
"alpha",
" alpha ",
"",
" ",
"beta",
},
want: []string{
"alpha",
"alpha",
"beta",
},
},
{
name: "does not deduplicate prefixes",
in: []string{
"alpha/",
"alpha/",
},
want: []string{
"alpha/",
"alpha/",
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
got := compactStrings(tt.in...)
if !reflect.DeepEqual(got, tt.want) {
t.Fatalf("expected %#v, got %#v", tt.want, got)
}
})
}
}
func TestAddStrings(t *testing.T) {
t.Parallel()
set := map[string]struct{}{
"existing": {},
}
addStrings(
set,
"alpha",
" alpha ",
"",
" ",
"beta",
)
want := map[string]struct{}{
"existing": {},
"alpha": {},
"beta": {},
}
if !reflect.DeepEqual(set, want) {
t.Fatalf("expected %#v, got %#v", want, set)
}
}
func TestManagedMetadataAddMethods(t *testing.T) {
t.Parallel()
m := ManagedMetadata{
labels: map[string]struct{}{},
annotations: map[string]struct{}{},
}
m.addLabels("example.corp/label", " example.corp/trimmed-label ", "")
m.addAnnotations("example.corp/annotation", " example.corp/trimmed-annotation ", "")
if !m.HasLabel("example.corp/label") {
t.Fatalf("expected added label")
}
if !m.HasLabel("example.corp/trimmed-label") {
t.Fatalf("expected trimmed added label")
}
if !m.HasAnnotation("example.corp/annotation") {
t.Fatalf("expected added annotation")
}
if !m.HasAnnotation("example.corp/trimmed-annotation") {
t.Fatalf("expected trimmed added annotation")
}
if m.HasLabel("") {
t.Fatalf("empty label must not be added")
}
if m.HasAnnotation("") {
t.Fatalf("empty annotation must not be added")
}
}
func TestManagedMetadataHasAnnotationPrefix(t *testing.T) {
t.Parallel()
m := ManagedMetadata{
annotations: map[string]struct{}{
"example.corp/exact": {},
},
annotationPrefixes: []string{
"example.corp/prefix/",
},
}
tests := []struct {
name string
key string
want bool
}{
{
name: "exact annotation",
key: "example.corp/exact",
want: true,
},
{
name: "prefix annotation",
key: "example.corp/prefix/value",
want: true,
},
{
name: "prefix itself also matches",
key: "example.corp/prefix/",
want: true,
},
{
name: "similar prefix does not match",
key: "example.corp/prefix-other/value",
want: false,
},
{
name: "unknown annotation",
key: "example.corp/unknown",
want: false,
},
{
name: "case sensitive prefix",
key: "Example.Corp/prefix/value",
want: false,
},
{
name: "empty key",
key: "",
want: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
got := m.HasAnnotation(tt.key)
if got != tt.want {
t.Fatalf("expected %t, got %t", tt.want, got)
}
})
}
}
func TestObjectSkipRuleShouldSkip(t *testing.T) {
t.Parallel()
tests := []struct {
name string
rule ObjectSkipRule
labels map[string]string
annotations map[string]string
want bool
}{
{
name: "empty rule does not skip",
rule: ObjectSkipRule{},
want: false,
},
{
name: "matching label skips",
rule: ObjectSkipRule{
Labels: map[string]string{
"managed-by": "controller",
},
},
labels: map[string]string{
"managed-by": "controller",
},
want: true,
},
{
name: "missing label does not skip",
rule: ObjectSkipRule{
Labels: map[string]string{
"managed-by": "controller",
},
},
labels: nil,
want: false,
},
{
name: "non matching label value does not skip",
rule: ObjectSkipRule{
Labels: map[string]string{
"managed-by": "controller",
},
},
labels: map[string]string{
"managed-by": "human",
},
want: false,
},
{
name: "label match is case-sensitive",
rule: ObjectSkipRule{
Labels: map[string]string{
"managed-by": "controller",
},
},
labels: map[string]string{
"managed-by": "Controller",
},
want: false,
},
{
name: "matching annotation skips",
rule: ObjectSkipRule{
Annotations: map[string]string{
"example.corp/skip": "true",
},
},
annotations: map[string]string{
"example.corp/skip": "true",
},
want: true,
},
{
name: "missing annotation does not skip",
rule: ObjectSkipRule{
Annotations: map[string]string{
"example.corp/skip": "true",
},
},
annotations: nil,
want: false,
},
{
name: "non matching annotation value does not skip",
rule: ObjectSkipRule{
Annotations: map[string]string{
"example.corp/skip": "true",
},
},
annotations: map[string]string{
"example.corp/skip": "false",
},
want: false,
},
{
name: "all labels must match",
rule: ObjectSkipRule{
Labels: map[string]string{
"managed-by": "controller",
"owner": "capsule",
},
},
labels: map[string]string{
"managed-by": "controller",
"owner": "other",
},
want: false,
},
{
name: "all annotations must match",
rule: ObjectSkipRule{
Annotations: map[string]string{
"example.corp/skip": "true",
"example.corp/owner": "capsule",
},
},
annotations: map[string]string{
"example.corp/skip": "true",
"example.corp/owner": "other",
},
want: false,
},
{
name: "labels and annotations must both match",
rule: ObjectSkipRule{
Labels: map[string]string{
"managed-by": "controller",
},
Annotations: map[string]string{
"example.corp/skip": "true",
},
},
labels: map[string]string{
"managed-by": "controller",
},
annotations: map[string]string{
"example.corp/skip": "true",
},
want: true,
},
{
name: "matching label but missing required annotation does not skip",
rule: ObjectSkipRule{
Labels: map[string]string{
"managed-by": "controller",
},
Annotations: map[string]string{
"example.corp/skip": "true",
},
},
labels: map[string]string{
"managed-by": "controller",
},
annotations: nil,
want: false,
},
{
name: "missing key must not match empty expected value",
rule: ObjectSkipRule{
Labels: map[string]string{
"empty": "",
},
},
labels: nil,
want: false,
},
{
name: "present empty value matches empty expected value",
rule: ObjectSkipRule{
Labels: map[string]string{
"empty": "",
},
},
labels: map[string]string{
"empty": "",
},
want: true,
},
{
name: "extra labels and annotations do not prevent match",
rule: ObjectSkipRule{
Labels: map[string]string{
"managed-by": "controller",
},
},
labels: map[string]string{
"managed-by": "controller",
"extra": "value",
},
annotations: map[string]string{
"extra": "value",
},
want: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
got := tt.rule.ShouldSkip(tt.labels, tt.annotations)
if got != tt.want {
t.Fatalf("expected %t, got %t", tt.want, got)
}
})
}
}
func TestObjectSkipRuleShouldSkipNilReceiver(t *testing.T) {
t.Parallel()
var rule *ObjectSkipRule
if rule.ShouldSkip(
map[string]string{
"managed-by": "controller",
},
nil,
) {
t.Fatalf("nil rule must not skip")
}
}
func TestDefaultObjectSkipRules(t *testing.T) {
t.Parallel()
rules := DefaultObjectSkipRules()
if len(rules) != 2 {
t.Fatalf("expected three default skip rules, got %d", len(rules))
}
if got := rules[0].Labels[NewManagedByCapsuleLabel]; got != ValueController {
t.Fatalf("expected default skip label %q=%q, got %q", NewManagedByCapsuleLabel, ValueController, got)
}
if got := rules[1].Labels[NewManagedByCapsuleLabel]; got != ValueControllerResources {
t.Fatalf("expected legacy default skip label %q=%q, got %q", NewManagedByCapsuleLabel, ValueControllerResources, got)
}
for _, rule := range rules {
if len(rule.Annotations) != 0 {
t.Fatalf("expected no default skip annotations, got %#v", rule.Annotations)
}
}
}
func TestShouldSkipObjectByRules(t *testing.T) {
t.Parallel()
tests := []struct {
name string
obj *metav1.PartialObjectMetadata
rules []ObjectSkipRule
want bool
}{
{
name: "nil object does not skip",
obj: nil,
rules: DefaultObjectSkipRules(),
want: false,
},
{
name: "nil rules do not skip",
obj: objectWithMetadata(
map[string]string{
NewManagedByCapsuleLabel: ValueController,
},
nil,
),
rules: nil,
want: false,
},
{
name: "empty rules do not skip",
obj: objectWithMetadata(
map[string]string{
NewManagedByCapsuleLabel: ValueController,
},
nil,
),
rules: []ObjectSkipRule{},
want: false,
},
{
name: "default Capsule controller-managed object skips",
obj: objectWithMetadata(
map[string]string{
NewManagedByCapsuleLabel: ValueController,
},
nil,
),
rules: DefaultObjectSkipRules(),
want: true,
},
{
name: "legacy Capsule resources object skips",
obj: objectWithMetadata(
map[string]string{
NewManagedByCapsuleLabel: ValueControllerResources,
},
nil,
),
rules: DefaultObjectSkipRules(),
want: true,
},
{
name: "default skip is case-sensitive",
obj: objectWithMetadata(
map[string]string{
NewManagedByCapsuleLabel: "Controller",
},
nil,
),
rules: DefaultObjectSkipRules(),
want: false,
},
{
name: "plain managed-by controller is not skipped by default",
obj: objectWithMetadata(
map[string]string{
"managed-by": "controller",
},
nil,
),
rules: DefaultObjectSkipRules(),
want: false,
},
{
name: "non matching object does not skip",
obj: objectWithMetadata(
map[string]string{
NewManagedByCapsuleLabel: "human",
},
nil,
),
rules: DefaultObjectSkipRules(),
want: false,
},
{
name: "any matching rule skips",
obj: objectWithMetadata(
map[string]string{
"app": "demo",
},
map[string]string{
"example.corp/skip": "true",
},
),
rules: []ObjectSkipRule{
{
Labels: map[string]string{
"app": "other",
},
},
{
Annotations: map[string]string{
"example.corp/skip": "true",
},
},
},
want: true,
},
{
name: "object without labels and annotations does not skip",
obj: objectWithMetadata(nil, nil),
rules: []ObjectSkipRule{
{
Labels: map[string]string{
"managed-by": "controller",
},
},
},
want: false,
},
{
name: "empty skip rule does not skip object",
obj: objectWithMetadata(
map[string]string{
"app": "demo",
},
nil,
),
rules: []ObjectSkipRule{
{},
},
want: false,
},
{
name: "custom plain managed-by rule skips",
obj: objectWithMetadata(
map[string]string{
"managed-by": "controller",
},
nil,
),
rules: []ObjectSkipRule{
{
Labels: map[string]string{
"managed-by": "controller",
},
},
},
want: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
got := ShouldSkipObjectByRules(tt.obj, tt.rules)
if got != tt.want {
t.Fatalf("expected %t, got %t", tt.want, got)
}
})
}
}
func objectWithMetadata(
labels map[string]string,
annotations map[string]string,
) *metav1.PartialObjectMetadata {
return &metav1.PartialObjectMetadata{
ObjectMeta: metav1.ObjectMeta{
Labels: labels,
Annotations: annotations,
},
}
}
-2
View File
@@ -14,7 +14,6 @@ var WithoutCapsuleManagedResourcesLabelSelector = func() string {
selection.NotIn,
[]string{
ValueController,
ValueControllerResources,
},
)
@@ -27,7 +26,6 @@ var WithCapsuleManagedResourcesLabelSelector = func() string {
selection.In,
[]string{
ValueController,
ValueControllerResources,
},
)