Skip to content
This repository was archived by the owner on Dec 16, 2025. It is now read-only.

Commit 2e4b501

Browse files
✨ Add integration tests for both controllers (#79)
* Add integration tests for both controllers Signed-off-by: michal.gubricky <[email protected]> * Update internal/test/integration/github/integration_test.go Co-authored-by: Roman Hros <[email protected]> Signed-off-by: Michal Gubricky <[email protected]> * Update internal/test/integration/github/integration_test.go Co-authored-by: Roman Hros <[email protected]> Signed-off-by: Michal Gubricky <[email protected]> * Update internal/test/integration/github/integration_test.go Co-authored-by: Roman Hros <[email protected]> Signed-off-by: Michal Gubricky <[email protected]> * Update internal/test/integration/openstack/integration_controller_test.go Co-authored-by: Roman Hros <[email protected]> Signed-off-by: Michal Gubricky <[email protected]> * Update internal/test/integration/openstack/integration_controller_test.go Co-authored-by: Roman Hros <[email protected]> Signed-off-by: Michal Gubricky <[email protected]> * Update internal/test/integration/openstack/integration_controller_test.go Co-authored-by: Roman Hros <[email protected]> Signed-off-by: Michal Gubricky <[email protected]> * Change GIT_ORG_NAME to SovereignCloudStack Signed-off-by: michal.gubricky <[email protected]> * Remove kind cluster logic and rename file Signed-off-by: michal.gubricky <[email protected]> --------- Signed-off-by: michal.gubricky <[email protected]> Signed-off-by: Michal Gubricky <[email protected]> Co-authored-by: Roman Hros <[email protected]>
1 parent 76702f8 commit 2e4b501

File tree

30 files changed

+5985
-11
lines changed

30 files changed

+5985
-11
lines changed

.github/workflows/test.yaml

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -55,15 +55,17 @@ jobs:
5555
- name: Running unit tests
5656
run: make test-unit
5757

58+
- name: Running integration tests
59+
env:
60+
GIT_PROVIDER: github
61+
GIT_ORG_NAME: SovereignCloudStack
62+
GIT_REPOSITORY_NAME: cluster-stacks
63+
GIT_ACCESS_TOKEN: ${{ secrets.GIT_ACCESS_TOKEN }}
64+
ENCODED_CLOUDS_YAML: ${{ secrets.ENCODED_CLOUDS_YAML }}
65+
run: make test-integration
66+
5867
# - name: Create Report
5968
# run: make report-cover-html report-cover-treemap
60-
# - name: Running integration tests
61-
# env:
62-
# GIT_PROVIDER: github
63-
# GIT_ORG_NAME: sovereignCloudStack
64-
# GIT_REPOSITORY_NAME: fake-cluster-stacks
65-
# GIT_ACCESS_TOKEN: ${{ secrets.GIT_ACCESS_TOKEN }}
66-
# run: make test-integration
6769

6870
# - name: Test Summary
6971
# uses: test-summary/action@62bc5c68de2a6a0d02039763b8c754569df99e3f # v2.1

Makefile

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -528,16 +528,32 @@ generate-modules-ci: generate-modules
528528

529529
KUBEBUILDER_ASSETS ?= $(shell $(SETUP_ENVTEST) use --use-env --bin-dir $(abspath $(TOOLS_BIN_DIR)) -p path $(KUBEBUILDER_ENVTEST_KUBERNETES_VERSION))
530530

531+
.PHONY: test-integration ## Run integration tests
532+
test-integration: test-integration-github test-integration-openstack
533+
echo done
534+
531535
.PHONY: test-unit
532536
test-unit: test-unit-openstack ## Run unit tests
533537
echo done
534538

535539
.PHONY: test-unit-openstack
536540
test-unit-openstack: $(SETUP_ENVTEST) $(GOTESTSUM) $(HELM)
537541
@mkdir -p $(shell pwd)/.coverage
538-
CREATE_KIND_CLUSTER=false KUBEBUILDER_ASSETS="$(KUBEBUILDER_ASSETS)" $(GOTESTSUM) --junitfile=.coverage/junit.xml --format testname -- -mod=vendor \
542+
KUBEBUILDER_ASSETS="$(KUBEBUILDER_ASSETS)" $(GOTESTSUM) --junitfile=.coverage/junit.xml --format testname -- -mod=vendor \
539543
-covermode=atomic -coverprofile=.coverage/cover.out -p=4 ./internal/controller/...
540544

545+
.PHONY: test-integration-github
546+
test-integration-github: $(SETUP_ENVTEST) $(GOTESTSUM)
547+
@mkdir -p $(shell pwd)/.coverage
548+
KUBEBUILDER_ASSETS="$(KUBEBUILDER_ASSETS)" $(GOTESTSUM) --junitfile=.coverage/junit.xml --format testname -- -mod=vendor \
549+
-covermode=atomic -coverprofile=.coverage/cover.out -p=1 ./internal/test/integration/github/...
550+
551+
.PHONY: test-integration-openstack
552+
test-integration-openstack: $(SETUP_ENVTEST) $(GOTESTSUM)
553+
@mkdir -p $(shell pwd)/.coverage
554+
KUBEBUILDER_ASSETS="$(KUBEBUILDER_ASSETS)" $(GOTESTSUM) --junitfile=.coverage/junit.xml --format testname -- -mod=vendor \
555+
-covermode=atomic -coverprofile=.coverage/cover.out -p=1 ./internal/test/integration/openstack/...
556+
541557
##@ Main Targets
542558
################
543559
# Main Targets #
@@ -565,8 +581,8 @@ boilerplate: generate-boilerplate ## Ensure that your files have a boilerplate h
565581
builder-image-push: ## Build $(CONTROLLER_SHORT)-builder to a new version. For more information see README.
566582
BUILDER_IMAGE=$(BUILDER_IMAGE) ./hack/upgrade-builder-image.sh
567583

568-
# .PHONY: test
569-
# test: test-unit test-integration ## Runs all unit and integration tests.
584+
.PHONY: test
585+
test: test-unit test-integration ## Runs all unit and integration tests.
570586

571587
create-workload-cluster-openstack: $(ENVSUBST) $(KUBECTL)
572588
cat .cluster.yaml | $(ENVSUBST) - | $(KUBECTL) apply -f -

go.mod

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ require (
1313
k8s.io/api v0.28.4
1414
k8s.io/apimachinery v0.28.4
1515
k8s.io/client-go v0.28.4
16+
k8s.io/klog/v2 v2.100.1
1617
sigs.k8s.io/cluster-api v1.6.0
1718
sigs.k8s.io/cluster-api-provider-openstack v0.9.0
1819
sigs.k8s.io/controller-runtime v0.16.3
@@ -69,6 +70,7 @@ require (
6970
go.uber.org/zap v1.25.0 // indirect
7071
golang.org/x/crypto v0.16.0 // indirect
7172
golang.org/x/exp v0.0.0-20231006140011-7918f672742d // indirect
73+
golang.org/x/mod v0.14.0 // indirect
7274
golang.org/x/net v0.19.0 // indirect
7375
golang.org/x/oauth2 v0.14.0 // indirect
7476
golang.org/x/sys v0.15.0 // indirect
@@ -84,7 +86,6 @@ require (
8486
gopkg.in/yaml.v3 v3.0.1 // indirect
8587
k8s.io/apiextensions-apiserver v0.28.4 // indirect
8688
k8s.io/component-base v0.28.4 // indirect
87-
k8s.io/klog/v2 v2.100.1 // indirect
8889
k8s.io/kube-openapi v0.0.0-20230717233707-2695361300d9 // indirect
8990
k8s.io/utils v0.0.0-20230505201702-9f6742963106 // indirect
9091
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,8 @@ golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
208208
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
209209
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
210210
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
211+
golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0=
212+
golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
211213
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
212214
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
213215
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=

internal/test/helpers/envtest.go

Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,194 @@
1+
/*
2+
Copyright 2022 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
// Package helpers includes helper functions important for unit and integration testing.
18+
package helpers
19+
20+
import (
21+
"context"
22+
"fmt"
23+
"path"
24+
"path/filepath"
25+
goruntime "runtime"
26+
27+
githubclient "github.com/SovereignCloudStack/cluster-stack-operator/pkg/github/client"
28+
githubmocks "github.com/SovereignCloudStack/cluster-stack-operator/pkg/github/client/mocks"
29+
g "github.com/onsi/ginkgo/v2"
30+
cspov1alpha1 "github.com/sovereignCloudStack/cluster-stack-provider-openstack/api/v1alpha1"
31+
corev1 "k8s.io/api/core/v1"
32+
apierrors "k8s.io/apimachinery/pkg/api/errors"
33+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
34+
"k8s.io/apimachinery/pkg/runtime"
35+
kerrors "k8s.io/apimachinery/pkg/util/errors"
36+
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
37+
clientgoscheme "k8s.io/client-go/kubernetes/scheme"
38+
"k8s.io/client-go/rest"
39+
"k8s.io/klog/v2"
40+
"k8s.io/klog/v2/klogr"
41+
"sigs.k8s.io/cluster-api/cmd/clusterctl/log"
42+
ctrl "sigs.k8s.io/controller-runtime"
43+
"sigs.k8s.io/controller-runtime/pkg/client"
44+
"sigs.k8s.io/controller-runtime/pkg/envtest"
45+
metricsserver "sigs.k8s.io/controller-runtime/pkg/metrics/server"
46+
)
47+
48+
func init() {
49+
klog.InitFlags(nil)
50+
logger := klogr.New()
51+
52+
// use klog as the internal logger for this envtest environment.
53+
log.SetLogger(logger)
54+
// additionally force all of the controllers to use the Ginkgo logger.
55+
ctrl.SetLogger(logger)
56+
// add logger for ginkgo
57+
klog.SetOutput(g.GinkgoWriter)
58+
}
59+
60+
var (
61+
scheme = runtime.NewScheme()
62+
env *envtest.Environment
63+
ctx = context.Background()
64+
)
65+
66+
const (
67+
// DefaultPodNamespace is default the namespace for the envtest resources.
68+
DefaultPodNamespace = "cspo-system"
69+
)
70+
71+
func init() {
72+
// Calculate the scheme.
73+
utilruntime.Must(clientgoscheme.AddToScheme(scheme))
74+
utilruntime.Must(cspov1alpha1.AddToScheme(scheme))
75+
76+
// Get the root of the current file to use in CRD paths.
77+
_, filename, _, _ := goruntime.Caller(0) //nolint:dogsled // external function
78+
root := path.Join(path.Dir(filename), "..", "..", "..")
79+
80+
crdPaths := []string{
81+
filepath.Join(root, "config", "crd", "bases"),
82+
}
83+
84+
// Create the test environment.
85+
env = &envtest.Environment{
86+
Scheme: scheme,
87+
ErrorIfCRDPathMissing: true,
88+
CRDDirectoryPaths: crdPaths,
89+
}
90+
}
91+
92+
type (
93+
// TestEnvironment encapsulates a Kubernetes local test environment.
94+
TestEnvironment struct {
95+
ctrl.Manager
96+
client.Client
97+
Config *rest.Config
98+
cancel context.CancelFunc
99+
GitHubClientFactory githubclient.Factory
100+
GitHubClient *githubmocks.Client
101+
}
102+
)
103+
104+
// NewTestEnvironment creates a new environment spinning up a local api-server.
105+
func NewTestEnvironment() *TestEnvironment {
106+
config, err := env.Start()
107+
if err != nil {
108+
klog.Fatalf("unable to start env: %s", err)
109+
}
110+
111+
// Build the controller manager.
112+
mgr, err := ctrl.NewManager(config, ctrl.Options{
113+
Scheme: env.Scheme,
114+
Metrics: metricsserver.Options{BindAddress: "0"},
115+
})
116+
if err != nil {
117+
klog.Fatalf("unable to create manager: %s", err)
118+
}
119+
120+
// create manager pod namespace
121+
ns := &corev1.Namespace{
122+
ObjectMeta: metav1.ObjectMeta{
123+
Name: DefaultPodNamespace,
124+
},
125+
}
126+
127+
if err = mgr.GetClient().Create(ctx, ns); err != nil {
128+
klog.Fatalf("unable to create manager pod namespace: %s", err)
129+
}
130+
131+
githubClient := &githubmocks.Client{}
132+
133+
testEnv := &TestEnvironment{
134+
Manager: mgr,
135+
Client: mgr.GetClient(),
136+
Config: mgr.GetConfig(),
137+
GitHubClientFactory: githubmocks.NewGitHubFactory(githubClient),
138+
GitHubClient: githubClient,
139+
}
140+
141+
return testEnv
142+
}
143+
144+
// StartManager starts the manager and sets a cancel function into the testEnv object.
145+
func (t *TestEnvironment) StartManager(ctx context.Context) error {
146+
ctx, cancel := context.WithCancel(ctx)
147+
t.cancel = cancel
148+
if err := t.Manager.Start(ctx); err != nil {
149+
return fmt.Errorf("failed to start manager: %w", err)
150+
}
151+
return nil
152+
}
153+
154+
// Stop stops the manager and cancels the context.
155+
func (t *TestEnvironment) Stop() error {
156+
t.cancel()
157+
if err := env.Stop(); err != nil {
158+
return fmt.Errorf("failed to stop environment; %w", err)
159+
}
160+
return nil
161+
}
162+
163+
// Cleanup deletes client objects.
164+
func (t *TestEnvironment) Cleanup(ctx context.Context, objs ...client.Object) error {
165+
errs := make([]error, 0, len(objs))
166+
for _, o := range objs {
167+
err := t.Client.Delete(ctx, o)
168+
if apierrors.IsNotFound(err) {
169+
// If the object is not found, it must've been garbage collected
170+
// already. For example, if we delete namespace first and then
171+
// objects within it.
172+
continue
173+
}
174+
errs = append(errs, err)
175+
}
176+
return kerrors.NewAggregate(errs)
177+
}
178+
179+
// CreateNamespace creates a namespace.
180+
func (t *TestEnvironment) CreateNamespace(ctx context.Context, generateName string) (*corev1.Namespace, error) {
181+
ns := &corev1.Namespace{
182+
ObjectMeta: metav1.ObjectMeta{
183+
GenerateName: fmt.Sprintf("%s-", generateName),
184+
Labels: map[string]string{
185+
"testenv/original-name": generateName,
186+
},
187+
},
188+
}
189+
if err := t.Client.Create(ctx, ns); err != nil {
190+
return nil, fmt.Errorf("failed to create namespace: %w", err)
191+
}
192+
193+
return ns, nil
194+
}
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
/*
2+
Copyright 2023 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package github
18+
19+
import (
20+
"testing"
21+
"time"
22+
23+
githubclient "github.com/SovereignCloudStack/cluster-stack-operator/pkg/github/client"
24+
. "github.com/onsi/ginkgo/v2"
25+
. "github.com/onsi/gomega"
26+
"github.com/sovereignCloudStack/cluster-stack-provider-openstack/internal/controller"
27+
"github.com/sovereignCloudStack/cluster-stack-provider-openstack/internal/test/helpers"
28+
ctrl "sigs.k8s.io/controller-runtime"
29+
)
30+
31+
const (
32+
timeout = time.Second * 20
33+
interval = 1000 * time.Millisecond
34+
)
35+
36+
func TestControllers(t *testing.T) {
37+
RegisterFailHandler(Fail)
38+
RunSpecs(t, "Controller Suite")
39+
}
40+
41+
var (
42+
ctx = ctrl.SetupSignalHandler()
43+
testEnv *helpers.TestEnvironment
44+
)
45+
46+
var _ = BeforeSuite(func() {
47+
testEnv = helpers.NewTestEnvironment()
48+
Expect((&controller.OpenStackClusterStackReleaseReconciler{
49+
Client: testEnv.Manager.GetClient(),
50+
GitHubClientFactory: githubclient.NewFactory(),
51+
ReleaseDirectory: "/tmp/downloads",
52+
}).SetupWithManager(testEnv.Manager)).To(Succeed())
53+
54+
go func() {
55+
defer GinkgoRecover()
56+
Expect(testEnv.StartManager(ctx)).To(Succeed())
57+
}()
58+
<-testEnv.Manager.Elected()
59+
})
60+
61+
var _ = AfterSuite(func() {
62+
Expect(testEnv.Stop()).To(Succeed())
63+
})

0 commit comments

Comments
 (0)