// ClientConfigForVersion returns the correct config for a server func (c *ClientCache) ClientConfigForVersion(version *unversioned.GroupVersion) (*restclient.Config, error) { if c.defaultConfig == nil { config, err := c.loader.ClientConfig() if err != nil { return nil, err } c.defaultConfig = config if c.matchVersion { if err := discovery.MatchesServerVersion(c.discoveryClient, config); err != nil { return nil, err } } } if version != nil { if config, ok := c.configs[*version]; ok { return config, nil } } // TODO: have a better config copy method config := *c.defaultConfig // TODO these fall out when we finish the refactor var preferredGV *unversioned.GroupVersion if version != nil { versionCopy := *version preferredGV = &versionCopy } oldclient.SetKubernetesDefaults(&config) negotiatedVersion, err := discovery.NegotiateVersion(c.discoveryClient, &config, preferredGV, registered.EnabledVersions()) if err != nil { return nil, err } config.GroupVersion = negotiatedVersion if version != nil { c.configs[*version] = &config } // `version` does not necessarily equal `config.Version`. However, we know that we call this method again with // `config.Version`, we should get the config we've just built. configCopy := config c.configs[*config.GroupVersion] = &configCopy return &config, nil }
// ClientConfigForVersion returns the correct config for a server func (c *ClientCache) ClientConfigForVersion(requiredVersion *schema.GroupVersion) (*restclient.Config, error) { // TODO: have a better config copy method config, discoveryClient, err := c.getDefaultConfig() if err != nil { return nil, err } if requiredVersion == nil && config.GroupVersion != nil { // if someone has set the values via flags, our config will have the groupVersion set // that means it is required. requiredVersion = config.GroupVersion } // required version may still be nil, since config.GroupVersion may have been nil. Do the check // before looking up from the cache if requiredVersion != nil { if config, ok := c.configs[*requiredVersion]; ok { return config, nil } } negotiatedVersion, err := discovery.NegotiateVersion(discoveryClient, requiredVersion, registered.EnabledVersions()) if err != nil { return nil, err } config.GroupVersion = negotiatedVersion // TODO this isn't what we want. Each clientset should be setting defaults as it sees fit. oldclient.SetKubernetesDefaults(&config) if requiredVersion != nil { c.configs[*requiredVersion] = &config } // `version` does not necessarily equal `config.Version`. However, we know that we call this method again with // `config.Version`, we should get the config we've just built. configCopy := config c.configs[*config.GroupVersion] = &configCopy return &config, nil }
func TestNegotiateVersion(t *testing.T) { tests := []struct { name string requiredVersion *schema.GroupVersion expectedVersion *schema.GroupVersion serverVersions []string clientVersions []schema.GroupVersion expectErr func(err error) bool sendErr error statusCode int }{ { name: "server supports client default", serverVersions: []string{"version1", registered.GroupOrDie(api.GroupName).GroupVersion.String()}, clientVersions: []schema.GroupVersion{{Version: "version1"}, registered.GroupOrDie(api.GroupName).GroupVersion}, expectedVersion: &schema.GroupVersion{Version: "version1"}, statusCode: http.StatusOK, }, { name: "server falls back to client supported", serverVersions: []string{"version1"}, clientVersions: []schema.GroupVersion{{Version: "version1"}, registered.GroupOrDie(api.GroupName).GroupVersion}, expectedVersion: &schema.GroupVersion{Version: "version1"}, statusCode: http.StatusOK, }, { name: "explicit version supported", requiredVersion: &schema.GroupVersion{Version: "v1"}, serverVersions: []string{"/version1", registered.GroupOrDie(api.GroupName).GroupVersion.String()}, clientVersions: []schema.GroupVersion{{Version: "version1"}, registered.GroupOrDie(api.GroupName).GroupVersion}, expectedVersion: &schema.GroupVersion{Version: "v1"}, statusCode: http.StatusOK, }, { name: "explicit version not supported on server", requiredVersion: &schema.GroupVersion{Version: "v1"}, serverVersions: []string{"version1"}, clientVersions: []schema.GroupVersion{{Version: "version1"}, registered.GroupOrDie(api.GroupName).GroupVersion}, expectErr: func(err error) bool { return strings.Contains(err.Error(), `server does not support API version "v1"`) }, statusCode: http.StatusOK, }, { name: "explicit version not supported on client", requiredVersion: &schema.GroupVersion{Version: "v1"}, serverVersions: []string{"v1"}, clientVersions: []schema.GroupVersion{{Version: "version1"}}, expectErr: func(err error) bool { return strings.Contains(err.Error(), `client does not support API version "v1"`) }, statusCode: http.StatusOK, }, { name: "connection refused error", serverVersions: []string{"version1"}, clientVersions: []schema.GroupVersion{{Version: "version1"}, registered.GroupOrDie(api.GroupName).GroupVersion}, sendErr: errors.New("connection refused"), expectErr: func(err error) bool { return strings.Contains(err.Error(), "connection refused") }, statusCode: http.StatusOK, }, { name: "discovery fails due to 403 Forbidden errors and thus serverVersions is empty, use default GroupVersion", clientVersions: []schema.GroupVersion{{Version: "version1"}, registered.GroupOrDie(api.GroupName).GroupVersion}, expectedVersion: &schema.GroupVersion{Version: "version1"}, statusCode: http.StatusForbidden, }, { name: "discovery fails due to 404 Not Found errors and thus serverVersions is empty, use requested GroupVersion", requiredVersion: &schema.GroupVersion{Version: "version1"}, clientVersions: []schema.GroupVersion{{Version: "version1"}, registered.GroupOrDie(api.GroupName).GroupVersion}, expectedVersion: &schema.GroupVersion{Version: "version1"}, statusCode: http.StatusNotFound, }, { name: "discovery fails due to 403 Forbidden errors and thus serverVersions is empty, no fallback GroupVersion", expectErr: func(err error) bool { return strings.Contains(err.Error(), "failed to negotiate an api version;") }, statusCode: http.StatusForbidden, }, } for _, test := range tests { fakeClient := &fake.RESTClient{ NegotiatedSerializer: testapi.Default.NegotiatedSerializer(), Resp: &http.Response{ StatusCode: test.statusCode, Body: objBody(&uapi.APIVersions{Versions: test.serverVersions}), }, Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { if test.sendErr != nil { return nil, test.sendErr } header := http.Header{} header.Set("Content-Type", runtime.ContentTypeJSON) return &http.Response{StatusCode: test.statusCode, Header: header, Body: objBody(&uapi.APIVersions{Versions: test.serverVersions})}, nil }), } c := discovery.NewDiscoveryClientForConfigOrDie(&restclient.Config{}) c.RESTClient().(*restclient.RESTClient).Client = fakeClient.Client response, err := discovery.NegotiateVersion(c, test.requiredVersion, test.clientVersions) if err == nil && test.expectErr != nil { t.Errorf("expected error, got nil for [%s].", test.name) } if err != nil { if test.expectErr == nil || !test.expectErr(err) { t.Errorf("unexpected error for [%s]: %v.", test.name, err) } continue } if *response != *test.expectedVersion { t.Errorf("%s: expected version %s, got %s.", test.name, test.expectedVersion, response) } } }