From 495433fbb70ed0602ebaa0b78319cdaff6b74493 Mon Sep 17 00:00:00 2001 From: Anastasis Andronidis Date: Thu, 13 Aug 2015 03:33:04 +0200 Subject: [PATCH] client can return swagger schema --- pkg/client/unversioned/client.go | 47 ++++++++++++++ pkg/client/unversioned/client_test.go | 62 +++++++++++++++++++ .../unversioned/testclient/testclient.go | 12 ++++ 3 files changed, 121 insertions(+) diff --git a/pkg/client/unversioned/client.go b/pkg/client/unversioned/client.go index 57a878bc44f..c1b7139513f 100644 --- a/pkg/client/unversioned/client.go +++ b/pkg/client/unversioned/client.go @@ -23,7 +23,10 @@ import ( "net/url" "strings" + "github.com/emicklei/go-restful/swagger" + "k8s.io/kubernetes/pkg/api" + "k8s.io/kubernetes/pkg/api/latest" "k8s.io/kubernetes/pkg/api/unversioned" "k8s.io/kubernetes/pkg/version" ) @@ -47,6 +50,7 @@ type Interface interface { PersistentVolumesInterface PersistentVolumeClaimsNamespacer ComponentStatusesInterface + SwaggerSchemaInterface Experimental() ExperimentalInterface } @@ -174,6 +178,49 @@ func (c *Client) ValidateComponents() (*api.ComponentStatusList, error) { return &api.ComponentStatusList{Items: statuses}, nil } +// SwaggerSchemaInterface has a method to retrieve the swagger schema. Used in +// client.Interface +type SwaggerSchemaInterface interface { + SwaggerSchema(version string) (*swagger.ApiDeclaration, error) +} + +// SwaggerSchema retrieves and parses the swagger API schema the server supports. +func (c *Client) SwaggerSchema(version string) (*swagger.ApiDeclaration, error) { + if version == "" { + version = latest.GroupOrDie("").Version + } + + vers, err := c.ServerAPIVersions() + if err != nil { + return nil, err + } + + // This check also takes care the case that kubectl is newer than the running endpoint + if stringDoesntExistIn(version, vers.Versions) { + return nil, fmt.Errorf("API version: %s is not supported by the server. Use one of: %v", version, vers.Versions) + } + + body, err := c.Get().AbsPath("/swaggerapi/api/" + version).Do().Raw() + if err != nil { + return nil, err + } + var schema swagger.ApiDeclaration + err = json.Unmarshal(body, &schema) + if err != nil { + return nil, fmt.Errorf("got '%s': %v", string(body), err) + } + return &schema, nil +} + +func stringDoesntExistIn(str string, slice []string) bool { + for _, s := range slice { + if s == str { + return false + } + } + return true +} + // IsTimeout tests if this is a timeout error in the underlying transport. // This is unbelievably ugly. // See: http://stackoverflow.com/questions/23494950/specifically-check-for-timeout-error for details diff --git a/pkg/client/unversioned/client_test.go b/pkg/client/unversioned/client_test.go index 72f412b4778..61b13fc44d2 100644 --- a/pkg/client/unversioned/client_test.go +++ b/pkg/client/unversioned/client_test.go @@ -26,6 +26,8 @@ import ( "strings" "testing" + "github.com/emicklei/go-restful/swagger" + "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api/testapi" "k8s.io/kubernetes/pkg/fields" @@ -290,3 +292,63 @@ func TestGetServerAPIVersions(t *testing.T) { t.Errorf("expected %v, got %v", e, a) } } + +func swaggerSchemaFakeServer() (*httptest.Server, error) { + request := 1 + var sErr error + + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { + var resp interface{} + if request == 1 { + resp = api.APIVersions{Versions: []string{"v1", "v2", "v3"}} + request++ + } else { + resp = swagger.ApiDeclaration{} + } + output, err := json.Marshal(resp) + if err != nil { + sErr = err + return + } + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + w.Write(output) + })) + return server, sErr +} + +func TestGetSwaggerSchema(t *testing.T) { + expect := swagger.ApiDeclaration{} + + server, err := swaggerSchemaFakeServer() + if err != nil { + t.Errorf("unexpected encoding error: %v", err) + } + + client := NewOrDie(&Config{Host: server.URL}) + got, err := client.SwaggerSchema("v1") + 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) + } +} + +func TestGetSwaggerSchemaFail(t *testing.T) { + expErr := "API version: v4 is not supported by the server. Use one of: [v1 v2 v3]" + + server, err := swaggerSchemaFakeServer() + if err != nil { + t.Errorf("unexpected encoding error: %v", err) + } + + client := NewOrDie(&Config{Host: server.URL}) + got, err := client.SwaggerSchema("v4") + if got != nil { + t.Fatalf("unexpected response: %v", got) + } + if err.Error() != expErr { + t.Errorf("expected an error, got %v", err) + } +} diff --git a/pkg/client/unversioned/testclient/testclient.go b/pkg/client/unversioned/testclient/testclient.go index 8a675674e14..b7fe13570bb 100644 --- a/pkg/client/unversioned/testclient/testclient.go +++ b/pkg/client/unversioned/testclient/testclient.go @@ -20,6 +20,8 @@ import ( "fmt" "sync" + "github.com/emicklei/go-restful/swagger" + "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api/registered" client "k8s.io/kubernetes/pkg/client/unversioned" @@ -282,6 +284,16 @@ func (c *Fake) ComponentStatuses() client.ComponentStatusInterface { return &FakeComponentStatuses{Fake: c} } +// SwaggerSchema returns an empty swagger.ApiDeclaration for testing +func (c *Fake) SwaggerSchema(version string) (*swagger.ApiDeclaration, error) { + action := ActionImpl{} + action.Verb = "get" + action.Resource = "/swaggerapi/api/" + version + + c.Invokes(action, nil) + return &swagger.ApiDeclaration{}, nil +} + type FakeExperimental struct { *Fake }