Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -138,8 +138,8 @@ Use `ginkgo.By()` for major steps ONLY. Do NOT use inside `Eventually` closures:

```go
// CORRECT
ginkgo.By("waiting for cluster to become Ready")
err := h.WaitForClusterPhase(ctx, clusterID, openapi.Ready, timeout)
ginkgo.By("waiting for cluster to become Reconciled")
err := h.WaitForClusterCondition(ctx, clusterID, client.ConditionTypeReconciled, openapi.ResourceConditionStatusTrue, timeout)

// INCORRECT - never do this
Eventually(func() {
Expand All @@ -156,7 +156,7 @@ Use `Eventually` with `g.Expect()` (not `Expect()`):
Eventually(func(g Gomega) {
cluster, err := h.Client.GetCluster(ctx, clusterID)
g.Expect(err).NotTo(HaveOccurred())
g.Expect(cluster.Status.Phase).To(Equal(openapi.Ready))
g.Expect(h.HasResourceCondition(cluster.Status.Conditions, client.ConditionTypeReconciled, openapi.ResourceConditionStatusTrue)).To(BeTrue())
}, timeout, pollInterval).Should(Succeed())
```

Expand Down Expand Up @@ -205,7 +205,7 @@ Available variables: `.Random`, `.Timestamp`. See `pkg/client/payload.go`.

### DO

- **Use helper functions**: Prefer `h.WaitForClusterPhase()` over manual polling
- **Use helper functions**: Prefer `h.WaitForClusterCondition()` over manual polling
- **Use config values**: `h.Cfg.Timeouts.*` for timeouts, `h.Cfg.Polling.*` for intervals
- **Store resource IDs**: Save IDs in variables for cleanup
- **Check errors**: Use `Expect(err).NotTo(HaveOccurred())`
Expand Down Expand Up @@ -268,10 +268,10 @@ Expect(err).NotTo(HaveOccurred())
clusterID = *cluster.Id
```

### Wait for Phase
### Wait for Condition

```go
err = h.WaitForClusterPhase(ctx, clusterID, openapi.Ready, h.Cfg.Timeouts.Cluster.Ready)
err = h.WaitForClusterCondition(ctx, clusterID, client.ConditionTypeReconciled, openapi.ResourceConditionStatusTrue, h.Cfg.Timeouts.Cluster.Reconciled)
Expect(err).NotTo(HaveOccurred())
```

Expand Down
12 changes: 6 additions & 6 deletions configs/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -30,20 +30,20 @@ api:

timeouts:
cluster:
# Maximum time to wait for cluster Ready state
# Maximum time to wait for cluster Reconciled condition
# Recommended: 5m
#
# Format: Duration string (e.g., "30m", "1h", "5m")
# Can be overridden by: HYPERFLEET_TIMEOUTS_CLUSTER_READY
ready: 5m
# Can be overridden by: HYPERFLEET_TIMEOUTS_CLUSTER_RECONCILED
reconciled: 5m

nodepool:
# Maximum time to wait for nodepool Ready state
# Maximum time to wait for nodepool Reconciled condition
# Recommended: 2m
#
# Format: Duration string
# Can be overridden by: HYPERFLEET_TIMEOUTS_NODEPOOL_READY
ready: 2m
# Can be overridden by: HYPERFLEET_TIMEOUTS_NODEPOOL_RECONCILED
reconciled: 2m

adapter:
# Maximum time to wait for adapter processing
Expand Down
6 changes: 3 additions & 3 deletions docs/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ HyperFleet E2E creates ephemeral resources per test for complete isolation.
Test starts
→ Create new Helper instance
→ GetTestCluster() creates cluster via API
→ Wait for cluster Ready phase
→ Wait for cluster Reconciled condition
→ Execute test assertions
→ CleanupTestCluster() deletes cluster
Test ends
Expand All @@ -51,7 +51,7 @@ resources:
keep: false
timeouts:
cluster:
ready: 5m
reconciled: 5m
```

## Core Packages
Expand Down Expand Up @@ -127,7 +127,7 @@ Built-in Defaults (lowest priority)
- `CleanupTestNodePool(ctx, clusterID, nodePoolID)` - Delete nodepool

**Wait Operations**:
- `WaitForClusterPhase(ctx, clusterID, phase, timeout)` - Poll until cluster reaches phase
- `WaitForClusterCondition(ctx, clusterID, conditionType, expectedStatus, timeout)` - Poll until cluster condition matches
- `WaitForAllAdapterConditions(ctx, clusterID, conditions)` - Wait for adapter conditions

**Condition Validation**:
Expand Down
23 changes: 12 additions & 11 deletions docs/development.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ import (
. "github.com/onsi/gomega"

"github.com/openshift-hyperfleet/hyperfleet-e2e/pkg/api/openapi"
"github.com/openshift-hyperfleet/hyperfleet-e2e/pkg/client"
"github.com/openshift-hyperfleet/hyperfleet-e2e/pkg/helper"
"github.com/openshift-hyperfleet/hyperfleet-e2e/pkg/labels"
)
Expand All @@ -94,8 +95,8 @@ var _ = ginkgo.Describe(testName,
Expect(err).NotTo(HaveOccurred())
clusterID = *cluster.Id

ginkgo.By("waiting for cluster to become Ready")
err = h.WaitForClusterPhase(ctx, clusterID, openapi.Ready, h.Cfg.Timeouts.Cluster.Ready)
ginkgo.By("waiting for cluster to become Reconciled")
err = h.WaitForClusterCondition(ctx, clusterID, client.ConditionTypeReconciled, openapi.ResourceConditionStatusTrue, h.Cfg.Timeouts.Cluster.Reconciled)
Expect(err).NotTo(HaveOccurred())
Comment thread
coderabbitai[bot] marked this conversation as resolved.
})

Expand Down Expand Up @@ -174,8 +175,8 @@ ginkgo.BeforeEach(func() {
ginkgo.By("submitting cluster creation request")
// ... perform action

ginkgo.By("waiting for cluster to become Ready")
// ... wait for state
ginkgo.By("waiting for cluster to become Reconciled")
// ... wait for condition

ginkgo.By("verifying adapter conditions")
// ... verify conditions
Expand Down Expand Up @@ -210,24 +211,24 @@ ginkgo.AfterEach(func(ctx context.Context) {
// Basic assertions
Expect(err).NotTo(HaveOccurred())
Expect(cluster.ID).NotTo(BeEmpty())
Expect(cluster.Status.Phase).To(Equal(openapi.Ready))
Expect(h.HasResourceCondition(cluster.Status.Conditions, client.ConditionTypeReconciled, openapi.ResourceConditionStatusTrue)).To(BeTrue())

// Eventually for async operations
Eventually(func(g Gomega) {
cluster, err := h.Client.GetCluster(ctx, clusterID)
g.Expect(err).NotTo(HaveOccurred())
g.Expect(cluster.Status.Phase).To(Equal(openapi.Ready))
}, h.Cfg.Timeouts.Cluster.Ready, h.Cfg.Polling.Interval).Should(Succeed())
g.Expect(h.HasResourceCondition(cluster.Status.Conditions, client.ConditionTypeReconciled, openapi.ResourceConditionStatusTrue)).To(BeTrue())
}, h.Cfg.Timeouts.Cluster.Reconciled, h.Cfg.Polling.Interval).Should(Succeed())
```

**Important**: Inside `Eventually` closures, use `g.Expect()` instead of `Expect()`

## Using Helper Functions

### Wait for Cluster Ready
### Wait for Cluster Reconciled

```go
err = h.WaitForClusterPhase(ctx, clusterID, openapi.Ready, h.Cfg.Timeouts.Cluster.Ready)
err = h.WaitForClusterCondition(ctx, clusterID, client.ConditionTypeReconciled, openapi.ResourceConditionStatusTrue, h.Cfg.Timeouts.Cluster.Reconciled)
Expect(err).NotTo(HaveOccurred())
```

Expand Down Expand Up @@ -321,13 +322,13 @@ cluster, err := h.Client.CreateClusterFromPayload(ctx, "testdata/payloads/cluste
Expect(err).NotTo(HaveOccurred())
```

### Wait for Phase Transition
### Wait for Condition Transition

```go
Eventually(func(g Gomega) {
cluster, err := h.Client.GetCluster(ctx, clusterID)
g.Expect(err).NotTo(HaveOccurred())
g.Expect(cluster.Status.Phase).To(Equal(openapi.Ready))
g.Expect(h.HasResourceCondition(cluster.Status.Conditions, client.ConditionTypeReconciled, openapi.ResourceConditionStatusTrue)).To(BeTrue())
}, timeout, pollInterval).Should(Succeed())
```

Expand Down
2 changes: 1 addition & 1 deletion e2e/cluster/concurrent_creation.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ var _ = ginkgo.Describe("[Suite: cluster][concurrent] System can process concurr
clusterID,
client.ConditionTypeReconciled,
openapi.ResourceConditionStatusTrue,
h.Cfg.Timeouts.Cluster.Ready,
h.Cfg.Timeouts.Cluster.Reconciled,
)
Expect(err).NotTo(HaveOccurred(), "cluster %d (%s) should reach Reconciled=True", i, clusterID)

Expand Down
2 changes: 1 addition & 1 deletion e2e/cluster/crash_recovery.go
Original file line number Diff line number Diff line change
Expand Up @@ -248,5 +248,5 @@ func verifyClusterReconciled(ctx context.Context, h *helper.Helper, clusterID st
g.Expect(h.HasResourceCondition(cl.Status.Conditions,
client.ConditionTypeAvailable, openapi.ResourceConditionStatusTrue)).To(BeTrue(),
"cluster Available condition should transition to True")
}, h.Cfg.Timeouts.Cluster.Ready, h.Cfg.Polling.Interval).Should(Succeed())
}, h.Cfg.Timeouts.Cluster.Reconciled, h.Cfg.Polling.Interval).Should(Succeed())
}
4 changes: 2 additions & 2 deletions e2e/cluster/creation.go
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ var _ = ginkgo.Describe("[Suite: cluster][baseline] Cluster Resource Type Lifecy
clusterID,
client.ConditionTypeReconciled,
openapi.ResourceConditionStatusTrue,
h.Cfg.Timeouts.Cluster.Ready,
h.Cfg.Timeouts.Cluster.Reconciled,
)
Expect(err).NotTo(HaveOccurred(), "cluster Reconciled condition should transition to True")

Expand Down Expand Up @@ -243,7 +243,7 @@ var _ = ginkgo.Describe("[Suite: cluster][baseline] Cluster Resource Type Lifecy
clusterID,
client.ConditionTypeReconciled,
openapi.ResourceConditionStatusTrue,
h.Cfg.Timeouts.Cluster.Ready,
h.Cfg.Timeouts.Cluster.Reconciled,
)
Expect(err).NotTo(HaveOccurred(), "cluster Reconciled condition should transition to True before cleanup")
})
Expand Down
4 changes: 2 additions & 2 deletions e2e/nodepool/concurrent_creation.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ var _ = ginkgo.Describe("[Suite: nodepool][concurrent] Multiple nodepools can co
npID,
client.ConditionTypeReconciled,
openapi.ResourceConditionStatusTrue,
h.Cfg.Timeouts.NodePool.Ready,
h.Cfg.Timeouts.NodePool.Reconciled,
)
Expect(err).NotTo(HaveOccurred(), "nodepool %d (%s) should reach Reconciled=True", i, npID)

Expand Down Expand Up @@ -187,7 +187,7 @@ var _ = ginkgo.Describe("[Suite: nodepool][concurrent] Multiple nodepools can co
clusterID,
client.ConditionTypeReconciled,
openapi.ResourceConditionStatusTrue,
h.Cfg.Timeouts.Cluster.Ready,
h.Cfg.Timeouts.Cluster.Reconciled,
)
if err != nil {
ginkgo.GinkgoWriter.Printf("WARNING: cluster %s did not reach Reconciled state before cleanup: %v\n", clusterID, err)
Expand Down
6 changes: 3 additions & 3 deletions e2e/nodepool/creation.go
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ var _ = ginkgo.Describe("[Suite: nodepool][baseline] NodePool Resource Type Life
nodepoolID,
client.ConditionTypeReconciled,
openapi.ResourceConditionStatusTrue,
h.Cfg.Timeouts.NodePool.Ready,
h.Cfg.Timeouts.NodePool.Reconciled,
)
Expect(err).NotTo(HaveOccurred(), "nodepool Reconciled condition should transition to True")

Expand Down Expand Up @@ -245,7 +245,7 @@ var _ = ginkgo.Describe("[Suite: nodepool][baseline] NodePool Resource Type Life
nodepoolID,
client.ConditionTypeReconciled,
openapi.ResourceConditionStatusTrue,
h.Cfg.Timeouts.NodePool.Ready,
h.Cfg.Timeouts.NodePool.Reconciled,
)
Expect(err).NotTo(HaveOccurred(), "nodepool Reconciled condition should transition to True")
})
Expand All @@ -267,7 +267,7 @@ var _ = ginkgo.Describe("[Suite: nodepool][baseline] NodePool Resource Type Life
clusterID,
client.ConditionTypeReconciled,
openapi.ResourceConditionStatusTrue,
h.Cfg.Timeouts.Cluster.Ready,
h.Cfg.Timeouts.Cluster.Reconciled,
)
if err != nil {
ginkgo.GinkgoWriter.Printf("WARNING: cluster %s did not reach Reconciled state before cleanup: %v\n", clusterID, err)
Expand Down
16 changes: 8 additions & 8 deletions pkg/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -143,12 +143,12 @@ type TimeoutsConfig struct {

// ClusterTimeouts contains cluster-related timeouts
type ClusterTimeouts struct {
Ready time.Duration `yaml:"ready" mapstructure:"ready"`
Reconciled time.Duration `yaml:"reconciled" mapstructure:"reconciled"`
}

// NodePoolTimeouts contains nodepool-related timeouts
type NodePoolTimeouts struct {
Ready time.Duration `yaml:"ready" mapstructure:"ready"`
Reconciled time.Duration `yaml:"reconciled" mapstructure:"reconciled"`
}

// AdapterTimeouts contains adapter-related timeouts
Expand Down Expand Up @@ -282,11 +282,11 @@ func applyViperValues(v reflect.Value, prefix string) {
// applyDefaults applies default values for unset fields
func (c *Config) applyDefaults() {
// Apply timeout defaults
if c.Timeouts.Cluster.Ready == 0 {
c.Timeouts.Cluster.Ready = DefaultClusterReadyTimeout
if c.Timeouts.Cluster.Reconciled == 0 {
c.Timeouts.Cluster.Reconciled = DefaultClusterReconciledTimeout
}
if c.Timeouts.NodePool.Ready == 0 {
c.Timeouts.NodePool.Ready = DefaultNodePoolReadyTimeout
if c.Timeouts.NodePool.Reconciled == 0 {
c.Timeouts.NodePool.Reconciled = DefaultNodePoolReconciledTimeout
}
if c.Timeouts.Adapter.Processing == 0 {
c.Timeouts.Adapter.Processing = DefaultAdapterProcessingTimeout
Expand Down Expand Up @@ -421,8 +421,8 @@ func (c *Config) Display() {
"gcp_project_id", c.GCPProjectID,
"output_dir", c.OutputDir,
"testdata_dir", c.TestDataDir,
"timeout_cluster_ready", c.Timeouts.Cluster.Ready,
"timeout_nodepool_ready", c.Timeouts.NodePool.Ready,
"timeout_cluster_reconciled", c.Timeouts.Cluster.Reconciled,
"timeout_nodepool_reconciled", c.Timeouts.NodePool.Reconciled,
"timeout_adapter_processing", c.Timeouts.Adapter.Processing,
"polling_interval", c.Polling.Interval,
"log_level", c.Log.Level,
Expand Down
8 changes: 4 additions & 4 deletions pkg/config/defaults.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,11 @@ const (

// Default timeout values
const (
// DefaultClusterReadyTimeout is the default timeout for waiting for a cluster to become ready
DefaultClusterReadyTimeout = 30 * time.Minute
// DefaultClusterReconciledTimeout is the default timeout for waiting for a cluster to become reconciled
DefaultClusterReconciledTimeout = 30 * time.Minute

// DefaultNodePoolReadyTimeout is the default timeout for waiting for a nodepool to become ready
DefaultNodePoolReadyTimeout = 30 * time.Minute
// DefaultNodePoolReconciledTimeout is the default timeout for waiting for a nodepool to become reconciled
DefaultNodePoolReconciledTimeout = 30 * time.Minute

// DefaultAdapterProcessingTimeout is the default timeout for waiting for adapter conditions
DefaultAdapterProcessingTimeout = 5 * time.Minute
Expand Down
Loading