Skip to content

Commit 14962a9

Browse files
committed
create cluster-api-tests-ext command for origin e2e
1 parent 07e4826 commit 14962a9

24 files changed

+957
-237
lines changed

.golangci.yml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,3 +134,13 @@ issues:
134134
- gochecknoglobals
135135
- err113
136136
- wrapcheck
137+
138+
# Exclude some linters from running on tests files.
139+
- path: e2e/.*|e2e/framework/.*
140+
linters:
141+
- gocyclo
142+
- dupl
143+
- gosec
144+
- gochecknoglobals
145+
- err113
146+
- funlen

Dockerfile.rhel

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,17 @@
11
FROM registry.ci.openshift.org/ocp/builder:rhel-9-golang-1.24-openshift-4.20 AS builder
22
WORKDIR /go/src/github.com/openshift/cluster-capi-operator
33
COPY . .
4-
RUN make build
4+
RUN make build && \
5+
mkdir -p /tmp/build && \
6+
cp /go/src/github.com/openshift/cluster-capi-operator/bin/cluster-api-tests-ext /tmp/build/cluster-api-tests-ext && \
7+
gzip /tmp/build/cluster-api-tests-ext
58

69
FROM registry.ci.openshift.org/ocp/4.20:base-rhel9
710
COPY --from=builder /go/src/github.com/openshift/cluster-capi-operator/bin/cluster-capi-operator .
811
COPY --from=builder /go/src/github.com/openshift/cluster-capi-operator/bin/machine-api-migration .
912
COPY --from=builder /go/src/github.com/openshift/cluster-capi-operator/manifests /manifests
13+
COPY --from=builder /tmp/build/cluster-api-tests-ext.gz .
1014

11-
LABEL io.openshift.release.operator true
15+
LABEL io.k8s.display-name="OpenShift Cluster CAPI Operator" \
16+
io.openshift.release.operator=true \
17+
io.openshift.tags="openshift,tests,e2e,e2e-extension"

Makefile

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ verify: fmt lint
2424
test: verify unit
2525

2626
# Build binaries
27-
build: operator migration manifests-gen
27+
build: operator migration manifests-gen cluster-api-tests-ext
2828

2929
.PHONY: manifests-gen
3030
manifests-gen:
@@ -39,6 +39,10 @@ migration:
3939
# building migration
4040
go build -o bin/machine-api-migration cmd/machine-api-migration/main.go
4141

42+
cluster-api-tests-ext:
43+
# building cluster-api-tests-ext
44+
go build -o bin/cluster-api-tests-ext ./cmd/cluster-api-tests-ext
45+
4246
.PHONY: localtestenv
4347
localtestenv: .localtestenv
4448

cmd/cluster-api-tests-ext/main.go

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
// Copyright 2024 Red Hat, Inc.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
package main
15+
16+
import (
17+
"fmt"
18+
"os"
19+
"regexp"
20+
"strings"
21+
22+
"github.com/openshift-eng/openshift-tests-extension/pkg/cmd"
23+
e "github.com/openshift-eng/openshift-tests-extension/pkg/extension"
24+
"github.com/openshift-eng/openshift-tests-extension/pkg/extension/extensiontests"
25+
g "github.com/openshift-eng/openshift-tests-extension/pkg/ginkgo"
26+
"github.com/spf13/cobra"
27+
28+
// If using ginkgo, import your tests here.
29+
_ "github.com/openshift/cluster-capi-operator/e2e"
30+
)
31+
32+
func main() {
33+
extensionRegistry := e.NewRegistry()
34+
capiExtension := e.NewExtension("openshift", "payload", "cluster-capi-operator")
35+
extensionRegistry.Register(capiExtension)
36+
37+
capiExtension.AddSuite(e.Suite{
38+
Name: "capio/conformance/parallel",
39+
Qualifiers: []string{`!labels.exists(l, l == "Serial") && labels.exists(l, l == "Conformance")`},
40+
})
41+
42+
capiExtension.AddSuite(e.Suite{
43+
Name: "capio/conformance/serial",
44+
Qualifiers: []string{`labels.exists(l, l == "Serial") && labels.exists(l, l == "Conformance")`},
45+
})
46+
47+
specs, err := g.BuildExtensionTestSpecsFromOpenShiftGinkgoSuite()
48+
if err != nil {
49+
panic(fmt.Sprintf("couldn't build extension test specs from ginkgo: %+v", err.Error()))
50+
}
51+
52+
// Initialization for kube ginkgo test framework needs to run before all tests execute
53+
specs.AddBeforeAll(func() {
54+
if err := initializeTestFramework(os.Getenv("TEST_PROVIDER")); err != nil {
55+
panic(err)
56+
}
57+
})
58+
59+
// Let's scan for tests with a platform label and create the rule for them such as [platform:vsphere]
60+
foundPlatforms := make(map[string]string)
61+
62+
for _, test := range specs.Select(extensiontests.NameContains("[platform:")).Names() {
63+
re := regexp.MustCompile(`\[platform:[a-z]*]`)
64+
65+
match := re.FindStringSubmatch(test)
66+
for _, platformDef := range match {
67+
if _, ok := foundPlatforms[platformDef]; !ok {
68+
platform := platformDef[strings.Index(platformDef, ":")+1 : len(platformDef)-1]
69+
foundPlatforms[platformDef] = platform
70+
specs.Select(extensiontests.NameContains(platformDef)).
71+
Include(extensiontests.PlatformEquals(platform))
72+
}
73+
}
74+
}
75+
76+
capiExtension.AddSpecs(specs)
77+
78+
root := &cobra.Command{
79+
Long: "Cluster CAPI Operator tests extension for OpenShift",
80+
}
81+
82+
root.AddCommand(cmd.DefaultExtensionCommands(extensionRegistry)...)
83+
84+
if err := func() error {
85+
return root.Execute()
86+
}(); err != nil {
87+
os.Exit(1)
88+
}
89+
}
Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
// Copyright 2024 Red Hat, Inc.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package main
16+
17+
import (
18+
"context"
19+
"encoding/json"
20+
"errors"
21+
"fmt"
22+
"os"
23+
"path/filepath"
24+
"strings"
25+
26+
"github.com/onsi/ginkgo/v2"
27+
"github.com/onsi/gomega"
28+
corev1 "k8s.io/api/core/v1"
29+
kclientset "k8s.io/client-go/kubernetes"
30+
"k8s.io/client-go/tools/clientcmd"
31+
"k8s.io/kubernetes/openshift-hack/e2e"
32+
conformancetestdata "k8s.io/kubernetes/test/conformance/testdata"
33+
"k8s.io/kubernetes/test/e2e/framework"
34+
"k8s.io/kubernetes/test/e2e/framework/testfiles"
35+
e2etestingmanifests "k8s.io/kubernetes/test/e2e/testing-manifests"
36+
testfixtures "k8s.io/kubernetes/test/fixtures"
37+
38+
// this appears to inexplicably auto-register global flags.
39+
_ "k8s.io/kubernetes/test/e2e/storage/drivers"
40+
41+
// these are loading important global flags that we need to get and set.
42+
_ "k8s.io/kubernetes/test/e2e"
43+
_ "k8s.io/kubernetes/test/e2e/lifecycle"
44+
)
45+
46+
var (
47+
errProviderJSONInvalid = errors.New("provider must be a JSON object with the 'type' key at a minimum")
48+
errProviderMissingType = errors.New("provider must be a JSON object with the 'type' key")
49+
errProviderConfigInvalid = errors.New("provider must decode into the ClusterConfig object")
50+
errUnableToSetArtifactDir = errors.New("unable to set ARTIFACT_DIR")
51+
errClientConfigInvalid = errors.New("failed to create client config")
52+
)
53+
54+
// copied directly from github.com/openshift/kubernetes/openshift-hack/cmd/k8s-tests-ext/provider.go
55+
// I attempted to use the clusterdiscovery.InitializeTestFramework in origin but it has too many additional parameters
56+
// that as an test-ext, I felt we shouldn't have to load all that. Hopefully origin's test-ext frameworks gets enhanced
57+
// to have a simple way to initialize all this w/o having to copy/pasta like the openshift/kubernetes project did.
58+
func initializeTestFramework(provider string) error {
59+
config, err := parseProviderConfig(provider)
60+
if err != nil {
61+
return err
62+
}
63+
64+
if err := setupTestContext(config); err != nil {
65+
return err
66+
}
67+
68+
if err := setupKubernetesClient(); err != nil {
69+
return err
70+
}
71+
72+
setupFrameworkDefaults()
73+
setupIPFamily(config)
74+
75+
return nil
76+
}
77+
78+
func parseProviderConfig(provider string) (*ClusterConfiguration, error) {
79+
providerInfo := &ClusterConfiguration{}
80+
if err := json.Unmarshal([]byte(provider), &providerInfo); err != nil {
81+
return nil, fmt.Errorf("%w: %w", errProviderJSONInvalid, err)
82+
}
83+
84+
if len(providerInfo.ProviderName) == 0 {
85+
return nil, errProviderMissingType
86+
}
87+
88+
config := &ClusterConfiguration{}
89+
if err := json.Unmarshal([]byte(provider), config); err != nil {
90+
return nil, fmt.Errorf("%w: %w", errProviderConfigInvalid, err)
91+
}
92+
93+
return config, nil
94+
}
95+
96+
func setupTestContext(config *ClusterConfiguration) error {
97+
testContext := &framework.TestContext
98+
testContext.Provider = config.ProviderName
99+
testContext.CloudConfig = framework.CloudConfig{
100+
ProjectID: config.ProjectID,
101+
Region: config.Region,
102+
Zone: config.Zone,
103+
Zones: config.Zones,
104+
NumNodes: config.NumNodes,
105+
MultiMaster: config.MultiMaster,
106+
MultiZone: config.MultiZone,
107+
ConfigFile: config.ConfigFile,
108+
}
109+
testContext.AllowedNotReadyNodes = -1
110+
testContext.MinStartupPods = -1
111+
testContext.MaxNodesToGather = 0
112+
testContext.KubeConfig = os.Getenv("KUBECONFIG")
113+
114+
if ad := os.Getenv("ARTIFACT_DIR"); len(strings.TrimSpace(ad)) == 0 {
115+
if err := os.Setenv("ARTIFACT_DIR", filepath.Join(os.TempDir(), "artifacts")); err != nil {
116+
return fmt.Errorf("%w: %w", errUnableToSetArtifactDir, err)
117+
}
118+
}
119+
120+
testContext.DeleteNamespace = os.Getenv("DELETE_NAMESPACE") != "false"
121+
testContext.VerifyServiceAccount = true
122+
123+
setupFileSources()
124+
125+
testContext.KubectlPath = "kubectl"
126+
testContext.KubeConfig = os.Getenv("KUBECONFIG")
127+
testContext.NodeOSDistro = "custom"
128+
testContext.MasterOSDistro = "custom"
129+
130+
return nil
131+
}
132+
133+
func setupFileSources() {
134+
testfiles.AddFileSource(e2etestingmanifests.GetE2ETestingManifestsFS())
135+
testfiles.AddFileSource(testfixtures.GetTestFixturesFS())
136+
testfiles.AddFileSource(conformancetestdata.GetConformanceTestdataFS())
137+
}
138+
139+
func setupKubernetesClient() error {
140+
testContext := &framework.TestContext
141+
clientConfig := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(&clientcmd.ClientConfigLoadingRules{ExplicitPath: testContext.KubeConfig}, &clientcmd.ConfigOverrides{})
142+
143+
cfg, err := clientConfig.ClientConfig()
144+
if err != nil {
145+
return fmt.Errorf("%w: %w", errClientConfigInvalid, err)
146+
}
147+
148+
testContext.Host = cfg.Host
149+
testContext.CreateTestingNS = func(ctx context.Context, baseName string, c kclientset.Interface, labels map[string]string) (*corev1.Namespace, error) {
150+
return e2e.CreateTestingNS(ctx, baseName, c, labels, true)
151+
}
152+
153+
return nil
154+
}
155+
156+
func setupFrameworkDefaults() {
157+
testContext := &framework.TestContext
158+
159+
gomega.RegisterFailHandler(ginkgo.Fail)
160+
framework.AfterReadingAllFlags(testContext)
161+
testContext.DumpLogsOnFailure = true
162+
testContext.ReportDir = os.Getenv("TEST_JUNIT_DIR")
163+
}
164+
165+
func setupIPFamily(config *ClusterConfiguration) {
166+
testContext := &framework.TestContext
167+
testContext.IPFamily = "ipv4"
168+
169+
if config.HasIPv6 && !config.HasIPv4 {
170+
testContext.IPFamily = "ipv6"
171+
}
172+
}

cmd/cluster-api-tests-ext/types.go

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
// Copyright 2024 Red Hat, Inc.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package main
16+
17+
// ClusterConfiguration is copied directly from github.com/openshift/origin/test/extended/util/cluster/cluster.go.
18+
type ClusterConfiguration struct {
19+
ProviderName string `json:"type"`
20+
21+
// These fields (and the "type" tag for ProviderName) chosen to match
22+
// upstream's e2e.CloudConfig.
23+
ProjectID string
24+
Region string
25+
Zone string
26+
NumNodes int
27+
MultiMaster bool
28+
MultiZone bool
29+
Zones []string
30+
ConfigFile string
31+
32+
// Disconnected is set for test jobs without external internet connectivity
33+
Disconnected bool
34+
35+
// SingleReplicaTopology is set for disabling disruptive tests or tests
36+
// that require high availability
37+
SingleReplicaTopology bool
38+
39+
// NetworkPlugin is the "official" plugin name
40+
NetworkPlugin string
41+
// NetworkPluginMode is an optional sub-identifier for the NetworkPlugin.
42+
// (Currently it is only used for OpenShiftSDN.)
43+
NetworkPluginMode string `json:"networkPluginMode,omitempty"`
44+
45+
// HasIPv4 and HasIPv6 determine whether IPv4-specific, IPv6-specific,
46+
// and dual-stack-specific tests are run
47+
HasIPv4 bool
48+
HasIPv6 bool
49+
50+
// HasSCTP determines whether SCTP connectivity tests can be run in the cluster
51+
HasSCTP bool
52+
53+
// IsProxied determines whether we are accessing the cluster through an HTTP proxy
54+
IsProxied bool
55+
56+
// IsIBMROKS determines whether the cluster is Managed IBM Cloud (ROKS)
57+
IsIBMROKS bool
58+
59+
// IsNoOptionalCapabilities indicates the cluster has no optional capabilities enabled
60+
HasNoOptionalCapabilities bool
61+
}

0 commit comments

Comments
 (0)