@bitblit/ratchet-epsilon-deployment 4.0.85-alpha
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +177 -0
- package/License.txt +13 -0
- package/README.md +242 -0
- package/dist/cjs/build/ratchet-epsilon-deployment-info.js +18 -0
- package/dist/cjs/deployment/cdk/epsilon-api-stack-props.js +2 -0
- package/dist/cjs/deployment/cdk/epsilon-api-stack.js +182 -0
- package/dist/cjs/deployment/cdk/epsilon-stack-util.js +71 -0
- package/dist/cjs/deployment/cdk/epsilon-website-stack-props.js +8 -0
- package/dist/cjs/deployment/cdk/epsilon-website-stack.js +157 -0
- package/dist/cjs/deployment/cdk/simple-additional-s3-website-mapping.js +2 -0
- package/dist/cjs/index.js +10 -0
- package/dist/es/build/ratchet-epsilon-deployment-info.js +14 -0
- package/dist/es/deployment/cdk/epsilon-api-stack-props.js +1 -0
- package/dist/es/deployment/cdk/epsilon-api-stack.js +178 -0
- package/dist/es/deployment/cdk/epsilon-stack-util.js +67 -0
- package/dist/es/deployment/cdk/epsilon-website-stack-props.js +5 -0
- package/dist/es/deployment/cdk/epsilon-website-stack.js +151 -0
- package/dist/es/deployment/cdk/simple-additional-s3-website-mapping.js +1 -0
- package/dist/es/index.js +7 -0
- package/dist/tsconfig.cjs.tsbuildinfo +1 -0
- package/dist/tsconfig.es.tsbuildinfo +1 -0
- package/dist/tsconfig.types.tsbuildinfo +1 -0
- package/dist/types/build/ratchet-epsilon-deployment-info.d.ts +5 -0
- package/dist/types/deployment/cdk/epsilon-api-stack-props.d.ts +17 -0
- package/dist/types/deployment/cdk/epsilon-api-stack.d.ts +9 -0
- package/dist/types/deployment/cdk/epsilon-stack-util.d.ts +13 -0
- package/dist/types/deployment/cdk/epsilon-website-stack-props.d.ts +15 -0
- package/dist/types/deployment/cdk/epsilon-website-stack.d.ts +13 -0
- package/dist/types/deployment/cdk/simple-additional-s3-website-mapping.d.ts +4 -0
- package/dist/types/index.d.ts +10 -0
- package/package.json +71 -0
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.EpsilonStackUtil = void 0;
|
|
4
|
+
const ratchet_common_1 = require("@bitblit/ratchet-common");
|
|
5
|
+
const aws_iam_1 = require("aws-cdk-lib/aws-iam");
|
|
6
|
+
class EpsilonStackUtil {
|
|
7
|
+
constructor() { }
|
|
8
|
+
static toEnvironmentVariables(input) {
|
|
9
|
+
const rval = [];
|
|
10
|
+
input.forEach((inval) => {
|
|
11
|
+
Object.keys(inval).forEach((k) => {
|
|
12
|
+
rval.push({
|
|
13
|
+
name: k,
|
|
14
|
+
value: ratchet_common_1.StringRatchet.safeString(inval[k]),
|
|
15
|
+
});
|
|
16
|
+
});
|
|
17
|
+
});
|
|
18
|
+
return rval;
|
|
19
|
+
}
|
|
20
|
+
static createDefaultPolicyStatementList(props, backgroundLambdaSqs, backgroundLambdaSns, interApiSns) {
|
|
21
|
+
const rval = (props.additionalPolicyStatements || []).concat([
|
|
22
|
+
new aws_iam_1.PolicyStatement({
|
|
23
|
+
effect: aws_iam_1.Effect.ALLOW,
|
|
24
|
+
actions: ['logs:CreateLogGroup', 'logs:CreateLogStream', 'logs:PutLogEvents'],
|
|
25
|
+
resources: ['arn:aws:logs:*:*:*'],
|
|
26
|
+
}),
|
|
27
|
+
new aws_iam_1.PolicyStatement({
|
|
28
|
+
effect: aws_iam_1.Effect.ALLOW,
|
|
29
|
+
actions: ['ses:SendEmail', 'ses:SendRawEmail'],
|
|
30
|
+
resources: ['arn:aws:ses:*'],
|
|
31
|
+
}),
|
|
32
|
+
new aws_iam_1.PolicyStatement({
|
|
33
|
+
effect: aws_iam_1.Effect.ALLOW,
|
|
34
|
+
actions: ['sqs:*'],
|
|
35
|
+
resources: [backgroundLambdaSqs.queueArn],
|
|
36
|
+
}),
|
|
37
|
+
new aws_iam_1.PolicyStatement({
|
|
38
|
+
effect: aws_iam_1.Effect.ALLOW,
|
|
39
|
+
actions: ['sns:*'],
|
|
40
|
+
resources: [backgroundLambdaSns.topicArn, interApiSns.topicArn],
|
|
41
|
+
}),
|
|
42
|
+
new aws_iam_1.PolicyStatement({
|
|
43
|
+
effect: aws_iam_1.Effect.ALLOW,
|
|
44
|
+
actions: ['batch:*'],
|
|
45
|
+
resources: ['*'],
|
|
46
|
+
}),
|
|
47
|
+
]);
|
|
48
|
+
return rval;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
exports.EpsilonStackUtil = EpsilonStackUtil;
|
|
52
|
+
EpsilonStackUtil.ALLOW_ECS = new aws_iam_1.PolicyStatement({
|
|
53
|
+
effect: aws_iam_1.Effect.ALLOW,
|
|
54
|
+
actions: ['ecs:*'],
|
|
55
|
+
resources: ['*'],
|
|
56
|
+
});
|
|
57
|
+
EpsilonStackUtil.ALLOW_ECR = new aws_iam_1.PolicyStatement({
|
|
58
|
+
effect: aws_iam_1.Effect.ALLOW,
|
|
59
|
+
actions: ['ecr:BatchCheckLayerAvailability', 'ecr:BatchGetImage', 'ecr:GetDownloadUrlForLayer', 'ecr:GetAuthorizationToken'],
|
|
60
|
+
resources: ['*'],
|
|
61
|
+
});
|
|
62
|
+
EpsilonStackUtil.ALLOW_RESTRICTED_LOGS = new aws_iam_1.PolicyStatement({
|
|
63
|
+
effect: aws_iam_1.Effect.ALLOW,
|
|
64
|
+
actions: ['logs:CreateLogStream', 'logs:PutLogEvents', 'logs:DescribeLogStreams', 'logs:CreateLogGroup'],
|
|
65
|
+
resources: ['*'],
|
|
66
|
+
});
|
|
67
|
+
EpsilonStackUtil.ECS_POLICY_STATEMENTS = [
|
|
68
|
+
EpsilonStackUtil.ALLOW_ECS,
|
|
69
|
+
EpsilonStackUtil.ALLOW_ECR,
|
|
70
|
+
EpsilonStackUtil.ALLOW_RESTRICTED_LOGS,
|
|
71
|
+
];
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.EpsilonWebsiteStackPropsRoute53Handling = void 0;
|
|
4
|
+
var EpsilonWebsiteStackPropsRoute53Handling;
|
|
5
|
+
(function (EpsilonWebsiteStackPropsRoute53Handling) {
|
|
6
|
+
EpsilonWebsiteStackPropsRoute53Handling["Update"] = "Update";
|
|
7
|
+
EpsilonWebsiteStackPropsRoute53Handling["DoNotUpdate"] = "DoNotUpdate";
|
|
8
|
+
})(EpsilonWebsiteStackPropsRoute53Handling = exports.EpsilonWebsiteStackPropsRoute53Handling || (exports.EpsilonWebsiteStackPropsRoute53Handling = {}));
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.EpsilonWebsiteStack = void 0;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
const aws_s3_1 = require("aws-cdk-lib/aws-s3");
|
|
6
|
+
const aws_cdk_lib_1 = require("aws-cdk-lib");
|
|
7
|
+
const path_1 = tslib_1.__importDefault(require("path"));
|
|
8
|
+
const aws_cloudfront_1 = require("aws-cdk-lib/aws-cloudfront");
|
|
9
|
+
const aws_route53_1 = require("aws-cdk-lib/aws-route53");
|
|
10
|
+
const aws_route53_targets_1 = require("aws-cdk-lib/aws-route53-targets");
|
|
11
|
+
const aws_s3_deployment_1 = require("aws-cdk-lib/aws-s3-deployment");
|
|
12
|
+
const epsilon_website_stack_props_1 = require("./epsilon-website-stack-props");
|
|
13
|
+
const ratchet_common_1 = require("@bitblit/ratchet-common");
|
|
14
|
+
class EpsilonWebsiteStack extends aws_cdk_lib_1.Stack {
|
|
15
|
+
constructor(scope, id, props) {
|
|
16
|
+
var _a;
|
|
17
|
+
super(scope, id, props);
|
|
18
|
+
const originAccessId = new aws_cloudfront_1.OriginAccessIdentity(this, id + 'OriginAccessId');
|
|
19
|
+
const websiteBucket = new aws_s3_1.Bucket(this, id + 'DeployBucket', {
|
|
20
|
+
bucketName: props.targetBucketName,
|
|
21
|
+
versioned: false,
|
|
22
|
+
publicReadAccess: false,
|
|
23
|
+
encryption: aws_s3_1.BucketEncryption.S3_MANAGED,
|
|
24
|
+
});
|
|
25
|
+
const extraBucketAndSource = (props.simpleAdditionalMappings || []).map((eb) => {
|
|
26
|
+
const nextBucket = new aws_s3_1.Bucket(this, eb.bucketName + 'DeployBucket', {
|
|
27
|
+
bucketName: eb.bucketName,
|
|
28
|
+
versioned: false,
|
|
29
|
+
publicReadAccess: false,
|
|
30
|
+
encryption: aws_s3_1.BucketEncryption.S3_MANAGED,
|
|
31
|
+
});
|
|
32
|
+
const nextBS = {
|
|
33
|
+
bucket: nextBucket,
|
|
34
|
+
sourceConfig: {
|
|
35
|
+
s3OriginSource: {
|
|
36
|
+
s3BucketSource: nextBucket,
|
|
37
|
+
originAccessIdentity: originAccessId,
|
|
38
|
+
},
|
|
39
|
+
behaviors: [
|
|
40
|
+
{
|
|
41
|
+
pathPattern: eb.pathPattern,
|
|
42
|
+
isDefaultBehavior: false,
|
|
43
|
+
compress: true,
|
|
44
|
+
defaultTtl: aws_cdk_lib_1.Duration.seconds(1),
|
|
45
|
+
minTtl: aws_cdk_lib_1.Duration.seconds(1),
|
|
46
|
+
maxTtl: aws_cdk_lib_1.Duration.seconds(1),
|
|
47
|
+
forwardedValues: {
|
|
48
|
+
queryString: false,
|
|
49
|
+
},
|
|
50
|
+
},
|
|
51
|
+
],
|
|
52
|
+
},
|
|
53
|
+
};
|
|
54
|
+
return nextBS;
|
|
55
|
+
});
|
|
56
|
+
const assetSource = {
|
|
57
|
+
s3OriginSource: {
|
|
58
|
+
s3BucketSource: websiteBucket,
|
|
59
|
+
originAccessIdentity: originAccessId,
|
|
60
|
+
},
|
|
61
|
+
behaviors: [
|
|
62
|
+
{
|
|
63
|
+
isDefaultBehavior: true,
|
|
64
|
+
compress: true,
|
|
65
|
+
defaultTtl: aws_cdk_lib_1.Duration.seconds(1),
|
|
66
|
+
minTtl: aws_cdk_lib_1.Duration.seconds(1),
|
|
67
|
+
maxTtl: aws_cdk_lib_1.Duration.seconds(1),
|
|
68
|
+
forwardedValues: {
|
|
69
|
+
queryString: false,
|
|
70
|
+
},
|
|
71
|
+
},
|
|
72
|
+
],
|
|
73
|
+
};
|
|
74
|
+
const apiSource = {
|
|
75
|
+
customOriginSource: {
|
|
76
|
+
domainName: props.apiDomainName,
|
|
77
|
+
originProtocolPolicy: aws_cloudfront_1.OriginProtocolPolicy.HTTPS_ONLY,
|
|
78
|
+
},
|
|
79
|
+
behaviors: [
|
|
80
|
+
{
|
|
81
|
+
compress: true,
|
|
82
|
+
forwardedValues: {
|
|
83
|
+
queryString: true,
|
|
84
|
+
cookies: {
|
|
85
|
+
forward: 'whitelist',
|
|
86
|
+
whitelistedNames: ['idToken'],
|
|
87
|
+
},
|
|
88
|
+
headers: ['Accept', 'Referer', 'Authorization', 'Content-Type'],
|
|
89
|
+
},
|
|
90
|
+
pathPattern: 'graphql',
|
|
91
|
+
defaultTtl: aws_cdk_lib_1.Duration.seconds(0),
|
|
92
|
+
maxTtl: aws_cdk_lib_1.Duration.seconds(0),
|
|
93
|
+
minTtl: aws_cdk_lib_1.Duration.seconds(0),
|
|
94
|
+
allowedMethods: aws_cloudfront_1.CloudFrontAllowedMethods.ALL,
|
|
95
|
+
cachedMethods: aws_cloudfront_1.CloudFrontAllowedCachedMethods.GET_HEAD,
|
|
96
|
+
},
|
|
97
|
+
],
|
|
98
|
+
};
|
|
99
|
+
const distributionProps = {
|
|
100
|
+
httpVersion: aws_cloudfront_1.HttpVersion.HTTP2,
|
|
101
|
+
defaultRootObject: 'index.html',
|
|
102
|
+
originConfigs: [assetSource, apiSource, ...extraBucketAndSource.map((s) => s.sourceConfig)],
|
|
103
|
+
errorConfigurations: [
|
|
104
|
+
{
|
|
105
|
+
errorCode: 404,
|
|
106
|
+
errorCachingMinTtl: 300,
|
|
107
|
+
responseCode: 200,
|
|
108
|
+
responsePagePath: '/index.html',
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
errorCode: 403,
|
|
112
|
+
errorCachingMinTtl: 300,
|
|
113
|
+
responseCode: 200,
|
|
114
|
+
responsePagePath: '/index.html',
|
|
115
|
+
},
|
|
116
|
+
],
|
|
117
|
+
priceClass: aws_cloudfront_1.PriceClass.PRICE_CLASS_ALL,
|
|
118
|
+
viewerProtocolPolicy: aws_cloudfront_1.ViewerProtocolPolicy.REDIRECT_TO_HTTPS,
|
|
119
|
+
viewerCertificate: {
|
|
120
|
+
aliases: props.cloudFrontDomainNames,
|
|
121
|
+
props: {
|
|
122
|
+
acmCertificateArn: props.cloudFrontHttpsCertificateArn,
|
|
123
|
+
sslSupportMethod: 'sni-only',
|
|
124
|
+
},
|
|
125
|
+
},
|
|
126
|
+
};
|
|
127
|
+
const cloudfrontDistro = new aws_cloudfront_1.CloudFrontWebDistribution(this, id + 'CloudfrontDistro', distributionProps);
|
|
128
|
+
if ((props === null || props === void 0 ? void 0 : props.route53Handling) === epsilon_website_stack_props_1.EpsilonWebsiteStackPropsRoute53Handling.Update) {
|
|
129
|
+
if ((_a = props === null || props === void 0 ? void 0 : props.cloudFrontDomainNames) === null || _a === void 0 ? void 0 : _a.length) {
|
|
130
|
+
for (let i = 0; i < props.cloudFrontDomainNames.length; i++) {
|
|
131
|
+
const domain = new aws_route53_1.RecordSet(this, id + 'DomainName-' + props.cloudFrontDomainNames[i], {
|
|
132
|
+
recordType: aws_route53_1.RecordType.A,
|
|
133
|
+
recordName: props.cloudFrontDomainNames[i],
|
|
134
|
+
target: {
|
|
135
|
+
aliasTarget: new aws_route53_targets_1.CloudFrontTarget(cloudfrontDistro),
|
|
136
|
+
},
|
|
137
|
+
zone: aws_route53_1.HostedZone.fromLookup(this, id, { domainName: EpsilonWebsiteStack.extractApexDomain(props.cloudFrontDomainNames[i]) }),
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
new aws_s3_deployment_1.BucketDeployment(this, id + 'SiteDeploy', {
|
|
143
|
+
sources: props.pathsToAssets.map((inPath) => aws_s3_deployment_1.Source.asset(path_1.default.resolve(inPath))),
|
|
144
|
+
destinationBucket: websiteBucket,
|
|
145
|
+
distribution: cloudfrontDistro,
|
|
146
|
+
distributionPaths: ['/*'],
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
static extractApexDomain(domainName) {
|
|
150
|
+
const pieces = ratchet_common_1.StringRatchet.trimToEmpty(domainName).split('.');
|
|
151
|
+
if (pieces.length < 2) {
|
|
152
|
+
ratchet_common_1.ErrorRatchet.throwFormattedErr('Not a valid domain name : %s', domainName);
|
|
153
|
+
}
|
|
154
|
+
return pieces[pieces.length - 2] + '.' + pieces[pieces.length - 1];
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
exports.EpsilonWebsiteStack = EpsilonWebsiteStack;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const tslib_1 = require("tslib");
|
|
4
|
+
tslib_1.__exportStar(require("./build/ratchet-epsilon-deployment-info"), exports);
|
|
5
|
+
tslib_1.__exportStar(require("./deployment/cdk/epsilon-api-stack-props"), exports);
|
|
6
|
+
tslib_1.__exportStar(require("./deployment/cdk/epsilon-api-stack"), exports);
|
|
7
|
+
tslib_1.__exportStar(require("./deployment/cdk/epsilon-stack-util"), exports);
|
|
8
|
+
tslib_1.__exportStar(require("./deployment/cdk/epsilon-website-stack-props"), exports);
|
|
9
|
+
tslib_1.__exportStar(require("./deployment/cdk/epsilon-website-stack"), exports);
|
|
10
|
+
tslib_1.__exportStar(require("./deployment/cdk/simple-additional-s3-website-mapping"), exports);
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export class RatchetEpsilonDeploymentInfo {
|
|
2
|
+
constructor() { }
|
|
3
|
+
static buildInformation() {
|
|
4
|
+
const val = {
|
|
5
|
+
version: '85',
|
|
6
|
+
hash: '7b2bd151314408f02450300b0d5fb0e008d38deb',
|
|
7
|
+
branch: 'alpha-2023-03-14-1',
|
|
8
|
+
tag: 'alpha-2023-03-14-1',
|
|
9
|
+
timeBuiltISO: '2023-03-14T20:58:46-0700',
|
|
10
|
+
notes: '',
|
|
11
|
+
};
|
|
12
|
+
return val;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
import { Duration, Lazy, Size, Stack } from 'aws-cdk-lib';
|
|
2
|
+
import { CfnComputeEnvironment, CfnJobDefinition, CfnJobQueue, } from 'aws-cdk-lib/aws-batch';
|
|
3
|
+
import { DockerImageCode, DockerImageFunction, FunctionUrlAuthType, HttpMethod } from 'aws-cdk-lib/aws-lambda';
|
|
4
|
+
import { CfnInstanceProfile, ManagedPolicy, PolicyDocument, Role, ServicePrincipal } from 'aws-cdk-lib/aws-iam';
|
|
5
|
+
import { Topic } from 'aws-cdk-lib/aws-sns';
|
|
6
|
+
import { Queue } from 'aws-cdk-lib/aws-sqs';
|
|
7
|
+
import { LambdaSubscription } from 'aws-cdk-lib/aws-sns-subscriptions';
|
|
8
|
+
import { Rule, Schedule } from 'aws-cdk-lib/aws-events';
|
|
9
|
+
import { LambdaFunction } from 'aws-cdk-lib/aws-events-targets';
|
|
10
|
+
import { DockerImageAsset } from 'aws-cdk-lib/aws-ecr-assets';
|
|
11
|
+
import { StringRatchet } from '@bitblit/ratchet-common';
|
|
12
|
+
import { EpsilonStackUtil } from './epsilon-stack-util';
|
|
13
|
+
import { RatchetEpsilonDeploymentInfo } from '../../build/ratchet-epsilon-deployment-info';
|
|
14
|
+
export class EpsilonApiStack extends Stack {
|
|
15
|
+
constructor(scope, id, props) {
|
|
16
|
+
super(scope, id, props);
|
|
17
|
+
const dockerImageAsset = new DockerImageAsset(this, id + 'DockerImage', {
|
|
18
|
+
directory: props.dockerFileFolder,
|
|
19
|
+
file: props.dockerFileName,
|
|
20
|
+
});
|
|
21
|
+
const dockerImageCode = DockerImageCode.fromImageAsset(props.dockerFileFolder, { file: props.dockerFileName });
|
|
22
|
+
const notificationTopic = new Topic(this, id + 'WorkNotificationTopic');
|
|
23
|
+
const workQueue = new Queue(this, id + 'WorkQueue', {
|
|
24
|
+
fifo: true,
|
|
25
|
+
retentionPeriod: Duration.hours(8),
|
|
26
|
+
visibilityTimeout: Duration.minutes(5),
|
|
27
|
+
contentBasedDeduplication: true,
|
|
28
|
+
...props,
|
|
29
|
+
});
|
|
30
|
+
const interApiGenericEventTopic = new Topic(this, id + 'InterApiTopic');
|
|
31
|
+
const epsilonEnv = {
|
|
32
|
+
EPSILON_AWS_REGION: StringRatchet.safeString(Stack.of(this).region),
|
|
33
|
+
EPSILON_AWS_AVAILABILITY_ZONES: StringRatchet.safeString(JSON.stringify(Stack.of(this).availabilityZones)),
|
|
34
|
+
EPSILON_BACKGROUND_SQS_QUEUE_URL: StringRatchet.safeString(workQueue.queueUrl),
|
|
35
|
+
EPSILON_BACKGROUND_SNS_TOPIC_ARN: StringRatchet.safeString(notificationTopic.topicArn),
|
|
36
|
+
EPSILON_INTER_API_EVENT_TOPIC_ARN: StringRatchet.safeString(interApiGenericEventTopic.topicArn),
|
|
37
|
+
EPSILON_LIB_BUILD_HASH: StringRatchet.safeString(RatchetEpsilonDeploymentInfo.buildInformation().hash),
|
|
38
|
+
EPSILON_LIB_BUILD_TIME: StringRatchet.safeString(RatchetEpsilonDeploymentInfo.buildInformation().timeBuiltISO),
|
|
39
|
+
EPSILON_LIB_BUILD_BRANCH_OR_TAG: StringRatchet.safeString(RatchetEpsilonDeploymentInfo.buildInformation().branch || RatchetEpsilonDeploymentInfo.buildInformation().tag),
|
|
40
|
+
EPSILON_LIB_BUILD_VERSION: StringRatchet.safeString(RatchetEpsilonDeploymentInfo.buildInformation().version),
|
|
41
|
+
};
|
|
42
|
+
const env = Object.assign({}, props.extraEnvironmentalVars || {}, epsilonEnv);
|
|
43
|
+
const ecsRole = new Role(this, id + 'AwsEcsRole', {
|
|
44
|
+
assumedBy: new ServicePrincipal('ec2.amazonaws.com'),
|
|
45
|
+
inlinePolicies: {
|
|
46
|
+
root: new PolicyDocument({
|
|
47
|
+
statements: EpsilonStackUtil.ECS_POLICY_STATEMENTS,
|
|
48
|
+
}),
|
|
49
|
+
},
|
|
50
|
+
});
|
|
51
|
+
const ecsInstanceProfile = new CfnInstanceProfile(this, id + 'EcsInstanceProfile', {
|
|
52
|
+
path: '/',
|
|
53
|
+
roles: [ecsRole.roleName],
|
|
54
|
+
});
|
|
55
|
+
const jobRole = new Role(this, id + 'AwsBatchRole', {
|
|
56
|
+
assumedBy: new ServicePrincipal('ecs-tasks.amazonaws.com'),
|
|
57
|
+
managedPolicies: [ManagedPolicy.fromAwsManagedPolicyName('service-role/AWSLambdaVPCAccessExecutionRole')],
|
|
58
|
+
inlinePolicies: {
|
|
59
|
+
root: new PolicyDocument({
|
|
60
|
+
statements: EpsilonStackUtil.createDefaultPolicyStatementList(props, workQueue, notificationTopic, interApiGenericEventTopic),
|
|
61
|
+
}),
|
|
62
|
+
},
|
|
63
|
+
});
|
|
64
|
+
const compEnvProps = {
|
|
65
|
+
replaceComputeEnvironment: false,
|
|
66
|
+
computeResources: {
|
|
67
|
+
minvCpus: 0,
|
|
68
|
+
maxvCpus: 16,
|
|
69
|
+
instanceTypes: ['optimal'],
|
|
70
|
+
instanceRole: ecsInstanceProfile.attrArn,
|
|
71
|
+
ec2KeyPair: props.batchInstancesEc2KeyPairName,
|
|
72
|
+
type: 'EC2',
|
|
73
|
+
subnets: props.vpcSubnetIds.map((s) => 'subnet-' + s),
|
|
74
|
+
securityGroupIds: props.lambdaSecurityGroupIds.map((s) => 'sg-' + s),
|
|
75
|
+
allocationStrategy: 'BEST_FIT',
|
|
76
|
+
},
|
|
77
|
+
serviceRole: 'arn:aws:iam::' + props.env.account + ':role/AWSBatchServiceRole',
|
|
78
|
+
type: 'MANAGED',
|
|
79
|
+
state: 'ENABLED',
|
|
80
|
+
};
|
|
81
|
+
const compEnv = new CfnComputeEnvironment(this, id + 'ComputeEnv', compEnvProps);
|
|
82
|
+
const batchJobQueueProps = {
|
|
83
|
+
state: 'ENABLED',
|
|
84
|
+
priority: 1,
|
|
85
|
+
computeEnvironmentOrder: [
|
|
86
|
+
{
|
|
87
|
+
computeEnvironment: compEnv.attrComputeEnvironmentArn,
|
|
88
|
+
order: 1,
|
|
89
|
+
},
|
|
90
|
+
],
|
|
91
|
+
tags: {
|
|
92
|
+
tagsKey: id,
|
|
93
|
+
},
|
|
94
|
+
};
|
|
95
|
+
const batchJobQueue = new CfnJobQueue(this, id + 'BatchJobQueue', batchJobQueueProps);
|
|
96
|
+
const batchEnvVars = EpsilonStackUtil.toEnvironmentVariables([
|
|
97
|
+
env,
|
|
98
|
+
props.extraEnvironmentalVars || {},
|
|
99
|
+
{
|
|
100
|
+
EPSILON_RUNNING_IN_AWS_BATCH: true,
|
|
101
|
+
},
|
|
102
|
+
]);
|
|
103
|
+
const jobProps = {
|
|
104
|
+
type: 'container',
|
|
105
|
+
platformCapabilities: ['EC2'],
|
|
106
|
+
containerProperties: {
|
|
107
|
+
mountPoints: [],
|
|
108
|
+
volumes: [],
|
|
109
|
+
memory: 4294,
|
|
110
|
+
privileged: false,
|
|
111
|
+
jobRoleArn: jobRole.roleArn,
|
|
112
|
+
readonlyRootFilesystem: false,
|
|
113
|
+
vcpus: 1,
|
|
114
|
+
image: dockerImageAsset.imageUri,
|
|
115
|
+
command: ['Ref::taskName', 'Ref::taskData', 'Ref::traceId', 'Ref::traceDepth'],
|
|
116
|
+
environment: batchEnvVars,
|
|
117
|
+
},
|
|
118
|
+
};
|
|
119
|
+
const jobDef = new CfnJobDefinition(this, id + 'JobDefinition', jobProps);
|
|
120
|
+
const lambdaRole = new Role(this, 'customRole', {
|
|
121
|
+
roleName: id + 'LambdaCustomRole',
|
|
122
|
+
assumedBy: new ServicePrincipal('lambda.amazonaws.com'),
|
|
123
|
+
managedPolicies: [ManagedPolicy.fromAwsManagedPolicyName('service-role/AWSLambdaVPCAccessExecutionRole')],
|
|
124
|
+
inlinePolicies: {
|
|
125
|
+
root: new PolicyDocument({
|
|
126
|
+
statements: EpsilonStackUtil.createDefaultPolicyStatementList(props, workQueue, notificationTopic, interApiGenericEventTopic),
|
|
127
|
+
}),
|
|
128
|
+
},
|
|
129
|
+
});
|
|
130
|
+
env['EPSILON_AWS_BATCH_JOB_DEFINITION_ARN'] = jobDef.ref;
|
|
131
|
+
env['EPSILON_AWS_BATCH_JOB_QUEUE_ARN'] = batchJobQueue.ref;
|
|
132
|
+
this.webHandler = new DockerImageFunction(this, id + 'Web', {
|
|
133
|
+
retryAttempts: 2,
|
|
134
|
+
memorySize: props.webMemorySizeMb || 128,
|
|
135
|
+
ephemeralStorageSize: Size.mebibytes(512),
|
|
136
|
+
timeout: Duration.seconds(props.webTimeoutSeconds || 20),
|
|
137
|
+
code: dockerImageCode,
|
|
138
|
+
role: lambdaRole,
|
|
139
|
+
environment: env,
|
|
140
|
+
});
|
|
141
|
+
if (props?.webLambdaPingMinutes && props.webLambdaPingMinutes > 0) {
|
|
142
|
+
const rule = new Rule(this, id + 'WebKeepaliveRule', {
|
|
143
|
+
schedule: Schedule.rate(Duration.minutes(Math.ceil(props.webLambdaPingMinutes))),
|
|
144
|
+
});
|
|
145
|
+
rule.addTarget(new LambdaFunction(this.webHandler));
|
|
146
|
+
}
|
|
147
|
+
const fnUrl = this.webHandler.addFunctionUrl({
|
|
148
|
+
authType: FunctionUrlAuthType.NONE,
|
|
149
|
+
cors: {
|
|
150
|
+
allowedOrigins: ['*'],
|
|
151
|
+
allowedHeaders: ['Content-Type', 'X-Amz-Date', 'Authorization', 'X-Api-Key'],
|
|
152
|
+
allowedMethods: [HttpMethod.ALL],
|
|
153
|
+
allowCredentials: true,
|
|
154
|
+
},
|
|
155
|
+
});
|
|
156
|
+
this.backgroundHandler = new DockerImageFunction(this, id + 'Background', {
|
|
157
|
+
retryAttempts: 2,
|
|
158
|
+
memorySize: props.backgroundMemorySizeMb || 3000,
|
|
159
|
+
ephemeralStorageSize: Size.mebibytes(512),
|
|
160
|
+
timeout: Duration.seconds(props.backgroundTimeoutSeconds || 900),
|
|
161
|
+
code: dockerImageCode,
|
|
162
|
+
role: lambdaRole,
|
|
163
|
+
environment: env,
|
|
164
|
+
});
|
|
165
|
+
notificationTopic.addSubscription(new LambdaSubscription(this.backgroundHandler));
|
|
166
|
+
interApiGenericEventTopic.addSubscription(new LambdaSubscription(this.backgroundHandler));
|
|
167
|
+
const rule = new Rule(this, id + 'CronRule', {
|
|
168
|
+
schedule: Schedule.rate(Duration.minutes(1)),
|
|
169
|
+
});
|
|
170
|
+
rule.addTarget(new LambdaFunction(this.backgroundHandler));
|
|
171
|
+
this.apiDomain = Lazy.uncachedString({
|
|
172
|
+
produce: (context) => {
|
|
173
|
+
const resolved = context.resolve(fnUrl.url);
|
|
174
|
+
return { 'Fn::Select': [2, { 'Fn::Split': ['/', resolved] }] };
|
|
175
|
+
},
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { StringRatchet } from '@bitblit/ratchet-common';
|
|
2
|
+
import { Effect, PolicyStatement } from 'aws-cdk-lib/aws-iam';
|
|
3
|
+
export class EpsilonStackUtil {
|
|
4
|
+
constructor() { }
|
|
5
|
+
static toEnvironmentVariables(input) {
|
|
6
|
+
const rval = [];
|
|
7
|
+
input.forEach((inval) => {
|
|
8
|
+
Object.keys(inval).forEach((k) => {
|
|
9
|
+
rval.push({
|
|
10
|
+
name: k,
|
|
11
|
+
value: StringRatchet.safeString(inval[k]),
|
|
12
|
+
});
|
|
13
|
+
});
|
|
14
|
+
});
|
|
15
|
+
return rval;
|
|
16
|
+
}
|
|
17
|
+
static createDefaultPolicyStatementList(props, backgroundLambdaSqs, backgroundLambdaSns, interApiSns) {
|
|
18
|
+
const rval = (props.additionalPolicyStatements || []).concat([
|
|
19
|
+
new PolicyStatement({
|
|
20
|
+
effect: Effect.ALLOW,
|
|
21
|
+
actions: ['logs:CreateLogGroup', 'logs:CreateLogStream', 'logs:PutLogEvents'],
|
|
22
|
+
resources: ['arn:aws:logs:*:*:*'],
|
|
23
|
+
}),
|
|
24
|
+
new PolicyStatement({
|
|
25
|
+
effect: Effect.ALLOW,
|
|
26
|
+
actions: ['ses:SendEmail', 'ses:SendRawEmail'],
|
|
27
|
+
resources: ['arn:aws:ses:*'],
|
|
28
|
+
}),
|
|
29
|
+
new PolicyStatement({
|
|
30
|
+
effect: Effect.ALLOW,
|
|
31
|
+
actions: ['sqs:*'],
|
|
32
|
+
resources: [backgroundLambdaSqs.queueArn],
|
|
33
|
+
}),
|
|
34
|
+
new PolicyStatement({
|
|
35
|
+
effect: Effect.ALLOW,
|
|
36
|
+
actions: ['sns:*'],
|
|
37
|
+
resources: [backgroundLambdaSns.topicArn, interApiSns.topicArn],
|
|
38
|
+
}),
|
|
39
|
+
new PolicyStatement({
|
|
40
|
+
effect: Effect.ALLOW,
|
|
41
|
+
actions: ['batch:*'],
|
|
42
|
+
resources: ['*'],
|
|
43
|
+
}),
|
|
44
|
+
]);
|
|
45
|
+
return rval;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
EpsilonStackUtil.ALLOW_ECS = new PolicyStatement({
|
|
49
|
+
effect: Effect.ALLOW,
|
|
50
|
+
actions: ['ecs:*'],
|
|
51
|
+
resources: ['*'],
|
|
52
|
+
});
|
|
53
|
+
EpsilonStackUtil.ALLOW_ECR = new PolicyStatement({
|
|
54
|
+
effect: Effect.ALLOW,
|
|
55
|
+
actions: ['ecr:BatchCheckLayerAvailability', 'ecr:BatchGetImage', 'ecr:GetDownloadUrlForLayer', 'ecr:GetAuthorizationToken'],
|
|
56
|
+
resources: ['*'],
|
|
57
|
+
});
|
|
58
|
+
EpsilonStackUtil.ALLOW_RESTRICTED_LOGS = new PolicyStatement({
|
|
59
|
+
effect: Effect.ALLOW,
|
|
60
|
+
actions: ['logs:CreateLogStream', 'logs:PutLogEvents', 'logs:DescribeLogStreams', 'logs:CreateLogGroup'],
|
|
61
|
+
resources: ['*'],
|
|
62
|
+
});
|
|
63
|
+
EpsilonStackUtil.ECS_POLICY_STATEMENTS = [
|
|
64
|
+
EpsilonStackUtil.ALLOW_ECS,
|
|
65
|
+
EpsilonStackUtil.ALLOW_ECR,
|
|
66
|
+
EpsilonStackUtil.ALLOW_RESTRICTED_LOGS,
|
|
67
|
+
];
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export var EpsilonWebsiteStackPropsRoute53Handling;
|
|
2
|
+
(function (EpsilonWebsiteStackPropsRoute53Handling) {
|
|
3
|
+
EpsilonWebsiteStackPropsRoute53Handling["Update"] = "Update";
|
|
4
|
+
EpsilonWebsiteStackPropsRoute53Handling["DoNotUpdate"] = "DoNotUpdate";
|
|
5
|
+
})(EpsilonWebsiteStackPropsRoute53Handling || (EpsilonWebsiteStackPropsRoute53Handling = {}));
|