forked from easierway/service_decorators
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathchaos_decorator.go
102 lines (95 loc) · 3.27 KB
/
chaos_decorator.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
package service_decorators
import (
"encoding/json"
"errors"
"math/rand"
"sync/atomic"
"time"
)
// ChaosEngineeringConfig is the configuration.
type ChaosEngineeringConfig struct {
IsToInjectChaos bool `json:"IsToInjectChaos"` //Is it to start chaos injection, if it is false, all chaos injects (chaos function, slow response) will be stopped.
AdditionalResponseTime int `json:"AdditionalResponseTime"` //Inject additional time spent to simulate slow response.
ChaosRate int `json:"ChaosRate"` //The proportion of the chaos response, the range is 0-100
}
// ChaosEngineeringDecorator is to inject the failure for Chaos Engineering
type ChaosEngineeringDecorator struct {
config atomic.Value
chaosResponseFn ServiceFunc
configStorage *ConfigStorage
}
func getChaosConfigFromStorage(configStorage ConfigStorage,
configName string) (*ChaosEngineeringConfig, error) {
configStr, err := configStorage.Get(configName)
config := ChaosEngineeringConfig{
IsToInjectChaos: false,
AdditionalResponseTime: 0,
ChaosRate: 0,
}
if err != nil {
return &config, err
}
err = json.Unmarshal([]byte(configStr), &config)
if err != nil {
return &ChaosEngineeringConfig{
IsToInjectChaos: false,
AdditionalResponseTime: 0,
ChaosRate: 0,
}, err
}
if config.ChaosRate < 0 || config.ChaosRate > 100 {
return &ChaosEngineeringConfig{
IsToInjectChaos: false,
AdditionalResponseTime: 0,
ChaosRate: 0,
}, errors.New("The value of ChaosRate should be in [0,100].")
}
return &config, nil
}
// CreateChaosEngineeringDecorator is to create a CChaosEngineeringDecorator
// configStore: the storage is used to store the chaos configurations
// configName: the config name in the storage
// chaosResponseFn: the function is to inject the failure for chaos engineering
func CreateChaosEngineeringDecorator(configStorage ConfigStorage, configName string,
chaosResponseFn ServiceFunc,
refreshInterval time.Duration) (*ChaosEngineeringDecorator, error) {
config, err := getChaosConfigFromStorage(configStorage, configName)
dec := ChaosEngineeringDecorator{}
dec.config.Store(config)
dec.chaosResponseFn = chaosResponseFn
go dec.refreshConfig(refreshInterval, configStorage, configName)
return &dec, err
}
// Decorate function is to add chaos engineering logic to the function
func (dec *ChaosEngineeringDecorator) Decorate(innerFn ServiceFunc) ServiceFunc {
return func(req Request) (Response, error) {
config, ok := dec.config.Load().(*ChaosEngineeringConfig)
if !ok || config == nil {
return innerFn(req)
}
if !config.IsToInjectChaos {
return innerFn(req)
}
reqSeri := rand.Intn(99) + 1
if reqSeri <= config.ChaosRate {
if config.AdditionalResponseTime > 0 {
time.Sleep(time.Duration(config.AdditionalResponseTime) * time.Millisecond)
}
if dec.chaosResponseFn != nil {
return dec.chaosResponseFn(req)
}
}
return innerFn(req)
}
}
func (dec *ChaosEngineeringDecorator) refreshConfig(
refreshInterval time.Duration, configStorage ConfigStorage,
configName string) {
if refreshInterval <= 0 {
return
}
for _ = range time.Tick(refreshInterval) {
updatedConfig, _ := getChaosConfigFromStorage(configStorage, configName)
dec.config.Store(updatedConfig)
}
}