From 9b0c08f77a35d5d4de450569676568a9a97b05aa Mon Sep 17 00:00:00 2001 From: Florian Apolloner Date: Fri, 14 Jun 2019 10:00:34 +0200 Subject: [PATCH] Add distributed_sqlite db backend and WSGI app This new feature is an implementation equivalent to the sqlite middleware from 0.x but in Django instead of Flask. It uses a WSGI middleware to map an URL to a location on the filesystem which is then loaded by the database backend. Fixes: https://github.com/ansible-community/ara/issues/57 Co-Authored-By: Florian Apolloner Co-Authored-By: David Moreau-Simard Change-Id: I338dae2fabda2af7fd0a75c795c7b4fd6b179940 --- .zuul.d/project.yaml | 2 + .zuul.d/role-jobs.yaml | 7 ++ ara/server/db/backends/__init__.py | 0 .../backends/distributed_sqlite/__init__.py | 0 .../db/backends/distributed_sqlite/base.py | 34 +++++++ ara/server/settings.py | 23 ++++- ara/server/wsgi.py | 76 ++++++++++++++++ doc/source/api-configuration.rst | 59 +++++++++++++ doc/source/distributed-sqlite-backend.rst | 88 +++++++++++++++++++ doc/source/index.rst | 1 + roles/ara_api/defaults/main.yaml | 11 ++- roles/ara_api/tasks/config.yaml | 12 +++ ...server.db.backends.distributed_sqlite.yaml | 22 +++++ roles/ara_api/templates/ara-api.service.j2 | 4 +- tests/test_tasks.yaml | 2 + tests/with_distributed_sqlite.yaml | 80 +++++++++++++++++ 16 files changed, 417 insertions(+), 4 deletions(-) create mode 100644 ara/server/db/backends/__init__.py create mode 100644 ara/server/db/backends/distributed_sqlite/__init__.py create mode 100644 ara/server/db/backends/distributed_sqlite/base.py create mode 100644 doc/source/distributed-sqlite-backend.rst create mode 100644 roles/ara_api/tasks/database_engine/ara.server.db.backends.distributed_sqlite.yaml create mode 100644 tests/with_distributed_sqlite.yaml diff --git a/.zuul.d/project.yaml b/.zuul.d/project.yaml index bc80acfc..01b392ad 100644 --- a/.zuul.d/project.yaml +++ b/.zuul.d/project.yaml @@ -11,6 +11,7 @@ - ansible-role-ara-api-ubuntu-2.6 - ansible-role-ara-api-ubuntu-postgresql - ansible-role-ara-api-ubuntu-mysql + - ansible-role-ara-api-fedora-distributed-sqlite - ansible-role-ara-web-ubuntu - ansible-role-ara-web-fedora - ara-tox-linters @@ -23,6 +24,7 @@ - ansible-role-ara-api-ubuntu-2.6 - ansible-role-ara-api-ubuntu-postgresql - ansible-role-ara-api-ubuntu-mysql + - ansible-role-ara-api-fedora-distributed-sqlite - ansible-role-ara-web-ubuntu - ansible-role-ara-web-fedora - ara-tox-linters diff --git a/.zuul.d/role-jobs.yaml b/.zuul.d/role-jobs.yaml index 41efa369..915c72a5 100644 --- a/.zuul.d/role-jobs.yaml +++ b/.zuul.d/role-jobs.yaml @@ -85,6 +85,13 @@ pre-run: tests/install_docker.yaml run: tests/with_mysql.yaml +- job: + name: ansible-role-ara-api-fedora-distributed-sqlite + parent: ansible-role-ara-api-fedora + roles: + - zuul: zuul/zuul-jobs + run: tests/with_distributed_sqlite.yaml + # ara-web jobs - job: name: ansible-role-ara-web-base diff --git a/ara/server/db/backends/__init__.py b/ara/server/db/backends/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/ara/server/db/backends/distributed_sqlite/__init__.py b/ara/server/db/backends/distributed_sqlite/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/ara/server/db/backends/distributed_sqlite/base.py b/ara/server/db/backends/distributed_sqlite/base.py new file mode 100644 index 00000000..eb7f5cb5 --- /dev/null +++ b/ara/server/db/backends/distributed_sqlite/base.py @@ -0,0 +1,34 @@ +# Copyright (c) 2019 Red Hat, Inc. +# +# This file is part of ARA Records Ansible. +# +# ARA is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# ARA is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with ARA. If not, see . + +import threading + +from django.db.backends.sqlite3.base import DatabaseWrapper as BaseDatabaseWrapper + +local_storage = threading.local() + + +class DatabaseWrapper(BaseDatabaseWrapper): + """ + Custom sqlite database backend meant to work with ara.server.wsgi.distributed_sqlite + in order to dynamically load different databases at runtime. + """ + + def get_new_connection(self, conn_params): + if hasattr(local_storage, "db_path") and local_storage.db_path: + conn_params["database"] = local_storage.db_path + return super().get_new_connection(conn_params) diff --git a/ara/server/settings.py b/ara/server/settings.py index 9f6a4aeb..69a4a3a2 100644 --- a/ara/server/settings.py +++ b/ara/server/settings.py @@ -87,6 +87,25 @@ def get_secret_key(): SECRET_KEY = get_secret_key() +# Whether or not to enable the distributed sqlite database backend and WSGI application. +DISTRIBUTED_SQLITE = settings.get("DISTRIBUTED_SQLITE", False) + +# Under which URL should requests be delegated to the distributed sqlite wsgi application +DISTRIBUTED_SQLITE_PREFIX = settings.get("DISTRIBUTED_SQLITE_PREFIX", "ara-api") + +# Root directory under which databases will be found relative to the requested URLs. +# This will restrict where the WSGI application will go to seek out databases. +# For example, the URL "example.org/some/path/ara-api" would translate to +# "/var/www/logs/some/path/ara-api" instead of "/some/path/ara-api". +DISTRIBUTED_SQLITE_ROOT = settings.get("DISTRIBUTED_SQLITE_ROOT", "/var/www/logs") + +if DISTRIBUTED_SQLITE: + WSGI_APPLICATION = "ara.server.wsgi.distributed_sqlite" + DATABASE_ENGINE = settings.get("DATABASE_ENGINE", "ara.server.db.backends.distributed_sqlite") +else: + WSGI_APPLICATION = "ara.server.wsgi.application" + DATABASE_ENGINE = settings.get("DATABASE_ENGINE", "django.db.backends.sqlite3") + # We're not expecting ARA to use multiple concurrent databases. # Make it easier for users to specify the configuration for a single database. DATABASE_ENGINE = settings.get("DATABASE_ENGINE", "django.db.backends.sqlite3") @@ -185,7 +204,6 @@ STATIC_ROOT = settings.get("STATIC_ROOT", os.path.join(BASE_DIR, "www", "static" MEDIA_URL = settings.get("MEDIA_URL", "/media/") MEDIA_ROOT = settings.get("MEDIA_ROOT", os.path.join(BASE_DIR, "www", "media")) -WSGI_APPLICATION = "ara.server.wsgi.application" ROOT_URLCONF = "ara.server.urls" APPEND_SLASH = False @@ -237,6 +255,9 @@ if not os.path.exists(DEFAULT_SETTINGS) and "ARA_SETTINGS" not in os.environ: READ_LOGIN_REQUIRED=READ_LOGIN_REQUIRED, WRITE_LOGIN_REQUIRED=WRITE_LOGIN_REQUIRED, PAGE_SIZE=PAGE_SIZE, + DISTRIBUTED_SQLITE=DISTRIBUTED_SQLITE, + DISTRIBUTED_SQLITE_PREFIX=DISTRIBUTED_SQLITE_PREFIX, + DISTRIBUTED_SQLITE_ROOT=DISTRIBUTED_SQLITE_ROOT, ) with open(DEFAULT_SETTINGS, "w+") as settings_file: comment = f""" diff --git a/ara/server/wsgi.py b/ara/server/wsgi.py index d83c0aec..a983fc8e 100644 --- a/ara/server/wsgi.py +++ b/ara/server/wsgi.py @@ -15,15 +15,91 @@ # You should have received a copy of the GNU General Public License # along with ARA. If not, see . +import logging import os from ara.setup.exceptions import MissingDjangoException try: from django.core.wsgi import get_wsgi_application + from django.core.handlers.wsgi import get_path_info, get_script_name except ImportError as e: raise MissingDjangoException from e os.environ.setdefault("DJANGO_SETTINGS_MODULE", "ara.server.settings") +logger = logging.getLogger(__name__) + +# The default WSGI application application = get_wsgi_application() + + +def handle_405(start_response): + start_response("405 Method Not Allowed", [("content-type", "text/html")]) + return [b"

Method Not Allowed

This endpoint is read only.

"] + + +def handle_404(start_response): + start_response("404 Not Found", [("content-type", "text/html")]) + return [b"

Not Found

The requested resource was not found on this server.

"] + + +def distributed_sqlite(environ, start_response): + """ + Custom WSGI application meant to work with ara.server.db.backends.distributed_sqlite + in order to dynamically load different databases at runtime. + """ + # This endpoint is read only, do not accept write requests. + if environ["REQUEST_METHOD"] not in ["GET", "HEAD", "OPTIONS"]: + handle_405(start_response) + + script_name = get_script_name(environ) + path_info = get_path_info(environ) + + from django.conf import settings + + # The root under which database files are expected + root = settings.DISTRIBUTED_SQLITE_ROOT + # The prefix after which everything should be delegated (ex: /ara-api) + prefix = settings.DISTRIBUTED_SQLITE_PREFIX + + # Static assets should always be served by the regular app + if path_info.startswith(settings.STATIC_URL): + return application(environ, start_response) + + if prefix not in path_info: + logger.warn("Ignoring request: URL does not contain delegated prefix (%s)" % prefix) + return handle_404(start_response) + + # Slice path_info up until after the prefix to obtain the requested directory + i = path_info.find(prefix) + len(prefix) + fs_path = path_info[:i] + + # Make sure we aren't escaping outside the root and the directory exists + db_dir = os.path.abspath(os.path.join(root, fs_path.lstrip("/"))) + if not db_dir.startswith(root): + logger.warn("Ignoring request: path is outside the root (%s)" % db_dir) + return handle_404(start_response) + elif not os.path.exists(db_dir): + logger.warn("Ignoring request: database directory not found (%s)" % db_dir) + return handle_404(start_response) + + # Find the database file and make sure it exists + db_file = os.path.join(db_dir, "ansible.sqlite") + if not os.path.exists(db_file): + logger.warn("Ignoring request: database file not found (%s)" % db_file) + return handle_404(start_response) + + # Tell Django about the new URLs it should be using + environ["SCRIPT_NAME"] = script_name + fs_path + environ["PATH_INFO"] = path_info[len(fs_path) :] # noqa: E203 + + # Store the path of the database in a thread so the distributed_sqlite + # database backend can retrieve it. + from ara.server.db.backends.distributed_sqlite.base import local_storage + + local_storage.db_path = db_file + try: + return application(environ, start_response) + finally: + del local_storage.db_path diff --git a/doc/source/api-configuration.rst b/doc/source/api-configuration.rst index 5d0c43df..7febfb8d 100644 --- a/doc/source/api-configuration.rst +++ b/doc/source/api-configuration.rst @@ -44,6 +44,12 @@ For more details, click on the configuration parameters. +--------------------------------+--------------------------------------------------------+------------------------------------------------------+ | ARA_DEBUG_ | ``False`` | Django's DEBUG_ setting | +--------------------------------+--------------------------------------------------------+------------------------------------------------------+ +| ARA_DISTRIBUTED_SQLITE_ | ``False`` | Whether to enable distributed sqlite backend | ++--------------------------------+--------------------------------------------------------+------------------------------------------------------+ +| ARA_DISTRIBUTED_SQLITE_PREFIX_ | ``ara-api`` | Prefix to delegate to the distributed sqlite backend | ++--------------------------------+--------------------------------------------------------+------------------------------------------------------+ +| ARA_DISTRIBUTED_SQLITE_ROOT_ | ``/var/www/logs`` | Root under which sqlite databases are expected | ++--------------------------------+--------------------------------------------------------+------------------------------------------------------+ | ARA_ENV_ | ``default`` | Environment to load configuration for | +--------------------------------+--------------------------------------------------------+------------------------------------------------------+ | ARA_LOGGING_ | See ARA_LOGGING_ | Logging configuration | @@ -157,8 +163,10 @@ ARA_DATABASE_ENGINE - **Default**: ``django.db.backends.sqlite3`` - **Examples**: + - ``django.db.backends.sqlite3`` - ``django.db.backends.postgresql`` - ``django.db.backends.mysql`` + - ``ara.server.db.backends.distributed_sqlite`` The Django database driver to use. @@ -245,6 +253,57 @@ Whether or not Django's debug mode should be enabled. The Django project recommends turning this off for production use. +ARA_DISTRIBUTED_SQLITE +~~~~~~~~~~~~~~~~~~~~~~ + +- **Environment variable**: ``ARA_DISTRIBUTED_SQLITE`` +- **Configuration file variable**: ``DISTRIBUTED_SQLITE`` +- **Provided by**: ``ara.server.db.backends.distributed_sqlite`` and ``ara.server.wsgi.distributed_sqlite`` +- **Type**: ``bool`` +- **Default**: ``False`` + +Whether or not to enable the distributed sqlite database backend and WSGI application. + +This feature is useful for loading different ARA sqlite databases dynamically +based on request URLs. + +For more information, see: :ref:`distributed sqlite backend `. + +ARA_DISTRIBUTED_SQLITE_PREFIX +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +- **Environment variable**: ``ARA_DISTRIBUTED_SQLITE_PREFIX`` +- **Configuration file variable**: ``DISTRIBUTED_SQLITE_PREFIX`` +- **Provided by**: ``ara.server.db.backends.distributed_sqlite`` and ``ara.server.wsgi.distributed_sqlite`` +- **Type**: ``string`` +- **Default**: ``ara-api`` + +Under which URL should requests be delegated to the distributed sqlite wsgi application. +``ara-api`` would delegate everything under ``.*/ara-api/.*`` + +The path leading to this prefix must contain the ``ansible.sqlite`` database file, for example: +``/var/www/logs/some/path/ara-api/ansible.sqlite``. + +For more information, see: :ref:`distributed sqlite backend `. + +ARA_DISTRIBUTED_SQLITE_ROOT +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +- **Environment variable**: ``ARA_DISTRIBUTED_SQLITE_ROOT`` +- **Configuration file variable**: ``DISTRIBUTED_SQLITE_ROOT`` +- **Provided by**: ``ara.server.db.backends.distributed_sqlite`` and ``ara.server.wsgi.distributed_sqlite`` +- **Type**: ``string`` +- **Default**: ``/var/www/logs`` + +Root directory under which databases will be found relative to the requested URLs. + +This will restrict where the WSGI application will go to seek out databases. + +For example, the URL ``example.org/some/path/ara-api`` would translate to +``/var/www/logs/some/path/ara-api``. + +For more information, see: :ref:`distributed sqlite backend `. + ARA_ENV ~~~~~~~ diff --git a/doc/source/distributed-sqlite-backend.rst b/doc/source/distributed-sqlite-backend.rst new file mode 100644 index 00000000..c7254b45 --- /dev/null +++ b/doc/source/distributed-sqlite-backend.rst @@ -0,0 +1,88 @@ +.. _distributed-sqlite-backend: + +Distributed sqlite database backend +=================================== + +The ARA API server provides an optional backend that dynamically loads sqlite +databases based on the requested URL with the help of a WSGI application +middleware. + +In summary, it maps an URL such as ``http://example.org/some/path/ara-api`` to +a location on the file system like ``/var/www/logs/some/path/ara-api`` and +loads an ``ansible.sqlite`` database from that directory, if it exists. + +.. note:: + This backend is not enabled by default and is designed with a specific range + of use cases in mind. This documentation attempts to explain if this might + be a good fit for you. + +Use case +-------- + +Running at least one Ansible playbook with the ARA Ansible callback plugin +enabled will generate a database at ``~/.ara/server/ansible.sqlite`` by default. + +sqlite, in the context of ARA, is good enough for most use cases: + +- it is portable: everything the API server needs is in a single file that you can upload anywhere +- no network dependency or latency: sqlite is on your filesystem and doesn't rely on a remote database server +- relatively lightweight: Ansible's own integration tests used ~13MB for 415 playbooks, 1935 files, 12456 tasks, 12762 results, 586 hosts (and host facts) + +However, since write concurrency does not scale very well with sqlite, it might +not be a good fit if you plan on having a single API server handle data for +multiple ``ansible-playbook`` commands running at the same time. + +The distributed sqlite database backend and WSGI middleware provide an +alternative to work around this limitation. + +This approach works best if it makes sense to logically split your playbooks +into different databases. One such example is in continuous integration (CI) +where you might have multiple jobs running Ansible playbooks concurrently. + +If each CI job is recording to its own database, you probably no longer have +write concurrency issues and the database can be uploaded in your logs or as an +artifact after the job has been completed. + +The file hierarchy on your log or artifact server might end up looking like +this:: + + /var/www/logs/ + ├── 1 + │   ├── ara-api + │   │   └── ansible.sqlite + │   └── console.txt + ├── 2 + │   ├── logs.tar.gz + │   └── some + │   └── path + │   └── ara-api + │   └── ansible.sqlite + └── 3 + ├── builds.txt + ├── dev + │   └── ara-api + │   └── ansible.sqlite + └── prod + └── ara-api + └── ansible.sqlite + +With the above example file tree, a single instance of the API server with the +distributed sqlite backend enabled would be able to respond to queries at the +following endpoints: + +- http://example.org/1/ara-api +- http://example.org/2/some/path/ara-api +- http://example.org/3/dev/ara-api +- http://example.org/3/prod/ara-api + +Configuration +------------- + +For enabling and configuring the distributed sqlite backend, see: + +- :ref:`ARA_DISTRIBUTED_SQLITE ` +- :ref:`ARA_DISTRIBUTED_SQLITE_PREFIX ` +- :ref:`ARA_DISTRIBUTED_SQLITE_ROOT ` + +When recording data to a sqlite database, the location of the database can be +defined with :ref:`ARA_DATABASE_NAME `. diff --git a/doc/source/index.rst b/doc/source/index.rst index e9bba017..9b613da3 100644 --- a/doc/source/index.rst +++ b/doc/source/index.rst @@ -16,6 +16,7 @@ Table of Contents API: Authentication and security API: Endpoint documentation API: Usage with built-in clients + API: Distributed sqlite backend Setting playbook names and labels Recording arbitrary data in playbooks Contributing to ARA diff --git a/roles/ara_api/defaults/main.yaml b/roles/ara_api/defaults/main.yaml index d02b9d74..0c507c84 100644 --- a/roles/ara_api/defaults/main.yaml +++ b/roles/ara_api/defaults/main.yaml @@ -152,8 +152,17 @@ ara_api_debug: false # Note: If no key is provided, a random one will be generated once and persisted ara_api_secret_key: null +# ARA_DISTRIBUTED_SQLITE - Whether to enable distributed sqlite backend +ara_api_distributed_sqlite: false + +# ARA_DISTRIBUTED_SQLITE_PREFIX - Prefix to delegate to the distributed sqlite backend +ara_api_distributed_sqlite_prefix: ara-api + +# ARA_DISTRIBUTED_SQLITE_ROOT - Root under which sqlite databases are expected +ara_api_distributed_sqlite_root: /var/www/logs + # ARA_DATABASE_ENGINE - Django’s ENGINE database setting -ara_api_database_engine: django.db.backends.sqlite3 +ara_api_database_engine: "{{ ara_api_distributed_sqlite | ternary('ara.server.db.backends.distributed_sqlite', 'django.db.backends.sqlite3') }}" # ARA_DATABASE_NAME - Django’s NAME database setting ara_api_database_name: "{{ ara_api_base_dir }}/ansible.sqlite" diff --git a/roles/ara_api/tasks/config.yaml b/roles/ara_api/tasks/config.yaml index ea3e4f77..39b4be76 100644 --- a/roles/ara_api/tasks/config.yaml +++ b/roles/ara_api/tasks/config.yaml @@ -59,6 +59,15 @@ ara_api_secret_key: "{{ generated_key.stdout }}" no_log: yes +- name: Validate distributed sqlite configuration + assert: + that: + - "ara_api_database_engine == 'ara.server.db.backends.distributed_sqlite'" + msg: | + The database engine should be 'ara.server.db.backends.distributed_sqlite' + when 'ara_api_distributed_sqlite' is true. + when: ara_api_distributed_sqlite + # Put configuration in a format we can write to a file - name: Reconcile configuration vars: @@ -75,6 +84,9 @@ DATABASE_PORT: "{{ ara_api_database_port }}" DATABASE_CONN_MAX_AGE: "{{ ara_api_database_conn_max_age }}" DEBUG: "{{ ara_api_debug }}" + DISTRIBUTED_SQLITE: "{{ ara_api_distributed_sqlite }}" + DISTRIBUTED_SQLITE_PREFIX: "{{ ara_api_distributed_sqlite_prefix }}" + DISTRIBUTED_SQLITE_ROOT: "{{ ara_api_distributed_sqlite_root }}" LOGGING: "{{ ara_api_logging }}" LOG_LEVEL: "{{ ara_api_log_level }}" SECRET_KEY: "{{ ara_api_secret_key }}" diff --git a/roles/ara_api/tasks/database_engine/ara.server.db.backends.distributed_sqlite.yaml b/roles/ara_api/tasks/database_engine/ara.server.db.backends.distributed_sqlite.yaml new file mode 100644 index 00000000..1394fee5 --- /dev/null +++ b/roles/ara_api/tasks/database_engine/ara.server.db.backends.distributed_sqlite.yaml @@ -0,0 +1,22 @@ +--- +# Copyright (c) 2019 Red Hat, Inc. +# +# This file is part of ARA Records Ansible. +# +# ARA Records Ansible is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# ARA Records Ansible is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with ARA Records Ansible. If not, see . + +# This ensures the WSGI application servers are set up to use the right endpoint +- name: Set WSGI application to distributed_sqlite + set_fact: + _ara_api_wsgi_application: "ara.server.wsgi:distributed_sqlite" diff --git a/roles/ara_api/templates/ara-api.service.j2 b/roles/ara_api/templates/ara-api.service.j2 index ede8f3a1..edb8e73e 100644 --- a/roles/ara_api/templates/ara-api.service.j2 +++ b/roles/ara_api/templates/ara-api.service.j2 @@ -8,9 +8,9 @@ RuntimeDirectory=ara-api WorkingDirectory={{ ara_api_root_dir }} Environment=ARA_SETTINGS={{ ara_api_settings }} {% if ara_api_venv %} -ExecStart={{ ara_api_venv_path }}/bin/gunicorn --workers=4 --bind {{ ara_api_wsgi_bind }} ara.server.wsgi +ExecStart={{ ara_api_venv_path }}/bin/gunicorn --workers=4 --bind {{ ara_api_wsgi_bind }} {{ _ara_api_wsgi_application | default('ara.server.wsgi') }} {% else %} -ExecStart=gunicorn --workers=4 --bind {{ ara_api_wsgi_bind }} ara.server.wsgi +ExecStart=gunicorn --workers=4 --bind {{ ara_api_wsgi_bind }} {{ _ara_api_wsgi_application | default('ara.server.wsgi') }} {% endif %} ExecReload=/bin/kill -s HUP $MAINPID ExecStop=/bin/kill -s TERM $MAINPID diff --git a/tests/test_tasks.yaml b/tests/test_tasks.yaml index 3a460945..c8508f0d 100644 --- a/tests/test_tasks.yaml +++ b/tests/test_tasks.yaml @@ -60,6 +60,8 @@ ANSIBLE_CALLBACK_PLUGINS: "{{ ara_setup_plugins.stdout }}/callback" ANSIBLE_ACTION_PLUGINS: "{{ ara_setup_plugins.stdout }}/action" ARA_SETTINGS: "{{ ara_api_settings }}" + ARA_API_CLIENT: "{{ ara_api_client | default('offline') }}" + ARA_API_SERVER: "{{ ara_api_server | default('http://127.0.0.1:8000') }}" vars: _ansible_playbook: "{{ ara_api_venv_path }}/bin/ansible-playbook -vvv" _test_root: "{{ ara_api_source_checkout }}/tests/integration" diff --git a/tests/with_distributed_sqlite.yaml b/tests/with_distributed_sqlite.yaml new file mode 100644 index 00000000..537d5d35 --- /dev/null +++ b/tests/with_distributed_sqlite.yaml @@ -0,0 +1,80 @@ +# Copyright (c) 2019 Red Hat, Inc. +# +# This file is part of ARA Records Ansible. +# +# ARA is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# ARA is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with ARA. If not, see . + +- name: Test the ARA API with the distributed sqlite backend + hosts: all + gather_facts: yes + vars: + ara_tests_cleanup: true + ara_api_root_dir: "{{ ansible_user_dir }}/.ara-tests" + ara_api_distributed_sqlite_root: "{{ ansible_user_dir }}/.ara-tests" + ara_api_wsgi_server: gunicorn + ara_api_secret_key: testing + ara_api_debug: true + ara_api_log_level: DEBUG + ara_api_server: "http://127.0.0.1:8000" + ara_api_client: http + _test_directories: + - "tests/some/path/ara-api" + - "tests/dev/ara-api" + - "tests/prod/ara-api" + tasks: + # Generate ourselves a fresh database to run tests with + - name: Set up the API with the default sqlite backend + include_role: + name: ara_api + public: yes + + # These are tasks rather than a standalone playbook to give us an easy + # access to all the variables within the same play. + - include_tasks: test_tasks.yaml + + - name: Enable the distributed sqlite backend + vars: + ara_api_distributed_sqlite: true + include_role: + name: ara_api + public: yes + + - name: Ensure there are no pending handlers + meta: flush_handlers + + - name: Create test directories + file: + path: "{{ ara_api_distributed_sqlite_root }}/{{ item }}" + state: directory + recurse: yes + loop: "{{ _test_directories }}" + + - name: Copy the database to the test directories + copy: + src: "{{ ara_api_database_name }}" + dest: "{{ ara_api_distributed_sqlite_root }}/{{ item }}/ansible.sqlite" + remote_src: true + loop: "{{ _test_directories }}" + + - name: Test that the API works + uri: + url: "http://127.0.0.1:8000/{{ item }}" + return_content: yes + register: api_test + loop: "{{ _test_directories }}" + + - name: Print API responses for debug purposes + debug: + msg: "{{ item['json'] | to_nice_json }}" + loop: "{{ api_test.results }}"