# Running ARA API server container images

The ARA API server is a good candidate for being served out of a container as the configuration and state can be kept in persistent files and databases.

The project maintains different scripts that are used to build and push simple container images to DockerHub.

The scripts are designed to yield images that are opinionated and “batteries-included” for the sake of simplicity. They install the necessary packages for connecting to MySQL and PostgreSQL databases and set up gunicorn as the application server.

You are encouraged to use these scripts as a base example that you can build, tweak and improve the container image according to your specific needs and preferences.

For example, precious megabytes can be saved by installing only the things you need and you can change the application server as well as it’s configuration.

## Building an image with buildah

You will need to install buildah.

The different scripts to build container images are available in the git repository:

- fedora-distribution.sh: Builds an image from Fedora 35 distribution packages

- fedora-pypi.sh: Builds an image from PyPi packages on Fedora 35

- fedora-source.sh: Builds an image from git source on Fedora 35

- centos-pypi.sh: Builds an image from PyPi packages on CentOS 8 Stream

The scripts have no arguments other than the ability to specify an optional name and tag:

    $ git clone https://github.com/ansible-community/ara
    $ cd ara/contrib/container-images
    $ ./fedora-source.sh ara-api:latest
    # [...]
    Getting image source signatures
    Copying blob 59bbb69efd73 skipped: already exists
    Copying blob ccc3e7c17eae done
    Copying config fb679fc301 done
    Writing manifest to image destination
    Storing signatures
    fb679fc301dde7007b4d219f1d30060b3b4b0d5883b030ee7058d7e9f5969fbe

The image will be available for use once the script has finished running:

    $ buildah images
    REPOSITORY          TAG      IMAGE ID       CREATED          SIZE
    localhost/ara-api   latest   fb679fc301dd   25 minutes ago   451 MB

## Running an image with podman

You will need to install podman.

Once an image has been built with the scripts above, you can validate that it is available to podman:

    $ podman images
    REPOSITORY          TAG      IMAGE ID       CREATED          SIZE
    localhost/ara-api   latest   fb679fc301dd   31 minutes ago   451 MB

First, create a directory where settings, logs and sqlite databases will persist inside a volume and then start the container:

    $ mkdir -p ~/.ara/server
    $ podman run --name api-server --detach --tty \
         --volume ~/.ara/server:/opt/ara:z -p 8000:8000 \
         localhost/ara-api
    bc4b7630c265bdac161f2e08116f3f45c2db519fb757ddf865bb0f212780fa8d

You can validate if the container is running properly with podman:

    $ podman ps
    CONTAINER ID  IMAGE                     COMMAND               CREATED         STATUS             PORTS                   NAMES
    bc4b7630c265  localhost/ara-api:latest  /usr/bin/gunicorn...  12 seconds ago  Up 11 seconds ago  0.0.0.0:8000->8000/tcp  api-server

    $ podman logs api-server
    [ara] No setting found for SECRET_KEY. Generating a random key...
    [ara] Writing default settings to /opt/ara/settings.yaml
    [ara] Using settings file: /opt/ara/settings.yaml
    Operations to perform:
    Apply all migrations: admin, api, auth, contenttypes, db, sessions
    Running migrations:
    Applying contenttypes.0001_initial... OK
    Applying auth.0001_initial... OK
    Applying admin.0001_initial... OK
    Applying admin.0002_logentry_remove_auto_add... OK
    Applying admin.0003_logentry_add_action_flag_choices... OK
    Applying api.0001_initial... OK
    Applying api.0002_remove_host_alias... OK
    Applying api.0003_add_missing_result_properties... OK
    Applying api.0004_duration_in_database... OK
    Applying api.0005_unique_label_names... OK
    Applying contenttypes.0002_remove_content_type_name... OK
    Applying auth.0002_alter_permission_name_max_length... OK
    Applying auth.0003_alter_user_email_max_length... OK
    Applying auth.0004_alter_user_username_opts... OK
    Applying auth.0005_alter_user_last_login_null... OK
    Applying auth.0006_require_contenttypes_0002... OK
    Applying auth.0007_alter_validators_add_error_messages... OK
    Applying auth.0008_alter_user_username_max_length... OK
    Applying auth.0009_alter_user_last_name_max_length... OK
    Applying auth.0010_alter_group_name_max_length... OK
    Applying auth.0011_update_proxy_permissions... OK
    Applying db.0001_initial... OK
    Applying sessions.0001_initial... OK
    [2020-05-05 17:29:22 +0000] [1] [INFO] Starting gunicorn 20.0.4
    [2020-05-05 17:29:22 +0000] [1] [INFO] Listening at: http://0.0.0.0:8000 (1)
    [2020-05-05 17:29:22 +0000] [1] [INFO] Using worker: sync
    [2020-05-05 17:29:22 +0000] [5] [INFO] Booting worker with pid: 5
    [2020-05-05 17:29:22 +0000] [6] [INFO] Booting worker with pid: 6
    [2020-05-05 17:29:23 +0000] [7] [INFO] Booting worker with pid: 7
    [2020-05-05 17:29:23 +0000] [8] [INFO] Booting worker with pid: 8

At this point, the API server will be running but it’ll be empty.

Data must be sent to it by running an Ansible playbook with the ARA callback installed and configured to use this API server.

## Sending data to the API server

Here’s an example of how it works:

    # Create and source a python3 virtual environment
    python3 -m venv ~/.ara/virtualenv
    source ~/.ara/virtualenv/bin/activate

    # Install Ansible and ARA
    pip3 install ansible ara

    # Configure Ansible to know where ARA's callback plugin is located
    export ANSIBLE_CALLBACK_PLUGINS=$(python3 -m ara.setup.callback_plugins)

    # Set up the ARA callback to know where the API server is
    export ARA_API_CLIENT=http
    export ARA_API_SERVER="http://127.0.0.1:8000"

    # Run any of your Ansible playbooks as you normally would
    ansible-playbook playbook.yml

As each task from the playbook starts and completes, their data will be available on the API server in real time as you refresh your queries.

## Common operations

### Modifying ARA’s API server settings

Settings for the API server will be found in `~/.ara/server/settings.yaml` (or `/opt/ara/settings.yaml` inside the container) and modifications are effective after a container restart:

    podman restart api-server

See the documentation for the full list of available options.

### Running outside of localhost

To run an API server that can be queried from other hosts, edit `~/.ara/server/settings.yaml` and add the desired hostname (or IP) in ALLOWED_HOSTS.

### Connecting to mysql, mariadb or postgresql backends

The ARA API server is a good candidate for living in a container because the state can be stored on remote database servers.

To connect to database backends other than the sqlite default, edit `~/.ara/server/settings.yaml` and look for the following settings:

- DATABASE_ENGINE

- DATABASE_NAME

- DATABASE_USER

- DATABASE_PASSWORD

- DATABASE_HOST

- DATABASE_PORT

- DATABASE_CONN_MAX_AGE

### Running SQL migrations

The container image will automatically take care of running SQL migrations before starting.

However, if you need to run them manually, either for a new database or after an upgrade, the command `ara-manage migrate` can be run from inside the container:

    $ podman exec -it api-server ara-manage migrate
    [ara] Using settings file: /opt/ara/settings.yaml
    Operations to perform:
    Apply all migrations: admin, api, auth, contenttypes, db, sessions
    Running migrations:
    Applying contenttypes.0001_initial... OK
    Applying auth.0001_initial... OK
    Applying admin.0001_initial... OK
    Applying admin.0002_logentry_remove_auto_add... OK
    Applying admin.0003_logentry_add_action_flag_choices... OK
    Applying api.0001_initial... OK
    Applying api.0002_remove_host_alias... OK
    Applying api.0003_add_missing_result_properties... OK
    Applying api.0004_duration_in_database... OK
    Applying api.0005_unique_label_names... OK
    Applying contenttypes.0002_remove_content_type_name... OK
    Applying auth.0002_alter_permission_name_max_length... OK
    Applying auth.0003_alter_user_email_max_length... OK
    Applying auth.0004_alter_user_username_opts... OK
    Applying auth.0005_alter_user_last_login_null... OK
    Applying auth.0006_require_contenttypes_0002... OK
    Applying auth.0007_alter_validators_add_error_messages... OK
    Applying auth.0008_alter_user_username_max_length... OK
    Applying auth.0009_alter_user_last_name_max_length... OK
    Applying auth.0010_alter_group_name_max_length... OK
    Applying auth.0011_update_proxy_permissions... OK
    Applying db.0001_initial... OK
    Applying sessions.0001_initial... OK