// ClientConfigForVersion returns the correct config for a server func (c *ClientCache) ClientConfigForVersion(version string) (*client.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 := client.MatchesServerVersion(c.defaultClient, config); err != nil { return nil, err } } } if config, ok := c.configs[version]; ok { return config, nil } // TODO: have a better config copy method config := *c.defaultConfig negotiatedVersion, err := client.NegotiateVersion(c.defaultClient, &config, version, registered.RegisteredVersions) if err != nil { return nil, err } config.Version = negotiatedVersion client.SetKubernetesDefaults(&config) 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 the config we've just built. config = config c.configs[config.Version] = &config return &config, nil }
// ClientConfigForVersion returns the correct config for a server func (c *ClientCache) ClientConfigForVersion(version string) (*client.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 := client.MatchesServerVersion(c.defaultClient, config); err != nil { return nil, err } } } if config, ok := c.configs[version]; ok { return config, nil } // TODO: have a better config copy method config := *c.defaultConfig negotiatedVersion, err := client.NegotiateVersion(c.defaultClient, &config, version, registered.RegisteredVersions) if err != nil { return nil, err } config.Version = negotiatedVersion client.SetKubernetesDefaults(&config) c.configs[version] = &config return &config, nil }
// ClientConfigForVersion returns the correct config for a server func (c *ClientCache) ClientConfigForVersion(version string) (*client.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 := client.MatchesServerVersion(c.defaultClient, config); err != nil { return nil, err } } } 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 len(version) > 0 { gv, err := unversioned.ParseGroupVersion(version) if err != nil { return nil, err } preferredGV = &gv } registeredGVs := []unversioned.GroupVersion{} for _, gvString := range registered.RegisteredVersions { gv, err := unversioned.ParseGroupVersion(gvString) if err != nil { return nil, err } registeredGVs = append(registeredGVs, gv) } negotiatedVersion, err := client.NegotiateVersion(c.defaultClient, &config, preferredGV, registeredGVs) if err != nil { return nil, err } config.GroupVersion = negotiatedVersion client.SetKubernetesDefaults(&config) 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 the config we've just built. configCopy := config c.configs[config.GroupVersion.String()] = &configCopy return &config, nil }
// ClientConfigForVersion returns the correct config for a server func (c *ClientCache) ClientConfigForVersion(version *unversioned.GroupVersion) (*client.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 := client.MatchesServerVersion(c.defaultClient, 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 } negotiatedVersion, err := client.NegotiateVersion(c.defaultClient, &config, preferredGV, registered.RegisteredGroupVersions) if err != nil { return nil, err } config.GroupVersion = negotiatedVersion client.SetKubernetesDefaults(&config) 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 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(version string) (*kclient.Config, error) { if c.defaultConfig == nil { config, err := c.loader.ClientConfig() if err != nil { return nil, err } c.defaultConfig = config } // TODO: have a better config copy method if config, ok := c.configs[version]; ok { return config, nil } if c.negotiatingClient == nil { // TODO: We want to reuse the upstream negotiation logic, which is coupled // to a concrete kube Client. The negotiation will ultimately try and // build an unversioned URL using the config prefix to ask for supported // server versions. If we use the default kube client config, the prefix // will be /api, while we need to use the OpenShift prefix to ask for the // OpenShift server versions. For now, set OpenShift defaults on the // config to ensure the right prefix gets used. The client cache and // negotiation logic should be refactored upstream to support downstream // reuse so that we don't need to do any of this cache or negotiation // duplication. negotiatingConfig := *c.defaultConfig client.SetOpenShiftDefaults(&negotiatingConfig) negotiatingClient, err := kclient.New(&negotiatingConfig) if err != nil { return nil, err } c.negotiatingClient = negotiatingClient } config := *c.defaultConfig negotiatedVersion, err := kclient.NegotiateVersion(c.negotiatingClient, &config, version, latest.Versions) if err != nil { return nil, err } config.Version = negotiatedVersion client.SetOpenShiftDefaults(&config) 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 the config we've just built. configCopy := config c.configs[config.Version] = &configCopy return &config, nil }
func TestNegotiateVersion(t *testing.T) { tests := []struct { name string version *uapi.GroupVersion expectedVersion *uapi.GroupVersion serverVersions []string clientVersions []uapi.GroupVersion config *restclient.Config expectErr func(err error) bool sendErr error statusCode int }{ { name: "server supports client default", version: &uapi.GroupVersion{Version: "version1"}, config: &restclient.Config{}, serverVersions: []string{"version1", registered.GroupOrDie(api.GroupName).GroupVersion.String()}, clientVersions: []uapi.GroupVersion{{Version: "version1"}, registered.GroupOrDie(api.GroupName).GroupVersion}, expectedVersion: &uapi.GroupVersion{Version: "version1"}, statusCode: http.StatusOK, }, { name: "server falls back to client supported", version: ®istered.GroupOrDie(api.GroupName).GroupVersion, config: &restclient.Config{}, serverVersions: []string{"version1"}, clientVersions: []uapi.GroupVersion{{Version: "version1"}, registered.GroupOrDie(api.GroupName).GroupVersion}, expectedVersion: &uapi.GroupVersion{Version: "version1"}, statusCode: http.StatusOK, }, { name: "explicit version supported", config: &restclient.Config{ContentConfig: restclient.ContentConfig{GroupVersion: ®istered.GroupOrDie(api.GroupName).GroupVersion}}, serverVersions: []string{"/version1", registered.GroupOrDie(api.GroupName).GroupVersion.String()}, clientVersions: []uapi.GroupVersion{{Version: "version1"}, registered.GroupOrDie(api.GroupName).GroupVersion}, expectedVersion: ®istered.GroupOrDie(api.GroupName).GroupVersion, statusCode: http.StatusOK, }, { name: "explicit version not supported", config: &restclient.Config{ContentConfig: restclient.ContentConfig{GroupVersion: ®istered.GroupOrDie(api.GroupName).GroupVersion}}, serverVersions: []string{"version1"}, clientVersions: []uapi.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: "connection refused error", config: &restclient.Config{ContentConfig: restclient.ContentConfig{GroupVersion: ®istered.GroupOrDie(api.GroupName).GroupVersion}}, serverVersions: []string{"version1"}, clientVersions: []uapi.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", config: &restclient.Config{ContentConfig: restclient.ContentConfig{GroupVersion: ®istered.GroupOrDie(api.GroupName).GroupVersion}}, clientVersions: []uapi.GroupVersion{{Version: "version1"}, registered.GroupOrDie(api.GroupName).GroupVersion}, expectedVersion: ®istered.GroupOrDie(api.GroupName).GroupVersion, statusCode: http.StatusForbidden, }, { name: "discovery fails due to 404 Not Found errors and thus serverVersions is empty, use requested GroupVersion", version: &uapi.GroupVersion{Version: "version1"}, config: &restclient.Config{ContentConfig: restclient.ContentConfig{GroupVersion: ®istered.GroupOrDie(api.GroupName).GroupVersion}}, clientVersions: []uapi.GroupVersion{{Version: "version1"}, registered.GroupOrDie(api.GroupName).GroupVersion}, expectedVersion: &uapi.GroupVersion{Version: "version1"}, statusCode: http.StatusNotFound, }, { name: "discovery fails due to 403 Forbidden errors and thus serverVersions is empty, no fallback GroupVersion", config: &restclient.Config{}, clientVersions: []uapi.GroupVersion{{Version: "version1"}, registered.GroupOrDie(api.GroupName).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 := unversioned.NewOrDie(test.config) c.DiscoveryClient.Client = fakeClient.Client response, err := unversioned.NegotiateVersion(c, test.config, test.version, 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) } } }
func TestNegotiateVersion(t *testing.T) { tests := []struct { name string version *uapi.GroupVersion expectedVersion *uapi.GroupVersion serverVersions []string clientVersions []uapi.GroupVersion config *unversioned.Config expectErr func(err error) bool sendErr error }{ { name: "server supports client default", version: &uapi.GroupVersion{Version: "version1"}, config: &unversioned.Config{}, serverVersions: []string{"/version1", testapi.Default.Version()}, clientVersions: []uapi.GroupVersion{{Version: "version1"}, {Version: testapi.Default.Version()}}, expectedVersion: &uapi.GroupVersion{Version: "version1"}, }, { name: "server falls back to client supported", version: &uapi.GroupVersion{Version: testapi.Default.Version()}, config: &unversioned.Config{}, serverVersions: []string{"/version1"}, clientVersions: []uapi.GroupVersion{{Version: "version1"}, {Version: testapi.Default.Version()}}, expectedVersion: &uapi.GroupVersion{Version: "version1"}, }, { name: "explicit version supported", config: &unversioned.Config{GroupVersion: testapi.Default.GroupVersion()}, serverVersions: []string{"/version1", testapi.Default.Version()}, clientVersions: []uapi.GroupVersion{{Version: "version1"}, {Version: testapi.Default.Version()}}, expectedVersion: &uapi.GroupVersion{Version: testapi.Default.Version()}, }, { name: "explicit version not supported", config: &unversioned.Config{GroupVersion: testapi.Default.GroupVersion()}, serverVersions: []string{"/version1"}, clientVersions: []uapi.GroupVersion{{Version: "version1"}, {Version: testapi.Default.Version()}}, expectErr: func(err error) bool { return strings.Contains(err.Error(), `server does not support API version "v1"`) }, }, { name: "connection refused error", config: &unversioned.Config{GroupVersion: testapi.Default.GroupVersion()}, serverVersions: []string{"/version1"}, clientVersions: []uapi.GroupVersion{{Version: "version1"}, {Version: testapi.Default.Version()}}, sendErr: errors.New("connection refused"), expectErr: func(err error) bool { return strings.Contains(err.Error(), "connection refused") }, }, } codec := testapi.Default.Codec() for _, test := range tests { fakeClient := &fake.RESTClient{ Codec: codec, Resp: &http.Response{ StatusCode: 200, Body: objBody(&unversionedapi.APIVersions{Versions: test.serverVersions}), }, Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { if test.sendErr != nil { return nil, test.sendErr } return &http.Response{StatusCode: 200, Body: objBody(&unversionedapi.APIVersions{Versions: test.serverVersions})}, nil }), } c := unversioned.NewOrDie(test.config) c.Client = fakeClient.Client response, err := unversioned.NegotiateVersion(c, test.config, test.version, 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) } } }
func TestNegotiateVersion(t *testing.T) { tests := []struct { name, version, expectedVersion string serverVersions []string clientVersions []string config *unversioned.Config expectErr bool }{ { name: "server supports client default", version: "version1", config: &unversioned.Config{}, serverVersions: []string{"version1", testapi.Default.Version()}, clientVersions: []string{"version1", testapi.Default.Version()}, expectedVersion: "version1", expectErr: false, }, { name: "server falls back to client supported", version: testapi.Default.Version(), config: &unversioned.Config{}, serverVersions: []string{"version1"}, clientVersions: []string{"version1", testapi.Default.Version()}, expectedVersion: "version1", expectErr: false, }, { name: "explicit version supported", version: "", config: &unversioned.Config{Version: testapi.Default.Version()}, serverVersions: []string{"version1", testapi.Default.Version()}, clientVersions: []string{"version1", testapi.Default.Version()}, expectedVersion: testapi.Default.Version(), expectErr: false, }, { name: "explicit version not supported", version: "", config: &unversioned.Config{Version: testapi.Default.Version()}, serverVersions: []string{"version1"}, clientVersions: []string{"version1", testapi.Default.Version()}, expectedVersion: "", expectErr: true, }, } codec := testapi.Default.Codec() for _, test := range tests { fakeClient := &fake.RESTClient{ Codec: codec, Resp: &http.Response{ StatusCode: 200, Body: objBody(&api.APIVersions{Versions: test.serverVersions}), }, Client: fake.HTTPClientFunc(func(req *http.Request) (*http.Response, error) { return &http.Response{StatusCode: 200, Body: objBody(&api.APIVersions{Versions: test.serverVersions})}, nil }), } c := unversioned.NewOrDie(test.config) c.Client = fakeClient.Client response, err := unversioned.NegotiateVersion(c, test.config, test.version, test.clientVersions) if err == nil && test.expectErr { t.Errorf("expected error, got nil for [%s].", test.name) } if err != nil && !test.expectErr { t.Errorf("unexpected error for [%s]: %v.", test.name, err) } if response != test.expectedVersion { t.Errorf("expected version %s, got %s.", test.expectedVersion, response) } } }