Repository navigation
Implementation of basic controller logic - #21
Conversation
- sts, svc and cm are created by operator - "Initialized" status is being set and updated - etcd cluster successfully bootstraps
| image: controller:latest | ||
| imagePullPolicy: Never |
There was a problem hiding this comment.
Suggest use full image path ghcr.io/aenix-io/controller:latest and imagePullPolicy: IfNotPresent for reuse yaml in static manifests and helm.
There was a problem hiding this comment.
It should be ghcr.io/aenix-io/etcd-operator/controller:latest or ghcr.io/aenix-io/etcd-operator:latest
There was a problem hiding this comment.
Or ghcr.io/aenix-io/etcd-operator/etcd-operator:latest
Oleg Kunitsyn (@hiddenmarten) could you comment on this, please?
There was a problem hiding this comment.
Andrei Kvapil (@kvaps)
Sure, I think it's better to use a simple structure of image tag, that will correspond with a GitHub repo path (It helps to avoid confusion):
{registry}/{organisation}/{application}:{versionBySemVer}
So, as a result:
ghcr.io/aenix-io/etcd-operator:v0.0.0
…ck#21) * feat: TLS support via BYO Secret refs (client + peer planes) Adds spec.tls with independently optional .client and .peer subtrees that reference user-provided Secrets. Within the client plane, presence of operatorClientSecretRef is the implicit mTLS toggle; peer is always mTLS. The whole subtree is CEL-locked immutable post-create — toggling TLS in place is a rolling restart that has to land on the operator's etcd client and every member Pod in lockstep, which v1 doesn't perform. The cluster controller mirrors the propagated secret refs onto each EtcdMember at creation so the member controller is self-sufficient. EtcdClientFactory now takes a *tls.Config and the operator builds one from the cluster's serverSecretRef.ca.crt (verify-server) and, in mTLS mode, operatorClientSecretRef.tls.{crt,key} (client identity). buildPod mounts the referenced Secrets, wires the etcd --cert-file/--key-file/--trusted- ca-file/--client-cert-auth/--peer-* flags, and switches the kubelet readiness probe to /health on a plaintext :2381 metrics listener (kubelet HTTPGet probes dial the Pod IP and can't present a client cert). Two operator-visible constraints land in the docs because etcd enforces them implicitly: - The server cert MUST carry clientAuth in its EKU alongside serverAuth. The grpc-gateway loopback self-dials the client API presenting the server cert as a client cert, and Go's crypto/tls enforces ExtKeyUsageClientAuth server-side during verification. Same reason the server-issuing CA must be present in --trusted-ca-file (the seeded ca.crt of serverSecretRef doubles as the trust bundle). - The peer cert MUST cover *.<cluster>.<ns>.svc.<cluster-domain> as a DNS SAN, not only the shortname wildcard. etcd's peer-mTLS verifier reverse-DNS-looks-up the connecting peer's IP and matches the resulting fully-qualified hostname against the cert's SANs; without the FQDN wildcard the seed silently EOFs every incoming peer connection and joining members crashloop on discovery failed. Tests: buildPod permutations (plaintext / server-TLS-only / mTLS / peer- TLS), deriveMemberTLS cluster→member projection, ensurePod precheck for missing Secret, plus CEL envtest cases covering both add-from-nil and remove-to-nil rejection and subtree-immutability. Existing tests updated for the new helper signatures. Co-Authored-By: Claude Opus 4.7 <[email protected]> Signed-off-by: Timofei Larkin <[email protected]> * fixup: surface TLS-config errors + correct rotation doc + cp typo Three small follow-ups: - The promote-after-converged branch swallowed both buildOperatorTLSConfig and EtcdClientFactory errors silently. Log them and fall through to updateStatus (matching the comment's intent and scaleUp's behaviour). A missing/typo'd operator-client Secret was previously a steady stream of silent 10s requeues with no operator log line. - The Phase 1 rotation procedure said "etcd reads the cert files at startup and won't pick up the change". That's wrong: Go's crypto/tls re-reads --cert-file / --key-file per handshake via GetCertificate, so server and peer certs hot-swap without a Pod restart. What does need a restart is the trust bundle (--trusted-ca-file / --peer-trusted-ca-file), which gets baked into an x509.CertPool at config time. Operator-side material rotates on the next reconcile because buildOperatorTLSConfig rebuilds the *tls.Config each time. Replaced the two-step blanket procedure with a per-material table. - operations.md's kubectl cp recipe used `<ns>/<some-client-cert-dir>` as the source path, which isn't valid kubectl cp syntax. Rewrote to a concrete local-directory source. Co-Authored-By: Claude Opus 4.7 <[email protected]> Signed-off-by: Timofei Larkin <[email protected]> --------- Signed-off-by: Timofei Larkin <[email protected]> Co-authored-by: Claude Opus 4.7 <[email protected]>
Controller now can successfully bootstrap etcd cluster :)
Controller creates Service, ConfigMap and StatefulSet based on EtcdCluster CR
Only EmptyDir storage is supported
Initialized status is being set and updated
fixes #13