Add first version of node-problem-detector

This commit is contained in:
Lantao Liu
2016-05-17 15:55:33 -07:00
parent 802acee7e3
commit f0312655bd
31 changed files with 2370 additions and 0 deletions
+134
View File
@@ -0,0 +1,134 @@
/*
Copyright 2016 The Kubernetes Authors All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package condition
import (
"reflect"
"sync"
"time"
"k8s.io/node-problem-detector/pkg/problemclient"
"k8s.io/node-problem-detector/pkg/types"
problemutil "k8s.io/node-problem-detector/pkg/util"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/util"
"github.com/golang/glog"
)
const (
// updatePeriod is the period which condition manager checks update.
updatePeriod = 1 * time.Second
// updateTimeout is the timeout of condition update operation.
updateTimeout = 10 * time.Second
// resyncPeriod is the period which condition manager does resync no matter whether these is any update.
resyncPeriod = 30 * time.Second
)
// ConditionManager synchronizes node conditions with the apiserver with problem client.
// It makes sure that:
// 1) Node conditions are updated to apiserver as soon as possible.
// 2) Node problem detector won't flood apiserver.
// 3) No one else could change the node conditions maintained by node problem detector.
// ConditionManager checks every updatePeriod to see whether there is node condition update. If there are any,
// it will synchronize with the apiserver. This addresses 1) and 2).
// ConditionManager synchronizes with apiserver every resyncPeriod no matter there is node condition update or
// not. This addresses 3).
type ConditionManager interface {
// Start starts the condition manager.
Start()
// UpdateCondition update specific condition.
UpdateCondition(types.Condition)
}
type conditionManager struct {
sync.Mutex
clock util.Clock
latest time.Time
client problemclient.Client
updates map[string]types.Condition
conditions map[string]types.Condition
}
// NewConditionManager creates a condition manager.
func NewConditionManager(client problemclient.Client, clock util.Clock) ConditionManager {
return &conditionManager{
client: client,
clock: clock,
updates: make(map[string]types.Condition),
conditions: make(map[string]types.Condition),
}
}
func (c *conditionManager) Start() {
go c.syncLoop()
}
func (c *conditionManager) UpdateCondition(condition types.Condition) {
c.Lock()
defer c.Unlock()
// New node condition will override the old condition, because we only need the newest
// condition for each condition type.
c.updates[condition.Type] = condition
}
func (c *conditionManager) syncLoop() {
updateCh := c.clock.Tick(updatePeriod)
for {
select {
case <-updateCh:
if c.checkUpdates() || c.checkResync() {
c.sync()
}
}
}
}
// checkUpdates checks whether there are recent updates.
func (c *conditionManager) checkUpdates() bool {
c.Lock()
defer c.Unlock()
needUpdate := false
for t, update := range c.updates {
if !reflect.DeepEqual(c.conditions[t], update) {
needUpdate = true
c.conditions[t] = update
}
delete(c.updates, t)
}
return needUpdate
}
// checkResync checks whether a resync is needed.
func (c *conditionManager) checkResync() bool {
return c.clock.Now().Sub(c.latest) >= resyncPeriod
}
// sync synchronizes node conditions with the apiserver.
func (c *conditionManager) sync() {
conditions := []api.NodeCondition{}
for i := range c.conditions {
conditions = append(conditions, problemutil.ConvertToAPICondition(c.conditions[i]))
}
if err := c.client.SetConditions(conditions, updateTimeout); err != nil {
// The conditions will be updated again in future sync
glog.Errorf("failed to update node conditions: %v", err)
return
}
c.latest = c.clock.Now()
}
+81
View File
@@ -0,0 +1,81 @@
/*
Copyright 2016 The Kubernetes Authors All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package condition
import (
"reflect"
"testing"
"time"
"k8s.io/node-problem-detector/pkg/problemclient"
"k8s.io/node-problem-detector/pkg/types"
problemutil "k8s.io/node-problem-detector/pkg/util"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/util"
)
func newTestManager() (*conditionManager, *problemclient.FakeProblemClient, *util.FakeClock) {
fakeClient := problemclient.NewFakeProblemClient()
fakeClock := util.NewFakeClock(time.Now())
manager := NewConditionManager(fakeClient, fakeClock)
return manager.(*conditionManager), fakeClient, fakeClock
}
func newTestCondition() types.Condition {
return types.Condition{
Type: "TestCondition",
Status: true,
Transition: time.Now(),
Reason: "TestReason",
Message: "test message",
}
}
func TestCheckUpdates(t *testing.T) {
condition := newTestCondition()
m, _, _ := newTestManager()
m.UpdateCondition(condition)
if !m.checkUpdates() {
t.Error("expected checkUpdates to be true, got false")
}
if !reflect.DeepEqual(condition, m.conditions[condition.Type]) {
t.Errorf("expected %+v, got %+v", condition, m.conditions[condition.Type])
}
if m.checkUpdates() {
t.Error("expected checkUpdates to be false, got true")
}
}
func TestSync(t *testing.T) {
m, fakeClient, fakeClock := newTestManager()
condition := newTestCondition()
m.conditions = map[string]types.Condition{condition.Type: condition}
m.sync()
expected := []api.NodeCondition{problemutil.ConvertToAPICondition(condition)}
err := fakeClient.AssertConditions(expected)
if err != nil {
t.Error(err)
}
if m.checkResync() {
t.Error("expected checkResync to be false, got true")
}
fakeClock.Step(resyncPeriod)
if !m.checkResync() {
t.Error("expected checkResync to be true, got false")
}
}