blob: 231498ca03246e9c53ad83530f578b3c3f532c90 [file] [log] [blame]
Matthias Andreas Benkard832a54e2019-01-29 09:27:38 +01001/*
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 "fmt"
22 "math"
23 "runtime/debug"
24 "strconv"
25 "strings"
26
27 "github.com/golang/glog"
28 "github.com/google/gofuzz"
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
40type 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.
47type Type int
48
49const (
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)
58func FromInt(val int) IntOrString {
59 if val > math.MaxInt32 || val < math.MinInt32 {
60 glog.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.
66func 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.
72func 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.
81func (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.
91func (intstr *IntOrString) String() string {
92 if intstr.Type == String {
93 return intstr.StrVal
94 }
95 return strconv.Itoa(intstr.IntValue())
96}
97
98// IntValue returns the IntVal if type Int, or if
99// it is a String, will attempt a conversion to int.
100func (intstr *IntOrString) IntValue() int {
101 if intstr.Type == String {
102 i, _ := strconv.Atoi(intstr.StrVal)
103 return i
104 }
105 return int(intstr.IntVal)
106}
107
108// MarshalJSON implements the json.Marshaller interface.
109func (intstr IntOrString) MarshalJSON() ([]byte, error) {
110 switch intstr.Type {
111 case Int:
112 return json.Marshal(intstr.IntVal)
113 case String:
114 return json.Marshal(intstr.StrVal)
115 default:
116 return []byte{}, fmt.Errorf("impossible IntOrString.Type")
117 }
118}
119
120// OpenAPISchemaType is used by the kube-openapi generator when constructing
121// the OpenAPI spec of this type.
122//
123// See: https://github.com/kubernetes/kube-openapi/tree/master/pkg/generators
124func (_ IntOrString) OpenAPISchemaType() []string { return []string{"string"} }
125
126// OpenAPISchemaFormat is used by the kube-openapi generator when constructing
127// the OpenAPI spec of this type.
128func (_ IntOrString) OpenAPISchemaFormat() string { return "int-or-string" }
129
130func (intstr *IntOrString) Fuzz(c fuzz.Continue) {
131 if intstr == nil {
132 return
133 }
134 if c.RandBool() {
135 intstr.Type = Int
136 c.Fuzz(&intstr.IntVal)
137 intstr.StrVal = ""
138 } else {
139 intstr.Type = String
140 intstr.IntVal = 0
141 c.Fuzz(&intstr.StrVal)
142 }
143}
144
145func GetValueFromIntOrPercent(intOrPercent *IntOrString, total int, roundUp bool) (int, error) {
146 value, isPercent, err := getIntOrPercentValue(intOrPercent)
147 if err != nil {
148 return 0, fmt.Errorf("invalid value for IntOrString: %v", err)
149 }
150 if isPercent {
151 if roundUp {
152 value = int(math.Ceil(float64(value) * (float64(total)) / 100))
153 } else {
154 value = int(math.Floor(float64(value) * (float64(total)) / 100))
155 }
156 }
157 return value, nil
158}
159
160func getIntOrPercentValue(intOrStr *IntOrString) (int, bool, error) {
161 switch intOrStr.Type {
162 case Int:
163 return intOrStr.IntValue(), false, nil
164 case String:
165 s := strings.Replace(intOrStr.StrVal, "%", "", -1)
166 v, err := strconv.Atoi(s)
167 if err != nil {
168 return 0, false, fmt.Errorf("invalid value %q: %v", intOrStr.StrVal, err)
169 }
170 return int(v), true, nil
171 }
172 return 0, false, fmt.Errorf("invalid type: neither int nor percentage")
173}