@webda/aws 4.0.0-beta.2 → 4.0.0-beta.4

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.
Files changed (169) hide show
  1. package/README.md +64 -7
  2. package/lib/deployers/awsdeployer.d.ts +340 -0
  3. package/lib/deployers/awsdeployer.d.ts.map +1 -0
  4. package/lib/deployers/awsdeployer.js +673 -0
  5. package/lib/deployers/awsdeployer.js.map +1 -0
  6. package/lib/deployers/cloudformation.service.d.ts +438 -0
  7. package/lib/deployers/cloudformation.service.d.ts.map +1 -0
  8. package/lib/deployers/cloudformation.service.js +889 -0
  9. package/lib/deployers/cloudformation.service.js.map +1 -0
  10. package/lib/deployers/index.d.ts +4 -254
  11. package/lib/deployers/index.d.ts.map +1 -1
  12. package/lib/deployers/index.js +4 -542
  13. package/lib/deployers/index.js.map +1 -1
  14. package/lib/deployers/lambda-entrypoint.d.ts +33 -1
  15. package/lib/deployers/lambda-entrypoint.d.ts.map +1 -1
  16. package/lib/deployers/lambda-entrypoint.js +44 -10
  17. package/lib/deployers/lambda-entrypoint.js.map +1 -1
  18. package/lib/deployers/lambdapackager.service.d.ts +123 -0
  19. package/lib/deployers/lambdapackager.service.d.ts.map +1 -0
  20. package/lib/deployers/lambdapackager.service.js +149 -0
  21. package/lib/deployers/lambdapackager.service.js.map +1 -0
  22. package/lib/deployers/zip.d.ts +42 -0
  23. package/lib/deployers/zip.d.ts.map +1 -0
  24. package/lib/deployers/zip.js +129 -0
  25. package/lib/deployers/zip.js.map +1 -0
  26. package/lib/index.d.ts +1 -0
  27. package/lib/index.d.ts.map +1 -1
  28. package/lib/index.js +1 -0
  29. package/lib/index.js.map +1 -1
  30. package/lib/services/aws-parameters.d.ts +33 -0
  31. package/lib/services/aws-parameters.d.ts.map +1 -0
  32. package/lib/services/aws-parameters.js +18 -0
  33. package/lib/services/aws-parameters.js.map +1 -0
  34. package/lib/services/awscommands.service.d.ts +39 -0
  35. package/lib/services/awscommands.service.d.ts.map +1 -0
  36. package/lib/services/awscommands.service.js +127 -0
  37. package/lib/services/awscommands.service.js.map +1 -0
  38. package/lib/services/cloudwatchlogger.service.d.ts +131 -0
  39. package/lib/services/cloudwatchlogger.service.d.ts.map +1 -0
  40. package/lib/services/{cloudwatchlogger.js → cloudwatchlogger.service.js} +60 -28
  41. package/lib/services/cloudwatchlogger.service.js.map +1 -0
  42. package/lib/services/contributors.d.ts +36 -0
  43. package/lib/services/contributors.d.ts.map +1 -0
  44. package/lib/services/contributors.js +2 -0
  45. package/lib/services/contributors.js.map +1 -0
  46. package/lib/services/dynamodb.service.d.ts +298 -0
  47. package/lib/services/dynamodb.service.d.ts.map +1 -0
  48. package/lib/services/dynamodb.service.js +986 -0
  49. package/lib/services/dynamodb.service.js.map +1 -0
  50. package/lib/services/index.d.ts +11 -31
  51. package/lib/services/index.d.ts.map +1 -1
  52. package/lib/services/index.js +11 -8
  53. package/lib/services/index.js.map +1 -1
  54. package/lib/services/lambdacaller.service.d.ts +81 -0
  55. package/lib/services/lambdacaller.service.d.ts.map +1 -0
  56. package/lib/services/lambdacaller.service.js +75 -0
  57. package/lib/services/lambdacaller.service.js.map +1 -0
  58. package/lib/services/lambdaserver.service.d.ts +127 -0
  59. package/lib/services/lambdaserver.service.d.ts.map +1 -0
  60. package/lib/services/lambdaserver.service.js +294 -0
  61. package/lib/services/lambdaserver.service.js.map +1 -0
  62. package/lib/services/{route53.d.ts → route53.service.d.ts} +21 -12
  63. package/lib/services/route53.service.d.ts.map +1 -0
  64. package/lib/services/{route53.js → route53.service.js} +25 -28
  65. package/lib/services/route53.service.js.map +1 -0
  66. package/lib/services/s3binary.service.d.ts +215 -0
  67. package/lib/services/s3binary.service.d.ts.map +1 -0
  68. package/lib/services/{s3binary.js → s3binary.service.js} +93 -55
  69. package/lib/services/s3binary.service.js.map +1 -0
  70. package/lib/services/secretsmanager.service.d.ts +102 -0
  71. package/lib/services/secretsmanager.service.d.ts.map +1 -0
  72. package/lib/services/{secretsmanager.js → secretsmanager.service.js} +40 -22
  73. package/lib/services/secretsmanager.service.js.map +1 -0
  74. package/lib/services/{sqsqueue.d.ts → sqsqueue.service.d.ts} +60 -35
  75. package/lib/services/sqsqueue.service.d.ts.map +1 -0
  76. package/lib/services/{sqsqueue.js → sqsqueue.service.js} +53 -28
  77. package/lib/services/sqsqueue.service.js.map +1 -0
  78. package/package.json +56 -64
  79. package/webda.module.json +1425 -264
  80. package/lib/deployers/cloudformation.d.ts +0 -317
  81. package/lib/deployers/cloudformation.d.ts.map +0 -1
  82. package/lib/deployers/cloudformation.js +0 -837
  83. package/lib/deployers/cloudformation.js.map +0 -1
  84. package/lib/deployers/cloudformation.spec.d.ts +0 -2
  85. package/lib/deployers/cloudformation.spec.d.ts.map +0 -1
  86. package/lib/deployers/cloudformation.spec.js +0 -530
  87. package/lib/deployers/cloudformation.spec.js.map +0 -1
  88. package/lib/deployers/index.spec.d.ts +0 -9
  89. package/lib/deployers/index.spec.d.ts.map +0 -1
  90. package/lib/deployers/index.spec.js +0 -595
  91. package/lib/deployers/index.spec.js.map +0 -1
  92. package/lib/deployers/lambdapackager.d.ts +0 -28
  93. package/lib/deployers/lambdapackager.d.ts.map +0 -1
  94. package/lib/deployers/lambdapackager.js +0 -26
  95. package/lib/deployers/lambdapackager.js.map +0 -1
  96. package/lib/deployers/lambdapackager.spec.d.ts +0 -2
  97. package/lib/deployers/lambdapackager.spec.d.ts.map +0 -1
  98. package/lib/deployers/lambdapackager.spec.js +0 -44
  99. package/lib/deployers/lambdapackager.spec.js.map +0 -1
  100. package/lib/index.spec.d.ts +0 -11
  101. package/lib/index.spec.d.ts.map +0 -1
  102. package/lib/index.spec.js +0 -42
  103. package/lib/index.spec.js.map +0 -1
  104. package/lib/services/aws-mixin.d.ts +0 -25
  105. package/lib/services/aws-mixin.d.ts.map +0 -1
  106. package/lib/services/aws-mixin.js +0 -18
  107. package/lib/services/aws-mixin.js.map +0 -1
  108. package/lib/services/cloudwatchlogger.d.ts +0 -82
  109. package/lib/services/cloudwatchlogger.d.ts.map +0 -1
  110. package/lib/services/cloudwatchlogger.js.map +0 -1
  111. package/lib/services/cloudwatchlogger.spec.d.ts +0 -2
  112. package/lib/services/cloudwatchlogger.spec.d.ts.map +0 -1
  113. package/lib/services/cloudwatchlogger.spec.js +0 -104
  114. package/lib/services/cloudwatchlogger.spec.js.map +0 -1
  115. package/lib/services/dynamodb.d.ts +0 -174
  116. package/lib/services/dynamodb.d.ts.map +0 -1
  117. package/lib/services/dynamodb.js +0 -711
  118. package/lib/services/dynamodb.js.map +0 -1
  119. package/lib/services/dynamodb.spec.d.ts +0 -24
  120. package/lib/services/dynamodb.spec.d.ts.map +0 -1
  121. package/lib/services/dynamodb.spec.js +0 -344
  122. package/lib/services/dynamodb.spec.js.map +0 -1
  123. package/lib/services/lambdacaller.d.ts +0 -69
  124. package/lib/services/lambdacaller.d.ts.map +0 -1
  125. package/lib/services/lambdacaller.js +0 -63
  126. package/lib/services/lambdacaller.js.map +0 -1
  127. package/lib/services/lambdacaller.spec.d.ts +0 -2
  128. package/lib/services/lambdacaller.spec.d.ts.map +0 -1
  129. package/lib/services/lambdacaller.spec.js +0 -70
  130. package/lib/services/lambdacaller.spec.js.map +0 -1
  131. package/lib/services/lambdaserver.d.ts +0 -87
  132. package/lib/services/lambdaserver.d.ts.map +0 -1
  133. package/lib/services/lambdaserver.js +0 -263
  134. package/lib/services/lambdaserver.js.map +0 -1
  135. package/lib/services/lambdaserver.spec.d.ts +0 -2
  136. package/lib/services/lambdaserver.spec.d.ts.map +0 -1
  137. package/lib/services/lambdaserver.spec.js +0 -417
  138. package/lib/services/lambdaserver.spec.js.map +0 -1
  139. package/lib/services/route53.d.ts.map +0 -1
  140. package/lib/services/route53.js.map +0 -1
  141. package/lib/services/route53.spec.d.ts +0 -2
  142. package/lib/services/route53.spec.d.ts.map +0 -1
  143. package/lib/services/route53.spec.js +0 -102
  144. package/lib/services/route53.spec.js.map +0 -1
  145. package/lib/services/s3binary.d.ts +0 -163
  146. package/lib/services/s3binary.d.ts.map +0 -1
  147. package/lib/services/s3binary.js.map +0 -1
  148. package/lib/services/s3binary.spec.d.ts +0 -2
  149. package/lib/services/s3binary.spec.d.ts.map +0 -1
  150. package/lib/services/s3binary.spec.js +0 -284
  151. package/lib/services/s3binary.spec.js.map +0 -1
  152. package/lib/services/secretsmanager.d.ts +0 -81
  153. package/lib/services/secretsmanager.d.ts.map +0 -1
  154. package/lib/services/secretsmanager.js.map +0 -1
  155. package/lib/services/secretsmanager.spec.d.ts +0 -2
  156. package/lib/services/secretsmanager.spec.d.ts.map +0 -1
  157. package/lib/services/secretsmanager.spec.js +0 -66
  158. package/lib/services/secretsmanager.spec.js.map +0 -1
  159. package/lib/services/sqsqueue.d.ts.map +0 -1
  160. package/lib/services/sqsqueue.js.map +0 -1
  161. package/lib/services/sqsqueue.spec.d.ts +0 -2
  162. package/lib/services/sqsqueue.spec.d.ts.map +0 -1
  163. package/lib/services/sqsqueue.spec.js +0 -157
  164. package/lib/services/sqsqueue.spec.js.map +0 -1
  165. package/lib/shell.d.ts +0 -13
  166. package/lib/shell.d.ts.map +0 -1
  167. package/lib/shell.js +0 -42
  168. package/lib/shell.js.map +0 -1
  169. 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