2014-12-27 21:48:27 +00:00
/ *
2015-05-01 16:19:44 +00:00
Copyright 2014 The Kubernetes Authors All rights reserved .
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 (
"fmt"
"io"
"net/url"
"os"
"strings"
2015-08-05 22:03:47 +00:00
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/meta"
2015-12-07 13:12:24 +00:00
"k8s.io/kubernetes/pkg/api/unversioned"
2015-08-05 22:03:47 +00:00
"k8s.io/kubernetes/pkg/api/validation"
"k8s.io/kubernetes/pkg/labels"
"k8s.io/kubernetes/pkg/runtime"
2015-10-14 05:18:37 +00:00
utilerrors "k8s.io/kubernetes/pkg/util/errors"
2015-09-09 17:45:01 +00:00
"k8s.io/kubernetes/pkg/util/sets"
2014-12-27 21:48:27 +00:00
)
2015-10-29 02:00:34 +00:00
var FileExtensions = [ ] string { ".json" , ".yaml" , ".yml" }
var InputExtensions = append ( FileExtensions , "stdin" )
2015-09-01 23:36:55 +00:00
2014-12-27 21:48:27 +00:00
// Builder provides convenience functions for taking arguments and parameters
// from the command line and converting them to a list of resources to iterate
// over using the Visitor interface.
type Builder struct {
mapper * Mapper
errs [ ] error
paths [ ] Visitor
stream bool
dir bool
2015-02-12 14:59:58 +00:00
selector labels . Selector
selectAll bool
2014-12-27 21:48:27 +00:00
resources [ ] string
namespace string
2015-02-19 14:17:23 +00:00
names [ ] string
2014-12-27 21:48:27 +00:00
2015-03-25 05:01:07 +00:00
resourceTuples [ ] resourceTuple
2014-12-27 21:48:27 +00:00
defaultNamespace bool
requireNamespace bool
flatten bool
latest bool
2015-04-20 19:00:52 +00:00
requireObject bool
2014-12-27 21:48:27 +00:00
singleResourceType bool
continueOnError bool
2015-05-07 20:53:43 +00:00
2015-12-02 20:20:10 +00:00
export bool
2015-05-07 20:53:43 +00:00
schema validation . Schema
2014-12-27 21:48:27 +00:00
}
2015-03-25 05:01:07 +00:00
type resourceTuple struct {
Resource string
Name string
}
2014-12-27 21:48:27 +00:00
// NewBuilder creates a builder that operates on generic objects.
2015-12-21 05:37:49 +00:00
func NewBuilder ( mapper meta . RESTMapper , typer runtime . ObjectTyper , clientMapper ClientMapper , decoder runtime . Decoder ) * Builder {
2014-12-27 21:48:27 +00:00
return & Builder {
2015-12-21 05:37:49 +00:00
mapper : & Mapper { typer , mapper , clientMapper , decoder } ,
2015-04-20 19:00:52 +00:00
requireObject : true ,
2014-12-27 21:48:27 +00:00
}
}
2015-05-07 20:53:43 +00:00
func ( b * Builder ) Schema ( schema validation . Schema ) * Builder {
b . schema = schema
return b
}
2015-06-14 16:39:58 +00:00
// FilenameParam groups input in two categories: URLs and files (files, directories, STDIN)
2015-06-26 20:49:34 +00:00
// If enforceNamespace is false, namespaces in the specs will be allowed to
// override the default namespace. If it is true, namespaces that don't match
// will cause an error.
2015-06-14 16:39:58 +00:00
// If ContinueOnError() is set prior to this method, objects on the path that are not
// recognized will be ignored (but logged at V(2)).
2016-03-28 19:44:21 +00:00
func ( b * Builder ) FilenameParam ( enforceNamespace , recursive bool , paths ... string ) * Builder {
2014-12-27 21:48:27 +00:00
for _ , s := range paths {
switch {
case s == "-" :
b . Stdin ( )
case strings . Index ( s , "http://" ) == 0 || strings . Index ( s , "https://" ) == 0 :
url , err := url . Parse ( s )
if err != nil {
b . errs = append ( b . errs , fmt . Errorf ( "the URL passed to filename %q is not valid: %v" , s , err ) )
continue
}
b . URL ( url )
default :
2016-03-28 19:44:21 +00:00
b . Path ( recursive , s )
2014-12-27 21:48:27 +00:00
}
}
2015-06-26 20:49:34 +00:00
if enforceNamespace {
b . RequireNamespace ( )
}
2014-12-27 21:48:27 +00:00
return b
}
// URL accepts a number of URLs directly.
func ( b * Builder ) URL ( urls ... * url . URL ) * Builder {
for _ , u := range urls {
b . paths = append ( b . paths , & URLVisitor {
2016-02-25 20:43:48 +00:00
URL : u ,
StreamVisitor : NewStreamVisitor ( nil , b . mapper , u . String ( ) , b . schema ) ,
2014-12-27 21:48:27 +00:00
} )
}
return b
}
// Stdin will read objects from the standard input. If ContinueOnError() is set
// prior to this method being called, objects in the stream that are unrecognized
// will be ignored (but logged at V(2)).
func ( b * Builder ) Stdin ( ) * Builder {
2015-06-14 16:39:58 +00:00
b . stream = true
2015-06-15 02:48:56 +00:00
b . paths = append ( b . paths , FileVisitorForSTDIN ( b . mapper , b . schema ) )
2015-06-14 16:39:58 +00:00
return b
2014-12-27 21:48:27 +00:00
}
// Stream will read objects from the provided reader, and if an error occurs will
// include the name string in the error message. If ContinueOnError() is set
// prior to this method being called, objects in the stream that are unrecognized
// will be ignored (but logged at V(2)).
func ( b * Builder ) Stream ( r io . Reader , name string ) * Builder {
b . stream = true
2015-06-15 02:48:56 +00:00
b . paths = append ( b . paths , NewStreamVisitor ( r , b . mapper , name , b . schema ) )
2014-12-27 21:48:27 +00:00
return b
}
2015-06-14 16:39:58 +00:00
// Path accepts a set of paths that may be files, directories (all can containing
// one or more resources). Creates a FileVisitor for each file and then each
// FileVisitor is streaming the content to a StreamVisitor. If ContinueOnError() is set
// prior to this method being called, objects on the path that are unrecognized will be
// ignored (but logged at V(2)).
2016-03-28 19:44:21 +00:00
func ( b * Builder ) Path ( recursive bool , paths ... string ) * Builder {
2014-12-27 21:48:27 +00:00
for _ , p := range paths {
2015-06-14 16:39:58 +00:00
_ , err := os . Stat ( p )
2014-12-27 21:48:27 +00:00
if os . IsNotExist ( err ) {
b . errs = append ( b . errs , fmt . Errorf ( "the path %q does not exist" , p ) )
continue
}
if err != nil {
b . errs = append ( b . errs , fmt . Errorf ( "the path %q cannot be accessed: %v" , p , err ) )
continue
}
2015-06-14 16:39:58 +00:00
2016-03-28 19:44:21 +00:00
visitors , err := ExpandPathsToFileVisitors ( b . mapper , p , recursive , FileExtensions , b . schema )
2015-06-14 16:39:58 +00:00
if err != nil {
b . errs = append ( b . errs , fmt . Errorf ( "error reading %q: %v" , p , err ) )
}
if len ( visitors ) > 1 {
2014-12-27 21:48:27 +00:00
b . dir = true
}
2015-06-14 16:39:58 +00:00
b . paths = append ( b . paths , visitors ... )
2014-12-27 21:48:27 +00:00
}
return b
}
// ResourceTypes is a list of types of resources to operate on, when listing objects on
// the server or retrieving objects that match a selector.
func ( b * Builder ) ResourceTypes ( types ... string ) * Builder {
b . resources = append ( b . resources , types ... )
return b
}
2015-10-19 17:42:19 +00:00
// ResourceNames accepts a default type and one or more names, and creates tuples of
2015-07-09 09:05:13 +00:00
// resources
func ( b * Builder ) ResourceNames ( resource string , names ... string ) * Builder {
for _ , name := range names {
2015-10-19 17:42:19 +00:00
// See if this input string is of type/name format
tuple , ok , err := splitResourceTypeName ( name )
2015-07-09 09:05:13 +00:00
if err != nil {
b . errs = append ( b . errs , err )
return b
}
if ok {
b . resourceTuples = append ( b . resourceTuples , tuple )
continue
}
// Use the given default type to create a resource tuple
b . resourceTuples = append ( b . resourceTuples , resourceTuple { Resource : resource , Name : name } )
}
return b
}
2014-12-27 21:48:27 +00:00
// SelectorParam defines a selector that should be applied to the object types to load.
// This will not affect files loaded from disk or URL. If the parameter is empty it is
// a no-op - to select all resources invoke `b.Selector(labels.Everything)`.
func ( b * Builder ) SelectorParam ( s string ) * Builder {
2015-02-25 16:19:10 +00:00
selector , err := labels . Parse ( s )
2014-12-27 21:48:27 +00:00
if err != nil {
b . errs = append ( b . errs , fmt . Errorf ( "the provided selector %q is not valid: %v" , s , err ) )
2015-01-14 16:07:05 +00:00
return b
2014-12-27 21:48:27 +00:00
}
if selector . Empty ( ) {
return b
}
2015-02-12 14:59:58 +00:00
if b . selectAll {
b . errs = append ( b . errs , fmt . Errorf ( "found non empty selector %q with previously set 'all' parameter. " , s ) )
return b
}
2014-12-27 21:48:27 +00:00
return b . Selector ( selector )
}
// Selector accepts a selector directly, and if non nil will trigger a list action.
func ( b * Builder ) Selector ( selector labels . Selector ) * Builder {
b . selector = selector
return b
}
2015-12-02 20:20:10 +00:00
// ExportParam accepts the export boolean for these resources
func ( b * Builder ) ExportParam ( export bool ) * Builder {
b . export = export
return b
}
2015-06-14 16:39:58 +00:00
// NamespaceParam accepts the namespace that these resources should be
// considered under from - used by DefaultNamespace() and RequireNamespace()
2014-12-27 21:48:27 +00:00
func ( b * Builder ) NamespaceParam ( namespace string ) * Builder {
b . namespace = namespace
return b
}
// DefaultNamespace instructs the builder to set the namespace value for any object found
// to NamespaceParam() if empty.
func ( b * Builder ) DefaultNamespace ( ) * Builder {
b . defaultNamespace = true
return b
}
2015-05-12 11:14:31 +00:00
// AllNamespaces instructs the builder to use NamespaceAll as a namespace to request resources
// acroll all namespace. This overrides the namespace set by NamespaceParam().
func ( b * Builder ) AllNamespaces ( allNamespace bool ) * Builder {
if allNamespace {
b . namespace = api . NamespaceAll
}
return b
}
2014-12-27 21:48:27 +00:00
// RequireNamespace instructs the builder to set the namespace value for any object found
// to NamespaceParam() if empty, and if the value on the resource does not match
// NamespaceParam() an error will be returned.
func ( b * Builder ) RequireNamespace ( ) * Builder {
b . requireNamespace = true
return b
}
2015-02-12 14:59:58 +00:00
// SelectEverythingParam
func ( b * Builder ) SelectAllParam ( selectAll bool ) * Builder {
if selectAll && b . selector != nil {
b . errs = append ( b . errs , fmt . Errorf ( "setting 'all' parameter but found a non empty selector. " ) )
return b
}
b . selectAll = selectAll
return b
}
2015-10-19 17:42:19 +00:00
// ResourceTypeOrNameArgs indicates that the builder should accept arguments
// of the form `(<type1>[,<type2>,...]|<type> <name1>[,<name2>,...])`. When one argument is
// received, the types provided will be retrieved from the server (and be comma delimited).
// When two or more arguments are received, they must be a single type and resource name(s).
2015-02-19 14:17:23 +00:00
// The allowEmptySelector permits to select all the resources (via Everything func).
2015-02-12 14:59:58 +00:00
func ( b * Builder ) ResourceTypeOrNameArgs ( allowEmptySelector bool , args ... string ) * Builder {
2015-12-03 19:32:22 +00:00
args = normalizeMultipleResourcesArgs ( args )
2015-10-19 17:42:19 +00:00
if ok , err := hasCombinedTypeArgs ( args ) ; ok {
2015-03-25 05:01:07 +00:00
if err != nil {
b . errs = append ( b . errs , err )
return b
}
for _ , s := range args {
2015-10-19 17:42:19 +00:00
tuple , ok , err := splitResourceTypeName ( s )
2015-07-09 09:05:13 +00:00
if err != nil {
b . errs = append ( b . errs , err )
2015-03-25 05:01:07 +00:00
return b
}
2015-07-09 09:05:13 +00:00
if ok {
b . resourceTuples = append ( b . resourceTuples , tuple )
2015-03-25 05:01:07 +00:00
}
}
return b
}
2015-07-20 08:33:50 +00:00
if len ( args ) > 0 {
// Try replacing aliases only in types
args [ 0 ] = b . replaceAliases ( args [ 0 ] )
}
2015-02-19 14:17:23 +00:00
switch {
case len ( args ) > 2 :
b . names = append ( b . names , args [ 1 : ] ... )
b . ResourceTypes ( SplitResourceArgument ( args [ 0 ] ) ... )
case len ( args ) == 2 :
b . names = append ( b . names , args [ 1 ] )
2014-12-27 21:48:27 +00:00
b . ResourceTypes ( SplitResourceArgument ( args [ 0 ] ) ... )
2015-02-19 14:17:23 +00:00
case len ( args ) == 1 :
2014-12-27 21:48:27 +00:00
b . ResourceTypes ( SplitResourceArgument ( args [ 0 ] ) ... )
2015-02-12 14:59:58 +00:00
if b . selector == nil && allowEmptySelector {
2014-12-27 21:48:27 +00:00
b . selector = labels . Everything ( )
}
2015-02-19 14:17:23 +00:00
case len ( args ) == 0 :
2014-12-27 21:48:27 +00:00
default :
b . errs = append ( b . errs , fmt . Errorf ( "when passing arguments, must be resource or resource and name" ) )
}
return b
}
2015-07-20 08:33:50 +00:00
// replaceAliases accepts an argument and tries to expand any existing
// aliases found in it
func ( b * Builder ) replaceAliases ( input string ) string {
2015-04-08 15:05:41 +00:00
replaced := [ ] string { }
2015-07-20 08:33:50 +00:00
for _ , arg := range strings . Split ( input , "," ) {
2015-10-19 17:42:19 +00:00
if aliases , ok := b . mapper . AliasesForResource ( arg ) ; ok {
2015-04-08 15:05:41 +00:00
arg = strings . Join ( aliases , "," )
}
replaced = append ( replaced , arg )
}
2015-07-20 08:33:50 +00:00
return strings . Join ( replaced , "," )
2015-04-08 15:05:41 +00:00
}
2015-03-25 05:01:07 +00:00
func hasCombinedTypeArgs ( args [ ] string ) ( bool , error ) {
hasSlash := 0
for _ , s := range args {
if strings . Contains ( s , "/" ) {
hasSlash ++
}
}
switch {
case hasSlash > 0 && hasSlash == len ( args ) :
return true , nil
2015-10-19 17:42:19 +00:00
case hasSlash > 0 && hasSlash != len ( args ) :
return true , fmt . Errorf ( "when passing arguments in resource/name form, all arguments must include the resource" )
2015-03-25 05:01:07 +00:00
default :
return false , nil
}
}
2015-12-03 19:32:22 +00:00
// Normalize args convert multiple resources to resource tuples, a,b,c d
// as a transform to a/d b/d c/d
func normalizeMultipleResourcesArgs ( args [ ] string ) [ ] string {
if len ( args ) >= 2 {
resources := [ ] string { }
resources = append ( resources , SplitResourceArgument ( args [ 0 ] ) ... )
if len ( resources ) > 1 {
names := [ ] string { }
names = append ( names , args [ 1 : ] ... )
newArgs := [ ] string { }
for _ , resource := range resources {
for _ , name := range names {
newArgs = append ( newArgs , strings . Join ( [ ] string { resource , name } , "/" ) )
}
}
return newArgs
}
}
return args
}
2015-07-09 09:05:13 +00:00
// splitResourceTypeName handles type/name resource formats and returns a resource tuple
// (empty or not), whether it successfully found one, and an error
func splitResourceTypeName ( s string ) ( resourceTuple , bool , error ) {
if ! strings . Contains ( s , "/" ) {
return resourceTuple { } , false , nil
}
seg := strings . Split ( s , "/" )
if len ( seg ) != 2 {
return resourceTuple { } , false , fmt . Errorf ( "arguments in resource/name form may not have more than one slash" )
}
resource , name := seg [ 0 ] , seg [ 1 ]
if len ( resource ) == 0 || len ( name ) == 0 || len ( SplitResourceArgument ( resource ) ) != 1 {
return resourceTuple { } , false , fmt . Errorf ( "arguments in resource/name form must have a single resource and name" )
}
return resourceTuple { Resource : resource , Name : name } , true , nil
}
2014-12-27 21:48:27 +00:00
// Flatten will convert any objects with a field named "Items" that is an array of runtime.Object
// compatible types into individual entries and give them their own items. The original object
// is not passed to any visitors.
func ( b * Builder ) Flatten ( ) * Builder {
b . flatten = true
return b
}
// Latest will fetch the latest copy of any objects loaded from URLs or files from the server.
func ( b * Builder ) Latest ( ) * Builder {
b . latest = true
return b
}
2015-04-20 19:00:52 +00:00
// RequireObject ensures that resulting infos have an object set. If false, resulting info may not have an object set.
func ( b * Builder ) RequireObject ( require bool ) * Builder {
b . requireObject = require
return b
}
2014-12-27 21:48:27 +00:00
// ContinueOnError will attempt to load and visit as many objects as possible, even if some visits
// return errors or some objects cannot be loaded. The default behavior is to terminate after
// the first error is returned from a VisitorFunc.
func ( b * Builder ) ContinueOnError ( ) * Builder {
b . continueOnError = true
return b
}
2014-12-31 23:35:52 +00:00
// SingleResourceType will cause the builder to error if the user specifies more than a single type
// of resource.
2014-12-27 21:48:27 +00:00
func ( b * Builder ) SingleResourceType ( ) * Builder {
b . singleResourceType = true
return b
}
2016-03-11 15:21:50 +00:00
// mappingFor returns the RESTMapping for the Kind referenced by the resource.
// prefers a fully specified GroupVersionResource match. If we don't have one match on GroupResource
func ( b * Builder ) mappingFor ( resourceArg string ) ( * meta . RESTMapping , error ) {
fullySpecifiedGVR , groupResource := unversioned . ParseResourceArg ( resourceArg )
gvk := unversioned . GroupVersionKind { }
if fullySpecifiedGVR != nil {
gvk , _ = b . mapper . KindFor ( * fullySpecifiedGVR )
}
if gvk . IsEmpty ( ) {
var err error
gvk , err = b . mapper . KindFor ( groupResource . WithVersion ( "" ) )
if err != nil {
return nil , err
}
}
return b . mapper . RESTMapping ( gvk . GroupKind ( ) , gvk . Version )
}
2014-12-27 21:48:27 +00:00
func ( b * Builder ) resourceMappings ( ) ( [ ] * meta . RESTMapping , error ) {
if len ( b . resources ) > 1 && b . singleResourceType {
return nil , fmt . Errorf ( "you may only specify a single resource type" )
}
mappings := [ ] * meta . RESTMapping { }
for _ , r := range b . resources {
2016-03-11 15:21:50 +00:00
mapping , err := b . mappingFor ( r )
2014-12-27 21:48:27 +00:00
if err != nil {
return nil , err
}
2016-03-11 15:21:50 +00:00
2014-12-27 21:48:27 +00:00
mappings = append ( mappings , mapping )
}
return mappings , nil
}
2015-03-25 05:01:07 +00:00
func ( b * Builder ) resourceTupleMappings ( ) ( map [ string ] * meta . RESTMapping , error ) {
mappings := make ( map [ string ] * meta . RESTMapping )
canonical := make ( map [ string ] struct { } )
for _ , r := range b . resourceTuples {
if _ , ok := mappings [ r . Resource ] ; ok {
continue
}
2016-03-11 15:21:50 +00:00
mapping , err := b . mappingFor ( r . Resource )
2015-03-25 05:01:07 +00:00
if err != nil {
return nil , err
}
2016-03-11 15:21:50 +00:00
2015-03-25 05:01:07 +00:00
mappings [ mapping . Resource ] = mapping
mappings [ r . Resource ] = mapping
canonical [ mapping . Resource ] = struct { } { }
}
if len ( canonical ) > 1 && b . singleResourceType {
return nil , fmt . Errorf ( "you may only specify a single resource type" )
}
return mappings , nil
}
2014-12-27 21:48:27 +00:00
func ( b * Builder ) visitorResult ( ) * Result {
if len ( b . errs ) > 0 {
2015-10-14 05:18:37 +00:00
return & Result { err : utilerrors . NewAggregate ( b . errs ) }
2014-12-27 21:48:27 +00:00
}
2015-02-12 14:59:58 +00:00
if b . selectAll {
b . selector = labels . Everything ( )
}
2014-12-27 21:48:27 +00:00
// visit selectors
if b . selector != nil {
2015-02-19 14:17:23 +00:00
if len ( b . names ) != 0 {
2014-12-27 21:48:27 +00:00
return & Result { err : fmt . Errorf ( "name cannot be provided when a selector is specified" ) }
}
2015-03-25 05:01:07 +00:00
if len ( b . resourceTuples ) != 0 {
return & Result { err : fmt . Errorf ( "selectors and the all flag cannot be used when passing resource/name arguments" ) }
}
2014-12-27 21:48:27 +00:00
if len ( b . resources ) == 0 {
return & Result { err : fmt . Errorf ( "at least one resource must be specified to use a selector" ) }
}
// empty selector has different error message for paths being provided
if len ( b . paths ) != 0 {
if b . selector . Empty ( ) {
return & Result { err : fmt . Errorf ( "when paths, URLs, or stdin is provided as input, you may not specify a resource by arguments as well" ) }
} else {
return & Result { err : fmt . Errorf ( "a selector may not be specified when path, URL, or stdin is provided as input" ) }
}
}
mappings , err := b . resourceMappings ( )
if err != nil {
return & Result { err : err }
}
visitors := [ ] Visitor { }
for _ , mapping := range mappings {
client , err := b . mapper . ClientForMapping ( mapping )
if err != nil {
return & Result { err : err }
}
2015-01-30 21:11:46 +00:00
selectorNamespace := b . namespace
if mapping . Scope . Name ( ) != meta . RESTScopeNameNamespace {
selectorNamespace = ""
}
2015-12-02 20:20:10 +00:00
visitors = append ( visitors , NewSelector ( client , mapping , selectorNamespace , b . selector , b . export ) )
2014-12-27 21:48:27 +00:00
}
if b . continueOnError {
return & Result { visitor : EagerVisitorList ( visitors ) , sources : visitors }
}
return & Result { visitor : VisitorList ( visitors ) , sources : visitors }
}
2015-03-25 05:01:07 +00:00
// visit items specified by resource and name
if len ( b . resourceTuples ) != 0 {
isSingular := len ( b . resourceTuples ) == 1
if len ( b . paths ) != 0 {
return & Result { singular : isSingular , err : fmt . Errorf ( "when paths, URLs, or stdin is provided as input, you may not specify a resource by arguments as well" ) }
}
if len ( b . resources ) != 0 {
return & Result { singular : isSingular , err : fmt . Errorf ( "you may not specify individual resources and bulk resources in the same call" ) }
}
// retrieve one client for each resource
mappings , err := b . resourceTupleMappings ( )
if err != nil {
return & Result { singular : isSingular , err : err }
}
clients := make ( map [ string ] RESTClient )
for _ , mapping := range mappings {
2015-12-13 02:02:47 +00:00
s := fmt . Sprintf ( "%s/%s" , mapping . GroupVersionKind . GroupVersion ( ) . String ( ) , mapping . Resource )
2015-03-25 05:01:07 +00:00
if _ , ok := clients [ s ] ; ok {
continue
}
client , err := b . mapper . ClientForMapping ( mapping )
if err != nil {
return & Result { err : err }
}
clients [ s ] = client
}
items := [ ] Visitor { }
for _ , tuple := range b . resourceTuples {
mapping , ok := mappings [ tuple . Resource ]
if ! ok {
return & Result { singular : isSingular , err : fmt . Errorf ( "resource %q is not recognized: %v" , tuple . Resource , mappings ) }
}
2015-12-13 02:02:47 +00:00
s := fmt . Sprintf ( "%s/%s" , mapping . GroupVersionKind . GroupVersion ( ) . String ( ) , mapping . Resource )
2015-03-25 05:01:07 +00:00
client , ok := clients [ s ]
if ! ok {
return & Result { singular : isSingular , err : fmt . Errorf ( "could not find a client for resource %q" , tuple . Resource ) }
}
selectorNamespace := b . namespace
if mapping . Scope . Name ( ) != meta . RESTScopeNameNamespace {
selectorNamespace = ""
} else {
if len ( b . namespace ) == 0 {
return & Result { singular : isSingular , err : fmt . Errorf ( "namespace may not be empty when retrieving a resource by name" ) }
}
}
2015-12-02 20:20:10 +00:00
info := NewInfo ( client , mapping , selectorNamespace , tuple . Name , b . export )
2015-03-25 05:01:07 +00:00
items = append ( items , info )
}
var visitors Visitor
if b . continueOnError {
visitors = EagerVisitorList ( items )
} else {
visitors = VisitorList ( items )
}
return & Result { singular : isSingular , visitor : visitors , sources : items }
}
2015-02-19 14:17:23 +00:00
// visit items specified by name
if len ( b . names ) != 0 {
isSingular := len ( b . names ) == 1
2014-12-27 21:48:27 +00:00
if len ( b . paths ) != 0 {
2015-02-19 14:17:23 +00:00
return & Result { singular : isSingular , err : fmt . Errorf ( "when paths, URLs, or stdin is provided as input, you may not specify a resource by arguments as well" ) }
2014-12-27 21:48:27 +00:00
}
if len ( b . resources ) == 0 {
2015-02-19 14:17:23 +00:00
return & Result { singular : isSingular , err : fmt . Errorf ( "you must provide a resource and a resource name together" ) }
2014-12-27 21:48:27 +00:00
}
if len ( b . resources ) > 1 {
2015-02-19 14:17:23 +00:00
return & Result { singular : isSingular , err : fmt . Errorf ( "you must specify only one resource" ) }
2014-12-27 21:48:27 +00:00
}
2015-02-19 14:17:23 +00:00
2014-12-27 21:48:27 +00:00
mappings , err := b . resourceMappings ( )
if err != nil {
2015-02-19 14:17:23 +00:00
return & Result { singular : isSingular , err : err }
2014-12-27 21:48:27 +00:00
}
2015-01-30 21:11:46 +00:00
mapping := mappings [ 0 ]
2015-02-19 14:17:23 +00:00
client , err := b . mapper . ClientForMapping ( mapping )
if err != nil {
return & Result { err : err }
}
selectorNamespace := b . namespace
2015-01-30 21:11:46 +00:00
if mapping . Scope . Name ( ) != meta . RESTScopeNameNamespace {
2015-02-19 14:17:23 +00:00
selectorNamespace = ""
2015-01-30 21:11:46 +00:00
} else {
if len ( b . namespace ) == 0 {
2015-02-19 14:17:23 +00:00
return & Result { singular : isSingular , err : fmt . Errorf ( "namespace may not be empty when retrieving a resource by name" ) }
2015-01-30 21:11:46 +00:00
}
}
2015-02-19 14:17:23 +00:00
visitors := [ ] Visitor { }
for _ , name := range b . names {
2015-12-02 20:20:10 +00:00
info := NewInfo ( client , mapping , selectorNamespace , name , b . export )
2015-02-19 14:17:23 +00:00
visitors = append ( visitors , info )
2014-12-27 21:48:27 +00:00
}
2015-02-19 14:17:23 +00:00
return & Result { singular : isSingular , visitor : VisitorList ( visitors ) , sources : visitors }
2014-12-27 21:48:27 +00:00
}
// visit items specified by paths
if len ( b . paths ) != 0 {
singular := ! b . dir && ! b . stream && len ( b . paths ) == 1
if len ( b . resources ) != 0 {
return & Result { singular : singular , err : fmt . Errorf ( "when paths, URLs, or stdin is provided as input, you may not specify resource arguments as well" ) }
}
var visitors Visitor
if b . continueOnError {
visitors = EagerVisitorList ( b . paths )
} else {
visitors = VisitorList ( b . paths )
}
// only items from disk can be refetched
if b . latest {
// must flatten lists prior to fetching
if b . flatten {
visitors = NewFlattenListVisitor ( visitors , b . mapper )
}
2015-08-05 03:43:48 +00:00
// must set namespace prior to fetching
if b . defaultNamespace {
visitors = NewDecoratedVisitor ( visitors , SetNamespace ( b . namespace ) )
}
2014-12-27 21:48:27 +00:00
visitors = NewDecoratedVisitor ( visitors , RetrieveLatest )
}
return & Result { singular : singular , visitor : visitors , sources : b . paths }
}
2015-10-29 02:00:34 +00:00
return & Result { err : fmt . Errorf ( "you must provide one or more resources by argument or filename (%s)" , strings . Join ( InputExtensions , "|" ) ) }
2014-12-27 21:48:27 +00:00
}
// Do returns a Result object with a Visitor for the resources identified by the Builder.
// The visitor will respect the error behavior specified by ContinueOnError. Note that stream
// inputs are consumed by the first execution - use Infos() or Object() on the Result to capture a list
// for further iteration.
func ( b * Builder ) Do ( ) * Result {
r := b . visitorResult ( )
if r . err != nil {
return r
}
if b . flatten {
r . visitor = NewFlattenListVisitor ( r . visitor , b . mapper )
}
helpers := [ ] VisitorFunc { }
if b . defaultNamespace {
helpers = append ( helpers , SetNamespace ( b . namespace ) )
}
if b . requireNamespace {
helpers = append ( helpers , RequireNamespace ( b . namespace ) )
}
2015-03-25 05:01:07 +00:00
helpers = append ( helpers , FilterNamespace )
2015-04-20 19:00:52 +00:00
if b . requireObject {
2015-03-25 05:01:07 +00:00
helpers = append ( helpers , RetrieveLazy )
}
2014-12-27 21:48:27 +00:00
r . visitor = NewDecoratedVisitor ( r . visitor , helpers ... )
2015-05-14 04:35:11 +00:00
if b . continueOnError {
r . visitor = ContinueOnErrorVisitor { r . visitor }
}
2014-12-27 21:48:27 +00:00
return r
}
2015-04-17 04:55:26 +00:00
// SplitResourceArgument splits the argument with commas and returns unique
// strings in the original order.
2014-12-27 21:48:27 +00:00
func SplitResourceArgument ( arg string ) [ ] string {
2015-04-17 04:55:26 +00:00
out := [ ] string { }
2015-09-09 17:45:01 +00:00
set := sets . NewString ( )
2015-04-17 04:55:26 +00:00
for _ , s := range strings . Split ( arg , "," ) {
if set . Has ( s ) {
continue
}
2015-04-17 15:36:25 +00:00
set . Insert ( s )
2015-04-17 04:55:26 +00:00
out = append ( out , s )
}
return out
2014-12-27 21:48:27 +00:00
}
2015-12-03 19:32:22 +00:00
// HasNames returns true if the provided args contain resource names
func HasNames ( args [ ] string ) ( bool , error ) {
args = normalizeMultipleResourcesArgs ( args )
hasCombinedTypes , err := hasCombinedTypeArgs ( args )
if err != nil {
return false , err
}
return hasCombinedTypes || len ( args ) > 1 , nil
}