mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-09-17 07:03:31 +00:00
Sync node status from node controller to master.
This commit is contained in:
@@ -1,125 +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 minion
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/client"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/probe"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/watch"
|
||||
|
||||
"github.com/golang/glog"
|
||||
)
|
||||
|
||||
type HealthyRegistry struct {
|
||||
delegate Registry
|
||||
client client.KubeletHealthChecker
|
||||
cache util.TimeCache
|
||||
}
|
||||
|
||||
func NewHealthyRegistry(delegate Registry, client client.KubeletHealthChecker, clock util.Clock, ttl time.Duration) Registry {
|
||||
h := &HealthyRegistry{
|
||||
delegate: delegate,
|
||||
client: client,
|
||||
}
|
||||
h.cache = util.NewTimeCache(clock, ttl, h.doCheck)
|
||||
return h
|
||||
}
|
||||
|
||||
func (r *HealthyRegistry) GetMinion(ctx api.Context, minionID string) (*api.Node, error) {
|
||||
minion, err := r.delegate.GetMinion(ctx, minionID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return r.checkMinion(minion), nil
|
||||
}
|
||||
|
||||
func (r *HealthyRegistry) DeleteMinion(ctx api.Context, minionID string) error {
|
||||
return r.delegate.DeleteMinion(ctx, minionID)
|
||||
}
|
||||
|
||||
func (r *HealthyRegistry) CreateMinion(ctx api.Context, minion *api.Node) error {
|
||||
return r.delegate.CreateMinion(ctx, minion)
|
||||
}
|
||||
|
||||
func (r *HealthyRegistry) UpdateMinion(ctx api.Context, minion *api.Node) error {
|
||||
return r.delegate.UpdateMinion(ctx, minion)
|
||||
}
|
||||
|
||||
func (r *HealthyRegistry) ListMinions(ctx api.Context) (currentMinions *api.NodeList, err error) {
|
||||
list, err := r.delegate.ListMinions(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// In case the cache is empty, health check in parallel instead of serially.
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(len(list.Items))
|
||||
for i := range list.Items {
|
||||
go func(i int) {
|
||||
list.Items[i] = *r.checkMinion(&list.Items[i])
|
||||
wg.Done()
|
||||
}(i)
|
||||
}
|
||||
wg.Wait()
|
||||
return list, nil
|
||||
}
|
||||
|
||||
func (r *HealthyRegistry) WatchMinions(ctx api.Context, label, field labels.Selector, resourceVersion string) (watch.Interface, error) {
|
||||
w, err := r.delegate.WatchMinions(ctx, label, field, resourceVersion)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return watch.Filter(w, watch.FilterFunc(func(in watch.Event) (watch.Event, bool) {
|
||||
if node, ok := in.Object.(*api.Node); ok && node != nil {
|
||||
in.Object = r.checkMinion(node)
|
||||
}
|
||||
return in, true
|
||||
})), nil
|
||||
}
|
||||
|
||||
func (r *HealthyRegistry) checkMinion(node *api.Node) *api.Node {
|
||||
condition := r.cache.Get(node.Name).(api.NodeConditionStatus)
|
||||
// TODO: distinguish other conditions like Reachable/Live, and begin storing this
|
||||
// data on nodes directly via sync loops.
|
||||
node.Status.Conditions = append(node.Status.Conditions, api.NodeCondition{
|
||||
Kind: api.NodeReady,
|
||||
Status: condition,
|
||||
})
|
||||
return node
|
||||
}
|
||||
|
||||
// This is called to fill the cache.
|
||||
func (r *HealthyRegistry) doCheck(key string) util.T {
|
||||
var nodeStatus api.NodeConditionStatus
|
||||
switch status, err := r.client.HealthCheck(key); {
|
||||
case err != nil:
|
||||
glog.V(2).Infof("HealthyRegistry: node %q health check error: %v", key, err)
|
||||
nodeStatus = api.ConditionUnknown
|
||||
case status == probe.Failure:
|
||||
nodeStatus = api.ConditionNone
|
||||
default:
|
||||
nodeStatus = api.ConditionFull
|
||||
}
|
||||
glog.V(3).Infof("HealthyRegistry: node %q status was %q", key, nodeStatus)
|
||||
return nodeStatus
|
||||
}
|
@@ -1,114 +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 minion
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/probe"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/registry/registrytest"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
|
||||
)
|
||||
|
||||
type alwaysYes struct{}
|
||||
|
||||
func (alwaysYes) HealthCheck(host string) (probe.Status, error) {
|
||||
return probe.Success, nil
|
||||
}
|
||||
|
||||
func TestBasicDelegation(t *testing.T) {
|
||||
ctx := api.NewContext()
|
||||
mockMinionRegistry := registrytest.NewMinionRegistry([]string{"m1", "m2", "m3"}, api.NodeResources{})
|
||||
healthy := NewHealthyRegistry(
|
||||
mockMinionRegistry,
|
||||
alwaysYes{},
|
||||
&util.FakeClock{},
|
||||
60*time.Second,
|
||||
)
|
||||
list, err := healthy.ListMinions(ctx)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
if !reflect.DeepEqual(list, &mockMinionRegistry.Minions) {
|
||||
t.Errorf("Expected %v, Got %v", mockMinionRegistry.Minions, list)
|
||||
}
|
||||
err = healthy.CreateMinion(ctx, &api.Node{
|
||||
ObjectMeta: api.ObjectMeta{Name: "foo"},
|
||||
})
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
minion, err := healthy.GetMinion(ctx, "m1")
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
if minion == nil {
|
||||
t.Errorf("Unexpected absence of 'm1'")
|
||||
}
|
||||
minion, err = healthy.GetMinion(ctx, "m5")
|
||||
if err == nil {
|
||||
t.Errorf("unexpected non-error")
|
||||
}
|
||||
if minion != nil {
|
||||
t.Errorf("Unexpected presence of 'm5'")
|
||||
}
|
||||
}
|
||||
|
||||
type notMinion struct {
|
||||
minion string
|
||||
}
|
||||
|
||||
func (n *notMinion) HealthCheck(host string) (probe.Status, error) {
|
||||
if host != n.minion {
|
||||
return probe.Success, nil
|
||||
} else {
|
||||
return probe.Failure, nil
|
||||
}
|
||||
}
|
||||
|
||||
func TestFiltering(t *testing.T) {
|
||||
ctx := api.NewContext()
|
||||
mockMinionRegistry := registrytest.NewMinionRegistry([]string{"m1", "m2", "m3"}, api.NodeResources{})
|
||||
healthy := NewHealthyRegistry(
|
||||
mockMinionRegistry,
|
||||
¬Minion{minion: "m1"},
|
||||
&util.FakeClock{},
|
||||
60*time.Second,
|
||||
)
|
||||
expected := []string{"m1", "m2", "m3"}
|
||||
list, err := healthy.ListMinions(ctx)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
expectedMinions := registrytest.MakeMinionList(expected, api.NodeResources{})
|
||||
expectedMinions.Items[0].Status.Conditions = []api.NodeCondition{{Kind: api.NodeReady, Status: api.ConditionNone}}
|
||||
expectedMinions.Items[1].Status.Conditions = []api.NodeCondition{{Kind: api.NodeReady, Status: api.ConditionFull}}
|
||||
expectedMinions.Items[2].Status.Conditions = []api.NodeCondition{{Kind: api.NodeReady, Status: api.ConditionFull}}
|
||||
if !reflect.DeepEqual(list, expectedMinions) {
|
||||
t.Errorf("Expected %v, Got %v", expected, list)
|
||||
}
|
||||
minion, err := healthy.GetMinion(ctx, "m1")
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
if minion == nil {
|
||||
t.Errorf("Unexpected empty 'm1'")
|
||||
}
|
||||
}
|
@@ -18,13 +18,11 @@ package minion
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/registry/registrytest"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
|
||||
)
|
||||
|
||||
func TestMinionRegistryREST(t *testing.T) {
|
||||
@@ -89,57 +87,6 @@ func TestMinionRegistryREST(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestMinionRegistryHealthCheck(t *testing.T) {
|
||||
minionRegistry := registrytest.NewMinionRegistry([]string{}, api.NodeResources{})
|
||||
minionHealthRegistry := NewHealthyRegistry(
|
||||
minionRegistry,
|
||||
¬Minion{minion: "m1"},
|
||||
&util.FakeClock{},
|
||||
60*time.Second,
|
||||
)
|
||||
|
||||
ms := NewREST(minionHealthRegistry)
|
||||
ctx := api.NewContext()
|
||||
|
||||
c, err := ms.Create(ctx, &api.Node{ObjectMeta: api.ObjectMeta{Name: "m1"}})
|
||||
if err != nil {
|
||||
t.Fatalf("insert failed: %v", err)
|
||||
}
|
||||
result := <-c
|
||||
if m, ok := result.Object.(*api.Node); !ok || m.Name != "m1" {
|
||||
t.Errorf("insert return value was weird: %#v", result)
|
||||
}
|
||||
if _, err := ms.Get(ctx, "m1"); err != nil {
|
||||
t.Errorf("node is unhealthy, expect no error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func contains(nodes *api.NodeList, nodeID string) bool {
|
||||
for _, node := range nodes.Items {
|
||||
if node.Name == nodeID {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func TestMinionRegistryInvalidUpdate(t *testing.T) {
|
||||
storage := NewREST(registrytest.NewMinionRegistry([]string{"foo", "bar"}, api.NodeResources{}))
|
||||
ctx := api.NewContext()
|
||||
obj, err := storage.Get(ctx, "foo")
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error: %v", err)
|
||||
}
|
||||
minion, ok := obj.(*api.Node)
|
||||
if !ok {
|
||||
t.Fatalf("Object is not a minion: %#v", obj)
|
||||
}
|
||||
minion.Status.HostIP = "1.2.3.4"
|
||||
if _, err = storage.Update(ctx, minion); err == nil {
|
||||
t.Error("Unexpected non-error.")
|
||||
}
|
||||
}
|
||||
|
||||
func TestMinionRegistryValidUpdate(t *testing.T) {
|
||||
storage := NewREST(registrytest.NewMinionRegistry([]string{"foo", "bar"}, api.NodeResources{}))
|
||||
ctx := api.NewContext()
|
||||
@@ -192,3 +139,12 @@ func TestMinionRegistryValidatesCreate(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func contains(nodes *api.NodeList, nodeID string) bool {
|
||||
for _, node := range nodes.Items {
|
||||
if node.Name == nodeID {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
Reference in New Issue
Block a user