diff --git a/pkg/controller/podautoscaler/metrics/metrics_client.go b/pkg/controller/podautoscaler/metrics/metrics_client.go index 76a2387c869..623a8fce2cb 100644 --- a/pkg/controller/podautoscaler/metrics/metrics_client.go +++ b/pkg/controller/podautoscaler/metrics/metrics_client.go @@ -88,7 +88,7 @@ func (h *HeapsterMetricsClient) GetResourceMetric(resource api.ResourceName, nam ProxyGet(h.heapsterScheme, h.heapsterService, h.heapsterPort, metricPath, params). DoRaw() if err != nil { - return nil, time.Time{}, fmt.Errorf("failed to get pod resource metrics: %v", err) + return nil, time.Time{}, fmt.Errorf("failed to get heapster service: %v", err) } glog.V(4).Infof("Heapster metrics result: %s", string(resultRaw)) @@ -158,7 +158,7 @@ func (h *HeapsterMetricsClient) GetRawMetric(metricName string, namespace string ProxyGet(h.heapsterScheme, h.heapsterService, h.heapsterPort, metricPath, map[string]string{"start": startTime.Format(time.RFC3339)}). DoRaw() if err != nil { - return nil, time.Time{}, fmt.Errorf("failed to get pod metrics: %v", err) + return nil, time.Time{}, fmt.Errorf("failed to get heapster service: %v", err) } var metrics heapster.MetricResultList diff --git a/pkg/controller/podautoscaler/replica_calculator.go b/pkg/controller/podautoscaler/replica_calculator.go index 4074b7801e7..1618cbda88f 100644 --- a/pkg/controller/podautoscaler/replica_calculator.go +++ b/pkg/controller/podautoscaler/replica_calculator.go @@ -116,7 +116,7 @@ func (c *ReplicaCalculator) GetResourceReplicas(currentReplicas int32, targetUti for podName := range missingPods { metrics[podName] = requests[podName] } - } else { + } else if usageRatio > 1.0 { // on a scale-up, treat missing pods as using 0% of the resource request for podName := range missingPods { metrics[podName] = 0 diff --git a/pkg/controller/podautoscaler/replica_calculator_test.go b/pkg/controller/podautoscaler/replica_calculator_test.go index 0819cc406fc..3defba8fee1 100644 --- a/pkg/controller/podautoscaler/replica_calculator_test.go +++ b/pkg/controller/podautoscaler/replica_calculator_test.go @@ -482,6 +482,105 @@ func TestReplicaCalcEmptyCPURequest(t *testing.T) { tc.runTest(t) } +func TestReplicaCalcMissingMetricsNoChangeEq(t *testing.T) { + tc := replicaCalcTestCase{ + currentReplicas: 2, + expectedReplicas: 2, + resource: &resourceInfo{ + name: api.ResourceCPU, + requests: []resource.Quantity{resource.MustParse("1.0"), resource.MustParse("1.0")}, + levels: []int64{1000}, + + targetUtilization: 100, + expectedUtilization: 100, + }, + } + tc.runTest(t) +} + +func TestReplicaCalcMissingMetricsNoChangeGt(t *testing.T) { + tc := replicaCalcTestCase{ + currentReplicas: 2, + expectedReplicas: 2, + resource: &resourceInfo{ + name: api.ResourceCPU, + requests: []resource.Quantity{resource.MustParse("1.0"), resource.MustParse("1.0")}, + levels: []int64{1900}, + + targetUtilization: 100, + expectedUtilization: 190, + }, + } + tc.runTest(t) +} + +func TestReplicaCalcMissingMetricsNoChangeLt(t *testing.T) { + tc := replicaCalcTestCase{ + currentReplicas: 2, + expectedReplicas: 2, + resource: &resourceInfo{ + name: api.ResourceCPU, + requests: []resource.Quantity{resource.MustParse("1.0"), resource.MustParse("1.0")}, + levels: []int64{600}, + + targetUtilization: 100, + expectedUtilization: 60, + }, + } + tc.runTest(t) +} + +func TestReplicaCalcMissingMetricsUnreadyNoChange(t *testing.T) { + tc := replicaCalcTestCase{ + currentReplicas: 3, + expectedReplicas: 3, + podReadiness: []api.ConditionStatus{api.ConditionFalse, api.ConditionTrue, api.ConditionTrue}, + resource: &resourceInfo{ + name: api.ResourceCPU, + requests: []resource.Quantity{resource.MustParse("1.0"), resource.MustParse("1.0"), resource.MustParse("1.0")}, + levels: []int64{100, 450}, + + targetUtilization: 50, + expectedUtilization: 45, + }, + } + tc.runTest(t) +} + +func TestReplicaCalcMissingMetricsUnreadyScaleUp(t *testing.T) { + tc := replicaCalcTestCase{ + currentReplicas: 3, + expectedReplicas: 4, + podReadiness: []api.ConditionStatus{api.ConditionFalse, api.ConditionTrue, api.ConditionTrue}, + resource: &resourceInfo{ + name: api.ResourceCPU, + requests: []resource.Quantity{resource.MustParse("1.0"), resource.MustParse("1.0"), resource.MustParse("1.0")}, + levels: []int64{100, 2000}, + + targetUtilization: 50, + expectedUtilization: 200, + }, + } + tc.runTest(t) +} + +func TestReplicaCalcMissingMetricsUnreadyScaleDown(t *testing.T) { + tc := replicaCalcTestCase{ + currentReplicas: 4, + expectedReplicas: 3, + podReadiness: []api.ConditionStatus{api.ConditionFalse, api.ConditionTrue, api.ConditionTrue, api.ConditionTrue}, + resource: &resourceInfo{ + name: api.ResourceCPU, + requests: []resource.Quantity{resource.MustParse("1.0"), resource.MustParse("1.0"), resource.MustParse("1.0"), resource.MustParse("1.0")}, + levels: []int64{100, 100, 100}, + + targetUtilization: 50, + expectedUtilization: 10, + }, + } + tc.runTest(t) +} + // TestComputedToleranceAlgImplementation is a regression test which // back-calculates a minimal percentage for downscaling based on a small percentage // increase in pod utilization which is calibrated against the tolerance value.