-
Notifications
You must be signed in to change notification settings - Fork 19
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Auto install ansible roles #7
Comments
I'm not really convinced that Approach 3 is actually a different approach to 2, just better documented. The advantages and disadvantages to approach 2 apply to approach 3. |
I agree that the role management has room for improvement. I appreciate the work and thought that has been put in to this. I think option 3 is the strongest, but not all the way there. There are spots that need to be refined and clarified before I could feel comfortable with what it being proposed.
|
@tima I'll try and address each in turn. For full disclosure, I am an advocate for Option 1, Option 2 came from @bcoca and Option 3 came from @chouseknecht . ansible/ansible#15444 is a draft implementation of Option 1. This proposal seems to get bogged down in lots of things that aren't just 'auto install roles' - we don't have to solve every single galaxy/roles problem with this proposal - some concerns might be better as parallel proposals or PRs
I can't see where it says that at all. There is the possibility of versioning info in meta/main.yml - the only place that doesn't have versions is playbooks but as you say, those should be separate concerns anyway.
I agree. I say that as an advantage of Option 1
This is an interesting problem that is not addressed by any of these solutions, but it is not a change from current situation. One approach might be to allow a
The implementation details for Option 3 will have to come from @bcoca or @chouseknecht
Again, this is the current situation and is actually orthogonal to this proposal.Agree, this metadata could be contained in .galaxy_install_info. However, this would not solve the problem you mention below of having a different v1.1 in QA to prod. The PR that introduced tree-ish versions would allow you to point at commit IDs rather than tags, but that's not really very user friendly (due to ordering concerns - it's difficult to see which commit ID is newer). Making role installation absolutely rock solid seems like a hard problem.
SHA commit id should be enough for git/hg based roles. The security of Galaxy service itself is not something I wish to address in this proposal
We'd also need to decide on what to do in the default case, which would presumably be an empty whitelist and auto install switched off. Should ansible-galaxy then refuse to install roles? Or does the whitelist only apply to auto role installation, which might be unexpected by those people actually relying on the whitelist.
I don't think I want to address Galaxy service concerns in this proposal at all - but if Galaxy provides the SHA id we can store it in .galaxy_install_info. I can see that this is definitely a potential problem, but I'm not sure how this can be solved here
I think the multiple version stuff should be out of scope for auto installation
I don't propose changing the current role specification.
I would definitely make this off by default and configurable in ansible.cfg
I don't see why this would need to change. This is just a different mechanism for installing roles. The use of those roles would happen in the same way.
I agree - I found that I was unclear as to where the code separation would be. At least this would mean that you wouldn't need to use ansible-galaxy to install roles! |
Some of the issues i've put into the implementation already or have been covered by @tima above, but I still think its worth noting a few things:
|
There are no new way with Option 1. The new directives (roles_file and (optionally) roles_path) just cause the roles to be installed. I don't understand what I've said to suggest that there is any change here.
I can't find where I got that impression from, apologies for misrepresenting your position.
As far as I'm concerned, you already have pretty much standardized on one format (the yaml roles file). The old style roles format was deprecated with zero consultation, but I can live with that.
I don't have any thoughts on this.
Ok. Thanks for clarifying that - I can live with that, I think we just call the variable role_auto_install_whitelist or something to make it obvious it only applies to auto installs.
Right - I think I see now. This is less manageable if people use one central roles location, although those people are currently asking for problems now. Put it this way, I don't see this as an issue of auto install really, which is just a shortcut - one thing that could help is that if you specify roles_file in the playbook, you must include roles_path too, which would lead to more playbook specific roles than relying on ansible.cfg's roles_path or C.DEFAULT_ROLES_PATH
This does not need to be part of this proposal, it is a separate concern.
Yep, understood from your feedback on the implementation PR. Ripping that additional functionality will greatly simplify the change, which is a good thing.
This is indeed a happy benefit. |
There are still several formats to specify roles:
Most of them are YAML, I would like to simplify to 1 or 2 (tarball being the 2nd). I'm not sure then if your rolesfile.yml is different or overlaps with one of the existing formats. |
I think there's a difference between role usage and role specification (and role source within the roles specification)
The |
We are using Ansible tower with github enterprise and It would be nice to not have to download the role and save it in order to use it. This way you could always get the latest master if you want and it. Also running locally you would not need to .gitignore these roles. It would be great to use the existing galaxy base and use the existing ansible.cfg + roles could look like
And/Or requirements.yml + roles could look like
|
This, plus some basic version pinning, is the only thing keeping me from breaking up some existing Ansible projects into lots of small pieces to put on Galaxy. It's not a huge amount of friction, but it's constantly there. It's also all the more striking that |
To be clear, I'm a massive 👎 on auto-install. Explicit is better than implicit and all that. From my comment on ansible/galaxy-issues#49: We're basically talking about installing dependent library software. In the Python world, we already have pip/virtualenv and conda as comparable examples, both of which support "make my current environment like this specification which comes from a file" in an idempotent way. requirements.yml gives us the specification, now we just need the idempotent install, rather than requiring the --force hack. Autoinstall, for me, would be akin to python magically downloading and installing a package as part of an import statement. Sure, you can implement one of the import hooks to do it, but most people would make decidedly unhappy faces in your general direction if you did. |
This ansible-galaxy autoinstall function is, at a minimum, a core level implementation of what already been in Ansible for Tower for sometime. |
I neither use, not want to use, Ansible Tower. |
@cjw296 this proposal is completely unrelated to Tower, other than that this would provide a benefit of Tower in core (Tower ensures that the correct role versions are installed). And saying that it's like python installing a package as part of an import statement, given the amount of ludicrous code that protects people who currently have old libraries (do |
I am also be a big -1 on auto installation. This has been solved in setuptools/pkg_resources by specifying a requirement and raising a VersionConflict when the version doesn't meet the requirement. I would be much more in favor of aborting and instructing the user to upgrade. |
@sivel that would be acceptable too. We'd still need to specify where those version specifications came from for a particular playbook though. |
See related / similar effort on the Python level: https://github.com/pypa/pipfile (and semi-related issue: ansible/galaxy-issues#165). |
@willthames - right, but |
@cjw296 that seems orthogonal to this proposal - aren't you just asking for ansible/ansible#12904 but without the need to force (which I'd be happy with tbh, but I was asked to put the force back in ansible/ansible#12904 (comment)) |
This proposal is completely unrelated - here I want ansible-playbook to update the roles, without needing ansible-galaxy to be involved at all. See ansible/ansible#15444 for a proposed implementation - the code is now out of date but the principle still seems reasonable. |
@willthames - I'm afraid @chouseknecht decided they were related. Perhaps you could ask them to re-open ansible/galaxy-issues#49 instead? |
@cjw296 I'd say it's not a ansible/galaxy-issue though, but an ansible/ansible issue, which is why ansible/ansible#12904 exists as a solution for some of the idempotency requirements, although #23 takes the solution for conflicting role requirements further. |
@willthames - have to be honest, I'm not that fussed about details, I just want |
Is there any plan to get it done by nearest future? |
Is there any update on this proposal ? I would love to download roles are as pre_task to my playbook |
You know the drill: no update means no update. |
In the past ( 1.9.x releases ), I used a callback plugin for that job, but not working with the ansible 2 releases. |
@samyscoub What plugin have you used to achieve it? I can't see any appropriate plugin in this list. |
@antonbormotov I've made my own custom callback with this function ( working only on ansible 1.x ), and with 2 extra options from ansible-playbook def playbook_on_start(self):
galaxy_noexec = self.playbook.extra_vars.get('galaxy_noexec')
requirements_file = "external_roles/requirements.yml"
galaxy_force = ""
if self.playbook.extra_vars.get('galaxy_force'):
galaxy_force = " --force"
if galaxy_noexec:
print('ANSIBLE-GALAXY | Skipping ansible-galaxy as requested by galaxy_noexec variable.')
return
if not os.path.isfile(os.getcwd() + '/' + requirements_file):
print('ANSIBLE-GALAXY | No <' + requirements_file + '> file found. Skipping ansible_galaxy.')
pass
ansible_galaxy_exe = "ansible-galaxy"
try:
call([ansible_galaxy_exe, 'version'], stdout=PIPE, stderr=PIPE)
except OSError:
print("ANSIBLE-GALAXY | %s not found in path. Please Re Install ansible." % ansible_galaxy_exe)
print('ANSIBLE-GALAXY | Current $PATH variable: %s' % os.environ['PATH'])
sys.exit(1)
print("ANSIBLE-GALAXY | Downloading remote roles dependencies into :\n<" + os.getcwd() + "roles> directory")
print('ANSIBLE-GALAXY | If you want to skip that, set \'-e galaxy_noexec=True\' into the ansible-playbook command line.')
print('ANSIBLE-GALAXY | If you want to force ansible-galxy to retrieve already downloaded roles, set \'-e galaxy_force=True\' into the ansible-playbook command line.')
os.system("cd " + os.getcwd() + " && " + ansible_galaxy_exe + " install -r " + requirements_file + "" + galaxy_force)
pass I've writtent the same code ( adapted to ansible V2 callback requirements ), def v2_playbook_on_start(self, playbook):
self.playbook = playbook
self.playbook_name = os.path.basename(self.playbook._file_name)
ansible_galaxy_exe = "ansible-galaxy"
galaxy_options = {
'galaxy_noexec': 'no',
'galaxy_force': 'no',
'galaxy_file': 'roles/requirements.yml',
}
if self._display.verbosity > 1:
from os.path import basename
self._display.banner("PLAYBOOK: %s" % basename(playbook._file_name))
if self._display.verbosity > 3:
if self._options is not None:
for option in dir(self._options):
if option.startswith('_') or option in ['read_file', 'ensure_value', 'read_module']:
continue
val = getattr(self._options,option)
if val:
self._display.vvvv('%s: %s' % (option,val))
if self._display.verbosity > 1:
self._display.display('%s | v2_playbook_on_start function from callback' % (self.callback_prefix))
galaxy_noexec =""
if self._options and self._options.extra_vars:
result = [m.group(1) for m in (re.search('^galaxy_noexec=(.*)$', l) for l in self._options.extra_vars) if m]
if len(result) > 0:
galaxy_options['galaxy_noexec'] = result.pop()
result = [m.group(1) for m in (re.search('^galaxy_force=(.*)$', l) for l in self._options.extra_vars) if m]
if len(result) > 0:
galaxy_options['galaxy_force'] = result.pop()
result = [m.group(1) for m in (re.search('^galaxy_file=(.*)$', l) for l in self._options.extra_vars) if m]
if len(result) > 0:
galaxy_options['galaxy_file'] = result.pop()
if self._display.verbosity > 1:
self._display.display('%s | galaxy_noexec: %s' % (self.callback_prefix,galaxy_options['galaxy_noexec']))
self._display.display('%s | galaxy_force: %s' % (self.callback_prefix,galaxy_options['galaxy_force']))
self._display.display('%s | galaxy_file: %s' % (self.callback_prefix,galaxy_options['galaxy_file']))
galaxy_force = ""
if galaxy_options['galaxy_force'] == 'yes':
galaxy_force = " --force"
if galaxy_options['galaxy_noexec'] == 'yes':
self._display.notice("%s | Skipping ansible-galaxy as requested by galaxy_noexec variable." % (self.callback_prefix))
return
try:
call([ansible_galaxy_exe, 'version'], stdout=PIPE, stderr=PIPE)
except OSError:
self._display.display("%s | `%s` executable file not found in path. Please re-install ansible." % (self.callback_prefix,ansible_galaxy_exe),color=C.COLOR_ERROR)
if self._display.verbosity > 1:
self._display.display("%s | Current $PATH variable: %s' % os.environ['PATH']" % (self.callback_prefix),color=C.COLOR_ERROR)
sys.exit(1)
self._display.display("%s | ANSIBLE will try to download roles automatically" % (self.callback_prefix),color=C.COLOR_WARN)
self._display.display("%s | If you want to skip that behaviour, set \'-e galaxy_noexec=yes\' into the ansible-playbook command line." % (self.callback_prefix),color=C.COLOR_WARN)
if galaxy_options['galaxy_force'] == 'no':
self._display.display("%s | If you want to force ansible-galaxy to retrieve already downloaded roles, set \'-e galaxy_force=yes\' into the ansible-playbook command line." % (self.callback_prefix),color=C.COLOR_WARN)
if os.path.isfile(galaxy_options['galaxy_file']):
self._display.display("%s | Downloading remote roles dependencies into roles directory set into galaxy conf file <%s>" % (self.callback_prefix,galaxy_options['galaxy_file']),color=C.COLOR_VERBOSE)
self._display.display("%s | If you want to overwrite this default file, set \'-e galaxy_file=/SOME_FILE\' into the ansible-playbook command line." % (self.callback_prefix),color=C.COLOR_WARN)
os.system("cd " + os.getcwd() + " && " + ansible_galaxy_exe + " install -r " + galaxy_options['galaxy_file'] + galaxy_force)
else:
self._display.display("%s | the default galaxy conf file <%s> does not exists, aborting" % (self.callback_prefix),color=C.COLOR_ERROR)
self._display.display("%s | please set a different galaxy conf file with \'-e galaxy_file=/SOME_FILE\' extra_var option into the ansible-playbook command line." % (self.callback_prefix,galaxy_options['galaxy_file']),color=C.COLOR_ERROR)
return
pass but the behaviour is not the same with ansible V2 => roles retrieving check from playbook is made before callback launching, and my own callback can't work when role has not been downloaded once 😕 |
An alternative proposal that I think addresses the motivation behind this issue: The main problem that I have with the current idiom of running ansible-galaxy install before ansible-playbook, is that when I change something in requirements.yml, I can't easily guarantee that other persons also working with the same playbook will run ansible-galaxy. There is the dangerous possibility that somebody will rollout the new version of the playbook with old copies of external roles. Instead of implementing auto-downloading of requirements in ansible-playbook, what would perfect for me, would be to instruct ansible-playbook on how to only verify that the requirements from requirements.yml are fulfilled. I.e. it could quickly (and locally) compare if the versions specified in there, match with what is downloaded, and only abort with an error message in case there is a version mismatch. I can then execute ansible-galaxy, that doesn't bother me. This would have the advantage that it would be a lot less intrusive and "magical", while at the same time solving the problem of making sure that everybody uses the same version of the external roles. |
@schweikert, none of that is possible unless we nail down 'role versioning', which is why I consider it a requirement before we even think of auto downloads. |
@bcoca Has that not been done yet? The devel documentation claims it is a feature of 2.5: http://docs.ansible.com/ansible/devel/roadmap/ROADMAP_2_5.html#role-versioning |
It seems, though, that for the purposes of auto-install, or version compare, we could do a pretty blind/dumb "does the version in Or am I missing something? I know for more advanced features like "give me 3.0 or later" (or "v3 or later") will require a more hard-lined approach to versioning, but this seems like a lot more limited/achievable scope. |
+1 for this proposal |
Ansible could achieve the dream of being a metaphorical "one button provisioner", but because of the lack of this feature it is truly a provisioner that can't even provision itself. Ironic. |
Any news? Waiting this in ansible-pull... |
With the inclusion of |
@agaffney with respect, that's a massive hack. Playbook authors shouldn't have to do that just to have roles auto-installed. It could just be seamless. |
It's definitely a bit of a hack, but it does address some of the previous concerns about using wrapper scripts and somebody running the playbook without the wrapper. However, it's not really that bad to have |
And as a side note, the Ansible devs have generally rejected specific feature requests for things that it's already possible to do in your playbook with only a little extra effort. |
I suppose this is a hack too, but making this the first task in a role ensures all - name: Download required roles
local_action: command ansible-galaxy install -r {{ role_path }}/roles/requirements.yml
become: false See ansible/ansible#56046 (comment) for why we can't call these dependencies, which I believe can get downloaded automatically, even though we should be able to. |
@colans I believe your paticular issue is already solved using "meta" and "dependencies: []". Doing ansible-galaxy against such role automatically downloads |
@vladimirtiukhtin My point was that meta/dependencies doesn't work properly. If I add a role there and then call it with See the issue I referenced for more information. |
Relying on an ansible-pull playbook to fetch dependencies forces you to use |
Proposal: Auto Install Ansible Roles
Author: Will Thames @willthames
Date: 2016/04/14
Motivation
To use the latest (or even a specific) version of a playbook with the
appropriate roles, the following steps are typically required:
The most likely step in this process to be forgotten is the middle step. While
we can improve processes and documentation to try and ensure that this step is
not skipped, we can improve ansible-playbook so that the step is not required.
Problems
What problems exist that this proposal will solve?
Solution proposal
Approach 1: Specify rolesfile and rolesdir in playbook
Provide new
rolesdir
androlesfile
keywords:Running ansible-playbook against such a playbook would cause the roles listed in
rolesfile
to be installed inrolesdir
.Add new configuration to allow default rolesfile, default rolesdir and
whether or not to auto update roles (defaulting to False)
Advantages
Disadvantage
Approach 2: Allow rolesfile inclusion
Allow the
roles
section to include a roles file:Running this playbook would cause the roles to be updated from the included
roles file.
This would also be functionally equivalent to specifying the roles file
content within the playbook:
Advantages
Disadvantage
playbook and upgrade playbook would likely have some overlap - currently
you can use the same rolesfile with ansible-galaxy so that the same
roles are available but only a subset of roles is used by the smaller
playbook.
as role tagging.
an override keyword for rolesdir and role auto update)
Approach 3:
Author: chouseknecht@chouseknecht
Date: 24/02/2016
This is a combination of ideas taken from IRC, the ansible development group, and conversations at the recent contributor's summit. It also incorporates most of the ideas from Approach 1 (above) with two notables exceptions: 1) it eliminates maintaining a roles file (or what we think of today as requirements.yml); and 2) it does not include the definition of rolesdir in the playbook.
Here's the approach:
Share the role install logic between ansible-playbook and ansible-galaxy so that ansible-playbook can resolve and install missing roles at playbook run time simply by evaluating the playbook.
Ansible-galaxy installs or preloads roles also by examining a playbook.
Deprecate support for requirements.yaml (the two points above make it unnecessary).
Make ansible-playbook auto-downloading of roles configurable in ansible.cfg. In certain circumstance it may be desirable to disable auto-download.
Provide one format for specifying a role whether in a playbook or in meta/main.yml. Suggested format:
For roles installed from Galaxy, Galaxy should provide some measure of security against version change. Galaxy should track the commit related to a version. If the role owner changes historical versions (today tags) and thus changes the commit hash, the affected version would become un-installable.
Refactor the install process to encompass the following :
Idempotency - If a role version is already installed, don’t attempt to install it again. If symlinks are present (see below), don’t break or remove them. (Note by @willthames: Improve ansible-galaxy handling of role versions ansible#12904)
Provide a --force option that overrides idempotency.
Install roles via tree-ish references, not just tags or commits (This PR is merged).
Support a whitelist of role sources. Galaxy should not be automatically assumed to be part of the whitelist.
Continue to be recursive, allowing roles to have dependencies specified in meta/main.yml.
Continue to install roles in the roles_path.
Use a symlink approach to managing role versions in the roles_path. Example:
Dependencies (optional)
Testing (optional)
Plan to improve the existing galaxy tests
Documentation (optional)
Update to various roles docs
Anything else?
The author's preferred solution remains number 1.
The problem with approach 3 is that version pinning becomes a playbook concern.
Having rolesfile specify roles versions, and playbooks make use of those roles, reduces the overall complexity of both. Using a shared roles_path directory for all roles will work if the playbook does specify versions, but no other tool pins versions in the source code itself (i.e. moving to this approach would be different to how pip, maven etc. specify the versions to use separately to the source code)
The text was updated successfully, but these errors were encountered: