You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
117 lines
3.7 KiB
117 lines
3.7 KiB
/* |
|
Copyright 2017 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 pager |
|
|
|
import ( |
|
"context" |
|
"fmt" |
|
|
|
"k8s.io/apimachinery/pkg/api/errors" |
|
"k8s.io/apimachinery/pkg/api/meta" |
|
metainternalversion "k8s.io/apimachinery/pkg/apis/meta/internalversion" |
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" |
|
"k8s.io/apimachinery/pkg/runtime" |
|
) |
|
|
|
const defaultPageSize = 500 |
|
|
|
// ListPageFunc returns a list object for the given list options. |
|
type ListPageFunc func(ctx context.Context, opts metav1.ListOptions) (runtime.Object, error) |
|
|
|
// SimplePageFunc adapts a context-less list function into one that accepts a context. |
|
func SimplePageFunc(fn func(opts metav1.ListOptions) (runtime.Object, error)) ListPageFunc { |
|
return func(ctx context.Context, opts metav1.ListOptions) (runtime.Object, error) { |
|
return fn(opts) |
|
} |
|
} |
|
|
|
// ListPager assists client code in breaking large list queries into multiple |
|
// smaller chunks of PageSize or smaller. PageFn is expected to accept a |
|
// metav1.ListOptions that supports paging and return a list. The pager does |
|
// not alter the field or label selectors on the initial options list. |
|
type ListPager struct { |
|
PageSize int64 |
|
PageFn ListPageFunc |
|
|
|
FullListIfExpired bool |
|
} |
|
|
|
// New creates a new pager from the provided pager function using the default |
|
// options. It will fall back to a full list if an expiration error is encountered |
|
// as a last resort. |
|
func New(fn ListPageFunc) *ListPager { |
|
return &ListPager{ |
|
PageSize: defaultPageSize, |
|
PageFn: fn, |
|
FullListIfExpired: true, |
|
} |
|
} |
|
|
|
// TODO: introduce other types of paging functions - such as those that retrieve from a list |
|
// of namespaces. |
|
|
|
// List returns a single list object, but attempts to retrieve smaller chunks from the |
|
// server to reduce the impact on the server. If the chunk attempt fails, it will load |
|
// the full list instead. The Limit field on options, if unset, will default to the page size. |
|
func (p *ListPager) List(ctx context.Context, options metav1.ListOptions) (runtime.Object, error) { |
|
if options.Limit == 0 { |
|
options.Limit = p.PageSize |
|
} |
|
var list *metainternalversion.List |
|
for { |
|
obj, err := p.PageFn(ctx, options) |
|
if err != nil { |
|
if !errors.IsResourceExpired(err) || !p.FullListIfExpired { |
|
return nil, err |
|
} |
|
// the list expired while we were processing, fall back to a full list |
|
options.Limit = 0 |
|
options.Continue = "" |
|
return p.PageFn(ctx, options) |
|
} |
|
m, err := meta.ListAccessor(obj) |
|
if err != nil { |
|
return nil, fmt.Errorf("returned object must be a list: %v", err) |
|
} |
|
|
|
// exit early and return the object we got if we haven't processed any pages |
|
if len(m.GetContinue()) == 0 && list == nil { |
|
return obj, nil |
|
} |
|
|
|
// initialize the list and fill its contents |
|
if list == nil { |
|
list = &metainternalversion.List{Items: make([]runtime.Object, 0, options.Limit+1)} |
|
list.ResourceVersion = m.GetResourceVersion() |
|
list.SelfLink = m.GetSelfLink() |
|
} |
|
if err := meta.EachListItem(obj, func(obj runtime.Object) error { |
|
list.Items = append(list.Items, obj) |
|
return nil |
|
}); err != nil { |
|
return nil, err |
|
} |
|
|
|
// if we have no more items, return the list |
|
if len(m.GetContinue()) == 0 { |
|
return list, nil |
|
} |
|
|
|
// set the next loop up |
|
options.Continue = m.GetContinue() |
|
} |
|
}
|
|
|