-
Notifications
You must be signed in to change notification settings - Fork 4k
/
product-stack.ts
173 lines (153 loc) · 5.08 KB
/
product-stack.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
import * as crypto from 'crypto';
import * as fs from 'fs';
import * as path from 'path';
import { Construct } from 'constructs';
import { ProductStackSynthesizer } from './private/product-stack-synthesizer';
import { ProductStackHistory } from './product-stack-history';
import { IBucket } from '../../aws-s3';
import { ServerSideEncryption } from '../../aws-s3-deployment';
import * as cdk from '../../core';
/**
* Product stack props.
*/
export interface ProductStackProps {
/**
* A Bucket can be passed to store assets, enabling ProductStack Asset support
*
* @default - No Bucket provided and Assets will not be supported.
*/
readonly assetBucket?: IBucket;
/**
* A ServerSideEncryption can be enabled to encrypt assets that are put into assetBucket
*
* @default - No encryption is used
*/
readonly serverSideEncryption? : ServerSideEncryption;
/**
* For AWS_KMS ServerSideEncryption a KMS KeyId must be provided which will be used to encrypt assets
*
* @default - No KMS KeyId and SSE_KMS encryption cannot be used
*/
readonly serverSideEncryptionAwsKmsKeyId? : string;
/**
* The amount of memory (in MiB) to allocate to the AWS Lambda function which
* replicates the files from the CDK bucket to the destination bucket.
*
* If you are deploying large files, you will need to increase this number
* accordingly.
*
* @default 128
*/
readonly memoryLimit?: number;
/**
* A description of the stack.
*
* @default - No description.
*/
readonly description?: string;
/**
* Include runtime versioning information in this Stack
*
* @default - `analyticsReporting` setting of containing `App`, or value of
* 'aws:cdk:version-reporting' context key
*/
readonly analyticsReporting?: boolean;
}
/**
* A Service Catalog product stack, which is similar in form to a Cloudformation nested stack.
* You can add the resources to this stack that you want to define for your service catalog product.
*
* This stack will not be treated as an independent deployment
* artifact (won't be listed in "cdk list" or deployable through "cdk deploy"),
* but rather only synthesized as a template and uploaded as an asset to S3.
*
*/
export class ProductStack extends cdk.Stack {
public readonly templateFile: string;
private _parentProductStackHistory?: ProductStackHistory;
private _templateUrl?: string;
private _parentStack: cdk.Stack;
private assetBucket?: IBucket;
constructor(scope: Construct, id: string, props: ProductStackProps = {}) {
const parentStack = findParentStack(scope);
super(scope, id, {
analyticsReporting: props.analyticsReporting,
description: props.description,
synthesizer: new ProductStackSynthesizer({
parentStack,
assetBucket: props.assetBucket,
serverSideEncryption: props.serverSideEncryption,
serverSideEncryptionAwsKmsKeyId: props.serverSideEncryptionAwsKmsKeyId,
memoryLimit: props.memoryLimit,
}),
});
this._parentStack = parentStack;
// this is the file name of the synthesized template file within the cloud assembly
this.templateFile = `${cdk.Names.uniqueId(this)}.product.template.json`;
this.assetBucket = props.assetBucket;
}
/**
* Set the parent product stack history
*
* @internal
*/
public _setParentProductStackHistory(parentProductStackHistory: ProductStackHistory) {
return this._parentProductStackHistory = parentProductStackHistory;
}
/**
* Fetch the template URL.
*
* @internal
*/
public _getTemplateUrl(): string {
return cdk.Lazy.uncachedString({ produce: () => this._templateUrl });
}
/**
* Fetch the asset bucket.
*
* @internal
*/
public _getAssetBucket(): IBucket | undefined {
return this.assetBucket;
}
/**
* Fetch the parent Stack.
*
* @internal
*/
public _getParentStack(): cdk.Stack {
return this._parentStack;
}
/**
* Synthesize the product stack template, overrides the `super` class method.
*
* Defines an asset at the parent stack which represents the template of this
* product stack.
*
* @internal
*/
public _synthesizeTemplate(session: cdk.ISynthesisSession): void {
const cfn = JSON.stringify(this._toCloudFormation(), undefined, 2);
const templateHash = crypto.createHash('sha256').update(cfn).digest('hex');
this._templateUrl = this._parentStack.synthesizer.addFileAsset({
packaging: cdk.FileAssetPackaging.FILE,
sourceHash: templateHash,
fileName: this.templateFile,
}).httpUrl;
if (this._parentProductStackHistory) {
this._parentProductStackHistory._writeTemplateToSnapshot(cfn);
}
fs.writeFileSync(path.join(session.assembly.outdir, this.templateFile), cfn);
}
}
/**
* Validates the scope for a product stack, which must be defined within the scope of another `Stack`.
*/
function findParentStack(scope: Construct): cdk.Stack {
try {
const parentStack = cdk.Stack.of(scope);
return parentStack as cdk.Stack;
} catch {
throw new Error('Product stacks must be defined within scope of another non-product stack');
}
}