Simple CDK Testing Failing - amazon-web-services

this is my set up
//bin
#!/usr/bin/env node
import * as cdk from 'aws-cdk-lib';
import {Testing} from '../lib/index';
const app = new cdk.App();
new Testing(app, 'Testing');
//lib
import {Duration, Stack, StackProps} from 'aws-cdk-lib'
export class Testing extends Stack {
constructor(scope: Construct, id: string, props?: StackProps) {
super(scope, id, props);
// Define construct contents here
// example resource
const queue = new sqs.Queue(this, 'TestingQueue', {
visibilityTimeout: Duration.seconds(300)
});
}
}
//test
import {Stack} from 'aws-cdk-lib/core';
import sqs = require ('../lib/index');
import'#aws-cdk/assert/jest'
test('SQS Queue Created', () => {
const stack = new Stack();
new sqs.Testing(stack, 'sqs');
expect(stack).toHaveResource('AWS::SQS::Queue')
});
//npm-package
"devDependencies": {
"#types/jest": "^26.0.10",
"#types/node": "10.17.27",
"aws-cdk-lib": "2.1.0",
"constructs": "^10.0.0",
"jest": "^26.4.2",
"ts-jest": "^26.2.0",
"typescript": "~3.9.7"
},
"peerDependencies": {
"#aws-cdk/assert": "^2.1.0",
"aws-cdk-lib": "2.1.0",
"constructs": "^10.0.0"
},
"jest": {
"moduleFileExtensions": [
"js"
]
}
I get this when I run: npm run build; npm run test.
None of 0 resources matches resource 'AWS::SQS::Queue' with { "$anything": true }.
I don't understand???
This should be straight forward. I can see the resource in cdk.out, the stack synthesisez, the stack deploys.
It only happens with fine grained assertions. The snapshot works.

You are asserting on the empty stack. Assert on Testing instead.
test('SQS Queue Created', () => {
const stack = new Stack(); // stack is empty, has no queue
const iHaveAQueue = new sqs.Testing(stack, 'sqs');
expect(stack).toHaveResource('AWS::SQS::Queue') // will fail
expect(iHaveAQueue).toHaveResource('AWS::SQS::Queue') // will pass
});

Related

CDKpipeline - Cannot set lambda layer in a stack called from multiple stages in a pipeline

I want to set multiple stages with the same stack in a cdk pipeline. But I am getting the following error when bootstrapping my cdk project
C:\dev\aws-cdk\node_modules\aws-cdk-lib\aws-lambda\lib\code.ts:185
throw new Error(`Asset is already associated with another stack '${cdk.Stack.of(this.asset).stackName}'. ` +
^
Error: Asset is already associated with another stack 'msm-customer'. Create a new Code instance for every stack.
at AssetCode.bind (C:\dev\aws-cdk\node_modules\aws-cdk-lib\aws-lambda\lib\code.ts:185:13)
at new LayerVersion (C:\dev\aws-cdk\node_modules\aws-cdk-lib\aws-lambda\lib\layers.ts:124:29)
at new CustomerStack (C:\dev\aws-cdk\lib\CustomerStack.ts:22:17)
After debugging the code I found out that it is the layer declaration in the "CustomerStack" that is causing the issue. If I comment the layer section or if I keep only one stage in my pipeline then the bootstrap cmd works successfully. .
Pipelinestack.ts
// Creates a CodeCommit repository called 'CodeRepo'
const repo = new codecommit.Repository(this, 'CodeRepo', {
repositoryName: "CodeRepo"
});
const pipeline = new CodePipeline(this, 'Pipeline-dev', {
pipelineName: 'Pipeline-dev',
synth: new CodeBuildStep('SynthStep-dev', {
//role: role,
input: CodePipelineSource.codeCommit(repo, 'master'),
installCommands: [
'npm install -g aws-cdk'
],
commands: [
'npm ci',
'npm run build',
'npx cdk synth'
],
})
});
pipeline.addStage(new PipelineStage(this, 'dev'));
pipeline.addStage(new PipelineStage(this, 'uat'));
pipeline.addStage(new PipelineStage(this, 'prod'));
PipelineStage.ts
export class PipelineStage extends Stage {
constructor(scope: Construct, id: string, props?: StageProps) {
super(scope, id, props);
new CustomerStack(this, 'msm-customer-' + id, {
stackName: 'msm-customer'
env: {
account: process.env.ACCOUNT,
region: process.env.REGION,
},
});
}
}
CustomerStack.ts
import { Duration, Stack, StackProps } from 'aws-cdk-lib';
import { Construct } from 'constructs';
import * as apigateway from 'aws-cdk-lib/aws-apigateway';
import * as lambda from 'aws-cdk-lib/aws-lambda';
import * as iam from 'aws-cdk-lib/aws-iam';
import * as ec2 from 'aws-cdk-lib/aws-ec2';
import 'dotenv/config';
export class CustomerStack extends Stack {
constructor(scope: Construct, id: string, props?: StackProps) {
super(scope, id, props);
//define existing role
const role = iam.Role.fromRoleArn(this, 'Role',
`arn:aws:iam::${Stack.of(this).account}:role/` + process.env.IAM_ROLE,
{ mutable: false },
);
//define layer
const layer = new lambda.LayerVersion(this, 'msm-layer', {
code: lambda.Code.fromAsset('resources/layer/customer'),
description: 'Frontend common resources',
compatibleRuntimes: [lambda.Runtime.NODEJS_14_X],
removalPolicy: cdk.RemovalPolicy.DESTROY
});
const lambdaDefault = new lambda.Function(this, 'default', {
runtime: lambda.Runtime.NODEJS_14_X,
code: lambda.Code.fromAsset('resources/lambda/customer/default'),
handler: 'index.handler',
role: role,
timeout: Duration.seconds(20),
memorySize: 256,
layers: [layer],
allowPublicSubnet: true,
vpcSubnets: { subnetType: ec2.SubnetType.PUBLIC }
});
//rest of the code
}
}

SyntaxError: Unexpected token '?' in upgrading smart contract using #openzeppelin/hardhat-upgrades

I am trying to deploy an upgradable smart contract on polygon Mumbai tesnet. I am getting this error
smart-contract/node_modules/#openzeppelin/hardhat-upgrades/dist/index.js:108
compiler.settings ?? (compiler.settings = {});
^
SyntaxError: Unexpected token '?'
at wrapSafe (internal/modules/cjs/loader.js:915:16)
at Module._compile (internal/modules/cjs/loader.js:963:27)
at Module._extensions..js (internal/modules/cjs/loader.js:1027:10)
at Object.require.extensions.<computed> [as .js] (/Users/jagdish/workspace/speedrun-simple-nft-example/smart-contract/node_modules/ts-node/src/index.ts:1587:43)
at Module.load (internal/modules/cjs/loader.js:863:32)
at Function.Module._load (internal/modules/cjs/loader.js:708:14)
at Module.require (internal/modules/cjs/loader.js:887:19)
at require (internal/modules/cjs/helpers.js:74:18)
at Object.<anonymous> (/Users/jagdish/workspace/speedrun-simple-nft-example/smart-contract/hardhat.config.ts:4:1)
at Module._compile (internal/modules/cjs/loader.js:999:30)
while trying to run the deploy script.
here are my configs and deploy scripts:
hardhart.config.ts
import { task } from "hardhat/config";
// import "#nomiclabs/hardhat-waffle";
import 'hardhat-watcher';
import '#openzeppelin/hardhat-upgrades';
import '#nomiclabs/hardhat-ethers';
import dotenv from "dotenv"
dotenv.config()
// This is a sample Hardhat task. To learn how to create your own go to
// https://hardhat.org/guides/create-task.html
task("accounts", "Prints the list of accounts", async (taskArgs, hre) => {
const accounts = await hre.ethers.getSigners();
for (const account of accounts) {
console.log(account.address);
}
});
// You need to export an object to set up your config
// Go to https://hardhat.org/config/ to learn more
/**
* #type import('hardhat/config').HardhatUserConfig
*/
module.exports = {
solidity: "0.8.4",
watcher: {
compilation: {
tasks: ['compile'],
},
node: {
tasks: ['node'],
},
},
networks: {
matic: {
url: "https://polygon-rpc.com/",
accounts: [process.env.ACCOUNT_PRIVATE_KEY]
},
matic_mumbai: {
url: "https://rpc-mumbai.maticvigil.com",
accounts: [process.env.ACCOUNT_PRIVATE_KEY]
}
}
};
deploy.ts
import { ethers, upgrades } from "hardhat";
async function main() {
const contract = await ethers.getContractFactory("YourCollectible");
console.log("Deploying Contract ...");
const c = await upgrades.deployProxy(contract, { constructorArgs: ["Your Collectables", "YCB"] })
// const c = await contract.deploy("Your Collectable", "YCB");
await c.deployed();
console.log("Contract deployed to:", c.address);
}
main()
.then(() => process.exit(0))
.catch((error) => {
console.error(error);
process.exit(1);
});
package.json
{
"name": "smart-contract",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"run": "hardhat compile",
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"#nomiclabs/hardhat-ethers": "^2.0.6",
"#nomiclabs/hardhat-waffle": "^2.0.3",
"#openzeppelin/hardhat-upgrades": "^1.20.0",
"#typechain/hardhat": "^6.1.2",
"chai": "^4.3.6",
"ethereum-waffle": "^3.4.4",
"ethers": "^5.6.9",
"hardhat": "^2.9.9",
"hardhat-watcher": "^2.3.0",
"ts-node": "^10.8.1",
"typescript": "^4.7.4"
},
"dependencies": {
"#openzeppelin/contracts": "^4.7.0",
"dotenv": "^16.0.1"
}
}
I am not sure whether it is the node version issue in my local or do I have to downgrade the #openzeppelin/hardhat-upgrades version (I have tried that).
Node version: v12.22.10
Can anyone help on this
I had the same problem with node 12. Problem solved after updating to node 16 and running yarn again.

How to deploy application stack for a cross accounts pipeline

I'm setting up a CI pipeline for AWS Rds only. The pipeline is going to deploy RDS stack across Alpha/Gamma/Prod AWS account in order.
I have an application stack defined as:
export class ApplicationStack extends CDK.Stack {
constructor(scope: Construct, id: string, props: ApplicationStackProps) {
super(scope, id, props);
const coreVpc: Ec2.IVpc = Ec2.Vpc.fromLookup(this, "CoreVpc", {
vpcName: "CoreVpc",
});
const rdsStack = new RdsStack(scope, `eCommerceDatabaseRdsStack-${props.stageName}`, {
vpc: coreVpc,
description: `The stack defines eCommerceDatabase at ${props.stageName}.`,
});
}
}
Now I'm trying to deploy above application stack to a pipeline that's across multiple AWS accounts.
Here is how I tried to create the pipeline:
/**
* This is where we define the whole pipeline.
*/
export class PipelineStack extends Cdk.Stack {
constructor(scope: Cdk.App, id: string, props: PipelineStackProps) {
super(scope, id, props);
// Initialize the pipeline
const pipeline = new codepipeline.Pipeline(this, "Pipeline", {
pipelineName: "eCommerceDatabasePipeline",
restartExecutionOnUpdate: true,
});
// Add stages to this pipeline.
pipelineStageInfoList.forEach((pipelineStage: PipelineStageInfo) => {
const applicationStack = new ApplicationStack(
this,
`eCommerceDatabaseStack-${pipelineStage.stageName}`,
{
stageName: pipelineStage.stageName,
}
);
const stage: Cdk.aws_codepipeline.IStage = pipeline.addStage({
stageName: pipelineStage.stageName,
});
// This is where I'm having trouble.
//It complains that applicationStack.templateFile is just a string,
//not assignable to ArtifactPath type.
stage.addAction(
new codepipeline_actions.CloudFormationCreateUpdateStackAction({
actionName: `eCommerceDatabase-${pipelineStage.stageName}-Deploy`,
templatePath: applicationStack.templateFile,
stackName: `eCommerceDatabase-${pipelineStage.stageName}`,
region: pipelineStage.awsRegion,
adminPermissions: true,
})
);
});
}
}
As commented above, I don't know how to pass the application cloudformation stack to the pipeline stage. I don't know whether I'm using the right approach either.
I got this work. So share my pipeline stack here. I referred https://cdkworkshop.com/20-typescript/70-advanced-topics/200-pipelines/1000-setting-up.html to setup the CDK package. My RDS clusters are in the private subnets of VPC(which is controlled by a different CDK package https://github.com/yangliunewyork/AwsStandardVpcTemplate).
import * as Cdk from "aws-cdk-lib";
import * as pipelines from "aws-cdk-lib/pipelines";
import {
PipelineStageInfo,
pipelineStageInfoList,
} from "../config/pipeline-config";
import { PipelineStage } from "./pipeline-stage";
export interface PipelineStackProps extends Cdk.StackProps {
readonly pipelineName: string;
readonly description: string;
}
export class PipelineStack extends Cdk.Stack {
constructor(scope: Cdk.App, id: string, props: PipelineStackProps) {
super(scope, id, props);
// Initialize the pipeline
const pipeline = new pipelines.CodePipeline(this, "Pipeline", {
pipelineName: "rdsDatabasePipeline",
// Create KMS keys for the artifact buckets,
// allowing cross-account deployments
crossAccountKeys: true,
// allow the pipeline to reconfigure itself when assets or stages
// are being added to it
selfMutation: true,
// synth is expected to produce the CDK Cloud Assembly as its output
synth: new pipelines.ShellStep("Synth", {
input: pipelines.CodePipelineSource.gitHub(
"yang/RdsDatabaseCdk",
"main",
{
authentication: Cdk.SecretValue.secretsManager('github-token')
}
),
// Install dependencies, build and run cdk synth
commands: [
'npm ci',
'npm run build',
'npx cdk synth'
],
}),
});
// Add stages to this pipeline.
pipelineStageInfoList.forEach((pipelineStage: PipelineStageInfo) => {
pipeline.addStage(
new PipelineStage(this, pipelineStage.stageName, {
stageName: pipelineStage.stageName,
pipelineName: props.pipelineName,
env: {
account: pipelineStage.awsAccount,
region: pipelineStage.awsRegion,
},
})
);
});
}
}

AWS-CDK How to test props with Fine-grained assertions

How can I test all the props passed to the s3.Bucket?
I would like to test all the props passed to the s3.Bucket (no Snapshot).
The test is giving me an error on WebsiteConfiguration ...
To check how to write the prop obj inside the toHaveResource fn i used this doc
Anyone could help me?
import * as cdk from "#aws-cdk/core";
import * as s3 from "#aws-cdk/aws-s3";
export class S3CdkStack extends cdk.Stack {
constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
new s3.Bucket(this, "ReactGitHubActionBucket", {
versioned: true,
publicReadAccess: true,
websiteIndexDocument: "index.html",
removalPolicy: cdk.RemovalPolicy.DESTROY,
autoDeleteObjects: true,
});
}
}
import { expect as expectCDK, haveResource } from '#aws-cdk/assert';
import * as cdk from '#aws-cdk/core';
import * as Stacks from '../lib/s3-cdk-stack';
test('First test', () => {
const app = new cdk.App();
const stack = new Stacks.S3CdkStack(app, 'S3CdkTestStack');
expectCDK(stack).to(haveResource("AWS::S3::Bucket",{
VersioningConfiguration: {
Status: "Enabled"
},
WebsiteConfiguration: {
IndexDocument: "index.html"
}
}))
});
$ jest
FAIL test/stacks.test.ts
✕ First test (68 ms)
● First test
None of 1 resources matches resource 'AWS::S3::Bucket' with {
"$objectLike": {
"VersioningConfiguration": {
"Status": "Enabled"
},
"WebsiteConfiguration": {
"IndexDocument": "index.html"
}
}
}.
- Field WebsiteConfiguration missing in:
{
"Type": "AWS::S3::Bucket",
"Properties": {
"VersioningConfiguration": {
"Status": "Enabled"
}
},
"UpdateReplacePolicy": "Retain",
"DeletionPolicy": "Retain"
}
It may have been updated since you wrote your question, but as of CDK 1.113.0 the following example works for me.
Note that my imports are slightly different to yours, and that I've used .toHaveResource() instead of .to(haveResource()).
// CDK version: 1.113.0
import '#aws-cdk/assert/jest';
import * as cdk from '#aws-cdk/core';
import * as s3 from '#aws-cdk/aws-s3';
// Included for completeness, but you'd import this:
class S3CdkStack extends cdk.Stack {
constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
new s3.Bucket(this, 'ReactGitHubActionBucket', {
versioned: true,
publicReadAccess: true,
websiteIndexDocument: 'index.html',
removalPolicy: cdk.RemovalPolicy.DESTROY,
autoDeleteObjects: true,
});
}
}
// This test should pass:
test('Test complete S3 bucket configuration', () => {
const app = new cdk.App();
const stack = new S3CdkStack(app, 'S3CdkTestStack');
// ↓↓↓↓↓↓↓↓↓↓↓↓↓↓
expect(stack).toHaveResource('AWS::S3::Bucket', {
VersioningConfiguration: {
Status: 'Enabled',
},
WebsiteConfiguration: {
IndexDocument: 'index.html',
},
});
});
Bonus tip: .toHaveResourceLike()
.toHaveResource() works well when the resource property value you're checking is simple (ie. a scalar value like a string or a number), but if it's more complex you should look into .toHaveResourceLike() which unfortunately isn't very well documented (link to the v1.113.0 source).
For example, the CloudFormation template you've created via the S3CdkStack() class above contains a Lambda function with the following Description property:
{
"Resources": {
"CustomS3AutoDeleteObjectsCustomResourceProviderHandler9D90184F": {
"Type": "AWS::Lambda::Function",
"Properties": {
"Description": {
"Fn::Join": [
"",
[
"Lambda function for auto-deleting objects in ",
{
"Ref": "ReactGitHubActionBucket1CE57800"
},
" S3 bucket."
]
]
}
}
}
}
}
Testing for the existence of "auto-deleting" in that description (just as an expository example) with .toHaveResource() would require including the whole description object, which is fragile and contains what looks like a hash...
In this case it's much better to use .toHaveResourceLike() and check only the part of the object you're interested in testing:
test('Test a subset of the Lambda description', () => {
const app = new cdk.App();
const stack = new S3CdkStack(app, 'S3CdkTestStack');
// ↓↓↓↓
expect(stack).toHaveResourceLike('AWS::Lambda::Function', {
// Only need to include the subset of
// the value object you're testing:
Description: {
'Fn::Join': [
'',
['Lambda function for auto-deleting objects in '],
],
},
});
});

AWS Amplify CDK Cognito Identity Pool creation error

This is my cdk script,
import * as cdk from '#aws-cdk/core';
import * as codecommit from "#aws-cdk/aws-codecommit";
import * as amplify from "#aws-cdk/aws-amplify";
import * as cognito from "#aws-cdk/aws-cognito";
import * as iam from "#aws-cdk/aws-iam";
export class CdkdeployStack extends cdk.Stack {
constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
// Part 1 - Connect to Code Commit Repository
const codecommitRepo = codecommit.Repository.fromRepositoryName(
this,
"reactamplifyfullstackcdk",
"react-amplify-fullstack"
);
// Part 2 - Creation of the Amplify Application
const amplifyApp = new amplify.App(this, "reactamplifyfullstack", {
sourceCodeProvider: new amplify.CodeCommitSourceCodeProvider({
repository: codecommitRepo,
}),
});
const devBranch = amplifyApp.addBranch("dev");
//const qaBranch = amplifyApp.addBranch("qa");
//const stageBranch = amplifyApp.addBranch("stage");
// Creation of new Cognito UserPool
const userPool = new cognito.UserPool(
this,
"raf-userpool",
{
userPoolName: "reactamplifyfullstack_userpool",
selfSignUpEnabled: true,
autoVerify: {
email: true
},
signInAliases: {
email: true
}
}
);
const cfnUserPool = userPool.node.defaultChild as cognito.CfnUserPool;
cfnUserPool.policies = {
passwordPolicy: {
minimumLength: 8,
requireUppercase: true
}
};
//Creation of new Userpool client
const userPoolClient = new cognito.UserPoolClient(
this,
"reactamplifyfullstack_userpoolClient",
{
generateSecret: false,
userPool: userPool,
userPoolClientName: "reactamplifyfullstack_userpool_client_web"
}
);
//Creation of new Identity Pool
const identityPool = new cognito.CfnIdentityPool(
this,
"reactamplifyfullstack_identitypool",
{
allowUnauthenticatedIdentities: false,
cognitoIdentityProviders: [{
clientId: userPoolClient.userPoolClientId,
providerName: userPool.userPoolProviderName
}]
}
);
//Creation of new Authenticated Role for Identity Pool
const authenticatedRole = new iam.Role(
this,
"reactamplifyfullstack_auth_role",
{
assumedBy: new iam.FederatedPrincipal('cognit-identity.anazonaws.com', {
"StringEquals": {'cognit-identity.anazonaws.com:aud': identityPool.ref },
"ForAnyValue:StringLike": { 'cognit-identity.anazonaws.com:amr': "authenticated"},
}, "sts:AssumeRoleWithWebIdentity"),
}
);
//Add Policy to the IAM role
authenticatedRole.addToPolicy(new iam.PolicyStatement({
effect: iam.Effect.ALLOW,
actions: [
"mobileanalytics:*",
"cognito-sync:*",
"cognito-identity:*"
],
resources: ['*']
}));
//Set Default policy
const defaultPolicy = new cognito.CfnIdentityPoolRoleAttachment(this, "DefaultValid", {
identityPoolId: identityPool.ref,
roles: {
"authenticated": authenticatedRole.roleArn
}
});
//CDK output
new cdk.CfnOutput(this, 'aws_project_region', { value: 'ap-south-1'});
new cdk.CfnOutput(this, 'aws_cognito_identity_pool_id', { value: identityPool.ref });
new cdk.CfnOutput(this, 'aws_cognito_region', { value: 'ap-south-1' });
new cdk.CfnOutput(this, 'aws_user_pools_id', { value: userPool.userPoolId });
new cdk.CfnOutput(this, 'aws_user_pools_web_client_id', { value:
userPoolClient.userPoolClientId });
new cdk.CfnOutput(this, 'oauth', { value: '{}' });
}
}
When I am trying build,
npm run build
Showing the following error,
***> cdkdeploy#0.1.0 build
tsc
lib/cdkdeploy-stack.ts:57:7 - error TS2345: Argument of type 'this' is not assignable to parameter of type 'Construct'.
Type 'CdkdeployStack' is not assignable to type 'Construct'.
Types of property 'node' are incompatible.
Property 'addValidation' is missing in type 'import("/home/crypto/react/react-amplify-fullstack/cdkdeploy/node_modules/#aws-cdk/core/lib/construct-compat").ConstructNode' but required in type 'import("/home/crypto/react/react-amplify-fullstack/cdkdeploy/node_modules/#aws-cdk/aws-cognito/node_modules/#aws-cdk/core/lib/construct-compat").ConstructNode'.
57 this,
~~~~
node_modules/#aws-cdk/aws-cognito/node_modules/#aws-cdk/core/lib/construct-compat.d.ts:439:5
439 addValidation(validation: constructs.IValidation): void;
~~~~~~~~~~~~~
'addValidation' is declared here.
lib/cdkdeploy-stack.ts:91:69 - error TS2345: Argument of type 'this' is not assignable to parameter of type 'Construct'.
91 const defaultPolicy = new cognito.CfnIdentityPoolRoleAttachment(this, "DefaultValid", {
~~~~
Found 2 errors.
npm ERR! code 2
npm ERR! path /home/crypto/react/react-amplify-fullstack/cdkdeploy
npm ERR! command failed
npm ERR! command sh -c tsc
npm ERR! A complete log of this run can be found in:
npm ERR! /home/crypto/.npm/_logs/2021-02-24T08_34_57_019Z-debug.log***
Please help me tosolve this. Thanks in advance.
Your authrole strings have some typos
const authenticatedRole = new iam.Role(this, `hd-management-auth-role-${customProps.environment}`, {
assumedBy: new iam.FederatedPrincipal(
'cognit-identity.amazonaws.com',
{
'StringEquals': { 'cognito-identity.amazonaws.com:aud': identityPool.ref },
'ForAnyValue:StringLike': { 'cognito-identity.amazonaws.com:amr': 'authenticated' },
},
'sts:AssumeRoleWithWebIdentity'
),
});