UNPKG

aws-cdk

Version:

CDK Toolkit, the command line tool for CDK apps

781 lines 89.3 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); /* eslint-disable import/order */ jest.mock('../../lib/api/deploy-stack'); jest.mock('../../lib/util/asset-publishing'); const fake_cloudformation_stack_1 = require("./fake-cloudformation-stack"); const deployments_1 = require("../../lib/api/deployments"); const deploy_stack_1 = require("../../lib/api/deploy-stack"); const common_1 = require("../../lib/api/hotswap/common"); const toolkit_info_1 = require("../../lib/api/toolkit-info"); const cloudformation_1 = require("../../lib/api/util/cloudformation"); const util_1 = require("../util"); const mock_sdk_1 = require("../util/mock-sdk"); let sdkProvider; let deployments; let mockToolkitInfoLookup; let currentCfnStackResources; let numberOfTimesListStackResourcesWasCalled; beforeEach(() => { jest.resetAllMocks(); sdkProvider = new mock_sdk_1.MockSdkProvider(); deployments = new deployments_1.Deployments({ sdkProvider }); numberOfTimesListStackResourcesWasCalled = 0; currentCfnStackResources = {}; sdkProvider.stubCloudFormation({ listStackResources: ({ StackName: stackName }) => { numberOfTimesListStackResourcesWasCalled++; const stackResources = currentCfnStackResources[stackName]; if (!stackResources) { throw new Error(`Stack with id ${stackName} does not exist`); } return { StackResourceSummaries: stackResources, }; }, }); toolkit_info_1.ToolkitInfo.lookup = mockToolkitInfoLookup = jest.fn().mockResolvedValue(toolkit_info_1.ToolkitInfo.bootstrapStackNotFoundInfo('TestBootstrapStack')); }); function mockSuccessfulBootstrapStackLookup(props) { const outputs = { BucketName: 'BUCKET_NAME', BucketDomainName: 'BUCKET_ENDPOINT', BootstrapVersion: '1', ...props, }; const fakeStack = (0, mock_sdk_1.mockBootstrapStack)(sdkProvider.sdk, { Outputs: Object.entries(outputs).map(([k, v]) => ({ OutputKey: k, OutputValue: `${v}`, })), }); mockToolkitInfoLookup.mockResolvedValue(toolkit_info_1.ToolkitInfo.fromStack(fakeStack)); } test('passes through hotswap=true to deployStack()', async () => { // WHEN await deployments.deployStack({ stack: (0, util_1.testStack)({ stackName: 'boop', }), hotswap: common_1.HotswapMode.FALL_BACK, }); // THEN expect(deploy_stack_1.deployStack).toHaveBeenCalledWith(expect.objectContaining({ hotswap: common_1.HotswapMode.FALL_BACK, })); }); test('placeholders are substituted in CloudFormation execution role', async () => { await deployments.deployStack({ stack: (0, util_1.testStack)({ stackName: 'boop', properties: { cloudFormationExecutionRoleArn: 'bloop:${AWS::Region}:${AWS::AccountId}', }, }), }); expect(deploy_stack_1.deployStack).toHaveBeenCalledWith(expect.objectContaining({ roleArn: 'bloop:here:123456789012', })); }); test('role with placeholders is assumed if assumerole is given', async () => { const mockForEnvironment = jest.fn().mockImplementation(() => { return { sdk: sdkProvider.sdk }; }); sdkProvider.forEnvironment = mockForEnvironment; await deployments.deployStack({ stack: (0, util_1.testStack)({ stackName: 'boop', properties: { assumeRoleArn: 'bloop:${AWS::Region}:${AWS::AccountId}', }, }), }); expect(mockForEnvironment).toHaveBeenCalledWith(expect.anything(), expect.anything(), expect.objectContaining({ assumeRoleArn: 'bloop:here:123456789012', })); }); test('deployment fails if bootstrap stack is missing', async () => { await expect(deployments.deployStack({ stack: (0, util_1.testStack)({ stackName: 'boop', properties: { assumeRoleArn: 'bloop:${AWS::Region}:${AWS::AccountId}', requiresBootstrapStackVersion: 99, }, }), })).rejects.toThrow(/requires a bootstrap stack/); }); test('deployment fails if bootstrap stack is too old', async () => { mockSuccessfulBootstrapStackLookup({ BootstrapVersion: 5, }); await expect(deployments.deployStack({ stack: (0, util_1.testStack)({ stackName: 'boop', properties: { assumeRoleArn: 'bloop:${AWS::Region}:${AWS::AccountId}', requiresBootstrapStackVersion: 99, }, }), })).rejects.toThrow(/requires bootstrap stack version '99', found '5'/); }); test.each([false, true])('if toolkit stack be found: %p but SSM parameter name is present deployment succeeds', async (canLookup) => { if (canLookup) { mockSuccessfulBootstrapStackLookup({ BootstrapVersion: 2, }); } let requestedParameterName; sdkProvider.stubSSM({ getParameter(request) { requestedParameterName = request.Name; return { Parameter: { Value: '99', }, }; }, }); await deployments.deployStack({ stack: (0, util_1.testStack)({ stackName: 'boop', properties: { assumeRoleArn: 'bloop:${AWS::Region}:${AWS::AccountId}', requiresBootstrapStackVersion: 99, bootstrapStackVersionSsmParameter: '/some/parameter', }, }), }); expect(requestedParameterName).toEqual('/some/parameter'); }); test('readCurrentTemplateWithNestedStacks() can handle non-Resources in the template', async () => { const cfnStack = new fake_cloudformation_stack_1.FakeCloudformationStack({ stackName: 'ParentOfStackWithOutputAndParameter', stackId: 'StackId', }); cloudformation_1.CloudFormationStack.lookup = (async (_, stackName) => { switch (stackName) { case 'ParentOfStackWithOutputAndParameter': cfnStack.template = async () => ({ Resources: { NestedStack: { Type: 'AWS::CloudFormation::Stack', Properties: { TemplateURL: 'https://www.magic-url.com', }, Metadata: { 'aws:asset:path': 'one-output-one-param-stack.nested.template.json', }, }, }, }); break; case 'NestedStack': cfnStack.template = async () => ({ Resources: { NestedResource: { Type: 'AWS::Something', Properties: { Property: 'old-value', }, }, }, Parameters: { NestedParam: { Type: 'String', }, }, Outputs: { NestedOutput: { Value: { Ref: 'NestedResource', }, }, }, }); break; default: throw new Error('unknown stack name ' + stackName + ' found'); } return cfnStack; }); const rootStack = (0, util_1.testStack)({ stackName: 'ParentOfStackWithOutputAndParameter', template: { Resources: { NestedStack: { Type: 'AWS::CloudFormation::Stack', Properties: { TemplateURL: 'https://www.magic-url.com', }, Metadata: { 'aws:asset:path': 'one-output-one-param-stack.nested.template.json', }, }, }, }, }); pushStackResourceSummaries('ParentOfStackWithOutputAndParameter', stackSummaryOf('NestedStack', 'AWS::CloudFormation::Stack', 'arn:aws:cloudformation:bermuda-triangle-1337:123456789012:stack/NestedStack/abcd')); // WHEN const nestedStackCount = (await deployments.readCurrentTemplateWithNestedStacks(rootStack)).nestedStackCount; const deployedTemplate = (await deployments.readCurrentTemplateWithNestedStacks(rootStack)).deployedTemplate; // THEN expect(nestedStackCount).toEqual(1); expect(deployedTemplate).toEqual({ Resources: { NestedStack: { Type: 'AWS::CloudFormation::Stack', Properties: { TemplateURL: 'https://www.magic-url.com', NestedTemplate: { Resources: { NestedResource: { Type: 'AWS::Something', Properties: { Property: 'old-value', }, }, }, Outputs: { NestedOutput: { Value: { Ref: 'NestedResource', }, }, }, Parameters: { NestedParam: { Type: 'String', }, }, }, }, Metadata: { 'aws:asset:path': 'one-output-one-param-stack.nested.template.json', }, }, }, }); expect(rootStack.template).toEqual({ Resources: { NestedStack: { Type: 'AWS::CloudFormation::Stack', Properties: { TemplateURL: 'https://www.magic-url.com', NestedTemplate: { Resources: { NestedResource: { Type: 'AWS::Something', Properties: { Property: 'new-value', }, }, }, Outputs: { NestedOutput: { Value: { Ref: 'NestedResource', }, }, }, Parameters: { NestedParam: { Type: 'Number', }, }, }, }, Metadata: { 'aws:asset:path': 'one-output-one-param-stack.nested.template.json', }, }, }, }); }); test('readCurrentTemplateWithNestedStacks() with a 3-level nested + sibling structure works', async () => { const cfnStack = new fake_cloudformation_stack_1.FakeCloudformationStack({ stackName: 'MultiLevelRoot', stackId: 'StackId', }); cloudformation_1.CloudFormationStack.lookup = (async (_, stackName) => { switch (stackName) { case 'MultiLevelRoot': cfnStack.template = async () => ({ Resources: { NestedStack: { Type: 'AWS::CloudFormation::Stack', Properties: { TemplateURL: 'https://www.magic-url.com', }, Metadata: { 'aws:asset:path': 'one-resource-two-stacks-stack.nested.template.json', }, }, }, }); break; case 'NestedStack': cfnStack.template = async () => ({ Resources: { SomeResource: { Type: 'AWS::Something', Properties: { Property: 'old-value', }, }, GrandChildStackA: { Type: 'AWS::CloudFormation::Stack', Properties: { TemplateURL: 'https://www.magic-url.com', }, Metadata: { 'aws:asset:path': 'one-resource-stack.nested.template.json', }, }, GrandChildStackB: { Type: 'AWS::CloudFormation::Stack', Properties: { TemplateURL: 'https://www.magic-url.com', }, Metadata: { 'aws:asset:path': 'one-resource-stack.nested.template.json', }, }, }, }); break; case 'GrandChildStackA': cfnStack.template = async () => ({ Resources: { SomeResource: { Type: 'AWS::Something', Properties: { Property: 'old-value', }, }, }, }); break; case 'GrandChildStackB': cfnStack.template = async () => ({ Resources: { SomeResource: { Type: 'AWS::Something', Properties: { Property: 'old-value', }, }, }, }); break; default: throw new Error('unknown stack name ' + stackName + ' found in deployments.test.ts'); } return cfnStack; }); const rootStack = (0, util_1.testStack)({ stackName: 'MultiLevelRoot', template: { Resources: { NestedStack: { Type: 'AWS::CloudFormation::Stack', Properties: { TemplateURL: 'https://www.magic-url.com', }, Metadata: { 'aws:asset:path': 'one-resource-two-stacks-stack.nested.template.json', }, }, }, }, }); pushStackResourceSummaries('MultiLevelRoot', stackSummaryOf('NestedStack', 'AWS::CloudFormation::Stack', 'arn:aws:cloudformation:bermuda-triangle-1337:123456789012:stack/NestedStack/abcd')); pushStackResourceSummaries('NestedStack', stackSummaryOf('GrandChildStackA', 'AWS::CloudFormation::Stack', 'arn:aws:cloudformation:bermuda-triangle-1337:123456789012:stack/GrandChildStackA/abcd'), stackSummaryOf('GrandChildStackB', 'AWS::CloudFormation::Stack', 'arn:aws:cloudformation:bermuda-triangle-1337:123456789012:stack/GrandChildStackB/abcd')); pushStackResourceSummaries('GrandChildStackA', stackSummaryOf('NestedStack', 'AWS::CloudFormation::Stack', 'arn:aws:cloudformation:bermuda-triangle-1337:123456789012:stack/GrandChildA/abcd')); pushStackResourceSummaries('GrandChildStackB', stackSummaryOf('NestedStack', 'AWS::CloudFormation::Stack', 'arn:aws:cloudformation:bermuda-triangle-1337:123456789012:stack/GrandChildB/abcd')); // WHEN const nestedStackCount = (await deployments.readCurrentTemplateWithNestedStacks(rootStack)).nestedStackCount; const deployedTemplate = (await deployments.readCurrentTemplateWithNestedStacks(rootStack)).deployedTemplate; // THEN expect(nestedStackCount).toEqual(3); expect(deployedTemplate).toEqual({ Resources: { NestedStack: { Type: 'AWS::CloudFormation::Stack', Properties: { TemplateURL: 'https://www.magic-url.com', NestedTemplate: { Resources: { GrandChildStackA: { Type: 'AWS::CloudFormation::Stack', Properties: { TemplateURL: 'https://www.magic-url.com', NestedTemplate: { Resources: { SomeResource: { Type: 'AWS::Something', Properties: { Property: 'old-value', }, }, }, }, }, Metadata: { 'aws:asset:path': 'one-resource-stack.nested.template.json', }, }, GrandChildStackB: { Type: 'AWS::CloudFormation::Stack', Properties: { TemplateURL: 'https://www.magic-url.com', NestedTemplate: { Resources: { SomeResource: { Type: 'AWS::Something', Properties: { Property: 'old-value', }, }, }, }, }, Metadata: { 'aws:asset:path': 'one-resource-stack.nested.template.json', }, }, SomeResource: { Type: 'AWS::Something', Properties: { Property: 'old-value', }, }, }, }, }, Metadata: { 'aws:asset:path': 'one-resource-two-stacks-stack.nested.template.json', }, }, }, }); expect(rootStack.template).toEqual({ Resources: { NestedStack: { Type: 'AWS::CloudFormation::Stack', Properties: { TemplateURL: 'https://www.magic-url.com', NestedTemplate: { Resources: { GrandChildStackA: { Type: 'AWS::CloudFormation::Stack', Properties: { TemplateURL: 'https://www.magic-url.com', NestedTemplate: { Resources: { SomeResource: { Type: 'AWS::Something', Properties: { Property: 'new-value', }, }, }, }, }, Metadata: { 'aws:asset:path': 'one-resource-stack.nested.template.json', }, }, GrandChildStackB: { Type: 'AWS::CloudFormation::Stack', Properties: { TemplateURL: 'https://www.magic-url.com', NestedTemplate: { Resources: { SomeResource: { Type: 'AWS::Something', Properties: { Property: 'new-value', }, }, }, }, }, Metadata: { 'aws:asset:path': 'one-resource-stack.nested.template.json', }, }, SomeResource: { Type: 'AWS::Something', Properties: { Property: 'new-value', }, }, }, }, }, Metadata: { 'aws:asset:path': 'one-resource-two-stacks-stack.nested.template.json', }, }, }, }); }); test('readCurrentTemplateWithNestedStacks() on an undeployed parent stack with an (also undeployed) nested stack works', async () => { // GIVEN const cfnStack = new fake_cloudformation_stack_1.FakeCloudformationStack({ stackName: 'UndeployedParent', stackId: 'StackId', }); cloudformation_1.CloudFormationStack.lookup = (async (_cfn, _stackName) => { cfnStack.template = async () => ({}); return cfnStack; }); const rootStack = (0, util_1.testStack)({ stackName: 'UndeployedParent', template: { Resources: { NestedStack: { Type: 'AWS::CloudFormation::Stack', Properties: { TemplateURL: 'https://www.magic-url.com', }, Metadata: { 'aws:asset:path': 'one-resource-one-stack-stack.nested.template.json', }, }, }, }, }); // WHEN const nestedStackCount = (await deployments.readCurrentTemplateWithNestedStacks(rootStack)).nestedStackCount; const deployedTemplate = (await deployments.readCurrentTemplateWithNestedStacks(rootStack)).deployedTemplate; // THEN expect(nestedStackCount).toEqual(2); expect(deployedTemplate).toEqual({ Resources: { NestedStack: { Type: 'AWS::CloudFormation::Stack', Properties: { NestedTemplate: { Resources: { NestedStack: { Type: 'AWS::CloudFormation::Stack', Properties: { NestedTemplate: {}, }, }, }, }, }, }, }, }); }); test('readCurrentTemplateWithNestedStacks() caches calls to listStackResources()', async () => { // GIVEN const cfnStack = new fake_cloudformation_stack_1.FakeCloudformationStack({ stackName: 'CachingRoot', stackId: 'StackId', }); cloudformation_1.CloudFormationStack.lookup = (async (_cfn, _stackName) => { cfnStack.template = async () => ({ Resources: { NestedStackA: { Type: 'AWS::CloudFormation::Stack', Properties: { TemplateURL: 'https://www.magic-url.com', }, Metadata: { 'aws:asset:path': 'one-resource-stack.nested.template.json', }, }, NestedStackB: { Type: 'AWS::CloudFormation::Stack', Properties: { TemplateURL: 'https://www.magic-url.com', }, Metadata: { 'aws:asset:path': 'one-resource-stack.nested.template.json', }, }, }, }); return cfnStack; }); const rootStack = (0, util_1.testStack)({ stackName: 'CachingRoot', template: { Resources: { NestedStackA: { Type: 'AWS::CloudFormation::Stack', Properties: { TemplateURL: 'https://www.magic-url.com', }, Metadata: { 'aws:asset:path': 'one-resource-stack.nested.template.json', }, }, NestedStackB: { Type: 'AWS::CloudFormation::Stack', Properties: { TemplateURL: 'https://www.magic-url.com', }, Metadata: { 'aws:asset:path': 'one-resource-stack.nested.template.json', }, }, }, }, }); pushStackResourceSummaries('CachingRoot', stackSummaryOf('NestedStackA', 'AWS::CloudFormation::Stack', 'arn:aws:cloudformation:bermuda-triangle-1337:123456789012:stack/one-resource-stack/abcd'), stackSummaryOf('NestedStackB', 'AWS::CloudFormation::Stack', 'arn:aws:cloudformation:bermuda-triangle-1337:123456789012:stack/one-resource-stack/abcd')); // WHEN await deployments.readCurrentTemplateWithNestedStacks(rootStack); // THEN expect(numberOfTimesListStackResourcesWasCalled).toEqual(1); }); test('readCurrentTemplateWithNestedStacks() succesfully ignores stacks without metadata', async () => { // GIVEN const cfnStack = new fake_cloudformation_stack_1.FakeCloudformationStack({ stackName: 'MetadataRoot', stackId: 'StackId', }); cloudformation_1.CloudFormationStack.lookup = (async (_, stackName) => { if (stackName === 'MetadataRoot') { cfnStack.template = async () => ({ Resources: { WithMetadata: { Type: 'AWS::CloudFormation::Stack', Properties: { TemplateURL: 'https://www.magic-url.com', }, Metadata: { 'aws:asset:path': 'one-resource-stack.nested.template.json', }, }, }, }); } else { cfnStack.template = async () => ({ Resources: { SomeResource: { Type: 'AWS::Something', Properties: { Property: 'old-value', }, }, }, }); } return cfnStack; }); const rootStack = (0, util_1.testStack)({ stackName: 'MetadataRoot', template: { Resources: { WithoutMetadata: { Properties: { TemplateURL: 'https://www.magic-url.com', }, Type: 'AWS::CloudFormation::Stack', }, WithEmptyMetadata: { Type: 'AWS::CloudFormation::Stack', Properties: { TemplateURL: 'https://www.magic-url.com', }, Metadata: {}, }, WithMetadata: { Type: 'AWS::CloudFormation::Stack', Properties: { TemplateURL: 'https://www.magic-url.com', }, Metadata: { 'aws:asset:path': 'one-resource-stack.nested.template.json', }, }, }, }, }); pushStackResourceSummaries('MetadataRoot', stackSummaryOf('WithMetadata', 'AWS::CloudFormation::Stack', 'arn:aws:cloudformation:bermuda-triangle-1337:123456789012:stack/one-resource-stack/abcd')); // WHEN const nestedStackCount = (await deployments.readCurrentTemplateWithNestedStacks(rootStack)).nestedStackCount; const deployedTemplate = (await deployments.readCurrentTemplateWithNestedStacks(rootStack)).deployedTemplate; // THEN expect(nestedStackCount).toEqual(1); expect(deployedTemplate).toEqual({ Resources: { WithMetadata: { Type: 'AWS::CloudFormation::Stack', Properties: { TemplateURL: 'https://www.magic-url.com', NestedTemplate: { Resources: { SomeResource: { Type: 'AWS::Something', Properties: { Property: 'old-value', }, }, }, }, }, Metadata: { 'aws:asset:path': 'one-resource-stack.nested.template.json', }, }, }, }); expect(rootStack.template).toEqual({ Resources: { WithoutMetadata: { Type: 'AWS::CloudFormation::Stack', Properties: { TemplateURL: 'https://www.magic-url.com', }, }, WithEmptyMetadata: { Type: 'AWS::CloudFormation::Stack', Properties: { TemplateURL: 'https://www.magic-url.com', }, Metadata: {}, }, WithMetadata: { Type: 'AWS::CloudFormation::Stack', Properties: { TemplateURL: 'https://www.magic-url.com', NestedTemplate: { Resources: { SomeResource: { Type: 'AWS::Something', Properties: { Property: 'new-value', }, }, }, }, }, Metadata: { 'aws:asset:path': 'one-resource-stack.nested.template.json', }, }, }, }); }); function pushStackResourceSummaries(stackName, ...items) { if (!currentCfnStackResources[stackName]) { currentCfnStackResources[stackName] = []; } currentCfnStackResources[stackName].push(...items); } function stackSummaryOf(logicalId, resourceType, physicalResourceId) { return { LogicalResourceId: logicalId, PhysicalResourceId: physicalResourceId, ResourceType: resourceType, ResourceStatus: 'CREATE_COMPLETE', LastUpdatedTimestamp: new Date(), }; } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xvdWRmb3JtYXRpb24tZGVwbG95bWVudHMudGVzdC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbImNsb3VkZm9ybWF0aW9uLWRlcGxveW1lbnRzLnRlc3QudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7QUFBQSxpQ0FBaUM7QUFDakMsSUFBSSxDQUFDLElBQUksQ0FBQyw0QkFBNEIsQ0FBQyxDQUFDO0FBQ3hDLElBQUksQ0FBQyxJQUFJLENBQUMsaUNBQWlDLENBQUMsQ0FBQztBQUc3QywyRUFBc0U7QUFDdEUsMkRBQXdEO0FBQ3hELDZEQUF5RDtBQUN6RCx5REFBMkQ7QUFDM0QsNkRBQXlEO0FBQ3pELHNFQUF3RTtBQUN4RSxrQ0FBb0M7QUFDcEMsK0NBQXVFO0FBRXZFLElBQUksV0FBNEIsQ0FBQztBQUNqQyxJQUFJLFdBQXdCLENBQUM7QUFDN0IsSUFBSSxxQkFBZ0MsQ0FBQztBQUNyQyxJQUFJLHdCQUFrRixDQUFDO0FBQ3ZGLElBQUksd0NBQWdELENBQUM7QUFDckQsVUFBVSxDQUFDLEdBQUcsRUFBRTtJQUNkLElBQUksQ0FBQyxhQUFhLEVBQUUsQ0FBQztJQUNyQixXQUFXLEdBQUcsSUFBSSwwQkFBZSxFQUFFLENBQUM7SUFDcEMsV0FBVyxHQUFHLElBQUkseUJBQVcsQ0FBQyxFQUFFLFdBQVcsRUFBRSxDQUFDLENBQUM7SUFFL0Msd0NBQXdDLEdBQUcsQ0FBQyxDQUFDO0lBQzdDLHdCQUF3QixHQUFHLEVBQUUsQ0FBQztJQUM5QixXQUFXLENBQUMsa0JBQWtCLENBQUM7UUFDN0Isa0JBQWtCLEVBQUUsQ0FBQyxFQUFFLFNBQVMsRUFBRSxTQUFTLEVBQUUsRUFBRSxFQUFFO1lBQy9DLHdDQUF3QyxFQUFFLENBQUM7WUFDM0MsTUFBTSxjQUFjLEdBQUcsd0JBQXdCLENBQUMsU0FBUyxDQUFDLENBQUM7WUFDM0QsSUFBSSxDQUFDLGNBQWMsRUFBRTtnQkFDbkIsTUFBTSxJQUFJLEtBQUssQ0FBQyxpQkFBaUIsU0FBUyxpQkFBaUIsQ0FB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