From 6a4c477af7649458cc9a9bb931e14b36eb94e934 Mon Sep 17 00:00:00 2001 From: Chao Xu Date: Thu, 1 Oct 2015 14:56:22 -0700 Subject: [PATCH 1/3] add a ServerAPIVersions function that visits both /api and /apis, and doesn't require a high-level client --- pkg/client/unversioned/helper.go | 46 +++++++++++++++++++++ pkg/client/unversioned/helper_test.go | 59 +++++++++++++++++++++++++++ 2 files changed, 105 insertions(+) diff --git a/pkg/client/unversioned/helper.go b/pkg/client/unversioned/helper.go index f93c2417eaf..98877735d22 100644 --- a/pkg/client/unversioned/helper.go +++ b/pkg/client/unversioned/helper.go @@ -17,6 +17,7 @@ limitations under the License. package unversioned import ( + "encoding/json" "fmt" "io/ioutil" "net" @@ -175,6 +176,51 @@ func MatchesServerVersion(client *Client, c *Config) error { return nil } +func extractGroupVersions(l *api.APIGroupList) []string { + var groupVersions []string + for _, g := range l.Groups { + for _, gv := range g.Versions { + groupVersions = append(groupVersions, gv.GroupVersion) + } + } + return groupVersions +} + +// ServerAPIVersions returns the GroupVersions supported by the API server. +// It creates a RESTClient based on the passed in config, but it doesn't rely +// on the Version, Codec, and Prefix of the config, because it uses AbsPath and +// takes the raw response. +func ServerAPIVersions(c *Config) (groupVersions []string, err error) { + client, err := RESTClientFor(c) + if err != nil { + return nil, err + } + // Get the groupVersions exposed at /api + body, err := client.Get().AbsPath("api").Do().Raw() + if err != nil { + return nil, err + } + var v api.APIVersions + err = json.Unmarshal(body, &v) + if err != nil { + return nil, fmt.Errorf("got '%s': %v", string(body), err) + } + groupVersions = append(groupVersions, v.Versions...) + // Get the groupVersions exposed at /apis + body, err = client.Get().AbsPath("apis").Do().Raw() + if err != nil { + return nil, err + } + var apiGroupList api.APIGroupList + err = json.Unmarshal(body, &apiGroupList) + if err != nil { + return nil, fmt.Errorf("got '%s': %v", string(body), err) + } + groupVersions = append(groupVersions, extractGroupVersions(&apiGroupList)...) + + return groupVersions, 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 registered client versions in order of diff --git a/pkg/client/unversioned/helper_test.go b/pkg/client/unversioned/helper_test.go index 21c70a890cb..0b2642b91ef 100644 --- a/pkg/client/unversioned/helper_test.go +++ b/pkg/client/unversioned/helper_test.go @@ -17,11 +17,14 @@ limitations under the License. package unversioned import ( + "encoding/json" "net/http" + "net/http/httptest" "reflect" "strings" "testing" + "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api/testapi" ) @@ -374,3 +377,59 @@ func TestSetKubernetesDefaultsUserAgent(t *testing.T) { t.Errorf("no user agent set: %#v", config) } } + +func TestHelperGetServerAPIVersions(t *testing.T) { + expect := []string{"v1", "v2", "v3"} + APIVersions := api.APIVersions{Versions: expect} + expect = append(expect, "group1/v1", "group1/v2", "group2/v1", "group2/v2") + APIGroupList := api.APIGroupList{ + Groups: []api.APIGroup{ + { + Versions: []api.GroupVersion{ + { + GroupVersion: "group1/v1", + }, + { + GroupVersion: "group1/v2", + }, + }, + }, + { + Versions: []api.GroupVersion{ + { + GroupVersion: "group2/v1", + }, + { + GroupVersion: "group2/v2", + }, + }, + }, + }, + } + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { + var output []byte + var err error + switch req.URL.Path { + case "/api": + output, err = json.Marshal(APIVersions) + + case "/apis": + output, err = json.Marshal(APIGroupList) + } + if err != nil { + t.Errorf("unexpected encoding error: %v", err) + return + } + + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + w.Write(output) + })) + got, err := ServerAPIVersions(&Config{Host: server.URL, Version: "invalid version", Codec: testapi.Default.Codec()}) + if err != nil { + t.Fatalf("unexpected encoding error: %v", err) + } + if e, a := expect, got; !reflect.DeepEqual(e, a) { + t.Errorf("expected %v, got %v", e, a) + } +} From 6dc99b52dd5090ca0680452d9ca6a46ca3580446 Mon Sep 17 00:00:00 2001 From: Chao Xu Date: Thu, 1 Oct 2015 16:23:01 -0700 Subject: [PATCH 2/3] use http.client in ServerAPIVersions --- pkg/client/unversioned/helper.go | 34 ++++++++++++++++++++++++++------ 1 file changed, 28 insertions(+), 6 deletions(-) diff --git a/pkg/client/unversioned/helper.go b/pkg/client/unversioned/helper.go index 98877735d22..01190d1213e 100644 --- a/pkg/client/unversioned/helper.go +++ b/pkg/client/unversioned/helper.go @@ -191,12 +191,29 @@ func extractGroupVersions(l *api.APIGroupList) []string { // on the Version, Codec, and Prefix of the config, because it uses AbsPath and // takes the raw response. func ServerAPIVersions(c *Config) (groupVersions []string, err error) { - client, err := RESTClientFor(c) + transport, err := TransportFor(c) + if err != nil { + return nil, err + } + client := http.Client{Transport: transport} + + configCopy := *c + configCopy.Version = "" + configCopy.Prefix = "" + baseURL, err := defaultServerUrlFor(c) if err != nil { return nil, err } // Get the groupVersions exposed at /api - body, err := client.Get().AbsPath("api").Do().Raw() + req, err := http.NewRequest("GET", baseURL.String()+"/api", nil) + if err != nil { + return nil, err + } + resp, err := client.Do(req) + if err != nil { + return nil, err + } + body, err := ioutil.ReadAll(resp.Body) if err != nil { return nil, err } @@ -207,7 +224,15 @@ func ServerAPIVersions(c *Config) (groupVersions []string, err error) { } groupVersions = append(groupVersions, v.Versions...) // Get the groupVersions exposed at /apis - body, err = client.Get().AbsPath("apis").Do().Raw() + req, err = http.NewRequest("GET", baseURL.String()+"/apis", nil) + if err != nil { + return nil, err + } + resp, err = client.Do(req) + if err != nil { + return nil, err + } + body, err = ioutil.ReadAll(resp.Body) if err != nil { return nil, err } @@ -519,9 +544,6 @@ func DefaultServerURL(host, prefix, version string, defaultTLS bool) (*url.URL, if host == "" { return nil, fmt.Errorf("host must be a URL or a host:port pair") } - if version == "" { - return nil, fmt.Errorf("version must be set") - } base := host hostURL, err := url.Parse(base) if err != nil { From efe7d30a6474e410c9b5f73ac960fa5c3e68c4cd Mon Sep 17 00:00:00 2001 From: Chao Xu Date: Wed, 7 Oct 2015 16:19:22 -0700 Subject: [PATCH 3/3] address lavalamp's comments --- pkg/client/unversioned/helper.go | 33 +++++++++++--------------------- 1 file changed, 11 insertions(+), 22 deletions(-) diff --git a/pkg/client/unversioned/helper.go b/pkg/client/unversioned/helper.go index 01190d1213e..e3344ed9eba 100644 --- a/pkg/client/unversioned/helper.go +++ b/pkg/client/unversioned/helper.go @@ -205,41 +205,30 @@ func ServerAPIVersions(c *Config) (groupVersions []string, err error) { return nil, err } // Get the groupVersions exposed at /api - req, err := http.NewRequest("GET", baseURL.String()+"/api", nil) - if err != nil { - return nil, err - } - resp, err := client.Do(req) - if err != nil { - return nil, err - } - body, err := ioutil.ReadAll(resp.Body) + baseURL.Path = "/api" + resp, err := client.Get(baseURL.String()) if err != nil { return nil, err } var v api.APIVersions - err = json.Unmarshal(body, &v) + defer resp.Body.Close() + err = json.NewDecoder(resp.Body).Decode(&v) if err != nil { - return nil, fmt.Errorf("got '%s': %v", string(body), err) + return nil, fmt.Errorf("unexpected error: %v", err) } + groupVersions = append(groupVersions, v.Versions...) // Get the groupVersions exposed at /apis - req, err = http.NewRequest("GET", baseURL.String()+"/apis", nil) - if err != nil { - return nil, err - } - resp, err = client.Do(req) - if err != nil { - return nil, err - } - body, err = ioutil.ReadAll(resp.Body) + baseURL.Path = "/apis" + resp2, err := client.Get(baseURL.String()) if err != nil { return nil, err } var apiGroupList api.APIGroupList - err = json.Unmarshal(body, &apiGroupList) + defer resp2.Body.Close() + err = json.NewDecoder(resp2.Body).Decode(&apiGroupList) if err != nil { - return nil, fmt.Errorf("got '%s': %v", string(body), err) + return nil, fmt.Errorf("unexpected error: %v", err) } groupVersions = append(groupVersions, extractGroupVersions(&apiGroupList)...)