From 7cda5673fd3f6c5cd56ea59d71b14115f2a388f2 Mon Sep 17 00:00:00 2001 From: Mitchell Valine Date: Wed, 22 Feb 2023 20:43:29 -0800 Subject: [PATCH] feat(codebuild): adds file asset support to build-spec (#24289) Adds `fromAsset` to the `BuildSpec` class to allow referencing local files as a project's buildspec. Uploads the file to s3 and references the object arn in the `buildSpec` property of `Codebuild.Project`. `isImmediate` is true for AssetBuildSpec because it's actual meaning is not can be defined at synth time, but it exsists somewhere other than the project's source code, which in this case is true. Requires referencing of the project so adds `scope` as an optional parameter to `toBuildSpec` method. fixes: #1138 ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- packages/@aws-cdk/aws-codebuild/README.md | 55 +- .../@aws-cdk/aws-codebuild/lib/build-spec.ts | 52 +- .../@aws-cdk/aws-codebuild/lib/project.ts | 2 +- .../aws-codebuild/test/build-spec-asset.yml | 6 + ...efaultTestDeployAssertC826AACC.assets.json | 32 + ...aultTestDeployAssertC826AACC.template.json | 530 ++++++++ .../CodeBuildAssetBuildSpecStack.assets.json | 32 + ...CodeBuildAssetBuildSpecStack.template.json | 257 ++++ ...a7a991c4d29cdfc917e4c4c5fac49ade421ad4.yml | 6 + .../index.js | 1205 +++++++++++++++++ .../cdk.out | 1 + .../integ.json | 12 + .../manifest.json | 226 ++++ .../tree.json | 796 +++++++++++ .../test/integ.asset-build-spec.ts | 56 + 15 files changed, 3262 insertions(+), 6 deletions(-) create mode 100644 packages/@aws-cdk/aws-codebuild/test/build-spec-asset.yml create mode 100644 packages/@aws-cdk/aws-codebuild/test/integ.asset-build-spec.js.snapshot/AssetBuildSpecTestDefaultTestDeployAssertC826AACC.assets.json create mode 100644 packages/@aws-cdk/aws-codebuild/test/integ.asset-build-spec.js.snapshot/AssetBuildSpecTestDefaultTestDeployAssertC826AACC.template.json create mode 100644 packages/@aws-cdk/aws-codebuild/test/integ.asset-build-spec.js.snapshot/CodeBuildAssetBuildSpecStack.assets.json create mode 100644 packages/@aws-cdk/aws-codebuild/test/integ.asset-build-spec.js.snapshot/CodeBuildAssetBuildSpecStack.template.json create mode 100644 packages/@aws-cdk/aws-codebuild/test/integ.asset-build-spec.js.snapshot/asset.0ddfdbafee06424f04319f1476a7a991c4d29cdfc917e4c4c5fac49ade421ad4.yml create mode 100644 packages/@aws-cdk/aws-codebuild/test/integ.asset-build-spec.js.snapshot/asset.73c20a669c041469f7fc3fc03d574b093b5b97e7c716f76c1e8117e6163e4dc4.bundle/index.js create mode 100644 packages/@aws-cdk/aws-codebuild/test/integ.asset-build-spec.js.snapshot/cdk.out create mode 100644 packages/@aws-cdk/aws-codebuild/test/integ.asset-build-spec.js.snapshot/integ.json create mode 100644 packages/@aws-cdk/aws-codebuild/test/integ.asset-build-spec.js.snapshot/manifest.json create mode 100644 packages/@aws-cdk/aws-codebuild/test/integ.asset-build-spec.js.snapshot/tree.json create mode 100644 packages/@aws-cdk/aws-codebuild/test/integ.asset-build-spec.ts diff --git a/packages/@aws-cdk/aws-codebuild/README.md b/packages/@aws-cdk/aws-codebuild/README.md index 8748b4368a898..37e09f272e338 100644 --- a/packages/@aws-cdk/aws-codebuild/README.md +++ b/packages/@aws-cdk/aws-codebuild/README.md @@ -114,6 +114,58 @@ const gitHubSource = codebuild.Source.gitHub({ }); ``` +## BuildSpec + +The build spec can be provided from a number of different sources + +### File path relative to the root of the source + +You can specify a specific filename that exists within the project's source artifact to use as the buildspec. + +```ts +const project = new codebuild.Project(this, 'MyProject', { + buildSpec: codebuild.BuildSpec.fromSourceFileName('my-buildspec.yml'), + source: codebuild.Source.gitHub({ + owner: 'awslabs', + repo: 'aws-cdk', + }) +}); +``` + +This will use `my-buildspec.yml` file within the `awslabs/aws-cdk` repository as the build spec. + +### File within the CDK project codebuild + +You can also specify a file within your cdk project directory to use as the buildspec. + +```ts +const project = new codebuild.Project(this, 'MyProject', { + buildSpec: codebuild.BuildSpec.fromAsset('my-buildspec.yml'), +}); +``` + +This file will be uploaded to S3 and referenced from the codebuild project. + +### Inline object + +```ts +const project = new codebuild.Project(this, 'MyProject', { + buildSpec: codebuild.BuildSpec.fromObject({ + version: '0.2', + }), +}); +``` + +This will result in the buildspec being rendered as JSON within the codebuild project, if you prefer it to be rendered as YAML, use `fromObjectToYaml`. + +```ts +const project = new codebuild.Project(this, 'MyProject', { + buildSpec: codebuild.BuildSpec.fromObjectToYaml({ + version: '0.2', + }), +}); +``` + ## Artifacts CodeBuild Projects can produce Artifacts and upload them to S3. For example: @@ -135,9 +187,6 @@ const project = new codebuild.Project(this, 'MyProject', { }); ``` -If you'd prefer your buildspec to be rendered as YAML in the template, -use the `fromObjectToYaml()` method instead of `fromObject()`. - Because we've not set the `name` property, this example will set the `overrideArtifactName` parameter, and produce an artifact named as defined in the Buildspec file, uploaded to an S3 bucket (`bucket`). The path will be diff --git a/packages/@aws-cdk/aws-codebuild/lib/build-spec.ts b/packages/@aws-cdk/aws-codebuild/lib/build-spec.ts index 3d7c248c622ed..92bae615e8e7b 100644 --- a/packages/@aws-cdk/aws-codebuild/lib/build-spec.ts +++ b/packages/@aws-cdk/aws-codebuild/lib/build-spec.ts @@ -1,5 +1,8 @@ +import * as s3_assets from '@aws-cdk/aws-s3-assets'; import { IResolveContext, Lazy, Stack } from '@aws-cdk/core'; +import { Construct } from 'constructs'; import * as yaml_cfn from './private/yaml-cfn'; +import { Project } from './project'; /** * BuildSpec for CodeBuild projects @@ -27,6 +30,15 @@ export abstract class BuildSpec { return new FilenameBuildSpec(filename); } + /** + * Use the contents of a local file as the build spec string + * + * Use this if you have a local .yml or .json file that you want to use as the buildspec + */ + public static fromAsset(path: string): BuildSpec { + return new AssetBuildSpec(path); + } + /** * Whether the buildspec is directly available or deferred until build-time */ @@ -38,7 +50,43 @@ export abstract class BuildSpec { /** * Render the represented BuildSpec */ - public abstract toBuildSpec(): string; + public abstract toBuildSpec(scope?: Construct): string; +} + +/** + * BuildSpec that just returns the contents of a local file + */ +class AssetBuildSpec extends BuildSpec { + public readonly isImmediate: boolean = true; + public asset?: s3_assets.Asset; + + constructor(public readonly path: string, private readonly options: s3_assets.AssetOptions = { }) { + super(); + } + + public toBuildSpec(scope?: Project): string { + if (!scope) { + throw new Error('`AssetBuildSpec` requires a `scope` argument'); + } + + // If the same AssetCode is used multiple times, retain only the first instantiation. + if (!this.asset) { + this.asset = new s3_assets.Asset(scope, 'Code', { + path: this.path, + ...this.options, + }); + } else if (Stack.of(this.asset) !== Stack.of(scope)) { + throw new Error(`Asset is already associated with another stack '${Stack.of(this.asset).stackName}'. ` + + 'Create a new BuildSpec instance for every stack.'); + } + + this.asset.grantRead(scope); + return this.asset.bucket.arnForObjects(this.asset.s3ObjectKey); + } + + public toString() { + return ``; + } } /** @@ -208,4 +256,4 @@ function mergeDeep(lhs: any, rhs: any): any { } return rhs; -}; \ No newline at end of file +}; diff --git a/packages/@aws-cdk/aws-codebuild/lib/project.ts b/packages/@aws-cdk/aws-codebuild/lib/project.ts index 3b05adf921760..a0f7e45f75701 100644 --- a/packages/@aws-cdk/aws-codebuild/lib/project.ts +++ b/packages/@aws-cdk/aws-codebuild/lib/project.ts @@ -1090,7 +1090,7 @@ export class Project extends ProjectBase { description: props.description, source: { ...sourceConfig.sourceProperty, - buildSpec: buildSpec && buildSpec.toBuildSpec(), + buildSpec: buildSpec && buildSpec.toBuildSpec(this), }, artifacts: artifactsConfig.artifactsProperty, serviceRole: this.role.roleArn, diff --git a/packages/@aws-cdk/aws-codebuild/test/build-spec-asset.yml b/packages/@aws-cdk/aws-codebuild/test/build-spec-asset.yml new file mode 100644 index 0000000000000..82f86c25d24f0 --- /dev/null +++ b/packages/@aws-cdk/aws-codebuild/test/build-spec-asset.yml @@ -0,0 +1,6 @@ +version: 0.2 + +phases: + build: + commands: + - echo running stuff diff --git a/packages/@aws-cdk/aws-codebuild/test/integ.asset-build-spec.js.snapshot/AssetBuildSpecTestDefaultTestDeployAssertC826AACC.assets.json b/packages/@aws-cdk/aws-codebuild/test/integ.asset-build-spec.js.snapshot/AssetBuildSpecTestDefaultTestDeployAssertC826AACC.assets.json new file mode 100644 index 0000000000000..d7aca261ba022 --- /dev/null +++ b/packages/@aws-cdk/aws-codebuild/test/integ.asset-build-spec.js.snapshot/AssetBuildSpecTestDefaultTestDeployAssertC826AACC.assets.json @@ -0,0 +1,32 @@ +{ + "version": "30.1.0", + "files": { + "73c20a669c041469f7fc3fc03d574b093b5b97e7c716f76c1e8117e6163e4dc4": { + "source": { + "path": "asset.73c20a669c041469f7fc3fc03d574b093b5b97e7c716f76c1e8117e6163e4dc4.bundle", + "packaging": "zip" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "73c20a669c041469f7fc3fc03d574b093b5b97e7c716f76c1e8117e6163e4dc4.zip", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" + } + } + }, + "c6d793ffaa5add8fe777815df3019dfa5848c214e42229f568e035859171a1b8": { + "source": { + "path": "AssetBuildSpecTestDefaultTestDeployAssertC826AACC.template.json", + "packaging": "file" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "c6d793ffaa5add8fe777815df3019dfa5848c214e42229f568e035859171a1b8.json", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" + } + } + } + }, + "dockerImages": {} +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-codebuild/test/integ.asset-build-spec.js.snapshot/AssetBuildSpecTestDefaultTestDeployAssertC826AACC.template.json b/packages/@aws-cdk/aws-codebuild/test/integ.asset-build-spec.js.snapshot/AssetBuildSpecTestDefaultTestDeployAssertC826AACC.template.json new file mode 100644 index 0000000000000..4f812a5d4f2f8 --- /dev/null +++ b/packages/@aws-cdk/aws-codebuild/test/integ.asset-build-spec.js.snapshot/AssetBuildSpecTestDefaultTestDeployAssertC826AACC.template.json @@ -0,0 +1,530 @@ +{ + "Resources": { + "AwsApiCallCodeBuildbatchGetProjects": { + "Type": "Custom::DeployAssert@SdkCallCodeBuildbatchGetProjects", + "Properties": { + "ServiceToken": { + "Fn::GetAtt": [ + "SingletonFunction1488541a7b23466481b69b4408076b81HandlerCD40AE9F", + "Arn" + ] + }, + "service": "CodeBuild", + "api": "batchGetProjects", + "expected": "{\"$StringLike\":\".+\"}", + "actualPath": "projects.0.source.buildspec", + "parameters": { + "names": [ + { + "Fn::ImportValue": "CodeBuildAssetBuildSpecStack:ExportsOutputRefMyProject39F7B0AE1CE3CA42" + } + ] + }, + "flattenResponse": "true", + "outputPaths": [ + "projects.0.source.buildspec" + ], + "salt": "1677121630131" + }, + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "SingletonFunction1488541a7b23466481b69b4408076b81Role37ABCE73": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "lambda.amazonaws.com" + } + } + ] + }, + "ManagedPolicyArns": [ + { + "Fn::Sub": "arn:${AWS::Partition}:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + } + ], + "Policies": [ + { + "PolicyName": "Inline", + "PolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Action": [ + "codebuild:BatchGetProjects" + ], + "Effect": "Allow", + "Resource": [ + "*" + ] + }, + { + "Action": [ + "s3:GetObject" + ], + "Effect": "Allow", + "Resource": [ + "*" + ] + }, + { + "Action": [ + "codebuild:StartBuild" + ], + "Effect": "Allow", + "Resource": [ + "*" + ] + }, + { + "Action": [ + "codebuild:BatchGetBuilds" + ], + "Effect": "Allow", + "Resource": [ + "*" + ] + }, + { + "Action": [ + "states:StartExecution" + ], + "Effect": "Allow", + "Resource": [ + "*" + ] + } + ] + } + } + ] + } + }, + "SingletonFunction1488541a7b23466481b69b4408076b81HandlerCD40AE9F": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Runtime": "nodejs14.x", + "Code": { + "S3Bucket": { + "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}" + }, + "S3Key": "73c20a669c041469f7fc3fc03d574b093b5b97e7c716f76c1e8117e6163e4dc4.zip" + }, + "Timeout": 120, + "Handler": "index.handler", + "Role": { + "Fn::GetAtt": [ + "SingletonFunction1488541a7b23466481b69b4408076b81Role37ABCE73", + "Arn" + ] + } + } + }, + "AwsApiCallS3getObject": { + "Type": "Custom::DeployAssert@SdkCallS3getObject", + "Properties": { + "ServiceToken": { + "Fn::GetAtt": [ + "SingletonFunction1488541a7b23466481b69b4408076b81HandlerCD40AE9F", + "Arn" + ] + }, + "service": "S3", + "api": "getObject", + "parameters": { + "Bucket": { + "Fn::Select": [ + 0, + { + "Fn::Split": [ + "/", + { + "Fn::Select": [ + 5, + { + "Fn::Split": [ + ":", + { + "Fn::GetAtt": [ + "AwsApiCallCodeBuildbatchGetProjects", + "apiCallResponse.projects.0.source.buildspec" + ] + } + ] + } + ] + } + ] + } + ] + }, + "Key": { + "Fn::Select": [ + 1, + { + "Fn::Split": [ + "/", + { + "Fn::Select": [ + 5, + { + "Fn::Split": [ + ":", + { + "Fn::GetAtt": [ + "AwsApiCallCodeBuildbatchGetProjects", + "apiCallResponse.projects.0.source.buildspec" + ] + } + ] + } + ] + } + ] + } + ] + } + }, + "flattenResponse": "false", + "salt": "1677121630135" + }, + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "AwsApiCallCodeBuildstartBuild": { + "Type": "Custom::DeployAssert@SdkCallCodeBuildstartBuild", + "Properties": { + "ServiceToken": { + "Fn::GetAtt": [ + "SingletonFunction1488541a7b23466481b69b4408076b81HandlerCD40AE9F", + "Arn" + ] + }, + "service": "CodeBuild", + "api": "startBuild", + "parameters": { + "projectName": { + "Fn::ImportValue": "CodeBuildAssetBuildSpecStack:ExportsOutputRefMyProject39F7B0AE1CE3CA42" + } + }, + "flattenResponse": "true", + "salt": "1677121630135" + }, + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "AwsApiCallCodeBuildbatchGetBuilds": { + "Type": "Custom::DeployAssert@SdkCallCodeBuildbatchGetBuilds", + "Properties": { + "ServiceToken": { + "Fn::GetAtt": [ + "SingletonFunction1488541a7b23466481b69b4408076b81HandlerCD40AE9F", + "Arn" + ] + }, + "service": "CodeBuild", + "api": "batchGetBuilds", + "expected": "{\"$StringLike\":\"SUCCEEDED\"}", + "actualPath": "builds.0.buildStatus", + "stateMachineArn": { + "Ref": "AwsApiCallCodeBuildbatchGetBuildsWaitFor678D530F" + }, + "parameters": { + "ids": [ + { + "Fn::GetAtt": [ + "AwsApiCallCodeBuildstartBuild", + "apiCallResponse.build.id" + ] + } + ] + }, + "flattenResponse": "true", + "outputPaths": [ + "builds.0.buildStatus" + ], + "salt": "1677121630135" + }, + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "AwsApiCallCodeBuildbatchGetBuildsWaitForIsCompleteProviderInvokeB7D5E279": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:InvokeFunction", + "FunctionName": { + "Fn::GetAtt": [ + "SingletonFunction76b3e830a873425f8453eddd85c86925Handler81461ECE", + "Arn" + ] + }, + "Principal": { + "Fn::GetAtt": [ + "AwsApiCallCodeBuildbatchGetBuildsWaitForRole390D0005", + "Arn" + ] + } + } + }, + "AwsApiCallCodeBuildbatchGetBuildsWaitForTimeoutProviderInvokeFAAC8293": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:InvokeFunction", + "FunctionName": { + "Fn::GetAtt": [ + "SingletonFunction5c1898e096fb4e3e95d5f6c67f3ce41aHandlerADF3E6EA", + "Arn" + ] + }, + "Principal": { + "Fn::GetAtt": [ + "AwsApiCallCodeBuildbatchGetBuildsWaitForRole390D0005", + "Arn" + ] + } + } + }, + "AwsApiCallCodeBuildbatchGetBuildsWaitForRole390D0005": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "states.amazonaws.com" + } + } + ] + }, + "Policies": [ + { + "PolicyName": "InlineInvokeFunctions", + "PolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Action": "lambda:InvokeFunction", + "Effect": "Allow", + "Resource": [ + { + "Fn::GetAtt": [ + "SingletonFunction76b3e830a873425f8453eddd85c86925Handler81461ECE", + "Arn" + ] + }, + { + "Fn::GetAtt": [ + "SingletonFunction5c1898e096fb4e3e95d5f6c67f3ce41aHandlerADF3E6EA", + "Arn" + ] + } + ] + } + ] + } + } + ] + } + }, + "AwsApiCallCodeBuildbatchGetBuildsWaitFor678D530F": { + "Type": "AWS::StepFunctions::StateMachine", + "Properties": { + "DefinitionString": { + "Fn::Join": [ + "", + [ + "{\"StartAt\":\"framework-isComplete-task\",\"States\":{\"framework-isComplete-task\":{\"End\":true,\"Retry\":[{\"ErrorEquals\":[\"States.ALL\"],\"IntervalSeconds\":30,\"MaxAttempts\":10,\"BackoffRate\":1}],\"Catch\":[{\"ErrorEquals\":[\"States.ALL\"],\"Next\":\"framework-onTimeout-task\"}],\"Type\":\"Task\",\"Resource\":\"", + { + "Fn::GetAtt": [ + "SingletonFunction76b3e830a873425f8453eddd85c86925Handler81461ECE", + "Arn" + ] + }, + "\"},\"framework-onTimeout-task\":{\"End\":true,\"Type\":\"Task\",\"Resource\":\"", + { + "Fn::GetAtt": [ + "SingletonFunction5c1898e096fb4e3e95d5f6c67f3ce41aHandlerADF3E6EA", + "Arn" + ] + }, + "\"}}}" + ] + ] + }, + "RoleArn": { + "Fn::GetAtt": [ + "AwsApiCallCodeBuildbatchGetBuildsWaitForRole390D0005", + "Arn" + ] + } + }, + "DependsOn": [ + "AwsApiCallCodeBuildbatchGetBuildsWaitForRole390D0005" + ] + }, + "SingletonFunction76b3e830a873425f8453eddd85c86925Role918961BB": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "lambda.amazonaws.com" + } + } + ] + }, + "ManagedPolicyArns": [ + { + "Fn::Sub": "arn:${AWS::Partition}:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + } + ], + "Policies": [ + { + "PolicyName": "Inline", + "PolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Action": [ + "codebuild:BatchGetBuilds" + ], + "Effect": "Allow", + "Resource": [ + "*" + ] + } + ] + } + } + ] + } + }, + "SingletonFunction76b3e830a873425f8453eddd85c86925Handler81461ECE": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Runtime": "nodejs14.x", + "Code": { + "S3Bucket": { + "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}" + }, + "S3Key": "73c20a669c041469f7fc3fc03d574b093b5b97e7c716f76c1e8117e6163e4dc4.zip" + }, + "Timeout": 120, + "Handler": "index.isComplete", + "Role": { + "Fn::GetAtt": [ + "SingletonFunction76b3e830a873425f8453eddd85c86925Role918961BB", + "Arn" + ] + } + } + }, + "SingletonFunction5c1898e096fb4e3e95d5f6c67f3ce41aRoleB84BD8CE": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "lambda.amazonaws.com" + } + } + ] + }, + "ManagedPolicyArns": [ + { + "Fn::Sub": "arn:${AWS::Partition}:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + } + ] + } + }, + "SingletonFunction5c1898e096fb4e3e95d5f6c67f3ce41aHandlerADF3E6EA": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Runtime": "nodejs14.x", + "Code": { + "S3Bucket": { + "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}" + }, + "S3Key": "73c20a669c041469f7fc3fc03d574b093b5b97e7c716f76c1e8117e6163e4dc4.zip" + }, + "Timeout": 120, + "Handler": "index.onTimeout", + "Role": { + "Fn::GetAtt": [ + "SingletonFunction5c1898e096fb4e3e95d5f6c67f3ce41aRoleB84BD8CE", + "Arn" + ] + } + } + } + }, + "Outputs": { + "AssertionResultsAwsApiCallCodeBuildbatchGetProjects": { + "Value": { + "Fn::GetAtt": [ + "AwsApiCallCodeBuildbatchGetProjects", + "assertion" + ] + } + }, + "AssertionResultsAwsApiCallCodeBuildbatchGetBuilds": { + "Value": { + "Fn::GetAtt": [ + "AwsApiCallCodeBuildbatchGetBuilds", + "assertion" + ] + } + } + }, + "Parameters": { + "BootstrapVersion": { + "Type": "AWS::SSM::Parameter::Value", + "Default": "/cdk-bootstrap/hnb659fds/version", + "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]" + } + }, + "Rules": { + "CheckBootstrapVersion": { + "Assertions": [ + { + "Assert": { + "Fn::Not": [ + { + "Fn::Contains": [ + [ + "1", + "2", + "3", + "4", + "5" + ], + { + "Ref": "BootstrapVersion" + } + ] + } + ] + }, + "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI." + } + ] + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-codebuild/test/integ.asset-build-spec.js.snapshot/CodeBuildAssetBuildSpecStack.assets.json b/packages/@aws-cdk/aws-codebuild/test/integ.asset-build-spec.js.snapshot/CodeBuildAssetBuildSpecStack.assets.json new file mode 100644 index 0000000000000..5651929249f36 --- /dev/null +++ b/packages/@aws-cdk/aws-codebuild/test/integ.asset-build-spec.js.snapshot/CodeBuildAssetBuildSpecStack.assets.json @@ -0,0 +1,32 @@ +{ + "version": "30.1.0", + "files": { + "0ddfdbafee06424f04319f1476a7a991c4d29cdfc917e4c4c5fac49ade421ad4": { + "source": { + "path": "asset.0ddfdbafee06424f04319f1476a7a991c4d29cdfc917e4c4c5fac49ade421ad4.yml", + "packaging": "file" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "0ddfdbafee06424f04319f1476a7a991c4d29cdfc917e4c4c5fac49ade421ad4.yml", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" + } + } + }, + "3843454984973fab240386685a4ee6813ab6e383ffb32068183fda7b8badb46e": { + "source": { + "path": "CodeBuildAssetBuildSpecStack.template.json", + "packaging": "file" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "3843454984973fab240386685a4ee6813ab6e383ffb32068183fda7b8badb46e.json", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" + } + } + } + }, + "dockerImages": {} +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-codebuild/test/integ.asset-build-spec.js.snapshot/CodeBuildAssetBuildSpecStack.template.json b/packages/@aws-cdk/aws-codebuild/test/integ.asset-build-spec.js.snapshot/CodeBuildAssetBuildSpecStack.template.json new file mode 100644 index 0000000000000..c4231385ef554 --- /dev/null +++ b/packages/@aws-cdk/aws-codebuild/test/integ.asset-build-spec.js.snapshot/CodeBuildAssetBuildSpecStack.template.json @@ -0,0 +1,257 @@ +{ + "Resources": { + "MyProjectRole9BBE5233": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "codebuild.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + } + } + }, + "MyProjectRoleDefaultPolicyB19B7C29": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "s3:GetBucket*", + "s3:GetObject*", + "s3:List*" + ], + "Effect": "Allow", + "Resource": [ + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":s3:::", + { + "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}" + }, + "/*" + ] + ] + }, + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":s3:::", + { + "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}" + } + ] + ] + } + ] + }, + { + "Action": [ + "logs:CreateLogGroup", + "logs:CreateLogStream", + "logs:PutLogEvents" + ], + "Effect": "Allow", + "Resource": [ + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":logs:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":log-group:/aws/codebuild/", + { + "Ref": "MyProject39F7B0AE" + }, + ":*" + ] + ] + }, + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":logs:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":log-group:/aws/codebuild/", + { + "Ref": "MyProject39F7B0AE" + } + ] + ] + } + ] + }, + { + "Action": [ + "codebuild:BatchPutCodeCoverages", + "codebuild:BatchPutTestCases", + "codebuild:CreateReport", + "codebuild:CreateReportGroup", + "codebuild:UpdateReport" + ], + "Effect": "Allow", + "Resource": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":codebuild:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":report-group/", + { + "Ref": "MyProject39F7B0AE" + }, + "-*" + ] + ] + } + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "MyProjectRoleDefaultPolicyB19B7C29", + "Roles": [ + { + "Ref": "MyProjectRole9BBE5233" + } + ] + } + }, + "MyProject39F7B0AE": { + "Type": "AWS::CodeBuild::Project", + "Properties": { + "Artifacts": { + "Type": "NO_ARTIFACTS" + }, + "Environment": { + "ComputeType": "BUILD_GENERAL1_SMALL", + "Image": "aws/codebuild/standard:1.0", + "ImagePullCredentialsType": "CODEBUILD", + "PrivilegedMode": false, + "Type": "LINUX_CONTAINER" + }, + "ServiceRole": { + "Fn::GetAtt": [ + "MyProjectRole9BBE5233", + "Arn" + ] + }, + "Source": { + "BuildSpec": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":s3:::", + { + "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}" + }, + "/0ddfdbafee06424f04319f1476a7a991c4d29cdfc917e4c4c5fac49ade421ad4.yml" + ] + ] + }, + "Type": "NO_SOURCE" + }, + "Cache": { + "Type": "NO_CACHE" + }, + "EncryptionKey": "alias/aws/s3" + } + } + }, + "Outputs": { + "ExportsOutputRefMyProject39F7B0AE1CE3CA42": { + "Value": { + "Ref": "MyProject39F7B0AE" + }, + "Export": { + "Name": "CodeBuildAssetBuildSpecStack:ExportsOutputRefMyProject39F7B0AE1CE3CA42" + } + } + }, + "Parameters": { + "BootstrapVersion": { + "Type": "AWS::SSM::Parameter::Value", + "Default": "/cdk-bootstrap/hnb659fds/version", + "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]" + } + }, + "Rules": { + "CheckBootstrapVersion": { + "Assertions": [ + { + "Assert": { + "Fn::Not": [ + { + "Fn::Contains": [ + [ + "1", + "2", + "3", + "4", + "5" + ], + { + "Ref": "BootstrapVersion" + } + ] + } + ] + }, + "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI." + } + ] + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-codebuild/test/integ.asset-build-spec.js.snapshot/asset.0ddfdbafee06424f04319f1476a7a991c4d29cdfc917e4c4c5fac49ade421ad4.yml b/packages/@aws-cdk/aws-codebuild/test/integ.asset-build-spec.js.snapshot/asset.0ddfdbafee06424f04319f1476a7a991c4d29cdfc917e4c4c5fac49ade421ad4.yml new file mode 100644 index 0000000000000..82f86c25d24f0 --- /dev/null +++ b/packages/@aws-cdk/aws-codebuild/test/integ.asset-build-spec.js.snapshot/asset.0ddfdbafee06424f04319f1476a7a991c4d29cdfc917e4c4c5fac49ade421ad4.yml @@ -0,0 +1,6 @@ +version: 0.2 + +phases: + build: + commands: + - echo running stuff diff --git a/packages/@aws-cdk/aws-codebuild/test/integ.asset-build-spec.js.snapshot/asset.73c20a669c041469f7fc3fc03d574b093b5b97e7c716f76c1e8117e6163e4dc4.bundle/index.js b/packages/@aws-cdk/aws-codebuild/test/integ.asset-build-spec.js.snapshot/asset.73c20a669c041469f7fc3fc03d574b093b5b97e7c716f76c1e8117e6163e4dc4.bundle/index.js new file mode 100644 index 0000000000000..58bcb1ef7f38e --- /dev/null +++ b/packages/@aws-cdk/aws-codebuild/test/integ.asset-build-spec.js.snapshot/asset.73c20a669c041469f7fc3fc03d574b093b5b97e7c716f76c1e8117e6163e4dc4.bundle/index.js @@ -0,0 +1,1205 @@ +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); + +// lib/assertions/providers/lambda-handler/index.ts +var lambda_handler_exports = {}; +__export(lambda_handler_exports, { + handler: () => handler, + isComplete: () => isComplete, + onTimeout: () => onTimeout +}); +module.exports = __toCommonJS(lambda_handler_exports); + +// ../assertions/lib/matcher.ts +var Matcher = class { + /** + * Check whether the provided object is a subtype of the `IMatcher`. + */ + static isMatcher(x) { + return x && x instanceof Matcher; + } +}; +var MatchResult = class { + constructor(target) { + this.failuresHere = /* @__PURE__ */ new Map(); + this.captures = /* @__PURE__ */ new Map(); + this.finalized = false; + this.innerMatchFailures = /* @__PURE__ */ new Map(); + this._hasFailed = false; + this._failCount = 0; + this._cost = 0; + this.target = target; + } + /** + * DEPRECATED + * @deprecated use recordFailure() + */ + push(matcher, path, message) { + return this.recordFailure({ matcher, path, message }); + } + /** + * Record a new failure into this result at a specific path. + */ + recordFailure(failure) { + const failKey = failure.path.join("."); + let list = this.failuresHere.get(failKey); + if (!list) { + list = []; + this.failuresHere.set(failKey, list); + } + this._failCount += 1; + this._cost += failure.cost ?? 1; + list.push(failure); + this._hasFailed = true; + return this; + } + /** Whether the match is a success */ + get isSuccess() { + return !this._hasFailed; + } + /** Does the result contain any failures. If not, the result is a success */ + hasFailed() { + return this._hasFailed; + } + /** The number of failures */ + get failCount() { + return this._failCount; + } + /** The cost of the failures so far */ + get failCost() { + return this._cost; + } + /** + * Compose the results of a previous match as a subtree. + * @param id the id of the parent tree. + */ + compose(id, inner) { + if (inner.hasFailed()) { + this._hasFailed = true; + this._failCount += inner.failCount; + this._cost += inner._cost; + this.innerMatchFailures.set(id, inner); + } + inner.captures.forEach((vals, capture) => { + vals.forEach((value) => this.recordCapture({ capture, value })); + }); + return this; + } + /** + * Prepare the result to be analyzed. + * This API *must* be called prior to analyzing these results. + */ + finished() { + if (this.finalized) { + return this; + } + if (this.failCount === 0) { + this.captures.forEach((vals, cap) => cap._captured.push(...vals)); + } + this.finalized = true; + return this; + } + /** + * Render the failed match in a presentable way + * + * Prefer using `renderMismatch` over this method. It is left for backwards + * compatibility for test suites that expect it, but `renderMismatch()` will + * produce better output. + */ + toHumanStrings() { + const failures = new Array(); + debugger; + recurse(this, []); + return failures.map((r) => { + const loc = r.path.length === 0 ? "" : ` at /${r.path.join("/")}`; + return "" + r.message + loc + ` (using ${r.matcher.name} matcher)`; + }); + function recurse(x, prefix) { + for (const fail of Array.from(x.failuresHere.values()).flat()) { + failures.push({ + matcher: fail.matcher, + message: fail.message, + path: [...prefix, ...fail.path] + }); + } + for (const [key, inner] of x.innerMatchFailures.entries()) { + recurse(inner, [...prefix, key]); + } + } + } + /** + * Do a deep render of the match result, showing the structure mismatches in context + */ + renderMismatch() { + if (!this.hasFailed()) { + return ""; + } + const parts = new Array(); + const indents = new Array(); + emitFailures(this, ""); + recurse(this); + return moveMarkersToFront(parts.join("").trimEnd()); + function emit(x) { + if (x === void 0) { + debugger; + } + parts.push(x.replace(/\n/g, ` +${indents.join("")}`)); + } + function emitFailures(r, path, scrapSet) { + for (const fail of r.failuresHere.get(path) ?? []) { + emit(`!! ${fail.message} +`); + } + scrapSet == null ? void 0 : scrapSet.delete(path); + } + function recurse(r) { + const remainingFailures = new Set(Array.from(r.failuresHere.keys()).filter((x) => x !== "")); + if (Array.isArray(r.target)) { + indents.push(" "); + emit("[\n"); + for (const [first, i] of enumFirst(range(r.target.length))) { + if (!first) { + emit(",\n"); + } + emitFailures(r, `${i}`, remainingFailures); + const innerMatcher = r.innerMatchFailures.get(`${i}`); + if (innerMatcher) { + emitFailures(innerMatcher, ""); + recurseComparingValues(innerMatcher, r.target[i]); + } else { + emit(renderAbridged(r.target[i])); + } + } + emitRemaining(); + indents.pop(); + emit("\n]"); + return; + } + if (r.target && typeof r.target === "object") { + indents.push(" "); + emit("{\n"); + const keys = Array.from(/* @__PURE__ */ new Set([ + ...Object.keys(r.target), + ...Array.from(remainingFailures) + ])).sort(); + for (const [first, key] of enumFirst(keys)) { + if (!first) { + emit(",\n"); + } + emitFailures(r, key, remainingFailures); + const innerMatcher = r.innerMatchFailures.get(key); + if (innerMatcher) { + emitFailures(innerMatcher, ""); + emit(`${jsonify(key)}: `); + recurseComparingValues(innerMatcher, r.target[key]); + } else { + emit(`${jsonify(key)}: `); + emit(renderAbridged(r.target[key])); + } + } + emitRemaining(); + indents.pop(); + emit("\n}"); + return; + } + emitRemaining(); + emit(jsonify(r.target)); + function emitRemaining() { + if (remainingFailures.size > 0) { + emit("\n"); + } + for (const key of remainingFailures) { + emitFailures(r, key); + } + } + } + function recurseComparingValues(inner, actualValue) { + if (inner.target === actualValue) { + return recurse(inner); + } + emit(renderAbridged(actualValue)); + emit(" <*> "); + recurse(inner); + } + function renderAbridged(x) { + if (Array.isArray(x)) { + switch (x.length) { + case 0: + return "[]"; + case 1: + return `[ ${renderAbridged(x[0])} ]`; + case 2: + if (x.every((e) => ["number", "boolean", "string"].includes(typeof e))) { + return `[ ${x.map(renderAbridged).join(", ")} ]`; + } + return "[ ... ]"; + default: + return "[ ... ]"; + } + } + if (x && typeof x === "object") { + const keys = Object.keys(x); + switch (keys.length) { + case 0: + return "{}"; + case 1: + return `{ ${JSON.stringify(keys[0])}: ${renderAbridged(x[keys[0]])} }`; + default: + return "{ ... }"; + } + } + return jsonify(x); + } + function jsonify(x) { + return JSON.stringify(x) ?? "undefined"; + } + function moveMarkersToFront(x) { + const re = /^(\s+)!!/gm; + return x.replace(re, (_, spaces) => `!!${spaces.substring(0, spaces.length - 2)}`); + } + } + /** + * Record a capture against in this match result. + */ + recordCapture(options) { + let values = this.captures.get(options.capture); + if (values === void 0) { + values = []; + } + values.push(options.value); + this.captures.set(options.capture, values); + } +}; +function* range(n) { + for (let i = 0; i < n; i++) { + yield i; + } +} +function* enumFirst(xs) { + let first = true; + for (const x of xs) { + yield [first, x]; + first = false; + } +} + +// ../assertions/lib/private/matchers/absent.ts +var AbsentMatch = class extends Matcher { + constructor(name) { + super(); + this.name = name; + } + test(actual) { + const result = new MatchResult(actual); + if (actual !== void 0) { + result.recordFailure({ + matcher: this, + path: [], + message: `Received ${actual}, but key should be absent` + }); + } + return result; + } +}; + +// ../assertions/lib/private/sorting.ts +function sortKeyComparator(keyFn) { + return (a, b) => { + const ak = keyFn(a); + const bk = keyFn(b); + for (let i = 0; i < ak.length && i < bk.length; i++) { + const av = ak[i]; + const bv = bk[i]; + let diff = 0; + if (typeof av === "number" && typeof bv === "number") { + diff = av - bv; + } else if (typeof av === "string" && typeof bv === "string") { + diff = av.localeCompare(bv); + } + if (diff !== 0) { + return diff; + } + } + return bk.length - ak.length; + }; +} + +// ../assertions/lib/private/sparse-matrix.ts +var SparseMatrix = class { + constructor() { + this.matrix = /* @__PURE__ */ new Map(); + } + get(row, col) { + var _a; + return (_a = this.matrix.get(row)) == null ? void 0 : _a.get(col); + } + row(row) { + var _a; + return Array.from(((_a = this.matrix.get(row)) == null ? void 0 : _a.entries()) ?? []); + } + set(row, col, value) { + let r = this.matrix.get(row); + if (!r) { + r = /* @__PURE__ */ new Map(); + this.matrix.set(row, r); + } + r.set(col, value); + } +}; + +// ../assertions/lib/private/type.ts +function getType(obj) { + return Array.isArray(obj) ? "array" : typeof obj; +} + +// ../assertions/lib/match.ts +var Match = class { + /** + * Use this matcher in the place of a field's value, if the field must not be present. + */ + static absent() { + return new AbsentMatch("absent"); + } + /** + * Matches the specified pattern with the array found in the same relative path of the target. + * The set of elements (or matchers) must be in the same order as would be found. + * @param pattern the pattern to match + */ + static arrayWith(pattern) { + return new ArrayMatch("arrayWith", pattern); + } + /** + * Matches the specified pattern with the array found in the same relative path of the target. + * The set of elements (or matchers) must match exactly and in order. + * @param pattern the pattern to match + */ + static arrayEquals(pattern) { + return new ArrayMatch("arrayEquals", pattern, { subsequence: false }); + } + /** + * Deep exact matching of the specified pattern to the target. + * @param pattern the pattern to match + */ + static exact(pattern) { + return new LiteralMatch("exact", pattern, { partialObjects: false }); + } + /** + * Matches the specified pattern to an object found in the same relative path of the target. + * The keys and their values (or matchers) must be present in the target but the target can be a superset. + * @param pattern the pattern to match + */ + static objectLike(pattern) { + return new ObjectMatch("objectLike", pattern); + } + /** + * Matches the specified pattern to an object found in the same relative path of the target. + * The keys and their values (or matchers) must match exactly with the target. + * @param pattern the pattern to match + */ + static objectEquals(pattern) { + return new ObjectMatch("objectEquals", pattern, { partial: false }); + } + /** + * Matches any target which does NOT follow the specified pattern. + * @param pattern the pattern to NOT match + */ + static not(pattern) { + return new NotMatch("not", pattern); + } + /** + * Matches any string-encoded JSON and applies the specified pattern after parsing it. + * @param pattern the pattern to match after parsing the encoded JSON. + */ + static serializedJson(pattern) { + return new SerializedJson("serializedJson", pattern); + } + /** + * Matches any non-null value at the target. + */ + static anyValue() { + return new AnyMatch("anyValue"); + } + /** + * Matches targets according to a regular expression + */ + static stringLikeRegexp(pattern) { + return new StringLikeRegexpMatch("stringLikeRegexp", pattern); + } +}; +var LiteralMatch = class extends Matcher { + constructor(name, pattern, options = {}) { + super(); + this.name = name; + this.pattern = pattern; + this.partialObjects = options.partialObjects ?? false; + if (Matcher.isMatcher(this.pattern)) { + throw new Error("LiteralMatch cannot directly contain another matcher. Remove the top-level matcher or nest it more deeply."); + } + } + test(actual) { + if (Array.isArray(this.pattern)) { + return new ArrayMatch(this.name, this.pattern, { subsequence: false, partialObjects: this.partialObjects }).test(actual); + } + if (typeof this.pattern === "object") { + return new ObjectMatch(this.name, this.pattern, { partial: this.partialObjects }).test(actual); + } + const result = new MatchResult(actual); + if (typeof this.pattern !== typeof actual) { + result.recordFailure({ + matcher: this, + path: [], + message: `Expected type ${typeof this.pattern} but received ${getType(actual)}` + }); + return result; + } + if (actual !== this.pattern) { + result.recordFailure({ + matcher: this, + path: [], + message: `Expected ${this.pattern} but received ${actual}` + }); + } + return result; + } +}; +var ArrayMatch = class extends Matcher { + constructor(name, pattern, options = {}) { + super(); + this.name = name; + this.pattern = pattern; + this.subsequence = options.subsequence ?? true; + this.partialObjects = options.partialObjects ?? false; + } + test(actual) { + if (!Array.isArray(actual)) { + return new MatchResult(actual).recordFailure({ + matcher: this, + path: [], + message: `Expected type array but received ${getType(actual)}` + }); + } + return this.subsequence ? this.testSubsequence(actual) : this.testFullArray(actual); + } + testFullArray(actual) { + const result = new MatchResult(actual); + let i = 0; + for (; i < this.pattern.length && i < actual.length; i++) { + const patternElement = this.pattern[i]; + const matcher = Matcher.isMatcher(patternElement) ? patternElement : new LiteralMatch(this.name, patternElement, { partialObjects: this.partialObjects }); + const innerResult = matcher.test(actual[i]); + result.compose(`${i}`, innerResult); + } + if (i < this.pattern.length) { + result.recordFailure({ + matcher: this, + message: `Not enough elements in array (expecting ${this.pattern.length}, got ${actual.length})`, + path: [`${i}`] + }); + } + if (i < actual.length) { + result.recordFailure({ + matcher: this, + message: `Too many elements in array (expecting ${this.pattern.length}, got ${actual.length})`, + path: [`${i}`] + }); + } + return result; + } + testSubsequence(actual) { + const result = new MatchResult(actual); + let patternIdx = 0; + let actualIdx = 0; + const matches = new SparseMatrix(); + while (patternIdx < this.pattern.length && actualIdx < actual.length) { + const patternElement = this.pattern[patternIdx]; + const matcher = Matcher.isMatcher(patternElement) ? patternElement : new LiteralMatch(this.name, patternElement, { partialObjects: this.partialObjects }); + const matcherName = matcher.name; + if (matcherName == "absent" || matcherName == "anyValue") { + throw new Error(`The Matcher ${matcherName}() cannot be nested within arrayWith()`); + } + const innerResult = matcher.test(actual[actualIdx]); + matches.set(patternIdx, actualIdx, innerResult); + actualIdx++; + if (innerResult.isSuccess) { + result.compose(`${actualIdx}`, innerResult); + patternIdx++; + } + } + if (patternIdx < this.pattern.length) { + for (let spi = 0; spi < patternIdx; spi++) { + const foundMatch = matches.row(spi).find(([, r]) => r.isSuccess); + if (!foundMatch) { + continue; + } + const [index] = foundMatch; + result.compose(`${index}`, new MatchResult(actual[index]).recordFailure({ + matcher: this, + message: `arrayWith pattern ${spi} matched here`, + path: [], + cost: 0 + // This is an informational message so it would be unfair to assign it cost + })); + } + const failedMatches = matches.row(patternIdx); + failedMatches.sort(sortKeyComparator(([i, r]) => [r.failCost, i])); + if (failedMatches.length > 0) { + const [index, innerResult] = failedMatches[0]; + result.recordFailure({ + matcher: this, + message: `Could not match arrayWith pattern ${patternIdx}. This is the closest match`, + path: [`${index}`], + cost: 0 + // Informational message + }); + result.compose(`${index}`, innerResult); + } else { + result.recordFailure({ + matcher: this, + message: `Could not match arrayWith pattern ${patternIdx}. No more elements to try`, + path: [`${actual.length}`] + }); + } + } + return result; + } +}; +var ObjectMatch = class extends Matcher { + constructor(name, pattern, options = {}) { + super(); + this.name = name; + this.pattern = pattern; + this.partial = options.partial ?? true; + } + test(actual) { + if (typeof actual !== "object" || Array.isArray(actual)) { + return new MatchResult(actual).recordFailure({ + matcher: this, + path: [], + message: `Expected type object but received ${getType(actual)}` + }); + } + const result = new MatchResult(actual); + if (!this.partial) { + for (const a of Object.keys(actual)) { + if (!(a in this.pattern)) { + result.recordFailure({ + matcher: this, + path: [a], + message: `Unexpected key ${a}` + }); + } + } + } + for (const [patternKey, patternVal] of Object.entries(this.pattern)) { + if (!(patternKey in actual) && !(patternVal instanceof AbsentMatch)) { + result.recordFailure({ + matcher: this, + path: [patternKey], + message: `Missing key '${patternKey}'` + }); + continue; + } + const matcher = Matcher.isMatcher(patternVal) ? patternVal : new LiteralMatch(this.name, patternVal, { partialObjects: this.partial }); + const inner = matcher.test(actual[patternKey]); + result.compose(patternKey, inner); + } + return result; + } +}; +var SerializedJson = class extends Matcher { + constructor(name, pattern) { + super(); + this.name = name; + this.pattern = pattern; + } + test(actual) { + if (getType(actual) !== "string") { + return new MatchResult(actual).recordFailure({ + matcher: this, + path: [], + message: `Expected JSON as a string but found ${getType(actual)}` + }); + } + let parsed; + try { + parsed = JSON.parse(actual); + } catch (err) { + if (err instanceof SyntaxError) { + return new MatchResult(actual).recordFailure({ + matcher: this, + path: [], + message: `Invalid JSON string: ${actual}` + }); + } else { + throw err; + } + } + const matcher = Matcher.isMatcher(this.pattern) ? this.pattern : new LiteralMatch(this.name, this.pattern); + const innerResult = matcher.test(parsed); + if (innerResult.hasFailed()) { + innerResult.recordFailure({ + matcher: this, + path: [], + message: "Encoded JSON value does not match" + }); + } + return innerResult; + } +}; +var NotMatch = class extends Matcher { + constructor(name, pattern) { + super(); + this.name = name; + this.pattern = pattern; + } + test(actual) { + const matcher = Matcher.isMatcher(this.pattern) ? this.pattern : new LiteralMatch(this.name, this.pattern); + const innerResult = matcher.test(actual); + const result = new MatchResult(actual); + if (innerResult.failCount === 0) { + result.recordFailure({ + matcher: this, + path: [], + message: `Found unexpected match: ${JSON.stringify(actual, void 0, 2)}` + }); + } + return result; + } +}; +var AnyMatch = class extends Matcher { + constructor(name) { + super(); + this.name = name; + } + test(actual) { + const result = new MatchResult(actual); + if (actual == null) { + result.recordFailure({ + matcher: this, + path: [], + message: "Expected a value but found none" + }); + } + return result; + } +}; +var StringLikeRegexpMatch = class extends Matcher { + constructor(name, pattern) { + super(); + this.name = name; + this.pattern = pattern; + } + test(actual) { + const result = new MatchResult(actual); + const regex = new RegExp(this.pattern, "gm"); + if (typeof actual !== "string") { + result.recordFailure({ + matcher: this, + path: [], + message: `Expected a string, but got '${typeof actual}'` + }); + } + if (!regex.test(actual)) { + result.recordFailure({ + matcher: this, + path: [], + message: `String '${actual}' did not match pattern '${this.pattern}'` + }); + } + return result; + } +}; + +// lib/assertions/providers/lambda-handler/base.ts +var https = __toESM(require("https")); +var url = __toESM(require("url")); +var AWS = __toESM(require("aws-sdk")); +var CustomResourceHandler = class { + constructor(event, context) { + this.event = event; + this.context = context; + this.timedOut = false; + this.timeout = setTimeout(async () => { + await this.respond({ + status: "FAILED", + reason: "Lambda Function Timeout", + data: this.context.logStreamName + }); + this.timedOut = true; + }, context.getRemainingTimeInMillis() - 1200); + this.event = event; + this.physicalResourceId = extractPhysicalResourceId(event); + } + /** + * Handles executing the custom resource event. If `stateMachineArn` is present + * in the props then trigger the waiter statemachine + */ + async handle() { + try { + if ("stateMachineArn" in this.event.ResourceProperties) { + const req = { + stateMachineArn: this.event.ResourceProperties.stateMachineArn, + name: this.event.RequestId, + input: JSON.stringify(this.event) + }; + await this.startExecution(req); + return; + } else { + const response = await this.processEvent(this.event.ResourceProperties); + return response; + } + } catch (e) { + console.log(e); + throw e; + } finally { + clearTimeout(this.timeout); + } + } + /** + * Handle async requests from the waiter state machine + */ + async handleIsComplete() { + try { + const result = await this.processEvent(this.event.ResourceProperties); + return result; + } catch (e) { + console.log(e); + return; + } finally { + clearTimeout(this.timeout); + } + } + /** + * Start a step function state machine which will wait for the request + * to be successful. + */ + async startExecution(req) { + try { + const sfn = new AWS.StepFunctions(); + await sfn.startExecution(req).promise(); + } finally { + clearTimeout(this.timeout); + } + } + respond(response) { + if (this.timedOut) { + return; + } + const cfResponse = { + Status: response.status, + Reason: response.reason, + PhysicalResourceId: this.physicalResourceId, + StackId: this.event.StackId, + RequestId: this.event.RequestId, + LogicalResourceId: this.event.LogicalResourceId, + NoEcho: false, + Data: response.data + }; + const responseBody = JSON.stringify(cfResponse); + console.log("Responding to CloudFormation", responseBody); + const parsedUrl = url.parse(this.event.ResponseURL); + const requestOptions = { + hostname: parsedUrl.hostname, + path: parsedUrl.path, + method: "PUT", + headers: { "content-type": "", "content-length": responseBody.length } + }; + return new Promise((resolve, reject) => { + try { + const request2 = https.request(requestOptions, resolve); + request2.on("error", reject); + request2.write(responseBody); + request2.end(); + } catch (e) { + reject(e); + } finally { + clearTimeout(this.timeout); + } + }); + } +}; +function extractPhysicalResourceId(event) { + switch (event.RequestType) { + case "Create": + return event.LogicalResourceId; + case "Update": + case "Delete": + return event.PhysicalResourceId; + } +} + +// lib/assertions/providers/lambda-handler/assertion.ts +var AssertionHandler = class extends CustomResourceHandler { + async processEvent(request2) { + let actual = decodeCall(request2.actual); + const expected = decodeCall(request2.expected); + let result; + const matcher = new MatchCreator(expected).getMatcher(); + console.log(`Testing equality between ${JSON.stringify(request2.actual)} and ${JSON.stringify(request2.expected)}`); + const matchResult = matcher.test(actual); + matchResult.finished(); + if (matchResult.hasFailed()) { + result = { + failed: true, + assertion: JSON.stringify({ + status: "fail", + message: matchResult.renderMismatch() + }) + }; + if (request2.failDeployment) { + throw new Error(result.assertion); + } + } else { + result = { + assertion: JSON.stringify({ + status: "success" + }) + }; + } + return result; + } +}; +var MatchCreator = class { + constructor(obj) { + this.parsedObj = { + matcher: obj + }; + } + /** + * Return a Matcher that can be tested against the actual results. + * This will convert the encoded matchers into their corresponding + * assertions matcher. + * + * For example: + * + * ExpectedResult.objectLike({ + * Messages: [{ + * Body: Match.objectLike({ + * Elements: Match.arrayWith([{ Asdf: 3 }]), + * Payload: Match.serializedJson({ key: 'value' }), + * }), + * }], + * }); + * + * Will be encoded as: + * { + * $ObjectLike: { + * Messages: [{ + * Body: { + * $ObjectLike: { + * Elements: { + * $ArrayWith: [{ Asdf: 3 }], + * }, + * Payload: { + * $SerializedJson: { key: 'value' } + * } + * }, + * }, + * }], + * }, + * } + * + * Which can then be parsed by this function. For each key (recursively) + * the parser will check if the value has one of the encoded matchers as a key + * and if so, it will set the value as the Matcher. So, + * + * { + * Body: { + * $ObjectLike: { + * Elements: { + * $ArrayWith: [{ Asdf: 3 }], + * }, + * Payload: { + * $SerializedJson: { key: 'value' } + * } + * }, + * }, + * } + * + * Will be converted to + * { + * Body: Match.objectLike({ + * Elements: Match.arrayWith([{ Asdf: 3 }]), + * Payload: Match.serializedJson({ key: 'value' }), + * }), + * } + */ + getMatcher() { + try { + const final = JSON.parse(JSON.stringify(this.parsedObj), function(_k, v) { + const nested = Object.keys(v)[0]; + switch (nested) { + case "$ArrayWith": + return Match.arrayWith(v[nested]); + case "$ObjectLike": + return Match.objectLike(v[nested]); + case "$StringLike": + return Match.stringLikeRegexp(v[nested]); + case "$SerializedJson": + return Match.serializedJson(v[nested]); + default: + return v; + } + }); + if (Matcher.isMatcher(final.matcher)) { + return final.matcher; + } + return Match.exact(final.matcher); + } catch { + return Match.exact(this.parsedObj.matcher); + } + } +}; +function decodeCall(call) { + if (!call) { + return void 0; + } + try { + const parsed = JSON.parse(call); + return parsed; + } catch (e) { + return call; + } +} + +// lib/assertions/providers/lambda-handler/utils.ts +function decode(object) { + return JSON.parse(JSON.stringify(object), (_k, v) => { + switch (v) { + case "TRUE:BOOLEAN": + return true; + case "FALSE:BOOLEAN": + return false; + default: + return v; + } + }); +} + +// lib/assertions/providers/lambda-handler/sdk.ts +function flatten(object) { + return Object.assign( + {}, + ...function _flatten(child, path = []) { + return [].concat(...Object.keys(child).map((key) => { + let childKey = Buffer.isBuffer(child[key]) ? child[key].toString("utf8") : child[key]; + if (typeof childKey === "string") { + childKey = isJsonString(childKey); + } + return typeof childKey === "object" && childKey !== null ? _flatten(childKey, path.concat([key])) : { [path.concat([key]).join(".")]: childKey }; + })); + }(object) + ); +} +var AwsApiCallHandler = class extends CustomResourceHandler { + async processEvent(request2) { + const AWS2 = require("aws-sdk"); + console.log(`AWS SDK VERSION: ${AWS2.VERSION}`); + if (!Object.prototype.hasOwnProperty.call(AWS2, request2.service)) { + throw Error(`Service ${request2.service} does not exist in AWS SDK version ${AWS2.VERSION}.`); + } + const service = new AWS2[request2.service](); + const response = await service[request2.api](request2.parameters && decode(request2.parameters)).promise(); + console.log(`SDK response received ${JSON.stringify(response)}`); + delete response.ResponseMetadata; + const respond = { + apiCallResponse: response + }; + const flatData = { + ...flatten(respond) + }; + let resp = respond; + if (request2.outputPaths) { + resp = filterKeys(flatData, request2.outputPaths); + } else if (request2.flattenResponse === "true") { + resp = flatData; + } + console.log(`Returning result ${JSON.stringify(resp)}`); + return resp; + } +}; +function filterKeys(object, searchStrings) { + return Object.entries(object).reduce((filteredObject, [key, value]) => { + for (const searchString of searchStrings) { + if (key.startsWith(`apiCallResponse.${searchString}`)) { + filteredObject[key] = value; + } + } + return filteredObject; + }, {}); +} +function isJsonString(value) { + try { + return JSON.parse(value); + } catch { + return value; + } +} + +// lib/assertions/providers/lambda-handler/types.ts +var ASSERT_RESOURCE_TYPE = "Custom::DeployAssert@AssertEquals"; +var SDK_RESOURCE_TYPE_PREFIX = "Custom::DeployAssert@SdkCall"; + +// lib/assertions/providers/lambda-handler/index.ts +async function handler(event, context) { + console.log(`Event: ${JSON.stringify({ ...event, ResponseURL: "..." })}`); + const provider = createResourceHandler(event, context); + try { + if (event.RequestType === "Delete") { + await provider.respond({ + status: "SUCCESS", + reason: "OK" + }); + return; + } + const result = await provider.handle(); + if ("stateMachineArn" in event.ResourceProperties) { + console.info('Found "stateMachineArn", waiter statemachine started'); + return; + } else if ("expected" in event.ResourceProperties) { + console.info('Found "expected", testing assertions'); + const actualPath = event.ResourceProperties.actualPath; + const actual = actualPath ? result[`apiCallResponse.${actualPath}`] : result.apiCallResponse; + const assertion = new AssertionHandler({ + ...event, + ResourceProperties: { + ServiceToken: event.ServiceToken, + actual, + expected: event.ResourceProperties.expected + } + }, context); + try { + const assertionResult = await assertion.handle(); + await provider.respond({ + status: "SUCCESS", + reason: "OK", + // return both the result of the API call _and_ the assertion results + data: { + ...assertionResult, + ...result + } + }); + return; + } catch (e) { + await provider.respond({ + status: "FAILED", + reason: e.message ?? "Internal Error" + }); + return; + } + } + await provider.respond({ + status: "SUCCESS", + reason: "OK", + data: result + }); + } catch (e) { + await provider.respond({ + status: "FAILED", + reason: e.message ?? "Internal Error" + }); + return; + } + return; +} +async function onTimeout(timeoutEvent) { + const isCompleteRequest = JSON.parse(JSON.parse(timeoutEvent.Cause).errorMessage); + const provider = createResourceHandler(isCompleteRequest, standardContext); + await provider.respond({ + status: "FAILED", + reason: "Operation timed out: " + JSON.stringify(isCompleteRequest) + }); +} +async function isComplete(event, context) { + console.log(`Event: ${JSON.stringify({ ...event, ResponseURL: "..." })}`); + const provider = createResourceHandler(event, context); + try { + const result = await provider.handleIsComplete(); + const actualPath = event.ResourceProperties.actualPath; + if (result) { + const actual = actualPath ? result[`apiCallResponse.${actualPath}`] : result.apiCallResponse; + if ("expected" in event.ResourceProperties) { + const assertion = new AssertionHandler({ + ...event, + ResourceProperties: { + ServiceToken: event.ServiceToken, + actual, + expected: event.ResourceProperties.expected + } + }, context); + const assertionResult = await assertion.handleIsComplete(); + if (!(assertionResult == null ? void 0 : assertionResult.failed)) { + await provider.respond({ + status: "SUCCESS", + reason: "OK", + data: { + ...assertionResult, + ...result + } + }); + return; + } else { + console.log(`Assertion Failed: ${JSON.stringify(assertionResult)}`); + throw new Error(JSON.stringify(event)); + } + } + await provider.respond({ + status: "SUCCESS", + reason: "OK", + data: result + }); + } else { + console.log("No result"); + throw new Error(JSON.stringify(event)); + } + return; + } catch (e) { + console.log(e); + throw new Error(JSON.stringify(event)); + } +} +function createResourceHandler(event, context) { + if (event.ResourceType.startsWith(SDK_RESOURCE_TYPE_PREFIX)) { + return new AwsApiCallHandler(event, context); + } else if (event.ResourceType.startsWith(ASSERT_RESOURCE_TYPE)) { + return new AssertionHandler(event, context); + } else { + throw new Error(`Unsupported resource type "${event.ResourceType}`); + } +} +var standardContext = { + getRemainingTimeInMillis: () => 9e4 +}; +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + handler, + isComplete, + onTimeout +}); diff --git a/packages/@aws-cdk/aws-codebuild/test/integ.asset-build-spec.js.snapshot/cdk.out b/packages/@aws-cdk/aws-codebuild/test/integ.asset-build-spec.js.snapshot/cdk.out new file mode 100644 index 0000000000000..b72fef144f05c --- /dev/null +++ b/packages/@aws-cdk/aws-codebuild/test/integ.asset-build-spec.js.snapshot/cdk.out @@ -0,0 +1 @@ +{"version":"30.1.0"} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-codebuild/test/integ.asset-build-spec.js.snapshot/integ.json b/packages/@aws-cdk/aws-codebuild/test/integ.asset-build-spec.js.snapshot/integ.json new file mode 100644 index 0000000000000..937430500c04b --- /dev/null +++ b/packages/@aws-cdk/aws-codebuild/test/integ.asset-build-spec.js.snapshot/integ.json @@ -0,0 +1,12 @@ +{ + "version": "30.1.0", + "testCases": { + "AssetBuildSpecTest/DefaultTest": { + "stacks": [ + "CodeBuildAssetBuildSpecStack" + ], + "assertionStack": "AssetBuildSpecTest/DefaultTest/DeployAssert", + "assertionStackName": "AssetBuildSpecTestDefaultTestDeployAssertC826AACC" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-codebuild/test/integ.asset-build-spec.js.snapshot/manifest.json b/packages/@aws-cdk/aws-codebuild/test/integ.asset-build-spec.js.snapshot/manifest.json new file mode 100644 index 0000000000000..7dc10e79675fd --- /dev/null +++ b/packages/@aws-cdk/aws-codebuild/test/integ.asset-build-spec.js.snapshot/manifest.json @@ -0,0 +1,226 @@ +{ + "version": "30.1.0", + "artifacts": { + "CodeBuildAssetBuildSpecStack.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "CodeBuildAssetBuildSpecStack.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "CodeBuildAssetBuildSpecStack": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "CodeBuildAssetBuildSpecStack.template.json", + "validateOnSynth": false, + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", + "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/3843454984973fab240386685a4ee6813ab6e383ffb32068183fda7b8badb46e.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", + "additionalDependencies": [ + "CodeBuildAssetBuildSpecStack.assets" + ], + "lookupRole": { + "arn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-lookup-role-${AWS::AccountId}-${AWS::Region}", + "requiresBootstrapStackVersion": 8, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "dependencies": [ + "CodeBuildAssetBuildSpecStack.assets" + ], + "metadata": { + "/CodeBuildAssetBuildSpecStack/MyProject/Role/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "MyProjectRole9BBE5233" + } + ], + "/CodeBuildAssetBuildSpecStack/MyProject/Role/DefaultPolicy/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "MyProjectRoleDefaultPolicyB19B7C29" + } + ], + "/CodeBuildAssetBuildSpecStack/MyProject/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "MyProject39F7B0AE" + } + ], + "/CodeBuildAssetBuildSpecStack/Exports/Output{\"Ref\":\"MyProject39F7B0AE\"}": [ + { + "type": "aws:cdk:logicalId", + "data": "ExportsOutputRefMyProject39F7B0AE1CE3CA42" + } + ], + "/CodeBuildAssetBuildSpecStack/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/CodeBuildAssetBuildSpecStack/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "CodeBuildAssetBuildSpecStack" + }, + "AssetBuildSpecTestDefaultTestDeployAssertC826AACC.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "AssetBuildSpecTestDefaultTestDeployAssertC826AACC.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "AssetBuildSpecTestDefaultTestDeployAssertC826AACC": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "AssetBuildSpecTestDefaultTestDeployAssertC826AACC.template.json", + "validateOnSynth": false, + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", + "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/c6d793ffaa5add8fe777815df3019dfa5848c214e42229f568e035859171a1b8.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", + "additionalDependencies": [ + "AssetBuildSpecTestDefaultTestDeployAssertC826AACC.assets" + ], + "lookupRole": { + "arn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-lookup-role-${AWS::AccountId}-${AWS::Region}", + "requiresBootstrapStackVersion": 8, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "dependencies": [ + "CodeBuildAssetBuildSpecStack", + "AssetBuildSpecTestDefaultTestDeployAssertC826AACC.assets" + ], + "metadata": { + "/AssetBuildSpecTest/DefaultTest/DeployAssert/AwsApiCallCodeBuildbatchGetProjects/Default/Default": [ + { + "type": "aws:cdk:logicalId", + "data": "AwsApiCallCodeBuildbatchGetProjects" + } + ], + "/AssetBuildSpecTest/DefaultTest/DeployAssert/AwsApiCallCodeBuildbatchGetProjects/AssertionResults": [ + { + "type": "aws:cdk:logicalId", + "data": "AssertionResultsAwsApiCallCodeBuildbatchGetProjects" + } + ], + "/AssetBuildSpecTest/DefaultTest/DeployAssert/SingletonFunction1488541a7b23466481b69b4408076b81/Role": [ + { + "type": "aws:cdk:logicalId", + "data": "SingletonFunction1488541a7b23466481b69b4408076b81Role37ABCE73" + } + ], + "/AssetBuildSpecTest/DefaultTest/DeployAssert/SingletonFunction1488541a7b23466481b69b4408076b81/Handler": [ + { + "type": "aws:cdk:logicalId", + "data": "SingletonFunction1488541a7b23466481b69b4408076b81HandlerCD40AE9F" + } + ], + "/AssetBuildSpecTest/DefaultTest/DeployAssert/AwsApiCallS3getObject/Default/Default": [ + { + "type": "aws:cdk:logicalId", + "data": "AwsApiCallS3getObject" + } + ], + "/AssetBuildSpecTest/DefaultTest/DeployAssert/AwsApiCallCodeBuildstartBuild/Default/Default": [ + { + "type": "aws:cdk:logicalId", + "data": "AwsApiCallCodeBuildstartBuild" + } + ], + "/AssetBuildSpecTest/DefaultTest/DeployAssert/AwsApiCallCodeBuildbatchGetBuilds/Default/Default": [ + { + "type": "aws:cdk:logicalId", + "data": "AwsApiCallCodeBuildbatchGetBuilds" + } + ], + "/AssetBuildSpecTest/DefaultTest/DeployAssert/AwsApiCallCodeBuildbatchGetBuilds/WaitFor/IsCompleteProvider/Invoke": [ + { + "type": "aws:cdk:logicalId", + "data": "AwsApiCallCodeBuildbatchGetBuildsWaitForIsCompleteProviderInvokeB7D5E279" + } + ], + "/AssetBuildSpecTest/DefaultTest/DeployAssert/AwsApiCallCodeBuildbatchGetBuilds/WaitFor/TimeoutProvider/Invoke": [ + { + "type": "aws:cdk:logicalId", + "data": "AwsApiCallCodeBuildbatchGetBuildsWaitForTimeoutProviderInvokeFAAC8293" + } + ], + "/AssetBuildSpecTest/DefaultTest/DeployAssert/AwsApiCallCodeBuildbatchGetBuilds/WaitFor/Role": [ + { + "type": "aws:cdk:logicalId", + "data": "AwsApiCallCodeBuildbatchGetBuildsWaitForRole390D0005" + } + ], + "/AssetBuildSpecTest/DefaultTest/DeployAssert/AwsApiCallCodeBuildbatchGetBuilds/WaitFor/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "AwsApiCallCodeBuildbatchGetBuildsWaitFor678D530F" + } + ], + "/AssetBuildSpecTest/DefaultTest/DeployAssert/AwsApiCallCodeBuildbatchGetBuilds/AssertionResults": [ + { + "type": "aws:cdk:logicalId", + "data": "AssertionResultsAwsApiCallCodeBuildbatchGetBuilds" + } + ], + "/AssetBuildSpecTest/DefaultTest/DeployAssert/SingletonFunction76b3e830a873425f8453eddd85c86925/Role": [ + { + "type": "aws:cdk:logicalId", + "data": "SingletonFunction76b3e830a873425f8453eddd85c86925Role918961BB" + } + ], + "/AssetBuildSpecTest/DefaultTest/DeployAssert/SingletonFunction76b3e830a873425f8453eddd85c86925/Handler": [ + { + "type": "aws:cdk:logicalId", + "data": "SingletonFunction76b3e830a873425f8453eddd85c86925Handler81461ECE" + } + ], + "/AssetBuildSpecTest/DefaultTest/DeployAssert/SingletonFunction5c1898e096fb4e3e95d5f6c67f3ce41a/Role": [ + { + "type": "aws:cdk:logicalId", + "data": "SingletonFunction5c1898e096fb4e3e95d5f6c67f3ce41aRoleB84BD8CE" + } + ], + "/AssetBuildSpecTest/DefaultTest/DeployAssert/SingletonFunction5c1898e096fb4e3e95d5f6c67f3ce41a/Handler": [ + { + "type": "aws:cdk:logicalId", + "data": "SingletonFunction5c1898e096fb4e3e95d5f6c67f3ce41aHandlerADF3E6EA" + } + ], + "/AssetBuildSpecTest/DefaultTest/DeployAssert/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/AssetBuildSpecTest/DefaultTest/DeployAssert/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "AssetBuildSpecTest/DefaultTest/DeployAssert" + }, + "Tree": { + "type": "cdk:tree", + "properties": { + "file": "tree.json" + } + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-codebuild/test/integ.asset-build-spec.js.snapshot/tree.json b/packages/@aws-cdk/aws-codebuild/test/integ.asset-build-spec.js.snapshot/tree.json new file mode 100644 index 0000000000000..e1fd324b91626 --- /dev/null +++ b/packages/@aws-cdk/aws-codebuild/test/integ.asset-build-spec.js.snapshot/tree.json @@ -0,0 +1,796 @@ +{ + "version": "tree-0.1", + "tree": { + "id": "App", + "path": "", + "children": { + "CodeBuildAssetBuildSpecStack": { + "id": "CodeBuildAssetBuildSpecStack", + "path": "CodeBuildAssetBuildSpecStack", + "children": { + "MyProject": { + "id": "MyProject", + "path": "CodeBuildAssetBuildSpecStack/MyProject", + "children": { + "Role": { + "id": "Role", + "path": "CodeBuildAssetBuildSpecStack/MyProject/Role", + "children": { + "ImportRole": { + "id": "ImportRole", + "path": "CodeBuildAssetBuildSpecStack/MyProject/Role/ImportRole", + "constructInfo": { + "fqn": "@aws-cdk/core.Resource", + "version": "0.0.0" + } + }, + "Resource": { + "id": "Resource", + "path": "CodeBuildAssetBuildSpecStack/MyProject/Role/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::IAM::Role", + "aws:cdk:cloudformation:props": { + "assumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "codebuild.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-iam.CfnRole", + "version": "0.0.0" + } + }, + "DefaultPolicy": { + "id": "DefaultPolicy", + "path": "CodeBuildAssetBuildSpecStack/MyProject/Role/DefaultPolicy", + "children": { + "Resource": { + "id": "Resource", + "path": "CodeBuildAssetBuildSpecStack/MyProject/Role/DefaultPolicy/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::IAM::Policy", + "aws:cdk:cloudformation:props": { + "policyDocument": { + "Statement": [ + { + "Action": [ + "s3:GetBucket*", + "s3:GetObject*", + "s3:List*" + ], + "Effect": "Allow", + "Resource": [ + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":s3:::", + { + "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}" + }, + "/*" + ] + ] + }, + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":s3:::", + { + "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}" + } + ] + ] + } + ] + }, + { + "Action": [ + "logs:CreateLogGroup", + "logs:CreateLogStream", + "logs:PutLogEvents" + ], + "Effect": "Allow", + "Resource": [ + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":logs:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":log-group:/aws/codebuild/", + { + "Ref": "MyProject39F7B0AE" + }, + ":*" + ] + ] + }, + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":logs:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":log-group:/aws/codebuild/", + { + "Ref": "MyProject39F7B0AE" + } + ] + ] + } + ] + }, + { + "Action": [ + "codebuild:BatchPutCodeCoverages", + "codebuild:BatchPutTestCases", + "codebuild:CreateReport", + "codebuild:CreateReportGroup", + "codebuild:UpdateReport" + ], + "Effect": "Allow", + "Resource": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":codebuild:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":report-group/", + { + "Ref": "MyProject39F7B0AE" + }, + "-*" + ] + ] + } + } + ], + "Version": "2012-10-17" + }, + "policyName": "MyProjectRoleDefaultPolicyB19B7C29", + "roles": [ + { + "Ref": "MyProjectRole9BBE5233" + } + ] + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-iam.CfnPolicy", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-iam.Policy", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-iam.Role", + "version": "0.0.0" + } + }, + "Code": { + "id": "Code", + "path": "CodeBuildAssetBuildSpecStack/MyProject/Code", + "children": { + "Stage": { + "id": "Stage", + "path": "CodeBuildAssetBuildSpecStack/MyProject/Code/Stage", + "constructInfo": { + "fqn": "@aws-cdk/core.AssetStaging", + "version": "0.0.0" + } + }, + "AssetBucket": { + "id": "AssetBucket", + "path": "CodeBuildAssetBuildSpecStack/MyProject/Code/AssetBucket", + "constructInfo": { + "fqn": "@aws-cdk/aws-s3.BucketBase", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-s3-assets.Asset", + "version": "0.0.0" + } + }, + "Resource": { + "id": "Resource", + "path": "CodeBuildAssetBuildSpecStack/MyProject/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::CodeBuild::Project", + "aws:cdk:cloudformation:props": { + "artifacts": { + "type": "NO_ARTIFACTS" + }, + "environment": { + "type": "LINUX_CONTAINER", + "image": "aws/codebuild/standard:1.0", + "imagePullCredentialsType": "CODEBUILD", + "privilegedMode": false, + "computeType": "BUILD_GENERAL1_SMALL" + }, + "serviceRole": { + "Fn::GetAtt": [ + "MyProjectRole9BBE5233", + "Arn" + ] + }, + "source": { + "type": "NO_SOURCE", + "buildSpec": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":s3:::", + { + "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}" + }, + "/0ddfdbafee06424f04319f1476a7a991c4d29cdfc917e4c4c5fac49ade421ad4.yml" + ] + ] + } + }, + "cache": { + "type": "NO_CACHE" + }, + "encryptionKey": "alias/aws/s3" + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-codebuild.CfnProject", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-codebuild.Project", + "version": "0.0.0" + } + }, + "Exports": { + "id": "Exports", + "path": "CodeBuildAssetBuildSpecStack/Exports", + "children": { + "Output{\"Ref\":\"MyProject39F7B0AE\"}": { + "id": "Output{\"Ref\":\"MyProject39F7B0AE\"}", + "path": "CodeBuildAssetBuildSpecStack/Exports/Output{\"Ref\":\"MyProject39F7B0AE\"}", + "constructInfo": { + "fqn": "@aws-cdk/core.CfnOutput", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.1.252" + } + }, + "BootstrapVersion": { + "id": "BootstrapVersion", + "path": "CodeBuildAssetBuildSpecStack/BootstrapVersion", + "constructInfo": { + "fqn": "@aws-cdk/core.CfnParameter", + "version": "0.0.0" + } + }, + "CheckBootstrapVersion": { + "id": "CheckBootstrapVersion", + "path": "CodeBuildAssetBuildSpecStack/CheckBootstrapVersion", + "constructInfo": { + "fqn": "@aws-cdk/core.CfnRule", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/core.Stack", + "version": "0.0.0" + } + }, + "AssetBuildSpecTest": { + "id": "AssetBuildSpecTest", + "path": "AssetBuildSpecTest", + "children": { + "DefaultTest": { + "id": "DefaultTest", + "path": "AssetBuildSpecTest/DefaultTest", + "children": { + "Default": { + "id": "Default", + "path": "AssetBuildSpecTest/DefaultTest/Default", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.1.252" + } + }, + "DeployAssert": { + "id": "DeployAssert", + "path": "AssetBuildSpecTest/DefaultTest/DeployAssert", + "children": { + "AwsApiCallCodeBuildbatchGetProjects": { + "id": "AwsApiCallCodeBuildbatchGetProjects", + "path": "AssetBuildSpecTest/DefaultTest/DeployAssert/AwsApiCallCodeBuildbatchGetProjects", + "children": { + "SdkProvider": { + "id": "SdkProvider", + "path": "AssetBuildSpecTest/DefaultTest/DeployAssert/AwsApiCallCodeBuildbatchGetProjects/SdkProvider", + "children": { + "AssertionsProvider": { + "id": "AssertionsProvider", + "path": "AssetBuildSpecTest/DefaultTest/DeployAssert/AwsApiCallCodeBuildbatchGetProjects/SdkProvider/AssertionsProvider", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.1.252" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests.AssertionsProvider", + "version": "0.0.0" + } + }, + "Default": { + "id": "Default", + "path": "AssetBuildSpecTest/DefaultTest/DeployAssert/AwsApiCallCodeBuildbatchGetProjects/Default", + "children": { + "Default": { + "id": "Default", + "path": "AssetBuildSpecTest/DefaultTest/DeployAssert/AwsApiCallCodeBuildbatchGetProjects/Default/Default", + "constructInfo": { + "fqn": "@aws-cdk/core.CfnResource", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/core.CustomResource", + "version": "0.0.0" + } + }, + "AssertionResults": { + "id": "AssertionResults", + "path": "AssetBuildSpecTest/DefaultTest/DeployAssert/AwsApiCallCodeBuildbatchGetProjects/AssertionResults", + "constructInfo": { + "fqn": "@aws-cdk/core.CfnOutput", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests.AwsApiCall", + "version": "0.0.0" + } + }, + "SingletonFunction1488541a7b23466481b69b4408076b81": { + "id": "SingletonFunction1488541a7b23466481b69b4408076b81", + "path": "AssetBuildSpecTest/DefaultTest/DeployAssert/SingletonFunction1488541a7b23466481b69b4408076b81", + "children": { + "Staging": { + "id": "Staging", + "path": "AssetBuildSpecTest/DefaultTest/DeployAssert/SingletonFunction1488541a7b23466481b69b4408076b81/Staging", + "constructInfo": { + "fqn": "@aws-cdk/core.AssetStaging", + "version": "0.0.0" + } + }, + "Role": { + "id": "Role", + "path": "AssetBuildSpecTest/DefaultTest/DeployAssert/SingletonFunction1488541a7b23466481b69b4408076b81/Role", + "constructInfo": { + "fqn": "@aws-cdk/core.CfnResource", + "version": "0.0.0" + } + }, + "Handler": { + "id": "Handler", + "path": "AssetBuildSpecTest/DefaultTest/DeployAssert/SingletonFunction1488541a7b23466481b69b4408076b81/Handler", + "constructInfo": { + "fqn": "@aws-cdk/core.CfnResource", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.1.252" + } + }, + "AwsApiCallS3getObject": { + "id": "AwsApiCallS3getObject", + "path": "AssetBuildSpecTest/DefaultTest/DeployAssert/AwsApiCallS3getObject", + "children": { + "SdkProvider": { + "id": "SdkProvider", + "path": "AssetBuildSpecTest/DefaultTest/DeployAssert/AwsApiCallS3getObject/SdkProvider", + "children": { + "AssertionsProvider": { + "id": "AssertionsProvider", + "path": "AssetBuildSpecTest/DefaultTest/DeployAssert/AwsApiCallS3getObject/SdkProvider/AssertionsProvider", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.1.252" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests.AssertionsProvider", + "version": "0.0.0" + } + }, + "Default": { + "id": "Default", + "path": "AssetBuildSpecTest/DefaultTest/DeployAssert/AwsApiCallS3getObject/Default", + "children": { + "Default": { + "id": "Default", + "path": "AssetBuildSpecTest/DefaultTest/DeployAssert/AwsApiCallS3getObject/Default/Default", + "constructInfo": { + "fqn": "@aws-cdk/core.CfnResource", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/core.CustomResource", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests.AwsApiCall", + "version": "0.0.0" + } + }, + "AwsApiCallCodeBuildstartBuild": { + "id": "AwsApiCallCodeBuildstartBuild", + "path": "AssetBuildSpecTest/DefaultTest/DeployAssert/AwsApiCallCodeBuildstartBuild", + "children": { + "SdkProvider": { + "id": "SdkProvider", + "path": "AssetBuildSpecTest/DefaultTest/DeployAssert/AwsApiCallCodeBuildstartBuild/SdkProvider", + "children": { + "AssertionsProvider": { + "id": "AssertionsProvider", + "path": "AssetBuildSpecTest/DefaultTest/DeployAssert/AwsApiCallCodeBuildstartBuild/SdkProvider/AssertionsProvider", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.1.252" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests.AssertionsProvider", + "version": "0.0.0" + } + }, + "Default": { + "id": "Default", + "path": "AssetBuildSpecTest/DefaultTest/DeployAssert/AwsApiCallCodeBuildstartBuild/Default", + "children": { + "Default": { + "id": "Default", + "path": "AssetBuildSpecTest/DefaultTest/DeployAssert/AwsApiCallCodeBuildstartBuild/Default/Default", + "constructInfo": { + "fqn": "@aws-cdk/core.CfnResource", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/core.CustomResource", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests.AwsApiCall", + "version": "0.0.0" + } + }, + "AwsApiCallCodeBuildbatchGetBuilds": { + "id": "AwsApiCallCodeBuildbatchGetBuilds", + "path": "AssetBuildSpecTest/DefaultTest/DeployAssert/AwsApiCallCodeBuildbatchGetBuilds", + "children": { + "SdkProvider": { + "id": "SdkProvider", + "path": "AssetBuildSpecTest/DefaultTest/DeployAssert/AwsApiCallCodeBuildbatchGetBuilds/SdkProvider", + "children": { + "AssertionsProvider": { + "id": "AssertionsProvider", + "path": "AssetBuildSpecTest/DefaultTest/DeployAssert/AwsApiCallCodeBuildbatchGetBuilds/SdkProvider/AssertionsProvider", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.1.252" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests.AssertionsProvider", + "version": "0.0.0" + } + }, + "Default": { + "id": "Default", + "path": "AssetBuildSpecTest/DefaultTest/DeployAssert/AwsApiCallCodeBuildbatchGetBuilds/Default", + "children": { + "Default": { + "id": "Default", + "path": "AssetBuildSpecTest/DefaultTest/DeployAssert/AwsApiCallCodeBuildbatchGetBuilds/Default/Default", + "constructInfo": { + "fqn": "@aws-cdk/core.CfnResource", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/core.CustomResource", + "version": "0.0.0" + } + }, + "WaitFor": { + "id": "WaitFor", + "path": "AssetBuildSpecTest/DefaultTest/DeployAssert/AwsApiCallCodeBuildbatchGetBuilds/WaitFor", + "children": { + "IsCompleteProvider": { + "id": "IsCompleteProvider", + "path": "AssetBuildSpecTest/DefaultTest/DeployAssert/AwsApiCallCodeBuildbatchGetBuilds/WaitFor/IsCompleteProvider", + "children": { + "AssertionsProvider": { + "id": "AssertionsProvider", + "path": "AssetBuildSpecTest/DefaultTest/DeployAssert/AwsApiCallCodeBuildbatchGetBuilds/WaitFor/IsCompleteProvider/AssertionsProvider", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.1.252" + } + }, + "Invoke": { + "id": "Invoke", + "path": "AssetBuildSpecTest/DefaultTest/DeployAssert/AwsApiCallCodeBuildbatchGetBuilds/WaitFor/IsCompleteProvider/Invoke", + "constructInfo": { + "fqn": "@aws-cdk/core.CfnResource", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests.AssertionsProvider", + "version": "0.0.0" + } + }, + "TimeoutProvider": { + "id": "TimeoutProvider", + "path": "AssetBuildSpecTest/DefaultTest/DeployAssert/AwsApiCallCodeBuildbatchGetBuilds/WaitFor/TimeoutProvider", + "children": { + "AssertionsProvider": { + "id": "AssertionsProvider", + "path": "AssetBuildSpecTest/DefaultTest/DeployAssert/AwsApiCallCodeBuildbatchGetBuilds/WaitFor/TimeoutProvider/AssertionsProvider", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.1.252" + } + }, + "Invoke": { + "id": "Invoke", + "path": "AssetBuildSpecTest/DefaultTest/DeployAssert/AwsApiCallCodeBuildbatchGetBuilds/WaitFor/TimeoutProvider/Invoke", + "constructInfo": { + "fqn": "@aws-cdk/core.CfnResource", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests.AssertionsProvider", + "version": "0.0.0" + } + }, + "Role": { + "id": "Role", + "path": "AssetBuildSpecTest/DefaultTest/DeployAssert/AwsApiCallCodeBuildbatchGetBuilds/WaitFor/Role", + "constructInfo": { + "fqn": "@aws-cdk/core.CfnResource", + "version": "0.0.0" + } + }, + "Resource": { + "id": "Resource", + "path": "AssetBuildSpecTest/DefaultTest/DeployAssert/AwsApiCallCodeBuildbatchGetBuilds/WaitFor/Resource", + "constructInfo": { + "fqn": "@aws-cdk/core.CfnResource", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests.WaiterStateMachine", + "version": "0.0.0" + } + }, + "AssertionResults": { + "id": "AssertionResults", + "path": "AssetBuildSpecTest/DefaultTest/DeployAssert/AwsApiCallCodeBuildbatchGetBuilds/AssertionResults", + "constructInfo": { + "fqn": "@aws-cdk/core.CfnOutput", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests.AwsApiCall", + "version": "0.0.0" + } + }, + "SingletonFunction76b3e830a873425f8453eddd85c86925": { + "id": "SingletonFunction76b3e830a873425f8453eddd85c86925", + "path": "AssetBuildSpecTest/DefaultTest/DeployAssert/SingletonFunction76b3e830a873425f8453eddd85c86925", + "children": { + "Staging": { + "id": "Staging", + "path": "AssetBuildSpecTest/DefaultTest/DeployAssert/SingletonFunction76b3e830a873425f8453eddd85c86925/Staging", + "constructInfo": { + "fqn": "@aws-cdk/core.AssetStaging", + "version": "0.0.0" + } + }, + "Role": { + "id": "Role", + "path": "AssetBuildSpecTest/DefaultTest/DeployAssert/SingletonFunction76b3e830a873425f8453eddd85c86925/Role", + "constructInfo": { + "fqn": "@aws-cdk/core.CfnResource", + "version": "0.0.0" + } + }, + "Handler": { + "id": "Handler", + "path": "AssetBuildSpecTest/DefaultTest/DeployAssert/SingletonFunction76b3e830a873425f8453eddd85c86925/Handler", + "constructInfo": { + "fqn": "@aws-cdk/core.CfnResource", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.1.252" + } + }, + "SingletonFunction5c1898e096fb4e3e95d5f6c67f3ce41a": { + "id": "SingletonFunction5c1898e096fb4e3e95d5f6c67f3ce41a", + "path": "AssetBuildSpecTest/DefaultTest/DeployAssert/SingletonFunction5c1898e096fb4e3e95d5f6c67f3ce41a", + "children": { + "Staging": { + "id": "Staging", + "path": "AssetBuildSpecTest/DefaultTest/DeployAssert/SingletonFunction5c1898e096fb4e3e95d5f6c67f3ce41a/Staging", + "constructInfo": { + "fqn": "@aws-cdk/core.AssetStaging", + "version": "0.0.0" + } + }, + "Role": { + "id": "Role", + "path": "AssetBuildSpecTest/DefaultTest/DeployAssert/SingletonFunction5c1898e096fb4e3e95d5f6c67f3ce41a/Role", + "constructInfo": { + "fqn": "@aws-cdk/core.CfnResource", + "version": "0.0.0" + } + }, + "Handler": { + "id": "Handler", + "path": "AssetBuildSpecTest/DefaultTest/DeployAssert/SingletonFunction5c1898e096fb4e3e95d5f6c67f3ce41a/Handler", + "constructInfo": { + "fqn": "@aws-cdk/core.CfnResource", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.1.252" + } + }, + "BootstrapVersion": { + "id": "BootstrapVersion", + "path": "AssetBuildSpecTest/DefaultTest/DeployAssert/BootstrapVersion", + "constructInfo": { + "fqn": "@aws-cdk/core.CfnParameter", + "version": "0.0.0" + } + }, + "CheckBootstrapVersion": { + "id": "CheckBootstrapVersion", + "path": "AssetBuildSpecTest/DefaultTest/DeployAssert/CheckBootstrapVersion", + "constructInfo": { + "fqn": "@aws-cdk/core.CfnRule", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/core.Stack", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests.IntegTestCase", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests.IntegTest", + "version": "0.0.0" + } + }, + "Tree": { + "id": "Tree", + "path": "Tree", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.1.252" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/core.App", + "version": "0.0.0" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-codebuild/test/integ.asset-build-spec.ts b/packages/@aws-cdk/aws-codebuild/test/integ.asset-build-spec.ts new file mode 100644 index 0000000000000..550f1768c371c --- /dev/null +++ b/packages/@aws-cdk/aws-codebuild/test/integ.asset-build-spec.ts @@ -0,0 +1,56 @@ +import * as path from 'path'; +import * as cdk from '@aws-cdk/core'; +import { IntegTest, ExpectedResult } from '@aws-cdk/integ-tests'; +import * as codebuild from '../lib'; + +const app = new cdk.App(); +const stack = new cdk.Stack(app, 'CodeBuildAssetBuildSpecStack'); + +// Create a codebuild project using a local asset as the buildspec file +const buildSpec = codebuild.BuildSpec.fromAsset(path.resolve(__dirname, 'build-spec-asset.yml')); +const project = new codebuild.Project(stack, 'MyProject', { + buildSpec, +}); + +const integ = new IntegTest(app, 'AssetBuildSpecTest', { testCases: [stack] }); + +const getBuildProject = integ.assertions.awsApiCall('CodeBuild', 'batchGetProjects', { + names: [project.projectName], +}); + +getBuildProject.assertAtPath( + 'projects.0.name.buildspec', + ExpectedResult.exact(project.projectName), +); + +getBuildProject.assertAtPath( + 'projects.0.source.buildspec', + ExpectedResult.stringLikeRegexp('.+'), +); + + +const getBuildProjectBuildSpecArn = getBuildProject.getAttString('projects.0.source.buildspec'); + +// Assert that the buildspec for the project is in fact an S3 object arn +// by parsing it and calling `getObject`. +const { resource, resourceName } = cdk.Arn.parse(getBuildProjectBuildSpecArn); +integ.assertions.awsApiCall('S3', 'getObject', { + Bucket: resource, + Key: resourceName, +}); + +// Kick off a build +const startBuild = integ.assertions.awsApiCall('CodeBuild', 'startBuild', { + projectName: project.projectName, +}); + +// Describe the build and wait for the status to be successful +integ.assertions.awsApiCall('CodeBuild', 'batchGetBuilds', { + ids: [startBuild.getAttString('build.id')], +}).assertAtPath( + 'builds.0.buildStatus', + ExpectedResult.stringLikeRegexp('SUCCEEDED'), +).waitForAssertions({ + totalTimeout: cdk.Duration.minutes(5), + interval: cdk.Duration.seconds(30), +});