Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Service Catalog: Product Stack Assets S3 Keys are not present in Product template output #23560

Closed
padaszewski opened this issue Jan 4, 2023 · 10 comments
Assignees
Labels
@aws-cdk/aws-s3 Related to Amazon S3 bug This issue is a bug. effort/medium Medium work item – several days of effort p2

Comments

@padaszewski
Copy link

Describe the bug

Hi,
I'm implementing the Product Stack Assets feature in combination with ProductStackHistory into our app and I faced quite a strange behavior. When I play around in the sandbox everything works fine and I get the desired output, however if I switch to our production code and try to use this feature then the S3 Key of the Function is missing.

Expected Behavior

export class CdkHelloStack extends cdk.Stack {
  constructor(scope: Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);
    const testAssetBucket = new Bucket(this, 'TestAssetBucket', {
      bucketName: 'test-asset-bucket',
    });
    const productStack = new ProductStack(this, 'MyProductStack', {
      assetBucket: testAssetBucket,
    });
    const testBucketAutoDelete = new Bucket(productStack, 'testBucketAutoDelete', {
      bucketName: 'test-bucket-auto-delete',
      autoDeleteObjects: true,
      removalPolicy: RemovalPolicy.DESTROY
    })

    const psh = new ProductStackHistory(this, 'psh', {
        productStack: productStack,
        currentVersionName: 'v1',
        currentVersionLocked: false,
        validateTemplate: true,
        directory: 'version-snapshots'
      })
      const product = new CloudFormationProduct(this, `cfnProduct`, {
        productName: 'product name',
        owner: 'product owner',
        productVersions: [
          psh.currentVersion(),
        ],
      })
     const portfolio = new Portfolio(this, 'port', {
       displayName: 'portfolio',
       providerName: 'provider',
     })
     portfolio.shareWithAccount('123456789123')
     portfolio.addProduct(product)
  }
}

The product template has a Function with following properties (as expected):

"CustomS3AutoDeleteObjectsCustomResourceProviderHandler9D90184F": {
      "Type": "AWS::Lambda::Function",
      "Properties": {
        "Code": {
          "S3Bucket": "test-asset-bucket",
          "S3Key": "3f51abb709b8e65167a45aeed02bab11540603d909005d7148230ba5ce6c74d7.zip"
        },
        ...
 }

Metadata:

"Metadata": {
        "aws:cdk:path": "CdkHelloStack/MyProductStack/Custom::S3AutoDeleteObjectsCustomResourceProvider/Handler",
        "aws:asset:path": "asset.3f51abb709b8e65167a45aeed02bab11540603d909005d7148230ba5ce6c74d7",
        "aws:asset:property": "Code"
      }

Current Behavior

In the sandbox works correct, however in production code I get this output in every template and function (S3Key):

"CustomS3AutoDeleteObjectsCustomResourceProviderHandler9D90184F": {
      "Type": "AWS::Lambda::Function",
      "Properties": {
        "Code": {
          "S3Bucket": "test-asset-bucket",
          "S3Key": ".zip"
        },
        ...
 }

In the metadata it seems to be recognized:

"Metadata": {
        "aws:cdk:path": "79chars/Custom::S3AutoDeleteObjectsCustomResourceProvider/Handler",
        "aws:asset:path": "..\\asset.3f51abb709b8e65167a45aeed02bab11540603d909005d7148230ba5ce6c74d7",
        "aws:asset:property": "Code"
      }

Asset path metadata value looks different comparing to sandbox.

Reproduction Steps

I literally copied the whole sandbox code into the production app so I should get multiple and exactly the same product templates.
Again, in an empty CDK App everything works good. Can it be that this feature is some kind of bugged when the structure of the app is deeply nested or the path are long?
Our's looks like following: app -> pipelines -> waves -> stages -> stacks -> (constructs) -> product stacks
The output product template paths in cdk.out are also quite long:
assembly-56chars/75chars.product.template.json
I do not have any other idea on how to debug this. Any ideas on how to proceed?

Possible Solution

No response

Additional Information/Context

No response

CDK CLI Version

2.59.0 (build b24095d)

Framework Version

No response

Node.js Version

v16.15.0

OS

Windows

Language

Typescript

Language Version

TypeScript (4.9.4)

Other information

No response

@padaszewski padaszewski added bug This issue is a bug. needs-triage This issue or PR still needs to be triaged. labels Jan 4, 2023
@github-actions github-actions bot added the @aws-cdk/aws-s3 Related to Amazon S3 label Jan 4, 2023
@padaszewski
Copy link
Author

@wanjacki Hope is okay when I ping You to have a look at this

@wanjacki
Copy link
Contributor

wanjacki commented Jan 4, 2023

@padaszewski
Having trouble replicating your issue as I'm not sure how you were able to get a file path like
assembly-56chars/75chars.product.template.json for your template.
Might need a it more info on how you stack is setup. Is that template inside of a assembly-56chars directory?

Likely does not help but does setting an outdir directly on your app directly do anything?

const app = new App(
  { outdir: 'cdk.out' },
);

@padaszewski
Copy link
Author

@wanjacki
Thx for the quick reply. Yes, that template is inside of this directory. Outdir prop didn't helped. I've created a sandbox version which is failing so You can replicate this issue. Just copy paste and synth.

#!/usr/bin/env node
import 'source-map-support/register';
import * as cdk from 'aws-cdk-lib';
import {pipelines, RemovalPolicy, Stack, Stage, StageProps} from 'aws-cdk-lib';
import {Construct} from "constructs";
import {CodePipeline} from "aws-cdk-lib/pipelines";
import {Bucket} from "aws-cdk-lib/aws-s3";
import {
  CloudFormationProduct,
  Portfolio,
  ProductStack,
  ProductStackHistory
} from "aws-cdk-lib/aws-servicecatalog";

const app = new cdk.App({outdir: 'cdk.out'},);

class PipelineStack extends Stack {
  constructor(scope: Construct, id: string) {
    super(scope, id)
    const pipeline = new CodePipeline(this, this.stackName, {
      pipelineName: this.stackName,
      synth: new pipelines.ShellStep('Synth', {
        input: pipelines.CodePipelineSource.connection('owner/repo', 'prod', {
          connectionArn: 'arn:aws:codestar-connections:eu-west-1:123456789123:connection/some-uuid',
        }),
        commands: ['some command'],
      }),
    })

    const portfolioWave = pipeline.addWave('DeployPortfolios')
    const tenantsExamples = ['tenant1', 'tenant2', 'tenant3']
    tenantsExamples.forEach((tenant) => {
      portfolioWave.addStage(
          new PortfolioStage(this, `tenant-portfolio-${tenant}-prod`, {
            tenantName: tenant
          }),
      )
    })
  }
}

interface PortfolioStageProps extends StageProps {
  readonly tenantName: string
}

class PortfolioStage extends Stage {
  constructor(scope: Construct, id: string, props: PortfolioStageProps) {
    super(scope, id, props)

    const portfolioStack: Stack = new Stack(this, 'stk')
    new PortfolioConstruct(portfolioStack, 'ctk', {
      tenantName: props.tenantName
    })
  }
}

interface PortfolioConstructProps {
  readonly tenantName: string
}

class PortfolioConstruct extends Construct {
  constructor(scope: Construct, id: string, props: PortfolioConstructProps) {
    super(scope, id)
    const testAssetBucket = new Bucket(this, 'TestAssetBucket', {
      bucketName: 'test-asset-bucket',
    })
    const productStack = new ProductStack(this, 'MyProductStack', {
      assetBucket: testAssetBucket,
    })
    const testBucketAutoDelete = new Bucket(productStack, 'testBucketAutoDelete', {
      bucketName: 'test-bucket-auto-delete',
      autoDeleteObjects: true,
      removalPolicy: RemovalPolicy.DESTROY,
    })

    const psh = new ProductStackHistory(this, 'psh', {
      productStack: productStack,
      currentVersionName: 'v1',
      currentVersionLocked: false,
      validateTemplate: true,
      directory: 'version-snapshots',
    })
    const product = new CloudFormationProduct(this, `${props.tenantName}-CfnProduct`, {
      productName: 'product name',
      owner: 'product owner',
      productVersions: [psh.currentVersion()],
    })
    const portfolio = new Portfolio(this, 'port', {
      displayName: 'portfolio',
      providerName: 'provider',
    })

    portfolio.shareWithAccount('123456789123')
    portfolio.addProduct(product)
  }
}

new PipelineStack(app, 'this-iss-long-pipeline-stack-name')

@padaszewski
Copy link
Author

Besides that I would like to reuse the Asset Bucket in each PortfolioStage. Is that possible? I tried to create the Asset Bucket in the PipelineStack but this leads to cross stage Errors while synthing. Any cool idea for that?

@padaszewski
Copy link
Author

Hint: I found out, that the asset name is ..\asset.3f51abb709b8e65167a45aeed02bab11540603d909005d7148230ba5ce6c74d7
This lands into the ProductStackSynthesizer and this line produces .zip as output. The question is why this asset has this filename? The very cheap solution would be to change this line to:

const s3Filename = asset.fileName?.split('asset.')[1] + '.zip';

Or to use a appropriate RegExp. The question however is why some assets begin with asset.xyz and some with ..\asset.xyz?

@wanjacki
Copy link
Contributor

wanjacki commented Jan 5, 2023

@padaszewski
You are correct, the reason why it is ..\ instead of asset.xyz is because it is nested and it references the relative location of the assetfile (which is always under cdk.out).

I will put in a fix for this.

@wanjacki
Copy link
Contributor

wanjacki commented Jan 5, 2023

Besides that I would like to reuse the Asset Bucket in each PortfolioStage. Is that possible? I tried to create the Asset Bucket in the PipelineStack but this leads to cross stage Errors while synthing. Any cool idea for that?

Not sure how we can get around this. You might be able to use a referenced bucketfromBucketName , only thing we are able to add permissions to a referenced bucket, so it needs to be setup when the original bucket is created.

@padaszewski
Copy link
Author

@wanjacki
Great, can't wait! Thanks a lot.
About the buckets thing: For now it is okay with the 1000 hard limit, but we are introducing continously new tenants into our aplication. Means for every stage additional 3 buckets, with the two apps setup is 6.
For now we are good, but I think we have to figure someting out for the future.

mergify bot pushed a commit that referenced this issue Jan 12, 2023
#23580)

Currently when assets are nested in a directory, ProductStack is unable to parse the fileName and produces an incorrect template.
Since `asset.fileName` is the relative to the root of the cloud assembly, in which this asset source resides, 
we need to call `path.basename` in order to get the actual name of the file before parsing it to generate the `s3Filename`

More details found in #23560 


----

### All Submissions:

* [X] Have you followed the guidelines in our [Contributing guide?](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md)

*By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
@pahud
Copy link
Contributor

pahud commented Feb 22, 2023

I assume this should have been fixed in #23580

Closing now. Feel free to re-open if it's still relevant.

@pahud pahud closed this as completed Feb 22, 2023
@pahud pahud added p2 effort/medium Medium work item – several days of effort and removed needs-triage This issue or PR still needs to be triaged. labels Feb 22, 2023
@github-actions
Copy link

⚠️COMMENT VISIBILITY WARNING⚠️

Comments on closed issues are hard for our team to see.
If you need more assistance, please either tag a team member or open a new issue that references this one.
If you wish to keep having a conversation with other community members under this issue feel free to do so.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
@aws-cdk/aws-s3 Related to Amazon S3 bug This issue is a bug. effort/medium Medium work item – several days of effort p2
Projects
None yet
Development

No branches or pull requests

4 participants