Skip to content

Commit 0815c3e

Browse files
committed
Early custom CRD support
1 parent 8f936b6 commit 0815c3e

File tree

3 files changed

+86
-20
lines changed

3 files changed

+86
-20
lines changed

client/client.go

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,15 @@ import (
1212
"k8s.io/apimachinery/pkg/api/meta"
1313
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
1414
unstructuredv1 "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
15+
"k8s.io/apimachinery/pkg/labels"
1516
"k8s.io/apimachinery/pkg/runtime"
1617
"k8s.io/apimachinery/pkg/runtime/schema"
1718
vpav1 "k8s.io/autoscaler/vertical-pod-autoscaler/pkg/apis/autoscaling.k8s.io/v1"
1819
"k8s.io/cli-runtime/pkg/resource"
1920
"k8s.io/client-go/discovery"
2021
"k8s.io/client-go/dynamic"
2122
corev1client "k8s.io/client-go/kubernetes/typed/core/v1"
23+
"k8s.io/client-go/scale"
2224
"k8s.io/client-go/tools/pager"
2325
)
2426

@@ -44,6 +46,7 @@ type Interface interface {
4446
ListVPAResources(context.Context, ListOptions) ([]*vpav1.VerticalPodAutoscaler, error)
4547
GetVPATarget(context.Context, *autoscalingv1.CrossVersionObjectReference, string) (*unstructuredv1.Unstructured, error)
4648
ListDependentPods(ctx context.Context, targetMeta metav1.ObjectMeta, labelSelector string) ([]*corev1.Pod, error)
49+
GetLabelSelectorFromResource(groupKind schema.GroupKind, namespace, name string) (labels.Selector, error)
4750
}
4851

4952
var _ Interface = (*client)(nil)
@@ -56,6 +59,7 @@ type client struct {
5659
discoveryClient discovery.DiscoveryInterface
5760
coreClient *corev1client.CoreV1Client
5861
mapper meta.RESTMapper
62+
scaleNamespacer scale.ScalesGetter
5963

6064
// lock during lazy init of the client
6165
sync.Mutex
@@ -222,6 +226,33 @@ func (c *client) ListDependentPods(ctx context.Context, targetMeta metav1.Object
222226
return pods, nil
223227
}
224228

229+
func (c *client) GetLabelSelectorFromResource(groupKind schema.GroupKind, namespace, name string) (labels.Selector, error) {
230+
mappings, err := c.mapper.RESTMappings(groupKind)
231+
if err != nil {
232+
return nil, err
233+
}
234+
235+
var lastError error
236+
for _, mapping := range mappings {
237+
groupResource := mapping.Resource.GroupResource()
238+
scale, err := c.scaleNamespacer.Scales(namespace).Get(context.TODO(), groupResource, name, metav1.GetOptions{})
239+
if err == nil {
240+
if scale.Status.Selector == "" {
241+
return nil, fmt.Errorf("Resource %s/%s has an empty selector for scale sub-resource", namespace, name)
242+
}
243+
selector, err := labels.Parse(scale.Status.Selector)
244+
if err != nil {
245+
return nil, err
246+
}
247+
return selector, nil
248+
}
249+
lastError = err
250+
}
251+
252+
// nothing found, apparently the resource does not support scale (or we lack RBAC)
253+
return nil, lastError
254+
}
255+
225256
// referenceMatchController returns whether the given
226257
// OwnerReference matches a target controller.
227258
func referenceMatchController(ref metav1.OwnerReference, targetMeta metav1.ObjectMeta) bool {

client/flags.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@ import (
77
"github.com/spf13/pflag"
88
"k8s.io/cli-runtime/pkg/genericclioptions"
99
"k8s.io/client-go/dynamic"
10+
kube_client "k8s.io/client-go/kubernetes"
1011
corev1client "k8s.io/client-go/kubernetes/typed/core/v1"
12+
"k8s.io/client-go/scale"
1113
"k8s.io/kubectl/pkg/cmd/get"
1214
cmdutil "k8s.io/kubectl/pkg/cmd/util"
1315
"k8s.io/kubectl/pkg/util"
@@ -107,12 +109,19 @@ func (f *Flags) NewClient() (Interface, error) {
107109
if err != nil {
108110
return nil, err
109111
}
112+
r := scale.NewDiscoveryScaleKindResolver(dis)
113+
114+
kubeClient := kube_client.NewForConfigOrDie(config)
115+
restClient := kubeClient.CoreV1().RESTClient()
116+
scaleNamespacer := scale.New(restClient, m, dynamic.LegacyAPIPathResolverFunc, r)
117+
110118
c := &client{
111119
flags: f,
112120
dynamicClient: dyn,
113121
discoveryClient: dis,
114122
coreClient: pc,
115123
mapper: m,
124+
scaleNamespacer: scaleNamespacer,
116125
}
117126
return c, nil
118127
}

vpa/target.go

Lines changed: 46 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
"k8s.io/apimachinery/pkg/api/resource"
1212
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
1313
unstructuredv1 "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
14+
"k8s.io/apimachinery/pkg/labels"
1415
"k8s.io/apimachinery/pkg/runtime"
1516
"k8s.io/apimachinery/pkg/runtime/schema"
1617

@@ -53,35 +54,60 @@ func NewTargetController(c client.Interface, ref *autoscalingv1.CrossVersionObje
5354
}
5455
kind := obj.GetKind()
5556

57+
isWellKnown := false
58+
5659
switch wellKnownControllerKind(kind) {
5760
case cj, ds, deploy, job, rs, rc, sts, ro:
61+
isWellKnown = true
5862
case node:
5963
// Some pods specify nodes as their owners,
6064
// but they aren't valid controllers that
6165
// the VPA supports, so we just skip them.
6266
return nil, fmt.Errorf("node is not a valid target")
6367
default:
64-
return nil, fmt.Errorf("unsupported target kind: %s", kind)
65-
}
66-
tc := &TargetController{
67-
Name: obj.GetName(),
68-
Namespace: obj.GetNamespace(),
69-
GroupVersionKind: obj.GetObjectKind().GroupVersionKind(),
70-
controllerKind: wellKnownControllerKind(kind),
71-
controllerObj: obj,
72-
}
73-
tc.podSpec, err = resolvePodSpec(obj)
74-
if err != nil {
75-
return nil, err
68+
isWellKnown = false
7669
}
77-
labelSelector, err := resolveLabelSelector(obj)
78-
if err != nil {
79-
return nil, err
80-
}
81-
selector, err := metav1.LabelSelectorAsSelector(labelSelector)
82-
if err != nil {
83-
return nil, err
70+
var tc *TargetController
71+
var selector labels.Selector
72+
73+
if isWellKnown {
74+
tc = &TargetController{
75+
Name: obj.GetName(),
76+
Namespace: obj.GetNamespace(),
77+
GroupVersionKind: obj.GetObjectKind().GroupVersionKind(),
78+
controllerKind: wellKnownControllerKind(kind),
79+
controllerObj: obj,
80+
}
81+
tc.podSpec, err = resolvePodSpec(obj)
82+
if err != nil {
83+
return nil, err
84+
}
85+
86+
labelSelector, err := resolveLabelSelector(obj)
87+
if err != nil {
88+
return nil, err
89+
}
90+
91+
selector, err = metav1.LabelSelectorAsSelector(labelSelector)
92+
if err != nil {
93+
return nil, err
94+
}
95+
96+
} else {
97+
tc = &TargetController{
98+
Name: obj.GetName(),
99+
Namespace: obj.GetNamespace(),
100+
GroupVersionKind: obj.GetObjectKind().GroupVersionKind(),
101+
controllerKind: wellKnownControllerKind(kind),
102+
controllerObj: obj,
103+
}
104+
selector, err = c.GetLabelSelectorFromResource(tc.GroupVersionKind.GroupKind(), tc.Namespace, tc.Name)
105+
106+
if err != nil {
107+
return nil, err
108+
}
84109
}
110+
85111
// The PodSpec template defined by a controller might not represent
86112
// the final spec of the pods. For example, a LimitRanger controller
87113
// could change the spec of pods to set default resource requests and
@@ -104,7 +130,7 @@ func NewTargetController(c client.Interface, ref *autoscalingv1.CrossVersionObje
104130
}
105131
if len(pods) != 0 {
106132
p := pods[0]
107-
if !reflect.DeepEqual(p.Spec.Containers, tc.podSpec.Containers) {
133+
if tc.podSpec == nil || !reflect.DeepEqual(p.Spec.Containers, tc.podSpec.Containers) {
108134
tc.podSpec = &p.Spec
109135
}
110136
}

0 commit comments

Comments
 (0)