Move the internal minion representation to match v1beta3

Moves to 'Spec' and 'Status' internally and removes duplicate
fields.  Moves Capacity into Spec and drops use of NodeResources
This commit is contained in:
Clayton Coleman 2014-11-19 17:39:10 -05:00
parent c688bd402f
commit 156000ef6d
16 changed files with 93 additions and 66 deletions

View File

@ -637,30 +637,42 @@ type EndpointsList struct {
Items []Endpoints `json:"items" yaml:"items"` Items []Endpoints `json:"items" yaml:"items"`
} }
// NodeResources represents resources on a Kubernetes system node // NodeSpec describes the attributes that a node is created with.
type NodeSpec struct {
// Capacity represents the available resources of a node
Capacity ResourceList `json:"capacity,omitempty" yaml:"capacity,omitempty"`
}
// NodeStatus is information about the current status of a node.
type NodeStatus struct {
// Queried from cloud provider, if available.
HostIP string `json:"hostIP,omitempty" yaml:"hostIP,omitempty"`
}
// NodeResources is an object for conveying resource information about a node.
// see https://github.com/GoogleCloudPlatform/kubernetes/blob/master/docs/resources.md for more details. // see https://github.com/GoogleCloudPlatform/kubernetes/blob/master/docs/resources.md for more details.
// TODO: Use ResourceList instead?
type NodeResources struct { type NodeResources struct {
// Capacity represents the available resources. // Capacity represents the available resources of a node
Capacity ResourceList `json:"capacity,omitempty" yaml:"capacity,omitempty"` Capacity ResourceList `json:"capacity,omitempty" yaml:"capacity,omitempty"`
} }
type ResourceName string type ResourceName string
// TODO Replace this with a more complete "Quantity" struct
type ResourceList map[ResourceName]util.IntOrString type ResourceList map[ResourceName]util.IntOrString
// Minion is a worker node in Kubernetenes. // Minion is a worker node in Kubernetenes
// The name of the minion according to etcd is in ID. // The name of the minion according to etcd is in ObjectMeta.Name.
// TODO: Rename to Node
type Minion struct { type Minion struct {
TypeMeta `json:",inline" yaml:",inline"` TypeMeta `json:",inline" yaml:",inline"`
ObjectMeta `json:"metadata,omitempty" yaml:"metadata,omitempty"` ObjectMeta `json:"metadata,omitempty" yaml:"metadata,omitempty"`
// Queried from cloud provider, if available. // Spec defines the behavior of a node.
HostIP string `json:"hostIP,omitempty" yaml:"hostIP,omitempty"` Spec NodeSpec `json:"spec,omitempty" yaml:"spec,omitempty"`
// Resources available on the node
NodeResources NodeResources `json:"resources,omitempty" yaml:"resources,omitempty"` // Status describes the current status of a Node
// Labels for the node Status NodeStatus `json:"status,omitempty" yaml:"status,omitempty"`
Labels map[string]string `json:"labels,omitempty" yaml:"labels,omitempty"`
} }
// MinionList is a list of minions. // MinionList is a list of minions.

View File

@ -498,12 +498,12 @@ func init() {
if err := s.Convert(&in.ObjectMeta, &out.TypeMeta, 0); err != nil { if err := s.Convert(&in.ObjectMeta, &out.TypeMeta, 0); err != nil {
return err return err
} }
if err := s.Convert(&in.Labels, &out.Labels, 0); err != nil { if err := s.Convert(&in.ObjectMeta.Labels, &out.Labels, 0); err != nil {
return err return err
} }
out.HostIP = in.HostIP out.HostIP = in.Status.HostIP
return s.Convert(&in.NodeResources, &out.NodeResources, 0) return s.Convert(&in.Spec.Capacity, &out.NodeResources.Capacity, 0)
}, },
func(in *Minion, out *newer.Minion, s conversion.Scope) error { func(in *Minion, out *newer.Minion, s conversion.Scope) error {
if err := s.Convert(&in.TypeMeta, &out.TypeMeta, 0); err != nil { if err := s.Convert(&in.TypeMeta, &out.TypeMeta, 0); err != nil {
@ -512,12 +512,12 @@ func init() {
if err := s.Convert(&in.TypeMeta, &out.ObjectMeta, 0); err != nil { if err := s.Convert(&in.TypeMeta, &out.ObjectMeta, 0); err != nil {
return err return err
} }
if err := s.Convert(&in.Labels, &out.Labels, 0); err != nil { if err := s.Convert(&in.Labels, &out.ObjectMeta.Labels, 0); err != nil {
return err return err
} }
out.HostIP = in.HostIP out.Status.HostIP = in.HostIP
return s.Convert(&in.NodeResources, &out.NodeResources, 0) return s.Convert(&in.NodeResources.Capacity, &out.Spec.Capacity, 0)
}, },
func(in *newer.BoundPod, out *BoundPod, s conversion.Scope) error { func(in *newer.BoundPod, out *BoundPod, s conversion.Scope) error {

View File

@ -427,12 +427,12 @@ func init() {
if err := s.Convert(&in.ObjectMeta, &out.TypeMeta, 0); err != nil { if err := s.Convert(&in.ObjectMeta, &out.TypeMeta, 0); err != nil {
return err return err
} }
if err := s.Convert(&in.Labels, &out.Labels, 0); err != nil { if err := s.Convert(&in.ObjectMeta.Labels, &out.Labels, 0); err != nil {
return err return err
} }
out.HostIP = in.HostIP out.HostIP = in.Status.HostIP
return s.Convert(&in.NodeResources, &out.NodeResources, 0) return s.Convert(&in.Spec.Capacity, &out.NodeResources.Capacity, 0)
}, },
func(in *Minion, out *newer.Minion, s conversion.Scope) error { func(in *Minion, out *newer.Minion, s conversion.Scope) error {
if err := s.Convert(&in.TypeMeta, &out.TypeMeta, 0); err != nil { if err := s.Convert(&in.TypeMeta, &out.TypeMeta, 0); err != nil {
@ -441,12 +441,12 @@ func init() {
if err := s.Convert(&in.TypeMeta, &out.ObjectMeta, 0); err != nil { if err := s.Convert(&in.TypeMeta, &out.ObjectMeta, 0); err != nil {
return err return err
} }
if err := s.Convert(&in.Labels, &out.Labels, 0); err != nil { if err := s.Convert(&in.Labels, &out.ObjectMeta.Labels, 0); err != nil {
return err return err
} }
out.HostIP = in.HostIP out.Status.HostIP = in.HostIP
return s.Convert(&in.NodeResources, &out.NodeResources, 0) return s.Convert(&in.NodeResources.Capacity, &out.Spec.Capacity, 0)
}, },
func(in *newer.BoundPod, out *BoundPod, s conversion.Scope) error { func(in *newer.BoundPod, out *BoundPod, s conversion.Scope) error {

View File

@ -664,24 +664,22 @@ type EndpointsList struct {
// NodeSpec describes the attributes that a node is created with. // NodeSpec describes the attributes that a node is created with.
type NodeSpec struct { type NodeSpec struct {
// Capacity represents the available resources of a node
// see https://github.com/GoogleCloudPlatform/kubernetes/blob/master/docs/resources.md for more details.
Capacity ResourceList `json:"capacity,omitempty" yaml:"capacity,omitempty"`
} }
// NodeStatus is information about the current status of a node. // NodeStatus is information about the current status of a node.
type NodeStatus struct { type NodeStatus struct {
} // Queried from cloud provider, if available.
HostIP string `json:"hostIP,omitempty" yaml:"hostIP,omitempty"`
// NodeResources represents resources on a Kubernetes system node
// see https://github.com/GoogleCloudPlatform/kubernetes/blob/master/docs/resources.md for more details.
type NodeResources struct {
// Capacity represents the available resources.
Capacity ResourceList `json:"capacity,omitempty" yaml:"capacity,omitempty"`
} }
type ResourceName string type ResourceName string
type ResourceList map[ResourceName]util.IntOrString type ResourceList map[ResourceName]util.IntOrString
// Node is a worker node in Kubernetenes. // Node is a worker node in Kubernetes.
// The name of the node according to etcd is in ID. // The name of the node according to etcd is in ID.
type Node struct { type Node struct {
TypeMeta `json:",inline" yaml:",inline"` TypeMeta `json:",inline" yaml:",inline"`
@ -692,9 +690,6 @@ type Node struct {
// Status describes the current status of a Node // Status describes the current status of a Node
Status NodeStatus `json:"status,omitempty" yaml:"status,omitempty"` Status NodeStatus `json:"status,omitempty" yaml:"status,omitempty"`
// NodeResources describe the resoruces available on the node.
NodeResources NodeResources `json:"resources,omitempty" yaml:"resources,omitempty"`
} }
// NodeList is a list of minions. // NodeList is a list of minions.

View File

@ -544,9 +544,7 @@ func ValidateMinion(minion *api.Minion) errs.ValidationErrorList {
// ValidateMinionUpdate tests to make sure a minion update can be applied. Modifies oldMinion. // ValidateMinionUpdate tests to make sure a minion update can be applied. Modifies oldMinion.
func ValidateMinionUpdate(oldMinion *api.Minion, minion *api.Minion) errs.ValidationErrorList { func ValidateMinionUpdate(oldMinion *api.Minion, minion *api.Minion) errs.ValidationErrorList {
allErrs := errs.ValidationErrorList{} allErrs := errs.ValidationErrorList{}
// TODO: why we need two labels for minion.
oldMinion.Labels = minion.Labels oldMinion.Labels = minion.Labels
oldMinion.ObjectMeta.Labels = minion.ObjectMeta.Labels
if !reflect.DeepEqual(oldMinion, minion) { if !reflect.DeepEqual(oldMinion, minion) {
allErrs = append(allErrs, fmt.Errorf("Update contains more than labels changes")) allErrs = append(allErrs, fmt.Errorf("Update contains more than labels changes"))
} }

View File

@ -1033,14 +1033,20 @@ func TestValidateMinion(t *testing.T) {
invalidSelector := map[string]string{"NoUppercaseOrSpecialCharsLike=Equals": "b"} invalidSelector := map[string]string{"NoUppercaseOrSpecialCharsLike=Equals": "b"}
successCases := []api.Minion{ successCases := []api.Minion{
{ {
ObjectMeta: api.ObjectMeta{Name: "abc"}, ObjectMeta: api.ObjectMeta{
HostIP: "something", Name: "abc",
Labels: validSelector, Labels: validSelector,
}, },
Status: api.NodeStatus{
HostIP: "something",
},
},
{ {
ObjectMeta: api.ObjectMeta{Name: "abc"}, ObjectMeta: api.ObjectMeta{Name: "abc"},
Status: api.NodeStatus{
HostIP: "something", HostIP: "something",
}, },
},
} }
for _, successCase := range successCases { for _, successCase := range successCases {
if errs := ValidateMinion(&successCase); len(errs) != 0 { if errs := ValidateMinion(&successCase); len(errs) != 0 {
@ -1050,14 +1056,20 @@ func TestValidateMinion(t *testing.T) {
errorCases := map[string]api.Minion{ errorCases := map[string]api.Minion{
"zero-length Name": { "zero-length Name": {
ObjectMeta: api.ObjectMeta{Name: ""}, ObjectMeta: api.ObjectMeta{
HostIP: "something", Name: "",
Labels: validSelector, Labels: validSelector,
}, },
Status: api.NodeStatus{
HostIP: "something",
},
},
"invalid-labels": { "invalid-labels": {
ObjectMeta: api.ObjectMeta{Name: "abc-123"}, ObjectMeta: api.ObjectMeta{
Name: "abc-123",
Labels: invalidSelector, Labels: invalidSelector,
}, },
},
} }
for k, v := range errorCases { for k, v := range errorCases {
errs := ValidateMinion(&v) errs := ValidateMinion(&v)

View File

@ -592,8 +592,10 @@ func TestCreateMinion(t *testing.T) {
ObjectMeta: api.ObjectMeta{ ObjectMeta: api.ObjectMeta{
Name: "minion-1", Name: "minion-1",
}, },
Status: api.NodeStatus{
HostIP: "123.321.456.654", HostIP: "123.321.456.654",
NodeResources: api.NodeResources{ },
Spec: api.NodeSpec{
Capacity: api.ResourceList{ Capacity: api.ResourceList{
resources.CPU: util.NewIntOrStringFromInt(1000), resources.CPU: util.NewIntOrStringFromInt(1000),
resources.Memory: util.NewIntOrStringFromInt(1024 * 1024), resources.Memory: util.NewIntOrStringFromInt(1024 * 1024),

View File

@ -71,7 +71,9 @@ func (s *MinionController) SyncStatic(period time.Duration) error {
} }
_, err := s.kubeClient.Minions().Create(&api.Minion{ _, err := s.kubeClient.Minions().Create(&api.Minion{
ObjectMeta: api.ObjectMeta{Name: minionID}, ObjectMeta: api.ObjectMeta{Name: minionID},
NodeResources: *s.staticResources, Spec: api.NodeSpec{
Capacity: s.staticResources.Capacity,
},
}) })
if err == nil { if err == nil {
registered.Insert(minionID) registered.Insert(minionID)
@ -145,7 +147,7 @@ func (s *MinionController) cloudMinions() (*api.MinionList, error) {
resources = s.staticResources resources = s.staticResources
} }
if resources != nil { if resources != nil {
result.Items[i].NodeResources = *resources result.Items[i].Spec.Capacity = resources.Capacity
} }
} }
return result, nil return result, nil

View File

@ -429,7 +429,7 @@ func (m *Master) getServersToValidate(c *Config) map[string]apiserver.Server {
glog.Errorf("Failed to list minions: %v", err) glog.Errorf("Failed to list minions: %v", err)
} }
for ix, node := range nodes.Items { for ix, node := range nodes.Items {
serversToValidate[fmt.Sprintf("node-%d", ix)] = apiserver.Server{Addr: node.HostIP, Port: 10250, Path: "/healthz"} serversToValidate[fmt.Sprintf("node-%d", ix)] = apiserver.Server{Addr: node.Status.HostIP, Port: 10250, Path: "/healthz"}
} }
return serversToValidate return serversToValidate
} }

View File

@ -135,7 +135,7 @@ func (rs *REST) ResourceLocation(ctx api.Context, id string) (string, error) {
if err != nil { if err != nil {
return "", err return "", err
} }
host := minion.HostIP host := minion.Status.HostIP
if host == "" { if host == "" {
host = minion.Name host = minion.Name
} }

View File

@ -130,7 +130,7 @@ func TestMinionStorageInvalidUpdate(t *testing.T) {
if !ok { if !ok {
t.Fatalf("Object is not a minion: %#v", obj) t.Fatalf("Object is not a minion: %#v", obj)
} }
minion.HostIP = "1.2.3.4" minion.Status.HostIP = "1.2.3.4"
if _, err = storage.Update(ctx, minion); err == nil { if _, err = storage.Update(ctx, minion); err == nil {
t.Error("Unexpected non-error.") t.Error("Unexpected non-error.")
} }
@ -163,14 +163,20 @@ func TestMinionStorageValidatesCreate(t *testing.T) {
invalidSelector := map[string]string{"NoUppercaseOrSpecialCharsLike=Equals": "b"} invalidSelector := map[string]string{"NoUppercaseOrSpecialCharsLike=Equals": "b"}
failureCases := map[string]api.Minion{ failureCases := map[string]api.Minion{
"zero-length Name": { "zero-length Name": {
ObjectMeta: api.ObjectMeta{Name: ""}, ObjectMeta: api.ObjectMeta{
HostIP: "something", Name: "",
Labels: validSelector, Labels: validSelector,
}, },
Status: api.NodeStatus{
HostIP: "something",
},
},
"invalid-labels": { "invalid-labels": {
ObjectMeta: api.ObjectMeta{Name: "abc-123"}, ObjectMeta: api.ObjectMeta{
Name: "abc-123",
Labels: invalidSelector, Labels: invalidSelector,
}, },
},
} }
for _, failureCase := range failureCases { for _, failureCase := range failureCases {
c, err := storage.Create(ctx, &failureCase) c, err := storage.Create(ctx, &failureCase)

View File

@ -35,7 +35,7 @@ func MakeMinionList(minions []string, nodeResources api.NodeResources) *api.Mini
} }
for i := range minions { for i := range minions {
list.Items[i].Name = minions[i] list.Items[i].Name = minions[i]
list.Items[i].NodeResources = nodeResources list.Items[i].Spec.Capacity = nodeResources.Capacity
} }
return &list return &list
} }

View File

@ -122,8 +122,8 @@ func (r *ResourceFit) PodFitsResources(pod api.Pod, existingPods []api.Pod, node
} }
// TODO: convert to general purpose resource matching, when pods ask for resources // TODO: convert to general purpose resource matching, when pods ask for resources
totalMilliCPU := int(resources.GetFloatResource(info.NodeResources.Capacity, resources.CPU, 0) * 1000) totalMilliCPU := int(resources.GetFloatResource(info.Spec.Capacity, resources.CPU, 0) * 1000)
totalMemory := resources.GetIntegerResource(info.NodeResources.Capacity, resources.Memory, 0) totalMemory := resources.GetIntegerResource(info.Spec.Capacity, resources.Memory, 0)
fitsCPU := totalMilliCPU == 0 || (totalMilliCPU-milliCPURequested) >= podRequest.milliCPU fitsCPU := totalMilliCPU == 0 || (totalMilliCPU-milliCPURequested) >= podRequest.milliCPU
fitsMemory := totalMemory == 0 || (totalMemory-memoryRequested) >= podRequest.memory fitsMemory := totalMemory == 0 || (totalMemory-memoryRequested) >= podRequest.memory

View File

@ -111,7 +111,7 @@ func TestPodFitsResources(t *testing.T) {
}, },
} }
for _, test := range tests { for _, test := range tests {
node := api.Minion{NodeResources: makeResources(10, 20)} node := api.Minion{Spec: api.NodeSpec{Capacity: makeResources(10, 20).Capacity}}
fit := ResourceFit{FakeNodeInfo(node)} fit := ResourceFit{FakeNodeInfo(node)}
fits, err := fit.PodFitsResources(test.pod, test.existingPods, "machine") fits, err := fit.PodFitsResources(test.pod, test.existingPods, "machine")
@ -335,7 +335,7 @@ func TestPodFitsSelector(t *testing.T) {
}, },
} }
for _, test := range tests { for _, test := range tests {
node := api.Minion{Labels: test.labels} node := api.Minion{ObjectMeta: api.ObjectMeta{Labels: test.labels}}
fit := NodeSelector{FakeNodeInfo(node)} fit := NodeSelector{FakeNodeInfo(node)}
fits, err := fit.PodSelectorMatches(test.pod, []api.Pod{}, "machine") fits, err := fit.PodSelectorMatches(test.pod, []api.Pod{}, "machine")

View File

@ -41,8 +41,8 @@ func calculateOccupancy(node api.Minion, pods []api.Pod) HostPriority {
} }
} }
percentageCPU := calculatePercentage(totalCPU, resources.GetIntegerResource(node.NodeResources.Capacity, resources.CPU, 0)) percentageCPU := calculatePercentage(totalCPU, resources.GetIntegerResource(node.Spec.Capacity, resources.CPU, 0))
percentageMemory := calculatePercentage(totalMemory, resources.GetIntegerResource(node.NodeResources.Capacity, resources.Memory, 0)) percentageMemory := calculatePercentage(totalMemory, resources.GetIntegerResource(node.Spec.Capacity, resources.Memory, 0))
glog.V(4).Infof("Least Requested Priority, AbsoluteRequested: (%d, %d) Percentage:(%d\\%m, %d\\%)", totalCPU, totalMemory, percentageCPU, percentageMemory) glog.V(4).Infof("Least Requested Priority, AbsoluteRequested: (%d, %d) Percentage:(%d\\%m, %d\\%)", totalCPU, totalMemory, percentageCPU, percentageMemory)
return HostPriority{ return HostPriority{

View File

@ -28,7 +28,7 @@ import (
func makeMinion(node string, cpu, memory int) api.Minion { func makeMinion(node string, cpu, memory int) api.Minion {
return api.Minion{ return api.Minion{
ObjectMeta: api.ObjectMeta{Name: node}, ObjectMeta: api.ObjectMeta{Name: node},
NodeResources: api.NodeResources{ Spec: api.NodeSpec{
Capacity: api.ResourceList{ Capacity: api.ResourceList{
resources.CPU: util.NewIntOrStringFromInt(cpu), resources.CPU: util.NewIntOrStringFromInt(cpu),
resources.Memory: util.NewIntOrStringFromInt(memory), resources.Memory: util.NewIntOrStringFromInt(memory),