wrap restclientgetter with match version option

pull/8/head
David Eads 2018-05-10 08:13:45 -04:00
parent 1f5357034b
commit 080d6a4b0d
6 changed files with 144 additions and 79 deletions

View File

@ -276,11 +276,12 @@ func NewKubectlCommand(in io.Reader, out, err io.Writer) *cobra.Command {
kubeConfigFlags := cmdutil.NewConfigFlags()
kubeConfigFlags.AddFlags(cmds.PersistentFlags())
matchVersionKubeConfigFlags := cmdutil.NewMatchVersionFlags(kubeConfigFlags)
matchVersionKubeConfigFlags.AddFlags(cmds.PersistentFlags())
cmds.PersistentFlags().AddGoFlagSet(flag.CommandLine)
f := cmdutil.NewFactory(kubeConfigFlags)
f.BindFlags(cmds.PersistentFlags())
f := cmdutil.NewFactory(matchVersionKubeConfigFlags)
// Sending in 'nil' for the getLanguageFn() results in using
// the LANG environment variable.

View File

@ -11,6 +11,7 @@ go_library(
"factory_client_access.go",
"factory_object_mapping.go",
"helpers.go",
"kubectl_match_version.go",
"printing.go",
],
importpath = "k8s.io/kubernetes/pkg/kubectl/cmd/util",

View File

@ -53,6 +53,7 @@ const (
flagUsername = "username"
flagPassword = "password"
flagTimeout = "request-timeout"
flagHTTPCacheDir = "cache-dir"
)
// TODO(juanvallejo): move to pkg/kubectl/genericclioptions once
@ -225,7 +226,7 @@ func (f *ConfigFlags) AddFlags(flags *pflag.FlagSet) {
flags.StringVar(f.KubeConfig, "kubeconfig", *f.KubeConfig, "Path to the kubeconfig file to use for CLI requests.")
}
if f.CacheDir != nil {
flags.StringVar(f.CacheDir, FlagHTTPCacheDir, *f.CacheDir, "Default HTTP cache directory")
flags.StringVar(f.CacheDir, flagHTTPCacheDir, *f.CacheDir, "Default HTTP cache directory")
}
// add config options

View File

@ -50,14 +50,6 @@ import (
"k8s.io/kubernetes/pkg/printers"
)
const (
FlagMatchBinaryVersion = "match-server-version"
)
var (
FlagHTTPCacheDir = "cache-dir"
)
// Factory provides abstractions that allow the Kubectl command to be extended across multiple types
// of resources and different API sets.
// The rings are here for a reason. In order for composers to be able to provide alternative factory implementations
@ -132,9 +124,6 @@ type ClientAccessFactory interface {
// using the factory.
Command(cmd *cobra.Command, showSecrets bool) string
// BindFlags adds any flags that are common to all kubectl sub commands.
BindFlags(flags *pflag.FlagSet)
// SuggestedPodTemplateResources returns a list of resource types that declare a pod template
SuggestedPodTemplateResources() []schema.GroupResource

View File

@ -26,7 +26,6 @@ import (
"path/filepath"
"regexp"
"strings"
"sync"
"k8s.io/api/core/v1"
@ -58,7 +57,6 @@ import (
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
"k8s.io/kubernetes/pkg/kubectl"
"k8s.io/kubernetes/pkg/kubectl/resource"
"k8s.io/kubernetes/pkg/version"
)
type RESTClientGetter interface {
@ -70,10 +68,6 @@ type RESTClientGetter interface {
type ring0Factory struct {
clientGetter RESTClientGetter
requireMatchedServerVersion bool
checkServerVersion sync.Once
matchesServerVersionErr error
}
func NewClientAccessFactory(clientGetter RESTClientGetter) ClientAccessFactory {
@ -88,6 +82,18 @@ func NewClientAccessFactory(clientGetter RESTClientGetter) ClientAccessFactory {
return f
}
func (f *ring0Factory) ClientConfig() (*restclient.Config, error) {
return f.clientGetter.ToRESTConfig()
}
func (f *ring0Factory) RESTMapper() (meta.RESTMapper, error) {
return f.clientGetter.ToRESTMapper()
}
func (f *ring0Factory) BareClientConfig() (*restclient.Config, error) {
return f.clientGetter.ToRESTConfig()
}
func (f *ring0Factory) DiscoveryClient() (discovery.CachedDiscoveryInterface, error) {
return f.clientGetter.ToDiscoveryClient()
}
@ -115,41 +121,6 @@ func (f *ring0Factory) DynamicClient() (dynamic.DynamicInterface, error) {
}
return dynamic.NewForConfig(clientConfig)
}
func (f *ring0Factory) checkMatchingServerVersion() error {
f.checkServerVersion.Do(func() {
if !f.requireMatchedServerVersion {
return
}
discoveryClient, err := f.DiscoveryClient()
if err != nil {
f.matchesServerVersionErr = err
return
}
f.matchesServerVersionErr = discovery.MatchesServerVersion(version.Get(), discoveryClient)
})
return f.matchesServerVersionErr
}
func (f *ring0Factory) ClientConfig() (*restclient.Config, error) {
if err := f.checkMatchingServerVersion(); err != nil {
return nil, err
}
clientConfig, err := f.clientGetter.ToRESTConfig()
if err != nil {
return nil, err
}
setKubernetesDefaults(clientConfig)
return clientConfig, nil
}
func (f *ring0Factory) RESTMapper() (meta.RESTMapper, error) {
return f.clientGetter.ToRESTMapper()
}
func (f *ring0Factory) BareClientConfig() (*restclient.Config, error) {
return f.clientGetter.ToRESTConfig()
}
// NewBuilder returns a new resource builder for structured api objects.
func (f *ring0Factory) NewBuilder() *resource.Builder {
@ -161,6 +132,7 @@ func (f *ring0Factory) RESTClient() (*restclient.RESTClient, error) {
if err != nil {
return nil, err
}
setKubernetesDefaults(clientConfig)
return restclient.RESTClientFor(clientConfig)
}
@ -330,14 +302,6 @@ func (f *ring0Factory) Command(cmd *cobra.Command, showSecrets bool) string {
return base + args + flags
}
func (f *ring0Factory) BindFlags(flags *pflag.FlagSet) {
// Globally persistent flags across all subcommands.
// TODO Change flag names to consts to allow safer lookup from subcommands.
// TODO Add a verbose flag that turns on glog logging. Probably need a way
// to do that automatically for every subcommand.
flags.BoolVar(&f.requireMatchedServerVersion, FlagMatchBinaryVersion, false, "Require server version to match client version")
}
func (f *ring0Factory) SuggestedPodTemplateResources() []schema.GroupResource {
return []schema.GroupResource{
{Resource: "replicationcontroller"},
@ -640,19 +604,3 @@ func InternalVersionJSONEncoder() runtime.Encoder {
encoder := legacyscheme.Codecs.LegacyCodec(legacyscheme.Scheme.PrioritizedVersionsAllGroups()...)
return unstructured.JSONFallbackEncoder{Encoder: encoder}
}
// setKubernetesDefaults sets default values on the provided client config for accessing the
// Kubernetes API or returns an error if any of the defaults are impossible or invalid.
// TODO this isn't what we want. Each clientset should be setting defaults as it sees fit.
func setKubernetesDefaults(config *restclient.Config) error {
// TODO remove this hack. This is allowing the GetOptions to be serialized.
config.GroupVersion = &schema.GroupVersion{Group: "", Version: "v1"}
if config.APIPath == "" {
config.APIPath = "/api"
}
if config.NegotiatedSerializer == nil {
config.NegotiatedSerializer = legacyscheme.Codecs
}
return restclient.SetKubernetesDefaults(config)
}

View File

@ -0,0 +1,125 @@
/*
Copyright 2018 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package util
import (
"sync"
"github.com/spf13/pflag"
"k8s.io/apimachinery/pkg/api/meta"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/client-go/discovery"
"k8s.io/client-go/rest"
"k8s.io/client-go/tools/clientcmd"
"k8s.io/kubernetes/pkg/api/legacyscheme"
"k8s.io/kubernetes/pkg/version"
)
const (
flagMatchBinaryVersion = "match-server-version"
)
// MatchVersionFlags is for setting the "match server version" function.
type MatchVersionFlags struct {
Delegate RESTClientGetter
RequireMatchedServerVersion bool
checkServerVersion sync.Once
matchesServerVersionErr error
}
var _ RESTClientGetter = &MatchVersionFlags{}
func (f *MatchVersionFlags) checkMatchingServerVersion() error {
f.checkServerVersion.Do(func() {
if !f.RequireMatchedServerVersion {
return
}
discoveryClient, err := f.Delegate.ToDiscoveryClient()
if err != nil {
f.matchesServerVersionErr = err
return
}
f.matchesServerVersionErr = discovery.MatchesServerVersion(version.Get(), discoveryClient)
})
return f.matchesServerVersionErr
}
// ToRESTConfig implements RESTClientGetter.
// Returns a REST client configuration based on a provided path
// to a .kubeconfig file, loading rules, and config flag overrides.
// Expects the AddFlags method to have been called.
func (f *MatchVersionFlags) ToRESTConfig() (*rest.Config, error) {
if err := f.checkMatchingServerVersion(); err != nil {
return nil, err
}
clientConfig, err := f.Delegate.ToRESTConfig()
if err != nil {
return nil, err
}
// TODO we should not have to do this. It smacks of something going wrong.
setKubernetesDefaults(clientConfig)
return clientConfig, nil
}
func (f *MatchVersionFlags) ToRawKubeConfigLoader() clientcmd.ClientConfig {
return f.Delegate.ToRawKubeConfigLoader()
}
func (f *MatchVersionFlags) ToDiscoveryClient() (discovery.CachedDiscoveryInterface, error) {
if err := f.checkMatchingServerVersion(); err != nil {
return nil, err
}
return f.Delegate.ToDiscoveryClient()
}
// RESTMapper returns a mapper.
func (f *MatchVersionFlags) ToRESTMapper() (meta.RESTMapper, error) {
if err := f.checkMatchingServerVersion(); err != nil {
return nil, err
}
return f.Delegate.ToRESTMapper()
}
func (f *MatchVersionFlags) AddFlags(flags *pflag.FlagSet) {
flags.BoolVar(&f.RequireMatchedServerVersion, flagMatchBinaryVersion, f.RequireMatchedServerVersion, "Require server version to match client version")
}
func NewMatchVersionFlags(delegate RESTClientGetter) *MatchVersionFlags {
return &MatchVersionFlags{
Delegate: delegate,
}
}
// setKubernetesDefaults sets default values on the provided client config for accessing the
// Kubernetes API or returns an error if any of the defaults are impossible or invalid.
// TODO this isn't what we want. Each clientset should be setting defaults as it sees fit.
func setKubernetesDefaults(config *rest.Config) error {
// TODO remove this hack. This is allowing the GetOptions to be serialized.
config.GroupVersion = &schema.GroupVersion{Group: "", Version: "v1"}
if config.APIPath == "" {
config.APIPath = "/api"
}
if config.NegotiatedSerializer == nil {
config.NegotiatedSerializer = legacyscheme.Codecs
}
return rest.SetKubernetesDefaults(config)
}