aws / aws-cdk

The AWS Cloud Development Kit is a framework for defining cloud infrastructure in code
https://aws.amazon.com/cdk
Apache License 2.0
11.39k stars 3.79k forks source link

CDK deploy: using exclusively tag creates incorrect lambda artifacts in nested stack #22834

Open ahmed-anas opened 1 year ago

ahmed-anas commented 1 year ago

Describe the bug

creating a lambda in a nested stack (if it is not in the nested stack, it works fine) and deploying using --exclusively, the lambda artifact created is incorrect and contains the whole cdk folder. running only cdk deploy works fine.

Expected Behavior

cdk deploy and cdk deploy --exclusively stateless-stack-* should both work and be identical in the stack they create

Current Behavior

Reproduction Steps

this is the a sample reproduction file I created

#!/usr/bin/env node
import { App, CfnParameter } from 'aws-cdk-lib';

import 'source-map-support/register';
import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
import { SecurityGroup, SubnetType, Vpc } from 'aws-cdk-lib/aws-ec2';
import { NodejsFunction } from 'aws-cdk-lib/aws-lambda-nodejs';
import { Runtime } from 'aws-cdk-lib/aws-lambda';
import { StringParameter } from 'aws-cdk-lib/aws-ssm';
import { camelCase } from 'lodash';

/**
 * Creates the infra.
 *
 * @param config Configuration
 */
function createInfra() {
  const regionalEnv = 'aat2useast1';
  new StatelessStack(
    app,
    regionalEnv
  );
}

class StatelessStack extends cdk.Stack {
  constructor(
    scope: Construct,
    env: string) {
    super(scope, `stateless-stack-${env}`, {
      env: {
        account: 'XXXXX',
        region: 'us-east-1',
      }
    });
    new TempMiniStack2(this, env)
  }
}

class TempMiniStack2 extends cdk.NestedStack {
  constructor(
    scope: Construct,
    env: string
  ) {
    super(scope, `tempministack2-${env}`);
    const vpcId = 'vpc-XXXXX';

    stringParameter(this, `temp-mini-stack2-${env}`, `temp-mini-stack2-${env}`, `temp-mini-stack2-${env}`, `temp-mini-stack2-${env}`);

    let doDeploy = true;
    const vpc = Vpc.fromLookup(scope, `TestLambdaVpc${env}`, { vpcId });
    const sg = new SecurityGroup(this, 'sec-grp-lambda2-temp', {
      vpc: vpc,
      allowAllOutbound: true,
      securityGroupName: `lambda-security-group2-temp-${env}`
    })

    if (doDeploy) {
      new NodejsFunction(this, `test-lambda-name-${env}`, {
        vpc: vpc,
        vpcSubnets: { subnetType: SubnetType.PRIVATE_WITH_EGRESS },
        securityGroups: [sg],
        functionName: `test-lambda-funcname-${env}`,
        entry: `../lambdas/src/restoreabcfinish/restoreabcfinish.ts`,
        handler: 'handler',
        runtime: Runtime.NODEJS_14_X,
      });
    }
  }
}
function stringParameter(scope: Construct, env: string, resource: string, name: string, value: string) {
  const parameter = new StringParameter(scope, `${resource}-${name}-${env}`, {
    stringValue: value,
    parameterName: `/${env}/${resource}/${name}`,
  });
  (parameter.node.defaultChild as CfnParameter).overrideLogicalId(`${camelCase(`${resource}-${name}`)}SP`);
}

const app = new App();
createInfra();

The content of the lambda file are irrelevant for this test case but it is

export async function handler() {
  return {};
}

Possible Solution

Additional Information/Context

package.json

{
  "name": "cdk",
  "version": "0.1.0",
  "bin": {
    "ccab2-cdk": "bin/ccab-app.js"
  },
  "scripts": {
    "deploy:working": "cdk deploy",
    "deploy:notworking": "cdk deploy --exclusively stateless-stack-*"
  },
  "devDependencies": {
    "@aws-cdk/assert": "2.50.0",
    "@types/fs-extra": "9.0.13",
    "@types/jest": "27.5.1",
    "@types/lodash": "4.14.186",
    "@types/mustache": "4.2.1",
    "@types/nconf": "0.10.3",
    "@types/node": "14.18.31",
    "@types/yamljs": "0.2.31",
    "@types/json-schema": "^7.0.11",
    "@typescript-eslint/eslint-plugin": "5.38.1",
    "@typescript-eslint/parser": "5.38.1",
    "aws-cdk": "2.50.0",
    "esbuild": "0.15.10",
    "eslint": "8.24.0",
    "eslint-config-standard": "17.0.0",
    "eslint-plugin-import": "2.26.0",
    "eslint-plugin-jest": "26.4.6",
    "eslint-plugin-node": "11.1.0",
    "eslint-plugin-promise": "6.0.1",
    "jest": "27.5.1",
    "ts-jest": "27.1.4",
    "ts-node": "10.9.1",
    "typescript": "4.8.4"
  },
  "dependencies": {
    "@aws-cdk/aws-appsync-alpha": "2.50.0-alpha.0",
    "@aws-cdk/aws-glue-alpha": "2.50.0-alpha.0",
    "@aws-cdk/aws-synthetics-alpha": "2.50.0-alpha.0",
    "@aws-solutions-constructs/aws-kinesisfirehose-s3": "2.25.0",
    "aws-cdk-lib": "2.50.0",
    "cdk-ec2-key-pair": "3.3.1",
    "cdk-iam-floyd": "0.437.0",
    "clone": "2.1.2",
    "constructs": "10.1.120",
    "fs-extra": "10.1.0",
    "mustache": "4.2.0",
    "nconf": "0.12.0",
    "source-map-support": "0.5.21",
    "yamljs": "0.3.0",
    "lodash": "4.17.21",
    "date-fns": "2.29.3"
  },
  "overrides": {
    "colors": "1.4.0",
    "cdk-iam-floyd": "0.437.0"
  }
}

CDK CLI Version

2.50.0

Framework Version

No response

Node.js Version

14

OS

Linux

Language

Typescript

Language Version

14.19.0

Other information

AWS support Case ID 11227109211

boillodmanuel commented 1 year ago

SOLVED in my case - seems a bit different

I had the same issue with a different scenario: the lamda asset contains the whole cdk folder instead of the lambda code folder.

When I run the cdk diff with --exclusively flag, the asset is correct (contains lambda code folder):

$ cdk diff <STACK> --exclusively

Bundling asset <STACK>/Handler/Code/Stage...

  cdk.out\bundling-temp-1dee8aac52600b32dee4d83c9d5779ad4fa5c468099c86605997d49353d32807\index.js      2.2mb
  cdk.out\bundling-temp-1dee8aac52600b32dee4d83c9d5779ad4fa5c468099c86605997d49353d32807\index.js.map  3.3mb

Done in 386ms
Stack <STACK> (<STACK>)

Resources
[~] AWS::Lambda::Function<FUNCTION_NAME>/Handler Handler886CB40B 
 ├─ [~] Code
 │   └─ [~] .S3Key:
 │       ├─ [-] 6739e30f6cee08ec5950b7cf6077bf601387ec83ca97c857095a92a422a61026.zip
 │       └─ [+] 88c7181e5b3e3e4fc4b91c4cf5c329c3d052de45d21e52adb11fc0de93102275.zip
 └─ [~] Metadata
     └─ [~] .aws:asset:path:
         ├─ [-] ..\asset.6739e30f6cee08ec5950b7cf6077bf601387ec83ca97c857095a92a422a61026
         └─ [+] ..\asset.88c7181e5b3e3e4fc4b91c4cf5c329c3d052de45d21e52adb11fc0de93102275

When I run the cdk diff without --exclusively flag, the asset is invalid (contains CDK folder):

$ cdk diff <STACK>

Including dependency stacks:<STACK_DEP1>, <STACK_DEP2>
Stack <STACK_DEP1> (<STACK_DEP1>)
There were no differences
Stack <STACK_DEP2> (<STACK_DEP2>)
There were no differences
Stack<STACK> (<STACK>)
Resources
[~] AWS::Lambda::Function <FUNCTION_NAME>/Handler Handler886CB40B 
 ├─ [~] Code
 │   └─ [~] .S3Key:
 │       ├─ [-] 6739e30f6cee08ec5950b7cf6077bf601387ec83ca97c857095a92a422a61026.zip
 │       └─ [+] 8d233736a3522a3bb4c3bc2c2f70cf637b01500f89560e73e579cbd5ea36e92a.zip
 └─ [~] Metadata
     └─ [~] .aws:asset:path:
         ├─ [-] ..\asset.6739e30f6cee08ec5950b7cf6077bf601387ec83ca97c857095a92a422a61026
         └─ [+] C:\Users\<CDK_PROJECT_PATH>

In the later case, we can notice that Bundling asset is not done and asset path is the local CDK folder.

I was using:

After upgrading aws-cdk to 2.50.0, the issue is gone. 🕺 🥳

ahmed-anas commented 1 year ago

Tested on 2.50.0 as well with no luck. Here is the package.json file

{
  "name": "cdk",
  "version": "0.1.0",
  "bin": {
    "ccab2-cdk": "bin/ccab-app.js"
  },
  "scripts": {
    "deploy:working": "cdk deploy",
    "deploy:notworking": "cdk deploy --exclusively stateless-stack-*"
  },
  "devDependencies": {
    "@aws-cdk/assert": "2.50.0",
    "@types/fs-extra": "9.0.13",
    "@types/jest": "27.5.1",
    "@types/lodash": "4.14.186",
    "@types/mustache": "4.2.1",
    "@types/nconf": "0.10.3",
    "@types/node": "14.18.31",
    "@types/yamljs": "0.2.31",
    "@types/json-schema": "^7.0.11",
    "@typescript-eslint/eslint-plugin": "5.38.1",
    "@typescript-eslint/parser": "5.38.1",
    "aws-cdk": "2.50.0",
    "esbuild": "0.15.10",
    "eslint": "8.24.0",
    "eslint-config-standard": "17.0.0",
    "eslint-plugin-import": "2.26.0",
    "eslint-plugin-jest": "26.4.6",
    "eslint-plugin-node": "11.1.0",
    "eslint-plugin-promise": "6.0.1",
    "jest": "27.5.1",
    "ts-jest": "27.1.4",
    "ts-node": "10.9.1",
    "typescript": "4.8.4"
  },
  "dependencies": {
    "@aws-cdk/aws-appsync-alpha": "2.50.0-alpha.0",
    "@aws-cdk/aws-glue-alpha": "2.50.0-alpha.0",
    "@aws-cdk/aws-synthetics-alpha": "2.50.0-alpha.0",
    "@aws-solutions-constructs/aws-kinesisfirehose-s3": "2.25.0",
    "aws-cdk-lib": "2.50.0",
    "cdk-ec2-key-pair": "3.3.1",
    "cdk-iam-floyd": "0.437.0",
    "clone": "2.1.2",
    "constructs": "10.1.120",
    "fs-extra": "10.1.0",
    "mustache": "4.2.0",
    "nconf": "0.12.0",
    "source-map-support": "0.5.21",
    "yamljs": "0.3.0",
    "lodash": "4.17.21",
    "date-fns": "2.29.3"
  },
  "overrides": {
    "colors": "1.4.0",
    "cdk-iam-floyd": "0.437.0"
  }
}
corymhall commented 1 year ago

I think this is due to #21248. I think the simplest fix might be to update the bundlingRequired function to match on the parent stack if it is a nested stack.

Maybe something like:

return bundlingStacks.some(pattern => minimatch(
  this.nestedStackParent?.node.id ?? this.node.id,
  pattern,
});
corymhall commented 1 year ago

As a temporary workaround you might be able to do:

cdk deploy --exclusively '{stateless-stack-*,stateless-stack-*/*}'
diranged commented 5 months ago

This issue is now over a year old and has no resolution?

TirushV commented 3 weeks ago

This is the biggest bug i have ever seen in cdk usage throughout. Its very sad and disappointing to see that it has been 2 years the issue is still open and AWS is not looking at these genuine issues.

@AWS-MattB