14
14
package chaosd
15
15
16
16
import (
17
- "bytes"
18
17
"encoding/json"
19
18
"fmt"
20
19
"io"
21
20
"math/rand"
22
- "net/http"
21
+ "os/exec"
22
+ "strings"
23
23
24
24
"github.com/chaos-mesh/chaosd/pkg/core"
25
25
"github.com/chaos-mesh/chaosd/pkg/server/utils"
@@ -34,17 +34,19 @@ var PatroniAttack AttackType = patroniAttack{}
34
34
func (patroniAttack ) Attack (options core.AttackConfig , _ Environment ) error {
35
35
attack := options .(* core.PatroniCommand )
36
36
37
- candidate := attack . Candidate
37
+ var responce [] byte
38
38
39
- leader := attack .Leader
40
-
41
- var scheduled_at string
42
-
43
- var url string
39
+ var address string
44
40
45
41
values := make (map [string ]string )
46
42
47
- patroniInfo , err := utils .GetPatroniInfo (attack .Address )
43
+ if attack .RemoteMode {
44
+ address = attack .Address
45
+ } else if attack .LocalMode {
46
+ address = "localhost"
47
+ }
48
+
49
+ patroniInfo , err := utils .GetPatroniInfo (address )
48
50
if err != nil {
49
51
err = errors .Errorf ("failed to get patroni info for %v: %v" , options .String (), err )
50
52
return errors .WithStack (err )
@@ -55,75 +57,109 @@ func (patroniAttack) Attack(options core.AttackConfig, _ Environment) error {
55
57
return errors .WithStack (err )
56
58
}
57
59
58
- if candidate == "" {
59
- candidate = patroniInfo .Replicas [rand .Intn (len (patroniInfo .Replicas ))]
60
+ if attack . Candidate == "" {
61
+ values [ " candidate" ] = patroniInfo .Replicas [rand .Intn (len (patroniInfo .Replicas ))]
60
62
}
61
63
62
- if leader == "" {
63
- leader = patroniInfo .Master
64
+ if attack . Leader == "" {
65
+ values [ " leader" ] = patroniInfo .Master
64
66
}
65
67
66
- switch options .String () {
67
- case "switchover" :
68
+ values ["scheduled_at" ] = attack .Scheduled_at
68
69
69
- scheduled_at = attack . Scheduled_at
70
+ cmd := options . String ()
70
71
71
- values = map [string ]string {"leader" : leader , "scheduled_at" : scheduled_at }
72
+ switch cmd {
73
+ case "switchover" :
72
74
73
- log .Info (fmt .Sprintf ("Switchover will be done from %v to another available replica in %v" , patroniInfo . Master , scheduled_at ))
75
+ log .Info (fmt .Sprintf ("Switchover will be done from %v to %v in %v" , values [ "leader" ], values [ "candidate" ], values [ " scheduled_at" ] ))
74
76
75
77
case "failover" :
76
78
77
- values = map [ string ] string { "candidate" : candidate }
79
+ log . Info ( fmt . Sprintf ( "Failover will be done from %v to %v" , values [ "leader" ], values [ " candidate" ]))
78
80
79
- log . Info ( fmt . Sprintf ( "Failover will be done from %v to %v" , patroniInfo . Master , candidate ))
81
+ }
80
82
83
+ if attack .RemoteMode {
84
+ responce , err = execPatroniAttackByRemoteMode (attack , cmd , values )
85
+ if err != nil {
86
+ return err
87
+ }
88
+ } else if attack .LocalMode {
89
+ responce , err = execPatroniAttackByLocalMode (attack , cmd , values )
90
+ if err != nil {
91
+ return err
92
+ }
81
93
}
82
94
83
- patroniAddr := attack .Address
95
+ if attack .RemoteMode {
96
+ log .S ().Infof ("Execute %v successfully: %v" , cmd , string (responce ))
97
+ }
84
98
85
- cmd := options .String ()
99
+ if attack .LocalMode {
100
+ log .S ().Infof ("Execute %v successfully" , cmd )
101
+ fmt .Println (string (responce ))
102
+ }
103
+
104
+ return nil
105
+ }
106
+
107
+ func execPatroniAttackByRemoteMode (attack * core.PatroniCommand , cmd string , values map [string ]string ) ([]byte , error ) {
108
+ patroniAddr := attack .Address
86
109
87
110
data , err := json .Marshal (values )
88
111
if err != nil {
89
112
err = errors .Errorf ("failed to marshal data: %v" , values )
90
- return errors .WithStack (err )
113
+ return nil , errors .WithStack (err )
91
114
}
92
115
93
- url = fmt .Sprintf ("http://%v:8008/%v" , patroniAddr , cmd )
94
-
95
- request , err := http .NewRequest ("POST" , url , bytes .NewBuffer (data ))
116
+ resp , err := utils .MakePostHTTPRequest (patroniAddr , 8008 , cmd , data , attack .User , attack .Password )
96
117
if err != nil {
97
- err = errors .Errorf ("failed to %v: %v" , cmd , err )
98
- return errors .WithStack (err )
118
+ return nil , errors .WithStack (err )
99
119
}
100
120
101
- request .Header .Set ("Content-Type" , "application/json" )
102
- request .SetBasicAuth (attack .User , attack .Password )
103
-
104
- client := & http.Client {}
105
- resp , error := client .Do (request )
106
- if error != nil {
107
- err = errors .Errorf ("failed to %v: %v" , cmd , err )
108
- return errors .WithStack (err )
121
+ if resp .StatusCode != 200 && resp .StatusCode != 202 {
122
+ //to simplify diagnostics
123
+ buf , err := io .ReadAll (resp .Body )
124
+ if err != nil {
125
+ err = errors .Errorf ("failed to read from %s responce: status code %v, responce %v, error %v" , cmd , resp .StatusCode , resp .Body , err )
126
+ return nil , err
127
+ }
128
+ err = errors .Errorf ("failed to exec %v request: status code %v, responce %v" , cmd , resp .StatusCode , buf )
129
+ return nil , errors .WithStack (err )
109
130
}
110
131
111
- defer resp .Body .Close ()
112
-
113
132
buf , err := io .ReadAll (resp .Body )
114
133
if err != nil {
115
- err = errors .Errorf ("failed to read %v responce: %v" , cmd , err )
116
- return errors .WithStack (err )
134
+ err = errors .Errorf ("failed to read %v from %s responce: %v" , resp . Body , cmd , err )
135
+ return nil , errors .WithStack (err )
117
136
}
118
137
119
- if resp .StatusCode != 200 && resp .StatusCode != 202 {
120
- err = errors .Errorf ("failed to %v: status code %v, responce %v" , cmd , resp .StatusCode , string (buf ))
121
- return errors .WithStack (err )
138
+ return buf , nil
139
+ }
140
+
141
+ func execPatroniAttackByLocalMode (attack * core.PatroniCommand , cmd string , values map [string ]string ) ([]byte , error ) {
142
+ var cmdTemplate string
143
+
144
+ if cmd == "failover" {
145
+ cmdTemplate = fmt .Sprintf ("patronictl %v --master %v --candidate %v --force" , cmd , values ["leader" ], values ["candidate" ])
146
+ } else if cmd == "switchover" {
147
+ cmdTemplate = fmt .Sprintf ("patronictl %v --master %v --candidate %v --scheduled %v --force" , cmd , values ["leader" ], values ["candidate" ], values ["scheduled_at" ])
148
+ }
149
+
150
+ execCmd := exec .Command ("bash" , "-c" , cmdTemplate )
151
+ output , err := execCmd .CombinedOutput ()
152
+ if err != nil {
153
+ log .S ().Errorf (fmt .Sprintf ("failed to %v: %v" , cmdTemplate , string (output )))
154
+ return nil , err
122
155
}
123
156
124
- log .S ().Infof ("Execute %v successfully: %v" , cmd , string (buf ))
157
+ if strings .Contains (string (output ), "failed" ) {
158
+ err = errors .New (string (output ))
159
+ return nil , err
160
+ }
125
161
126
- return nil
162
+ return output , nil
127
163
}
128
164
129
165
func (patroniAttack ) Recover (exp core.Experiment , _ Environment ) error {
0 commit comments