/
schedule.go
113 lines (92 loc) · 3.04 KB
/
schedule.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
package schedule
import (
"fmt"
"math/rand"
"os"
"strings"
"time"
"github.com/golang/glog"
"github.com/asobti/kube-monkey/calendar"
"github.com/asobti/kube-monkey/chaos"
"github.com/asobti/kube-monkey/config"
"github.com/asobti/kube-monkey/victims/factory"
)
const (
Today = "\t********** Today's schedule **********"
KubeMonkeyID = "\tKubeMonkey ID: %s"
NoTermination = "\tNo terminations scheduled"
HeaderRow = "\tk8 Api Kind\tKind Namespace\tKind Name\t\tTermination Time"
SepRow = "\t-----------\t--------------\t---------\t\t----------------"
RowFormat = "\t%s\t%s\t%s\t\t%s"
DateFormat = "01/02/2006 15:04:05 -0700 MST"
End = "\t********** End of schedule **********"
)
type Schedule struct {
entries []*chaos.Chaos
}
func (s *Schedule) Entries() []*chaos.Chaos {
return s.entries
}
func (s *Schedule) Add(entry *chaos.Chaos) {
s.entries = append(s.entries, entry)
}
func (s *Schedule) String() string {
schedString := []string{}
schedString = append(schedString, fmt.Sprint(Today))
kubeMonkeyID := os.Getenv("KUBE_MONKEY_ID")
if kubeMonkeyID != "" {
schedString = append(schedString, fmt.Sprintf(KubeMonkeyID, kubeMonkeyID))
}
if len(s.entries) == 0 {
schedString = append(schedString, fmt.Sprint(NoTermination))
} else {
schedString = append(schedString, fmt.Sprint(HeaderRow))
schedString = append(schedString, fmt.Sprint(SepRow))
for _, chaos := range s.entries {
schedString = append(schedString, fmt.Sprintf(RowFormat, chaos.Victim().Kind(), chaos.Victim().Namespace(), chaos.Victim().Name(), chaos.KillAt().Format(DateFormat)))
}
}
schedString = append(schedString, fmt.Sprint(End))
return strings.Join(schedString, "\n")
}
func (s Schedule) Print() {
glog.V(4).Infof("Status Update: %v terminations scheduled today", len(s.entries))
for _, chaos := range s.entries {
glog.V(4).Infof("%s %s scheduled for termination at %s", chaos.Victim().Kind(), chaos.Victim().Name(), chaos.KillAt().Format(DateFormat))
}
}
func New() (*Schedule, error) {
glog.V(3).Info("Status Update: Generating schedule for terminations")
victims, err := factory.EligibleVictims()
if err != nil {
return nil, err
}
schedule := &Schedule{
entries: []*chaos.Chaos{},
}
for _, victim := range victims {
killtime := CalculateKillTime()
if ShouldScheduleChaos(victim.Mtbf()) {
schedule.Add(chaos.New(killtime, victim))
}
}
return schedule, nil
}
func CalculateKillTime() time.Time {
loc := config.Timezone()
if config.DebugEnabled() && config.DebugScheduleImmediateKill() {
r := rand.New(rand.NewSource(time.Now().UnixNano()))
// calculate a second-offset in the next minute
secOffset := r.Intn(60)
return time.Now().In(loc).Add(time.Duration(secOffset) * time.Second)
}
return calendar.RandomTimeInRange(config.StartHour(), config.EndHour(), loc)
}
func ShouldScheduleChaos(mtbf int) bool {
if config.DebugEnabled() && config.DebugForceShouldKill() {
return true
}
r := rand.New(rand.NewSource(time.Now().UnixNano()))
probability := 1 / float64(mtbf)
return probability > r.Float64()
}