cloudmason 2.7.50 → 2.7.52

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.
@@ -1,9 +1,37 @@
1
1
  const { MarketplaceCatalogClient, StartChangeSetCommand, DescribeChangeSetCommand } = require("@aws-sdk/client-marketplace-catalog");
2
2
  const { EC2Client, DescribeImagesCommand } = require("@aws-sdk/client-ec2");
3
+ const { S3Client, GetObjectCommand } = require("@aws-sdk/client-s3");
3
4
  const fs = require('fs');
4
5
  const path = require('path');
5
6
  const Params = require('./helpers/params');
6
7
 
8
+ // Parse path-style, virtual-hosted, or s3:// URLs into {bucket, key, region}
9
+ function parseS3Url(url){
10
+ if (!url){ throw new Error('Missing S3 URL'); }
11
+ let m;
12
+ if ((m = url.match(/^s3:\/\/([^/]+)\/(.+)$/))) { return { bucket: m[1], key: m[2], region: null }; }
13
+ if ((m = url.match(/^https:\/\/s3\.([^.]+)\.amazonaws\.com\/([^/]+)\/(.+)$/))) { return { bucket: m[2], key: m[3], region: m[1] }; }
14
+ if ((m = url.match(/^https:\/\/s3\.amazonaws\.com\/([^/]+)\/(.+)$/))) { return { bucket: m[1], key: m[2], region: null }; }
15
+ if ((m = url.match(/^https:\/\/([^.]+)\.s3\.([^.]+)\.amazonaws\.com\/(.+)$/))) { return { bucket: m[1], key: m[3], region: m[2] }; }
16
+ if ((m = url.match(/^https:\/\/([^.]+)\.s3\.amazonaws\.com\/(.+)$/))) { return { bucket: m[1], key: m[2], region: null }; }
17
+ throw new Error('Unrecognized S3 URL format: ' + url);
18
+ }
19
+
20
+ // AWS Marketplace requires virtual-hosted-style URLs for Template/ArchitectureDiagram
21
+ function toVirtualHostedUrl(url){
22
+ const { bucket, key, region } = parseS3Url(url);
23
+ return region
24
+ ? `https://${bucket}.s3.${region}.amazonaws.com/${key}`
25
+ : `https://${bucket}.s3.amazonaws.com/${key}`;
26
+ }
27
+
28
+ async function readS3Text(url){
29
+ const { bucket, key, region } = parseS3Url(url);
30
+ const client = new S3Client({ region: region || process.env.orgRegion });
31
+ const resp = await client.send(new GetObjectCommand({ Bucket: bucket, Key: key }));
32
+ return await resp.Body.transformToString();
33
+ }
34
+
7
35
  exports.add_listing = async function(args){
8
36
  console.log('Adding Listing>>', args.app, args.pid);
9
37
  await Params.addPid(args.app,args.pid.trim());
@@ -29,11 +57,14 @@ exports.main = async function(args){
29
57
  pubArgs.amiId = instanceVersion.baseAMI_Id;
30
58
  pubArgs.arch = instanceVersion.arch || 'x86_64';
31
59
  if (args.cft){
32
- pubArgs.cftS3Url = instanceVersion.stackURL;
33
- if (!pubArgs.cftS3Url){
34
- console.log('ERR: No stack URL found for version',pubArgs.version);
35
- throw new Error('No stack URL found for version:' + pubArgs.version);
60
+ pubArgs.cftS3Url = args.cft;
61
+ const missing = ['short','long','diagram'].filter(k => !args[k]);
62
+ if (missing.length){
63
+ throw new Error('Missing required CFT args: ' + missing.map(k=>'-'+k).join(', '));
36
64
  }
65
+ pubArgs.shortDescS3Url = args.short;
66
+ pubArgs.longDescS3Url = args.long;
67
+ pubArgs.archDiagramS3Url = args.diagram;
37
68
  console.log('Publishing AMI + CFT:\n\t',Object.entries(pubArgs).map(([k,v])=>{return `${k}:${v}`}).join('\n\t'));
38
69
  await exports.updateAmiCft(pubArgs);
39
70
  } else {
@@ -170,9 +201,15 @@ const updateAmiVersion = async ({productId, amiId, version, changeDescription, a
170
201
 
171
202
  // Update AMI + CloudFormation Template Function
172
203
 
173
- exports.updateAmiCft = async ({productId, amiId, version, changeDescription, arch, cftS3Url}) => {
204
+ exports.updateAmiCft = async ({productId, amiId, version, changeDescription, arch, cftS3Url, shortDescS3Url, longDescS3Url, archDiagramS3Url}) => {
174
205
  const client = new MarketplaceCatalogClient({ region: process.env.orgRegion });
175
206
  console.log('Updating AMI+CFT version:', productId, amiId, version, changeDescription, cftS3Url);
207
+
208
+ const shortDescription = await readS3Text(shortDescS3Url);
209
+ const longDescription = await readS3Text(longDescS3Url);
210
+ const templateUrl = toVirtualHostedUrl(cftS3Url);
211
+ const diagramUrl = toVirtualHostedUrl(archDiagramS3Url);
212
+
176
213
  try {
177
214
  const changeSet = {
178
215
  Catalog: "AWSMarketplace",
@@ -194,9 +231,12 @@ exports.updateAmiCft = async ({productId, amiId, version, changeDescription, arc
194
231
  DeliveryOptionTitle: "AMI with CloudFormation Template",
195
232
  Details: {
196
233
  "DeploymentTemplateDeliveryOptionDetails": {
234
+ "ShortDescription": shortDescription,
235
+ "LongDescription": longDescription,
197
236
  "UsageInstructions": "Visit Theorim.ai/install for installation instructions",
198
237
  "RecommendedInstanceType": arch === 'arm' ? "r8g.medium" : "m6a.large",
199
- "Template": cftS3Url,
238
+ "ArchitectureDiagram": diagramUrl,
239
+ "Template": templateUrl,
200
240
  "TemplateSources": [
201
241
  {
202
242
  "ParameterName": "AmiId",
package/main.js CHANGED
@@ -115,7 +115,10 @@ const Commands = {
115
115
  {n: 'v', desc: 'Version to launch', pattern: `[0-9]{1,20}`, r: true},
116
116
  {n: 'stack', desc: 'Path of stack.yaml (required for AMI-only publish)', r: false},
117
117
  {n: 'out', desc: 'Output path of marketplace stack (AMI-only publish)', r: false},
118
- {n: 'cft', desc: 'Publish as AMI + CloudFormation Template delivery', r: false}
118
+ {n: 'cft', desc: 'S3 URL of CloudFormation template (presence triggers AMI + CFT delivery)', r: false},
119
+ {n: 'short', desc: 'S3 URL of short-description text file (required when -cft is set)', r: false},
120
+ {n: 'long', desc: 'S3 URL of long-description text file (required when -cft is set)', r: false},
121
+ {n: 'diagram', desc: 'S3 URL of architecture diagram image (required when -cft is set)', r: false}
119
122
  ]
120
123
  },
121
124
  'await-ami': {
package/package.json CHANGED
@@ -1 +1 @@
1
- {"name":"cloudmason","version":"2.7.50","description":"","main":"main.js","scripts":{"build":"node build.js"},"bin":{"mason":"./main.js"},"repository":{"type":"git","url":"https://github.com/kai-harvey/cloudmason.git"},"author":"Kai Harvey","license":"ISC","dependencies":{"@aws-sdk/client-acm":"^3.418.0","@aws-sdk/client-auto-scaling":"^3.470.0","@aws-sdk/client-cloudformation":"^3.418.0","@aws-sdk/client-ec2":"^3.864.0","@aws-sdk/client-iam":"^3.864.0","@aws-sdk/client-marketplace-catalog":"^3.716.0","@aws-sdk/client-route-53":"^3.425.0","@aws-sdk/client-s3":"^3.418.0","@aws-sdk/client-ssm":"^3.421.0","adm-zip":"^0.5.10","ssh2":"^1.16.0","yaml":"^2.6.1"}}
1
+ {"name":"cloudmason","version":"2.7.52","description":"","main":"main.js","scripts":{"build":"node build.js"},"bin":{"mason":"./main.js"},"repository":{"type":"git","url":"https://github.com/kai-harvey/cloudmason.git"},"author":"Kai Harvey","license":"ISC","dependencies":{"@aws-sdk/client-acm":"^3.418.0","@aws-sdk/client-auto-scaling":"^3.470.0","@aws-sdk/client-cloudformation":"^3.418.0","@aws-sdk/client-ec2":"^3.864.0","@aws-sdk/client-iam":"^3.864.0","@aws-sdk/client-marketplace-catalog":"^3.716.0","@aws-sdk/client-route-53":"^3.425.0","@aws-sdk/client-s3":"^3.418.0","@aws-sdk/client-ssm":"^3.421.0","adm-zip":"^0.5.10","ssh2":"^1.16.0","yaml":"^2.6.1"}}