Adopt fc-updater into ArgoCD
This commit is contained in:
47
apps/fc-updater/README.md
Normal file
47
apps/fc-updater/README.md
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
# fc-updater — Update Center GitOps adoption
|
||||||
|
|
||||||
|
**Status:** adopted into `bluejay-infra` on 2026-05-06. The live ArgoCD
|
||||||
|
Application is `infra-fc-updater`, generated by the `bluejay-infra`
|
||||||
|
ApplicationSet with automated sync, `prune: true`, and `selfHeal: true`.
|
||||||
|
|
||||||
|
## Managed manifest set
|
||||||
|
|
||||||
|
`apps/fc-updater/fc-updater.yaml` manages:
|
||||||
|
|
||||||
|
- `Namespace/fc-updater`
|
||||||
|
- `PersistentVolumeClaim/updatecenter-data`
|
||||||
|
- `Deployment/updatecenter-web`
|
||||||
|
- `Service/updatecenter-web`
|
||||||
|
- `Certificate/updatecenter-web-tls`
|
||||||
|
- `Certificate/updatecenter-web-internal-tls`
|
||||||
|
- `IngressRoute/updatecenter-web`
|
||||||
|
- `IngressRoute/updatecenter-web-internal`
|
||||||
|
- `IngressRoute/updatecenter-web-public`
|
||||||
|
|
||||||
|
The Deployment intentionally sets `revisionHistoryLimit: 3` and
|
||||||
|
`strategy.type: Recreate`. The service is singleton + SQLite/local bundle
|
||||||
|
storage on `PersistentVolumeClaim/updatecenter-data`, pinned to
|
||||||
|
`rke2-server`.
|
||||||
|
|
||||||
|
## Runtime dependencies intentionally not stored here
|
||||||
|
|
||||||
|
These live Secrets are pre-existing runtime material and are not committed to
|
||||||
|
Git:
|
||||||
|
|
||||||
|
- `updater-bootstrap-auth`
|
||||||
|
- `updater-signing`
|
||||||
|
- `updater-webhooks`
|
||||||
|
- `cf-origin-flowercore-io`
|
||||||
|
|
||||||
|
Rotate the Cloudflare Origin Certificate through
|
||||||
|
`FlowerCore.Notes/docs/standards/code-signing-rotation-runbook.md`; the
|
||||||
|
shared origin cert must exist in every namespace that serves a
|
||||||
|
`*.flowercore.io` public IngressRoute.
|
||||||
|
|
||||||
|
## Verification
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
kubectl.exe --kubeconfig C:\Users\AndrewStoltz\.kube\rke2.yaml -n argocd get application infra-fc-updater
|
||||||
|
kubectl.exe --kubeconfig C:\Users\AndrewStoltz\.kube\rke2.yaml -n fc-updater get deploy,svc,ingressroute,certificate,pvc
|
||||||
|
curl.exe -sk https://update.flowercore.io/api/v1/manifests/_schema
|
||||||
|
```
|
||||||
248
apps/fc-updater/fc-updater.yaml
Normal file
248
apps/fc-updater/fc-updater.yaml
Normal file
@@ -0,0 +1,248 @@
|
|||||||
|
# FlowerCore Update Center
|
||||||
|
# GitOps adoption of the live fc-updater namespace after PUB-1/PUB-3.
|
||||||
|
# Runtime credentials remain in existing K8s Secrets; do not store them here.
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Namespace
|
||||||
|
metadata:
|
||||||
|
name: fc-updater
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/part-of: flowercore
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: PersistentVolumeClaim
|
||||||
|
metadata:
|
||||||
|
name: updatecenter-data
|
||||||
|
namespace: fc-updater
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/name: updatecenter-web
|
||||||
|
app.kubernetes.io/part-of: flowercore
|
||||||
|
spec:
|
||||||
|
accessModes:
|
||||||
|
- ReadWriteOnce
|
||||||
|
storageClassName: longhorn
|
||||||
|
volumeMode: Filesystem
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
storage: 10Gi
|
||||||
|
---
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: updatecenter-web
|
||||||
|
namespace: fc-updater
|
||||||
|
labels:
|
||||||
|
app: updatecenter-web
|
||||||
|
app.kubernetes.io/name: updatecenter-web
|
||||||
|
app.kubernetes.io/part-of: flowercore
|
||||||
|
spec:
|
||||||
|
replicas: 1
|
||||||
|
revisionHistoryLimit: 3
|
||||||
|
strategy:
|
||||||
|
# SQLite + local bundle storage live on a single RWO PVC. Recreate avoids
|
||||||
|
# two pods overlapping the same write path during future image bumps.
|
||||||
|
type: Recreate
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: updatecenter-web
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: updatecenter-web
|
||||||
|
spec:
|
||||||
|
nodeName: rke2-server
|
||||||
|
containers:
|
||||||
|
- name: web
|
||||||
|
image: localhost/fc-updater-web:v20260506-pub3-fix1
|
||||||
|
imagePullPolicy: Never
|
||||||
|
ports:
|
||||||
|
- containerPort: 8080
|
||||||
|
name: http
|
||||||
|
env:
|
||||||
|
- name: ASPNETCORE_URLS
|
||||||
|
value: http://+:8080
|
||||||
|
- name: FlowerCore__Updater__Database__Provider
|
||||||
|
value: sqlite
|
||||||
|
- name: FlowerCore__Updater__Database__ConnectionString
|
||||||
|
value: Data Source=/data/updatecenter.db
|
||||||
|
- name: FlowerCore__Updater__BundleStorage__LocalFs__RootDirectory
|
||||||
|
value: /data/bundles
|
||||||
|
- name: FlowerCore__Updater__Auth__Bootstrap__Enabled
|
||||||
|
value: "true"
|
||||||
|
- name: FlowerCore__Updater__Auth__Bootstrap__Username
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: updater-bootstrap-auth
|
||||||
|
key: username
|
||||||
|
- name: FlowerCore__Updater__Auth__Bootstrap__Password
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: updater-bootstrap-auth
|
||||||
|
key: password
|
||||||
|
- name: FlowerCore__Updater__Auth__Bootstrap__SigningKey
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: updater-bootstrap-auth
|
||||||
|
key: signing-key
|
||||||
|
- name: FlowerCore__Updater__Signing__AutoSignOnPublish
|
||||||
|
value: "true"
|
||||||
|
- name: FlowerCore__Updater__Signing__RequireSignatureOnPublish
|
||||||
|
value: "true"
|
||||||
|
- name: FlowerCore__Updater__Signing__PfxBase64
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: updater-signing
|
||||||
|
key: pfx-base64
|
||||||
|
- name: FlowerCore__Updater__Signing__PfxPassword
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: updater-signing
|
||||||
|
key: pfx-password
|
||||||
|
- name: FlowerCore__Updater__Signing__OpItemReference
|
||||||
|
value: op://FlowerCore/step-ca-codesign
|
||||||
|
- name: FlowerCore__Updater__Signing__TrustAnchorPath
|
||||||
|
value: /etc/flowercore-updater/signing/root-ca.pem
|
||||||
|
- name: FlowerCore__Updater__GitHub__Token
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: updater-webhooks
|
||||||
|
key: github-token
|
||||||
|
- name: FlowerCore__Updater__GitHub__WebhookSecret
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: updater-webhooks
|
||||||
|
key: github-webhook-secret
|
||||||
|
- name: FlowerCore__Updater__Gitea__Token
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: updater-webhooks
|
||||||
|
key: gitea-token
|
||||||
|
- name: FlowerCore__Updater__Gitea__WebhookSecret
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: updater-webhooks
|
||||||
|
key: gitea-webhook-secret
|
||||||
|
readinessProbe:
|
||||||
|
tcpSocket:
|
||||||
|
port: http
|
||||||
|
initialDelaySeconds: 10
|
||||||
|
periodSeconds: 15
|
||||||
|
livenessProbe:
|
||||||
|
tcpSocket:
|
||||||
|
port: http
|
||||||
|
initialDelaySeconds: 30
|
||||||
|
periodSeconds: 30
|
||||||
|
volumeMounts:
|
||||||
|
- name: data
|
||||||
|
mountPath: /data
|
||||||
|
- name: signing
|
||||||
|
mountPath: /etc/flowercore-updater/signing
|
||||||
|
readOnly: true
|
||||||
|
volumes:
|
||||||
|
- name: data
|
||||||
|
persistentVolumeClaim:
|
||||||
|
claimName: updatecenter-data
|
||||||
|
- name: signing
|
||||||
|
secret:
|
||||||
|
secretName: updater-signing
|
||||||
|
items:
|
||||||
|
- key: root-ca.pem
|
||||||
|
path: root-ca.pem
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: updatecenter-web
|
||||||
|
namespace: fc-updater
|
||||||
|
labels:
|
||||||
|
app: updatecenter-web
|
||||||
|
app.kubernetes.io/name: updatecenter-web
|
||||||
|
app.kubernetes.io/part-of: flowercore
|
||||||
|
spec:
|
||||||
|
type: ClusterIP
|
||||||
|
selector:
|
||||||
|
app: updatecenter-web
|
||||||
|
ports:
|
||||||
|
- name: http
|
||||||
|
port: 8080
|
||||||
|
targetPort: http
|
||||||
|
---
|
||||||
|
apiVersion: cert-manager.io/v1
|
||||||
|
kind: Certificate
|
||||||
|
metadata:
|
||||||
|
name: updatecenter-web-tls
|
||||||
|
namespace: fc-updater
|
||||||
|
spec:
|
||||||
|
secretName: updatecenter-web-tls
|
||||||
|
issuerRef:
|
||||||
|
name: step-ca-acme
|
||||||
|
kind: ClusterIssuer
|
||||||
|
dnsNames:
|
||||||
|
- updatecenter.iamworkin.lan
|
||||||
|
- updates.iamworkin.lan
|
||||||
|
---
|
||||||
|
apiVersion: cert-manager.io/v1
|
||||||
|
kind: Certificate
|
||||||
|
metadata:
|
||||||
|
name: updatecenter-web-internal-tls
|
||||||
|
namespace: fc-updater
|
||||||
|
spec:
|
||||||
|
secretName: updatecenter-web-internal-tls
|
||||||
|
issuerRef:
|
||||||
|
name: step-ca-acme
|
||||||
|
kind: ClusterIssuer
|
||||||
|
dnsNames:
|
||||||
|
- updatecenter-internal.iamworkin.lan
|
||||||
|
---
|
||||||
|
apiVersion: traefik.io/v1alpha1
|
||||||
|
kind: IngressRoute
|
||||||
|
metadata:
|
||||||
|
name: updatecenter-web
|
||||||
|
namespace: fc-updater
|
||||||
|
spec:
|
||||||
|
entryPoints:
|
||||||
|
- web
|
||||||
|
- websecure
|
||||||
|
routes:
|
||||||
|
- match: (Host(`updatecenter.iamworkin.lan`) || Host(`updates.iamworkin.lan`)) && (Method(`GET`) || Method(`HEAD`) || Method(`POST`) || Method(`OPTIONS`))
|
||||||
|
kind: Rule
|
||||||
|
services:
|
||||||
|
- name: updatecenter-web
|
||||||
|
port: 8080
|
||||||
|
tls:
|
||||||
|
secretName: updatecenter-web-tls
|
||||||
|
---
|
||||||
|
apiVersion: traefik.io/v1alpha1
|
||||||
|
kind: IngressRoute
|
||||||
|
metadata:
|
||||||
|
name: updatecenter-web-internal
|
||||||
|
namespace: fc-updater
|
||||||
|
spec:
|
||||||
|
entryPoints:
|
||||||
|
- web
|
||||||
|
- websecure
|
||||||
|
routes:
|
||||||
|
- match: Host(`updatecenter-internal.iamworkin.lan`)
|
||||||
|
kind: Rule
|
||||||
|
services:
|
||||||
|
- name: updatecenter-web
|
||||||
|
port: 8080
|
||||||
|
tls:
|
||||||
|
secretName: updatecenter-web-internal-tls
|
||||||
|
---
|
||||||
|
apiVersion: traefik.io/v1alpha1
|
||||||
|
kind: IngressRoute
|
||||||
|
metadata:
|
||||||
|
name: updatecenter-web-public
|
||||||
|
namespace: fc-updater
|
||||||
|
spec:
|
||||||
|
entryPoints:
|
||||||
|
- websecure
|
||||||
|
routes:
|
||||||
|
- match: (Host(`update.flowercore.io`) || Host(`updates.flowercore.io`)) && (Method(`GET`) || Method(`HEAD`) || Method(`POST`) || Method(`OPTIONS`))
|
||||||
|
kind: Rule
|
||||||
|
services:
|
||||||
|
- name: updatecenter-web
|
||||||
|
port: 8080
|
||||||
|
tls:
|
||||||
|
secretName: cf-origin-flowercore-io
|
||||||
7
apps/fc-updater/kustomization.yaml
Normal file
7
apps/fc-updater/kustomization.yaml
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
# ArgoCD's bluejay-infra ApplicationSet uses a directory generator and does
|
||||||
|
# not require kustomization.yaml. Keep this anyway as the manifest inventory
|
||||||
|
# and for local `kubectl kustomize apps/fc-updater` previews.
|
||||||
|
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||||
|
kind: Kustomization
|
||||||
|
resources:
|
||||||
|
- fc-updater.yaml
|
||||||
@@ -1,5 +1,10 @@
|
|||||||
# FlowerCore Tenant — flowercore.io (main brand)
|
# FlowerCore Tenant — retired flowercore.io placeholder.
|
||||||
# Public-facing placeholder landing page served by nginx
|
#
|
||||||
|
# Public flowercore.io/www.flowercore.io routing is now owned by
|
||||||
|
# apps/fc-landing/fc-landing.yaml. This tenant placeholder remains available
|
||||||
|
# only as an in-cluster service; do not create a duplicate public
|
||||||
|
# IngressRoute here because it competes with fc-landing and requires a
|
||||||
|
# namespace-local cf-origin-flowercore-io Secret.
|
||||||
# ArgoCD managed - BlueJay Lab
|
# ArgoCD managed - BlueJay Lab
|
||||||
---
|
---
|
||||||
apiVersion: v1
|
apiVersion: v1
|
||||||
@@ -10,12 +15,6 @@ metadata:
|
|||||||
app.kubernetes.io/part-of: bluejay-infra
|
app.kubernetes.io/part-of: bluejay-infra
|
||||||
flowercore.io/tenant: flowercore
|
flowercore.io/tenant: flowercore
|
||||||
---
|
---
|
||||||
# NOTE: The existing cf-origin-flowercore-io secret (covering *.flowercore.io)
|
|
||||||
# must be copied into this namespace. It already exists in other namespaces.
|
|
||||||
# Copy with: kubectl get secret cf-origin-flowercore-io -n fc-system -o yaml \
|
|
||||||
# | sed 's/namespace: .*/namespace: tenant-flowercore/' \
|
|
||||||
# | kubectl apply -f -
|
|
||||||
---
|
|
||||||
# Landing page HTML
|
# Landing page HTML
|
||||||
apiVersion: v1
|
apiVersion: v1
|
||||||
kind: ConfigMap
|
kind: ConfigMap
|
||||||
@@ -311,22 +310,3 @@ spec:
|
|||||||
- port: 80
|
- port: 80
|
||||||
targetPort: 80
|
targetPort: 80
|
||||||
name: http
|
name: http
|
||||||
---
|
|
||||||
# Traefik IngressRoute — public via Cloudflare
|
|
||||||
# Uses existing cf-origin-flowercore-io cert (must be copied to this namespace)
|
|
||||||
apiVersion: traefik.io/v1alpha1
|
|
||||||
kind: IngressRoute
|
|
||||||
metadata:
|
|
||||||
name: flowercore-web
|
|
||||||
namespace: tenant-flowercore
|
|
||||||
spec:
|
|
||||||
entryPoints:
|
|
||||||
- websecure
|
|
||||||
routes:
|
|
||||||
- match: Host(`flowercore.io`) || Host(`www.flowercore.io`)
|
|
||||||
kind: Rule
|
|
||||||
services:
|
|
||||||
- name: flowercore-web
|
|
||||||
port: 80
|
|
||||||
tls:
|
|
||||||
secretName: cf-origin-flowercore-io
|
|
||||||
|
|||||||
@@ -22,10 +22,16 @@ public sealed class FleetManifestLintTests
|
|||||||
// (bootstrap-JWT) so its allowlist is GET||HEAD||POST||OPTIONS — but
|
// (bootstrap-JWT) so its allowlist is GET||HEAD||POST||OPTIONS — but
|
||||||
// PUT/PATCH/DELETE must still 404 at the route. Anything wider than this
|
// PUT/PATCH/DELETE must still 404 at the route. Anything wider than this
|
||||||
// set should fail this lint.
|
// set should fail this lint.
|
||||||
|
//
|
||||||
|
// PUB-1 (2026-05-06): update.flowercore.io / updates.flowercore.io were
|
||||||
|
// added for the Cloudflare-proxied public Update Center edge. They use the
|
||||||
|
// same bounded read-write allowlist as the LAN pair.
|
||||||
private static readonly HashSet<string> PublicReadWriteAllowlistHosts = new(StringComparer.Ordinal)
|
private static readonly HashSet<string> PublicReadWriteAllowlistHosts = new(StringComparer.Ordinal)
|
||||||
{
|
{
|
||||||
"updatecenter.iamworkin.lan",
|
"updatecenter.iamworkin.lan",
|
||||||
"updates.iamworkin.lan",
|
"updates.iamworkin.lan",
|
||||||
|
"update.flowercore.io",
|
||||||
|
"updates.flowercore.io",
|
||||||
};
|
};
|
||||||
|
|
||||||
private static readonly HashSet<string> ApiKeyProtectedDeployments = new(StringComparer.Ordinal)
|
private static readonly HashSet<string> ApiKeyProtectedDeployments = new(StringComparer.Ordinal)
|
||||||
|
|||||||
@@ -6,7 +6,12 @@ package bluejayinfra.public_readwrite_allowlist
|
|||||||
# PUT/PATCH/DELETE must still 404 at the route. Any host in this set MUST
|
# PUT/PATCH/DELETE must still 404 at the route. Any host in this set MUST
|
||||||
# include all four required methods AND MUST NOT include any forbidden
|
# include all four required methods AND MUST NOT include any forbidden
|
||||||
# method.
|
# method.
|
||||||
public_readwrite_hosts := {"updatecenter.iamworkin.lan", "updates.iamworkin.lan"}
|
public_readwrite_hosts := {
|
||||||
|
"updatecenter.iamworkin.lan",
|
||||||
|
"updates.iamworkin.lan",
|
||||||
|
"update.flowercore.io",
|
||||||
|
"updates.flowercore.io",
|
||||||
|
}
|
||||||
|
|
||||||
required_methods := {"GET", "HEAD", "POST", "OPTIONS"}
|
required_methods := {"GET", "HEAD", "POST", "OPTIONS"}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user