diff --git a/pkg/azclient/Makefile b/pkg/azclient/Makefile index 2acfc3a225..32e3656b24 100644 --- a/pkg/azclient/Makefile +++ b/pkg/azclient/Makefile @@ -78,7 +78,9 @@ generatecode: build ## Generate client $(TYPESCAFFOLD) --package github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v4 --package-alias armcompute --resource AvailabilitySet --client-name AvailabilitySetsClient --verbs get,list $(TYPESCAFFOLD) --package github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v4 --package-alias armcompute --resource VirtualMachine --client-name VirtualMachinesClient --verbs createorupdate,delete,list --expand $(TYPESCAFFOLD) --package github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v4 --package-alias armcompute --resource VirtualMachineScaleSet --client-name VirtualMachineScaleSetsClient --verbs get,createorupdate,delete,list + $(TYPESCAFFOLD) --package github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v4 --package-alias armcompute --resource VirtualMachineScaleSet --subresource VirtualMachineScaleSetVM --client-name VirtualMachineScaleSetVMsClient --verbs get,delete,list $(TYPESCAFFOLD) --package github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v4 --package-alias armcompute --resource Snapshot --client-name SnapshotsClient --verbs get,createorupdate,delete + $(TYPESCAFFOLD) --package github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork/v3 --package-alias armnetwork --resource VirtualNetwork --subresource Subnet --client-name SubnetsClient --verbs get,createorupdate,delete,list --expand $(TYPESCAFFOLD) --package github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork/v3 --package-alias armnetwork --resource Interface --client-name InterfacesClient --verbs get,createorupdate,delete,list --expand $(TYPESCAFFOLD) --package github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork/v3 --package-alias armnetwork --resource LoadBalancer --client-name LoadBalancersClient --verbs get,createorupdate,delete,list --expand $(TYPESCAFFOLD) --package github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork/v3 --package-alias armnetwork --resource PrivateEndpoint --client-name PrivateEndpointsClient --verbs get,createorupdate --expand diff --git a/pkg/azclient/client-gen/cmd/typescaffold/type_scaffold.go b/pkg/azclient/client-gen/cmd/typescaffold/type_scaffold.go index c930f9147f..b0c67b76c4 100644 --- a/pkg/azclient/client-gen/cmd/typescaffold/type_scaffold.go +++ b/pkg/azclient/client-gen/cmd/typescaffold/type_scaffold.go @@ -30,6 +30,7 @@ import ( type TypeScaffoldOptions struct { Resource string + SubResource string Package string PackageAlias string ClientName string @@ -38,7 +39,11 @@ type TypeScaffoldOptions struct { } var ( - TypeTemplateRaw = ` + TypeTemplateHeader = ` +{{ $resource := .Resource}} +{{ if (gt (len .SubResource) 0) }} +{{ $resource = .SubResource}} +{{ end }} /* Copyright {{now.UTC.Year}} The Kubernetes Authors. @@ -56,12 +61,13 @@ limitations under the License. */ // +azure:enableclientgen:=true -package {{tolower .Resource}}client +package {{tolower $resource}}client import ( {{tolower .PackageAlias}} "{{.Package}}" "sigs.k8s.io/cloud-provider-azure/pkg/azclient/utils" ) - +` + TypeResourceTemplate = ` // +azure:client:verbs={{join .Verbs ";"}},resource={{.Resource}},packageName={{.Package}},packageAlias={{tolower .PackageAlias}},clientName={{.ClientName}},expand={{.Expand}} type Interface interface { {{ $expandable := .Expand}} @@ -78,6 +84,24 @@ type Interface interface { utils.DeleteFunc[{{tolower $packageAlias}}.{{$resource}}]{{end}} {{end}} } +` + TypeSubResourceTemplate = ` +// +azure:client:verbs={{join .Verbs ";"}},resource={{.Resource}},subResource={{.SubResource}},packageName={{.Package}},packageAlias={{tolower .PackageAlias}},clientName={{.ClientName}},expand={{.Expand}} +type Interface interface { +{{ $expandable := .Expand}} +{{ $packageAlias := .PackageAlias}} +{{ $resource := .SubResource}} +{{range $index,$element := .Verbs}} +{{if strequal $element "Get"}} + {{ if $expandable }}utils.SubResourceGetWithExpandFunc[{{tolower $packageAlias}}.{{$resource}}]{{ else }}utils.SubResourceGetFunc[{{tolower $packageAlias}}.{{$resource}}]{{end}}{{end}} +{{if or (strequal $element "ListByRG") (strequal $element "List") }} + utils.SubResourceListFunc[{{tolower $packageAlias}}.{{$resource}}]{{end}} +{{if strequal $element "CreateOrUpdate"}} + utils.SubResourceCreateOrUpdateFunc[{{tolower $packageAlias}}.{{$resource}}]{{end}} +{{if strequal $element "Delete"}} + utils.SubResourceDeleteFunc[{{tolower $packageAlias}}.{{$resource}}]{{end}} +{{end}} +} ` typesTemplateHelpers = template.FuncMap{ "tolower": strings.ToLower, @@ -85,8 +109,9 @@ type Interface interface { "join": strings.Join, "strequal": strings.EqualFold, } + typesSubResourceTemplate = template.Must(template.New("object-scaffolding").Funcs(typesTemplateHelpers).Parse(TypeTemplateHeader + TypeSubResourceTemplate)) - typesTemplate = template.Must(template.New("object-scaffolding").Funcs(typesTemplateHelpers).Parse(TypeTemplateRaw)) + typesResourceTemplate = template.Must(template.New("object-scaffolding").Funcs(typesTemplateHelpers).Parse(TypeTemplateHeader + TypeResourceTemplate)) ) func main() { @@ -99,18 +124,30 @@ func main() { Long: `scaffold client interface for azure resource`, Run: func(cmd *cobra.Command, args []string) { - content := new(bytes.Buffer) - if err := typesTemplate.Execute(content, scaffoldOptions); err != nil { - fmt.Printf("failed to generate client from template %s\n", err.Error()) - return + if len(scaffoldOptions.SubResource) > 0 { + if err := typesSubResourceTemplate.Execute(content, scaffoldOptions); err != nil { + fmt.Printf("failed to generate client from template %s\n", err.Error()) + return + } + } else { + if err := typesResourceTemplate.Execute(content, scaffoldOptions); err != nil { + fmt.Printf("failed to generate client from template %s\n", err.Error()) + return + } } formattedContent, err := format.Source(content.Bytes()) if err != nil { - fmt.Printf("failed to generate client %s\n", err.Error()) + fmt.Printf("failed to generate client %s\noriginal content: \n%s\n", err.Error(), content.String()) return } - fileName := strings.ToLower(scaffoldOptions.Resource) + "client" + var resourceName string + if len(scaffoldOptions.SubResource) > 0 { + resourceName = scaffoldOptions.SubResource + } else { + resourceName = scaffoldOptions.Resource + } + fileName := strings.ToLower(resourceName) + "client" if err := os.MkdirAll(fileName, 0755); err != nil && !os.IsExist(err) { fmt.Printf("failed to create dir %s\n", err.Error()) return @@ -152,8 +189,11 @@ func main() { } rootCmd.Flags().StringSliceVar(&scaffoldOptions.Verbs, "verbs", []string{"get", "createorupdate", "delete", "listbyrg"}, "verbs") rootCmd.Flags().BoolVar(&scaffoldOptions.Expand, "expand", false, "get support expand params") + rootCmd.Flags().StringVar(&scaffoldOptions.SubResource, "subresource", "", "subresource name") + err := rootCmd.Execute() if err != nil { + fmt.Printf("failed to generate interface %s\n", err.Error()) os.Exit(1) } } diff --git a/pkg/azclient/client-gen/generator/template.go b/pkg/azclient/client-gen/generator/template.go index 26f7f5a16b..db60939185 100644 --- a/pkg/azclient/client-gen/generator/template.go +++ b/pkg/azclient/client-gen/generator/template.go @@ -21,6 +21,7 @@ import "html/template" type ClientGenConfig struct { Verbs []string Resource string + SubResource string `marker:"subResource,optional"` PackageName string PackageAlias string ClientName string @@ -48,14 +49,18 @@ func New(subscriptionID string, credential azcore.TokenCredential, options *arm. `)) const CreateOrUpdateFuncTemplateRaw = ` -// CreateOrUpdate creates or updates a {{.Resource}}. -func (client *Client) CreateOrUpdate(ctx context.Context, resourceGroupName string, resourceName string, resource {{.PackageAlias}}.{{.Resource}}) (*{{.PackageAlias}}.{{.Resource}}, error) { - resp, err := utils.NewPollerWrapper(client.{{.ClientName}}.BeginCreateOrUpdate(ctx, resourceGroupName, resourceName, resource, nil)).WaitforPollerResp(ctx) +{{ $resource := .Resource}} +{{ if (gt (len .SubResource) 0) }} +{{ $resource = .SubResource}} +{{ end }} +// CreateOrUpdate creates or updates a {{$resource}}. +func (client *Client) CreateOrUpdate(ctx context.Context, resourceGroupName string, resourceName string,{{with .SubResource}}parentResourceName string, {{end}} resource {{.PackageAlias}}.{{$resource}}) (*{{.PackageAlias}}.{{$resource}}, error) { + resp, err := utils.NewPollerWrapper(client.{{.ClientName}}.BeginCreateOrUpdate(ctx, resourceGroupName, resourceName,{{with .SubResource}}parentResourceName,{{end}} resource, nil)).WaitforPollerResp(ctx) if err != nil { return nil, err } if resp != nil { - return &resp.{{.Resource}}, nil + return &resp.{{$resource}}, nil } return nil, nil } @@ -64,8 +69,12 @@ func (client *Client) CreateOrUpdate(ctx context.Context, resourceGroupName stri var CreateOrUpdateFuncTemplate = template.Must(template.New("object-scaffolding-create-func").Parse(CreateOrUpdateFuncTemplateRaw)) const ListByRGFuncTemplateRaw = ` -// List gets a list of {{.Resource}} in the resource group. -func (client *Client) List(ctx context.Context, resourceGroupName string) (result []*{{.PackageAlias}}.{{.Resource}}, rerr error) { +{{ $resource := .Resource}} +{{ if (gt (len .SubResource) 0) }} +{{ $resource = .SubResource}} +{{ end }} +// List gets a list of {{$resource}} in the resource group. +func (client *Client) List(ctx context.Context, resourceGroupName string{{with .SubResource}}, parentResourceName string{{end}}) (result []*{{.PackageAlias}}.{{$resource}}, rerr error) { pager := client.{{.ClientName}}.NewListByResourceGroupPager(resourceGroupName, nil) for pager.More() { nextResult, err := pager.NextPage(ctx) @@ -81,9 +90,13 @@ func (client *Client) List(ctx context.Context, resourceGroupName string) (resul var ListByRGFuncTemplate = template.Must(template.New("object-scaffolding-list-func").Parse(ListByRGFuncTemplateRaw)) const ListFuncTemplateRaw = ` -// List gets a list of {{.Resource}} in the resource group. -func (client *Client) List(ctx context.Context, resourceGroupName string) (result []*{{.PackageAlias}}.{{.Resource}}, rerr error) { - pager := client.{{.ClientName}}.NewListPager(resourceGroupName, nil) +{{ $resource := .Resource}} +{{ if (gt (len .SubResource) 0) }} +{{ $resource = .SubResource}} +{{ end }} +// List gets a list of {{$resource}} in the resource group. +func (client *Client) List(ctx context.Context, resourceGroupName string{{with .SubResource}}, parentResourceName string{{end}}) (result []*{{.PackageAlias}}.{{$resource}}, rerr error) { + pager := client.{{.ClientName}}.NewListPager(resourceGroupName,{{with .SubResource}}parentResourceName,{{end}} nil) for pager.More() { nextResult, err := pager.NextPage(ctx) if err != nil { @@ -98,9 +111,13 @@ func (client *Client) List(ctx context.Context, resourceGroupName string) (resul var ListFuncTemplate = template.Must(template.New("object-scaffolding-list-func").Parse(ListFuncTemplateRaw)) const DeleteFuncTemplateRaw = ` -// Delete deletes a {{.Resource}} by name. -func (client *Client) Delete(ctx context.Context, resourceGroupName string, resourceName string) error { - _, err := utils.NewPollerWrapper(client.BeginDelete(ctx, resourceGroupName, resourceName, nil)).WaitforPollerResp(ctx) +{{ $resource := .Resource}} +{{ if (gt (len .SubResource) 0) }} +{{ $resource = .SubResource}} +{{ end }} +// Delete deletes a {{$resource}} by name. +func (client *Client) Delete(ctx context.Context, resourceGroupName string, {{with .SubResource}} parentResourceName string, {{end}}resourceName string) error { + _, err := utils.NewPollerWrapper(client.BeginDelete(ctx, resourceGroupName,{{with .SubResource}}parentResourceName,{{end}} resourceName, nil)).WaitforPollerResp(ctx) return err } ` @@ -108,19 +125,23 @@ func (client *Client) Delete(ctx context.Context, resourceGroupName string, reso var DeleteFuncTemplate = template.Must(template.New("object-scaffolding-delete-func").Parse(DeleteFuncTemplateRaw)) const GetFuncTemplateRaw = ` -// Get gets the {{.Resource}} -func (client *Client) Get(ctx context.Context, resourceGroupName string, resourceName string{{if .Expand}}, expand *string{{end}}) (result *{{.PackageAlias}}.{{.Resource}}, rerr error) { +{{ $resource := .Resource}} +{{ if (gt (len .SubResource) 0) }} +{{ $resource = .SubResource}} +{{ end }} +// Get gets the {{$resource}} +func (client *Client) Get(ctx context.Context, resourceGroupName string, {{with .SubResource}}parentResourceName string,{{end}} resourceName string{{if .Expand}}, expand *string{{end}}) (result *{{.PackageAlias}}.{{$resource}}, rerr error) { var ops *{{.PackageAlias}}.{{.ClientName}}GetOptions {{if .Expand}}if expand != nil { ops = &{{.PackageAlias}}.{{.ClientName}}GetOptions{ Expand: expand } }{{end}} - resp, err := client.{{.ClientName}}.Get(ctx, resourceGroupName, resourceName, ops) + resp, err := client.{{.ClientName}}.Get(ctx, resourceGroupName,{{with .SubResource}}parentResourceName,{{end}} resourceName, ops) if err != nil { return nil, err } //handle statuscode - return &resp.{{.Resource}}, nil + return &resp.{{$resource}}, nil } ` @@ -136,7 +157,10 @@ type ImportStatement struct { var TestSuiteTemplate = template.Must(template.New("object-scaffolding-test-suite").Parse( ` - +{{ $resource := .Resource}} +{{ if (gt (len .SubResource) 0) }} +{{ $resource = .SubResource}} +{{ end }} func TestClient(t *testing.T) { RegisterFailHandler(Fail) RunSpecs(t, "Client Suite") @@ -152,7 +176,7 @@ var recorder *recording.Recorder var realClient Interface var _ = BeforeSuite(func(ctx context.Context) { - recorder, err = recording.NewRecorder("testdata/{{.Resource}}") + recorder, err = recording.NewRecorder("testdata/{{$resource}}") Expect(err).ToNot(HaveOccurred()) subscriptionID = recorder.SubscriptionID() Expect(err).NotTo(HaveOccurred()) diff --git a/pkg/azclient/deploymentclient/mock_deploymentclient/interface.go b/pkg/azclient/deploymentclient/mock_deploymentclient/interface.go index 9ae97813a7..3d6351b203 100644 --- a/pkg/azclient/deploymentclient/mock_deploymentclient/interface.go +++ b/pkg/azclient/deploymentclient/mock_deploymentclient/interface.go @@ -24,6 +24,7 @@ import ( context "context" reflect "reflect" + armresources "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources" gomock "github.com/golang/mock/gomock" ) @@ -50,6 +51,21 @@ func (m *MockInterface) EXPECT() *MockInterfaceMockRecorder { return m.recorder } +// CreateOrUpdate mocks base method. +func (m *MockInterface) CreateOrUpdate(arg0 context.Context, arg1, arg2 string, arg3 armresources.Deployment) (*armresources.DeploymentExtended, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "CreateOrUpdate", arg0, arg1, arg2, arg3) + ret0, _ := ret[0].(*armresources.DeploymentExtended) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// CreateOrUpdate indicates an expected call of CreateOrUpdate. +func (mr *MockInterfaceMockRecorder) CreateOrUpdate(arg0, arg1, arg2, arg3 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateOrUpdate", reflect.TypeOf((*MockInterface)(nil).CreateOrUpdate), arg0, arg1, arg2, arg3) +} + // Delete mocks base method. func (m *MockInterface) Delete(arg0 context.Context, arg1, arg2 string) error { m.ctrl.T.Helper() @@ -63,3 +79,33 @@ func (mr *MockInterfaceMockRecorder) Delete(arg0, arg1, arg2 interface{}) *gomoc mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Delete", reflect.TypeOf((*MockInterface)(nil).Delete), arg0, arg1, arg2) } + +// Get mocks base method. +func (m *MockInterface) Get(arg0 context.Context, arg1, arg2 string) (*armresources.DeploymentExtended, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Get", arg0, arg1, arg2) + ret0, _ := ret[0].(*armresources.DeploymentExtended) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Get indicates an expected call of Get. +func (mr *MockInterfaceMockRecorder) Get(arg0, arg1, arg2 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockInterface)(nil).Get), arg0, arg1, arg2) +} + +// List mocks base method. +func (m *MockInterface) List(arg0 context.Context, arg1 string) ([]*armresources.DeploymentExtended, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "List", arg0, arg1) + ret0, _ := ret[0].([]*armresources.DeploymentExtended) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// List indicates an expected call of List. +func (mr *MockInterfaceMockRecorder) List(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "List", reflect.TypeOf((*MockInterface)(nil).List), arg0, arg1) +} diff --git a/pkg/azclient/go.mod b/pkg/azclient/go.mod index 9f5a75a452..d4c28135d2 100644 --- a/pkg/azclient/go.mod +++ b/pkg/azclient/go.mod @@ -11,6 +11,7 @@ require ( github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/privatedns/armprivatedns v1.1.0 github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources v1.1.1 github.com/Azure/go-armbalancer v0.0.2 + github.com/golang/mock v1.6.0 github.com/google/go-cmp v0.5.9 github.com/google/uuid v1.3.0 github.com/onsi/ginkgo/v2 v2.9.5 @@ -29,7 +30,6 @@ require ( github.com/go-logr/stdr v1.2.2 // indirect github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect github.com/golang-jwt/jwt/v4 v4.5.0 // indirect - github.com/golang/mock v1.6.0 // indirect github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 // indirect github.com/kr/pretty v0.3.0 // indirect github.com/kylelemons/godebug v1.1.0 // indirect diff --git a/pkg/azclient/interfaceclient/interface.go b/pkg/azclient/interfaceclient/interface.go index e3a8e9ddd7..21e3db7b35 100644 --- a/pkg/azclient/interfaceclient/interface.go +++ b/pkg/azclient/interfaceclient/interface.go @@ -18,6 +18,8 @@ limitations under the License. package interfaceclient import ( + "context" + armnetwork "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork/v3" "sigs.k8s.io/cloud-provider-azure/pkg/azclient/utils" @@ -26,7 +28,8 @@ import ( // +azure:client:verbs=get;createorupdate;delete;list,resource=Interface,packageName=github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork/v3,packageAlias=armnetwork,clientName=InterfacesClient,expand=true type Interface interface { utils.GetWithExpandFunc[armnetwork.Interface] - + // GetVirtualMachineScaleSetNetworkInterface gets a network.Interface of VMSS VM. + GetVirtualMachineScaleSetNetworkInterface(ctx context.Context, resourceGroupName string, virtualMachineScaleSetName string, virtualmachineIndex string, networkInterfaceName string, options *armnetwork.InterfacesClientGetVirtualMachineScaleSetNetworkInterfaceOptions) (armnetwork.InterfacesClientGetVirtualMachineScaleSetNetworkInterfaceResponse, error) utils.CreateOrUpdateFunc[armnetwork.Interface] utils.DeleteFunc[armnetwork.Interface] diff --git a/pkg/azclient/interfaceclient/mock_interfaceclient/interface.go b/pkg/azclient/interfaceclient/mock_interfaceclient/interface.go index 3a29ffa6a5..88092ffec0 100644 --- a/pkg/azclient/interfaceclient/mock_interfaceclient/interface.go +++ b/pkg/azclient/interfaceclient/mock_interfaceclient/interface.go @@ -95,6 +95,21 @@ func (mr *MockInterfaceMockRecorder) Get(arg0, arg1, arg2, arg3 interface{}) *go return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockInterface)(nil).Get), arg0, arg1, arg2, arg3) } +// GetVirtualMachineScaleSetNetworkInterface mocks base method. +func (m *MockInterface) GetVirtualMachineScaleSetNetworkInterface(arg0 context.Context, arg1, arg2, arg3, arg4 string, arg5 *armnetwork.InterfacesClientGetVirtualMachineScaleSetNetworkInterfaceOptions) (armnetwork.InterfacesClientGetVirtualMachineScaleSetNetworkInterfaceResponse, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetVirtualMachineScaleSetNetworkInterface", arg0, arg1, arg2, arg3, arg4, arg5) + ret0, _ := ret[0].(armnetwork.InterfacesClientGetVirtualMachineScaleSetNetworkInterfaceResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetVirtualMachineScaleSetNetworkInterface indicates an expected call of GetVirtualMachineScaleSetNetworkInterface. +func (mr *MockInterfaceMockRecorder) GetVirtualMachineScaleSetNetworkInterface(arg0, arg1, arg2, arg3, arg4, arg5 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetVirtualMachineScaleSetNetworkInterface", reflect.TypeOf((*MockInterface)(nil).GetVirtualMachineScaleSetNetworkInterface), arg0, arg1, arg2, arg3, arg4, arg5) +} + // List mocks base method. func (m *MockInterface) List(arg0 context.Context, arg1 string) ([]*armnetwork.Interface, error) { m.ctrl.T.Helper() diff --git a/pkg/azclient/snapshotclient/mock_snapshotclient/interface.go b/pkg/azclient/snapshotclient/mock_snapshotclient/interface.go index ae276eada7..f2dde2cedf 100644 --- a/pkg/azclient/snapshotclient/mock_snapshotclient/interface.go +++ b/pkg/azclient/snapshotclient/mock_snapshotclient/interface.go @@ -94,3 +94,18 @@ func (mr *MockInterfaceMockRecorder) Get(arg0, arg1, arg2 interface{}) *gomock.C mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockInterface)(nil).Get), arg0, arg1, arg2) } + +// List mocks base method. +func (m *MockInterface) List(arg0 context.Context, arg1 string) ([]*armcompute.Snapshot, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "List", arg0, arg1) + ret0, _ := ret[0].([]*armcompute.Snapshot) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// List indicates an expected call of List. +func (mr *MockInterfaceMockRecorder) List(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "List", reflect.TypeOf((*MockInterface)(nil).List), arg0, arg1) +} diff --git a/pkg/azclient/subnetclient/interface.go b/pkg/azclient/subnetclient/interface.go new file mode 100644 index 0000000000..e55a16c6f0 --- /dev/null +++ b/pkg/azclient/subnetclient/interface.go @@ -0,0 +1,35 @@ +/* +Copyright 2023 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. +*/ + +// +azure:enableclientgen:=true +package subnetclient + +import ( + armnetwork "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork/v3" + + "sigs.k8s.io/cloud-provider-azure/pkg/azclient/utils" +) + +// +azure:client:verbs=get;createorupdate;delete;list,resource=VirtualNetwork,subResource=Subnet,packageName=github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork/v3,packageAlias=armnetwork,clientName=SubnetsClient,expand=true +type Interface interface { + utils.SubResourceGetWithExpandFunc[armnetwork.Subnet] + + utils.SubResourceCreateOrUpdateFunc[armnetwork.Subnet] + + utils.SubResourceDeleteFunc[armnetwork.Subnet] + + utils.SubResourceListFunc[armnetwork.Subnet] +} diff --git a/pkg/azclient/subnetclient/mock_subnetclient/interface.go b/pkg/azclient/subnetclient/mock_subnetclient/interface.go new file mode 100644 index 0000000000..18355bfd78 --- /dev/null +++ b/pkg/azclient/subnetclient/mock_subnetclient/interface.go @@ -0,0 +1,111 @@ +// /* +// 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: sigs.k8s.io/cloud-provider-azure/pkg/azclient/subnetclient (interfaces: Interface) + +// Package mock_subnetclient is a generated GoMock package. +package mock_subnetclient + +import ( + context "context" + reflect "reflect" + + armnetwork "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork/v3" + gomock "github.com/golang/mock/gomock" +) + +// MockInterface is a mock of Interface interface. +type MockInterface struct { + ctrl *gomock.Controller + recorder *MockInterfaceMockRecorder +} + +// MockInterfaceMockRecorder is the mock recorder for MockInterface. +type MockInterfaceMockRecorder struct { + mock *MockInterface +} + +// NewMockInterface creates a new mock instance. +func NewMockInterface(ctrl *gomock.Controller) *MockInterface { + mock := &MockInterface{ctrl: ctrl} + mock.recorder = &MockInterfaceMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockInterface) EXPECT() *MockInterfaceMockRecorder { + return m.recorder +} + +// CreateOrUpdate mocks base method. +func (m *MockInterface) CreateOrUpdate(arg0 context.Context, arg1, arg2, arg3 string, arg4 armnetwork.Subnet) (*armnetwork.Subnet, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "CreateOrUpdate", arg0, arg1, arg2, arg3, arg4) + ret0, _ := ret[0].(*armnetwork.Subnet) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// CreateOrUpdate indicates an expected call of CreateOrUpdate. +func (mr *MockInterfaceMockRecorder) CreateOrUpdate(arg0, arg1, arg2, arg3, arg4 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateOrUpdate", reflect.TypeOf((*MockInterface)(nil).CreateOrUpdate), arg0, arg1, arg2, arg3, arg4) +} + +// Delete mocks base method. +func (m *MockInterface) Delete(arg0 context.Context, arg1, arg2, arg3 string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Delete", arg0, arg1, arg2, arg3) + ret0, _ := ret[0].(error) + return ret0 +} + +// Delete indicates an expected call of Delete. +func (mr *MockInterfaceMockRecorder) Delete(arg0, arg1, arg2, arg3 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Delete", reflect.TypeOf((*MockInterface)(nil).Delete), arg0, arg1, arg2, arg3) +} + +// Get mocks base method. +func (m *MockInterface) Get(arg0 context.Context, arg1, arg2, arg3 string, arg4 *string) (*armnetwork.Subnet, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Get", arg0, arg1, arg2, arg3, arg4) + ret0, _ := ret[0].(*armnetwork.Subnet) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Get indicates an expected call of Get. +func (mr *MockInterfaceMockRecorder) Get(arg0, arg1, arg2, arg3, arg4 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockInterface)(nil).Get), arg0, arg1, arg2, arg3, arg4) +} + +// List mocks base method. +func (m *MockInterface) List(arg0 context.Context, arg1, arg2 string) ([]*armnetwork.Subnet, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "List", arg0, arg1, arg2) + ret0, _ := ret[0].([]*armnetwork.Subnet) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// List indicates an expected call of List. +func (mr *MockInterfaceMockRecorder) List(arg0, arg1, arg2 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "List", reflect.TypeOf((*MockInterface)(nil).List), arg0, arg1, arg2) +} diff --git a/pkg/azclient/subnetclient/subnetclient_suite_test.go b/pkg/azclient/subnetclient/subnetclient_suite_test.go new file mode 100644 index 0000000000..c12f98a892 --- /dev/null +++ b/pkg/azclient/subnetclient/subnetclient_suite_test.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 client-gen. DO NOT EDIT. +package subnetclient + +import ( + "context" + "testing" + "time" + + "github.com/Azure/azure-sdk-for-go/sdk/azcore" + "github.com/Azure/azure-sdk-for-go/sdk/azcore/arm" + "github.com/Azure/azure-sdk-for-go/sdk/azcore/policy" + "github.com/Azure/azure-sdk-for-go/sdk/azcore/to" + "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources" + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + + "sigs.k8s.io/cloud-provider-azure/pkg/azclient/recording" +) + +func TestClient(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "Client Suite") +} + +var resourceGroupName = "aks-cit" +var resourceName = "testdisk" +var subscriptionID string +var location = "eastus" +var resourceGroupClient *armresources.ResourceGroupsClient +var err error +var recorder *recording.Recorder +var realClient Interface + +var _ = BeforeSuite(func(ctx context.Context) { + recorder, err = recording.NewRecorder("testdata/Subnet") + Expect(err).ToNot(HaveOccurred()) + subscriptionID = recorder.SubscriptionID() + Expect(err).NotTo(HaveOccurred()) + cred := recorder.TokenCredential() + resourceGroupClient, err = armresources.NewResourceGroupsClient(subscriptionID, cred, &arm.ClientOptions{ + ClientOptions: azcore.ClientOptions{ + Retry: policy.RetryOptions{ + MaxRetryDelay: 1 * time.Millisecond, + RetryDelay: 1 * time.Millisecond, + }, + Transport: recorder.HTTPClient(), + }, + }) + Expect(err).NotTo(HaveOccurred()) + realClient, err = New(subscriptionID, recorder.TokenCredential(), &arm.ClientOptions{ + ClientOptions: azcore.ClientOptions{ + Retry: policy.RetryOptions{ + MaxRetryDelay: 1 * time.Millisecond, + RetryDelay: 1 * time.Millisecond, + }, + Transport: recorder.HTTPClient(), + }, + }) + Expect(err).NotTo(HaveOccurred()) + _, err = resourceGroupClient.CreateOrUpdate( + ctx, + resourceGroupName, + armresources.ResourceGroup{ + Location: to.Ptr(location), + }, + nil) + Expect(err).NotTo(HaveOccurred()) +}) + +var _ = AfterSuite(func(ctx context.Context) { + pollerResp, err := resourceGroupClient.BeginDelete(ctx, resourceGroupName, nil) + Expect(err).NotTo(HaveOccurred()) + _, err = pollerResp.PollUntilDone(ctx, nil) + Expect(err).NotTo(HaveOccurred()) + + err = recorder.Stop() + Expect(err).ToNot(HaveOccurred()) +}) diff --git a/pkg/azclient/subnetclient/zz_generated_client.go b/pkg/azclient/subnetclient/zz_generated_client.go new file mode 100644 index 0000000000..96e4398541 --- /dev/null +++ b/pkg/azclient/subnetclient/zz_generated_client.go @@ -0,0 +1,90 @@ +// /* +// 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 client-gen. DO NOT EDIT. +package subnetclient + +import ( + "context" + + "github.com/Azure/azure-sdk-for-go/sdk/azcore" + "github.com/Azure/azure-sdk-for-go/sdk/azcore/arm" + armnetwork "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork/v3" + + "sigs.k8s.io/cloud-provider-azure/pkg/azclient/utils" +) + +type Client struct { + *armnetwork.SubnetsClient +} + +func New(subscriptionID string, credential azcore.TokenCredential, options *arm.ClientOptions) (Interface, error) { + if options == nil { + options = utils.GetDefaultOption() + } + + client, err := armnetwork.NewSubnetsClient(subscriptionID, credential, options) + if err != nil { + return nil, err + } + return &Client{client}, nil +} + +// Get gets the Subnet +func (client *Client) Get(ctx context.Context, resourceGroupName string, parentResourceName string, resourceName string, expand *string) (result *armnetwork.Subnet, rerr error) { + var ops *armnetwork.SubnetsClientGetOptions + if expand != nil { + ops = &armnetwork.SubnetsClientGetOptions{Expand: expand} + } + + resp, err := client.SubnetsClient.Get(ctx, resourceGroupName, parentResourceName, resourceName, ops) + if err != nil { + return nil, err + } + //handle statuscode + return &resp.Subnet, nil +} + +// CreateOrUpdate creates or updates a Subnet. +func (client *Client) CreateOrUpdate(ctx context.Context, resourceGroupName string, resourceName string, parentResourceName string, resource armnetwork.Subnet) (*armnetwork.Subnet, error) { + resp, err := utils.NewPollerWrapper(client.SubnetsClient.BeginCreateOrUpdate(ctx, resourceGroupName, resourceName, parentResourceName, resource, nil)).WaitforPollerResp(ctx) + if err != nil { + return nil, err + } + if resp != nil { + return &resp.Subnet, nil + } + return nil, nil +} + +// Delete deletes a Subnet by name. +func (client *Client) Delete(ctx context.Context, resourceGroupName string, parentResourceName string, resourceName string) error { + _, err := utils.NewPollerWrapper(client.BeginDelete(ctx, resourceGroupName, parentResourceName, resourceName, nil)).WaitforPollerResp(ctx) + return err +} + +// List gets a list of Subnet in the resource group. +func (client *Client) List(ctx context.Context, resourceGroupName string, parentResourceName string) (result []*armnetwork.Subnet, rerr error) { + pager := client.SubnetsClient.NewListPager(resourceGroupName, parentResourceName, nil) + for pager.More() { + nextResult, err := pager.NextPage(ctx) + if err != nil { + return nil, err + } + result = append(result, nextResult.Value...) + } + return result, nil +} diff --git a/pkg/azclient/utils/interface.go b/pkg/azclient/utils/interface.go index 34665530f1..02ca3b1209 100644 --- a/pkg/azclient/utils/interface.go +++ b/pkg/azclient/utils/interface.go @@ -23,22 +23,47 @@ type GetFunc[Type interface{}] interface { Get(ctx context.Context, resourceGroupName string, resourceName string) (result *Type, rerr error) } +// Get gets the service resource +type SubResourceGetFunc[Type interface{}] interface { + Get(ctx context.Context, resourceGroupName string, parentResourceName string, resourceName string) (result *Type, rerr error) +} + // Get gets the service resource type GetWithExpandFunc[Type interface{}] interface { Get(ctx context.Context, resourceGroupName string, resourceName string, expand *string) (result *Type, rerr error) } +// Get gets the service resource +type SubResourceGetWithExpandFunc[Type interface{}] interface { + Get(ctx context.Context, resourceGroupName string, parentResourceName string, resourceName string, expand *string) (result *Type, rerr error) +} + // List gets a list of service resource in the resource group. type ListFunc[Type interface{}] interface { List(ctx context.Context, resourceGroupName string) (result []*Type, rerr error) } +// List gets a list of service resource in the resource group. +type SubResourceListFunc[Type interface{}] interface { + List(ctx context.Context, resourceGroupName string, parentResourceName string) (result []*Type, rerr error) +} + // CreateOrUpdate creates or updates a service resource. type CreateOrUpdateFunc[Type interface{}] interface { CreateOrUpdate(ctx context.Context, resourceGroupName string, resourceName string, resourceParam Type) (*Type, error) } +// CreateOrUpdate creates or updates a service resource. +type SubResourceCreateOrUpdateFunc[Type interface{}] interface { + CreateOrUpdate(ctx context.Context, resourceGroupName string, parentResourceName string, resourceName string, resourceParam Type) (*Type, error) +} + // Delete deletes a service resource by name. type DeleteFunc[Type interface{}] interface { Delete(ctx context.Context, resourceGroupName string, resourceName string) error } + +// Delete deletes a service resource by name. +type SubResourceDeleteFunc[Type interface{}] interface { + Delete(ctx context.Context, resourceGroupName string, parentResourceName string, resourceName string) error +} diff --git a/pkg/azclient/virtualmachineclient/mock_virtualmachineclient/interface.go b/pkg/azclient/virtualmachineclient/mock_virtualmachineclient/interface.go index d0a47af2f0..8ede0baf2d 100644 --- a/pkg/azclient/virtualmachineclient/mock_virtualmachineclient/interface.go +++ b/pkg/azclient/virtualmachineclient/mock_virtualmachineclient/interface.go @@ -80,6 +80,21 @@ func (mr *MockInterfaceMockRecorder) Delete(arg0, arg1, arg2 interface{}) *gomoc return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Delete", reflect.TypeOf((*MockInterface)(nil).Delete), arg0, arg1, arg2) } +// Get mocks base method. +func (m *MockInterface) Get(arg0 context.Context, arg1, arg2 string, arg3 *string) (*armcompute.VirtualMachine, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Get", arg0, arg1, arg2, arg3) + ret0, _ := ret[0].(*armcompute.VirtualMachine) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Get indicates an expected call of Get. +func (mr *MockInterfaceMockRecorder) Get(arg0, arg1, arg2, arg3 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockInterface)(nil).Get), arg0, arg1, arg2, arg3) +} + // List mocks base method. func (m *MockInterface) List(arg0 context.Context, arg1 string) ([]*armcompute.VirtualMachine, error) { m.ctrl.T.Helper() diff --git a/pkg/azclient/virtualmachinescalesetvmclient/custom.go b/pkg/azclient/virtualmachinescalesetvmclient/custom.go new file mode 100644 index 0000000000..6400bfb9c0 --- /dev/null +++ b/pkg/azclient/virtualmachinescalesetvmclient/custom.go @@ -0,0 +1,84 @@ +/* +Copyright 2023 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 virtualmachinescalesetvmclient + +import ( + "context" + "errors" + "sync" + + armcompute "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v4" + "sigs.k8s.io/cloud-provider-azure/pkg/azclient/utils" +) + +// Update updates a VirtualMachine. +func (client *Client) Update(ctx context.Context, resourceGroupName string, VMScaleSetName string, instanceID string, parameters armcompute.VirtualMachineScaleSetVM) (*armcompute.VirtualMachineScaleSetVM, error) { + resp, err := client.UpdateAsync(ctx, resourceGroupName, VMScaleSetName, instanceID, parameters).WaitforPollerResp(ctx) + if err != nil { + return nil, err + } + if resp != nil { + return &resp.VirtualMachineScaleSetVM, nil + } + return nil, nil +} + +func (client *Client) UpdateAsync(ctx context.Context, resourceGroupName string, VMScaleSetName string, instanceID string, parameters armcompute.VirtualMachineScaleSetVM) *utils.PollerWrapper[armcompute.VirtualMachineScaleSetVMsClientUpdateResponse] { + return utils.NewPollerWrapper(client.VirtualMachineScaleSetVMsClient.BeginUpdate(ctx, resourceGroupName, VMScaleSetName, instanceID, parameters, nil)) +} + +func UpdateVMsInBatch(ctx context.Context, client *Client, resourceGroupName string, VMScaleSetName string, instances map[string]armcompute.VirtualMachineScaleSetVM, batchSize int) error { + if batchSize <= 0 { + return errors.New("batchSize should be greater than 0") + } + + if batchSize == 1 { + for instanceID, vm := range instances { + if _, err := client.Update(ctx, resourceGroupName, VMScaleSetName, instanceID, vm); err != nil { + return err + } + } + return nil + } + + cocurrentFence := make(chan struct{}, batchSize) + errChannel := make(chan error, len(instances)) + var workerGroup sync.WaitGroup + var err error + for instanceID, vm := range instances { + select { + case cocurrentFence <- struct{}{}: + workerGroup.Add(1) + go func(instanceID string, vm armcompute.VirtualMachineScaleSetVM) { + defer workerGroup.Done() + defer func() { <-cocurrentFence }() + _, err := client.Update(ctx, resourceGroupName, VMScaleSetName, instanceID, vm) + if err != nil { + errChannel <- err + return + } + }(instanceID, vm) + case err = <-errChannel: + if err != nil { + break + } + } + } + workerGroup.Wait() + close(cocurrentFence) + close(errChannel) + return err +} diff --git a/pkg/azclient/virtualmachinescalesetvmclient/interface.go b/pkg/azclient/virtualmachinescalesetvmclient/interface.go new file mode 100644 index 0000000000..0b0c28ddaa --- /dev/null +++ b/pkg/azclient/virtualmachinescalesetvmclient/interface.go @@ -0,0 +1,37 @@ +/* +Copyright 2023 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. +*/ + +// +azure:enableclientgen:=true +package virtualmachinescalesetvmclient + +import ( + "context" + + armcompute "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v4" + + "sigs.k8s.io/cloud-provider-azure/pkg/azclient/utils" +) + +// +azure:client:verbs=get;delete;list,resource=VirtualMachineScaleSet,subResource=VirtualMachineScaleSetVM,packageName=github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v4,packageAlias=armcompute,clientName=VirtualMachineScaleSetVMsClient,expand=false +type Interface interface { + utils.SubResourceGetFunc[armcompute.VirtualMachineScaleSetVM] + utils.SubResourceDeleteFunc[armcompute.VirtualMachineScaleSetVM] + utils.SubResourceListFunc[armcompute.VirtualMachineScaleSetVM] + // Update updates a VirtualMachineScaleSetVM. + Update(ctx context.Context, resourceGroupName string, VMScaleSetName string, instanceID string, parameters armcompute.VirtualMachineScaleSetVM) (*armcompute.VirtualMachineScaleSetVM, error) + // UpdateAsync updates a VirtualMachineScaleSetVM asynchronously + UpdateAsync(ctx context.Context, resourceGroupName string, VMScaleSetName string, instanceID string, parameters armcompute.VirtualMachineScaleSetVM) *utils.PollerWrapper[armcompute.VirtualMachineScaleSetVMsClientUpdateResponse] +} diff --git a/pkg/azclient/virtualmachinescalesetvmclient/mock_virtualmachinescalesetvmclient/interface.go b/pkg/azclient/virtualmachinescalesetvmclient/mock_virtualmachinescalesetvmclient/interface.go new file mode 100644 index 0000000000..ec9852c0cc --- /dev/null +++ b/pkg/azclient/virtualmachinescalesetvmclient/mock_virtualmachinescalesetvmclient/interface.go @@ -0,0 +1,96 @@ +// /* +// 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: sigs.k8s.io/cloud-provider-azure/pkg/azclient/virtualmachinescalesetvmclient (interfaces: Interface) + +// Package mock_virtualmachinescalesetvmclient is a generated GoMock package. +package mock_virtualmachinescalesetvmclient + +import ( + context "context" + reflect "reflect" + + armcompute "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v4" + gomock "github.com/golang/mock/gomock" +) + +// MockInterface is a mock of Interface interface. +type MockInterface struct { + ctrl *gomock.Controller + recorder *MockInterfaceMockRecorder +} + +// MockInterfaceMockRecorder is the mock recorder for MockInterface. +type MockInterfaceMockRecorder struct { + mock *MockInterface +} + +// NewMockInterface creates a new mock instance. +func NewMockInterface(ctrl *gomock.Controller) *MockInterface { + mock := &MockInterface{ctrl: ctrl} + mock.recorder = &MockInterfaceMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockInterface) EXPECT() *MockInterfaceMockRecorder { + return m.recorder +} + +// Delete mocks base method. +func (m *MockInterface) Delete(arg0 context.Context, arg1, arg2, arg3 string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Delete", arg0, arg1, arg2, arg3) + ret0, _ := ret[0].(error) + return ret0 +} + +// Delete indicates an expected call of Delete. +func (mr *MockInterfaceMockRecorder) Delete(arg0, arg1, arg2, arg3 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Delete", reflect.TypeOf((*MockInterface)(nil).Delete), arg0, arg1, arg2, arg3) +} + +// Get mocks base method. +func (m *MockInterface) Get(arg0 context.Context, arg1, arg2, arg3 string) (*armcompute.VirtualMachineScaleSetVM, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Get", arg0, arg1, arg2, arg3) + ret0, _ := ret[0].(*armcompute.VirtualMachineScaleSetVM) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Get indicates an expected call of Get. +func (mr *MockInterfaceMockRecorder) Get(arg0, arg1, arg2, arg3 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockInterface)(nil).Get), arg0, arg1, arg2, arg3) +} + +// List mocks base method. +func (m *MockInterface) List(arg0 context.Context, arg1, arg2 string) ([]*armcompute.VirtualMachineScaleSetVM, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "List", arg0, arg1, arg2) + ret0, _ := ret[0].([]*armcompute.VirtualMachineScaleSetVM) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// List indicates an expected call of List. +func (mr *MockInterfaceMockRecorder) List(arg0, arg1, arg2 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "List", reflect.TypeOf((*MockInterface)(nil).List), arg0, arg1, arg2) +} diff --git a/pkg/azclient/virtualmachinescalesetvmclient/virtualmachinescalesetvmclient_suite_test.go b/pkg/azclient/virtualmachinescalesetvmclient/virtualmachinescalesetvmclient_suite_test.go new file mode 100644 index 0000000000..ddcd850743 --- /dev/null +++ b/pkg/azclient/virtualmachinescalesetvmclient/virtualmachinescalesetvmclient_suite_test.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 client-gen. DO NOT EDIT. +package virtualmachinescalesetvmclient + +import ( + "context" + "testing" + "time" + + "github.com/Azure/azure-sdk-for-go/sdk/azcore" + "github.com/Azure/azure-sdk-for-go/sdk/azcore/arm" + "github.com/Azure/azure-sdk-for-go/sdk/azcore/policy" + "github.com/Azure/azure-sdk-for-go/sdk/azcore/to" + "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources" + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + + "sigs.k8s.io/cloud-provider-azure/pkg/azclient/recording" +) + +func TestClient(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "Client Suite") +} + +var resourceGroupName = "aks-cit" +var resourceName = "testdisk" +var subscriptionID string +var location = "eastus" +var resourceGroupClient *armresources.ResourceGroupsClient +var err error +var recorder *recording.Recorder +var realClient Interface + +var _ = BeforeSuite(func(ctx context.Context) { + recorder, err = recording.NewRecorder("testdata/VirtualMachineScaleSetVM") + Expect(err).ToNot(HaveOccurred()) + subscriptionID = recorder.SubscriptionID() + Expect(err).NotTo(HaveOccurred()) + cred := recorder.TokenCredential() + resourceGroupClient, err = armresources.NewResourceGroupsClient(subscriptionID, cred, &arm.ClientOptions{ + ClientOptions: azcore.ClientOptions{ + Retry: policy.RetryOptions{ + MaxRetryDelay: 1 * time.Millisecond, + RetryDelay: 1 * time.Millisecond, + }, + Transport: recorder.HTTPClient(), + }, + }) + Expect(err).NotTo(HaveOccurred()) + realClient, err = New(subscriptionID, recorder.TokenCredential(), &arm.ClientOptions{ + ClientOptions: azcore.ClientOptions{ + Retry: policy.RetryOptions{ + MaxRetryDelay: 1 * time.Millisecond, + RetryDelay: 1 * time.Millisecond, + }, + Transport: recorder.HTTPClient(), + }, + }) + Expect(err).NotTo(HaveOccurred()) + _, err = resourceGroupClient.CreateOrUpdate( + ctx, + resourceGroupName, + armresources.ResourceGroup{ + Location: to.Ptr(location), + }, + nil) + Expect(err).NotTo(HaveOccurred()) +}) + +var _ = AfterSuite(func(ctx context.Context) { + pollerResp, err := resourceGroupClient.BeginDelete(ctx, resourceGroupName, nil) + Expect(err).NotTo(HaveOccurred()) + _, err = pollerResp.PollUntilDone(ctx, nil) + Expect(err).NotTo(HaveOccurred()) + + err = recorder.Stop() + Expect(err).ToNot(HaveOccurred()) +}) diff --git a/pkg/azclient/virtualmachinescalesetvmclient/zz_generated_client.go b/pkg/azclient/virtualmachinescalesetvmclient/zz_generated_client.go new file mode 100644 index 0000000000..f3bf3da417 --- /dev/null +++ b/pkg/azclient/virtualmachinescalesetvmclient/zz_generated_client.go @@ -0,0 +1,75 @@ +// /* +// 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 client-gen. DO NOT EDIT. +package virtualmachinescalesetvmclient + +import ( + "context" + + "github.com/Azure/azure-sdk-for-go/sdk/azcore" + "github.com/Azure/azure-sdk-for-go/sdk/azcore/arm" + armcompute "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v4" + + "sigs.k8s.io/cloud-provider-azure/pkg/azclient/utils" +) + +type Client struct { + *armcompute.VirtualMachineScaleSetVMsClient +} + +func New(subscriptionID string, credential azcore.TokenCredential, options *arm.ClientOptions) (Interface, error) { + if options == nil { + options = utils.GetDefaultOption() + } + + client, err := armcompute.NewVirtualMachineScaleSetVMsClient(subscriptionID, credential, options) + if err != nil { + return nil, err + } + return &Client{client}, nil +} + +// Get gets the VirtualMachineScaleSetVM +func (client *Client) Get(ctx context.Context, resourceGroupName string, parentResourceName string, resourceName string) (result *armcompute.VirtualMachineScaleSetVM, rerr error) { + var ops *armcompute.VirtualMachineScaleSetVMsClientGetOptions + + resp, err := client.VirtualMachineScaleSetVMsClient.Get(ctx, resourceGroupName, parentResourceName, resourceName, ops) + if err != nil { + return nil, err + } + //handle statuscode + return &resp.VirtualMachineScaleSetVM, nil +} + +// Delete deletes a VirtualMachineScaleSetVM by name. +func (client *Client) Delete(ctx context.Context, resourceGroupName string, parentResourceName string, resourceName string) error { + _, err := utils.NewPollerWrapper(client.BeginDelete(ctx, resourceGroupName, parentResourceName, resourceName, nil)).WaitforPollerResp(ctx) + return err +} + +// List gets a list of VirtualMachineScaleSetVM in the resource group. +func (client *Client) List(ctx context.Context, resourceGroupName string, parentResourceName string) (result []*armcompute.VirtualMachineScaleSetVM, rerr error) { + pager := client.VirtualMachineScaleSetVMsClient.NewListPager(resourceGroupName, parentResourceName, nil) + for pager.More() { + nextResult, err := pager.NextPage(ctx) + if err != nil { + return nil, err + } + result = append(result, nextResult.Value...) + } + return result, nil +}