diff --git a/Makefile b/Makefile index a29b0e0..7bb7eb3 100644 --- a/Makefile +++ b/Makefile @@ -43,8 +43,9 @@ manifests: controller-gen ## Generate WebhookConfiguration, ClusterRole and Cust $(CONTROLLER_GEN) rbac:roleName=manager-role crd webhook paths="./..." output:crd:artifacts:config=config/crd/bases .PHONY: generate -generate: controller-gen ## Generate code containing DeepCopy, DeepCopyInto, and DeepCopyObject method implementations. +generate: controller-gen counterfeiter ## Generate code containing DeepCopy, DeepCopyInto, and DeepCopyObject method implementations. $(CONTROLLER_GEN) object:headerFile="hack/boilerplate.go.txt" paths="./..." + go generate ./... .PHONY: fmt fmt: ## Run go fmt against code. @@ -55,8 +56,8 @@ vet: ## Run go vet against code. go vet ./... .PHONY: test -test: manifests generate fmt vet envtest ## Run tests. - KUBEBUILDER_ASSETS="$(shell $(ENVTEST) use $(ENVTEST_K8S_VERSION) --bin-dir $(LOCALBIN) -p path)" go test ./... -coverprofile cover.out +test: ## Run tests. + go test -v ./controllers/... ./internal/... ##@ Build @@ -130,6 +131,7 @@ $(LOCALBIN): KUSTOMIZE ?= $(LOCALBIN)/kustomize CONTROLLER_GEN ?= $(LOCALBIN)/controller-gen ENVTEST ?= $(LOCALBIN)/setup-envtest +COUNTERFEITER := $(LOCALBIN)/counterfeiter ## Tool Versions KUSTOMIZE_VERSION ?= v3.8.7 @@ -146,6 +148,11 @@ controller-gen: $(CONTROLLER_GEN) ## Download controller-gen locally if necessar $(CONTROLLER_GEN): $(LOCALBIN) test -s $(LOCALBIN)/controller-gen || GOBIN=$(LOCALBIN) go install sigs.k8s.io/controller-tools/cmd/controller-gen@$(CONTROLLER_TOOLS_VERSION) +.PHONY: counterfeiter +counterfeiter: $(COUNTERFEITER) +$(COUNTERFEITER): $(LOCALBIN) + test -s $(LOCALBIN)/counterfeiter || GOBIN=$(LOCALBIN) go install github.com/maxbrunsfeld/counterfeiter/v6 + .PHONY: envtest envtest: $(ENVTEST) ## Download envtest-setup locally if necessary. $(ENVTEST): $(LOCALBIN) diff --git a/controllers/fakes/fake_client.go b/controllers/fakes/fake_client.go new file mode 100644 index 0000000..d51b765 --- /dev/null +++ b/controllers/fakes/fake_client.go @@ -0,0 +1,486 @@ +// Code generated by counterfeiter. DO NOT EDIT. +package fakes + +import ( + "context" + "sync" + + "github.com/weaveworks-liquidmetal/controller-pkg/client" + "github.com/weaveworks-liquidmetal/flintlock/api/services/microvm/v1alpha1" + "google.golang.org/grpc" + "google.golang.org/protobuf/types/known/emptypb" +) + +type FakeClient struct { + CloseStub func() + closeMutex sync.RWMutex + closeArgsForCall []struct { + } + CreateMicroVMStub func(context.Context, *v1alpha1.CreateMicroVMRequest, ...grpc.CallOption) (*v1alpha1.CreateMicroVMResponse, error) + createMicroVMMutex sync.RWMutex + createMicroVMArgsForCall []struct { + arg1 context.Context + arg2 *v1alpha1.CreateMicroVMRequest + arg3 []grpc.CallOption + } + createMicroVMReturns struct { + result1 *v1alpha1.CreateMicroVMResponse + result2 error + } + createMicroVMReturnsOnCall map[int]struct { + result1 *v1alpha1.CreateMicroVMResponse + result2 error + } + DeleteMicroVMStub func(context.Context, *v1alpha1.DeleteMicroVMRequest, ...grpc.CallOption) (*emptypb.Empty, error) + deleteMicroVMMutex sync.RWMutex + deleteMicroVMArgsForCall []struct { + arg1 context.Context + arg2 *v1alpha1.DeleteMicroVMRequest + arg3 []grpc.CallOption + } + deleteMicroVMReturns struct { + result1 *emptypb.Empty + result2 error + } + deleteMicroVMReturnsOnCall map[int]struct { + result1 *emptypb.Empty + result2 error + } + GetMicroVMStub func(context.Context, *v1alpha1.GetMicroVMRequest, ...grpc.CallOption) (*v1alpha1.GetMicroVMResponse, error) + getMicroVMMutex sync.RWMutex + getMicroVMArgsForCall []struct { + arg1 context.Context + arg2 *v1alpha1.GetMicroVMRequest + arg3 []grpc.CallOption + } + getMicroVMReturns struct { + result1 *v1alpha1.GetMicroVMResponse + result2 error + } + getMicroVMReturnsOnCall map[int]struct { + result1 *v1alpha1.GetMicroVMResponse + result2 error + } + ListMicroVMsStub func(context.Context, *v1alpha1.ListMicroVMsRequest, ...grpc.CallOption) (*v1alpha1.ListMicroVMsResponse, error) + listMicroVMsMutex sync.RWMutex + listMicroVMsArgsForCall []struct { + arg1 context.Context + arg2 *v1alpha1.ListMicroVMsRequest + arg3 []grpc.CallOption + } + listMicroVMsReturns struct { + result1 *v1alpha1.ListMicroVMsResponse + result2 error + } + listMicroVMsReturnsOnCall map[int]struct { + result1 *v1alpha1.ListMicroVMsResponse + result2 error + } + ListMicroVMsStreamStub func(context.Context, *v1alpha1.ListMicroVMsRequest, ...grpc.CallOption) (v1alpha1.MicroVM_ListMicroVMsStreamClient, error) + listMicroVMsStreamMutex sync.RWMutex + listMicroVMsStreamArgsForCall []struct { + arg1 context.Context + arg2 *v1alpha1.ListMicroVMsRequest + arg3 []grpc.CallOption + } + listMicroVMsStreamReturns struct { + result1 v1alpha1.MicroVM_ListMicroVMsStreamClient + result2 error + } + listMicroVMsStreamReturnsOnCall map[int]struct { + result1 v1alpha1.MicroVM_ListMicroVMsStreamClient + result2 error + } + invocations map[string][][]interface{} + invocationsMutex sync.RWMutex +} + +func (fake *FakeClient) Close() { + fake.closeMutex.Lock() + fake.closeArgsForCall = append(fake.closeArgsForCall, struct { + }{}) + stub := fake.CloseStub + fake.recordInvocation("Close", []interface{}{}) + fake.closeMutex.Unlock() + if stub != nil { + fake.CloseStub() + } +} + +func (fake *FakeClient) CloseCallCount() int { + fake.closeMutex.RLock() + defer fake.closeMutex.RUnlock() + return len(fake.closeArgsForCall) +} + +func (fake *FakeClient) CloseCalls(stub func()) { + fake.closeMutex.Lock() + defer fake.closeMutex.Unlock() + fake.CloseStub = stub +} + +func (fake *FakeClient) CreateMicroVM(arg1 context.Context, arg2 *v1alpha1.CreateMicroVMRequest, arg3 ...grpc.CallOption) (*v1alpha1.CreateMicroVMResponse, error) { + fake.createMicroVMMutex.Lock() + ret, specificReturn := fake.createMicroVMReturnsOnCall[len(fake.createMicroVMArgsForCall)] + fake.createMicroVMArgsForCall = append(fake.createMicroVMArgsForCall, struct { + arg1 context.Context + arg2 *v1alpha1.CreateMicroVMRequest + arg3 []grpc.CallOption + }{arg1, arg2, arg3}) + stub := fake.CreateMicroVMStub + fakeReturns := fake.createMicroVMReturns + fake.recordInvocation("CreateMicroVM", []interface{}{arg1, arg2, arg3}) + fake.createMicroVMMutex.Unlock() + if stub != nil { + return stub(arg1, arg2, arg3...) + } + if specificReturn { + return ret.result1, ret.result2 + } + return fakeReturns.result1, fakeReturns.result2 +} + +func (fake *FakeClient) CreateMicroVMCallCount() int { + fake.createMicroVMMutex.RLock() + defer fake.createMicroVMMutex.RUnlock() + return len(fake.createMicroVMArgsForCall) +} + +func (fake *FakeClient) CreateMicroVMCalls(stub func(context.Context, *v1alpha1.CreateMicroVMRequest, ...grpc.CallOption) (*v1alpha1.CreateMicroVMResponse, error)) { + fake.createMicroVMMutex.Lock() + defer fake.createMicroVMMutex.Unlock() + fake.CreateMicroVMStub = stub +} + +func (fake *FakeClient) CreateMicroVMArgsForCall(i int) (context.Context, *v1alpha1.CreateMicroVMRequest, []grpc.CallOption) { + fake.createMicroVMMutex.RLock() + defer fake.createMicroVMMutex.RUnlock() + argsForCall := fake.createMicroVMArgsForCall[i] + return argsForCall.arg1, argsForCall.arg2, argsForCall.arg3 +} + +func (fake *FakeClient) CreateMicroVMReturns(result1 *v1alpha1.CreateMicroVMResponse, result2 error) { + fake.createMicroVMMutex.Lock() + defer fake.createMicroVMMutex.Unlock() + fake.CreateMicroVMStub = nil + fake.createMicroVMReturns = struct { + result1 *v1alpha1.CreateMicroVMResponse + result2 error + }{result1, result2} +} + +func (fake *FakeClient) CreateMicroVMReturnsOnCall(i int, result1 *v1alpha1.CreateMicroVMResponse, result2 error) { + fake.createMicroVMMutex.Lock() + defer fake.createMicroVMMutex.Unlock() + fake.CreateMicroVMStub = nil + if fake.createMicroVMReturnsOnCall == nil { + fake.createMicroVMReturnsOnCall = make(map[int]struct { + result1 *v1alpha1.CreateMicroVMResponse + result2 error + }) + } + fake.createMicroVMReturnsOnCall[i] = struct { + result1 *v1alpha1.CreateMicroVMResponse + result2 error + }{result1, result2} +} + +func (fake *FakeClient) DeleteMicroVM(arg1 context.Context, arg2 *v1alpha1.DeleteMicroVMRequest, arg3 ...grpc.CallOption) (*emptypb.Empty, error) { + fake.deleteMicroVMMutex.Lock() + ret, specificReturn := fake.deleteMicroVMReturnsOnCall[len(fake.deleteMicroVMArgsForCall)] + fake.deleteMicroVMArgsForCall = append(fake.deleteMicroVMArgsForCall, struct { + arg1 context.Context + arg2 *v1alpha1.DeleteMicroVMRequest + arg3 []grpc.CallOption + }{arg1, arg2, arg3}) + stub := fake.DeleteMicroVMStub + fakeReturns := fake.deleteMicroVMReturns + fake.recordInvocation("DeleteMicroVM", []interface{}{arg1, arg2, arg3}) + fake.deleteMicroVMMutex.Unlock() + if stub != nil { + return stub(arg1, arg2, arg3...) + } + if specificReturn { + return ret.result1, ret.result2 + } + return fakeReturns.result1, fakeReturns.result2 +} + +func (fake *FakeClient) DeleteMicroVMCallCount() int { + fake.deleteMicroVMMutex.RLock() + defer fake.deleteMicroVMMutex.RUnlock() + return len(fake.deleteMicroVMArgsForCall) +} + +func (fake *FakeClient) DeleteMicroVMCalls(stub func(context.Context, *v1alpha1.DeleteMicroVMRequest, ...grpc.CallOption) (*emptypb.Empty, error)) { + fake.deleteMicroVMMutex.Lock() + defer fake.deleteMicroVMMutex.Unlock() + fake.DeleteMicroVMStub = stub +} + +func (fake *FakeClient) DeleteMicroVMArgsForCall(i int) (context.Context, *v1alpha1.DeleteMicroVMRequest, []grpc.CallOption) { + fake.deleteMicroVMMutex.RLock() + defer fake.deleteMicroVMMutex.RUnlock() + argsForCall := fake.deleteMicroVMArgsForCall[i] + return argsForCall.arg1, argsForCall.arg2, argsForCall.arg3 +} + +func (fake *FakeClient) DeleteMicroVMReturns(result1 *emptypb.Empty, result2 error) { + fake.deleteMicroVMMutex.Lock() + defer fake.deleteMicroVMMutex.Unlock() + fake.DeleteMicroVMStub = nil + fake.deleteMicroVMReturns = struct { + result1 *emptypb.Empty + result2 error + }{result1, result2} +} + +func (fake *FakeClient) DeleteMicroVMReturnsOnCall(i int, result1 *emptypb.Empty, result2 error) { + fake.deleteMicroVMMutex.Lock() + defer fake.deleteMicroVMMutex.Unlock() + fake.DeleteMicroVMStub = nil + if fake.deleteMicroVMReturnsOnCall == nil { + fake.deleteMicroVMReturnsOnCall = make(map[int]struct { + result1 *emptypb.Empty + result2 error + }) + } + fake.deleteMicroVMReturnsOnCall[i] = struct { + result1 *emptypb.Empty + result2 error + }{result1, result2} +} + +func (fake *FakeClient) GetMicroVM(arg1 context.Context, arg2 *v1alpha1.GetMicroVMRequest, arg3 ...grpc.CallOption) (*v1alpha1.GetMicroVMResponse, error) { + fake.getMicroVMMutex.Lock() + ret, specificReturn := fake.getMicroVMReturnsOnCall[len(fake.getMicroVMArgsForCall)] + fake.getMicroVMArgsForCall = append(fake.getMicroVMArgsForCall, struct { + arg1 context.Context + arg2 *v1alpha1.GetMicroVMRequest + arg3 []grpc.CallOption + }{arg1, arg2, arg3}) + stub := fake.GetMicroVMStub + fakeReturns := fake.getMicroVMReturns + fake.recordInvocation("GetMicroVM", []interface{}{arg1, arg2, arg3}) + fake.getMicroVMMutex.Unlock() + if stub != nil { + return stub(arg1, arg2, arg3...) + } + if specificReturn { + return ret.result1, ret.result2 + } + return fakeReturns.result1, fakeReturns.result2 +} + +func (fake *FakeClient) GetMicroVMCallCount() int { + fake.getMicroVMMutex.RLock() + defer fake.getMicroVMMutex.RUnlock() + return len(fake.getMicroVMArgsForCall) +} + +func (fake *FakeClient) GetMicroVMCalls(stub func(context.Context, *v1alpha1.GetMicroVMRequest, ...grpc.CallOption) (*v1alpha1.GetMicroVMResponse, error)) { + fake.getMicroVMMutex.Lock() + defer fake.getMicroVMMutex.Unlock() + fake.GetMicroVMStub = stub +} + +func (fake *FakeClient) GetMicroVMArgsForCall(i int) (context.Context, *v1alpha1.GetMicroVMRequest, []grpc.CallOption) { + fake.getMicroVMMutex.RLock() + defer fake.getMicroVMMutex.RUnlock() + argsForCall := fake.getMicroVMArgsForCall[i] + return argsForCall.arg1, argsForCall.arg2, argsForCall.arg3 +} + +func (fake *FakeClient) GetMicroVMReturns(result1 *v1alpha1.GetMicroVMResponse, result2 error) { + fake.getMicroVMMutex.Lock() + defer fake.getMicroVMMutex.Unlock() + fake.GetMicroVMStub = nil + fake.getMicroVMReturns = struct { + result1 *v1alpha1.GetMicroVMResponse + result2 error + }{result1, result2} +} + +func (fake *FakeClient) GetMicroVMReturnsOnCall(i int, result1 *v1alpha1.GetMicroVMResponse, result2 error) { + fake.getMicroVMMutex.Lock() + defer fake.getMicroVMMutex.Unlock() + fake.GetMicroVMStub = nil + if fake.getMicroVMReturnsOnCall == nil { + fake.getMicroVMReturnsOnCall = make(map[int]struct { + result1 *v1alpha1.GetMicroVMResponse + result2 error + }) + } + fake.getMicroVMReturnsOnCall[i] = struct { + result1 *v1alpha1.GetMicroVMResponse + result2 error + }{result1, result2} +} + +func (fake *FakeClient) ListMicroVMs(arg1 context.Context, arg2 *v1alpha1.ListMicroVMsRequest, arg3 ...grpc.CallOption) (*v1alpha1.ListMicroVMsResponse, error) { + fake.listMicroVMsMutex.Lock() + ret, specificReturn := fake.listMicroVMsReturnsOnCall[len(fake.listMicroVMsArgsForCall)] + fake.listMicroVMsArgsForCall = append(fake.listMicroVMsArgsForCall, struct { + arg1 context.Context + arg2 *v1alpha1.ListMicroVMsRequest + arg3 []grpc.CallOption + }{arg1, arg2, arg3}) + stub := fake.ListMicroVMsStub + fakeReturns := fake.listMicroVMsReturns + fake.recordInvocation("ListMicroVMs", []interface{}{arg1, arg2, arg3}) + fake.listMicroVMsMutex.Unlock() + if stub != nil { + return stub(arg1, arg2, arg3...) + } + if specificReturn { + return ret.result1, ret.result2 + } + return fakeReturns.result1, fakeReturns.result2 +} + +func (fake *FakeClient) ListMicroVMsCallCount() int { + fake.listMicroVMsMutex.RLock() + defer fake.listMicroVMsMutex.RUnlock() + return len(fake.listMicroVMsArgsForCall) +} + +func (fake *FakeClient) ListMicroVMsCalls(stub func(context.Context, *v1alpha1.ListMicroVMsRequest, ...grpc.CallOption) (*v1alpha1.ListMicroVMsResponse, error)) { + fake.listMicroVMsMutex.Lock() + defer fake.listMicroVMsMutex.Unlock() + fake.ListMicroVMsStub = stub +} + +func (fake *FakeClient) ListMicroVMsArgsForCall(i int) (context.Context, *v1alpha1.ListMicroVMsRequest, []grpc.CallOption) { + fake.listMicroVMsMutex.RLock() + defer fake.listMicroVMsMutex.RUnlock() + argsForCall := fake.listMicroVMsArgsForCall[i] + return argsForCall.arg1, argsForCall.arg2, argsForCall.arg3 +} + +func (fake *FakeClient) ListMicroVMsReturns(result1 *v1alpha1.ListMicroVMsResponse, result2 error) { + fake.listMicroVMsMutex.Lock() + defer fake.listMicroVMsMutex.Unlock() + fake.ListMicroVMsStub = nil + fake.listMicroVMsReturns = struct { + result1 *v1alpha1.ListMicroVMsResponse + result2 error + }{result1, result2} +} + +func (fake *FakeClient) ListMicroVMsReturnsOnCall(i int, result1 *v1alpha1.ListMicroVMsResponse, result2 error) { + fake.listMicroVMsMutex.Lock() + defer fake.listMicroVMsMutex.Unlock() + fake.ListMicroVMsStub = nil + if fake.listMicroVMsReturnsOnCall == nil { + fake.listMicroVMsReturnsOnCall = make(map[int]struct { + result1 *v1alpha1.ListMicroVMsResponse + result2 error + }) + } + fake.listMicroVMsReturnsOnCall[i] = struct { + result1 *v1alpha1.ListMicroVMsResponse + result2 error + }{result1, result2} +} + +func (fake *FakeClient) ListMicroVMsStream(arg1 context.Context, arg2 *v1alpha1.ListMicroVMsRequest, arg3 ...grpc.CallOption) (v1alpha1.MicroVM_ListMicroVMsStreamClient, error) { + fake.listMicroVMsStreamMutex.Lock() + ret, specificReturn := fake.listMicroVMsStreamReturnsOnCall[len(fake.listMicroVMsStreamArgsForCall)] + fake.listMicroVMsStreamArgsForCall = append(fake.listMicroVMsStreamArgsForCall, struct { + arg1 context.Context + arg2 *v1alpha1.ListMicroVMsRequest + arg3 []grpc.CallOption + }{arg1, arg2, arg3}) + stub := fake.ListMicroVMsStreamStub + fakeReturns := fake.listMicroVMsStreamReturns + fake.recordInvocation("ListMicroVMsStream", []interface{}{arg1, arg2, arg3}) + fake.listMicroVMsStreamMutex.Unlock() + if stub != nil { + return stub(arg1, arg2, arg3...) + } + if specificReturn { + return ret.result1, ret.result2 + } + return fakeReturns.result1, fakeReturns.result2 +} + +func (fake *FakeClient) ListMicroVMsStreamCallCount() int { + fake.listMicroVMsStreamMutex.RLock() + defer fake.listMicroVMsStreamMutex.RUnlock() + return len(fake.listMicroVMsStreamArgsForCall) +} + +func (fake *FakeClient) ListMicroVMsStreamCalls(stub func(context.Context, *v1alpha1.ListMicroVMsRequest, ...grpc.CallOption) (v1alpha1.MicroVM_ListMicroVMsStreamClient, error)) { + fake.listMicroVMsStreamMutex.Lock() + defer fake.listMicroVMsStreamMutex.Unlock() + fake.ListMicroVMsStreamStub = stub +} + +func (fake *FakeClient) ListMicroVMsStreamArgsForCall(i int) (context.Context, *v1alpha1.ListMicroVMsRequest, []grpc.CallOption) { + fake.listMicroVMsStreamMutex.RLock() + defer fake.listMicroVMsStreamMutex.RUnlock() + argsForCall := fake.listMicroVMsStreamArgsForCall[i] + return argsForCall.arg1, argsForCall.arg2, argsForCall.arg3 +} + +func (fake *FakeClient) ListMicroVMsStreamReturns(result1 v1alpha1.MicroVM_ListMicroVMsStreamClient, result2 error) { + fake.listMicroVMsStreamMutex.Lock() + defer fake.listMicroVMsStreamMutex.Unlock() + fake.ListMicroVMsStreamStub = nil + fake.listMicroVMsStreamReturns = struct { + result1 v1alpha1.MicroVM_ListMicroVMsStreamClient + result2 error + }{result1, result2} +} + +func (fake *FakeClient) ListMicroVMsStreamReturnsOnCall(i int, result1 v1alpha1.MicroVM_ListMicroVMsStreamClient, result2 error) { + fake.listMicroVMsStreamMutex.Lock() + defer fake.listMicroVMsStreamMutex.Unlock() + fake.ListMicroVMsStreamStub = nil + if fake.listMicroVMsStreamReturnsOnCall == nil { + fake.listMicroVMsStreamReturnsOnCall = make(map[int]struct { + result1 v1alpha1.MicroVM_ListMicroVMsStreamClient + result2 error + }) + } + fake.listMicroVMsStreamReturnsOnCall[i] = struct { + result1 v1alpha1.MicroVM_ListMicroVMsStreamClient + result2 error + }{result1, result2} +} + +func (fake *FakeClient) Invocations() map[string][][]interface{} { + fake.invocationsMutex.RLock() + defer fake.invocationsMutex.RUnlock() + fake.closeMutex.RLock() + defer fake.closeMutex.RUnlock() + fake.createMicroVMMutex.RLock() + defer fake.createMicroVMMutex.RUnlock() + fake.deleteMicroVMMutex.RLock() + defer fake.deleteMicroVMMutex.RUnlock() + fake.getMicroVMMutex.RLock() + defer fake.getMicroVMMutex.RUnlock() + fake.listMicroVMsMutex.RLock() + defer fake.listMicroVMsMutex.RUnlock() + fake.listMicroVMsStreamMutex.RLock() + defer fake.listMicroVMsStreamMutex.RUnlock() + copiedInvocations := map[string][][]interface{}{} + for key, value := range fake.invocations { + copiedInvocations[key] = value + } + return copiedInvocations +} + +func (fake *FakeClient) recordInvocation(key string, args []interface{}) { + fake.invocationsMutex.Lock() + defer fake.invocationsMutex.Unlock() + if fake.invocations == nil { + fake.invocations = map[string][][]interface{}{} + } + if fake.invocations[key] == nil { + fake.invocations[key] = [][]interface{}{} + } + fake.invocations[key] = append(fake.invocations[key], args) +} + +var _ client.Client = new(FakeClient) diff --git a/controllers/fakes/generate.go b/controllers/fakes/generate.go new file mode 100644 index 0000000..7f25d7d --- /dev/null +++ b/controllers/fakes/generate.go @@ -0,0 +1,7 @@ +// Copyright 2022 Weaveworks or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: MPL-2.0 + +package fakes + +// Run go generate to regenerate this mock. +//go:generate ../../bin/counterfeiter -o fake_client.go github.com/weaveworks-liquidmetal/controller-pkg/client.Client diff --git a/controllers/helpers_test.go b/controllers/helpers_test.go new file mode 100644 index 0000000..ad9e151 --- /dev/null +++ b/controllers/helpers_test.go @@ -0,0 +1,235 @@ +// Copyright 2022 Weaveworks or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: MPL-2.0. + +package controllers_test + +import ( + "context" + "encoding/base64" + "fmt" + + . "github.com/onsi/gomega" + "gopkg.in/yaml.v2" + + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/types" + "k8s.io/utils/pointer" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/client/fake" + + clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" + "sigs.k8s.io/cluster-api/util/conditions" + + flclient "github.com/weaveworks-liquidmetal/controller-pkg/client" + "github.com/weaveworks-liquidmetal/controller-pkg/types/microvm" + flintlockv1 "github.com/weaveworks-liquidmetal/flintlock/api/services/microvm/v1alpha1" + flintlocktypes "github.com/weaveworks-liquidmetal/flintlock/api/types" + "github.com/weaveworks-liquidmetal/flintlock/client/cloudinit/userdata" + infrav1 "github.com/weaveworks-liquidmetal/microvm-operator/api/v1alpha1" + "github.com/weaveworks-liquidmetal/microvm-operator/controllers" + "github.com/weaveworks-liquidmetal/microvm-operator/controllers/fakes" +) + +const ( + testNamespace = "ns1" + testMicrovmName = "mvm1" + testMicrovmUID = "ABCDEF123456" + testBootstrapData = "somesamplebootstrapsdata" +) + +func asRuntimeObject(microvm *infrav1.Microvm) []runtime.Object { + objects := []runtime.Object{} + + if microvm != nil { + objects = append(objects, microvm) + } + + return objects +} + +func reconcileMicrovm(client client.Client, mockAPIClient flclient.Client) (ctrl.Result, error) { + mvmController := &controllers.MicrovmReconciler{ + Client: client, + MvmClientFunc: func(address string, opts ...flclient.Options) (flclient.Client, error) { + return mockAPIClient, nil + }, + } + + request := ctrl.Request{ + NamespacedName: types.NamespacedName{ + Name: testMicrovmName, + Namespace: testNamespace, + }, + } + + return mvmController.Reconcile(context.TODO(), request) +} + +func getMicrovm(c client.Client, name, namespace string) (*infrav1.Microvm, error) { + key := client.ObjectKey{ + Name: name, + Namespace: namespace, + } + + mvm := &infrav1.Microvm{} + err := c.Get(context.TODO(), key, mvm) + return mvm, err +} + +func createFakeClient(g *WithT, objects []runtime.Object) client.Client { + scheme := runtime.NewScheme() + + g.Expect(infrav1.AddToScheme(scheme)).To(Succeed()) + g.Expect(corev1.AddToScheme(scheme)).To(Succeed()) + + return fake.NewClientBuilder().WithScheme(scheme).WithRuntimeObjects(objects...).Build() +} + +func createMicrovm() *infrav1.Microvm { + return &infrav1.Microvm{ + ObjectMeta: metav1.ObjectMeta{ + Name: testMicrovmName, + Namespace: testNamespace, + }, + Spec: infrav1.MicrovmSpec{ + Host: infrav1.HostSpec{ + Host: microvm.Host{Endpoint: "127.0.0.1:9090"}, + }, + ProviderID: pointer.String(testMicrovmUID), + VMSpec: microvm.VMSpec{ + VCPU: 2, + MemoryMb: 2048, + RootVolume: microvm.Volume{ + Image: "docker.io/richardcase/ubuntu-bionic-test:cloudimage_v0.0.1", + ReadOnly: false, + }, + Kernel: microvm.ContainerFileSource{ + Image: "docker.io/richardcase/ubuntu-bionic-kernel:0.0.11", + Filename: "vmlinuz", + }, + Initrd: µvm.ContainerFileSource{ + Image: "docker.io/richardcase/ubuntu-bionic-kernel:0.0.11", + Filename: "initrd-generic", + }, + NetworkInterfaces: []microvm.NetworkInterface{ + { + GuestDeviceName: "eth0", + GuestMAC: "", + Type: microvm.IfaceTypeMacvtap, + Address: "", + }, + }, + }, + }, + } +} + +func withExistingMicrovm(fc *fakes.FakeClient, mvmState flintlocktypes.MicroVMStatus_MicroVMState) { + fc.GetMicroVMReturns(&flintlockv1.GetMicroVMResponse{ + Microvm: &flintlocktypes.MicroVM{ + Spec: &flintlocktypes.MicroVMSpec{ + Uid: pointer.String(testMicrovmUID), + }, + Status: &flintlocktypes.MicroVMStatus{ + State: mvmState, + }, + }, + }, nil) +} + +func withMissingMicrovm(fc *fakes.FakeClient) { + fc.GetMicroVMReturns(&flintlockv1.GetMicroVMResponse{}, nil) +} + +func withCreateMicrovmSuccess(fc *fakes.FakeClient) { + fc.CreateMicroVMReturns(&flintlockv1.CreateMicroVMResponse{ + Microvm: &flintlocktypes.MicroVM{ + Spec: &flintlocktypes.MicroVMSpec{ + Uid: pointer.String(testMicrovmUID), + }, + Status: &flintlocktypes.MicroVMStatus{ + State: flintlocktypes.MicroVMStatus_PENDING, + }, + }, + }, nil) +} + +func assertConditionTrue(g *WithT, from conditions.Getter, conditionType clusterv1.ConditionType) { + c := conditions.Get(from, conditionType) + g.Expect(c).ToNot(BeNil(), "Conditions expected to be set") + g.Expect(c.Status).To(Equal(corev1.ConditionTrue), "Condition should be marked true") +} + +func assertConditionFalse(g *WithT, from conditions.Getter, conditionType clusterv1.ConditionType, reason string) { + c := conditions.Get(from, conditionType) + g.Expect(c).ToNot(BeNil(), "Conditions expected to be set") + g.Expect(c.Status).To(Equal(corev1.ConditionFalse), "Condition should be marked false") + g.Expect(c.Reason).To(Equal(reason)) +} + +func assertVMState(g *WithT, machine *infrav1.Microvm, expectedState microvm.VMState) { + g.Expect(machine.Status.VMState).NotTo(BeNil()) + g.Expect(*machine.Status.VMState).To(BeEquivalentTo(expectedState)) +} + +func assertMicrovmReconciled(g *WithT, reconciled *infrav1.Microvm) { + assertConditionTrue(g, reconciled, infrav1.MicrovmReadyCondition) + assertVMState(g, reconciled, microvm.VMStateRunning) + assertFinalizer(g, reconciled) + g.Expect(reconciled.Spec.ProviderID).ToNot(BeNil()) + expectedProviderID := fmt.Sprintf("microvm://127.0.0.1:9090/%s", testMicrovmUID) + g.Expect(*reconciled.Spec.ProviderID).To(Equal(expectedProviderID)) + g.Expect(reconciled.Status.Ready).To(BeTrue(), "The Ready property must be true when the machine has been reconciled") +} + +func assertFinalizer(g *WithT, reconciled *infrav1.Microvm) { + g.Expect(reconciled.ObjectMeta.Finalizers).NotTo(BeEmpty(), "Expected at least one finalizer to be set") + g.Expect(hasMicrovmFinalizer(reconciled)).To(BeTrue(), "Expect the mvm machine finalizer") +} + +func hasMicrovmFinalizer(machine *infrav1.Microvm) bool { + if len(machine.ObjectMeta.Finalizers) == 0 { + return false + } + + for _, f := range machine.ObjectMeta.Finalizers { + if f == infrav1.MvmFinalizer { + return true + } + } + + return false +} + +func assertMicrovmNotReady(g *WithT, machine *infrav1.Microvm) { + g.Expect(machine.Status.Ready).To(BeFalse()) +} + +func assertVendorData(g *WithT, vendorDataRaw string, expectedSSHKeys []microvm.SSHPublicKey) { + g.Expect(vendorDataRaw).ToNot(Equal("")) + g.Expect(expectedSSHKeys).ToNot(BeNil()) + + data, err := base64.StdEncoding.DecodeString(vendorDataRaw) + g.Expect(err).NotTo(HaveOccurred(), "expect vendor data to be base64 encoded") + + vendorData := &userdata.UserData{} + g.Expect(yaml.Unmarshal(data, vendorData)).To(Succeed(), "expect vendor data to unmarshall to cloud-init userdata") + + users := vendorData.Users + g.Expect(users).NotTo(BeNil()) + g.Expect(len(users)).To(Equal(len(expectedSSHKeys))) + + for i, user := range users { + g.Expect(user.Name).To(Equal(expectedSSHKeys[i].User)) + + keys := user.SSHAuthorizedKeys + g.Expect(keys).To(HaveLen(1)) + g.Expect(keys[0]).To(Equal(expectedSSHKeys[i].AuthorizedKeys[0])) + } + + vendorDataStr := string(data) + g.Expect(vendorDataStr).To(ContainSubstring("#cloud-config\n")) +} diff --git a/controllers/microvm_controller.go b/controllers/microvm_controller.go index 001dc07..b55d63b 100644 --- a/controllers/microvm_controller.go +++ b/controllers/microvm_controller.go @@ -63,7 +63,7 @@ func (r *MicrovmReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ct return ctrl.Result{}, nil } - log.Error(err, "error getting microvmmachine", "id", req.NamespacedName) + log.Error(err, "error getting microvm", "id", req.NamespacedName) return ctrl.Result{}, fmt.Errorf("unable to reconcile: %w", err) } @@ -123,11 +123,11 @@ func (r *MicrovmReconciler) reconcileDelete( // Mark the machine as no longer ready before we delete. mvmScope.SetNotReady(infrav1.MicrovmDeletingReason, "Info", "") - if err := mvmScope.Patch(); err != nil { - mvmScope.Error(err, "failed to patch object") - - return ctrl.Result{}, err - } + defer func() { + if err := mvmScope.Patch(); err != nil { + mvmScope.Error(err, "failed to patch object") + } + }() if microvm.Status.State != flintlocktypes.MicroVMStatus_DELETING { if _, err := mvmSvc.Delete(ctx); err != nil { @@ -269,7 +269,7 @@ func (r *MicrovmReconciler) parseMicroVMState( mvmScope.V(2).Info("microvm is deleting") return ctrl.Result{RequeueAfter: requeuePeriod}, nil - // NO IDEA WHAT IS GOING ON WITH THIS MVM + // NO IDEA WHAT IS GOING ON WITH THIS MVM default: mvmScope.MicroVM.Status.VMState = µvm.VMStateUnknown mvmScope.SetNotReady( diff --git a/controllers/microvm_controller_test.go b/controllers/microvm_controller_test.go new file mode 100644 index 0000000..0d2c6bc --- /dev/null +++ b/controllers/microvm_controller_test.go @@ -0,0 +1,360 @@ +package controllers_test + +import ( + "errors" + "fmt" + "testing" + "time" + + . "github.com/onsi/gomega" + "github.com/weaveworks-liquidmetal/controller-pkg/types/microvm" + flintlocktypes "github.com/weaveworks-liquidmetal/flintlock/api/types" + infrav1 "github.com/weaveworks-liquidmetal/microvm-operator/api/v1alpha1" + "github.com/weaveworks-liquidmetal/microvm-operator/controllers/fakes" + apierrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/utils/pointer" +) + +func TestMicrovm_Reconcile_MissingObject(t *testing.T) { + g := NewWithT(t) + + mvm := infrav1.Microvm{} + + client := createFakeClient(g, asRuntimeObject(&mvm)) + result, err := reconcileMicrovm(client, nil) + g.Expect(err).NotTo(HaveOccurred(), "Reconciling when microvm doesn't exist should not error") + g.Expect(result.IsZero()).To(BeTrue(), "Expect no requeue to be requested") +} + +func TestMicrovm_ReconcileNormal_ServiceGetError(t *testing.T) { + g := NewWithT(t) + + mvm := createMicrovm() + + fakeAPIClient := fakes.FakeClient{} + fakeAPIClient.GetMicroVMReturns(nil, errors.New("something terrible happened")) + + client := createFakeClient(g, asRuntimeObject(mvm)) + _, err := reconcileMicrovm(client, &fakeAPIClient) + g.Expect(err).To(HaveOccurred(), "Reconciling when microvm service 'Get' errors should return error") +} + +func TestMicrovm_ReconcileNormal_VMExistsAndRunning(t *testing.T) { + g := NewWithT(t) + + mvm := createMicrovm() + + fakeAPIClient := fakes.FakeClient{} + withExistingMicrovm(&fakeAPIClient, flintlocktypes.MicroVMStatus_CREATED) + + client := createFakeClient(g, asRuntimeObject(mvm)) + result, err := reconcileMicrovm(client, &fakeAPIClient) + g.Expect(err).NotTo(HaveOccurred(), "Reconciling when microvm service exists should not return error") + g.Expect(result.IsZero()).To(BeTrue(), "Expect no requeue to be requested") + + reconciled, err := getMicrovm(client, testMicrovmName, testNamespace) + g.Expect(err).NotTo(HaveOccurred(), "Getting microvm should not fail") + assertMicrovmReconciled(g, reconciled) +} + +func TestMicrovm_ReconcileNormal_VMExistsAndPending(t *testing.T) { + g := NewWithT(t) + + mvm := createMicrovm() + + fakeAPIClient := fakes.FakeClient{} + withExistingMicrovm(&fakeAPIClient, flintlocktypes.MicroVMStatus_PENDING) + + client := createFakeClient(g, asRuntimeObject(mvm)) + result, err := reconcileMicrovm(client, &fakeAPIClient) + g.Expect(err).NotTo(HaveOccurred(), "Reconciling when microvm service exists and state pending should not return error") + g.Expect(result.IsZero()).To(BeFalse(), "Expect a requeue to be requested") + + reconciled, err := getMicrovm(client, testMicrovmName, testNamespace) + g.Expect(err).NotTo(HaveOccurred(), "Getting microvm machine should not fail") + + assertConditionFalse(g, reconciled, infrav1.MicrovmReadyCondition, infrav1.MicrovmPendingReason) + assertVMState(g, reconciled, microvm.VMStatePending) + assertFinalizer(g, reconciled) +} + +func TestMicrovm_ReconcileNormal_VMExistsButFailed(t *testing.T) { + g := NewWithT(t) + + mvm := createMicrovm() + + fakeAPIClient := fakes.FakeClient{} + withExistingMicrovm(&fakeAPIClient, flintlocktypes.MicroVMStatus_FAILED) + + client := createFakeClient(g, asRuntimeObject(mvm)) + _, err := reconcileMicrovm(client, &fakeAPIClient) + g.Expect(err).To(HaveOccurred(), "Reconciling when microvm service exists and state failed should return an error") + + reconciled, err := getMicrovm(client, testMicrovmName, testNamespace) + g.Expect(err).NotTo(HaveOccurred(), "Getting microvm machine should not fail") + + assertConditionFalse(g, reconciled, infrav1.MicrovmReadyCondition, infrav1.MicrovmProvisionFailedReason) + assertVMState(g, reconciled, microvm.VMStateFailed) + assertFinalizer(g, reconciled) +} + +func TestMicrovm_ReconcileNormal_VMExistsButUnknownState(t *testing.T) { + g := NewWithT(t) + + mvm := createMicrovm() + + fakeAPIClient := fakes.FakeClient{} + withExistingMicrovm(&fakeAPIClient, flintlocktypes.MicroVMStatus_MicroVMState(42)) + + client := createFakeClient(g, asRuntimeObject(mvm)) + _, err := reconcileMicrovm(client, &fakeAPIClient) + g.Expect(err).To(HaveOccurred(), "Reconciling when microvm service exists and state is unknown should return an error") + + reconciled, err := getMicrovm(client, testMicrovmName, testNamespace) + g.Expect(err).NotTo(HaveOccurred(), "Getting microvm machine should not fail") + + assertConditionFalse(g, reconciled, infrav1.MicrovmReadyCondition, infrav1.MicrovmUnknownStateReason) + assertVMState(g, reconciled, microvm.VMStateUnknown) + assertFinalizer(g, reconciled) +} + +func TestMicrovm_ReconcileNormal_NoVmCreateSucceeds(t *testing.T) { + g := NewWithT(t) + + mvm := createMicrovm() + mvm.Spec.ProviderID = nil + + fakeAPIClient := fakes.FakeClient{} + withMissingMicrovm(&fakeAPIClient) + withCreateMicrovmSuccess(&fakeAPIClient) + + client := createFakeClient(g, asRuntimeObject(mvm)) + result, err := reconcileMicrovm(client, &fakeAPIClient) + + g.Expect(err).NotTo(HaveOccurred(), "Reconciling when creating microvm should not return error") + g.Expect(result.IsZero()).To(BeFalse(), "Expect requeue to be requested after create") + + _, createReq, _ := fakeAPIClient.CreateMicroVMArgsForCall(0) + g.Expect(createReq.Microvm).ToNot(BeNil()) + + reconciled, err := getMicrovm(client, testMicrovmName, testNamespace) + g.Expect(err).NotTo(HaveOccurred(), "Getting microvm machine should not fail") + + expectedProviderID := fmt.Sprintf("microvm://127.0.0.1:9090/%s", testMicrovmUID) + g.Expect(reconciled.Spec.ProviderID).To(Equal(pointer.String(expectedProviderID))) + + assertConditionFalse(g, reconciled, infrav1.MicrovmReadyCondition, infrav1.MicrovmPendingReason) + assertVMState(g, reconciled, microvm.VMStatePending) + assertFinalizer(g, reconciled) +} + +func TestMicrovm_ReconcileNormal_NoVmCreateWithUserdataSucceeds(t *testing.T) { + t.Parallel() + g := NewWithT(t) + + mvm := createMicrovm() + mvm.Spec.ProviderID = nil + mvm.Spec.UserData = pointer.String(testBootstrapData) + + fakeAPIClient := fakes.FakeClient{} + withMissingMicrovm(&fakeAPIClient) + withCreateMicrovmSuccess(&fakeAPIClient) + + client := createFakeClient(g, asRuntimeObject(mvm)) + result, err := reconcileMicrovm(client, &fakeAPIClient) + g.Expect(err).NotTo(HaveOccurred(), "Reconciling when creating microvm should not return error") + g.Expect(result.IsZero()).To(BeFalse(), "Expect requeue to be requested after create") + + _, createReq, _ := fakeAPIClient.CreateMicroVMArgsForCall(0) + g.Expect(createReq.Microvm).ToNot(BeNil()) + g.Expect(createReq.Microvm.Metadata).To(HaveLen(3)) + g.Expect(createReq.Microvm.Metadata).To(HaveKeyWithValue("user-data", testBootstrapData)) +} + +func TestMicrovm_ReconcileNormal_NoVmCreateWithLabelsSucceeds(t *testing.T) { + t.Parallel() + g := NewWithT(t) + + expectedLabels := map[string]string{ + "label": "one", + } + + mvm := createMicrovm() + mvm.Spec.ProviderID = nil + mvm.Spec.Labels = expectedLabels + + fakeAPIClient := fakes.FakeClient{} + withMissingMicrovm(&fakeAPIClient) + withCreateMicrovmSuccess(&fakeAPIClient) + + client := createFakeClient(g, asRuntimeObject(mvm)) + result, err := reconcileMicrovm(client, &fakeAPIClient) + g.Expect(err).NotTo(HaveOccurred(), "Reconciling when creating microvm should not return error") + g.Expect(result.IsZero()).To(BeFalse(), "Expect requeue to be requested after create") + + _, createReq, _ := fakeAPIClient.CreateMicroVMArgsForCall(0) + g.Expect(createReq.Microvm).ToNot(BeNil()) + g.Expect(createReq.Microvm.Labels).To(HaveLen(1)) + g.Expect(createReq.Microvm.Labels).To(Equal(expectedLabels)) +} + +func TestMicrovm_ReconcileNormal_NoVmCreateWithSSHSucceeds(t *testing.T) { + t.Parallel() + g := NewWithT(t) + + expectedKeys := []microvm.SSHPublicKey{{ + AuthorizedKeys: []string{"MachineSSH"}, + User: "root", + }, { + AuthorizedKeys: []string{"MachineSSH"}, + User: "ubuntu", + }} + + mvm := createMicrovm() + mvm.Spec.ProviderID = nil + mvm.Spec.SSHPublicKeys = expectedKeys + + fakeAPIClient := fakes.FakeClient{} + withMissingMicrovm(&fakeAPIClient) + withCreateMicrovmSuccess(&fakeAPIClient) + + client := createFakeClient(g, asRuntimeObject(mvm)) + result, err := reconcileMicrovm(client, &fakeAPIClient) + g.Expect(err).NotTo(HaveOccurred(), "Reconciling when creating microvm should not return error") + g.Expect(result.IsZero()).To(BeFalse(), "Expect requeue to be requested after create") + + _, createReq, _ := fakeAPIClient.CreateMicroVMArgsForCall(0) + g.Expect(createReq.Microvm).ToNot(BeNil()) + // g.Expect(createReq.Microvm.Labels).To(HaveLen(1)) + g.Expect(createReq.Microvm.Metadata).To(HaveLen(3)) + + // expectedBootstrapData := base64.StdEncoding.EncodeToString([]byte(testbootStrapData)) + // g.Expect(createReq.Microvm.Metadata).To(HaveKeyWithValue("user-data", expectedBootstrapData)) + + g.Expect(createReq.Microvm.Metadata).To(HaveKey("vendor-data"), "expect cloud-init vendor-data to be created") + assertVendorData(g, createReq.Microvm.Metadata["vendor-data"], expectedKeys) +} + +func TestMicrovm_ReconcileNormal_NoVmCreateWithAdditionalReconcileSucceeds(t *testing.T) { + g := NewWithT(t) + + mvm := createMicrovm() + mvm.Spec.ProviderID = nil + + fakeAPIClient := fakes.FakeClient{} + withMissingMicrovm(&fakeAPIClient) + withCreateMicrovmSuccess(&fakeAPIClient) + + client := createFakeClient(g, asRuntimeObject(mvm)) + result, err := reconcileMicrovm(client, &fakeAPIClient) + g.Expect(err).NotTo(HaveOccurred(), "Reconciling when creating microvm should not return error") + g.Expect(result.IsZero()).To(BeFalse(), "Expect requeue to be requested after create") + + withExistingMicrovm(&fakeAPIClient, flintlocktypes.MicroVMStatus_CREATED) + _, err = reconcileMicrovm(client, &fakeAPIClient) + g.Expect(err).NotTo(HaveOccurred(), "Reconciling should not return an error") + + reconciled, err := getMicrovm(client, testMicrovmName, testNamespace) + g.Expect(err).NotTo(HaveOccurred(), "Getting microvm machine should not fail") + assertMicrovmReconciled(g, reconciled) +} + +func TestMicrovm_ReconcileDelete_Succeeds(t *testing.T) { + g := NewWithT(t) + + mvm := createMicrovm() + mvm.DeletionTimestamp = &metav1.Time{ + Time: time.Now(), + } + mvm.Spec.ProviderID = pointer.String(fmt.Sprintf("microvm://127.0.0.1:9090/%s", testMicrovmUID)) + mvm.Finalizers = []string{infrav1.MvmFinalizer} + + fakeAPIClient := fakes.FakeClient{} + withExistingMicrovm(&fakeAPIClient, flintlocktypes.MicroVMStatus_CREATED) + + client := createFakeClient(g, asRuntimeObject(mvm)) + + result, err := reconcileMicrovm(client, &fakeAPIClient) + g.Expect(err).NotTo(HaveOccurred(), "Reconciling when deleting microvm should not return error") + g.Expect(result.Requeue).To(BeFalse()) + g.Expect(result.RequeueAfter).To(BeNumerically(">", time.Duration(0))) + + g.Expect(fakeAPIClient.DeleteMicroVMCallCount()).To(Equal(1)) + _, deleteReq, _ := fakeAPIClient.DeleteMicroVMArgsForCall(0) + g.Expect(deleteReq.Uid).To(Equal(testMicrovmUID)) + + withMissingMicrovm(&fakeAPIClient) + _, err = reconcileMicrovm(client, &fakeAPIClient) + g.Expect(err).NotTo(HaveOccurred(), "Reconciling when deleting microvm should not return error") + + _, err = getMicrovm(client, testMicrovmName, testNamespace) + g.Expect(apierrors.IsNotFound(err)).To(BeTrue()) +} + +func TestMicrovm_ReconcileDelete_GetReturnsNil(t *testing.T) { + g := NewWithT(t) + + mvm := createMicrovm() + mvm.DeletionTimestamp = &metav1.Time{ + Time: time.Now(), + } + mvm.Finalizers = []string{infrav1.MvmFinalizer} + + fakeAPIClient := fakes.FakeClient{} + withMissingMicrovm(&fakeAPIClient) + + client := createFakeClient(g, asRuntimeObject(mvm)) + + result, err := reconcileMicrovm(client, &fakeAPIClient) + g.Expect(err).NotTo(HaveOccurred(), "Reconciling when deleting microvm should not return error") + g.Expect(result.Requeue).To(BeFalse()) + g.Expect(result.RequeueAfter).To(Equal(time.Duration(0))) + + g.Expect(fakeAPIClient.DeleteMicroVMCallCount()).To(Equal(0)) + + _, err = getMicrovm(client, testMicrovmName, testNamespace) + g.Expect(apierrors.IsNotFound(err)).To(BeTrue()) +} + +func TestMicrovm_ReconcileDelete_GetErrors(t *testing.T) { + g := NewWithT(t) + + mvm := createMicrovm() + mvm.DeletionTimestamp = &metav1.Time{ + Time: time.Now(), + } + mvm.Finalizers = []string{infrav1.MvmFinalizer} + + fakeAPIClient := fakes.FakeClient{} + withExistingMicrovm(&fakeAPIClient, flintlocktypes.MicroVMStatus_CREATED) + fakeAPIClient.GetMicroVMReturns(nil, errors.New("something terrible happened")) + + client := createFakeClient(g, asRuntimeObject(mvm)) + _, err := reconcileMicrovm(client, &fakeAPIClient) + g.Expect(err).To(HaveOccurred(), "Reconciling when microvm service exists errors should return error") +} + +func TestMicrovm_ReconcileDelete_DeleteErrors(t *testing.T) { + g := NewWithT(t) + + mvm := createMicrovm() + mvm.DeletionTimestamp = &metav1.Time{ + Time: time.Now(), + } + mvm.Finalizers = []string{infrav1.MvmFinalizer} + + fakeAPIClient := fakes.FakeClient{} + withExistingMicrovm(&fakeAPIClient, flintlocktypes.MicroVMStatus_CREATED) + fakeAPIClient.DeleteMicroVMReturns(nil, errors.New("something terrible happened")) + + client := createFakeClient(g, asRuntimeObject(mvm)) + _, err := reconcileMicrovm(client, &fakeAPIClient) + g.Expect(err).To(HaveOccurred(), "Reconciling when deleting microvm errors should return error") + + reconciled, err := getMicrovm(client, testMicrovmName, testNamespace) + g.Expect(err).NotTo(HaveOccurred(), "Getting microvm machine should not fail") + + assertConditionFalse(g, reconciled, infrav1.MicrovmReadyCondition, infrav1.MicrovmDeleteFailedReason) + assertMicrovmNotReady(g, reconciled) +} diff --git a/controllers/suite_test.go b/controllers/suite_test.go deleted file mode 100644 index 58e2ce8..0000000 --- a/controllers/suite_test.go +++ /dev/null @@ -1,80 +0,0 @@ -/* -Copyright 2022 Weaveworks. - -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. -*/ - -package controllers - -import ( - "path/filepath" - "testing" - - . "github.com/onsi/ginkgo/v2" - . "github.com/onsi/gomega" - - "k8s.io/client-go/kubernetes/scheme" - "k8s.io/client-go/rest" - "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/envtest" - logf "sigs.k8s.io/controller-runtime/pkg/log" - "sigs.k8s.io/controller-runtime/pkg/log/zap" - - infrastructurev1alpha1 "github.com/weaveworks-liquidmetal/microvm-operator/api/v1alpha1" - //+kubebuilder:scaffold:imports -) - -// These tests use Ginkgo (BDD-style Go testing framework). Refer to -// http://onsi.github.io/ginkgo/ to learn more about Ginkgo. - -var cfg *rest.Config -var k8sClient client.Client -var testEnv *envtest.Environment - -func TestAPIs(t *testing.T) { - RegisterFailHandler(Fail) - - RunSpecs(t, "Controller Suite") -} - -var _ = BeforeSuite(func() { - logf.SetLogger(zap.New(zap.WriteTo(GinkgoWriter), zap.UseDevMode(true))) - - By("bootstrapping test environment") - testEnv = &envtest.Environment{ - CRDDirectoryPaths: []string{filepath.Join("..", "config", "crd", "bases")}, - ErrorIfCRDPathMissing: true, - } - - var err error - // cfg is defined in this file globally. - cfg, err = testEnv.Start() - Expect(err).NotTo(HaveOccurred()) - Expect(cfg).NotTo(BeNil()) - - err = infrastructurev1alpha1.AddToScheme(scheme.Scheme) - Expect(err).NotTo(HaveOccurred()) - - //+kubebuilder:scaffold:scheme - - k8sClient, err = client.New(cfg, client.Options{Scheme: scheme.Scheme}) - Expect(err).NotTo(HaveOccurred()) - Expect(k8sClient).NotTo(BeNil()) - -}) - -var _ = AfterSuite(func() { - By("tearing down the test environment") - err := testEnv.Stop() - Expect(err).NotTo(HaveOccurred()) -}) diff --git a/go.mod b/go.mod index b9c6ef3..fc86bfe 100644 --- a/go.mod +++ b/go.mod @@ -4,15 +4,19 @@ go 1.19 require ( github.com/go-logr/logr v1.2.3 - github.com/onsi/ginkgo/v2 v2.1.4 github.com/onsi/gomega v1.20.0 github.com/weaveworks-liquidmetal/controller-pkg/client v0.0.0-20221118161315-83de77687232 github.com/weaveworks-liquidmetal/controller-pkg/services/microvm v0.0.0-20221118161315-83de77687232 github.com/weaveworks-liquidmetal/controller-pkg/types/microvm v0.0.0-20221118161315-83de77687232 github.com/weaveworks-liquidmetal/flintlock/api v0.0.0-20221108110312-4cf137879fb2 + github.com/weaveworks-liquidmetal/flintlock/client v0.0.0-20221108110312-4cf137879fb2 + google.golang.org/grpc v1.50.1 + google.golang.org/protobuf v1.28.1 + gopkg.in/yaml.v2 v2.4.0 k8s.io/api v0.25.0 k8s.io/apimachinery v0.25.0 k8s.io/client-go v0.25.0 + k8s.io/utils v0.0.0-20221108210102-8e77b1f39fe2 sigs.k8s.io/cluster-api v1.2.5 sigs.k8s.io/controller-runtime v0.13.0 ) @@ -32,6 +36,7 @@ require ( github.com/cespare/xxhash/v2 v2.1.2 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/emicklei/go-restful/v3 v3.8.0 // indirect + github.com/evanphx/json-patch v4.12.0+incompatible // indirect github.com/evanphx/json-patch/v5 v5.6.0 // indirect github.com/fsnotify/fsnotify v1.5.4 // indirect github.com/go-logr/zapr v1.2.3 // indirect @@ -62,7 +67,6 @@ require ( github.com/prometheus/common v0.32.1 // indirect github.com/prometheus/procfs v0.7.3 // indirect github.com/spf13/pflag v1.0.5 // indirect - github.com/weaveworks-liquidmetal/flintlock/client v0.0.0-20221108110312-4cf137879fb2 // indirect github.com/yitsushi/macpot v1.0.2 // indirect go.uber.org/atomic v1.7.0 // indirect go.uber.org/multierr v1.6.0 // indirect @@ -77,16 +81,13 @@ require ( gomodules.xyz/jsonpatch/v2 v2.2.0 // indirect google.golang.org/appengine v1.6.7 // indirect google.golang.org/genproto v0.0.0-20220519153652-3a47de7e79bd // indirect - google.golang.org/grpc v1.50.1 // indirect - google.golang.org/protobuf v1.28.1 // indirect + gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect gopkg.in/inf.v0 v0.9.1 // indirect - gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect k8s.io/apiextensions-apiserver v0.25.0 // indirect k8s.io/component-base v0.25.0 // indirect k8s.io/klog/v2 v2.80.1 // indirect k8s.io/kube-openapi v0.0.0-20220803162953-67bda5d908f1 // indirect - k8s.io/utils v0.0.0-20221108210102-8e77b1f39fe2 // indirect sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 // indirect sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect sigs.k8s.io/yaml v1.3.0 // indirect diff --git a/go.sum b/go.sum index ab79f85..e494010 100644 --- a/go.sum +++ b/go.sum @@ -126,6 +126,7 @@ github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go. github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/evanphx/json-patch v0.5.2/go.mod h1:ZWS5hhDbVDyob71nXKNL0+PWn6ToqBHMikGIFbs31qQ= github.com/evanphx/json-patch v4.12.0+incompatible h1:4onqiflcdA9EOZ4RxV643DvftH5pOlLGNtQ5lPWQu84= +github.com/evanphx/json-patch v4.12.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/evanphx/json-patch/v5 v5.6.0 h1:b91NhWfaz02IuVxO9faSllyAtNXHMPkC5J8sJCLunww= github.com/evanphx/json-patch/v5 v5.6.0/go.mod h1:G79N1coSVB93tBe7j6PhzjmR3/2VvlbKOFpnXhI9Bw4= github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI= @@ -276,6 +277,8 @@ github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxv github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= @@ -301,12 +304,10 @@ github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= github.com/onsi/ginkgo/v2 v2.1.4 h1:GNapqRSid3zijZ9H77KrgVG4/8KqiyRsxcSxe+7ApXY= -github.com/onsi/ginkgo/v2 v2.1.4/go.mod h1:um6tUpWM/cxCK3/FK8BXqEiUMUwRgSM4JXG47RKZmLU= github.com/onsi/gomega v1.20.0 h1:8W0cWlwFkflGPLltQvLRB7ZVD5HuP6ng320w2IS245Q= github.com/onsi/gomega v1.20.0/go.mod h1:DtrZpjmvpn2mPm4YWQa0/ALMDj9v4YxLgojwPeREyVo= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= @@ -793,8 +794,9 @@ gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLks gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=