Files
Aleksei Sviridkin 1b723eb938 feat(securitygroup-controller): maintain membership labels for SecurityGroups
Add a controller that keeps each SecurityGroup's membership label
(securitygroup.sdn.cozystack.io/<name>) in sync with its attachments: it
stamps the label onto the pods of every attached application and removes it
when an application is detached or the SecurityGroup is deleted. The backing
CiliumNetworkPolicy's endpointSelector matches that label, so this is what
makes a SecurityGroup apply to its members.

The controller watches the marked backing policies and managed-app pods. It
resolves each attachment through the lineage labels the lineage webhook
already stamps — never the attachment list directly — and only ever labels
pods in the SecurityGroup's own namespace, using single-key merge patches so
it cannot clobber another group's label or a pod's lineage labels. A finalizer
on the backing policy guarantees the labels are stripped before the policy is
removed. Informer caches are bounded to managed pods and SecurityGroup-owned
policies.

Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Aleksei Sviridkin <f@lex.la>
2026-06-25 17:50:33 +03:00

102 lines
3.1 KiB
Go

// SPDX-License-Identifier: Apache-2.0
// Copyright 2026 The Cozystack Authors.
package securitygroupcontroller
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
)
// CiliumNetworkPolicy is a metadata-only, in-tree mirror of the cilium.io/v2
// CiliumNetworkPolicy resource. The securitygroup-controller only reads a
// policy's marker label, its attachments annotation and its finalizers — never
// the spec — so this mirror deliberately omits the spec entirely, keeping the
// controller binary free of the full Cilium module (whose Kubernetes pin is
// incompatible with this project's apimachinery fork).
//
// NEVER Update an object of this type: a PUT would serialize it without a spec
// and wipe the real policy's rules. Finalizer changes go through MergeFrom
// patches, which carry only the changed fields.
type CiliumNetworkPolicy struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
}
// CiliumNetworkPolicyList is a list of CiliumNetworkPolicy objects.
type CiliumNetworkPolicyList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []CiliumNetworkPolicy `json:"items"`
}
var ciliumGroupVersion = schema.GroupVersion{Group: "cilium.io", Version: "v2"}
// AddToScheme registers the in-tree CiliumNetworkPolicy mirror so the
// controller-runtime client can list and patch the backing policies.
func AddToScheme(scheme *runtime.Scheme) error {
scheme.AddKnownTypes(ciliumGroupVersion,
&CiliumNetworkPolicy{},
&CiliumNetworkPolicyList{},
)
metav1.AddToGroupVersion(scheme, ciliumGroupVersion)
return nil
}
// DeepCopyInto copies the receiver into out.
func (in *CiliumNetworkPolicy) DeepCopyInto(out *CiliumNetworkPolicy) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
}
// DeepCopy returns a deep copy of the receiver.
func (in *CiliumNetworkPolicy) DeepCopy() *CiliumNetworkPolicy {
if in == nil {
return nil
}
out := new(CiliumNetworkPolicy)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject returns a deep copy of the receiver as a runtime.Object.
func (in *CiliumNetworkPolicy) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto copies the receiver into out.
func (in *CiliumNetworkPolicyList) DeepCopyInto(out *CiliumNetworkPolicyList) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ListMeta.DeepCopyInto(&out.ListMeta)
if in.Items != nil {
out.Items = make([]CiliumNetworkPolicy, len(in.Items))
for i := range in.Items {
in.Items[i].DeepCopyInto(&out.Items[i])
}
}
}
// DeepCopy returns a deep copy of the receiver.
func (in *CiliumNetworkPolicyList) DeepCopy() *CiliumNetworkPolicyList {
if in == nil {
return nil
}
out := new(CiliumNetworkPolicyList)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject returns a deep copy of the receiver as a runtime.Object.
func (in *CiliumNetworkPolicyList) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}