Repository: kubernetes/ingress-nginx
Pull request #9195 (2022-10-20 17:44:12)

Implement parseFloat for annotations
We are working on a new balancer implementation in the Shopify fork (to be upstreamed soon) and we need to pass an annotation that is a float.

An alternative is to use `int` annotation multiplied by `100` but we thought it would be more user-friendly to have support for floats in configuration.

@ElvinEfendi 

k8s-ci-robot: @kirs: This issue is currently awaiting triage.

If Ingress contributors determines this is a relevant issue, they will accept it by applying the `triage/accepted` label and provide further guidance.

The `triage/accepted` label can be added by org members by writing `/triage accepted` in a comment.


<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository.
</details>
ElvinEfendi: /ok-to-test
k8s-ci-robot: [APPROVALNOTIFIER] This PR is **APPROVED**

This pull-request has been approved by: *<a href="https://github.com/kubernetes/ingress-nginx/pull/9195#issuecomment-1286066556" title="LGTM">ElvinEfendi</a>*, *<a href="https://github.com/kubernetes/ingress-nginx/pull/9195#" title="Author self-approved">kirs</a>*

The full list of commands accepted by this bot can be found [here](https://go.k8s.io/bot-commands?repo=kubernetes%2Fingress-nginx).

The pull request process is described [here](https://git.k8s.io/community/contributors/guide/owners.md#the-code-review-process)

<details >
Needs approval from an approver in each of these files:

- ~~[OWNERS](https://github.com/kubernetes/ingress-nginx/blob/main/OWNERS)~~ [ElvinEfendi]

Approvers can indicate their approval by writing `/approve` in a comment
Approvers can cancel approval by writing `/approve cancel` in a comment
</details>
<!-- META={"approvers":[]} -->

Patch:
diff --git a/internal/ingress/annotations/parser/main.go b/internal/ingress/annotations/parser/main.go
index b39e409b9..107a278b0 100644
--- a/internal/ingress/annotations/parser/main.go
+++ b/internal/ingress/annotations/parser/main.go
@@ -80,6 +80,18 @@ func (a ingAnnotations) parseInt(name string) (int, error) {
 	return 0, errors.ErrMissingAnnotations
 }
 
+func (a ingAnnotations) parseFloat32(name string) (float32, error) {
+	val, ok := a[name]
+	if ok {
+		i, err := strconv.ParseFloat(val, 32)
+		if err != nil {
+			return 0, errors.NewInvalidAnnotationContent(name, val)
+		}
+		return float32(i), nil
+	}
+	return 0, errors.ErrMissingAnnotations
+}
+
 func checkAnnotation(name string, ing *networking.Ingress) error {
 	if ing == nil || len(ing.GetAnnotations()) == 0 {
 		return errors.ErrMissingAnnotations
@@ -122,6 +134,16 @@ func GetIntAnnotation(name string, ing *networking.Ingress) (int, error) {
 	return ingAnnotations(ing.GetAnnotations()).parseInt(v)
 }
 
+// GetFloatAnnotation extracts a float32 from an Ingress annotation
+func GetFloatAnnotation(name string, ing *networking.Ingress) (float32, error) {
+	v := GetAnnotationWithPrefix(name)
+	err := checkAnnotation(v, ing)
+	if err != nil {
+		return 0, err
+	}
+	return ingAnnotations(ing.GetAnnotations()).parseFloat32(v)
+}
+
 // GetAnnotationWithPrefix returns the prefix of ingress annotations
 func GetAnnotationWithPrefix(suffix string) string {
 	return fmt.Sprintf("%v/%v", AnnotationsPrefix, suffix)


Test patch:
diff --git a/internal/ingress/annotations/parser/main_test.go b/internal/ingress/annotations/parser/main_test.go
index 7b01a1230..318e024d3 100644
--- a/internal/ingress/annotations/parser/main_test.go
+++ b/internal/ingress/annotations/parser/main_test.go
@@ -130,6 +130,47 @@ rewrite (?i)/arcgis/services/Utilities/Geometry/GeometryServer(.*)$ /arcgis/serv
 	}
 }
 
+func TestGetFloatAnnotation(t *testing.T) {
+	ing := buildIngress()
+
+	_, err := GetFloatAnnotation("", nil)
+	if err == nil {
+		t.Errorf("expected error but retuned nil")
+	}
+
+	tests := []struct {
+		name   string
+		field  string
+		value  string
+		exp    float32
+		expErr bool
+	}{
+		{"valid - A", "string", "1.5", 1.5, false},
+		{"valid - B", "string", "2", 2, false},
+		{"valid - C", "string", "100.0", 100, false},
+	}
+
+	data := map[string]string{}
+	ing.SetAnnotations(data)
+
+	for _, test := range tests {
+		data[GetAnnotationWithPrefix(test.field)] = test.value
+
+		s, err := GetFloatAnnotation(test.field, ing)
+		if test.expErr {
+			if err == nil {
+				t.Errorf("%v: expected error but retuned nil", test.name)
+			}
+			continue
+		}
+		if s != test.exp {
+			t.Errorf("%v: expected \"%v\" but \"%v\" was returned", test.name, test.exp, s)
+		}
+
+		delete(data, test.field)
+	}
+}
+
 func TestGetIntAnnotation(t *testing.T) {
 	ing := buildIngress()