Skip to content

Commit e1c3c2f

Browse files
committed
Add YAML merge strategy to copy operations
Signed-off-by: Stefan Prodan <[email protected]>
1 parent 70c82dc commit e1c3c2f

File tree

8 files changed

+535
-37
lines changed

8 files changed

+535
-37
lines changed

README.md

Lines changed: 32 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ apiVersion: source.extensions.fluxcd.io/v1beta1
3030
kind: ArtifactGenerator
3131
metadata:
3232
name: my-app
33-
namespace: flux-system
33+
namespace: apps
3434
spec:
3535
sources:
3636
- alias: backend
@@ -56,9 +56,9 @@ apiVersion: kustomize.toolkit.fluxcd.io/v1
5656
kind: Kustomization
5757
metadata:
5858
name: my-app
59-
namespace: flux-system
59+
namespace: apps
6060
spec:
61-
interval: 10m
61+
interval: 15m
6262
sourceRef:
6363
kind: ExternalArtifact
6464
name: my-app-composite
@@ -75,26 +75,27 @@ apiVersion: source.extensions.fluxcd.io/v1beta1
7575
kind: ArtifactGenerator
7676
metadata:
7777
name: platform-services
78-
namespace: flux-system
78+
namespace: platform
7979
spec:
8080
sources:
8181
- alias: monorepo
8282
kind: GitRepository
8383
name: platform-monorepo
8484
artifacts:
85-
- name: policy-config
85+
- name: policies
8686
copy:
8787
- from: "@monorepo/platform/policy/**"
8888
to: "@artifact/"
8989
- name: auth-service
9090
copy:
9191
- from: "@monorepo/services/auth/deploy/**"
9292
to: "@artifact/"
93+
exclude: ["*.md"]
9394
- name: api-gateway
9495
copy:
9596
- from: "@monorepo/services/gateway/deploy/**"
9697
to: "@artifact/"
97-
exclude: ["**/charts/**"]
98+
exclude: ["*.md", "**/charts/**"]
9899
```
99100
100101
Each service gets its own ExternalArtifact with an independent revision.
@@ -116,43 +117,56 @@ spec:
116117
- alias: chart
117118
kind: OCIRepository
118119
name: podinfo-chart
119-
namespace: apps
120120
- alias: repo
121121
kind: GitRepository
122122
name: podinfo-values
123-
namespace: apps
124123
artifacts:
125124
- name: podinfo-composite
126125
originRevision: "@chart" # Track chart version
127126
copy:
128127
- from: "@chart/"
129128
to: "@artifact/"
129+
- from: "@repo/charts/podinfo/values.yaml"
130+
to: "@artifact/podinfo/values.yaml"
131+
strategy: Overwrite
130132
- from: "@repo/charts/podinfo/values-prod.yaml"
131-
to: "@artifact/podinfo/values.yaml" # Override default values
133+
to: "@artifact/podinfo/values.yaml"
134+
strategy: Merge
135+
---
136+
apiVersion: helm.toolkit.fluxcd.io/v2
137+
kind: HelmRelease
138+
metadata:
139+
name: podinfo
140+
namespace: apps
141+
spec:
142+
interval: 15m
143+
releaseName: podinfo
144+
chartRef:
145+
kind: ExternalArtifact
146+
name: podinfo-composite
132147
```
133148

134149
## Key Features
135150

136151
### Content-based Revision Tracking
137152

138-
The generated artifacts are versioned as `latest@sha256:<hash>` where the hash
139-
is computed from the artifact's content. This means:
153+
The generated artifacts are versioned based on the hash computed from the artifact's content.
154+
This means:
140155

141156
- ✅ Identical content = same revision (no unnecessary reconciliations)
142-
- ✅ Any change = new revision (guaranteed updates)
157+
- ✅ Any change to included content = new revision (guaranteed updates)
143158
- ✅ Automatic change detection across all source types
144159

145-
The controller attaches the origin source revision (e.g. Git commit SHA)
146-
to the generated artifact metadata for traceability and Flux commit status updates.
147-
148160
### Flexible Copy Operations
149161

150-
Copy operations support glob patterns and exclusions:
162+
Copy operations support glob patterns, exclusions and YAML merge:
151163

152164
- `@source/file.yaml` → `@artifact/dest/` - Copy file into directory
153165
- `@source/dir/` → `@artifact/dest/` - Copy directory as subdirectory
154166
- `@source/dir/**` → `@artifact/dest/` - Copy directory contents
155167
- `@source/file.yaml` → `@artifact/existing.yaml` - Later copy overwrites earlier ones
168+
- `exclude: ["*.md"]` - Exclude matching files from copy
169+
- `strategy: Merge` - Deep merge YAML files instead of overwriting
156170

157171
## Use Cases
158172

@@ -164,7 +178,7 @@ Copy operations support glob patterns and exclusions:
164178
- **Vendor Chart Customization** - Combine upstream Helm charts from OCI registries with
165179
your organization's custom values and configuration overrides stored in Git.
166180
- **Selective Deployment** - Deploy only changed components from large repositories
167-
by decomposing them into focused artifacts.
181+
by decomposing them into dedicated artifacts.
168182

169183
## API Reference
170184

@@ -173,4 +187,4 @@ Copy operations support glob patterns and exclusions:
173187
## Contributing
174188

175189
This project is Apache 2.0 licensed and accepts contributions via GitHub pull requests.
176-
To start contributing please see the [development guide](CONTRIBUTING.md).
190+
To start contributing please see the [contrib guide](CONTRIBUTING.md).

api/v1beta1/artifactgenerator_types.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ const (
3535
AccessDeniedReason = "AccessDenied"
3636
ValidationFailedReason = "ValidationFailed"
3737
SourceFetchFailedReason = "SourceFetchFailed"
38+
OverwriteStrategy = "Overwrite"
39+
MergeStrategy = "Merge"
3840
EnabledValue = "enabled"
3941
DisabledValue = "disabled"
4042
)
@@ -143,6 +145,14 @@ type CopyOperation struct {
143145
// +kubebuilder:validation:MaxItems=100
144146
// +optional
145147
Exclude []string `json:"exclude,omitempty"`
148+
149+
// Strategy specifies the copy strategy to use.
150+
// 'Overwrite' will overwrite existing files in the destination.
151+
// 'Merge' is for merging YAML files using Helm values merge strategy.
152+
// If not specified, defaults to 'Overwrite'.
153+
// +optional
154+
// +kubebuilder:validation:Enum=Overwrite;Merge
155+
Strategy string `json:"strategy,omitempty"`
146156
}
147157

148158
// ArtifactGeneratorStatus defines the observed state of ArtifactGenerator.

config/crd/bases/source.extensions.fluxcd.io_artifactgenerators.yaml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,16 @@ spec:
6868
maxLength: 1024
6969
pattern: ^@([a-z0-9]([a-z0-9_-]*[a-z0-9])?)/(.*)$
7070
type: string
71+
strategy:
72+
description: |-
73+
Strategy specifies the copy strategy to use.
74+
'Overwrite' will overwrite existing files in the destination.
75+
'Merge' is for merging YAML files using Helm values merge strategy.
76+
If not specified, defaults to 'Overwrite'.
77+
enum:
78+
- Overwrite
79+
- Merge
80+
type: string
7181
to:
7282
description: |-
7383
To specifies the destination path within the artifact.

config/testdata/composition/generators.yaml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,5 +18,9 @@ spec:
1818
- from: "@chart/**"
1919
to: "@artifact/"
2020
exclude: ["*.md"]
21+
- from: "@repo/charts/podinfo/values.yaml"
22+
to: "@artifact/podinfo/values.yaml"
23+
strategy: Overwrite
2124
- from: "@repo/charts/podinfo/values-prod.yaml"
2225
to: "@artifact/podinfo/values.yaml"
26+
strategy: Merge

docs/spec/v1beta1/artifactgenerators.md

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -96,11 +96,12 @@ spec:
9696
to: "@artifact/"
9797
- from: "@repo/charts/podinfo/values-prod.yaml"
9898
to: "@artifact/podinfo/values.yaml"
99+
strategy: Merge # Or `Overwrite` to replace the values.yaml
99100
```
100101

101102
The above generator will create an ExternalArtifact named `podinfo-composite` in the `apps` namespace,
102-
which contains the Helm chart from the `podinfo-chart` OCI repository with the `values.yaml` overwritten
103-
by the `values-prod.yaml` file from the Git repository.
103+
which contains the Helm chart from the `podinfo-chart` OCI repository with the `values.yaml` merged with
104+
`values-prod.yaml` from the Git repository.
104105

105106
The generated ExternalArtifact can be deployed using a Flux HelmRelease, for example:
106107

@@ -263,6 +264,8 @@ Each copy operation specifies how to copy files from sources into the generated
263264
the root of the generated artifact and `path` is the relative path to a file or directory.
264265
- `exclude` (optional): A list of glob patterns to filter out from the source selection.
265266
Any file matched by `from` that also matches an exclude pattern will be ignored.
267+
- `strategy` (optional): Can be `Overwrite` (default) or `Merge`. The `Merge` strategy
268+
only works for YAML files and merges using the same logic as Helm for `values.yaml` files.
266269

267270
Copy operations use `cp`-like semantics:
268271

@@ -297,6 +300,21 @@ Examples of copy operations:
297300
- "**/testdata/**" # Excludes all files under any testdata/ dir
298301
```
299302
303+
Example of copy with `Merge` strategy:
304+
305+
```yaml
306+
# Copy the chart contents (this includes chart-name/values.yaml)
307+
- from: "@chart/"
308+
to: "@artifact/"
309+
# Merge values.yaml files - (like `helm --values values-prod.yaml`)
310+
- from: "@git/values-prod.yaml"
311+
to: "@artifact/chart-name/values.yaml"
312+
strategy: Merge
313+
```
314+
315+
**Note** that the merge strategy will replace _arrays_ entirely,
316+
the behavior is the same as Helm's `values.yaml` merging.
317+
300318
## Working with ArtifactGenerators
301319

302320
### Suspend and Resume Reconciliation

0 commit comments

Comments
 (0)