@abgov/nx-oc 12.3.0 → 12.5.0
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/adsp/adsp-utils.js +28 -0
- package/src/adsp/adsp-utils.js.map +1 -1
- package/src/adsp/token-cache.d.ts +9 -0
- package/src/adsp/token-cache.js +49 -0
- package/src/adsp/token-cache.js.map +1 -0
- package/src/adsp/token-cache.spec.ts +117 -0
- package/src/generators/apply-infra/apply-infra.js +6 -5
- package/src/generators/apply-infra/apply-infra.js.map +1 -1
- package/src/generators/apply-infra/apply-infra.spec.ts +17 -4
- package/src/utils/oc-utils.d.ts +1 -0
- package/src/utils/oc-utils.js +32 -0
- package/src/utils/oc-utils.js.map +1 -1
- package/src/utils/oc-utils.spec.ts +67 -2
package/package.json
CHANGED
package/src/adsp/adsp-utils.js
CHANGED
|
@@ -14,12 +14,14 @@ const express = require("express");
|
|
|
14
14
|
const open = require("open");
|
|
15
15
|
const simple_oauth2_1 = require("simple-oauth2");
|
|
16
16
|
const environments_1 = require("./environments");
|
|
17
|
+
const token_cache_1 = require("./token-cache");
|
|
17
18
|
function hasDependency(host, dependency) {
|
|
18
19
|
const { dependencies, devDependencies } = (0, devkit_1.readJson)(host, 'package.json');
|
|
19
20
|
return !!(dependencies === null || dependencies === void 0 ? void 0 : dependencies[dependency]) || !!(devDependencies === null || devDependencies === void 0 ? void 0 : devDependencies[dependency]);
|
|
20
21
|
}
|
|
21
22
|
function realmLogin(accessServiceUrl, realm) {
|
|
22
23
|
return tslib_1.__awaiter(this, void 0, void 0, function* () {
|
|
24
|
+
var _a, _b;
|
|
23
25
|
const client = new simple_oauth2_1.AuthorizationCode({
|
|
24
26
|
client: {
|
|
25
27
|
id: 'nx-adsp-cli',
|
|
@@ -31,6 +33,28 @@ function realmLogin(accessServiceUrl, realm) {
|
|
|
31
33
|
authorizePath: `/auth/realms/${realm}/protocol/openid-connect/auth`,
|
|
32
34
|
},
|
|
33
35
|
});
|
|
36
|
+
// Core realm is only used to fetch the tenant list — not worth caching.
|
|
37
|
+
if (realm !== 'core') {
|
|
38
|
+
const cached = (0, token_cache_1.getCachedToken)(accessServiceUrl, realm);
|
|
39
|
+
if (cached) {
|
|
40
|
+
if (!(0, token_cache_1.isExpired)(cached)) {
|
|
41
|
+
return cached.accessToken;
|
|
42
|
+
}
|
|
43
|
+
if (cached.refreshToken) {
|
|
44
|
+
try {
|
|
45
|
+
const { token } = yield client
|
|
46
|
+
.createToken({ access_token: cached.accessToken, refresh_token: cached.refreshToken })
|
|
47
|
+
.refresh();
|
|
48
|
+
const expiresIn = (_a = token['expires_in']) !== null && _a !== void 0 ? _a : 300;
|
|
49
|
+
(0, token_cache_1.setCachedToken)(accessServiceUrl, realm, token['access_token'], token['refresh_token'], expiresIn);
|
|
50
|
+
return token['access_token'];
|
|
51
|
+
}
|
|
52
|
+
catch (_c) {
|
|
53
|
+
// Refresh failed — fall through to browser login.
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
34
58
|
const redirect_uri = 'http://localhost:3000/callback';
|
|
35
59
|
const authorizationUri = client.authorizeURL({
|
|
36
60
|
redirect_uri,
|
|
@@ -59,6 +83,10 @@ function realmLogin(accessServiceUrl, realm) {
|
|
|
59
83
|
tokenPromise,
|
|
60
84
|
new Promise((_, reject) => setTimeout(() => reject(new Error('Timed out waiting for login.')), 120000)),
|
|
61
85
|
]).finally(() => server.close());
|
|
86
|
+
if (realm !== 'core') {
|
|
87
|
+
const expiresIn = (_b = token['expires_in']) !== null && _b !== void 0 ? _b : 300;
|
|
88
|
+
(0, token_cache_1.setCachedToken)(accessServiceUrl, realm, token['access_token'], token['refresh_token'], expiresIn);
|
|
89
|
+
}
|
|
62
90
|
return token['access_token'];
|
|
63
91
|
});
|
|
64
92
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"adsp-utils.js","sourceRoot":"","sources":["../../../../../packages/nx-oc/src/adsp/adsp-utils.ts"],"names":[],"mappings":";;
|
|
1
|
+
{"version":3,"file":"adsp-utils.js","sourceRoot":"","sources":["../../../../../packages/nx-oc/src/adsp/adsp-utils.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export interface CacheEntry {
|
|
2
|
+
accessToken: string;
|
|
3
|
+
refreshToken?: string;
|
|
4
|
+
expiresAt: string;
|
|
5
|
+
}
|
|
6
|
+
export declare function getCacheFilePath(): string;
|
|
7
|
+
export declare function isExpired(entry: CacheEntry): boolean;
|
|
8
|
+
export declare function getCachedToken(accessServiceUrl: string, realm: string): CacheEntry | null;
|
|
9
|
+
export declare function setCachedToken(accessServiceUrl: string, realm: string, accessToken: string, refreshToken: string | undefined, expiresIn: number): void;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getCacheFilePath = getCacheFilePath;
|
|
4
|
+
exports.isExpired = isExpired;
|
|
5
|
+
exports.getCachedToken = getCachedToken;
|
|
6
|
+
exports.setCachedToken = setCachedToken;
|
|
7
|
+
const fs = require("fs");
|
|
8
|
+
const os = require("os");
|
|
9
|
+
const path = require("path");
|
|
10
|
+
const EXPIRY_BUFFER_MS = 60000;
|
|
11
|
+
function getCacheFilePath() {
|
|
12
|
+
return path.join(os.homedir(), '.nx-adsp', 'token-cache.json');
|
|
13
|
+
}
|
|
14
|
+
function readCache() {
|
|
15
|
+
try {
|
|
16
|
+
return JSON.parse(fs.readFileSync(getCacheFilePath(), 'utf-8'));
|
|
17
|
+
}
|
|
18
|
+
catch (_a) {
|
|
19
|
+
return {};
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
function writeCache(cache) {
|
|
23
|
+
const filePath = getCacheFilePath();
|
|
24
|
+
const dir = path.dirname(filePath);
|
|
25
|
+
if (!fs.existsSync(dir)) {
|
|
26
|
+
fs.mkdirSync(dir, { recursive: true, mode: 0o700 });
|
|
27
|
+
}
|
|
28
|
+
fs.writeFileSync(filePath, JSON.stringify(cache, null, 2), { mode: 0o600, encoding: 'utf-8' });
|
|
29
|
+
}
|
|
30
|
+
function cacheKey(accessServiceUrl, realm) {
|
|
31
|
+
return `${accessServiceUrl}::${realm}`;
|
|
32
|
+
}
|
|
33
|
+
function isExpired(entry) {
|
|
34
|
+
return new Date(entry.expiresAt).getTime() - EXPIRY_BUFFER_MS < Date.now();
|
|
35
|
+
}
|
|
36
|
+
function getCachedToken(accessServiceUrl, realm) {
|
|
37
|
+
var _a;
|
|
38
|
+
return (_a = readCache()[cacheKey(accessServiceUrl, realm)]) !== null && _a !== void 0 ? _a : null;
|
|
39
|
+
}
|
|
40
|
+
function setCachedToken(accessServiceUrl, realm, accessToken, refreshToken, expiresIn) {
|
|
41
|
+
const cache = readCache();
|
|
42
|
+
cache[cacheKey(accessServiceUrl, realm)] = {
|
|
43
|
+
accessToken,
|
|
44
|
+
refreshToken,
|
|
45
|
+
expiresAt: new Date(Date.now() + expiresIn * 1000).toISOString(),
|
|
46
|
+
};
|
|
47
|
+
writeCache(cache);
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=token-cache.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"token-cache.js","sourceRoot":"","sources":["../../../../../packages/nx-oc/src/adsp/token-cache.ts"],"names":[],"mappings":";;AAcA,4CAEC;AAuBD,8BAEC;AAED,wCAEC;AAED,wCAcC;AA7DD,yBAAyB;AACzB,yBAAyB;AACzB,6BAA6B;AAU7B,MAAM,gBAAgB,GAAG,KAAM,CAAC;AAEhC,SAAgB,gBAAgB;IAC9B,OAAO,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,UAAU,EAAE,kBAAkB,CAAC,CAAC;AACjE,CAAC;AAED,SAAS,SAAS;IAChB,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,CAAC,gBAAgB,EAAE,EAAE,OAAO,CAAC,CAAe,CAAC;IAChF,CAAC;IAAC,WAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC;AAED,SAAS,UAAU,CAAC,KAAiB;IACnC,MAAM,QAAQ,GAAG,gBAAgB,EAAE,CAAC;IACpC,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;IACnC,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;QACxB,EAAE,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IACtD,CAAC;IACD,EAAE,CAAC,aAAa,CAAC,QAAQ,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC,CAAC;AACjG,CAAC;AAED,SAAS,QAAQ,CAAC,gBAAwB,EAAE,KAAa;IACvD,OAAO,GAAG,gBAAgB,KAAK,KAAK,EAAE,CAAC;AACzC,CAAC;AAED,SAAgB,SAAS,CAAC,KAAiB;IACzC,OAAO,IAAI,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,OAAO,EAAE,GAAG,gBAAgB,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;AAC7E,CAAC;AAED,SAAgB,cAAc,CAAC,gBAAwB,EAAE,KAAa;;IACpE,OAAO,MAAA,SAAS,EAAE,CAAC,QAAQ,CAAC,gBAAgB,EAAE,KAAK,CAAC,CAAC,mCAAI,IAAI,CAAC;AAChE,CAAC;AAED,SAAgB,cAAc,CAC5B,gBAAwB,EACxB,KAAa,EACb,WAAmB,EACnB,YAAgC,EAChC,SAAiB;IAEjB,MAAM,KAAK,GAAG,SAAS,EAAE,CAAC;IAC1B,KAAK,CAAC,QAAQ,CAAC,gBAAgB,EAAE,KAAK,CAAC,CAAC,GAAG;QACzC,WAAW;QACX,YAAY;QACZ,SAAS,EAAE,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS,GAAG,IAAI,CAAC,CAAC,WAAW,EAAE;KACjE,CAAC;IACF,UAAU,CAAC,KAAK,CAAC,CAAC;AACpB,CAAC"}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import * as fs from 'fs';
|
|
2
|
+
import * as os from 'os';
|
|
3
|
+
import * as path from 'path';
|
|
4
|
+
import { getCacheFilePath, getCachedToken, isExpired, setCachedToken, CacheEntry } from './token-cache';
|
|
5
|
+
|
|
6
|
+
jest.mock('fs');
|
|
7
|
+
jest.mock('os');
|
|
8
|
+
|
|
9
|
+
const mockedFs = jest.mocked(fs);
|
|
10
|
+
const mockedOs = jest.mocked(os);
|
|
11
|
+
|
|
12
|
+
const FAKE_HOME = '/home/testuser';
|
|
13
|
+
const CACHE_PATH = path.join(FAKE_HOME, '.nx-adsp', 'token-cache.json');
|
|
14
|
+
|
|
15
|
+
const ACCESS_URL = 'https://access.example.com/auth';
|
|
16
|
+
const REALM = 'my-tenant';
|
|
17
|
+
|
|
18
|
+
function futureIso(secondsFromNow: number): string {
|
|
19
|
+
return new Date(Date.now() + secondsFromNow * 1000).toISOString();
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function pastIso(secondsAgo: number): string {
|
|
23
|
+
return new Date(Date.now() - secondsAgo * 1000).toISOString();
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
describe('getCacheFilePath', () => {
|
|
27
|
+
it('returns path under home directory', () => {
|
|
28
|
+
mockedOs.homedir.mockReturnValue(FAKE_HOME);
|
|
29
|
+
expect(getCacheFilePath()).toBe(CACHE_PATH);
|
|
30
|
+
});
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
describe('isExpired', () => {
|
|
34
|
+
it('returns false when token expires in the future (beyond buffer)', () => {
|
|
35
|
+
const entry: CacheEntry = { accessToken: 'tok', expiresAt: futureIso(120) };
|
|
36
|
+
expect(isExpired(entry)).toBe(false);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it('returns true when token is past its expiry', () => {
|
|
40
|
+
const entry: CacheEntry = { accessToken: 'tok', expiresAt: pastIso(10) };
|
|
41
|
+
expect(isExpired(entry)).toBe(true);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it('returns true when token expires within the 60-second buffer', () => {
|
|
45
|
+
const entry: CacheEntry = { accessToken: 'tok', expiresAt: futureIso(30) };
|
|
46
|
+
expect(isExpired(entry)).toBe(true);
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
describe('getCachedToken', () => {
|
|
51
|
+
beforeEach(() => {
|
|
52
|
+
mockedOs.homedir.mockReturnValue(FAKE_HOME);
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it('returns null when cache file does not exist', () => {
|
|
56
|
+
mockedFs.readFileSync.mockImplementation(() => { throw new Error('ENOENT'); });
|
|
57
|
+
expect(getCachedToken(ACCESS_URL, REALM)).toBeNull();
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it('returns null when realm is not in cache', () => {
|
|
61
|
+
mockedFs.readFileSync.mockReturnValue(JSON.stringify({ 'other::realm': { accessToken: 'x', expiresAt: futureIso(3600) } }));
|
|
62
|
+
expect(getCachedToken(ACCESS_URL, REALM)).toBeNull();
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it('returns the cached entry when present', () => {
|
|
66
|
+
const entry: CacheEntry = { accessToken: 'abc', refreshToken: 'ref', expiresAt: futureIso(3600) };
|
|
67
|
+
const key = `${ACCESS_URL}::${REALM}`;
|
|
68
|
+
mockedFs.readFileSync.mockReturnValue(JSON.stringify({ [key]: entry }));
|
|
69
|
+
|
|
70
|
+
expect(getCachedToken(ACCESS_URL, REALM)).toEqual(entry);
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
describe('setCachedToken', () => {
|
|
75
|
+
beforeEach(() => {
|
|
76
|
+
mockedOs.homedir.mockReturnValue(FAKE_HOME);
|
|
77
|
+
mockedFs.existsSync.mockReturnValue(true);
|
|
78
|
+
mockedFs.readFileSync.mockImplementation(() => { throw new Error('ENOENT'); });
|
|
79
|
+
(mockedFs.writeFileSync as jest.Mock).mockReset();
|
|
80
|
+
(mockedFs.mkdirSync as jest.Mock).mockReset();
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
it('writes a new entry with correct fields', () => {
|
|
84
|
+
setCachedToken(ACCESS_URL, REALM, 'access-tok', 'refresh-tok', 3600);
|
|
85
|
+
|
|
86
|
+
expect(mockedFs.writeFileSync).toHaveBeenCalledWith(
|
|
87
|
+
CACHE_PATH,
|
|
88
|
+
expect.stringContaining('"accessToken": "access-tok"'),
|
|
89
|
+
expect.objectContaining({ mode: 0o600 })
|
|
90
|
+
);
|
|
91
|
+
|
|
92
|
+
const written = JSON.parse((mockedFs.writeFileSync as jest.Mock).mock.calls[0][1] as string);
|
|
93
|
+
const entry = written[`${ACCESS_URL}::${REALM}`];
|
|
94
|
+
expect(entry.accessToken).toBe('access-tok');
|
|
95
|
+
expect(entry.refreshToken).toBe('refresh-tok');
|
|
96
|
+
expect(new Date(entry.expiresAt).getTime()).toBeGreaterThan(Date.now() + 3590 * 1000);
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
it('writes without refreshToken when not provided', () => {
|
|
100
|
+
setCachedToken(ACCESS_URL, REALM, 'access-tok', undefined, 3600);
|
|
101
|
+
|
|
102
|
+
const written = JSON.parse((mockedFs.writeFileSync as jest.Mock).mock.calls[0][1] as string);
|
|
103
|
+
const entry = written[`${ACCESS_URL}::${REALM}`];
|
|
104
|
+
expect(entry.refreshToken).toBeUndefined();
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
it('creates the cache directory if it does not exist', () => {
|
|
108
|
+
mockedFs.existsSync.mockReturnValue(false);
|
|
109
|
+
|
|
110
|
+
setCachedToken(ACCESS_URL, REALM, 'tok', undefined, 300);
|
|
111
|
+
|
|
112
|
+
expect(mockedFs.mkdirSync).toHaveBeenCalledWith(
|
|
113
|
+
path.dirname(CACHE_PATH),
|
|
114
|
+
expect.objectContaining({ recursive: true, mode: 0o700 })
|
|
115
|
+
);
|
|
116
|
+
});
|
|
117
|
+
});
|
|
@@ -14,13 +14,14 @@ function applyOcResources(host) {
|
|
|
14
14
|
}
|
|
15
15
|
function default_1(host) {
|
|
16
16
|
return tslib_1.__awaiter(this, void 0, void 0, function* () {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
applyOcResources(host);
|
|
17
|
+
try {
|
|
18
|
+
(0, oc_utils_1.ensureOcLogin)();
|
|
20
19
|
}
|
|
21
|
-
|
|
22
|
-
console.log(
|
|
20
|
+
catch (e) {
|
|
21
|
+
console.log(e instanceof Error ? e.message : String(e));
|
|
22
|
+
return;
|
|
23
23
|
}
|
|
24
|
+
applyOcResources(host);
|
|
24
25
|
});
|
|
25
26
|
}
|
|
26
27
|
//# sourceMappingURL=apply-infra.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"apply-infra.js","sourceRoot":"","sources":["../../../../../../packages/nx-oc/src/generators/apply-infra/apply-infra.ts"],"names":[],"mappings":";;AAuBA,
|
|
1
|
+
{"version":3,"file":"apply-infra.js","sourceRoot":"","sources":["../../../../../../packages/nx-oc/src/generators/apply-infra/apply-infra.ts"],"names":[],"mappings":";;AAuBA,4BAQC;;AA9BD,mDAAmE;AAEnE,SAAS,gBAAgB,CAAC,IAAU;IAClC,MAAM,EAAE,OAAO,EAAE,eAAe,EAAE,MAAM,EAAE,WAAW,EAAE,GAAG,IAAA,uBAAY,EACpE,OAAO,EACP,EAAE,EACF,IAAI,CAAC,IAAI,CAAC,kCAAkC,CAAC,CAC9C,CAAC;IACF,OAAO,CAAC,GAAG,CAAC,WAAW,aAAX,WAAW,uBAAX,WAAW,CAAE,QAAQ,EAAE,CAAC,CAAC;IAErC,MAAM,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,IAAA,uBAAY,EAC1D,OAAO,EACP,EAAE,EACF,IAAI,CAAC,IAAI,CAAC,6BAA6B,CAAC,CACzC,CAAC;IACF,OAAO,CAAC,GAAG,CAAC,MAAM,aAAN,MAAM,uBAAN,MAAM,CAAE,QAAQ,EAAE,CAAC,CAAC;IAEhC,IAAI,CAAC,eAAe,IAAI,CAAC,UAAU,EAAE,CAAC;QACpC,OAAO,CAAC,GAAG,CAAC,kDAAkD,CAAC,CAAC;IAClE,CAAC;AACH,CAAC;AAED,mBAA+B,IAAU;;QACvC,IAAI,CAAC;YACH,IAAA,wBAAa,GAAE,CAAC;QAClB,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,OAAO,CAAC,GAAG,CAAC,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;YACxD,OAAO;QACT,CAAC;QACD,gBAAgB,CAAC,IAAI,CAAC,CAAC;IACzB,CAAC;CAAA"}
|
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
import { createTree } from '@nx/devkit/testing';
|
|
2
2
|
import { mocked } from 'jest-mock';
|
|
3
3
|
import generator from './apply-infra';
|
|
4
|
-
import { runOcCommand } from '../../utils/oc-utils';
|
|
4
|
+
import { ensureOcLogin, runOcCommand } from '../../utils/oc-utils';
|
|
5
5
|
import { writeJson } from '@nx/devkit';
|
|
6
6
|
|
|
7
7
|
jest.mock('../../utils/oc-utils');
|
|
8
|
+
const mockedEnsureOcLogin = mocked(ensureOcLogin);
|
|
8
9
|
const mockedRunOcCommand = mocked(runOcCommand);
|
|
9
10
|
|
|
10
11
|
describe('Apply Infra Generator', () => {
|
|
11
12
|
beforeEach(() => {
|
|
13
|
+
mockedEnsureOcLogin.mockReset();
|
|
12
14
|
mockedRunOcCommand.mockReset();
|
|
13
15
|
});
|
|
14
16
|
|
|
@@ -21,9 +23,20 @@ describe('Apply Infra Generator', () => {
|
|
|
21
23
|
|
|
22
24
|
await generator(host);
|
|
23
25
|
|
|
24
|
-
expect(
|
|
25
|
-
expect(mockedRunOcCommand.mock.calls
|
|
26
|
+
expect(mockedEnsureOcLogin).toHaveBeenCalledTimes(1);
|
|
27
|
+
expect(mockedRunOcCommand.mock.calls.length).toBe(2);
|
|
28
|
+
expect(mockedRunOcCommand.mock.calls[0][0]).toBe('apply');
|
|
26
29
|
expect(mockedRunOcCommand.mock.calls[1][0]).toBe('apply');
|
|
27
|
-
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it('logs login error and exits cleanly without running oc apply', async () => {
|
|
33
|
+
const host = createTree();
|
|
34
|
+
mockedEnsureOcLogin.mockImplementation(() => {
|
|
35
|
+
throw new Error('oc CLI is not installed or not on PATH.');
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
await generator(host);
|
|
39
|
+
|
|
40
|
+
expect(mockedRunOcCommand).not.toHaveBeenCalled();
|
|
28
41
|
});
|
|
29
42
|
});
|
package/src/utils/oc-utils.d.ts
CHANGED
package/src/utils/oc-utils.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.runOcCommand = runOcCommand;
|
|
4
|
+
exports.ensureOcLogin = ensureOcLogin;
|
|
4
5
|
const child_process_1 = require("child_process");
|
|
5
6
|
function runOcCommand(command, params, input) {
|
|
6
7
|
const args = input
|
|
@@ -16,4 +17,35 @@ function runOcCommand(command, params, input) {
|
|
|
16
17
|
return { success: false };
|
|
17
18
|
}
|
|
18
19
|
}
|
|
20
|
+
function ensureOcLogin() {
|
|
21
|
+
try {
|
|
22
|
+
(0, child_process_1.execFileSync)('oc', ['version', '--client'], { stdio: 'pipe' });
|
|
23
|
+
}
|
|
24
|
+
catch (_a) {
|
|
25
|
+
throw new Error("oc CLI is not installed or not on PATH. Install it from https://mirror.openshift.com/pub/openshift-v4/clients/ocp/latest/ then re-run.");
|
|
26
|
+
}
|
|
27
|
+
try {
|
|
28
|
+
(0, child_process_1.execFileSync)('oc', ['whoami'], { stdio: 'pipe' });
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
catch (_b) {
|
|
32
|
+
// not logged in — fall through to interactive login
|
|
33
|
+
}
|
|
34
|
+
let server = null;
|
|
35
|
+
try {
|
|
36
|
+
server =
|
|
37
|
+
(0, child_process_1.execFileSync)('oc', ['config', 'view', '--minify', '-o', 'jsonpath={.clusters[0].cluster.server}'], { stdio: 'pipe' })
|
|
38
|
+
.toString()
|
|
39
|
+
.trim() || null;
|
|
40
|
+
}
|
|
41
|
+
catch (_c) {
|
|
42
|
+
// no kubeconfig — oc login will prompt for server
|
|
43
|
+
}
|
|
44
|
+
console.log('Not logged in to OpenShift. Starting interactive login...');
|
|
45
|
+
const loginArgs = server ? ['login', server, '--web'] : ['login', '--web'];
|
|
46
|
+
const result = (0, child_process_1.spawnSync)('oc', loginArgs, { stdio: 'inherit' });
|
|
47
|
+
if (result.status !== 0) {
|
|
48
|
+
throw new Error("OpenShift login failed or was cancelled. Run 'oc login' manually then re-run the generator.");
|
|
49
|
+
}
|
|
50
|
+
}
|
|
19
51
|
//# sourceMappingURL=oc-utils.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"oc-utils.js","sourceRoot":"","sources":["../../../../../packages/nx-oc/src/utils/oc-utils.ts"],"names":[],"mappings":";;AAEA,oCAiBC;
|
|
1
|
+
{"version":3,"file":"oc-utils.js","sourceRoot":"","sources":["../../../../../packages/nx-oc/src/utils/oc-utils.ts"],"names":[],"mappings":";;AAEA,oCAiBC;AAED,sCAmCC;AAxDD,iDAAwD;AAExD,SAAgB,YAAY,CAC1B,OAAwD,EACxD,MAAgB,EAChB,KAAc;IAEd,MAAM,IAAI,GAAG,KAAK;QAChB,CAAC,CAAC,CAAC,OAAO,EAAE,IAAI,EAAE,GAAG,EAAE,GAAG,MAAM,CAAC;QACjC,CAAC,CAAC,CAAC,OAAO,EAAE,GAAG,MAAM,CAAC,CAAC;IAEzB,IAAI,CAAC;QACH,OAAO,CAAC,GAAG,CAAC,yBAAyB,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QACvD,MAAM,MAAM,GAAG,IAAA,4BAAY,EAAC,IAAI,EAAE,IAAI,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,CAAC;QAClE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;IACnC,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,OAAO,CAAC,GAAG,CAAC,iCAAiC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;QAClE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;IAC5B,CAAC;AACH,CAAC;AAED,SAAgB,aAAa;IAC3B,IAAI,CAAC;QACH,IAAA,4BAAY,EAAC,IAAI,EAAE,CAAC,SAAS,EAAE,UAAU,CAAC,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC;IACjE,CAAC;IAAC,WAAM,CAAC;QACP,MAAM,IAAI,KAAK,CACb,wIAAwI,CACzI,CAAC;IACJ,CAAC;IAED,IAAI,CAAC;QACH,IAAA,4BAAY,EAAC,IAAI,EAAE,CAAC,QAAQ,CAAC,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC;QAClD,OAAO;IACT,CAAC;IAAC,WAAM,CAAC;QACP,oDAAoD;IACtD,CAAC;IAED,IAAI,MAAM,GAAkB,IAAI,CAAC;IACjC,IAAI,CAAC;QACH,MAAM;YACJ,IAAA,4BAAY,EAAC,IAAI,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,IAAI,EAAE,wCAAwC,CAAC,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC;iBAClH,QAAQ,EAAE;iBACV,IAAI,EAAE,IAAI,IAAI,CAAC;IACtB,CAAC;IAAC,WAAM,CAAC;QACP,kDAAkD;IACpD,CAAC;IAED,OAAO,CAAC,GAAG,CAAC,2DAA2D,CAAC,CAAC;IACzE,MAAM,SAAS,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;IAC3E,MAAM,MAAM,GAAG,IAAA,yBAAS,EAAC,IAAI,EAAE,SAAS,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC,CAAC;IAEhE,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACxB,MAAM,IAAI,KAAK,CACb,6FAA6F,CAC9F,CAAC;IACJ,CAAC;AACH,CAAC"}
|
|
@@ -1,9 +1,13 @@
|
|
|
1
|
-
import { execFileSync } from 'child_process';
|
|
1
|
+
import { execFileSync, spawnSync } from 'child_process';
|
|
2
2
|
import { mocked } from 'jest-mock';
|
|
3
|
-
import { runOcCommand } from './oc-utils';
|
|
3
|
+
import { ensureOcLogin, runOcCommand } from './oc-utils';
|
|
4
4
|
|
|
5
5
|
jest.mock('child_process');
|
|
6
6
|
const mockedExecFileSync = mocked(execFileSync);
|
|
7
|
+
const mockedSpawnSync = mocked(spawnSync);
|
|
8
|
+
|
|
9
|
+
const SPAWN_OK = { status: 0, pid: 1, output: [], stdout: Buffer.from(''), stderr: Buffer.from(''), signal: null };
|
|
10
|
+
const SPAWN_FAIL = { ...SPAWN_OK, status: 1 };
|
|
7
11
|
|
|
8
12
|
describe('runOcCommand', () => {
|
|
9
13
|
beforeEach(() => {
|
|
@@ -61,3 +65,64 @@ describe('runOcCommand', () => {
|
|
|
61
65
|
expect(result.stdout).toBeUndefined();
|
|
62
66
|
});
|
|
63
67
|
});
|
|
68
|
+
|
|
69
|
+
describe('ensureOcLogin', () => {
|
|
70
|
+
beforeEach(() => {
|
|
71
|
+
mockedExecFileSync.mockReset();
|
|
72
|
+
mockedSpawnSync.mockReset();
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
it('returns without spawning login when already logged in', () => {
|
|
76
|
+
mockedExecFileSync.mockReturnValueOnce(Buffer.from('Client Version: 4.14.0')); // oc version --client
|
|
77
|
+
mockedExecFileSync.mockReturnValueOnce(Buffer.from('developer')); // oc whoami
|
|
78
|
+
|
|
79
|
+
ensureOcLogin();
|
|
80
|
+
|
|
81
|
+
expect(mockedSpawnSync).not.toHaveBeenCalled();
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
it('throws a clear error when oc is not installed', () => {
|
|
85
|
+
mockedExecFileSync.mockImplementationOnce(() => {
|
|
86
|
+
throw new Error('oc: command not found');
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
expect(() => ensureOcLogin()).toThrow('oc CLI is not installed');
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
it('spawns oc login with server and --web when kubeconfig has a current context', () => {
|
|
93
|
+
mockedExecFileSync.mockReturnValueOnce(Buffer.from('Client Version: 4.14.0')); // oc version --client
|
|
94
|
+
mockedExecFileSync.mockImplementationOnce(() => { throw new Error('not logged in'); }); // oc whoami
|
|
95
|
+
mockedExecFileSync.mockReturnValueOnce(Buffer.from('https://api.example.openshift.com:6443')); // oc config view
|
|
96
|
+
mockedSpawnSync.mockReturnValue(SPAWN_OK);
|
|
97
|
+
|
|
98
|
+
ensureOcLogin();
|
|
99
|
+
|
|
100
|
+
expect(mockedSpawnSync).toHaveBeenCalledWith(
|
|
101
|
+
'oc',
|
|
102
|
+
['login', 'https://api.example.openshift.com:6443', '--web'],
|
|
103
|
+
{ stdio: 'inherit' }
|
|
104
|
+
);
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
it('spawns oc login with only --web when no kubeconfig exists', () => {
|
|
108
|
+
mockedExecFileSync.mockReturnValueOnce(Buffer.from('Client Version: 4.14.0')); // oc version --client
|
|
109
|
+
mockedExecFileSync.mockImplementationOnce(() => { throw new Error('not logged in'); }); // oc whoami
|
|
110
|
+
mockedExecFileSync.mockImplementationOnce(() => { throw new Error('no config'); }); // oc config view
|
|
111
|
+
|
|
112
|
+
mockedSpawnSync.mockReturnValue(SPAWN_OK);
|
|
113
|
+
|
|
114
|
+
ensureOcLogin();
|
|
115
|
+
|
|
116
|
+
expect(mockedSpawnSync).toHaveBeenCalledWith('oc', ['login', '--web'], { stdio: 'inherit' });
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
it('throws when login is cancelled or fails', () => {
|
|
120
|
+
mockedExecFileSync.mockReturnValueOnce(Buffer.from('Client Version: 4.14.0')); // oc version --client
|
|
121
|
+
mockedExecFileSync.mockImplementationOnce(() => { throw new Error('not logged in'); }); // oc whoami
|
|
122
|
+
mockedExecFileSync.mockImplementationOnce(() => { throw new Error('no config'); }); // oc config view
|
|
123
|
+
|
|
124
|
+
mockedSpawnSync.mockReturnValue(SPAWN_FAIL);
|
|
125
|
+
|
|
126
|
+
expect(() => ensureOcLogin()).toThrow('OpenShift login failed or was cancelled');
|
|
127
|
+
});
|
|
128
|
+
});
|