From 6b042bbec349a7ff4b7aecb8d4666c20746cc815 Mon Sep 17 00:00:00 2001 From: Boris Glimcher Date: Thu, 7 Sep 2023 17:45:03 +0300 Subject: [PATCH] feat(list): add more pagination limits to list funcs Signed-off-by: Boris Glimcher --- pkg/evpn/bridge.go | 19 +++++++++++-------- pkg/evpn/bridge_test.go | 25 +++++++++++++++++++++++++ pkg/evpn/evpn.go | 20 ++++++++++---------- pkg/evpn/port.go | 21 +++++++++++++-------- pkg/evpn/port_test.go | 25 +++++++++++++++++++++++++ pkg/evpn/svi.go | 19 +++++++++++-------- pkg/evpn/svi_test.go | 25 +++++++++++++++++++++++++ pkg/evpn/vrf.go | 20 +++++++++++--------- pkg/evpn/vrf_test.go | 25 +++++++++++++++++++++++++ 9 files changed, 156 insertions(+), 43 deletions(-) diff --git a/pkg/evpn/bridge.go b/pkg/evpn/bridge.go index 9d558f9e..d93a5b1b 100644 --- a/pkg/evpn/bridge.go +++ b/pkg/evpn/bridge.go @@ -13,6 +13,7 @@ import ( "net" "sort" + "github.com/google/uuid" "github.com/vishvananda/netlink" pb "github.com/opiproject/opi-api/network/evpn-gw/v1alpha1/gen/go" @@ -253,25 +254,27 @@ func (s *Server) ListLogicalBridges(_ context.Context, in *pb.ListLogicalBridges log.Printf("error: %v", err) return nil, err } - // fetch object from the database + // fetch pagination from the database, calculate size and offset size, offset, perr := extractPagination(in.PageSize, in.PageToken, s.Pagination) if perr != nil { log.Printf("error: %v", perr) return nil, perr } - token := "" - log.Printf("Limiting result len(%d) to [%d:%d]", len(s.Bridges), offset, size) - // result, hasMoreElements := limitPagination(s.Bridges, offset, size) - // if hasMoreElements { - // token = uuid.New().String() - // s.Pagination[token] = offset + size - // } + // fetch object from the database Blobarray := []*pb.LogicalBridge{} for _, bridge := range s.Bridges { r := protoClone(bridge) r.Status = &pb.LogicalBridgeStatus{OperStatus: pb.LBOperStatus_LB_OPER_STATUS_UP} Blobarray = append(Blobarray, r) } + // sort is needed, since MAP is unsorted in golang, and we might get different results sortLogicalBridges(Blobarray) + log.Printf("Limiting result len(%d) to [%d:%d]", len(Blobarray), offset, size) + Blobarray, hasMoreElements := limitPagination(Blobarray, offset, size) + token := "" + if hasMoreElements { + token = uuid.New().String() + s.Pagination[token] = offset + size + } return &pb.ListLogicalBridgesResponse{LogicalBridges: Blobarray, NextPageToken: token}, nil } diff --git a/pkg/evpn/bridge_test.go b/pkg/evpn/bridge_test.go index 1667386a..37de5854 100644 --- a/pkg/evpn/bridge_test.go +++ b/pkg/evpn/bridge_test.go @@ -667,6 +667,30 @@ func Test_ListLogicalBridges(t *testing.T) { size: 0, token: "unknown-pagination-token", }, + "pagination overflow": { + in: "", + out: []*pb.LogicalBridge{&testLogicalBridgeWithStatus}, + errCode: codes.OK, + errMsg: "", + size: 1000, + token: "", + }, + "pagination normal": { + in: "", + out: []*pb.LogicalBridge{&testLogicalBridgeWithStatus}, + errCode: codes.OK, + errMsg: "", + size: 1, + token: "", + }, + "pagination offset": { + in: "", + out: []*pb.LogicalBridge{}, + errCode: codes.OK, + errMsg: "", + size: 1, + token: "existing-pagination-token", + }, } // run tests @@ -693,6 +717,7 @@ func Test_ListLogicalBridges(t *testing.T) { opi.Bridges[testLogicalBridgeName] = protoClone(&testLogicalBridge) opi.Bridges[testLogicalBridgeName].Name = testLogicalBridgeName + opi.Pagination["existing-pagination-token"] = 1 request := &pb.ListLogicalBridgesRequest{PageSize: tt.size, PageToken: tt.token} response, err := client.ListLogicalBridges(ctx, request) diff --git a/pkg/evpn/evpn.go b/pkg/evpn/evpn.go index b03f4852..9a42059e 100644 --- a/pkg/evpn/evpn.go +++ b/pkg/evpn/evpn.go @@ -107,13 +107,13 @@ func extractPagination(pageSize int32, pageToken string, pagination map[string]i return size, offset, nil } -// func limitPagination[T any](result []T, offset int, size int) ([]T, bool) { -// end := offset + size -// hasMoreElements := false -// if end < len(result) { -// hasMoreElements = true -// } else { -// end = len(result) -// } -// return result[offset:end], hasMoreElements -// } +func limitPagination[T any](result []T, offset int, size int) ([]T, bool) { + end := offset + size + hasMoreElements := false + if end < len(result) { + hasMoreElements = true + } else { + end = len(result) + } + return result[offset:end], hasMoreElements +} diff --git a/pkg/evpn/port.go b/pkg/evpn/port.go index 97b7d122..38340bb5 100644 --- a/pkg/evpn/port.go +++ b/pkg/evpn/port.go @@ -12,6 +12,7 @@ import ( "path" "sort" + "github.com/google/uuid" // "github.com/vishvananda/netlink" pb "github.com/opiproject/opi-api/network/evpn-gw/v1alpha1/gen/go" @@ -73,6 +74,8 @@ func (s *Server) CreateBridgePort(_ context.Context, in *pb.CreateBridgePortRequ } // get base interface (e.g.: eth2) iface, err := s.nLink.LinkByName(resourceID) + // TODO: maybe we need to create a new iface here and not rely on existing one ? + // iface := &netlink.Dummy{LinkAttrs: netlink.LinkAttrs{Name: resourceID}} if err != nil { err := status.Errorf(codes.NotFound, "unable to find key %s", resourceID) log.Printf("error: %v", err) @@ -278,25 +281,27 @@ func (s *Server) ListBridgePorts(_ context.Context, in *pb.ListBridgePortsReques log.Printf("error: %v", err) return nil, err } - // fetch object from the database + // fetch pagination from the database, calculate size and offset size, offset, perr := extractPagination(in.PageSize, in.PageToken, s.Pagination) if perr != nil { log.Printf("error: %v", perr) return nil, perr } - token := "" - log.Printf("Limiting result len(%d) to [%d:%d]", len(s.Ports), offset, size) - // result, hasMoreElements := limitPagination(s.Ports, offset, size) - // if hasMoreElements { - // token = uuid.New().String() - // s.Pagination[token] = offset + size - // } + // fetch object from the database Blobarray := []*pb.BridgePort{} for _, port := range s.Ports { r := protoClone(port) r.Status = &pb.BridgePortStatus{OperStatus: pb.BPOperStatus_BP_OPER_STATUS_UP} Blobarray = append(Blobarray, r) } + // sort is needed, since MAP is unsorted in golang, and we might get different results sortBridgePorts(Blobarray) + log.Printf("Limiting result len(%d) to [%d:%d]", len(Blobarray), offset, size) + Blobarray, hasMoreElements := limitPagination(Blobarray, offset, size) + token := "" + if hasMoreElements { + token = uuid.New().String() + s.Pagination[token] = offset + size + } return &pb.ListBridgePortsResponse{BridgePorts: Blobarray, NextPageToken: token}, nil } diff --git a/pkg/evpn/port_test.go b/pkg/evpn/port_test.go index 3cf7c50f..7d9df46c 100644 --- a/pkg/evpn/port_test.go +++ b/pkg/evpn/port_test.go @@ -700,6 +700,30 @@ func Test_ListBridgePorts(t *testing.T) { size: 0, token: "unknown-pagination-token", }, + "pagination overflow": { + in: "", + out: []*pb.BridgePort{&testBridgePortWithStatus}, + errCode: codes.OK, + errMsg: "", + size: 1000, + token: "", + }, + "pagination normal": { + in: "", + out: []*pb.BridgePort{&testBridgePortWithStatus}, + errCode: codes.OK, + errMsg: "", + size: 1, + token: "", + }, + "pagination offset": { + in: "", + out: []*pb.BridgePort{}, + errCode: codes.OK, + errMsg: "", + size: 1, + token: "existing-pagination-token", + }, } // run tests @@ -726,6 +750,7 @@ func Test_ListBridgePorts(t *testing.T) { opi.Ports[testBridgePortName] = protoClone(&testBridgePort) opi.Ports[testBridgePortName].Name = testBridgePortName + opi.Pagination["existing-pagination-token"] = 1 request := &pb.ListBridgePortsRequest{PageSize: tt.size, PageToken: tt.token} response, err := client.ListBridgePorts(ctx, request) diff --git a/pkg/evpn/svi.go b/pkg/evpn/svi.go index cbc1a69a..80a81da0 100644 --- a/pkg/evpn/svi.go +++ b/pkg/evpn/svi.go @@ -14,6 +14,7 @@ import ( "path" "sort" + "github.com/google/uuid" "github.com/vishvananda/netlink" pb "github.com/opiproject/opi-api/network/evpn-gw/v1alpha1/gen/go" @@ -311,25 +312,27 @@ func (s *Server) ListSvis(_ context.Context, in *pb.ListSvisRequest) (*pb.ListSv log.Printf("error: %v", err) return nil, err } - // fetch object from the database + // fetch pagination from the database, calculate size and offset size, offset, perr := extractPagination(in.PageSize, in.PageToken, s.Pagination) if perr != nil { log.Printf("error: %v", perr) return nil, perr } - token := "" - log.Printf("Limiting result len(%d) to [%d:%d]", len(s.Svis), offset, size) - // result, hasMoreElements := limitPagination(s.Svis, offset, size) - // if hasMoreElements { - // token = uuid.New().String() - // s.Pagination[token] = offset + size - // } + // fetch object from the database Blobarray := []*pb.Svi{} for _, svi := range s.Svis { r := protoClone(svi) r.Status = &pb.SviStatus{OperStatus: pb.SVIOperStatus_SVI_OPER_STATUS_UP} Blobarray = append(Blobarray, r) } + // sort is needed, since MAP is unsorted in golang, and we might get different results sortSvis(Blobarray) + log.Printf("Limiting result len(%d) to [%d:%d]", len(Blobarray), offset, size) + Blobarray, hasMoreElements := limitPagination(Blobarray, offset, size) + token := "" + if hasMoreElements { + token = uuid.New().String() + s.Pagination[token] = offset + size + } return &pb.ListSvisResponse{Svis: Blobarray, NextPageToken: token}, nil } diff --git a/pkg/evpn/svi_test.go b/pkg/evpn/svi_test.go index 3add299c..d35f0366 100644 --- a/pkg/evpn/svi_test.go +++ b/pkg/evpn/svi_test.go @@ -826,6 +826,30 @@ func Test_ListSvis(t *testing.T) { size: 0, token: "unknown-pagination-token", }, + "pagination overflow": { + in: "", + out: []*pb.Svi{&testSviWithStatus}, + errCode: codes.OK, + errMsg: "", + size: 1000, + token: "", + }, + "pagination normal": { + in: "", + out: []*pb.Svi{&testSviWithStatus}, + errCode: codes.OK, + errMsg: "", + size: 1, + token: "", + }, + "pagination offset": { + in: "", + out: []*pb.Svi{}, + errCode: codes.OK, + errMsg: "", + size: 1, + token: "existing-pagination-token", + }, } // run tests @@ -852,6 +876,7 @@ func Test_ListSvis(t *testing.T) { opi.Svis[testSviName] = protoClone(&testSvi) opi.Svis[testSviName].Name = testSviName + opi.Pagination["existing-pagination-token"] = 1 request := &pb.ListSvisRequest{PageSize: tt.size, PageToken: tt.token} response, err := client.ListSvis(ctx, request) diff --git a/pkg/evpn/vrf.go b/pkg/evpn/vrf.go index 8cf0a3d5..3a1b484f 100644 --- a/pkg/evpn/vrf.go +++ b/pkg/evpn/vrf.go @@ -15,6 +15,7 @@ import ( "path" "sort" + "github.com/google/uuid" "github.com/vishvananda/netlink" pb "github.com/opiproject/opi-api/network/evpn-gw/v1alpha1/gen/go" @@ -326,26 +327,27 @@ func (s *Server) ListVrfs(_ context.Context, in *pb.ListVrfsRequest) (*pb.ListVr log.Printf("error: %v", err) return nil, err } - // fetch object from the database + // fetch pagination from the database, calculate size and offset size, offset, perr := extractPagination(in.PageSize, in.PageToken, s.Pagination) if perr != nil { log.Printf("error: %v", perr) return nil, perr } - token := "" - log.Printf("Limiting result len(%d) to [%d:%d]", len(s.Vrfs), offset, size) - // result, hasMoreElements := limitPagination(s.Vrfs, offset, size) - // if hasMoreElements { - // token = uuid.New().String() - // s.Pagination[token] = offset + size - // } + // fetch object from the database Blobarray := []*pb.Vrf{} for _, vrf := range s.Vrfs { r := protoClone(vrf) r.Status = &pb.VrfStatus{LocalAs: 4} Blobarray = append(Blobarray, r) } - // TODO: fetch object from the database + // sort is needed, since MAP is unsorted in golang, and we might get different results sortVrfs(Blobarray) + log.Printf("Limiting result len(%d) to [%d:%d]", len(Blobarray), offset, size) + Blobarray, hasMoreElements := limitPagination(Blobarray, offset, size) + token := "" + if hasMoreElements { + token = uuid.New().String() + s.Pagination[token] = offset + size + } return &pb.ListVrfsResponse{Vrfs: Blobarray, NextPageToken: token}, nil } diff --git a/pkg/evpn/vrf_test.go b/pkg/evpn/vrf_test.go index ca3bfcae..49040913 100644 --- a/pkg/evpn/vrf_test.go +++ b/pkg/evpn/vrf_test.go @@ -858,6 +858,30 @@ func Test_ListVrfs(t *testing.T) { size: 0, token: "unknown-pagination-token", }, + "pagination overflow": { + in: "", + out: []*pb.Vrf{&testVrfWithStatus}, + errCode: codes.OK, + errMsg: "", + size: 1000, + token: "", + }, + "pagination normal": { + in: "", + out: []*pb.Vrf{&testVrfWithStatus}, + errCode: codes.OK, + errMsg: "", + size: 1, + token: "", + }, + "pagination offset": { + in: "", + out: []*pb.Vrf{}, + errCode: codes.OK, + errMsg: "", + size: 1, + token: "existing-pagination-token", + }, } // run tests @@ -884,6 +908,7 @@ func Test_ListVrfs(t *testing.T) { opi.Vrfs[testVrfName] = protoClone(&testVrf) opi.Vrfs[testVrfName].Name = testVrfName + opi.Pagination["existing-pagination-token"] = 1 request := &pb.ListVrfsRequest{PageSize: tt.size, PageToken: tt.token} response, err := client.ListVrfs(ctx, request)