underpost 3.2.10 → 3.2.11
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/.vscode/extensions.json +9 -9
- package/.vscode/settings.json +12 -1
- package/CHANGELOG.md +74 -1
- package/CLI-HELP.md +80 -26
- package/README.md +3 -3
- package/bin/build.js +9 -6
- package/bin/build.template.js +187 -0
- package/bin/deploy.js +29 -18
- package/conf.js +1 -4
- package/manifests/cronjobs/dd-cron/dd-cron-backup.yaml +1 -1
- package/manifests/cronjobs/dd-cron/dd-cron-dns.yaml +1 -1
- package/manifests/deployment/dd-default-development/deployment.yaml +2 -2
- package/manifests/deployment/dd-test-development/deployment.yaml +2 -2
- package/manifests/lxd/lxd-admin-profile.yaml +12 -3
- package/manifests/mongodb-4.4/headless-service.yaml +10 -0
- package/manifests/mongodb-4.4/kustomization.yaml +3 -1
- package/manifests/mongodb-4.4/mongodb-nodeport.yaml +17 -0
- package/manifests/mongodb-4.4/pv-pvc.yaml +10 -14
- package/manifests/mongodb-4.4/statefulset.yaml +79 -0
- package/manifests/mongodb-4.4/storage-class.yaml +9 -0
- package/manifests/valkey/statefulset.yaml +1 -1
- package/manifests/valkey/valkey-nodeport.yaml +17 -0
- package/package.json +3 -3
- package/scripts/ipxe-setup.sh +52 -49
- package/scripts/k3s-node-setup.sh +84 -68
- package/scripts/lxd-vm-setup.sh +193 -8
- package/scripts/maas-nat-firewalld.sh +145 -0
- package/src/cli/baremetal.js +115 -93
- package/src/cli/cluster.js +548 -221
- package/src/cli/deploy.js +131 -166
- package/src/cli/fs.js +11 -3
- package/src/cli/index.js +75 -17
- package/src/cli/lxd.js +1034 -240
- package/src/cli/monitor.js +9 -3
- package/src/cli/release.js +72 -36
- package/src/cli/repository.js +10 -16
- package/src/cli/run.js +70 -53
- package/src/cli/secrets.js +11 -2
- package/src/client/components/core/Auth.js +4 -3
- package/src/client/components/core/ClientEvents.js +76 -0
- package/src/client/components/core/EventBus.js +4 -0
- package/src/client/components/core/Modal.js +82 -41
- package/src/db/DataBaseProvider.js +9 -9
- package/src/db/mariadb/MariaDB.js +2 -1
- package/src/db/mongo/MongoBootstrap.js +592 -522
- package/src/db/mongo/MongooseDB.js +19 -15
- package/src/index.js +1 -1
- package/src/server/conf.js +62 -15
- package/src/server/proxy.js +9 -2
- package/src/server/start.js +7 -3
- package/src/server/valkey.js +2 -0
- package/bin/file.js +0 -220
- package/bin/vs.js +0 -74
- package/bin/zed.js +0 -84
package/src/cli/deploy.js
CHANGED
|
@@ -78,7 +78,8 @@ class UnderpostDeploy {
|
|
|
78
78
|
return `
|
|
79
79
|
- conditions:
|
|
80
80
|
- prefix: ${path}
|
|
81
|
-
${
|
|
81
|
+
${
|
|
82
|
+
pathRewritePolicy
|
|
82
83
|
? `pathRewritePolicy:
|
|
83
84
|
replacePrefix:
|
|
84
85
|
${pathRewritePolicy.map(
|
|
@@ -88,26 +89,30 @@ class UnderpostDeploy {
|
|
|
88
89
|
).join(`
|
|
89
90
|
`)}`
|
|
90
91
|
: ''
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
}`
|
|
92
|
+
}${
|
|
93
|
+
timeoutPolicy
|
|
94
|
+
? `\n timeoutPolicy:\n${timeoutPolicy.response ? ` response: ${timeoutPolicy.response}\n` : ''}${
|
|
95
|
+
timeoutPolicy.idle ? ` idle: ${timeoutPolicy.idle}\n` : ''
|
|
96
|
+
}`
|
|
94
97
|
: ''
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
}`
|
|
98
|
+
}${
|
|
99
|
+
retryPolicy
|
|
100
|
+
? `\n retryPolicy:\n${retryPolicy.count !== undefined ? ` count: ${retryPolicy.count}\n` : ''}${
|
|
101
|
+
retryPolicy.perTryTimeout ? ` perTryTimeout: ${retryPolicy.perTryTimeout}\n` : ''
|
|
102
|
+
}`
|
|
98
103
|
: ''
|
|
99
|
-
|
|
104
|
+
}
|
|
100
105
|
enableWebsockets: true
|
|
101
106
|
services:
|
|
102
107
|
${deploymentVersions
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
108
|
+
.map(
|
|
109
|
+
(version, i) =>
|
|
110
|
+
` - name: ${serviceId ? serviceId : `${deployId}-${env}-${version}-service`}
|
|
106
111
|
port: ${port}
|
|
107
112
|
weight: ${i === 0 ? 100 : 0}
|
|
108
113
|
`,
|
|
109
|
-
|
|
110
|
-
|
|
114
|
+
)
|
|
115
|
+
.join('')}`;
|
|
111
116
|
},
|
|
112
117
|
/**
|
|
113
118
|
* Creates a YAML deployment configuration for a deployment.
|
|
@@ -154,17 +159,17 @@ class UnderpostDeploy {
|
|
|
154
159
|
cmd =
|
|
155
160
|
pullBundle || skipFullBuild
|
|
156
161
|
? [
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
+
// When pullBundle (or skipFullBuild) is set the container pulls the pre-built client
|
|
163
|
+
// bundle from Cloudinary (push-bundle must have been run on the dev machine beforehand).
|
|
164
|
+
`underpost secret underpost --create-from-env`,
|
|
165
|
+
`underpost start --build --run --pull-bundle --skip-full-build ${deployId} ${env}`,
|
|
166
|
+
]
|
|
162
167
|
: [
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
+
// `npm install -g npm@11.2.0`,
|
|
169
|
+
// `npm install -g underpost`,
|
|
170
|
+
`underpost secret underpost --create-from-env`,
|
|
171
|
+
`underpost start --build --run ${deployId} ${env}`,
|
|
172
|
+
];
|
|
168
173
|
const packageJson = JSON.parse(fs.readFileSync('./package.json', 'utf8'));
|
|
169
174
|
if (!volumes) volumes = [];
|
|
170
175
|
const confVolume = fs.existsSync(`./engine-private/conf/${deployId}/conf.volume.json`)
|
|
@@ -199,12 +204,14 @@ spec:
|
|
|
199
204
|
envFrom:
|
|
200
205
|
- secretRef:
|
|
201
206
|
name: underpost-config
|
|
202
|
-
${
|
|
203
|
-
|
|
207
|
+
${
|
|
208
|
+
containerPort
|
|
209
|
+
? ` ports:
|
|
204
210
|
- containerPort: ${containerPort}
|
|
205
211
|
`
|
|
206
|
-
|
|
207
|
-
|
|
212
|
+
: ''
|
|
213
|
+
}${
|
|
214
|
+
resources
|
|
208
215
|
? ` resources:
|
|
209
216
|
requests:
|
|
210
217
|
memory: "${resources.requests.memory}"
|
|
@@ -213,46 +220,50 @@ ${containerPort
|
|
|
213
220
|
memory: "${resources.limits.memory}"
|
|
214
221
|
cpu: "${resources.limits.cpu}"`
|
|
215
222
|
: ''
|
|
216
|
-
|
|
223
|
+
}
|
|
217
224
|
command:
|
|
218
225
|
- /bin/sh
|
|
219
226
|
- -c
|
|
220
227
|
- >
|
|
221
228
|
${cmd.join(' &&\n ')}
|
|
222
|
-
${
|
|
223
|
-
|
|
229
|
+
${
|
|
230
|
+
readinessProbe
|
|
231
|
+
? ` readinessProbe:
|
|
224
232
|
${JSON.stringify(readinessProbe, null, 2)
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
233
|
+
.split('\n')
|
|
234
|
+
.map((l) => ' ' + l)
|
|
235
|
+
.join('\n')}
|
|
228
236
|
`
|
|
229
|
-
|
|
230
|
-
|
|
237
|
+
: ''
|
|
238
|
+
}${
|
|
239
|
+
livenessProbe
|
|
231
240
|
? ` livenessProbe:
|
|
232
241
|
${JSON.stringify(livenessProbe, null, 2)
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
242
|
+
.split('\n')
|
|
243
|
+
.map((l) => ' ' + l)
|
|
244
|
+
.join('\n')}
|
|
236
245
|
`
|
|
237
246
|
: ''
|
|
238
|
-
|
|
247
|
+
}${
|
|
248
|
+
lifecycle
|
|
239
249
|
? ` lifecycle:
|
|
240
250
|
${JSON.stringify(lifecycle, null, 2)
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
251
|
+
.split('\n')
|
|
252
|
+
.map((l) => ' ' + l)
|
|
253
|
+
.join('\n')}
|
|
244
254
|
`
|
|
245
255
|
: ''
|
|
246
|
-
|
|
256
|
+
}
|
|
247
257
|
|
|
248
|
-
${
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
258
|
+
${
|
|
259
|
+
volumes.length > 0
|
|
260
|
+
? Underpost.deploy
|
|
261
|
+
.volumeFactory(volumes.map((v) => ((v.version = `${deployId}-${env}-${suffix}`), v)))
|
|
262
|
+
.render.split(`\n`)
|
|
263
|
+
.map((l) => ' ' + l)
|
|
264
|
+
.join(`\n`)
|
|
265
|
+
: ''
|
|
266
|
+
}
|
|
256
267
|
---
|
|
257
268
|
apiVersion: v1
|
|
258
269
|
kind: Service
|
|
@@ -312,19 +323,19 @@ spec:
|
|
|
312
323
|
for (const deploymentVersion of deploymentVersions) {
|
|
313
324
|
deploymentYamlParts += `---
|
|
314
325
|
${Underpost.deploy
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
326
|
+
.deploymentYamlPartsFactory({
|
|
327
|
+
deployId,
|
|
328
|
+
env,
|
|
329
|
+
suffix: deploymentVersion,
|
|
330
|
+
replicas,
|
|
331
|
+
image,
|
|
332
|
+
namespace: options.namespace,
|
|
333
|
+
cmd: options.cmd ? options.cmd.split(',').map((c) => c.trim()) : undefined,
|
|
334
|
+
skipFullBuild: options.skipFullBuild,
|
|
335
|
+
pullBundle: options.pullBundle,
|
|
336
|
+
imagePullPolicy: options.imagePullPolicy,
|
|
337
|
+
})
|
|
338
|
+
.replace('{{ports}}', buildKindPorts(fromPort, toPort))}
|
|
328
339
|
`;
|
|
329
340
|
}
|
|
330
341
|
fs.writeFileSync(`./engine-private/conf/${deployId}/build/${env}/deployment.yaml`, deploymentYamlParts, 'utf8');
|
|
@@ -376,20 +387,20 @@ ${Underpost.deploy
|
|
|
376
387
|
let proxyRoutes = '';
|
|
377
388
|
const globalTimeoutPolicy =
|
|
378
389
|
(options.timeoutResponse && options.timeoutResponse !== '') ||
|
|
379
|
-
|
|
390
|
+
(options.timeoutIdle && options.timeoutIdle !== '')
|
|
380
391
|
? {
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
392
|
+
response: options.timeoutResponse,
|
|
393
|
+
idle: options.timeoutIdle,
|
|
394
|
+
}
|
|
384
395
|
: undefined;
|
|
385
396
|
const globalRetryPolicy =
|
|
386
397
|
options.retryCount ||
|
|
387
|
-
|
|
388
|
-
|
|
398
|
+
options.retryCount === 0 ||
|
|
399
|
+
(options.retryPerTryTimeout && options.retryPerTryTimeout !== '')
|
|
389
400
|
? {
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
401
|
+
count: options.retryCount,
|
|
402
|
+
perTryTimeout: options.retryPerTryTimeout,
|
|
403
|
+
}
|
|
393
404
|
: undefined;
|
|
394
405
|
if (!options.disableDeploymentProxy)
|
|
395
406
|
for (const conditionObj of pathPortAssignment) {
|
|
@@ -577,12 +588,13 @@ metadata:
|
|
|
577
588
|
namespace: ${options.namespace}
|
|
578
589
|
spec:
|
|
579
590
|
virtualhost:
|
|
580
|
-
fqdn: ${host}${
|
|
581
|
-
|
|
582
|
-
|
|
591
|
+
fqdn: ${host}${
|
|
592
|
+
env === 'development'
|
|
593
|
+
? ''
|
|
594
|
+
: `
|
|
583
595
|
tls:
|
|
584
596
|
secretName: ${host}`
|
|
585
|
-
|
|
597
|
+
}
|
|
586
598
|
routes:`;
|
|
587
599
|
},
|
|
588
600
|
|
|
@@ -604,13 +616,11 @@ spec:
|
|
|
604
616
|
* @param {string} options.traffic - Traffic status for the deployment.
|
|
605
617
|
* @param {string} options.replicas - Number of replicas for the deployment.
|
|
606
618
|
* @param {string} options.node - Node name for resource allocation.
|
|
607
|
-
* @param {boolean} options.restoreHosts - Whether to restore the hosts file.
|
|
608
619
|
* @param {boolean} options.disableUpdateDeployment - Whether to disable deployment updates.
|
|
609
620
|
* @param {boolean} options.disableUpdateProxy - Whether to disable proxy updates.
|
|
610
621
|
* @param {boolean} options.disableDeploymentProxy - Whether to disable deployment proxy.
|
|
611
622
|
* @param {boolean} options.disableUpdateVolume - Whether to disable volume updates.
|
|
612
623
|
* @param {boolean} options.status - Whether to display deployment status.
|
|
613
|
-
* @param {boolean} options.etcHosts - Whether to display the /etc/hosts file.
|
|
614
624
|
* @param {boolean} options.disableUpdateUnderpostConfig - Whether to disable Underpost config updates.
|
|
615
625
|
* @param {string} [options.namespace] - Kubernetes namespace for the deployment.
|
|
616
626
|
* @param {string} [options.timeoutResponse] - Timeout response setting for the deployment.
|
|
@@ -648,13 +658,11 @@ spec:
|
|
|
648
658
|
traffic: '',
|
|
649
659
|
replicas: '',
|
|
650
660
|
node: '',
|
|
651
|
-
restoreHosts: false,
|
|
652
661
|
disableUpdateDeployment: false,
|
|
653
662
|
disableUpdateProxy: false,
|
|
654
663
|
disableDeploymentProxy: false,
|
|
655
664
|
disableUpdateVolume: false,
|
|
656
665
|
status: false,
|
|
657
|
-
etcHosts: false,
|
|
658
666
|
disableUpdateUnderpostConfig: false,
|
|
659
667
|
namespace: '',
|
|
660
668
|
timeoutResponse: '',
|
|
@@ -738,14 +746,6 @@ EOF`);
|
|
|
738
746
|
return;
|
|
739
747
|
}
|
|
740
748
|
if (!options.disableUpdateUnderpostConfig) Underpost.deploy.configMap(env);
|
|
741
|
-
let renderHosts = '';
|
|
742
|
-
let etcHosts = [];
|
|
743
|
-
if (options.restoreHosts === true) {
|
|
744
|
-
const factoryResult = Underpost.deploy.etcHostFactory(etcHosts);
|
|
745
|
-
renderHosts = factoryResult.renderHosts;
|
|
746
|
-
logger.info(renderHosts);
|
|
747
|
-
return;
|
|
748
|
-
}
|
|
749
749
|
|
|
750
750
|
for (const _deployId of deployList.split(',')) {
|
|
751
751
|
const deployId = _deployId.trim();
|
|
@@ -753,6 +753,10 @@ EOF`);
|
|
|
753
753
|
if (options.expose === true) {
|
|
754
754
|
const kindType = options.kindType ? options.kindType : 'svc';
|
|
755
755
|
const svc = Underpost.kubectl.get(deployId, kindType)[0];
|
|
756
|
+
if (!svc) {
|
|
757
|
+
logger.error(`No ${kindType} found matching '${deployId}', skipping expose`);
|
|
758
|
+
continue;
|
|
759
|
+
}
|
|
756
760
|
const port = options.exposePort
|
|
757
761
|
? parseInt(options.exposePort)
|
|
758
762
|
: options.port
|
|
@@ -802,7 +806,6 @@ EOF`);
|
|
|
802
806
|
if (Underpost.deploy.isValidTLSContext({ host, env, options }))
|
|
803
807
|
shellExec(`sudo kubectl delete Certificate ${host} -n ${namespace} --ignore-not-found`);
|
|
804
808
|
}
|
|
805
|
-
if (!options.remove) etcHosts.push(host);
|
|
806
809
|
}
|
|
807
810
|
|
|
808
811
|
const manifestsPath =
|
|
@@ -823,15 +826,6 @@ EOF`);
|
|
|
823
826
|
shellExec(`sudo kubectl apply -f ./${manifestsPath}/secret.yaml -n ${namespace}`);
|
|
824
827
|
}
|
|
825
828
|
}
|
|
826
|
-
if (options.etcHosts === true) {
|
|
827
|
-
const factoryResult = Underpost.deploy.etcHostFactory(etcHosts);
|
|
828
|
-
renderHosts = factoryResult.renderHosts;
|
|
829
|
-
}
|
|
830
|
-
if (renderHosts)
|
|
831
|
-
logger.info(
|
|
832
|
-
`
|
|
833
|
-
` + renderHosts,
|
|
834
|
-
);
|
|
835
829
|
},
|
|
836
830
|
/**
|
|
837
831
|
* Checks the status of a deployment.
|
|
@@ -1002,10 +996,10 @@ EOF`);
|
|
|
1002
996
|
shellExec(`kubectl delete pv ${pvId} --ignore-not-found`);
|
|
1003
997
|
shellExec(`kubectl apply -f - -n ${namespace} <<EOF
|
|
1004
998
|
${Underpost.deploy.persistentVolumeFactory({
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
999
|
+
hostPath: rootVolumeHostPath,
|
|
1000
|
+
pvcId,
|
|
1001
|
+
namespace,
|
|
1002
|
+
})}
|
|
1009
1003
|
EOF
|
|
1010
1004
|
`);
|
|
1011
1005
|
},
|
|
@@ -1064,22 +1058,23 @@ ${secret ? ` readOnly: true\n` : ''}`;
|
|
|
1064
1058
|
|
|
1065
1059
|
_volumes += `
|
|
1066
1060
|
- name: ${volumeName}
|
|
1067
|
-
${
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1061
|
+
${
|
|
1062
|
+
emptyDir
|
|
1063
|
+
? ` emptyDir: {}`
|
|
1064
|
+
: secret
|
|
1065
|
+
? ` secret:
|
|
1071
1066
|
secretName: ${secret}`
|
|
1072
|
-
|
|
1073
|
-
|
|
1067
|
+
: configMap
|
|
1068
|
+
? ` configMap:
|
|
1074
1069
|
name: ${configMap}`
|
|
1075
|
-
|
|
1076
|
-
|
|
1070
|
+
: claimName
|
|
1071
|
+
? ` persistentVolumeClaim:
|
|
1077
1072
|
claimName: ${claimName}`
|
|
1078
|
-
|
|
1073
|
+
: ` hostPath:
|
|
1079
1074
|
path: ${volumeHostPath}
|
|
1080
1075
|
type: ${volumeType}
|
|
1081
1076
|
`
|
|
1082
|
-
|
|
1077
|
+
}
|
|
1083
1078
|
|
|
1084
1079
|
`;
|
|
1085
1080
|
});
|
|
@@ -1131,42 +1126,6 @@ spec:
|
|
|
1131
1126
|
storage: 5Gi`;
|
|
1132
1127
|
},
|
|
1133
1128
|
|
|
1134
|
-
/**
|
|
1135
|
-
* Creates a hosts file for a deployment.
|
|
1136
|
-
* @param {Array<string>} hosts - List of hosts to be added to the hosts file.
|
|
1137
|
-
* @param {object} options - Options for the hosts file creation.
|
|
1138
|
-
* @param {boolean} options.append - Whether to append to the existing hosts file.
|
|
1139
|
-
* @returns {object} - Object containing the rendered hosts file.
|
|
1140
|
-
* @memberof UnderpostDeploy
|
|
1141
|
-
*/
|
|
1142
|
-
etcHostFactory(hosts = [], options = { append: false }) {
|
|
1143
|
-
hosts = hosts.map((host) => {
|
|
1144
|
-
try {
|
|
1145
|
-
if (!host.startsWith('http')) host = `http://${host}`;
|
|
1146
|
-
const hostname = new URL(host).hostname;
|
|
1147
|
-
logger.info('Hostname extract valid', { host, hostname });
|
|
1148
|
-
return hostname;
|
|
1149
|
-
} catch (e) {
|
|
1150
|
-
logger.warn('No hostname extract valid', host);
|
|
1151
|
-
return host;
|
|
1152
|
-
}
|
|
1153
|
-
});
|
|
1154
|
-
const renderHosts = `127.0.0.1 ${hosts.join(
|
|
1155
|
-
' ',
|
|
1156
|
-
)} localhost localhost.localdomain localhost4 localhost4.localdomain4
|
|
1157
|
-
::1 localhost localhost.localdomain localhost6 localhost6.localdomain6`;
|
|
1158
|
-
|
|
1159
|
-
if (options && options.append && fs.existsSync(`/etc/hosts`)) {
|
|
1160
|
-
fs.writeFileSync(
|
|
1161
|
-
`/etc/hosts`,
|
|
1162
|
-
fs.readFileSync(`/etc/hosts`, 'utf8') +
|
|
1163
|
-
`
|
|
1164
|
-
${renderHosts}`,
|
|
1165
|
-
'utf8',
|
|
1166
|
-
);
|
|
1167
|
-
} else fs.writeFileSync(`/etc/hosts`, renderHosts, 'utf8');
|
|
1168
|
-
return { renderHosts };
|
|
1169
|
-
},
|
|
1170
1129
|
/**
|
|
1171
1130
|
* Checks if a TLS context is valid.
|
|
1172
1131
|
* @param {object} options - Options for the check.
|
|
@@ -1191,13 +1150,13 @@ ${renderHosts}`,
|
|
|
1191
1150
|
* `src/client/public/nexodev/docs/references/Deploy custom instance to K8S.md`.
|
|
1192
1151
|
*
|
|
1193
1152
|
* Container-status:
|
|
1194
|
-
* `underpost config get container-status` is
|
|
1195
|
-
* the display column and
|
|
1196
|
-
*
|
|
1197
|
-
*
|
|
1198
|
-
*
|
|
1199
|
-
*
|
|
1200
|
-
*
|
|
1153
|
+
* `underpost config get container-status` is read from each pod for both
|
|
1154
|
+
* the display column and as a second ready gate alongside the K8S Ready
|
|
1155
|
+
* condition. Both must be satisfied before the monitor exits:
|
|
1156
|
+
* 1. K8S readinessProbe (TCP socket) — ensures the port is bound.
|
|
1157
|
+
* 2. container-status == `<deploy>-<env>-running-deployment` — ensures
|
|
1158
|
+
* the application has completed its own startup sequence.
|
|
1159
|
+
* Early-abort on `error` container-status remains in effect.
|
|
1201
1160
|
*
|
|
1202
1161
|
* @param {string} deployId - Deployment ID for which the ready status is being monitored.
|
|
1203
1162
|
* @param {string} env - Environment for which the ready status is being monitored.
|
|
@@ -1234,10 +1193,13 @@ ${renderHosts}`,
|
|
|
1234
1193
|
}
|
|
1235
1194
|
};
|
|
1236
1195
|
|
|
1237
|
-
|
|
1238
1196
|
for (let i = 0; i < maxIterations; i++) {
|
|
1239
1197
|
const result = await Underpost.deploy.checkDeploymentReadyStatus(
|
|
1240
|
-
deployId,
|
|
1198
|
+
deployId,
|
|
1199
|
+
env,
|
|
1200
|
+
targetTraffic,
|
|
1201
|
+
ignorePods,
|
|
1202
|
+
namespace,
|
|
1241
1203
|
);
|
|
1242
1204
|
|
|
1243
1205
|
const allPods = [...result.readyPods, ...result.notReadyPods];
|
|
@@ -1246,20 +1208,22 @@ ${renderHosts}`,
|
|
|
1246
1208
|
for (const pod of allPods) {
|
|
1247
1209
|
if (!pod?.NAME) continue;
|
|
1248
1210
|
const status = readContainerStatus(pod.NAME);
|
|
1249
|
-
if (status === 'error')
|
|
1250
|
-
throw new Error(`Pod ${pod.NAME} has error status`);
|
|
1211
|
+
if (status === 'error') throw new Error(`Pod ${pod.NAME} has error status`);
|
|
1251
1212
|
podStatusCache.set(pod.NAME, status);
|
|
1252
1213
|
}
|
|
1253
1214
|
|
|
1254
1215
|
const allPodsK8sReady = allPods.length > 0 && result.notReadyPods.length === 0;
|
|
1255
1216
|
|
|
1217
|
+
const allPodsStatusReady =
|
|
1218
|
+
allPods.length > 0 && allPods.every((pod) => podStatusCache.get(pod.NAME) === expectedContainerStatus);
|
|
1219
|
+
|
|
1256
1220
|
// Print snapshot for every pod — annotate when container-status hasn't caught
|
|
1257
|
-
// up to the K8S Ready condition
|
|
1221
|
+
// up to the K8S Ready condition yet.
|
|
1258
1222
|
for (const pod of allPods) {
|
|
1259
1223
|
const status = podStatusCache.get(pod.NAME) || containerStatusDefault;
|
|
1260
1224
|
const podStatus = pod.STATUS || 'Unknown';
|
|
1261
1225
|
const statusMatchesExpected = status === expectedContainerStatus;
|
|
1262
|
-
const statusDisplay = statusMatchesExpected ? status : `${status} (
|
|
1226
|
+
const statusDisplay = statusMatchesExpected ? status : `${status} (pending)`;
|
|
1263
1227
|
|
|
1264
1228
|
console.log(
|
|
1265
1229
|
'Target pod:',
|
|
@@ -1271,10 +1235,11 @@ ${renderHosts}`,
|
|
|
1271
1235
|
);
|
|
1272
1236
|
}
|
|
1273
1237
|
|
|
1274
|
-
//
|
|
1275
|
-
//
|
|
1276
|
-
//
|
|
1277
|
-
|
|
1238
|
+
// Both K8S readinessProbe AND container-status must be satisfied before
|
|
1239
|
+
// declaring the deployment ready. The TCP probe ensures the port is bound;
|
|
1240
|
+
// container-status == running-deployment ensures the application has
|
|
1241
|
+
// completed its own startup sequence so traffic is not switched prematurely.
|
|
1242
|
+
if (allPodsK8sReady && allPodsStatusReady) {
|
|
1278
1243
|
logger.info(`${tag} | All pods Ready (K8S readinessProbe satisfied)`);
|
|
1279
1244
|
return result;
|
|
1280
1245
|
}
|
package/src/cli/fs.js
CHANGED
|
@@ -127,7 +127,11 @@ class UnderpostFileStorage {
|
|
|
127
127
|
if (options.git === true) {
|
|
128
128
|
const gitPath = hasPathFilter ? basePath : '.';
|
|
129
129
|
shellExec(`cd ${gitPath} && git add .`);
|
|
130
|
-
shellExec(`underpost cmt ${gitPath} feat
|
|
130
|
+
shellExec(`underpost cmt ${gitPath} feat`, {
|
|
131
|
+
silentOnError: true,
|
|
132
|
+
silent: true,
|
|
133
|
+
disableLog: true,
|
|
134
|
+
});
|
|
131
135
|
}
|
|
132
136
|
|
|
133
137
|
return;
|
|
@@ -155,7 +159,7 @@ class UnderpostFileStorage {
|
|
|
155
159
|
if (options.git === true) {
|
|
156
160
|
Underpost.repo.initLocalRepo({ path });
|
|
157
161
|
shellExec(`cd ${path} && git add . && git commit -m "Base pull state"`, {
|
|
158
|
-
silentOnError: true
|
|
162
|
+
silentOnError: true,
|
|
159
163
|
});
|
|
160
164
|
}
|
|
161
165
|
} else {
|
|
@@ -175,7 +179,11 @@ class UnderpostFileStorage {
|
|
|
175
179
|
Underpost.fs.writeStorageConf(storage, storageConf);
|
|
176
180
|
if (options.git === true) {
|
|
177
181
|
shellExec(`cd ${path} && git add .`);
|
|
178
|
-
shellExec(`underpost cmt ${path} feat
|
|
182
|
+
shellExec(`underpost cmt ${path} feat`, {
|
|
183
|
+
silentOnError: true,
|
|
184
|
+
silent: true,
|
|
185
|
+
disableLog: true,
|
|
186
|
+
});
|
|
179
187
|
}
|
|
180
188
|
},
|
|
181
189
|
/**
|