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