Skip to content

fix: forEach and child tracking for NetworkPolicy, ResourceQuota, LimitRange, ClusterRole, ClusterRoleBinding#202

Merged
iAlexeze merged 13 commits into
mainfrom
fix/children-foreach-gaps
Jul 7, 2026
Merged

fix: forEach and child tracking for NetworkPolicy, ResourceQuota, LimitRange, ClusterRole, ClusterRoleBinding#202
iAlexeze merged 13 commits into
mainfrom
fix/children-foreach-gaps

Conversation

@iAlexeze

@iAlexeze iAlexeze commented Jul 5, 2026

Copy link
Copy Markdown
Collaborator

Summary

forEach: support for NetworkPolicy, ResourceQuota, LimitRange, ClusterRole, and ClusterRoleBinding was deferred from v0.7.8 when these types were first introduced. A declared forEach: block was silently dropped — no expansion, no error. None of these types appeared as tracked children in the CR detail view or Control Center Resources tab.

Also fixes a bug in pkg/merger where motif paths supplied as bare relative paths (e.g. motifs/01-namespaced/motif.yaml) were not recognised as file paths, causing ork validate -f <katalog> to fail when run from outside the katalog's directory.

Adds ork e2e --report-file <path> — a CI-agnostic flag that writes test results as a markdown table to a file in addition to stdout — and onFailure: diagnostics at both spec level and per-expectation level.


What changed

pkg/types/

  • types_networkpolicy.goForEach *ForEachSpec added to NetworkPolicyTemplateSource
  • types_resourcequota.goForEach *ForEachSpec added to ResourceQuotaTemplateSource
  • types_limitrange.goForEach *ForEachSpec added to LimitRangeTemplateSource
  • types_rbac.goForEach *ForEachSpec added to ClusterRoleTemplateSource and ClusterRoleBindingTemplateSource

pkg/children/

  • gvr.goResourceQuotaGVR and LimitRangeGVR vars added; both added to ChildGVRs()
  • foreach.goExpandForEachNetworkPolicies, ExpandForEachResourceQuotas, ExpandForEachLimitRanges, ExpandForEachClusterRoles, ExpandForEachClusterRoleBindings added
  • read.goNetworkPolicies added to both onCreate and onReconcile merge blocks in mergeTemplates
  • names.gonetworkPolicyNames, resourceQuotaNames, limitRangeNames, clusterRoleNames, clusterRoleBindingNames added
  • children.go — read blocks for all five types added; resources now appear in the CR detail children map

pkg/children/fixtures/

Restructured from a single flat katalog into a motif-based fixture covering every child resource type, all using forEach:. Tenant CRD is cluster-scoped so owner references from namespaced children in different namespaces are valid and not GC'd.

  • katalog.yaml — one katalog importing six focused motifs (one per resource group)
  • crd.yaml — Tenant CRD (cluster-scoped) with spec.namespaces, spec.roles, and spec.apps array fields
  • cr.yaml — sample Tenant CR with values for all three arrays
  • motifs/01-namespaced/motif.yaml — Namespace, NetworkPolicy, ResourceQuota, LimitRange; all forEach over spec.namespaces
  • motifs/02-rbac/motif.yaml — ClusterRole, ClusterRoleBinding (spec.roles); ServiceAccount, Role, RoleBinding (spec.namespaces); ClusterRoleBinding subjects use namespace: default
  • motifs/03-workloads/motif.yaml — Deployment, StatefulSet, ReplicaSet, Pod, Job, CronJob; all forEach over spec.apps
  • motifs/04-network/motif.yaml — Service, Ingress, HPA, PDB; all forEach over spec.apps
  • motifs/05-config/motif.yaml — ConfigMap, Secret; forEach over spec.namespaces
  • motifs/06-storage/motif.yaml — PV (spec.apps), PVC (spec.namespaces)
  • README.md — why the fixture exists, coverage table, running locally, adding a new child type

pkg/merger/helper.go

  • isFileMotif extended to recognise bare relative paths (e.g. motifs/foo/motif.yaml) as file paths. Previously only ./, ../, and absolute paths were matched; bare paths without a leading ./ were silently treated as OCI references and failed to resolve.

pkg/reconciler/

  • run_template_reconcile.go — runner calls for all five types now pass slices through ExpandForEach* wrappers

pkg/e2e/

  • result.goMarkdown() renders results as two GFM tables: passed (icon, test, time) and failed (icon, test, time, error); newlines in error messages replaced with .
  • runner.go — terminal output split: passed cases first, then failed cases with full error indented line-by-line; ReportFile string in Options writes the markdown report to a file and prints Report written to <path>; runOnFailure called at spec level when any expectation fails, and per-expectation immediately when that checkpoint fails
  • onfailure.go (new) — runOnFailure prints diagnostic kubectl and shell command output; never fails; printOnFailureKubectl mirrors the five read subcommands (get, logs, describe, events, exec); printDiag formats each entry with label and indented output
  • pkg/types/e2e.goE2EOnFailure type added; OnFailure *E2EOnFailure added to both E2ESpec (spec-level, runs once after all expectations) and E2EExpectation (per-expectation, runs immediately on that checkpoint's failure)

cmd/cli/e2e.go

  • --report-file <path> flag added; wired through to e2e.Options.ReportFile

examples/use-cases/full-stack-app/

  • 06-full-stack/katalog.yamlmanaged-database CRD block commented out by default; uncomment when running this example in isolation
  • 06-full-stack/README.md — Step 1 updated with uncomment instruction and expected two-CRD validate output
  • README.md — Troubleshooting section added explaining the duplicate CRD error and fix

Root cause

forEach: support for the five v0.7.8 types was deferred — runners in pkg/resources/ and builtins in pkg/children/builtins.go were completed, but the pkg/children/ plumbing (ExpandForEach*, *Names, GVR vars, read blocks in mergeTemplates, child tracking in children.go) was not.

isFileMotif assumed all relative paths start with ./ or ../, which is not enforced when users write motif imports by hand.


Test plan

  • ork validate -f pkg/children/fixtures/katalog.yaml — passes from repo root
  • ork run -f pkg/children/fixtures/katalog.yaml — Tenant CR applied; all resource types created
  • kubectl get namespacesacme-dev and acme-staging appear
  • kubectl get networkpolicies -A — deny-all policies in each namespace
  • kubectl get resourcequotas -A — quotas in each namespace
  • kubectl get limitranges -A — limit ranges in each namespace
  • kubectl get clusterrolesacme-developer and acme-reviewer appear
  • kubectl get clusterrolebindings — bindings appear
  • kubectl get deployments -Aapi and worker deployments appear
  • CR detail in Control Center Resources tab shows all resource kinds as children
  • ork e2e --report-file /tmp/results.mdReport written to /tmp/results.md printed; file contains two GFM tables (passed + failed with Error column)
  • Failing e2e with spec.onFailure: — diagnostic output printed after all expectations complete
  • Failing e2e with per-expectation onFailure: — diagnostic output printed immediately after that checkpoint fails

iAlexeze added 13 commits July 5, 2026 09:15
…ource types

NetworkPolicy, ResourceQuota, LimitRange, ClusterRole, and ClusterRoleBinding
were missing ForEach field wiring, ExpandForEach* functions, name extractors,
GVR entries, and read blocks — so forEach produced no expansion and none of
the resources appeared as tracked children.
isFileMotif only matched ./  ../  and absolute paths. Bare relative paths
like motifs/01-namespaced/motif.yaml were silently treated as OCI refs,
causing ork validate to fail when run from outside the katalog directory.
…ypes

Six motifs (namespaced, rbac, workloads, network, config, storage) imported
into one katalog. Every resource uses forEach, giving a single runnable
fixture for developing and verifying children changes.
ork e2e --report-file <path> writes test results as a GFM table in addition
to stdout. Designed like Go's -coverprofile — the caller decides what to do
with the file. ork-action passes $GITHUB_STEP_SUMMARY to populate the CI
job summary automatically.
Passed and failed cases are rendered in separate tables so the error
column only appears when there are failures. Newlines in error messages
are collapsed to ". " to keep table rows valid GFM.
… detail

Passed cases print first, failed cases follow with the full error message
indented beneath each — multiline want/got errors render on separate lines.
Also fixes a broken link to the e2e-universal guide in faqs/06-testing.md.
spec.onFailure runs once after all expectations when any failed.
expect[].onFailure runs immediately when that specific checkpoint fails,
capturing cluster state at the moment of failure.

Both accept the same kubectl DSL (get, logs, describe, events, exec)
and raw commands. Assertion fields are ignored — output is always printed.

Also bumps Chart.yaml to v0.7.10 and updates schema docs and fixture.
… isolated vs composite runs

06-full-stack/katalog.yaml declares managed-database commented out so the
root composite can import it from 03-cross-crd without a duplicate CRD error.

Step 1 in 06-full-stack/README.md now tells users to uncomment it when
running in isolation. Root README.md adds a Troubleshooting section explaining
the duplicate CRD error and the fix.
…subject namespace

Cluster-scoped CR fixes cross-namespace owner reference GC — Kubernetes
was deleting namespaced children (NetworkPolicy, ResourceQuota, LimitRange)
because their owner reference pointed to a Namespaced CR in a different
namespace.

ClusterRoleBinding subject namespace hardcoded to default — it was
resolving to the role name (developer/reviewer) from the forEach loop,
producing an invalid namespace value.
…ion use linkTitle

sync-docs.sh splits CHANGELOG.md into one page per release under
content/docs/changelog/; _index.md auto-generated as a release index.

sidebar.yaml: Changelog added after Roadmap.
sidebar.html + single.html: display text uses .LinkTitle so changelog
entries show just the version number while full title remains the page heading.

Also: tip admonition indent fixed in 00-kubernetes-basics.md;
roadmap last-updated date bumped to July 2026;
CHANGELOG.md [UNRELEASED] marker removed — v0.7.10 is ready to ship.
@iAlexeze iAlexeze merged commit c84eef5 into main Jul 7, 2026
6 checks passed
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.

1 participant