...

Source file src/k8s.io/apimachinery/pkg/util/intstr/intstr.go

Documentation: k8s.io/apimachinery/pkg/util/intstr

     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  }
   233  

View as plain text