cloudmason 1.9.32 → 1.9.33
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/commands/await-ami.js +119 -0
- package/commands/publish.js +8 -80
- package/main.js +8 -0
- package/package.json +1 -1
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
const { MarketplaceCatalogClient, DescribeEntityCommand } = require("@aws-sdk/client-marketplace-catalog");
|
|
2
|
+
const Params = require('./helpers/params');
|
|
3
|
+
|
|
4
|
+
exports.main = async function(args){
|
|
5
|
+
// Get app and product ID
|
|
6
|
+
const app = await Params.getApp(args.app);
|
|
7
|
+
const productId = app.pid;
|
|
8
|
+
const version = args.v;
|
|
9
|
+
|
|
10
|
+
if (!productId) {
|
|
11
|
+
console.log('ERR: No marketplace listing found for app:', args.app);
|
|
12
|
+
throw new Error('No marketplace listing found. Use new-listing first.');
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
console.log('Waiting for AMI version to become available:');
|
|
16
|
+
console.log('\tProduct ID:', productId);
|
|
17
|
+
console.log('\tVersion:', version);
|
|
18
|
+
console.log('----------');
|
|
19
|
+
|
|
20
|
+
// Create AWS client
|
|
21
|
+
const client = new MarketplaceCatalogClient({ region: process.env.orgRegion });
|
|
22
|
+
|
|
23
|
+
// Wait for version availability
|
|
24
|
+
await waitForVersionAvailability(client, productId, version);
|
|
25
|
+
|
|
26
|
+
return true;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Wait for Version Availability Function
|
|
30
|
+
const waitForVersionAvailability = async (client, productId, version) => {
|
|
31
|
+
const maxAttempts = 1080; // 90 minutes with 5-second intervals (90 * 60 / 5 = 1080)
|
|
32
|
+
let attempts = 0;
|
|
33
|
+
|
|
34
|
+
console.log(`Polling entity for version ${version} availability...`);
|
|
35
|
+
console.log(`Timeout: 90 minutes (will check every 5 seconds)`);
|
|
36
|
+
|
|
37
|
+
while (attempts < maxAttempts) {
|
|
38
|
+
await new Promise(resolve => setTimeout(resolve, 5000)); // Wait 5 seconds
|
|
39
|
+
|
|
40
|
+
try {
|
|
41
|
+
const describeEntityCommand = new DescribeEntityCommand({
|
|
42
|
+
Catalog: "AWSMarketplace",
|
|
43
|
+
EntityId: productId,
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
const entityResponse = await client.send(describeEntityCommand);
|
|
47
|
+
|
|
48
|
+
// Parse the Details field which contains version information
|
|
49
|
+
let details;
|
|
50
|
+
if (typeof entityResponse.Details === 'string') {
|
|
51
|
+
details = JSON.parse(entityResponse.Details);
|
|
52
|
+
} else {
|
|
53
|
+
details = entityResponse.Details;
|
|
54
|
+
}
|
|
55
|
+
// Check if version exists in Versions array
|
|
56
|
+
const versionInfo = details.Versions?.find(v => v.VersionTitle === version);
|
|
57
|
+
console.log('Version details:', versionInfo);
|
|
58
|
+
|
|
59
|
+
if (versionInfo) {
|
|
60
|
+
// Check 1: Version must have Sources array with AMI
|
|
61
|
+
const hasSources = Array.isArray(versionInfo.Sources) &&
|
|
62
|
+
versionInfo.Sources.length > 0 &&
|
|
63
|
+
versionInfo.Sources.some(source => source.Image && source.Image.startsWith('ami-'));
|
|
64
|
+
|
|
65
|
+
// Check 2: Version must have DeliveryOptions array
|
|
66
|
+
const hasDeliveryOptions = Array.isArray(versionInfo.DeliveryOptions) &&
|
|
67
|
+
versionInfo.DeliveryOptions.length > 0;
|
|
68
|
+
|
|
69
|
+
// Check 3: At least one DeliveryOption must have Visibility set to 'Public'
|
|
70
|
+
const isPubliclyVisible = hasDeliveryOptions &&
|
|
71
|
+
versionInfo.DeliveryOptions.some(option => option.Visibility === 'Public');
|
|
72
|
+
|
|
73
|
+
if (hasSources && hasDeliveryOptions && isPubliclyVisible) {
|
|
74
|
+
console.log(`✓ Version ${version} is now available to consumers`);
|
|
75
|
+
|
|
76
|
+
// Extract and display AMI details
|
|
77
|
+
const amiSources = versionInfo.Sources.filter(s => s.Image);
|
|
78
|
+
amiSources.forEach(source => {
|
|
79
|
+
console.log(` AMI ID: ${source.Image}`);
|
|
80
|
+
console.log(` Architecture: ${source.Architecture}`);
|
|
81
|
+
console.log(` Type: ${source.VirtualizationType}`);
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
// Display public delivery options
|
|
85
|
+
const publicOptions = versionInfo.DeliveryOptions.filter(opt => opt.Visibility === 'Public');
|
|
86
|
+
console.log(` Public Delivery Options: ${publicOptions.length}`);
|
|
87
|
+
publicOptions.forEach(opt => {
|
|
88
|
+
console.log(` - ${opt.Title || opt.Type}`);
|
|
89
|
+
if (opt.AmiAlias) console.log(` SSM Alias: ${opt.AmiAlias}`);
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
return true;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Provide detailed feedback on what's missing
|
|
96
|
+
const elapsedMinutes = Math.floor((attempts * 5) / 60);
|
|
97
|
+
const reasons = [];
|
|
98
|
+
if (!hasSources) reasons.push('no AMI sources');
|
|
99
|
+
if (!hasDeliveryOptions) reasons.push('no delivery options');
|
|
100
|
+
if (hasDeliveryOptions && !isPubliclyVisible) reasons.push('not publicly visible yet');
|
|
101
|
+
|
|
102
|
+
console.log(`Version ${version} found but not yet fully available: ${reasons.join(', ')} (${elapsedMinutes}m ${(attempts * 5) % 60}s elapsed, attempt ${attempts + 1}/${maxAttempts})`);
|
|
103
|
+
} else {
|
|
104
|
+
const elapsedMinutes = Math.floor((attempts * 5) / 60);
|
|
105
|
+
console.log(`Version ${version} not yet visible in entity (${elapsedMinutes}m ${(attempts * 5) % 60}s elapsed, attempt ${attempts + 1}/${maxAttempts})`);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
} catch (error) {
|
|
109
|
+
console.error("Error checking entity status:", error.message);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
attempts++;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
console.warn(`⚠ Warning: Version availability check timed out after 90 minutes`);
|
|
116
|
+
console.warn("Version may still be under AWS Marketplace review");
|
|
117
|
+
console.warn("The changeset succeeded, but the version is not yet publicly available to consumers");
|
|
118
|
+
return false;
|
|
119
|
+
};
|
package/commands/publish.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const { MarketplaceCatalogClient, StartChangeSetCommand,DescribeChangeSetCommand
|
|
1
|
+
const { MarketplaceCatalogClient, StartChangeSetCommand, DescribeChangeSetCommand } = require("@aws-sdk/client-marketplace-catalog");
|
|
2
2
|
const { EC2Client, DescribeImagesCommand } = require("@aws-sdk/client-ec2");
|
|
3
3
|
const fs = require('fs');
|
|
4
4
|
const path = require('path');
|
|
@@ -15,8 +15,7 @@ exports.main = async function(args){
|
|
|
15
15
|
// -- Get Version & Descriptions
|
|
16
16
|
const pubArgs = {
|
|
17
17
|
version: args.v,
|
|
18
|
-
changeDescription: args.desc
|
|
19
|
-
wait: args.wait || false
|
|
18
|
+
changeDescription: args.desc
|
|
20
19
|
};
|
|
21
20
|
|
|
22
21
|
// -- Get Params
|
|
@@ -46,6 +45,11 @@ exports.main = async function(args){
|
|
|
46
45
|
const newFileName = path.resolve(args.out);
|
|
47
46
|
console.log('Updating Template:',newFileName);
|
|
48
47
|
fs.writeFileSync(newFileName,stackTxt);
|
|
48
|
+
|
|
49
|
+
// -- Suggest next step
|
|
50
|
+
console.log('\nTo wait for this version to be publicly available in marketplace, run:');
|
|
51
|
+
console.log(` mason await-ami -app ${args.app} -v ${args.v}`);
|
|
52
|
+
|
|
49
53
|
return true
|
|
50
54
|
}
|
|
51
55
|
|
|
@@ -54,7 +58,7 @@ exports.main = async function(args){
|
|
|
54
58
|
|
|
55
59
|
// Update AMI Function
|
|
56
60
|
|
|
57
|
-
const updateAmiVersion = async ({productId, amiId, version, changeDescription
|
|
61
|
+
const updateAmiVersion = async ({productId, amiId, version, changeDescription}) => {
|
|
58
62
|
const client = new MarketplaceCatalogClient({ region: process.env.orgRegion }); // Update the region if needed
|
|
59
63
|
console.log('Updating AMI version:',productId, amiId, version, changeDescription);
|
|
60
64
|
try {
|
|
@@ -142,12 +146,6 @@ const updateAmiVersion = async ({productId, amiId, version, changeDescription, w
|
|
|
142
146
|
}
|
|
143
147
|
}
|
|
144
148
|
|
|
145
|
-
// If wait flag is set, poll entity until version is publicly available
|
|
146
|
-
if (wait && status === "SUCCEEDED") {
|
|
147
|
-
console.log("Waiting for version to become available to consumers...");
|
|
148
|
-
await waitForVersionAvailability(client, productId, version);
|
|
149
|
-
}
|
|
150
|
-
|
|
151
149
|
} catch (error) {
|
|
152
150
|
console.error("Error updating AMI version:", error);
|
|
153
151
|
throw error;
|
|
@@ -155,76 +153,6 @@ const updateAmiVersion = async ({productId, amiId, version, changeDescription, w
|
|
|
155
153
|
};
|
|
156
154
|
|
|
157
155
|
|
|
158
|
-
// Wait for Version Availability Function
|
|
159
|
-
const waitForVersionAvailability = async (client, productId, version) => {
|
|
160
|
-
const maxAttempts = 1080; // 90 minutes with 5-second intervals (90 * 60 / 5 = 1080)
|
|
161
|
-
let attempts = 0;
|
|
162
|
-
|
|
163
|
-
console.log(`Polling entity for version ${version} availability...`);
|
|
164
|
-
console.log(`Timeout: 90 minutes (will check every 5 seconds)`);
|
|
165
|
-
|
|
166
|
-
while (attempts < maxAttempts) {
|
|
167
|
-
await new Promise(resolve => setTimeout(resolve, 5000)); // Wait 5 seconds
|
|
168
|
-
|
|
169
|
-
try {
|
|
170
|
-
const describeEntityCommand = new DescribeEntityCommand({
|
|
171
|
-
Catalog: "AWSMarketplace",
|
|
172
|
-
EntityId: productId,
|
|
173
|
-
});
|
|
174
|
-
|
|
175
|
-
const entityResponse = await client.send(describeEntityCommand);
|
|
176
|
-
|
|
177
|
-
// Parse the Details field which contains version information
|
|
178
|
-
let details;
|
|
179
|
-
if (typeof entityResponse.Details === 'string') {
|
|
180
|
-
details = JSON.parse(entityResponse.Details);
|
|
181
|
-
} else {
|
|
182
|
-
details = entityResponse.Details;
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
// Check if version exists in Versions array
|
|
186
|
-
const versionInfo = details.Versions?.find(v => v.VersionTitle === version);
|
|
187
|
-
|
|
188
|
-
if (versionInfo) {
|
|
189
|
-
// Check if version has delivery options (indicates it's available)
|
|
190
|
-
const hasDeliveryOptions = versionInfo.DeliveryOptions &&
|
|
191
|
-
versionInfo.DeliveryOptions.length > 0;
|
|
192
|
-
|
|
193
|
-
if (hasDeliveryOptions) {
|
|
194
|
-
// Check if any delivery option has Sources (indicates AMI is accessible)
|
|
195
|
-
const hasActiveSources = versionInfo.DeliveryOptions.some(
|
|
196
|
-
option => option.Details?.AmiDeliveryOptionDetails?.AmiSource ||
|
|
197
|
-
option.Details?.AmiSource
|
|
198
|
-
);
|
|
199
|
-
|
|
200
|
-
if (hasActiveSources) {
|
|
201
|
-
console.log(`✓ Version ${version} is now available to consumers`);
|
|
202
|
-
console.log("Version details:", JSON.stringify(versionInfo, null, 2));
|
|
203
|
-
return true;
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
const elapsedMinutes = Math.floor((attempts * 5) / 60);
|
|
208
|
-
console.log(`Version ${version} found but not yet fully available (${elapsedMinutes}m ${(attempts * 5) % 60}s elapsed, attempt ${attempts + 1}/${maxAttempts})`);
|
|
209
|
-
} else {
|
|
210
|
-
const elapsedMinutes = Math.floor((attempts * 5) / 60);
|
|
211
|
-
console.log(`Version ${version} not yet visible in entity (${elapsedMinutes}m ${(attempts * 5) % 60}s elapsed, attempt ${attempts + 1}/${maxAttempts})`);
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
} catch (error) {
|
|
215
|
-
console.error("Error checking entity status:", error.message);
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
attempts++;
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
console.warn(`⚠ Warning: Version availability check timed out after 90 minutes`);
|
|
222
|
-
console.warn("Version may still be under AWS Marketplace review");
|
|
223
|
-
console.warn("The changeset succeeded, but the version is not yet publicly available to consumers");
|
|
224
|
-
return false;
|
|
225
|
-
};
|
|
226
|
-
|
|
227
|
-
|
|
228
156
|
// Get AMI Ids Function
|
|
229
157
|
const getRegions = async (productId) => {
|
|
230
158
|
const client = new MarketplaceCatalogClient({ region: process.env.orgRegion }); // Update region if needed
|
package/main.js
CHANGED
|
@@ -118,6 +118,14 @@ const Commands = {
|
|
|
118
118
|
{n: 'out', desc: 'Output path of marketplace stack', r: true}
|
|
119
119
|
]
|
|
120
120
|
},
|
|
121
|
+
'await-ami': {
|
|
122
|
+
desc: 'Wait for an AMI version to be publicly available in marketplace',
|
|
123
|
+
exec: require('./commands/await-ami').main,
|
|
124
|
+
args: [
|
|
125
|
+
{n: 'app', desc: 'Name of existing app', pattern: `[A-Za-z]{2,20}`, r: true},
|
|
126
|
+
{n: 'v', desc: 'Version to wait for', pattern: `[0-9]{1,20}`, r: true}
|
|
127
|
+
]
|
|
128
|
+
},
|
|
121
129
|
'inspect': {
|
|
122
130
|
desc: 'Get stack status and Ec2 console logs for an instance',
|
|
123
131
|
exec: require('./commands/inspect').main,
|
package/package.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"name":"cloudmason","version":"1.9.
|
|
1
|
+
{"name":"cloudmason","version":"1.9.33","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"}}
|