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.
- package/.github/workflows/pwa-microservices-template-page.cd.yml +5 -4
- package/.github/workflows/release.cd.yml +7 -7
- package/README.md +7 -8
- package/bin/build.js +6 -1
- package/bin/deploy.js +2 -196
- package/cli.md +154 -80
- package/manifests/deployment/dd-default-development/deployment.yaml +4 -4
- package/manifests/deployment/dd-test-development/deployment.yaml +2 -2
- package/package.json +1 -1
- package/scripts/disk-clean.sh +216 -0
- package/scripts/rocky-setup.sh +1 -0
- package/scripts/ssh-cluster-info.sh +4 -3
- package/src/cli/cluster.js +1 -1
- package/src/cli/db.js +1143 -201
- package/src/cli/deploy.js +93 -24
- package/src/cli/env.js +2 -2
- package/src/cli/image.js +198 -133
- package/src/cli/index.js +111 -44
- package/src/cli/lxd.js +73 -74
- package/src/cli/monitor.js +20 -9
- package/src/cli/repository.js +212 -5
- package/src/cli/run.js +207 -74
- package/src/cli/ssh.js +642 -14
- package/src/client/components/core/CommonJs.js +0 -1
- package/src/db/mongo/MongooseDB.js +5 -1
- package/src/index.js +1 -1
- package/src/monitor.js +11 -1
- package/src/server/backup.js +1 -1
- package/src/server/conf.js +1 -1
- package/src/server/dns.js +242 -1
- package/src/server/process.js +6 -1
- package/src/server/start.js +2 -0
- package/scripts/snap-clean.sh +0 -26
- package/src/client/public/default/plantuml/client-conf.svg +0 -1
- package/src/client/public/default/plantuml/client-schema.svg +0 -1
- package/src/client/public/default/plantuml/cron-conf.svg +0 -1
- package/src/client/public/default/plantuml/cron-schema.svg +0 -1
- package/src/client/public/default/plantuml/server-conf.svg +0 -1
- package/src/client/public/default/plantuml/server-schema.svg +0 -1
- package/src/client/public/default/plantuml/ssr-conf.svg +0 -1
- 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
|
|
499
|
-
const
|
|
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
|
|
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
|
-
|
|
661
|
-
shellExec(`sudo kubectl exec -i ${NAME} -- sh -c "${cmd}"`, {
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
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
|
|
899
|
-
|
|
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(
|
|
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
|
|
986
|
+
`${iteratorTag} | Deployment in progress... | Delay number monitor iterations: ${checkStatusIteration}`,
|
|
924
987
|
);
|
|
925
988
|
}
|
|
926
|
-
logger.info(
|
|
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',
|
|
936
|
-
if (
|
|
1004
|
+
getCurrentLoadedImages(node = 'kind-worker', options = { spec: false, namespace: '' }) {
|
|
1005
|
+
if (options.spec) {
|
|
937
1006
|
const raw = shellExec(
|
|
938
|
-
`kubectl get pods
|
|
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.
|
|
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.
|
|
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
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
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
|
-
|
|
69
|
-
|
|
70
|
-
}
|
|
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
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
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
|
}
|