include_role (role revamp implementation) (#17232)

* attempt #11 to role_include

* fixes from jimi-c

* do not override load_data, move all to load

* removed debugging

* implemented tasks_from parameter, must break cache

* fixed issue with cache and tasks_from

* make resolution of from_tasks prioritize literal

* avoid role dependency dedupe when include_role

* fixed role deps and handlers are now loaded

* simplified code, enabled k=v parsing

used example from jimi-c

* load role defaults for task when include_role

* fixed issue with from_Tasks overriding all subdirs

* corrected priority order of main candidates

* made tasks_from a more generic interface to roles

* fix block inheritance and handler order

* allow vars: clause into included role

* pull vars already processed vs from raw data

* fix from jimi-c blocks i broke

* added back append for dynamic includes

* only allow for basename in from parameter

* fix for docs when no default

* fixed notes

* added include_role to changelog
This commit is contained in:
Brian Coca 2016-08-26 13:42:13 -04:00 committed by GitHub
commit bd9094c925
11 changed files with 163 additions and 30 deletions

View file

@ -66,7 +66,7 @@ class Role(Base, Become, Conditional, Taggable):
_delegate_to = FieldAttribute(isa='string')
_delegate_facts = FieldAttribute(isa='bool', default=False)
def __init__(self, play=None):
def __init__(self, play=None, from_files=None):
self._role_name = None
self._role_path = None
self._role_params = dict()
@ -83,6 +83,10 @@ class Role(Base, Become, Conditional, Taggable):
self._had_task_run = dict()
self._completed = dict()
if from_files is None:
from_files = {}
self._tasks_from = from_files.get('tasks')
super(Role, self).__init__()
def __repr__(self):
@ -92,7 +96,10 @@ class Role(Base, Become, Conditional, Taggable):
return self._role_name
@staticmethod
def load(role_include, play, parent_role=None):
def load(role_include, play, parent_role=None, from_files=None):
if from_files is None:
from_files = {}
try:
# The ROLE_CACHE is a dictionary of role names, with each entry
# containing another dictionary corresponding to a set of parameters
@ -104,6 +111,10 @@ class Role(Base, Become, Conditional, Taggable):
params['when'] = role_include.when
if role_include.tags is not None:
params['tags'] = role_include.tags
if from_files is not None:
params['from_files'] = from_files
if role_include.vars:
params['vars'] = role_include.vars
hashed_params = hash_params(params)
if role_include.role in play.ROLE_CACHE:
for (entry, role_obj) in iteritems(play.ROLE_CACHE[role_include.role]):
@ -112,7 +123,7 @@ class Role(Base, Become, Conditional, Taggable):
role_obj.add_parent(parent_role)
return role_obj
r = Role(play=play)
r = Role(play=play, from_files=from_files)
r._load_role_data(role_include, parent_role=parent_role)
if role_include.role not in play.ROLE_CACHE:
@ -163,7 +174,7 @@ class Role(Base, Become, Conditional, Taggable):
else:
self._metadata = RoleMetadata()
task_data = self._load_role_yaml('tasks')
task_data = self._load_role_yaml('tasks', main=self._tasks_from)
if task_data:
try:
self._task_blocks = load_list_of_blocks(task_data, play=self._play, role=self, loader=self._loader, variable_manager=self._variable_manager)
@ -190,23 +201,36 @@ class Role(Base, Become, Conditional, Taggable):
elif not isinstance(self._default_vars, dict):
raise AnsibleParserError("The defaults/main.yml file for role '%s' must contain a dictionary of variables" % self._role_name)
def _load_role_yaml(self, subdir):
def _load_role_yaml(self, subdir, main=None):
file_path = os.path.join(self._role_path, subdir)
if self._loader.path_exists(file_path) and self._loader.is_directory(file_path):
main_file = self._resolve_main(file_path)
main_file = self._resolve_main(file_path, main)
if self._loader.path_exists(main_file):
return self._loader.load_from_file(main_file)
return None
def _resolve_main(self, basepath):
def _resolve_main(self, basepath, main=None):
''' flexibly handle variations in main filenames '''
post = False
# allow override if set, otherwise use default
if main is None:
main = 'main'
post = True
bare_main = os.path.join(basepath, main)
possible_mains = (
os.path.join(basepath, 'main.yml'),
os.path.join(basepath, 'main.yaml'),
os.path.join(basepath, 'main.json'),
os.path.join(basepath, 'main'),
os.path.join(basepath, '%s.yml' % main),
os.path.join(basepath, '%s.yaml' % main),
os.path.join(basepath, '%s.json' % main),
)
if post:
possible_mains = possible_mains + (bare_main,)
else:
possible_mains = (bare_main,) + possible_mains
if sum([self._loader.is_file(x) for x in possible_mains]) > 1:
raise AnsibleError("found multiple main files at %s, only one allowed" % (basepath))
else:
@ -274,6 +298,7 @@ class Role(Base, Become, Conditional, Taggable):
for dep in self.get_all_dependencies():
all_vars = combine_vars(all_vars, dep.get_vars(include_params=include_params))
all_vars = combine_vars(all_vars, self.vars)
all_vars = combine_vars(all_vars, self._role_vars)
if include_params:
all_vars = combine_vars(all_vars, self.get_role_params(dep_chain=dep_chain))