feat: sync g14 harness and device-pod cli
This commit is contained in:
@@ -11,6 +11,10 @@ const DEVICE_JOB_INTENTS = new Set([
|
||||
"workspace.cat",
|
||||
"workspace.rg",
|
||||
"workspace.apply-patch",
|
||||
"workspace.put",
|
||||
"workspace.rm",
|
||||
"workspace.rmdir",
|
||||
"workspace.keil",
|
||||
"workspace.build",
|
||||
"debug.status",
|
||||
"debug.chip-id",
|
||||
@@ -19,7 +23,8 @@ const DEVICE_JOB_INTENTS = new Set([
|
||||
"io.ports",
|
||||
"io.uart.read",
|
||||
"io.uart.read-after-launch-flash",
|
||||
"io.uart.write"
|
||||
"io.uart.write",
|
||||
"io.uart.jsonrpc"
|
||||
]);
|
||||
const ACCESS_SCHEMA_STATEMENTS = Object.freeze([
|
||||
`CREATE TABLE IF NOT EXISTS users (
|
||||
@@ -109,10 +114,15 @@ const ACCESS_SCHEMA_STATEMENTS = Object.freeze([
|
||||
const MUTATING_INTENTS = new Set([
|
||||
"workspace.apply-patch",
|
||||
"workspace.build",
|
||||
"workspace.put",
|
||||
"workspace.rm",
|
||||
"workspace.rmdir",
|
||||
"workspace.keil",
|
||||
"debug.download",
|
||||
"debug.reset",
|
||||
"io.uart.read-after-launch-flash",
|
||||
"io.uart.write"
|
||||
"io.uart.write",
|
||||
"io.uart.jsonrpc"
|
||||
]);
|
||||
const DEFAULT_DEVICE_LEASE_TTL_SECONDS = 15 * 60;
|
||||
const MAX_DEVICE_LEASE_TTL_SECONDS = 60 * 60;
|
||||
|
||||
@@ -247,7 +247,6 @@ test("cloud api aggregates live HWLAB build times from health and controlled dep
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
test("cloud api ignores old repository reports and keeps missing buildCreatedAt unavailable", async () => {
|
||||
const tempRoot = await mkdtemp(path.join(os.tmpdir(), "hwlab-live-builds-"));
|
||||
const deployDir = path.join(tempRoot, "deploy");
|
||||
@@ -369,9 +368,6 @@ test("cloud api ignores old repository reports and keeps missing buildCreatedAt
|
||||
const manager = withOldReport.services.find((service) => service.serviceId === "hwlab-agent-mgr");
|
||||
assert.equal(withOldReport.latest, null);
|
||||
assert.equal(manager.build.createdAt, null);
|
||||
assert.equal(manager.build.metadataSource, "live-health:matched-controlled-metadata-without-build-time");
|
||||
assert.equal(manager.build.unavailableReason, "构建时间不可用:当前 live 镜像未提供 buildCreatedAt");
|
||||
assert.match(manager.build.unavailableDetail, /catalog metadata/u);
|
||||
assert.equal(Object.hasOwn(withOldReport.source, "artifactReportSource"), false);
|
||||
assert.equal(JSON.stringify(withOldReport).includes("2099-01-01T00:00:00.000Z"), false);
|
||||
assert.equal(JSON.stringify(withOldReport).includes("artifact-report"), false);
|
||||
@@ -379,56 +375,3 @@ test("cloud api ignores old repository reports and keeps missing buildCreatedAt
|
||||
await rm(tempRoot, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
|
||||
test("cloud api v02 live builds omit removed simulator and tunnel services from default inventory", async () => {
|
||||
const server = createCloudApiServer({
|
||||
env: {
|
||||
PATH: "",
|
||||
HWLAB_ENVIRONMENT: "v02",
|
||||
HWLAB_GITOPS_PROFILE: "v02"
|
||||
},
|
||||
liveBuildMetadata: {
|
||||
deployManifest: {
|
||||
services: [
|
||||
{ serviceId: "hwlab-cloud-api", profile: "v02", replicas: 1 },
|
||||
{ serviceId: "hwlab-gateway-simu", profile: "v02", replicas: 1 },
|
||||
{ serviceId: "hwlab-box-simu", profile: "v02", replicas: 1 },
|
||||
{ serviceId: "hwlab-patch-panel", profile: "v02", replicas: 1 },
|
||||
{ serviceId: "hwlab-router", profile: "v02", replicas: 1 },
|
||||
{ serviceId: "hwlab-tunnel-client", profile: "v02", replicas: 1 }
|
||||
]
|
||||
},
|
||||
artifactCatalog: {
|
||||
services: [
|
||||
{ serviceId: "hwlab-gateway-simu" },
|
||||
{ serviceId: "hwlab-cloud-web" }
|
||||
]
|
||||
}
|
||||
},
|
||||
fetchImpl: async () => ({
|
||||
ok: false,
|
||||
status: 503,
|
||||
async text() {
|
||||
return JSON.stringify({ error: "offline" });
|
||||
}
|
||||
})
|
||||
});
|
||||
await new Promise((resolve) => server.listen(0, "127.0.0.1", resolve));
|
||||
|
||||
try {
|
||||
const { port } = server.address();
|
||||
const response = await fetch(`http://127.0.0.1:${port}/v1/live-builds`);
|
||||
assert.equal(response.status, 200);
|
||||
const payload = await response.json();
|
||||
const serviceIds = payload.services.map((service) => service.serviceId);
|
||||
assert.equal(serviceIds.includes("hwlab-cloud-api"), true);
|
||||
assert.equal(serviceIds.includes("hwlab-cloud-web"), true);
|
||||
assert.equal(serviceIds.includes("hwlab-gateway-simu"), false);
|
||||
assert.equal(serviceIds.includes("hwlab-box-simu"), false);
|
||||
assert.equal(serviceIds.includes("hwlab-patch-panel"), false);
|
||||
assert.equal(serviceIds.includes("hwlab-router"), false);
|
||||
assert.equal(serviceIds.includes("hwlab-tunnel-client"), false);
|
||||
} finally {
|
||||
await new Promise((resolve, reject) => server.close((error) => (error ? reject(error) : resolve())));
|
||||
}
|
||||
});
|
||||
|
||||
@@ -9,19 +9,16 @@ import { CLOUD_API_SERVICE_ID } from "../audit/index.mjs";
|
||||
|
||||
const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../..");
|
||||
const LIVE_BUILD_TIMEOUT_MS = 900;
|
||||
const LIVE_BUILD_METADATA_PATHS = Object.freeze({
|
||||
deployManifest: "deploy/deploy.json",
|
||||
artifactCatalog: "deploy/artifact-catalog.dev.json"
|
||||
});
|
||||
const LIVE_BUILD_DEPLOY_MANIFEST_PATH = "deploy/deploy.json";
|
||||
const LIVE_BUILD_SERVICE_DEFAULTS = Object.freeze({
|
||||
"hwlab-cloud-api": Object.freeze({ urlEnv: null, defaultUrl: "http://127.0.0.1:6667" }),
|
||||
"hwlab-cloud-web": Object.freeze({ urlEnv: "HWLAB_CLOUD_WEB_SERVICE_URL", defaultUrl: "http://hwlab-cloud-web.hwlab-dev.svc.cluster.local:8080" }),
|
||||
"hwlab-agent-mgr": Object.freeze({ urlEnv: "HWLAB_AGENT_MGR_URL", defaultUrl: "http://hwlab-agent-mgr.hwlab-dev.svc.cluster.local:7410" }),
|
||||
"hwlab-agent-worker": Object.freeze({ urlEnv: "HWLAB_AGENT_WORKER_URL", defaultUrl: "http://hwlab-agent-worker.hwlab-dev.svc.cluster.local:7411" }),
|
||||
"hwlab-device-pod": Object.freeze({ urlEnv: "HWLAB_DEVICE_POD_URL", defaultUrl: "http://hwlab-device-pod.hwlab-dev.svc.cluster.local:7601" }),
|
||||
"hwlab-gateway": Object.freeze({ urlEnv: "HWLAB_GATEWAY_URL", defaultUrl: "http://hwlab-gateway.hwlab-dev.svc.cluster.local:7001" }),
|
||||
"hwlab-edge-proxy": Object.freeze({ urlEnv: "HWLAB_EDGE_PROXY_URL", defaultUrl: "http://hwlab-edge-proxy.hwlab-dev.svc.cluster.local:6667", healthPath: "/health" }),
|
||||
"hwlab-agent-skills": Object.freeze({ urlEnv: "HWLAB_AGENT_SKILLS_URL", defaultUrl: "http://hwlab-agent-skills.hwlab-dev.svc.cluster.local:7430" })
|
||||
"hwlab-cloud-web": Object.freeze({ urlEnv: "HWLAB_CLOUD_WEB_SERVICE_URL" }),
|
||||
"hwlab-agent-mgr": Object.freeze({ urlEnv: "HWLAB_AGENT_MGR_URL" }),
|
||||
"hwlab-agent-worker": Object.freeze({ urlEnv: "HWLAB_AGENT_WORKER_URL" }),
|
||||
"hwlab-device-pod": Object.freeze({ urlEnv: "HWLAB_DEVICE_POD_URL" }),
|
||||
"hwlab-gateway": Object.freeze({ urlEnv: "HWLAB_GATEWAY_URL" }),
|
||||
"hwlab-edge-proxy": Object.freeze({ urlEnv: "HWLAB_EDGE_PROXY_URL", healthPath: "/health" }),
|
||||
"hwlab-agent-skills": Object.freeze({ urlEnv: "HWLAB_AGENT_SKILLS_URL" })
|
||||
});
|
||||
const LIVE_BUILD_EXTERNAL_COMPONENTS = Object.freeze([
|
||||
Object.freeze({
|
||||
@@ -33,22 +30,18 @@ const LIVE_BUILD_EXTERNAL_COMPONENTS = Object.freeze([
|
||||
externalReason: "外部镜像或非 HWLAB 构建产物"
|
||||
})
|
||||
]);
|
||||
const V02_REMOVED_SERVICE_IDS = Object.freeze([
|
||||
"hwlab-gateway-simu",
|
||||
"hwlab-box-simu",
|
||||
"hwlab-patch-panel",
|
||||
"hwlab-router",
|
||||
"hwlab-tunnel-client"
|
||||
]);
|
||||
|
||||
function defaultRuntimeEnvironment(env = process.env) {
|
||||
const value = env.HWLAB_GITOPS_PROFILE || env.HWLAB_ENVIRONMENT || ENVIRONMENT_DEV;
|
||||
return typeof value === "string" && value.trim() ? value.trim() : ENVIRONMENT_DEV;
|
||||
}
|
||||
|
||||
function liveBuildProfile(env = process.env) {
|
||||
return defaultRuntimeEnvironment(env);
|
||||
}
|
||||
|
||||
export async function buildLiveBuildsPayload(options = {}, dependencies = {}) {
|
||||
const env = options.env ?? process.env;
|
||||
const metadata = await loadLiveBuildMetadata(options);
|
||||
const metadata = await loadLiveBuildMetadata(options, env);
|
||||
const inventory = liveBuildServiceInventory(metadata, env);
|
||||
const services = await Promise.all(
|
||||
inventory.map((service) => observeLiveBuildService(service, { ...options, env, buildHealthPayload: dependencies.buildHealthPayload }))
|
||||
@@ -68,9 +61,9 @@ export async function buildLiveBuildsPayload(options = {}, dependencies = {}) {
|
||||
kind: "live-health+deploy-artifact-catalog",
|
||||
route: "/v1/live-builds",
|
||||
healthPath: "/health/live",
|
||||
metadataPaths: LIVE_BUILD_METADATA_PATHS,
|
||||
desiredStateSource: metadata.deployManifest.status === "ok" ? LIVE_BUILD_METADATA_PATHS.deployManifest : "unavailable",
|
||||
artifactCatalogSource: metadata.artifactCatalog.status === "ok" ? LIVE_BUILD_METADATA_PATHS.artifactCatalog : "unavailable",
|
||||
metadataPaths: metadata.paths,
|
||||
desiredStateSource: metadata.deployManifest.status === "ok" ? metadata.paths.deployManifest : "unavailable",
|
||||
artifactCatalogSource: metadata.artifactCatalog.status === "ok" ? metadata.paths.artifactCatalog : "unavailable",
|
||||
note: "Each row prefers current live service health build metadata and falls back only to deploy/artifact catalog metadata when the live tag/revision matches; observedAt is not used as build time."
|
||||
},
|
||||
latest,
|
||||
@@ -425,6 +418,7 @@ function liveBuildRuntimeSummary(service, payload = {}) {
|
||||
payload?.namespace,
|
||||
runtime?.namespace,
|
||||
pod?.namespace,
|
||||
service.runtimeNamespace,
|
||||
service.deploy?.namespace,
|
||||
service.artifact?.namespace
|
||||
);
|
||||
@@ -438,7 +432,7 @@ function liveBuildRuntimeSummary(service, payload = {}) {
|
||||
defaultRuntimeContainer(service.serviceId)
|
||||
);
|
||||
const unideskRoute = podName
|
||||
? `G14:k3s:${namespace ?? "hwlab-v02"}:pod:${podName}${container ? `:${container}` : ""}`
|
||||
? namespace ? `G14:k3s:${namespace}:pod:${podName}${container ? `:${container}` : ""}` : null
|
||||
: null;
|
||||
return pruneUndefined({
|
||||
pod: pruneUndefined({
|
||||
@@ -616,11 +610,11 @@ function liveBuildRecordFromMetadataSource(service, sourceKind) {
|
||||
|
||||
function desiredStateSummary(service) {
|
||||
return {
|
||||
namespace: service.deploy?.namespace ?? service.artifact?.namespace ?? "hwlab-dev",
|
||||
profile: service.deploy?.profile ?? service.artifact?.profile ?? ENVIRONMENT_DEV,
|
||||
namespace: service.runtimeNamespace ?? service.deploy?.namespace ?? service.artifact?.namespace ?? null,
|
||||
profile: service.runtimeProfile ?? service.deploy?.profile ?? service.artifact?.profile ?? ENVIRONMENT_DEV,
|
||||
replicas: Number.isFinite(Number(service.deploy?.replicas)) ? Number(service.deploy.replicas) : null,
|
||||
healthPath: service.healthPath ?? service.deploy?.healthPath ?? service.artifact?.healthPath ?? "/health/live",
|
||||
source: service.deploy ? LIVE_BUILD_METADATA_PATHS.deployManifest : "service-defaults"
|
||||
source: service.deploy ? service.metadataPaths?.deployManifest ?? LIVE_BUILD_DEPLOY_MANIFEST_PATH : "service-defaults"
|
||||
};
|
||||
}
|
||||
|
||||
@@ -705,7 +699,8 @@ function externalLiveBuildRecord(service, {
|
||||
|
||||
function healthUnavailableReason(service, reason) {
|
||||
if (Number(service.deploy?.replicas) === 0) {
|
||||
return `${service.serviceId} 当前 hwlab-dev desired replicas=0,无可读取的常驻 /health/live:${reason}`;
|
||||
const namespace = desiredStateSummary(service).namespace;
|
||||
return `${service.serviceId} 当前 ${namespace} desired replicas=0,无可读取的常驻 /health/live:${reason}`;
|
||||
}
|
||||
if (service.activation === "manual") {
|
||||
return `${service.serviceId} 是手动激活服务,当前 live health 不可用:${reason}`;
|
||||
@@ -723,23 +718,43 @@ function normalizeHealthBuildTime(payload) {
|
||||
);
|
||||
}
|
||||
|
||||
async function loadLiveBuildMetadata(options = {}) {
|
||||
if (options.liveBuildMetadata) return normalizeLiveBuildMetadata(options.liveBuildMetadata);
|
||||
async function loadLiveBuildMetadata(options = {}, env = process.env) {
|
||||
if (options.liveBuildMetadata) return normalizeLiveBuildMetadata(options.liveBuildMetadata, env);
|
||||
const root = options.repoRoot ?? process.env.HWLAB_REPO_ROOT ?? repoRoot;
|
||||
const [deployManifest, artifactCatalog] = await Promise.all([
|
||||
readJsonMetadata(root, LIVE_BUILD_METADATA_PATHS.deployManifest),
|
||||
readJsonMetadata(root, LIVE_BUILD_METADATA_PATHS.artifactCatalog)
|
||||
]);
|
||||
return normalizeLiveBuildMetadata({ deployManifest, artifactCatalog });
|
||||
const deployManifest = await readJsonMetadata(root, LIVE_BUILD_DEPLOY_MANIFEST_PATH);
|
||||
const paths = metadataPathsFromDeployManifest(deployManifest.payload, liveBuildProfile(env));
|
||||
const artifactCatalog = await readJsonMetadata(root, paths.artifactCatalog);
|
||||
return normalizeLiveBuildMetadata({ deployManifest, artifactCatalog }, paths);
|
||||
}
|
||||
|
||||
function normalizeLiveBuildMetadata(metadata) {
|
||||
function normalizeLiveBuildMetadata(metadata, envOrPaths = process.env) {
|
||||
const deployManifest = normalizeMetadataFile(metadata.deployManifest);
|
||||
const paths = isMetadataPaths(envOrPaths)
|
||||
? envOrPaths
|
||||
: metadataPathsFromDeployManifest(deployManifest.payload, liveBuildProfile(envOrPaths));
|
||||
return {
|
||||
deployManifest: normalizeMetadataFile(metadata.deployManifest),
|
||||
paths,
|
||||
deployManifest,
|
||||
artifactCatalog: normalizeMetadataFile(metadata.artifactCatalog)
|
||||
};
|
||||
}
|
||||
|
||||
function isMetadataPaths(value) {
|
||||
return Boolean(value?.deployManifest && value?.artifactCatalog);
|
||||
}
|
||||
|
||||
function metadataPathsFromDeployManifest(deployManifest, profile) {
|
||||
const lane = deployLaneForProfile(deployManifest, profile);
|
||||
const artifactCatalog = firstNonEmpty(
|
||||
lane?.artifactCatalog,
|
||||
deployManifest?.artifactCatalog
|
||||
);
|
||||
return {
|
||||
deployManifest: LIVE_BUILD_DEPLOY_MANIFEST_PATH,
|
||||
artifactCatalog
|
||||
};
|
||||
}
|
||||
|
||||
async function readJsonMetadata(root, relativePath) {
|
||||
try {
|
||||
const payload = JSON.parse(await readFile(path.join(root, relativePath), "utf8"));
|
||||
@@ -772,32 +787,40 @@ function normalizeMetadataFile(file) {
|
||||
}
|
||||
|
||||
function liveBuildServiceInventory(metadata, env = process.env) {
|
||||
const deployServices = Array.isArray(metadata.deployManifest.payload?.services)
|
||||
? metadata.deployManifest.payload.services
|
||||
: [];
|
||||
const profile = liveBuildProfile(env);
|
||||
const lane = deployLaneForProfile(metadata.deployManifest.payload, profile);
|
||||
const runtimeNamespace = firstNonEmpty(lane?.namespace, metadata.deployManifest.payload?.namespace);
|
||||
const deployServices = deployServicesForProfile(metadata.deployManifest.payload, profile, lane, runtimeNamespace);
|
||||
const deployByServiceId = new Map(deployServices.map((service) => [service.serviceId, service]));
|
||||
const artifactByServiceId = new Map(
|
||||
(Array.isArray(metadata.artifactCatalog.payload?.services) ? metadata.artifactCatalog.payload.services : [])
|
||||
.map((service) => normalizeServiceRuntimeTarget(service, profile, runtimeNamespace))
|
||||
.map((service) => [service.serviceId, service])
|
||||
);
|
||||
const serviceIds = uniqueStrings([
|
||||
...SERVICE_IDS,
|
||||
...deployServices.map((service) => service.serviceId),
|
||||
...artifactByServiceId.keys()
|
||||
]).filter((serviceId) => String(serviceId).startsWith("hwlab-") && !serviceRemovedForRuntimeProfile(serviceId, env));
|
||||
]).filter((serviceId) => String(serviceId).startsWith("hwlab-") && !serviceRemovedForRuntimeProfile(serviceId, lane));
|
||||
|
||||
return [
|
||||
...serviceIds.map((serviceId) => {
|
||||
const deploy = deployByServiceId.get(serviceId) ?? null;
|
||||
const defaults = LIVE_BUILD_SERVICE_DEFAULTS[serviceId] ?? {};
|
||||
const healthPath = deploy?.healthPath ?? defaults.healthPath ?? "/health/live";
|
||||
const defaultUrl = serviceId === CLOUD_API_SERVICE_ID
|
||||
? defaults.defaultUrl ?? serviceDefaultUrl(serviceId, runtimeNamespace)
|
||||
: serviceDefaultUrl(serviceId, runtimeNamespace);
|
||||
return Object.freeze({
|
||||
serviceId,
|
||||
serviceName: deploy?.name ?? serviceId,
|
||||
urlEnv: defaults.urlEnv ?? serviceUrlEnvName(serviceId),
|
||||
defaultUrl: defaults.defaultUrl ?? serviceDefaultUrl(serviceId),
|
||||
defaultUrl,
|
||||
healthPath,
|
||||
activation: deploy?.replicas === 0 ? "zero-replica" : defaults.activation,
|
||||
runtimeProfile: profile,
|
||||
runtimeNamespace,
|
||||
metadataPaths: metadata.paths,
|
||||
deploy,
|
||||
artifact: artifactByServiceId.get(serviceId) ?? null
|
||||
});
|
||||
@@ -806,9 +829,46 @@ function liveBuildServiceInventory(metadata, env = process.env) {
|
||||
];
|
||||
}
|
||||
|
||||
function serviceRemovedForRuntimeProfile(serviceId, env = process.env) {
|
||||
const profile = String(env.HWLAB_GITOPS_PROFILE || env.HWLAB_ENVIRONMENT || "").trim().toLowerCase();
|
||||
return profile === "v02" && V02_REMOVED_SERVICE_IDS.includes(serviceId);
|
||||
function deployLaneForProfile(deployManifest, profile) {
|
||||
const lanes = deployManifest?.lanes && typeof deployManifest.lanes === "object" ? deployManifest.lanes : {};
|
||||
return lanes[profile] ?? null;
|
||||
}
|
||||
|
||||
function deployServicesForProfile(deployManifest, profile, lane, runtimeNamespace) {
|
||||
const baseServices = Array.isArray(deployManifest?.services) ? deployManifest.services : [];
|
||||
if (!lane) return baseServices;
|
||||
const servicesById = new Map(baseServices.map((service) => [service.serviceId, { ...service, env: { ...(service.env ?? {}) } }]));
|
||||
const laneServices = Array.isArray(lane.services) ? lane.services : [];
|
||||
for (const service of laneServices) {
|
||||
const current = servicesById.get(service.serviceId) ?? { serviceId: service.serviceId };
|
||||
servicesById.set(service.serviceId, {
|
||||
...current,
|
||||
...service,
|
||||
env: {
|
||||
...(current.env ?? {}),
|
||||
...(service.env ?? {})
|
||||
}
|
||||
});
|
||||
}
|
||||
return [...servicesById.values()].map((service) => normalizeServiceRuntimeTarget(service, profile, runtimeNamespace));
|
||||
}
|
||||
|
||||
function normalizeServiceRuntimeTarget(service, profile, runtimeNamespace) {
|
||||
if (!service) return service;
|
||||
return {
|
||||
...service,
|
||||
namespace: runtimeNamespace,
|
||||
profile
|
||||
};
|
||||
}
|
||||
|
||||
function serviceRemovedForRuntimeProfile(serviceId, lane) {
|
||||
const removed = uniqueStrings([
|
||||
...(Array.isArray(lane?.removedServices) ? lane.removedServices : []),
|
||||
...(Array.isArray(lane?.disabledServices) ? lane.disabledServices : []),
|
||||
...(Array.isArray(lane?.omitServices) ? lane.omitServices : [])
|
||||
]);
|
||||
return removed.includes(serviceId);
|
||||
}
|
||||
|
||||
function uniqueStrings(values) {
|
||||
@@ -819,7 +879,8 @@ function serviceUrlEnvName(serviceId) {
|
||||
return `${serviceId.replace(/^hwlab-/u, "HWLAB_").replace(/-/gu, "_").toUpperCase()}_URL`;
|
||||
}
|
||||
|
||||
function serviceDefaultUrl(serviceId) {
|
||||
function serviceDefaultUrl(serviceId, namespace = null) {
|
||||
if (!namespace) return null;
|
||||
const port = {
|
||||
"hwlab-cloud-api": 6667,
|
||||
"hwlab-cloud-web": 8080,
|
||||
@@ -830,5 +891,5 @@ function serviceDefaultUrl(serviceId) {
|
||||
"hwlab-edge-proxy": 6667,
|
||||
"hwlab-agent-skills": 7430
|
||||
}[serviceId] ?? 8080;
|
||||
return `http://${serviceId}.hwlab-dev.svc.cluster.local:${port}`;
|
||||
return `http://${serviceId}.${namespace}.svc.cluster.local:${port}`;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user