Files
capsule/pkg/api/meta/metadata.go
T
77d1810bb9 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>
2026-07-02 13:42:08 +02:00

214 lines
4.0 KiB
Go

// 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
}