@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 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.26",
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.72.0",
43
- "@aws-sdk/client-lambda": "3.72.0",
44
- "@aws-sdk/client-s3": "3.72.0",
45
- "@aws-sdk/client-secrets-manager": "3.72.0",
46
- "@aws-sdk/client-ssm": "3.72.0",
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.2.0",
48
+ "@google-cloud/functions": "1.3.0",
49
49
  "@google-cloud/secret-manager": "3.12.0",
50
- "@google-cloud/storage": "5.19.2",
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.0",
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.13.0",
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.3.8",
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",
@@ -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
- this.log.info(chalk`{green ok}: ${aliasArn}`);
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
- // check if route already exists
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
- // create or update alias
724
- await this.createOrUpdateAlias(suffix.replace('.', '_'), functionName, incrementalVersion);
725
- }
766
+ await this.updateAuthorizers(ApiId, functionName, aliasArn);
726
767
 
727
- if (cleanup) {
728
- await this.cleanUpIntegrations(functionName);
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) {