diff --git a/cmd/kubelet/app/server.go b/cmd/kubelet/app/server.go index bf2cc623d9e..a3a1076a48b 100644 --- a/cmd/kubelet/app/server.go +++ b/cmd/kubelet/app/server.go @@ -927,10 +927,10 @@ func parseResourceList(m utilconfig.ConfigurationMap) (api.ResourceList, error) if err != nil { return nil, err } - if q.Amount.Sign() == -1 { + if q.Sign() == -1 { return nil, fmt.Errorf("resource quantity for %q cannot be negative: %v", k, v) } - rl[api.ResourceName(k)] = *q + rl[api.ResourceName(k)] = q default: return nil, fmt.Errorf("cannot reserve %q resource", k) } diff --git a/contrib/mesos/pkg/scheduler/resources/resource.go b/contrib/mesos/pkg/scheduler/resources/resource.go index 792ab0cf413..660b4a69f72 100644 --- a/contrib/mesos/pkg/scheduler/resources/resource.go +++ b/contrib/mesos/pkg/scheduler/resources/resource.go @@ -85,14 +85,8 @@ func podResources(pod *api.Pod, resourceName api.ResourceName, def, min resource } // add up the request and limit sum for all containers - err = requestSum.Add(request) - if err != nil { - return - } - err = limitSum.Add(limit) - if err != nil { - return - } + requestSum.Add(request) + limitSum.Add(limit) // optionally write request and limit back if write { diff --git a/federation/apis/federation/types.generated.go b/federation/apis/federation/types.generated.go index afde3d9b852..2f75772c90e 100644 --- a/federation/apis/federation/types.generated.go +++ b/federation/apis/federation/types.generated.go @@ -25,11 +25,10 @@ import ( "errors" "fmt" codec1978 "github.com/ugorji/go/codec" - pkg4_inf_v0 "gopkg.in/inf.v0" pkg1_api "k8s.io/kubernetes/pkg/api" pkg3_resource "k8s.io/kubernetes/pkg/api/resource" pkg2_unversioned "k8s.io/kubernetes/pkg/api/unversioned" - pkg5_types "k8s.io/kubernetes/pkg/types" + pkg4_types "k8s.io/kubernetes/pkg/types" "reflect" "runtime" time "time" @@ -65,13 +64,12 @@ func init() { panic(err) } if false { // reference the types, but skip this branch at build/run time - var v0 pkg4_inf_v0.Dec - var v1 pkg1_api.ConditionStatus - var v2 pkg3_resource.Quantity - var v3 pkg2_unversioned.Time - var v4 pkg5_types.UID - var v5 time.Time - _, _, _, _, _, _ = v0, v1, v2, v3, v4, v5 + var v0 pkg1_api.ConditionStatus + var v1 pkg3_resource.Quantity + var v2 pkg2_unversioned.Time + var v3 pkg4_types.UID + var v4 time.Time + _, _, _, _, _ = v0, v1, v2, v3, v4 } } diff --git a/federation/apis/federation/v1alpha1/types.generated.go b/federation/apis/federation/v1alpha1/types.generated.go index 3bb77246a7f..70837248d6c 100644 --- a/federation/apis/federation/v1alpha1/types.generated.go +++ b/federation/apis/federation/v1alpha1/types.generated.go @@ -25,11 +25,10 @@ import ( "errors" "fmt" codec1978 "github.com/ugorji/go/codec" - pkg4_inf_v0 "gopkg.in/inf.v0" pkg3_resource "k8s.io/kubernetes/pkg/api/resource" pkg2_unversioned "k8s.io/kubernetes/pkg/api/unversioned" pkg1_v1 "k8s.io/kubernetes/pkg/api/v1" - pkg5_types "k8s.io/kubernetes/pkg/types" + pkg4_types "k8s.io/kubernetes/pkg/types" "reflect" "runtime" time "time" @@ -65,13 +64,12 @@ func init() { panic(err) } if false { // reference the types, but skip this branch at build/run time - var v0 pkg4_inf_v0.Dec - var v1 pkg3_resource.Quantity - var v2 pkg2_unversioned.Time - var v3 pkg1_v1.ConditionStatus - var v4 pkg5_types.UID - var v5 time.Time - _, _, _, _, _, _ = v0, v1, v2, v3, v4, v5 + var v0 pkg3_resource.Quantity + var v1 pkg2_unversioned.Time + var v2 pkg1_v1.ConditionStatus + var v3 pkg4_types.UID + var v4 time.Time + _, _, _, _, _ = v0, v1, v2, v3, v4 } } diff --git a/hack/lib/golang.sh b/hack/lib/golang.sh index 4f5c4b7f52d..1d9b21dde4e 100755 --- a/hack/lib/golang.sh +++ b/hack/lib/golang.sh @@ -68,7 +68,7 @@ else linux/amd64 linux/arm linux/arm64 - linux/ppc64le + #linux/ppc64le # temporarily disabled due to a linking error ) # If we update this we should also update the set of golang compilers we build @@ -78,7 +78,7 @@ else linux/386 linux/arm linux/arm64 - linux/ppc64le + #linux/ppc64le # temporarily disabled due to a linking error darwin/amd64 darwin/386 windows/amd64 diff --git a/pkg/api/helpers.go b/pkg/api/helpers.go index 070e39630de..7af5ed8af50 100644 --- a/pkg/api/helpers.go +++ b/pkg/api/helpers.go @@ -58,16 +58,7 @@ var Semantic = conversion.EqualitiesOrDie( // TODO: if we decide it's important, it should be safe to start comparing the format. // // Uninitialized quantities are equivalent to 0 quantities. - if a.Amount == nil && b.MilliValue() == 0 { - return true - } - if b.Amount == nil && a.MilliValue() == 0 { - return true - } - if a.Amount == nil || b.Amount == nil { - return false - } - return a.Amount.Cmp(b.Amount) == 0 + return a.Cmp(b) == 0 }, func(a, b unversioned.Time) bool { return a.UTC() == b.UTC() diff --git a/pkg/api/helpers_test.go b/pkg/api/helpers_test.go index 142cf7307e0..28c696b43c6 100644 --- a/pkg/api/helpers_test.go +++ b/pkg/api/helpers_test.go @@ -23,8 +23,6 @@ import ( "k8s.io/kubernetes/pkg/api/resource" "k8s.io/kubernetes/pkg/labels" - - inf "gopkg.in/inf.v0" ) func TestConversionError(t *testing.T) { @@ -56,8 +54,8 @@ func TestSemantic(t *testing.T) { {resource.Quantity{}, resource.MustParse("0"), true}, {resource.Quantity{}, resource.MustParse("1m"), false}, { - resource.Quantity{Amount: inf.NewDec(5, 0), Format: resource.BinarySI}, - resource.Quantity{Amount: inf.NewDec(5, 0), Format: resource.DecimalSI}, + resource.NewQuantity(5, resource.BinarySI), + resource.NewQuantity(5, resource.DecimalSI), true, }, {resource.MustParse("2m"), resource.MustParse("1m"), false}, diff --git a/pkg/api/resource/amount.go b/pkg/api/resource/amount.go new file mode 100644 index 00000000000..49caf3c1736 --- /dev/null +++ b/pkg/api/resource/amount.go @@ -0,0 +1,298 @@ +/* +Copyright 2014 The Kubernetes Authors All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package resource + +import ( + "math/big" + "strconv" + + inf "gopkg.in/inf.v0" +) + +// Scale is used for getting and setting the base-10 scaled value. +// Base-2 scales are omitted for mathematical simplicity. +// See Quantity.ScaledValue for more details. +type Scale int32 + +// infScale adapts a Scale value to an inf.Scale value. +func (s Scale) infScale() inf.Scale { + return inf.Scale(-s) // inf.Scale is upside-down +} + +const ( + Nano Scale = -9 + Micro Scale = -6 + Milli Scale = -3 + Kilo Scale = 3 + Mega Scale = 6 + Giga Scale = 9 + Tera Scale = 12 + Peta Scale = 15 + Exa Scale = 18 +) + +var ( + Zero = int64Amount{} + + // Used by quantity strings - treat as read only + zeroBytes = []byte("0") +) + +// int64Amount represents a fixed precision numerator and arbitary scale exponent. It is faster +// than operations on inf.Dec for values that can be represented as int64. +type int64Amount struct { + value int64 + scale Scale +} + +// Sign returns 0 if the value is zero, -1 if it is less than 0, or 1 if it is greater than 0. +func (a int64Amount) Sign() int { + switch { + case a.value == 0: + return 0 + case a.value > 0: + return 1 + default: + return -1 + } +} + +// AsInt64 returns the current amount as an int64 at scale 0, or false if the value cannot be +// represented in an int64 OR would result in a loss of precision. This method is intended as +// an optimization to avoid calling AsDec. +func (a int64Amount) AsInt64() (int64, bool) { + if a.scale == 0 { + return a.value, true + } + if a.scale < 0 { + // TODO: attempt to reduce factors, although it is assumed that factors are reduced prior + // to the int64Amount being created. + return 0, false + } + return positiveScaleInt64(a.value, a.scale) +} + +// AsScaledInt64 returns an int64 representing the value of this amount at the specified scale, +// rounding up, or false if that would result in overflow. (1e20).AsScaledInt64(1) would result +// in overflow because 1e19 is not representable as an int64. Note that setting a scale larger +// than the current value may result in loss of precision - i.e. (1e-6).AsScaledInt64(0) would +// return 1, because 0.000001 is rounded up to 1. +func (a int64Amount) AsScaledInt64(scale Scale) (result int64, ok bool) { + if a.scale < scale { + result, _ = negativeScaleInt64(a.value, scale-a.scale) + return result, true + } + return positiveScaleInt64(a.value, a.scale-scale) +} + +// AsDec returns an inf.Dec representation of this value. +func (a int64Amount) AsDec() *inf.Dec { + var base inf.Dec + base.SetUnscaled(a.value) + base.SetScale(inf.Scale(-a.scale)) + return &base +} + +// Cmp returns 0 if a and b are equal, 1 if a is greater than b, or -1 if a is less than b. +func (a int64Amount) Cmp(b int64Amount) int { + switch { + case a.scale == b.scale: + // compare only the unscaled portion + case a.scale > b.scale: + result, remainder, exact := divideByScaleInt64(b.value, a.scale-b.scale) + if !exact { + return a.AsDec().Cmp(b.AsDec()) + } + if result == a.value { + switch { + case remainder == 0: + return 0 + case remainder > 0: + return -1 + default: + return 1 + } + } + b.value = result + default: + result, remainder, exact := divideByScaleInt64(a.value, b.scale-a.scale) + if !exact { + return a.AsDec().Cmp(b.AsDec()) + } + if result == b.value { + switch { + case remainder == 0: + return 0 + case remainder > 0: + return 1 + default: + return -1 + } + } + a.value = result + } + + switch { + case a.value == b.value: + return 0 + case a.value < b.value: + return -1 + default: + return 1 + } +} + +// Add adds two int64Amounts together, matching scales. It will return false and not mutate +// a if overflow or underflow would result. +func (a *int64Amount) Add(b int64Amount) bool { + switch { + case b.value == 0: + return true + case a.value == 0: + a.value = b.value + a.scale = b.scale + return true + case a.scale == b.scale: + c, ok := int64Add(a.value, b.value) + if !ok { + return false + } + a.value = c + case a.scale > b.scale: + c, ok := positiveScaleInt64(a.value, a.scale-b.scale) + if !ok { + return false + } + c, ok = int64Add(c, b.value) + if !ok { + return false + } + a.scale = b.scale + a.value = c + default: + c, ok := positiveScaleInt64(b.value, b.scale-a.scale) + if !ok { + return false + } + c, ok = int64Add(a.value, c) + if !ok { + return false + } + a.value = c + } + return true +} + +// Sub removes the value of b from the current amount, or returns false if underflow would result. +func (a *int64Amount) Sub(b int64Amount) bool { + return a.Add(int64Amount{value: -b.value, scale: b.scale}) +} + +// AsScale adjusts this amount to set a minimum scale, rounding up, and returns true iff no precision +// was lost. (1.1e5).AsScale(5) would return 1.1e5, but (1.1e5).AsScale(6) would return 1e6. +func (a int64Amount) AsScale(scale Scale) (int64Amount, bool) { + if a.scale >= scale { + return a, true + } + result, exact := negativeScaleInt64(a.value, scale-a.scale) + return int64Amount{value: result, scale: scale}, exact +} + +// AsCanonicalBytes accepts a buffer to write the base-10 string value of this field to, and returns +// either that buffer or a larger buffer and the current exponent of the value. The value is adjusted +// until the exponent is a multiple of 3 - i.e. 1.1e5 would return "110", 3. +func (a int64Amount) AsCanonicalBytes(out []byte) (result []byte, exponent int32) { + mantissa := a.value + exponent = int32(a.scale) + + amount, times := removeInt64Factors(mantissa, 10) + exponent += int32(times) + + // make sure exponent is a multiple of 3 + var ok bool + switch exponent % 3 { + case 1, -2: + amount, ok = int64MultiplyScale10(amount) + if !ok { + return infDecAmount{a.AsDec()}.AsCanonicalBytes(out) + } + exponent = exponent - 1 + case 2, -1: + amount, ok = int64MultiplyScale100(amount) + if !ok { + return infDecAmount{a.AsDec()}.AsCanonicalBytes(out) + } + exponent = exponent - 2 + } + return strconv.AppendInt(out, amount, 10), exponent +} + +// AsCanonicalBase1024Bytes accepts a buffer to write the base-1024 string value of this field to, and returns +// either that buffer or a larger buffer and the current exponent of the value. 2048 is 2 * 1024 ^ 1 and would +// return []byte("2048"), 1. +func (a int64Amount) AsCanonicalBase1024Bytes(out []byte) (result []byte, exponent int32) { + value, ok := a.AsScaledInt64(0) + if !ok { + return infDecAmount{a.AsDec()}.AsCanonicalBase1024Bytes(out) + } + amount, exponent := removeInt64Factors(value, 1024) + return strconv.AppendInt(out, amount, 10), exponent +} + +// infDecAmount implements common operations over an inf.Dec that are specific to the quantity +// representation. +type infDecAmount struct { + *inf.Dec +} + +// AsScale adjusts this amount to set a minimum scale, rounding up, and returns true iff no precision +// was lost. (1.1e5).AsScale(5) would return 1.1e5, but (1.1e5).AsScale(6) would return 1e6. +func (a infDecAmount) AsScale(scale Scale) (infDecAmount, bool) { + tmp := &inf.Dec{} + tmp.Round(a.Dec, scale.infScale(), inf.RoundUp) + return infDecAmount{tmp}, tmp.Cmp(a.Dec) == 0 +} + +// AsCanonicalBytes accepts a buffer to write the base-10 string value of this field to, and returns +// either that buffer or a larger buffer and the current exponent of the value. The value is adjusted +// until the exponent is a multiple of 3 - i.e. 1.1e5 would return "110", 3. +func (a infDecAmount) AsCanonicalBytes(out []byte) (result []byte, exponent int32) { + mantissa := a.Dec.UnscaledBig() + exponent = int32(-a.Dec.Scale()) + amount := big.NewInt(0).Set(mantissa) + // move all factors of 10 into the exponent for easy reasoning + amount, times := removeBigIntFactors(amount, bigTen) + exponent += times + + // make sure exponent is a multiple of 3 + for exponent%3 != 0 { + amount.Mul(amount, bigTen) + exponent-- + } + + return append(out, amount.String()...), exponent +} + +// AsCanonicalBase1024Bytes accepts a buffer to write the base-1024 string value of this field to, and returns +// either that buffer or a larger buffer and the current exponent of the value. 2048 is 2 * 1024 ^ 1 and would +// return []byte("2048"), 1. +func (a infDecAmount) AsCanonicalBase1024Bytes(out []byte) (result []byte, exponent int32) { + tmp := &inf.Dec{} + tmp.Round(a.Dec, 0, inf.RoundUp) + amount, exponent := removeBigIntFactors(tmp.UnscaledBig(), big1024) + return append(out, amount.String()...), exponent +} diff --git a/pkg/api/resource/amount_test.go b/pkg/api/resource/amount_test.go new file mode 100644 index 00000000000..acaad6307b6 --- /dev/null +++ b/pkg/api/resource/amount_test.go @@ -0,0 +1,111 @@ +/* +Copyright 2014 The Kubernetes Authors All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package resource + +import ( + "testing" +) + +func TestInt64AmountAsInt64(t *testing.T) { + for _, test := range []struct { + value int64 + scale Scale + result int64 + ok bool + }{ + {100, 0, 100, true}, + {100, 1, 1000, true}, + {100, -5, 0, false}, + {100, 100, 0, false}, + } { + r, ok := int64Amount{value: test.value, scale: test.scale}.AsInt64() + if r != test.result { + t.Errorf("%v: unexpected result: %d", test, r) + } + if ok != test.ok { + t.Errorf("%v: unexpected ok: %t", test, ok) + } + } +} + +func TestInt64AmountAdd(t *testing.T) { + for _, test := range []struct { + a, b, c int64Amount + ok bool + }{ + {int64Amount{value: 100, scale: 1}, int64Amount{value: 10, scale: 2}, int64Amount{value: 200, scale: 1}, true}, + {int64Amount{value: 100, scale: 1}, int64Amount{value: 1, scale: 2}, int64Amount{value: 110, scale: 1}, true}, + {int64Amount{value: 100, scale: 1}, int64Amount{value: 1, scale: 100}, int64Amount{value: 1, scale: 100}, false}, + {int64Amount{value: -5, scale: 2}, int64Amount{value: 50, scale: 1}, int64Amount{value: 0, scale: 1}, true}, + {int64Amount{value: -5, scale: 2}, int64Amount{value: 5, scale: 2}, int64Amount{value: 0, scale: 2}, true}, + + {int64Amount{value: mostPositive, scale: -1}, int64Amount{value: 1, scale: -1}, int64Amount{value: 0, scale: -1}, false}, + {int64Amount{value: mostPositive, scale: -1}, int64Amount{value: 0, scale: -1}, int64Amount{value: mostPositive, scale: -1}, true}, + {int64Amount{value: mostPositive / 10, scale: 1}, int64Amount{value: 10, scale: 0}, int64Amount{value: mostPositive, scale: -1}, false}, + } { + c := test.a + ok := c.Add(test.b) + if ok != test.ok { + t.Errorf("%v: unexpected ok: %t", test, ok) + } + if ok { + if c != test.c { + t.Errorf("%v: unexpected result: %d", test, c) + } + } else { + if c != test.a { + t.Errorf("%v: overflow addition mutated source: %d", test, c) + } + } + + // addition is commutative + c = test.b + if ok := c.Add(test.a); ok != test.ok { + t.Errorf("%v: unexpected ok: %t", test, ok) + } + if ok { + if c != test.c { + t.Errorf("%v: unexpected result: %d", test, c) + } + } else { + if c != test.b { + t.Errorf("%v: overflow addition mutated source: %d", test, c) + } + } + } +} +func TestInt64AsCanonicalString(t *testing.T) { + for _, test := range []struct { + value int64 + scale Scale + result string + exponent int32 + }{ + {100, 0, "100", 0}, + {100, 1, "1", 3}, + {100, -1, "10", 0}, + {10800, -10, "1080", -9}, + } { + r, exp := int64Amount{value: test.value, scale: test.scale}.AsCanonicalBytes(nil) + if string(r) != test.result { + t.Errorf("%v: unexpected result: %s", test, r) + } + if exp != test.exponent { + t.Errorf("%v: unexpected exponent: %d", test, exp) + } + } +} diff --git a/pkg/api/resource/deep_copy_generated.go b/pkg/api/resource/deep_copy_generated.go index 6a34e2d4342..3f72141073e 100644 --- a/pkg/api/resource/deep_copy_generated.go +++ b/pkg/api/resource/deep_copy_generated.go @@ -21,35 +21,27 @@ limitations under the License. package resource import ( - inf_v0 "gopkg.in/inf.v0" conversion "k8s.io/kubernetes/pkg/conversion" ) func DeepCopy_resource_Quantity(in Quantity, out *Quantity, c *conversion.Cloner) error { - if in.Amount != nil { - in, out := in.Amount, &out.Amount - *out = new(inf_v0.Dec) - if newVal, err := c.DeepCopy(*in); err != nil { - return err - } else { - **out = newVal.(inf_v0.Dec) - } + if newVal, err := c.DeepCopy(in.i); err != nil { + return err } else { - out.Amount = nil + out.i = newVal.(int64Amount) } - out.Format = in.Format - return nil -} - -func DeepCopy_resource_QuantityProto(in QuantityProto, out *QuantityProto, c *conversion.Cloner) error { - out.Format = in.Format - out.Scale = in.Scale - if in.Bigint != nil { - in, out := in.Bigint, &out.Bigint + if newVal, err := c.DeepCopy(in.d); err != nil { + return err + } else { + out.d = newVal.(infDecAmount) + } + if in.s != nil { + in, out := in.s, &out.s *out = make([]byte, len(in)) copy(*out, in) } else { - out.Bigint = nil + out.s = nil } + out.Format = in.Format return nil } diff --git a/pkg/api/resource/generated.pb.go b/pkg/api/resource/generated.pb.go index 7484f92761a..cf9447a321f 100644 --- a/pkg/api/resource/generated.pb.go +++ b/pkg/api/resource/generated.pb.go @@ -26,7 +26,6 @@ limitations under the License. It has these top-level messages: Quantity - QuantityProto */ package resource @@ -34,8 +33,6 @@ import proto "github.com/gogo/protobuf/proto" import fmt "fmt" import math "math" -import io "io" - // Reference imports to suppress errors if they are not otherwise used. var _ = proto.Marshal var _ = fmt.Errorf @@ -44,328 +41,6 @@ var _ = math.Inf func (m *Quantity) Reset() { *m = Quantity{} } func (*Quantity) ProtoMessage() {} -func (m *QuantityProto) Reset() { *m = QuantityProto{} } -func (m *QuantityProto) String() string { return proto.CompactTextString(m) } -func (*QuantityProto) ProtoMessage() {} - func init() { proto.RegisterType((*Quantity)(nil), "k8s.io.kubernetes.pkg.api.resource.Quantity") - proto.RegisterType((*QuantityProto)(nil), "k8s.io.kubernetes.pkg.api.resource.QuantityProto") } -func (m *QuantityProto) Marshal() (data []byte, err error) { - size := m.Size() - data = make([]byte, size) - n, err := m.MarshalTo(data) - if err != nil { - return nil, err - } - return data[:n], nil -} - -func (m *QuantityProto) MarshalTo(data []byte) (int, error) { - var i int - _ = i - var l int - _ = l - data[i] = 0xa - i++ - i = encodeVarintGenerated(data, i, uint64(len(m.Format))) - i += copy(data[i:], m.Format) - data[i] = 0x10 - i++ - i = encodeVarintGenerated(data, i, uint64(m.Scale)) - if m.Bigint != nil { - data[i] = 0x1a - i++ - i = encodeVarintGenerated(data, i, uint64(len(m.Bigint))) - i += copy(data[i:], m.Bigint) - } - return i, nil -} - -func encodeFixed64Generated(data []byte, offset int, v uint64) int { - data[offset] = uint8(v) - data[offset+1] = uint8(v >> 8) - data[offset+2] = uint8(v >> 16) - data[offset+3] = uint8(v >> 24) - data[offset+4] = uint8(v >> 32) - data[offset+5] = uint8(v >> 40) - data[offset+6] = uint8(v >> 48) - data[offset+7] = uint8(v >> 56) - return offset + 8 -} -func encodeFixed32Generated(data []byte, offset int, v uint32) int { - data[offset] = uint8(v) - data[offset+1] = uint8(v >> 8) - data[offset+2] = uint8(v >> 16) - data[offset+3] = uint8(v >> 24) - return offset + 4 -} -func encodeVarintGenerated(data []byte, offset int, v uint64) int { - for v >= 1<<7 { - data[offset] = uint8(v&0x7f | 0x80) - v >>= 7 - offset++ - } - data[offset] = uint8(v) - return offset + 1 -} -func (m *QuantityProto) Size() (n int) { - var l int - _ = l - l = len(m.Format) - n += 1 + l + sovGenerated(uint64(l)) - n += 1 + sovGenerated(uint64(m.Scale)) - if m.Bigint != nil { - l = len(m.Bigint) - n += 1 + l + sovGenerated(uint64(l)) - } - return n -} - -func sovGenerated(x uint64) (n int) { - for { - n++ - x >>= 7 - if x == 0 { - break - } - } - return n -} -func sozGenerated(x uint64) (n int) { - return sovGenerated(uint64((x << 1) ^ uint64((int64(x) >> 63)))) -} -func (m *QuantityProto) Unmarshal(data []byte) error { - l := len(data) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowGenerated - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := data[iNdEx] - iNdEx++ - wire |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: QuantityProto: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: QuantityProto: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Format", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowGenerated - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := data[iNdEx] - iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthGenerated - } - postIndex := iNdEx + intStringLen - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Format = Format(data[iNdEx:postIndex]) - iNdEx = postIndex - case 2: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field Scale", wireType) - } - m.Scale = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowGenerated - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := data[iNdEx] - iNdEx++ - m.Scale |= (int32(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - case 3: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Bigint", wireType) - } - var byteLen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowGenerated - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := data[iNdEx] - iNdEx++ - byteLen |= (int(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - if byteLen < 0 { - return ErrInvalidLengthGenerated - } - postIndex := iNdEx + byteLen - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Bigint = append(m.Bigint[:0], data[iNdEx:postIndex]...) - if m.Bigint == nil { - m.Bigint = []byte{} - } - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipGenerated(data[iNdEx:]) - if err != nil { - return err - } - if skippy < 0 { - return ErrInvalidLengthGenerated - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func skipGenerated(data []byte) (n int, err error) { - l := len(data) - iNdEx := 0 - for iNdEx < l { - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return 0, ErrIntOverflowGenerated - } - if iNdEx >= l { - return 0, io.ErrUnexpectedEOF - } - b := data[iNdEx] - iNdEx++ - wire |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - wireType := int(wire & 0x7) - switch wireType { - case 0: - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return 0, ErrIntOverflowGenerated - } - if iNdEx >= l { - return 0, io.ErrUnexpectedEOF - } - iNdEx++ - if data[iNdEx-1] < 0x80 { - break - } - } - return iNdEx, nil - case 1: - iNdEx += 8 - return iNdEx, nil - case 2: - var length int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return 0, ErrIntOverflowGenerated - } - if iNdEx >= l { - return 0, io.ErrUnexpectedEOF - } - b := data[iNdEx] - iNdEx++ - length |= (int(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - iNdEx += length - if length < 0 { - return 0, ErrInvalidLengthGenerated - } - return iNdEx, nil - case 3: - for { - var innerWire uint64 - var start int = iNdEx - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return 0, ErrIntOverflowGenerated - } - if iNdEx >= l { - return 0, io.ErrUnexpectedEOF - } - b := data[iNdEx] - iNdEx++ - innerWire |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - innerWireType := int(innerWire & 0x7) - if innerWireType == 4 { - break - } - next, err := skipGenerated(data[start:]) - if err != nil { - return 0, err - } - iNdEx = start + next - } - return iNdEx, nil - case 4: - return iNdEx, nil - case 5: - iNdEx += 4 - return iNdEx, nil - default: - return 0, fmt.Errorf("proto: illegal wireType %d", wireType) - } - } - panic("unreachable") -} - -var ( - ErrInvalidLengthGenerated = fmt.Errorf("proto: negative length found during unmarshaling") - ErrIntOverflowGenerated = fmt.Errorf("proto: integer overflow") -) diff --git a/pkg/api/resource/generated.proto b/pkg/api/resource/generated.proto index 34ede4581bc..8a1376c32e1 100644 --- a/pkg/api/resource/generated.proto +++ b/pkg/api/resource/generated.proto @@ -84,26 +84,10 @@ option go_package = "resource"; // cause implementors to also use a fixed point implementation. // // +protobuf=true -// +protobuf.embed=QuantityProto +// +protobuf.embed=string // +protobuf.options.marshal=false // +protobuf.options.(gogoproto.goproto_stringer)=false message Quantity { - optional QuantityProto QuantityProto = 1; -} - -// QuantityProto is a struct that is equivalent to Quantity, but intended for -// protobuf marshalling/unmarshalling. It is generated into a serialization -// that matches Quantity. Do not use in Go structs. -// -// +protobuf=true -message QuantityProto { - // The format of the quantity - optional string format = 1; - - // The scale dimension of the value - optional int32 scale = 2; - - // Bigint is serialized as a raw bytes array - optional bytes bigint = 3; + optional string string = 1; } diff --git a/pkg/api/resource/math.go b/pkg/api/resource/math.go new file mode 100644 index 00000000000..163aafa5db6 --- /dev/null +++ b/pkg/api/resource/math.go @@ -0,0 +1,327 @@ +/* +Copyright 2014 The Kubernetes Authors All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package resource + +import ( + "math/big" + + inf "gopkg.in/inf.v0" +) + +const ( + // maxInt64Factors is the highest value that will be checked when removing factors of 10 from an int64. + // It is also the maximum decimal digits that can be represented with an int64. + maxInt64Factors = 18 +) + +var ( + // Commonly needed big.Int values-- treat as read only! + bigTen = big.NewInt(10) + bigZero = big.NewInt(0) + bigOne = big.NewInt(1) + bigThousand = big.NewInt(1000) + big1024 = big.NewInt(1024) + + // Commonly needed inf.Dec values-- treat as read only! + decZero = inf.NewDec(0, 0) + decOne = inf.NewDec(1, 0) + decMinusOne = inf.NewDec(-1, 0) + decThousand = inf.NewDec(1000, 0) + dec1024 = inf.NewDec(1024, 0) + decMinus1024 = inf.NewDec(-1024, 0) + + // Largest (in magnitude) number allowed. + maxAllowed = infDecAmount{inf.NewDec((1<<63)-1, 0)} // == max int64 + + // The maximum value we can represent milli-units for. + // Compare with the return value of Quantity.Value() to + // see if it's safe to use Quantity.MilliValue(). + MaxMilliValue = int64(((1 << 63) - 1) / 1000) +) + +const mostNegative = -(mostPositive + 1) +const mostPositive = 1<<63 - 1 + +// int64Add returns a+b, or false if that would overflow int64. +func int64Add(a, b int64) (int64, bool) { + c := a + b + switch { + case a > 0 && b > 0: + if c < 0 { + return 0, false + } + case a < 0 && b < 0: + if c > 0 { + return 0, false + } + if a == mostNegative && b == mostNegative { + return 0, false + } + } + return c, true +} + +// int64Multiply returns a*b, or false if that would overflow or underflow int64. +func int64Multiply(a, b int64) (int64, bool) { + if a == 0 || b == 0 || a == 1 || b == 1 { + return a * b, true + } + if a == mostNegative || b == mostNegative { + return 0, false + } + c := a * b + return c, c/b == a +} + +// int64MultiplyScale returns a*b, assuming b is greater than one, or false if that would overflow or underflow int64. +// Use when b is known to be greater than one. +func int64MultiplyScale(a int64, b int64) (int64, bool) { + if a == 0 || a == 1 { + return a * b, true + } + if a == mostNegative && b != 1 { + return 0, false + } + c := a * b + return c, c/b == a +} + +// int64MultiplyScale10 multiplies a by 10, or returns false if that would overflow. This method is faster than +// int64Multiply(a, 10) because the compiler can optimize constant factor multiplication. +func int64MultiplyScale10(a int64) (int64, bool) { + if a == 0 || a == 1 { + return a * 10, true + } + if a == mostNegative { + return 0, false + } + c := a * 10 + return c, c/10 == a +} + +// int64MultiplyScale100 multiplies a by 100, or returns false if that would overflow. This method is faster than +// int64Multiply(a, 100) because the compiler can optimize constant factor multiplication. +func int64MultiplyScale100(a int64) (int64, bool) { + if a == 0 || a == 1 { + return a * 100, true + } + if a == mostNegative { + return 0, false + } + c := a * 100 + return c, c/100 == a +} + +// int64MultiplyScale1000 multiplies a by 1000, or returns false if that would overflow. This method is faster than +// int64Multiply(a, 1000) because the compiler can optimize constant factor multiplication. +func int64MultiplyScale1000(a int64) (int64, bool) { + if a == 0 || a == 1 { + return a * 1000, true + } + if a == mostNegative { + return 0, false + } + c := a * 1000 + return c, c/1000 == a +} + +// positiveScaleInt64 multiplies base by 10^scale, returning false if the +// value overflows. Passing a negative scale is undefined. +func positiveScaleInt64(base int64, scale Scale) (int64, bool) { + switch scale { + case 0: + return base, true + case 1: + return int64MultiplyScale10(base) + case 2: + return int64MultiplyScale100(base) + case 3: + return int64MultiplyScale1000(base) + case 6: + return int64MultiplyScale(base, 1000000) + case 9: + return int64MultiplyScale(base, 1000000000) + default: + value := base + var ok bool + for i := Scale(0); i < scale; i++ { + if value, ok = int64MultiplyScale(value, 10); !ok { + return 0, false + } + } + return value, true + } +} + +// negativeScaleInt64 reduces base by the provided scale, rounding up, until the +// value is zero or the scale is reached. Passing a negative scale is undefined. +// The value returned, if not exact, is rounded away from zero. +func negativeScaleInt64(base int64, scale Scale) (result int64, exact bool) { + if scale == 0 { + return base, true + } + + value := base + var fraction bool + for i := Scale(0); i < scale; i++ { + if !fraction && value%10 != 0 { + fraction = true + } + value = value / 10 + if value == 0 { + if fraction { + if base > 0 { + return 1, false + } + return -1, false + } + return 0, true + } + } + if fraction { + if base > 0 { + value += 1 + } else { + value += -1 + } + } + return value, !fraction +} + +func pow10Int64(b int64) int64 { + switch b { + case 0: + return 1 + case 1: + return 10 + case 2: + return 100 + case 3: + return 1000 + case 4: + return 10000 + case 5: + return 100000 + case 6: + return 1000000 + case 7: + return 10000000 + case 8: + return 100000000 + case 9: + return 1000000000 + case 10: + return 10000000000 + case 11: + return 100000000000 + case 12: + return 1000000000000 + case 13: + return 10000000000000 + case 14: + return 100000000000000 + case 15: + return 1000000000000000 + case 16: + return 10000000000000000 + case 17: + return 100000000000000000 + case 18: + return 1000000000000000000 + default: + return 0 + } +} + +// powInt64 raises a to the bth power. Is not overflow aware. +func powInt64(a, b int64) int64 { + p := int64(1) + for b > 0 { + if b&1 != 0 { + p *= a + } + b >>= 1 + a *= a + } + return p +} + +// negativeScaleInt64 returns the result of dividing base by scale * 10 and the remainder, or +// false if no such division is possible. Dividing by negative scales is undefined. +func divideByScaleInt64(base int64, scale Scale) (result, remainder int64, exact bool) { + if scale == 0 { + return base, 0, true + } + // the max scale representable in base 10 in an int64 is 18 decimal places + if scale >= 18 { + return 0, base, false + } + divisor := pow10Int64(int64(scale)) + return base / divisor, base % divisor, true +} + +// removeInt64Factors divides in a loop; the return values have the property that +// value == result * base ^ scale +func removeInt64Factors(value int64, base int64) (result int64, times int32) { + times = 0 + result = value + negative := result < 0 + if negative { + result = -result + } + switch base { + // allow the compiler to optimize the common cases + case 10: + for result >= 10 && result%10 == 0 { + times++ + result = result / 10 + } + // allow the compiler to optimize the common cases + case 1024: + for result >= 1024 && result%1024 == 0 { + times++ + result = result / 1024 + } + default: + for result >= base && result%base == 0 { + times++ + result = result / base + } + } + if negative { + result = -result + } + return result, times +} + +// removeBigIntFactors divides in a loop; the return values have the property that +// d == result * factor ^ times +// d may be modified in place. +// If d == 0, then the return values will be (0, 0) +func removeBigIntFactors(d, factor *big.Int) (result *big.Int, times int32) { + q := big.NewInt(0) + m := big.NewInt(0) + for d.Cmp(bigZero) != 0 { + q.DivMod(d, factor, m) + if m.Cmp(bigZero) != 0 { + break + } + times++ + d, q = q, d + } + return d, times +} diff --git a/pkg/api/resource/math_test.go b/pkg/api/resource/math_test.go new file mode 100644 index 00000000000..0fdda12e0a1 --- /dev/null +++ b/pkg/api/resource/math_test.go @@ -0,0 +1,211 @@ +/* +Copyright 2014 The Kubernetes Authors All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package resource + +import ( + "testing" +) + +func TestDetectOverflowAdd(t *testing.T) { + for _, test := range []struct { + a, b int64 + c int64 + ok bool + }{ + {0, 0, 0, true}, + {-1, 1, 0, true}, + {0, 1, 1, true}, + {2, 2, 4, true}, + {2, -2, 0, true}, + {-2, -2, -4, true}, + + {mostNegative, -1, 0, false}, + {mostNegative, 1, mostNegative + 1, true}, + {mostPositive, -1, mostPositive - 1, true}, + {mostPositive, 1, 0, false}, + + {mostNegative, mostPositive, -1, true}, + {mostPositive, mostNegative, -1, true}, + {mostPositive, mostPositive, 0, false}, + {mostNegative, mostNegative, 0, false}, + + {-mostPositive, mostNegative, 0, false}, + {mostNegative, -mostPositive, 0, false}, + {-mostPositive, -mostPositive, 0, false}, + } { + c, ok := int64Add(test.a, test.b) + if c != test.c { + t.Errorf("%v: unexpected result: %d", test, c) + } + if ok != test.ok { + t.Errorf("%v: unexpected overflow: %t", test, ok) + } + // addition is commutative + d, ok2 := int64Add(test.b, test.a) + if c != d || ok != ok2 { + t.Errorf("%v: not commutative: %d %t", test, d, ok2) + } + } +} + +func TestDetectOverflowMultiply(t *testing.T) { + for _, test := range []struct { + a, b int64 + c int64 + ok bool + }{ + {0, 0, 0, true}, + {-1, 1, -1, true}, + {-1, -1, 1, true}, + {1, 1, 1, true}, + {0, 1, 0, true}, + {1, 0, 0, true}, + {2, 2, 4, true}, + {2, -2, -4, true}, + {-2, -2, 4, true}, + + {mostNegative, -1, 0, false}, + {mostNegative, 1, mostNegative, true}, + {mostPositive, -1, -mostPositive, true}, + {mostPositive, 1, mostPositive, true}, + + {mostNegative, mostPositive, 0, false}, + {mostPositive, mostNegative, 0, false}, + {mostPositive, mostPositive, 1, false}, + {mostNegative, mostNegative, 0, false}, + + {-mostPositive, mostNegative, 0, false}, + {mostNegative, -mostPositive, 0, false}, + {-mostPositive, -mostPositive, 1, false}, + } { + c, ok := int64Multiply(test.a, test.b) + if c != test.c { + t.Errorf("%v: unexpected result: %d", test, c) + } + if ok != test.ok { + t.Errorf("%v: unexpected overflow: %t", test, ok) + } + // multiplication is commutative + d, ok2 := int64Multiply(test.b, test.a) + if c != d || ok != ok2 { + t.Errorf("%v: not commutative: %d %t", test, d, ok2) + } + } +} + +func TestDetectOverflowScale(t *testing.T) { + for _, a := range []int64{0, -1, 1, 10, -10, mostPositive, mostNegative, -mostPositive} { + for _, b := range []int64{1, 2, 10, 100, 1000, mostPositive} { + expect, expectOk := int64Multiply(a, b) + + c, ok := int64MultiplyScale(a, b) + if c != expect { + t.Errorf("%d*%d: unexpected result: %d", a, b, c) + } + if ok != expectOk { + t.Errorf("%d*%d: unexpected overflow: %t", a, b, ok) + } + } + for _, test := range []struct { + base int64 + fn func(a int64) (int64, bool) + }{ + {10, int64MultiplyScale10}, + {100, int64MultiplyScale100}, + {1000, int64MultiplyScale1000}, + } { + expect, expectOk := int64Multiply(a, test.base) + c, ok := test.fn(a) + if c != expect { + t.Errorf("%d*%d: unexpected result: %d", a, test.base, c) + } + if ok != expectOk { + t.Errorf("%d*%d: unexpected overflow: %t", a, test.base, ok) + } + } + } +} + +func TestRemoveInt64Factors(t *testing.T) { + for _, test := range []struct { + value int64 + max int64 + result int64 + scale int32 + }{ + {100, 10, 1, 2}, + {100, 10, 1, 2}, + {100, 100, 1, 1}, + {1, 10, 1, 0}, + } { + r, s := removeInt64Factors(test.value, test.max) + if r != test.result { + t.Errorf("%v: unexpected result: %d", test, r) + } + if s != test.scale { + t.Errorf("%v: unexpected scale: %d", test, s) + } + } +} + +func TestNegativeScaleInt64(t *testing.T) { + for _, test := range []struct { + base int64 + scale Scale + result int64 + exact bool + }{ + {1234567, 0, 1234567, true}, + {1234567, 1, 123457, false}, + {1234567, 2, 12346, false}, + {1234567, 3, 1235, false}, + {1234567, 4, 124, false}, + + {-1234567, 0, -1234567, true}, + {-1234567, 1, -123457, false}, + {-1234567, 2, -12346, false}, + {-1234567, 3, -1235, false}, + {-1234567, 4, -124, false}, + + {1000, 0, 1000, true}, + {1000, 1, 100, true}, + {1000, 2, 10, true}, + {1000, 3, 1, true}, + {1000, 4, 1, false}, + + {-1000, 0, -1000, true}, + {-1000, 1, -100, true}, + {-1000, 2, -10, true}, + {-1000, 3, -1, true}, + {-1000, 4, -1, false}, + + {0, 0, 0, true}, + {0, 1, 0, true}, + {0, 2, 0, true}, + + // negative scale is undefined behavior + {1000, -1, 1000, true}, + } { + result, exact := negativeScaleInt64(test.base, test.scale) + if result != test.result { + t.Errorf("%v: unexpected result: %d", test, result) + } + if exact != test.exact { + t.Errorf("%v: unexpected exact: %t", test, exact) + } + } +} diff --git a/pkg/api/resource/quantity.go b/pkg/api/resource/quantity.go index e61c8800ec4..47c0ac08e82 100644 --- a/pkg/api/resource/quantity.go +++ b/pkg/api/resource/quantity.go @@ -17,10 +17,12 @@ limitations under the License. package resource import ( + "bytes" "errors" "fmt" "math/big" "regexp" + "strconv" "strings" flag "github.com/spf13/pflag" @@ -86,19 +88,34 @@ import ( // cause implementors to also use a fixed point implementation. // // +protobuf=true -// +protobuf.embed=QuantityProto +// +protobuf.embed=string // +protobuf.options.marshal=false // +protobuf.options.(gogoproto.goproto_stringer)=false type Quantity struct { - // Amount is public, so you can manipulate it if the accessor - // functions are not sufficient. - Amount *inf.Dec + // i is the quantity in int64 scaled form, if d.Dec == nil + i int64Amount + // d is the quantity in inf.Dec form if d.Dec != nil + d infDecAmount + // s is the generated value of this quantity to avoid recalculation + s []byte // Change Format at will. See the comment for Canonicalize for // more details. Format } +// CanonicalValue allows a quantity amount to be converted to a string. +type CanonicalValue interface { + // AsCanonicalBytes returns a byte array representing the string representation + // of the value mantissa and an int32 representing its exponent in base-10. Callers may + // pass a byte slice to the method to avoid allocations. + AsCanonicalBytes(out []byte) ([]byte, int32) + // AsCanonicalBase1024Bytes returns a byte array representing the string representation + // of the value mantissa and an int32 representing its exponent in base-1024. Callers + // may pass a byte slice to the method to avoid allocations. + AsCanonicalBase1024Bytes(out []byte) ([]byte, int32) +} + // Format lists the three possible formattings of a quantity. type Format string @@ -115,26 +132,9 @@ func MustParse(str string) Quantity { if err != nil { panic(fmt.Errorf("cannot parse '%v': %v", str, err)) } - return *q + return q } -// Scale is used for getting and setting the base-10 scaled value. -// Base-2 scales are omitted for mathematical simplicity. -// See Quantity.ScaledValue for more details. -type Scale int - -const ( - Nano Scale = -9 - Micro Scale = -6 - Milli Scale = -3 - Kilo Scale = 3 - Mega Scale = 6 - Giga Scale = 9 - Tera Scale = 12 - Peta Scale = 15 - Exa Scale = 18 -) - const ( // splitREString is used to separate a number from its suffix; as such, // this is overly permissive, but that's OK-- it will be checked later. @@ -149,47 +149,189 @@ var ( ErrFormatWrong = errors.New("quantities must match the regular expression '" + splitREString + "'") ErrNumeric = errors.New("unable to parse numeric part of quantity") ErrSuffix = errors.New("unable to parse quantity's suffix") - - // Commonly needed big.Int values-- treat as read only! - bigTen = big.NewInt(10) - bigZero = big.NewInt(0) - bigOne = big.NewInt(1) - bigThousand = big.NewInt(1000) - big1024 = big.NewInt(1024) - - // Commonly needed inf.Dec values-- treat as read only! - decZero = inf.NewDec(0, 0) - decOne = inf.NewDec(1, 0) - decMinusOne = inf.NewDec(-1, 0) - decThousand = inf.NewDec(1000, 0) - dec1024 = inf.NewDec(1024, 0) - decMinus1024 = inf.NewDec(-1024, 0) - - // Largest (in magnitude) number allowed. - maxAllowed = inf.NewDec((1<<63)-1, 0) // == max int64 - - // The maximum value we can represent milli-units for. - // Compare with the return value of Quantity.Value() to - // see if it's safe to use Quantity.MilliValue(). - MaxMilliValue = int64(((1 << 63) - 1) / 1000) ) +// parseQuantityString is a fast scanner for quantity values. +func parseQuantityString(str string) (positive bool, value, num, denom, suffix string, err error) { + positive = true + pos := 0 + end := len(str) + + // handle leading sign + if pos < end { + switch str[0] { + case '-': + positive = false + pos++ + case '+': + pos++ + } + } + + // strip leading zeros +Zeroes: + for i := pos; ; i++ { + if i >= end { + num = "0" + value = num + return + } + switch str[i] { + case '0': + pos++ + default: + break Zeroes + } + } + + // extract the numerator +Num: + for i := pos; ; i++ { + if i >= end { + num = str[pos:end] + value = str[0:end] + return + } + switch str[i] { + case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': + default: + num = str[pos:i] + pos = i + break Num + } + } + + // if we stripped all numerator positions, always return 0 + if len(num) == 0 { + num = "0" + } + + // handle a denominator + if pos < end && str[pos] == '.' { + pos++ + Denom: + for i := pos; ; i++ { + if i >= end { + denom = str[pos:end] + value = str[0:end] + return + } + switch str[i] { + case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': + default: + denom = str[pos:i] + pos = i + break Denom + } + } + // TODO: we currently allow 1.G, but we may not want to in the future. + // if len(denom) == 0 { + // err = ErrFormatWrong + // return + // } + } + value = str[0:pos] + + // grab the elements of the suffix + suffixStart := pos + for i := pos; ; i++ { + if i >= end { + suffix = str[suffixStart:end] + return + } + if !strings.ContainsAny(str[i:i+1], "eEinumkKMGTP") { + pos = i + break + } + } + if pos < end { + switch str[pos] { + case '-', '+': + pos++ + } + } +Suffix: + for i := pos; ; i++ { + if i >= end { + suffix = str[suffixStart:end] + return + } + switch str[i] { + case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': + default: + pos = i + break Suffix + } + } + // we encountered a non decimal in the Suffix loop, but the last character + // was not a valid exponent + err = ErrFormatWrong + return +} + // ParseQuantity turns str into a Quantity, or returns an error. -func ParseQuantity(str string) (*Quantity, error) { - parts := splitRE.FindStringSubmatch(strings.TrimSpace(str)) - // regexp returns are entire match, followed by an entry for each () section. - if len(parts) != 3 { - return nil, ErrFormatWrong +func ParseQuantity(str string) (Quantity, error) { + if len(str) == 0 { + return Quantity{}, ErrFormatWrong + } + if str == "0" { + return Quantity{Format: DecimalSI}, nil + } + + positive, value, num, denom, suf, err := parseQuantityString(str) + if err != nil { + return Quantity{}, err + } + + base, exponent, format, ok := quantitySuffixer.interpret(suffix(suf)) + if !ok { + return Quantity{}, ErrSuffix + } + + precision := int32(0) + scale := int32(0) + mantissa := int64(1) + switch format { + case DecimalExponent, DecimalSI: + scale = exponent + precision = maxInt64Factors - int32(len(num)+len(denom)) + case BinarySI: + scale = 0 + switch { + case exponent >= 0 && len(denom) == 0: + // only handle positive binary numbers with the fast path + mantissa = int64(int64(mantissa) << uint64(exponent)) + // 1Mi (2^20) has ~6 digits of decimal precision, so exponent*3/10 -1 is roughly the precision + precision = 15 - int32(len(num)) - int32(float32(exponent)*3/10) - 1 + default: + precision = -1 + } + } + + if precision >= 0 { + // if we have a denominator, shift the entire value to the left by the number of places in the + // denominator + scale -= int32(len(denom)) + if scale >= int32(Nano) { + shifted := num + denom + + var value int64 + value, err := strconv.ParseInt(shifted, 10, 64) + if err != nil { + return Quantity{}, ErrNumeric + } + if result, ok := int64Multiply(value, int64(mantissa)); ok { + if !positive { + result = -result + } + return Quantity{i: int64Amount{value: result, scale: Scale(scale)}, Format: format}, nil + } + } } amount := new(inf.Dec) - if _, ok := amount.SetString(parts[1]); !ok { - return nil, ErrNumeric - } - - base, exponent, format, ok := quantitySuffixer.interpret(suffix(parts[2])) - if !ok { - return nil, ErrSuffix + if _, ok := amount.SetString(value); !ok { + return Quantity{}, ErrNumeric } // So that no one but us has to think about suffixes, remove it. @@ -217,9 +359,11 @@ func ParseQuantity(str string) (*Quantity, error) { } // The max is just a simple cap. - if amount.Cmp(maxAllowed) > 0 { - amount.Set(maxAllowed) + // TODO: this prevents accumulating quantities greater than int64, for instance quota across a cluster + if format == BinarySI && amount.Cmp(maxAllowed.Dec) > 0 { + amount.Set(maxAllowed.Dec) } + if format == BinarySI && amount.Cmp(decOne) < 0 && amount.Cmp(decZero) > 0 { // This avoids rounding and hopefully confusion, too. format = DecimalSI @@ -228,25 +372,7 @@ func ParseQuantity(str string) (*Quantity, error) { amount.Neg(amount) } - return &Quantity{amount, format}, nil -} - -// removeFactors divides in a loop; the return values have the property that -// d == result * factor ^ times -// d may be modified in place. -// If d == 0, then the return values will be (0, 0) -func removeFactors(d, factor *big.Int) (result *big.Int, times int) { - q := big.NewInt(0) - m := big.NewInt(0) - for d.Cmp(bigZero) != 0 { - q.DivMod(d, factor, m) - if m.Cmp(bigZero) != 0 { - break - } - times++ - d, q = q, d - } - return d, times + return Quantity{d: infDecAmount{amount}, Format: format}, nil } // Canonicalize returns the canonical form of q and its suffix (see comment on Quantity). @@ -256,27 +382,22 @@ func removeFactors(d, factor *big.Int) (result *big.Int, times int) { // -1 and +1, it will be emitted as if q.Format were DecimalSI. // * Otherwise, if q.Format is set to BinarySI, frational parts of q.Amount will be // rounded up. (1.1i becomes 2i.) -func (q *Quantity) Canonicalize() (string, suffix) { - if q.Amount == nil { - return "0", "" - } - - // zero is zero always - if q.Amount.Cmp(&inf.Dec{}) == 0 { - return "0", "" +func (q *Quantity) CanonicalizeBytes(out []byte) (result, suffix []byte) { + if q.IsZero() { + return zeroBytes, nil } + var rounded CanonicalValue format := q.Format switch format { case DecimalExponent, DecimalSI: case BinarySI: - if q.Amount.Cmp(decMinus1024) > 0 && q.Amount.Cmp(dec1024) < 0 { + if q.CmpInt64(-1024) > 0 && q.CmpInt64(1024) < 0 { // This avoids rounding and hopefully confusion, too. format = DecimalSI } else { - tmp := &inf.Dec{} - tmp.Round(q.Amount, 0, inf.RoundUp) - if tmp.Cmp(q.Amount) != 0 { + var exact bool + if rounded, exact = q.AsScale(0); !exact { // Don't lose precision-- show as DecimalSI format = DecimalSI } @@ -289,125 +410,223 @@ func (q *Quantity) Canonicalize() (string, suffix) { // one of the other formats. switch format { case DecimalExponent, DecimalSI: - mantissa := q.Amount.UnscaledBig() - exponent := int(-q.Amount.Scale()) - amount := big.NewInt(0).Set(mantissa) - // move all factors of 10 into the exponent for easy reasoning - amount, times := removeFactors(amount, bigTen) - exponent += times - - // make sure exponent is a multiple of 3 - for exponent%3 != 0 { - amount.Mul(amount, bigTen) - exponent-- - } - - suffix, _ := quantitySuffixer.construct(10, exponent, format) - number := amount.String() + number, exponent := q.AsCanonicalBytes(out) + suffix, _ := quantitySuffixer.constructBytes(10, exponent, format) return number, suffix - case BinarySI: - tmp := &inf.Dec{} - tmp.Round(q.Amount, 0, inf.RoundUp) - - amount, exponent := removeFactors(tmp.UnscaledBig(), big1024) - suffix, _ := quantitySuffixer.construct(2, exponent*10, format) - number := amount.String() + default: + // format must be BinarySI + number, exponent := rounded.AsCanonicalBase1024Bytes(out) + suffix, _ := quantitySuffixer.constructBytes(2, exponent*10, format) return number, suffix } - return "0", "" } +// AsInt64 returns a representation of the current value as an int64 if a fast conversion +// is possible. If false is returned, callers must use the inf.Dec form of this quantity. +func (q *Quantity) AsInt64() (int64, bool) { + if q.d.Dec != nil { + return 0, false + } + return q.i.AsInt64() +} + +// ToDec promotes the quantity in place to use an inf.Dec representation and returns itself. +func (q *Quantity) ToDec() *Quantity { + if q.d.Dec == nil { + q.d.Dec = q.i.AsDec() + q.i = int64Amount{} + } + return q +} + +// AsDec returns the quantity as represented by a scaled inf.Dec. +func (q *Quantity) AsDec() *inf.Dec { + if q.d.Dec != nil { + return q.d.Dec + } + q.d.Dec = q.i.AsDec() + q.i = int64Amount{} + return q.d.Dec +} + +// AsCanonicalBytes returns the canonical byte representation of this quantity as a mantissa +// and base 10 exponent. The out byte slice may be passed to the method to avoid an extra +// allocation. +func (q *Quantity) AsCanonicalBytes(out []byte) (result []byte, exponent int32) { + if q.d.Dec != nil { + return q.d.AsCanonicalBytes(out) + } + return q.i.AsCanonicalBytes(out) +} + +// IsZero returns true if the quantity is equal to zero. +func (q *Quantity) IsZero() bool { + if q.d.Dec != nil { + return q.d.Dec.Sign() == 0 + } + return q.i.value == 0 +} + +// Sign returns 0 if the quantity is zero, -1 if the quantity is less than zero, or 1 if the +// quantity is greater than zero. +func (q *Quantity) Sign() int { + if q.d.Dec != nil { + return q.d.Dec.Sign() + } + return q.i.Sign() +} + +// AsScaled returns the current value, rounded up to the provided scale, and returns +// false if the scale resulted in a loss of precision. +func (q *Quantity) AsScale(scale Scale) (CanonicalValue, bool) { + if q.d.Dec != nil { + return q.d.AsScale(scale) + } + return q.i.AsScale(scale) +} + +// RoundUp updates the quantity to the provided scale, ensuring that the value is at +// least 1. False is returned if the rounding operation resulted in a loss of precision. +// Negative numbers are rounded away from zero (-9 scale 1 rounds to -10). +func (q *Quantity) RoundUp(scale Scale) bool { + if q.d.Dec != nil { + d, exact := q.d.AsScale(scale) + q.d = d + return exact + } + i, exact := q.i.AsScale(scale) + q.i = i + return exact +} + +// Add adds the provide y quantity to the current value. If the current value is zero, +// the format of the quantity will be updated to the format of y. +func (q *Quantity) Add(y Quantity) { + q.s = nil + if q.d.Dec == nil && y.d.Dec == nil { + if q.i.value == 0 { + q.Format = y.Format + } + if q.i.Add(y.i) { + return + } + } else if q.IsZero() { + q.Format = y.Format + } + q.ToDec().d.Dec.Add(q.d.Dec, y.AsDec()) +} + +// Sub subtracts the provided quantity from the current value in place. If the current +// value is zero, the format of the quantity will be updated to the format of y. +func (q *Quantity) Sub(y Quantity) { + q.s = nil + if q.IsZero() { + q.Format = y.Format + } + if q.d.Dec == nil && y.d.Dec == nil && q.i.Sub(y.i) { + return + } + q.ToDec().d.Dec.Sub(q.d.Dec, y.AsDec()) +} + +// Cmp returns 0 if the quantity is equal to y, -1 if the quantity is less than y, or 1 if the +// quantity is greater than y. +func (q *Quantity) Cmp(y Quantity) int { + if q.d.Dec == nil && y.d.Dec == nil { + return q.i.Cmp(y.i) + } + return q.AsDec().Cmp(y.AsDec()) +} + +// CmpInt64 returns 0 if the quantity is equal to y, -1 if the quantity is less than y, or 1 if the +// quantity is greater than y. +func (q *Quantity) CmpInt64(y int64) int { + if q.d.Dec != nil { + return q.d.Dec.Cmp(inf.NewDec(y, inf.Scale(0))) + } + return q.i.Cmp(int64Amount{value: y}) +} + +// Neg sets quantity to be the negative value of itself. +func (q *Quantity) Neg() { + q.s = nil + if q.d.Dec == nil { + q.i.value = -q.i.value + return + } + q.d.Dec.Neg(q.d.Dec) +} + +// toBytes ensures q.s is set to a byte slice representing the canonical string form of this +// quantity and then returns the value. CanonicalizeBytes is an expensive operation, and caching +// this result significantly reduces the cost of normal parse / marshal operations on Quantity. +func (q *Quantity) toBytes() []byte { + if q.s == nil { + result := make([]byte, 0, int64QuantityExpectedBytes) + number, suffix := q.CanonicalizeBytes(result) + number = append(number, suffix...) + q.s = number + } + return q.s +} + +// int64QuantityExpectedBytes is the expected width in bytes of the canonical string representation +// of most Quantity values. +const int64QuantityExpectedBytes = 18 + // String formats the Quantity as a string. func (q *Quantity) String() string { - number, suffix := q.Canonicalize() - return number + string(suffix) -} - -// Cmp compares q and y and returns: -// -// -1 if q < y -// 0 if q == y -// +1 if q > y -// -func (q *Quantity) Cmp(y Quantity) int { - if q.Amount == nil { - if y.Amount == nil { - return 0 - } - return -y.Amount.Sign() - } - if y.Amount == nil { - return q.Amount.Sign() - } - return q.Amount.Cmp(y.Amount) -} - -func (q *Quantity) Add(y Quantity) error { - switch { - case y.Amount == nil: - // Adding 0: do nothing. - case q.Amount == nil: - q.Amount = &inf.Dec{} - return q.Add(y) - default: - // we want to preserve the format of the non-zero value - zero := &inf.Dec{} - if q.Amount.Cmp(zero) == 0 && y.Amount.Cmp(zero) != 0 { - q.Format = y.Format - } - q.Amount.Add(q.Amount, y.Amount) - } - return nil -} - -func (q *Quantity) Sub(y Quantity) error { - switch { - case y.Amount == nil: - // Subtracting 0: do nothing. - case q.Amount == nil: - q.Amount = &inf.Dec{} - return q.Sub(y) - default: - // we want to preserve the format of the non-zero value - zero := &inf.Dec{} - if q.Amount.Cmp(zero) == 0 && y.Amount.Cmp(zero) != 0 { - q.Format = y.Format - } - q.Amount.Sub(q.Amount, y.Amount) - } - return nil -} - -// Neg sets q to the negative value of y. -// It updates the format of q to match y. -func (q *Quantity) Neg(y Quantity) error { - switch { - case y.Amount == nil: - *q = y - case q.Amount == nil: - q.Amount = &inf.Dec{} - fallthrough - default: - q.Amount.Neg(y.Amount) - q.Format = y.Format - } - return nil + return string(q.toBytes()) } // MarshalJSON implements the json.Marshaller interface. func (q Quantity) MarshalJSON() ([]byte, error) { - return []byte(`"` + q.String() + `"`), nil + if q.s != nil { + out := make([]byte, len(q.s)+2) + out[0], out[len(out)-1] = '"', '"' + copy(out[1:], q.s) + return out, nil + } + result := make([]byte, int64QuantityExpectedBytes, int64QuantityExpectedBytes) + result[0] = '"' + number, suffix := q.CanonicalizeBytes(result[1:1]) + // if the same slice was returned to us that we passed in, avoid another allocation by copying number into + // the source slice and returning that + if len(number) > 0 && &number[0] == &result[1] && (len(number)+len(suffix)+2) <= int64QuantityExpectedBytes { + number = append(number, suffix...) + number = append(number, '"') + return result[:1+len(number)], nil + } + // if CanonicalizeBytes needed more space than our slice provided, we may need to allocate again so use + // append + result = result[:1] + result = append(result, number...) + result = append(result, suffix...) + result = append(result, '"') + return result, nil } // UnmarshalJSON implements the json.Unmarshaller interface. func (q *Quantity) UnmarshalJSON(value []byte) error { - str := string(value) - parsed, err := ParseQuantity(strings.Trim(str, `"`)) + l := len(value) + if l == 4 && bytes.Equal(value, []byte("null")) { + q.d.Dec = nil + q.i = int64Amount{} + return nil + } + if l < 2 { + return ErrFormatWrong + } + if value[0] == '"' && value[l-1] == '"' { + value = value[1 : l-1] + } + parsed, err := ParseQuantity(string(value)) if err != nil { return err } + parsed.s = value // This copy is safe because parsed will not be referred to again. - *q = *parsed + *q = parsed return nil } @@ -415,7 +634,7 @@ func (q *Quantity) UnmarshalJSON(value []byte) error { // value in the given format. func NewQuantity(value int64, format Format) *Quantity { return &Quantity{ - Amount: inf.NewDec(value, 0), + i: int64Amount{value: value}, Format: format, } } @@ -426,7 +645,7 @@ func NewQuantity(value int64, format Format) *Quantity { // values x where (-1 < x < 1) && (x != 0). func NewMilliQuantity(value int64, format Format) *Quantity { return &Quantity{ - Amount: inf.NewDec(value, 3), + i: int64Amount{value: value, scale: -3}, Format: format, } } @@ -435,7 +654,7 @@ func NewMilliQuantity(value int64, format Format) *Quantity { // value * 10^scale in DecimalSI format. func NewScaledQuantity(value int64, scale Scale) *Quantity { return &Quantity{ - Amount: inf.NewDec(value, scale.infScale()), + i: int64Amount{value: value, scale: scale}, Format: DecimalSI, } } @@ -454,10 +673,12 @@ func (q *Quantity) MilliValue() int64 { // ScaledValue returns the value of ceil(q * 10^scale); this could overflow an int64. // To detect overflow, call Value() first and verify the expected magnitude. func (q *Quantity) ScaledValue(scale Scale) int64 { - if q.Amount == nil { - return 0 + if q.d.Dec == nil { + i, _ := q.i.AsScaledInt64(scale) + return i } - return scaledValue(q.Amount.UnscaledBig(), int(q.Amount.Scale()), int(scale.infScale())) + dec := q.d.Dec + return scaledValue(dec.UnscaledBig(), int(dec.Scale()), int(scale.infScale())) } // Set sets q's value to be value. @@ -472,22 +693,25 @@ func (q *Quantity) SetMilli(value int64) { // SetScaled sets q's value to be value * 10^scale func (q *Quantity) SetScaled(value int64, scale Scale) { - if q.Amount == nil { - q.Amount = &inf.Dec{} - } - q.Amount.SetUnscaled(value) - q.Amount.SetScale(scale.infScale()) + q.s = nil + q.d.Dec = nil + q.i = int64Amount{value: value, scale: scale} } // Copy is a convenience function that makes a deep copy for you. Non-deep // copies of quantities share pointers and you will regret that. func (q *Quantity) Copy() *Quantity { - if q.Amount == nil { - return NewQuantity(0, q.Format) + if q.d.Dec == nil { + return &Quantity{ + s: q.s, + i: q.i, + Format: q.Format, + } } tmp := &inf.Dec{} return &Quantity{ - Amount: tmp.Set(q.Amount), + s: q.s, + d: infDecAmount{tmp.Set(q.d.Dec)}, Format: q.Format, } } @@ -504,7 +728,7 @@ func (qf qFlag) Set(val string) error { return err } // This copy is OK because q will not be referenced again. - *qf.dest = *q + *qf.dest = q return nil } @@ -531,8 +755,3 @@ func QuantityFlag(flagName, defaultValue, description string) *Quantity { func NewQuantityFlagValue(q *Quantity) flag.Value { return qFlag{q} } - -// infScale adapts a Scale value to an inf.Scale value. -func (s Scale) infScale() inf.Scale { - return inf.Scale(-s) // inf.Scale is upside-down -} diff --git a/pkg/api/resource/quantity_proto.go b/pkg/api/resource/quantity_proto.go index 01d5c2997ea..0159a4112b7 100644 --- a/pkg/api/resource/quantity_proto.go +++ b/pkg/api/resource/quantity_proto.go @@ -17,62 +17,268 @@ limitations under the License. package resource import ( - "math/big" + "fmt" + "io" - inf "gopkg.in/inf.v0" + "github.com/gogo/protobuf/proto" ) -// QuantityProto is a struct that is equivalent to Quantity, but intended for -// protobuf marshalling/unmarshalling. It is generated into a serialization -// that matches Quantity. Do not use in Go structs. -// -// +protobuf=true -type QuantityProto struct { - // The format of the quantity - Format Format `protobuf:"bytes,1,opt,name=format,casttype=Format"` - // The scale dimension of the value - Scale int32 `protobuf:"varint,2,opt,name=scale"` - // Bigint is serialized as a raw bytes array - Bigint []byte `protobuf:"bytes,3,opt,name=bigint"` +var _ proto.Sizer = &Quantity{} + +func (m *Quantity) Marshal() (data []byte, err error) { + size := m.Size() + data = make([]byte, size) + n, err := m.MarshalTo(data) + if err != nil { + return nil, err + } + return data[:n], nil } -// ProtoTime returns the Time as a new ProtoTime value. -func (q *Quantity) QuantityProto() *QuantityProto { - if q == nil { - return &QuantityProto{} - } - p := &QuantityProto{ - Format: q.Format, - } - if q.Amount != nil { - p.Scale = int32(q.Amount.Scale()) - p.Bigint = q.Amount.UnscaledBig().Bytes() - } - return p +// MarshalTo is a customized version of the generated Protobuf unmarshaler for a struct +// with a single string field. +func (m *Quantity) MarshalTo(data []byte) (int, error) { + var i int + _ = i + var l int + _ = l + + data[i] = 0xa + i++ + // BEGIN CUSTOM MARSHAL + out := m.toBytes() + i = encodeVarintGenerated(data, i, uint64(len(out))) + i += copy(data[i:], out) + // END CUSTOM MARSHAL + + return i, nil } -// Size implements the protobuf marshalling interface. -func (q *Quantity) Size() (n int) { return q.QuantityProto().Size() } - -// Reset implements the protobuf marshalling interface. -func (q *Quantity) Unmarshal(data []byte) error { - p := QuantityProto{} - if err := p.Unmarshal(data); err != nil { - return err +func encodeVarintGenerated(data []byte, offset int, v uint64) int { + for v >= 1<<7 { + data[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + data[offset] = uint8(v) + return offset + 1 +} + +func (m *Quantity) Size() (n int) { + var l int + _ = l + + // BEGIN CUSTOM SIZE + l = len(m.toBytes()) + // END CUSTOM SIZE + + n += 1 + l + sovGenerated(uint64(l)) + return n +} + +func sovGenerated(x uint64) (n int) { + for { + n++ + x >>= 7 + if x == 0 { + break + } + } + return n +} + +// Unmarshal is a customized version of the generated Protobuf unmarshaler for a struct +// with a single string field. +func (m *Quantity) Unmarshal(data []byte) error { + l := len(data) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := data[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Quantity: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Quantity: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field String_", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := data[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + s := string(data[iNdEx:postIndex]) + + // BEGIN CUSTOM DECODE + p, err := ParseQuantity(s) + if err != nil { + return err + } + *m = p + // END CUSTOM DECODE + + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipGenerated(data[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthGenerated + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF } - q.Format = p.Format - b := big.NewInt(0) - b.SetBytes(p.Bigint) - q.Amount = inf.NewDecBig(b, inf.Scale(p.Scale)) return nil } -// Marshal implements the protobuf marshalling interface. -func (q *Quantity) Marshal() (data []byte, err error) { - return q.QuantityProto().Marshal() +func skipGenerated(data []byte) (n int, err error) { + l := len(data) + iNdEx := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowGenerated + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := data[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowGenerated + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if data[iNdEx-1] < 0x80 { + break + } + } + return iNdEx, nil + case 1: + iNdEx += 8 + return iNdEx, nil + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowGenerated + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := data[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + iNdEx += length + if length < 0 { + return 0, ErrInvalidLengthGenerated + } + return iNdEx, nil + case 3: + for { + var innerWire uint64 + var start int = iNdEx + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowGenerated + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := data[iNdEx] + iNdEx++ + innerWire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + innerWireType := int(innerWire & 0x7) + if innerWireType == 4 { + break + } + next, err := skipGenerated(data[start:]) + if err != nil { + return 0, err + } + iNdEx = start + next + } + return iNdEx, nil + case 4: + return iNdEx, nil + case 5: + iNdEx += 4 + return iNdEx, nil + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + } + panic("unreachable") } -// MarshalTo implements the protobuf marshalling interface. -func (q *Quantity) MarshalTo(data []byte) (int, error) { - return q.QuantityProto().MarshalTo(data) -} +var ( + ErrInvalidLengthGenerated = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowGenerated = fmt.Errorf("proto: integer overflow") +) diff --git a/pkg/api/resource/quantity_test.go b/pkg/api/resource/quantity_test.go index 66d1563a27c..e37a8eca2c1 100644 --- a/pkg/api/resource/quantity_test.go +++ b/pkg/api/resource/quantity_test.go @@ -17,8 +17,8 @@ limitations under the License. package resource import ( - //"reflect" "encoding/json" + "math/rand" "testing" fuzz "github.com/google/gofuzz" @@ -31,14 +31,29 @@ var ( testQuantityFlag = QuantityFlag("quantityFlag", "1M", "dummy flag for testing the quantity flag mechanism") ) -func dec(i int64, exponent int) *inf.Dec { +var useInfDec bool + +func amount(i int64, exponent int) infDecAmount { // See the below test-- scale is the negative of an exponent. - return inf.NewDec(i, inf.Scale(-exponent)) + return infDecAmount{inf.NewDec(i, inf.Scale(-exponent))} +} + +func dec(i int64, exponent int) infDecAmount { + // See the below test-- scale is the negative of an exponent. + return infDecAmount{inf.NewDec(i, inf.Scale(-exponent))} +} + +func decQuantity(i int64, exponent int, format Format) Quantity { + return Quantity{d: dec(i, exponent), Format: format} +} + +func intQuantity(i int64, exponent Scale, format Format) Quantity { + return Quantity{i: int64Amount{value: i, scale: exponent}, Format: format} } func TestDec(t *testing.T) { table := []struct { - got *inf.Dec + got infDecAmount expect string }{ {dec(1, 0), "1"}, @@ -52,7 +67,7 @@ func TestDec(t *testing.T) { } for _, item := range table { - if e, a := item.expect, item.got.String(); e != a { + if e, a := item.expect, item.got.Dec.String(); e != a { t.Errorf("expected %v, got %v", e, a) } } @@ -75,19 +90,14 @@ func TestQuantityAddZeroPreservesSuffix(t *testing.T) { value := MustParse(testValue) v1 := *value.Copy() // ensure non-zero + zero = non-zero (suffix preserved) - err := v1.Add(zero) - if err != nil { - t.Errorf("Unexpected error %v", err) - } + v1.Add(zero) // ensure zero + non-zero = non-zero (suffix preserved) v2 := *zero.Copy() - err = v2.Add(value) - if err != nil { - t.Errorf("Unexpected error %v", err) - } - // ensure we preserved the input value + v2.Add(value) + if v1.String() != testValue { t.Errorf("Expected %v, actual %v", testValue, v1.String()) + continue } if v2.String() != testValue { t.Errorf("Expected %v, actual %v", testValue, v2.String()) @@ -104,10 +114,7 @@ func TestQuantitySubZeroPreservesSuffix(t *testing.T) { value := MustParse(testValue) v1 := *value.Copy() // ensure non-zero - zero = non-zero (suffix preserved) - err := v1.Sub(zero) - if err != nil { - t.Errorf("Unexpected error %v", err) - } + v1.Sub(zero) // ensure we preserved the input value if v1.String() != testValue { t.Errorf("Expected %v, actual %v", testValue, v1.String()) @@ -115,15 +122,9 @@ func TestQuantitySubZeroPreservesSuffix(t *testing.T) { // ensure zero - non-zero = -non-zero (suffix preserved) v2 := *zero.Copy() - err = v2.Sub(value) - if err != nil { - t.Errorf("Unexpected error %v", err) - } + v2.Sub(value) negVal := *value.Copy() - err = negVal.Neg(negVal) - if err != nil { - t.Errorf("Unexpected error %v", err) - } + negVal.Neg() if v2.String() != negVal.String() { t.Errorf("Expected %v, actual %v", negVal.String(), v2.String()) } @@ -133,10 +134,8 @@ func TestQuantitySubZeroPreservesSuffix(t *testing.T) { // Verifies that you get 0 as canonical value if internal value is 0, and not 0 func TestQuantityCanocicalizeZero(t *testing.T) { val := MustParse("1000m") - x := val.Amount - y := dec(1, 0) - z := val.Amount.Sub(x, y) - zero := Quantity{z, DecimalSI} + val.i.Sub(int64Amount{value: 1}) + zero := Quantity{i: val.i, Format: DecimalSI} if expected, actual := "0", zero.String(); expected != actual { t.Errorf("Expected %v, actual %v", expected, actual) } @@ -166,187 +165,300 @@ func TestQuantityCmp(t *testing.T) { y *inf.Dec expect int }{ - {dec(0, 0), dec(0, 0), 0}, - {nil, dec(0, 0), 0}, - {dec(0, 0), nil, 0}, + {dec(0, 0).Dec, dec(0, 0).Dec, 0}, + {nil, dec(0, 0).Dec, 0}, + {dec(0, 0).Dec, nil, 0}, {nil, nil, 0}, - {nil, dec(10, 0), -1}, - {nil, dec(-10, 0), 1}, - {dec(10, 0), nil, 1}, - {dec(-10, 0), nil, -1}, + {nil, dec(10, 0).Dec, -1}, + {nil, dec(-10, 0).Dec, 1}, + {dec(10, 0).Dec, nil, 1}, + {dec(-10, 0).Dec, nil, -1}, } for _, nilCase := range nils { - q1 := Quantity{nilCase.x, DecimalSI} - q2 := Quantity{nilCase.y, DecimalSI} + q1 := Quantity{d: infDecAmount{nilCase.x}, Format: DecimalSI} + q2 := Quantity{d: infDecAmount{nilCase.y}, Format: DecimalSI} if result := q1.Cmp(q2); result != nilCase.expect { t.Errorf("X: %v, Y: %v, Expected: %v, Actual: %v", nilCase.x, nilCase.y, nilCase.expect, result) } } } +func TestParseQuantityString(t *testing.T) { + table := []struct { + input string + positive bool + value string + num, denom, suffix string + }{ + {"0.025Ti", true, "0.025", "0", "025", "Ti"}, + {"1.025Ti", true, "1.025", "1", "025", "Ti"}, + {"-1.025Ti", false, "-1.025", "1", "025", "Ti"}, + {".", true, ".", "0", "", ""}, + {"-.", false, "-.", "0", "", ""}, + {"1E-3", true, "1", "1", "", "E-3"}, + } + for _, test := range table { + positive, value, num, denom, suffix, err := parseQuantityString(test.input) + if err != nil { + t.Errorf("%s: error: %v", test.input, err) + continue + } + if positive != test.positive || value != test.value || num != test.num || denom != test.denom || suffix != test.suffix { + t.Errorf("%s: unmatched: %t %q %q %q %q", test.input, positive, value, num, denom, suffix) + } + } +} + func TestQuantityParse(t *testing.T) { + if _, err := ParseQuantity(""); err == nil { + t.Errorf("expected empty string to return error") + } + table := []struct { input string expect Quantity }{ - {"0", Quantity{dec(0, 0), DecimalSI}}, - {"0n", Quantity{dec(0, 0), DecimalSI}}, - {"0u", Quantity{dec(0, 0), DecimalSI}}, - {"0m", Quantity{dec(0, 0), DecimalSI}}, - {"0Ki", Quantity{dec(0, 0), BinarySI}}, - {"0k", Quantity{dec(0, 0), DecimalSI}}, - {"0Mi", Quantity{dec(0, 0), BinarySI}}, - {"0M", Quantity{dec(0, 0), DecimalSI}}, - {"0Gi", Quantity{dec(0, 0), BinarySI}}, - {"0G", Quantity{dec(0, 0), DecimalSI}}, - {"0Ti", Quantity{dec(0, 0), BinarySI}}, - {"0T", Quantity{dec(0, 0), DecimalSI}}, + {"0", decQuantity(0, 0, DecimalSI)}, + {"0n", decQuantity(0, 0, DecimalSI)}, + {"0u", decQuantity(0, 0, DecimalSI)}, + {"0m", decQuantity(0, 0, DecimalSI)}, + {"0Ki", decQuantity(0, 0, BinarySI)}, + {"0k", decQuantity(0, 0, DecimalSI)}, + {"0Mi", decQuantity(0, 0, BinarySI)}, + {"0M", decQuantity(0, 0, DecimalSI)}, + {"0Gi", decQuantity(0, 0, BinarySI)}, + {"0G", decQuantity(0, 0, DecimalSI)}, + {"0Ti", decQuantity(0, 0, BinarySI)}, + {"0T", decQuantity(0, 0, DecimalSI)}, // Binary suffixes - {"1Ki", Quantity{dec(1024, 0), BinarySI}}, - {"8Ki", Quantity{dec(8*1024, 0), BinarySI}}, - {"7Mi", Quantity{dec(7*1024*1024, 0), BinarySI}}, - {"6Gi", Quantity{dec(6*1024*1024*1024, 0), BinarySI}}, - {"5Ti", Quantity{dec(5*1024*1024*1024*1024, 0), BinarySI}}, - {"4Pi", Quantity{dec(4*1024*1024*1024*1024*1024, 0), BinarySI}}, - {"3Ei", Quantity{dec(3*1024*1024*1024*1024*1024*1024, 0), BinarySI}}, + {"1Ki", decQuantity(1024, 0, BinarySI)}, + {"8Ki", decQuantity(8*1024, 0, BinarySI)}, + {"7Mi", decQuantity(7*1024*1024, 0, BinarySI)}, + {"6Gi", decQuantity(6*1024*1024*1024, 0, BinarySI)}, + {"5Ti", decQuantity(5*1024*1024*1024*1024, 0, BinarySI)}, + {"4Pi", decQuantity(4*1024*1024*1024*1024*1024, 0, BinarySI)}, + {"3Ei", decQuantity(3*1024*1024*1024*1024*1024*1024, 0, BinarySI)}, - {"10Ti", Quantity{dec(10*1024*1024*1024*1024, 0), BinarySI}}, - {"100Ti", Quantity{dec(100*1024*1024*1024*1024, 0), BinarySI}}, + {"10Ti", decQuantity(10*1024*1024*1024*1024, 0, BinarySI)}, + {"100Ti", decQuantity(100*1024*1024*1024*1024, 0, BinarySI)}, // Decimal suffixes - {"5n", Quantity{dec(5, -9), DecimalSI}}, - {"4u", Quantity{dec(4, -6), DecimalSI}}, - {"3m", Quantity{dec(3, -3), DecimalSI}}, - {"9", Quantity{dec(9, 0), DecimalSI}}, - {"8k", Quantity{dec(8, 3), DecimalSI}}, - {"7M", Quantity{dec(7, 6), DecimalSI}}, - {"6G", Quantity{dec(6, 9), DecimalSI}}, - {"5T", Quantity{dec(5, 12), DecimalSI}}, - {"40T", Quantity{dec(4, 13), DecimalSI}}, - {"300T", Quantity{dec(3, 14), DecimalSI}}, - {"2P", Quantity{dec(2, 15), DecimalSI}}, - {"1E", Quantity{dec(1, 18), DecimalSI}}, + {"5n", decQuantity(5, -9, DecimalSI)}, + {"4u", decQuantity(4, -6, DecimalSI)}, + {"3m", decQuantity(3, -3, DecimalSI)}, + {"9", decQuantity(9, 0, DecimalSI)}, + {"8k", decQuantity(8, 3, DecimalSI)}, + {"50k", decQuantity(5, 4, DecimalSI)}, + {"7M", decQuantity(7, 6, DecimalSI)}, + {"6G", decQuantity(6, 9, DecimalSI)}, + {"5T", decQuantity(5, 12, DecimalSI)}, + {"40T", decQuantity(4, 13, DecimalSI)}, + {"300T", decQuantity(3, 14, DecimalSI)}, + {"2P", decQuantity(2, 15, DecimalSI)}, + {"1E", decQuantity(1, 18, DecimalSI)}, // Decimal exponents - {"1E-3", Quantity{dec(1, -3), DecimalExponent}}, - {"1e3", Quantity{dec(1, 3), DecimalExponent}}, - {"1E6", Quantity{dec(1, 6), DecimalExponent}}, - {"1e9", Quantity{dec(1, 9), DecimalExponent}}, - {"1E12", Quantity{dec(1, 12), DecimalExponent}}, - {"1e15", Quantity{dec(1, 15), DecimalExponent}}, - {"1E18", Quantity{dec(1, 18), DecimalExponent}}, + {"1E-3", decQuantity(1, -3, DecimalExponent)}, + {"1e3", decQuantity(1, 3, DecimalExponent)}, + {"1E6", decQuantity(1, 6, DecimalExponent)}, + {"1e9", decQuantity(1, 9, DecimalExponent)}, + {"1E12", decQuantity(1, 12, DecimalExponent)}, + {"1e15", decQuantity(1, 15, DecimalExponent)}, + {"1E18", decQuantity(1, 18, DecimalExponent)}, // Nonstandard but still parsable - {"1e14", Quantity{dec(1, 14), DecimalExponent}}, - {"1e13", Quantity{dec(1, 13), DecimalExponent}}, - {"1e3", Quantity{dec(1, 3), DecimalExponent}}, - {"100.035k", Quantity{dec(100035, 0), DecimalSI}}, + {"1e14", decQuantity(1, 14, DecimalExponent)}, + {"1e13", decQuantity(1, 13, DecimalExponent)}, + {"1e3", decQuantity(1, 3, DecimalExponent)}, + {"100.035k", decQuantity(100035, 0, DecimalSI)}, // Things that look like floating point - {"0.001", Quantity{dec(1, -3), DecimalSI}}, - {"0.0005k", Quantity{dec(5, -1), DecimalSI}}, - {"0.005", Quantity{dec(5, -3), DecimalSI}}, - {"0.05", Quantity{dec(5, -2), DecimalSI}}, - {"0.5", Quantity{dec(5, -1), DecimalSI}}, - {"0.00050k", Quantity{dec(5, -1), DecimalSI}}, - {"0.00500", Quantity{dec(5, -3), DecimalSI}}, - {"0.05000", Quantity{dec(5, -2), DecimalSI}}, - {"0.50000", Quantity{dec(5, -1), DecimalSI}}, - {"0.5e0", Quantity{dec(5, -1), DecimalExponent}}, - {"0.5e-1", Quantity{dec(5, -2), DecimalExponent}}, - {"0.5e-2", Quantity{dec(5, -3), DecimalExponent}}, - {"0.5e0", Quantity{dec(5, -1), DecimalExponent}}, - {"10.035M", Quantity{dec(10035, 3), DecimalSI}}, + {"0.001", decQuantity(1, -3, DecimalSI)}, + {"0.0005k", decQuantity(5, -1, DecimalSI)}, + {"0.005", decQuantity(5, -3, DecimalSI)}, + {"0.05", decQuantity(5, -2, DecimalSI)}, + {"0.5", decQuantity(5, -1, DecimalSI)}, + {"0.00050k", decQuantity(5, -1, DecimalSI)}, + {"0.00500", decQuantity(5, -3, DecimalSI)}, + {"0.05000", decQuantity(5, -2, DecimalSI)}, + {"0.50000", decQuantity(5, -1, DecimalSI)}, + {"0.5e0", decQuantity(5, -1, DecimalExponent)}, + {"0.5e-1", decQuantity(5, -2, DecimalExponent)}, + {"0.5e-2", decQuantity(5, -3, DecimalExponent)}, + {"0.5e0", decQuantity(5, -1, DecimalExponent)}, + {"10.035M", decQuantity(10035, 3, DecimalSI)}, - {"1.2e3", Quantity{dec(12, 2), DecimalExponent}}, - {"1.3E+6", Quantity{dec(13, 5), DecimalExponent}}, - {"1.40e9", Quantity{dec(14, 8), DecimalExponent}}, - {"1.53E12", Quantity{dec(153, 10), DecimalExponent}}, - {"1.6e15", Quantity{dec(16, 14), DecimalExponent}}, - {"1.7E18", Quantity{dec(17, 17), DecimalExponent}}, + {"1.2e3", decQuantity(12, 2, DecimalExponent)}, + {"1.3E+6", decQuantity(13, 5, DecimalExponent)}, + {"1.40e9", decQuantity(14, 8, DecimalExponent)}, + {"1.53E12", decQuantity(153, 10, DecimalExponent)}, + {"1.6e15", decQuantity(16, 14, DecimalExponent)}, + {"1.7E18", decQuantity(17, 17, DecimalExponent)}, - {"9.01", Quantity{dec(901, -2), DecimalSI}}, - {"8.1k", Quantity{dec(81, 2), DecimalSI}}, - {"7.123456M", Quantity{dec(7123456, 0), DecimalSI}}, - {"6.987654321G", Quantity{dec(6987654321, 0), DecimalSI}}, - {"5.444T", Quantity{dec(5444, 9), DecimalSI}}, - {"40.1T", Quantity{dec(401, 11), DecimalSI}}, - {"300.2T", Quantity{dec(3002, 11), DecimalSI}}, - {"2.5P", Quantity{dec(25, 14), DecimalSI}}, - {"1.01E", Quantity{dec(101, 16), DecimalSI}}, + {"9.01", decQuantity(901, -2, DecimalSI)}, + {"8.1k", decQuantity(81, 2, DecimalSI)}, + {"7.123456M", decQuantity(7123456, 0, DecimalSI)}, + {"6.987654321G", decQuantity(6987654321, 0, DecimalSI)}, + {"5.444T", decQuantity(5444, 9, DecimalSI)}, + {"40.1T", decQuantity(401, 11, DecimalSI)}, + {"300.2T", decQuantity(3002, 11, DecimalSI)}, + {"2.5P", decQuantity(25, 14, DecimalSI)}, + {"1.01E", decQuantity(101, 16, DecimalSI)}, // Things that saturate/round - {"3.001n", Quantity{dec(4, -9), DecimalSI}}, - {"1.1E-9", Quantity{dec(2, -9), DecimalExponent}}, - {"0.0000000001", Quantity{dec(1, -9), DecimalSI}}, - {"0.0000000005", Quantity{dec(1, -9), DecimalSI}}, - {"0.00000000050", Quantity{dec(1, -9), DecimalSI}}, - {"0.5e-9", Quantity{dec(1, -9), DecimalExponent}}, - {"0.9n", Quantity{dec(1, -9), DecimalSI}}, - {"0.00000012345", Quantity{dec(124, -9), DecimalSI}}, - {"0.00000012354", Quantity{dec(124, -9), DecimalSI}}, - {"9Ei", Quantity{maxAllowed, BinarySI}}, - {"9223372036854775807Ki", Quantity{maxAllowed, BinarySI}}, - {"12E", Quantity{maxAllowed, DecimalSI}}, + {"3.001n", decQuantity(4, -9, DecimalSI)}, + {"1.1E-9", decQuantity(2, -9, DecimalExponent)}, + {"0.0000000001", decQuantity(1, -9, DecimalSI)}, + {"0.0000000005", decQuantity(1, -9, DecimalSI)}, + {"0.00000000050", decQuantity(1, -9, DecimalSI)}, + {"0.5e-9", decQuantity(1, -9, DecimalExponent)}, + {"0.9n", decQuantity(1, -9, DecimalSI)}, + {"0.00000012345", decQuantity(124, -9, DecimalSI)}, + {"0.00000012354", decQuantity(124, -9, DecimalSI)}, + {"9Ei", Quantity{d: maxAllowed, Format: BinarySI}}, + {"9223372036854775807Ki", Quantity{d: maxAllowed, Format: BinarySI}}, + {"12E", decQuantity(12, 18, DecimalSI)}, // We'll accept fractional binary stuff, too. - {"100.035Ki", Quantity{dec(10243584, -2), BinarySI}}, - {"0.5Mi", Quantity{dec(.5*1024*1024, 0), BinarySI}}, - {"0.05Gi", Quantity{dec(536870912, -1), BinarySI}}, - {"0.025Ti", Quantity{dec(274877906944, -1), BinarySI}}, + {"100.035Ki", decQuantity(10243584, -2, BinarySI)}, + {"0.5Mi", decQuantity(.5*1024*1024, 0, BinarySI)}, + {"0.05Gi", decQuantity(536870912, -1, BinarySI)}, + {"0.025Ti", decQuantity(274877906944, -1, BinarySI)}, // Things written by trolls - {"0.000000000001Ki", Quantity{dec(2, -9), DecimalSI}}, // rounds up, changes format - {".001", Quantity{dec(1, -3), DecimalSI}}, - {".0001k", Quantity{dec(100, -3), DecimalSI}}, - {"1.", Quantity{dec(1, 0), DecimalSI}}, - {"1.G", Quantity{dec(1, 9), DecimalSI}}, + {"0.000000000001Ki", decQuantity(2, -9, DecimalSI)}, // rounds up, changes format + {".001", decQuantity(1, -3, DecimalSI)}, + {".0001k", decQuantity(100, -3, DecimalSI)}, + {"1.", decQuantity(1, 0, DecimalSI)}, + {"1.G", decQuantity(1, 9, DecimalSI)}, } - for _, item := range table { - got, err := ParseQuantity(item.input) - if err != nil { - t.Errorf("%v: unexpected error: %v", item.input, err) - continue - } - if e, a := item.expect.Amount, got.Amount; e.Cmp(a) != 0 { - t.Errorf("%v: expected %v, got %v", item.input, e, a) - } - if e, a := item.expect.Format, got.Format; e != a { - t.Errorf("%v: expected %#v, got %#v", item.input, e, a) - } - } + for _, asDec := range []bool{false, true} { + for _, item := range table { + got, err := ParseQuantity(item.input) + if err != nil { + t.Errorf("%v: unexpected error: %v", item.input, err) + continue + } + if asDec { + got.AsDec() + } - // Try the negative version of everything - desired := &inf.Dec{} - for _, item := range table { - got, err := ParseQuantity("-" + item.input) - if err != nil { - t.Errorf("-%v: unexpected error: %v", item.input, err) - continue - } - desired.Neg(item.expect.Amount) - if e, a := desired, got.Amount; e.Cmp(a) != 0 { - t.Errorf("%v: expected %v, got %v", item.input, e, a) - } - if e, a := item.expect.Format, got.Format; e != a { - t.Errorf("%v: expected %#v, got %#v", item.input, e, a) - } - } + if e, a := item.expect, got; e.Cmp(a) != 0 { + t.Errorf("%v: expected %v, got %v", item.input, e.String(), a.String()) + } + if e, a := item.expect.Format, got.Format; e != a { + t.Errorf("%v: expected %#v, got %#v", item.input, e, a) + } - // Try everything with an explicit + - for _, item := range table { - got, err := ParseQuantity("+" + item.input) - if err != nil { - t.Errorf("-%v: unexpected error: %v", item.input, err) - continue + if asDec { + if i, ok := got.AsInt64(); i != 0 || ok { + t.Errorf("%v: expected inf.Dec to return false for AsInt64: %d", item.input, i) + } + continue + } + i, ok := item.expect.AsInt64() + if !ok { + continue + } + j, ok := got.AsInt64() + if !ok { + if got.d.Dec == nil && got.i.scale >= 0 { + t.Errorf("%v: is an int64Amount, but can't return AsInt64: %v", item.input, got) + } + continue + } + if i != j { + t.Errorf("%v: expected equivalent representation as int64: %d %d", item.input, i, j) + } } - if e, a := item.expect.Amount, got.Amount; e.Cmp(a) != 0 { - t.Errorf("%v: expected %v, got %v", item.input, e, a) + + for _, item := range table { + got, err := ParseQuantity(item.input) + if err != nil { + t.Errorf("%v: unexpected error: %v", item.input, err) + continue + } + + if asDec { + got.AsDec() + } + + // verify that we can decompose the input and get the same result by building up from the base. + positive, _, num, denom, suffix, err := parseQuantityString(item.input) + if err != nil { + t.Errorf("%v: unexpected error: %v", item.input, err) + continue + } + if got.Sign() >= 0 && !positive || got.Sign() < 0 && positive { + t.Errorf("%v: positive was incorrect: %t", item.input, positive) + continue + } + var value string + if !positive { + value = "-" + } + value += num + if len(denom) > 0 { + value += "." + denom + } + value += suffix + if len(value) == 0 { + t.Errorf("%v: did not parse correctly, %q %q %q", item.input, num, denom, suffix) + } + expected, err := ParseQuantity(value) + if err != nil { + t.Errorf("%v: unexpected error for %s: %v", item.input, value, err) + continue + } + if expected.Cmp(got) != 0 { + t.Errorf("%v: not the same as %s", item.input, value) + continue + } } - if e, a := item.expect.Format, got.Format; e != a { - t.Errorf("%v: expected %#v, got %#v", item.input, e, a) + + // Try the negative version of everything + desired := &inf.Dec{} + expect := Quantity{d: infDecAmount{Dec: desired}} + for _, item := range table { + got, err := ParseQuantity("-" + item.input) + if err != nil { + t.Errorf("-%v: unexpected error: %v", item.input, err) + continue + } + if asDec { + got.AsDec() + } + + expected := item.expect + desired.Neg(expected.AsDec()) + + if e, a := expect, got; e.Cmp(a) != 0 { + t.Errorf("%v: expected %s, got %s", item.input, e.String(), a.String()) + } + if e, a := expected.Format, got.Format; e != a { + t.Errorf("%v: expected %#v, got %#v", item.input, e, a) + } + } + + // Try everything with an explicit + + for _, item := range table { + got, err := ParseQuantity("+" + item.input) + if err != nil { + t.Errorf("-%v: unexpected error: %v", item.input, err) + continue + } + if asDec { + got.AsDec() + } + + if e, a := item.expect, got; e.Cmp(a) != 0 { + t.Errorf("%v(%t): expected %s, got %s", item.input, asDec, e.String(), a.String()) + } + if e, a := item.expect.Format, got.Format; e != a { + t.Errorf("%v: expected %#v, got %#v", item.input, e, a) + } } } @@ -359,6 +471,7 @@ func TestQuantityParse(t *testing.T) { ".5i", "1i", "-3.01i", + "-3.01e-", } for _, item := range invalid { _, err := ParseQuantity(item) @@ -368,53 +481,193 @@ func TestQuantityParse(t *testing.T) { } } +func TestQuantityRoundUp(t *testing.T) { + table := []struct { + in string + scale Scale + expect Quantity + ok bool + }{ + {"9.01", -3, decQuantity(901, -2, DecimalSI), true}, + {"9.01", -2, decQuantity(901, -2, DecimalSI), true}, + {"9.01", -1, decQuantity(91, -1, DecimalSI), false}, + {"9.01", 0, decQuantity(10, 0, DecimalSI), false}, + {"9.01", 1, decQuantity(10, 0, DecimalSI), false}, + {"9.01", 2, decQuantity(100, 0, DecimalSI), false}, + + {"-9.01", -3, decQuantity(-901, -2, DecimalSI), true}, + {"-9.01", -2, decQuantity(-901, -2, DecimalSI), true}, + {"-9.01", -1, decQuantity(-91, -1, DecimalSI), false}, + {"-9.01", 0, decQuantity(-10, 0, DecimalSI), false}, + {"-9.01", 1, decQuantity(-10, 0, DecimalSI), false}, + {"-9.01", 2, decQuantity(-100, 0, DecimalSI), false}, + } + + for _, asDec := range []bool{false, true} { + for _, item := range table { + got, err := ParseQuantity(item.in) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + expect := *item.expect.Copy() + if asDec { + got.AsDec() + } + if ok := got.RoundUp(item.scale); ok != item.ok { + t.Errorf("%s(%d,%t): unexpected ok: %t", item.in, item.scale, asDec, ok) + } + if got.Cmp(expect) != 0 { + t.Errorf("%s(%d,%t): unexpected round: %s vs %s", item.in, item.scale, asDec, got.String(), expect.String()) + } + } + } +} + +func TestQuantityCmpInt64AndDec(t *testing.T) { + table := []struct { + a, b Quantity + cmp int + }{ + {intQuantity(901, -2, DecimalSI), intQuantity(901, -2, DecimalSI), 0}, + {intQuantity(90, -1, DecimalSI), intQuantity(901, -2, DecimalSI), -1}, + {intQuantity(901, -2, DecimalSI), intQuantity(900, -2, DecimalSI), 1}, + {intQuantity(0, 0, DecimalSI), intQuantity(0, 0, DecimalSI), 0}, + {intQuantity(0, 1, DecimalSI), intQuantity(0, -1, DecimalSI), 0}, + {intQuantity(0, -1, DecimalSI), intQuantity(0, 1, DecimalSI), 0}, + {intQuantity(800, -3, DecimalSI), intQuantity(1, 0, DecimalSI), -1}, + {intQuantity(800, -3, DecimalSI), intQuantity(79, -2, DecimalSI), 1}, + + {intQuantity(mostPositive, 0, DecimalSI), intQuantity(1, -1, DecimalSI), 1}, + {intQuantity(mostPositive, 1, DecimalSI), intQuantity(1, 0, DecimalSI), 1}, + {intQuantity(mostPositive, 1, DecimalSI), intQuantity(1, 1, DecimalSI), 1}, + {intQuantity(mostPositive, 1, DecimalSI), intQuantity(0, 1, DecimalSI), 1}, + {intQuantity(mostPositive, -16, DecimalSI), intQuantity(1, 3, DecimalSI), -1}, + + {intQuantity(mostNegative, 0, DecimalSI), intQuantity(0, 0, DecimalSI), -1}, + {intQuantity(mostNegative, -18, DecimalSI), intQuantity(-1, 0, DecimalSI), -1}, + {intQuantity(mostNegative, -19, DecimalSI), intQuantity(-1, 0, DecimalSI), 1}, + + {intQuantity(1*1000000*1000000*1000000, -17, DecimalSI), intQuantity(1, 1, DecimalSI), 0}, + {intQuantity(1*1000000*1000000*1000000, -17, DecimalSI), intQuantity(-10, 0, DecimalSI), 1}, + {intQuantity(-1*1000000*1000000*1000000, -17, DecimalSI), intQuantity(-10, 0, DecimalSI), 0}, + {intQuantity(1*1000000*1000000*1000000, -17, DecimalSI), intQuantity(1, 0, DecimalSI), 1}, + + {intQuantity(1*1000000*1000000*1000000+1, -17, DecimalSI), intQuantity(1, 1, DecimalSI), 1}, + {intQuantity(1*1000000*1000000*1000000-1, -17, DecimalSI), intQuantity(1, 1, DecimalSI), -1}, + } + + for _, item := range table { + if cmp := item.a.Cmp(item.b); cmp != item.cmp { + t.Errorf("%#v: unexpected Cmp: %d", item, cmp) + } + if cmp := item.b.Cmp(item.a); cmp != -item.cmp { + t.Errorf("%#v: unexpected inverted Cmp: %d", item, cmp) + } + } + + for _, item := range table { + a, b := *item.a.Copy(), *item.b.Copy() + a.AsDec() + if cmp := a.Cmp(b); cmp != item.cmp { + t.Errorf("%#v: unexpected Cmp: %d", item, cmp) + } + if cmp := b.Cmp(a); cmp != -item.cmp { + t.Errorf("%#v: unexpected inverted Cmp: %d", item, cmp) + } + } + + for _, item := range table { + a, b := *item.a.Copy(), *item.b.Copy() + b.AsDec() + if cmp := a.Cmp(b); cmp != item.cmp { + t.Errorf("%#v: unexpected Cmp: %d", item, cmp) + } + if cmp := b.Cmp(a); cmp != -item.cmp { + t.Errorf("%#v: unexpected inverted Cmp: %d", item, cmp) + } + } + + for _, item := range table { + a, b := *item.a.Copy(), *item.b.Copy() + a.AsDec() + b.AsDec() + if cmp := a.Cmp(b); cmp != item.cmp { + t.Errorf("%#v: unexpected Cmp: %d", item, cmp) + } + if cmp := b.Cmp(a); cmp != -item.cmp { + t.Errorf("%#v: unexpected inverted Cmp: %d", item, cmp) + } + } +} + +func TestQuantityNeg(t *testing.T) { + table := []struct { + a Quantity + out string + }{ + {intQuantity(901, -2, DecimalSI), "-9010m"}, + {decQuantity(901, -2, DecimalSI), "-9010m"}, + } + + for i, item := range table { + out := *item.a.Copy() + out.Neg() + if out.Cmp(item.a) == 0 { + t.Errorf("%d: negating an item should not mutate the source: %s", i, out.String()) + } + if out.String() != item.out { + t.Errorf("%d: negating did not equal exact value: %s", i, out.String()) + } + } +} + func TestQuantityString(t *testing.T) { table := []struct { in Quantity expect string }{ - {Quantity{dec(1024*1024*1024, 0), BinarySI}, "1Gi"}, - {Quantity{dec(300*1024*1024, 0), BinarySI}, "300Mi"}, - {Quantity{dec(6*1024, 0), BinarySI}, "6Ki"}, - {Quantity{dec(1001*1024*1024*1024, 0), BinarySI}, "1001Gi"}, - {Quantity{dec(1024*1024*1024*1024, 0), BinarySI}, "1Ti"}, - {Quantity{dec(5, 0), BinarySI}, "5"}, - {Quantity{dec(500, -3), BinarySI}, "500m"}, - {Quantity{dec(1, 9), DecimalSI}, "1G"}, - {Quantity{dec(1000, 6), DecimalSI}, "1G"}, - {Quantity{dec(1000000, 3), DecimalSI}, "1G"}, - {Quantity{dec(1000000000, 0), DecimalSI}, "1G"}, - {Quantity{dec(1, -3), DecimalSI}, "1m"}, - {Quantity{dec(80, -3), DecimalSI}, "80m"}, - {Quantity{dec(1080, -3), DecimalSI}, "1080m"}, - {Quantity{dec(108, -2), DecimalSI}, "1080m"}, - {Quantity{dec(10800, -4), DecimalSI}, "1080m"}, - {Quantity{dec(300, 6), DecimalSI}, "300M"}, - {Quantity{dec(1, 12), DecimalSI}, "1T"}, - {Quantity{dec(1234567, 6), DecimalSI}, "1234567M"}, - {Quantity{dec(1234567, -3), BinarySI}, "1234567m"}, - {Quantity{dec(3, 3), DecimalSI}, "3k"}, - {Quantity{dec(1025, 0), BinarySI}, "1025"}, - {Quantity{dec(0, 0), DecimalSI}, "0"}, - {Quantity{dec(0, 0), BinarySI}, "0"}, - {Quantity{dec(1, 9), DecimalExponent}, "1e9"}, - {Quantity{dec(1, -3), DecimalExponent}, "1e-3"}, - {Quantity{dec(1, -9), DecimalExponent}, "1e-9"}, - {Quantity{dec(80, -3), DecimalExponent}, "80e-3"}, - {Quantity{dec(300, 6), DecimalExponent}, "300e6"}, - {Quantity{dec(1, 12), DecimalExponent}, "1e12"}, - {Quantity{dec(1, 3), DecimalExponent}, "1e3"}, - {Quantity{dec(3, 3), DecimalExponent}, "3e3"}, - {Quantity{dec(3, 3), DecimalSI}, "3k"}, - {Quantity{dec(0, 0), DecimalExponent}, "0"}, - {Quantity{dec(1, -9), DecimalSI}, "1n"}, - {Quantity{dec(80, -9), DecimalSI}, "80n"}, - {Quantity{dec(1080, -9), DecimalSI}, "1080n"}, - {Quantity{dec(108, -8), DecimalSI}, "1080n"}, - {Quantity{dec(10800, -10), DecimalSI}, "1080n"}, - {Quantity{dec(1, -6), DecimalSI}, "1u"}, - {Quantity{dec(80, -6), DecimalSI}, "80u"}, - {Quantity{dec(1080, -6), DecimalSI}, "1080u"}, + {decQuantity(1024*1024*1024, 0, BinarySI), "1Gi"}, + {decQuantity(300*1024*1024, 0, BinarySI), "300Mi"}, + {decQuantity(6*1024, 0, BinarySI), "6Ki"}, + {decQuantity(1001*1024*1024*1024, 0, BinarySI), "1001Gi"}, + {decQuantity(1024*1024*1024*1024, 0, BinarySI), "1Ti"}, + {decQuantity(5, 0, BinarySI), "5"}, + {decQuantity(500, -3, BinarySI), "500m"}, + {decQuantity(1, 9, DecimalSI), "1G"}, + {decQuantity(1000, 6, DecimalSI), "1G"}, + {decQuantity(1000000, 3, DecimalSI), "1G"}, + {decQuantity(1000000000, 0, DecimalSI), "1G"}, + {decQuantity(1, -3, DecimalSI), "1m"}, + {decQuantity(80, -3, DecimalSI), "80m"}, + {decQuantity(1080, -3, DecimalSI), "1080m"}, + {decQuantity(108, -2, DecimalSI), "1080m"}, + {decQuantity(10800, -4, DecimalSI), "1080m"}, + {decQuantity(300, 6, DecimalSI), "300M"}, + {decQuantity(1, 12, DecimalSI), "1T"}, + {decQuantity(1234567, 6, DecimalSI), "1234567M"}, + {decQuantity(1234567, -3, BinarySI), "1234567m"}, + {decQuantity(3, 3, DecimalSI), "3k"}, + {decQuantity(1025, 0, BinarySI), "1025"}, + {decQuantity(0, 0, DecimalSI), "0"}, + {decQuantity(0, 0, BinarySI), "0"}, + {decQuantity(1, 9, DecimalExponent), "1e9"}, + {decQuantity(1, -3, DecimalExponent), "1e-3"}, + {decQuantity(1, -9, DecimalExponent), "1e-9"}, + {decQuantity(80, -3, DecimalExponent), "80e-3"}, + {decQuantity(300, 6, DecimalExponent), "300e6"}, + {decQuantity(1, 12, DecimalExponent), "1e12"}, + {decQuantity(1, 3, DecimalExponent), "1e3"}, + {decQuantity(3, 3, DecimalExponent), "3e3"}, + {decQuantity(3, 3, DecimalSI), "3k"}, + {decQuantity(0, 0, DecimalExponent), "0"}, + {decQuantity(1, -9, DecimalSI), "1n"}, + {decQuantity(80, -9, DecimalSI), "80n"}, + {decQuantity(1080, -9, DecimalSI), "1080n"}, + {decQuantity(108, -8, DecimalSI), "1080n"}, + {decQuantity(10800, -10, DecimalSI), "1080n"}, + {decQuantity(1, -6, DecimalSI), "1u"}, + {decQuantity(80, -6, DecimalSI), "80u"}, + {decQuantity(1080, -6, DecimalSI), "1080u"}, } for _, item := range table { got := item.in.String() @@ -424,12 +677,12 @@ func TestQuantityString(t *testing.T) { } desired := &inf.Dec{} // Avoid modifying the values in the table. for _, item := range table { - if item.in.Amount.Cmp(decZero) == 0 { + if item.in.Cmp(Quantity{}) == 0 { // Don't expect it to print "-0" ever continue } q := item.in - q.Amount = desired.Neg(q.Amount) + q.d = infDecAmount{desired.Neg(q.AsDec())} if e, a := "-"+item.expect, q.String(); e != a { t.Errorf("%#v: expected %v, got %v", item.in, e, a) } @@ -468,28 +721,32 @@ func TestQuantityParseEmit(t *testing.T) { t.Errorf("Couldn't parse %v", item.in) continue } - if q.Amount.Cmp(decZero) == 0 { + if q.Cmp(Quantity{}) == 0 { continue } if e, a := "-"+item.expect, q.String(); e != a { - t.Errorf("%#v: expected %v, got %v", item.in, e, a) + t.Errorf("%#v: expected %v, got %v (%#v)", item.in, e, a, q.i) } } } var fuzzer = fuzz.New().Funcs( func(q *Quantity, c fuzz.Continue) { - q.Amount = &inf.Dec{} + q.i = Zero if c.RandBool() { q.Format = BinarySI if c.RandBool() { - q.Amount.SetScale(0) - q.Amount.SetUnscaled(c.Int63()) + dec := &inf.Dec{} + q.d = infDecAmount{Dec: dec} + dec.SetScale(0) + dec.SetUnscaled(c.Int63()) return } // Be sure to test cases like 1Mi - q.Amount.SetScale(0) - q.Amount.SetUnscaled(c.Int63n(1024) << uint(10*c.Intn(5))) + dec := &inf.Dec{} + q.d = infDecAmount{Dec: dec} + dec.SetScale(0) + dec.SetUnscaled(c.Int63n(1024) << uint(10*c.Intn(5))) return } if c.RandBool() { @@ -498,13 +755,17 @@ var fuzzer = fuzz.New().Funcs( q.Format = DecimalExponent } if c.RandBool() { - q.Amount.SetScale(inf.Scale(c.Intn(4))) - q.Amount.SetUnscaled(c.Int63()) + dec := &inf.Dec{} + q.d = infDecAmount{Dec: dec} + dec.SetScale(inf.Scale(c.Intn(4))) + dec.SetUnscaled(c.Int63()) return } // Be sure to test cases like 1M - q.Amount.SetScale(inf.Scale(3 - c.Intn(15))) - q.Amount.SetUnscaled(c.Int63n(1000)) + dec := &inf.Dec{} + q.d = infDecAmount{Dec: dec} + dec.SetScale(inf.Scale(3 - c.Intn(15))) + dec.SetUnscaled(c.Int63n(1000)) }, ) @@ -514,14 +775,16 @@ func TestJSON(t *testing.T) { fuzzer.Fuzz(q) b, err := json.Marshal(q) if err != nil { - t.Errorf("error encoding %v", q) + t.Errorf("error encoding %v: %v", q, err) + continue } q2 := &Quantity{} err = json.Unmarshal(b, q2) if err != nil { - t.Errorf("%v: error decoding %v", q, string(b)) + t.Logf("%d: %s", i, string(b)) + t.Errorf("%v: error decoding %v: %v", q, string(b), err) } - if q2.Amount.Cmp(q.Amount) != 0 { + if q2.Cmp(*q) != 0 { t.Errorf("Expected equal: %v, %v (json was '%v')", q, q2, string(b)) } } @@ -582,25 +845,33 @@ func TestNewSet(t *testing.T) { {1024 * 1024, BinarySI, "1Mi"}, } - for _, item := range table { - q := NewQuantity(item.value, item.format) - if e, a := item.expect, q.String(); e != a { - t.Errorf("Expected %v, got %v; %#v", e, a, q) + for _, asDec := range []bool{false, true} { + for _, item := range table { + q := NewQuantity(item.value, item.format) + if asDec { + q.ToDec() + } + if e, a := item.expect, q.String(); e != a { + t.Errorf("Expected %v, got %v; %#v", e, a, q) + } + q2, err := ParseQuantity(q.String()) + if err != nil { + t.Errorf("Round trip failed on %v", q) + } + if e, a := item.value, q2.Value(); e != a { + t.Errorf("Expected %v, got %v", e, a) + } } - q2, err := ParseQuantity(q.String()) - if err != nil { - t.Errorf("Round trip failed on %v", q) - } - if e, a := item.value, q2.Value(); e != a { - t.Errorf("Expected %v, got %v", e, a) - } - } - for _, item := range table { - q := NewQuantity(0, item.format) - q.Set(item.value) - if e, a := item.expect, q.String(); e != a { - t.Errorf("Set: Expected %v, got %v; %#v", e, a, q) + for _, item := range table { + q := NewQuantity(0, item.format) + q.Set(item.value) + if asDec { + q.ToDec() + } + if e, a := item.expect, q.String(); e != a { + t.Errorf("Set: Expected %v, got %v; %#v", e, a, q) + } } } } @@ -716,12 +987,12 @@ func TestSub(t *testing.T) { b Quantity expected Quantity }{ - {Quantity{dec(10, 0), DecimalSI}, Quantity{dec(1, 1), DecimalSI}, Quantity{dec(0, 0), DecimalSI}}, - {Quantity{dec(10, 0), DecimalSI}, Quantity{dec(1, 0), BinarySI}, Quantity{dec(9, 0), DecimalSI}}, - {Quantity{dec(10, 0), BinarySI}, Quantity{dec(1, 0), DecimalSI}, Quantity{dec(9, 0), BinarySI}}, - {Quantity{nil, DecimalSI}, Quantity{dec(50, 0), DecimalSI}, Quantity{dec(-50, 0), DecimalSI}}, - {Quantity{dec(50, 0), DecimalSI}, Quantity{nil, DecimalSI}, Quantity{dec(50, 0), DecimalSI}}, - {Quantity{nil, DecimalSI}, Quantity{nil, DecimalSI}, Quantity{dec(0, 0), DecimalSI}}, + {decQuantity(10, 0, DecimalSI), decQuantity(1, 1, DecimalSI), decQuantity(0, 0, DecimalSI)}, + {decQuantity(10, 0, DecimalSI), decQuantity(1, 0, BinarySI), decQuantity(9, 0, DecimalSI)}, + {decQuantity(10, 0, BinarySI), decQuantity(1, 0, DecimalSI), decQuantity(9, 0, BinarySI)}, + {Quantity{Format: DecimalSI}, decQuantity(50, 0, DecimalSI), decQuantity(-50, 0, DecimalSI)}, + {decQuantity(50, 0, DecimalSI), Quantity{Format: DecimalSI}, decQuantity(50, 0, DecimalSI)}, + {Quantity{Format: DecimalSI}, Quantity{Format: DecimalSI}, decQuantity(0, 0, DecimalSI)}, } for i, test := range tests { @@ -738,52 +1009,21 @@ func TestNeg(t *testing.T) { b Quantity expected Quantity }{ - { - a: Quantity{dec(0, 0), DecimalSI}, - b: Quantity{dec(10, 0), DecimalSI}, - expected: Quantity{dec(-10, 0), DecimalSI}, - }, - { - a: Quantity{dec(0, 0), DecimalSI}, - b: Quantity{dec(-10, 0), DecimalSI}, - expected: Quantity{dec(10, 0), DecimalSI}, - }, - { - a: Quantity{dec(0, 0), DecimalSI}, - b: Quantity{dec(10, 0), BinarySI}, - expected: Quantity{dec(-10, 0), BinarySI}, - }, - { - a: Quantity{dec(0, 0), DecimalSI}, - b: Quantity{dec(0, 0), BinarySI}, - expected: Quantity{dec(0, 0), BinarySI}, - }, - { - a: Quantity{}, - b: Quantity{dec(10, 0), BinarySI}, - expected: Quantity{dec(-10, 0), BinarySI}, - }, - { - a: Quantity{dec(10, 0), BinarySI}, - b: Quantity{}, - expected: Quantity{}, - }, - { - a: Quantity{dec(10, 0), BinarySI}, - b: Quantity{Format: DecimalSI}, - expected: Quantity{dec(0, 0), DecimalSI}, - }, + {a: intQuantity(0, 0, DecimalSI), expected: intQuantity(0, 0, DecimalSI)}, + {a: Quantity{}, expected: Quantity{}}, + {a: intQuantity(10, 0, BinarySI), expected: intQuantity(-10, 0, BinarySI)}, + {a: intQuantity(-10, 0, BinarySI), expected: intQuantity(10, 0, BinarySI)}, + {a: decQuantity(0, 0, DecimalSI), expected: intQuantity(0, 0, DecimalSI)}, + {a: decQuantity(10, 0, BinarySI), expected: intQuantity(-10, 0, BinarySI)}, + {a: decQuantity(-10, 0, BinarySI), expected: intQuantity(10, 0, BinarySI)}, } for i, test := range tests { - test.a.Neg(test.b) + a := test.a.Copy() + a.Neg() // ensure value is same - if test.a.Cmp(test.expected) != 0 { - t.Errorf("[%d] Expected %q, got %q", i, test.expected.String(), test.a.String()) - } - // ensure format is updated - if test.a.Format != test.expected.Format { - t.Errorf("[%d] Expected format %v, got format %v", i, test.expected.Format, test.a.Format) + if a.Cmp(test.expected) != 0 { + t.Errorf("[%d] Expected %q, got %q", i, test.expected.String(), a.String()) } } } @@ -794,12 +1034,12 @@ func TestAdd(t *testing.T) { b Quantity expected Quantity }{ - {Quantity{dec(10, 0), DecimalSI}, Quantity{dec(1, 1), DecimalSI}, Quantity{dec(20, 0), DecimalSI}}, - {Quantity{dec(10, 0), DecimalSI}, Quantity{dec(1, 0), BinarySI}, Quantity{dec(11, 0), DecimalSI}}, - {Quantity{dec(10, 0), BinarySI}, Quantity{dec(1, 0), DecimalSI}, Quantity{dec(11, 0), BinarySI}}, - {Quantity{nil, DecimalSI}, Quantity{dec(50, 0), DecimalSI}, Quantity{dec(50, 0), DecimalSI}}, - {Quantity{dec(50, 0), DecimalSI}, Quantity{nil, DecimalSI}, Quantity{dec(50, 0), DecimalSI}}, - {Quantity{nil, DecimalSI}, Quantity{nil, DecimalSI}, Quantity{dec(0, 0), DecimalSI}}, + {decQuantity(10, 0, DecimalSI), decQuantity(1, 1, DecimalSI), decQuantity(20, 0, DecimalSI)}, + {decQuantity(10, 0, DecimalSI), decQuantity(1, 0, BinarySI), decQuantity(11, 0, DecimalSI)}, + {decQuantity(10, 0, BinarySI), decQuantity(1, 0, DecimalSI), decQuantity(11, 0, BinarySI)}, + {Quantity{Format: DecimalSI}, decQuantity(50, 0, DecimalSI), decQuantity(50, 0, DecimalSI)}, + {decQuantity(50, 0, DecimalSI), Quantity{Format: DecimalSI}, decQuantity(50, 0, DecimalSI)}, + {Quantity{Format: DecimalSI}, Quantity{Format: DecimalSI}, decQuantity(0, 0, DecimalSI)}, } for i, test := range tests { @@ -810,18 +1050,73 @@ func TestAdd(t *testing.T) { } } +func TestAddSubRoundTrip(t *testing.T) { + for k := -10; k <= 10; k++ { + q := Quantity{Format: DecimalSI} + var order []int64 + for i := 0; i < 100; i++ { + j := rand.Int63() + order = append(order, j) + q.Add(*NewScaledQuantity(j, Scale(k))) + } + for _, j := range order { + q.Sub(*NewScaledQuantity(j, Scale(k))) + } + if !q.IsZero() { + t.Errorf("addition and subtraction did not cancel: %s", &q) + } + } +} + +func TestAddSubRoundTripAcrossScales(t *testing.T) { + q := Quantity{Format: DecimalSI} + var order []int64 + for i := 0; i < 100; i++ { + j := rand.Int63() + order = append(order, j) + q.Add(*NewScaledQuantity(j, Scale(j%20-10))) + } + for _, j := range order { + q.Sub(*NewScaledQuantity(j, Scale(j%20-10))) + } + if !q.IsZero() { + t.Errorf("addition and subtraction did not cancel: %s", &q) + } +} + +func TestNegateRoundTrip(t *testing.T) { + for _, asDec := range []bool{false, true} { + for k := -10; k <= 10; k++ { + for i := 0; i < 100; i++ { + j := rand.Int63() + q := *NewScaledQuantity(j, Scale(k)) + if asDec { + q.AsDec() + } + + b := q.Copy() + b.Neg() + b.Neg() + if b.Cmp(q) != 0 { + t.Errorf("double negation did not cancel: %s", &q) + } + } + } + } +} func benchmarkQuantities() []Quantity { return []Quantity{ - {dec(1024*1024*1024, 0), BinarySI}, - {dec(1024*1024*1024*1024, 0), BinarySI}, - {dec(1000000, 3), DecimalSI}, - {dec(1000000000, 0), DecimalSI}, - {dec(1, -3), DecimalSI}, - {dec(80, -3), DecimalSI}, - {dec(1080, -3), DecimalSI}, - {dec(0, 0), BinarySI}, - {dec(1, 9), DecimalExponent}, - {dec(1, -9), DecimalSI}, + intQuantity(1024*1024*1024, 0, BinarySI), + intQuantity(1024*1024*1024*1024, 0, BinarySI), + intQuantity(1000000, 3, DecimalSI), + intQuantity(1000000000, 0, DecimalSI), + intQuantity(1, -3, DecimalSI), + intQuantity(80, -3, DecimalSI), + intQuantity(1080, -3, DecimalSI), + intQuantity(0, 0, BinarySI), + intQuantity(1, 9, DecimalExponent), + intQuantity(1, -9, DecimalSI), + intQuantity(1000000, 10, DecimalSI), } } @@ -830,7 +1125,27 @@ func BenchmarkQuantityString(b *testing.B) { b.ResetTimer() var s string for i := 0; i < b.N; i++ { - s = values[i%len(values)].String() + q := values[i%len(values)] + q.s = nil + s = q.String() + } + b.StopTimer() + if len(s) == 0 { + b.Fatal(s) + } +} + +func BenchmarkQuantityStringBinarySI(b *testing.B) { + values := benchmarkQuantities() + for i := range values { + values[i].Format = BinarySI + } + b.ResetTimer() + var s string + for i := 0; i < b.N; i++ { + q := values[i%len(values)] + q.s = nil + s = q.String() } b.StopTimer() if len(s) == 0 { @@ -842,7 +1157,9 @@ func BenchmarkQuantityMarshalJSON(b *testing.B) { values := benchmarkQuantities() b.ResetTimer() for i := 0; i < b.N; i++ { - if _, err := values[i%len(values)].MarshalJSON(); err != nil { + q := values[i%len(values)] + q.s = nil + if _, err := q.MarshalJSON(); err != nil { b.Fatal(err) } } @@ -885,8 +1202,9 @@ func BenchmarkParseQuantity(b *testing.B) { func BenchmarkCanonicalize(b *testing.B) { values := benchmarkQuantities() b.ResetTimer() + buffer := make([]byte, 0, 100) for i := 0; i < b.N; i++ { - s, _ := values[i%len(values)].Canonicalize() + s, _ := values[i%len(values)].CanonicalizeBytes(buffer) if len(s) == 0 { b.Fatal(s) } @@ -894,31 +1212,40 @@ func BenchmarkCanonicalize(b *testing.B) { b.StopTimer() } +func BenchmarkQuantityRoundUp(b *testing.B) { + values := benchmarkQuantities() + b.ResetTimer() + for i := 0; i < b.N; i++ { + q := values[i%len(values)] + copied := q + copied.RoundUp(-3) + } + b.StopTimer() +} + func BenchmarkQuantityCopy(b *testing.B) { values := benchmarkQuantities() b.ResetTimer() for i := 0; i < b.N; i++ { - q := values[i%len(values)].Copy() - if q.Amount == nil { - b.Fatal(q) - } + values[i%len(values)].Copy() } b.StopTimer() } func BenchmarkQuantityAdd(b *testing.B) { values := benchmarkQuantities() + base := &Quantity{} b.ResetTimer() for i := 0; i < b.N; i++ { q := values[i%len(values)] - if err := q.Add(q); err != nil { - b.Fatal(err) - } + base.d.Dec = nil + base.i = int64Amount{value: 100} + base.Add(q) } b.StopTimer() } -func BenchmarkQuantityRound(b *testing.B) { +func BenchmarkQuantityCmp(b *testing.B) { values := benchmarkQuantities() b.ResetTimer() for i := 0; i < b.N; i++ { diff --git a/pkg/api/resource/suffix.go b/pkg/api/resource/suffix.go index 529712365d7..0aa2ce2bf60 100644 --- a/pkg/api/resource/suffix.go +++ b/pkg/api/resource/suffix.go @@ -24,8 +24,9 @@ type suffix string // suffixer can interpret and construct suffixes. type suffixer interface { - interpret(suffix) (base, exponent int, fmt Format, ok bool) - construct(base, exponent int, fmt Format) (s suffix, ok bool) + interpret(suffix) (base, exponent int32, fmt Format, ok bool) + construct(base, exponent int32, fmt Format) (s suffix, ok bool) + constructBytes(base, exponent int32, fmt Format) (s []byte, ok bool) } // quantitySuffixer handles suffixes for all three formats that quantity @@ -33,12 +34,13 @@ type suffixer interface { var quantitySuffixer = newSuffixer() type bePair struct { - base, exponent int + base, exponent int32 } type listSuffixer struct { - suffixToBE map[suffix]bePair - beToSuffix map[bePair]suffix + suffixToBE map[suffix]bePair + beToSuffix map[bePair]suffix + beToSuffixBytes map[bePair][]byte } func (ls *listSuffixer) addSuffix(s suffix, pair bePair) { @@ -48,11 +50,15 @@ func (ls *listSuffixer) addSuffix(s suffix, pair bePair) { if ls.beToSuffix == nil { ls.beToSuffix = map[bePair]suffix{} } + if ls.beToSuffixBytes == nil { + ls.beToSuffixBytes = map[bePair][]byte{} + } ls.suffixToBE[s] = pair ls.beToSuffix[pair] = s + ls.beToSuffixBytes[pair] = []byte(s) } -func (ls *listSuffixer) lookup(s suffix) (base, exponent int, ok bool) { +func (ls *listSuffixer) lookup(s suffix) (base, exponent int32, ok bool) { pair, ok := ls.suffixToBE[s] if !ok { return 0, 0, false @@ -60,19 +66,50 @@ func (ls *listSuffixer) lookup(s suffix) (base, exponent int, ok bool) { return pair.base, pair.exponent, true } -func (ls *listSuffixer) construct(base, exponent int) (s suffix, ok bool) { +func (ls *listSuffixer) construct(base, exponent int32) (s suffix, ok bool) { s, ok = ls.beToSuffix[bePair{base, exponent}] return } +func (ls *listSuffixer) constructBytes(base, exponent int32) (s []byte, ok bool) { + s, ok = ls.beToSuffixBytes[bePair{base, exponent}] + return +} + type suffixHandler struct { decSuffixes listSuffixer binSuffixes listSuffixer } +type fastLookup struct { + *suffixHandler +} + +func (l fastLookup) interpret(s suffix) (base, exponent int32, format Format, ok bool) { + switch s { + case "": + return 10, 0, DecimalSI, true + case "n": + return 10, -9, DecimalSI, true + case "u": + return 10, -6, DecimalSI, true + case "m": + return 10, -3, DecimalSI, true + case "k": + return 10, 3, DecimalSI, true + case "M": + return 10, 6, DecimalSI, true + case "G": + return 10, 9, DecimalSI, true + } + return l.suffixHandler.interpret(s) +} + func newSuffixer() suffixer { sh := &suffixHandler{} + // IMPORTANT: if you change this section you must change fastLookup + sh.binSuffixes.addSuffix("Ki", bePair{2, 10}) sh.binSuffixes.addSuffix("Mi", bePair{2, 20}) sh.binSuffixes.addSuffix("Gi", bePair{2, 30}) @@ -94,10 +131,10 @@ func newSuffixer() suffixer { sh.decSuffixes.addSuffix("P", bePair{10, 15}) sh.decSuffixes.addSuffix("E", bePair{10, 18}) - return sh + return fastLookup{sh} } -func (sh *suffixHandler) construct(base, exponent int, fmt Format) (s suffix, ok bool) { +func (sh *suffixHandler) construct(base, exponent int32, fmt Format) (s suffix, ok bool) { switch fmt { case DecimalSI: return sh.decSuffixes.construct(base, exponent) @@ -115,7 +152,32 @@ func (sh *suffixHandler) construct(base, exponent int, fmt Format) (s suffix, ok return "", false } -func (sh *suffixHandler) interpret(suffix suffix) (base, exponent int, fmt Format, ok bool) { +func (sh *suffixHandler) constructBytes(base, exponent int32, format Format) (s []byte, ok bool) { + switch format { + case DecimalSI: + return sh.decSuffixes.constructBytes(base, exponent) + case BinarySI: + return sh.binSuffixes.constructBytes(base, exponent) + case DecimalExponent: + if base != 10 { + return nil, false + } + if exponent == 0 { + return nil, true + } + result := make([]byte, 8, 8) + result[0] = 'e' + number := strconv.AppendInt(result[1:1], int64(exponent), 10) + if &result[1] == &number[0] { + return result[:1+len(number)], true + } + result = append(result[:1], number...) + return result, true + } + return nil, false +} + +func (sh *suffixHandler) interpret(suffix suffix) (base, exponent int32, fmt Format, ok bool) { // Try lookup tables first if b, e, ok := sh.decSuffixes.lookup(suffix); ok { return b, e, DecimalSI, true @@ -129,7 +191,7 @@ func (sh *suffixHandler) interpret(suffix suffix) (base, exponent int, fmt Forma if err != nil { return 0, 0, DecimalExponent, false } - return 10, int(parsed), DecimalExponent, true + return 10, int32(parsed), DecimalExponent, true } return 0, 0, DecimalExponent, false diff --git a/pkg/api/resource_helpers.go b/pkg/api/resource_helpers.go index 74bc1720c93..2a664d8dc8a 100644 --- a/pkg/api/resource_helpers.go +++ b/pkg/api/resource_helpers.go @@ -151,15 +151,17 @@ func PodRequestsAndLimits(pod *Pod) (reqs map[ResourceName]resource.Quantity, li for name, quantity := range container.Resources.Requests { if value, ok := reqs[name]; !ok { reqs[name] = *quantity.Copy() - } else if err = value.Add(quantity); err != nil { - return nil, nil, err + } else { + value.Add(quantity) + reqs[name] = value } } for name, quantity := range container.Resources.Limits { if value, ok := limits[name]; !ok { limits[name] = *quantity.Copy() - } else if err = value.Add(quantity); err != nil { - return nil, nil, err + } else { + value.Add(quantity) + limits[name] = value } } } diff --git a/pkg/api/resource_helpers_test.go b/pkg/api/resource_helpers_test.go index 12c0ce7db65..d13929fa946 100644 --- a/pkg/api/resource_helpers_test.go +++ b/pkg/api/resource_helpers_test.go @@ -32,10 +32,10 @@ func TestResourceHelpers(t *testing.T) { "kube.io/storage": memoryLimit, }, } - if res := resourceSpec.Limits.Cpu(); *res != cpuLimit { + if res := resourceSpec.Limits.Cpu(); res.Cmp(cpuLimit) != 0 { t.Errorf("expected cpulimit %v, got %v", cpuLimit, res) } - if res := resourceSpec.Limits.Memory(); *res != memoryLimit { + if res := resourceSpec.Limits.Memory(); res.Cmp(memoryLimit) != 0 { t.Errorf("expected memorylimit %v, got %v", memoryLimit, res) } resourceSpec = ResourceRequirements{ @@ -47,7 +47,7 @@ func TestResourceHelpers(t *testing.T) { if res := resourceSpec.Limits.Cpu(); res.Value() != 0 { t.Errorf("expected cpulimit %v, got %v", 0, res) } - if res := resourceSpec.Limits.Memory(); *res != memoryLimit { + if res := resourceSpec.Limits.Memory(); res.Cmp(memoryLimit) != 0 { t.Errorf("expected memorylimit %v, got %v", memoryLimit, res) } } diff --git a/pkg/api/types.generated.go b/pkg/api/types.generated.go index 5632f87f097..0e46d2c4fe5 100644 --- a/pkg/api/types.generated.go +++ b/pkg/api/types.generated.go @@ -25,14 +25,13 @@ import ( "errors" "fmt" codec1978 "github.com/ugorji/go/codec" - pkg4_inf_v0 "gopkg.in/inf.v0" pkg3_resource "k8s.io/kubernetes/pkg/api/resource" pkg2_unversioned "k8s.io/kubernetes/pkg/api/unversioned" - pkg7_fields "k8s.io/kubernetes/pkg/fields" - pkg6_labels "k8s.io/kubernetes/pkg/labels" - pkg8_runtime "k8s.io/kubernetes/pkg/runtime" + pkg6_fields "k8s.io/kubernetes/pkg/fields" + pkg5_labels "k8s.io/kubernetes/pkg/labels" + pkg7_runtime "k8s.io/kubernetes/pkg/runtime" pkg1_types "k8s.io/kubernetes/pkg/types" - pkg5_intstr "k8s.io/kubernetes/pkg/util/intstr" + pkg4_intstr "k8s.io/kubernetes/pkg/util/intstr" "reflect" "runtime" time "time" @@ -68,16 +67,15 @@ func init() { panic(err) } if false { // reference the types, but skip this branch at build/run time - var v0 pkg4_inf_v0.Dec - var v1 pkg3_resource.Quantity - var v2 pkg2_unversioned.Time - var v3 pkg7_fields.Selector - var v4 pkg6_labels.Selector - var v5 pkg8_runtime.Object - var v6 pkg1_types.UID - var v7 pkg5_intstr.IntOrString - var v8 time.Time - _, _, _, _, _, _, _, _, _ = v0, v1, v2, v3, v4, v5, v6, v7, v8 + var v0 pkg3_resource.Quantity + var v1 pkg2_unversioned.Time + var v2 pkg6_fields.Selector + var v3 pkg5_labels.Selector + var v4 pkg7_runtime.Object + var v5 pkg1_types.UID + var v6 pkg4_intstr.IntOrString + var v7 time.Time + _, _, _, _, _, _, _, _ = v0, v1, v2, v3, v4, v5, v6, v7 } } @@ -16227,7 +16225,7 @@ func (x *HTTPGetAction) codecDecodeSelfFromMap(l int, d *codec1978.Decoder) { } case "port": if r.TryDecodeAsNil() { - x.Port = pkg5_intstr.IntOrString{} + x.Port = pkg4_intstr.IntOrString{} } else { yyv5 := &x.Port yym6 := z.DecBinary() @@ -16306,7 +16304,7 @@ func (x *HTTPGetAction) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) { } z.DecSendContainerState(codecSelfer_containerArrayElem1234) if r.TryDecodeAsNil() { - x.Port = pkg5_intstr.IntOrString{} + x.Port = pkg4_intstr.IntOrString{} } else { yyv13 := &x.Port yym14 := z.DecBinary() @@ -16542,7 +16540,7 @@ func (x *TCPSocketAction) codecDecodeSelfFromMap(l int, d *codec1978.Decoder) { switch yys3 { case "port": if r.TryDecodeAsNil() { - x.Port = pkg5_intstr.IntOrString{} + x.Port = pkg4_intstr.IntOrString{} } else { yyv4 := &x.Port yym5 := z.DecBinary() @@ -16581,7 +16579,7 @@ func (x *TCPSocketAction) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) } z.DecSendContainerState(codecSelfer_containerArrayElem1234) if r.TryDecodeAsNil() { - x.Port = pkg5_intstr.IntOrString{} + x.Port = pkg4_intstr.IntOrString{} } else { yyv7 := &x.Port yym8 := z.DecBinary() @@ -31535,7 +31533,7 @@ func (x *ServicePort) codecDecodeSelfFromMap(l int, d *codec1978.Decoder) { } case "targetPort": if r.TryDecodeAsNil() { - x.TargetPort = pkg5_intstr.IntOrString{} + x.TargetPort = pkg4_intstr.IntOrString{} } else { yyv7 := &x.TargetPort yym8 := z.DecBinary() @@ -31628,7 +31626,7 @@ func (x *ServicePort) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) { } z.DecSendContainerState(codecSelfer_containerArrayElem1234) if r.TryDecodeAsNil() { - x.TargetPort = pkg5_intstr.IntOrString{} + x.TargetPort = pkg4_intstr.IntOrString{} } else { yyv14 := &x.TargetPort yym15 := z.DecBinary() @@ -45477,7 +45475,7 @@ func (x *List) CodecEncodeSelf(e *codec1978.Encoder) { _ = yym9 if false { } else { - h.encSliceruntime_Object(([]pkg8_runtime.Object)(x.Items), e) + h.encSliceruntime_Object(([]pkg7_runtime.Object)(x.Items), e) } } } else { @@ -45491,7 +45489,7 @@ func (x *List) CodecEncodeSelf(e *codec1978.Encoder) { _ = yym10 if false { } else { - h.encSliceruntime_Object(([]pkg8_runtime.Object)(x.Items), e) + h.encSliceruntime_Object(([]pkg7_runtime.Object)(x.Items), e) } } } @@ -45628,7 +45626,7 @@ func (x *List) codecDecodeSelfFromMap(l int, d *codec1978.Decoder) { _ = yym7 if false { } else { - h.decSliceruntime_Object((*[]pkg8_runtime.Object)(yyv6), d) + h.decSliceruntime_Object((*[]pkg7_runtime.Object)(yyv6), d) } } case "kind": @@ -45699,7 +45697,7 @@ func (x *List) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) { _ = yym14 if false { } else { - h.decSliceruntime_Object((*[]pkg8_runtime.Object)(yyv13), d) + h.decSliceruntime_Object((*[]pkg7_runtime.Object)(yyv13), d) } } yyj10++ @@ -56094,7 +56092,7 @@ func (x codecSelfer1234) decResourceList(v *ResourceList, d *codec1978.Decoder) yyl1 := r.ReadMapStart() yybh1 := z.DecBasicHandle() if yyv1 == nil { - yyrl1, _ := z.DecInferLen(yyl1, yybh1.MaxInitLen, 40) + yyrl1, _ := z.DecInferLen(yyl1, yybh1.MaxInitLen, 80) yyv1 = make(map[ResourceName]pkg3_resource.Quantity, yyrl1) *v = yyv1 } @@ -56643,7 +56641,7 @@ func (x codecSelfer1234) decSliceEvent(v *[]Event, d *codec1978.Decoder) { } } -func (x codecSelfer1234) encSliceruntime_Object(v []pkg8_runtime.Object, e *codec1978.Encoder) { +func (x codecSelfer1234) encSliceruntime_Object(v []pkg7_runtime.Object, e *codec1978.Encoder) { var h codecSelfer1234 z, r := codec1978.GenHelperEncoder(e) _, _, _ = h, z, r @@ -56665,7 +56663,7 @@ func (x codecSelfer1234) encSliceruntime_Object(v []pkg8_runtime.Object, e *code z.EncSendContainerState(codecSelfer_containerArrayEnd1234) } -func (x codecSelfer1234) decSliceruntime_Object(v *[]pkg8_runtime.Object, d *codec1978.Decoder) { +func (x codecSelfer1234) decSliceruntime_Object(v *[]pkg7_runtime.Object, d *codec1978.Decoder) { var h codecSelfer1234 z, r := codec1978.GenHelperDecoder(d) _, _, _ = h, z, r @@ -56676,7 +56674,7 @@ func (x codecSelfer1234) decSliceruntime_Object(v *[]pkg8_runtime.Object, d *cod _ = yyc1 if yyl1 == 0 { if yyv1 == nil { - yyv1 = []pkg8_runtime.Object{} + yyv1 = []pkg7_runtime.Object{} yyc1 = true } else if len(yyv1) != 0 { yyv1 = yyv1[:0] @@ -56696,10 +56694,10 @@ func (x codecSelfer1234) decSliceruntime_Object(v *[]pkg8_runtime.Object, d *cod if yyrl1 <= cap(yyv1) { yyv1 = yyv1[:yyrl1] } else { - yyv1 = make([]pkg8_runtime.Object, yyrl1) + yyv1 = make([]pkg7_runtime.Object, yyrl1) } } else { - yyv1 = make([]pkg8_runtime.Object, yyrl1) + yyv1 = make([]pkg7_runtime.Object, yyrl1) } yyc1 = true yyrr1 = len(yyv1) @@ -56752,7 +56750,7 @@ func (x codecSelfer1234) decSliceruntime_Object(v *[]pkg8_runtime.Object, d *cod for ; !r.CheckBreak(); yyj1++ { if yyj1 >= len(yyv1) { - yyv1 = append(yyv1, nil) // var yyz1 pkg8_runtime.Object + yyv1 = append(yyv1, nil) // var yyz1 pkg7_runtime.Object yyc1 = true } yyh1.ElemContainerState(yyj1) @@ -56779,7 +56777,7 @@ func (x codecSelfer1234) decSliceruntime_Object(v *[]pkg8_runtime.Object, d *cod yyv1 = yyv1[:yyj1] yyc1 = true } else if yyj1 == 0 && yyv1 == nil { - yyv1 = []pkg8_runtime.Object{} + yyv1 = []pkg7_runtime.Object{} yyc1 = true } } diff --git a/pkg/api/v1/conversion.go b/pkg/api/v1/conversion.go index 7dc8f1e84da..77a53dade91 100644 --- a/pkg/api/v1/conversion.go +++ b/pkg/api/v1/conversion.go @@ -20,8 +20,6 @@ import ( "encoding/json" "fmt" - inf "gopkg.in/inf.v0" - "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/conversion" "k8s.io/kubernetes/pkg/runtime" @@ -670,8 +668,8 @@ func Convert_v1_ResourceList_To_api_ResourceList(in *ResourceList, out *api.Reso // TODO(#18538): We round up resource values to milli scale to maintain API compatibility. // In the future, we should instead reject values that need rounding. - const milliScale = 3 - value.Amount.Round(value.Amount, milliScale, inf.RoundUp) + const milliScale = -3 + value.RoundUp(milliScale) converted[api.ResourceName(key)] = *value } diff --git a/pkg/api/v1/types.generated.go b/pkg/api/v1/types.generated.go index f5e7f1531dc..7b9d953f7c5 100644 --- a/pkg/api/v1/types.generated.go +++ b/pkg/api/v1/types.generated.go @@ -25,12 +25,11 @@ import ( "errors" "fmt" codec1978 "github.com/ugorji/go/codec" - pkg4_inf_v0 "gopkg.in/inf.v0" pkg3_resource "k8s.io/kubernetes/pkg/api/resource" pkg2_unversioned "k8s.io/kubernetes/pkg/api/unversioned" - pkg6_runtime "k8s.io/kubernetes/pkg/runtime" + pkg5_runtime "k8s.io/kubernetes/pkg/runtime" pkg1_types "k8s.io/kubernetes/pkg/types" - pkg5_intstr "k8s.io/kubernetes/pkg/util/intstr" + pkg4_intstr "k8s.io/kubernetes/pkg/util/intstr" "reflect" "runtime" time "time" @@ -66,14 +65,13 @@ func init() { panic(err) } if false { // reference the types, but skip this branch at build/run time - var v0 pkg4_inf_v0.Dec - var v1 pkg3_resource.Quantity - var v2 pkg2_unversioned.Time - var v3 pkg6_runtime.RawExtension - var v4 pkg1_types.UID - var v5 pkg5_intstr.IntOrString - var v6 time.Time - _, _, _, _, _, _, _ = v0, v1, v2, v3, v4, v5, v6 + var v0 pkg3_resource.Quantity + var v1 pkg2_unversioned.Time + var v2 pkg5_runtime.RawExtension + var v3 pkg1_types.UID + var v4 pkg4_intstr.IntOrString + var v5 time.Time + _, _, _, _, _, _ = v0, v1, v2, v3, v4, v5 } } @@ -15844,7 +15842,7 @@ func (x *HTTPGetAction) codecDecodeSelfFromMap(l int, d *codec1978.Decoder) { } case "port": if r.TryDecodeAsNil() { - x.Port = pkg5_intstr.IntOrString{} + x.Port = pkg4_intstr.IntOrString{} } else { yyv5 := &x.Port yym6 := z.DecBinary() @@ -15923,7 +15921,7 @@ func (x *HTTPGetAction) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) { } z.DecSendContainerState(codecSelfer_containerArrayElem1234) if r.TryDecodeAsNil() { - x.Port = pkg5_intstr.IntOrString{} + x.Port = pkg4_intstr.IntOrString{} } else { yyv13 := &x.Port yym14 := z.DecBinary() @@ -16152,7 +16150,7 @@ func (x *TCPSocketAction) codecDecodeSelfFromMap(l int, d *codec1978.Decoder) { switch yys3 { case "port": if r.TryDecodeAsNil() { - x.Port = pkg5_intstr.IntOrString{} + x.Port = pkg4_intstr.IntOrString{} } else { yyv4 := &x.Port yym5 := z.DecBinary() @@ -16191,7 +16189,7 @@ func (x *TCPSocketAction) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) } z.DecSendContainerState(codecSelfer_containerArrayElem1234) if r.TryDecodeAsNil() { - x.Port = pkg5_intstr.IntOrString{} + x.Port = pkg4_intstr.IntOrString{} } else { yyv7 := &x.Port yym8 := z.DecBinary() @@ -31024,7 +31022,7 @@ func (x *ServicePort) codecDecodeSelfFromMap(l int, d *codec1978.Decoder) { } case "targetPort": if r.TryDecodeAsNil() { - x.TargetPort = pkg5_intstr.IntOrString{} + x.TargetPort = pkg4_intstr.IntOrString{} } else { yyv7 := &x.TargetPort yym8 := z.DecBinary() @@ -31117,7 +31115,7 @@ func (x *ServicePort) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) { } z.DecSendContainerState(codecSelfer_containerArrayElem1234) if r.TryDecodeAsNil() { - x.TargetPort = pkg5_intstr.IntOrString{} + x.TargetPort = pkg4_intstr.IntOrString{} } else { yyv14 := &x.TargetPort yym15 := z.DecBinary() @@ -45289,7 +45287,7 @@ func (x *List) CodecEncodeSelf(e *codec1978.Encoder) { _ = yym9 if false { } else { - h.encSliceruntime_RawExtension(([]pkg6_runtime.RawExtension)(x.Items), e) + h.encSliceruntime_RawExtension(([]pkg5_runtime.RawExtension)(x.Items), e) } } } else { @@ -45303,7 +45301,7 @@ func (x *List) CodecEncodeSelf(e *codec1978.Encoder) { _ = yym10 if false { } else { - h.encSliceruntime_RawExtension(([]pkg6_runtime.RawExtension)(x.Items), e) + h.encSliceruntime_RawExtension(([]pkg5_runtime.RawExtension)(x.Items), e) } } } @@ -45440,7 +45438,7 @@ func (x *List) codecDecodeSelfFromMap(l int, d *codec1978.Decoder) { _ = yym7 if false { } else { - h.decSliceruntime_RawExtension((*[]pkg6_runtime.RawExtension)(yyv6), d) + h.decSliceruntime_RawExtension((*[]pkg5_runtime.RawExtension)(yyv6), d) } } case "kind": @@ -45511,7 +45509,7 @@ func (x *List) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) { _ = yym14 if false { } else { - h.decSliceruntime_RawExtension((*[]pkg6_runtime.RawExtension)(yyv13), d) + h.decSliceruntime_RawExtension((*[]pkg5_runtime.RawExtension)(yyv13), d) } } yyj10++ @@ -56147,7 +56145,7 @@ func (x codecSelfer1234) decResourceList(v *ResourceList, d *codec1978.Decoder) yyl1 := r.ReadMapStart() yybh1 := z.DecBasicHandle() if yyv1 == nil { - yyrl1, _ := z.DecInferLen(yyl1, yybh1.MaxInitLen, 40) + yyrl1, _ := z.DecInferLen(yyl1, yybh1.MaxInitLen, 80) yyv1 = make(map[ResourceName]pkg3_resource.Quantity, yyrl1) *v = yyv1 } @@ -56696,7 +56694,7 @@ func (x codecSelfer1234) decSliceEvent(v *[]Event, d *codec1978.Decoder) { } } -func (x codecSelfer1234) encSliceruntime_RawExtension(v []pkg6_runtime.RawExtension, e *codec1978.Encoder) { +func (x codecSelfer1234) encSliceruntime_RawExtension(v []pkg5_runtime.RawExtension, e *codec1978.Encoder) { var h codecSelfer1234 z, r := codec1978.GenHelperEncoder(e) _, _, _ = h, z, r @@ -56717,7 +56715,7 @@ func (x codecSelfer1234) encSliceruntime_RawExtension(v []pkg6_runtime.RawExtens z.EncSendContainerState(codecSelfer_containerArrayEnd1234) } -func (x codecSelfer1234) decSliceruntime_RawExtension(v *[]pkg6_runtime.RawExtension, d *codec1978.Decoder) { +func (x codecSelfer1234) decSliceruntime_RawExtension(v *[]pkg5_runtime.RawExtension, d *codec1978.Decoder) { var h codecSelfer1234 z, r := codec1978.GenHelperDecoder(d) _, _, _ = h, z, r @@ -56728,7 +56726,7 @@ func (x codecSelfer1234) decSliceruntime_RawExtension(v *[]pkg6_runtime.RawExten _ = yyc1 if yyl1 == 0 { if yyv1 == nil { - yyv1 = []pkg6_runtime.RawExtension{} + yyv1 = []pkg5_runtime.RawExtension{} yyc1 = true } else if len(yyv1) != 0 { yyv1 = yyv1[:0] @@ -56748,10 +56746,10 @@ func (x codecSelfer1234) decSliceruntime_RawExtension(v *[]pkg6_runtime.RawExten if yyrl1 <= cap(yyv1) { yyv1 = yyv1[:yyrl1] } else { - yyv1 = make([]pkg6_runtime.RawExtension, yyrl1) + yyv1 = make([]pkg5_runtime.RawExtension, yyrl1) } } else { - yyv1 = make([]pkg6_runtime.RawExtension, yyrl1) + yyv1 = make([]pkg5_runtime.RawExtension, yyrl1) } yyc1 = true yyrr1 = len(yyv1) @@ -56766,7 +56764,7 @@ func (x codecSelfer1234) decSliceruntime_RawExtension(v *[]pkg6_runtime.RawExten for ; yyj1 < yyrr1; yyj1++ { yyh1.ElemContainerState(yyj1) if r.TryDecodeAsNil() { - yyv1[yyj1] = pkg6_runtime.RawExtension{} + yyv1[yyj1] = pkg5_runtime.RawExtension{} } else { yyv2 := &yyv1[yyj1] yym3 := z.DecBinary() @@ -56783,10 +56781,10 @@ func (x codecSelfer1234) decSliceruntime_RawExtension(v *[]pkg6_runtime.RawExten } if yyrt1 { for ; yyj1 < yyl1; yyj1++ { - yyv1 = append(yyv1, pkg6_runtime.RawExtension{}) + yyv1 = append(yyv1, pkg5_runtime.RawExtension{}) yyh1.ElemContainerState(yyj1) if r.TryDecodeAsNil() { - yyv1[yyj1] = pkg6_runtime.RawExtension{} + yyv1[yyj1] = pkg5_runtime.RawExtension{} } else { yyv4 := &yyv1[yyj1] yym5 := z.DecBinary() @@ -56808,13 +56806,13 @@ func (x codecSelfer1234) decSliceruntime_RawExtension(v *[]pkg6_runtime.RawExten for ; !r.CheckBreak(); yyj1++ { if yyj1 >= len(yyv1) { - yyv1 = append(yyv1, pkg6_runtime.RawExtension{}) // var yyz1 pkg6_runtime.RawExtension + yyv1 = append(yyv1, pkg5_runtime.RawExtension{}) // var yyz1 pkg5_runtime.RawExtension yyc1 = true } yyh1.ElemContainerState(yyj1) if yyj1 < len(yyv1) { if r.TryDecodeAsNil() { - yyv1[yyj1] = pkg6_runtime.RawExtension{} + yyv1[yyj1] = pkg5_runtime.RawExtension{} } else { yyv6 := &yyv1[yyj1] yym7 := z.DecBinary() @@ -56837,7 +56835,7 @@ func (x codecSelfer1234) decSliceruntime_RawExtension(v *[]pkg6_runtime.RawExten yyv1 = yyv1[:yyj1] yyc1 = true } else if yyj1 == 0 && yyv1 == nil { - yyv1 = []pkg6_runtime.RawExtension{} + yyv1 = []pkg5_runtime.RawExtension{} yyc1 = true } } diff --git a/pkg/apis/apps/types.generated.go b/pkg/apis/apps/types.generated.go index 50418724784..9903f8e1357 100644 --- a/pkg/apis/apps/types.generated.go +++ b/pkg/apis/apps/types.generated.go @@ -25,12 +25,11 @@ import ( "errors" "fmt" codec1978 "github.com/ugorji/go/codec" - pkg5_inf_v0 "gopkg.in/inf.v0" pkg2_api "k8s.io/kubernetes/pkg/api" pkg4_resource "k8s.io/kubernetes/pkg/api/resource" pkg1_unversioned "k8s.io/kubernetes/pkg/api/unversioned" pkg3_types "k8s.io/kubernetes/pkg/types" - pkg6_intstr "k8s.io/kubernetes/pkg/util/intstr" + pkg5_intstr "k8s.io/kubernetes/pkg/util/intstr" "reflect" "runtime" time "time" @@ -66,14 +65,13 @@ func init() { panic(err) } if false { // reference the types, but skip this branch at build/run time - var v0 pkg5_inf_v0.Dec - var v1 pkg2_api.ObjectMeta - var v2 pkg4_resource.Quantity - var v3 pkg1_unversioned.TypeMeta - var v4 pkg3_types.UID - var v5 pkg6_intstr.IntOrString - var v6 time.Time - _, _, _, _, _, _, _ = v0, v1, v2, v3, v4, v5, v6 + var v0 pkg2_api.ObjectMeta + var v1 pkg4_resource.Quantity + var v2 pkg1_unversioned.TypeMeta + var v3 pkg3_types.UID + var v4 pkg5_intstr.IntOrString + var v5 time.Time + _, _, _, _, _, _ = v0, v1, v2, v3, v4, v5 } } diff --git a/pkg/apis/apps/v1alpha1/types.generated.go b/pkg/apis/apps/v1alpha1/types.generated.go index 38edac9e59b..a548f53133b 100644 --- a/pkg/apis/apps/v1alpha1/types.generated.go +++ b/pkg/apis/apps/v1alpha1/types.generated.go @@ -25,12 +25,11 @@ import ( "errors" "fmt" codec1978 "github.com/ugorji/go/codec" - pkg5_inf_v0 "gopkg.in/inf.v0" pkg4_resource "k8s.io/kubernetes/pkg/api/resource" pkg1_unversioned "k8s.io/kubernetes/pkg/api/unversioned" pkg2_v1 "k8s.io/kubernetes/pkg/api/v1" pkg3_types "k8s.io/kubernetes/pkg/types" - pkg6_intstr "k8s.io/kubernetes/pkg/util/intstr" + pkg5_intstr "k8s.io/kubernetes/pkg/util/intstr" "reflect" "runtime" time "time" @@ -66,14 +65,13 @@ func init() { panic(err) } if false { // reference the types, but skip this branch at build/run time - var v0 pkg5_inf_v0.Dec - var v1 pkg4_resource.Quantity - var v2 pkg1_unversioned.TypeMeta - var v3 pkg2_v1.ObjectMeta - var v4 pkg3_types.UID - var v5 pkg6_intstr.IntOrString - var v6 time.Time - _, _, _, _, _, _, _ = v0, v1, v2, v3, v4, v5, v6 + var v0 pkg4_resource.Quantity + var v1 pkg1_unversioned.TypeMeta + var v2 pkg2_v1.ObjectMeta + var v3 pkg3_types.UID + var v4 pkg5_intstr.IntOrString + var v5 time.Time + _, _, _, _, _, _ = v0, v1, v2, v3, v4, v5 } } diff --git a/pkg/apis/batch/types.generated.go b/pkg/apis/batch/types.generated.go index 01589cda732..68605cc67ad 100644 --- a/pkg/apis/batch/types.generated.go +++ b/pkg/apis/batch/types.generated.go @@ -25,12 +25,11 @@ import ( "errors" "fmt" codec1978 "github.com/ugorji/go/codec" - pkg5_inf_v0 "gopkg.in/inf.v0" pkg2_api "k8s.io/kubernetes/pkg/api" pkg4_resource "k8s.io/kubernetes/pkg/api/resource" pkg1_unversioned "k8s.io/kubernetes/pkg/api/unversioned" pkg3_types "k8s.io/kubernetes/pkg/types" - pkg6_intstr "k8s.io/kubernetes/pkg/util/intstr" + pkg5_intstr "k8s.io/kubernetes/pkg/util/intstr" "reflect" "runtime" time "time" @@ -66,14 +65,13 @@ func init() { panic(err) } if false { // reference the types, but skip this branch at build/run time - var v0 pkg5_inf_v0.Dec - var v1 pkg2_api.ObjectMeta - var v2 pkg4_resource.Quantity - var v3 pkg1_unversioned.TypeMeta - var v4 pkg3_types.UID - var v5 pkg6_intstr.IntOrString - var v6 time.Time - _, _, _, _, _, _, _ = v0, v1, v2, v3, v4, v5, v6 + var v0 pkg2_api.ObjectMeta + var v1 pkg4_resource.Quantity + var v2 pkg1_unversioned.TypeMeta + var v3 pkg3_types.UID + var v4 pkg5_intstr.IntOrString + var v5 time.Time + _, _, _, _, _, _ = v0, v1, v2, v3, v4, v5 } } diff --git a/pkg/apis/batch/v1/types.generated.go b/pkg/apis/batch/v1/types.generated.go index b305bb42bb9..58d5f6f54c7 100644 --- a/pkg/apis/batch/v1/types.generated.go +++ b/pkg/apis/batch/v1/types.generated.go @@ -25,12 +25,11 @@ import ( "errors" "fmt" codec1978 "github.com/ugorji/go/codec" - pkg5_inf_v0 "gopkg.in/inf.v0" pkg4_resource "k8s.io/kubernetes/pkg/api/resource" pkg1_unversioned "k8s.io/kubernetes/pkg/api/unversioned" pkg2_v1 "k8s.io/kubernetes/pkg/api/v1" pkg3_types "k8s.io/kubernetes/pkg/types" - pkg6_intstr "k8s.io/kubernetes/pkg/util/intstr" + pkg5_intstr "k8s.io/kubernetes/pkg/util/intstr" "reflect" "runtime" time "time" @@ -66,14 +65,13 @@ func init() { panic(err) } if false { // reference the types, but skip this branch at build/run time - var v0 pkg5_inf_v0.Dec - var v1 pkg4_resource.Quantity - var v2 pkg1_unversioned.TypeMeta - var v3 pkg2_v1.ObjectMeta - var v4 pkg3_types.UID - var v5 pkg6_intstr.IntOrString - var v6 time.Time - _, _, _, _, _, _, _ = v0, v1, v2, v3, v4, v5, v6 + var v0 pkg4_resource.Quantity + var v1 pkg1_unversioned.TypeMeta + var v2 pkg2_v1.ObjectMeta + var v3 pkg3_types.UID + var v4 pkg5_intstr.IntOrString + var v5 time.Time + _, _, _, _, _, _ = v0, v1, v2, v3, v4, v5 } } diff --git a/pkg/apis/batch/v2alpha1/types.generated.go b/pkg/apis/batch/v2alpha1/types.generated.go index f1369d6893b..239ae8c2c0d 100644 --- a/pkg/apis/batch/v2alpha1/types.generated.go +++ b/pkg/apis/batch/v2alpha1/types.generated.go @@ -25,12 +25,11 @@ import ( "errors" "fmt" codec1978 "github.com/ugorji/go/codec" - pkg5_inf_v0 "gopkg.in/inf.v0" pkg4_resource "k8s.io/kubernetes/pkg/api/resource" pkg1_unversioned "k8s.io/kubernetes/pkg/api/unversioned" pkg2_v1 "k8s.io/kubernetes/pkg/api/v1" pkg3_types "k8s.io/kubernetes/pkg/types" - pkg6_intstr "k8s.io/kubernetes/pkg/util/intstr" + pkg5_intstr "k8s.io/kubernetes/pkg/util/intstr" "reflect" "runtime" time "time" @@ -66,14 +65,13 @@ func init() { panic(err) } if false { // reference the types, but skip this branch at build/run time - var v0 pkg5_inf_v0.Dec - var v1 pkg4_resource.Quantity - var v2 pkg1_unversioned.TypeMeta - var v3 pkg2_v1.ObjectMeta - var v4 pkg3_types.UID - var v5 pkg6_intstr.IntOrString - var v6 time.Time - _, _, _, _, _, _, _ = v0, v1, v2, v3, v4, v5, v6 + var v0 pkg4_resource.Quantity + var v1 pkg1_unversioned.TypeMeta + var v2 pkg2_v1.ObjectMeta + var v3 pkg3_types.UID + var v4 pkg5_intstr.IntOrString + var v5 time.Time + _, _, _, _, _, _ = v0, v1, v2, v3, v4, v5 } } diff --git a/pkg/apis/extensions/types.generated.go b/pkg/apis/extensions/types.generated.go index f5b42e377f4..ee8155f7655 100644 --- a/pkg/apis/extensions/types.generated.go +++ b/pkg/apis/extensions/types.generated.go @@ -25,12 +25,11 @@ import ( "errors" "fmt" codec1978 "github.com/ugorji/go/codec" - pkg5_inf_v0 "gopkg.in/inf.v0" pkg2_api "k8s.io/kubernetes/pkg/api" pkg4_resource "k8s.io/kubernetes/pkg/api/resource" pkg1_unversioned "k8s.io/kubernetes/pkg/api/unversioned" pkg3_types "k8s.io/kubernetes/pkg/types" - pkg6_intstr "k8s.io/kubernetes/pkg/util/intstr" + pkg5_intstr "k8s.io/kubernetes/pkg/util/intstr" "reflect" "runtime" time "time" @@ -66,14 +65,13 @@ func init() { panic(err) } if false { // reference the types, but skip this branch at build/run time - var v0 pkg5_inf_v0.Dec - var v1 pkg2_api.ObjectMeta - var v2 pkg4_resource.Quantity - var v3 pkg1_unversioned.LabelSelector - var v4 pkg3_types.UID - var v5 pkg6_intstr.IntOrString - var v6 time.Time - _, _, _, _, _, _, _ = v0, v1, v2, v3, v4, v5, v6 + var v0 pkg2_api.ObjectMeta + var v1 pkg4_resource.Quantity + var v2 pkg1_unversioned.LabelSelector + var v3 pkg3_types.UID + var v4 pkg5_intstr.IntOrString + var v5 time.Time + _, _, _, _, _, _ = v0, v1, v2, v3, v4, v5 } } @@ -4916,7 +4914,7 @@ func (x *RollingUpdateDeployment) codecDecodeSelfFromMap(l int, d *codec1978.Dec switch yys3 { case "maxUnavailable": if r.TryDecodeAsNil() { - x.MaxUnavailable = pkg6_intstr.IntOrString{} + x.MaxUnavailable = pkg5_intstr.IntOrString{} } else { yyv4 := &x.MaxUnavailable yym5 := z.DecBinary() @@ -4931,7 +4929,7 @@ func (x *RollingUpdateDeployment) codecDecodeSelfFromMap(l int, d *codec1978.Dec } case "maxSurge": if r.TryDecodeAsNil() { - x.MaxSurge = pkg6_intstr.IntOrString{} + x.MaxSurge = pkg5_intstr.IntOrString{} } else { yyv6 := &x.MaxSurge yym7 := z.DecBinary() @@ -4970,7 +4968,7 @@ func (x *RollingUpdateDeployment) codecDecodeSelfFromArray(l int, d *codec1978.D } z.DecSendContainerState(codecSelfer_containerArrayElem1234) if r.TryDecodeAsNil() { - x.MaxUnavailable = pkg6_intstr.IntOrString{} + x.MaxUnavailable = pkg5_intstr.IntOrString{} } else { yyv9 := &x.MaxUnavailable yym10 := z.DecBinary() @@ -4995,7 +4993,7 @@ func (x *RollingUpdateDeployment) codecDecodeSelfFromArray(l int, d *codec1978.D } z.DecSendContainerState(codecSelfer_containerArrayElem1234) if r.TryDecodeAsNil() { - x.MaxSurge = pkg6_intstr.IntOrString{} + x.MaxSurge = pkg5_intstr.IntOrString{} } else { yyv11 := &x.MaxSurge yym12 := z.DecBinary() @@ -9593,7 +9591,7 @@ func (x *IngressBackend) codecDecodeSelfFromMap(l int, d *codec1978.Decoder) { } case "servicePort": if r.TryDecodeAsNil() { - x.ServicePort = pkg6_intstr.IntOrString{} + x.ServicePort = pkg5_intstr.IntOrString{} } else { yyv5 := &x.ServicePort yym6 := z.DecBinary() @@ -9648,7 +9646,7 @@ func (x *IngressBackend) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) { } z.DecSendContainerState(codecSelfer_containerArrayElem1234) if r.TryDecodeAsNil() { - x.ServicePort = pkg6_intstr.IntOrString{} + x.ServicePort = pkg5_intstr.IntOrString{} } else { yyv9 := &x.ServicePort yym10 := z.DecBinary() @@ -13849,7 +13847,7 @@ func (x codecSelfer1234) decSliceCustomMetricTarget(v *[]CustomMetricTarget, d * yyrg1 := len(yyv1) > 0 yyv21 := yyv1 - yyrl1, yyrt1 = z.DecInferLen(yyl1, z.DecBasicHandle().MaxInitLen, 40) + yyrl1, yyrt1 = z.DecInferLen(yyl1, z.DecBasicHandle().MaxInitLen, 80) if yyrt1 { if yyrl1 <= cap(yyv1) { yyv1 = yyv1[:yyrl1] @@ -13968,7 +13966,7 @@ func (x codecSelfer1234) decSliceCustomMetricCurrentStatus(v *[]CustomMetricCurr yyrg1 := len(yyv1) > 0 yyv21 := yyv1 - yyrl1, yyrt1 = z.DecInferLen(yyl1, z.DecBasicHandle().MaxInitLen, 40) + yyrl1, yyrt1 = z.DecInferLen(yyl1, z.DecBasicHandle().MaxInitLen, 80) if yyrt1 { if yyrl1 <= cap(yyv1) { yyv1 = yyv1[:yyrl1] diff --git a/pkg/apis/extensions/v1beta1/types.generated.go b/pkg/apis/extensions/v1beta1/types.generated.go index 84e3d95850d..e647fa7ae98 100644 --- a/pkg/apis/extensions/v1beta1/types.generated.go +++ b/pkg/apis/extensions/v1beta1/types.generated.go @@ -25,12 +25,11 @@ import ( "errors" "fmt" codec1978 "github.com/ugorji/go/codec" - pkg5_inf_v0 "gopkg.in/inf.v0" pkg4_resource "k8s.io/kubernetes/pkg/api/resource" pkg1_unversioned "k8s.io/kubernetes/pkg/api/unversioned" pkg2_v1 "k8s.io/kubernetes/pkg/api/v1" pkg3_types "k8s.io/kubernetes/pkg/types" - pkg6_intstr "k8s.io/kubernetes/pkg/util/intstr" + pkg5_intstr "k8s.io/kubernetes/pkg/util/intstr" "reflect" "runtime" time "time" @@ -66,14 +65,13 @@ func init() { panic(err) } if false { // reference the types, but skip this branch at build/run time - var v0 pkg5_inf_v0.Dec - var v1 pkg4_resource.Quantity - var v2 pkg1_unversioned.TypeMeta - var v3 pkg2_v1.ObjectMeta - var v4 pkg3_types.UID - var v5 pkg6_intstr.IntOrString - var v6 time.Time - _, _, _, _, _, _, _ = v0, v1, v2, v3, v4, v5, v6 + var v0 pkg4_resource.Quantity + var v1 pkg1_unversioned.TypeMeta + var v2 pkg2_v1.ObjectMeta + var v3 pkg3_types.UID + var v4 pkg5_intstr.IntOrString + var v5 time.Time + _, _, _, _, _, _ = v0, v1, v2, v3, v4, v5 } } @@ -6923,7 +6921,7 @@ func (x *RollingUpdateDeployment) codecDecodeSelfFromMap(l int, d *codec1978.Dec } } else { if x.MaxUnavailable == nil { - x.MaxUnavailable = new(pkg6_intstr.IntOrString) + x.MaxUnavailable = new(pkg5_intstr.IntOrString) } yym5 := z.DecBinary() _ = yym5 @@ -6942,7 +6940,7 @@ func (x *RollingUpdateDeployment) codecDecodeSelfFromMap(l int, d *codec1978.Dec } } else { if x.MaxSurge == nil { - x.MaxSurge = new(pkg6_intstr.IntOrString) + x.MaxSurge = new(pkg5_intstr.IntOrString) } yym7 := z.DecBinary() _ = yym7 @@ -6985,7 +6983,7 @@ func (x *RollingUpdateDeployment) codecDecodeSelfFromArray(l int, d *codec1978.D } } else { if x.MaxUnavailable == nil { - x.MaxUnavailable = new(pkg6_intstr.IntOrString) + x.MaxUnavailable = new(pkg5_intstr.IntOrString) } yym10 := z.DecBinary() _ = yym10 @@ -7014,7 +7012,7 @@ func (x *RollingUpdateDeployment) codecDecodeSelfFromArray(l int, d *codec1978.D } } else { if x.MaxSurge == nil { - x.MaxSurge = new(pkg6_intstr.IntOrString) + x.MaxSurge = new(pkg5_intstr.IntOrString) } yym12 := z.DecBinary() _ = yym12 @@ -13794,7 +13792,7 @@ func (x *IngressBackend) codecDecodeSelfFromMap(l int, d *codec1978.Decoder) { } case "servicePort": if r.TryDecodeAsNil() { - x.ServicePort = pkg6_intstr.IntOrString{} + x.ServicePort = pkg5_intstr.IntOrString{} } else { yyv5 := &x.ServicePort yym6 := z.DecBinary() @@ -13849,7 +13847,7 @@ func (x *IngressBackend) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) { } z.DecSendContainerState(codecSelfer_containerArrayElem1234) if r.TryDecodeAsNil() { - x.ServicePort = pkg6_intstr.IntOrString{} + x.ServicePort = pkg5_intstr.IntOrString{} } else { yyv9 := &x.ServicePort yym10 := z.DecBinary() @@ -19393,7 +19391,7 @@ func (x codecSelfer1234) decSliceCustomMetricTarget(v *[]CustomMetricTarget, d * yyrg1 := len(yyv1) > 0 yyv21 := yyv1 - yyrl1, yyrt1 = z.DecInferLen(yyl1, z.DecBasicHandle().MaxInitLen, 40) + yyrl1, yyrt1 = z.DecInferLen(yyl1, z.DecBasicHandle().MaxInitLen, 80) if yyrt1 { if yyrl1 <= cap(yyv1) { yyv1 = yyv1[:yyrl1] @@ -19512,7 +19510,7 @@ func (x codecSelfer1234) decSliceCustomMetricCurrentStatus(v *[]CustomMetricCurr yyrg1 := len(yyv1) > 0 yyv21 := yyv1 - yyrl1, yyrt1 = z.DecInferLen(yyl1, z.DecBasicHandle().MaxInitLen, 40) + yyrl1, yyrt1 = z.DecInferLen(yyl1, z.DecBasicHandle().MaxInitLen, 80) if yyrt1 { if yyrl1 <= cap(yyv1) { yyv1 = yyv1[:yyrl1] diff --git a/pkg/controller/petset/fakes.go b/pkg/controller/petset/fakes.go index 04e3e99050d..6c1c8e713f4 100644 --- a/pkg/controller/petset/fakes.go +++ b/pkg/controller/petset/fakes.go @@ -44,10 +44,7 @@ func newPVC(name string) api.PersistentVolumeClaim { Spec: api.PersistentVolumeClaimSpec{ Resources: api.ResourceRequirements{ Requests: api.ResourceList{ - api.ResourceStorage: resource.Quantity{ - Amount: dec(1, 0), - Format: resource.BinarySI, - }, + api.ResourceStorage: *resource.NewQuantity(1, resource.BinarySI), }, }, }, diff --git a/pkg/controller/podautoscaler/horizontal.go b/pkg/controller/podautoscaler/horizontal.go index a8e092cafe9..2ca083ff20d 100644 --- a/pkg/controller/podautoscaler/horizontal.go +++ b/pkg/controller/podautoscaler/horizontal.go @@ -237,7 +237,7 @@ func (a *HorizontalController) computeReplicasForCustomMetrics(hpa *autoscaling. } statusList.Items = append(statusList.Items, extensions.CustomMetricCurrentStatus{ Name: customMetricTarget.Name, - CurrentValue: *quantity, + CurrentValue: quantity, }) } byteStatusList, err := json.Marshal(statusList) diff --git a/pkg/controller/podautoscaler/metrics/metrics_client.go b/pkg/controller/podautoscaler/metrics/metrics_client.go index 1d70f5f205b..e115f285f89 100644 --- a/pkg/controller/podautoscaler/metrics/metrics_client.go +++ b/pkg/controller/podautoscaler/metrics/metrics_client.go @@ -129,8 +129,7 @@ func (h *HeapsterMetricsClient) GetCpuConsumptionAndRequestInMillis(namespace st podNames = append(podNames, pod.Name) for _, container := range pod.Spec.Containers { - containerRequest := container.Resources.Requests[api.ResourceCPU] - if containerRequest.Amount != nil { + if containerRequest, ok := container.Resources.Requests[api.ResourceCPU]; ok { requestSum += containerRequest.MilliValue() } else { missing = true diff --git a/pkg/kubectl/describe.go b/pkg/kubectl/describe.go index d8a7f39be67..ae13fa56626 100644 --- a/pkg/kubectl/describe.go +++ b/pkg/kubectl/describe.go @@ -1841,15 +1841,17 @@ func getPodsTotalRequestsAndLimits(podList *api.PodList) (reqs map[api.ResourceN for podReqName, podReqValue := range podReqs { if value, ok := reqs[podReqName]; !ok { reqs[podReqName] = *podReqValue.Copy() - } else if err = value.Add(podReqValue); err != nil { - return nil, nil, err + } else { + value.Add(podReqValue) + reqs[podReqName] = value } } for podLimitName, podLimitValue := range podLimits { if value, ok := limits[podLimitName]; !ok { limits[podLimitName] = *podLimitValue.Copy() - } else if err = value.Add(podLimitValue); err != nil { - return nil, nil, err + } else { + value.Add(podLimitValue) + limits[podLimitName] = value } } } diff --git a/pkg/kubectl/describe_test.go b/pkg/kubectl/describe_test.go index fc7358cf8f2..ee8d99cf700 100644 --- a/pkg/kubectl/describe_test.go +++ b/pkg/kubectl/describe_test.go @@ -431,7 +431,7 @@ func TestGetPodsTotalRequests(t *testing.T) { if err != nil { t.Errorf("Unexpected error %v", err) } - if !reflect.DeepEqual(reqs, testCase.expectedReqs) { + if !api.Semantic.DeepEqual(reqs, testCase.expectedReqs) { t.Errorf("Expected %v, got %v", testCase.expectedReqs, reqs) } } diff --git a/pkg/kubectl/run.go b/pkg/kubectl/run.go index f24641e120a..19a732c7e60 100644 --- a/pkg/kubectl/run.go +++ b/pkg/kubectl/run.go @@ -434,7 +434,7 @@ func populateResourceList(spec string) (api.ResourceList, error) { if err != nil { return nil, err } - result[resourceName] = *resourceQuantity + result[resourceName] = resourceQuantity } return result, nil } @@ -458,7 +458,7 @@ func populateV1ResourceList(spec string) (v1.ResourceList, error) { if err != nil { return nil, err } - result[resourceName] = *resourceQuantity + result[resourceName] = resourceQuantity } return result, nil } diff --git a/pkg/kubectl/run_test.go b/pkg/kubectl/run_test.go index b211fc363e9..88238a8fb05 100644 --- a/pkg/kubectl/run_test.go +++ b/pkg/kubectl/run_test.go @@ -378,10 +378,12 @@ func TestGenerate(t *testing.T) { }, } generator := BasicReplicationController{} - for _, test := range tests { + for i, test := range tests { obj, err := generator.Generate(test.params) + t.Logf("%d: %#v", i, obj) if !test.expectErr && err != nil { t.Errorf("unexpected error: %v", err) + continue } if test.expectErr && err != nil { continue diff --git a/pkg/kubelet/dockertools/manager.go b/pkg/kubelet/dockertools/manager.go index 10ac9e281f3..3c854651074 100644 --- a/pkg/kubelet/dockertools/manager.go +++ b/pkg/kubelet/dockertools/manager.go @@ -581,7 +581,7 @@ func (dm *DockerManager) runContainer( // If request is not specified, but limit is, we want request to default to limit. // API server does this for new containers, but we repeat this logic in Kubelet // for containers running on existing Kubernetes clusters. - if cpuRequest.Amount == nil && cpuLimit.Amount != nil { + if cpuRequest.IsZero() && !cpuLimit.IsZero() { cpuShares = milliCPUToShares(cpuLimit.MilliValue()) } else { // if cpuRequest.Amount is nil, then milliCPUToShares will return the minimal number diff --git a/pkg/kubelet/eviction/helpers.go b/pkg/kubelet/eviction/helpers.go index e7077be1a16..7a0e3b2f4c0 100644 --- a/pkg/kubelet/eviction/helpers.go +++ b/pkg/kubelet/eviction/helpers.go @@ -148,7 +148,7 @@ func parseThresholdStatement(statement string) (Threshold, error) { return Threshold{ Signal: signal, Operator: operator, - Value: *quantity, + Value: &quantity, }, nil } @@ -213,14 +213,10 @@ func podUsage(podStats statsapi.PodStats) (api.ResourceList, error) { // disk usage (if known) // TODO: need to handle volumes for _, fsStats := range []*statsapi.FsStats{container.Rootfs, container.Logs} { - if err := disk.Add(*diskUsage(fsStats)); err != nil { - return nil, err - } + disk.Add(*diskUsage(fsStats)) } // memory usage (if known) - if err := memory.Add(*memoryUsage(container.Memory)); err != nil { - return nil, err - } + memory.Add(*memoryUsage(container.Memory)) } return api.ResourceList{ api.ResourceMemory: memory, @@ -430,7 +426,7 @@ func makeSignalObservations(summaryProvider stats.SummaryProvider) (signalObserv statsFunc := cachedStatsFunc(summary.Pods) // build an evaluation context for current eviction signals result := signalObservations{} - result[SignalMemoryAvailable] = *resource.NewQuantity(int64(*summary.Node.Memory.AvailableBytes), resource.BinarySI) + result[SignalMemoryAvailable] = resource.NewQuantity(int64(*summary.Node.Memory.AvailableBytes), resource.BinarySI) return result, statsFunc, nil } @@ -446,7 +442,7 @@ func thresholdsMet(thresholds []Threshold, observations signalObservations) []Th } // determine if we have met the specified threshold thresholdMet := false - thresholdResult := threshold.Value.Cmp(observed) + thresholdResult := threshold.Value.Cmp(*observed) switch threshold.Operator { case OpLessThan: thresholdMet = thresholdResult > 0 @@ -538,7 +534,7 @@ func hasNodeCondition(inputs []api.NodeConditionType, item api.NodeConditionType // hasThreshold returns true if the node condition is in the input list func hasThreshold(inputs []Threshold, item Threshold) bool { for _, input := range inputs { - if input.GracePeriod == item.GracePeriod && input.Operator == item.Operator && input.Signal == item.Signal && input.Value.Cmp(item.Value) == 0 { + if input.GracePeriod == item.GracePeriod && input.Operator == item.Operator && input.Signal == item.Signal && input.Value.Cmp(*item.Value) == 0 { return true } } diff --git a/pkg/kubelet/eviction/helpers_test.go b/pkg/kubelet/eviction/helpers_test.go index b908b734652..6d412c9d1c7 100644 --- a/pkg/kubelet/eviction/helpers_test.go +++ b/pkg/kubelet/eviction/helpers_test.go @@ -30,6 +30,11 @@ import ( "k8s.io/kubernetes/pkg/types" ) +func quantityMustParse(value string) *resource.Quantity { + q := resource.MustParse(value) + return &q +} + func TestParseThresholdConfig(t *testing.T) { gracePeriod, _ := time.ParseDuration("30s") testCases := map[string]struct { @@ -55,12 +60,12 @@ func TestParseThresholdConfig(t *testing.T) { { Signal: SignalMemoryAvailable, Operator: OpLessThan, - Value: resource.MustParse("150Mi"), + Value: quantityMustParse("150Mi"), }, { Signal: SignalMemoryAvailable, Operator: OpLessThan, - Value: resource.MustParse("300Mi"), + Value: quantityMustParse("300Mi"), GracePeriod: gracePeriod, }, }, @@ -145,7 +150,7 @@ func thresholdEqual(a Threshold, b Threshold) bool { return a.GracePeriod == b.GracePeriod && a.Operator == b.Operator && a.Signal == b.Signal && - a.Value.Cmp(b.Value) == 0 + a.Value.Cmp(*b.Value) == 0 } // TestOrderedByQoS ensures we order BestEffort < Burstable < Guaranteed @@ -348,7 +353,7 @@ func TestThresholdsMet(t *testing.T) { hardThreshold := Threshold{ Signal: SignalMemoryAvailable, Operator: OpLessThan, - Value: resource.MustParse("1Gi"), + Value: quantityMustParse("1Gi"), } testCases := map[string]struct { thresholds []Threshold @@ -363,14 +368,14 @@ func TestThresholdsMet(t *testing.T) { "threshold-met": { thresholds: []Threshold{hardThreshold}, observations: signalObservations{ - SignalMemoryAvailable: resource.MustParse("500Mi"), + SignalMemoryAvailable: quantityMustParse("500Mi"), }, result: []Threshold{hardThreshold}, }, "threshold-not-met": { thresholds: []Threshold{hardThreshold}, observations: signalObservations{ - SignalMemoryAvailable: resource.MustParse("2Gi"), + SignalMemoryAvailable: quantityMustParse("2Gi"), }, result: []Threshold{}, }, @@ -387,7 +392,7 @@ func TestThresholdsFirstObservedAt(t *testing.T) { hardThreshold := Threshold{ Signal: SignalMemoryAvailable, Operator: OpLessThan, - Value: resource.MustParse("1Gi"), + Value: quantityMustParse("1Gi"), } now := unversioned.Now() oldTime := unversioned.NewTime(now.Time.Add(-1 * time.Minute)) @@ -435,12 +440,12 @@ func TestThresholdsMetGracePeriod(t *testing.T) { hardThreshold := Threshold{ Signal: SignalMemoryAvailable, Operator: OpLessThan, - Value: resource.MustParse("1Gi"), + Value: quantityMustParse("1Gi"), } softThreshold := Threshold{ Signal: SignalMemoryAvailable, Operator: OpLessThan, - Value: resource.MustParse("2Gi"), + Value: quantityMustParse("2Gi"), GracePeriod: 1 * time.Minute, } oldTime := unversioned.NewTime(now.Time.Add(-2 * time.Minute)) diff --git a/pkg/kubelet/eviction/manager_test.go b/pkg/kubelet/eviction/manager_test.go index 0d5fa77291e..a936a2f18d8 100644 --- a/pkg/kubelet/eviction/manager_test.go +++ b/pkg/kubelet/eviction/manager_test.go @@ -103,7 +103,7 @@ func TestMemoryPressure(t *testing.T) { { Signal: SignalMemoryAvailable, Operator: OpLessThan, - Value: resource.MustParse("1Gi"), + Value: quantityMustParse("1Gi"), }, }, } diff --git a/pkg/kubelet/eviction/types.go b/pkg/kubelet/eviction/types.go index a39a30394f9..d75406d1b45 100644 --- a/pkg/kubelet/eviction/types.go +++ b/pkg/kubelet/eviction/types.go @@ -55,7 +55,7 @@ type Threshold struct { // Operator represents a relationship of a signal to a value. Operator ThresholdOperator // value is a quantity associated with the signal that is evaluated against the specified operator. - Value resource.Quantity + Value *resource.Quantity // GracePeriod represents the amount of time that a threshold must be met before eviction is triggered. GracePeriod time.Duration } @@ -88,7 +88,7 @@ type statsFunc func(pod *api.Pod) (statsapi.PodStats, bool) type rankFunc func(pods []*api.Pod, stats statsFunc) // signalObservations maps a signal to an observed quantity -type signalObservations map[Signal]resource.Quantity +type signalObservations map[Signal]*resource.Quantity // thresholdsObservedAt maps a threshold to a time that it was observed type thresholdsObservedAt map[Threshold]time.Time diff --git a/pkg/kubelet/kubelet.go b/pkg/kubelet/kubelet.go index 92cdac77d33..9cd28d9bf5b 100644 --- a/pkg/kubelet/kubelet.go +++ b/pkg/kubelet/kubelet.go @@ -3051,7 +3051,7 @@ func (kl *Kubelet) setNodeStatusMachineInfo(node *api.Node) { if kl.reservation.Kubernetes != nil { value.Sub(kl.reservation.Kubernetes[k]) } - if value.Amount != nil && value.Amount.Sign() < 0 { + if value.Sign() < 0 { // Negative Allocatable resources don't make sense. value.Set(0) } diff --git a/pkg/kubelet/kubelet_test.go b/pkg/kubelet/kubelet_test.go index 9955269eed0..c06193a57e8 100644 --- a/pkg/kubelet/kubelet_test.go +++ b/pkg/kubelet/kubelet_test.go @@ -3853,16 +3853,16 @@ func TestExtractBandwidthResources(t *testing.T) { }, { pod: testPod("10M", ""), - expectedIngress: ten, + expectedIngress: &ten, }, { pod: testPod("", "10M"), - expectedEgress: ten, + expectedEgress: &ten, }, { pod: testPod("4M", "20M"), - expectedIngress: four, - expectedEgress: twenty, + expectedIngress: &four, + expectedEgress: &twenty, }, { pod: testPod("foo", ""), diff --git a/pkg/quota/resources.go b/pkg/quota/resources.go index 0cb03a84af1..0d5a7503417 100644 --- a/pkg/quota/resources.go +++ b/pkg/quota/resources.go @@ -93,7 +93,7 @@ func Subtract(a api.ResourceList, b api.ResourceList) api.ResourceList { for key, value := range b { if _, found := result[key]; !found { quantity := *value.Copy() - quantity.Neg(value) + quantity.Neg() result[key] = quantity } } diff --git a/pkg/util/bandwidth/utils.go b/pkg/util/bandwidth/utils.go index 989096f9346..b501e9b5c80 100644 --- a/pkg/util/bandwidth/utils.go +++ b/pkg/util/bandwidth/utils.go @@ -38,18 +38,22 @@ func validateBandwidthIsReasonable(rsrc *resource.Quantity) error { func ExtractPodBandwidthResources(podAnnotations map[string]string) (ingress, egress *resource.Quantity, err error) { str, found := podAnnotations["kubernetes.io/ingress-bandwidth"] if found { - if ingress, err = resource.ParseQuantity(str); err != nil { + ingressValue, err := resource.ParseQuantity(str) + if err != nil { return nil, nil, err } + ingress = &ingressValue if err := validateBandwidthIsReasonable(ingress); err != nil { return nil, nil, err } } str, found = podAnnotations["kubernetes.io/egress-bandwidth"] if found { - if egress, err = resource.ParseQuantity(str); err != nil { + egressValue, err := resource.ParseQuantity(str) + if err != nil { return nil, nil, err } + egress = &egressValue if err := validateBandwidthIsReasonable(egress); err != nil { return nil, nil, err } diff --git a/pkg/volume/testing/testing.go b/pkg/volume/testing/testing.go index f1a420cf28b..b126004f22a 100644 --- a/pkg/volume/testing/testing.go +++ b/pkg/volume/testing/testing.go @@ -383,5 +383,5 @@ func FindEmptyDirectoryUsageOnTmpfs() (*resource.Quantity, error) { return nil, fmt.Errorf("failed to parse 'du' output %s due to error %v", out, err) } used.Format = resource.BinarySI - return used, nil + return &used, nil } diff --git a/pkg/volume/util/fs_darwin.go b/pkg/volume/util/fs_darwin.go index 91d5ef97077..ef7a1618229 100644 --- a/pkg/volume/util/fs_darwin.go +++ b/pkg/volume/util/fs_darwin.go @@ -43,5 +43,5 @@ func Du(path string) (*resource.Quantity, error) { return nil, fmt.Errorf("failed to parse 'du' output %s due to error %v", out, err) } used.Format = resource.BinarySI - return used, nil + return &used, nil } diff --git a/pkg/volume/util/fs_linux.go b/pkg/volume/util/fs_linux.go index 39d1b0399a8..5193c0a3fc9 100644 --- a/pkg/volume/util/fs_linux.go +++ b/pkg/volume/util/fs_linux.go @@ -57,5 +57,5 @@ func Du(path string) (*resource.Quantity, error) { return nil, fmt.Errorf("failed to parse 'du' output %s due to error %v", out, err) } used.Format = resource.BinarySI - return used, nil + return &used, nil } diff --git a/test/e2e/limit_range.go b/test/e2e/limit_range.go index c59feeed673..5e18c525b5b 100644 --- a/test/e2e/limit_range.go +++ b/test/e2e/limit_range.go @@ -104,7 +104,7 @@ var _ = framework.KubeDescribe("LimitRange", func() { }) func equalResourceRequirement(expected api.ResourceRequirements, actual api.ResourceRequirements) error { - framework.Logf("Verifying requests: expected %s with actual %s", expected.Requests, actual.Requests) + framework.Logf("Verifying requests: expected %v with actual %v", expected.Requests, actual.Requests) err := equalResourceList(expected.Requests, actual.Requests) if err != nil { return err diff --git a/test/e2e/petset.go b/test/e2e/petset.go index 82dbb51c3ce..8a5bccc66dd 100644 --- a/test/e2e/petset.go +++ b/test/e2e/petset.go @@ -314,10 +314,7 @@ func newPVC(name string) api.PersistentVolumeClaim { }, Resources: api.ResourceRequirements{ Requests: api.ResourceList{ - api.ResourceStorage: resource.Quantity{ - Amount: dec(1, 0), - Format: resource.BinarySI, - }, + api.ResourceStorage: *resource.NewQuantity(1, resource.BinarySI), }, }, }, diff --git a/vendor/github.com/appc/spec/schema/types/isolator_resources.go b/vendor/github.com/appc/spec/schema/types/isolator_resources.go index 2ac5130d1c2..d97827b96f4 100644 --- a/vendor/github.com/appc/spec/schema/types/isolator_resources.go +++ b/vendor/github.com/appc/spec/schema/types/isolator_resources.go @@ -155,8 +155,8 @@ func NewResourceCPUIsolator(request, limit string) (*ResourceCPU, error) { res := &ResourceCPU{ ResourceBase{ resourceValue{ - Request: req, - Limit: lim, + Request: &req, + Limit: &lim, }, }, } @@ -209,8 +209,8 @@ func NewResourceMemoryIsolator(request, limit string) (*ResourceMemory, error) { res := &ResourceMemory{ ResourceBase{ resourceValue{ - Request: req, - Limit: lim, + Request: &req, + Limit: &lim, }, }, }