-
Notifications
You must be signed in to change notification settings - Fork 152
CNTRLPLANE-2270: OTE scaffolding and E2E tests #492
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| package main | ||
|
|
||
| import ( | ||
| _ "github.com/openshift/build-machinery-go" | ||
| _ "github.com/openshift/cluster-config-operator/test/e2e" | ||
| ) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,73 +1,91 @@ | ||
| /* | ||
| This command is used to run the Cluster Config Operator tests extension for OpenShift. | ||
| It registers the Cluster Config Operator tests with the OpenShift Tests Extension framework | ||
| and provides a command-line interface to execute them. | ||
| For further information, please refer to the documentation at: | ||
| https://github.com/openshift-eng/openshift-tests-extension/blob/main/cmd/example-tests/main.go | ||
| */ | ||
| // Package main provides the OpenShift Test Extension (OTE) CLI binary for cluster-config-operator. | ||
| // | ||
| // DEPLOYMENT MODEL: | ||
| // Once this binary is statically compiled (CGO_ENABLED=0), compressed, and included in the | ||
| // component Dockerfile, it will be registered in openshift/origin's test extension registry. | ||
| // The tests will then automatically execute through origin's orchestration infrastructure in | ||
| // matching existing CI jobs WITHOUT requiring standalone job configurations in openshift/release. | ||
| // For further information, please refer to the documentation at: | ||
| // https://github.com/openshift-eng/openshift-tests-extension/blob/main/cmd/example-tests/main.go | ||
|
|
||
| package main | ||
|
|
||
| import ( | ||
| "fmt" | ||
| "os" | ||
|
|
||
| "github.com/spf13/cobra" | ||
| "k8s.io/component-base/cli" | ||
|
|
||
| otecmd "github.com/openshift-eng/openshift-tests-extension/pkg/cmd" | ||
| oteextension "github.com/openshift-eng/openshift-tests-extension/pkg/extension" | ||
| "github.com/openshift/cluster-config-operator/pkg/version" | ||
| oteginkgo "github.com/openshift-eng/openshift-tests-extension/pkg/ginkgo" | ||
|
|
||
| "k8s.io/klog/v2" | ||
|
|
||
| // Import the test package to register Ginkgo test suites | ||
| _ "github.com/openshift/cluster-config-operator/test/e2e" | ||
| ) | ||
|
|
||
| func main() { | ||
| cmd, err := newOperatorTestCommand() | ||
| if err != nil { | ||
| klog.Fatal(err) | ||
| } | ||
|
|
||
| code := cli.Run(cmd) | ||
| os.Exit(code) | ||
| if err := cmd.Execute(); err != nil { | ||
| os.Exit(1) | ||
| } | ||
| } | ||
|
|
||
| func newOperatorTestCommand() (*cobra.Command, error) { | ||
| registry, err := prepareOperatorTestsRegistry() | ||
| if err != nil { | ||
| return nil, fmt.Errorf("failed to prepare test registry: %w", err) | ||
| return nil, err | ||
| } | ||
|
|
||
| cmd := &cobra.Command{ | ||
| Use: "cluster-config-operator-tests-ext", | ||
| Short: "A binary used to run cluster-config-operator tests as part of OTE.", | ||
| Long: "Cluster Config Operator Tests Extension", | ||
| Run: func(cmd *cobra.Command, args []string) { | ||
| // no-op, logic is provided by the OTE framework | ||
| if err := cmd.Help(); err != nil { | ||
| klog.Fatal(err) | ||
| } | ||
| }, | ||
| } | ||
|
|
||
| if v := version.Get().String(); len(v) == 0 { | ||
| cmd.Version = "<unknown>" | ||
| } else { | ||
| cmd.Version = v | ||
| } | ||
|
|
||
| cmd.AddCommand(otecmd.DefaultExtensionCommands(registry)...) | ||
|
|
||
| return cmd, nil | ||
| } | ||
|
|
||
| // prepareOperatorTestsRegistry creates the OTE registry for this operator. | ||
| // | ||
| // Note: | ||
| // | ||
| // This method must be called before adding the registry to the OTE framework. | ||
| func prepareOperatorTestsRegistry() (*oteextension.Registry, error) { | ||
| registry := oteextension.NewRegistry() | ||
| extension := oteextension.NewExtension("openshift", "payload", "cluster-config-operator") | ||
|
|
||
| // parallel suite runs non-serial, non-disruptive tests concurrently with parallelism of 4. | ||
| extension.AddSuite(oteextension.Suite{ | ||
| Name: "openshift/cluster-config-operator/operator/parallel", | ||
| Parallelism: 4, | ||
| Qualifiers: []string{ | ||
| `!name.contains("[Serial]") && !name.contains("[Disruptive]")`, | ||
| }, | ||
| }) | ||
|
|
||
| // <Place-holder> disruptive suite runs serial or disruptive tests one at a time, may impact cluster stability. | ||
| extension.AddSuite(oteextension.Suite{ | ||
| Name: "openshift/cluster-config-operator/operator/disruptive", | ||
| Parallelism: 1, | ||
| ClusterStability: oteextension.ClusterStabilityDisruptive, | ||
| Qualifiers: []string{ | ||
| `name.contains("[Serial]") || name.contains("[Disruptive]")`, | ||
| }, | ||
| }) | ||
|
|
||
| specs, err := oteginkgo.BuildExtensionTestSpecsFromOpenShiftGinkgoSuite() | ||
| if err != nil { | ||
| return nil, fmt.Errorf("couldn't build extension test specs from ginkgo: %w", err) | ||
| } | ||
|
|
||
| extension.AddSpecs(specs) | ||
| registry.Register(extension) | ||
| return registry, nil | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,134 @@ | ||
| package e2e | ||
|
|
||
| import ( | ||
| "context" | ||
| "testing" | ||
| "time" | ||
|
|
||
| g "github.com/onsi/ginkgo/v2" | ||
| "github.com/stretchr/testify/require" | ||
|
|
||
| configclient "github.com/openshift/client-go/config/clientset/versioned" | ||
| apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" | ||
| apiextensionsclient "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset" | ||
| metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||
| "k8s.io/apimachinery/pkg/util/wait" | ||
| "k8s.io/client-go/rest" | ||
| "k8s.io/client-go/tools/clientcmd" | ||
| ) | ||
|
|
||
| const ( | ||
| infrastructureResourceName = "cluster" | ||
| pollInterval = 5 * time.Second | ||
| pollTimeout = 2 * time.Minute | ||
| ) | ||
|
|
||
| // Test suite for cluster-config-operator configuration validation | ||
| var _ = g.Describe("cluster-config-operator", func() { | ||
| g.It("should establish config.openshift.io CRDs as healthy and serving [apigroup:config.openshift.io][Operator]", func() { | ||
| testConfigCRDsEstablished(g.GinkgoTB()) | ||
| }) | ||
|
|
||
| g.It("should render cluster-wide Infrastructure configuration [apigroup:config.openshift.io][Operator]", func() { | ||
| testInfrastructureConfiguration(g.GinkgoTB()) | ||
| }) | ||
| }) | ||
|
|
||
| // testConfigCRDsEstablished verifies that core config.openshift.io CRDs are established and serving. | ||
| // cluster-config-operator is responsible for applying these CRDs during cluster bootstrap. | ||
| // This test validates that the API server recognizes and serves these critical API definitions. | ||
| func testConfigCRDsEstablished(t testing.TB) { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. it is new case no need go standard framework design. We can use ginkgo format |
||
| t.Helper() | ||
|
|
||
| config, err := getClientConfig() | ||
| require.NoError(t, err, "failed to get client config") | ||
|
|
||
| apiextClient, err := apiextensionsclient.NewForConfig(config) | ||
| require.NoError(t, err, "failed to create apiextensions client") | ||
|
|
||
| // Core config.openshift.io CRDs that cluster-config-operator establishes | ||
| coreCRDs := []string{ | ||
| "infrastructures.config.openshift.io", | ||
| "featuregates.config.openshift.io", | ||
| "schedulers.config.openshift.io", | ||
| "networks.config.openshift.io", | ||
| "clusterversions.config.openshift.io", | ||
| } | ||
|
|
||
| ctx := context.TODO() | ||
| for _, crdName := range coreCRDs { | ||
| err = wait.PollUntilContextTimeout(ctx, pollInterval, pollTimeout, true, func(ctx context.Context) (bool, error) { | ||
| crd, getErr := apiextClient.ApiextensionsV1().CustomResourceDefinitions().Get(ctx, crdName, metav1.GetOptions{}) | ||
| if getErr != nil { | ||
| t.Logf("Failed to get CRD %s: %v (will retry)", crdName, getErr) | ||
| return false, nil | ||
| } | ||
|
|
||
| // Check if CRD is Established | ||
| established := false | ||
| for _, condition := range crd.Status.Conditions { | ||
| if condition.Type == apiextensionsv1.Established && condition.Status == apiextensionsv1.ConditionTrue { | ||
| established = true | ||
| break | ||
| } | ||
| } | ||
|
|
||
| if !established { | ||
| t.Logf("CRD %s not yet Established (will retry)", crdName) | ||
| return false, nil | ||
| } | ||
|
|
||
| t.Logf("CRD %s is Established and serving", crdName) | ||
| return true, nil | ||
| }) | ||
|
|
||
| require.NoError(t, err, "timed out waiting for CRD %s to be Established", crdName) | ||
| } | ||
| } | ||
|
|
||
| // testInfrastructureConfiguration verifies that the cluster-config-operator has successfully | ||
| // rendered the cluster-wide Infrastructure configuration object named "cluster". | ||
| // This resource is created and maintained by cluster-config-operator during bootstrap and | ||
| // contains critical platform metadata such as platform type, API server URLs, and topology. | ||
| func testInfrastructureConfiguration(t testing.TB) { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. is this cases added some value? |
||
| t.Helper() | ||
|
|
||
| config, err := getClientConfig() | ||
| require.NoError(t, err, "failed to get client config") | ||
|
|
||
| configClient, err := configclient.NewForConfig(config) | ||
| require.NoError(t, err, "failed to create config client") | ||
|
|
||
| // Poll until the Infrastructure resource is available and valid | ||
| err = wait.PollUntilContextTimeout(context.TODO(), pollInterval, pollTimeout, true, func(ctx context.Context) (bool, error) { | ||
| infra, getErr := configClient.ConfigV1().Infrastructures().Get(ctx, infrastructureResourceName, metav1.GetOptions{}) | ||
| if getErr != nil { | ||
| t.Logf("Failed to get Infrastructure resource %s: %v (will retry)", infrastructureResourceName, getErr) | ||
| return false, nil | ||
| } | ||
|
|
||
| // Verify that the Infrastructure resource has required fields populated | ||
| if infra.Status.PlatformStatus == nil { | ||
| t.Logf("Infrastructure %s has nil PlatformStatus (will retry)", infrastructureResourceName) | ||
| return false, nil | ||
| } | ||
|
|
||
| if infra.Status.PlatformStatus.Type == "" { | ||
| t.Logf("Infrastructure %s has empty platform type (will retry)", infrastructureResourceName) | ||
| return false, nil | ||
| } | ||
|
|
||
| t.Logf("Infrastructure %s is valid: platform=%s", infrastructureResourceName, infra.Status.PlatformStatus.Type) | ||
| return true, nil | ||
| }) | ||
|
|
||
| require.NoError(t, err, "timed out waiting for Infrastructure resource to be valid") | ||
| } | ||
|
|
||
| // getClientConfig builds a rest.Config from the default kubeconfig loading rules | ||
| func getClientConfig() (*rest.Config, error) { | ||
| loadingRules := clientcmd.NewDefaultClientConfigLoadingRules() | ||
| configOverrides := &clientcmd.ConfigOverrides{} | ||
| kubeConfig := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(loadingRules, configOverrides) | ||
| return kubeConfig.ClientConfig() | ||
| } | ||
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
better to move go dependencies in separate commit from test cases