Skip to content

zemanlx/kat

Repository files navigation

Kat logo

kat - Kubernetes Admission Tester

CI Go Report Card License

kat is a lightweight, local testing tool for Kubernetes Admission Policies (ValidatingAdmissionPolicy and MutatingAdmissionPolicy). It allows you to write test cases using standard Kubernetes manifests and verify your policies' behavior without needing a running cluster.

Features

  • Standard Kubernetes YAML: Write tests using plain K8s manifests - no new DSL to learn.
  • Full CEL Support: Uses the official Kubernetes CEL libraries for 100% accurate evaluation.
  • Comprehensive Policy Support:
    • ValidatingAdmissionPolicy (Allow, Deny, Warn, Audit)
    • MutatingAdmissionPolicy (Mutate, No-op)
  • All Operations: Supports CREATE, UPDATE, DELETE, and CONNECT operations.
  • Golden File Testing: Automatically verifies mutated objects against expected golden files.
  • Rich Context: Simulate complex scenarios with userInfo, namespaceObject, and matchConditions.
  • Parameter Testing: Test parameter-driven policies (paramKind/paramRef).

Installation

go install github.com/zemanlx/kat@latest

Or build from source:

git clone https://github.com/zemanlx/kat.git
cd kat
go install

Usage

The recommended way to use kat is to run it from the root of your repository. It will automatically discover and execute all tests found in tests/ directories recursively.

kat .

This commands will:

  1. Find all tests directories.
  2. Locate the corresponding Policy and Binding for each test (by looking up in the directory tree).
  3. Execute all found tests.

You can also target specific directories or files:

# Run tests for a specific policy
kat ./policies/my-policy/tests

# Run a specific test case
kat ./policies/my-policy/tests/my-policy.basic-test.object.yaml

Flags

  • -run <regex>: Run only tests matching the regex pattern.
  • -v: Verbose output (shows detailed execution steps).
  • -json: Output results in JSON format.
kat -v -run "prod-.*-deny" .

Project Structure & Discovery

kat is designed to fit naturally into existing Kubernetes repositories, including those using Kustomize.

The tool works by discovery:

  1. It looks for tests/ directories containing test files.
  2. It looks for policy and binding files in the parent directory of tests/.

Supported filenames include:

  • policy.yaml / policies.yaml
  • binding.yaml / bindings.yaml
  • Any file ending in .policy.yaml or .binding.yaml

Note: You can define multiple policies and bindings in a single file (separated by ---), or split them across multiple files. The tool loads all valid policy/binding resources found in the directory.

This allows you to keep your tests co-located with your policy definitions. You just need to add a tests/ folder alongside your manifests.

Example Layout:

policies/
├── team-label-policy/
│   ├── kustomization.yaml  # (Optional) Kustomize file
│   ├── policy.yaml         # The AdmissionPolicy definition
│   ├── binding.yaml        # The AdmissionPolicyBinding
│   └── tests/              # Add this folder for kat
│       ├── team-label.has-label.allow.object.yaml
│       ├── team-label.missing.deny.object.yaml
│       └── ...

In this setup, running kat . at the root will automatically find the tests directory, associate it with the policy in the parent team-label-policy directory, and execute the tests.

Writing Tests

Tests are defined by file naming conventions. The filename structure determines the test type and expectations.

File Naming Convention

Pattern: <policy-name>.<test-name>.<expect>.<type>.yaml

Requirement: The <policy-name> prefix must match the metadata.name of the policy being tested. (If a directory contains only a single policy, kats automatically associates all tests with that policy).

  • expect: allow, deny, warn, audit (for Validating)
  • type: object, oldObject, request, params

Validating Admission Policy

1. Expect Allow: Create a file ending in .allow.object.yaml.

# my-policy.test-1.allow.object.yaml
apiVersion: v1
kind: Pod
metadata:
  name: allowed-pod
  labels:
    cost-center: "123"

2. Expect Deny: Create a file ending in .deny.object.yaml.

# my-policy.test-2.deny.object.yaml
apiVersion: v1
kind: Pod
metadata:
  name: denied-pod
  # Missing required labels

3. Expect Specific Message: Add a .message.txt file side-by-side.

# my-policy.test-2.deny.message.txt
Pod must have a cost-center label

Mutating Admission Policy

1. Mutation Test: Provide the input object and the expected output (golden file).

  • Input: my-policy.test-1.object.yaml
  • Expected: my-policy.test-1.gold.yaml

If the actual mutation result differs from the golden file, the test fails and prints a diff.

Advanced Scenarios

Request Context (.request.yaml)

Use a .request.yaml file to provide additional admission context like user info or namespace details.

# my-policy.test-1.allow.request.yaml
operation: CREATE
userInfo:
  username: "system:serviceaccount:kube-system:job-controller"
namespaceObject:
  metadata:
    labels:
      environment: production

Parameters (.params.yaml)

For policies using paramKind, provide the parameter resource.

# my-policy.test-1.allow.params.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: policy-config
data:
  excludedNamespaces: "kube-system,monitoring"

Authorizer Mocking (.authorizer.yaml)

You can mock Kubernetes Authorizer responses (SubjectAccessReview) for policies that use authorizer checks in CEL. Create a .authorizer.yaml file side-by-side with your test files.

# my-policy.test-1.allow.authorizer.yaml
- group: ""
  resource: "pods"
  subresource: ""
  namespace: "default"
  verb: "create"
  decision: "allow"

The mock matches requests based on group, resource, subresource, namespace, and verb. By default, any check not explicitly mocked will return "NoOpinion" (which usually results in a denial or failed check depending on policy logic).

Operations (UPDATE / DELETE)

  • UPDATE: Provide both .object.yaml (new) and .oldObject.yaml (old).
  • DELETE: Provide only .oldObject.yaml (resource being deleted).

Examples

Check the test-policies-pass directory for a comprehensive set of examples covering:

  • Basic validation and mutation
  • Parameters and ConfigMaps
  • Namespace-based logic
  • CONNECT operations (kubectl exec)
  • Match conditions
  • Warnings and Audit annotations

Packages

No packages published

Languages