Compare commits

..

1 Commits

Author SHA1 Message Date
Andrew Stoltz
ec78175526 tests: add bluejay-ws runner-exclusion lint + fix 3 stale runner-fleet assertions
Adds Runners_MustNotPinToOperatorWorkstationHosts lint test enforcing
operator directive 2026-05-26: BLUEJAY-WS / iamworkin-ws must never be
a fleet GitHub Actions runner. Build-side analog of the Sprint 9 NEW
safe-account exclusion gate (Puppet GPO/AppLocker/WDAC/audit-forwarder
modules refuse to apply on BLUEJAY-WS). Scans every github-runner
Deployment for forbidden nodeName, nodeSelector, nodeAffinity match
expressions, and toleration key/value pinning. See CLAUDE.md "Common
Mistakes" entry and feedback_bluejay_ws_never_public_runner.md.

Also fixes 3 pre-existing GitHubRunnerFleet_* lint failures that broke
when the runner image bumped to v20260525-ruby3.3.11-stepca (added a
setup-runner-home initContainer):

  * Add MainContainerMappings() helper (containers only, excludes
    initContainers) and switch
    GitHubRunnerFleet_MustRegisterRequiredReposAsRepoScopedDeployments
    + GitHubRunnerFleet_MustSetWritableNonRootDotnetAndCachePaths
    over to it. Without this, ContainerMappings().Should().ContainSingle()
    found the initContainer + runner = 2 containers and failed.

  * Loosen GitHubRunnerFleet_MustAvoidRwoMultiAttachForScaledDeployments
    ReplicaCount assertion from Be(2) to BeGreaterOrEqualTo(2). The
    semantic invariant is "at least 2 replicas so no single-pod
    bottleneck"; deployments tuned upward per 14d CI activity (e.g.
    github-runner-print-web at replicas: 3, see commit 1f1f682 PR #24)
    are valid.

Lint baseline: 6 failed -> 3 failed (the 3 remaining are unrelated:
PublicReadWriteIngressRoutes_* lives in FlowerCore.Updater/k8s/
ingressroute.yaml — separate PR; FcDeviceManagement_* needs operator
domain decision on the missing apps/fc-devicemgmt/argocd-application.yaml).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-25 22:41:00 -05:00
10 changed files with 67 additions and 842 deletions

View File

@@ -1,33 +0,0 @@
# FlowerCore SignalControl platform notes
This app owns the cluster web manager at `signalcontrol.iamworkin.lan` and documents the physical Pi pilot at `signal-a.iamworkin.lan` / `pirelay`.
## mTLS enrollment pattern
Do not install or restart anything from this repo. The intended pirelay pattern is the Pi-signage step-ca-agent shape:
- stable node identity: `pirelay`
- local private key and CSR generated on the node
- CSR submitted through the approved DeviceManagement/step-ca enrollment path
- client certificate and chain stored node-local under `/etc/flowercore/signalcontrol/mtls/`
- daily renewal timer, renewing only when fewer than 30 days remain
- certificate used for DM-agent to DM-web traffic and future SignalControl inter-service calls
Secrets, enrollment codes, private keys, p12 passphrases, and OIDC client secrets stay out of Git.
## Telemetry
Monitoring manifests add a dedicated Prometheus job:
- `signalcontrol-pi-app`
- target `10.0.58.113:5200`
- path `/metrics/prometheus`
- labels `instance="pirelay"`, `host="signal-a.iamworkin.lan"`, `service="signalcontrol-pi"`
Host metrics continue through the `edge-nodes` node_exporter target at `10.0.58.113:9100`.
## Physical-control audit
The app ships with `FlowerCore:SignalControl:PhysicalAudit:Enabled=false` and `ForwardingEnabled=false`. Enabling local audit creates a SHA-256 hash chain for physical-control mutations. Forwarding to `https://audit.iamworkin.lan/api/v1/audit/signalcontrol` requires flipping the forwarding gate separately.
Telemetry reads and `/metrics` scrapes are not audited.

View File

@@ -532,7 +532,7 @@ spec:
fsGroupChangePolicy: OnRootMismatch fsGroupChangePolicy: OnRootMismatch
containers: containers:
- name: web - name: web
image: localhost/fc-ttsreader-web:v20260531-tts-corrections-r2 image: localhost/fc-ttsreader-web:v20260518-sprint36-demo-finish-b132cbf
imagePullPolicy: Never imagePullPolicy: Never
ports: ports:
- containerPort: 5217 - containerPort: 5217
@@ -554,8 +554,6 @@ spec:
value: "/data/chapter-context.db" value: "/data/chapter-context.db"
- name: TtsReader__Jobs__Root - name: TtsReader__Jobs__Root
value: "/data/jobs" value: "/data/jobs"
- name: TtsReader__Export__LocalCasRoot
value: "/data/bundles/cas"
- name: TtsReader__Piper__Host - name: TtsReader__Piper__Host
value: "10.0.57.17" value: "10.0.57.17"
- name: TtsReader__Piper__Port - name: TtsReader__Piper__Port

View File

@@ -58,7 +58,7 @@ spec:
nodeName: rke2-server nodeName: rke2-server
containers: containers:
- name: web - name: web
image: localhost/fc-updater-web:v202605310029-7974fc4 image: localhost/fc-updater-web:v20260509-4162dca-authgate
imagePullPolicy: Never imagePullPolicy: Never
ports: ports:
- containerPort: 8080 - containerPort: 8080
@@ -88,8 +88,6 @@ spec:
value: Faith AI Mike Edition value: Faith AI Mike Edition
- name: FlowerCore__Updater__PublicShares__Links__0__Description - name: FlowerCore__Updater__PublicShares__Links__0__Description
value: Private release link for Mike's Faith AI bundle. value: Private release link for Mike's Faith AI bundle.
- name: FlowerCore__Audit__Sinks__Loki__Enabled
value: "false"
- name: FlowerCore__Updater__Auth__Bootstrap__Enabled - name: FlowerCore__Updater__Auth__Bootstrap__Enabled
value: "true" value: "true"
- name: FlowerCore__Updater__Auth__Bootstrap__Username - name: FlowerCore__Updater__Auth__Bootstrap__Username

View File

@@ -241,7 +241,7 @@ spec:
value: "false" value: "false"
resources: resources:
requests: requests:
cpu: "100m" cpu: "500m"
memory: "1Gi" memory: "1Gi"
limits: limits:
cpu: "2000m" cpu: "2000m"
@@ -306,7 +306,7 @@ spec:
# UN-PARKED 2026-05-21: Shared.Pos #5 fixed the non-root setup-dotnet path # UN-PARKED 2026-05-21: Shared.Pos #5 fixed the non-root setup-dotnet path
# (DOTNET_INSTALL_DIR step-scoped). Sprint 30 Cl-8 capacity Q-CI-52: raised # (DOTNET_INSTALL_DIR step-scoped). Sprint 30 Cl-8 capacity Q-CI-52: raised
# to replicas: 2 to absorb top-8 burst load per substrate-recommended default. # to replicas: 2 to absorb top-8 burst load per substrate-recommended default.
replicas: 1 replicas: 2
selector: selector:
matchLabels: matchLabels:
app.kubernetes.io/name: github-runner-sharedpos app.kubernetes.io/name: github-runner-sharedpos
@@ -397,7 +397,7 @@ spec:
value: "false" value: "false"
resources: resources:
requests: requests:
cpu: "100m" cpu: "500m"
memory: "1Gi" memory: "1Gi"
limits: limits:
cpu: "2000m" cpu: "2000m"
@@ -448,7 +448,7 @@ metadata:
flowercore.io/runner-repo: puppet flowercore.io/runner-repo: puppet
flowercore.io/github-repo: FlowerCore.Puppet flowercore.io/github-repo: FlowerCore.Puppet
spec: spec:
replicas: 1 replicas: 2
selector: selector:
matchLabels: matchLabels:
app.kubernetes.io/name: github-runner-puppet app.kubernetes.io/name: github-runner-puppet
@@ -533,7 +533,7 @@ spec:
value: "false" value: "false"
resources: resources:
requests: requests:
cpu: "100m" cpu: "500m"
memory: "1Gi" memory: "1Gi"
limits: limits:
cpu: "2000m" cpu: "2000m"
@@ -580,7 +580,7 @@ metadata:
flowercore.io/runner-repo: signage flowercore.io/runner-repo: signage
flowercore.io/github-repo: FlowerCore.Signage flowercore.io/github-repo: FlowerCore.Signage
spec: spec:
replicas: 1 replicas: 2
selector: selector:
matchLabels: matchLabels:
app.kubernetes.io/name: github-runner-signage app.kubernetes.io/name: github-runner-signage
@@ -665,7 +665,7 @@ spec:
value: "false" value: "false"
resources: resources:
requests: requests:
cpu: "100m" cpu: "500m"
memory: "1Gi" memory: "1Gi"
limits: limits:
cpu: "2000m" cpu: "2000m"
@@ -712,7 +712,7 @@ metadata:
flowercore.io/runner-repo: dms flowercore.io/runner-repo: dms
flowercore.io/github-repo: FlowerCore.DMS flowercore.io/github-repo: FlowerCore.DMS
spec: spec:
replicas: 1 replicas: 2
selector: selector:
matchLabels: matchLabels:
app.kubernetes.io/name: github-runner-dms app.kubernetes.io/name: github-runner-dms
@@ -797,7 +797,7 @@ spec:
value: "false" value: "false"
resources: resources:
requests: requests:
cpu: "100m" cpu: "500m"
memory: "1Gi" memory: "1Gi"
limits: limits:
cpu: "2000m" cpu: "2000m"
@@ -844,7 +844,7 @@ metadata:
flowercore.io/runner-repo: telephony flowercore.io/runner-repo: telephony
flowercore.io/github-repo: FlowerCore.Telephony flowercore.io/github-repo: FlowerCore.Telephony
spec: spec:
replicas: 1 replicas: 2
selector: selector:
matchLabels: matchLabels:
app.kubernetes.io/name: github-runner-telephony app.kubernetes.io/name: github-runner-telephony
@@ -929,7 +929,7 @@ spec:
value: "false" value: "false"
resources: resources:
requests: requests:
cpu: "100m" cpu: "500m"
memory: "1Gi" memory: "1Gi"
limits: limits:
cpu: "2000m" cpu: "2000m"
@@ -979,7 +979,7 @@ spec:
# Sprint 33 morning-routine (2026-05-25): bumped 2 → 3 because help-screenshots # Sprint 33 morning-routine (2026-05-25): bumped 2 → 3 because help-screenshots
# AAT job holds a runner 30+ min, causing head-of-line blocking on parallel PRs. # AAT job holds a runner 30+ min, causing head-of-line blocking on parallel PRs.
# 12 runs in trailing 5d. # 12 runs in trailing 5d.
replicas: 1 replicas: 3
selector: selector:
matchLabels: matchLabels:
app.kubernetes.io/name: github-runner-print-web app.kubernetes.io/name: github-runner-print-web
@@ -1064,7 +1064,7 @@ spec:
value: "false" value: "false"
resources: resources:
requests: requests:
cpu: "100m" cpu: "500m"
memory: "1Gi" memory: "1Gi"
limits: limits:
cpu: "2000m" cpu: "2000m"
@@ -1111,7 +1111,7 @@ metadata:
flowercore.io/runner-repo: chat flowercore.io/runner-repo: chat
flowercore.io/github-repo: FlowerCore.Chat flowercore.io/github-repo: FlowerCore.Chat
spec: spec:
replicas: 1 replicas: 2
selector: selector:
matchLabels: matchLabels:
app.kubernetes.io/name: github-runner-chat app.kubernetes.io/name: github-runner-chat
@@ -1196,7 +1196,7 @@ spec:
value: "false" value: "false"
resources: resources:
requests: requests:
cpu: "100m" cpu: "500m"
memory: "1Gi" memory: "1Gi"
limits: limits:
cpu: "2000m" cpu: "2000m"
@@ -1243,7 +1243,7 @@ metadata:
flowercore.io/runner-repo: mysql flowercore.io/runner-repo: mysql
flowercore.io/github-repo: FlowerCore.MySQL flowercore.io/github-repo: FlowerCore.MySQL
spec: spec:
replicas: 1 replicas: 2
selector: selector:
matchLabels: matchLabels:
app.kubernetes.io/name: github-runner-mysql app.kubernetes.io/name: github-runner-mysql
@@ -1328,7 +1328,7 @@ spec:
value: "false" value: "false"
resources: resources:
requests: requests:
cpu: "100m" cpu: "500m"
memory: "1Gi" memory: "1Gi"
limits: limits:
cpu: "2000m" cpu: "2000m"
@@ -1375,7 +1375,7 @@ metadata:
flowercore.io/runner-repo: kiosk-linux flowercore.io/runner-repo: kiosk-linux
flowercore.io/github-repo: FlowerCore.Kiosk.Linux flowercore.io/github-repo: FlowerCore.Kiosk.Linux
spec: spec:
replicas: 1 replicas: 2
selector: selector:
matchLabels: matchLabels:
app.kubernetes.io/name: github-runner-kiosk-linux app.kubernetes.io/name: github-runner-kiosk-linux
@@ -1460,7 +1460,7 @@ spec:
value: "false" value: "false"
resources: resources:
requests: requests:
cpu: "100m" cpu: "500m"
memory: "1Gi" memory: "1Gi"
limits: limits:
cpu: "2000m" cpu: "2000m"
@@ -1509,7 +1509,7 @@ metadata:
flowercore.io/runner-repo: marquee flowercore.io/runner-repo: marquee
flowercore.io/github-repo: FlowerCore.Marquee flowercore.io/github-repo: FlowerCore.Marquee
spec: spec:
replicas: 1 replicas: 2
selector: selector:
matchLabels: matchLabels:
app.kubernetes.io/name: github-runner-marquee app.kubernetes.io/name: github-runner-marquee
@@ -1594,7 +1594,7 @@ spec:
value: "false" value: "false"
resources: resources:
requests: requests:
cpu: "100m" cpu: "500m"
memory: "1Gi" memory: "1Gi"
limits: limits:
cpu: "2000m" cpu: "2000m"
@@ -1643,7 +1643,7 @@ metadata:
flowercore.io/runner-repo: tts-reader flowercore.io/runner-repo: tts-reader
flowercore.io/github-repo: FlowerCore.TtsReader flowercore.io/github-repo: FlowerCore.TtsReader
spec: spec:
replicas: 1 replicas: 2
selector: selector:
matchLabels: matchLabels:
app.kubernetes.io/name: github-runner-tts-reader app.kubernetes.io/name: github-runner-tts-reader
@@ -1732,7 +1732,7 @@ spec:
# symptoms surfaced. 8Gi gives ~30% headroom over peak observed. # symptoms surfaced. 8Gi gives ~30% headroom over peak observed.
resources: resources:
requests: requests:
cpu: "100m" cpu: "500m"
memory: "2Gi" memory: "2Gi"
limits: limits:
cpu: "2000m" cpu: "2000m"
@@ -1867,7 +1867,7 @@ spec:
value: "false" value: "false"
resources: resources:
requests: requests:
cpu: "100m" cpu: "500m"
memory: "1Gi" memory: "1Gi"
limits: limits:
cpu: "2000m" cpu: "2000m"
@@ -2001,7 +2001,7 @@ spec:
value: "false" value: "false"
resources: resources:
requests: requests:
cpu: "100m" cpu: "500m"
memory: "1Gi" memory: "1Gi"
limits: limits:
cpu: "2000m" cpu: "2000m"
@@ -2135,7 +2135,7 @@ spec:
value: "false" value: "false"
resources: resources:
requests: requests:
cpu: "100m" cpu: "500m"
memory: "1Gi" memory: "1Gi"
limits: limits:
cpu: "2000m" cpu: "2000m"
@@ -2269,7 +2269,7 @@ spec:
value: "false" value: "false"
resources: resources:
requests: requests:
cpu: "100m" cpu: "500m"
memory: "1Gi" memory: "1Gi"
limits: limits:
cpu: "2000m" cpu: "2000m"
@@ -2317,7 +2317,7 @@ metadata:
flowercore.io/runner-repo: remote-desktop flowercore.io/runner-repo: remote-desktop
flowercore.io/github-repo: FlowerCore.RemoteDesktop flowercore.io/github-repo: FlowerCore.RemoteDesktop
spec: spec:
replicas: 1 replicas: 2
selector: selector:
matchLabels: matchLabels:
app.kubernetes.io/name: github-runner-remote-desktop app.kubernetes.io/name: github-runner-remote-desktop
@@ -2402,7 +2402,7 @@ spec:
value: "false" value: "false"
resources: resources:
requests: requests:
cpu: "100m" cpu: "500m"
memory: "1Gi" memory: "1Gi"
limits: limits:
cpu: "2000m" cpu: "2000m"
@@ -2536,7 +2536,7 @@ spec:
value: "false" value: "false"
resources: resources:
requests: requests:
cpu: "100m" cpu: "500m"
memory: "1Gi" memory: "1Gi"
limits: limits:
cpu: "2000m" cpu: "2000m"
@@ -2584,7 +2584,7 @@ metadata:
flowercore.io/runner-repo: distribution flowercore.io/runner-repo: distribution
flowercore.io/github-repo: FlowerCore.Distribution flowercore.io/github-repo: FlowerCore.Distribution
spec: spec:
replicas: 1 replicas: 2
selector: selector:
matchLabels: matchLabels:
app.kubernetes.io/name: github-runner-distribution app.kubernetes.io/name: github-runner-distribution
@@ -2669,7 +2669,7 @@ spec:
value: "false" value: "false"
resources: resources:
requests: requests:
cpu: "100m" cpu: "500m"
memory: "1Gi" memory: "1Gi"
limits: limits:
cpu: "2000m" cpu: "2000m"
@@ -2717,7 +2717,7 @@ metadata:
flowercore.io/runner-repo: scoreboard flowercore.io/runner-repo: scoreboard
flowercore.io/github-repo: FlowerCore.Scoreboard flowercore.io/github-repo: FlowerCore.Scoreboard
spec: spec:
replicas: 1 replicas: 2
selector: selector:
matchLabels: matchLabels:
app.kubernetes.io/name: github-runner-scoreboard app.kubernetes.io/name: github-runner-scoreboard
@@ -2802,7 +2802,7 @@ spec:
value: "false" value: "false"
resources: resources:
requests: requests:
cpu: "100m" cpu: "500m"
memory: "1Gi" memory: "1Gi"
limits: limits:
cpu: "2000m" cpu: "2000m"
@@ -2850,7 +2850,7 @@ metadata:
flowercore.io/runner-repo: segment-display flowercore.io/runner-repo: segment-display
flowercore.io/github-repo: FlowerCore.SegmentDisplay flowercore.io/github-repo: FlowerCore.SegmentDisplay
spec: spec:
replicas: 1 replicas: 2
selector: selector:
matchLabels: matchLabels:
app.kubernetes.io/name: github-runner-segment-display app.kubernetes.io/name: github-runner-segment-display
@@ -2935,7 +2935,7 @@ spec:
value: "false" value: "false"
resources: resources:
requests: requests:
cpu: "100m" cpu: "500m"
memory: "1Gi" memory: "1Gi"
limits: limits:
cpu: "2000m" cpu: "2000m"
@@ -2983,7 +2983,7 @@ metadata:
flowercore.io/runner-repo: signage-contracts flowercore.io/runner-repo: signage-contracts
flowercore.io/github-repo: FlowerCore.Signage.Contracts flowercore.io/github-repo: FlowerCore.Signage.Contracts
spec: spec:
replicas: 1 replicas: 2
selector: selector:
matchLabels: matchLabels:
app.kubernetes.io/name: github-runner-signage-contracts app.kubernetes.io/name: github-runner-signage-contracts
@@ -3068,7 +3068,7 @@ spec:
value: "false" value: "false"
resources: resources:
requests: requests:
cpu: "100m" cpu: "500m"
memory: "1Gi" memory: "1Gi"
limits: limits:
cpu: "2000m" cpu: "2000m"
@@ -3116,7 +3116,7 @@ metadata:
flowercore.io/runner-repo: signal-control flowercore.io/runner-repo: signal-control
flowercore.io/github-repo: FlowerCore.SignalControl flowercore.io/github-repo: FlowerCore.SignalControl
spec: spec:
replicas: 1 replicas: 2
selector: selector:
matchLabels: matchLabels:
app.kubernetes.io/name: github-runner-signal-control app.kubernetes.io/name: github-runner-signal-control
@@ -3201,7 +3201,7 @@ spec:
value: "false" value: "false"
resources: resources:
requests: requests:
cpu: "100m" cpu: "500m"
memory: "1Gi" memory: "1Gi"
limits: limits:
cpu: "2000m" cpu: "2000m"
@@ -3335,7 +3335,7 @@ spec:
value: "false" value: "false"
resources: resources:
requests: requests:
cpu: "100m" cpu: "500m"
memory: "1Gi" memory: "1Gi"
limits: limits:
cpu: "2000m" cpu: "2000m"
@@ -3469,7 +3469,7 @@ spec:
value: "false" value: "false"
resources: resources:
requests: requests:
cpu: "100m" cpu: "500m"
memory: "1Gi" memory: "1Gi"
limits: limits:
cpu: "2000m" cpu: "2000m"
@@ -3603,7 +3603,7 @@ spec:
value: "false" value: "false"
resources: resources:
requests: requests:
cpu: "100m" cpu: "500m"
memory: "1Gi" memory: "1Gi"
limits: limits:
cpu: "2000m" cpu: "2000m"
@@ -3737,7 +3737,7 @@ spec:
value: "false" value: "false"
resources: resources:
requests: requests:
cpu: "100m" cpu: "500m"
memory: "1Gi" memory: "1Gi"
limits: limits:
cpu: "2000m" cpu: "2000m"
@@ -3871,7 +3871,7 @@ spec:
value: "false" value: "false"
resources: resources:
requests: requests:
cpu: "100m" cpu: "500m"
memory: "1Gi" memory: "1Gi"
limits: limits:
cpu: "2000m" cpu: "2000m"
@@ -3919,7 +3919,7 @@ metadata:
flowercore.io/runner-repo: pimanager flowercore.io/runner-repo: pimanager
flowercore.io/github-repo: FlowerCore.PiManager flowercore.io/github-repo: FlowerCore.PiManager
spec: spec:
replicas: 1 replicas: 2
selector: selector:
matchLabels: matchLabels:
app.kubernetes.io/name: github-runner-pimanager app.kubernetes.io/name: github-runner-pimanager
@@ -4004,7 +4004,7 @@ spec:
value: "false" value: "false"
resources: resources:
requests: requests:
cpu: "100m" cpu: "500m"
memory: "1Gi" memory: "1Gi"
limits: limits:
cpu: "2000m" cpu: "2000m"
@@ -4053,7 +4053,7 @@ metadata:
flowercore.io/runner-repo: updater flowercore.io/runner-repo: updater
flowercore.io/github-repo: FlowerCore.Updater flowercore.io/github-repo: FlowerCore.Updater
spec: spec:
replicas: 1 replicas: 2
selector: selector:
matchLabels: matchLabels:
app.kubernetes.io/name: github-runner-updater app.kubernetes.io/name: github-runner-updater
@@ -4138,419 +4138,7 @@ spec:
value: "false" value: "false"
resources: resources:
requests: requests:
cpu: "100m" cpu: "500m"
memory: "1Gi"
limits:
cpu: "2000m"
memory: "4Gi"
volumeMounts:
- name: runner-home
mountPath: /home/runner
- name: nuget-cache
mountPath: /home/runner/.nuget/packages
- name: tmp
mountPath: /tmp
livenessProbe:
exec:
command:
- /bin/sh
- -c
- "pgrep -f Runner.Listener > /dev/null"
initialDelaySeconds: 30
periodSeconds: 30
failureThreshold: 3
volumes:
- name: runner-home
emptyDir: {}
- name: nuget-cache
emptyDir:
sizeLimit: 2Gi
- name: tmp
emptyDir: {}
restartPolicy: Always
---
# Runner for FlowerCore.DeviceManagement. Two replicas use per-pod emptyDir
# caches, so backlog can drain without sharing a ReadWriteOnce PVC. Added
# 2026-05-26 morning-routine — DM had ZERO registered runners while Sprint 37
# Cx-1 PRs #20 (CI-to-Linux migration), #21 (WDAC), and #22 (AppLocker) were
# all queued indefinitely. Chicken-and-egg: the migration PRs need Linux
# runners that the migration creates.
apiVersion: apps/v1
kind: Deployment
metadata:
name: github-runner-device-management
namespace: github-runner
labels:
app.kubernetes.io/name: github-runner-device-management
app.kubernetes.io/component: runner
app.kubernetes.io/part-of: flowercore
app.kubernetes.io/managed-by: argocd
flowercore.io/created-by: argocd
flowercore.io/runner-repo: device-management
flowercore.io/github-repo: FlowerCore.DeviceManagement
spec:
# Single replica until cluster CPU pressure resolves; the fleet-wide
# request right-sizing pass is queued for a future sweep.
replicas: 1
selector:
matchLabels:
app.kubernetes.io/name: github-runner-device-management
strategy:
type: Recreate
template:
metadata:
labels:
app.kubernetes.io/name: github-runner-device-management
app.kubernetes.io/component: runner
app.kubernetes.io/part-of: flowercore
flowercore.io/created-by: argocd
flowercore.io/runner-repo: device-management
flowercore.io/github-repo: FlowerCore.DeviceManagement
spec:
serviceAccountName: github-runner
securityContext:
runAsNonRoot: true
runAsUser: 1001
runAsGroup: 1001
fsGroup: 1001
initContainers:
- name: setup-runner-home
image: localhost/fc-github-runner:v20260525-ruby3.3.11-stepca
imagePullPolicy: Never
command:
- sh
- -c
- |
set -e
mkdir -p /home/runner/.dotnet /home/runner/.nuget/packages /home/runner/.nuget/NuGet /home/runner/.cache /home/runner/_tool
if [ -d /opt/runner-toolcache/Ruby ] && [ ! -d /home/runner/_tool/Ruby ]; then
cp -a /opt/runner-toolcache/Ruby /home/runner/_tool/
fi
chown -R 1001:1001 /home/runner/.dotnet /home/runner/.nuget /home/runner/.cache /home/runner/_tool
chmod -R 755 /home/runner/.dotnet /home/runner/.nuget /home/runner/.cache /home/runner/_tool
securityContext:
runAsUser: 0
runAsNonRoot: false
volumeMounts:
- name: runner-home
mountPath: /home/runner
containers:
- name: runner
image: localhost/fc-github-runner:v20260525-ruby3.3.11-stepca
imagePullPolicy: Never
env:
- name: REPO_URL
value: "https://github.com/astoltz/FlowerCore.DeviceManagement"
- name: RUNNER_NAME_PREFIX
value: "rke2-linux-device-management"
- name: RUNNER_WORKDIR
value: "/tmp/runner/work"
- name: EPHEMERAL
value: "true"
- name: LABELS
value: "self-hosted,linux,fc-build-linux"
- name: HOME
value: "/home/runner"
- name: DOTNET_INSTALL_DIR
value: "/home/runner/.dotnet"
- name: DOTNET_CLI_TELEMETRY_OPTOUT
value: "1"
- name: DOTNET_NOLOGO
value: "1"
- name: DOTNET_GENERATE_ASPNET_CERTIFICATE
value: "false"
- name: DOTNET_CLI_HOME
value: "/home/runner"
- name: NUGET_PACKAGES
value: "/home/runner/.nuget/packages"
- name: XDG_CACHE_HOME
value: "/home/runner/.cache"
- name: RUNNER_TOOL_CACHE
value: "/home/runner/_tool"
- name: ACCESS_TOKEN
valueFrom:
secretKeyRef:
name: github-runner-token
key: credential
- name: RUN_AS_ROOT
value: "false"
resources:
# Reduced from 500m → 100m 2026-05-26 because cluster CPU
# requests were at 99% across all 3 nodes; idle runners use ~1m.
# Burst headroom preserved by limits.cpu: 2000m.
requests:
cpu: "100m"
memory: "1Gi"
limits:
cpu: "2000m"
memory: "4Gi"
volumeMounts:
- name: runner-home
mountPath: /home/runner
- name: nuget-cache
mountPath: /home/runner/.nuget/packages
- name: tmp
mountPath: /tmp
livenessProbe:
exec:
command:
- /bin/sh
- -c
- "pgrep -f Runner.Listener > /dev/null"
initialDelaySeconds: 30
periodSeconds: 30
failureThreshold: 3
volumes:
- name: runner-home
emptyDir: {}
- name: nuget-cache
emptyDir:
sizeLimit: 2Gi
- name: tmp
emptyDir: {}
restartPolicy: Always
---
# Runner for FlowerCore.AiStation.Linux. Two replicas use per-pod emptyDir
# caches. Added 2026-05-26 — #13 master-CI-fix PR was queued indefinitely
# (Linux job has no runner; Windows job remains queued until the Windows
# runner host substrate lands per Sprint 36 v2 Cl-2 / ADR-174).
apiVersion: apps/v1
kind: Deployment
metadata:
name: github-runner-aistation-linux
namespace: github-runner
labels:
app.kubernetes.io/name: github-runner-aistation-linux
app.kubernetes.io/component: runner
app.kubernetes.io/part-of: flowercore
app.kubernetes.io/managed-by: argocd
flowercore.io/created-by: argocd
flowercore.io/runner-repo: aistation-linux
flowercore.io/github-repo: FlowerCore.AiStation.Linux
spec:
# Single replica until cluster CPU pressure resolves; the fleet-wide
# request right-sizing pass is queued for a future sweep.
replicas: 1
selector:
matchLabels:
app.kubernetes.io/name: github-runner-aistation-linux
strategy:
type: Recreate
template:
metadata:
labels:
app.kubernetes.io/name: github-runner-aistation-linux
app.kubernetes.io/component: runner
app.kubernetes.io/part-of: flowercore
flowercore.io/created-by: argocd
flowercore.io/runner-repo: aistation-linux
flowercore.io/github-repo: FlowerCore.AiStation.Linux
spec:
serviceAccountName: github-runner
securityContext:
runAsNonRoot: true
runAsUser: 1001
runAsGroup: 1001
fsGroup: 1001
initContainers:
- name: setup-runner-home
image: localhost/fc-github-runner:v20260525-ruby3.3.11-stepca
imagePullPolicy: Never
command:
- sh
- -c
- |
set -e
mkdir -p /home/runner/.dotnet /home/runner/.nuget/packages /home/runner/.nuget/NuGet /home/runner/.cache /home/runner/_tool
if [ -d /opt/runner-toolcache/Ruby ] && [ ! -d /home/runner/_tool/Ruby ]; then
cp -a /opt/runner-toolcache/Ruby /home/runner/_tool/
fi
chown -R 1001:1001 /home/runner/.dotnet /home/runner/.nuget /home/runner/.cache /home/runner/_tool
chmod -R 755 /home/runner/.dotnet /home/runner/.nuget /home/runner/.cache /home/runner/_tool
securityContext:
runAsUser: 0
runAsNonRoot: false
volumeMounts:
- name: runner-home
mountPath: /home/runner
containers:
- name: runner
image: localhost/fc-github-runner:v20260525-ruby3.3.11-stepca
imagePullPolicy: Never
env:
- name: REPO_URL
value: "https://github.com/astoltz/FlowerCore.AiStation.Linux"
- name: RUNNER_NAME_PREFIX
value: "rke2-linux-aistation-linux"
- name: RUNNER_WORKDIR
value: "/tmp/runner/work"
- name: EPHEMERAL
value: "true"
- name: LABELS
value: "self-hosted,linux,fc-build-linux"
- name: HOME
value: "/home/runner"
- name: DOTNET_INSTALL_DIR
value: "/home/runner/.dotnet"
- name: DOTNET_CLI_TELEMETRY_OPTOUT
value: "1"
- name: DOTNET_NOLOGO
value: "1"
- name: DOTNET_GENERATE_ASPNET_CERTIFICATE
value: "false"
- name: DOTNET_CLI_HOME
value: "/home/runner"
- name: NUGET_PACKAGES
value: "/home/runner/.nuget/packages"
- name: XDG_CACHE_HOME
value: "/home/runner/.cache"
- name: RUNNER_TOOL_CACHE
value: "/home/runner/_tool"
- name: ACCESS_TOKEN
valueFrom:
secretKeyRef:
name: github-runner-token
key: credential
- name: RUN_AS_ROOT
value: "false"
resources:
requests:
cpu: "100m"
memory: "1Gi"
limits:
cpu: "2000m"
memory: "4Gi"
volumeMounts:
- name: runner-home
mountPath: /home/runner
- name: nuget-cache
mountPath: /home/runner/.nuget/packages
- name: tmp
mountPath: /tmp
livenessProbe:
exec:
command:
- /bin/sh
- -c
- "pgrep -f Runner.Listener > /dev/null"
initialDelaySeconds: 30
periodSeconds: 30
failureThreshold: 3
volumes:
- name: runner-home
emptyDir: {}
- name: nuget-cache
emptyDir:
sizeLimit: 2Gi
- name: tmp
emptyDir: {}
restartPolicy: Always
---
# Runner for FlowerCore.WorldBuilder. Two replicas use per-pod emptyDir
# caches. Added 2026-05-26 — #3 and #4 Linux migration PRs queued
# indefinitely with one stale offline runner registered against the repo.
apiVersion: apps/v1
kind: Deployment
metadata:
name: github-runner-worldbuilder
namespace: github-runner
labels:
app.kubernetes.io/name: github-runner-worldbuilder
app.kubernetes.io/component: runner
app.kubernetes.io/part-of: flowercore
app.kubernetes.io/managed-by: argocd
flowercore.io/created-by: argocd
flowercore.io/runner-repo: worldbuilder
flowercore.io/github-repo: FlowerCore.WorldBuilder
spec:
# Single replica until cluster CPU pressure resolves; the fleet-wide
# request right-sizing pass is queued for a future sweep.
replicas: 1
selector:
matchLabels:
app.kubernetes.io/name: github-runner-worldbuilder
strategy:
type: Recreate
template:
metadata:
labels:
app.kubernetes.io/name: github-runner-worldbuilder
app.kubernetes.io/component: runner
app.kubernetes.io/part-of: flowercore
flowercore.io/created-by: argocd
flowercore.io/runner-repo: worldbuilder
flowercore.io/github-repo: FlowerCore.WorldBuilder
spec:
serviceAccountName: github-runner
securityContext:
runAsNonRoot: true
runAsUser: 1001
runAsGroup: 1001
fsGroup: 1001
initContainers:
- name: setup-runner-home
image: localhost/fc-github-runner:v20260525-ruby3.3.11-stepca
imagePullPolicy: Never
command:
- sh
- -c
- |
set -e
mkdir -p /home/runner/.dotnet /home/runner/.nuget/packages /home/runner/.nuget/NuGet /home/runner/.cache /home/runner/_tool
if [ -d /opt/runner-toolcache/Ruby ] && [ ! -d /home/runner/_tool/Ruby ]; then
cp -a /opt/runner-toolcache/Ruby /home/runner/_tool/
fi
chown -R 1001:1001 /home/runner/.dotnet /home/runner/.nuget /home/runner/.cache /home/runner/_tool
chmod -R 755 /home/runner/.dotnet /home/runner/.nuget /home/runner/.cache /home/runner/_tool
securityContext:
runAsUser: 0
runAsNonRoot: false
volumeMounts:
- name: runner-home
mountPath: /home/runner
containers:
- name: runner
image: localhost/fc-github-runner:v20260525-ruby3.3.11-stepca
imagePullPolicy: Never
env:
- name: REPO_URL
value: "https://github.com/astoltz/FlowerCore.WorldBuilder"
- name: RUNNER_NAME_PREFIX
value: "rke2-linux-worldbuilder"
- name: RUNNER_WORKDIR
value: "/tmp/runner/work"
- name: EPHEMERAL
value: "true"
- name: LABELS
value: "self-hosted,linux,fc-build-linux"
- name: HOME
value: "/home/runner"
- name: DOTNET_INSTALL_DIR
value: "/home/runner/.dotnet"
- name: DOTNET_CLI_TELEMETRY_OPTOUT
value: "1"
- name: DOTNET_NOLOGO
value: "1"
- name: DOTNET_GENERATE_ASPNET_CERTIFICATE
value: "false"
- name: DOTNET_CLI_HOME
value: "/home/runner"
- name: NUGET_PACKAGES
value: "/home/runner/.nuget/packages"
- name: XDG_CACHE_HOME
value: "/home/runner/.cache"
- name: RUNNER_TOOL_CACHE
value: "/home/runner/_tool"
- name: ACCESS_TOKEN
valueFrom:
secretKeyRef:
name: github-runner-token
key: credential
- name: RUN_AS_ROOT
value: "false"
resources:
requests:
cpu: "100m"
memory: "1Gi" memory: "1Gi"
limits: limits:
cpu: "2000m" cpu: "2000m"
@@ -4587,6 +4175,3 @@ spec:
# Common as the only PVC-backed runner at replicas: 1. Any future multi-replica # Common as the only PVC-backed runner at replicas: 1. Any future multi-replica
# runner must use per-pod emptyDir caches, not a shared ReadWriteOnce PVC. # runner must use per-pod emptyDir caches, not a shared ReadWriteOnce PVC.
# 2026-05-25: PiManager added (was missed in the Sprint 32 long-tail sweep). # 2026-05-25: PiManager added (was missed in the Sprint 32 long-tail sweep).
# 2026-05-26: Updater + DeviceManagement + AiStation.Linux + WorldBuilder
# added by the morning-routine sweep — those repos had had ZERO online Linux
# PR-CI capacity, blocking the Sprint 37 Cx-1 Linux-CI-migration PRs.

View File

@@ -46,7 +46,7 @@ spec:
spec: spec:
containers: containers:
- name: intranet-web - name: intranet-web
image: localhost/fc-intranet-web:v20260531-ttsreader-bridge image: localhost/fc-intranet-web:v20260508-brochure-w1
imagePullPolicy: Never imagePullPolicy: Never
ports: ports:
- containerPort: 5300 - containerPort: 5300

View File

@@ -25,7 +25,7 @@ metadata:
role: github-actions-runner role: github-actions-runner
flowercore.io/managed-by: bluejay-infra flowercore.io/managed-by: bluejay-infra
spec: spec:
runStrategy: Halted runStrategy: Always
template: template:
metadata: metadata:
labels: labels:

View File

@@ -207,13 +207,20 @@ spec:
- port: 993 - port: 993
targetPort: 993 targetPort: 993
name: imaps name: imaps
# --- mail-tls Certificate REMOVED 2026-06-01 --- ---
# mail-tls is now managed OUTSIDE cert-manager: issued from step-ca's JWK 'admin' # TLS Certificate via cert-manager
# provisioner and auto-renewed by a systemd timer on noc1 (step ca renew), which apiVersion: cert-manager.io/v1
# writes the mail-tls secret directly. step-ca-acme only has an HTTP-01 (Traefik) kind: Certificate
# solver, but mail.iamworkin.lan must resolve to the dedicated MetalLB IP 10.0.56.202 metadata:
# (SMTP/IMAP), so HTTP-01 cannot validate. Do NOT re-add a cert-manager Certificate name: mail-tls
# here unless a DNS-01 solver is deployed for step-ca-acme. namespace: mail
spec:
secretName: mail-tls
issuerRef:
name: step-ca-acme
kind: ClusterIssuer
dnsNames:
- mail.iamworkin.lan
--- ---
# Traefik IngressRoute - Webmail placeholder # Traefik IngressRoute - Webmail placeholder
apiVersion: traefik.io/v1alpha1 apiVersion: traefik.io/v1alpha1

View File

@@ -1,260 +0,0 @@
# Grafana dashboard ConfigMap for FlowerCore.SignalControl on pirelay.
#
# The Grafana Deployment in noc-monitoring.yaml mounts this ConfigMap at
# /var/lib/grafana/dashboards/signalcontrol. The paired Prometheus jobs are:
# - signalcontrol-pi-app: 10.0.58.113:5200 /metrics/prometheus
# - edge-nodes: 10.0.58.113:9100 with instance="pirelay"
---
apiVersion: v1
kind: ConfigMap
metadata:
name: grafana-dashboard-signalcontrol
namespace: monitoring
data:
signalcontrol.json: |
{
"annotations": { "list": [] },
"editable": true,
"fiscalYearStartMonth": 0,
"graphTooltip": 0,
"id": null,
"links": [],
"panels": [
{
"datasource": { "type": "prometheus", "uid": "prometheus" },
"fieldConfig": {
"defaults": {
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{ "color": "red", "value": null },
{ "color": "green", "value": 1 }
]
},
"unit": "short"
},
"overrides": []
},
"gridPos": { "h": 5, "w": 6, "x": 0, "y": 0 },
"id": 1,
"options": {
"colorMode": "value",
"graphMode": "none",
"justifyMode": "auto",
"orientation": "auto",
"reduceOptions": { "calcs": [ "lastNotNull" ], "fields": "", "values": false },
"textMode": "auto"
},
"targets": [
{ "editorMode": "code", "expr": "up{job=\"signalcontrol-pi-app\",instance=\"pirelay\"}", "range": true, "refId": "A" }
],
"title": "SignalControl App Up",
"type": "stat"
},
{
"datasource": { "type": "prometheus", "uid": "prometheus" },
"fieldConfig": {
"defaults": {
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{ "color": "red", "value": null },
{ "color": "green", "value": 1 }
]
},
"unit": "short"
},
"overrides": []
},
"gridPos": { "h": 5, "w": 6, "x": 6, "y": 0 },
"id": 2,
"options": {
"colorMode": "value",
"graphMode": "none",
"justifyMode": "auto",
"orientation": "auto",
"reduceOptions": { "calcs": [ "lastNotNull" ], "fields": "", "values": false },
"textMode": "auto"
},
"targets": [
{ "editorMode": "code", "expr": "up{job=\"edge-nodes\",instance=\"pirelay\"}", "range": true, "refId": "A" }
],
"title": "pirelay node_exporter Up",
"type": "stat"
},
{
"datasource": { "type": "prometheus", "uid": "prometheus" },
"fieldConfig": { "defaults": { "unit": "short" }, "overrides": [] },
"gridPos": { "h": 5, "w": 6, "x": 12, "y": 0 },
"id": 3,
"options": {
"colorMode": "value",
"graphMode": "area",
"justifyMode": "auto",
"orientation": "auto",
"reduceOptions": { "calcs": [ "lastNotNull" ], "fields": "", "values": false },
"textMode": "name"
},
"targets": [
{ "editorMode": "code", "expr": "signalcontrol_active_pattern{job=\"signalcontrol-pi-app\",instance=\"pirelay\"}", "legendFormat": "{{pattern}}", "range": true, "refId": "A" }
],
"title": "Active Pattern",
"type": "stat"
},
{
"datasource": { "type": "prometheus", "uid": "prometheus" },
"fieldConfig": { "defaults": { "unit": "short" }, "overrides": [] },
"gridPos": { "h": 5, "w": 6, "x": 18, "y": 0 },
"id": 4,
"options": {
"colorMode": "value",
"graphMode": "area",
"justifyMode": "auto",
"orientation": "auto",
"reduceOptions": { "calcs": [ "lastNotNull" ], "fields": "", "values": false },
"textMode": "name"
},
"targets": [
{ "editorMode": "code", "expr": "signalcontrol_phase{job=\"signalcontrol-pi-app\",instance=\"pirelay\"}", "legendFormat": "{{phase}}", "range": true, "refId": "A" }
],
"title": "Current Phase",
"type": "stat"
},
{
"datasource": { "type": "prometheus", "uid": "prometheus" },
"fieldConfig": { "defaults": { "unit": "ops" }, "overrides": [] },
"gridPos": { "h": 8, "w": 12, "x": 0, "y": 5 },
"id": 5,
"options": { "legend": { "displayMode": "table", "placement": "bottom" }, "tooltip": { "mode": "single" } },
"targets": [
{
"editorMode": "code",
"expr": "sum by (channel, state) (rate(signal_relay_writes_total{job=\"signalcontrol-pi-app\",instance=\"pirelay\"}[$__rate_interval]))",
"legendFormat": "channel {{channel}} {{state}}",
"range": true,
"refId": "A"
}
],
"title": "Relay Activations",
"type": "timeseries"
},
{
"datasource": { "type": "prometheus", "uid": "prometheus" },
"fieldConfig": { "defaults": { "unit": "ops" }, "overrides": [] },
"gridPos": { "h": 8, "w": 12, "x": 12, "y": 5 },
"id": 6,
"options": { "legend": { "displayMode": "table", "placement": "bottom" }, "tooltip": { "mode": "single" } },
"targets": [
{
"editorMode": "code",
"expr": "sum by (source, to_phase) (rate(signal_transitions_total{job=\"signalcontrol-pi-app\",instance=\"pirelay\"}[$__rate_interval]))",
"legendFormat": "{{source}} -> {{to_phase}}",
"range": true,
"refId": "A"
}
],
"title": "Phase Dwell / Transitions",
"type": "timeseries"
},
{
"datasource": { "type": "prometheus", "uid": "prometheus" },
"fieldConfig": { "defaults": { "unit": "short" }, "overrides": [] },
"gridPos": { "h": 8, "w": 12, "x": 0, "y": 13 },
"id": 7,
"options": { "legend": { "displayMode": "table", "placement": "bottom" }, "tooltip": { "mode": "single" } },
"targets": [
{
"editorMode": "code",
"expr": "sum by (action) (increase(signal_schedule_fires_total{job=\"signalcontrol-pi-app\",instance=\"pirelay\"}[24h]))",
"legendFormat": "{{action}}",
"range": true,
"refId": "A"
},
{
"editorMode": "code",
"expr": "sum by (from_pattern, to_pattern) (increase(flowercore_signalcontrol_pattern_switches_total{job=\"signalcontrol-pi-app\",instance=\"pirelay\"}[24h]))",
"legendFormat": "{{from_pattern}} -> {{to_pattern}}",
"range": true,
"refId": "B"
}
],
"title": "Schedule Fires and Pattern Switches",
"type": "timeseries"
},
{
"datasource": { "type": "prometheus", "uid": "prometheus" },
"fieldConfig": { "defaults": { "unit": "percentunit" }, "overrides": [] },
"gridPos": { "h": 8, "w": 12, "x": 12, "y": 13 },
"id": 8,
"options": { "legend": { "displayMode": "table", "placement": "bottom" }, "tooltip": { "mode": "single" } },
"targets": [
{
"editorMode": "code",
"expr": "1 - avg by (instance) (rate(node_cpu_seconds_total{job=\"edge-nodes\",instance=\"pirelay\",mode=\"idle\"}[$__rate_interval]))",
"legendFormat": "CPU",
"range": true,
"refId": "A"
},
{
"editorMode": "code",
"expr": "1 - (node_memory_MemAvailable_bytes{job=\"edge-nodes\",instance=\"pirelay\"} / node_memory_MemTotal_bytes{job=\"edge-nodes\",instance=\"pirelay\"})",
"legendFormat": "Memory",
"range": true,
"refId": "B"
}
],
"title": "pirelay Host Utilization",
"type": "timeseries"
},
{
"datasource": { "type": "prometheus", "uid": "prometheus" },
"fieldConfig": { "defaults": { "unit": "short" }, "overrides": [] },
"gridPos": { "h": 6, "w": 12, "x": 0, "y": 21 },
"id": 9,
"options": {
"colorMode": "value",
"graphMode": "area",
"justifyMode": "auto",
"orientation": "auto",
"reduceOptions": { "calcs": [ "lastNotNull" ], "fields": "", "values": false },
"textMode": "auto"
},
"targets": [
{ "editorMode": "code", "expr": "signalcontrol_screen_saver_enabled{job=\"signalcontrol-pi-app\",instance=\"pirelay\"}", "range": true, "refId": "A" }
],
"title": "Screen-saver Enabled",
"type": "stat"
},
{
"datasource": { "type": "prometheus", "uid": "prometheus" },
"fieldConfig": { "defaults": { "unit": "short" }, "overrides": [] },
"gridPos": { "h": 6, "w": 12, "x": 12, "y": 21 },
"id": 10,
"options": {
"colorMode": "value",
"graphMode": "area",
"justifyMode": "auto",
"orientation": "auto",
"reduceOptions": { "calcs": [ "lastNotNull" ], "fields": "", "values": false },
"textMode": "name"
},
"targets": [
{ "editorMode": "code", "expr": "signalcontrol_animation_active{job=\"signalcontrol-pi-app\",instance=\"pirelay\"}", "legendFormat": "{{planner}}", "range": true, "refId": "A" }
],
"title": "Screen-saver / Animation Engaged",
"type": "stat"
}
],
"refresh": "30s",
"schemaVersion": 39,
"style": "dark",
"tags": [ "flowercore", "signalcontrol", "pirelay" ],
"templating": { "list": [] },
"time": { "from": "now-24h", "to": "now" },
"timezone": "browser",
"title": "FlowerCore SignalControl",
"uid": "flowercore-signalcontrol",
"version": 1
}

View File

@@ -230,19 +230,6 @@ data:
vlan: "home" vlan: "home"
device: "pi3-ks0212" device: "pi3-ks0212"
# SignalControl Pi-edition app metrics (pirelay / signal-a)
- job_name: "signalcontrol-pi-app"
scrape_interval: 15s
metrics_path: /metrics/prometheus
static_configs:
- targets: ["10.0.58.113:5200"]
labels:
instance: "pirelay"
host: "signal-a.iamworkin.lan"
service: "signalcontrol-pi"
vlan: "home"
device: "pi3-ks0212"
# Epson ET-3750 EcoTank Printer SNMP # Epson ET-3750 EcoTank Printer SNMP
- job_name: "snmp-printer" - job_name: "snmp-printer"
scrape_interval: 5m scrape_interval: 5m
@@ -4064,9 +4051,6 @@ spec:
- name: dashboards-remotedesktop - name: dashboards-remotedesktop
mountPath: /var/lib/grafana/dashboards/remotedesktop mountPath: /var/lib/grafana/dashboards/remotedesktop
readOnly: true readOnly: true
- name: dashboards-signalcontrol
mountPath: /var/lib/grafana/dashboards/signalcontrol
readOnly: true
- name: datasource-provisioning - name: datasource-provisioning
mountPath: /etc/grafana/provisioning/datasources mountPath: /etc/grafana/provisioning/datasources
readOnly: true readOnly: true
@@ -4120,9 +4104,6 @@ spec:
- name: dashboards-remotedesktop - name: dashboards-remotedesktop
configMap: configMap:
name: grafana-dashboard-remotedesktop name: grafana-dashboard-remotedesktop
- name: dashboards-signalcontrol
configMap:
name: grafana-dashboard-signalcontrol
- name: datasource-provisioning - name: datasource-provisioning
configMap: configMap:
name: grafana-datasource-provisioning name: grafana-datasource-provisioning

View File

@@ -1,51 +0,0 @@
using FluentAssertions;
using Xunit;
namespace BluejayInfraLint.Tests;
[Trait("Category", "Unit")]
public sealed class SignalControlPlatformManifestTests
{
private static readonly string Root = ManifestInventory.Load().BluejayRoot;
[Fact]
public void Monitoring_PrometheusScrapesSignalControlPiAppAndPirelayNodeExporter()
{
var monitoring = File.ReadAllText(Path.Combine(Root, "apps", "monitoring", "noc-monitoring.yaml"));
monitoring.Should().Contain("job_name: \"signalcontrol-pi-app\"");
monitoring.Should().Contain("metrics_path: /metrics/prometheus");
monitoring.Should().Contain("targets: [\"10.0.58.113:5200\"]");
monitoring.Should().Contain("host: \"signal-a.iamworkin.lan\"");
monitoring.Should().Contain("targets: [\"10.0.58.113:9100\"]");
monitoring.Should().Contain("instance: \"pirelay\"");
}
[Fact]
public void Monitoring_GrafanaMountsSignalControlDashboard()
{
var monitoring = File.ReadAllText(Path.Combine(Root, "apps", "monitoring", "noc-monitoring.yaml"));
var dashboard = File.ReadAllText(Path.Combine(Root, "apps", "monitoring", "grafana-dashboard-signalcontrol.yaml"));
monitoring.Should().Contain("name: dashboards-signalcontrol");
monitoring.Should().Contain("mountPath: /var/lib/grafana/dashboards/signalcontrol");
monitoring.Should().Contain("name: grafana-dashboard-signalcontrol");
dashboard.Should().Contain("\"uid\": \"flowercore-signalcontrol\"");
dashboard.Should().Contain("signalcontrol_active_pattern");
dashboard.Should().Contain("signal_relay_writes_total");
dashboard.Should().Contain("node_cpu_seconds_total");
}
[Fact]
public void FcSignalControlReadme_DocumentsMtlsTelemetryAndDefaultOffAudit()
{
var readme = File.ReadAllText(Path.Combine(Root, "apps", "fc-signalcontrol", "README.md"));
readme.Should().Contain("step-ca-agent");
readme.Should().Contain("10.0.58.113:5200");
readme.Should().Contain("10.0.58.113:9100");
readme.Should().Contain("PhysicalAudit:Enabled=false");
readme.Should().Contain("ForwardingEnabled=false");
readme.Should().Contain("Secrets, enrollment codes, private keys");
}
}