From 894a3e6d3ff68b8686297e601f81d5fdff311e35 Mon Sep 17 00:00:00 2001 From: Daniel Smith Date: Fri, 2 Jan 2015 19:10:03 -0800 Subject: [PATCH 1/6] Wire resource.Quantity into api --- pkg/api/serialization_test.go | 28 ++++++++++++++-- pkg/api/types.go | 33 +++++++++++++++---- pkg/api/v1beta1/conversion.go | 61 ++++++++++++++++++++++++++++++++++- pkg/api/v1beta1/types.go | 9 +++++- pkg/api/v1beta2/conversion.go | 61 ++++++++++++++++++++++++++++++++++- pkg/api/v1beta2/types.go | 9 +++++- pkg/api/v1beta3/types.go | 26 ++++++++++----- pkg/util/util.go | 8 +++++ 8 files changed, 214 insertions(+), 21 deletions(-) diff --git a/pkg/api/serialization_test.go b/pkg/api/serialization_test.go index 5c79980e154..156e0119eaf 100644 --- a/pkg/api/serialization_test.go +++ b/pkg/api/serialization_test.go @@ -27,16 +27,28 @@ import ( "github.com/GoogleCloudPlatform/kubernetes/pkg/api" "github.com/GoogleCloudPlatform/kubernetes/pkg/api/latest" "github.com/GoogleCloudPlatform/kubernetes/pkg/api/meta" + "github.com/GoogleCloudPlatform/kubernetes/pkg/api/resource" "github.com/GoogleCloudPlatform/kubernetes/pkg/api/v1beta1" "github.com/GoogleCloudPlatform/kubernetes/pkg/api/v1beta2" + "github.com/GoogleCloudPlatform/kubernetes/pkg/conversion" "github.com/GoogleCloudPlatform/kubernetes/pkg/runtime" "github.com/GoogleCloudPlatform/kubernetes/pkg/util" + docker "github.com/fsouza/go-dockerclient" fuzz "github.com/google/gofuzz" + "speter.net/go/exp/math/dec/inf" ) var fuzzIters = flag.Int("fuzz_iters", 40, "How many fuzzing iterations to do.") +// apiObjectComparer can do semantic deep equality checks for api objects. +var apiObjectComparer = conversion.EqualitiesOrDie( + func(a, b resource.Quantity) bool { + // Ignore formatting, only care that numeric value stayed the same. + return a.Amount.Cmp(b.Amount) == 0 + }, +) + // apiObjectFuzzer can randomly populate api objects. var apiObjectFuzzer = fuzz.New().NilChance(.5).NumElements(1, 1).Funcs( func(j *runtime.PluginBase, c fuzz.Continue) { @@ -136,6 +148,16 @@ var apiObjectFuzzer = fuzz.New().NilChance(.5).NumElements(1, 1).Funcs( c.RandString(): c.RandString(), } }, + + func(q *resource.Quantity, c fuzz.Continue) { + // Real Quantity fuzz testing is done elsewhere; + // this limited subset of functionality survives + // round-tripping to v1beta1/2. + q.Amount = &inf.Dec{} + q.Format = resource.DecimalExponent + //q.Amount.SetScale(inf.Scale(-c.Intn(12))) + q.Amount.SetUnscaled(c.Int63n(1000)) + }, ) func runTest(t *testing.T, codec runtime.Codec, source runtime.Object) { @@ -159,7 +181,7 @@ func runTest(t *testing.T, codec runtime.Codec, source runtime.Object) { t.Errorf("0: %v: %v\nCodec: %v\nData: %s\nSource: %#v", name, err, codec, string(data), source) return } - if !reflect.DeepEqual(source, obj2) { + if !apiObjectComparer.DeepEqual(source, obj2) { t.Errorf("1: %v: diff: %v\nCodec: %v\nData: %s\nSource: %#v", name, util.ObjectGoPrintDiff(source, obj2), codec, string(data), source) return } @@ -170,7 +192,7 @@ func runTest(t *testing.T, codec runtime.Codec, source runtime.Object) { t.Errorf("2: %v: %v", name, err) return } - if !reflect.DeepEqual(source, obj3) { + if !apiObjectComparer.DeepEqual(source, obj3) { t.Errorf("3: %v: diff: %v\nCodec: %v", name, util.ObjectDiff(source, obj3), codec) return } @@ -244,7 +266,7 @@ func TestEncode_Ptr(t *testing.T) { if _, ok := obj2.(*api.Pod); !ok { t.Fatalf("Got wrong type") } - if !reflect.DeepEqual(obj2, pod) { + if !apiObjectComparer.DeepEqual(obj2, pod) { t.Errorf("Expected:\n %#v,\n Got:\n %#v", &pod, obj2) } } diff --git a/pkg/api/types.go b/pkg/api/types.go index d7c0e6e3cf6..ebb32586ede 100644 --- a/pkg/api/types.go +++ b/pkg/api/types.go @@ -17,6 +17,7 @@ limitations under the License. package api import ( + "github.com/GoogleCloudPlatform/kubernetes/pkg/api/resource" "github.com/GoogleCloudPlatform/kubernetes/pkg/runtime" "github.com/GoogleCloudPlatform/kubernetes/pkg/util" ) @@ -309,12 +310,12 @@ type Container struct { Ports []Port `json:"ports,omitempty"` Env []EnvVar `json:"env,omitempty"` // Optional: Defaults to unlimited. - Memory int `json:"memory,omitempty"` + Memory resource.Quantity `json:"memory,omitempty"` // Optional: Defaults to unlimited. - CPU int `json:"cpu,omitempty"` - VolumeMounts []VolumeMount `json:"volumeMounts,omitempty"` - LivenessProbe *LivenessProbe `json:"livenessProbe,omitempty"` - Lifecycle *Lifecycle `json:"lifecycle,omitempty"` + CPU resource.Quantity `json:"cpu,omitempty"` + VolumeMounts []VolumeMount `json:"volumeMounts,omitempty"` + LivenessProbe *LivenessProbe `json:"livenessProbe,omitempty"` + Lifecycle *Lifecycle `json:"lifecycle,omitempty"` // Optional: Defaults to /dev/termination-log TerminationMessagePath string `json:"terminationMessagePath,omitempty"` // Optional: Default to false. @@ -747,9 +748,29 @@ type NodeResources struct { Capacity ResourceList `json:"capacity,omitempty"` } +// ResourceName is the name identifying various resources in a ResourceList. type ResourceName string -type ResourceList map[ResourceName]util.IntOrString +const ( + // CPU, in cores. (500m = .5 cores) + ResourceCPU ResourceName = "cpu" + // Memory, in bytes. (500Gi = 500GiB = 500 * 1024 * 1024 * 1024) + ResourceMemory ResourceName = "memory" +) + +// ResourceList is a set of (resource name, quantity) pairs. +type ResourceList map[ResourceName]resource.Quantity + +// Get is a convenience function, which returns a 0 quantity if the +// resource list is nil, empty, or lacks a value for the requested resource. +// Treat as read only! +func (rl ResourceList) Get(name ResourceName) *resource.Quantity { + if rl == nil { + return &resource.Quantity{} + } + q := rl[name] + return &q +} // Node is a worker node in Kubernetenes // The name of the node according to etcd is in ObjectMeta.Name. diff --git a/pkg/api/v1beta1/conversion.go b/pkg/api/v1beta1/conversion.go index f4ff1f5a854..3248d31d4d8 100644 --- a/pkg/api/v1beta1/conversion.go +++ b/pkg/api/v1beta1/conversion.go @@ -18,10 +18,13 @@ package v1beta1 import ( "errors" + "fmt" "strconv" newer "github.com/GoogleCloudPlatform/kubernetes/pkg/api" + "github.com/GoogleCloudPlatform/kubernetes/pkg/api/resource" "github.com/GoogleCloudPlatform/kubernetes/pkg/conversion" + "github.com/GoogleCloudPlatform/kubernetes/pkg/util" ) func init() { @@ -38,7 +41,7 @@ func init() { // newer.Scheme.AddStructFieldConversion(string(""), "Status", string(""), "Condition") // newer.Scheme.AddStructFieldConversion(string(""), "Condition", string(""), "Status") - newer.Scheme.AddConversionFuncs( + err := newer.Scheme.AddConversionFuncs( // TypeMeta must be split into two objects func(in *newer.TypeMeta, out *TypeMeta, s conversion.Scope) error { out.Kind = in.Kind @@ -582,5 +585,61 @@ func init() { out.Timestamp = in.Timestamp return s.Convert(&in.InvolvedObject, &out.InvolvedObject, 0) }, + + // This is triggered for the Memory field of Container. + func(in *int64, out *resource.Quantity, s conversion.Scope) error { + out.Set(*in) + out.Format = resource.BinarySI + return nil + }, + func(in *resource.Quantity, out *int64, s conversion.Scope) error { + *out = in.Value() + return nil + }, + + // This is triggered by the CPU field of Container. + // Note that if we add other int/Quantity conversions my + // simple hack (int64=Value(), int=MilliValue()) here won't work. + func(in *int, out *resource.Quantity, s conversion.Scope) error { + out.SetMilli(int64(*in)) + out.Format = resource.DecimalSI + return nil + }, + func(in *resource.Quantity, out *int, s conversion.Scope) error { + *out = int(in.MilliValue()) + return nil + }, + + // Convert resource lists. + func(in *ResourceList, out *newer.ResourceList, s conversion.Scope) error { + *out = newer.ResourceList{} + for k, v := range *in { + fv, err := strconv.ParseFloat(v.String(), 64) + if err != nil { + return fmt.Errorf("value '%v' of '%v': %v", v, k, err) + } + if k == ResourceCPU { + (*out)[newer.ResourceCPU] = *resource.NewMilliQuantity(int64(fv*1000), resource.DecimalSI) + } else { + (*out)[newer.ResourceName(k)] = *resource.NewQuantity(int64(fv), resource.BinarySI) + } + } + return nil + }, + func(in *newer.ResourceList, out *ResourceList, s conversion.Scope) error { + *out = ResourceList{} + for k, v := range *in { + if k == newer.ResourceCPU { + (*out)[ResourceCPU] = util.NewIntOrStringFromString(fmt.Sprintf("%v", float64(v.MilliValue())/1000)) + } else { + (*out)[ResourceName(k)] = util.NewIntOrStringFromInt(int(v.Value())) + } + } + return nil + }, ) + if err != nil { + // If one of the conversion functions is malformed, detect it immediately. + panic(err) + } } diff --git a/pkg/api/v1beta1/types.go b/pkg/api/v1beta1/types.go index fa4eebac2f3..77e19b6b9b5 100644 --- a/pkg/api/v1beta1/types.go +++ b/pkg/api/v1beta1/types.go @@ -254,7 +254,7 @@ type Container struct { Ports []Port `json:"ports,omitempty" description:"list of ports to expose from the container"` Env []EnvVar `json:"env,omitempty" description:"list of environment variables to set in the container"` // Optional: Defaults to unlimited. - Memory int `json:"memory,omitempty" description:"memory limit in bytes; defaults to unlimited"` + Memory int64 `json:"memory,omitempty" description:"memory limit in bytes; defaults to unlimited"` // Optional: Defaults to unlimited. CPU int `json:"cpu,omitempty" description:"CPU share in thousandths of a core"` VolumeMounts []VolumeMount `json:"volumeMounts,omitempty" description:"pod volumes to mount into the container's filesystem"` @@ -583,6 +583,13 @@ type NodeResources struct { type ResourceName string +const ( + // CPU, in cores. (floating point w/ 3 decimal places) + ResourceCPU ResourceName = "cpu" + // Memory, in bytes. + ResourceMemory ResourceName = "memory" +) + type ResourceList map[ResourceName]util.IntOrString // Minion is a worker node in Kubernetenes. diff --git a/pkg/api/v1beta2/conversion.go b/pkg/api/v1beta2/conversion.go index 10e4ad98b0c..00d8cfb313a 100644 --- a/pkg/api/v1beta2/conversion.go +++ b/pkg/api/v1beta2/conversion.go @@ -18,10 +18,13 @@ package v1beta2 import ( "errors" + "fmt" "strconv" newer "github.com/GoogleCloudPlatform/kubernetes/pkg/api" + "github.com/GoogleCloudPlatform/kubernetes/pkg/api/resource" "github.com/GoogleCloudPlatform/kubernetes/pkg/conversion" + "github.com/GoogleCloudPlatform/kubernetes/pkg/util" ) func init() { @@ -38,7 +41,7 @@ func init() { // newer.Scheme.AddStructFieldConversion(string(""), "Status", string(""), "Condition") // newer.Scheme.AddStructFieldConversion(string(""), "Condition", string(""), "Status") - newer.Scheme.AddConversionFuncs( + err := newer.Scheme.AddConversionFuncs( // TypeMeta must be split into two objects func(in *newer.TypeMeta, out *TypeMeta, s conversion.Scope) error { out.Kind = in.Kind @@ -498,5 +501,61 @@ func init() { out.Timestamp = in.Timestamp return s.Convert(&in.InvolvedObject, &out.InvolvedObject, 0) }, + + // This is triggered for the Memory field of Container. + func(in *int64, out *resource.Quantity, s conversion.Scope) error { + out.Set(*in) + out.Format = resource.BinarySI + return nil + }, + func(in *resource.Quantity, out *int64, s conversion.Scope) error { + *out = in.Value() + return nil + }, + + // This is triggered by the CPU field of Container. + // Note that if we add other int/Quantity conversions my + // simple hack (int64=Value(), int=MilliValue()) here won't work. + func(in *int, out *resource.Quantity, s conversion.Scope) error { + out.SetMilli(int64(*in)) + out.Format = resource.DecimalSI + return nil + }, + func(in *resource.Quantity, out *int, s conversion.Scope) error { + *out = int(in.MilliValue()) + return nil + }, + + // Convert resource lists. + func(in *ResourceList, out *newer.ResourceList, s conversion.Scope) error { + *out = newer.ResourceList{} + for k, v := range *in { + fv, err := strconv.ParseFloat(v.String(), 64) + if err != nil { + return fmt.Errorf("value '%v' of '%v': %v", v, k, err) + } + if k == ResourceCPU { + (*out)[newer.ResourceCPU] = *resource.NewMilliQuantity(int64(fv*1000), resource.DecimalSI) + } else { + (*out)[newer.ResourceName(k)] = *resource.NewQuantity(int64(fv), resource.BinarySI) + } + } + return nil + }, + func(in *newer.ResourceList, out *ResourceList, s conversion.Scope) error { + *out = ResourceList{} + for k, v := range *in { + if k == newer.ResourceCPU { + (*out)[ResourceCPU] = util.NewIntOrStringFromString(fmt.Sprintf("%v", float64(v.MilliValue())/1000)) + } else { + (*out)[ResourceName(k)] = util.NewIntOrStringFromInt(int(v.Value())) + } + } + return nil + }, ) + if err != nil { + // If one of the conversion functions is malformed, detect it immediately. + panic(err) + } } diff --git a/pkg/api/v1beta2/types.go b/pkg/api/v1beta2/types.go index 7438f264fa8..e8c26dab202 100644 --- a/pkg/api/v1beta2/types.go +++ b/pkg/api/v1beta2/types.go @@ -218,7 +218,7 @@ type Container struct { Ports []Port `json:"ports,omitempty" description:"list of ports to expose from the container"` Env []EnvVar `json:"env,omitempty" description:"list of environment variables to set in the container"` // Optional: Defaults to unlimited. - Memory int `json:"memory,omitempty" description:"memory limit in bytes; defaults to unlimited"` + Memory int64 `json:"memory,omitempty" description:"memory limit in bytes; defaults to unlimited"` // Optional: Defaults to unlimited. CPU int `json:"cpu,omitempty" description:"CPU share in thousandths of a core"` VolumeMounts []VolumeMount `json:"volumeMounts,omitempty" description:"pod volumes to mount into the container's filesystem"` @@ -546,6 +546,13 @@ type NodeResources struct { type ResourceName string +const ( + // CPU, in cores. (500m = .5 cores) + ResourceCPU ResourceName = "cpu" + // Memory, in bytes. (500Gi = 500GiB = 500 * 1024 * 1024 * 1024) + ResourceMemory ResourceName = "memory" +) + type ResourceList map[ResourceName]util.IntOrString // Minion is a worker node in Kubernetenes. diff --git a/pkg/api/v1beta3/types.go b/pkg/api/v1beta3/types.go index daf7b01b42d..cca9fed96d3 100644 --- a/pkg/api/v1beta3/types.go +++ b/pkg/api/v1beta3/types.go @@ -17,6 +17,7 @@ limitations under the License. package v1beta3 import ( + "github.com/GoogleCloudPlatform/kubernetes/pkg/api/resource" "github.com/GoogleCloudPlatform/kubernetes/pkg/runtime" "github.com/GoogleCloudPlatform/kubernetes/pkg/util" ) @@ -326,13 +327,13 @@ type Container struct { WorkingDir string `json:"workingDir,omitempty"` Ports []Port `json:"ports,omitempty"` Env []EnvVar `json:"env,omitempty"` - // Optional: Defaults to unlimited. - Memory int `json:"memory,omitempty"` - // Optional: Defaults to unlimited. - CPU int `json:"cpu,omitempty"` - VolumeMounts []VolumeMount `json:"volumeMounts,omitempty"` - LivenessProbe *LivenessProbe `json:"livenessProbe,omitempty"` - Lifecycle *Lifecycle `json:"lifecycle,omitempty"` + // Optional: Defaults to unlimited. Units: bytes. + Memory resource.Quantity `json:"memory,omitempty"` + // Optional: Defaults to unlimited. Units: Cores. (500m == 1/2 core) + CPU resource.Quantity `json:"cpu,omitempty"` + VolumeMounts []VolumeMount `json:"volumeMounts,omitempty"` + LivenessProbe *LivenessProbe `json:"livenessProbe,omitempty"` + Lifecycle *Lifecycle `json:"lifecycle,omitempty"` // Optional: Defaults to /dev/termination-log TerminationMessagePath string `json:"terminationMessagePath,omitempty"` // Optional: Default to false. @@ -776,9 +777,18 @@ type NodeCondition struct { Message string `json:"message,omitempty"` } +// ResourceName is the name identifying various resources in a ResourceList. type ResourceName string -type ResourceList map[ResourceName]util.IntOrString +const ( + // CPU, in cores. (500m = .5 cores) + ResourceCPU ResourceName = "cpu" + // Memory, in bytes. (500Gi = 500GiB = 500 * 1024 * 1024 * 1024) + ResourceMemory ResourceName = "memory" +) + +// ResourceList is a set of (resource name, quantity) pairs. +type ResourceList map[ResourceName]resource.Quantity // Node is a worker node in Kubernetes. // The name of the node according to etcd is in ID. diff --git a/pkg/util/util.go b/pkg/util/util.go index f7ee32f44d7..a84356888ad 100644 --- a/pkg/util/util.go +++ b/pkg/util/util.go @@ -113,6 +113,14 @@ func (intstr *IntOrString) UnmarshalJSON(value []byte) error { return json.Unmarshal(value, &intstr.IntVal) } +// String returns the string value, or Itoa's the int value. +func (intstr *IntOrString) String() string { + if intstr.Kind == IntstrString { + return intstr.StrVal + } + return strconv.Itoa(intstr.IntVal) +} + // MarshalJSON implements the json.Marshaller interface. func (intstr IntOrString) MarshalJSON() ([]byte, error) { switch intstr.Kind { From 40d29f4f08003896e031876459aa21d581440464 Mon Sep 17 00:00:00 2001 From: Daniel Smith Date: Mon, 5 Jan 2015 12:39:37 -0800 Subject: [PATCH 2/6] Remove obsolete resources package --- pkg/resources/doc.go | 18 ---- pkg/resources/resources.go | 75 -------------- pkg/resources/resources_test.go | 169 -------------------------------- 3 files changed, 262 deletions(-) delete mode 100644 pkg/resources/doc.go delete mode 100644 pkg/resources/resources.go delete mode 100644 pkg/resources/resources_test.go diff --git a/pkg/resources/doc.go b/pkg/resources/doc.go deleted file mode 100644 index 1c42e5b38be..00000000000 --- a/pkg/resources/doc.go +++ /dev/null @@ -1,18 +0,0 @@ -/* -Copyright 2014 Google Inc. 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 resources has constants and utilities for dealing with resources -package resources diff --git a/pkg/resources/resources.go b/pkg/resources/resources.go deleted file mode 100644 index 4647b367b59..00000000000 --- a/pkg/resources/resources.go +++ /dev/null @@ -1,75 +0,0 @@ -/* -Copyright 2014 Google Inc. 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 resources - -import ( - "strconv" - - "github.com/GoogleCloudPlatform/kubernetes/pkg/api" - "github.com/GoogleCloudPlatform/kubernetes/pkg/util" - "github.com/golang/glog" -) - -const ( - CPU api.ResourceName = "cpu" - Memory api.ResourceName = "memory" -) - -// TODO: None of these currently handle SI units - -func GetFloatResource(resources api.ResourceList, name api.ResourceName, def float64) float64 { - value, found := resources[name] - if !found { - return def - } - if value.Kind == util.IntstrInt { - return float64(value.IntVal) - } - result, err := strconv.ParseFloat(value.StrVal, 64) - if err != nil { - glog.Errorf("parsing failed for %s: %s", name, value.StrVal) - return def - } - return result -} - -func GetIntegerResource(resources api.ResourceList, name api.ResourceName, def int) int { - value, found := resources[name] - if !found { - return def - } - if value.Kind == util.IntstrInt { - return value.IntVal - } - result, err := strconv.Atoi(value.StrVal) - if err != nil { - glog.Errorf("parsing failed for %s: %s", name, value.StrVal) - return def - } - return result -} - -func GetStringResource(resources api.ResourceList, name api.ResourceName, def string) string { - value, found := resources[name] - if !found { - return def - } - if value.Kind == util.IntstrInt { - return strconv.Itoa(value.IntVal) - } - return value.StrVal -} diff --git a/pkg/resources/resources_test.go b/pkg/resources/resources_test.go deleted file mode 100644 index 263e25a54d2..00000000000 --- a/pkg/resources/resources_test.go +++ /dev/null @@ -1,169 +0,0 @@ -/* -Copyright 2014 Google Inc. 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 resources - -import ( - "testing" - - "github.com/GoogleCloudPlatform/kubernetes/pkg/api" - "github.com/GoogleCloudPlatform/kubernetes/pkg/util" -) - -func TestGetInteger(t *testing.T) { - tests := []struct { - res api.ResourceList - name api.ResourceName - expected int - def int - test string - }{ - { - res: api.ResourceList{}, - name: CPU, - expected: 1, - def: 1, - test: "nothing present", - }, - { - res: api.ResourceList{ - CPU: util.NewIntOrStringFromInt(2), - }, - name: CPU, - expected: 2, - def: 1, - test: "present", - }, - { - res: api.ResourceList{ - Memory: util.NewIntOrStringFromInt(2), - }, - name: CPU, - expected: 1, - def: 1, - test: "not-present", - }, - { - res: api.ResourceList{ - CPU: util.NewIntOrStringFromString("2"), - }, - name: CPU, - expected: 2, - def: 1, - test: "present-string", - }, - { - res: api.ResourceList{ - CPU: util.NewIntOrStringFromString("foo"), - }, - name: CPU, - expected: 1, - def: 1, - test: "present-invalid", - }, - } - - for _, test := range tests { - val := GetIntegerResource(test.res, test.name, test.def) - if val != test.expected { - t.Errorf("expected: %d found %d", test.expected, val) - } - } -} -func TestGetFloat(t *testing.T) { - tests := []struct { - res api.ResourceList - name api.ResourceName - expected float64 - def float64 - test string - }{ - { - res: api.ResourceList{}, - name: CPU, - expected: 1.5, - def: 1.5, - test: "nothing present", - }, - { - res: api.ResourceList{ - CPU: util.NewIntOrStringFromInt(2), - }, - name: CPU, - expected: 2.0, - def: 1.5, - test: "present", - }, - { - res: api.ResourceList{ - CPU: util.NewIntOrStringFromString("2.5"), - }, - name: CPU, - expected: 2.5, - def: 1, - test: "present-string", - }, - { - res: api.ResourceList{ - CPU: util.NewIntOrStringFromString("foo"), - }, - name: CPU, - expected: 1, - def: 1, - test: "present-invalid", - }, - } - - for _, test := range tests { - val := GetFloatResource(test.res, test.name, test.def) - if val != test.expected { - t.Errorf("expected: %f found %f", test.expected, val) - } - } -} -func TestGetString(t *testing.T) { - tests := []struct { - res api.ResourceList - name api.ResourceName - expected string - def string - test string - }{ - { - res: api.ResourceList{}, - name: CPU, - expected: "foo", - def: "foo", - test: "nothing present", - }, - { - res: api.ResourceList{ - CPU: util.NewIntOrStringFromString("bar"), - }, - name: CPU, - expected: "bar", - def: "foo", - test: "present", - }, - } - - for _, test := range tests { - val := GetStringResource(test.res, test.name, test.def) - if val != test.expected { - t.Errorf("expected: %s found %s", test.expected, val) - } - } -} From 7f49ba0dcf090c1ba5b292f317b94d2b90990596 Mon Sep 17 00:00:00 2001 From: Daniel Smith Date: Mon, 5 Jan 2015 13:16:18 -0800 Subject: [PATCH 3/6] Put quantity into packages kubelet, GCE, validation, client --- pkg/api/validation/validation_test.go | 25 +++++++++++---------- pkg/client/client_test.go | 8 +++---- pkg/cloudprovider/gce/gce.go | 16 ++++++++------ pkg/kubelet/kubelet.go | 6 ++--- pkg/scheduler/predicates.go | 18 +++++++-------- pkg/scheduler/predicates_test.go | 19 +++++----------- pkg/scheduler/priorities.go | 32 +++++++++++++++++---------- pkg/scheduler/priorities_test.go | 17 +++++++------- 8 files changed, 71 insertions(+), 70 deletions(-) diff --git a/pkg/api/validation/validation_test.go b/pkg/api/validation/validation_test.go index a93ba27f48a..3cb6f183331 100644 --- a/pkg/api/validation/validation_test.go +++ b/pkg/api/validation/validation_test.go @@ -22,6 +22,7 @@ import ( "github.com/GoogleCloudPlatform/kubernetes/pkg/api" "github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors" + "github.com/GoogleCloudPlatform/kubernetes/pkg/api/resource" "github.com/GoogleCloudPlatform/kubernetes/pkg/capabilities" "github.com/GoogleCloudPlatform/kubernetes/pkg/registry/registrytest" "github.com/GoogleCloudPlatform/kubernetes/pkg/util" @@ -372,8 +373,8 @@ func TestValidateManifest(t *testing.T) { Image: "image", Command: []string{"foo", "bar"}, WorkingDir: "/tmp", - Memory: 1, - CPU: 1, + Memory: *resource.Q("1"), + CPU: *resource.Q("1"), Ports: []api.Port{ {Name: "p1", ContainerPort: 80, HostPort: 8080}, {Name: "p2", ContainerPort: 81}, @@ -623,7 +624,7 @@ func TestValidatePodUpdate(t *testing.T) { Containers: []api.Container{ { Image: "foo:V1", - CPU: 100, + CPU: *resource.Q("100m"), }, }, }, @@ -634,7 +635,7 @@ func TestValidatePodUpdate(t *testing.T) { Containers: []api.Container{ { Image: "foo:V2", - CPU: 1000, + CPU: *resource.Q("1000m"), }, }, }, @@ -1300,8 +1301,8 @@ func TestValidateMinionUpdate(t *testing.T) { }, Spec: api.NodeSpec{ Capacity: api.ResourceList{ - "cpu": util.NewIntOrStringFromInt(10000), - "memory": util.NewIntOrStringFromInt(100), + api.ResourceCPU: *resource.Q("10000"), + api.ResourceMemory: *resource.Q("100"), }, }, }, api.Node{ @@ -1310,8 +1311,8 @@ func TestValidateMinionUpdate(t *testing.T) { }, Spec: api.NodeSpec{ Capacity: api.ResourceList{ - "cpu": util.NewIntOrStringFromInt(100), - "memory": util.NewIntOrStringFromInt(10000), + api.ResourceCPU: *resource.Q("100"), + api.ResourceMemory: *resource.Q("10000"), }, }, }, true}, @@ -1322,8 +1323,8 @@ func TestValidateMinionUpdate(t *testing.T) { }, Spec: api.NodeSpec{ Capacity: api.ResourceList{ - "cpu": util.NewIntOrStringFromInt(10000), - "memory": util.NewIntOrStringFromInt(100), + api.ResourceCPU: *resource.Q("10000"), + api.ResourceMemory: *resource.Q("100"), }, }, }, api.Node{ @@ -1333,8 +1334,8 @@ func TestValidateMinionUpdate(t *testing.T) { }, Spec: api.NodeSpec{ Capacity: api.ResourceList{ - "cpu": util.NewIntOrStringFromInt(100), - "memory": util.NewIntOrStringFromInt(10000), + api.ResourceCPU: *resource.Q("100"), + api.ResourceMemory: *resource.Q("10000"), }, }, }, true}, diff --git a/pkg/client/client_test.go b/pkg/client/client_test.go index b6180fc17ae..4283d5217aa 100644 --- a/pkg/client/client_test.go +++ b/pkg/client/client_test.go @@ -28,9 +28,9 @@ import ( "github.com/GoogleCloudPlatform/kubernetes/pkg/api" "github.com/GoogleCloudPlatform/kubernetes/pkg/api/latest" + "github.com/GoogleCloudPlatform/kubernetes/pkg/api/resource" "github.com/GoogleCloudPlatform/kubernetes/pkg/api/testapi" "github.com/GoogleCloudPlatform/kubernetes/pkg/labels" - "github.com/GoogleCloudPlatform/kubernetes/pkg/resources" "github.com/GoogleCloudPlatform/kubernetes/pkg/runtime" "github.com/GoogleCloudPlatform/kubernetes/pkg/util" "github.com/GoogleCloudPlatform/kubernetes/pkg/version" @@ -96,7 +96,7 @@ func (c *testClient) Setup() *testClient { func (c *testClient) Validate(t *testing.T, received runtime.Object, err error) { c.ValidateCommon(t, err) - if c.Response.Body != nil && !reflect.DeepEqual(c.Response.Body, received) { + if c.Response.Body != nil && !api.Semantic.DeepEqual(c.Response.Body, received) { t.Errorf("bad response for request %#v: expected %#v, got %#v", c.Request, c.Response.Body, received) } } @@ -734,8 +734,8 @@ func TestCreateMinion(t *testing.T) { }, Spec: api.NodeSpec{ Capacity: api.ResourceList{ - resources.CPU: util.NewIntOrStringFromInt(1000), - resources.Memory: util.NewIntOrStringFromInt(1024 * 1024), + api.ResourceCPU: *resource.Q("1000m"), + api.ResourceMemory: *resource.Q("1Mi"), }, }, } diff --git a/pkg/cloudprovider/gce/gce.go b/pkg/cloudprovider/gce/gce.go index 991b49186bd..5916918f9cb 100644 --- a/pkg/cloudprovider/gce/gce.go +++ b/pkg/cloudprovider/gce/gce.go @@ -28,13 +28,13 @@ import ( "strings" "time" + "github.com/GoogleCloudPlatform/kubernetes/pkg/api" + "github.com/GoogleCloudPlatform/kubernetes/pkg/api/resource" + "github.com/GoogleCloudPlatform/kubernetes/pkg/cloudprovider" + "code.google.com/p/goauth2/compute/serviceaccount" compute "code.google.com/p/google-api-go-client/compute/v1" container "code.google.com/p/google-api-go-client/container/v1beta1" - "github.com/GoogleCloudPlatform/kubernetes/pkg/api" - "github.com/GoogleCloudPlatform/kubernetes/pkg/cloudprovider" - "github.com/GoogleCloudPlatform/kubernetes/pkg/resources" - "github.com/GoogleCloudPlatform/kubernetes/pkg/util" "github.com/golang/glog" ) @@ -338,11 +338,12 @@ func (gce *GCECloud) List(filter string) ([]string, error) { return instances, nil } -func makeResources(cpu float32, memory float32) *api.NodeResources { +// cpu is in cores, memory is in GiB +func makeResources(cpu float64, memory float64) *api.NodeResources { return &api.NodeResources{ Capacity: api.ResourceList{ - resources.CPU: util.NewIntOrStringFromInt(int(cpu * 1000)), - resources.Memory: util.NewIntOrStringFromInt(int(memory * 1024 * 1024 * 1024)), + api.ResourceCPU: *resource.NewMilliQuantity(int64(cpu*1000), resource.DecimalSI), + api.ResourceMemory: *resource.NewQuantity(int64(memory*1024*1024*1024), resource.BinarySI), }, } } @@ -359,6 +360,7 @@ func (gce *GCECloud) GetNodeResources(name string) (*api.NodeResources, error) { if err != nil { return nil, err } + // TODO: actually read machine size instead of this awful hack. switch canonicalizeMachineType(res.MachineType) { case "f1-micro": return makeResources(1, 0.6), nil diff --git a/pkg/kubelet/kubelet.go b/pkg/kubelet/kubelet.go index 2cc6ae01273..bc16d9f5ea9 100644 --- a/pkg/kubelet/kubelet.go +++ b/pkg/kubelet/kubelet.go @@ -398,7 +398,7 @@ func makePortsAndBindings(container *api.Container) (map[docker.Port]struct{}, m return exposedPorts, portBindings } -func milliCPUToShares(milliCPU int) int { +func milliCPUToShares(milliCPU int64) int64 { if milliCPU == 0 { // zero milliCPU means unset. Use kernel default. return 0 @@ -537,8 +537,8 @@ func (kl *Kubelet) runContainer(pod *api.BoundPod, container *api.Container, pod ExposedPorts: exposedPorts, Hostname: pod.Name, Image: container.Image, - Memory: int64(container.Memory), - CPUShares: int64(milliCPUToShares(container.CPU)), + Memory: container.Memory.Value(), + CPUShares: milliCPUToShares(container.CPU.MilliValue()), WorkingDir: container.WorkingDir, }, } diff --git a/pkg/scheduler/predicates.go b/pkg/scheduler/predicates.go index 6768016b832..9e4aa2ec738 100644 --- a/pkg/scheduler/predicates.go +++ b/pkg/scheduler/predicates.go @@ -22,7 +22,6 @@ import ( "github.com/GoogleCloudPlatform/kubernetes/pkg/api" "github.com/GoogleCloudPlatform/kubernetes/pkg/client" "github.com/GoogleCloudPlatform/kubernetes/pkg/labels" - "github.com/GoogleCloudPlatform/kubernetes/pkg/resources" "github.com/golang/glog" ) @@ -89,15 +88,15 @@ type ResourceFit struct { } type resourceRequest struct { - milliCPU int - memory int + milliCPU int64 + memory int64 } func getResourceRequest(pod *api.Pod) resourceRequest { result := resourceRequest{} for ix := range pod.Spec.Containers { - result.memory += pod.Spec.Containers[ix].Memory - result.milliCPU += pod.Spec.Containers[ix].CPU + result.memory += pod.Spec.Containers[ix].Memory.Value() + result.milliCPU += pod.Spec.Containers[ix].CPU.MilliValue() } return result } @@ -113,17 +112,16 @@ func (r *ResourceFit) PodFitsResources(pod api.Pod, existingPods []api.Pod, node if err != nil { return false, err } - milliCPURequested := 0 - memoryRequested := 0 + milliCPURequested := int64(0) + memoryRequested := int64(0) for ix := range existingPods { existingRequest := getResourceRequest(&existingPods[ix]) milliCPURequested += existingRequest.milliCPU memoryRequested += existingRequest.memory } - // TODO: convert to general purpose resource matching, when pods ask for resources - totalMilliCPU := int(resources.GetFloatResource(info.Spec.Capacity, resources.CPU, 0) * 1000) - totalMemory := resources.GetIntegerResource(info.Spec.Capacity, resources.Memory, 0) + totalMilliCPU := info.Spec.Capacity.Get(api.ResourceCPU).MilliValue() + totalMemory := info.Spec.Capacity.Get(api.ResourceMemory).Value() fitsCPU := totalMilliCPU == 0 || (totalMilliCPU-milliCPURequested) >= podRequest.milliCPU fitsMemory := totalMemory == 0 || (totalMemory-memoryRequested) >= podRequest.memory diff --git a/pkg/scheduler/predicates_test.go b/pkg/scheduler/predicates_test.go index 106efbcb426..5fe556207f1 100644 --- a/pkg/scheduler/predicates_test.go +++ b/pkg/scheduler/predicates_test.go @@ -21,8 +21,7 @@ import ( "testing" "github.com/GoogleCloudPlatform/kubernetes/pkg/api" - "github.com/GoogleCloudPlatform/kubernetes/pkg/resources" - "github.com/GoogleCloudPlatform/kubernetes/pkg/util" + "github.com/GoogleCloudPlatform/kubernetes/pkg/api/resource" ) type FakeNodeInfo api.Node @@ -32,17 +31,11 @@ func (n FakeNodeInfo) GetNodeInfo(nodeName string) (*api.Node, error) { return &node, nil } -func makeResources(milliCPU int, memory int) api.NodeResources { +func makeResources(milliCPU int64, memory int64) api.NodeResources { return api.NodeResources{ Capacity: api.ResourceList{ - resources.CPU: util.IntOrString{ - IntVal: milliCPU, - Kind: util.IntstrInt, - }, - resources.Memory: util.IntOrString{ - IntVal: memory, - Kind: util.IntstrInt, - }, + api.ResourceCPU: *resource.NewMilliQuantity(milliCPU, resource.DecimalSI), + api.ResourceMemory: *resource.NewQuantity(memory, resource.BinarySI), }, } } @@ -51,8 +44,8 @@ func newResourcePod(usage ...resourceRequest) api.Pod { containers := []api.Container{} for _, req := range usage { containers = append(containers, api.Container{ - Memory: req.memory, - CPU: req.milliCPU, + Memory: *resource.NewQuantity(req.memory, resource.BinarySI), + CPU: *resource.NewMilliQuantity(req.milliCPU, resource.DecimalSI), }) } return api.Pod{ diff --git a/pkg/scheduler/priorities.go b/pkg/scheduler/priorities.go index c2ec09bbdec..dc3250cb8a2 100644 --- a/pkg/scheduler/priorities.go +++ b/pkg/scheduler/priorities.go @@ -18,13 +18,12 @@ package scheduler import ( "github.com/GoogleCloudPlatform/kubernetes/pkg/api" - "github.com/GoogleCloudPlatform/kubernetes/pkg/resources" "github.com/golang/glog" ) // the unused capacity is calculated on a scale of 0-10 // 0 being the lowest priority and 10 being the highest -func calculateScore(requested, capacity int, node string) int { +func calculateScore(requested, capacity int64, node string) int { if capacity == 0 { return 0 } @@ -32,30 +31,39 @@ func calculateScore(requested, capacity int, node string) int { glog.Errorf("Combined requested resources from existing pods exceeds capacity on minion: %s", node) return 0 } - return ((capacity - requested) * 10) / capacity + return int(((capacity - requested) * 10) / capacity) } // Calculate the occupancy on a node. 'node' has information about the resources on the node. // 'pods' is a list of pods currently scheduled on the node. func calculateOccupancy(pod api.Pod, node api.Node, pods []api.Pod) HostPriority { - totalCPU := 0 - totalMemory := 0 + totalMilliCPU := int64(0) + totalMemory := int64(0) for _, existingPod := range pods { for _, container := range existingPod.Spec.Containers { - totalCPU += container.CPU - totalMemory += container.Memory + totalMilliCPU += container.CPU.MilliValue() + totalMemory += container.Memory.Value() } } // Add the resources requested by the current pod being scheduled. // This also helps differentiate between differently sized, but empty, minions. for _, container := range pod.Spec.Containers { - totalCPU += container.CPU - totalMemory += container.Memory + totalMilliCPU += container.CPU.MilliValue() + totalMemory += container.Memory.Value() } - cpuScore := calculateScore(totalCPU, resources.GetIntegerResource(node.Spec.Capacity, resources.CPU, 0), node.Name) - memoryScore := calculateScore(totalMemory, resources.GetIntegerResource(node.Spec.Capacity, resources.Memory, 0), node.Name) - glog.V(4).Infof("Least Requested Priority, AbsoluteRequested: (%d, %d) Score:(%d, %d)", totalCPU, totalMemory, cpuScore, memoryScore) + capacityMilliCPU := node.Spec.Capacity.Get(api.ResourceCPU).MilliValue() + capacityMemory := node.Spec.Capacity.Get(api.ResourceMemory).Value() + + cpuScore := calculateScore(totalMilliCPU, capacityMilliCPU, node.Name) + memoryScore := calculateScore(totalMemory, capacityMemory, node.Name) + glog.V(4).Infof( + "%v -> %v: Least Requested Priority, AbsoluteRequested: (%d, %d) / (%d, %d) Score: (%d, %d)", + pod.Name, node.Name, + totalMilliCPU, totalMemory, + capacityMilliCPU, capacityMemory, + cpuScore, memoryScore, + ) return HostPriority{ host: node.Name, diff --git a/pkg/scheduler/priorities_test.go b/pkg/scheduler/priorities_test.go index 327d877f44d..1081709d90c 100644 --- a/pkg/scheduler/priorities_test.go +++ b/pkg/scheduler/priorities_test.go @@ -21,17 +21,16 @@ import ( "testing" "github.com/GoogleCloudPlatform/kubernetes/pkg/api" - "github.com/GoogleCloudPlatform/kubernetes/pkg/resources" - "github.com/GoogleCloudPlatform/kubernetes/pkg/util" + "github.com/GoogleCloudPlatform/kubernetes/pkg/api/resource" ) -func makeMinion(node string, cpu, memory int) api.Node { +func makeMinion(node string, milliCPU, memory int64) api.Node { return api.Node{ ObjectMeta: api.ObjectMeta{Name: node}, Spec: api.NodeSpec{ Capacity: api.ResourceList{ - resources.CPU: util.NewIntOrStringFromInt(cpu), - resources.Memory: util.NewIntOrStringFromInt(memory), + api.ResourceCPU: *resource.NewMilliQuantity(milliCPU, resource.DecimalSI), + api.ResourceMemory: *resource.NewQuantity(memory, resource.BinarySI), }, }, } @@ -57,14 +56,14 @@ func TestLeastRequested(t *testing.T) { } cpuOnly := api.PodSpec{ Containers: []api.Container{ - {CPU: 1000}, - {CPU: 2000}, + {CPU: *resource.Q("1000m")}, + {CPU: *resource.Q("2000m")}, }, } cpuAndMemory := api.PodSpec{ Containers: []api.Container{ - {CPU: 1000, Memory: 2000}, - {CPU: 2000, Memory: 3000}, + {CPU: *resource.Q("1000m"), Memory: *resource.Q("2000")}, + {CPU: *resource.Q("2000m"), Memory: *resource.Q("3000")}, }, } tests := []struct { From 0d628b3bff2fafa334b76e322f60cc095cac3192 Mon Sep 17 00:00:00 2001 From: Daniel Smith Date: Mon, 5 Jan 2015 13:38:39 -0800 Subject: [PATCH 4/6] Make semantic deep equal public feature * Use semantic deep equal when validating * More test cases for deep equal --- pkg/api/helpers.go | 22 ++++++++++++++++++++++ pkg/api/serialization_test.go | 15 +++------------ pkg/api/validation/validation.go | 7 +++---- pkg/conversion/deep_equal_test.go | 17 +++++++++++++++++ 4 files changed, 45 insertions(+), 16 deletions(-) diff --git a/pkg/api/helpers.go b/pkg/api/helpers.go index a593dde3d90..5ee4fa1b010 100644 --- a/pkg/api/helpers.go +++ b/pkg/api/helpers.go @@ -18,6 +18,28 @@ package api import ( "strings" + + "github.com/GoogleCloudPlatform/kubernetes/pkg/api/resource" + "github.com/GoogleCloudPlatform/kubernetes/pkg/conversion" +) + +// Semantic can do semantic deep equality checks for api objects. +// Example: api.Semantic.DeepEqual(aPod, aPodWithNonNilButEmptyMaps) == true +var Semantic = conversion.EqualitiesOrDie( + func(a, b resource.Quantity) bool { + // Ignore formatting, only care that numeric value stayed the same. + // TODO: if we decide it's important, after we drop v1beta1/2, we + // could start comparing format. + // + // Uninitialized quantities are equivilent to 0 quantities. + if a.Amount == nil && b.MilliValue() == 0 { + return true + } + if b.Amount == nil && a.MilliValue() == 0 { + return true + } + return a.Amount.Cmp(b.Amount) == 0 + }, ) // TODO: Address these per #1502 diff --git a/pkg/api/serialization_test.go b/pkg/api/serialization_test.go index 156e0119eaf..46330c4e10d 100644 --- a/pkg/api/serialization_test.go +++ b/pkg/api/serialization_test.go @@ -30,7 +30,6 @@ import ( "github.com/GoogleCloudPlatform/kubernetes/pkg/api/resource" "github.com/GoogleCloudPlatform/kubernetes/pkg/api/v1beta1" "github.com/GoogleCloudPlatform/kubernetes/pkg/api/v1beta2" - "github.com/GoogleCloudPlatform/kubernetes/pkg/conversion" "github.com/GoogleCloudPlatform/kubernetes/pkg/runtime" "github.com/GoogleCloudPlatform/kubernetes/pkg/util" @@ -41,14 +40,6 @@ import ( var fuzzIters = flag.Int("fuzz_iters", 40, "How many fuzzing iterations to do.") -// apiObjectComparer can do semantic deep equality checks for api objects. -var apiObjectComparer = conversion.EqualitiesOrDie( - func(a, b resource.Quantity) bool { - // Ignore formatting, only care that numeric value stayed the same. - return a.Amount.Cmp(b.Amount) == 0 - }, -) - // apiObjectFuzzer can randomly populate api objects. var apiObjectFuzzer = fuzz.New().NilChance(.5).NumElements(1, 1).Funcs( func(j *runtime.PluginBase, c fuzz.Continue) { @@ -181,7 +172,7 @@ func runTest(t *testing.T, codec runtime.Codec, source runtime.Object) { t.Errorf("0: %v: %v\nCodec: %v\nData: %s\nSource: %#v", name, err, codec, string(data), source) return } - if !apiObjectComparer.DeepEqual(source, obj2) { + if !api.Semantic.DeepEqual(source, obj2) { t.Errorf("1: %v: diff: %v\nCodec: %v\nData: %s\nSource: %#v", name, util.ObjectGoPrintDiff(source, obj2), codec, string(data), source) return } @@ -192,7 +183,7 @@ func runTest(t *testing.T, codec runtime.Codec, source runtime.Object) { t.Errorf("2: %v: %v", name, err) return } - if !apiObjectComparer.DeepEqual(source, obj3) { + if !api.Semantic.DeepEqual(source, obj3) { t.Errorf("3: %v: diff: %v\nCodec: %v", name, util.ObjectDiff(source, obj3), codec) return } @@ -266,7 +257,7 @@ func TestEncode_Ptr(t *testing.T) { if _, ok := obj2.(*api.Pod); !ok { t.Fatalf("Got wrong type") } - if !apiObjectComparer.DeepEqual(obj2, pod) { + if !api.Semantic.DeepEqual(obj2, pod) { t.Errorf("Expected:\n %#v,\n Got:\n %#v", &pod, obj2) } } diff --git a/pkg/api/validation/validation.go b/pkg/api/validation/validation.go index 4731c48b886..8566d8012c1 100644 --- a/pkg/api/validation/validation.go +++ b/pkg/api/validation/validation.go @@ -18,7 +18,6 @@ package validation import ( "fmt" - "reflect" "strings" "github.com/GoogleCloudPlatform/kubernetes/pkg/api" @@ -429,7 +428,7 @@ func ValidatePodUpdate(newPod, oldPod *api.Pod) errs.ValidationErrorList { newContainers = append(newContainers, container) } pod.Spec.Containers = newContainers - if !reflect.DeepEqual(pod.Spec, oldPod.Spec) { + if !api.Semantic.DeepEqual(pod.Spec, oldPod.Spec) { // TODO: a better error would include all immutable fields explicitly. allErrs = append(allErrs, errs.NewFieldInvalid("spec.containers", newPod.Spec.Containers, "some fields are immutable")) } @@ -586,7 +585,7 @@ func ValidateMinion(minion *api.Node) errs.ValidationErrorList { func ValidateMinionUpdate(oldMinion *api.Node, minion *api.Node) errs.ValidationErrorList { allErrs := errs.ValidationErrorList{} - if !reflect.DeepEqual(minion.Status, api.NodeStatus{}) { + if !api.Semantic.DeepEqual(minion.Status, api.NodeStatus{}) { allErrs = append(allErrs, errs.NewFieldInvalid("status", minion.Status, "status must be empty")) } @@ -596,7 +595,7 @@ func ValidateMinionUpdate(oldMinion *api.Node, minion *api.Node) errs.Validation // Clear status oldMinion.Status = minion.Status - if !reflect.DeepEqual(oldMinion, minion) { + if !api.Semantic.DeepEqual(oldMinion, minion) { glog.V(4).Infof("Update failed validation %#v vs %#v", oldMinion, minion) allErrs = append(allErrs, fmt.Errorf("update contains more than labels or capacity changes")) } diff --git a/pkg/conversion/deep_equal_test.go b/pkg/conversion/deep_equal_test.go index 5d4a5dabbb7..d89ab53e5cf 100644 --- a/pkg/conversion/deep_equal_test.go +++ b/pkg/conversion/deep_equal_test.go @@ -22,15 +22,28 @@ import ( func TestEqualities(t *testing.T) { e := Equalities{} + type Bar struct { + X int + } + type Baz struct { + Y Bar + } err := e.AddFuncs( func(a, b int) bool { return a+1 == b }, + func(a, b Bar) bool { + return a.X*10 == b.X + }, ) if err != nil { t.Fatalf("Unexpected: %v", err) } + type Foo struct { + X int + } + table := []struct { a, b interface{} equal bool @@ -38,6 +51,10 @@ func TestEqualities(t *testing.T) { {1, 2, true}, {2, 1, false}, {"foo", "foo", true}, + {Foo{1}, Foo{2}, true}, + {Bar{1}, Bar{10}, true}, + {&Bar{1}, &Bar{10}, true}, + {Baz{Bar{1}}, Baz{Bar{10}}, true}, {map[string]int{"foo": 1}, map[string]int{"foo": 2}, true}, {map[string]int{}, map[string]int(nil), true}, {[]int{}, []int(nil), true}, From 35f54addcaa84fcc9330d7b172cdab8f267ea709 Mon Sep 17 00:00:00 2001 From: Daniel Smith Date: Mon, 5 Jan 2015 14:37:07 -0800 Subject: [PATCH 5/6] Update more packages, tests, binaries for quantity make etcd registry pass test fix kubelet config for quantity fix openstack for quantity fix controller for quantity fix last tests for quantity wire into binaries fix controller manager fix build for 32 bit systems --- .../controller-manager.go | 22 +++++-------------- pkg/api/latest/latest_test.go | 3 +-- pkg/api/resource/quantity.go | 2 +- pkg/api/v1beta1/conversion_test.go | 4 ++-- pkg/cloudprovider/openstack/openstack.go | 12 +++++----- pkg/controller/replication_controller_test.go | 5 ++--- pkg/kubelet/config/config_test.go | 9 +------- pkg/kubelet/config/file_test.go | 9 ++++---- pkg/kubelet/config/http_test.go | 3 +-- pkg/registry/controller/rest_test.go | 7 +++--- pkg/registry/etcd/etcd_test.go | 6 ++--- pkg/standalone/standalone.go | 19 +++------------- 12 files changed, 32 insertions(+), 69 deletions(-) diff --git a/cmd/kube-controller-manager/controller-manager.go b/cmd/kube-controller-manager/controller-manager.go index ceb7e487e4b..884d1a8f8ea 100644 --- a/cmd/kube-controller-manager/controller-manager.go +++ b/cmd/kube-controller-manager/controller-manager.go @@ -22,20 +22,19 @@ package main import ( "flag" - "math" "net" "net/http" "strconv" "time" "github.com/GoogleCloudPlatform/kubernetes/pkg/api" + "github.com/GoogleCloudPlatform/kubernetes/pkg/api/resource" "github.com/GoogleCloudPlatform/kubernetes/pkg/client" "github.com/GoogleCloudPlatform/kubernetes/pkg/cloudprovider" minionControllerPkg "github.com/GoogleCloudPlatform/kubernetes/pkg/cloudprovider/controller" replicationControllerPkg "github.com/GoogleCloudPlatform/kubernetes/pkg/controller" _ "github.com/GoogleCloudPlatform/kubernetes/pkg/healthz" "github.com/GoogleCloudPlatform/kubernetes/pkg/master/ports" - "github.com/GoogleCloudPlatform/kubernetes/pkg/resources" "github.com/GoogleCloudPlatform/kubernetes/pkg/service" "github.com/GoogleCloudPlatform/kubernetes/pkg/util" "github.com/GoogleCloudPlatform/kubernetes/pkg/version/verflag" @@ -51,8 +50,9 @@ var ( minionRegexp = flag.String("minion_regexp", "", "If non empty, and -cloud_provider is specified, a regular expression for matching minion VMs.") machineList util.StringList // TODO: Discover these by pinging the host machines, and rip out these flags. + // TODO: in the meantime, use resource.QuantityFlag() instead of these nodeMilliCPU = flag.Int64("node_milli_cpu", 1000, "The amount of MilliCPU provisioned on each node") - nodeMemory = flag.Int64("node_memory", 3*1024*1024*1024, "The amount of memory (in bytes) provisioned on each node") + nodeMemory = resource.QuantityFlag("node_memory", "3Gi", "The amount of memory (in bytes) provisioned on each node") ) func init() { @@ -90,18 +90,6 @@ func main() { glog.Fatalf("Invalid API configuration: %v", err) } - if int64(int(*nodeMilliCPU)) != *nodeMilliCPU { - glog.Warningf("node_milli_cpu is too big for platform. Clamping: %d -> %d", - *nodeMilliCPU, math.MaxInt32) - *nodeMilliCPU = math.MaxInt32 - } - - if int64(int(*nodeMemory)) != *nodeMemory { - glog.Warningf("node_memory is too big for platform. Clamping: %d -> %d", - *nodeMemory, math.MaxInt32) - *nodeMemory = math.MaxInt32 - } - go http.ListenAndServe(net.JoinHostPort(address.String(), strconv.Itoa(*port)), nil) endpoints := service.NewEndpointController(kubeClient) @@ -113,8 +101,8 @@ func main() { cloud := cloudprovider.InitCloudProvider(*cloudProvider, *cloudConfigFile) nodeResources := &api.NodeResources{ Capacity: api.ResourceList{ - resources.CPU: util.NewIntOrStringFromInt(int(*nodeMilliCPU)), - resources.Memory: util.NewIntOrStringFromInt(int(*nodeMemory)), + api.ResourceCPU: *resource.NewMilliQuantity(*nodeMilliCPU, resource.DecimalSI), + api.ResourceMemory: *nodeMemory, }, } minionController := minionControllerPkg.NewMinionController(cloud, *minionRegexp, machineList, nodeResources, kubeClient) diff --git a/pkg/api/latest/latest_test.go b/pkg/api/latest/latest_test.go index bedb8a1fcae..56ae1a26e50 100644 --- a/pkg/api/latest/latest_test.go +++ b/pkg/api/latest/latest_test.go @@ -18,7 +18,6 @@ package latest import ( "encoding/json" - "reflect" "strconv" "testing" @@ -167,7 +166,7 @@ func TestInternalRoundTrip(t *testing.T) { continue } - if !reflect.DeepEqual(obj, actual) { + if !internal.Semantic.DeepEqual(obj, actual) { t.Errorf("%s: diff %s", k, util.ObjectDiff(obj, actual)) } } diff --git a/pkg/api/resource/quantity.go b/pkg/api/resource/quantity.go index 3b04fcd91c6..c92f47ff74a 100644 --- a/pkg/api/resource/quantity.go +++ b/pkg/api/resource/quantity.go @@ -148,7 +148,7 @@ var ( // 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 = ((1 << 63) - 1) / 1000 + MaxMilliValue = int64(((1 << 63) - 1) / 1000) ) // ParseQuantity turns str into a Quantity, or returns an error. diff --git a/pkg/api/v1beta1/conversion_test.go b/pkg/api/v1beta1/conversion_test.go index 01fc2d170cd..a3a5a8cbed9 100644 --- a/pkg/api/v1beta1/conversion_test.go +++ b/pkg/api/v1beta1/conversion_test.go @@ -201,7 +201,7 @@ func TestMinionListConversionToNew(t *testing.T) { if err != nil { t.Errorf("Unexpected error: %v", err) } - if e, a := item.newML, got; !reflect.DeepEqual(e, a) { + if e, a := item.newML, got; !newer.Semantic.DeepEqual(e, a) { t.Errorf("Expected: %#v, got %#v", e, a) } } @@ -234,7 +234,7 @@ func TestMinionListConversionToOld(t *testing.T) { if err != nil { t.Errorf("Unexpected error: %v", err) } - if e, a := oldML, got; !reflect.DeepEqual(e, a) { + if e, a := oldML, got; !newer.Semantic.DeepEqual(e, a) { t.Errorf("Expected: %#v, got %#v", e, a) } } diff --git a/pkg/cloudprovider/openstack/openstack.go b/pkg/cloudprovider/openstack/openstack.go index f744b3b100b..f546b2baf40 100644 --- a/pkg/cloudprovider/openstack/openstack.go +++ b/pkg/cloudprovider/openstack/openstack.go @@ -36,8 +36,8 @@ import ( "github.com/rackspace/gophercloud/pagination" "github.com/GoogleCloudPlatform/kubernetes/pkg/api" + "github.com/GoogleCloudPlatform/kubernetes/pkg/api/resource" "github.com/GoogleCloudPlatform/kubernetes/pkg/cloudprovider" - "github.com/GoogleCloudPlatform/kubernetes/pkg/util" "github.com/golang/glog" ) @@ -169,11 +169,11 @@ func (os *OpenStack) Instances() (cloudprovider.Instances, bool) { for _, flavor := range flavorList { rsrc := api.NodeResources{ Capacity: api.ResourceList{ - "cpu": util.NewIntOrStringFromInt(flavor.VCPUs), - "memory": util.NewIntOrStringFromString(fmt.Sprintf("%dMiB", flavor.RAM)), - "openstack.org/disk": util.NewIntOrStringFromString(fmt.Sprintf("%dGB", flavor.Disk)), - "openstack.org/rxTxFactor": util.NewIntOrStringFromInt(int(flavor.RxTxFactor * 1000)), - "openstack.org/swap": util.NewIntOrStringFromString(fmt.Sprintf("%dMiB", flavor.Swap)), + api.ResourceCPU: *resource.NewMilliQuantity(int64(flavor.VCPUs*1000), resource.DecimalSI), + api.ResourceMemory: *resource.Q(fmt.Sprintf("%dMi", flavor.RAM)), + "openstack.org/disk": *resource.Q(fmt.Sprintf("%dG", flavor.Disk)), + "openstack.org/rxTxFactor": *resource.NewQuantity(int64(flavor.RxTxFactor*1000), resource.DecimalSI), + "openstack.org/swap": *resource.Q(fmt.Sprintf("%dMiB", flavor.Swap)), }, } flavor_to_resource[flavor.ID] = &rsrc diff --git a/pkg/controller/replication_controller_test.go b/pkg/controller/replication_controller_test.go index 21f17d768d5..d6fdd7f8780 100644 --- a/pkg/controller/replication_controller_test.go +++ b/pkg/controller/replication_controller_test.go @@ -21,7 +21,6 @@ import ( "net/http" "net/http/httptest" "path" - "reflect" "sync" "testing" "time" @@ -233,7 +232,7 @@ func TestCreateReplica(t *testing.T) { if err != nil { t.Errorf("Unexpected error: %#v", err) } - if !reflect.DeepEqual(&expectedPod, actualPod) { + if !api.Semantic.DeepEqual(&expectedPod, actualPod) { t.Logf("Body: %s", fakeHandler.RequestBody) t.Errorf("Unexpected mismatch. Expected\n %#v,\n Got:\n %#v", &expectedPod, actualPod) } @@ -345,7 +344,7 @@ func TestWatchControllers(t *testing.T) { var testControllerSpec api.ReplicationController received := make(chan struct{}) manager.syncHandler = func(controllerSpec api.ReplicationController) error { - if !reflect.DeepEqual(controllerSpec, testControllerSpec) { + if !api.Semantic.DeepEqual(controllerSpec, testControllerSpec) { t.Errorf("Expected %#v, but got %#v", testControllerSpec, controllerSpec) } close(received) diff --git a/pkg/kubelet/config/config_test.go b/pkg/kubelet/config/config_test.go index 245008f52d0..fdf7f9fed9f 100644 --- a/pkg/kubelet/config/config_test.go +++ b/pkg/kubelet/config/config_test.go @@ -17,7 +17,6 @@ limitations under the License. package config import ( - "reflect" "sort" "testing" @@ -65,13 +64,7 @@ func CreateValidPod(name, namespace, source string) api.BoundPod { } func CreatePodUpdate(op kubelet.PodOperation, source string, pods ...api.BoundPod) kubelet.PodUpdate { - // We deliberately return an empty slice instead of a nil pointer here - // because reflect.DeepEqual differentiates between the two and we need to - // pick one for consistency. newPods := make([]api.BoundPod, len(pods)) - if len(pods) == 0 { - return kubelet.PodUpdate{newPods, op, source} - } for i := range pods { newPods[i] = pods[i] } @@ -89,7 +82,7 @@ func expectPodUpdate(t *testing.T, ch <-chan kubelet.PodUpdate, expected ...kube for i := range expected { update := <-ch sort.Sort(sortedPods(update.Pods)) - if !reflect.DeepEqual(expected[i], update) { + if !api.Semantic.DeepEqual(expected[i], update) { t.Fatalf("Expected %#v, Got %#v", expected[i], update) } } diff --git a/pkg/kubelet/config/file_test.go b/pkg/kubelet/config/file_test.go index 135bdea5467..3c9e2a3e4ee 100644 --- a/pkg/kubelet/config/file_test.go +++ b/pkg/kubelet/config/file_test.go @@ -20,7 +20,6 @@ import ( "encoding/json" "io/ioutil" "os" - "reflect" "sort" "testing" "time" @@ -130,7 +129,7 @@ func TestReadFromFile(t *testing.T) { Containers: []api.Container{{Image: "test/image", TerminationMessagePath: "/dev/termination-log"}}, }, }) - if !reflect.DeepEqual(expected, update) { + if !api.Semantic.DeepEqual(expected, update) { t.Fatalf("Expected %#v, Got %#v", expected, update) } @@ -171,7 +170,7 @@ func TestExtractFromValidDataFile(t *testing.T) { } update := (<-ch).(kubelet.PodUpdate) expected := CreatePodUpdate(kubelet.SET, kubelet.FileSource, expectedPod) - if !reflect.DeepEqual(expected, update) { + if !api.Semantic.DeepEqual(expected, update) { t.Errorf("Expected %#v, Got %#v", expected, update) } } @@ -192,7 +191,7 @@ func TestExtractFromEmptyDir(t *testing.T) { update := (<-ch).(kubelet.PodUpdate) expected := CreatePodUpdate(kubelet.SET, kubelet.FileSource) - if !reflect.DeepEqual(expected, update) { + if !api.Semantic.DeepEqual(expected, update) { t.Errorf("Expected %#v, Got %#v", expected, update) } } @@ -242,7 +241,7 @@ func TestExtractFromDir(t *testing.T) { expected := CreatePodUpdate(kubelet.SET, kubelet.FileSource, pods...) sort.Sort(sortedPods(update.Pods)) sort.Sort(sortedPods(expected.Pods)) - if !reflect.DeepEqual(expected, update) { + if !api.Semantic.DeepEqual(expected, update) { t.Fatalf("Expected %#v, Got %#v", expected, update) } for i := range update.Pods { diff --git a/pkg/kubelet/config/http_test.go b/pkg/kubelet/config/http_test.go index a1c0b7d101b..016b70373fc 100644 --- a/pkg/kubelet/config/http_test.go +++ b/pkg/kubelet/config/http_test.go @@ -19,7 +19,6 @@ package config import ( "encoding/json" "net/http/httptest" - "reflect" "testing" "time" @@ -193,7 +192,7 @@ func TestExtractFromHTTP(t *testing.T) { continue } update := (<-ch).(kubelet.PodUpdate) - if !reflect.DeepEqual(testCase.expected, update) { + if !api.Semantic.DeepEqual(testCase.expected, update) { t.Errorf("%s: Expected: %#v, Got: %#v", testCase.desc, testCase.expected, update) } for i := range update.Pods { diff --git a/pkg/registry/controller/rest_test.go b/pkg/registry/controller/rest_test.go index febe607f25d..585a956c0ce 100644 --- a/pkg/registry/controller/rest_test.go +++ b/pkg/registry/controller/rest_test.go @@ -20,7 +20,6 @@ import ( "encoding/json" "fmt" "io/ioutil" - "reflect" "strings" "testing" "time" @@ -136,7 +135,7 @@ func TestControllerDecode(t *testing.T) { t.Errorf("unexpected error: %v", err) } - if !reflect.DeepEqual(controller, controllerOut) { + if !api.Semantic.DeepEqual(controller, controllerOut) { t.Errorf("Expected %#v, found %#v", controller, controllerOut) } } @@ -208,7 +207,7 @@ func TestControllerParsing(t *testing.T) { t.Errorf("unexpected error: %v", err) } - if !reflect.DeepEqual(controller, expectedController) { + if !api.Semantic.DeepEqual(controller, expectedController) { t.Errorf("Parsing failed: %s %#v %#v", string(data), controller, expectedController) } } @@ -375,7 +374,7 @@ func TestFillCurrentState(t *testing.T) { if controller.Status.Replicas != 2 { t.Errorf("expected 2, got: %d", controller.Status.Replicas) } - if !reflect.DeepEqual(fakeLister.s, labels.Set(controller.Spec.Selector).AsSelector()) { + if !api.Semantic.DeepEqual(fakeLister.s, labels.Set(controller.Spec.Selector).AsSelector()) { t.Errorf("unexpected output: %#v %#v", labels.Set(controller.Spec.Selector).AsSelector(), fakeLister.s) } } diff --git a/pkg/registry/etcd/etcd_test.go b/pkg/registry/etcd/etcd_test.go index 6326b683df3..ef6647eb6b1 100644 --- a/pkg/registry/etcd/etcd_test.go +++ b/pkg/registry/etcd/etcd_test.go @@ -448,7 +448,7 @@ func TestEtcdUpdatePodNotScheduled(t *testing.T) { } var podOut api.Pod latest.Codec.DecodeInto([]byte(response.Node.Value), &podOut) - if !reflect.DeepEqual(podOut, podIn) { + if !api.Semantic.DeepEqual(podOut, podIn) { t.Errorf("expected: %v, got: %v", podOut, podIn) } } @@ -529,7 +529,7 @@ func TestEtcdUpdatePodScheduled(t *testing.T) { } var podOut api.Pod latest.Codec.DecodeInto([]byte(response.Node.Value), &podOut) - if !reflect.DeepEqual(podOut, podIn) { + if !api.Semantic.DeepEqual(podOut, podIn) { t.Errorf("expected: %#v, got: %#v", podOut, podIn) } @@ -542,7 +542,7 @@ func TestEtcdUpdatePodScheduled(t *testing.T) { t.Fatalf("unexpected error decoding response: %v", err) } - if len(list.Items) != 2 || !reflect.DeepEqual(list.Items[0].Spec, podIn.Spec) { + if len(list.Items) != 2 || !api.Semantic.DeepEqual(list.Items[0].Spec, podIn.Spec) { t.Errorf("unexpected container list: %d\n items[0] - %#v\n podin.spec - %#v\n", len(list.Items), list.Items[0].Spec, podIn.Spec) } } diff --git a/pkg/standalone/standalone.go b/pkg/standalone/standalone.go index b9af29ac77a..88edd9b3b6f 100644 --- a/pkg/standalone/standalone.go +++ b/pkg/standalone/standalone.go @@ -18,12 +18,12 @@ package standalone import ( "fmt" - "math" "net" "net/http" "time" "github.com/GoogleCloudPlatform/kubernetes/pkg/api" + "github.com/GoogleCloudPlatform/kubernetes/pkg/api/resource" "github.com/GoogleCloudPlatform/kubernetes/pkg/apiserver" "github.com/GoogleCloudPlatform/kubernetes/pkg/client" minionControllerPkg "github.com/GoogleCloudPlatform/kubernetes/pkg/cloudprovider/controller" @@ -32,7 +32,6 @@ import ( "github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet/config" "github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet/dockertools" "github.com/GoogleCloudPlatform/kubernetes/pkg/master" - "github.com/GoogleCloudPlatform/kubernetes/pkg/resources" "github.com/GoogleCloudPlatform/kubernetes/pkg/service" "github.com/GoogleCloudPlatform/kubernetes/pkg/tools" "github.com/GoogleCloudPlatform/kubernetes/pkg/util" @@ -99,22 +98,10 @@ func RunScheduler(cl *client.Client) { // RunControllerManager starts a controller func RunControllerManager(machineList []string, cl *client.Client, nodeMilliCPU, nodeMemory int64) { - if int64(int(nodeMilliCPU)) != nodeMilliCPU { - glog.Warningf("node_milli_cpu is too big for platform. Clamping: %d -> %d", - nodeMilliCPU, math.MaxInt32) - nodeMilliCPU = math.MaxInt32 - } - - if int64(int(nodeMemory)) != nodeMemory { - glog.Warningf("node_memory is too big for platform. Clamping: %d -> %d", - nodeMemory, math.MaxInt32) - nodeMemory = math.MaxInt32 - } - nodeResources := &api.NodeResources{ Capacity: api.ResourceList{ - resources.CPU: util.NewIntOrStringFromInt(int(nodeMilliCPU)), - resources.Memory: util.NewIntOrStringFromInt(int(nodeMemory)), + api.ResourceCPU: *resource.NewMilliQuantity(nodeMilliCPU, resource.DecimalSI), + api.ResourceMemory: *resource.NewQuantity(nodeMemory, resource.BinarySI), }, } minionController := minionControllerPkg.NewMinionController(nil, "", machineList, nodeResources, cl) From 2050131b9a779be36d0821eaed0c851d6e416edb Mon Sep 17 00:00:00 2001 From: Daniel Smith Date: Tue, 6 Jan 2015 17:20:01 -0800 Subject: [PATCH 6/6] ParseOrDie -> MustParse; stop returning pointer --- pkg/api/resource/quantity.go | 15 ++++++-------- pkg/api/resource/quantity_example_test.go | 10 +++++----- pkg/api/validation/validation_test.go | 24 +++++++++++------------ pkg/client/client_test.go | 4 ++-- pkg/cloudprovider/openstack/openstack.go | 6 +++--- pkg/scheduler/priorities_test.go | 8 ++++---- 6 files changed, 32 insertions(+), 35 deletions(-) diff --git a/pkg/api/resource/quantity.go b/pkg/api/resource/quantity.go index c92f47ff74a..4ae4d38148c 100644 --- a/pkg/api/resource/quantity.go +++ b/pkg/api/resource/quantity.go @@ -102,14 +102,14 @@ const ( DecimalSI = Format("DecimalSI") // e.g., 12M (12 * 10^6) ) -// ParseOrDie turns the given string into a quantity or panics; for tests +// MustParse turns the given string into a quantity or panics; for tests // or others cases where you know the string is valid. -func ParseOrDie(str string) *Quantity { +func MustParse(str string) Quantity { q, err := ParseQuantity(str) if err != nil { panic(fmt.Errorf("cannot parse '%v': %v", str, err)) } - return q + return *q } const ( @@ -403,10 +403,7 @@ func (qf qFlag) String() string { // QuantityFlag is a helper that makes a quantity flag (using standard flag package). // Will panic if defaultValue is not a valid quantity. func QuantityFlag(flagName, defaultValue, description string) *Quantity { - q, err := ParseQuantity(defaultValue) - if err != nil { - panic(fmt.Errorf("can't use %v as a quantity: %v", defaultValue, err)) - } - flag.Var(qFlag{q}, flagName, description) - return q + q := MustParse(defaultValue) + flag.Var(qFlag{&q}, flagName, description) + return &q } diff --git a/pkg/api/resource/quantity_example_test.go b/pkg/api/resource/quantity_example_test.go index 5442cb546c4..f9cbe862f5b 100644 --- a/pkg/api/resource/quantity_example_test.go +++ b/pkg/api/resource/quantity_example_test.go @@ -38,17 +38,17 @@ func ExampleFormat() { // cores = 5300m } -func ExampleParseOrDie() { - memorySize := resource.ParseOrDie("5Gi") +func ExampleMustParse() { + memorySize := resource.MustParse("5Gi") fmt.Printf("memorySize = %v (%v)\n", memorySize.Value(), memorySize.Format) - diskSize := resource.ParseOrDie("5G") + diskSize := resource.MustParse("5G") fmt.Printf("diskSize = %v (%v)\n", diskSize.Value(), diskSize.Format) - cores := resource.ParseOrDie("5300m") + cores := resource.MustParse("5300m") fmt.Printf("milliCores = %v (%v)\n", cores.MilliValue(), cores.Format) - cores2 := resource.ParseOrDie("5.4") + cores2 := resource.MustParse("5.4") fmt.Printf("milliCores = %v (%v)\n", cores2.MilliValue(), cores2.Format) // Output: diff --git a/pkg/api/validation/validation_test.go b/pkg/api/validation/validation_test.go index 3cb6f183331..827aa40ad53 100644 --- a/pkg/api/validation/validation_test.go +++ b/pkg/api/validation/validation_test.go @@ -373,8 +373,8 @@ func TestValidateManifest(t *testing.T) { Image: "image", Command: []string{"foo", "bar"}, WorkingDir: "/tmp", - Memory: *resource.Q("1"), - CPU: *resource.Q("1"), + Memory: resource.MustParse("1"), + CPU: resource.MustParse("1"), Ports: []api.Port{ {Name: "p1", ContainerPort: 80, HostPort: 8080}, {Name: "p2", ContainerPort: 81}, @@ -624,7 +624,7 @@ func TestValidatePodUpdate(t *testing.T) { Containers: []api.Container{ { Image: "foo:V1", - CPU: *resource.Q("100m"), + CPU: resource.MustParse("100m"), }, }, }, @@ -635,7 +635,7 @@ func TestValidatePodUpdate(t *testing.T) { Containers: []api.Container{ { Image: "foo:V2", - CPU: *resource.Q("1000m"), + CPU: resource.MustParse("1000m"), }, }, }, @@ -1301,8 +1301,8 @@ func TestValidateMinionUpdate(t *testing.T) { }, Spec: api.NodeSpec{ Capacity: api.ResourceList{ - api.ResourceCPU: *resource.Q("10000"), - api.ResourceMemory: *resource.Q("100"), + api.ResourceCPU: resource.MustParse("10000"), + api.ResourceMemory: resource.MustParse("100"), }, }, }, api.Node{ @@ -1311,8 +1311,8 @@ func TestValidateMinionUpdate(t *testing.T) { }, Spec: api.NodeSpec{ Capacity: api.ResourceList{ - api.ResourceCPU: *resource.Q("100"), - api.ResourceMemory: *resource.Q("10000"), + api.ResourceCPU: resource.MustParse("100"), + api.ResourceMemory: resource.MustParse("10000"), }, }, }, true}, @@ -1323,8 +1323,8 @@ func TestValidateMinionUpdate(t *testing.T) { }, Spec: api.NodeSpec{ Capacity: api.ResourceList{ - api.ResourceCPU: *resource.Q("10000"), - api.ResourceMemory: *resource.Q("100"), + api.ResourceCPU: resource.MustParse("10000"), + api.ResourceMemory: resource.MustParse("100"), }, }, }, api.Node{ @@ -1334,8 +1334,8 @@ func TestValidateMinionUpdate(t *testing.T) { }, Spec: api.NodeSpec{ Capacity: api.ResourceList{ - api.ResourceCPU: *resource.Q("100"), - api.ResourceMemory: *resource.Q("10000"), + api.ResourceCPU: resource.MustParse("100"), + api.ResourceMemory: resource.MustParse("10000"), }, }, }, true}, diff --git a/pkg/client/client_test.go b/pkg/client/client_test.go index 4283d5217aa..c4d01d717f7 100644 --- a/pkg/client/client_test.go +++ b/pkg/client/client_test.go @@ -734,8 +734,8 @@ func TestCreateMinion(t *testing.T) { }, Spec: api.NodeSpec{ Capacity: api.ResourceList{ - api.ResourceCPU: *resource.Q("1000m"), - api.ResourceMemory: *resource.Q("1Mi"), + api.ResourceCPU: resource.MustParse("1000m"), + api.ResourceMemory: resource.MustParse("1Mi"), }, }, } diff --git a/pkg/cloudprovider/openstack/openstack.go b/pkg/cloudprovider/openstack/openstack.go index f546b2baf40..4e5947d6cca 100644 --- a/pkg/cloudprovider/openstack/openstack.go +++ b/pkg/cloudprovider/openstack/openstack.go @@ -170,10 +170,10 @@ func (os *OpenStack) Instances() (cloudprovider.Instances, bool) { rsrc := api.NodeResources{ Capacity: api.ResourceList{ api.ResourceCPU: *resource.NewMilliQuantity(int64(flavor.VCPUs*1000), resource.DecimalSI), - api.ResourceMemory: *resource.Q(fmt.Sprintf("%dMi", flavor.RAM)), - "openstack.org/disk": *resource.Q(fmt.Sprintf("%dG", flavor.Disk)), + api.ResourceMemory: resource.MustParse(fmt.Sprintf("%dMi", flavor.RAM)), + "openstack.org/disk": resource.MustParse(fmt.Sprintf("%dG", flavor.Disk)), "openstack.org/rxTxFactor": *resource.NewQuantity(int64(flavor.RxTxFactor*1000), resource.DecimalSI), - "openstack.org/swap": *resource.Q(fmt.Sprintf("%dMiB", flavor.Swap)), + "openstack.org/swap": resource.MustParse(fmt.Sprintf("%dMi", flavor.Swap)), }, } flavor_to_resource[flavor.ID] = &rsrc diff --git a/pkg/scheduler/priorities_test.go b/pkg/scheduler/priorities_test.go index 1081709d90c..23f932e0a0d 100644 --- a/pkg/scheduler/priorities_test.go +++ b/pkg/scheduler/priorities_test.go @@ -56,14 +56,14 @@ func TestLeastRequested(t *testing.T) { } cpuOnly := api.PodSpec{ Containers: []api.Container{ - {CPU: *resource.Q("1000m")}, - {CPU: *resource.Q("2000m")}, + {CPU: resource.MustParse("1000m")}, + {CPU: resource.MustParse("2000m")}, }, } cpuAndMemory := api.PodSpec{ Containers: []api.Container{ - {CPU: *resource.Q("1000m"), Memory: *resource.Q("2000")}, - {CPU: *resource.Q("2000m"), Memory: *resource.Q("3000")}, + {CPU: resource.MustParse("1000m"), Memory: resource.MustParse("2000")}, + {CPU: resource.MustParse("2000m"), Memory: resource.MustParse("3000")}, }, } tests := []struct {