Repository: kubeless/kubeless
Pull request #1117 (2020-03-02 06:42:09)

If HPA already exists, perform an update.
**Issue Ref**: #1116 
 
**Description**: 

If the creation of a HPA returns an error becasue one with that name already exists, then perform an update.
This prevents the error triggering a retry up to the retry limit and slowing down queue consumption.

**TODOs**:
 - [X] Ready to review
 - [X] Automated Tests
 - [ ] Docs



Patch:
diff --git a/pkg/controller/function_controller.go b/pkg/controller/function_controller.go
index 4b78b814..c9d00172 100644
--- a/pkg/controller/function_controller.go
+++ b/pkg/controller/function_controller.go
@@ -367,6 +367,9 @@ func (c *FunctionController) ensureK8sResources(funcObj *kubelessApi.Function) e
 			}
 		}
 		err = utils.CreateAutoscale(c.clientset, funcObj.Spec.HorizontalPodAutoscaler)
+		if err != nil && k8sErrors.IsAlreadyExists(err) {
+			err = utils.UpdateAutoscale(c.clientset, funcObj.Spec.HorizontalPodAutoscaler)
+		}
 		if err != nil {
 			return err
 		}
diff --git a/pkg/utils/k8sutil.go b/pkg/utils/k8sutil.go
index ed3b846a..06db12c0 100644
--- a/pkg/utils/k8sutil.go
+++ b/pkg/utils/k8sutil.go
@@ -319,10 +319,12 @@ func doRESTReq(restIface rest.Interface, groupVersion, verb, resource, elem, nam
 // CreateAutoscale creates HPA object for function
 func CreateAutoscale(client kubernetes.Interface, hpa v2beta1.HorizontalPodAutoscaler) error {
 	_, err := client.AutoscalingV2beta1().HorizontalPodAutoscalers(hpa.ObjectMeta.Namespace).Create(&hpa)
-	if err != nil {
-		return err
-	}
+	return err
+}
 
+// UpdateAutoscale updates an existing HPA object for a function
+func UpdateAutoscale(client kubernetes.Interface, hpa v2beta1.HorizontalPodAutoscaler) error {
+	_, err := client.AutoscalingV2beta1().HorizontalPodAutoscalers(hpa.ObjectMeta.Namespace).Update(&hpa)
 	return err
 }
 


Test patch:
diff --git a/pkg/utils/k8sutil_test.go b/pkg/utils/k8sutil_test.go
index 08fc1801..760ac093 100644
--- a/pkg/utils/k8sutil_test.go
+++ b/pkg/utils/k8sutil_test.go
@@ -78,6 +78,45 @@ func TestCreateAutoscaleResource(t *testing.T) {
 	}
 }
 
+func TestUpdateAutoscaleResource(t *testing.T) {
+	clientset := fake.NewSimpleClientset()
+	name := "foo"
+	ns := "myns"
+
+	// Create a pre-existing HPA
+	hpaDef := v2beta1.HorizontalPodAutoscaler{
+		ObjectMeta: metav1.ObjectMeta{
+			Name:      name,
+			Namespace: ns,
+		},
+	}
+	if err := CreateAutoscale(clientset, hpaDef); err != nil {
+		t.Fatalf("Creating autoscale returned err: %v", err)
+	}
+
+	// Perform an update
+	hpaDef = v2beta1.HorizontalPodAutoscaler{
+		ObjectMeta: metav1.ObjectMeta{
+			Name:      name,
+			Namespace: ns,
+			Labels: map[string]string{
+				"baz": "qux",
+			},
+		},
+	}
+	if err := UpdateAutoscale(clientset, hpaDef); err != nil {
+		t.Fatalf("Updating autoscale returned err: %v", err)
+	}
+
+	hpa, err := clientset.AutoscalingV2beta1().HorizontalPodAutoscalers(ns).Get(name, metav1.GetOptions{})
+	if err != nil {
+		t.Fatalf("Updating autoscale returned err: %v", err)
+	}
+	if hpa.ObjectMeta.Name != "foo" {
+		t.Fatalf("Updating wrong scale target name")
+	}
+}
+
 func TestDeleteAutoscaleResource(t *testing.T) {
 	myNsFoo := metav1.ObjectMeta{
 		Namespace: "myns",