-
Notifications
You must be signed in to change notification settings - Fork 3.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix(pipelines): stack tags #10533
fix(pipelines): stack tags #10533
Changes from 1 commit
901857b
d9b0339
8bbe552
527c94e
4106f9d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1 @@ | ||
{"version":"5.0.0"} | ||
{"version":"6.0.0"} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
import * as cxschema from '@aws-cdk/cloud-assembly-schema'; | ||
import * as cxapi from '../lib'; | ||
import { rimraf } from './util'; | ||
|
||
let builder: cxapi.CloudAssemblyBuilder; | ||
beforeEach(() => { | ||
builder = new cxapi.CloudAssemblyBuilder(); | ||
}); | ||
|
||
afterEach(() => { | ||
rimraf(builder.outdir); | ||
}); | ||
|
||
test('read tags', () => { | ||
// GIVEN | ||
builder.addArtifact('Stack', { | ||
type: cxschema.ArtifactType.AWS_CLOUDFORMATION_STACK, | ||
environment: 'aws://1222344/us-east-1', | ||
properties: { | ||
templateFile: 'bla.json', | ||
tags: { | ||
foo: 'bar', | ||
}, | ||
}, | ||
}); | ||
|
||
// WHEN | ||
const assembly = builder.buildAssembly(); | ||
|
||
// THEN | ||
expect(assembly.getStackByName('Stack').tags).toEqual({ foo: 'bar' }); | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
import * as fs from 'fs'; | ||
import * as path from 'path'; | ||
|
||
/** | ||
* rm -rf reimplementation, don't want to depend on an NPM package for this | ||
*/ | ||
export function rimraf(fsPath: string) { | ||
try { | ||
const isDir = fs.lstatSync(fsPath).isDirectory(); | ||
|
||
if (isDir) { | ||
for (const file of fs.readdirSync(fsPath)) { | ||
rimraf(path.join(fsPath, file)); | ||
} | ||
fs.rmdirSync(fsPath); | ||
} else { | ||
fs.unlinkSync(fsPath); | ||
} | ||
} catch (e) { | ||
// We will survive ENOENT | ||
if (e.code !== 'ENOENT') { throw e; } | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,4 @@ | ||
import * as fs from 'fs'; | ||
import * as path from 'path'; | ||
import * as cfn from '@aws-cdk/aws-cloudformation'; | ||
import * as codepipeline from '@aws-cdk/aws-codepipeline'; | ||
|
@@ -112,6 +113,11 @@ export interface DeployCdkStackActionProps extends DeployCdkStackActionOptions { | |
* @default - No dependencies | ||
*/ | ||
readonly dependencyStackArtifactIds?: string[]; | ||
|
||
/** | ||
* Template configuration path relative to the input artifact | ||
*/ | ||
readonly templateConfigurationPath?: string; | ||
} | ||
|
||
/** | ||
|
@@ -156,12 +162,25 @@ export class DeployCdkStackAction implements codepipeline.IAction { | |
// It should be easier to get this, but for now it is what it is. | ||
const appAsmRoot = assemblyBuilderOf(appOf(scope)).outdir; | ||
const fullTemplatePath = path.join(artifact.assembly.directory, artifact.templateFile); | ||
const templatePath = path.relative(appAsmRoot, fullTemplatePath); | ||
|
||
let fullConfigPath; | ||
if (Object.keys(artifact.tags).length > 0) { | ||
fullConfigPath = `${fullTemplatePath}.config.json`; | ||
|
||
// Write the template configuration file (for parameters into CreateChangeSet call that | ||
// cannot be configured any other way). They must come from a file, and there's unfortunately | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What's wrong with this way? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Feels like this would be an excellent case for a construct adding an artifact to the synthesized output of a cloud assembly, as an implementation detail of the construct. What's happening now is that we're writing a file which is right next to another file that we assume is going to be in a cloud assembly, of which we kinda guess the name is probably going to be unused, which is invisible any of the rest of the framework. |
||
// no better hook to write this file (`construct.onSynthesize()` would have been the prime candidate | ||
// but that is being deprecated--and DeployCdkStackAction isn't even a construct). | ||
writeTemplateConfiguration(fullConfigPath, { | ||
Tags: artifact.tags, | ||
}); | ||
} | ||
|
||
return new DeployCdkStackAction({ | ||
actionRole, | ||
cloudFormationExecutionRole, | ||
templatePath, | ||
templatePath: path.relative(appAsmRoot, fullTemplatePath), | ||
templateConfigurationPath: fullConfigPath ? path.relative(appAsmRoot, fullConfigPath) : undefined, | ||
region, | ||
stackArtifactId: artifact.id, | ||
dependencyStackArtifactIds: artifact.dependencies.filter(isStackArtifact).map(s => s.id), | ||
|
@@ -223,6 +242,7 @@ export class DeployCdkStackAction implements codepipeline.IAction { | |
deploymentRole: props.cloudFormationExecutionRole, | ||
region: props.region, | ||
capabilities: [cfn.CloudFormationCapabilities.NAMED_IAM, cfn.CloudFormationCapabilities.AUTO_EXPAND], | ||
templateConfiguration: props.templateConfigurationPath ? props.cloudAssemblyInput.atPath(props.templateConfigurationPath) : undefined, | ||
}); | ||
this.executeChangeSetAction = new cpactions.CloudFormationExecuteChangeSetAction({ | ||
actionName: `${baseActionName}.Deploy`, | ||
|
@@ -331,3 +351,23 @@ function isStackArtifact(a: cxapi.CloudArtifact): a is cxapi.CloudFormationStack | |
// return a instanceof cxapi.CloudFormationStackArtifact; | ||
return a.constructor.name === 'CloudFormationStackArtifact'; | ||
} | ||
|
||
/** | ||
* Template configuration in a CodePipeline | ||
* | ||
* @see https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/continuous-delivery-codepipeline-cfn-artifacts.html#w2ab1c13c17c15 | ||
*/ | ||
interface TemplateConfiguration { | ||
readonly Parameters?: Record<string, string>; | ||
readonly Tags?: Record<string, string>; | ||
readonly StackPolicy?: { | ||
readonly Statements: Array<Record<string, string>>; | ||
}; | ||
} | ||
|
||
/** | ||
* Write template configuration to the given file | ||
*/ | ||
function writeTemplateConfiguration(filename: string, config: TemplateConfiguration) { | ||
fs.writeFileSync(filename, JSON.stringify(config, undefined, 2), { encoding: 'utf-8' }); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's wrong with using an npm package for tests?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's see. I can either have this 10-line function or