Skip to content

Commit fc404d7

Browse files
committed
Make plural handling more robust
1 parent 54308fe commit fc404d7

File tree

4 files changed

+32
-42
lines changed

4 files changed

+32
-42
lines changed
Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,16 @@
11
evaluate:
22
type: "shell"
33
timeout: 2500
4-
shell:
4+
shell:
55
entrypoint: "python"
6-
command:
6+
command:
77
- "/evaluate.py"
88
metric:
99
type: "shell"
1010
timeout: 2500
11-
shell:
11+
shell:
1212
entrypoint: "python"
13-
command:
13+
command:
1414
- "/metric.py"
15-
runMode: "per-pod"
15+
runMode: "per-pod"
16+
logVerbosity: 3

internal/resourceclient/resourceclient.go

Lines changed: 6 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,12 @@ package resourceclient
1818

1919
import (
2020
"context"
21-
"fmt"
22-
23-
"strings"
2421

2522
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2623
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
2724
"k8s.io/apimachinery/pkg/runtime/schema"
2825
"k8s.io/client-go/dynamic"
26+
"k8s.io/client-go/restmapper"
2927
)
3028

3129
// Client provides methods for retrieving arbitrary Kubernetes resources, returned as generalised metav1.Object, which
@@ -37,31 +35,21 @@ type Client interface {
3735
// UnstructuredClient is an implementation of the arbitrary resource client that uses a dynamic Kubernetes interface,
3836
// retrieving unstructured k8s objects and converting them to metav1.Object
3937
type UnstructuredClient struct {
40-
Dynamic dynamic.Interface
38+
Dynamic dynamic.Interface
39+
RESTMapper restmapper.DeferredDiscoveryRESTMapper
4140
}
4241

4342
// Get takes descriptors of a Kubernetes object (api version, kind, name, namespace) and fetches the matching object,
4443
// returning it as an unstructured Kubernetes resource
4544
func (u *UnstructuredClient) Get(apiVersion string, kind string, name string, namespace string) (*unstructured.Unstructured, error) {
46-
// TODO: update this to be less hacky
47-
// Convert to plural and lowercase
48-
kindPlural := fmt.Sprintf("%ss", strings.ToLower(kind))
49-
50-
// Parse group version
51-
resourceGV, err := schema.ParseGroupVersion(apiVersion)
45+
resourceGK := schema.FromAPIVersionAndKind(apiVersion, kind)
46+
mapping, err := u.RESTMapper.RESTMapping(resourceGK.GroupKind(), resourceGK.Version)
5247
if err != nil {
5348
return nil, err
5449
}
5550

56-
// Build GVR
57-
resourceGVR := schema.GroupVersionResource{
58-
Group: resourceGV.Group,
59-
Version: resourceGV.Version,
60-
Resource: kindPlural,
61-
}
62-
6351
// Get resource
64-
resource, err := u.Dynamic.Resource(resourceGVR).Namespace(namespace).Get(context.Background(), name, metav1.GetOptions{})
52+
resource, err := u.Dynamic.Resource(mapping.Resource).Namespace(namespace).Get(context.Background(), name, metav1.GetOptions{})
6553
if err != nil {
6654
return nil, err
6755
}

internal/scaling/scaling.go

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ import (
2222
"context"
2323
"encoding/json"
2424
"fmt"
25-
"strings"
2625
"time"
2726

2827
"github.com/golang/glog"
@@ -34,6 +33,7 @@ import (
3433
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
3534
"k8s.io/apimachinery/pkg/runtime/schema"
3635
"k8s.io/apimachinery/pkg/types"
36+
"k8s.io/client-go/restmapper"
3737
k8sscale "k8s.io/client-go/scale"
3838
)
3939

@@ -49,6 +49,7 @@ type Scale struct {
4949
Config *config.Config
5050
Execute execute.Executer
5151
StabilizationEvaluations []TimestampedEvaluation
52+
RESTMapper restmapper.DeferredDiscoveryRESTMapper
5253
}
5354

5455
// TimestampedEvaluation is used to associate an evaluation with a timestamp, used in stabilizing evaluations
@@ -133,14 +134,12 @@ func (s *Scale) Scale(info scale.Info, scaleResource *autoscalingv1.Scale) (*eva
133134
glog.V(0).Infof("Rescaling from %d to %d replicas", currentReplicas, targetReplicas)
134135
glog.V(3).Infoln("Attempting to parse group version")
135136
// Parse group version
136-
resourceGV, err := schema.ParseGroupVersion(info.ScaleTargetRef.APIVersion)
137+
resourceGK := schema.FromAPIVersionAndKind(info.ScaleTargetRef.APIVersion, info.ScaleTargetRef.Kind)
138+
mapping, err := s.RESTMapper.RESTMapping(resourceGK.GroupKind(), resourceGK.Version)
137139
if err != nil {
138140
return nil, err
139141
}
140-
glog.V(3).Infof("Group version parsed: %+v", resourceGV)
141-
142-
kindPlural := fmt.Sprintf("%ss", strings.ToLower(info.ScaleTargetRef.Kind))
143-
targetGVR := resourceGV.WithResource(kindPlural)
142+
glog.V(3).Infof("Group version parsed: %+v", mapping.Resource)
144143

145144
glog.V(3).Infoln("Attempting to apply scaling changes to resource")
146145

@@ -149,7 +148,7 @@ func (s *Scale) Scale(info scale.Info, scaleResource *autoscalingv1.Scale) (*eva
149148

150149
glog.V(3).Infof("Applying patch: %s to resource %s in namespace %s", string(patch), scaleResource.Name, info.Namespace)
151150

152-
_, err = s.Scaler.Scales(info.Namespace).Patch(context.Background(), targetGVR, scaleResource.Name, types.StrategicMergePatchType, patch, metav1.PatchOptions{})
151+
_, err = s.Scaler.Scales(info.Namespace).Patch(context.Background(), mapping.Resource, scaleResource.Name, types.StrategicMergePatchType, patch, metav1.PatchOptions{})
153152
if err != nil {
154153
return nil, fmt.Errorf("failed to apply scaling changes to resource: %w", err)
155154
}

main.go

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,8 @@ import (
5151
"github.com/jthomperoo/k8shorizmetrics/v3"
5252
"github.com/jthomperoo/k8shorizmetrics/v3/metricsclient"
5353
"github.com/jthomperoo/k8shorizmetrics/v3/podsclient"
54+
"k8s.io/client-go/discovery"
55+
memory "k8s.io/client-go/discovery/cached"
5456
"k8s.io/client-go/dynamic"
5557
"k8s.io/client-go/kubernetes"
5658
"k8s.io/client-go/rest"
@@ -123,11 +125,11 @@ func main() {
123125
glog.Fatalf("Fail to set up Kubernetes dynamic client: %s", err)
124126
}
125127

126-
// Get group resources
127-
groupResources, err := restmapper.GetAPIGroupResources(clientset.Discovery())
128+
discoveryClient, err := discovery.NewDiscoveryClientForConfig(clusterConfig)
128129
if err != nil {
129-
glog.Fatalf("Fail get group resources: %s", err)
130+
glog.Fatalf("Fail to set up Kubernetes discovery client: %s", err)
130131
}
132+
cachedDiscoveryClient := memory.NewMemCacheClient(discoveryClient)
131133

132134
// Set logging level
133135
err = flag.Lookup("v").Value.Set(strconv.Itoa(int(loadedConfig.LogVerbosity)))
@@ -140,20 +142,19 @@ func main() {
140142

141143
// Set up resource client
142144
resourceClient := &resourceclient.UnstructuredClient{
143-
Dynamic: dynamicClient,
145+
Dynamic: dynamicClient,
146+
RESTMapper: *restmapper.NewDeferredDiscoveryRESTMapper(cachedDiscoveryClient),
144147
}
145148

146149
scaleClient := k8sscale.New(
147150
clientset.RESTClient(),
148-
restmapper.NewDiscoveryRESTMapper(groupResources),
151+
restmapper.NewDeferredDiscoveryRESTMapper(cachedDiscoveryClient),
149152
dynamic.LegacyAPIPathResolverFunc,
150-
k8sscale.NewDiscoveryScaleKindResolver(
151-
clientset.Discovery(),
152-
),
153+
k8sscale.NewDiscoveryScaleKindResolver(cachedDiscoveryClient),
153154
)
154155

155156
// Create K8s metric gatherer, with required clients and configuration
156-
metricsclient := metricsclient.NewClient(clusterConfig, clientset.Discovery())
157+
metricsclient := metricsclient.NewClient(clusterConfig, cachedDiscoveryClient)
157158
podsclient := &podsclient.OnDemandPodLister{
158159
Clientset: clientset,
159160
}
@@ -178,9 +179,10 @@ func main() {
178179

179180
// Set up scaling client
180181
scaler := &scaling.Scale{
181-
Scaler: scaleClient,
182-
Config: loadedConfig,
183-
Execute: combinedExecute,
182+
Scaler: scaleClient,
183+
Config: loadedConfig,
184+
Execute: combinedExecute,
185+
RESTMapper: *restmapper.NewDeferredDiscoveryRESTMapper(cachedDiscoveryClient),
184186
}
185187

186188
// Set up metric gathering

0 commit comments

Comments
 (0)