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            if task.implicit:
108                # skip meta: role_complete
109                continue
110            role = task._role
111            if not role:
112                continue
113
114            yield (role.get_name(),
115                   self.var_manager.get_vars(play=play, task=task))
116
117    @patch('ansible.playbook.role.definition.unfrackpath',
118           mock_unfrackpath_noop)
119    def test_simple(self):
120
121        """Test one-level include with default tasks and variables"""
122
123        play = Play.load(dict(
124            name="test play",
125            hosts=['foo'],
126            gather_facts=False,
127            tasks=[
128                {'include_role': 'name=l3'}
129            ]
130        ), loader=self.loader, variable_manager=self.var_manager)
131
132        tasks = play.compile()
133        tested = False
134        for role, task_vars in self.get_tasks_vars(play, tasks):
135            tested = True
136            self.assertEqual(task_vars.get('l3_variable'), 'l3-main')
137            self.assertEqual(task_vars.get('test_variable'), 'l3-main')
138        self.assertTrue(tested)
139
140    @patch('ansible.playbook.role.definition.unfrackpath',
141           mock_unfrackpath_noop)
142    def test_simple_alt_files(self):
143
144        """Test one-level include with alternative tasks and variables"""
145
146        play = Play.load(dict(
147            name="test play",
148            hosts=['foo'],
149            gather_facts=False,
150            tasks=[{'include_role': 'name=l3 tasks_from=alt defaults_from=alt'}]),
151            loader=self.loader, variable_manager=self.var_manager)
152
153        tasks = play.compile()
154        tested = False
155        for role, task_vars in self.get_tasks_vars(play, tasks):
156            tested = True
157            self.assertEqual(task_vars.get('l3_variable'), 'l3-alt')
158            self.assertEqual(task_vars.get('test_variable'), 'l3-alt')
159        self.assertTrue(tested)
160
161    @patch('ansible.playbook.role.definition.unfrackpath',
162           mock_unfrackpath_noop)
163    def test_nested(self):
164
165        """
166        Test nested includes with default tasks and variables.
167
168        Variables from outer roles should be inherited, but overridden in inner
169        roles.
170        """
171
172        play = Play.load(dict(
173            name="test play",
174            hosts=['foo'],
175            gather_facts=False,
176            tasks=[
177                {'include_role': 'name=l1'}
178            ]
179        ), loader=self.loader, variable_manager=self.var_manager)
180
181        tasks = play.compile()
182        expected_roles = ['l1', 'l2', 'l3']
183        for role, task_vars in self.get_tasks_vars(play, tasks):
184            expected_roles.remove(role)
185            # Outer-most role must not have variables from inner roles yet
186            if role == 'l1':
187                self.assertEqual(task_vars.get('l1_variable'), 'l1-main')
188                self.assertEqual(task_vars.get('l2_variable'), None)
189                self.assertEqual(task_vars.get('l3_variable'), None)
190                self.assertEqual(task_vars.get('test_variable'), 'l1-main')
191            # Middle role must have variables from outer role, but not inner
192            elif role == 'l2':
193                self.assertEqual(task_vars.get('l1_variable'), 'l1-main')
194                self.assertEqual(task_vars.get('l2_variable'), 'l2-main')
195                self.assertEqual(task_vars.get('l3_variable'), None)
196                self.assertEqual(task_vars.get('test_variable'), 'l2-main')
197            # Inner role must have variables from both outer roles
198            elif role == 'l3':
199                self.assertEqual(task_vars.get('l1_variable'), 'l1-main')
200                self.assertEqual(task_vars.get('l2_variable'), 'l2-main')
201                self.assertEqual(task_vars.get('l3_variable'), 'l3-main')
202                self.assertEqual(task_vars.get('test_variable'), 'l3-main')
203            else:
204                self.fail()
205        self.assertFalse(expected_roles)
206
207    @patch('ansible.playbook.role.definition.unfrackpath',
208           mock_unfrackpath_noop)
209    def test_nested_alt_files(self):
210
211        """
212        Test nested includes with alternative tasks and variables.
213
214        Variables from outer roles should be inherited, but overridden in inner
215        roles.
216        """
217
218        play = Play.load(dict(
219            name="test play",
220            hosts=['foo'],
221            gather_facts=False,
222            tasks=[
223                {'include_role': 'name=l1 tasks_from=alt defaults_from=alt'}
224            ]
225        ), loader=self.loader, variable_manager=self.var_manager)
226
227        tasks = play.compile()
228        expected_roles = ['l1', 'l2', 'l3']
229        for role, task_vars in self.get_tasks_vars(play, tasks):
230            expected_roles.remove(role)
231            # Outer-most role must not have variables from inner roles yet
232            if role == 'l1':
233                self.assertEqual(task_vars.get('l1_variable'), 'l1-alt')
234                self.assertEqual(task_vars.get('l2_variable'), None)
235                self.assertEqual(task_vars.get('l3_variable'), None)
236                self.assertEqual(task_vars.get('test_variable'), 'l1-alt')
237            # Middle role must have variables from outer role, but not inner
238            elif role == 'l2':
239                self.assertEqual(task_vars.get('l1_variable'), 'l1-alt')
240                self.assertEqual(task_vars.get('l2_variable'), 'l2-alt')
241                self.assertEqual(task_vars.get('l3_variable'), None)
242                self.assertEqual(task_vars.get('test_variable'), 'l2-alt')
243            # Inner role must have variables from both outer roles
244            elif role == 'l3':
245                self.assertEqual(task_vars.get('l1_variable'), 'l1-alt')
246                self.assertEqual(task_vars.get('l2_variable'), 'l2-alt')
247                self.assertEqual(task_vars.get('l3_variable'), 'l3-alt')
248                self.assertEqual(task_vars.get('test_variable'), 'l3-alt')
249            else:
250                self.fail()
251        self.assertFalse(expected_roles)
252