Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
K
k3s
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Registry
Registry
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Jacklull
k3s
Commits
7cf66443
Commit
7cf66443
authored
Dec 09, 2014
by
derekwaynecarr
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Move namespace from query param to path part
parent
58ba3c7f
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
16 changed files
with
388 additions
and
156 deletions
+388
-156
apiserver.go
pkg/apiserver/apiserver.go
+1
-0
apiserver_test.go
pkg/apiserver/apiserver_test.go
+2
-2
handlers.go
pkg/apiserver/handlers.go
+81
-20
handlers_test.go
pkg/apiserver/handlers_test.go
+75
-0
proxy.go
pkg/apiserver/proxy.go
+10
-14
proxy_test.go
pkg/apiserver/proxy_test.go
+28
-21
redirect.go
pkg/apiserver/redirect.go
+10
-9
redirect_test.go
pkg/apiserver/redirect_test.go
+53
-0
resthandler.go
pkg/apiserver/resthandler.go
+27
-27
watch.go
pkg/apiserver/watch.go
+9
-7
client_test.go
pkg/client/client_test.go
+57
-34
request.go
pkg/client/request.go
+1
-1
replication_controller_test.go
pkg/controller/replication_controller_test.go
+1
-1
resthelper.go
pkg/kubectl/resthelper.go
+5
-5
resthelper_test.go
pkg/kubectl/resthelper_test.go
+26
-13
factory_test.go
plugin/pkg/scheduler/factory/factory_test.go
+2
-2
No files found.
pkg/apiserver/apiserver.go
View file @
7cf66443
...
...
@@ -222,6 +222,7 @@ func (g *APIGroupVersion) InstallREST(container *restful.Container, root string,
for
path
,
storage
:=
range
g
.
handler
.
storage
{
registerResourceHandlers
(
ws
,
version
,
path
,
storage
,
kinds
,
h
)
registerResourceHandlers
(
ws
,
version
,
"ns/{namespace}/"
+
path
,
storage
,
kinds
,
h
)
}
// TODO: port the rest of these. Sadly, if we don't, we'll have inconsistent
...
...
pkg/apiserver/apiserver_test.go
View file @
7cf66443
...
...
@@ -683,7 +683,7 @@ func TestSyncCreate(t *testing.T) {
t
:
t
,
name
:
"bar"
,
namespace
:
"other"
,
expectedSet
:
"/prefix/version/
foo/bar?namespace=othe
r"
,
expectedSet
:
"/prefix/version/
ns/other/foo/ba
r"
,
}
handler
:=
Handle
(
map
[
string
]
RESTStorage
{
"foo"
:
&
storage
,
...
...
@@ -696,7 +696,7 @@ func TestSyncCreate(t *testing.T) {
Other
:
"bar"
,
}
data
,
_
:=
codec
.
Encode
(
simple
)
request
,
err
:=
http
.
NewRequest
(
"POST"
,
server
.
URL
+
"/prefix/version/foo?sync=true"
,
bytes
.
NewBuffer
(
data
))
request
,
err
:=
http
.
NewRequest
(
"POST"
,
server
.
URL
+
"/prefix/version/
ns/other/
foo?sync=true"
,
bytes
.
NewBuffer
(
data
))
if
err
!=
nil
{
t
.
Errorf
(
"unexpected error: %v"
,
err
)
}
...
...
pkg/apiserver/handlers.go
View file @
7cf66443
...
...
@@ -23,6 +23,7 @@ import (
"runtime/debug"
"strings"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/auth/authorizer"
authhandlers
"github.com/GoogleCloudPlatform/kubernetes/pkg/auth/handlers"
"github.com/GoogleCloudPlatform/kubernetes/pkg/httplog"
...
...
@@ -40,24 +41,6 @@ var specialVerbs = map[string]bool{
"watch"
:
true
,
}
// KindFromRequest returns Kind if Kind can be extracted from the request. Otherwise, the empty string.
func
KindFromRequest
(
req
http
.
Request
)
string
{
// TODO: find a way to keep this code's assumptions about paths up to date with changes in the code. Maybe instead
// of directly adding handler's code to the master's Mux, have a function which forces the structure when adding
// them.
parts
:=
splitPath
(
req
.
URL
.
Path
)
if
len
(
parts
)
>
2
&&
parts
[
0
]
==
"api"
{
if
_
,
ok
:=
specialVerbs
[
parts
[
2
]];
ok
{
if
len
(
parts
)
>
3
{
return
parts
[
3
]
}
}
else
{
return
parts
[
2
]
}
}
return
""
}
// IsReadOnlyReq() is true for any (or at least many) request which has no observable
// side effects on state of apiserver (though there may be internal side effects like
// caching and logging).
...
...
@@ -185,14 +168,16 @@ func (r *requestAttributeGetter) GetAttribs(req *http.Request) authorizer.Attrib
attribs
.
ReadOnly
=
IsReadOnlyReq
(
*
req
)
namespace
,
kind
,
_
,
_
:=
KindAndNamespace
(
req
)
// If a path follows the conventions of the REST object store, then
// we can extract the object Kind. Otherwise, not.
attribs
.
Kind
=
KindFromRequest
(
*
req
)
attribs
.
Kind
=
kind
// If the request specifies a namespace, then the namespace is filled in.
// Assumes there is no empty string namespace. Unspecified results
// in empty (does not understand defaulting rules.)
attribs
.
Namespace
=
req
.
URL
.
Query
()
.
Get
(
"namespace"
)
attribs
.
Namespace
=
namespace
return
&
attribs
}
...
...
@@ -208,3 +193,79 @@ func WithAuthorizationCheck(handler http.Handler, getAttribs RequestAttributeGet
forbidden
(
w
,
req
)
})
}
// KindAndNamespace returns the kind, namespace, and path parts for the request relative to /{kind}/{name}
// Valid Inputs:
// Storage paths
// /ns/{namespace}/{kind}
// /ns/{namespace}/{kind}/{resourceName}
// /{kind}
// /{kind}/{resourceName}
// /{kind}/{resourceName}?namespace={namespace}
// /{kind}?namespace={namespace}
//
// Special verbs:
// /proxy/{kind}/{resourceName}
// /proxy/ns/{namespace}/{kind}/{resourceName}
// /redirect/ns/{namespace}/{kind}/{resourceName}
// /redirect/{kind}/{resourceName}
// /watch/{kind}
// /watch/ns/{namespace}/{kind}
//
// Fully qualified paths for above:
// /api/{version}/*
// /api/{version}/*
func
KindAndNamespace
(
req
*
http
.
Request
)
(
namespace
,
kind
string
,
parts
[]
string
,
err
error
)
{
parts
=
splitPath
(
req
.
URL
.
Path
)
if
len
(
parts
)
<
1
{
err
=
fmt
.
Errorf
(
"Unable to determine kind and namespace from an empty URL path"
)
return
}
// handle input of form /api/{version}/* by adjusting special paths
if
parts
[
0
]
==
"api"
{
if
len
(
parts
)
>
2
{
parts
=
parts
[
2
:
]
}
else
{
err
=
fmt
.
Errorf
(
"Unable to determine kind and namespace from url, %v"
,
req
.
URL
)
return
}
}
// handle input of form /{specialVerb}/*
if
_
,
ok
:=
specialVerbs
[
parts
[
0
]];
ok
{
if
len
(
parts
)
>
1
{
parts
=
parts
[
1
:
]
}
else
{
err
=
fmt
.
Errorf
(
"Unable to determine kind and namespace from url, %v"
,
req
.
URL
)
return
}
}
// URL forms: /ns/{namespace}/{kind}/*, where parts are adjusted to be relative to kind
if
parts
[
0
]
==
"ns"
{
if
len
(
parts
)
<
3
{
err
=
fmt
.
Errorf
(
"ResourceTypeAndNamespace expects a path of form /ns/{namespace}/*"
)
return
}
namespace
=
parts
[
1
]
kind
=
parts
[
2
]
parts
=
parts
[
2
:
]
return
}
// URL forms: /{kind}/*
// URL forms: POST /{kind} is a legacy API convention to create in "default" namespace
// URL forms: /{kind}/{resourceName} use the "default" namespace if omitted from query param
// URL forms: /{kind} assume cross-namespace operation if omitted from query param
kind
=
parts
[
0
]
namespace
=
req
.
URL
.
Query
()
.
Get
(
"namespace"
)
if
len
(
namespace
)
==
0
{
if
len
(
parts
)
>
1
||
req
.
Method
==
"POST"
{
namespace
=
api
.
NamespaceDefault
}
else
{
namespace
=
api
.
NamespaceAll
}
}
return
}
pkg/apiserver/handlers_test.go
View file @
7cf66443
...
...
@@ -19,7 +19,10 @@ package apiserver
import
(
"net/http"
"net/http/httptest"
"reflect"
"testing"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
)
type
fakeRL
bool
...
...
@@ -59,3 +62,75 @@ func TestReadOnly(t *testing.T) {
http
.
DefaultClient
.
Do
(
req
)
}
}
func
TestKindAndNamespace
(
t
*
testing
.
T
)
{
successCases
:=
[]
struct
{
url
string
expectedNamespace
string
expectedKind
string
expectedParts
[]
string
}{
// resource paths
{
"/ns/other/pods"
,
"other"
,
"pods"
,
[]
string
{
"pods"
}},
{
"/ns/other/pods/foo"
,
"other"
,
"pods"
,
[]
string
{
"pods"
,
"foo"
}},
{
"/pods"
,
api
.
NamespaceAll
,
"pods"
,
[]
string
{
"pods"
}},
{
"/pods/foo"
,
api
.
NamespaceDefault
,
"pods"
,
[]
string
{
"pods"
,
"foo"
}},
{
"/pods/foo?namespace=other"
,
"other"
,
"pods"
,
[]
string
{
"pods"
,
"foo"
}},
{
"/pods?namespace=other"
,
"other"
,
"pods"
,
[]
string
{
"pods"
}},
// special verbs
{
"/proxy/ns/other/pods/foo"
,
"other"
,
"pods"
,
[]
string
{
"pods"
,
"foo"
}},
{
"/proxy/pods/foo"
,
api
.
NamespaceDefault
,
"pods"
,
[]
string
{
"pods"
,
"foo"
}},
{
"/redirect/ns/other/pods/foo"
,
"other"
,
"pods"
,
[]
string
{
"pods"
,
"foo"
}},
{
"/redirect/pods/foo"
,
api
.
NamespaceDefault
,
"pods"
,
[]
string
{
"pods"
,
"foo"
}},
{
"/watch/pods"
,
api
.
NamespaceAll
,
"pods"
,
[]
string
{
"pods"
}},
{
"/watch/ns/other/pods"
,
"other"
,
"pods"
,
[]
string
{
"pods"
}},
// full-qualified paths
{
"/api/v1beta1/ns/other/pods"
,
"other"
,
"pods"
,
[]
string
{
"pods"
}},
{
"/api/v1beta1/ns/other/pods/foo"
,
"other"
,
"pods"
,
[]
string
{
"pods"
,
"foo"
}},
{
"/api/v1beta1/pods"
,
api
.
NamespaceAll
,
"pods"
,
[]
string
{
"pods"
}},
{
"/api/v1beta1/pods/foo"
,
api
.
NamespaceDefault
,
"pods"
,
[]
string
{
"pods"
,
"foo"
}},
{
"/api/v1beta1/pods/foo?namespace=other"
,
"other"
,
"pods"
,
[]
string
{
"pods"
,
"foo"
}},
{
"/api/v1beta1/pods?namespace=other"
,
"other"
,
"pods"
,
[]
string
{
"pods"
}},
{
"/api/v1beta1/proxy/pods/foo"
,
api
.
NamespaceDefault
,
"pods"
,
[]
string
{
"pods"
,
"foo"
}},
{
"/api/v1beta1/redirect/pods/foo"
,
api
.
NamespaceDefault
,
"pods"
,
[]
string
{
"pods"
,
"foo"
}},
{
"/api/v1beta1/watch/pods"
,
api
.
NamespaceAll
,
"pods"
,
[]
string
{
"pods"
}},
{
"/api/v1beta1/watch/ns/other/pods"
,
"other"
,
"pods"
,
[]
string
{
"pods"
}},
}
for
_
,
successCase
:=
range
successCases
{
req
,
_
:=
http
.
NewRequest
(
"GET"
,
successCase
.
url
,
nil
)
namespace
,
kind
,
parts
,
err
:=
KindAndNamespace
(
req
)
if
err
!=
nil
{
t
.
Errorf
(
"Unexpected error for url: %s"
,
successCase
.
url
)
}
if
successCase
.
expectedNamespace
!=
namespace
{
t
.
Errorf
(
"Unexpected namespace for url: %s, expected: %s, actual: %s"
,
successCase
.
url
,
successCase
.
expectedNamespace
,
namespace
)
}
if
successCase
.
expectedKind
!=
kind
{
t
.
Errorf
(
"Unexpected resourceType for url: %s, expected: %s, actual: %s"
,
successCase
.
url
,
successCase
.
expectedKind
,
kind
)
}
if
!
reflect
.
DeepEqual
(
successCase
.
expectedParts
,
parts
)
{
t
.
Errorf
(
"Unexpected parts for url: %s, expected: %v, actual: %v"
,
successCase
.
url
,
successCase
.
expectedParts
,
parts
)
}
}
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"
,
}
for
k
,
v
:=
range
errorCases
{
req
,
err
:=
http
.
NewRequest
(
"GET"
,
v
,
nil
)
if
err
!=
nil
{
t
.
Errorf
(
"Unexpected error %v"
,
err
)
}
_
,
_
,
_
,
err
=
KindAndNamespace
(
req
)
if
err
==
nil
{
t
.
Errorf
(
"Expected error for key: %s"
,
k
)
}
}
}
pkg/apiserver/proxy.go
View file @
7cf66443
...
...
@@ -78,35 +78,31 @@ type ProxyHandler struct {
}
func
(
r
*
ProxyHandler
)
ServeHTTP
(
w
http
.
ResponseWriter
,
req
*
http
.
Request
)
{
// use the default namespace to address the service
ctx
:=
api
.
NewDefaultContext
()
// if not in default namespace, provide the query parameter
// TODO this will need to go in the path in the future and not as a query parameter
namespace
:=
req
.
URL
.
Query
()
.
Get
(
"namespace"
)
if
len
(
namespace
)
>
0
{
ctx
=
api
.
WithNamespace
(
ctx
,
namespace
)
}
parts
:=
strings
.
SplitN
(
req
.
URL
.
Path
,
"/"
,
3
)
namespace
,
kind
,
parts
,
err
:=
KindAndNamespace
(
req
)
if
err
!=
nil
{
notFound
(
w
,
req
)
return
}
ctx
:=
api
.
WithNamespace
(
api
.
NewContext
(),
namespace
)
if
len
(
parts
)
<
2
{
notFound
(
w
,
req
)
return
}
resourceName
:=
parts
[
0
]
id
:=
parts
[
1
]
rest
:=
""
if
len
(
parts
)
==
3
{
rest
=
parts
[
2
]
}
storage
,
ok
:=
r
.
storage
[
resourceName
]
storage
,
ok
:=
r
.
storage
[
kind
]
if
!
ok
{
httplog
.
LogOf
(
req
,
w
)
.
Addf
(
"'%v' has no storage object"
,
resourceName
)
httplog
.
LogOf
(
req
,
w
)
.
Addf
(
"'%v' has no storage object"
,
kind
)
notFound
(
w
,
req
)
return
}
redirector
,
ok
:=
storage
.
(
Redirector
)
if
!
ok
{
httplog
.
LogOf
(
req
,
w
)
.
Addf
(
"'%v' is not a redirector"
,
resourceName
)
httplog
.
LogOf
(
req
,
w
)
.
Addf
(
"'%v' is not a redirector"
,
kind
)
notFound
(
w
,
req
)
return
}
...
...
@@ -150,7 +146,7 @@ func (r *ProxyHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
proxy
.
Transport
=
&
proxyTransport
{
proxyScheme
:
req
.
URL
.
Scheme
,
proxyHost
:
req
.
URL
.
Host
,
proxyPathPrepend
:
path
.
Join
(
r
.
prefix
,
resourceName
,
id
),
proxyPathPrepend
:
path
.
Join
(
r
.
prefix
,
"ns"
,
namespace
,
kind
,
id
),
}
proxy
.
FlushInterval
=
200
*
time
.
Millisecond
proxy
.
ServeHTTP
(
w
,
newReq
)
...
...
pkg/apiserver/proxy_test.go
View file @
7cf66443
...
...
@@ -142,7 +142,7 @@ func TestProxy(t *testing.T) {
{
"POST"
,
"/some/other/dir"
,
"question"
,
"answer"
,
"default"
},
{
"PUT"
,
"/some/dir/id"
,
"different question"
,
"answer"
,
"default"
},
{
"DELETE"
,
"/some/dir/id"
,
""
,
"ok"
,
"default"
},
{
"GET"
,
"/some/dir/id
?namespace=other
"
,
""
,
"answer"
,
"other"
},
{
"GET"
,
"/some/dir/id"
,
""
,
"answer"
,
"other"
},
}
for
_
,
item
:=
range
table
{
...
...
@@ -169,27 +169,34 @@ func TestProxy(t *testing.T) {
server
:=
httptest
.
NewServer
(
handler
)
defer
server
.
Close
()
req
,
err
:=
http
.
NewRequest
(
item
.
method
,
server
.
URL
+
"/prefix/version/proxy/foo/id"
+
item
.
path
,
strings
.
NewReader
(
item
.
reqBody
),
)
if
err
!=
nil
{
t
.
Errorf
(
"%v - unexpected error %v"
,
item
.
method
,
err
)
continue
}
resp
,
err
:=
http
.
DefaultClient
.
Do
(
req
)
if
err
!=
nil
{
t
.
Errorf
(
"%v - unexpected error %v"
,
item
.
method
,
err
)
continue
// test each supported URL pattern for finding the redirection resource in the proxy in a particular namespace
proxyTestPatterns
:=
[]
string
{
"/prefix/version/proxy/foo/id"
+
item
.
path
+
"?namespace="
+
item
.
reqNamespace
,
"/prefix/version/proxy/ns/"
+
item
.
reqNamespace
+
"/foo/id"
+
item
.
path
,
}
gotResp
,
err
:=
ioutil
.
ReadAll
(
resp
.
Body
)
if
err
!=
nil
{
t
.
Errorf
(
"%v - unexpected error %v"
,
item
.
method
,
err
)
}
resp
.
Body
.
Close
()
if
e
,
a
:=
item
.
respBody
,
string
(
gotResp
);
e
!=
a
{
t
.
Errorf
(
"%v - expected %v, got %v"
,
item
.
method
,
e
,
a
)
for
_
,
proxyTestPattern
:=
range
proxyTestPatterns
{
req
,
err
:=
http
.
NewRequest
(
item
.
method
,
server
.
URL
+
proxyTestPattern
,
strings
.
NewReader
(
item
.
reqBody
),
)
if
err
!=
nil
{
t
.
Errorf
(
"%v - unexpected error %v"
,
item
.
method
,
err
)
continue
}
resp
,
err
:=
http
.
DefaultClient
.
Do
(
req
)
if
err
!=
nil
{
t
.
Errorf
(
"%v - unexpected error %v"
,
item
.
method
,
err
)
continue
}
gotResp
,
err
:=
ioutil
.
ReadAll
(
resp
.
Body
)
if
err
!=
nil
{
t
.
Errorf
(
"%v - unexpected error %v"
,
item
.
method
,
err
)
}
resp
.
Body
.
Close
()
if
e
,
a
:=
item
.
respBody
,
string
(
gotResp
);
e
!=
a
{
t
.
Errorf
(
"%v - expected %v, got %v"
,
item
.
method
,
e
,
a
)
}
}
}
}
pkg/apiserver/redirect.go
View file @
7cf66443
...
...
@@ -30,28 +30,29 @@ type RedirectHandler struct {
}
func
(
r
*
RedirectHandler
)
ServeHTTP
(
w
http
.
ResponseWriter
,
req
*
http
.
Request
)
{
ctx
:=
api
.
NewDefaultContext
(
)
namespace
:=
req
.
URL
.
Query
()
.
Get
(
"namespace"
)
if
len
(
namespace
)
>
0
{
ctx
=
api
.
WithNamespace
(
ctx
,
namespace
)
namespace
,
kind
,
parts
,
err
:=
KindAndNamespace
(
req
)
if
err
!=
nil
{
notFound
(
w
,
req
)
return
}
parts
:=
splitPath
(
req
.
URL
.
Path
)
ctx
:=
api
.
WithNamespace
(
api
.
NewContext
(),
namespace
)
// redirection requires /kind/resourceName path parts
if
len
(
parts
)
!=
2
||
req
.
Method
!=
"GET"
{
notFound
(
w
,
req
)
return
}
resourceName
:=
parts
[
0
]
id
:=
parts
[
1
]
storage
,
ok
:=
r
.
storage
[
resourceName
]
storage
,
ok
:=
r
.
storage
[
kind
]
if
!
ok
{
httplog
.
LogOf
(
req
,
w
)
.
Addf
(
"'%v' has no storage object"
,
resourceName
)
httplog
.
LogOf
(
req
,
w
)
.
Addf
(
"'%v' has no storage object"
,
kind
)
notFound
(
w
,
req
)
return
}
redirector
,
ok
:=
storage
.
(
Redirector
)
if
!
ok
{
httplog
.
LogOf
(
req
,
w
)
.
Addf
(
"'%v' is not a redirector"
,
resourceName
)
httplog
.
LogOf
(
req
,
w
)
.
Addf
(
"'%v' is not a redirector"
,
kind
)
notFound
(
w
,
req
)
return
}
...
...
pkg/apiserver/redirect_test.go
View file @
7cf66443
...
...
@@ -76,3 +76,56 @@ func TestRedirect(t *testing.T) {
}
}
}
func
TestRedirectWithNamespaces
(
t
*
testing
.
T
)
{
simpleStorage
:=
&
SimpleRESTStorage
{
errors
:
map
[
string
]
error
{},
expectedResourceNamespace
:
"other"
,
}
handler
:=
Handle
(
map
[
string
]
RESTStorage
{
"foo"
:
simpleStorage
,
},
codec
,
"/prefix"
,
"version"
,
selfLinker
)
server
:=
httptest
.
NewServer
(
handler
)
defer
server
.
Close
()
dontFollow
:=
errors
.
New
(
"don't follow"
)
client
:=
http
.
Client
{
CheckRedirect
:
func
(
req
*
http
.
Request
,
via
[]
*
http
.
Request
)
error
{
return
dontFollow
},
}
table
:=
[]
struct
{
id
string
err
error
code
int
}{
{
"cozy"
,
nil
,
http
.
StatusTemporaryRedirect
},
{
"horse"
,
errors
.
New
(
"no such id"
),
http
.
StatusInternalServerError
},
}
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
)
if
resp
==
nil
{
t
.
Fatalf
(
"Unexpected nil resp"
)
}
resp
.
Body
.
Close
()
if
e
,
a
:=
item
.
code
,
resp
.
StatusCode
;
e
!=
a
{
t
.
Errorf
(
"Expected %v, got %v"
,
e
,
a
)
}
if
e
,
a
:=
item
.
id
,
simpleStorage
.
requestedResourceLocationID
;
e
!=
a
{
t
.
Errorf
(
"Expected %v, got %v"
,
e
,
a
)
}
if
item
.
err
!=
nil
{
continue
}
if
err
==
nil
||
err
.
(
*
url
.
Error
)
.
Err
!=
dontFollow
{
t
.
Errorf
(
"Unexpected err %#v"
,
err
)
}
if
e
,
a
:=
item
.
id
,
resp
.
Header
.
Get
(
"Location"
);
e
!=
a
{
t
.
Errorf
(
"Expected %v, got %v"
,
e
,
a
)
}
}
}
pkg/apiserver/resthandler.go
View file @
7cf66443
...
...
@@ -41,19 +41,19 @@ type RESTHandler struct {
// ServeHTTP handles requests to all RESTStorage objects.
func
(
h
*
RESTHandler
)
ServeHTTP
(
w
http
.
ResponseWriter
,
req
*
http
.
Request
)
{
parts
:=
splitPath
(
req
.
URL
.
Path
)
if
len
(
parts
)
<
1
{
namespace
,
kind
,
parts
,
err
:=
KindAndNamespace
(
req
)
if
err
!=
nil
{
notFound
(
w
,
req
)
return
}
storage
:=
h
.
storage
[
parts
[
0
]
]
storage
:=
h
.
storage
[
kind
]
if
storage
==
nil
{
httplog
.
LogOf
(
req
,
w
)
.
Addf
(
"'%v' has no storage object"
,
parts
[
0
]
)
httplog
.
LogOf
(
req
,
w
)
.
Addf
(
"'%v' has no storage object"
,
kind
)
notFound
(
w
,
req
)
return
}
h
.
handleRESTStorage
(
parts
,
req
,
w
,
storage
)
h
.
handleRESTStorage
(
parts
,
req
,
w
,
storage
,
namespace
)
}
// Sets the SelfLink field of the object.
...
...
@@ -66,12 +66,17 @@ func (h *RESTHandler) setSelfLink(obj runtime.Object, req *http.Request) error {
if
err
!=
nil
{
return
err
}
// TODO Remove this when namespace is in path
// we need to add namespace as a query param, if its not in the resource path
if
len
(
namespace
)
>
0
{
query
:=
newURL
.
Query
()
query
.
Set
(
"namespace"
,
namespace
)
newURL
.
RawQuery
=
query
.
Encode
()
parts
:=
splitPath
(
req
.
URL
.
Path
)
if
parts
[
0
]
!=
"ns"
{
query
:=
newURL
.
Query
()
query
.
Set
(
"namespace"
,
namespace
)
newURL
.
RawQuery
=
query
.
Encode
()
}
}
err
=
h
.
selfLinker
.
SetSelfLink
(
obj
,
newURL
.
String
())
if
err
!=
nil
{
return
err
...
...
@@ -107,11 +112,14 @@ func (h *RESTHandler) setSelfLinkAddName(obj runtime.Object, req *http.Request)
newURL
.
Path
=
path
.
Join
(
h
.
canonicalPrefix
,
req
.
URL
.
Path
,
name
)
newURL
.
RawQuery
=
""
newURL
.
Fragment
=
""
//
TODO Remove this when namespace is in
path
//
we need to add namespace as a query param, if its not in the resource
path
if
len
(
namespace
)
>
0
{
query
:=
newURL
.
Query
()
query
.
Set
(
"namespace"
,
namespace
)
newURL
.
RawQuery
=
query
.
Encode
()
parts
:=
splitPath
(
req
.
URL
.
Path
)
if
parts
[
0
]
!=
"ns"
{
query
:=
newURL
.
Query
()
query
.
Set
(
"namespace"
,
namespace
)
newURL
.
RawQuery
=
query
.
Encode
()
}
}
return
h
.
selfLinker
.
SetSelfLink
(
obj
,
newURL
.
String
())
}
...
...
@@ -138,18 +146,10 @@ func curry(f func(runtime.Object, *http.Request) error, req *http.Request) func(
// sync=[false|true] Synchronous request (only applies to create, update, delete operations)
// timeout=<duration> Timeout for synchronous requests, only applies if sync=true
// labels=<label-selector> Used for filtering list operations
func
(
h
*
RESTHandler
)
handleRESTStorage
(
parts
[]
string
,
req
*
http
.
Request
,
w
http
.
ResponseWriter
,
storage
RESTStorage
)
{
ctx
:=
api
.
NewContext
(
)
func
(
h
*
RESTHandler
)
handleRESTStorage
(
parts
[]
string
,
req
*
http
.
Request
,
w
http
.
ResponseWriter
,
storage
RESTStorage
,
namespace
string
)
{
ctx
:=
api
.
WithNamespace
(
api
.
NewContext
(),
namespace
)
sync
:=
req
.
URL
.
Query
()
.
Get
(
"sync"
)
==
"true"
timeout
:=
parseTimeout
(
req
.
URL
.
Query
()
.
Get
(
"timeout"
))
// TODO for now, we pull namespace from query parameter, but according to spec, it must go in resource path in future PR
// if a namespace if specified, it's always used.
// for list/watch operations, a namespace is not required if omitted.
// for all other operations, if namespace is omitted, we will default to default namespace.
namespace
:=
req
.
URL
.
Query
()
.
Get
(
"namespace"
)
if
len
(
namespace
)
>
0
{
ctx
=
api
.
WithNamespace
(
ctx
,
namespace
)
}
switch
req
.
Method
{
case
"GET"
:
switch
len
(
parts
)
{
...
...
@@ -175,7 +175,7 @@ func (h *RESTHandler) handleRESTStorage(parts []string, req *http.Request, w htt
}
writeJSON
(
http
.
StatusOK
,
h
.
codec
,
list
,
w
)
case
2
:
item
,
err
:=
storage
.
Get
(
api
.
WithNamespaceDefaultIfNone
(
ctx
)
,
parts
[
1
])
item
,
err
:=
storage
.
Get
(
ctx
,
parts
[
1
])
if
err
!=
nil
{
errorJSON
(
err
,
h
.
codec
,
w
)
return
...
...
@@ -205,7 +205,7 @@ func (h *RESTHandler) handleRESTStorage(parts []string, req *http.Request, w htt
errorJSON
(
err
,
h
.
codec
,
w
)
return
}
out
,
err
:=
storage
.
Create
(
api
.
WithNamespaceDefaultIfNone
(
ctx
)
,
obj
)
out
,
err
:=
storage
.
Create
(
ctx
,
obj
)
if
err
!=
nil
{
errorJSON
(
err
,
h
.
codec
,
w
)
return
...
...
@@ -218,7 +218,7 @@ func (h *RESTHandler) handleRESTStorage(parts []string, req *http.Request, w htt
notFound
(
w
,
req
)
return
}
out
,
err
:=
storage
.
Delete
(
api
.
WithNamespaceDefaultIfNone
(
ctx
)
,
parts
[
1
])
out
,
err
:=
storage
.
Delete
(
ctx
,
parts
[
1
])
if
err
!=
nil
{
errorJSON
(
err
,
h
.
codec
,
w
)
return
...
...
@@ -242,7 +242,7 @@ func (h *RESTHandler) handleRESTStorage(parts []string, req *http.Request, w htt
errorJSON
(
err
,
h
.
codec
,
w
)
return
}
out
,
err
:=
storage
.
Update
(
api
.
WithNamespaceDefaultIfNone
(
ctx
)
,
obj
)
out
,
err
:=
storage
.
Update
(
ctx
,
obj
)
if
err
!=
nil
{
errorJSON
(
err
,
h
.
codec
,
w
)
return
...
...
pkg/apiserver/watch.go
View file @
7cf66443
...
...
@@ -77,17 +77,19 @@ func isWebsocketRequest(req *http.Request) bool {
// ServeHTTP processes watch requests.
func
(
h
*
WatchHandler
)
ServeHTTP
(
w
http
.
ResponseWriter
,
req
*
http
.
Request
)
{
ctx
:=
api
.
NewContext
()
namespace
:=
req
.
URL
.
Query
()
.
Get
(
"namespace"
)
if
len
(
namespace
)
>
0
{
ctx
=
api
.
WithNamespace
(
ctx
,
namespace
)
if
req
.
Method
!=
"GET"
{
notFound
(
w
,
req
)
return
}
parts
:=
splitPath
(
req
.
URL
.
Path
)
if
len
(
parts
)
<
1
||
req
.
Method
!=
"GET"
{
namespace
,
kind
,
_
,
err
:=
KindAndNamespace
(
req
)
if
err
!=
nil
{
notFound
(
w
,
req
)
return
}
storage
:=
h
.
storage
[
parts
[
0
]]
ctx
:=
api
.
WithNamespace
(
api
.
NewContext
(),
namespace
)
storage
:=
h
.
storage
[
kind
]
if
storage
==
nil
{
notFound
(
w
,
req
)
return
...
...
pkg/client/client_test.go
View file @
7cf66443
This diff is collapsed.
Click to expand it.
pkg/client/request.go
View file @
7cf66443
...
...
@@ -136,7 +136,7 @@ func (r *Request) Namespace(namespace string) *Request {
return
r
}
if
len
(
namespace
)
>
0
{
return
r
.
setParam
(
"namespace"
,
namespace
)
return
r
.
Path
(
"ns"
)
.
Path
(
namespace
)
}
return
r
}
...
...
pkg/controller/replication_controller_test.go
View file @
7cf66443
...
...
@@ -221,7 +221,7 @@ func TestCreateReplica(t *testing.T) {
},
Spec
:
controllerSpec
.
Spec
.
Template
.
Spec
,
}
fakeHandler
.
ValidateRequest
(
t
,
makeURL
(
"/
pods?namespace=default
"
),
"POST"
,
nil
)
fakeHandler
.
ValidateRequest
(
t
,
makeURL
(
"/
ns/default/pods
"
),
"POST"
,
nil
)
actualPod
,
err
:=
client
.
Codec
.
Decode
([]
byte
(
fakeHandler
.
RequestBody
))
if
err
!=
nil
{
t
.
Errorf
(
"Unexpected error: %#v"
,
err
)
...
...
pkg/kubectl/resthelper.go
View file @
7cf66443
...
...
@@ -47,14 +47,14 @@ func NewRESTHelper(client RESTClient, mapping *meta.RESTMapping) *RESTHelper {
}
func
(
m
*
RESTHelper
)
Get
(
namespace
,
name
string
,
selector
labels
.
Selector
)
(
runtime
.
Object
,
error
)
{
return
m
.
RESTClient
.
Get
()
.
Path
(
m
.
Resource
)
.
Namespace
(
namespa
ce
)
.
Path
(
name
)
.
SelectorParam
(
"labels"
,
selector
)
.
Do
()
.
Get
()
return
m
.
RESTClient
.
Get
()
.
Namespace
(
namespace
)
.
Path
(
m
.
Resour
ce
)
.
Path
(
name
)
.
SelectorParam
(
"labels"
,
selector
)
.
Do
()
.
Get
()
}
func
(
m
*
RESTHelper
)
Watch
(
namespace
,
resourceVersion
string
,
labelSelector
,
fieldSelector
labels
.
Selector
)
(
watch
.
Interface
,
error
)
{
return
m
.
RESTClient
.
Get
()
.
Path
(
"watch"
)
.
Path
(
m
.
Resource
)
.
Namespace
(
namespace
)
.
Path
(
m
.
Resource
)
.
Param
(
"resourceVersion"
,
resourceVersion
)
.
SelectorParam
(
"labels"
,
labelSelector
)
.
SelectorParam
(
"fields"
,
fieldSelector
)
.
...
...
@@ -62,7 +62,7 @@ func (m *RESTHelper) Watch(namespace, resourceVersion string, labelSelector, fie
}
func
(
m
*
RESTHelper
)
Delete
(
namespace
,
name
string
)
error
{
return
m
.
RESTClient
.
Delete
()
.
Path
(
m
.
Resource
)
.
Namespace
(
namespa
ce
)
.
Path
(
name
)
.
Do
()
.
Error
()
return
m
.
RESTClient
.
Delete
()
.
Namespace
(
namespace
)
.
Path
(
m
.
Resour
ce
)
.
Path
(
name
)
.
Do
()
.
Error
()
}
func
(
m
*
RESTHelper
)
Create
(
namespace
string
,
modify
bool
,
data
[]
byte
)
error
{
...
...
@@ -95,7 +95,7 @@ func (m *RESTHelper) Create(namespace string, modify bool, data []byte) error {
}
func
createResource
(
c
RESTClient
,
resourcePath
,
namespace
string
,
data
[]
byte
)
error
{
return
c
.
Post
()
.
Path
(
resourcePath
)
.
Namespace
(
namespace
)
.
Body
(
data
)
.
Do
()
.
Error
()
return
c
.
Post
()
.
Namespace
(
namespace
)
.
Path
(
resourcePath
)
.
Body
(
data
)
.
Do
()
.
Error
()
}
func
(
m
*
RESTHelper
)
Update
(
namespace
,
name
string
,
overwrite
bool
,
data
[]
byte
)
error
{
...
...
@@ -138,5 +138,5 @@ func (m *RESTHelper) Update(namespace, name string, overwrite bool, data []byte)
}
func
updateResource
(
c
RESTClient
,
resourcePath
,
namespace
,
name
string
,
data
[]
byte
)
error
{
return
c
.
Put
()
.
Path
(
resourcePath
)
.
Namespace
(
namespace
)
.
Path
(
name
)
.
Body
(
data
)
.
Do
()
.
Error
()
return
c
.
Put
()
.
Namespace
(
namespace
)
.
Path
(
resourcePath
)
.
Path
(
name
)
.
Body
(
data
)
.
Do
()
.
Error
()
}
pkg/kubectl/resthelper_test.go
View file @
7cf66443
...
...
@@ -37,6 +37,15 @@ func objBody(obj runtime.Object) io.ReadCloser {
return
ioutil
.
NopCloser
(
bytes
.
NewReader
([]
byte
(
runtime
.
EncodeOrDie
(
testapi
.
Codec
(),
obj
))))
}
// splitPath returns the segments for a URL path.
func
splitPath
(
path
string
)
[]
string
{
path
=
strings
.
Trim
(
path
,
"/"
)
if
path
==
""
{
return
[]
string
{}
}
return
strings
.
Split
(
path
,
"/"
)
}
func
TestRESTHelperDelete
(
t
*
testing
.
T
)
{
tests
:=
[]
struct
{
Err
bool
...
...
@@ -65,12 +74,13 @@ func TestRESTHelperDelete(t *testing.T) {
t
.
Errorf
(
"unexpected method: %#v"
,
req
)
return
false
}
if
!
strings
.
HasSuffix
(
req
.
URL
.
Path
,
"/foo"
)
{
t
.
Errorf
(
"url doesn't contain name: %#v"
,
req
)
parts
:=
splitPath
(
req
.
URL
.
Path
)
if
parts
[
1
]
!=
"bar"
{
t
.
Errorf
(
"url doesn't contain namespace: %#v"
,
req
)
return
false
}
if
req
.
URL
.
Query
()
.
Get
(
"namespace"
)
!=
"bar
"
{
t
.
Errorf
(
"url doesn't contain name
space
: %#v"
,
req
)
if
parts
[
2
]
!=
"foo
"
{
t
.
Errorf
(
"url doesn't contain name: %#v"
,
req
)
return
false
}
return
true
...
...
@@ -105,7 +115,8 @@ func TestRESTHelperCreate(t *testing.T) {
t
.
Errorf
(
"unexpected method: %#v"
,
req
)
return
false
}
if
req
.
URL
.
Query
()
.
Get
(
"namespace"
)
!=
"bar"
{
parts
:=
splitPath
(
req
.
URL
.
Path
)
if
parts
[
1
]
!=
"bar"
{
t
.
Errorf
(
"url doesn't contain namespace: %#v"
,
req
)
return
false
}
...
...
@@ -230,12 +241,13 @@ func TestRESTHelperGet(t *testing.T) {
t
.
Errorf
(
"unexpected method: %#v"
,
req
)
return
false
}
if
!
strings
.
HasSuffix
(
req
.
URL
.
Path
,
"/foo"
)
{
t
.
Errorf
(
"url doesn't contain name: %#v"
,
req
)
parts
:=
splitPath
(
req
.
URL
.
Path
)
if
parts
[
1
]
!=
"bar"
{
t
.
Errorf
(
"url doesn't contain namespace: %#v"
,
req
)
return
false
}
if
req
.
URL
.
Query
()
.
Get
(
"namespace"
)
!=
"bar
"
{
t
.
Errorf
(
"url doesn't contain name
space
: %#v"
,
req
)
if
parts
[
2
]
!=
"foo
"
{
t
.
Errorf
(
"url doesn't contain name: %#v"
,
req
)
return
false
}
return
true
...
...
@@ -273,12 +285,13 @@ func TestRESTHelperUpdate(t *testing.T) {
t
.
Errorf
(
"unexpected method: %#v"
,
req
)
return
false
}
if
!
strings
.
HasSuffix
(
req
.
URL
.
Path
,
"/foo"
)
{
t
.
Errorf
(
"url doesn't contain name: %#v"
,
req
)
parts
:=
splitPath
(
req
.
URL
.
Path
)
if
parts
[
1
]
!=
"bar"
{
t
.
Errorf
(
"url doesn't contain namespace: %#v"
,
req
)
return
false
}
if
req
.
URL
.
Query
()
.
Get
(
"namespace"
)
!=
"bar
"
{
t
.
Errorf
(
"url doesn't contain name
space
: %#v"
,
req
)
if
parts
[
2
]
!=
"foo
"
{
t
.
Errorf
(
"url doesn't contain name: %#v"
,
req
)
return
false
}
return
true
...
...
plugin/pkg/scheduler/factory/factory_test.go
View file @
7cf66443
...
...
@@ -192,7 +192,7 @@ func TestDefaultErrorFunc(t *testing.T) {
}
mux
:=
http
.
NewServeMux
()
// FakeHandler musn't be sent requests other than the one you want to test.
mux
.
Handle
(
"/api/"
+
testapi
.
Version
()
+
"/pods/foo"
,
&
handler
)
mux
.
Handle
(
"/api/"
+
testapi
.
Version
()
+
"/
ns/bar/
pods/foo"
,
&
handler
)
server
:=
httptest
.
NewServer
(
mux
)
defer
server
.
Close
()
factory
:=
NewConfigFactory
(
client
.
NewOrDie
(
&
client
.
Config
{
Host
:
server
.
URL
,
Version
:
testapi
.
Version
()}))
...
...
@@ -213,7 +213,7 @@ func TestDefaultErrorFunc(t *testing.T) {
if
!
exists
{
continue
}
handler
.
ValidateRequest
(
t
,
"/api/"
+
testapi
.
Version
()
+
"/
pods/foo?namespace=bar
"
,
"GET"
,
nil
)
handler
.
ValidateRequest
(
t
,
"/api/"
+
testapi
.
Version
()
+
"/
ns/bar/pods/foo
"
,
"GET"
,
nil
)
if
e
,
a
:=
testPod
,
got
;
!
reflect
.
DeepEqual
(
e
,
a
)
{
t
.
Errorf
(
"Expected %v, got %v"
,
e
,
a
)
}
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment