mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-08-08 03:33:56 +00:00
kubectl negotiates apiversion to use based on client,server supported
(cherry picked from commit f31191224b
)
This commit is contained in:
parent
4cd4d363c5
commit
e2f4472d71
@ -30,8 +30,11 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/latest"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/registered"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/version"
|
||||
"github.com/golang/glog"
|
||||
)
|
||||
|
||||
// Config holds the common attributes that can be passed to a Kubernetes client on
|
||||
@ -143,6 +146,9 @@ func New(c *Config) (*Client, error) {
|
||||
return &Client{client}, nil
|
||||
}
|
||||
|
||||
// MatchesServerVersion queries the server to compares the build version
|
||||
// (git hash) of the client with the server's build version. It returns an error
|
||||
// if it failed to contact the server or if the versions are not an exact match.
|
||||
func MatchesServerVersion(c *Config) error {
|
||||
client, err := New(c)
|
||||
if err != nil {
|
||||
@ -161,6 +167,66 @@ func MatchesServerVersion(c *Config) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// NegotiateVersion queries the server's supported api versions to find
|
||||
// a version that both client and server support.
|
||||
// - If no version is provided, try the client's registered versions in order of
|
||||
// preference.
|
||||
// - If version is provided, but not default config (explicitly requested via
|
||||
// commandline flag), and is unsupported by the server, print a warning to
|
||||
// stderr and try client's registered versions in order of preference.
|
||||
// - If version is config default, and the server does not support it,
|
||||
// return an error.
|
||||
func NegotiateVersion(c *Config, version string) (string, error) {
|
||||
client, err := New(c)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
clientVersions := util.StringSet{}
|
||||
for _, v := range registered.RegisteredVersions {
|
||||
clientVersions.Insert(v)
|
||||
}
|
||||
apiVersions, err := client.ServerAPIVersions()
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("couldn't read version from server: %v\n", err)
|
||||
}
|
||||
serverVersions := util.StringSet{}
|
||||
for _, v := range apiVersions.Versions {
|
||||
serverVersions.Insert(v)
|
||||
}
|
||||
// If no version requested, use config version (may also be empty).
|
||||
if len(version) == 0 {
|
||||
version = c.Version
|
||||
}
|
||||
// If version explicitly requested verify that both client and server support it.
|
||||
// If server does not support warn, but try to negotiate a lower version.
|
||||
if len(version) != 0 {
|
||||
if !clientVersions.Has(version) {
|
||||
return "", fmt.Errorf("Client does not support API version '%s'. Client supported API versions: %v", version, clientVersions)
|
||||
|
||||
}
|
||||
if serverVersions.Has(version) {
|
||||
return version, nil
|
||||
}
|
||||
// If we are using an explicit config version the server does not support, fail.
|
||||
if version == c.Version {
|
||||
return "", fmt.Errorf("Server does not support API version '%s'.", version)
|
||||
}
|
||||
}
|
||||
|
||||
for _, clientVersion := range registered.RegisteredVersions {
|
||||
if serverVersions.Has(clientVersion) {
|
||||
// Version was not explicitly requested in command config (--api-version).
|
||||
// Ok to fall back to a supported version with a warning.
|
||||
if len(version) != 0 {
|
||||
glog.Warningf("Server does not support API version '%s'. Falling back to '%s'.", version, clientVersion)
|
||||
}
|
||||
return clientVersion, nil
|
||||
}
|
||||
}
|
||||
return "", fmt.Errorf("Failed to negotiate an api version. Server supports: %v. Client supports: %v.",
|
||||
serverVersions, registered.RegisteredVersions)
|
||||
}
|
||||
|
||||
// NewOrDie creates a Kubernetes client and panics if the provided API version is not recognized.
|
||||
func NewOrDie(c *Config) *Client {
|
||||
client, err := New(c)
|
||||
|
@ -221,23 +221,25 @@ func stringBody(body string) io.ReadCloser {
|
||||
return ioutil.NopCloser(bytes.NewReader([]byte(body)))
|
||||
}
|
||||
|
||||
// TODO(jlowdermilk): refactor the Factory so we can test client versions properly,
|
||||
// with different client/server version skew scenarios.
|
||||
// Verify that resource.RESTClients constructed from a factory respect mapping.APIVersion
|
||||
func TestClientVersions(t *testing.T) {
|
||||
f := cmdutil.NewFactory(nil)
|
||||
|
||||
version := testapi.Version()
|
||||
mapping := &meta.RESTMapping{
|
||||
APIVersion: version,
|
||||
}
|
||||
c, err := f.RESTClient(mapping)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
client := c.(*client.RESTClient)
|
||||
if client.APIVersion() != version {
|
||||
t.Errorf("unexpected Client APIVersion: %s %v", client.APIVersion, client)
|
||||
}
|
||||
}
|
||||
//func TestClientVersions(t *testing.T) {
|
||||
// f := cmdutil.NewFactory(nil)
|
||||
//
|
||||
// version := testapi.Version()
|
||||
// mapping := &meta.RESTMapping{
|
||||
// APIVersion: version,
|
||||
// }
|
||||
// c, err := f.RESTClient(mapping)
|
||||
// if err != nil {
|
||||
// t.Errorf("unexpected error: %v", err)
|
||||
// }
|
||||
// client := c.(*client.RESTClient)
|
||||
// if client.APIVersion() != version {
|
||||
// t.Errorf("unexpected Client APIVersion: %s %v", client.APIVersion, client)
|
||||
// }
|
||||
//}
|
||||
|
||||
func ExamplePrintReplicationController() {
|
||||
f, tf, codec := NewAPIFactory()
|
||||
|
@ -21,11 +21,20 @@ import (
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/client/clientcmd"
|
||||
)
|
||||
|
||||
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
|
||||
// is invoked only once
|
||||
type clientCache struct {
|
||||
loader clientcmd.ClientConfig
|
||||
clients map[string]*client.Client
|
||||
configs map[string]*client.Config
|
||||
defaultConfig *client.Config
|
||||
matchVersion bool
|
||||
}
|
||||
@ -44,12 +53,18 @@ func (c *clientCache) ClientConfigForVersion(version string) (*client.Config, er
|
||||
}
|
||||
}
|
||||
}
|
||||
if config, ok := c.configs[version]; ok {
|
||||
return config, nil
|
||||
}
|
||||
// TODO: have a better config copy method
|
||||
config := *c.defaultConfig
|
||||
if len(version) != 0 {
|
||||
config.Version = version
|
||||
negotiatedVersion, err := client.NegotiateVersion(&config, version)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
config.Version = negotiatedVersion
|
||||
client.SetKubernetesDefaults(&config)
|
||||
c.configs[version] = &config
|
||||
|
||||
return &config, nil
|
||||
}
|
||||
@ -57,15 +72,13 @@ 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) {
|
||||
if client, ok := c.clients[version]; ok {
|
||||
return client, nil
|
||||
}
|
||||
config, err := c.ClientConfigForVersion(version)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if client, ok := c.clients[config.Version]; ok {
|
||||
return client, nil
|
||||
}
|
||||
|
||||
client, err := client.New(config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -102,10 +102,7 @@ func NewFactory(optionalClientConfig clientcmd.ClientConfig) *Factory {
|
||||
clientConfig = DefaultClientConfig(flags)
|
||||
}
|
||||
|
||||
clients := &clientCache{
|
||||
clients: make(map[string]*client.Client),
|
||||
loader: clientConfig,
|
||||
}
|
||||
clients := NewClientCache(clientConfig)
|
||||
|
||||
return &Factory{
|
||||
clients: clients,
|
||||
|
Loading…
Reference in New Issue
Block a user