Skip to content

Commit

Permalink
Merge branch 'master' into huijbers/integ-bootstrap-last2
Browse files Browse the repository at this point in the history
  • Loading branch information
rix0rrr committed May 4, 2021
2 parents 55f3c32 + affaaad commit a0d893f
Show file tree
Hide file tree
Showing 8 changed files with 103 additions and 43 deletions.
10 changes: 5 additions & 5 deletions packages/@aws-cdk/aws-codebuild/lib/project.ts
Original file line number Diff line number Diff line change
Expand Up @@ -720,7 +720,7 @@ export class Project extends ProjectBase {

const ret = new Array<CfnProject.EnvironmentVariableProperty>();
const ssmIamResources = new Array<string>();
const secretsManagerIamResources = new Array<string>();
const secretsManagerIamResources = new Set<string>();
const kmsIamResources = new Set<string>();

for (const [name, envVariable] of Object.entries(environmentVariables)) {
Expand Down Expand Up @@ -771,7 +771,7 @@ export class Project extends ProjectBase {

// if we are passed a Token, we should assume it's the ARN of the Secret
// (as the name would not work anyway, because it would be the full name, which CodeBuild does not support)
secretsManagerIamResources.push(secretArn);
secretsManagerIamResources.add(secretArn);
} else {
// check if the provided value is a full ARN of the Secret
let parsedArn: ArnComponents | undefined;
Expand All @@ -791,7 +791,7 @@ export class Project extends ProjectBase {
// If we were given just a name, it must be partial, as CodeBuild doesn't support providing full names.
// In this case, we need to accommodate for the generated suffix in the IAM resource name
: `${secretName}-??????`;
secretsManagerIamResources.push(stack.formatArn({
secretsManagerIamResources.add(stack.formatArn({
service: 'secretsmanager',
resource: 'secret',
resourceName: secretIamResourceName,
Expand Down Expand Up @@ -828,10 +828,10 @@ export class Project extends ProjectBase {
resources: ssmIamResources,
}));
}
if (secretsManagerIamResources.length !== 0) {
if (secretsManagerIamResources.size !== 0) {
principal?.grantPrincipal.addToPrincipalPolicy(new iam.PolicyStatement({
actions: ['secretsmanager:GetSecretValue'],
resources: secretsManagerIamResources,
resources: Array.from(secretsManagerIamResources),
}));
}
if (kmsIamResources.size !== 0) {
Expand Down
51 changes: 51 additions & 0 deletions packages/@aws-cdk/aws-codebuild/test/test.project.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1231,6 +1231,57 @@ export = {
test.done();
},

'when the same new secret is provided with different JSON keys, only adds the resource once'(test: Test) {
// GIVEN
const stack = new cdk.Stack();

// WHEN
const secret = new secretsmanager.Secret(stack, 'Secret');
new codebuild.PipelineProject(stack, 'Project', {
environmentVariables: {
'ENV_VAR1': {
type: codebuild.BuildEnvironmentVariableType.SECRETS_MANAGER,
value: `${secret.secretArn}:json-key1`,
},
'ENV_VAR2': {
type: codebuild.BuildEnvironmentVariableType.SECRETS_MANAGER,
value: `${secret.secretArn}:json-key2`,
},
},
});

// THEN
expect(stack).to(haveResourceLike('AWS::CodeBuild::Project', {
'Environment': {
'EnvironmentVariables': [
{
'Name': 'ENV_VAR1',
'Type': 'SECRETS_MANAGER',
'Value': { 'Fn::Join': ['', [{ 'Ref': 'SecretA720EF05' }, ':json-key1']] },
},
{
'Name': 'ENV_VAR2',
'Type': 'SECRETS_MANAGER',
'Value': { 'Fn::Join': ['', [{ 'Ref': 'SecretA720EF05' }, ':json-key2']] },
},
],
},
}));

// THEN
expect(stack).to(haveResourceLike('AWS::IAM::Policy', {
'PolicyDocument': {
'Statement': arrayWith({
'Action': 'secretsmanager:GetSecretValue',
'Effect': 'Allow',
'Resource': { 'Ref': 'SecretA720EF05' },
}),
},
}));

test.done();
},

'can be provided as the ARN attribute of a new Secret, followed by a JSON key'(test: Test) {
// GIVEN
const stack = new cdk.Stack();
Expand Down
26 changes: 7 additions & 19 deletions packages/@aws-cdk/aws-eks/lib/managed-nodegroup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,12 @@ export class Nodegroup extends Resource implements INodegroup {
throw new Error(`Minimum capacity ${this.minSize} can't be greater than desired size ${this.desiredSize}`);
}

if (props.launchTemplateSpec && props.diskSize) {
// see - https://docs.aws.amazon.com/eks/latest/userguide/launch-templates.html
// and https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-eks-nodegroup.html#cfn-eks-nodegroup-disksize
throw new Error('diskSize must be specified within the launch template');
}

if (props.instanceType && props.instanceTypes) {
throw new Error('"instanceType is deprecated, please use "instanceTypes" only.');
}
Expand Down Expand Up @@ -331,6 +337,7 @@ export class Nodegroup extends Resource implements INodegroup {
// because this doesn't have a default value, meaning the user had to explicitly configure this.
instanceTypes: instanceTypes?.map(t => t.toString()),
labels: props.labels,
launchTemplate: props.launchTemplateSpec,
releaseVersion: props.releaseVersion,
remoteAccess: props.remoteAccess ? {
ec2SshKey: props.remoteAccess.sshKeyName,
Expand All @@ -345,25 +352,6 @@ export class Nodegroup extends Resource implements INodegroup {
tags: props.tags,
});

if (props.launchTemplateSpec) {
if (props.diskSize) {
// see - https://docs.aws.amazon.com/eks/latest/userguide/launch-templates.html
// and https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-eks-nodegroup.html#cfn-eks-nodegroup-disksize
throw new Error('diskSize must be specified within the launch template');
}
/**
* Instance types can be specified either in `instanceType` or launch template but not both. AS we can not check the content of
* the provided launch template and the `instanceType` property is preferrable. We allow users to define `instanceType` property here.
* see - https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-eks-nodegroup.html#cfn-eks-nodegroup-instancetypes
*/
// TODO: update this when the L1 resource spec is updated.
resource.addPropertyOverride('LaunchTemplate', {
Id: props.launchTemplateSpec.id,
Version: props.launchTemplateSpec.version,
});
}


// managed nodegroups update the `aws-auth` on creation, but we still need to track
// its state for consistency.
if (this.cluster instanceof Cluster) {
Expand Down
4 changes: 2 additions & 2 deletions packages/@aws-cdk/aws-elasticsearch/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ To perform version upgrades without replacing the entire domain, specify the `en
import * as es from '@aws-cdk/aws-elasticsearch';

const devDomain = new es.Domain(this, 'Domain', {
version: es.ElasticsearchVersion.V7_9,
version: es.ElasticsearchVersion.V7_10,
enableVersionUpgrade: true // defaults to false
});
```
Expand Down Expand Up @@ -265,7 +265,7 @@ UltraWarm nodes can be enabled to provide a cost-effective way to store large am

```ts
const domain = new es.Domain(this, 'Domain', {
version: es.ElasticsearchVersion.V7_9,
version: es.ElasticsearchVersion.V7_10,
capacity: {
masterNodes: 2,
warmNodes: 2,
Expand Down
3 changes: 3 additions & 0 deletions packages/@aws-cdk/aws-elasticsearch/lib/domain.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,9 @@ export class ElasticsearchVersion {
/** AWS Elasticsearch 7.9 */
public static readonly V7_9 = ElasticsearchVersion.of('7.9');

/** AWS Elasticsearch 7.10 */
public static readonly V7_10 = ElasticsearchVersion.of('7.10');

/**
* Custom Elasticsearch version
* @param version custom version number
Expand Down
4 changes: 2 additions & 2 deletions packages/@aws-cdk/aws-elasticsearch/test/domain.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ test('subnets and security groups can be provided when vpc is used', () => {
vpc,
});
const domain = new Domain(stack, 'Domain', {
version: ElasticsearchVersion.V7_9,
version: ElasticsearchVersion.V7_10,
vpc,
vpcSubnets: [{ subnets: [vpc.privateSubnets[0]] }],
securityGroups: [securityGroup],
Expand Down Expand Up @@ -77,7 +77,7 @@ test('default subnets and security group when vpc is used', () => {

const vpc = new Vpc(stack, 'Vpc');
const domain = new Domain(stack, 'Domain', {
version: ElasticsearchVersion.V7_9,
version: ElasticsearchVersion.V7_10,
vpc,
});

Expand Down
30 changes: 15 additions & 15 deletions packages/aws-cdk/test/integ/cli/bootstrapping.integtest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { integTest } from '../helpers/test-helpers';
jest.setTimeout(600_000);

integTest('can bootstrap without execution', withDefaultFixture(async (fixture) => {
const bootstrapStackName = fixture.fullStackName('bootstrap-stack');
const bootstrapStackName = fixture.bootstrapStackName;

await fixture.cdkBootstrapLegacy({
toolkitStackName: bootstrapStackName,
Expand All @@ -21,7 +21,7 @@ integTest('can bootstrap without execution', withDefaultFixture(async (fixture)
}));

integTest('upgrade legacy bootstrap stack to new bootstrap stack while in use', withDefaultFixture(async (fixture) => {
const bootstrapStackName = fixture.fullStackName('bootstrap-stack');
const bootstrapStackName = fixture.bootstrapStackName;

const legacyBootstrapBucketName = `aws-cdk-bootstrap-integ-test-legacy-bckt-${randomString()}`;
const newBootstrapBucketName = `aws-cdk-bootstrap-integ-test-v2-bckt-${randomString()}`;
Expand Down Expand Up @@ -57,7 +57,7 @@ integTest('upgrade legacy bootstrap stack to new bootstrap stack while in use',
}));

integTest('can and deploy if omitting execution policies', withDefaultFixture(async (fixture) => {
const bootstrapStackName = fixture.fullStackName('bootstrap-stack');
const bootstrapStackName = fixture.bootstrapStackName;

await fixture.cdkBootstrapModern({
toolkitStackName: bootstrapStackName,
Expand All @@ -74,7 +74,7 @@ integTest('can and deploy if omitting execution policies', withDefaultFixture(as
}));

integTest('deploy new style synthesis to new style bootstrap', withDefaultFixture(async (fixture) => {
const bootstrapStackName = fixture.fullStackName('bootstrap-stack');
const bootstrapStackName = fixture.bootstrapStackName;

await fixture.cdkBootstrapModern({
toolkitStackName: bootstrapStackName,
Expand All @@ -92,7 +92,7 @@ integTest('deploy new style synthesis to new style bootstrap', withDefaultFixtur
}));

integTest('deploy new style synthesis to new style bootstrap (with docker image)', withDefaultFixture(async (fixture) => {
const bootstrapStackName = fixture.fullStackName('bootstrap-stack');
const bootstrapStackName = fixture.bootstrapStackName;

await fixture.cdkBootstrapModern({
toolkitStackName: bootstrapStackName,
Expand All @@ -110,7 +110,7 @@ integTest('deploy new style synthesis to new style bootstrap (with docker image)
}));

integTest('deploy old style synthesis to new style bootstrap', withDefaultFixture(async (fixture) => {
const bootstrapStackName = fixture.fullStackName('bootstrap-stack');
const bootstrapStackName = fixture.bootstrapStackName;

await fixture.cdkBootstrapModern({
toolkitStackName: bootstrapStackName,
Expand All @@ -126,7 +126,7 @@ integTest('deploy old style synthesis to new style bootstrap', withDefaultFixtur
}));

integTest('deploying new style synthesis to old style bootstrap fails', withDefaultFixture(async (fixture) => {
const bootstrapStackName = fixture.fullStackName('bootstrap-stack');
const bootstrapStackName = fixture.bootstrapStackName;

await fixture.cdkBootstrapLegacy({
toolkitStackName: bootstrapStackName,
Expand All @@ -143,7 +143,7 @@ integTest('deploying new style synthesis to old style bootstrap fails', withDefa
}));

integTest('can create a legacy bootstrap stack with --public-access-block-configuration=false', withDefaultFixture(async (fixture) => {
const bootstrapStackName = fixture.fullStackName('bootstrap-stack-1');
const bootstrapStackName = fixture.bootstrapStackName;

await fixture.cdkBootstrapLegacy({
verbose: true,
Expand All @@ -159,8 +159,8 @@ integTest('can create a legacy bootstrap stack with --public-access-block-config
}));

integTest('can create multiple legacy bootstrap stacks', withDefaultFixture(async (fixture) => {
const bootstrapStackName1 = fixture.fullStackName('bootstrap-stack-1');
const bootstrapStackName2 = fixture.fullStackName('bootstrap-stack-2');
const bootstrapStackName1 = `${fixture.bootstrapStackName}-1`;
const bootstrapStackName2 = `${fixture.bootstrapStackName}-2`;

// deploy two toolkit stacks into the same environment (see #1416)
// one with tags
Expand All @@ -183,7 +183,7 @@ integTest('can create multiple legacy bootstrap stacks', withDefaultFixture(asyn
integTest('can dump the template, modify and use it to deploy a custom bootstrap stack', withDefaultFixture(async (fixture) => {
let template = await fixture.cdkBootstrapModern({
// toolkitStackName doesn't matter for this particular invocation
toolkitStackName: fixture.fullStackName('bootstrap-stack'),
toolkitStackName: fixture.bootstrapStackName,
showTemplate: true,
cliOptions: {
captureStderr: false,
Expand All @@ -200,14 +200,14 @@ integTest('can dump the template, modify and use it to deploy a custom bootstrap
const filename = path.join(fixture.integTestDir, `${fixture.qualifier}-template.yaml`);
fs.writeFileSync(filename, template, { encoding: 'utf-8' });
await fixture.cdkBootstrapModern({
toolkitStackName: fixture.fullStackName('bootstrap-stack'),
toolkitStackName: fixture.bootstrapStackName,
template: filename,
cfnExecutionPolicy: 'arn:aws:iam::aws:policy/AdministratorAccess',
});
}));

integTest('switch on termination protection, switch is left alone on re-bootstrap', withDefaultFixture(async (fixture) => {
const bootstrapStackName = fixture.fullStackName('bootstrap-stack');
const bootstrapStackName = fixture.bootstrapStackName;

await fixture.cdkBootstrapModern({
verbose: true,
Expand All @@ -226,7 +226,7 @@ integTest('switch on termination protection, switch is left alone on re-bootstra
}));

integTest('add tags, left alone on re-bootstrap', withDefaultFixture(async (fixture) => {
const bootstrapStackName = fixture.fullStackName('bootstrap-stack');
const bootstrapStackName = fixture.bootstrapStackName;

await fixture.cdkBootstrapModern({
verbose: true,
Expand All @@ -247,7 +247,7 @@ integTest('add tags, left alone on re-bootstrap', withDefaultFixture(async (fixt
}));

integTest('can deploy modern-synthesized stack even if bootstrap stack name is unknown', withDefaultFixture(async (fixture) => {
const bootstrapStackName = fixture.fullStackName('bootstrap-stack');
const bootstrapStackName = fixture.bootstrapStackName;

await fixture.cdkBootstrapModern({
toolkitStackName: bootstrapStackName,
Expand Down
18 changes: 18 additions & 0 deletions packages/aws-cdk/test/integ/helpers/cdk.ts
Original file line number Diff line number Diff line change
Expand Up @@ -382,6 +382,10 @@ export class TestFixture {
});
}

public get bootstrapStackName() {
return this.fullStackName('bootstrap-stack');
}

public fullStackName(stackName: string): string;
public fullStackName(stackNames: string[]): string[];
public fullStackName(stackNames: string | string[]): string | string[] {
Expand All @@ -408,6 +412,8 @@ export class TestFixture {
public async dispose(success: boolean) {
const stacksToDelete = await this.deleteableStacks(this.stackNamePrefix);

this.sortBootstrapStacksToTheEnd(stacksToDelete);

// Bootstrap stacks have buckets that need to be cleaned
const bucketNames = stacksToDelete.map(stack => outputFromStack('BucketName', stack)).filter(defined);
await Promise.all(bucketNames.map(b => this.aws.emptyBucket(b)));
Expand Down Expand Up @@ -458,6 +464,18 @@ export class TestFixture {
.filter(s => statusFilter.includes(s.StackStatus))
.filter(s => s.RootId === undefined); // Only delete parent stacks. Nested stacks are deleted in the process
}

private sortBootstrapStacksToTheEnd(stacks: AWS.CloudFormation.Stack[]) {
stacks.sort((a, b) => {
const aBs = a.StackName.startsWith(this.bootstrapStackName);
const bBs = b.StackName.startsWith(this.bootstrapStackName);

return aBs != bBs
// '+' converts a boolean to 0 or 1
? (+aBs) - (+bBs)
: a.StackName.localeCompare(b.StackName);
});
}
}

/**
Expand Down

0 comments on commit a0d893f

Please sign in to comment.