Skip to content
Open
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
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,6 @@
.cache

_out/

# Test binaries
*.test
15 changes: 15 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,21 @@ test-e2e-autoscaler: ## Run openshift specific e2e test including autoscaler
test-e2e-periodic-autoscaler: ## Run openshift specific periodic e2e test including autoscaler
hack/ci-integration.sh $(GINKGO_ARGS) --label-filter='periodic&&autoscaler' -p

.PHONY: test-mapi
test-mapi: ## Run MAPI authoritative testing (MAPI auth, CAPI mirrors)
TEST_BACKEND_TYPE=MAPI TEST_AUTHORITATIVE_API=MAPI hack/ci-integration.sh $(GINKGO_ARGS) --label-filter='unified||mapi'

.PHONY: test-capi
test-capi: ## Run pure CAPI testing (no MAPI involvement)
TEST_BACKEND_TYPE=CAPI TEST_AUTHORITATIVE_API=CAPI hack/ci-integration.sh $(GINKGO_ARGS) --label-filter='unified'

.PHONY: test-mapi-with-capi-auth
test-mapi-with-capi-auth: ## Run tests with MAPI backend and CAPI authority (conversion layer)
TEST_BACKEND_TYPE=MAPI TEST_AUTHORITATIVE_API=CAPI hack/ci-integration.sh $(GINKGO_ARGS) --label-filter='unified'

.PHONY: test-all
test-all: test-mapi test-capi test-mapi-with-capi-auth ## Run all unified framework test scenarios
Comment on lines +96 to +109
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot Apr 8, 2026

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# First, let's confirm the exact content of the Makefile at lines 96-109
sed -n '96,109p' Makefile

Repository: openshift/cluster-api-actuator-pkg

Length of output: 840


🏁 Script executed:

# Search for references to these test targets and label filters
rg 'test-mapi|test-capi|label-filter' -A 2 -B 2

Repository: openshift/cluster-api-actuator-pkg

Length of output: 8592


🏁 Script executed:

# Check the hack/ci-integration.sh to understand how label-filter is used
head -50 hack/ci-integration.sh

Repository: openshift/cluster-api-actuator-pkg

Length of output: 502


🏁 Script executed:

# Search for test definitions to understand what unified, mapi labels mean
fd -e go | xargs grep -l 'Label.*unified\|Label.*mapi' 2>/dev/null | head -5

Repository: openshift/cluster-api-actuator-pkg

Length of output: 180


🏁 Script executed:

# Look for any documentation or comments about the test matrix strategy
rg 'test matrix|parity|backend.*authority' -i

Repository: openshift/cluster-api-actuator-pkg

Length of output: 20847


🏁 Script executed:

# Check the ginkgo-labels.go file to understand test labels
cat pkg/framework/ginkgo-labels.go

Repository: openshift/cluster-api-actuator-pkg

Length of output: 1762


🏁 Script executed:

# Search for tests with "mapi" label to see what's included
rg 'Label.*mapi|Label.*unified' -A 2 -B 2

Repository: openshift/cluster-api-actuator-pkg

Length of output: 2555


🏁 Script executed:

# Check if there's any documentation or comments about the test matrix design
rg 'test-mapi|test-capi|test-all' -B 5 -A 5 | head -100

Repository: openshift/cluster-api-actuator-pkg

Length of output: 3860


Keep the backend matrix on the same suite.

test-mapi runs unified||mapi while test-capi and test-mapi-with-capi-auth both run only unified, so test-all does not compare the same test suite across backends. This breaks parity interpretation and allows legacy MAPI-only tests to skew the matrix results.

Suggested change
 .PHONY: test-mapi
 test-mapi: ## Run MAPI authoritative testing (MAPI auth, CAPI mirrors)
-	TEST_BACKEND_TYPE=MAPI TEST_AUTHORITATIVE_API=MAPI hack/ci-integration.sh $(GINKGO_ARGS) --label-filter='unified||mapi'
+	TEST_BACKEND_TYPE=MAPI TEST_AUTHORITATIVE_API=MAPI hack/ci-integration.sh $(GINKGO_ARGS) --label-filter='unified'

If legacy MAPI coverage is needed, keep it under a separate target instead of including it in this parity matrix.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
.PHONY: test-mapi
test-mapi: ## Run MAPI authoritative testing (MAPI auth, CAPI mirrors)
TEST_BACKEND_TYPE=MAPI TEST_AUTHORITATIVE_API=MAPI hack/ci-integration.sh $(GINKGO_ARGS) --label-filter='unified||mapi'
.PHONY: test-capi
test-capi: ## Run pure CAPI testing (no MAPI involvement)
TEST_BACKEND_TYPE=CAPI TEST_AUTHORITATIVE_API=CAPI hack/ci-integration.sh $(GINKGO_ARGS) --label-filter='unified'
.PHONY: test-mapi-with-capi-auth
test-mapi-with-capi-auth: ## Run tests with MAPI backend and CAPI authority (conversion layer)
TEST_BACKEND_TYPE=MAPI TEST_AUTHORITATIVE_API=CAPI hack/ci-integration.sh $(GINKGO_ARGS) --label-filter='unified'
.PHONY: test-all
test-all: test-mapi test-capi test-mapi-with-capi-auth ## Run all unified framework test scenarios
.PHONY: test-mapi
test-mapi: ## Run MAPI authoritative testing (MAPI auth, CAPI mirrors)
TEST_BACKEND_TYPE=MAPI TEST_AUTHORITATIVE_API=MAPI hack/ci-integration.sh $(GINKGO_ARGS) --label-filter='unified'
.PHONY: test-capi
test-capi: ## Run pure CAPI testing (no MAPI involvement)
TEST_BACKEND_TYPE=CAPI TEST_AUTHORITATIVE_API=CAPI hack/ci-integration.sh $(GINKGO_ARGS) --label-filter='unified'
.PHONY: test-mapi-with-capi-auth
test-mapi-with-capi-auth: ## Run tests with MAPI backend and CAPI authority (conversion layer)
TEST_BACKEND_TYPE=MAPI TEST_AUTHORITATIVE_API=CAPI hack/ci-integration.sh $(GINKGO_ARGS) --label-filter='unified'
.PHONY: test-all
test-all: test-mapi test-capi test-mapi-with-capi-auth ## Run all unified framework test scenarios
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Makefile` around lines 96 - 109, The test-matrix is inconsistent because
test-mapi uses --label-filter='unified||mapi' while test-capi and
test-mapi-with-capi-auth use 'unified', causing mismatched suites; change the
Makefile so all three matrix targets (test-mapi, test-capi,
test-mapi-with-capi-auth) use the same --label-filter='unified' to ensure
parity, and if you still need legacy-only MAPI runs extract the 'unified||mapi'
filter into a separate PHONY target (e.g., legacy-mapi) instead of including it
in the matrix.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I chose to make test-capi and test-mapi both running unified and backend-specific tests in order to support full backend testing.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

@sunzhaohua2, understood — that's a valid design goal. One thing worth noting for consistency: if the intent is full backend coverage per target, then test-capi would also need --label-filter='unified||capi' (assuming a capi label exists or gets added) to achieve the same depth of coverage on the CAPI side. As it stands, MAPI gets more test coverage than CAPI in test-all, which is an asymmetry. If that's intentional (e.g., CAPI doesn't have backend-specific tests yet), it's fine to leave as-is — just worth documenting.

Happy to leave this as-is given your explanation.


.PHONY: help
help:
@grep -E '^[a-zA-Z/0-9_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}'
5 changes: 5 additions & 0 deletions pkg/e2e_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (

"k8s.io/client-go/kubernetes/scheme"
"k8s.io/klog"
"sigs.k8s.io/controller-runtime/pkg/envtest/komega"

osconfigv1 "github.com/openshift/api/config/v1"
machinev1 "github.com/openshift/api/machine/v1beta1"
Expand All @@ -27,6 +28,7 @@ import (
_ "github.com/openshift/cluster-api-actuator-pkg/pkg/mapi"
_ "github.com/openshift/cluster-api-actuator-pkg/pkg/operators"
_ "github.com/openshift/cluster-api-actuator-pkg/pkg/providers"
_ "github.com/openshift/cluster-api-actuator-pkg/pkg/unified/e2e"
)

func init() {
Expand Down Expand Up @@ -75,6 +77,9 @@ var _ = BeforeSuite(func() {
client, err := framework.LoadClient()
Expect(err).ToNot(HaveOccurred())

// Set komega client for all tests
komega.SetClient(client)

ctx := framework.GetContext()

platform, err := framework.GetPlatform(ctx, client)
Expand Down
3 changes: 3 additions & 0 deletions pkg/framework/ginkgo-labels.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,7 @@ var (

// LabelConnectedOnly indicates that the test can run in a connection cluster only.
LabelConnectedOnly = ginkgo.Label("connected-only")

// LabelUnified applies to tests using the unified MAPI/CAPI testing framework.
LabelUnified = ginkgo.Label("unified")
)
14 changes: 14 additions & 0 deletions pkg/framework/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,3 +113,17 @@ func GetControlPlaneHostAndPort(ctx context.Context, cl client.Client) (string,

return apiURL.Hostname(), int32(port), nil
}

// MergeLabels merges multiple label mappings into a single map.
// Later maps override earlier ones for conflicting keys.
func MergeLabels(labelMaps ...map[string]string) map[string]string {
result := make(map[string]string)

for _, labels := range labelMaps {
for k, v := range labels {
result[k] = v
}
}

return result
}
75 changes: 75 additions & 0 deletions pkg/unified/backends/backend_interface.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package backends

import (
"context"
"fmt"

configv1 "github.com/openshift/api/config/v1"
corev1 "k8s.io/api/core/v1"
runtimeclient "sigs.k8s.io/controller-runtime/pkg/client"

"github.com/openshift/cluster-api-actuator-pkg/pkg/unified/config"
)

// MachineBackend defines the abstract interface for machine backends.
type MachineBackend interface {
GetBackendType() config.BackendType
GetAuthoritativeAPI() config.BackendType
CreateMachineTemplate(ctx context.Context, client runtimeclient.Client, platform configv1.PlatformType, params BackendMachineTemplateParams) (interface{}, error)
DeleteMachineTemplate(ctx context.Context, client runtimeclient.Client, template interface{}) error
CreateMachineSet(ctx context.Context, client runtimeclient.Client, params BackendMachineSetParams) (interface{}, error)
DeleteMachineSet(ctx context.Context, client runtimeclient.Client, machineSet interface{}) error
WaitForMachineSetDeleted(ctx context.Context, client runtimeclient.Client, machineSet interface{}) error
WaitForMachinesRunning(ctx context.Context, client runtimeclient.Client, machineSet interface{}) error
GetMachineSetStatus(ctx context.Context, client runtimeclient.Client, machineSet interface{}) (*MachineSetStatus, error)
GetNodesFromMachineSet(ctx context.Context, client runtimeclient.Client, machineSet interface{}) ([]corev1.Node, error)
}

// BackendMachineSetParams defines common parameters for creating machine sets.
type BackendMachineSetParams struct {
Name string
Replicas int32
Labels map[string]string
Annotations map[string]string
Template interface{}
FailureDomain string
// AuthoritativeAPI specifies which API should be authoritative for this MachineSet
AuthoritativeAPI config.BackendType
}

// BackendMachineTemplateParams defines common parameters for creating machine templates.
type BackendMachineTemplateParams struct {
Name string
Platform configv1.PlatformType
Spec interface{}
}

// MachineSetStatus defines common structure for machine set status.
type MachineSetStatus struct {
Replicas int32
AvailableReplicas int32
ReadyReplicas int32
AuthoritativeAPI string
}

// NewBackend creates appropriate backend instance based on configuration.
func NewBackend(backendType config.BackendType, authoritativeAPI config.BackendType) (MachineBackend, error) {
switch backendType {
case config.BackendTypeMAPI:
return NewMAPIBackend(authoritativeAPI), nil
case config.BackendTypeCAPI:
return NewCAPIBackend(authoritativeAPI), nil
default:
return nil, fmt.Errorf("unsupported backend type: %s", backendType)
}
}

// NewMAPIBackend creates a new MAPI backend instance.
func NewMAPIBackend(authoritativeAPI config.BackendType) MachineBackend {
return &mapiBackend{backendType: config.BackendTypeMAPI, authoritativeAPI: authoritativeAPI}
}

// NewCAPIBackend creates a new CAPI backend instance.
func NewCAPIBackend(authoritativeAPI config.BackendType) MachineBackend {
return &capiBackend{backendType: config.BackendTypeCAPI, authoritativeAPI: authoritativeAPI}
}
Loading