mirror of
https://github.com/k3s-io/kubernetes.git
synced 2026-01-05 07:27:21 +00:00
Add third party support to kubectl
This commit is contained in:
@@ -39,6 +39,7 @@ import (
|
||||
"k8s.io/kubernetes/pkg/api/meta"
|
||||
"k8s.io/kubernetes/pkg/api/unversioned"
|
||||
"k8s.io/kubernetes/pkg/api/validation"
|
||||
"k8s.io/kubernetes/pkg/apimachinery"
|
||||
"k8s.io/kubernetes/pkg/apimachinery/registered"
|
||||
"k8s.io/kubernetes/pkg/apis/autoscaling"
|
||||
"k8s.io/kubernetes/pkg/apis/batch"
|
||||
@@ -51,6 +52,7 @@ import (
|
||||
"k8s.io/kubernetes/pkg/kubectl"
|
||||
"k8s.io/kubernetes/pkg/kubectl/resource"
|
||||
"k8s.io/kubernetes/pkg/labels"
|
||||
"k8s.io/kubernetes/pkg/registry/thirdpartyresourcedata"
|
||||
"k8s.io/kubernetes/pkg/runtime"
|
||||
"k8s.io/kubernetes/pkg/runtime/serializer/json"
|
||||
utilflag "k8s.io/kubernetes/pkg/util/flag"
|
||||
@@ -69,8 +71,9 @@ type Factory struct {
|
||||
clients *ClientCache
|
||||
flags *pflag.FlagSet
|
||||
|
||||
// Returns interfaces for dealing with arbitrary runtime.Objects.
|
||||
Object func() (meta.RESTMapper, runtime.ObjectTyper)
|
||||
// Returns interfaces for dealing with arbitrary runtime.Objects. If thirdPartyDiscovery is true, performs API calls
|
||||
// to discovery dynamic API objects registered by third parties.
|
||||
Object func(thirdPartyDiscovery bool) (meta.RESTMapper, runtime.ObjectTyper)
|
||||
// Returns interfaces for decoding objects - if toInternal is set, decoded objects will be converted
|
||||
// into their internal form (if possible). Eventually the internal form will be removed as an option,
|
||||
// and only versioned objects will be returned.
|
||||
@@ -179,6 +182,31 @@ func DefaultGenerators(cmdName string) map[string]kubectl.Generator {
|
||||
return generators[cmdName]
|
||||
}
|
||||
|
||||
func getGroupVersionKinds(gvks []unversioned.GroupVersionKind, group string) []unversioned.GroupVersionKind {
|
||||
result := []unversioned.GroupVersionKind{}
|
||||
for ix := range gvks {
|
||||
if gvks[ix].Group == group {
|
||||
result = append(result, gvks[ix])
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func makeInterfacesFor(versionList []unversioned.GroupVersion) func(version unversioned.GroupVersion) (*meta.VersionInterfaces, error) {
|
||||
accessor := meta.NewAccessor()
|
||||
return func(version unversioned.GroupVersion) (*meta.VersionInterfaces, error) {
|
||||
for ix := range versionList {
|
||||
if versionList[ix].String() == version.String() {
|
||||
return &meta.VersionInterfaces{
|
||||
ObjectConvertor: thirdpartyresourcedata.NewThirdPartyObjectConverter(api.Scheme),
|
||||
MetadataAccessor: accessor,
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
return nil, fmt.Errorf("unsupported storage version: %s (valid: %v)", version, versionList)
|
||||
}
|
||||
}
|
||||
|
||||
// NewFactory creates a factory with the default Kubernetes resources defined
|
||||
// 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.
|
||||
@@ -199,17 +227,61 @@ func NewFactory(optionalClientConfig clientcmd.ClientConfig) *Factory {
|
||||
clients: clients,
|
||||
flags: flags,
|
||||
|
||||
Object: func() (meta.RESTMapper, runtime.ObjectTyper) {
|
||||
// If discoverDynamicAPIs is true, make API calls to the discovery service to find APIs that
|
||||
// have been dynamically added to the apiserver
|
||||
Object: func(discoverDynamicAPIs bool) (meta.RESTMapper, runtime.ObjectTyper) {
|
||||
cfg, err := clientConfig.ClientConfig()
|
||||
CheckErr(err)
|
||||
cmdApiVersion := unversioned.GroupVersion{}
|
||||
if cfg.GroupVersion != nil {
|
||||
cmdApiVersion = *cfg.GroupVersion
|
||||
}
|
||||
if discoverDynamicAPIs {
|
||||
client, err := clients.ClientForVersion(&unversioned.GroupVersion{Version: "v1"})
|
||||
CheckErr(err)
|
||||
|
||||
versions, gvks, err := GetThirdPartyGroupVersions(client.Discovery())
|
||||
CheckErr(err)
|
||||
if len(versions) > 0 {
|
||||
priorityMapper, ok := mapper.RESTMapper.(meta.PriorityRESTMapper)
|
||||
if !ok {
|
||||
CheckErr(fmt.Errorf("expected PriorityMapper, saw: %v", mapper.RESTMapper))
|
||||
return nil, nil
|
||||
}
|
||||
multiMapper, ok := priorityMapper.Delegate.(meta.MultiRESTMapper)
|
||||
if !ok {
|
||||
CheckErr(fmt.Errorf("unexpected type: %v", mapper.RESTMapper))
|
||||
return nil, nil
|
||||
}
|
||||
groupsMap := map[string][]unversioned.GroupVersion{}
|
||||
for _, version := range versions {
|
||||
groupsMap[version.Group] = append(groupsMap[version.Group], version)
|
||||
}
|
||||
for group, versionList := range groupsMap {
|
||||
preferredExternalVersion := versionList[0]
|
||||
|
||||
thirdPartyMapper, err := kubectl.NewThirdPartyResourceMapper(versionList, getGroupVersionKinds(gvks, group))
|
||||
CheckErr(err)
|
||||
accessor := meta.NewAccessor()
|
||||
groupMeta := apimachinery.GroupMeta{
|
||||
GroupVersion: preferredExternalVersion,
|
||||
GroupVersions: versionList,
|
||||
RESTMapper: thirdPartyMapper,
|
||||
SelfLinker: runtime.SelfLinker(accessor),
|
||||
InterfacesFor: makeInterfacesFor(versionList),
|
||||
}
|
||||
|
||||
CheckErr(registered.RegisterGroup(groupMeta))
|
||||
registered.AddThirdPartyAPIGroupVersions(versionList...)
|
||||
multiMapper = append(meta.MultiRESTMapper{thirdPartyMapper}, multiMapper...)
|
||||
}
|
||||
priorityMapper.Delegate = multiMapper
|
||||
// Re-assign to the RESTMapper here because priorityMapper is actually a copy, so if we
|
||||
// don't re-assign, the above assignement won't actually update mapper.RESTMapper
|
||||
mapper.RESTMapper = priorityMapper
|
||||
}
|
||||
}
|
||||
outputRESTMapper := kubectl.OutputVersionMapper{RESTMapper: mapper, OutputVersions: []unversioned.GroupVersion{cmdApiVersion}}
|
||||
|
||||
// eventually this should allow me choose a group priority based on the order of the discovery doc, for now hardcode a given order
|
||||
priorityRESTMapper := meta.PriorityRESTMapper{
|
||||
Delegate: outputRESTMapper,
|
||||
ResourcePriority: []unversioned.GroupVersionResource{
|
||||
@@ -223,7 +295,6 @@ func NewFactory(optionalClientConfig clientcmd.ClientConfig) *Factory {
|
||||
{Group: metrics.GroupName, Version: meta.AnyVersion, Kind: meta.AnyKind},
|
||||
},
|
||||
}
|
||||
|
||||
return priorityRESTMapper, api.Scheme
|
||||
},
|
||||
Client: func() (*client.Client, error) {
|
||||
@@ -233,22 +304,39 @@ func NewFactory(optionalClientConfig clientcmd.ClientConfig) *Factory {
|
||||
return clients.ClientConfigForVersion(nil)
|
||||
},
|
||||
ClientForMapping: func(mapping *meta.RESTMapping) (resource.RESTClient, error) {
|
||||
gvk := mapping.GroupVersionKind
|
||||
mappingVersion := mapping.GroupVersionKind.GroupVersion()
|
||||
client, err := clients.ClientForVersion(&mappingVersion)
|
||||
c, err := clients.ClientForVersion(&mappingVersion)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
switch mapping.GroupVersionKind.Group {
|
||||
switch gvk.Group {
|
||||
case api.GroupName:
|
||||
return client.RESTClient, nil
|
||||
return c.RESTClient, nil
|
||||
case autoscaling.GroupName:
|
||||
return client.AutoscalingClient.RESTClient, nil
|
||||
return c.AutoscalingClient.RESTClient, nil
|
||||
case batch.GroupName:
|
||||
return client.BatchClient.RESTClient, nil
|
||||
return c.BatchClient.RESTClient, nil
|
||||
case extensions.GroupName:
|
||||
return client.ExtensionsClient.RESTClient, nil
|
||||
return c.ExtensionsClient.RESTClient, nil
|
||||
case api.SchemeGroupVersion.Group:
|
||||
return c.RESTClient, nil
|
||||
case extensions.SchemeGroupVersion.Group:
|
||||
return c.ExtensionsClient.RESTClient, nil
|
||||
default:
|
||||
if !registered.IsThirdPartyAPIGroupVersion(gvk.GroupVersion()) {
|
||||
return nil, fmt.Errorf("unknown api group/version: %s", gvk.String())
|
||||
}
|
||||
cfg, err := clientConfig.ClientConfig()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
gv := gvk.GroupVersion()
|
||||
cfg.GroupVersion = &gv
|
||||
cfg.APIPath = "/apis"
|
||||
cfg.Codec = thirdpartyresourcedata.NewCodec(c.ExtensionsClient.RESTClient.Codec(), gvk.Kind)
|
||||
return restclient.RESTClientFor(cfg)
|
||||
}
|
||||
return nil, fmt.Errorf("unable to get RESTClient for resource '%s'", mapping.Resource)
|
||||
},
|
||||
Describer: func(mapping *meta.RESTMapping) (kubectl.Describer, error) {
|
||||
mappingVersion := mapping.GroupVersionKind.GroupVersion()
|
||||
@@ -810,6 +898,10 @@ func (c *clientSwaggerSchema) ValidateBytes(data []byte) error {
|
||||
}
|
||||
return getSchemaAndValidate(c.c.BatchClient.RESTClient, data, "apis/", gvk.GroupVersion().String(), c.cacheDir)
|
||||
}
|
||||
if registered.IsThirdPartyAPIGroupVersion(gvk.GroupVersion()) {
|
||||
// Don't attempt to validate third party objects
|
||||
return nil
|
||||
}
|
||||
if gvk.Group == extensions.GroupName {
|
||||
if c.c.ExtensionsClient == nil {
|
||||
return errors.New("unable to validate: no experimental client")
|
||||
@@ -874,8 +966,7 @@ func DefaultClientConfig(flags *pflag.FlagSet) clientcmd.ClientConfig {
|
||||
}
|
||||
|
||||
// PrintObject prints an api object given command line flags to modify the output format
|
||||
func (f *Factory) PrintObject(cmd *cobra.Command, obj runtime.Object, out io.Writer) error {
|
||||
mapper, _ := f.Object()
|
||||
func (f *Factory) PrintObject(cmd *cobra.Command, mapper meta.RESTMapper, obj runtime.Object, out io.Writer) error {
|
||||
gvk, err := api.Scheme.ObjectKind(obj)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -936,8 +1027,8 @@ func (f *Factory) PrinterForMapping(cmd *cobra.Command, mapping *meta.RESTMappin
|
||||
}
|
||||
|
||||
// One stop shopping for a Builder
|
||||
func (f *Factory) NewBuilder() *resource.Builder {
|
||||
mapper, typer := f.Object()
|
||||
func (f *Factory) NewBuilder(thirdPartyDiscovery bool) *resource.Builder {
|
||||
mapper, typer := f.Object(thirdPartyDiscovery)
|
||||
|
||||
return resource.NewBuilder(mapper, typer, resource.ClientMapperFunc(f.ClientForMapping), f.Decoder(true))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user