0

I would like to check that every Service in a rendered Helm chart has exactly one matching Pod.

A Pod to service association exists when every entry specified in a Services spec.selector object is reflected in a Pods metadata.labels object (which can have additional keys).

The following policy is tested using Conftest by running conftest test --combine {YAML_FILE} and checks that every Service has at least one matching Pod. I'm completely unsure how to transform this so that it checks for exactly one matching Pod.

package main

import future.keywords.every

in_set(e, s) { s[e] }

get_pod(resource) := pod {
  in_set(resource.kind, {"Deployment", "StatefulSet", "Job"})
  pod := resource.spec.template
}

# ensure that every service has at least one matching pod
# TODO: ensure that every service has exactly one matching pod
deny_service_without_matching_pod[msg] {
  service := input[_].contents
  service.kind == "Service"
  selector := object.get(service, ["spec", "selector"], {})
  pods := { p | p := get_pod(input[_].contents) }
  every pod in pods {
    labels := object.get(pod, ["metadata", "labels"], {})
    matches := { key | some key; labels[key] == selector[key] }
    count(matches) != count(selector)
  }
  msg := sprintf("service %s has no matching pod", [service.metadata.name])
}

Marginal note: The get_pod function doesn't retrieve all PodTemplates that can possibly occur in a Helm chart. Other checks are in place to keep the Kubernetes API-surface of the Helm chart small - so in this case, Pods can only occur in Deployment, StatefulSet and Job.

Maybe there are rego experts here that can chime in and help. That would be very appreciated!

Codepunkt
  • 532
  • 4
  • 15

1 Answers1

2

Since there's no sample data provided, this is untested code. It should work though :)

package main

import future.keywords.in

pods := { pod | 
  resource := input[_].contents
  resource.kind in {"Deployment", "StatefulSet", "Job"}
  pod := resource.spec.template
}

services := { service |
  service := input[_].contents
  service.kind == "Service"
}

pods_matching_selector(selector) := { pod |
  selector != {}
  some pod in pods
  labels := pod.metadata.labels
  some key
  labels[key] == selector[key]
}

deny_service_without_one_matching_pod[msg] {
  some service in services
  selector := object.get(service, ["spec", "selector"], {})
  matching_pods := count(pods_matching_selector(selector))
  matching_pods != 1
  msg := sprintf(
    "service %s has %d matching pods, must have exactly one",
    [service.metadata.name, matching_pods]
  )
}
Codepunkt
  • 532
  • 4
  • 15
Devoops
  • 2,018
  • 8
  • 21