1# (c) 2016, Daniel Miranda <danielkza2@gmail.com>
2#
3# This file is part of Ansible
4#
5# Ansible is free software: you can redistribute it and/or modify
6# it under the terms of the GNU General Public License as published by
7# the Free Software Foundation, either version 3 of the License, or
8# (at your option) any later version.
9#
10# Ansible is distributed in the hope that it will be useful,
11# but WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13# GNU General Public License for more details.
14#
15# You should have received a copy of the GNU General Public License
16# along with Ansible.  If not, see <http://www.gnu.org/licenses/>.
17
18# Make coding more python3-ish
19from __future__ import (absolute_import, division, print_function)
20__metaclass__ = type
21
22from units.compat import unittest
23from units.compat.mock import patch
24
25from ansible.playbook import Play
26from ansible.playbook.role_include import IncludeRole
27from ansible.playbook.task import Task
28from ansible.vars.manager import VariableManager
29
30from units.mock.loader import DictDataLoader
31from units.mock.path import mock_unfrackpath_noop
32
33
34class TestIncludeRole(unittest.TestCase):
35
36    def setUp(self):
37
38        self.loader = DictDataLoader({
39            '/usr/local/etc/ansible/roles/l1/tasks/main.yml': """
40                - shell: echo 'hello world from l1'
41                - include_role: name=l2
42            """,
43            '/usr/local/etc/ansible/roles/l1/tasks/alt.yml': """
44                - shell: echo 'hello world from l1 alt'
45                - include_role: name=l2 tasks_from=alt defaults_from=alt
46            """,
47            '/usr/local/etc/ansible/roles/l1/defaults/main.yml': """
48                test_variable: l1-main
49                l1_variable: l1-main
50            """,
51            '/usr/local/etc/ansible/roles/l1/defaults/alt.yml': """
52                test_variable: l1-alt
53                l1_variable: l1-alt
54            """,
55            '/usr/local/etc/ansible/roles/l2/tasks/main.yml': """
56                - shell: echo 'hello world from l2'
57                - include_role: name=l3
58            """,
59            '/usr/local/etc/ansible/roles/l2/tasks/alt.yml': """
60                - shell: echo 'hello world from l2 alt'
61                - include_role: name=l3 tasks_from=alt defaults_from=alt
62            """,
63            '/usr/local/etc/ansible/roles/l2/defaults/main.yml': """
64                test_variable: l2-main
65                l2_variable: l2-main
66            """,
67            '/usr/local/etc/ansible/roles/l2/defaults/alt.yml': """
68                test_variable: l2-alt
69                l2_variable: l2-alt
70            """,
71            '/usr/local/etc/ansible/roles/l3/tasks/main.yml': """
72                - shell: echo 'hello world from l3'
73            """,
74            '/usr/local/etc/ansible/roles/l3/tasks/alt.yml': """
75                - shell: echo 'hello world from l3 alt'
76            """,
77            '/usr/local/etc/ansible/roles/l3/defaults/main.yml': """
78                test_variable: l3-main
79                l3_variable: l3-main
80            """,
81            '/usr/local/etc/ansible/roles/l3/defaults/alt.yml': """
82                test_variable: l3-alt
83                l3_variable: l3-alt
84            """
85        })
86
87        self.var_manager = VariableManager(loader=self.loader)
88
89    def tearDown(self):
90        pass
91
92    def flatten_tasks(self, tasks):
93        for task in tasks:
94            if isinstance(task, IncludeRole):
95                blocks, handlers = task.get_block_list(loader=self.loader)
96                for block in blocks:
97                    for t in self.flatten_tasks(block.block):
98                        yield t
99            elif isinstance(task, Task):
100                yield task
101            else:
102                for t in self.flatten_tasks(task.block):
103                    yield t
104
105    def get_tasks_vars(self, play, tasks):
106        for task in self.flatten_tasks(tasks):
107            role = task._role
108            if not role:
109                continue
110
111            yield (role.get_name(),
112                   self.var_manager.get_vars(play=play, task=task))
113
114    @patch('ansible.playbook.role.definition.unfrackpath',
115           mock_unfrackpath_noop)
116    def test_simple(self):
117
118        """Test one-level include with default tasks and variables"""
119
120        play = Play.load(dict(
121            name="test play",
122            hosts=['foo'],
123            gather_facts=False,
124            tasks=[
125                {'include_role': 'name=l3'}
126            ]
127        ), loader=self.loader, variable_manager=self.var_manager)
128
129        tasks = play.compile()
130        tested = False
131        for role, task_vars in self.get_tasks_vars(play, tasks):
132            tested = True
133            self.assertEqual(task_vars.get('l3_variable'), 'l3-main')
134            self.assertEqual(task_vars.get('test_variable'), 'l3-main')
135        self.assertTrue(tested)
136
137    @patch('ansible.playbook.role.definition.unfrackpath',
138           mock_unfrackpath_noop)
139    def test_simple_alt_files(self):
140
141        """Test one-level include with alternative tasks and variables"""
142
143        play = Play.load(dict(
144            name="test play",
145            hosts=['foo'],
146            gather_facts=False,
147            tasks=[{'include_role': 'name=l3 tasks_from=alt defaults_from=alt'}]),
148            loader=self.loader, variable_manager=self.var_manager)
149
150        tasks = play.compile()
151        tested = False
152        for role, task_vars in self.get_tasks_vars(play, tasks):
153            tested = True
154            self.assertEqual(task_vars.get('l3_variable'), 'l3-alt')
155            self.assertEqual(task_vars.get('test_variable'), 'l3-alt')
156        self.assertTrue(tested)
157
158    @patch('ansible.playbook.role.definition.unfrackpath',
159           mock_unfrackpath_noop)
160    def test_nested(self):
161
162        """
163        Test nested includes with default tasks and variables.
164
165        Variables from outer roles should be inherited, but overridden in inner
166        roles.
167        """
168
169        play = Play.load(dict(
170            name="test play",
171            hosts=['foo'],
172            gather_facts=False,
173            tasks=[
174                {'include_role': 'name=l1'}
175            ]
176        ), loader=self.loader, variable_manager=self.var_manager)
177
178        tasks = play.compile()
179        expected_roles = ['l1', 'l2', 'l3']
180        for role, task_vars in self.get_tasks_vars(play, tasks):
181            expected_roles.remove(role)
182            # Outer-most role must not have variables from inner roles yet
183            if role == 'l1':
184                self.assertEqual(task_vars.get('l1_variable'), 'l1-main')
185                self.assertEqual(task_vars.get('l2_variable'), None)
186                self.assertEqual(task_vars.get('l3_variable'), None)
187                self.assertEqual(task_vars.get('test_variable'), 'l1-main')
188            # Middle role must have variables from outer role, but not inner
189            elif role == 'l2':
190                self.assertEqual(task_vars.get('l1_variable'), 'l1-main')
191                self.assertEqual(task_vars.get('l2_variable'), 'l2-main')
192                self.assertEqual(task_vars.get('l3_variable'), None)
193                self.assertEqual(task_vars.get('test_variable'), 'l2-main')
194            # Inner role must have variables from both outer roles
195            elif role == 'l3':
196                self.assertEqual(task_vars.get('l1_variable'), 'l1-main')
197                self.assertEqual(task_vars.get('l2_variable'), 'l2-main')
198                self.assertEqual(task_vars.get('l3_variable'), 'l3-main')
199                self.assertEqual(task_vars.get('test_variable'), 'l3-main')
200            else:
201                self.fail()
202        self.assertFalse(expected_roles)
203
204    @patch('ansible.playbook.role.definition.unfrackpath',
205           mock_unfrackpath_noop)
206    def test_nested_alt_files(self):
207
208        """
209        Test nested includes with alternative tasks and variables.
210
211        Variables from outer roles should be inherited, but overridden in inner
212        roles.
213        """
214
215        play = Play.load(dict(
216            name="test play",
217            hosts=['foo'],
218            gather_facts=False,
219            tasks=[
220                {'include_role': 'name=l1 tasks_from=alt defaults_from=alt'}
221            ]
222        ), loader=self.loader, variable_manager=self.var_manager)
223
224        tasks = play.compile()
225        expected_roles = ['l1', 'l2', 'l3']
226        for role, task_vars in self.get_tasks_vars(play, tasks):
227            expected_roles.remove(role)
228            # Outer-most role must not have variables from inner roles yet
229            if role == 'l1':
230                self.assertEqual(task_vars.get('l1_variable'), 'l1-alt')
231                self.assertEqual(task_vars.get('l2_variable'), None)
232                self.assertEqual(task_vars.get('l3_variable'), None)
233                self.assertEqual(task_vars.get('test_variable'), 'l1-alt')
234            # Middle role must have variables from outer role, but not inner
235            elif role == 'l2':
236                self.assertEqual(task_vars.get('l1_variable'), 'l1-alt')
237                self.assertEqual(task_vars.get('l2_variable'), 'l2-alt')
238                self.assertEqual(task_vars.get('l3_variable'), None)
239                self.assertEqual(task_vars.get('test_variable'), 'l2-alt')
240            # Inner role must have variables from both outer roles
241            elif role == 'l3':
242                self.assertEqual(task_vars.get('l1_variable'), 'l1-alt')
243                self.assertEqual(task_vars.get('l2_variable'), 'l2-alt')
244                self.assertEqual(task_vars.get('l3_variable'), 'l3-alt')
245                self.assertEqual(task_vars.get('test_variable'), 'l3-alt')
246            else:
247                self.fail()
248        self.assertFalse(expected_roles)
249