Skip to content

Commit

Permalink
Merge pull request #165 from Leaseweb/feat/testability
Browse files Browse the repository at this point in the history
Fix idempotency and improve testability
  • Loading branch information
hrak authored Nov 25, 2024
2 parents 26998d1 + 5f3c4ef commit a6c8784
Show file tree
Hide file tree
Showing 21 changed files with 943 additions and 342 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/pr-check.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ jobs:

build:
name: Test & Build
runs-on: ubuntu-20.04
runs-on: ubuntu-24.04
steps:
- name: Setup up Go 1.22
uses: actions/setup-go@v5
Expand Down
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@
/test/e2e/ginkgo
cloud-config
/vendor

/hack/tools/bin
27 changes: 27 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,20 @@ IMPORTPATH_LDFLAGS = -X ${PKG}/pkg/driver.driverVersion=$(REV) -X ${PKG}/pkg/dri
LDFLAGS = -s -w
FULL_LDFLAGS = $(LDFLAGS) $(IMPORTPATH_LDFLAGS)

export REPO_ROOT := $(shell git rev-parse --show-toplevel)

# Directories
TOOLS_DIR := $(REPO_ROOT)/hack/tools
TOOLS_BIN_DIR := $(TOOLS_DIR)/bin
BIN_DIR ?= bin

GO_INSTALL := ./hack/go_install.sh

MOCKGEN_BIN := mockgen
MOCKGEN_VER := v0.5.0
MOCKGEN := $(abspath $(TOOLS_BIN_DIR)/$(MOCKGEN_BIN)-$(MOCKGEN_VER))
MOCKGEN_PKG := go.uber.org/mock/mockgen

.PHONY: all
all: build

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

.PHONY: generate-mocks
generate-mocks: $(MOCKGEN) pkg/cloud/mock_cloud.go ## Generate mocks needed for testing. Primarily mocks of the cloud package.
pkg/cloud/mock%.go: $(shell find ./pkg/cloud -type f -name "*test*" -prune -o -print)
go generate ./...

.PHONY: test
test:
go test ./...
Expand All @@ -59,3 +78,11 @@ test/e2e/e2e.test test/e2e/ginkgo:
.PHONY: test-e2e
test-e2e: setup-external-e2e
bash ./test/e2e/run.sh

##@ hack/tools:

.PHONY: $(MOCKGEN_BIN)
$(MOCKGEN_BIN): $(MOCKGEN) ## Build a local copy of mockgen.

$(MOCKGEN): # Build mockgen from tools folder.
GOBIN=$(TOOLS_BIN_DIR) $(GO_INSTALL) $(MOCKGEN_PKG) $(MOCKGEN_BIN) $(MOCKGEN_VER)
2 changes: 1 addition & 1 deletion cmd/cloudstack-csi-driver/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ func main() {
ctx := klog.NewContext(context.Background(), logger)
csConnector := cloud.New(config)

d, err := driver.New(ctx, csConnector, &options, nil)
d, err := driver.NewDriver(ctx, csConnector, &options, nil)
if err != nil {
logger.Error(err, "Failed to initialize driver")
klog.FlushAndExit(klog.ExitFlushTimeout, 1)
Expand Down
11 changes: 7 additions & 4 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,13 @@ toolchain go1.22.8
require (
github.com/apache/cloudstack-go/v2 v2.16.1
github.com/container-storage-interface/spec v1.9.0
github.com/golang/mock v1.6.0
github.com/hashicorp/go-uuid v1.0.3
github.com/kubernetes-csi/csi-lib-utils v0.18.1
github.com/kubernetes-csi/csi-test/v5 v5.2.0
github.com/spf13/pflag v1.0.5
github.com/stretchr/testify v1.9.0
go.uber.org/mock v0.5.0
golang.org/x/sys v0.26.0
golang.org/x/text v0.19.0
google.golang.org/grpc v1.65.0
Expand All @@ -37,7 +40,6 @@ require (
github.com/go-openapi/swag v0.22.3 // indirect
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/mock v1.6.0 // indirect
github.com/golang/protobuf v1.5.4 // indirect
github.com/google/gnostic-models v0.6.8 // indirect
github.com/google/go-cmp v0.6.0 // indirect
Expand All @@ -56,18 +58,19 @@ require (
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/onsi/ginkgo/v2 v2.15.0 // indirect
github.com/onsi/gomega v1.31.0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/prometheus/client_golang v1.16.0 // indirect
github.com/prometheus/client_model v0.4.0 // indirect
github.com/prometheus/common v0.44.0 // indirect
github.com/prometheus/procfs v0.10.1 // indirect
github.com/spf13/cobra v1.7.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
go.uber.org/zap v1.27.0 // indirect
golang.org/x/net v0.25.0 // indirect
golang.org/x/net v0.26.0 // indirect
golang.org/x/oauth2 v0.20.0 // indirect
golang.org/x/term v0.20.0 // indirect
golang.org/x/term v0.21.0 // indirect
golang.org/x/time v0.5.0 // indirect
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect
golang.org/x/tools v0.22.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240528184218-531527333157 // indirect
google.golang.org/protobuf v1.34.1 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
Expand Down
18 changes: 10 additions & 8 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -115,13 +115,15 @@ github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
go.uber.org/mock v0.5.0 h1:KAMbZvZPyBPWgD14IrIQ38QCyjwpvVVV6K/bHl1IwQU=
go.uber.org/mock v0.5.0/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM=
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
Expand All @@ -137,8 +139,8 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac=
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ=
golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE=
golang.org/x/oauth2 v0.20.0 h1:4mQdhULixXKP1rwYBW0vAijoXnkTG0BLCDRzfe1idMo=
golang.org/x/oauth2 v0.20.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
Expand All @@ -157,8 +159,8 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo=
golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.20.0 h1:VnkxpohqXaOBYJtBmEppKUG6mXpi+4O6purfc2+sMhw=
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
golang.org/x/term v0.21.0 h1:WVXCp+/EBEHOj53Rvu+7KiT/iElMrO8ACK16SMZ3jaA=
golang.org/x/term v0.21.0/go.mod h1:ooXLefLobQVslOqselCNF4SxFAaoS6KujMbsGzSDmX0=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM=
Expand All @@ -170,8 +172,8 @@ golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtn
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg=
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
golang.org/x/tools v0.22.0 h1:gqSGLZqv+AI9lIQzniJ0nZDRG5GBPsSi+DRNHWNz6yA=
golang.org/x/tools v0.22.0/go.mod h1:aCwcsjqvq7Yqt6TNyX7QMU2enbQ/Gt0bo6krSeEri+c=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
Expand Down
45 changes: 45 additions & 0 deletions hack/go_install.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
#!/usr/bin/env bash
# Copyright 2021 The Kubernetes Authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

set -o errexit
set -o nounset
set -o pipefail

if [ -z "${1}" ]; then
echo "must provide module as first parameter"
exit 1
fi

if [ -z "${2}" ]; then
echo "must provide binary name as second parameter"
exit 1
fi

if [ -z "${3}" ]; then
echo "must provide version as third parameter"
exit 1
fi

if [ -z "${GOBIN}" ]; then
echo "GOBIN is not set. Must set GOBIN to install the bin in a specified directory."
exit 1
fi

rm -f "${GOBIN}/${2}"* || true

# install the golang module specified as the first argument
go install "${1}@${3}"
mv "${GOBIN}/${2}" "${GOBIN}/${2}-${3}"
ln -sf "${GOBIN}/${2}-${3}" "${GOBIN}/${2}"
10 changes: 6 additions & 4 deletions pkg/cloud/cloud.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,10 @@ import (
"github.com/apache/cloudstack-go/v2/cloudstack"
)

// Interface is the CloudStack client interface.
type Interface interface {
//go:generate ../../hack/tools/bin/mockgen -destination=./mock_cloud.go -package=cloud -source ./cloud.go

// Cloud is the CloudStack client interface.
type Cloud interface {
GetNodeInfo(ctx context.Context, vmName string) (*VM, error)
GetVMByID(ctx context.Context, vmID string) (*VM, error)

Expand Down Expand Up @@ -52,14 +54,14 @@ var (
ErrTooManyResults = errors.New("too many results")
)

// client is the implementation of Interface.
// client is the implementation of Cloud.
type client struct {
*cloudstack.CloudStackClient
projectID string
}

// New creates a new cloud connector, given its configuration.
func New(config *Config) Interface {
func New(config *Config) Cloud {
csClient := &client{
projectID: config.ProjectID,
}
Expand Down
42 changes: 37 additions & 5 deletions pkg/cloud/fake/fake.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ package fake

import (
"context"
"errors"

"github.com/hashicorp/go-uuid"

Expand All @@ -21,7 +22,7 @@ type fakeConnector struct {

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

func (f *fakeConnector) AttachVolume(_ context.Context, _, _ string) (string, error) {
return "1", nil
func (f *fakeConnector) AttachVolume(_ context.Context, volumeID, nodeID string) (string, error) {
if f.getVolumesPerServer(nodeID) >= 16 {
return "", errors.New("The specified VM already has the maximum number of data disks (16) attached. Please specify another VM.") //nolint:revive,stylecheck
}

if vol, ok := f.volumesByID[volumeID]; ok {
vol.VirtualMachineID = nodeID
f.volumesByID[volumeID] = vol
f.volumesByName[vol.Name] = vol

return "1", nil
}

return "", cloud.ErrNotFound
}

func (f *fakeConnector) DetachVolume(_ context.Context, _ string) error {
return nil
func (f *fakeConnector) DetachVolume(_ context.Context, volumeID string) error {
if vol, ok := f.volumesByID[volumeID]; ok {
vol.VirtualMachineID = ""
f.volumesByID[volumeID] = vol
f.volumesByName[vol.Name] = vol

return nil
}

return cloud.ErrNotFound
}

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

return cloud.ErrNotFound
}

func (f *fakeConnector) getVolumesPerServer(nodeID string) int {
volumesCount := 0
for _, v := range f.volumesByID {
if v.VirtualMachineID == nodeID {
volumesCount++
}
}

return volumesCount
}
Loading

0 comments on commit a6c8784

Please sign in to comment.