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