Skip to content

feat: add workload wiring for resource dependencies - #3404

Merged
Mirage20 merged 25 commits into
openchoreo:mainfrom
Mirage20:issue-3334
May 11, 2026
Merged

Mirage20 merged 25 commits into
openchoreo:mainfrom
Mirage20:issue-3334

Conversation

@Mirage20

Copy link
Copy Markdown
Contributor

Purpose

Wire workloads to consume managed-resource outputs declared via dependencies.resources[]. Builds on the resource abstraction CRDs from #3392 and mirrors the existing endpoint-dependency pattern (dependencies.endpoints[].envBindings).

Approach

API

  • New WorkloadResourceDependency on Workload.spec.dependencies.resources[] with ref, envBindings, and fileBindings
  • New ResourceDependencyTarget and PendingResourceDependency on ReleaseBindingStatus for reverse-watch and diagnostics

Pipeline (renderer)

  • Per-output dispatch into env vars: literal value: becomes env.value; secretKeyRef: / configMapKeyRef: become env.valueFrom.*
  • fileBindings synthesize one volume + mount per (ref, sourceKind, sourceName), deduped, with deterministic FNV-1a names under an r- prefix disjoint from the configurations side's file-mount- prefix
  • Two new CEL macros: ${dependencies.toContainerVolumeMounts()} and ${dependencies.toVolumes()}
  • Pipeline-side env-var, volume, and volume-mount lists use local typed structs shared with the configurations side so ${configurations.toX() + dependencies.toX()} is type-compatible in CEL

Controller (releasebinding)

  • New composite index IndexKeyResourceReleaseBindingOwnerEnv. Resolver hops per dep, gates on the provider's typed ConditionReady, threads outputs through the renderer
  • New ConditionResourceDependenciesReady folded into Ready with priority ConnectionsResolved > ResourceDependenciesReady > ResourcesReady
  • Reverse-watch on ResourceReleaseBinding via a target index plus an outputs-changed predicate. Stability guard blocks ReleaseSynced=True while any dep is pending

Samples

  • Default CCTs (service, webapp, worker, scheduled-task) and the two component-with-* samples render ${configurations.toX() + dependencies.toX()} for volumes and volumeMounts so workloads with both configurations and resource dependencies produce a valid Pod

Related Issues

Closes #3334
Related #3107

Checklist

  • Tests added or updated (unit, integration, etc.)
  • Samples updated (if applicable)
  • Added backport/<release-branch> label if this should be backported (e.g., backport/release-v1.0)

@coderabbitai

coderabbitai Bot commented May 10, 2026 •

Copy link
Copy Markdown

Review Change Stack
No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 15443a8a-93ad-45d1-8924-03ccd73de962

📥 Commits

Reviewing files that changed from the base of the PR and between 7cb89ba and a368cd9.

⛔ Files ignored due to path filters (4)
  • api/v1alpha1/zz_generated.deepcopy.go is excluded by !**/zz_generated.*
  • config/crd/bases/openchoreo.dev_componentreleases.yaml is excluded by !config/crd/bases/**
  • config/crd/bases/openchoreo.dev_releasebindings.yaml is excluded by !config/crd/bases/**
  • config/crd/bases/openchoreo.dev_workloads.yaml is excluded by !config/crd/bases/**
📒 Files selected for processing (36)
  • api/v1alpha1/releasebinding_types.go
  • api/v1alpha1/workload_types.go
  • docs/templating/context.md
  • install/helm/openchoreo-control-plane/crds/openchoreo.dev_componentreleases.yaml
  • install/helm/openchoreo-control-plane/crds/openchoreo.dev_releasebindings.yaml
  • install/helm/openchoreo-control-plane/crds/openchoreo.dev_workloads.yaml
  • internal/controller/releasebinding/controller.go
  • internal/controller/releasebinding/controller_conditions.go
  • internal/controller/releasebinding/controller_connections.go
  • internal/controller/releasebinding/controller_integration_test.go
  • internal/controller/releasebinding/controller_resourcedependencies.go
  • internal/controller/releasebinding/controller_resourcedependencies_integration_test.go
  • internal/controller/releasebinding/controller_resourcedependencies_test.go
  • internal/controller/releasebinding/controller_status.go
  • internal/controller/releasebinding/controller_watch.go
  • internal/controller/releasebinding/suite_test.go
  • internal/controller/watch.go
  • internal/controller/watch_test.go
  • internal/pipeline/component/context/builder_test.go
  • internal/pipeline/component/context/cel_extensions.go
  • internal/pipeline/component/context/cel_extensions_test.go
  • internal/pipeline/component/context/cel_return_types.go
  • internal/pipeline/component/context/resourcedependency.go
  • internal/pipeline/component/context/resourcedependency_test.go
  • internal/pipeline/component/context/types.go
  • internal/pipeline/component/context/types_test.go
  • internal/pipeline/component/pipeline.go
  • internal/pipeline/component/types.go
  • internal/validation/component/cel_env_test.go
  • samples/component-types/component-with-configs/component-with-configs.yaml
  • samples/component-types/component-with-embedded-traits/component-with-embedded-traits.yaml
  • samples/getting-started/all.yaml
  • samples/getting-started/component-types/scheduled-task.yaml
  • samples/getting-started/component-types/service.yaml
  • samples/getting-started/component-types/webapp.yaml
  • samples/getting-started/component-types/worker.yaml
✅ Files skipped from review due to trivial changes (1)
  • samples/component-types/component-with-embedded-traits/component-with-embedded-traits.yaml
🚧 Files skipped from review as they are similar to previous changes (32)
  • samples/component-types/component-with-configs/component-with-configs.yaml
  • install/helm/openchoreo-control-plane/crds/openchoreo.dev_releasebindings.yaml
  • internal/controller/releasebinding/controller_conditions.go
  • internal/controller/watch.go
  • internal/pipeline/component/pipeline.go
  • samples/getting-started/component-types/scheduled-task.yaml
  • install/helm/openchoreo-control-plane/crds/openchoreo.dev_workloads.yaml
  • install/helm/openchoreo-control-plane/crds/openchoreo.dev_componentreleases.yaml
  • api/v1alpha1/releasebinding_types.go
  • internal/pipeline/component/context/builder_test.go
  • samples/getting-started/component-types/service.yaml
  • internal/controller/releasebinding/controller_resourcedependencies.go
  • samples/getting-started/component-types/webapp.yaml
  • internal/controller/releasebinding/controller_connections.go
  • internal/pipeline/component/types.go
  • samples/getting-started/all.yaml
  • internal/controller/releasebinding/controller_watch.go
  • internal/pipeline/component/context/resourcedependency.go
  • internal/controller/releasebinding/controller_resourcedependencies_integration_test.go
  • internal/controller/releasebinding/controller_status.go
  • internal/validation/component/cel_env_test.go
  • internal/pipeline/component/context/cel_extensions.go
  • internal/controller/releasebinding/controller.go
  • internal/pipeline/component/context/cel_extensions_test.go
  • internal/pipeline/component/context/types.go
  • internal/pipeline/component/context/cel_return_types.go
  • api/v1alpha1/workload_types.go
  • internal/controller/releasebinding/controller_resourcedependencies_test.go
  • internal/pipeline/component/context/resourcedependency_test.go
  • internal/controller/releasebinding/suite_test.go
  • docs/templating/context.md
  • internal/pipeline/component/context/types_test.go

📝 Walkthrough

Changed Files Summary

File count by top-level directory:

  • api/v1alpha1/ – 2 files (+104 lines)
  • install/helm/openchoreo-control-plane/crds/ – 3 CRD manifest files (+128 lines)
  • internal/controller/releasebinding/ – 9 files (+1,593 lines)
  • internal/controller/ – 2 files (+95 lines)
  • internal/pipeline/component/context/ – 8 files (+906 lines)
  • internal/pipeline/component/ – 2 files (+9 lines)
  • internal/validation/component/ – 1 file (+44 lines)
  • docs/ – 1 file (+103 lines)
  • samples/ – 7 YAML files (+16 lines)

Total: ~35 files, ~2,700 net additions (no deletions except deprecated types)


API and CRD Surface Changes

New API types (api/v1alpha1/):

  • WorkloadResourceDependency – ref (required) + envBindings/fileBindings maps with non-empty CEL validation
  • ResourceDependencyTarget – consumer namespace/project/resourceName/environment for reverse indexing
  • PendingResourceDependency – same fields + reason string for status diagnostics

Workload CRD (spec.dependencies):

  • Added resources[] keyed by ref (max 50 entries); each entry: ref (required, minLength 1), envBindings{} (optional, CEL validates non-empty values), fileBindings{} (optional, CEL validates non-empty values)
  • x-kubernetes-list-type: map with x-kubernetes-list-map-keys: [ref] prevents accidental duplication

ReleaseBinding CRD (status):

  • Added resourceDependencyTargets[] – array of target tuples (namespace, project, resourceName, environment; all minLength 1)
  • Added pendingResourceDependencies[] – array of unresolved entries (namespace, project, resourceName with minLength 1; reason plain string)

Compatibility risk: Low
All additions are optional; no breaking changes to existing fields. Empty resources[] is default-safe.


Tests Added and Critical Coverage

19 test functions across 3 new test files:

controller_resourcedependencies_test.go (13 tests):

  • Target construction, condition aggregation, readiness logic
  • Reverse-watch indexing keying and deduplication
  • Predicate behavior (outputs changed, Ready flips, generation changes)
  • Provider readiness gates and pending state tracking
  • Composite index key round-trip validation

controller_resourcedependencies_integration_test.go (1 suite, 3 scenarios):

  • Missing provider binding → pending status, ReleaseSynced blocked
  • Ready provider with value output → env var rendering
  • Configurations + dependencies volume concatenation → deduplication by prefix

resourcedependency_test.go (3 tests):

  • Env/volume/mount synthesis from secret/configmap refs
  • FNV-1a deterministic naming with r- prefix isolation from config volumes
  • Output deduplication, combined env+file bindings, empty output handling

Updated tests (5 files, ~230 lines):

  • builder_test.go – dependencies context includes resources, volumeMounts, volumes fields
  • cel_extensions_test.go – new TestDependenciesVolumeMacros for both configurations and dependencies receivers
  • cel_env_test.go – CEL type-safety for configurations.toVolumes() + dependencies.toVolumes()
  • suite_test.go – cached client + manager-backed cache with SetupSharedIndexes()
  • types_test.go – merged dependency view assertions (env vars, volume mounts, volumes)

Critical gaps:

  • No RBAC/authz tests – no validation that workload consumers are scoped to their own project's resources
  • No secret/configmap existence validation – volumes reference external secrets/configmaps without pre-flight checks
  • No transient error recovery – missing tests for API list/get failures during resolution
  • No predicate edge cases – missing tests for malformed provider bindings or orphaned references

Risk Hotspots

1. Volume/Secret reference timing (Medium risk)

  • File bindings synthesize K8s VolumeEntry objects pointing to provider-managed secrets/configmaps without verifying they exist
  • Code path: resourcedependency.go:makeEnvVar() and file binding case dispatch directly reference SecretKeyRef.Name / ConfigMapKeyRef.Name without existence check
  • Mitigation: allResourceDependenciesResolved() blocks ReleaseSynced until provider ResourceReleaseBinding.Status.Ready=True; but no guarantee that Ready state persists until pod scheduling
  • Recommendation: Add pre-flight validation or admission webhook to verify secret/configmap exists in target namespace

2. Reconciliation predicate churn (Medium risk)

  • resourceReleaseBindingOutputsChangedPredicate() watches provider ResourceReleaseBinding output changes; Missing/flapping Ready condition could trigger repeated reconciliation
  • Mitigation: readyConditionChanged() compares both Status and ObservedGeneration to avoid spurious updates
  • Verified in tests: TestResourceReleaseBindingOutputsChangedPredicate covers Ready flips and generation changes

3. Volume deduplication correctness (Low risk)

  • Multiple file bindings to the same secret/configmap within a single dependency share one volume; volumes are keyed by (dep.Ref, kind, name) tuple
  • Code uses map lookup to deduplicate before appending VolumeEntry objects
  • Risk: Collision if dependency ref + secret name combination matches across different resource types (secret vs configmap)
  • Mitigation: volumeKey struct includes kind field; FNV-1a naming uses resourceDepVolumeName(ref, kind, name) with deterministic order
  • Verified in tests: TestResourceDepVolumeName_ and TestBuildResourceDependencyItem (deduplication case)

4. CEL expression type safety (Low risk)

  • New EnvVarEntry type (replacing old ConnectionEnvVar) must be compatible with configurations-derived env vars for concatenation
  • Code: Both configurations and dependencies now return []EnvVarEntry from context builders
  • Verified in tests: TestBuildComponentCELEnv_ConfigurationsDependenciesConcat confirms type-safe + concatenation

5. Composite index collision (Low risk)

  • Resource dependency reverse-watch uses IndexKeyResourceReleaseBindingOwnerEnv = projectName/resourceName/environment
  • Volume naming uses FNV-1a prefix r-, distinct from config volume prefix file-mount-
  • No collision observed; deterministic iteration (sorted map keys) ensures stable naming
  • Verified in tests: TestMakeResourceReleaseBindingOwnerEnvKey round-trip validation

6. Install/upgrade path (Low risk)

  • CRD schemas are additive-only; new resources[] field is optional with no required inner fields at root
  • No controller version coupling; Helm charts show schema-only additions
  • No migration logic required; empty dependencies.resources is safe default
  • Verified: Sample templates updated to include dependencies context; backward compatible

Walkthrough

This PR extends OpenChoreo workloads to declare resource dependencies and bind their outputs to container environment variables and file mounts. It adds API types, updates CRDs, implements controller resolution with reverse-watch indexing, and wires resolved dependencies through the rendering pipeline. Comprehensive integration and unit tests validate end-to-end behavior.

Changes

Resource Dependency Feature

Layer / File(s) Summary
API Schema
api/v1alpha1/workload_types.go, api/v1alpha1/releasebinding_types.go
Introduce WorkloadResourceDependency with ref, envBindings, and fileBindings fields; add ResourceDependencyTarget and PendingResourceDependency to track resolution state.
CRD Registration
install/helm/openchoreo-control-plane/crds/openchoreo.dev_workloads.yaml, openchoreo.dev_releasebindings.yaml, openchoreo.dev_componentreleases.yaml
Register resource dependency fields in Kubernetes schemas with validation: required ref, optional non-empty env var/mount path mappings.
Condition Types
internal/controller/releasebinding/controller_conditions.go
Define ConditionResourceDependenciesReady with reasons: AllResourceDependenciesReady, ResourceDependenciesPending, NoResourceDependencies.
Shared Indexing
internal/controller/watch.go, internal/controller/watch_test.go
Add composite owner+environment index for ResourceReleaseBinding lookup by (projectName, resourceName, environment) for reverse-watch discovery.
Watch and Predicate
internal/controller/releasebinding/controller_watch.go
Implement resource dependency target indexing and predicate to trigger consumer reconciliation when provider outputs or Ready status change.
Resolution Logic
internal/controller/releasebinding/controller_resourcedependencies.go
Resolve each dependency by building targets, listing providers via index, handling missing/multiple/not-ready providers as pending, extracting outputs when ready.
Reconciliation Integration
internal/controller/releasebinding/controller.go
Call populateResourceDependencyStatus during reconcile, pass resolved items to pipeline, enforce stability guard blocking ReleaseSynced until all dependencies resolved.
Readiness Aggregation
internal/controller/releasebinding/controller_status.go
Integrate ResourceDependenciesReady into setReadyCondition with priority after connectionsResolved and before resourcesReady.
Environment Variable Types
internal/pipeline/component/context/cel_return_types.go
Define EnvVarEntry with optional Value and/or ValueFrom, EnvVarSourceEntry for secret/configmap refs, and KeyRef for key references.
Dependency Item Building
internal/pipeline/component/context/resourcedependency.go, resourcedependency_test.go
Implement BuildResourceDependencyItem to convert output bindings into deterministic env vars, volume mounts, and deduplicated volumes with error handling.
Dependencies Context
internal/pipeline/component/context/types.go
Expand ConnectionsData and ConnectionsContextData to include Resources field and merged top-level lists for env vars, volume mounts, volumes.
CEL Macro Extensions
internal/pipeline/component/context/cel_extensions.go, cel_extensions_test.go
Extend toVolumes() and toContainerVolumeMounts() macros to support dependencies receiver in addition to configurations.
Connection Env Var Update
internal/controller/releasebinding/controller_connections.go
Switch connection env vars to use shared EnvVarEntry type instead of ConnectionEnvVar.
Pipeline Wiring
internal/pipeline/component/pipeline.go, internal/pipeline/component/types.go
Wire ResourceDependencyItems from controller into RenderInput and populate dependencies context for trait evaluation.
Integration Tests
internal/controller/releasebinding/controller_resourcedependencies_integration_test.go
Test scenarios: provider missing (pending), provider ready with outputs (resolved, synced), and volume/mount concatenation between configurations and dependencies.
Unit Tests
internal/controller/releasebinding/controller_resourcedependencies_test.go
Test target building, readiness aggregation, indexing, reverse-watch discovery, predicate filtering, and condition priority.
Context Tests
internal/pipeline/component/context/resourcedependency_test.go, types_test.go, cel_extensions_test.go, builder_test.go
Test dependency item building, context data merging, CEL macros, and builder expectations for resource dependencies.
Documentation
docs/templating/context.md
Document resource dependency structure, separated items/resources views, merged convenience lists, and CEL helper methods.
Sample Templates
samples/component-types/*, samples/getting-started/*
Update component type deployment templates to concatenate configuration and dependency volume/mount projections.

🎯 4 (Complex) | ⏱️ ~60 minutes

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
docs/templating/context.md (1)

535-637: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Document the resource-dependency half of dependencies end-to-end.

This section still reads as connection-only. It omits dependencies.resources, dependencies.volumeMounts, dependencies.volumes, and the new dependencies.toContainerVolumeMounts() / dependencies.toVolumes() helpers. The example on Lines 601-606 also drops valueFrom, so secret/configMap-backed dependency envs are documented incorrectly.

Also applies to: 852-868

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/templating/context.md` around lines 535 - 637, Update the dependencies
docs to cover resource-dependency fields and helpers end-to-end: add
descriptions and examples for dependencies.resources, dependencies.volumeMounts,
dependencies.volumes and document the new helper methods
dependencies.toContainerVolumeMounts() and dependencies.toVolumes(); update the
examples (including the env example around lines ~601) to show EnvVar.valueFrom
usage (secretKeyRef/configMapKeyRef) as well as literal value, and ensure the
table and example YAML reflect that dependencies.envVars is merged from both
connection items and resource dependencies and that items/envVars are never
null; reference symbols to change: dependencies.resources,
dependencies.volumeMounts, dependencies.volumes,
dependencies.toContainerVolumeMounts(), dependencies.toVolumes(), and
EnvVar.valueFrom.
🧹 Nitpick comments (1)
internal/controller/releasebinding/controller_resourcedependencies.go (1)

216-222: ⚡ Quick win

Consider checking cond.ObservedGeneration against rrb.Generation before treating the provider as Ready.

isResourceReleaseBindingReady only inspects cond.Status. If the provider ResourceReleaseBinding's spec is updated (e.g., outputs change) but its controller has not yet reconciled the new generation, the previously-written Ready=True condition is still present and the consumer here will accept it and dispatch potentially-stale rrb.Status.Outputs into the renderer. Adding an ObservedGeneration == Generation check makes the readiness gate robust to provider-controller lag and aligns with the PR's stated goal of waiting for "full steady-state on the provider".

♻️ Proposed defensive check
 func isResourceReleaseBindingReady(rrb *openchoreov1alpha1.ResourceReleaseBinding) bool {
 	cond := meta.FindStatusCondition(rrb.Status.Conditions, string(resourcereleasebinding.ConditionReady))
 	if cond == nil {
 		return false
 	}
-	return cond.Status == metav1.ConditionTrue
+	if cond.ObservedGeneration != 0 && cond.ObservedGeneration < rrb.Generation {
+		return false
+	}
+	return cond.Status == metav1.ConditionTrue
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/controller/releasebinding/controller_resourcedependencies.go` around
lines 216 - 222, Update isResourceReleaseBindingReady to also verify that the
condition's ObservedGeneration matches the ResourceReleaseBinding's current
Generation before returning true: locate isResourceReleaseBindingReady(rrb
*openchoreov1alpha1.ResourceReleaseBinding), after retrieving cond via
meta.FindStatusCondition check cond != nil, then require cond.Status ==
metav1.ConditionTrue AND cond.ObservedGeneration == rrb.Generation (treat
missing/zero ObservedGeneration as not current). This ensures the function only
treats a provider as Ready when the controller has reconciled the current
spec/generation.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/templating/context.md`:
- Around line 722-734: The TraitContext example wrongly includes
dataplane.publicVirtualHost which is not part of the DataPlaneData shape used by
TraitContext/ComponentContext; remove the publicVirtualHost entry from the YAML
example under dataplane and update the prose to only show secretStore and
observabilityPlaneRef (keeping observabilityPlaneRef fields kind and name).
Locate the example block labeled "dataplane:" in docs/templating/context.md and
delete the publicVirtualHost line and its comment, ensuring references to
ComponentContext, TraitContext, and DataPlaneData remain consistent.

In `@install/helm/openchoreo-control-plane/crds/openchoreo.dev_workloads.yaml`:
- Around line 234-249: The CRD currently allows empty strings for envBindings
and fileBindings values; to fix, update the Go API type that declares the
envBindings and fileBindings fields (the map fields on the ResourceType/Workload
spec type) so the map value cannot be empty: create a small aliased type like
NonEmptyString string with a kubebuilder validation marker //
+kubebuilder:validation:MinLength=1 and replace map[string]string with
map[string]NonEmptyString for the EnvBindings and FileBindings fields (or add
the equivalent kubebuilder validation marker directly if your toolchain supports
per-value validation), then regenerate the CRDs under
install/helm/openchoreo-control-plane/crds/.

In `@internal/pipeline/component/context/resourcedependency.go`:
- Around line 93-132: The loop over fileBindingNames (using dep.FileBindings and
outputByName) must detect and reject duplicate mount paths before appending to
item.VolumeMounts; add a small set (map[string]struct{}) like mountPathSeen and,
for each outputName, check mountPath := dep.FileBindings[outputName] and if
mountPath is already in the set return a clear dependency error (e.g.,
fmt.Errorf("%w: %s", ErrDuplicateMountPath, mountPath) — create
ErrDuplicateMountPath if it doesn’t exist); only when mountPath is new add it to
the set and proceed with existing logic that builds volumes (volumes map,
volumeKey, resourceDepVolumeName) and appends VolumeMountEntry to
item.VolumeMounts.

---

Outside diff comments:
In `@docs/templating/context.md`:
- Around line 535-637: Update the dependencies docs to cover resource-dependency
fields and helpers end-to-end: add descriptions and examples for
dependencies.resources, dependencies.volumeMounts, dependencies.volumes and
document the new helper methods dependencies.toContainerVolumeMounts() and
dependencies.toVolumes(); update the examples (including the env example around
lines ~601) to show EnvVar.valueFrom usage (secretKeyRef/configMapKeyRef) as
well as literal value, and ensure the table and example YAML reflect that
dependencies.envVars is merged from both connection items and resource
dependencies and that items/envVars are never null; reference symbols to change:
dependencies.resources, dependencies.volumeMounts, dependencies.volumes,
dependencies.toContainerVolumeMounts(), dependencies.toVolumes(), and
EnvVar.valueFrom.

---

Nitpick comments:
In `@internal/controller/releasebinding/controller_resourcedependencies.go`:
- Around line 216-222: Update isResourceReleaseBindingReady to also verify that
the condition's ObservedGeneration matches the ResourceReleaseBinding's current
Generation before returning true: locate isResourceReleaseBindingReady(rrb
*openchoreov1alpha1.ResourceReleaseBinding), after retrieving cond via
meta.FindStatusCondition check cond != nil, then require cond.Status ==
metav1.ConditionTrue AND cond.ObservedGeneration == rrb.Generation (treat
missing/zero ObservedGeneration as not current). This ensures the function only
treats a provider as Ready when the controller has reconciled the current
spec/generation.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 38a52623-89c3-4dd5-ae1e-019ad010f6cb

📥 Commits

Reviewing files that changed from the base of the PR and between d1efe58 and 418b017.

⛔ Files ignored due to path filters (4)
  • api/v1alpha1/zz_generated.deepcopy.go is excluded by !**/zz_generated.*
  • config/crd/bases/openchoreo.dev_componentreleases.yaml is excluded by !config/crd/bases/**
  • config/crd/bases/openchoreo.dev_releasebindings.yaml is excluded by !config/crd/bases/**
  • config/crd/bases/openchoreo.dev_workloads.yaml is excluded by !config/crd/bases/**
📒 Files selected for processing (36)
  • api/v1alpha1/releasebinding_types.go
  • api/v1alpha1/workload_types.go
  • docs/templating/context.md
  • install/helm/openchoreo-control-plane/crds/openchoreo.dev_componentreleases.yaml
  • install/helm/openchoreo-control-plane/crds/openchoreo.dev_releasebindings.yaml
  • install/helm/openchoreo-control-plane/crds/openchoreo.dev_workloads.yaml
  • internal/controller/releasebinding/controller.go
  • internal/controller/releasebinding/controller_conditions.go
  • internal/controller/releasebinding/controller_connections.go
  • internal/controller/releasebinding/controller_integration_test.go
  • internal/controller/releasebinding/controller_resourcedependencies.go
  • internal/controller/releasebinding/controller_resourcedependencies_integration_test.go
  • internal/controller/releasebinding/controller_resourcedependencies_test.go
  • internal/controller/releasebinding/controller_status.go
  • internal/controller/releasebinding/controller_watch.go
  • internal/controller/releasebinding/suite_test.go
  • internal/controller/watch.go
  • internal/controller/watch_test.go
  • internal/pipeline/component/context/builder_test.go
  • internal/pipeline/component/context/cel_extensions.go
  • internal/pipeline/component/context/cel_extensions_test.go
  • internal/pipeline/component/context/cel_return_types.go
  • internal/pipeline/component/context/resourcedependency.go
  • internal/pipeline/component/context/resourcedependency_test.go
  • internal/pipeline/component/context/types.go
  • internal/pipeline/component/context/types_test.go
  • internal/pipeline/component/pipeline.go
  • internal/pipeline/component/types.go
  • internal/validation/component/cel_env_test.go
  • samples/component-types/component-with-configs/component-with-configs.yaml
  • samples/component-types/component-with-embedded-traits/component-with-embedded-traits.yaml
  • samples/getting-started/all.yaml
  • samples/getting-started/component-types/scheduled-task.yaml
  • samples/getting-started/component-types/service.yaml
  • samples/getting-started/component-types/webapp.yaml
  • samples/getting-started/component-types/worker.yaml

Comment thread docs/templating/context.md Outdated
Comment thread internal/pipeline/component/context/resourcedependency.go
@Mirage20

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented May 10, 2026

Copy link
Copy Markdown
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@api/v1alpha1/workload_types.go`:
- Around line 242-243: The current CEL validation on EnvBindings only checks map
values, allowing empty keys; change the kubebuilder XValidation rule on
EnvBindings to require both non-empty keys and non-empty values by using a
predicate like self.all(k, k != '' && self[k].size() > 0) (replace the existing
rule expression), and make the same change to the other map binding field in the
same struct that has the similar validation to ensure keys cannot be empty there
either.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 1a77b075-015f-45ed-89d6-f7066b97d315

📥 Commits

Reviewing files that changed from the base of the PR and between 418b017 and 4fc6ba5.

⛔ Files ignored due to path filters (2)
  • config/crd/bases/openchoreo.dev_componentreleases.yaml is excluded by !config/crd/bases/**
  • config/crd/bases/openchoreo.dev_workloads.yaml is excluded by !config/crd/bases/**
📒 Files selected for processing (6)
  • api/v1alpha1/workload_types.go
  • docs/templating/context.md
  • install/helm/openchoreo-control-plane/crds/openchoreo.dev_componentreleases.yaml
  • install/helm/openchoreo-control-plane/crds/openchoreo.dev_workloads.yaml
  • internal/controller/releasebinding/controller_resourcedependencies.go
  • internal/controller/releasebinding/controller_resourcedependencies_test.go
✅ Files skipped from review due to trivial changes (1)
  • docs/templating/context.md
🚧 Files skipped from review as they are similar to previous changes (4)
  • install/helm/openchoreo-control-plane/crds/openchoreo.dev_workloads.yaml
  • internal/controller/releasebinding/controller_resourcedependencies.go
  • install/helm/openchoreo-control-plane/crds/openchoreo.dev_componentreleases.yaml
  • internal/controller/releasebinding/controller_resourcedependencies_test.go

Comment thread api/v1alpha1/workload_types.go Outdated
@Mirage20

Copy link
Copy Markdown
Contributor Author

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented May 10, 2026

Copy link
Copy Markdown
✅ Actions performed

Full review triggered.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 5

♻️ Duplicate comments (1)
api/v1alpha1/workload_types.go (1)

242-250: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Validate dependency binding map keys as non-empty

Line 242 and Line 249 only validate map values. Empty output-name keys are still admitted and can produce unresolvable dependency bindings. Validate both keys and values.

Suggested patch
-	// +kubebuilder:validation:XValidation:rule="self.all(k, self[k].size() > 0)",message="envBindings values (env var names) cannot be empty"
+	// +kubebuilder:validation:XValidation:rule="self.all(k, k.size() > 0 && self[k].size() > 0)",message="envBindings keys (output names) and values (env var names) cannot be empty"
 	EnvBindings map[string]string `json:"envBindings,omitempty"`
@@
-	// +kubebuilder:validation:XValidation:rule="self.all(k, self[k].size() > 0)",message="fileBindings values (mount paths) cannot be empty"
+	// +kubebuilder:validation:XValidation:rule="self.all(k, k.size() > 0 && self[k].size() > 0)",message="fileBindings keys (output names) and values (mount paths) cannot be empty"
 	FileBindings map[string]string `json:"fileBindings,omitempty"`
#!/bin/bash
# Verify current rule only checks map values.
rg -n 'XValidation:rule="self\.all\(k, self\[k\]\.size\(\) > 0\)"|EnvBindings|FileBindings' api/v1alpha1/workload_types.go -C2

As per coding guidelines, "Verify kubebuilder validation tags are consistent and safe."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@api/v1alpha1/workload_types.go` around lines 242 - 250, The XValidation tags
on the EnvBindings and FileBindings fields only check map values; update the
kubebuilder validation rule for both EnvBindings and FileBindings so it asserts
both the map key and the map value are non-empty (i.e., check k.size() > 0 and
self[k].size() > 0 in the rule expression) so empty output-name keys are
rejected; locate the annotations on the EnvBindings and FileBindings fields and
replace the existing rule string accordingly.
🧹 Nitpick comments (1)
internal/pipeline/component/context/resourcedependency_test.go (1)

270-273: ⚡ Quick win

Handle BuildResourceDependencyItem errors explicitly in this determinism test.

The test currently discards errors, which can mask regressions and lead to less actionable failures when indexing into Volumes.

Proposed patch
-		first, _ := BuildResourceDependencyItem(dep, outputs)
+		first, err := BuildResourceDependencyItem(dep, outputs)
+		require.NoError(t, err)
+		require.Len(t, first.Volumes, 1)
 		for i := 0; i < 10; i++ {
-			again, _ := BuildResourceDependencyItem(dep, outputs)
+			again, err := BuildResourceDependencyItem(dep, outputs)
+			require.NoError(t, err)
+			require.Len(t, again.Volumes, 1)
 			require.Equal(t, first.Volumes[0].Name, again.Volumes[0].Name)
 		}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/pipeline/component/context/resourcedependency_test.go` around lines
270 - 273, The test currently ignores errors from BuildResourceDependencyItem
and then indexes into Volumes, which can hide failures; change the calls to
capture and assert on the error (e.g., first, err :=
BuildResourceDependencyItem(dep, outputs) with require.NoError(t, err) and
inside the loop again, err := BuildResourceDependencyItem(dep, outputs) with
require.NoError(t, err)) before comparing first.Volumes[0].Name and
again.Volumes[0].Name to ensure the test fails clearly if
BuildResourceDependencyItem returns an error.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/templating/context.md`:
- Around line 699-708: The snippet's env only includes dependency envs,
contradicting the heading; update the template so the container env merges
configuration-derived envs with dependency envs (e.g., use
configurations.toContainerEnvs() + dependencies.toContainerEnvs()) or explicitly
document/ADD envFrom as needed; change the env line in the template (referencing
configurations.toContainerEnvs() and dependencies.toContainerEnvs()) to reflect
the combined projection so configurations are not dropped.

In `@install/helm/openchoreo-control-plane/crds/openchoreo.dev_workloads.yaml`:
- Around line 242-256: The CEL validations only check map values; update the Go
API types that define envBindings and fileBindings (the map fields named
EnvBindings/FileBindings or similar in the workload API struct) to enforce
non-empty keys by adding a validation tag or marker (e.g.,
kubebuilder:validation:MinLength=1 for map keys or a
+kubebuilder:validation:XValidation rule that ensures self.keys().all(k,
k.size() > 0)), then regenerate the CRD so the generated YAML includes CEL rules
that assert both keys and values are non-empty for envBindings and fileBindings.
Make sure the code changes reference the exact field names EnvBindings and
FileBindings (or envBindings/fileBindings in the Go struct) and run the CRD
generation step to update
install/helm/openchoreo-control-plane/crds/openchoreo.dev_workloads.yaml.

In
`@internal/controller/releasebinding/controller_resourcedependencies_integration_test.go`:
- Around line 270-276: The Ready condition in the test fixtures doesn't set
ObservedGeneration, so isResourceReleaseBindingReady() rejects the condition;
update the metav1.Condition used for rrb.Status.Conditions (and the similar
fixture at the other block) to include ObservedGeneration equal to
rrb.Generation (i.e., set ObservedGeneration: rrb.Generation) before calling
k8sClient.Status().Update so the condition is accepted as for the current
generation.

In `@internal/controller/releasebinding/controller_watch.go`:
- Around line 173-225: The predicate currently only triggers on Status.Outputs
or Ready.Status flips (resourceReleaseBindingOutputsChangedPredicate ->
UpdateFunc / readyConditionStatusChanged), but misses metadata.generation and
Ready.ObservedGeneration changes so consumers stay stale; update the UpdateFunc
to also return true when oldRRB.GetGeneration() != newRRB.GetGeneration() or
when the Ready condition's ObservedGeneration differs between old and new
(similar to how isResourceReleaseBindingReady uses ObservedGeneration), i.e.,
extract the Ready condition from oldRRB and newRRB and compare their
ObservedGeneration fields and short-circuit true if they differ.

In `@internal/pipeline/component/context/types.go`:
- Around line 403-416: The flattener appends every resource's Volumes into
mergedVolumes, causing duplicate VolumeEntry names when the same dependency is
referenced twice; modify the loop that builds mergedVolumes (in the same
function handling data.Resources and mergedVolumes) to deduplicate by
VolumeEntry.Name: keep a local map[string]struct{} (e.g., seenVolumeNames) and
only append a VolumeEntry to mergedVolumes if its Name is not in the map, then
mark it seen; leave each resource's item.Volumes unchanged and ensure you
reference the VolumeEntry.Name field when checking uniqueness (this will prevent
duplicate volume names produced by BuildResourceDependencyItem).

---

Duplicate comments:
In `@api/v1alpha1/workload_types.go`:
- Around line 242-250: The XValidation tags on the EnvBindings and FileBindings
fields only check map values; update the kubebuilder validation rule for both
EnvBindings and FileBindings so it asserts both the map key and the map value
are non-empty (i.e., check k.size() > 0 and self[k].size() > 0 in the rule
expression) so empty output-name keys are rejected; locate the annotations on
the EnvBindings and FileBindings fields and replace the existing rule string
accordingly.

---

Nitpick comments:
In `@internal/pipeline/component/context/resourcedependency_test.go`:
- Around line 270-273: The test currently ignores errors from
BuildResourceDependencyItem and then indexes into Volumes, which can hide
failures; change the calls to capture and assert on the error (e.g., first, err
:= BuildResourceDependencyItem(dep, outputs) with require.NoError(t, err) and
inside the loop again, err := BuildResourceDependencyItem(dep, outputs) with
require.NoError(t, err)) before comparing first.Volumes[0].Name and
again.Volumes[0].Name to ensure the test fails clearly if
BuildResourceDependencyItem returns an error.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: c119f6a4-57bf-41ca-8e91-a26d0efd5943

📥 Commits

Reviewing files that changed from the base of the PR and between d1efe58 and 4fc6ba5.

⛔ Files ignored due to path filters (4)
  • api/v1alpha1/zz_generated.deepcopy.go is excluded by !**/zz_generated.*
  • config/crd/bases/openchoreo.dev_componentreleases.yaml is excluded by !config/crd/bases/**
  • config/crd/bases/openchoreo.dev_releasebindings.yaml is excluded by !config/crd/bases/**
  • config/crd/bases/openchoreo.dev_workloads.yaml is excluded by !config/crd/bases/**
📒 Files selected for processing (36)
  • api/v1alpha1/releasebinding_types.go
  • api/v1alpha1/workload_types.go
  • docs/templating/context.md
  • install/helm/openchoreo-control-plane/crds/openchoreo.dev_componentreleases.yaml
  • install/helm/openchoreo-control-plane/crds/openchoreo.dev_releasebindings.yaml
  • install/helm/openchoreo-control-plane/crds/openchoreo.dev_workloads.yaml
  • internal/controller/releasebinding/controller.go
  • internal/controller/releasebinding/controller_conditions.go
  • internal/controller/releasebinding/controller_connections.go
  • internal/controller/releasebinding/controller_integration_test.go
  • internal/controller/releasebinding/controller_resourcedependencies.go
  • internal/controller/releasebinding/controller_resourcedependencies_integration_test.go
  • internal/controller/releasebinding/controller_resourcedependencies_test.go
  • internal/controller/releasebinding/controller_status.go
  • internal/controller/releasebinding/controller_watch.go
  • internal/controller/releasebinding/suite_test.go
  • internal/controller/watch.go
  • internal/controller/watch_test.go
  • internal/pipeline/component/context/builder_test.go
  • internal/pipeline/component/context/cel_extensions.go
  • internal/pipeline/component/context/cel_extensions_test.go
  • internal/pipeline/component/context/cel_return_types.go
  • internal/pipeline/component/context/resourcedependency.go
  • internal/pipeline/component/context/resourcedependency_test.go
  • internal/pipeline/component/context/types.go
  • internal/pipeline/component/context/types_test.go
  • internal/pipeline/component/pipeline.go
  • internal/pipeline/component/types.go
  • internal/validation/component/cel_env_test.go
  • samples/component-types/component-with-configs/component-with-configs.yaml
  • samples/component-types/component-with-embedded-traits/component-with-embedded-traits.yaml
  • samples/getting-started/all.yaml
  • samples/getting-started/component-types/scheduled-task.yaml
  • samples/getting-started/component-types/service.yaml
  • samples/getting-started/component-types/webapp.yaml
  • samples/getting-started/component-types/worker.yaml

Comment thread docs/templating/context.md
Comment thread install/helm/openchoreo-control-plane/crds/openchoreo.dev_workloads.yaml Outdated
Comment thread internal/controller/releasebinding/controller_watch.go Outdated
Comment thread internal/pipeline/component/context/types.go
@Mirage20

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented May 10, 2026

Copy link
Copy Markdown
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick comments (2)
internal/controller/releasebinding/controller_watch.go (2)

53-67: ⚡ Quick win

Consider validating target fields before indexing.

indexResourceDependencyTargets builds index keys without checking whether t.Namespace, t.Project, t.ResourceName, or t.Environment are non-empty. If Status.ResourceDependencyTargets contains a malformed entry (e.g., empty Project), the resulting partial key (e.g., "ns//resource/env") will be indexed and could incorrectly match queries from other malformed providers. The query-side guard at lines 248–250 prevents querying with partial keys, but a symmetric guard here would prevent indexing them in the first place.

While the controller should write well-formed status, adding a defensive check mirrors the rationale in the findConsumerReleaseBindingsForResourceReleaseBinding comment and improves robustness against bugs or races.

Proposed validation
 func indexResourceDependencyTargets(rb *openchoreov1alpha1.ReleaseBinding) []string {
 	if len(rb.Status.ResourceDependencyTargets) == 0 {
 		return nil
 	}
 	seen := make(map[string]struct{})
 	var keys []string
 	for _, t := range rb.Status.ResourceDependencyTargets {
+		// Skip malformed targets to avoid partial-key matches.
+		if t.Namespace == "" || t.Project == "" || t.ResourceName == "" || t.Environment == "" {
+			continue
+		}
 		key := makeResourceDependencyTargetKey(t.Namespace, t.Project, t.ResourceName, t.Environment)
 		if _, ok := seen[key]; !ok {
 			seen[key] = struct{}{}
 			keys = append(keys, key)
 		}
 	}
 	return keys
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/controller/releasebinding/controller_watch.go` around lines 53 - 67,
indexResourceDependencyTargets currently builds keys from
rb.Status.ResourceDependencyTargets without validating fields which can create
partial keys (e.g., "ns//resource/env"); update indexResourceDependencyTargets
to skip any target t where t.Namespace, t.Project, t.ResourceName or
t.Environment are empty before calling makeResourceDependencyTargetKey,
optionally emitting a warning via the controller logger when a malformed target
is encountered, and keep deduplication via the seen map unchanged so only
well-formed keys are indexed.

51-51: ⚡ Quick win

Clarify the comment wording.

The comment states "Exported helper kept package-private," but the function is unexported (lowercase identifier). The phrasing is contradictory.

Suggested revision
-// Exported helper kept package-private so unit tests can register the same indexer the
+// Package-private helper so unit tests can register the same indexer the
 // production setup uses on the manager's cache.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/controller/releasebinding/controller_watch.go` at line 51, The
comment above the package-private helper in controller_watch.go is
contradictory; change it to clearly state that the helper is intentionally
unexported (lowercase) so unit tests can register the same indexer — e.g., "keep
this helper unexported so unit tests can register the same indexer" — and update
the comment near the helper function (the unexported indexer registration
helper) accordingly.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@internal/controller/releasebinding/controller_watch.go`:
- Around line 53-67: indexResourceDependencyTargets currently builds keys from
rb.Status.ResourceDependencyTargets without validating fields which can create
partial keys (e.g., "ns//resource/env"); update indexResourceDependencyTargets
to skip any target t where t.Namespace, t.Project, t.ResourceName or
t.Environment are empty before calling makeResourceDependencyTargetKey,
optionally emitting a warning via the controller logger when a malformed target
is encountered, and keep deduplication via the seen map unchanged so only
well-formed keys are indexed.
- Line 51: The comment above the package-private helper in controller_watch.go
is contradictory; change it to clearly state that the helper is intentionally
unexported (lowercase) so unit tests can register the same indexer — e.g., "keep
this helper unexported so unit tests can register the same indexer" — and update
the comment near the helper function (the unexported indexer registration
helper) accordingly.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: ef03f6b8-a80d-4f5c-b542-9bc59e9d564e

📥 Commits

Reviewing files that changed from the base of the PR and between 4fc6ba5 and 7cb89ba.

⛔ Files ignored due to path filters (2)
  • config/crd/bases/openchoreo.dev_componentreleases.yaml is excluded by !config/crd/bases/**
  • config/crd/bases/openchoreo.dev_workloads.yaml is excluded by !config/crd/bases/**
📒 Files selected for processing (7)
  • api/v1alpha1/workload_types.go
  • docs/templating/context.md
  • install/helm/openchoreo-control-plane/crds/openchoreo.dev_componentreleases.yaml
  • install/helm/openchoreo-control-plane/crds/openchoreo.dev_workloads.yaml
  • internal/controller/releasebinding/controller_resourcedependencies_integration_test.go
  • internal/controller/releasebinding/controller_resourcedependencies_test.go
  • internal/controller/releasebinding/controller_watch.go
🚧 Files skipped from review as they are similar to previous changes (6)
  • install/helm/openchoreo-control-plane/crds/openchoreo.dev_componentreleases.yaml
  • install/helm/openchoreo-control-plane/crds/openchoreo.dev_workloads.yaml
  • internal/controller/releasebinding/controller_resourcedependencies_integration_test.go
  • api/v1alpha1/workload_types.go
  • docs/templating/context.md
  • internal/controller/releasebinding/controller_resourcedependencies_test.go

Mirage20 added 3 commits May 11, 2026 18:47
- Add WorkloadResourceDependency on Workload.spec.dependencies.resources[] with ref, envBindings (output to env var name) and fileBindings (output to mount path)
- Add ResourceDependencyTarget on ReleaseBindingStatus as the reverse-watch index source for ResourceReleaseBinding-to-consumer enqueue
- Add PendingResourceDependency on ReleaseBindingStatus for diagnostic surface
- Add GetDependencyResources getter on WorkloadTemplateSpec
- Regenerate DeepCopy and CRDs (Workload, ReleaseBinding, ComponentRelease)

Signed-off-by: Miraj Abeysekara <[email protected]>
- BuildResourceDependencyItem dispatches per-output on source kind: value -> literal env, secretKeyRef / configMapKeyRef -> valueFrom env, fileBindings -> volume + mount with subPath
- Deterministic volume names via r-{fnv1a32(ref:kind:name)} keep the namespace disjoint from configurations.toVolumes() (file-mount-*) and from sibling deps that share a Secret/ConfigMap
- Volume dedup per (dep.Ref, sourceKind, sourceName) so multiple files from the same Secret share one volume with multiple mounts
- ErrOutputNotResolved / ErrInvalidFileBinding sentinels let callers distinguish wait-for-resolve from permanent misconfiguration
- 20 unit tests covering dispatch matrix, dedup, deterministic ordering, length bounds, and r-/file-mount- prefix isolation

Signed-off-by: Miraj Abeysekara <[email protected]>
- Replace ConnectionEnvVar with corev1.EnvVar throughout pipeline-context so the merged dependencies.envVars can carry valueFrom-shaped resource outputs alongside literal endpoint env vars
- Extend ConnectionsContextData with Resources (per-resource view), VolumeMounts and Volumes (merged across resources); add Resources to ConnectionsData
- Extend RenderInput with ResourceDependencyItems and thread it through pipeline.go into ConnectionsData.Resources
- Update endpoint-side buildEnvVarsForConnection to return []corev1.EnvVar
- Update existing test sites (cel_extensions_test, builder_test) for the rename and the new dependency-shape keys
- Add 7 TestNewDependenciesContextData_ResourceMerge subtests covering per-item view, merged env vars, volume merging, empty/nil handling, and endpoint-only backward compat
- Update docs/templating/context.md to describe the EnvVar shape with valueFrom

Signed-off-by: Miraj Abeysekara <[email protected]>
Mirage20 added 21 commits May 11, 2026 18:47
- Add ConditionResourceDependenciesReady plus three reasons (AllResourceDependenciesReady, ResourceDependenciesPending, NoResourceDependencies) mirroring the endpoint-side ConnectionsResolved precedent
- Add IndexKeyResourceReleaseBindingOwnerEnv composite index over (project, resource, environment) on ResourceReleaseBinding, with MakeResourceReleaseBindingOwnerEnvKey and IndexResourceReleaseBindingOwnerEnv helpers, registered in SetupSharedIndexes
- Add pure helpers buildResourceDependencyTargets, allResourceDependenciesResolved, setResourceDependenciesCondition for the resolver and condition wiring that will land next; mirrors controller_connections.go shape
- 17 unit tests covering target derivation, resolved-gate semantics, condition branches, key-builder, and the round-trip invariant that the consumer-side target key matches what the index extracts from a provider RRB; empty-field guards on the index function

Signed-off-by: Miraj Abeysekara <[email protected]>
- resolveResourceDependency does the per-dep API hop via the (project, resource, environment) field index, gates on the provider's Ready condition using the typed resourcereleasebinding.ConditionReady, and dispatches outputs through BuildResourceDependencyItem to produce a ResourceDependencyItem
- resolveResourceDependencies orchestrates per-dep and aborts on the first transient API error so the caller requeues without acting on a partial view
- Failure modes surface as PendingResourceDependency entries with descriptive Reason: provider missing, multiple providers (defensive), provider not Ready, ErrOutputNotResolved, ErrInvalidFileBinding
- 9 fake-client subtests across TestResolveResourceDependency and TestResolveResourceDependencies cover each failure path including an interceptor-injected list error

Signed-off-by: Miraj Abeysekara <[email protected]>
…ator

- Extend setReadyCondition to find ConditionResourceDependenciesReady, treat it as optional (absent = pass, mirroring ConnectionsResolved), and fold it into the all-true gate
- Priority order when multiple sub-conditions are False: ConnectionsResolved -> ResourceDependenciesReady -> ResourcesReady; locked by two priority tests so a future reorder is caught
- 5 TestSetReadyConditionWithResourceDependencies subtests cover: deps False blocks Ready with its reason, deps True yields Ready, deps absent does not block (backward compat), connections wins over resource deps, resource deps wins over resources ready

Signed-off-by: Miraj Abeysekara <[email protected]>
…ncile

- Resolve resource dependencies after the existing connection resolution: populate Status.ResourceDependencyTargets and Status.PendingResourceDependencies, thread the resolved items into RenderInput.ResourceDependencyItems for the pipeline
- Add a resource-dep stability guard mirroring the connections guard: when any provider ResourceReleaseBinding is missing, not Ready, or has not yet populated a referenced output, set ConditionResourceDependenciesReady=False and return early without marking ReleaseSynced=True

Signed-off-by: Miraj Abeysekara <[email protected]>
- Add resourceDependencyTargetsIndex over ReleaseBinding.Status.ResourceDependencyTargets so consumer enqueue is O(1) by (namespace, project, resourceName, environment) key
- Add resourceReleaseBindingOutputsChangedPredicate that fires only on Status.Outputs changes and Ready condition flips; Synced and other status churn are filtered out so consumers are not re-enqueued for events they don't care about
- Add findConsumerReleaseBindingsForResourceReleaseBinding mapper with an empty-Owner guard mirroring IndexResourceReleaseBindingOwnerEnv
- Wire Watches(&ResourceReleaseBinding{}, ...) into SetupWithManager with a comment documenting why the two consumer-side watches don't form a cycle
- 19 subtests covering key builder, indexer (dedup, empty handling), mapper (positive, no consumers, wrong type, malformed RRB), predicate branches, and a round-trip test that locks consumer-side buildResourceDependencyTargets against the mapper's key construction

Signed-off-by: Miraj Abeysekara <[email protected]>
- Add EnvVarEntry, EnvVarSourceEntry, KeyRef typed structs that mirror corev1.EnvVar/EnvVarSource/KeyRef for the subset of shapes the platform emits; JSON-compatible so the rendered Pod spec is unchanged
- Switch ResourceDependencyItem, ConnectionItem, ConnectionsContextData fields and buildEnvVarsForConnection from corev1 types to the local EnvVarEntry / VolumeEntry / VolumeMountEntry. configurations.toX() and dependencies.toX() now return the same CEL element type so the canonical CCT shape ${configurations.toX() + dependencies.toX()} type-checks in the validation environment
- Add omitempty to VolumeMountEntry.SubPath so JSON serialization matches corev1.VolumeMount
- Lock the concat invariant with TestBuildComponentCELEnv_ConfigurationsDependenciesConcat covering toVolumes, toContainerVolumeMounts, and toContainerEnvs self-concat

Signed-off-by: Miraj Abeysekara <[email protected]>
- Extract populateResourceDependencyStatus helper to consolidate the three-step status-write dance for resource deps; keeps reconcileRelease readable. Add nolint:gocyclo on reconcileRelease (matches precedent on setResourcesReadyStatus and workflowrun's reconcile) since the chain is at threshold + 1 and the complexity is structural
- Drop unparam fields from newProviderRRB test helper; project always "proj" and environment always "dev" across all call sites, so hardcode them in the helper
- Switch mergedVolumeMounts and mergedVolumes in newDependenciesContextData from []T{} to make([]T, 0, len(data.Resources)) for prealloc

Signed-off-by: Miraj Abeysekara <[email protected]>
Ginkgo specs running against envtest go in _integration_test.go per
the convention used by 13 other controllers (Pattern B). Pure rename,
no content change.

Signed-off-by: Miraj Abeysekara <[email protected]>
- Add manager-backed cached client to the suite via ctrl.NewManager +
  controller.SetupSharedIndexes; covers the 6 types the shared indexes
  span. Direct k8sClient stays for existing read-after-write specs;
  k8sCachedClient is for the new field-index resolver-path specs.
- Add gate-engages spec: workload with dependencies.resources but no
  matching provider RRB engages the gate, populates Pending status,
  blocks ReleaseSynced.
- Add end-to-end env-var spec: provider RRB Ready with a value-kind
  output threads through to the rendered Deployment's container.env.
- Add concat-volumes coexistence spec: container.files +
  dependencies.fileBindings render side-by-side under
  configurations.toX() + dependencies.toX(); 3 volumes with disjoint
  file-mount-* and r-* prefixes; locks JSON-compat between the typed
  structs shared by both CEL helpers.

Signed-off-by: Miraj Abeysekara <[email protected]>
- Update getting-started CCTs (service, webapp, worker, scheduled-task) and the inline copies in all.yaml to render ${configurations.toX() + dependencies.toX()} for volumes and volumeMounts
- Update the two component-types samples (component-with-configs, component-with-embedded-traits) for consistency
- Without this, fileBindings on a resource dependency produces a Pod whose volumeMounts reference volume names absent from volumes
- No-op for samples without resource deps; the empty concat elides cleanly from the rendered manifest
- Live-validated on k3d-openchoreo: 3-volume Pod with mounts readable in the container, and url-shortener sample (4 components with endpoint deps) renders unchanged

Signed-off-by: Miraj Abeysekara <[email protected]>
- Add CEL XValidation on Workload.spec.dependencies.resources[].envBindings and .fileBindings rejecting empty map values at admission
- Regenerate CRDs (config/crd/bases + helm-shipped copies); embedded workload spec on ComponentRelease picks up the validation as expected

Signed-off-by: Miraj Abeysekara <[email protected]>
- isResourceReleaseBindingReady now requires cond.ObservedGeneration == rrb.Generation, so a stale Ready from a prior reconcile no longer gates the consumer's resolver while the provider is mid-reconcile
- Add TestIsResourceReleaseBindingReady covering ready+current, ready+stale, not-ready, and missing-condition

Signed-off-by: Miraj Abeysekara <[email protected]>
- Fix TraitContext dataplane subsection: drop the non-existent publicVirtualHost field and reference the ComponentContext dataplane shape, consistent with sibling subsections
- Expand ComponentContext dependencies: document the resources per-item view plus the merged volumeMounts and volumes lists; add ResourceDependencyItem, VolumeMount, and Volume tables; show valueFrom in the env-projection example; add a combined configurations + dependencies example
- Add helpers dependencies.toContainerVolumeMounts() and dependencies.toVolumes() to the helper-methods table
- Mirror the new shape in the TraitContext dependencies subsection

Signed-off-by: Miraj Abeysekara <[email protected]>
The XValidation rule on envBindings/fileBindings was rejected by kube-apiserver during CRD install: estimated rule cost exceeded the per-rule budget by 15.7x. Without an iteration cap, the cost estimator assumes worst-case map size.

- Add MaxProperties=50 to both maps (matches the existing MaxItems=50 cap on Resources []WorkloadResourceDependency)
- Tighten the XValidation rule to also reject empty map keys (output names), not just empty values. Empty keys would silently produce ambiguous bindings downstream. The combined rule doubles per-iteration work but stays well under budget with the new bound
- Regenerate CRDs (config/crd/bases + helm-shipped copies); the bound and rule flow through to the embedded workload spec on ComponentRelease as well

Signed-off-by: Miraj Abeysekara <[email protected]>
The previous predicate fired only on Status.Outputs change or Ready.Status flip. With isResourceReleaseBindingReady now requiring cond.ObservedGeneration == rrb.Generation, two new transitions need to re-enqueue consumers:

- A spec edit that bumps metadata.generation (Ready stays True but ObservedGeneration becomes stale → consumers gate off)
- A controller catch-up that advances Ready.ObservedGeneration without flipping Status (consumers were gated off and must re-evaluate)

Without these, consumers can stay ReleaseSynced=True against stale provider state until some unrelated event triggers reconcile.

- Add Generation comparison to the predicate's UpdateFunc
- Rename readyConditionStatusChanged to readyConditionChanged and extend it to compare ObservedGeneration as well
- Add fires_on_generation_change and fires_on_ready_observed_generation_change subtests

Signed-off-by: Miraj Abeysekara <[email protected]>
isResourceReleaseBindingReady now requires the Ready condition's ObservedGeneration to match rrb.Generation. envtest assigns Generation=1 on Create, but the integration fixtures left ObservedGeneration at 0, so the consumer resolver treated providers as not-ready and ReleaseSynced=True assertions timed out.

Signed-off-by: Miraj Abeysekara <[email protected]>
The example heading promised configurations + dependencies, but the body only injected dependency env vars. Configuration-derived envs land via envFrom (configMapRef + secretRef), so the projection needs both env (deps) and envFrom (configurations) to match the heading.

Signed-off-by: Miraj Abeysekara <[email protected]>
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.

Add workload wiring for resource dependencies

3 participants