@webda/aws 4.0.0-beta.1 → 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 +1 -0
- package/lib/index.d.ts.map +1 -0
- 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} +66 -34
- 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 +12 -31
- package/lib/services/index.d.ts.map +1 -0
- 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 -11
- package/lib/services/route53.service.d.ts.map +1 -0
- package/lib/services/{route53.js → route53.service.js} +35 -38
- 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} +113 -74
- 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} +42 -24
- package/lib/services/secretsmanager.service.js.map +1 -0
- package/lib/services/{sqsqueue.d.ts → sqsqueue.service.d.ts} +60 -34
- package/lib/services/sqsqueue.service.d.ts.map +1 -0
- package/lib/services/{sqsqueue.js → sqsqueue.service.js} +60 -35
- package/lib/services/sqsqueue.service.js.map +1 -0
- package/package.json +59 -65
- package/webda.module.json +738 -377
- package/lib/deployers/cloudformation.d.ts +0 -316
- package/lib/deployers/cloudformation.js +0 -837
- package/lib/deployers/cloudformation.js.map +0 -1
- package/lib/deployers/cloudformation.spec.d.ts +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 -254
- package/lib/deployers/index.js +0 -543
- package/lib/deployers/index.js.map +0 -1
- package/lib/deployers/index.spec.d.ts +0 -8
- 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 -1
- package/lib/deployers/lambda-entrypoint.js +0 -11
- package/lib/deployers/lambda-entrypoint.js.map +0 -1
- package/lib/deployers/lambdapackager.d.ts +0 -27
- package/lib/deployers/lambdapackager.js +0 -26
- package/lib/deployers/lambdapackager.js.map +0 -1
- package/lib/deployers/lambdapackager.spec.d.ts +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 -10
- package/lib/index.spec.js +0 -42
- package/lib/index.spec.js.map +0 -1
- package/lib/services/aws-mixin.d.ts +0 -24
- package/lib/services/aws-mixin.js +0 -18
- package/lib/services/aws-mixin.js.map +0 -1
- package/lib/services/cloudwatchlogger.d.ts +0 -81
- package/lib/services/cloudwatchlogger.js.map +0 -1
- package/lib/services/cloudwatchlogger.spec.d.ts +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 -173
- package/lib/services/dynamodb.js +0 -711
- package/lib/services/dynamodb.js.map +0 -1
- package/lib/services/dynamodb.spec.d.ts +0 -23
- package/lib/services/dynamodb.spec.js +0 -339
- package/lib/services/dynamodb.spec.js.map +0 -1
- package/lib/services/lambdacaller.d.ts +0 -68
- package/lib/services/lambdacaller.js +0 -63
- package/lib/services/lambdacaller.js.map +0 -1
- package/lib/services/lambdacaller.spec.d.ts +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 -86
- package/lib/services/lambdaserver.js +0 -264
- package/lib/services/lambdaserver.js.map +0 -1
- package/lib/services/lambdaserver.spec.d.ts +0 -1
- package/lib/services/lambdaserver.spec.js +0 -417
- package/lib/services/lambdaserver.spec.js.map +0 -1
- package/lib/services/route53.js.map +0 -1
- package/lib/services/route53.spec.d.ts +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 -162
- package/lib/services/s3binary.js.map +0 -1
- package/lib/services/s3binary.spec.d.ts +0 -1
- package/lib/services/s3binary.spec.js +0 -269
- package/lib/services/s3binary.spec.js.map +0 -1
- package/lib/services/secretsmanager.d.ts +0 -80
- package/lib/services/secretsmanager.js.map +0 -1
- package/lib/services/secretsmanager.spec.d.ts +0 -1
- package/lib/services/secretsmanager.spec.js +0 -66
- package/lib/services/secretsmanager.spec.js.map +0 -1
- package/lib/services/sqsqueue.js.map +0 -1
- package/lib/services/sqsqueue.spec.d.ts +0 -1
- package/lib/services/sqsqueue.spec.js +0 -152
- package/lib/services/sqsqueue.spec.js.map +0 -1
- package/lib/shell.d.ts +0 -12
- 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
|
-
let 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
|
-
let 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
|
-
let 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
|
-
let 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 (let 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 (let 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 (let 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
|
-
let 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
|
-
let 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
|
-
let 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
|
-
let 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
|
-
let 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
|
-
let 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
|
-
let 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
|
-
let cloudformation = new CloudFormation({});
|
|
424
|
-
let 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
|
-
let changes = await this.waitFor(async (resolve) => {
|
|
429
|
-
let 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
|
-
let 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
|
-
let 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
|
-
let services = this.manager.getWebda().getServices();
|
|
538
|
-
// Build policy
|
|
539
|
-
for (let i in services) {
|
|
540
|
-
if ("getCloudFormation" in services[i]) {
|
|
541
|
-
// Update to match recuring policy - might need to split if policy too big
|
|
542
|
-
let res = services[i].getCloudFormation(this);
|
|
543
|
-
for (let 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
|
-
let info = await this.getAWSIdentity();
|
|
555
|
-
let arn = `arn:aws:lambda:${this.getRegion()}:${info.Account}:function:${this.resources.Lambda.FunctionName}`;
|
|
556
|
-
for (let 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 (let 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
|
-
let 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
|
-
let 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
|
-
let 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
|
-
let packageDescr = console.app.getAppPath("package.json");
|
|
781
|
-
if (!fs.existsSync(packageDescr)) {
|
|
782
|
-
console.log("ERROR", "package.json not found");
|
|
783
|
-
return -1;
|
|
784
|
-
}
|
|
785
|
-
let pkg = JSONUtils.loadFile(packageDescr);
|
|
786
|
-
pkg.webda = pkg.webda || {};
|
|
787
|
-
let 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
|
-
let pkgName = (pkg.name || "").replace(/@/g, "").replace(/\//g, "-");
|
|
797
|
-
let 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
|
-
let 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
|