k8s.io/apimachinery@v0.29.2/pkg/util/intstr/intstr.go (about) 1 /* 2 Copyright 2014 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package intstr 18 19 import ( 20 "encoding/json" 21 "errors" 22 "fmt" 23 "math" 24 "runtime/debug" 25 "strconv" 26 "strings" 27 28 "k8s.io/klog/v2" 29 ) 30 31 // IntOrString is a type that can hold an int32 or a string. When used in 32 // JSON or YAML marshalling and unmarshalling, it produces or consumes the 33 // inner type. This allows you to have, for example, a JSON field that can 34 // accept a name or number. 35 // TODO: Rename to Int32OrString 36 // 37 // +protobuf=true 38 // +protobuf.options.(gogoproto.goproto_stringer)=false 39 // +k8s:openapi-gen=true 40 type IntOrString struct { 41 Type Type `protobuf:"varint,1,opt,name=type,casttype=Type"` 42 IntVal int32 `protobuf:"varint,2,opt,name=intVal"` 43 StrVal string `protobuf:"bytes,3,opt,name=strVal"` 44 } 45 46 // Type represents the stored type of IntOrString. 47 type Type int64 48 49 const ( 50 Int Type = iota // The IntOrString holds an int. 51 String // The IntOrString holds a string. 52 ) 53 54 // FromInt creates an IntOrString object with an int32 value. It is 55 // your responsibility not to call this method with a value greater 56 // than int32. 57 // Deprecated: use FromInt32 instead. 58 func FromInt(val int) IntOrString { 59 if val > math.MaxInt32 || val < math.MinInt32 { 60 klog.Errorf("value: %d overflows int32\n%s\n", val, debug.Stack()) 61 } 62 return IntOrString{Type: Int, IntVal: int32(val)} 63 } 64 65 // FromInt32 creates an IntOrString object with an int32 value. 66 func FromInt32(val int32) IntOrString { 67 return IntOrString{Type: Int, IntVal: val} 68 } 69 70 // FromString creates an IntOrString object with a string value. 71 func FromString(val string) IntOrString { 72 return IntOrString{Type: String, StrVal: val} 73 } 74 75 // Parse the given string and try to convert it to an int32 integer before 76 // setting it as a string value. 77 func Parse(val string) IntOrString { 78 i, err := strconv.ParseInt(val, 10, 32) 79 if err != nil { 80 return FromString(val) 81 } 82 return FromInt32(int32(i)) 83 } 84 85 // UnmarshalJSON implements the json.Unmarshaller interface. 86 func (intstr *IntOrString) UnmarshalJSON(value []byte) error { 87 if value[0] == '"' { 88 intstr.Type = String 89 return json.Unmarshal(value, &intstr.StrVal) 90 } 91 intstr.Type = Int 92 return json.Unmarshal(value, &intstr.IntVal) 93 } 94 95 // String returns the string value, or the Itoa of the int value. 96 func (intstr *IntOrString) String() string { 97 if intstr == nil { 98 return "<nil>" 99 } 100 if intstr.Type == String { 101 return intstr.StrVal 102 } 103 return strconv.Itoa(intstr.IntValue()) 104 } 105 106 // IntValue returns the IntVal if type Int, or if 107 // it is a String, will attempt a conversion to int, 108 // returning 0 if a parsing error occurs. 109 func (intstr *IntOrString) IntValue() int { 110 if intstr.Type == String { 111 i, _ := strconv.Atoi(intstr.StrVal) 112 return i 113 } 114 return int(intstr.IntVal) 115 } 116 117 // MarshalJSON implements the json.Marshaller interface. 118 func (intstr IntOrString) MarshalJSON() ([]byte, error) { 119 switch intstr.Type { 120 case Int: 121 return json.Marshal(intstr.IntVal) 122 case String: 123 return json.Marshal(intstr.StrVal) 124 default: 125 return []byte{}, fmt.Errorf("impossible IntOrString.Type") 126 } 127 } 128 129 // OpenAPISchemaType is used by the kube-openapi generator when constructing 130 // the OpenAPI spec of this type. 131 // 132 // See: https://github.com/kubernetes/kube-openapi/tree/master/pkg/generators 133 func (IntOrString) OpenAPISchemaType() []string { return []string{"string"} } 134 135 // OpenAPISchemaFormat is used by the kube-openapi generator when constructing 136 // the OpenAPI spec of this type. 137 func (IntOrString) OpenAPISchemaFormat() string { return "int-or-string" } 138 139 // OpenAPIV3OneOfTypes is used by the kube-openapi generator when constructing 140 // the OpenAPI v3 spec of this type. 141 func (IntOrString) OpenAPIV3OneOfTypes() []string { return []string{"integer", "string"} } 142 143 func ValueOrDefault(intOrPercent *IntOrString, defaultValue IntOrString) *IntOrString { 144 if intOrPercent == nil { 145 return &defaultValue 146 } 147 return intOrPercent 148 } 149 150 // GetScaledValueFromIntOrPercent is meant to replace GetValueFromIntOrPercent. 151 // This method returns a scaled value from an IntOrString type. If the IntOrString 152 // is a percentage string value it's treated as a percentage and scaled appropriately 153 // in accordance to the total, if it's an int value it's treated as a simple value and 154 // if it is a string value which is either non-numeric or numeric but lacking a trailing '%' it returns an error. 155 func GetScaledValueFromIntOrPercent(intOrPercent *IntOrString, total int, roundUp bool) (int, error) { 156 if intOrPercent == nil { 157 return 0, errors.New("nil value for IntOrString") 158 } 159 value, isPercent, err := getIntOrPercentValueSafely(intOrPercent) 160 if err != nil { 161 return 0, fmt.Errorf("invalid value for IntOrString: %v", err) 162 } 163 if isPercent { 164 if roundUp { 165 value = int(math.Ceil(float64(value) * (float64(total)) / 100)) 166 } else { 167 value = int(math.Floor(float64(value) * (float64(total)) / 100)) 168 } 169 } 170 return value, nil 171 } 172 173 // GetValueFromIntOrPercent was deprecated in favor of 174 // GetScaledValueFromIntOrPercent. This method was treating all int as a numeric value and all 175 // strings with or without a percent symbol as a percentage value. 176 // Deprecated 177 func GetValueFromIntOrPercent(intOrPercent *IntOrString, total int, roundUp bool) (int, error) { 178 if intOrPercent == nil { 179 return 0, errors.New("nil value for IntOrString") 180 } 181 value, isPercent, err := getIntOrPercentValue(intOrPercent) 182 if err != nil { 183 return 0, fmt.Errorf("invalid value for IntOrString: %v", err) 184 } 185 if isPercent { 186 if roundUp { 187 value = int(math.Ceil(float64(value) * (float64(total)) / 100)) 188 } else { 189 value = int(math.Floor(float64(value) * (float64(total)) / 100)) 190 } 191 } 192 return value, nil 193 } 194 195 // getIntOrPercentValue is a legacy function and only meant to be called by GetValueFromIntOrPercent 196 // For a more correct implementation call getIntOrPercentSafely 197 func getIntOrPercentValue(intOrStr *IntOrString) (int, bool, error) { 198 switch intOrStr.Type { 199 case Int: 200 return intOrStr.IntValue(), false, nil 201 case String: 202 s := strings.Replace(intOrStr.StrVal, "%", "", -1) 203 v, err := strconv.Atoi(s) 204 if err != nil { 205 return 0, false, fmt.Errorf("invalid value %q: %v", intOrStr.StrVal, err) 206 } 207 return int(v), true, nil 208 } 209 return 0, false, fmt.Errorf("invalid type: neither int nor percentage") 210 } 211 212 func getIntOrPercentValueSafely(intOrStr *IntOrString) (int, bool, error) { 213 switch intOrStr.Type { 214 case Int: 215 return intOrStr.IntValue(), false, nil 216 case String: 217 isPercent := false 218 s := intOrStr.StrVal 219 if strings.HasSuffix(s, "%") { 220 isPercent = true 221 s = strings.TrimSuffix(intOrStr.StrVal, "%") 222 } else { 223 return 0, false, fmt.Errorf("invalid type: string is not a percentage") 224 } 225 v, err := strconv.Atoi(s) 226 if err != nil { 227 return 0, false, fmt.Errorf("invalid value %q: %v", intOrStr.StrVal, err) 228 } 229 return int(v), isPercent, nil 230 } 231 return 0, false, fmt.Errorf("invalid type: neither int nor percentage") 232 }