Skip to content

Commit a6c8784

Browse files
authored
Merge pull request #165 from Leaseweb/feat/testability
Fix idempotency and improve testability
2 parents 26998d1 + 5f3c4ef commit a6c8784

File tree

21 files changed

+943
-342
lines changed

21 files changed

+943
-342
lines changed

.github/workflows/pr-check.yaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ jobs:
2222

2323
build:
2424
name: Test & Build
25-
runs-on: ubuntu-20.04
25+
runs-on: ubuntu-24.04
2626
steps:
2727
- name: Setup up Go 1.22
2828
uses: actions/setup-go@v5

.gitignore

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,4 @@
33
/test/e2e/ginkgo
44
cloud-config
55
/vendor
6-
6+
/hack/tools/bin

Makefile

+27
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,20 @@ IMPORTPATH_LDFLAGS = -X ${PKG}/pkg/driver.driverVersion=$(REV) -X ${PKG}/pkg/dri
1818
LDFLAGS = -s -w
1919
FULL_LDFLAGS = $(LDFLAGS) $(IMPORTPATH_LDFLAGS)
2020

21+
export REPO_ROOT := $(shell git rev-parse --show-toplevel)
22+
23+
# Directories
24+
TOOLS_DIR := $(REPO_ROOT)/hack/tools
25+
TOOLS_BIN_DIR := $(TOOLS_DIR)/bin
26+
BIN_DIR ?= bin
27+
28+
GO_INSTALL := ./hack/go_install.sh
29+
30+
MOCKGEN_BIN := mockgen
31+
MOCKGEN_VER := v0.5.0
32+
MOCKGEN := $(abspath $(TOOLS_BIN_DIR)/$(MOCKGEN_BIN)-$(MOCKGEN_VER))
33+
MOCKGEN_PKG := go.uber.org/mock/mockgen
34+
2135
.PHONY: all
2236
all: build
2337

@@ -41,6 +55,11 @@ $(CMDS:%=container-%): container-%: build-%
4155
$(DOCKER) build -f ./cmd/$*/Dockerfile -t $*:latest \
4256
--label org.opencontainers.image.revision=$(REV) .
4357

58+
.PHONY: generate-mocks
59+
generate-mocks: $(MOCKGEN) pkg/cloud/mock_cloud.go ## Generate mocks needed for testing. Primarily mocks of the cloud package.
60+
pkg/cloud/mock%.go: $(shell find ./pkg/cloud -type f -name "*test*" -prune -o -print)
61+
go generate ./...
62+
4463
.PHONY: test
4564
test:
4665
go test ./...
@@ -59,3 +78,11 @@ test/e2e/e2e.test test/e2e/ginkgo:
5978
.PHONY: test-e2e
6079
test-e2e: setup-external-e2e
6180
bash ./test/e2e/run.sh
81+
82+
##@ hack/tools:
83+
84+
.PHONY: $(MOCKGEN_BIN)
85+
$(MOCKGEN_BIN): $(MOCKGEN) ## Build a local copy of mockgen.
86+
87+
$(MOCKGEN): # Build mockgen from tools folder.
88+
GOBIN=$(TOOLS_BIN_DIR) $(GO_INSTALL) $(MOCKGEN_PKG) $(MOCKGEN_BIN) $(MOCKGEN_VER)

cmd/cloudstack-csi-driver/main.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ func main() {
9595
ctx := klog.NewContext(context.Background(), logger)
9696
csConnector := cloud.New(config)
9797

98-
d, err := driver.New(ctx, csConnector, &options, nil)
98+
d, err := driver.NewDriver(ctx, csConnector, &options, nil)
9999
if err != nil {
100100
logger.Error(err, "Failed to initialize driver")
101101
klog.FlushAndExit(klog.ExitFlushTimeout, 1)

go.mod

+7-4
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,13 @@ toolchain go1.22.8
77
require (
88
github.com/apache/cloudstack-go/v2 v2.16.1
99
github.com/container-storage-interface/spec v1.9.0
10+
github.com/golang/mock v1.6.0
1011
github.com/hashicorp/go-uuid v1.0.3
1112
github.com/kubernetes-csi/csi-lib-utils v0.18.1
1213
github.com/kubernetes-csi/csi-test/v5 v5.2.0
1314
github.com/spf13/pflag v1.0.5
15+
github.com/stretchr/testify v1.9.0
16+
go.uber.org/mock v0.5.0
1417
golang.org/x/sys v0.26.0
1518
golang.org/x/text v0.19.0
1619
google.golang.org/grpc v1.65.0
@@ -37,7 +40,6 @@ require (
3740
github.com/go-openapi/swag v0.22.3 // indirect
3841
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect
3942
github.com/gogo/protobuf v1.3.2 // indirect
40-
github.com/golang/mock v1.6.0 // indirect
4143
github.com/golang/protobuf v1.5.4 // indirect
4244
github.com/google/gnostic-models v0.6.8 // indirect
4345
github.com/google/go-cmp v0.6.0 // indirect
@@ -56,18 +58,19 @@ require (
5658
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
5759
github.com/onsi/ginkgo/v2 v2.15.0 // indirect
5860
github.com/onsi/gomega v1.31.0 // indirect
61+
github.com/pmezard/go-difflib v1.0.0 // indirect
5962
github.com/prometheus/client_golang v1.16.0 // indirect
6063
github.com/prometheus/client_model v0.4.0 // indirect
6164
github.com/prometheus/common v0.44.0 // indirect
6265
github.com/prometheus/procfs v0.10.1 // indirect
6366
github.com/spf13/cobra v1.7.0 // indirect
6467
go.uber.org/multierr v1.11.0 // indirect
6568
go.uber.org/zap v1.27.0 // indirect
66-
golang.org/x/net v0.25.0 // indirect
69+
golang.org/x/net v0.26.0 // indirect
6770
golang.org/x/oauth2 v0.20.0 // indirect
68-
golang.org/x/term v0.20.0 // indirect
71+
golang.org/x/term v0.21.0 // indirect
6972
golang.org/x/time v0.5.0 // indirect
70-
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect
73+
golang.org/x/tools v0.22.0 // indirect
7174
google.golang.org/genproto/googleapis/rpc v0.0.0-20240528184218-531527333157 // indirect
7275
google.golang.org/protobuf v1.34.1 // indirect
7376
gopkg.in/inf.v0 v0.9.1 // indirect

go.sum

+10-8
Original file line numberDiff line numberDiff line change
@@ -115,13 +115,15 @@ github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
115115
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
116116
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
117117
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
118-
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
119-
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
118+
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
119+
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
120120
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
121121
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
122122
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
123123
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
124124
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
125+
go.uber.org/mock v0.5.0 h1:KAMbZvZPyBPWgD14IrIQ38QCyjwpvVVV6K/bHl1IwQU=
126+
go.uber.org/mock v0.5.0/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM=
125127
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
126128
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
127129
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
@@ -137,8 +139,8 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL
137139
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
138140
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
139141
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
140-
golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac=
141-
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
142+
golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ=
143+
golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE=
142144
golang.org/x/oauth2 v0.20.0 h1:4mQdhULixXKP1rwYBW0vAijoXnkTG0BLCDRzfe1idMo=
143145
golang.org/x/oauth2 v0.20.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
144146
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -157,8 +159,8 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc
157159
golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo=
158160
golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
159161
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
160-
golang.org/x/term v0.20.0 h1:VnkxpohqXaOBYJtBmEppKUG6mXpi+4O6purfc2+sMhw=
161-
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
162+
golang.org/x/term v0.21.0 h1:WVXCp+/EBEHOj53Rvu+7KiT/iElMrO8ACK16SMZ3jaA=
163+
golang.org/x/term v0.21.0/go.mod h1:ooXLefLobQVslOqselCNF4SxFAaoS6KujMbsGzSDmX0=
162164
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
163165
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
164166
golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM=
@@ -170,8 +172,8 @@ golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtn
170172
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
171173
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
172174
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
173-
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg=
174-
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
175+
golang.org/x/tools v0.22.0 h1:gqSGLZqv+AI9lIQzniJ0nZDRG5GBPsSi+DRNHWNz6yA=
176+
golang.org/x/tools v0.22.0/go.mod h1:aCwcsjqvq7Yqt6TNyX7QMU2enbQ/Gt0bo6krSeEri+c=
175177
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
176178
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
177179
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=

hack/go_install.sh

+45
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
#!/usr/bin/env bash
2+
# Copyright 2021 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+
set -o errexit
17+
set -o nounset
18+
set -o pipefail
19+
20+
if [ -z "${1}" ]; then
21+
echo "must provide module as first parameter"
22+
exit 1
23+
fi
24+
25+
if [ -z "${2}" ]; then
26+
echo "must provide binary name as second parameter"
27+
exit 1
28+
fi
29+
30+
if [ -z "${3}" ]; then
31+
echo "must provide version as third parameter"
32+
exit 1
33+
fi
34+
35+
if [ -z "${GOBIN}" ]; then
36+
echo "GOBIN is not set. Must set GOBIN to install the bin in a specified directory."
37+
exit 1
38+
fi
39+
40+
rm -f "${GOBIN}/${2}"* || true
41+
42+
# install the golang module specified as the first argument
43+
go install "${1}@${3}"
44+
mv "${GOBIN}/${2}" "${GOBIN}/${2}-${3}"
45+
ln -sf "${GOBIN}/${2}-${3}" "${GOBIN}/${2}"

pkg/cloud/cloud.go

+6-4
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,10 @@ import (
99
"github.com/apache/cloudstack-go/v2/cloudstack"
1010
)
1111

12-
// Interface is the CloudStack client interface.
13-
type Interface interface {
12+
//go:generate ../../hack/tools/bin/mockgen -destination=./mock_cloud.go -package=cloud -source ./cloud.go
13+
14+
// Cloud is the CloudStack client interface.
15+
type Cloud interface {
1416
GetNodeInfo(ctx context.Context, vmName string) (*VM, error)
1517
GetVMByID(ctx context.Context, vmID string) (*VM, error)
1618

@@ -52,14 +54,14 @@ var (
5254
ErrTooManyResults = errors.New("too many results")
5355
)
5456

55-
// client is the implementation of Interface.
57+
// client is the implementation of Cloud.
5658
type client struct {
5759
*cloudstack.CloudStackClient
5860
projectID string
5961
}
6062

6163
// New creates a new cloud connector, given its configuration.
62-
func New(config *Config) Interface {
64+
func New(config *Config) Cloud {
6365
csClient := &client{
6466
projectID: config.ProjectID,
6567
}

pkg/cloud/fake/fake.go

+37-5
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ package fake
44

55
import (
66
"context"
7+
"errors"
78

89
"github.com/hashicorp/go-uuid"
910

@@ -21,7 +22,7 @@ type fakeConnector struct {
2122

2223
// New returns a new fake implementation of the
2324
// CloudStack connector.
24-
func New() cloud.Interface {
25+
func New() cloud.Cloud {
2526
volume := cloud.Volume{
2627
ID: "ace9f28b-3081-40c1-8353-4cc3e3014072",
2728
Name: "vol-1",
@@ -102,12 +103,32 @@ func (f *fakeConnector) DeleteVolume(_ context.Context, id string) error {
102103
return nil
103104
}
104105

105-
func (f *fakeConnector) AttachVolume(_ context.Context, _, _ string) (string, error) {
106-
return "1", nil
106+
func (f *fakeConnector) AttachVolume(_ context.Context, volumeID, nodeID string) (string, error) {
107+
if f.getVolumesPerServer(nodeID) >= 16 {
108+
return "", errors.New("The specified VM already has the maximum number of data disks (16) attached. Please specify another VM.") //nolint:revive,stylecheck
109+
}
110+
111+
if vol, ok := f.volumesByID[volumeID]; ok {
112+
vol.VirtualMachineID = nodeID
113+
f.volumesByID[volumeID] = vol
114+
f.volumesByName[vol.Name] = vol
115+
116+
return "1", nil
117+
}
118+
119+
return "", cloud.ErrNotFound
107120
}
108121

109-
func (f *fakeConnector) DetachVolume(_ context.Context, _ string) error {
110-
return nil
122+
func (f *fakeConnector) DetachVolume(_ context.Context, volumeID string) error {
123+
if vol, ok := f.volumesByID[volumeID]; ok {
124+
vol.VirtualMachineID = ""
125+
f.volumesByID[volumeID] = vol
126+
f.volumesByName[vol.Name] = vol
127+
128+
return nil
129+
}
130+
131+
return cloud.ErrNotFound
111132
}
112133

113134
func (f *fakeConnector) ExpandVolume(_ context.Context, volumeID string, newSizeInGB int64) error {
@@ -124,3 +145,14 @@ func (f *fakeConnector) ExpandVolume(_ context.Context, volumeID string, newSize
124145

125146
return cloud.ErrNotFound
126147
}
148+
149+
func (f *fakeConnector) getVolumesPerServer(nodeID string) int {
150+
volumesCount := 0
151+
for _, v := range f.volumesByID {
152+
if v.VirtualMachineID == nodeID {
153+
volumesCount++
154+
}
155+
}
156+
157+
return volumesCount
158+
}

0 commit comments

Comments
 (0)