@jacebenson/jsn 4.3.7-dev.26 → 4.3.7-dev.28
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/package.json +1 -1
- package/src/commands/midservers.js +73 -7
- package/src/midserver-install.js +11 -6
package/package.json
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
1
3
|
import { input, password as passwordPrompt, select } from '@inquirer/prompts';
|
|
2
4
|
import { confirmDelete, formatRecordForDisplay, getStringField, interactiveList, canPrompt } from '../helpers.js';
|
|
3
5
|
import { FormatAuto } from '../output.js';
|
|
4
6
|
import { declareCapabilities } from '../capabilities.js';
|
|
5
7
|
import { resolveRecord, unwrapSysId } from '../resolve-record.js';
|
|
6
|
-
import { detectDocker, defaultRun, installDockerMidServer, validateMidCredentials } from '../midserver-install.js';
|
|
8
|
+
import { detectDocker, defaultRun, installDockerMidServer, midInstallDirectory, validateMidCredentials } from '../midserver-install.js';
|
|
7
9
|
|
|
8
10
|
const AGENT_TABLE = 'ecc_agent';
|
|
9
11
|
const QUEUE_TABLE = 'ecc_queue';
|
|
@@ -190,6 +192,41 @@ async function waitForMidServerUp(app, name, timeoutMs, intervalMs) {
|
|
|
190
192
|
throw new Error(`MID Server '${fullName}' did not report Up within ${timeoutMs}ms`);
|
|
191
193
|
}
|
|
192
194
|
|
|
195
|
+
function removeLocalMidServer(instanceURL, name) {
|
|
196
|
+
const installDir = midInstallDirectory(instanceURL, name);
|
|
197
|
+
const composePath = path.join(installDir, 'docker-compose.yaml');
|
|
198
|
+
const envPath = path.join(installDir, '.env');
|
|
199
|
+
if (!fs.existsSync(composePath)) return false;
|
|
200
|
+
const composeArgs = ['compose', ...(fs.existsSync(envPath) ? ['--env-file', envPath] : []), '-f', composePath, 'down'];
|
|
201
|
+
try {
|
|
202
|
+
defaultRun('docker', composeArgs, { cwd: installDir });
|
|
203
|
+
} catch {
|
|
204
|
+
defaultRun('sudo', ['systemctl', 'start', 'docker']);
|
|
205
|
+
defaultRun('sudo', ['docker', ...composeArgs], { cwd: installDir });
|
|
206
|
+
}
|
|
207
|
+
try {
|
|
208
|
+
fs.rmSync(installDir, { recursive: true, force: true });
|
|
209
|
+
} catch {
|
|
210
|
+
defaultRun('sudo', ['rm', '-rf', '--', installDir]);
|
|
211
|
+
}
|
|
212
|
+
return true;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
function showMidServerLogs(app, identifier, argv) {
|
|
216
|
+
const installDir = midInstallDirectory(app.getEffectiveInstance(), identifier);
|
|
217
|
+
const composePath = path.join(installDir, 'docker-compose.yaml');
|
|
218
|
+
const envPath = path.join(installDir, '.env');
|
|
219
|
+
if (!fs.existsSync(composePath)) throw new Error(`No local Compose installation found for MID Server '${identifier}'`);
|
|
220
|
+
const args = ['compose', ...(fs.existsSync(envPath) ? ['--env-file', envPath] : []), '-f', composePath, 'logs', '--tail', String(argv.tail)];
|
|
221
|
+
if (argv.follow) args.push('--follow');
|
|
222
|
+
try {
|
|
223
|
+
defaultRun('docker', args, { cwd: installDir });
|
|
224
|
+
} catch {
|
|
225
|
+
defaultRun('sudo', ['systemctl', 'start', 'docker']);
|
|
226
|
+
defaultRun('sudo', ['docker', ...args], { cwd: installDir });
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
193
230
|
export function midserversCmd(wrap) {
|
|
194
231
|
return {
|
|
195
232
|
command: 'midservers [subcommand]',
|
|
@@ -218,6 +255,26 @@ export function midserversCmd(wrap) {
|
|
|
218
255
|
app.ok(record, { summary: `MID Server: ${getStringField(record, 'name') || argv.identifier}` });
|
|
219
256
|
}),
|
|
220
257
|
})
|
|
258
|
+
.command({
|
|
259
|
+
command: 'logs <identifier>',
|
|
260
|
+
describe: 'Show local Docker Compose logs for a MID Server',
|
|
261
|
+
builder: (b) => b
|
|
262
|
+
.option('tail', { type: 'number', default: 100, describe: 'Number of log lines' })
|
|
263
|
+
.option('follow', { type: 'boolean', default: false, describe: 'Follow the logs' }),
|
|
264
|
+
handler: wrap(async (argv, app) => {
|
|
265
|
+
showMidServerLogs(app, argv.identifier, argv);
|
|
266
|
+
}),
|
|
267
|
+
})
|
|
268
|
+
.command({
|
|
269
|
+
command: '<identifier> logs',
|
|
270
|
+
describe: 'Show local Docker Compose logs for a MID Server',
|
|
271
|
+
builder: (b) => b
|
|
272
|
+
.option('tail', { type: 'number', default: 100, describe: 'Number of log lines' })
|
|
273
|
+
.option('follow', { type: 'boolean', default: false, describe: 'Follow the logs' }),
|
|
274
|
+
handler: wrap(async (argv, app) => {
|
|
275
|
+
showMidServerLogs(app, argv.identifier, argv);
|
|
276
|
+
}),
|
|
277
|
+
})
|
|
221
278
|
.command({
|
|
222
279
|
command: 'add',
|
|
223
280
|
describe: 'Install and verify a MID Server',
|
|
@@ -226,7 +283,7 @@ export function midserversCmd(wrap) {
|
|
|
226
283
|
.option('username', { type: 'string', describe: 'MID Server username (password is always prompted)' })
|
|
227
284
|
.option('install-method', { type: 'string', choices: ['docker', 'direct'], describe: 'Installation method (prompted when omitted)' })
|
|
228
285
|
.option('directory', { type: 'string', describe: 'Docker installation directory' })
|
|
229
|
-
.option('wait-timeout', { type: 'number', default:
|
|
286
|
+
.option('wait-timeout', { type: 'number', default: 300000, describe: 'How long to wait for the MID Server to report Up' })
|
|
230
287
|
.option('wait-interval', { type: 'number', default: 3000, describe: 'MID Server status polling interval in milliseconds' }),
|
|
231
288
|
handler: wrap(async (argv, app) => {
|
|
232
289
|
app.requireInstance();
|
|
@@ -272,13 +329,14 @@ export function midserversCmd(wrap) {
|
|
|
272
329
|
});
|
|
273
330
|
if (installMethod === 'sudo') {
|
|
274
331
|
defaultRun('sudo', ['systemctl', 'start', 'docker']);
|
|
275
|
-
defaultRun('sudo', ['docker', 'compose', '-f', installation.composePath, 'up', '-d'], { cwd: installation.installDir });
|
|
332
|
+
defaultRun('sudo', ['docker', 'compose', '--env-file', path.join(installation.installDir, '.env'), '-f', installation.composePath, 'up', '-d'], { cwd: installation.installDir });
|
|
276
333
|
installation.prepared = false;
|
|
277
334
|
}
|
|
278
335
|
if (installation.prepared) {
|
|
279
336
|
app.ok(installation, { summary: `Prepared MID Server files in ${installation.installDir}. Run: ${installation.runCommand}` });
|
|
280
337
|
return;
|
|
281
338
|
}
|
|
339
|
+
console.log('MID Server container started. ServiceNow may take a few minutes to register it and report Up.');
|
|
282
340
|
let record;
|
|
283
341
|
try {
|
|
284
342
|
record = await waitForMidServerUp(app, installation.name, Math.max(1, argv['wait-timeout']), Math.max(500, argv['wait-interval']));
|
|
@@ -302,7 +360,14 @@ export function midserversCmd(wrap) {
|
|
|
302
360
|
handler: wrap(async (argv, app) => {
|
|
303
361
|
let record;
|
|
304
362
|
if (argv.identifier) {
|
|
305
|
-
|
|
363
|
+
try {
|
|
364
|
+
record = await resolveMidServer(app.sdk, argv.identifier);
|
|
365
|
+
} catch (error) {
|
|
366
|
+
if (error.code !== 'not_found') throw error;
|
|
367
|
+
const localDir = midInstallDirectory(app.getEffectiveInstance(), argv.identifier);
|
|
368
|
+
if (!fs.existsSync(path.join(localDir, 'docker-compose.yaml'))) throw error;
|
|
369
|
+
record = null;
|
|
370
|
+
}
|
|
306
371
|
} else {
|
|
307
372
|
if (!canPrompt() || app.output.getFormat() !== FormatAuto) {
|
|
308
373
|
throw new Error('An identifier is required outside an interactive terminal');
|
|
@@ -323,11 +388,12 @@ export function midserversCmd(wrap) {
|
|
|
323
388
|
});
|
|
324
389
|
if (!record) return;
|
|
325
390
|
}
|
|
326
|
-
const sysId = unwrapSysId(record);
|
|
391
|
+
const sysId = record ? unwrapSysId(record) : null;
|
|
327
392
|
const name = getStringField(record, 'name') || argv.identifier || sysId;
|
|
328
393
|
await confirmDelete(app, argv, `Remove MID Server '${name}'`);
|
|
329
|
-
|
|
330
|
-
app.
|
|
394
|
+
const localRemoved = removeLocalMidServer(app.getEffectiveInstance(), name);
|
|
395
|
+
if (record) await app.sdk.delete(AGENT_TABLE, sysId);
|
|
396
|
+
app.ok({ name, sys_id: sysId, deleted: Boolean(record), local_compose_removed: localRemoved }, { summary: `Removed MID Server '${name}'${localRemoved ? ' and its local Compose stack' : ''}` });
|
|
331
397
|
}),
|
|
332
398
|
})
|
|
333
399
|
.command({
|
package/src/midserver-install.js
CHANGED
|
@@ -90,6 +90,11 @@ function instanceName(instanceURL) {
|
|
|
90
90
|
return hostname.replace(/\.service-now\.com$|\.servicenowservices\.com$/i, '').replace(/[^A-Za-z0-9._-]/g, '_');
|
|
91
91
|
}
|
|
92
92
|
|
|
93
|
+
export function midInstallDirectory(instanceURL, name, homeDirectory = os.homedir()) {
|
|
94
|
+
const shortName = normalizeMidName(name);
|
|
95
|
+
return path.join(homeDirectory, '.servicenow', 'mids', instanceName(instanceURL), shortName);
|
|
96
|
+
}
|
|
97
|
+
|
|
93
98
|
export async function installDockerMidServer({
|
|
94
99
|
instanceURL,
|
|
95
100
|
username,
|
|
@@ -106,11 +111,11 @@ export async function installDockerMidServer({
|
|
|
106
111
|
const displayName = `mid.server.${shortName}`;
|
|
107
112
|
const releaseName = suppliedReleaseName || (await validateMidCredentials(instanceURL, username, password, fetchImpl)).releaseName;
|
|
108
113
|
const recipeURL = buildMidRecipeUrl(releaseName);
|
|
109
|
-
const
|
|
110
|
-
const installDir = directory || path.join(os.homedir(), '.servicenow', 'mids', serviceNowInstance, shortName);
|
|
114
|
+
const installDir = directory || midInstallDirectory(instanceURL, name);
|
|
111
115
|
fs.mkdirSync(installDir, { recursive: true, mode: 0o700 });
|
|
112
116
|
const exportDir = path.join(installDir, 'export');
|
|
113
|
-
fs.mkdirSync(exportDir, { recursive: true, mode:
|
|
117
|
+
fs.mkdirSync(exportDir, { recursive: true, mode: 0o777 });
|
|
118
|
+
fs.chmodSync(exportDir, 0o777);
|
|
114
119
|
const workDir = fs.mkdtempSync(path.join(os.tmpdir(), 'jsn-mid-'));
|
|
115
120
|
const filename = path.basename(recipeURL);
|
|
116
121
|
const archive = path.join(workDir, filename);
|
|
@@ -144,18 +149,18 @@ export async function installDockerMidServer({
|
|
|
144
149
|
composePath,
|
|
145
150
|
serviceName,
|
|
146
151
|
prepared: true,
|
|
147
|
-
runCommand: `docker compose -f ${composePath} up -d`,
|
|
152
|
+
runCommand: `docker compose --env-file ${envPath} -f ${composePath} up -d`,
|
|
148
153
|
};
|
|
149
154
|
}
|
|
150
155
|
run('docker', ['build', '--tag', `${serviceName}:${releaseName}`, persistedRecipeDir]);
|
|
151
|
-
run('docker', ['compose', '-f', composePath, 'up', '-d'], { cwd: installDir, env });
|
|
156
|
+
run('docker', ['compose', '--env-file', envPath, '-f', composePath, 'up', '-d'], { cwd: installDir, env });
|
|
152
157
|
return {
|
|
153
158
|
name: displayName,
|
|
154
159
|
releaseName,
|
|
155
160
|
installDir,
|
|
156
161
|
composePath,
|
|
157
162
|
serviceName,
|
|
158
|
-
rollback: () => run('docker', ['compose', '-f', composePath, 'down'], { cwd: installDir, env }),
|
|
163
|
+
rollback: () => run('docker', ['compose', '--env-file', envPath, '-f', composePath, 'down'], { cwd: installDir, env }),
|
|
159
164
|
};
|
|
160
165
|
} finally {
|
|
161
166
|
fs.rmSync(workDir, { recursive: true, force: true });
|