~jjo/charms/precise/httpload/trunk

« back to all changes in this revision

Viewing changes to lib/charm-helpers/tests/contrib/openstack/test_os_templating.py

  • Committer: JuanJo Ciarlante
  • Date: 2013-08-15 00:01:56 UTC
  • Revision ID: jjo@canonical.com-20130815000156-elktlh6p9ljozcx3
added lp:charm-helpers

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
 
 
2
import os
 
3
 
 
4
import unittest
 
5
from mock import patch, call, MagicMock
 
6
 
 
7
import charmhelpers.contrib.openstack.templating as templating
 
8
 
 
9
 
 
10
class FakeContextGenerator(object):
 
11
    interfaces = None
 
12
 
 
13
    def set(self, interfaces, context):
 
14
        self.interfaces = interfaces
 
15
        self.context = context
 
16
 
 
17
    def __call__(self):
 
18
        return self.context
 
19
 
 
20
 
 
21
class FakeLoader(object):
 
22
    def set(self, template):
 
23
        self.template = template
 
24
 
 
25
    def get(self, name):
 
26
        return self.template
 
27
 
 
28
 
 
29
class MockFSLoader(object):
 
30
    def __init__(self, dirs):
 
31
        self.searchpath = [dirs]
 
32
 
 
33
 
 
34
class MockChoiceLoader(object):
 
35
    def __init__(self, loaders):
 
36
        self.loaders = loaders
 
37
 
 
38
 
 
39
def MockTemplate():
 
40
    templ = MagicMock()
 
41
    templ.render = MagicMock()
 
42
    return templ
 
43
 
 
44
 
 
45
class TemplatingTests(unittest.TestCase):
 
46
    def setUp(self):
 
47
        path = os.path.dirname(__file__)
 
48
        self.loader = FakeLoader()
 
49
        self.context = FakeContextGenerator()
 
50
 
 
51
        self.addCleanup(patch.object(templating, 'apt_install').start().stop())
 
52
        self.addCleanup(patch.object(templating, 'log').start().stop())
 
53
 
 
54
        templating.FileSystemLoader = MockFSLoader
 
55
        templating.ChoiceLoader = MockChoiceLoader
 
56
        templating.Environment = MagicMock
 
57
 
 
58
        self.renderer = templating.OSConfigRenderer(templates_dir=path,
 
59
                                                    openstack_release='folsom')
 
60
 
 
61
    @patch.object(templating, 'apt_install')
 
62
    def test_initializing_a_render_ensures_jinja2_present(self, apt):
 
63
        '''Creatinga new renderer object installs jinja2 if needed'''
 
64
        # temp. undo the patching from setUp
 
65
        templating.FileSystemLoader = None
 
66
        templating.ChoiceLoader = None
 
67
        templating.Environment = None
 
68
        templating.OSConfigRenderer(templates_dir='/tmp',
 
69
                                    openstack_release='foo')
 
70
        templating.FileSystemLoader = MockFSLoader
 
71
        templating.ChoiceLoader = MockChoiceLoader
 
72
        templating.Environment = MagicMock
 
73
        apt.assert_called_with('python-jinja2')
 
74
 
 
75
    def test_create_renderer_invalid_templates_dir(self):
 
76
        '''Ensure OSConfigRenderer checks templates_dir'''
 
77
        self.assertRaises(templating.OSConfigException,
 
78
                          templating.OSConfigRenderer,
 
79
                          templates_dir='/tmp/foooo0',
 
80
                          openstack_release='grizzly')
 
81
 
 
82
    def test_render_unregistered_config(self):
 
83
        '''Ensure cannot render an unregistered config file'''
 
84
        self.assertRaises(templating.OSConfigException,
 
85
                          self.renderer.render,
 
86
                          config_file='/tmp/foo')
 
87
 
 
88
    def test_write_unregistered_config(self):
 
89
        '''Ensure cannot write an unregistered config file'''
 
90
        self.assertRaises(templating.OSConfigException,
 
91
                          self.renderer.write,
 
92
                          config_file='/tmp/foo')
 
93
 
 
94
    def test_render_complete_context(self):
 
95
        '''It renders a template when provided a complete context'''
 
96
        self.loader.set('{{ foo }}')
 
97
        self.context.set(interfaces=['fooservice'], context={'foo': 'bar'})
 
98
        self.renderer.register('/tmp/foo', [self.context])
 
99
        with patch.object(self.renderer, '_get_template') as _get_t:
 
100
            fake_tmpl = MockTemplate()
 
101
            _get_t.return_value = fake_tmpl
 
102
            self.renderer.render('/tmp/foo')
 
103
            fake_tmpl.render.assert_called_with(self.context())
 
104
        self.assertIn('fooservice', self.renderer.complete_contexts())
 
105
 
 
106
    def test_render_incomplete_context_with_template(self):
 
107
        '''It renders a template when provided an incomplete context'''
 
108
        self.context.set(interfaces=['fooservice'], context={})
 
109
        self.renderer.register('/tmp/foo', [self.context])
 
110
        with patch.object(self.renderer, '_get_template') as _get_t:
 
111
            fake_tmpl = MockTemplate()
 
112
            _get_t.return_value = fake_tmpl
 
113
            self.renderer.render('/tmp/foo')
 
114
            fake_tmpl.render.assert_called_with({})
 
115
            self.assertNotIn('fooservice', self.renderer.complete_contexts())
 
116
 
 
117
    @patch('__builtin__.open')
 
118
    @patch.object(templating, 'get_loader')
 
119
    def test_write_out_config(self, loader, _open):
 
120
        '''It writes a templated config when provided a complete context'''
 
121
        self.context.set(interfaces=['fooservice'], context={'foo': 'bar'})
 
122
        self.renderer.register('/tmp/foo', [self.context])
 
123
        with patch.object(self.renderer, '_get_template') as _get_t:
 
124
            fake_tmpl = MockTemplate()
 
125
            _get_t.return_value = fake_tmpl
 
126
            self.renderer.write('/tmp/foo')
 
127
            _open.assert_called_with('/tmp/foo', 'wb')
 
128
 
 
129
    def test_write_all(self):
 
130
        '''It writes out all configuration files at once'''
 
131
        self.context.set(interfaces=['fooservice'], context={'foo': 'bar'})
 
132
        self.renderer.register('/tmp/foo', [self.context])
 
133
        self.renderer.register('/tmp/bar', [self.context])
 
134
        ex_calls = [
 
135
            call('/tmp/bar'),
 
136
            call('/tmp/foo'),
 
137
        ]
 
138
        with patch.object(self.renderer, 'write') as _write:
 
139
            self.renderer.write_all()
 
140
            self.assertEquals(ex_calls, _write.call_args_list)
 
141
            pass
 
142
 
 
143
    @patch.object(templating, 'get_loader')
 
144
    def test_reset_template_loader_for_new_os_release(self, loader):
 
145
        self.loader.set('')
 
146
        self.context.set(interfaces=['fooservice'], context={})
 
147
        loader.return_value = MockFSLoader('/tmp/foo')
 
148
        self.renderer.register('/tmp/foo', [self.context])
 
149
        self.renderer.render('/tmp/foo')
 
150
        loader.assert_called_with(os.path.dirname(__file__), 'folsom')
 
151
        self.renderer.set_release(openstack_release='grizzly')
 
152
        self.renderer.render('/tmp/foo')
 
153
        loader.assert_called_with(os.path.dirname(__file__), 'grizzly')
 
154
 
 
155
    @patch.object(templating, 'get_loader')
 
156
    def test_incomplete_context_not_reported_complete(self, loader):
 
157
        '''It does not recognize an incomplete context as a complete context'''
 
158
        self.context.set(interfaces=['fooservice'], context={})
 
159
        self.renderer.register('/tmp/foo', [self.context])
 
160
        self.assertNotIn('fooservice', self.renderer.complete_contexts())
 
161
 
 
162
    @patch.object(templating, 'get_loader')
 
163
    def test_complete_context_reported_complete(self, loader):
 
164
        '''It recognizes a complete context as a complete context'''
 
165
        self.context.set(interfaces=['fooservice'], context={'foo': 'bar'})
 
166
        self.renderer.register('/tmp/foo', [self.context])
 
167
        self.assertIn('fooservice', self.renderer.complete_contexts())
 
168
 
 
169
    @patch('os.path.isdir')
 
170
    def test_get_loader_no_templates_dir(self, isdir):
 
171
        '''Ensure getting loader fails with no template dir'''
 
172
        isdir.return_value = False
 
173
        self.assertRaises(templating.OSConfigException,
 
174
                          templating.get_loader,
 
175
                          templates_dir='/tmp/foo', os_release='foo')
 
176
 
 
177
    @patch('os.path.isdir')
 
178
    def test_get_loader_all_search_paths(self, isdir):
 
179
        '''Ensure loader reverse searches of all release template dirs'''
 
180
        isdir.return_value = True
 
181
        choice_loader = templating.get_loader('/tmp/foo',
 
182
                                              os_release='icehouse')
 
183
        dirs = [l.searchpath for l in choice_loader.loaders]
 
184
 
 
185
        common_tmplts = os.path.join(os.path.dirname(templating.__file__),
 
186
                                     'templates')
 
187
        expected = [['/tmp/foo/icehouse'],
 
188
                    ['/tmp/foo/havana'],
 
189
                    ['/tmp/foo/grizzly'],
 
190
                    ['/tmp/foo/folsom'],
 
191
                    ['/tmp/foo/essex'],
 
192
                    ['/tmp/foo/diablo'],
 
193
                    ['/tmp/foo'],
 
194
                    [common_tmplts]]
 
195
        self.assertEquals(dirs, expected)
 
196
 
 
197
    @patch('os.path.isdir')
 
198
    def test_get_loader_some_search_paths(self, isdir):
 
199
        '''Ensure loader reverse searches of some release template dirs'''
 
200
        isdir.return_value = True
 
201
        choice_loader = templating.get_loader('/tmp/foo', os_release='grizzly')
 
202
        dirs = [l.searchpath for l in choice_loader.loaders]
 
203
 
 
204
        common_tmplts = os.path.join(os.path.dirname(templating.__file__),
 
205
                                     'templates')
 
206
 
 
207
        expected = [['/tmp/foo/grizzly'],
 
208
                    ['/tmp/foo/folsom'],
 
209
                    ['/tmp/foo/essex'],
 
210
                    ['/tmp/foo/diablo'],
 
211
                    ['/tmp/foo'],
 
212
                    [common_tmplts]]
 
213
        self.assertEquals(dirs, expected)
 
214
 
 
215
    def test_register_template_with_list_of_contexts(self):
 
216
        '''Ensure registering a template with a list of context generators'''
 
217
        def _c1():
 
218
            pass
 
219
 
 
220
        def _c2():
 
221
            pass
 
222
        tmpl = templating.OSConfigTemplate(config_file='/tmp/foo',
 
223
                                           contexts=[_c1, _c2])
 
224
        self.assertEquals(tmpl.contexts, [_c1, _c2])
 
225
 
 
226
    def test_register_template_with_single_context(self):
 
227
        '''Ensure registering a template with a single non-list context'''
 
228
        def _c1():
 
229
            pass
 
230
        tmpl = templating.OSConfigTemplate(config_file='/tmp/foo',
 
231
                                           contexts=_c1)
 
232
        self.assertEquals(tmpl.contexts, [_c1])