Skip to content

Commit 1e9517d

Browse files
committed
Add new check for cluster adapter status and adjust cases
1 parent 1ff635e commit 1e9517d

4 files changed

Lines changed: 193 additions & 147 deletions

File tree

configs/config.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ timeouts:
5151
#
5252
# Format: Duration string
5353
# Can be overridden by: HYPERFLEET_TIMEOUTS_ADAPTER_PROCESSING
54-
processing: 1m
54+
processing: 2m
5555

5656
# ============================================================================
5757
# Polling Configuration

e2e/cluster/creation.go

Lines changed: 130 additions & 115 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package cluster
22

33
import (
44
"context"
5+
"fmt"
56
"time"
67

78
"github.com/onsi/ginkgo/v2"
@@ -13,8 +14,8 @@ import (
1314
"github.com/openshift-hyperfleet/hyperfleet-e2e/pkg/labels"
1415
)
1516

16-
var _ = ginkgo.Describe("Cluster Resource Type Lifecycle",
17-
ginkgo.Label(labels.Tier0, "baseline"),
17+
var _ = ginkgo.Describe("[Suite: cluster][baseline] Cluster Resource Type Lifecycle",
18+
ginkgo.Label(labels.Tier0),
1819
func() {
1920
var h *helper.Helper
2021
var clusterID string
@@ -158,6 +159,20 @@ var _ = ginkgo.Describe("Cluster Resource Type Lifecycle",
158159
"cluster condition %s should have observed_generation=1 for new creation request", condition.Type)
159160
}
160161
}
162+
163+
// Validate adapter-specific conditions in cluster status
164+
// Each required adapter should report its own condition type (e.g., ClNamespaceSuccessful, ClJobSuccessful)
165+
for _, adapterName := range h.Cfg.Adapters.Cluster {
166+
expectedCondType := h.AdapterNameToClusterConditionType(adapterName)
167+
hasAdapterCondition := h.HasResourceCondition(
168+
finalCluster.Status.Conditions,
169+
expectedCondType,
170+
openapi.ResourceConditionStatusTrue,
171+
)
172+
Expect(hasAdapterCondition).To(BeTrue(),
173+
"cluster should have %s=True condition for adapter %s",
174+
expectedCondType, adapterName)
175+
}
161176
})
162177
})
163178

@@ -211,8 +226,7 @@ var _ = ginkgo.Describe("Cluster Resource Type Lifecycle",
211226
for _, adapterName := range h.Cfg.Adapters.Cluster {
212227
verifier, exists := adapterResourceVerifiers[adapterName]
213228
if !exists {
214-
ginkgo.GinkgoWriter.Printf("Warning: no K8s resource verifier defined for adapter %s, skipping\n", adapterName)
215-
continue
229+
ginkgo.Fail(fmt.Sprintf("No K8s resource verifier defined for adapter %s - test configuration error", adapterName))
216230
}
217231

218232
ginkgo.By("Verifying Kubernetes resource for adapter: " + adapterName)
@@ -226,138 +240,139 @@ var _ = ginkgo.Describe("Cluster Resource Type Lifecycle",
226240
})
227241

228242
ginkgo.Describe("Adapter Dependency Relationships Workflow Validation", func() {
229-
// This test validates adapter dependency relationships:
230-
// 1. During cl-job execution: cl-deployment Applied=False and Available=Unknown (never False)
231-
// 2. After cl-job completes: cl-deployment can proceed (no validation on Available during execution)
232-
// 3. Eventually: cl-deployment Available becomes True (success)
233-
ginkgo.It("should validate cl-deployment dependency on cl-job with comprehensive condition checks",
234-
func(ctx context.Context) {
235-
pollingInterval := "1s"
236-
237-
ginkgo.By("Verify cl-deployment initial state and dependency waiting behavior")
238-
// Capture cl-deployment's initial waiting state
239-
// Poll until cl-deployment appears in the statuses
240-
var foundInitialState bool
241-
Eventually(func(g Gomega) {
243+
// This test validates adapter dependency relationships:
244+
// 1. During cl-job execution: cl-deployment Applied=False and Available=Unknown (never False)
245+
// 2. After cl-job completes: cl-deployment can proceed (no validation on Available during execution)
246+
// 3. Eventually: cl-deployment Available becomes True (success)
247+
ginkgo.It("should validate cl-deployment dependency on cl-job with comprehensive condition checks",
248+
func(ctx context.Context) {
249+
pollingInterval := "1s"
250+
251+
ginkgo.By("Verify cl-deployment initial state and dependency waiting behavior")
252+
// Capture cl-deployment's initial waiting state
253+
// Poll until cl-deployment appears in the statuses
254+
var foundInitialState bool
255+
Eventually(func(g Gomega) {
256+
foundInitialState = false
257+
statuses, err := h.Client.GetClusterStatuses(ctx, clusterID)
258+
g.Expect(err).NotTo(HaveOccurred(), "failed to get cluster statuses")
259+
260+
// Find cl-deployment adapter
261+
for _, adapter := range statuses.Items {
262+
if adapter.Adapter == "cl-deployment" {
263+
foundInitialState = true
264+
265+
// Verify initial waiting state
266+
hasAppliedFalse := h.HasAdapterCondition(
267+
adapter.Conditions,
268+
client.ConditionTypeApplied,
269+
openapi.AdapterConditionStatusFalse,
270+
)
271+
g.Expect(hasAppliedFalse).To(BeTrue(),
272+
"cl-deployment Applied condition should be False initially (waiting for cl-job)")
273+
274+
hasAvailableUnknown := h.HasAdapterCondition(
275+
adapter.Conditions,
276+
client.ConditionTypeAvailable,
277+
openapi.AdapterConditionStatusUnknown,
278+
)
279+
g.Expect(hasAvailableUnknown).To(BeTrue(),
280+
"cl-deployment Available condition should be Unknown initially (waiting for cl-job)")
281+
282+
hasHealthTrue := h.HasAdapterCondition(
283+
adapter.Conditions,
284+
client.ConditionTypeHealth,
285+
openapi.AdapterConditionStatusTrue,
286+
)
287+
g.Expect(hasHealthTrue).To(BeTrue(),
288+
"cl-deployment Health condition should be True (adapter is healthy, just waiting)")
289+
290+
return
291+
}
292+
}
293+
g.Expect(foundInitialState).To(BeTrue(), "cl-deployment adapter should appear in statuses")
294+
}, h.Cfg.Timeouts.Adapter.Processing, pollingInterval).Should(Succeed())
295+
296+
ginkgo.By("Verify dependency: cl-deployment Applied=False and Available=Unknown during cl-job execution")
297+
// Poll continuously until cl-deployment Available becomes True:
298+
// - Before cl-job Available=True: verify cl-deployment Applied=False and Available!=False
299+
// - After cl-job Available=True: only wait for cl-deployment Available=True
300+
// - Exit when cl-deployment Available=True
301+
timeout := time.After(h.Cfg.Timeouts.Adapter.Processing)
302+
ticker := time.NewTicker(1 * time.Second)
303+
defer ticker.Stop()
304+
305+
var jobAvailableReachedTrue bool
306+
307+
pollLoop:
308+
for {
309+
select {
310+
case <-timeout:
311+
ginkgo.Fail("Timed out waiting for cl-deployment Available condition to become True")
312+
case <-ticker.C:
242313
statuses, err := h.Client.GetClusterStatuses(ctx, clusterID)
243-
g.Expect(err).NotTo(HaveOccurred(), "failed to get cluster statuses")
314+
Expect(err).NotTo(HaveOccurred(), "failed to get cluster statuses")
315+
316+
var jobAvailableTrue bool
317+
var deploymentAppliedTrue bool
318+
var deploymentAvailableTrue bool
319+
var deploymentAvailableFalse bool
244320

245-
// Find cl-deployment adapter
246321
for _, adapter := range statuses.Items {
322+
if adapter.Adapter == "cl-job" {
323+
jobAvailableTrue = h.HasAdapterCondition(
324+
adapter.Conditions,
325+
client.ConditionTypeAvailable,
326+
openapi.AdapterConditionStatusTrue,
327+
)
328+
}
247329
if adapter.Adapter == "cl-deployment" {
248-
foundInitialState = true
249-
250-
// Verify initial waiting state
251-
hasAppliedFalse := h.HasAdapterCondition(
330+
deploymentAppliedTrue = h.HasAdapterCondition(
252331
adapter.Conditions,
253332
client.ConditionTypeApplied,
254-
openapi.AdapterConditionStatusFalse,
333+
openapi.AdapterConditionStatusTrue,
255334
)
256-
g.Expect(hasAppliedFalse).To(BeTrue(),
257-
"cl-deployment Applied condition should be False initially (waiting for cl-job)")
258-
259-
hasAvailableUnknown := h.HasAdapterCondition(
335+
deploymentAvailableTrue = h.HasAdapterCondition(
260336
adapter.Conditions,
261337
client.ConditionTypeAvailable,
262-
openapi.AdapterConditionStatusUnknown,
338+
openapi.AdapterConditionStatusTrue,
263339
)
264-
g.Expect(hasAvailableUnknown).To(BeTrue(),
265-
"cl-deployment Available condition should be Unknown initially (waiting for cl-job)")
266-
267-
hasHealthTrue := h.HasAdapterCondition(
340+
deploymentAvailableFalse = h.HasAdapterCondition(
268341
adapter.Conditions,
269-
client.ConditionTypeHealth,
270-
openapi.AdapterConditionStatusTrue,
342+
client.ConditionTypeAvailable,
343+
openapi.AdapterConditionStatusFalse,
271344
)
272-
g.Expect(hasHealthTrue).To(BeTrue(),
273-
"cl-deployment Health condition should be True (adapter is healthy, just waiting)")
274-
275-
return
276345
}
277346
}
278-
g.Expect(foundInitialState).To(BeTrue(), "cl-deployment adapter should appear in statuses")
279-
}, h.Cfg.Timeouts.Adapter.Processing, pollingInterval).Should(Succeed())
280-
281-
ginkgo.By("Verify dependency: cl-deployment Applied=False and Available=Unknown during cl-job execution")
282-
// Poll continuously until cl-deployment Available becomes True:
283-
// - Before cl-job Available=True: verify cl-deployment Applied=False and Available!=False
284-
// - After cl-job Available=True: only wait for cl-deployment Available=True
285-
// - Exit when cl-deployment Available=True
286-
timeout := time.After(h.Cfg.Timeouts.Adapter.Processing)
287-
ticker := time.NewTicker(1 * time.Second)
288-
defer ticker.Stop()
289-
290-
var jobAvailableReachedTrue bool
291-
292-
pollLoop:
293-
for {
294-
select {
295-
case <-timeout:
296-
ginkgo.Fail("Timed out waiting for cl-deployment Available condition to become True")
297-
case <-ticker.C:
298-
statuses, err := h.Client.GetClusterStatuses(ctx, clusterID)
299-
Expect(err).NotTo(HaveOccurred(), "failed to get cluster statuses")
300-
301-
var jobAvailableTrue bool
302-
var deploymentAppliedTrue bool
303-
var deploymentAvailableTrue bool
304-
var deploymentAvailableFalse bool
305-
306-
for _, adapter := range statuses.Items {
307-
if adapter.Adapter == "cl-job" {
308-
jobAvailableTrue = h.HasAdapterCondition(
309-
adapter.Conditions,
310-
client.ConditionTypeAvailable,
311-
openapi.AdapterConditionStatusTrue,
312-
)
313-
}
314-
if adapter.Adapter == "cl-deployment" {
315-
deploymentAppliedTrue = h.HasAdapterCondition(
316-
adapter.Conditions,
317-
client.ConditionTypeApplied,
318-
openapi.AdapterConditionStatusTrue,
319-
)
320-
deploymentAvailableTrue = h.HasAdapterCondition(
321-
adapter.Conditions,
322-
client.ConditionTypeAvailable,
323-
openapi.AdapterConditionStatusTrue,
324-
)
325-
deploymentAvailableFalse = h.HasAdapterCondition(
326-
adapter.Conditions,
327-
client.ConditionTypeAvailable,
328-
openapi.AdapterConditionStatusFalse,
329-
)
330-
}
331-
}
332347

333-
// Track when cl-job Available first becomes True
334-
if jobAvailableTrue && !jobAvailableReachedTrue {
335-
jobAvailableReachedTrue = true
336-
ginkgo.GinkgoWriter.Printf("cl-job Available=True reached, cl-deployment can now proceed\n")
337-
}
348+
// Track when cl-job Available first becomes True
349+
if jobAvailableTrue && !jobAvailableReachedTrue {
350+
jobAvailableReachedTrue = true
351+
ginkgo.GinkgoWriter.Printf("cl-job Available=True reached, cl-deployment can now proceed\n")
352+
}
338353

339-
// Validate dependency enforcement: only check while cl-job is still executing
340-
if !jobAvailableReachedTrue {
341-
// cl-deployment should not start applying resources until cl-job completes
342-
Expect(deploymentAppliedTrue).To(BeFalse(),
343-
"cl-deployment Applied should remain False while cl-job Available is not True yet")
354+
// Validate dependency enforcement: only check while cl-job is still executing
355+
if !jobAvailableReachedTrue {
356+
// cl-deployment should not start applying resources until cl-job completes
357+
Expect(deploymentAppliedTrue).To(BeFalse(),
358+
"cl-deployment Applied should remain False while cl-job Available is not True yet")
344359

345-
// cl-deployment Available should stay Unknown (not False) while waiting for cl-job
346-
Expect(deploymentAvailableFalse).To(BeFalse(),
347-
"cl-deployment Available must be Unknown (not False) during cl-job execution")
348-
}
360+
// cl-deployment Available should stay Unknown (not False) while waiting for cl-job
361+
Expect(deploymentAvailableFalse).To(BeFalse(),
362+
"cl-deployment Available must be Unknown (not False) during cl-job execution")
363+
}
349364

350-
// Exit when cl-deployment Available becomes True (workflow complete)
351-
if deploymentAvailableTrue {
352-
ginkgo.GinkgoWriter.Printf("cl-deployment Available=True reached, dependency validation successful\n")
353-
break pollLoop
354-
}
365+
// Exit when cl-deployment Available becomes True (workflow complete)
366+
if deploymentAvailableTrue {
367+
ginkgo.GinkgoWriter.Printf("cl-deployment Available=True reached, dependency validation successful\n")
368+
break pollLoop
355369
}
356370
}
371+
}
357372

358-
ginkgo.GinkgoWriter.Printf("Successfully validated cl-deployment dependency on cl-job with correct condition transitions\n")
359-
})
360-
})
373+
ginkgo.GinkgoWriter.Printf("Successfully validated cl-deployment dependency on cl-job with correct condition transitions\n")
374+
})
375+
})
361376

362377
ginkgo.AfterEach(func(ctx context.Context) {
363378
// Skip cleanup if helper not initialized or no cluster created

pkg/helper/k8s.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,9 @@ func (h *Helper) VerifyJobComplete(ctx context.Context, namespace string, expect
131131
if !ok || len(items) == 0 {
132132
return fmt.Errorf("no job found in namespace %s with selector %s", namespace, labelSelector)
133133
}
134+
if len(items) > 1 {
135+
return fmt.Errorf("multiple jobs (%d) found in namespace %s with selector %s - expected exactly one", len(items), namespace, labelSelector)
136+
}
134137

135138
// Get the first job (should be only one)
136139
jobData, ok := items[0].(map[string]interface{})
@@ -218,6 +221,9 @@ func (h *Helper) VerifyDeploymentAvailable(ctx context.Context, namespace string
218221
if !ok || len(items) == 0 {
219222
return fmt.Errorf("no deployment found in namespace %s with selector %s", namespace, labelSelector)
220223
}
224+
if len(items) > 1 {
225+
return fmt.Errorf("multiple deployments (%d) found in namespace %s with selector %s - expected exactly one", len(items), namespace, labelSelector)
226+
}
221227

222228
// Get the first deployment (should be only one)
223229
deployData, ok := items[0].(map[string]interface{})

0 commit comments

Comments
 (0)