blob: 6576def82e71cc7b1e1b780be4db3fd48ed9e5b7 [file] [log] [blame]
Matteo Scandoloa4285862020-12-01 18:10:10 -08001/*
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/google/gofuzz"
29 "k8s.io/klog/v2"
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 int64
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 klog.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,
101// returning 0 if a parsing error occurs.
102func (intstr *IntOrString) IntValue() int {
103 if intstr.Type == String {
104 i, _ := strconv.Atoi(intstr.StrVal)
105 return i
106 }
107 return int(intstr.IntVal)
108}
109
110// MarshalJSON implements the json.Marshaller interface.
111func (intstr IntOrString) MarshalJSON() ([]byte, error) {
112 switch intstr.Type {
113 case Int:
114 return json.Marshal(intstr.IntVal)
115 case String:
116 return json.Marshal(intstr.StrVal)
117 default:
118 return []byte{}, fmt.Errorf("impossible IntOrString.Type")
119 }
120}
121
122// OpenAPISchemaType is used by the kube-openapi generator when constructing
123// the OpenAPI spec of this type.
124//
125// See: https://github.com/kubernetes/kube-openapi/tree/master/pkg/generators
126func (IntOrString) OpenAPISchemaType() []string { return []string{"string"} }
127
128// OpenAPISchemaFormat is used by the kube-openapi generator when constructing
129// the OpenAPI spec of this type.
130func (IntOrString) OpenAPISchemaFormat() string { return "int-or-string" }
131
132func (intstr *IntOrString) Fuzz(c fuzz.Continue) {
133 if intstr == nil {
134 return
135 }
136 if c.RandBool() {
137 intstr.Type = Int
138 c.Fuzz(&intstr.IntVal)
139 intstr.StrVal = ""
140 } else {
141 intstr.Type = String
142 intstr.IntVal = 0
143 c.Fuzz(&intstr.StrVal)
144 }
145}
146
147func ValueOrDefault(intOrPercent *IntOrString, defaultValue IntOrString) *IntOrString {
148 if intOrPercent == nil {
149 return &defaultValue
150 }
151 return intOrPercent
152}
153
154func GetValueFromIntOrPercent(intOrPercent *IntOrString, total int, roundUp bool) (int, error) {
155 if intOrPercent == nil {
156 return 0, errors.New("nil value for IntOrString")
157 }
158 value, isPercent, err := getIntOrPercentValue(intOrPercent)
159 if err != nil {
160 return 0, fmt.Errorf("invalid value for IntOrString: %v", err)
161 }
162 if isPercent {
163 if roundUp {
164 value = int(math.Ceil(float64(value) * (float64(total)) / 100))
165 } else {
166 value = int(math.Floor(float64(value) * (float64(total)) / 100))
167 }
168 }
169 return value, nil
170}
171
172func getIntOrPercentValue(intOrStr *IntOrString) (int, bool, error) {
173 switch intOrStr.Type {
174 case Int:
175 return intOrStr.IntValue(), false, nil
176 case String:
177 s := strings.Replace(intOrStr.StrVal, "%", "", -1)
178 v, err := strconv.Atoi(s)
179 if err != nil {
180 return 0, false, fmt.Errorf("invalid value %q: %v", intOrStr.StrVal, err)
181 }
182 return int(v), true, nil
183 }
184 return 0, false, fmt.Errorf("invalid type: neither int nor percentage")
185}