Code refactoring for bpa operator
[icn.git] / cmd / bpa-operator / vendor / k8s.io / client-go / tools / pager / pager.go
1 /*
2 Copyright 2017 The Kubernetes Authors.
3
4 Licensed under the Apache License, Version 2.0 (the "License");
5 you may not use this file except in compliance with the License.
6 You may obtain a copy of the License at
7
8     http://www.apache.org/licenses/LICENSE-2.0
9
10 Unless required by applicable law or agreed to in writing, software
11 distributed under the License is distributed on an "AS IS" BASIS,
12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 See the License for the specific language governing permissions and
14 limitations under the License.
15 */
16
17 package pager
18
19 import (
20         "context"
21         "fmt"
22
23         "k8s.io/apimachinery/pkg/api/errors"
24         "k8s.io/apimachinery/pkg/api/meta"
25         metainternalversion "k8s.io/apimachinery/pkg/apis/meta/internalversion"
26         metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
27         "k8s.io/apimachinery/pkg/runtime"
28 )
29
30 const defaultPageSize = 500
31
32 // ListPageFunc returns a list object for the given list options.
33 type ListPageFunc func(ctx context.Context, opts metav1.ListOptions) (runtime.Object, error)
34
35 // SimplePageFunc adapts a context-less list function into one that accepts a context.
36 func SimplePageFunc(fn func(opts metav1.ListOptions) (runtime.Object, error)) ListPageFunc {
37         return func(ctx context.Context, opts metav1.ListOptions) (runtime.Object, error) {
38                 return fn(opts)
39         }
40 }
41
42 // ListPager assists client code in breaking large list queries into multiple
43 // smaller chunks of PageSize or smaller. PageFn is expected to accept a
44 // metav1.ListOptions that supports paging and return a list. The pager does
45 // not alter the field or label selectors on the initial options list.
46 type ListPager struct {
47         PageSize int64
48         PageFn   ListPageFunc
49
50         FullListIfExpired bool
51 }
52
53 // New creates a new pager from the provided pager function using the default
54 // options. It will fall back to a full list if an expiration error is encountered
55 // as a last resort.
56 func New(fn ListPageFunc) *ListPager {
57         return &ListPager{
58                 PageSize:          defaultPageSize,
59                 PageFn:            fn,
60                 FullListIfExpired: true,
61         }
62 }
63
64 // TODO: introduce other types of paging functions - such as those that retrieve from a list
65 // of namespaces.
66
67 // List returns a single list object, but attempts to retrieve smaller chunks from the
68 // server to reduce the impact on the server. If the chunk attempt fails, it will load
69 // the full list instead. The Limit field on options, if unset, will default to the page size.
70 func (p *ListPager) List(ctx context.Context, options metav1.ListOptions) (runtime.Object, error) {
71         if options.Limit == 0 {
72                 options.Limit = p.PageSize
73         }
74         var list *metainternalversion.List
75         for {
76                 obj, err := p.PageFn(ctx, options)
77                 if err != nil {
78                         if !errors.IsResourceExpired(err) || !p.FullListIfExpired {
79                                 return nil, err
80                         }
81                         // the list expired while we were processing, fall back to a full list
82                         options.Limit = 0
83                         options.Continue = ""
84                         return p.PageFn(ctx, options)
85                 }
86                 m, err := meta.ListAccessor(obj)
87                 if err != nil {
88                         return nil, fmt.Errorf("returned object must be a list: %v", err)
89                 }
90
91                 // exit early and return the object we got if we haven't processed any pages
92                 if len(m.GetContinue()) == 0 && list == nil {
93                         return obj, nil
94                 }
95
96                 // initialize the list and fill its contents
97                 if list == nil {
98                         list = &metainternalversion.List{Items: make([]runtime.Object, 0, options.Limit+1)}
99                         list.ResourceVersion = m.GetResourceVersion()
100                         list.SelfLink = m.GetSelfLink()
101                 }
102                 if err := meta.EachListItem(obj, func(obj runtime.Object) error {
103                         list.Items = append(list.Items, obj)
104                         return nil
105                 }); err != nil {
106                         return nil, err
107                 }
108
109                 // if we have no more items, return the list
110                 if len(m.GetContinue()) == 0 {
111                         return list, nil
112                 }
113
114                 // set the next loop up
115                 options.Continue = m.GetContinue()
116         }
117 }