@adobe/helix-deploy 6.2.26 → 6.2.29
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +21 -0
- package/package.json +11 -11
- package/src/deploy/AWSConfig.js +14 -2
- package/src/deploy/AWSDeployer.js +118 -52
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,24 @@
|
|
|
1
|
+
## [6.2.29](https://github.com/adobe/helix-deploy/compare/v6.2.28...v6.2.29) (2022-04-27)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Bug Fixes
|
|
5
|
+
|
|
6
|
+
* add tags to newly created polyfunc bucket ([#405](https://github.com/adobe/helix-deploy/issues/405)) ([db4286e](https://github.com/adobe/helix-deploy/commit/db4286ea579d91ee2ad8963b87f7fa6562f8947c))
|
|
7
|
+
|
|
8
|
+
## [6.2.28](https://github.com/adobe/helix-deploy/compare/v6.2.27...v6.2.28) (2022-04-25)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Bug Fixes
|
|
12
|
+
|
|
13
|
+
* **deps:** update external fixes ([#403](https://github.com/adobe/helix-deploy/issues/403)) ([f628019](https://github.com/adobe/helix-deploy/commit/f6280196f599823b563d9f6f1145806284334bcf))
|
|
14
|
+
|
|
15
|
+
## [6.2.27](https://github.com/adobe/helix-deploy/compare/v6.2.26...v6.2.27) (2022-04-25)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
### Bug Fixes
|
|
19
|
+
|
|
20
|
+
* update links should link route to alias, not specific version ([#402](https://github.com/adobe/helix-deploy/issues/402)) ([45d7ea3](https://github.com/adobe/helix-deploy/commit/45d7ea31350f7027330a97dd3773404963f85149))
|
|
21
|
+
|
|
1
22
|
## [6.2.26](https://github.com/adobe/helix-deploy/compare/v6.2.25...v6.2.26) (2022-04-22)
|
|
2
23
|
|
|
3
24
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adobe/helix-deploy",
|
|
3
|
-
"version": "6.2.
|
|
3
|
+
"version": "6.2.29",
|
|
4
4
|
"description": "Library and Commandline Tools to build and deploy OpenWhisk Actions",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"homepage": "https://github.com/adobe/helix-deploy#readme",
|
|
@@ -39,15 +39,15 @@
|
|
|
39
39
|
"dependencies": {
|
|
40
40
|
"@adobe/fastly-native-promises": "2.0.7",
|
|
41
41
|
"@adobe/helix-fetch": "3.0.9",
|
|
42
|
-
"@aws-sdk/client-apigatewayv2": "3.
|
|
43
|
-
"@aws-sdk/client-lambda": "3.
|
|
44
|
-
"@aws-sdk/client-s3": "3.
|
|
45
|
-
"@aws-sdk/client-secrets-manager": "3.
|
|
46
|
-
"@aws-sdk/client-ssm": "3.
|
|
42
|
+
"@aws-sdk/client-apigatewayv2": "3.76.0",
|
|
43
|
+
"@aws-sdk/client-lambda": "3.76.0",
|
|
44
|
+
"@aws-sdk/client-s3": "3.76.0",
|
|
45
|
+
"@aws-sdk/client-secrets-manager": "3.76.0",
|
|
46
|
+
"@aws-sdk/client-ssm": "3.76.0",
|
|
47
47
|
"@fastly/js-compute": "0.2.4",
|
|
48
|
-
"@google-cloud/functions": "1.
|
|
48
|
+
"@google-cloud/functions": "1.3.0",
|
|
49
49
|
"@google-cloud/secret-manager": "3.12.0",
|
|
50
|
-
"@google-cloud/storage": "5.19.
|
|
50
|
+
"@google-cloud/storage": "5.19.3",
|
|
51
51
|
"@rollup/plugin-alias": "3.1.9",
|
|
52
52
|
"@rollup/plugin-commonjs": "21.1.0",
|
|
53
53
|
"@rollup/plugin-json": "4.1.0",
|
|
@@ -77,15 +77,15 @@
|
|
|
77
77
|
"@adobe/helix-universal-logger": "2.0.7",
|
|
78
78
|
"@semantic-release/changelog": "6.0.1",
|
|
79
79
|
"@semantic-release/git": "10.0.1",
|
|
80
|
-
"c8": "7.11.
|
|
80
|
+
"c8": "7.11.2",
|
|
81
81
|
"chai": "4.3.6",
|
|
82
82
|
"chai-http": "4.3.0",
|
|
83
83
|
"codecov": "3.8.3",
|
|
84
|
-
"eslint": "8.
|
|
84
|
+
"eslint": "8.14.0",
|
|
85
85
|
"eslint-plugin-header": "3.1.1",
|
|
86
86
|
"eslint-plugin-import": "2.26.0",
|
|
87
87
|
"husky": "7.0.4",
|
|
88
|
-
"lint-staged": "12.
|
|
88
|
+
"lint-staged": "12.4.0",
|
|
89
89
|
"mocha": "9.2.2",
|
|
90
90
|
"mocha-junit-reporter": "2.0.2",
|
|
91
91
|
"mocha-multi-reporters": "1.5.1",
|
package/src/deploy/AWSConfig.js
CHANGED
|
@@ -27,6 +27,7 @@ export default class AWSConfig {
|
|
|
27
27
|
createAuthorizer: '',
|
|
28
28
|
attachAuthorizer: '',
|
|
29
29
|
identitySources: ['$request.header.Authorization'],
|
|
30
|
+
deployTemplate: 'helix-deploy-template',
|
|
30
31
|
});
|
|
31
32
|
}
|
|
32
33
|
|
|
@@ -42,7 +43,8 @@ export default class AWSConfig {
|
|
|
42
43
|
.withAWSCleanUpBuckets(argv.awsCleanupBuckets)
|
|
43
44
|
.withAWSCleanUpIntegrations(argv.awsCleanupIntegrations)
|
|
44
45
|
.withAWSCreateRoutes(argv.awsCreateRoutes)
|
|
45
|
-
.withAWSParamsManager(argv.awsParameterManager)
|
|
46
|
+
.withAWSParamsManager(argv.awsParameterManager)
|
|
47
|
+
.withAWSDeployTemplate(argv.awsDeployTemplate);
|
|
46
48
|
}
|
|
47
49
|
|
|
48
50
|
withAWSRegion(value) {
|
|
@@ -100,11 +102,16 @@ export default class AWSConfig {
|
|
|
100
102
|
return this;
|
|
101
103
|
}
|
|
102
104
|
|
|
105
|
+
withAWSDeployTemplate(value) {
|
|
106
|
+
this.deployTemplate = value;
|
|
107
|
+
return this;
|
|
108
|
+
}
|
|
109
|
+
|
|
103
110
|
static yarg(yargs) {
|
|
104
111
|
return yargs
|
|
105
112
|
.group(['aws-region', 'aws-api', 'aws-role', 'aws-cleanup-buckets', 'aws-cleanup-integrations',
|
|
106
113
|
'aws-create-routes', 'aws-create-authorizer', 'aws-attach-authorizer', 'aws-lambda-format',
|
|
107
|
-
'aws-parameter-manager'], 'AWS Deployment Options')
|
|
114
|
+
'aws-parameter-manager', 'aws-deploy-template'], 'AWS Deployment Options')
|
|
108
115
|
.option('aws-region', {
|
|
109
116
|
description: 'the AWS region to deploy lambda functions to',
|
|
110
117
|
type: 'string',
|
|
@@ -162,6 +169,11 @@ export default class AWSConfig {
|
|
|
162
169
|
description: 'Cleans up unused integrations',
|
|
163
170
|
type: 'boolean',
|
|
164
171
|
default: false,
|
|
172
|
+
})
|
|
173
|
+
.option('aws-deploy-template', {
|
|
174
|
+
description: 'Name of the deploy S3 bucket template to use',
|
|
175
|
+
type: 'string',
|
|
176
|
+
default: 'helix-deploy-template',
|
|
165
177
|
});
|
|
166
178
|
}
|
|
167
179
|
}
|
|
@@ -13,8 +13,9 @@
|
|
|
13
13
|
import chalk from 'chalk-template';
|
|
14
14
|
import {
|
|
15
15
|
CreateBucketCommand, DeleteBucketCommand, DeleteObjectCommand, DeleteObjectsCommand,
|
|
16
|
-
ListBucketsCommand,
|
|
16
|
+
GetBucketTaggingCommand, ListBucketsCommand,
|
|
17
17
|
ListObjectsV2Command, PutObjectCommand,
|
|
18
|
+
PutPublicAccessBlockCommand, PutBucketTaggingCommand,
|
|
18
19
|
S3Client,
|
|
19
20
|
} from '@aws-sdk/client-s3';
|
|
20
21
|
|
|
@@ -151,6 +152,44 @@ export default class AWSDeployer extends BaseDeployer {
|
|
|
151
152
|
Bucket: this._bucket,
|
|
152
153
|
}));
|
|
153
154
|
this.log.info(chalk`{green ok:} bucket ${data.Location} created`);
|
|
155
|
+
|
|
156
|
+
const { deployTemplate } = this._cfg;
|
|
157
|
+
if (!deployTemplate) {
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
let tags;
|
|
162
|
+
try {
|
|
163
|
+
// Obtain tags from template bucket
|
|
164
|
+
const result = await this._s3.send(new GetBucketTaggingCommand({
|
|
165
|
+
Bucket: deployTemplate,
|
|
166
|
+
}));
|
|
167
|
+
tags = result.TagSet;
|
|
168
|
+
} catch (e) {
|
|
169
|
+
this.log.warn(`Unable to obtain default tags from template bucket: ${this.bucket}`, e);
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// Block public access
|
|
174
|
+
await this._s3.send(new PutPublicAccessBlockCommand({
|
|
175
|
+
Bucket: this._bucket,
|
|
176
|
+
PublicAccessBlockConfiguration: {
|
|
177
|
+
BlockPublicAcls: true,
|
|
178
|
+
IgnorePublicAcls: true,
|
|
179
|
+
BlockPublicPolicy: true,
|
|
180
|
+
RestrictPublicBuckets: true,
|
|
181
|
+
},
|
|
182
|
+
}));
|
|
183
|
+
this.log.info(chalk`{green ok:} bucket ${data.Location} hidden from public`);
|
|
184
|
+
|
|
185
|
+
// Put required tags
|
|
186
|
+
await this._s3.send(new PutBucketTaggingCommand({
|
|
187
|
+
Bucket: this._bucket,
|
|
188
|
+
Tagging: {
|
|
189
|
+
TagSet: tags,
|
|
190
|
+
},
|
|
191
|
+
}));
|
|
192
|
+
this.log.info(chalk`{green ok:} added tags to bucket ${data.Location}`);
|
|
154
193
|
}
|
|
155
194
|
|
|
156
195
|
async uploadZIP() {
|
|
@@ -228,7 +267,8 @@ export default class AWSDeployer extends BaseDeployer {
|
|
|
228
267
|
if (e.name === 'ResourceNotFoundException') {
|
|
229
268
|
this.log.info(chalk`{green ok}: does not exist yet.`);
|
|
230
269
|
this.log.info(chalk`--: creating new Lambda function {yellow ${functionName}}`);
|
|
231
|
-
await this._lambda.send(new CreateFunctionCommand(functionConfig));
|
|
270
|
+
const res = await this._lambda.send(new CreateFunctionCommand(functionConfig));
|
|
271
|
+
baseARN = res.FunctionArn;
|
|
232
272
|
} else {
|
|
233
273
|
this.log.error(chalk`Unable to verify existence of Lambda function {yellow ${functionName}}`);
|
|
234
274
|
throw e;
|
|
@@ -627,21 +667,23 @@ export default class AWSDeployer extends BaseDeployer {
|
|
|
627
667
|
Name: name,
|
|
628
668
|
}));
|
|
629
669
|
this.log.info(chalk`--: updating alias {blue ${name}}...`);
|
|
630
|
-
await this._lambda.send(new UpdateAliasCommand({
|
|
670
|
+
const res = await this._lambda.send(new UpdateAliasCommand({
|
|
631
671
|
FunctionName: functionName,
|
|
632
672
|
Name: name,
|
|
633
673
|
FunctionVersion: functionVersion,
|
|
634
674
|
}));
|
|
635
675
|
this.log.info(chalk`{green ok:} updated alias {blue ${name}} to version {yellow ${functionVersion}}.`);
|
|
676
|
+
return res.AliasArn;
|
|
636
677
|
} catch (e) {
|
|
637
678
|
if (e.name === 'ResourceNotFoundException') {
|
|
638
679
|
this.log.info(chalk`--: creating alias {blue ${name}}...`);
|
|
639
|
-
await this._lambda.send(new CreateAliasCommand({
|
|
680
|
+
const res = await this._lambda.send(new CreateAliasCommand({
|
|
640
681
|
FunctionName: functionName,
|
|
641
682
|
Name: name,
|
|
642
683
|
FunctionVersion: functionVersion,
|
|
643
684
|
}));
|
|
644
685
|
this.log.info(chalk`{green ok:} created alias {blue ${name}} for version {yellow ${functionVersion}}.`);
|
|
686
|
+
return res.AliasArn;
|
|
645
687
|
} else {
|
|
646
688
|
this.log.error(`Unable to verify existence of Lambda alias ${name}`);
|
|
647
689
|
throw e;
|
|
@@ -654,9 +696,7 @@ export default class AWSDeployer extends BaseDeployer {
|
|
|
654
696
|
const { ApiId } = await this.initApiId();
|
|
655
697
|
const functionVersion = cfg.version.replace(/\./g, '_');
|
|
656
698
|
|
|
657
|
-
// get function alias
|
|
658
699
|
let res;
|
|
659
|
-
let aliasArn;
|
|
660
700
|
let incrementalVersion;
|
|
661
701
|
try {
|
|
662
702
|
this.log.info(chalk`--: fetching alias ...`);
|
|
@@ -664,71 +704,97 @@ export default class AWSDeployer extends BaseDeployer {
|
|
|
664
704
|
FunctionName: functionName,
|
|
665
705
|
Name: functionVersion,
|
|
666
706
|
}));
|
|
667
|
-
aliasArn = res.AliasArn;
|
|
668
707
|
incrementalVersion = res.FunctionVersion;
|
|
669
|
-
|
|
708
|
+
// eslint-disable-next-line prefer-destructuring
|
|
709
|
+
this._accountId = res.AliasArn.split(':')[4];
|
|
710
|
+
this.log.info(chalk`{green ok}: ${functionName}@${functionVersion} => ${incrementalVersion}`);
|
|
670
711
|
} catch (e) {
|
|
671
712
|
this.log.error(chalk`{red error}: Unable to create link to function ${functionName}`);
|
|
672
713
|
throw e;
|
|
673
714
|
}
|
|
674
715
|
|
|
675
|
-
// find integration
|
|
676
|
-
let integration = await this.findIntegration(ApiId, aliasArn);
|
|
677
|
-
let cleanup = false;
|
|
678
|
-
if (integration) {
|
|
679
|
-
this.log.info(`--: using existing integration "${integration.IntegrationId}" for "${aliasArn}"`);
|
|
680
|
-
} else {
|
|
681
|
-
integration = await this._api.send(new CreateIntegrationCommand({
|
|
682
|
-
ApiId,
|
|
683
|
-
IntegrationMethod: 'POST',
|
|
684
|
-
IntegrationType: 'AWS_PROXY',
|
|
685
|
-
IntegrationUri: aliasArn,
|
|
686
|
-
PayloadFormatVersion: '2.0',
|
|
687
|
-
TimeoutInMillis: Math.min(cfg.timeout, 30000),
|
|
688
|
-
}));
|
|
689
|
-
this.log.info(chalk`{green ok:} created new integration "${integration.IntegrationId}" for "${aliasArn}"`);
|
|
690
|
-
cleanup = true;
|
|
691
|
-
}
|
|
692
|
-
const { IntegrationId } = integration;
|
|
693
|
-
|
|
694
716
|
// get all the routes
|
|
695
717
|
this.log.info(chalk`--: fetching routes ...`);
|
|
696
718
|
const routes = await this.fetchRoutes(ApiId);
|
|
697
|
-
const routeParams = {
|
|
698
|
-
ApiId,
|
|
699
|
-
Target: `integrations/${IntegrationId}`,
|
|
700
|
-
AuthorizerId: undefined,
|
|
701
|
-
AuthorizationType: 'NONE',
|
|
702
|
-
};
|
|
703
|
-
if (this._cfg.attachAuthorizer) {
|
|
704
|
-
this.log.info(chalk`--: fetching authorizers...`);
|
|
705
|
-
const authorizers = await this.fetchAuthorizers(ApiId);
|
|
706
|
-
const authorizer = authorizers.find((info) => info.Name === this._cfg.attachAuthorizer);
|
|
707
|
-
if (!authorizer) {
|
|
708
|
-
throw Error(`Specified authorizer ${this._cfg.attachAuthorizer} does not exist in api ${ApiId}.`);
|
|
709
|
-
}
|
|
710
|
-
routeParams.AuthorizerId = authorizer.AuthorizerId;
|
|
711
|
-
routeParams.AuthorizationType = 'CUSTOM';
|
|
712
|
-
this.log.info(chalk`{green ok:} configuring routes with authorizer {blue ${this._cfg.attachAuthorizer}} {yellow ${authorizer.AuthorizerId}}`);
|
|
713
|
-
}
|
|
714
719
|
|
|
715
720
|
// create routes for each symlink
|
|
716
721
|
const sfx = this.getLinkVersions();
|
|
717
722
|
|
|
718
723
|
for (const suffix of sfx) {
|
|
719
|
-
//
|
|
724
|
+
// create or update alias
|
|
725
|
+
const aliasArn = await this.createOrUpdateAlias(suffix.replace('.', '_'), functionName, incrementalVersion);
|
|
726
|
+
|
|
727
|
+
// find or create integration
|
|
728
|
+
let integration = await this.findIntegration(ApiId, aliasArn);
|
|
729
|
+
if (integration) {
|
|
730
|
+
this.log.info(`--: using existing integration "${integration.IntegrationId}" for "${aliasArn}"`);
|
|
731
|
+
} else {
|
|
732
|
+
integration = await this._api.send(new CreateIntegrationCommand({
|
|
733
|
+
ApiId,
|
|
734
|
+
IntegrationMethod: 'POST',
|
|
735
|
+
IntegrationType: 'AWS_PROXY',
|
|
736
|
+
IntegrationUri: aliasArn,
|
|
737
|
+
PayloadFormatVersion: '2.0',
|
|
738
|
+
TimeoutInMillis: Math.min(cfg.timeout, 30000),
|
|
739
|
+
}));
|
|
740
|
+
this.log.info(chalk`{green ok:} created new integration "${integration.IntegrationId}" for "${aliasArn}"`);
|
|
741
|
+
}
|
|
742
|
+
const { IntegrationId } = integration;
|
|
743
|
+
|
|
744
|
+
const routeParams = {
|
|
745
|
+
ApiId,
|
|
746
|
+
Target: `integrations/${IntegrationId}`,
|
|
747
|
+
AuthorizerId: undefined,
|
|
748
|
+
AuthorizationType: 'NONE',
|
|
749
|
+
};
|
|
750
|
+
if (this._cfg.attachAuthorizer) {
|
|
751
|
+
this.log.info(chalk`--: fetching authorizers...`);
|
|
752
|
+
const authorizers = await this.fetchAuthorizers(ApiId);
|
|
753
|
+
const authorizer = authorizers.find((info) => info.Name === this._cfg.attachAuthorizer);
|
|
754
|
+
if (!authorizer) {
|
|
755
|
+
throw Error(`Specified authorizer ${this._cfg.attachAuthorizer} does not exist in api ${ApiId}.`);
|
|
756
|
+
}
|
|
757
|
+
routeParams.AuthorizerId = authorizer.AuthorizerId;
|
|
758
|
+
routeParams.AuthorizationType = 'CUSTOM';
|
|
759
|
+
this.log.info(chalk`{green ok:} configuring routes with authorizer {blue ${this._cfg.attachAuthorizer}} {yellow ${authorizer.AuthorizerId}}`);
|
|
760
|
+
}
|
|
761
|
+
|
|
762
|
+
// create or update routes
|
|
720
763
|
await this.createOrUpdateRoute(routes, routeParams, `ANY /${cfg.packageName}/${cfg.baseName}/${suffix}`);
|
|
721
764
|
await this.createOrUpdateRoute(routes, routeParams, `ANY /${cfg.packageName}/${cfg.baseName}/${suffix}/{path+}`);
|
|
722
765
|
|
|
723
|
-
|
|
724
|
-
await this.createOrUpdateAlias(suffix.replace('.', '_'), functionName, incrementalVersion);
|
|
725
|
-
}
|
|
766
|
+
await this.updateAuthorizers(ApiId, functionName, aliasArn);
|
|
726
767
|
|
|
727
|
-
|
|
728
|
-
|
|
768
|
+
// add permissions to invoke function (with and without path)
|
|
769
|
+
let sourceArn = `arn:aws:execute-api:${this._cfg.region}:${this._accountId}:${ApiId}/*/*/${cfg.packageName}/${cfg.baseName}/${suffix}`;
|
|
770
|
+
try {
|
|
771
|
+
// eslint-disable-next-line no-await-in-loop
|
|
772
|
+
await this._lambda.send(new AddPermissionCommand({
|
|
773
|
+
FunctionName: aliasArn,
|
|
774
|
+
Action: 'lambda:InvokeFunction',
|
|
775
|
+
SourceArn: sourceArn,
|
|
776
|
+
Principal: 'apigateway.amazonaws.com',
|
|
777
|
+
StatementId: crypto.createHash('md5').update(aliasArn + sourceArn).digest('hex'),
|
|
778
|
+
}));
|
|
779
|
+
this.log.info(chalk`{green ok:} added invoke permissions for ${sourceArn}`);
|
|
780
|
+
} catch (e) {
|
|
781
|
+
// ignore, most likely the permission already exists
|
|
782
|
+
}
|
|
783
|
+
sourceArn = `arn:aws:execute-api:${this._cfg.region}:${this._accountId}:${ApiId}/*/*/${cfg.packageName}/${cfg.baseName}/${suffix}/{path+}`;
|
|
784
|
+
try {
|
|
785
|
+
// eslint-disable-next-line no-await-in-loop
|
|
786
|
+
await this._lambda.send(new AddPermissionCommand({
|
|
787
|
+
FunctionName: aliasArn,
|
|
788
|
+
Action: 'lambda:InvokeFunction',
|
|
789
|
+
SourceArn: sourceArn,
|
|
790
|
+
Principal: 'apigateway.amazonaws.com',
|
|
791
|
+
StatementId: crypto.createHash('md5').update(aliasArn + sourceArn).digest('hex'),
|
|
792
|
+
}));
|
|
793
|
+
this.log.info(chalk`{green ok:} added invoke permissions for ${sourceArn}`);
|
|
794
|
+
} catch (e) {
|
|
795
|
+
// ignore, most likely the permission already exists
|
|
796
|
+
}
|
|
729
797
|
}
|
|
730
|
-
|
|
731
|
-
await this.updateAuthorizers(ApiId, functionName, aliasArn);
|
|
732
798
|
}
|
|
733
799
|
|
|
734
800
|
async updateAuthorizers(ApiId, functionName, aliasArn) {
|