blob: 642b83cec2173813c75bd55f7aa52123b1b3e439 [file] [log] [blame]
Zack Williamse940c7a2019-08-21 14:25:39 -07001/*
2Copyright 2014 The Kubernetes Authors.
3
4Licensed under the Apache License, Version 2.0 (the "License");
5you may not use this file except in compliance with the License.
6You may obtain a copy of the License at
7
8 http://www.apache.org/licenses/LICENSE-2.0
9
10Unless required by applicable law or agreed to in writing, software
11distributed under the License is distributed on an "AS IS" BASIS,
12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13See the License for the specific language governing permissions and
14limitations under the License.
15*/
16
17package intstr
18
19import (
20 "encoding/json"
21 "errors"
22 "fmt"
23 "math"
24 "runtime/debug"
25 "strconv"
26 "strings"
27
28 "github.com/golang/glog"
29 "github.com/google/gofuzz"
30)
31
32// IntOrString is a type that can hold an int32 or a string. When used in
33// JSON or YAML marshalling and unmarshalling, it produces or consumes the
34// inner type. This allows you to have, for example, a JSON field that can
35// accept a name or number.
36// TODO: Rename to Int32OrString
37//
38// +protobuf=true
39// +protobuf.options.(gogoproto.goproto_stringer)=false
40// +k8s:openapi-gen=true
41type IntOrString struct {
42 Type Type `protobuf:"varint,1,opt,name=type,casttype=Type"`
43 IntVal int32 `protobuf:"varint,2,opt,name=intVal"`
44 StrVal string `protobuf:"bytes,3,opt,name=strVal"`
45}
46
47// Type represents the stored type of IntOrString.
48type Type int
49
50const (
51 Int Type = iota // The IntOrString holds an int.
52 String // The IntOrString holds a string.
53)
54
55// FromInt creates an IntOrString object with an int32 value. It is
56// your responsibility not to call this method with a value greater
57// than int32.
58// TODO: convert to (val int32)
59func FromInt(val int) IntOrString {
60 if val > math.MaxInt32 || val < math.MinInt32 {
61 glog.Errorf("value: %d overflows int32\n%s\n", val, debug.Stack())
62 }
63 return IntOrString{Type: Int, IntVal: int32(val)}
64}
65
66// FromString creates an IntOrString object with a string value.
67func FromString(val string) IntOrString {
68 return IntOrString{Type: String, StrVal: val}
69}
70
71// Parse the given string and try to convert it to an integer before
72// setting it as a string value.
73func Parse(val string) IntOrString {
74 i, err := strconv.Atoi(val)
75 if err != nil {
76 return FromString(val)
77 }
78 return FromInt(i)
79}
80
81// UnmarshalJSON implements the json.Unmarshaller interface.
82func (intstr *IntOrString) UnmarshalJSON(value []byte) error {
83 if value[0] == '"' {
84 intstr.Type = String
85 return json.Unmarshal(value, &intstr.StrVal)
86 }
87 intstr.Type = Int
88 return json.Unmarshal(value, &intstr.IntVal)
89}
90
91// String returns the string value, or the Itoa of the int value.
92func (intstr *IntOrString) String() string {
93 if intstr.Type == String {
94 return intstr.StrVal
95 }
96 return strconv.Itoa(intstr.IntValue())
97}
98
99// IntValue returns the IntVal if type Int, or if
100// it is a String, will attempt a conversion to int.
101func (intstr *IntOrString) IntValue() int {
102 if intstr.Type == String {
103 i, _ := strconv.Atoi(intstr.StrVal)
104 return i
105 }
106 return int(intstr.IntVal)
107}
108
109// MarshalJSON implements the json.Marshaller interface.
110func (intstr IntOrString) MarshalJSON() ([]byte, error) {
111 switch intstr.Type {
112 case Int:
113 return json.Marshal(intstr.IntVal)
114 case String:
115 return json.Marshal(intstr.StrVal)
116 default:
117 return []byte{}, fmt.Errorf("impossible IntOrString.Type")
118 }
119}
120
121// OpenAPISchemaType is used by the kube-openapi generator when constructing
122// the OpenAPI spec of this type.
123//
124// See: https://github.com/kubernetes/kube-openapi/tree/master/pkg/generators
125func (_ IntOrString) OpenAPISchemaType() []string { return []string{"string"} }
126
127// OpenAPISchemaFormat is used by the kube-openapi generator when constructing
128// the OpenAPI spec of this type.
129func (_ IntOrString) OpenAPISchemaFormat() string { return "int-or-string" }
130
131func (intstr *IntOrString) Fuzz(c fuzz.Continue) {
132 if intstr == nil {
133 return
134 }
135 if c.RandBool() {
136 intstr.Type = Int
137 c.Fuzz(&intstr.IntVal)
138 intstr.StrVal = ""
139 } else {
140 intstr.Type = String
141 intstr.IntVal = 0
142 c.Fuzz(&intstr.StrVal)
143 }
144}
145
146func ValueOrDefault(intOrPercent *IntOrString, defaultValue IntOrString) *IntOrString {
147 if intOrPercent == nil {
148 return &defaultValue
149 }
150 return intOrPercent
151}
152
153func GetValueFromIntOrPercent(intOrPercent *IntOrString, total int, roundUp bool) (int, error) {
154 if intOrPercent == nil {
155 return 0, errors.New("nil value for IntOrString")
156 }
157 value, isPercent, err := getIntOrPercentValue(intOrPercent)
158 if err != nil {
159 return 0, fmt.Errorf("invalid value for IntOrString: %v", err)
160 }
161 if isPercent {
162 if roundUp {
163 value = int(math.Ceil(float64(value) * (float64(total)) / 100))
164 } else {
165 value = int(math.Floor(float64(value) * (float64(total)) / 100))
166 }
167 }
168 return value, nil
169}
170
171func getIntOrPercentValue(intOrStr *IntOrString) (int, bool, error) {
172 switch intOrStr.Type {
173 case Int:
174 return intOrStr.IntValue(), false, nil
175 case String:
176 s := strings.Replace(intOrStr.StrVal, "%", "", -1)
177 v, err := strconv.Atoi(s)
178 if err != nil {
179 return 0, false, fmt.Errorf("invalid value %q: %v", intOrStr.StrVal, err)
180 }
181 return int(v), true, nil
182 }
183 return 0, false, fmt.Errorf("invalid type: neither int nor percentage")
184}