mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-29 22:46:12 +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",
|
"PodProxyOptions",
|
||||||
"Daemon")
|
"Daemon")
|
||||||
|
|
||||||
mapper := api.NewDefaultRESTMapper(versions, InterfacesFor, importPrefix, ignoredKinds, rootScoped)
|
mapper := api.NewDefaultRESTMapper("api", versions, InterfacesFor, importPrefix, ignoredKinds, rootScoped)
|
||||||
// setup aliases for groups of resources
|
// setup aliases for groups of resources
|
||||||
mapper.AddResourceAlias("all", userResources...)
|
mapper.AddResourceAlias("all", userResources...)
|
||||||
RESTMapper = mapper
|
RESTMapper = mapper
|
||||||
|
@ -33,11 +33,12 @@ func RegisterRESTMapper(m meta.RESTMapper) {
|
|||||||
RESTMapper = append(RESTMapper.(meta.MultiRESTMapper), m)
|
RESTMapper = append(RESTMapper.(meta.MultiRESTMapper), m)
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewDefaultRESTMapper(versions []string, interfacesFunc meta.VersionInterfacesFunc, importPathPrefix string,
|
func NewDefaultRESTMapper(group string, versions []string, interfacesFunc meta.VersionInterfacesFunc,
|
||||||
ignoredKinds, rootScoped util.StringSet) *meta.DefaultRESTMapper {
|
importPathPrefix string, ignoredKinds, rootScoped util.StringSet) *meta.DefaultRESTMapper {
|
||||||
|
|
||||||
mapper := meta.NewDefaultRESTMapper(versions, interfacesFunc)
|
mapper := meta.NewDefaultRESTMapper(group, versions, interfacesFunc)
|
||||||
// enumerate all supported versions, get the kinds, and register with the mapper how to address our resources.
|
// enumerate all supported versions, get the kinds, and register with the mapper how to address
|
||||||
|
// our resources.
|
||||||
for _, version := range versions {
|
for _, version := range versions {
|
||||||
for kind, oType := range Scheme.KnownTypes(version) {
|
for kind, oType := range Scheme.KnownTypes(version) {
|
||||||
// TODO: Remove import path prefix check.
|
// 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
|
// RESTMapper allows clients to map resources to kind, and map kind and version
|
||||||
// to interfaces for manipulating those objects. It is primarily intended for
|
// to interfaces for manipulating those objects. It is primarily intended for
|
||||||
// consumers of Kubernetes compatible REST APIs as defined in docs/api-conventions.md.
|
// 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 {
|
type RESTMapper interface {
|
||||||
VersionAndKindForResource(resource string) (defaultVersion, kind string, err error)
|
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)
|
RESTMapping(kind string, versions ...string) (*RESTMapping, error)
|
||||||
AliasesForResource(resource string) ([]string, bool)
|
AliasesForResource(resource string) ([]string, bool)
|
||||||
ResourceSingularizer(resource string) (singular string, err error)
|
ResourceSingularizer(resource string) (singular string, err error)
|
||||||
|
@ -76,6 +76,7 @@ type DefaultRESTMapper struct {
|
|||||||
mapping map[string]typeMeta
|
mapping map[string]typeMeta
|
||||||
reverse map[typeMeta]string
|
reverse map[typeMeta]string
|
||||||
scopes map[typeMeta]RESTScope
|
scopes map[typeMeta]RESTScope
|
||||||
|
group string
|
||||||
versions []string
|
versions []string
|
||||||
plurals map[string]string
|
plurals map[string]string
|
||||||
singulars 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
|
// NewDefaultRESTMapper initializes a mapping between Kind and APIVersion
|
||||||
// to a resource name and back based on the objects in a runtime.Scheme
|
// 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
|
// and the Kubernetes API conventions. Takes a group name, a priority list of the versions
|
||||||
// search when an object has no default version (set empty to return an error)
|
// 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.
|
// 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)
|
mapping := make(map[string]typeMeta)
|
||||||
reverse := make(map[typeMeta]string)
|
reverse := make(map[typeMeta]string)
|
||||||
scopes := make(map[typeMeta]RESTScope)
|
scopes := make(map[typeMeta]RESTScope)
|
||||||
@ -103,6 +104,7 @@ func NewDefaultRESTMapper(versions []string, f VersionInterfacesFunc) *DefaultRE
|
|||||||
mapping: mapping,
|
mapping: mapping,
|
||||||
reverse: reverse,
|
reverse: reverse,
|
||||||
scopes: scopes,
|
scopes: scopes,
|
||||||
|
group: group,
|
||||||
versions: versions,
|
versions: versions,
|
||||||
plurals: plurals,
|
plurals: plurals,
|
||||||
singulars: singulars,
|
singulars: singulars,
|
||||||
@ -174,6 +176,13 @@ func (m *DefaultRESTMapper) VersionAndKindForResource(resource string) (defaultV
|
|||||||
return meta.APIVersion, meta.Kind, nil
|
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
|
// 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
|
// 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
|
// 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
|
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
|
// RESTMapping provides the REST mapping for the resource based on the resource
|
||||||
// kind and version. This implementation supports multiple REST schemas and
|
// kind and version. This implementation supports multiple REST schemas and
|
||||||
// return the first match.
|
// return the first match.
|
||||||
|
@ -93,7 +93,7 @@ func TestRESTMapperVersionAndKindForResource(t *testing.T) {
|
|||||||
{Resource: "internalObjects", MixedCase: true, Kind: "InternalObject", APIVersion: "test"},
|
{Resource: "internalObjects", MixedCase: true, Kind: "InternalObject", APIVersion: "test"},
|
||||||
}
|
}
|
||||||
for i, testCase := range testCases {
|
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)
|
mapper.Add(RESTScopeNamespace, testCase.Kind, testCase.APIVersion, testCase.MixedCase)
|
||||||
v, k, err := mapper.VersionAndKindForResource(testCase.Resource)
|
v, k, err := mapper.VersionAndKindForResource(testCase.Resource)
|
||||||
hasErr := err != nil
|
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) {
|
func TestKindToResource(t *testing.T) {
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
Kind string
|
Kind string
|
||||||
@ -159,7 +186,7 @@ func TestRESTMapperResourceSingularizer(t *testing.T) {
|
|||||||
{Kind: "lowercases", APIVersion: "test", MixedCase: false, Plural: "lowercases", Singular: "lowercases"},
|
{Kind: "lowercases", APIVersion: "test", MixedCase: false, Plural: "lowercases", Singular: "lowercases"},
|
||||||
}
|
}
|
||||||
for i, testCase := range testCases {
|
for i, testCase := range testCases {
|
||||||
mapper := NewDefaultRESTMapper([]string{"test"}, fakeInterfaces)
|
mapper := NewDefaultRESTMapper("tgroup", []string{"test"}, fakeInterfaces)
|
||||||
// create singular/plural mapping
|
// create singular/plural mapping
|
||||||
mapper.Add(RESTScopeNamespace, testCase.Kind, testCase.APIVersion, testCase.MixedCase)
|
mapper.Add(RESTScopeNamespace, testCase.Kind, testCase.APIVersion, testCase.MixedCase)
|
||||||
singular, _ := mapper.ResourceSingularizer(testCase.Plural)
|
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
|
// TODO: add test for a resource that exists in one version but not another
|
||||||
}
|
}
|
||||||
for i, testCase := range testCases {
|
for i, testCase := range testCases {
|
||||||
mapper := NewDefaultRESTMapper(testCase.DefaultVersions, fakeInterfaces)
|
mapper := NewDefaultRESTMapper("tgroup", testCase.DefaultVersions, fakeInterfaces)
|
||||||
mapper.Add(RESTScopeNamespace, "InternalObject", "test", testCase.MixedCase)
|
mapper.Add(RESTScopeNamespace, "InternalObject", "test", testCase.MixedCase)
|
||||||
mapping, err := mapper.RESTMapping(testCase.Kind, testCase.APIVersions...)
|
mapping, err := mapper.RESTMapping(testCase.Kind, testCase.APIVersions...)
|
||||||
hasErr := err != nil
|
hasErr := err != nil
|
||||||
@ -225,7 +252,7 @@ func TestRESTMapperRESTMapping(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestRESTMapperRESTMappingSelectsVersion(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, "InternalObject", "test1", false)
|
||||||
mapper.Add(RESTScopeNamespace, "OtherObject", "test2", false)
|
mapper.Add(RESTScopeNamespace, "OtherObject", "test2", false)
|
||||||
|
|
||||||
@ -278,7 +305,7 @@ func TestRESTMapperRESTMappingSelectsVersion(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestRESTMapperReportsErrorOnBadVersion(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)
|
mapper.Add(RESTScopeNamespace, "InternalObject", "test1", false)
|
||||||
_, err := mapper.RESTMapping("InternalObject", "test1")
|
_, err := mapper.RESTMapping("InternalObject", "test1")
|
||||||
if err == nil {
|
if err == nil {
|
||||||
|
@ -88,7 +88,7 @@ func interfacesFor(version string) (*meta.VersionInterfaces, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func newMapper() *meta.DefaultRESTMapper {
|
func newMapper() *meta.DefaultRESTMapper {
|
||||||
return meta.NewDefaultRESTMapper(versions, interfacesFor)
|
return meta.NewDefaultRESTMapper("testgroup", versions, interfacesFor)
|
||||||
}
|
}
|
||||||
|
|
||||||
func addTestTypes() {
|
func addTestTypes() {
|
||||||
|
@ -55,7 +55,7 @@ func init() {
|
|||||||
|
|
||||||
ignoredKinds := util.NewStringSet()
|
ignoredKinds := util.NewStringSet()
|
||||||
|
|
||||||
RESTMapper = api.NewDefaultRESTMapper(Versions, InterfacesFor, importPrefix, ignoredKinds, rootScoped)
|
RESTMapper = api.NewDefaultRESTMapper("experimental", Versions, InterfacesFor, importPrefix, ignoredKinds, rootScoped)
|
||||||
api.RegisterRESTMapper(RESTMapper)
|
api.RegisterRESTMapper(RESTMapper)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,12 +17,13 @@ limitations under the License.
|
|||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
"k8s.io/kubernetes/pkg/kubectl"
|
"k8s.io/kubernetes/pkg/api"
|
||||||
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
|
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -40,7 +41,7 @@ func NewCmdApiVersions(f *cmdutil.Factory, out io.Writer) *cobra.Command {
|
|||||||
return cmd
|
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" {
|
if len(os.Args) > 1 && os.Args[1] == "apiversions" {
|
||||||
printDeprecationWarning("api-versions", "apiversions")
|
printDeprecationWarning("api-versions", "apiversions")
|
||||||
}
|
}
|
||||||
@ -50,6 +51,24 @@ func RunApiVersions(f *cmdutil.Factory, out io.Writer) error {
|
|||||||
return err
|
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
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -83,7 +83,7 @@ func newExternalScheme() (*runtime.Scheme, meta.RESTMapper, runtime.Codec) {
|
|||||||
|
|
||||||
codec := runtime.CodecFor(scheme, "unlikelyversion")
|
codec := runtime.CodecFor(scheme, "unlikelyversion")
|
||||||
validVersion := testapi.Version()
|
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{
|
return &meta.VersionInterfaces{
|
||||||
Codec: runtime.CodecFor(scheme, version),
|
Codec: runtime.CodecFor(scheme, version),
|
||||||
ObjectConvertor: scheme,
|
ObjectConvertor: scheme,
|
||||||
|
@ -22,17 +22,17 @@ import (
|
|||||||
"k8s.io/kubernetes/pkg/client/clientcmd"
|
"k8s.io/kubernetes/pkg/client/clientcmd"
|
||||||
)
|
)
|
||||||
|
|
||||||
func NewClientCache(loader clientcmd.ClientConfig) *clientCache {
|
func NewClientCache(loader clientcmd.ClientConfig) *ClientCache {
|
||||||
return &clientCache{
|
return &ClientCache{
|
||||||
clients: make(map[string]*client.Client),
|
clients: make(map[string]*client.Client),
|
||||||
configs: make(map[string]*client.Config),
|
configs: make(map[string]*client.Config),
|
||||||
loader: loader,
|
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
|
// is invoked only once
|
||||||
type clientCache struct {
|
type ClientCache struct {
|
||||||
loader clientcmd.ClientConfig
|
loader clientcmd.ClientConfig
|
||||||
clients map[string]*client.Client
|
clients map[string]*client.Client
|
||||||
configs map[string]*client.Config
|
configs map[string]*client.Config
|
||||||
@ -42,7 +42,7 @@ type clientCache struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ClientConfigForVersion returns the correct config for a server
|
// 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 {
|
if c.defaultConfig == nil {
|
||||||
config, err := c.loader.ClientConfig()
|
config, err := c.loader.ClientConfig()
|
||||||
if err != nil {
|
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
|
// ClientForVersion initializes or reuses a client for the specified version, or returns an
|
||||||
// error if that is not possible
|
// 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 {
|
if client, ok := c.clients[version]; ok {
|
||||||
return client, nil
|
return client, nil
|
||||||
}
|
}
|
||||||
|
@ -17,6 +17,7 @@ limitations under the License.
|
|||||||
package util
|
package util
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
@ -27,7 +28,6 @@ import (
|
|||||||
"github.com/spf13/pflag"
|
"github.com/spf13/pflag"
|
||||||
|
|
||||||
"k8s.io/kubernetes/pkg/api"
|
"k8s.io/kubernetes/pkg/api"
|
||||||
"k8s.io/kubernetes/pkg/api/latest"
|
|
||||||
"k8s.io/kubernetes/pkg/api/meta"
|
"k8s.io/kubernetes/pkg/api/meta"
|
||||||
"k8s.io/kubernetes/pkg/api/registered"
|
"k8s.io/kubernetes/pkg/api/registered"
|
||||||
"k8s.io/kubernetes/pkg/api/validation"
|
"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
|
// TODO: pass the various interfaces on the factory directly into the command constructors (so the
|
||||||
// commands are decoupled from the factory).
|
// commands are decoupled from the factory).
|
||||||
type Factory struct {
|
type Factory struct {
|
||||||
clients *clientCache
|
clients *ClientCache
|
||||||
flags *pflag.FlagSet
|
flags *pflag.FlagSet
|
||||||
generators map[string]kubectl.Generator
|
generators map[string]kubectl.Generator
|
||||||
|
|
||||||
@ -57,6 +57,8 @@ type Factory struct {
|
|||||||
Object func() (meta.RESTMapper, runtime.ObjectTyper)
|
Object func() (meta.RESTMapper, runtime.ObjectTyper)
|
||||||
// Returns a client for accessing Kubernetes resources or an error.
|
// Returns a client for accessing Kubernetes resources or an error.
|
||||||
Client func() (*client.Client, 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.
|
// Returns a client.Config for accessing the Kubernetes server.
|
||||||
ClientConfig func() (*client.Config, error)
|
ClientConfig func() (*client.Config, error)
|
||||||
// Returns a RESTClient for working with the specified RESTMapping or an error. This is intended
|
// 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 nil, then flags will be bound to a new clientcmd.ClientConfig.
|
||||||
// if optionalClientConfig is not nil, then this factory will make use of it.
|
// if optionalClientConfig is not nil, then this factory will make use of it.
|
||||||
func NewFactory(optionalClientConfig clientcmd.ClientConfig) *Factory {
|
func NewFactory(optionalClientConfig clientcmd.ClientConfig) *Factory {
|
||||||
mapper := kubectl.ShortcutExpander{RESTMapper: latest.RESTMapper}
|
mapper := kubectl.ShortcutExpander{RESTMapper: api.RESTMapper}
|
||||||
|
|
||||||
flags := pflag.NewFlagSet("", pflag.ContinueOnError)
|
flags := pflag.NewFlagSet("", pflag.ContinueOnError)
|
||||||
flags.SetNormalizeFunc(util.WarnWordSepNormalizeFunc) // Warn for "_" flags
|
flags.SetNormalizeFunc(util.WarnWordSepNormalizeFunc) // Warn for "_" flags
|
||||||
@ -109,6 +111,25 @@ func NewFactory(optionalClientConfig clientcmd.ClientConfig) *Factory {
|
|||||||
|
|
||||||
clients := NewClientCache(clientConfig)
|
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{
|
return &Factory{
|
||||||
clients: clients,
|
clients: clients,
|
||||||
flags: flags,
|
flags: flags,
|
||||||
@ -124,26 +145,46 @@ func NewFactory(optionalClientConfig clientcmd.ClientConfig) *Factory {
|
|||||||
Client: func() (*client.Client, error) {
|
Client: func() (*client.Client, error) {
|
||||||
return clients.ClientForVersion("")
|
return clients.ClientForVersion("")
|
||||||
},
|
},
|
||||||
|
ExperimentalClient: func() (*client.ExperimentalClient, error) {
|
||||||
|
return experimentalClient, experimentalClientErr
|
||||||
|
},
|
||||||
ClientConfig: func() (*client.Config, error) {
|
ClientConfig: func() (*client.Config, error) {
|
||||||
return clients.ClientConfigForVersion("")
|
return clients.ClientConfigForVersion("")
|
||||||
},
|
},
|
||||||
RESTClient: func(mapping *meta.RESTMapping) (resource.RESTClient, error) {
|
RESTClient: func(mapping *meta.RESTMapping) (resource.RESTClient, error) {
|
||||||
client, err := clients.ClientForVersion(mapping.APIVersion)
|
group, err := api.RESTMapper.GroupForResource(mapping.Resource)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
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) {
|
Describer: func(mapping *meta.RESTMapping) (kubectl.Describer, error) {
|
||||||
client, err := clients.ClientForVersion(mapping.APIVersion)
|
group, err := api.RESTMapper.GroupForResource(mapping.Resource)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
describer, ok := kubectl.DescriberFor(mapping.Kind, client)
|
client, expClient, err := getBothClients(group, mapping.APIVersion)
|
||||||
if !ok {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("no description has been implemented for %q", mapping.Kind)
|
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) {
|
Printer: func(mapping *meta.RESTMapping, noHeaders, withNamespace bool, wide bool, columnLabels []string) (kubectl.ResourcePrinter, error) {
|
||||||
return kubectl.NewHumanReadablePrinter(noHeaders, withNamespace, wide, columnLabels), nil
|
return kubectl.NewHumanReadablePrinter(noHeaders, withNamespace, wide, columnLabels), nil
|
||||||
@ -192,18 +233,26 @@ func NewFactory(optionalClientConfig clientcmd.ClientConfig) *Factory {
|
|||||||
return meta.NewAccessor().Labels(object)
|
return meta.NewAccessor().Labels(object)
|
||||||
},
|
},
|
||||||
Scaler: func(mapping *meta.RESTMapping) (kubectl.Scaler, error) {
|
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 {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return kubectl.ScalerFor(mapping.Kind, kubectl.NewScalerClient(client))
|
return kubectl.ScalerFor(mapping.Kind, kubectl.NewScalerClient(client))
|
||||||
},
|
},
|
||||||
Reaper: func(mapping *meta.RESTMapping) (kubectl.Reaper, error) {
|
Reaper: func(mapping *meta.RESTMapping) (kubectl.Reaper, error) {
|
||||||
client, err := clients.ClientForVersion(mapping.APIVersion)
|
group, err := api.RESTMapper.GroupForResource(mapping.Resource)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
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) {
|
Validator: func() (validation.Schema, error) {
|
||||||
if flags.Lookup("validate").Value.String() == "true" {
|
if flags.Lookup("validate").Value.String() == "true" {
|
||||||
@ -211,7 +260,7 @@ func NewFactory(optionalClientConfig clientcmd.ClientConfig) *Factory {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return &clientSwaggerSchema{client, api.Scheme}, nil
|
return &clientSwaggerSchema{client, experimentalClient, api.Scheme}, nil
|
||||||
}
|
}
|
||||||
return validation.NullSchema{}, nil
|
return validation.NullSchema{}, nil
|
||||||
},
|
},
|
||||||
@ -274,20 +323,14 @@ func getServicePorts(spec api.ServiceSpec) []string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type clientSwaggerSchema struct {
|
type clientSwaggerSchema struct {
|
||||||
c *client.Client
|
c *client.Client
|
||||||
t runtime.ObjectTyper
|
ec *client.ExperimentalClient
|
||||||
|
t runtime.ObjectTyper
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *clientSwaggerSchema) ValidateBytes(data []byte) error {
|
func getSchemaAndValidate(c *client.RESTClient, data []byte, group, version string) error {
|
||||||
version, _, err := runtime.UnstructuredJSONScheme.DataVersionAndKind(data)
|
schemaData, err := c.Get().
|
||||||
if err != nil {
|
AbsPath("/swaggerapi", group, version).
|
||||||
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).
|
|
||||||
Do().
|
Do().
|
||||||
Raw()
|
Raw()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -300,6 +343,28 @@ func (c *clientSwaggerSchema) ValidateBytes(data []byte) error {
|
|||||||
return schema.ValidateBytes(data)
|
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:
|
// 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. 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:
|
// 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
|
return m
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func expDescriberMap(c *client.ExperimentalClient) map[string]Describer {
|
||||||
|
return map[string]Describer{}
|
||||||
|
}
|
||||||
|
|
||||||
// List of all resource types we can describe
|
// List of all resource types we can describe
|
||||||
func DescribableResources() []string {
|
func DescribableResources() []string {
|
||||||
keys := make([]string, 0)
|
keys := make([]string, 0)
|
||||||
@ -95,12 +99,15 @@ func DescribableResources() []string {
|
|||||||
|
|
||||||
// Describer returns the default describe functions for each of the standard
|
// Describer returns the default describe functions for each of the standard
|
||||||
// Kubernetes types.
|
// Kubernetes types.
|
||||||
func DescriberFor(kind string, c *client.Client) (Describer, bool) {
|
func DescriberFor(kind string, c *client.Client, ec *client.ExperimentalClient) (Describer, bool) {
|
||||||
f, ok := describerMap(c)[kind]
|
var f Describer
|
||||||
if ok {
|
var ok bool
|
||||||
return f, true
|
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.
|
// DefaultObjectDescriber can describe the default Kubernetes objects.
|
||||||
|
@ -52,7 +52,7 @@ func IsNoSuchReaperError(err error) bool {
|
|||||||
return ok
|
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 {
|
switch kind {
|
||||||
case "ReplicationController":
|
case "ReplicationController":
|
||||||
return &ReplicationControllerReaper{c, Interval, Timeout}, nil
|
return &ReplicationControllerReaper{c, Interval, Timeout}, nil
|
||||||
|
@ -156,7 +156,7 @@ func TestSimpleStop(t *testing.T) {
|
|||||||
}
|
}
|
||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
fake := test.fake
|
fake := test.fake
|
||||||
reaper, err := ReaperFor(test.kind, fake)
|
reaper, err := ReaperFor(test.kind, fake, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("unexpected error: %v (%s)", err, test.test)
|
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) {
|
func GetClientVersion(w io.Writer) {
|
||||||
fmt.Fprintf(w, "Client Version: %#v\n", version.Get())
|
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() {
|
defer func() {
|
||||||
// Resize the replication controller to zero to get rid of pods.
|
// Resize the replication controller to zero to get rid of pods.
|
||||||
By("Cleaning up the replication controller")
|
By("Cleaning up the replication controller")
|
||||||
rcReaper, err := kubectl.ReaperFor("ReplicationController", f.Client)
|
rcReaper, err := kubectl.ReaperFor("ReplicationController", f.Client, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
Logf("Failed to cleanup replication controller %v: %v.", controller.Name, err)
|
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
|
// StopRC stops the rc via kubectl's stop library
|
||||||
func StopRC(rc *api.ReplicationController, restClient *client.Client) error {
|
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 {
|
if err != nil || reaper == nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user