Skip to content

Commit

Permalink
Add some unit tests to get above coverage requirements
Browse files Browse the repository at this point in the history
  • Loading branch information
thunderboltsid committed Mar 11, 2024
1 parent 935ae25 commit bb65336
Show file tree
Hide file tree
Showing 13 changed files with 1,237 additions and 62 deletions.
22 changes: 16 additions & 6 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ PLATFORMS_E2E ?= linux/amd64
KIND_CLUSTER_NAME ?= capi-test

# ENVTEST_K8S_VERSION refers to the version of kubebuilder assets to be downloaded by envtest binary.
ENVTEST_K8S_VERSION = 1.23
ENVTEST_K8S_VERSION = 1.29

#
# Directories.
Expand Down Expand Up @@ -174,7 +174,7 @@ kind-delete: ## Delete the kind cluster
##@ Build

.PHONY: build
build: generate fmt ## Build manager binary.
build: mocks generate fmt ## Build manager binary.
echo "Git commit hash: ${GIT_COMMIT_HASH}"
go build -ldflags "-X main.gitCommitHash=${GIT_COMMIT_HASH}" -o bin/manager main.go

Expand Down Expand Up @@ -299,16 +299,26 @@ prepare-local-clusterctl: manifests cluster-templates ## Prepare overide file f
env LOCAL_PROVIDER_VERSION=$(LOCAL_PROVIDER_VERSION) \
envsubst -no-unset -no-empty -no-digit < ./clusterctl.yaml > ~/.cluster-api/clusterctl.yaml

.PHONY: mocks
mocks: ## Generate mocks for the project
mockgen -destination=mocks/ctlclient/client_mock.go -package=mockctlclient sigs.k8s.io/controller-runtime/pkg/client Client
mockgen -destination=mocks/ctlclient/manager_mock.go -package=mockctlclient sigs.k8s.io/controller-runtime/pkg/manager Manager
mockgen -destination=mocks/ctlclient/cache_mock.go -package=mockctlclient sigs.k8s.io/controller-runtime/pkg/cache Cache
mockgen -destination=mocks/k8sclient/cm_informer.go -package=mockk8sclient k8s.io/client-go/informers/core/v1 ConfigMapInformer
mockgen -destination=mocks/k8sclient/secret_informer.go -package=mockk8sclient k8s.io/client-go/informers/core/v1 SecretInformer

GOTESTPKGS = $(shell go list ./... | grep -v /mocks)

.PHONY: unit-test
unit-test: ## Run unit tests.
unit-test: mocks ## Run unit tests.
ifeq ($(EXPORT_RESULT), true)
$(eval OUTPUT_OPTIONS = | go-junit-report -set-exit-code > junit-report.xml)
endif
KUBEBUILDER_ASSETS="$(shell setup-envtest use $(ENVTEST_K8S_VERSION) --arch=amd64 -p path)" $(GOTEST) ./... $(OUTPUT_OPTIONS)
KUBEBUILDER_ASSETS="$(shell setup-envtest use $(ENVTEST_K8S_VERSION) --arch=amd64 -p path)" $(GOTEST) $(GOTESTPKGS) $(OUTPUT_OPTIONS)

.PHONY: coverage
coverage: ## Run the tests of the project and export the coverage
KUBEBUILDER_ASSETS="$(shell setup-envtest use $(ENVTEST_K8S_VERSION) --arch=amd64 -p path)" $(GOTEST) -cover -covermode=count -coverprofile=profile.cov ./...
coverage: mocks ## Run the tests of the project and export the coverage
KUBEBUILDER_ASSETS="$(shell setup-envtest use $(ENVTEST_K8S_VERSION) --arch=amd64 -p path)" $(GOTEST) -cover -covermode=count -coverprofile=profile.cov $(GOTESTPKGS)
$(GOTOOL) cover -func profile.cov
ifeq ($(EXPORT_RESULT), true)
gocov convert profile.cov | gocov-xml > coverage.xml
Expand Down
1 change: 1 addition & 0 deletions devbox.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
"kubernetes-code-generator@0.25.4",
"kubernetes-controller-tools@0.13.0",
"kustomize@5.3.0",
"mockgen@1.6.0",
"yamllint@1.35.1",
"path:./hack/flakes#go-apidiff",
"path:./hack/flakes#go-mod-upgrade",
Expand Down
20 changes: 20 additions & 0 deletions devbox.lock
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,26 @@
}
}
},
"mockgen@1.6.0": {
"last_modified": "2023-12-13T22:54:10Z",
"resolved": "github:NixOS/nixpkgs/fd04bea4cbf76f86f244b9e2549fca066db8ddff#mockgen",
"source": "devbox-search",
"version": "1.6.0",
"systems": {
"aarch64-darwin": {
"store_path": "/nix/store/8jsr1j1h8g3k3j5rg8vv79aviffxrfny-mockgen-1.6.0"
},
"aarch64-linux": {
"store_path": "/nix/store/34rrlc5lz43mibljdd7ai0qyi7zcq4ic-mockgen-1.6.0"
},
"x86_64-darwin": {
"store_path": "/nix/store/cj1r5vllmiq7fh4izsmlacjrdkw2h4b5-mockgen-1.6.0"
},
"x86_64-linux": {
"store_path": "/nix/store/hs49h3p9ss5f5bqv6phgsx93s4cawif2-mockgen-1.6.0"
}
}
},
"yamllint@1.35.1": {
"last_modified": "2024-02-24T23:06:34Z",
"resolved": "github:NixOS/nixpkgs/9a9dae8f6319600fa9aebde37f340975cab4b8c0#yamllint",
Expand Down
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ require (
github.com/gobuffalo/flect v1.0.2 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/golang/mock v1.6.0 // indirect
github.com/golang/protobuf v1.5.3 // indirect
github.com/google/cel-go v0.17.7 // indirect
github.com/google/gnostic-models v0.6.8 // indirect
Expand Down Expand Up @@ -136,6 +137,7 @@ require (
go.uber.org/multierr v1.11.0 // indirect
golang.org/x/crypto v0.17.0 // indirect
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect
golang.org/x/mod v0.14.0 // indirect
golang.org/x/net v0.19.0 // indirect
golang.org/x/oauth2 v0.14.0 // indirect
golang.org/x/sync v0.5.0 // indirect
Expand Down
5 changes: 5 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,8 @@ github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt
github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8=
github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
Expand Down Expand Up @@ -741,6 +743,8 @@ golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0=
golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
Expand Down Expand Up @@ -968,6 +972,7 @@ golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4f
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
Expand Down
170 changes: 114 additions & 56 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,36 +17,39 @@ limitations under the License.
package main

import (
"context"
"flag"
"fmt"
"os"
"time"

"github.com/go-logr/logr"
"github.com/spf13/pflag"
"go.uber.org/zap/zapcore"
"k8s.io/apimachinery/pkg/runtime"
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
"k8s.io/client-go/informers"
coreinformers "k8s.io/client-go/informers/core/v1"
"k8s.io/client-go/kubernetes"
clientgoscheme "k8s.io/client-go/kubernetes/scheme"
_ "k8s.io/client-go/plugin/pkg/client/auth"
"k8s.io/client-go/rest"
"k8s.io/client-go/tools/cache"
capiv1 "sigs.k8s.io/cluster-api/api/v1beta1"
bootstrapv1 "sigs.k8s.io/cluster-api/bootstrap/kubeadm/api/v1beta1"
capiflags "sigs.k8s.io/cluster-api/util/flags"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/healthz"
"sigs.k8s.io/controller-runtime/pkg/log/zap"
"sigs.k8s.io/controller-runtime/pkg/manager"

infrav1alpha4 "github.com/nutanix-cloud-native/cluster-api-provider-nutanix/api/v1alpha4"
infrav1 "github.com/nutanix-cloud-native/cluster-api-provider-nutanix/api/v1beta1"
"github.com/nutanix-cloud-native/cluster-api-provider-nutanix/controllers"
//+kubebuilder:scaffold:imports
)

var (
scheme = runtime.NewScheme()
setupLog = ctrl.Log.WithName("setup")
)
var scheme = runtime.NewScheme()

// gitCommitHash is the git commit hash of the code that is running.
var gitCommitHash string
Expand All @@ -65,23 +68,22 @@ const (
defaultMaxConcurrentReconciles = 10
)

func main() {
var (
enableLeaderElection bool
probeAddr string
maxConcurrentReconciles int
diagnosticsOptions capiflags.DiagnosticsOptions
)
type managerConfig struct {
enableLeaderElection bool
probeAddr string
maxConcurrentReconciles int
diagnosticsOptions capiflags.DiagnosticsOptions

logger logr.Logger
restConfig *rest.Config
}

capiflags.AddDiagnosticsOptions(pflag.CommandLine, &diagnosticsOptions)
pflag.StringVar(&probeAddr, "health-probe-bind-address", ":8081", "The address the probe endpoint binds to.")
pflag.BoolVar(&enableLeaderElection, "leader-elect", false,
"Enable leader election for controller manager. "+
"Enabling this will ensure there is only one active controller manager.")
pflag.IntVar(
&maxConcurrentReconciles,
"max-concurrent-reconciles",
defaultMaxConcurrentReconciles,
func parseFlags(config *managerConfig) {
capiflags.AddDiagnosticsOptions(pflag.CommandLine, &config.diagnosticsOptions)
pflag.StringVar(&config.probeAddr, "health-probe-bind-address", ":8081", "The address the probe endpoint binds to.")
pflag.BoolVar(&config.enableLeaderElection, "leader-elect", false,
"Enable leader election for controller manager. Enabling this will ensure there is only one active controller manager.")
pflag.IntVar(&config.maxConcurrentReconciles, "max-concurrent-reconciles", defaultMaxConcurrentReconciles,
"The maximum number of allowed, concurrent reconciles.")

opts := zap.Options{
Expand All @@ -93,29 +95,29 @@ func main() {
ctrl.SetLogger(logger)
pflag.CommandLine.AddGoFlagSet(flag.CommandLine)
pflag.Parse()
}

setupLog.Info("Initializing Nutanix Cluster API Infrastructure Provider", "Git Hash", gitCommitHash)
func setupLogger() logr.Logger {
return ctrl.Log.WithName("setup")
}

mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{
Scheme: scheme,
Metrics: capiflags.GetDiagnosticsOptions(diagnosticsOptions),
HealthProbeBindAddress: probeAddr,
LeaderElection: enableLeaderElection,
LeaderElectionID: "f265110d.cluster.x-k8s.io",
})
if err != nil {
setupLog.Error(err, "unable to create manager")
os.Exit(1)
func addHealthChecks(mgr manager.Manager) error {
if err := mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil {
return fmt.Errorf("unable to set up health check: %w", err)
}

// Set up the context that's going to be used in controllers and for the manager.
ctx := ctrl.SetupSignalHandler()
if err := mgr.AddReadyzCheck("readyz", healthz.Ping); err != nil {
return fmt.Errorf("unable to set up ready check: %w", err)
}

return nil
}

func createInformers(ctx context.Context, mgr manager.Manager) (coreinformers.SecretInformer, coreinformers.ConfigMapInformer, error) {
// Create a secret informer for the Nutanix client
clientset, err := kubernetes.NewForConfig(mgr.GetConfig())
if err != nil {
setupLog.Error(err, "unable to create clientset for management cluster")
os.Exit(1)
return nil, nil, fmt.Errorf("unable to create clientset for management cluster: %w", err)
}

informerFactory := informers.NewSharedInformerFactory(clientset, time.Minute)
Expand All @@ -129,50 +131,106 @@ func main() {
go cmInformer.Run(ctx.Done())
cache.WaitForCacheSync(ctx.Done(), cmInformer.HasSynced)

clusterCtrl, err := controllers.NewNutanixClusterReconciler(mgr.GetClient(),
return secretInformer, configMapInformer, nil
}

func setupControllers(ctx context.Context, mgr manager.Manager, secretInformer coreinformers.SecretInformer,
configMapInformer coreinformers.ConfigMapInformer, opts ...controllers.ControllerConfigOpts,
) error {
clusterCtrl, err := controllers.NewNutanixClusterReconciler(
mgr.GetClient(),
secretInformer,
configMapInformer,
mgr.GetScheme(),
controllers.WithMaxConcurrentReconciles(maxConcurrentReconciles),
opts...,
)
if err != nil {
setupLog.Error(err, "unable to create controller", "controller", "NutanixCluster")
os.Exit(1)
return fmt.Errorf("unable to create NutanixCluster controller: %w", err)
}

if err = clusterCtrl.SetupWithManager(ctx, mgr); err != nil {
setupLog.Error(err, "unable to create controller", "controller", "NutanixCluster")
os.Exit(1)
if err := clusterCtrl.SetupWithManager(ctx, mgr); err != nil {
return fmt.Errorf("unable to setup NutanixCluster controller with manager: %w", err)

Check warning on line 152 in main.go

View check run for this annotation

Codecov / codecov/patch

main.go#L152

Added line #L152 was not covered by tests
}

machineCtrl, err := controllers.NewNutanixMachineReconciler(
mgr.GetClient(),
secretInformer,
configMapInformer,
mgr.GetScheme(),
controllers.WithMaxConcurrentReconciles(maxConcurrentReconciles),
opts...,
)
if err != nil {
setupLog.Error(err, "unable to create controller", "controller", "NutanixMachine")
os.Exit(1)
return fmt.Errorf("unable to create NutanixMachine controller: %w", err)

Check warning on line 163 in main.go

View check run for this annotation

Codecov / codecov/patch

main.go#L163

Added line #L163 was not covered by tests
}
if err = machineCtrl.SetupWithManager(ctx, mgr); err != nil {
setupLog.Error(err, "unable to create controller", "controller", "NutanixMachine")
os.Exit(1)

if err := machineCtrl.SetupWithManager(ctx, mgr); err != nil {
return fmt.Errorf("unable to setup NutanixMachine controller with manager: %w", err)

Check warning on line 167 in main.go

View check run for this annotation

Codecov / codecov/patch

main.go#L167

Added line #L167 was not covered by tests
}
//+kubebuilder:scaffold:builder

if err := mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil {
setupLog.Error(err, "unable to set up health check")
os.Exit(1)
return nil
}

func runManager(ctx context.Context, mgr manager.Manager, config *managerConfig) error {
secretInformer, configMapInformer, err := createInformers(ctx, mgr)
if err != nil {
return fmt.Errorf("unable to create informers: %w", err)
}
if err := mgr.AddReadyzCheck("readyz", healthz.Ping); err != nil {
setupLog.Error(err, "unable to set up ready check")
os.Exit(1)

if err := setupControllers(ctx, mgr, secretInformer, configMapInformer,
controllers.WithMaxConcurrentReconciles(config.maxConcurrentReconciles)); err != nil {
return fmt.Errorf("unable to setup controllers: %w", err)

Check warning on line 182 in main.go

View check run for this annotation

Codecov / codecov/patch

main.go#L182

Added line #L182 was not covered by tests
}

setupLog.Info("starting CAPX Controller Manager")
config.logger.Info("starting CAPX Controller Manager")
if err := mgr.Start(ctx); err != nil {
setupLog.Error(err, "problem running manager")
return fmt.Errorf("problem running manager: %w", err)
}

return nil
}

func initializeManager(config *managerConfig) (manager.Manager, error) {
mgr, err := ctrl.NewManager(config.restConfig, ctrl.Options{
Scheme: scheme,
Metrics: capiflags.GetDiagnosticsOptions(config.diagnosticsOptions),
HealthProbeBindAddress: config.probeAddr,
LeaderElection: config.enableLeaderElection,
LeaderElectionID: "f265110d.cluster.x-k8s.io",
})
if err != nil {
return nil, fmt.Errorf("unable to create manager: %w", err)
}

if err := addHealthChecks(mgr); err != nil {
return nil, fmt.Errorf("unable to add health checks to manager: %w", err)

Check warning on line 206 in main.go

View check run for this annotation

Codecov / codecov/patch

main.go#L206

Added line #L206 was not covered by tests
}

return mgr, nil
}

func main() {
logger := setupLogger()
logger.Info("Initializing Nutanix Cluster API Infrastructure Provider", "Git Hash", gitCommitHash)

Check warning on line 214 in main.go

View check run for this annotation

Codecov / codecov/patch

main.go#L213-L214

Added lines #L213 - L214 were not covered by tests

restConfig := ctrl.GetConfigOrDie()

Check warning on line 216 in main.go

View check run for this annotation

Codecov / codecov/patch

main.go#L216

Added line #L216 was not covered by tests

config := &managerConfig{

Check warning on line 218 in main.go

View check run for this annotation

Codecov / codecov/patch

main.go#L218

Added line #L218 was not covered by tests
logger: logger,
restConfig: restConfig,
}
parseFlags(config)

Check warning on line 222 in main.go

View check run for this annotation

Codecov / codecov/patch

main.go#L222

Added line #L222 was not covered by tests

mgr, err := initializeManager(config)
if err != nil {
logger.Error(err, "unable to create manager")
os.Exit(1)

Check warning on line 227 in main.go

View check run for this annotation

Codecov / codecov/patch

main.go#L224-L227

Added lines #L224 - L227 were not covered by tests
}

// Set up the context that's going to be used in controllers and for the manager.
ctx := ctrl.SetupSignalHandler()
if err := runManager(ctx, mgr, config); err != nil {
logger.Error(err, "problem running manager")

Check warning on line 233 in main.go

View check run for this annotation

Codecov / codecov/patch

main.go#L231-L233

Added lines #L231 - L233 were not covered by tests
os.Exit(1)
}
}
Loading

0 comments on commit bb65336

Please sign in to comment.