diff --git a/.zuul.d/zuul.yaml b/.zuul.d/zuul.yaml index d17ecf2..6b3061a 100644 --- a/.zuul.d/zuul.yaml +++ b/.zuul.d/zuul.yaml @@ -79,6 +79,37 @@ parent: ara-server-role-integration-base nodeset: fedora-latest +- job: + name: ara-web-role-integration-base + parent: base + vars: + ara_web_source: "/home/zuul/src/git.openstack.org/openstack/ara-web" + ara_web_api_server: "https://api.demo.recordsansible.org" + files: + - playbooks/* + - roles/ara_frontend_nginx/* + - roles/ara_web/* + - src/.* + - public/.* + - package.json + - package-lock.json + - .zuul.d/* + required-projects: + - openstack/ara-web + - openstack/ara-infra + run: playbooks/ara-web.yaml + post-run: tests/role-ara-web-post.yaml + +- job: + name: ara-web-role-integration-ubuntu + parent: ara-web-role-integration-base + nodeset: ubuntu-bionic + +- job: + name: ara-web-role-integration-fedora + parent: ara-web-role-integration-base + nodeset: fedora-latest + - project: check: jobs: @@ -89,6 +120,8 @@ - ara-integration-ubuntu-2.6 - ara-server-role-integration-ubuntu - ara-server-role-integration-fedora + - ara-web-role-integration-ubuntu + - ara-web-role-integration-fedora gate: jobs: - ara-infra-website @@ -96,3 +129,5 @@ - ara-integration-ubuntu-2.6 - ara-server-role-integration-ubuntu - ara-server-role-integration-fedora + - ara-web-role-integration-ubuntu + - ara-web-role-integration-fedora diff --git a/playbooks/ara-web.yaml b/playbooks/ara-web.yaml new file mode 100644 index 0000000..cd1c9af --- /dev/null +++ b/playbooks/ara-web.yaml @@ -0,0 +1,7 @@ +- name: Deploy ara-web + hosts: all + gather_facts: yes + vars: + ansible_python_interpreter: /usr/bin/python3 + roles: + - ara_web diff --git a/roles/ara_frontend_nginx/tasks/main.yaml b/roles/ara_frontend_nginx/tasks/main.yaml index ddf4f43..f57075f 100644 --- a/roles/ara_frontend_nginx/tasks/main.yaml +++ b/roles/ara_frontend_nginx/tasks/main.yaml @@ -57,6 +57,26 @@ notify: - restart nginx + - when: + - ara_web_frontend_fqdn is defined + - ara_web_static_dir is defined + block: + - name: Set up the ara-web nginx vhost + template: + src: "{{ ara_nginx_web_vhost | default('ara-web.conf.j2') }}" + dest: "{{ ara_nginx_config_path }}/ara-web.conf" + notify: + - restart nginx + + - name: Enable the nginx configuration on Debian-like systems + file: + src: "{{ ara_nginx_config_path }}/ara-web.conf" + dest: /etc/nginx/sites-enabled/ara-web.conf + state: link + when: ansible_os_family == 'Debian' + notify: + - restart nginx + - name: Enable and start nginx service: name: nginx diff --git a/roles/ara_frontend_nginx/templates/ara-web.conf.j2 b/roles/ara_frontend_nginx/templates/ara-web.conf.j2 new file mode 100644 index 0000000..89a6272 --- /dev/null +++ b/roles/ara_frontend_nginx/templates/ara-web.conf.j2 @@ -0,0 +1,33 @@ +{% if ara_web_dev_server %} +upstream ara_web { + # fail_timeout=0 means we always retry an upstream even if it failed + # to return a good HTTP response + server {{ ara_web_dev_server_bind_address }}:{{ ara_web_dev_server_bind_port }} fail_timeout=0; +} +{% endif %} + +server { + listen 80; + keepalive_timeout 5; + server_name {{ ara_web_frontend_fqdn }}; + root {{ ara_web_static_dir }}; + + access_log /var/log/nginx/{{ ara_web_frontend_fqdn }}_access.log; + error_log /var/log/nginx/{{ ara_web_frontend_fqdn }}_error.log; + + {% if ara_web_dev_server %} + location / { + # checks if the file exists, if not found proxy to app + try_files $uri @proxy_to_app; + } + + location @proxy_to_app { + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header Host $http_host; + + proxy_redirect off; + proxy_pass http://ara_web; + } + {% endif %} +} diff --git a/roles/ara_web/README.rst b/roles/ara_web/README.rst new file mode 100644 index 0000000..c199741 --- /dev/null +++ b/roles/ara_web/README.rst @@ -0,0 +1,63 @@ +ansible-role-ara-web +==================== + +This Ansible role provides a framework for installing one or many instances of +`ara-web `_ in a variety of +opinionated deployment topologies. + +It is currently tested and supported against Ubuntu 18.04 and Fedora 29. + +Role Variables +============== + +See ``defaults/main.yaml``. + +TL;DR +===== + +This is what the role does by default out of the box: + +- Retrieves ara-web from source +- Installs ara-web dependencies with npm +- Configures an ara-server API endpoint in ara-web's ``public/config.json`` file +- Sets up a systemd unit file for running ara-web with the embedded development server + +About deployment topologies +=========================== + +This Ansible role is designed to support different opinionated topologies that +can be selected with role variables. + +For example, the following role variables are used to provide the topology from +the ``TL;DR`` above: + +- ``ara_web_install_method: source`` +- ``ara_web_dev_server: true`` +- ``ara_web_frontend_server: nginx`` + +The intent is that as the role gains support for other install methods or +frontend servers, it will be possible to mix and match according to preference +or requirements. + +When the development server is not in use, the application will be statically +built instead and served by the frontend server entirely. + +Copyright +========= + +:: + + Copyright (c) 2019 Red Hat, Inc. + + 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 . diff --git a/roles/ara_web/defaults/main.yaml b/roles/ara_web/defaults/main.yaml new file mode 100644 index 0000000..2185533 --- /dev/null +++ b/roles/ara_web/defaults/main.yaml @@ -0,0 +1,68 @@ +--- +# 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 . + +# How ara-web will be installed +# - source (default): installs from a local or remote git repository specified by ara_web_source +# - npm (planned): installs from npm +ara_web_install_method: source + +# When installing from source, the location of the remote or local git repository +ara_web_source: "https://git.openstack.org/openstack/ara-web" + +# Location where ara-web will be checked out +ara_web_source_checkout: "{{ ansible_user_dir }}/.ara/git/ara-web" + +# Location where node_modules will be installed +ara_web_node_modules_dir: "{{ ansible_user_dir }}/.ara/lib/ara-web" + +# Version of ara-web to install +# This can be a git ref (tag, branch, commit) when installed from source +# When using "latest" as the source version, HEAD will be used +ara_web_install_version: latest + +# Whether to force an update of the git repository and a reinstallation of the +# node dependencies. +ara_web_force_update: true + +# Whether to use the embedded react web server or not +# Setting this to false means ara-web will be statically built instead +ara_web_dev_server: true + +# When the development server is enabled, the address it will be listening on +ara_web_dev_server_bind_address: 127.0.0.1 + +# When the development server is enabled, the port it will be listening on +ara_web_dev_server_bind_port: 3000 + +# When using static builds without the dev server, path to ara-web static assets +ara_web_static_dir: "{{ ansible_user_dir }}/.ara/www/ara-web" + +# Version of nodesource nodejs repositories to install +ara_web_nodejs_version: 10 + +# ara-server API endpoint to use +ara_web_api_server: "http://127.0.0.1:8000" + +# The frontend server for serving ara-web +# - null (default): none, users are expected to use the development server directly or deploy their own web server +# - nginx: when performance of the development server is an issue +# - apache (planned) +ara_web_frontend_server: null + +# When using a web server, the hostname to listen on +ara_web_frontend_fqdn: "127.0.0.1" diff --git a/roles/ara_web/handlers/main.yaml b/roles/ara_web/handlers/main.yaml new file mode 100644 index 0000000..9706317 --- /dev/null +++ b/roles/ara_web/handlers/main.yaml @@ -0,0 +1,31 @@ +--- +# 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 . + +# Is there a better way ? Static files are not created with the httpd context +- name: restore selinux context for static files + become: true + command: "restorecon -Rv {{ ara_web_static_dir }}" + when: ansible_os_family == "RedHat" + +- name: restart ara-web + become: true + service: + name: ara-web + state: restarted + when: + - ara_web_service_enabled is not changed diff --git a/roles/ara_web/meta/main.yaml b/roles/ara_web/meta/main.yaml new file mode 100644 index 0000000..e94836d --- /dev/null +++ b/roles/ara_web/meta/main.yaml @@ -0,0 +1,36 @@ +--- +# 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 . + +galaxy_info: + author: David Moreau-Simard + description: Role to set up ara-web + license: GPLv3 + min_ansible_version: 2.7 + platforms: + - name: Fedora + versions: + - 29 + - name: Ubuntu + versions: + - bionic + galaxy_tags: + - ansible + - ara + - ara-web + +dependencies: [] diff --git a/roles/ara_web/tasks/install/source.yaml b/roles/ara_web/tasks/install/source.yaml new file mode 100644 index 0000000..5c7a476 --- /dev/null +++ b/roles/ara_web/tasks/install/source.yaml @@ -0,0 +1,109 @@ +--- +# 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 . + +- name: Ensure libselinux-python is installed for Red Hat derivatives + become: yes + package: + name: libselinux-python + state: present + when: ansible_os_family == "RedHat" + +- name: Ensure git is installed + become: yes + package: + name: git + state: present + +# TODO: node_modules and public/config.json are local to the git repository so this is not idempotent +- name: Prepare git repository for ara-web + git: + repo: "{{ ara_web_source }}" + dest: "{{ ara_web_source_checkout }}" + version: "{{ (ara_web_install_version == 'latest') | ternary('HEAD', ara_web_install_version) }}" + force: "{{ ara_web_force_update }}" + +- name: Install ara-web npm dependencies + npm: + path: "{{ ara_web_source_checkout }}" + global: no + production: yes + state: "{{ ara_web_force_update | ternary('latest', 'present') }}" + notify: + - restart ara-web + +- name: Configure ara-server API endpoint for ara-web + vars: + web_config: + apiURL: "{{ ara_web_api_server }}" + copy: + content: "{{ web_config | to_nice_json(indent=2) }}" + dest: "{{ ara_web_source_checkout }}/public/config.json" + mode: 0644 + notify: + - restart ara-web + +- when: ara_web_dev_server | bool + become: yes + block: + - name: Set up systemd unit file for ara-web + template: + src: ara-web.service.j2 + dest: /etc/systemd/system/ara-web.service + owner: root + group: root + mode: 0644 + notify: + - restart ara-web + + - name: Enable and start ara-web + service: + name: ara-web + state: started + enabled: yes + daemon_reload: yes + register: ara_web_service_enabled + +- when: not ara_web_dev_server | bool + block: + - name: Stop and disable ara-web + become: yes + service: + name: ara-web + state: stopped + enabled: no + + - name: Ensure systemd unit file is not configured + become: yes + file: + path: /etc/systemd/system/ara-web.service + state: absent + + - name: Run a production build of ara-web + command: npm run build + args: + chdir: "{{ ara_web_source_checkout }}" + creates: "{{ ara_web_source_checkout }}/build" + + - name: Synchronize build to web directory + become: yes + command: | + rsync -rlog --delete-delay \ + --chown {{ ansible_user_uid }}:{{ ansible_user_gid }} \ + {{ ara_web_source_checkout }}/build/ {{ ara_web_static_dir }} + notify: + - restore selinux context for static files diff --git a/roles/ara_web/tasks/main.yaml b/roles/ara_web/tasks/main.yaml new file mode 100644 index 0000000..c358d4c --- /dev/null +++ b/roles/ara_web/tasks/main.yaml @@ -0,0 +1,28 @@ +--- +# 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 . + +- name: Install nodejs + include_tasks: nodejs.yaml + +- name: Include ara-web installation + include_tasks: install/{{ ara_web_install_method }}.yaml + +- name: Include frontend server installation + include_role: + name: "ara_frontend_{{ ara_web_frontend_server }}" + when: ara_web_frontend_server is not none diff --git a/roles/ara_web/tasks/nodejs.yaml b/roles/ara_web/tasks/nodejs.yaml new file mode 100644 index 0000000..1df9180 --- /dev/null +++ b/roles/ara_web/tasks/nodejs.yaml @@ -0,0 +1,53 @@ +--- +# 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 . + +- when: ansible_os_family == "Debian" + become: yes + block: + - name: Install apt-transport-https + package: + name: apt-transport-https + state: present + + - name: Install nodesource repository key + apt_key: + url: "https://deb.nodesource.com/gpgkey/nodesource.gpg.key" + + - name: Install nodesource apt source repository + apt_repository: + repo: "deb-src https://deb.nodesource.com/node_{{ ara_web_nodejs_version }}.x {{ ansible_distribution_release }} main" + state: present + + - name: Install nodesource apt repository + apt_repository: + repo: "deb https://deb.nodesource.com/node_{{ ara_web_nodejs_version }}.x {{ ansible_distribution_release }} main" + state: present + update_cache: yes + +- name: Install Nodesource yum repository + become: yes + package: + name: "https://rpm.nodesource.com/pub_{{ ara_web_nodejs_version }}.x/fc/{{ ansible_distribution_major_version }}/{{ ansible_architecture }}/nodesource-release-fc{{ ansible_distribution_major_version }}-1.noarch.rpm" + state: present + when: ansible_os_family == "RedHat" + +- name: Install nodejs + become: yes + package: + name: nodejs + state: present diff --git a/roles/ara_web/templates/ara-web.service.j2 b/roles/ara_web/templates/ara-web.service.j2 new file mode 100644 index 0000000..95685e3 --- /dev/null +++ b/roles/ara_web/templates/ara-web.service.j2 @@ -0,0 +1,16 @@ +[Unit] +Description=ARA Records Ansible web client +After=network.target + +[Service] +PIDFile=/run/ara-web/pid +User={{ ansible_user_id }} +RuntimeDirectory=ara-web +WorkingDirectory={{ ara_web_source_checkout }} +ExecStart=/usr/bin/npm start --host {{ ara_web_dev_server_bind_address }} --port {{ ara_web_dev_server_bind_port }} +ExecReload=/bin/kill -s HUP $MAINPID +ExecStop=/bin/kill -s TERM $MAINPID +PrivateTmp=true + +[Install] +WantedBy=multi-user.target diff --git a/tests/role-ara-web-post.yaml b/tests/role-ara-web-post.yaml new file mode 100644 index 0000000..2faac0a --- /dev/null +++ b/tests/role-ara-web-post.yaml @@ -0,0 +1,48 @@ +- name: Deploy ara-web + hosts: all + gather_facts: yes + vars: + ara_web_source_checkout: "{{ ansible_user_dir }}/.ara/git/ara-web" + tasks: + # Before building the application, we need to set the homepage argument + # from package.json to use the URL where logs will be uploaded. + - name: Resolve Zuul log path + include_role: + name: set-zuul-log-path-fact + + - name: Read package.json + command: "cat {{ ara_web_source_checkout }}/package.json" + register: package_json + + - name: Set homepage parameter + vars: + build_url: "http://logs.openstack.org/{{ zuul_log_path }}/npm/html/" + set_fact: + package_json: "{{ package_json.stdout | from_json | combine({'homepage': build_url}) }}" + + - name: Write package.json + copy: + content: "{{ package_json | to_nice_json }}" + dest: "{{ ara_web_source_checkout }}/package.json" + + - name: Run a production build of ara-web + command: npm run build + args: + chdir: "{{ ara_web_source_checkout }}" + creates: "{{ ara_web_source_checkout }}/build" + + - name: Create log directory + file: + path: "{{ ansible_user_dir }}/workspace/logs" + state: directory + recurse: yes + + - name: Recover production build of ara-web + command: cp -rp {{ ara_web_source_checkout }}/build {{ ansible_user_dir }}/workspace/logs/ + + - name: Upload build to log server + synchronize: + src: "{{ ansible_user_dir }}/workspace/logs" + dest: "{{ zuul.executor.log_root }}" + mode: pull + verify_host: true