Skip to content
This repository has been archived by the owner on Jan 12, 2023. It is now read-only.

Commit

Permalink
Tests and minor refactorings
Browse files Browse the repository at this point in the history
  • Loading branch information
alpe committed Feb 11, 2020
1 parent ce4fbc1 commit f68476e
Show file tree
Hide file tree
Showing 3 changed files with 176 additions and 12 deletions.
18 changes: 8 additions & 10 deletions cmd/evicter/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,15 @@ type podProvisioner interface {
}

type Controller struct {
podStore cache.Indexer
podStore cache.Store
queue workqueue.RateLimitingInterface
informer cache.Controller
podProvisioner podProvisioner
incubationPeriodSeconds time.Duration
started time.Time
}

func NewController(queue workqueue.RateLimitingInterface, indexer cache.Indexer, informer cache.Controller, podProvisioner podProvisioner, incubationPeriodSeconds int64) *Controller {
func NewController(queue workqueue.RateLimitingInterface, indexer cache.Store, informer cache.Controller, podProvisioner podProvisioner, incubationPeriodSeconds int64) *Controller {
return &Controller{
informer: informer,
podStore: indexer,
Expand All @@ -53,9 +53,9 @@ const (
// annotationPreventEviction is a break-glass annotation to prevent automated eviction
annotationPreventEviction = "k-rail/tainted-prevent-eviction"
// annotationTimestamp stores the unix timestamp when the root event happened
annotationTimestamp = "k-rail/tainted-timestamp"
annotationTimestamp = "k-rail/tainted-timestamp"
// annotationReason is used to define any additional reason in a human readable form
annotationReason = "k-rail/tainted-reason"
annotationReason = "k-rail/tainted-reason"
)

const defaultEvictionReason = "Tainted"
Expand Down Expand Up @@ -89,14 +89,12 @@ func canEvict(pod *v1.Pod, incubationPeriod time.Duration) bool {
if pod == nil {
return false
}
val, ok := pod.Annotations[annotationPreventEviction]
if ok {
if val == "yes" || val == "true" {
return false
}
switch pod.Annotations[annotationPreventEviction] {
case "yes", "true", "1", "YES", "TRUE", "Yes", "True":
return false
}

val, ok = pod.Annotations[annotationTimestamp]
val, ok := pod.Annotations[annotationTimestamp]
if ok {
i, err := strconv.ParseInt(val, 10, 64)
if err != nil {
Expand Down
167 changes: 167 additions & 0 deletions cmd/evicter/controller_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
package main

import (
"reflect"
"strconv"
"testing"
"time"

v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/tools/cache"
)

func TestCanEvict(t *testing.T) {
now := int(time.Now().Unix())
specs := map[string]struct {
srcAnn map[string]string
expResult bool
}{
"with timestamp after incubation period": {
srcAnn: map[string]string{
"k-rail/tainted-timestamp": strconv.Itoa(now - 1),
"k-rail/tainted-reason": "test",
},
expResult: true,
},
"with timestamp in incubation period": {
srcAnn: map[string]string{
"k-rail/tainted-timestamp": strconv.Itoa(now),
"k-rail/tainted-reason": "test",
},
expResult: false,
},
"without timestamp annotation": {
srcAnn: map[string]string{
"k-rail/tainted-reason": "test",
},
expResult: true,
},
"with timestamp containing non timestamp string": {
srcAnn: map[string]string{
"k-rail/tainted-timestamp": "",
"k-rail/tainted-reason": "test",
},
expResult: true,
},
"with preventEviction annotation": {
srcAnn: map[string]string{
"k-rail/tainted-timestamp": strconv.Itoa(now - 1),
"k-rail/tainted-reason": "test",
"k-rail/tainted-prevent-eviction": "true",
},
expResult: false,
},
"with preventEviction annotation - uppercase": {
srcAnn: map[string]string{
"k-rail/tainted-timestamp": strconv.Itoa(now - 1),
"k-rail/tainted-reason": "test",
"k-rail/tainted-prevent-eviction": "TRUE",
},
expResult: false,
},
"with preventEviction annotation - yes": {
srcAnn: map[string]string{
"k-rail/tainted-timestamp": strconv.Itoa(now - 1),
"k-rail/tainted-reason": "test",
"k-rail/tainted-prevent-eviction": "yes",
},
expResult: false,
},
"with preventEviction annotation - non bool": {
srcAnn: map[string]string{
"k-rail/tainted-timestamp": strconv.Itoa(now - 1),
"k-rail/tainted-reason": "test",
"k-rail/tainted-prevent-eviction": "",
},
expResult: true,
},
}
for msg, spec := range specs {
t.Run(msg, func(t *testing.T) {
pod := v1.Pod{ObjectMeta: metav1.ObjectMeta{Annotations: spec.srcAnn}}
if got := canEvict(&pod, time.Second); spec.expResult != got {
t.Errorf("expected %v but got %v", spec.expResult, got)
}
})
}
}

func TestEvictPod(t *testing.T) {
now := int(time.Now().Unix())

specs := map[string]struct {
srcAnn map[string]string
expReason string
expMsg string
expNoEviction bool
}{
"evicted with custom reason": {
srcAnn: map[string]string{
"k-rail/tainted-timestamp": strconv.Itoa(now - 1),
"k-rail/tainted-reason": "test",
},
expReason: "Tainted",
expMsg: "test",
},
"evicted with default reason": {
srcAnn: map[string]string{
"k-rail/tainted-timestamp": strconv.Itoa(now - 1),
},
expReason: "Tainted",
expMsg: noEvictionNote,
},
"not evicted with annotation": {
srcAnn: map[string]string{
"k-rail/tainted-timestamp": strconv.Itoa(now - 1),
"k-rail/tainted-prevent-eviction": "yes",
},
expNoEviction: true,
},
}
for msg, spec := range specs {
t.Run(msg, func(t *testing.T) {
store := cache.NewStore(cache.MetaNamespaceKeyFunc)
pod := &v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "myPod", Annotations: spec.srcAnn}}
store.Add(pod)
prov := &recordingPodProvisioner{}
c := NewController(nil, store, nil, prov, 1)
// when
err := c.evictPod("myPod")
// then
if err != nil {
t.Fatalf("unexpected error: %+v", err)
}
if spec.expNoEviction {
if prov.evictedPods != nil {
t.Fatalf("expected no call but got %v", prov.evictedPods)
}
return
}
// there should be 1 call
if exp, got := []*v1.Pod{pod}, prov.evictedPods; !reflect.DeepEqual(exp, got) {
t.Errorf("expected %v but got %v", exp, got)
}
if exp, got := []string{spec.expReason}, prov.reasons; !reflect.DeepEqual(exp, got) {
t.Errorf("expected %v but got %v", exp, got)
}
if exp, got := []string{spec.expMsg}, prov.msgs; !reflect.DeepEqual(exp, got) {
t.Errorf("expected %v but got %v", exp, got)
}
})
}
}

type recordingPodProvisioner struct {
evictedPods []*v1.Pod
reasons []string
msgs []string
result error
}

func (r *recordingPodProvisioner) Evict(pod *v1.Pod, reason, msg string) error {
r.evictedPods = append(r.evictedPods, pod)
r.reasons = append(r.reasons, reason)
r.msgs = append(r.msgs, msg)
return r.result
}
3 changes: 1 addition & 2 deletions cmd/evicter/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ import (
"k8s.io/klog"
)


func main() {
var (
kubeconfig = flag.String("kubeconfig", "", "absolute path to the kubeconfig file: `<home>/.kube/config`")
Expand Down Expand Up @@ -165,7 +164,7 @@ func (p *podEvicter) Evict(pod *v1.Pod, reason, msg string) error {
if err != nil {
return errors.Wrap(err, "eviction")
}
p.eventRecorder.Eventf(pod, v1.EventTypeNormal, reason, msg)
p.eventRecorder.Eventf(pod, v1.EventTypeNormal, reason, msg)
return nil
}

Expand Down

0 comments on commit f68476e

Please sign in to comment.