mirror of
				https://github.com/optim-enterprises-bv/kubernetes.git
				synced 2025-11-03 19:58:17 +00:00 
			
		
		
		
	Merge pull request #43767 from deads2k/server-13-namer
Automatic merge from submit-queue remove go-restful from namer for rest handling Our RESTHandler code is currently tightly coupled to go-restful, but there's no reason for this coupling. It makes integrations that want API handling (decode, sanity check, admission, verb handling), but don't need the REST installer flow impractical. I know of two layers now: metrics and TPR. This starts the process of unwinding by switching the `ScopeNamer` (used for request identification and selflinks) to use the standard http library along with the `RequestInfo` we place in the context for authorization and any other interested layer. @kubernetes/sig-api-machinery-misc @smarterclayton @ncdc @sttts
This commit is contained in:
		@@ -26,6 +26,7 @@ import (
 | 
				
			|||||||
	"math/rand"
 | 
						"math/rand"
 | 
				
			||||||
	"net/http"
 | 
						"net/http"
 | 
				
			||||||
	"net/http/httptest"
 | 
						"net/http/httptest"
 | 
				
			||||||
 | 
						"net/http/httputil"
 | 
				
			||||||
	"net/url"
 | 
						"net/url"
 | 
				
			||||||
	"reflect"
 | 
						"reflect"
 | 
				
			||||||
	"strconv"
 | 
						"strconv"
 | 
				
			||||||
@@ -331,7 +332,17 @@ func handleInternal(storage map[string]rest.Storage, admissionControl admission.
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return &defaultAPIServer{mux, container}
 | 
						handler := genericapifilters.WithRequestInfo(mux, testRequestInfoResolver(), requestContextMapper)
 | 
				
			||||||
 | 
						handler = request.WithRequestContext(handler, requestContextMapper)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return &defaultAPIServer{handler, container}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func testRequestInfoResolver() *request.RequestInfoFactory {
 | 
				
			||||||
 | 
						return &request.RequestInfoFactory{
 | 
				
			||||||
 | 
							APIPrefixes:          sets.NewString("api", "apis"),
 | 
				
			||||||
 | 
							GrouplessAPIPrefixes: sets.NewString("api"),
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func TestSimpleSetupRight(t *testing.T) {
 | 
					func TestSimpleSetupRight(t *testing.T) {
 | 
				
			||||||
@@ -746,7 +757,7 @@ func TestNotFound(t *testing.T) {
 | 
				
			|||||||
		"groupless root DELETE with extra segment":    {"DELETE", "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/simpleroots/bar/baz", http.StatusNotFound},
 | 
							"groupless root DELETE with extra segment":    {"DELETE", "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/simpleroots/bar/baz", http.StatusNotFound},
 | 
				
			||||||
		"groupless root PUT without extra segment":    {"PUT", "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/simpleroots", http.StatusMethodNotAllowed},
 | 
							"groupless root PUT without extra segment":    {"PUT", "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/simpleroots", http.StatusMethodNotAllowed},
 | 
				
			||||||
		"groupless root PUT with extra segment":       {"PUT", "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/simpleroots/bar/baz", http.StatusNotFound},
 | 
							"groupless root PUT with extra segment":       {"PUT", "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/simpleroots/bar/baz", http.StatusNotFound},
 | 
				
			||||||
		"groupless root watch missing storage":        {"GET", "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/watch/", http.StatusNotFound},
 | 
							"groupless root watch missing storage":        {"GET", "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/watch/", http.StatusInternalServerError},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		"groupless namespaced PATCH method":                 {"PATCH", "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/namespaces/ns/simples", http.StatusMethodNotAllowed},
 | 
							"groupless namespaced PATCH method":                 {"PATCH", "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/namespaces/ns/simples", http.StatusMethodNotAllowed},
 | 
				
			||||||
		"groupless namespaced GET long prefix":              {"GET", "/" + grouplessPrefix + "/", http.StatusNotFound},
 | 
							"groupless namespaced GET long prefix":              {"GET", "/" + grouplessPrefix + "/", http.StatusNotFound},
 | 
				
			||||||
@@ -757,7 +768,7 @@ func TestNotFound(t *testing.T) {
 | 
				
			|||||||
		"groupless namespaced DELETE with extra segment":    {"DELETE", "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/namespaces/ns/simples/bar/baz", http.StatusNotFound},
 | 
							"groupless namespaced DELETE with extra segment":    {"DELETE", "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/namespaces/ns/simples/bar/baz", http.StatusNotFound},
 | 
				
			||||||
		"groupless namespaced PUT without extra segment":    {"PUT", "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/namespaces/ns/simples", http.StatusMethodNotAllowed},
 | 
							"groupless namespaced PUT without extra segment":    {"PUT", "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/namespaces/ns/simples", http.StatusMethodNotAllowed},
 | 
				
			||||||
		"groupless namespaced PUT with extra segment":       {"PUT", "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/namespaces/ns/simples/bar/baz", http.StatusNotFound},
 | 
							"groupless namespaced PUT with extra segment":       {"PUT", "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/namespaces/ns/simples/bar/baz", http.StatusNotFound},
 | 
				
			||||||
		"groupless namespaced watch missing storage":        {"GET", "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/watch/", http.StatusNotFound},
 | 
							"groupless namespaced watch missing storage":        {"GET", "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/watch/", http.StatusInternalServerError},
 | 
				
			||||||
		"groupless namespaced watch with bad method":        {"POST", "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/watch/namespaces/ns/simples/bar", http.StatusMethodNotAllowed},
 | 
							"groupless namespaced watch with bad method":        {"POST", "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/watch/namespaces/ns/simples/bar", http.StatusMethodNotAllowed},
 | 
				
			||||||
		"groupless namespaced watch param with bad method":  {"POST", "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/namespaces/ns/simples/bar?watch=true", http.StatusMethodNotAllowed},
 | 
							"groupless namespaced watch param with bad method":  {"POST", "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/namespaces/ns/simples/bar?watch=true", http.StatusMethodNotAllowed},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -777,7 +788,7 @@ func TestNotFound(t *testing.T) {
 | 
				
			|||||||
		"root DELETE with extra segment":    {"DELETE", "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/simpleroots/bar/baz", http.StatusNotFound},
 | 
							"root DELETE with extra segment":    {"DELETE", "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/simpleroots/bar/baz", http.StatusNotFound},
 | 
				
			||||||
		"root PUT without extra segment":    {"PUT", "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/simpleroots", http.StatusMethodNotAllowed},
 | 
							"root PUT without extra segment":    {"PUT", "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/simpleroots", http.StatusMethodNotAllowed},
 | 
				
			||||||
		"root PUT with extra segment":       {"PUT", "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/simpleroots/bar/baz", http.StatusNotFound},
 | 
							"root PUT with extra segment":       {"PUT", "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/simpleroots/bar/baz", http.StatusNotFound},
 | 
				
			||||||
		"root watch missing storage":        {"GET", "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/watch/", http.StatusNotFound},
 | 
							"root watch missing storage":        {"GET", "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/watch/", http.StatusInternalServerError},
 | 
				
			||||||
		// TODO: JTL: "root watch with bad method":        {"POST", "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/watch/simpleroot/bar", http.StatusMethodNotAllowed},
 | 
							// TODO: JTL: "root watch with bad method":        {"POST", "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/watch/simpleroot/bar", http.StatusMethodNotAllowed},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		"namespaced PATCH method":                 {"PATCH", "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/namespaces/ns/simples", http.StatusMethodNotAllowed},
 | 
							"namespaced PATCH method":                 {"PATCH", "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/namespaces/ns/simples", http.StatusMethodNotAllowed},
 | 
				
			||||||
@@ -789,7 +800,7 @@ func TestNotFound(t *testing.T) {
 | 
				
			|||||||
		"namespaced DELETE with extra segment":    {"DELETE", "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/namespaces/ns/simples/bar/baz", http.StatusNotFound},
 | 
							"namespaced DELETE with extra segment":    {"DELETE", "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/namespaces/ns/simples/bar/baz", http.StatusNotFound},
 | 
				
			||||||
		"namespaced PUT without extra segment":    {"PUT", "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/namespaces/ns/simples", http.StatusMethodNotAllowed},
 | 
							"namespaced PUT without extra segment":    {"PUT", "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/namespaces/ns/simples", http.StatusMethodNotAllowed},
 | 
				
			||||||
		"namespaced PUT with extra segment":       {"PUT", "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/namespaces/ns/simples/bar/baz", http.StatusNotFound},
 | 
							"namespaced PUT with extra segment":       {"PUT", "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/namespaces/ns/simples/bar/baz", http.StatusNotFound},
 | 
				
			||||||
		"namespaced watch missing storage":        {"GET", "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/watch/", http.StatusNotFound},
 | 
							"namespaced watch missing storage":        {"GET", "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/watch/", http.StatusInternalServerError},
 | 
				
			||||||
		"namespaced watch with bad method":        {"POST", "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/watch/namespaces/ns/simples/bar", http.StatusMethodNotAllowed},
 | 
							"namespaced watch with bad method":        {"POST", "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/watch/namespaces/ns/simples/bar", http.StatusMethodNotAllowed},
 | 
				
			||||||
		"namespaced watch param with bad method":  {"POST", "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/namespaces/ns/simples/bar?watch=true", http.StatusMethodNotAllowed},
 | 
							"namespaced watch param with bad method":  {"POST", "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/namespaces/ns/simples/bar?watch=true", http.StatusMethodNotAllowed},
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -1081,7 +1092,7 @@ func TestList(t *testing.T) {
 | 
				
			|||||||
		if !simpleStorage.namespacePresent {
 | 
							if !simpleStorage.namespacePresent {
 | 
				
			||||||
			t.Errorf("%d: namespace not set", i)
 | 
								t.Errorf("%d: namespace not set", i)
 | 
				
			||||||
		} else if simpleStorage.actualNamespace != testCase.namespace {
 | 
							} else if simpleStorage.actualNamespace != testCase.namespace {
 | 
				
			||||||
			t.Errorf("%d: unexpected resource namespace: %s", i, simpleStorage.actualNamespace)
 | 
								t.Errorf("%d: %q unexpected resource namespace: %s", i, testCase.url, simpleStorage.actualNamespace)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		if simpleStorage.requestedLabelSelector == nil || simpleStorage.requestedLabelSelector.String() != testCase.label {
 | 
							if simpleStorage.requestedLabelSelector == nil || simpleStorage.requestedLabelSelector.String() != testCase.label {
 | 
				
			||||||
			t.Errorf("%d: unexpected label selector: %v", i, simpleStorage.requestedLabelSelector)
 | 
								t.Errorf("%d: unexpected label selector: %v", i, simpleStorage.requestedLabelSelector)
 | 
				
			||||||
@@ -1169,6 +1180,7 @@ func TestNonEmptyList(t *testing.T) {
 | 
				
			|||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		t.Fatalf("unexpected error: %v", err)
 | 
							t.Fatalf("unexpected error: %v", err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						t.Log(body)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if len(listOut.Items) != 1 {
 | 
						if len(listOut.Items) != 1 {
 | 
				
			||||||
		t.Errorf("Unexpected response: %#v", listOut)
 | 
							t.Errorf("Unexpected response: %#v", listOut)
 | 
				
			||||||
@@ -2220,10 +2232,12 @@ func TestPatch(t *testing.T) {
 | 
				
			|||||||
	client := http.Client{}
 | 
						client := http.Client{}
 | 
				
			||||||
	request, err := http.NewRequest("PATCH", server.URL+"/"+prefix+"/"+testGroupVersion.Group+"/"+testGroupVersion.Version+"/namespaces/default/simple/"+ID, bytes.NewReader([]byte(`{"labels":{"foo":"bar"}}`)))
 | 
						request, err := http.NewRequest("PATCH", server.URL+"/"+prefix+"/"+testGroupVersion.Group+"/"+testGroupVersion.Version+"/namespaces/default/simple/"+ID, bytes.NewReader([]byte(`{"labels":{"foo":"bar"}}`)))
 | 
				
			||||||
	request.Header.Set("Content-Type", "application/merge-patch+json; charset=UTF-8")
 | 
						request.Header.Set("Content-Type", "application/merge-patch+json; charset=UTF-8")
 | 
				
			||||||
	_, err = client.Do(request)
 | 
						response, err := client.Do(request)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		t.Errorf("unexpected error: %v", err)
 | 
							t.Errorf("unexpected error: %v", err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						dump, _ := httputil.DumpResponse(response, true)
 | 
				
			||||||
 | 
						t.Log(string(dump))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if simpleStorage.updated == nil || simpleStorage.updated.Labels["foo"] != "bar" {
 | 
						if simpleStorage.updated == nil || simpleStorage.updated.Labels["foo"] != "bar" {
 | 
				
			||||||
		t.Errorf("Unexpected update value %#v, expected %#v.", simpleStorage.updated, item)
 | 
							t.Errorf("Unexpected update value %#v, expected %#v.", simpleStorage.updated, item)
 | 
				
			||||||
@@ -2292,10 +2306,12 @@ func TestUpdate(t *testing.T) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	client := http.Client{}
 | 
						client := http.Client{}
 | 
				
			||||||
	request, err := http.NewRequest("PUT", server.URL+"/"+prefix+"/"+testGroupVersion.Group+"/"+testGroupVersion.Version+"/namespaces/default/simple/"+ID, bytes.NewReader(body))
 | 
						request, err := http.NewRequest("PUT", server.URL+"/"+prefix+"/"+testGroupVersion.Group+"/"+testGroupVersion.Version+"/namespaces/default/simple/"+ID, bytes.NewReader(body))
 | 
				
			||||||
	_, err = client.Do(request)
 | 
						response, err := client.Do(request)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		t.Errorf("unexpected error: %v", err)
 | 
							t.Errorf("unexpected error: %v", err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						dump, _ := httputil.DumpResponse(response, true)
 | 
				
			||||||
 | 
						t.Log(string(dump))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if simpleStorage.updated == nil || simpleStorage.updated.Name != item.Name {
 | 
						if simpleStorage.updated == nil || simpleStorage.updated.Name != item.Name {
 | 
				
			||||||
		t.Errorf("Unexpected update value %#v, expected %#v.", simpleStorage.updated, item)
 | 
							t.Errorf("Unexpected update value %#v, expected %#v.", simpleStorage.updated, item)
 | 
				
			||||||
@@ -2333,6 +2349,9 @@ func TestUpdateInvokesAdmissionControl(t *testing.T) {
 | 
				
			|||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		t.Errorf("unexpected error: %v", err)
 | 
							t.Errorf("unexpected error: %v", err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						dump, _ := httputil.DumpResponse(response, true)
 | 
				
			||||||
 | 
						t.Log(string(dump))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if response.StatusCode != http.StatusForbidden {
 | 
						if response.StatusCode != http.StatusForbidden {
 | 
				
			||||||
		t.Errorf("Unexpected response %#v", response)
 | 
							t.Errorf("Unexpected response %#v", response)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -2343,7 +2362,7 @@ func TestUpdateRequiresMatchingName(t *testing.T) {
 | 
				
			|||||||
	simpleStorage := SimpleRESTStorage{}
 | 
						simpleStorage := SimpleRESTStorage{}
 | 
				
			||||||
	ID := "id"
 | 
						ID := "id"
 | 
				
			||||||
	storage["simple"] = &simpleStorage
 | 
						storage["simple"] = &simpleStorage
 | 
				
			||||||
	handler := handleDeny(storage)
 | 
						handler := handle(storage)
 | 
				
			||||||
	server := httptest.NewServer(handler)
 | 
						server := httptest.NewServer(handler)
 | 
				
			||||||
	defer server.Close()
 | 
						defer server.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -2363,6 +2382,8 @@ func TestUpdateRequiresMatchingName(t *testing.T) {
 | 
				
			|||||||
		t.Errorf("unexpected error: %v", err)
 | 
							t.Errorf("unexpected error: %v", err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if response.StatusCode != http.StatusBadRequest {
 | 
						if response.StatusCode != http.StatusBadRequest {
 | 
				
			||||||
 | 
							dump, _ := httputil.DumpResponse(response, true)
 | 
				
			||||||
 | 
							t.Log(string(dump))
 | 
				
			||||||
		t.Errorf("Unexpected response %#v", response)
 | 
							t.Errorf("Unexpected response %#v", response)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -2394,13 +2415,16 @@ func TestUpdateAllowsMissingNamespace(t *testing.T) {
 | 
				
			|||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		t.Errorf("unexpected error: %v", err)
 | 
							t.Errorf("unexpected error: %v", err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						dump, _ := httputil.DumpResponse(response, true)
 | 
				
			||||||
 | 
						t.Log(string(dump))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if response.StatusCode != http.StatusOK {
 | 
						if response.StatusCode != http.StatusOK {
 | 
				
			||||||
		t.Errorf("Unexpected response %#v", response)
 | 
							t.Errorf("Unexpected response %#v", response)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// when the object name and namespace can't be retrieved, skip name checking
 | 
					// when the object name and namespace can't be retrieved, don't update.  It isn't safe.
 | 
				
			||||||
func TestUpdateAllowsMismatchedNamespaceOnError(t *testing.T) {
 | 
					func TestUpdateDisallowsMismatchedNamespaceOnError(t *testing.T) {
 | 
				
			||||||
	storage := map[string]rest.Storage{}
 | 
						storage := map[string]rest.Storage{}
 | 
				
			||||||
	simpleStorage := SimpleRESTStorage{}
 | 
						simpleStorage := SimpleRESTStorage{}
 | 
				
			||||||
	ID := "id"
 | 
						ID := "id"
 | 
				
			||||||
@@ -2428,13 +2452,15 @@ func TestUpdateAllowsMismatchedNamespaceOnError(t *testing.T) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	client := http.Client{}
 | 
						client := http.Client{}
 | 
				
			||||||
	request, err := http.NewRequest("PUT", server.URL+"/"+prefix+"/"+testGroupVersion.Group+"/"+testGroupVersion.Version+"/namespaces/default/simple/"+ID, bytes.NewReader(body))
 | 
						request, err := http.NewRequest("PUT", server.URL+"/"+prefix+"/"+testGroupVersion.Group+"/"+testGroupVersion.Version+"/namespaces/default/simple/"+ID, bytes.NewReader(body))
 | 
				
			||||||
	_, err = client.Do(request)
 | 
						response, err := client.Do(request)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		t.Errorf("unexpected error: %v", err)
 | 
							t.Errorf("unexpected error: %v", err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						dump, _ := httputil.DumpResponse(response, true)
 | 
				
			||||||
 | 
						t.Log(string(dump))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if simpleStorage.updated == nil || simpleStorage.updated.Name != item.Name {
 | 
						if simpleStorage.updated != nil {
 | 
				
			||||||
		t.Errorf("Unexpected update value %#v, expected %#v.", simpleStorage.updated, item)
 | 
							t.Errorf("Unexpected update value %#v.", simpleStorage.updated)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if selfLinker.called {
 | 
						if selfLinker.called {
 | 
				
			||||||
		t.Errorf("self link ignored")
 | 
							t.Errorf("self link ignored")
 | 
				
			||||||
@@ -2605,14 +2631,17 @@ func TestUpdateREST(t *testing.T) {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	testREST := func(t *testing.T, container *restful.Container, barCode int) {
 | 
						testREST := func(t *testing.T, container *restful.Container, barCode int) {
 | 
				
			||||||
 | 
							handler := genericapifilters.WithRequestInfo(container, newTestRequestInfoResolver(), requestContextMapper)
 | 
				
			||||||
 | 
							handler = request.WithRequestContext(handler, requestContextMapper)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		w := httptest.NewRecorder()
 | 
							w := httptest.NewRecorder()
 | 
				
			||||||
		container.ServeHTTP(w, &http.Request{Method: "GET", URL: &url.URL{Path: "/" + prefix + "/" + newGroupVersion.Group + "/" + newGroupVersion.Version + "/namespaces/test/foo/test"}})
 | 
							handler.ServeHTTP(w, &http.Request{Method: "GET", URL: &url.URL{Path: "/" + prefix + "/" + newGroupVersion.Group + "/" + newGroupVersion.Version + "/namespaces/test/foo/test"}})
 | 
				
			||||||
		if w.Code != http.StatusOK {
 | 
							if w.Code != http.StatusOK {
 | 
				
			||||||
			t.Fatalf("expected OK: %#v", w)
 | 
								t.Fatalf("expected OK: %#v", w)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		w = httptest.NewRecorder()
 | 
							w = httptest.NewRecorder()
 | 
				
			||||||
		container.ServeHTTP(w, &http.Request{Method: "GET", URL: &url.URL{Path: "/" + prefix + "/" + newGroupVersion.Group + "/" + newGroupVersion.Version + "/namespaces/test/bar/test"}})
 | 
							handler.ServeHTTP(w, &http.Request{Method: "GET", URL: &url.URL{Path: "/" + prefix + "/" + newGroupVersion.Group + "/" + newGroupVersion.Version + "/namespaces/test/bar/test"}})
 | 
				
			||||||
		if w.Code != barCode {
 | 
							if w.Code != barCode {
 | 
				
			||||||
			t.Errorf("expected response code %d for GET to bar but received %d", barCode, w.Code)
 | 
								t.Errorf("expected response code %d for GET to bar but received %d", barCode, w.Code)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
@@ -2716,16 +2745,19 @@ func TestParentResourceIsRequired(t *testing.T) {
 | 
				
			|||||||
		t.Fatal(err)
 | 
							t.Fatal(err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						handler := genericapifilters.WithRequestInfo(container, newTestRequestInfoResolver(), requestContextMapper)
 | 
				
			||||||
 | 
						handler = request.WithRequestContext(handler, requestContextMapper)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// resource is NOT registered in the root scope
 | 
						// resource is NOT registered in the root scope
 | 
				
			||||||
	w := httptest.NewRecorder()
 | 
						w := httptest.NewRecorder()
 | 
				
			||||||
	container.ServeHTTP(w, &http.Request{Method: "GET", URL: &url.URL{Path: "/" + prefix + "/simple/test/sub"}})
 | 
						handler.ServeHTTP(w, &http.Request{Method: "GET", URL: &url.URL{Path: "/" + prefix + "/simple/test/sub"}})
 | 
				
			||||||
	if w.Code != http.StatusNotFound {
 | 
						if w.Code != http.StatusNotFound {
 | 
				
			||||||
		t.Errorf("expected not found: %#v", w)
 | 
							t.Errorf("expected not found: %#v", w)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// resource is registered in the namespace scope
 | 
						// resource is registered in the namespace scope
 | 
				
			||||||
	w = httptest.NewRecorder()
 | 
						w = httptest.NewRecorder()
 | 
				
			||||||
	container.ServeHTTP(w, &http.Request{Method: "GET", URL: &url.URL{Path: "/" + prefix + "/" + newGroupVersion.Group + "/" + newGroupVersion.Version + "/namespaces/test/simple/test/sub"}})
 | 
						handler.ServeHTTP(w, &http.Request{Method: "GET", URL: &url.URL{Path: "/" + prefix + "/" + newGroupVersion.Group + "/" + newGroupVersion.Version + "/namespaces/test/simple/test/sub"}})
 | 
				
			||||||
	if w.Code != http.StatusOK {
 | 
						if w.Code != http.StatusOK {
 | 
				
			||||||
		t.Fatalf("expected OK: %#v", w)
 | 
							t.Fatalf("expected OK: %#v", w)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										147
									
								
								staging/src/k8s.io/apiserver/pkg/endpoints/handlers/namer.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										147
									
								
								staging/src/k8s.io/apiserver/pkg/endpoints/handlers/namer.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,147 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					Copyright 2017 The Kubernetes Authors.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Licensed under the Apache License, Version 2.0 (the "License");
 | 
				
			||||||
 | 
					you may not use this file except in compliance with the License.
 | 
				
			||||||
 | 
					You may obtain a copy of the License at
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    http://www.apache.org/licenses/LICENSE-2.0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Unless required by applicable law or agreed to in writing, software
 | 
				
			||||||
 | 
					distributed under the License is distributed on an "AS IS" BASIS,
 | 
				
			||||||
 | 
					WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
				
			||||||
 | 
					See the License for the specific language governing permissions and
 | 
				
			||||||
 | 
					limitations under the License.
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package handlers
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"net/http"
 | 
				
			||||||
 | 
						"net/url"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"k8s.io/apimachinery/pkg/api/errors"
 | 
				
			||||||
 | 
						"k8s.io/apimachinery/pkg/runtime"
 | 
				
			||||||
 | 
						"k8s.io/apiserver/pkg/endpoints/request"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ContextFunc returns a Context given a request - a context must be returned
 | 
				
			||||||
 | 
					type ContextFunc func(req *http.Request) request.Context
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ScopeNamer handles accessing names from requests and objects
 | 
				
			||||||
 | 
					type ScopeNamer interface {
 | 
				
			||||||
 | 
						// Namespace returns the appropriate namespace value from the request (may be empty) or an
 | 
				
			||||||
 | 
						// error.
 | 
				
			||||||
 | 
						Namespace(req *http.Request) (namespace string, err error)
 | 
				
			||||||
 | 
						// Name returns the name from the request, and an optional namespace value if this is a namespace
 | 
				
			||||||
 | 
						// scoped call. An error is returned if the name is not available.
 | 
				
			||||||
 | 
						Name(req *http.Request) (namespace, name string, err error)
 | 
				
			||||||
 | 
						// ObjectName returns the namespace and name from an object if they exist, or an error if the object
 | 
				
			||||||
 | 
						// does not support names.
 | 
				
			||||||
 | 
						ObjectName(obj runtime.Object) (namespace, name string, err error)
 | 
				
			||||||
 | 
						// SetSelfLink sets the provided URL onto the object. The method should return nil if the object
 | 
				
			||||||
 | 
						// does not support selfLinks.
 | 
				
			||||||
 | 
						SetSelfLink(obj runtime.Object, url string) error
 | 
				
			||||||
 | 
						// GenerateLink creates an encoded URI for a given runtime object that represents the canonical path
 | 
				
			||||||
 | 
						// and query.
 | 
				
			||||||
 | 
						GenerateLink(req *http.Request, obj runtime.Object) (uri string, err error)
 | 
				
			||||||
 | 
						// GenerateLink creates an encoded URI for a list that represents the canonical path and query.
 | 
				
			||||||
 | 
						GenerateListLink(req *http.Request) (uri string, err error)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type ContextBasedNaming struct {
 | 
				
			||||||
 | 
						GetContext    ContextFunc
 | 
				
			||||||
 | 
						SelfLinker    runtime.SelfLinker
 | 
				
			||||||
 | 
						ClusterScoped bool
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						SelfLinkPathPrefix string
 | 
				
			||||||
 | 
						SelfLinkPathSuffix string
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ContextBasedNaming implements ScopeNamer
 | 
				
			||||||
 | 
					var _ ScopeNamer = ContextBasedNaming{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (n ContextBasedNaming) SetSelfLink(obj runtime.Object, url string) error {
 | 
				
			||||||
 | 
						return n.SelfLinker.SetSelfLink(obj, url)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (n ContextBasedNaming) Namespace(req *http.Request) (namespace string, err error) {
 | 
				
			||||||
 | 
						requestInfo, ok := request.RequestInfoFrom(n.GetContext(req))
 | 
				
			||||||
 | 
						if !ok {
 | 
				
			||||||
 | 
							return "", fmt.Errorf("missing requestInfo")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return requestInfo.Namespace, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (n ContextBasedNaming) Name(req *http.Request) (namespace, name string, err error) {
 | 
				
			||||||
 | 
						requestInfo, ok := request.RequestInfoFrom(n.GetContext(req))
 | 
				
			||||||
 | 
						if !ok {
 | 
				
			||||||
 | 
							return "", "", fmt.Errorf("missing requestInfo")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						ns, err := n.Namespace(req)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return "", "", err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if len(requestInfo.Name) == 0 {
 | 
				
			||||||
 | 
							return "", "", errEmptyName
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return ns, requestInfo.Name, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (n ContextBasedNaming) GenerateLink(req *http.Request, obj runtime.Object) (uri string, err error) {
 | 
				
			||||||
 | 
						namespace, name, err := n.ObjectName(obj)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return "", err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						requestInfo, ok := request.RequestInfoFrom(n.GetContext(req))
 | 
				
			||||||
 | 
						if !ok {
 | 
				
			||||||
 | 
							return "", fmt.Errorf("missing requestInfo")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if len(namespace) == 0 && len(name) == 0 {
 | 
				
			||||||
 | 
							if len(requestInfo.Name) == 0 {
 | 
				
			||||||
 | 
								return "", errEmptyName
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							namespace = requestInfo.Namespace
 | 
				
			||||||
 | 
							name = requestInfo.Name
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if n.ClusterScoped {
 | 
				
			||||||
 | 
							return n.SelfLinkPathPrefix + url.QueryEscape(name) + n.SelfLinkPathSuffix, nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return n.SelfLinkPathPrefix +
 | 
				
			||||||
 | 
								url.QueryEscape(namespace) +
 | 
				
			||||||
 | 
								"/" + url.QueryEscape(requestInfo.Resource) + "/" +
 | 
				
			||||||
 | 
								url.QueryEscape(name) +
 | 
				
			||||||
 | 
								n.SelfLinkPathSuffix,
 | 
				
			||||||
 | 
							nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (n ContextBasedNaming) GenerateListLink(req *http.Request) (uri string, err error) {
 | 
				
			||||||
 | 
						if len(req.URL.RawPath) > 0 {
 | 
				
			||||||
 | 
							return req.URL.RawPath, nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return req.URL.EscapedPath(), nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (n ContextBasedNaming) ObjectName(obj runtime.Object) (namespace, name string, err error) {
 | 
				
			||||||
 | 
						name, err = n.SelfLinker.Name(obj)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return "", "", err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if len(name) == 0 {
 | 
				
			||||||
 | 
							return "", "", errEmptyName
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						namespace, err = n.SelfLinker.Namespace(obj)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return "", "", err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return namespace, name, err
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// errEmptyName is returned when API requests do not fill the name section of the path.
 | 
				
			||||||
 | 
					var errEmptyName = errors.NewBadRequest("name must be provided")
 | 
				
			||||||
@@ -50,30 +50,6 @@ import (
 | 
				
			|||||||
	utiltrace "k8s.io/apiserver/pkg/util/trace"
 | 
						utiltrace "k8s.io/apiserver/pkg/util/trace"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// ContextFunc returns a Context given a request - a context must be returned
 | 
					 | 
				
			||||||
type ContextFunc func(req *restful.Request) request.Context
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// ScopeNamer handles accessing names from requests and objects
 | 
					 | 
				
			||||||
type ScopeNamer interface {
 | 
					 | 
				
			||||||
	// Namespace returns the appropriate namespace value from the request (may be empty) or an
 | 
					 | 
				
			||||||
	// error.
 | 
					 | 
				
			||||||
	Namespace(req *restful.Request) (namespace string, err error)
 | 
					 | 
				
			||||||
	// Name returns the name from the request, and an optional namespace value if this is a namespace
 | 
					 | 
				
			||||||
	// scoped call. An error is returned if the name is not available.
 | 
					 | 
				
			||||||
	Name(req *restful.Request) (namespace, name string, err error)
 | 
					 | 
				
			||||||
	// ObjectName returns the namespace and name from an object if they exist, or an error if the object
 | 
					 | 
				
			||||||
	// does not support names.
 | 
					 | 
				
			||||||
	ObjectName(obj runtime.Object) (namespace, name string, err error)
 | 
					 | 
				
			||||||
	// SetSelfLink sets the provided URL onto the object. The method should return nil if the object
 | 
					 | 
				
			||||||
	// does not support selfLinks.
 | 
					 | 
				
			||||||
	SetSelfLink(obj runtime.Object, url string) error
 | 
					 | 
				
			||||||
	// GenerateLink creates an encoded URI for a given runtime object that represents the canonical path
 | 
					 | 
				
			||||||
	// and query.
 | 
					 | 
				
			||||||
	GenerateLink(req *restful.Request, obj runtime.Object) (uri string, err error)
 | 
					 | 
				
			||||||
	// GenerateLink creates an encoded URI for a list that represents the canonical path and query.
 | 
					 | 
				
			||||||
	GenerateListLink(req *restful.Request) (uri string, err error)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// RequestScope encapsulates common fields across all RESTful handler methods.
 | 
					// RequestScope encapsulates common fields across all RESTful handler methods.
 | 
				
			||||||
type RequestScope struct {
 | 
					type RequestScope struct {
 | 
				
			||||||
	Namer ScopeNamer
 | 
						Namer ScopeNamer
 | 
				
			||||||
@@ -112,12 +88,12 @@ const MaxRetryWhenPatchConflicts = 5
 | 
				
			|||||||
func getResourceHandler(scope RequestScope, getter getterFunc) restful.RouteFunction {
 | 
					func getResourceHandler(scope RequestScope, getter getterFunc) restful.RouteFunction {
 | 
				
			||||||
	return func(req *restful.Request, res *restful.Response) {
 | 
						return func(req *restful.Request, res *restful.Response) {
 | 
				
			||||||
		w := res.ResponseWriter
 | 
							w := res.ResponseWriter
 | 
				
			||||||
		namespace, name, err := scope.Namer.Name(req)
 | 
							namespace, name, err := scope.Namer.Name(req.Request)
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			scope.err(err, res.ResponseWriter, req.Request)
 | 
								scope.err(err, res.ResponseWriter, req.Request)
 | 
				
			||||||
			return
 | 
								return
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		ctx := scope.ContextFunc(req)
 | 
							ctx := scope.ContextFunc(req.Request)
 | 
				
			||||||
		ctx = request.WithNamespace(ctx, namespace)
 | 
							ctx = request.WithNamespace(ctx, namespace)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		result, err := getter(ctx, name, req)
 | 
							result, err := getter(ctx, name, req)
 | 
				
			||||||
@@ -196,12 +172,12 @@ func getRequestOptions(req *restful.Request, scope RequestScope, into runtime.Ob
 | 
				
			|||||||
func ConnectResource(connecter rest.Connecter, scope RequestScope, admit admission.Interface, restPath string) restful.RouteFunction {
 | 
					func ConnectResource(connecter rest.Connecter, scope RequestScope, admit admission.Interface, restPath string) restful.RouteFunction {
 | 
				
			||||||
	return func(req *restful.Request, res *restful.Response) {
 | 
						return func(req *restful.Request, res *restful.Response) {
 | 
				
			||||||
		w := res.ResponseWriter
 | 
							w := res.ResponseWriter
 | 
				
			||||||
		namespace, name, err := scope.Namer.Name(req)
 | 
							namespace, name, err := scope.Namer.Name(req.Request)
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			scope.err(err, res.ResponseWriter, req.Request)
 | 
								scope.err(err, res.ResponseWriter, req.Request)
 | 
				
			||||||
			return
 | 
								return
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		ctx := scope.ContextFunc(req)
 | 
							ctx := scope.ContextFunc(req.Request)
 | 
				
			||||||
		ctx = request.WithNamespace(ctx, namespace)
 | 
							ctx = request.WithNamespace(ctx, namespace)
 | 
				
			||||||
		opts, subpath, subpathKey := connecter.NewConnectOptions()
 | 
							opts, subpath, subpathKey := connecter.NewConnectOptions()
 | 
				
			||||||
		if err := getRequestOptions(req, scope, opts, subpath, subpathKey); err != nil {
 | 
							if err := getRequestOptions(req, scope, opts, subpath, subpathKey); err != nil {
 | 
				
			||||||
@@ -254,7 +230,7 @@ func ListResource(r rest.Lister, rw rest.Watcher, scope RequestScope, forceWatch
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
		w := res.ResponseWriter
 | 
							w := res.ResponseWriter
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		namespace, err := scope.Namer.Namespace(req)
 | 
							namespace, err := scope.Namer.Namespace(req.Request)
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			scope.err(err, res.ResponseWriter, req.Request)
 | 
								scope.err(err, res.ResponseWriter, req.Request)
 | 
				
			||||||
			return
 | 
								return
 | 
				
			||||||
@@ -263,12 +239,12 @@ func ListResource(r rest.Lister, rw rest.Watcher, scope RequestScope, forceWatch
 | 
				
			|||||||
		// Watches for single objects are routed to this function.
 | 
							// Watches for single objects are routed to this function.
 | 
				
			||||||
		// Treat a /name parameter the same as a field selector entry.
 | 
							// Treat a /name parameter the same as a field selector entry.
 | 
				
			||||||
		hasName := true
 | 
							hasName := true
 | 
				
			||||||
		_, name, err := scope.Namer.Name(req)
 | 
							_, name, err := scope.Namer.Name(req.Request)
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			hasName = false
 | 
								hasName = false
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		ctx := scope.ContextFunc(req)
 | 
							ctx := scope.ContextFunc(req.Request)
 | 
				
			||||||
		ctx = request.WithNamespace(ctx, namespace)
 | 
							ctx = request.WithNamespace(ctx, namespace)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		opts := metainternalversion.ListOptions{}
 | 
							opts := metainternalversion.ListOptions{}
 | 
				
			||||||
@@ -371,16 +347,16 @@ func createHandler(r rest.NamedCreater, scope RequestScope, typer runtime.Object
 | 
				
			|||||||
			err             error
 | 
								err             error
 | 
				
			||||||
		)
 | 
							)
 | 
				
			||||||
		if includeName {
 | 
							if includeName {
 | 
				
			||||||
			namespace, name, err = scope.Namer.Name(req)
 | 
								namespace, name, err = scope.Namer.Name(req.Request)
 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
			namespace, err = scope.Namer.Namespace(req)
 | 
								namespace, err = scope.Namer.Namespace(req.Request)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			scope.err(err, res.ResponseWriter, req.Request)
 | 
								scope.err(err, res.ResponseWriter, req.Request)
 | 
				
			||||||
			return
 | 
								return
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		ctx := scope.ContextFunc(req)
 | 
							ctx := scope.ContextFunc(req.Request)
 | 
				
			||||||
		ctx = request.WithNamespace(ctx, namespace)
 | 
							ctx = request.WithNamespace(ctx, namespace)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		gv := scope.Kind.GroupVersion()
 | 
							gv := scope.Kind.GroupVersion()
 | 
				
			||||||
@@ -476,13 +452,13 @@ func PatchResource(r rest.Patcher, scope RequestScope, admit admission.Interface
 | 
				
			|||||||
		// api_installer)
 | 
							// api_installer)
 | 
				
			||||||
		timeout := parseTimeout(req.Request.URL.Query().Get("timeout"))
 | 
							timeout := parseTimeout(req.Request.URL.Query().Get("timeout"))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		namespace, name, err := scope.Namer.Name(req)
 | 
							namespace, name, err := scope.Namer.Name(req.Request)
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			scope.err(err, res.ResponseWriter, req.Request)
 | 
								scope.err(err, res.ResponseWriter, req.Request)
 | 
				
			||||||
			return
 | 
								return
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		ctx := scope.ContextFunc(req)
 | 
							ctx := scope.ContextFunc(req.Request)
 | 
				
			||||||
		ctx = request.WithNamespace(ctx, namespace)
 | 
							ctx = request.WithNamespace(ctx, namespace)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		versionedObj, err := converter.ConvertToVersion(r.New(), scope.Kind.GroupVersion())
 | 
							versionedObj, err := converter.ConvertToVersion(r.New(), scope.Kind.GroupVersion())
 | 
				
			||||||
@@ -790,12 +766,12 @@ func UpdateResource(r rest.Updater, scope RequestScope, typer runtime.ObjectType
 | 
				
			|||||||
		// TODO: we either want to remove timeout or document it (if we document, move timeout out of this function and declare it in api_installer)
 | 
							// TODO: we either want to remove timeout or document it (if we document, move timeout out of this function and declare it in api_installer)
 | 
				
			||||||
		timeout := parseTimeout(req.Request.URL.Query().Get("timeout"))
 | 
							timeout := parseTimeout(req.Request.URL.Query().Get("timeout"))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		namespace, name, err := scope.Namer.Name(req)
 | 
							namespace, name, err := scope.Namer.Name(req.Request)
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			scope.err(err, res.ResponseWriter, req.Request)
 | 
								scope.err(err, res.ResponseWriter, req.Request)
 | 
				
			||||||
			return
 | 
								return
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		ctx := scope.ContextFunc(req)
 | 
							ctx := scope.ContextFunc(req.Request)
 | 
				
			||||||
		ctx = request.WithNamespace(ctx, namespace)
 | 
							ctx = request.WithNamespace(ctx, namespace)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		body, err := readBody(req.Request)
 | 
							body, err := readBody(req.Request)
 | 
				
			||||||
@@ -877,12 +853,12 @@ func DeleteResource(r rest.GracefulDeleter, allowsOptions bool, scope RequestSco
 | 
				
			|||||||
		// TODO: we either want to remove timeout or document it (if we document, move timeout out of this function and declare it in api_installer)
 | 
							// TODO: we either want to remove timeout or document it (if we document, move timeout out of this function and declare it in api_installer)
 | 
				
			||||||
		timeout := parseTimeout(req.Request.URL.Query().Get("timeout"))
 | 
							timeout := parseTimeout(req.Request.URL.Query().Get("timeout"))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		namespace, name, err := scope.Namer.Name(req)
 | 
							namespace, name, err := scope.Namer.Name(req.Request)
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			scope.err(err, res.ResponseWriter, req.Request)
 | 
								scope.err(err, res.ResponseWriter, req.Request)
 | 
				
			||||||
			return
 | 
								return
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		ctx := scope.ContextFunc(req)
 | 
							ctx := scope.ContextFunc(req.Request)
 | 
				
			||||||
		ctx = request.WithNamespace(ctx, namespace)
 | 
							ctx = request.WithNamespace(ctx, namespace)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		options := &metav1.DeleteOptions{}
 | 
							options := &metav1.DeleteOptions{}
 | 
				
			||||||
@@ -985,13 +961,13 @@ func DeleteCollection(r rest.CollectionDeleter, checkBody bool, scope RequestSco
 | 
				
			|||||||
		// TODO: we either want to remove timeout or document it (if we document, move timeout out of this function and declare it in api_installer)
 | 
							// TODO: we either want to remove timeout or document it (if we document, move timeout out of this function and declare it in api_installer)
 | 
				
			||||||
		timeout := parseTimeout(req.Request.URL.Query().Get("timeout"))
 | 
							timeout := parseTimeout(req.Request.URL.Query().Get("timeout"))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		namespace, err := scope.Namer.Namespace(req)
 | 
							namespace, err := scope.Namer.Namespace(req.Request)
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			scope.err(err, res.ResponseWriter, req.Request)
 | 
								scope.err(err, res.ResponseWriter, req.Request)
 | 
				
			||||||
			return
 | 
								return
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		ctx := scope.ContextFunc(req)
 | 
							ctx := scope.ContextFunc(req.Request)
 | 
				
			||||||
		ctx = request.WithNamespace(ctx, namespace)
 | 
							ctx = request.WithNamespace(ctx, namespace)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if admit != nil && admit.Handles(admission.Delete) {
 | 
							if admit != nil && admit.Handles(admission.Delete) {
 | 
				
			||||||
@@ -1139,7 +1115,7 @@ func transformDecodeError(typer runtime.ObjectTyper, baseErr error, into runtime
 | 
				
			|||||||
// plus the path and query generated by the provided linkFunc
 | 
					// plus the path and query generated by the provided linkFunc
 | 
				
			||||||
func setSelfLink(obj runtime.Object, req *restful.Request, namer ScopeNamer) error {
 | 
					func setSelfLink(obj runtime.Object, req *restful.Request, namer ScopeNamer) error {
 | 
				
			||||||
	// TODO: SelfLink generation should return a full URL?
 | 
						// TODO: SelfLink generation should return a full URL?
 | 
				
			||||||
	uri, err := namer.GenerateLink(req, obj)
 | 
						uri, err := namer.GenerateLink(req.Request, obj)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil
 | 
							return nil
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -1163,21 +1139,22 @@ func hasUID(obj runtime.Object) (bool, error) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// checkName checks the provided name against the request
 | 
					// checkName checks the provided name against the request
 | 
				
			||||||
func checkName(obj runtime.Object, name, namespace string, namer ScopeNamer) error {
 | 
					func checkName(obj runtime.Object, name, namespace string, namer ScopeNamer) error {
 | 
				
			||||||
	if objNamespace, objName, err := namer.ObjectName(obj); err == nil {
 | 
						objNamespace, objName, err := namer.ObjectName(obj)
 | 
				
			||||||
		if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
			return err
 | 
							return errors.NewBadRequest(fmt.Sprintf(
 | 
				
			||||||
		}
 | 
								"the name of the object (%s based on URL) was undeterminable: %v", name, err))
 | 
				
			||||||
		if objName != name {
 | 
						}
 | 
				
			||||||
 | 
						if objName != name {
 | 
				
			||||||
 | 
							return errors.NewBadRequest(fmt.Sprintf(
 | 
				
			||||||
 | 
								"the name of the object (%s) does not match the name on the URL (%s)", objName, name))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if len(namespace) > 0 {
 | 
				
			||||||
 | 
							if len(objNamespace) > 0 && objNamespace != namespace {
 | 
				
			||||||
			return errors.NewBadRequest(fmt.Sprintf(
 | 
								return errors.NewBadRequest(fmt.Sprintf(
 | 
				
			||||||
				"the name of the object (%s) does not match the name on the URL (%s)", objName, name))
 | 
									"the namespace of the object (%s) does not match the namespace on the request (%s)", objNamespace, namespace))
 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		if len(namespace) > 0 {
 | 
					 | 
				
			||||||
			if len(objNamespace) > 0 && objNamespace != namespace {
 | 
					 | 
				
			||||||
				return errors.NewBadRequest(fmt.Sprintf(
 | 
					 | 
				
			||||||
					"the namespace of the object (%s) does not match the namespace on the request (%s)", objNamespace, namespace))
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return nil
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -1188,7 +1165,7 @@ func setListSelfLink(obj runtime.Object, req *restful.Request, namer ScopeNamer)
 | 
				
			|||||||
		return 0, nil
 | 
							return 0, nil
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	uri, err := namer.GenerateListLink(req)
 | 
						uri, err := namer.GenerateListLink(req.Request)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return 0, err
 | 
							return 0, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -19,11 +19,11 @@ package handlers
 | 
				
			|||||||
import (
 | 
					import (
 | 
				
			||||||
	"errors"
 | 
						"errors"
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
 | 
						"net/http"
 | 
				
			||||||
	"reflect"
 | 
						"reflect"
 | 
				
			||||||
	"testing"
 | 
						"testing"
 | 
				
			||||||
	"time"
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/emicklei/go-restful"
 | 
					 | 
				
			||||||
	"github.com/evanphx/json-patch"
 | 
						"github.com/evanphx/json-patch"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	apiequality "k8s.io/apimachinery/pkg/api/equality"
 | 
						apiequality "k8s.io/apimachinery/pkg/api/equality"
 | 
				
			||||||
@@ -128,13 +128,13 @@ type testNamer struct {
 | 
				
			|||||||
	name      string
 | 
						name      string
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (p *testNamer) Namespace(req *restful.Request) (namespace string, err error) {
 | 
					func (p *testNamer) Namespace(req *http.Request) (namespace string, err error) {
 | 
				
			||||||
	return p.namespace, nil
 | 
						return p.namespace, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Name returns the name from the request, and an optional namespace value if this is a namespace
 | 
					// Name returns the name from the request, and an optional namespace value if this is a namespace
 | 
				
			||||||
// scoped call. An error is returned if the name is not available.
 | 
					// scoped call. An error is returned if the name is not available.
 | 
				
			||||||
func (p *testNamer) Name(req *restful.Request) (namespace, name string, err error) {
 | 
					func (p *testNamer) Name(req *http.Request) (namespace, name string, err error) {
 | 
				
			||||||
	return p.namespace, p.name, nil
 | 
						return p.namespace, p.name, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -151,12 +151,12 @@ func (p *testNamer) SetSelfLink(obj runtime.Object, url string) error {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// GenerateLink creates a path and query for a given runtime object that represents the canonical path.
 | 
					// GenerateLink creates a path and query for a given runtime object that represents the canonical path.
 | 
				
			||||||
func (p *testNamer) GenerateLink(req *restful.Request, obj runtime.Object) (uri string, err error) {
 | 
					func (p *testNamer) GenerateLink(req *http.Request, obj runtime.Object) (uri string, err error) {
 | 
				
			||||||
	return "", errors.New("not implemented")
 | 
						return "", errors.New("not implemented")
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// GenerateLink creates a path and query for a list that represents the canonical path.
 | 
					// GenerateLink creates a path and query for a list that represents the canonical path.
 | 
				
			||||||
func (p *testNamer) GenerateListLink(req *restful.Request) (uri string, err error) {
 | 
					func (p *testNamer) GenerateListLink(req *http.Request) (uri string, err error) {
 | 
				
			||||||
	return "", errors.New("not implemented")
 | 
						return "", errors.New("not implemented")
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -17,10 +17,8 @@ limitations under the License.
 | 
				
			|||||||
package endpoints
 | 
					package endpoints
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"bytes"
 | 
					 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"net/http"
 | 
						"net/http"
 | 
				
			||||||
	"net/url"
 | 
					 | 
				
			||||||
	gpath "path"
 | 
						gpath "path"
 | 
				
			||||||
	"reflect"
 | 
						"reflect"
 | 
				
			||||||
	"sort"
 | 
						"sort"
 | 
				
			||||||
@@ -28,7 +26,6 @@ import (
 | 
				
			|||||||
	"time"
 | 
						"time"
 | 
				
			||||||
	"unicode"
 | 
						"unicode"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"k8s.io/apimachinery/pkg/api/errors"
 | 
					 | 
				
			||||||
	"k8s.io/apimachinery/pkg/api/meta"
 | 
						"k8s.io/apimachinery/pkg/api/meta"
 | 
				
			||||||
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 | 
						metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 | 
				
			||||||
	"k8s.io/apimachinery/pkg/conversion"
 | 
						"k8s.io/apimachinery/pkg/conversion"
 | 
				
			||||||
@@ -79,9 +76,6 @@ var toDiscoveryKubeVerb = map[string]string{
 | 
				
			|||||||
	"WATCHLIST":        "watch",
 | 
						"WATCHLIST":        "watch",
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// errEmptyName is returned when API requests do not fill the name section of the path.
 | 
					 | 
				
			||||||
var errEmptyName = errors.NewBadRequest("name must be provided")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Installs handlers for API resources.
 | 
					// Installs handlers for API resources.
 | 
				
			||||||
func (a *APIInstaller) Install(ws *restful.WebService) (apiResources []metav1.APIResource, errors []error) {
 | 
					func (a *APIInstaller) Install(ws *restful.WebService) (apiResources []metav1.APIResource, errors []error) {
 | 
				
			||||||
	errors = make([]error, 0)
 | 
						errors = make([]error, 0)
 | 
				
			||||||
@@ -191,6 +185,9 @@ func (a *APIInstaller) restMapping(resource string) (*meta.RESTMapping, error) {
 | 
				
			|||||||
func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storage, ws *restful.WebService, proxyHandler http.Handler) (*metav1.APIResource, error) {
 | 
					func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storage, ws *restful.WebService, proxyHandler http.Handler) (*metav1.APIResource, error) {
 | 
				
			||||||
	admit := a.group.Admit
 | 
						admit := a.group.Admit
 | 
				
			||||||
	context := a.group.Context
 | 
						context := a.group.Context
 | 
				
			||||||
 | 
						if context == nil {
 | 
				
			||||||
 | 
							return nil, fmt.Errorf("%v missing Context", a.group.GroupVersion)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	optionsExternalVersion := a.group.GroupVersion
 | 
						optionsExternalVersion := a.group.GroupVersion
 | 
				
			||||||
	if a.group.OptionsExternalVersion != nil {
 | 
						if a.group.OptionsExternalVersion != nil {
 | 
				
			||||||
@@ -342,14 +339,11 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	var ctxFn handlers.ContextFunc
 | 
						var ctxFn handlers.ContextFunc
 | 
				
			||||||
	ctxFn = func(req *restful.Request) request.Context {
 | 
						ctxFn = func(req *http.Request) request.Context {
 | 
				
			||||||
		if context == nil {
 | 
							if ctx, ok := context.Get(req); ok {
 | 
				
			||||||
			return request.WithUserAgent(request.NewContext(), req.HeaderParameter("User-Agent"))
 | 
								return request.WithUserAgent(ctx, req.Header.Get("User-Agent"))
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		if ctx, ok := context.Get(req.Request); ok {
 | 
							return request.WithUserAgent(request.NewContext(), req.Header.Get("User-Agent"))
 | 
				
			||||||
			return request.WithUserAgent(ctx, req.HeaderParameter("User-Agent"))
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		return request.WithUserAgent(request.NewContext(), req.HeaderParameter("User-Agent"))
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	allowWatchList := isWatcher && isLister // watching on lists is allowed only for kinds that support both watch and list.
 | 
						allowWatchList := isWatcher && isLister // watching on lists is allowed only for kinds that support both watch and list.
 | 
				
			||||||
@@ -394,7 +388,13 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag
 | 
				
			|||||||
		apiResource.Name = path
 | 
							apiResource.Name = path
 | 
				
			||||||
		apiResource.Namespaced = false
 | 
							apiResource.Namespaced = false
 | 
				
			||||||
		apiResource.Kind = resourceKind
 | 
							apiResource.Kind = resourceKind
 | 
				
			||||||
		namer := rootScopeNaming{scope, a.group.Linker, gpath.Join(a.prefix, resourcePath, "/"), suffix}
 | 
							namer := handlers.ContextBasedNaming{
 | 
				
			||||||
 | 
								GetContext:         ctxFn,
 | 
				
			||||||
 | 
								SelfLinker:         a.group.Linker,
 | 
				
			||||||
 | 
								ClusterScoped:      true,
 | 
				
			||||||
 | 
								SelfLinkPathPrefix: gpath.Join(a.prefix, resourcePath, "/"),
 | 
				
			||||||
 | 
								SelfLinkPathSuffix: suffix,
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// Handler for standard REST verbs (GET, PUT, POST and DELETE).
 | 
							// Handler for standard REST verbs (GET, PUT, POST and DELETE).
 | 
				
			||||||
		// Add actions at the resource path: /api/apiVersion/resource
 | 
							// Add actions at the resource path: /api/apiVersion/resource
 | 
				
			||||||
@@ -430,9 +430,7 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
		resourcePath := namespacedPath
 | 
							resourcePath := namespacedPath
 | 
				
			||||||
		resourceParams := namespaceParams
 | 
							resourceParams := namespaceParams
 | 
				
			||||||
		itemPathPrefix := gpath.Join(a.prefix, scope.ParamName()) + "/"
 | 
					 | 
				
			||||||
		itemPath := namespacedPath + "/{name}"
 | 
							itemPath := namespacedPath + "/{name}"
 | 
				
			||||||
		itemPathMiddle := "/" + resource + "/"
 | 
					 | 
				
			||||||
		nameParams := append(namespaceParams, nameParam)
 | 
							nameParams := append(namespaceParams, nameParam)
 | 
				
			||||||
		proxyParams := append(nameParams, pathParam)
 | 
							proxyParams := append(nameParams, pathParam)
 | 
				
			||||||
		itemPathSuffix := ""
 | 
							itemPathSuffix := ""
 | 
				
			||||||
@@ -445,17 +443,13 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag
 | 
				
			|||||||
		apiResource.Name = path
 | 
							apiResource.Name = path
 | 
				
			||||||
		apiResource.Namespaced = true
 | 
							apiResource.Namespaced = true
 | 
				
			||||||
		apiResource.Kind = resourceKind
 | 
							apiResource.Kind = resourceKind
 | 
				
			||||||
 | 
							namer := handlers.ContextBasedNaming{
 | 
				
			||||||
		itemPathFn := func(name, namespace string) bytes.Buffer {
 | 
								GetContext:         ctxFn,
 | 
				
			||||||
			var buf bytes.Buffer
 | 
								SelfLinker:         a.group.Linker,
 | 
				
			||||||
			buf.WriteString(itemPathPrefix)
 | 
								ClusterScoped:      false,
 | 
				
			||||||
			buf.WriteString(url.QueryEscape(namespace))
 | 
								SelfLinkPathPrefix: gpath.Join(a.prefix, scope.ParamName()) + "/",
 | 
				
			||||||
			buf.WriteString(itemPathMiddle)
 | 
								SelfLinkPathSuffix: itemPathSuffix,
 | 
				
			||||||
			buf.WriteString(url.QueryEscape(name))
 | 
					 | 
				
			||||||
			buf.WriteString(itemPathSuffix)
 | 
					 | 
				
			||||||
			return buf
 | 
					 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		namer := scopeNaming{scope, a.group.Linker, itemPathFn, false}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
		actions = appendIf(actions, action{"LIST", resourcePath, resourceParams, namer, false}, isLister)
 | 
							actions = appendIf(actions, action{"LIST", resourcePath, resourceParams, namer, false}, isLister)
 | 
				
			||||||
		actions = appendIf(actions, action{"POST", resourcePath, resourceParams, namer, false}, isCreater)
 | 
							actions = appendIf(actions, action{"POST", resourcePath, resourceParams, namer, false}, isCreater)
 | 
				
			||||||
@@ -484,7 +478,6 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag
 | 
				
			|||||||
		// For ex: LIST all pods in all namespaces by sending a LIST request at /api/apiVersion/pods.
 | 
							// For ex: LIST all pods in all namespaces by sending a LIST request at /api/apiVersion/pods.
 | 
				
			||||||
		// TODO: more strongly type whether a resource allows these actions on "all namespaces" (bulk delete)
 | 
							// TODO: more strongly type whether a resource allows these actions on "all namespaces" (bulk delete)
 | 
				
			||||||
		if !hasSubresource {
 | 
							if !hasSubresource {
 | 
				
			||||||
			namer = scopeNaming{scope, a.group.Linker, itemPathFn, true}
 | 
					 | 
				
			||||||
			actions = appendIf(actions, action{"LIST", resource, params, namer, true}, isLister)
 | 
								actions = appendIf(actions, action{"LIST", resource, params, namer, true}, isLister)
 | 
				
			||||||
			actions = appendIf(actions, action{"WATCHLIST", "watch/" + resource, params, namer, true}, allowWatchList)
 | 
								actions = appendIf(actions, action{"WATCHLIST", "watch/" + resource, params, namer, true}, allowWatchList)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
@@ -811,149 +804,6 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag
 | 
				
			|||||||
	return &apiResource, nil
 | 
						return &apiResource, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// rootScopeNaming reads only names from a request and ignores namespaces. It implements ScopeNamer
 | 
					 | 
				
			||||||
// for root scoped resources.
 | 
					 | 
				
			||||||
type rootScopeNaming struct {
 | 
					 | 
				
			||||||
	scope meta.RESTScope
 | 
					 | 
				
			||||||
	runtime.SelfLinker
 | 
					 | 
				
			||||||
	pathPrefix string
 | 
					 | 
				
			||||||
	pathSuffix string
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// rootScopeNaming implements ScopeNamer
 | 
					 | 
				
			||||||
var _ handlers.ScopeNamer = rootScopeNaming{}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Namespace returns an empty string because root scoped objects have no namespace.
 | 
					 | 
				
			||||||
func (n rootScopeNaming) Namespace(req *restful.Request) (namespace string, err error) {
 | 
					 | 
				
			||||||
	return "", nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Name returns the name from the path and an empty string for namespace, or an error if the
 | 
					 | 
				
			||||||
// name is empty.
 | 
					 | 
				
			||||||
func (n rootScopeNaming) Name(req *restful.Request) (namespace, name string, err error) {
 | 
					 | 
				
			||||||
	name = req.PathParameter("name")
 | 
					 | 
				
			||||||
	if len(name) == 0 {
 | 
					 | 
				
			||||||
		return "", "", errEmptyName
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return "", name, nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// GenerateLink returns the appropriate path and query to locate an object by its canonical path.
 | 
					 | 
				
			||||||
func (n rootScopeNaming) GenerateLink(req *restful.Request, obj runtime.Object) (uri string, err error) {
 | 
					 | 
				
			||||||
	_, name, err := n.ObjectName(obj)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return "", err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if len(name) == 0 {
 | 
					 | 
				
			||||||
		_, name, err = n.Name(req)
 | 
					 | 
				
			||||||
		if err != nil {
 | 
					 | 
				
			||||||
			return "", err
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return n.pathPrefix + url.QueryEscape(name) + n.pathSuffix, nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// GenerateListLink returns the appropriate path and query to locate a list by its canonical path.
 | 
					 | 
				
			||||||
func (n rootScopeNaming) GenerateListLink(req *restful.Request) (uri string, err error) {
 | 
					 | 
				
			||||||
	if len(req.Request.URL.RawPath) > 0 {
 | 
					 | 
				
			||||||
		return req.Request.URL.RawPath, nil
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return req.Request.URL.EscapedPath(), nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// ObjectName returns the name set on the object, or an error if the
 | 
					 | 
				
			||||||
// name cannot be returned. Namespace is empty
 | 
					 | 
				
			||||||
// TODO: distinguish between objects with name/namespace and without via a specific error.
 | 
					 | 
				
			||||||
func (n rootScopeNaming) ObjectName(obj runtime.Object) (namespace, name string, err error) {
 | 
					 | 
				
			||||||
	name, err = n.SelfLinker.Name(obj)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return "", "", err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if len(name) == 0 {
 | 
					 | 
				
			||||||
		return "", "", errEmptyName
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return "", name, nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// scopeNaming returns naming information from a request. It implements ScopeNamer for
 | 
					 | 
				
			||||||
// namespace scoped resources.
 | 
					 | 
				
			||||||
type scopeNaming struct {
 | 
					 | 
				
			||||||
	scope meta.RESTScope
 | 
					 | 
				
			||||||
	runtime.SelfLinker
 | 
					 | 
				
			||||||
	itemPathFn    func(name, namespace string) bytes.Buffer
 | 
					 | 
				
			||||||
	allNamespaces bool
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// scopeNaming implements ScopeNamer
 | 
					 | 
				
			||||||
var _ handlers.ScopeNamer = scopeNaming{}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Namespace returns the namespace from the path or the default.
 | 
					 | 
				
			||||||
func (n scopeNaming) Namespace(req *restful.Request) (namespace string, err error) {
 | 
					 | 
				
			||||||
	if n.allNamespaces {
 | 
					 | 
				
			||||||
		return "", nil
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	namespace = req.PathParameter(n.scope.ArgumentName())
 | 
					 | 
				
			||||||
	if len(namespace) == 0 {
 | 
					 | 
				
			||||||
		// a URL was constructed without the namespace, or this method was invoked
 | 
					 | 
				
			||||||
		// on an object without a namespace path parameter.
 | 
					 | 
				
			||||||
		return "", fmt.Errorf("no namespace parameter found on request")
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return namespace, nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Name returns the name from the path, the namespace (or default), or an error if the
 | 
					 | 
				
			||||||
// name is empty.
 | 
					 | 
				
			||||||
func (n scopeNaming) Name(req *restful.Request) (namespace, name string, err error) {
 | 
					 | 
				
			||||||
	namespace, _ = n.Namespace(req)
 | 
					 | 
				
			||||||
	name = req.PathParameter("name")
 | 
					 | 
				
			||||||
	if len(name) == 0 {
 | 
					 | 
				
			||||||
		return "", "", errEmptyName
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// GenerateLink returns the appropriate path and query to locate an object by its canonical path.
 | 
					 | 
				
			||||||
func (n scopeNaming) GenerateLink(req *restful.Request, obj runtime.Object) (uri string, err error) {
 | 
					 | 
				
			||||||
	namespace, name, err := n.ObjectName(obj)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return "", err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if len(namespace) == 0 && len(name) == 0 {
 | 
					 | 
				
			||||||
		namespace, name, err = n.Name(req)
 | 
					 | 
				
			||||||
		if err != nil {
 | 
					 | 
				
			||||||
			return "", err
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if len(name) == 0 {
 | 
					 | 
				
			||||||
		return "", errEmptyName
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	result := n.itemPathFn(name, namespace)
 | 
					 | 
				
			||||||
	return result.String(), nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// GenerateListLink returns the appropriate path and query to locate a list by its canonical path.
 | 
					 | 
				
			||||||
func (n scopeNaming) GenerateListLink(req *restful.Request) (uri string, err error) {
 | 
					 | 
				
			||||||
	if len(req.Request.URL.RawPath) > 0 {
 | 
					 | 
				
			||||||
		return req.Request.URL.RawPath, nil
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return req.Request.URL.EscapedPath(), nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// ObjectName returns the name and namespace set on the object, or an error if the
 | 
					 | 
				
			||||||
// name cannot be returned.
 | 
					 | 
				
			||||||
// TODO: distinguish between objects with name/namespace and without via a specific error.
 | 
					 | 
				
			||||||
func (n scopeNaming) ObjectName(obj runtime.Object) (namespace, name string, err error) {
 | 
					 | 
				
			||||||
	name, err = n.SelfLinker.Name(obj)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return "", "", err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	namespace, err = n.SelfLinker.Namespace(obj)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return "", "", err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return namespace, name, err
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// This magic incantation returns *ptrToObject for an arbitrary pointer
 | 
					// This magic incantation returns *ptrToObject for an arbitrary pointer
 | 
				
			||||||
func indirectArbitraryPointer(ptrToObject interface{}) interface{} {
 | 
					func indirectArbitraryPointer(ptrToObject interface{}) interface{} {
 | 
				
			||||||
	return reflect.Indirect(reflect.ValueOf(ptrToObject)).Interface()
 | 
						return reflect.Indirect(reflect.ValueOf(ptrToObject)).Interface()
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -17,46 +17,9 @@ limitations under the License.
 | 
				
			|||||||
package endpoints
 | 
					package endpoints
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"bytes"
 | 
					 | 
				
			||||||
	"testing"
 | 
						"testing"
 | 
				
			||||||
 | 
					 | 
				
			||||||
	"k8s.io/apimachinery/pkg/api/meta"
 | 
					 | 
				
			||||||
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 | 
					 | 
				
			||||||
	"k8s.io/client-go/pkg/api"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	"github.com/emicklei/go-restful"
 | 
					 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func TestScopeNamingGenerateLink(t *testing.T) {
 | 
					 | 
				
			||||||
	selfLinker := &setTestSelfLinker{
 | 
					 | 
				
			||||||
		t:           t,
 | 
					 | 
				
			||||||
		expectedSet: "/api/v1/namespaces/other/services/foo",
 | 
					 | 
				
			||||||
		name:        "foo",
 | 
					 | 
				
			||||||
		namespace:   "other",
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	s := scopeNaming{
 | 
					 | 
				
			||||||
		meta.RESTScopeNamespace,
 | 
					 | 
				
			||||||
		selfLinker,
 | 
					 | 
				
			||||||
		func(name, namespace string) bytes.Buffer {
 | 
					 | 
				
			||||||
			return *bytes.NewBufferString("/api/v1/namespaces/" + namespace + "/services/" + name)
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
		true,
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	service := &api.Service{
 | 
					 | 
				
			||||||
		ObjectMeta: metav1.ObjectMeta{
 | 
					 | 
				
			||||||
			Name:      "foo",
 | 
					 | 
				
			||||||
			Namespace: "other",
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
		TypeMeta: metav1.TypeMeta{
 | 
					 | 
				
			||||||
			Kind: "Service",
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	_, err := s.GenerateLink(&restful.Request{}, service)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		t.Errorf("Unexpected error %v", err)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func TestIsVowel(t *testing.T) {
 | 
					func TestIsVowel(t *testing.T) {
 | 
				
			||||||
	tests := []struct {
 | 
						tests := []struct {
 | 
				
			||||||
		name string
 | 
							name string
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										3
									
								
								vendor/BUILD
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								vendor/BUILD
									
									
									
									
										vendored
									
									
								
							@@ -9950,7 +9950,6 @@ go_test(
 | 
				
			|||||||
        "//vendor:k8s.io/apiserver/pkg/endpoints/request",
 | 
					        "//vendor:k8s.io/apiserver/pkg/endpoints/request",
 | 
				
			||||||
        "//vendor:k8s.io/apiserver/pkg/endpoints/testing",
 | 
					        "//vendor:k8s.io/apiserver/pkg/endpoints/testing",
 | 
				
			||||||
        "//vendor:k8s.io/apiserver/pkg/registry/rest",
 | 
					        "//vendor:k8s.io/apiserver/pkg/registry/rest",
 | 
				
			||||||
        "//vendor:k8s.io/client-go/pkg/api",
 | 
					 | 
				
			||||||
    ],
 | 
					    ],
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -10041,7 +10040,6 @@ go_test(
 | 
				
			|||||||
    library = ":k8s.io/apiserver/pkg/endpoints/handlers",
 | 
					    library = ":k8s.io/apiserver/pkg/endpoints/handlers",
 | 
				
			||||||
    tags = ["automanaged"],
 | 
					    tags = ["automanaged"],
 | 
				
			||||||
    deps = [
 | 
					    deps = [
 | 
				
			||||||
        "//vendor:github.com/emicklei/go-restful",
 | 
					 | 
				
			||||||
        "//vendor:github.com/evanphx/json-patch",
 | 
					        "//vendor:github.com/evanphx/json-patch",
 | 
				
			||||||
        "//vendor:k8s.io/apimachinery/pkg/api/equality",
 | 
					        "//vendor:k8s.io/apimachinery/pkg/api/equality",
 | 
				
			||||||
        "//vendor:k8s.io/apimachinery/pkg/api/errors",
 | 
					        "//vendor:k8s.io/apimachinery/pkg/api/errors",
 | 
				
			||||||
@@ -10064,6 +10062,7 @@ go_library(
 | 
				
			|||||||
    srcs = [
 | 
					    srcs = [
 | 
				
			||||||
        "k8s.io/apiserver/pkg/endpoints/handlers/discovery.go",
 | 
					        "k8s.io/apiserver/pkg/endpoints/handlers/discovery.go",
 | 
				
			||||||
        "k8s.io/apiserver/pkg/endpoints/handlers/doc.go",
 | 
					        "k8s.io/apiserver/pkg/endpoints/handlers/doc.go",
 | 
				
			||||||
 | 
					        "k8s.io/apiserver/pkg/endpoints/handlers/namer.go",
 | 
				
			||||||
        "k8s.io/apiserver/pkg/endpoints/handlers/patch.go",
 | 
					        "k8s.io/apiserver/pkg/endpoints/handlers/patch.go",
 | 
				
			||||||
        "k8s.io/apiserver/pkg/endpoints/handlers/proxy.go",
 | 
					        "k8s.io/apiserver/pkg/endpoints/handlers/proxy.go",
 | 
				
			||||||
        "k8s.io/apiserver/pkg/endpoints/handlers/rest.go",
 | 
					        "k8s.io/apiserver/pkg/endpoints/handlers/rest.go",
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user