Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 14 additions & 1 deletion vertical-pod-autoscaler/pkg/target/fetcher.go
Original file line number Diff line number Diff line change
Expand Up @@ -157,13 +157,26 @@ func getLabelSelector(informer cache.SharedIndexInformer, kind, namespace, name
case (*batchv1.Job):
return metav1.LabelSelectorAsSelector(apiObj.Spec.Selector)
case (*batchv1.CronJob):
return metav1.LabelSelectorAsSelector(metav1.SetAsLabelSelector(apiObj.Spec.JobTemplate.Spec.Template.Labels))
return getLabelSelectorFromCronJob(apiObj)
case (*corev1.ReplicationController):
return metav1.LabelSelectorAsSelector(metav1.SetAsLabelSelector(apiObj.Spec.Selector))
}
return nil, errors.New("don't know how to read label selector")
}

// getLabelSelectorFromCronJob returns the selector used to match pods for a VPA targeting this CronJob.
// Omitted template labels deserialize as nil; SetAsLabelSelector(nil) yields a nil LabelSelector and
// LabelSelectorAsSelector(nil) is labels.Nothing(), which matches no pods (issue #9483). Controller
// identity is already enforced by targetRef name/kind elsewhere, so an empty template label set
// means any pod labels for this CronJob's workload.
func getLabelSelectorFromCronJob(apiObj *batchv1.CronJob) (labels.Selector, error) {
templateLabels := apiObj.Spec.JobTemplate.Spec.Template.Labels
if len(templateLabels) == 0 {
return labels.Everything(), nil
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure that labels.Everything() is the right solution here. I'm pretty sure this will match all pods in that namespace, which may cause performance issues.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree. in large clusters this can be very problematic.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I never thought in that direction thanks for highlighting this aspect @adrianmoisey @omerap12
what could be the right approach here ?can you please help here ?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the original issue where we discussed this, we concluded to document the behaviour, rather than fix it.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yup. @adrianmoisey , will it be beneficial if we would print something when user apply VPA with targetRef pointing at CronJob/Job?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we can. Returning a warning doesn't feel right for this

}
return metav1.LabelSelectorAsSelector(metav1.SetAsLabelSelector(templateLabels))
}

func (f *vpaTargetSelectorFetcher) getLabelSelectorFromResource(
ctx context.Context, groupKind schema.GroupKind, namespace, name string,
) (labels.Selector, error) {
Expand Down
76 changes: 76 additions & 0 deletions vertical-pod-autoscaler/pkg/target/fetcher_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
/*
Copyright The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package target

import (
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
batchv1 "k8s.io/api/batch/v1"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
)

func TestGetLabelSelectorCronJobNilOrEmptyTemplateLabels(t *testing.T) {
t.Parallel()
for name, labelsMap := range map[string]map[string]string{
"nil labels": nil,
"empty labels": {},
} {
t.Run(name, func(t *testing.T) {
t.Parallel()
cj := &batchv1.CronJob{
ObjectMeta: metav1.ObjectMeta{Namespace: "ns", Name: "cj"},
Spec: batchv1.CronJobSpec{
JobTemplate: batchv1.JobTemplateSpec{
Spec: batchv1.JobSpec{
Template: corev1.PodTemplateSpec{
ObjectMeta: metav1.ObjectMeta{Labels: labelsMap},
},
},
},
},
}
sel, err := getLabelSelectorFromCronJob(cj)
require.NoError(t, err)
assert.True(t, sel.Matches(labels.Set{}), "pod with no labels should match")
assert.True(t, sel.Matches(labels.Set{"batch.kubernetes.io/job-name": "cj-123"}), "pod with controller labels should match")
})
}
}

func TestGetLabelSelectorCronJobNonEmptyTemplateLabels(t *testing.T) {
t.Parallel()
cj := &batchv1.CronJob{
ObjectMeta: metav1.ObjectMeta{Namespace: "ns", Name: "cj"},
Spec: batchv1.CronJobSpec{
JobTemplate: batchv1.JobTemplateSpec{
Spec: batchv1.JobSpec{
Template: corev1.PodTemplateSpec{
ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{"app": "longrunning"}},
},
},
},
},
}
sel, err := getLabelSelectorFromCronJob(cj)
require.NoError(t, err)
assert.True(t, sel.Matches(labels.Set{"app": "longrunning"}))
assert.False(t, sel.Matches(labels.Set{"app": "other"}))
}