Merge pull request #1045 from pikasTech/fix/1017-caddy-route-order
fix(web-probe): keep sentinel caddy path routes
This commit is contained in:
@@ -2389,19 +2389,12 @@ function applySentinelCaddyBlock(state: SentinelCicdState, timeoutSeconds: numbe
|
||||
" }",
|
||||
"}",
|
||||
];
|
||||
const block = routePrefix === "/"
|
||||
? [
|
||||
`${hostname} {`,
|
||||
...proxyLines.map((line) => ` ${line}`),
|
||||
"}",
|
||||
"",
|
||||
].join("\n")
|
||||
: [
|
||||
`handle_path ${routePrefix}* {`,
|
||||
...proxyLines.map((line) => ` ${line}`),
|
||||
"}",
|
||||
"",
|
||||
].join("\n");
|
||||
const block = [
|
||||
routePrefix === "/" ? "handle {" : `handle_path ${routePrefix}* {`,
|
||||
...proxyLines.map((line) => ` ${line}`),
|
||||
"}",
|
||||
"",
|
||||
].join("\n");
|
||||
const blockB64 = Buffer.from(block, "utf8").toString("base64");
|
||||
const script = [
|
||||
"set +e",
|
||||
@@ -2429,6 +2422,28 @@ function applySentinelCaddyBlock(state: SentinelCicdState, timeoutSeconds: numbe
|
||||
"begin = f'# BEGIN {marker}'",
|
||||
"end = f'# END {marker}'",
|
||||
"pattern = re.compile(rf'(?ms)^[ \\t]*# BEGIN {re.escape(marker)}\\n.*?^[ \\t]*# END {re.escape(marker)}\\n*')",
|
||||
"def collect_nested_managed(segment):",
|
||||
" preserved = []",
|
||||
" lines = segment.splitlines()",
|
||||
" index = 0",
|
||||
" while index < len(lines):",
|
||||
" stripped = lines[index].strip()",
|
||||
" if stripped.startswith('# BEGIN ') and stripped != begin:",
|
||||
" owner_text = stripped[len('# BEGIN '):]",
|
||||
" end_line = '# END ' + owner_text",
|
||||
" block_lines = [lines[index]]",
|
||||
" index += 1",
|
||||
" while index < len(lines):",
|
||||
" block_lines.append(lines[index])",
|
||||
" if lines[index].strip() == end_line:",
|
||||
" break",
|
||||
" index += 1",
|
||||
" preserved.append('\\n'.join(block_lines).rstrip() + '\\n')",
|
||||
" index += 1",
|
||||
" return preserved",
|
||||
"preserved_blocks = []",
|
||||
"for match in pattern.finditer(text):",
|
||||
" preserved_blocks.extend(collect_nested_managed(match.group(0)))",
|
||||
"text = pattern.sub('', text)",
|
||||
"def site_span(src, host):",
|
||||
" match = re.search(rf'(?m)^([ \\t]*){re.escape(host)}[ \\t]*\\{{[ \\t]*\\n', src)",
|
||||
@@ -2449,21 +2464,42 @@ function applySentinelCaddyBlock(state: SentinelCicdState, timeoutSeconds: numbe
|
||||
" return match.start(), end_index, index, match.end()",
|
||||
" index += 1",
|
||||
" raise ValueError(f'unclosed Caddy site block for {host}')",
|
||||
"if route_prefix == '/':",
|
||||
" managed = f'{begin}\\n{block.rstrip()}\\n{end}\\n'",
|
||||
" text = text.rstrip() + '\\n\\n' + managed",
|
||||
"handler = '\\n'.join((' ' + line) if line else '' for line in block.rstrip().splitlines())",
|
||||
"managed = f' {begin}\\n{handler}\\n {end}\\n'",
|
||||
"def fallback_insert_pos(site, relative_open, close_rel):",
|
||||
" for match in re.finditer(r'(?m)^[ \\t]*handle[ \\t]*\\{[ \\t]*\\n', site[relative_open:close_rel]):",
|
||||
" pos = relative_open + match.start()",
|
||||
" prev_end = pos - 1",
|
||||
" if prev_end >= 0 and site[prev_end] == '\\n':",
|
||||
" prev_end -= 1",
|
||||
" if prev_end >= 0:",
|
||||
" prev_start = site.rfind('\\n', 0, prev_end + 1) + 1",
|
||||
" if site[prev_start:prev_end + 1].strip().startswith('# BEGIN '):",
|
||||
" return prev_start",
|
||||
" return pos",
|
||||
" return relative_open",
|
||||
"def append_before_close(site, close_rel, addition):",
|
||||
" prefix = site[:close_rel]",
|
||||
" suffix = site[close_rel:]",
|
||||
" if prefix and not prefix.endswith('\\n'):",
|
||||
" prefix += '\\n'",
|
||||
" return prefix + addition + suffix",
|
||||
"span = site_span(text, hostname)",
|
||||
"if span is None:",
|
||||
" body = ''.join(preserved_blocks) + managed",
|
||||
" text = text.rstrip() + '\\n\\n' + f'{hostname} {{\\n{body}}}\\n'",
|
||||
"else:",
|
||||
" handler = '\\n'.join((' ' + line) if line else '' for line in block.rstrip().splitlines())",
|
||||
" managed = f' {begin}\\n{handler}\\n {end}\\n'",
|
||||
" span = site_span(text, hostname)",
|
||||
" if span is None:",
|
||||
" text = text.rstrip() + '\\n\\n' + f'{hostname} {{\\n{managed}}}\\n'",
|
||||
" start, stop, close_index, open_end = span",
|
||||
" site = text[start:stop]",
|
||||
" relative_open = open_end - start",
|
||||
" close_rel = close_index - start",
|
||||
" additions = ''.join(preserved_blocks) + managed",
|
||||
" if route_prefix == '/':",
|
||||
" replacement = append_before_close(site, close_rel, additions)",
|
||||
" else:",
|
||||
" start, stop, close_index, open_end = span",
|
||||
" site = text[start:stop]",
|
||||
" relative_open = open_end - start",
|
||||
" replacement = site[:relative_open] + managed + site[relative_open:]",
|
||||
" text = text[:start] + replacement + text[stop:]",
|
||||
" insert_at = fallback_insert_pos(site, relative_open, close_rel)",
|
||||
" replacement = site[:insert_at] + additions + site[insert_at:]",
|
||||
" text = text[:start] + replacement + text[stop:]",
|
||||
"config.write_text(text, encoding='utf-8')",
|
||||
"PY",
|
||||
"python_rc=$?",
|
||||
@@ -2471,13 +2507,21 @@ function applySentinelCaddyBlock(state: SentinelCicdState, timeoutSeconds: numbe
|
||||
"reload_rc=",
|
||||
"if [ \"$python_rc\" = 0 ]; then sudo caddy validate --config \"$next\" --adapter caddyfile >/tmp/web-probe-sentinel-caddy-validate.out 2>/tmp/web-probe-sentinel-caddy-validate.err; validate_rc=$?; fi",
|
||||
"if [ \"$validate_rc\" = 0 ]; then sudo install -m 0644 \"$next\" \"$config_path\" >/tmp/web-probe-sentinel-caddy-install.out 2>/tmp/web-probe-sentinel-caddy-install.err && (sudo systemctl reload \"$service\" >/tmp/web-probe-sentinel-caddy-reload.out 2>/tmp/web-probe-sentinel-caddy-reload.err || sudo systemctl restart \"$service\" >>/tmp/web-probe-sentinel-caddy-reload.out 2>>/tmp/web-probe-sentinel-caddy-reload.err); reload_rc=$?; fi",
|
||||
"probe_rc=1",
|
||||
"probe_status=",
|
||||
"if [ \"$reload_rc\" = 0 ]; then",
|
||||
" probe_path=\"$route_prefix\"",
|
||||
" if [ \"$probe_path\" = \"/\" ]; then probe_url=\"https://$hostname/\"; else probe_url=\"https://$hostname$probe_path/\"; fi",
|
||||
" probe_status=$(curl -k -sS -o /tmp/web-probe-sentinel-caddy-probe.out -w '%{http_code}' --max-time 10 --resolve \"$hostname:443:127.0.0.1\" \"$probe_url\" 2>/tmp/web-probe-sentinel-caddy-probe.err)",
|
||||
" probe_rc=$?",
|
||||
"fi",
|
||||
"after_present=no",
|
||||
"grep -Fq \"# BEGIN $marker\" \"$config_path\" 2>/dev/null && after_present=yes",
|
||||
"active=$(systemctl is-active \"$service\" 2>/dev/null || true)",
|
||||
"err=$(cat /tmp/web-probe-sentinel-caddy-python.err /tmp/web-probe-sentinel-caddy-validate.err /tmp/web-probe-sentinel-caddy-install.err /tmp/web-probe-sentinel-caddy-reload.err 2>/dev/null | tr '\\n' ';' | cut -c1-1000 || true)",
|
||||
"python3 - \"$python_rc\" \"$validate_rc\" \"$reload_rc\" \"$after_present\" \"$active\" \"$hostname\" \"$config_path\" \"$err\" <<'PY'",
|
||||
"err=$(cat /tmp/web-probe-sentinel-caddy-python.err /tmp/web-probe-sentinel-caddy-validate.err /tmp/web-probe-sentinel-caddy-install.err /tmp/web-probe-sentinel-caddy-reload.err /tmp/web-probe-sentinel-caddy-probe.err 2>/dev/null | tr '\\n' ';' | cut -c1-1000 || true)",
|
||||
"python3 - \"$python_rc\" \"$validate_rc\" \"$reload_rc\" \"$probe_rc\" \"$probe_status\" \"$after_present\" \"$active\" \"$hostname\" \"$config_path\" \"$err\" <<'PY'",
|
||||
"import json, sys",
|
||||
"python_rc, validate_rc, reload_rc, after_present, active, hostname, config_path, error_preview = sys.argv[1:9]",
|
||||
"python_rc, validate_rc, reload_rc, probe_rc, probe_status, after_present, active, hostname, config_path, error_preview = sys.argv[1:11]",
|
||||
"def num(value):",
|
||||
" if value == '':",
|
||||
" return None",
|
||||
@@ -2485,13 +2529,18 @@ function applySentinelCaddyBlock(state: SentinelCicdState, timeoutSeconds: numbe
|
||||
" return int(value)",
|
||||
" except ValueError:",
|
||||
" return None",
|
||||
"http_status = num(probe_status)",
|
||||
"probe_ok = num(probe_rc) == 0 and http_status is not None and 200 <= http_status < 400",
|
||||
"payload = {",
|
||||
" 'ok': num(python_rc) == 0 and num(validate_rc) == 0 and num(reload_rc) == 0 and after_present == 'yes',",
|
||||
" 'ok': num(python_rc) == 0 and num(validate_rc) == 0 and num(reload_rc) == 0 and probe_ok and after_present == 'yes',",
|
||||
" 'hostname': hostname,",
|
||||
" 'configPath': config_path,",
|
||||
" 'pythonExitCode': num(python_rc),",
|
||||
" 'validateExitCode': num(validate_rc),",
|
||||
" 'reloadExitCode': num(reload_rc),",
|
||||
" 'routeProbeExitCode': num(probe_rc),",
|
||||
" 'routeProbeHttpStatus': http_status,",
|
||||
" 'routeProbeOk': probe_ok,",
|
||||
" 'afterBlockPresent': after_present == 'yes',",
|
||||
" 'active': active,",
|
||||
" 'errorPreview': error_preview,",
|
||||
@@ -3294,11 +3343,11 @@ function renderControlPlaneResult(result: Record<string, unknown>): string {
|
||||
"",
|
||||
Object.keys(runtimeSecretsApply).length === 0 ? "RUNTIME_SECRETS\n-" : table(["OK", "SECRETS", "KEYS", "SKIPPED"], [[runtimeSecretsApply.ok, runtimeSecretsApply.secretCount ?? "-", runtimeSecretsApply.keyCount ?? "-", runtimeSecretsApply.skippedKeyCount ?? "-"]]),
|
||||
"",
|
||||
Object.keys(publicExposureApply).length === 0 ? "PUBLIC_EXPOSURE_APPLY\n-" : table(["OK", "SECRET", "CADDY", "HOST"], [[publicExposureApply.ok, record(publicExposureApply.secret).ok, record(publicExposureApply.caddy).ok, publicExposureApply.hostname]]),
|
||||
Object.keys(publicExposureApply).length === 0 ? "PUBLIC_EXPOSURE_APPLY\n-" : table(["OK", "SECRET", "CADDY", "HOST", "ROUTE_HTTP"], [[publicExposureApply.ok, record(publicExposureApply.secret).ok, record(publicExposureApply.caddy).ok, publicExposureApply.hostname, record(publicExposureApply.caddy).routeProbeHttpStatus ?? "-"]]),
|
||||
"",
|
||||
Object.keys(publicExposureCaddy).length === 0 || publicExposureCaddy.ok === true
|
||||
? "CADDY_APPLY_DETAIL\n-"
|
||||
: table(["PY", "VALIDATE", "RELOAD", "BLOCK", "ACTIVE", "ERROR", "STDOUT", "STDERR"], [[publicExposureCaddy.pythonExitCode, publicExposureCaddy.validateExitCode, publicExposureCaddy.reloadExitCode, publicExposureCaddy.afterBlockPresent, publicExposureCaddy.active, short(publicExposureCaddy.errorPreview), short(record(publicExposureCaddy.result).stdoutPreview), short(record(publicExposureCaddy.result).stderrPreview)]]),
|
||||
: table(["PY", "VALIDATE", "RELOAD", "PROBE", "HTTP", "BLOCK", "ACTIVE", "ERROR", "STDOUT", "STDERR"], [[publicExposureCaddy.pythonExitCode, publicExposureCaddy.validateExitCode, publicExposureCaddy.reloadExitCode, publicExposureCaddy.routeProbeExitCode, publicExposureCaddy.routeProbeHttpStatus, publicExposureCaddy.afterBlockPresent, publicExposureCaddy.active, short(publicExposureCaddy.errorPreview), short(record(publicExposureCaddy.result).stdoutPreview), short(record(publicExposureCaddy.result).stderrPreview)]]),
|
||||
"",
|
||||
Object.keys(argoApply).length === 0 ? "ARGO_APPLY\n-" : table(["OK", "EXIT", "PREVIEW"], [[argoApply.ok, record(argoApply.result).exitCode, record(argoApply.result).stdoutPreview]]),
|
||||
"",
|
||||
|
||||
Reference in New Issue
Block a user