Merge pull request #1164 from pikasTech/fix/1148-moonbridge-runtime-image
fix: add YAML runtime image build for D518 HWLAB
This commit is contained in:
@@ -427,6 +427,15 @@ lanes:
|
||||
runtimeImageRewrites:
|
||||
- source: fatedier/frpc:v0.68.1
|
||||
target: 127.0.0.1:5000/hwlab/frpc:v0.68.1
|
||||
runtimeImageBuilds:
|
||||
- id: moonbridge
|
||||
kind: moonbridge
|
||||
target: 127.0.0.1:5000/hwlab/moonbridge:1b99888d3dae
|
||||
sourceRepo: https://github.com/ZhiYi-R/moon-bridge.git
|
||||
sourceRef: 1b99888d3dae889b79ee602cb875c7907f7e76f2
|
||||
builderImage: golang:1.25-bookworm
|
||||
goProxy: https://goproxy.cn,direct
|
||||
dockerNetworkMode: host
|
||||
public:
|
||||
webUrl: https://hwlab.pikapython.com
|
||||
apiUrl: https://hwlab.pikapython.com
|
||||
@@ -628,6 +637,15 @@ lanes:
|
||||
runtimeImageRewrites:
|
||||
- source: fatedier/frpc:v0.68.1
|
||||
target: 127.0.0.1:5000/hwlab/frpc:v0.68.1
|
||||
runtimeImageBuilds:
|
||||
- id: moonbridge
|
||||
kind: moonbridge
|
||||
target: 127.0.0.1:5000/hwlab/moonbridge:1b99888d3dae
|
||||
sourceRepo: https://github.com/ZhiYi-R/moon-bridge.git
|
||||
sourceRef: 1b99888d3dae889b79ee602cb875c7907f7e76f2
|
||||
builderImage: golang:1.25-bookworm
|
||||
goProxy: https://goproxy.cn,direct
|
||||
dockerNetworkMode: host
|
||||
public:
|
||||
webUrl: https://hwlab.pikapython.com
|
||||
apiUrl: https://hwlab.pikapython.com
|
||||
@@ -795,9 +813,9 @@ networkProfiles:
|
||||
- 82.156.23.220
|
||||
- 74.48.78.17
|
||||
dockerBuildProxy:
|
||||
http: http://sub2api-egress-proxy.platform-infra.svc.cluster.local:10808
|
||||
https: http://sub2api-egress-proxy.platform-infra.svc.cluster.local:10808
|
||||
all: http://sub2api-egress-proxy.platform-infra.svc.cluster.local:10808
|
||||
http: http://127.0.0.1:10808
|
||||
https: http://127.0.0.1:10808
|
||||
all: http://127.0.0.1:10808
|
||||
noProxy:
|
||||
- localhost
|
||||
- 127.0.0.1
|
||||
@@ -832,9 +850,9 @@ networkProfiles:
|
||||
- 82.156.23.220
|
||||
- 74.48.78.17
|
||||
dockerBuildProxy:
|
||||
http: http://sub2api-egress-proxy.platform-infra.svc.cluster.local:10808
|
||||
https: http://sub2api-egress-proxy.platform-infra.svc.cluster.local:10808
|
||||
all: http://sub2api-egress-proxy.platform-infra.svc.cluster.local:10808
|
||||
http: http://127.0.0.1:10808
|
||||
https: http://127.0.0.1:10808
|
||||
all: http://127.0.0.1:10808
|
||||
noProxy:
|
||||
- localhost
|
||||
- 127.0.0.1
|
||||
|
||||
@@ -246,7 +246,7 @@ targets:
|
||||
image: ghcr.io/sagernet/sing-box:latest
|
||||
imagePullPolicy: IfNotPresent
|
||||
listenPort: 10808
|
||||
hostNetwork: false
|
||||
hostNetwork: true
|
||||
sourceConfigRef: config/platform-infra/egress-proxy-sources.yaml#sources.master-shadowsocks
|
||||
applyToSub2Api: true
|
||||
applyToSentinel: true
|
||||
|
||||
@@ -254,6 +254,17 @@ export interface HwlabRuntimeImageRewriteSpec {
|
||||
readonly target: string;
|
||||
}
|
||||
|
||||
export interface HwlabRuntimeImageBuildSpec {
|
||||
readonly id: string;
|
||||
readonly kind: "moonbridge";
|
||||
readonly target: string;
|
||||
readonly sourceRepo: string;
|
||||
readonly sourceRef: string;
|
||||
readonly builderImage: string;
|
||||
readonly goProxy: string;
|
||||
readonly dockerNetworkMode: "default" | "host";
|
||||
}
|
||||
|
||||
export interface HwlabRuntimePublicExposureFrpcProxySpec {
|
||||
readonly name: string;
|
||||
readonly localIP: string;
|
||||
@@ -346,6 +357,7 @@ export interface HwlabRuntimeLaneSpec {
|
||||
readonly publicExposure: HwlabRuntimePublicExposureSpec | null;
|
||||
readonly observability: HwlabRuntimeObservabilitySpec;
|
||||
readonly runtimeImageRewrites: readonly HwlabRuntimeImageRewriteSpec[];
|
||||
readonly runtimeImageBuilds: readonly HwlabRuntimeImageBuildSpec[];
|
||||
readonly networkProfileId: string;
|
||||
readonly downloadProfileId: string;
|
||||
readonly networkProfile: HwlabNetworkProfileSpec;
|
||||
@@ -395,6 +407,7 @@ interface HwlabLaneConfig {
|
||||
readonly publicExposure: HwlabRuntimePublicExposureSpec | null;
|
||||
readonly observability: HwlabRuntimeObservabilitySpec;
|
||||
readonly runtimeImageRewrites: readonly HwlabRuntimeImageRewriteSpec[];
|
||||
readonly runtimeImageBuilds: readonly HwlabRuntimeImageBuildSpec[];
|
||||
}
|
||||
|
||||
interface HwlabNodeLaneConfig {
|
||||
@@ -421,6 +434,12 @@ function stringField(obj: Record<string, unknown>, key: string, path: string): s
|
||||
return value;
|
||||
}
|
||||
|
||||
function enumStringField<T extends string>(obj: Record<string, unknown>, key: string, path: string, allowed: readonly T[]): T {
|
||||
const value = stringField(obj, key, path);
|
||||
if (!allowed.includes(value as T)) throw new Error(`${path}.${key} must be one of ${allowed.join(", ")}`);
|
||||
return value as T;
|
||||
}
|
||||
|
||||
function numberField(obj: Record<string, unknown>, key: string, path: string): number {
|
||||
const value = obj[key];
|
||||
if (typeof value !== "number" || !Number.isInteger(value) || value <= 0) throw new Error(`${path}.${key} must be a positive integer`);
|
||||
@@ -630,6 +649,7 @@ function laneConfig(id: HwlabRuntimeLane, raw: Record<string, unknown>): HwlabLa
|
||||
publicExposure: publicExposureConfig(raw.publicExposure, `lanes.${id}.publicExposure`),
|
||||
observability: observabilityConfig(raw.observability, `lanes.${id}.observability`),
|
||||
runtimeImageRewrites: runtimeImageRewritesConfig(raw.runtimeImageRewrites, `lanes.${id}.runtimeImageRewrites`),
|
||||
runtimeImageBuilds: runtimeImageBuildsConfig(raw.runtimeImageBuilds, `lanes.${id}.runtimeImageBuilds`),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1149,6 +1169,27 @@ function runtimeImageRewritesConfig(value: unknown, path: string): HwlabRuntimeI
|
||||
});
|
||||
}
|
||||
|
||||
function runtimeImageBuildsConfig(value: unknown, path: string): HwlabRuntimeImageBuildSpec[] {
|
||||
if (value === undefined) return [];
|
||||
if (!Array.isArray(value)) throw new Error(`${path} must be an array`);
|
||||
return value.map((item, index) => {
|
||||
const itemPath = `${path}[${index}]`;
|
||||
const raw = asRecord(item, itemPath);
|
||||
const kind = stringField(raw, "kind", itemPath);
|
||||
if (kind !== "moonbridge") throw new Error(`${itemPath}.kind must be moonbridge`);
|
||||
return {
|
||||
id: stringField(raw, "id", itemPath),
|
||||
kind,
|
||||
target: stringField(raw, "target", itemPath),
|
||||
sourceRepo: stringField(raw, "sourceRepo", itemPath),
|
||||
sourceRef: stringField(raw, "sourceRef", itemPath),
|
||||
builderImage: stringField(raw, "builderImage", itemPath),
|
||||
goProxy: stringField(raw, "goProxy", itemPath),
|
||||
dockerNetworkMode: enumStringField(raw, "dockerNetworkMode", itemPath, ["default", "host"]),
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
function readHwlabNodeLaneConfig(): HwlabNodeLaneConfig {
|
||||
const path = rootPath(HWLAB_NODE_LANE_CONFIG_PATH);
|
||||
const raw = readFileSync(path, "utf8");
|
||||
@@ -1273,6 +1314,7 @@ function buildRuntimeLaneSpec(config: HwlabLaneConfig): HwlabRuntimeLaneSpec {
|
||||
publicExposure: config.publicExposure,
|
||||
observability: config.observability,
|
||||
runtimeImageRewrites: config.runtimeImageRewrites,
|
||||
runtimeImageBuilds: config.runtimeImageBuilds,
|
||||
networkProfileId: networkProfile.id,
|
||||
downloadProfileId: downloadProfile.id,
|
||||
networkProfile,
|
||||
|
||||
@@ -153,6 +153,7 @@ export function nodeRuntimeExpected(spec: HwlabRuntimeLaneSpec): Record<string,
|
||||
},
|
||||
observability: spec.observability,
|
||||
runtimeImageRewrites: spec.runtimeImageRewrites,
|
||||
runtimeImageBuilds: spec.runtimeImageBuilds,
|
||||
externalPostgres: spec.externalPostgres === undefined ? null : {
|
||||
active: activeExternalPostgres !== undefined,
|
||||
provider: spec.externalPostgres.provider,
|
||||
|
||||
@@ -33,7 +33,7 @@ import { isCommandSuccess, nodeRuntimeGitopsRoot, runNodeK3sArgs, runNodeK3sScri
|
||||
import { nodeRuntimeCicdWaitWarning } from "./git-mirror";
|
||||
import { parseNodeScopedDelegatedOptions } from "./plan";
|
||||
import { nodeRuntimePipelineFailureSummary, nodeRuntimePipelineRunDiagnostics } from "./render";
|
||||
import { compactRuntimeCommand, runNodeHostScript } from "./runtime-common";
|
||||
import { compactRuntimeCommand, runNodeHostScript, runNodeHostScriptAsync } from "./runtime-common";
|
||||
import { assertLane, assertNodeId, keyValueLinesFromText, numericField, optionValue, optionalStringValue, positiveIntegerOption, positiveIntegerValue, record, requiredOption, shellQuote, statusText, stringValue, stripOptions } from "./utils";
|
||||
import { discoverWebObserveIndexEntry, readWebObserveIndexEntry } from "./web-observe-render";
|
||||
import { assertKnownOptions, nodeWebProbeAutoCommandTimeoutSeconds, nodeWebProbeDefaultUrl, normalizeNodeWebProbeObserveArgs, parseNodeWebProbeObserveOptions, parseNodeWebProbeSentinelOptions, parseWebProbeBrowserProxyMode } from "./web-probe-observe";
|
||||
@@ -95,6 +95,7 @@ export function nodeRuntimeRenderOverlay(spec: HwlabRuntimeLaneSpec): Record<str
|
||||
observability: spec.observability,
|
||||
runtimeStore: spec.runtimeStore,
|
||||
runtimeImageRewrites: spec.runtimeImageRewrites,
|
||||
runtimeImageBuilds: spec.runtimeImageBuilds,
|
||||
registryPrefix: spec.registryPrefix,
|
||||
buildkitSidecarImage: spec.buildkit?.sidecarImage,
|
||||
publicWebUrl: spec.publicWebUrl,
|
||||
@@ -809,24 +810,54 @@ interface NodeRuntimeImageDependency {
|
||||
id: string;
|
||||
target: string;
|
||||
source: string;
|
||||
mode: "pull" | "build-moonbridge";
|
||||
sourceRepo?: string;
|
||||
sourceRef?: string;
|
||||
builderImage?: string;
|
||||
goProxy?: string;
|
||||
dockerNetworkMode?: "default" | "host";
|
||||
}
|
||||
|
||||
function nodeRuntimeImageDependencies(spec: HwlabRuntimeLaneSpec): NodeRuntimeImageDependency[] {
|
||||
const images: NodeRuntimeImageDependency[] = [
|
||||
{ id: "base", target: spec.baseImage, source: spec.baseImageSource ?? "" },
|
||||
{ id: "base", target: spec.baseImage, source: spec.baseImageSource ?? "", mode: "pull" },
|
||||
];
|
||||
if (spec.buildkit !== undefined) {
|
||||
images.push({ id: "buildkit", target: spec.buildkit.sidecarImage, source: spec.buildkit.sourceImage });
|
||||
images.push({ id: "buildkit", target: spec.buildkit.sidecarImage, source: spec.buildkit.sourceImage, mode: "pull" });
|
||||
}
|
||||
spec.runtimeImageRewrites.forEach((rewrite, index) => {
|
||||
images.push({ id: `rewrite-${index + 1}`, target: rewrite.target, source: rewrite.source });
|
||||
images.push({ id: `rewrite-${index + 1}`, target: rewrite.target, source: rewrite.source, mode: "pull" });
|
||||
});
|
||||
spec.runtimeImageBuilds.forEach((build) => {
|
||||
images.push({
|
||||
id: build.id,
|
||||
target: build.target,
|
||||
source: `${build.sourceRepo}#${build.sourceRef}`,
|
||||
mode: "build-moonbridge",
|
||||
sourceRepo: build.sourceRepo,
|
||||
sourceRef: build.sourceRef,
|
||||
builderImage: build.builderImage,
|
||||
goProxy: build.goProxy,
|
||||
dockerNetworkMode: build.dockerNetworkMode,
|
||||
});
|
||||
});
|
||||
return images;
|
||||
}
|
||||
|
||||
function nodeRuntimeImageCalls(spec: HwlabRuntimeLaneSpec, functionName: string): string {
|
||||
return nodeRuntimeImageDependencies(spec)
|
||||
.map((image) => `${functionName} ${shellQuote(image.id)} ${shellQuote(image.target)} ${shellQuote(image.source)}`)
|
||||
.map((image) => [
|
||||
functionName,
|
||||
image.id,
|
||||
image.target,
|
||||
image.source,
|
||||
image.mode,
|
||||
image.sourceRepo ?? "",
|
||||
image.sourceRef ?? "",
|
||||
image.builderImage ?? "",
|
||||
image.goProxy ?? "",
|
||||
image.dockerNetworkMode ?? "",
|
||||
].map(shellQuote).join(" "))
|
||||
.join("\n");
|
||||
}
|
||||
|
||||
@@ -845,7 +876,7 @@ export function nodeRuntimeBaseImageStatus(spec: HwlabRuntimeLaneSpec, timeoutSe
|
||||
const script = [
|
||||
"set -eu",
|
||||
"check_image() {",
|
||||
" id=\"$1\"; target=\"$2\"; source=\"$3\"",
|
||||
" id=\"$1\"; target=\"$2\"; source=\"$3\"; mode=\"$4\"",
|
||||
" repo_tag=${target#*/}",
|
||||
" repo=${repo_tag%:*}",
|
||||
" tag=${repo_tag##*:}",
|
||||
@@ -856,12 +887,12 @@ export function nodeRuntimeBaseImageStatus(spec: HwlabRuntimeLaneSpec, timeoutSe
|
||||
" target_present=false",
|
||||
" if docker image inspect \"$target\" >/dev/null 2>&1; then target_present=true; fi",
|
||||
" source_present=false",
|
||||
" if [ -n \"$source\" ] && docker image inspect \"$source\" >/dev/null 2>&1; then source_present=true; fi",
|
||||
" if [ \"$mode\" = build-moonbridge ]; then source_present=true; elif [ -n \"$source\" ] && docker image inspect \"$source\" >/dev/null 2>&1; then source_present=true; fi",
|
||||
" source_configured=false",
|
||||
" if [ -n \"$source\" ]; then source_configured=true; fi",
|
||||
" python3 - \"$id\" \"$target\" \"$source\" \"$source_configured\" \"$registry_url\" \"$registry_present\" \"$target_present\" \"$source_present\" <<'PY'",
|
||||
" python3 - \"$id\" \"$target\" \"$source\" \"$mode\" \"$source_configured\" \"$registry_url\" \"$registry_present\" \"$target_present\" \"$source_present\" <<'PY'",
|
||||
"import json, sys",
|
||||
"keys=['id','target','source','sourceConfigured','registryUrl','registryTagPresent','targetImagePresent','sourceImagePresent']",
|
||||
"keys=['id','target','source','mode','sourceConfigured','registryUrl','registryTagPresent','targetImagePresent','sourceImagePresent']",
|
||||
"data=dict(zip(keys, sys.argv[1:]))",
|
||||
"for key in ['sourceConfigured','registryTagPresent','targetImagePresent','sourceImagePresent']:",
|
||||
" data[key] = data[key] == 'true'",
|
||||
@@ -893,9 +924,57 @@ export function ensureNodeBaseImage(spec: HwlabRuntimeLaneSpec, dryRun: boolean,
|
||||
"set -eu",
|
||||
`dry_run=${shellQuote(dryRun ? "true" : "false")}`,
|
||||
`pull_retries=${shellQuote(String(Math.max(1, spec.downloadProfile.docker.pullRetries)))}`,
|
||||
`build_http_proxy=${shellQuote(spec.networkProfile.dockerBuildProxy.http)}`,
|
||||
`build_https_proxy=${shellQuote(spec.networkProfile.dockerBuildProxy.https)}`,
|
||||
`build_all_proxy=${shellQuote(spec.networkProfile.dockerBuildProxy.all)}`,
|
||||
`build_no_proxy=${shellQuote(spec.networkProfile.dockerBuildProxy.noProxy.join(","))}`,
|
||||
"resolve_host_proxy_url() {",
|
||||
" raw_url=\"$1\"",
|
||||
" if [ -z \"$raw_url\" ]; then printf '\\n'; return 0; fi",
|
||||
" info=$(python3 - \"$raw_url\" <<'PY'",
|
||||
"from urllib.parse import urlparse",
|
||||
"import sys",
|
||||
"url = sys.argv[1]",
|
||||
"parsed = urlparse(url)",
|
||||
"host = parsed.hostname or ''",
|
||||
"port = parsed.port",
|
||||
"scheme = parsed.scheme or 'http'",
|
||||
"parts = host.split('.')",
|
||||
"if len(parts) >= 4 and parts[2] == 'svc':",
|
||||
" print('\\t'.join(['service', scheme, parts[0], parts[1], str(port or 80)]))",
|
||||
"else:",
|
||||
" print('\\t'.join(['literal', url]))",
|
||||
"PY",
|
||||
" )",
|
||||
" kind=$(printf '%s' \"$info\" | cut -f1)",
|
||||
" service_cluster_ip() {",
|
||||
" service_namespace=\"$1\"; service_name=\"$2\"",
|
||||
" cluster_ip=\"\"",
|
||||
" if command -v kubectl >/dev/null 2>&1; then",
|
||||
" cluster_ip=$(kubectl -n \"$service_namespace\" get svc \"$service_name\" -o jsonpath='{.spec.clusterIP}' 2>/dev/null || true)",
|
||||
" fi",
|
||||
" if { [ -z \"$cluster_ip\" ] || [ \"$cluster_ip\" = None ]; } && command -v k3s >/dev/null 2>&1; then",
|
||||
" cluster_ip=$(k3s kubectl -n \"$service_namespace\" get svc \"$service_name\" -o jsonpath='{.spec.clusterIP}' 2>/dev/null || true)",
|
||||
" fi",
|
||||
" printf '%s\\n' \"$cluster_ip\"",
|
||||
" }",
|
||||
" if [ \"$kind\" = service ]; then",
|
||||
" scheme=$(printf '%s' \"$info\" | cut -f2)",
|
||||
" service_name=$(printf '%s' \"$info\" | cut -f3)",
|
||||
" service_namespace=$(printf '%s' \"$info\" | cut -f4)",
|
||||
" service_port=$(printf '%s' \"$info\" | cut -f5)",
|
||||
" cluster_ip=$(service_cluster_ip \"$service_namespace\" \"$service_name\")",
|
||||
" if [ -n \"$cluster_ip\" ] && [ \"$cluster_ip\" != None ]; then printf '%s://%s:%s\\n' \"$scheme\" \"$cluster_ip\" \"$service_port\"; else printf '%s\\n' \"$raw_url\"; fi",
|
||||
" else",
|
||||
" printf '%s\\n' \"$raw_url\"",
|
||||
" fi",
|
||||
"}",
|
||||
"build_http_proxy=$(resolve_host_proxy_url \"$build_http_proxy\")",
|
||||
"build_https_proxy=$(resolve_host_proxy_url \"$build_https_proxy\")",
|
||||
"build_all_proxy=$(resolve_host_proxy_url \"$build_all_proxy\")",
|
||||
"failed=false",
|
||||
"ensure_image() {",
|
||||
" id=\"$1\"; target=\"$2\"; source=\"$3\"",
|
||||
" id=\"$1\"; target=\"$2\"; source=\"$3\"; mode=\"$4\"; source_repo=\"$5\"; source_ref=\"$6\"; builder_image=\"$7\"; go_proxy=\"$8\"; docker_network_mode=\"$9\"",
|
||||
" repo_tag=${target#*/}",
|
||||
" repo=${repo_tag%:*}",
|
||||
" tag=${repo_tag##*:}",
|
||||
@@ -910,13 +989,47 @@ export function ensureNodeBaseImage(spec: HwlabRuntimeLaneSpec, dryRun: boolean,
|
||||
" if [ \"$present\" = false ]; then",
|
||||
" action=seed",
|
||||
" if [ \"$dry_run\" = false ]; then",
|
||||
" if [ \"$mode\" = build-moonbridge ]; then",
|
||||
" action=build",
|
||||
" source_present_before=build-source",
|
||||
" tmpdir=$(mktemp -d /tmp/hwlab-node-runtime-image-$id.XXXXXX)",
|
||||
" dockerfile=\"$tmpdir/Dockerfile\"",
|
||||
" cat > \"$dockerfile\" <<'DOCKERFILE'",
|
||||
"ARG BUILDER_IMAGE",
|
||||
"FROM ${BUILDER_IMAGE} AS builder",
|
||||
"ARG MOONBRIDGE_REPO",
|
||||
"ARG MOONBRIDGE_REF",
|
||||
"ARG GOPROXY_VALUE",
|
||||
"ENV GOPROXY=${GOPROXY_VALUE}",
|
||||
"WORKDIR /src",
|
||||
"RUN git clone --filter=blob:none \"${MOONBRIDGE_REPO}\" moon-bridge",
|
||||
"WORKDIR /src/moon-bridge",
|
||||
"RUN git checkout \"${MOONBRIDGE_REF}\" \\",
|
||||
" && go mod download \\",
|
||||
" && CGO_ENABLED=0 GOOS=linux go build -trimpath -ldflags=\"-s -w\" -o /out/moonbridge ./cmd/moonbridge",
|
||||
"FROM scratch",
|
||||
"WORKDIR /app",
|
||||
"COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt",
|
||||
"COPY --from=builder /out/moonbridge /app/moonbridge",
|
||||
"EXPOSE 4001",
|
||||
"USER 65532:65532",
|
||||
"ENTRYPOINT [\"/app/moonbridge\"]",
|
||||
"DOCKERFILE",
|
||||
" if env HTTP_PROXY=\"$build_http_proxy\" HTTPS_PROXY=\"$build_https_proxy\" ALL_PROXY=\"$build_all_proxy\" NO_PROXY=\"$build_no_proxy\" http_proxy=\"$build_http_proxy\" https_proxy=\"$build_https_proxy\" all_proxy=\"$build_all_proxy\" no_proxy=\"$build_no_proxy\" docker build --network \"$docker_network_mode\" --build-arg BUILDER_IMAGE=\"$builder_image\" --build-arg MOONBRIDGE_REPO=\"$source_repo\" --build-arg MOONBRIDGE_REF=\"$source_ref\" --build-arg GOPROXY_VALUE=\"$go_proxy\" --build-arg HTTP_PROXY=\"$build_http_proxy\" --build-arg HTTPS_PROXY=\"$build_https_proxy\" --build-arg ALL_PROXY=\"$build_all_proxy\" --build-arg NO_PROXY=\"$build_no_proxy\" -t \"$target\" -f \"$dockerfile\" \"$tmpdir\" >/tmp/hwlab-node-runtime-image-$id-build.out 2>&1; then",
|
||||
" docker push \"$target\" >/tmp/hwlab-node-runtime-image-$id-push.out 2>&1 || { cat /tmp/hwlab-node-runtime-image-$id-push.out >&2 2>/dev/null || true; failed=true; }",
|
||||
" else",
|
||||
" cat /tmp/hwlab-node-runtime-image-$id-build.out >&2 2>/dev/null || true",
|
||||
" failed=true",
|
||||
" fi",
|
||||
" rm -rf \"$tmpdir\"",
|
||||
" else",
|
||||
" source_present_before=true",
|
||||
" if ! docker image inspect \"$source\" >/dev/null 2>&1; then",
|
||||
" source_present_before=false",
|
||||
" attempt=1",
|
||||
" while [ \"$attempt\" -le \"$pull_retries\" ]; do",
|
||||
" pull_attempts=$attempt",
|
||||
" if docker pull \"$source\" >/tmp/hwlab-node-runtime-image-$id-pull.out 2>&1; then pulled_source=true; break; fi",
|
||||
" if env HTTP_PROXY=\"$build_http_proxy\" HTTPS_PROXY=\"$build_https_proxy\" ALL_PROXY=\"$build_all_proxy\" NO_PROXY=\"$build_no_proxy\" http_proxy=\"$build_http_proxy\" https_proxy=\"$build_https_proxy\" all_proxy=\"$build_all_proxy\" no_proxy=\"$build_no_proxy\" docker pull \"$source\" >/tmp/hwlab-node-runtime-image-$id-pull.out 2>&1; then pulled_source=true; break; fi",
|
||||
" attempt=$((attempt + 1))",
|
||||
" sleep 2",
|
||||
" done",
|
||||
@@ -930,13 +1043,14 @@ export function ensureNodeBaseImage(spec: HwlabRuntimeLaneSpec, dryRun: boolean,
|
||||
" failed=true",
|
||||
" fi",
|
||||
" fi",
|
||||
" fi",
|
||||
" fi",
|
||||
" fi",
|
||||
" after=false",
|
||||
" if curl -fsS \"$registry_url\" 2>/dev/null | grep -q '\"'\"$tag\"'\"'; then after=true; fi",
|
||||
" python3 - \"$id\" \"$target\" \"$source\" \"$dry_run\" \"$present\" \"$after\" \"$action\" \"$pull_retries\" \"$source_present_before\" \"$pulled_source\" \"$pull_attempts\" <<'PY'",
|
||||
" python3 - \"$id\" \"$target\" \"$source\" \"$mode\" \"$dry_run\" \"$present\" \"$after\" \"$action\" \"$pull_retries\" \"$source_present_before\" \"$pulled_source\" \"$pull_attempts\" <<'PY'",
|
||||
"import json, sys",
|
||||
"keys=['id','target','source','dryRun','presentBefore','presentAfter','action','pullRetries','sourcePresentBefore','pulledSource','pullAttempts']",
|
||||
"keys=['id','target','source','mode','dryRun','presentBefore','presentAfter','action','pullRetries','sourcePresentBefore','pulledSource','pullAttempts']",
|
||||
"data=dict(zip(keys, sys.argv[1:]))",
|
||||
"for key in ['dryRun','presentBefore','presentAfter','pulledSource']:",
|
||||
" data[key] = data[key] == 'true'",
|
||||
@@ -949,7 +1063,10 @@ export function ensureNodeBaseImage(spec: HwlabRuntimeLaneSpec, dryRun: boolean,
|
||||
nodeRuntimeImageCalls(spec, "ensure_image"),
|
||||
"if [ \"$failed\" = true ]; then exit 1; fi",
|
||||
].join("\n");
|
||||
const result = runNodeHostScript(spec, script, Math.min(timeoutSeconds, 300));
|
||||
const hasBuild = nodeRuntimeImageDependencies(spec).some((image) => image.mode === "build-moonbridge");
|
||||
const result = hasBuild && !dryRun
|
||||
? runNodeHostScriptAsync(spec, script, Math.min(timeoutSeconds, 600), "runtime-image-build")
|
||||
: runNodeHostScript(spec, script, Math.min(timeoutSeconds, 300));
|
||||
const imageRows = parseNodeRuntimeImageRows(statusText(result));
|
||||
const base = imageRows.find((image) => image.id === "base") ?? {};
|
||||
return {
|
||||
|
||||
Reference in New Issue
Block a user