feat: RFC-001 cloud-native optimization (M1+M2+M3+M4 + docs)#103
Merged
Conversation
Add a priority="0" project_spec block referencing the gospec Go backend SDLC standard (architecture, coding, API, observability, delivery, ops red lines). Existing openskills index preserved at priority="1". Also list gospec in the available skills table. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Four-milestone proposal scoping the Frontier / Frontlas cloud-native hardening: M1 P0 bug fixes, M2 production baseline (CRD v1alpha2 + graceful shutdown + non-root), M3 observability (/healthz /readyz /metrics + Prometheus + slog), M4 delivery (Makefile single entry + frontlas Helm + image signing + Conditions status). Status: draft. Each milestone ships as its own PR. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1. TLS cert/key volume mounted the CA secret instead of the cert/key
secret, so mTLS pods received an empty certificate path.
(frontiercluster_deployment.go:114)
2. getEBCAFromSecret silently swallowed read errors as ("", nil),
masking apiserver / RBAC failures behind a misleading "CA not
found" path. Now wraps with %w and propagates.
(frontiercluster_tls.go)
3. Operator-managed TLS secret names lacked a separator
("<fc>edgebound-..." → "<fc>-edgebound-..."), producing names
that clashed with project naming style.
(frontiercluster_fields.go)
4. Frontlas frontier-plane port was hard-coded to 40012 with no
way to override it via CRD. Add ControlPlane.FrontierPlanePort
(default 40012) and wire it through a new FRONTLAS_FRONTIERPLANE_PORT
env var into pkg/frontlas/config so the listen addr follows.
(types.go, fields.go, deployment.go, frontlas/config/config.go)
Add 7 unit tests covering all four fixes (api/v1alpha1 +
internal/controller). go.mod toolchain version directive realigned
by `go mod tidy` to satisfy local Go 1.24; no dependency changes.
Refs: docs/rfc/RFC-001-cloud-native-optimization.md (M1)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
…wordSecret Add a generic PodOverrides struct that lets users tune the most common production-grade Pod-level fields without breaking v1alpha1 — Resources, NodeSelector, Tolerations, TopologySpreadConstraints, Affinity, PriorityClassName, ServiceAccountName, ImagePullSecrets, ImagePullPolicy, Annotations, Labels, PodSecurityContext, ContainerSecurityContext, TerminationGracePeriodSeconds, LivenessProbe, ReadinessProbe, Lifecycle. Embed PodOverrides as Frontier.Pod and Frontlas.Pod. The legacy NodeAffinity field stays for backwards compat but Pod.Affinity, when set, fully replaces it. Add Redis.PasswordSecret (corev1.SecretKeySelector). Takes precedence over the existing Redis.Password plaintext when both are set; the plaintext field is kept for backwards compat but marked deprecated. Resolves the gospec security red line "密钥禁止进代码仓库 / 镜像 / 日志" — operator no longer needs to materialize the password into spec env. zz_generated.deepcopy.go and CRD schema regenerated by controller-gen. v1alpha1 → v1alpha1: existing CRs keep working (all new fields optional). Version bump to v1beta1 deferred to M4 to bundle with Status conditions. Refs: docs/rfc/RFC-001-cloud-native-optimization.md (M2) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Wire PodOverrides through container_builder + podtemplatespec_builder so the Reconciler can render every covered field. The builders gain typed setters for Resources, LivenessProbe, Lifecycle, SecurityContext (container), TopologySpreadConstraints, PriorityClassName, ServiceAccountName, ImagePullSecrets, PodSecurityContext, full Affinity, plus annotation and label merge helpers. Add internal/controller/podoverrides.go: per-component default tables (frontierDefaults / frontlasDefaults) plus pickXxx helpers that resolve override-or-default for each field. The defaults encode several gospec red lines and operational best practices: - ImagePullPolicy: IfNotPresent (was PullAlways) - Pod / Container SecurityContext: runAsNonRoot=true, RunAsUser=65532, drop ALL capabilities, no privilege escalation, RuntimeDefault seccomp - TerminationGracePeriodSeconds: 60 for frontier (long-lived edge connections), 30 for frontlas - Lifecycle.PreStop: sleep 10, gives kube-proxy time to remove the pod from Service Endpoints before SIGTERM - LivenessProbe and ReadinessProbe: TCP socket on listener ports for frontier (HTTP /readyz endpoint comes in M3); Frontlas keeps its /cluster/v1/health readiness and gains a TCP liveness - PodAntiAffinity: PreferredDuringScheduling (was Required, which made small clusters fail to schedule when replicas exceeded node count) Redis password env switches to corev1.EnvVarSource SecretKeyRef when Redis.PasswordSecret is set; the legacy plaintext path still works when only Redis.Password is provided. Frontier deployment also gains FRONTIER_DRAIN_SECONDS env, computed as terminationGracePeriodSeconds - 10, to be consumed by the binary's shutdown handler (next commit). 12 unit tests cover the picker logic, default safety properties (nonRoot, drop ALL caps, preferred not required affinity), and Redis env routing. Refs: docs/rfc/RFC-001-cloud-native-optimization.md (M2) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
When SIGTERM arrives, sleep for FRONTIER_DRAIN_SECONDS (default 30s, operator sets it dynamically based on terminationGracePeriodSeconds) before invoking frontier.Close(). This is the minimum-useful step toward graceful shutdown for long-lived edge connections: it gives kube-proxy time to fully propagate the endpoint removal that the preStop sleep started, and lets in-flight edge sessions finish naturally instead of being torn down the instant the signal hits. Full deregister-from-Frontlas + close-listeners-but-keep-existing flow is deferred to M3, where it will compose with the upcoming /readyz endpoint (readiness flips to 503 -> kube removes endpoint -> drain). Refs: docs/rfc/RFC-001-cloud-native-optimization.md (M2) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Add a system user (UID 65532) to both Alpine images and switch USER to it. UID 65532 matches the operator's default PodSecurityContext.RunAsUser, so runAsNonRoot=true now passes for stock images without users having to override SecurityContext. Resolves the gospec security red line "容器以非 root 用户运行". Refs: docs/rfc/RFC-001-cloud-native-optimization.md (M2) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two amendments to RFC-001 captured in the changelog table: - M2 stays on v1alpha1 with optional fields rather than introducing v1alpha2. Multi-version CRDs require a conversion strategy; None needs schema parity (defeating the new fields), Webhook adds an in-cluster service + cert lifecycle to M2 scope. Version bump moved to M4 alongside Status conditions, going straight to v1beta1. - M2 through M4 ship on a single feat/rfc-001-m1 branch instead of one PR per milestone, per user direction after M1 verification. Refs: docs/rfc/RFC-001-cloud-native-optimization.md Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ns schema
CRD-level changes that improve day-to-day kubectl experience and lay
the groundwork for modern Kubernetes-style status reporting (M4.3).
- shortNames: fc, fcs (kubectl get fc just works)
- categories: frontier (kubectl get all -l frontier picks it up)
- printcolumns: Phase, FrontierReady, FrontlasReady, Age, Message
(Message is priority=1 so it only shows in -o wide)
- Status now exposes:
* conditions []metav1.Condition (Available/Progressing/Degraded)
* observedGeneration
* frontierReadyReplicas / frontlasReadyReplicas
- Phase + Message kept for backwards compat and the printcolumn,
but tagged Deprecated in favor of Conditions
zz_generated.deepcopy.go and CRD YAML regenerated by controller-gen.
Refs: docs/rfc/RFC-001-cloud-native-optimization.md (M4.3)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…icas + Events
Reconciler now writes a full modern K8s status alongside the legacy Phase:
- Available / Progressing / Degraded Conditions, set in lockstep with Phase
transitions. Status changes refresh LastTransitionTime; no-op writes
preserve it (idiomatic K8s condition behavior).
- ObservedGeneration written on every status update so consumers can detect
stale status.
- FrontierReadyReplicas / FrontlasReadyReplicas pulled from the underlying
Deployment.Status.ReadyReplicas. ensureDeployment now returns a
deploymentReadiness struct instead of a bare bool so the controller can
feed both numbers into status regardless of pending/failed/running path.
EventRecorder ("frontier-operator") emits Kubernetes Events at meaningful
state transitions:
- ServiceEnsureFailed (warning) — Service reconcile error
- TLSEnsureFailed (warning) — TLS Secret reconcile error
- DeploymentEnsureFailed (warning) — Deployment apply error
- Available (normal) — fired once when the cluster first becomes Running,
not on every successful reconcile
RBAC: + events:create;patch (the standard EventRecorder verbs).
Refs: docs/rfc/RFC-001-cloud-native-optimization.md (M4.1, M4.2)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Resolves the gospec red line "all external services must expose
/healthz, /readyz, /metrics" for both frontier and frontlas (M3.1, M3.2).
New shared package pkg/observability:
- Server with three endpoints on a single configurable port.
* /healthz — liveness; always 200 if process responds
* /readyz — readiness; calls a pluggable ReadinessFn, returns 503 with
a one-line reason when not ready
* /metrics — Prometheus default registry (Go runtime + process collectors
auto-included; business code can add custom collectors via promauto)
- Atomic readiness function pointer so business code can flip readiness
state at any point in the lifecycle (start, register-with-Frontlas,
shutdown).
- Graceful Shutdown(timeout) wired into the parent component's Close().
Wiring:
- Frontier: default port 0.0.0.0:9091. Configurable via the new
spec.observability block in frontier.yaml. Default ReadinessFn is "always
ready"; future iterations can plug in Frontlas registration state.
- Frontlas: default port 0.0.0.0:9092. ReadinessFn pings Redis with a 2s
timeout — if Redis is unreachable, /readyz returns 503 (kube removes
the pod from Endpoints, frontier nodes route around it).
- Frontlas Dao gains a Ping(ctx) helper that wraps RDS.Ping.
Existing /cluster/v1/health gRPC-gateway endpoint stays for backwards
compat with the operator's frontlas readinessProbe.
Adds github.com/prometheus/client_golang v1.23.2.
Refs: docs/rfc/RFC-001-cloud-native-optimization.md (M3)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replaces the 68-line stub at /docs/operator with a full reference covering
the M1+M2+M3+M4 deliverables.
Twelve sections:
1. Overview — two-tier architecture, what one CR gets you
2. Installation — install.yaml + RBAC walkthrough
3. Quick start — minimal CR + verification commands
4. CRD field reference — three tables (Frontier, Frontlas, PodOverrides)
documenting every spec field, type, default, and intent
5. Common scenarios — mTLS, production resources/scheduling, private
registry, Prometheus + cert-manager annotations, SecurityContext
override for legacy images
6. Status & Conditions — Available/Progressing/Degraded semantics
7. Observability endpoints — /healthz, /readyz, /metrics ports for
both frontier and frontlas
8. Common kubectl operations — short name fc, patches, JSON path tricks
9. Operator behavior — reconcile order, owner references, graceful
shutdown env, event types
10. Troubleshooting — six failure modes mapped to root cause + remediation
11. Known limitations — no kubectl scale, alpha API, Helm gaps,
no validation webhook
12. Roadmap — pointer to RFC-001 in the repo
Verified with `npx next build` — page generates statically alongside the
existing /docs/* routes.
Refs: docs/rfc/RFC-001-cloud-native-optimization.md (user-facing
deliverable for the cloud-native optimization initiative)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
8 tasks
singchia
added a commit
that referenced
this pull request
May 2, 2026
First release after RFC-001 cloud-native optimization (PRs #103, #104, #105): - M1 — operator P0 bug fixes (TLS volume, Secret naming, frontier-plane port) - M2 — production baseline (PodOverrides, nonRoot images, Redis SecretRef, graceful shutdown, default probes / preStop / grace) - M3 — observability HTTP server (/healthz, /readyz, /metrics) on frontier:9091 and frontlas:9092 - M4 — Status Conditions, EventRecorder, CRD shortName fc, printcolumns - M5 — full-stack Helm chart (frontier + frontlas + bundled bitnami/redis), published at https://singchia.github.io/frontier Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
启动 RFC-001 云原生部署优化专项 的第一个里程碑(M1)。本 PR 包含三块独立 commit:
docs/rfc/RFC-001-cloud-native-optimization.md,提出 M1-M4 四阶段计划。M1 修复的 4 个 bug
pkg/operator/internal/controller/frontiercluster_deployment.go:114getEBCAFromSecret把 Secret 读取错误吞成("", nil),apiserver / RBAC 失败被掩盖成"CA 不存在"pkg/operator/internal/controller/frontiercluster_tls.go<fc>edgebound-...→<fc>-edgebound-...)pkg/operator/api/v1alpha1/frontiercluster_fields.goControlPlane.FrontierPlanePort+FRONTLAS_FRONTIERPLANE_PORTenv,下沉到 frontlas configtypes.go/fields.go/deployment.go/pkg/frontlas/config/config.goTest plan
go build ./...(root + operator 模块)go vet ./...TestEBTLSOperatorSecretNames_HaveDashSeparatorTestFrontlasServicePort_FrontierPlanePort(4 子测试)TestGetEBCAFromSecret_PropagatesReadError/_MissingCAKey/_Happycd pkg/operator && go test ./...全绿kubectl apply验证)frontierPlanePort字段)对 v1alpha1 老用户向后兼容(optional 字段,不填走默认 40012)兼容性说明
OwnerReference不会被立即 GC,但不再被引用。可在kubectl get fc删除时被清理;也可手动删除。建议在 CHANGELOG 提示。关联
docs/rfc/RFC-001-cloud-native-optimization.md🤖 Generated with Claude Code