diff --git a/controllers/openstackcluster_controller_test.go b/controllers/openstackcluster_controller_test.go index a79b6df33a..819431d482 100644 --- a/controllers/openstackcluster_controller_test.go +++ b/controllers/openstackcluster_controller_test.go @@ -20,6 +20,9 @@ import ( "context" "fmt" + "github.com/golang/mock/gomock" + "github.com/gophercloud/gophercloud/openstack/compute/v2/servers" + "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/security/groups" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -32,6 +35,9 @@ import ( "sigs.k8s.io/controller-runtime/pkg/reconcile" infrav1 "sigs.k8s.io/cluster-api-provider-openstack/api/v1alpha6" + "sigs.k8s.io/cluster-api-provider-openstack/pkg/clients" + mock_clients "sigs.k8s.io/cluster-api-provider-openstack/pkg/clients/mocks" + "sigs.k8s.io/cluster-api-provider-openstack/pkg/scope" ) var ( @@ -165,51 +171,35 @@ var _ = Describe("OpenStackCluster controller", func() { Expect(err).ToNot(BeNil()) Expect(result).To(Equal(reconcile.Result{})) }) + It("should be able to reconcile when basition disabled", func() { + mockCtrl := gomock.NewController(GinkgoT()) + computeClient := mock_clients.NewMockComputeClient(mockCtrl) + networkClient := mock_clients.NewMockNetworkClient(mockCtrl) + loadBalancerClient := mock_clients.NewMockLoadBalancerClient(mockCtrl) + + scope := scope.NewMockScopeFactory(computeClient, networkClient, loadBalancerClient) + + computeClientRecorder := computeClient.EXPECT() + computeClientRecorder.ListServers(servers.ListOpts{ + Name: "^capi-cluster-bastion$", + }).Return([]clients.ServerExt{}, nil) + + networkClientRecorder := networkClient.EXPECT() + networkClientRecorder.ListSecGroup(gomock.Any()).Return([]groups.SecGroup{}, nil) + + testCluster.SetName("no-bastion") + testCluster.Spec = infrav1.OpenStackClusterSpec{ + Bastion: &infrav1.Bastion{ + Enabled: false, + }, + } + err := k8sClient.Create(ctx, testCluster) + Expect(err).To(BeNil()) + err = k8sClient.Create(ctx, capiCluster) + Expect(err).To(BeNil()) - // TODO: This test is set to pending (PIt instead of It) since it is not working. - PIt("should be able to reconcile when basition disabled", func() { - // verify := false - // cloud := clientconfig.Cloud{ - // Cloud: "test", - // RegionName: "test", - // Verify: &verify, - // AuthInfo: &clientconfig.AuthInfo{ - // AuthURL: "https://example.com:5000", - // Username: "testuser", - // Password: "secret", - // ProjectName: "test", - // DomainName: "test", - // UserDomainName: "test", - // }, - // } - // // TODO: Can we fake the client in some way? - // providerClient, clientOpts, _, err := provider.NewClient(cloud, nil) - // Expect(err).To(BeNil()) - // scope := &scope.Scope{ - // ProviderClient: providerClient, - // ProviderClientOpts: clientOpts, - // } - - // TODO: This won't work without filling in proper values. - /* - scope := &scope.Scope{ - ProviderClient: &gophercloud.ProviderClient{}, - ProviderClientOpts: &clientconfig.ClientOpts{}, - } - testCluster.SetName("no-bastion") - testCluster.Spec = infrav1.OpenStackClusterSpec{ - Bastion: &infrav1.Bastion{ - Enabled: false, - }, - } - err := k8sClient.Create(ctx, testCluster) - Expect(err).To(BeNil()) - err = k8sClient.Create(ctx, capiCluster) - Expect(err).To(BeNil()) - - err = deleteBastion(scope, capiCluster, testCluster) - Expect(err).To(BeNil()) - */ + err = deleteBastion(scope, capiCluster, testCluster) + Expect(err).To(BeNil()) }) }) diff --git a/pkg/scope/mock.go b/pkg/scope/mock.go new file mode 100644 index 0000000000..f59fd7698b --- /dev/null +++ b/pkg/scope/mock.go @@ -0,0 +1,69 @@ +/* +Copyright 2022 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. +*/ + +package scope + +import ( + "context" + + "github.com/go-logr/logr" + "sigs.k8s.io/controller-runtime/pkg/client" + + infrav1 "sigs.k8s.io/cluster-api-provider-openstack/api/v1alpha6" + "sigs.k8s.io/cluster-api-provider-openstack/pkg/clients" + mock_clients "sigs.k8s.io/cluster-api-provider-openstack/pkg/clients/mocks" +) + +// MockScopeFactory implements both the ScopeFactory and ClientScope interfaces. It can be used in place of the default ProviderScopeFactory +// when we want to use mocked service clients which do not attempt to connect to a running OpenStack cloud. +type MockScopeFactory struct { + ComputeClient *mock_clients.MockComputeClient + NetworkClient *mock_clients.MockNetworkClient + LoadBalancerClient *mock_clients.MockLoadBalancerClient + baseScope +} + +func NewMockScopeFactory(computeClient *mock_clients.MockComputeClient, networkClient *mock_clients.MockNetworkClient, loadBalancerClient *mock_clients.MockLoadBalancerClient) *MockScopeFactory { + return &MockScopeFactory{ + ComputeClient: computeClient, + NetworkClient: networkClient, + LoadBalancerClient: loadBalancerClient, + baseScope: baseScope{ + projectID: "", + logger: logr.Discard(), + }, + } +} + +func (f *MockScopeFactory) NewClientScopeFromMachine(ctx context.Context, ctrlClient client.Client, openStackMachine *infrav1.OpenStackMachine, logger logr.Logger) (ClientScope, error) { + return f, nil +} + +func (f *MockScopeFactory) NewClientScopeFromCluster(ctx context.Context, ctrlClient client.Client, openStackCluster *infrav1.OpenStackCluster, logger logr.Logger) (ClientScope, error) { + return f, nil +} + +func (f *MockScopeFactory) NewComputeClient() (clients.ComputeClient, error) { + return f.ComputeClient, nil +} + +func (f *MockScopeFactory) NewNetworkClient() (clients.NetworkClient, error) { + return f.NetworkClient, nil +} + +func (f *MockScopeFactory) NewLoadBalancerClient() (clients.LoadBalancerClient, error) { + return f.LoadBalancerClient, nil +} diff --git a/pkg/scope/provider.go b/pkg/scope/provider.go index 5ee311abcd..6e5fe5ec26 100644 --- a/pkg/scope/provider.go +++ b/pkg/scope/provider.go @@ -38,6 +38,7 @@ const ( caSecretKey = "cacert" ) +// ProviderScopeFactory is the default scope factory. It generates service clients which make OpenStack API calls against a running cloud. var ProviderScopeFactory Factory = providerScopeFactory{} type providerScopeFactory struct{} diff --git a/pkg/scope/scope.go b/pkg/scope/scope.go index 25f3c161af..f500cfb3c4 100644 --- a/pkg/scope/scope.go +++ b/pkg/scope/scope.go @@ -26,11 +26,13 @@ import ( "sigs.k8s.io/cluster-api-provider-openstack/pkg/clients" ) +// Factory instantiates a new ClientScope using credentials from either a cluster or a machine. type Factory interface { NewClientScopeFromMachine(ctx context.Context, ctrlClient client.Client, openStackMachine *infrav1.OpenStackMachine, logger logr.Logger) (ClientScope, error) NewClientScopeFromCluster(ctx context.Context, ctrlClient client.Client, openStackCluster *infrav1.OpenStackCluster, logger logr.Logger) (ClientScope, error) } +// ClientScope is a Scope that can also be used to create service clients. type ClientScope interface { NewComputeClient() (clients.ComputeClient, error) NewNetworkClient() (clients.NetworkClient, error) @@ -38,7 +40,7 @@ type ClientScope interface { Scope } -// Scope is used to initialize Services from Controllers. +// Scope contains arguments common to most operations. type Scope interface { Logger() logr.Logger ProjectID() string