@webda/aws 4.0.0-beta.2 → 4.0.0-beta.3
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/README.md +64 -7
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js.map +1 -1
- package/lib/services/aws-parameters.d.ts +33 -0
- package/lib/services/aws-parameters.d.ts.map +1 -0
- package/lib/services/aws-parameters.js +18 -0
- package/lib/services/aws-parameters.js.map +1 -0
- package/lib/services/awscommands.service.d.ts +39 -0
- package/lib/services/awscommands.service.d.ts.map +1 -0
- package/lib/services/awscommands.service.js +127 -0
- package/lib/services/awscommands.service.js.map +1 -0
- package/lib/services/cloudwatchlogger.service.d.ts +131 -0
- package/lib/services/cloudwatchlogger.service.d.ts.map +1 -0
- package/lib/services/{cloudwatchlogger.js → cloudwatchlogger.service.js} +60 -28
- package/lib/services/cloudwatchlogger.service.js.map +1 -0
- package/lib/services/contributors.d.ts +37 -0
- package/lib/services/contributors.d.ts.map +1 -0
- package/lib/services/contributors.js +2 -0
- package/lib/services/contributors.js.map +1 -0
- package/lib/services/dynamodb.service.d.ts +297 -0
- package/lib/services/dynamodb.service.d.ts.map +1 -0
- package/lib/services/dynamodb.service.js +985 -0
- package/lib/services/dynamodb.service.js.map +1 -0
- package/lib/services/index.d.ts +11 -31
- package/lib/services/index.d.ts.map +1 -1
- package/lib/services/index.js +11 -8
- package/lib/services/index.js.map +1 -1
- package/lib/services/lambdacaller.service.d.ts +81 -0
- package/lib/services/lambdacaller.service.d.ts.map +1 -0
- package/lib/services/lambdacaller.service.js +75 -0
- package/lib/services/lambdacaller.service.js.map +1 -0
- package/lib/services/lambdaserver.service.d.ts +127 -0
- package/lib/services/lambdaserver.service.d.ts.map +1 -0
- package/lib/services/lambdaserver.service.js +294 -0
- package/lib/services/lambdaserver.service.js.map +1 -0
- package/lib/services/{route53.d.ts → route53.service.d.ts} +21 -12
- package/lib/services/route53.service.d.ts.map +1 -0
- package/lib/services/{route53.js → route53.service.js} +25 -28
- package/lib/services/route53.service.js.map +1 -0
- package/lib/services/s3binary.service.d.ts +215 -0
- package/lib/services/s3binary.service.d.ts.map +1 -0
- package/lib/services/{s3binary.js → s3binary.service.js} +93 -55
- package/lib/services/s3binary.service.js.map +1 -0
- package/lib/services/secretsmanager.service.d.ts +102 -0
- package/lib/services/secretsmanager.service.d.ts.map +1 -0
- package/lib/services/{secretsmanager.js → secretsmanager.service.js} +40 -22
- package/lib/services/secretsmanager.service.js.map +1 -0
- package/lib/services/{sqsqueue.d.ts → sqsqueue.service.d.ts} +60 -35
- package/lib/services/sqsqueue.service.d.ts.map +1 -0
- package/lib/services/{sqsqueue.js → sqsqueue.service.js} +53 -28
- package/lib/services/sqsqueue.service.js.map +1 -0
- package/package.json +58 -63
- package/webda.module.json +743 -280
- package/lib/deployers/cloudformation.d.ts +0 -317
- package/lib/deployers/cloudformation.d.ts.map +0 -1
- package/lib/deployers/cloudformation.js +0 -837
- package/lib/deployers/cloudformation.js.map +0 -1
- package/lib/deployers/cloudformation.spec.d.ts +0 -2
- package/lib/deployers/cloudformation.spec.d.ts.map +0 -1
- package/lib/deployers/cloudformation.spec.js +0 -530
- package/lib/deployers/cloudformation.spec.js.map +0 -1
- package/lib/deployers/index.d.ts +0 -255
- package/lib/deployers/index.d.ts.map +0 -1
- package/lib/deployers/index.js +0 -543
- package/lib/deployers/index.js.map +0 -1
- package/lib/deployers/index.spec.d.ts +0 -9
- package/lib/deployers/index.spec.d.ts.map +0 -1
- package/lib/deployers/index.spec.js +0 -595
- package/lib/deployers/index.spec.js.map +0 -1
- package/lib/deployers/lambda-entrypoint.d.ts +0 -2
- package/lib/deployers/lambda-entrypoint.d.ts.map +0 -1
- package/lib/deployers/lambda-entrypoint.js +0 -13
- package/lib/deployers/lambda-entrypoint.js.map +0 -1
- package/lib/deployers/lambdapackager.d.ts +0 -28
- package/lib/deployers/lambdapackager.d.ts.map +0 -1
- package/lib/deployers/lambdapackager.js +0 -26
- package/lib/deployers/lambdapackager.js.map +0 -1
- package/lib/deployers/lambdapackager.spec.d.ts +0 -2
- package/lib/deployers/lambdapackager.spec.d.ts.map +0 -1
- package/lib/deployers/lambdapackager.spec.js +0 -44
- package/lib/deployers/lambdapackager.spec.js.map +0 -1
- package/lib/index.spec.d.ts +0 -11
- package/lib/index.spec.d.ts.map +0 -1
- package/lib/index.spec.js +0 -42
- package/lib/index.spec.js.map +0 -1
- package/lib/services/aws-mixin.d.ts +0 -25
- package/lib/services/aws-mixin.d.ts.map +0 -1
- package/lib/services/aws-mixin.js +0 -18
- package/lib/services/aws-mixin.js.map +0 -1
- package/lib/services/cloudwatchlogger.d.ts +0 -82
- package/lib/services/cloudwatchlogger.d.ts.map +0 -1
- package/lib/services/cloudwatchlogger.js.map +0 -1
- package/lib/services/cloudwatchlogger.spec.d.ts +0 -2
- package/lib/services/cloudwatchlogger.spec.d.ts.map +0 -1
- package/lib/services/cloudwatchlogger.spec.js +0 -104
- package/lib/services/cloudwatchlogger.spec.js.map +0 -1
- package/lib/services/dynamodb.d.ts +0 -174
- package/lib/services/dynamodb.d.ts.map +0 -1
- package/lib/services/dynamodb.js +0 -711
- package/lib/services/dynamodb.js.map +0 -1
- package/lib/services/dynamodb.spec.d.ts +0 -24
- package/lib/services/dynamodb.spec.d.ts.map +0 -1
- package/lib/services/dynamodb.spec.js +0 -344
- package/lib/services/dynamodb.spec.js.map +0 -1
- package/lib/services/lambdacaller.d.ts +0 -69
- package/lib/services/lambdacaller.d.ts.map +0 -1
- package/lib/services/lambdacaller.js +0 -63
- package/lib/services/lambdacaller.js.map +0 -1
- package/lib/services/lambdacaller.spec.d.ts +0 -2
- package/lib/services/lambdacaller.spec.d.ts.map +0 -1
- package/lib/services/lambdacaller.spec.js +0 -70
- package/lib/services/lambdacaller.spec.js.map +0 -1
- package/lib/services/lambdaserver.d.ts +0 -87
- package/lib/services/lambdaserver.d.ts.map +0 -1
- package/lib/services/lambdaserver.js +0 -263
- package/lib/services/lambdaserver.js.map +0 -1
- package/lib/services/lambdaserver.spec.d.ts +0 -2
- package/lib/services/lambdaserver.spec.d.ts.map +0 -1
- package/lib/services/lambdaserver.spec.js +0 -417
- package/lib/services/lambdaserver.spec.js.map +0 -1
- package/lib/services/route53.d.ts.map +0 -1
- package/lib/services/route53.js.map +0 -1
- package/lib/services/route53.spec.d.ts +0 -2
- package/lib/services/route53.spec.d.ts.map +0 -1
- package/lib/services/route53.spec.js +0 -102
- package/lib/services/route53.spec.js.map +0 -1
- package/lib/services/s3binary.d.ts +0 -163
- package/lib/services/s3binary.d.ts.map +0 -1
- package/lib/services/s3binary.js.map +0 -1
- package/lib/services/s3binary.spec.d.ts +0 -2
- package/lib/services/s3binary.spec.d.ts.map +0 -1
- package/lib/services/s3binary.spec.js +0 -284
- package/lib/services/s3binary.spec.js.map +0 -1
- package/lib/services/secretsmanager.d.ts +0 -81
- package/lib/services/secretsmanager.d.ts.map +0 -1
- package/lib/services/secretsmanager.js.map +0 -1
- package/lib/services/secretsmanager.spec.d.ts +0 -2
- package/lib/services/secretsmanager.spec.d.ts.map +0 -1
- package/lib/services/secretsmanager.spec.js +0 -66
- package/lib/services/secretsmanager.spec.js.map +0 -1
- package/lib/services/sqsqueue.d.ts.map +0 -1
- package/lib/services/sqsqueue.js.map +0 -1
- package/lib/services/sqsqueue.spec.d.ts +0 -2
- package/lib/services/sqsqueue.spec.d.ts.map +0 -1
- package/lib/services/sqsqueue.spec.js +0 -157
- package/lib/services/sqsqueue.spec.js.map +0 -1
- package/lib/shell.d.ts +0 -13
- package/lib/shell.d.ts.map +0 -1
- package/lib/shell.js +0 -42
- package/lib/shell.js.map +0 -1
- package/webda.shell.json +0 -11
|
@@ -1,837 +0,0 @@
|
|
|
1
|
-
import { APIGateway } from "@aws-sdk/client-api-gateway";
|
|
2
|
-
import { CloudFormation } from "@aws-sdk/client-cloudformation";
|
|
3
|
-
import { STS } from "@aws-sdk/client-sts";
|
|
4
|
-
import { JSONUtils, WebdaError } from "@webda/core";
|
|
5
|
-
import * as fs from "fs";
|
|
6
|
-
import * as path from "path";
|
|
7
|
-
import * as YAML from "yaml";
|
|
8
|
-
import { AWSDeployer } from "./index.js";
|
|
9
|
-
const LAMBDA_LATEST_VERSION = "nodejs14.x";
|
|
10
|
-
/**
|
|
11
|
-
* Deploy the application and its resources using AWS CloudFormation
|
|
12
|
-
*
|
|
13
|
-
* @WebdaDeployer WebdaAWSDeployer/CloudFormation
|
|
14
|
-
*/
|
|
15
|
-
export default class CloudFormationDeployer extends AWSDeployer {
|
|
16
|
-
constructor(manager, resources) {
|
|
17
|
-
super(manager, resources);
|
|
18
|
-
this.template = {};
|
|
19
|
-
this.result = {};
|
|
20
|
-
}
|
|
21
|
-
async defaultResources() {
|
|
22
|
-
var _a, _b, _c;
|
|
23
|
-
await super.defaultResources();
|
|
24
|
-
const packageDesc = this.getApplication().getPackageDescription();
|
|
25
|
-
packageDesc.webda = packageDesc.webda || {};
|
|
26
|
-
packageDesc.webda.aws = packageDesc.webda.aws || {};
|
|
27
|
-
if (this.resources.ResourcesToImport) {
|
|
28
|
-
if (!this.resources.ChangeSetType) {
|
|
29
|
-
this.resources.ChangeSetType = "IMPORT";
|
|
30
|
-
}
|
|
31
|
-
if (this.resources.ChangeSetType !== "IMPORT") {
|
|
32
|
-
throw new Error("ChangeSetType cannot be anything else than IMPORT if you have ResourcesToImport set");
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
this.resources.ChangeSetType = this.resources.ChangeSetType || "CREATE";
|
|
36
|
-
this.resources.AssetsBucket = this.resources.AssetsBucket || packageDesc.webda.aws.AssetsBucket;
|
|
37
|
-
if (!this.resources.AssetsBucket) {
|
|
38
|
-
throw new WebdaError.CodeError("ASSETS_BUCKET_REQUIRED", "AssetsBucket must be defined");
|
|
39
|
-
}
|
|
40
|
-
this.resources.AssetsPrefix = this.resources.AssetsPrefix || "${deployment}/${deployer.name}/";
|
|
41
|
-
this.resources.Description = this.resources.Description || "Deployed by @webda/aws/cloudformation";
|
|
42
|
-
this.resources.FileName = this.resources.FileName || `cloudformation-${this.resources.name}`;
|
|
43
|
-
this.resources.StackName = this.resources.StackName || this.resources.name;
|
|
44
|
-
this.resources.Format = this.resources.Format || "JSON";
|
|
45
|
-
this.resources.OpenAPIFileName = this.resources.OpenAPIFileName || "${resources.name}-openapi-${package.version}";
|
|
46
|
-
this.resources.OpenAPITitle = this.resources.OpenAPITitle || this.resources.name;
|
|
47
|
-
// Default Lambda value
|
|
48
|
-
if (this.resources.Lambda) {
|
|
49
|
-
let zipPath = "lambda-${package.version}.zip";
|
|
50
|
-
if (this.resources.LambdaPackager) {
|
|
51
|
-
zipPath = this.resources.LambdaPackager.zipPath;
|
|
52
|
-
}
|
|
53
|
-
(_a = this.resources).LambdaPackager ?? (_a.LambdaPackager = { zipPath });
|
|
54
|
-
this.resources.LambdaPackager.zipPath = zipPath;
|
|
55
|
-
this.resources.Lambda.Runtime = this.resources.Lambda.Runtime || LAMBDA_LATEST_VERSION;
|
|
56
|
-
this.resources.Lambda.MemorySize = this.resources.Lambda.MemorySize || 2048;
|
|
57
|
-
this.resources.Lambda.Timeout = this.resources.Lambda.Timeout || 30;
|
|
58
|
-
this.resources.Lambda.Handler =
|
|
59
|
-
this.resources.Lambda.Handler || "node_modules/@webda/aws/lib/deployers/lambda-entrypoint.handler";
|
|
60
|
-
this.resources.Lambda.FunctionName = this.resources.Lambda.FunctionName || this.resources.name;
|
|
61
|
-
if (!this.resources.Lambda.Role) {
|
|
62
|
-
this.resources.Lambda.Role = { "Fn::GetAtt": ["Role", "Arn"] };
|
|
63
|
-
// If no role is specified auto enable Role and Policy creation
|
|
64
|
-
this.resources.Role = this.resources.Role || {};
|
|
65
|
-
this.resources.Policy = this.resources.Policy || {};
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
// Default Role
|
|
69
|
-
if (this.resources.Role) {
|
|
70
|
-
this.resources.Role.RoleName = this.resources.Role.RoleName || `${this.resources.name}Role`;
|
|
71
|
-
if (!this.resources.Role.Policies || this.resources.Role.Policies.length === 0) {
|
|
72
|
-
this.resources.Policy = this.resources.Policy || {};
|
|
73
|
-
}
|
|
74
|
-
this.resources.Role.AssumeRolePolicyDocument = this.resources.Role.AssumeRolePolicyDocument || {
|
|
75
|
-
Statement: []
|
|
76
|
-
};
|
|
77
|
-
this.resources.Role.AssumeRolePolicyDocument.Statement =
|
|
78
|
-
this.resources.Role.AssumeRolePolicyDocument.Statement || [];
|
|
79
|
-
this.resources.Role.AssumeRolePolicyDocument.Version =
|
|
80
|
-
this.resources.Role.AssumeRolePolicyDocument.Version || "2012-10-17";
|
|
81
|
-
}
|
|
82
|
-
// Default Policy
|
|
83
|
-
if (this.resources.Policy) {
|
|
84
|
-
this.resources.Policy.PolicyName = this.resources.Policy.PolicyName || `${this.resources.name}Policy`;
|
|
85
|
-
this.resources.Policy.Roles = this.resources.Policy.Roles || [];
|
|
86
|
-
if (this.resources.Role) {
|
|
87
|
-
this.resources.Policy.Roles.push({ Ref: "Role" });
|
|
88
|
-
}
|
|
89
|
-
this.resources.Policy.PolicyDocument = this.resources.Policy.PolicyDocument || { Statement: [] };
|
|
90
|
-
}
|
|
91
|
-
this.resources.Tags = this.transformMapTagsToArray(this.resources.Tags || []);
|
|
92
|
-
if (this.resources.APIGatewayStage) {
|
|
93
|
-
this.resources.APIGatewayStage.StageName =
|
|
94
|
-
this.resources.APIGatewayStage.StageName ||
|
|
95
|
-
// @ts-ignore
|
|
96
|
-
this.getApplication().currentDeployment;
|
|
97
|
-
}
|
|
98
|
-
// Activate Domain
|
|
99
|
-
if (this.resources.APIGatewayDomain) {
|
|
100
|
-
(_b = this.resources.APIGatewayDomain).SecurityPolicy ?? (_b.SecurityPolicy = "TLS_1_2");
|
|
101
|
-
}
|
|
102
|
-
// Default BasePathMapping
|
|
103
|
-
if (this.resources.APIGatewayBasePathMapping) {
|
|
104
|
-
this.resources.APIGatewayBasePathMapping.BasePath = this.resources.APIGatewayBasePathMapping.BasePath || "";
|
|
105
|
-
(_c = this.resources.APIGatewayBasePathMapping).DomainName ?? (_c.DomainName = this.resources.APIGatewayDomain.DomainName);
|
|
106
|
-
if (this.resources.APIGatewayBasePathMapping.DomainName.endsWith(".")) {
|
|
107
|
-
this.resources.APIGatewayBasePathMapping.DomainName =
|
|
108
|
-
this.resources.APIGatewayBasePathMapping.DomainName.substring(0, this.resources.APIGatewayBasePathMapping.DomainName.length - 1);
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
// Activate Domain
|
|
112
|
-
if (this.resources.APIGatewayV2Domain) {
|
|
113
|
-
// Enable BasePathMapping if does not exist
|
|
114
|
-
this.resources.APIGatewayV2ApiMapping = this.resources.APIGatewayV2ApiMapping || {};
|
|
115
|
-
this.resources.APIGatewayV2Domain.DomainNameConfigurations =
|
|
116
|
-
this.resources.APIGatewayV2Domain.DomainNameConfigurations || [];
|
|
117
|
-
}
|
|
118
|
-
// Default BasePathMapping
|
|
119
|
-
if (this.resources.APIGatewayV2ApiMapping) {
|
|
120
|
-
this.resources.APIGatewayV2ApiMapping.BasePath = this.resources.APIGatewayV2ApiMapping.BasePath || "";
|
|
121
|
-
this.resources.APIGatewayV2ApiMapping.DomainName =
|
|
122
|
-
this.resources.APIGatewayV2ApiMapping.DomainName || this.resources.APIGatewayDomain.DomainName;
|
|
123
|
-
if (this.resources.APIGatewayV2ApiMapping.DomainName.endsWith(".")) {
|
|
124
|
-
this.resources.APIGatewayV2ApiMapping.DomainName = this.resources.APIGatewayV2ApiMapping.DomainName.substring(0, this.resources.APIGatewayV2ApiMapping.DomainName.length - 1);
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
this.resources.Statics = (this.resources.Statics || []);
|
|
128
|
-
this.resources.Statics.forEach(conf => {
|
|
129
|
-
// Should move to init
|
|
130
|
-
if (!conf.AssetsPath) {
|
|
131
|
-
conf.AssetsPath = conf.Source;
|
|
132
|
-
if (conf.AssetsPath.startsWith("/")) {
|
|
133
|
-
conf.AssetsPath = conf.AssetsPath.substring(1);
|
|
134
|
-
}
|
|
135
|
-
if (!conf.AssetsPath.endsWith("/")) {
|
|
136
|
-
conf.AssetsPath += "/";
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
if (conf.CloudFront) {
|
|
140
|
-
const DistributionConfig = conf.CloudFront.DistributionConfig || {};
|
|
141
|
-
DistributionConfig.Aliases = DistributionConfig.Aliases || [];
|
|
142
|
-
if (DistributionConfig.Aliases.indexOf(conf.DomainName) < 0) {
|
|
143
|
-
DistributionConfig.Aliases.push(conf.DomainName);
|
|
144
|
-
}
|
|
145
|
-
DistributionConfig.PriceClass = DistributionConfig.PriceClass || "PriceClass_100";
|
|
146
|
-
DistributionConfig.Comment = DistributionConfig.Comment || "Deployed with @webda/aws/cloudformation";
|
|
147
|
-
if (DistributionConfig.Enabled === undefined) {
|
|
148
|
-
DistributionConfig.Enabled = true;
|
|
149
|
-
}
|
|
150
|
-
if (!DistributionConfig.DefaultCacheBehavior) {
|
|
151
|
-
DistributionConfig.DefaultCacheBehavior = {
|
|
152
|
-
AllowedMethods: ["GET", "HEAD"],
|
|
153
|
-
ViewerProtocolPolicy: "redirect-to-https",
|
|
154
|
-
TargetOriginId: conf.DomainName,
|
|
155
|
-
ForwardedValues: {
|
|
156
|
-
QueryString: false
|
|
157
|
-
}
|
|
158
|
-
};
|
|
159
|
-
}
|
|
160
|
-
if (!DistributionConfig.Origins) {
|
|
161
|
-
DistributionConfig.Origins = [
|
|
162
|
-
{
|
|
163
|
-
DomainName: `${this.resources.AssetsBucket}.s3.amazonaws.com`,
|
|
164
|
-
Id: conf.DomainName,
|
|
165
|
-
OriginPath: `/${conf.AssetsPath.substring(0, conf.AssetsPath.length - 1)}`,
|
|
166
|
-
S3OriginConfig: {}
|
|
167
|
-
}
|
|
168
|
-
];
|
|
169
|
-
}
|
|
170
|
-
conf.CloudFront.DistributionConfig = DistributionConfig;
|
|
171
|
-
}
|
|
172
|
-
});
|
|
173
|
-
// Manage Docker build
|
|
174
|
-
if (this.resources.Docker) {
|
|
175
|
-
this.resources.Docker.push = this.resources.Docker.push === undefined ? true : this.resources.Docker.push;
|
|
176
|
-
this.resources.Docker.includeRepository =
|
|
177
|
-
this.resources.Docker.includeRepository === undefined ? true : this.resources.Docker.includeRepository;
|
|
178
|
-
if (this.resources.Docker.includeRepository && this.resources.Docker.tag) {
|
|
179
|
-
const accountId = (await this.getAWSIdentity()).Account;
|
|
180
|
-
this.resources.Docker.tag =
|
|
181
|
-
accountId + ".dkr.ecr.eu-west-1.amazonaws.com/${package.webda.aws.Repository}:" + this.resources.Docker.tag;
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
async deploy() {
|
|
186
|
-
const { Description } = this.resources;
|
|
187
|
-
this.template = {
|
|
188
|
-
Description,
|
|
189
|
-
Resources: { ...this.resources.CustomResources }
|
|
190
|
-
};
|
|
191
|
-
this.result = {};
|
|
192
|
-
// Ensure S3 bucket exist
|
|
193
|
-
this.logger.log("DEBUG", "Check assets bucket", this.resources.AssetsBucket);
|
|
194
|
-
await this.createBucket(this.resources.AssetsBucket);
|
|
195
|
-
// Check if we need OpenAPI export
|
|
196
|
-
const openapi = await this.completeOpenAPI(this.manager.getWebda().exportOpenAPI(false));
|
|
197
|
-
this.openapiS3Object = this.getStringified(openapi, this.resources.OpenAPIFileName);
|
|
198
|
-
await this.putFilesOnBucket(this.resources.AssetsBucket, [this.openapiS3Object]);
|
|
199
|
-
// If APIGatewayImportOpenApi update REST API
|
|
200
|
-
if (this.resources.APIGatewayImportOpenApi) {
|
|
201
|
-
this.logger.log("INFO", "Importing open api");
|
|
202
|
-
await this.importOpenApi(openapi);
|
|
203
|
-
}
|
|
204
|
-
// Build Docker if needed
|
|
205
|
-
if (this.resources.Docker && this.resources.Docker.tag) {
|
|
206
|
-
this.logger.log("INFO", "Building Docker image", this.resources.Docker.tag);
|
|
207
|
-
await this.buildDocker();
|
|
208
|
-
}
|
|
209
|
-
this.logger.log("INFO", "Uploading statics");
|
|
210
|
-
// Upload any Statics
|
|
211
|
-
await this.uploadStatics();
|
|
212
|
-
// Dynamicly call each methods
|
|
213
|
-
for (const i in this.resources) {
|
|
214
|
-
if (this[i] && typeof this[i] === "function") {
|
|
215
|
-
this.logger.log("TRACE", "Add CloudFormation Resource", i);
|
|
216
|
-
await this[i]();
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
// Add any static
|
|
220
|
-
for (const i in this.resources.Statics) {
|
|
221
|
-
this.logger.log("TRACE", "Add Static Resource", i);
|
|
222
|
-
await this.createStatic(this.resources.Statics[i]);
|
|
223
|
-
}
|
|
224
|
-
this.logger.log("INFO", "Deploy with CloudFormation");
|
|
225
|
-
// Upload new version it
|
|
226
|
-
await this.sendCloudFormationTemplate();
|
|
227
|
-
// Load the stack
|
|
228
|
-
await this.createCloudFormation();
|
|
229
|
-
return this.result;
|
|
230
|
-
}
|
|
231
|
-
/**
|
|
232
|
-
* Upload any asset to bucket
|
|
233
|
-
*/
|
|
234
|
-
async uploadStatics() {
|
|
235
|
-
for (const i in this.resources.Statics) {
|
|
236
|
-
const { Source, AssetsPath } = this.resources.Statics[i];
|
|
237
|
-
await this.putFolderOnBucket(this.resources.AssetsBucket, this.manager.getApplication().getAppPath(Source), AssetsPath);
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
async createStatic(info) {
|
|
241
|
-
const { DomainName, CloudFront, Bucket } = info;
|
|
242
|
-
info.Bucket = info.Bucket || {};
|
|
243
|
-
// Create bucket
|
|
244
|
-
const resPrefix = `Static${DomainName.replace(/\./g, "")}`;
|
|
245
|
-
if (Bucket) {
|
|
246
|
-
this.template.Resources[`${resPrefix}Bucket`] = {
|
|
247
|
-
Type: "AWS::S3::Bucket",
|
|
248
|
-
Properties: {
|
|
249
|
-
...info.Bucket,
|
|
250
|
-
BucketName: DomainName,
|
|
251
|
-
Tags: this.getDefaultTags(info.Bucket.Tags)
|
|
252
|
-
}
|
|
253
|
-
};
|
|
254
|
-
}
|
|
255
|
-
if (CloudFront) {
|
|
256
|
-
if (!CloudFront.DistributionConfig.ViewerCertificate) {
|
|
257
|
-
CloudFront.DistributionConfig.ViewerCertificate = {
|
|
258
|
-
AcmCertificateArn: (await this.getCertificate(DomainName)).CertificateArn,
|
|
259
|
-
SslSupportMethod: "sni-only"
|
|
260
|
-
};
|
|
261
|
-
}
|
|
262
|
-
this.template.Resources[`${resPrefix}CloudFront`] = {
|
|
263
|
-
Type: "AWS::CloudFront::Distribution",
|
|
264
|
-
Properties: {
|
|
265
|
-
DistributionConfig: CloudFront.DistributionConfig,
|
|
266
|
-
Tags: this.getDefaultTags(info.CloudFront.Tags)
|
|
267
|
-
}
|
|
268
|
-
};
|
|
269
|
-
await this.createCloudFormationDNSEntry({ "Fn::GetAtt": [`${resPrefix}CloudFront`, "DomainName"] }, DomainName, "Z2FDTNDATAQYW2" // Constant as seen here: https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-route53-aliastarget-1.html#cfn-route53-aliastarget-hostedzoneid
|
|
270
|
-
);
|
|
271
|
-
}
|
|
272
|
-
}
|
|
273
|
-
/**
|
|
274
|
-
* Copy the CloudFormation template to the Assets bucket
|
|
275
|
-
*/
|
|
276
|
-
async sendCloudFormationTemplate() {
|
|
277
|
-
const res = this.getStringified(this.template, this.resources.FileName);
|
|
278
|
-
this.result.CloudFormation = {
|
|
279
|
-
Bucket: this.resources.AssetsBucket,
|
|
280
|
-
Key: res.key
|
|
281
|
-
};
|
|
282
|
-
this.result.CloudFormationContent = res.src.toString();
|
|
283
|
-
await this.putFilesOnBucket(this.resources.AssetsBucket, [res]);
|
|
284
|
-
}
|
|
285
|
-
/**
|
|
286
|
-
* Delete the CloudFormation stack
|
|
287
|
-
* @returns
|
|
288
|
-
*/
|
|
289
|
-
async deleteCloudFormation() {
|
|
290
|
-
const cloudformation = new CloudFormation({});
|
|
291
|
-
await cloudformation.deleteStack({ StackName: this.resources.StackName });
|
|
292
|
-
return this.waitFor(async (resolve) => {
|
|
293
|
-
try {
|
|
294
|
-
await cloudformation.describeStacks({
|
|
295
|
-
StackName: this.resources.StackName
|
|
296
|
-
});
|
|
297
|
-
}
|
|
298
|
-
catch (err) {
|
|
299
|
-
resolve();
|
|
300
|
-
return true;
|
|
301
|
-
}
|
|
302
|
-
}, 50, "Waiting on stack to be deleted", 5000);
|
|
303
|
-
}
|
|
304
|
-
/**
|
|
305
|
-
*
|
|
306
|
-
* @param ref
|
|
307
|
-
* @param domain
|
|
308
|
-
* @param HostedZoneId
|
|
309
|
-
* @returns
|
|
310
|
-
*/
|
|
311
|
-
async createCloudFormationDNSEntry(ref, domain, HostedZoneId) {
|
|
312
|
-
const zone = await this.getZoneForDomainName(domain);
|
|
313
|
-
if (!zone) {
|
|
314
|
-
this.logger.log("WARN", "Cannot find Route53 zone for", domain, ", you will have to create manually the CNAME");
|
|
315
|
-
return;
|
|
316
|
-
}
|
|
317
|
-
// Possible conflict if my.name.domain.io and myn.ame.domain.io exists
|
|
318
|
-
this.template.Resources[`DNSEntry${domain.replace(/\./g, "")}`] = {
|
|
319
|
-
Type: "AWS::Route53::RecordSet",
|
|
320
|
-
Properties: {
|
|
321
|
-
AliasTarget: { DNSName: ref, HostedZoneId },
|
|
322
|
-
Comment: "Created by @webda/aws/cloudformation",
|
|
323
|
-
Type: "A",
|
|
324
|
-
Name: domain,
|
|
325
|
-
HostedZoneId: zone.Id
|
|
326
|
-
}
|
|
327
|
-
};
|
|
328
|
-
}
|
|
329
|
-
async importOpenApi(openapi) {
|
|
330
|
-
const apigateway = new APIGateway({});
|
|
331
|
-
console.log("Creating API Gateway");
|
|
332
|
-
await apigateway.putRestApi({
|
|
333
|
-
body: Buffer.from(JSON.stringify(openapi)),
|
|
334
|
-
failOnWarnings: false,
|
|
335
|
-
restApiId: this.resources.APIGatewayImportOpenApi,
|
|
336
|
-
mode: "overwrite"
|
|
337
|
-
});
|
|
338
|
-
}
|
|
339
|
-
/**
|
|
340
|
-
* Create the stack changeset
|
|
341
|
-
* @param cloudformation
|
|
342
|
-
* @returns
|
|
343
|
-
*/
|
|
344
|
-
async createCloudFormationChangeSet(cloudformation) {
|
|
345
|
-
const changeSetParams = {
|
|
346
|
-
...this.resources.StackOptions,
|
|
347
|
-
StackName: this.resources.StackName,
|
|
348
|
-
ChangeSetName: "WebdaCloudFormationDeployer",
|
|
349
|
-
Capabilities: ["CAPABILITY_IAM", "CAPABILITY_NAMED_IAM"],
|
|
350
|
-
Tags: this.getDefaultTags("StackOptions"),
|
|
351
|
-
TemplateURL: `https://${this.result.CloudFormation.Bucket}.s3.amazonaws.com/${this.result.CloudFormation.Key}`,
|
|
352
|
-
ResourcesToImport: this.resources.ResourcesToImport
|
|
353
|
-
};
|
|
354
|
-
let changeSet;
|
|
355
|
-
try {
|
|
356
|
-
changeSet = await cloudformation.createChangeSet({
|
|
357
|
-
...changeSetParams,
|
|
358
|
-
ChangeSetType: this.resources.ChangeSetType === "IMPORT" ? "IMPORT" : "UPDATE"
|
|
359
|
-
});
|
|
360
|
-
}
|
|
361
|
-
catch (err) {
|
|
362
|
-
if (err.message.endsWith(" is in ROLLBACK_COMPLETE state and can not be updated.")) {
|
|
363
|
-
this.logger.log("WARN", "Deleting buguous stack");
|
|
364
|
-
await this.deleteCloudFormation();
|
|
365
|
-
changeSet = await cloudformation.createChangeSet({
|
|
366
|
-
...changeSetParams,
|
|
367
|
-
ChangeSetType: this.resources.ChangeSetType
|
|
368
|
-
});
|
|
369
|
-
}
|
|
370
|
-
else if (err.message === `Stack [${this.resources.StackName}] does not exist`) {
|
|
371
|
-
changeSet = await cloudformation.createChangeSet({
|
|
372
|
-
...changeSetParams,
|
|
373
|
-
ChangeSetType: this.resources.ChangeSetType
|
|
374
|
-
});
|
|
375
|
-
}
|
|
376
|
-
else if (err.message.endsWith(" state and can not be updated.")) {
|
|
377
|
-
await this.waitFor(async (resolve) => {
|
|
378
|
-
const res = await cloudformation.describeStacks({
|
|
379
|
-
StackName: this.resources.StackName
|
|
380
|
-
});
|
|
381
|
-
if (res.Stacks.length === 0) {
|
|
382
|
-
// If it disapeared, recreate
|
|
383
|
-
changeSet = await cloudformation.createChangeSet({
|
|
384
|
-
...changeSetParams,
|
|
385
|
-
ChangeSetType: this.resources.ChangeSetType
|
|
386
|
-
});
|
|
387
|
-
resolve();
|
|
388
|
-
return true;
|
|
389
|
-
}
|
|
390
|
-
if (res.Stacks[0].StackStatus.endsWith("COMPLETE")) {
|
|
391
|
-
changeSet = await cloudformation.createChangeSet({
|
|
392
|
-
...changeSetParams,
|
|
393
|
-
ChangeSetType: this.resources.ChangeSetType === "IMPORT" ? "IMPORT" : "UPDATE"
|
|
394
|
-
});
|
|
395
|
-
resolve();
|
|
396
|
-
return true;
|
|
397
|
-
}
|
|
398
|
-
return false;
|
|
399
|
-
}, 50, "Waiting for COMPLETE state", 5000);
|
|
400
|
-
}
|
|
401
|
-
else if (err.code === "AlreadyExistsException") {
|
|
402
|
-
this.logger.log("WARN", "ChangeSet exists and need to be clean");
|
|
403
|
-
await cloudformation.deleteChangeSet({
|
|
404
|
-
StackName: this.resources.StackName,
|
|
405
|
-
ChangeSetName: "WebdaCloudFormationDeployer"
|
|
406
|
-
});
|
|
407
|
-
changeSet = await cloudformation.createChangeSet({
|
|
408
|
-
...changeSetParams,
|
|
409
|
-
ChangeSetType: this.resources.ChangeSetType === "IMPORT" ? "IMPORT" : "UPDATE"
|
|
410
|
-
});
|
|
411
|
-
}
|
|
412
|
-
else {
|
|
413
|
-
throw err;
|
|
414
|
-
}
|
|
415
|
-
}
|
|
416
|
-
return changeSet;
|
|
417
|
-
}
|
|
418
|
-
/**
|
|
419
|
-
* Upload and create the cloudformation stack or update it
|
|
420
|
-
* @returns
|
|
421
|
-
*/
|
|
422
|
-
async createCloudFormation() {
|
|
423
|
-
const cloudformation = new CloudFormation({});
|
|
424
|
-
const changeSet = await this.createCloudFormationChangeSet(cloudformation);
|
|
425
|
-
// Wait for change set
|
|
426
|
-
this.logger.log("TRACE", `ChangeSet: ${changeSet}`);
|
|
427
|
-
// It will trigger an exception if timeout
|
|
428
|
-
const changes = await this.waitFor(async (resolve) => {
|
|
429
|
-
const localChanges = await cloudformation.describeChangeSet({
|
|
430
|
-
ChangeSetName: "WebdaCloudFormationDeployer",
|
|
431
|
-
StackName: this.resources.StackName
|
|
432
|
-
});
|
|
433
|
-
if (localChanges.Status === "FAILED" || localChanges.Status === "CREATE_COMPLETE") {
|
|
434
|
-
resolve(localChanges);
|
|
435
|
-
return true;
|
|
436
|
-
}
|
|
437
|
-
}, 50, "Waiting for ChangeSet to be ready...", 5000);
|
|
438
|
-
if (changes.Status === "FAILED") {
|
|
439
|
-
if (changes.StatusReason ===
|
|
440
|
-
"The submitted information didn't contain changes. Submit different information to create a change set.") {
|
|
441
|
-
this.logger.log("INFO", "No changes to be made");
|
|
442
|
-
}
|
|
443
|
-
else {
|
|
444
|
-
this.logger.log("ERROR", "Cannot execute ChangeSet:", changes.StatusReason);
|
|
445
|
-
}
|
|
446
|
-
return;
|
|
447
|
-
}
|
|
448
|
-
changes.Changes.filter(j => j.Type === "Resource").forEach(({ ResourceChange: info }) => this.logger.log("INFO", `${info.Action.toUpperCase().padEnd(38)} ${info.ResourceType.padEnd(30)} ${info.LogicalResourceId}`));
|
|
449
|
-
this.logger.log("INFO", "Executing Change Set");
|
|
450
|
-
let lastEvent = (await cloudformation.describeStackEvents({
|
|
451
|
-
StackName: this.resources.StackName
|
|
452
|
-
})).StackEvents.shift();
|
|
453
|
-
await cloudformation.executeChangeSet({
|
|
454
|
-
ChangeSetName: "WebdaCloudFormationDeployer",
|
|
455
|
-
StackName: this.resources.StackName
|
|
456
|
-
});
|
|
457
|
-
// Wait for the completion of change and display events in the meantime
|
|
458
|
-
this.logger.log("INFO", "Waiting for update completion");
|
|
459
|
-
let i = 0;
|
|
460
|
-
let Timeout = true;
|
|
461
|
-
do {
|
|
462
|
-
const events = (await cloudformation.describeStackEvents({
|
|
463
|
-
StackName: this.resources.StackName
|
|
464
|
-
})).StackEvents;
|
|
465
|
-
let display = lastEvent ? false : true;
|
|
466
|
-
let event;
|
|
467
|
-
while ((event = events.pop())) {
|
|
468
|
-
if (display) {
|
|
469
|
-
this.logger.log("INFO", `${event.ResourceStatus.padEnd(38)} ${event.ResourceType.padEnd(30)} ${event.LogicalResourceId}`);
|
|
470
|
-
lastEvent = event;
|
|
471
|
-
if (lastEvent.LogicalResourceId === this.resources.StackName &&
|
|
472
|
-
(lastEvent.ResourceStatus === "UPDATE_COMPLETE" ||
|
|
473
|
-
lastEvent.ResourceStatus === "CREATE_COMPLETE" ||
|
|
474
|
-
lastEvent.ResourceStatus === "ROLLBACK_COMPLETE" ||
|
|
475
|
-
lastEvent.ResourceStatus === "UPDATE_ROLLBACK_COMPLETE")) {
|
|
476
|
-
Timeout = false;
|
|
477
|
-
break;
|
|
478
|
-
}
|
|
479
|
-
}
|
|
480
|
-
else if (event.EventId === lastEvent.EventId) {
|
|
481
|
-
display = true;
|
|
482
|
-
}
|
|
483
|
-
}
|
|
484
|
-
// If we are done do not pause
|
|
485
|
-
if (Timeout) {
|
|
486
|
-
await this.sleep(10);
|
|
487
|
-
i++;
|
|
488
|
-
}
|
|
489
|
-
} while (i < 60 && Timeout);
|
|
490
|
-
if (Timeout) {
|
|
491
|
-
this.logger.log("WARN", "Timeout while waiting for stack to update");
|
|
492
|
-
return;
|
|
493
|
-
}
|
|
494
|
-
/*
|
|
495
|
-
CloudFormation does not update Stage when resources are modified
|
|
496
|
-
|
|
497
|
-
https://stackoverflow.com/questions/41423439/cloudformation-doesnt-deploy-to-api-gateway-stages-on-update
|
|
498
|
-
*/
|
|
499
|
-
if (this.APIGateway) {
|
|
500
|
-
let NextToken = undefined;
|
|
501
|
-
let stageName;
|
|
502
|
-
let restApiId;
|
|
503
|
-
// Retrieve resources
|
|
504
|
-
do {
|
|
505
|
-
const res = await cloudformation.listStackResources({
|
|
506
|
-
StackName: this.resources.StackName,
|
|
507
|
-
NextToken
|
|
508
|
-
});
|
|
509
|
-
NextToken = res.NextToken;
|
|
510
|
-
res.StackResourceSummaries.forEach(resource => {
|
|
511
|
-
if (resource.ResourceType === "AWS::ApiGateway::Stage") {
|
|
512
|
-
stageName = resource.PhysicalResourceId;
|
|
513
|
-
}
|
|
514
|
-
else if (resource.ResourceType === "AWS::ApiGateway::RestApi") {
|
|
515
|
-
restApiId = resource.PhysicalResourceId;
|
|
516
|
-
}
|
|
517
|
-
});
|
|
518
|
-
} while (NextToken);
|
|
519
|
-
// Get RestAPIId
|
|
520
|
-
if (restApiId && stageName) {
|
|
521
|
-
const gw = new APIGateway({});
|
|
522
|
-
await gw.createDeployment({
|
|
523
|
-
restApiId,
|
|
524
|
-
stageName
|
|
525
|
-
});
|
|
526
|
-
}
|
|
527
|
-
}
|
|
528
|
-
}
|
|
529
|
-
/**
|
|
530
|
-
* Pause for x seconds
|
|
531
|
-
* @param seconds
|
|
532
|
-
*/
|
|
533
|
-
async sleep(sec) {
|
|
534
|
-
await new Promise(resolve => setTimeout(resolve, sec * 1000));
|
|
535
|
-
}
|
|
536
|
-
async Resources() {
|
|
537
|
-
const services = this.manager.getWebda().getServices();
|
|
538
|
-
// Build policy
|
|
539
|
-
for (const i in services) {
|
|
540
|
-
if ("getCloudFormation" in services[i]) {
|
|
541
|
-
// Update to match recuring policy - might need to split if policy too big
|
|
542
|
-
const res = services[i].getCloudFormation(this);
|
|
543
|
-
for (const j in res) {
|
|
544
|
-
this.template.Resources[`Service${i}_${j}`] = res[j];
|
|
545
|
-
}
|
|
546
|
-
}
|
|
547
|
-
}
|
|
548
|
-
}
|
|
549
|
-
getRegion() {
|
|
550
|
-
return "us-east-1";
|
|
551
|
-
}
|
|
552
|
-
async completeOpenAPI(openapi) {
|
|
553
|
-
openapi.info.title = this.resources.OpenAPITitle;
|
|
554
|
-
const info = await this.getAWSIdentity();
|
|
555
|
-
const arn = `arn:aws:lambda:${this.getRegion()}:${info.Account}:function:${this.resources.Lambda.FunctionName}`;
|
|
556
|
-
for (const p in openapi.paths) {
|
|
557
|
-
// Not using mockCors as the {@link RequestFilter.checkRequest} can be dynamic
|
|
558
|
-
// Invalid mapping expression parameter specified: method.response.header.Access-Control-Allow-Credentials
|
|
559
|
-
if (!openapi.paths[p]["options"]) {
|
|
560
|
-
openapi.paths[p]["options"] = {};
|
|
561
|
-
}
|
|
562
|
-
for (const m in openapi.paths[p]) {
|
|
563
|
-
openapi.paths[p][m]["x-amazon-apigateway-integration"] = {
|
|
564
|
-
httpMethod: "POST",
|
|
565
|
-
uri: `arn:aws:apigateway:${this.getRegion()}:lambda:path/2015-03-31/functions/${arn}/invocations`,
|
|
566
|
-
type: "aws_proxy"
|
|
567
|
-
};
|
|
568
|
-
}
|
|
569
|
-
}
|
|
570
|
-
return openapi;
|
|
571
|
-
}
|
|
572
|
-
getStringified(object, filename, addPrefix = true) {
|
|
573
|
-
let key = addPrefix ? `${this.resources.AssetsPrefix}${filename}` : filename;
|
|
574
|
-
if (key.startsWith("/")) {
|
|
575
|
-
key = key.substring(1);
|
|
576
|
-
}
|
|
577
|
-
let src;
|
|
578
|
-
// Default to Format parameter
|
|
579
|
-
let format = this.resources.Format;
|
|
580
|
-
// Auto guess format
|
|
581
|
-
if (filename.endsWith(".yml") || filename.endsWith(".yaml")) {
|
|
582
|
-
format = "YAML";
|
|
583
|
-
}
|
|
584
|
-
else if (filename.endsWith(".json")) {
|
|
585
|
-
format = "JSON";
|
|
586
|
-
}
|
|
587
|
-
if (format === "YAML") {
|
|
588
|
-
src = Buffer.from(YAML.stringify(object));
|
|
589
|
-
if (!key.endsWith(".yml") && !key.endsWith(".yaml")) {
|
|
590
|
-
key += ".yml";
|
|
591
|
-
}
|
|
592
|
-
}
|
|
593
|
-
else {
|
|
594
|
-
src = Buffer.from(JSON.stringify(object, undefined, 2));
|
|
595
|
-
if (!key.endsWith(".json")) {
|
|
596
|
-
key += ".json";
|
|
597
|
-
}
|
|
598
|
-
}
|
|
599
|
-
return {
|
|
600
|
-
key,
|
|
601
|
-
src
|
|
602
|
-
};
|
|
603
|
-
}
|
|
604
|
-
async APIGateway() {
|
|
605
|
-
this.template.Resources.APIGateway = {
|
|
606
|
-
Type: "AWS::ApiGateway::RestApi",
|
|
607
|
-
Properties: {
|
|
608
|
-
...this.resources.APIGateway,
|
|
609
|
-
Tags: this.getDefaultTags("APIGateway"),
|
|
610
|
-
BodyS3Location: {
|
|
611
|
-
Bucket: this.resources.AssetsBucket,
|
|
612
|
-
Key: this.openapiS3Object.key
|
|
613
|
-
}
|
|
614
|
-
},
|
|
615
|
-
DependsOn: "LambdaFunction"
|
|
616
|
-
};
|
|
617
|
-
this.template.Resources.LambdaApiGatewayPermission = {
|
|
618
|
-
Type: "AWS::Lambda::Permission",
|
|
619
|
-
Properties: {
|
|
620
|
-
Action: "lambda:InvokeFunction",
|
|
621
|
-
FunctionName: this.resources.Lambda.FunctionName,
|
|
622
|
-
Principal: "apigateway.amazonaws.com",
|
|
623
|
-
SourceArn: {
|
|
624
|
-
"Fn::Join": [
|
|
625
|
-
":",
|
|
626
|
-
[
|
|
627
|
-
"arn:aws:execute-api",
|
|
628
|
-
{ Ref: "AWS::Region" },
|
|
629
|
-
{ Ref: "AWS::AccountId" },
|
|
630
|
-
{ "Fn::Join": ["", [{ Ref: "APIGateway" }, "/*"]] }
|
|
631
|
-
]
|
|
632
|
-
]
|
|
633
|
-
}
|
|
634
|
-
},
|
|
635
|
-
DependsOn: "LambdaFunction"
|
|
636
|
-
};
|
|
637
|
-
this.template.Resources.APIGatewayDeployment = {
|
|
638
|
-
Type: "AWS::ApiGateway::Deployment",
|
|
639
|
-
Properties: {
|
|
640
|
-
...this.resources.APIGatewayDeployment,
|
|
641
|
-
RestApiId: { Ref: "APIGateway" }
|
|
642
|
-
}
|
|
643
|
-
};
|
|
644
|
-
// SourceArn: "arn:aws:execute-api:" + AWS.config.region + ":" + awsId + ":" + this.restApiId + "/*"
|
|
645
|
-
this.template.Resources.APIGatewayStage = {
|
|
646
|
-
Type: "AWS::ApiGateway::Stage",
|
|
647
|
-
Properties: {
|
|
648
|
-
...this.resources.APIGatewayStage,
|
|
649
|
-
Tags: this.getDefaultTags("APIGatewayStage"),
|
|
650
|
-
DeploymentId: { Ref: "APIGatewayDeployment" },
|
|
651
|
-
RestApiId: { Ref: "APIGateway" }
|
|
652
|
-
}
|
|
653
|
-
};
|
|
654
|
-
}
|
|
655
|
-
async APIGatewayDomain() {
|
|
656
|
-
let region;
|
|
657
|
-
if (this.resources.APIGatewayDomain.EndpointConfiguration) {
|
|
658
|
-
if (this.resources.APIGatewayDomain.EndpointConfiguration.Types.indexOf("EDGE") >= 0) {
|
|
659
|
-
region = "us-east-1";
|
|
660
|
-
}
|
|
661
|
-
}
|
|
662
|
-
else {
|
|
663
|
-
region = "us-east-1";
|
|
664
|
-
}
|
|
665
|
-
if (!this.resources.APIGatewayDomain.CertificateArn) {
|
|
666
|
-
this.resources.APIGatewayDomain.CertificateArn = (await this.getCertificate(this.resources.APIGatewayDomain.DomainName, region)).CertificateArn;
|
|
667
|
-
}
|
|
668
|
-
this.template.Resources.APIGatewayDomain = {
|
|
669
|
-
Type: "AWS::ApiGateway::DomainName",
|
|
670
|
-
Properties: {
|
|
671
|
-
...this.resources.APIGatewayDomain,
|
|
672
|
-
Tags: this.getDefaultTags("APIGatewayDomain")
|
|
673
|
-
}
|
|
674
|
-
};
|
|
675
|
-
this.template.Resources.APIGatewayBasePathMapping = {
|
|
676
|
-
Type: "AWS::ApiGateway::BasePathMapping",
|
|
677
|
-
Properties: {
|
|
678
|
-
...this.resources.APIGatewayBasePathMapping,
|
|
679
|
-
DomainName: { Ref: "APIGatewayDomain" },
|
|
680
|
-
RestApiId: { Ref: "APIGateway" },
|
|
681
|
-
Stage: { Ref: "APIGatewayStage" }
|
|
682
|
-
}
|
|
683
|
-
};
|
|
684
|
-
// Should do conditional with RegionalDomainName/RegionalHostedZoneId
|
|
685
|
-
await this.createCloudFormationDNSEntry({ "Fn::GetAtt": ["APIGatewayDomain", "DistributionDomainName"] }, this.resources.APIGatewayDomain.DomainName, { "Fn::GetAtt": ["APIGatewayDomain", "DistributionHostedZoneId"] });
|
|
686
|
-
}
|
|
687
|
-
async Policy() {
|
|
688
|
-
const PolicyDocument = JSON.stringify(await this.getPolicyDocument(this.resources.Policy.PolicyDocument.Statement), undefined, 2);
|
|
689
|
-
this.template.Resources.Policy = {
|
|
690
|
-
Type: "AWS::IAM::Policy",
|
|
691
|
-
Properties: {
|
|
692
|
-
...this.resources.Policy,
|
|
693
|
-
PolicyDocument
|
|
694
|
-
}
|
|
695
|
-
};
|
|
696
|
-
}
|
|
697
|
-
addAssumeRolePolicyStatement(...statements) {
|
|
698
|
-
// If we have a Role generation only
|
|
699
|
-
if (this.resources.Role) {
|
|
700
|
-
this.resources.Role.AssumeRolePolicyDocument.Statement.push(...statements);
|
|
701
|
-
}
|
|
702
|
-
}
|
|
703
|
-
async Role() {
|
|
704
|
-
// add auto generation
|
|
705
|
-
const AssumeRolePolicyDocument = JSON.stringify(this.resources.Role.AssumeRolePolicyDocument, undefined, 2);
|
|
706
|
-
this.template.Resources.Role = {
|
|
707
|
-
Type: "AWS::IAM::Role",
|
|
708
|
-
Properties: {
|
|
709
|
-
...this.resources.Role,
|
|
710
|
-
AssumeRolePolicyDocument,
|
|
711
|
-
Tags: this.getDefaultTags("Role")
|
|
712
|
-
}
|
|
713
|
-
};
|
|
714
|
-
}
|
|
715
|
-
async Lambda() {
|
|
716
|
-
const Code = await this.generateLambdaPackage();
|
|
717
|
-
this.addAssumeRolePolicyStatement({
|
|
718
|
-
Effect: "Allow",
|
|
719
|
-
Principal: { Service: "lambda.amazonaws.com" },
|
|
720
|
-
Action: "sts:AssumeRole"
|
|
721
|
-
});
|
|
722
|
-
this.template.Resources.LambdaFunction = {
|
|
723
|
-
Type: "AWS::Lambda::Function",
|
|
724
|
-
Properties: {
|
|
725
|
-
...this.resources.Lambda,
|
|
726
|
-
Code,
|
|
727
|
-
Tags: this.getDefaultTags("Lambda")
|
|
728
|
-
}
|
|
729
|
-
};
|
|
730
|
-
}
|
|
731
|
-
async buildDocker() {
|
|
732
|
-
// Launch the Docker deployment
|
|
733
|
-
await this.manager.run("WebdaDeployer/Docker", {
|
|
734
|
-
...this.resources.Docker
|
|
735
|
-
});
|
|
736
|
-
}
|
|
737
|
-
async Fargate() {
|
|
738
|
-
this.addAssumeRolePolicyStatement({
|
|
739
|
-
Effect: "Allow",
|
|
740
|
-
Principal: { Service: "ecs-tasks.amazonaws.com" },
|
|
741
|
-
Action: "sts:AssumeRole"
|
|
742
|
-
});
|
|
743
|
-
}
|
|
744
|
-
async generateLambdaPackage() {
|
|
745
|
-
const { AssetsBucket: S3Bucket, LambdaPackager, AssetsPrefix = "", KeepPackage = false } = this.resources;
|
|
746
|
-
const { zipPath: ZipPath } = LambdaPackager;
|
|
747
|
-
const result = {
|
|
748
|
-
S3Bucket,
|
|
749
|
-
S3Key: AssetsPrefix + path.basename(ZipPath)
|
|
750
|
-
};
|
|
751
|
-
// Create the package
|
|
752
|
-
await this.manager.run("WebdaAWSDeployer/LambdaPackager", LambdaPackager);
|
|
753
|
-
// Copy package to S3
|
|
754
|
-
await this.putFilesOnBucket(result.S3Bucket, [
|
|
755
|
-
{
|
|
756
|
-
key: result.S3Key,
|
|
757
|
-
src: ZipPath
|
|
758
|
-
}
|
|
759
|
-
]);
|
|
760
|
-
/* c8 ignore next 3 */
|
|
761
|
-
if (!KeepPackage) {
|
|
762
|
-
fs.unlinkSync(ZipPath);
|
|
763
|
-
}
|
|
764
|
-
return result;
|
|
765
|
-
}
|
|
766
|
-
/**
|
|
767
|
-
* Init the project on AWS
|
|
768
|
-
*
|
|
769
|
-
* It creates a CloudFormation stack with one ECR and one Bucket
|
|
770
|
-
* and save their name in package.json(webda.aws)
|
|
771
|
-
*
|
|
772
|
-
* If you want to use your own ECR and S3 for assets just add
|
|
773
|
-
* them inside your package.json with `webda.aws.AssetsBucket` and
|
|
774
|
-
* `webda.aws.Repository`
|
|
775
|
-
*
|
|
776
|
-
* @param console
|
|
777
|
-
* @returns
|
|
778
|
-
*/
|
|
779
|
-
static async init(console) {
|
|
780
|
-
const packageDescr = console.app.getAppPath("package.json");
|
|
781
|
-
if (!fs.existsSync(packageDescr)) {
|
|
782
|
-
console.log("ERROR", "package.json not found");
|
|
783
|
-
return -1;
|
|
784
|
-
}
|
|
785
|
-
const pkg = JSONUtils.loadFile(packageDescr);
|
|
786
|
-
pkg.webda = pkg.webda || {};
|
|
787
|
-
const sts = new STS({});
|
|
788
|
-
let identity;
|
|
789
|
-
try {
|
|
790
|
-
identity = await sts.getCallerIdentity({});
|
|
791
|
-
}
|
|
792
|
-
catch (ex) {
|
|
793
|
-
console.log("ERROR", "Cannot retrieve your AWS credentials, make sure to have a correct AWS setup");
|
|
794
|
-
return -1;
|
|
795
|
-
}
|
|
796
|
-
const pkgName = (pkg.name || "").replace(/@/g, "").replace(/\//g, "-");
|
|
797
|
-
const StackName = `webda-${pkgName}-global`;
|
|
798
|
-
if (!pkg.webda.aws) {
|
|
799
|
-
console.log("INFO", `This will create a small CloudFormation on your AWS account (${identity.Account}) and region (${sts.config.region || process.env.AWS_DEFAULT_REGION || "us-east-1"})`);
|
|
800
|
-
console.log("INFO", `The stack will be called ${StackName}`);
|
|
801
|
-
pkg.webda.aws = {
|
|
802
|
-
AssetsBucket: `webda-${pkgName}-assets`,
|
|
803
|
-
Repository: `webda-${pkgName}`
|
|
804
|
-
};
|
|
805
|
-
const cloudformation = new CloudFormation({});
|
|
806
|
-
await cloudformation.createStack({
|
|
807
|
-
StackName,
|
|
808
|
-
TemplateBody: JSON.stringify({
|
|
809
|
-
Resources: {
|
|
810
|
-
WebdaAssetsBucket: {
|
|
811
|
-
Type: "AWS::S3::Bucket",
|
|
812
|
-
Properties: {
|
|
813
|
-
BucketName: pkg.webda.aws.AssetsBucket
|
|
814
|
-
}
|
|
815
|
-
},
|
|
816
|
-
WebdaECR: {
|
|
817
|
-
Type: "AWS::ECR::Repository",
|
|
818
|
-
Properties: {
|
|
819
|
-
RepositoryName: pkg.webda.aws.Repository
|
|
820
|
-
}
|
|
821
|
-
}
|
|
822
|
-
}
|
|
823
|
-
})
|
|
824
|
-
});
|
|
825
|
-
console.log("INFO", "Updating package description with default information");
|
|
826
|
-
JSONUtils.saveFile(pkg, packageDescr);
|
|
827
|
-
return 0;
|
|
828
|
-
}
|
|
829
|
-
else {
|
|
830
|
-
// Check if stack already exists
|
|
831
|
-
console.log("WARN", "Default information are already in your package.json");
|
|
832
|
-
return -1;
|
|
833
|
-
}
|
|
834
|
-
}
|
|
835
|
-
}
|
|
836
|
-
export { CloudFormationDeployer, LAMBDA_LATEST_VERSION };
|
|
837
|
-
//# sourceMappingURL=cloudformation.js.map
|