mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-28 05:57:25 +00:00
Allow errors to be ignored by the builder
This commit is contained in:
parent
bc86b31a8b
commit
d24c5b145e
@ -21,16 +21,13 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"reflect"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/meta"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/meta"
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/util/errors"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/util/errors"
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/watch"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Builder provides convenience functions for taking arguments and parameters
|
// Builder provides convenience functions for taking arguments and parameters
|
||||||
@ -408,156 +405,6 @@ func (b *Builder) Do() *Result {
|
|||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
|
|
||||||
// Result contains helper methods for dealing with the outcome of a Builder.
|
|
||||||
type Result struct {
|
|
||||||
err error
|
|
||||||
visitor Visitor
|
|
||||||
|
|
||||||
sources []Visitor
|
|
||||||
singular bool
|
|
||||||
|
|
||||||
// populated by a call to Infos
|
|
||||||
info []*Info
|
|
||||||
}
|
|
||||||
|
|
||||||
// Err returns one or more errors (via a util.ErrorList) that occurred prior
|
|
||||||
// to visiting the elements in the visitor. To see all errors including those
|
|
||||||
// that occur during visitation, invoke Infos().
|
|
||||||
func (r *Result) Err() error {
|
|
||||||
return r.err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Visit implements the Visitor interface on the items described in the Builder.
|
|
||||||
// Note that some visitor sources are not traversable more than once, or may
|
|
||||||
// return different results. If you wish to operate on the same set of resources
|
|
||||||
// multiple times, use the Infos() method.
|
|
||||||
func (r *Result) Visit(fn VisitorFunc) error {
|
|
||||||
if r.err != nil {
|
|
||||||
return r.err
|
|
||||||
}
|
|
||||||
return r.visitor.Visit(fn)
|
|
||||||
}
|
|
||||||
|
|
||||||
// IntoSingular sets the provided boolean pointer to true if the Builder input
|
|
||||||
// reflected a single item, or multiple.
|
|
||||||
func (r *Result) IntoSingular(b *bool) *Result {
|
|
||||||
*b = r.singular
|
|
||||||
return r
|
|
||||||
}
|
|
||||||
|
|
||||||
// Infos returns an array of all of the resource infos retrieved via traversal.
|
|
||||||
// Will attempt to traverse the entire set of visitors only once, and will return
|
|
||||||
// a cached list on subsequent calls.
|
|
||||||
func (r *Result) Infos() ([]*Info, error) {
|
|
||||||
if r.err != nil {
|
|
||||||
return nil, r.err
|
|
||||||
}
|
|
||||||
if r.info != nil {
|
|
||||||
return r.info, nil
|
|
||||||
}
|
|
||||||
infos := []*Info{}
|
|
||||||
err := r.visitor.Visit(func(info *Info) error {
|
|
||||||
infos = append(infos, info)
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
r.info, r.err = infos, err
|
|
||||||
return infos, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Object returns a single object representing the output of a single visit to all
|
|
||||||
// found resources. If the Builder was a singular context (expected to return a
|
|
||||||
// single resource by user input) and only a single resource was found, the resource
|
|
||||||
// will be returned as is. Otherwise, the returned resources will be part of an
|
|
||||||
// api.List. The ResourceVersion of the api.List will be set only if it is identical
|
|
||||||
// across all infos returned.
|
|
||||||
func (r *Result) Object() (runtime.Object, error) {
|
|
||||||
infos, err := r.Infos()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
versions := util.StringSet{}
|
|
||||||
objects := []runtime.Object{}
|
|
||||||
for _, info := range infos {
|
|
||||||
if info.Object != nil {
|
|
||||||
objects = append(objects, info.Object)
|
|
||||||
versions.Insert(info.ResourceVersion)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(objects) == 1 {
|
|
||||||
if r.singular {
|
|
||||||
return objects[0], nil
|
|
||||||
}
|
|
||||||
// if the item is a list already, don't create another list
|
|
||||||
if _, err := runtime.GetItemsPtr(objects[0]); err == nil {
|
|
||||||
return objects[0], nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
version := ""
|
|
||||||
if len(versions) == 1 {
|
|
||||||
version = versions.List()[0]
|
|
||||||
}
|
|
||||||
return &api.List{
|
|
||||||
ListMeta: api.ListMeta{
|
|
||||||
ResourceVersion: version,
|
|
||||||
},
|
|
||||||
Items: objects,
|
|
||||||
}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// ResourceMapping returns a single meta.RESTMapping representing the
|
|
||||||
// resources located by the builder, or an error if more than one
|
|
||||||
// mapping was found.
|
|
||||||
func (r *Result) ResourceMapping() (*meta.RESTMapping, error) {
|
|
||||||
if r.err != nil {
|
|
||||||
return nil, r.err
|
|
||||||
}
|
|
||||||
mappings := map[string]*meta.RESTMapping{}
|
|
||||||
for i := range r.sources {
|
|
||||||
m, ok := r.sources[i].(ResourceMapping)
|
|
||||||
if !ok {
|
|
||||||
return nil, fmt.Errorf("a resource mapping could not be loaded from %v", reflect.TypeOf(r.sources[i]))
|
|
||||||
}
|
|
||||||
mapping := m.ResourceMapping()
|
|
||||||
mappings[mapping.Resource] = mapping
|
|
||||||
}
|
|
||||||
if len(mappings) != 1 {
|
|
||||||
return nil, fmt.Errorf("expected only a single resource type")
|
|
||||||
}
|
|
||||||
for _, mapping := range mappings {
|
|
||||||
return mapping, nil
|
|
||||||
}
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Watch retrieves changes that occur on the server to the specified resource.
|
|
||||||
// It currently supports watching a single source - if the resource source
|
|
||||||
// (selectors or pure types) can be watched, they will be, otherwise the list
|
|
||||||
// will be visited (equivalent to the Infos() call) and if there is a single
|
|
||||||
// resource present, it will be watched, otherwise an error will be returned.
|
|
||||||
func (r *Result) Watch(resourceVersion string) (watch.Interface, error) {
|
|
||||||
if r.err != nil {
|
|
||||||
return nil, r.err
|
|
||||||
}
|
|
||||||
if len(r.sources) != 1 {
|
|
||||||
return nil, fmt.Errorf("you may only watch a single resource or type of resource at a time")
|
|
||||||
}
|
|
||||||
w, ok := r.sources[0].(Watchable)
|
|
||||||
if !ok {
|
|
||||||
info, err := r.Infos()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if len(info) != 1 {
|
|
||||||
return nil, fmt.Errorf("watch is only supported on a single resource - %d resources were found", len(info))
|
|
||||||
}
|
|
||||||
return info[0].Watch(resourceVersion)
|
|
||||||
}
|
|
||||||
return w.Watch(resourceVersion)
|
|
||||||
}
|
|
||||||
|
|
||||||
func SplitResourceArgument(arg string) []string {
|
func SplitResourceArgument(arg string) []string {
|
||||||
set := util.NewStringSet()
|
set := util.NewStringSet()
|
||||||
set.Insert(strings.Split(arg, ",")...)
|
set.Insert(strings.Split(arg, ",")...)
|
||||||
|
202
pkg/kubectl/resource/result.go
Normal file
202
pkg/kubectl/resource/result.go
Normal file
@ -0,0 +1,202 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2014 Google Inc. All rights reserved.
|
||||||
|
|
||||||
|
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 resource
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/meta"
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/util/errors"
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/watch"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ErrMatchFunc can be used to filter errors that may not be true failures.
|
||||||
|
type ErrMatchFunc func(error) bool
|
||||||
|
|
||||||
|
// Result contains helper methods for dealing with the outcome of a Builder.
|
||||||
|
type Result struct {
|
||||||
|
err error
|
||||||
|
visitor Visitor
|
||||||
|
|
||||||
|
sources []Visitor
|
||||||
|
singular bool
|
||||||
|
|
||||||
|
ignoreErrors []errors.Matcher
|
||||||
|
|
||||||
|
// populated by a call to Infos
|
||||||
|
info []*Info
|
||||||
|
}
|
||||||
|
|
||||||
|
// IgnoreErrors will filter errors that occur when by visiting the result
|
||||||
|
// (but not errors that occur by creating the result in the first place),
|
||||||
|
// eliminating any that match fns. This is best used in combination with
|
||||||
|
// Builder.ContinueOnError(), where the visitors accumulate errors and return
|
||||||
|
// them after visiting as a slice of errors. If no errors remain after
|
||||||
|
// filtering, the various visitor methods on Result will return nil for
|
||||||
|
// err.
|
||||||
|
func (r *Result) IgnoreErrors(fns ...ErrMatchFunc) *Result {
|
||||||
|
for _, fn := range fns {
|
||||||
|
r.ignoreErrors = append(r.ignoreErrors, errors.Matcher(fn))
|
||||||
|
}
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
// Err returns one or more errors (via a util.ErrorList) that occurred prior
|
||||||
|
// to visiting the elements in the visitor. To see all errors including those
|
||||||
|
// that occur during visitation, invoke Infos().
|
||||||
|
func (r *Result) Err() error {
|
||||||
|
return r.err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Visit implements the Visitor interface on the items described in the Builder.
|
||||||
|
// Note that some visitor sources are not traversable more than once, or may
|
||||||
|
// return different results. If you wish to operate on the same set of resources
|
||||||
|
// multiple times, use the Infos() method.
|
||||||
|
func (r *Result) Visit(fn VisitorFunc) error {
|
||||||
|
if r.err != nil {
|
||||||
|
return r.err
|
||||||
|
}
|
||||||
|
err := r.visitor.Visit(fn)
|
||||||
|
return errors.FilterOut(err, r.ignoreErrors...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IntoSingular sets the provided boolean pointer to true if the Builder input
|
||||||
|
// reflected a single item, or multiple.
|
||||||
|
func (r *Result) IntoSingular(b *bool) *Result {
|
||||||
|
*b = r.singular
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
// Infos returns an array of all of the resource infos retrieved via traversal.
|
||||||
|
// Will attempt to traverse the entire set of visitors only once, and will return
|
||||||
|
// a cached list on subsequent calls.
|
||||||
|
func (r *Result) Infos() ([]*Info, error) {
|
||||||
|
if r.err != nil {
|
||||||
|
return nil, r.err
|
||||||
|
}
|
||||||
|
if r.info != nil {
|
||||||
|
return r.info, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
infos := []*Info{}
|
||||||
|
err := r.visitor.Visit(func(info *Info) error {
|
||||||
|
infos = append(infos, info)
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
err = errors.FilterOut(err, r.ignoreErrors...)
|
||||||
|
|
||||||
|
r.info, r.err = infos, err
|
||||||
|
return infos, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Object returns a single object representing the output of a single visit to all
|
||||||
|
// found resources. If the Builder was a singular context (expected to return a
|
||||||
|
// single resource by user input) and only a single resource was found, the resource
|
||||||
|
// will be returned as is. Otherwise, the returned resources will be part of an
|
||||||
|
// api.List. The ResourceVersion of the api.List will be set only if it is identical
|
||||||
|
// across all infos returned.
|
||||||
|
func (r *Result) Object() (runtime.Object, error) {
|
||||||
|
infos, err := r.Infos()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
versions := util.StringSet{}
|
||||||
|
objects := []runtime.Object{}
|
||||||
|
for _, info := range infos {
|
||||||
|
if info.Object != nil {
|
||||||
|
objects = append(objects, info.Object)
|
||||||
|
versions.Insert(info.ResourceVersion)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(objects) == 1 {
|
||||||
|
if r.singular {
|
||||||
|
return objects[0], nil
|
||||||
|
}
|
||||||
|
// if the item is a list already, don't create another list
|
||||||
|
if _, err := runtime.GetItemsPtr(objects[0]); err == nil {
|
||||||
|
return objects[0], nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
version := ""
|
||||||
|
if len(versions) == 1 {
|
||||||
|
version = versions.List()[0]
|
||||||
|
}
|
||||||
|
return &api.List{
|
||||||
|
ListMeta: api.ListMeta{
|
||||||
|
ResourceVersion: version,
|
||||||
|
},
|
||||||
|
Items: objects,
|
||||||
|
}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// ResourceMapping returns a single meta.RESTMapping representing the
|
||||||
|
// resources located by the builder, or an error if more than one
|
||||||
|
// mapping was found.
|
||||||
|
func (r *Result) ResourceMapping() (*meta.RESTMapping, error) {
|
||||||
|
if r.err != nil {
|
||||||
|
return nil, r.err
|
||||||
|
}
|
||||||
|
mappings := map[string]*meta.RESTMapping{}
|
||||||
|
for i := range r.sources {
|
||||||
|
m, ok := r.sources[i].(ResourceMapping)
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("a resource mapping could not be loaded from %v", reflect.TypeOf(r.sources[i]))
|
||||||
|
}
|
||||||
|
mapping := m.ResourceMapping()
|
||||||
|
mappings[mapping.Resource] = mapping
|
||||||
|
}
|
||||||
|
if len(mappings) != 1 {
|
||||||
|
return nil, fmt.Errorf("expected only a single resource type")
|
||||||
|
}
|
||||||
|
for _, mapping := range mappings {
|
||||||
|
return mapping, nil
|
||||||
|
}
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Watch retrieves changes that occur on the server to the specified resource.
|
||||||
|
// It currently supports watching a single source - if the resource source
|
||||||
|
// (selectors or pure types) can be watched, they will be, otherwise the list
|
||||||
|
// will be visited (equivalent to the Infos() call) and if there is a single
|
||||||
|
// resource present, it will be watched, otherwise an error will be returned.
|
||||||
|
func (r *Result) Watch(resourceVersion string) (watch.Interface, error) {
|
||||||
|
if r.err != nil {
|
||||||
|
return nil, r.err
|
||||||
|
}
|
||||||
|
if len(r.sources) != 1 {
|
||||||
|
return nil, fmt.Errorf("you may only watch a single resource or type of resource at a time")
|
||||||
|
}
|
||||||
|
w, ok := r.sources[0].(Watchable)
|
||||||
|
if !ok {
|
||||||
|
info, err := r.Infos()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if len(info) != 1 {
|
||||||
|
return nil, fmt.Errorf("watch is only supported on a single resource - %d resources were found", len(info))
|
||||||
|
}
|
||||||
|
return info[0].Watch(resourceVersion)
|
||||||
|
}
|
||||||
|
return w.Watch(resourceVersion)
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user