ymal: compose repeated node lane templates

This commit is contained in:
root
2026-07-08 06:34:23 +02:00
parent 0cea74367a
commit d083e88bb9
13 changed files with 1232 additions and 1585 deletions
+131 -253
View File
@@ -2,11 +2,9 @@ version: 1
kind: AgentRunConfig
metadata:
name: agentrun
manager:
baseUrl: https://agentrun.74-48-78-17.nip.io/
timeoutMs: 15000
publicExposure:
enabled: true
proxyName: agentrun-v01-frpc
@@ -23,13 +21,11 @@ publicExposure:
serviceName: caddy
upstreamBaseUrl: http://127.0.0.1:22880
responseHeaderTimeoutSeconds: 60
auth:
env: HWLAB_API_KEY
file: /root/.config/hwlab-v02/master-server-admin-api-key.env
header: Authorization
scheme: Bearer
client:
role: render-only
transport: direct-http
@@ -40,7 +36,7 @@ client:
backendProfile: codex
workspaceRef:
kind: opaque
path: .
path: "."
executionPolicy:
sandbox: workspace-write
approval: never
@@ -48,12 +44,10 @@ client:
network: enabled
secretScope:
allowCredentialEcho: false
controlPlane:
default:
node: G14
lane: v01
nodes:
G14:
route: G14
@@ -70,7 +64,6 @@ controlPlane:
NC01:
route: NC01
kubeRoute: NC01:k3s
lanes:
v01:
node: G14
@@ -120,7 +113,7 @@ controlPlane:
value: 74.48.78.17
bootRepoUrl: http://git-mirror-http.devops-infra.svc.cluster.local/pikasTech/agentrun.git
imageBuild:
context: .
context: "."
containerfile: deploy/container/Containerfile
repository: agentrun-mgr-env
network: host
@@ -131,7 +124,7 @@ controlPlane:
noProxy:
- localhost
- 127.0.0.1
- ::1
- "::1"
- 127.0.0.1:5000
- localhost:5000
- .svc
@@ -145,7 +138,7 @@ controlPlane:
noProxy:
- localhost
- 127.0.0.1
- ::1
- "::1"
- 127.0.0.1:5000
- localhost:5000
- .svc
@@ -275,49 +268,7 @@ controlPlane:
key: config.toml
providerCredential:
profile: codex
v02:
node: D601
version: v0.2
source:
statusMode: k3s-git-mirror
repository: pikasTech/agentrun
branch: v0.2
sourceAuthority:
mode: gitMirrorSnapshot
resolver: k8s-git-mirror
allowHostGit: false
allowHostWorkspace: false
allowGithubDirectInPipeline: false
sourceSnapshot:
stageRefPrefix: refs/unidesk/snapshots/agentrun-yaml-lane/{branch}
missingObjectPolicy: fail-fast
refreshPolicy: sync-before-snapshot
bootstrapFromBranch: v0.1
bootstrapTimeoutSeconds: 900
bootstrapPollSeconds: 15
remote: git@github.com:pikasTech/agentrun.git
workspace: /home/ubuntu/workspace/agentrun-v02
runtime:
namespace: agentrun-v02
managerDeployment: agentrun-mgr
managerService: agentrun-mgr
managerPort: 8080
internalBaseUrl: http://agentrun-mgr.agentrun-v02.svc.cluster.local:8080
ci:
namespace: agentrun-ci
pipeline: agentrun-v02-ci-image-publish
pipelineRunPrefix: agentrun-v02-ci
serviceAccountName: agentrun-v02-tekton-runner
registryPrefix: 127.0.0.1:5000/agentrun
toolsImage: 127.0.0.1:5000/hwlab/hwlab-ci-node-tools:node22-alpine-bun-v1
buildkitImage: 127.0.0.1:5000/hwlab/buildkit:rootless
gitops:
branch: v0.2-gitops
path: deploy/gitops/node/d601/runtime-v02
argoNamespace: argocd
argoApplication: agentrun-d601-v02
repoURL: http://git-mirror-http.devops-infra.svc.cluster.local:8080/pikasTech/agentrun.git
deployment:
format: unidesk-yaml-only
gitopsRoot: deploy/gitops/node/d601
@@ -345,7 +296,7 @@ controlPlane:
value: 74.48.78.17
bootRepoUrl: http://git-mirror-http.devops-infra.svc.cluster.local:8080/pikasTech/agentrun.git
imageBuild:
context: .
context: "."
containerfile: deploy/container/Containerfile
repository: agentrun-mgr-env
network: host
@@ -356,7 +307,7 @@ controlPlane:
noProxy:
- localhost
- 127.0.0.1
- ::1
- "::1"
- 127.0.0.1:5000
- localhost:5000
- .svc
@@ -370,7 +321,7 @@ controlPlane:
noProxy:
- localhost
- 127.0.0.1
- ::1
- "::1"
- 127.0.0.1:5000
- localhost:5000
- .svc
@@ -564,64 +515,20 @@ controlPlane:
key: model-catalog.json
providerCredential:
profile: dsflash-go
- id: tool-github-pr-token
sourceRef: /root/unidesk/.env/gh_token.txt
sourceKey: GH_TOKEN
sourceFormat: raw-token
targetRef:
namespace: agentrun-v02
name: agentrun-v01-tool-github-pr
key: GH_TOKEN
- id: tool-unidesk-ssh-token
sourceRef: /root/unidesk/.state/docker-compose.env
sourceKey: UNIDESK_SSH_CLIENT_TOKEN
targetRef:
namespace: agentrun-v02
name: agentrun-v01-tool-unidesk-ssh
key: UNIDESK_SSH_CLIENT_TOKEN
jd01-v02:
node: JD01
version: v0.2
source:
statusMode: k3s-git-mirror
repository: pikasTech/agentrun
branch: v0.2
sourceAuthority:
mode: gitMirrorSnapshot
resolver: k8s-git-mirror
allowHostGit: false
allowHostWorkspace: false
allowGithubDirectInPipeline: false
sourceSnapshot:
stageRefPrefix: refs/unidesk/snapshots/agentrun-yaml-lane/{branch}
missingObjectPolicy: fail-fast
refreshPolicy: sync-before-snapshot
bootstrapFromBranch: v0.1
bootstrapTimeoutSeconds: 900
bootstrapPollSeconds: 15
- extends: controlPlane.templates.secrets.githubPrRawToken
- extends: controlPlane.templates.secrets.unideskSshToken
extends: controlPlane.templates.agentrunV02Lane
variables:
NODE: D601
remote: git@github.com:pikasTech/agentrun.git
workspace: /root/workspace/agentrun-v02
runtime:
namespace: agentrun-v02
managerDeployment: agentrun-mgr
managerService: agentrun-mgr
managerPort: 8080
internalBaseUrl: http://agentrun-mgr.agentrun-v02.svc.cluster.local:8080
ci:
namespace: agentrun-ci
pipeline: agentrun-jd01-v02-ci-image-publish
pipelineRunPrefix: agentrun-jd01-v02-ci
serviceAccountName: agentrun-jd01-v02-tekton-runner
registryPrefix: 127.0.0.1:5000/agentrun
toolsImage: 127.0.0.1:5000/hwlab/hwlab-ci-node-tools:node22-alpine-bun-v1
buildkitImage: 127.0.0.1:5000/hwlab/buildkit:rootless
gitops:
branch: jd01-v0.2-gitops
path: deploy/gitops/node/jd01/runtime-v02
argoNamespace: argocd
argoApplication: agentrun-jd01-v02
repoURL: http://git-mirror-http.devops-infra.svc.cluster.local:8080/pikasTech/agentrun.git
workspace: /home/ubuntu/workspace/agentrun-v02
pipeline: agentrun-v02-ci-image-publish
pipelineRunPrefix: agentrun-v02-ci
serviceAccountName: agentrun-v02-tekton-runner
gitopsBranch: v0.2-gitops
gitopsPath: deploy/gitops/node/d601/runtime-v02
argoApplication: agentrun-d601-v02
jd01-v02:
deployment:
format: unidesk-yaml-only
gitopsRoot: deploy/gitops/node/jd01
@@ -649,7 +556,7 @@ controlPlane:
value: 74.48.78.17
bootRepoUrl: http://gitea-http.devops-infra.svc.cluster.local:3000/mirrors/pikasTech-agentrun.git
imageBuild:
context: .
context: "."
containerfile: deploy/container/Containerfile
repository: agentrun-mgr-env
network: host
@@ -660,7 +567,7 @@ controlPlane:
noProxy:
- localhost
- 127.0.0.1
- ::1
- "::1"
- 127.0.0.1:5000
- localhost:5000
- .svc
@@ -674,7 +581,7 @@ controlPlane:
noProxy:
- localhost
- 127.0.0.1
- ::1
- "::1"
- 127.0.0.1:5000
- localhost:5000
- .svc
@@ -716,7 +623,7 @@ controlPlane:
noProxyExtra:
- localhost
- 127.0.0.1
- ::1
- "::1"
- .svc
- .svc.cluster.local
- .cluster.local
@@ -864,64 +771,20 @@ controlPlane:
key: model-catalog.json
providerCredential:
profile: dsflash-go
- id: tool-github-pr-token
sourceRef: /root/unidesk/.env/gh_token.txt
sourceKey: GH_TOKEN
sourceFormat: raw-token
targetRef:
namespace: agentrun-v02
name: agentrun-v01-tool-github-pr
key: GH_TOKEN
- id: tool-unidesk-ssh-token
sourceRef: /root/unidesk/.state/docker-compose.env
sourceKey: UNIDESK_SSH_CLIENT_TOKEN
targetRef:
namespace: agentrun-v02
name: agentrun-v01-tool-unidesk-ssh
key: UNIDESK_SSH_CLIENT_TOKEN
- extends: controlPlane.templates.secrets.githubPrRawToken
- extends: controlPlane.templates.secrets.unideskSshToken
extends: controlPlane.templates.agentrunV02Lane
variables:
NODE: JD01
remote: git@github.com:pikasTech/agentrun.git
workspace: /root/workspace/agentrun-v02
pipeline: agentrun-jd01-v02-ci-image-publish
pipelineRunPrefix: agentrun-jd01-v02-ci
serviceAccountName: agentrun-jd01-v02-tekton-runner
gitopsBranch: jd01-v0.2-gitops
gitopsPath: deploy/gitops/node/jd01/runtime-v02
argoApplication: agentrun-jd01-v02
nc01-v02:
node: NC01
version: v0.2
source:
statusMode: k3s-git-mirror
repository: pikasTech/agentrun
branch: v0.2
sourceAuthority:
mode: gitMirrorSnapshot
resolver: k8s-git-mirror
allowHostGit: false
allowHostWorkspace: false
allowGithubDirectInPipeline: false
sourceSnapshot:
stageRefPrefix: refs/unidesk/snapshots/agentrun-yaml-lane/{branch}
missingObjectPolicy: fail-fast
refreshPolicy: sync-before-snapshot
bootstrapFromBranch: v0.1
bootstrapTimeoutSeconds: 900
bootstrapPollSeconds: 15
remote: http://gitea-http.devops-infra.svc.cluster.local:3000/mirrors/pikasTech-agentrun.git
workspace: /root/agentrun
runtime:
namespace: agentrun-v02
managerDeployment: agentrun-mgr
managerService: agentrun-mgr
managerPort: 8080
internalBaseUrl: http://agentrun-mgr.agentrun-v02.svc.cluster.local:8080
ci:
namespace: agentrun-ci
pipeline: agentrun-nc01-v02-ci-image-publish
pipelineRunPrefix: agentrun-nc01-v02-ci
serviceAccountName: agentrun-nc01-v02-tekton-runner
registryPrefix: 127.0.0.1:5000/agentrun
toolsImage: 127.0.0.1:5000/hwlab/hwlab-ci-node-tools:node22-alpine-bun-v1
buildkitImage: 127.0.0.1:5000/hwlab/buildkit:rootless
gitops:
branch: nc01-v0.2-gitops
path: deploy/gitops/node/nc01/runtime-v02
argoNamespace: argocd
argoApplication: agentrun-nc01-v02
repoURL: http://git-mirror-http.devops-infra.svc.cluster.local:8080/pikasTech/agentrun.git
deployment:
format: unidesk-yaml-only
gitopsRoot: deploy/gitops/node/nc01
@@ -949,7 +812,7 @@ controlPlane:
value: 152.53.229.148
bootRepoUrl: http://gitea-http.devops-infra.svc.cluster.local:3000/mirrors/pikasTech-agentrun.git
imageBuild:
context: .
context: "."
containerfile: deploy/container/Containerfile
repository: agentrun-mgr-env
network: host
@@ -960,7 +823,7 @@ controlPlane:
noProxy:
- localhost
- 127.0.0.1
- ::1
- "::1"
- 127.0.0.1:5000
- localhost:5000
- .svc
@@ -974,7 +837,7 @@ controlPlane:
noProxy:
- localhost
- 127.0.0.1
- ::1
- "::1"
- 127.0.0.1:5000
- localhost:5000
- .svc
@@ -1016,7 +879,7 @@ controlPlane:
noProxyExtra:
- localhost
- 127.0.0.1
- ::1
- "::1"
- .svc
- .svc.cluster.local
- .cluster.local
@@ -1137,64 +1000,20 @@ controlPlane:
key: config.toml
providerCredential:
profile: codex
- id: tool-github-pr-token
sourceRef: /root/unidesk/.env/gh_token.txt
sourceKey: GH_TOKEN
sourceFormat: raw-token
targetRef:
namespace: agentrun-v02
name: agentrun-v01-tool-github-pr
key: GH_TOKEN
- id: tool-unidesk-ssh-token
sourceRef: /root/unidesk/.state/docker-compose.env
sourceKey: UNIDESK_SSH_CLIENT_TOKEN
targetRef:
namespace: agentrun-v02
name: agentrun-v01-tool-unidesk-ssh
key: UNIDESK_SSH_CLIENT_TOKEN
- extends: controlPlane.templates.secrets.githubPrRawToken
- extends: controlPlane.templates.secrets.unideskSshToken
extends: controlPlane.templates.agentrunV02Lane
variables:
NODE: NC01
remote: http://gitea-http.devops-infra.svc.cluster.local:3000/mirrors/pikasTech-agentrun.git
workspace: /root/agentrun
pipeline: agentrun-nc01-v02-ci-image-publish
pipelineRunPrefix: agentrun-nc01-v02-ci
serviceAccountName: agentrun-nc01-v02-tekton-runner
gitopsBranch: nc01-v0.2-gitops
gitopsPath: deploy/gitops/node/nc01/runtime-v02
argoApplication: agentrun-nc01-v02
d518-v02:
node: D518
version: v0.2
source:
statusMode: k3s-git-mirror
repository: pikasTech/agentrun
branch: v0.2
sourceAuthority:
mode: gitMirrorSnapshot
resolver: k8s-git-mirror
allowHostGit: false
allowHostWorkspace: false
allowGithubDirectInPipeline: false
sourceSnapshot:
stageRefPrefix: refs/unidesk/snapshots/agentrun-yaml-lane/{branch}
missingObjectPolicy: fail-fast
refreshPolicy: sync-before-snapshot
bootstrapFromBranch: v0.1
bootstrapTimeoutSeconds: 900
bootstrapPollSeconds: 15
remote: git@github.com:pikasTech/agentrun.git
workspace: /home/ubuntu/workspace/agentrun-v02
runtime:
namespace: agentrun-v02
managerDeployment: agentrun-mgr
managerService: agentrun-mgr
managerPort: 8080
internalBaseUrl: http://agentrun-mgr.agentrun-v02.svc.cluster.local:8080
ci:
namespace: agentrun-ci
pipeline: agentrun-d518-v02-ci-image-publish
pipelineRunPrefix: agentrun-d518-v02-ci
serviceAccountName: agentrun-d518-v02-tekton-runner
registryPrefix: 127.0.0.1:5000/agentrun
toolsImage: 127.0.0.1:5000/hwlab/hwlab-ci-node-tools:node22-alpine-bun-v1
buildkitImage: 127.0.0.1:5000/hwlab/buildkit:rootless
gitops:
branch: d518-v0.2-gitops
path: deploy/gitops/node/d518/runtime-v02
argoNamespace: argocd
argoApplication: agentrun-d518-v02
repoURL: http://git-mirror-http.devops-infra.svc.cluster.local:8080/pikasTech/agentrun.git
deployment:
format: unidesk-yaml-only
gitopsRoot: deploy/gitops/node/d518
@@ -1227,7 +1046,7 @@ controlPlane:
value: 74.48.78.17
bootRepoUrl: http://git-mirror-http.devops-infra.svc.cluster.local:8080/pikasTech/agentrun.git
imageBuild:
context: .
context: "."
containerfile: deploy/container/Containerfile
repository: agentrun-mgr-env
network: host
@@ -1238,7 +1057,7 @@ controlPlane:
noProxy:
- localhost
- 127.0.0.1
- ::1
- "::1"
- 127.0.0.1:5000
- localhost:5000
- .svc
@@ -1252,7 +1071,7 @@ controlPlane:
noProxy:
- localhost
- 127.0.0.1
- ::1
- "::1"
- 127.0.0.1:5000
- localhost:5000
- .svc
@@ -1465,18 +1284,77 @@ controlPlane:
key: config.toml
providerCredential:
profile: fake-echo
- id: tool-github-pr-token
sourceRef: /root/unidesk/.env/gh_token.txt
sourceKey: GH_TOKEN
sourceFormat: raw-token
targetRef:
namespace: agentrun-v02
name: agentrun-v01-tool-github-pr
key: GH_TOKEN
- id: tool-unidesk-ssh-token
sourceRef: /root/unidesk/.state/docker-compose.env
sourceKey: UNIDESK_SSH_CLIENT_TOKEN
targetRef:
namespace: agentrun-v02
name: agentrun-v01-tool-unidesk-ssh
key: UNIDESK_SSH_CLIENT_TOKEN
- extends: controlPlane.templates.secrets.githubPrRawToken
- extends: controlPlane.templates.secrets.unideskSshToken
extends: controlPlane.templates.agentrunV02Lane
variables:
NODE: D518
remote: git@github.com:pikasTech/agentrun.git
workspace: /home/ubuntu/workspace/agentrun-v02
pipeline: agentrun-d518-v02-ci-image-publish
pipelineRunPrefix: agentrun-d518-v02-ci
serviceAccountName: agentrun-d518-v02-tekton-runner
gitopsBranch: d518-v0.2-gitops
gitopsPath: deploy/gitops/node/d518/runtime-v02
argoApplication: agentrun-d518-v02
templates:
agentrunV02Lane:
version: v0.2
node: "${NODE}"
source:
statusMode: k3s-git-mirror
repository: pikasTech/agentrun
branch: v0.2
sourceAuthority:
mode: gitMirrorSnapshot
resolver: k8s-git-mirror
allowHostGit: false
allowHostWorkspace: false
allowGithubDirectInPipeline: false
sourceSnapshot:
stageRefPrefix: "refs/unidesk/snapshots/agentrun-yaml-lane/{branch}"
missingObjectPolicy: fail-fast
refreshPolicy: sync-before-snapshot
bootstrapFromBranch: v0.1
bootstrapTimeoutSeconds: 900
bootstrapPollSeconds: 15
remote: "${remote}"
workspace: "${workspace}"
runtime:
namespace: agentrun-v02
managerDeployment: agentrun-mgr
managerService: agentrun-mgr
managerPort: 8080
internalBaseUrl: http://agentrun-mgr.agentrun-v02.svc.cluster.local:8080
ci:
namespace: agentrun-ci
pipeline: "${pipeline}"
pipelineRunPrefix: "${pipelineRunPrefix}"
serviceAccountName: "${serviceAccountName}"
registryPrefix: 127.0.0.1:5000/agentrun
toolsImage: 127.0.0.1:5000/hwlab/hwlab-ci-node-tools:node22-alpine-bun-v1
buildkitImage: 127.0.0.1:5000/hwlab/buildkit:rootless
gitops:
branch: "${gitopsBranch}"
path: "${gitopsPath}"
argoNamespace: argocd
argoApplication: "${argoApplication}"
repoURL: http://git-mirror-http.devops-infra.svc.cluster.local:8080/pikasTech/agentrun.git
secrets:
githubPrRawToken:
id: tool-github-pr-token
sourceRef: /root/unidesk/.env/gh_token.txt
sourceKey: GH_TOKEN
sourceFormat: raw-token
targetRef:
namespace: agentrun-v02
name: agentrun-v01-tool-github-pr
key: GH_TOKEN
unideskSshToken:
id: tool-unidesk-ssh-token
sourceRef: /root/unidesk/.state/docker-compose.env
sourceKey: UNIDESK_SSH_CLIENT_TOKEN
targetRef:
namespace: agentrun-v02
name: agentrun-v01-tool-unidesk-ssh
key: UNIDESK_SSH_CLIENT_TOKEN
File diff suppressed because it is too large Load Diff
+293 -261
View File
@@ -7,22 +7,20 @@ metadata:
composition:
mode: yaml-anchors-and-merge
intent: node overlays inherit common web-probe sentinel baselines and render node/lane identity from variables.
baselines:
sentinel: &sentinel-base
sentinel:
enabled: true
mode: web-probe-observe-wrapper
runtime:
common: &runtime-common
namespace: hwlab-${lane}
common:
namespace: "hwlab-${lane}"
listenHost: 0.0.0.0
servicePort: 8080
pvcStorage: 10Gi
replicas: 1
healthPath: /api/health
metricsPath: /metrics
scheduler: &scheduler-10m
scheduler:
intervalMs: 600000
heartbeatStaleSeconds: 900
maxConcurrentRuns: 1
@@ -30,11 +28,12 @@ baselines:
observability:
otel:
enabled: true
serviceName: hwlab-web-probe-sentinel-${nodeLower}
serviceName: "hwlab-web-probe-sentinel-${nodeLower}"
tracesEndpoint: http://otel-collector.platform-infra.svc.cluster.local:4318/v1/traces
sampler: parentbased_traceidratio
samplerArg: "1"
kubernetesApi:
&kubernetesApi
endpoint:
host: 172.16.0.5
port: 6443
@@ -45,20 +44,19 @@ baselines:
port: 443
- cidr: 172.16.0.5/32
port: 6443
scheduler15m: &scheduler-15m
scheduler15m:
intervalMs: 900000
heartbeatStaleSeconds: 900
maxConcurrentRuns: 1
sqlite: &sqlite-common
sqlite:
busyTimeoutMs: 2000
cicd:
source: &cicd-source
source:
repository: pikasTech/unidesk
branch: master
gitSshUrl: ssh://git@ssh.github.com:443/pikasTech/unidesk.git
gitMirrorReadUrl: http://git-mirror-http.devops-infra.svc.cluster.local:8080/pikasTech/unidesk.git
buildContext: .
buildContext: "."
entrypoint: scripts/web-probe-sentinel-service.ts
checkoutPaths:
- scripts
@@ -68,23 +66,23 @@ baselines:
- package.json
- bun.lock
- bun.lockb
sourceAuthority: &cicd-source-authority
sourceAuthority:
mode: gitMirrorSnapshot
resolver: k8s-git-mirror
allowHostGit: false
allowGithubDirectInPipeline: false
sourceSnapshot: &cicd-source-snapshot
stageRefPrefix: refs/unidesk/snapshots/web-probe-sentinel/{branch}
sourceSnapshot:
stageRefPrefix: "refs/unidesk/snapshots/web-probe-sentinel/{branch}"
missingObjectPolicy: fail-fast
refreshPolicy: sync-before-snapshot
cacheRef: source.gitMirrorReadUrl
builder: &cicd-builder
builder:
namespace: devops-infra
sourceMode: sparse-git-checkout
gitSshSecretName: git-mirror-github-ssh
activeDeadlineSeconds: 900
ttlSecondsAfterFinished: 3600
monitorWeb: &monitor-web
monitorWeb:
frontendStack: vue3-vendored-browser-build
runtimeMode: runner-served-bridge
assetRoot: scripts/assets/web-probe-sentinel-monitor-web
@@ -99,7 +97,7 @@ baselines:
verifyPhase: pre-image-build
buildkitState:
mode: hostPath
path: /var/lib/unidesk/web-probe-sentinel/buildkit-${nodeLower}
path: "/var/lib/unidesk/web-probe-sentinel/buildkit-${nodeLower}"
type: DirectoryOrCreate
gitMirror:
source: source.gitMirrorReadUrl
@@ -107,12 +105,12 @@ baselines:
postFlush: required
ciBudget:
maxSeconds: 120
maintenance: &maintenance
maintenance:
startCommand: sentinel maintenance start
stopCommand: sentinel maintenance stop
confirmWait: &confirm-wait
confirmWait:
maxSeconds: 120
publishCurrent: &publish-current
publishCurrent:
endToEndBudget:
maxSeconds: 120
stageBudgets:
@@ -131,14 +129,13 @@ baselines:
waitTimeoutMs: 60000
commandTimeoutSeconds: 90
fullPage: false
publicExposure:
common: &public-exposure-common
common:
enabled: true
mode: pk01-caddy-frp-path
hostname: monitor.pikapython.com
expectedA: 82.156.23.220
frpc: &frpc-common
frpc:
image: 127.0.0.1:5000/hwlab/frpc:v0.68.1
serverAddr: 82.156.23.220
serverPort: 22000
@@ -146,144 +143,57 @@ baselines:
tokenSourceKey: FRP_TOKEN
secretKey: frpc.toml
tokenKey: token
caddy: &caddy-common
caddy:
route: PK01
configPath: /etc/caddy/Caddyfile
serviceName: caddy
email: ops@pikapython.com
tls: auto
responseHeaderTimeoutSeconds: 600
secrets:
jd01BootstrapSource: &jd01-bootstrap-source
jd01BootstrapSource:
purpose: bootstrap-admin
sourceRef: .env/HWLAB_admin.txt
sourceKey: HWLAB_ADMIN_PASSWORD
sourceLine: 2
dsflashPromptSource: &dsflash-prompt-source
dsflashPromptSource:
purpose: prompt-set
sourceRef: hwlab/web-probe-sentinel-dsflash-go.env
sourceKey: DSFLASH_GO_TOOL_CALL_10X_PROMPTS_JSON
frpTokenSource: &frp-token-source
frpTokenSource:
purpose: frp-token
sourceRef: platform-infra/pk01-frp.env
sourceKey: FRP_TOKEN
nodes:
JD01:
target: &jd01-target
node: ${NODE}
lane: ${LANE}
publicOriginRef: config/hwlab-node-lanes.yaml#lanes.${LANE}.targets.${NODE}.public.webUrl
cicdCommon: &jd01-cicd-common
controlPlaneConfigRef: config/hwlab-node-control-plane.yaml#targets[1]
source:
<<: *cicd-source
gitMirrorReadUrl: http://gitea-http.devops-infra.svc.cluster.local:3000/mirrors/pikasTech-unidesk.git
sourceAuthority:
<<: *cicd-source-authority
mode: giteaSnapshot
resolver: gitea-mirror
sourceSnapshot:
<<: *cicd-source-snapshot
stageRefPrefix: refs/unidesk/snapshots/gitea-actions/unidesk-master
refreshPolicy: gitea-controlled-snapshot
argo: &jd01-argo
namespace: argocd
projectName: hwlab-jd01
repoURL: http://git-mirror-http.devops-infra.svc.cluster.local:8080/pikasTech/HWLAB.git
targetRevision: v0.3-gitops
maintenance:
<<: *maintenance
monitorWeb:
<<: *monitor-web
gitMirror:
source: source.gitMirrorReadUrl
preSync: not-required
postFlush: not-required
confirmWait:
<<: *confirm-wait
publishCurrent:
<<: *publish-current
target:
extends: templates.webProbeSentinelTarget
cicdCommon:
extends: templates.webProbeSentinelCicdCommon
sentinels:
jd01-web-probe-sentinel:
sentinel:
<<: *sentinel-base
id: jd01-web-probe-sentinel
configRefs:
runtime: config/hwlab-web-probe-sentinel/profiles.yaml#nodes.${NODE}.sentinels.${nodeLower}-web-probe-sentinel.runtime
scenarios: config/hwlab-web-probe-sentinel/scenarios.multi-sentinel.yaml#sentinel.scenarios
promptSet: config/hwlab-web-probe-sentinel/prompt-set.dsflash-go.yaml#sentinel.promptSet
reportViews: config/hwlab-web-probe-sentinel/report-views.multi-sentinel.yaml#sentinel.reportViews
publicExposure: config/hwlab-web-probe-sentinel/profiles.yaml#nodes.${NODE}.sentinels.${nodeLower}-web-probe-sentinel.publicExposure
cicd: config/hwlab-web-probe-sentinel/profiles.yaml#nodes.${NODE}.sentinels.${nodeLower}-web-probe-sentinel.cicd
secrets: config/hwlab-web-probe-sentinel/profiles.yaml#nodes.${NODE}.sentinels.${nodeLower}-web-probe-sentinel.secrets
extends: templates.webProbeSentinelSentinel
runtime:
<<: *runtime-common
target:
<<: *jd01-target
observeWrapperRef: config/hwlab-node-lanes.yaml#lanes.${LANE}.targets.${NODE}.observability.webProbe.sentinels[0]
serviceAccountName: hwlab-web-probe-sentinel-${nodeLower}
deploymentName: hwlab-web-probe-sentinel-${nodeLower}
serviceName: hwlab-web-probe-sentinel-${nodeLower}
pvcName: hwlab-web-probe-sentinel-${nodeLower}-state
stateRoot: /var/lib/web-probe-sentinel-${nodeLower}
imageRef: 127.0.0.1:5000/hwlab/web-probe-sentinel-${nodeLower}:source-commit
scheduler:
<<: *scheduler-10m
sqlite:
<<: *sqlite-common
path: /var/lib/web-probe-sentinel-${nodeLower}/index.sqlite
extends: templates.webProbeSentinelRuntime
kubernetesApi:
*kubernetesApi
publicExposure:
<<: *public-exposure-common
publicBaseUrl: https://monitor.pikapython.com/sentinels/${nodeLower}-web-probe-sentinel
routePrefix: /sentinels/${nodeLower}-web-probe-sentinel
extends: templates.webProbeSentinelPublicExposure
frpc:
<<: *frpc-common
deploymentName: hwlab-web-probe-sentinel-${nodeLower}-frpc
secretName: hwlab-web-probe-sentinel-${nodeLower}-frpc
httpProxy:
name: hwlab-${nodeLower}-${lane}-web-probe-sentinel
remotePort: 22098
localIP: hwlab-web-probe-sentinel-${nodeLower}.hwlab-${lane}.svc.cluster.local
localPort: 8080
caddy:
<<: *caddy-common
managedBlockOwner: hwlab-web-probe-sentinel-${nodeLower}-${lane}
cicd:
<<: *jd01-cicd-common
builder:
<<: *cicd-builder
jobPrefix: web-probe-sentinel-${nodeLower}-publish
gitopsPath: deploy/gitops/node/${nodeLower}/web-probe-sentinel
argo:
<<: *jd01-argo
applicationName: hwlab-web-probe-sentinel-${nodeLower}
image:
repository: 127.0.0.1:5000/hwlab/web-probe-sentinel-${nodeLower}
tagSource: source-commit
baseImageRef: config/hwlab-node-control-plane.yaml#targets[1].tekton.toolsImage.output
envRecipeRef: config/hwlab-web-probe-sentinel/profiles.yaml#nodes.${NODE}.sentinels.${nodeLower}-web-probe-sentinel.runtime
targetValidation:
scenarioId: workbench-dsflash-go-hwpod-two-turn-freeze-repro
maxSeconds: 600
serviceUnavailablePolicy: structured-failure
cadenceScheduler:
enabled: true
reason: k8s-native-periodic-quick-verify
concurrencyPolicy: Forbid
startingDeadlineSeconds: 600
successfulJobsHistoryLimit: 3
failedJobsHistoryLimit: 5
activeDeadlineSlackSeconds: 60
ttlSecondsAfterFinished: 86400
backoffLimit: 0
extends: templates.webProbeSentinelCicd
secrets:
sources:
- <<: *jd01-bootstrap-source
- <<: *dsflash-prompt-source
- sourceLine: 2
sourceKey: HWLAB_ADMIN_PASSWORD
sourceRef: .env/HWLAB_admin.txt
purpose: bootstrap-admin
- sourceKey: DSFLASH_GO_TOOL_CALL_10X_PROMPTS_JSON
sourceRef: hwlab/web-probe-sentinel-dsflash-go.env
purpose: prompt-set
- purpose: account-a
sourceRef: .env/HWLAB_admin.txt
sourceKey: HWLAB_BOOTSTRAP_ADMIN_PASSWORD
@@ -292,102 +202,52 @@ nodes:
usernameSourceRef: .env/HWLAB_admin.txt
usernameSourceLine: 1
- purpose: account-b
sourceRef: hwlab/${nodeLower}-${lane}-preset-users.env
sourceKey: ${NODE}_SECOND_USER_PASSWORD
sourceRef: "hwlab/${nodeLower}-${lane}-preset-users.env"
sourceKey: "${NODE}_SECOND_USER_PASSWORD"
format: web-account-json
username: ${nodeLower}-sentinel@hwlab.local
- <<: *frp-token-source
username: "${nodeLower}-sentinel@hwlab.local"
- sourceKey: FRP_TOKEN
sourceRef: platform-infra/pk01-frp.env
purpose: frp-token
runtimeSecrets:
- name: hwlab-web-probe-sentinel-${nodeLower}-bootstrap
namespace: hwlab-${lane}
- name: "hwlab-web-probe-sentinel-${nodeLower}-bootstrap"
namespace: "hwlab-${lane}"
data:
- sourcePurpose: bootstrap-admin
targetKey: bootstrap-admin-password
- name: hwlab-web-probe-sentinel-${nodeLower}-prompt-set
namespace: hwlab-${lane}
- name: "hwlab-web-probe-sentinel-${nodeLower}-prompt-set"
namespace: "hwlab-${lane}"
data:
- sourcePurpose: prompt-set
targetKey: prompts.json
- name: hwlab-web-probe-sentinel-${nodeLower}-accounts
namespace: hwlab-${lane}
- name: "hwlab-web-probe-sentinel-${nodeLower}-accounts"
namespace: "hwlab-${lane}"
data:
- sourcePurpose: account-a
targetKey: account-a.json
- sourcePurpose: account-b
targetKey: account-b.json
- name: hwlab-web-probe-sentinel-${nodeLower}-frpc
namespace: hwlab-${lane}
- name: "hwlab-web-probe-sentinel-${nodeLower}-frpc"
namespace: "hwlab-${lane}"
data:
- sourcePurpose: frp-token
targetKey: token
variables:
LANE: v03
controlPlaneIndex: "1"
argoProjectName: hwlab-jd01
NODE: JD01
NC01:
target: &nc01-target
node: ${NODE}
lane: ${LANE}
publicOriginRef: config/hwlab-node-lanes.yaml#lanes.${LANE}.targets.${NODE}.public.webUrl
cicdCommon: &nc01-cicd-common
controlPlaneConfigRef: config/hwlab-node-control-plane.yaml#targets[2]
source:
<<: *cicd-source
gitSshUrl: http://gitea-http.devops-infra.svc.cluster.local:3000/mirrors/pikasTech-unidesk.git
gitMirrorReadUrl: http://gitea-http.devops-infra.svc.cluster.local:3000/mirrors/pikasTech-unidesk.git
sourceAuthority:
<<: *cicd-source-authority
mode: giteaSnapshot
resolver: gitea-mirror
sourceSnapshot:
<<: *cicd-source-snapshot
stageRefPrefix: refs/unidesk/snapshots/gitea-actions/unidesk-master
refreshPolicy: gitea-controlled-snapshot
argo: &nc01-argo
namespace: argocd
projectName: hwlab-v03
repoURL: http://git-mirror-http.devops-infra.svc.cluster.local:8080/pikasTech/HWLAB.git
targetRevision: v0.3-gitops
maintenance:
<<: *maintenance
monitorWeb:
<<: *monitor-web
gitMirror:
source: source.gitMirrorReadUrl
preSync: not-required
postFlush: not-required
confirmWait:
<<: *confirm-wait
publishCurrent:
<<: *publish-current
target:
extends: templates.webProbeSentinelTarget
cicdCommon:
extends: templates.webProbeSentinelCicdCommon
sentinels:
nc01-web-probe-sentinel:
sentinel:
<<: *sentinel-base
id: nc01-web-probe-sentinel
configRefs:
runtime: config/hwlab-web-probe-sentinel/profiles.yaml#nodes.${NODE}.sentinels.${nodeLower}-web-probe-sentinel.runtime
scenarios: config/hwlab-web-probe-sentinel/scenarios.multi-sentinel.yaml#sentinel.scenarios
promptSet: config/hwlab-web-probe-sentinel/prompt-set.dsflash-go.yaml#sentinel.promptSet
reportViews: config/hwlab-web-probe-sentinel/report-views.multi-sentinel.yaml#sentinel.reportViews
publicExposure: config/hwlab-web-probe-sentinel/profiles.yaml#nodes.${NODE}.sentinels.${nodeLower}-web-probe-sentinel.publicExposure
cicd: config/hwlab-web-probe-sentinel/profiles.yaml#nodes.${NODE}.sentinels.${nodeLower}-web-probe-sentinel.cicd
secrets: config/hwlab-web-probe-sentinel/profiles.yaml#nodes.${NODE}.sentinels.${nodeLower}-web-probe-sentinel.secrets
extends: templates.webProbeSentinelSentinel
runtime:
<<: *runtime-common
target:
<<: *nc01-target
observeWrapperRef: config/hwlab-node-lanes.yaml#lanes.${LANE}.targets.${NODE}.observability.webProbe.sentinels[0]
serviceAccountName: hwlab-web-probe-sentinel-${nodeLower}
deploymentName: hwlab-web-probe-sentinel-${nodeLower}
serviceName: hwlab-web-probe-sentinel-${nodeLower}
pvcName: hwlab-web-probe-sentinel-${nodeLower}-state
stateRoot: /var/lib/web-probe-sentinel-${nodeLower}
imageRef: 127.0.0.1:5000/hwlab/web-probe-sentinel-${nodeLower}:source-commit
scheduler:
<<: *scheduler-10m
sqlite:
<<: *sqlite-common
path: /var/lib/web-probe-sentinel-${nodeLower}/index.sqlite
extends: templates.webProbeSentinelRuntime
kubernetesApi:
endpoint:
host: 10.43.0.1
@@ -398,86 +258,258 @@ nodes:
- cidr: 10.43.0.1/32
port: 443
publicExposure:
<<: *public-exposure-common
publicBaseUrl: https://monitor.pikapython.com/sentinels/${nodeLower}-web-probe-sentinel
routePrefix: /sentinels/${nodeLower}-web-probe-sentinel
extends: templates.webProbeSentinelPublicExposure
frpc:
<<: *frpc-common
deploymentName: hwlab-web-probe-sentinel-${nodeLower}-frpc
secretName: hwlab-web-probe-sentinel-${nodeLower}-frpc
httpProxy:
name: hwlab-${nodeLower}-${lane}-web-probe-sentinel
remotePort: 22084
localIP: hwlab-web-probe-sentinel-${nodeLower}.hwlab-${lane}.svc.cluster.local
localPort: 8080
caddy:
<<: *caddy-common
managedBlockOwner: hwlab-web-probe-sentinel-${nodeLower}-${lane}
cicd:
<<: *nc01-cicd-common
builder:
<<: *cicd-builder
jobPrefix: web-probe-sentinel-${nodeLower}-publish
gitopsPath: deploy/gitops/node/${nodeLower}/web-probe-sentinel
argo:
<<: *nc01-argo
applicationName: hwlab-web-probe-sentinel-${nodeLower}
image:
repository: 127.0.0.1:5000/hwlab/web-probe-sentinel-${nodeLower}
tagSource: source-commit
baseImageRef: config/hwlab-node-control-plane.yaml#targets[2].tekton.toolsImage.output
envRecipeRef: config/hwlab-web-probe-sentinel/profiles.yaml#nodes.${NODE}.sentinels.${nodeLower}-web-probe-sentinel.runtime
targetValidation:
scenarioId: workbench-dsflash-go-hwpod-two-turn-freeze-repro
maxSeconds: 600
serviceUnavailablePolicy: structured-failure
cadenceScheduler:
enabled: true
reason: k8s-native-periodic-quick-verify
concurrencyPolicy: Forbid
startingDeadlineSeconds: 600
successfulJobsHistoryLimit: 3
failedJobsHistoryLimit: 5
activeDeadlineSlackSeconds: 60
ttlSecondsAfterFinished: 86400
backoffLimit: 0
extends: templates.webProbeSentinelCicd
secrets:
sources:
- purpose: bootstrap-admin
sourceRef: hwlab/${nodeLower}-${lane}-bootstrap-admin.env
sourceRef: "hwlab/${nodeLower}-${lane}-bootstrap-admin.env"
sourceKey: HWLAB_BOOTSTRAP_ADMIN_PASSWORD
- <<: *dsflash-prompt-source
- sourceKey: DSFLASH_GO_TOOL_CALL_10X_PROMPTS_JSON
sourceRef: hwlab/web-probe-sentinel-dsflash-go.env
purpose: prompt-set
- purpose: account-a
sourceRef: hwlab/${nodeLower}-${lane}-bootstrap-admin.env
sourceRef: "hwlab/${nodeLower}-${lane}-bootstrap-admin.env"
sourceKey: HWLAB_BOOTSTRAP_ADMIN_PASSWORD
format: web-account-json
username: admin
- purpose: account-b
sourceRef: hwlab/${nodeLower}-${lane}-preset-users.env
sourceKey: ${NODE}_SECOND_USER_PASSWORD
sourceRef: "hwlab/${nodeLower}-${lane}-preset-users.env"
sourceKey: "${NODE}_SECOND_USER_PASSWORD"
format: web-account-json
username: ${nodeLower}-sentinel@hwlab.local
- <<: *frp-token-source
username: "${nodeLower}-sentinel@hwlab.local"
- sourceKey: FRP_TOKEN
sourceRef: platform-infra/pk01-frp.env
purpose: frp-token
runtimeSecrets:
- name: hwlab-web-probe-sentinel-${nodeLower}-bootstrap
namespace: hwlab-${lane}
- name: "hwlab-web-probe-sentinel-${nodeLower}-bootstrap"
namespace: "hwlab-${lane}"
data:
- sourcePurpose: bootstrap-admin
targetKey: bootstrap-admin-password
- name: hwlab-web-probe-sentinel-${nodeLower}-prompt-set
namespace: hwlab-${lane}
- name: "hwlab-web-probe-sentinel-${nodeLower}-prompt-set"
namespace: "hwlab-${lane}"
data:
- sourcePurpose: prompt-set
targetKey: prompts.json
- name: hwlab-web-probe-sentinel-${nodeLower}-accounts
namespace: hwlab-${lane}
- name: "hwlab-web-probe-sentinel-${nodeLower}-accounts"
namespace: "hwlab-${lane}"
data:
- sourcePurpose: account-a
targetKey: account-a.json
- sourcePurpose: account-b
targetKey: account-b.json
- name: hwlab-web-probe-sentinel-${nodeLower}-frpc
namespace: hwlab-${lane}
- name: "hwlab-web-probe-sentinel-${nodeLower}-frpc"
namespace: "hwlab-${lane}"
data:
- sourcePurpose: frp-token
targetKey: token
variables:
LANE: v03
controlPlaneIndex: "2"
argoProjectName: hwlab-v03
NODE: NC01
templates:
webProbeSentinelTarget:
node: "${NODE}"
lane: "${LANE}"
publicOriginRef: "config/hwlab-node-lanes.yaml#lanes.${LANE}.targets.${NODE}.public.webUrl"
webProbeSentinelCicdCommon:
controlPlaneConfigRef: "config/hwlab-node-control-plane.yaml#targets[${controlPlaneIndex}]"
source:
checkoutPaths:
- scripts
- config
- config.json
- src
- package.json
- bun.lock
- bun.lockb
entrypoint: scripts/web-probe-sentinel-service.ts
buildContext: "."
gitMirrorReadUrl: http://gitea-http.devops-infra.svc.cluster.local:3000/mirrors/pikasTech-unidesk.git
gitSshUrl: ssh://git@ssh.github.com:443/pikasTech/unidesk.git
branch: master
repository: pikasTech/unidesk
sourceAuthority:
allowGithubDirectInPipeline: false
allowHostGit: false
resolver: gitea-mirror
mode: giteaSnapshot
sourceSnapshot:
cacheRef: source.gitMirrorReadUrl
refreshPolicy: gitea-controlled-snapshot
missingObjectPolicy: fail-fast
stageRefPrefix: refs/unidesk/snapshots/gitea-actions/unidesk-master
argo:
namespace: argocd
projectName: "${argoProjectName}"
repoURL: http://git-mirror-http.devops-infra.svc.cluster.local:8080/pikasTech/HWLAB.git
targetRevision: v0.3-gitops
maintenance:
stopCommand: sentinel maintenance stop
startCommand: sentinel maintenance start
monitorWeb:
ciBudget:
maxSeconds: 120
gitMirror:
source: source.gitMirrorReadUrl
preSync: not-required
postFlush: not-required
imageBuild:
packageMode: copy-only-containerfile
networkMode: host
proxySource: node.networkProfile.imageBuildProxy
contextIgnore: generated
verifyPhase: pre-image-build
buildkitState:
mode: hostPath
path: "/var/lib/unidesk/web-probe-sentinel/buildkit-${nodeLower}"
type: DirectoryOrCreate
envReuse:
mode: k8s-buildkit-and-ci-node-deps
nodeDepsPath: /opt/hwlab-ci-node-deps/node_modules
assetRoot: scripts/assets/web-probe-sentinel-monitor-web
runtimeMode: runner-served-bridge
frontendStack: vue3-vendored-browser-build
confirmWait:
maxSeconds: 120
publishCurrent:
dashboard:
enabled: true
required: true
viewport: 1440x900
timeoutMs: 30000
waitTimeoutMs: 60000
commandTimeoutSeconds: 90
fullPage: false
stageBudgets:
sourceSyncSeconds: 20
sourceFetchSeconds: 20
monitorWebVerifySeconds: 15
imageBuildSeconds: 45
gitopsSeconds: 15
argoRuntimeSeconds: 30
dashboardVerifySeconds: 30
endToEndBudget:
maxSeconds: 120
webProbeSentinelSentinel:
mode: web-probe-observe-wrapper
enabled: true
id: "${nodeLower}-web-probe-sentinel"
configRefs:
runtime: "config/hwlab-web-probe-sentinel/profiles.yaml#nodes.${NODE}.sentinels.${nodeLower}-web-probe-sentinel.runtime"
scenarios: "config/hwlab-web-probe-sentinel/scenarios.multi-sentinel.yaml#sentinel.scenarios"
promptSet: "config/hwlab-web-probe-sentinel/prompt-set.dsflash-go.yaml#sentinel.promptSet"
reportViews: "config/hwlab-web-probe-sentinel/report-views.multi-sentinel.yaml#sentinel.reportViews"
publicExposure: "config/hwlab-web-probe-sentinel/profiles.yaml#nodes.${NODE}.sentinels.${nodeLower}-web-probe-sentinel.publicExposure"
cicd: "config/hwlab-web-probe-sentinel/profiles.yaml#nodes.${NODE}.sentinels.${nodeLower}-web-probe-sentinel.cicd"
secrets: "config/hwlab-web-probe-sentinel/profiles.yaml#nodes.${NODE}.sentinels.${nodeLower}-web-probe-sentinel.secrets"
webProbeSentinelRuntime:
kubernetesApi:
endpoint:
host: 172.16.0.5
port: 6443
egress:
enabled: true
rules:
- cidr: 10.43.0.1/32
port: 443
- cidr: 172.16.0.5/32
port: 6443
observability:
otel:
enabled: true
serviceName: "hwlab-web-probe-sentinel-${nodeLower}"
tracesEndpoint: http://otel-collector.platform-infra.svc.cluster.local:4318/v1/traces
sampler: parentbased_traceidratio
samplerArg: "1"
scheduler:
freshnessWarningMultiple: 2
maxConcurrentRuns: 1
heartbeatStaleSeconds: 900
intervalMs: 600000
metricsPath: /metrics
healthPath: /api/health
replicas: 1
pvcStorage: 10Gi
servicePort: 8080
listenHost: 0.0.0.0
namespace: "hwlab-${lane}"
target:
extends: templates.webProbeSentinelTarget
observeWrapperRef: "config/hwlab-node-lanes.yaml#lanes.${LANE}.targets.${NODE}.observability.webProbe.sentinels[0]"
serviceAccountName: "hwlab-web-probe-sentinel-${nodeLower}"
deploymentName: "hwlab-web-probe-sentinel-${nodeLower}"
serviceName: "hwlab-web-probe-sentinel-${nodeLower}"
pvcName: "hwlab-web-probe-sentinel-${nodeLower}-state"
stateRoot: "/var/lib/web-probe-sentinel-${nodeLower}"
imageRef: "127.0.0.1:5000/hwlab/web-probe-sentinel-${nodeLower}:source-commit"
sqlite:
busyTimeoutMs: 2000
path: "/var/lib/web-probe-sentinel-${nodeLower}/index.sqlite"
webProbeSentinelPublicExposure:
expectedA: 82.156.23.220
hostname: monitor.pikapython.com
mode: pk01-caddy-frp-path
enabled: true
publicBaseUrl: "https://monitor.pikapython.com/sentinels/${nodeLower}-web-probe-sentinel"
routePrefix: "/sentinels/${nodeLower}-web-probe-sentinel"
frpc:
tokenKey: token
secretKey: frpc.toml
tokenSourceKey: FRP_TOKEN
tokenSourceRef: platform-infra/pk01-frp.env
serverPort: 22000
serverAddr: 82.156.23.220
image: 127.0.0.1:5000/hwlab/frpc:v0.68.1
deploymentName: "hwlab-web-probe-sentinel-${nodeLower}-frpc"
secretName: "hwlab-web-probe-sentinel-${nodeLower}-frpc"
httpProxy:
name: "hwlab-${nodeLower}-${lane}-web-probe-sentinel"
remotePort: 0
localIP: "hwlab-web-probe-sentinel-${nodeLower}.hwlab-${lane}.svc.cluster.local"
localPort: 8080
caddy:
responseHeaderTimeoutSeconds: 600
tls: auto
email: ops@pikapython.com
serviceName: caddy
configPath: /etc/caddy/Caddyfile
route: PK01
managedBlockOwner: "hwlab-web-probe-sentinel-${nodeLower}-${lane}"
webProbeSentinelCicd:
extends: templates.webProbeSentinelCicdCommon
builder:
ttlSecondsAfterFinished: 3600
activeDeadlineSeconds: 900
gitSshSecretName: git-mirror-github-ssh
sourceMode: sparse-git-checkout
namespace: devops-infra
jobPrefix: "web-probe-sentinel-${nodeLower}-publish"
gitopsPath: "deploy/gitops/node/${nodeLower}/web-probe-sentinel"
argo:
applicationName: "hwlab-web-probe-sentinel-${nodeLower}"
image:
repository: "127.0.0.1:5000/hwlab/web-probe-sentinel-${nodeLower}"
tagSource: source-commit
baseImageRef: "config/hwlab-node-control-plane.yaml#targets[${controlPlaneIndex}].tekton.toolsImage.output"
envRecipeRef: "config/hwlab-web-probe-sentinel/profiles.yaml#nodes.${NODE}.sentinels.${nodeLower}-web-probe-sentinel.runtime"
targetValidation:
scenarioId: workbench-dsflash-go-hwpod-two-turn-freeze-repro
maxSeconds: 600
serviceUnavailablePolicy: structured-failure
cadenceScheduler:
enabled: true
reason: k8s-native-periodic-quick-verify
concurrencyPolicy: Forbid
startingDeadlineSeconds: 600
successfulJobsHistoryLimit: 3
failedJobsHistoryLimit: 5
activeDeadlineSlackSeconds: 60
ttlSecondsAfterFinished: 86400
backoffLimit: 0
+103 -165
View File
@@ -6,16 +6,14 @@ metadata:
- 1148
- 1110
- 1117
defaults:
targetId: JD01
server:
id: benchmark-validated-master-shadowsocks-server
enabled: true
benchmarkRef: pikasTech/unidesk#1110
implementationRef: pikasTech/unidesk#1117
sourceConfigRef: config/platform-infra/egress-proxy-sources.yaml#sources.master-shadowsocks
benchmarkRef: "pikasTech/unidesk#1110"
implementationRef: "pikasTech/unidesk#1117"
sourceConfigRef: "config/platform-infra/egress-proxy-sources.yaml#sources.master-shadowsocks"
composeFile: config/platform-infra/sub2api-master-egress-proxy.compose.yaml
serviceName: sub2api-master-egress-proxy
containerName: unidesk-sub2api-master-egress-proxy
@@ -27,176 +25,116 @@ server:
mode: tcp
host: 127.0.0.1
port: 18792
sources:
nc01-vpn-server-shadowsocks:
sourceType: vpn-server-shadowsocks
serverRef: /root/vpn-server
extends: templates.sources.singBoxHostClient
benchmarkRef: local-vpn-server
implementationRef: /root/vpn-server
sourceConfigRef: config/platform-infra/egress-proxy-sources.yaml#sources.nc01-vpn-server-shadowsocks
client:
mode: trans-static-binary
upstreamUrl: https://github.com/SagerNet/sing-box/releases/download/v1.13.14/sing-box-1.13.14-linux-amd64.tar.gz
version: v1.13.14
archiveCachePath: .state/artifacts/platform-infra/sing-box-1.13.14-linux-amd64.tar.gz
archiveSha256: f48703461a15476951ac4967cdad339d986f4b8096b4eb3ff0829a500502d697
archiveInstallPath: /var/cache/unidesk/host-egress-proxy/sing-box-1.13.14-linux-amd64.tar.gz
binaryMember: sing-box-1.13.14-linux-amd64/sing-box
binaryCachePath: .state/artifacts/platform-infra/sing-box-1.13.14-linux-amd64
binarySha256: 68aeab83cc4ab2659a5b92232261a20746ccdafc3b3d1e19b2d63247eec3bbf7
installPath: /usr/local/bin/sing-box
configPath: /etc/unidesk/host-egress-proxy/sing-box.json
unitPath: /etc/systemd/system/unidesk-host-egress-proxy.service
serviceName: unidesk-host-egress-proxy
listenHost: 127.0.0.1
listenPort: 10808
podAccess:
enabled: true
listenHost: 10.42.0.1
listenPort: 10808
proxyUrl: http://10.42.0.1:10808
clashApiListen: 127.0.0.1:19090
healthUrl: http://127.0.0.1:19090/connections
proxyUrl: http://127.0.0.1:10808
externalProbeUrl: https://www.gstatic.com/generate_204
jd01-real-deps-master-shadowsocks:
sourceType: vpn-server-shadowsocks
serverRef: /root/vpn-server
benchmarkRef: pikasTech/unidesk#1110
implementationRef: /root/vpn-server
sourceConfigRef: config/platform-infra/egress-proxy-sources.yaml#sources.nc01-vpn-server-shadowsocks
client:
mode: trans-static-binary
upstreamUrl: https://github.com/SagerNet/sing-box/releases/download/v1.13.14/sing-box-1.13.14-linux-amd64.tar.gz
version: v1.13.14
archiveCachePath: .state/artifacts/platform-infra/sing-box-1.13.14-linux-amd64.tar.gz
archiveSha256: f48703461a15476951ac4967cdad339d986f4b8096b4eb3ff0829a500502d697
archiveInstallPath: /var/cache/unidesk/host-egress-proxy/sing-box-1.13.14-linux-amd64.tar.gz
binaryMember: sing-box-1.13.14-linux-amd64/sing-box
binaryCachePath: .state/artifacts/platform-infra/sing-box-1.13.14-linux-amd64
binarySha256: 68aeab83cc4ab2659a5b92232261a20746ccdafc3b3d1e19b2d63247eec3bbf7
installPath: /usr/local/bin/sing-box
configPath: /etc/unidesk/host-egress-proxy/sing-box.json
unitPath: /etc/systemd/system/unidesk-host-egress-proxy.service
serviceName: unidesk-host-egress-proxy
listenHost: 127.0.0.1
listenPort: 10808
podAccess:
enabled: true
listenHost: 10.42.0.1
listenPort: 10808
proxyUrl: http://10.42.0.1:10808
clashApiListen: 127.0.0.1:19090
healthUrl: http://127.0.0.1:19090/connections
proxyUrl: http://127.0.0.1:10808
externalProbeUrl: https://www.gstatic.com/generate_204
extends: templates.sources.singBoxHostClient
benchmarkRef: "pikasTech/unidesk#1110"
targets:
NC01:
extends: templates.targets.hostProxyClient
route: NC01
enabled: true
sourceRef: sources.nc01-vpn-server-shadowsocks
env:
httpProxy: http://127.0.0.1:10808
httpsProxy: http://127.0.0.1:10808
allProxy: http://127.0.0.1:10808
noProxy:
- localhost
- 127.0.0.1
- ::1
- host.docker.internal
- 152.53.229.148
- 10.0.0.0/8
- 10.42.0.0/16
- 10.43.0.0/16
- 172.16.0.0/12
- 192.168.0.0/16
- .svc
- .svc.cluster.local
- .cluster.local
- kubernetes
- kubernetes.default
- kubernetes.default.svc
- argocd-repo-server
- argocd-repo-server.argocd
- argocd-redis
- argocd-redis.argocd
- git-mirror-http
- git-mirror-http.devops-infra
- git-mirror-write
- git-mirror-write.devops-infra
- 127.0.0.1:5000
- localhost:5000
- hyueapi.com
- .hyueapi.com
files:
envFile: /etc/unidesk/proxy.env
profile: /etc/profile.d/unidesk-proxy.sh
apt: /etc/apt/apt.conf.d/90unidesk-proxy
dockerSystemdDropIn: /etc/systemd/system/docker.service.d/10-unidesk-proxy.conf
k3sSystemdDropIn: /etc/systemd/system/k3s.service.d/10-unidesk-proxy.conf
trans:
hostProxyEnv:
enabled: true
envFileRef: files.envFile
applyTo: host-posix-commands
apply:
reloadSystemd: true
restartDocker: true
restartK3s: true
noProxyRefs:
common: noProxy.common
extras: noProxy.nodeExtras.NC01
JD01:
extends: templates.targets.hostProxyClient
route: JD01
enabled: true
sourceRef: sources.jd01-real-deps-master-shadowsocks
env:
httpProxy: http://127.0.0.1:10808
httpsProxy: http://127.0.0.1:10808
allProxy: http://127.0.0.1:10808
noProxy:
- localhost
- 127.0.0.1
- ::1
- host.docker.internal
- 74.48.78.17
- 82.156.23.220
- 10.0.0.0/8
- 10.42.0.0/16
- 10.43.0.0/16
- 172.16.0.0/12
- 192.168.0.0/16
- .svc
- .svc.cluster.local
- .cluster.local
- kubernetes
- kubernetes.default
- kubernetes.default.svc
- argocd-repo-server
- argocd-repo-server.argocd
- argocd-redis
- argocd-redis.argocd
- git-mirror-http
- git-mirror-http.devops-infra
- git-mirror-write
- git-mirror-write.devops-infra
- 127.0.0.1:5000
- localhost:5000
- hyueapi.com
- .hyueapi.com
files:
envFile: /etc/unidesk/proxy.env
profile: /etc/profile.d/unidesk-proxy.sh
apt: /etc/apt/apt.conf.d/90unidesk-proxy
dockerSystemdDropIn: /etc/systemd/system/docker.service.d/10-unidesk-proxy.conf
k3sSystemdDropIn: /etc/systemd/system/k3s.service.d/10-unidesk-proxy.conf
trans:
hostProxyEnv:
enabled: true
envFileRef: files.envFile
applyTo: host-posix-commands
apply:
reloadSystemd: true
restartDocker: true
restartK3s: true
noProxyRefs:
common: noProxy.common
extras: noProxy.nodeExtras.JD01
templates:
sources:
singBoxHostClient:
sourceType: vpn-server-shadowsocks
serverRef: /root/vpn-server
implementationRef: /root/vpn-server
sourceConfigRef: "config/platform-infra/egress-proxy-sources.yaml#sources.nc01-vpn-server-shadowsocks"
client:
mode: trans-static-binary
upstreamUrl: https://github.com/SagerNet/sing-box/releases/download/v1.13.14/sing-box-1.13.14-linux-amd64.tar.gz
version: v1.13.14
archiveCachePath: .state/artifacts/platform-infra/sing-box-1.13.14-linux-amd64.tar.gz
archiveSha256: f48703461a15476951ac4967cdad339d986f4b8096b4eb3ff0829a500502d697
archiveInstallPath: /var/cache/unidesk/host-egress-proxy/sing-box-1.13.14-linux-amd64.tar.gz
binaryMember: sing-box-1.13.14-linux-amd64/sing-box
binaryCachePath: .state/artifacts/platform-infra/sing-box-1.13.14-linux-amd64
binarySha256: 68aeab83cc4ab2659a5b92232261a20746ccdafc3b3d1e19b2d63247eec3bbf7
installPath: /usr/local/bin/sing-box
configPath: /etc/unidesk/host-egress-proxy/sing-box.json
unitPath: /etc/systemd/system/unidesk-host-egress-proxy.service
serviceName: unidesk-host-egress-proxy
listenHost: 127.0.0.1
listenPort: 10808
podAccess:
enabled: true
listenHost: 10.42.0.1
listenPort: 10808
proxyUrl: http://10.42.0.1:10808
clashApiListen: 127.0.0.1:19090
healthUrl: http://127.0.0.1:19090/connections
proxyUrl: http://127.0.0.1:10808
externalProbeUrl: https://www.gstatic.com/generate_204
targets:
hostProxyClient:
enabled: true
env:
httpProxy: http://127.0.0.1:10808
httpsProxy: http://127.0.0.1:10808
allProxy: http://127.0.0.1:10808
files:
envFile: /etc/unidesk/proxy.env
profile: /etc/profile.d/unidesk-proxy.sh
apt: /etc/apt/apt.conf.d/90unidesk-proxy
dockerSystemdDropIn: /etc/systemd/system/docker.service.d/10-unidesk-proxy.conf
k3sSystemdDropIn: /etc/systemd/system/k3s.service.d/10-unidesk-proxy.conf
trans:
hostProxyEnv:
enabled: true
envFileRef: files.envFile
applyTo: host-posix-commands
apply:
reloadSystemd: true
restartDocker: true
restartK3s: true
noProxy:
common:
- localhost
- 127.0.0.1
- "::1"
- host.docker.internal
- 10.0.0.0/8
- 10.42.0.0/16
- 10.43.0.0/16
- 172.16.0.0/12
- 192.168.0.0/16
- .svc
- .svc.cluster.local
- .cluster.local
- kubernetes
- kubernetes.default
- kubernetes.default.svc
- argocd-repo-server
- argocd-repo-server.argocd
- argocd-redis
- argocd-redis.argocd
- git-mirror-http
- git-mirror-http.devops-infra
- git-mirror-write
- git-mirror-write.devops-infra
- 127.0.0.1:5000
- localhost:5000
- hyueapi.com
- .hyueapi.com
nodeExtras:
NC01:
- 152.53.229.148
JD01:
- 74.48.78.17
- 82.156.23.220
+157 -212
View File
@@ -1,6 +1,5 @@
version: 1
kind: platform-infra-pipelines-as-code
metadata:
id: pac-gitea-consumers
owner: unidesk
@@ -10,14 +9,11 @@ metadata:
- 1555
- 1557
- 1560
defaults:
targetId: NC01
consumerId: hwlab-nc01-v03
display:
timeZone: Asia/Shanghai
release:
version: v0.48.0
manifestUrl: https://github.com/openshift-pipelines/pipelines-as-code/releases/download/v0.48.0/release.k8s.yaml
@@ -25,13 +21,11 @@ release:
controllerServiceName: pipelines-as-code-controller
controllerServicePort: 8080
waitTimeoutSeconds: 55
closeout:
gitOpsMirrorFlush:
enabled: true
waitTimeoutSeconds: 55
maxAttempts: 5
targets:
- id: JD01
route: JD01:k3s
@@ -43,9 +37,8 @@ targets:
namespace: agentrun-ci
role: active-cicd-trigger-authority
enabled: true
gitea:
configRef: config/platform-infra/gitea.yaml#sourceAuthority.repositories
configRef: "config/platform-infra/gitea.yaml#sourceAuthority.repositories"
internalBaseUrl: http://gitea-http.devops-infra.svc.cluster.local:3000
admin:
sourceRoot: /root/unidesk
@@ -60,212 +53,164 @@ gitea:
events:
- push
branch: params.source_branch
repositories:
- id: agentrun-jd01-v02
name: agentrun-jd01-v02
namespace: agentrun-ci
providerType: gitea
- extends: templates.repositories.agentrunV02
variables:
NODE: JD01
LANE: v02
url: https://gitea.pikapython.com/mirrors/pikasTech-agentrun
cloneUrl: http://gitea-http.devops-infra.svc.cluster.local:3000/mirrors/pikasTech-agentrun.git
owner: mirrors
repo: pikasTech-agentrun
secretName: pac-gitea-agentrun-jd01-v02
tokenKey: token
webhookSecretKey: webhook.secret
concurrencyLimit: 1
params:
git_read_url: http://gitea-http.devops-infra.svc.cluster.local:3000/mirrors/pikasTech-agentrun.git
git_write_url: http://gitea-http.devops-infra.svc.cluster.local:3000/mirrors/pikasTech-agentrun.git
source_branch: v0.2
gitops_branch: jd01-v0.2-gitops
# Historical prefix name retained for existing snapshot refs; active architecture is Gitea + PaC, not Gitea Actions.
source_snapshot_prefix: refs/unidesk/snapshots/gitea-actions/agentrun-v0.2
pipeline_name: agentrun-jd01-v02-ci-image-publish
pipeline_run_prefix: agentrun-jd01-v02-ci
service_account: agentrun-jd01-v02-tekton-runner
workspace_pvc_size: 2Gi
- id: agentrun-nc01-v02
name: agentrun-nc01-v02
namespace: agentrun-ci
providerType: gitea
url: http://gitea-http.devops-infra.svc.cluster.local:3000/mirrors/pikasTech-agentrun
cloneUrl: http://gitea-http.devops-infra.svc.cluster.local:3000/mirrors/pikasTech-agentrun.git
owner: mirrors
repo: pikasTech-agentrun
secretName: pac-gitea-agentrun-nc01-v02
tokenKey: token
webhookSecretKey: webhook.secret
concurrencyLimit: 1
params:
git_read_url: http://gitea-http.devops-infra.svc.cluster.local:3000/mirrors/pikasTech-agentrun.git
git_write_url: http://gitea-http.devops-infra.svc.cluster.local:3000/mirrors/pikasTech-agentrun.git
source_branch: v0.2
gitops_branch: nc01-v0.2-gitops
# Historical prefix name retained for existing snapshot refs; active architecture is Gitea + PaC, not Gitea Actions.
source_snapshot_prefix: refs/unidesk/snapshots/gitea-actions/agentrun-v0.2
pipeline_name: agentrun-nc01-v02-ci-image-publish
pipeline_run_prefix: agentrun-nc01-v02-ci
service_account: agentrun-nc01-v02-tekton-runner
workspace_pvc_size: 2Gi
- id: sentinel-jd01-v03
name: sentinel-jd01-v03
namespace: devops-infra
providerType: gitea
- extends: templates.repositories.agentrunV02
variables:
NODE: NC01
LANE: v02
- extends: templates.repositories.sentinelV03
variables:
NODE: JD01
LANE: v03
url: https://gitea.pikapython.com/mirrors/pikasTech-unidesk
cloneUrl: http://gitea-http.devops-infra.svc.cluster.local:3000/mirrors/pikasTech-unidesk.git
owner: mirrors
repo: pikasTech-unidesk
secretName: pac-gitea-sentinel-jd01-v03
tokenKey: token
webhookSecretKey: webhook.secret
concurrencyLimit: 1
params:
git_read_url: http://gitea-http.devops-infra.svc.cluster.local:3000/mirrors/pikasTech-unidesk.git
source_branch: master
# Historical prefix name retained for existing snapshot refs; active architecture is Gitea + PaC, not Gitea Actions.
source_snapshot_prefix: refs/unidesk/snapshots/gitea-actions/unidesk-master
node: JD01
lane: v03
sentinel_id: jd01-web-probe-sentinel
image_repository: 127.0.0.1:5000/hwlab/web-probe-sentinel-jd01
registry_probe_base: http://127.0.0.1:5000
gitops_branch: v0.3-gitops
gitops_manifest_path: deploy/gitops/node/jd01/web-probe-sentinel/web-probe-sentinel.yaml
pipeline_name: hwlab-web-probe-sentinel-jd01-pac
pipeline_run_prefix: hwlab-web-probe-sentinel-jd01
- id: sentinel-nc01-v03
name: sentinel-nc01-v03
namespace: devops-infra
providerType: gitea
url: http://gitea-http.devops-infra.svc.cluster.local:3000/mirrors/pikasTech-unidesk
cloneUrl: http://gitea-http.devops-infra.svc.cluster.local:3000/mirrors/pikasTech-unidesk.git
owner: mirrors
repo: pikasTech-unidesk
secretName: pac-gitea-sentinel-nc01-v03
tokenKey: token
webhookSecretKey: webhook.secret
concurrencyLimit: 1
params:
git_read_url: http://gitea-http.devops-infra.svc.cluster.local:3000/mirrors/pikasTech-unidesk.git
source_branch: master
# Historical prefix name retained for existing snapshot refs; active architecture is Gitea + PaC, not Gitea Actions.
source_snapshot_prefix: refs/unidesk/snapshots/gitea-actions/unidesk-master
node: NC01
lane: v03
sentinel_id: nc01-web-probe-sentinel
image_repository: 127.0.0.1:5000/hwlab/web-probe-sentinel-nc01
registry_probe_base: http://127.0.0.1:5000
gitops_branch: v0.3-gitops
gitops_manifest_path: deploy/gitops/node/nc01/web-probe-sentinel/web-probe-sentinel.yaml
pipeline_name: hwlab-web-probe-sentinel-nc01-pac
pipeline_run_prefix: hwlab-web-probe-sentinel-nc01
- id: hwlab-jd01-v03
name: hwlab-jd01-v03
namespace: hwlab-ci
providerType: gitea
- extends: templates.repositories.sentinelV03
variables:
NODE: NC01
LANE: v03
- extends: templates.repositories.hwlabV03
variables:
NODE: JD01
LANE: v03
url: https://gitea.pikapython.com/mirrors/pikasTech-HWLAB
cloneUrl: http://gitea-http.devops-infra.svc.cluster.local:3000/mirrors/pikasTech-HWLAB.git
owner: mirrors
repo: pikasTech-HWLAB
secretName: pac-gitea-hwlab-jd01-v03
tokenKey: token
webhookSecretKey: webhook.secret
concurrencyLimit: 1
params:
git_read_url: http://gitea-http.devops-infra.svc.cluster.local:3000/mirrors/pikasTech-HWLAB.git
git_write_url: http://git-mirror-write.devops-infra.svc.cluster.local:8080/pikasTech/HWLAB.git
source_branch: v0.3
gitops_branch: v0.3-gitops
# Historical prefix name retained for existing snapshot refs; active architecture is Gitea + PaC, not Gitea Actions.
source_snapshot_prefix: refs/unidesk/snapshots/gitea-actions/hwlab-node-runtime/v0.3
pipeline_name: hwlab-jd01-v03-ci-image-publish
pipeline_run_prefix: hwlab-jd01-v03-ci-poll
service_account: hwlab-jd01-v03-tekton-runner
node: JD01
lane: v03
runtime_namespace: hwlab-v03
- id: hwlab-nc01-v03
name: hwlab-nc01-v03
namespace: hwlab-ci
providerType: gitea
url: http://gitea-http.devops-infra.svc.cluster.local:3000/mirrors/pikasTech-HWLAB
cloneUrl: http://gitea-http.devops-infra.svc.cluster.local:3000/mirrors/pikasTech-HWLAB.git
owner: mirrors
repo: pikasTech-HWLAB
secretName: pac-gitea-hwlab-nc01-v03
tokenKey: token
webhookSecretKey: webhook.secret
concurrencyLimit: 1
params:
git_read_url: http://gitea-http.devops-infra.svc.cluster.local:3000/mirrors/pikasTech-HWLAB.git
git_write_url: http://git-mirror-write.devops-infra.svc.cluster.local:8080/pikasTech/HWLAB.git
source_branch: v0.3
gitops_branch: v0.3-gitops
# Historical prefix name retained for existing snapshot refs; active architecture is Gitea + PaC, not Gitea Actions.
source_snapshot_prefix: refs/unidesk/snapshots/gitea-actions/hwlab-node-runtime/v0.3
pipeline_name: hwlab-nc01-v03-ci-image-publish
pipeline_run_prefix: hwlab-nc01-v03-ci-poll
service_account: hwlab-nc01-v03-tekton-runner
node: NC01
lane: v03
runtime_namespace: hwlab-v03
- extends: templates.repositories.hwlabV03
variables:
NODE: NC01
LANE: v03
consumers:
- id: agentrun-jd01-v02
repositoryRef: agentrun-jd01-v02
node: JD01
lane: jd01-v02
namespace: agentrun-ci
pipeline: agentrun-jd01-v02-ci-image-publish
pipelineRunPrefix: agentrun-jd01-v02-ci
argoNamespace: argocd
argoApplication: agentrun-jd01-v02
- id: agentrun-nc01-v02
repositoryRef: agentrun-nc01-v02
node: NC01
lane: nc01-v02
namespace: agentrun-ci
pipeline: agentrun-nc01-v02-ci-image-publish
pipelineRunPrefix: agentrun-nc01-v02-ci
argoNamespace: argocd
argoApplication: agentrun-nc01-v02
- id: sentinel-jd01-v03
repositoryRef: sentinel-jd01-v03
node: JD01
lane: v03
namespace: devops-infra
pipeline: hwlab-web-probe-sentinel-jd01-pac
pipelineRunPrefix: hwlab-web-probe-sentinel-jd01
argoNamespace: argocd
argoApplication: hwlab-web-probe-sentinel-jd01
- id: sentinel-nc01-v03
repositoryRef: sentinel-nc01-v03
node: NC01
lane: v03
namespace: devops-infra
pipeline: hwlab-web-probe-sentinel-nc01-pac
pipelineRunPrefix: hwlab-web-probe-sentinel-nc01
argoNamespace: argocd
argoApplication: hwlab-web-probe-sentinel-nc01
- id: hwlab-jd01-v03
repositoryRef: hwlab-jd01-v03
node: JD01
lane: v03
namespace: hwlab-ci
pipeline: hwlab-jd01-v03-ci-image-publish
pipelineRunPrefix: hwlab-jd01-v03-ci-poll
argoNamespace: argocd
argoApplication: hwlab-node-v03
closeoutGitOpsMirrorFlush: true
- id: hwlab-nc01-v03
repositoryRef: hwlab-nc01-v03
node: NC01
lane: v03
namespace: hwlab-ci
pipeline: hwlab-nc01-v03-ci-image-publish
pipelineRunPrefix: hwlab-nc01-v03-ci-poll
argoNamespace: argocd
argoApplication: hwlab-node-v03
closeoutGitOpsMirrorFlush: true
- extends: templates.consumers.agentrunV02
variables:
NODE: JD01
LANE: v02
- extends: templates.consumers.agentrunV02
variables:
NODE: NC01
LANE: v02
- extends: templates.consumers.sentinelV03
variables:
NODE: JD01
LANE: v03
- extends: templates.consumers.sentinelV03
variables:
NODE: NC01
LANE: v03
- extends: templates.consumers.hwlabV03
variables:
NODE: JD01
LANE: v03
- extends: templates.consumers.hwlabV03
variables:
NODE: NC01
LANE: v03
templates:
repositories:
agentrunV02:
id: "agentrun-${nodeLower}-v02"
name: "agentrun-${nodeLower}-v02"
namespace: agentrun-ci
providerType: gitea
url: http://gitea-http.devops-infra.svc.cluster.local:3000/mirrors/pikasTech-agentrun
cloneUrl: http://gitea-http.devops-infra.svc.cluster.local:3000/mirrors/pikasTech-agentrun.git
owner: mirrors
repo: pikasTech-agentrun
secretName: "pac-gitea-agentrun-${nodeLower}-v02"
tokenKey: token
webhookSecretKey: webhook.secret
concurrencyLimit: 1
params:
git_read_url: http://gitea-http.devops-infra.svc.cluster.local:3000/mirrors/pikasTech-agentrun.git
git_write_url: http://gitea-http.devops-infra.svc.cluster.local:3000/mirrors/pikasTech-agentrun.git
source_branch: v0.2
gitops_branch: "${nodeLower}-v0.2-gitops"
source_snapshot_prefix: refs/unidesk/snapshots/gitea-actions/agentrun-v0.2
pipeline_name: "agentrun-${nodeLower}-v02-ci-image-publish"
pipeline_run_prefix: "agentrun-${nodeLower}-v02-ci"
service_account: "agentrun-${nodeLower}-v02-tekton-runner"
workspace_pvc_size: 2Gi
sentinelV03:
id: "sentinel-${nodeLower}-v03"
name: "sentinel-${nodeLower}-v03"
namespace: devops-infra
providerType: gitea
url: http://gitea-http.devops-infra.svc.cluster.local:3000/mirrors/pikasTech-unidesk
cloneUrl: http://gitea-http.devops-infra.svc.cluster.local:3000/mirrors/pikasTech-unidesk.git
owner: mirrors
repo: pikasTech-unidesk
secretName: "pac-gitea-sentinel-${nodeLower}-v03"
tokenKey: token
webhookSecretKey: webhook.secret
concurrencyLimit: 1
params:
git_read_url: http://gitea-http.devops-infra.svc.cluster.local:3000/mirrors/pikasTech-unidesk.git
source_branch: master
source_snapshot_prefix: refs/unidesk/snapshots/gitea-actions/unidesk-master
node: "${NODE}"
lane: v03
sentinel_id: "${nodeLower}-web-probe-sentinel"
image_repository: "127.0.0.1:5000/hwlab/web-probe-sentinel-${nodeLower}"
registry_probe_base: http://127.0.0.1:5000
gitops_branch: v0.3-gitops
gitops_manifest_path: "deploy/gitops/node/${nodeLower}/web-probe-sentinel/web-probe-sentinel.yaml"
pipeline_name: "hwlab-web-probe-sentinel-${nodeLower}-pac"
pipeline_run_prefix: "hwlab-web-probe-sentinel-${nodeLower}"
hwlabV03:
id: "hwlab-${nodeLower}-v03"
name: "hwlab-${nodeLower}-v03"
namespace: hwlab-ci
providerType: gitea
url: http://gitea-http.devops-infra.svc.cluster.local:3000/mirrors/pikasTech-HWLAB
cloneUrl: http://gitea-http.devops-infra.svc.cluster.local:3000/mirrors/pikasTech-HWLAB.git
owner: mirrors
repo: pikasTech-HWLAB
secretName: "pac-gitea-hwlab-${nodeLower}-v03"
tokenKey: token
webhookSecretKey: webhook.secret
concurrencyLimit: 1
params:
git_read_url: http://gitea-http.devops-infra.svc.cluster.local:3000/mirrors/pikasTech-HWLAB.git
git_write_url: http://git-mirror-write.devops-infra.svc.cluster.local:8080/pikasTech/HWLAB.git
source_branch: v0.3
gitops_branch: v0.3-gitops
source_snapshot_prefix: refs/unidesk/snapshots/gitea-actions/hwlab-node-runtime/v0.3
pipeline_name: "hwlab-${nodeLower}-v03-ci-image-publish"
pipeline_run_prefix: "hwlab-${nodeLower}-v03-ci-poll"
service_account: "hwlab-${nodeLower}-v03-tekton-runner"
node: "${NODE}"
lane: v03
runtime_namespace: hwlab-v03
consumers:
agentrunV02:
id: "agentrun-${nodeLower}-v02"
repositoryRef: "agentrun-${nodeLower}-v02"
node: "${NODE}"
lane: "${nodeLower}-v02"
namespace: agentrun-ci
pipeline: "agentrun-${nodeLower}-v02-ci-image-publish"
pipelineRunPrefix: "agentrun-${nodeLower}-v02-ci"
argoNamespace: argocd
argoApplication: "agentrun-${nodeLower}-v02"
sentinelV03:
id: "sentinel-${nodeLower}-v03"
repositoryRef: "sentinel-${nodeLower}-v03"
node: "${NODE}"
lane: v03
namespace: devops-infra
pipeline: "hwlab-web-probe-sentinel-${nodeLower}-pac"
pipelineRunPrefix: "hwlab-web-probe-sentinel-${nodeLower}"
argoNamespace: argocd
argoApplication: "hwlab-web-probe-sentinel-${nodeLower}"
hwlabV03:
id: "hwlab-${nodeLower}-v03"
repositoryRef: "hwlab-${nodeLower}-v03"
node: "${NODE}"
lane: v03
namespace: hwlab-ci
pipeline: "hwlab-${nodeLower}-v03-ci-image-publish"
pipelineRunPrefix: "hwlab-${nodeLower}-v03-ci-poll"
argoNamespace: argocd
argoApplication: hwlab-node-v03
closeoutGitOpsMirrorFlush: true
+2 -1
View File
@@ -13,6 +13,7 @@ import {
stringArrayField,
stringField,
} from "./platform-infra-ops-library";
import { materializeYamlComposition } from "./yaml-composition";
export const AGENTRUN_CONFIG_PATH = "config/agentrun.yaml";
@@ -464,7 +465,7 @@ export function agentRunSourceSnapshotRef(spec: AgentRunLaneSpec, sourceCommit:
function readAgentRunControlPlaneConfig(env: NodeJS.ProcessEnv): AgentRunControlPlaneConfig {
const configPath = env.AGENTRUN_CONTROL_PLANE_CONFIG ?? rootPath(AGENTRUN_CONFIG_PATH);
const root = readYamlRecord<Record<string, unknown>>(configPath);
const root = materializeYamlComposition(readYamlRecord<Record<string, unknown>>(configPath), { label: configPath }).value;
validateConfigEnvelope(root, configPath);
const controlPlane = recordField(root, "controlPlane", configPath);
const defaultTarget = recordField(controlPlane, "default", `${configPath}.controlPlane`);
+5 -1
View File
@@ -5,6 +5,7 @@
// Responsibility: YAML source-of-truth parsing for HWLAB node/lane Workbench observability.
import { readFileSync } from "node:fs";
import { rootPath } from "./config";
import { materializeYamlComposition } from "./yaml-composition";
export type HwlabRuntimeLane = string;
@@ -1685,7 +1686,10 @@ function runtimeImageBuildsConfig(value: unknown, path: string): HwlabRuntimeIma
function readHwlabNodeLaneConfig(): HwlabNodeLaneConfig {
const path = rootPath(HWLAB_NODE_LANE_CONFIG_PATH);
const raw = readFileSync(path, "utf8");
const parsed = asRecord(Bun.YAML.parse(raw) as unknown, HWLAB_NODE_LANE_CONFIG_PATH);
const parsed = asRecord(
materializeYamlComposition(Bun.YAML.parse(raw) as unknown, { label: HWLAB_NODE_LANE_CONFIG_PATH }).value,
HWLAB_NODE_LANE_CONFIG_PATH,
);
validateConfigEnvelope(parsed);
const defaultTarget = parseDefaultTarget(asRecord(parsed.defaults, `${HWLAB_NODE_LANE_CONFIG_PATH}.defaults`));
const requiredNoProxy = stringArrayField(parsed, "requiredNoProxy", HWLAB_NODE_LANE_CONFIG_PATH);
@@ -3,6 +3,7 @@
import { createHash } from "node:crypto";
import { existsSync, readFileSync } from "node:fs";
import { rootPath } from "./config";
import { materializeYamlComposition } from "./yaml-composition";
export interface WebProbeSentinelTemplateContext {
readonly nodeId: string;
@@ -34,7 +35,7 @@ export function readWebProbeSentinelConfigRef(context: WebProbeSentinelTemplateC
const absPath = rootPath(parsed.file);
if (!existsSync(absPath)) throw new Error(`${parsed.file} does not exist`);
const text = readFileSync(absPath, "utf8");
const doc = Bun.YAML.parse(text) as unknown;
const doc = materializeYamlComposition(Bun.YAML.parse(text) as unknown, { label: parsed.file }).value;
const vars = { ...builtinVars, ...documentVars(doc, builtinVars) };
const path = renderTemplateString(parsed.path, vars, `${parsed.file}#path`);
const target = valueAtConfigPath(doc, path);
@@ -4,6 +4,7 @@ import { existsSync, readFileSync } from "node:fs";
import { rootPath } from "./config";
import { readWebProbeSentinelConfigRefTarget, type WebProbeSentinelTemplateContext } from "./hwlab-node-web-sentinel-config-ref";
import { HWLAB_WEB_PROBE_SENTINEL_CONFIG_REF_KEYS, type HwlabRuntimeLaneSpec, type HwlabRuntimeWebProbeSentinelConfigRefKey, type HwlabRuntimeWebProbeSentinelRegistryItemSpec } from "./hwlab-node-lanes";
import { materializeYamlComposition } from "./yaml-composition";
export interface ResolvedWebProbeSentinel {
readonly id: string;
@@ -169,7 +170,7 @@ export function readConfigRefTarget(ref: string, context?: WebProbeSentinelTempl
}
const absPath = rootPath(file);
if (!existsSync(absPath)) throw new Error(`${file} does not exist`);
const doc = Bun.YAML.parse(readFileSync(absPath, "utf8")) as unknown;
const doc = materializeYamlComposition(Bun.YAML.parse(readFileSync(absPath, "utf8")) as unknown, { label: file }).value;
return valueAtPath(doc, path);
}
+31 -4
View File
@@ -9,6 +9,7 @@ import type { RenderedCliResult } from "./output";
import { shQuote } from "./platform-infra-public-service";
import { fingerprintSecretValues, readEnvSourceFile, requiredEnvValue } from "./secrets";
import { transHostProxyEnvSummary } from "./trans-host-proxy";
import { materializeYamlComposition } from "./yaml-composition";
const configPath = "config/platform-infra/host-proxy.yaml";
@@ -161,7 +162,7 @@ function parseOptions(args: string[]): HostProxyOptions {
}
function readHostProxyConfig(): HostProxyConfig {
const root = record(Bun.YAML.parse(readFileSync(rootPath(configPath), "utf8")), configPath);
const root = record(materializeYamlComposition(Bun.YAML.parse(readFileSync(rootPath(configPath), "utf8")) as unknown, { label: configPath }).value, configPath);
if (root.kind !== "platform-infra-host-proxy") throw new Error(`${configPath}.kind must be platform-infra-host-proxy`);
const defaultsRaw = record(root.defaults, `${configPath}.defaults`);
const server = parseServer(record(root.server, `${configPath}.server`), `${configPath}.server`);
@@ -172,7 +173,7 @@ function readHostProxyConfig(): HostProxyConfig {
}));
const targets: Record<string, HostProxyTarget> = {};
for (const [id, value] of Object.entries(record(root.targets, `${configPath}.targets`))) {
targets[id] = parseTarget(id, record(value, `${configPath}.targets.${id}`), sources);
targets[id] = parseTarget(id, record(value, `${configPath}.targets.${id}`), sources, root);
}
return {
defaults: { targetId: stringField(defaultsRaw, "targetId", `${configPath}.defaults`) },
@@ -315,7 +316,7 @@ function podAccessSpec(raw: Record<string, unknown>, label: string): HostProxyPo
return { enabled, listenHost, listenPort, proxyUrl };
}
function parseTarget(id: string, raw: Record<string, unknown>, sources: Record<string, HostProxySource>): HostProxyTarget {
function parseTarget(id: string, raw: Record<string, unknown>, sources: Record<string, HostProxySource>, root: Record<string, unknown>): HostProxyTarget {
const sourceRef = stringField(raw, "sourceRef", `${configPath}.targets.${id}`);
if (!sourceRef.startsWith("sources.")) throw new Error(`${configPath}.targets.${id}.sourceRef must use sources.<id>`);
const sourceId = sourceRef.slice("sources.".length);
@@ -324,7 +325,9 @@ function parseTarget(id: string, raw: Record<string, unknown>, sources: Record<s
const envRaw = record(raw.env, `${configPath}.targets.${id}.env`);
const filesRaw = record(raw.files, `${configPath}.targets.${id}.files`);
const applyRaw = record(raw.apply, `${configPath}.targets.${id}.apply`);
const noProxy = stringArrayField(envRaw, "noProxy", `${configPath}.targets.${id}.env`);
const noProxy = envRaw.noProxy === undefined
? noProxyFromRefs(envRaw, root, `${configPath}.targets.${id}.env`)
: stringArrayField(envRaw, "noProxy", `${configPath}.targets.${id}.env`);
if (!noProxy.includes("hyueapi.com") || !noProxy.includes(".hyueapi.com")) {
throw new Error(`${configPath}.targets.${id}.env.noProxy must preserve hyueapi.com and .hyueapi.com`);
}
@@ -359,6 +362,30 @@ function parseTarget(id: string, raw: Record<string, unknown>, sources: Record<s
return target;
}
function noProxyFromRefs(envRaw: Record<string, unknown>, root: Record<string, unknown>, label: string): string[] {
const refs = record(envRaw.noProxyRefs, `${label}.noProxyRefs`);
const values: string[] = [];
for (const key of ["common", "extras"] as const) {
const ref = stringField(refs, key, `${label}.noProxyRefs`);
values.push(...stringArrayAtPath(root, ref, `${label}.noProxyRefs.${key}`));
}
return [...new Set(values)];
}
function stringArrayAtPath(root: unknown, ref: string, label: string): string[] {
let current: unknown = root;
for (const segment of ref.split(".")) {
if (!/^[A-Za-z0-9_-]+$/u.test(segment) || typeof current !== "object" || current === null || Array.isArray(current)) {
throw new Error(`${label} references invalid path ${ref}`);
}
current = (current as Record<string, unknown>)[segment];
}
if (!Array.isArray(current) || !current.every((item) => typeof item === "string" && item.length > 0)) {
throw new Error(`${label} must reference a string array`);
}
return [...current];
}
function resolveTarget(config: HostProxyConfig, targetId: string): HostProxyTarget {
const target = config.targets[targetId];
if (target === undefined) throw new Error(`${configPath}.targets.${targetId} is not defined`);
@@ -15,6 +15,7 @@ import {
shQuote,
sha256Fingerprint,
} from "./platform-infra-ops-library";
import { materializeYamlComposition } from "./yaml-composition";
const configFile = rootPath("config", "platform-infra", "pipelines-as-code.yaml");
const configLabel = "config/platform-infra/pipelines-as-code.yaml";
@@ -213,7 +214,7 @@ function help(): Record<string, unknown> {
}
function readPacConfig(): PacConfig {
const root = readYamlRecord<Record<string, unknown>>(configFile, "platform-infra-pipelines-as-code");
const root = materializeYamlComposition(readYamlRecord<Record<string, unknown>>(configFile, "platform-infra-pipelines-as-code"), { label: configLabel }).value;
const release = y.objectField(root, "release", "");
const closeout = y.objectField(root, "closeout", "");
const gitOpsMirrorFlush = y.objectField(closeout, "gitOpsMirrorFlush", "closeout");
+2 -1
View File
@@ -1,6 +1,7 @@
import { existsSync, readFileSync } from "node:fs";
import { join } from "node:path";
import { repoRoot } from "./config";
import { materializeYamlComposition } from "./yaml-composition";
export const transHostProxyConfigPath = "config/platform-infra/host-proxy.yaml";
@@ -19,7 +20,7 @@ export interface TransHostProxyEnvRule {
export function readTransHostProxyEnvRule(targetId: string): TransHostProxyEnvRule | null {
const absolutePath = join(repoRoot, transHostProxyConfigPath);
if (!existsSync(absolutePath)) return null;
const root = record(Bun.YAML.parse(readFileSync(absolutePath, "utf8")), transHostProxyConfigPath);
const root = record(materializeYamlComposition(Bun.YAML.parse(readFileSync(absolutePath, "utf8")) as unknown, { label: transHostProxyConfigPath }).value, transHostProxyConfigPath);
if (root.kind !== "platform-infra-host-proxy") throw new Error(`${transHostProxyConfigPath}.kind must be platform-infra-host-proxy`);
const targets = record(root.targets, `${transHostProxyConfigPath}.targets`);
const targetRaw = targets[targetId];
+166
View File
@@ -0,0 +1,166 @@
// SPEC: pikasTech/unidesk#1579 YAML-first composition.
// Responsibility: materialize lightweight YAML templates before domain parsers validate shapes.
export interface YamlCompositionResult<T> {
readonly value: T;
readonly traces: readonly YamlCompositionTrace[];
}
export interface YamlCompositionTrace {
readonly path: string;
readonly extends: readonly string[];
readonly overrideKeys: readonly string[];
readonly variables: Record<string, string>;
}
interface CompositionOptions {
readonly label: string;
readonly stripTemplateKeys?: boolean;
}
const COMPOSITION_KEYS = new Set(["extends", "overrides", "variables"]);
const TEMPLATE_ROOT_KEYS = new Set(["templates", "baselines"]);
export function materializeYamlComposition<T>(input: T, options: CompositionOptions): YamlCompositionResult<T> {
const traces: YamlCompositionTrace[] = [];
const root = input as unknown;
const value = materializeValue(root, root, options.label, {}, traces, []);
if (options.stripTemplateKeys === true && isRecord(value)) {
const stripped = { ...value };
for (const key of TEMPLATE_ROOT_KEYS) delete stripped[key];
return { value: stripped as T, traces };
}
return { value: value as T, traces };
}
export function renderYamlTemplateStrings<T>(input: T, variables: Record<string, string>): T {
return renderValue(input as unknown, deriveVariables(variables)) as T;
}
function materializeValue(
value: unknown,
root: unknown,
path: string,
inheritedVariables: Record<string, string>,
traces: YamlCompositionTrace[],
stack: readonly string[],
): unknown {
if (Array.isArray(value)) {
return value.map((item, index) => materializeValue(item, root, `${path}[${index}]`, inheritedVariables, traces, stack));
}
if (!isRecord(value)) return renderValue(value, inheritedVariables);
const localVariables = deriveVariables({ ...inheritedVariables, ...stringRecord(value.variables) });
const refs = extendsRefs(value.extends);
let merged: Record<string, unknown> = {};
if (refs.length > 0) {
for (const ref of refs) {
if (stack.includes(ref)) throw new Error(`${path}.extends has cyclic template reference ${ref}`);
const template = valueAtPath(root, ref);
if (!isRecord(template)) throw new Error(`${path}.extends references missing object ${ref}`);
const renderedTemplate = materializeValue(template, root, ref, localVariables, traces, [...stack, ref]);
merged = deepMerge(merged, asRecord(renderedTemplate, ref));
}
}
const own = stripCompositionFields(value);
const overrides = value.overrides === undefined ? {} : asRecord(value.overrides, `${path}.overrides`);
merged = deepMerge(merged, own);
merged = deepMerge(merged, overrides);
const rendered = renderObjectScalars(merged, deriveVariables({ ...variablesFromRecord(merged), ...localVariables }));
const result = Object.fromEntries(
Object.entries(asRecord(rendered, path)).map(([key, item]) => [
key,
materializeValue(item, root, `${path}.${key}`, deriveVariables({ ...localVariables, ...variablesFromRecord(rendered) }), traces, stack),
]),
);
if (refs.length > 0) {
traces.push({
path,
extends: refs,
overrideKeys: Object.keys(overrides).sort(),
variables: deriveVariables({ ...localVariables, ...variablesFromRecord(result) }),
});
}
return result;
}
function stripCompositionFields(value: Record<string, unknown>): Record<string, unknown> {
return Object.fromEntries(Object.entries(value).filter(([key]) => !COMPOSITION_KEYS.has(key)));
}
function extendsRefs(value: unknown): string[] {
if (value === undefined) return [];
if (typeof value === "string" && value.trim().length > 0) return [value.trim()];
if (Array.isArray(value) && value.every((item) => typeof item === "string" && item.trim().length > 0)) {
return value.map((item) => item.trim());
}
throw new Error("extends must be a non-empty string or string array");
}
function renderValue(value: unknown, variables: Record<string, string>): unknown {
if (typeof value === "string") {
return value.replace(/\$\{([A-Za-z][A-Za-z0-9_]*)\}/gu, (match, name: string) => variables[name] ?? match);
}
if (Array.isArray(value)) return value.map((item) => renderValue(item, variables));
if (!isRecord(value)) return value;
return Object.fromEntries(Object.entries(value).map(([key, item]) => [key, renderValue(item, variables)]));
}
function renderObjectScalars(value: Record<string, unknown>, variables: Record<string, string>): Record<string, unknown> {
return Object.fromEntries(Object.entries(value).map(([key, item]) => [
key,
typeof item === "string" ? renderValue(item, variables) : item,
]));
}
function variablesFromRecord(value: unknown): Record<string, string> {
if (!isRecord(value)) return {};
const result: Record<string, string> = {};
if (typeof value.node === "string" && value.node.length > 0) result.NODE = value.node;
if (typeof value.lane === "string" && value.lane.length > 0) result.LANE = value.lane;
if (typeof value.id === "string" && value.id.length > 0) result.targetId = value.id;
return deriveVariables(result);
}
function deriveVariables(input: Record<string, string>): Record<string, string> {
const result = { ...input };
if (result.NODE !== undefined) result.nodeLower = result.NODE.toLowerCase();
if (result.LANE !== undefined) result.lane = result.LANE;
if (result.lane !== undefined && result.LANE === undefined) result.LANE = result.lane;
return result;
}
function stringRecord(value: unknown): Record<string, string> {
if (value === undefined) return {};
const raw = asRecord(value, "variables");
return Object.fromEntries(Object.entries(raw).map(([key, item]) => {
if (typeof item !== "string" || item.length === 0) throw new Error(`variables.${key} must be a non-empty string`);
return [key, item];
}));
}
function deepMerge(base: Record<string, unknown>, override: Record<string, unknown>): Record<string, unknown> {
const result: Record<string, unknown> = { ...base };
for (const [key, value] of Object.entries(override)) {
const existing = result[key];
result[key] = isRecord(existing) && isRecord(value) ? deepMerge(existing, value) : value;
}
return result;
}
function valueAtPath(root: unknown, ref: string): unknown {
let current: unknown = root;
for (const segment of ref.split(".")) {
if (!/^[A-Za-z0-9_-]+$/u.test(segment) || !isRecord(current)) return undefined;
current = current[segment];
}
return current;
}
function asRecord(value: unknown, path: string): Record<string, unknown> {
if (!isRecord(value)) throw new Error(`${path} must be an object`);
return value;
}
function isRecord(value: unknown): value is Record<string, unknown> {
return typeof value === "object" && value !== null && !Array.isArray(value);
}