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
60f0c28f
Commit
60f0c28f
authored
Apr 13, 2015
by
Nikhil Jindal
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #6725 from smarterclayton/support_subpath_on_getter_with_options
Allow subpath on GET for GetterWithOptions
parents
810ad711
d9161a1f
Hide whitespace changes
Inline
Side-by-side
Showing
11 changed files
with
407 additions
and
56 deletions
+407
-56
api.json
api/swagger-spec/api.json
+1
-2
resourceListing.json
api/swagger-spec/resourceListing.json
+1
-2
v1beta1.json
api/swagger-spec/v1beta1.json
+98
-6
v1beta2.json
api/swagger-spec/v1beta2.json
+96
-4
v1beta3.json
api/swagger-spec/v1beta3.json
+89
-4
version.json
api/swagger-spec/version.json
+1
-2
rest.go
pkg/api/rest/rest.go
+8
-3
api_installer.go
pkg/apiserver/api_installer.go
+43
-27
apiserver_test.go
pkg/apiserver/apiserver_test.go
+52
-2
resthandler.go
pkg/apiserver/resthandler.go
+11
-2
etcd.go
pkg/registry/pod/etcd/etcd.go
+7
-2
No files found.
api/swagger-spec/api.json
View file @
60f0c28f
...
...
@@ -24,4 +24,4 @@
]
}
]
}
\ No newline at end of file
}
api/swagger-spec/resourceListing.json
View file @
60f0c28f
...
...
@@ -27,4 +27,4 @@
"title"
:
""
,
"description"
:
""
}
}
\ No newline at end of file
}
api/swagger-spec/v1beta1.json
View file @
60f0c28f
...
...
@@ -1920,6 +1920,49 @@
]
},
{
"path"
:
"/api/v1beta1/minions/{name}/status"
,
"description"
:
"API at /api/v1beta1 version v1beta1"
,
"operations"
:
[
{
"type"
:
"v1beta1.Minion"
,
"method"
:
"PUT"
,
"summary"
:
"replace the specified Node"
,
"nickname"
:
"replaceNode"
,
"parameters"
:
[
{
"type"
:
"string"
,
"paramType"
:
"path"
,
"name"
:
"name"
,
"description"
:
"name of the Node"
,
"required"
:
true
,
"allowMultiple"
:
false
},
{
"type"
:
"v1beta1.Minion"
,
"paramType"
:
"body"
,
"name"
:
"body"
,
"description"
:
""
,
"required"
:
true
,
"allowMultiple"
:
false
}
],
"responseMessages"
:
[
{
"code"
:
200
,
"message"
:
"OK"
,
"responseModel"
:
"v1beta1.Minion"
}
],
"produces"
:
[
"application/json"
],
"consumes"
:
[
"*/*"
]
}
]
},
{
"path"
:
"/api/v1beta1/namespaces"
,
"description"
:
"API at /api/v1beta1 version v1beta1"
,
"operations"
:
[
...
...
@@ -2978,6 +3021,49 @@
]
},
{
"path"
:
"/api/v1beta1/nodes/{name}/status"
,
"description"
:
"API at /api/v1beta1 version v1beta1"
,
"operations"
:
[
{
"type"
:
"v1beta1.Minion"
,
"method"
:
"PUT"
,
"summary"
:
"replace the specified Node"
,
"nickname"
:
"replaceNode"
,
"parameters"
:
[
{
"type"
:
"string"
,
"paramType"
:
"path"
,
"name"
:
"name"
,
"description"
:
"name of the Node"
,
"required"
:
true
,
"allowMultiple"
:
false
},
{
"type"
:
"v1beta1.Minion"
,
"paramType"
:
"body"
,
"name"
:
"body"
,
"description"
:
""
,
"required"
:
true
,
"allowMultiple"
:
false
}
],
"responseMessages"
:
[
{
"code"
:
200
,
"message"
:
"OK"
,
"responseModel"
:
"v1beta1.Minion"
}
],
"produces"
:
[
"application/json"
],
"consumes"
:
[
"*/*"
]
}
]
},
{
"path"
:
"/api/v1beta1/persistentVolumeClaims"
,
"description"
:
"API at /api/v1beta1 version v1beta1"
,
"operations"
:
[
...
...
@@ -4617,6 +4703,14 @@
"parameters"
:
[
{
"type"
:
"string"
,
"paramType"
:
"path"
,
"name"
:
"name"
,
"description"
:
"name of the Binding"
,
"required"
:
true
,
"allowMultiple"
:
false
},
{
"type"
:
"string"
,
"paramType"
:
"query"
,
"name"
:
"namespace"
,
"description"
:
"object name and auth scope, such as for teams and projects"
,
...
...
@@ -6913,7 +7007,6 @@
"required"
:
[
"name"
,
"image"
,
"entrypoint:omitempty"
,
"imagePullPolicy"
],
"properties"
:
{
...
...
@@ -6933,7 +7026,7 @@
"format"
:
"int32"
,
"description"
:
"CPU share in thousandths of a core; cannot be updated"
},
"entrypoint
:omitempty
"
:
{
"entrypoint"
:
{
"type"
:
"array"
,
"items"
:
{
"type"
:
"string"
...
...
@@ -8634,9 +8727,9 @@
"v1beta1.PersistentVolumeSpec"
:
{
"id"
:
"v1beta1.PersistentVolumeSpec"
,
"required"
:
[
"persistentDisk"
,
"hostPath"
,
"glusterfs"
,
"persistentDisk"
"glusterfs"
],
"properties"
:
{
"accessModes"
:
{
...
...
@@ -9800,4 +9893,4 @@
}
}
}
}
\ No newline at end of file
}
api/swagger-spec/v1beta2.json
View file @
60f0c28f
...
...
@@ -1920,6 +1920,49 @@
]
},
{
"path"
:
"/api/v1beta2/minions/{name}/status"
,
"description"
:
"API at /api/v1beta2 version v1beta2"
,
"operations"
:
[
{
"type"
:
"v1beta2.Minion"
,
"method"
:
"PUT"
,
"summary"
:
"replace the specified Node"
,
"nickname"
:
"replaceNode"
,
"parameters"
:
[
{
"type"
:
"string"
,
"paramType"
:
"path"
,
"name"
:
"name"
,
"description"
:
"name of the Node"
,
"required"
:
true
,
"allowMultiple"
:
false
},
{
"type"
:
"v1beta2.Minion"
,
"paramType"
:
"body"
,
"name"
:
"body"
,
"description"
:
""
,
"required"
:
true
,
"allowMultiple"
:
false
}
],
"responseMessages"
:
[
{
"code"
:
200
,
"message"
:
"OK"
,
"responseModel"
:
"v1beta2.Minion"
}
],
"produces"
:
[
"application/json"
],
"consumes"
:
[
"*/*"
]
}
]
},
{
"path"
:
"/api/v1beta2/namespaces"
,
"description"
:
"API at /api/v1beta2 version v1beta2"
,
"operations"
:
[
...
...
@@ -2978,6 +3021,49 @@
]
},
{
"path"
:
"/api/v1beta2/nodes/{name}/status"
,
"description"
:
"API at /api/v1beta2 version v1beta2"
,
"operations"
:
[
{
"type"
:
"v1beta2.Minion"
,
"method"
:
"PUT"
,
"summary"
:
"replace the specified Node"
,
"nickname"
:
"replaceNode"
,
"parameters"
:
[
{
"type"
:
"string"
,
"paramType"
:
"path"
,
"name"
:
"name"
,
"description"
:
"name of the Node"
,
"required"
:
true
,
"allowMultiple"
:
false
},
{
"type"
:
"v1beta2.Minion"
,
"paramType"
:
"body"
,
"name"
:
"body"
,
"description"
:
""
,
"required"
:
true
,
"allowMultiple"
:
false
}
],
"responseMessages"
:
[
{
"code"
:
200
,
"message"
:
"OK"
,
"responseModel"
:
"v1beta2.Minion"
}
],
"produces"
:
[
"application/json"
],
"consumes"
:
[
"*/*"
]
}
]
},
{
"path"
:
"/api/v1beta2/persistentVolumeClaims"
,
"description"
:
"API at /api/v1beta2 version v1beta2"
,
"operations"
:
[
...
...
@@ -4617,6 +4703,14 @@
"parameters"
:
[
{
"type"
:
"string"
,
"paramType"
:
"path"
,
"name"
:
"name"
,
"description"
:
"name of the Binding"
,
"required"
:
true
,
"allowMultiple"
:
false
},
{
"type"
:
"string"
,
"paramType"
:
"query"
,
"name"
:
"namespace"
,
"description"
:
"object name and auth scope, such as for teams and projects"
,
...
...
@@ -6913,7 +7007,6 @@
"required"
:
[
"name"
,
"image"
,
"entrypoint:omitempty"
,
"imagePullPolicy"
],
"properties"
:
{
...
...
@@ -6933,7 +7026,7 @@
"format"
:
"int32"
,
"description"
:
"CPU share in thousandths of a core; cannot be updated"
},
"entrypoint
:omitempty
"
:
{
"entrypoint"
:
{
"type"
:
"array"
,
"items"
:
{
"type"
:
"string"
...
...
@@ -9781,4 +9874,4 @@
}
}
}
}
\ No newline at end of file
}
api/swagger-spec/v1beta3.json
View file @
60f0c28f
...
...
@@ -1664,6 +1664,49 @@
]
},
{
"path"
:
"/api/v1beta3/minions/{name}/status"
,
"description"
:
"API at /api/v1beta3 version v1beta3"
,
"operations"
:
[
{
"type"
:
"v1beta3.Node"
,
"method"
:
"PUT"
,
"summary"
:
"replace the specified Node"
,
"nickname"
:
"replaceNode"
,
"parameters"
:
[
{
"type"
:
"string"
,
"paramType"
:
"path"
,
"name"
:
"name"
,
"description"
:
"name of the Node"
,
"required"
:
true
,
"allowMultiple"
:
false
},
{
"type"
:
"v1beta3.Node"
,
"paramType"
:
"body"
,
"name"
:
"body"
,
"description"
:
""
,
"required"
:
true
,
"allowMultiple"
:
false
}
],
"responseMessages"
:
[
{
"code"
:
200
,
"message"
:
"OK"
,
"responseModel"
:
"v1beta3.Node"
}
],
"produces"
:
[
"application/json"
],
"consumes"
:
[
"*/*"
]
}
]
},
{
"path"
:
"/api/v1beta3/namespaces"
,
"description"
:
"API at /api/v1beta3 version v1beta3"
,
"operations"
:
[
...
...
@@ -2722,6 +2765,49 @@
]
},
{
"path"
:
"/api/v1beta3/nodes/{name}/status"
,
"description"
:
"API at /api/v1beta3 version v1beta3"
,
"operations"
:
[
{
"type"
:
"v1beta3.Node"
,
"method"
:
"PUT"
,
"summary"
:
"replace the specified Node"
,
"nickname"
:
"replaceNode"
,
"parameters"
:
[
{
"type"
:
"string"
,
"paramType"
:
"path"
,
"name"
:
"name"
,
"description"
:
"name of the Node"
,
"required"
:
true
,
"allowMultiple"
:
false
},
{
"type"
:
"v1beta3.Node"
,
"paramType"
:
"body"
,
"name"
:
"body"
,
"description"
:
""
,
"required"
:
true
,
"allowMultiple"
:
false
}
],
"responseMessages"
:
[
{
"code"
:
200
,
"message"
:
"OK"
,
"responseModel"
:
"v1beta3.Node"
}
],
"produces"
:
[
"application/json"
],
"consumes"
:
[
"*/*"
]
}
]
},
{
"path"
:
"/api/v1beta3/namespaces/{namespaces}/persistentvolumeclaims"
,
"description"
:
"API at /api/v1beta3 version v1beta3"
,
"operations"
:
[
...
...
@@ -9778,14 +9864,14 @@
"id"
:
"v1beta3.Volume"
,
"required"
:
[
"name"
,
"gcePersistentDisk"
,
"gitRepo"
,
"secret"
,
"nfs"
,
"iscsi"
,
"glusterfs"
,
"hostPath"
,
"emptyDir"
,
"gcePersistentDisk"
"emptyDir"
],
"properties"
:
{
"emptyDir"
:
{
...
...
@@ -9848,4 +9934,4 @@
}
}
}
}
\ No newline at end of file
}
api/swagger-spec/version.json
View file @
60f0c28f
...
...
@@ -24,4 +24,4 @@
]
}
]
}
\ No newline at end of file
}
pkg/api/rest/rest.go
View file @
60f0c28f
...
...
@@ -55,7 +55,8 @@ type Getter interface {
}
// GetterWithOptions is an object that retrieve a named RESTful resource and takes
// additional options on the get request
// additional options on the get request. It allows a caller to also receive the
// subpath of the GET request.
type
GetterWithOptions
interface
{
// Get finds a resource in the storage by name and returns it.
// Although it can return an arbitrary error value, IsNotFound(err) is true for the
...
...
@@ -65,8 +66,12 @@ type GetterWithOptions interface {
Get
(
ctx
api
.
Context
,
name
string
,
options
runtime
.
Object
)
(
runtime
.
Object
,
error
)
// NewGetOptions returns an empty options object that will be used to pass
// options to the Get method.
NewGetOptions
()
runtime
.
Object
// options to the Get method. It may return a bool and a string, if true, the
// value of the request path below the object will be included as the named
// string in the serialization of the runtime object. E.g., returning "path"
// will convert the trailing request scheme value to "path" in the map[string][]string
// passed to the convertor.
NewGetOptions
()
(
runtime
.
Object
,
bool
,
string
)
}
// Deleter is an object that can delete a named RESTful resource.
...
...
pkg/apiserver/api_installer.go
View file @
60f0c28f
...
...
@@ -110,6 +110,7 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag
// TODO: support deeper paths
return
fmt
.
Errorf
(
"api_installer allows only one or two segment paths (resource or resource/subresource)"
)
}
hasSubresource
:=
len
(
subresource
)
>
0
object
:=
storage
.
New
()
_
,
kind
,
err
:=
a
.
group
.
Typer
.
ObjectVersionAndKind
(
object
)
...
...
@@ -177,10 +178,14 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag
return
err
}
versionedStatus
:=
indirectArbitraryPointer
(
versionedStatusPtr
)
var
getOptions
runtime
.
Object
var
getOptionsKind
string
var
(
getOptions
runtime
.
Object
getOptionsKind
string
getSubpath
bool
getSubpathKey
string
)
if
isGetterWithOptions
{
getOptions
=
getterWithOptions
.
NewGetOptions
()
getOptions
,
getSubpath
,
getSubpathKey
=
getterWithOptions
.
NewGetOptions
()
_
,
getOptionsKind
,
err
=
a
.
group
.
Typer
.
ObjectVersionAndKind
(
getOptions
)
if
err
!=
nil
{
return
err
...
...
@@ -206,21 +211,26 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag
// Get the list of actions for the given scope.
if
scope
.
Name
()
!=
meta
.
RESTScopeNameNamespace
{
resourcePath
:=
resource
resourceParams
:=
params
itemPath
:=
resourcePath
+
"/{name}"
if
len
(
subresource
)
>
0
{
nameParams
:=
append
(
params
,
nameParam
)
proxyParams
:=
append
(
nameParams
,
pathParam
)
if
hasSubresource
{
itemPath
=
itemPath
+
"/"
+
subresource
resourcePath
=
itemPath
resourceParams
=
nameParams
}
nameParams
:=
append
(
params
,
nameParam
)
proxyParams
:=
append
(
nameParams
,
pathParam
)
namer
:=
rootScopeNaming
{
scope
,
a
.
group
.
Linker
,
gpath
.
Join
(
a
.
prefix
,
itemPath
)}
// Handler for standard REST verbs (GET, PUT, POST and DELETE).
actions
=
appendIf
(
actions
,
action
{
"LIST"
,
resourcePath
,
p
arams
,
namer
},
isLister
)
actions
=
appendIf
(
actions
,
action
{
"POST"
,
resourcePath
,
p
arams
,
namer
},
isCreater
)
actions
=
appendIf
(
actions
,
action
{
"WATCHLIST"
,
"watch/"
+
resourcePath
,
p
arams
,
namer
},
allowWatchList
)
actions
=
appendIf
(
actions
,
action
{
"LIST"
,
resourcePath
,
resourceP
arams
,
namer
},
isLister
)
actions
=
appendIf
(
actions
,
action
{
"POST"
,
resourcePath
,
resourceP
arams
,
namer
},
isCreater
)
actions
=
appendIf
(
actions
,
action
{
"WATCHLIST"
,
"watch/"
+
resourcePath
,
resourceP
arams
,
namer
},
allowWatchList
)
actions
=
appendIf
(
actions
,
action
{
"GET"
,
itemPath
,
nameParams
,
namer
},
isGetter
)
if
getSubpath
{
actions
=
appendIf
(
actions
,
action
{
"GET"
,
itemPath
+
"/{path:*}"
,
proxyParams
,
namer
},
isGetter
)
}
actions
=
appendIf
(
actions
,
action
{
"PUT"
,
itemPath
,
nameParams
,
namer
},
isUpdater
)
actions
=
appendIf
(
actions
,
action
{
"PATCH"
,
itemPath
,
nameParams
,
namer
},
isPatcher
)
actions
=
appendIf
(
actions
,
action
{
"DELETE"
,
itemPath
,
nameParams
,
namer
},
isDeleter
)
...
...
@@ -238,25 +248,26 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag
namespaceParams
:=
[]
*
restful
.
Parameter
{
namespaceParam
}
resourcePath
:=
namespacedPath
resourceParams
:=
namespaceParams
itemPath
:=
namespacedPath
+
"/{name}"
if
len
(
subresource
)
>
0
{
nameParams
:=
append
(
namespaceParams
,
nameParam
)
proxyParams
:=
append
(
nameParams
,
pathParam
)
if
hasSubresource
{
itemPath
=
itemPath
+
"/"
+
subresource
resourcePath
=
itemPath
resourceParams
=
nameParams
}
nameParams
:=
append
(
namespaceParams
,
nameParam
)
proxyParams
:=
append
(
nameParams
,
pathParam
)
namer
:=
scopeNaming
{
scope
,
a
.
group
.
Linker
,
gpath
.
Join
(
a
.
prefix
,
itemPath
),
false
}
actions
=
appendIf
(
actions
,
action
{
"LIST"
,
resourcePath
,
namespaceParams
,
namer
},
isLister
)
// Some paths ("/pods/{name}/binding", I'm looking at you) contain an embedded '{name}')
if
strings
.
Contains
(
resourcePath
,
"{name}"
)
{
actions
=
appendIf
(
actions
,
action
{
"POST"
,
resourcePath
,
nameParams
,
namer
},
isCreater
)
}
else
{
actions
=
appendIf
(
actions
,
action
{
"POST"
,
resourcePath
,
namespaceParams
,
namer
},
isCreater
)
}
actions
=
appendIf
(
actions
,
action
{
"WATCHLIST"
,
"watch/"
+
resourcePath
,
namespaceParams
,
namer
},
allowWatchList
)
actions
=
appendIf
(
actions
,
action
{
"LIST"
,
resourcePath
,
resourceParams
,
namer
},
isLister
)
actions
=
appendIf
(
actions
,
action
{
"POST"
,
resourcePath
,
resourceParams
,
namer
},
isCreater
)
// DEPRECATED
actions
=
appendIf
(
actions
,
action
{
"WATCHLIST"
,
"watch/"
+
resourcePath
,
resourceParams
,
namer
},
allowWatchList
)
actions
=
appendIf
(
actions
,
action
{
"GET"
,
itemPath
,
nameParams
,
namer
},
isGetter
)
if
getSubpath
{
actions
=
appendIf
(
actions
,
action
{
"GET"
,
itemPath
+
"/{path:*}"
,
proxyParams
,
namer
},
isGetter
)
}
actions
=
appendIf
(
actions
,
action
{
"PUT"
,
itemPath
,
nameParams
,
namer
},
isUpdater
)
actions
=
appendIf
(
actions
,
action
{
"PATCH"
,
itemPath
,
nameParams
,
namer
},
isPatcher
)
actions
=
appendIf
(
actions
,
action
{
"DELETE"
,
itemPath
,
nameParams
,
namer
},
isDeleter
)
...
...
@@ -278,20 +289,25 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag
basePath
:=
resource
resourcePath
:=
basePath
resourceParams
:=
namespaceParams
itemPath
:=
resourcePath
+
"/{name}"
if
len
(
subresource
)
>
0
{
nameParams
:=
append
(
namespaceParams
,
nameParam
)
proxyParams
:=
append
(
nameParams
,
pathParam
)
if
hasSubresource
{
itemPath
=
itemPath
+
"/"
+
subresource
resourcePath
=
itemPath
resourceParams
=
nameParams
}
nameParams
:=
append
(
namespaceParams
,
nameParam
)
proxyParams
:=
append
(
nameParams
,
pathParam
)
namer
:=
legacyScopeNaming
{
scope
,
a
.
group
.
Linker
,
gpath
.
Join
(
a
.
prefix
,
itemPath
)}
actions
=
appendIf
(
actions
,
action
{
"LIST"
,
resourcePath
,
namespa
ceParams
,
namer
},
isLister
)
actions
=
appendIf
(
actions
,
action
{
"POST"
,
resourcePath
,
namespa
ceParams
,
namer
},
isCreater
)
actions
=
appendIf
(
actions
,
action
{
"WATCHLIST"
,
"watch/"
+
resourcePath
,
namespa
ceParams
,
namer
},
allowWatchList
)
actions
=
appendIf
(
actions
,
action
{
"LIST"
,
resourcePath
,
resour
ceParams
,
namer
},
isLister
)
actions
=
appendIf
(
actions
,
action
{
"POST"
,
resourcePath
,
resour
ceParams
,
namer
},
isCreater
)
actions
=
appendIf
(
actions
,
action
{
"WATCHLIST"
,
"watch/"
+
resourcePath
,
resour
ceParams
,
namer
},
allowWatchList
)
actions
=
appendIf
(
actions
,
action
{
"GET"
,
itemPath
,
nameParams
,
namer
},
isGetter
)
if
getSubpath
{
actions
=
appendIf
(
actions
,
action
{
"GET"
,
itemPath
+
"/{path:*}"
,
proxyParams
,
namer
},
isGetter
)
}
actions
=
appendIf
(
actions
,
action
{
"PUT"
,
itemPath
,
nameParams
,
namer
},
isUpdater
)
actions
=
appendIf
(
actions
,
action
{
"PATCH"
,
itemPath
,
nameParams
,
namer
},
isPatcher
)
actions
=
appendIf
(
actions
,
action
{
"DELETE"
,
itemPath
,
nameParams
,
namer
},
isDeleter
)
...
...
@@ -336,7 +352,7 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag
case
"GET"
:
// Get a resource.
var
handler
restful
.
RouteFunction
if
isGetterWithOptions
{
handler
=
GetResourceWithOptions
(
getterWithOptions
,
reqScope
,
getOptionsKind
)
handler
=
GetResourceWithOptions
(
getterWithOptions
,
reqScope
,
getOptionsKind
,
getSubpath
,
getSubpathKey
)
}
else
{
handler
=
GetResource
(
getter
,
reqScope
)
}
...
...
pkg/apiserver/apiserver_test.go
View file @
60f0c28f
...
...
@@ -239,6 +239,7 @@ type SimpleGetOptions struct {
api
.
TypeMeta
`json:",inline"`
Param1
string
`json:"param1"`
Param2
string
`json:"param2"`
Path
string
`json:"atAPath"`
}
func
(
*
SimpleGetOptions
)
IsAnAPIObject
()
{}
...
...
@@ -459,9 +460,12 @@ func (m *MetadataRESTStorage) ProducesMIMETypes(method string) []string {
return
m
.
types
}
var
_
rest
.
StorageMetadata
=
&
MetadataRESTStorage
{}
type
GetWithOptionsRESTStorage
struct
{
*
SimpleRESTStorage
optionsReceived
runtime
.
Object
takesPath
string
}
func
(
r
*
GetWithOptionsRESTStorage
)
Get
(
ctx
api
.
Context
,
name
string
,
options
runtime
.
Object
)
(
runtime
.
Object
,
error
)
{
...
...
@@ -472,10 +476,15 @@ func (r *GetWithOptionsRESTStorage) Get(ctx api.Context, name string, options ru
return
r
.
SimpleRESTStorage
.
Get
(
ctx
,
name
)
}
func
(
r
*
GetWithOptionsRESTStorage
)
NewGetOptions
()
runtime
.
Object
{
return
&
SimpleGetOptions
{}
func
(
r
*
GetWithOptionsRESTStorage
)
NewGetOptions
()
(
runtime
.
Object
,
bool
,
string
)
{
if
len
(
r
.
takesPath
)
>
0
{
return
&
SimpleGetOptions
{},
true
,
r
.
takesPath
}
return
&
SimpleGetOptions
{},
false
,
""
}
var
_
rest
.
GetterWithOptions
=
&
GetWithOptionsRESTStorage
{}
func
extractBody
(
response
*
http
.
Response
,
object
runtime
.
Object
)
(
string
,
error
)
{
defer
response
.
Body
.
Close
()
body
,
err
:=
ioutil
.
ReadAll
(
response
.
Body
)
...
...
@@ -963,6 +972,47 @@ func TestGetWithOptions(t *testing.T) {
}
}
func
TestGetWithOptionsAndPath
(
t
*
testing
.
T
)
{
storage
:=
map
[
string
]
rest
.
Storage
{}
simpleStorage
:=
GetWithOptionsRESTStorage
{
SimpleRESTStorage
:
&
SimpleRESTStorage
{
item
:
Simple
{
Other
:
"foo"
,
},
},
takesPath
:
"atAPath"
,
}
storage
[
"simple"
]
=
&
simpleStorage
handler
:=
handle
(
storage
)
server
:=
httptest
.
NewServer
(
handler
)
defer
server
.
Close
()
resp
,
err
:=
http
.
Get
(
server
.
URL
+
"/api/version/simple/id/a/different/path?param1=test1¶m2=test2&atAPath=not"
)
if
err
!=
nil
{
t
.
Fatalf
(
"unexpected error: %v"
,
err
)
}
if
resp
.
StatusCode
!=
http
.
StatusOK
{
t
.
Fatalf
(
"unexpected response: %#v"
,
resp
)
}
var
itemOut
Simple
body
,
err
:=
extractBody
(
resp
,
&
itemOut
)
if
err
!=
nil
{
t
.
Errorf
(
"unexpected error: %v"
,
err
)
}
if
itemOut
.
Name
!=
simpleStorage
.
item
.
Name
{
t
.
Errorf
(
"Unexpected data: %#v, expected %#v (%s)"
,
itemOut
,
simpleStorage
.
item
,
string
(
body
))
}
opts
,
ok
:=
simpleStorage
.
optionsReceived
.
(
*
SimpleGetOptions
)
if
!
ok
{
t
.
Errorf
(
"Unexpected options object received: %#v"
,
simpleStorage
.
optionsReceived
)
return
}
if
opts
.
Param1
!=
"test1"
||
opts
.
Param2
!=
"test2"
||
opts
.
Path
!=
"a/different/path"
{
t
.
Errorf
(
"Did not receive expected options: %#v"
,
opts
)
}
}
func
TestGetAlternateSelfLink
(
t
*
testing
.
T
)
{
storage
:=
map
[
string
]
rest
.
Storage
{}
simpleStorage
:=
SimpleRESTStorage
{
...
...
pkg/apiserver/resthandler.go
View file @
60f0c28f
...
...
@@ -113,10 +113,19 @@ func GetResource(r rest.Getter, scope RequestScope) restful.RouteFunction {
}
// GetResourceWithOptions returns a function that handles retrieving a single resource from a rest.Storage object.
func
GetResourceWithOptions
(
r
rest
.
GetterWithOptions
,
scope
RequestScope
,
getOptionsKind
string
)
restful
.
RouteFunction
{
func
GetResourceWithOptions
(
r
rest
.
GetterWithOptions
,
scope
RequestScope
,
getOptionsKind
string
,
subpath
bool
,
subpathKey
string
)
restful
.
RouteFunction
{
return
getResourceHandler
(
scope
,
func
(
ctx
api
.
Context
,
name
string
,
req
*
restful
.
Request
)
(
runtime
.
Object
,
error
)
{
opts
,
err
:=
queryToObject
(
req
.
Request
.
URL
.
Query
(),
scope
,
getOptionsKind
)
query
:=
req
.
Request
.
URL
.
Query
()
if
subpath
{
newQuery
:=
make
(
url
.
Values
)
for
k
,
v
:=
range
query
{
newQuery
[
k
]
=
v
}
newQuery
[
subpathKey
]
=
[]
string
{
req
.
PathParameter
(
"path"
)}
query
=
newQuery
}
opts
,
err
:=
queryToObject
(
query
,
scope
,
getOptionsKind
)
if
err
!=
nil
{
return
nil
,
err
}
...
...
pkg/registry/pod/etcd/etcd.go
View file @
60f0c28f
...
...
@@ -110,6 +110,8 @@ func (r *BindingREST) New() runtime.Object {
return
&
api
.
Binding
{}
}
var
_
=
rest
.
Creater
(
&
BindingREST
{})
// Create ensures a pod is bound to a specific host.
func
(
r
*
BindingREST
)
Create
(
ctx
api
.
Context
,
obj
runtime
.
Object
)
(
out
runtime
.
Object
,
err
error
)
{
binding
:=
obj
.
(
*
api
.
Binding
)
...
...
@@ -197,6 +199,9 @@ type LogREST struct {
kubeletConn
client
.
ConnectionInfoGetter
}
// LogREST implements GetterWithOptions
var
_
=
rest
.
GetterWithOptions
(
&
LogREST
{})
// New creates a new Pod log options object
func
(
r
*
LogREST
)
New
()
runtime
.
Object
{
return
&
api
.
PodLogOptions
{}
...
...
@@ -221,6 +226,6 @@ func (r *LogREST) Get(ctx api.Context, name string, opts runtime.Object) (runtim
}
// NewGetOptions creates a new options object
func
(
r
*
LogREST
)
NewGetOptions
()
runtime
.
Object
{
return
&
api
.
PodLogOptions
{}
func
(
r
*
LogREST
)
NewGetOptions
()
(
runtime
.
Object
,
bool
,
string
)
{
return
&
api
.
PodLogOptions
{}
,
false
,
""
}
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