underpost 2.89.35 → 2.89.44

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.
Files changed (44) hide show
  1. package/README.md +4 -2
  2. package/bin/deploy.js +22 -15
  3. package/cli.md +23 -2
  4. package/manifests/deployment/dd-default-development/deployment.yaml +2 -2
  5. package/manifests/deployment/dd-test-development/deployment.yaml +10 -6
  6. package/manifests/deployment/dd-test-development/proxy.yaml +2 -0
  7. package/manifests/deployment/kafka/deployment.yaml +0 -2
  8. package/manifests/deployment/spark/spark-pi-py.yaml +0 -1
  9. package/manifests/deployment/tensorflow/tf-gpu-test.yaml +0 -2
  10. package/manifests/envoy-service-nodeport.yaml +0 -1
  11. package/manifests/kubeadm-calico-config.yaml +10 -115
  12. package/manifests/letsencrypt-prod.yaml +0 -1
  13. package/manifests/mariadb/statefulset.yaml +1 -1
  14. package/manifests/mongodb/statefulset.yaml +11 -11
  15. package/manifests/mongodb-4.4/service-deployment.yaml +1 -3
  16. package/manifests/mysql/pv-pvc.yaml +1 -1
  17. package/manifests/mysql/statefulset.yaml +1 -1
  18. package/manifests/valkey/service.yaml +0 -1
  19. package/manifests/valkey/statefulset.yaml +2 -3
  20. package/package.json +1 -1
  21. package/scripts/device-scan.sh +43 -21
  22. package/scripts/rpmfusion-ffmpeg-setup.sh +1 -0
  23. package/src/api/user/user.model.js +10 -1
  24. package/src/cli/cluster.js +51 -26
  25. package/src/cli/deploy.js +73 -39
  26. package/src/cli/index.js +22 -1
  27. package/src/cli/monitor.js +9 -5
  28. package/src/cli/repository.js +1 -1
  29. package/src/cli/run.js +38 -21
  30. package/src/client/components/core/Logger.js +1 -1
  31. package/src/client/components/core/Modal.js +5 -0
  32. package/src/client/components/core/ObjectLayerEngineModal.js +334 -71
  33. package/src/client/components/core/ObjectLayerEngineViewer.js +170 -403
  34. package/src/client/components/core/Router.js +10 -1
  35. package/src/client/services/default/default.management.js +25 -5
  36. package/src/index.js +1 -1
  37. package/src/server/client-build.js +5 -4
  38. package/src/server/conf.js +1 -1
  39. package/src/server/start.js +1 -1
  40. package/manifests/kubelet-config.yaml +0 -65
  41. package/manifests/mongodb/backup-access.yaml +0 -16
  42. package/manifests/mongodb/backup-cronjob.yaml +0 -42
  43. package/manifests/mongodb/backup-pv-pvc.yaml +0 -22
  44. package/manifests/mongodb/configmap.yaml +0 -26
@@ -1,26 +1,42 @@
1
1
  #!/usr/bin/env bash
2
+ set -u -o pipefail
2
3
 
3
4
  for iface_path in /sys/class/net/*; do
5
+ [ -e "$iface_path" ] || continue
4
6
  name=$(basename "$iface_path")
5
- mac=$(< "$iface_path/address")
6
- ip=$(ip -4 addr show dev "$name" \
7
- | grep -oP '(?<=inet\s)\d+(\.\d+){3}' || echo "—")
8
- operstate=$(< "$iface_path/operstate")
9
- mtu=$(< "$iface_path/mtu")
10
-
11
- # Driver
12
- if [ -L "$iface_path/device/driver" ]; then
7
+
8
+ # MAC address
9
+ if [ -r "$iface_path/address" ]; then
10
+ mac=$(< "$iface_path/address")
11
+ else
12
+ mac="—"
13
+ fi
14
+
15
+ # IPv4: collect all IPv4 CIDRs, strip masks, join with commas (or show —)
16
+ ip_info=$(ip -4 -o addr show dev "$name" 2>/dev/null | awk '{print $4}')
17
+ if [ -n "$ip_info" ]; then
18
+ # Use word-splitting intentionally to iterate lines from ip_info
19
+ ip=$(printf "%s\n" $ip_info | awk -F/ '{print $1}' | paste -sd, -)
20
+ else
21
+ ip="—"
22
+ fi
23
+
24
+ # operstate and mtu
25
+ operstate=$(< "$iface_path/operstate" 2>/dev/null || echo "—")
26
+ mtu=$(< "$iface_path/mtu" 2>/dev/null || echo "—")
27
+
28
+ # Driver (if available)
29
+ if [ -e "$iface_path/device/driver" ]; then
13
30
  driver=$(basename "$(readlink -f "$iface_path/device/driver")")
14
31
  else
15
32
  driver="—"
16
33
  fi
17
34
 
18
- # Vendor device ID PCI
35
+ # PCI vendor:device (if available)
19
36
  pci_dev="$iface_path/device"
20
- if [ -f "$pci_dev/vendor" ] && [ -f "$pci_dev/device" ]; then
37
+ if [ -r "$pci_dev/vendor" ] && [ -r "$pci_dev/device" ]; then
21
38
  vendor_id=$(< "$pci_dev/vendor")
22
39
  device_id=$(< "$pci_dev/device")
23
- # parse 0x8086 to 8086, etc.
24
40
  vendor_id=${vendor_id#0x}
25
41
  device_id=${device_id#0x}
26
42
  pci="${vendor_id}:${device_id}"
@@ -28,16 +44,22 @@ for iface_path in /sys/class/net/*; do
28
44
  pci="—"
29
45
  fi
30
46
 
31
- # Link Speed
47
+ # Link speed: only append unit if numeric
32
48
  speed=$(cat "$iface_path/speed" 2>/dev/null || echo "—")
49
+ if [[ "$speed" =~ ^[0-9]+$ ]]; then
50
+ speed_label="${speed} Mb/s"
51
+ else
52
+ speed_label="$speed"
53
+ fi
54
+
55
+ # Print formatted output
56
+ printf 'Interface: %s\n' "$name"
57
+ printf ' MAC: %s\n' "$mac"
58
+ printf ' IPv4: %s\n' "$ip"
59
+ printf ' State: %s\n' "$operstate"
60
+ printf ' MTU: %s\n' "$mtu"
61
+ printf ' Driver: %s\n' "$driver"
62
+ printf ' PCI Vendor:Device: %s\n' "$pci"
63
+ printf ' Link Speed: %s\n\n' "$speed_label"
33
64
 
34
- echo "Interface: $name"
35
- echo " MAC: $mac"
36
- echo " IPv4: $ip"
37
- echo " State: $operstate"
38
- echo " MTU: $mtu"
39
- echo " Driver: $driver"
40
- echo " PCI Vendor:Device ID: $pci"
41
- echo " Link Speed: ${speed}Mb/s"
42
- echo
43
65
  done
@@ -32,6 +32,7 @@ echo "6) Try to install audio helper packages that sometimes block ffmpeg (ladsp
32
32
  # These may be provided by CRB/EPEL or other compatible repos
33
33
  dnf -y install ladspa || echo "ladspa not available from enabled repos (will try later)"
34
34
  dnf -y install rubberband || echo "rubberband not available from enabled repos (will try later)"
35
+ dnf -y install libwebp-tools || echo "libwebp-tools not available from enabled repos (will try later)"
35
36
 
36
37
  echo "7) Try installing ffmpeg (several fallbacks tried)"
37
38
  if dnf -y install ffmpeg ffmpeg-devel --allowerasing; then
@@ -73,7 +73,16 @@ const ProviderSchema = UserSchema;
73
73
  const UserDto = {
74
74
  select: {
75
75
  get: () => {
76
- return { _id: 1, username: 1, email: 1, role: 1, emailConfirmed: 1, profileImageId: 1 };
76
+ return {
77
+ _id: 1,
78
+ username: 1,
79
+ email: 1,
80
+ role: 1,
81
+ emailConfirmed: 1,
82
+ profileImageId: 1,
83
+ createdAt: 1,
84
+ updatedAt: 1,
85
+ };
77
86
  },
78
87
  getAll: () => {
79
88
  return { _id: 1 };
@@ -44,7 +44,8 @@ class UnderpostCluster {
44
44
  * @param {boolean} [options.listPods=false] - List Kubernetes pods.
45
45
  * @param {boolean} [options.reset=false] - Perform a comprehensive reset of Kubernetes and container environments.
46
46
  * @param {boolean} [options.dev=false] - Run in development mode (adjusts paths).
47
- * @param {string} [options.nsUse=''] - Set the current kubectl namespace.
47
+ * @param {string} [options.nsUse=''] - Set the current kubectl namespace (creates namespace if it doesn't exist).
48
+ * @param {string} [options.namespace='default'] - Kubernetes namespace for cluster operations.
48
49
  * @param {boolean} [options.infoCapacity=false] - Display resource capacity information for the cluster.
49
50
  * @param {boolean} [options.infoCapacityPod=false] - Display resource capacity information for pods.
50
51
  * @param {boolean} [options.pullImage=false] - Pull necessary Docker images before deployment.
@@ -79,6 +80,7 @@ class UnderpostCluster {
79
80
  reset: false,
80
81
  dev: false,
81
82
  nsUse: '',
83
+ namespace: 'default',
82
84
  infoCapacity: false,
83
85
  infoCapacityPod: false,
84
86
  pullImage: false,
@@ -116,8 +118,26 @@ class UnderpostCluster {
116
118
  if (options.infoCapacity === true)
117
119
  return logger.info('', UnderpostCluster.API.getResourcesCapacity(options.kubeadm || options.k3s)); // Adjust for k3s
118
120
  if (options.listPods === true) return console.table(UnderpostDeploy.API.get(podName ?? undefined));
121
+ // Set default namespace if not specified
122
+ if (!options.namespace) options.namespace = 'default';
123
+
119
124
  if (options.nsUse && typeof options.nsUse === 'string') {
125
+ // Verify if namespace exists, create if not
126
+ const namespaceExists = shellExec(`kubectl get namespace ${options.nsUse} --ignore-not-found -o name`, {
127
+ stdout: true,
128
+ silent: true,
129
+ }).trim();
130
+
131
+ if (!namespaceExists) {
132
+ logger.info(`Namespace '${options.nsUse}' does not exist. Creating it...`);
133
+ shellExec(`kubectl create namespace ${options.nsUse}`);
134
+ logger.info(`Namespace '${options.nsUse}' created successfully.`);
135
+ } else {
136
+ logger.info(`Namespace '${options.nsUse}' already exists.`);
137
+ }
138
+
120
139
  shellExec(`kubectl config set-context --current --namespace=${options.nsUse}`);
140
+ logger.info(`Context switched to namespace: ${options.nsUse}`);
121
141
  return;
122
142
  }
123
143
  if (options.info === true) {
@@ -242,14 +262,17 @@ class UnderpostCluster {
242
262
  shellExec(
243
263
  `sudo kubectl create -f https://raw.githubusercontent.com/projectcalico/calico/v3.29.3/manifests/tigera-operator.yaml`,
244
264
  );
245
- shellExec(`sudo kubectl apply -f ${underpostRoot}/manifests/kubeadm-calico-config.yaml`);
265
+ shellExec(
266
+ `sudo kubectl apply -f ${underpostRoot}/manifests/kubeadm-calico-config.yaml -n ${options.namespace}`,
267
+ );
268
+
246
269
  // Untaint control plane node to allow scheduling pods
247
270
  const nodeName = os.hostname();
248
271
  shellExec(`kubectl taint nodes ${nodeName} node-role.kubernetes.io/control-plane:NoSchedule-`);
249
272
  // Install local-path-provisioner for dynamic PVCs (optional but recommended)
250
273
  logger.info('Installing local-path-provisioner...');
251
274
  shellExec(
252
- `kubectl apply -f https://raw.githubusercontent.com/rancher/local-path-provisioner/master/deploy/local-path-storage.yaml`,
275
+ `kubectl apply -f https://raw.githubusercontent.com/rancher/local-path-provisioner/master/deploy/local-path-storage.yaml -n ${options.namespace}`,
253
276
  );
254
277
  } else {
255
278
  // Kind cluster initialization (if not using kubeadm or k3s)
@@ -286,13 +309,13 @@ class UnderpostCluster {
286
309
  }
287
310
 
288
311
  if (options.grafana === true) {
289
- shellExec(`kubectl delete deployment grafana --ignore-not-found`);
290
- shellExec(`kubectl apply -k ${underpostRoot}/manifests/grafana`);
312
+ shellExec(`kubectl delete deployment grafana -n ${options.namespace} --ignore-not-found`);
313
+ shellExec(`kubectl apply -k ${underpostRoot}/manifests/grafana -n ${options.namespace}`);
291
314
  const yaml = `${fs
292
315
  .readFileSync(`${underpostRoot}/manifests/grafana/deployment.yaml`, 'utf8')
293
316
  .replace('{{GF_SERVER_ROOT_URL}}', options.hosts.split(',')[0])}`;
294
317
  console.log(yaml);
295
- shellExec(`kubectl apply -f - <<EOF
318
+ shellExec(`kubectl apply -f - -n ${options.namespace} <<EOF
296
319
  ${yaml}
297
320
  EOF
298
321
  `);
@@ -311,7 +334,7 @@ EOF
311
334
  .join(',')}]`,
312
335
  )}`;
313
336
  console.log(yaml);
314
- shellExec(`kubectl apply -f - <<EOF
337
+ shellExec(`kubectl apply -f - -n ${options.namespace} <<EOF
315
338
  ${yaml}
316
339
  EOF
317
340
  `);
@@ -340,15 +363,15 @@ EOF
340
363
  // For kubeadm/k3s, ensure it's available for containerd
341
364
  shellExec(`sudo crictl pull valkey/valkey:latest`);
342
365
  }
343
- shellExec(`kubectl delete statefulset valkey-service --ignore-not-found`);
344
- shellExec(`kubectl apply -k ${underpostRoot}/manifests/valkey`);
366
+ shellExec(`kubectl delete statefulset valkey-service -n ${options.namespace} --ignore-not-found`);
367
+ shellExec(`kubectl apply -k ${underpostRoot}/manifests/valkey -n ${options.namespace}`);
345
368
  await UnderpostTest.API.statusMonitor('valkey-service', 'Running', 'pods', 1000, 60);
346
369
  }
347
370
  if (options.full === true || options.mariadb === true) {
348
371
  shellExec(
349
- `sudo kubectl create secret generic mariadb-secret --from-file=username=/home/dd/engine/engine-private/mariadb-username --from-file=password=/home/dd/engine/engine-private/mariadb-password --dry-run=client -o yaml | kubectl apply -f -`,
372
+ `sudo kubectl create secret generic mariadb-secret --from-file=username=/home/dd/engine/engine-private/mariadb-username --from-file=password=/home/dd/engine/engine-private/mariadb-password --dry-run=client -o yaml | kubectl apply -f - -n ${options.namespace}`,
350
373
  );
351
- shellExec(`kubectl delete statefulset mariadb-statefulset --ignore-not-found`);
374
+ shellExec(`kubectl delete statefulset mariadb-statefulset -n ${options.namespace} --ignore-not-found`);
352
375
 
353
376
  if (options.pullImage === true) {
354
377
  // shellExec(`sudo podman pull mariadb:latest`);
@@ -360,17 +383,17 @@ EOF
360
383
  // For kubeadm/k3s, ensure it's available for containerd
361
384
  shellExec(`sudo crictl pull mariadb:latest`);
362
385
  }
363
- shellExec(`kubectl apply -f ${underpostRoot}/manifests/mariadb/storage-class.yaml`);
364
- shellExec(`kubectl apply -k ${underpostRoot}/manifests/mariadb`);
386
+ shellExec(`kubectl apply -f ${underpostRoot}/manifests/mariadb/storage-class.yaml -n ${options.namespace}`);
387
+ shellExec(`kubectl apply -k ${underpostRoot}/manifests/mariadb -n ${options.namespace}`);
365
388
  }
366
389
  if (options.full === true || options.mysql === true) {
367
390
  shellExec(
368
- `sudo kubectl create secret generic mysql-secret --from-file=username=/home/dd/engine/engine-private/mysql-username --from-file=password=/home/dd/engine/engine-private/mysql-password --dry-run=client -o yaml | kubectl apply -f -`,
391
+ `sudo kubectl create secret generic mysql-secret --from-file=username=/home/dd/engine/engine-private/mysql-username --from-file=password=/home/dd/engine/engine-private/mysql-password --dry-run=client -o yaml | kubectl apply -f - -n ${options.namespace}`,
369
392
  );
370
393
  shellExec(`sudo mkdir -p /mnt/data`);
371
394
  shellExec(`sudo chmod 777 /mnt/data`);
372
395
  shellExec(`sudo chown -R root:root /mnt/data`);
373
- shellExec(`kubectl apply -k ${underpostRoot}/manifests/mysql`);
396
+ shellExec(`kubectl apply -k ${underpostRoot}/manifests/mysql -n ${options.namespace}`);
374
397
  }
375
398
  if (options.full === true || options.postgresql === true) {
376
399
  if (options.pullImage === true) {
@@ -383,9 +406,9 @@ EOF
383
406
  shellExec(`sudo crictl pull postgres:latest`);
384
407
  }
385
408
  shellExec(
386
- `sudo kubectl create secret generic postgres-secret --from-file=password=/home/dd/engine/engine-private/postgresql-password --dry-run=client -o yaml | kubectl apply -f -`,
409
+ `sudo kubectl create secret generic postgres-secret --from-file=password=/home/dd/engine/engine-private/postgresql-password --dry-run=client -o yaml | kubectl apply -f - -n ${options.namespace}`,
387
410
  );
388
- shellExec(`kubectl apply -k ${underpostRoot}/manifests/postgresql`);
411
+ shellExec(`kubectl apply -k ${underpostRoot}/manifests/postgresql -n ${options.namespace}`);
389
412
  }
390
413
  if (options.mongodb4 === true) {
391
414
  if (options.pullImage === true) {
@@ -397,7 +420,7 @@ EOF
397
420
  // For kubeadm/k3s, ensure it's available for containerd
398
421
  shellExec(`sudo crictl pull mongo:4.4`);
399
422
  }
400
- shellExec(`kubectl apply -k ${underpostRoot}/manifests/mongodb-4.4`);
423
+ shellExec(`kubectl apply -k ${underpostRoot}/manifests/mongodb-4.4 -n ${options.namespace}`);
401
424
 
402
425
  const deploymentName = 'mongodb-deployment';
403
426
 
@@ -428,14 +451,14 @@ EOF
428
451
  shellExec(`sudo crictl pull mongo:latest`);
429
452
  }
430
453
  shellExec(
431
- `sudo kubectl create secret generic mongodb-keyfile --from-file=/home/dd/engine/engine-private/mongodb-keyfile --dry-run=client -o yaml | kubectl apply -f -`,
454
+ `sudo kubectl create secret generic mongodb-keyfile --from-file=/home/dd/engine/engine-private/mongodb-keyfile --dry-run=client -o yaml | kubectl apply -f - -n ${options.namespace}`,
432
455
  );
433
456
  shellExec(
434
- `sudo kubectl create secret generic mongodb-secret --from-file=username=/home/dd/engine/engine-private/mongodb-username --from-file=password=/home/dd/engine/engine-private/mongodb-password --dry-run=client -o yaml | kubectl apply -f -`,
457
+ `sudo kubectl create secret generic mongodb-secret --from-file=username=/home/dd/engine/engine-private/mongodb-username --from-file=password=/home/dd/engine/engine-private/mongodb-password --dry-run=client -o yaml | kubectl apply -f - -n ${options.namespace}`,
435
458
  );
436
- shellExec(`kubectl delete statefulset mongodb --ignore-not-found`);
437
- shellExec(`kubectl apply -f ${underpostRoot}/manifests/mongodb/storage-class.yaml`);
438
- shellExec(`kubectl apply -k ${underpostRoot}/manifests/mongodb`);
459
+ shellExec(`kubectl delete statefulset mongodb -n ${options.namespace} --ignore-not-found`);
460
+ shellExec(`kubectl apply -f ${underpostRoot}/manifests/mongodb/storage-class.yaml -n ${options.namespace}`);
461
+ shellExec(`kubectl apply -k ${underpostRoot}/manifests/mongodb -n ${options.namespace}`);
439
462
 
440
463
  const successInstance = await UnderpostTest.API.statusMonitor('mongodb-0', 'Running', 'pods', 1000, 60 * 10);
441
464
 
@@ -456,10 +479,12 @@ EOF
456
479
  }
457
480
 
458
481
  if (options.full === true || options.contour === true) {
459
- shellExec(`kubectl apply -f https://projectcontour.io/quickstart/contour.yaml`);
482
+ shellExec(`kubectl apply -f https://projectcontour.io/quickstart/contour.yaml -n ${options.namespace}`);
460
483
  if (options.kubeadm === true) {
461
484
  // Envoy service might need NodePort for kubeadm
462
- shellExec(`sudo kubectl apply -f ${underpostRoot}/manifests/envoy-service-nodeport.yaml`);
485
+ shellExec(
486
+ `sudo kubectl apply -f ${underpostRoot}/manifests/envoy-service-nodeport.yaml -n ${options.namespace}`,
487
+ );
463
488
  }
464
489
  // K3s has a built-in LoadBalancer (Klipper-lb) that can expose services,
465
490
  // so a specific NodePort service might not be needed or can be configured differently.
@@ -479,7 +504,7 @@ EOF
479
504
 
480
505
  const letsEncName = 'letsencrypt-prod';
481
506
  shellExec(`sudo kubectl delete ClusterIssuer ${letsEncName} --ignore-not-found`);
482
- shellExec(`sudo kubectl apply -f ${underpostRoot}/manifests/${letsEncName}.yaml`);
507
+ shellExec(`sudo kubectl apply -f ${underpostRoot}/manifests/${letsEncName}.yaml -n ${options.namespace}`);
483
508
  }
484
509
  },
485
510
 
package/src/cli/deploy.js CHANGED
@@ -129,10 +129,11 @@ class UnderpostDeploy {
129
129
  * @param {object} resources - Resource configuration for the deployment.
130
130
  * @param {number} replicas - Number of replicas for the deployment.
131
131
  * @param {string} image - Docker image for the deployment.
132
+ * @param {string} namespace - Kubernetes namespace for the deployment.
132
133
  * @returns {string} - YAML deployment configuration for the specified deployment.
133
134
  * @memberof UnderpostDeploy
134
135
  */
135
- deploymentYamlPartsFactory({ deployId, env, suffix, resources, replicas, image }) {
136
+ deploymentYamlPartsFactory({ deployId, env, suffix, resources, replicas, image, namespace }) {
136
137
  const packageJson = JSON.parse(fs.readFileSync('./package.json', 'utf8'));
137
138
  let volumes = [
138
139
  {
@@ -149,6 +150,7 @@ class UnderpostDeploy {
149
150
  kind: Deployment
150
151
  metadata:
151
152
  name: ${deployId}-${env}-${suffix}
153
+ namespace: ${namespace ? namespace : 'default'}
152
154
  labels:
153
155
  app: ${deployId}-${env}-${suffix}
154
156
  spec:
@@ -179,7 +181,7 @@ spec:
179
181
  npm install -g underpost &&
180
182
  underpost secret underpost --create-from-file /etc/config/.env.${env} &&
181
183
  underpost start --build --run ${deployId} ${env}
182
- ${UnderpostDeploy.API.volumeFactory(volumes)
184
+ ${UnderpostDeploy.API.volumeFactory(volumes.map((v) => ((v.version = `${deployId}-${env}-${suffix}`), v)))
183
185
  .render.split(`\n`)
184
186
  .map((l) => ' ' + l)
185
187
  .join(`\n`)}
@@ -188,6 +190,7 @@ apiVersion: v1
188
190
  kind: Service
189
191
  metadata:
190
192
  name: ${deployId}-${env}-${suffix}-service
193
+ namespace: ${namespace}
191
194
  spec:
192
195
  selector:
193
196
  app: ${deployId}-${env}-${suffix}
@@ -201,6 +204,7 @@ spec:
201
204
  * @param {object} options - Options for the manifest build process.
202
205
  * @param {string} options.replicas - Number of replicas for each deployment.
203
206
  * @param {string} options.image - Docker image for the deployment.
207
+ * @param {string} options.namespace - Kubernetes namespace for the deployment.
204
208
  * @returns {Promise<void>} - Promise that resolves when the manifest is built.
205
209
  * @memberof UnderpostDeploy
206
210
  */
@@ -208,6 +212,7 @@ spec:
208
212
  const resources = UnderpostDeploy.API.resourcesFactory();
209
213
  const replicas = options.replicas;
210
214
  const image = options.image;
215
+ if (!options.namespace) options.namespace = 'default';
211
216
 
212
217
  for (const _deployId of deployList.split(',')) {
213
218
  const deployId = _deployId.trim();
@@ -235,6 +240,7 @@ ${UnderpostDeploy.API.deploymentYamlPartsFactory({
235
240
  resources,
236
241
  replicas,
237
242
  image,
243
+ namespace: options.namespace,
238
244
  }).replace('{{ports}}', buildKindPorts(fromPort, toPort))}
239
245
  `;
240
246
  }
@@ -257,6 +263,7 @@ apiVersion: projectcontour.io/v1
257
263
  kind: HTTPProxy
258
264
  metadata:
259
265
  name: ${host}
266
+ namespace: ${options.namespace}
260
267
  spec:
261
268
  virtualhost:
262
269
  fqdn: ${host}${
@@ -316,16 +323,18 @@ spec:
316
323
  /**
317
324
  * Builds a Certificate resource for a host using cert-manager.
318
325
  * @param {string} host - Hostname for which the certificate is being built.
326
+ * @param {string} namespace - Kubernetes namespace for the certificate.
319
327
  * @returns {string} - Certificate resource YAML for the specified host.
320
328
  * @memberof UnderpostDeploy
321
329
  */
322
- buildCertManagerCertificate({ host }) {
330
+ buildCertManagerCertificate({ host, namespace }) {
323
331
  return `
324
332
  ---
325
333
  apiVersion: cert-manager.io/v1
326
334
  kind: Certificate
327
335
  metadata:
328
336
  name: ${host}
337
+ namespace: ${namespace}
329
338
  spec:
330
339
  commonName: ${host}
331
340
  dnsNames:
@@ -376,6 +385,7 @@ spec:
376
385
  * @param {boolean} options.status - Whether to display deployment status.
377
386
  * @param {boolean} options.etcHosts - Whether to display the /etc/hosts file.
378
387
  * @param {boolean} options.disableUpdateUnderpostConfig - Whether to disable Underpost config updates.
388
+ * @param {string} [options.namespace] - Kubernetes namespace for the deployment.
379
389
  * @returns {Promise<void>} - Promise that resolves when the deployment process is complete.
380
390
  * @memberof UnderpostDeploy
381
391
  */
@@ -404,6 +414,7 @@ spec:
404
414
  status: false,
405
415
  etcHosts: false,
406
416
  disableUpdateUnderpostConfig: false,
417
+ namespace: '',
407
418
  },
408
419
  ) {
409
420
  if (options.infoUtil === true)
@@ -437,15 +448,16 @@ kubectl wait --for=jsonpath='{.status.phase}'=Running pod/busybox1
437
448
  kubectl wait --for='jsonpath={.status.conditions[?(@.type=="Ready")].status}=True' pod/busybox1
438
449
  kubectl wait --for=delete pod/busybox1 --timeout=60s
439
450
 
440
- fqdn: <service>.<namespace>.<kind(svc/pod)>.<cluster-domain(cluster.local)>
441
451
  node bin run cluster-build
442
452
  node bin run template-deploy
443
453
  node bin run ssh-deploy (sync-)engine-core
444
454
  node bin run cluster --dev 'express,dd-test+dd-core'
445
455
  node bin run dd-container --dev
456
+ node bin run promote dd-default production
446
457
  node bin dockerfile-pull-base-images --dev --path 'image-path' --kind-load
447
458
  node bin/deploy update-default-conf <deploy-id>
448
459
 
460
+ fqdn: <service>.<namespace>.<kind(svc/pod)>.<cluster-domain(cluster.local)>
449
461
  kubectl run --rm -it test-dns --image=busybox:latest --restart=Never -- /bin/sh -c "
450
462
  nslookup kubernetes.default.svc.cluster.local;
451
463
  nslookup mongodb-service.default.svc.cluster.local;
@@ -467,10 +479,11 @@ docker login nvcr.io
467
479
  Username: $oauthtoken
468
480
  Password: <Your Key>
469
481
  `);
482
+ const namespace = options.namespace ? options.namespace : 'default';
470
483
  if (!deployList && options.certHosts) {
471
484
  for (const host of options.certHosts.split(',')) {
472
- shellExec(`sudo kubectl apply -f - <<EOF
473
- ${UnderpostDeploy.API.buildCertManagerCertificate({ host })}
485
+ shellExec(`sudo kubectl apply -f - -n ${namespace} <<EOF
486
+ ${UnderpostDeploy.API.buildCertManagerCertificate({ host, namespace })}
474
487
  EOF`);
475
488
  }
476
489
  return;
@@ -532,36 +545,44 @@ EOF`);
532
545
  continue;
533
546
  }
534
547
 
535
- if (!options.disableUpdateDeployment)
536
- for (const version of options.versions.split(',')) {
537
- shellExec(`sudo kubectl delete svc ${deployId}-${env}-${version}-service`);
538
- shellExec(`sudo kubectl delete deployment ${deployId}-${env}-${version}`);
539
- }
540
-
541
548
  const confServer = JSON.parse(fs.readFileSync(`./engine-private/conf/${deployId}/conf.server.json`, 'utf8'));
542
549
  const confVolume = fs.existsSync(`./engine-private/conf/${deployId}/conf.volume.json`)
543
550
  ? JSON.parse(fs.readFileSync(`./engine-private/conf/${deployId}/conf.volume.json`, 'utf8'))
544
551
  : [];
545
552
 
546
- if (!options.disableUpdateVolume) {
547
- for (const volume of confVolume) {
548
- shellExec(`kubectl delete pvc ${volume.claimName}`);
549
- shellExec(`kubectl delete pv ${volume.claimName.replace('pvc-', 'pv-')}`);
550
- shellExec(`kubectl apply -f - <<EOF
553
+ if (!options.disableUpdateDeployment)
554
+ for (const version of options.versions.split(',')) {
555
+ shellExec(
556
+ `sudo kubectl delete svc ${deployId}-${env}-${version}-service -n ${namespace} --ignore-not-found`,
557
+ );
558
+ shellExec(
559
+ `sudo kubectl delete deployment ${deployId}-${env}-${version} -n ${namespace} --ignore-not-found`,
560
+ );
561
+ if (!options.disableUpdateVolume) {
562
+ for (const volume of confVolume) {
563
+ const pvcId = `${volume.claimName}-${deployId}-${env}-${version}`;
564
+ const pvId = `${volume.claimName.replace('pvc-', 'pv-')}-${deployId}-${env}-${version}`;
565
+ const rootVolumeHostPath = `/home/dd/engine/volume/${pvId}`;
566
+ if (!fs.existsSync(rootVolumeHostPath)) fs.mkdirSync(rootVolumeHostPath, { recursive: true });
567
+ fs.copySync(volume.volumeMountPath, rootVolumeHostPath);
568
+ shellExec(`kubectl delete pvc ${pvcId} -n ${namespace} --ignore-not-found`);
569
+ shellExec(`kubectl delete pv ${pvId} --ignore-not-found`);
570
+ shellExec(`kubectl apply -f - -n ${namespace} <<EOF
551
571
  ${UnderpostDeploy.API.persistentVolumeFactory({
552
- hostPath: volume.volumeMountPath,
553
- pvcId: volume.claimName,
572
+ hostPath: rootVolumeHostPath,
573
+ pvcId,
554
574
  })}
555
575
  EOF
556
576
  `);
577
+ }
578
+ }
557
579
  }
558
- }
559
580
 
560
581
  for (const host of Object.keys(confServer)) {
561
582
  if (!options.disableUpdateProxy) {
562
- shellExec(`sudo kubectl delete HTTPProxy ${host}`);
583
+ shellExec(`sudo kubectl delete HTTPProxy ${host} -n ${namespace} --ignore-not-found`);
563
584
  if (UnderpostDeploy.API.isValidTLSContext({ host, env, options }))
564
- shellExec(`sudo kubectl delete Certificate ${host}`);
585
+ shellExec(`sudo kubectl delete Certificate ${host} -n ${namespace} --ignore-not-found`);
565
586
  }
566
587
  if (!options.remove) etcHosts.push(host);
567
588
  }
@@ -572,11 +593,13 @@ EOF
572
593
  : `manifests/deployment/${deployId}-${env}`;
573
594
 
574
595
  if (!options.remove) {
575
- if (!options.disableUpdateDeployment) shellExec(`sudo kubectl apply -f ./${manifestsPath}/deployment.yaml`);
576
- if (!options.disableUpdateProxy) shellExec(`sudo kubectl apply -f ./${manifestsPath}/proxy.yaml`);
596
+ if (!options.disableUpdateDeployment)
597
+ shellExec(`sudo kubectl apply -f ./${manifestsPath}/deployment.yaml -n ${namespace}`);
598
+ if (!options.disableUpdateProxy)
599
+ shellExec(`sudo kubectl apply -f ./${manifestsPath}/proxy.yaml -n ${namespace}`);
577
600
 
578
601
  if (UnderpostDeploy.API.isValidTLSContext({ host: Object.keys(confServer)[0], env, options }))
579
- shellExec(`sudo kubectl apply -f ./${manifestsPath}/secret.yaml`);
602
+ shellExec(`sudo kubectl apply -f ./${manifestsPath}/secret.yaml -n ${namespace}`);
580
603
  }
581
604
  }
582
605
  if (options.etcHosts === true) {
@@ -593,15 +616,19 @@ EOF
593
616
  * Retrieves information about a deployment.
594
617
  * @param {string} deployId - Deployment ID for which information is being retrieved.
595
618
  * @param {string} kindType - Type of Kubernetes resource to retrieve information for (e.g. 'pods').
619
+ * @param {string} namespace - Kubernetes namespace to retrieve information from.
596
620
  * @returns {Array<object>} - Array of objects containing information about the deployment.
597
621
  * @memberof UnderpostDeploy
598
622
  */
599
- get(deployId, kindType = 'pods') {
600
- const raw = shellExec(`sudo kubectl get ${kindType} --all-namespaces -o wide`, {
601
- stdout: true,
602
- disableLog: true,
603
- silent: true,
604
- });
623
+ get(deployId, kindType = 'pods', namespace = '') {
624
+ const raw = shellExec(
625
+ `sudo kubectl get ${kindType}${namespace ? ` -n ${namespace}` : ` --all-namespaces`} -o wide`,
626
+ {
627
+ stdout: true,
628
+ disableLog: true,
629
+ silent: true,
630
+ },
631
+ );
605
632
 
606
633
  const heads = raw
607
634
  .split(`\n`)[0]
@@ -715,12 +742,13 @@ EOF
715
742
  /**
716
743
  * Creates a configmap for a deployment.
717
744
  * @param {string} env - Environment for which the configmap is being created.
745
+ * @param {string} [namespace='default'] - Kubernetes namespace for the configmap.
718
746
  * @memberof UnderpostDeploy
719
747
  */
720
- configMap(env) {
721
- shellExec(`kubectl delete configmap underpost-config`);
748
+ configMap(env, namespace = 'default') {
749
+ shellExec(`kubectl delete configmap underpost-config -n ${namespace} --ignore-not-found`);
722
750
  shellExec(
723
- `kubectl create configmap underpost-config --from-file=/home/dd/engine/engine-private/conf/dd-cron/.env.${env}`,
751
+ `kubectl create configmap underpost-config --from-file=/home/dd/engine/engine-private/conf/dd-cron/.env.${env} --dry-run=client -o yaml | kubectl apply -f - -n ${namespace}`,
724
752
  );
725
753
  },
726
754
  /**
@@ -729,14 +757,15 @@ EOF
729
757
  * @param {string} env - Environment for which the traffic is being switched.
730
758
  * @param {string} targetTraffic - Target traffic status for the deployment.
731
759
  * @param {number} replicas - Number of replicas for the deployment.
760
+ * @param {string} [namespace='default'] - Kubernetes namespace for the deployment.
732
761
  * @memberof UnderpostDeploy
733
762
  */
734
- switchTraffic(deployId, env, targetTraffic, replicas = 1) {
763
+ switchTraffic(deployId, env, targetTraffic, replicas = 1, namespace = 'default') {
735
764
  UnderpostRootEnv.API.set(`${deployId}-${env}-traffic`, targetTraffic);
736
765
  shellExec(
737
- `node bin deploy --info-router --build-manifest --traffic ${targetTraffic} --replicas ${replicas} ${deployId} ${env}`,
766
+ `node bin deploy --info-router --build-manifest --traffic ${targetTraffic} --replicas ${replicas} --namespace ${namespace} ${deployId} ${env}`,
738
767
  );
739
- shellExec(`sudo kubectl apply -f ./engine-private/conf/${deployId}/build/${env}/proxy.yaml`);
768
+ shellExec(`sudo kubectl apply -f ./engine-private/conf/${deployId}/build/${env}/proxy.yaml -n ${namespace}`);
740
769
  },
741
770
 
742
771
  /**
@@ -760,6 +789,7 @@ EOF
760
789
  volumeType: 'Directory',
761
790
  claimName: null,
762
791
  configMap: null,
792
+ version: null,
763
793
  },
764
794
  ],
765
795
  ) {
@@ -768,7 +798,11 @@ EOF
768
798
  let _volumes = `
769
799
  volumes:`;
770
800
  volumes.map((volumeData) => {
771
- const { volumeName, volumeMountPath, volumeHostPath, volumeType, claimName, configMap } = volumeData;
801
+ let { volumeName, volumeMountPath, volumeHostPath, volumeType, claimName, configMap, version } = volumeData;
802
+ if (version) {
803
+ volumeName = `${volumeName}-${version}`;
804
+ claimName = claimName ? `${claimName}-${version}` : null;
805
+ }
772
806
  _volumeMounts += `
773
807
  - name: ${volumeName}
774
808
  mountPath: ${volumeMountPath}
@@ -899,7 +933,7 @@ ${renderHosts}`,
899
933
  getCurrentLoadedImages(node = 'kind-worker', specContainers = false) {
900
934
  if (specContainers) {
901
935
  const raw = shellExec(
902
- `kubectl get pods --all-namespaces -o=jsonpath='{range .items[*]}{"\\n"}{.metadata.name}{":\\t"}{range .spec.containers[*]}{.image}{", "}{end}{end}'`,
936
+ `kubectl get pods --all-namespaces -o=jsonpath='{range .items[*]}{"\\n"}{.metadata.namespace}{"/"}{.metadata.name}{":\\t"}{range .spec.containers[*]}{.image}{", "}{end}{end}'`,
903
937
  {
904
938
  stdout: true,
905
939
  silent: true,