Commit 7ee767e4 authored by Marcin Wielgus's avatar Marcin Wielgus

Merge pull request #21743 from piosz/hpa-status

HPA update its status even if getting metrics failed
parents 6f73f89f 0dfeb813
...@@ -196,6 +196,7 @@ func (a *HorizontalController) reconcileAutoscaler(hpa extensions.HorizontalPodA ...@@ -196,6 +196,7 @@ func (a *HorizontalController) reconcileAutoscaler(hpa extensions.HorizontalPodA
if hpa.Spec.CPUUtilization != nil { if hpa.Spec.CPUUtilization != nil {
cpuDesiredReplicas, cpuCurrentUtilization, cpuTimestamp, err = a.computeReplicasForCPUUtilization(hpa, scale) cpuDesiredReplicas, cpuCurrentUtilization, cpuTimestamp, err = a.computeReplicasForCPUUtilization(hpa, scale)
if err != nil { if err != nil {
a.updateCurrentReplicasInStatus(hpa, currentReplicas)
a.eventRecorder.Event(&hpa, api.EventTypeWarning, "FailedComputeReplicas", err.Error()) a.eventRecorder.Event(&hpa, api.EventTypeWarning, "FailedComputeReplicas", err.Error())
return fmt.Errorf("failed to compute desired number of replicas based on CPU utilization for %s: %v", reference, err) return fmt.Errorf("failed to compute desired number of replicas based on CPU utilization for %s: %v", reference, err)
} }
...@@ -204,6 +205,7 @@ func (a *HorizontalController) reconcileAutoscaler(hpa extensions.HorizontalPodA ...@@ -204,6 +205,7 @@ func (a *HorizontalController) reconcileAutoscaler(hpa extensions.HorizontalPodA
if cmAnnotation, cmAnnotationFound := hpa.Annotations[HpaCustomMetricsTargetAnnotationName]; cmAnnotationFound { if cmAnnotation, cmAnnotationFound := hpa.Annotations[HpaCustomMetricsTargetAnnotationName]; cmAnnotationFound {
cmDesiredReplicas, cmStatus, cmTimestamp, err = a.computeReplicasForCustomMetrics(hpa, scale, cmAnnotation) cmDesiredReplicas, cmStatus, cmTimestamp, err = a.computeReplicasForCustomMetrics(hpa, scale, cmAnnotation)
if err != nil { if err != nil {
a.updateCurrentReplicasInStatus(hpa, currentReplicas)
a.eventRecorder.Event(&hpa, api.EventTypeWarning, "FailedComputeCMReplicas", err.Error()) a.eventRecorder.Event(&hpa, api.EventTypeWarning, "FailedComputeCMReplicas", err.Error())
return fmt.Errorf("failed to compute desired number of replicas based on Custom Metrics for %s: %v", reference, err) return fmt.Errorf("failed to compute desired number of replicas based on Custom Metrics for %s: %v", reference, err)
} }
...@@ -231,15 +233,33 @@ func (a *HorizontalController) reconcileAutoscaler(hpa extensions.HorizontalPodA ...@@ -231,15 +233,33 @@ func (a *HorizontalController) reconcileAutoscaler(hpa extensions.HorizontalPodA
desiredReplicas = hpa.Spec.MaxReplicas desiredReplicas = hpa.Spec.MaxReplicas
} }
} }
rescale := false
rescale := shouldScale(hpa, currentReplicas, desiredReplicas, timestamp)
if rescale {
scale.Spec.Replicas = desiredReplicas
_, err = a.scaleNamespacer.Scales(hpa.Namespace).Update(hpa.Spec.ScaleRef.Kind, scale)
if err != nil {
a.eventRecorder.Eventf(&hpa, api.EventTypeWarning, "FailedRescale", "New size: %d; error: %v", desiredReplicas, err.Error())
return fmt.Errorf("failed to rescale %s: %v", reference, err)
}
a.eventRecorder.Eventf(&hpa, api.EventTypeNormal, "SuccessfulRescale", "New size: %d", desiredReplicas)
glog.Infof("Successfull rescale of %s, old size: %d, new size: %d",
hpa.Name, currentReplicas, desiredReplicas)
} else {
desiredReplicas = currentReplicas
}
return a.updateStatus(hpa, currentReplicas, desiredReplicas, cpuCurrentUtilization, cmStatus, rescale)
}
func shouldScale(hpa extensions.HorizontalPodAutoscaler, currentReplicas, desiredReplicas int, timestamp time.Time) bool {
if desiredReplicas != currentReplicas { if desiredReplicas != currentReplicas {
// Going down only if the usageRatio dropped significantly below the target // Going down only if the usageRatio dropped significantly below the target
// and there was no rescaling in the last downscaleForbiddenWindow. // and there was no rescaling in the last downscaleForbiddenWindow.
if desiredReplicas < currentReplicas && if desiredReplicas < currentReplicas &&
(hpa.Status.LastScaleTime == nil || (hpa.Status.LastScaleTime == nil ||
hpa.Status.LastScaleTime.Add(downscaleForbiddenWindow).Before(timestamp)) { hpa.Status.LastScaleTime.Add(downscaleForbiddenWindow).Before(timestamp)) {
rescale = true return true
} }
// Going up only if the usage ratio increased significantly above the target // Going up only if the usage ratio increased significantly above the target
...@@ -247,24 +267,20 @@ func (a *HorizontalController) reconcileAutoscaler(hpa extensions.HorizontalPodA ...@@ -247,24 +267,20 @@ func (a *HorizontalController) reconcileAutoscaler(hpa extensions.HorizontalPodA
if desiredReplicas > currentReplicas && if desiredReplicas > currentReplicas &&
(hpa.Status.LastScaleTime == nil || (hpa.Status.LastScaleTime == nil ||
hpa.Status.LastScaleTime.Add(upscaleForbiddenWindow).Before(timestamp)) { hpa.Status.LastScaleTime.Add(upscaleForbiddenWindow).Before(timestamp)) {
rescale = true return true
} }
} }
return false
}
if rescale { func (a *HorizontalController) updateCurrentReplicasInStatus(hpa extensions.HorizontalPodAutoscaler, currentReplicas int) {
scale.Spec.Replicas = desiredReplicas err := a.updateStatus(hpa, currentReplicas, hpa.Status.DesiredReplicas, hpa.Status.CurrentCPUUtilizationPercentage, hpa.Annotations[HpaCustomMetricsStatusAnnotationName], false)
_, err = a.scaleNamespacer.Scales(hpa.Namespace).Update(hpa.Spec.ScaleRef.Kind, scale) if err != nil {
if err != nil { glog.Errorf("%v", err)
a.eventRecorder.Eventf(&hpa, api.EventTypeWarning, "FailedRescale", "New size: %d; error: %v", desiredReplicas, err.Error())
return fmt.Errorf("failed to rescale %s: %v", reference, err)
}
a.eventRecorder.Eventf(&hpa, api.EventTypeNormal, "SuccessfulRescale", "New size: %d", desiredReplicas)
glog.Infof("Successfull rescale of %s, old size: %d, new size: %d",
hpa.Name, currentReplicas, desiredReplicas)
} else {
desiredReplicas = currentReplicas
} }
}
func (a *HorizontalController) updateStatus(hpa extensions.HorizontalPodAutoscaler, currentReplicas, desiredReplicas int, cpuCurrentUtilization *int, cmStatus string, rescale bool) error {
hpa.Status = extensions.HorizontalPodAutoscalerStatus{ hpa.Status = extensions.HorizontalPodAutoscalerStatus{
CurrentReplicas: currentReplicas, CurrentReplicas: currentReplicas,
DesiredReplicas: desiredReplicas, DesiredReplicas: desiredReplicas,
...@@ -280,7 +296,7 @@ func (a *HorizontalController) reconcileAutoscaler(hpa extensions.HorizontalPodA ...@@ -280,7 +296,7 @@ func (a *HorizontalController) reconcileAutoscaler(hpa extensions.HorizontalPodA
hpa.Status.LastScaleTime = &now hpa.Status.LastScaleTime = &now
} }
_, err = a.hpaNamespacer.HorizontalPodAutoscalers(hpa.Namespace).UpdateStatus(&hpa) _, err := a.hpaNamespacer.HorizontalPodAutoscalers(hpa.Namespace).UpdateStatus(&hpa)
if err != nil { if err != nil {
a.eventRecorder.Event(&hpa, api.EventTypeWarning, "FailedUpdateStatus", err.Error()) a.eventRecorder.Event(&hpa, api.EventTypeWarning, "FailedUpdateStatus", err.Error())
return fmt.Errorf("failed to update status for %s: %v", hpa.Name, err) return fmt.Errorf("failed to update status for %s: %v", hpa.Name, err)
......
...@@ -66,6 +66,7 @@ type testCase struct { ...@@ -66,6 +66,7 @@ type testCase struct {
reportedCPURequests []resource.Quantity reportedCPURequests []resource.Quantity
cmTarget *extensions.CustomMetricTargetList cmTarget *extensions.CustomMetricTargetList
scaleUpdated bool scaleUpdated bool
statusUpdated bool
eventCreated bool eventCreated bool
verifyEvents bool verifyEvents bool
} }
...@@ -77,6 +78,7 @@ func (tc *testCase) prepareTestClient(t *testing.T) *fake.Clientset { ...@@ -77,6 +78,7 @@ func (tc *testCase) prepareTestClient(t *testing.T) *fake.Clientset {
podNamePrefix := "test-pod" podNamePrefix := "test-pod"
tc.scaleUpdated = false tc.scaleUpdated = false
tc.statusUpdated = false
tc.eventCreated = false tc.eventCreated = false
fakeClient := &fake.Clientset{} fakeClient := &fake.Clientset{}
...@@ -98,6 +100,10 @@ func (tc *testCase) prepareTestClient(t *testing.T) *fake.Clientset { ...@@ -98,6 +100,10 @@ func (tc *testCase) prepareTestClient(t *testing.T) *fake.Clientset {
MinReplicas: &tc.minReplicas, MinReplicas: &tc.minReplicas,
MaxReplicas: tc.maxReplicas, MaxReplicas: tc.maxReplicas,
}, },
Status: extensions.HorizontalPodAutoscalerStatus{
CurrentReplicas: tc.initialReplicas,
DesiredReplicas: tc.initialReplicas,
},
}, },
}, },
} }
...@@ -191,6 +197,7 @@ func (tc *testCase) prepareTestClient(t *testing.T) *fake.Clientset { ...@@ -191,6 +197,7 @@ func (tc *testCase) prepareTestClient(t *testing.T) *fake.Clientset {
assert.Equal(t, namespace, obj.Namespace) assert.Equal(t, namespace, obj.Namespace)
assert.Equal(t, hpaName, obj.Name) assert.Equal(t, hpaName, obj.Name)
assert.Equal(t, tc.desiredReplicas, obj.Status.DesiredReplicas) assert.Equal(t, tc.desiredReplicas, obj.Status.DesiredReplicas)
tc.statusUpdated = true
return true, obj, nil return true, obj, nil
}) })
...@@ -209,6 +216,7 @@ func (tc *testCase) prepareTestClient(t *testing.T) *fake.Clientset { ...@@ -209,6 +216,7 @@ func (tc *testCase) prepareTestClient(t *testing.T) *fake.Clientset {
func (tc *testCase) verifyResults(t *testing.T) { func (tc *testCase) verifyResults(t *testing.T) {
assert.Equal(t, tc.initialReplicas != tc.desiredReplicas, tc.scaleUpdated) assert.Equal(t, tc.initialReplicas != tc.desiredReplicas, tc.scaleUpdated)
assert.True(t, tc.statusUpdated)
if tc.verifyEvents { if tc.verifyEvents {
assert.Equal(t, tc.initialReplicas != tc.desiredReplicas, tc.eventCreated) assert.Equal(t, tc.initialReplicas != tc.desiredReplicas, tc.eventCreated)
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment