Merge pull request #30119 from dzemanov/template-secrets-in-each
fix(scaffolder-backend): use secrets within `each` step
This commit is contained in:
@@ -0,0 +1,13 @@
|
||||
---
|
||||
'@backstage/plugin-scaffolder-backend': patch
|
||||
---
|
||||
|
||||
Enable usage of secrets within 'each' step of software templates. For example, you can now structure your `each` step like this:
|
||||
|
||||
```
|
||||
each:
|
||||
[
|
||||
{ name: "Service1", token: "${{ secrets.token1 }}" },
|
||||
{ name: "Service2", token: "${{ secrets.token2 }}" },
|
||||
]
|
||||
```
|
||||
@@ -274,6 +274,58 @@ spec:
|
||||
password: ${{ secrets.password }}
|
||||
```
|
||||
|
||||
You can also consume secrets within `each` step of the template.
|
||||
|
||||
```yaml
|
||||
apiVersion: scaffolder.backstage.io/v1beta3
|
||||
kind: Template
|
||||
metadata:
|
||||
name: v1beta3-demo
|
||||
title: Test Action template
|
||||
description: scaffolder v1beta3 template demo
|
||||
spec:
|
||||
owner: backstage/techdocs-core
|
||||
type: service
|
||||
|
||||
parameters:
|
||||
- title: Authentication
|
||||
description: Provide authentication for the resource
|
||||
required:
|
||||
- service1
|
||||
- token1
|
||||
- service2
|
||||
- token2
|
||||
properties:
|
||||
service1:
|
||||
type: string
|
||||
token1:
|
||||
type: string
|
||||
ui:field: Secret
|
||||
service2:
|
||||
type: string
|
||||
token2:
|
||||
type: string
|
||||
ui:field: Secret
|
||||
|
||||
steps:
|
||||
- id: setupAuthentication
|
||||
action: auth:create
|
||||
each:
|
||||
[
|
||||
{
|
||||
name: '${{ parameters.service1 }}',
|
||||
token: '${{ secrets.token1 }}',
|
||||
},
|
||||
{
|
||||
name: '${{ parameters.service2 }}',
|
||||
token: '${{ secrets.token2 }}',
|
||||
},
|
||||
]
|
||||
input:
|
||||
name: ${{ each.value.name }}
|
||||
token: ${{ each.value.token }}
|
||||
```
|
||||
|
||||
### Custom step layouts
|
||||
|
||||
If you find that the default layout of the form used in a particular step does not meet your needs then you can supply your own [custom step layout](./writing-custom-step-layouts.md).
|
||||
|
||||
@@ -832,6 +832,42 @@ describe('NunjucksWorkflowRunner', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('should run a step repeatedly - flat values with secrets', async () => {
|
||||
const secrets = {
|
||||
s1: 'secret-value1',
|
||||
s2: 'secret-value2',
|
||||
s3: 'secret-value3',
|
||||
};
|
||||
const task = createMockTaskWithSpec(
|
||||
{
|
||||
steps: [
|
||||
{
|
||||
id: 'test',
|
||||
name: 'name',
|
||||
each: [
|
||||
'${{ secrets.s1 }}',
|
||||
'${{ secrets.s2 }}',
|
||||
'${{ secrets.s3 }}',
|
||||
],
|
||||
action: 'jest-mock-action',
|
||||
input: { secret: '${{each.value}}' },
|
||||
},
|
||||
],
|
||||
},
|
||||
secrets,
|
||||
);
|
||||
await runner.execute(task);
|
||||
|
||||
Object.values(secrets).forEach((secret, idx) => {
|
||||
expectTaskLog(
|
||||
`info: Running step each: {"key":"${idx}","value":"***"}`,
|
||||
);
|
||||
expect(fakeActionHandler).toHaveBeenCalledWith(
|
||||
expect.objectContaining({ input: { secret } }),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it('should run a step repeatedly - object list', async () => {
|
||||
const task = createMockTaskWithSpec({
|
||||
steps: [
|
||||
@@ -862,6 +898,46 @@ describe('NunjucksWorkflowRunner', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('should run a step repeatedly - object list with secrets', async () => {
|
||||
const secrets = {
|
||||
s1: 'secret-value1',
|
||||
s2: 'secret-value2',
|
||||
};
|
||||
const names = ['Service1', 'Service2'];
|
||||
const task = createMockTaskWithSpec(
|
||||
{
|
||||
steps: [
|
||||
{
|
||||
id: 'test',
|
||||
name: 'name',
|
||||
each: [
|
||||
{ name: names[0], token: '${{ secrets.s1 }}' },
|
||||
{ name: names[1], token: '${{ secrets.s2 }}' },
|
||||
],
|
||||
action: 'jest-mock-action',
|
||||
input: {
|
||||
name: '${{each.value.name}}',
|
||||
token: '${{each.value.token}}',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
secrets,
|
||||
);
|
||||
await runner.execute(task);
|
||||
|
||||
Object.values(secrets).forEach((secret, idx) => {
|
||||
expectTaskLog(
|
||||
`info: Running step each: {"key":"${idx}","value":"[object Object]"}`,
|
||||
);
|
||||
expect(fakeActionHandler).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
input: { name: names[idx], token: secret },
|
||||
}),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it('should run a step repeatedly - object', async () => {
|
||||
const settings = {
|
||||
color: 'blue',
|
||||
|
||||
@@ -299,7 +299,12 @@ export class NunjucksWorkflowRunner implements WorkflowRunner {
|
||||
}
|
||||
|
||||
const resolvedEach =
|
||||
step.each && this.render(step.each, context, renderTemplate);
|
||||
step.each &&
|
||||
this.render(
|
||||
step.each,
|
||||
{ ...context, secrets: task.secrets ?? {} },
|
||||
renderTemplate,
|
||||
);
|
||||
|
||||
if (step.each && !resolvedEach) {
|
||||
throw new InputError(
|
||||
|
||||
Reference in New Issue
Block a user