github.com/spotmaxtech/k8s-apimachinery-v0260@v0.0.1/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 // TODO: convert to (val int32) 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 // FromString creates an IntOrString object with a string value. 66 func FromString(val string) IntOrString { 67 return IntOrString{Type: String, StrVal: val} 68 } 69 70 // Parse the given string and try to convert it to an integer before 71 // setting it as a string value. 72 func Parse(val string) IntOrString { 73 i, err := strconv.Atoi(val) 74 if err != nil { 75 return FromString(val) 76 } 77 return FromInt(i) 78 } 79 80 // UnmarshalJSON implements the json.Unmarshaller interface. 81 func (intstr *IntOrString) UnmarshalJSON(value []byte) error { 82 if value[0] == '"' { 83 intstr.Type = String 84 return json.Unmarshal(value, &intstr.StrVal) 85 } 86 intstr.Type = Int 87 return json.Unmarshal(value, &intstr.IntVal) 88 } 89 90 // String returns the string value, or the Itoa of the int value. 91 func (intstr *IntOrString) String() string { 92 if intstr == nil { 93 return "<nil>" 94 } 95 if intstr.Type == String { 96 return intstr.StrVal 97 } 98 return strconv.Itoa(intstr.IntValue()) 99 } 100 101 // IntValue returns the IntVal if type Int, or if 102 // it is a String, will attempt a conversion to int, 103 // returning 0 if a parsing error occurs. 104 func (intstr *IntOrString) IntValue() int { 105 if intstr.Type == String { 106 i, _ := strconv.Atoi(intstr.StrVal) 107 return i 108 } 109 return int(intstr.IntVal) 110 } 111 112 // MarshalJSON implements the json.Marshaller interface. 113 func (intstr IntOrString) MarshalJSON() ([]byte, error) { 114 switch intstr.Type { 115 case Int: 116 return json.Marshal(intstr.IntVal) 117 case String: 118 return json.Marshal(intstr.StrVal) 119 default: 120 return []byte{}, fmt.Errorf("impossible IntOrString.Type") 121 } 122 } 123 124 // OpenAPISchemaType is used by the kube-openapi generator when constructing 125 // the OpenAPI spec of this type. 126 // 127 // See: https://github.com/kubernetes/kube-openapi/tree/master/pkg/generators 128 func (IntOrString) OpenAPISchemaType() []string { return []string{"string"} } 129 130 // OpenAPISchemaFormat is used by the kube-openapi generator when constructing 131 // the OpenAPI spec of this type. 132 func (IntOrString) OpenAPISchemaFormat() string { return "int-or-string" } 133 134 // OpenAPIV3OneOfTypes is used by the kube-openapi generator when constructing 135 // the OpenAPI v3 spec of this type. 136 func (IntOrString) OpenAPIV3OneOfTypes() []string { return []string{"integer", "string"} } 137 138 func ValueOrDefault(intOrPercent *IntOrString, defaultValue IntOrString) *IntOrString { 139 if intOrPercent == nil { 140 return &defaultValue 141 } 142 return intOrPercent 143 } 144 145 // GetScaledValueFromIntOrPercent is meant to replace GetValueFromIntOrPercent. 146 // This method returns a scaled value from an IntOrString type. If the IntOrString 147 // is a percentage string value it's treated as a percentage and scaled appropriately 148 // in accordance to the total, if it's an int value it's treated as a simple value and 149 // if it is a string value which is either non-numeric or numeric but lacking a trailing '%' it returns an error. 150 func GetScaledValueFromIntOrPercent(intOrPercent *IntOrString, total int, roundUp bool) (int, error) { 151 if intOrPercent == nil { 152 return 0, errors.New("nil value for IntOrString") 153 } 154 value, isPercent, err := getIntOrPercentValueSafely(intOrPercent) 155 if err != nil { 156 return 0, fmt.Errorf("invalid value for IntOrString: %v", err) 157 } 158 if isPercent { 159 if roundUp { 160 value = int(math.Ceil(float64(value) * (float64(total)) / 100)) 161 } else { 162 value = int(math.Floor(float64(value) * (float64(total)) / 100)) 163 } 164 } 165 return value, nil 166 } 167 168 // GetValueFromIntOrPercent was deprecated in favor of 169 // GetScaledValueFromIntOrPercent. This method was treating all int as a numeric value and all 170 // strings with or without a percent symbol as a percentage value. 171 // Deprecated 172 func GetValueFromIntOrPercent(intOrPercent *IntOrString, total int, roundUp bool) (int, error) { 173 if intOrPercent == nil { 174 return 0, errors.New("nil value for IntOrString") 175 } 176 value, isPercent, err := getIntOrPercentValue(intOrPercent) 177 if err != nil { 178 return 0, fmt.Errorf("invalid value for IntOrString: %v", err) 179 } 180 if isPercent { 181 if roundUp { 182 value = int(math.Ceil(float64(value) * (float64(total)) / 100)) 183 } else { 184 value = int(math.Floor(float64(value) * (float64(total)) / 100)) 185 } 186 } 187 return value, nil 188 } 189 190 // getIntOrPercentValue is a legacy function and only meant to be called by GetValueFromIntOrPercent 191 // For a more correct implementation call getIntOrPercentSafely 192 func getIntOrPercentValue(intOrStr *IntOrString) (int, bool, error) { 193 switch intOrStr.Type { 194 case Int: 195 return intOrStr.IntValue(), false, nil 196 case String: 197 s := strings.Replace(intOrStr.StrVal, "%", "", -1) 198 v, err := strconv.Atoi(s) 199 if err != nil { 200 return 0, false, fmt.Errorf("invalid value %q: %v", intOrStr.StrVal, err) 201 } 202 return int(v), true, nil 203 } 204 return 0, false, fmt.Errorf("invalid type: neither int nor percentage") 205 } 206 207 func getIntOrPercentValueSafely(intOrStr *IntOrString) (int, bool, error) { 208 switch intOrStr.Type { 209 case Int: 210 return intOrStr.IntValue(), false, nil 211 case String: 212 isPercent := false 213 s := intOrStr.StrVal 214 if strings.HasSuffix(s, "%") { 215 isPercent = true 216 s = strings.TrimSuffix(intOrStr.StrVal, "%") 217 } else { 218 return 0, false, fmt.Errorf("invalid type: string is not a percentage") 219 } 220 v, err := strconv.Atoi(s) 221 if err != nil { 222 return 0, false, fmt.Errorf("invalid value %q: %v", intOrStr.StrVal, err) 223 } 224 return int(v), isPercent, nil 225 } 226 return 0, false, fmt.Errorf("invalid type: neither int nor percentage") 227 }