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  }