From 75e3551b1ec4cfcbbbb9ebe9c6d67cf4f6855b16 Mon Sep 17 00:00:00 2001 From: Adam Ruka Date: Mon, 19 Nov 2018 14:44:37 -0800 Subject: [PATCH] feat(aws-codepipeline): Jenkins build and test Actions. --- .../aws-codepipeline-api/lib/action.ts | 52 ---- .../aws-codepipeline-api/lib/build-action.ts | 9 +- .../aws-codepipeline-api/lib/source-action.ts | 2 +- .../aws-codepipeline-api/lib/test-action.ts | 9 +- packages/@aws-cdk/aws-codepipeline/README.md | 55 ++++ .../lib/custom-action-registration.ts | 145 +++++++++ .../@aws-cdk/aws-codepipeline/lib/index.ts | 2 + .../aws-codepipeline/lib/jenkins-actions.ts | 120 ++++++++ .../aws-codepipeline/lib/jenkins-provider.ts | 277 +++++++++++++++++ .../test/integ.pipeline-jenkins.expected.json | 284 ++++++++++++++++++ .../test/integ.pipeline-jenkins.ts | 39 +++ 11 files changed, 939 insertions(+), 55 deletions(-) create mode 100644 packages/@aws-cdk/aws-codepipeline/lib/custom-action-registration.ts create mode 100644 packages/@aws-cdk/aws-codepipeline/lib/jenkins-actions.ts create mode 100644 packages/@aws-cdk/aws-codepipeline/lib/jenkins-provider.ts create mode 100644 packages/@aws-cdk/aws-codepipeline/test/integ.pipeline-jenkins.expected.json create mode 100644 packages/@aws-cdk/aws-codepipeline/test/integ.pipeline-jenkins.ts diff --git a/packages/@aws-cdk/aws-codepipeline-api/lib/action.ts b/packages/@aws-cdk/aws-codepipeline-api/lib/action.ts index 0f6ef1754787d..f960cd2acf5ca 100644 --- a/packages/@aws-cdk/aws-codepipeline-api/lib/action.ts +++ b/packages/@aws-cdk/aws-codepipeline-api/lib/action.ts @@ -315,55 +315,3 @@ export abstract class Action extends cdk.Construct { // }); // } // } - -/* - TODO: A Jenkins build needs a corresponding custom action for each "Jenkins provider". - This should be created automatically. - - Example custom action created to execute Jenkins: - { - "id": { - "category": "Test", - "provider": "", - "owner": "Custom", - "version": "1" - }, - "outputArtifactDetails": { - "minimumCount": 0, - "maximumCount": 5 - }, - "settings": { - "executionUrlTemplate": "https://www.google.com/job/{Config:ProjectName}/{ExternalExecutionId}", - "entityUrlTemplate": "https://www.google.com/job/{Config:ProjectName}" - }, - "actionConfigurationProperties": [ - { - "queryable": true, - "key": true, - "name": "ProjectName", - "required": true, - "secret": false - } - ], - "inputArtifactDetails": { - "minimumCount": 0, - "maximumCount": 5 - } - } -*/ - -// export class JenkinsBuild extends BuildAction { -// constructor(parent: Stage, name: string, jenkinsProvider: string, project: string) { -// super(parent, name, jenkinsProvider, DefaultBounds(), { -// ProjectName: project -// }); -// } -// } - -// export class JenkinsTest extends TestAction { -// constructor(parent: Stage, name: string, jenkinsProvider: string, project: string) { -// super(parent, name, jenkinsProvider, DefaultBounds(), { -// ProjectName: project -// }); -// } -// } diff --git a/packages/@aws-cdk/aws-codepipeline-api/lib/build-action.ts b/packages/@aws-cdk/aws-codepipeline-api/lib/build-action.ts index da078f34a4bd1..b7310b90c54d1 100644 --- a/packages/@aws-cdk/aws-codepipeline-api/lib/build-action.ts +++ b/packages/@aws-cdk/aws-codepipeline-api/lib/build-action.ts @@ -22,12 +22,19 @@ export interface BuildActionProps extends CommonActionProps, CommonActionConstru artifactBounds: ActionArtifactBounds; /** - * The source action owner (could be 'AWS', 'ThirdParty' or 'Custom'). + * The build Action owner (could be 'AWS', 'ThirdParty' or 'Custom'). * * @default 'AWS' */ owner?: string; + /** + * The build Action version. + * + * @default '1' + */ + version?: string; + /** * The name of the build's output artifact. */ diff --git a/packages/@aws-cdk/aws-codepipeline-api/lib/source-action.ts b/packages/@aws-cdk/aws-codepipeline-api/lib/source-action.ts index 83c2a355b4f1e..f12a39efdd4b8 100644 --- a/packages/@aws-cdk/aws-codepipeline-api/lib/source-action.ts +++ b/packages/@aws-cdk/aws-codepipeline-api/lib/source-action.ts @@ -14,7 +14,7 @@ export interface SourceActionProps extends CommonActionProps, CommonActionConstr owner?: string; /** - * The source action verison. + * The source Action version. * * @default "1" */ diff --git a/packages/@aws-cdk/aws-codepipeline-api/lib/test-action.ts b/packages/@aws-cdk/aws-codepipeline-api/lib/test-action.ts index 1d20efab9c9ac..49a2cededd5b2 100644 --- a/packages/@aws-cdk/aws-codepipeline-api/lib/test-action.ts +++ b/packages/@aws-cdk/aws-codepipeline-api/lib/test-action.ts @@ -36,12 +36,19 @@ export interface TestActionProps extends CommonActionProps, CommonActionConstruc artifactBounds: ActionArtifactBounds; /** - * The source action owner (could be 'AWS', 'ThirdParty' or 'Custom'). + * The test Action owner (could be 'AWS', 'ThirdParty' or 'Custom'). * * @default 'AWS' */ owner?: string; + /** + * The test Action version. + * + * @default '1' + */ + version?: string; + /** * The action's configuration. These are key-value pairs that specify input values for an action. * For more information, see the AWS CodePipeline User Guide. diff --git a/packages/@aws-cdk/aws-codepipeline/README.md b/packages/@aws-cdk/aws-codepipeline/README.md index b86bad6267dff..8a1ae513e972e 100644 --- a/packages/@aws-cdk/aws-codepipeline/README.md +++ b/packages/@aws-cdk/aws-codepipeline/README.md @@ -95,6 +95,61 @@ but `notifyEmails` were, a new SNS Topic will be created (and accessible through the `notificationTopic` property of the Action). +#### Jenkins Actions + +In order to use Jenkins Actions in the Pipeline, +you first need to create a `JenkinsProvider`: + +```ts +const jenkinsProvider = new codepipeline.JenkinsProvider(this, 'JenkinsProvider', { + providerName: 'MyJenkinsProvider', + serverUrl: 'http://my-jenkins.com:8080', + version: '2', // optional, default: '1' +}); +``` + +If you've registered a Jenkins provider in a different CDK app, +or outside the CDK (in the CodePipeline AWS Console, for example), +you can import it: + +```ts +const jenkinsProvider = codepipeline.JenkinsProvider.import(this, 'JenkinsProvider', { + providerName: 'MyJenkinsProvider', + serverUrl: 'http://my-jenkins.com:8080', + version: '2', // optional, default: '1' +}); +``` + +Note that a Jenkins provider +(identified by the provider name-category(build/test)-version tuple) +must always be registered in the given account, in the given AWS region, +before it can be used in CodePipeline. + +With a `JenkinsProvider`, +we can create a Jenkins Action: + +```ts +const buildAction = new codepipeline.JenkinsBuildAction(this, 'JenkinsBuild', { + stage: buildStage, + jenkinsProvider: jenkinsProvider, + projectName: 'MyProject', +}); +// there's also a JenkinsTestAction that works identically +``` + +You can also add the Action to the Pipeline directly: + +```ts +// equivalent to the code above: +const buildAction = jenkinsProvider.addToPipeline(buildStage, 'JenkinsBuild', { + projectName: 'MyProject', +}); + +const testAction = jenkinsProvider.addToPipelineAsTest(buildStage, 'JenkinsTest', { + projectName: 'MyProject', +}); +``` + ### Cross-region CodePipelines You can also use the cross-region feature to deploy resources diff --git a/packages/@aws-cdk/aws-codepipeline/lib/custom-action-registration.ts b/packages/@aws-cdk/aws-codepipeline/lib/custom-action-registration.ts new file mode 100644 index 0000000000000..764f0319a9a29 --- /dev/null +++ b/packages/@aws-cdk/aws-codepipeline/lib/custom-action-registration.ts @@ -0,0 +1,145 @@ +import cpapi = require('@aws-cdk/aws-codepipeline-api'); +import cdk = require('@aws-cdk/cdk'); +import { cloudformation } from './codepipeline.generated'; + +/** + * The creation attributes used for defining a configuration property + * of a custom Action. + */ +export interface CustomActionProperty { + /** + * The name of the property. + * You use this name in the `configuration` attribute when defining your custom Action class. + */ + name: string; + + /** + * The description of the property. + * + * @default the description will be empty + */ + description?: string; + + // because of @see URLs + // tslint:disable:max-line-length + + /** + * Whether this property is a key. + * + * @default false + * @see https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-codepipeline-customactiontype-configurationproperties.html#cfn-codepipeline-customactiontype-configurationproperties-key + */ + key?: boolean; + + /** + * Whether this property is queryable. + * Note that only a single property of a custom Action can be queryable. + * + * @default false + * @see https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-codepipeline-customactiontype-configurationproperties.html#cfn-codepipeline-customactiontype-configurationproperties-queryable + */ + queryable?: boolean; + + // tslint:enable:max-line-length + + /** + * Whether this property is required. + */ + required: boolean; + + /** + * Whether this property is secret, + * like a password, or access key. + * + * @default false + */ + secret?: boolean; + + /** + * The type of the property, + * like 'String', 'Number', or 'Boolean'. + * + * @default 'String' + */ + type?: string; +} + +/** + * Properties of registering a custom Action. + */ +export interface CustomActionRegistrationProps { + /** + * The category of the Action. + */ + category: cpapi.ActionCategory; + + /** + * The artifact bounds of the Action. + */ + artifactBounds: cpapi.ActionArtifactBounds; + + /** + * The provider of the Action. + */ + provider: string; + + /** + * The version of your Action. + * + * @default '1' + */ + version?: string; + + /** + * The URL shown for the entire Action in the Pipeline UI. + */ + entityUrl?: string; + + /** + * The URL shown for a particular execution of an Action in the Pipeline UI. + */ + executionUrl?: string; + + /** + * The properties used for customizing the instance of your Action. + * + * @default [] + */ + actionProperties?: CustomActionProperty[]; +} + +/** + * The resource representing registering a custom Action with CodePipeline. + * For the Action to be usable, it has to be registered for every region and every account it's used in. + * In addition to this class, you should most likely also provide your clients a class + * representing your custom Action, extending the Action class, + * and taking the `actionProperties` as properly typed, construction properties. + */ +export class CustomActionRegistration extends cdk.Construct { + constructor(parent: cdk.Construct, id: string, props: CustomActionRegistrationProps) { + super(parent, id); + + new cloudformation.CustomActionTypeResource(this, 'Resource', { + category: props.category, + inputArtifactDetails: { + minimumCount: props.artifactBounds.minInputs, + maximumCount: props.artifactBounds.maxInputs, + }, + outputArtifactDetails: { + minimumCount: props.artifactBounds.minOutputs, + maximumCount: props.artifactBounds.maxOutputs, + }, + provider: props.provider, + version: props.version, + settings: { + entityUrlTemplate: props.entityUrl, + executionUrlTemplate: props.executionUrl, + }, + configurationProperties: props.actionProperties === undefined ? undefined : props.actionProperties.map((ap) => { return { + key: ap.key || false, + secret: ap.secret || false, + ...ap, + }; }), + }); + } +} diff --git a/packages/@aws-cdk/aws-codepipeline/lib/index.ts b/packages/@aws-cdk/aws-codepipeline/lib/index.ts index ca18a2780392d..f104e7d9cc54b 100644 --- a/packages/@aws-cdk/aws-codepipeline/lib/index.ts +++ b/packages/@aws-cdk/aws-codepipeline/lib/index.ts @@ -1,5 +1,7 @@ export * from './cross-region-scaffold-stack'; export * from './github-source-action'; +export * from './jenkins-actions'; +export * from './jenkins-provider'; export * from './manual-approval-action'; export * from './pipeline'; export * from './stage'; diff --git a/packages/@aws-cdk/aws-codepipeline/lib/jenkins-actions.ts b/packages/@aws-cdk/aws-codepipeline/lib/jenkins-actions.ts new file mode 100644 index 0000000000000..a37dad7d9fe23 --- /dev/null +++ b/packages/@aws-cdk/aws-codepipeline/lib/jenkins-actions.ts @@ -0,0 +1,120 @@ +import cpapi = require('@aws-cdk/aws-codepipeline-api'); +import cdk = require('@aws-cdk/cdk'); +import { IJenkinsProvider, jenkinsArtifactsBounds } from "./jenkins-provider"; + +/** + * Common construction properties of all Jenkins Pipeline Actions. + */ +export interface BasicJenkinsActionProps extends cpapi.CommonActionProps { + /** + * The name of the project (sometimes also called job, or task) + * on your Jenkins installation that will be invoked by this Action. + * + * @example 'MyJob' + */ + projectName: string; + + /** + * The source to use as input for this build. + * + * @default CodePipeline will use the output of the last Action from a previous Stage as input + */ + inputArtifact?: cpapi.Artifact; +} + +/** + * Common properties for creating {@link JenkinsBuildAction} - + * either directly, through its constructor, + * or through {@link JenkinsProvider#addToPipeline}. + */ +export interface BasicJenkinsBuildActionProps extends BasicJenkinsActionProps { + /** + * The name of the build's output artifact. + * + * @default an auto-generated name will be used + */ + outputArtifactName?: string; +} + +/** + * Construction properties of {@link JenkinsBuildAction}. + */ +export interface JenkinsBuildActionProps extends BasicJenkinsBuildActionProps, + cpapi.CommonActionConstructProps { + /** + * The Jenkins Provider for this Action. + */ + jenkinsProvider: IJenkinsProvider; +} + +/** + * Jenkins build CodePipeline Action. + * + * @see https://docs.aws.amazon.com/codepipeline/latest/userguide/tutorials-four-stage-pipeline.html + */ +export class JenkinsBuildAction extends cpapi.BuildAction { + constructor(parent: cdk.Construct, name: string, props: JenkinsBuildActionProps) { + super(parent, name, { + provider: props.jenkinsProvider.providerName, + owner: 'Custom', + artifactBounds: jenkinsArtifactsBounds, + version: props.jenkinsProvider.version, + configuration: { + ProjectName: props.projectName, + }, + ...props, + }); + + props.jenkinsProvider._registerBuildProvider(); + } +} + +/** + * Common properties for creating {@link JenkinsTestAction} - + * either directly, through its constructor, + * or through {@link JenkinsProvider#addToPipelineAsTest}. + */ +export interface BasicJenkinsTestActionProps extends BasicJenkinsActionProps { + /** + * The optional name of the primary output artifact. + * If you provide a value here, + * then the `outputArtifact` property of your Action will be non-null. + * If you don't, `outputArtifact` will be `null`. + * + * @default the Action will not have an output artifact + */ + outputArtifactName?: string; +} + +/** + * Construction properties of {@link JenkinsTestAction}. + */ +export interface JenkinsTestActionProps extends BasicJenkinsTestActionProps, + cpapi.CommonActionConstructProps { + /** + * The Jenkins Provider for this Action. + */ + jenkinsProvider: IJenkinsProvider; +} + +/** + * Jenkins test CodePipeline Action. + * + * @see https://docs.aws.amazon.com/codepipeline/latest/userguide/tutorials-four-stage-pipeline.html + */ +export class JenkinsTestAction extends cpapi.TestAction { + constructor(parent: cdk.Construct, name: string, props: JenkinsTestActionProps) { + super(parent, name, { + provider: props.jenkinsProvider.providerName, + owner: 'Custom', + artifactBounds: jenkinsArtifactsBounds, + version: props.jenkinsProvider.version, + configuration: { + ProjectName: props.projectName, + }, + ...props, + }); + + props.jenkinsProvider._registerTestProvider(); + } +} diff --git a/packages/@aws-cdk/aws-codepipeline/lib/jenkins-provider.ts b/packages/@aws-cdk/aws-codepipeline/lib/jenkins-provider.ts new file mode 100644 index 0000000000000..e0ee2616f7a73 --- /dev/null +++ b/packages/@aws-cdk/aws-codepipeline/lib/jenkins-provider.ts @@ -0,0 +1,277 @@ +import cpapi = require('@aws-cdk/aws-codepipeline-api'); +import cdk = require('@aws-cdk/cdk'); +import { CustomActionRegistration } from "./custom-action-registration"; +import { + BasicJenkinsBuildActionProps, + BasicJenkinsTestActionProps, + JenkinsBuildAction, + JenkinsTestAction +} from "./jenkins-actions"; + +/** + * A Jenkins provider. + * + * If you want to create a new Jenkins provider managed alongside your CDK code, + * instantiate the {@link JenkinsProvider} class directly. + * + * If you want to reference an already registered provider, + * use the {@link JenkinsProvider#import} method. + */ +export interface IJenkinsProvider { + readonly providerName: string; + readonly serverUrl: string; + readonly version: string; + + /** + * Convenience method for creating a new {@link JenkinsBuildAction}, + * and adding it to the given Stage. + * + * @param stage the Pipeline Stage to add the new Action to + * @param name the name of the newly created Action + * @param props construction properties of the new Action + * @returns the newly created {@link JenkinsBuildAction} + */ + addToPipeline(stage: cpapi.IStage, name: string, props: BasicJenkinsBuildActionProps): + JenkinsBuildAction; + + /** + * Convenience method for creating a new {@link JenkinsTestAction}, + * and adding it to the given Stage. + * + * @param stage the Pipeline Stage to add the new Action to + * @param name the name of the newly created Action + * @param props construction properties of the new Action + * @returns the newly created {@link JenkinsTestAction} + */ + addToPipelineAsTest(stage: cpapi.IStage, name: string, props: BasicJenkinsTestActionProps): + JenkinsTestAction; + + /** + * Registers a Jenkins Provider for the build category. + * This method will be automatically called when creating + * a {@link JenkinsBuildAction}, + * so you should never need to call it explicitly. + */ + _registerBuildProvider(): void; + + /** + * Registers a Jenkins Provider for the test category. + * This method will be automatically called when creating + * a {@link JenkinsTestAction}, + * so you should never need to call it explicitly. + */ + _registerTestProvider(): void; +} + +/** + * Properties for importing an existing Jenkins provider. + */ +export interface JenkinsProviderImportProps { + /** + * The name of the Jenkins provider that you set in the AWS CodePipeline plugin configuration of your Jenkins project. + * + * @example 'MyJenkinsProvider' + */ + providerName: string; + + /** + * The base URL of your Jenkins server. + * + * @example 'http://myjenkins.com:8080' + */ + serverUrl: string; + + /** + * The version of your provider. + * + * @default '1' + */ + version?: string; +} + +export interface JenkinsProviderProps { + /** + * The name of the Jenkins provider that you set in the AWS CodePipeline plugin configuration of your Jenkins project. + * + * @example 'MyJenkinsProvider' + */ + providerName: string; + + /** + * The base URL of your Jenkins server. + * + * @example 'http://myjenkins.com:8080' + */ + serverUrl: string; + + /** + * The version of your provider. + * + * @default '1' + */ + version?: string; + + /** + * Whether to immediately register a Jenkins Provider for the build category. + * The Provider will always be registered if you create a {@link JenkinsBuildAction}. + */ + forBuild?: boolean; + + /** + * Whether to immediately register a Jenkins Provider for the test category. + * The Provider will always be registered if you create a {@link JenkinsTestAction}. + */ + forTest?: boolean; +} + +export abstract class BaseJenkinsProvider extends cdk.Construct implements IJenkinsProvider { + public abstract readonly providerName: string; + public abstract readonly serverUrl: string; + public readonly version: string; + + protected constructor(parent: cdk.Construct, id: string, version?: string) { + super(parent, id); + + this.version = version || '1'; + } + + public export(): JenkinsProviderImportProps { + return { + providerName: new cdk.Output(this, 'JenkinsProviderName', { + value: this.providerName, + }).makeImportValue().toString(), + serverUrl: new cdk.Output(this, 'JenkinsServerUrl', { + value: this.serverUrl, + }).makeImportValue().toString(), + version: new cdk.Output(this, 'JenkinsProviderVersion', { + value: this.version, + }).makeImportValue().toString(), + }; + } + + public addToPipeline(stage: cpapi.IStage, name: string, props: BasicJenkinsBuildActionProps): + JenkinsBuildAction { + return new JenkinsBuildAction(this, name, { + stage, + jenkinsProvider: this, + ...props, + }); + } + + public addToPipelineAsTest(stage: cpapi.IStage, name: string, props: BasicJenkinsTestActionProps): + JenkinsTestAction { + return new JenkinsTestAction(this, name, { + stage, + jenkinsProvider: this, + ...props, + }); + } + + public abstract _registerBuildProvider(): void; + public abstract _registerTestProvider(): void; +} + +/** + * A class representing Jenkins providers. + * + * @see #import + */ +export class JenkinsProvider extends BaseJenkinsProvider { + /** + * Import a Jenkins provider registered either outside the CDK, + * or in a different CDK Stack. + * + * @param parent the parent Construct for the new provider + * @param id the identifier of the new provider Construct + * @param props the properties used to identify the existing provider + * @returns a new Construct representing a reference to an existing Jenkins provider + */ + public static import(parent: cdk.Construct, id: string, props: JenkinsProviderImportProps): IJenkinsProvider { + return new ImportedJenkinsProvider(parent, id, props); + } + + public readonly providerName: string; + public readonly serverUrl: string; + private buildIncluded = false; + private testIncluded = false; + + constructor(parent: cdk.Construct, id: string, props: JenkinsProviderProps) { + super(parent, id, props.version); + + this.providerName = props.providerName; + this.serverUrl = props.serverUrl; + + if (props.forBuild === true) { + this._registerBuildProvider(); + } + if (props.forTest === true) { + this._registerTestProvider(); + } + } + + public _registerBuildProvider(): void { + if (this.buildIncluded) { + return; + } + this.buildIncluded = true; + this.registerJenkinsCustomAction('JenkinsBuildProviderResource', cpapi.ActionCategory.Build); + } + + public _registerTestProvider(): void { + if (this.testIncluded) { + return; + } + this.testIncluded = true; + this.registerJenkinsCustomAction('JenkinsTestProviderResource', cpapi.ActionCategory.Test); + } + + private registerJenkinsCustomAction(id: string, category: cpapi.ActionCategory) { + new CustomActionRegistration(this, id, { + category, + artifactBounds: jenkinsArtifactsBounds, + provider: this.providerName, + version: this.version, + entityUrl: appendToUrl(this.serverUrl, 'job/{Config:ProjectName}'), + executionUrl: appendToUrl(this.serverUrl, 'job/{Config:ProjectName}/{ExternalExecutionId}'), + actionProperties: [ + { + name: 'ProjectName', + required: true, + key: true, + queryable: true, + }, + ], + }); + } +} + +class ImportedJenkinsProvider extends BaseJenkinsProvider { + public readonly providerName: string; + public readonly serverUrl: string; + + constructor(parent: cdk.Construct, id: string, props: JenkinsProviderImportProps) { + super(parent, id, props.version); + + this.providerName = props.providerName; + this.serverUrl = props.serverUrl; + } + + public _registerBuildProvider(): void { + // do nothing + } + + public _registerTestProvider(): void { + // do nothing + } +} + +function appendToUrl(baseUrl: string, path: string): string { + return baseUrl.endsWith('/') ? baseUrl + path : `${baseUrl}/${path}`; +} + +export const jenkinsArtifactsBounds: cpapi.ActionArtifactBounds = { + minInputs: 0, + maxInputs: 5, + minOutputs: 0, + maxOutputs: 5 +}; diff --git a/packages/@aws-cdk/aws-codepipeline/test/integ.pipeline-jenkins.expected.json b/packages/@aws-cdk/aws-codepipeline/test/integ.pipeline-jenkins.expected.json new file mode 100644 index 0000000000000..56d66c4978305 --- /dev/null +++ b/packages/@aws-cdk/aws-codepipeline/test/integ.pipeline-jenkins.expected.json @@ -0,0 +1,284 @@ +{ + "Resources": { + "MyBucketF68F3FF0": { + "Type": "AWS::S3::Bucket", + "DeletionPolicy": "Retain", + "Properties": { + "VersioningConfiguration": { + "Status": "Enabled" + } + } + }, + "PipelineRoleD68726F7": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "codepipeline.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + } + } + }, + "PipelineRoleDefaultPolicyC7A05455": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "s3:GetObject*", + "s3:GetBucket*", + "s3:List*", + "s3:DeleteObject*", + "s3:PutObject*", + "s3:Abort*" + ], + "Effect": "Allow", + "Resource": [ + { + "Fn::GetAtt": [ + "MyBucketF68F3FF0", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "MyBucketF68F3FF0", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + }, + { + "Action": [ + "s3:GetObject*", + "s3:GetBucket*", + "s3:List*" + ], + "Effect": "Allow", + "Resource": [ + { + "Fn::GetAtt": [ + "MyBucketF68F3FF0", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "MyBucketF68F3FF0", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "PipelineRoleDefaultPolicyC7A05455", + "Roles": [ + { + "Ref": "PipelineRoleD68726F7" + } + ] + } + }, + "PipelineC660917D": { + "Type": "AWS::CodePipeline::Pipeline", + "Properties": { + "ArtifactStore": { + "Location": { + "Ref": "MyBucketF68F3FF0" + }, + "Type": "S3" + }, + "RoleArn": { + "Fn::GetAtt": [ + "PipelineRoleD68726F7", + "Arn" + ] + }, + "Stages": [ + { + "Actions": [ + { + "ActionTypeId": { + "Category": "Source", + "Owner": "AWS", + "Provider": "S3", + "Version": "1" + }, + "Configuration": { + "S3Bucket": { + "Ref": "MyBucketF68F3FF0" + }, + "S3ObjectKey": "some/path", + "PollForSourceChanges": true + }, + "InputArtifacts": [], + "Name": "S3", + "OutputArtifacts": [ + { + "Name": "Artifact_awscdkcodepipelinejenkinsMyBucketS32154568D" + } + ], + "RunOrder": 1 + } + ], + "Name": "Source" + }, + { + "Actions": [ + { + "ActionTypeId": { + "Category": "Build", + "Owner": "Custom", + "Provider": "JenkinsProvider", + "Version": "2" + }, + "Configuration": { + "ProjectName": "JenkinsProject1" + }, + "InputArtifacts": [ + { + "Name": "Artifact_awscdkcodepipelinejenkinsMyBucketS32154568D" + } + ], + "Name": "JenkinsBuild", + "OutputArtifacts": [ + { + "Name": "Artifact_awscdkcodepipelinejenkinsJenkinsProviderJenkinsBuild6007E9FD" + } + ], + "RunOrder": 1 + }, + { + "ActionTypeId": { + "Category": "Test", + "Owner": "Custom", + "Provider": "JenkinsProvider", + "Version": "2" + }, + "Configuration": { + "ProjectName": "JenkinsProject2" + }, + "InputArtifacts": [ + { + "Name": "Artifact_awscdkcodepipelinejenkinsMyBucketS32154568D" + } + ], + "Name": "JenkinsTest", + "OutputArtifacts": [], + "RunOrder": 1 + }, + { + "ActionTypeId": { + "Category": "Test", + "Owner": "Custom", + "Provider": "JenkinsProvider", + "Version": "2" + }, + "Configuration": { + "ProjectName": "JenkinsProject3" + }, + "InputArtifacts": [ + { + "Name": "Artifact_awscdkcodepipelinejenkinsMyBucketS32154568D" + } + ], + "Name": "JenkinsTest2", + "OutputArtifacts": [], + "RunOrder": 1 + } + ], + "Name": "Build" + } + ] + }, + "DependsOn": [ + "PipelineRoleD68726F7", + "PipelineRoleDefaultPolicyC7A05455" + ] + }, + "JenkinsProviderJenkinsBuildProviderResourceD9231CAC": { + "Type": "AWS::CodePipeline::CustomActionType", + "Properties": { + "Category": "Build", + "InputArtifactDetails": { + "MaximumCount": 5, + "MinimumCount": 0 + }, + "OutputArtifactDetails": { + "MaximumCount": 5, + "MinimumCount": 0 + }, + "Provider": "JenkinsProvider", + "ConfigurationProperties": [ + { + "Key": true, + "Name": "ProjectName", + "Queryable": true, + "Required": true, + "Secret": false + } + ], + "Settings": { + "EntityUrlTemplate": "http://myjenkins.com:8080/job/{Config:ProjectName}", + "ExecutionUrlTemplate": "http://myjenkins.com:8080/job/{Config:ProjectName}/{ExternalExecutionId}" + }, + "Version": "2" + } + }, + "JenkinsProviderJenkinsTestProviderResourceF0CF8F0E": { + "Type": "AWS::CodePipeline::CustomActionType", + "Properties": { + "Category": "Test", + "InputArtifactDetails": { + "MaximumCount": 5, + "MinimumCount": 0 + }, + "OutputArtifactDetails": { + "MaximumCount": 5, + "MinimumCount": 0 + }, + "Provider": "JenkinsProvider", + "ConfigurationProperties": [ + { + "Key": true, + "Name": "ProjectName", + "Queryable": true, + "Required": true, + "Secret": false + } + ], + "Settings": { + "EntityUrlTemplate": "http://myjenkins.com:8080/job/{Config:ProjectName}", + "ExecutionUrlTemplate": "http://myjenkins.com:8080/job/{Config:ProjectName}/{ExternalExecutionId}" + }, + "Version": "2" + } + } + } +} diff --git a/packages/@aws-cdk/aws-codepipeline/test/integ.pipeline-jenkins.ts b/packages/@aws-cdk/aws-codepipeline/test/integ.pipeline-jenkins.ts new file mode 100644 index 0000000000000..5a1c6c7847101 --- /dev/null +++ b/packages/@aws-cdk/aws-codepipeline/test/integ.pipeline-jenkins.ts @@ -0,0 +1,39 @@ +import s3 = require('@aws-cdk/aws-s3'); +import cdk = require('@aws-cdk/cdk'); +import codepipeline = require('../lib'); + +const app = new cdk.App(); + +const stack = new cdk.Stack(app, 'aws-cdk-codepipeline-jenkins'); + +const bucket = new s3.Bucket(stack, 'MyBucket', { + versioned: true, +}); + +const pipeline = new codepipeline.Pipeline(stack, 'Pipeline', { + artifactBucket: bucket, +}); + +const sourceStage = pipeline.addStage('Source'); +bucket.addToPipeline(sourceStage, 'S3', { + bucketKey: 'some/path', +}); + +const jenkinsProvider = new codepipeline.JenkinsProvider(stack, 'JenkinsProvider', { + providerName: 'JenkinsProvider', + serverUrl: 'http://myjenkins.com:8080', + version: '2', +}); + +const buildStage = pipeline.addStage('Build'); +jenkinsProvider.addToPipeline(buildStage, 'JenkinsBuild', { + projectName: 'JenkinsProject1', +}); +jenkinsProvider.addToPipelineAsTest(buildStage, 'JenkinsTest', { + projectName: 'JenkinsProject2', +}); +jenkinsProvider.addToPipelineAsTest(buildStage, 'JenkinsTest2', { + projectName: 'JenkinsProject3', +}); + +app.run();