Skip to content

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

text
spec.security.networkPolicy

Defined in api/v1alpha1/memcached_types.go on the NetworkPolicySpec struct:

go
type NetworkPolicySpec struct {
    Enabled        bool                              `json:"enabled,omitempty"`
    AllowedSources []networkingv1.NetworkPolicyPeer  `json:"allowedSources,omitempty,omitzero"`
}
FieldTypeRequiredDefaultDescription
enabledboolNofalseControls whether a NetworkPolicy is created
allowedSources[]NetworkPolicyPeerNoList of peers allowed to access Memcached; when empty, all sources are allowed

AllowedSources

Each entry in allowedSources is a standard Kubernetes NetworkPolicyPeer, supporting:

Peer FieldTypeDescription
namespaceSelectorLabelSelectorSelects namespaces whose pods are allowed
podSelectorLabelSelectorSelects pods within the allowed namespaces
ipBlockIPBlockAllows 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:

PortProtocolConditionPurpose
11211TCPAlwaysMemcached client connections
11212TCPspec.security.tls.enabled is trueMemcached TLS connections
9150TCPspec.monitoring.enabled is truePrometheus metrics exporter

All ports are included in a single IngressRule, ensuring the from peers apply uniformly to all allowed ports.

Ingress From (AllowedSources)

AllowedSources ValueIngress from FieldEffect
nil or emptyNot set (omitted)All sources allowed (Kubernetes default)
Non-empty listSet to the list of peersOnly 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 KeyValuePurpose
app.kubernetes.io/namememcachedIdentifies the application
app.kubernetes.io/instance<cr-name>Distinguishes instances of the same application
app.kubernetes.io/managed-bymemcached-operatorIdentifies 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:

go
np.Spec.PodSelector = metav1.LabelSelector{
    MatchLabels: labelsForMemcached(mc.Name),
}

Policy Types

The NetworkPolicy specifies Ingress as the only policy type:

go
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:

go
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.security is nil
  • spec.security.networkPolicy is nil
  • spec.security.networkPolicy.enabled is false

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:

FieldValue
apiVersionmemcached.c5c3.io/v1alpha1
kindMemcached
name<cr-name>
uid<cr-uid>
controllertrue
blockOwnerDeletiontrue

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)

yaml
apiVersion: memcached.c5c3.io/v1alpha1
kind: Memcached
metadata:
  name: my-cache
  namespace: default
spec:
  replicas: 3
  security:
    networkPolicy:
      enabled: true

Produces:

yaml
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: 11211

NetworkPolicy with Namespace Selector

yaml
spec:
  replicas: 3
  security:
    networkPolicy:
      enabled: true
      allowedSources:
        - namespaceSelector:
            matchLabels:
              env: production

Produces a NetworkPolicy with ingress restricted to pods from namespaces labeled env: production.

NetworkPolicy with Pod Selector

yaml
spec:
  replicas: 3
  security:
    networkPolicy:
      enabled: true
      allowedSources:
        - podSelector:
            matchLabels:
              app: my-webapp

Produces a NetworkPolicy with ingress restricted to pods labeled app: my-webapp in the same namespace.

NetworkPolicy with Monitoring Enabled

yaml
spec:
  replicas: 3
  monitoring:
    enabled: true
  security:
    networkPolicy:
      enabled: true

Produces a NetworkPolicy with ingress ports 11211 (memcached) and 9150 (metrics).

NetworkPolicy with TLS Enabled

yaml
spec:
  replicas: 3
  security:
    tls:
      enabled: true
      certificateSecretRef:
        name: memcached-tls
    networkPolicy:
      enabled: true

Produces a NetworkPolicy with ingress ports 11211 (memcached) and 11212 (memcached-tls).

Full Security Configuration

yaml
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-webapp

Produces 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)

yaml
apiVersion: memcached.c5c3.io/v1alpha1
kind: Memcached
metadata:
  name: my-cache
spec:
  replicas: 3

No NetworkPolicy is created. The reconcileNetworkPolicy method returns immediately.


Runtime Behavior

ActionResult
Enable NetworkPolicy (enabled: true)NetworkPolicy created with port 11211 on next reconcile
Enable monitoringPort 9150 added to NetworkPolicy ingress on next reconcile
Disable monitoringPort 9150 removed from NetworkPolicy ingress on next reconcile
Enable TLSPort 11212 added to NetworkPolicy ingress on next reconcile
Disable TLSPort 11212 removed from NetworkPolicy ingress on next reconcile
Set allowedSourcesIngress from peers updated on next reconcile
Clear allowedSourcesIngress from peers removed (all sources allowed)
Disable NetworkPolicy (enabled: false)NetworkPolicy reconciliation skipped; existing NetworkPolicy persists until CR is deleted
Remove security sectionNetworkPolicy reconciliation skipped; existing NetworkPolicy persists until CR is deleted
Delete Memcached CRNetworkPolicy deleted via garbage collection (owner reference)
Reconcile twice with same specNo 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:

go
func constructNetworkPolicy(mc *Memcached, np *NetworkPolicy)
  • Sets metadata.labels and spec.podSelector.matchLabels using labelsForMemcached
  • Sets spec.policyTypes to [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 from peers from allowedSources when non-empty
  • Omits ingress from field when allowedSources is empty (allowing all sources)
  • Fully replaces spec.Ingress on every call for idempotent behavior with CreateOrUpdate

The networkPolicyEnabled function is a pure guard:

go
func networkPolicyEnabled(mc *Memcached) bool
  • Returns false when spec.security is nil
  • Returns false when spec.security.networkPolicy is nil
  • Returns false when enabled is false
  • Returns true only when enabled is true