def deserialize(self, data): # import is here to avoid import loops from ansible.playbook.task_include import TaskInclude from ansible.playbook.handler_task_include import HandlerTaskInclude parent_data = data.get('parent', None) if parent_data: parent_type = data.get('parent_type') if parent_type == 'Block': p = Block() elif parent_type == 'TaskInclude': p = TaskInclude() elif parent_type == 'HandlerTaskInclude': p = HandlerTaskInclude() p.deserialize(parent_data) self._parent = p del data['parent'] role_data = data.get('role') if role_data: r = Role() r.deserialize(role_data) self._role = r del data['role'] super(Task, self).deserialize(data)
def test__load_role_yaml(self, _load_from_file): _load_from_file.return_value = dict(foo='bar') r = Role() with patch('os.path.exists', return_value=True): with patch('os.path.isdir', return_value=True): res = r._load_role_yaml('/fake/path', 'some_subdir') self.assertEqual(res, dict(foo='bar'))
def deserialize(self, data): ''' Override of the default deserialize method, to match the above overridden serialize method ''' from ansible.playbook.task import Task # we don't want the full set of attributes (the task lists), as that # would lead to a serialize/deserialize loop for attr in self._get_base_attributes(): if attr in data and attr not in ('block', 'rescue', 'always'): setattr(self, attr, data.get(attr)) self._dep_chain = data.get('dep_chain', []) # if there was a serialized role, unpack it too role_data = data.get('role') if role_data: r = Role() r.deserialize(role_data) self._role = r # if there was a serialized task include, unpack it too ti_data = data.get('task_include') if ti_data: ti = Task() ti.deserialize(ti_data) self._task_include = ti pb_data = data.get('parent_block') if pb_data: pb = Block() pb.deserialize(pb_data) self._parent_block = pb
def deserialize(self, data): ''' Override of the default deserialize method, to match the above overridden serialize method ''' from ansible.playbook.task import Task # unpack the when attribute, which is the only one we want self.when = data.get('when') self._dep_chain = data.get('dep_chain', []) # if there was a serialized role, unpack it too role_data = data.get('role') if role_data: r = Role() r.deserialize(role_data) self._role = r # if there was a serialized task include, unpack it too ti_data = data.get('task_include') if ti_data: ti = Task() ti.deserialize(ti_data) self._task_include = ti
def deserialize(self, data): # import is here to avoid import loops #from ansible.playbook.task_include import TaskInclude block_data = data.get('block') if block_data: b = Block() b.deserialize(block_data) self._block = b del data['block'] role_data = data.get('role') if role_data: r = Role() r.deserialize(role_data) self._role = r del data['role'] ti_data = data.get('task_include') if ti_data: #ti = TaskInclude() ti = Task() ti.deserialize(ti_data) self._task_include = ti del data['task_include'] super(Task, self).deserialize(data)
def test_serialize(self): fake_loader = DictDataLoader({ "/etc/ansible/roles/foo_vars/defaults/main/foo/bar.yml": """ foo: bar """, "/etc/ansible/roles/foo_vars/vars/main/bar/foo.yml": """ foo: bam """, }) mock_play = MagicMock() mock_play.ROLE_CACHE = {} i = RoleInclude.load('foo_vars', play=mock_play, loader=fake_loader) r = Role.load(i, play=mock_play) res = r.serialize() log.debug('res: %s', res) self.assertEqual(r._default_vars, dict(foo='bar')) self.assertEqual(r._role_vars, dict(foo='bam')) r2 = Role.load(i, play=mock_play) r2.deserialize(res) log.debug('r2: %s', r2)
def deserialize(self, data): ''' Override of the default deserialize method, to match the above overridden serialize method ''' from ansible.playbook.task import Task # we don't want the full set of attributes (the task lists), as that # would lead to a serialize/deserialize loop for attr in self._get_base_attributes(): if attr in data and attr not in ('block', 'rescue', 'always'): setattr(self, attr, data.get(attr)) self._dep_chain = data.get('dep_chain', []) # if there was a serialized role, unpack it too role_data = data.get('role') if role_data: r = Role() r.deserialize(role_data) self._role = r # if there was a serialized task include, unpack it too ti_data = data.get('task_include') if ti_data: ti = Task() ti.deserialize(ti_data) self._task_include = ti
def test_role__load_list_of_blocks(self): task = dict(action='test') r = Role() self.assertEqual(r._load_list_of_blocks([]), []) res = r._load_list_of_blocks([task]) self.assertEqual(len(res), 1) assert isinstance(res[0], Block) res = r._load_list_of_blocks([task, task, task]) self.assertEqual(len(res), 3)
def deserialize(self, data): super(Play, self).deserialize(data) if 'roles' in data: role_data = data.get('roles', []) roles = [] for role in role_data: r = Role() r.deserialize(role) roles.append(r) setattr(self, 'roles', roles) del data['roles']
def deserialize(self, data): ''' Override of the default deserialize method, to match the above overridden serialize method ''' # unpack the when attribute, which is the only one we want self.when = data.get('when') # if there was a serialized role, unpack it too role_data = data.get('role') if role_data: r = Role() r.deserialize(role_data) self._role = r
def get_block_list(self, play=None, variable_manager=None, loader=None): # only need play passed in when dynamic if play is None: myplay = self._parent._play else: myplay = play ri = RoleInclude.load(self._role_name, play=myplay, variable_manager=variable_manager, loader=loader) ri.vars.update(self.vars) # build role actual_role = Role.load(ri, myplay, parent_role=self._parent_role, from_files=self._from_files) actual_role._metadata.allow_duplicates = self.allow_duplicates # compile role blocks = actual_role.compile(play=myplay) # set parent to ensure proper inheritance for b in blocks: b._parent = self # updated available handlers in play myplay.handlers = myplay.handlers + actual_role.get_handler_blocks(play=myplay) return blocks
def get_block_list(self, play=None, variable_manager=None, loader=None): # only need play passed in when dynamic if play is None: myplay = self._parent._play else: myplay = play ri = RoleInclude.load(self._role_name, play=myplay, variable_manager=variable_manager, loader=loader) ri.vars.update(self.vars) # build role actual_role = Role.load(ri, myplay, parent_role=self._parent_role, from_files=self._from_files) actual_role._metadata.allow_duplicates = self.allow_duplicates # save this for later use self._role_path = actual_role._role_path # compile role with parent roles as dependencies to ensure they inherit # variables if not self._parent_role: dep_chain = [] else: dep_chain = list(self._parent_role._parents) dep_chain.append(self._parent_role) blocks = actual_role.compile(play=myplay, dep_chain=dep_chain) for b in blocks: b._parent = self # updated available handlers in play handlers = actual_role.get_handler_blocks(play=myplay) myplay.handlers = myplay.handlers + handlers return blocks, handlers
def _load_roles(self, attr, ds): ''' Loads and returns a list of RoleInclude objects from the datastructure list of role definitions and creates the Role from those objects ''' if ds is None: ds = [] try: role_includes = load_list_of_roles( ds, play=self, variable_manager=self._variable_manager, loader=self._loader, collection_search_list=self.collections) except AssertionError as e: raise AnsibleParserError( "A malformed role declaration was encountered.", obj=self._ds, orig_exc=e) roles = [] for ri in role_includes: roles.append(Role.load(ri, play=self)) self.roles[:0] = roles return self.roles
def get_block_list(self, play=None, variable_manager=None, loader=None): # only need play passed in when dynamic if play is None: myplay = self._parent._play else: myplay = play ri = RoleInclude.load(self._role_name, play=myplay, variable_manager=variable_manager, loader=loader) ri.vars.update(self.vars) #ri._role_params.update(self.args) # jimi-c cant we avoid this? #build role actual_role = Role.load(ri, myplay, parent_role=self._parent_role, from_files=self._from_files) # compile role blocks = actual_role.compile(play=myplay) # set parent to ensure proper inheritance for b in blocks: b._parent = self._parent # updated available handlers in play myplay.handlers = myplay.handlers + actual_role.get_handler_blocks( play=myplay) return blocks
def deserialize(self, data): super(Play, self).deserialize(data) self._included_path = data.get('included_path', None) self._action_groups = data.get('action_groups', {}) self._group_actions = data.get('group_actions', {}) if 'roles' in data: role_data = data.get('roles', []) roles = [] for role in role_data: r = Role() r.deserialize(role) roles.append(r) setattr(self, 'roles', roles) del data['roles']
def deserialize(self, data): block_data = data.get('block') self._dep_chain = data.get('dep_chain', []) if block_data: b = Block() b.deserialize(block_data) self._block = b del data['block'] role_data = data.get('role') if role_data: r = Role() r.deserialize(role_data) self._role = r del data['role'] super(Task, self).deserialize(data)
def get_block_list(self, play=None, variable_manager=None, loader=None): # only need play passed in when dynamic if play is None: myplay = self._parent._play else: myplay = play ri = RoleInclude.load(self._role_name, play=myplay, variable_manager=variable_manager, loader=loader, collection_list=self.collections) ri.vars.update(self.vars) # build role actual_role = Role.load(ri, myplay, parent_role=self._parent_role, from_files=self._from_files, from_include=True) # proxy allow_duplicates attribute to role if explicitly set if self.allow_duplicates is not None: actual_role._metadata.allow_duplicates = self.allow_duplicates # in any case sync allow_duplicates between the role and this include statement # This is the side effect if we didnt explicitly setted the allow_duplicates attribute # to fallback on the included role setting if self.allow_duplicates is None and actual_role._metadata: self.allow_duplicates = actual_role._metadata.allow_duplicates if self.statically_loaded or self.public: myplay.roles.append(actual_role) # save this for later use self._role_path = actual_role._role_path self._role = actual_role # compile role with parent roles as dependencies to ensure they inherit # variables if not self._parent_role: dep_chain = [] else: dep_chain = list(self._parent_role._parents) dep_chain.append(self._parent_role) p_block = self.build_parent_block() # collections value is not inherited; override with the value we calculated during role setup p_block.collections = actual_role.collections blocks = actual_role.compile(play=myplay, dep_chain=dep_chain) for b in blocks: b._parent = p_block # HACK: parent inheritance doesn't seem to have a way to handle this intermediate override until squashed/finalized b.collections = actual_role.collections # updated available handlers in play handlers = actual_role.get_handler_blocks(play=myplay) for h in handlers: h._parent = p_block myplay.handlers = myplay.handlers + handlers return blocks, handlers
def _load_vars(var_path: Path, r: ansrole.Role) -> Mapping[str, Value]: ds = r._load_role_yaml(str( var_path.parent.relative_to(Path(r._role_path))), var_path.name, allow_dir=False) if ds is None: return {} if not isinstance(ds, ansobj.AnsibleMapping): # Hijack AnsibleError so we can log the broken file raise ans.errors.AnsibleError(f'Corrupt variables: {ds}') return ds
def get_block_list(self, play=None, variable_manager=None, loader=None): # only need play passed in when dynamic if play is None: myplay = self._parent._play else: myplay = play ri = RoleInclude.load(self._role_name, play=myplay, variable_manager=variable_manager, loader=loader, collection_list=self.collections) ri.vars.update(self.vars) if variable_manager is not None: available_variables = variable_manager.get_vars(play=myplay, task=self) else: available_variables = {} templar = Templar(loader=loader, variables=available_variables) from_files = templar.template(self._from_files) # build role actual_role = Role.load(ri, myplay, parent_role=self._parent_role, from_files=from_files, from_include=True, validate=self.rolespec_validate) actual_role._metadata.allow_duplicates = self.allow_duplicates if self.statically_loaded or self.public: myplay.roles.append(actual_role) # save this for later use self._role_path = actual_role._role_path # compile role with parent roles as dependencies to ensure they inherit # variables if not self._parent_role: dep_chain = [] else: dep_chain = list(self._parent_role._parents) dep_chain.append(self._parent_role) p_block = self.build_parent_block() # collections value is not inherited; override with the value we calculated during role setup p_block.collections = actual_role.collections blocks = actual_role.compile(play=myplay, dep_chain=dep_chain) for b in blocks: b._parent = p_block # HACK: parent inheritance doesn't seem to have a way to handle this intermediate override until squashed/finalized b.collections = actual_role.collections # updated available handlers in play handlers = actual_role.get_handler_blocks(play=myplay) for h in handlers: h._parent = p_block myplay.handlers = myplay.handlers + handlers return blocks, handlers
def _load_roles(self, attr, ds): ''' Loads and returns a list of RoleInclude objects from the datastructure list of role definitions and creates the Role from those objects ''' role_includes = load_list_of_roles(ds, variable_manager=self._variable_manager, loader=self._loader) roles = [] for ri in role_includes: roles.append(Role.load(ri)) return roles
def deserialize(self, data): ''' Override of the default deserialize method, to match the above overridden serialize method ''' # import is here to avoid import loops from ansible.playbook.task import Task from ansible.playbook.task_include import TaskInclude from ansible.playbook.handler_task_include import HandlerTaskInclude # we don't want the full set of attributes (the task lists), as that # would lead to a serialize/deserialize loop for attr in self._valid_attrs: if attr in data and attr not in ('block', 'rescue', 'always'): setattr(self, attr, data.get(attr)) self._dep_chain = data.get('dep_chain', None) self._eor = data.get('eor', False) # if there was a serialized role, unpack it too role_data = data.get('role') if role_data: r = Role() r.deserialize(role_data) self._role = r parent_data = data.get('parent') if parent_data: parent_type = data.get('parent_type') if parent_type == 'Block': p = Block() elif parent_type == 'TaskInclude': p = TaskInclude() elif parent_type == 'HandlerTaskInclude': p = HandlerTaskInclude() p.deserialize(parent_data) self._parent = p self._dep_chain = self._parent.get_dep_chain()
def test_load_role_complex(self, _load_role_yaml, _get_role_path): _get_role_path.return_value = ('foo', '/etc/ansible/roles/foo') def fake_load_role_yaml(role_path, subdir): if role_path == '/etc/ansible/roles/foo': if subdir == 'tasks': return [dict(shell='echo "hello world"')] return None _load_role_yaml.side_effect = fake_load_role_yaml r = Role.load(dict(role='foo'))
def _load_roles(self, attr, ds): ''' Loads and returns a list of RoleInclude objects from the datastructure list of role definitions and creates the Role from those objects ''' role_includes = load_list_of_roles( ds, variable_manager=self._variable_manager, loader=self._loader) roles = [] for ri in role_includes: roles.append(Role.load(ri)) return roles
def test_load_role_with_handlers(self): fake_loader = DictDataLoader({ "/etc/ansible/roles/foo_handlers/handlers/main.yml": """ - name: test handler shell: echo 'hello world' """, }) i = RoleInclude.load('foo_handlers', loader=fake_loader) r = Role.load(i) self.assertEqual(len(r._handler_blocks), 1) assert isinstance(r._handler_blocks[0], Block)
def test_load_role_with_tasks(self): fake_loader = DictDataLoader({ "/etc/ansible/roles/foo_tasks/tasks/main.yml": """ - shell: echo 'hello world' """, }) i = RoleInclude.load('foo_tasks', loader=fake_loader) r = Role.load(i) self.assertEqual(str(r), 'foo_tasks') self.assertEqual(len(r._task_blocks), 1) assert isinstance(r._task_blocks[0], Block)
def deserialize(self, data): ''' Override of the default deserialize method, to match the above overridden serialize method ''' from ansible.playbook.task_include import TaskInclude # unpack the when attribute, which is the only one we want self.when = data.get('when') # if there was a serialized role, unpack it too role_data = data.get('role') if role_data: r = Role() r.deserialize(role_data) self._role = r # if there was a serialized task include, unpack it too ti_data = data.get('task_include') if ti_data: ti = TaskInclude() ti.deserialize(ti_data) self._task_include = ti
def test_load_role_complex(self): # FIXME: add tests for the more complex uses of # params and tags/when statements fake_loader = DictDataLoader({ "/etc/ansible/roles/foo_complex/tasks/main.yml": """ - shell: echo 'hello world' """, }) i = RoleInclude.load(dict(role='foo_complex'), loader=fake_loader) r = Role.load(i) self.assertEqual(r.get_name(), "foo_complex")
def test_load_role_with_handlers(self): fake_loader = DictDataLoader({ "/etc/ansible/roles/foo/handlers/main.yml": """ - name: test handler shell: echo 'hello world' """, }) i = RoleInclude.load('foo', loader=fake_loader) r = Role.load(i) self.assertEqual(len(r._handler_blocks), 1) assert isinstance(r._handler_blocks[0], Block)
def test_load_role_with_tasks(self): fake_loader = DictDataLoader({ "/etc/ansible/roles/foo/tasks/main.yml": """ - shell: echo 'hello world' """, }) i = RoleInclude.load('foo', loader=fake_loader) r = Role.load(i) self.assertEqual(str(r), 'foo') self.assertEqual(len(r._task_blocks), 1) assert isinstance(r._task_blocks[0], Block)
def test_load_role_with_metadata(self, _load_role_yaml, _get_role_path): _get_role_path.return_value = ('foo', '/etc/ansible/roles/foo') def fake_load_role_yaml(role_path, subdir): if role_path == '/etc/ansible/roles/foo': if subdir == 'meta': return dict(dependencies=[], allow_duplicates=False) return None _load_role_yaml.side_effect = fake_load_role_yaml r = Role.load('foo') self.assertEqual(r.metadata, dict(dependencies=[], allow_duplicates=False))
def test_load_role_with_tasks(self, _load_role_yaml, _get_role_path): _get_role_path.return_value = ('foo', '/etc/ansible/roles/foo') def fake_load_role_yaml(role_path, subdir): if role_path == '/etc/ansible/roles/foo': if subdir == 'tasks': return [dict(shell='echo "hello world"')] return None _load_role_yaml.side_effect = fake_load_role_yaml r = Role.load('foo') self.assertEqual(len(r.task_blocks), 1) assert isinstance(r.task_blocks[0], Block)
def test_load_role_with_vars(self): fake_loader = DictDataLoader({ "/etc/ansible/roles/foo_vars/defaults/main.yml": """ foo: bar """, "/etc/ansible/roles/foo_vars/vars/main.yml": """ foo: bam """, }) i = RoleInclude.load('foo_vars', loader=fake_loader) r = Role.load(i) self.assertEqual(r._default_vars, dict(foo='bar')) self.assertEqual(r._role_vars, dict(foo='bam'))
def _load_tasks(task_path: Path, r: ansrole.Role, handlers: bool = False) -> Sequence[anspb.block.Block]: ds = r._load_role_yaml(str( task_path.parent.relative_to(Path(r._role_path))), task_path.name, allow_dir=False) # assert isinstance(ds, ansobj.AnsibleSequence), ds blks = anspb.helpers.load_list_of_blocks( cast(ansobj.AnsibleSequence, ds), play=r._play, role=r, loader=r._loader, variable_manager=r._variable_manager, use_handlers=handlers) return blks
def test_load_role_complex(self): # FIXME: add tests for the more complex uses of # params and tags/when statements fake_loader = DictDataLoader({ "/etc/ansible/roles/foo/tasks/main.yml": """ - shell: echo 'hello world' """, }) i = RoleInclude.load(dict(role='foo'), loader=fake_loader) r = Role.load(i) self.assertEqual(r.get_name(), "foo")
def test_load_role_with_handlers(self): fake_loader = DictDataLoader({ "/etc/ansible/roles/foo_handlers/handlers/main.yml": """ - name: test handler shell: echo 'hello world' """, }) mock_play = MagicMock() mock_play.ROLE_CACHE = {} i = RoleInclude.load('foo_handlers', play=mock_play, loader=fake_loader) r = Role.load(i, play=mock_play) self.assertEqual(len(r._handler_blocks), 1) assert isinstance(r._handler_blocks[0], Block)
def test_load_role_with_vars(self, _load_role_yaml, _get_role_path): _get_role_path.return_value = ('foo', '/etc/ansible/roles/foo') def fake_load_role_yaml(role_path, subdir): if role_path == '/etc/ansible/roles/foo': if subdir == 'defaults': return dict(foo='bar') elif subdir == 'vars': return dict(foo='bam') return None _load_role_yaml.side_effect = fake_load_role_yaml r = Role.load('foo') self.assertEqual(r.default_vars, dict(foo='bar')) self.assertEqual(r.role_vars, dict(foo='bam'))
def test_load_role_with_vars_dir_vs_file(self): fake_loader = DictDataLoader({ "/etc/ansible/roles/foo_vars/vars/main/foo.yml": """ foo: bar """, "/etc/ansible/roles/foo_vars/vars/main.yml": """ foo: bam """, }) mock_play = MagicMock() mock_play.ROLE_CACHE = {} i = RoleInclude.load('foo_vars', play=mock_play, loader=fake_loader) r = Role.load(i, play=mock_play) self.assertEqual(r._role_vars, dict(foo='bam'))
def test_load_role_complex(self): # FIXME: add tests for the more complex uses of # params and tags/when statements fake_loader = DictDataLoader({ "/etc/ansible/roles/foo_complex/tasks/main.yml": """ - shell: echo 'hello world' """, }) mock_play = MagicMock() mock_play.ROLE_CACHE = {} i = RoleInclude.load(dict(role='foo_complex'), play=mock_play, loader=fake_loader) r = Role.load(i, play=mock_play) self.assertEqual(r.get_name(), "foo_complex")
def _load_roles(self, attr, ds): ''' Loads and returns a list of RoleInclude objects from the datastructure list of role definitions and creates the Role from those objects ''' if ds is None: ds = [] try: role_includes = load_list_of_roles(ds, play=self, variable_manager=self._variable_manager, loader=self._loader) except AssertionError: raise AnsibleParserError("A malformed role declaration was encountered.", obj=self._ds) roles = [] for ri in role_includes: roles.append(Role.load(ri, play=self)) return roles
def _compile_roles(self): ''' Handles the role compilation step, returning a flat list of tasks with the lowest level dependencies first. For example, if a role R has a dependency D1, which also has a dependency D2, the tasks from D2 are merged first, followed by D1, and lastly by the tasks from the parent role R last. This is done for all roles in the Play. ''' task_list = [] if len(self.roles) > 0: for ri in self.roles: # The internal list of roles are actualy RoleInclude objects, # so we load the role from that now role = Role.load(ri) # FIXME: evauluate conditional of roles here? task_list.extend(role.compile()) return task_list
def test_load_role_with_metadata(self): fake_loader = DictDataLoader( { "/etc/ansible/roles/foo/meta/main.yml": """ allow_duplicates: true dependencies: - bar galaxy_info: a: 1 b: 2 c: 3 """, "/etc/ansible/roles/bar/meta/main.yml": """ dependencies: - baz """, "/etc/ansible/roles/baz/meta/main.yml": """ dependencies: - bam """, "/etc/ansible/roles/bam/meta/main.yml": """ dependencies: [] """, "/etc/ansible/roles/bad1/meta/main.yml": """ 1 """, "/etc/ansible/roles/bad2/meta/main.yml": """ foo: bar """, "/etc/ansible/roles/recursive1/meta/main.yml": """ dependencies: ['recursive2'] """, "/etc/ansible/roles/recursive2/meta/main.yml": """ dependencies: ['recursive1'] """, } ) i = RoleInclude.load("foo", loader=fake_loader) r = Role.load(i) role_deps = r.get_direct_dependencies() self.assertEqual(len(role_deps), 1) self.assertEqual(type(role_deps[0]), Role) self.assertEqual(len(role_deps[0].get_parents()), 1) self.assertEqual(role_deps[0].get_parents()[0], r) self.assertEqual(r._metadata.allow_duplicates, True) self.assertEqual(r._metadata.galaxy_info, dict(a=1, b=2, c=3)) all_deps = r.get_all_dependencies() self.assertEqual(len(all_deps), 3) self.assertEqual(all_deps[0].get_name(), "bar") self.assertEqual(all_deps[1].get_name(), "baz") self.assertEqual(all_deps[2].get_name(), "bam") i = RoleInclude.load("bad1", loader=fake_loader) self.assertRaises(AnsibleParserError, Role.load, i) i = RoleInclude.load("bad2", loader=fake_loader) self.assertRaises(AnsibleParserError, Role.load, i) i = RoleInclude.load("recursive1", loader=fake_loader) self.assertRaises(AnsibleError, Role.load, i)
def test_load_role_with_metadata(self): fake_loader = DictDataLoader({ '/etc/ansible/roles/foo_metadata/meta/main.yml': """ allow_duplicates: true dependencies: - bar_metadata galaxy_info: a: 1 b: 2 c: 3 """, '/etc/ansible/roles/bar_metadata/meta/main.yml': """ dependencies: - baz_metadata """, '/etc/ansible/roles/baz_metadata/meta/main.yml': """ dependencies: - bam_metadata """, '/etc/ansible/roles/bam_metadata/meta/main.yml': """ dependencies: [] """, '/etc/ansible/roles/bad1_metadata/meta/main.yml': """ 1 """, '/etc/ansible/roles/bad2_metadata/meta/main.yml': """ foo: bar """, '/etc/ansible/roles/recursive1_metadata/meta/main.yml': """ dependencies: ['recursive2_metadata'] """, '/etc/ansible/roles/recursive2_metadata/meta/main.yml': """ dependencies: ['recursive1_metadata'] """, }) mock_play = MagicMock() mock_play.ROLE_CACHE = {} i = RoleInclude.load('foo_metadata', play=mock_play, loader=fake_loader) r = Role.load(i, play=mock_play) role_deps = r.get_direct_dependencies() self.assertEqual(len(role_deps), 1) self.assertEqual(type(role_deps[0]), Role) self.assertEqual(len(role_deps[0].get_parents()), 1) self.assertEqual(role_deps[0].get_parents()[0], r) self.assertEqual(r._metadata.allow_duplicates, True) self.assertEqual(r._metadata.galaxy_info, dict(a=1, b=2, c=3)) all_deps = r.get_all_dependencies() self.assertEqual(len(all_deps), 3) self.assertEqual(all_deps[0].get_name(), 'bam_metadata') self.assertEqual(all_deps[1].get_name(), 'baz_metadata') self.assertEqual(all_deps[2].get_name(), 'bar_metadata') i = RoleInclude.load('bad1_metadata', play=mock_play, loader=fake_loader) self.assertRaises(AnsibleParserError, Role.load, i, play=mock_play) i = RoleInclude.load('bad2_metadata', play=mock_play, loader=fake_loader) self.assertRaises(AnsibleParserError, Role.load, i, play=mock_play) i = RoleInclude.load('recursive1_metadata', play=mock_play, loader=fake_loader) self.assertRaises(AnsibleError, Role.load, i, play=mock_play)