You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
173 lines
4.8 KiB
173 lines
4.8 KiB
/* |
|
Copyright 2014 The Kubernetes Authors. |
|
|
|
Licensed under the Apache License, Version 2.0 (the "License"); |
|
you may not use this file except in compliance with the License. |
|
You may obtain a copy of the License at |
|
|
|
http://www.apache.org/licenses/LICENSE-2.0 |
|
|
|
Unless required by applicable law or agreed to in writing, software |
|
distributed under the License is distributed on an "AS IS" BASIS, |
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
See the License for the specific language governing permissions and |
|
limitations under the License. |
|
*/ |
|
|
|
package intstr |
|
|
|
import ( |
|
"encoding/json" |
|
"fmt" |
|
"math" |
|
"runtime/debug" |
|
"strconv" |
|
"strings" |
|
|
|
"github.com/golang/glog" |
|
"github.com/google/gofuzz" |
|
) |
|
|
|
// IntOrString is a type that can hold an int32 or a string. When used in |
|
// JSON or YAML marshalling and unmarshalling, it produces or consumes the |
|
// inner type. This allows you to have, for example, a JSON field that can |
|
// accept a name or number. |
|
// TODO: Rename to Int32OrString |
|
// |
|
// +protobuf=true |
|
// +protobuf.options.(gogoproto.goproto_stringer)=false |
|
// +k8s:openapi-gen=true |
|
type IntOrString struct { |
|
Type Type `protobuf:"varint,1,opt,name=type,casttype=Type"` |
|
IntVal int32 `protobuf:"varint,2,opt,name=intVal"` |
|
StrVal string `protobuf:"bytes,3,opt,name=strVal"` |
|
} |
|
|
|
// Type represents the stored type of IntOrString. |
|
type Type int |
|
|
|
const ( |
|
Int Type = iota // The IntOrString holds an int. |
|
String // The IntOrString holds a string. |
|
) |
|
|
|
// FromInt creates an IntOrString object with an int32 value. It is |
|
// your responsibility not to call this method with a value greater |
|
// than int32. |
|
// TODO: convert to (val int32) |
|
func FromInt(val int) IntOrString { |
|
if val > math.MaxInt32 || val < math.MinInt32 { |
|
glog.Errorf("value: %d overflows int32\n%s\n", val, debug.Stack()) |
|
} |
|
return IntOrString{Type: Int, IntVal: int32(val)} |
|
} |
|
|
|
// FromString creates an IntOrString object with a string value. |
|
func FromString(val string) IntOrString { |
|
return IntOrString{Type: String, StrVal: val} |
|
} |
|
|
|
// Parse the given string and try to convert it to an integer before |
|
// setting it as a string value. |
|
func Parse(val string) IntOrString { |
|
i, err := strconv.Atoi(val) |
|
if err != nil { |
|
return FromString(val) |
|
} |
|
return FromInt(i) |
|
} |
|
|
|
// UnmarshalJSON implements the json.Unmarshaller interface. |
|
func (intstr *IntOrString) UnmarshalJSON(value []byte) error { |
|
if value[0] == '"' { |
|
intstr.Type = String |
|
return json.Unmarshal(value, &intstr.StrVal) |
|
} |
|
intstr.Type = Int |
|
return json.Unmarshal(value, &intstr.IntVal) |
|
} |
|
|
|
// String returns the string value, or the Itoa of the int value. |
|
func (intstr *IntOrString) String() string { |
|
if intstr.Type == String { |
|
return intstr.StrVal |
|
} |
|
return strconv.Itoa(intstr.IntValue()) |
|
} |
|
|
|
// IntValue returns the IntVal if type Int, or if |
|
// it is a String, will attempt a conversion to int. |
|
func (intstr *IntOrString) IntValue() int { |
|
if intstr.Type == String { |
|
i, _ := strconv.Atoi(intstr.StrVal) |
|
return i |
|
} |
|
return int(intstr.IntVal) |
|
} |
|
|
|
// MarshalJSON implements the json.Marshaller interface. |
|
func (intstr IntOrString) MarshalJSON() ([]byte, error) { |
|
switch intstr.Type { |
|
case Int: |
|
return json.Marshal(intstr.IntVal) |
|
case String: |
|
return json.Marshal(intstr.StrVal) |
|
default: |
|
return []byte{}, fmt.Errorf("impossible IntOrString.Type") |
|
} |
|
} |
|
|
|
// OpenAPISchemaType is used by the kube-openapi generator when constructing |
|
// the OpenAPI spec of this type. |
|
// |
|
// See: https://github.com/kubernetes/kube-openapi/tree/master/pkg/generators |
|
func (_ IntOrString) OpenAPISchemaType() []string { return []string{"string"} } |
|
|
|
// OpenAPISchemaFormat is used by the kube-openapi generator when constructing |
|
// the OpenAPI spec of this type. |
|
func (_ IntOrString) OpenAPISchemaFormat() string { return "int-or-string" } |
|
|
|
func (intstr *IntOrString) Fuzz(c fuzz.Continue) { |
|
if intstr == nil { |
|
return |
|
} |
|
if c.RandBool() { |
|
intstr.Type = Int |
|
c.Fuzz(&intstr.IntVal) |
|
intstr.StrVal = "" |
|
} else { |
|
intstr.Type = String |
|
intstr.IntVal = 0 |
|
c.Fuzz(&intstr.StrVal) |
|
} |
|
} |
|
|
|
func GetValueFromIntOrPercent(intOrPercent *IntOrString, total int, roundUp bool) (int, error) { |
|
value, isPercent, err := getIntOrPercentValue(intOrPercent) |
|
if err != nil { |
|
return 0, fmt.Errorf("invalid value for IntOrString: %v", err) |
|
} |
|
if isPercent { |
|
if roundUp { |
|
value = int(math.Ceil(float64(value) * (float64(total)) / 100)) |
|
} else { |
|
value = int(math.Floor(float64(value) * (float64(total)) / 100)) |
|
} |
|
} |
|
return value, nil |
|
} |
|
|
|
func getIntOrPercentValue(intOrStr *IntOrString) (int, bool, error) { |
|
switch intOrStr.Type { |
|
case Int: |
|
return intOrStr.IntValue(), false, nil |
|
case String: |
|
s := strings.Replace(intOrStr.StrVal, "%", "", -1) |
|
v, err := strconv.Atoi(s) |
|
if err != nil { |
|
return 0, false, fmt.Errorf("invalid value %q: %v", intOrStr.StrVal, err) |
|
} |
|
return int(v), true, nil |
|
} |
|
return 0, false, fmt.Errorf("invalid type: neither int nor percentage") |
|
}
|
|
|