mirror of
https://github.com/kubernetes/client-go.git
synced 2025-06-23 05:37:13 +00:00
Dynamic client support subresource create/get/update/patch verbs
Kubernetes-commit: e10cdb3b0f42bafcdf1d1a95e6fb14cbfe2b4ab7
This commit is contained in:
parent
5f85fe426e
commit
93a87a9af9
@ -145,6 +145,19 @@ type ResourceClient struct {
|
||||
parameterCodec runtime.ParameterCodec
|
||||
}
|
||||
|
||||
func (rc *ResourceClient) parseResourceSubresourceName() (string, []string) {
|
||||
var resourceName string
|
||||
var subresourceName []string
|
||||
if strings.Contains(rc.resource.Name, "/") {
|
||||
resourceName = strings.Split(rc.resource.Name, "/")[0]
|
||||
subresourceName = strings.Split(rc.resource.Name, "/")[1:]
|
||||
} else {
|
||||
resourceName = rc.resource.Name
|
||||
}
|
||||
|
||||
return resourceName, subresourceName
|
||||
}
|
||||
|
||||
// List returns a list of objects for this resource.
|
||||
func (rc *ResourceClient) List(opts metav1.ListOptions) (runtime.Object, error) {
|
||||
parameterEncoder := rc.parameterCodec
|
||||
@ -166,9 +179,11 @@ func (rc *ResourceClient) Get(name string, opts metav1.GetOptions) (*unstructure
|
||||
parameterEncoder = defaultParameterEncoder
|
||||
}
|
||||
result := new(unstructured.Unstructured)
|
||||
resourceName, subresourceName := rc.parseResourceSubresourceName()
|
||||
err := rc.cl.Get().
|
||||
NamespaceIfScoped(rc.ns, rc.resource.Namespaced).
|
||||
Resource(rc.resource.Name).
|
||||
Resource(resourceName).
|
||||
SubResource(subresourceName...).
|
||||
VersionedParams(&opts, parameterEncoder).
|
||||
Name(name).
|
||||
Do().
|
||||
@ -205,11 +220,26 @@ func (rc *ResourceClient) DeleteCollection(deleteOptions *metav1.DeleteOptions,
|
||||
// Create creates the provided resource.
|
||||
func (rc *ResourceClient) Create(obj *unstructured.Unstructured) (*unstructured.Unstructured, error) {
|
||||
result := new(unstructured.Unstructured)
|
||||
err := rc.cl.Post().
|
||||
resourceName, subresourceName := rc.parseResourceSubresourceName()
|
||||
req := rc.cl.Post().
|
||||
NamespaceIfScoped(rc.ns, rc.resource.Namespaced).
|
||||
Resource(rc.resource.Name).
|
||||
Body(obj).
|
||||
Do().
|
||||
Resource(resourceName).
|
||||
Body(obj)
|
||||
if len(subresourceName) > 0 {
|
||||
// If the provided resource is a subresource, the POST request should contain
|
||||
// object name. Examples of subresources that support Create operation:
|
||||
// core/v1/pods/{name}/binding
|
||||
// core/v1/pods/{name}/eviction
|
||||
// extensions/v1beta1/deployments/{name}/rollback
|
||||
// apps/v1beta1/deployments/{name}/rollback
|
||||
// NOTE: Currently our system assumes every subresource object has the same
|
||||
// name as the parent resource object. E.g. a pods/binding object having
|
||||
// metadada.name "foo" means pod "foo" is being bound. We may need to
|
||||
// change this if we break the assumption in the future.
|
||||
req = req.SubResource(subresourceName...).
|
||||
Name(obj.GetName())
|
||||
}
|
||||
err := req.Do().
|
||||
Into(result)
|
||||
return result, err
|
||||
}
|
||||
@ -220,9 +250,15 @@ func (rc *ResourceClient) Update(obj *unstructured.Unstructured) (*unstructured.
|
||||
if len(obj.GetName()) == 0 {
|
||||
return result, errors.New("object missing name")
|
||||
}
|
||||
resourceName, subresourceName := rc.parseResourceSubresourceName()
|
||||
err := rc.cl.Put().
|
||||
NamespaceIfScoped(rc.ns, rc.resource.Namespaced).
|
||||
Resource(rc.resource.Name).
|
||||
Resource(resourceName).
|
||||
SubResource(subresourceName...).
|
||||
// NOTE: Currently our system assumes every subresource object has the same
|
||||
// name as the parent resource object. E.g. a pods/binding object having
|
||||
// metadada.name "foo" means pod "foo" is being bound. We may need to
|
||||
// change this if we break the assumption in the future.
|
||||
Name(obj.GetName()).
|
||||
Body(obj).
|
||||
Do().
|
||||
@ -246,9 +282,11 @@ func (rc *ResourceClient) Watch(opts metav1.ListOptions) (watch.Interface, error
|
||||
|
||||
func (rc *ResourceClient) Patch(name string, pt types.PatchType, data []byte) (*unstructured.Unstructured, error) {
|
||||
result := new(unstructured.Unstructured)
|
||||
resourceName, subresourceName := rc.parseResourceSubresourceName()
|
||||
err := rc.cl.Patch(pt).
|
||||
NamespaceIfScoped(rc.ns, rc.resource.Namespaced).
|
||||
Resource(rc.resource.Name).
|
||||
Resource(resourceName).
|
||||
SubResource(subresourceName...).
|
||||
Name(name).
|
||||
Body(data).
|
||||
Do().
|
||||
|
@ -150,6 +150,7 @@ func TestList(t *testing.T) {
|
||||
|
||||
func TestGet(t *testing.T) {
|
||||
tcs := []struct {
|
||||
resource string
|
||||
namespace string
|
||||
name string
|
||||
path string
|
||||
@ -157,22 +158,39 @@ func TestGet(t *testing.T) {
|
||||
want *unstructured.Unstructured
|
||||
}{
|
||||
{
|
||||
name: "normal_get",
|
||||
path: "/api/gtest/vtest/rtest/normal_get",
|
||||
resp: getJSON("vTest", "rTest", "normal_get"),
|
||||
want: getObject("vTest", "rTest", "normal_get"),
|
||||
resource: "rtest",
|
||||
name: "normal_get",
|
||||
path: "/api/gtest/vtest/rtest/normal_get",
|
||||
resp: getJSON("vTest", "rTest", "normal_get"),
|
||||
want: getObject("vTest", "rTest", "normal_get"),
|
||||
},
|
||||
{
|
||||
resource: "rtest",
|
||||
namespace: "nstest",
|
||||
name: "namespaced_get",
|
||||
path: "/api/gtest/vtest/namespaces/nstest/rtest/namespaced_get",
|
||||
resp: getJSON("vTest", "rTest", "namespaced_get"),
|
||||
want: getObject("vTest", "rTest", "namespaced_get"),
|
||||
},
|
||||
{
|
||||
resource: "rtest/srtest",
|
||||
name: "normal_subresource_get",
|
||||
path: "/api/gtest/vtest/rtest/normal_subresource_get/srtest",
|
||||
resp: getJSON("vTest", "srTest", "normal_subresource_get"),
|
||||
want: getObject("vTest", "srTest", "normal_subresource_get"),
|
||||
},
|
||||
{
|
||||
resource: "rtest/srtest",
|
||||
namespace: "nstest",
|
||||
name: "namespaced_subresource_get",
|
||||
path: "/api/gtest/vtest/namespaces/nstest/rtest/namespaced_subresource_get/srtest",
|
||||
resp: getJSON("vTest", "srTest", "namespaced_subresource_get"),
|
||||
want: getObject("vTest", "srTest", "namespaced_subresource_get"),
|
||||
},
|
||||
}
|
||||
for _, tc := range tcs {
|
||||
gv := &schema.GroupVersion{Group: "gtest", Version: "vtest"}
|
||||
resource := &metav1.APIResource{Name: "rtest", Namespaced: len(tc.namespace) != 0}
|
||||
resource := &metav1.APIResource{Name: tc.resource, Namespaced: len(tc.namespace) != 0}
|
||||
cl, srv, err := getClientServer(gv, func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != "GET" {
|
||||
t.Errorf("Get(%q) got HTTP method %s. wanted GET", tc.name, r.Method)
|
||||
@ -303,26 +321,42 @@ func TestDeleteCollection(t *testing.T) {
|
||||
|
||||
func TestCreate(t *testing.T) {
|
||||
tcs := []struct {
|
||||
resource string
|
||||
name string
|
||||
namespace string
|
||||
obj *unstructured.Unstructured
|
||||
path string
|
||||
}{
|
||||
{
|
||||
name: "normal_create",
|
||||
path: "/api/gtest/vtest/rtest",
|
||||
obj: getObject("vTest", "rTest", "normal_create"),
|
||||
resource: "rtest",
|
||||
name: "normal_create",
|
||||
path: "/api/gtest/vtest/rtest",
|
||||
obj: getObject("vTest", "rTest", "normal_create"),
|
||||
},
|
||||
{
|
||||
resource: "rtest",
|
||||
name: "namespaced_create",
|
||||
namespace: "nstest",
|
||||
path: "/api/gtest/vtest/namespaces/nstest/rtest",
|
||||
obj: getObject("vTest", "rTest", "namespaced_create"),
|
||||
},
|
||||
{
|
||||
resource: "rtest/srtest",
|
||||
name: "normal_subresource_create",
|
||||
path: "/api/gtest/vtest/rtest/normal_subresource_create/srtest",
|
||||
obj: getObject("vTest", "srTest", "normal_subresource_create"),
|
||||
},
|
||||
{
|
||||
resource: "rtest/srtest",
|
||||
name: "namespaced_subresource_create",
|
||||
namespace: "nstest",
|
||||
path: "/api/gtest/vtest/namespaces/nstest/rtest/namespaced_subresource_create/srtest",
|
||||
obj: getObject("vTest", "srTest", "namespaced_subresource_create"),
|
||||
},
|
||||
}
|
||||
for _, tc := range tcs {
|
||||
gv := &schema.GroupVersion{Group: "gtest", Version: "vtest"}
|
||||
resource := &metav1.APIResource{Name: "rtest", Namespaced: len(tc.namespace) != 0}
|
||||
resource := &metav1.APIResource{Name: tc.resource, Namespaced: len(tc.namespace) != 0}
|
||||
cl, srv, err := getClientServer(gv, func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != "POST" {
|
||||
t.Errorf("Create(%q) got HTTP method %s. wanted POST", tc.name, r.Method)
|
||||
@ -362,26 +396,42 @@ func TestCreate(t *testing.T) {
|
||||
|
||||
func TestUpdate(t *testing.T) {
|
||||
tcs := []struct {
|
||||
resource string
|
||||
name string
|
||||
namespace string
|
||||
obj *unstructured.Unstructured
|
||||
path string
|
||||
}{
|
||||
{
|
||||
name: "normal_update",
|
||||
path: "/api/gtest/vtest/rtest/normal_update",
|
||||
obj: getObject("vTest", "rTest", "normal_update"),
|
||||
resource: "rtest",
|
||||
name: "normal_update",
|
||||
path: "/api/gtest/vtest/rtest/normal_update",
|
||||
obj: getObject("vTest", "rTest", "normal_update"),
|
||||
},
|
||||
{
|
||||
resource: "rtest",
|
||||
name: "namespaced_update",
|
||||
namespace: "nstest",
|
||||
path: "/api/gtest/vtest/namespaces/nstest/rtest/namespaced_update",
|
||||
obj: getObject("vTest", "rTest", "namespaced_update"),
|
||||
},
|
||||
{
|
||||
resource: "rtest/srtest",
|
||||
name: "normal_subresource_update",
|
||||
path: "/api/gtest/vtest/rtest/normal_update/srtest",
|
||||
obj: getObject("vTest", "srTest", "normal_update"),
|
||||
},
|
||||
{
|
||||
resource: "rtest/srtest",
|
||||
name: "namespaced_subresource_update",
|
||||
namespace: "nstest",
|
||||
path: "/api/gtest/vtest/namespaces/nstest/rtest/namespaced_update/srtest",
|
||||
obj: getObject("vTest", "srTest", "namespaced_update"),
|
||||
},
|
||||
}
|
||||
for _, tc := range tcs {
|
||||
gv := &schema.GroupVersion{Group: "gtest", Version: "vtest"}
|
||||
resource := &metav1.APIResource{Name: "rtest", Namespaced: len(tc.namespace) != 0}
|
||||
resource := &metav1.APIResource{Name: tc.resource, Namespaced: len(tc.namespace) != 0}
|
||||
cl, srv, err := getClientServer(gv, func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != "PUT" {
|
||||
t.Errorf("Update(%q) got HTTP method %s. wanted PUT", tc.name, r.Method)
|
||||
@ -492,6 +542,7 @@ func TestWatch(t *testing.T) {
|
||||
|
||||
func TestPatch(t *testing.T) {
|
||||
tcs := []struct {
|
||||
resource string
|
||||
name string
|
||||
namespace string
|
||||
patch []byte
|
||||
@ -499,22 +550,39 @@ func TestPatch(t *testing.T) {
|
||||
path string
|
||||
}{
|
||||
{
|
||||
name: "normal_patch",
|
||||
path: "/api/gtest/vtest/rtest/normal_patch",
|
||||
patch: getJSON("vTest", "rTest", "normal_patch"),
|
||||
want: getObject("vTest", "rTest", "normal_patch"),
|
||||
resource: "rtest",
|
||||
name: "normal_patch",
|
||||
path: "/api/gtest/vtest/rtest/normal_patch",
|
||||
patch: getJSON("vTest", "rTest", "normal_patch"),
|
||||
want: getObject("vTest", "rTest", "normal_patch"),
|
||||
},
|
||||
{
|
||||
resource: "rtest",
|
||||
name: "namespaced_patch",
|
||||
namespace: "nstest",
|
||||
path: "/api/gtest/vtest/namespaces/nstest/rtest/namespaced_patch",
|
||||
patch: getJSON("vTest", "rTest", "namespaced_patch"),
|
||||
want: getObject("vTest", "rTest", "namespaced_patch"),
|
||||
},
|
||||
{
|
||||
resource: "rtest/srtest",
|
||||
name: "normal_subresource_patch",
|
||||
path: "/api/gtest/vtest/rtest/normal_subresource_patch/srtest",
|
||||
patch: getJSON("vTest", "srTest", "normal_subresource_patch"),
|
||||
want: getObject("vTest", "srTest", "normal_subresource_patch"),
|
||||
},
|
||||
{
|
||||
resource: "rtest/srtest",
|
||||
name: "namespaced_subresource_patch",
|
||||
namespace: "nstest",
|
||||
path: "/api/gtest/vtest/namespaces/nstest/rtest/namespaced_subresource_patch/srtest",
|
||||
patch: getJSON("vTest", "srTest", "namespaced_subresource_patch"),
|
||||
want: getObject("vTest", "srTest", "namespaced_subresource_patch"),
|
||||
},
|
||||
}
|
||||
for _, tc := range tcs {
|
||||
gv := &schema.GroupVersion{Group: "gtest", Version: "vtest"}
|
||||
resource := &metav1.APIResource{Name: "rtest", Namespaced: len(tc.namespace) != 0}
|
||||
resource := &metav1.APIResource{Name: tc.resource, Namespaced: len(tc.namespace) != 0}
|
||||
cl, srv, err := getClientServer(gv, func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != "PATCH" {
|
||||
t.Errorf("Patch(%q) got HTTP method %s. wanted PATCH", tc.name, r.Method)
|
||||
|
Loading…
Reference in New Issue
Block a user