Conversation
✅ Deploy Preview for kamaji-documentation canceled.
|
prometherion
left a comment
There was a problem hiding this comment.
I'm a bit lost here: it seems the proposed public API Server address is not pushed into the kubeadm functions, such as:
kamaji/internal/resources/kubeadm_config.go
Line 106 in 99fb71e
and the following one for kubeconfig
kamaji/internal/resources/kubeconfig.go
Line 234 in 99fb71e
|
@rossigee I'm sorry the rebase will be painful, let me know if I can take care of that! |
2bea3f5 to
eaffca1
Compare
✅ Deploy Preview for kamaji-documentation canceled.
|
922610a to
566d3c9
Compare
d3e5503 to
44e305e
Compare
44e305e to
3574521
Compare
|
Hi @prometherion - I've addressed your review comments from the previous iteration:
The branch has been rebased onto latest master, and lint/tests pass. Ready for re-review! |
|
Hi @prometherion - the review comments have been addressed:
Rebased on latest master, lint/tests pass. Ready for re-review! |
|
Updated - now uses PublicAPIServerAddress in both kubeadm config and kubeconfigs per original review request. Ready for re-review! |
b5291db to
143a262
Compare
|
Hi @prometherion - I've addressed your review comments:
The branch has been rebased and cleaned up. Ready for re-review! |
|
The fix has been updated to use cluster-local SVC addresses (e.g., tcp-name.namespace.svc:6443) for controller-manager and scheduler kubeconfigs, instead of the public API Server address. This avoids unnecessary network hops through the load balancer while keeping traffic within the cluster. The test now expects the server URL to have suffix |
|
I've addressed the review comments:
The e2e test has been updated to expect suffix in the server URL for controller-manager and scheduler kubeconfigs. |
834d2fe to
55bfc57
Compare
|
@prometherion rebased onto current A correction. My comments of 2026-07-24/25 said Two things on this branch that need your call, both now spelled out in the description:
Also: this branch carries the fix for the flaky |
1d16cdf to
4813b0c
Compare
Add PreGeneratedCertificatesSpec to allow users to provide their own certificates instead of auto-generating them. Key features: - CertificateReference and KeyReference types for referencing secrets - PreGeneratedCertificatesSpec with CA, APIServer, KubeletClient, FrontProxyCA, FrontProxyClient, and ServiceAccount fields - Webhook validation for certificate/key references - Unit and e2e tests for the feature Signed-off-by: Ross Golder <ross@golder.org>
The webhook validates that pre-generated certificates exist in secrets, but the certificate generation code wasn't reading from them. This adds the usePreGeneratedCACertificate method to actually use the user-provided certificates instead of generating new ones.
…ources - ca_certificate.go: usePreGeneratedCACertificate - api_server_certificate.go: usePreGeneratedAPIServerCertificate - api_server_kubelet_client_certificate.go: usePreGeneratedKubeletClientCertificate - front_proxy_ca_certificate.go: usePreGeneratedFrontProxyCACertificate - front-proxy-client-certificate.go: usePreGeneratedFrontProxyClientCertificate - sa_certificate.go: usePreGeneratedSACertificate
The pre-generated certificates validation webhook was not being invoked because the handler was not registered in the webhook chain.
…rtificates The SecretNamespace field has been removed from CertificateReference and KeyReference types since cross-namespace secret references require complex RBAC setup that violates the principle of least privilege. Secrets referenced in pre-generated certificates must now be in the same namespace as the TenantControlPlane. Updated: - api/v1alpha1/tenantcontrolplane_types.go (removed SecretNamespace fields) - internal/resources/*.go (removed secretNamespace handling) - internal/webhook/handlers/tcp_pregenerated_certs.go (removed validation) - internal/webhook/handlers/tcp_pregenerated_certs_test.go (updated test)
Add PreGeneratedCertificatesSpec to allow users to provide their own certificates instead of auto-generating them. Key features: - CertificateReference and KeyReference types for referencing secrets - PreGeneratedCertificatesSpec with CA, APIServer, KubeletClient, FrontProxyCA, FrontProxyClient, and ServiceAccount fields - Webhook validation for certificate/key references - Unit and e2e tests for the feature Signed-off-by: Ross Golder <ross@golder.org>
The webhook validates that pre-generated certificates exist in secrets, but the certificate generation code wasn't reading from them. This adds the usePreGeneratedCACertificate method to actually use the user-provided certificates instead of generating new ones.
…ources - ca_certificate.go: usePreGeneratedCACertificate - api_server_certificate.go: usePreGeneratedAPIServerCertificate - api_server_kubelet_client_certificate.go: usePreGeneratedKubeletClientCertificate - front_proxy_ca_certificate.go: usePreGeneratedFrontProxyCACertificate - front-proxy-client-certificate.go: usePreGeneratedFrontProxyClientCertificate - sa_certificate.go: usePreGeneratedSACertificate
…rtificates The SecretNamespace field has been removed from CertificateReference and KeyReference types since cross-namespace secret references require complex RBAC setup that violates the principle of least privilege. Secrets referenced in pre-generated certificates must now be in the same namespace as the TenantControlPlane. Updated: - api/v1alpha1/tenantcontrolplane_types.go (removed SecretNamespace fields) - internal/resources/*.go (removed secretNamespace handling) - internal/webhook/handlers/tcp_pregenerated_certs.go (removed validation) - internal/webhook/handlers/tcp_pregenerated_certs_test.go (updated test)
Add RBACBootstrapSpec with AdminGroups and AdminUsers defaults. Bootstrap field added to TenantControlPlaneSpec. This enables RBAC bootstrap functionality with sensible defaults: - AdminUsers defaults to ["kubernetes-admin"] - AdminGroups defaults to ["system:masters"] Signed-off-by: Ross Golder <ross@golder.org>
This adds the RBAC bootstrap controller implementation that creates ClusterRoleBindings for admin users and groups specified in the TenantControlPlane's BootstrapSpec. Changes: - Add BootstrapStatus and RBACBootstrapStatus types to track status - Add RBACBootstrap resource that implements the Resource interface - Create ClusterRoleBinding with cluster-admin role for specified users/groups - Add unit tests for RBAC bootstrap functionality - Integrate RBAC bootstrap resource into controller resources The controller creates ClusterRoleBindings in the tenant cluster to grant cluster-admin privileges to specified users and groups at bootstrap time.
…atement, blank line before return
The ClusterRoleBinding was never created. Define() set a placeholder name and mutate() reassigned it, but controllerutil.CreateOrUpdate captures the object key before invoking the MutateFn and rejects a change to it, so every reconcile failed with "MutateFn cannot mutate object name and/or object namespace". CleanUp() resolved the same placeholder and so never found the object it was meant to delete. The name is now computed in Define() from the Tenant Control Plane name alone. Deriving it from the spec contents meant that editing adminUsers or adminGroups renamed the object and orphaned the previously created binding. RBACBootstrapSpec.Enabled becomes a *bool. With a plain bool, omitempty dropped an explicit false during serialization and the API server re-applied the true default, leaving no way to disable the feature. The repeated nil checks are replaced by TenantControlPlane.IsRBACBootstrapEnabled(). The existing unit tests all called mutate() directly, so no test exercised the code path that runs in production. Added specs covering creation, idempotence, naming stability across spec edits, unset and explicit-false Enabled, and cleanup, plus an e2e spec asserting the binding lands in the Tenant cluster and is removed when the feature is disabled. Documents the feature at docs/content/guides/rbac-bootstrap.md.
…rap test The spec used a ClusterIP service with the kind node address and the default port, so the generated kubeconfig pointed at https://<kind-node>:6443 - the management cluster's own API server. The tenant CA correctly refused to verify that certificate and the spec failed with an x509 unknown-authority error before it ever reached an RBAC assertion. Uses a NodePort on a random high port, as the datastore migration spec does, so the tenant API server is actually reachable from the test process. Also scopes the "exactly one binding" assertion to the component label: Kamaji creates other ClusterRoleBindings in the tenant cluster, from the kubeadm phases and from the addons, that carry the same control plane name label.
…udget TriggerChannel is a one-shot, best-effort notification with no retry for status-only TenantControlPlane changes. Its 10s send deadline was shorter than controller-runtime's own default 2-minute CacheSyncTimeout, which every registered controller (including the receiving one) blocks on during startup. Under a slow initial cache sync, the notification could be silently dropped forever, since nothing else re-triggers it. This surfaced as e2e/tcp_migration_test.go timing out waiting for the kamaji-freeze ValidatingWebhookConfiguration after the Kubernetes version bump to v1.35.7 (commit 8a19328), which broadened the tenant apiserver's discoverable API surface and made cache-sync take measurably longer.
When using preGeneratedCertificates, the kubeconfig checksum calculation included config.Checksum() which depends on cert SANs added from the control plane Service IPs (ClusterIPs and LoadBalancer ingress IPs). As the Service gets provisioned with IPs, the cert SANs change, causing config.Checksum() to change, which triggers kubeconfig regeneration even though the actual certificates are fixed. This creates an infinite reconciliation loop where: 1. Kubeconfig is regenerated → component checksums change 2. Deployment pod-template-hash changes → new ReplicaSet created 3. Next reconcile triggered → go to step 1 For pregenerated certificates, the actual certs are immutable, so changes to cert SANs should not trigger kubeconfig regeneration. Fix by excluding config.Checksum() from the kubeconfig checksum calculation when preGeneratedCertificates are specified. Fixes TenantControlPlanes stuck in "Provisioning" status with pregenerated certificates, enabling them to transition to "Ready" once service provisioning completes.
IsKubeconfigCAValid used strict bytes.Equal between the certificate embedded in the kubeconfig and the CA stored in the CA secret. When the CA secret holds the certificate in a different encoding (e.g. an openssl x509 -text dump), the comparison always failed, forcing the kubeconfig to be regenerated on every reconciliation and causing a continuous regeneration loop. Compare the parsed x509 certificates instead, which is encoding agnostic and matches the proof check performed in IsKubeconfigValid.
- Add missing Resources field to KonnectivityAgentSpec struct for resource allocation control on konnectivity agents - Fix unreachable code in DeclaredControlPlaneAddress() by removing dead return statement after switch default case - Remove unused getLoadBalancerAddress() function and related error import - Fix import ordering in trigger_channel.go (gci/gofmt linter issues) - Add blank line before break statement (nlreturn linter requirement) All tests passing, golangci-lint: 0 issues
07aa0ef to
73b4972
Compare
Regenerate CRD definitions to reflect changes from source code: - Remove dead code and unused functions - Add Resources field to KonnectivityAgentSpec - Update derived CRD schemas accordingly
Add Public API Server Address Support
Overview
Adds an optional
publicAPIServerAddressfield toServiceSpec, allowing a DNS hostname to be advertised externally instead of a LoadBalancer IP. This lets the advertised endpoint match a certificate SAN and avoids x509 errors for external clients.Related: #1110 (split management/tenant addressing — this PR covers the DNS-hostname surface for external clients, complementing
networkProfile.advertiseAddress), #1019 (LoadBalancer ingress hostname / AWS CNAME use case).Key Changes
API
PublicAPIServerAddresstoServiceSpec(optional, no default).PublicControlPlaneAddress(), returning the public address when set and falling back toAssignedControlPlaneAddress()otherwise.Makefile's pinnedcontroller-gen.Where the public address is (and is not) used
Following review feedback, the public address is used only on externally-facing surfaces:
internal/resources/kubeadm_config.go— sets the control plane endpoint and appends the public address to the certificate SANs.DeclaredControlPlaneAddress()deliberately never returns the public address: kubeadm requiresLocalAPIEndpoint.AdvertiseAddressto be a real IP.The scheduler and controller-manager kubeconfigs continue to use the loopback address, as requested in review. The earlier
replaceServerURLWithPublicAddresspost-processing was reverted in 6e23040 and no longer exists. The only remaining change ininternal/resources/kubeconfig.gois a no-op refactor collapsing two identicalswitcharms into one.Tests & docs
PublicControlPlaneAddress()(default/custom port, set/unset, error cases).e2e/tcp_public_address_test.go, with per-test TCP cleanup.docs/content/guides/public-api-server-address.mdplus generated API reference.Items needing maintainer input
Two changes on this branch go beyond the feature itself. Both are called out explicitly rather than buried in the diff, and I am happy to drop either.
1.
DeclaredControlPlaneAddress()was rewritten — this is a behaviour change.On
master, for aLoadBalancerservice it returns the LoadBalancer ingress IP, rejects ingress hostnames (with a documented rationale), and returnsNonExposedLoadBalancerError/MissingValidIPErrorwhile no IP is assigned — errors thatShouldReconcileErrorBeIgnoreduses to requeue.On this branch it returns
svc.Spec.ClusterIP, falling back to<name>.<namespace>.svc. Consequences:I do not think this belongs in this PR. Unless you want it kept, I will revert
DeclaredControlPlaneAddress()to themasterimplementation and rebase.2.
TriggerChannelTimeoutraised from 30s to 3 minutes (controllers/utils/trigger_channel.go).This predates #1258 and survived the rebase onto it. The rationale: the send must outlast controller-runtime's
CacheSyncTimeout(default 2 minutes), which every registered controller blocks on at startup — otherwise a status-only notification can be dropped with nothing to re-trigger it. With #1258's buffered channel this only bites once the buffer is full, so it is now a narrower concern. If you would rather keep the 30s you just merged, say so and I will drop it.Usage
CI
All checks green on the current head, including the full
Kubernetese2e suite.This branch also carries the fix for the flaky
tcp_migration_test.gowebhook-wait, widening theEventuallyfrom 1 to 5 minutes. CI run 30307341733 showed the manager going silent for 2m16s with nothing logged — CPU starvation on the 2-vCPU runner, where many control planes, datastores and apiservers compete for two cores, not a Kamaji bug. The same spec currently fails on #1252 and #1251, which lack this fix. Happy to split it into its own PR if that unblocks them sooner.Breaking changes
The
publicAPIServerAddressfield itself is optional and defaults to existing behaviour. TheDeclaredControlPlaneAddress()rewrite described above is a behaviour change for LoadBalancer services; my proposal is to revert it.