Skip to content

feat(sync): preserve not-ready clusters in local kubeconfig - #87

Open
onuryilmaz wants to merge 1 commit into
mainfrom
feat/preserve-notready-on-sync
Open

onuryilmaz wants to merge 1 commit into
mainfrom
feat/preserve-notready-on-sync

Conversation

@onuryilmaz

Copy link
Copy Markdown
Contributor

Summary

  • Not-ready clusters are no longer deleted from the local kubeconfig on sync. A transient Ready=False condition (e.g. during a Greenhouse controller upgrade or a missing secret key) previously caused cloudctl to remove the local entry, making the cluster unreachable.
  • Existing local entries for not-ready clusters are preserved via stubs injected into the server-side config before the stale-cleanup pass — no content is overwritten.
  • A new --include-not-ready flag fully merges not-ready cluster spec data, for cases where the CRD spec is known-good but the controller is temporarily degraded.

Motivation

During the Greenhouse v0.18.0 → v0.19.0 rollout (PR cloudoperators/greenhouse#2245), all 208 ClusterKubeconfig resources in the sci namespace report Ready=False with reason KubeconfigMissing. Their spec.kubeconfig is fully populated. Running cloudctl sync in this window would delete all 208 entries from users' local kubeconfigs, cutting off cluster access at the worst possible time.

Behaviour change

Scenario Before After
Cluster Ready=False, exists in local kubeconfig Deleted on next sync Preserved
Cluster Ready=False, not yet in local kubeconfig Not added Not added (unchanged)
--include-not-ready flag N/A Spec data merged even if Ready=False
Cluster genuinely removed from Greenhouse Deleted Deleted (unchanged)

Test plan

  • TestInjectNotReadyStubs_PreservesLocalEntries — stub injection keeps existing local entries
  • TestInjectNotReadyStubs_SkipsUnknownClusters — no stubs for clusters not yet in local config
  • TestIncludeNotReady_MergesSpecData — --include-not-ready merges spec data from not-ready CRDs
  • All existing cmd tests pass

Previously, clusters whose Ready condition was False were silently
removed from the local kubeconfig on the next sync. This meant a
transient controller failure (e.g. missing secret key during a
Greenhouse upgrade) would cut off access to the cluster, making it
impossible to reach and investigate the issue.

This change keeps not-ready entries intact in the local kubeconfig:
- notReady clusters that already exist locally are preserved via stub
  entries injected into the server-side config before the stale-cleanup
  pass in mergeKubeconfig. The stubs prevent deletion without touching
  the local entry's content.
- A new --include-not-ready flag opts into fully merging not-ready
  cluster spec data, useful when the controller is temporarily degraded
  but the CRD spec is known-good (e.g. during a Greenhouse rollout).

Signed-off-by: onuryilmaz <onur.yilmaz@sap.com>
@onuryilmaz
onuryilmaz requested review from a team as code owners November 7, 2026 14:09
Copilot AI balanced review requested due to automatic review settings November 7, 2026 14:09

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The sync logic can abort on prefixed references, create dangling references, and delete managed entries when resource and context names differ.

6 open issues
What changed in this PR

This PR aims to preserve local Kubernetes access during transient Greenhouse readiness failures by keeping existing kubeconfig entries for not-ready clusters.

Changes:

  • Adds local-entry stubs intended to prevent stale cleanup from deleting not-ready clusters.
  • Introduces --include-not-ready to opt into merging their spec data.
  • Adds tests for stub creation, unknown-cluster handling, and not-ready spec conversion.
File Description
cmd/​sync.go Adds preservation logic, the opt-in flag, and updated command help.
cmd/​sync_test.go Adds tests for preservation stubs and not-ready spec merging.

Review effort: Balanced

Comment thread cmd/sync.go
continue
}
// Context name matches the CKC name (contexts are not prefixed).
if ctxName != ckc.Name {
Comment thread cmd/sync.go
}
// Inject stub context.
if _, exists := serverConfig.Contexts[ctxName]; !exists {
serverConfig.Contexts[ctxName] = localCtx.DeepCopy()
Comment thread cmd/sync.go
syncCmd.Flags().StringVar(&kubeloginTokenCacheDir, "kubelogin-token-cache-dir", defaultTokenCacheDir, "Directory for OIDC token cache files")

syncCmd.Flags().BoolVar(&dryRun, "dry-run", false, "Preview changes without writing to the kubeconfig file")
syncCmd.Flags().BoolVar(&includeNotReady, "include-not-ready", false, "Also merge clusters whose Ready condition is False (spec data must be present)")
Comment thread cmd/sync.go

serverConfig, err := buildIncomingKubeconfig(ready)
toMerge := ready
if includeNotReady {
Comment thread cmd/sync.go
serverConfig, err := buildIncomingKubeconfig(ready)
toMerge := ready
if includeNotReady {
toMerge = append(toMerge, notReady...)
Comment thread cmd/sync.go
// Look for any local managed context whose name matches this CKC, then copy its
// referenced cluster/authinfo names into serverConfig so cleanup skips them.
for ctxName, localCtx := range localConfig.Contexts {
if localCtx == nil {
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants