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
fd65427e
Commit
fd65427e
authored
May 04, 2015
by
Cesar Wong
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
API Server - pass path name in context of create request for subresource
Allows a REST storage for a subresource to obtain name in path from request.
parent
9927a85c
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
118 additions
and
5 deletions
+118
-5
rest.go
pkg/api/rest/rest.go
+31
-0
api_installer.go
pkg/apiserver/api_installer.go
+12
-1
apiserver_test.go
pkg/apiserver/apiserver_test.go
+45
-0
resthandler.go
pkg/apiserver/resthandler.go
+30
-4
No files found.
pkg/api/rest/rest.go
View file @
fd65427e
...
@@ -28,6 +28,25 @@ import (
...
@@ -28,6 +28,25 @@ import (
"github.com/GoogleCloudPlatform/kubernetes/pkg/watch"
"github.com/GoogleCloudPlatform/kubernetes/pkg/watch"
)
)
//TODO:
// Storage interfaces need to be separated into two groups; those that operate
// on collections and those that operate on individually named items.
// Collection interfaces:
// (Method: Current -> Proposed)
// GET: Lister -> CollectionGetter
// WATCH: Watcher -> CollectionWatcher
// CREATE: Creater -> CollectionCreater
// DELETE: (n/a) -> CollectionDeleter
// UPDATE: (n/a) -> CollectionUpdater
//
// Single item interfaces:
// (Method: Current -> Proposed)
// GET: Getter -> NamedGetter
// WATCH: (n/a) -> NamedWatcher
// CREATE: (n/a) -> NamedCreater
// DELETE: Deleter -> NamedDeleter
// UPDATE: Update -> NamedUpdater
// Storage is a generic interface for RESTful storage services.
// Storage is a generic interface for RESTful storage services.
// Resources which are exported to the RESTful API of apiserver need to implement this interface. It is expected
// Resources which are exported to the RESTful API of apiserver need to implement this interface. It is expected
// that objects may implement any of the below interfaces.
// that objects may implement any of the below interfaces.
...
@@ -117,6 +136,18 @@ type Creater interface {
...
@@ -117,6 +136,18 @@ type Creater interface {
Create
(
ctx
api
.
Context
,
obj
runtime
.
Object
)
(
runtime
.
Object
,
error
)
Create
(
ctx
api
.
Context
,
obj
runtime
.
Object
)
(
runtime
.
Object
,
error
)
}
}
// NamedCreater is an object that can create an instance of a RESTful object using a name parameter.
type
NamedCreater
interface
{
// New returns an empty object that can be used with Create after request data has been put into it.
// This object must be a pointer type for use with Codec.DecodeInto([]byte, runtime.Object)
New
()
runtime
.
Object
// Create creates a new version of a resource. It expects a name parameter from the path.
// This is needed for create operations on subresources which include the name of the parent
// resource in the path.
Create
(
ctx
api
.
Context
,
name
string
,
obj
runtime
.
Object
)
(
runtime
.
Object
,
error
)
}
// Updater is an object that can update an instance of a RESTful object.
// Updater is an object that can update an instance of a RESTful object.
type
Updater
interface
{
type
Updater
interface
{
// New returns an empty object that can be used with Update after request data has been put into it.
// New returns an empty object that can be used with Update after request data has been put into it.
...
...
pkg/apiserver/api_installer.go
View file @
fd65427e
...
@@ -130,6 +130,7 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag
...
@@ -130,6 +130,7 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag
// what verbs are supported by the storage, used to know what verbs we support per path
// what verbs are supported by the storage, used to know what verbs we support per path
creater
,
isCreater
:=
storage
.
(
rest
.
Creater
)
creater
,
isCreater
:=
storage
.
(
rest
.
Creater
)
namedCreater
,
isNamedCreater
:=
storage
.
(
rest
.
NamedCreater
)
lister
,
isLister
:=
storage
.
(
rest
.
Lister
)
lister
,
isLister
:=
storage
.
(
rest
.
Lister
)
getter
,
isGetter
:=
storage
.
(
rest
.
Getter
)
getter
,
isGetter
:=
storage
.
(
rest
.
Getter
)
getterWithOptions
,
isGetterWithOptions
:=
storage
.
(
rest
.
GetterWithOptions
)
getterWithOptions
,
isGetterWithOptions
:=
storage
.
(
rest
.
GetterWithOptions
)
...
@@ -145,6 +146,10 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag
...
@@ -145,6 +146,10 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag
storageMeta
=
defaultStorageMetadata
{}
storageMeta
=
defaultStorageMetadata
{}
}
}
if
isNamedCreater
{
isCreater
=
true
}
var
versionedList
interface
{}
var
versionedList
interface
{}
if
isLister
{
if
isLister
{
list
:=
lister
.
NewList
()
list
:=
lister
.
NewList
()
...
@@ -436,7 +441,13 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag
...
@@ -436,7 +441,13 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag
addParams
(
route
,
action
.
Params
)
addParams
(
route
,
action
.
Params
)
ws
.
Route
(
route
)
ws
.
Route
(
route
)
case
"POST"
:
// Create a resource.
case
"POST"
:
// Create a resource.
route
:=
ws
.
POST
(
action
.
Path
)
.
To
(
CreateResource
(
creater
,
reqScope
,
a
.
group
.
Typer
,
admit
))
.
var
handler
restful
.
RouteFunction
if
isNamedCreater
{
handler
=
CreateNamedResource
(
namedCreater
,
reqScope
,
a
.
group
.
Typer
,
admit
)
}
else
{
handler
=
CreateResource
(
creater
,
reqScope
,
a
.
group
.
Typer
,
admit
)
}
route
:=
ws
.
POST
(
action
.
Path
)
.
To
(
handler
)
.
Filter
(
m
)
.
Filter
(
m
)
.
Doc
(
"create a "
+
kind
)
.
Doc
(
"create a "
+
kind
)
.
Operation
(
"create"
+
kind
)
.
Operation
(
"create"
+
kind
)
.
...
...
pkg/apiserver/apiserver_test.go
View file @
fd65427e
...
@@ -531,6 +531,25 @@ func (r *GetWithOptionsRESTStorage) NewGetOptions() (runtime.Object, bool, strin
...
@@ -531,6 +531,25 @@ func (r *GetWithOptionsRESTStorage) NewGetOptions() (runtime.Object, bool, strin
var
_
rest
.
GetterWithOptions
=
&
GetWithOptionsRESTStorage
{}
var
_
rest
.
GetterWithOptions
=
&
GetWithOptionsRESTStorage
{}
type
NamedCreaterRESTStorage
struct
{
*
SimpleRESTStorage
createdName
string
}
func
(
storage
*
NamedCreaterRESTStorage
)
Create
(
ctx
api
.
Context
,
name
string
,
obj
runtime
.
Object
)
(
runtime
.
Object
,
error
)
{
storage
.
checkContext
(
ctx
)
storage
.
created
=
obj
.
(
*
Simple
)
storage
.
createdName
=
name
if
err
:=
storage
.
errors
[
"create"
];
err
!=
nil
{
return
nil
,
err
}
var
err
error
if
storage
.
injectedFunction
!=
nil
{
obj
,
err
=
storage
.
injectedFunction
(
obj
)
}
return
obj
,
err
}
func
extractBody
(
response
*
http
.
Response
,
object
runtime
.
Object
)
(
string
,
error
)
{
func
extractBody
(
response
*
http
.
Response
,
object
runtime
.
Object
)
(
string
,
error
)
{
defer
response
.
Body
.
Close
()
defer
response
.
Body
.
Close
()
body
,
err
:=
ioutil
.
ReadAll
(
response
.
Body
)
body
,
err
:=
ioutil
.
ReadAll
(
response
.
Body
)
...
@@ -1810,6 +1829,32 @@ func TestCreateChecksDecode(t *testing.T) {
...
@@ -1810,6 +1829,32 @@ func TestCreateChecksDecode(t *testing.T) {
}
}
}
}
func
TestCreateWithName
(
t
*
testing
.
T
)
{
pathName
:=
"helloworld"
storage
:=
&
NamedCreaterRESTStorage
{
SimpleRESTStorage
:
&
SimpleRESTStorage
{}}
handler
:=
handle
(
map
[
string
]
rest
.
Storage
{
"simple/sub"
:
storage
})
server
:=
httptest
.
NewServer
(
handler
)
defer
server
.
Close
()
client
:=
http
.
Client
{}
simple
:=
&
Simple
{
Other
:
"foo"
}
data
,
_
:=
codec
.
Encode
(
simple
)
request
,
err
:=
http
.
NewRequest
(
"POST"
,
server
.
URL
+
"/api/version/simple/"
+
pathName
+
"/sub"
,
bytes
.
NewBuffer
(
data
))
if
err
!=
nil
{
t
.
Errorf
(
"unexpected error: %v"
,
err
)
}
response
,
err
:=
client
.
Do
(
request
)
if
err
!=
nil
{
t
.
Errorf
(
"unexpected error: %v"
,
err
)
}
if
response
.
StatusCode
!=
http
.
StatusCreated
{
t
.
Errorf
(
"Unexpected response %#v"
,
response
)
}
if
storage
.
createdName
!=
pathName
{
t
.
Errorf
(
"Did not get expected name in create context. Got: %s, Expected: %s"
,
storage
.
createdName
,
pathName
)
}
}
func
TestUpdateChecksDecode
(
t
*
testing
.
T
)
{
func
TestUpdateChecksDecode
(
t
*
testing
.
T
)
{
handler
:=
handle
(
map
[
string
]
rest
.
Storage
{
"simple"
:
&
SimpleRESTStorage
{}})
handler
:=
handle
(
map
[
string
]
rest
.
Storage
{
"simple"
:
&
SimpleRESTStorage
{}})
server
:=
httptest
.
NewServer
(
handler
)
server
:=
httptest
.
NewServer
(
handler
)
...
...
pkg/apiserver/resthandler.go
View file @
fd65427e
...
@@ -256,19 +256,27 @@ func ListResource(r rest.Lister, rw rest.Watcher, scope RequestScope, forceWatch
...
@@ -256,19 +256,27 @@ func ListResource(r rest.Lister, rw rest.Watcher, scope RequestScope, forceWatch
}
}
}
}
// CreateResource returns a function that will handle a resource creation.
func
createHandler
(
r
rest
.
NamedCreater
,
scope
RequestScope
,
typer
runtime
.
ObjectTyper
,
admit
admission
.
Interface
,
includeName
bool
)
restful
.
RouteFunction
{
func
CreateResource
(
r
rest
.
Creater
,
scope
RequestScope
,
typer
runtime
.
ObjectTyper
,
admit
admission
.
Interface
)
restful
.
RouteFunction
{
return
func
(
req
*
restful
.
Request
,
res
*
restful
.
Response
)
{
return
func
(
req
*
restful
.
Request
,
res
*
restful
.
Response
)
{
w
:=
res
.
ResponseWriter
w
:=
res
.
ResponseWriter
// 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
)
var
(
namespace
,
name
string
err
error
)
if
includeName
{
namespace
,
name
,
err
=
scope
.
Namer
.
Name
(
req
)
}
else
{
namespace
,
err
=
scope
.
Namer
.
Namespace
(
req
)
}
if
err
!=
nil
{
if
err
!=
nil
{
errorJSON
(
err
,
scope
.
Codec
,
w
)
errorJSON
(
err
,
scope
.
Codec
,
w
)
return
return
}
}
ctx
:=
scope
.
ContextFunc
(
req
)
ctx
:=
scope
.
ContextFunc
(
req
)
ctx
=
api
.
WithNamespace
(
ctx
,
namespace
)
ctx
=
api
.
WithNamespace
(
ctx
,
namespace
)
...
@@ -292,7 +300,7 @@ func CreateResource(r rest.Creater, scope RequestScope, typer runtime.ObjectType
...
@@ -292,7 +300,7 @@ func CreateResource(r rest.Creater, scope RequestScope, typer runtime.ObjectType
}
}
result
,
err
:=
finishRequest
(
timeout
,
func
()
(
runtime
.
Object
,
error
)
{
result
,
err
:=
finishRequest
(
timeout
,
func
()
(
runtime
.
Object
,
error
)
{
out
,
err
:=
r
.
Create
(
ctx
,
obj
)
out
,
err
:=
r
.
Create
(
ctx
,
name
,
obj
)
if
status
,
ok
:=
out
.
(
*
api
.
Status
);
ok
&&
err
==
nil
&&
status
.
Code
==
0
{
if
status
,
ok
:=
out
.
(
*
api
.
Status
);
ok
&&
err
==
nil
&&
status
.
Code
==
0
{
status
.
Code
=
http
.
StatusCreated
status
.
Code
=
http
.
StatusCreated
}
}
...
@@ -312,6 +320,24 @@ func CreateResource(r rest.Creater, scope RequestScope, typer runtime.ObjectType
...
@@ -312,6 +320,24 @@ func CreateResource(r rest.Creater, scope RequestScope, typer runtime.ObjectType
}
}
}
}
// CreateNamedResource returns a function that will handle a resource creation with name.
func
CreateNamedResource
(
r
rest
.
NamedCreater
,
scope
RequestScope
,
typer
runtime
.
ObjectTyper
,
admit
admission
.
Interface
)
restful
.
RouteFunction
{
return
createHandler
(
r
,
scope
,
typer
,
admit
,
true
)
}
// CreateResource returns a function that will handle a resource creation.
func
CreateResource
(
r
rest
.
Creater
,
scope
RequestScope
,
typer
runtime
.
ObjectTyper
,
admit
admission
.
Interface
)
restful
.
RouteFunction
{
return
createHandler
(
&
namedCreaterAdapter
{
r
},
scope
,
typer
,
admit
,
false
)
}
type
namedCreaterAdapter
struct
{
rest
.
Creater
}
func
(
c
*
namedCreaterAdapter
)
Create
(
ctx
api
.
Context
,
name
string
,
obj
runtime
.
Object
)
(
runtime
.
Object
,
error
)
{
return
c
.
Creater
.
Create
(
ctx
,
obj
)
}
// PatchResource returns a function that will handle a resource patch
// PatchResource returns a function that will handle a resource patch
// TODO: Eventually PatchResource should just use GuaranteedUpdate and this routine should be a bit cleaner
// TODO: Eventually PatchResource should just use GuaranteedUpdate and this routine should be a bit cleaner
func
PatchResource
(
r
rest
.
Patcher
,
scope
RequestScope
,
typer
runtime
.
ObjectTyper
,
admit
admission
.
Interface
,
converter
runtime
.
ObjectConvertor
)
restful
.
RouteFunction
{
func
PatchResource
(
r
rest
.
Patcher
,
scope
RequestScope
,
typer
runtime
.
ObjectTyper
,
admit
admission
.
Interface
,
converter
runtime
.
ObjectConvertor
)
restful
.
RouteFunction
{
...
...
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