Operator API Reference
Complete technical reference for the Challenge Operator validation APIs.
This page provides the complete reference for the Challenge Operator's custom resources: StaticValidation and DynamicValidation.
Overview
The Challenge Operator provides two types of validation:
| Type | Purpose | Implementation |
|---|---|---|
| StaticValidation | Validates resource structure using Rego policies | Open Policy Agent (OPA) |
| DynamicValidation | Validates runtime behavior | Built-in checks (logs, status, rbac) |
Both use the same TargetRef system to select which resources to validate.
StaticValidation
Validates the structure of Kubernetes manifests using Rego rules (Open Policy Agent).
API Version
apiVersion: challenge.kubeasy.dev/v1alpha1
kind: StaticValidationSpec
| Field | Type | Required | Description |
|---|---|---|---|
target | TargetRef | Yes | Selects resources to validate |
rulesConfigMap | ConfigMapRef | Yes | Reference to ConfigMap containing Rego rules |
TargetRef
Specifies which resources to validate:
| Field | Type | Required | Description |
|---|---|---|---|
apiVersion | string | Yes | Resource API version (e.g., v1, batch/v1) |
kind | string | Yes | Resource kind (e.g., Pod, Deployment) |
labelSelector | LabelSelector | No | Match resources by labels |
name | string | No | Match a specific resource by name |
Resolution priority:
- If
nameis set → validates that specific resource - If
labelSelectoris set → validates all matching resources - If neither → validates all resources of that kind in the namespace
ConfigMapRef
| Field | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Name of ConfigMap containing Rego rules |
ConfigMap requirements:
- Must be in the same namespace as the StaticValidation
- Must contain
.regofiles in the data section - Each rule file must define
data.kubeasy.challenge.violationquery - Violations must return array of
{msg: string}objects
Status
status:
allPassed: boolean # true if all rules passed
lastChecked: timestamp # Last validation time
error: string # Error message if validation failed
resources: # Per-resource results
- target:
apiVersion: string
kind: string
name: string
ruleResults:
- rule: string # Rule name
status: string # "Pass" or "Fail"
message: string # Violation messageComplete example
StaticValidation resource:
apiVersion: challenge.kubeasy.dev/v1alpha1
kind: StaticValidation
metadata:
name: pod-resource-limits
namespace: my-challenge
spec:
target:
apiVersion: v1
kind: Pod
labelSelector:
matchLabels:
app: web-app
rulesConfigMap:
name: resource-limits-rulesConfigMap with Rego rules:
apiVersion: v1
kind: ConfigMap
metadata:
name: resource-limits-rules
namespace: my-challenge
data:
resource-limits.rego: |
package kubeasy.challenge
violation[{"msg": msg}] {
container := input.spec.containers[_]
not container.resources.limits.cpu
msg := sprintf("Container %s missing CPU limit", [container.name])
}
violation[{"msg": msg}] {
container := input.spec.containers[_]
not container.resources.limits.memory
msg := sprintf("Container %s missing memory limit", [container.name])
}
security.rego: |
package kubeasy.challenge
violation[{"msg": msg}] {
input.spec.securityContext.runAsNonRoot != true
msg := "Pod must run as non-root user"
}DynamicValidation
Validates runtime behavior of resources using built-in checks.
API Version
apiVersion: challenge.kubeasy.dev/v1alpha1
kind: DynamicValidationSpec
| Field | Type | Required | Description |
|---|---|---|---|
target | TargetRef | Yes | Selects resources to validate (same as StaticValidation) |
checks | []DynamicCheck | Yes | List of checks to perform |
DynamicCheck
Each check has a kind field and type-specific configuration:
checks:
- kind: logs | status | rbac
logCheck: {...} # Only for kind: logs
statusCheck: {...} # Only for kind: status
rbacCheck: {...} # Only for kind: rbacCheck Types
1. logs - Log Content Validation
Searches for expected strings in pod logs.
Works only with: Pod resources
Fields:
| Field | Type | Required | Description |
|---|---|---|---|
expectedString | string | Yes | String to search for in logs |
container | string | No | Container name (defaults to first container) |
sinceSeconds | int64 | No | Only check logs from last N seconds |
Example:
apiVersion: challenge.kubeasy.dev/v1alpha1
kind: DynamicValidation
metadata:
name: pod-logs-check
namespace: my-challenge
spec:
target:
apiVersion: v1
kind: Pod
labelSelector:
matchLabels:
app: web-app
checks:
- kind: logs
logCheck:
expectedString: "Server started successfully"
container: nginx
sinceSeconds: 3002. status - Status Condition Validation
Checks resource status conditions or status fields.
Works with: Any resource with .status.conditions or .status fields
Fields:
| Field | Type | Required | Description |
|---|---|---|---|
condition | string | Yes | Condition name to check (e.g., Ready, Complete) |
expectedStatus | string | Yes | Expected status value (e.g., True, true) |
timeoutSeconds | int64 | No | Maximum time to wait for condition |
Behavior:
- First checks
.status.conditions[]array for matching condition - If no conditions array, checks
.status.<condition>field directly - Case-insensitive for status field names
Example - Pod readiness:
apiVersion: challenge.kubeasy.dev/v1alpha1
kind: DynamicValidation
metadata:
name: pod-ready-check
namespace: my-challenge
spec:
target:
apiVersion: v1
kind: Pod
labelSelector:
matchLabels:
app: web-app
checks:
- kind: status
statusCheck:
condition: "Ready"
expectedStatus: "True"
timeoutSeconds: 60Example - Job completion:
apiVersion: challenge.kubeasy.dev/v1alpha1
kind: DynamicValidation
metadata:
name: job-completion-check
namespace: my-challenge
spec:
target:
apiVersion: batch/v1
kind: Job
name: batch-job
checks:
- kind: status
statusCheck:
condition: "Complete"
expectedStatus: "True"3. rbac - RBAC Permission Validation
Verifies ServiceAccount has required permissions using SubjectAccessReview.
Works with: Pod resources (validates the pod's ServiceAccount)
Fields:
| Field | Type | Required | Description |
|---|---|---|---|
serviceAccountName | string | Yes | ServiceAccount to validate |
resourceAttributes | []ResourceAttribute | Yes | Permissions to verify |
ResourceAttribute fields:
| Field | Type | Required | Description |
|---|---|---|---|
verb | string | Yes | Permission verb (get, list, watch, create, update, patch, delete) |
group | string | No | API group (empty string for core API) |
resource | string | Yes | Resource type (pods, configmaps, secrets, etc.) |
name | string | No | Specific resource name (optional) |
Example:
apiVersion: challenge.kubeasy.dev/v1alpha1
kind: DynamicValidation
metadata:
name: rbac-permissions-check
namespace: my-challenge
spec:
target:
apiVersion: v1
kind: Pod
labelSelector:
matchLabels:
app: web-app
checks:
- kind: rbac
rbacCheck:
serviceAccountName: web-app-sa
resourceAttributes:
- verb: get
resource: configmaps
- verb: list
resource: pods
- verb: get
resource: secrets
name: app-secretsDynamicValidation Status
status:
allPassed: boolean # true if all checks passed
lastChecked: timestamp # Last validation time
error: string # Error message if validation failed
resources: # Per-resource results
- target:
apiVersion: string
kind: string
name: string
checkResults:
- kind: string # Check type (logs, status, rbac)
status: string # "Pass" or "Fail"
message: string # Check result detailsExample status:
status:
allPassed: true
lastChecked: "2025-10-23T10:30:00Z"
resources:
- target:
apiVersion: v1
kind: Pod
name: web-app-abc123
checkResults:
- kind: logs
status: Pass
message: 'Found expected string "Server started successfully" in logs'
- kind: status
status: Pass
message: 'Condition "Ready" has status "True"'
- kind: rbac
status: Pass
message: "All RBAC permissions verified"Complete Challenge Example
Here's a realistic challenge validation setup:
---
# Static validation: Ensure pods have resource limits
apiVersion: challenge.kubeasy.dev/v1alpha1
kind: StaticValidation
metadata:
name: resource-validation
namespace: my-challenge
spec:
target:
apiVersion: v1
kind: Pod
labelSelector:
matchLabels:
app: web-app
rulesConfigMap:
name: resource-rules
---
# ConfigMap with Rego rules
apiVersion: v1
kind: ConfigMap
metadata:
name: resource-rules
namespace: my-challenge
data:
limits.rego: |
package kubeasy.challenge
violation[{"msg": msg}] {
container := input.spec.containers[_]
not container.resources.limits
msg := sprintf("Container %s must have resource limits", [container.name])
}
---
# Dynamic validation: Check runtime behavior
apiVersion: challenge.kubeasy.dev/v1alpha1
kind: DynamicValidation
metadata:
name: runtime-validation
namespace: my-challenge
spec:
target:
apiVersion: v1
kind: Pod
labelSelector:
matchLabels:
app: web-app
checks:
# Check pod is ready
- kind: status
statusCheck:
condition: "Ready"
expectedStatus: "True"
# Check logs show success
- kind: logs
logCheck:
expectedString: "Application started"
# Verify RBAC permissions
- kind: rbac
rbacCheck:
serviceAccountName: web-app-sa
resourceAttributes:
- verb: get
resource: configmaps
- verb: list
resource: secretsController Behavior
Reconciliation
Both controllers reconcile every 30 seconds:
- Re-evaluates all checks
- Updates status with latest results
- Continues until deleted
Target Resolution
When multiple resources match the target:
- Each resource is validated independently
- Status shows results per resource
allPassedis true only if ALL resources pass ALL checks
Error Handling
If validation encounters errors:
status.errorcontains the error messagestatus.allPassedis set to false- Individual check/rule results may show partial success
Writing Rego Rules
Rule Structure
All Rego rules must:
- Be in the
kubeasy.challengepackage - Define a
violationrule - Return objects with
msgfield
package kubeasy.challenge
# Rule name is derived from the violation condition
violation[{"msg": msg}] {
# Your validation logic
# Use 'input' to access the resource being validated
condition_that_should_fail
msg := "Error message explaining the violation"
}Accessing Resource Fields
The input object contains the full resource:
# Access spec fields
input.spec.replicas
input.spec.containers[_].image
# Access metadata
input.metadata.labels["app"]
input.metadata.name
# Access status (if available)
input.status.phaseMultiple Violations
One rule can return multiple violations:
violation[{"msg": msg}] {
container := input.spec.containers[_]
not container.resources.limits.cpu
msg := sprintf("Container %s missing CPU limit", [container.name])
}
violation[{"msg": msg}] {
container := input.spec.containers[_]
not container.resources.limits.memory
msg := sprintf("Container %s missing memory limit", [container.name])
}Example Rules
Require labels:
package kubeasy.challenge
violation[{"msg": msg}] {
not input.metadata.labels.app
msg := "Pod must have 'app' label"
}
violation[{"msg": msg}] {
not input.metadata.labels.version
msg := "Pod must have 'version' label"
}Security context validation:
package kubeasy.challenge
violation[{"msg": msg}] {
container := input.spec.containers[_]
container.securityContext.privileged == true
msg := sprintf("Container %s cannot run as privileged", [container.name])
}
violation[{"msg": msg}] {
not input.spec.securityContext.runAsNonRoot
msg := "Pod must run as non-root"
}Best Practices
StaticValidation
- Keep rules focused: One ConfigMap per validation concern
- Clear messages: Include resource names and specific fields in violation messages
- Test rules: Verify rules work with valid and invalid resources
DynamicValidation
-
Use appropriate checks:
logsfor application outputstatusfor Kubernetes conditionsrbacfor permission verification
-
Set timeouts: Use
timeoutSecondsfor checks that may take time -
Container names: Specify
containerin log checks if pods have multiple containers
General
- Namespace scope: Both CRDs are namespaced - they only validate resources in the same namespace
- Label selectors: Use consistent labels across manifests and validations
- Status monitoring: Check
.status.allPassedto determine if challenge is solved
Troubleshooting
View validation status
kubectl get staticvalidation -n <namespace>
kubectl get dynamicvalidation -n <namespace>Detailed status
kubectl get staticvalidation <name> -n <namespace> -o yaml
kubectl get dynamicvalidation <name> -n <namespace> -o yamlCheck operator logs
kubectl logs -n kubeasy-system -l app=challenge-operatorTest Rego rules
Use the opa CLI to test rules locally:
opa eval -d rules.rego -i resource.json 'data.kubeasy.challenge.violation'Related Resources
- Validation Rules Guide - Practical examples
- Testing Challenges - Testing validation locally
- Challenge Operator GitHub - Source code
- OPA Documentation - Learn Rego