Skip to content

Commit bf51aa0

Browse files
authored
fix(template): support template strings in ConfigTemplate.configs (#5796)
* fix(template): support template strings in `ConfigTemplate.configs` * test(template): fix some test assertions * test(template): fix more test assertions * test(template): add new test case * fix(template): pass config source to string resolver
1 parent ada699a commit bf51aa0

File tree

7 files changed

+103
-11
lines changed

7 files changed

+103
-11
lines changed

core/src/config/render-template.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import type { Garden } from "../garden.js"
2727
import { ConfigurationError, GardenError } from "../exceptions.js"
2828
import { resolve, posix } from "path"
2929
import fsExtra from "fs-extra"
30+
3031
const { ensureDir } = fsExtra
3132
import type { TemplatedModuleConfig } from "../plugins/templated.js"
3233
import { omit } from "lodash-es"
@@ -273,9 +274,16 @@ async function renderConfigs({
273274
renderConfig: RenderTemplateConfig
274275
}): Promise<TemplatableConfig[]> {
275276
const templateDescription = `${configTemplateKind} '${template.name}'`
277+
const templateConfigs = template.configs || []
278+
const partiallyResolvedTemplateConfigs = resolveTemplateStrings({
279+
value: templateConfigs,
280+
context,
281+
contextOpts: { allowPartial: true },
282+
source: { yamlDoc: template.internal.yamlDoc, basePath: ["inputs"] },
283+
})
276284

277285
return Promise.all(
278-
(template.configs || []).map(async (m) => {
286+
partiallyResolvedTemplateConfigs.map(async (m) => {
279287
// Resolve just the name, which must be immediately resolvable
280288
let resolvedName = m.name
281289

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
apiVersion: garden.io/v1
2+
kind: Project
3+
name: config-templates-with-templating
4+
environments:
5+
- name: local
6+
providers:
7+
- name: local-kubernetes
8+
environments: [local]
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
kind: RenderTemplate
2+
template: template-runs
3+
name: my-runs
4+
inputs:
5+
names: ["run-a", "run-b"]
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"type": "object",
3+
"properties": {
4+
"names": {
5+
"type": "array"
6+
}
7+
}
8+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
kind: ConfigTemplate
2+
name: template-runs
3+
inputsSchemaPath: schema.json
4+
5+
configs:
6+
- $concat:
7+
$forEach: ${inputs.names}
8+
$return:
9+
kind: Run
10+
type: exec
11+
name: "${item.value}"
12+
spec:
13+
command: ["echo", "${item.value}"]

core/test/unit/src/config/workflow.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -269,8 +269,8 @@ describe("resolveWorkflowConfig", () => {
269269
}
270270

271271
expect(workflow).to.exist
272-
expect(workflow.steps[0].script).to.equal('echo "${inputs.envName}"') // <- resolved later
273-
expect(omit(workflow.internal, "yamlDoc")).to.eql(internal)
272+
expect(workflow.steps[0].script).to.equal('echo "${environment.name}"') // <- resolved later
273+
expect(omit(workflow.internal, "yamlDoc")).to.eql(internal) // <- `inputs.envName` should be resolved
274274
})
275275

276276
describe("populateNamespaceForTriggers", () => {

core/test/unit/src/garden.ts

Lines changed: 58 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ import { fileURLToPath } from "node:url"
7878
import { resolveMsg } from "../../../src/logger/log-entry.js"
7979
import { getCloudDistributionName } from "../../../src/util/cloud.js"
8080
import { styles } from "../../../src/logger/styles.js"
81+
import type { RunActionConfig } from "../../../src/actions/run.js"
8182

8283
const moduleDirName = dirname(fileURLToPath(import.meta.url))
8384

@@ -2991,8 +2992,8 @@ describe("Garden", () => {
29912992
templateName: "combo",
29922993
inputs: {
29932994
name: "test",
2994-
envName: "${environment.name}", // <- resolved later
2995-
providerKey: "${providers.test-plugin.outputs.testKey}", // <- resolved later
2995+
envName: "${environment.name}", // <- should be resolved to itself
2996+
providerKey: "${providers.test-plugin.outputs.testKey}", // <- should be resolved to itself
29962997
},
29972998
}
29982999

@@ -3001,17 +3002,66 @@ describe("Garden", () => {
30013002
expect(test).to.exist
30023003

30033004
expect(build.type).to.equal("test")
3004-
expect(build.spec.command).to.include("${inputs.name}") // <- resolved later
3005+
expect(build.spec.command).to.include(internal.inputs.name) // <- should be resolved
30053006
expect(omit(build.internal, "yamlDoc")).to.eql(internal)
30063007

3007-
expect(deploy["build"]).to.equal("${parent.name}-${inputs.name}") // <- resolved later
3008+
expect(deploy["build"]).to.equal(`${internal.parentName}-${internal.inputs.name}`) // <- should be resolved
30083009
expect(omit(deploy.internal, "yamlDoc")).to.eql(internal)
30093010

3010-
expect(test.dependencies).to.eql(["build.${parent.name}-${inputs.name}"]) // <- resolved later
3011-
expect(test.spec.command).to.eql(["echo", "${inputs.envName}", "${inputs.providerKey}"]) // <- resolved later
3011+
expect(test.dependencies).to.eql([`build.${internal.parentName}-${internal.inputs.name}`]) // <- should be resolved
3012+
expect(test.spec.command).to.eql(["echo", internal.inputs.envName, internal.inputs.providerKey]) // <- should be resolved
30123013
expect(omit(test.internal, "yamlDoc")).to.eql(internal)
30133014
})
30143015

3016+
it("should resolve actions from templated config templates", async () => {
3017+
const garden = await makeTestGarden(getDataDir("test-projects", "config-templates-with-templating"))
3018+
await garden.scanAndAddConfigs()
3019+
3020+
const configs = await garden.getRawActionConfigs()
3021+
const runs = configs.Run
3022+
expect(runs).to.be.not.empty
3023+
3024+
const runNameA = "run-a"
3025+
const runA = runs[runNameA] as RunActionConfig
3026+
expect(runA).to.exist
3027+
3028+
const runNameB = "run-b"
3029+
const runB = runs[runNameB] as RunActionConfig
3030+
expect(runA).to.exist
3031+
3032+
const internal = {
3033+
basePath: garden.projectRoot,
3034+
configFilePath: join(garden.projectRoot, "runs.garden.yml"),
3035+
parentName: "my-runs",
3036+
templateName: "template-runs",
3037+
inputs: { names: [runNameA, runNameB] },
3038+
}
3039+
3040+
const expectedRunA: Partial<RunActionConfig> = {
3041+
kind: "Run",
3042+
type: "exec",
3043+
name: runNameA,
3044+
spec: {
3045+
command: ["echo", runNameA],
3046+
},
3047+
internal,
3048+
}
3049+
expect(omit(runA, "internal")).to.eql(omit(expectedRunA, "internal"))
3050+
expect(omit(runA.internal, "yamlDoc")).to.eql(expectedRunA.internal)
3051+
3052+
const expectedRunB: Partial<RunActionConfig> = {
3053+
kind: "Run",
3054+
type: "exec",
3055+
name: runNameB,
3056+
spec: {
3057+
command: ["echo", runNameB],
3058+
},
3059+
internal,
3060+
}
3061+
expect(omit(runB, "internal")).to.eql(omit(expectedRunB, "internal"))
3062+
expect(omit(runB.internal, "yamlDoc")).to.eql(expectedRunB.internal)
3063+
})
3064+
30153065
it("should resolve a workflow from a template", async () => {
30163066
const garden = await makeTestGarden(getDataDir("test-projects", "config-templates"))
30173067
await garden.scanAndAddConfigs()
@@ -3025,12 +3075,12 @@ describe("Garden", () => {
30253075
templateName: "workflows",
30263076
inputs: {
30273077
name: "test",
3028-
envName: "${environment.name}", // <- resolved later
3078+
envName: "${environment.name}", // <- should be resolved to itself
30293079
},
30303080
}
30313081

30323082
expect(workflow).to.exist
3033-
expect(workflow.steps).to.eql([{ script: 'echo "${inputs.envName}"' }]) // <- resolved later
3083+
expect(workflow.steps).to.eql([{ script: `echo "${internal.inputs.envName}"` }]) // <- should be resolved
30343084
expect(omit(workflow.internal, "yamlDoc")).to.eql(internal)
30353085
})
30363086

0 commit comments

Comments
 (0)