Merge pull request #59293 from roycaihw/openapi_endpoint

Automatic merge from submit-queue (batch tested with PRs 60011, 59256, 59293, 60328, 60367). If you want to cherry-pick this change to another branch, please follow the instructions <a href="https://github.com/kubernetes/community/blob/master/contributors/devel/cherry-picks.md">here</a>.

Serve OpenAPI spec with single /openapi/v2 endpoint

**What this PR does / why we need it**:
We are deprecating format-separated endpoints (`/swagger.json`, `/swagger-2.0.0.json`, `/swagger-2.0.0.pb-v1`, `/swagger-2.0.0.pb-v1.gz`) for OpenAPI spec, and switching to a single `/openapi/v2` endpoint in Kubernetes 1.10. The design doc and deprecation process are tracked at: https://docs.google.com/document/d/19lEqE9lc4yHJ3WJAJxS_G7TcORIJXGHyq3wpwcH28nU

Requested format is specified by setting HTTP headers

header | possible values
-- | --
Accept | `application/json`, `application/com.github.proto-openapi.spec.v2@v1.0+protobuf`
Accept-Encoding | `gzip`

This PR changes dynamic_client (and kubectl as a result) to use the new endpoint. The old endpoints will remain in 1.10 and 1.11, and get removed in 1.12.

**Which issue(s) this PR fixes** *(optional, in `fixes #<issue number>(, fixes #<issue_number>, ...)` format, will close the issue(s) when PR gets merged)*:
Fixes #

**Special notes for your reviewer**:

**Release note**:

```release-note
action required: Deprecate format-separated endpoints for OpenAPI spec. Please use single `/openapi/v2` endpoint instead.
```

/sig api-machinery

Kubernetes-commit: d6153194d929ad6c036d5bbbf67a6f892e75feb5
This commit is contained in:
Kubernetes Publisher 2018-02-26 23:47:53 -08:00
commit fbdccbf09b
3 changed files with 647 additions and 585 deletions

108
Godeps/Godeps.json generated
View File

@ -364,219 +364,219 @@
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/api/equality", "ImportPath": "k8s.io/apimachinery/pkg/api/equality",
"Rev": "0406a944760b00b7a75b559f642673d7bb07b48a" "Rev": "bf4c8d907ba9e7fee82a73bad5346aeebff5d06e"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/api/errors", "ImportPath": "k8s.io/apimachinery/pkg/api/errors",
"Rev": "0406a944760b00b7a75b559f642673d7bb07b48a" "Rev": "bf4c8d907ba9e7fee82a73bad5346aeebff5d06e"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/api/meta", "ImportPath": "k8s.io/apimachinery/pkg/api/meta",
"Rev": "0406a944760b00b7a75b559f642673d7bb07b48a" "Rev": "bf4c8d907ba9e7fee82a73bad5346aeebff5d06e"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/api/resource", "ImportPath": "k8s.io/apimachinery/pkg/api/resource",
"Rev": "0406a944760b00b7a75b559f642673d7bb07b48a" "Rev": "bf4c8d907ba9e7fee82a73bad5346aeebff5d06e"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/api/testing", "ImportPath": "k8s.io/apimachinery/pkg/api/testing",
"Rev": "0406a944760b00b7a75b559f642673d7bb07b48a" "Rev": "bf4c8d907ba9e7fee82a73bad5346aeebff5d06e"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/api/testing/fuzzer", "ImportPath": "k8s.io/apimachinery/pkg/api/testing/fuzzer",
"Rev": "0406a944760b00b7a75b559f642673d7bb07b48a" "Rev": "bf4c8d907ba9e7fee82a73bad5346aeebff5d06e"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/api/testing/roundtrip", "ImportPath": "k8s.io/apimachinery/pkg/api/testing/roundtrip",
"Rev": "0406a944760b00b7a75b559f642673d7bb07b48a" "Rev": "bf4c8d907ba9e7fee82a73bad5346aeebff5d06e"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/apimachinery", "ImportPath": "k8s.io/apimachinery/pkg/apimachinery",
"Rev": "0406a944760b00b7a75b559f642673d7bb07b48a" "Rev": "bf4c8d907ba9e7fee82a73bad5346aeebff5d06e"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/apimachinery/announced", "ImportPath": "k8s.io/apimachinery/pkg/apimachinery/announced",
"Rev": "0406a944760b00b7a75b559f642673d7bb07b48a" "Rev": "bf4c8d907ba9e7fee82a73bad5346aeebff5d06e"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/apimachinery/registered", "ImportPath": "k8s.io/apimachinery/pkg/apimachinery/registered",
"Rev": "0406a944760b00b7a75b559f642673d7bb07b48a" "Rev": "bf4c8d907ba9e7fee82a73bad5346aeebff5d06e"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/apis/meta/fuzzer", "ImportPath": "k8s.io/apimachinery/pkg/apis/meta/fuzzer",
"Rev": "0406a944760b00b7a75b559f642673d7bb07b48a" "Rev": "bf4c8d907ba9e7fee82a73bad5346aeebff5d06e"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/apis/meta/internalversion", "ImportPath": "k8s.io/apimachinery/pkg/apis/meta/internalversion",
"Rev": "0406a944760b00b7a75b559f642673d7bb07b48a" "Rev": "bf4c8d907ba9e7fee82a73bad5346aeebff5d06e"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/apis/meta/v1", "ImportPath": "k8s.io/apimachinery/pkg/apis/meta/v1",
"Rev": "0406a944760b00b7a75b559f642673d7bb07b48a" "Rev": "bf4c8d907ba9e7fee82a73bad5346aeebff5d06e"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured", "ImportPath": "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured",
"Rev": "0406a944760b00b7a75b559f642673d7bb07b48a" "Rev": "bf4c8d907ba9e7fee82a73bad5346aeebff5d06e"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/apis/meta/v1beta1", "ImportPath": "k8s.io/apimachinery/pkg/apis/meta/v1beta1",
"Rev": "0406a944760b00b7a75b559f642673d7bb07b48a" "Rev": "bf4c8d907ba9e7fee82a73bad5346aeebff5d06e"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/conversion", "ImportPath": "k8s.io/apimachinery/pkg/conversion",
"Rev": "0406a944760b00b7a75b559f642673d7bb07b48a" "Rev": "bf4c8d907ba9e7fee82a73bad5346aeebff5d06e"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/conversion/queryparams", "ImportPath": "k8s.io/apimachinery/pkg/conversion/queryparams",
"Rev": "0406a944760b00b7a75b559f642673d7bb07b48a" "Rev": "bf4c8d907ba9e7fee82a73bad5346aeebff5d06e"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/fields", "ImportPath": "k8s.io/apimachinery/pkg/fields",
"Rev": "0406a944760b00b7a75b559f642673d7bb07b48a" "Rev": "bf4c8d907ba9e7fee82a73bad5346aeebff5d06e"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/labels", "ImportPath": "k8s.io/apimachinery/pkg/labels",
"Rev": "0406a944760b00b7a75b559f642673d7bb07b48a" "Rev": "bf4c8d907ba9e7fee82a73bad5346aeebff5d06e"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/runtime", "ImportPath": "k8s.io/apimachinery/pkg/runtime",
"Rev": "0406a944760b00b7a75b559f642673d7bb07b48a" "Rev": "bf4c8d907ba9e7fee82a73bad5346aeebff5d06e"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/runtime/schema", "ImportPath": "k8s.io/apimachinery/pkg/runtime/schema",
"Rev": "0406a944760b00b7a75b559f642673d7bb07b48a" "Rev": "bf4c8d907ba9e7fee82a73bad5346aeebff5d06e"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/runtime/serializer", "ImportPath": "k8s.io/apimachinery/pkg/runtime/serializer",
"Rev": "0406a944760b00b7a75b559f642673d7bb07b48a" "Rev": "bf4c8d907ba9e7fee82a73bad5346aeebff5d06e"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/runtime/serializer/json", "ImportPath": "k8s.io/apimachinery/pkg/runtime/serializer/json",
"Rev": "0406a944760b00b7a75b559f642673d7bb07b48a" "Rev": "bf4c8d907ba9e7fee82a73bad5346aeebff5d06e"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/runtime/serializer/protobuf", "ImportPath": "k8s.io/apimachinery/pkg/runtime/serializer/protobuf",
"Rev": "0406a944760b00b7a75b559f642673d7bb07b48a" "Rev": "bf4c8d907ba9e7fee82a73bad5346aeebff5d06e"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/runtime/serializer/recognizer", "ImportPath": "k8s.io/apimachinery/pkg/runtime/serializer/recognizer",
"Rev": "0406a944760b00b7a75b559f642673d7bb07b48a" "Rev": "bf4c8d907ba9e7fee82a73bad5346aeebff5d06e"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/runtime/serializer/streaming", "ImportPath": "k8s.io/apimachinery/pkg/runtime/serializer/streaming",
"Rev": "0406a944760b00b7a75b559f642673d7bb07b48a" "Rev": "bf4c8d907ba9e7fee82a73bad5346aeebff5d06e"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/runtime/serializer/versioning", "ImportPath": "k8s.io/apimachinery/pkg/runtime/serializer/versioning",
"Rev": "0406a944760b00b7a75b559f642673d7bb07b48a" "Rev": "bf4c8d907ba9e7fee82a73bad5346aeebff5d06e"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/selection", "ImportPath": "k8s.io/apimachinery/pkg/selection",
"Rev": "0406a944760b00b7a75b559f642673d7bb07b48a" "Rev": "bf4c8d907ba9e7fee82a73bad5346aeebff5d06e"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/types", "ImportPath": "k8s.io/apimachinery/pkg/types",
"Rev": "0406a944760b00b7a75b559f642673d7bb07b48a" "Rev": "bf4c8d907ba9e7fee82a73bad5346aeebff5d06e"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/util/cache", "ImportPath": "k8s.io/apimachinery/pkg/util/cache",
"Rev": "0406a944760b00b7a75b559f642673d7bb07b48a" "Rev": "bf4c8d907ba9e7fee82a73bad5346aeebff5d06e"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/util/clock", "ImportPath": "k8s.io/apimachinery/pkg/util/clock",
"Rev": "0406a944760b00b7a75b559f642673d7bb07b48a" "Rev": "bf4c8d907ba9e7fee82a73bad5346aeebff5d06e"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/util/diff", "ImportPath": "k8s.io/apimachinery/pkg/util/diff",
"Rev": "0406a944760b00b7a75b559f642673d7bb07b48a" "Rev": "bf4c8d907ba9e7fee82a73bad5346aeebff5d06e"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/util/errors", "ImportPath": "k8s.io/apimachinery/pkg/util/errors",
"Rev": "0406a944760b00b7a75b559f642673d7bb07b48a" "Rev": "bf4c8d907ba9e7fee82a73bad5346aeebff5d06e"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/util/framer", "ImportPath": "k8s.io/apimachinery/pkg/util/framer",
"Rev": "0406a944760b00b7a75b559f642673d7bb07b48a" "Rev": "bf4c8d907ba9e7fee82a73bad5346aeebff5d06e"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/util/httpstream", "ImportPath": "k8s.io/apimachinery/pkg/util/httpstream",
"Rev": "0406a944760b00b7a75b559f642673d7bb07b48a" "Rev": "bf4c8d907ba9e7fee82a73bad5346aeebff5d06e"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/util/httpstream/spdy", "ImportPath": "k8s.io/apimachinery/pkg/util/httpstream/spdy",
"Rev": "0406a944760b00b7a75b559f642673d7bb07b48a" "Rev": "bf4c8d907ba9e7fee82a73bad5346aeebff5d06e"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/util/intstr", "ImportPath": "k8s.io/apimachinery/pkg/util/intstr",
"Rev": "0406a944760b00b7a75b559f642673d7bb07b48a" "Rev": "bf4c8d907ba9e7fee82a73bad5346aeebff5d06e"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/util/json", "ImportPath": "k8s.io/apimachinery/pkg/util/json",
"Rev": "0406a944760b00b7a75b559f642673d7bb07b48a" "Rev": "bf4c8d907ba9e7fee82a73bad5346aeebff5d06e"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/util/mergepatch", "ImportPath": "k8s.io/apimachinery/pkg/util/mergepatch",
"Rev": "0406a944760b00b7a75b559f642673d7bb07b48a" "Rev": "bf4c8d907ba9e7fee82a73bad5346aeebff5d06e"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/util/net", "ImportPath": "k8s.io/apimachinery/pkg/util/net",
"Rev": "0406a944760b00b7a75b559f642673d7bb07b48a" "Rev": "bf4c8d907ba9e7fee82a73bad5346aeebff5d06e"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/util/remotecommand", "ImportPath": "k8s.io/apimachinery/pkg/util/remotecommand",
"Rev": "0406a944760b00b7a75b559f642673d7bb07b48a" "Rev": "bf4c8d907ba9e7fee82a73bad5346aeebff5d06e"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/util/runtime", "ImportPath": "k8s.io/apimachinery/pkg/util/runtime",
"Rev": "0406a944760b00b7a75b559f642673d7bb07b48a" "Rev": "bf4c8d907ba9e7fee82a73bad5346aeebff5d06e"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/util/sets", "ImportPath": "k8s.io/apimachinery/pkg/util/sets",
"Rev": "0406a944760b00b7a75b559f642673d7bb07b48a" "Rev": "bf4c8d907ba9e7fee82a73bad5346aeebff5d06e"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/util/strategicpatch", "ImportPath": "k8s.io/apimachinery/pkg/util/strategicpatch",
"Rev": "0406a944760b00b7a75b559f642673d7bb07b48a" "Rev": "bf4c8d907ba9e7fee82a73bad5346aeebff5d06e"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/util/validation", "ImportPath": "k8s.io/apimachinery/pkg/util/validation",
"Rev": "0406a944760b00b7a75b559f642673d7bb07b48a" "Rev": "bf4c8d907ba9e7fee82a73bad5346aeebff5d06e"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/util/validation/field", "ImportPath": "k8s.io/apimachinery/pkg/util/validation/field",
"Rev": "0406a944760b00b7a75b559f642673d7bb07b48a" "Rev": "bf4c8d907ba9e7fee82a73bad5346aeebff5d06e"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/util/wait", "ImportPath": "k8s.io/apimachinery/pkg/util/wait",
"Rev": "0406a944760b00b7a75b559f642673d7bb07b48a" "Rev": "bf4c8d907ba9e7fee82a73bad5346aeebff5d06e"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/util/yaml", "ImportPath": "k8s.io/apimachinery/pkg/util/yaml",
"Rev": "0406a944760b00b7a75b559f642673d7bb07b48a" "Rev": "bf4c8d907ba9e7fee82a73bad5346aeebff5d06e"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/version", "ImportPath": "k8s.io/apimachinery/pkg/version",
"Rev": "0406a944760b00b7a75b559f642673d7bb07b48a" "Rev": "bf4c8d907ba9e7fee82a73bad5346aeebff5d06e"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/watch", "ImportPath": "k8s.io/apimachinery/pkg/watch",
"Rev": "0406a944760b00b7a75b559f642673d7bb07b48a" "Rev": "bf4c8d907ba9e7fee82a73bad5346aeebff5d06e"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/third_party/forked/golang/json", "ImportPath": "k8s.io/apimachinery/third_party/forked/golang/json",
"Rev": "0406a944760b00b7a75b559f642673d7bb07b48a" "Rev": "bf4c8d907ba9e7fee82a73bad5346aeebff5d06e"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/third_party/forked/golang/netutil", "ImportPath": "k8s.io/apimachinery/third_party/forked/golang/netutil",
"Rev": "0406a944760b00b7a75b559f642673d7bb07b48a" "Rev": "bf4c8d907ba9e7fee82a73bad5346aeebff5d06e"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/third_party/forked/golang/reflect", "ImportPath": "k8s.io/apimachinery/third_party/forked/golang/reflect",
"Rev": "0406a944760b00b7a75b559f642673d7bb07b48a" "Rev": "bf4c8d907ba9e7fee82a73bad5346aeebff5d06e"
}, },
{ {
"ImportPath": "k8s.io/kube-openapi/pkg/util/proto", "ImportPath": "k8s.io/kube-openapi/pkg/util/proto",
"Rev": "a07b7bbb58e7fdc5144f8d7046331d29fc9ad3b3" "Rev": "50ae88d24ede7b8bad68e23c805b5d3da5c8abaf"
} }
] ]
} }

View File

@ -36,8 +36,12 @@ import (
restclient "k8s.io/client-go/rest" restclient "k8s.io/client-go/rest"
) )
// defaultRetries is the number of times a resource discovery is repeated if an api group disappears on the fly (e.g. ThirdPartyResources). const (
const defaultRetries = 2 // defaultRetries is the number of times a resource discovery is repeated if an api group disappears on the fly (e.g. ThirdPartyResources).
defaultRetries = 2
// protobuf mime type
mimePb = "application/com.github.proto-openapi.spec.v2@v1.0+protobuf"
)
// DiscoveryInterface holds the methods that discover server-supported API groups, // DiscoveryInterface holds the methods that discover server-supported API groups,
// versions and resources. // versions and resources.
@ -329,10 +333,19 @@ func (d *DiscoveryClient) ServerVersion() (*version.Info, error) {
// OpenAPISchema fetches the open api schema using a rest client and parses the proto. // OpenAPISchema fetches the open api schema using a rest client and parses the proto.
func (d *DiscoveryClient) OpenAPISchema() (*openapi_v2.Document, error) { func (d *DiscoveryClient) OpenAPISchema() (*openapi_v2.Document, error) {
data, err := d.restClient.Get().AbsPath("/swagger-2.0.0.pb-v1").Do().Raw() data, err := d.restClient.Get().AbsPath("/openapi/v2").SetHeader("Accept", mimePb).Do().Raw()
if err != nil {
if errors.IsForbidden(err) || errors.IsNotFound(err) {
// single endpoint not found/registered in old server, try to fetch old endpoint
// TODO(roycaihw): remove this in 1.11
data, err = d.restClient.Get().AbsPath("/swagger-2.0.0.pb-v1").Do().Raw()
if err != nil { if err != nil {
return nil, err return nil, err
} }
} else {
return nil, err
}
}
document := &openapi_v2.Document{} document := &openapi_v2.Document{}
err = proto.Unmarshal(data, document) err = proto.Unmarshal(data, document)
if err != nil { if err != nil {

View File

@ -326,9 +326,14 @@ var returnedOpenAPI = openapi_v2.Document{
}, },
} }
func openapiSchemaFakeServer() (*httptest.Server, error) { func openapiSchemaDeprecatedFakeServer() (*httptest.Server, error) {
var sErr error var sErr error
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
// old server returns 403 on new endpoint request
if req.URL.Path == "/openapi/v2" {
w.WriteHeader(http.StatusForbidden)
return
}
if req.URL.Path != "/swagger-2.0.0.pb-v1" { if req.URL.Path != "/swagger-2.0.0.pb-v1" {
sErr = fmt.Errorf("Unexpected url %v", req.URL) sErr = fmt.Errorf("Unexpected url %v", req.URL)
} }
@ -349,6 +354,33 @@ func openapiSchemaFakeServer() (*httptest.Server, error) {
return server, sErr return server, sErr
} }
func openapiSchemaFakeServer() (*httptest.Server, error) {
var sErr error
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
if req.URL.Path != "/openapi/v2" {
sErr = fmt.Errorf("Unexpected url %v", req.URL)
}
if req.Method != "GET" {
sErr = fmt.Errorf("Unexpected method %v", req.Method)
}
decipherableFormat := req.Header.Get("Accept")
if decipherableFormat != "application/com.github.proto-openapi.spec.v2@v1.0+protobuf" {
sErr = fmt.Errorf("Unexpected accept mime type %v", decipherableFormat)
}
mime.AddExtensionType(".pb-v1", "application/com.github.googleapis.gnostic.OpenAPIv2@68f4ded+protobuf")
output, err := proto.Marshal(&returnedOpenAPI)
if err != nil {
sErr = err
return
}
w.WriteHeader(http.StatusOK)
w.Write(output)
}))
return server, sErr
}
func TestGetOpenAPISchema(t *testing.T) { func TestGetOpenAPISchema(t *testing.T) {
server, err := openapiSchemaFakeServer() server, err := openapiSchemaFakeServer()
if err != nil { if err != nil {
@ -366,6 +398,23 @@ func TestGetOpenAPISchema(t *testing.T) {
} }
} }
func TestGetOpenAPISchemaFallback(t *testing.T) {
server, err := openapiSchemaDeprecatedFakeServer()
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",