Strip version when encoding discovery API objects at endpoints that exist since release 1.1

This commit is contained in:
Chao Xu 2016-02-11 17:03:43 -08:00
parent 4d59d700c2
commit 39838745e5
2 changed files with 141 additions and 4 deletions

View File

@ -221,7 +221,11 @@ func serviceErrorHandler(s runtime.NegotiatedSerializer, requestResolver *Reques
func AddApiWebService(s runtime.NegotiatedSerializer, container *restful.Container, apiPrefix string, getAPIVersionsFunc func(req *restful.Request) *unversioned.APIVersions) {
// TODO: InstallREST should register each version automatically
versionHandler := APIVersionHandler(s, getAPIVersionsFunc)
// Because in release 1.1, /api returns response with empty APIVersion, we
// use StripVersionNegotiatedSerializer to keep the response backwards
// compatible.
ss := StripVersionNegotiatedSerializer{s}
versionHandler := APIVersionHandler(ss, getAPIVersionsFunc)
ws := new(restful.WebService)
ws.Path(apiPrefix)
ws.Doc("get available API versions")
@ -233,9 +237,52 @@ func AddApiWebService(s runtime.NegotiatedSerializer, container *restful.Contain
container.Add(ws)
}
// stripVersionEncoder strips APIVersion field from the encoding output. It's
// used to keep the responses at the discovery endpoints backward compatible
// with release-1.1, when the responses have empty APIVersion.
type stripVersionEncoder struct {
encoder runtime.Encoder
serializer runtime.Serializer
}
func (c stripVersionEncoder) EncodeToStream(obj runtime.Object, w io.Writer, overrides ...unversioned.GroupVersion) error {
buf := bytes.NewBuffer([]byte{})
err := c.encoder.EncodeToStream(obj, buf, overrides...)
if err != nil {
return err
}
roundTrippedObj, gvk, err := c.serializer.Decode(buf.Bytes(), nil, nil)
if err != nil {
return err
}
gvk.Group = ""
gvk.Version = ""
roundTrippedObj.GetObjectKind().SetGroupVersionKind(gvk)
return c.serializer.EncodeToStream(roundTrippedObj, w)
}
// StripVersionNegotiatedSerializer will return stripVersionEncoder when
// EncoderForVersion is called. See comments for stripVersionEncoder.
type StripVersionNegotiatedSerializer struct {
runtime.NegotiatedSerializer
}
func (n StripVersionNegotiatedSerializer) EncoderForVersion(serializer runtime.Serializer, gv unversioned.GroupVersion) runtime.Encoder {
encoder := n.NegotiatedSerializer.EncoderForVersion(serializer, gv)
return stripVersionEncoder{encoder, serializer}
}
func keepUnversioned(group string) bool {
return group == "" || group == "extensions"
}
// Adds a service to return the supported api versions at /apis.
func AddApisWebService(s runtime.NegotiatedSerializer, container *restful.Container, apiPrefix string, f func(req *restful.Request) []unversioned.APIGroup) {
rootAPIHandler := RootAPIHandler(s, f)
// Because in release 1.1, /apis returns response with empty APIVersion, we
// use StripVersionNegotiatedSerializer to keep the response backwards
// compatible.
ss := StripVersionNegotiatedSerializer{s}
rootAPIHandler := RootAPIHandler(ss, f)
ws := new(restful.WebService)
ws.Path(apiPrefix)
ws.Doc("get available API versions")
@ -250,7 +297,14 @@ func AddApisWebService(s runtime.NegotiatedSerializer, container *restful.Contai
// Adds a service to return the supported versions, preferred version, and name
// of a group. E.g., a such web service will be registered at /apis/extensions.
func AddGroupWebService(s runtime.NegotiatedSerializer, container *restful.Container, path string, group unversioned.APIGroup) {
groupHandler := GroupHandler(s, group)
ss := s
if keepUnversioned(group.Name) {
// Because in release 1.1, /apis/extensions returns response with empty
// APIVersion, we use StripVersionNegotiatedSerializer to keep the
// response backwards compatible.
ss = StripVersionNegotiatedSerializer{s}
}
groupHandler := GroupHandler(ss, group)
ws := new(restful.WebService)
ws.Path(path)
ws.Doc("get information of a group")
@ -265,7 +319,14 @@ func AddGroupWebService(s runtime.NegotiatedSerializer, container *restful.Conta
// Adds a service to return the supported resources, E.g., a such web service
// will be registered at /apis/extensions/v1.
func AddSupportedResourcesWebService(s runtime.NegotiatedSerializer, ws *restful.WebService, groupVersion unversioned.GroupVersion, apiResources []unversioned.APIResource) {
resourceHandler := SupportedResourcesHandler(s, groupVersion, apiResources)
ss := s
if keepUnversioned(groupVersion.Group) {
// Because in release 1.1, /apis/extensions/v1beta1 returns response
// with empty APIVersion, we use StripVersionNegotiatedSerializer to
// keep the response backwards compatible.
ss = StripVersionNegotiatedSerializer{s}
}
resourceHandler := SupportedResourcesHandler(ss, groupVersion, apiResources)
ws.Route(ws.GET("/").To(resourceHandler).
Doc("get available resources").
Operation("getAPIResources").

View File

@ -260,6 +260,82 @@ func TestGetNodeAddresses(t *testing.T) {
assert.Equal([]string{"127.0.0.2", "127.0.0.2"}, addrs)
}
// Because we need to be backwards compatible with release 1.1, at endpoints
// that exist in release 1.1, the responses should have empty APIVersion.
func TestAPIVersionOfDiscoveryEndpoints(t *testing.T) {
master, etcdserver, _, assert := newMaster(t)
defer etcdserver.Terminate(t)
server := httptest.NewServer(master.HandlerContainer.ServeMux)
// /api exists in release-1.1
resp, err := http.Get(server.URL + "/api")
if err != nil {
t.Errorf("unexpected error: %v", err)
}
apiVersions := unversioned.APIVersions{}
assert.NoError(decodeResponse(resp, &apiVersions))
assert.Equal(apiVersions.APIVersion, "")
// /api/v1 exists in release-1.1
resp, err = http.Get(server.URL + "/api/v1")
if err != nil {
t.Errorf("unexpected error: %v", err)
}
resourceList := unversioned.APIResourceList{}
assert.NoError(decodeResponse(resp, &resourceList))
assert.Equal(resourceList.APIVersion, "")
// /apis exists in release-1.1
resp, err = http.Get(server.URL + "/apis")
if err != nil {
t.Errorf("unexpected error: %v", err)
}
groupList := unversioned.APIGroupList{}
assert.NoError(decodeResponse(resp, &groupList))
assert.Equal(groupList.APIVersion, "")
// /apis/extensions exists in release-1.1
resp, err = http.Get(server.URL + "/apis/extensions")
if err != nil {
t.Errorf("unexpected error: %v", err)
}
group := unversioned.APIGroup{}
assert.NoError(decodeResponse(resp, &group))
assert.Equal(group.APIVersion, "")
// /apis/extensions/v1beta1 exists in release-1.1
resp, err = http.Get(server.URL + "/apis/extensions/v1beta1")
if err != nil {
t.Errorf("unexpected error: %v", err)
}
resourceList = unversioned.APIResourceList{}
assert.NoError(decodeResponse(resp, &resourceList))
assert.Equal(resourceList.APIVersion, "")
// /apis/autoscaling doesn't exist in release-1.1, so the APIVersion field
// should be non-empty in the results returned by the server.
resp, err = http.Get(server.URL + "/apis/autoscaling")
if err != nil {
t.Errorf("unexpected error: %v", err)
}
group = unversioned.APIGroup{}
assert.NoError(decodeResponse(resp, &group))
assert.Equal(group.APIVersion, "v1")
// apis/autoscaling/v1 doesn't exist in release-1.1, so the APIVersion field
// should be non-empty in the results returned by the server.
resp, err = http.Get(server.URL + "/apis/autoscaling/v1")
if err != nil {
t.Errorf("unexpected error: %v", err)
}
resourceList = unversioned.APIResourceList{}
assert.NoError(decodeResponse(resp, &resourceList))
assert.Equal(resourceList.APIVersion, "v1")
}
func TestDiscoveryAtAPIS(t *testing.T) {
master, etcdserver, config, assert := newMaster(t)
defer etcdserver.Terminate(t)