callback: Add support for specifying default playbook labels

This allows users to set default playbooks labels without needing to
specify the 'ara_playbook_label' variable.

In practice, it can be used to set labels on playbooks without needing
to modify playbooks or the playbook vars.

Fixes: https://github.com/ansible-community/ara/issues/95
Change-Id: Ib1d3b33d1766905061e5534677764aa9c42a99b0
This commit is contained in:
David Moreau Simard 2019-11-29 12:24:46 -05:00
parent 8d9af9b8dc
commit a8b8ff5c59
5 changed files with 81 additions and 25 deletions

View File

@ -105,6 +105,15 @@ options:
ini: ini:
- section: ara - section: ara
key: api_timeout key: api_timeout
default_labels:
description: A list of default labels that will be applied to playbooks
type: list
default: []
env:
- name: ARA_DEFAULT_LABELS
ini:
- section: ara
key: default_labels
ignored_facts: ignored_facts:
description: List of host facts that will not be saved by ARA description: List of host facts that will not be saved by ARA
type: list type: list
@ -154,6 +163,7 @@ class CallbackModule(CallbackBase):
def set_options(self, task_keys=None, var_options=None, direct=None): def set_options(self, task_keys=None, var_options=None, direct=None):
super(CallbackModule, self).set_options(task_keys=task_keys, var_options=var_options, direct=direct) super(CallbackModule, self).set_options(task_keys=task_keys, var_options=var_options, direct=direct)
self.default_labels = self.get_option("default_labels")
self.ignored_facts = self.get_option("ignored_facts") self.ignored_facts = self.get_option("ignored_facts")
self.ignored_arguments = self.get_option("ignored_arguments") self.ignored_arguments = self.get_option("ignored_arguments")
@ -206,8 +216,20 @@ class CallbackModule(CallbackBase):
play_vars = play._variable_manager.get_vars(play=play)["vars"] play_vars = play._variable_manager.get_vars(play=play)["vars"]
if "ara_playbook_name" in play_vars: if "ara_playbook_name" in play_vars:
self._set_playbook_name(name=play_vars["ara_playbook_name"]) self._set_playbook_name(name=play_vars["ara_playbook_name"])
labels = self.default_labels
if "ara_playbook_labels" in play_vars: if "ara_playbook_labels" in play_vars:
self._set_playbook_labels(labels=play_vars["ara_playbook_labels"]) # ara_playbook_labels can be supplied as a list inside a playbook
# but it might also be specified as a comma separated string when
# using extra-vars
if isinstance(play_vars["ara_playbook_labels"], list):
labels.extend(play_vars["ara_playbook_labels"])
elif isinstance(play_vars["ara_playbook_labels"], str):
labels.extend(play_vars["ara_playbook_labels"].split(","))
else:
raise TypeError("ara_playbook_labels must be a list or a comma-separated string")
if labels:
self._set_playbook_labels(labels=labels)
# Record all the files involved in the play # Record all the files involved in the play
for path in play._loader._FILE_CACHE.keys(): for path in play._loader._FILE_CACHE.keys():
@ -307,7 +329,7 @@ class CallbackModule(CallbackBase):
self.playbook = self.client.patch("/api/v1/playbooks/%s" % self.playbook["id"], name=name) self.playbook = self.client.patch("/api/v1/playbooks/%s" % self.playbook["id"], name=name)
def _set_playbook_labels(self, labels): def _set_playbook_labels(self, labels):
if self.playbook["labels"] != labels: if sorted(self.playbook["labels"]) != sorted(labels):
self.playbook = self.client.patch("/api/v1/playbooks/%s" % self.playbook["id"], labels=labels) self.playbook = self.client.patch("/api/v1/playbooks/%s" % self.playbook["id"], labels=labels)
def _get_or_create_file(self, path): def _get_or_create_file(self, path):

View File

@ -48,6 +48,7 @@ an ``ansible.cfg`` file:
api_username = user api_username = user
api_password = password api_password = password
api_timeout = 15 api_timeout = 15
default_labels = dev,deploy
ignored_facts = '["ansible_env", "ansible_all_ipv4_addresses"]' ignored_facts = '["ansible_env", "ansible_all_ipv4_addresses"]'
ignored_arguments = '["extra_vars", "vault_password_files"]' ignored_arguments = '["extra_vars", "vault_password_files"]'
@ -60,6 +61,7 @@ or as environment variables:
export ARA_API_USERNAME=user export ARA_API_USERNAME=user
export ARA_API_PASSWORD=password export ARA_API_PASSWORD=password
export ARA_API_TIMEOUT=15 export ARA_API_TIMEOUT=15
export ARA_DEFAULT_LABELS=dev,deploy
export ARA_IGNORED_FACTS='["ansible_env", "ansible_all_ipv4_addresses"]' export ARA_IGNORED_FACTS='["ansible_env", "ansible_all_ipv4_addresses"]'
export ARA_IGNORED_ARGUMENTS='["extra_vars", "vault_password_files"]' export ARA_IGNORED_ARGUMENTS='["extra_vars", "vault_password_files"]'

View File

@ -1,33 +1,22 @@
Setting playbook names and labels Playbook names and labels
================================= =========================
ARA provides the ability for users to specify playbook names and labels in ARA allows users to specify playbook names and labels in order to better
order to better distinguish playbooks run in different environments or purposes. distinguish playbooks run in different environments or for different purposes.
Names and labels are also searchable by the ARA API, allowing you to find Once your playbooks have names and labels, the API allows you to easily search
playbooks matching your query. for them, for example:
- ``/api/v1/playbooks?name=<playbook_name>``
- ``/api/v1/playbooks?label=<label_name>``
Names and labels are set as regular Ansible variables: Names and labels are set as regular Ansible variables:
- ``ara_playbook_name`` - ``ara_playbook_name``
- ``ara_playbook_labels`` - ``ara_playbook_labels``
These variables can be provided by your Ansible inventory, directly in your These variables are picked up by ARA at the beginning of a play and can be
playbook file, as extra-vars or any other way supported by Ansible. provided directly in your playbook file:
For example, in an inventory:
.. code-block:: ini
[dev]
host1
host2
[dev:vars]
ara_playbook_name=deploy-dev
ara_playbook_labels='["deploy", "dev"]'
In a playbook:
.. code-block:: yaml .. code-block:: yaml
@ -47,4 +36,25 @@ Or as extra-vars:
ansible-playbook -i hosts playbook.yaml \ ansible-playbook -i hosts playbook.yaml \
-e ara_playbook_name=deploy-dev \ -e ara_playbook_name=deploy-dev \
-e ara_playbook_labels='["deploy", "dev"]' -e ara_playbook_labels=deploy,dev
Default labels
--------------
If necessary, ARA can be configured to set one or more labels on every recorded
playbook by default.
This can be done either through an ``ansible.cfg`` file like so:
.. code-block:: ini
[defaults]
# ...
[ara]
default_labels = first_label,second_label
or through the ``ARA_DEFAULT_LABELS`` environment variable:
.. code-block:: bash
export ARA_DEFAULT_LABELS=first_label,second_label

View File

@ -89,6 +89,16 @@
command: "{{ ara_api_venv_path }}/bin/python -m ara.setup.plugins" command: "{{ ara_api_venv_path }}/bin/python -m ara.setup.plugins"
register: ara_setup_plugins register: ara_setup_plugins
- name: Set default labels with Zuul
set_fact:
_default_labels:
- "nodepool.provider:{{ nodepool.provider }}"
- "zuul.change:{{ zuul.change }}"
- "zuul.executor:{{ zuul.executor.hostname }}"
- "zuul.pipeline:{{ zuul.pipeline }}"
- "zuul.project:{{ zuul.project.canonical_name }}"
when: zuul is defined
# These aren't in the same task (i.e, with loop) so we can tell individual test # These aren't in the same task (i.e, with loop) so we can tell individual test
# runs apart easily rather than keeping all the output bundled in a single task. # runs apart easily rather than keeping all the output bundled in a single task.
# TODO: Add validation for the tests # TODO: Add validation for the tests
@ -101,6 +111,7 @@
ARA_SECRET_KEY: "{{ ara_api_secret_key }}" ARA_SECRET_KEY: "{{ ara_api_secret_key }}"
ARA_API_CLIENT: "{{ ara_api_client | default('offline') }}" ARA_API_CLIENT: "{{ ara_api_client | default('offline') }}"
ARA_API_SERVER: "{{ ara_api_server | default('http://127.0.0.1:8000') }}" ARA_API_SERVER: "{{ ara_api_server | default('http://127.0.0.1:8000') }}"
ARA_DEFAULT_LABELS: "{{ _default_labels | join(', ') | default('default-label') }}"
PATH: "{{ ara_api_venv_path }}/bin:/bin:/usr/bin:/usr/local/bin" PATH: "{{ ara_api_venv_path }}/bin:/bin:/usr/bin:/usr/local/bin"
vars: vars:
_test_root: "{{ ara_api_source }}/tests/integration" _test_root: "{{ ara_api_source }}/tests/integration"

View File

@ -63,6 +63,16 @@
changed_when: false changed_when: false
register: ara_setup_plugins register: ara_setup_plugins
- name: Set default labels with Zuul
set_fact:
_default_labels:
- "nodepool.provider:{{ nodepool.provider }}"
- "zuul.change:{{ zuul.change }}"
- "zuul.executor:{{ zuul.executor.hostname }}"
- "zuul.pipeline:{{ zuul.pipeline }}"
- "zuul.project:{{ zuul.project.canonical_name }}"
when: zuul is defined
# These aren't in the same task (i.e, with loop) so we can tell individual test # These aren't in the same task (i.e, with loop) so we can tell individual test
# runs apart easily rather than keeping all the output bundled in a single task. # runs apart easily rather than keeping all the output bundled in a single task.
- environment: - environment:
@ -71,6 +81,7 @@
ARA_SETTINGS: "{{ ara_api_settings }}" ARA_SETTINGS: "{{ ara_api_settings }}"
ARA_API_CLIENT: "{{ ara_api_client | default('offline') }}" ARA_API_CLIENT: "{{ ara_api_client | default('offline') }}"
ARA_API_SERVER: "{{ ara_api_server | default('http://127.0.0.1:8000') }}" ARA_API_SERVER: "{{ ara_api_server | default('http://127.0.0.1:8000') }}"
ARA_DEFAULT_LABELS: "{{ _default_labels | join(', ') | default('default-label') }}"
PATH: "{{ path_with_virtualenv | default('/usr/bin:/usr/local/bin') }}" PATH: "{{ path_with_virtualenv | default('/usr/bin:/usr/local/bin') }}"
vars: vars:
_test_root: "{{ ara_api_source_checkout }}/tests/integration" _test_root: "{{ ara_api_source_checkout }}/tests/integration"