Skip to content

Commit

Permalink
check targetRef of VPA against pod ownerRef
Browse files Browse the repository at this point in the history
  • Loading branch information
dbenque committed Jan 19, 2024
1 parent 3a3b388 commit 2bba2ba
Show file tree
Hide file tree
Showing 9 changed files with 177 additions and 42 deletions.
20 changes: 13 additions & 7 deletions vertical-pod-autoscaler/pkg/admission-controller/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,11 @@ import (
"time"

apiv1 "k8s.io/api/core/v1"
"k8s.io/client-go/informers"
kube_client "k8s.io/client-go/kubernetes"
kube_flag "k8s.io/component-base/cli/flag"
"k8s.io/klog/v2"

"k8s.io/autoscaler/vertical-pod-autoscaler/common"
"k8s.io/autoscaler/vertical-pod-autoscaler/pkg/admission-controller/logic"
"k8s.io/autoscaler/vertical-pod-autoscaler/pkg/admission-controller/resource/pod"
Expand All @@ -32,20 +37,20 @@ import (
"k8s.io/autoscaler/vertical-pod-autoscaler/pkg/admission-controller/resource/vpa"
vpa_clientset "k8s.io/autoscaler/vertical-pod-autoscaler/pkg/client/clientset/versioned"
"k8s.io/autoscaler/vertical-pod-autoscaler/pkg/target"
controllerfetcher "k8s.io/autoscaler/vertical-pod-autoscaler/pkg/target/controller_fetcher"
"k8s.io/autoscaler/vertical-pod-autoscaler/pkg/utils/limitrange"
"k8s.io/autoscaler/vertical-pod-autoscaler/pkg/utils/metrics"
metrics_admission "k8s.io/autoscaler/vertical-pod-autoscaler/pkg/utils/metrics/admission"
"k8s.io/autoscaler/vertical-pod-autoscaler/pkg/utils/status"
vpa_api_util "k8s.io/autoscaler/vertical-pod-autoscaler/pkg/utils/vpa"
"k8s.io/client-go/informers"
kube_client "k8s.io/client-go/kubernetes"
kube_flag "k8s.io/component-base/cli/flag"
"k8s.io/klog/v2"
)

const (
defaultResyncPeriod = 10 * time.Minute
statusUpdateInterval = 10 * time.Second
defaultResyncPeriod = 10 * time.Minute
statusUpdateInterval = 10 * time.Second
scaleCacheEntryLifetime time.Duration = time.Hour
scaleCacheEntryFreshnessTime time.Duration = 10 * time.Minute
scaleCacheEntryJitterFactor float64 = 1.
)

var (
Expand Down Expand Up @@ -87,6 +92,7 @@ func main() {
kubeClient := kube_client.NewForConfigOrDie(config)
factory := informers.NewSharedInformerFactory(kubeClient, defaultResyncPeriod)
targetSelectorFetcher := target.NewVpaTargetSelectorFetcher(config, kubeClient, factory)
controllerFetcher := controllerfetcher.NewControllerFetcher(config, kubeClient, factory, scaleCacheEntryFreshnessTime, scaleCacheEntryLifetime, scaleCacheEntryJitterFactor)
podPreprocessor := pod.NewDefaultPreProcessor()
vpaPreprocessor := vpa.NewDefaultPreProcessor()
var limitRangeCalculator limitrange.LimitRangeCalculator
Expand All @@ -96,7 +102,7 @@ func main() {
limitRangeCalculator = limitrange.NewNoopLimitsCalculator()
}
recommendationProvider := recommendation.NewProvider(limitRangeCalculator, vpa_api_util.NewCappingRecommendationProcessor(limitRangeCalculator))
vpaMatcher := vpa.NewMatcher(vpaLister, targetSelectorFetcher)
vpaMatcher := vpa.NewMatcher(vpaLister, targetSelectorFetcher, controllerFetcher)

hostname, err := os.Hostname()
if err != nil {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,13 @@ package vpa
import (
core "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/klog/v2"

vpa_types "k8s.io/autoscaler/vertical-pod-autoscaler/pkg/apis/autoscaling.k8s.io/v1"
vpa_lister "k8s.io/autoscaler/vertical-pod-autoscaler/pkg/client/listers/autoscaling.k8s.io/v1"
"k8s.io/autoscaler/vertical-pod-autoscaler/pkg/target"
controllerfetcher "k8s.io/autoscaler/vertical-pod-autoscaler/pkg/target/controller_fetcher"
vpa_api_util "k8s.io/autoscaler/vertical-pod-autoscaler/pkg/utils/vpa"
"k8s.io/klog/v2"
)

// Matcher is capable of returning a single matching VPA object
Expand All @@ -33,15 +35,18 @@ type Matcher interface {
}

type matcher struct {
vpaLister vpa_lister.VerticalPodAutoscalerLister
selectorFetcher target.VpaTargetSelectorFetcher
vpaLister vpa_lister.VerticalPodAutoscalerLister
selectorFetcher target.VpaTargetSelectorFetcher
controllerFetcher controllerfetcher.ControllerFetcher
}

// NewMatcher returns a new VPA matcher.
func NewMatcher(vpaLister vpa_lister.VerticalPodAutoscalerLister,
selectorFetcher target.VpaTargetSelectorFetcher) Matcher {
selectorFetcher target.VpaTargetSelectorFetcher,
controllerFetcher controllerfetcher.ControllerFetcher) Matcher {
return &matcher{vpaLister: vpaLister,
selectorFetcher: selectorFetcher}
selectorFetcher: selectorFetcher,
controllerFetcher: controllerFetcher}
}

func (m *matcher) GetMatchingVPA(pod *core.Pod) *vpa_types.VerticalPodAutoscaler {
Expand All @@ -66,7 +71,7 @@ func (m *matcher) GetMatchingVPA(pod *core.Pod) *vpa_types.VerticalPodAutoscaler
})
}
klog.V(2).Infof("Let's choose from %d configs for pod %s/%s", len(onConfigs), pod.Namespace, pod.Name)
result := vpa_api_util.GetControllingVPAForPod(pod, onConfigs)
result := vpa_api_util.GetControllingVPAForPod(pod, onConfigs, m.controllerFetcher)
if result != nil {
return result.Vpa
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,13 @@ package vpa
import (
"testing"

v1 "k8s.io/api/autoscaling/v1"
core "k8s.io/api/core/v1"
meta "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"

vpa_types "k8s.io/autoscaler/vertical-pod-autoscaler/pkg/apis/autoscaling.k8s.io/v1"
controllerfetcher "k8s.io/autoscaler/vertical-pod-autoscaler/pkg/target/controller_fetcher"
target_mock "k8s.io/autoscaler/vertical-pod-autoscaler/pkg/target/mock"
"k8s.io/autoscaler/vertical-pod-autoscaler/pkg/utils/test"

Expand All @@ -37,7 +40,22 @@ func parseLabelSelector(selector string) labels.Selector {
}

func TestGetMatchingVpa(t *testing.T) {
podBuilder := test.Pod().WithName("test-pod").WithLabels(map[string]string{"app": "test"}).
rc := core.ReplicationController{
TypeMeta: meta.TypeMeta{
Kind: "ReplicationController",
APIVersion: "apps/v1",
},
ObjectMeta: meta.ObjectMeta{
Name: "rc",
Namespace: "default",
},
}
targetRef := &v1.CrossVersionObjectReference{
Kind: rc.Kind,
Name: rc.Name,
APIVersion: rc.APIVersion,
}
podBuilder := test.Pod().WithName("test-pod").WithLabels(map[string]string{"app": "test"}).WithCreator(&rc.ObjectMeta, &rc.TypeMeta).
AddContainer(test.Container().WithName("i-am-container").Get())
vpaBuilder := test.VerticalPodAutoscaler().WithContainer("i-am-container")
testCases := []struct {
Expand All @@ -52,7 +70,7 @@ func TestGetMatchingVpa(t *testing.T) {
name: "matching selector",
pod: podBuilder.Get(),
vpas: []*vpa_types.VerticalPodAutoscaler{
vpaBuilder.WithUpdateMode(vpa_types.UpdateModeAuto).WithName("auto-vpa").Get(),
vpaBuilder.WithUpdateMode(vpa_types.UpdateModeAuto).WithName("auto-vpa").WithTargetRef(targetRef).Get(),
},
labelSelector: "app = test",
expectedFound: true,
Expand All @@ -61,24 +79,24 @@ func TestGetMatchingVpa(t *testing.T) {
name: "not matching selector",
pod: podBuilder.Get(),
vpas: []*vpa_types.VerticalPodAutoscaler{
vpaBuilder.WithUpdateMode(vpa_types.UpdateModeAuto).WithName("auto-vpa").Get(),
vpaBuilder.WithUpdateMode(vpa_types.UpdateModeAuto).WithName("auto-vpa").WithTargetRef(targetRef).Get(),
},
labelSelector: "app = differentApp",
expectedFound: false,
}, {
name: "off mode",
pod: podBuilder.Get(),
vpas: []*vpa_types.VerticalPodAutoscaler{
vpaBuilder.WithUpdateMode(vpa_types.UpdateModeOff).WithName("off-vpa").Get(),
vpaBuilder.WithUpdateMode(vpa_types.UpdateModeOff).WithName("off-vpa").WithTargetRef(targetRef).Get(),
},
labelSelector: "app = test",
expectedFound: false,
}, {
name: "two vpas one in off mode",
pod: podBuilder.Get(),
vpas: []*vpa_types.VerticalPodAutoscaler{
vpaBuilder.WithUpdateMode(vpa_types.UpdateModeOff).WithName("off-vpa").Get(),
vpaBuilder.WithUpdateMode(vpa_types.UpdateModeAuto).WithName("auto-vpa").Get(),
vpaBuilder.WithUpdateMode(vpa_types.UpdateModeOff).WithName("off-vpa").WithTargetRef(targetRef).Get(),
vpaBuilder.WithUpdateMode(vpa_types.UpdateModeAuto).WithName("auto-vpa").WithTargetRef(targetRef).Get(),
},
labelSelector: "app = test",
expectedFound: true,
Expand All @@ -87,7 +105,7 @@ func TestGetMatchingVpa(t *testing.T) {
name: "initial mode",
pod: podBuilder.Get(),
vpas: []*vpa_types.VerticalPodAutoscaler{
vpaBuilder.WithUpdateMode(vpa_types.UpdateModeInitial).WithName("initial-vpa").Get(),
vpaBuilder.WithUpdateMode(vpa_types.UpdateModeInitial).WithName("initial-vpa").WithTargetRef(targetRef).Get(),
},
labelSelector: "app = test",
expectedFound: true,
Expand All @@ -114,7 +132,7 @@ func TestGetMatchingVpa(t *testing.T) {
vpaLister.On("VerticalPodAutoscalers", "default").Return(vpaNamespaceLister)

mockSelectorFetcher.EXPECT().Fetch(gomock.Any()).AnyTimes().Return(parseLabelSelector(tc.labelSelector), nil)
matcher := NewMatcher(vpaLister, mockSelectorFetcher)
matcher := NewMatcher(vpaLister, mockSelectorFetcher, controllerfetcher.FakeControllerFetcher{})

vpa := matcher.GetMatchingVPA(tc.pod)
if tc.expectedFound && assert.NotNil(t, vpa) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/*
Copyright 2019 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 controllerfetcher

// FakeControllerFetcher should be used in test only. It returns exactly the same controllerKey
type FakeControllerFetcher struct{}

// FindTopMostWellKnownOrScalable returns the same key for that fake implementation
func (f FakeControllerFetcher) FindTopMostWellKnownOrScalable(controller *ControllerKeyWithAPIVersion) (*ControllerKeyWithAPIVersion, error) {
return controller, nil
}

var _ ControllerFetcher = &FakeControllerFetcher{}
23 changes: 14 additions & 9 deletions vertical-pod-autoscaler/pkg/updater/logic/updater.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,23 +26,25 @@ import (
apiv1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/fields"
"k8s.io/apimachinery/pkg/labels"
kube_client "k8s.io/client-go/kubernetes"
"k8s.io/client-go/kubernetes/fake"
"k8s.io/client-go/kubernetes/scheme"
clientv1 "k8s.io/client-go/kubernetes/typed/core/v1"
v1lister "k8s.io/client-go/listers/core/v1"
"k8s.io/client-go/tools/cache"
"k8s.io/client-go/tools/record"
"k8s.io/klog/v2"

vpa_types "k8s.io/autoscaler/vertical-pod-autoscaler/pkg/apis/autoscaling.k8s.io/v1"
vpa_clientset "k8s.io/autoscaler/vertical-pod-autoscaler/pkg/client/clientset/versioned"
vpa_lister "k8s.io/autoscaler/vertical-pod-autoscaler/pkg/client/listers/autoscaling.k8s.io/v1"
"k8s.io/autoscaler/vertical-pod-autoscaler/pkg/target"
controllerfetcher "k8s.io/autoscaler/vertical-pod-autoscaler/pkg/target/controller_fetcher"
"k8s.io/autoscaler/vertical-pod-autoscaler/pkg/updater/eviction"
"k8s.io/autoscaler/vertical-pod-autoscaler/pkg/updater/priority"
metrics_updater "k8s.io/autoscaler/vertical-pod-autoscaler/pkg/utils/metrics/updater"
"k8s.io/autoscaler/vertical-pod-autoscaler/pkg/utils/status"
vpa_api_util "k8s.io/autoscaler/vertical-pod-autoscaler/pkg/utils/vpa"
kube_client "k8s.io/client-go/kubernetes"
"k8s.io/client-go/kubernetes/fake"
"k8s.io/client-go/kubernetes/scheme"
clientv1 "k8s.io/client-go/kubernetes/typed/core/v1"
v1lister "k8s.io/client-go/listers/core/v1"
"k8s.io/client-go/tools/cache"
"k8s.io/client-go/tools/record"
"k8s.io/klog/v2"
)

// Updater performs updates on pods if recommended by Vertical Pod Autoscaler
Expand All @@ -63,6 +65,7 @@ type updater struct {
selectorFetcher target.VpaTargetSelectorFetcher
useAdmissionControllerStatus bool
statusValidator status.Validator
controllerFetcher controllerfetcher.ControllerFetcher
}

// NewUpdater creates Updater with given configuration
Expand All @@ -78,6 +81,7 @@ func NewUpdater(
recommendationProcessor vpa_api_util.RecommendationProcessor,
evictionAdmission priority.PodEvictionAdmission,
selectorFetcher target.VpaTargetSelectorFetcher,
controllerFetcher controllerfetcher.ControllerFetcher,
priorityProcessor priority.PriorityProcessor,
namespace string,
) (Updater, error) {
Expand All @@ -96,6 +100,7 @@ func NewUpdater(
evictionAdmission: evictionAdmission,
priorityProcessor: priorityProcessor,
selectorFetcher: selectorFetcher,
controllerFetcher: controllerFetcher,
useAdmissionControllerStatus: useAdmissionControllerStatus,
statusValidator: status.NewValidator(
kubeClient,
Expand Down Expand Up @@ -167,7 +172,7 @@ func (u *updater) RunOnce(ctx context.Context) {

controlledPods := make(map[*vpa_types.VerticalPodAutoscaler][]*apiv1.Pod)
for _, pod := range allLivePods {
controllingVPA := vpa_api_util.GetControllingVPAForPod(pod, vpas)
controllingVPA := vpa_api_util.GetControllingVPAForPod(pod, vpas, u.controllerFetcher)
if controllingVPA != nil {
controlledPods[controllingVPA.Vpa] = append(controlledPods[controllingVPA.Vpa], pod)
}
Expand Down
17 changes: 15 additions & 2 deletions vertical-pod-autoscaler/pkg/updater/logic/updater_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,17 @@ import (
"time"

"golang.org/x/time/rate"
v1 "k8s.io/api/autoscaling/v1"

"github.com/golang/mock/gomock"
"github.com/stretchr/testify/assert"
apiv1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"

vpa_types "k8s.io/autoscaler/vertical-pod-autoscaler/pkg/apis/autoscaling.k8s.io/v1"
controllerfetcher "k8s.io/autoscaler/vertical-pod-autoscaler/pkg/target/controller_fetcher"
target_mock "k8s.io/autoscaler/vertical-pod-autoscaler/pkg/target/mock"
"k8s.io/autoscaler/vertical-pod-autoscaler/pkg/updater/eviction"
"k8s.io/autoscaler/vertical-pod-autoscaler/pkg/updater/priority"
Expand Down Expand Up @@ -132,6 +135,10 @@ func testRunOnceBase(
selector := parseLabelSelector("app = testingApp")
containerName := "container1"
rc := apiv1.ReplicationController{
TypeMeta: metav1.TypeMeta{
Kind: "ReplicationController",
APIVersion: "apps/v1",
},
ObjectMeta: metav1.ObjectMeta{
Name: "rc",
Namespace: "default",
Expand Down Expand Up @@ -159,13 +166,18 @@ func testRunOnceBase(

podLister := &test.PodListerMock{}
podLister.On("List").Return(pods, nil)

targetRef := &v1.CrossVersionObjectReference{
Kind: rc.Kind,
Name: rc.Name,
APIVersion: rc.APIVersion,
}
vpaObj := test.VerticalPodAutoscaler().
WithContainer(containerName).
WithTarget("2", "200M").
WithMinAllowed(containerName, "1", "100M").
WithMaxAllowed(containerName, "3", "1G").
Get()
WithTargetRef(targetRef).Get()

vpaObj.Spec.UpdatePolicy = &vpa_types.PodUpdatePolicy{UpdateMode: &updateMode}
vpaLister.On("List").Return([]*vpa_types.VerticalPodAutoscaler{vpaObj}, nil).Once()

Expand All @@ -179,6 +191,7 @@ func testRunOnceBase(
evictionAdmission: priority.NewDefaultPodEvictionAdmission(),
recommendationProcessor: &test.FakeRecommendationProcessor{},
selectorFetcher: mockSelectorFetcher,
controllerFetcher: controllerfetcher.FakeControllerFetcher{},
useAdmissionControllerStatus: true,
statusValidator: statusValidator,
priorityProcessor: priority.NewProcessor(),
Expand Down
Loading

0 comments on commit 2bba2ba

Please sign in to comment.