Skip to content

Commit e8d9f4b

Browse files
EmilienMstephenfin
authored andcommitted
Add cluster-capi-operator-specific e2e tests
These are heavily based on the tests for other platforms, which are currently included in the cluster-capi-operator tree [1] but which will eventually be moved out to the openshift forks of the respective CAPI implementations. The key difference from these is that (a) we don't create a cluster (since we have the infracluster controller for this) and (b) we obviously use OpenStack-specific semantics. [1] https://github.com/openshift/cluster-capi-operator/tree/release-4.15/e2e Co-Authored-By: Emilien Macchi <[email protected]> Co-Authored-By: Stephen Finucane <[email protected]>
1 parent 8a0920a commit e8d9f4b

File tree

4 files changed

+291
-1
lines changed

4 files changed

+291
-1
lines changed

openshift/Makefile

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,10 +77,11 @@ modules:
7777

7878
.PHONY: test
7979
test:
80-
go test ./...
80+
go test $(shell go list ./... | grep -v /e2e)
8181

8282
.PHONY: e2e
8383
e2e:
84+
./hack/test.sh "./e2e/..." 30m
8485

8586
.PHONY: ALWAYS
8687
ALWAYS:

openshift/e2e/e2e_test.go

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
package e2e
2+
3+
import (
4+
"context"
5+
"testing"
6+
7+
. "github.com/onsi/ginkgo/v2"
8+
. "github.com/onsi/gomega"
9+
configv1 "github.com/openshift/api/config/v1"
10+
mapiv1 "github.com/openshift/api/machine/v1beta1"
11+
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
12+
"k8s.io/client-go/kubernetes/scheme"
13+
clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
14+
runtimeclient "sigs.k8s.io/controller-runtime/pkg/client"
15+
"sigs.k8s.io/controller-runtime/pkg/client/config"
16+
17+
infrav1 "sigs.k8s.io/cluster-api-provider-openstack/api/v1alpha7"
18+
)
19+
20+
const (
21+
infrastructureName = "cluster"
22+
infraAPIVersion = "infrastructure.cluster.x-k8s.io/v1beta1"
23+
)
24+
25+
var (
26+
cl runtimeclient.Client
27+
ctx = context.Background()
28+
platform configv1.PlatformType
29+
clusterName string
30+
mapiInfrastructure *configv1.Infrastructure
31+
)
32+
33+
func init() {
34+
utilruntime.Must(configv1.Install(scheme.Scheme))
35+
utilruntime.Must(infrav1.AddToScheme(scheme.Scheme))
36+
utilruntime.Must(clusterv1.AddToScheme(scheme.Scheme))
37+
utilruntime.Must(mapiv1.AddToScheme(scheme.Scheme))
38+
}
39+
40+
func TestAPIs(t *testing.T) {
41+
RegisterFailHandler(Fail)
42+
RunSpecs(t, "Cluster API Suite")
43+
}
44+
45+
var _ = BeforeSuite(func() {
46+
cfg, err := config.GetConfig()
47+
Expect(err).ToNot(HaveOccurred())
48+
49+
cl, err = runtimeclient.New(cfg, runtimeclient.Options{})
50+
Expect(err).ToNot(HaveOccurred())
51+
52+
infra := &configv1.Infrastructure{}
53+
infraName := runtimeclient.ObjectKey{
54+
Name: infrastructureName,
55+
}
56+
Expect(cl.Get(ctx, infraName, infra)).To(Succeed())
57+
Expect(infra.Status.PlatformStatus).ToNot(BeNil())
58+
mapiInfrastructure = infra
59+
clusterName = infra.Status.InfrastructureName
60+
platform = infra.Status.PlatformStatus.Type
61+
})

openshift/e2e/openstack_test.go

Lines changed: 182 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,182 @@
1+
package e2e
2+
3+
import (
4+
. "github.com/onsi/ginkgo/v2"
5+
. "github.com/onsi/gomega"
6+
configv1 "github.com/openshift/api/config/v1"
7+
mapiv1alpha1 "github.com/openshift/api/machine/v1alpha1"
8+
mapiv1beta1 "github.com/openshift/api/machine/v1beta1"
9+
"github.com/openshift/cluster-capi-operator/e2e/framework"
10+
corev1 "k8s.io/api/core/v1"
11+
apierrors "k8s.io/apimachinery/pkg/api/errors"
12+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
13+
clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
14+
"sigs.k8s.io/controller-runtime/pkg/client"
15+
yaml "sigs.k8s.io/yaml"
16+
17+
"github.com/openshift/cluster-api-provider-openstack/openshift/pkg/infraclustercontroller"
18+
19+
infrav1 "sigs.k8s.io/cluster-api-provider-openstack/api/v1alpha7"
20+
)
21+
22+
const (
23+
openStackMachineTemplateName = "openstack-machine-template"
24+
)
25+
26+
var _ = Describe("Cluster API OpenStack MachineSet", Ordered, func() {
27+
var openStackMachineTemplate *infrav1.OpenStackMachineTemplate
28+
var machineSet *clusterv1.MachineSet
29+
var mapiMachineSpec *mapiv1alpha1.OpenstackProviderSpec
30+
31+
BeforeAll(func() {
32+
if platform != configv1.OpenStackPlatformType {
33+
Skip("Skipping OpenStack E2E tests")
34+
}
35+
framework.CreateCoreCluster(cl, clusterName, "OpenStackCluster")
36+
mapiMachineSpec = getOpenStackMAPIProviderSpec(cl)
37+
})
38+
39+
AfterEach(func() {
40+
if platform != configv1.OpenStackPlatformType {
41+
// Because AfterEach always runs, even when tests are skipped, we have to
42+
// explicitly skip it here for other platforms.
43+
Skip("Skipping OpenStack E2E tests")
44+
}
45+
framework.DeleteMachineSets(cl, machineSet)
46+
framework.WaitForMachineSetsDeleted(cl, machineSet)
47+
framework.DeleteObjects(cl, openStackMachineTemplate)
48+
})
49+
50+
It("should be able to run a machine", func() {
51+
openStackMachineTemplate = createOpenStackMachineTemplate(cl, mapiMachineSpec)
52+
53+
machineSet = framework.CreateMachineSet(cl, framework.NewMachineSetParams(
54+
"openstack-machineset",
55+
clusterName,
56+
"",
57+
1,
58+
corev1.ObjectReference{
59+
Kind: "OpenStackMachineTemplate",
60+
APIVersion: infraAPIVersion,
61+
Name: openStackMachineTemplateName,
62+
},
63+
))
64+
65+
framework.WaitForMachineSet(cl, machineSet.Name)
66+
})
67+
})
68+
69+
func getOpenStackMAPIProviderSpec(cl client.Client) *mapiv1alpha1.OpenstackProviderSpec {
70+
machineSetList := &mapiv1beta1.MachineSetList{}
71+
Expect(cl.List(ctx, machineSetList, client.InNamespace(framework.MAPINamespace))).To(Succeed())
72+
73+
Expect(machineSetList.Items).ToNot(HaveLen(0))
74+
machineSet := machineSetList.Items[0]
75+
Expect(machineSet.Spec.Template.Spec.ProviderSpec.Value).ToNot(BeNil())
76+
77+
providerSpec := &mapiv1alpha1.OpenstackProviderSpec{}
78+
Expect(yaml.Unmarshal(machineSet.Spec.Template.Spec.ProviderSpec.Value.Raw, providerSpec)).To(Succeed())
79+
80+
return providerSpec
81+
}
82+
83+
func createOpenStackMachineTemplate(cl client.Client, mapiProviderSpec *mapiv1alpha1.OpenstackProviderSpec) *infrav1.OpenStackMachineTemplate {
84+
By("Creating OpenStack machine template")
85+
86+
Expect(mapiProviderSpec).ToNot(BeNil())
87+
Expect(mapiProviderSpec.Flavor).ToNot(BeEmpty())
88+
// NOTE(stephenfin): Installer does not populate ps.Image when ps.RootVolume is set and will
89+
// instead populate ps.RootVolume.SourceUUID. Moreover, according to the ClusterOSImage option
90+
// definition this is always the name of the image and never the UUID. We should allow UUID
91+
// at some point and this will need an update.
92+
if mapiProviderSpec.RootVolume != nil {
93+
Expect(mapiProviderSpec.RootVolume.SourceUUID).ToNot(BeEmpty())
94+
} else {
95+
Expect(mapiProviderSpec.Image).ToNot(BeEmpty())
96+
}
97+
Expect(len(mapiProviderSpec.Networks)).To(BeNumerically(">", 0))
98+
Expect(len(mapiProviderSpec.Networks[0].Subnets)).To(BeNumerically(">", 0))
99+
Expect(mapiProviderSpec.Tags).ToNot(BeNil())
100+
Expect(len(mapiProviderSpec.Tags)).To(BeNumerically(">", 0))
101+
102+
var image string
103+
var rootVolume *infrav1.RootVolume
104+
105+
if mapiProviderSpec.RootVolume != nil {
106+
rootVolume = &infrav1.RootVolume{
107+
Size: mapiProviderSpec.RootVolume.Size,
108+
VolumeType: mapiProviderSpec.RootVolume.VolumeType,
109+
AvailabilityZone: mapiProviderSpec.RootVolume.Zone,
110+
}
111+
} else {
112+
image = mapiProviderSpec.Image
113+
}
114+
115+
// NOTE(stephenfin): We intentionally ignore additional networks for now since we don't care
116+
// about e.g. Manila shares. We can re-evaluate this if necessary.
117+
ports := []infrav1.PortOpts{}
118+
for _, subnet := range mapiProviderSpec.Networks[0].Subnets {
119+
port := infrav1.PortOpts{
120+
FixedIPs: []infrav1.FixedIP{
121+
{
122+
Subnet: &infrav1.SubnetFilter{
123+
// NOTE(stephenfin): Only one of name or ID will be set.
124+
ID: subnet.Filter.ID,
125+
Name: subnet.Filter.Name,
126+
Tags: subnet.Filter.Tags,
127+
},
128+
},
129+
},
130+
}
131+
ports = append(ports, port)
132+
}
133+
port := infrav1.PortOpts{
134+
Network: &infrav1.NetworkFilter{
135+
// The installer still sets NetworkParam.Filter.ID rather than NetworkParam.ID,
136+
// at least as of 4.15.
137+
ID: mapiProviderSpec.Networks[0].Filter.ID, //nolint:staticcheck
138+
Name: mapiProviderSpec.Networks[0].Filter.Name,
139+
},
140+
}
141+
ports = append(ports, port)
142+
143+
// NOTE(stephenfin): Ditto for security groups
144+
securityGroups := []infrav1.SecurityGroupFilter{
145+
{
146+
Name: mapiProviderSpec.SecurityGroups[0].Name,
147+
ID: mapiProviderSpec.SecurityGroups[0].UUID,
148+
},
149+
}
150+
151+
openStackMachineSpec := infrav1.OpenStackMachineSpec{
152+
CloudName: infraclustercontroller.CloudName,
153+
Flavor: mapiProviderSpec.Flavor,
154+
IdentityRef: &infrav1.OpenStackIdentityReference{
155+
Kind: "Secret",
156+
Name: infraclustercontroller.CredentialsSecretName,
157+
},
158+
Image: image,
159+
Ports: ports,
160+
RootVolume: rootVolume,
161+
SecurityGroups: securityGroups,
162+
Tags: mapiProviderSpec.Tags,
163+
}
164+
165+
openStackMachineTemplate := &infrav1.OpenStackMachineTemplate{
166+
ObjectMeta: metav1.ObjectMeta{
167+
Name: openStackMachineTemplateName,
168+
Namespace: framework.CAPINamespace,
169+
},
170+
Spec: infrav1.OpenStackMachineTemplateSpec{
171+
Template: infrav1.OpenStackMachineTemplateResource{
172+
Spec: openStackMachineSpec,
173+
},
174+
},
175+
}
176+
177+
if err := cl.Create(ctx, openStackMachineTemplate); err != nil && !apierrors.IsAlreadyExists(err) {
178+
Expect(err).ToNot(HaveOccurred())
179+
}
180+
181+
return openStackMachineTemplate
182+
}

openshift/hack/test.sh

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
#!/bin/bash
2+
3+
set -o nounset
4+
set -o pipefail
5+
6+
REPO_ROOT=$(dirname "${BASH_SOURCE}")/..
7+
8+
TEST_DIRS=$1
9+
TIMEOUT=$2
10+
11+
OPENSHIFT_CI=${OPENSHIFT_CI:-""}
12+
ARTIFACT_DIR=${ARTIFACT_DIR:-""}
13+
GINKGO=${GINKGO:-"go run ${REPO_ROOT}/vendor/github.com/onsi/ginkgo/v2/ginkgo"}
14+
GINKGO_ARGS=${GINKGO_ARGS:-"-r -v --randomize-all --randomize-suites --keep-going --race --trace --timeout=${TIMEOUT}"}
15+
GINKGO_EXTRA_ARGS=${GINKGO_EXTRA_ARGS:-""}
16+
17+
# Ensure that some home var is set and that it's not the root.
18+
# This is required for the kubebuilder cache.
19+
export HOME=${HOME:=/tmp/kubebuilder-testing}
20+
if [ $HOME == "/" ]; then
21+
export HOME=/tmp/kubebuilder-testing
22+
fi
23+
24+
if [ "$OPENSHIFT_CI" == "true" ] && [ -n "$ARTIFACT_DIR" ] && [ -d "$ARTIFACT_DIR" ]; then # detect ci environment there
25+
GINKGO_ARGS="${GINKGO_ARGS} --junit-report=junit_cluster_capi_operator.xml --cover --coverprofile=test-unit-coverage.out --output-dir=${ARTIFACT_DIR}"
26+
fi
27+
28+
# Print the command we are going to run as Make would.
29+
echo ${GINKGO} ${GINKGO_ARGS} ${GINKGO_EXTRA_ARGS} ${TEST_DIRS}
30+
${GINKGO} ${GINKGO_ARGS} ${GINKGO_EXTRA_ARGS} ${TEST_DIRS}
31+
# Capture the test result to exit on error after coverage.
32+
TEST_RESULT=$?
33+
34+
if [ -f "${ARTIFACT_DIR}/test-unit-coverage.out" ]; then
35+
# Convert the coverage to html for spyglass.
36+
go tool cover -html=${ARTIFACT_DIR}/test-unit-coverage.out -o ${ARTIFACT_DIR}/test-unit-coverage.html
37+
38+
# Report the coverage at the end of the test output.
39+
echo -n "Coverage "
40+
go tool cover -func=${ARTIFACT_DIR}/test-unit-coverage.out | tail -n 1
41+
# Blank new line after the coverage output to make it easier to read when there is an error.
42+
echo
43+
fi
44+
45+
# Ensure we exit based on the test result, coverage results are supplementary.
46+
exit ${TEST_RESULT}

0 commit comments

Comments
 (0)