feat: render workbench redis cache workload
This commit is contained in:
+116
-3
@@ -1010,7 +1010,7 @@ function runtimeStoreEnvForProfile(deploy, profile, serviceId) {
|
||||
|
||||
function workbenchRuntimeRedisEnvForProfile(deploy, profile, serviceId) {
|
||||
if (!isRuntimeLane(profile) || serviceId !== "hwlab-workbench-runtime") return {};
|
||||
const redis = runtimeLaneConfig(deploy, profile)?.workbenchRuntime?.cache?.redis;
|
||||
const redis = workbenchRuntimeRedisConfigForProfile(deploy, profile);
|
||||
if (!redis || typeof redis !== "object" || Array.isArray(redis)) return {};
|
||||
const env = {
|
||||
HWLAB_WORKBENCH_CACHE_REDIS_ENABLED: booleanEnv(Boolean(redis.enabled))
|
||||
@@ -1049,6 +1049,28 @@ function workbenchRuntimeRedisEnvForProfile(deploy, profile, serviceId) {
|
||||
return env;
|
||||
}
|
||||
|
||||
function workbenchRuntimeRedisConfigForProfile(deploy, profile) {
|
||||
if (!isRuntimeLane(profile)) return null;
|
||||
const label = `deploy.lanes.${profile}.workbenchRuntime.cache.redis`;
|
||||
const redis = runtimeLaneConfig(deploy, profile)?.workbenchRuntime?.cache?.redis;
|
||||
if (!redis || redis.enabled !== true) return null;
|
||||
configObject(redis, label);
|
||||
const namespace = requiredConfigString(redis, "namespace", label);
|
||||
assert.equal(namespace, namespaceNameForProfile(profile), `${label}.namespace must match ${namespaceNameForProfile(profile)}`);
|
||||
const serviceName = requiredConfigString(redis, "serviceName", label);
|
||||
const image = requiredConfigString(redis, "image", label);
|
||||
const port = requiredConfigPositiveInteger(redis, "port", label);
|
||||
assert.ok(port <= 65535, `${label}.port must be a valid TCP port`);
|
||||
const resources = configObject(redis.resources, `${label}.resources`);
|
||||
configObject(resources.requests, `${label}.resources.requests`);
|
||||
configObject(resources.limits, `${label}.resources.limits`);
|
||||
const memoryPolicy = configObject(redis.memoryPolicy, `${label}.memoryPolicy`);
|
||||
assert.equal(memoryPolicy.persistence, "disabled", `${label}.memoryPolicy.persistence must be disabled`);
|
||||
requiredConfigString(memoryPolicy, "maxMemory", `${label}.memoryPolicy`);
|
||||
requiredConfigString(memoryPolicy, "eviction", `${label}.memoryPolicy`);
|
||||
return redis;
|
||||
}
|
||||
|
||||
function booleanEnv(value) {
|
||||
return value ? "1" : "0";
|
||||
}
|
||||
@@ -5149,12 +5171,101 @@ function v02OpenFgaManifest({ profile = "v02", source }) {
|
||||
};
|
||||
}
|
||||
|
||||
function runtimeKustomization({ profile = "dev", includeDeviceAgent = profile === "dev", externalPostgres = false } = {}) {
|
||||
function workbenchRuntimeRedisConf(config) {
|
||||
return [
|
||||
"save \"\"",
|
||||
"appendonly no",
|
||||
`maxmemory ${config.memoryPolicy.maxMemory}`,
|
||||
`maxmemory-policy ${config.memoryPolicy.eviction}`,
|
||||
""
|
||||
].join("\n");
|
||||
}
|
||||
|
||||
function workbenchRuntimeRedisManifest({ profile = "v03", config, source }) {
|
||||
const namespace = config.namespace;
|
||||
const name = config.serviceName;
|
||||
const port = config.port;
|
||||
const selector = { "app.kubernetes.io/name": name };
|
||||
const labels = {
|
||||
...selector,
|
||||
"app.kubernetes.io/part-of": "hwlab",
|
||||
"hwlab.pikastech.local/cache-role": "workbench-derived-read",
|
||||
"hwlab.pikastech.local/component": "workbench-runtime-cache",
|
||||
"hwlab.pikastech.local/environment": profile,
|
||||
"hwlab.pikastech.local/gitops-target": profile,
|
||||
"hwlab.pikastech.local/profile": profile,
|
||||
"hwlab.pikastech.local/service-id": name,
|
||||
"hwlab.pikastech.local/source-commit": source.full
|
||||
};
|
||||
const annotations = { "hwlab.pikastech.local/rendered-by": "scripts/gitops-render.mjs" };
|
||||
const runtimeAnnotations = { ...annotations, "argocd.argoproj.io/sync-wave": "2" };
|
||||
const templateLabels = { ...labels, ...selector };
|
||||
return {
|
||||
apiVersion: "v1",
|
||||
kind: "List",
|
||||
items: [
|
||||
{
|
||||
apiVersion: "v1",
|
||||
kind: "ConfigMap",
|
||||
metadata: { name: `${name}-config`, namespace, labels, annotations: runtimeAnnotations },
|
||||
data: { "redis.conf": workbenchRuntimeRedisConf(config) }
|
||||
},
|
||||
{
|
||||
apiVersion: "apps/v1",
|
||||
kind: "Deployment",
|
||||
metadata: { name, namespace, labels, annotations: runtimeAnnotations },
|
||||
spec: {
|
||||
replicas: 1,
|
||||
selector: { matchLabels: selector },
|
||||
template: {
|
||||
metadata: { labels: templateLabels, annotations: runtimeAnnotations },
|
||||
spec: {
|
||||
containers: [{
|
||||
name: "redis",
|
||||
image: config.image,
|
||||
imagePullPolicy: "IfNotPresent",
|
||||
args: ["redis-server", "/usr/local/etc/redis/redis.conf"],
|
||||
ports: [{ name: "redis", containerPort: port }],
|
||||
readinessProbe: { exec: { command: ["redis-cli", "-p", String(port), "ping"] }, initialDelaySeconds: 3, periodSeconds: 10, timeoutSeconds: 2, failureThreshold: 3 },
|
||||
livenessProbe: { exec: { command: ["redis-cli", "-p", String(port), "ping"] }, initialDelaySeconds: 15, periodSeconds: 20, timeoutSeconds: 3, failureThreshold: 3 },
|
||||
resources: cloneJson(config.resources),
|
||||
volumeMounts: [{ name: "config", mountPath: "/usr/local/etc/redis", readOnly: true }]
|
||||
}],
|
||||
volumes: [{ name: "config", configMap: { name: `${name}-config` } }]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
apiVersion: "v1",
|
||||
kind: "Service",
|
||||
metadata: { name, namespace, labels, annotations: runtimeAnnotations },
|
||||
spec: { type: "ClusterIP", selector, ports: [{ name: "redis", port, targetPort: "redis", protocol: "TCP" }] }
|
||||
},
|
||||
{
|
||||
apiVersion: "networking.k8s.io/v1",
|
||||
kind: "NetworkPolicy",
|
||||
metadata: { name: `${name}-ingress`, namespace, labels, annotations: runtimeAnnotations },
|
||||
spec: {
|
||||
podSelector: { matchLabels: selector },
|
||||
policyTypes: ["Ingress"],
|
||||
ingress: [{
|
||||
from: [{ podSelector: { matchLabels: { "app.kubernetes.io/name": "hwlab-workbench-runtime" } } }],
|
||||
ports: [{ protocol: "TCP", port }]
|
||||
}]
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
|
||||
function runtimeKustomization({ profile = "dev", includeDeviceAgent = profile === "dev", externalPostgres = false, workbenchRedis = false } = {}) {
|
||||
const resources = ["namespace.yaml", "services.yaml", "health-contract.yaml", "workloads.yaml", "deepseek-proxy.yaml"];
|
||||
if (!isRuntimeLane(profile)) resources.splice(1, 0, "code-agent-codex-config.yaml");
|
||||
if (isRuntimeLane(profile)) {
|
||||
if (externalPostgres) resources.push("external-postgres.yaml");
|
||||
else resources.push("postgres.yaml");
|
||||
if (workbenchRedis) resources.push("workbench-redis.yaml");
|
||||
resources.push("openfga.yaml", "observability.yaml");
|
||||
}
|
||||
if (includeDeviceAgent) resources.push("device-agent-71-freq.yaml");
|
||||
@@ -5289,6 +5400,7 @@ async function plannedFiles(args) {
|
||||
const namespace = namespaceNameForProfile(profile);
|
||||
const includeDeviceAgent = profile === "dev";
|
||||
const externalPostgres = externalPostgresConfigForLane(deploy, profile);
|
||||
const workbenchRedis = workbenchRuntimeRedisConfigForProfile(deploy, profile);
|
||||
const profileLabels = { ...baseLabels, "hwlab.pikastech.local/environment": runtimeLabelForProfile(profile), "hwlab.pikastech.local/profile": runtimeLabelForProfile(profile) };
|
||||
const runtimeNamespace = cloneJson(namespaceTemplate);
|
||||
runtimeNamespace.metadata.name = namespace;
|
||||
@@ -5296,7 +5408,7 @@ async function plannedFiles(args) {
|
||||
annotate(runtimeNamespace.metadata, annotations);
|
||||
const runtimePath = runtimePathForProfile(profile);
|
||||
const endpoints = profileEndpoint(args, profile);
|
||||
putJson(`${runtimePath}/kustomization.yaml`, runtimeKustomization({ profile, includeDeviceAgent, externalPostgres: Boolean(externalPostgres) }));
|
||||
putJson(`${runtimePath}/kustomization.yaml`, runtimeKustomization({ profile, includeDeviceAgent, externalPostgres: Boolean(externalPostgres), workbenchRedis: Boolean(workbenchRedis) }));
|
||||
putJson(`${runtimePath}/namespace.yaml`, runtimeNamespace);
|
||||
if (!isRuntimeLane(profile)) putJson(`${runtimePath}/code-agent-codex-config.yaml`, transformListNamespace(config, namespace, profileLabels, annotations));
|
||||
putJson(`${runtimePath}/services.yaml`, transformServices({ services, namespace, labels: profileLabels, annotations, profile, deploy }));
|
||||
@@ -5305,6 +5417,7 @@ async function plannedFiles(args) {
|
||||
putJson(`${runtimePath}/deepseek-proxy.yaml`, deepSeekProxyManifest({ profile, source, sourceBranch: args.sourceBranch, sourceRepo: args.sourceRepo, gitReadUrl: args.gitReadUrl, deploy, registryPrefix: args.registryPrefix, catalog: artifactCatalog, useDeployImages: args.useDeployImages, metricsSidecarSha256 }));
|
||||
if (isRuntimeLane(profile) && externalPostgres) putJson(`${runtimePath}/external-postgres.yaml`, externalPostgresManifest({ profile, config: externalPostgres, source }));
|
||||
if (isRuntimeLane(profile) && !externalPostgres) putJson(`${runtimePath}/postgres.yaml`, v02PostgresManifest({ profile, migrationSql, source }));
|
||||
if (workbenchRedis) putJson(`${runtimePath}/workbench-redis.yaml`, workbenchRuntimeRedisManifest({ profile, config: workbenchRedis, source }));
|
||||
if (isRuntimeLane(profile)) putJson(`${runtimePath}/openfga.yaml`, v02OpenFgaManifest({ profile, source }));
|
||||
if (isRuntimeLane(profile)) putJson(`${runtimePath}/observability.yaml`, observabilityManifest({ deploy, profile, namespace, labels: profileLabels, annotations, metricsSidecarScript }));
|
||||
if (includeDeviceAgent) putJson(`${runtimePath}/device-agent-71-freq.yaml`, deviceAgent71FreqManifest({ profile, source, registryPrefix: args.registryPrefix, catalog: artifactCatalog, useDeployImages: args.useDeployImages }));
|
||||
|
||||
Reference in New Issue
Block a user