mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-08-03 17:30:00 +00:00
Merge pull request #6725 from smarterclayton/support_subpath_on_getter_with_options
Allow subpath on GET for GetterWithOptions
This commit is contained in:
commit
60f0c28fd4
@ -24,4 +24,4 @@
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -27,4 +27,4 @@
|
|||||||
"title": "",
|
"title": "",
|
||||||
"description": ""
|
"description": ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1919,6 +1919,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",
|
"path": "/api/v1beta1/namespaces",
|
||||||
"description": "API at /api/v1beta1 version v1beta1",
|
"description": "API at /api/v1beta1 version v1beta1",
|
||||||
@ -2977,6 +3020,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",
|
"path": "/api/v1beta1/persistentVolumeClaims",
|
||||||
"description": "API at /api/v1beta1 version v1beta1",
|
"description": "API at /api/v1beta1 version v1beta1",
|
||||||
@ -4615,6 +4701,14 @@
|
|||||||
"summary": "create a Binding",
|
"summary": "create a Binding",
|
||||||
"nickname": "createBinding",
|
"nickname": "createBinding",
|
||||||
"parameters": [
|
"parameters": [
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"paramType": "path",
|
||||||
|
"name": "name",
|
||||||
|
"description": "name of the Binding",
|
||||||
|
"required": true,
|
||||||
|
"allowMultiple": false
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"paramType": "query",
|
"paramType": "query",
|
||||||
@ -6913,7 +7007,6 @@
|
|||||||
"required": [
|
"required": [
|
||||||
"name",
|
"name",
|
||||||
"image",
|
"image",
|
||||||
"entrypoint:omitempty",
|
|
||||||
"imagePullPolicy"
|
"imagePullPolicy"
|
||||||
],
|
],
|
||||||
"properties": {
|
"properties": {
|
||||||
@ -6933,7 +7026,7 @@
|
|||||||
"format": "int32",
|
"format": "int32",
|
||||||
"description": "CPU share in thousandths of a core; cannot be updated"
|
"description": "CPU share in thousandths of a core; cannot be updated"
|
||||||
},
|
},
|
||||||
"entrypoint:omitempty": {
|
"entrypoint": {
|
||||||
"type": "array",
|
"type": "array",
|
||||||
"items": {
|
"items": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
@ -8634,9 +8727,9 @@
|
|||||||
"v1beta1.PersistentVolumeSpec": {
|
"v1beta1.PersistentVolumeSpec": {
|
||||||
"id": "v1beta1.PersistentVolumeSpec",
|
"id": "v1beta1.PersistentVolumeSpec",
|
||||||
"required": [
|
"required": [
|
||||||
|
"persistentDisk",
|
||||||
"hostPath",
|
"hostPath",
|
||||||
"glusterfs",
|
"glusterfs"
|
||||||
"persistentDisk"
|
|
||||||
],
|
],
|
||||||
"properties": {
|
"properties": {
|
||||||
"accessModes": {
|
"accessModes": {
|
||||||
@ -9800,4 +9893,4 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1919,6 +1919,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",
|
"path": "/api/v1beta2/namespaces",
|
||||||
"description": "API at /api/v1beta2 version v1beta2",
|
"description": "API at /api/v1beta2 version v1beta2",
|
||||||
@ -2977,6 +3020,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",
|
"path": "/api/v1beta2/persistentVolumeClaims",
|
||||||
"description": "API at /api/v1beta2 version v1beta2",
|
"description": "API at /api/v1beta2 version v1beta2",
|
||||||
@ -4615,6 +4701,14 @@
|
|||||||
"summary": "create a Binding",
|
"summary": "create a Binding",
|
||||||
"nickname": "createBinding",
|
"nickname": "createBinding",
|
||||||
"parameters": [
|
"parameters": [
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"paramType": "path",
|
||||||
|
"name": "name",
|
||||||
|
"description": "name of the Binding",
|
||||||
|
"required": true,
|
||||||
|
"allowMultiple": false
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"paramType": "query",
|
"paramType": "query",
|
||||||
@ -6913,7 +7007,6 @@
|
|||||||
"required": [
|
"required": [
|
||||||
"name",
|
"name",
|
||||||
"image",
|
"image",
|
||||||
"entrypoint:omitempty",
|
|
||||||
"imagePullPolicy"
|
"imagePullPolicy"
|
||||||
],
|
],
|
||||||
"properties": {
|
"properties": {
|
||||||
@ -6933,7 +7026,7 @@
|
|||||||
"format": "int32",
|
"format": "int32",
|
||||||
"description": "CPU share in thousandths of a core; cannot be updated"
|
"description": "CPU share in thousandths of a core; cannot be updated"
|
||||||
},
|
},
|
||||||
"entrypoint:omitempty": {
|
"entrypoint": {
|
||||||
"type": "array",
|
"type": "array",
|
||||||
"items": {
|
"items": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
@ -9781,4 +9874,4 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1663,6 +1663,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",
|
"path": "/api/v1beta3/namespaces",
|
||||||
"description": "API at /api/v1beta3 version v1beta3",
|
"description": "API at /api/v1beta3 version v1beta3",
|
||||||
@ -2721,6 +2764,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",
|
"path": "/api/v1beta3/namespaces/{namespaces}/persistentvolumeclaims",
|
||||||
"description": "API at /api/v1beta3 version v1beta3",
|
"description": "API at /api/v1beta3 version v1beta3",
|
||||||
@ -9778,14 +9864,14 @@
|
|||||||
"id": "v1beta3.Volume",
|
"id": "v1beta3.Volume",
|
||||||
"required": [
|
"required": [
|
||||||
"name",
|
"name",
|
||||||
|
"gcePersistentDisk",
|
||||||
"gitRepo",
|
"gitRepo",
|
||||||
"secret",
|
"secret",
|
||||||
"nfs",
|
"nfs",
|
||||||
"iscsi",
|
"iscsi",
|
||||||
"glusterfs",
|
"glusterfs",
|
||||||
"hostPath",
|
"hostPath",
|
||||||
"emptyDir",
|
"emptyDir"
|
||||||
"gcePersistentDisk"
|
|
||||||
],
|
],
|
||||||
"properties": {
|
"properties": {
|
||||||
"emptyDir": {
|
"emptyDir": {
|
||||||
@ -9848,4 +9934,4 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -24,4 +24,4 @@
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -55,7 +55,8 @@ type Getter interface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetterWithOptions is an object that retrieve a named RESTful resource and takes
|
// 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 {
|
type GetterWithOptions interface {
|
||||||
// Get finds a resource in the storage by name and returns it.
|
// 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
|
// 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)
|
Get(ctx api.Context, name string, options runtime.Object) (runtime.Object, error)
|
||||||
|
|
||||||
// NewGetOptions returns an empty options object that will be used to pass
|
// NewGetOptions returns an empty options object that will be used to pass
|
||||||
// options to the Get method.
|
// options to the Get method. It may return a bool and a string, if true, the
|
||||||
NewGetOptions() runtime.Object
|
// 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.
|
// Deleter is an object that can delete a named RESTful resource.
|
||||||
|
@ -110,6 +110,7 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag
|
|||||||
// TODO: support deeper paths
|
// TODO: support deeper paths
|
||||||
return fmt.Errorf("api_installer allows only one or two segment paths (resource or resource/subresource)")
|
return fmt.Errorf("api_installer allows only one or two segment paths (resource or resource/subresource)")
|
||||||
}
|
}
|
||||||
|
hasSubresource := len(subresource) > 0
|
||||||
|
|
||||||
object := storage.New()
|
object := storage.New()
|
||||||
_, kind, err := a.group.Typer.ObjectVersionAndKind(object)
|
_, kind, err := a.group.Typer.ObjectVersionAndKind(object)
|
||||||
@ -177,10 +178,14 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
versionedStatus := indirectArbitraryPointer(versionedStatusPtr)
|
versionedStatus := indirectArbitraryPointer(versionedStatusPtr)
|
||||||
var getOptions runtime.Object
|
var (
|
||||||
var getOptionsKind string
|
getOptions runtime.Object
|
||||||
|
getOptionsKind string
|
||||||
|
getSubpath bool
|
||||||
|
getSubpathKey string
|
||||||
|
)
|
||||||
if isGetterWithOptions {
|
if isGetterWithOptions {
|
||||||
getOptions = getterWithOptions.NewGetOptions()
|
getOptions, getSubpath, getSubpathKey = getterWithOptions.NewGetOptions()
|
||||||
_, getOptionsKind, err = a.group.Typer.ObjectVersionAndKind(getOptions)
|
_, getOptionsKind, err = a.group.Typer.ObjectVersionAndKind(getOptions)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -206,21 +211,26 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag
|
|||||||
// Get the list of actions for the given scope.
|
// Get the list of actions for the given scope.
|
||||||
if scope.Name() != meta.RESTScopeNameNamespace {
|
if scope.Name() != meta.RESTScopeNameNamespace {
|
||||||
resourcePath := resource
|
resourcePath := resource
|
||||||
|
resourceParams := params
|
||||||
itemPath := resourcePath + "/{name}"
|
itemPath := resourcePath + "/{name}"
|
||||||
if len(subresource) > 0 {
|
|
||||||
itemPath = itemPath + "/" + subresource
|
|
||||||
resourcePath = itemPath
|
|
||||||
}
|
|
||||||
nameParams := append(params, nameParam)
|
nameParams := append(params, nameParam)
|
||||||
proxyParams := append(nameParams, pathParam)
|
proxyParams := append(nameParams, pathParam)
|
||||||
|
if hasSubresource {
|
||||||
|
itemPath = itemPath + "/" + subresource
|
||||||
|
resourcePath = itemPath
|
||||||
|
resourceParams = nameParams
|
||||||
|
}
|
||||||
namer := rootScopeNaming{scope, a.group.Linker, gpath.Join(a.prefix, itemPath)}
|
namer := rootScopeNaming{scope, a.group.Linker, gpath.Join(a.prefix, itemPath)}
|
||||||
|
|
||||||
// Handler for standard REST verbs (GET, PUT, POST and DELETE).
|
// Handler for standard REST verbs (GET, PUT, POST and DELETE).
|
||||||
actions = appendIf(actions, action{"LIST", resourcePath, params, namer}, isLister)
|
actions = appendIf(actions, action{"LIST", resourcePath, resourceParams, namer}, isLister)
|
||||||
actions = appendIf(actions, action{"POST", resourcePath, params, namer}, isCreater)
|
actions = appendIf(actions, action{"POST", resourcePath, resourceParams, namer}, isCreater)
|
||||||
actions = appendIf(actions, action{"WATCHLIST", "watch/" + resourcePath, params, namer}, allowWatchList)
|
actions = appendIf(actions, action{"WATCHLIST", "watch/" + resourcePath, resourceParams, namer}, allowWatchList)
|
||||||
|
|
||||||
actions = appendIf(actions, action{"GET", itemPath, nameParams, namer}, isGetter)
|
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{"PUT", itemPath, nameParams, namer}, isUpdater)
|
||||||
actions = appendIf(actions, action{"PATCH", itemPath, nameParams, namer}, isPatcher)
|
actions = appendIf(actions, action{"PATCH", itemPath, nameParams, namer}, isPatcher)
|
||||||
actions = appendIf(actions, action{"DELETE", itemPath, nameParams, namer}, isDeleter)
|
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}
|
namespaceParams := []*restful.Parameter{namespaceParam}
|
||||||
|
|
||||||
resourcePath := namespacedPath
|
resourcePath := namespacedPath
|
||||||
|
resourceParams := namespaceParams
|
||||||
itemPath := namespacedPath + "/{name}"
|
itemPath := namespacedPath + "/{name}"
|
||||||
if len(subresource) > 0 {
|
|
||||||
itemPath = itemPath + "/" + subresource
|
|
||||||
resourcePath = itemPath
|
|
||||||
}
|
|
||||||
nameParams := append(namespaceParams, nameParam)
|
nameParams := append(namespaceParams, nameParam)
|
||||||
proxyParams := append(nameParams, pathParam)
|
proxyParams := append(nameParams, pathParam)
|
||||||
|
if hasSubresource {
|
||||||
|
itemPath = itemPath + "/" + subresource
|
||||||
|
resourcePath = itemPath
|
||||||
|
resourceParams = nameParams
|
||||||
|
}
|
||||||
namer := scopeNaming{scope, a.group.Linker, gpath.Join(a.prefix, itemPath), false}
|
namer := scopeNaming{scope, a.group.Linker, gpath.Join(a.prefix, itemPath), false}
|
||||||
|
|
||||||
actions = appendIf(actions, action{"LIST", resourcePath, namespaceParams, namer}, isLister)
|
actions = appendIf(actions, action{"LIST", resourcePath, resourceParams, namer}, isLister)
|
||||||
// Some paths ("/pods/{name}/binding", I'm looking at you) contain an embedded '{name}')
|
actions = appendIf(actions, action{"POST", resourcePath, resourceParams, namer}, isCreater)
|
||||||
if strings.Contains(resourcePath, "{name}") {
|
// DEPRECATED
|
||||||
actions = appendIf(actions, action{"POST", resourcePath, nameParams, namer}, isCreater)
|
actions = appendIf(actions, action{"WATCHLIST", "watch/" + resourcePath, resourceParams, namer}, allowWatchList)
|
||||||
} else {
|
|
||||||
actions = appendIf(actions, action{"POST", resourcePath, namespaceParams, namer}, isCreater)
|
|
||||||
}
|
|
||||||
actions = appendIf(actions, action{"WATCHLIST", "watch/" + resourcePath, namespaceParams, namer}, allowWatchList)
|
|
||||||
|
|
||||||
actions = appendIf(actions, action{"GET", itemPath, nameParams, namer}, isGetter)
|
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{"PUT", itemPath, nameParams, namer}, isUpdater)
|
||||||
actions = appendIf(actions, action{"PATCH", itemPath, nameParams, namer}, isPatcher)
|
actions = appendIf(actions, action{"PATCH", itemPath, nameParams, namer}, isPatcher)
|
||||||
actions = appendIf(actions, action{"DELETE", itemPath, nameParams, namer}, isDeleter)
|
actions = appendIf(actions, action{"DELETE", itemPath, nameParams, namer}, isDeleter)
|
||||||
@ -278,20 +289,25 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag
|
|||||||
|
|
||||||
basePath := resource
|
basePath := resource
|
||||||
resourcePath := basePath
|
resourcePath := basePath
|
||||||
|
resourceParams := namespaceParams
|
||||||
itemPath := resourcePath + "/{name}"
|
itemPath := resourcePath + "/{name}"
|
||||||
if len(subresource) > 0 {
|
|
||||||
itemPath = itemPath + "/" + subresource
|
|
||||||
resourcePath = itemPath
|
|
||||||
}
|
|
||||||
nameParams := append(namespaceParams, nameParam)
|
nameParams := append(namespaceParams, nameParam)
|
||||||
proxyParams := append(nameParams, pathParam)
|
proxyParams := append(nameParams, pathParam)
|
||||||
|
if hasSubresource {
|
||||||
|
itemPath = itemPath + "/" + subresource
|
||||||
|
resourcePath = itemPath
|
||||||
|
resourceParams = nameParams
|
||||||
|
}
|
||||||
namer := legacyScopeNaming{scope, a.group.Linker, gpath.Join(a.prefix, itemPath)}
|
namer := legacyScopeNaming{scope, a.group.Linker, gpath.Join(a.prefix, itemPath)}
|
||||||
|
|
||||||
actions = appendIf(actions, action{"LIST", resourcePath, namespaceParams, namer}, isLister)
|
actions = appendIf(actions, action{"LIST", resourcePath, resourceParams, namer}, isLister)
|
||||||
actions = appendIf(actions, action{"POST", resourcePath, namespaceParams, namer}, isCreater)
|
actions = appendIf(actions, action{"POST", resourcePath, resourceParams, namer}, isCreater)
|
||||||
actions = appendIf(actions, action{"WATCHLIST", "watch/" + resourcePath, namespaceParams, namer}, allowWatchList)
|
actions = appendIf(actions, action{"WATCHLIST", "watch/" + resourcePath, resourceParams, namer}, allowWatchList)
|
||||||
|
|
||||||
actions = appendIf(actions, action{"GET", itemPath, nameParams, namer}, isGetter)
|
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{"PUT", itemPath, nameParams, namer}, isUpdater)
|
||||||
actions = appendIf(actions, action{"PATCH", itemPath, nameParams, namer}, isPatcher)
|
actions = appendIf(actions, action{"PATCH", itemPath, nameParams, namer}, isPatcher)
|
||||||
actions = appendIf(actions, action{"DELETE", itemPath, nameParams, namer}, isDeleter)
|
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.
|
case "GET": // Get a resource.
|
||||||
var handler restful.RouteFunction
|
var handler restful.RouteFunction
|
||||||
if isGetterWithOptions {
|
if isGetterWithOptions {
|
||||||
handler = GetResourceWithOptions(getterWithOptions, reqScope, getOptionsKind)
|
handler = GetResourceWithOptions(getterWithOptions, reqScope, getOptionsKind, getSubpath, getSubpathKey)
|
||||||
} else {
|
} else {
|
||||||
handler = GetResource(getter, reqScope)
|
handler = GetResource(getter, reqScope)
|
||||||
}
|
}
|
||||||
|
@ -239,6 +239,7 @@ type SimpleGetOptions struct {
|
|||||||
api.TypeMeta `json:",inline"`
|
api.TypeMeta `json:",inline"`
|
||||||
Param1 string `json:"param1"`
|
Param1 string `json:"param1"`
|
||||||
Param2 string `json:"param2"`
|
Param2 string `json:"param2"`
|
||||||
|
Path string `json:"atAPath"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (*SimpleGetOptions) IsAnAPIObject() {}
|
func (*SimpleGetOptions) IsAnAPIObject() {}
|
||||||
@ -459,9 +460,12 @@ func (m *MetadataRESTStorage) ProducesMIMETypes(method string) []string {
|
|||||||
return m.types
|
return m.types
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var _ rest.StorageMetadata = &MetadataRESTStorage{}
|
||||||
|
|
||||||
type GetWithOptionsRESTStorage struct {
|
type GetWithOptionsRESTStorage struct {
|
||||||
*SimpleRESTStorage
|
*SimpleRESTStorage
|
||||||
optionsReceived runtime.Object
|
optionsReceived runtime.Object
|
||||||
|
takesPath string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *GetWithOptionsRESTStorage) Get(ctx api.Context, name string, options runtime.Object) (runtime.Object, error) {
|
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)
|
return r.SimpleRESTStorage.Get(ctx, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *GetWithOptionsRESTStorage) NewGetOptions() runtime.Object {
|
func (r *GetWithOptionsRESTStorage) NewGetOptions() (runtime.Object, bool, string) {
|
||||||
return &SimpleGetOptions{}
|
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) {
|
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)
|
||||||
@ -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) {
|
func TestGetAlternateSelfLink(t *testing.T) {
|
||||||
storage := map[string]rest.Storage{}
|
storage := map[string]rest.Storage{}
|
||||||
simpleStorage := SimpleRESTStorage{
|
simpleStorage := SimpleRESTStorage{
|
||||||
|
@ -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.
|
// 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,
|
return getResourceHandler(scope,
|
||||||
func(ctx api.Context, name string, req *restful.Request) (runtime.Object, error) {
|
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 {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -110,6 +110,8 @@ func (r *BindingREST) New() runtime.Object {
|
|||||||
return &api.Binding{}
|
return &api.Binding{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var _ = rest.Creater(&BindingREST{})
|
||||||
|
|
||||||
// Create ensures a pod is bound to a specific host.
|
// 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) {
|
func (r *BindingREST) Create(ctx api.Context, obj runtime.Object) (out runtime.Object, err error) {
|
||||||
binding := obj.(*api.Binding)
|
binding := obj.(*api.Binding)
|
||||||
@ -197,6 +199,9 @@ type LogREST struct {
|
|||||||
kubeletConn client.ConnectionInfoGetter
|
kubeletConn client.ConnectionInfoGetter
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// LogREST implements GetterWithOptions
|
||||||
|
var _ = rest.GetterWithOptions(&LogREST{})
|
||||||
|
|
||||||
// New creates a new Pod log options object
|
// New creates a new Pod log options object
|
||||||
func (r *LogREST) New() runtime.Object {
|
func (r *LogREST) New() runtime.Object {
|
||||||
return &api.PodLogOptions{}
|
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
|
// NewGetOptions creates a new options object
|
||||||
func (r *LogREST) NewGetOptions() runtime.Object {
|
func (r *LogREST) NewGetOptions() (runtime.Object, bool, string) {
|
||||||
return &api.PodLogOptions{}
|
return &api.PodLogOptions{}, false, ""
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user