Add support for Namespace as Kind

Add example for using namespaces
This commit is contained in:
derekwaynecarr
2015-01-19 16:50:00 -05:00
parent 151be7773c
commit 0bd0e12bbc
55 changed files with 1612 additions and 91 deletions

View File

@@ -232,7 +232,7 @@ func (storage *SimpleRESTStorage) Watch(ctx api.Context, label, field labels.Sel
storage.requestedLabelSelector = label
storage.requestedFieldSelector = field
storage.requestedResourceVersion = resourceVersion
storage.requestedResourceNamespace = api.Namespace(ctx)
storage.requestedResourceNamespace = api.NamespaceValue(ctx)
if err := storage.errors["watch"]; err != nil {
return nil, err
}
@@ -243,7 +243,7 @@ func (storage *SimpleRESTStorage) Watch(ctx api.Context, label, field labels.Sel
// Implement Redirector.
func (storage *SimpleRESTStorage) ResourceLocation(ctx api.Context, id string) (string, error) {
// validate that the namespace context on the request matches the expected input
storage.requestedResourceNamespace = api.Namespace(ctx)
storage.requestedResourceNamespace = api.NamespaceValue(ctx)
if storage.expectedResourceNamespace != storage.requestedResourceNamespace {
return "", fmt.Errorf("Expected request namespace %s, but got namespace %s", storage.expectedResourceNamespace, storage.requestedResourceNamespace)
}

View File

@@ -223,8 +223,10 @@ type APIRequestInfoResolver struct {
// GetAPIRequestInfo returns the information from the http request. If error is not nil, APIRequestInfo holds the information as best it is known before the failure
// Valid Inputs:
// Storage paths
// /ns/{namespace}/{resource}
// /ns/{namespace}/{resource}/{resourceName}
// /namespaces
// /namespaces/{namespace}
// /namespaces/{namespace}/{resource}
// /namespaces/{namespace}/{resource}/{resourceName}
// /{resource}
// /{resource}/{resourceName}
// /{resource}/{resourceName}?namespace={namespace}
@@ -287,15 +289,16 @@ func (r *APIRequestInfoResolver) GetAPIRequestInfo(req *http.Request) (APIReques
}
// URL forms: /ns/{namespace}/{resource}/*, where parts are adjusted to be relative to kind
if currentParts[0] == "ns" {
// URL forms: /namespaces/{namespace}/{kind}/*, where parts are adjusted to be relative to kind
if currentParts[0] == "namespaces" {
if len(currentParts) < 3 {
return requestInfo, fmt.Errorf("ResourceTypeAndNamespace expects a path of form /ns/{namespace}/*")
requestInfo.Namespace = ""
requestInfo.Resource = "namespaces"
} else {
requestInfo.Resource = currentParts[2]
requestInfo.Namespace = currentParts[1]
currentParts = currentParts[2:]
}
requestInfo.Resource = currentParts[2]
requestInfo.Namespace = currentParts[1]
currentParts = currentParts[2:]
} else {
// URL forms: /{resource}/*
// URL forms: POST /{resource} is a legacy API convention to create in "default" namespace

View File

@@ -77,9 +77,13 @@ func TestGetAPIRequestInfo(t *testing.T) {
expectedName string
expectedParts []string
}{
// resource paths
{"GET", "/ns/other/pods", "list", "", "other", "pods", "Pod", "", []string{"pods"}},
{"GET", "/ns/other/pods/foo", "get", "", "other", "pods", "Pod", "foo", []string{"pods", "foo"}},
{"GET", "/namespaces", "list", "", "", "namespaces", "Namespace", "", []string{"namespaces"}},
{"GET", "/namespaces/other", "get", "", "", "namespaces", "Namespace", "other", []string{"namespaces", "other"}},
{"GET", "/namespaces/other/pods", "list", "", "other", "pods", "Pod", "", []string{"pods"}},
{"GET", "/namespaces/other/pods/foo", "get", "", "other", "pods", "Pod", "foo", []string{"pods", "foo"}},
{"GET", "/pods", "list", "", api.NamespaceAll, "pods", "Pod", "", []string{"pods"}},
{"POST", "/pods", "create", "", api.NamespaceDefault, "pods", "Pod", "", []string{"pods"}},
{"GET", "/pods/foo", "get", "", api.NamespaceDefault, "pods", "Pod", "foo", []string{"pods", "foo"}},
@@ -87,16 +91,16 @@ func TestGetAPIRequestInfo(t *testing.T) {
{"GET", "/pods?namespace=other", "list", "", "other", "pods", "Pod", "", []string{"pods"}},
// special verbs
{"GET", "/proxy/ns/other/pods/foo", "proxy", "", "other", "pods", "Pod", "foo", []string{"pods", "foo"}},
{"GET", "/proxy/namespaces/other/pods/foo", "proxy", "", "other", "pods", "Pod", "foo", []string{"pods", "foo"}},
{"GET", "/proxy/pods/foo", "proxy", "", api.NamespaceDefault, "pods", "Pod", "foo", []string{"pods", "foo"}},
{"GET", "/redirect/ns/other/pods/foo", "redirect", "", "other", "pods", "Pod", "foo", []string{"pods", "foo"}},
{"GET", "/redirect/namespaces/other/pods/foo", "redirect", "", "other", "pods", "Pod", "foo", []string{"pods", "foo"}},
{"GET", "/redirect/pods/foo", "redirect", "", api.NamespaceDefault, "pods", "Pod", "foo", []string{"pods", "foo"}},
{"GET", "/watch/pods", "watch", "", api.NamespaceAll, "pods", "Pod", "", []string{"pods"}},
{"GET", "/watch/ns/other/pods", "watch", "", "other", "pods", "Pod", "", []string{"pods"}},
{"GET", "/watch/namespaces/other/pods", "watch", "", "other", "pods", "Pod", "", []string{"pods"}},
// fully-qualified paths
{"GET", "/api/v1beta1/ns/other/pods", "list", "v1beta1", "other", "pods", "Pod", "", []string{"pods"}},
{"GET", "/api/v1beta1/ns/other/pods/foo", "get", "v1beta1", "other", "pods", "Pod", "foo", []string{"pods", "foo"}},
{"GET", "/api/v1beta1/namespaces/other/pods", "list", "v1beta1", "other", "pods", "Pod", "", []string{"pods"}},
{"GET", "/api/v1beta1/namespaces/other/pods/foo", "get", "v1beta1", "other", "pods", "Pod", "foo", []string{"pods", "foo"}},
{"GET", "/api/v1beta1/pods", "list", "v1beta1", api.NamespaceAll, "pods", "Pod", "", []string{"pods"}},
{"POST", "/api/v1beta1/pods", "create", "v1beta1", api.NamespaceDefault, "pods", "Pod", "", []string{"pods"}},
{"GET", "/api/v1beta1/pods/foo", "get", "v1beta1", api.NamespaceDefault, "pods", "Pod", "foo", []string{"pods", "foo"}},
@@ -105,7 +109,7 @@ func TestGetAPIRequestInfo(t *testing.T) {
{"GET", "/api/v1beta1/proxy/pods/foo", "proxy", "v1beta1", api.NamespaceDefault, "pods", "Pod", "foo", []string{"pods", "foo"}},
{"GET", "/api/v1beta1/redirect/pods/foo", "redirect", "v1beta1", api.NamespaceDefault, "pods", "Pod", "foo", []string{"pods", "foo"}},
{"GET", "/api/v1beta1/watch/pods", "watch", "v1beta1", api.NamespaceAll, "pods", "Pod", "", []string{"pods"}},
{"GET", "/api/v1beta1/watch/ns/other/pods", "watch", "v1beta1", "other", "pods", "Pod", "", []string{"pods"}},
{"GET", "/api/v1beta1/watch/namespaces/other/pods", "watch", "v1beta1", "other", "pods", "Pod", "", []string{"pods"}},
}
apiRequestInfoResolver := &APIRequestInfoResolver{util.NewStringSet("api"), latest.RESTMapper}
@@ -141,11 +145,9 @@ func TestGetAPIRequestInfo(t *testing.T) {
}
errorCases := map[string]string{
"no resource path": "/",
"missing resource type": "/ns/other",
"just apiversion": "/api/v1beta1/",
"apiversion with no resource": "/api/v1beta1/",
"apiversion with just namespace": "/api/v1beta1/ns/other",
"no resource path": "/",
"just apiversion": "/api/v1beta1/",
"apiversion with no resource": "/api/v1beta1/",
}
for k, v := range errorCases {
req, err := http.NewRequest("GET", v, nil)

View File

@@ -294,7 +294,7 @@ func TestProxy(t *testing.T) {
server *httptest.Server
proxyTestPattern string
}{
{namespaceServer, "/prefix/version/proxy/ns/" + item.reqNamespace + "/foo/id" + item.path},
{namespaceServer, "/prefix/version/proxy/namespaces/" + item.reqNamespace + "/foo/id" + item.path},
{legacyNamespaceServer, "/prefix/version/proxy/foo/id" + item.path + "?namespace=" + item.reqNamespace},
}

View File

@@ -107,7 +107,7 @@ func TestRedirectWithNamespaces(t *testing.T) {
for _, item := range table {
simpleStorage.errors["resourceLocation"] = item.err
simpleStorage.resourceLocation = item.id
resp, err := client.Get(server.URL + "/prefix/version/redirect/ns/other/foo/" + item.id)
resp, err := client.Get(server.URL + "/prefix/version/redirect/namespaces/other/foo/" + item.id)
if resp == nil {
t.Fatalf("Unexpected nil resp")
}

View File

@@ -45,6 +45,7 @@ type RESTHandler struct {
func (h *RESTHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
requestInfo, err := h.apiRequestInfoResolver.GetAPIRequestInfo(req)
if err != nil {
glog.Errorf("Unable to handle request %s %s %v", requestInfo.Namespace, requestInfo.Kind, err)
notFound(w, req)
return
}