Skip to content

Commit

Permalink
feat(pipelines): Allow specifying a VPC for pipelines.CdkPipeline, st…
Browse files Browse the repository at this point in the history
…andardNpmSynth, and standardYarnSynth (#10453)

feat(pipelines): Allow specifying a VPC for pipelines.CdkPipeline, standardNpmSynth, and standardYarnSynth. Fixes #9982.

----

*By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
  • Loading branch information
markusl committed Sep 22, 2020
1 parent 994d3c3 commit 2e0824b
Show file tree
Hide file tree
Showing 7 changed files with 187 additions and 1 deletion.
3 changes: 3 additions & 0 deletions packages/@aws-cdk/pipelines/README.md
Expand Up @@ -135,6 +135,9 @@ class MyPipelineStack extends Stack {
sourceArtifact,
cloudAssemblyArtifact,

// Optionally specify a VPC in which the action runs
vpc: new ec2.Vpc(this, 'NpmSynthVpc'),

// Use this if you need a build step (if you're not using ts-node
// or if you have TypeScript Lambdas that need to be compiled).
buildCommand: 'npm run build',
Expand Down
19 changes: 19 additions & 0 deletions packages/@aws-cdk/pipelines/lib/actions/publish-assets-action.ts
@@ -1,6 +1,7 @@
import * as codebuild from '@aws-cdk/aws-codebuild';
import * as codepipeline from '@aws-cdk/aws-codepipeline';
import * as codepipeline_actions from '@aws-cdk/aws-codepipeline-actions';
import * as ec2 from '@aws-cdk/aws-ec2';
import * as events from '@aws-cdk/aws-events';
import * as iam from '@aws-cdk/aws-iam';
import { Construct, Lazy } from '@aws-cdk/core';
Expand Down Expand Up @@ -59,6 +60,22 @@ export interface PublishAssetsActionProps {
* @default - Automatically generated
*/
readonly role?: iam.IRole;

/**
* The VPC where to execute the PublishAssetsAction.
*
* @default - No VPC
*/
readonly vpc?: ec2.IVpc;

/**
* Which subnets to use.
*
* Only used if 'vpc' is supplied.
*
* @default - All private subnets.
*/
readonly subnetSelection?: ec2.SubnetSelection;
}

/**
Expand All @@ -85,6 +102,8 @@ export class PublishAssetsAction extends Construct implements codepipeline.IActi
buildImage: codebuild.LinuxBuildImage.STANDARD_4_0,
privileged: (props.assetType === AssetType.DOCKER_IMAGE) ? true : undefined,
},
vpc: props.vpc,
subnetSelection: props.subnetSelection,
buildSpec: codebuild.BuildSpec.fromObject({
version: '0.2',
phases: {
Expand Down
23 changes: 23 additions & 0 deletions packages/@aws-cdk/pipelines/lib/pipeline.ts
@@ -1,5 +1,6 @@
import * as path from 'path';
import * as codepipeline from '@aws-cdk/aws-codepipeline';
import * as ec2 from '@aws-cdk/aws-ec2';
import * as iam from '@aws-cdk/aws-iam';
import { Annotations, App, CfnOutput, Construct, PhysicalName, Stack, Stage, Aspects } from '@aws-cdk/core';
import { AssetType, DeployCdkStackAction, PublishAssetsAction, UpdatePipelineAction } from './actions';
Expand Down Expand Up @@ -63,6 +64,22 @@ export interface CdkPipelineProps {
* @default - Latest version
*/
readonly cdkCliVersion?: string;

/**
* The VPC where to execute the CdkPipeline actions.
*
* @default - No VPC
*/
readonly vpc?: ec2.IVpc;

/**
* Which subnets to use.
*
* Only used if 'vpc' is supplied.
*
* @default - All private subnets.
*/
readonly subnetSelection?: ec2.SubnetSelection;
}

/**
Expand Down Expand Up @@ -147,6 +164,8 @@ export class CdkPipeline extends Construct {
cdkCliVersion: props.cdkCliVersion,
pipeline: this._pipeline,
projectName: maybeSuffix(props.pipelineName, '-publish'),
vpc: props.vpc,
subnetSelection: props.subnetSelection,
});

Aspects.of(this).add({ visit: () => this._assets.removeAssetsStageIfEmpty() });
Expand Down Expand Up @@ -294,6 +313,8 @@ interface AssetPublishingProps {
readonly pipeline: codepipeline.Pipeline;
readonly cdkCliVersion?: string;
readonly projectName?: string;
readonly vpc?: ec2.IVpc;
readonly subnetSelection?: ec2.SubnetSelection;
}

/**
Expand Down Expand Up @@ -361,6 +382,8 @@ class AssetPublishing extends Construct {
cdkCliVersion: this.props.cdkCliVersion,
assetType: command.assetType,
role: this.assetRoles[command.assetType],
vpc: this.props.vpc,
subnetSelection: this.props.subnetSelection,
});
this.stage.addAction(action);
}
Expand Down
23 changes: 23 additions & 0 deletions packages/@aws-cdk/pipelines/lib/synths/simple-synth-action.ts
Expand Up @@ -3,6 +3,7 @@ import * as path from 'path';
import * as codebuild from '@aws-cdk/aws-codebuild';
import * as codepipeline from '@aws-cdk/aws-codepipeline';
import * as codepipeline_actions from '@aws-cdk/aws-codepipeline-actions';
import * as ec2 from '@aws-cdk/aws-ec2';
import * as events from '@aws-cdk/aws-events';
import * as iam from '@aws-cdk/aws-iam';
import { Construct, Stack } from '@aws-cdk/core';
Expand Down Expand Up @@ -88,6 +89,22 @@ export interface SimpleSynthOptions {
* @default - No policy statements added to CodeBuild Project Role
*/
readonly rolePolicyStatements?: iam.PolicyStatement[];

/**
* The VPC where to execute the SimpleSynth.
*
* @default - No VPC
*/
readonly vpc?: ec2.IVpc;

/**
* Which subnets to use.
*
* Only used if 'vpc' is supplied.
*
* @default - All private subnets.
*/
readonly subnetSelection?: ec2.SubnetSelection;
}

/**
Expand Down Expand Up @@ -186,6 +203,8 @@ export class SimpleSynthAction implements codepipeline.IAction, iam.IGrantable {
...options,
installCommand: options.installCommand ?? 'npm ci',
synthCommand: options.synthCommand ?? 'npx cdk synth',
vpc: options.vpc,
subnetSelection: options.subnetSelection,
});
}

Expand All @@ -201,6 +220,8 @@ export class SimpleSynthAction implements codepipeline.IAction, iam.IGrantable {
...options,
installCommand: options.installCommand ?? 'yarn install --frozen-lockfile',
synthCommand: options.synthCommand ?? 'npx cdk synth',
vpc: options.vpc,
subnetSelection: options.subnetSelection,
});
}

Expand Down Expand Up @@ -314,6 +335,8 @@ export class SimpleSynthAction implements codepipeline.IAction, iam.IGrantable {
const project = new codebuild.PipelineProject(scope, 'CdkBuildProject', {
projectName: this.props.projectName,
environment,
vpc: this.props.vpc,
subnetSelection: this.props.subnetSelection,
buildSpec,
environmentVariables,
});
Expand Down
83 changes: 83 additions & 0 deletions packages/@aws-cdk/pipelines/test/builds.test.ts
Expand Up @@ -2,6 +2,7 @@ import { arrayWith, deepObjectLike, encodedJson, objectLike, Capture } from '@aw
import '@aws-cdk/assert/jest';
import * as cbuild from '@aws-cdk/aws-codebuild';
import * as codepipeline from '@aws-cdk/aws-codepipeline';
import * as ec2 from '@aws-cdk/aws-ec2';
import * as s3 from '@aws-cdk/aws-s3';
import { Stack } from '@aws-cdk/core';
import * as cdkp from '../lib';
Expand Down Expand Up @@ -218,6 +219,88 @@ test('Standard (NPM) synth can output additional artifacts', () => {
});
});

test('Standard (NPM) synth can run in a VPC', () => {
// WHEN
new TestGitHubNpmPipeline(pipelineStack, 'Cdk', {
sourceArtifact,
cloudAssemblyArtifact,
synthAction: cdkp.SimpleSynthAction.standardNpmSynth({
vpc: new ec2.Vpc(pipelineStack, 'NpmSynthTestVpc'),
sourceArtifact,
cloudAssemblyArtifact,
}),
});

// THEN
expect(pipelineStack).toHaveResourceLike('AWS::CodeBuild::Project', {
VpcConfig: {
SecurityGroupIds: [
{
'Fn::GetAtt': [
'CdkPipelineBuildSynthCdkBuildProjectSecurityGroupEA44D7C2',
'GroupId',
],
},
],
Subnets: [
{
Ref: 'NpmSynthTestVpcPrivateSubnet1Subnet81E3AA56',
},
{
Ref: 'NpmSynthTestVpcPrivateSubnet2SubnetC1CA3EF0',
},
{
Ref: 'NpmSynthTestVpcPrivateSubnet3SubnetA04163EE',
},
],
VpcId: {
Ref: 'NpmSynthTestVpc5E703F25',
},
},
});
});

test('Standard (Yarn) synth can run in a VPC', () => {
// WHEN
new TestGitHubNpmPipeline(pipelineStack, 'Cdk', {
sourceArtifact,
cloudAssemblyArtifact,
synthAction: cdkp.SimpleSynthAction.standardYarnSynth({
vpc: new ec2.Vpc(pipelineStack, 'YarnSynthTestVpc'),
sourceArtifact,
cloudAssemblyArtifact,
}),
});

// THEN
expect(pipelineStack).toHaveResourceLike('AWS::CodeBuild::Project', {
VpcConfig: {
SecurityGroupIds: [
{
'Fn::GetAtt': [
'CdkPipelineBuildSynthCdkBuildProjectSecurityGroupEA44D7C2',
'GroupId',
],
},
],
Subnets: [
{
Ref: 'YarnSynthTestVpcPrivateSubnet1Subnet2805334B',
},
{
Ref: 'YarnSynthTestVpcPrivateSubnet2SubnetDCFBF596',
},
{
Ref: 'YarnSynthTestVpcPrivateSubnet3SubnetE11E0C86',
},
],
VpcId: {
Ref: 'YarnSynthTestVpc5F654735',
},
},
});
});

test('Pipeline action contains a hash that changes as the buildspec changes', () => {
const hash1 = synthWithAction((sa, cxa) => cdkp.SimpleSynthAction.standardNpmSynth({
sourceArtifact: sa,
Expand Down
35 changes: 34 additions & 1 deletion packages/@aws-cdk/pipelines/test/pipeline-assets.test.ts
@@ -1,9 +1,9 @@
import * as path from 'path';
import { arrayWith, deepObjectLike, encodedJson, notMatching, objectLike, stringLike } from '@aws-cdk/assert';
import '@aws-cdk/assert/jest';
import * as ecr_assets from '@aws-cdk/aws-ecr-assets';
import * as s3_assets from '@aws-cdk/aws-s3-assets';
import { Construct, Stack, Stage, StageProps } from '@aws-cdk/core';
import * as path from 'path';
import * as cdkp from '../lib';
import { BucketStack, PIPELINE_ENV, TestApp, TestGitHubNpmPipeline } from './testutil';

Expand Down Expand Up @@ -156,6 +156,39 @@ test('docker image asset publishers use privilegedmode, have right AssumeRole',
});
});

test('docker image asset can use a VPC', () => {
// WHEN
pipeline.addApplicationStage(new DockerAssetApp(app, 'DockerAssetApp'));

// THEN
expect(pipelineStack).toHaveResourceLike('AWS::CodeBuild::Project', {
VpcConfig: objectLike({
SecurityGroupIds: [
{
'Fn::GetAtt': [
'CdkAssetsDockerAsset1SecurityGroup078F5C66',
'GroupId',
],
},
],
Subnets: [
{
Ref: 'TestVpcPrivateSubnet1SubnetCC65D771',
},
{
Ref: 'TestVpcPrivateSubnet2SubnetDE0C64A2',
},
{
Ref: 'TestVpcPrivateSubnet3Subnet2311D32F',
},
],
VpcId: {
Ref: 'TestVpcE77CE678',
},
}),
});
});

test('can control fix/CLI version used in pipeline selfupdate', () => {
// WHEN
const stack2 = new Stack(app, 'Stack2', { env: PIPELINE_ENV });
Expand Down
2 changes: 2 additions & 0 deletions packages/@aws-cdk/pipelines/test/testutil.ts
Expand Up @@ -2,6 +2,7 @@ import * as fs from 'fs';
import * as path from 'path';
import * as codepipeline from '@aws-cdk/aws-codepipeline';
import * as codepipeline_actions from '@aws-cdk/aws-codepipeline-actions';
import * as ec2 from '@aws-cdk/aws-ec2';
import * as s3 from '@aws-cdk/aws-s3';
import { App, AppProps, Construct, Environment, SecretValue, Stack, StackProps, Stage } from '@aws-cdk/core';
import * as cdkp from '../lib';
Expand Down Expand Up @@ -45,6 +46,7 @@ export class TestGitHubNpmPipeline extends cdkp.CdkPipeline {
sourceArtifact,
cloudAssemblyArtifact,
}),
vpc: new ec2.Vpc(scope, 'TestVpc'),
cloudAssemblyArtifact,
...props,
});
Expand Down

0 comments on commit 2e0824b

Please sign in to comment.