Repository: kubernetes-sigs/gcp-compute-persistent-disk-csi-driver
Pull request #1011 (2022-06-10 04:13:00)

Automated cherry pick of #999: Add implicit ListVolumesResponse#Entry pagination limit
Cherry pick of #999 on release-1.7.

#999: Add implicit ListVolumesResponse#Entry pagination limit

For details on the cherry pick process, see the [cherry pick requests](https://git.k8s.io/community/contributors/devel/sig-release/cherry-picks.md) page.

```release-note
Enforce implicit pagination limit of 500 of the ListVolumesResponse#Entry field when ListVolumesRequest#max_entries is not set
```


Patch:
diff --git a/pkg/gce-pd-csi-driver/controller.go b/pkg/gce-pd-csi-driver/controller.go
index c65a9cec..92941a5d 100644
--- a/pkg/gce-pd-csi-driver/controller.go
+++ b/pkg/gce-pd-csi-driver/controller.go
@@ -81,6 +81,13 @@ const (
 
 	replicationTypeNone       = "none"
 	replicationTypeRegionalPD = "regional-pd"
+
+	// The maximum number of entries that we can include in the
+	// ListVolumesResposne
+	// In reality, the limit here is 4MB (based on gRPC client response limits),
+	// but 500 is a good proxy (gives ~8KB of data per ListVolumesResponse#Entry)
+	// See https://github.com/grpc/grpc/blob/master/include/grpc/impl/codegen/grpc_types.h#L503)
+	maxListVolumesResponseEntries = 500
 )
 
 func isDiskReady(disk *gce.CloudDisk) (bool, error) {
@@ -708,7 +715,7 @@ func (gceCS *GCEControllerServer) ListVolumes(ctx context.Context, req *csi.List
 
 	var maxEntries int = int(req.MaxEntries)
 	if maxEntries == 0 {
-		maxEntries = len(gceCS.disks)
+		maxEntries = maxListVolumesResponseEntries
 	}
 
 	entries := []*csi.ListVolumesResponse_Entry{}


Test patch:
diff --git a/pkg/gce-pd-csi-driver/controller_test.go b/pkg/gce-pd-csi-driver/controller_test.go
index 649e3270..d92d390b 100644
--- a/pkg/gce-pd-csi-driver/controller_test.go
+++ b/pkg/gce-pd-csi-driver/controller_test.go
@@ -15,6 +15,7 @@ limitations under the License.
 package gceGCEDriver
 
 import (
+	"context"
 	"fmt"
 	"math/rand"
 	"reflect"
@@ -25,8 +26,6 @@ import (
 	"github.com/GoogleCloudPlatform/k8s-cloud-provider/pkg/cloud/meta"
 	"github.com/golang/protobuf/ptypes"
 
-	"context"
-
 	compute "google.golang.org/api/compute/v1"
 	"google.golang.org/grpc/codes"
 	"google.golang.org/grpc/status"
@@ -840,8 +839,79 @@ func TestCreateVolumeArguments(t *testing.T) {
 	}
 }
 
+func TestListVolumePagination(t *testing.T) {
+	testCases := []struct {
+		name            string
+		diskCount       int
+		maxEntries      int32
+		expectedEntries []int
+	}{
+		{
+			name:            "no pagination (implicit)",
+			diskCount:       325,
+			expectedEntries: []int{325},
+		},
+		{
+			name:            "no pagination (explicit)",
+			diskCount:       2500,
+			maxEntries:      2500,
+			expectedEntries: []int{2500},
+		},
+		{
+			name:            "pagination (implicit)",
+			diskCount:       1327,
+			expectedEntries: []int{500, 500, 327},
+		},
+		{
+			name:            "pagination (explicit)",
+			diskCount:       723,
+			maxEntries:      200,
+			expectedEntries: []int{200, 200, 200, 123},
+		},
+		{
+			name:            "pagination (explicit)",
+			diskCount:       3253,
+			maxEntries:      1000,
+			expectedEntries: []int{1000, 1000, 1000, 253},
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			// Setup new driver each time so no interference
+			var d []*gce.CloudDisk
+			for i := 0; i < tc.diskCount; i++ {
+				// Create diskCount dummy disks
+				d = append(d, gce.CloudDiskFromV1(&compute.Disk{Name: fmt.Sprintf("%v", i)}))
+			}
+			gceDriver := initGCEDriver(t, d)
+			tok := ""
+			for i, expectedEntry := range tc.expectedEntries {
+				lvr := &csi.ListVolumesRequest{
+					MaxEntries:    tc.maxEntries,
+					StartingToken: tok,
+				}
+				resp, err := gceDriver.cs.ListVolumes(context.TODO(), lvr)
+				if err != nil {
+					t.Fatalf("Got error %v", err)
+					return
+				}
+
+				if len(resp.Entries) != expectedEntry {
+					t.Fatalf("Got %v entries, expected %v on call # %d", len(resp.Entries), expectedEntry, i+1)
+				}
+
+				tok = resp.NextToken
+			}
+			if len(tok) != 0 {
+				t.Fatalf("Expected no more entries, but got NextToken in response: %s", tok)
+			}
+		})
+	}
+}
+
 func TestListVolumeArgs(t *testing.T) {
-	diskCount := 600
+	diskCount := 500
 	testCases := []struct {
 		name            string
 		maxEntries      int32