reconciler

package
v0.1.7 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 16, 2026 License: Apache-2.0 Imports: 49 Imported by: 0

README

pkg/reconciler

The reconciler package is the execution engine of every Orkestra operator. It takes a CR event from the Kubernetes informer and turns it into a sequence of API calls — creating, updating, or deleting child resources according to the Katalog's declarative configuration.

What lives here

File Role
generic.go GenericReconciler[T] — the single reconciler used by all CRDs
generic_autoscale.go AutoscaleTarget, AutoscalerRunner, ResyncLoopStarter, QueueInjector, QueueDepthReporter implementations
autoscaler.go Autoscaler — condition evaluation loop; applies and restores overrides
autoscale_semaphore.go ResizableSemaphore — O(1) runtime concurrency resize without stopping goroutines
autoscale_metrics.go AutoMetrics — atomic runtime metrics (queue depth, P95 latency, error rate) read by the autoscaler
run_template_reconcile.go Declarative pipeline: normalize → resolver → onCreate → onReconcile → providers
normalize.go applyNormalize — in-memory spec normalization before mutation/validation
run_*.go Per-resource-type runners (deployments, services, secrets, cronjobs, …)
run_foreach.go forEach: expansion — expands declarations into N copies before runners see them
conditions.go EvaluateWhen wrappers and helpers
children.go Reads owned child resources into the resolver's .children.* data
run_namespace_guard.go CheckNamespace — allowed/restricted namespace enforcement

Developer documentation

Full step-by-step documentation is in docs/.

I want to… Go to
Understand the full reconcile pipeline 01 — Architecture
Understand what every run_*.go must implement 02 — The run_*.go Contract
Understand the registry layer 03 — Registry Layer
Debug condition / activeNames issues 04 — Conditions
Understand forEach: expansion 05 — forEach
Add normalize: to a Katalog 06 — normalize
Add a new resource type end-to-end 07 — Adding a Resource

For the autoscaler design and the operatorBox.autoscale: declaration, see docs/runtime-manual/concepts/operator-autoscaler.md.

Documentation

Overview

pkg/reconciler/children.go

pkg/reconciler/conditions.go

pkg/reconciler/generic.go

pkg/reconciler/generic_autoscale.go

AutoscaleTarget implementation for GenericReconciler.

GenericReconciler implements the three AutoscaleTarget methods so it can be passed directly to NewAutoscaler. The autoscaler calls these methods; user code should not call them directly.

ResizeWorkers — fully wired via ResizableSemaphore. SetQueueDepthLimit — enforced atomically in Workqueue.Enqueue. SetResyncInterval — drives an independent resync goroutine that re-enqueues

all cached objects at the declared interval. 0 = idle (informer handles
baseline resync). The goroutine is started once by startCRDWorkers via the
ResyncLoopStarter interface; SetResyncInterval adjusts its rate at runtime.

pkg/reconciler/generic_namespace.go

Namespace guard wiring for GenericReconciler.

RestrictedNamespaces and AllowedNamespaces live on orktypes.CRDEntry directly — not on OperatorBoxConfig. GenericReconciler stores the full CRDEntry as r.crd, so the guard reads r.crd.RestrictedNamespaces and r.crd.AllowedNamespaces directly.

namespaceGuardFunc is called once per runResourceGroup invocation and returns a closure. run_*.go files call it after resolving the target namespace and before condition evaluation.

Fast path: returns nil when CRD has no restrictions — all run_*.go guard with "if guard != nil" so nil means zero allocation, zero check.

pkg/reconciler/generic_registry.go

KatalogRegistry — the interface GenericReconciler needs from the Kordinator registry to perform zero-API-call cross-CRD observation.

Why a local interface instead of importing pkg/kordinator directly:

pkg/reconciler → pkg/kordinator would create an import cycle.
pkg/kordinator already imports pkg/reconciler (for domain.Reconciler).
A local interface breaks the cycle — Go's implicit interface satisfaction
means *kordinator.ResourceKatalog satisfies this automatically.

pkg/reconciler/kube_reader_adapter.go

KubeReader adapter — bridges kubeclient.Kubeclient to the orktypes.KubeReader interface used by provider libraries.

Providers receive KubeReader not *kubeclient.Kubeclient because:

  • The interface is narrow: GetSecret and GetConfigMap only — no write access
  • Providers must not write Kubernetes resources (Orkestra owns cluster state)
  • A narrow interface is easier to mock in provider tests
  • The kubeclient package is internal; providers should not depend on it

pkg/reconciler/normalize.go

applyNormalize — canonical spec transformation.

Called as the first step of reconcileImpl, before mutation and validation. Returns a deep copy of the CR with normalize.spec fields replaced by their rendered values. The informer cache object is never modified.

Pipeline position:

informer cache → DeepCopy → applyNormalize → mutation → validation → runTemplateReconcile

After applyNormalize, every downstream step sees the normalized spec. The raw CR in etcd is unchanged — normalize is purely an in-memory operation.

Example — CronJob accepting both string and map schedule:

normalize:
  spec:
    schedule: >
      {{ if eq (typeOf .spec.schedule) "map" }}
        {{ cronExpr .spec.schedule.minute .spec.schedule.hour .spec.schedule.dayOfMonth .spec.schedule.month .spec.schedule.dayOfWeek }}
      {{ else }}
        {{ .spec.schedule }}
      {{ end }}

After normalize, .spec.schedule is always a cron string. onReconcile templates use {{ .spec.schedule }} directly — no branching needed.

pkg/reconciler/run_configmaps.go

pkg/reconciler/run_cronjobs.go

pkg/reconciler/run_cross.go

Cross-CRD HTTP fallback — fetches a CR detail from another Orkestra instance.

The primary path (informer cache, zero API calls) lives in run_template_reconcile.go in ReadCrossFromDeclarations. That function calls fetchCrossViaHTTP when the informer is unavailable (cross-binary, cross-cluster).

The full informer-cache path requires threading the Kordinator registry into GenericReconciler. ReadCrossFromInformer is called instead of fetchCrossViaHTTP for same-binary CRDs.

pkg/reconciler/run_deployments.go

pkg/reconciler/run_docker.go

Docker build/push dispatch — runs before resource groups in runTemplateReconcile.

The Docker hook performs a minimal docker build and optional push of the declared image. Results are injected into the resolver context via resolver.WithDocker() so subsequent template expressions and when: conditions can reference them.

Call sequence in runTemplateReconcile:

  1. resolver = NewResolver(ctx, obj)
  2. resolver = resolver.WithCross(ReadCross(...)) ← cross-CRD first
  3. resolver, err = runGit(ctx, ...) ← Git second
  4. resolver, err = runExternal(ctx, ...) ← HTTP third
  5. resolver, err = runDocker(ctx, ...) ← Docker fourth
  6. runDeployments, runServices, ... etc.

Results in template context:

.docker.image           → built image reference
.docker.buildSucceeded  → "true" or "false"
.docker.error           → error message (if any)
.docker.called          → "true" if hook executed

When: conditions can gate on these:

when:
  - field: docker.buildSucceeded
    equals: "true"

pkg/reconciler/run_docker_helper.go

pkg/reconciler/run_external.go

External HTTP call dispatch — runs before resource groups in runTemplateReconcile.

Calls are executed sequentially in declaration order. Results are injected into the resolver context via resolver.WithExternal() so subsequent template expressions and when: conditions can reference them.

Call sequence in runTemplateReconcile:

  1. resolver = NewResolver(ctx, obj)
  2. resolver = resolver.WithCross(ReadCross(...)) ← cross-CRD first
  3. resolver, err = runExternal(ctx, resolver, ...) ← HTTP calls second
  4. runDeployments, runServices, ... etc. ← resources use results

Results in template context:

.external.<name>.status   → HTTP status code ("200", "404", "503")
.external.<name>.body     → response body (first 4096 bytes)
.external.<name>.error    → error message on failure
.external.<name>.called   → "true" if the call was made

When: conditions on deployments can gate on these:

when:
  - field: external.health-check.status
    equals: "200"

pkg/reconciler/run_foreach.go

forEach expansion — expands template sources with a forEach declaration into N resolved sources, one per element in the list field.

forEach works on every resource type. The expansion happens before the resource-specific run_*.go function is called — each run_*.go function receives an already-expanded slice and is unaware of forEach.

Expansion sequence in runTemplateReconcile:

deployments := expandForEachDeployments(resolver, t.Deployments)
runDeployments(ctx, kube, resolver, obj, deployments, update)

YAML:

onReconcile:
  deployments:
    - name: "{{ .metadata.name }}-{{ .item }}"
      image: "{{ .spec.image }}"
      forEach:
        field: spec.regions
        as: item

For CR with spec.regions: ["us-east-1", "eu-west-1"]: Produces two DeploymentTemplateSources:

{Name: "my-app-us-east-1", Image: "nginx:latest", ...}
{Name: "my-app-eu-west-1", Image: "nginx:latest", ...}

The expansion resolves ALL template expressions immediately — the returned slice contains static (non-template) values ready for the registry functions.

when: and anyOf: on forEach sources are evaluated per-item — each expanded source may pass or fail conditions independently.

pkg/reconciler/run_git.go

Git hook dispatch — runs before external calls and resource groups in runTemplateReconcile.

The Git hook performs a minimal clone/fetch of the declared repository, computes the current commit hash, detects whether the commit changed since the previous reconcile, and injects results into the resolver context via resolver.WithGit().

Call sequence in runTemplateReconcile:

  1. resolver = NewResolver(ctx, obj)
  2. resolver = resolver.WithCross(ReadCross(...)) ← cross-CRD first
  3. resolver, err = runGit(ctx, gvk, resolver, ...) ← Git second
  4. resolver, err = runExternal(ctx, ...) ← HTTP third
  5. resolver, err = runDocker(ctx, ...) ← Docker fourth
  6. runDeployments, runServices, ... etc.

Results in template context:

.git.commit     → HEAD commit hash
.git.changed    → "true" if commit changed since last reconcile
.git.path       → working directory path
.git.error      → error message if operation failed
.git.called     → "true" if Git hook executed
.git.succeeded  → "true" if Git operation succeeded

When: conditions can gate on these:

when:
  - field: git.changed
    equals: "true"

Change detection is annotation-based — the last-seen commit is stored in:

orkestra.konductor.io/last-commit

This annotation survives pod restarts, preventing spurious rebuilds.

pkg/reconciler/run_jobs.go

pkg/reconciler/run_mutation.go

pkg/reconciler/run_namespace_guard.go

pkg/reconciler/run_providers.go

Provider dispatch — the engine that connects Katalog declarations to registered provider libraries.

This file is called from runTemplateReconcile after all Kubernetes resource groups (deployments, services, jobs, etc.) have been reconciled. It:

  1. Skips immediately if no provider blocks are declared or no providers registered
  2. Resolves template expressions in every declaration field
  3. Evaluates when: conditions — drops declarations that do not pass
  4. Calls provider.Reconcile(ctx, req) for each active block

On CR deletion (finalizer path), runProviderDelete is called instead. It calls provider.Delete for all declarations — no condition filtering.

Integration in runTemplateReconcile (after all Kubernetes resource groups):

if err := runProviders(ctx, obj, resolver, rc.ProviderBlocks, providerRegistry, kubeReader); err != nil {
    return fmt.Errorf("providers: %w", err)
}

Integration in the finalizer handler:

if err := runProviderDelete(ctx, obj, resolver, rc.ProviderBlocks, providerRegistry, kubeReader); err != nil {
    return fmt.Errorf("provider cleanup: %w", err)
}

pkg/reconciler/run_secrets_tls.go

TLS certificate generation and secret rotation for Orkestra secrets.

Extends the once: true pattern with:

  • rotateAfter: <duration> — time-based rotation using a generated-at annotation
  • tls: {...} — self-signed CA + signed certificate generation

The generated Secret is annotated with:

orkestra.konductor.io/generated-at: "2026-04-06T08:00:00Z"
orkestra.konductor.io/rotate-after: "90d"

On each reconcile, secretNeedsRotation reads these annotations and returns true when the threshold is crossed. The caller deletes and recreates.

TLS generation produces a Secret of type kubernetes.io/tls with:

tls.crt — PEM-encoded signed certificate
tls.key — PEM-encoded private key (for the server)
ca.crt  — PEM-encoded self-signed CA certificate

The CA is unique per Secret — appropriate for self-signed operator webhooks. For shared CAs across multiple services, store the CA in a separate Secret and reference it (planned: ca.secretRef field).

pkg/reconciler/run_secrets.go

Adds to the previous version:

  • orktypes.EvaluateWhen instead of evaluateConditions (fixes anyOf: being ignored)
  • rotateAfter: <duration> support — time-based credential rotation
  • tls: {...} support — self-signed CA + signed certificate generation

Execution order per secret declaration:

  1. EvaluateWhen(when:, anyOf:) — skip if conditions fail
  2. Once/rotation check a. once: true, rotateAfter set — check annotation, delete if expired b. once: true, no rotateAfter — skip if exists c. once: false — standard create/update
  3. Resolve template expressions — randomAlphanumeric evaluated here
  4. TLS generation (if tls: is set) — generate CA + cert, skip data resolution
  5. Create/Update/CopyToNamespaces
  6. Annotate with generated-at — only when rotateAfter is set

pkg/reconciler/run_secrets_once.go

once: true on secrets — idempotent random secret generation.

The Kubernetes reconcile loop is called on every resync. A secret with random data (password, API key) would change every 30 seconds without the once: guard. once: true makes the create step check for existence first — if the Secret already exists, skip template evaluation entirely.

This is the check-before-generate pattern used by cert-manager and every other tool that generates credentials idempotently.

YAML:

secrets:
  - name: "{{ .metadata.name }}-credentials"
    once: true
    data:
      password: "{{ randomAlphanumeric 32 }}"
      apiKey:   "{{ randomHex 16 }}"
      jwtSecret: "{{ randomBase64 32 }}"

Behaviour:

  • First reconcile: Secret does not exist → evaluate templates → create
  • Every subsequent reconcile: Secret exists → skip completely (no-op)
  • CR deleted: Secret deleted via owner reference (garbage collection)

once: true is IGNORED when update=true (onReconcile with reconcile: true). A secret in onReconcile should use static or deterministic template values. Combining once: true with reconcile: true logs a warning and skips the once: guard — the caller opted into continuous reconciliation.

once: false (default): standard create/update behavior, unchanged.

pkg/reconciler/run_serviceaccounts.go

pkg/reconciler/run_services.go

pkg/reconciler/run_status.go

Status management — three layers:

Layer 1 — Automatic Ready condition.

Written after every reconcile regardless of Katalog declarations.
reason: ReconcileSucceeded on success, ReconcileError on failure.
No Katalog declaration required. Requires subresources.status in the CRD.

Layer 2 — Declarative status fields.

Written from status.fields declarations in the Katalog.
Template expressions resolved against the live CR object map.
Optional when: conditions gate individual field writes — this is what
makes declarative state machines possible.
Written only on successful reconcile.

Layer 3 — Child resource propagation.

Children are read after runTemplateReconcile and injected into the
resolver context via WithChildren. The returned resolver is used for
all subsequent status resolution.
Status field expressions reference children as:
  {{ .children.deployment.status.readyReplicas }}
  {{ .children.cronjob.status.lastScheduleTime }}
Children are keyed by lowercase Kind — matches kubectl conventions.

pkg/reconciler/run_template_reconcile.go

Execution order (each step can reference all previous steps):

  1. Recieve NewResolver → .spec.*, .status.*, .metadata.*
  2. r.readCross → .cross.<kind>.status.* (informer cache, zero API calls)
  3. runExternal → .external.<n>.status, .body (HTTP calls)
  4. forEach expand → N sources from N-element list fields
  5. onCreate groups → deployments, services, secrets, configmaps, ...
  6. onReconcile groups
  7. runProviders → aws:, mongodb:, ... (external infra)

pkg/reconciler/run_validation.go

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func AddFinalizer

func AddFinalizer(o domain.Object, finalizer string) (updated bool)

func ContainsFinalizer

func ContainsFinalizer(o domain.Object, finalizer string) bool

func ReadChildren

func ReadChildren(
	ctx context.Context,
	kube *kubeclient.Kubeclient,
	obj domain.Object,
	resolver *orktmpl.Resolver,
	rc orktypes.OperatorBoxConfig,
) map[string]interface{}

ReadChildren reads all child resources declared in the Katalog's onCreate templates and returns a structured map for use in status field expressions.

The returned map is injected into the template resolver under the "children" key. Status field expressions can then reference child resource state:

# Singular — the first/only resource of this type
{{ .children.deployment.status.readyReplicas }}
{{ .children.service.status.loadBalancer.ingress }}

# Plural — all resources of this type, by name
{{ (index .children.deployments "my-site-api").status.readyReplicas }}

ReadChildren is called after runTemplateReconcile — child resources exist at this point. Missing status fields resolve to "" (missingkey=zero), which is correct eventual consistency behaviour for newly created resources.

API cost: one GET per child resource type × count. For the common case (one Deployment + one Service), this is two GETs.

This function never returns an error that should fail the reconcile. Read failures are logged and the child is omitted from the map. Status patching proceeds with whatever children were successfully read.

func ReadCrossFromInformer

func ReadCrossFromInformer(
	indexer interface {
		GetByKey(key string) (interface{}, bool, error)
	},
	key string,
) map[string]interface{}

ReadCrossFromInformer reads cross-CRD data from an informer cache. Zero API server calls — pure in-memory map lookup.

indexer is the GetIndexer() of a SharedIndexInformer for the target CRD. key is "namespace/name" for namespaced CRDs or "name" for cluster-scoped.

Returns a map with the same shape as fetchCrossViaHTTP — callers are agnostic to which path was used.

func RemoveFinalizer

func RemoveFinalizer(o domain.Object, finalizer string) (updated bool)

Types

type AutoscalerRunner

type AutoscalerRunner interface {
	RunAutoscaler(ctx context.Context)
}

AutoscalerRunner is an optional interface implemented by reconcilers that carry an embedded autoscaler. DependencyKordinator checks for this after calling ReconcilerFactory() and launches the goroutine if present.

type DockerBuildResult

type DockerBuildResult struct {
	Error string
}

type DockerPushResult

type DockerPushResult struct {
	Error string
}

type GenericReconciler

type GenericReconciler[T domain.Object] struct {
	// contains filtered or unexported fields
}

GenericReconciler manages the full lifecycle of one CRD.

This file is a pure dispatcher. It owns:

  • Context enrichment
  • Cache reads
  • Deletion routing
  • Finalizer/Annotation/Labele management
  • Template interpretation
  • Reconcile priority (Go hooks → declarative templates → no-op)
  • Event firing and logging

Resource-specific logic lives in separate files:

run_deployments.go    — Deployment create/update
run_services.go       — Service create/update
run_secrets.go        — Secret create/copy/sync
run_configmaps.go     — ConfigMap create/copy/sync
run_serviceaccounts.go — ServiceAccount create
run_jobs.go           — Job create (onDelete cleanup)
run_cronjobs.go       — CronJob create/update

Adding a new resource type:

  1. Add a file run_<resource>.go with a runXxx() function
  2. Call it from runTemplateReconcile() and/or runTemplateOnDelete()
  3. Add the field to orktypes.HookTemplates That is all — generic.go does not change.

func NewGenericReconciler

func NewGenericReconciler[T domain.Object](
	crd orktypes.CRDEntry,
	informer cache.SharedIndexInformer,
	ev *event.Event,
	kube *kubeclient.Kubeclient,
	anyHooks domain.AnyReconcileHooks,
	newObj func() T,
	katalogRegistry *kordinator.ResourceKatalog,
	providerRegistry orktypes.ProviderRegistry,
	providerStats providerStatsRecorder,
) *GenericReconciler[T]

func (*GenericReconciler[T]) Reconcile

func (r *GenericReconciler[T]) Reconcile(ctx context.Context, key string) error

Reconcile dispatches to the correct reconcile implementation. Order:

  1. Conditional provisioning (when blocks) — handled by runTemplateReconcile
  2. Go hooks → Declarative templates → No-op (through reconcileImpl())

The semaphore gates concurrent execution — when an autoscaler is active it can reduce effective concurrency below the goroutine count without stopping goroutines.

func (*GenericReconciler[T]) ReportQueueDepth

func (r *GenericReconciler[T]) ReportQueueDepth(depth int64)

ReportQueueDepth updates the live queue depth metric read by the autoscaler. Called by the worker loop after each item is processed.

func (*GenericReconciler[T]) ResizeWorkers

func (r *GenericReconciler[T]) ResizeWorkers(n int)

ResizeWorkers adjusts the semaphore capacity, changing how many goroutines may be inside Reconcile simultaneously. In-flight reconciles are never interrupted — they complete normally before the new limit takes effect.

func (*GenericReconciler[T]) RunAutoscaler

func (r *GenericReconciler[T]) RunAutoscaler(ctx context.Context)

RunAutoscaler starts the autoscale evaluation loop. Blocks until ctx is cancelled. Called by DependencyKordinator in a dedicated goroutine. No-op when no autoscale spec is declared.

func (*GenericReconciler[T]) SetQueue

func (r *GenericReconciler[T]) SetQueue(wq *orkqueue.Workqueue)

SetQueue injects the per-CRD workqueue. Called by startCRDWorkers after ReconcilerFactory() so both SetQueueDepthLimit and the resync goroutine have a reference to the right queue.

func (*GenericReconciler[T]) SetQueueDepthLimit

func (r *GenericReconciler[T]) SetQueueDepthLimit(n int)

SetQueueDepthLimit updates the workqueue's depth limit. New enqueue calls that would push the queue beyond this limit are dropped with a warning. 0 means unlimited (the default). Safe to call at any time.

func (*GenericReconciler[T]) SetResyncInterval

func (r *GenericReconciler[T]) SetResyncInterval(d time.Duration)

SetResyncInterval sets the resync goroutine's fire rate. 0 idles the goroutine — the informer's built-in resync handles the baseline cadence. Non-zero values trigger an additional re-enqueue of all cached objects at that interval, independently of the informer's resync period.

func (*GenericReconciler[T]) StartResyncLoop

func (r *GenericReconciler[T]) StartResyncLoop(ctx context.Context)

StartResyncLoop launches the adjustable resync goroutine. Called once by startCRDWorkers when autoscale: is declared on the operatorbox.

type GitOperationResult

type GitOperationResult struct {
	Operation string // "clone" | "fetch"
	Commit    string
	Changed   string // "true" | "false"
	Path      string
	Error     string
}

type KatalogRegistry

type KatalogRegistry interface {
	// GetInformerByName returns the SharedIndexInformer for a CRD by its
	// lowercase name (the map key in spec.crds).
	// Returns nil, false when the CRD is not registered.
	GetInformerByName(name string) (cache.SharedIndexInformer, bool)
}

KatalogRegistry is the interface GenericReconciler uses to look up sibling CRD informers for cross-CRD observation. Satisfied by *kordinator.ResourceKatalog without importing that package.

type MutationChange

type MutationChange struct {
	Field    string
	OldValue string
	NewValue interface{}
	Type     string // "default" or "override"
}

MutationChange describes one field mutation that was applied.

type MutationResult

type MutationResult struct {
	Applied int
	Changes []MutationChange
}

MutationResult holds the outcome of applying mutation rules.

type NamespaceGuardResult

type NamespaceGuardResult struct {
	Allowed   bool
	Namespace string
	Pattern   string // matched restricted or allowed pattern (for logs)
	Reason    string // "restricted", "not-allowed", or ""
}

NamespaceGuardResult holds the outcome of a namespace restriction check.

func CheckNamespace

func CheckNamespace(
	ctx context.Context,
	obj domain.Object,
	targetNamespace string,
	restricted orktypes.RestrictedNamespaces,
	allowed orktypes.AllowedNamespaces,
	crdName string,
) *NamespaceGuardResult

CheckNamespace determines whether a target namespace is permitted for child resource creation. It evaluates both RestrictedNamespaces and AllowedNamespaces with the following precedence:

  1. RestrictedNamespaces — deny-list (always wins)
  2. AllowedNamespaces — allow-list (optional; empty = allow all)

Called before onCreate, onReconcile, onDelete, and before registry dispatch.

func (*NamespaceGuardResult) EventMessage

func (r *NamespaceGuardResult) EventMessage(resourceKind, resourceName string) string

EventMessage returns the Kubernetes event message for a blocked namespace.

type QueueDepthReporter

type QueueDepthReporter interface {
	ReportQueueDepth(depth int64)
}

QueueDepthReporter is an optional interface reconcilers may implement to receive live queue depth updates from the worker loop.

type QueueInjector

type QueueInjector interface {
	SetQueue(wq *orkqueue.Workqueue)
}

QueueInjector is an optional interface for injecting the per-CRD workqueue after the reconciler is constructed. Called by startCRDWorkers.

type ResyncLoopStarter

type ResyncLoopStarter interface {
	StartResyncLoop(ctx context.Context)
}

ResyncLoopStarter is an optional interface for reconcilers that support an adjustable resync goroutine. DependencyKordinator starts it once per CRD.

type TLSBundle

type TLSBundle struct {
	CertPEM   []byte // tls.crt — signed server certificate, PEM
	KeyPEM    []byte // tls.key — server private key, PEM
	CACertPEM []byte // ca.crt  — CA certificate, PEM (for caBundle in webhooks)
}

TLSBundle holds the generated certificate material.

func GenerateTLSBundle

func GenerateTLSBundle(commonName string, dnsNames []string, validFor string) (*TLSBundle, error)

GenerateTLSBundle generates a self-signed CA and a server certificate signed by it. The server certificate has the given common name and DNS SANs. validFor is the certificate validity duration ("1y", "90d", etc.).

Returns a TLSBundle containing PEM-encoded cert, key, and CA cert. All three are stored in the Secret so consumers have what they need:

  • tls.crt + tls.key for the server
  • ca.crt for clients that need to verify the server cert

type ValidationResult

type ValidationResult struct {
	// Passed — true when all rules passed
	Passed bool

	// Deny
	Deny bool

	// Violations — one entry per failed rule
	// Warning
	Warnings []ValidationViolation // action: warn violations

	Violations []ValidationViolation // action: deny violations
}

ValidationResult holds the outcome of running all validation rules for one reconcile.

func (*ValidationResult) Blocked

func (r *ValidationResult) Blocked() bool

Blocked returns true if reconciliation must stop. Deny takes precedence; warnings alone do NOT block.

func (*ValidationResult) DenialError

func (r *ValidationResult) DenialError() error

DenialError returns a proper error describing the denial reason.

func (*ValidationResult) DenialMessage

func (r *ValidationResult) DenialMessage() string

DenialMessage returns a human‑readable summary of the denial cause.

func (*ValidationResult) Denied

func (r *ValidationResult) Denied() bool

Denied returns true if validation rules explicitly blocked reconciliation.

func (*ValidationResult) Error

func (r *ValidationResult) Error() error

Error returns a combined error message for all violations.

func (*ValidationResult) HasViolations

func (r *ValidationResult) HasViolations() bool

HasViolations returns true if any rules failed.

func (*ValidationResult) HasWarnings

func (r *ValidationResult) HasWarnings() bool

HasWarnings returns true if any advisory warnings were produced.

func (*ValidationResult) Log

func (r *ValidationResult) Log(crd, name, ns string)

Log helper for debugging

func (*ValidationResult) ViolationSummary

func (r *ValidationResult) ViolationSummary() string

ViolationSummary returns a semicolon‑joined list of violations.

func (*ValidationResult) WarningSummary

func (r *ValidationResult) WarningSummary() string

WarningSummary returns a semicolon‑joined list of warnings.

type ValidationViolation

type ValidationViolation struct {
	Field   string
	Rule    string
	Value   string                    // the actual CR field value that failed
	Message string                    // the user-defined message from the Katalog
	Action  orktypes.ValidationAction // 'warn' or 'deny' as defined by user
}

ValidationViolation describes one failed validation rule.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL