2015-04-07 18:21:25 +00:00
|
|
|
/*
|
2016-06-03 00:25:58 +00:00
|
|
|
Copyright 2014 The Kubernetes Authors.
|
2015-04-07 18:21:25 +00:00
|
|
|
|
|
|
|
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 (
|
2016-10-28 17:01:32 +00:00
|
|
|
"sync"
|
|
|
|
|
2017-01-11 14:09:48 +00:00
|
|
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
2017-01-25 19:00:30 +00:00
|
|
|
"k8s.io/client-go/discovery"
|
2017-08-13 17:53:55 +00:00
|
|
|
"k8s.io/client-go/kubernetes"
|
2017-01-19 18:27:59 +00:00
|
|
|
restclient "k8s.io/client-go/rest"
|
2017-01-20 18:06:17 +00:00
|
|
|
"k8s.io/client-go/tools/clientcmd"
|
2017-06-08 06:02:38 +00:00
|
|
|
fedclientset "k8s.io/kubernetes/federation/client/clientset_generated/federation_clientset"
|
2016-09-08 14:24:02 +00:00
|
|
|
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
|
2016-10-24 14:58:05 +00:00
|
|
|
oldclient "k8s.io/kubernetes/pkg/client/unversioned"
|
2017-01-25 20:07:10 +00:00
|
|
|
"k8s.io/kubernetes/pkg/version"
|
2015-04-07 18:21:25 +00:00
|
|
|
)
|
|
|
|
|
2016-12-15 18:10:33 +00:00
|
|
|
func NewClientCache(loader clientcmd.ClientConfig, discoveryClientFactory DiscoveryClientFactory) *ClientCache {
|
2015-08-10 20:05:57 +00:00
|
|
|
return &ClientCache{
|
2017-02-16 11:46:15 +00:00
|
|
|
clientsets: make(map[schema.GroupVersion]internalclientset.Interface),
|
2016-12-15 18:10:33 +00:00
|
|
|
configs: make(map[schema.GroupVersion]*restclient.Config),
|
|
|
|
fedClientSets: make(map[schema.GroupVersion]fedclientset.Interface),
|
|
|
|
loader: loader,
|
|
|
|
discoveryClientFactory: discoveryClientFactory,
|
2015-06-13 02:06:18 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-08-10 20:05:57 +00:00
|
|
|
// ClientCache caches previously loaded clients for reuse, and ensures MatchServerVersion
|
2015-04-07 18:21:25 +00:00
|
|
|
// is invoked only once
|
2015-08-10 20:05:57 +00:00
|
|
|
type ClientCache struct {
|
2015-04-07 18:21:25 +00:00
|
|
|
loader clientcmd.ClientConfig
|
2017-02-16 11:46:15 +00:00
|
|
|
clientsets map[schema.GroupVersion]internalclientset.Interface
|
2016-11-30 07:27:27 +00:00
|
|
|
fedClientSets map[schema.GroupVersion]fedclientset.Interface
|
2016-11-21 02:55:31 +00:00
|
|
|
configs map[schema.GroupVersion]*restclient.Config
|
2016-10-28 17:01:32 +00:00
|
|
|
|
2017-02-13 15:03:04 +00:00
|
|
|
// noVersionConfig provides a cached config for the case of no required version specified
|
|
|
|
noVersionConfig *restclient.Config
|
|
|
|
|
2016-10-28 17:01:32 +00:00
|
|
|
matchVersion bool
|
|
|
|
|
2017-04-18 22:31:52 +00:00
|
|
|
lock sync.Mutex
|
|
|
|
defaultConfig *restclient.Config
|
2016-12-15 18:10:33 +00:00
|
|
|
// discoveryClientFactory comes as a factory method so that we can defer resolution until after
|
|
|
|
// argument evaluation
|
|
|
|
discoveryClientFactory DiscoveryClientFactory
|
|
|
|
discoveryClient discovery.DiscoveryInterface
|
2017-08-13 17:53:55 +00:00
|
|
|
|
2017-08-16 21:22:32 +00:00
|
|
|
kubernetesClientCache KubernetesClientCache
|
2017-08-13 17:53:55 +00:00
|
|
|
}
|
|
|
|
|
2017-08-16 21:22:32 +00:00
|
|
|
// KubernetesClientCache creates a new kubernetes.Clientset one time
|
|
|
|
// and then returns the result for all future requests
|
2017-08-13 17:53:55 +00:00
|
|
|
type KubernetesClientCache struct {
|
|
|
|
// once makes sure the client is only initialized once
|
|
|
|
once sync.Once
|
|
|
|
// client is the cached client value
|
|
|
|
client *kubernetes.Clientset
|
|
|
|
// err is the cached error value
|
|
|
|
err error
|
|
|
|
}
|
|
|
|
|
2017-08-16 21:22:32 +00:00
|
|
|
// KubernetesClientSetForVersion returns a new kubernetes.Clientset. It will cache the value
|
2017-08-13 17:53:55 +00:00
|
|
|
// the first time it is called and return the cached value on subsequent calls.
|
2017-08-16 21:22:32 +00:00
|
|
|
// If an error is encountered the first time KubernetesClientSetForVersion is called,
|
2017-08-13 17:53:55 +00:00
|
|
|
// the error will be cached.
|
2017-08-16 21:22:32 +00:00
|
|
|
func (c *ClientCache) KubernetesClientSetForVersion(requiredVersion *schema.GroupVersion) (*kubernetes.Clientset, error) {
|
|
|
|
c.kubernetesClientCache.once.Do(func() {
|
2017-08-13 17:53:55 +00:00
|
|
|
config, err := c.ClientConfigForVersion(requiredVersion)
|
|
|
|
if err != nil {
|
2017-08-16 21:22:32 +00:00
|
|
|
c.kubernetesClientCache.err = err
|
2017-08-13 17:53:55 +00:00
|
|
|
return
|
|
|
|
}
|
2017-08-16 21:22:32 +00:00
|
|
|
c.kubernetesClientCache.client, c.kubernetesClientCache.err = kubernetes.NewForConfig(config)
|
2017-08-13 17:53:55 +00:00
|
|
|
})
|
2017-08-16 21:22:32 +00:00
|
|
|
return c.kubernetesClientCache.client, c.kubernetesClientCache.err
|
2015-04-07 18:21:25 +00:00
|
|
|
}
|
|
|
|
|
2016-10-28 17:01:32 +00:00
|
|
|
// also looks up the discovery client. We can't do this during init because the flags won't have been set
|
2017-04-18 22:31:52 +00:00
|
|
|
// because this is constructed pre-command execution before the command tree is
|
|
|
|
// even set up. Requires the lock to already be acquired
|
2016-10-28 17:01:32 +00:00
|
|
|
func (c *ClientCache) getDefaultConfig() (restclient.Config, discovery.DiscoveryInterface, error) {
|
|
|
|
if c.defaultConfig != nil && c.discoveryClient != nil {
|
|
|
|
return *c.defaultConfig, c.discoveryClient, nil
|
2015-04-07 18:21:25 +00:00
|
|
|
}
|
2016-10-28 17:01:32 +00:00
|
|
|
|
|
|
|
config, err := c.loader.ClientConfig()
|
|
|
|
if err != nil {
|
|
|
|
return restclient.Config{}, nil, err
|
|
|
|
}
|
2016-12-15 18:10:33 +00:00
|
|
|
discoveryClient, err := c.discoveryClientFactory.DiscoveryClient()
|
2016-10-28 17:01:32 +00:00
|
|
|
if err != nil {
|
|
|
|
return restclient.Config{}, nil, err
|
|
|
|
}
|
|
|
|
if c.matchVersion {
|
2017-01-25 20:07:10 +00:00
|
|
|
if err := discovery.MatchesServerVersion(version.Get(), discoveryClient); err != nil {
|
2016-10-28 17:01:32 +00:00
|
|
|
return restclient.Config{}, nil, err
|
2015-12-08 20:14:38 +00:00
|
|
|
}
|
2015-06-13 02:06:18 +00:00
|
|
|
}
|
2015-12-08 20:14:38 +00:00
|
|
|
|
2016-10-28 17:01:32 +00:00
|
|
|
c.defaultConfig = config
|
|
|
|
c.discoveryClient = discoveryClient
|
|
|
|
return *c.defaultConfig, c.discoveryClient, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// ClientConfigForVersion returns the correct config for a server
|
2016-11-21 02:55:31 +00:00
|
|
|
func (c *ClientCache) ClientConfigForVersion(requiredVersion *schema.GroupVersion) (*restclient.Config, error) {
|
2017-04-18 22:31:52 +00:00
|
|
|
c.lock.Lock()
|
|
|
|
defer c.lock.Unlock()
|
|
|
|
|
|
|
|
return c.clientConfigForVersion(requiredVersion)
|
|
|
|
}
|
|
|
|
|
|
|
|
// clientConfigForVersion returns the correct config for a server
|
|
|
|
func (c *ClientCache) clientConfigForVersion(requiredVersion *schema.GroupVersion) (*restclient.Config, error) {
|
2017-05-12 18:39:56 +00:00
|
|
|
// only lookup in the cache if the requiredVersion is set
|
2016-10-28 17:01:32 +00:00
|
|
|
if requiredVersion != nil {
|
|
|
|
if config, ok := c.configs[*requiredVersion]; ok {
|
2017-02-13 15:03:04 +00:00
|
|
|
return copyConfig(config), nil
|
2016-10-28 17:01:32 +00:00
|
|
|
}
|
2017-02-13 15:03:04 +00:00
|
|
|
} else if c.noVersionConfig != nil {
|
|
|
|
return copyConfig(c.noVersionConfig), nil
|
2015-11-13 21:20:54 +00:00
|
|
|
}
|
|
|
|
|
2017-05-12 19:12:04 +00:00
|
|
|
// this returns a shallow copy to work with
|
|
|
|
config, discoveryClient, err := c.getDefaultConfig()
|
2015-06-13 02:06:18 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
2015-04-07 18:21:25 +00:00
|
|
|
}
|
2017-05-12 19:12:04 +00:00
|
|
|
|
|
|
|
if requiredVersion != nil {
|
|
|
|
if err := discovery.ServerSupportsVersion(discoveryClient, *requiredVersion); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
config.GroupVersion = requiredVersion
|
|
|
|
} else {
|
|
|
|
// TODO remove this hack. This is allowing the GetOptions to be serialized.
|
|
|
|
config.GroupVersion = &schema.GroupVersion{Group: "", Version: "v1"}
|
|
|
|
}
|
2015-12-08 20:14:38 +00:00
|
|
|
|
2016-10-28 17:01:32 +00:00
|
|
|
// TODO this isn't what we want. Each clientset should be setting defaults as it sees fit.
|
|
|
|
oldclient.SetKubernetesDefaults(&config)
|
|
|
|
|
|
|
|
if requiredVersion != nil {
|
2017-02-13 15:03:04 +00:00
|
|
|
c.configs[*requiredVersion] = copyConfig(&config)
|
|
|
|
} else {
|
|
|
|
c.noVersionConfig = copyConfig(&config)
|
2015-12-08 20:14:38 +00:00
|
|
|
}
|
2015-04-07 18:21:25 +00:00
|
|
|
|
2015-11-10 14:16:23 +00:00
|
|
|
// `version` does not necessarily equal `config.Version`. However, we know that we call this method again with
|
2016-08-02 16:51:51 +00:00
|
|
|
// `config.Version`, we should get the config we've just built.
|
2017-02-13 15:03:04 +00:00
|
|
|
c.configs[*config.GroupVersion] = copyConfig(&config)
|
|
|
|
|
|
|
|
return copyConfig(&config), nil
|
|
|
|
}
|
2015-11-10 14:16:23 +00:00
|
|
|
|
2017-02-13 15:03:04 +00:00
|
|
|
func copyConfig(in *restclient.Config) *restclient.Config {
|
|
|
|
configCopy := *in
|
|
|
|
copyGroupVersion := *configCopy.GroupVersion
|
|
|
|
configCopy.GroupVersion = ©GroupVersion
|
|
|
|
return &configCopy
|
2015-04-07 18:21:25 +00:00
|
|
|
}
|
|
|
|
|
2016-09-08 14:24:02 +00:00
|
|
|
// ClientSetForVersion initializes or reuses a clientset for the specified version, or returns an
|
|
|
|
// error if that is not possible
|
2017-02-16 11:46:15 +00:00
|
|
|
func (c *ClientCache) ClientSetForVersion(requiredVersion *schema.GroupVersion) (internalclientset.Interface, error) {
|
2017-04-18 22:31:52 +00:00
|
|
|
c.lock.Lock()
|
|
|
|
defer c.lock.Unlock()
|
|
|
|
|
2016-10-28 17:01:32 +00:00
|
|
|
if requiredVersion != nil {
|
|
|
|
if clientset, ok := c.clientsets[*requiredVersion]; ok {
|
2016-09-08 14:24:02 +00:00
|
|
|
return clientset, nil
|
|
|
|
}
|
|
|
|
}
|
2017-04-18 22:31:52 +00:00
|
|
|
config, err := c.clientConfigForVersion(requiredVersion)
|
2016-09-08 14:24:02 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
clientset, err := internalclientset.NewForConfig(config)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
c.clientsets[*config.GroupVersion] = clientset
|
|
|
|
|
|
|
|
// `version` does not necessarily equal `config.Version`. However, we know that if we call this method again with
|
|
|
|
// `version`, we should get a client based on the same config we just found. There's no guarantee that a client
|
|
|
|
// is copiable, so create a new client and save it in the cache.
|
2016-10-28 17:01:32 +00:00
|
|
|
if requiredVersion != nil {
|
2016-09-08 14:24:02 +00:00
|
|
|
configCopy := *config
|
|
|
|
clientset, err := internalclientset.NewForConfig(&configCopy)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2016-10-28 17:01:32 +00:00
|
|
|
c.clientsets[*requiredVersion] = clientset
|
2016-09-08 14:24:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return clientset, nil
|
|
|
|
}
|
|
|
|
|
2016-11-30 07:27:27 +00:00
|
|
|
func (c *ClientCache) FederationClientSetForVersion(version *schema.GroupVersion) (fedclientset.Interface, error) {
|
2017-04-18 22:31:52 +00:00
|
|
|
c.lock.Lock()
|
|
|
|
defer c.lock.Unlock()
|
|
|
|
|
|
|
|
return c.federationClientSetForVersion(version)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *ClientCache) federationClientSetForVersion(version *schema.GroupVersion) (fedclientset.Interface, error) {
|
2016-04-26 14:33:47 +00:00
|
|
|
if version != nil {
|
|
|
|
if clientSet, found := c.fedClientSets[*version]; found {
|
|
|
|
return clientSet, nil
|
|
|
|
}
|
|
|
|
}
|
2017-04-18 22:31:52 +00:00
|
|
|
config, err := c.clientConfigForVersion(version)
|
2016-04-26 14:33:47 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO: support multi versions of client with clientset
|
2016-11-30 07:27:27 +00:00
|
|
|
clientSet, err := fedclientset.NewForConfig(config)
|
2016-04-26 14:33:47 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
c.fedClientSets[*config.GroupVersion] = clientSet
|
|
|
|
|
|
|
|
if version != nil {
|
|
|
|
configCopy := *config
|
2016-11-30 07:27:27 +00:00
|
|
|
clientSet, err := fedclientset.NewForConfig(&configCopy)
|
2016-04-26 14:33:47 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
c.fedClientSets[*version] = clientSet
|
|
|
|
}
|
|
|
|
|
|
|
|
return clientSet, nil
|
|
|
|
}
|
|
|
|
|
2016-11-21 02:55:31 +00:00
|
|
|
func (c *ClientCache) FederationClientForVersion(version *schema.GroupVersion) (*restclient.RESTClient, error) {
|
2017-04-18 22:31:52 +00:00
|
|
|
c.lock.Lock()
|
|
|
|
defer c.lock.Unlock()
|
|
|
|
|
|
|
|
fedClientSet, err := c.federationClientSetForVersion(version)
|
2016-04-26 14:33:47 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2016-10-13 12:56:07 +00:00
|
|
|
return fedClientSet.Federation().RESTClient().(*restclient.RESTClient), nil
|
2016-04-26 14:33:47 +00:00
|
|
|
}
|