Strip version when encoding discovery API objects at endpoints that exist since release 1.1
This commit is contained in:
		@@ -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) {
 | 
					func AddApiWebService(s runtime.NegotiatedSerializer, container *restful.Container, apiPrefix string, getAPIVersionsFunc func(req *restful.Request) *unversioned.APIVersions) {
 | 
				
			||||||
	// TODO: InstallREST should register each version automatically
 | 
						// 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 := new(restful.WebService)
 | 
				
			||||||
	ws.Path(apiPrefix)
 | 
						ws.Path(apiPrefix)
 | 
				
			||||||
	ws.Doc("get available API versions")
 | 
						ws.Doc("get available API versions")
 | 
				
			||||||
@@ -233,9 +237,52 @@ func AddApiWebService(s runtime.NegotiatedSerializer, container *restful.Contain
 | 
				
			|||||||
	container.Add(ws)
 | 
						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.
 | 
					// 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) {
 | 
					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 := new(restful.WebService)
 | 
				
			||||||
	ws.Path(apiPrefix)
 | 
						ws.Path(apiPrefix)
 | 
				
			||||||
	ws.Doc("get available API versions")
 | 
						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
 | 
					// 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.
 | 
					// 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) {
 | 
					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 := new(restful.WebService)
 | 
				
			||||||
	ws.Path(path)
 | 
						ws.Path(path)
 | 
				
			||||||
	ws.Doc("get information of a group")
 | 
						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
 | 
					// Adds a service to return the supported resources, E.g., a such web service
 | 
				
			||||||
// will be registered at /apis/extensions/v1.
 | 
					// will be registered at /apis/extensions/v1.
 | 
				
			||||||
func AddSupportedResourcesWebService(s runtime.NegotiatedSerializer, ws *restful.WebService, groupVersion unversioned.GroupVersion, apiResources []unversioned.APIResource) {
 | 
					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).
 | 
						ws.Route(ws.GET("/").To(resourceHandler).
 | 
				
			||||||
		Doc("get available resources").
 | 
							Doc("get available resources").
 | 
				
			||||||
		Operation("getAPIResources").
 | 
							Operation("getAPIResources").
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -260,6 +260,82 @@ func TestGetNodeAddresses(t *testing.T) {
 | 
				
			|||||||
	assert.Equal([]string{"127.0.0.2", "127.0.0.2"}, addrs)
 | 
						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) {
 | 
					func TestDiscoveryAtAPIS(t *testing.T) {
 | 
				
			||||||
	master, etcdserver, config, assert := newMaster(t)
 | 
						master, etcdserver, config, assert := newMaster(t)
 | 
				
			||||||
	defer etcdserver.Terminate(t)
 | 
						defer etcdserver.Terminate(t)
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user