mirror of
				https://github.com/optim-enterprises-bv/kubernetes.git
				synced 2025-11-03 19:58:17 +00:00 
			
		
		
		
	Merge pull request #12405 from uluyol/kubectlexp
Add experimental api support to kubectl
This commit is contained in:
		@@ -97,7 +97,7 @@ func init() {
 | 
			
		||||
		"PodProxyOptions",
 | 
			
		||||
		"Daemon")
 | 
			
		||||
 | 
			
		||||
	mapper := api.NewDefaultRESTMapper(versions, InterfacesFor, importPrefix, ignoredKinds, rootScoped)
 | 
			
		||||
	mapper := api.NewDefaultRESTMapper("api", versions, InterfacesFor, importPrefix, ignoredKinds, rootScoped)
 | 
			
		||||
	// setup aliases for groups of resources
 | 
			
		||||
	mapper.AddResourceAlias("all", userResources...)
 | 
			
		||||
	RESTMapper = mapper
 | 
			
		||||
 
 | 
			
		||||
@@ -33,11 +33,12 @@ func RegisterRESTMapper(m meta.RESTMapper) {
 | 
			
		||||
	RESTMapper = append(RESTMapper.(meta.MultiRESTMapper), m)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func NewDefaultRESTMapper(versions []string, interfacesFunc meta.VersionInterfacesFunc, importPathPrefix string,
 | 
			
		||||
	ignoredKinds, rootScoped util.StringSet) *meta.DefaultRESTMapper {
 | 
			
		||||
func NewDefaultRESTMapper(group string, versions []string, interfacesFunc meta.VersionInterfacesFunc,
 | 
			
		||||
	importPathPrefix string, ignoredKinds, rootScoped util.StringSet) *meta.DefaultRESTMapper {
 | 
			
		||||
 | 
			
		||||
	mapper := meta.NewDefaultRESTMapper(versions, interfacesFunc)
 | 
			
		||||
	// enumerate all supported versions, get the kinds, and register with the mapper how to address our resources.
 | 
			
		||||
	mapper := meta.NewDefaultRESTMapper(group, versions, interfacesFunc)
 | 
			
		||||
	// enumerate all supported versions, get the kinds, and register with the mapper how to address
 | 
			
		||||
	// our resources.
 | 
			
		||||
	for _, version := range versions {
 | 
			
		||||
		for kind, oType := range Scheme.KnownTypes(version) {
 | 
			
		||||
			// TODO: Remove import path prefix check.
 | 
			
		||||
 
 | 
			
		||||
@@ -142,8 +142,18 @@ type RESTMapping struct {
 | 
			
		||||
// RESTMapper allows clients to map resources to kind, and map kind and version
 | 
			
		||||
// to interfaces for manipulating those objects. It is primarily intended for
 | 
			
		||||
// consumers of Kubernetes compatible REST APIs as defined in docs/api-conventions.md.
 | 
			
		||||
//
 | 
			
		||||
// The Kubernetes API provides versioned resources and object kinds which are scoped
 | 
			
		||||
// to API groups. In other words, kinds and resources should not be assumed to be
 | 
			
		||||
// unique across groups.
 | 
			
		||||
//
 | 
			
		||||
// TODO(caesarxuchao): Add proper multi-group support so that kinds & resources are
 | 
			
		||||
// scoped to groups. See http://issues.k8s.io/12413 and http://issues.k8s.io/10009.
 | 
			
		||||
type RESTMapper interface {
 | 
			
		||||
	VersionAndKindForResource(resource string) (defaultVersion, kind string, err error)
 | 
			
		||||
	// TODO(caesarxuchao): Remove GroupForResource when multi-group support is in (since
 | 
			
		||||
	// group will be part of the version).
 | 
			
		||||
	GroupForResource(resource string) (string, error)
 | 
			
		||||
	RESTMapping(kind string, versions ...string) (*RESTMapping, error)
 | 
			
		||||
	AliasesForResource(resource string) ([]string, bool)
 | 
			
		||||
	ResourceSingularizer(resource string) (singular string, err error)
 | 
			
		||||
 
 | 
			
		||||
@@ -76,6 +76,7 @@ type DefaultRESTMapper struct {
 | 
			
		||||
	mapping        map[string]typeMeta
 | 
			
		||||
	reverse        map[typeMeta]string
 | 
			
		||||
	scopes         map[typeMeta]RESTScope
 | 
			
		||||
	group          string
 | 
			
		||||
	versions       []string
 | 
			
		||||
	plurals        map[string]string
 | 
			
		||||
	singulars      map[string]string
 | 
			
		||||
@@ -88,10 +89,10 @@ type VersionInterfacesFunc func(apiVersion string) (*VersionInterfaces, error)
 | 
			
		||||
 | 
			
		||||
// NewDefaultRESTMapper initializes a mapping between Kind and APIVersion
 | 
			
		||||
// to a resource name and back based on the objects in a runtime.Scheme
 | 
			
		||||
// and the Kubernetes API conventions. Takes a priority list of the versions to
 | 
			
		||||
// search when an object has no default version (set empty to return an error)
 | 
			
		||||
// and the Kubernetes API conventions. Takes a group name, a priority list of the versions
 | 
			
		||||
// to search when an object has no default version (set empty to return an error),
 | 
			
		||||
// and a function that retrieves the correct codec and metadata for a given version.
 | 
			
		||||
func NewDefaultRESTMapper(versions []string, f VersionInterfacesFunc) *DefaultRESTMapper {
 | 
			
		||||
func NewDefaultRESTMapper(group string, versions []string, f VersionInterfacesFunc) *DefaultRESTMapper {
 | 
			
		||||
	mapping := make(map[string]typeMeta)
 | 
			
		||||
	reverse := make(map[typeMeta]string)
 | 
			
		||||
	scopes := make(map[typeMeta]RESTScope)
 | 
			
		||||
@@ -103,6 +104,7 @@ func NewDefaultRESTMapper(versions []string, f VersionInterfacesFunc) *DefaultRE
 | 
			
		||||
		mapping:        mapping,
 | 
			
		||||
		reverse:        reverse,
 | 
			
		||||
		scopes:         scopes,
 | 
			
		||||
		group:          group,
 | 
			
		||||
		versions:       versions,
 | 
			
		||||
		plurals:        plurals,
 | 
			
		||||
		singulars:      singulars,
 | 
			
		||||
@@ -174,6 +176,13 @@ func (m *DefaultRESTMapper) VersionAndKindForResource(resource string) (defaultV
 | 
			
		||||
	return meta.APIVersion, meta.Kind, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *DefaultRESTMapper) GroupForResource(resource string) (string, error) {
 | 
			
		||||
	if _, ok := m.mapping[strings.ToLower(resource)]; !ok {
 | 
			
		||||
		return "", fmt.Errorf("no resource %q has been defined", resource)
 | 
			
		||||
	}
 | 
			
		||||
	return m.group, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// RESTMapping returns a struct representing the resource path and conversion interfaces a
 | 
			
		||||
// RESTClient should use to operate on the provided kind in order of versions. If a version search
 | 
			
		||||
// order is not provided, the search order provided to DefaultRESTMapper will be used to resolve which
 | 
			
		||||
@@ -292,6 +301,18 @@ func (m MultiRESTMapper) VersionAndKindForResource(resource string) (defaultVers
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GroupForResource provides the Group mappings for the REST resources. This
 | 
			
		||||
// implementation supports multiple REST schemas and returns the first match.
 | 
			
		||||
func (m MultiRESTMapper) GroupForResource(resource string) (group string, err error) {
 | 
			
		||||
	for _, t := range m {
 | 
			
		||||
		group, err = t.GroupForResource(resource)
 | 
			
		||||
		if err == nil {
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// RESTMapping provides the REST mapping for the resource based on the resource
 | 
			
		||||
// kind and version. This implementation supports multiple REST schemas and
 | 
			
		||||
// return the first match.
 | 
			
		||||
 
 | 
			
		||||
@@ -93,7 +93,7 @@ func TestRESTMapperVersionAndKindForResource(t *testing.T) {
 | 
			
		||||
		{Resource: "internalObjects", MixedCase: true, Kind: "InternalObject", APIVersion: "test"},
 | 
			
		||||
	}
 | 
			
		||||
	for i, testCase := range testCases {
 | 
			
		||||
		mapper := NewDefaultRESTMapper([]string{"test"}, fakeInterfaces)
 | 
			
		||||
		mapper := NewDefaultRESTMapper("tgroup", []string{"test"}, fakeInterfaces)
 | 
			
		||||
		mapper.Add(RESTScopeNamespace, testCase.Kind, testCase.APIVersion, testCase.MixedCase)
 | 
			
		||||
		v, k, err := mapper.VersionAndKindForResource(testCase.Resource)
 | 
			
		||||
		hasErr := err != nil
 | 
			
		||||
@@ -107,6 +107,33 @@ func TestRESTMapperVersionAndKindForResource(t *testing.T) {
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestRESTMapperGroupForResource(t *testing.T) {
 | 
			
		||||
	testCases := []struct {
 | 
			
		||||
		Resource                string
 | 
			
		||||
		Kind, APIVersion, Group string
 | 
			
		||||
		Err                     bool
 | 
			
		||||
	}{
 | 
			
		||||
		{Resource: "myObject", Kind: "MyObject", APIVersion: "test", Group: "testapi"},
 | 
			
		||||
		{Resource: "myobject", Kind: "MyObject", APIVersion: "test", Group: "testapi2"},
 | 
			
		||||
		{Resource: "myObje", Err: true, Kind: "MyObject", APIVersion: "test", Group: "testapi"},
 | 
			
		||||
		{Resource: "myobje", Err: true, Kind: "MyObject", APIVersion: "test", Group: "testapi"},
 | 
			
		||||
	}
 | 
			
		||||
	for i, testCase := range testCases {
 | 
			
		||||
		mapper := NewDefaultRESTMapper(testCase.Group, []string{"test"}, fakeInterfaces)
 | 
			
		||||
		mapper.Add(RESTScopeNamespace, testCase.Kind, testCase.APIVersion, false)
 | 
			
		||||
		g, err := mapper.GroupForResource(testCase.Resource)
 | 
			
		||||
		if testCase.Err {
 | 
			
		||||
			if err == nil {
 | 
			
		||||
				t.Errorf("%d: expected error", i)
 | 
			
		||||
			}
 | 
			
		||||
		} else if err != nil {
 | 
			
		||||
			t.Errorf("%d: unexpected error: %v", i, err)
 | 
			
		||||
		} else if g != testCase.Group {
 | 
			
		||||
			t.Errorf("%d: expected group %q, got %q", i, testCase.Group, g)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestKindToResource(t *testing.T) {
 | 
			
		||||
	testCases := []struct {
 | 
			
		||||
		Kind             string
 | 
			
		||||
@@ -159,7 +186,7 @@ func TestRESTMapperResourceSingularizer(t *testing.T) {
 | 
			
		||||
		{Kind: "lowercases", APIVersion: "test", MixedCase: false, Plural: "lowercases", Singular: "lowercases"},
 | 
			
		||||
	}
 | 
			
		||||
	for i, testCase := range testCases {
 | 
			
		||||
		mapper := NewDefaultRESTMapper([]string{"test"}, fakeInterfaces)
 | 
			
		||||
		mapper := NewDefaultRESTMapper("tgroup", []string{"test"}, fakeInterfaces)
 | 
			
		||||
		// create singular/plural mapping
 | 
			
		||||
		mapper.Add(RESTScopeNamespace, testCase.Kind, testCase.APIVersion, testCase.MixedCase)
 | 
			
		||||
		singular, _ := mapper.ResourceSingularizer(testCase.Plural)
 | 
			
		||||
@@ -198,7 +225,7 @@ func TestRESTMapperRESTMapping(t *testing.T) {
 | 
			
		||||
		// TODO: add test for a resource that exists in one version but not another
 | 
			
		||||
	}
 | 
			
		||||
	for i, testCase := range testCases {
 | 
			
		||||
		mapper := NewDefaultRESTMapper(testCase.DefaultVersions, fakeInterfaces)
 | 
			
		||||
		mapper := NewDefaultRESTMapper("tgroup", testCase.DefaultVersions, fakeInterfaces)
 | 
			
		||||
		mapper.Add(RESTScopeNamespace, "InternalObject", "test", testCase.MixedCase)
 | 
			
		||||
		mapping, err := mapper.RESTMapping(testCase.Kind, testCase.APIVersions...)
 | 
			
		||||
		hasErr := err != nil
 | 
			
		||||
@@ -225,7 +252,7 @@ func TestRESTMapperRESTMapping(t *testing.T) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestRESTMapperRESTMappingSelectsVersion(t *testing.T) {
 | 
			
		||||
	mapper := NewDefaultRESTMapper([]string{"test1", "test2"}, fakeInterfaces)
 | 
			
		||||
	mapper := NewDefaultRESTMapper("tgroup", []string{"test1", "test2"}, fakeInterfaces)
 | 
			
		||||
	mapper.Add(RESTScopeNamespace, "InternalObject", "test1", false)
 | 
			
		||||
	mapper.Add(RESTScopeNamespace, "OtherObject", "test2", false)
 | 
			
		||||
 | 
			
		||||
@@ -278,7 +305,7 @@ func TestRESTMapperRESTMappingSelectsVersion(t *testing.T) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestRESTMapperReportsErrorOnBadVersion(t *testing.T) {
 | 
			
		||||
	mapper := NewDefaultRESTMapper([]string{"test1", "test2"}, unmatchedVersionInterfaces)
 | 
			
		||||
	mapper := NewDefaultRESTMapper("tgroup", []string{"test1", "test2"}, unmatchedVersionInterfaces)
 | 
			
		||||
	mapper.Add(RESTScopeNamespace, "InternalObject", "test1", false)
 | 
			
		||||
	_, err := mapper.RESTMapping("InternalObject", "test1")
 | 
			
		||||
	if err == nil {
 | 
			
		||||
 
 | 
			
		||||
@@ -88,7 +88,7 @@ func interfacesFor(version string) (*meta.VersionInterfaces, error) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func newMapper() *meta.DefaultRESTMapper {
 | 
			
		||||
	return meta.NewDefaultRESTMapper(versions, interfacesFor)
 | 
			
		||||
	return meta.NewDefaultRESTMapper("testgroup", versions, interfacesFor)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func addTestTypes() {
 | 
			
		||||
 
 | 
			
		||||
@@ -55,7 +55,7 @@ func init() {
 | 
			
		||||
 | 
			
		||||
	ignoredKinds := util.NewStringSet()
 | 
			
		||||
 | 
			
		||||
	RESTMapper = api.NewDefaultRESTMapper(Versions, InterfacesFor, importPrefix, ignoredKinds, rootScoped)
 | 
			
		||||
	RESTMapper = api.NewDefaultRESTMapper("experimental", Versions, InterfacesFor, importPrefix, ignoredKinds, rootScoped)
 | 
			
		||||
	api.RegisterRESTMapper(RESTMapper)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -17,12 +17,13 @@ limitations under the License.
 | 
			
		||||
package cmd
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io"
 | 
			
		||||
	"os"
 | 
			
		||||
 | 
			
		||||
	"github.com/spf13/cobra"
 | 
			
		||||
 | 
			
		||||
	"k8s.io/kubernetes/pkg/kubectl"
 | 
			
		||||
	"k8s.io/kubernetes/pkg/api"
 | 
			
		||||
	cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@@ -40,7 +41,7 @@ func NewCmdApiVersions(f *cmdutil.Factory, out io.Writer) *cobra.Command {
 | 
			
		||||
	return cmd
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func RunApiVersions(f *cmdutil.Factory, out io.Writer) error {
 | 
			
		||||
func RunApiVersions(f *cmdutil.Factory, w io.Writer) error {
 | 
			
		||||
	if len(os.Args) > 1 && os.Args[1] == "apiversions" {
 | 
			
		||||
		printDeprecationWarning("api-versions", "apiversions")
 | 
			
		||||
	}
 | 
			
		||||
@@ -50,6 +51,24 @@ func RunApiVersions(f *cmdutil.Factory, out io.Writer) error {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	kubectl.GetApiVersions(out, client)
 | 
			
		||||
	apiVersions, err := client.ServerAPIVersions()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		fmt.Printf("Couldn't get available api versions from server: %v\n", err)
 | 
			
		||||
		os.Exit(1)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var expAPIVersions *api.APIVersions
 | 
			
		||||
	showExpVersions := false
 | 
			
		||||
	expClient, err := f.ExperimentalClient()
 | 
			
		||||
	if err == nil {
 | 
			
		||||
		expAPIVersions, err = expClient.ServerAPIVersions()
 | 
			
		||||
		showExpVersions = err == nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	fmt.Fprintf(w, "Available Server Api Versions: %#v\n", *apiVersions)
 | 
			
		||||
	if showExpVersions {
 | 
			
		||||
		fmt.Fprintf(w, "Available Server Experimental Api Versions: %#v\n", *expAPIVersions)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -83,7 +83,7 @@ func newExternalScheme() (*runtime.Scheme, meta.RESTMapper, runtime.Codec) {
 | 
			
		||||
 | 
			
		||||
	codec := runtime.CodecFor(scheme, "unlikelyversion")
 | 
			
		||||
	validVersion := testapi.Version()
 | 
			
		||||
	mapper := meta.NewDefaultRESTMapper([]string{"unlikelyversion", validVersion}, func(version string) (*meta.VersionInterfaces, error) {
 | 
			
		||||
	mapper := meta.NewDefaultRESTMapper("apitest", []string{"unlikelyversion", validVersion}, func(version string) (*meta.VersionInterfaces, error) {
 | 
			
		||||
		return &meta.VersionInterfaces{
 | 
			
		||||
			Codec:            runtime.CodecFor(scheme, version),
 | 
			
		||||
			ObjectConvertor:  scheme,
 | 
			
		||||
 
 | 
			
		||||
@@ -22,17 +22,17 @@ import (
 | 
			
		||||
	"k8s.io/kubernetes/pkg/client/clientcmd"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func NewClientCache(loader clientcmd.ClientConfig) *clientCache {
 | 
			
		||||
	return &clientCache{
 | 
			
		||||
func NewClientCache(loader clientcmd.ClientConfig) *ClientCache {
 | 
			
		||||
	return &ClientCache{
 | 
			
		||||
		clients: make(map[string]*client.Client),
 | 
			
		||||
		configs: make(map[string]*client.Config),
 | 
			
		||||
		loader:  loader,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// clientCache caches previously loaded clients for reuse, and ensures MatchServerVersion
 | 
			
		||||
// ClientCache caches previously loaded clients for reuse, and ensures MatchServerVersion
 | 
			
		||||
// is invoked only once
 | 
			
		||||
type clientCache struct {
 | 
			
		||||
type ClientCache struct {
 | 
			
		||||
	loader        clientcmd.ClientConfig
 | 
			
		||||
	clients       map[string]*client.Client
 | 
			
		||||
	configs       map[string]*client.Config
 | 
			
		||||
@@ -42,7 +42,7 @@ type clientCache struct {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ClientConfigForVersion returns the correct config for a server
 | 
			
		||||
func (c *clientCache) ClientConfigForVersion(version string) (*client.Config, error) {
 | 
			
		||||
func (c *ClientCache) ClientConfigForVersion(version string) (*client.Config, error) {
 | 
			
		||||
	if c.defaultConfig == nil {
 | 
			
		||||
		config, err := c.loader.ClientConfig()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
@@ -73,7 +73,7 @@ func (c *clientCache) ClientConfigForVersion(version string) (*client.Config, er
 | 
			
		||||
 | 
			
		||||
// ClientForVersion initializes or reuses a client for the specified version, or returns an
 | 
			
		||||
// error if that is not possible
 | 
			
		||||
func (c *clientCache) ClientForVersion(version string) (*client.Client, error) {
 | 
			
		||||
func (c *ClientCache) ClientForVersion(version string) (*client.Client, error) {
 | 
			
		||||
	if client, ok := c.clients[version]; ok {
 | 
			
		||||
		return client, nil
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
@@ -17,6 +17,7 @@ limitations under the License.
 | 
			
		||||
package util
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"errors"
 | 
			
		||||
	"flag"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io"
 | 
			
		||||
@@ -27,7 +28,6 @@ import (
 | 
			
		||||
	"github.com/spf13/pflag"
 | 
			
		||||
 | 
			
		||||
	"k8s.io/kubernetes/pkg/api"
 | 
			
		||||
	"k8s.io/kubernetes/pkg/api/latest"
 | 
			
		||||
	"k8s.io/kubernetes/pkg/api/meta"
 | 
			
		||||
	"k8s.io/kubernetes/pkg/api/registered"
 | 
			
		||||
	"k8s.io/kubernetes/pkg/api/validation"
 | 
			
		||||
@@ -49,7 +49,7 @@ const (
 | 
			
		||||
// TODO: pass the various interfaces on the factory directly into the command constructors (so the
 | 
			
		||||
// commands are decoupled from the factory).
 | 
			
		||||
type Factory struct {
 | 
			
		||||
	clients    *clientCache
 | 
			
		||||
	clients    *ClientCache
 | 
			
		||||
	flags      *pflag.FlagSet
 | 
			
		||||
	generators map[string]kubectl.Generator
 | 
			
		||||
 | 
			
		||||
@@ -57,6 +57,8 @@ type Factory struct {
 | 
			
		||||
	Object func() (meta.RESTMapper, runtime.ObjectTyper)
 | 
			
		||||
	// Returns a client for accessing Kubernetes resources or an error.
 | 
			
		||||
	Client func() (*client.Client, error)
 | 
			
		||||
	// Returns a client for accessing experimental Kubernetes resources or an error.
 | 
			
		||||
	ExperimentalClient func() (*client.ExperimentalClient, error)
 | 
			
		||||
	// Returns a client.Config for accessing the Kubernetes server.
 | 
			
		||||
	ClientConfig func() (*client.Config, error)
 | 
			
		||||
	// Returns a RESTClient for working with the specified RESTMapping or an error. This is intended
 | 
			
		||||
@@ -90,7 +92,7 @@ type Factory struct {
 | 
			
		||||
// if optionalClientConfig is nil, then flags will be bound to a new clientcmd.ClientConfig.
 | 
			
		||||
// if optionalClientConfig is not nil, then this factory will make use of it.
 | 
			
		||||
func NewFactory(optionalClientConfig clientcmd.ClientConfig) *Factory {
 | 
			
		||||
	mapper := kubectl.ShortcutExpander{RESTMapper: latest.RESTMapper}
 | 
			
		||||
	mapper := kubectl.ShortcutExpander{RESTMapper: api.RESTMapper}
 | 
			
		||||
 | 
			
		||||
	flags := pflag.NewFlagSet("", pflag.ContinueOnError)
 | 
			
		||||
	flags.SetNormalizeFunc(util.WarnWordSepNormalizeFunc) // Warn for "_" flags
 | 
			
		||||
@@ -109,6 +111,25 @@ func NewFactory(optionalClientConfig clientcmd.ClientConfig) *Factory {
 | 
			
		||||
 | 
			
		||||
	clients := NewClientCache(clientConfig)
 | 
			
		||||
 | 
			
		||||
	// Initialize the experimental client (if possible). Failing here is non-fatal, errors
 | 
			
		||||
	// will be returned when an experimental client is explicitly requested.
 | 
			
		||||
	var experimentalClient *client.ExperimentalClient
 | 
			
		||||
	cfg, experimentalClientErr := clientConfig.ClientConfig()
 | 
			
		||||
	if experimentalClientErr == nil {
 | 
			
		||||
		experimentalClient, experimentalClientErr = client.NewExperimental(cfg)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	noClientErr := errors.New("could not get client")
 | 
			
		||||
	getBothClients := func(group string, version string) (client *client.Client, expClient *client.ExperimentalClient, err error) {
 | 
			
		||||
		err = noClientErr
 | 
			
		||||
		switch group {
 | 
			
		||||
		case "api":
 | 
			
		||||
			client, err = clients.ClientForVersion(version)
 | 
			
		||||
		case "experimental":
 | 
			
		||||
			expClient, err = experimentalClient, experimentalClientErr
 | 
			
		||||
		}
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	return &Factory{
 | 
			
		||||
		clients:    clients,
 | 
			
		||||
		flags:      flags,
 | 
			
		||||
@@ -124,26 +145,46 @@ func NewFactory(optionalClientConfig clientcmd.ClientConfig) *Factory {
 | 
			
		||||
		Client: func() (*client.Client, error) {
 | 
			
		||||
			return clients.ClientForVersion("")
 | 
			
		||||
		},
 | 
			
		||||
		ExperimentalClient: func() (*client.ExperimentalClient, error) {
 | 
			
		||||
			return experimentalClient, experimentalClientErr
 | 
			
		||||
		},
 | 
			
		||||
		ClientConfig: func() (*client.Config, error) {
 | 
			
		||||
			return clients.ClientConfigForVersion("")
 | 
			
		||||
		},
 | 
			
		||||
		RESTClient: func(mapping *meta.RESTMapping) (resource.RESTClient, error) {
 | 
			
		||||
			client, err := clients.ClientForVersion(mapping.APIVersion)
 | 
			
		||||
			group, err := api.RESTMapper.GroupForResource(mapping.Resource)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return nil, err
 | 
			
		||||
			}
 | 
			
		||||
			return client.RESTClient, nil
 | 
			
		||||
			switch group {
 | 
			
		||||
			case "api":
 | 
			
		||||
				client, err := clients.ClientForVersion(mapping.APIVersion)
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					return nil, err
 | 
			
		||||
				}
 | 
			
		||||
				return client.RESTClient, nil
 | 
			
		||||
			case "experimental":
 | 
			
		||||
				client, err := experimentalClient, experimentalClientErr
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					return nil, err
 | 
			
		||||
				}
 | 
			
		||||
				return client.RESTClient, nil
 | 
			
		||||
			}
 | 
			
		||||
			return nil, fmt.Errorf("unable to get RESTClient for resource '%s'", mapping.Resource)
 | 
			
		||||
		},
 | 
			
		||||
		Describer: func(mapping *meta.RESTMapping) (kubectl.Describer, error) {
 | 
			
		||||
			client, err := clients.ClientForVersion(mapping.APIVersion)
 | 
			
		||||
			group, err := api.RESTMapper.GroupForResource(mapping.Resource)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return nil, err
 | 
			
		||||
			}
 | 
			
		||||
			describer, ok := kubectl.DescriberFor(mapping.Kind, client)
 | 
			
		||||
			if !ok {
 | 
			
		||||
				return nil, fmt.Errorf("no description has been implemented for %q", mapping.Kind)
 | 
			
		||||
			client, expClient, err := getBothClients(group, mapping.APIVersion)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return nil, err
 | 
			
		||||
			}
 | 
			
		||||
			return describer, nil
 | 
			
		||||
			if describer, ok := kubectl.DescriberFor(mapping.Kind, client, expClient); ok {
 | 
			
		||||
				return describer, nil
 | 
			
		||||
			}
 | 
			
		||||
			return nil, fmt.Errorf("no description has been implemented for %q", mapping.Kind)
 | 
			
		||||
		},
 | 
			
		||||
		Printer: func(mapping *meta.RESTMapping, noHeaders, withNamespace bool, wide bool, columnLabels []string) (kubectl.ResourcePrinter, error) {
 | 
			
		||||
			return kubectl.NewHumanReadablePrinter(noHeaders, withNamespace, wide, columnLabels), nil
 | 
			
		||||
@@ -192,18 +233,26 @@ func NewFactory(optionalClientConfig clientcmd.ClientConfig) *Factory {
 | 
			
		||||
			return meta.NewAccessor().Labels(object)
 | 
			
		||||
		},
 | 
			
		||||
		Scaler: func(mapping *meta.RESTMapping) (kubectl.Scaler, error) {
 | 
			
		||||
			client, err := clients.ClientForVersion(mapping.APIVersion)
 | 
			
		||||
			group, err := api.RESTMapper.GroupForResource(mapping.Resource)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return nil, err
 | 
			
		||||
			}
 | 
			
		||||
			client, _, err := getBothClients(group, mapping.APIVersion)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return nil, err
 | 
			
		||||
			}
 | 
			
		||||
			return kubectl.ScalerFor(mapping.Kind, kubectl.NewScalerClient(client))
 | 
			
		||||
		},
 | 
			
		||||
		Reaper: func(mapping *meta.RESTMapping) (kubectl.Reaper, error) {
 | 
			
		||||
			client, err := clients.ClientForVersion(mapping.APIVersion)
 | 
			
		||||
			group, err := api.RESTMapper.GroupForResource(mapping.Resource)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return nil, err
 | 
			
		||||
			}
 | 
			
		||||
			return kubectl.ReaperFor(mapping.Kind, client)
 | 
			
		||||
			client, expClient, err := getBothClients(group, mapping.APIVersion)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return nil, err
 | 
			
		||||
			}
 | 
			
		||||
			return kubectl.ReaperFor(mapping.Kind, client, expClient)
 | 
			
		||||
		},
 | 
			
		||||
		Validator: func() (validation.Schema, error) {
 | 
			
		||||
			if flags.Lookup("validate").Value.String() == "true" {
 | 
			
		||||
@@ -211,7 +260,7 @@ func NewFactory(optionalClientConfig clientcmd.ClientConfig) *Factory {
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					return nil, err
 | 
			
		||||
				}
 | 
			
		||||
				return &clientSwaggerSchema{client, api.Scheme}, nil
 | 
			
		||||
				return &clientSwaggerSchema{client, experimentalClient, api.Scheme}, nil
 | 
			
		||||
			}
 | 
			
		||||
			return validation.NullSchema{}, nil
 | 
			
		||||
		},
 | 
			
		||||
@@ -274,20 +323,14 @@ func getServicePorts(spec api.ServiceSpec) []string {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type clientSwaggerSchema struct {
 | 
			
		||||
	c *client.Client
 | 
			
		||||
	t runtime.ObjectTyper
 | 
			
		||||
	c  *client.Client
 | 
			
		||||
	ec *client.ExperimentalClient
 | 
			
		||||
	t  runtime.ObjectTyper
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *clientSwaggerSchema) ValidateBytes(data []byte) error {
 | 
			
		||||
	version, _, err := runtime.UnstructuredJSONScheme.DataVersionAndKind(data)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	if ok := registered.IsRegisteredAPIVersion(version); !ok {
 | 
			
		||||
		return fmt.Errorf("API version %q isn't supported, only supports API versions %q", version, registered.RegisteredVersions)
 | 
			
		||||
	}
 | 
			
		||||
	schemaData, err := c.c.RESTClient.Get().
 | 
			
		||||
		AbsPath("/swaggerapi/api", version).
 | 
			
		||||
func getSchemaAndValidate(c *client.RESTClient, data []byte, group, version string) error {
 | 
			
		||||
	schemaData, err := c.Get().
 | 
			
		||||
		AbsPath("/swaggerapi", group, version).
 | 
			
		||||
		Do().
 | 
			
		||||
		Raw()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
@@ -300,6 +343,28 @@ func (c *clientSwaggerSchema) ValidateBytes(data []byte) error {
 | 
			
		||||
	return schema.ValidateBytes(data)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *clientSwaggerSchema) ValidateBytes(data []byte) error {
 | 
			
		||||
	version, _, err := runtime.UnstructuredJSONScheme.DataVersionAndKind(data)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	if ok := registered.IsRegisteredAPIVersion(version); !ok {
 | 
			
		||||
		return fmt.Errorf("API version %q isn't supported, only supports API versions %q", version, registered.RegisteredVersions)
 | 
			
		||||
	}
 | 
			
		||||
	// First try stable api, if we can't validate using that, try experimental.
 | 
			
		||||
	// If experimental fails, return error from stable api.
 | 
			
		||||
	// TODO: Figure out which group to try once multiple group support is merged
 | 
			
		||||
	//       instead of trying everything.
 | 
			
		||||
	err = getSchemaAndValidate(c.c.RESTClient, data, "api", version)
 | 
			
		||||
	if err != nil && c.ec != nil {
 | 
			
		||||
		errExp := getSchemaAndValidate(c.ec.RESTClient, data, "experimental", version)
 | 
			
		||||
		if errExp == nil {
 | 
			
		||||
			return nil
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// DefaultClientConfig creates a clientcmd.ClientConfig with the following hierarchy:
 | 
			
		||||
//   1.  Use the kubeconfig builder.  The number of merges and overrides here gets a little crazy.  Stay with me.
 | 
			
		||||
//       1.  Merge together the kubeconfig itself.  This is done with the following hierarchy rules:
 | 
			
		||||
 
 | 
			
		||||
@@ -82,6 +82,10 @@ func describerMap(c *client.Client) map[string]Describer {
 | 
			
		||||
	return m
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func expDescriberMap(c *client.ExperimentalClient) map[string]Describer {
 | 
			
		||||
	return map[string]Describer{}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// List of all resource types we can describe
 | 
			
		||||
func DescribableResources() []string {
 | 
			
		||||
	keys := make([]string, 0)
 | 
			
		||||
@@ -95,12 +99,15 @@ func DescribableResources() []string {
 | 
			
		||||
 | 
			
		||||
// Describer returns the default describe functions for each of the standard
 | 
			
		||||
// Kubernetes types.
 | 
			
		||||
func DescriberFor(kind string, c *client.Client) (Describer, bool) {
 | 
			
		||||
	f, ok := describerMap(c)[kind]
 | 
			
		||||
	if ok {
 | 
			
		||||
		return f, true
 | 
			
		||||
func DescriberFor(kind string, c *client.Client, ec *client.ExperimentalClient) (Describer, bool) {
 | 
			
		||||
	var f Describer
 | 
			
		||||
	var ok bool
 | 
			
		||||
	if c != nil {
 | 
			
		||||
		f, ok = describerMap(c)[kind]
 | 
			
		||||
	} else if ec != nil {
 | 
			
		||||
		f, ok = expDescriberMap(ec)[kind]
 | 
			
		||||
	}
 | 
			
		||||
	return nil, false
 | 
			
		||||
	return f, ok
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// DefaultObjectDescriber can describe the default Kubernetes objects.
 | 
			
		||||
 
 | 
			
		||||
@@ -52,7 +52,7 @@ func IsNoSuchReaperError(err error) bool {
 | 
			
		||||
	return ok
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func ReaperFor(kind string, c client.Interface) (Reaper, error) {
 | 
			
		||||
func ReaperFor(kind string, c client.Interface, ec *client.ExperimentalClient) (Reaper, error) {
 | 
			
		||||
	switch kind {
 | 
			
		||||
	case "ReplicationController":
 | 
			
		||||
		return &ReplicationControllerReaper{c, Interval, Timeout}, nil
 | 
			
		||||
 
 | 
			
		||||
@@ -156,7 +156,7 @@ func TestSimpleStop(t *testing.T) {
 | 
			
		||||
	}
 | 
			
		||||
	for _, test := range tests {
 | 
			
		||||
		fake := test.fake
 | 
			
		||||
		reaper, err := ReaperFor(test.kind, fake)
 | 
			
		||||
		reaper, err := ReaperFor(test.kind, fake, nil)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			t.Errorf("unexpected error: %v (%s)", err, test.test)
 | 
			
		||||
		}
 | 
			
		||||
 
 | 
			
		||||
@@ -40,13 +40,3 @@ func GetVersion(w io.Writer, kubeClient client.Interface) {
 | 
			
		||||
func GetClientVersion(w io.Writer) {
 | 
			
		||||
	fmt.Fprintf(w, "Client Version: %#v\n", version.Get())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func GetApiVersions(w io.Writer, kubeClient client.Interface) {
 | 
			
		||||
	apiVersions, err := kubeClient.ServerAPIVersions()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		fmt.Printf("Couldn't get available api versions from server: %v\n", err)
 | 
			
		||||
		os.Exit(1)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	fmt.Fprintf(w, "Available Server Api Versions: %#v\n", *apiVersions)
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -89,7 +89,7 @@ func ServeImageOrFail(f *Framework, test string, image string) {
 | 
			
		||||
	defer func() {
 | 
			
		||||
		// Resize the replication controller to zero to get rid of pods.
 | 
			
		||||
		By("Cleaning up the replication controller")
 | 
			
		||||
		rcReaper, err := kubectl.ReaperFor("ReplicationController", f.Client)
 | 
			
		||||
		rcReaper, err := kubectl.ReaperFor("ReplicationController", f.Client, nil)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			Logf("Failed to cleanup replication controller %v: %v.", controller.Name, err)
 | 
			
		||||
		}
 | 
			
		||||
 
 | 
			
		||||
@@ -190,7 +190,7 @@ func RCFromManifest(fileName string) *api.ReplicationController {
 | 
			
		||||
 | 
			
		||||
// StopRC stops the rc via kubectl's stop library
 | 
			
		||||
func StopRC(rc *api.ReplicationController, restClient *client.Client) error {
 | 
			
		||||
	reaper, err := kubectl.ReaperFor("ReplicationController", restClient)
 | 
			
		||||
	reaper, err := kubectl.ReaperFor("ReplicationController", restClient, nil)
 | 
			
		||||
	if err != nil || reaper == nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user