Skip to content

Commit

Permalink
feat(core): allow sparse arrays for more fields
Browse files Browse the repository at this point in the history
We now allow sparse arrays for many more config fields, such as
`services[].args` (and `command`) and in workflow step commands.

This can now be done in most places where arrays of strings are provided
in configuration.

For example, this will now work in a workflow step command (where the
step command will be run without additional arguments if
`var.serviceNames` is null):
```
[deploy, ${var.serviceNames ? var.serviceNames : null}]
```
This makes it easier to implement highly dynamic configuration and
reduce repetition in certain cases.
  • Loading branch information
thsig authored and edvald committed Oct 7, 2021
1 parent 0c85925 commit b286373
Show file tree
Hide file tree
Showing 12 changed files with 45 additions and 37 deletions.
2 changes: 1 addition & 1 deletion core/src/commands/run/workflow.ts
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ export class RunWorkflowCommand extends Command<Args, {}> {

try {
if (step.command) {
step.command = resolveTemplateStrings(step.command, stepTemplateContext)
step.command = resolveTemplateStrings(step.command, stepTemplateContext).filter((arg) => !!arg)
stepResult = await runStepCommand(stepParams)
} else if (step.script) {
step.script = resolveTemplateString(step.script, stepTemplateContext)
Expand Down
2 changes: 1 addition & 1 deletion core/src/config/workflow.ts
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,7 @@ export const workflowStepSchema = () => {
fields.
`),
command: joi
.array()
.sparseArray()
.items(joi.string())
.description(
dedent`
Expand Down
2 changes: 1 addition & 1 deletion core/src/plugins/base-volume.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export interface BaseVolumeSpec extends ModuleSpec {
export const baseVolumeSpecSchema = () =>
baseModuleSpecSchema().keys({
accessModes: joi
.array()
.sparseArray()
.items(joi.string().allow("ReadOnlyMany", "ReadWriteOnce", "ReadWriteMany"))
.required()
.unique()
Expand Down
35 changes: 19 additions & 16 deletions core/src/plugins/container/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -168,12 +168,12 @@ export interface ContainerHotReloadSpec {
const hotReloadConfigSchema = () =>
joi.object().keys({
sync: joi
.array()
.sparseArray()
.items(hotReloadSyncSchema())
.required()
.description("Specify one or more source files or directories to automatically sync into the running container."),
postSyncCommand: joi
.array()
.sparseArray()
.items(joi.string())
.optional()
.description(`An optional command to run inside the container after syncing.`)
Expand Down Expand Up @@ -271,9 +271,12 @@ export interface ContainerDevModeSpec {

export const containerDevModeSchema = () =>
joi.object().keys({
args: joi.array().items(joi.string()).description("Override the default container arguments when in dev mode."),
args: joi
.sparseArray()
.items(joi.string())
.description("Override the default container arguments when in dev mode."),
command: joi
.array()
.sparseArray()
.items(joi.string())
.description("Override the default container command (i.e. entrypoint) when in dev mode."),
sync: joi
Expand Down Expand Up @@ -374,7 +377,7 @@ const healthCheckSchema = () =>
})
.description("Set this to check the service's health by making an HTTP request."),
command: joi
.array()
.sparseArray()
.items(joi.string())
.description("Set this to check the service's health by running a command in its container."),
tcpPort: joi
Expand Down Expand Up @@ -539,14 +542,14 @@ const containerPrivilegedSchema = (targetType: string) =>

const containerAddCapabilitiesSchema = (targetType: string) =>
joi
.array()
.sparseArray()
.items(joi.string())
.optional()
.description(`POSIX capabilities to add to the running ${targetType}'s main container.`)

const containerDropCapabilitiesSchema = (targetType: string) =>
joi
.array()
.sparseArray()
.items(joi.string())
.optional()
.description(`POSIX capabilities to remove from the running ${targetType}'s main container.`)
Expand All @@ -561,12 +564,12 @@ const containerServiceSchema = () =>
`
),
command: joi
.array()
.sparseArray()
.items(joi.string().allow(""))
.description("The command/entrypoint to run the container with when starting the service.")
.example(commandExample),
args: joi
.array()
.sparseArray()
.items(joi.string().allow(""))
.description("The arguments to run the container with when starting the service.")
.example(["npm", "start"]),
Expand All @@ -581,7 +584,7 @@ const containerServiceSchema = () =>
env: containerEnvVarsSchema(),
healthCheck: healthCheckSchema().description("Specify how the service's health should be checked after deploying."),
hotReloadCommand: joi
.array()
.sparseArray()
.items(joi.string())
.description(
deline`
Expand All @@ -590,7 +593,7 @@ const containerServiceSchema = () =>
)
.example(commandExample),
hotReloadArgs: joi
.array()
.sparseArray()
.items(joi.string())
.description(
deline`
Expand Down Expand Up @@ -701,13 +704,13 @@ export interface ContainerTestSpec extends BaseTestSpec {
export const containerTestSchema = () =>
baseTestSpecSchema().keys({
args: joi
.array()
.sparseArray()
.items(joi.string().allow(""))
.description("The arguments used to run the test inside the container.")
.example(["npm", "test"]),
artifacts: artifactsSchema(),
command: joi
.array()
.sparseArray()
.items(joi.string().allow(""))
.description("The command/entrypoint used to run the test inside the container.")
.example(commandExample),
Expand Down Expand Up @@ -738,14 +741,14 @@ export const containerTaskSchema = () =>
baseTaskSpecSchema()
.keys({
args: joi
.array()
.sparseArray()
.items(joi.string().allow(""))
.description("The arguments used to run the task inside the container.")
.example(["rake", "db:migrate"]),
artifacts: artifactsSchema(),
cacheResult: cacheResultSchema(),
command: joi
.array()
.sparseArray()
.items(joi.string().allow(""))
.description("The command/entrypoint used to run the task inside the container.")
.example(commandExample),
Expand Down Expand Up @@ -803,7 +806,7 @@ export const containerModuleSpecSchema = () =>
Note: Garden will always set a \`GARDEN_MODULE_VERSION\` argument with the module version at build time.
`),
extraFlags: joi.array().items(joi.string()).description(deline`
extraFlags: joi.sparseArray().items(joi.string()).description(deline`
Specify extra flags to use when building the container image.
Note that arguments may not be portable across implementations.`),
// TODO: validate the image name format
Expand Down
10 changes: 5 additions & 5 deletions core/src/plugins/exec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ export const execServiceSchema = () =>
baseServiceSpecSchema()
.keys({
deployCommand: joi
.array()
.sparseArray()
.items(joi.string().allow(""))
.description(
dedent`
Expand All @@ -78,7 +78,7 @@ export const execServiceSchema = () =>
)
.required(),
statusCommand: joi
.array()
.sparseArray()
.items(joi.string().allow(""))
.description(
dedent`
Expand All @@ -93,7 +93,7 @@ export const execServiceSchema = () =>
`
),
cleanupCommand: joi
.array()
.sparseArray()
.items(joi.string().allow(""))
.description(
dedent`
Expand All @@ -116,7 +116,7 @@ export const execTestSchema = () =>
baseTestSpecSchema()
.keys({
command: joi
.array()
.sparseArray()
.items(joi.string().allow(""))
.description(
dedent`
Expand All @@ -142,7 +142,7 @@ export const execTaskSpecSchema = () =>
.keys({
artifacts: artifactsSchema().description("A list of artifacts to copy after the task run."),
command: joi
.array()
.sparseArray()
.items(joi.string().allow(""))
.description(
dedent`
Expand Down
12 changes: 6 additions & 6 deletions core/src/plugins/kubernetes/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -398,7 +398,7 @@ export const kubernetesConfigBase = () =>
.object()
.keys({
extraFlags: joi
.array()
.sparseArray()
.items(joi.string())
.description(
`Specify extra flags to use when building the container image with kaniko. Flags set on \`container\` modules take precedence over these.`
Expand Down Expand Up @@ -787,7 +787,7 @@ export const containerModuleSchema = () =>

export const hotReloadArgsSchema = () =>
joi
.array()
.sparseArray()
.items(joi.string())
.description("If specified, overrides the arguments for the main container when running in hot-reload mode.")
.example(["nodemon", "my-server.js"])
Expand Down Expand Up @@ -842,12 +842,12 @@ export const kubernetesTaskSchema = () =>
),
cacheResult: cacheResultSchema(),
command: joi
.array()
.sparseArray()
.items(joi.string().allow(""))
.description("The command/entrypoint used to run the task inside the container.")
.example(commandExample),
args: joi
.array()
.sparseArray()
.items(joi.string().allow(""))
.description("The arguments to pass to the container used for execution.")
.example(["rake", "db:migrate"]),
Expand All @@ -870,12 +870,12 @@ export const kubernetesTestSchema = () =>
${runPodSpecWhitelistDescription}`
),
command: joi
.array()
.sparseArray()
.items(joi.string().allow(""))
.description("The command/entrypoint used to run the test inside the container.")
.example(commandExample),
args: joi
.array()
.sparseArray()
.items(joi.string().allow(""))
.description("The arguments to pass to the container used for testing.")
.example(["npm", "test"]),
Expand Down
2 changes: 1 addition & 1 deletion core/src/plugins/openfaas/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ export interface OpenFaasModuleSpec extends ExecModuleSpecBase {
const openfaasTestSchema = () =>
baseTestSpecSchema().keys({
command: joi
.array()
.sparseArray()
.items(joi.string())
.description("The command to run in the module build context in order to test it.")
.required(),
Expand Down
2 changes: 1 addition & 1 deletion core/src/types/plugin/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ export const runResultSchema = () =>
.keys({
moduleName: joi.string().description("The name of the module that was run."),
command: joi
.array()
.sparseArray()
.items(joi.string().allow(""))
.required()
.description("The command that was run in the module."),
Expand Down
2 changes: 1 addition & 1 deletion core/src/types/plugin/task/getTaskResult.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export const taskResultSchema = () =>
moduleName: joi.string().description("The name of the module that the task belongs to."),
taskName: joi.string().description("The name of the task that was run."),
command: joi
.array()
.sparseArray()
.items(joi.string().allow(""))
.required()
.description("The command that the task ran in the module."),
Expand Down
4 changes: 2 additions & 2 deletions core/test/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,9 +95,9 @@ async function runModule(params: RunModuleParams): Promise<RunResult> {
export const projectRootA = getDataDir("test-project-a")
export const projectTestFailsRoot = getDataDir("test-project-fails")

const testModuleTestSchema = () => containerTestSchema().keys({ command: joi.array().items(joi.string()) })
const testModuleTestSchema = () => containerTestSchema().keys({ command: joi.sparseArray().items(joi.string()) })

const testModuleTaskSchema = () => containerTaskSchema().keys({ command: joi.array().items(joi.string()) })
const testModuleTaskSchema = () => containerTaskSchema().keys({ command: joi.sparseArray().items(joi.string()) })

export const testModuleSpecSchema = () =>
containerModuleSpecSchema().keys({
Expand Down
7 changes: 6 additions & 1 deletion core/test/unit/src/commands/run/workflow.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ describe("RunWorkflowCommand", () => {
{ command: ["deploy"], description: "deploy services" },
{ command: ["get", "outputs"] },
{ command: ["test"] },
{ command: ["deploy", "${var.foo}"] }, // <-- the second (null) element should get filtered out
{ command: ["run", "test", "module-a", "unit"] },
{ command: ["run", "task", "task-a"] },
{ command: ["delete", "service", "service-a"] },
Expand All @@ -63,7 +64,11 @@ describe("RunWorkflowCommand", () => {
},
])

await cmd.action({ ...defaultParams, args: { workflow: "workflow-a" } })
garden.variables = { foo: null }

const result = await cmd.action({ ...defaultParams, args: { workflow: "workflow-a" } })

expect(result.errors || []).to.eql([])
})

it("should add workflowStep metadata to log entries provided to steps", async () => {
Expand Down
2 changes: 1 addition & 1 deletion plugins/jib/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ const jibModuleSchema = () =>
.default("docker")
.description("Specify the image format in the resulting tar file. Only used if `tarOnly: true`."),
extraFlags: joi
.array()
.sparseArray()
.items(joi.string())
.description(`Specify extra flags to pass to maven/gradle when building the container image.`),
}),
Expand Down

0 comments on commit b286373

Please sign in to comment.