Skip to content

Commit 0564d52

Browse files
author
Kubernetes Submit Queue
authored
Merge pull request kubernetes#53205 from kawych/master
Automatic merge from submit-queue (batch tested with PRs 50223, 53205). If you want to cherry-pick this change to another branch, please follow the instructions <a href="https://github.com/kubernetes/community/blob/master/contributors/devel/cherry-picks.md">here</a>. Create e2e tests for Custom Metrics - Stackdriver Adapter and HPA based on custom metrics from Stackdriver **What this PR does / why we need it**: - Add e2e test for Custom Metrics - Stackdriver Adapter - Add 2e2 test for HPA based on custom metrics from Stackdriver - Enable HorizontalPodAutoscalerUseRESTClients option **Release note**: ```release-note Horizontal pod autoscaler uses REST clients through the kube-aggregator instead of the legacy client through the API server proxy. ```
2 parents 82869c5 + f3fddae commit 0564d52

File tree

9 files changed

+570
-10
lines changed

9 files changed

+570
-10
lines changed

cluster/gce/gci/configure-helper.sh

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1610,6 +1610,10 @@ function start-kube-controller-manager {
16101610
if [[ -n "${CLUSTER_SIGNING_DURATION:-}" ]]; then
16111611
params+=" --experimental-cluster-signing-duration=$CLUSTER_SIGNING_DURATION"
16121612
fi
1613+
# disable using HPA metrics REST clients if metrics-server isn't enabled
1614+
if [[ "${ENABLE_METRICS_SERVER:-}" != "true" ]]; then
1615+
params+=" --horizontal-pod-autoscaler-use-rest-clients=false"
1616+
fi
16131617

16141618
local -r kube_rc_docker_tag=$(cat /home/kubernetes/kube-docker-files/kube-controller-manager.docker_tag)
16151619
local container_env=""

cmd/kube-controller-manager/app/options/options.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ func NewCMServer() *CMServer {
115115
ClusterSigningDuration: metav1.Duration{Duration: helpers.OneYear},
116116
ReconcilerSyncLoopPeriod: metav1.Duration{Duration: 60 * time.Second},
117117
EnableTaintManager: true,
118-
HorizontalPodAutoscalerUseRESTClients: false,
118+
HorizontalPodAutoscalerUseRESTClients: true,
119119
},
120120
}
121121
s.LeaderElection.LeaderElect = true

cmd/kube-controller-manager/app/options/options_test.go

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -192,15 +192,16 @@ func TestAddFlags(t *testing.T) {
192192
{Group: "apiregistration.k8s.io", Resource: "apiservices"},
193193
{Group: "apiextensions.k8s.io", Resource: "customresourcedefinitions"},
194194
},
195-
NodeEvictionRate: 0.2,
196-
SecondaryNodeEvictionRate: 0.05,
197-
LargeClusterSizeThreshold: 100,
198-
UnhealthyZoneThreshold: 0.6,
199-
DisableAttachDetachReconcilerSync: true,
200-
ReconcilerSyncLoopPeriod: metav1.Duration{Duration: 30 * time.Second},
201-
Controllers: []string{"foo", "bar"},
202-
EnableTaintManager: false,
203-
UseServiceAccountCredentials: true,
195+
NodeEvictionRate: 0.2,
196+
SecondaryNodeEvictionRate: 0.05,
197+
LargeClusterSizeThreshold: 100,
198+
UnhealthyZoneThreshold: 0.6,
199+
DisableAttachDetachReconcilerSync: true,
200+
ReconcilerSyncLoopPeriod: metav1.Duration{Duration: 30 * time.Second},
201+
Controllers: []string{"foo", "bar"},
202+
EnableTaintManager: false,
203+
HorizontalPodAutoscalerUseRESTClients: true,
204+
UseServiceAccountCredentials: true,
204205
},
205206
Kubeconfig: "/kubeconfig",
206207
Master: "192.168.4.20",

test/e2e/autoscaling/BUILD

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ go_library(
1111
"autoscaling_timer.go",
1212
"cluster_autoscaler_scalability.go",
1313
"cluster_size_autoscaling.go",
14+
"custom_metrics_autoscaling.go",
1415
"dns_autoscaling.go",
1516
"framework.go",
1617
"horizontal_pod_autoscaling.go",
@@ -19,11 +20,15 @@ go_library(
1920
"//pkg/api:go_default_library",
2021
"//test/e2e/common:go_default_library",
2122
"//test/e2e/framework:go_default_library",
23+
"//test/e2e/instrumentation/monitoring:go_default_library",
2224
"//test/e2e/scheduling:go_default_library",
2325
"//test/utils:go_default_library",
2426
"//vendor/github.com/golang/glog:go_default_library",
2527
"//vendor/github.com/onsi/ginkgo:go_default_library",
2628
"//vendor/github.com/onsi/gomega:go_default_library",
29+
"//vendor/golang.org/x/oauth2/google:go_default_library",
30+
"//vendor/google.golang.org/api/monitoring/v3:go_default_library",
31+
"//vendor/k8s.io/api/autoscaling/v2beta1:go_default_library",
2732
"//vendor/k8s.io/api/core/v1:go_default_library",
2833
"//vendor/k8s.io/api/policy/v1beta1:go_default_library",
2934
"//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library",
Lines changed: 210 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,210 @@
1+
/*
2+
Copyright 2017 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package autoscaling
18+
19+
import (
20+
"context"
21+
"time"
22+
23+
"golang.org/x/oauth2/google"
24+
clientset "k8s.io/client-go/kubernetes"
25+
26+
. "github.com/onsi/ginkgo"
27+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
28+
"k8s.io/kubernetes/test/e2e/framework"
29+
30+
gcm "google.golang.org/api/monitoring/v3"
31+
as "k8s.io/api/autoscaling/v2beta1"
32+
"k8s.io/apimachinery/pkg/api/resource"
33+
"k8s.io/apimachinery/pkg/util/wait"
34+
"k8s.io/kubernetes/test/e2e/instrumentation/monitoring"
35+
)
36+
37+
const (
38+
stackdriverExporterDeployment = "stackdriver-exporter-deployment"
39+
dummyDeploymentName = "dummy-deployment"
40+
stackdriverExporterPod = "stackdriver-exporter-pod"
41+
)
42+
43+
var _ = SIGDescribe("[HPA] Horizontal pod autoscaling (scale resource: Custom Metrics from Stackdriver)", func() {
44+
BeforeEach(func() {
45+
framework.SkipUnlessProviderIs("gce")
46+
})
47+
48+
f := framework.NewDefaultFramework("horizontal-pod-autoscaling")
49+
var kubeClient clientset.Interface
50+
51+
It("should autoscale with Custom Metrics from Stackdriver [Feature:CustomMetricsAutoscaling]", func() {
52+
kubeClient = f.ClientSet
53+
testHPA(f, kubeClient)
54+
})
55+
})
56+
57+
func testHPA(f *framework.Framework, kubeClient clientset.Interface) {
58+
projectId := framework.TestContext.CloudConfig.ProjectID
59+
60+
ctx := context.Background()
61+
client, err := google.DefaultClient(ctx, gcm.CloudPlatformScope)
62+
63+
// Hack for running tests locally, needed to authenticate in Stackdriver
64+
// If this is your use case, create application default credentials:
65+
// $ gcloud auth application-default login
66+
// and uncomment following lines:
67+
/*
68+
ts, err := google.DefaultTokenSource(oauth2.NoContext)
69+
framework.Logf("Couldn't get application default credentials, %v", err)
70+
if err != nil {
71+
framework.Failf("Error accessing application default credentials, %v", err)
72+
}
73+
client := oauth2.NewClient(oauth2.NoContext, ts)
74+
*/
75+
76+
gcmService, err := gcm.New(client)
77+
if err != nil {
78+
framework.Failf("Failed to create gcm service, %v", err)
79+
}
80+
81+
// Set up a cluster: create a custom metric and set up k8s-sd adapter
82+
err = monitoring.CreateDescriptors(gcmService, projectId)
83+
if err != nil {
84+
framework.Failf("Failed to create metric descriptor: %v", err)
85+
}
86+
defer monitoring.CleanupDescriptors(gcmService, projectId)
87+
88+
err = monitoring.CreateAdapter()
89+
if err != nil {
90+
framework.Failf("Failed to set up: %v", err)
91+
}
92+
defer monitoring.CleanupAdapter()
93+
94+
// Run application that exports the metric
95+
err = createDeploymentsToScale(f, kubeClient)
96+
if err != nil {
97+
framework.Failf("Failed to create stackdriver-exporter pod: %v", err)
98+
}
99+
defer cleanupDeploymentsToScale(f, kubeClient)
100+
101+
// Autoscale the deployments
102+
err = createPodsHPA(f, kubeClient)
103+
if err != nil {
104+
framework.Failf("Failed to create 'Pods' HPA: %v", err)
105+
}
106+
err = createObjectHPA(f, kubeClient)
107+
if err != nil {
108+
framework.Failf("Failed to create 'Objects' HPA: %v", err)
109+
}
110+
111+
waitForReplicas(stackdriverExporterDeployment, f.Namespace.ObjectMeta.Name, kubeClient, 15*time.Minute, 1)
112+
waitForReplicas(dummyDeploymentName, f.Namespace.ObjectMeta.Name, kubeClient, 15*time.Minute, 1)
113+
}
114+
115+
func createDeploymentsToScale(f *framework.Framework, cs clientset.Interface) error {
116+
_, err := cs.Extensions().Deployments(f.Namespace.ObjectMeta.Name).Create(monitoring.StackdriverExporterDeployment(stackdriverExporterDeployment, f.Namespace.Name, 2, 100))
117+
if err != nil {
118+
return err
119+
}
120+
_, err = cs.Core().Pods(f.Namespace.ObjectMeta.Name).Create(monitoring.StackdriverExporterPod(stackdriverExporterPod, f.Namespace.Name, stackdriverExporterPod, monitoring.CustomMetricName, 100))
121+
if err != nil {
122+
return err
123+
}
124+
_, err = cs.Extensions().Deployments(f.Namespace.ObjectMeta.Name).Create(monitoring.StackdriverExporterDeployment(dummyDeploymentName, f.Namespace.Name, 2, 100))
125+
return err
126+
}
127+
128+
func cleanupDeploymentsToScale(f *framework.Framework, cs clientset.Interface) {
129+
_ = cs.Extensions().Deployments(f.Namespace.ObjectMeta.Name).Delete(stackdriverExporterDeployment, &metav1.DeleteOptions{})
130+
_ = cs.Core().Pods(f.Namespace.ObjectMeta.Name).Delete(stackdriverExporterPod, &metav1.DeleteOptions{})
131+
_ = cs.Extensions().Deployments(f.Namespace.ObjectMeta.Name).Delete(dummyDeploymentName, &metav1.DeleteOptions{})
132+
}
133+
134+
func createPodsHPA(f *framework.Framework, cs clientset.Interface) error {
135+
var minReplicas int32 = 1
136+
_, err := cs.AutoscalingV2beta1().HorizontalPodAutoscalers(f.Namespace.ObjectMeta.Name).Create(&as.HorizontalPodAutoscaler{
137+
ObjectMeta: metav1.ObjectMeta{
138+
Name: "custom-metrics-pods-hpa",
139+
Namespace: f.Namespace.ObjectMeta.Name,
140+
},
141+
Spec: as.HorizontalPodAutoscalerSpec{
142+
Metrics: []as.MetricSpec{
143+
{
144+
Type: as.PodsMetricSourceType,
145+
Pods: &as.PodsMetricSource{
146+
MetricName: monitoring.CustomMetricName,
147+
TargetAverageValue: *resource.NewQuantity(200, resource.DecimalSI),
148+
},
149+
},
150+
},
151+
MaxReplicas: 3,
152+
MinReplicas: &minReplicas,
153+
ScaleTargetRef: as.CrossVersionObjectReference{
154+
APIVersion: "extensions/v1beta1",
155+
Kind: "Deployment",
156+
Name: stackdriverExporterDeployment,
157+
},
158+
},
159+
})
160+
return err
161+
}
162+
163+
func createObjectHPA(f *framework.Framework, cs clientset.Interface) error {
164+
var minReplicas int32 = 1
165+
_, err := cs.AutoscalingV2beta1().HorizontalPodAutoscalers(f.Namespace.ObjectMeta.Name).Create(&as.HorizontalPodAutoscaler{
166+
ObjectMeta: metav1.ObjectMeta{
167+
Name: "custom-metrics-objects-hpa",
168+
Namespace: f.Namespace.ObjectMeta.Name,
169+
},
170+
Spec: as.HorizontalPodAutoscalerSpec{
171+
Metrics: []as.MetricSpec{
172+
{
173+
Type: as.ObjectMetricSourceType,
174+
Object: &as.ObjectMetricSource{
175+
MetricName: monitoring.CustomMetricName,
176+
Target: as.CrossVersionObjectReference{
177+
Kind: "Pod",
178+
Name: stackdriverExporterPod,
179+
},
180+
TargetValue: *resource.NewQuantity(200, resource.DecimalSI),
181+
},
182+
},
183+
},
184+
MaxReplicas: 3,
185+
MinReplicas: &minReplicas,
186+
ScaleTargetRef: as.CrossVersionObjectReference{
187+
APIVersion: "extensions/v1beta1",
188+
Kind: "Deployment",
189+
Name: dummyDeploymentName,
190+
},
191+
},
192+
})
193+
return err
194+
}
195+
196+
func waitForReplicas(deploymentName, namespace string, cs clientset.Interface, timeout time.Duration, desiredReplicas int) {
197+
interval := 20 * time.Second
198+
err := wait.PollImmediate(interval, timeout, func() (bool, error) {
199+
deployment, err := cs.Extensions().Deployments(namespace).Get(deploymentName, metav1.GetOptions{})
200+
if err != nil {
201+
framework.Failf("Failed to get replication controller %s: %v", deployment, err)
202+
}
203+
replicas := int(deployment.Status.ReadyReplicas)
204+
framework.Logf("waiting for %d replicas (current: %d)", desiredReplicas, replicas)
205+
return replicas == desiredReplicas, nil // Expected number of replicas found. Exit.
206+
})
207+
if err != nil {
208+
framework.Failf("Timeout waiting %v for %v replicas", timeout, desiredReplicas)
209+
}
210+
}

test/e2e/instrumentation/monitoring/BUILD

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ go_library(
99
name = "go_default_library",
1010
srcs = [
1111
"cadvisor.go",
12+
"custom_metrics_deployments.go",
13+
"custom_metrics_stackdriver.go",
1214
"influxdb.go",
1315
"metrics_grabber.go",
1416
"stackdriver.go",
@@ -23,10 +25,17 @@ go_library(
2325
"//vendor/github.com/onsi/gomega:go_default_library",
2426
"//vendor/golang.org/x/oauth2/google:go_default_library",
2527
"//vendor/google.golang.org/api/monitoring/v3:go_default_library",
28+
"//vendor/k8s.io/api/core/v1:go_default_library",
29+
"//vendor/k8s.io/api/extensions/v1beta1:go_default_library",
2630
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
2731
"//vendor/k8s.io/apimachinery/pkg/labels:go_default_library",
32+
"//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
33+
"//vendor/k8s.io/apimachinery/pkg/selection:go_default_library",
2834
"//vendor/k8s.io/apimachinery/pkg/util/wait:go_default_library",
35+
"//vendor/k8s.io/client-go/discovery:go_default_library",
2936
"//vendor/k8s.io/client-go/kubernetes:go_default_library",
37+
"//vendor/k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset:go_default_library",
38+
"//vendor/k8s.io/metrics/pkg/client/custom_metrics:go_default_library",
3039
],
3140
)
3241

0 commit comments

Comments
 (0)