mirror of
				https://github.com/optim-enterprises-bv/kubernetes.git
				synced 2025-11-03 19:58:17 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			293 lines
		
	
	
		
			9.1 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			293 lines
		
	
	
		
			9.1 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
/*
 | 
						|
Copyright 2014 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 resource
 | 
						|
 | 
						|
import (
 | 
						|
	"fmt"
 | 
						|
	"reflect"
 | 
						|
 | 
						|
	"k8s.io/kubernetes/pkg/api"
 | 
						|
	"k8s.io/kubernetes/pkg/api/meta"
 | 
						|
	"k8s.io/kubernetes/pkg/apimachinery/registered"
 | 
						|
	"k8s.io/kubernetes/pkg/apis/extensions"
 | 
						|
	metav1 "k8s.io/kubernetes/pkg/apis/meta/v1"
 | 
						|
	"k8s.io/kubernetes/pkg/runtime"
 | 
						|
	"k8s.io/kubernetes/pkg/runtime/schema"
 | 
						|
	utilerrors "k8s.io/kubernetes/pkg/util/errors"
 | 
						|
	"k8s.io/kubernetes/pkg/util/sets"
 | 
						|
	"k8s.io/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 []utilerrors.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, utilerrors.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 utilerrors.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, err error) error {
 | 
						|
		if err != nil {
 | 
						|
			return err
 | 
						|
		}
 | 
						|
		infos = append(infos, info)
 | 
						|
		return nil
 | 
						|
	})
 | 
						|
	err = utilerrors.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 := sets.String{}
 | 
						|
	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 meta.IsListType(objects[0]) {
 | 
						|
			return objects[0], nil
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	version := ""
 | 
						|
	if len(versions) == 1 {
 | 
						|
		version = versions.List()[0]
 | 
						|
	}
 | 
						|
	return &api.List{
 | 
						|
		ListMeta: metav1.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 individual resources and resource collections - %d resources were found", len(info))
 | 
						|
		}
 | 
						|
		return info[0].Watch(resourceVersion)
 | 
						|
	}
 | 
						|
	return w.Watch(resourceVersion)
 | 
						|
}
 | 
						|
 | 
						|
// AsVersionedObject converts a list of infos into a single object - either a List containing
 | 
						|
// the objects as children, or if only a single Object is present, as that object. The provided
 | 
						|
// version will be preferred as the conversion target, but the Object's mapping version will be
 | 
						|
// used if that version is not present.
 | 
						|
func AsVersionedObject(infos []*Info, forceList bool, version schema.GroupVersion, encoder runtime.Encoder) (runtime.Object, error) {
 | 
						|
	objects, err := AsVersionedObjects(infos, version, encoder)
 | 
						|
	if err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
 | 
						|
	var object runtime.Object
 | 
						|
	if len(objects) == 1 && !forceList {
 | 
						|
		object = objects[0]
 | 
						|
	} else {
 | 
						|
		object = &api.List{Items: objects}
 | 
						|
		converted, err := TryConvert(api.Scheme, object, version, registered.GroupOrDie(api.GroupName).GroupVersion)
 | 
						|
		if err != nil {
 | 
						|
			return nil, err
 | 
						|
		}
 | 
						|
		object = converted
 | 
						|
	}
 | 
						|
	return object, nil
 | 
						|
}
 | 
						|
 | 
						|
// AsVersionedObjects converts a list of infos into versioned objects. The provided
 | 
						|
// version will be preferred as the conversion target, but the Object's mapping version will be
 | 
						|
// used if that version is not present.
 | 
						|
func AsVersionedObjects(infos []*Info, version schema.GroupVersion, encoder runtime.Encoder) ([]runtime.Object, error) {
 | 
						|
	objects := []runtime.Object{}
 | 
						|
	for _, info := range infos {
 | 
						|
		if info.Object == nil {
 | 
						|
			continue
 | 
						|
		}
 | 
						|
 | 
						|
		// TODO: use info.VersionedObject as the value?
 | 
						|
		switch obj := info.Object.(type) {
 | 
						|
		case *extensions.ThirdPartyResourceData:
 | 
						|
			objects = append(objects, &runtime.Unknown{Raw: obj.Data})
 | 
						|
			continue
 | 
						|
		}
 | 
						|
 | 
						|
		// objects that are not part of api.Scheme must be converted to JSON
 | 
						|
		// TODO: convert to map[string]interface{}, attach to runtime.Unknown?
 | 
						|
		if !version.Empty() {
 | 
						|
			if _, _, err := api.Scheme.ObjectKinds(info.Object); runtime.IsNotRegisteredError(err) {
 | 
						|
				// TODO: ideally this would encode to version, but we don't expose multiple codecs here.
 | 
						|
				data, err := runtime.Encode(encoder, info.Object)
 | 
						|
				if err != nil {
 | 
						|
					return nil, err
 | 
						|
				}
 | 
						|
				// TODO: Set ContentEncoding and ContentType.
 | 
						|
				objects = append(objects, &runtime.Unknown{Raw: data})
 | 
						|
				continue
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		converted, err := TryConvert(info.Mapping.ObjectConvertor, info.Object, version, info.Mapping.GroupVersionKind.GroupVersion())
 | 
						|
		if err != nil {
 | 
						|
			return nil, err
 | 
						|
		}
 | 
						|
		objects = append(objects, converted)
 | 
						|
	}
 | 
						|
	return objects, nil
 | 
						|
}
 | 
						|
 | 
						|
// TryConvert attempts to convert the given object to the provided versions in order. This function assumes
 | 
						|
// the object is in internal version.
 | 
						|
func TryConvert(converter runtime.ObjectConvertor, object runtime.Object, versions ...schema.GroupVersion) (runtime.Object, error) {
 | 
						|
	var last error
 | 
						|
	for _, version := range versions {
 | 
						|
		if version.Empty() {
 | 
						|
			return object, nil
 | 
						|
		}
 | 
						|
		obj, err := converter.ConvertToVersion(object, version)
 | 
						|
		if err != nil {
 | 
						|
			last = err
 | 
						|
			continue
 | 
						|
		}
 | 
						|
		return obj, nil
 | 
						|
	}
 | 
						|
	return nil, last
 | 
						|
}
 |