Skip to content

feat(chart): make CRD installation configurable - #346

Open
christianhuth wants to merge 2 commits into
hardbyte:mainfrom
christianhuth:fix/crd-install-toggles
Open

feat(chart): make CRD installation configurable#346
christianhuth wants to merge 2 commits into
hardbyte:mainfrom
christianhuth:fix/crd-install-toggles

Conversation

@christianhuth

@christianhuth christianhuth commented Aug 20, 2026

Copy link
Copy Markdown

Fixes #345.

What

The chart shipped its CRDs in crds/, which Helm never templates. Two consequences:

  • The documented installCRDs value in values.yaml was never referenced by anything — it had no effect.
  • There was no way to opt out of installing the shared wgpolicyk8s.io CRDs, so Netchecks conflicts with any other component that installs them (Kyverno, the Trivy PolicyReport adapters, Kubescape). Since the copy vendored here is generated with controller-gen v0.9.0 and Kyverno's with v0.20.0, continuous reconciliation can silently replace the newer schema with the older one.

--skip-crds was not a workaround, because it also drops networkassertions.yaml, without which the operator cannot run.

The toggle is not a free choice, and the docs say so. The operator writes to wgpolicyk8s.io/v1beta1 specifically (reconciler.rs). The copy shipped here serves v1alpha1, v1alpha2 and v1beta1; Kyverno's serves only v1alpha2. So this chart's copy is a superset that satisfies both tools, while the reverse is not true — handing ownership to Kyverno breaks reconciliation with 404 page not found. The README and the values comment spell that out, with the command to check the served versions first. See #347 for the underlying fix.

This moves the CRDs into templates/ and gates them through values:

crds:
  install: true          # master switch, honours the legacy `installCRDs`
  keep: true             # annotate with `helm.sh/resource-policy: keep`
  groups:
    netchecks: true      # NetworkAssertion (netchecks.io)
    wgpolicyk8s: true    # PolicyReport, ClusterPolicyReport (wgpolicyk8s.io)

Defaults keep today's behaviour: everything installed, nothing removed on uninstall.

crds.keep addresses the one real downside of leaving crds/ — templates follow the release lifecycle and would otherwise be deleted by helm uninstall, taking existing NetworkAssertion and PolicyReport resources with them. With the annotation set by default, uninstall leaves them alone, as crds/ did.

installCRDs is honoured as an alias, so installCRDs: false now does what its comment always claimed.

Also in here

Duplicate CRD removed. crds/clusterpolicyreports.yaml carried a second, byte-identical copy of policyreports.wgpolicyk8s.io, which is already defined in crds/policyreports.yaml. Dropped.

Upgrade note

This is the one behaviour change that needs calling out in the release notes. Objects installed from crds/ carry no Helm ownership metadata, so when they start being rendered from templates/, helm upgrade refuses to adopt them (pkg/action/upgrade.gocheckOwnership) and fails with invalid ownership metadata.

Documented in the chart README with the one-time adoption command:

for crd in networkassertions.netchecks.io policyreports.wgpolicyk8s.io clusterpolicyreports.wgpolicyk8s.io; do
  kubectl label crd "$crd" app.kubernetes.io/managed-by=Helm --overwrite
  kubectl annotate crd "$crd" \
    meta.helm.sh/release-name=<release> \
    meta.helm.sh/release-namespace=<namespace> --overwrite
done

Users who would rather not adopt them can set crds.install: false and manage the CRDs outside the chart.

Chart version bumped 0.3.1 → 0.4.0.

Testing

helm lint passes. Render matrix, all output parsed as YAML:

values CRDs rendered keep annotations
defaults 3 3
crds.keep=false 3 0
crds.groups.wgpolicyk8s=false 1 (NetworkAssertion) 1
crds.groups.netchecks=false 2 (PolicyReports) 2
crds.install=false 0 0
installCRDs=false 0 0
installCRDs=true 3 3

examples/kind-installation/values.yaml already contained a crds.install: true key that had no effect; it now resolves against the new schema, so the kind-based integration tests keep getting the CRDs.

I have not run the kind integration suite locally — no cluster to hand here.

Two things deliberately left out of scope

This PR only touches the chart. Both of these are downstream of the move and are yours to decide on:

  1. manifests/deploy.yaml is not regenerated here. Worth knowing that it currently contains no CRDs at all, because create-static-manifests.sh uses helm template, which skips crds/ — while the installation docs present that file as installing "the NetworkAssertion CRDs and the Netchecks operator". After this change, regenerating it picks up all three, which fixes that mismatch.

  2. create-static-manifests.sh may want --set crds.keep=false. Its sed -i.bak '/helm.sh/d' strips the helm.sh/resource-policy: keep line, which leaves a bare annotations: key on the NetworkAssertion CRD in the generated file. Valid YAML and accepted by Kubernetes, but untidy; rendering with crds.keep=false avoids it at the source.

Unrelated observation

.github/workflows/helm-chart.yaml runs ct lint --chart-dirs charts from the repo root and triggers on paths: charts/**, but the chart lives at operator/charts/netchecks. As far as I can tell that job finds no charts and the path filter never matches, so chart linting isn't actually running. Happy to open a separate issue.

@christianhuth
christianhuth force-pushed the fix/crd-install-toggles branch from 3ce1c8c to 1038d55 Compare August 20, 2026 11:02
The chart shipped its CRDs in `crds/`, which Helm never templates, so the
documented `installCRDs` value had no effect and there was no way to opt out
of installing the shared `wgpolicyk8s.io` CRDs. That made Netchecks hard to
run alongside anything else that produces PolicyReports, and risked silently
overwriting a newer schema with the copy vendored here.

Move the CRDs into `templates/` and gate them:

  crds.install            master switch (honours legacy `installCRDs`)
  crds.keep               annotate with `helm.sh/resource-policy: keep`
  crds.groups.netchecks   NetworkAssertion (`netchecks.io`)
  crds.groups.wgpolicyk8s PolicyReport/ClusterPolicyReport (`wgpolicyk8s.io`)

`crds.keep` defaults to true so that uninstalling the release leaves the CRDs
and their resources in place, matching the previous `crds/` behaviour.

Also drop a duplicate `policyreports.wgpolicyk8s.io` definition that was
carried in `crds/clusterpolicyreports.yaml` alongside the identical one in
`crds/policyreports.yaml`.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@christianhuth
christianhuth force-pushed the fix/crd-install-toggles branch from 1038d55 to 026a8dc Compare August 20, 2026 11:07

@hardbyte hardbyte left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! Looks good to me. Mind seeing why it failed ci?

The operator writes reports to `wgpolicyk8s.io/v1beta1` specifically. The CRDs
shipped by this chart serve v1alpha1, v1alpha2 and v1beta1, but other owners of
the same API group may serve fewer — Kyverno's chart serves only v1alpha2.

Handing ownership to such a component leaves the operator unable to write
reports, and NetworkAssertions fail to reconcile with `404 page not found`.

Document the constraint next to the toggle so it is not read as a free choice.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@christianhuth

Copy link
Copy Markdown
Author

Pushed 688f422 after finding that the toggle this PR adds is not a free choice.

The operator writes to wgpolicyk8s.io/v1beta1 specifically, and the CRD copies distributed for this shared API group disagree on served versions — this chart serves v1alpha1/v1alpha2/v1beta1, Kyverno's serves only v1alpha2. So this chart's copy is a superset that satisfies both projects, while handing ownership the other way breaks reconciliation with a bare 404 page not found.

The README and the values comment now state the constraint and give the command to check served versions before disabling. Opened #347 for the underlying fix — runtime version discovery in the operator, after which the toggle really would be unconstrained.

@christianhuth

Copy link
Copy Markdown
Author

Thanks! Looks good to me. Mind seeing why it failed ci?

https://github.com/hardbyte/netchecks/actions/runs/32362264967/job/96409014469?pr=346#step:7:499

#27 CANCELED
------
 > exporting to image:
------
ERROR: failed to build: failed to solve: failed to push ghcr.io/hardbyte/netchecks:pr-346: denied: installation not allowed to Write organization package

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Helm chart installs shared wgpolicyk8s.io CRDs unconditionally, conflicting with Kyverno

2 participants