Skip to content

Commit ff6fde4

Browse files
committed
feat(sync): only sync kubeconfig with Ready status
1 parent 4031c5b commit ff6fde4

File tree

3 files changed

+97
-1
lines changed

3 files changed

+97
-1
lines changed

cmd/sync.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import (
1313
"maps"
1414
"strings"
1515

16+
greenhousemetav1alpha1 "github.com/cloudoperators/greenhouse/api/meta/v1alpha1"
1617
"github.com/cloudoperators/greenhouse/api/v1alpha1"
1718
"github.com/spf13/cobra"
1819
"k8s.io/apimachinery/pkg/runtime"
@@ -91,6 +92,9 @@ func runSync(cmd *cobra.Command, args []string) error {
9192
}
9293
clusterKubeconfigs = list.Items
9394
}
95+
// Filter only Ready ClusterKubeconfigs
96+
clusterKubeconfigs = filterReady(clusterKubeconfigs)
97+
9498
if len(clusterKubeconfigs) == 0 {
9599
log.Println("No ClusterKubeconfigs found to sync.")
96100
return nil
@@ -124,6 +128,21 @@ func runSync(cmd *cobra.Command, args []string) error {
124128
return nil
125129
}
126130

131+
// filterReady returns only ClusterKubeconfigs that have Ready condition set to True.
132+
func filterReady(items []v1alpha1.ClusterKubeconfig) []v1alpha1.ClusterKubeconfig {
133+
if len(items) == 0 {
134+
return items
135+
}
136+
eligible := make([]v1alpha1.ClusterKubeconfig, 0, len(items))
137+
for _, ckc := range items {
138+
cond := ckc.Status.Conditions.GetConditionByType(greenhousemetav1alpha1.ReadyCondition)
139+
if cond == nil || cond.IsTrue() {
140+
eligible = append(eligible, ckc)
141+
}
142+
}
143+
return eligible
144+
}
145+
127146
// buildIncomingKubeconfig converts the list of typed ClusterKubeconfig objects
128147
// into a clientcmdapi.Config.
129148
func buildIncomingKubeconfig(items []v1alpha1.ClusterKubeconfig) (*clientcmdapi.Config, error) {

cmd/sync_test.go

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,10 @@ import (
77
"bytes"
88
"testing"
99

10+
greenhousemetav1alpha1 "github.com/cloudoperators/greenhouse/api/meta/v1alpha1"
11+
greenhousev1alpha1 "github.com/cloudoperators/greenhouse/api/v1alpha1"
1012
. "github.com/onsi/gomega"
13+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
1114
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
1215
)
1316

@@ -145,3 +148,62 @@ func TestGenerateAuthInfoKey_CertBased(t *testing.T) {
145148
g.Expect(ka).To(Equal(kb))
146149
g.Expect(bytes.HasPrefix([]byte(ka), []byte("cert:"))).To(BeTrue(), "cert-based key should have cert: prefix")
147150
}
151+
152+
func TestFilterReady_IncludesOnlyReady(t *testing.T) {
153+
g := NewWithT(t)
154+
155+
readyCkc := greenhousev1alpha1.ClusterKubeconfig{
156+
ObjectMeta: metav1.ObjectMeta{Name: "ready-cluster"},
157+
Status: greenhousev1alpha1.ClusterKubeconfigStatus{},
158+
}
159+
// Set Ready=True
160+
readyCkc.Status.Conditions.SetConditions(
161+
greenhousemetav1alpha1.TrueCondition(
162+
greenhousemetav1alpha1.ReadyCondition,
163+
"TestReason",
164+
"ready",
165+
),
166+
)
167+
168+
notReadyCkc := greenhousev1alpha1.ClusterKubeconfig{
169+
ObjectMeta: metav1.ObjectMeta{Name: "notready-cluster"},
170+
Status: greenhousev1alpha1.ClusterKubeconfigStatus{},
171+
}
172+
// Set Ready=False
173+
notReadyCkc.Status.Conditions.SetConditions(
174+
greenhousemetav1alpha1.FalseCondition(
175+
greenhousemetav1alpha1.ReadyCondition,
176+
"TestReason",
177+
"not ready",
178+
),
179+
)
180+
181+
noCondCkc := greenhousev1alpha1.ClusterKubeconfig{
182+
ObjectMeta: metav1.ObjectMeta{Name: "nocond-cluster"},
183+
}
184+
185+
out := filterReady([]greenhousev1alpha1.ClusterKubeconfig{readyCkc, notReadyCkc, noCondCkc})
186+
g.Expect(out).To(HaveLen(1))
187+
g.Expect(out[0].Name).To(Equal("ready-cluster"))
188+
}
189+
190+
func TestFilterReady_EmptyAndNoneReady(t *testing.T) {
191+
g := NewWithT(t)
192+
193+
// Empty input
194+
out := filterReady(nil)
195+
g.Expect(out).To(BeNil())
196+
197+
// None ready input
198+
a := greenhousev1alpha1.ClusterKubeconfig{ObjectMeta: metav1.ObjectMeta{Name: "a"}}
199+
a.Status.Conditions.SetConditions(
200+
greenhousemetav1alpha1.FalseCondition(
201+
greenhousemetav1alpha1.ReadyCondition,
202+
"TestReason",
203+
"not ready",
204+
),
205+
)
206+
b := greenhousev1alpha1.ClusterKubeconfig{ObjectMeta: metav1.ObjectMeta{Name: "b"}}
207+
out2 := filterReady([]greenhousev1alpha1.ClusterKubeconfig{a, b})
208+
g.Expect(out2).To(HaveLen(0))
209+
}

e2e/sync_test.go

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,8 @@ func TestE2E_Sync(t *testing.T) {
3232
crFile := filepath.Join(os.TempDir(), "clusterkubeconfig-e2e.yaml")
3333

3434
// Prefer applying the CRD from the repository path that matches the provided spec (greenhouse.sap group).
35-
remoteCRD := "https://raw.githubusercontent.com/cloudoperators/greenhouse/refs/heads/main/charts/manager/crds/greenhouse.sap_clusterkubeconfigs.yaml"
35+
// Use the correct raw URL format: /{owner}/{repo}/{branch}/{path}
36+
remoteCRD := "https://raw.githubusercontent.com/cloudoperators/greenhouse/main/charts/manager/crds/greenhouse.sap_clusterkubeconfigs.yaml"
3637

3738
// Try local cache first (optional), otherwise fall back to the remote CRD above.
3839
appliedCRD := false
@@ -103,6 +104,20 @@ spec:
103104
t.Fatalf("apply CR failed: %v (stderr: %s)", err, stderr)
104105
}
105106

107+
// Since cloudctl sync only considers ClusterKubeconfigs with Ready=True,
108+
// patch the status of our demo resource accordingly and wait until it's reflected.
109+
// Note: We don't have a controller in this e2e setup, so we set the status manually.
110+
if _, stderr, err := runCmd(
111+
"kubectl", "--kubeconfig", kubeconfig,
112+
"-n", ns,
113+
"patch", "clusterkubeconfig", "demo",
114+
"--type", "merge",
115+
"--subresource", "status",
116+
"-p", `{"status":{"conditions":[{"type":"Ready","status":"True","reason":"E2E","message":"ready"}]}}`,
117+
); err != nil {
118+
t.Fatalf("patch status failed: %v (stderr: %s)", err, stderr)
119+
}
120+
106121
// Target kubeconfig file
107122
targetKubeconfig := filepath.Join(os.TempDir(), "e2e-sync-target-kubeconfig")
108123
createEmptyKubeconfigFile(t, targetKubeconfig)

0 commit comments

Comments
 (0)