mirror of
https://github.com/ansible-collections/community.general.git
synced 2024-09-14 20:13:21 +02:00
Make v2 playbook class attributes inheritable
Also fixing some other become-related things
This commit is contained in:
parent
8d8c4c0615
commit
393246fdd3
14 changed files with 152 additions and 51 deletions
|
@ -157,11 +157,8 @@ class ConnectionInformation:
|
|||
new_info.copy(self)
|
||||
|
||||
for attr in ('connection', 'remote_user', 'become', 'become_user', 'become_pass', 'become_method', 'environment', 'no_log'):
|
||||
attr_val = None
|
||||
if hasattr(task, attr):
|
||||
attr_val = getattr(task, attr)
|
||||
if task._block and hasattr(task._block, attr) and not attr_val:
|
||||
attr_val = getattr(task._block, attr)
|
||||
if attr_val:
|
||||
setattr(new_info, attr, attr_val)
|
||||
|
||||
|
@ -184,6 +181,7 @@ class ConnectionInformation:
|
|||
|
||||
executable = executable or '$SHELL'
|
||||
|
||||
success_cmd = pipes.quote('echo %s; %s' % (success_key, cmd))
|
||||
if self.become:
|
||||
if self.become_method == 'sudo':
|
||||
# Rather than detect if sudo wants a password this time, -k makes sudo always ask for
|
||||
|
@ -195,23 +193,23 @@ class ConnectionInformation:
|
|||
exe = become_settings.get('sudo_exe', C.DEFAULT_SUDO_EXE)
|
||||
flags = become_settings.get('sudo_flags', C.DEFAULT_SUDO_FLAGS)
|
||||
becomecmd = '%s -k && %s %s -S -p "%s" -u %s %s -c %s' % \
|
||||
(exe, exe, flags or C.DEFAULT_SUDO_FLAGS, prompt, self.become_user, executable, pipes.quote('echo %s; %s' % (success_key, cmd)))
|
||||
(exe, exe, flags or C.DEFAULT_SUDO_FLAGS, prompt, self.become_user, executable, success_cmd)
|
||||
|
||||
elif self.become_method == 'su':
|
||||
exe = become_settings.get('su_exe', C.DEFAULT_SU_EXE)
|
||||
flags = become_settings.get('su_flags', C.DEFAULT_SU_FLAGS)
|
||||
becomecmd = '%s %s %s -c "%s -c %s"' % (exe, flags, self.become_user, executable, pipes.quote('echo %s; %s' % (success_key, cmd)))
|
||||
becomecmd = '%s %s %s -c "%s -c %s"' % (exe, flags, self.become_user, executable, success_cmd)
|
||||
|
||||
elif self.become_method == 'pbrun':
|
||||
exe = become_settings.get('pbrun_exe', 'pbrun')
|
||||
flags = become_settings.get('pbrun_flags', '')
|
||||
becomecmd = '%s -b -l %s -u %s "%s"' % (exe, flags, user, pipes.quote('echo %s; %s' % (success_key, cmd)))
|
||||
becomecmd = '%s -b -l %s -u %s "%s"' % (exe, flags, user, success_cmd)
|
||||
|
||||
elif self.become_method == 'pfexec':
|
||||
exe = become_settings.get('pfexec_exe', 'pbrun')
|
||||
flags = become_settings.get('pfexec_flags', '')
|
||||
# No user as it uses it's own exec_attr to figure it out
|
||||
becomecmd = '%s %s "%s"' % (exe, flags, pipes.quote('echo %s; %s' % (success_key, cmd)))
|
||||
becomecmd = '%s %s "%s"' % (exe, flags, success_cmd)
|
||||
|
||||
else:
|
||||
raise errors.AnsibleError("Privilege escalation method not found: %s" % method)
|
||||
|
|
|
@ -72,11 +72,20 @@ class Base:
|
|||
def munge(self, ds):
|
||||
''' infrequently used method to do some pre-processing of legacy terms '''
|
||||
|
||||
for base_class in self.__class__.__bases__:
|
||||
method = getattr(self, ("_munge_%s" % base_class.__name__).lower(), None)
|
||||
if method:
|
||||
ds = method(ds)
|
||||
def _get_base_classes_munge(target_class):
|
||||
base_classes = list(target_class.__bases__[:])
|
||||
for base_class in target_class.__bases__:
|
||||
base_classes.extend( _get_base_classes_munge(base_class))
|
||||
return base_classes
|
||||
|
||||
base_classes = list(self.__class__.__bases__[:])
|
||||
for base_class in self.__class__.__bases__:
|
||||
base_classes.extend(_get_base_classes_munge(base_class))
|
||||
|
||||
for base_class in base_classes:
|
||||
method = getattr(self, "_munge_%s" % base_class.__name__.lower(), None)
|
||||
if method:
|
||||
return method(ds)
|
||||
return ds
|
||||
|
||||
def load_data(self, ds, variable_manager=None, loader=None):
|
||||
|
@ -271,15 +280,21 @@ class Base:
|
|||
# optionally allowing masking by accessors
|
||||
|
||||
if not needle.startswith("_"):
|
||||
method = "get_%s" % needle
|
||||
if method in self.__dict__:
|
||||
return method(self)
|
||||
method = "_get_attr_%s" % needle
|
||||
if method in dir(self):
|
||||
return getattr(self, method)()
|
||||
|
||||
if needle in self._attributes:
|
||||
return self._attributes[needle]
|
||||
|
||||
raise AttributeError("attribute not found in %s: %s" % (self.__class__.__name__, needle))
|
||||
|
||||
def __setattr__(self, needle, value):
|
||||
if hasattr(self, '_attributes') and needle in self._attributes:
|
||||
self._attributes[needle] = value
|
||||
else:
|
||||
super(Base, self).__setattr__(needle, value)
|
||||
|
||||
def __getstate__(self):
|
||||
return self.serialize()
|
||||
|
||||
|
|
|
@ -95,3 +95,41 @@ class Become:
|
|||
ds['become_user'] = C.DEFAULT_BECOME_USER
|
||||
|
||||
return ds
|
||||
|
||||
def _get_attr_become(self):
|
||||
'''
|
||||
Override for the 'become' getattr fetcher, used from Base.
|
||||
'''
|
||||
if hasattr(self, '_get_parent_attribute'):
|
||||
return self._get_parent_attribute('become')
|
||||
else:
|
||||
return self._attributes['become']
|
||||
|
||||
def _get_attr_become_method(self):
|
||||
'''
|
||||
Override for the 'become_method' getattr fetcher, used from Base.
|
||||
'''
|
||||
if hasattr(self, '_get_parent_attribute'):
|
||||
return self._get_parent_attribute('become_method')
|
||||
else:
|
||||
return self._attributes['become_method']
|
||||
|
||||
def _get_attr_become_user(self):
|
||||
'''
|
||||
Override for the 'become_user' getattr fetcher, used from Base.
|
||||
'''
|
||||
if hasattr(self, '_get_parent_attribute'):
|
||||
return self._get_parent_attribute('become_user')
|
||||
else:
|
||||
return self._attributes['become_user']
|
||||
|
||||
def _get_attr_become_password(self):
|
||||
'''
|
||||
Override for the 'become_password' getattr fetcher, used from Base.
|
||||
'''
|
||||
if hasattr(self, '_get_parent_attribute'):
|
||||
return self._get_parent_attribute('become_password')
|
||||
else:
|
||||
return self._attributes['become_password']
|
||||
|
||||
|
||||
|
|
|
@ -131,23 +131,24 @@ class Block(Base, Become, Conditional, Taggable):
|
|||
# use_handlers=self._use_handlers,
|
||||
# )
|
||||
|
||||
def compile(self):
|
||||
'''
|
||||
Returns the task list for this object
|
||||
'''
|
||||
|
||||
task_list = []
|
||||
for task in self.block:
|
||||
# FIXME: evaulate task tags/conditionals here
|
||||
task_list.extend(task.compile())
|
||||
|
||||
return task_list
|
||||
|
||||
def copy(self):
|
||||
def _dupe_task_list(task_list, new_block):
|
||||
new_task_list = []
|
||||
for task in task_list:
|
||||
new_task = task.copy(exclude_block=True)
|
||||
new_task._block = new_block
|
||||
new_task_list.append(new_task)
|
||||
return new_task_list
|
||||
|
||||
new_me = super(Block, self).copy()
|
||||
new_me._use_handlers = self._use_handlers
|
||||
new_me._dep_chain = self._dep_chain[:]
|
||||
|
||||
new_me.block = _dupe_task_list(self.block or [], new_me)
|
||||
new_me.rescue = _dupe_task_list(self.rescue or [], new_me)
|
||||
new_me.always = _dupe_task_list(self.always or [], new_me)
|
||||
print("new block tasks are: %s" % new_me.block)
|
||||
|
||||
new_me._parent_block = None
|
||||
if self._parent_block:
|
||||
new_me._parent_block = self._parent_block.copy()
|
||||
|
@ -252,3 +253,24 @@ class Block(Base, Become, Conditional, Taggable):
|
|||
for dep in self._dep_chain:
|
||||
dep.set_loader(loader)
|
||||
|
||||
def _get_parent_attribute(self, attr):
|
||||
'''
|
||||
Generic logic to get the attribute or parent attribute for a block value.
|
||||
'''
|
||||
|
||||
value = self._attributes[attr]
|
||||
if not value:
|
||||
if self._parent_block:
|
||||
value = getattr(self._block, attr)
|
||||
elif self._role:
|
||||
value = getattr(self._role, attr)
|
||||
if not value and len(self._dep_chain):
|
||||
reverse_dep_chain = self._dep_chain[:]
|
||||
reverse_dep_chain.reverse()
|
||||
for dep in reverse_dep_chain:
|
||||
value = getattr(dep, attr)
|
||||
if value:
|
||||
break
|
||||
|
||||
return value
|
||||
|
||||
|
|
|
@ -37,6 +37,7 @@ def load_list_of_blocks(ds, parent_block=None, role=None, task_include=None, use
|
|||
assert type(ds) in (list, NoneType)
|
||||
|
||||
block_list = []
|
||||
print("in load list of blocks, ds is: %s" % ds)
|
||||
if ds:
|
||||
for block in ds:
|
||||
b = Block.load(
|
||||
|
@ -50,6 +51,7 @@ def load_list_of_blocks(ds, parent_block=None, role=None, task_include=None, use
|
|||
)
|
||||
block_list.append(b)
|
||||
|
||||
print("-> returning block list: %s" % block_list)
|
||||
return block_list
|
||||
|
||||
|
||||
|
|
|
@ -219,6 +219,7 @@ class Play(Base, Taggable, Become):
|
|||
block_list.extend(self.tasks)
|
||||
block_list.extend(self.post_tasks)
|
||||
|
||||
print("block list is: %s" % block_list)
|
||||
return block_list
|
||||
|
||||
def get_vars(self):
|
||||
|
|
|
@ -30,6 +30,7 @@ from ansible.errors import AnsibleError, AnsibleParserError
|
|||
from ansible.parsing import DataLoader
|
||||
from ansible.playbook.attribute import FieldAttribute
|
||||
from ansible.playbook.base import Base
|
||||
from ansible.playbook.become import Become
|
||||
from ansible.playbook.conditional import Conditional
|
||||
from ansible.playbook.helpers import load_list_of_blocks, compile_block_list
|
||||
from ansible.playbook.role.include import RoleInclude
|
||||
|
@ -69,7 +70,7 @@ def hash_params(params):
|
|||
ROLE_CACHE = dict()
|
||||
|
||||
|
||||
class Role(Base, Conditional, Taggable):
|
||||
class Role(Base, Become, Conditional, Taggable):
|
||||
|
||||
def __init__(self):
|
||||
self._role_name = None
|
||||
|
@ -136,6 +137,12 @@ class Role(Base, Conditional, Taggable):
|
|||
if parent_role:
|
||||
self.add_parent(parent_role)
|
||||
|
||||
# copy over all field attributes, except for when and tags, which
|
||||
# are special cases and need to preserve pre-existing values
|
||||
for (attr_name, _) in iteritems(self._get_base_attributes()):
|
||||
if attr_name not in ('when', 'tags'):
|
||||
setattr(self, attr_name, getattr(role_include, attr_name))
|
||||
|
||||
current_when = getattr(self, 'when')[:]
|
||||
current_when.extend(role_include.when)
|
||||
setattr(self, 'when', current_when)
|
||||
|
@ -144,10 +151,6 @@ class Role(Base, Conditional, Taggable):
|
|||
current_tags.extend(role_include.tags)
|
||||
setattr(self, 'tags', current_tags)
|
||||
|
||||
# save the current base directory for the loader and set it to the current role path
|
||||
#cur_basedir = self._loader.get_basedir()
|
||||
#self._loader.set_basedir(self._role_path)
|
||||
|
||||
# load the role's files, if they exist
|
||||
library = os.path.join(self._role_path, 'library')
|
||||
if os.path.isdir(library):
|
||||
|
@ -179,9 +182,6 @@ class Role(Base, Conditional, Taggable):
|
|||
elif self._default_vars is None:
|
||||
self._default_vars = dict()
|
||||
|
||||
# and finally restore the previous base directory
|
||||
#self._loader.set_basedir(cur_basedir)
|
||||
|
||||
def _load_role_yaml(self, subdir):
|
||||
file_path = os.path.join(self._role_path, subdir)
|
||||
if self._loader.path_exists(file_path) and self._loader.is_directory(file_path):
|
||||
|
@ -313,9 +313,6 @@ class Role(Base, Conditional, Taggable):
|
|||
for dep in deps:
|
||||
dep_blocks = dep.compile(dep_chain=new_dep_chain)
|
||||
for dep_block in dep_blocks:
|
||||
# since we're modifying the task, and need it to be unique,
|
||||
# we make a copy of it here and assign the dependency chain
|
||||
# to the copy, then append the copy to the task list.
|
||||
new_dep_block = dep_block.copy()
|
||||
new_dep_block._dep_chain = new_dep_chain
|
||||
block_list.append(new_dep_block)
|
||||
|
|
|
@ -28,6 +28,7 @@ from ansible.errors import AnsibleError
|
|||
from ansible.parsing.yaml.objects import AnsibleBaseYAMLObject, AnsibleMapping
|
||||
from ansible.playbook.attribute import Attribute, FieldAttribute
|
||||
from ansible.playbook.base import Base
|
||||
from ansible.playbook.become import Become
|
||||
from ansible.playbook.conditional import Conditional
|
||||
from ansible.playbook.taggable import Taggable
|
||||
from ansible.utils.path import unfrackpath
|
||||
|
@ -36,7 +37,7 @@ from ansible.utils.path import unfrackpath
|
|||
__all__ = ['RoleDefinition']
|
||||
|
||||
|
||||
class RoleDefinition(Base, Conditional, Taggable):
|
||||
class RoleDefinition(Base, Become, Conditional, Taggable):
|
||||
|
||||
_role = FieldAttribute(isa='string')
|
||||
|
||||
|
@ -57,6 +58,9 @@ class RoleDefinition(Base, Conditional, Taggable):
|
|||
|
||||
assert isinstance(ds, dict) or isinstance(ds, string_types)
|
||||
|
||||
if isinstance(ds, dict):
|
||||
ds = super(RoleDefinition, self).munge(ds)
|
||||
|
||||
# we create a new data structure here, using the same
|
||||
# object used internally by the YAML parsing code so we
|
||||
# can preserve file:line:column information if it exists
|
||||
|
@ -88,7 +92,7 @@ class RoleDefinition(Base, Conditional, Taggable):
|
|||
self._ds = ds
|
||||
|
||||
# and return the cleaned-up data structure
|
||||
return super(RoleDefinition, self).munge(new_ds)
|
||||
return new_ds
|
||||
|
||||
def _load_role_name(self, ds):
|
||||
'''
|
||||
|
|
|
@ -210,20 +210,21 @@ class Task(Base, Conditional, Taggable, Become):
|
|||
del all_vars['when']
|
||||
return all_vars
|
||||
|
||||
def compile(self):
|
||||
'''
|
||||
For tasks, this is just a dummy method returning an array
|
||||
with 'self' in it, so we don't have to care about task types
|
||||
further up the chain.
|
||||
'''
|
||||
# no longer used, as blocks are the lowest level of compilation now
|
||||
#def compile(self):
|
||||
# '''
|
||||
# For tasks, this is just a dummy method returning an array
|
||||
# with 'self' in it, so we don't have to care about task types
|
||||
# further up the chain.
|
||||
# '''
|
||||
#
|
||||
# return [self]
|
||||
|
||||
return [self]
|
||||
|
||||
def copy(self):
|
||||
def copy(self, exclude_block=False):
|
||||
new_me = super(Task, self).copy()
|
||||
|
||||
new_me._block = None
|
||||
if self._block:
|
||||
if self._block and not exclude_block:
|
||||
new_me._block = self._block.copy()
|
||||
|
||||
new_me._role = None
|
||||
|
@ -309,3 +310,12 @@ class Task(Base, Conditional, Taggable, Become):
|
|||
if self._task_include:
|
||||
self._task_include.set_loader(loader)
|
||||
|
||||
def _get_parent_attribute(self, attr):
|
||||
'''
|
||||
Generic logic to get the attribute or parent attribute for a task value.
|
||||
'''
|
||||
value = self._attributes[attr]
|
||||
if not value and self._block:
|
||||
value = getattr(self._block, attr)
|
||||
return value
|
||||
|
||||
|
|
1
v2/samples/roles/test_become_r1/meta/main.yml
Normal file
1
v2/samples/roles/test_become_r1/meta/main.yml
Normal file
|
@ -0,0 +1 @@
|
|||
allow_duplicates: yes
|
2
v2/samples/roles/test_become_r1/tasks/main.yml
Normal file
2
v2/samples/roles/test_become_r1/tasks/main.yml
Normal file
|
@ -0,0 +1,2 @@
|
|||
- debug: msg="this is test_become_r1"
|
||||
- command: whoami
|
3
v2/samples/roles/test_become_r2/meta/main.yml
Normal file
3
v2/samples/roles/test_become_r2/meta/main.yml
Normal file
|
@ -0,0 +1,3 @@
|
|||
allow_duplicates: yes
|
||||
dependencies:
|
||||
- test_become_r1
|
2
v2/samples/roles/test_become_r2/tasks/main.yml
Normal file
2
v2/samples/roles/test_become_r2/tasks/main.yml
Normal file
|
@ -0,0 +1,2 @@
|
|||
- debug: msg="this is test_become_r2"
|
||||
- command: whoami
|
|
@ -1,8 +1,14 @@
|
|||
- hosts: all
|
||||
gather_facts: no
|
||||
roles:
|
||||
- { role: test_become_r2 }
|
||||
- { role: test_become_r2, sudo_user: testing }
|
||||
tasks:
|
||||
- command: whoami
|
||||
- command: whoami
|
||||
become_user: testing
|
||||
- block:
|
||||
- command: whoami
|
||||
- block:
|
||||
- command: whoami
|
||||
become_user: testing
|
||||
|
|
Loading…
Reference in a new issue