diff --git a/cloud/scope/machine.go b/cloud/scope/machine.go index 3c7d531f987..7c8995fcdf5 100644 --- a/cloud/scope/machine.go +++ b/cloud/scope/machine.go @@ -19,6 +19,7 @@ package scope import ( "context" "encoding/base64" + "github.com/Azure/go-autorest/autorest/to" "github.com/go-logr/logr" "github.com/pkg/errors" @@ -187,6 +188,17 @@ func (m *MachineScope) DiskSpecs() []azure.DiskSpec { return []azure.DiskSpec{spec} } +// BastionSpecs returns the bastion specs. +func (m *MachineScope) BastionSpecs() []azure.BastionSpec { + spec := azure.BastionSpec{ + Name: azure.GenerateOSDiskName(m.Name()), + SubnetName: m.Subnet().Name, + PublicIPName: azure.GenerateNodePublicIPName(azure.GenerateNICName(m.Name())), + VNetName: m.Vnet().Name, + } + return []azure.BastionSpec{spec} +} + // RoleAssignmentSpecs returns the role assignment specs. func (m *MachineScope) RoleAssignmentSpecs() []azure.RoleAssignmentSpec { if m.AzureMachine.Spec.Identity == infrav1.VMIdentitySystemAssigned { diff --git a/cloud/services/bastionhosts/bastionhosts.go b/cloud/services/bastionhosts/bastionhosts.go new file mode 100644 index 00000000000..82b90c0ac28 --- /dev/null +++ b/cloud/services/bastionhosts/bastionhosts.go @@ -0,0 +1,143 @@ +/* +Copyright 2020 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 bastionhosts + +import ( + "context" + "fmt" + "strings" + + "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2019-06-01/network" + "github.com/Azure/go-autorest/autorest/to" + "github.com/pkg/errors" + + infrav1 "sigs.k8s.io/cluster-api-provider-azure/api/v1alpha3" + azure "sigs.k8s.io/cluster-api-provider-azure/cloud" + "sigs.k8s.io/cluster-api-provider-azure/cloud/converters" +) + +// Reconcile gets/creates/updates a bastion host. +func (s *Service) Reconcile(ctx context.Context) error { + for _, bastionSpec := range s.Scope.BastionSpecs() { + s.Scope.V(2).Info("getting subnet in vnet", "subnet", bastionSpec.SubnetName, "vNet", bastionSpec.VNetName) + subnet, err := s.SubnetsClient.Get(ctx, s.Scope.ResourceGroup(), bastionSpec.VNetName, bastionSpec.SubnetName) + if err != nil { + return errors.Wrap(err, "failed to get subnet") + } + s.Scope.V(2).Info("successfully got subnet in vnet", "subnet", bastionSpec.SubnetName, "vNet", bastionSpec.VNetName) + + s.Scope.V(2).Info("checking if public ip exist otherwise will try to create", "publicIP", bastionSpec.PublicIPName) + publicIP := network.PublicIPAddress{} + publicIP, err = s.PublicIPsClient.Get(ctx, s.Scope.ResourceGroup(), bastionSpec.PublicIPName) + if err != nil && azure.ResourceNotFound(err) { + iperr := s.createBastionPublicIP(ctx, bastionSpec.PublicIPName) + if iperr != nil { + return errors.Wrap(iperr, "failed to create bastion publicIP") + } + var errPublicIP error + publicIP, errPublicIP = s.PublicIPsClient.Get(ctx, s.Scope.ResourceGroup(), bastionSpec.PublicIPName) + if errPublicIP != nil { + return errors.Wrap(errPublicIP, "failed to get created publicIP") + } + } else if err != nil { + return errors.Wrap(err, "failed to get existing publicIP") + } + s.Scope.V(2).Info("successfully got public ip", "publicIP", bastionSpec.PublicIPName) + + s.Scope.V(2).Info("creating bastion host", "bastion", bastionSpec.Name) + bastionHostIPConfigName := fmt.Sprintf("%s-%s", bastionSpec.Name, "bastionIP") + err = s.Client.CreateOrUpdate( + ctx, + s.Scope.ResourceGroup(), + bastionSpec.Name, + network.BastionHost{ + Name: to.StringPtr(bastionSpec.Name), + Location: to.StringPtr(s.Scope.Location()), + Tags: converters.TagsToMap(infrav1.Build(infrav1.BuildParams{ + ClusterName: s.Scope.ClusterName(), + Lifecycle: infrav1.ResourceLifecycleOwned, + Name: to.StringPtr(bastionSpec.Name), + Role: to.StringPtr("Bastion"), + })), + BastionHostPropertiesFormat: &network.BastionHostPropertiesFormat{ + DNSName: to.StringPtr(fmt.Sprintf("%s-bastion", strings.ToLower(bastionSpec.Name))), + IPConfigurations: &[]network.BastionHostIPConfiguration{ + { + Name: to.StringPtr(bastionHostIPConfigName), + BastionHostIPConfigurationPropertiesFormat: &network.BastionHostIPConfigurationPropertiesFormat{ + Subnet: &network.SubResource{ + ID: subnet.ID, + }, + PublicIPAddress: &network.SubResource{ + ID: publicIP.ID, + }, + PrivateIPAllocationMethod: network.Static, + }, + }, + }, + }, + }, + ) + if err != nil { + return errors.Wrap(err, "cannot create bastion host") + } + + s.Scope.V(2).Info("successfully created bastion host", "bastion", bastionSpec.Name) + } + return nil +} + +// Delete deletes the bastion host with the provided scope. +func (s *Service) Delete(ctx context.Context) error { + for _, bastionSpec := range s.Scope.BastionSpecs() { + + s.Scope.V(2).Info("deleting bastion host", "bastion", bastionSpec.Name) + + err := s.Client.Delete(ctx, s.Scope.ResourceGroup(), bastionSpec.Name) + if err != nil && azure.ResourceNotFound(err) { + // already deleted + return nil + } + if err != nil { + return errors.Wrapf(err, "failed to delete Bastion Host %s in resource group %s", bastionSpec.Name, s.Scope.ResourceGroup()) + } + + s.Scope.V(2).Info("successfully deleted bastion host", "bastion", bastionSpec.Name) + } + return nil +} + +func (s *Service) createBastionPublicIP(ctx context.Context, ipName string) error { + s.Scope.V(2).Info("creating bastion public IP", "public IP", ipName) + return s.PublicIPsClient.CreateOrUpdate( + ctx, + s.Scope.ResourceGroup(), + ipName, + network.PublicIPAddress{ + Sku: &network.PublicIPAddressSku{Name: network.PublicIPAddressSkuNameStandard}, + Name: to.StringPtr(ipName), + Location: to.StringPtr(s.Scope.Location()), + PublicIPAddressPropertiesFormat: &network.PublicIPAddressPropertiesFormat{ + PublicIPAddressVersion: network.IPv4, + PublicIPAllocationMethod: network.Static, + DNSSettings: &network.PublicIPAddressDNSSettings{ + DomainNameLabel: to.StringPtr(strings.ToLower(ipName)), + }, + }, + }, + ) +} diff --git a/cloud/services/bastionhosts/bastionhosts_test.go b/cloud/services/bastionhosts/bastionhosts_test.go new file mode 100644 index 00000000000..e46513b9939 --- /dev/null +++ b/cloud/services/bastionhosts/bastionhosts_test.go @@ -0,0 +1,372 @@ +/* +Copyright 2020 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 bastionhosts + +import ( + "context" + "net/http" + "testing" + + . "github.com/onsi/gomega" + azure "sigs.k8s.io/cluster-api-provider-azure/cloud" + mock_bastionhosts "sigs.k8s.io/cluster-api-provider-azure/cloud/services/bastionhosts/mocks_bastionhosts" + "sigs.k8s.io/cluster-api-provider-azure/cloud/services/publicips/mock_publicips" + "sigs.k8s.io/cluster-api-provider-azure/cloud/services/subnets/mock_subnets" + + "github.com/Azure/go-autorest/autorest" + "github.com/golang/mock/gomock" + + network "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2019-06-01/network" + "k8s.io/client-go/kubernetes/scheme" + "k8s.io/klog/klogr" + clusterv1 "sigs.k8s.io/cluster-api/api/v1alpha3" +) + +func init() { + _ = clusterv1.AddToScheme(scheme.Scheme) +} + +func TestReconcileBastionHosts(t *testing.T) { + testcases := []struct { + name string + expectedError string + expect func(s *mock_bastionhosts.MockBastionScopeMockRecorder, + m *mock_bastionhosts.MockClientMockRecorder, + mSubnet *mock_subnets.MockClientMockRecorder, + mPublicIP *mock_publicips.MockClientMockRecorder) + }{ + { + name: "fail to get subnets", + expectedError: "failed to get subnet: #: Internal Server Error: StatusCode=500", + expect: func(s *mock_bastionhosts.MockBastionScopeMockRecorder, + m *mock_bastionhosts.MockClientMockRecorder, + mSubnet *mock_subnets.MockClientMockRecorder, + mPublicIP *mock_publicips.MockClientMockRecorder) { + s.V(gomock.AssignableToTypeOf(2)).AnyTimes().Return(klogr.New()) + s.BastionSpecs().Return([]azure.BastionSpec{ + { + Name: "my-bastion", + VNetName: "my-vnet", + SubnetName: "my-subnet", + PublicIPName: "my-publicip", + }, + }) + s.ResourceGroup().AnyTimes().Return("my-rg") + mSubnet.Get(context.TODO(), "my-rg", "my-vnet", "my-subnet"). + Return(network.Subnet{}, autorest.NewErrorWithResponse("", "", &http.Response{StatusCode: 500}, "Internal Server Error")) + }, + }, + { + name: "fail to get publicip", + expectedError: "failed to get existing publicIP: #: Internal Server Error: StatusCode=500", + expect: func(s *mock_bastionhosts.MockBastionScopeMockRecorder, + m *mock_bastionhosts.MockClientMockRecorder, + mSubnet *mock_subnets.MockClientMockRecorder, + mPublicIP *mock_publicips.MockClientMockRecorder) { + s.V(gomock.AssignableToTypeOf(2)).AnyTimes().Return(klogr.New()) + s.BastionSpecs().Return([]azure.BastionSpec{ + { + Name: "my-bastion", + VNetName: "my-vnet", + SubnetName: "my-subnet", + PublicIPName: "my-publicip", + }, + }) + s.ResourceGroup().AnyTimes().Return("my-rg") + gomock.InOrder( + mSubnet.Get(context.TODO(), "my-rg", "my-vnet", "my-subnet").Return(network.Subnet{}, nil), + mPublicIP.Get(context.TODO(), "my-rg", "my-publicip").Return(network.PublicIPAddress{}, autorest.NewErrorWithResponse("", "", &http.Response{StatusCode: 500}, "Internal Server Error")), + ) + }, + }, + { + name: "create publicip fails", + expectedError: "failed to create bastion publicIP: #: Internal Server Error: StatusCode=500", + expect: func(s *mock_bastionhosts.MockBastionScopeMockRecorder, + m *mock_bastionhosts.MockClientMockRecorder, + mSubnet *mock_subnets.MockClientMockRecorder, + mPublicIP *mock_publicips.MockClientMockRecorder) { + s.V(gomock.AssignableToTypeOf(2)).AnyTimes().Return(klogr.New()) + s.BastionSpecs().Return([]azure.BastionSpec{ + { + Name: "my-bastion", + VNetName: "my-vnet", + SubnetName: "my-subnet", + PublicIPName: "my-publicip", + }, + }) + s.ResourceGroup().AnyTimes().Return("my-rg") + s.Location().AnyTimes().Return("fake-location") + gomock.InOrder( + mSubnet.Get(context.TODO(), "my-rg", "my-vnet", "my-subnet").Return(network.Subnet{}, nil), + mPublicIP.Get(context.TODO(), "my-rg", "my-publicip").Return(network.PublicIPAddress{}, autorest.NewErrorWithResponse("", "", &http.Response{StatusCode: 404}, "Not found")), + mPublicIP.CreateOrUpdate(context.TODO(), "my-rg", "my-publicip", gomock.AssignableToTypeOf(network.PublicIPAddress{})).Return(autorest.NewErrorWithResponse("", "", &http.Response{StatusCode: 500}, "Internal Server Error")), + ) + }, + }, + { + name: "fails to get a created publicip", + expectedError: "failed to get created publicIP: #: Internal Server Error: StatusCode=500", + expect: func(s *mock_bastionhosts.MockBastionScopeMockRecorder, + m *mock_bastionhosts.MockClientMockRecorder, + mSubnet *mock_subnets.MockClientMockRecorder, + mPublicIP *mock_publicips.MockClientMockRecorder) { + s.V(gomock.AssignableToTypeOf(2)).AnyTimes().Return(klogr.New()) + s.BastionSpecs().Return([]azure.BastionSpec{ + { + Name: "my-bastion", + VNetName: "my-vnet", + SubnetName: "my-subnet", + PublicIPName: "my-publicip", + }, + }) + s.ResourceGroup().AnyTimes().Return("my-rg") + s.Location().AnyTimes().Return("fake-location") + mSubnet.Get(context.TODO(), "my-rg", "my-vnet", "my-subnet").Return(network.Subnet{}, nil) + mPublicIP.Get(context.TODO(), "my-rg", "my-publicip").Return(network.PublicIPAddress{}, autorest.NewErrorWithResponse("", "", &http.Response{StatusCode: 404}, "Not found")) + mPublicIP.CreateOrUpdate(context.TODO(), "my-rg", "my-publicip", gomock.AssignableToTypeOf(network.PublicIPAddress{})).Return(nil) + mPublicIP.Get(context.TODO(), "my-rg", "my-publicip").Return(network.PublicIPAddress{}, autorest.NewErrorWithResponse("", "", &http.Response{StatusCode: 500}, "Internal Server Error")) + }, + }, + { + name: "bastion successfully created with created public ip", + expectedError: "", + expect: func(s *mock_bastionhosts.MockBastionScopeMockRecorder, + m *mock_bastionhosts.MockClientMockRecorder, + mSubnet *mock_subnets.MockClientMockRecorder, + mPublicIP *mock_publicips.MockClientMockRecorder) { + s.V(gomock.AssignableToTypeOf(2)).AnyTimes().Return(klogr.New()) + s.BastionSpecs().Return([]azure.BastionSpec{ + { + Name: "my-bastion", + VNetName: "my-vnet", + SubnetName: "my-subnet", + PublicIPName: "my-publicip", + }, + }) + s.ResourceGroup().AnyTimes().Return("my-rg") + s.Location().AnyTimes().Return("fake-location") + s.ClusterName().AnyTimes().Return("fake-cluster") + gomock.InOrder( + mSubnet.Get(context.TODO(), "my-rg", "my-vnet", "my-subnet").Return(network.Subnet{}, nil), + mPublicIP.Get(context.TODO(), "my-rg", "my-publicip").Return(network.PublicIPAddress{}, autorest.NewErrorWithResponse("", "", &http.Response{StatusCode: 404}, "Not found")), + mPublicIP.CreateOrUpdate(context.TODO(), "my-rg", "my-publicip", gomock.AssignableToTypeOf(network.PublicIPAddress{})).Return(nil), + mPublicIP.Get(context.TODO(), "my-rg", "my-publicip").Return(network.PublicIPAddress{}, nil), + m.CreateOrUpdate(context.TODO(), "my-rg", "my-bastion", gomock.AssignableToTypeOf(network.BastionHost{})), + ) + }, + }, + { + name: "bastion successfully created", + expectedError: "", + expect: func(s *mock_bastionhosts.MockBastionScopeMockRecorder, + m *mock_bastionhosts.MockClientMockRecorder, + mSubnet *mock_subnets.MockClientMockRecorder, + mPublicIP *mock_publicips.MockClientMockRecorder) { + s.V(gomock.AssignableToTypeOf(2)).AnyTimes().Return(klogr.New()) + s.BastionSpecs().Return([]azure.BastionSpec{ + { + Name: "my-bastion", + VNetName: "my-vnet", + SubnetName: "my-subnet", + PublicIPName: "my-publicip", + }, + }) + s.ResourceGroup().AnyTimes().Return("my-rg") + s.Location().AnyTimes().Return("fake-location") + s.ClusterName().AnyTimes().Return("fake-cluster") + gomock.InOrder( + mSubnet.Get(context.TODO(), "my-rg", "my-vnet", "my-subnet").Return(network.Subnet{}, nil), + mPublicIP.Get(context.TODO(), "my-rg", "my-publicip").Return(network.PublicIPAddress{}, nil), + m.CreateOrUpdate(context.TODO(), "my-rg", "my-bastion", gomock.AssignableToTypeOf(network.BastionHost{})), + ) + }, + }, + { + name: "fail to create a bastion", + expectedError: "cannot create bastion host: #: Internal Server Error: StatusCode=500", + expect: func(s *mock_bastionhosts.MockBastionScopeMockRecorder, + m *mock_bastionhosts.MockClientMockRecorder, + mSubnet *mock_subnets.MockClientMockRecorder, + mPublicIP *mock_publicips.MockClientMockRecorder) { + s.V(gomock.AssignableToTypeOf(2)).AnyTimes().Return(klogr.New()) + s.BastionSpecs().Return([]azure.BastionSpec{ + { + Name: "my-bastion", + VNetName: "my-vnet", + SubnetName: "my-subnet", + PublicIPName: "my-publicip", + }, + }) + s.ResourceGroup().AnyTimes().Return("my-rg") + s.Location().AnyTimes().Return("fake-location") + s.ClusterName().AnyTimes().Return("fake-cluster") + gomock.InOrder( + mSubnet.Get(context.TODO(), "my-rg", "my-vnet", "my-subnet").Return(network.Subnet{}, nil), + mPublicIP.Get(context.TODO(), "my-rg", "my-publicip").Return(network.PublicIPAddress{}, nil), + m.CreateOrUpdate(context.TODO(), "my-rg", "my-bastion", gomock.AssignableToTypeOf(network.BastionHost{})).Return(autorest.NewErrorWithResponse("", "", &http.Response{StatusCode: 500}, "Internal Server Error")), + ) + }, + }, + } + + for _, tc := range testcases { + t.Run(tc.name, func(t *testing.T) { + g := NewWithT(t) + + t.Parallel() + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + scopeMock := mock_bastionhosts.NewMockBastionScope(mockCtrl) + clientMock := mock_bastionhosts.NewMockClient(mockCtrl) + subnetMock := mock_subnets.NewMockClient(mockCtrl) + publicIPsMock := mock_publicips.NewMockClient(mockCtrl) + + tc.expect(scopeMock.EXPECT(), clientMock.EXPECT(), + subnetMock.EXPECT(), publicIPsMock.EXPECT()) + + s := &Service{ + Scope: scopeMock, + Client: clientMock, + SubnetsClient: subnetMock, + PublicIPsClient: publicIPsMock, + } + + err := s.Reconcile(context.TODO()) + if tc.expectedError != "" { + g.Expect(err).To(HaveOccurred()) + g.Expect(err).To(MatchError(tc.expectedError)) + } else { + g.Expect(err).NotTo(HaveOccurred()) + } + }) + } +} + +func TestDeleteBastionHost(t *testing.T) { + testcases := []struct { + name string + expectedError string + expect func(s *mock_bastionhosts.MockBastionScopeMockRecorder, + m *mock_bastionhosts.MockClientMockRecorder, + mSubnet *mock_subnets.MockClientMockRecorder, + mPublicIP *mock_publicips.MockClientMockRecorder) + }{ + { + name: "successfully delete an existing bastion host", + + expectedError: "", + expect: func(s *mock_bastionhosts.MockBastionScopeMockRecorder, + m *mock_bastionhosts.MockClientMockRecorder, + mSubnet *mock_subnets.MockClientMockRecorder, + mPublicIP *mock_publicips.MockClientMockRecorder) { + s.V(gomock.AssignableToTypeOf(2)).AnyTimes().Return(klogr.New()) + s.BastionSpecs().Return([]azure.BastionSpec{ + { + Name: "my-bastionhost", + VNetName: "my-vnet", + SubnetName: "my-subnet", + PublicIPName: "my-publicip", + }, + { + Name: "my-bastionhost1", + VNetName: "my-vnet", + SubnetName: "my-subnet", + PublicIPName: "my-publicip", + }, + }) + s.ResourceGroup().AnyTimes().Return("my-rg") + m.Delete(context.TODO(), "my-rg", "my-bastionhost") + m.Delete(context.TODO(), "my-rg", "my-bastionhost1") + }, + }, + { + name: "bastion host already deleted", + expectedError: "", + expect: func(s *mock_bastionhosts.MockBastionScopeMockRecorder, + m *mock_bastionhosts.MockClientMockRecorder, + mSubnet *mock_subnets.MockClientMockRecorder, + mPublicIP *mock_publicips.MockClientMockRecorder) { + s.V(gomock.AssignableToTypeOf(2)).AnyTimes().Return(klogr.New()) + s.BastionSpecs().Return([]azure.BastionSpec{ + { + Name: "my-bastionhost", + VNetName: "my-vnet", + SubnetName: "my-subnet", + PublicIPName: "my-publicip", + }, + }) + s.ResourceGroup().AnyTimes().Return("my-rg") + m.Delete(context.TODO(), "my-rg", "my-bastionhost"). + Return(autorest.NewErrorWithResponse("", "", &http.Response{StatusCode: 404}, "Not found")) + }, + }, + { + name: "bastion host deletion fails", + expectedError: "failed to delete Bastion Host my-bastionhost in resource group my-rg: #: Internal Server Error: StatusCode=500", + expect: func(s *mock_bastionhosts.MockBastionScopeMockRecorder, + m *mock_bastionhosts.MockClientMockRecorder, + mSubnet *mock_subnets.MockClientMockRecorder, + mPublicIP *mock_publicips.MockClientMockRecorder) { + s.V(gomock.AssignableToTypeOf(2)).AnyTimes().Return(klogr.New()) + s.BastionSpecs().Return([]azure.BastionSpec{ + { + Name: "my-bastionhost", + VNetName: "my-vnet", + SubnetName: "my-subnet", + PublicIPName: "my-publicip", + }, + }) + s.ResourceGroup().AnyTimes().Return("my-rg") + m.Delete(context.TODO(), "my-rg", "my-bastionhost"). + Return(autorest.NewErrorWithResponse("", "", &http.Response{StatusCode: 500}, "Internal Server Error")) + }, + }, + } + + for _, tc := range testcases { + t.Run(tc.name, func(t *testing.T) { + g := NewWithT(t) + + t.Parallel() + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + scopeMock := mock_bastionhosts.NewMockBastionScope(mockCtrl) + clientMock := mock_bastionhosts.NewMockClient(mockCtrl) + subnetMock := mock_subnets.NewMockClient(mockCtrl) + publicIPsMock := mock_publicips.NewMockClient(mockCtrl) + + tc.expect(scopeMock.EXPECT(), clientMock.EXPECT(), + subnetMock.EXPECT(), publicIPsMock.EXPECT()) + + s := &Service{ + Scope: scopeMock, + Client: clientMock, + SubnetsClient: subnetMock, + PublicIPsClient: publicIPsMock, + } + + err := s.Delete(context.TODO()) + if tc.expectedError != "" { + g.Expect(err).To(HaveOccurred()) + g.Expect(err).To(MatchError(tc.expectedError)) + } else { + g.Expect(err).NotTo(HaveOccurred()) + } + }) + } +} diff --git a/cloud/services/bastionhosts/client.go b/cloud/services/bastionhosts/client.go new file mode 100644 index 00000000000..7e65b639efb --- /dev/null +++ b/cloud/services/bastionhosts/client.go @@ -0,0 +1,86 @@ +/* +Copyright 2020 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 bastionhosts + +import ( + "context" + + "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2019-06-01/network" + "github.com/Azure/go-autorest/autorest" + azure "sigs.k8s.io/cluster-api-provider-azure/cloud" +) + +// Client wraps go-sdk +type Client interface { + Get(context.Context, string, string) (network.BastionHost, error) + CreateOrUpdate(context.Context, string, string, network.BastionHost) error + Delete(context.Context, string, string) error +} + +// AzureClient contains the Azure go-sdk Client +type AzureClient struct { + interfaces network.BastionHostsClient +} + +var _ Client = &AzureClient{} + +// NewClient creates a new VM client from subscription ID. +func NewClient(auth azure.Authorizer) *AzureClient { + c := newBastionHostsClient(auth.SubscriptionID(), auth.BaseURI(), auth.Authorizer()) + return &AzureClient{c} +} + +// newBastionHostsClient creates a new bastion host client from subscription ID. +func newBastionHostsClient(subscriptionID string, baseURI string, authorizer autorest.Authorizer) network.BastionHostsClient { + bastionClient := network.NewBastionHostsClientWithBaseURI(baseURI, subscriptionID) + bastionClient.Authorizer = authorizer + bastionClient.AddToUserAgent(azure.UserAgent()) + return bastionClient +} + +// Get gets information about the specified bastion host. +func (ac *AzureClient) Get(ctx context.Context, resourceGroupName, bastionName string) (network.BastionHost, error) { + return ac.interfaces.Get(ctx, resourceGroupName, bastionName) +} + +// CreateOrUpdate creates or updates a bastion host. +func (ac *AzureClient) CreateOrUpdate(ctx context.Context, resourceGroupName string, bastionName string, bastionHost network.BastionHost) error { + future, err := ac.interfaces.CreateOrUpdate(ctx, resourceGroupName, bastionName, bastionHost) + if err != nil { + return err + } + err = future.WaitForCompletionRef(ctx, ac.interfaces.Client) + if err != nil { + return err + } + _, err = future.Result(ac.interfaces) + return err +} + +// Delete deletes the specified network interface. +func (ac *AzureClient) Delete(ctx context.Context, resourceGroupName, bastionName string) error { + future, err := ac.interfaces.Delete(ctx, resourceGroupName, bastionName) + if err != nil { + return err + } + err = future.WaitForCompletionRef(ctx, ac.interfaces.Client) + if err != nil { + return err + } + _, err = future.Result(ac.interfaces) + return err +} diff --git a/cloud/services/bastionhosts/mocks_bastionhosts/bastionhosts_mock.go b/cloud/services/bastionhosts/mocks_bastionhosts/bastionhosts_mock.go new file mode 100644 index 00000000000..82027d5ffba --- /dev/null +++ b/cloud/services/bastionhosts/mocks_bastionhosts/bastionhosts_mock.go @@ -0,0 +1,385 @@ +/* +Copyright 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. +*/ + +// Code generated by MockGen. DO NOT EDIT. +// Source: ../service.go + +// Package mock_bastionhosts is a generated GoMock package. +package mock_bastionhosts + +import ( + autorest "github.com/Azure/go-autorest/autorest" + logr "github.com/go-logr/logr" + gomock "github.com/golang/mock/gomock" + reflect "reflect" + v1alpha3 "sigs.k8s.io/cluster-api-provider-azure/api/v1alpha3" + azure "sigs.k8s.io/cluster-api-provider-azure/cloud" +) + +// MockBastionScope is a mock of BastionScope interface. +type MockBastionScope struct { + ctrl *gomock.Controller + recorder *MockBastionScopeMockRecorder +} + +// MockBastionScopeMockRecorder is the mock recorder for MockBastionScope. +type MockBastionScopeMockRecorder struct { + mock *MockBastionScope +} + +// NewMockBastionScope creates a new mock instance. +func NewMockBastionScope(ctrl *gomock.Controller) *MockBastionScope { + mock := &MockBastionScope{ctrl: ctrl} + mock.recorder = &MockBastionScopeMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockBastionScope) EXPECT() *MockBastionScopeMockRecorder { + return m.recorder +} + +// SubscriptionID mocks base method. +func (m *MockBastionScope) SubscriptionID() string { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "SubscriptionID") + ret0, _ := ret[0].(string) + return ret0 +} + +// SubscriptionID indicates an expected call of SubscriptionID. +func (mr *MockBastionScopeMockRecorder) SubscriptionID() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SubscriptionID", reflect.TypeOf((*MockBastionScope)(nil).SubscriptionID)) +} + +// ClientID mocks base method. +func (m *MockBastionScope) ClientID() string { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ClientID") + ret0, _ := ret[0].(string) + return ret0 +} + +// ClientID indicates an expected call of ClientID. +func (mr *MockBastionScopeMockRecorder) ClientID() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ClientID", reflect.TypeOf((*MockBastionScope)(nil).ClientID)) +} + +// ClientSecret mocks base method. +func (m *MockBastionScope) ClientSecret() string { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ClientSecret") + ret0, _ := ret[0].(string) + return ret0 +} + +// ClientSecret indicates an expected call of ClientSecret. +func (mr *MockBastionScopeMockRecorder) ClientSecret() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ClientSecret", reflect.TypeOf((*MockBastionScope)(nil).ClientSecret)) +} + +// CloudEnvironment mocks base method. +func (m *MockBastionScope) CloudEnvironment() string { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "CloudEnvironment") + ret0, _ := ret[0].(string) + return ret0 +} + +// CloudEnvironment indicates an expected call of CloudEnvironment. +func (mr *MockBastionScopeMockRecorder) CloudEnvironment() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CloudEnvironment", reflect.TypeOf((*MockBastionScope)(nil).CloudEnvironment)) +} + +// TenantID mocks base method. +func (m *MockBastionScope) TenantID() string { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "TenantID") + ret0, _ := ret[0].(string) + return ret0 +} + +// TenantID indicates an expected call of TenantID. +func (mr *MockBastionScopeMockRecorder) TenantID() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "TenantID", reflect.TypeOf((*MockBastionScope)(nil).TenantID)) +} + +// BaseURI mocks base method. +func (m *MockBastionScope) BaseURI() string { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "BaseURI") + ret0, _ := ret[0].(string) + return ret0 +} + +// BaseURI indicates an expected call of BaseURI. +func (mr *MockBastionScopeMockRecorder) BaseURI() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "BaseURI", reflect.TypeOf((*MockBastionScope)(nil).BaseURI)) +} + +// Authorizer mocks base method. +func (m *MockBastionScope) Authorizer() autorest.Authorizer { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Authorizer") + ret0, _ := ret[0].(autorest.Authorizer) + return ret0 +} + +// Authorizer indicates an expected call of Authorizer. +func (mr *MockBastionScopeMockRecorder) Authorizer() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Authorizer", reflect.TypeOf((*MockBastionScope)(nil).Authorizer)) +} + +// ResourceGroup mocks base method. +func (m *MockBastionScope) ResourceGroup() string { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ResourceGroup") + ret0, _ := ret[0].(string) + return ret0 +} + +// ResourceGroup indicates an expected call of ResourceGroup. +func (mr *MockBastionScopeMockRecorder) ResourceGroup() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ResourceGroup", reflect.TypeOf((*MockBastionScope)(nil).ResourceGroup)) +} + +// ClusterName mocks base method. +func (m *MockBastionScope) ClusterName() string { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ClusterName") + ret0, _ := ret[0].(string) + return ret0 +} + +// ClusterName indicates an expected call of ClusterName. +func (mr *MockBastionScopeMockRecorder) ClusterName() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ClusterName", reflect.TypeOf((*MockBastionScope)(nil).ClusterName)) +} + +// Location mocks base method. +func (m *MockBastionScope) Location() string { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Location") + ret0, _ := ret[0].(string) + return ret0 +} + +// Location indicates an expected call of Location. +func (mr *MockBastionScopeMockRecorder) Location() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Location", reflect.TypeOf((*MockBastionScope)(nil).Location)) +} + +// AdditionalTags mocks base method. +func (m *MockBastionScope) AdditionalTags() v1alpha3.Tags { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "AdditionalTags") + ret0, _ := ret[0].(v1alpha3.Tags) + return ret0 +} + +// AdditionalTags indicates an expected call of AdditionalTags. +func (mr *MockBastionScopeMockRecorder) AdditionalTags() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AdditionalTags", reflect.TypeOf((*MockBastionScope)(nil).AdditionalTags)) +} + +// Vnet mocks base method. +func (m *MockBastionScope) Vnet() *v1alpha3.VnetSpec { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Vnet") + ret0, _ := ret[0].(*v1alpha3.VnetSpec) + return ret0 +} + +// Vnet indicates an expected call of Vnet. +func (mr *MockBastionScopeMockRecorder) Vnet() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Vnet", reflect.TypeOf((*MockBastionScope)(nil).Vnet)) +} + +// IsVnetManaged mocks base method. +func (m *MockBastionScope) IsVnetManaged() bool { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "IsVnetManaged") + ret0, _ := ret[0].(bool) + return ret0 +} + +// IsVnetManaged indicates an expected call of IsVnetManaged. +func (mr *MockBastionScopeMockRecorder) IsVnetManaged() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsVnetManaged", reflect.TypeOf((*MockBastionScope)(nil).IsVnetManaged)) +} + +// NodeSubnet mocks base method. +func (m *MockBastionScope) NodeSubnet() *v1alpha3.SubnetSpec { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "NodeSubnet") + ret0, _ := ret[0].(*v1alpha3.SubnetSpec) + return ret0 +} + +// NodeSubnet indicates an expected call of NodeSubnet. +func (mr *MockBastionScopeMockRecorder) NodeSubnet() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NodeSubnet", reflect.TypeOf((*MockBastionScope)(nil).NodeSubnet)) +} + +// ControlPlaneSubnet mocks base method. +func (m *MockBastionScope) ControlPlaneSubnet() *v1alpha3.SubnetSpec { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ControlPlaneSubnet") + ret0, _ := ret[0].(*v1alpha3.SubnetSpec) + return ret0 +} + +// ControlPlaneSubnet indicates an expected call of ControlPlaneSubnet. +func (mr *MockBastionScopeMockRecorder) ControlPlaneSubnet() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ControlPlaneSubnet", reflect.TypeOf((*MockBastionScope)(nil).ControlPlaneSubnet)) +} + +// RouteTable mocks base method. +func (m *MockBastionScope) RouteTable() *v1alpha3.RouteTable { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "RouteTable") + ret0, _ := ret[0].(*v1alpha3.RouteTable) + return ret0 +} + +// RouteTable indicates an expected call of RouteTable. +func (mr *MockBastionScopeMockRecorder) RouteTable() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RouteTable", reflect.TypeOf((*MockBastionScope)(nil).RouteTable)) +} + +// Info mocks base method. +func (m *MockBastionScope) Info(msg string, keysAndValues ...interface{}) { + m.ctrl.T.Helper() + varargs := []interface{}{msg} + for _, a := range keysAndValues { + varargs = append(varargs, a) + } + m.ctrl.Call(m, "Info", varargs...) +} + +// Info indicates an expected call of Info. +func (mr *MockBastionScopeMockRecorder) Info(msg interface{}, keysAndValues ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{msg}, keysAndValues...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Info", reflect.TypeOf((*MockBastionScope)(nil).Info), varargs...) +} + +// Enabled mocks base method. +func (m *MockBastionScope) Enabled() bool { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Enabled") + ret0, _ := ret[0].(bool) + return ret0 +} + +// Enabled indicates an expected call of Enabled. +func (mr *MockBastionScopeMockRecorder) Enabled() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Enabled", reflect.TypeOf((*MockBastionScope)(nil).Enabled)) +} + +// Error mocks base method. +func (m *MockBastionScope) Error(err error, msg string, keysAndValues ...interface{}) { + m.ctrl.T.Helper() + varargs := []interface{}{err, msg} + for _, a := range keysAndValues { + varargs = append(varargs, a) + } + m.ctrl.Call(m, "Error", varargs...) +} + +// Error indicates an expected call of Error. +func (mr *MockBastionScopeMockRecorder) Error(err, msg interface{}, keysAndValues ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{err, msg}, keysAndValues...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Error", reflect.TypeOf((*MockBastionScope)(nil).Error), varargs...) +} + +// V mocks base method. +func (m *MockBastionScope) V(level int) logr.InfoLogger { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "V", level) + ret0, _ := ret[0].(logr.InfoLogger) + return ret0 +} + +// V indicates an expected call of V. +func (mr *MockBastionScopeMockRecorder) V(level interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "V", reflect.TypeOf((*MockBastionScope)(nil).V), level) +} + +// WithValues mocks base method. +func (m *MockBastionScope) WithValues(keysAndValues ...interface{}) logr.Logger { + m.ctrl.T.Helper() + varargs := []interface{}{} + for _, a := range keysAndValues { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "WithValues", varargs...) + ret0, _ := ret[0].(logr.Logger) + return ret0 +} + +// WithValues indicates an expected call of WithValues. +func (mr *MockBastionScopeMockRecorder) WithValues(keysAndValues ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "WithValues", reflect.TypeOf((*MockBastionScope)(nil).WithValues), keysAndValues...) +} + +// WithName mocks base method. +func (m *MockBastionScope) WithName(name string) logr.Logger { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "WithName", name) + ret0, _ := ret[0].(logr.Logger) + return ret0 +} + +// WithName indicates an expected call of WithName. +func (mr *MockBastionScopeMockRecorder) WithName(name interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "WithName", reflect.TypeOf((*MockBastionScope)(nil).WithName), name) +} + +// BastionSpecs mocks base method. +func (m *MockBastionScope) BastionSpecs() []azure.BastionSpec { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "BastionSpecs") + ret0, _ := ret[0].([]azure.BastionSpec) + return ret0 +} + +// BastionSpecs indicates an expected call of BastionSpecs. +func (mr *MockBastionScopeMockRecorder) BastionSpecs() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "BastionSpecs", reflect.TypeOf((*MockBastionScope)(nil).BastionSpecs)) +} diff --git a/cloud/services/bastionhosts/mocks_bastionhosts/client_mock.go b/cloud/services/bastionhosts/mocks_bastionhosts/client_mock.go new file mode 100644 index 00000000000..09958140886 --- /dev/null +++ b/cloud/services/bastionhosts/mocks_bastionhosts/client_mock.go @@ -0,0 +1,94 @@ +/* +Copyright 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. +*/ + +// Code generated by MockGen. DO NOT EDIT. +// Source: ../client.go + +// Package mock_bastionhosts is a generated GoMock package. +package mock_bastionhosts + +import ( + context "context" + network "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2019-06-01/network" + gomock "github.com/golang/mock/gomock" + reflect "reflect" +) + +// MockClient is a mock of Client interface. +type MockClient struct { + ctrl *gomock.Controller + recorder *MockClientMockRecorder +} + +// MockClientMockRecorder is the mock recorder for MockClient. +type MockClientMockRecorder struct { + mock *MockClient +} + +// NewMockClient creates a new mock instance. +func NewMockClient(ctrl *gomock.Controller) *MockClient { + mock := &MockClient{ctrl: ctrl} + mock.recorder = &MockClientMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockClient) EXPECT() *MockClientMockRecorder { + return m.recorder +} + +// Get mocks base method. +func (m *MockClient) Get(arg0 context.Context, arg1, arg2 string) (network.BastionHost, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Get", arg0, arg1, arg2) + ret0, _ := ret[0].(network.BastionHost) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Get indicates an expected call of Get. +func (mr *MockClientMockRecorder) Get(arg0, arg1, arg2 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockClient)(nil).Get), arg0, arg1, arg2) +} + +// CreateOrUpdate mocks base method. +func (m *MockClient) CreateOrUpdate(arg0 context.Context, arg1, arg2 string, arg3 network.BastionHost) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "CreateOrUpdate", arg0, arg1, arg2, arg3) + ret0, _ := ret[0].(error) + return ret0 +} + +// CreateOrUpdate indicates an expected call of CreateOrUpdate. +func (mr *MockClientMockRecorder) CreateOrUpdate(arg0, arg1, arg2, arg3 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateOrUpdate", reflect.TypeOf((*MockClient)(nil).CreateOrUpdate), arg0, arg1, arg2, arg3) +} + +// Delete mocks base method. +func (m *MockClient) Delete(arg0 context.Context, arg1, arg2 string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Delete", arg0, arg1, arg2) + ret0, _ := ret[0].(error) + return ret0 +} + +// Delete indicates an expected call of Delete. +func (mr *MockClientMockRecorder) Delete(arg0, arg1, arg2 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Delete", reflect.TypeOf((*MockClient)(nil).Delete), arg0, arg1, arg2) +} diff --git a/cloud/services/bastionhosts/mocks_bastionhosts/doc.go b/cloud/services/bastionhosts/mocks_bastionhosts/doc.go new file mode 100644 index 00000000000..4e209e770ee --- /dev/null +++ b/cloud/services/bastionhosts/mocks_bastionhosts/doc.go @@ -0,0 +1,22 @@ +/* +Copyright 2020 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. +*/ + +// Run go generate to regenerate this mock. +//go:generate ../../../../hack/tools/bin/mockgen -destination client_mock.go -package mock_bastionhosts -source ../client.go Client +//go:generate ../../../../hack/tools/bin/mockgen -destination bastionhosts_mock.go -package mock_bastionhosts -source ../service.go BastionScope +//go:generate /usr/bin/env bash -c "cat ../../../../hack/boilerplate/boilerplate.generatego.txt client_mock.go > _client_mock.go && mv _client_mock.go client_mock.go" +//go:generate /usr/bin/env bash -c "cat ../../../../hack/boilerplate/boilerplate.generatego.txt bastionhosts_mock.go > _bastionhosts_mock.go && mv _bastionhosts_mock.go bastionhosts_mock.go" +package mock_bastionhosts //nolint diff --git a/cloud/services/bastionhosts/service.go b/cloud/services/bastionhosts/service.go new file mode 100644 index 00000000000..926e062d932 --- /dev/null +++ b/cloud/services/bastionhosts/service.go @@ -0,0 +1,49 @@ +/* +Copyright 2020 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 bastionhosts + +import ( + "github.com/go-logr/logr" + azure "sigs.k8s.io/cluster-api-provider-azure/cloud" + "sigs.k8s.io/cluster-api-provider-azure/cloud/services/publicips" + "sigs.k8s.io/cluster-api-provider-azure/cloud/services/subnets" +) + +// BastionScope defines the scope interface for a bastion host service. +type BastionScope interface { + azure.ClusterDescriber + logr.Logger + BastionSpecs() []azure.BastionSpec +} + +// Service provides operations on azure resources +type Service struct { + Scope BastionScope + Client + SubnetsClient subnets.Client + PublicIPsClient publicips.Client +} + +// NewService creates a new service. +func NewService(scope BastionScope) *Service { + return &Service{ + Scope: scope, + Client: NewClient(scope), + SubnetsClient: subnets.NewClient(scope), + PublicIPsClient: publicips.NewClient(scope), + } +} diff --git a/cloud/services/networkinterfaces/networkinterfaces.go b/cloud/services/networkinterfaces/networkinterfaces.go index 66aa25a6912..cd31357e45d 100644 --- a/cloud/services/networkinterfaces/networkinterfaces.go +++ b/cloud/services/networkinterfaces/networkinterfaces.go @@ -18,6 +18,7 @@ package networkinterfaces import ( "context" + "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2019-06-01/network" "github.com/Azure/go-autorest/autorest/to" "github.com/pkg/errors" diff --git a/cloud/types.go b/cloud/types.go index 190cd5453d5..3fd616da2e5 100644 --- a/cloud/types.go +++ b/cloud/types.go @@ -115,3 +115,11 @@ type VMSpec struct { UserAssignedIdentities []infrav1.UserAssignedIdentity SpotVMOptions *infrav1.SpotVMOptions } + +// BastionSpec specification for bastion host +type BastionSpec struct { + Name string + SubnetName string + PublicIPName string + VNetName string +}