github.com/argoproj/argo-cd/v2@v2.10.9/docs/operator-manual/applicationset/Generators-Matrix.md (about) 1 # Matrix Generator 2 3 The Matrix generator combines the parameters generated by two child generators, iterating through every combination of each generator's generated parameters. 4 5 By combining both generators parameters, to produce every possible combination, this allows you to gain the intrinsic properties of both generators. For example, a small subset of the many possible use cases include: 6 7 - *SCM Provider Generator + Cluster Generator*: Scanning the repositories of a GitHub organization for application resources, and targeting those resources to all available clusters. 8 - *Git File Generator + List Generator*: Providing a list of applications to deploy via configuration files, with optional configuration options, and deploying them to a fixed list of clusters. 9 - *Git Directory Generator + Cluster Decision Resource Generator*: Locate application resources contained within folders of a Git repository, and deploy them to a list of clusters provided via an external custom resource. 10 - And so on... 11 12 Any set of generators may be used, with the combined values of those generators inserted into the `template` parameters, as usual. 13 14 **Note**: If both child generators are Git generators, one or both of them must use the `pathParamPrefix` option to avoid conflicts when merging the child generators’ items. 15 16 ## Example: Git Directory generator + Cluster generator 17 18 As an example, imagine that we have two clusters: 19 20 - A `staging` cluster (at `https://1.2.3.4`) 21 - A `production` cluster (at `https://2.4.6.8`) 22 23 And our application YAMLs are defined in a Git repository: 24 25 - Argo Workflows controller (examples/git-generator-directory/cluster-addons/argo-workflows) 26 - Prometheus operator (/examples/git-generator-directory/cluster-addons/prometheus-operator) 27 28 Our goal is to deploy both applications onto both clusters, and, more generally, in the future to automatically deploy new applications in the Git repository, and to new clusters defined within Argo CD, as well. 29 30 For this we will use the Matrix generator, with the Git and the Cluster as child generators: 31 32 ```yaml 33 apiVersion: argoproj.io/v1alpha1 34 kind: ApplicationSet 35 metadata: 36 name: cluster-git 37 spec: 38 goTemplate: true 39 goTemplateOptions: ["missingkey=error"] 40 generators: 41 # matrix 'parent' generator 42 - matrix: 43 generators: 44 # git generator, 'child' #1 45 - git: 46 repoURL: https://github.com/argoproj/argo-cd.git 47 revision: HEAD 48 directories: 49 - path: applicationset/examples/matrix/cluster-addons/* 50 # cluster generator, 'child' #2 51 - clusters: 52 selector: 53 matchLabels: 54 argocd.argoproj.io/secret-type: cluster 55 template: 56 metadata: 57 name: '{{.path.basename}}-{{.name}}' 58 spec: 59 project: '{{index .metadata.labels "environment"}}' 60 source: 61 repoURL: https://github.com/argoproj/argo-cd.git 62 targetRevision: HEAD 63 path: '{{.path.path}}' 64 destination: 65 server: '{{.server}}' 66 namespace: '{{.path.basename}}' 67 ``` 68 69 First, the Git directory generator will scan the Git repository, discovering directories under the specified path. It discovers the argo-workflows and prometheus-operator applications, and produces two corresponding sets of parameters: 70 ```yaml 71 - path: /examples/git-generator-directory/cluster-addons/argo-workflows 72 path.basename: argo-workflows 73 74 - path: /examples/git-generator-directory/cluster-addons/prometheus-operator 75 path.basename: prometheus-operator 76 ``` 77 78 Next, the Cluster generator scans the [set of clusters defined in Argo CD](Generators-Cluster.md), finds the staging and production cluster secrets, and produce two corresponding sets of parameters: 79 ```yaml 80 - name: staging 81 server: https://1.2.3.4 82 83 - name: production 84 server: https://2.4.6.8 85 ``` 86 87 Finally, the Matrix generator will combine both sets of outputs, and produce: 88 ```yaml 89 - name: staging 90 server: https://1.2.3.4 91 path: /examples/git-generator-directory/cluster-addons/argo-workflows 92 path.basename: argo-workflows 93 94 - name: staging 95 server: https://1.2.3.4 96 path: /examples/git-generator-directory/cluster-addons/prometheus-operator 97 path.basename: prometheus-operator 98 99 - name: production 100 server: https://2.4.6.8 101 path: /examples/git-generator-directory/cluster-addons/argo-workflows 102 path.basename: argo-workflows 103 104 - name: production 105 server: https://2.4.6.8 106 path: /examples/git-generator-directory/cluster-addons/prometheus-operator 107 path.basename: prometheus-operator 108 ``` 109 (*The full example can be found [here](https://github.com/argoproj/argo-cd/tree/master/applicationset/examples/matrix).*) 110 111 ## Using Parameters from one child generator in another child generator 112 113 The Matrix generator allows using the parameters generated by one child generator inside another child generator. 114 Below is an example that uses a git-files generator in conjunction with a cluster generator. 115 116 ```yaml 117 apiVersion: argoproj.io/v1alpha1 118 kind: ApplicationSet 119 metadata: 120 name: cluster-git 121 spec: 122 goTemplate: true 123 goTemplateOptions: ["missingkey=error"] 124 generators: 125 # matrix 'parent' generator 126 - matrix: 127 generators: 128 # git generator, 'child' #1 129 - git: 130 repoURL: https://github.com/argoproj/applicationset.git 131 revision: HEAD 132 files: 133 - path: "examples/git-generator-files-discovery/cluster-config/**/config.json" 134 # cluster generator, 'child' #2 135 - clusters: 136 selector: 137 matchLabels: 138 argocd.argoproj.io/secret-type: cluster 139 kubernetes.io/environment: '{{.path.basename}}' 140 template: 141 metadata: 142 name: '{{.name}}-guestbook' 143 spec: 144 project: default 145 source: 146 repoURL: https://github.com/argoproj/applicationset.git 147 targetRevision: HEAD 148 path: "examples/git-generator-files-discovery/apps/guestbook" 149 destination: 150 server: '{{.server}}' 151 namespace: guestbook 152 ``` 153 Here is the corresponding folder structure for the git repository used by the git-files generator: 154 155 ``` 156 ├── apps 157 │ └── guestbook 158 │ ├── guestbook-ui-deployment.yaml 159 │ ├── guestbook-ui-svc.yaml 160 │ └── kustomization.yaml 161 ├── cluster-config 162 │ └── engineering 163 │ ├── dev 164 │ │ └── config.json 165 │ └── prod 166 │ └── config.json 167 └── git-generator-files.yaml 168 ``` 169 In the above example, the `{{.path.basename}}` parameters produced by the git-files generator will resolve to `dev` and `prod`. 170 In the 2nd child generator, the label selector with label `kubernetes.io/environment: {{.path.basename}}` will resolve with the values produced by the first child generator's parameters (`kubernetes.io/environment: prod` and `kubernetes.io/environment: dev`). 171 172 So in the above example, clusters with the label `kubernetes.io/environment: prod` will have only prod-specific configuration (ie. `prod/config.json`) applied to it, wheres clusters 173 with the label `kubernetes.io/environment: dev` will have only dev-specific configuration (ie. `dev/config.json`) 174 175 ## Overriding parameters from one child generator in another child generator 176 177 The Matrix Generator allows parameters with the same name to be defined in multiple child generators. This is useful, for example, to define default values for all stages in one generator and override them with stage-specific values in another generator. The example below generates a Helm-based application using a matrix generator with two git generators: the first provides stage-specific values (one directory per stage) and the second provides global values for all stages. 178 179 ```yaml 180 apiVersion: argoproj.io/v1alpha1 181 kind: ApplicationSet 182 metadata: 183 name: parameter-override-example 184 spec: 185 generators: 186 - matrix: 187 generators: 188 - git: 189 repoURL: https://github.com/example/values.git 190 revision: HEAD 191 files: 192 - path: "**/stage.values.yaml" 193 - git: 194 repoURL: https://github.com/example/values.git 195 revision: HEAD 196 files: 197 - path: "global.values.yaml" 198 goTemplate: true 199 template: 200 metadata: 201 name: example 202 spec: 203 project: default 204 source: 205 repoURL: https://github.com/example/example-app.git 206 targetRevision: HEAD 207 path: . 208 helm: 209 values: | 210 {{ `{{ . | mustToPrettyJson }}` }} 211 destination: 212 server: in-cluster 213 namespace: default 214 ``` 215 216 Given the following structure/content of the example/values repository: 217 218 ``` 219 ├── test 220 │ └── stage.values.yaml 221 │ stageName: test 222 │ cpuRequest: 100m 223 │ debugEnabled: true 224 ├── staging 225 │ └── stage.values.yaml 226 │ stageName: staging 227 ├── production 228 │ └── stage.values.yaml 229 │ stageName: production 230 │ memoryLimit: 512Mi 231 │ debugEnabled: false 232 └── global.values.yaml 233 cpuRequest: 200m 234 memoryLimit: 256Mi 235 debugEnabled: true 236 ``` 237 238 The matrix generator above would yield the following results: 239 240 ```yaml 241 - stageName: test 242 cpuRequest: 100m 243 memoryLimit: 256Mi 244 debugEnabled: true 245 246 - stageName: staging 247 cpuRequest: 200m 248 memoryLimit: 256Mi 249 debugEnabled: true 250 251 - stageName: production 252 cpuRequest: 200m 253 memoryLimit: 512Mi 254 debugEnabled: false 255 ``` 256 257 ## Example: Two Git Generators Using `pathParamPrefix` 258 259 The matrix generator will fail if its children produce results containing identical keys with differing values. 260 This poses a problem for matrix generators where both children are Git generators since they auto-populate `path`-related parameters in their outputs. 261 To avoid this problem, specify a `pathParamPrefix` on one or both of the child generators to avoid conflicting parameter keys in the output. 262 263 ```yaml 264 apiVersion: argoproj.io/v1alpha1 265 kind: ApplicationSet 266 metadata: 267 name: two-gits-with-path-param-prefix 268 spec: 269 goTemplate: true 270 goTemplateOptions: ["missingkey=error"] 271 generators: 272 - matrix: 273 generators: 274 # git file generator referencing files containing details about each 275 # app to be deployed (e.g., `appName`). 276 - git: 277 repoURL: https://github.com/some-org/some-repo.git 278 revision: HEAD 279 files: 280 - path: "apps/*.json" 281 pathParamPrefix: app 282 # git file generator referencing files containing details about 283 # locations to which each app should deploy (e.g., `region` and 284 # `clusterName`). 285 - git: 286 repoURL: https://github.com/some-org/some-repo.git 287 revision: HEAD 288 files: 289 - path: "targets/{{.appName}}/*.json" 290 pathParamPrefix: target 291 template: {} # ... 292 ``` 293 294 Then, given the following file structure/content: 295 296 ``` 297 ├── apps 298 │ ├── app-one.json 299 │ │ { "appName": "app-one" } 300 │ └── app-two.json 301 │ { "appName": "app-two" } 302 └── targets 303 ├── app-one 304 │ ├── east-cluster-one.json 305 │ │ { "region": "east", "clusterName": "cluster-one" } 306 │ └── east-cluster-two.json 307 │ { "region": "east", "clusterName": "cluster-two" } 308 └── app-two 309 ├── east-cluster-one.json 310 │ { "region": "east", "clusterName": "cluster-one" } 311 └── west-cluster-three.json 312 { "region": "west", "clusterName": "cluster-three" } 313 ``` 314 315 …the matrix generator above would yield the following results: 316 317 ```yaml 318 - appName: app-one 319 app.path: /apps 320 app.path.filename: app-one.json 321 # plus additional path-related parameters from the first child generator, all 322 # prefixed with "app". 323 region: east 324 clusterName: cluster-one 325 target.path: /targets/app-one 326 target.path.filename: east-cluster-one.json 327 # plus additional path-related parameters from the second child generator, all 328 # prefixed with "target". 329 330 - appName: app-one 331 app.path: /apps 332 app.path.filename: app-one.json 333 region: east 334 clusterName: cluster-two 335 target.path: /targets/app-one 336 target.path.filename: east-cluster-two.json 337 338 - appName: app-two 339 app.path: /apps 340 app.path.filename: app-two.json 341 region: east 342 clusterName: cluster-one 343 target.path: /targets/app-two 344 target.path.filename: east-cluster-one.json 345 346 - appName: app-two 347 app.path: /apps 348 app.path.filename: app-two.json 349 region: west 350 clusterName: cluster-three 351 target.path: /targets/app-two 352 target.path.filename: west-cluster-three.json 353 ``` 354 355 ## Restrictions 356 357 1. The Matrix generator currently only supports combining the outputs of only two child generators (eg does not support generating combinations for 3 or more). 358 359 1. You should specify only a single generator per array entry, eg this is not valid: 360 361 - matrix: 362 generators: 363 - list: # (...) 364 git: # (...) 365 366 - While this *will* be accepted by Kubernetes API validation, the controller will report an error on generation. Each generator should be specified in a separate array element, as in the examples above. 367 368 1. The Matrix generator does not currently support [`template` overrides](Template.md#generator-templates) specified on child generators, eg this `template` will not be processed: 369 370 - matrix: 371 generators: 372 - list: 373 elements: 374 - # (...) 375 template: { } # Not processed 376 377 1. Combination-type generators (matrix or merge) can only be nested once. For example, this will not work: 378 379 - matrix: 380 generators: 381 - matrix: 382 generators: 383 - matrix: # This third level is invalid. 384 generators: 385 - list: 386 elements: 387 - # (...) 388 389 1. When using parameters from one child generator inside another child generator, the child generator that *consumes* the parameters **must come after** the child generator that *produces* the parameters. 390 For example, the below example would be invalid (cluster-generator must come after the git-files generator): 391 392 - matrix: 393 generators: 394 # cluster generator, 'child' #1 395 - clusters: 396 selector: 397 matchLabels: 398 argocd.argoproj.io/secret-type: cluster 399 kubernetes.io/environment: '{{.path.basename}}' # {{.path.basename}} is produced by git-files generator 400 # git generator, 'child' #2 401 - git: 402 repoURL: https://github.com/argoproj/applicationset.git 403 revision: HEAD 404 files: 405 - path: "examples/git-generator-files-discovery/cluster-config/**/config.json" 406 407 1. You cannot have both child generators consuming parameters from each another. In the example below, the cluster generator is consuming the `{{.path.basename}}` parameter produced by the git-files generator, whereas the git-files generator is consuming the `{{.name}}` parameter produced by the cluster generator. This will result in a circular dependency, which is invalid. 408 409 - matrix: 410 generators: 411 # cluster generator, 'child' #1 412 - clusters: 413 selector: 414 matchLabels: 415 argocd.argoproj.io/secret-type: cluster 416 kubernetes.io/environment: '{{.path.basename}}' # {{.path.basename}} is produced by git-files generator 417 # git generator, 'child' #2 418 - git: 419 repoURL: https://github.com/argoproj/applicationset.git 420 revision: HEAD 421 files: 422 - path: "examples/git-generator-files-discovery/cluster-config/engineering/{{.name}}**/config.json" # {{.name}} is produced by cluster generator 423 424 1. When using a Matrix generator nested inside another Matrix or Merge generator, [Post Selectors](Generators-Post-Selector.md) for this nested generator's generators will only be applied when enabled via `spec.applyNestedSelectors`. You may also need to enable this even if your Post Selectors are not within the nested matrix or Merge generator, but are instead a sibling of a nested Matrix or Merge generator. 425 426 - matrix: 427 generators: 428 - matrix: 429 generators: 430 - list 431 elements: 432 - # (...) 433 selector: { } # Only applied when applyNestedSelectors is true