mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-28 22:17:14 +00:00
Merge pull request #12405 from uluyol/kubectlexp
Add experimental api support to kubectl
This commit is contained in:
commit
59da05efdf
@ -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
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user