mirror of
https://github.com/kubernetes/client-go.git
synced 2025-06-27 15:39:39 +00:00
kubectl OpenAPI support
Kubernetes-commit: 212c2a3a7217ff7e0a7c895582c7a25d03ac7f8c
This commit is contained in:
parent
c192350f2c
commit
97a8b3b040
@ -25,6 +25,8 @@ import (
|
|||||||
|
|
||||||
"github.com/emicklei/go-restful/swagger"
|
"github.com/emicklei/go-restful/swagger"
|
||||||
|
|
||||||
|
"github.com/go-openapi/loads"
|
||||||
|
"github.com/go-openapi/spec"
|
||||||
"k8s.io/apimachinery/pkg/api/errors"
|
"k8s.io/apimachinery/pkg/api/errors"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/runtime"
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
@ -47,6 +49,7 @@ type DiscoveryInterface interface {
|
|||||||
ServerResourcesInterface
|
ServerResourcesInterface
|
||||||
ServerVersionInterface
|
ServerVersionInterface
|
||||||
SwaggerSchemaInterface
|
SwaggerSchemaInterface
|
||||||
|
OpenAPISchemaInterface
|
||||||
}
|
}
|
||||||
|
|
||||||
// CachedDiscoveryInterface is a DiscoveryInterface with cache invalidation and freshness.
|
// CachedDiscoveryInterface is a DiscoveryInterface with cache invalidation and freshness.
|
||||||
@ -91,6 +94,12 @@ type SwaggerSchemaInterface interface {
|
|||||||
SwaggerSchema(version schema.GroupVersion) (*swagger.ApiDeclaration, error)
|
SwaggerSchema(version schema.GroupVersion) (*swagger.ApiDeclaration, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// OpenAPISchemaInterface has a method to retrieve the open API schema.
|
||||||
|
type OpenAPISchemaInterface interface {
|
||||||
|
// OpenAPISchema retrieves and parses the swagger API schema the server supports.
|
||||||
|
OpenAPISchema() (*spec.Swagger, error)
|
||||||
|
}
|
||||||
|
|
||||||
// DiscoveryClient implements the functions that discover server-supported API groups,
|
// DiscoveryClient implements the functions that discover server-supported API groups,
|
||||||
// versions and resources.
|
// versions and resources.
|
||||||
type DiscoveryClient struct {
|
type DiscoveryClient struct {
|
||||||
@ -332,6 +341,7 @@ func (d *DiscoveryClient) ServerVersion() (*version.Info, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// SwaggerSchema retrieves and parses the swagger API schema the server supports.
|
// SwaggerSchema retrieves and parses the swagger API schema the server supports.
|
||||||
|
// TODO: Replace usages with Open API. Tracked in https://github.com/kubernetes/kubernetes/issues/44589
|
||||||
func (d *DiscoveryClient) SwaggerSchema(version schema.GroupVersion) (*swagger.ApiDeclaration, error) {
|
func (d *DiscoveryClient) SwaggerSchema(version schema.GroupVersion) (*swagger.ApiDeclaration, error) {
|
||||||
if version.Empty() {
|
if version.Empty() {
|
||||||
return nil, fmt.Errorf("groupVersion cannot be empty")
|
return nil, fmt.Errorf("groupVersion cannot be empty")
|
||||||
@ -365,6 +375,21 @@ func (d *DiscoveryClient) SwaggerSchema(version schema.GroupVersion) (*swagger.A
|
|||||||
return &schema, nil
|
return &schema, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// OpenAPISchema fetches the open api schema using a rest client and parses the json.
|
||||||
|
// Warning: this is very expensive (~1.2s)
|
||||||
|
func (d *DiscoveryClient) OpenAPISchema() (*spec.Swagger, error) {
|
||||||
|
data, err := d.restClient.Get().AbsPath("/swagger.json").Do().Raw()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
msg := json.RawMessage(data)
|
||||||
|
doc, err := loads.Analyzed(msg, "")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return doc.Spec(), err
|
||||||
|
}
|
||||||
|
|
||||||
// withRetries retries the given recovery function in case the groups supported by the server change after ServerGroup() returns.
|
// withRetries retries the given recovery function in case the groups supported by the server change after ServerGroup() returns.
|
||||||
func withRetries(maxRetries int, f func(failEarly bool) ([]*metav1.APIResourceList, error)) ([]*metav1.APIResourceList, error) {
|
func withRetries(maxRetries int, f func(failEarly bool) ([]*metav1.APIResourceList, error)) ([]*metav1.APIResourceList, error) {
|
||||||
var result []*metav1.APIResourceList
|
var result []*metav1.APIResourceList
|
||||||
|
@ -18,6 +18,7 @@ package discovery_test
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
"reflect"
|
"reflect"
|
||||||
@ -25,6 +26,7 @@ import (
|
|||||||
|
|
||||||
"github.com/emicklei/go-restful/swagger"
|
"github.com/emicklei/go-restful/swagger"
|
||||||
|
|
||||||
|
"github.com/go-openapi/spec"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
"k8s.io/apimachinery/pkg/util/sets"
|
"k8s.io/apimachinery/pkg/util/sets"
|
||||||
@ -325,6 +327,81 @@ func TestGetSwaggerSchemaFail(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var returnedOpenAPI = spec.Swagger{
|
||||||
|
SwaggerProps: spec.SwaggerProps{
|
||||||
|
Definitions: spec.Definitions{
|
||||||
|
"fake.type.1": spec.Schema{
|
||||||
|
SchemaProps: spec.SchemaProps{
|
||||||
|
Properties: map[string]spec.Schema{
|
||||||
|
"count": {
|
||||||
|
SchemaProps: spec.SchemaProps{
|
||||||
|
Type: []string{"integer"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"fake.type.2": spec.Schema{
|
||||||
|
SchemaProps: spec.SchemaProps{
|
||||||
|
Properties: map[string]spec.Schema{
|
||||||
|
"count": {
|
||||||
|
SchemaProps: spec.SchemaProps{
|
||||||
|
Type: []string{"array"},
|
||||||
|
Items: &spec.SchemaOrArray{
|
||||||
|
Schema: &spec.Schema{
|
||||||
|
SchemaProps: spec.SchemaProps{
|
||||||
|
Type: []string{"string"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func openapiSchemaFakeServer() (*httptest.Server, error) {
|
||||||
|
var sErr error
|
||||||
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||||
|
if req.URL.Path != "/swagger.json" {
|
||||||
|
sErr = fmt.Errorf("Unexpected url %v", req.URL)
|
||||||
|
}
|
||||||
|
if req.Method != "GET" {
|
||||||
|
sErr = fmt.Errorf("Unexpected method %v", req.Method)
|
||||||
|
}
|
||||||
|
|
||||||
|
output, err := json.Marshal(returnedOpenAPI)
|
||||||
|
if err != nil {
|
||||||
|
sErr = err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
w.WriteHeader(http.StatusOK)
|
||||||
|
w.Write(output)
|
||||||
|
}))
|
||||||
|
return server, sErr
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetOpenAPISchema(t *testing.T) {
|
||||||
|
server, err := openapiSchemaFakeServer()
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("unexpected error starting fake server: %v", err)
|
||||||
|
}
|
||||||
|
defer server.Close()
|
||||||
|
|
||||||
|
client := NewDiscoveryClientForConfigOrDie(&restclient.Config{Host: server.URL})
|
||||||
|
got, err := client.OpenAPISchema()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error getting openapi: %v", err)
|
||||||
|
}
|
||||||
|
if e, a := returnedOpenAPI, *got; !reflect.DeepEqual(e, a) {
|
||||||
|
t.Errorf("expected %v, got %v", e, a)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestServerPreferredResources(t *testing.T) {
|
func TestServerPreferredResources(t *testing.T) {
|
||||||
stable := metav1.APIResourceList{
|
stable := metav1.APIResourceList{
|
||||||
GroupVersion: "v1",
|
GroupVersion: "v1",
|
||||||
|
@ -21,6 +21,7 @@ import (
|
|||||||
|
|
||||||
"github.com/emicklei/go-restful/swagger"
|
"github.com/emicklei/go-restful/swagger"
|
||||||
|
|
||||||
|
"github.com/go-openapi/spec"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
"k8s.io/apimachinery/pkg/version"
|
"k8s.io/apimachinery/pkg/version"
|
||||||
@ -92,6 +93,8 @@ func (c *FakeDiscovery) SwaggerSchema(version schema.GroupVersion) (*swagger.Api
|
|||||||
return &swagger.ApiDeclaration{}, nil
|
return &swagger.ApiDeclaration{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *FakeDiscovery) OpenAPISchema() (*spec.Swagger, error) { return &spec.Swagger{}, nil }
|
||||||
|
|
||||||
func (c *FakeDiscovery) RESTClient() restclient.Interface {
|
func (c *FakeDiscovery) RESTClient() restclient.Interface {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -30,6 +30,7 @@ import (
|
|||||||
"k8s.io/client-go/rest/fake"
|
"k8s.io/client-go/rest/fake"
|
||||||
|
|
||||||
"github.com/emicklei/go-restful/swagger"
|
"github.com/emicklei/go-restful/swagger"
|
||||||
|
"github.com/go-openapi/spec"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -347,3 +348,7 @@ func (c *fakeCachedDiscoveryInterface) ServerVersion() (*version.Info, error) {
|
|||||||
func (c *fakeCachedDiscoveryInterface) SwaggerSchema(version schema.GroupVersion) (*swagger.ApiDeclaration, error) {
|
func (c *fakeCachedDiscoveryInterface) SwaggerSchema(version schema.GroupVersion) (*swagger.ApiDeclaration, error) {
|
||||||
return &swagger.ApiDeclaration{}, nil
|
return &swagger.ApiDeclaration{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *fakeCachedDiscoveryInterface) OpenAPISchema() (*spec.Swagger, error) {
|
||||||
|
return &spec.Swagger{}, nil
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user