-
Notifications
You must be signed in to change notification settings - Fork 20
/
Copy pathapicommands_tests.py
309 lines (266 loc) · 11.3 KB
/
apicommands_tests.py
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
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
import unittest
from test_utils import MockShell
from saboteur.apicommands import build_add_fault_command
from saboteur.voluptuous import Invalid
class TestBase(unittest.TestCase):
def setUp(self):
self.shell = MockShell()
def assertValidAndGeneratesShellCommand(self, params, *expected_shell_commands):
command = build_add_fault_command(self.shell, params)
command.validate()
command.execute()
self.assertEqual(self.shell.commands, list(expected_shell_commands))
def assertValid(self, params):
command = build_add_fault_command(self.shell, params)
command.validate()
def assertInvalid(self, params, expected_field, expected_message):
try:
command = build_add_fault_command(self.shell, params)
command.validate()
raise AssertionError("Expected validation error: " + expected_message)
except Invalid as e:
self.assertEqual(expected_field, str(e.path[0]));
self.assertEquals(expected_message, e.error_message)
class TestBasicFaultCommands(TestBase):
def test_valid_basic_params(self):
self.assertValid({
'name': 'do-some-damage',
'type': 'NETWORK_FAILURE',
'direction': 'IN',
'to_port': 8080
})
def test_invalid_type(self):
self.assertInvalid({
'name': 'do-some-damage',
'type': 'BAD_BALLOON',
'direction': 'OUT',
}, "type", "must be present and one of ['DELAY', 'FIREWALL_TIMEOUT', 'NETWORK_FAILURE', 'PACKET_LOSS', 'SERVICE_FAILURE']")
def test_invalid_direction(self):
self.assertInvalid({
'name': 'do-some-damage',
'type': 'NETWORK_FAILURE',
'direction': 'SIDEWAYS',
}, "direction", "SIDEWAYS is not one of ['IN', 'OUT']")
def test_invalid_to_port(self):
self.assertInvalid({
'name': 'do-some-damage',
'type': 'NETWORK_FAILURE',
'direction': 'IN'
}, "to_port", "required key not provided")
class TestServiceFailure(TestBase):
def test_webserver_shut_down(self):
params = {
'name': "web-server-down",
'type': "SERVICE_FAILURE",
'direction': "IN",
'to_port': 8080,
'protocol': "TCP"
}
self.assertValidAndGeneratesShellCommand(params,
'sudo /sbin/iptables -A INPUT -p TCP -j REJECT --reject-with tcp-reset --dport 8080')
class TestNetworkFailure(TestBase):
def test_isolate_webserver(self):
params = {
'name': 'isolate-web-server',
'type': 'NETWORK_FAILURE',
'direction': 'IN',
'to_port': 80,
'protocol': 'TCP'
}
self.assertValidAndGeneratesShellCommand(params, 'sudo /sbin/iptables -A INPUT -p TCP -j DROP --dport 80')
def test_isolate_udp_server(self):
params = {
'name': "isolate-streaming-server",
'type': "NETWORK_FAILURE",
'direction': "IN",
'to_port': 8111,
'protocol': "UDP"
}
self.assertValidAndGeneratesShellCommand(params, 'sudo /sbin/iptables -A INPUT -p UDP -j DROP --dport 8111')
def test_client_dependency_unreachable(self):
params = {
'name': "connectivity-to-dependency-down",
'type': "NETWORK_FAILURE",
'direction': "OUT",
'to': 'my.dest.host.com',
'to_port': 443,
'protocol': "TCP"
}
self.assertValidAndGeneratesShellCommand(params, 'sudo /sbin/iptables -A OUTPUT -p TCP -j DROP -d my.dest.host.com --dport 443')
def test_specifying_source(self):
params = {
'name': "network-failure-by-source-host",
'type': "NETWORK_FAILURE",
'direction': "IN",
'from': 'my.source.host.com',
'protocol': "TCP",
'to_port': 8111
}
self.assertValidAndGeneratesShellCommand(params, 'sudo /sbin/iptables -A INPUT -p TCP -j DROP -s my.source.host.com --dport 8111')
class TestFirewallTimeout(TestBase):
def test_missing_timeout(self):
self.assertInvalid({
'name': 'effing-firewalls',
'type': 'FIREWALL_TIMEOUT',
'direction': 'IN',
'to_port': 8111
}, "timeout", "required key not provided")
def test_firewall_timeout(self):
params = {
'name': "network-failure-by-source-host",
'type': "FIREWALL_TIMEOUT",
'direction': "IN",
'to_port': 3000,
'protocol': "TCP",
'timeout': 101
}
self.assertValidAndGeneratesShellCommand(params,
'sudo /sbin/iptables -A INPUT -p TCP -j ACCEPT --dport 3000 -m conntrack --ctstate NEW,ESTABLISHED',
'sudo /sbin/iptables -A INPUT -p TCP -j DROP --dport 3000',
'echo 0 | sudo tee /proc/sys/net/netfilter/nf_conntrack_tcp_loose',
'echo 101 | sudo tee /proc/sys/net/netfilter/nf_conntrack_tcp_timeout_established')
class TestDelay(TestBase):
def test_invalid_delay_missing_delay(self):
self.assertInvalid({
'name': 'lagtastic',
'type': 'DELAY',
'direction': 'IN',
'to_port': 7878
}, 'delay', "required key not provided")
def test_invalid_delay_distribution(self):
self.assertInvalid({
'name': 'lagtastic',
'type': 'DELAY',
'direction': 'IN',
'delay': 120,
'distribution': 1,
}, "distribution", "expected str")
def test_invalid_delay_correlation(self):
self.assertInvalid({
'name': 'lagtastic',
'type': 'DELAY',
'direction': 'IN',
'delay': 120,
'correlation': 'something',
}, "correlation", "expected int")
def test_invalid_delay_variance(self):
self.assertInvalid({
'name': 'lagtastic',
'type': 'DELAY',
'direction': 'IN',
'delay': 120,
'variance': 'variable',
}, "variance", "expected int")
def test_invalid_delay_probability(self):
self.assertInvalid({
'name': 'lagtastic',
'type': 'DELAY',
'direction': 'IN',
'delay': 120,
'probability': 'about half',
}, "probability", "expected float")
def test_normally_distributed_delay(self):
params = {
'name': "normally-distributed-delay",
'type': "DELAY",
'direction': "IN",
'to_port': 4411,
'delay': 160,
'variance': 12,
'distribution': 'normal'}
self.shell.next_result = 'eth0\nvmnet8'
self.assertValidAndGeneratesShellCommand(params,
"netstat -i | tail -n+3 | cut -f1 -d ' '",
'sudo /sbin/tc qdisc add dev eth0 root handle 1: prio',
'sudo /sbin/tc qdisc add dev eth0 parent 1:3 handle 11: netem delay 160ms 12ms distribution normal',
'sudo /sbin/tc filter add dev eth0 protocol ip parent 1:0 prio 3 u32 match ip sport 4411 0xffff flowid 1:3',
'sudo /sbin/tc qdisc add dev vmnet8 root handle 1: prio',
'sudo /sbin/tc qdisc add dev vmnet8 parent 1:3 handle 11: netem delay 160ms 12ms distribution normal',
'sudo /sbin/tc filter add dev vmnet8 protocol ip parent 1:0 prio 3 u32 match ip sport 4411 0xffff flowid 1:3')
def test_pareto_distributed_delay(self):
params = {
'name': "pareto-distributed-delay",
'type': "DELAY",
'direction': "IN",
'to_port': 8822,
'delay': 350,
'variance': 50,
'distribution': 'pareto'}
self.shell.next_result = 'eth0'
self.assertValidAndGeneratesShellCommand(params,
"netstat -i | tail -n+3 | cut -f1 -d ' '",
'sudo /sbin/tc qdisc add dev eth0 root handle 1: prio',
'sudo /sbin/tc qdisc add dev eth0 parent 1:3 handle 11: netem delay 350ms 50ms distribution pareto',
'sudo /sbin/tc filter add dev eth0 protocol ip parent 1:0 prio 3 u32 match ip sport 8822 0xffff flowid 1:3')
def test_uniformly_distributed_delay(self):
params = {
'name': "uniformly-distributed-delay",
'type': "DELAY",
'direction': "IN",
'to_port': 8822,
'delay': 120,
'variance': 5,
'correlation': 25}
self.shell.next_result = 'eth0'
self.assertValidAndGeneratesShellCommand(params,
"netstat -i | tail -n+3 | cut -f1 -d ' '",
'sudo /sbin/tc qdisc add dev eth0 root handle 1: prio',
'sudo /sbin/tc qdisc add dev eth0 parent 1:3 handle 11: netem delay 120ms 5ms 25%',
'sudo /sbin/tc filter add dev eth0 protocol ip parent 1:0 prio 3 u32 match ip sport 8822 0xffff flowid 1:3')
def test_outbound_delay(self):
params = {
'name': "outbound-delay",
'type': "DELAY",
'direction': "OUT",
'to_port': 8822,
'delay': 350}
self.shell.next_result = 'eth0'
self.assertValidAndGeneratesShellCommand(params,
"netstat -i | tail -n+3 | cut -f1 -d ' '",
'sudo /sbin/tc qdisc add dev eth0 root handle 1: prio',
'sudo /sbin/tc qdisc add dev eth0 parent 1:3 handle 11: netem delay 350ms',
'sudo /sbin/tc filter add dev eth0 protocol ip parent 1:0 prio 3 u32 match ip dport 8822 0xffff flowid 1:3')
class TestPacketLoss(TestBase):
def test_invalid_probability(self):
self.assertInvalid({
'name': 'scatty',
'type': 'PACKET_LOSS',
'direction': 'IN',
'probability': 'very little',
}, "probability", "expected float")
def test_invalid_correlation(self):
self.assertInvalid({
'name': 'scatty',
'type': 'PACKET_LOSS',
'direction': 'IN',
'correlation': 'what?',
}, "correlation", "expected int")
def test_packet_loss(self):
params = {
'name': "packet-loss",
'type': "PACKET_LOSS",
'direction': "IN",
'to_port': 9191,
'probability': 0.3}
self.shell.next_result = 'eth0'
self.assertValidAndGeneratesShellCommand(params,
"netstat -i | tail -n+3 | cut -f1 -d ' '",
'sudo /sbin/tc qdisc add dev eth0 root handle 1: prio',
'sudo /sbin/tc qdisc add dev eth0 parent 1:3 handle 11: netem loss 0.3%',
'sudo /sbin/tc filter add dev eth0 protocol ip parent 1:0 prio 3 u32 match ip sport 9191 0xffff flowid 1:3')
def test_packet_loss_with_correlation(self):
params = {
'name': "packet-loss-with-correlation",
'type': "PACKET_LOSS",
'direction': "IN",
'to_port': 9191,
'probability': 0.2,
'correlation': 21}
self.shell.next_result = 'eth0'
self.assertValidAndGeneratesShellCommand(params,
"netstat -i | tail -n+3 | cut -f1 -d ' '",
'sudo /sbin/tc qdisc add dev eth0 root handle 1: prio',
'sudo /sbin/tc qdisc add dev eth0 parent 1:3 handle 11: netem loss 0.2% 21%',
'sudo /sbin/tc filter add dev eth0 protocol ip parent 1:0 prio 3 u32 match ip sport 9191 0xffff flowid 1:3')
if __name__ == '__main__':
unittest.main()