chainsaw

package
v0.20.0-rc1 Latest Latest
Warning

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

Go to latest
Published: Aug 24, 2026 License: Apache-2.0 Imports: 29 Imported by: 0

Documentation

Overview

Package chainsaw executes Chainsaw-style assertions against a live Kubernetes cluster, in-process. It supports two modes:

  • Raw K8s resource YAML: pure field matching via the chainsaw Go library (assertRawResources → checks.Check).
  • Chainsaw Test format (apiVersion: chainsaw.kyverno.io/v1alpha1): walks Spec.Steps[].Try[] and dispatches the assert / error operations to the same checks.Check engine (runChainsawTestInProcess in inprocess.go).

The earlier `runChainsawBinary` path that exec'd /usr/local/bin/chainsaw was removed in #1236; the read-only allowlist (pkg/chainsaw/allowlist.go) restricts registry- declared content to assert/error only, which is exactly the subset the in-process executor implements. No external binary is shipped or invoked.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func IsChainsawTest

func IsChainsawTest(raw string) bool

IsChainsawTest returns true if any document in the YAML stream is a Chainsaw Test (apiVersion group chainsaw.kyverno.io, kind Test). Exported so the deployment validator can partition Test-format asserts (which dispatch to the in-process executor with allowlist enforcement) from raw K8s resource YAML (which uses the Go assertion library directly). Originally added in PR #1231 to gate the binary-shipping path; retained in #1236 because the dispatch split is still useful — Test-format content runs through the read-only allowlist guard before evaluation; raw K8s YAML bypasses that guard (it has no operations to gate).

Detection parses each document's apiVersion/kind rather than scanning for substrings (#2040). The substring form failed in both directions: a quoted `kind: "Test"` was not recognized, and raw K8s YAML that merely mentioned both strings — in a comment, an annotation, or ConfigMap data — was dispatched as a Test, unmarshaling to zero steps and passing without evaluating anything.

Any document matching is enough: a stream that carries a Test must reach the allowlist guard regardless of which document it sits in. Undecodable content reports false and surfaces its parse error on the raw path, which reports document context.

func NewDynamicClientForConfig

func NewDynamicClientForConfig(restConfig *rest.Config) (dynamic.Interface, error)

NewDynamicClientForConfig builds the dynamic client the fetcher reads through, carrying the same request bound NewRESTMapperForConfig applies. Exported for callers that construct the two halves separately (the deployment validator keeps its own client so ctx.DynamicClient stays an injection seam) — going through it keeps both halves bounded alike.

func NewRESTMapperForConfig

func NewRESTMapperForConfig(restConfig *rest.Config) (meta.RESTMapper, error)

NewRESTMapperForConfig builds the discovery-backed RESTMapper the fetcher uses to resolve a GroupVersionKind to a resource and its scope. Discovery is deferred: no API call happens until the first mapping lookup.

Prefer NewClusterFetcherWithClient when the mapper is destined for a fetcher: it also wires the partial-discovery probe, which a mapper alone cannot carry.

func ValidateTestReadOnly

func ValidateTestReadOnly(component, yamlContent string) error

ValidateTestReadOnly parses chainsaw Test YAML content (possibly multi-document) and rejects any operation other than `assert` or `error`. Used at runtime to bound the blast radius of registry-declared health checks: the deployment validator Job runs under a ServiceAccount bound to cluster-admin (pkg/validator/job/rbac.go:41-67), so registry content must not be able to invoke state-changing chainsaw operations (apply, create, delete, patch, update) or side-effecting collectors (script, command, wait, sleep, podLogs, events, describe, get, proxy).

Multi-document support: a single `---`-separated stream may carry more than one Test; each is unmarshaled and validated independently. Empty documents and non-Test documents (different apiVersion/kind) are skipped. Per PR #1235 review.

Both per-step (`spec.steps[].try/catch/finally/cleanup`) and top-level (`spec.catch`) operation lists are validated.

Caller contract: invoke only on content that IsChainsawTest reports as Test format. Raw K8s YAML asserts have no operations and are unaffected.

Returns ErrCodeInvalidRequest naming the offending document index + step + operation so the operator can pinpoint the registry entry that violated the allowlist. PR #1223 will surface the same rule at lint time so violations are caught before they ever reach the validator.

Types

type ClusterFetcherOption

type ClusterFetcherOption func(*clusterFetcher)

ClusterFetcherOption configures a cluster fetcher.

func WithGroupDiscovery

func WithGroupDiscovery(d groupDiscoverer) ClusterFetcherOption

WithGroupDiscovery supplies the discovery client the fetcher consults when the RESTMapper reports no match for a kind, so a group that discovery could not enumerate is reported as ErrCodeUnavailable rather than "this cluster does not serve that kind".

Pass the SAME cached discovery client that backs the mapper. Production callers get this wiring for free from NewClusterFetcherForConfig / NewClusterFetcherWithClient.

type ComponentAssert

type ComponentAssert struct {
	// Name is the component name (e.g., "gpu-operator").
	Name string

	// AssertYAML is the raw Chainsaw assert file content.
	AssertYAML string
}

ComponentAssert holds the data needed to run assertions for one component.

type ResourceFetcher

type ResourceFetcher interface {
	// Fetch retrieves a single Kubernetes resource as an unstructured map.
	// Returns ErrCodeNotFound when the resource doesn't exist.
	Fetch(ctx context.Context, apiVersion, kind, namespace, name string) (map[string]interface{}, error)

	// List enumerates Kubernetes resources of the given kind in the
	// given namespace, optionally narrowed by labels (empty = no
	// selector). Cluster-scoped resources should pass an empty
	// namespace. Returns an empty slice (not error) when no resources
	// match — the caller distinguishes "list returned empty" from
	// "list call failed".
	//
	// Added in #1236 so the in-process Chainsaw Test executor can
	// handle assertions / error blocks that target a namespace + label
	// selector without specifying a resource name (the pod-phase /
	// container-state patterns that dominate the registry-declared
	// health checks).
	List(ctx context.Context, apiVersion, kind, namespace string, labels map[string]string) ([]map[string]interface{}, error)
}

ResourceFetcher abstracts fetching Kubernetes resources for testability.

func NewClusterFetcher

func NewClusterFetcher(client dynamic.Interface, mapper meta.RESTMapper, opts ...ClusterFetcherOption) ResourceFetcher

NewClusterFetcher creates a ResourceFetcher that queries a live Kubernetes cluster.

Without WithGroupDiscovery the fetcher cannot tell a genuine no-match apart from a kind whose API group failed discovery, and classifies a bare NoKindMatchError as ErrCodeNotFound. That is the fail-open direction for a negative assertion, so every production path builds the fetcher through NewClusterFetcherForConfig or NewClusterFetcherWithClient, which wire the probe. This constructor remains the injection seam for tests that supply a hand-built RESTMapper.

func NewClusterFetcherForConfig

func NewClusterFetcherForConfig(restConfig *rest.Config) (ResourceFetcher, error)

NewClusterFetcherForConfig builds a ResourceFetcher from a client configuration, constructing the dynamic client it reads through, the discovery-backed RESTMapper it resolves scope with, and the partial-discovery probe that keeps a no-match honest.

Callers that already hold a dynamic client (the deployment validator keeps ctx.DynamicClient as an injection seam) should use NewClusterFetcherWithClient so the mapper and probe wiring is still shared.

func NewClusterFetcherWithClient

func NewClusterFetcherWithClient(client dynamic.Interface, restConfig *rest.Config) (ResourceFetcher, error)

NewClusterFetcherWithClient builds a ResourceFetcher around a caller-supplied dynamic client, deriving the RESTMapper and its discovery probe from restConfig. Both are backed by one cached discovery client, so invalidating the mapper's cache also invalidates the probe's view.

type Result

type Result struct {
	// Component is the component name.
	Component string

	// Passed indicates whether the assertion passed.
	Passed bool

	// Output contains diagnostic detail for failures.
	Output string

	// Error contains any error from executing the assertion.
	Error error
}

Result holds the outcome of an assertion run for one component.

func Run

func Run(ctx context.Context, asserts []ComponentAssert, timeout time.Duration, fetcher ResourceFetcher) []Result

Run executes assertions for a set of components against live cluster resources. Components are run concurrently with bounded parallelism. Chainsaw Test format dispatches to the in-process executor (runChainsawTestInProcess); raw K8s resource YAML uses the Go library assertion engine (assertRawResources).

Jump to

Keyboard shortcuts

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