cyberia 2.89.2 → 2.89.45

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 (64) hide show
  1. package/.env.development +2 -0
  2. package/.env.production +2 -0
  3. package/.env.test +2 -0
  4. package/.github/workflows/engine-cyberia.cd.yml +4 -0
  5. package/.github/workflows/release.cd.yml +2 -0
  6. package/bin/cyberia.js +10 -7
  7. package/bin/deploy.js +22 -15
  8. package/bin/index.js +10 -7
  9. package/cli.md +105 -54
  10. package/deployment.yaml +34 -6
  11. package/manifests/deployment/dd-default-development/deployment.yaml +2 -2
  12. package/manifests/deployment/dd-test-development/deployment.yaml +18 -6
  13. package/manifests/deployment/dd-test-development/proxy.yaml +2 -0
  14. package/manifests/deployment/kafka/deployment.yaml +0 -2
  15. package/manifests/deployment/spark/spark-pi-py.yaml +0 -1
  16. package/manifests/deployment/tensorflow/tf-gpu-test.yaml +0 -2
  17. package/manifests/envoy-service-nodeport.yaml +0 -1
  18. package/manifests/kubeadm-calico-config.yaml +10 -115
  19. package/manifests/letsencrypt-prod.yaml +0 -1
  20. package/manifests/mariadb/statefulset.yaml +1 -1
  21. package/manifests/mongodb/statefulset.yaml +11 -11
  22. package/manifests/mongodb-4.4/service-deployment.yaml +1 -3
  23. package/manifests/mysql/pv-pvc.yaml +1 -1
  24. package/manifests/mysql/statefulset.yaml +1 -1
  25. package/manifests/pv-pvc-dd.yaml +34 -0
  26. package/manifests/valkey/service.yaml +0 -1
  27. package/manifests/valkey/statefulset.yaml +2 -3
  28. package/package.json +1 -1
  29. package/proxy.yaml +6 -0
  30. package/scripts/device-scan.sh +43 -21
  31. package/scripts/gen-fqdns.sh +14 -0
  32. package/scripts/ip-info.sh +118 -0
  33. package/scripts/rpmfusion-ffmpeg-setup.sh +1 -0
  34. package/src/api/object-layer/object-layer.controller.js +19 -0
  35. package/src/api/object-layer/object-layer.router.js +4 -0
  36. package/src/api/object-layer/object-layer.service.js +111 -0
  37. package/src/api/user/user.model.js +10 -1
  38. package/src/cli/cluster.js +88 -75
  39. package/src/cli/deploy.js +165 -85
  40. package/src/cli/index.js +44 -3
  41. package/src/cli/monitor.js +12 -6
  42. package/src/cli/repository.js +13 -1
  43. package/src/cli/run.js +127 -60
  44. package/src/client/components/core/Logger.js +1 -1
  45. package/src/client/components/core/Modal.js +5 -0
  46. package/src/client/components/core/ObjectLayerEngineModal.js +336 -72
  47. package/src/client/components/core/ObjectLayerEngineViewer.js +239 -420
  48. package/src/client/components/core/Router.js +10 -1
  49. package/src/client/components/cyberia-portal/LogInCyberiaPortal.js +1 -1
  50. package/src/client/components/cyberia-portal/LogOutCyberiaPortal.js +1 -1
  51. package/src/client/components/cyberia-portal/MenuCyberiaPortal.js +1 -1
  52. package/src/client/components/cyberia-portal/ObjectLayerCyberiaPortal.js +44 -4
  53. package/src/client/services/default/default.management.js +25 -5
  54. package/src/client/services/object-layer/object-layer.management.js +8 -8
  55. package/src/client/services/object-layer/object-layer.service.js +34 -0
  56. package/src/index.js +1 -1
  57. package/src/server/client-build.js +5 -4
  58. package/src/server/conf.js +1 -1
  59. package/src/server/start.js +3 -1
  60. package/manifests/kubelet-config.yaml +0 -65
  61. package/manifests/mongodb/backup-access.yaml +0 -16
  62. package/manifests/mongodb/backup-cronjob.yaml +0 -42
  63. package/manifests/mongodb/backup-pv-pvc.yaml +0 -22
  64. package/manifests/mongodb/configmap.yaml +0 -26
package/src/cli/deploy.js CHANGED
@@ -129,15 +129,28 @@ 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'));
138
+ let volumes = [
139
+ {
140
+ volumeMountPath: '/etc/config',
141
+ volumeName: 'config-volume',
142
+ configMap: 'underpost-config',
143
+ },
144
+ ];
145
+ const confVolume = fs.existsSync(`./engine-private/conf/${deployId}/conf.volume.json`)
146
+ ? JSON.parse(fs.readFileSync(`./engine-private/conf/${deployId}/conf.volume.json`, 'utf8'))
147
+ : [];
148
+ volumes = volumes.concat(confVolume);
137
149
  return `apiVersion: apps/v1
138
150
  kind: Deployment
139
151
  metadata:
140
152
  name: ${deployId}-${env}-${suffix}
153
+ namespace: ${namespace ? namespace : 'default'}
141
154
  labels:
142
155
  app: ${deployId}-${env}-${suffix}
143
156
  spec:
@@ -168,18 +181,16 @@ spec:
168
181
  npm install -g underpost &&
169
182
  underpost secret underpost --create-from-file /etc/config/.env.${env} &&
170
183
  underpost start --build --run ${deployId} ${env}
171
- volumeMounts:
172
- - name: config-volume
173
- mountPath: /etc/config
174
- volumes:
175
- - name: config-volume
176
- configMap:
177
- name: underpost-config
184
+ ${UnderpostDeploy.API.volumeFactory(volumes.map((v) => ((v.version = `${deployId}-${env}-${suffix}`), v)))
185
+ .render.split(`\n`)
186
+ .map((l) => ' ' + l)
187
+ .join(`\n`)}
178
188
  ---
179
189
  apiVersion: v1
180
190
  kind: Service
181
191
  metadata:
182
192
  name: ${deployId}-${env}-${suffix}-service
193
+ namespace: ${namespace}
183
194
  spec:
184
195
  selector:
185
196
  app: ${deployId}-${env}-${suffix}
@@ -193,6 +204,7 @@ spec:
193
204
  * @param {object} options - Options for the manifest build process.
194
205
  * @param {string} options.replicas - Number of replicas for each deployment.
195
206
  * @param {string} options.image - Docker image for the deployment.
207
+ * @param {string} options.namespace - Kubernetes namespace for the deployment.
196
208
  * @returns {Promise<void>} - Promise that resolves when the manifest is built.
197
209
  * @memberof UnderpostDeploy
198
210
  */
@@ -200,6 +212,7 @@ spec:
200
212
  const resources = UnderpostDeploy.API.resourcesFactory();
201
213
  const replicas = options.replicas;
202
214
  const image = options.image;
215
+ if (!options.namespace) options.namespace = 'default';
203
216
 
204
217
  for (const _deployId of deployList.split(',')) {
205
218
  const deployId = _deployId.trim();
@@ -227,6 +240,7 @@ ${UnderpostDeploy.API.deploymentYamlPartsFactory({
227
240
  resources,
228
241
  replicas,
229
242
  image,
243
+ namespace: options.namespace,
230
244
  }).replace('{{ports}}', buildKindPorts(fromPort, toPort))}
231
245
  `;
232
246
  }
@@ -249,6 +263,7 @@ apiVersion: projectcontour.io/v1
249
263
  kind: HTTPProxy
250
264
  metadata:
251
265
  name: ${host}
266
+ namespace: ${options.namespace}
252
267
  spec:
253
268
  virtualhost:
254
269
  fqdn: ${host}${
@@ -308,16 +323,18 @@ spec:
308
323
  /**
309
324
  * Builds a Certificate resource for a host using cert-manager.
310
325
  * @param {string} host - Hostname for which the certificate is being built.
326
+ * @param {string} namespace - Kubernetes namespace for the certificate.
311
327
  * @returns {string} - Certificate resource YAML for the specified host.
312
328
  * @memberof UnderpostDeploy
313
329
  */
314
- buildCertManagerCertificate({ host }) {
330
+ buildCertManagerCertificate({ host, namespace }) {
315
331
  return `
316
332
  ---
317
333
  apiVersion: cert-manager.io/v1
318
334
  kind: Certificate
319
335
  metadata:
320
336
  name: ${host}
337
+ namespace: ${namespace}
321
338
  spec:
322
339
  commonName: ${host}
323
340
  dnsNames:
@@ -364,9 +381,11 @@ spec:
364
381
  * @param {boolean} options.disableUpdateDeployment - Whether to disable deployment updates.
365
382
  * @param {boolean} options.disableUpdateProxy - Whether to disable proxy updates.
366
383
  * @param {boolean} options.disableDeploymentProxy - Whether to disable deployment proxy.
384
+ * @param {boolean} options.disableUpdateVolume - Whether to disable volume updates.
367
385
  * @param {boolean} options.status - Whether to display deployment status.
368
386
  * @param {boolean} options.etcHosts - Whether to display the /etc/hosts file.
369
387
  * @param {boolean} options.disableUpdateUnderpostConfig - Whether to disable Underpost config updates.
388
+ * @param {string} [options.namespace] - Kubernetes namespace for the deployment.
370
389
  * @returns {Promise<void>} - Promise that resolves when the deployment process is complete.
371
390
  * @memberof UnderpostDeploy
372
391
  */
@@ -391,67 +410,18 @@ spec:
391
410
  disableUpdateDeployment: false,
392
411
  disableUpdateProxy: false,
393
412
  disableDeploymentProxy: false,
413
+ disableUpdateVolume: false,
394
414
  status: false,
395
415
  etcHosts: false,
396
416
  disableUpdateUnderpostConfig: false,
417
+ namespace: '',
397
418
  },
398
419
  ) {
399
- if (options.infoUtil === true)
400
- return logger.info(`
401
- kubectl rollout restart deployment/deployment-name
402
- kubectl rollout undo deployment/deployment-name
403
- kubectl scale statefulsets <stateful-set-name> --replicas=<new-replicas>
404
- kubectl get pods -w
405
- kubectl patch statefulset valkey-service --type='json' -p='[{"op": "replace", "path": "/spec/template/spec/containers/0/image", "value":"valkey/valkey:latest"}]'
406
- kubectl patch statefulset valkey-service -p '{"spec":{"template":{"spec":{"containers":[{"name":"valkey-service","imagePullPolicy":"Never"}]}}}}'
407
- kubectl logs -f <pod-name>
408
- kubectl describe pod <pod-name>
409
- kubectl exec -it <pod-name> -- bash
410
- kubectl exec -it <pod-name> -- sh
411
- docker exec -it kind-control-plane bash
412
- curl -4 -v google.com
413
- kubectl taint nodes <node-name> node-role.kubernetes.io/control-plane:NoSchedule-
414
- kubectl run test-pod --image=busybox:latest --restart=Never -- /bin/sh -c "while true; do sleep 30; done;"
415
- kubectl run test-pod --image=alpine/curl:latest --restart=Never -- sh -c "sleep infinity"
416
- kubectl get ippools -o yaml
417
- kubectl get node <node-name> -o jsonpath='{.spec.podCIDR}'
418
- kubectl patch ippool default-ipv4-ippool --type='json' -p='[{"op": "replace", "path": "/spec/cidr", "value": "10.244.0.0/16"}]'
419
- kubectl patch ippool default-ipv4-ippool --type='json' -p='[{"op": "replace", "path": "/spec/cidr", "value": "192.168.0.0/24"}]'
420
- sudo podman run --rm localhost/<image-name>:<image-version> <command>
421
- kubectl get configmap kubelet-config -n kube-system -o yaml > kubelet-config.yaml
422
- kubectl -n kube-system rollout restart daemonset kube-proxy
423
- kubectl get EndpointSlice -o wide --all-namespaces -w
424
- kubectl apply -k manifests/deployment/adminer/.
425
- kubectl wait --for=condition=Ready pod/busybox1
426
- kubectl wait --for=jsonpath='{.status.phase}'=Running pod/busybox1
427
- kubectl wait --for='jsonpath={.status.conditions[?(@.type=="Ready")].status}=True' pod/busybox1
428
- kubectl wait --for=delete pod/busybox1 --timeout=60s
429
-
430
- kubectl run --rm -it test-dns --image=busybox:latest --restart=Never -- /bin/sh -c "
431
- nslookup kubernetes.default.svc.cluster.local;
432
- nslookup mongodb-service.default.svc.cluster.local;
433
- nslookup valkey-service.default.svc.cluster.local;
434
- nc -vz mongodb-service 27017;
435
- nc -vz valkey-service 6379;
436
- echo exit code: \\\$?
437
- "
438
-
439
- kubectl apply -f - <<EOF
440
- apiVersion: apps/v1
441
- kind: StatefulSet
442
- metadata:
443
- name: ...
444
- EOF
445
-
446
- https://org.ngc.nvidia.com/setup/api-keys
447
- docker login nvcr.io
448
- Username: $oauthtoken
449
- Password: <Your Key>
450
- `);
420
+ const namespace = options.namespace ? options.namespace : 'default';
451
421
  if (!deployList && options.certHosts) {
452
422
  for (const host of options.certHosts.split(',')) {
453
- shellExec(`sudo kubectl apply -f - <<EOF
454
- ${UnderpostDeploy.API.buildCertManagerCertificate({ host })}
423
+ shellExec(`sudo kubectl apply -f - -n ${namespace} <<EOF
424
+ ${UnderpostDeploy.API.buildCertManagerCertificate({ host, namespace })}
455
425
  EOF`);
456
426
  }
457
427
  return;
@@ -513,19 +483,44 @@ EOF`);
513
483
  continue;
514
484
  }
515
485
 
486
+ const confServer = JSON.parse(fs.readFileSync(`./engine-private/conf/${deployId}/conf.server.json`, 'utf8'));
487
+ const confVolume = fs.existsSync(`./engine-private/conf/${deployId}/conf.volume.json`)
488
+ ? JSON.parse(fs.readFileSync(`./engine-private/conf/${deployId}/conf.volume.json`, 'utf8'))
489
+ : [];
490
+
516
491
  if (!options.disableUpdateDeployment)
517
492
  for (const version of options.versions.split(',')) {
518
- shellExec(`sudo kubectl delete svc ${deployId}-${env}-${version}-service`);
519
- shellExec(`sudo kubectl delete deployment ${deployId}-${env}-${version}`);
493
+ shellExec(
494
+ `sudo kubectl delete svc ${deployId}-${env}-${version}-service -n ${namespace} --ignore-not-found`,
495
+ );
496
+ shellExec(
497
+ `sudo kubectl delete deployment ${deployId}-${env}-${version} -n ${namespace} --ignore-not-found`,
498
+ );
499
+ if (!options.disableUpdateVolume) {
500
+ for (const volume of confVolume) {
501
+ const pvcId = `${volume.claimName}-${deployId}-${env}-${version}`;
502
+ const pvId = `${volume.claimName.replace('pvc-', 'pv-')}-${deployId}-${env}-${version}`;
503
+ const rootVolumeHostPath = `/home/dd/engine/volume/${pvId}`;
504
+ if (!fs.existsSync(rootVolumeHostPath)) fs.mkdirSync(rootVolumeHostPath, { recursive: true });
505
+ fs.copySync(volume.volumeMountPath, rootVolumeHostPath);
506
+ shellExec(`kubectl delete pvc ${pvcId} -n ${namespace} --ignore-not-found`);
507
+ shellExec(`kubectl delete pv ${pvId} --ignore-not-found`);
508
+ shellExec(`kubectl apply -f - -n ${namespace} <<EOF
509
+ ${UnderpostDeploy.API.persistentVolumeFactory({
510
+ hostPath: rootVolumeHostPath,
511
+ pvcId,
512
+ })}
513
+ EOF
514
+ `);
515
+ }
516
+ }
520
517
  }
521
518
 
522
- const confServer = JSON.parse(fs.readFileSync(`./engine-private/conf/${deployId}/conf.server.json`, 'utf8'));
523
-
524
519
  for (const host of Object.keys(confServer)) {
525
520
  if (!options.disableUpdateProxy) {
526
- shellExec(`sudo kubectl delete HTTPProxy ${host}`);
521
+ shellExec(`sudo kubectl delete HTTPProxy ${host} -n ${namespace} --ignore-not-found`);
527
522
  if (UnderpostDeploy.API.isValidTLSContext({ host, env, options }))
528
- shellExec(`sudo kubectl delete Certificate ${host}`);
523
+ shellExec(`sudo kubectl delete Certificate ${host} -n ${namespace} --ignore-not-found`);
529
524
  }
530
525
  if (!options.remove) etcHosts.push(host);
531
526
  }
@@ -536,11 +531,13 @@ EOF`);
536
531
  : `manifests/deployment/${deployId}-${env}`;
537
532
 
538
533
  if (!options.remove) {
539
- if (!options.disableUpdateDeployment) shellExec(`sudo kubectl apply -f ./${manifestsPath}/deployment.yaml`);
540
- if (!options.disableUpdateProxy) shellExec(`sudo kubectl apply -f ./${manifestsPath}/proxy.yaml`);
534
+ if (!options.disableUpdateDeployment)
535
+ shellExec(`sudo kubectl apply -f ./${manifestsPath}/deployment.yaml -n ${namespace}`);
536
+ if (!options.disableUpdateProxy)
537
+ shellExec(`sudo kubectl apply -f ./${manifestsPath}/proxy.yaml -n ${namespace}`);
541
538
 
542
539
  if (UnderpostDeploy.API.isValidTLSContext({ host: Object.keys(confServer)[0], env, options }))
543
- shellExec(`sudo kubectl apply -f ./${manifestsPath}/secret.yaml`);
540
+ shellExec(`sudo kubectl apply -f ./${manifestsPath}/secret.yaml -n ${namespace}`);
544
541
  }
545
542
  }
546
543
  if (options.etcHosts === true) {
@@ -557,15 +554,19 @@ EOF`);
557
554
  * Retrieves information about a deployment.
558
555
  * @param {string} deployId - Deployment ID for which information is being retrieved.
559
556
  * @param {string} kindType - Type of Kubernetes resource to retrieve information for (e.g. 'pods').
557
+ * @param {string} namespace - Kubernetes namespace to retrieve information from.
560
558
  * @returns {Array<object>} - Array of objects containing information about the deployment.
561
559
  * @memberof UnderpostDeploy
562
560
  */
563
- get(deployId, kindType = 'pods') {
564
- const raw = shellExec(`sudo kubectl get ${kindType} --all-namespaces -o wide`, {
565
- stdout: true,
566
- disableLog: true,
567
- silent: true,
568
- });
561
+ get(deployId, kindType = 'pods', namespace = '') {
562
+ const raw = shellExec(
563
+ `sudo kubectl get ${kindType}${namespace ? ` -n ${namespace}` : ` --all-namespaces`} -o wide`,
564
+ {
565
+ stdout: true,
566
+ disableLog: true,
567
+ silent: true,
568
+ },
569
+ );
569
570
 
570
571
  const heads = raw
571
572
  .split(`\n`)[0]
@@ -679,12 +680,13 @@ EOF`);
679
680
  /**
680
681
  * Creates a configmap for a deployment.
681
682
  * @param {string} env - Environment for which the configmap is being created.
683
+ * @param {string} [namespace='default'] - Kubernetes namespace for the configmap.
682
684
  * @memberof UnderpostDeploy
683
685
  */
684
- configMap(env) {
685
- shellExec(`kubectl delete configmap underpost-config`);
686
+ configMap(env, namespace = 'default') {
687
+ shellExec(`kubectl delete configmap underpost-config -n ${namespace} --ignore-not-found`);
686
688
  shellExec(
687
- `kubectl create configmap underpost-config --from-file=/home/dd/engine/engine-private/conf/dd-cron/.env.${env}`,
689
+ `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}`,
688
690
  );
689
691
  },
690
692
  /**
@@ -693,15 +695,93 @@ EOF`);
693
695
  * @param {string} env - Environment for which the traffic is being switched.
694
696
  * @param {string} targetTraffic - Target traffic status for the deployment.
695
697
  * @param {number} replicas - Number of replicas for the deployment.
698
+ * @param {string} [namespace='default'] - Kubernetes namespace for the deployment.
696
699
  * @memberof UnderpostDeploy
697
700
  */
698
- switchTraffic(deployId, env, targetTraffic, replicas = 1) {
701
+ switchTraffic(deployId, env, targetTraffic, replicas = 1, namespace = 'default') {
699
702
  UnderpostRootEnv.API.set(`${deployId}-${env}-traffic`, targetTraffic);
700
703
  shellExec(
701
- `node bin deploy --info-router --build-manifest --traffic ${targetTraffic} --replicas ${replicas} ${deployId} ${env}`,
704
+ `node bin deploy --info-router --build-manifest --traffic ${targetTraffic} --replicas ${replicas} --namespace ${namespace} ${deployId} ${env}`,
702
705
  );
703
- shellExec(`sudo kubectl apply -f ./engine-private/conf/${deployId}/build/${env}/proxy.yaml`);
706
+ shellExec(`sudo kubectl apply -f ./engine-private/conf/${deployId}/build/${env}/proxy.yaml -n ${namespace}`);
704
707
  },
708
+
709
+ /**
710
+ * Creates volume mounts and volumes for a deployment.
711
+ * @param {Array<volume>} volumes - List of volume configurations.
712
+ * @param {string} volume.volumeName - Name of the volume.
713
+ * @param {string} volume.volumeMountPath - Mount path of the volume in the container.
714
+ * @param {string} volume.volumeHostPath - Host path of the volume.
715
+ * @param {string} volume.volumeType - Type of the volume (e.g. 'Directory').
716
+ * @param {string|null} volume.claimName - Name of the persistent volume claim (if applicable).
717
+ * @param {string|null} volume.configMap - Name of the config map (if applicable).
718
+ * @returns {object} - Object containing the rendered volume mounts and volumes.
719
+ * @memberof UnderpostDeploy
720
+ */
721
+ volumeFactory(
722
+ volumes = [
723
+ {
724
+ volumeName: 'volume-name',
725
+ volumeMountPath: '/path/in/container',
726
+ volumeHostPath: '/path/on/host',
727
+ volumeType: 'Directory',
728
+ claimName: null,
729
+ configMap: null,
730
+ version: null,
731
+ },
732
+ ],
733
+ ) {
734
+ let _volumeMounts = `
735
+ volumeMounts:`;
736
+ let _volumes = `
737
+ volumes:`;
738
+ volumes.map((volumeData) => {
739
+ let { volumeName, volumeMountPath, volumeHostPath, volumeType, claimName, configMap, version } = volumeData;
740
+ if (version) {
741
+ volumeName = `${volumeName}-${version}`;
742
+ claimName = claimName ? `${claimName}-${version}` : null;
743
+ }
744
+ _volumeMounts += `
745
+ - name: ${volumeName}
746
+ mountPath: ${volumeMountPath}
747
+ `;
748
+
749
+ _volumes += `
750
+ - name: ${volumeName}
751
+ ${
752
+ configMap
753
+ ? ` configMap:
754
+ name: ${configMap}`
755
+ : claimName
756
+ ? ` persistentVolumeClaim:
757
+ claimName: ${claimName}`
758
+ : ` hostPath:
759
+ path: ${volumeHostPath}
760
+ type: ${volumeType}
761
+ `
762
+ }
763
+
764
+ `;
765
+ });
766
+ return { render: _volumeMounts + _volumes };
767
+ },
768
+
769
+ /**
770
+ * Creates a persistent volume and persistent volume claim for a deployment.
771
+ * @param {object} options - Options for the persistent volume and claim creation.
772
+ * @param {string} options.hostPath - Host path for the persistent volume.
773
+ * @param {string} options.pvcId - Persistent volume claim ID.
774
+ * @returns {string} - YAML configuration for the persistent volume and claim.
775
+ * @memberof UnderpostDeploy
776
+ */
777
+ persistentVolumeFactory({ hostPath, pvcId }) {
778
+ return fs
779
+ .readFileSync(`./manifests/pv-pvc-dd.yaml`, 'utf8')
780
+ .replace('/home/dd', hostPath)
781
+ .replace('pv-dd', pvcId.replace('pvc-', 'pv-'))
782
+ .replace('pvc-dd', pvcId);
783
+ },
784
+
705
785
  /**
706
786
  * Creates a hosts file for a deployment.
707
787
  * @param {Array<string>} hosts - List of hosts to be added to the hosts file.
@@ -791,7 +871,7 @@ ${renderHosts}`,
791
871
  getCurrentLoadedImages(node = 'kind-worker', specContainers = false) {
792
872
  if (specContainers) {
793
873
  const raw = shellExec(
794
- `kubectl get pods --all-namespaces -o=jsonpath='{range .items[*]}{"\\n"}{.metadata.name}{":\\t"}{range .spec.containers[*]}{.image}{", "}{end}{end}'`,
874
+ `kubectl get pods --all-namespaces -o=jsonpath='{range .items[*]}{"\\n"}{.metadata.namespace}{"/"}{.metadata.name}{":\\t"}{range .spec.containers[*]}{.image}{", "}{end}{end}'`,
795
875
  {
796
876
  stdout: true,
797
877
  silent: true,
package/src/cli/index.js CHANGED
@@ -7,6 +7,8 @@ import { commitData } from '../client/components/core/CommonJs.js';
7
7
  import UnderpostLxd from './lxd.js';
8
8
  import UnderpostBaremetal from './baremetal.js';
9
9
  import UnderpostRun from './run.js';
10
+ import Dns from '../server/dns.js';
11
+ import { pbcopy } from '../server/process.js';
10
12
 
11
13
  // Load environment variables from .env file
12
14
  const underpostRootPath = getUnderpostRootPath();
@@ -78,6 +80,8 @@ program
78
80
  .option('--msg <msg>', 'Sets a custom commit message.')
79
81
  .option('--deploy-id <deploy-id>', 'Sets the deployment configuration ID for the commit context.')
80
82
  .option('--cached', 'Commit staged changes only or context.')
83
+ .option('--hashes <hashes>', 'Comma-separated list of specific file hashes of commits.')
84
+ .option('--extension <extension>', 'specific file extensions of commits.')
81
85
  .description('Manages commits to a GitHub repository, supporting various commit types and options.')
82
86
  .action(Underpost.repo.commit);
83
87
 
@@ -127,6 +131,16 @@ program
127
131
  .description('Displays the root path of the npm installation.')
128
132
  .action(() => console.log(getNpmRootPath()));
129
133
 
134
+ program
135
+ .command('ip')
136
+ .option('--copy', 'Copies the IP addresses to the clipboard.')
137
+ .description('Displays the current public machine IP addresses.')
138
+ .action(async (options) => {
139
+ const ip = await Dns.getPublicIp();
140
+ if (options.copy) return pbcopy(ip);
141
+ return console.log(ip);
142
+ });
143
+
130
144
  // 'cluster' command: Manage Kubernetes clusters
131
145
  program
132
146
  .command('cluster')
@@ -142,10 +156,20 @@ program
142
156
  .option('--contour', 'Initializes the cluster with Project Contour base HTTPProxy and Envoy.')
143
157
  .option('--cert-manager', "Initializes the cluster with a Let's Encrypt production ClusterIssuer.")
144
158
  .option('--dedicated-gpu', 'Initializes the cluster with dedicated GPU base resources and environment settings.')
145
- .option('--info', 'Retrieves information about all deployed Kubernetes objects.')
146
159
  .option('--full', 'Initializes the cluster with all available statefulsets and services.')
147
- .option('--ns-use <ns-name>', 'Switches the current Kubernetes context to the specified namespace.')
160
+ .option(
161
+ '--ns-use <ns-name>',
162
+ "Switches the current Kubernetes context to the specified namespace (creates if it doesn't exist).",
163
+ )
148
164
  .option('--kubeadm', 'Initializes the cluster using kubeadm for control plane management.')
165
+ .option(
166
+ '--pod-network-cidr <cidr>',
167
+ 'Sets custom pod network CIDR for kubeadm cluster initialization (defaults to "192.168.0.0/16").',
168
+ )
169
+ .option(
170
+ '--control-plane-endpoint <endpoint>',
171
+ 'Sets custom control plane endpoint for kubeadm cluster initialization (defaults to "localhost:6443").',
172
+ )
149
173
  .option('--grafana', 'Initializes the cluster with a Grafana deployment.')
150
174
  .option(
151
175
  '--prom [hosts]',
@@ -163,6 +187,8 @@ program
163
187
  .option('--chown', 'Sets the appropriate ownership for Kubernetes kubeconfig files.')
164
188
  .option('--k3s', 'Initializes the cluster using K3s (Lightweight Kubernetes).')
165
189
  .option('--hosts <hosts>', 'A comma-separated list of cluster hostnames or IP addresses.')
190
+ .option('--remove-volume-host-paths', 'Removes specified volume host paths after execution.')
191
+ .option('--namespace <namespace>', 'Kubernetes namespace for cluster operations (defaults to "default").')
166
192
  .action(Underpost.cluster.init)
167
193
  .description('Manages Kubernetes clusters, defaulting to Kind cluster initialization.');
168
194
 
@@ -178,7 +204,6 @@ program
178
204
  .option('--sync', 'Synchronizes deployment environment variables, ports, and replica counts.')
179
205
  .option('--info-router', 'Displays the current router structure and configuration.')
180
206
  .option('--expose', 'Exposes services matching the provided deployment ID list.')
181
- .option('--info-util', 'Displays useful `kubectl` utility management commands.')
182
207
  .option('--cert', 'Resets TLS/SSL certificate secrets for deployments.')
183
208
  .option('--cert-hosts <hosts>', 'Resets TLS/SSL certificate secrets for specified hosts.')
184
209
  .option('--node <node>', 'Sets optional node for deployment operations.')
@@ -193,6 +218,7 @@ program
193
218
  .option('--disable-update-deployment', 'Disables updates to deployments.')
194
219
  .option('--disable-update-proxy', 'Disables updates to proxies.')
195
220
  .option('--disable-deployment-proxy', 'Disables proxies of deployments.')
221
+ .option('--disable-update-volume', 'Disables updates to volume mounts during deployment.')
196
222
  .option(
197
223
  '--status',
198
224
  'Retrieves current network traffic data from resource deployments and the host machine network configuration.',
@@ -201,6 +227,7 @@ program
201
227
  .option('--etc-hosts', 'Enables the etc-hosts context for deployment operations.')
202
228
  .option('--restore-hosts', 'Restores default `/etc/hosts` entries.')
203
229
  .option('--disable-update-underpost-config', 'Disables updates to Underpost configuration during deployment.')
230
+ .option('--namespace <namespace>', 'Kubernetes namespace for deployment operations (defaults to "default").')
204
231
  .description('Manages application deployments, defaulting to deploying development pods.')
205
232
  .action(Underpost.deploy.callback);
206
233
 
@@ -392,6 +419,18 @@ program
392
419
  .option('--image-name <image-name>', 'Optional: Specifies the image name for test execution.')
393
420
  .option('--container-name <container-name>', 'Optional: Specifies the container name for test execution.')
394
421
  .option('--namespace <namespace>', 'Optional: Specifies the namespace for test execution.')
422
+ .option('--tty', 'Enables TTY for the container in deploy-job.')
423
+ .option('--stdin', 'Keeps STDIN open for the container in deploy-job.')
424
+ .option('--restart-policy <policy>', 'Sets the restart policy for the job in deploy-job.')
425
+ .option('--runtime-class-name <name>', 'Sets the runtime class name for the job in deploy-job.')
426
+ .option('--image-pull-policy <policy>', 'Sets the image pull policy for the job in deploy-job.')
427
+ .option('--api-version <version>', 'Sets the API version for the job manifest in deploy-job.')
428
+ .option(
429
+ '--labels <labels>',
430
+ 'Optional: Specifies a comma-separated list of key-value pairs for labels (e.g., "app=my-app,env=prod").',
431
+ )
432
+ .option('--claim-name <name>', 'Optional: Specifies the claim name for volume mounting in deploy-job.')
433
+ .option('--kind <kind-type>', 'Specifies the kind of Kubernetes resource (e.g., Job, Deployment) for deploy-job.')
395
434
  .option('--kubeadm', 'Flag to indicate Kubeadm cluster type context')
396
435
  .option('--k3s', 'Flag to indicate K3s cluster type context')
397
436
  .option('--force', 'Forces operation, overriding any warnings or conflicts.')
@@ -399,7 +438,9 @@ program
399
438
  .option('--reset', 'Resets the runner state before execution.')
400
439
  .option('--terminal', 'Enables terminal mode for interactive script execution.')
401
440
  .option('--dev-proxy-port-offset <port-offset>', 'Sets a custom port offset for development proxy.')
441
+ .option('--host-network', 'Enables host network mode for the runner execution.')
402
442
  .option('--conf-server-path <conf-server-path>', 'Sets a custom configuration server path.')
443
+ .option('--underpost-root <underpost-root>', 'Sets a custom Underpost root path.')
403
444
  .description('Runs a script from the specified path.')
404
445
  .action(UnderpostRun.API.callback);
405
446
 
@@ -39,6 +39,7 @@ class UnderpostMonitor {
39
39
  * @param {string} [options.type=''] - Type of deployment (e.g., 'blue-green', 'remote').
40
40
  * @param {string} [options.replicas=''] - Number of replicas for the deployment.
41
41
  * @param {boolean} [options.sync=false] - Synchronize traffic switching with the deployment.
42
+ * @param {string} [options.namespace=''] - Kubernetes namespace for the deployment.
42
43
  * @param {object} [commanderOptions] - Options passed from the command line interface.
43
44
  * @param {object} [auxRouter] - Optional router configuration for the deployment.
44
45
  * @memberof UnderpostMonitor
@@ -46,10 +47,11 @@ class UnderpostMonitor {
46
47
  async callback(
47
48
  deployId,
48
49
  env = 'development',
49
- options = { now: false, single: false, msInterval: '', type: '', replicas: '', sync: false },
50
+ options = { now: false, single: false, msInterval: '', type: '', replicas: '', sync: false, namespace: '' },
50
51
  commanderOptions,
51
52
  auxRouter,
52
53
  ) {
54
+ if (!options.namespace) options.namespace = 'default';
53
55
  if (deployId === 'dd' && fs.existsSync(`./engine-private/deploy/dd.router`)) {
54
56
  for (const _deployId of fs.readFileSync(`./engine-private/deploy/dd.router`, 'utf8').split(','))
55
57
  UnderpostMonitor.API.callback(
@@ -95,12 +97,13 @@ class UnderpostMonitor {
95
97
  if (traffic === 'blue') traffic = 'green';
96
98
  else traffic = 'blue';
97
99
  UnderpostRootEnv.API.set(`${deployId}-${env}-traffic`, traffic);
100
+ const namespace = options.namespace || 'default';
98
101
  shellExec(
99
102
  `node bin deploy --info-router --build-manifest --traffic ${traffic} --replicas ${
100
103
  options.replicas ? options.replicas : 1
101
- } ${deployId} ${env}`,
104
+ } --namespace ${namespace} ${deployId} ${env}`,
102
105
  );
103
- shellExec(`sudo kubectl apply -f ./engine-private/conf/${deployId}/build/${env}/proxy.yaml`);
106
+ shellExec(`sudo kubectl apply -f ./engine-private/conf/${deployId}/build/${env}/proxy.yaml -n ${namespace}`);
104
107
  };
105
108
 
106
109
  const monitor = async (reject) => {
@@ -152,12 +155,15 @@ class UnderpostMonitor {
152
155
  fs.readFileSync(`./engine-private/conf/${deployId}/conf.server.json`, 'utf8'),
153
156
  );
154
157
 
155
- UnderpostDeploy.API.configMap(env);
158
+ const namespace = options.namespace || 'default';
159
+ UnderpostDeploy.API.configMap(env, namespace);
156
160
 
157
161
  for (const host of Object.keys(confServer)) {
158
- shellExec(`sudo kubectl delete HTTPProxy ${host}`);
162
+ shellExec(`sudo kubectl delete HTTPProxy ${host} -n ${namespace} --ignore-not-found`);
159
163
  }
160
- shellExec(`sudo kubectl rollout restart deployment/${deployId}-${env}-${traffic}`);
164
+ shellExec(
165
+ `sudo kubectl rollout restart deployment/${deployId}-${env}-${traffic} -n ${namespace}`,
166
+ );
161
167
 
162
168
  switchTraffic();
163
169
  }
@@ -89,6 +89,8 @@ class UnderpostRepository {
89
89
  * @param {boolean} [options.lastMsg=0] - If greater than 0, copies or show the last last single n commit message to clipboard.
90
90
  * @param {string} [options.msg=''] - If provided, outputs this message instead of committing.
91
91
  * @param {string} [options.deployId=''] - An optional deploy ID to include in the commit message.
92
+ * @param {string} [options.hashes=''] - If provided with diff option, shows the diff between two hashes.
93
+ * @param {string} [options.extension=''] - If provided with diff option, filters the diff by this file extension.
92
94
  * @memberof UnderpostRepository
93
95
  */
94
96
  commit(
@@ -107,9 +109,19 @@ class UnderpostRepository {
107
109
  log: 0,
108
110
  msg: '',
109
111
  deployId: '',
112
+ hashes: '',
113
+ extension: '',
110
114
  },
111
115
  ) {
112
116
  if (!repoPath) repoPath = '.';
117
+ if (options.diff && options.hashes) {
118
+ const hashes = options.hashes.split(',');
119
+ const cmd = `git --no-pager diff ${hashes[0]} ${hashes[1] ? hashes[1] : 'HEAD'}${options.extension ? ` -- '*.${options.extension}'` : ''}`;
120
+ if (options.copy) {
121
+ pbcopy(cmd);
122
+ } else console.log(cmd);
123
+ return;
124
+ }
113
125
  if (options.msg) {
114
126
  options.msg = options.msg.replaceAll('"', '').replaceAll(`'`, '').replaceAll('`', '');
115
127
  let key = Object.keys(commitData).find((k) => k && options.msg.toLocaleLowerCase().slice(0, 16).match(k));
@@ -167,7 +179,7 @@ class UnderpostRepository {
167
179
  return;
168
180
  }
169
181
  if (options.info) return logger.info('', commitData);
170
- const _message = `${commitType}${subModule ? `(${subModule})` : ''}${process.argv.includes('!') ? '!' : ''}: ${
182
+ const _message = `${commitType}${subModule ? `(${subModule})` : ''}: ${
171
183
  commitData[commitType].emoji
172
184
  } ${message ? message : commitData[commitType].description}`;
173
185
  if (options.copy) return pbcopy(_message);