feat(carvel): emit cross-deployment consumes on runtime config addon job - #666
Conversation
Extend job-spec-overlay sidecar format with two optional fields: runtime_config_from runtime_config_deployment When set on a boshLinkConsumer entry, generateRuntimeConfigs() now includes the link in the registry-data addon job's consumes map using the BOSH cross-deployment link resolution schema (from/deployment). Also extracts a readJobSpecOverlays() helper to share overlay-reading between generateBoshReleaseDir() and generateRuntimeConfigs(), avoiding the need for consumers to post-process the built tile to inject consumes. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…yment The runtime_config_ prefix is unnecessary — from and deployment have no meaning in job.MF, so there is no ambiguity with the existing name/type/optional fields. The shorter names also mirror the BOSH runtime config schema directly. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
This PR extends Carvel tile “job-spec-overlay” sidecars so authors can declare BOSH cross-deployment link resolution for the generated runtime config addon job, and refactors overlay reading into a shared helper.
Changes:
- Added
runtime_config_from/runtime_config_deploymentto the*.job-spec-overlay.ymlconsumes schema and propagate those into the runtime config addon job’sconsumes:map. - Introduced
readJobSpecOverlays()to share sidecar parsing betweengenerateBoshReleaseDir()andgenerateRuntimeConfigs(). - Updated unit/integration tests and sample tile testdata to cover parsing + runtime config emission.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| internal/carvel/testdata/sample-tile/packageinstalls/test-install.job-spec-overlay.yml | Adds runtime-config cross-deployment fields to the sample overlay consumes entry. |
| internal/carvel/models/job.go | Extends the runtime-config job model to support a consumes map with from/deployment. |
| internal/carvel/baker.go | Adds overlay-reading helper and emits cross-deployment consumes in generated runtime config. |
| internal/carvel/baker_test.go | Adds tests for parsing the new overlay fields and asserting consumes emission in runtime config output. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| allConsumes, err := b.readJobSpecOverlays() | ||
| if err != nil { | ||
| return err | ||
| } | ||
| deduped := b.deduplicateConsumes(allConsumes) | ||
|
|
||
| registryDataSpec, err := buildRegistryDataSpec(registryDataTemplates, registryDataProperties, deduped) |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 4 out of 4 changed files in this pull request and generated 4 comments.
Comments suppressed due to low confidence (1)
internal/carvel/baker.go:321
- PR description says the sidecar format is extended with
runtime_config_from/runtime_config_deployment, but boshLinkConsumer is extended withyaml:"from"/yaml:"deployment". Because buildRegistryDataSpec marshals []boshLinkConsumer directly, anyfrom/deploymentvalues in overlays will also be emitted into the generated registry-data job.MF, not just the runtime config addon job. If the intent is to affect only runtime configs, consider keeping job.MF overlay fields unchanged and adding separateruntime_config_*fields that are consumed only by generateRuntimeConfigs().
// boshLinkConsumer declares a BOSH link the registry-data job should consume.
// Populated from per-packageinstall *.job-spec-overlay.yml sidecar files.
// When From or Deployment is set, kiln also emits a cross-deployment consumes
// entry for the link in the runtime config addon job.
type boshLinkConsumer struct {
Name string `yaml:"name"`
Type string `yaml:"type"`
Optional bool `yaml:"optional"`
From string `yaml:"from,omitempty"`
Deployment string `yaml:"deployment,omitempty"`
}
| // JobConsumes declares cross-deployment link resolution for a runtime config addon job. | ||
| // When a job-spec-overlay declares runtime_config_from or runtime_config_deployment, | ||
| // kiln includes this entry in the addon job's consumes map in the generated metadata. |
| // When From or Deployment is set, kiln also emits a cross-deployment consumes | ||
| // entry for the link in the runtime config addon job. | ||
| type boshLinkConsumer struct { | ||
| Name string `yaml:"name"` | ||
| Type string `yaml:"type"` | ||
| Optional bool `yaml:"optional"` | ||
| Name string `yaml:"name"` | ||
| Type string `yaml:"type"` | ||
| Optional bool `yaml:"optional"` | ||
| From string `yaml:"from,omitempty"` | ||
| Deployment string `yaml:"deployment,omitempty"` |
There was a problem hiding this comment.
I don't think merging is what you want here. Deduping wholesale is reasonable.
| It("parses from and deployment fields for cross-deployment link resolution", func() { | ||
| content := ` | ||
| consumes: | ||
| - name: nats-tls | ||
| type: nats-tls | ||
| optional: false | ||
| from: nats-tls | ||
| deployment: "(( ..cf.deployment_name ))" | ||
| ` | ||
| var overlay jobSpecOverlay | ||
| err := yaml.Unmarshal([]byte(content), &overlay) | ||
| Expect(err).NotTo(HaveOccurred()) | ||
| Expect(overlay.Consumes).To(HaveLen(1)) | ||
| c := overlay.Consumes[0] | ||
| Expect(c.Name).To(Equal("nats-tls")) | ||
| Expect(c.From).To(Equal("nats-tls")) | ||
| Expect(c.Deployment).To(Equal("(( ..cf.deployment_name ))")) | ||
| }) |
- Fix stale JobConsumes doc comment (was referencing old field names) - Expand deduplicateConsumes warning to include from/deployment fields - Add tests for partial field presence (only from set, only deployment set) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.
Comments suppressed due to low confidence (1)
internal/carvel/baker.go:322
- Adding
from/deploymenttoboshLinkConsumermeansbuildRegistryDataSpec()will now serialize those fields into the registry-data job.MFconsumes:list (because it YAML-marshals[]boshLinkConsumer). BOSH job spec link consumes entries typically only allowname/type/optional, so this can produce an invalid job spec when overlays set these fields. Consider customizing YAML marshalling forboshLinkConsumer(or using a separate type) so job.MF output never includes cross-deployment fields.
Name string `yaml:"name"`
Type string `yaml:"type"`
Optional bool `yaml:"optional"`
From string `yaml:"from,omitempty"`
Deployment string `yaml:"deployment,omitempty"`
| It("emits a consumes entry when only from is set (no deployment)", func() { | ||
| content := ` | ||
| consumes: | ||
| - name: nats-tls | ||
| type: nats-tls | ||
| optional: false | ||
| from: nats-tls | ||
| ` | ||
| var overlay jobSpecOverlay | ||
| err := yaml.Unmarshal([]byte(content), &overlay) | ||
| Expect(err).NotTo(HaveOccurred()) | ||
| Expect(overlay.Consumes[0].From).To(Equal("nats-tls")) | ||
| Expect(overlay.Consumes[0].Deployment).To(BeEmpty()) | ||
| }) | ||
|
|
||
| It("emits a consumes entry when only deployment is set (no from)", func() { |
rizwanreza
left a comment
There was a problem hiding this comment.
Approved.
FWIW, Copilot has some invalid points (i.e. I wouldn't merge duplicates).
- Introduce jobMFConsumes struct to prevent from/deployment fields from leaking into the BOSH job.MF spec via buildRegistryDataSpec. The full boshLinkConsumer (including from/deployment) is only used internally; only name/type/optional are marshaled into job.MF. - Wrap os.ReadFile error in readJobSpecOverlays with the overlay path for consistent diagnostics (matches the existing YAML parse error wrapping). - Rename two It(...) test descriptions from "emits..." to "parses..." to accurately reflect that they test YAML unmarshaling, not output emission. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The jobMF prefix is overly specific to where the struct is used. boshConsumes better names what it represents: the BOSH job spec consumes schema (name/type/optional only). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
| var overlay jobSpecOverlay | ||
| err := yaml.Unmarshal([]byte(content), &overlay) | ||
| Expect(err).NotTo(HaveOccurred()) | ||
| Expect(overlay.Consumes[0].From).To(Equal("nats-tls")) | ||
| Expect(overlay.Consumes[0].Deployment).To(BeEmpty()) |
| var overlay jobSpecOverlay | ||
| err := yaml.Unmarshal([]byte(content), &overlay) | ||
| Expect(err).NotTo(HaveOccurred()) | ||
| Expect(overlay.Consumes[0].From).To(BeEmpty()) | ||
| Expect(overlay.Consumes[0].Deployment).To(Equal("(( ..cf.deployment_name ))")) |
| consumesMap := make(map[string]models.JobConsumes) | ||
| for _, c := range deduped { | ||
| if c.From != "" || c.Deployment != "" { | ||
| consumesMap[c.Name] = models.JobConsumes{ | ||
| From: c.From, |
Summary
Motivation
Tile authors currently have to post-process the built `.pivotal` to inject a `consumes:` block into the runtime config addon job after `kiln carvel bake`. This change makes it possible to declare cross-deployment link resolution natively in the sidecar files, eliminating the need for that patch step.
Example overlay
Generated runtime config addon job will include:
Test plan
🤖 Generated with Claude Code