underpost 3.2.12 → 3.2.14
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/.github/workflows/ghpkg.ci.yml +1 -0
- package/.github/workflows/npmpkg.ci.yml +9 -5
- package/CHANGELOG.md +58 -1
- package/CLI-HELP.md +906 -1130
- package/README.md +47 -41
- package/bin/build.js +88 -137
- package/bin/build.template.js +23 -179
- package/bin/deploy.js +4 -1
- package/bin/index.js +2 -2
- package/conf.js +11 -37
- package/manifests/cronjobs/dd-cron/dd-cron-backup.yaml +2 -2
- package/manifests/cronjobs/dd-cron/dd-cron-dns.yaml +1 -1
- package/manifests/deployment/dd-default-development/deployment.yaml +2 -2
- package/package.json +9 -14
- package/src/cli/deploy.js +0 -228
- package/src/cli/image.js +58 -4
- package/src/cli/monitor.js +190 -6
- package/src/cli/release.js +5 -5
- package/src/cli/repository.js +80 -3
- package/src/cli/run.js +115 -69
- package/src/index.js +1 -1
- package/src/runtime/wp/Dockerfile +3 -3
- package/src/server/catalog-underpost.js +61 -0
- package/src/server/catalog.js +77 -0
- package/src/server/conf.js +334 -49
- package/src/server/start.js +7 -3
- package/test/deploy-monitor.test.js +188 -0
- package/manifests/deployment/dd-test-development/deployment.yaml +0 -256
- package/manifests/deployment/dd-test-development/proxy.yaml +0 -102
package/bin/index.js
CHANGED
package/conf.js
CHANGED
|
@@ -95,16 +95,8 @@ const DefaultConf = /**/ {
|
|
|
95
95
|
{ path: '/default-management', client: 'Default', ssr: 'Default' },
|
|
96
96
|
{ client: 'Default', ssr: 'Default', path: '/404', title: '404 Not Found' },
|
|
97
97
|
{ client: 'Default', ssr: 'Default', path: '/500', title: '500 Server Error' },
|
|
98
|
-
{
|
|
99
|
-
|
|
100
|
-
client: 'Default',
|
|
101
|
-
ssr: 'Default',
|
|
102
|
-
},
|
|
103
|
-
{
|
|
104
|
-
path: '/chat',
|
|
105
|
-
client: 'Default',
|
|
106
|
-
ssr: 'Default',
|
|
107
|
-
},
|
|
98
|
+
{ path: '/blog', client: 'Default', ssr: 'Default' },
|
|
99
|
+
{ path: '/chat', client: 'Default', ssr: 'Default' },
|
|
108
100
|
],
|
|
109
101
|
dists: [
|
|
110
102
|
{
|
|
@@ -121,10 +113,7 @@ const DefaultConf = /**/ {
|
|
|
121
113
|
import_name_build: '/dist/sortablejs/sortable.complete.esm.js',
|
|
122
114
|
},
|
|
123
115
|
{ folder: './node_modules/validator', public_folder: '/dist/validator' },
|
|
124
|
-
{
|
|
125
|
-
folder: './node_modules/easymde/dist',
|
|
126
|
-
public_folder: '/dist/easymde',
|
|
127
|
-
},
|
|
116
|
+
{ folder: './node_modules/easymde/dist', public_folder: '/dist/easymde' },
|
|
128
117
|
{
|
|
129
118
|
folder: './node_modules/marked/lib',
|
|
130
119
|
public_folder: '/dist/marked',
|
|
@@ -157,7 +146,6 @@ const DefaultConf = /**/ {
|
|
|
157
146
|
import_name: 'dexie',
|
|
158
147
|
import_name_build: '/dist/dexie/dexie.mjs',
|
|
159
148
|
},
|
|
160
|
-
|
|
161
149
|
{ folder: './node_modules/peerjs/dist', public_folder: '/dist/peerjs' },
|
|
162
150
|
],
|
|
163
151
|
services: ['default', 'core', 'user', 'test', 'file', 'document'],
|
|
@@ -201,40 +189,26 @@ const DefaultConf = /**/ {
|
|
|
201
189
|
proxy: [80, 443],
|
|
202
190
|
db: {
|
|
203
191
|
provider: 'env:DB_PROVIDER:mongoose',
|
|
204
|
-
host: 'env:DB_HOST:mongodb://
|
|
192
|
+
host: 'env:DB_HOST:mongodb://127.0.0.1:27017',
|
|
205
193
|
name: 'env:DB_NAME:default',
|
|
206
194
|
replicaSet: 'env:DB_REPLICA_SET:rs0',
|
|
195
|
+
authSource: 'env:DB_AUTH_SOURCE:admin',
|
|
196
|
+
user: 'env:DB_USER:',
|
|
197
|
+
password: 'env:DB_PASSWORD:',
|
|
207
198
|
},
|
|
208
199
|
mailer: {
|
|
209
|
-
sender: {
|
|
210
|
-
email: 'env:MAILER_SENDER_EMAIL:noreply@default.net',
|
|
211
|
-
name: 'env:MAILER_SENDER_NAME:Default',
|
|
212
|
-
},
|
|
200
|
+
sender: { email: 'env:MAILER_SENDER_EMAIL:noreply@default.net', name: 'env:MAILER_SENDER_NAME:Default' },
|
|
213
201
|
transport: {
|
|
214
202
|
host: 'env:SMTP_HOST:smtp.default.com',
|
|
215
203
|
port: 'env:SMTP_PORT:int:465',
|
|
216
204
|
secure: 'env:SMTP_SECURE:bool:true',
|
|
217
|
-
auth: {
|
|
218
|
-
user: 'env:SMTP_AUTH_USER:',
|
|
219
|
-
pass: 'env:SMTP_AUTH_PASS:',
|
|
220
|
-
},
|
|
205
|
+
auth: { user: 'env:SMTP_AUTH_USER:', pass: 'env:SMTP_AUTH_PASS:' },
|
|
221
206
|
},
|
|
222
207
|
},
|
|
223
|
-
valkey: {
|
|
224
|
-
port: 'env:VALKEY_PORT:int:6379',
|
|
225
|
-
host: 'env:VALKEY_HOST:127.0.0.1',
|
|
226
|
-
},
|
|
227
|
-
},
|
|
228
|
-
},
|
|
229
|
-
'www.default.net': {
|
|
230
|
-
'/': {
|
|
231
|
-
client: null,
|
|
232
|
-
runtime: 'nodejs',
|
|
233
|
-
apis: [],
|
|
234
|
-
origins: [],
|
|
235
|
-
proxy: [80, 443],
|
|
208
|
+
valkey: { port: 'env:VALKEY_PORT:int:6379', host: 'env:VALKEY_HOST:127.0.0.1' },
|
|
236
209
|
},
|
|
237
210
|
},
|
|
211
|
+
'www.default.net': { '/': { client: null, runtime: 'nodejs', apis: [], origins: [], proxy: [80, 443] } },
|
|
238
212
|
},
|
|
239
213
|
cron: {
|
|
240
214
|
records: {
|
|
@@ -23,14 +23,14 @@ spec:
|
|
|
23
23
|
spec:
|
|
24
24
|
containers:
|
|
25
25
|
- name: dd-cron-backup
|
|
26
|
-
image: underpost/underpost-engine:v3.2.
|
|
26
|
+
image: underpost/underpost-engine:v3.2.14
|
|
27
27
|
command:
|
|
28
28
|
- /bin/sh
|
|
29
29
|
- -c
|
|
30
30
|
- >
|
|
31
31
|
cd /home/dd/engine &&
|
|
32
32
|
node bin env dd-cron production &&
|
|
33
|
-
node bin cron dd-lampp,dd-cyberia,dd-core,dd-test backup --git --kubeadm
|
|
33
|
+
node bin cron dd-lampp,dd-cyberia,dd-core,dd-prototype,dd-test backup --git --kubeadm
|
|
34
34
|
volumeMounts:
|
|
35
35
|
- mountPath: /home/dd/engine
|
|
36
36
|
name: underpost-cron-container-volume
|
|
@@ -17,7 +17,7 @@ spec:
|
|
|
17
17
|
spec:
|
|
18
18
|
containers:
|
|
19
19
|
- name: dd-default-development-blue
|
|
20
|
-
image: underpost/underpost-engine:v3.2.
|
|
20
|
+
image: underpost/underpost-engine:v3.2.14
|
|
21
21
|
# resources:
|
|
22
22
|
# requests:
|
|
23
23
|
# memory: "124Ki"
|
|
@@ -98,7 +98,7 @@ spec:
|
|
|
98
98
|
spec:
|
|
99
99
|
containers:
|
|
100
100
|
- name: dd-default-development-green
|
|
101
|
-
image: underpost/underpost-engine:v3.2.
|
|
101
|
+
image: underpost/underpost-engine:v3.2.14
|
|
102
102
|
# resources:
|
|
103
103
|
# requests:
|
|
104
104
|
# memory: "124Ki"
|
package/package.json
CHANGED
|
@@ -2,12 +2,13 @@
|
|
|
2
2
|
"type": "module",
|
|
3
3
|
"main": "src/index.js",
|
|
4
4
|
"name": "underpost",
|
|
5
|
-
"version": "3.2.
|
|
5
|
+
"version": "3.2.14",
|
|
6
6
|
"description": "Underpost Platform — end-to-end CI/CD and application-delivery toolchain CLI. Covers bare metal, Kubernetes, K3s, kubeadm, LXD, container/image orchestration, secrets, databases, cron jobs, monitoring, SSH, runners, PWA + Workbox delivery, and release orchestration. Extensible via downstream CLIs.",
|
|
7
7
|
"scripts": {
|
|
8
8
|
"start": "node --max-old-space-size=8192 src/server",
|
|
9
9
|
"build": "node bin client",
|
|
10
10
|
"test": "NODE_ENV=test c8 mocha",
|
|
11
|
+
"test:monitor": "NODE_ENV=test c8 mocha test/deploy-monitor.test.js",
|
|
11
12
|
"dev": "NODE_ENV=development nodemon src/server",
|
|
12
13
|
"dev:container": "NODE_ENV=development node src/server",
|
|
13
14
|
"prod:container": "NODE_ENV=production node src/server",
|
|
@@ -45,18 +46,12 @@
|
|
|
45
46
|
"k3s",
|
|
46
47
|
"kubeadm",
|
|
47
48
|
"lxd",
|
|
48
|
-
"
|
|
49
|
+
"baremetal",
|
|
49
50
|
"container-orchestration",
|
|
50
51
|
"image-management",
|
|
51
52
|
"pwa",
|
|
52
53
|
"workbox",
|
|
53
|
-
"microservices"
|
|
54
|
-
"template",
|
|
55
|
-
"builder",
|
|
56
|
-
"engine",
|
|
57
|
-
"server",
|
|
58
|
-
"proxy",
|
|
59
|
-
"client"
|
|
54
|
+
"microservices"
|
|
60
55
|
],
|
|
61
56
|
"author": "https://github.com/underpostnet",
|
|
62
57
|
"license": "MIT",
|
|
@@ -78,12 +73,12 @@
|
|
|
78
73
|
"clipboardy": "^5.3.1",
|
|
79
74
|
"cloudinary": "^2.10.0",
|
|
80
75
|
"colors": "^1.4.0",
|
|
81
|
-
"commander": "^
|
|
76
|
+
"commander": "^15.0.0",
|
|
82
77
|
"compression": "^1.7.4",
|
|
83
78
|
"cookie-parser": "^1.4.7",
|
|
84
79
|
"cors": "^2.8.6",
|
|
85
80
|
"d3": "^7.9.0",
|
|
86
|
-
"dexie": "^4.
|
|
81
|
+
"dexie": "^4.4.3",
|
|
87
82
|
"dotenv": "^17.4.2",
|
|
88
83
|
"easymde": "^2.21.0",
|
|
89
84
|
"esbuild": "^0.28.0",
|
|
@@ -93,7 +88,7 @@
|
|
|
93
88
|
"express-rate-limit": "^8.5.2",
|
|
94
89
|
"express-slow-down": "^3.1.0",
|
|
95
90
|
"fast-json-stable-stringify": "^2.1.0",
|
|
96
|
-
"favicons": "^7.
|
|
91
|
+
"favicons": "^7.3.0",
|
|
97
92
|
"fs-extra": "^11.3.5",
|
|
98
93
|
"fullcalendar": "^6.1.15",
|
|
99
94
|
"helmet": "^8.2.0",
|
|
@@ -106,9 +101,9 @@
|
|
|
106
101
|
"mariadb": "^3.2.2",
|
|
107
102
|
"mocha": "^11.7.6",
|
|
108
103
|
"marked": "^18.0.4",
|
|
109
|
-
"mongoose": "^9.6.
|
|
104
|
+
"mongoose": "^9.6.3",
|
|
110
105
|
"morgan": "^1.10.0",
|
|
111
|
-
"nodemailer": "^8.0.
|
|
106
|
+
"nodemailer": "^8.0.10",
|
|
112
107
|
"nodemon": "^3.0.1",
|
|
113
108
|
"peer": "^1.0.2",
|
|
114
109
|
"peerjs": "^1.5.5",
|
package/src/cli/deploy.js
CHANGED
|
@@ -20,7 +20,6 @@ import { loggerFactory } from '../server/logger.js';
|
|
|
20
20
|
import { shellExec } from '../server/process.js';
|
|
21
21
|
import fs from 'fs-extra';
|
|
22
22
|
import dotenv from 'dotenv';
|
|
23
|
-
import { timer } from '../client/components/core/CommonJs.js';
|
|
24
23
|
import os from 'node:os';
|
|
25
24
|
import Underpost from '../index.js';
|
|
26
25
|
|
|
@@ -827,59 +826,6 @@ EOF`);
|
|
|
827
826
|
}
|
|
828
827
|
}
|
|
829
828
|
},
|
|
830
|
-
/**
|
|
831
|
-
* Checks the status of a deployment.
|
|
832
|
-
* @param {string} deployId - Deployment ID for which the status is being checked.
|
|
833
|
-
* @param {string} env - Environment for which the status is being checked.
|
|
834
|
-
* @param {string} traffic - Current traffic status for the deployment.
|
|
835
|
-
* @param {Array<string>} ignoresNames - List of pod names to ignore.
|
|
836
|
-
* @param {string} [namespace='default'] - Kubernetes namespace for the deployment.
|
|
837
|
-
* @returns {object} - Object containing the status of the deployment.
|
|
838
|
-
* @memberof UnderpostDeploy
|
|
839
|
-
*/
|
|
840
|
-
async checkDeploymentReadyStatus(deployId, env, traffic, ignoresNames = [], namespace = 'default') {
|
|
841
|
-
const pods = Underpost.kubectl.get(`${deployId}-${env}-${traffic}`, 'pods', namespace);
|
|
842
|
-
const readyPods = [];
|
|
843
|
-
const notReadyPods = [];
|
|
844
|
-
|
|
845
|
-
// Readiness signal: the pod's Kubernetes `Ready` condition driven by the
|
|
846
|
-
// container's readinessProbe (TCP socket, HTTP get, or exec). Set by kubelet
|
|
847
|
-
// when the probe passes. A failed or crashing runtime never becomes Ready —
|
|
848
|
-
// kubelet surfaces CrashLoopBackOff and this gate stays closed.
|
|
849
|
-
for (const pod of pods) {
|
|
850
|
-
const { NAME } = pod;
|
|
851
|
-
if (ignoresNames && ignoresNames.find((t) => NAME.trim().toLowerCase().match(t.trim().toLowerCase()))) continue;
|
|
852
|
-
|
|
853
|
-
let podJson = null;
|
|
854
|
-
try {
|
|
855
|
-
// Pod may not exist yet (between deployment apply and pod
|
|
856
|
-
// scheduling). silentOnError lets the monitor loop continue
|
|
857
|
-
// instead of aborting on the transient NotFound exit.
|
|
858
|
-
const raw = shellExec(`sudo kubectl get pod ${NAME} -n ${namespace} -o json`, {
|
|
859
|
-
silent: true,
|
|
860
|
-
disableLog: true,
|
|
861
|
-
stdout: true,
|
|
862
|
-
silentOnError: true,
|
|
863
|
-
});
|
|
864
|
-
podJson = raw ? JSON.parse(raw) : null;
|
|
865
|
-
} catch (_) {
|
|
866
|
-
podJson = null;
|
|
867
|
-
}
|
|
868
|
-
const conditions = podJson?.status?.conditions || [];
|
|
869
|
-
const readyCondition = conditions.find((c) => c.type === 'Ready');
|
|
870
|
-
const k8sReady = readyCondition?.status === 'True';
|
|
871
|
-
|
|
872
|
-
pod.out = JSON.stringify({ k8sReady, condition: readyCondition ?? null });
|
|
873
|
-
|
|
874
|
-
if (k8sReady) readyPods.push(pod);
|
|
875
|
-
else notReadyPods.push(pod);
|
|
876
|
-
}
|
|
877
|
-
return {
|
|
878
|
-
ready: pods.length > 0 && notReadyPods.length === 0,
|
|
879
|
-
notReadyPods,
|
|
880
|
-
readyPods,
|
|
881
|
-
};
|
|
882
|
-
},
|
|
883
829
|
/**
|
|
884
830
|
* Creates a Kubernetes Secret for a deployment (replaces configMap for secret data).
|
|
885
831
|
* Secrets are mounted as tmpfs (never written to node disk) and support RBAC restrictions.
|
|
@@ -1139,180 +1085,6 @@ spec:
|
|
|
1139
1085
|
env === 'production' &&
|
|
1140
1086
|
options.cert === true &&
|
|
1141
1087
|
(!options.certHosts || options.certHosts.split(',').includes(host)),
|
|
1142
|
-
/**
|
|
1143
|
-
* Monitors the ready status of a deployment.
|
|
1144
|
-
*
|
|
1145
|
-
* Ready signal:
|
|
1146
|
-
* The orchestrator gate is the Kubernetes pod Ready condition. When the
|
|
1147
|
-
* container's `readinessProbe` succeeds, kubelet flips
|
|
1148
|
-
* `status.conditions[Ready]` to True and `checkDeploymentReadyStatus`
|
|
1149
|
-
* returns the pod in `readyPods`. This is the only required signal — see
|
|
1150
|
-
* `src/client/public/nexodev/docs/references/Deploy custom instance to K8S.md`.
|
|
1151
|
-
*
|
|
1152
|
-
* Container-status:
|
|
1153
|
-
* `underpost config get container-status` is read from each pod for both
|
|
1154
|
-
* the display column and as a second ready gate alongside the K8S Ready
|
|
1155
|
-
* condition. Both must be satisfied before the monitor exits:
|
|
1156
|
-
* 1. K8S readinessProbe (TCP socket) — ensures the port is bound.
|
|
1157
|
-
* 2. container-status == `<deploy>-<env>-running-deployment` — ensures
|
|
1158
|
-
* the application has completed its own startup sequence.
|
|
1159
|
-
* Early-abort on `error` container-status remains in effect.
|
|
1160
|
-
*
|
|
1161
|
-
* @param {string} deployId - Deployment ID for which the ready status is being monitored.
|
|
1162
|
-
* @param {string} env - Environment for which the ready status is being monitored.
|
|
1163
|
-
* @param {string} targetTraffic - Target traffic status for the deployment.
|
|
1164
|
-
* @param {Array<string>} ignorePods - List of pod names to ignore.
|
|
1165
|
-
* @param {string} [namespace='default'] - Kubernetes namespace for the deployment.
|
|
1166
|
-
* @returns {object} - Object containing the ready status of the deployment.
|
|
1167
|
-
* @memberof UnderpostDeploy
|
|
1168
|
-
*/
|
|
1169
|
-
async monitorReadyRunner(deployId, env, targetTraffic, ignorePods = [], namespace = 'default') {
|
|
1170
|
-
const delayMs = 1000;
|
|
1171
|
-
const maxIterations = 3000;
|
|
1172
|
-
const deploymentId = `${deployId}-${env}-${targetTraffic}`;
|
|
1173
|
-
const expectedContainerStatus = `${deployId}-${env}-running-deployment`;
|
|
1174
|
-
const tag = `[${deploymentId}]`;
|
|
1175
|
-
const containerStatusDefault = 'waiting for status';
|
|
1176
|
-
|
|
1177
|
-
logger.info('Deployment init', { deployId, env, targetTraffic, namespace });
|
|
1178
|
-
|
|
1179
|
-
// Per-pod cache of last-known container-status (persists across retries)
|
|
1180
|
-
const podStatusCache = new Map();
|
|
1181
|
-
|
|
1182
|
-
const readContainerStatus = (podName) => {
|
|
1183
|
-
try {
|
|
1184
|
-
const raw = shellExec(
|
|
1185
|
-
`sudo kubectl exec ${podName} -n ${namespace} -- sh -c 'underpost config get container-status --plain'`,
|
|
1186
|
-
{ silent: true, disableLog: true, stdout: true, silentOnError: true },
|
|
1187
|
-
);
|
|
1188
|
-
const val = raw ? raw.toString().trim() : '';
|
|
1189
|
-
return val && val !== 'undefined' ? val : containerStatusDefault;
|
|
1190
|
-
} catch (_) {
|
|
1191
|
-
// exec failed (e.g. pod not yet running) — preserve last known value
|
|
1192
|
-
return podStatusCache.get(podName) || containerStatusDefault;
|
|
1193
|
-
}
|
|
1194
|
-
};
|
|
1195
|
-
|
|
1196
|
-
for (let i = 0; i < maxIterations; i++) {
|
|
1197
|
-
const result = await Underpost.deploy.checkDeploymentReadyStatus(
|
|
1198
|
-
deployId,
|
|
1199
|
-
env,
|
|
1200
|
-
targetTraffic,
|
|
1201
|
-
ignorePods,
|
|
1202
|
-
namespace,
|
|
1203
|
-
);
|
|
1204
|
-
|
|
1205
|
-
const allPods = [...result.readyPods, ...result.notReadyPods];
|
|
1206
|
-
|
|
1207
|
-
// Update cache with latest status for each pod (informational + error gate)
|
|
1208
|
-
for (const pod of allPods) {
|
|
1209
|
-
if (!pod?.NAME) continue;
|
|
1210
|
-
const status = readContainerStatus(pod.NAME);
|
|
1211
|
-
if (status === 'error') throw new Error(`Pod ${pod.NAME} has error status`);
|
|
1212
|
-
podStatusCache.set(pod.NAME, status);
|
|
1213
|
-
}
|
|
1214
|
-
|
|
1215
|
-
const allPodsK8sReady = allPods.length > 0 && result.notReadyPods.length === 0;
|
|
1216
|
-
|
|
1217
|
-
const allPodsStatusReady =
|
|
1218
|
-
allPods.length > 0 && allPods.every((pod) => podStatusCache.get(pod.NAME) === expectedContainerStatus);
|
|
1219
|
-
|
|
1220
|
-
// Print snapshot for every pod — annotate when container-status hasn't caught
|
|
1221
|
-
// up to the K8S Ready condition yet.
|
|
1222
|
-
for (const pod of allPods) {
|
|
1223
|
-
const status = podStatusCache.get(pod.NAME) || containerStatusDefault;
|
|
1224
|
-
const podStatus = pod.STATUS || 'Unknown';
|
|
1225
|
-
const statusMatchesExpected = status === expectedContainerStatus;
|
|
1226
|
-
const statusDisplay = statusMatchesExpected ? status : `${status} (pending)`;
|
|
1227
|
-
|
|
1228
|
-
console.log(
|
|
1229
|
-
'Target pod:',
|
|
1230
|
-
pod.NAME[pod.NAME.includes('green') ? 'bgGreen' : 'bgBlue'].bold.black,
|
|
1231
|
-
'| Pod status:',
|
|
1232
|
-
podStatus.bold.yellow,
|
|
1233
|
-
'| Runtime status:',
|
|
1234
|
-
statusDisplay.bold.cyan,
|
|
1235
|
-
);
|
|
1236
|
-
}
|
|
1237
|
-
|
|
1238
|
-
// Both K8S readinessProbe AND container-status must be satisfied before
|
|
1239
|
-
// declaring the deployment ready. The TCP probe ensures the port is bound;
|
|
1240
|
-
// container-status == running-deployment ensures the application has
|
|
1241
|
-
// completed its own startup sequence so traffic is not switched prematurely.
|
|
1242
|
-
if (allPodsK8sReady && allPodsStatusReady) {
|
|
1243
|
-
logger.info(`${tag} | All pods Ready (K8S readinessProbe satisfied)`);
|
|
1244
|
-
return result;
|
|
1245
|
-
}
|
|
1246
|
-
|
|
1247
|
-
await timer(delayMs);
|
|
1248
|
-
|
|
1249
|
-
if ((i + 1) % 10 === 0) {
|
|
1250
|
-
logger.info(`${tag} | In progress... iteration ${i + 1}`);
|
|
1251
|
-
}
|
|
1252
|
-
}
|
|
1253
|
-
|
|
1254
|
-
logger.error(`${tag} | Deployment timeout after ${maxIterations} iterations`);
|
|
1255
|
-
throw new Error(
|
|
1256
|
-
`monitorReadyRunner timeout: ${deploymentId} did not become Ready within ${maxIterations}*${delayMs}ms`,
|
|
1257
|
-
);
|
|
1258
|
-
},
|
|
1259
|
-
|
|
1260
|
-
/**
|
|
1261
|
-
* Retrieves the currently loaded images in the Kubernetes cluster.
|
|
1262
|
-
* @param {string} [node='kind-worker'] - Node name to check for loaded images.
|
|
1263
|
-
* @param {object} options - Options for the image retrieval.
|
|
1264
|
-
* @param {boolean} options.spec - Whether to retrieve images from the pod specifications.
|
|
1265
|
-
* @param {string} options.namespace - Kubernetes namespace to filter pods.
|
|
1266
|
-
* @returns {Array<object>} - Array of objects containing pod names and their corresponding images.
|
|
1267
|
-
* @memberof UnderpostDeploy
|
|
1268
|
-
*/
|
|
1269
|
-
getCurrentLoadedImages(node = 'kind-worker', options = { spec: false, namespace: '' }) {
|
|
1270
|
-
if (options.spec) {
|
|
1271
|
-
const raw = shellExec(
|
|
1272
|
-
`kubectl get pods ${options.namespace ? `--namespace ${options.namespace}` : `--all-namespaces`} -o=jsonpath='{range .items[*]}{"\\n"}{.metadata.namespace}{"/"}{.metadata.name}{":\\t"}{range .spec.containers[*]}{.image}{", "}{end}{end}'`,
|
|
1273
|
-
{
|
|
1274
|
-
stdout: true,
|
|
1275
|
-
silent: true,
|
|
1276
|
-
},
|
|
1277
|
-
);
|
|
1278
|
-
return raw
|
|
1279
|
-
.split(`\n`)
|
|
1280
|
-
.map((lines) => ({
|
|
1281
|
-
pod: lines.split('\t')[0].replaceAll(':', '').trim(),
|
|
1282
|
-
image: lines.split('\t')[1] ? lines.split('\t')[1].replaceAll(',', '').trim() : null,
|
|
1283
|
-
}))
|
|
1284
|
-
.filter((o) => o.image);
|
|
1285
|
-
}
|
|
1286
|
-
const raw = shellExec(node === 'kind-worker' ? `docker exec -i ${node} crictl images` : `crictl images`, {
|
|
1287
|
-
stdout: true,
|
|
1288
|
-
silent: true,
|
|
1289
|
-
});
|
|
1290
|
-
|
|
1291
|
-
const heads = raw
|
|
1292
|
-
.split(`\n`)[0]
|
|
1293
|
-
.split(' ')
|
|
1294
|
-
.filter((_r) => _r.trim());
|
|
1295
|
-
|
|
1296
|
-
const pods = raw
|
|
1297
|
-
.split(`\n`)
|
|
1298
|
-
.filter((r) => !r.match('IMAGE'))
|
|
1299
|
-
.map((r) => r.split(' ').filter((_r) => _r.trim()));
|
|
1300
|
-
|
|
1301
|
-
const result = [];
|
|
1302
|
-
|
|
1303
|
-
for (const row of pods) {
|
|
1304
|
-
if (row.length === 0) continue;
|
|
1305
|
-
const pod = {};
|
|
1306
|
-
let index = -1;
|
|
1307
|
-
for (const head of heads) {
|
|
1308
|
-
if (head in pod) continue;
|
|
1309
|
-
index++;
|
|
1310
|
-
pod[head] = row[index];
|
|
1311
|
-
}
|
|
1312
|
-
result.push(pod);
|
|
1313
|
-
}
|
|
1314
|
-
return result;
|
|
1315
|
-
},
|
|
1316
1088
|
|
|
1317
1089
|
/**
|
|
1318
1090
|
* Predefined resource templates for Kubernetes deployments.
|
package/src/cli/image.js
CHANGED
|
@@ -122,6 +122,63 @@ class UnderpostImage {
|
|
|
122
122
|
else if (kubeadm === true) shellExec(`sudo ctr -n k8s.io images import ${tarFile}`);
|
|
123
123
|
else if (k3s === true) shellExec(`sudo k3s ctr images import ${tarFile}`);
|
|
124
124
|
},
|
|
125
|
+
/**
|
|
126
|
+
* @method getCurrentLoaded
|
|
127
|
+
* @description Retrieves the currently loaded images in the Kubernetes cluster.
|
|
128
|
+
* @param {string} [node='kind-worker'] - Node name to check for loaded images.
|
|
129
|
+
* @param {object} options - Options for the image retrieval.
|
|
130
|
+
* @param {boolean} options.spec - Whether to retrieve images from the pod specifications.
|
|
131
|
+
* @param {string} options.namespace - Kubernetes namespace to filter pods.
|
|
132
|
+
* @returns {Array<object>} - Array of objects containing pod names and their corresponding images.
|
|
133
|
+
* @memberof UnderpostImage
|
|
134
|
+
*/
|
|
135
|
+
getCurrentLoaded(node = 'kind-worker', options = { spec: false, namespace: '' }) {
|
|
136
|
+
if (options.spec) {
|
|
137
|
+
const raw = shellExec(
|
|
138
|
+
`kubectl get pods ${options.namespace ? `--namespace ${options.namespace}` : `--all-namespaces`} -o=jsonpath='{range .items[*]}{"\\n"}{.metadata.namespace}{"/"}{.metadata.name}{":\\t"}{range .spec.containers[*]}{.image}{", "}{end}{end}'`,
|
|
139
|
+
{
|
|
140
|
+
stdout: true,
|
|
141
|
+
silent: true,
|
|
142
|
+
},
|
|
143
|
+
);
|
|
144
|
+
return raw
|
|
145
|
+
.split(`\n`)
|
|
146
|
+
.map((lines) => ({
|
|
147
|
+
pod: lines.split('\t')[0].replaceAll(':', '').trim(),
|
|
148
|
+
image: lines.split('\t')[1] ? lines.split('\t')[1].replaceAll(',', '').trim() : null,
|
|
149
|
+
}))
|
|
150
|
+
.filter((o) => o.image);
|
|
151
|
+
}
|
|
152
|
+
const raw = shellExec(node === 'kind-worker' ? `docker exec -i ${node} crictl images` : `crictl images`, {
|
|
153
|
+
stdout: true,
|
|
154
|
+
silent: true,
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
const heads = raw
|
|
158
|
+
.split(`\n`)[0]
|
|
159
|
+
.split(' ')
|
|
160
|
+
.filter((_r) => _r.trim());
|
|
161
|
+
|
|
162
|
+
const pods = raw
|
|
163
|
+
.split(`\n`)
|
|
164
|
+
.filter((r) => !r.match('IMAGE'))
|
|
165
|
+
.map((r) => r.split(' ').filter((_r) => _r.trim()));
|
|
166
|
+
|
|
167
|
+
const result = [];
|
|
168
|
+
|
|
169
|
+
for (const row of pods) {
|
|
170
|
+
if (row.length === 0) continue;
|
|
171
|
+
const pod = {};
|
|
172
|
+
let index = -1;
|
|
173
|
+
for (const head of heads) {
|
|
174
|
+
if (head in pod) continue;
|
|
175
|
+
index++;
|
|
176
|
+
pod[head] = row[index];
|
|
177
|
+
}
|
|
178
|
+
result.push(pod);
|
|
179
|
+
}
|
|
180
|
+
return result;
|
|
181
|
+
},
|
|
125
182
|
/**
|
|
126
183
|
* @method list
|
|
127
184
|
* @description Lists currently loaded Docker images in the specified Kubernetes cluster node.
|
|
@@ -139,10 +196,7 @@ class UnderpostImage {
|
|
|
139
196
|
list(options = { nodeName: '', namespace: '', spec: false, log: false, k3s: false, kubeadm: false, kind: false }) {
|
|
140
197
|
if ((options.kubeadm === true || options.k3s === true) && !options.nodeName)
|
|
141
198
|
options.nodeName = shellExec('echo $HOSTNAME', { stdout: true, silent: true }).trim();
|
|
142
|
-
const list = Underpost.
|
|
143
|
-
options.nodeName ? options.nodeName : 'kind-worker',
|
|
144
|
-
options,
|
|
145
|
-
);
|
|
199
|
+
const list = Underpost.image.getCurrentLoaded(options.nodeName ? options.nodeName : 'kind-worker', options);
|
|
146
200
|
if (options.log) console.table(list);
|
|
147
201
|
return list;
|
|
148
202
|
},
|