Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
223 changes: 223 additions & 0 deletions config/olm/template/manifests/kollect.clusterserviceversion.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,229 @@ metadata:
},
"suspend": false
}
},
{
"apiVersion": "kollect.dev/v1alpha1",
"kind": "KollectInventory",
"metadata": {
"name": "team-inventory",
"namespace": "default"
},
"spec": {
"exportMinInterval": "30s",
"snapshotSinkRefs": [
{
"name": "git-inventory",
"exportMinInterval": "1h"
}
],
"databaseSinkRefs": [
{
"name": "postgres-inventory"
}
],
"suspend": false
}
},
{
"apiVersion": "kollect.dev/v1alpha1",
"kind": "KollectSnapshotSink",
"metadata": {
"name": "git-inventory",
"namespace": "default"
},
"spec": {
"type": "git",
"endpoint": "https://github.com/platformrelay/kollect-inventory-demo.git",
"cluster": "prod-west",
"pathTemplate": "clusters/{cluster}/inventory/{namespace}/{name}.json",
"connectionTest": true,
"secretRef": {
"name": "git-push-credentials",
"namespace": "default"
},
"git": {
"branch": "main",
"pushPolicy": "Commit",
"auth": {
"type": "token"
}
}
}
},
{
"apiVersion": "kollect.dev/v1alpha1",
"kind": "KollectEventSink",
"metadata": {
"name": "nats-inventory",
"namespace": "default"
},
"spec": {
"type": "nats",
"cluster": "prod-west",
"connectionTest": true,
"secretRef": {
"name": "nats-credentials",
"namespace": "default"
},
"nats": {
"url": "nats://nats.kollect-system.svc:4222",
"subject": "inventory.events",
"stream": "kollect_events"
}
}
},
{
"apiVersion": "kollect.dev/v1alpha1",
"kind": "KollectDatabaseSink",
"metadata": {
"name": "postgres-inventory",
"namespace": "default"
},
"spec": {
"type": "postgres",
"cluster": "prod-west",
"connectionTest": true,
"provisioning": {
"mode": "ensure"
},
"postgres": {
"databaseRef": {
"name": "inventory-postgres-dsn",
"namespace": "default"
},
"schema": "public",
"table": "inventory_items"
}
}
},
{
"apiVersion": "kollect.dev/v1alpha1",
"kind": "KollectConnectionTest",
"metadata": {
"name": "postgres-inventory-probe",
"namespace": "default"
},
"spec": {
"sinkRef": {
"databaseSinkRef": "postgres-inventory"
},
"ttlSecondsAfterFinished": 300
}
},
{
"apiVersion": "kollect.dev/v1alpha1",
"kind": "KollectScope",
"metadata": {
"name": "team-ceiling",
"namespace": "default"
},
"spec": {
"allowedGVKs": [
{
"group": "apps",
"version": "v1",
"kind": "Deployment"
},
{
"group": "",
"version": "v1",
"kind": "Service"
}
],
"allowedNamespaces": [
"default"
],
"snapshotSinkRefs": [
"git-inventory"
],
"databaseSinkRefs": [
"postgres-inventory"
],
"minExportInterval": "30s"
}
},
{
"apiVersion": "kollect.dev/v1alpha1",
"kind": "KollectClusterTarget",
"metadata": {
"name": "platform-deployments"
},
"spec": {
"profileRef": {
"name": "deployment-images",
"namespace": "default"
},
"namespaceSelector": {
"matchLabels": {
"kollect.dev/tenant": "platform"
}
},
"suspend": false
}
},
{
"apiVersion": "kollect.dev/v1alpha1",
"kind": "KollectClusterInventory",
"metadata": {
"name": "platform-rollup"
},
"spec": {
"targetRefs": [
"platform-deployments"
],
"namespaceSelector": {
"matchLabels": {
"kollect.dev/tenant": "platform"
}
},
"sinkNamespace": "default",
"snapshotSinkRefs": [
{
"name": "git-inventory"
}
],
"databaseSinkRefs": [
{
"name": "postgres-inventory"
}
],
"suspend": false
}
},
{
"apiVersion": "kollect.dev/v1alpha1",
"kind": "KollectClusterScope",
"metadata": {
"name": "platform-ceiling"
},
"spec": {
"allowedGVKs": [
{
"group": "apps",
"version": "v1",
"kind": "Deployment"
},
{
"group": "",
"version": "v1",
"kind": "Service"
}
],
"deniedNamespaces": [
"kube-system"
],
"allowedStaticRefNamespaces": [
"default"
],
"snapshotSinkRefs": [
"git-inventory"
],
"databaseSinkRefs": [
"postgres-inventory"
],
"minExportInterval": "30s"
}
}
]
categories: Monitoring,Integration & Delivery
Expand Down
67 changes: 62 additions & 5 deletions hack/test/dist_olm_bundle_test.sh
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#!/usr/bin/env bash
# DIST-OLM-01: generate-olm-bundle must emit a registry+v1 bundle with all owned CRDs.
# DIST-OH-01: every owned CRD must carry an alm-examples entry.
set -euo pipefail

ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
Expand All @@ -17,6 +18,14 @@ pass() {
TEMPLATE="${ROOT}/config/olm/template/manifests/kollect.clusterserviceversion.yaml"
[[ -f "${TEMPLATE}" ]] || fail "${TEMPLATE} is missing"

# yq drives the CSV/role.yaml RBAC drift gate, jq the alm-examples coverage gate.
# Both are hard requirements: without them there is nothing to compare and the
# gates below would be skipped rather than enforced.
command -v yq >/dev/null 2>&1 ||
fail "yq (mikefarah/yq v4) is required for the CSV/role.yaml RBAC drift gate"
command -v jq >/dev/null 2>&1 ||
fail "jq is required for the CSV alm-examples coverage gate"

grep -Fq 'make generate-olm-bundle' "${ROOT}/Makefile" ||
fail "Makefile must define generate-olm-bundle target"

Expand All @@ -42,16 +51,64 @@ csv="${BUNDLE_DIR}/manifests/kollect.clusterserviceversion.yaml"
grep -Fq "kollect.v${VERSION}" "${csv}" || fail "CSV metadata.name must include version"
grep -Fq "ghcr.io/platformrelay/kollect@${IMAGE_DIGEST}" "${csv}" ||
fail "CSV must digest-pin the controller image"
grep -Fq 'kind: KollectProfile' "${csv}" || fail "CSV alm-examples must include KollectProfile"
grep -Fq 'kind: KollectTarget' "${csv}" || fail "CSV alm-examples must include KollectTarget"

# alm-examples coverage gate (DIST-OH-01). The OpenShift console and operatorhub.io
# pre-fill "Create instance" from metadata.annotations["alm-examples"]; an owned CRD
# with no entry hands the user a blank YAML editor, and the OperatorHub hosted
# validator warns "provided API should have an example annotation". Comparing the two
# sets is what makes a newly owned CRD unable to ship without an example -- the two
# `grep -Fq 'kind: Kollect...'` checks this replaces matched the *owned* list (plain
# YAML), never the JSON annotation, so they never tested alm-examples at all.
#
# Both the template (the file an author edits) and the generated bundle CSV (what
# upstream actually validates) are checked: generation is a sed pass, so a
# template-only assertion would not prove the annotation survives it.
check_alm_examples() {
local manifest="$1" label="$2"
local examples example_kinds owned_kinds example_count unique_count

examples="$(yq '.metadata.annotations."alm-examples"' "${manifest}")"
[[ -n "${examples}" && "${examples}" != "null" ]] ||
fail "${label}: metadata.annotations[\"alm-examples\"] is missing or empty"

printf '%s' "${examples}" | jq empty ||
fail "${label}: alm-examples is not valid JSON"
printf '%s' "${examples}" | jq -e 'type == "array"' >/dev/null ||
fail "${label}: alm-examples must be a JSON array"

example_kinds="$(printf '%s' "${examples}" | jq -r '.[].kind' | LC_ALL=C sort)"
owned_kinds="$(yq '.spec.customresourcedefinitions.owned[].kind' "${manifest}" | LC_ALL=C sort)"

# Both extractions must be non-empty, otherwise a mistyped yq/jq path would make
# the comparison below pass vacuously and the gate would never fire.
[[ -n "${example_kinds}" ]] ||
fail "${label}: extracted no kinds from alm-examples -- the coverage gate would pass vacuously"
[[ -n "${owned_kinds}" ]] ||
fail "${label}: extracted no kinds from spec.customresourcedefinitions.owned -- the coverage gate would pass vacuously"

# One example per owned CRD: a duplicated kind must not mask a missing one.
example_count="$(printf '%s\n' "${example_kinds}" | wc -l | tr -d ' ')"
unique_count="$(printf '%s\n' "${example_kinds}" | LC_ALL=C sort -u | wc -l | tr -d ' ')"
[[ "${example_count}" == "${unique_count}" ]] ||
fail "${label}: alm-examples has duplicate kinds (${example_count} entries, ${unique_count} distinct)"

if [[ "${example_kinds}" != "${owned_kinds}" ]]; then
printf 'dist olm bundle: %s alm-examples do not cover spec.customresourcedefinitions.owned ("<" alm-examples, ">" owned):\n' \
"${label}" >&2
diff <(printf '%s\n' "${example_kinds}") <(printf '%s\n' "${owned_kinds}") >&2 || true
fail "${label}: add one alm-examples entry per owned CRD in config/olm/template/manifests/kollect.clusterserviceversion.yaml"
fi

pass "${label}: alm-examples covers all ${example_count} owned CRDs"
}

check_alm_examples "${TEMPLATE}" "CSV template"
check_alm_examples "${csv}" "generated CSV"

# RBAC drift gate: the CSV clusterPermissions are a hand copy of the
# controller-gen-generated config/rbac/role.yaml. Without this gate the next
# +kubebuilder:rbac marker change would silently ship an under-privileged bundle
# that 403s at runtime on OperatorHub while the Helm chart keeps working.
command -v yq >/dev/null 2>&1 ||
fail "yq (mikefarah/yq v4) is required for the CSV/role.yaml RBAC drift gate"

ROLE="${ROOT}/config/rbac/role.yaml"
[[ -f "${ROLE}" ]] || fail "${ROLE} is missing"

Expand Down
Loading