underpost 3.2.14 → 3.2.22
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/CHANGELOG.md +94 -1
- package/CLI-HELP.md +99 -30
- package/README.md +2 -2
- package/bin/build.js +12 -1
- package/bin/build.template.js +4 -2
- 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/package.json +1 -1
- package/scripts/link-local-underpost-cli.sh +6 -0
- package/scripts/test-monitor.sh +250 -0
- package/src/cli/deploy.js +200 -54
- package/src/cli/env.js +1 -4
- package/src/cli/index.js +47 -0
- package/src/cli/monitor.js +269 -72
- package/src/cli/release.js +21 -6
- package/src/cli/repository.js +21 -4
- package/src/cli/run.js +44 -4
- package/src/client/components/core/PanelForm.js +44 -44
- package/src/db/mongo/MongooseDB.js +2 -1
- package/src/index.js +1 -1
- package/src/server/conf.js +91 -18
- package/src/server/ipfs-client.js +5 -3
- package/src/server/runtime-status.js +235 -0
- package/src/server/start.js +26 -9
- package/test/deploy-monitor.test.js +132 -69
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
#
|
|
3
|
+
# test-monitor.sh — end-to-end deploy + two-phase monitor smoke test.
|
|
4
|
+
#
|
|
5
|
+
# Two deployment shapes are supported (see
|
|
6
|
+
# src/client/public/nexodev/docs/references/Deploy-Monitor-PRD.md and
|
|
7
|
+
# 'Deploy custom instance to K8S.md'):
|
|
8
|
+
#
|
|
9
|
+
# --mode runtime `underpost start` deploy (e.g. dd-test). Monitored with the
|
|
10
|
+
# HTTP gate: /_internal/ready probes + port-forward status.
|
|
11
|
+
# --mode instance Custom instances from conf.instances.json (e.g. cyberia
|
|
12
|
+
# mmo-server / mmo-client). Monitored with the kubernetes gate
|
|
13
|
+
# (TCP readinessProbe) + exec status transport.
|
|
14
|
+
#
|
|
15
|
+
# Every variable in the DEFAULTS block below is overridable with a flag of the
|
|
16
|
+
# same name (--env, --deploy-id, …). Run with --help for the full list.
|
|
17
|
+
#
|
|
18
|
+
set -euo pipefail
|
|
19
|
+
|
|
20
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
21
|
+
ENGINE_DIR="$(cd "${SCRIPT_DIR}/.." && pwd)"
|
|
22
|
+
cd "$ENGINE_DIR"
|
|
23
|
+
|
|
24
|
+
# ─────────────────────────── DEFAULTS (all overridable) ───────────────────────────
|
|
25
|
+
MODE=runtime # runtime | instance
|
|
26
|
+
ENV=development # development | production
|
|
27
|
+
DEPLOY_ID=dd-test # deploy id (instance mode: parent of conf.instances.json)
|
|
28
|
+
INSTANCE_IDS= # instance mode: csv of ids (default: all in conf.instances.json)
|
|
29
|
+
IMAGE=underpost/wp:v3.2.14 # runtime mode image (instance mode reads image from conf)
|
|
30
|
+
VERSIONS=green # csv of blue/green versions
|
|
31
|
+
REPLICAS=1 # replicas per deployment
|
|
32
|
+
NAMESPACE=default # k8s namespace
|
|
33
|
+
CLUSTER= # kind | kubeadm | k3s | "" (auto/none)
|
|
34
|
+
TIMEOUT_RESPONSE=300000ms # HTTPProxy per-route response timeout
|
|
35
|
+
TEMPLATE_REPO=underpostnet/pwa-microservices-template-private # runtime mode link repo
|
|
36
|
+
ENVOY_NAMESPACE=projectcontour # ingress namespace (instance TLS exposure)
|
|
37
|
+
ENVOY_SERVICE=envoy # ingress service (instance TLS exposure)
|
|
38
|
+
HTTPS_PORT=443 # local https port for instance TLS exposure
|
|
39
|
+
|
|
40
|
+
USE_CERT=false # runtime: pass --cert to the proxy step
|
|
41
|
+
USE_PULL_BUNDLE=false # runtime: include --pull-bundle in the start cmd
|
|
42
|
+
USE_TLS=false # generate self-signed certs + expose over HTTPS
|
|
43
|
+
USE_TEST_REPO=false # runtime: publish src to engine-test-<id> + pod clones it (--private-test-repo)
|
|
44
|
+
DO_BUILD_TEMPLATE=true # run `node bin/build.template --update-private`
|
|
45
|
+
DO_CLUSTER_MANIFESTS=true # run `node bin run build-cluster-deployment-manifests`
|
|
46
|
+
DO_EXPOSE=true # expose locally after deploy + monitor
|
|
47
|
+
|
|
48
|
+
usage() {
|
|
49
|
+
cat <<'EOF'
|
|
50
|
+
Usage: scripts/test-monitor.sh [flags]
|
|
51
|
+
|
|
52
|
+
Modes:
|
|
53
|
+
--mode <runtime|instance> Deployment shape to test (default: runtime)
|
|
54
|
+
|
|
55
|
+
Common (every default is overridable):
|
|
56
|
+
--env <development|production>
|
|
57
|
+
--deploy-id <id>
|
|
58
|
+
--instance-ids <csv> instance mode only; default = all ids in conf.instances.json
|
|
59
|
+
--image <image> runtime mode only
|
|
60
|
+
--versions <csv> e.g. green or blue,green
|
|
61
|
+
--replicas <n>
|
|
62
|
+
--namespace <ns>
|
|
63
|
+
--cluster <kind|kubeadm|k3s|none>
|
|
64
|
+
--timeout-response <dur> e.g. 300000ms
|
|
65
|
+
--template-repo <owner/repo> runtime mode link repo
|
|
66
|
+
--envoy-namespace <ns> instance TLS exposure ingress namespace
|
|
67
|
+
--envoy-service <name> instance TLS exposure ingress service
|
|
68
|
+
--https-port <port> local https port for instance TLS exposure
|
|
69
|
+
|
|
70
|
+
Toggles (use --x / --no-x):
|
|
71
|
+
--tls | --no-tls self-signed certs + HTTPS exposure (default: off)
|
|
72
|
+
--cert | --no-cert runtime proxy --cert (default: off)
|
|
73
|
+
--pull-bundle | --no-pull-bundle runtime start --pull-bundle (default: off)
|
|
74
|
+
--test-repo | --no-test-repo runtime: publish src to engine-test-<id> and have the
|
|
75
|
+
pod clone it via --private-test-repo (default: off)
|
|
76
|
+
--expose | --no-expose local exposure after monitor (default: on)
|
|
77
|
+
--build-template | --no-build-template sync private template (default: on)
|
|
78
|
+
--cluster-manifests | --no-cluster-manifests rebuild cluster manifests (default: on)
|
|
79
|
+
|
|
80
|
+
-h, --help
|
|
81
|
+
|
|
82
|
+
Examples:
|
|
83
|
+
# Runtime deploy (default), no TLS
|
|
84
|
+
scripts/test-monitor.sh
|
|
85
|
+
|
|
86
|
+
# Runtime deploy over HTTPS
|
|
87
|
+
scripts/test-monitor.sh --tls
|
|
88
|
+
|
|
89
|
+
# Runtime deploy from a work-in-progress test source repo (engine-test-<id>)
|
|
90
|
+
scripts/test-monitor.sh --test-repo
|
|
91
|
+
|
|
92
|
+
# Cyberia instances (both), dev on kind, no TLS
|
|
93
|
+
scripts/test-monitor.sh --mode instance --deploy-id dd-cyberia --cluster kind
|
|
94
|
+
|
|
95
|
+
# Only the mmo-server instance, production, over HTTPS
|
|
96
|
+
scripts/test-monitor.sh --mode instance --deploy-id dd-cyberia \
|
|
97
|
+
--instance-ids mmo-server --env production --cluster kubeadm --tls
|
|
98
|
+
EOF
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
# ─────────────────────────── flag parsing ───────────────────────────
|
|
102
|
+
# Supports `--key value` and `--key=value` for value flags, plus --x/--no-x toggles.
|
|
103
|
+
needval() { [[ $# -ge 2 ]] || { echo "Missing value for $1" >&2; exit 1; }; }
|
|
104
|
+
while [[ $# -gt 0 ]]; do
|
|
105
|
+
case "$1" in
|
|
106
|
+
--mode) needval "$@"; MODE="$2"; shift 2;; --mode=*) MODE="${1#*=}"; shift;;
|
|
107
|
+
--env) needval "$@"; ENV="$2"; shift 2;; --env=*) ENV="${1#*=}"; shift;;
|
|
108
|
+
--deploy-id) needval "$@"; DEPLOY_ID="$2"; shift 2;; --deploy-id=*) DEPLOY_ID="${1#*=}"; shift;;
|
|
109
|
+
--instance-ids) needval "$@"; INSTANCE_IDS="$2"; shift 2;; --instance-ids=*) INSTANCE_IDS="${1#*=}"; shift;;
|
|
110
|
+
--image) needval "$@"; IMAGE="$2"; shift 2;; --image=*) IMAGE="${1#*=}"; shift;;
|
|
111
|
+
--versions) needval "$@"; VERSIONS="$2"; shift 2;; --versions=*) VERSIONS="${1#*=}"; shift;;
|
|
112
|
+
--replicas) needval "$@"; REPLICAS="$2"; shift 2;; --replicas=*) REPLICAS="${1#*=}"; shift;;
|
|
113
|
+
--namespace) needval "$@"; NAMESPACE="$2"; shift 2;; --namespace=*) NAMESPACE="${1#*=}"; shift;;
|
|
114
|
+
--cluster) needval "$@"; CLUSTER="$2"; shift 2;; --cluster=*) CLUSTER="${1#*=}"; shift;;
|
|
115
|
+
--timeout-response) needval "$@"; TIMEOUT_RESPONSE="$2"; shift 2;; --timeout-response=*) TIMEOUT_RESPONSE="${1#*=}"; shift;;
|
|
116
|
+
--template-repo) needval "$@"; TEMPLATE_REPO="$2"; shift 2;; --template-repo=*) TEMPLATE_REPO="${1#*=}"; shift;;
|
|
117
|
+
--envoy-namespace) needval "$@"; ENVOY_NAMESPACE="$2"; shift 2;; --envoy-namespace=*) ENVOY_NAMESPACE="${1#*=}"; shift;;
|
|
118
|
+
--envoy-service) needval "$@"; ENVOY_SERVICE="$2"; shift 2;; --envoy-service=*) ENVOY_SERVICE="${1#*=}"; shift;;
|
|
119
|
+
--https-port) needval "$@"; HTTPS_PORT="$2"; shift 2;; --https-port=*) HTTPS_PORT="${1#*=}"; shift;;
|
|
120
|
+
--tls) USE_TLS=true; shift;; --no-tls) USE_TLS=false; shift;;
|
|
121
|
+
--cert) USE_CERT=true; shift;; --no-cert) USE_CERT=false; shift;;
|
|
122
|
+
--pull-bundle) USE_PULL_BUNDLE=true; shift;; --no-pull-bundle) USE_PULL_BUNDLE=false; shift;;
|
|
123
|
+
--test-repo) USE_TEST_REPO=true; shift;; --no-test-repo) USE_TEST_REPO=false; shift;;
|
|
124
|
+
--expose) DO_EXPOSE=true; shift;; --no-expose) DO_EXPOSE=false; shift;;
|
|
125
|
+
--build-template) DO_BUILD_TEMPLATE=true; shift;; --no-build-template) DO_BUILD_TEMPLATE=false; shift;;
|
|
126
|
+
--cluster-manifests) DO_CLUSTER_MANIFESTS=true; shift;; --no-cluster-manifests) DO_CLUSTER_MANIFESTS=false; shift;;
|
|
127
|
+
-h|--help) usage; exit 0;;
|
|
128
|
+
*) echo "Unknown flag: $1" >&2; usage; exit 1;;
|
|
129
|
+
esac
|
|
130
|
+
done
|
|
131
|
+
|
|
132
|
+
# ─────────────────────────── derived flags ───────────────────────────
|
|
133
|
+
CLUSTER_FLAG=""
|
|
134
|
+
case "$CLUSTER" in
|
|
135
|
+
kind) CLUSTER_FLAG="--kind";;
|
|
136
|
+
kubeadm) CLUSTER_FLAG="--kubeadm";;
|
|
137
|
+
k3s) CLUSTER_FLAG="--k3s";;
|
|
138
|
+
""|none) CLUSTER_FLAG="";;
|
|
139
|
+
*) echo "Unknown --cluster: $CLUSTER (expected kind|kubeadm|k3s|none)" >&2; exit 1;;
|
|
140
|
+
esac
|
|
141
|
+
DEV_FLAG=""; [ "$ENV" = development ] && DEV_FLAG="--dev"
|
|
142
|
+
INSTANCES_CONF="./engine-private/conf/${DEPLOY_ID}/conf.instances.json"
|
|
143
|
+
SERVER_CONF="./engine-private/conf/${DEPLOY_ID}/conf.server.json"
|
|
144
|
+
|
|
145
|
+
echo "[test-monitor] mode=$MODE env=$ENV deploy=$DEPLOY_ID cluster=${CLUSTER:-none} tls=$USE_TLS test-repo=$USE_TEST_REPO expose=$DO_EXPOSE"
|
|
146
|
+
|
|
147
|
+
# ─────────────────────────── shared helpers ───────────────────────────
|
|
148
|
+
|
|
149
|
+
# setup_tls_secrets <host> [<host> …] — regenerate self-signed certs and create
|
|
150
|
+
# the per-host k8s TLS secret the HTTPProxy references (name == host).
|
|
151
|
+
setup_tls_secrets() {
|
|
152
|
+
local ssl_base="${ENGINE_DIR}/engine-private/ssl"
|
|
153
|
+
for host in "$@"; do
|
|
154
|
+
local cert_dir="${ssl_base}/${host}"
|
|
155
|
+
mkdir -p "$cert_dir"
|
|
156
|
+
bash "${SCRIPT_DIR}/ssl.sh" "$cert_dir" "$host"
|
|
157
|
+
local name_safe="${host//[^a-zA-Z0-9_.-]/_}"
|
|
158
|
+
kubectl delete secret "$host" -n "$NAMESPACE" --ignore-not-found
|
|
159
|
+
kubectl create secret tls "$host" \
|
|
160
|
+
--cert="${cert_dir}/${name_safe}.pem" \
|
|
161
|
+
--key="${cert_dir}/${name_safe}-key.pem" \
|
|
162
|
+
-n "$NAMESPACE"
|
|
163
|
+
done
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
# hosts_from <conf.json> — unique hosts declared in a conf.server.json or conf.instances.json.
|
|
167
|
+
hosts_from() {
|
|
168
|
+
node -e "
|
|
169
|
+
const c = require('$1');
|
|
170
|
+
const hosts = Array.isArray(c) ? c.map(i => i.host) : Object.keys(c);
|
|
171
|
+
console.log([...new Set(hosts)].join(' '));
|
|
172
|
+
"
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
# ─────────────────────────── pre-steps ───────────────────────────
|
|
176
|
+
[ "$DO_CLUSTER_MANIFESTS" = true ] && node bin run build-cluster-deployment-manifests
|
|
177
|
+
[ "$DO_BUILD_TEMPLATE" = true ] && node bin/build.template --update-private
|
|
178
|
+
|
|
179
|
+
# ─────────────────────────── runtime mode ───────────────────────────
|
|
180
|
+
run_runtime_mode() {
|
|
181
|
+
local link_cmd="cd /home/dd,underpost clone ${TEMPLATE_REPO},cd /home/dd/${TEMPLATE_REPO##*/},npm install,npm link"
|
|
182
|
+
local deploy_cmd proxy_flag="" expose_flags="" start_flags=""
|
|
183
|
+
|
|
184
|
+
# Publish the local engine src to underpostnet/engine-test-<id> so the pod
|
|
185
|
+
# (started with --private-test-repo) clones this work-in-progress source.
|
|
186
|
+
[ "$USE_TEST_REPO" = true ] && node bin/build "$DEPLOY_ID" --update-private
|
|
187
|
+
|
|
188
|
+
[ "$USE_PULL_BUNDLE" = true ] && start_flags="${start_flags} --pull-bundle"
|
|
189
|
+
[ "$USE_TEST_REPO" = true ] && start_flags="${start_flags} --private-test-repo"
|
|
190
|
+
deploy_cmd="${link_cmd},underpost start --build --run${start_flags} ${DEPLOY_ID} ${ENV}"
|
|
191
|
+
|
|
192
|
+
[ "$USE_CERT" = true ] && proxy_flag="--cert"
|
|
193
|
+
[ "$USE_TLS" = true ] && proxy_flag="--self-signed"
|
|
194
|
+
|
|
195
|
+
node bin deploy "$DEPLOY_ID" "$ENV" $CLUSTER_FLAG --sync --build-manifest \
|
|
196
|
+
--image "$IMAGE" --timeout-response "$TIMEOUT_RESPONSE" --versions "$VERSIONS" --replicas "$REPLICAS" --cmd "$deploy_cmd"
|
|
197
|
+
node bin deploy "$DEPLOY_ID" "$ENV" $CLUSTER_FLAG --disable-update-proxy $proxy_flag
|
|
198
|
+
node bin monitor "$DEPLOY_ID" "$ENV" --ready-deployment --promote \
|
|
199
|
+
--timeout-response "$TIMEOUT_RESPONSE" --versions "$VERSIONS" --replicas "$REPLICAS"
|
|
200
|
+
|
|
201
|
+
node bin run etc-hosts --deploy-id "$DEPLOY_ID"
|
|
202
|
+
|
|
203
|
+
if [ "$USE_TLS" = true ]; then
|
|
204
|
+
setup_tls_secrets $(hosts_from "$SERVER_CONF")
|
|
205
|
+
expose_flags="--tls"
|
|
206
|
+
fi
|
|
207
|
+
[ "$DO_EXPOSE" = true ] && node bin deploy --expose --local-proxy "$DEPLOY_ID" "$ENV" $expose_flags
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
# ─────────────────────────── instance mode ───────────────────────────
|
|
211
|
+
run_instance_mode() {
|
|
212
|
+
[ -f "$INSTANCES_CONF" ] || { echo "Missing $INSTANCES_CONF" >&2; exit 1; }
|
|
213
|
+
if [ -z "$INSTANCE_IDS" ]; then
|
|
214
|
+
INSTANCE_IDS=$(node -e "console.log(require('$INSTANCES_CONF').map(i=>i.id).join(','))")
|
|
215
|
+
fi
|
|
216
|
+
local tls_flag=""; [ "$USE_TLS" = true ] && tls_flag="--tls --test"
|
|
217
|
+
|
|
218
|
+
# `run instance` builds/pulls the image, applies the manifest, monitors with the
|
|
219
|
+
# kubernetes gate (TCP readinessProbe) + exec status, then promotes traffic.
|
|
220
|
+
IFS=',' read -ra ids <<< "$INSTANCE_IDS"
|
|
221
|
+
for id in "${ids[@]}"; do
|
|
222
|
+
[ -n "$id" ] || continue
|
|
223
|
+
echo "[test-monitor] deploying instance ${DEPLOY_ID},${id}"
|
|
224
|
+
node bin run instance "${DEPLOY_ID},${id},${REPLICAS}" \
|
|
225
|
+
$DEV_FLAG $CLUSTER_FLAG --namespace "$NAMESPACE" --etc-hosts $tls_flag \
|
|
226
|
+
${IMAGE:+--image-name "$IMAGE"}
|
|
227
|
+
done
|
|
228
|
+
|
|
229
|
+
[ "$DO_EXPOSE" = true ] || return 0
|
|
230
|
+
if [ "$USE_TLS" = true ]; then
|
|
231
|
+
# Instances are routed by the Contour ingress; port-forward Envoy so the
|
|
232
|
+
# HTTPProxy host routing (server/client.cyberiaonline.com) works locally.
|
|
233
|
+
echo "[test-monitor] exposing HTTPS via ${ENVOY_NAMESPACE}/${ENVOY_SERVICE} on :${HTTPS_PORT}"
|
|
234
|
+
sudo kubectl port-forward -n "$ENVOY_NAMESPACE" "svc/${ENVOY_SERVICE}" "${HTTPS_PORT}:443" &
|
|
235
|
+
else
|
|
236
|
+
# Plain HTTP: port-forward each instance service port directly.
|
|
237
|
+
for id in "${ids[@]}"; do
|
|
238
|
+
[ -n "$id" ] || continue
|
|
239
|
+
node bin deploy --expose "${DEPLOY_ID}-${id}" "$ENV" --namespace "$NAMESPACE" || true
|
|
240
|
+
done
|
|
241
|
+
fi
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
case "$MODE" in
|
|
245
|
+
runtime) run_runtime_mode;;
|
|
246
|
+
instance) run_instance_mode;;
|
|
247
|
+
*) echo "Unknown --mode: $MODE (expected runtime|instance)" >&2; exit 1;;
|
|
248
|
+
esac
|
|
249
|
+
|
|
250
|
+
echo "[test-monitor] done (mode=$MODE tls=$USE_TLS)"
|
package/src/cli/deploy.js
CHANGED
|
@@ -18,6 +18,7 @@ import {
|
|
|
18
18
|
} from '../server/conf.js';
|
|
19
19
|
import { loggerFactory } from '../server/logger.js';
|
|
20
20
|
import { shellExec } from '../server/process.js';
|
|
21
|
+
import { INTERNAL_READY_PATH, INTERNAL_HEALTH_PATH } from '../server/runtime-status.js';
|
|
21
22
|
import fs from 'fs-extra';
|
|
22
23
|
import dotenv from 'dotenv';
|
|
23
24
|
import os from 'node:os';
|
|
@@ -113,6 +114,64 @@ class UnderpostDeploy {
|
|
|
113
114
|
)
|
|
114
115
|
.join('')}`;
|
|
115
116
|
},
|
|
117
|
+
/**
|
|
118
|
+
* Builds Kubernetes probes that gate on the in-pod internal status endpoint.
|
|
119
|
+
*
|
|
120
|
+
* HTTP mode (default) aligns Kubernetes pod readiness with actual Underpost
|
|
121
|
+
* runtime readiness:
|
|
122
|
+
* - readinessProbe → GET /_internal/ready (200 only when running-deployment)
|
|
123
|
+
* - livenessProbe → GET /_internal/health (deadlock / hung-process detection)
|
|
124
|
+
* - startupProbe → GET /_internal/ready (long window for hot-built/slow boots)
|
|
125
|
+
*
|
|
126
|
+
* Migration: pass `useHttp: false` to emit the legacy TCP socket probes
|
|
127
|
+
* (port-bound only) for deployments not yet serving the internal endpoint.
|
|
128
|
+
*
|
|
129
|
+
* @param {object} opts
|
|
130
|
+
* @param {number} opts.port - In-pod internal status port (deployment base PORT).
|
|
131
|
+
* @param {boolean} [opts.useHttp=true] - Emit HTTP probes; false → legacy TCP.
|
|
132
|
+
* @param {boolean} [opts.liveness=true] - Include a livenessProbe.
|
|
133
|
+
* @param {boolean} [opts.startup=true] - Include a startupProbe.
|
|
134
|
+
* @returns {{readinessProbe: object, livenessProbe?: object, startupProbe?: object}}
|
|
135
|
+
* @memberof UnderpostDeploy
|
|
136
|
+
*/
|
|
137
|
+
runtimeProbesFactory({ port, useHttp = true, liveness = true, startup = true } = {}) {
|
|
138
|
+
if (!port) return {};
|
|
139
|
+
if (!useHttp) {
|
|
140
|
+
const tcp = { tcpSocket: { port }, initialDelaySeconds: 5, periodSeconds: 10, failureThreshold: 6 };
|
|
141
|
+
const probes = { readinessProbe: tcp };
|
|
142
|
+
if (liveness) probes.livenessProbe = { ...tcp, initialDelaySeconds: 30 };
|
|
143
|
+
return probes;
|
|
144
|
+
}
|
|
145
|
+
const probes = {
|
|
146
|
+
readinessProbe: {
|
|
147
|
+
httpGet: { path: INTERNAL_READY_PATH, port },
|
|
148
|
+
initialDelaySeconds: 5,
|
|
149
|
+
periodSeconds: 5,
|
|
150
|
+
timeoutSeconds: 3,
|
|
151
|
+
failureThreshold: 3,
|
|
152
|
+
},
|
|
153
|
+
};
|
|
154
|
+
if (liveness)
|
|
155
|
+
probes.livenessProbe = {
|
|
156
|
+
httpGet: { path: INTERNAL_HEALTH_PATH, port },
|
|
157
|
+
initialDelaySeconds: 30,
|
|
158
|
+
periodSeconds: 15,
|
|
159
|
+
timeoutSeconds: 3,
|
|
160
|
+
failureThreshold: 3,
|
|
161
|
+
};
|
|
162
|
+
if (startup)
|
|
163
|
+
// A startupProbe suspends readiness/liveness until it first succeeds, so
|
|
164
|
+
// its window bounds in-container hot builds and slow boots. 180 × 10s =
|
|
165
|
+
// 30 min before the pod is considered failed to start.
|
|
166
|
+
probes.startupProbe = {
|
|
167
|
+
httpGet: { path: INTERNAL_READY_PATH, port },
|
|
168
|
+
initialDelaySeconds: 10,
|
|
169
|
+
periodSeconds: 10,
|
|
170
|
+
timeoutSeconds: 3,
|
|
171
|
+
failureThreshold: 180,
|
|
172
|
+
};
|
|
173
|
+
return probes;
|
|
174
|
+
},
|
|
116
175
|
/**
|
|
117
176
|
* Creates a YAML deployment configuration for a deployment.
|
|
118
177
|
* @param {string} deployId - Deployment ID for which the deployment is being created.
|
|
@@ -127,6 +186,11 @@ class UnderpostDeploy {
|
|
|
127
186
|
* @param {boolean} skipFullBuild - Whether to skip the full client bundle build during deployment.
|
|
128
187
|
* @param {boolean} pullBundle - Whether to pull the pre-built client bundle from Cloudinary before starting. Use together with skipFullBuild to skip the local build entirely.
|
|
129
188
|
* @param {string} [imagePullPolicy] - Container imagePullPolicy override (`Always`, `IfNotPresent`, `Never`). When omitted, defaults to `Never` for `localhost/` images and `IfNotPresent` otherwise.
|
|
189
|
+
* @param {object} lifecycle - Kubernetes lifecycle hooks configuration for the deployment container.
|
|
190
|
+
* @param {object} readinessProbe - Kubernetes readiness probe configuration for the deployment container.
|
|
191
|
+
* @param {object} livenessProbe - Kubernetes liveness probe configuration for the deployment container.
|
|
192
|
+
* @param {object} startupProbe - Kubernetes startup probe configuration for the deployment container.
|
|
193
|
+
* @param {number} containerPort - Container port to expose for the deployment.
|
|
130
194
|
* @returns {string} - YAML deployment configuration for the specified deployment.
|
|
131
195
|
* @memberof UnderpostDeploy
|
|
132
196
|
*/
|
|
@@ -152,7 +216,12 @@ class UnderpostDeploy {
|
|
|
152
216
|
lifecycle,
|
|
153
217
|
readinessProbe,
|
|
154
218
|
livenessProbe,
|
|
219
|
+
startupProbe,
|
|
155
220
|
containerPort,
|
|
221
|
+
// Explicit, secret-free internal status port injected as an env var so the
|
|
222
|
+
// in-pod endpoint binds exactly what the probes and the monitor target,
|
|
223
|
+
// independent of the ambient `PORT` baked into the image/secret.
|
|
224
|
+
internalStatusPort,
|
|
156
225
|
}) {
|
|
157
226
|
if (!cmd)
|
|
158
227
|
cmd =
|
|
@@ -204,12 +273,19 @@ spec:
|
|
|
204
273
|
- secretRef:
|
|
205
274
|
name: underpost-config
|
|
206
275
|
${
|
|
207
|
-
|
|
208
|
-
? `
|
|
209
|
-
-
|
|
276
|
+
internalStatusPort
|
|
277
|
+
? ` env:
|
|
278
|
+
- name: UNDERPOST_INTERNAL_PORT
|
|
279
|
+
value: "${internalStatusPort}"
|
|
210
280
|
`
|
|
211
281
|
: ''
|
|
212
282
|
}${
|
|
283
|
+
containerPort
|
|
284
|
+
? ` ports:
|
|
285
|
+
- containerPort: ${containerPort}
|
|
286
|
+
`
|
|
287
|
+
: ''
|
|
288
|
+
}${
|
|
213
289
|
resources
|
|
214
290
|
? ` resources:
|
|
215
291
|
requests:
|
|
@@ -241,6 +317,15 @@ ${JSON.stringify(livenessProbe, null, 2)
|
|
|
241
317
|
.split('\n')
|
|
242
318
|
.map((l) => ' ' + l)
|
|
243
319
|
.join('\n')}
|
|
320
|
+
`
|
|
321
|
+
: ''
|
|
322
|
+
}${
|
|
323
|
+
startupProbe
|
|
324
|
+
? ` startupProbe:
|
|
325
|
+
${JSON.stringify(startupProbe, null, 2)
|
|
326
|
+
.split('\n')
|
|
327
|
+
.map((l) => ' ' + l)
|
|
328
|
+
.join('\n')}
|
|
244
329
|
`
|
|
245
330
|
: ''
|
|
246
331
|
}${
|
|
@@ -282,18 +367,22 @@ spec:
|
|
|
282
367
|
* @param {object} options - Options for the manifest build process.
|
|
283
368
|
* @param {string} options.replicas - Number of replicas for each deployment.
|
|
284
369
|
* @param {string} options.image - Docker image for the deployment.
|
|
285
|
-
* @param {string} options.namespace - Kubernetes namespace for the deployment.
|
|
370
|
+
* @param {string} options.namespace - Kubernetes namespace for the deployment (defaults to "default").
|
|
286
371
|
* @param {string} [options.versions] - Comma-separated list of versions to deploy.
|
|
287
372
|
* @param {string} [options.cmd] - Custom initialization command for deploymentYamlPartsFactory (comma-separated commands).
|
|
288
|
-
* @param {string} [options.timeoutResponse] -
|
|
289
|
-
* @param {string} [options.timeoutIdle] -
|
|
290
|
-
* @param {string} [options.retryCount] -
|
|
291
|
-
* @param {string} [options.retryPerTryTimeout] -
|
|
292
|
-
* @param {boolean} [options.disableDeploymentProxy] - Whether to disable deployment proxy.
|
|
293
|
-
* @param {string} [options.traffic] -
|
|
294
|
-
* @param {boolean} [options.
|
|
373
|
+
* @param {string} [options.timeoutResponse] - HTTPProxy per-route response timeout (e.g. "300000ms", "infinity").
|
|
374
|
+
* @param {string} [options.timeoutIdle] - HTTPProxy per-route idle timeout (e.g. "10s", "infinity").
|
|
375
|
+
* @param {string} [options.retryCount] - HTTPProxy per-route retry count (e.g. 3).
|
|
376
|
+
* @param {string} [options.retryPerTryTimeout] - HTTPProxy per-route per-try timeout (e.g. "150ms").
|
|
377
|
+
* @param {boolean} [options.disableDeploymentProxy] - Whether to disable deployment proxy route generation.
|
|
378
|
+
* @param {string} [options.traffic] - Comma-separated active traffic colour(s) used to select which versions receive traffic (e.g. "blue", "green").
|
|
379
|
+
* @param {boolean} [options.cert] - Whether to include cert-manager Certificate resources in secret.yaml (production only).
|
|
380
|
+
* @param {boolean} [options.selfSigned] - Whether to include TLS block in HTTPProxy using a pre-created self-signed secret. Enables HTTPS for development without cert-manager.
|
|
381
|
+
* @param {boolean} [options.skipFullBuild] - Whether to skip the full client bundle build; forwarded to deploymentYamlPartsFactory.
|
|
295
382
|
* @param {boolean} [options.pullBundle] - Whether to pull the pre-built client bundle from Cloudinary; forwarded to deploymentYamlPartsFactory. Use together with skipFullBuild.
|
|
296
|
-
* @param {string} [options.imagePullPolicy] - Container imagePullPolicy override (`Always`, `IfNotPresent`, `Never`); forwarded to deploymentYamlPartsFactory.
|
|
383
|
+
* @param {string} [options.imagePullPolicy] - Container imagePullPolicy override (`Always`, `IfNotPresent`, `Never`); forwarded to deploymentYamlPartsFactory. Defaults to `Never` for `localhost/` images and `IfNotPresent` otherwise.
|
|
384
|
+
* @param {boolean} [options.disableRuntimeProbes] - Omit internal-status HTTP probes from generated manifests. When true no readiness/liveness/startup probes are emitted.
|
|
385
|
+
* @param {boolean} [options.tcpProbes] - Emit legacy TCP socket probes instead of HTTP internal-status probes (migration path).
|
|
297
386
|
* @returns {Promise<void>} - Promise that resolves when the manifest is built.
|
|
298
387
|
* @memberof UnderpostDeploy
|
|
299
388
|
*/
|
|
@@ -318,6 +407,17 @@ spec:
|
|
|
318
407
|
|
|
319
408
|
logger.info('port range', { deployId, fromPort, toPort });
|
|
320
409
|
|
|
410
|
+
// The internal status endpoint binds `fromPort - 1`: app instances bind
|
|
411
|
+
// the router range starting at `fromPort`, so this slot is always free
|
|
412
|
+
// inside the pod. It is injected into the pod env (UNDERPOST_INTERNAL_PORT)
|
|
413
|
+
// and used for both the probes and the monitor's port-forward target so
|
|
414
|
+
// all three agree regardless of the image's ambient PORT.
|
|
415
|
+
// Opt out with `--disable-runtime-probes` to keep legacy probe-less pods.
|
|
416
|
+
const internalPort = fromPort - 1;
|
|
417
|
+
const probes = options.disableRuntimeProbes
|
|
418
|
+
? {}
|
|
419
|
+
: Underpost.deploy.runtimeProbesFactory({ port: internalPort, useHttp: !options.tcpProbes });
|
|
420
|
+
|
|
321
421
|
let deploymentYamlParts = '';
|
|
322
422
|
for (const deploymentVersion of deploymentVersions) {
|
|
323
423
|
deploymentYamlParts += `---
|
|
@@ -333,6 +433,10 @@ ${Underpost.deploy
|
|
|
333
433
|
skipFullBuild: options.skipFullBuild,
|
|
334
434
|
pullBundle: options.pullBundle,
|
|
335
435
|
imagePullPolicy: options.imagePullPolicy,
|
|
436
|
+
internalStatusPort: options.disableRuntimeProbes ? undefined : internalPort,
|
|
437
|
+
readinessProbe: probes.readinessProbe,
|
|
438
|
+
livenessProbe: probes.livenessProbe,
|
|
439
|
+
startupProbe: probes.startupProbe,
|
|
336
440
|
})
|
|
337
441
|
.replace('{{ports}}', buildKindPorts(fromPort, toPort))}
|
|
338
442
|
`;
|
|
@@ -375,7 +479,7 @@ ${Underpost.deploy
|
|
|
375
479
|
: [];
|
|
376
480
|
|
|
377
481
|
for (const host of Object.keys(confServer)) {
|
|
378
|
-
if (env === 'production')
|
|
482
|
+
if (env === 'production' && options.cert === true)
|
|
379
483
|
secretYaml += Underpost.deploy.buildCertManagerCertificate({ host, namespace: options.namespace });
|
|
380
484
|
|
|
381
485
|
const pathPortAssignment = pathPortAssignmentData[host];
|
|
@@ -578,6 +682,7 @@ spec:
|
|
|
578
682
|
* @memberof UnderpostDeploy
|
|
579
683
|
*/
|
|
580
684
|
baseProxyYamlFactory({ host, env, options }) {
|
|
685
|
+
const includeTls = env !== 'development' || options.selfSigned === true;
|
|
581
686
|
return `
|
|
582
687
|
---
|
|
583
688
|
apiVersion: projectcontour.io/v1
|
|
@@ -588,11 +693,11 @@ metadata:
|
|
|
588
693
|
spec:
|
|
589
694
|
virtualhost:
|
|
590
695
|
fqdn: ${host}${
|
|
591
|
-
|
|
592
|
-
?
|
|
593
|
-
: `
|
|
696
|
+
includeTls
|
|
697
|
+
? `
|
|
594
698
|
tls:
|
|
595
699
|
secretName: ${host}`
|
|
700
|
+
: ''
|
|
596
701
|
}
|
|
597
702
|
routes:`;
|
|
598
703
|
},
|
|
@@ -608,8 +713,9 @@ spec:
|
|
|
608
713
|
* @param {boolean} options.buildManifest - Whether to build the deployment manifest.
|
|
609
714
|
* @param {boolean} options.infoUtil - Whether to display utility information.
|
|
610
715
|
* @param {boolean} options.expose - Whether to expose the deployment.
|
|
611
|
-
* @param {boolean} options.cert - Whether to create
|
|
612
|
-
* @param {string} options.certHosts - Comma-separated list of hosts for which to create certificates.
|
|
716
|
+
* @param {boolean} options.cert - Whether to create cert-manager Certificate resources for the deployment.
|
|
717
|
+
* @param {string} options.certHosts - Comma-separated list of hosts for which to create cert-manager certificates.
|
|
718
|
+
* @param {boolean} options.selfSigned - Use a pre-created self-signed TLS secret instead of cert-manager. The secret must already exist in the namespace with the same name as the host. Enables TLS in the Contour HTTPProxy virtualhost without requiring a production ClusterIssuer.
|
|
613
719
|
* @param {string} options.versions - Comma-separated list of versions to deploy.
|
|
614
720
|
* @param {string} options.image - Docker image for the deployment.
|
|
615
721
|
* @param {string} options.traffic - Traffic status for the deployment.
|
|
@@ -621,22 +727,27 @@ spec:
|
|
|
621
727
|
* @param {boolean} options.disableUpdateVolume - Whether to disable volume updates.
|
|
622
728
|
* @param {boolean} options.status - Whether to display deployment status.
|
|
623
729
|
* @param {boolean} options.disableUpdateUnderpostConfig - Whether to disable Underpost config updates.
|
|
624
|
-
* @param {string} [options.namespace] - Kubernetes namespace for the deployment.
|
|
625
|
-
* @param {string} [options.timeoutResponse] -
|
|
626
|
-
* @param {string} [options.timeoutIdle] -
|
|
627
|
-
* @param {string} [options.retryCount] -
|
|
628
|
-
* @param {string} [options.retryPerTryTimeout] -
|
|
629
|
-
* @param {string} [options.kindType] -
|
|
630
|
-
* @param {number} [options.port] - Port number for exposing the deployment.
|
|
631
|
-
* @param {string} [options.cmd] - Custom initialization command
|
|
632
|
-
* @param {number} [options.exposePort] -
|
|
730
|
+
* @param {string} [options.namespace] - Kubernetes namespace for the deployment (defaults to "default").
|
|
731
|
+
* @param {string} [options.timeoutResponse] - HTTPProxy per-route response timeout (e.g. "300000ms", "infinity").
|
|
732
|
+
* @param {string} [options.timeoutIdle] - HTTPProxy per-route idle timeout (e.g. "10s", "infinity").
|
|
733
|
+
* @param {string} [options.retryCount] - HTTPProxy per-route retry count (e.g. 3).
|
|
734
|
+
* @param {string} [options.retryPerTryTimeout] - HTTPProxy per-route per-try timeout (e.g. "150ms").
|
|
735
|
+
* @param {string} [options.kindType] - Kubernetes resource kind to target when using --expose (defaults to "svc").
|
|
736
|
+
* @param {number} [options.port] - Port number override for exposing the deployment.
|
|
737
|
+
* @param {string} [options.cmd] - Custom initialization command (comma-separated) for deploymentYamlPartsFactory.
|
|
738
|
+
* @param {number} [options.exposePort] - Remote port override when --expose is active (overrides auto-detected service port). Used as both local and remote port unless exposeLocalPort is also set.
|
|
739
|
+
* @param {number} [options.exposeLocalPort] - Local port override for --expose (e.g. 80); remote port is still auto-detected. Enables /etc/hosts access without a port in the browser URL.
|
|
740
|
+
* @param {boolean} [options.localProxy] - When true (with --expose), forward all service TCP ports locally and start the Node.js path-routing proxy for full path-based routing (e.g. /wp alongside /).
|
|
741
|
+
* @param {boolean} [options.tls] - When true (with --expose --local-proxy), start the proxy on port 443 with TLS using self-signed certificates resolved from the local SSL store.
|
|
633
742
|
* @param {boolean} [options.k3s] - Whether to use k3s cluster context.
|
|
634
743
|
* @param {boolean} [options.kubeadm] - Whether to use kubeadm cluster context.
|
|
635
744
|
* @param {boolean} [options.kind] - Whether to use kind cluster context.
|
|
636
745
|
* @param {boolean} [options.gitClean] - Whether to run git clean on volume mount paths before copying.
|
|
637
746
|
* @param {boolean} [options.skipFullBuild] - Whether to skip the full client bundle build; passed through to buildManifest/deploymentYamlPartsFactory.
|
|
638
747
|
* @param {boolean} [options.pullBundle] - Whether to pull the pre-built client bundle from Cloudinary; passed through to buildManifest/deploymentYamlPartsFactory. Use together with skipFullBuild.
|
|
639
|
-
* @param {string} [options.imagePullPolicy] - Container imagePullPolicy override (`Always`, `IfNotPresent`, `Never`); passed through to buildManifest/deploymentYamlPartsFactory.
|
|
748
|
+
* @param {string} [options.imagePullPolicy] - Container imagePullPolicy override (`Always`, `IfNotPresent`, `Never`); passed through to buildManifest/deploymentYamlPartsFactory. Defaults to `Never` for `localhost/` images and `IfNotPresent` otherwise.
|
|
749
|
+
* @param {boolean} [options.disableRuntimeProbes] - Omit internal-status HTTP probes from generated manifests. When true no readiness/liveness/startup probes are emitted.
|
|
750
|
+
* @param {boolean} [options.tcpProbes] - Emit legacy TCP socket probes instead of HTTP internal-status probes.
|
|
640
751
|
* @returns {Promise<void>} - Promise that resolves when the deployment process is complete.
|
|
641
752
|
* @memberof UnderpostDeploy
|
|
642
753
|
*/
|
|
@@ -671,6 +782,10 @@ spec:
|
|
|
671
782
|
kindType: '',
|
|
672
783
|
port: 0,
|
|
673
784
|
exposePort: 0,
|
|
785
|
+
exposeLocalPort: 0,
|
|
786
|
+
localProxy: false,
|
|
787
|
+
tls: false,
|
|
788
|
+
selfSigned: false,
|
|
674
789
|
cmd: '',
|
|
675
790
|
k3s: false,
|
|
676
791
|
kubeadm: false,
|
|
@@ -756,20 +871,50 @@ EOF`);
|
|
|
756
871
|
logger.error(`No ${kindType} found matching '${deployId}', skipping expose`);
|
|
757
872
|
continue;
|
|
758
873
|
}
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
874
|
+
if (options.localProxy) {
|
|
875
|
+
const svcPorts = [
|
|
876
|
+
...new Set(
|
|
877
|
+
svc['PORT(S)']
|
|
878
|
+
.split(',')
|
|
879
|
+
.filter((p) => p.includes('/TCP'))
|
|
880
|
+
.map((p) => parseInt(p.split(':')[0])),
|
|
881
|
+
),
|
|
882
|
+
];
|
|
883
|
+
for (const svcPort of svcPorts) {
|
|
884
|
+
shellExec(`sudo kubectl port-forward -n ${namespace} ${kindType}/${svc.NAME} ${svcPort}:${svcPort}`, {
|
|
885
|
+
async: true,
|
|
886
|
+
});
|
|
887
|
+
}
|
|
888
|
+
const envFile = `./engine-private/conf/${deployId}/.env.${env}`;
|
|
889
|
+
let basePort = svcPorts[0] - 1;
|
|
890
|
+
if (fs.existsSync(envFile)) {
|
|
891
|
+
const portMatch = fs.readFileSync(envFile, 'utf8').match(/^PORT=(\d+)/m);
|
|
892
|
+
if (portMatch) basePort = parseInt(portMatch[1]);
|
|
893
|
+
}
|
|
894
|
+
logger.info(deployId, { svc, svcPorts, basePort });
|
|
895
|
+
const tlsFlag = options.tls ? ' tls' : '';
|
|
896
|
+
shellExec(
|
|
897
|
+
`NODE_ENV=${env} PORT=${basePort} DEV_PROXY_PORT_OFFSET=0 node src/proxy proxy ${deployId} ${env}${tlsFlag}`,
|
|
898
|
+
{ async: true },
|
|
899
|
+
);
|
|
900
|
+
} else {
|
|
901
|
+
const remotePort = options.exposePort
|
|
902
|
+
? parseInt(options.exposePort)
|
|
903
|
+
: options.port
|
|
904
|
+
? parseInt(options.port)
|
|
905
|
+
: kindType !== 'svc'
|
|
906
|
+
? 80
|
|
907
|
+
: parseInt(svc[`PORT(S)`].split('/TCP')[0]);
|
|
908
|
+
const localPort = options.exposeLocalPort ? parseInt(options.exposeLocalPort) : remotePort;
|
|
909
|
+
logger.info(deployId, {
|
|
910
|
+
svc,
|
|
911
|
+
localPort,
|
|
912
|
+
remotePort,
|
|
913
|
+
});
|
|
914
|
+
shellExec(`sudo kubectl port-forward -n ${namespace} ${kindType}/${svc.NAME} ${localPort}:${remotePort}`, {
|
|
915
|
+
async: true,
|
|
916
|
+
});
|
|
917
|
+
}
|
|
773
918
|
continue;
|
|
774
919
|
}
|
|
775
920
|
|
|
@@ -821,7 +966,10 @@ EOF`);
|
|
|
821
966
|
if (!options.disableUpdateProxy)
|
|
822
967
|
shellExec(`sudo kubectl apply -f ./${manifestsPath}/proxy.yaml -n ${namespace}`);
|
|
823
968
|
|
|
824
|
-
if (
|
|
969
|
+
if (
|
|
970
|
+
Underpost.deploy.isValidTLSContext({ host: Object.keys(confServer)[0], env, options }) &&
|
|
971
|
+
!options.selfSigned
|
|
972
|
+
)
|
|
825
973
|
shellExec(`sudo kubectl apply -f ./${manifestsPath}/secret.yaml -n ${namespace}`);
|
|
826
974
|
}
|
|
827
975
|
}
|
|
@@ -924,15 +1072,12 @@ EOF`);
|
|
|
924
1072
|
if (options.gitClean && volume.volumeMountPath) {
|
|
925
1073
|
Underpost.repo.clean({ paths: [volume.volumeMountPath] });
|
|
926
1074
|
}
|
|
927
|
-
if (
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
shellExec(`docker exec -i kind-worker bash -c "mkdir -p ${rootVolumeHostPath}"`);
|
|
932
|
-
// shellExec(`docker cp ${volume.volumeMountPath} kind-worker:${rootVolumeHostPath}`);
|
|
933
|
-
shellExec(`tar -C ${volume.volumeMountPath} -c . | docker cp - kind-worker:${rootVolumeHostPath}`);
|
|
1075
|
+
if (clusterContext === 'kind') {
|
|
1076
|
+
const kindNode = options.nodeName || 'kind-worker';
|
|
1077
|
+
shellExec(`docker exec -i ${kindNode} bash -c "mkdir -p ${rootVolumeHostPath}"`);
|
|
1078
|
+
shellExec(`tar -C ${volume.volumeMountPath} -c . | docker cp - ${kindNode}:${rootVolumeHostPath}`);
|
|
934
1079
|
shellExec(
|
|
935
|
-
`docker exec -i
|
|
1080
|
+
`docker exec -i ${kindNode} bash -c "chown -R 1000:1000 ${rootVolumeHostPath}; chmod -R 755 ${rootVolumeHostPath}"`,
|
|
936
1081
|
);
|
|
937
1082
|
} else {
|
|
938
1083
|
if (!fs.existsSync(rootVolumeHostPath)) fs.mkdirSync(rootVolumeHostPath, { recursive: true });
|
|
@@ -1082,9 +1227,10 @@ spec:
|
|
|
1082
1227
|
* @memberof UnderpostDeploy
|
|
1083
1228
|
*/
|
|
1084
1229
|
isValidTLSContext: ({ host, env, options }) =>
|
|
1085
|
-
env === 'production' &&
|
|
1086
|
-
|
|
1087
|
-
|
|
1230
|
+
(env === 'production' &&
|
|
1231
|
+
options.cert === true &&
|
|
1232
|
+
(!options.certHosts || options.certHosts.split(',').includes(host))) ||
|
|
1233
|
+
options.selfSigned === true,
|
|
1088
1234
|
|
|
1089
1235
|
/**
|
|
1090
1236
|
* Predefined resource templates for Kubernetes deployments.
|