mirror of
https://github.com/kubernetes/client-go.git
synced 2025-06-24 06:07:48 +00:00
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:
commit
fbdccbf09b
1160
Godeps/Godeps.json
generated
1160
Godeps/Godeps.json
generated
File diff suppressed because it is too large
Load Diff
@ -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,9 +333,18 @@ 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 err != nil {
|
||||||
return nil, err
|
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 {
|
||||||
|
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)
|
||||||
|
@ -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",
|
||||||
|
Loading…
Reference in New Issue
Block a user