mirror of
https://github.com/kubernetes/client-go.git
synced 2025-06-28 16:07:29 +00:00
Merge pull request #80699 from knight42/feat/patch-scale
feat: add Patch method to ScaleInterface Kubernetes-commit: 182ae7b9a47f14f55c3fed5f8f948ace647263e9
This commit is contained in:
commit
62f300f03a
2
Godeps/Godeps.json
generated
2
Godeps/Godeps.json
generated
@ -264,7 +264,7 @@
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/apimachinery",
|
||||
"Rev": "ac5d3b819fc6"
|
||||
"Rev": "423f5d784010"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/gengo",
|
||||
|
4
go.mod
4
go.mod
@ -27,7 +27,7 @@ require (
|
||||
golang.org/x/time v0.0.0-20161028155119-f51c12702a4d
|
||||
google.golang.org/appengine v1.5.0 // indirect
|
||||
k8s.io/api v0.0.0-20190808180749-077ce48e77da
|
||||
k8s.io/apimachinery v0.0.0-20190808180622-ac5d3b819fc6
|
||||
k8s.io/apimachinery v0.0.0-20190809020650-423f5d784010
|
||||
k8s.io/klog v0.3.1
|
||||
k8s.io/utils v0.0.0-20190801114015-581e00157fb1
|
||||
sigs.k8s.io/yaml v1.1.0
|
||||
@ -41,5 +41,5 @@ replace (
|
||||
golang.org/x/text => golang.org/x/text v0.3.1-0.20181227161524-e6919f6577db
|
||||
golang.org/x/tools => golang.org/x/tools v0.0.0-20190313210603-aa82965741a9
|
||||
k8s.io/api => k8s.io/api v0.0.0-20190808180749-077ce48e77da
|
||||
k8s.io/apimachinery => k8s.io/apimachinery v0.0.0-20190808180622-ac5d3b819fc6
|
||||
k8s.io/apimachinery => k8s.io/apimachinery v0.0.0-20190809020650-423f5d784010
|
||||
)
|
||||
|
2
go.sum
2
go.sum
@ -127,7 +127,7 @@ gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
k8s.io/api v0.0.0-20190808180749-077ce48e77da/go.mod h1:irWZZ8fkUYB2+fwyvjN9QMt0m5/1PYsJc1eJElzGHeM=
|
||||
k8s.io/apimachinery v0.0.0-20190808180622-ac5d3b819fc6/go.mod h1:Waf/xTS2FGRrgXCkO5FP3XxTOWh0qLf2QhL1qFZZ/R8=
|
||||
k8s.io/apimachinery v0.0.0-20190809020650-423f5d784010/go.mod h1:Waf/xTS2FGRrgXCkO5FP3XxTOWh0qLf2QhL1qFZZ/R8=
|
||||
k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
|
||||
k8s.io/klog v0.0.0-20181102134211-b9b56d5dfc92/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=
|
||||
k8s.io/klog v0.3.0/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=
|
||||
|
100
scale/client.go
100
scale/client.go
@ -23,6 +23,7 @@ import (
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
serializer "k8s.io/apimachinery/pkg/runtime/serializer"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/client-go/dynamic"
|
||||
restclient "k8s.io/client-go/rest"
|
||||
)
|
||||
@ -75,6 +76,19 @@ func New(baseClient restclient.Interface, mapper PreferredResourceMapper, resolv
|
||||
}
|
||||
}
|
||||
|
||||
// apiPathFor returns the absolute api path for the given GroupVersion
|
||||
func (c *scaleClient) apiPathFor(groupVer schema.GroupVersion) string {
|
||||
// we need to set the API path based on GroupVersion (defaulting to the legacy path if none is set)
|
||||
// TODO: we "cheat" here since the API path really only depends on group ATM, but this should
|
||||
// *probably* take GroupVersionResource and not GroupVersionKind.
|
||||
apiPath := c.apiPathResolverFunc(groupVer.WithKind(""))
|
||||
if apiPath == "" {
|
||||
apiPath = "/api"
|
||||
}
|
||||
|
||||
return restclient.DefaultVersionedAPIPath(apiPath, groupVer)
|
||||
}
|
||||
|
||||
// pathAndVersionFor returns the appropriate base path and the associated full GroupVersionResource
|
||||
// for the given GroupResource
|
||||
func (c *scaleClient) pathAndVersionFor(resource schema.GroupResource) (string, schema.GroupVersionResource, error) {
|
||||
@ -85,17 +99,7 @@ func (c *scaleClient) pathAndVersionFor(resource schema.GroupResource) (string,
|
||||
|
||||
groupVer := gvr.GroupVersion()
|
||||
|
||||
// we need to set the API path based on GroupVersion (defaulting to the legacy path if none is set)
|
||||
// TODO: we "cheat" here since the API path really only depends on group ATM, but this should
|
||||
// *probably* take GroupVersionResource and not GroupVersionKind.
|
||||
apiPath := c.apiPathResolverFunc(groupVer.WithKind(""))
|
||||
if apiPath == "" {
|
||||
apiPath = "/api"
|
||||
}
|
||||
|
||||
path := restclient.DefaultVersionedAPIPath(apiPath, groupVer)
|
||||
|
||||
return path, gvr, nil
|
||||
return c.apiPathFor(groupVer), gvr, nil
|
||||
}
|
||||
|
||||
// namespacedScaleClient is an ScaleInterface for fetching
|
||||
@ -105,6 +109,27 @@ type namespacedScaleClient struct {
|
||||
namespace string
|
||||
}
|
||||
|
||||
// convertToScale converts the response body to autoscaling/v1.Scale
|
||||
func convertToScale(result *restclient.Result) (*autoscaling.Scale, error) {
|
||||
scaleBytes, err := result.Raw()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
decoder := scaleConverter.codecs.UniversalDecoder(scaleConverter.ScaleVersions()...)
|
||||
rawScaleObj, err := runtime.Decode(decoder, scaleBytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// convert whatever this is to autoscaling/v1.Scale
|
||||
scaleObj, err := scaleConverter.ConvertToVersion(rawScaleObj, autoscaling.SchemeGroupVersion)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("received an object from a /scale endpoint which was not convertible to autoscaling Scale: %v", err)
|
||||
}
|
||||
|
||||
return scaleObj.(*autoscaling.Scale), nil
|
||||
}
|
||||
|
||||
func (c *scaleClient) Scales(namespace string) ScaleInterface {
|
||||
return &namespacedScaleClient{
|
||||
client: c,
|
||||
@ -134,23 +159,7 @@ func (c *namespacedScaleClient) Get(resource schema.GroupResource, name string)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
scaleBytes, err := result.Raw()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
decoder := scaleConverter.codecs.UniversalDecoder(scaleConverter.ScaleVersions()...)
|
||||
rawScaleObj, err := runtime.Decode(decoder, scaleBytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// convert whatever this is to autoscaling/v1.Scale
|
||||
scaleObj, err := scaleConverter.ConvertToVersion(rawScaleObj, autoscaling.SchemeGroupVersion)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("received an object from a /scale endpoint which was not convertible to autoscaling Scale: %v", err)
|
||||
}
|
||||
|
||||
return scaleObj.(*autoscaling.Scale), nil
|
||||
return convertToScale(&result)
|
||||
}
|
||||
|
||||
func (c *namespacedScaleClient) Update(resource schema.GroupResource, scale *autoscaling.Scale) (*autoscaling.Scale, error) {
|
||||
@ -195,21 +204,22 @@ func (c *namespacedScaleClient) Update(resource schema.GroupResource, scale *aut
|
||||
return nil, err
|
||||
}
|
||||
|
||||
scaleBytes, err := result.Raw()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
decoder := scaleConverter.codecs.UniversalDecoder(scaleConverter.ScaleVersions()...)
|
||||
rawScaleObj, err := runtime.Decode(decoder, scaleBytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// convert whatever this is back to autoscaling/v1.Scale
|
||||
scaleObj, err := scaleConverter.ConvertToVersion(rawScaleObj, autoscaling.SchemeGroupVersion)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("received an object from a /scale endpoint which was not convertible to autoscaling Scale: %v", err)
|
||||
}
|
||||
|
||||
return scaleObj.(*autoscaling.Scale), err
|
||||
return convertToScale(&result)
|
||||
}
|
||||
|
||||
func (c *namespacedScaleClient) Patch(gvr schema.GroupVersionResource, name string, pt types.PatchType, data []byte) (*autoscaling.Scale, error) {
|
||||
groupVersion := gvr.GroupVersion()
|
||||
result := c.client.clientBase.Patch(pt).
|
||||
AbsPath(c.client.apiPathFor(groupVersion)).
|
||||
Namespace(c.namespace).
|
||||
Resource(gvr.Resource).
|
||||
Name(name).
|
||||
SubResource("scale").
|
||||
Body(data).
|
||||
Do()
|
||||
if err := result.Error(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return convertToScale(&result)
|
||||
}
|
||||
|
@ -25,9 +25,11 @@ import (
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
jsonpatch "github.com/evanphx/json-patch"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
fakedisco "k8s.io/client-go/discovery/fake"
|
||||
"k8s.io/client-go/dynamic"
|
||||
fakerest "k8s.io/client-go/rest/fake"
|
||||
@ -197,6 +199,37 @@ func fakeScaleClient(t *testing.T) (ScalesGetter, []schema.GroupResource) {
|
||||
return nil, err
|
||||
}
|
||||
return &http.Response{StatusCode: 200, Header: defaultHeaders(), Body: bytesBody(res)}, nil
|
||||
case "PATCH":
|
||||
body, err := ioutil.ReadAll(req.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
originScale, err := json.Marshal(scale)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var res []byte
|
||||
contentType := req.Header.Get("Content-Type")
|
||||
pt := types.PatchType(contentType)
|
||||
switch pt {
|
||||
case types.MergePatchType:
|
||||
res, err = jsonpatch.MergePatch(originScale, body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
case types.JSONPatchType:
|
||||
patch, err := jsonpatch.DecodePatch(body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
res, err = patch.Apply(originScale)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
default:
|
||||
return nil, fmt.Errorf("invalid patch type")
|
||||
}
|
||||
return &http.Response{StatusCode: 200, Header: defaultHeaders(), Body: bytesBody(res)}, nil
|
||||
default:
|
||||
return nil, fmt.Errorf("unexpected request for URL %q with method %q", req.URL.String(), req.Method)
|
||||
}
|
||||
@ -213,10 +246,10 @@ func fakeScaleClient(t *testing.T) (ScalesGetter, []schema.GroupResource) {
|
||||
client := New(fakeClient, restMapper, dynamic.LegacyAPIPathResolverFunc, resolver)
|
||||
|
||||
groupResources := []schema.GroupResource{
|
||||
{Group: corev1.GroupName, Resource: "replicationcontroller"},
|
||||
{Group: extv1beta1.GroupName, Resource: "replicaset"},
|
||||
{Group: appsv1beta2.GroupName, Resource: "deployment"},
|
||||
{Group: "cheese.testing.k8s.io", Resource: "cheddar"},
|
||||
{Group: corev1.GroupName, Resource: "replicationcontrollers"},
|
||||
{Group: extv1beta1.GroupName, Resource: "replicasets"},
|
||||
{Group: appsv1beta2.GroupName, Resource: "deployments"},
|
||||
{Group: "cheese.testing.k8s.io", Resource: "cheddars"},
|
||||
}
|
||||
|
||||
return client, groupResources
|
||||
@ -277,3 +310,55 @@ func TestUpdateScale(t *testing.T) {
|
||||
assert.Equal(t, expectedScale, scale, "should have returned the expected scale for %s", groupResource.String())
|
||||
}
|
||||
}
|
||||
|
||||
func TestPatchScale(t *testing.T) {
|
||||
scaleClient, groupResources := fakeScaleClient(t)
|
||||
expectedScale := &autoscalingv1.Scale{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: "Scale",
|
||||
APIVersion: autoscalingv1.SchemeGroupVersion.String(),
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "foo",
|
||||
},
|
||||
Spec: autoscalingv1.ScaleSpec{Replicas: 5},
|
||||
Status: autoscalingv1.ScaleStatus{
|
||||
Replicas: 10,
|
||||
Selector: "foo=bar",
|
||||
},
|
||||
}
|
||||
gvrs := make([]schema.GroupVersionResource, 0, len(groupResources))
|
||||
for _, gr := range groupResources {
|
||||
switch gr.Group {
|
||||
case corev1.GroupName:
|
||||
gvrs = append(gvrs, gr.WithVersion(corev1.SchemeGroupVersion.Version))
|
||||
case extv1beta1.GroupName:
|
||||
gvrs = append(gvrs, gr.WithVersion(extv1beta1.SchemeGroupVersion.Version))
|
||||
case appsv1beta2.GroupName:
|
||||
gvrs = append(gvrs, gr.WithVersion(appsv1beta2.SchemeGroupVersion.Version))
|
||||
default:
|
||||
// Group cheese.testing.k8s.io
|
||||
gvrs = append(gvrs, gr.WithVersion("v27alpha15"))
|
||||
}
|
||||
}
|
||||
|
||||
patch := []byte(`{"spec":{"replicas":5}}`)
|
||||
for _, gvr := range gvrs {
|
||||
scale, err := scaleClient.Scales("default").Patch(gvr, "foo", types.MergePatchType, patch)
|
||||
if !assert.NoError(t, err, "should have been able to fetch a scale for %s", gvr.String()) {
|
||||
continue
|
||||
}
|
||||
assert.NotNil(t, scale, "should have returned a non-nil scale for %s", gvr.String())
|
||||
assert.Equal(t, expectedScale, scale, "should have returned the expected scale for %s", gvr.String())
|
||||
}
|
||||
|
||||
patch = []byte(`[{"op":"replace","path":"/spec/replicas","value":5}]`)
|
||||
for _, gvr := range gvrs {
|
||||
scale, err := scaleClient.Scales("default").Patch(gvr, "foo", types.JSONPatchType, patch)
|
||||
if !assert.NoError(t, err, "should have been able to fetch a scale for %s", gvr.String()) {
|
||||
continue
|
||||
}
|
||||
assert.NotNil(t, scale, "should have returned a non-nil scale for %s", gvr.String())
|
||||
assert.Equal(t, expectedScale, scale, "should have returned the expected scale for %s", gvr.String())
|
||||
}
|
||||
}
|
||||
|
@ -22,6 +22,7 @@ package fake
|
||||
import (
|
||||
autoscalingapi "k8s.io/api/autoscaling/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/client-go/scale"
|
||||
"k8s.io/client-go/testing"
|
||||
)
|
||||
@ -63,5 +64,15 @@ func (f *fakeNamespacedScaleClient) Update(resource schema.GroupResource, scale
|
||||
}
|
||||
|
||||
return obj.(*autoscalingapi.Scale), err
|
||||
|
||||
}
|
||||
|
||||
func (f *fakeNamespacedScaleClient) Patch(gvr schema.GroupVersionResource, name string, pt types.PatchType, patch []byte) (*autoscalingapi.Scale, error) {
|
||||
obj, err := f.fake.
|
||||
Invokes(testing.NewPatchSubresourceAction(gvr, f.namespace, name, pt, patch, "scale"), &autoscalingapi.Scale{})
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return obj.(*autoscalingapi.Scale), err
|
||||
}
|
||||
|
@ -19,6 +19,7 @@ package scale
|
||||
import (
|
||||
autoscalingapi "k8s.io/api/autoscaling/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
)
|
||||
|
||||
// ScalesGetter can produce a ScaleInterface
|
||||
@ -36,4 +37,7 @@ type ScaleInterface interface {
|
||||
|
||||
// Update updates the scale of the given scalable resource.
|
||||
Update(resource schema.GroupResource, scale *autoscalingapi.Scale) (*autoscalingapi.Scale, error)
|
||||
|
||||
// Patch patches the scale of the given scalable resource.
|
||||
Patch(gvr schema.GroupVersionResource, name string, pt types.PatchType, data []byte) (*autoscalingapi.Scale, error)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user