underpost 2.90.4 → 2.95.1

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 (41) hide show
  1. package/.github/workflows/pwa-microservices-template-page.cd.yml +5 -4
  2. package/.github/workflows/release.cd.yml +7 -7
  3. package/README.md +7 -8
  4. package/bin/build.js +6 -1
  5. package/bin/deploy.js +2 -196
  6. package/cli.md +154 -80
  7. package/manifests/deployment/dd-default-development/deployment.yaml +4 -4
  8. package/manifests/deployment/dd-test-development/deployment.yaml +2 -2
  9. package/package.json +1 -1
  10. package/scripts/disk-clean.sh +216 -0
  11. package/scripts/rocky-setup.sh +1 -0
  12. package/scripts/ssh-cluster-info.sh +4 -3
  13. package/src/cli/cluster.js +1 -1
  14. package/src/cli/db.js +1143 -201
  15. package/src/cli/deploy.js +93 -24
  16. package/src/cli/env.js +2 -2
  17. package/src/cli/image.js +198 -133
  18. package/src/cli/index.js +111 -44
  19. package/src/cli/lxd.js +73 -74
  20. package/src/cli/monitor.js +20 -9
  21. package/src/cli/repository.js +212 -5
  22. package/src/cli/run.js +207 -74
  23. package/src/cli/ssh.js +642 -14
  24. package/src/client/components/core/CommonJs.js +0 -1
  25. package/src/db/mongo/MongooseDB.js +5 -1
  26. package/src/index.js +1 -1
  27. package/src/monitor.js +11 -1
  28. package/src/server/backup.js +1 -1
  29. package/src/server/conf.js +1 -1
  30. package/src/server/dns.js +242 -1
  31. package/src/server/process.js +6 -1
  32. package/src/server/start.js +2 -0
  33. package/scripts/snap-clean.sh +0 -26
  34. package/src/client/public/default/plantuml/client-conf.svg +0 -1
  35. package/src/client/public/default/plantuml/client-schema.svg +0 -1
  36. package/src/client/public/default/plantuml/cron-conf.svg +0 -1
  37. package/src/client/public/default/plantuml/cron-schema.svg +0 -1
  38. package/src/client/public/default/plantuml/server-conf.svg +0 -1
  39. package/src/client/public/default/plantuml/server-schema.svg +0 -1
  40. package/src/client/public/default/plantuml/ssr-conf.svg +0 -1
  41. package/src/client/public/default/plantuml/ssr-schema.svg +0 -1
package/src/cli/deploy.js CHANGED
@@ -387,6 +387,8 @@ spec:
387
387
  * @param {boolean} options.etcHosts - Whether to display the /etc/hosts file.
388
388
  * @param {boolean} options.disableUpdateUnderpostConfig - Whether to disable Underpost config updates.
389
389
  * @param {string} [options.namespace] - Kubernetes namespace for the deployment.
390
+ * @param {string} [options.kindType] - Type of Kubernetes resource to retrieve information for.
391
+ * @param {number} [options.port] - Port number for exposing the deployment.
390
392
  * @returns {Promise<void>} - Promise that resolves when the deployment process is complete.
391
393
  * @memberof UnderpostDeploy
392
394
  */
@@ -416,6 +418,8 @@ spec:
416
418
  etcHosts: false,
417
419
  disableUpdateUnderpostConfig: false,
418
420
  namespace: '',
421
+ kindType: '',
422
+ port: 0,
419
423
  },
420
424
  ) {
421
425
  const namespace = options.namespace ? options.namespace : 'default';
@@ -495,13 +499,20 @@ EOF`);
495
499
  const deployId = _deployId.trim();
496
500
  if (!deployId) continue;
497
501
  if (options.expose === true) {
498
- const svc = UnderpostDeploy.API.get(deployId, 'svc')[0];
499
- const port = parseInt(svc[`PORT(S)`].split('/TCP')[0]);
502
+ const kindType = options.kindType ? options.kindType : 'svc';
503
+ const svc = UnderpostDeploy.API.get(deployId, kindType)[0];
504
+ const port = options.port
505
+ ? options.port
506
+ : kindType !== 'svc'
507
+ ? 80
508
+ : parseInt(svc[`PORT(S)`].split('/TCP')[0]);
500
509
  logger.info(deployId, {
501
510
  svc,
502
511
  port,
503
512
  });
504
- shellExec(`sudo kubectl port-forward -n default svc/${svc.NAME} ${port}:${port}`, { async: true });
513
+ shellExec(`sudo kubectl port-forward -n ${namespace} ${kindType}/${svc.NAME} ${port}:${port}`, {
514
+ async: true,
515
+ });
505
516
  continue;
506
517
  }
507
518
 
@@ -646,26 +657,30 @@ EOF`);
646
657
  * @param {string} env - Environment for which the status is being checked.
647
658
  * @param {string} traffic - Current traffic status for the deployment.
648
659
  * @param {Array<string>} ignoresNames - List of pod names to ignore.
660
+ * @param {string} [namespace='default'] - Kubernetes namespace for the deployment.
649
661
  * @returns {object} - Object containing the status of the deployment.
650
662
  * @memberof UnderpostDeploy
651
663
  */
652
- checkDeploymentReadyStatus(deployId, env, traffic, ignoresNames = []) {
664
+ async checkDeploymentReadyStatus(deployId, env, traffic, ignoresNames = [], namespace = 'default') {
653
665
  const cmd = `underpost config get container-status`;
654
- const pods = UnderpostDeploy.API.get(`${deployId}-${env}-${traffic}`);
666
+ const pods = UnderpostDeploy.API.get(`${deployId}-${env}-${traffic}`, 'pods', namespace);
655
667
  const readyPods = [];
656
668
  const notReadyPods = [];
657
669
  for (const pod of pods) {
658
670
  const { NAME } = pod;
659
671
  if (ignoresNames && ignoresNames.find((t) => NAME.trim().toLowerCase().match(t.trim().toLowerCase()))) continue;
660
- if (
661
- shellExec(`sudo kubectl exec -i ${NAME} -- sh -c "${cmd}"`, { stdout: true }).match(
662
- `${deployId}-${env}-running-deployment`,
663
- )
664
- ) {
665
- readyPods.push(pod);
666
- } else {
667
- notReadyPods.push(pod);
668
- }
672
+ const out = await new Promise((resolve) => {
673
+ shellExec(`sudo kubectl exec -i ${NAME} -n ${namespace} -- sh -c "${cmd}"`, {
674
+ silent: true,
675
+ disableLog: true,
676
+ callback: function (code, stdout, stderr) {
677
+ return resolve(JSON.stringify({ code, stdout, stderr }));
678
+ },
679
+ });
680
+ });
681
+ pod.out = out;
682
+ const ready = out.match(`${deployId}-${env}-running-deployment`);
683
+ ready ? readyPods.push(pod) : notReadyPods.push(pod);
669
684
  }
670
685
  return {
671
686
  ready: pods.length > 0 && notReadyPods.length === 0,
@@ -888,15 +903,18 @@ ${renderHosts}`,
888
903
  * @param {string} env - Environment for which the ready status is being monitored.
889
904
  * @param {string} targetTraffic - Target traffic status for the deployment.
890
905
  * @param {Array<string>} ignorePods - List of pod names to ignore.
906
+ * @param {string} [namespace='default'] - Kubernetes namespace for the deployment.
907
+ * @param {string} [outLogType=''] - Type of log output.
891
908
  * @returns {object} - Object containing the ready status of the deployment.
892
909
  * @memberof UnderpostDeploy
893
910
  */
894
- async monitorReadyRunner(deployId, env, targetTraffic, ignorePods = []) {
911
+ async monitorReadyRunner(deployId, env, targetTraffic, ignorePods = [], namespace = 'default', outLogType = '') {
895
912
  let checkStatusIteration = 0;
896
913
  const checkStatusIterationMsDelay = 1000;
897
914
  const maxIterations = 500;
898
- const iteratorTag = `[${deployId}-${env}-${targetTraffic}]`;
899
- logger.info('Deployment init', { deployId, env, targetTraffic, checkStatusIterationMsDelay });
915
+ const deploymentId = `${deployId}-${env}-${targetTraffic}`;
916
+ const iteratorTag = `[${deploymentId}]`;
917
+ logger.info('Deployment init', { deployId, env, targetTraffic, checkStatusIterationMsDelay, namespace });
900
918
  const minReadyOk = 3;
901
919
  let readyOk = 0;
902
920
  let result = {
@@ -904,7 +922,7 @@ ${renderHosts}`,
904
922
  notReadyPods: [],
905
923
  readyPods: [],
906
924
  };
907
-
925
+ let lastMsg = {};
908
926
  while (readyOk < minReadyOk) {
909
927
  if (checkStatusIteration >= maxIterations) {
910
928
  logger.error(
@@ -912,30 +930,81 @@ ${renderHosts}`,
912
930
  );
913
931
  break;
914
932
  }
915
- result = UnderpostDeploy.API.checkDeploymentReadyStatus(deployId, env, targetTraffic, ignorePods);
933
+ result = await UnderpostDeploy.API.checkDeploymentReadyStatus(
934
+ deployId,
935
+ env,
936
+ targetTraffic,
937
+ ignorePods,
938
+ namespace,
939
+ );
916
940
  if (result.ready === true) {
917
941
  readyOk++;
918
942
  logger.info(`${iteratorTag} | Deployment ready. Verification number: ${readyOk}`);
943
+ for (const pod of result.readyPods) {
944
+ const { NAME } = pod;
945
+ lastMsg[NAME] = 'Deployment ready';
946
+ console.log(
947
+ 'Target pod:',
948
+ NAME[NAME.match('green') ? 'bgGreen' : 'bgBlue'].bold.black,
949
+ '| Status:',
950
+ lastMsg[NAME].bold.magenta,
951
+ );
952
+ }
953
+ }
954
+
955
+ switch (outLogType) {
956
+ case 'underpost': {
957
+ let indexOf = -1;
958
+ for (const pod of result.notReadyPods) {
959
+ indexOf++;
960
+ const { NAME, out } = pod;
961
+
962
+ if (out.match('not') && out.match('found') && checkStatusIteration <= 20 && out.match(deploymentId))
963
+ lastMsg[NAME] = 'Starting deployment';
964
+ else if (out.match('not') && out.match('found') && checkStatusIteration <= 20 && out.match('underpost'))
965
+ lastMsg[NAME] = 'Installing underpost cli';
966
+ else if (out.match('not') && out.match('found') && checkStatusIteration <= 20 && out.match('task'))
967
+ lastMsg[NAME] = 'Initializing setup task';
968
+ else if (out.match('Empty environment variables')) lastMsg[NAME] = 'Setup environment';
969
+ else if (out.match(`${deployId}-${env}-build-deployment`)) lastMsg[NAME] = 'Building apps/services';
970
+ else if (out.match(`${deployId}-${env}-initializing-deployment`))
971
+ lastMsg[NAME] = 'Initializing apps/services';
972
+ else if (!lastMsg[NAME]) lastMsg[NAME] = `Waiting for status`;
973
+
974
+ console.log(
975
+ 'Target pod:',
976
+ NAME[NAME.match('green') ? 'bgGreen' : 'bgBlue'].bold.black,
977
+ '| Status:',
978
+ lastMsg[NAME].bold.magenta,
979
+ );
980
+ }
981
+ }
919
982
  }
920
983
  await timer(checkStatusIterationMsDelay);
921
984
  checkStatusIteration++;
922
985
  logger.info(
923
- `${iteratorTag} | Deployment in progress... | Delay number check iterations: ${checkStatusIteration}`,
986
+ `${iteratorTag} | Deployment in progress... | Delay number monitor iterations: ${checkStatusIteration}`,
924
987
  );
925
988
  }
926
- logger.info(`${iteratorTag} | Deployment ready. | Total delay number check iterations: ${checkStatusIteration}`);
989
+ logger.info(
990
+ `${iteratorTag} | Deployment ready. | Total delay number monitor iterations: ${checkStatusIteration}`,
991
+ );
927
992
  return result;
928
993
  },
929
994
 
930
995
  /**
931
996
  * Retrieves the currently loaded images in the Kubernetes cluster.
997
+ * @param {string} [node='kind-worker'] - Node name to check for loaded images.
998
+ * @param {object} options - Options for the image retrieval.
999
+ * @param {boolean} options.spec - Whether to retrieve images from the pod specifications.
1000
+ * @param {string} options.namespace - Kubernetes namespace to filter pods.
932
1001
  * @returns {Array<object>} - Array of objects containing pod names and their corresponding images.
933
1002
  * @memberof UnderpostDeploy
934
1003
  */
935
- getCurrentLoadedImages(node = 'kind-worker', specContainers = false) {
936
- if (specContainers) {
1004
+ getCurrentLoadedImages(node = 'kind-worker', options = { spec: false, namespace: '' }) {
1005
+ if (options.spec) {
937
1006
  const raw = shellExec(
938
- `kubectl get pods --all-namespaces -o=jsonpath='{range .items[*]}{"\\n"}{.metadata.namespace}{"/"}{.metadata.name}{":\\t"}{range .spec.containers[*]}{.image}{", "}{end}{end}'`,
1007
+ `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}'`,
939
1008
  {
940
1009
  stdout: true,
941
1010
  silent: true,
package/src/cli/env.js CHANGED
@@ -63,7 +63,7 @@ class UnderpostRootEnv {
63
63
  const exeRootPath = `${getNpmRootPath()}/underpost`;
64
64
  const envPath = `${exeRootPath}/.env`;
65
65
  if (!fs.existsSync(envPath)) {
66
- logger.error(`Unable to find underpost root environment`);
66
+ logger.warn(`Empty environment variables`);
67
67
  return undefined;
68
68
  }
69
69
  const env = dotenv.parse(fs.readFileSync(envPath, 'utf8'));
@@ -80,7 +80,7 @@ class UnderpostRootEnv {
80
80
  const exeRootPath = `${getNpmRootPath()}/underpost`;
81
81
  const envPath = `${exeRootPath}/.env`;
82
82
  if (!fs.existsSync(envPath)) {
83
- logger.error(`Unable to find underpost root environment`);
83
+ logger.warn(`Empty environment variables`);
84
84
  return {};
85
85
  }
86
86
  const env = dotenv.parse(fs.readFileSync(envPath, 'utf8'));
package/src/cli/image.js CHANGED
@@ -24,143 +24,208 @@ const logger = loggerFactory(import.meta);
24
24
  */
25
25
  class UnderpostImage {
26
26
  static API = {
27
- dockerfile: {
28
- /**
29
- * @method pullBaseImages
30
- * @description Pulls base images and builds a 'rockylinux9-underpost' image,
31
- * then loads it into the specified Kubernetes cluster type (Kind, Kubeadm, or K3s).
32
- * @param {object} options - Options for pulling and loading images.
33
- * @param {boolean} [options.kindLoad=false] - If true, load image into Kind cluster.
34
- * @param {boolean} [options.kubeadmLoad=false] - If true, load image into Kubeadm cluster.
35
- * @param {boolean} [options.k3sLoad=false] - If true, load image into K3s cluster.
36
- * @param {string} [options.path=false] - Path to the Dockerfile context.
37
- * @param {boolean} [options.dev=false] - If true, use development mode.
38
- * @param {string} [options.version=''] - Version tag for the image.
39
- * @memberof UnderpostImage
40
- */
41
- pullBaseImages(
42
- options = {
43
- kindLoad: false,
44
- kubeadmLoad: false,
45
- k3sLoad: false,
46
- path: false,
47
- dev: false,
48
- version: '',
49
- },
50
- ) {
51
- // shellExec(`sudo podman pull docker.io/library/debian:buster`);
52
- shellExec(`sudo podman pull docker.io/library/rockylinux:9`);
53
- const baseCommand = options.dev ? 'node bin' : 'underpost';
54
- const baseCommandOption = options.dev ? ' --dev' : '';
55
- const IMAGE_NAME = `rockylinux9-underpost`;
56
- const IMAGE_NAME_FULL = `${IMAGE_NAME}:${options.version ? options.version : Underpost.version}`;
57
- let LOAD_TYPE = '';
58
- if (options.kindLoad === true) {
59
- LOAD_TYPE = `--kind-load`;
60
- } else if (options.kubeadmLoad === true) {
61
- LOAD_TYPE = `--kubeadm-load`;
62
- } else if (options.k3sLoad === true) {
63
- // Handle K3s load type
64
- LOAD_TYPE = `--k3s-load`;
27
+ /**
28
+ * @method pullBaseImages
29
+ * @description Pulls base images and builds a 'rockylinux9-underpost' image,
30
+ * then loads it into the specified Kubernetes cluster type (Kind, Kubeadm, or K3s).
31
+ * @param {object} options - Options for pulling and loading images.
32
+ * @param {boolean} [options.kind=false] - If true, load image into Kind cluster.
33
+ * @param {boolean} [options.kubeadm=false] - If true, load image into Kubeadm cluster.
34
+ * @param {boolean} [options.k3s=false] - If true, load image into K3s cluster.
35
+ * @param {string} [options.path=false] - Path to the Dockerfile context.
36
+ * @param {boolean} [options.dev=false] - If true, use development mode.
37
+ * @param {string} [options.version=''] - Version tag for the image.
38
+ * @param {string} [options.imageName=''] - Custom name for the image.
39
+ * @memberof UnderpostImage
40
+ */
41
+ pullBaseImages(
42
+ options = {
43
+ kind: false,
44
+ kubeadm: false,
45
+ k3s: false,
46
+ path: false,
47
+ dev: false,
48
+ version: '',
49
+ imageName: '',
50
+ },
51
+ ) {
52
+ shellExec(`sudo podman pull docker.io/library/rockylinux:9`);
53
+ const baseCommand = options.dev ? 'node bin' : 'underpost';
54
+ const baseCommandOption = options.dev ? ' --dev' : '';
55
+ const IMAGE_NAME = options.imageName
56
+ ? `options.imageName${options.version ? `:${options.version}` : ''}`
57
+ : `rockylinux9-underpost:${options.version ? options.version : Underpost.version}`;
58
+ let LOAD_TYPE = '';
59
+ if (options.kind === true) LOAD_TYPE = `--kind`;
60
+ else if (options.kubeadm === true) LOAD_TYPE = `--kubeadm`;
61
+ else if (options.k3s === true) LOAD_TYPE = `--k3s`;
62
+ shellExec(
63
+ `${baseCommand} image${baseCommandOption} --build --podman-save --reset --image-path=. --path ${
64
+ options.path ? options.path : getUnderpostRootPath()
65
+ } --image-name=${IMAGE_NAME} ${LOAD_TYPE}`,
66
+ );
67
+ },
68
+ /**
69
+ * @method build
70
+ * @description Builds a Docker image using Podman, optionally saves it as a tar archive,
71
+ * and loads it into a specified Kubernetes cluster (Kind, Kubeadm, or K3s).
72
+ * @param {object} options - Options for building and loading images.
73
+ * @param {string} [options.path=''] - The path to the directory containing the Dockerfile.
74
+ * @param {string} [options.imageName=''] - The name and tag for the image (e.g., 'my-app:latest').
75
+ * @param {string} [options.version=''] - Version tag for the image.
76
+ * @param {string} [options.imagePath=''] - Directory to save the image tar file.
77
+ * @param {string} [options.dockerfileName=''] - Name of the Dockerfile (defaults to 'Dockerfile').
78
+ * @param {boolean} [options.podmanSave=false] - If true, save the image as a tar archive using Podman.
79
+ * @param {boolean} [options.kind=false] - If true, load the image archive into a Kind cluster.
80
+ * @param {boolean} [options.kubeadm=false] - If true, load the image archive into a Kubeadm cluster (uses 'ctr').
81
+ * @param {boolean} [options.k3s=false] - If true, load the image archive into a K3s cluster (uses 'k3s ctr').
82
+ * @param {boolean} [options.secrets=false] - If true, load secrets from the .env file for the build.
83
+ * @param {string} [options.secretsPath=''] - Custom path to the .env file for secrets.
84
+ * @param {boolean} [options.reset=false] - If true, perform a no-cache build.
85
+ * @param {boolean} [options.dev=false] - If true, use development mode.
86
+ * @memberof UnderpostImage
87
+ */
88
+ build(
89
+ options = {
90
+ path: '',
91
+ imageName: '',
92
+ version: '',
93
+ imagePath: '',
94
+ dockerfileName: '',
95
+ podmanSave: false,
96
+ kind: false,
97
+ kubeadm: false,
98
+ k3s: false,
99
+ secrets: false,
100
+ secretsPath: '',
101
+ reset: false,
102
+ dev: false,
103
+ },
104
+ ) {
105
+ let {
106
+ path,
107
+ imageName,
108
+ version,
109
+ imagePath,
110
+ dockerfileName,
111
+ podmanSave,
112
+ secrets,
113
+ secretsPath,
114
+ kind,
115
+ kubeadm,
116
+ k3s,
117
+ reset,
118
+ dev,
119
+ } = options;
120
+ if (!path) path = '.';
121
+ if (!imageName) imageName = `rockylinux9-underpost:${Underpost.version}`;
122
+ if (!version) version = 'latest';
123
+ version = imageName && imageName.match(':') ? '' : `:${version}`;
124
+ const podManImg = `localhost/${imageName}${version}`;
125
+ if (imagePath && typeof imagePath === 'string' && !fs.existsSync(imagePath))
126
+ fs.mkdirSync(imagePath, { recursive: true });
127
+ const tarFile = `${imagePath}/${imageName.replace(':', '_')}.tar`;
128
+ let secretsInput = ' ';
129
+ let secretDockerInput = '';
130
+ let cache = '';
131
+ if (secrets === true) {
132
+ const envObj = dotenv.parse(
133
+ fs.readFileSync(
134
+ secretsPath && typeof secretsPath === 'string' ? secretsPath : `${getNpmRootPath()}/underpost/.env`,
135
+ 'utf8',
136
+ ),
137
+ );
138
+ for (const key of Object.keys(envObj)) {
139
+ secretsInput += ` && export ${key}="${envObj[key]}" `; // Example: $(cat gitlab-token.txt)
140
+ secretDockerInput += ` --secret id=${key},env=${key} \ `;
65
141
  }
66
-
142
+ }
143
+ if (reset === true) cache += ' --rm --no-cache';
144
+ if (path && typeof path === 'string')
67
145
  shellExec(
68
- `${baseCommand} dockerfile-image-build${baseCommandOption} --podman-save --reset --image-path=. --path ${
69
- options.path ?? getUnderpostRootPath()
70
- } --image-name=${IMAGE_NAME_FULL} ${LOAD_TYPE}`,
146
+ `cd ${path}${secretsInput}&& sudo podman build -f ./${
147
+ dockerfileName && typeof dockerfileName === 'string' ? dockerfileName : 'Dockerfile'
148
+ } -t ${imageName} --pull=never --cap-add=CAP_AUDIT_WRITE${cache}${secretDockerInput} --network host`,
71
149
  );
72
- },
73
- /**
74
- * @method build
75
- * @description Builds a Docker image using Podman, optionally saves it as a tar archive,
76
- * and loads it into a specified Kubernetes cluster (Kind, Kubeadm, or K3s).
77
- * @param {object} options - Options for building and loading images.
78
- * @param {string} [options.path=''] - The path to the directory containing the Dockerfile.
79
- * @param {string} [options.imageName=''] - The name and tag for the image (e.g., 'my-app:latest').
80
- * @param {string} [options.imagePath=''] - Directory to save the image tar file.
81
- * @param {string} [options.dockerfileName=''] - Name of the Dockerfile (defaults to 'Dockerfile').
82
- * @param {boolean} [options.podmanSave=false] - If true, save the image as a tar archive using Podman.
83
- * @param {boolean} [options.kindLoad=false] - If true, load the image archive into a Kind cluster.
84
- * @param {boolean} [options.kubeadmLoad=false] - If true, load the image archive into a Kubeadm cluster (uses 'ctr').
85
- * @param {boolean} [options.k3sLoad=false] - If true, load the image archive into a K3s cluster (uses 'k3s ctr').
86
- * @param {boolean} [options.secrets=false] - If true, load secrets from the .env file for the build.
87
- * @param {string} [options.secretsPath=''] - Custom path to the .env file for secrets.
88
- * @param {boolean} [options.reset=false] - If true, perform a no-cache build.
89
- * @param {boolean} [options.dev=false] - If true, use development mode.
90
- * @memberof UnderpostImage
91
- */
92
- build(
93
- options = {
94
- path: '',
95
- imageName: '',
96
- imagePath: '',
97
- dockerfileName: '',
98
- podmanSave: false,
99
- kindLoad: false,
100
- kubeadmLoad: false,
101
- k3sLoad: false,
102
- secrets: false,
103
- secretsPath: '',
104
- reset: false,
105
- dev: false,
106
- },
107
- ) {
108
- let {
109
- path,
110
- imageName,
111
- imagePath,
112
- dockerfileName,
113
- podmanSave,
114
- secrets,
115
- secretsPath,
116
- kindLoad,
117
- kubeadmLoad,
118
- k3sLoad,
119
- reset,
120
- dev,
121
- } = options;
122
- if (!path) path = '.';
123
- const podManImg = `localhost/${imageName}`;
124
- if (imagePath && typeof imagePath === 'string' && !fs.existsSync(imagePath))
125
- fs.mkdirSync(imagePath, { recursive: true });
126
- const tarFile = `${imagePath}/${imageName.replace(':', '_')}.tar`;
127
- let secretsInput = ' ';
128
- let secretDockerInput = '';
129
- let cache = '';
130
- if (secrets === true) {
131
- const envObj = dotenv.parse(
132
- fs.readFileSync(
133
- secretsPath && typeof secretsPath === 'string' ? secretsPath : `${getNpmRootPath()}/underpost/.env`,
134
- 'utf8',
135
- ),
136
- );
137
- for (const key of Object.keys(envObj)) {
138
- secretsInput += ` && export ${key}="${envObj[key]}" `; // Example: $(cat gitlab-token.txt)
139
- secretDockerInput += ` --secret id=${key},env=${key} \ `;
140
- }
141
- }
142
- if (reset === true) cache += ' --rm --no-cache';
143
- if (path && typeof path === 'string')
144
- shellExec(
145
- `cd ${path}${secretsInput}&& sudo podman build -f ./${
146
- dockerfileName && typeof dockerfileName === 'string' ? dockerfileName : 'Dockerfile'
147
- } -t ${imageName} --pull=never --cap-add=CAP_AUDIT_WRITE${cache}${secretDockerInput} --network host`,
148
- );
149
150
 
150
- if (podmanSave === true) {
151
- if (fs.existsSync(tarFile)) fs.removeSync(tarFile);
152
- shellExec(`podman save -o ${tarFile} ${podManImg}`);
153
- }
154
- if (kindLoad === true) shellExec(`sudo kind load image-archive ${tarFile}`);
155
- if (kubeadmLoad === true) {
156
- // Use 'ctr' for Kubeadm
157
- shellExec(`sudo ctr -n k8s.io images import ${tarFile}`);
158
- }
159
- if (k3sLoad === true) {
160
- // Use 'k3s ctr' for K3s
161
- shellExec(`sudo k3s ctr images import ${tarFile}`);
162
- }
163
- },
151
+ if (podmanSave === true) {
152
+ if (fs.existsSync(tarFile)) fs.removeSync(tarFile);
153
+ shellExec(`podman save -o ${tarFile} ${podManImg}`);
154
+ }
155
+ if (kind === true) shellExec(`sudo kind load image-archive ${tarFile}`);
156
+ else if (kubeadm === true) shellExec(`sudo ctr -n k8s.io images import ${tarFile}`);
157
+ else if (k3s === true) shellExec(`sudo k3s ctr images import ${tarFile}`);
158
+ },
159
+ /**
160
+ * @method list
161
+ * @description Lists currently loaded Docker images in the specified Kubernetes cluster node.
162
+ * @param {object} options - Options for listing loaded images.
163
+ * @param {string} [options.nodeName='kind-worker'] - The name of the node to query.
164
+ * @param {string} [options.namespace=''] - The namespace to filter images (if applicable).
165
+ * @param {boolean} [options.spec=false] - If true, include detailed specifications of each image.
166
+ * @param {boolean} [options.log=false] - If true, log the list of images to the console.
167
+ * @param {boolean} [options.k3s=false] - If true, list images from a K3s cluster.
168
+ * @param {boolean} [options.kubeadm=false] - If true, list images from a Kubeadm cluster.
169
+ * @param {boolean} [options.kind=false] - If true, list images from a Kind cluster.
170
+ * @returns {Array} - An array of loaded images information.
171
+ * @memberof UnderpostImage
172
+ */
173
+ list(options = { nodeName: '', namespace: '', spec: false, log: false, k3s: false, kubeadm: false, kind: false }) {
174
+ if ((options.kubeadm === true || options.k3s === true) && !options.nodeName)
175
+ options.nodeName = shellExec('echo $HOSTNAME', { stdout: true, silent: true }).trim();
176
+ const list = Underpost.deploy.getCurrentLoadedImages(
177
+ options.nodeName ? options.nodeName : 'kind-worker',
178
+ options,
179
+ );
180
+ if (options.log) console.table(list);
181
+ return list;
182
+ },
183
+ /**
184
+ * @method rm
185
+ * @description Removes a specified Docker image from the specified Kubernetes cluster type (Kind, Kubeadm, or K3s).
186
+ * @param {object} options - Options for removing the image.
187
+ * @param {string} [options.imageName=''] - The name and tag of the image to be removed.
188
+ * @param {boolean} [options.k3s=false] - If true, remove the image from a K3s cluster.
189
+ * @param {boolean} [options.kubeadm=false] - If true, remove the image from a Kubeadm cluster.
190
+ * @param {boolean} [options.kind=false] - If true, remove the image from a Kind cluster.
191
+ * @memberof UnderpostImage
192
+ */
193
+ rm(options = { imageName: '', k3s: false, kubeadm: false, kind: false }) {
194
+ let { imageName, k3s, kubeadm, kind } = options;
195
+ if (kind === true) {
196
+ shellExec(`docker exec -i kind-control-plane crictl rmi ${imageName}`);
197
+ shellExec(`docker exec -i kind-worker crictl rmi ${imageName}`);
198
+ } else if (kubeadm === true) {
199
+ shellExec(`crictl rmi ${imageName}`);
200
+ } else if (k3s === true) {
201
+ shellExec(`sudo k3s ctr images rm ${imageName}`);
202
+ }
203
+ },
204
+ /**
205
+ * @method pullDockerHubImage
206
+ * @description Pulls a Docker image from Docker Hub and loads it into the specified Kubernetes cluster type (Kind, Kubeadm, or K3s).
207
+ * @param {object} options - Options for pulling and loading the image.
208
+ * @param {boolean} [options.k3s=false] - If true, load the image into a K3s cluster.
209
+ * @param {boolean} [options.kubeadm=false] - If true, load the image into a Kubeadm cluster.
210
+ * @param {boolean} [options.kind=false] - If true, load the image into a Kind cluster.
211
+ * @param {string} [options.dockerhubImage=''] - The name of the Docker Hub image to be pulled.
212
+ * @param {string} [options.version=''] - The version tag of the image to be pulled.
213
+ * @memberof UnderpostImage
214
+ */
215
+ pullDockerHubImage(options = { k3s: false, kubeadm: false, kind: false, dockerhubImage: '', version: '' }) {
216
+ if (options.dockerhubImage === 'underpost') {
217
+ options.dockerhubImage = 'underpost/underpost-engine';
218
+ if (!options.version) options.version = Underpost.version;
219
+ }
220
+ if (!options.version) options.version = 'latest';
221
+ const version = options.dockerhubImage && options.dockerhubImage.match(':') ? '' : `:${options.version}`;
222
+ const image = `${options.dockerhubImage}${version}`;
223
+ if (options.kind === true) {
224
+ shellExec(`docker pull ${image}`);
225
+ shellExec(`sudo kind load docker-image ${image}`);
226
+ } else {
227
+ shellExec(`sudo crictl pull ${image}`);
228
+ }
164
229
  },
165
230
  };
166
231
  }