NetworkPolicy Reconciliation
Reference documentation for the NetworkPolicy reconciliation logic that restricts ingress traffic to Memcached pods.
Source: internal/controller/networkpolicy.go, internal/controller/memcached_controller.go
Overview
When spec.security.networkPolicy.enabled is true, the reconciler ensures a matching NetworkPolicy exists in the same namespace with the same name as the Memcached CR. The NetworkPolicy is constructed from the CR spec using a pure builder function, then applied via controllerutil.CreateOrUpdate for idempotent create/update semantics. A controller owner reference on the NetworkPolicy enables automatic garbage collection when the Memcached CR is deleted.
The NetworkPolicy is opt-in — it is only created when explicitly enabled.
CRD Field Path
spec.security.networkPolicyDefined in api/v1alpha1/memcached_types.go on the NetworkPolicySpec struct:
type NetworkPolicySpec struct {
Enabled bool `json:"enabled,omitempty"`
AllowedSources []networkingv1.NetworkPolicyPeer `json:"allowedSources,omitempty,omitzero"`
}| Field | Type | Required | Default | Description |
|---|---|---|---|---|
enabled | bool | No | false | Controls whether a NetworkPolicy is created |
allowedSources | []NetworkPolicyPeer | No | — | List of peers allowed to access Memcached; when empty, all sources are allowed |
AllowedSources
Each entry in allowedSources is a standard Kubernetes NetworkPolicyPeer, supporting:
| Peer Field | Type | Description |
|---|---|---|
namespaceSelector | LabelSelector | Selects namespaces whose pods are allowed |
podSelector | LabelSelector | Selects pods within the allowed namespaces |
ipBlock | IPBlock | Allows traffic from a CIDR range with optional exceptions |
Using the native Kubernetes type directly gives operators full NetworkPolicy peer semantics without a custom abstraction layer.
NetworkPolicy Construction
constructNetworkPolicy(mc *Memcached, np *NetworkPolicy) sets the desired state of the NetworkPolicy in-place. It is called within the controllerutil.CreateOrUpdate mutate function so that both creation and updates use identical logic.
Ingress Ports
The builder dynamically computes ingress ports based on the CR state:
| Port | Protocol | Condition | Purpose |
|---|---|---|---|
| 11211 | TCP | Always | Memcached client connections |
| 11212 | TCP | spec.security.tls.enabled is true | Memcached TLS connections |
| 9150 | TCP | spec.monitoring.enabled is true | Prometheus metrics exporter |
All ports are included in a single IngressRule, ensuring the from peers apply uniformly to all allowed ports.
Ingress From (AllowedSources)
| AllowedSources Value | Ingress from Field | Effect |
|---|---|---|
| nil or empty | Not set (omitted) | All sources allowed (Kubernetes default) |
| Non-empty list | Set to the list of peers | Only matching peers allowed |
Per Kubernetes NetworkPolicy semantics, an ingress rule with ports but no from field allows traffic from any source on those ports.
Labels
The NetworkPolicy uses the same standard Kubernetes recommended labels as the Deployment and Service, generated by labelsForMemcached(name):
| Label Key | Value | Purpose |
|---|---|---|
app.kubernetes.io/name | memcached | Identifies the application |
app.kubernetes.io/instance | <cr-name> | Distinguishes instances of the same application |
app.kubernetes.io/managed-by | memcached-operator | Identifies the managing controller |
Pod Selector
The NetworkPolicy spec.podSelector uses the same label set as the Deployment's spec.selector.matchLabels, ensuring the policy targets only pods managed by the same Memcached CR instance:
np.Spec.PodSelector = metav1.LabelSelector{
MatchLabels: labelsForMemcached(mc.Name),
}Policy Types
The NetworkPolicy specifies Ingress as the only policy type:
np.Spec.PolicyTypes = []networkingv1.PolicyType{networkingv1.PolicyTypeIngress}This restricts incoming traffic while leaving egress unrestricted.
Reconciliation Method
reconcileNetworkPolicy(ctx, mc *Memcached) on MemcachedReconciler ensures the NetworkPolicy matches the desired state:
func (r *MemcachedReconciler) reconcileNetworkPolicy(ctx context.Context, mc *memcachedv1alpha1.Memcached) error {
if !networkPolicyEnabled(mc) {
return nil
}
np := &networkingv1.NetworkPolicy{
ObjectMeta: metav1.ObjectMeta{
Name: mc.Name,
Namespace: mc.Namespace,
},
}
_, err := r.reconcileResource(ctx, mc, np, func() error {
constructNetworkPolicy(mc, np)
return nil
}, "NetworkPolicy")
return err
}Skip Logic
The networkPolicyEnabled guard returns false (skipping NetworkPolicy reconciliation) when:
spec.securityis nilspec.security.networkPolicyis nilspec.security.networkPolicy.enabledisfalse
When NetworkPolicy is not enabled, reconcileNetworkPolicy returns nil immediately without error.
Owner Reference
The reconcileResource helper calls controllerutil.SetControllerReference, adding an owner reference to the NetworkPolicy's metadata:
| Field | Value |
|---|---|
apiVersion | memcached.c5c3.io/v1alpha1 |
kind | Memcached |
name | <cr-name> |
uid | <cr-uid> |
controller | true |
blockOwnerDeletion | true |
This enables:
- Garbage collection: Deleting the Memcached CR automatically deletes the owned NetworkPolicy via Kubernetes' owner reference cascade.
- Watch filtering: The
Owns(&networkingv1.NetworkPolicy{})watch on the controller maps NetworkPolicy events back to the owning Memcached CR for reconciliation.
Reconciliation Order
reconcileNetworkPolicy is called between reconcileServiceMonitor and reconcileStatus in the main Reconcile function.
CR Examples
NetworkPolicy with Defaults (All Sources Allowed)
apiVersion: memcached.c5c3.io/v1alpha1
kind: Memcached
metadata:
name: my-cache
namespace: default
spec:
replicas: 3
security:
networkPolicy:
enabled: trueProduces:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: my-cache
namespace: default
labels:
app.kubernetes.io/name: memcached
app.kubernetes.io/instance: my-cache
app.kubernetes.io/managed-by: memcached-operator
ownerReferences:
- apiVersion: memcached.c5c3.io/v1alpha1
kind: Memcached
name: my-cache
controller: true
blockOwnerDeletion: true
spec:
podSelector:
matchLabels:
app.kubernetes.io/name: memcached
app.kubernetes.io/instance: my-cache
app.kubernetes.io/managed-by: memcached-operator
policyTypes:
- Ingress
ingress:
- ports:
- protocol: TCP
port: 11211NetworkPolicy with Namespace Selector
spec:
replicas: 3
security:
networkPolicy:
enabled: true
allowedSources:
- namespaceSelector:
matchLabels:
env: productionProduces a NetworkPolicy with ingress restricted to pods from namespaces labeled env: production.
NetworkPolicy with Pod Selector
spec:
replicas: 3
security:
networkPolicy:
enabled: true
allowedSources:
- podSelector:
matchLabels:
app: my-webappProduces a NetworkPolicy with ingress restricted to pods labeled app: my-webapp in the same namespace.
NetworkPolicy with Monitoring Enabled
spec:
replicas: 3
monitoring:
enabled: true
security:
networkPolicy:
enabled: trueProduces a NetworkPolicy with ingress ports 11211 (memcached) and 9150 (metrics).
NetworkPolicy with TLS Enabled
spec:
replicas: 3
security:
tls:
enabled: true
certificateSecretRef:
name: memcached-tls
networkPolicy:
enabled: trueProduces a NetworkPolicy with ingress ports 11211 (memcached) and 11212 (memcached-tls).
Full Security Configuration
apiVersion: memcached.c5c3.io/v1alpha1
kind: Memcached
metadata:
name: my-cache
namespace: default
spec:
replicas: 3
monitoring:
enabled: true
security:
tls:
enabled: true
certificateSecretRef:
name: memcached-tls
networkPolicy:
enabled: true
allowedSources:
- namespaceSelector:
matchLabels:
env: production
podSelector:
matchLabels:
app: my-webappProduces a NetworkPolicy with all three ingress ports (11211, 11212, 9150) and traffic restricted to pods labeled app: my-webapp in namespaces labeled env: production.
NetworkPolicy Disabled (Default)
apiVersion: memcached.c5c3.io/v1alpha1
kind: Memcached
metadata:
name: my-cache
spec:
replicas: 3No NetworkPolicy is created. The reconcileNetworkPolicy method returns immediately.
Runtime Behavior
| Action | Result |
|---|---|
Enable NetworkPolicy (enabled: true) | NetworkPolicy created with port 11211 on next reconcile |
| Enable monitoring | Port 9150 added to NetworkPolicy ingress on next reconcile |
| Disable monitoring | Port 9150 removed from NetworkPolicy ingress on next reconcile |
| Enable TLS | Port 11212 added to NetworkPolicy ingress on next reconcile |
| Disable TLS | Port 11212 removed from NetworkPolicy ingress on next reconcile |
Set allowedSources | Ingress from peers updated on next reconcile |
Clear allowedSources | Ingress from peers removed (all sources allowed) |
Disable NetworkPolicy (enabled: false) | NetworkPolicy reconciliation skipped; existing NetworkPolicy persists until CR is deleted |
Remove security section | NetworkPolicy reconciliation skipped; existing NetworkPolicy persists until CR is deleted |
| Delete Memcached CR | NetworkPolicy deleted via garbage collection (owner reference) |
| Reconcile twice with same spec | No NetworkPolicy update (idempotent) |
| External drift (manual NetworkPolicy edit) | Corrected on next reconciliation cycle |
Implementation
The constructNetworkPolicy function in internal/controller/networkpolicy.go is a pure function that sets NetworkPolicy desired state in-place:
func constructNetworkPolicy(mc *Memcached, np *NetworkPolicy)- Sets
metadata.labelsandspec.podSelector.matchLabelsusinglabelsForMemcached - Sets
spec.policyTypesto[Ingress] - Always includes port 11211/TCP (memcached)
- Adds port 11212/TCP when TLS is enabled
- Adds port 9150/TCP when monitoring is enabled
- Sets ingress
frompeers fromallowedSourceswhen non-empty - Omits ingress
fromfield whenallowedSourcesis empty (allowing all sources) - Fully replaces
spec.Ingresson every call for idempotent behavior withCreateOrUpdate
The networkPolicyEnabled function is a pure guard:
func networkPolicyEnabled(mc *Memcached) bool- Returns
falsewhenspec.securityis nil - Returns
falsewhenspec.security.networkPolicyis nil - Returns
falsewhenenabledisfalse - Returns
trueonly whenenabledistrue