~bloodearnest/charms/precise/squid-reverseproxy/trunk

« back to all changes in this revision

Viewing changes to hooks/tests/test_helpers.py

  • Committer: Marco Ceppi
  • Date: 2013-11-07 01:40:09 UTC
  • mfrom: (28.1.85 master)
  • Revision ID: marco@ceppi.net-20131107014009-lqqg63wkyt6ot2ou
[sidnei] Greatly improve test coverage
[sidnei] Allow the use of an X-Balancer-Name header to select which cache_peer backend will be used for a specific request.
[sidnei] Support 'all-services' being set in the relation, in the way that the haproxy sets it, in addition to the previously supported 'sitenames' setting. Makes it compatible with the haproxy charm.
[sidnei] When the list of supported 'sitenames' (computed from dstdomain acls) changes, notify services related via the 'cached-website' relation. This allows to add new services in the haproxy service (or really, any other service related), which notifies the squid service, which then bubbles up to services related via cached-website.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
import os
 
2
import json
 
3
import yaml
 
4
 
 
5
from os.path import dirname
 
6
 
 
7
from testtools import TestCase
 
8
from testtools.matchers import AfterPreprocessing, Contains
 
9
from mock import patch
 
10
 
 
11
import hooks
 
12
from charmhelpers.core.hookenv import Serializable
 
13
 
 
14
 
 
15
def normalize_whitespace(data):
 
16
    return ' '.join(chunk for chunk in data.split())
 
17
 
 
18
 
 
19
@patch.dict(os.environ, {"CHARM_DIR": dirname(dirname(dirname(__file__)))})
 
20
class SquidConfigTest(TestCase):
 
21
 
 
22
    def _assert_contents(self, *expected):
 
23
        def side_effect(got):
 
24
            for chunk in expected:
 
25
                self.assertThat(got, AfterPreprocessing(
 
26
                    normalize_whitespace,
 
27
                    Contains(normalize_whitespace(chunk))))
 
28
        return side_effect
 
29
 
 
30
    def _apply_patch(self, name):
 
31
        p = patch(name)
 
32
        mocked_name = p.start()
 
33
        self.addCleanup(p.stop)
 
34
        return mocked_name
 
35
 
 
36
    def setUp(self):
 
37
        super(SquidConfigTest, self).setUp()
 
38
        self.get_reverse_sites = self._apply_patch('hooks.get_reverse_sites')
 
39
        self.get_forward_sites = self._apply_patch('hooks.get_forward_sites')
 
40
        self.config_get = self._apply_patch('hooks.config_get')
 
41
        self.write_squid3_config = self._apply_patch(
 
42
            'hooks.write_squid3_config')
 
43
 
 
44
        self.get_forward_sites.return_value = {}
 
45
 
 
46
    def test_squid_config_no_sites(self):
 
47
        self.config_get.return_value = Serializable({
 
48
            "refresh_patterns": "",
 
49
            "cache_size_mb": 1024,
 
50
            "target_objs_per_dir": 1024,
 
51
            "avg_obj_size_kb": 1024,
 
52
            "via": "on",
 
53
        })
 
54
        self.get_reverse_sites.return_value = None
 
55
        self.write_squid3_config.side_effect = self._assert_contents(
 
56
            """
 
57
            always_direct deny all
 
58
            """,
 
59
            """
 
60
            via on
 
61
            """,
 
62
            )
 
63
        hooks.construct_squid3_config()
 
64
 
 
65
    def test_squid_config_via_off(self):
 
66
        self.config_get.return_value = Serializable({
 
67
            "refresh_patterns": "",
 
68
            "cache_size_mb": 1024,
 
69
            "target_objs_per_dir": 1024,
 
70
            "avg_obj_size_kb": 1024,
 
71
            "via": "off",
 
72
        })
 
73
        self.get_reverse_sites.return_value = None
 
74
        self.write_squid3_config.side_effect = self._assert_contents(
 
75
            """
 
76
            via off
 
77
            """,
 
78
            )
 
79
        hooks.construct_squid3_config()
 
80
 
 
81
    def test_squid_config_refresh_pattern_json(self):
 
82
        self.config_get.return_value = Serializable({
 
83
            "refresh_patterns": json.dumps(
 
84
                {"http://www.ubuntu.com":
 
85
                 {"min": 0, "percent": 20, "max": 60}}),
 
86
            "cache_size_mb": 1024,
 
87
            "target_objs_per_dir": 1024,
 
88
            "avg_obj_size_kb": 1024,
 
89
        })
 
90
        self.get_reverse_sites.return_value = None
 
91
        self.write_squid3_config.side_effect = self._assert_contents(
 
92
            """
 
93
            refresh_pattern http://www.ubuntu.com 0 20% 60
 
94
            """,
 
95
            )
 
96
        hooks.construct_squid3_config()
 
97
 
 
98
    def test_squid_config_refresh_pattern_yaml(self):
 
99
        self.config_get.return_value = Serializable({
 
100
            "refresh_patterns": yaml.dump(
 
101
                {"http://www.ubuntu.com":
 
102
                 {"min": 0, "percent": 20, "max": 60}}),
 
103
            "cache_size_mb": 1024,
 
104
            "target_objs_per_dir": 1024,
 
105
            "avg_obj_size_kb": 1024,
 
106
        })
 
107
        self.get_reverse_sites.return_value = None
 
108
        self.write_squid3_config.side_effect = self._assert_contents(
 
109
            """
 
110
            refresh_pattern http://www.ubuntu.com 0 20% 60
 
111
            """,
 
112
            )
 
113
        hooks.construct_squid3_config()
 
114
 
 
115
    def test_squid_config_refresh_pattern_options(self):
 
116
        self.config_get.return_value = Serializable({
 
117
            "refresh_patterns": yaml.dump(
 
118
                {"http://www.ubuntu.com":
 
119
                 {"min": 0, "percent": 20, "max": 60,
 
120
                  "options": ["override-lastmod",
 
121
                              "reload-into-ims"]}}),
 
122
            "cache_size_mb": 1024,
 
123
            "target_objs_per_dir": 1024,
 
124
            "avg_obj_size_kb": 1024,
 
125
        })
 
126
        self.get_reverse_sites.return_value = None
 
127
        self.write_squid3_config.side_effect = self._assert_contents(
 
128
            """
 
129
            refresh_pattern http://www.ubuntu.com 0 20% 60
 
130
            override-lastmod reload-into-ims
 
131
            refresh_pattern . 30 20% 4320
 
132
            """,
 
133
            )
 
134
        hooks.construct_squid3_config()
 
135
 
 
136
    def test_squid_config_refresh_pattern_default(self):
 
137
        self.config_get.return_value = Serializable({
 
138
            "refresh_patterns": yaml.dump(
 
139
                {".":
 
140
                 {"min": 0, "percent": 20, "max": 60,
 
141
                  "options": ["override-lastmod",
 
142
                              "reload-into-ims"]}}),
 
143
            "cache_size_mb": 1024,
 
144
            "target_objs_per_dir": 1024,
 
145
            "avg_obj_size_kb": 1024,
 
146
        })
 
147
        self.get_reverse_sites.return_value = None
 
148
        self.write_squid3_config.side_effect = self._assert_contents(
 
149
            """
 
150
            refresh_pattern . 0 20% 60
 
151
            override-lastmod reload-into-ims
 
152
            """,
 
153
            )
 
154
        hooks.construct_squid3_config()
 
155
 
 
156
    def test_squid_config_no_sitenames(self):
 
157
        self.config_get.return_value = Serializable({
 
158
            "refresh_patterns": "",
 
159
            "cache_size_mb": 1024,
 
160
            "target_objs_per_dir": 1024,
 
161
            "avg_obj_size_kb": 1024,
 
162
        })
 
163
        self.get_reverse_sites.return_value = {
 
164
            None: [
 
165
                hooks.Server("website_1__foo_1", "1.2.3.4", 4242, ''),
 
166
                hooks.Server("website_1__foo_2", "1.2.3.5", 4242, ''),
 
167
                ],
 
168
            }
 
169
        self.write_squid3_config.side_effect = self._assert_contents(
 
170
            """
 
171
            acl no_sitename_acl myport
 
172
            http_access allow accel_ports no_sitename_acl
 
173
            never_direct allow no_sitename_acl
 
174
            """,
 
175
            """
 
176
            cache_peer 1.2.3.4 parent 4242 0 name=website_1__foo_1 no-query
 
177
            no-digest originserver round-robin login=PASS
 
178
            cache_peer_access website_1__foo_1 allow no_sitename_acl
 
179
            cache_peer_access website_1__foo_1 deny all
 
180
            """,
 
181
            """
 
182
            cache_peer 1.2.3.5 parent 4242 0 name=website_1__foo_2 no-query
 
183
            no-digest originserver round-robin login=PASS
 
184
            cache_peer_access website_1__foo_2 allow no_sitename_acl
 
185
            cache_peer_access website_1__foo_2 deny all
 
186
            """
 
187
            )
 
188
        hooks.construct_squid3_config()
 
189
 
 
190
    def test_squid_config_with_domain(self):
 
191
        self.config_get.return_value = Serializable({
 
192
            "refresh_patterns": "",
 
193
            "cache_size_mb": 1024,
 
194
            "target_objs_per_dir": 1024,
 
195
            "avg_obj_size_kb": 1024,
 
196
        })
 
197
        self.get_reverse_sites.return_value = {
 
198
            "foo.com": [
 
199
                hooks.Server("foo_com__website_1__foo_1",
 
200
                             "1.2.3.4", 4242, "forceddomain=example.com"),
 
201
                hooks.Server("foo_com__website_1__foo_2",
 
202
                             "1.2.3.5", 4242, "forceddomain=example.com"),
 
203
                ],
 
204
            }
 
205
        self.write_squid3_config.side_effect = self._assert_contents(
 
206
            """
 
207
            acl s_1_acl dstdomain foo.com
 
208
            http_access allow accel_ports s_1_acl
 
209
            http_access allow CONNECT SSL_ports s_1_acl
 
210
            always_direct allow CONNECT SSL_ports s_1_acl
 
211
            always_direct deny s_1_acl
 
212
            """,
 
213
            """
 
214
            cache_peer 1.2.3.4 parent 4242 0 name=foo_com__website_1__foo_1
 
215
            no-query no-digest originserver round-robin login=PASS
 
216
            forceddomain=example.com
 
217
            cache_peer_access foo_com__website_1__foo_1 allow s_1_acl
 
218
            cache_peer_access foo_com__website_1__foo_1 deny all
 
219
            """,
 
220
            """
 
221
            cache_peer 1.2.3.5 parent 4242 0 name=foo_com__website_1__foo_2
 
222
            no-query no-digest originserver round-robin login=PASS
 
223
            forceddomain=example.com
 
224
            cache_peer_access foo_com__website_1__foo_2 allow s_1_acl
 
225
            cache_peer_access foo_com__website_1__foo_2 deny all
 
226
            """
 
227
            )
 
228
        hooks.construct_squid3_config()
 
229
 
 
230
    def test_with_domain_no_servers_only_direct(self):
 
231
        self.config_get.return_value = Serializable({
 
232
            "refresh_patterns": "",
 
233
            "cache_size_mb": 1024,
 
234
            "target_objs_per_dir": 1024,
 
235
            "avg_obj_size_kb": 1024,
 
236
        })
 
237
        self.get_reverse_sites.return_value = {
 
238
            "foo.com": [
 
239
                ],
 
240
            }
 
241
        self.write_squid3_config.side_effect = self._assert_contents(
 
242
            """
 
243
            acl s_1_acl dstdomain foo.com
 
244
            http_access allow accel_ports s_1_acl
 
245
            http_access allow CONNECT SSL_ports s_1_acl
 
246
            always_direct allow s_1_acl
 
247
            """,
 
248
            )
 
249
        hooks.construct_squid3_config()
 
250
 
 
251
    def test_with_balancer_no_servers_only_direct(self):
 
252
        self.config_get.return_value = Serializable({
 
253
            "refresh_patterns": "",
 
254
            "cache_size_mb": 1024,
 
255
            "target_objs_per_dir": 1024,
 
256
            "avg_obj_size_kb": 1024,
 
257
            "x_balancer_name_allowed": True,
 
258
        })
 
259
        self.get_reverse_sites.return_value = {
 
260
            "foo.com": [],
 
261
            }
 
262
        self.write_squid3_config.side_effect = self._assert_contents(
 
263
            """
 
264
            acl s_1_acl dstdomain foo.com
 
265
            http_access allow accel_ports s_1_acl
 
266
            http_access allow CONNECT SSL_ports s_1_acl
 
267
            always_direct allow s_1_acl
 
268
            """,
 
269
            """
 
270
            acl s_1_balancer req_header X-Balancer-Name foo\.com
 
271
            http_access allow accel_ports s_1_balancer
 
272
            http_access allow CONNECT SSL_ports s_1_balancer
 
273
            always_direct allow s_1_balancer
 
274
            """,
 
275
            )
 
276
        hooks.construct_squid3_config()
 
277
 
 
278
    def test_with_balancer_name(self):
 
279
        self.config_get.return_value = Serializable({
 
280
            "refresh_patterns": "",
 
281
            "cache_size_mb": 1024,
 
282
            "target_objs_per_dir": 1024,
 
283
            "avg_obj_size_kb": 1024,
 
284
            "x_balancer_name_allowed": True,
 
285
            })
 
286
        self.get_reverse_sites.return_value = {
 
287
            "foo.com": [
 
288
                hooks.Server("foo_com__website_1__foo_1",
 
289
                             "1.2.3.4", 4242, ''),
 
290
                hooks.Server("foo_com__website_1__foo_2",
 
291
                             "1.2.3.5", 4242, ''),
 
292
                ],
 
293
            }
 
294
        self.write_squid3_config.side_effect = self._assert_contents(
 
295
            """
 
296
            acl s_1_acl dstdomain foo.com
 
297
            http_access allow accel_ports s_1_acl
 
298
            http_access allow CONNECT SSL_ports s_1_acl
 
299
            always_direct allow CONNECT SSL_ports s_1_acl
 
300
            always_direct deny s_1_acl
 
301
            """,
 
302
            """
 
303
            acl s_1_balancer req_header X-Balancer-Name foo\.com
 
304
            http_access allow accel_ports s_1_balancer
 
305
            http_access allow CONNECT SSL_ports s_1_balancer
 
306
            always_direct allow CONNECT SSL_ports s_1_balancer
 
307
            always_direct deny s_1_balancer
 
308
            """,
 
309
            """
 
310
            cache_peer 1.2.3.4 parent 4242 0 name=foo_com__website_1__foo_1
 
311
            no-query no-digest originserver round-robin login=PASS
 
312
            cache_peer_access foo_com__website_1__foo_1 allow s_1_acl
 
313
            cache_peer_access foo_com__website_1__foo_1 allow s_1_balancer
 
314
            cache_peer_access foo_com__website_1__foo_1 deny all
 
315
            """,
 
316
            """
 
317
            cache_peer 1.2.3.5 parent 4242 0 name=foo_com__website_1__foo_2
 
318
            no-query no-digest originserver round-robin login=PASS
 
319
            cache_peer_access foo_com__website_1__foo_2 allow s_1_acl
 
320
            """
 
321
            )
 
322
        hooks.construct_squid3_config()
 
323
 
 
324
    def test_forward_enabled(self):
 
325
        self.config_get.return_value = Serializable({
 
326
            "enable_forward_proxy": True,
 
327
            "refresh_patterns": "",
 
328
            "cache_size_mb": 1024,
 
329
            "target_objs_per_dir": 1024,
 
330
            "avg_obj_size_kb": 1024,
 
331
        })
 
332
        self.get_reverse_sites.return_value = {
 
333
            "foo.com": [],
 
334
            }
 
335
        self.get_forward_sites.return_value = [
 
336
            {'private-address': '1.2.3.4', 'name': 'service_unit_0'},
 
337
            {'private-address': '2.3.4.5', 'name': 'service_unit_1'},
 
338
        ]
 
339
        self.write_squid3_config.side_effect = self._assert_contents(
 
340
            """
 
341
            acl fwd_service_unit_0 src 1.2.3.4
 
342
            http_access allow fwd_service_unit_0
 
343
            http_access allow CONNECT SSL_ports fwd_service_unit_0
 
344
            always_direct allow fwd_service_unit_0
 
345
            always_direct allow CONNECT SSL_ports fwd_service_unit_0
 
346
            acl fwd_service_unit_1 src 2.3.4.5
 
347
            http_access allow fwd_service_unit_1
 
348
            http_access allow CONNECT SSL_ports fwd_service_unit_1
 
349
            always_direct allow fwd_service_unit_1
 
350
            always_direct allow CONNECT SSL_ports fwd_service_unit_1
 
351
            """,
 
352
            """
 
353
            acl s_1_acl dstdomain foo.com
 
354
            http_access allow accel_ports s_1_acl
 
355
            http_access allow CONNECT SSL_ports s_1_acl
 
356
            always_direct allow s_1_acl
 
357
            """
 
358
            )
 
359
        hooks.construct_squid3_config()
 
360
 
 
361
    def test_squid_config_cache_enabled(self):
 
362
        self.config_get.return_value = Serializable({
 
363
            "refresh_patterns": "",
 
364
            "cache_size_mb": 1024,
 
365
            "cache_mem_mb": 256,
 
366
            "cache_dir": "/var/run/squid3",
 
367
            "target_objs_per_dir": 16,
 
368
            "avg_obj_size_kb": 4,
 
369
        })
 
370
        self.get_reverse_sites.return_value = None
 
371
        self.write_squid3_config.side_effect = self._assert_contents(
 
372
            """
 
373
            cache_dir aufs /var/run/squid3 1024 32 512
 
374
            cache_mem 256 MB
 
375
            """,
 
376
            )
 
377
        hooks.construct_squid3_config()
 
378
 
 
379
    def test_squid_config_cache_disabled(self):
 
380
        self.config_get.return_value = Serializable({
 
381
            "refresh_patterns": "",
 
382
            "cache_size_mb": 0,
 
383
            "target_objs_per_dir": 1024,
 
384
            "avg_obj_size_kb": 1024,
 
385
        })
 
386
        self.get_reverse_sites.return_value = None
 
387
        self.write_squid3_config.side_effect = self._assert_contents(
 
388
            """
 
389
            cache deny all
 
390
            """,
 
391
            )
 
392
        hooks.construct_squid3_config()
 
393
 
 
394
 
 
395
class HelpersTest(TestCase):
 
396
    def test_gets_config(self):
 
397
        json_string = '{"foo": "BAR"}'
 
398
        with patch('subprocess.check_output') as check_output:
 
399
            check_output.return_value = json_string
 
400
 
 
401
            result = hooks.config_get()
 
402
 
 
403
            self.assertEqual(result['foo'], 'BAR')
 
404
            check_output.assert_called_with(['config-get', '--format=json'])
 
405
 
 
406
    def test_gets_config_with_scope(self):
 
407
        json_string = '{"foo": "BAR"}'
 
408
        with patch('subprocess.check_output') as check_output:
 
409
            check_output.return_value = json_string
 
410
 
 
411
            result = hooks.config_get(scope='baz')
 
412
 
 
413
            self.assertEqual(result['foo'], 'BAR')
 
414
            check_output.assert_called_with(['config-get', 'baz',
 
415
                                             '--format=json'])