Change:
With latest master it can happen that pods get scheduled on minions that are not up and get stuck in state. Also the command now also lists the down minions.
Master now returns all minions, with minion status set. Scheduler hasn't been updated yet. I'll put a fix for this. kubectl should return only running minions, and take a '-a' option to list all minions now. This also needs to be fixed.
, Deyuan Deng wrote:
Yeah, the main concern is that minion status is not as clear as Pod, e.g. running minion may not be eligible to schedule a pod. I'm still trying to figure out the best way to convey the message to client.
Re-open as now list all minions, including down ones. We should probabaly first filter out unhealthy ones in kubectl, before we can show any status.
What do you mean by "filter out"? We should be caching minion status in the apiserver. As proposed, we should show all minions that exist, including unresponsive ones.
I mean do not show down minions for now for consistency (users do not expect down minion to be listed). I agree with what proposed, but I'm trying to figure out how / what we convey the status message to user. NodePhase + NodeConditions are essential for k8s components, but are we going to expose all such data to user?
Why wouldn't users need to see down minions? I would think as an admin i would want to see phase + conditions
nvm My original concern was that phase and conditions are meant to be used by k8s, so we may need some translation to make it easier to understand for admin. Also, they are not fully populated, so I thought we may delay showing status for now. I certainly have no problem showing down minions and adding status. I've created a PR

Discussion:
// Watch minions. // Minions may be listed frequently, so provide a local up-to-date cache. if false { <del> // Disable this code until minions support watches. </del> <ins> // Disable this code until minions support watches. Note when this code is enabled, // we need to make sure minion ListWatcher has proper FieldSelector. </ins> cache.NewReflector(f.createMinionLW(), &api.Node{}, f.MinionLister.Store).Run() } else { cache.NewPoller(f.pollMinions, 10*time.Second, f.MinionLister.Store).Run()

} } <del> // pollMinions lists all minions and returns an enumerator for cache.Poller. </del> <ins> // pollMinions lists all minions and filter out unhealthy ones, then returns // an enumerator for cache.Poller. </ins> func (factory *ConfigFactory) pollMinions() (cache.Enumerator, error) { <del> list := &api.NodeList{} err := factory.Client.Get().Resource("minions").Do().Into(list) </del> <ins> allNodes := &api.NodeList{} err := factory.Client.Get().Resource("minions").Do().Into(allNodes) </ins> if err != nil { return nil, err } <del> return &nodeEnumerator{list}, nil </del> <ins> nodes := &api.NodeList{ TypeMeta: allNodes.TypeMeta, ListMeta: allNodes.ListMeta, } for _, node := range allNodes.Items { conditionMap := make(map[api.NodeConditionKind]*api.NodeCondition) for i := range node.Status.Conditions { cond := node.Status.Conditions[i] conditionMap[cond.Kind] = &cond } if condition, ok := conditionMap[api.NodeReady]; ok { if condition.Status == api.ConditionFull { nodes.Items = append(nodes.Items, node) } } else if condition, ok := conditionMap[api.NodeReachable]; ok { if condition.Status == api.ConditionFull { nodes.Items = append(nodes.Items, node) } } else { // If no condition is set, either node health check is disabled (master // flag "healthCheckMinions" is set to false), or we get unknown condition. // In such cases, we add nodes unconditionally. nodes.Items = append(nodes.Items, node) } } return &nodeEnumerator{nodes}, nil </ins> } func (factory *ConfigFactory) makeDefaultErrorFunc(backoff *podBackoff, podQueue *cache.FIFO) func(pod *api.Pod, err error) {

func TestPollMinions(t *testing.T) { table := []struct { <del> minions []api.Node </del> <ins> minions       []api.Node expectedCount int </ins> }{ { minions: []api.Node{ <del> {ObjectMeta: api.ObjectMeta{Name: "foo"}}, {ObjectMeta: api.ObjectMeta{Name: "bar"}}, </del> <ins> { ObjectMeta: api.ObjectMeta{Name: "foo"}, Status: api.NodeStatus{ Conditions: []api.NodeCondition{ {Kind: api.NodeReady, Status: api.ConditionFull}, }, }, }, { ObjectMeta: api.ObjectMeta{Name: "bar"}, Status: api.NodeStatus{ Conditions: []api.NodeCondition{ {Kind: api.NodeReachable, Status: api.ConditionFull}, }, }, }, { ObjectMeta: api.ObjectMeta{Name: "baz"}, Status: api.NodeStatus{ Conditions: []api.NodeCondition{ {Kind: api.NodeReady, Status: api.ConditionFull}, {Kind: api.NodeReachable, Status: api.ConditionFull}, }, }, }, { ObjectMeta: api.ObjectMeta{Name: "baz"}, Status: api.NodeStatus{ Conditions: []api.NodeCondition{ {Kind: api.NodeReady, Status: api.ConditionFull}, {Kind: api.NodeReady, Status: api.ConditionFull}, }, }, }, </ins> }, <ins> expectedCount: 4, }, { minions: []api.Node{ { ObjectMeta: api.ObjectMeta{Name: "foo"}, Status: api.NodeStatus{ Conditions: []api.NodeCondition{ {Kind: api.NodeReady, Status: api.ConditionFull}, }, }, }, { ObjectMeta: api.ObjectMeta{Name: "bar"}, Status: api.NodeStatus{ Conditions: []api.NodeCondition{ {Kind: api.NodeReady, Status: api.ConditionNone}, }, }, }, }, expectedCount: 1, }, { minions: []api.Node{ { ObjectMeta: api.ObjectMeta{Name: "foo"}, Status: api.NodeStatus{ Conditions: []api.NodeCondition{ {Kind: api.NodeReady, Status: api.ConditionFull}, {Kind: api.NodeReachable, Status: api.ConditionNone}}, }, }, }, expectedCount: 1, }, { minions: []api.Node{ { ObjectMeta: api.ObjectMeta{Name: "foo"}, Status: api.NodeStatus{ Conditions: []api.NodeCondition{ {Kind: api.NodeReachable, Status: api.ConditionFull}, {Kind: "invalidValue", Status: api.ConditionNone}}, }, }, }, expectedCount: 1, }, { minions: []api.Node{ { ObjectMeta: api.ObjectMeta{Name: "foo"}, Status: api.NodeStatus{ Conditions: []api.NodeCondition{}, }, }, }, expectedCount: 1, </ins> }, }

} handler.ValidateRequest(t, "/api/"+testapi.Version()+"/minions", "GET", nil) <del> if e, a := len(item.minions), ce.Len(); e != a { t.Errorf("Expected %v, got %v", e, a) </del> <ins> if a := ce.Len(); item.expectedCount != a { t.Errorf("Expected %v, got %v", item.expectedCount, a) </ins> } } }