From b192630522f4167302748409f4e9f710202fe98c Mon Sep 17 00:00:00 2001 From: Pengfei Ni Date: Wed, 4 Nov 2020 15:50:33 +0800 Subject: [PATCH] Fix paging issues when Azure API returns empty values with non-empty nextLink --- .../azure_containerserviceclient.go | 10 +++++-- .../azure_containerserviceclient_test.go | 29 ++++++++++++++++++ .../azure_deploymentclient.go | 10 +++++-- .../azure_deploymentclient_test.go | 29 ++++++++++++++++++ .../clients/diskclient/azure_diskclient.go | 10 +++++-- .../azure_loadbalancerclient.go | 10 +++++-- .../azure_loadbalancerclient_test.go | 30 +++++++++++++++++++ .../publicipclient/azure_publicipclient.go | 10 +++++-- .../azure_publicipclient_test.go | 29 ++++++++++++++++++ .../azure_securitygroupclient.go | 10 +++++-- .../azure_securitygroupclient_test.go | 29 ++++++++++++++++++ .../snapshotclient/azure_snapshotclient.go | 10 +++++-- .../azure_snapshotclient_test.go | 29 ++++++++++++++++++ .../azure_storageaccountclient.go | 10 +++++-- .../subnetclient/azure_subnetclient.go | 10 +++++-- .../subnetclient/azure_subnetclient_test.go | 29 ++++++++++++++++++ .../azure/clients/vmclient/azure_vmclient.go | 10 +++++-- .../clients/vmclient/azure_vmclient_test.go | 29 ++++++++++++++++++ .../clients/vmssclient/azure_vmssclient.go | 10 +++++-- .../vmssclient/azure_vmssclient_test.go | 29 ++++++++++++++++++ .../vmssvmclient/azure_vmssvmclient.go | 10 +++++-- .../vmssvmclient/azure_vmssvmclient_test.go | 29 ++++++++++++++++++ 22 files changed, 387 insertions(+), 24 deletions(-) diff --git a/staging/src/k8s.io/legacy-cloud-providers/azure/clients/containerserviceclient/azure_containerserviceclient.go b/staging/src/k8s.io/legacy-cloud-providers/azure/clients/containerserviceclient/azure_containerserviceclient.go index 0681a275e0c..e81065add66 100644 --- a/staging/src/k8s.io/legacy-cloud-providers/azure/clients/containerserviceclient/azure_containerserviceclient.go +++ b/staging/src/k8s.io/legacy-cloud-providers/azure/clients/containerserviceclient/azure_containerserviceclient.go @@ -192,8 +192,14 @@ func (c *Client) listManagedCluster(ctx context.Context, resourceGroupName strin return result, retry.GetError(resp, err) } - for page.NotDone() { - result = append(result, *page.Response().Value...) + for { + result = append(result, page.Values()...) + + // Abort the loop when there's no nextLink in the response. + if to.String(page.Response().NextLink) == "" { + break + } + if err = page.NextWithContext(ctx); err != nil { klog.V(5).Infof("Received error in %s: resourceID: %s, error: %s", "managedcluster.list.next", resourceID, err) return result, retry.GetError(page.Response().Response.Response, err) diff --git a/staging/src/k8s.io/legacy-cloud-providers/azure/clients/containerserviceclient/azure_containerserviceclient_test.go b/staging/src/k8s.io/legacy-cloud-providers/azure/clients/containerserviceclient/azure_containerserviceclient_test.go index fed183167fc..403e2559c4a 100644 --- a/staging/src/k8s.io/legacy-cloud-providers/azure/clients/containerserviceclient/azure_containerserviceclient_test.go +++ b/staging/src/k8s.io/legacy-cloud-providers/azure/clients/containerserviceclient/azure_containerserviceclient_test.go @@ -373,6 +373,35 @@ func TestListWithListResponderError(t *testing.T) { assert.Equal(t, 0, len(result)) } +func TestListWithNextPage(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + resourceID := "/subscriptions/subscriptionID/resourceGroups/rg/providers/Microsoft.ContainerService/managedClusters" + armClient := mockarmclient.NewMockInterface(ctrl) + mcList := []containerservice.ManagedCluster{getTestManagedCluster("cluster"), getTestManagedCluster("cluster1"), getTestManagedCluster("cluster2")} + responseBody, err := json.Marshal(containerservice.ManagedClusterListResult{Value: &mcList, NextLink: to.StringPtr("nextLink")}) + assert.NoError(t, err) + pagedResponse, err := json.Marshal(containerservice.ManagedClusterListResult{Value: &mcList}) + assert.NoError(t, err) + armClient.EXPECT().PrepareGetRequest(gomock.Any(), gomock.Any()).Return(&http.Request{}, nil) + armClient.EXPECT().Send(gomock.Any(), gomock.Any()).Return( + &http.Response{ + StatusCode: http.StatusOK, + Body: ioutil.NopCloser(bytes.NewReader(pagedResponse)), + }, nil) + armClient.EXPECT().GetResource(gomock.Any(), resourceID, "").Return( + &http.Response{ + StatusCode: http.StatusOK, + Body: ioutil.NopCloser(bytes.NewReader(responseBody)), + }, nil).Times(1) + armClient.EXPECT().CloseResponse(gomock.Any(), gomock.Any()).Times(2) + mcClient := getTestManagedClusterClient(armClient) + result, rerr := mcClient.List(context.TODO(), "rg") + assert.Nil(t, rerr) + assert.Equal(t, 6, len(result)) +} + func TestListNeverRateLimiter(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() diff --git a/staging/src/k8s.io/legacy-cloud-providers/azure/clients/deploymentclient/azure_deploymentclient.go b/staging/src/k8s.io/legacy-cloud-providers/azure/clients/deploymentclient/azure_deploymentclient.go index b9e00740eca..830f48dcc55 100644 --- a/staging/src/k8s.io/legacy-cloud-providers/azure/clients/deploymentclient/azure_deploymentclient.go +++ b/staging/src/k8s.io/legacy-cloud-providers/azure/clients/deploymentclient/azure_deploymentclient.go @@ -192,8 +192,14 @@ func (c *Client) listDeployment(ctx context.Context, resourceGroupName string) ( return result, retry.GetError(resp, err) } - for page.NotDone() { - result = append(result, *page.Response().Value...) + for { + result = append(result, page.Values()...) + + // Abort the loop when there's no nextLink in the response. + if to.String(page.Response().NextLink) == "" { + break + } + if err = page.NextWithContext(ctx); err != nil { klog.V(5).Infof("Received error in %s: resourceID: %s, error: %s", "deployment.list.next", resourceID, err) return result, retry.GetError(page.Response().Response.Response, err) diff --git a/staging/src/k8s.io/legacy-cloud-providers/azure/clients/deploymentclient/azure_deploymentclient_test.go b/staging/src/k8s.io/legacy-cloud-providers/azure/clients/deploymentclient/azure_deploymentclient_test.go index cad9d741d24..2c0c10d1b74 100644 --- a/staging/src/k8s.io/legacy-cloud-providers/azure/clients/deploymentclient/azure_deploymentclient_test.go +++ b/staging/src/k8s.io/legacy-cloud-providers/azure/clients/deploymentclient/azure_deploymentclient_test.go @@ -371,6 +371,35 @@ func TestListWithListResponderError(t *testing.T) { assert.Equal(t, 0, len(result)) } +func TestListWithNextPage(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + resourceID := "/subscriptions/subscriptionID/resourceGroups/rg/providers/Microsoft.Resources/deployments" + armClient := mockarmclient.NewMockInterface(ctrl) + dpList := []resources.DeploymentExtended{getTestDeploymentExtended("dep"), getTestDeploymentExtended("dep1"), getTestDeploymentExtended("dep2")} + partialResponse, err := json.Marshal(resources.DeploymentListResult{Value: &dpList, NextLink: to.StringPtr("nextLink")}) + assert.NoError(t, err) + pagedResponse, err := json.Marshal(resources.DeploymentListResult{Value: &dpList}) + assert.NoError(t, err) + armClient.EXPECT().PrepareGetRequest(gomock.Any(), gomock.Any()).Return(&http.Request{}, nil) + armClient.EXPECT().Send(gomock.Any(), gomock.Any()).Return( + &http.Response{ + StatusCode: http.StatusOK, + Body: ioutil.NopCloser(bytes.NewReader(pagedResponse)), + }, nil) + armClient.EXPECT().GetResource(gomock.Any(), resourceID, "").Return( + &http.Response{ + StatusCode: http.StatusOK, + Body: ioutil.NopCloser(bytes.NewReader(partialResponse)), + }, nil).Times(1) + armClient.EXPECT().CloseResponse(gomock.Any(), gomock.Any()).Times(2) + dpClient := getTestDeploymentClient(armClient) + result, rerr := dpClient.List(context.TODO(), "rg") + assert.Nil(t, rerr) + assert.Equal(t, 6, len(result)) +} + func TestListNeverRateLimiter(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() diff --git a/staging/src/k8s.io/legacy-cloud-providers/azure/clients/diskclient/azure_diskclient.go b/staging/src/k8s.io/legacy-cloud-providers/azure/clients/diskclient/azure_diskclient.go index 3ac28f1f75a..d0de9c5611f 100644 --- a/staging/src/k8s.io/legacy-cloud-providers/azure/clients/diskclient/azure_diskclient.go +++ b/staging/src/k8s.io/legacy-cloud-providers/azure/clients/diskclient/azure_diskclient.go @@ -341,8 +341,14 @@ func (c *Client) ListByResourceGroup(ctx context.Context, resourceGroupName stri return result, retry.GetError(resp, err) } - for page.NotDone() { - result = append(result, *page.Response().Value...) + for { + result = append(result, page.Values()...) + + // Abort the loop when there's no nextLink in the response. + if to.String(page.Response().NextLink) == "" { + break + } + if err = page.NextWithContext(ctx); err != nil { klog.V(5).Infof("Received error in %s: resourceID: %s, error: %s", "disk.list.next", resourceID, err) return result, retry.GetError(page.Response().Response.Response, err) diff --git a/staging/src/k8s.io/legacy-cloud-providers/azure/clients/loadbalancerclient/azure_loadbalancerclient.go b/staging/src/k8s.io/legacy-cloud-providers/azure/clients/loadbalancerclient/azure_loadbalancerclient.go index 8ef0f5fbedf..eac4bcd88d2 100644 --- a/staging/src/k8s.io/legacy-cloud-providers/azure/clients/loadbalancerclient/azure_loadbalancerclient.go +++ b/staging/src/k8s.io/legacy-cloud-providers/azure/clients/loadbalancerclient/azure_loadbalancerclient.go @@ -192,8 +192,14 @@ func (c *Client) listLB(ctx context.Context, resourceGroupName string) ([]networ return result, retry.GetError(resp, err) } - for page.NotDone() { - result = append(result, *page.Response().Value...) + for { + result = append(result, page.Values()...) + + // Abort the loop when there's no nextLink in the response. + if to.String(page.Response().NextLink) == "" { + break + } + if err = page.NextWithContext(ctx); err != nil { klog.V(5).Infof("Received error in %s: resourceID: %s, error: %s", "loadbalancer.list.next", resourceID, err) return result, retry.GetError(page.Response().Response.Response, err) diff --git a/staging/src/k8s.io/legacy-cloud-providers/azure/clients/loadbalancerclient/azure_loadbalancerclient_test.go b/staging/src/k8s.io/legacy-cloud-providers/azure/clients/loadbalancerclient/azure_loadbalancerclient_test.go index 1574bdb0fb7..e55fbbfa5f1 100644 --- a/staging/src/k8s.io/legacy-cloud-providers/azure/clients/loadbalancerclient/azure_loadbalancerclient_test.go +++ b/staging/src/k8s.io/legacy-cloud-providers/azure/clients/loadbalancerclient/azure_loadbalancerclient_test.go @@ -165,6 +165,36 @@ func TestList(t *testing.T) { assert.Equal(t, throttleErr, rerr) } +func TestListWithNextPage(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + resourceID := "/subscriptions/subscriptionID/resourceGroups/rg/providers/Microsoft.Network/loadBalancers" + armClient := mockarmclient.NewMockInterface(ctrl) + lbList := []network.LoadBalancer{getTestLoadBalancer("lb1"), getTestLoadBalancer("lb2"), getTestLoadBalancer("lb3")} + partialResponse, err := json.Marshal(network.LoadBalancerListResult{Value: &lbList, NextLink: to.StringPtr("nextLink")}) + assert.NoError(t, err) + pagedResponse, err := json.Marshal(network.LoadBalancerListResult{Value: &lbList}) + assert.NoError(t, err) + armClient.EXPECT().PrepareGetRequest(gomock.Any(), gomock.Any()).Return(&http.Request{}, nil) + armClient.EXPECT().Send(gomock.Any(), gomock.Any()).Return( + &http.Response{ + StatusCode: http.StatusOK, + Body: ioutil.NopCloser(bytes.NewReader(pagedResponse)), + }, nil) + armClient.EXPECT().GetResource(gomock.Any(), resourceID, "").Return( + &http.Response{ + StatusCode: http.StatusOK, + Body: ioutil.NopCloser(bytes.NewReader(partialResponse)), + }, nil).Times(1) + armClient.EXPECT().CloseResponse(gomock.Any(), gomock.Any()).Times(2) + + lbClient := getTestLoadBalancerClient(armClient) + result, rerr := lbClient.List(context.TODO(), "rg") + assert.Nil(t, rerr) + assert.Equal(t, 6, len(result)) +} + func TestListNextResultsMultiPages(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() diff --git a/staging/src/k8s.io/legacy-cloud-providers/azure/clients/publicipclient/azure_publicipclient.go b/staging/src/k8s.io/legacy-cloud-providers/azure/clients/publicipclient/azure_publicipclient.go index c15b9ff2eaa..56ed0ef56c4 100644 --- a/staging/src/k8s.io/legacy-cloud-providers/azure/clients/publicipclient/azure_publicipclient.go +++ b/staging/src/k8s.io/legacy-cloud-providers/azure/clients/publicipclient/azure_publicipclient.go @@ -265,8 +265,14 @@ func (c *Client) listPublicIPAddress(ctx context.Context, resourceGroupName stri return result, retry.GetError(resp, err) } - for page.NotDone() { - result = append(result, *page.Response().Value...) + for { + result = append(result, page.Values()...) + + // Abort the loop when there's no nextLink in the response. + if to.String(page.Response().NextLink) == "" { + break + } + if err = page.NextWithContext(ctx); err != nil { klog.V(5).Infof("Received error in %s: resourceID: %s, error: %s", "publicip.list.next", resourceID, err) return result, retry.GetError(page.Response().Response.Response, err) diff --git a/staging/src/k8s.io/legacy-cloud-providers/azure/clients/publicipclient/azure_publicipclient_test.go b/staging/src/k8s.io/legacy-cloud-providers/azure/clients/publicipclient/azure_publicipclient_test.go index ebbccbc9eb1..46c4e765f78 100644 --- a/staging/src/k8s.io/legacy-cloud-providers/azure/clients/publicipclient/azure_publicipclient_test.go +++ b/staging/src/k8s.io/legacy-cloud-providers/azure/clients/publicipclient/azure_publicipclient_test.go @@ -431,6 +431,35 @@ func TestListWithListResponderError(t *testing.T) { assert.Equal(t, 0, len(result)) } +func TestListWithNextPage(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + resourceID := "/subscriptions/subscriptionID/resourceGroups/rg/providers/Microsoft.Network/publicIPAddresses" + armClient := mockarmclient.NewMockInterface(ctrl) + pipList := []network.PublicIPAddress{getTestPublicIPAddress("pip1"), getTestPublicIPAddress("pip2"), getTestPublicIPAddress("pip3")} + partialResponse, err := json.Marshal(network.PublicIPAddressListResult{Value: &pipList, NextLink: to.StringPtr("nextLink")}) + assert.NoError(t, err) + pagedResponse, err := json.Marshal(network.PublicIPAddressListResult{Value: &pipList}) + assert.NoError(t, err) + armClient.EXPECT().PrepareGetRequest(gomock.Any(), gomock.Any()).Return(&http.Request{}, nil) + armClient.EXPECT().Send(gomock.Any(), gomock.Any()).Return( + &http.Response{ + StatusCode: http.StatusOK, + Body: ioutil.NopCloser(bytes.NewReader(pagedResponse)), + }, nil) + armClient.EXPECT().GetResource(gomock.Any(), resourceID, "").Return( + &http.Response{ + StatusCode: http.StatusOK, + Body: ioutil.NopCloser(bytes.NewReader(partialResponse)), + }, nil).Times(1) + armClient.EXPECT().CloseResponse(gomock.Any(), gomock.Any()).Times(2) + pipClient := getTestPublicIPAddressClient(armClient) + result, rerr := pipClient.List(context.TODO(), "rg") + assert.Nil(t, rerr) + assert.Equal(t, 6, len(result)) +} + func TestListNeverRateLimiter(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() diff --git a/staging/src/k8s.io/legacy-cloud-providers/azure/clients/securitygroupclient/azure_securitygroupclient.go b/staging/src/k8s.io/legacy-cloud-providers/azure/clients/securitygroupclient/azure_securitygroupclient.go index 9071708d3fa..60cae58429e 100644 --- a/staging/src/k8s.io/legacy-cloud-providers/azure/clients/securitygroupclient/azure_securitygroupclient.go +++ b/staging/src/k8s.io/legacy-cloud-providers/azure/clients/securitygroupclient/azure_securitygroupclient.go @@ -192,8 +192,14 @@ func (c *Client) listSecurityGroup(ctx context.Context, resourceGroupName string return result, retry.GetError(resp, err) } - for page.NotDone() { - result = append(result, *page.Response().Value...) + for { + result = append(result, page.Values()...) + + // Abort the loop when there's no nextLink in the response. + if to.String(page.Response().NextLink) == "" { + break + } + if err = page.NextWithContext(ctx); err != nil { klog.V(5).Infof("Received error in %s: resourceID: %s, error: %s", "securitygroup.list.next", resourceID, err) return result, retry.GetError(page.Response().Response.Response, err) diff --git a/staging/src/k8s.io/legacy-cloud-providers/azure/clients/securitygroupclient/azure_securitygroupclient_test.go b/staging/src/k8s.io/legacy-cloud-providers/azure/clients/securitygroupclient/azure_securitygroupclient_test.go index ef94917b3ef..3027fb60c5a 100644 --- a/staging/src/k8s.io/legacy-cloud-providers/azure/clients/securitygroupclient/azure_securitygroupclient_test.go +++ b/staging/src/k8s.io/legacy-cloud-providers/azure/clients/securitygroupclient/azure_securitygroupclient_test.go @@ -331,6 +331,35 @@ func TestListWithListResponderError(t *testing.T) { assert.Equal(t, 0, len(result)) } +func TestListWithNextPage(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + resourceID := "/subscriptions/subscriptionID/resourceGroups/rg/providers/Microsoft.Network/networkSecurityGroups" + armClient := mockarmclient.NewMockInterface(ctrl) + nsgList := []network.SecurityGroup{getTestSecurityGroup("nsg1"), getTestSecurityGroup("nsg2"), getTestSecurityGroup("nsg3")} + partialResponse, err := json.Marshal(network.SecurityGroupListResult{Value: &nsgList, NextLink: to.StringPtr("nextLink")}) + assert.NoError(t, err) + pagedResponse, err := json.Marshal(network.SecurityGroupListResult{Value: &nsgList}) + assert.NoError(t, err) + armClient.EXPECT().PrepareGetRequest(gomock.Any(), gomock.Any()).Return(&http.Request{}, nil) + armClient.EXPECT().Send(gomock.Any(), gomock.Any()).Return( + &http.Response{ + StatusCode: http.StatusOK, + Body: ioutil.NopCloser(bytes.NewReader(pagedResponse)), + }, nil) + armClient.EXPECT().GetResource(gomock.Any(), resourceID, "").Return( + &http.Response{ + StatusCode: http.StatusOK, + Body: ioutil.NopCloser(bytes.NewReader(partialResponse)), + }, nil).Times(1) + armClient.EXPECT().CloseResponse(gomock.Any(), gomock.Any()).Times(2) + nsgClient := getTestSecurityGroupClient(armClient) + result, rerr := nsgClient.List(context.TODO(), "rg") + assert.Nil(t, rerr) + assert.Equal(t, 6, len(result)) +} + func TestListNeverRateLimiter(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() diff --git a/staging/src/k8s.io/legacy-cloud-providers/azure/clients/snapshotclient/azure_snapshotclient.go b/staging/src/k8s.io/legacy-cloud-providers/azure/clients/snapshotclient/azure_snapshotclient.go index 56cf31f6a24..300cec5a9dc 100644 --- a/staging/src/k8s.io/legacy-cloud-providers/azure/clients/snapshotclient/azure_snapshotclient.go +++ b/staging/src/k8s.io/legacy-cloud-providers/azure/clients/snapshotclient/azure_snapshotclient.go @@ -303,8 +303,14 @@ func (c *Client) listSnapshotsByResourceGroup(ctx context.Context, resourceGroup return result, retry.GetError(resp, err) } - for page.NotDone() { - result = append(result, *page.Response().Value...) + for { + result = append(result, page.Values()...) + + // Abort the loop when there's no nextLink in the response. + if to.String(page.Response().NextLink) == "" { + break + } + if err = page.NextWithContext(ctx); err != nil { klog.V(5).Infof("Received error in %s: resourceID: %s, error: %s", "snapshot.list.next", resourceID, err) return result, retry.GetError(page.Response().Response.Response, err) diff --git a/staging/src/k8s.io/legacy-cloud-providers/azure/clients/snapshotclient/azure_snapshotclient_test.go b/staging/src/k8s.io/legacy-cloud-providers/azure/clients/snapshotclient/azure_snapshotclient_test.go index 77b0949a62e..d460604af06 100644 --- a/staging/src/k8s.io/legacy-cloud-providers/azure/clients/snapshotclient/azure_snapshotclient_test.go +++ b/staging/src/k8s.io/legacy-cloud-providers/azure/clients/snapshotclient/azure_snapshotclient_test.go @@ -295,6 +295,35 @@ func TestListByResourceGroupWithListResponderError(t *testing.T) { assert.Equal(t, 0, len(result)) } +func TestListWithNextPage(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + resourceID := "/subscriptions/subscriptionID/resourceGroups/rg/providers/Microsoft.Compute/snapshots" + armClient := mockarmclient.NewMockInterface(ctrl) + snList := []compute.Snapshot{getTestSnapshot("sn1"), getTestSnapshot("sn2"), getTestSnapshot("sn3")} + partialResponse, err := json.Marshal(compute.SnapshotList{Value: &snList, NextLink: to.StringPtr("nextLink")}) + assert.NoError(t, err) + pagedResponse, err := json.Marshal(compute.SnapshotList{Value: &snList}) + assert.NoError(t, err) + armClient.EXPECT().PrepareGetRequest(gomock.Any(), gomock.Any()).Return(&http.Request{}, nil) + armClient.EXPECT().Send(gomock.Any(), gomock.Any()).Return( + &http.Response{ + StatusCode: http.StatusOK, + Body: ioutil.NopCloser(bytes.NewReader(pagedResponse)), + }, nil) + armClient.EXPECT().GetResource(gomock.Any(), resourceID, "").Return( + &http.Response{ + StatusCode: http.StatusOK, + Body: ioutil.NopCloser(bytes.NewReader(partialResponse)), + }, nil).Times(1) + armClient.EXPECT().CloseResponse(gomock.Any(), gomock.Any()).Times(2) + snClient := getTestSnapshotClient(armClient) + result, rerr := snClient.ListByResourceGroup(context.TODO(), "rg") + assert.Nil(t, rerr) + assert.Equal(t, 6, len(result)) +} + func TestListByResourceGroupNeverRateLimiter(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() diff --git a/staging/src/k8s.io/legacy-cloud-providers/azure/clients/storageaccountclient/azure_storageaccountclient.go b/staging/src/k8s.io/legacy-cloud-providers/azure/clients/storageaccountclient/azure_storageaccountclient.go index 1f1558980c9..856ca92c523 100644 --- a/staging/src/k8s.io/legacy-cloud-providers/azure/clients/storageaccountclient/azure_storageaccountclient.go +++ b/staging/src/k8s.io/legacy-cloud-providers/azure/clients/storageaccountclient/azure_storageaccountclient.go @@ -364,8 +364,14 @@ func (c *Client) ListStorageAccountByResourceGroup(ctx context.Context, resource return result, retry.GetError(resp, err) } - for page.NotDone() { - result = append(result, *page.Response().Value...) + for { + result = append(result, page.Values()...) + + // Abort the loop when there's no nextLink in the response. + if to.String(page.Response().NextLink) == "" { + break + } + if err = page.NextWithContext(ctx); err != nil { klog.V(5).Infof("Received error in %s: resourceID: %s, error: %s", "storageAccount.list.next", resourceID, err) return result, retry.GetError(page.Response().Response.Response, err) diff --git a/staging/src/k8s.io/legacy-cloud-providers/azure/clients/subnetclient/azure_subnetclient.go b/staging/src/k8s.io/legacy-cloud-providers/azure/clients/subnetclient/azure_subnetclient.go index 9536b26cbe8..e50cc040f13 100644 --- a/staging/src/k8s.io/legacy-cloud-providers/azure/clients/subnetclient/azure_subnetclient.go +++ b/staging/src/k8s.io/legacy-cloud-providers/azure/clients/subnetclient/azure_subnetclient.go @@ -197,8 +197,14 @@ func (c *Client) listSubnet(ctx context.Context, resourceGroupName string, virtu return result, retry.GetError(resp, err) } - for page.NotDone() { - result = append(result, *page.Response().Value...) + for { + result = append(result, page.Values()...) + + // Abort the loop when there's no nextLink in the response. + if to.String(page.Response().NextLink) == "" { + break + } + if err = page.NextWithContext(ctx); err != nil { klog.V(5).Infof("Received error in %s: resourceID: %s, error: %s", "subnet.list.next", resourceID, err) return result, retry.GetError(page.Response().Response.Response, err) diff --git a/staging/src/k8s.io/legacy-cloud-providers/azure/clients/subnetclient/azure_subnetclient_test.go b/staging/src/k8s.io/legacy-cloud-providers/azure/clients/subnetclient/azure_subnetclient_test.go index ecf4f03adfb..593ab14999c 100644 --- a/staging/src/k8s.io/legacy-cloud-providers/azure/clients/subnetclient/azure_subnetclient_test.go +++ b/staging/src/k8s.io/legacy-cloud-providers/azure/clients/subnetclient/azure_subnetclient_test.go @@ -305,6 +305,35 @@ func TestListWithListResponderError(t *testing.T) { assert.Equal(t, 0, len(result)) } +func TestListWithNextPage(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + resourceID := "/subscriptions/subscriptionID/resourceGroups/rg/providers/Microsoft.Network/virtualNetworks/vnet/subnets" + armClient := mockarmclient.NewMockInterface(ctrl) + subnetList := []network.Subnet{getTestSubnet("subnet1"), getTestSubnet("subnet2"), getTestSubnet("subnet3")} + partialResponse, err := json.Marshal(network.SubnetListResult{Value: &subnetList, NextLink: to.StringPtr("nextLink")}) + assert.NoError(t, err) + pagedResponse, err := json.Marshal(network.SubnetListResult{Value: &subnetList}) + assert.NoError(t, err) + armClient.EXPECT().PrepareGetRequest(gomock.Any(), gomock.Any()).Return(&http.Request{}, nil) + armClient.EXPECT().Send(gomock.Any(), gomock.Any()).Return( + &http.Response{ + StatusCode: http.StatusOK, + Body: ioutil.NopCloser(bytes.NewReader(pagedResponse)), + }, nil) + armClient.EXPECT().GetResource(gomock.Any(), resourceID, "").Return( + &http.Response{ + StatusCode: http.StatusOK, + Body: ioutil.NopCloser(bytes.NewReader(partialResponse)), + }, nil).Times(1) + armClient.EXPECT().CloseResponse(gomock.Any(), gomock.Any()).Times(2) + subnetClient := getTestSubnetClient(armClient) + result, rerr := subnetClient.List(context.TODO(), "rg", "vnet") + assert.Nil(t, rerr) + assert.Equal(t, 6, len(result)) +} + func TestListNeverRateLimiter(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() diff --git a/staging/src/k8s.io/legacy-cloud-providers/azure/clients/vmclient/azure_vmclient.go b/staging/src/k8s.io/legacy-cloud-providers/azure/clients/vmclient/azure_vmclient.go index 3950e5304a6..5d9a8a6879f 100644 --- a/staging/src/k8s.io/legacy-cloud-providers/azure/clients/vmclient/azure_vmclient.go +++ b/staging/src/k8s.io/legacy-cloud-providers/azure/clients/vmclient/azure_vmclient.go @@ -194,8 +194,14 @@ func (c *Client) listVM(ctx context.Context, resourceGroupName string) ([]comput return result, retry.GetError(resp, err) } - for page.NotDone() { - result = append(result, *page.Response().Value...) + for { + result = append(result, page.Values()...) + + // Abort the loop when there's no nextLink in the response. + if to.String(page.Response().NextLink) == "" { + break + } + if err = page.NextWithContext(ctx); err != nil { klog.V(5).Infof("Received error in %s: resourceID: %s, error: %s", "vm.list.next", resourceID, err) return result, retry.GetError(page.Response().Response.Response, err) diff --git a/staging/src/k8s.io/legacy-cloud-providers/azure/clients/vmclient/azure_vmclient_test.go b/staging/src/k8s.io/legacy-cloud-providers/azure/clients/vmclient/azure_vmclient_test.go index 55947022f6e..eeea2998416 100644 --- a/staging/src/k8s.io/legacy-cloud-providers/azure/clients/vmclient/azure_vmclient_test.go +++ b/staging/src/k8s.io/legacy-cloud-providers/azure/clients/vmclient/azure_vmclient_test.go @@ -295,6 +295,35 @@ func TestListWithListResponderError(t *testing.T) { assert.Equal(t, 0, len(result)) } +func TestListWithNextPage(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + resourceID := "/subscriptions/subscriptionID/resourceGroups/rg/providers/Microsoft.Compute/virtualMachines" + armClient := mockarmclient.NewMockInterface(ctrl) + vmList := []compute.VirtualMachine{getTestVM("vm1"), getTestVM("vm2"), getTestVM("vm3")} + partialResponse, err := json.Marshal(compute.VirtualMachineListResult{Value: &vmList, NextLink: to.StringPtr("nextLink")}) + assert.NoError(t, err) + pagedResponse, err := json.Marshal(compute.VirtualMachineListResult{Value: &vmList}) + assert.NoError(t, err) + armClient.EXPECT().PrepareGetRequest(gomock.Any(), gomock.Any()).Return(&http.Request{}, nil) + armClient.EXPECT().Send(gomock.Any(), gomock.Any()).Return( + &http.Response{ + StatusCode: http.StatusOK, + Body: ioutil.NopCloser(bytes.NewReader(pagedResponse)), + }, nil) + armClient.EXPECT().GetResource(gomock.Any(), resourceID, "").Return( + &http.Response{ + StatusCode: http.StatusOK, + Body: ioutil.NopCloser(bytes.NewReader(partialResponse)), + }, nil).Times(1) + armClient.EXPECT().CloseResponse(gomock.Any(), gomock.Any()).Times(2) + vmClient := getTestVMClient(armClient) + result, rerr := vmClient.List(context.TODO(), "rg") + assert.Nil(t, rerr) + assert.Equal(t, 6, len(result)) +} + func TestListNeverRateLimiter(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() diff --git a/staging/src/k8s.io/legacy-cloud-providers/azure/clients/vmssclient/azure_vmssclient.go b/staging/src/k8s.io/legacy-cloud-providers/azure/clients/vmssclient/azure_vmssclient.go index 8c7c43287f4..9d8de0d93f2 100644 --- a/staging/src/k8s.io/legacy-cloud-providers/azure/clients/vmssclient/azure_vmssclient.go +++ b/staging/src/k8s.io/legacy-cloud-providers/azure/clients/vmssclient/azure_vmssclient.go @@ -192,8 +192,14 @@ func (c *Client) listVMSS(ctx context.Context, resourceGroupName string) ([]comp return result, retry.GetError(resp, err) } - for page.NotDone() { - result = append(result, *page.Response().Value...) + for { + result = append(result, page.Values()...) + + // Abort the loop when there's no nextLink in the response. + if to.String(page.Response().NextLink) == "" { + break + } + if err = page.NextWithContext(ctx); err != nil { klog.V(5).Infof("Received error in %s: resourceID: %s, error: %s", "vmss.list.next", resourceID, err) return result, retry.GetError(page.Response().Response.Response, err) diff --git a/staging/src/k8s.io/legacy-cloud-providers/azure/clients/vmssclient/azure_vmssclient_test.go b/staging/src/k8s.io/legacy-cloud-providers/azure/clients/vmssclient/azure_vmssclient_test.go index 839c79330e3..f100eff1c04 100644 --- a/staging/src/k8s.io/legacy-cloud-providers/azure/clients/vmssclient/azure_vmssclient_test.go +++ b/staging/src/k8s.io/legacy-cloud-providers/azure/clients/vmssclient/azure_vmssclient_test.go @@ -296,6 +296,35 @@ func TestListWithListResponderError(t *testing.T) { assert.Equal(t, 0, len(result)) } +func TestListWithNextPage(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + resourceID := "/subscriptions/subscriptionID/resourceGroups/rg/providers/Microsoft.Compute/virtualMachineScaleSets" + armClient := mockarmclient.NewMockInterface(ctrl) + vmssList := []compute.VirtualMachineScaleSet{getTestVMSS("vmss1"), getTestVMSS("vmss2"), getTestVMSS("vmss3")} + partialResponse, err := json.Marshal(compute.VirtualMachineScaleSetListResult{Value: &vmssList, NextLink: to.StringPtr("nextLink")}) + assert.NoError(t, err) + pagedResponse, err := json.Marshal(compute.VirtualMachineScaleSetListResult{Value: &vmssList}) + assert.NoError(t, err) + armClient.EXPECT().PrepareGetRequest(gomock.Any(), gomock.Any()).Return(&http.Request{}, nil) + armClient.EXPECT().Send(gomock.Any(), gomock.Any()).Return( + &http.Response{ + StatusCode: http.StatusOK, + Body: ioutil.NopCloser(bytes.NewReader(pagedResponse)), + }, nil) + armClient.EXPECT().GetResource(gomock.Any(), resourceID, "").Return( + &http.Response{ + StatusCode: http.StatusOK, + Body: ioutil.NopCloser(bytes.NewReader(partialResponse)), + }, nil).Times(1) + armClient.EXPECT().CloseResponse(gomock.Any(), gomock.Any()).Times(2) + vmssClient := getTestVMSSClient(armClient) + result, rerr := vmssClient.List(context.TODO(), "rg") + assert.Nil(t, rerr) + assert.Equal(t, 6, len(result)) +} + func TestListNeverRateLimiter(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() diff --git a/staging/src/k8s.io/legacy-cloud-providers/azure/clients/vmssvmclient/azure_vmssvmclient.go b/staging/src/k8s.io/legacy-cloud-providers/azure/clients/vmssvmclient/azure_vmssvmclient.go index 02c197176d1..1e36fbf6000 100644 --- a/staging/src/k8s.io/legacy-cloud-providers/azure/clients/vmssvmclient/azure_vmssvmclient.go +++ b/staging/src/k8s.io/legacy-cloud-providers/azure/clients/vmssvmclient/azure_vmssvmclient.go @@ -198,8 +198,14 @@ func (c *Client) listVMSSVM(ctx context.Context, resourceGroupName string, virtu return result, retry.GetError(resp, err) } - for page.NotDone() { - result = append(result, *page.Response().Value...) + for { + result = append(result, page.Values()...) + + // Abort the loop when there's no nextLink in the response. + if to.String(page.Response().NextLink) == "" { + break + } + if err = page.NextWithContext(ctx); err != nil { klog.V(5).Infof("Received error in %s: resourceID: %s, error: %s", "vmssvm.list.next", resourceID, err) return result, retry.GetError(page.Response().Response.Response, err) diff --git a/staging/src/k8s.io/legacy-cloud-providers/azure/clients/vmssvmclient/azure_vmssvmclient_test.go b/staging/src/k8s.io/legacy-cloud-providers/azure/clients/vmssvmclient/azure_vmssvmclient_test.go index 8a313ffa097..edf2a1f3914 100644 --- a/staging/src/k8s.io/legacy-cloud-providers/azure/clients/vmssvmclient/azure_vmssvmclient_test.go +++ b/staging/src/k8s.io/legacy-cloud-providers/azure/clients/vmssvmclient/azure_vmssvmclient_test.go @@ -295,6 +295,35 @@ func TestListWithListResponderError(t *testing.T) { assert.Equal(t, 0, len(result)) } +func TestListWithNextPage(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + resourceID := "/subscriptions/subscriptionID/resourceGroups/rg/providers/Microsoft.Compute/virtualMachineScaleSets/vmss1/virtualMachines" + armClient := mockarmclient.NewMockInterface(ctrl) + vmssvmList := []compute.VirtualMachineScaleSetVM{getTestVMSSVM("vmss1", "1"), getTestVMSSVM("vmss1", "2"), getTestVMSSVM("vmss1", "3")} + partialResponse, err := json.Marshal(compute.VirtualMachineScaleSetVMListResult{Value: &vmssvmList, NextLink: to.StringPtr("nextLink")}) + assert.NoError(t, err) + pagedResponse, err := json.Marshal(compute.VirtualMachineScaleSetVMListResult{Value: &vmssvmList}) + assert.NoError(t, err) + armClient.EXPECT().PrepareGetRequest(gomock.Any(), gomock.Any()).Return(&http.Request{}, nil) + armClient.EXPECT().Send(gomock.Any(), gomock.Any()).Return( + &http.Response{ + StatusCode: http.StatusOK, + Body: ioutil.NopCloser(bytes.NewReader(pagedResponse)), + }, nil) + armClient.EXPECT().GetResource(gomock.Any(), resourceID, "InstanceView").Return( + &http.Response{ + StatusCode: http.StatusOK, + Body: ioutil.NopCloser(bytes.NewReader(partialResponse)), + }, nil).Times(1) + armClient.EXPECT().CloseResponse(gomock.Any(), gomock.Any()).Times(2) + vmssvmClient := getTestVMSSVMClient(armClient) + result, rerr := vmssvmClient.List(context.TODO(), "rg", "vmss1", "InstanceView") + assert.Nil(t, rerr) + assert.Equal(t, 6, len(result)) +} + func TestListNeverRateLimiter(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish()