|
@@ -3,6 +3,7 @@ package models
|
|
|
import (
|
|
import (
|
|
|
"fmt"
|
|
"fmt"
|
|
|
"io"
|
|
"io"
|
|
|
|
|
+ "math"
|
|
|
"reflect"
|
|
"reflect"
|
|
|
"strconv"
|
|
"strconv"
|
|
|
"strings"
|
|
"strings"
|
|
@@ -215,24 +216,52 @@ func (cp *CustomPricing) GetSharedOverheadCostPerMonth() float64 {
|
|
|
return sharedCostPerMonth
|
|
return sharedCostPerMonth
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-func SetCustomPricingField(obj *CustomPricing, name string, value string) error {
|
|
|
|
|
|
|
+func sanitizeFloatString(number string, allowNaN bool) (string, error) {
|
|
|
|
|
+ num, err := strconv.ParseFloat(number, 64)
|
|
|
|
|
+ if err != nil {
|
|
|
|
|
+ return "", fmt.Errorf("expected a string representing a number; got '%s'", number)
|
|
|
|
|
+ }
|
|
|
|
|
+ if !allowNaN && math.IsNaN(num) {
|
|
|
|
|
+ return "", fmt.Errorf("expected a string representing a number; got 'NaN'")
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
|
|
+ // Format the numerical string we just parsed.
|
|
|
|
|
+ return strconv.FormatFloat(num, 'f', -1, 64), nil
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+func SetCustomPricingField(obj *CustomPricing, name string, value string) error {
|
|
|
structValue := reflect.ValueOf(obj).Elem()
|
|
structValue := reflect.ValueOf(obj).Elem()
|
|
|
structFieldValue := structValue.FieldByName(name)
|
|
structFieldValue := structValue.FieldByName(name)
|
|
|
|
|
|
|
|
if !structFieldValue.IsValid() {
|
|
if !structFieldValue.IsValid() {
|
|
|
- return fmt.Errorf("No such field: %s in obj", name)
|
|
|
|
|
|
|
+ return fmt.Errorf("no such field: %s in obj", name)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
if !structFieldValue.CanSet() {
|
|
if !structFieldValue.CanSet() {
|
|
|
- return fmt.Errorf("Cannot set %s field value", name)
|
|
|
|
|
|
|
+ return fmt.Errorf("cannot set %s field value", name)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // If the custom pricing field is expected to be a string representation
|
|
|
|
|
+ // of a floating point number, e.g. a resource price, then do some extra
|
|
|
|
|
+ // validation work in order to prevent "NaN" and other invalid strings
|
|
|
|
|
+ // from getting set here.
|
|
|
|
|
+ switch strings.ToLower(name) {
|
|
|
|
|
+ case "cpu", "gpu", "ram", "spotcpu", "spotgpu", "spotram", "storage", "zonenetworkegress", "regionnetworkegress", "internetnetworkegress":
|
|
|
|
|
+ // Validate that "value" represents a real floating point number, and
|
|
|
|
|
+ // set precision, bits, etc. Do not allow NaN.
|
|
|
|
|
+ val, err := sanitizeFloatString(value, false)
|
|
|
|
|
+ if err != nil {
|
|
|
|
|
+ return fmt.Errorf("invalid numeric value for field '%s': %s", name, value)
|
|
|
|
|
+ }
|
|
|
|
|
+ value = val
|
|
|
|
|
+ default:
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
structFieldType := structFieldValue.Type()
|
|
structFieldType := structFieldValue.Type()
|
|
|
value = sanitizePolicy.Sanitize(value)
|
|
value = sanitizePolicy.Sanitize(value)
|
|
|
val := reflect.ValueOf(value)
|
|
val := reflect.ValueOf(value)
|
|
|
if structFieldType != val.Type() {
|
|
if structFieldType != val.Type() {
|
|
|
- return fmt.Errorf("Provided value type didn't match custom pricing field type")
|
|
|
|
|
|
|
+ return fmt.Errorf("provided value type didn't match custom pricing field type")
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
structFieldValue.Set(val)
|
|
structFieldValue.Set(val)
|