2014-12-27 21:48:27 +00:00
/ *
2016-06-03 00:25:58 +00:00
Copyright 2014 The Kubernetes Authors .
2014-12-27 21:48:27 +00:00
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 (
2015-01-14 18:06:53 +00:00
"bytes"
2014-12-27 21:48:27 +00:00
"fmt"
"io"
"net/http"
"net/url"
"os"
"path/filepath"
2016-05-05 22:57:25 +00:00
"time"
2014-12-27 21:48:27 +00:00
2016-12-20 07:02:39 +00:00
"golang.org/x/text/encoding/unicode"
"golang.org/x/text/transform"
2017-01-13 17:48:50 +00:00
"k8s.io/apimachinery/pkg/api/errors"
2017-01-11 14:09:48 +00:00
"k8s.io/apimachinery/pkg/api/meta"
2017-01-22 03:36:02 +00:00
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2017-11-14 04:00:18 +00:00
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
2017-01-11 14:09:48 +00:00
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
utilerrors "k8s.io/apimachinery/pkg/util/errors"
"k8s.io/apimachinery/pkg/util/yaml"
"k8s.io/apimachinery/pkg/watch"
2017-05-23 17:29:19 +00:00
"k8s.io/kubernetes/pkg/kubectl/validation"
2014-12-27 21:48:27 +00:00
)
2015-08-18 01:15:34 +00:00
const (
constSTDINstr string = "STDIN"
stopValidateMessage = "if you choose to ignore these errors, turn validation off with --validate=false"
)
2015-06-14 16:39:58 +00:00
2014-12-27 21:48:27 +00:00
// Visitor lets clients walk a list of resources.
type Visitor interface {
Visit ( VisitorFunc ) error
}
2015-06-15 02:48:56 +00:00
// VisitorFunc implements the Visitor interface for a matching function.
// If there was a problem walking a list of resources, the incoming error
// will describe the problem and the function can decide how to handle that error.
// A nil returned indicates to accept an error to continue loops even when errors happen.
// This is useful for ignoring certain kinds of errors or aggregating errors in some way.
type VisitorFunc func ( * Info , error ) error
2014-12-27 21:48:27 +00:00
// Watchable describes a resource that can be watched for changes that occur on the server,
// beginning after the provided resource version.
type Watchable interface {
Watch ( resourceVersion string ) ( watch . Interface , error )
}
// ResourceMapping allows an object to return the resource mapping associated with
// the resource or resources it represents.
type ResourceMapping interface {
ResourceMapping ( ) * meta . RESTMapping
}
// Info contains temporary info to execute a REST call, or show the results
// of an already completed REST call.
type Info struct {
2017-11-14 04:00:18 +00:00
Client RESTClient
// Mapping may be nil if the object has no available metadata, but is still parseable
// from disk.
Mapping * meta . RESTMapping
// Namespace will be set if the object is namespaced and has a specified value.
2014-12-27 21:48:27 +00:00
Namespace string
Name string
2015-06-23 12:26:27 +00:00
// Optional, Source is the filename or URL to template file (.json or .yaml),
// or stdin to use to handle the resource
Source string
2017-11-14 04:00:18 +00:00
// Optional, this is the most recent value returned by the server if available. It will
// typically be in unstructured or internal forms, depending on how the Builder was
// defined. If retrieved from the server, the Builder expects the mapping client to
// decide the final form. Use the AsVersioned, AsUnstructured, and AsInternal helpers
// to alter the object versions.
2016-12-09 00:28:48 +00:00
Object runtime . Object
2014-12-27 21:48:27 +00:00
// Optional, this is the most recent resource version the server knows about for
// this type of resource. It may not match the resource version of the object,
// but if set it should be equal to or newer than the resource version of the
// object (however the server defines resource version).
ResourceVersion string
2015-12-02 20:20:10 +00:00
// Optional, should this resource be exported, stripped of cluster-specific and instance specific fields
Export bool
2014-12-27 21:48:27 +00:00
}
// Visit implements Visitor
func ( i * Info ) Visit ( fn VisitorFunc ) error {
2015-06-15 02:48:56 +00:00
return fn ( i , nil )
2014-12-27 21:48:27 +00:00
}
2015-02-06 16:57:52 +00:00
// Get retrieves the object from the Namespace and Name fields
2015-12-02 20:20:10 +00:00
func ( i * Info ) Get ( ) ( err error ) {
obj , err := NewHelper ( i . Client , i . Mapping ) . Get ( i . Namespace , i . Name , i . Export )
2014-12-27 21:48:27 +00:00
if err != nil {
2017-01-22 03:36:02 +00:00
if errors . IsNotFound ( err ) && len ( i . Namespace ) > 0 && i . Namespace != metav1 . NamespaceDefault && i . Namespace != metav1 . NamespaceAll {
2016-08-30 03:57:59 +00:00
err2 := i . Client . Get ( ) . AbsPath ( "api" , "v1" , "namespaces" , i . Namespace ) . Do ( ) . Error ( )
if err2 != nil && errors . IsNotFound ( err2 ) {
return err2
}
}
2014-12-27 21:48:27 +00:00
return err
}
i . Object = obj
i . ResourceVersion , _ = i . Mapping . MetadataAccessor . ResourceVersion ( obj )
return nil
}
2015-02-06 16:57:52 +00:00
// Refresh updates the object with another object. If ignoreError is set
// the Object will be updated even if name, namespace, or resourceVersion
// attributes cannot be loaded from the object.
func ( i * Info ) Refresh ( obj runtime . Object , ignoreError bool ) error {
name , err := i . Mapping . MetadataAccessor . Name ( obj )
if err != nil {
if ! ignoreError {
return err
}
} else {
i . Name = name
}
namespace , err := i . Mapping . MetadataAccessor . Namespace ( obj )
if err != nil {
if ! ignoreError {
return err
}
} else {
i . Namespace = namespace
}
version , err := i . Mapping . MetadataAccessor . ResourceVersion ( obj )
if err != nil {
if ! ignoreError {
return err
}
} else {
i . ResourceVersion = version
}
i . Object = obj
return nil
}
2015-03-25 05:01:07 +00:00
// Namespaced returns true if the object belongs to a namespace
func ( i * Info ) Namespaced ( ) bool {
return i . Mapping != nil && i . Mapping . Scope . Name ( ) == meta . RESTScopeNameNamespace
}
2014-12-27 21:48:27 +00:00
// Watch returns server changes to this object after it was retrieved.
func ( i * Info ) Watch ( resourceVersion string ) ( watch . Interface , error ) {
return NewHelper ( i . Client , i . Mapping ) . WatchSingle ( i . Namespace , i . Name , resourceVersion )
}
// ResourceMapping returns the mapping for this resource and implements ResourceMapping
func ( i * Info ) ResourceMapping ( ) * meta . RESTMapping {
return i . Mapping
}
2017-11-14 04:00:18 +00:00
// Internal attempts to convert the provided object to an internal type or returns an error.
func ( i * Info ) Internal ( ) ( runtime . Object , error ) {
return i . Mapping . ConvertToVersion ( i . Object , i . Mapping . GroupVersionKind . GroupKind ( ) . WithVersion ( runtime . APIVersionInternal ) . GroupVersion ( ) )
}
// AsInternal returns the object in internal form if possible, or i.Object if it cannot be
// converted.
func ( i * Info ) AsInternal ( ) runtime . Object {
if obj , err := i . Internal ( ) ; err == nil {
return obj
}
return i . Object
}
// Versioned returns the object as a Go type in the mapping's version or returns an error.
func ( i * Info ) Versioned ( ) ( runtime . Object , error ) {
return i . Mapping . ConvertToVersion ( i . Object , i . Mapping . GroupVersionKind . GroupVersion ( ) )
}
// AsVersioned returns the object as a Go object in the external form if possible (matching the
// group version kind of the mapping, or i.Object if it cannot be converted.
func ( i * Info ) AsVersioned ( ) runtime . Object {
if obj , err := i . Versioned ( ) ; err == nil {
return obj
}
return i . Object
}
// Unstructured returns the current object in unstructured form (as a runtime.Unstructured)
func ( i * Info ) Unstructured ( ) ( runtime . Unstructured , error ) {
switch t := i . Object . ( type ) {
case runtime . Unstructured :
return t , nil
case * runtime . Unknown :
gvk := i . Mapping . GroupVersionKind
out , _ , err := unstructured . UnstructuredJSONScheme . Decode ( t . Raw , & gvk , nil )
return out . ( runtime . Unstructured ) , err
default :
out := & unstructured . Unstructured { }
if err := i . Mapping . Convert ( i . Object , out , nil ) ; err != nil {
return nil , err
}
return out , nil
}
}
// AsUnstructured returns the object as a Go object in external form as a runtime.Unstructured
// (map of JSON equivalent values) or as i.Object if it cannot be converted.
func ( i * Info ) AsUnstructured ( ) runtime . Object {
if out , err := i . Unstructured ( ) ; err == nil {
return out
}
return i . Object
}
2014-12-27 21:48:27 +00:00
// VisitorList implements Visit for the sub visitors it contains. The first error
// returned from a child Visitor will terminate iteration.
type VisitorList [ ] Visitor
// Visit implements Visitor
func ( l VisitorList ) Visit ( fn VisitorFunc ) error {
for i := range l {
if err := l [ i ] . Visit ( fn ) ; err != nil {
return err
}
}
return nil
}
// EagerVisitorList implements Visit for the sub visitors it contains. All errors
// will be captured and returned at the end of iteration.
type EagerVisitorList [ ] Visitor
// Visit implements Visitor, and gathers errors that occur during processing until
// all sub visitors have been visited.
func ( l EagerVisitorList ) Visit ( fn VisitorFunc ) error {
errs := [ ] error ( nil )
for i := range l {
2015-06-15 02:48:56 +00:00
if err := l [ i ] . Visit ( func ( info * Info , err error ) error {
if err != nil {
errs = append ( errs , err )
return nil
}
if err := fn ( info , nil ) ; err != nil {
2014-12-27 21:48:27 +00:00
errs = append ( errs , err )
}
return nil
} ) ; err != nil {
errs = append ( errs , err )
}
}
2015-10-14 05:18:37 +00:00
return utilerrors . NewAggregate ( errs )
2014-12-27 21:48:27 +00:00
}
2015-05-07 20:53:43 +00:00
func ValidateSchema ( data [ ] byte , schema validation . Schema ) error {
if schema == nil {
return nil
}
if err := schema . ValidateBytes ( data ) ; err != nil {
2015-08-18 01:15:34 +00:00
return fmt . Errorf ( "error validating data: %v; %s" , err , stopValidateMessage )
2015-05-07 20:53:43 +00:00
}
return nil
}
2014-12-27 21:48:27 +00:00
// URLVisitor downloads the contents of a URL, and if successful, returns
// an info object representing the downloaded object.
type URLVisitor struct {
2016-02-25 20:43:48 +00:00
URL * url . URL
* StreamVisitor
2016-05-09 23:19:39 +00:00
HttpAttemptCount int
2014-12-27 21:48:27 +00:00
}
func ( v * URLVisitor ) Visit ( fn VisitorFunc ) error {
2016-05-09 23:19:39 +00:00
body , err := readHttpWithRetries ( httpgetImpl , time . Second , v . URL . String ( ) , v . HttpAttemptCount )
2014-12-27 21:48:27 +00:00
if err != nil {
2015-06-25 08:41:45 +00:00
return err
2014-12-27 21:48:27 +00:00
}
2016-05-05 22:57:25 +00:00
defer body . Close ( )
v . StreamVisitor . Reader = body
return v . StreamVisitor . Visit ( fn )
}
2016-05-09 23:19:39 +00:00
// readHttpWithRetries tries to http.Get the v.URL retries times before giving up.
func readHttpWithRetries ( get httpget , duration time . Duration , u string , attempts int ) ( io . ReadCloser , error ) {
2016-05-05 22:57:25 +00:00
var err error
var body io . ReadCloser
2016-05-09 23:19:39 +00:00
if attempts <= 0 {
return nil , fmt . Errorf ( "http attempts must be greater than 0, was %d" , attempts )
}
for i := 0 ; i < attempts ; i ++ {
2016-05-05 22:57:25 +00:00
var statusCode int
var status string
if i > 0 {
time . Sleep ( duration )
}
// Try to get the URL
statusCode , status , body , err = get ( u )
// Retry Errors
if err != nil {
continue
}
// Error - Set the error condition from the StatusCode
2016-11-23 09:17:16 +00:00
if statusCode != http . StatusOK {
2016-05-26 13:20:42 +00:00
err = fmt . Errorf ( "unable to read URL %q, server reported %s, status code=%d" , u , status , statusCode )
2016-05-05 22:57:25 +00:00
}
if statusCode >= 500 && statusCode < 600 {
// Retry 500's
continue
} else {
// Don't retry other StatusCodes
break
}
2014-12-27 21:48:27 +00:00
}
2016-05-05 22:57:25 +00:00
return body , err
}
2016-02-25 20:43:48 +00:00
2016-05-05 22:57:25 +00:00
// httpget Defines function to retrieve a url and return the results. Exists for unit test stubbing.
type httpget func ( url string ) ( int , string , io . ReadCloser , error )
// httpgetImpl Implements a function to retrieve a url and return the results.
func httpgetImpl ( url string ) ( int , string , io . ReadCloser , error ) {
resp , err := http . Get ( url )
if err != nil {
return 0 , "" , nil , err
}
return resp . StatusCode , resp . Status , resp . Body , nil
2014-12-27 21:48:27 +00:00
}
// DecoratedVisitor will invoke the decorators in order prior to invoking the visitor function
// passed to Visit. An error will terminate the visit.
type DecoratedVisitor struct {
visitor Visitor
decorators [ ] VisitorFunc
}
// NewDecoratedVisitor will create a visitor that invokes the provided visitor functions before
// the user supplied visitor function is invoked, giving them the opportunity to mutate the Info
// object or terminate early with an error.
func NewDecoratedVisitor ( v Visitor , fn ... VisitorFunc ) Visitor {
if len ( fn ) == 0 {
return v
}
return DecoratedVisitor { v , fn }
}
// Visit implements Visitor
func ( v DecoratedVisitor ) Visit ( fn VisitorFunc ) error {
2015-06-15 02:48:56 +00:00
return v . visitor . Visit ( func ( info * Info , err error ) error {
if err != nil {
return err
}
2014-12-27 21:48:27 +00:00
for i := range v . decorators {
2015-06-15 02:48:56 +00:00
if err := v . decorators [ i ] ( info , nil ) ; err != nil {
2014-12-27 21:48:27 +00:00
return err
}
}
2015-06-15 02:48:56 +00:00
return fn ( info , nil )
2014-12-27 21:48:27 +00:00
} )
}
2015-05-14 04:35:11 +00:00
// ContinueOnErrorVisitor visits each item and, if an error occurs on
// any individual item, returns an aggregate error after all items
// are visited.
type ContinueOnErrorVisitor struct {
Visitor
}
// Visit returns nil if no error occurs during traversal, a regular
// error if one occurs, or if multiple errors occur, an aggregate
// error. If the provided visitor fails on any individual item it
// will not prevent the remaining items from being visited. An error
// returned by the visitor directly may still result in some items
// not being visited.
func ( v ContinueOnErrorVisitor ) Visit ( fn VisitorFunc ) error {
errs := [ ] error { }
2015-06-15 02:48:56 +00:00
err := v . Visitor . Visit ( func ( info * Info , err error ) error {
if err != nil {
errs = append ( errs , err )
return nil
}
if err := fn ( info , nil ) ; err != nil {
2015-05-14 04:35:11 +00:00
errs = append ( errs , err )
}
return nil
} )
if err != nil {
errs = append ( errs , err )
}
if len ( errs ) == 1 {
return errs [ 0 ]
}
2015-10-14 05:18:37 +00:00
return utilerrors . NewAggregate ( errs )
2015-05-14 04:35:11 +00:00
}
2014-12-27 21:48:27 +00:00
// FlattenListVisitor flattens any objects that runtime.ExtractList recognizes as a list
// - has an "Items" public field that is a slice of runtime.Objects or objects satisfying
// that interface - into multiple Infos. An error on any sub item (for instance, if a List
// contains an object that does not have a registered client or resource) will terminate
// the visit.
// TODO: allow errors to be aggregated?
type FlattenListVisitor struct {
Visitor
* Mapper
}
// NewFlattenListVisitor creates a visitor that will expand list style runtime.Objects
// into individual items and then visit them individually.
func NewFlattenListVisitor ( v Visitor , mapper * Mapper ) Visitor {
return FlattenListVisitor { v , mapper }
}
func ( v FlattenListVisitor ) Visit ( fn VisitorFunc ) error {
2015-06-15 02:48:56 +00:00
return v . Visitor . Visit ( func ( info * Info , err error ) error {
if err != nil {
return err
}
2014-12-27 21:48:27 +00:00
if info . Object == nil {
2015-06-15 02:48:56 +00:00
return fn ( info , nil )
2014-12-27 21:48:27 +00:00
}
2015-11-12 10:45:42 +00:00
items , err := meta . ExtractList ( info . Object )
2014-12-27 21:48:27 +00:00
if err != nil {
2015-06-15 02:48:56 +00:00
return fn ( info , nil )
2014-12-27 21:48:27 +00:00
}
2017-11-14 03:56:59 +00:00
if errs := runtime . DecodeList ( items , v . Mapper . Decoder ) ; len ( errs ) > 0 {
2015-10-14 05:18:37 +00:00
return utilerrors . NewAggregate ( errs )
2015-04-29 03:15:16 +00:00
}
2016-03-11 04:49:00 +00:00
// If we have a GroupVersionKind on the list, prioritize that when asking for info on the objects contained in the list
2016-11-21 02:55:31 +00:00
var preferredGVKs [ ] schema . GroupVersionKind
2016-08-17 21:09:51 +00:00
if info . Mapping != nil && ! info . Mapping . GroupVersionKind . Empty ( ) {
2016-03-11 04:49:00 +00:00
preferredGVKs = append ( preferredGVKs , info . Mapping . GroupVersionKind )
}
2014-12-27 21:48:27 +00:00
for i := range items {
2016-03-11 04:49:00 +00:00
item , err := v . InfoForObject ( items [ i ] , preferredGVKs )
2014-12-27 21:48:27 +00:00
if err != nil {
return err
}
if len ( info . ResourceVersion ) != 0 {
item . ResourceVersion = info . ResourceVersion
}
2015-06-15 02:48:56 +00:00
if err := fn ( item , nil ) ; err != nil {
2014-12-27 21:48:27 +00:00
return err
}
}
return nil
} )
}
2015-06-14 16:39:58 +00:00
func ignoreFile ( path string , extensions [ ] string ) bool {
if len ( extensions ) == 0 {
return false
}
ext := filepath . Ext ( path )
for _ , s := range extensions {
if s == ext {
return false
}
}
return true
}
// FileVisitorForSTDIN return a special FileVisitor just for STDIN
2015-06-15 02:48:56 +00:00
func FileVisitorForSTDIN ( mapper * Mapper , schema validation . Schema ) Visitor {
2015-06-14 16:39:58 +00:00
return & FileVisitor {
Path : constSTDINstr ,
2015-06-15 02:48:56 +00:00
StreamVisitor : NewStreamVisitor ( nil , mapper , constSTDINstr , schema ) ,
2015-06-14 16:39:58 +00:00
}
}
// ExpandPathsToFileVisitors will return a slice of FileVisitors that will handle files from the provided path.
2016-08-02 16:51:51 +00:00
// After FileVisitors open the files, they will pass an io.Reader to a StreamVisitor to do the reading. (stdin
2015-06-14 16:39:58 +00:00
// is also taken care of). Paths argument also accepts a single file, and will return a single visitor
2015-06-15 02:48:56 +00:00
func ExpandPathsToFileVisitors ( mapper * Mapper , paths string , recursive bool , extensions [ ] string , schema validation . Schema ) ( [ ] Visitor , error ) {
2015-06-14 16:39:58 +00:00
var visitors [ ] Visitor
err := filepath . Walk ( paths , func ( path string , fi os . FileInfo , err error ) error {
if err != nil {
return err
}
if fi . IsDir ( ) {
if path != paths && ! recursive {
return filepath . SkipDir
}
return nil
}
2015-07-07 20:43:55 +00:00
// Don't check extension if the filepath was passed explicitly
if path != paths && ignoreFile ( path , extensions ) {
2015-06-14 16:39:58 +00:00
return nil
}
visitor := & FileVisitor {
Path : path ,
2015-06-15 02:48:56 +00:00
StreamVisitor : NewStreamVisitor ( nil , mapper , path , schema ) ,
2015-06-14 16:39:58 +00:00
}
visitors = append ( visitors , visitor )
return nil
} )
if err != nil {
return nil , err
}
return visitors , nil
}
// FileVisitor is wrapping around a StreamVisitor, to handle open/close files
type FileVisitor struct {
Path string
* StreamVisitor
}
// Visit in a FileVisitor is just taking care of opening/closing files
func ( v * FileVisitor ) Visit ( fn VisitorFunc ) error {
var f * os . File
if v . Path == constSTDINstr {
f = os . Stdin
} else {
var err error
2017-07-13 10:24:46 +00:00
f , err = os . Open ( v . Path )
if err != nil {
2015-06-25 08:41:45 +00:00
return err
2015-06-14 16:39:58 +00:00
}
2017-07-13 10:24:46 +00:00
defer f . Close ( )
2015-06-14 16:39:58 +00:00
}
2016-12-20 07:02:39 +00:00
// TODO: Consider adding a flag to force to UTF16, apparently some
// Windows tools don't write the BOM
utf16bom := unicode . BOMOverride ( unicode . UTF8 . NewDecoder ( ) )
v . StreamVisitor . Reader = transform . NewReader ( f , utf16bom )
2015-06-14 16:39:58 +00:00
return v . StreamVisitor . Visit ( fn )
}
2014-12-27 21:48:27 +00:00
// StreamVisitor reads objects from an io.Reader and walks them. A stream visitor can only be
// visited once.
// TODO: depends on objects being in JSON format before being passed to decode - need to implement
// a stream decoder method on runtime.Codec to properly handle this.
type StreamVisitor struct {
io . Reader
* Mapper
2015-06-15 02:48:56 +00:00
Source string
Schema validation . Schema
2014-12-27 21:48:27 +00:00
}
2015-06-14 16:39:58 +00:00
// NewStreamVisitor is a helper function that is useful when we want to change the fields of the struct but keep calls the same.
2015-06-15 02:48:56 +00:00
func NewStreamVisitor ( r io . Reader , mapper * Mapper , source string , schema validation . Schema ) * StreamVisitor {
2015-06-14 16:39:58 +00:00
return & StreamVisitor {
2015-06-15 02:48:56 +00:00
Reader : r ,
Mapper : mapper ,
Source : source ,
Schema : schema ,
2015-06-14 16:39:58 +00:00
}
2014-12-27 21:48:27 +00:00
}
2015-06-14 16:39:58 +00:00
// Visit implements Visitor over a stream. StreamVisitor is able to distinct multiple resources in one stream.
2014-12-27 21:48:27 +00:00
func ( v * StreamVisitor ) Visit ( fn VisitorFunc ) error {
2015-01-14 05:03:56 +00:00
d := yaml . NewYAMLOrJSONDecoder ( v . Reader , 4096 )
2014-12-27 21:48:27 +00:00
for {
ext := runtime . RawExtension { }
if err := d . Decode ( & ext ) ; err != nil {
if err == io . EOF {
return nil
}
return err
}
2016-03-17 10:32:14 +00:00
// TODO: This needs to be able to handle object in other encodings and schemas.
ext . Raw = bytes . TrimSpace ( ext . Raw )
if len ( ext . Raw ) == 0 || bytes . Equal ( ext . Raw , [ ] byte ( "null" ) ) {
2015-01-14 18:06:53 +00:00
continue
}
2016-03-17 10:32:14 +00:00
if err := ValidateSchema ( ext . Raw , v . Schema ) ; err != nil {
2015-06-25 08:41:45 +00:00
return fmt . Errorf ( "error validating %q: %v" , v . Source , err )
2015-05-07 20:53:43 +00:00
}
2016-03-17 10:32:14 +00:00
info , err := v . InfoForData ( ext . Raw , v . Source )
2014-12-27 21:48:27 +00:00
if err != nil {
2015-06-15 02:48:56 +00:00
if fnErr := fn ( info , err ) ; fnErr != nil {
return fnErr
2014-12-27 21:48:27 +00:00
}
2015-06-15 02:48:56 +00:00
continue
2014-12-27 21:48:27 +00:00
}
2015-06-15 02:48:56 +00:00
if err := fn ( info , nil ) ; err != nil {
2014-12-27 21:48:27 +00:00
return err
}
}
}
2015-06-15 02:48:56 +00:00
func UpdateObjectNamespace ( info * Info , err error ) error {
if err != nil {
return err
}
2014-12-27 21:48:27 +00:00
if info . Object != nil {
return info . Mapping . MetadataAccessor . SetNamespace ( info . Object , info . Namespace )
}
return nil
}
2015-01-29 23:00:42 +00:00
// FilterNamespace omits the namespace if the object is not namespace scoped
2015-06-15 02:48:56 +00:00
func FilterNamespace ( info * Info , err error ) error {
if err != nil {
return err
}
2015-03-25 05:01:07 +00:00
if ! info . Namespaced ( ) {
info . Namespace = ""
2015-06-15 02:48:56 +00:00
UpdateObjectNamespace ( info , nil )
2015-01-29 23:00:42 +00:00
}
2015-03-25 05:01:07 +00:00
return nil
2015-01-29 23:00:42 +00:00
}
2014-12-27 21:48:27 +00:00
// SetNamespace ensures that every Info object visited will have a namespace
// set. If info.Object is set, it will be mutated as well.
func SetNamespace ( namespace string ) VisitorFunc {
2015-06-15 02:48:56 +00:00
return func ( info * Info , err error ) error {
if err != nil {
return err
}
2015-05-08 03:51:36 +00:00
if ! info . Namespaced ( ) {
return nil
}
2014-12-27 21:48:27 +00:00
if len ( info . Namespace ) == 0 {
info . Namespace = namespace
2015-06-15 02:48:56 +00:00
UpdateObjectNamespace ( info , nil )
2014-12-27 21:48:27 +00:00
}
return nil
}
}
// RequireNamespace will either set a namespace if none is provided on the
// Info object, or if the namespace is set and does not match the provided
// value, returns an error. This is intended to guard against administrators
// accidentally operating on resources outside their namespace.
func RequireNamespace ( namespace string ) VisitorFunc {
2015-06-15 02:48:56 +00:00
return func ( info * Info , err error ) error {
if err != nil {
return err
}
2015-03-25 05:01:07 +00:00
if ! info . Namespaced ( ) {
return nil
}
2014-12-27 21:48:27 +00:00
if len ( info . Namespace ) == 0 {
info . Namespace = namespace
2015-06-15 02:48:56 +00:00
UpdateObjectNamespace ( info , nil )
2014-12-27 21:48:27 +00:00
return nil
}
if info . Namespace != namespace {
return fmt . Errorf ( "the namespace from the provided object %q does not match the namespace %q. You must pass '--namespace=%s' to perform this operation." , info . Namespace , namespace , info . Namespace )
}
return nil
}
}
// RetrieveLatest updates the Object on each Info by invoking a standard client
// Get.
2015-06-15 02:48:56 +00:00
func RetrieveLatest ( info * Info , err error ) error {
if err != nil {
return err
}
2015-11-12 10:45:42 +00:00
if meta . IsListType ( info . Object ) {
2015-10-14 22:06:06 +00:00
return fmt . Errorf ( "watch is only supported on individual resources and resource collections, but a list of resources is found" )
}
2015-03-25 05:01:07 +00:00
if len ( info . Name ) == 0 {
2014-12-27 21:48:27 +00:00
return nil
}
2015-03-25 05:01:07 +00:00
if info . Namespaced ( ) && len ( info . Namespace ) == 0 {
return fmt . Errorf ( "no namespace set on resource %s %q" , info . Mapping . Resource , info . Name )
}
2016-08-08 22:30:56 +00:00
return info . Get ( )
2014-12-27 21:48:27 +00:00
}
2015-03-25 05:01:07 +00:00
// RetrieveLazy updates the object if it has not been loaded yet.
2015-06-15 02:48:56 +00:00
func RetrieveLazy ( info * Info , err error ) error {
if err != nil {
return err
}
2015-03-25 05:01:07 +00:00
if info . Object == nil {
return info . Get ( )
}
return nil
}
2016-09-13 20:10:21 +00:00
2017-04-18 12:44:25 +00:00
// CreateAndRefresh creates an object from input info and refreshes info with that object
func CreateAndRefresh ( info * Info ) error {
obj , err := NewHelper ( info . Client , info . Mapping ) . Create ( info . Namespace , true , info . Object )
if err != nil {
return err
}
info . Refresh ( obj , true )
return nil
}
2016-09-13 20:10:21 +00:00
type FilterFunc func ( info * Info , err error ) ( bool , error )
type FilteredVisitor struct {
visitor Visitor
filters [ ] FilterFunc
}
func NewFilteredVisitor ( v Visitor , fn ... FilterFunc ) Visitor {
if len ( fn ) == 0 {
return v
}
return FilteredVisitor { v , fn }
}
func ( v FilteredVisitor ) Visit ( fn VisitorFunc ) error {
return v . visitor . Visit ( func ( info * Info , err error ) error {
if err != nil {
return err
}
for _ , filter := range v . filters {
ok , err := filter ( info , nil )
if err != nil {
return err
}
if ! ok {
return nil
}
}
return fn ( info , nil )
} )
}
2017-08-04 06:54:17 +00:00
func FilterByLabelSelector ( s labels . Selector ) FilterFunc {
2016-09-13 20:10:21 +00:00
return func ( info * Info , err error ) ( bool , error ) {
if err != nil {
return false , err
}
a , err := meta . Accessor ( info . Object )
if err != nil {
return false , err
}
if ! s . Matches ( labels . Set ( a . GetLabels ( ) ) ) {
return false , nil
}
return true , nil
}
}
2017-02-12 20:06:17 +00:00
type InfoListVisitor [ ] * Info
func ( infos InfoListVisitor ) Visit ( fn VisitorFunc ) error {
var err error
for _ , i := range infos {
err = fn ( i , err )
}
return err
}