1
16
17 package scale
18
19 import (
20 "context"
21 "fmt"
22
23 autoscaling "k8s.io/api/autoscaling/v1"
24 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
25 "k8s.io/apimachinery/pkg/runtime"
26 "k8s.io/apimachinery/pkg/runtime/schema"
27 serializer "k8s.io/apimachinery/pkg/runtime/serializer"
28 "k8s.io/apimachinery/pkg/types"
29 "k8s.io/client-go/dynamic"
30 restclient "k8s.io/client-go/rest"
31 )
32
33 var scaleConverter = NewScaleConverter()
34 var codecs = serializer.NewCodecFactory(scaleConverter.Scheme())
35 var parameterScheme = runtime.NewScheme()
36 var dynamicParameterCodec = runtime.NewParameterCodec(parameterScheme)
37
38 var versionV1 = schema.GroupVersion{Version: "v1"}
39
40 func init() {
41 metav1.AddToGroupVersion(parameterScheme, versionV1)
42 }
43
44
45
46
47
48
49 type scaleClient struct {
50 mapper PreferredResourceMapper
51
52 apiPathResolverFunc dynamic.APIPathResolverFunc
53 scaleKindResolver ScaleKindResolver
54 clientBase restclient.Interface
55 }
56
57
58
59
60 func NewForConfig(cfg *restclient.Config, mapper PreferredResourceMapper, resolver dynamic.APIPathResolverFunc, scaleKindResolver ScaleKindResolver) (ScalesGetter, error) {
61
62 cfg.GroupVersion = &schema.GroupVersion{}
63
64 cfg.NegotiatedSerializer = codecs.WithoutConversion()
65 if len(cfg.UserAgent) == 0 {
66 cfg.UserAgent = restclient.DefaultKubernetesUserAgent()
67 }
68
69 client, err := restclient.RESTClientFor(cfg)
70 if err != nil {
71 return nil, err
72 }
73
74 return New(client, mapper, resolver, scaleKindResolver), nil
75 }
76
77
78
79 func New(baseClient restclient.Interface, mapper PreferredResourceMapper, resolver dynamic.APIPathResolverFunc, scaleKindResolver ScaleKindResolver) ScalesGetter {
80 return &scaleClient{
81 mapper: mapper,
82
83 apiPathResolverFunc: resolver,
84 scaleKindResolver: scaleKindResolver,
85 clientBase: baseClient,
86 }
87 }
88
89
90 func (c *scaleClient) apiPathFor(groupVer schema.GroupVersion) string {
91
92
93
94 apiPath := c.apiPathResolverFunc(groupVer.WithKind(""))
95 if apiPath == "" {
96 apiPath = "/api"
97 }
98
99 return restclient.DefaultVersionedAPIPath(apiPath, groupVer)
100 }
101
102
103
104 func (c *scaleClient) pathAndVersionFor(resource schema.GroupResource) (string, schema.GroupVersionResource, error) {
105 gvr, err := c.mapper.ResourceFor(resource.WithVersion(""))
106 if err != nil {
107 return "", gvr, fmt.Errorf("unable to get full preferred group-version-resource for %s: %v", resource.String(), err)
108 }
109
110 groupVer := gvr.GroupVersion()
111
112 return c.apiPathFor(groupVer), gvr, nil
113 }
114
115
116
117 type namespacedScaleClient struct {
118 client *scaleClient
119 namespace string
120 }
121
122
123 func convertToScale(result *restclient.Result) (*autoscaling.Scale, error) {
124 scaleBytes, err := result.Raw()
125 if err != nil {
126 return nil, err
127 }
128 decoder := scaleConverter.codecs.UniversalDecoder(scaleConverter.ScaleVersions()...)
129 rawScaleObj, err := runtime.Decode(decoder, scaleBytes)
130 if err != nil {
131 return nil, err
132 }
133
134
135 scaleObj, err := scaleConverter.ConvertToVersion(rawScaleObj, autoscaling.SchemeGroupVersion)
136 if err != nil {
137 return nil, fmt.Errorf("received an object from a /scale endpoint which was not convertible to autoscaling Scale: %v", err)
138 }
139
140 return scaleObj.(*autoscaling.Scale), nil
141 }
142
143 func (c *scaleClient) Scales(namespace string) ScaleInterface {
144 return &namespacedScaleClient{
145 client: c,
146 namespace: namespace,
147 }
148 }
149
150 func (c *namespacedScaleClient) Get(ctx context.Context, resource schema.GroupResource, name string, opts metav1.GetOptions) (*autoscaling.Scale, error) {
151
152
153
154
155
156 path, gvr, err := c.client.pathAndVersionFor(resource)
157 if err != nil {
158 return nil, fmt.Errorf("unable to get client for %s: %v", resource.String(), err)
159 }
160
161 result := c.client.clientBase.Get().
162 AbsPath(path).
163 NamespaceIfScoped(c.namespace, c.namespace != "").
164 Resource(gvr.Resource).
165 Name(name).
166 SubResource("scale").
167 SpecificallyVersionedParams(&opts, dynamicParameterCodec, versionV1).
168 Do(ctx)
169 if err := result.Error(); err != nil {
170 return nil, err
171 }
172
173 return convertToScale(&result)
174 }
175
176 func (c *namespacedScaleClient) Update(ctx context.Context, resource schema.GroupResource, scale *autoscaling.Scale, opts metav1.UpdateOptions) (*autoscaling.Scale, error) {
177 path, gvr, err := c.client.pathAndVersionFor(resource)
178 if err != nil {
179 return nil, fmt.Errorf("unable to get client for %s: %v", resource.String(), err)
180 }
181
182
183
184
185
186
187 desiredGVK, err := c.client.scaleKindResolver.ScaleForResource(gvr)
188 if err != nil {
189 return nil, fmt.Errorf("could not find proper group-version for scale subresource of %s: %v", gvr.String(), err)
190 }
191
192
193 scaleUpdate, err := scaleConverter.ConvertToVersion(scale, desiredGVK.GroupVersion())
194 if err != nil {
195 return nil, fmt.Errorf("could not convert scale update to external Scale: %v", err)
196 }
197 encoder := scaleConverter.codecs.LegacyCodec(desiredGVK.GroupVersion())
198 scaleUpdateBytes, err := runtime.Encode(encoder, scaleUpdate)
199 if err != nil {
200 return nil, fmt.Errorf("could not encode scale update to external Scale: %v", err)
201 }
202
203 result := c.client.clientBase.Put().
204 AbsPath(path).
205 NamespaceIfScoped(c.namespace, c.namespace != "").
206 Resource(gvr.Resource).
207 Name(scale.Name).
208 SubResource("scale").
209 SpecificallyVersionedParams(&opts, dynamicParameterCodec, versionV1).
210 Body(scaleUpdateBytes).
211 Do(ctx)
212 if err := result.Error(); err != nil {
213
214
215
216 return nil, err
217 }
218
219 return convertToScale(&result)
220 }
221
222 func (c *namespacedScaleClient) Patch(ctx context.Context, gvr schema.GroupVersionResource, name string, pt types.PatchType, data []byte, opts metav1.PatchOptions) (*autoscaling.Scale, error) {
223 groupVersion := gvr.GroupVersion()
224 result := c.client.clientBase.Patch(pt).
225 AbsPath(c.client.apiPathFor(groupVersion)).
226 NamespaceIfScoped(c.namespace, c.namespace != "").
227 Resource(gvr.Resource).
228 Name(name).
229 SubResource("scale").
230 SpecificallyVersionedParams(&opts, dynamicParameterCodec, versionV1).
231 Body(data).
232 Do(ctx)
233 if err := result.Error(); err != nil {
234 return nil, err
235 }
236
237 return convertToScale(&result)
238 }
239
View as plain text