Compare commits
1 Commits
211ecbf294
...
feat/redis
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c1416c6968 |
@@ -1,26 +0,0 @@
|
|||||||
# Runtime secrets for FlowerCore.DeviceManagement.
|
|
||||||
#
|
|
||||||
# OnePasswordItem operator syncs this item into a Kubernetes Secret with the
|
|
||||||
# same name. Expected fields:
|
|
||||||
# DB-Password
|
|
||||||
# mtls-ca.pem
|
|
||||||
# mtls-client.crt
|
|
||||||
# mtls-client.key
|
|
||||||
# mtls-chain.pem
|
|
||||||
#
|
|
||||||
# Do not add literal secret values to this repo. Runtime pods consume the
|
|
||||||
# synced Secret through env vars and read-only mounts.
|
|
||||||
apiVersion: onepassword.com/v1
|
|
||||||
kind: OnePasswordItem
|
|
||||||
metadata:
|
|
||||||
name: fc-devicemgmt-runtime
|
|
||||||
namespace: fc-devicemgmt
|
|
||||||
labels:
|
|
||||||
app.kubernetes.io/name: fc-devicemgmt
|
|
||||||
app.kubernetes.io/component: secrets
|
|
||||||
app.kubernetes.io/part-of: flowercore
|
|
||||||
app.kubernetes.io/managed-by: argocd
|
|
||||||
flowercore.io/tenant-id: system
|
|
||||||
flowercore.io/created-by: bluejay-infra
|
|
||||||
spec:
|
|
||||||
itemPath: "vaults/IAmWorkin/items/FlowerCore DeviceManagement Runtime"
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
# Explicit ArgoCD Application shape for bootstrap/review.
|
|
||||||
#
|
|
||||||
# The live bluejay-infra ApplicationSet already discovers apps/* directories
|
|
||||||
# and creates this same Application name (`infra-fc-devicemgmt`) automatically.
|
|
||||||
# Keep repoURL on the internal Gitea ClusterIP URL; ArgoCD does not trust the
|
|
||||||
# external step-ca HTTPS endpoint.
|
|
||||||
apiVersion: argoproj.io/v1alpha1
|
|
||||||
kind: Application
|
|
||||||
metadata:
|
|
||||||
name: infra-fc-devicemgmt
|
|
||||||
namespace: argocd
|
|
||||||
labels:
|
|
||||||
app.kubernetes.io/name: fc-devicemgmt
|
|
||||||
app.kubernetes.io/part-of: flowercore
|
|
||||||
app.kubernetes.io/managed-by: argocd
|
|
||||||
flowercore.io/tenant-id: system
|
|
||||||
flowercore.io/created-by: bluejay-infra
|
|
||||||
spec:
|
|
||||||
project: default
|
|
||||||
source:
|
|
||||||
repoURL: http://gitea-clusterip.gitea.svc.cluster.local:3000/bluejay/bluejay-infra.git
|
|
||||||
targetRevision: main
|
|
||||||
path: apps/fc-devicemgmt
|
|
||||||
destination:
|
|
||||||
server: https://kubernetes.default.svc
|
|
||||||
namespace: fc-devicemgmt
|
|
||||||
syncPolicy:
|
|
||||||
automated:
|
|
||||||
prune: true
|
|
||||||
selfHeal: true
|
|
||||||
syncOptions:
|
|
||||||
- CreateNamespace=true
|
|
||||||
- ServerSideApply=true
|
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
# Certificate for devices.iamworkin.lan.
|
|
||||||
#
|
|
||||||
# Preflight gate: FlowerCore.DNS / pfSense must contain an explicit A record:
|
|
||||||
# devices.iamworkin.lan -> 10.0.56.200
|
|
||||||
# before this Certificate is synced. step-ca ACME cannot see the CoreDNS
|
|
||||||
# wildcard, so missing pfSense DNS produces cert-manager HTTP-01 backoff
|
|
||||||
# (feedback_pfsense_dns_required_for_acme).
|
|
||||||
apiVersion: cert-manager.io/v1
|
|
||||||
kind: Certificate
|
|
||||||
metadata:
|
|
||||||
name: fc-devicemgmt-web-tls
|
|
||||||
namespace: fc-devicemgmt
|
|
||||||
labels:
|
|
||||||
app.kubernetes.io/name: fc-devicemgmt-web
|
|
||||||
app.kubernetes.io/component: web
|
|
||||||
app.kubernetes.io/part-of: flowercore
|
|
||||||
app.kubernetes.io/managed-by: argocd
|
|
||||||
flowercore.io/tenant-id: system
|
|
||||||
flowercore.io/created-by: bluejay-infra
|
|
||||||
annotations:
|
|
||||||
flowercore.io/dns-preflight: "devices.iamworkin.lan must resolve to 10.0.56.200 before ACME sync"
|
|
||||||
spec:
|
|
||||||
secretName: fc-devicemgmt-web-tls
|
|
||||||
issuerRef:
|
|
||||||
name: step-ca-acme
|
|
||||||
kind: ClusterIssuer
|
|
||||||
dnsNames:
|
|
||||||
- devices.iamworkin.lan
|
|
||||||
duration: 720h
|
|
||||||
renewBefore: 240h
|
|
||||||
@@ -1,81 +0,0 @@
|
|||||||
apiVersion: rbac.authorization.k8s.io/v1
|
|
||||||
kind: ClusterRole
|
|
||||||
metadata:
|
|
||||||
name: fc-devicemgmt-operator
|
|
||||||
labels:
|
|
||||||
app.kubernetes.io/name: fc-devicemgmt-operator
|
|
||||||
app.kubernetes.io/component: operator
|
|
||||||
app.kubernetes.io/part-of: flowercore
|
|
||||||
app.kubernetes.io/managed-by: argocd
|
|
||||||
flowercore.io/tenant-id: system
|
|
||||||
flowercore.io/created-by: bluejay-infra
|
|
||||||
rules:
|
|
||||||
- apiGroups:
|
|
||||||
- devices.flowercore.io
|
|
||||||
resources:
|
|
||||||
- '*'
|
|
||||||
verbs:
|
|
||||||
- get
|
|
||||||
- list
|
|
||||||
- watch
|
|
||||||
- create
|
|
||||||
- update
|
|
||||||
- patch
|
|
||||||
- delete
|
|
||||||
- apiGroups:
|
|
||||||
- devices.flowercore.io
|
|
||||||
resources:
|
|
||||||
- devices/status
|
|
||||||
- devices/finalizers
|
|
||||||
- devicegroups/status
|
|
||||||
- devicegroups/finalizers
|
|
||||||
- devicepolicies/status
|
|
||||||
- devicepolicies/finalizers
|
|
||||||
- remotecommands/status
|
|
||||||
- remotecommands/finalizers
|
|
||||||
verbs:
|
|
||||||
- get
|
|
||||||
- update
|
|
||||||
- patch
|
|
||||||
- apiGroups:
|
|
||||||
- apps
|
|
||||||
resources:
|
|
||||||
- deployments
|
|
||||||
verbs:
|
|
||||||
- get
|
|
||||||
- apiGroups:
|
|
||||||
- ""
|
|
||||||
resources:
|
|
||||||
- pods
|
|
||||||
- services
|
|
||||||
- configmaps
|
|
||||||
- secrets
|
|
||||||
- events
|
|
||||||
verbs:
|
|
||||||
- get
|
|
||||||
- list
|
|
||||||
- watch
|
|
||||||
- create
|
|
||||||
- update
|
|
||||||
- patch
|
|
||||||
- delete
|
|
||||||
- apiGroups:
|
|
||||||
- batch
|
|
||||||
resources:
|
|
||||||
- jobs
|
|
||||||
verbs:
|
|
||||||
- get
|
|
||||||
- list
|
|
||||||
- watch
|
|
||||||
- create
|
|
||||||
- update
|
|
||||||
- patch
|
|
||||||
- delete
|
|
||||||
- apiGroups:
|
|
||||||
- networking.k8s.io
|
|
||||||
resources:
|
|
||||||
- networkpolicies
|
|
||||||
verbs:
|
|
||||||
- get
|
|
||||||
- list
|
|
||||||
- watch
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
apiVersion: rbac.authorization.k8s.io/v1
|
|
||||||
kind: ClusterRoleBinding
|
|
||||||
metadata:
|
|
||||||
name: fc-devicemgmt-operator
|
|
||||||
labels:
|
|
||||||
app.kubernetes.io/name: fc-devicemgmt-operator
|
|
||||||
app.kubernetes.io/component: operator
|
|
||||||
app.kubernetes.io/part-of: flowercore
|
|
||||||
app.kubernetes.io/managed-by: argocd
|
|
||||||
flowercore.io/tenant-id: system
|
|
||||||
flowercore.io/created-by: bluejay-infra
|
|
||||||
roleRef:
|
|
||||||
apiGroup: rbac.authorization.k8s.io
|
|
||||||
kind: ClusterRole
|
|
||||||
name: fc-devicemgmt-operator
|
|
||||||
subjects:
|
|
||||||
- kind: ServiceAccount
|
|
||||||
name: fc-devicemgmt-operator
|
|
||||||
namespace: fc-devicemgmt
|
|
||||||
@@ -1,109 +0,0 @@
|
|||||||
# FlowerCore.DeviceManagement Operator.
|
|
||||||
#
|
|
||||||
# KubeOps controller for devices.flowercore.io resources. Operator-created
|
|
||||||
# children must set OwnerReferences + traceability labels/annotations per
|
|
||||||
# k8s-pod-ownership-and-traceability-standard.md. RBAC below grants
|
|
||||||
# apps/deployments/get so the process can resolve its own Deployment UID.
|
|
||||||
apiVersion: apps/v1
|
|
||||||
kind: Deployment
|
|
||||||
metadata:
|
|
||||||
name: fc-devicemgmt-operator
|
|
||||||
namespace: fc-devicemgmt
|
|
||||||
labels:
|
|
||||||
app: fc-devicemgmt-operator
|
|
||||||
app.kubernetes.io/name: fc-devicemgmt-operator
|
|
||||||
app.kubernetes.io/component: operator
|
|
||||||
app.kubernetes.io/part-of: flowercore
|
|
||||||
app.kubernetes.io/managed-by: argocd
|
|
||||||
flowercore.io/tenant-id: system
|
|
||||||
flowercore.io/created-by: bluejay-infra
|
|
||||||
annotations:
|
|
||||||
flowercore.io/traceability-standard: k8s-pod-ownership-and-traceability-standard
|
|
||||||
spec:
|
|
||||||
replicas: 1
|
|
||||||
revisionHistoryLimit: 3
|
|
||||||
selector:
|
|
||||||
matchLabels:
|
|
||||||
app: fc-devicemgmt-operator
|
|
||||||
template:
|
|
||||||
metadata:
|
|
||||||
labels:
|
|
||||||
app: fc-devicemgmt-operator
|
|
||||||
app.kubernetes.io/name: fc-devicemgmt-operator
|
|
||||||
app.kubernetes.io/component: operator
|
|
||||||
app.kubernetes.io/part-of: flowercore
|
|
||||||
app.kubernetes.io/managed-by: argocd
|
|
||||||
flowercore.io/tenant-id: system
|
|
||||||
flowercore.io/created-by: bluejay-infra
|
|
||||||
annotations:
|
|
||||||
prometheus.io/scrape: "true"
|
|
||||||
prometheus.io/port: "8080"
|
|
||||||
prometheus.io/path: "/metrics"
|
|
||||||
flowercore.io/audit-trace-id: "runtime-activity-trace"
|
|
||||||
spec:
|
|
||||||
serviceAccountName: fc-devicemgmt-operator
|
|
||||||
securityContext:
|
|
||||||
fsGroup: 1654
|
|
||||||
fsGroupChangePolicy: OnRootMismatch
|
|
||||||
containers:
|
|
||||||
- name: operator
|
|
||||||
image: localhost/fc-devicemgmt-operator:v20260512-cx5
|
|
||||||
imagePullPolicy: Never
|
|
||||||
ports:
|
|
||||||
- name: metrics
|
|
||||||
containerPort: 8080
|
|
||||||
env:
|
|
||||||
- name: ASPNETCORE_ENVIRONMENT
|
|
||||||
value: "Production"
|
|
||||||
- name: DOTNET_SYSTEM_GLOBALIZATION_INVARIANT
|
|
||||||
value: "false"
|
|
||||||
- name: POD_NAME
|
|
||||||
valueFrom:
|
|
||||||
fieldRef:
|
|
||||||
fieldPath: metadata.name
|
|
||||||
- name: POD_NAMESPACE
|
|
||||||
valueFrom:
|
|
||||||
fieldRef:
|
|
||||||
fieldPath: metadata.namespace
|
|
||||||
- name: FLOWERCORE_KUBERNETES_OWNER_DEPLOYMENT
|
|
||||||
value: "fc-devicemgmt-operator"
|
|
||||||
- name: FlowerCore__Service__Name
|
|
||||||
value: "FlowerCore.DeviceManagement.Operator"
|
|
||||||
- name: FlowerCore__DeviceManagement__DefaultTenantId
|
|
||||||
value: "system"
|
|
||||||
resources:
|
|
||||||
requests:
|
|
||||||
cpu: 50m
|
|
||||||
memory: 128Mi
|
|
||||||
limits:
|
|
||||||
cpu: 500m
|
|
||||||
memory: 512Mi
|
|
||||||
readinessProbe:
|
|
||||||
tcpSocket:
|
|
||||||
port: 8080
|
|
||||||
initialDelaySeconds: 5
|
|
||||||
periodSeconds: 10
|
|
||||||
livenessProbe:
|
|
||||||
tcpSocket:
|
|
||||||
port: 8080
|
|
||||||
initialDelaySeconds: 20
|
|
||||||
periodSeconds: 30
|
|
||||||
securityContext:
|
|
||||||
runAsNonRoot: true
|
|
||||||
runAsUser: 1654
|
|
||||||
runAsGroup: 1654
|
|
||||||
allowPrivilegeEscalation: false
|
|
||||||
readOnlyRootFilesystem: true
|
|
||||||
capabilities:
|
|
||||||
drop:
|
|
||||||
- ALL
|
|
||||||
volumeMounts:
|
|
||||||
- name: tmp
|
|
||||||
mountPath: /tmp
|
|
||||||
- name: logs
|
|
||||||
mountPath: /app/logs
|
|
||||||
volumes:
|
|
||||||
- name: tmp
|
|
||||||
emptyDir: {}
|
|
||||||
- name: logs
|
|
||||||
emptyDir: {}
|
|
||||||
@@ -1,135 +0,0 @@
|
|||||||
# FlowerCore.DeviceManagement Web.
|
|
||||||
#
|
|
||||||
# Source repo is expected to ship FlowerCore.DeviceManagement.Web in a later
|
|
||||||
# Sprint 9+ lane. This manifest is static-valid without requiring the image to
|
|
||||||
# exist yet; import localhost/fc-devicemgmt-web:<tag> to all schedulable RKE2
|
|
||||||
# nodes before letting ArgoCD sync a live rollout.
|
|
||||||
apiVersion: apps/v1
|
|
||||||
kind: Deployment
|
|
||||||
metadata:
|
|
||||||
name: fc-devicemgmt-web
|
|
||||||
namespace: fc-devicemgmt
|
|
||||||
labels:
|
|
||||||
app: fc-devicemgmt-web
|
|
||||||
app.kubernetes.io/name: fc-devicemgmt-web
|
|
||||||
app.kubernetes.io/component: web
|
|
||||||
app.kubernetes.io/part-of: flowercore
|
|
||||||
app.kubernetes.io/managed-by: argocd
|
|
||||||
flowercore.io/tenant-id: system
|
|
||||||
flowercore.io/created-by: bluejay-infra
|
|
||||||
annotations:
|
|
||||||
flowercore.io/traceability-standard: k8s-pod-ownership-and-traceability-standard
|
|
||||||
spec:
|
|
||||||
replicas: 2
|
|
||||||
revisionHistoryLimit: 3
|
|
||||||
selector:
|
|
||||||
matchLabels:
|
|
||||||
app: fc-devicemgmt-web
|
|
||||||
template:
|
|
||||||
metadata:
|
|
||||||
labels:
|
|
||||||
app: fc-devicemgmt-web
|
|
||||||
app.kubernetes.io/name: fc-devicemgmt-web
|
|
||||||
app.kubernetes.io/component: web
|
|
||||||
app.kubernetes.io/part-of: flowercore
|
|
||||||
app.kubernetes.io/managed-by: argocd
|
|
||||||
flowercore.io/tenant-id: system
|
|
||||||
flowercore.io/created-by: bluejay-infra
|
|
||||||
annotations:
|
|
||||||
prometheus.io/scrape: "true"
|
|
||||||
prometheus.io/port: "8080"
|
|
||||||
prometheus.io/path: "/metrics"
|
|
||||||
flowercore.io/audit-trace-id: "runtime-activity-trace"
|
|
||||||
spec:
|
|
||||||
securityContext:
|
|
||||||
fsGroup: 1654
|
|
||||||
fsGroupChangePolicy: OnRootMismatch
|
|
||||||
containers:
|
|
||||||
- name: web
|
|
||||||
image: localhost/fc-devicemgmt-web:v20260512-cx5
|
|
||||||
imagePullPolicy: Never
|
|
||||||
ports:
|
|
||||||
- name: http
|
|
||||||
containerPort: 8080
|
|
||||||
env:
|
|
||||||
- name: ASPNETCORE_URLS
|
|
||||||
value: "http://+:8080"
|
|
||||||
- name: ASPNETCORE_ENVIRONMENT
|
|
||||||
value: "Production"
|
|
||||||
- name: DOTNET_SYSTEM_GLOBALIZATION_INVARIANT
|
|
||||||
value: "false"
|
|
||||||
- name: FlowerCore__Service__Name
|
|
||||||
value: "FlowerCore.DeviceManagement.Web"
|
|
||||||
- name: FlowerCore__DeviceManagement__DefaultTenantId
|
|
||||||
value: "system"
|
|
||||||
- name: FlowerCore__Database__Provider
|
|
||||||
value: "MySql"
|
|
||||||
- name: FlowerCore__Database__Host
|
|
||||||
value: "mysql.fc-mysql.svc"
|
|
||||||
- name: FlowerCore__Database__Database
|
|
||||||
value: "flowercore_devicemgmt"
|
|
||||||
- name: FlowerCore__Database__User
|
|
||||||
value: "fc_devicemgmt"
|
|
||||||
- name: FlowerCore__Database__Password
|
|
||||||
valueFrom:
|
|
||||||
secretKeyRef:
|
|
||||||
name: fc-devicemgmt-runtime
|
|
||||||
key: DB-Password
|
|
||||||
- name: FlowerCore__DeviceManagement__AgentMtls__CaPath
|
|
||||||
value: "/secrets/devicemgmt-mtls/mtls-ca.pem"
|
|
||||||
- name: FlowerCore__DeviceManagement__AgentMtls__ClientCertificatePath
|
|
||||||
value: "/secrets/devicemgmt-mtls/mtls-client.crt"
|
|
||||||
- name: FlowerCore__DeviceManagement__AgentMtls__ClientKeyPath
|
|
||||||
value: "/secrets/devicemgmt-mtls/mtls-client.key"
|
|
||||||
- name: FlowerCore__EventBus__Redis__Configuration
|
|
||||||
value: "redis.fc-redis.svc:6379"
|
|
||||||
resources:
|
|
||||||
requests:
|
|
||||||
cpu: 100m
|
|
||||||
memory: 256Mi
|
|
||||||
limits:
|
|
||||||
cpu: 1000m
|
|
||||||
memory: 768Mi
|
|
||||||
startupProbe:
|
|
||||||
tcpSocket:
|
|
||||||
port: 8080
|
|
||||||
initialDelaySeconds: 5
|
|
||||||
periodSeconds: 5
|
|
||||||
failureThreshold: 30
|
|
||||||
readinessProbe:
|
|
||||||
tcpSocket:
|
|
||||||
port: 8080
|
|
||||||
periodSeconds: 10
|
|
||||||
failureThreshold: 3
|
|
||||||
livenessProbe:
|
|
||||||
tcpSocket:
|
|
||||||
port: 8080
|
|
||||||
initialDelaySeconds: 30
|
|
||||||
periodSeconds: 30
|
|
||||||
failureThreshold: 3
|
|
||||||
securityContext:
|
|
||||||
runAsNonRoot: true
|
|
||||||
runAsUser: 1654
|
|
||||||
runAsGroup: 1654
|
|
||||||
allowPrivilegeEscalation: false
|
|
||||||
readOnlyRootFilesystem: true
|
|
||||||
capabilities:
|
|
||||||
drop:
|
|
||||||
- ALL
|
|
||||||
volumeMounts:
|
|
||||||
- name: tmp
|
|
||||||
mountPath: /tmp
|
|
||||||
- name: logs
|
|
||||||
mountPath: /app/logs
|
|
||||||
- name: devicemgmt-mtls
|
|
||||||
mountPath: /secrets/devicemgmt-mtls
|
|
||||||
readOnly: true
|
|
||||||
volumes:
|
|
||||||
- name: tmp
|
|
||||||
emptyDir: {}
|
|
||||||
- name: logs
|
|
||||||
emptyDir: {}
|
|
||||||
- name: devicemgmt-mtls
|
|
||||||
secret:
|
|
||||||
secretName: fc-devicemgmt-runtime
|
|
||||||
defaultMode: 0400
|
|
||||||
@@ -1,55 +0,0 @@
|
|||||||
# LAN ingress for FlowerCore.DeviceManagement Web.
|
|
||||||
#
|
|
||||||
# RKE2 Traefik has no built-in ACME resolver configured. Keep TLS certificate
|
|
||||||
# ownership in cert-manager Certificate/fc-devicemgmt-web-tls.
|
|
||||||
apiVersion: traefik.io/v1alpha1
|
|
||||||
kind: IngressRoute
|
|
||||||
metadata:
|
|
||||||
name: fc-devicemgmt-web
|
|
||||||
namespace: fc-devicemgmt
|
|
||||||
labels:
|
|
||||||
app.kubernetes.io/name: fc-devicemgmt-web
|
|
||||||
app.kubernetes.io/component: web
|
|
||||||
app.kubernetes.io/part-of: flowercore
|
|
||||||
app.kubernetes.io/managed-by: argocd
|
|
||||||
flowercore.io/tenant-id: system
|
|
||||||
flowercore.io/created-by: bluejay-infra
|
|
||||||
spec:
|
|
||||||
entryPoints:
|
|
||||||
- websecure
|
|
||||||
routes:
|
|
||||||
- match: Host(`devices.iamworkin.lan`)
|
|
||||||
kind: Rule
|
|
||||||
services:
|
|
||||||
- name: fc-devicemgmt-web
|
|
||||||
port: 80
|
|
||||||
tls:
|
|
||||||
secretName: fc-devicemgmt-web-tls
|
|
||||||
|
|
||||||
# Future public agent/update host gate (OFF by default):
|
|
||||||
#
|
|
||||||
# Do not enable `update.flowercore.io` here until Authentik OIDC Q-OIDC-1
|
|
||||||
# resolves the public-device-management auth model and route ownership with
|
|
||||||
# UpdateCenter. When enabled, use a separate public IngressRoute with an
|
|
||||||
# explicit Method allowlist, public-host auth middleware, and public TLS
|
|
||||||
# certificate strategy. Leaving this as comments keeps ArgoCD from stealing
|
|
||||||
# live UpdateCenter traffic.
|
|
||||||
#
|
|
||||||
# apiVersion: traefik.io/v1alpha1
|
|
||||||
# kind: IngressRoute
|
|
||||||
# metadata:
|
|
||||||
# name: fc-devicemgmt-web-public
|
|
||||||
# namespace: fc-devicemgmt
|
|
||||||
# annotations:
|
|
||||||
# flowercore.io/public-host-gate: "disabled-until-Q-OIDC-1"
|
|
||||||
# spec:
|
|
||||||
# entryPoints:
|
|
||||||
# - websecure
|
|
||||||
# routes:
|
|
||||||
# - match: Host(`update.flowercore.io`) && (Method(`GET`) || Method(`HEAD`) || Method(`POST`) || Method(`OPTIONS`))
|
|
||||||
# kind: Rule
|
|
||||||
# services:
|
|
||||||
# - name: fc-devicemgmt-web
|
|
||||||
# port: 80
|
|
||||||
# tls:
|
|
||||||
# secretName: fc-devicemgmt-public-tls
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
# FlowerCore.DeviceManagement namespace.
|
|
||||||
#
|
|
||||||
# ArgoCD discovers this directory as Application `infra-fc-devicemgmt`.
|
|
||||||
apiVersion: v1
|
|
||||||
kind: Namespace
|
|
||||||
metadata:
|
|
||||||
name: fc-devicemgmt
|
|
||||||
labels:
|
|
||||||
app.kubernetes.io/name: fc-devicemgmt
|
|
||||||
app.kubernetes.io/part-of: flowercore
|
|
||||||
app.kubernetes.io/managed-by: argocd
|
|
||||||
flowercore.io/tenant-id: system
|
|
||||||
flowercore.io/created-by: bluejay-infra
|
|
||||||
@@ -1,224 +0,0 @@
|
|||||||
# FlowerCore.DeviceManagement NetworkPolicies.
|
|
||||||
#
|
|
||||||
# NetworkPolicies belong in bluejay-infra so ArgoCD owns rebuild state.
|
|
||||||
# Rules include Traefik post-DNAT backend ports per
|
|
||||||
# feedback_netpol_dnat_backend_port and Synology NFS egress for the requested
|
|
||||||
# cold-tier / future artifact path.
|
|
||||||
---
|
|
||||||
apiVersion: networking.k8s.io/v1
|
|
||||||
kind: NetworkPolicy
|
|
||||||
metadata:
|
|
||||||
name: fc-devicemgmt-web-isolation
|
|
||||||
namespace: fc-devicemgmt
|
|
||||||
labels:
|
|
||||||
app.kubernetes.io/name: fc-devicemgmt-web
|
|
||||||
app.kubernetes.io/component: web
|
|
||||||
app.kubernetes.io/part-of: flowercore
|
|
||||||
app.kubernetes.io/managed-by: argocd
|
|
||||||
flowercore.io/tenant-id: system
|
|
||||||
flowercore.io/created-by: bluejay-infra
|
|
||||||
spec:
|
|
||||||
podSelector:
|
|
||||||
matchLabels:
|
|
||||||
app: fc-devicemgmt-web
|
|
||||||
policyTypes:
|
|
||||||
- Ingress
|
|
||||||
- Egress
|
|
||||||
ingress:
|
|
||||||
# LAN edge: only cluster Traefik should reach the Web pod for
|
|
||||||
# devices.iamworkin.lan.
|
|
||||||
- from:
|
|
||||||
- namespaceSelector:
|
|
||||||
matchLabels:
|
|
||||||
kubernetes.io/metadata.name: traefik-system
|
|
||||||
podSelector:
|
|
||||||
matchLabels:
|
|
||||||
app.kubernetes.io/name: traefik
|
|
||||||
ports:
|
|
||||||
- port: 8080
|
|
||||||
protocol: TCP
|
|
||||||
# Direct LAN diagnostics are allowed only from FlowerCore LAN/VPN ranges.
|
|
||||||
- from:
|
|
||||||
- ipBlock:
|
|
||||||
cidr: 10.0.56.0/24
|
|
||||||
- ipBlock:
|
|
||||||
cidr: 10.0.57.0/24
|
|
||||||
- ipBlock:
|
|
||||||
cidr: 10.0.58.0/24
|
|
||||||
- ipBlock:
|
|
||||||
cidr: 10.0.68.0/27
|
|
||||||
ports:
|
|
||||||
- port: 8080
|
|
||||||
protocol: TCP
|
|
||||||
egress:
|
|
||||||
# CoreDNS.
|
|
||||||
- to:
|
|
||||||
- namespaceSelector:
|
|
||||||
matchLabels:
|
|
||||||
kubernetes.io/metadata.name: kube-system
|
|
||||||
podSelector:
|
|
||||||
matchLabels:
|
|
||||||
k8s-app: kube-dns
|
|
||||||
ports:
|
|
||||||
- port: 53
|
|
||||||
protocol: UDP
|
|
||||||
- port: 53
|
|
||||||
protocol: TCP
|
|
||||||
# Database namespace.
|
|
||||||
- to:
|
|
||||||
- namespaceSelector:
|
|
||||||
matchLabels:
|
|
||||||
kubernetes.io/metadata.name: fc-mysql
|
|
||||||
ports:
|
|
||||||
- port: 3306
|
|
||||||
protocol: TCP
|
|
||||||
# Redis backplane for multi-replica SignalR / live-status fan-out.
|
|
||||||
- to:
|
|
||||||
- namespaceSelector:
|
|
||||||
matchLabels:
|
|
||||||
kubernetes.io/metadata.name: fc-redis
|
|
||||||
ports:
|
|
||||||
- port: 6379
|
|
||||||
protocol: TCP
|
|
||||||
# Traefik VIP / in-cluster Traefik for self-callbacks and public URL
|
|
||||||
# generation tests. Include post-DNAT backend ports 8443 + 8080.
|
|
||||||
- to:
|
|
||||||
- ipBlock:
|
|
||||||
cidr: 10.0.56.200/32
|
|
||||||
- namespaceSelector:
|
|
||||||
matchLabels:
|
|
||||||
kubernetes.io/metadata.name: traefik-system
|
|
||||||
podSelector:
|
|
||||||
matchLabels:
|
|
||||||
app.kubernetes.io/name: traefik
|
|
||||||
ports:
|
|
||||||
- port: 80
|
|
||||||
protocol: TCP
|
|
||||||
- port: 443
|
|
||||||
protocol: TCP
|
|
||||||
- port: 8080
|
|
||||||
protocol: TCP
|
|
||||||
- port: 8443
|
|
||||||
protocol: TCP
|
|
||||||
# Agent egress: LAN/VPN devices may run DM Agent in Generic, Kiosk, Pi,
|
|
||||||
# ThinClient, or Server mode. Keep this private-range only.
|
|
||||||
- to:
|
|
||||||
- ipBlock:
|
|
||||||
cidr: 10.0.56.0/24
|
|
||||||
- ipBlock:
|
|
||||||
cidr: 10.0.57.0/24
|
|
||||||
- ipBlock:
|
|
||||||
cidr: 10.0.58.0/24
|
|
||||||
- ipBlock:
|
|
||||||
cidr: 10.0.68.0/27
|
|
||||||
ports:
|
|
||||||
- port: 80
|
|
||||||
protocol: TCP
|
|
||||||
- port: 443
|
|
||||||
protocol: TCP
|
|
||||||
- port: 8080
|
|
||||||
protocol: TCP
|
|
||||||
- port: 8443
|
|
||||||
protocol: TCP
|
|
||||||
- port: 5000
|
|
||||||
protocol: TCP
|
|
||||||
- port: 5001
|
|
||||||
protocol: TCP
|
|
||||||
# Synology NFS cold-tier / artifact mount allowance.
|
|
||||||
- to:
|
|
||||||
- ipBlock:
|
|
||||||
cidr: 10.0.58.3/32
|
|
||||||
ports:
|
|
||||||
- port: 2049
|
|
||||||
protocol: TCP
|
|
||||||
- port: 2049
|
|
||||||
protocol: UDP
|
|
||||||
- port: 111
|
|
||||||
protocol: TCP
|
|
||||||
- port: 111
|
|
||||||
protocol: UDP
|
|
||||||
---
|
|
||||||
apiVersion: networking.k8s.io/v1
|
|
||||||
kind: NetworkPolicy
|
|
||||||
metadata:
|
|
||||||
name: fc-devicemgmt-operator-isolation
|
|
||||||
namespace: fc-devicemgmt
|
|
||||||
labels:
|
|
||||||
app.kubernetes.io/name: fc-devicemgmt-operator
|
|
||||||
app.kubernetes.io/component: operator
|
|
||||||
app.kubernetes.io/part-of: flowercore
|
|
||||||
app.kubernetes.io/managed-by: argocd
|
|
||||||
flowercore.io/tenant-id: system
|
|
||||||
flowercore.io/created-by: bluejay-infra
|
|
||||||
spec:
|
|
||||||
podSelector:
|
|
||||||
matchLabels:
|
|
||||||
app: fc-devicemgmt-operator
|
|
||||||
policyTypes:
|
|
||||||
- Ingress
|
|
||||||
- Egress
|
|
||||||
ingress:
|
|
||||||
- from:
|
|
||||||
- namespaceSelector:
|
|
||||||
matchLabels:
|
|
||||||
kubernetes.io/metadata.name: monitoring
|
|
||||||
ports:
|
|
||||||
- port: 8080
|
|
||||||
protocol: TCP
|
|
||||||
egress:
|
|
||||||
# CoreDNS.
|
|
||||||
- to:
|
|
||||||
- namespaceSelector:
|
|
||||||
matchLabels:
|
|
||||||
kubernetes.io/metadata.name: kube-system
|
|
||||||
podSelector:
|
|
||||||
matchLabels:
|
|
||||||
k8s-app: kube-dns
|
|
||||||
ports:
|
|
||||||
- port: 53
|
|
||||||
protocol: UDP
|
|
||||||
- port: 53
|
|
||||||
protocol: TCP
|
|
||||||
# Kubernetes API for KubeOps reconciliation and Deployment UID lookup.
|
|
||||||
- to: []
|
|
||||||
ports:
|
|
||||||
- port: 443
|
|
||||||
protocol: TCP
|
|
||||||
- port: 6443
|
|
||||||
protocol: TCP
|
|
||||||
# Agent egress for operator-initiated probes / fallback command dispatch.
|
|
||||||
- to:
|
|
||||||
- ipBlock:
|
|
||||||
cidr: 10.0.56.0/24
|
|
||||||
- ipBlock:
|
|
||||||
cidr: 10.0.57.0/24
|
|
||||||
- ipBlock:
|
|
||||||
cidr: 10.0.58.0/24
|
|
||||||
- ipBlock:
|
|
||||||
cidr: 10.0.68.0/27
|
|
||||||
ports:
|
|
||||||
- port: 80
|
|
||||||
protocol: TCP
|
|
||||||
- port: 443
|
|
||||||
protocol: TCP
|
|
||||||
- port: 8080
|
|
||||||
protocol: TCP
|
|
||||||
- port: 8443
|
|
||||||
protocol: TCP
|
|
||||||
- port: 5000
|
|
||||||
protocol: TCP
|
|
||||||
- port: 5001
|
|
||||||
protocol: TCP
|
|
||||||
# Synology NFS allowance for future cold-tier/audit archival jobs.
|
|
||||||
- to:
|
|
||||||
- ipBlock:
|
|
||||||
cidr: 10.0.58.3/32
|
|
||||||
ports:
|
|
||||||
- port: 2049
|
|
||||||
protocol: TCP
|
|
||||||
- port: 2049
|
|
||||||
protocol: UDP
|
|
||||||
- port: 111
|
|
||||||
protocol: TCP
|
|
||||||
- port: 111
|
|
||||||
protocol: UDP
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
apiVersion: v1
|
|
||||||
kind: Service
|
|
||||||
metadata:
|
|
||||||
name: fc-devicemgmt-web
|
|
||||||
namespace: fc-devicemgmt
|
|
||||||
labels:
|
|
||||||
app: fc-devicemgmt-web
|
|
||||||
app.kubernetes.io/name: fc-devicemgmt-web
|
|
||||||
app.kubernetes.io/component: web
|
|
||||||
app.kubernetes.io/part-of: flowercore
|
|
||||||
app.kubernetes.io/managed-by: argocd
|
|
||||||
flowercore.io/tenant-id: system
|
|
||||||
flowercore.io/created-by: bluejay-infra
|
|
||||||
spec:
|
|
||||||
type: ClusterIP
|
|
||||||
selector:
|
|
||||||
app: fc-devicemgmt-web
|
|
||||||
ports:
|
|
||||||
- name: http
|
|
||||||
port: 80
|
|
||||||
targetPort: 8080
|
|
||||||
protocol: TCP
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
apiVersion: v1
|
|
||||||
kind: ServiceAccount
|
|
||||||
metadata:
|
|
||||||
name: fc-devicemgmt-operator
|
|
||||||
namespace: fc-devicemgmt
|
|
||||||
labels:
|
|
||||||
app.kubernetes.io/name: fc-devicemgmt-operator
|
|
||||||
app.kubernetes.io/component: operator
|
|
||||||
app.kubernetes.io/part-of: flowercore
|
|
||||||
app.kubernetes.io/managed-by: argocd
|
|
||||||
flowercore.io/tenant-id: system
|
|
||||||
flowercore.io/created-by: bluejay-infra
|
|
||||||
@@ -1,171 +0,0 @@
|
|||||||
# fc-redis — SignalR backplane for cross-product event bus
|
|
||||||
#
|
|
||||||
# Lands per Q-SO-1 resolution (2026-05-11 PM): SignalR backplane in Phase A,
|
|
||||||
# not Phase C as originally drafted. Operator directive: "Redis can be
|
|
||||||
# deployed just fine as it's another FlowerCore technology we'll want to
|
|
||||||
# manage."
|
|
||||||
#
|
|
||||||
# Phase A scope (this file):
|
|
||||||
# - Single Redis 7.x Alpine pod
|
|
||||||
# - 1Gi Longhorn RWO PVC for AOF persistence
|
|
||||||
# - ClusterIP Service at `redis.fc-redis.svc.cluster.local:6379`
|
|
||||||
# - No AUTH (in-cluster only; not exposed externally)
|
|
||||||
# - No IngressRoute (backplane is server-to-server only)
|
|
||||||
#
|
|
||||||
# Consumers (Phase A IMPL across FC services):
|
|
||||||
# - FlowerCore.Signage.Web (OpsConsoleHub)
|
|
||||||
# - FlowerCore.Scoreboard.Web (ScoreboardHub)
|
|
||||||
# - FlowerCore.SignalControl.Web
|
|
||||||
# - FlowerCore.DMS.Web
|
|
||||||
# - Any other product joining the cross-product event bus
|
|
||||||
#
|
|
||||||
# Each consumer adds:
|
|
||||||
# services.AddSignalR()
|
|
||||||
# .AddStackExchangeRedis(
|
|
||||||
# "redis.fc-redis.svc.cluster.local:6379",
|
|
||||||
# opts => opts.Configuration.ChannelPrefix =
|
|
||||||
# StackExchange.Redis.RedisChannel.Literal("fc-opsconsole"));
|
|
||||||
#
|
|
||||||
# Phase B / C follow-ons (out of scope here):
|
|
||||||
# - Redis Sentinel for HA (3-node)
|
|
||||||
# - AUTH password from 1Password Connect (rotate via /rotate-password)
|
|
||||||
# - redis_exporter sidecar for Prometheus scrape
|
|
||||||
# - Network policies restricting which namespaces can dial 6379
|
|
||||||
#
|
|
||||||
# Design: docs/signage/operations-console-phase-2-design.md §3.5
|
|
||||||
# Decision: Q-SO-1 (RESOLVED 2026-05-11 PM)
|
|
||||||
# Memory: feedback_blooming_ui_pattern_no_iframes
|
|
||||||
---
|
|
||||||
apiVersion: v1
|
|
||||||
kind: Namespace
|
|
||||||
metadata:
|
|
||||||
name: fc-redis
|
|
||||||
labels:
|
|
||||||
app.kubernetes.io/part-of: flowercore
|
|
||||||
app.kubernetes.io/managed-by: argocd
|
|
||||||
---
|
|
||||||
apiVersion: v1
|
|
||||||
kind: PersistentVolumeClaim
|
|
||||||
metadata:
|
|
||||||
name: fc-redis-data
|
|
||||||
namespace: fc-redis
|
|
||||||
spec:
|
|
||||||
accessModes:
|
|
||||||
- ReadWriteOnce
|
|
||||||
storageClassName: longhorn
|
|
||||||
resources:
|
|
||||||
requests:
|
|
||||||
storage: 1Gi
|
|
||||||
---
|
|
||||||
apiVersion: v1
|
|
||||||
kind: ConfigMap
|
|
||||||
metadata:
|
|
||||||
name: fc-redis-config
|
|
||||||
namespace: fc-redis
|
|
||||||
data:
|
|
||||||
redis.conf: |
|
|
||||||
# Phase A — minimal config; no AUTH, no replication.
|
|
||||||
bind 0.0.0.0
|
|
||||||
protected-mode no
|
|
||||||
port 6379
|
|
||||||
tcp-backlog 511
|
|
||||||
timeout 0
|
|
||||||
tcp-keepalive 300
|
|
||||||
|
|
||||||
# Persistence: AOF (fsync every second is the standard SignalR-backplane
|
|
||||||
# durability sweet spot — the backplane only needs to survive Redis
|
|
||||||
# restarts, not absolute zero loss).
|
|
||||||
appendonly yes
|
|
||||||
appendfsync everysec
|
|
||||||
auto-aof-rewrite-percentage 100
|
|
||||||
auto-aof-rewrite-min-size 64mb
|
|
||||||
|
|
||||||
# Reasonable defaults — let Redis pick most things.
|
|
||||||
maxmemory-policy allkeys-lru
|
|
||||||
maxmemory 256mb
|
|
||||||
|
|
||||||
# Logging
|
|
||||||
loglevel notice
|
|
||||||
---
|
|
||||||
apiVersion: apps/v1
|
|
||||||
kind: Deployment
|
|
||||||
metadata:
|
|
||||||
name: fc-redis
|
|
||||||
namespace: fc-redis
|
|
||||||
labels:
|
|
||||||
app: fc-redis
|
|
||||||
spec:
|
|
||||||
replicas: 1
|
|
||||||
strategy:
|
|
||||||
type: Recreate # RWO PVC; do not do rolling update
|
|
||||||
selector:
|
|
||||||
matchLabels:
|
|
||||||
app: fc-redis
|
|
||||||
template:
|
|
||||||
metadata:
|
|
||||||
labels:
|
|
||||||
app: fc-redis
|
|
||||||
spec:
|
|
||||||
securityContext:
|
|
||||||
runAsNonRoot: true
|
|
||||||
runAsUser: 999 # redis:7-alpine default uid
|
|
||||||
runAsGroup: 999
|
|
||||||
fsGroup: 999
|
|
||||||
containers:
|
|
||||||
- name: redis
|
|
||||||
image: redis:7-alpine
|
|
||||||
imagePullPolicy: IfNotPresent
|
|
||||||
command: ["redis-server", "/etc/redis/redis.conf"]
|
|
||||||
ports:
|
|
||||||
- name: redis
|
|
||||||
containerPort: 6379
|
|
||||||
resources:
|
|
||||||
requests:
|
|
||||||
cpu: "50m"
|
|
||||||
memory: "128Mi"
|
|
||||||
limits:
|
|
||||||
cpu: "500m"
|
|
||||||
memory: "384Mi"
|
|
||||||
volumeMounts:
|
|
||||||
- name: data
|
|
||||||
mountPath: /data
|
|
||||||
- name: config
|
|
||||||
mountPath: /etc/redis
|
|
||||||
readOnly: true
|
|
||||||
livenessProbe:
|
|
||||||
tcpSocket:
|
|
||||||
port: 6379
|
|
||||||
initialDelaySeconds: 5
|
|
||||||
periodSeconds: 10
|
|
||||||
readinessProbe:
|
|
||||||
exec:
|
|
||||||
command: ["redis-cli", "ping"]
|
|
||||||
initialDelaySeconds: 2
|
|
||||||
periodSeconds: 5
|
|
||||||
securityContext:
|
|
||||||
allowPrivilegeEscalation: false
|
|
||||||
readOnlyRootFilesystem: true
|
|
||||||
capabilities:
|
|
||||||
drop: [ALL]
|
|
||||||
volumes:
|
|
||||||
- name: data
|
|
||||||
persistentVolumeClaim:
|
|
||||||
claimName: fc-redis-data
|
|
||||||
- name: config
|
|
||||||
configMap:
|
|
||||||
name: fc-redis-config
|
|
||||||
---
|
|
||||||
apiVersion: v1
|
|
||||||
kind: Service
|
|
||||||
metadata:
|
|
||||||
name: redis
|
|
||||||
namespace: fc-redis
|
|
||||||
spec:
|
|
||||||
type: ClusterIP
|
|
||||||
selector:
|
|
||||||
app: fc-redis
|
|
||||||
ports:
|
|
||||||
- name: redis
|
|
||||||
port: 6379
|
|
||||||
targetPort: 6379
|
|
||||||
protocol: TCP
|
|
||||||
@@ -466,11 +466,11 @@ spec:
|
|||||||
itemPath: vaults/IAmWorkin/items/Guacamole JSON Auth
|
itemPath: vaults/IAmWorkin/items/Guacamole JSON Auth
|
||||||
---
|
---
|
||||||
---
|
---
|
||||||
# 1Password-backed credentials for Mac mini VNC access (Phase 1 <EFBFBD> 2026-04-28)
|
# 1Password-backed credentials for Mac mini VNC access (Phase 1 — 2026-04-28)
|
||||||
# The operator mints Secret 'macmini-vnc-creds' with keys: username, password, VNC Password
|
# The operator mints Secret 'macmini-vnc-creds' with keys: username, password, VNC Password
|
||||||
# Note: '1Password' field label 'VNC Password' -> K8s Secret key 'VNC Password' (space retained)
|
# Note: '1Password' field label 'VNC Password' -> K8s Secret key 'VNC Password' (space retained)
|
||||||
# Guacamole VNC connection password is sourced from the 'VNC Password' field.
|
# Guacamole VNC connection password is sourced from the 'VNC Password' field.
|
||||||
# Actual IP is 10.0.56.115 (INFRA VLAN) <EFBFBD> the 1P item 'IP' field is kept as backup reference.
|
# Actual IP is 10.0.56.115 (INFRA VLAN) — the 1P item 'IP' field is kept as backup reference.
|
||||||
apiVersion: onepassword.com/v1
|
apiVersion: onepassword.com/v1
|
||||||
kind: OnePasswordItem
|
kind: OnePasswordItem
|
||||||
metadata:
|
metadata:
|
||||||
@@ -481,7 +481,6 @@ metadata:
|
|||||||
app.kubernetes.io/part-of: flowercore
|
app.kubernetes.io/part-of: flowercore
|
||||||
spec:
|
spec:
|
||||||
itemPath: vaults/IAmWorkin/items/Mac Mini
|
itemPath: vaults/IAmWorkin/items/Mac Mini
|
||||||
---
|
|
||||||
# Blue Jay Branding Extension (CSS + translations)
|
# Blue Jay Branding Extension (CSS + translations)
|
||||||
apiVersion: v1
|
apiVersion: v1
|
||||||
kind: ConfigMap
|
kind: ConfigMap
|
||||||
|
|||||||
@@ -291,184 +291,6 @@ public sealed class FleetManifestLintTests
|
|||||||
violations.Should().BeEmpty();
|
violations.Should().BeEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void FcDeviceManagement_MustShipExpectedManifestSet()
|
|
||||||
{
|
|
||||||
var appRoot = Path.Combine(Inventory.BluejayRoot, "apps", "fc-devicemgmt");
|
|
||||||
Directory.Exists(appRoot).Should().BeTrue("Sprint 8 Cx-5 owns apps/fc-devicemgmt.");
|
|
||||||
|
|
||||||
var expectedFiles = new[]
|
|
||||||
{
|
|
||||||
"1password-item.yaml",
|
|
||||||
"argocd-application.yaml",
|
|
||||||
"certificate-web.yaml",
|
|
||||||
"clusterrole-operator.yaml",
|
|
||||||
"clusterrolebinding-operator.yaml",
|
|
||||||
"deployment-operator.yaml",
|
|
||||||
"deployment-web.yaml",
|
|
||||||
"ingressroute-web.yaml",
|
|
||||||
"namespace.yaml",
|
|
||||||
"network-policy.yaml",
|
|
||||||
"service-web.yaml",
|
|
||||||
"serviceaccount-operator.yaml",
|
|
||||||
};
|
|
||||||
|
|
||||||
Directory.GetFiles(appRoot, "*.yaml")
|
|
||||||
.Select(Path.GetFileName)
|
|
||||||
.Should()
|
|
||||||
.BeEquivalentTo(expectedFiles);
|
|
||||||
|
|
||||||
foreach (var expectedFile in expectedFiles)
|
|
||||||
{
|
|
||||||
FcDeviceManagementDocuments()
|
|
||||||
.Should()
|
|
||||||
.Contain(document => document.RelativePath == $"fc-devicemgmt/{expectedFile}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void FcDeviceManagement_ObjectsMustCarryStandardTraceabilityLabels()
|
|
||||||
{
|
|
||||||
var requiredLabels = new[]
|
|
||||||
{
|
|
||||||
"app.kubernetes.io/name",
|
|
||||||
"app.kubernetes.io/part-of",
|
|
||||||
"app.kubernetes.io/managed-by",
|
|
||||||
"flowercore.io/tenant-id",
|
|
||||||
"flowercore.io/created-by",
|
|
||||||
};
|
|
||||||
|
|
||||||
var violations = FcDeviceManagementDocuments()
|
|
||||||
.SelectMany(document => requiredLabels
|
|
||||||
.Where(label => string.IsNullOrWhiteSpace(document.Scalar("metadata", "labels", label)))
|
|
||||||
.Select(label => $"{document.Descriptor} is missing metadata.labels['{label}']."))
|
|
||||||
.Concat(FcDeviceManagementDocuments()
|
|
||||||
.Where(document => document.Kind == "Deployment")
|
|
||||||
.SelectMany(document => requiredLabels
|
|
||||||
.Where(label => string.IsNullOrWhiteSpace(document.Scalar("spec", "template", "metadata", "labels", label)))
|
|
||||||
.Select(label => $"{document.Descriptor} pod template is missing metadata.labels['{label}'].")))
|
|
||||||
.Concat(FcDeviceManagementDocuments()
|
|
||||||
.Where(document => document.Kind == "Deployment")
|
|
||||||
.Where(document => string.IsNullOrWhiteSpace(document.Scalar("spec", "template", "metadata", "annotations", "flowercore.io/audit-trace-id")))
|
|
||||||
.Select(document => $"{document.Descriptor} pod template is missing flowercore.io/audit-trace-id."))
|
|
||||||
.ToList();
|
|
||||||
|
|
||||||
violations.Should().BeEmpty();
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void FcDeviceManagement_IngressMustUseCertManagerAndKeepPublicHostDisabled()
|
|
||||||
{
|
|
||||||
var appText = string.Join(
|
|
||||||
Environment.NewLine,
|
|
||||||
Directory.GetFiles(Path.Combine(Inventory.BluejayRoot, "apps", "fc-devicemgmt"), "*.yaml")
|
|
||||||
.Select(File.ReadAllText));
|
|
||||||
|
|
||||||
appText.Should().NotContain("certResolver");
|
|
||||||
appText.Should().Contain("update.flowercore.io");
|
|
||||||
appText.Should().Contain("disabled-until-Q-OIDC-1");
|
|
||||||
|
|
||||||
FcDeviceManagementDocuments()
|
|
||||||
.Where(document => document.Kind == "IngressRoute")
|
|
||||||
.SelectMany(document => document.MappingSequence("spec", "routes"))
|
|
||||||
.Select(route => ManifestNodeExtensions.Scalar(route, "match") ?? string.Empty)
|
|
||||||
.Should()
|
|
||||||
.Contain(match => match.Contains("Host(`devices.iamworkin.lan`)", StringComparison.Ordinal))
|
|
||||||
.And.NotContain(match => match.Contains("Host(`update.flowercore.io`)", StringComparison.Ordinal));
|
|
||||||
|
|
||||||
var certificate = FcDeviceManagementDocuments()
|
|
||||||
.Single(document => document.Kind == "Certificate" && document.Name == "fc-devicemgmt-web-tls");
|
|
||||||
|
|
||||||
certificate.Scalar("spec", "issuerRef", "name").Should().Be("step-ca-acme");
|
|
||||||
certificate.Scalar("spec", "issuerRef", "kind").Should().Be("ClusterIssuer");
|
|
||||||
ManifestNodeExtensions.ScalarSequence(certificate.Root, "spec", "dnsNames")
|
|
||||||
.Should()
|
|
||||||
.ContainSingle("devices.iamworkin.lan");
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void FcDeviceManagement_OperatorRbacMustCoverDevicesAndOwnerLookup()
|
|
||||||
{
|
|
||||||
var clusterRole = FcDeviceManagementDocuments()
|
|
||||||
.Single(document => document.Kind == "ClusterRole" && document.Name == "fc-devicemgmt-operator");
|
|
||||||
var allScalars = clusterRole.AllScalars().ToList();
|
|
||||||
|
|
||||||
allScalars.Should().Contain("devices.flowercore.io");
|
|
||||||
allScalars.Should().Contain("*");
|
|
||||||
allScalars.Should().Contain("deployments");
|
|
||||||
allScalars.Should().Contain("get");
|
|
||||||
|
|
||||||
var operatorDeployment = FcDeviceManagementDocuments()
|
|
||||||
.Single(document => document.Kind == "Deployment" && document.Name == "fc-devicemgmt-operator");
|
|
||||||
|
|
||||||
operatorDeployment.AllScalars().Should().Contain("FLOWERCORE_KUBERNETES_OWNER_DEPLOYMENT");
|
|
||||||
operatorDeployment.AllScalars().Should().Contain("fc-devicemgmt-operator");
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void FcDeviceManagement_RuntimeSecretsMustUseOnePasswordItemPattern()
|
|
||||||
{
|
|
||||||
var item = FcDeviceManagementDocuments()
|
|
||||||
.Single(document => document.Kind == "OnePasswordItem" && document.Name == "fc-devicemgmt-runtime");
|
|
||||||
|
|
||||||
item.Scalar("spec", "itemPath")
|
|
||||||
.Should()
|
|
||||||
.Be("vaults/IAmWorkin/items/FlowerCore DeviceManagement Runtime");
|
|
||||||
|
|
||||||
var appText = string.Join(
|
|
||||||
Environment.NewLine,
|
|
||||||
Directory.GetFiles(Path.Combine(Inventory.BluejayRoot, "apps", "fc-devicemgmt"), "*.yaml")
|
|
||||||
.Select(File.ReadAllText));
|
|
||||||
|
|
||||||
FcDeviceManagementDocuments().Should().NotContain(document => document.Kind == "Secret");
|
|
||||||
appText.Should().Contain("secretKeyRef:");
|
|
||||||
appText.Should().Contain("secretName: fc-devicemgmt-runtime");
|
|
||||||
appText.Should().NotContain("stringData:");
|
|
||||||
appText.Should().NotContain("from-literal");
|
|
||||||
appText.Should().NotContain("tls.key:");
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void FcDeviceManagement_NetworkPoliciesMustAllowLanAgentsSynologyAndDnatPorts()
|
|
||||||
{
|
|
||||||
var policies = FcDeviceManagementDocuments()
|
|
||||||
.Where(document => document.Kind == "NetworkPolicy")
|
|
||||||
.ToList();
|
|
||||||
|
|
||||||
policies.Should().HaveCount(2);
|
|
||||||
|
|
||||||
var combinedScalars = policies.SelectMany(policy => policy.AllScalars()).ToList();
|
|
||||||
combinedScalars.Should().Contain("10.0.56.0/24");
|
|
||||||
combinedScalars.Should().Contain("10.0.57.0/24");
|
|
||||||
combinedScalars.Should().Contain("10.0.58.0/24");
|
|
||||||
combinedScalars.Should().Contain("10.0.68.0/27");
|
|
||||||
combinedScalars.Should().Contain("10.0.58.3/32");
|
|
||||||
|
|
||||||
var combinedEgressPorts = policies.SelectMany(policy => policy.EgressPorts()).ToHashSet(StringComparer.Ordinal);
|
|
||||||
combinedEgressPorts.Should().Contain(new[] { "80", "443", "8080", "8443", "2049", "111" });
|
|
||||||
|
|
||||||
var traefikVipPolicies = policies
|
|
||||||
.Where(policy => policy.AllScalars().Any(value => value.Contains("10.0.56.200", StringComparison.Ordinal)))
|
|
||||||
.ToList();
|
|
||||||
|
|
||||||
traefikVipPolicies.Should().ContainSingle();
|
|
||||||
traefikVipPolicies[0].EgressPorts().Should().Contain(new[] { "80", "443", "8080", "8443" });
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void FcDeviceManagement_ArgocdApplicationMustMatchApplicationSetDiscoveryConventions()
|
|
||||||
{
|
|
||||||
var application = FcDeviceManagementDocuments()
|
|
||||||
.Single(document => document.Kind == "Application" && document.Name == "infra-fc-devicemgmt");
|
|
||||||
|
|
||||||
application.Namespace.Should().Be("argocd");
|
|
||||||
application.Scalar("spec", "source", "repoURL")
|
|
||||||
.Should()
|
|
||||||
.Be("http://gitea-clusterip.gitea.svc.cluster.local:3000/bluejay/bluejay-infra.git");
|
|
||||||
application.Scalar("spec", "source", "path").Should().Be("apps/fc-devicemgmt");
|
|
||||||
application.Scalar("spec", "destination", "namespace").Should().Be("fc-devicemgmt");
|
|
||||||
}
|
|
||||||
|
|
||||||
private static IEnumerable<string> ProbeViolations(
|
private static IEnumerable<string> ProbeViolations(
|
||||||
ManifestDocument document,
|
ManifestDocument document,
|
||||||
YamlMappingNode container,
|
YamlMappingNode container,
|
||||||
@@ -492,13 +314,6 @@ public sealed class FleetManifestLintTests
|
|||||||
$"{document.Descriptor} container '{containerName}' still uses {probeKey}.httpGet on /health.",
|
$"{document.Descriptor} container '{containerName}' still uses {probeKey}.httpGet on /health.",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private static IReadOnlyList<ManifestDocument> FcDeviceManagementDocuments()
|
|
||||||
{
|
|
||||||
return Inventory.Documents
|
|
||||||
.Where(document => document.RelativePath.StartsWith("fc-devicemgmt/", StringComparison.Ordinal))
|
|
||||||
.ToList();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal sealed class ManifestInventory
|
internal sealed class ManifestInventory
|
||||||
|
|||||||
Reference in New Issue
Block a user