diff --git a/.github/workflows/molecule.yml b/.github/workflows/molecule.yml
index a422025a..8642c0f2 100644
--- a/.github/workflows/molecule.yml
+++ b/.github/workflows/molecule.yml
@@ -8,11 +8,11 @@ on:
jobs:
list-scenarios:
- runs-on: ubuntu-20.04
+ runs-on: ubuntu-22.04
outputs:
matrix: ${{ steps.listscenarios.outputs.scenarios }}
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
- id: listscenarios
uses: ome/action-ansible-molecule-list-scenarios@main
@@ -20,22 +20,21 @@ jobs:
name: Test
needs:
- list-scenarios
- runs-on: ubuntu-20.04
+ runs-on: ubuntu-22.04
strategy:
# Keep running so we can see if other tests pass
fail-fast: false
matrix:
scenario: ${{fromJson(needs.list-scenarios.outputs.matrix)}}
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
- uses: actions/setup-python@v4
with:
- python-version: '3.8'
+ python-version: '3.9'
- name: Install Ansible & Molecule
run: |
pip install "ansible<8" "ansible-lint<6.13" flake8
pip install "molecule<5" "ansible-compat<4"
pip install molecule-plugins[docker] pytest-testinfra
- pip3.8 install jmespath
- name: Run molecule
run: molecule test -s "${{ matrix.scenario }}"
diff --git a/molecule/docker-prod/Dockerfile.j2 b/molecule/docker-prod/Dockerfile.j2
deleted file mode 100644
index 7e2d467d..00000000
--- a/molecule/docker-prod/Dockerfile.j2
+++ /dev/null
@@ -1,22 +0,0 @@
-# Molecule managed
-
-{% if item.registry is defined %}
-FROM {{ item.registry.url }}/{{ item.image }}
-{% else %}
-FROM {{ item.image }}
-{% endif %}
-
-{% if item.env is defined %}
-{% for var, value in item.env.items() %}
-{% if value %}
-ENV {{ var }} {{ value }}
-{% endif %}
-{% endfor %}
-{% endif %}
-
-RUN if [ $(command -v apt-get) ]; then apt-get update && apt-get install -y python sudo python-jmespath bash ca-certificates iproute2 && apt-get clean; \
- elif [ $(command -v dnf) ]; then dnf makecache && dnf --assumeyes install python sudo python-devel python*-dnf bash iproute && dnf clean all; \
- elif [ $(command -v yum) ]; then yum makecache fast && yum install -y python sudo python36-jmespath python38-jmespath yum-plugin-ovl bash iproute ca-certificates && sed -i 's/plugins=0/plugins=1/g' /etc/yum.conf && yum clean all; \
- elif [ $(command -v zypper) ]; then zypper refresh && zypper install -y python sudo bash python-xml iproute2 && zypper clean -a; \
- elif [ $(command -v apk) ]; then apk update && apk add --no-cache python sudo bash ca-certificates; \
- elif [ $(command -v xbps-install) ]; then xbps-install -Syu && xbps-install -y python sudo bash ca-certificates iproute2 && xbps-remove -O; fi
diff --git a/molecule/docker-prod/converge.yml b/molecule/docker-prod/converge.yml
deleted file mode 100644
index 775d3456..00000000
--- a/molecule/docker-prod/converge.yml
+++ /dev/null
@@ -1,19 +0,0 @@
----
-- name: Converge
- hosts: ome-dockr-prod1.openmicroscopy.org
- tasks:
- - name: Workaround to get host IP inside docker
- shell: hostname -I | cut -d' ' -f1
- register: hostname_ip
- check_mode: false
- changed_when: false
- tags:
- # Ignore [306] Shells that use pipes should set the pipefail option
- - skip_ansible_lint
-
- - name: Set address of postgres for redmine
- set_fact:
- redmine_tracker_db_host: "{{ hostname_ip.stdout }}"
-
-- name: Import-playbook
- import_playbook: ../../omedev/docker-prod-apps.yml
diff --git a/molecule/docker-prod/molecule.yml b/molecule/docker-prod/molecule.yml
deleted file mode 100644
index f43e3aa3..00000000
--- a/molecule/docker-prod/molecule.yml
+++ /dev/null
@@ -1,56 +0,0 @@
----
-dependency:
- name: galaxy
- options:
- role-file: requirements.yml
-driver:
- name: docker
-lint: |
- yamllint .
- ansible-lint
- flake8
-platforms:
- - name: ome-dockr-prod1.openmicroscopy.org
- image: centos/systemd:latest
- command: /sbin/init
- privileged: true
- groups:
- - docker-hosts
- - omedev-docker
- published_ports:
- - "0.0.0.0:9090:9090/tcp"
-provisioner:
- name: ansible
- playbooks:
- prepare: prepare.yml
- converge: converge.yml
- inventory:
- host_vars:
- ome-dockr-prod1.openmicroscopy.org:
- ome_monitored_node_exporter_hosts:
- - node.example.org
- ome_monitored_postgres_hosts:
- - pg.example.org
- ome_monitored_omero_server_hosts:
- - omeroserver.example.org
- ome_monitored_omero_web_hosts:
- - omeroweb.example.org
- prometheus_docker_data_volume: /srv/prometheus
- nfs_minio_data_volume: /srv/minio
- redmine_tracker_docker_data_volume: /srv/redmine-files
-
- group_vars:
- # all:
- # molecule_test: true
- docker-hosts:
- # This should allow docker-in-docker to work
- docker_storage_driver: vfs
- # Latest version 17.12.1.ce-1.el7.centos has a bug that prevents
- # testing on travis: https://github.com/docker/for-linux/issues/219
- docker_version: 17.09.1.ce-1.el7.centos
- lint:
- name: ansible-lint
-scenario:
- name: docker-prod
-verifier:
- name: testinfra
diff --git a/molecule/docker-prod/prepare.yml b/molecule/docker-prod/prepare.yml
deleted file mode 100644
index 7bb8ba41..00000000
--- a/molecule/docker-prod/prepare.yml
+++ /dev/null
@@ -1,22 +0,0 @@
----
-- name: Prepare
- hosts: ome-dockr-prod1.openmicroscopy.org
- roles:
- - role: ome.postgresql
- postgresql_version: "13"
- postgresql_server_auth:
- - database: redmine
- user: redmine
- address: 0.0.0.0/0
- postgresql_databases:
- - name: redmine
- owner: redmine
- postgresql_users:
- - user: redmine
- password: redmine
- databases:
- - redmine
- postgresql_server_listen: "'*'"
-
-- name: Import playbook
- import_playbook: ../../omedev/playbook.yml
diff --git a/molecule/docker-prod/tests/test_default.py b/molecule/docker-prod/tests/test_default.py
deleted file mode 100644
index ced9ed28..00000000
--- a/molecule/docker-prod/tests/test_default.py
+++ /dev/null
@@ -1,39 +0,0 @@
-import json
-import os
-import testinfra.utils.ansible_runner
-
-testinfra_hosts = testinfra.utils.ansible_runner.AnsibleRunner(
- os.environ['MOLECULE_INVENTORY_FILE']).get_hosts('all')
-
-
-def test_prometheus_targets(host):
- out = host.check_output(
- 'curl -k -f --user admin:monitoring '
- 'https://localhost/prometheus/api/v1/targets')
- d = json.loads(out)
- assert d['status'] == 'success'
- assert d['data']['droppedTargets'] == []
- unique_instances = set(
- t['labels']['instance'] for t in d['data']['activeTargets'])
- assert len({
- 'node.example.org:443',
- 'pg.example.org:443',
- 'omeroserver.example.org:443',
- 'omeroweb.example.org:443',
- 'idr.openmicroscopy.org:443',
- 'idr1.openmicroscopy.org:443',
- 'idr2.openmicroscopy.org:443',
- 'localhost:9090',
- }.difference(unique_instances)) == 0
-
-
-def test_minio_connect(host):
- out = host.check_output('curl -s http://localhost:9000 -I')
- assert 'Server: MinIO/' in out
-
-
-def test_redmine_connect(host):
- out = host.check_output(
- 'curl -k -f -L -H "Host: idr-redmine-docker.openmicroscopy.org" '
- 'https://localhost/')
- assert '
Redmine' in out
diff --git a/molecule/nightshade-webclients/molecule.yml b/molecule/nightshade-webclients/molecule.yml
deleted file mode 100644
index 71cd0c18..00000000
--- a/molecule/nightshade-webclients/molecule.yml
+++ /dev/null
@@ -1,58 +0,0 @@
----
-dependency:
- name: galaxy
- options:
- role-file: requirements.yml
-driver:
- name: docker
-lint: |
- yamllint .
- ansible-lint
- flake8
-platforms:
- - name: ns-webclients
- image: centos/systemd
- command: /sbin/init
- privileged: true
- groups:
- - docker-hosts
- - omero-web
- - monitored
-provisioner:
- name: ansible
- options:
- diff: true
- inventory:
- group_vars:
- all:
- molecule_test: true
- docker-hosts:
- # firewalld isn't installed, don't attempt to disable
- iptables_raw_disable_firewalld: false
- playbooks:
- converge: ../../site.yml
- lint:
- name: ansible-lint
-scenario:
- name: nightshade-webclients
- converge_sequence:
- - converge
- test_sequence:
- - destroy
- # dependency must come first so that ansible-lint will see a custom module
- # This might be fixed by https://github.com/ansible/molecule/pull/1739
- - dependency
- - lint
- - syntax
- - create
- - prepare
- - converge
- # FIXME: Some tasks are not idempotent
- # - idempotence
- ################################################################################
- # FIXME: Tests hang on Travis but pass locally
- # - verify
- ################################################################################
- - destroy
-verifier:
- name: testinfra
diff --git a/molecule/nightshade-webclients/tests/test_default.py b/molecule/nightshade-webclients/tests/test_default.py
deleted file mode 100644
index a88b0cc6..00000000
--- a/molecule/nightshade-webclients/tests/test_default.py
+++ /dev/null
@@ -1,40 +0,0 @@
-import os
-import pytest
-import testinfra.utils.ansible_runner
-
-testinfra_hosts = testinfra.utils.ansible_runner.AnsibleRunner(
- os.environ['MOLECULE_INVENTORY_FILE']).get_hosts('all')
-
-OMERO = '/opt/omero/server/OMERO.server/bin/omero'
-OMERO_LOGIN = '-C -s localhost -u root -w omero'
-
-
-@pytest.mark.parametrize("name", [
- 'nginx',
- 'omero-web',
- 'prometheus-node-exporter',
-])
-def test_service_running_and_enabled(host, name):
- service = host.service(name)
- assert service.is_running
- assert service.is_enabled
-
-
-def test_omero_metrics(host):
- out = host.check_output(
- 'curl -f -u monitoring:monitoring -k '
- 'https://localhost/django_prometheus/metrics')
- assert "django_http_responses_body_total_bytes_count" in out
-
-
-def test_omero_metrics_auth_fail(host):
- out = host.run(
- 'curl -f -u monitoring:incorrect -k '
- 'https://localhost/django_prometheus/metrics')
- assert out.rc == 22
- assert '401' in out.stderr
-
-
-def test_omero_nginx_ssl(host):
- out = host.check_output('curl -fkI https://localhost/')
- assert 'Location: /webclient/' in out
diff --git a/molecule/ome-demoserver/molecule.yml b/molecule/ome-demoserver/molecule.yml
index 3e399874..9964c028 100644
--- a/molecule/ome-demoserver/molecule.yml
+++ b/molecule/ome-demoserver/molecule.yml
@@ -11,23 +11,23 @@ lint: |
flake8
platforms:
- name: ome-demoserver
- image: centos:7
+ image: eniocarboni/docker-rockylinux-systemd:9
groups:
- ome-demoservers
provisioner:
name: ansible
playbooks:
- converge: ../../site.yml
+ converge: ../../omero/ome-demoserver.yml
lint:
name: ansible-lint
scenario:
name: ome-demoserver
test_sequence:
+ - dependency
- destroy
# dependency must come first so that ansible-lint will see a custom module
# This might be fixed by https://github.com/ansible/molecule/pull/1739
- dependency
- - lint
- syntax
verifier:
name: testinfra
diff --git a/molecule/ome-dundeeomero/Dockerfile.j2 b/molecule/ome-dundeeomero/Dockerfile.j2
deleted file mode 100644
index 00b7fd61..00000000
--- a/molecule/ome-dundeeomero/Dockerfile.j2
+++ /dev/null
@@ -1,22 +0,0 @@
-# Molecule managed
-
-{% if item.registry is defined %}
-FROM {{ item.registry.url }}/{{ item.image }}
-{% else %}
-FROM {{ item.image }}
-{% endif %}
-
-{% if item.env is defined %}
-{% for var, value in item.env.items() %}
-{% if value %}
-ENV {{ var }} {{ value }}
-{% endif %}
-{% endfor %}
-{% endif %}
-
-RUN if [ $(command -v apt-get) ]; then apt-get update && apt-get install -y python sudo bash ca-certificates iproute2 && apt-get clean; \
- elif [ $(command -v dnf) ]; then dnf makecache && dnf --assumeyes install python sudo python-devel python*-dnf bash iproute && dnf clean all; \
- elif [ $(command -v yum) ]; then yum makecache fast && yum install -y python sudo yum-plugin-ovl bash iproute ca-certificates && sed -i 's/plugins=0/plugins=1/g' /etc/yum.conf && yum clean all; \
- elif [ $(command -v zypper) ]; then zypper refresh && zypper install -y python sudo bash python-xml iproute2 && zypper clean -a; \
- elif [ $(command -v apk) ]; then apk update && apk add --no-cache python sudo bash ca-certificates; \
- elif [ $(command -v xbps-install) ]; then xbps-install -Syu && xbps-install -y python sudo bash ca-certificates iproute2 && xbps-remove -O; fi
diff --git a/molecule/ome-dundeeomero/molecule.yml b/molecule/ome-dundeeomero/molecule.yml
deleted file mode 100644
index 8260f968..00000000
--- a/molecule/ome-dundeeomero/molecule.yml
+++ /dev/null
@@ -1,59 +0,0 @@
----
-dependency:
- name: galaxy
- options:
- role-file: requirements.yml
-driver:
- name: docker
-lint: |
- yamllint .
- ansible-lint
- flake8
-platforms:
- - name: ome-dundeeomero.openmicroscopy.org
- image: centos/systemd
- command: /sbin/init
- privileged: true
- groups:
- - docker-hosts
- - omero-server
- - monitored
-provisioner:
- name: ansible
- options:
- diff: true
- # skip-tags:
- # - "skip_molecule"
- inventory:
- group_vars:
- all:
- molecule_test: true
- docker-hosts:
- # firewalld isn't installed, don't attempt to disable
- iptables_raw_disable_firewalld: false
- playbooks:
- converge: ../../site.yml
- lint:
- name: ansible-lint
- # env:
- # ANSIBLE_ROLES_PATH: ../../vendor
-scenario:
- name: ome-dundeeomero
- converge_sequence:
- - converge
- test_sequence:
- - destroy
- # dependency must come first so that ansible-lint will see a custom module
- # This might be fixed by https://github.com/ansible/molecule/pull/1739
- - dependency
- - lint
- - syntax
- - create
- - prepare
- - converge
- # FIXME: Some tasks are not idempotent
- # - idempotence
- - verify
- - destroy
-verifier:
- name: testinfra
diff --git a/molecule/ome-dundeeomero/tests/test_default.py b/molecule/ome-dundeeomero/tests/test_default.py
deleted file mode 100644
index dc1ac485..00000000
--- a/molecule/ome-dundeeomero/tests/test_default.py
+++ /dev/null
@@ -1,46 +0,0 @@
-import os
-import pytest
-import testinfra.utils.ansible_runner
-
-testinfra_hosts = testinfra.utils.ansible_runner.AnsibleRunner(
- os.environ['MOLECULE_INVENTORY_FILE']).get_hosts('all')
-
-OMERO = '/opt/omero/server/OMERO.server/bin/omero'
-OMERO_LOGIN = '-C -s localhost -u root -w omero'
-
-
-@pytest.mark.parametrize("name", [
- 'nginx',
- 'omero-server',
- 'postgresql-11',
- 'prometheus-node-exporter',
- 'prometheus-omero-exporter',
- 'prometheus-postgres-exporter',
-])
-def test_service_running_and_enabled(host, name):
- service = host.service(name)
- assert service.is_running
- assert service.is_enabled
-
-
-def test_omero_login(host):
- with host.sudo('omero-server'):
- host.check_output(
- '/opt/omero/server/OMERO.server/bin/omero '
- 'login -C -s localhost -u root -w omero')
-
-
-@pytest.mark.parametrize("curl", [
- 'localhost:9449/metrics',
- '-u monitoring:monitoring -k https://localhost/metrics/9449',
-])
-def test_omero_metrics(host, curl):
- out = host.check_output('curl -f %s' % curl)
- assert 'omero_sessions_active' in out
-
-
-def test_omero_metrics_auth_fail(host):
- out = host.run(
- 'curl -f -u monitoring:incorrect -k https://localhost/metrics/9449')
- assert out.rc == 22
- assert '401' in out.stderr
diff --git a/molecule/ome-pg-prod/prepare.yml b/molecule/ome-pg-prod/prepare.yml
deleted file mode 100644
index 04a5e83f..00000000
--- a/molecule/ome-pg-prod/prepare.yml
+++ /dev/null
@@ -1,9 +0,0 @@
-# Workaround lack of cron on Docker
-- name: Prepare pg prod
- hosts: ome-pg-prod1.openmicroscopy.org
- tasks:
- - name: Install cron
- become: true
- ansible.builtin.yum:
- name: cronie
- state: present
diff --git a/molecule/ome-pg-prod/tests/test_default.py b/molecule/ome-pg-prod/tests/test_default.py
deleted file mode 100644
index baa73f3b..00000000
--- a/molecule/ome-pg-prod/tests/test_default.py
+++ /dev/null
@@ -1,16 +0,0 @@
-import os
-import testinfra.utils.ansible_runner
-
-testinfra_hosts = testinfra.utils.ansible_runner.AnsibleRunner(
- os.environ['MOLECULE_INVENTORY_FILE']).get_hosts('all')
-
-
-def test_service_running_and_enabled(host):
- assert host.service('postgresql-9.6').is_running
- assert host.service('postgresql-9.6').is_enabled
-
-
-def test_dbs(host):
- out = host.check_output(
- 'PGPASSWORD=idr-redmine psql -hlocalhost -Uidr-redmine -l -tA')
- assert 'idr-redmine|idr-redmine|UTF8|' in out
diff --git a/molecule/omero-training-server/Dockerfile.j2 b/molecule/omero-training-server/Dockerfile.j2
deleted file mode 100644
index 00b7fd61..00000000
--- a/molecule/omero-training-server/Dockerfile.j2
+++ /dev/null
@@ -1,22 +0,0 @@
-# Molecule managed
-
-{% if item.registry is defined %}
-FROM {{ item.registry.url }}/{{ item.image }}
-{% else %}
-FROM {{ item.image }}
-{% endif %}
-
-{% if item.env is defined %}
-{% for var, value in item.env.items() %}
-{% if value %}
-ENV {{ var }} {{ value }}
-{% endif %}
-{% endfor %}
-{% endif %}
-
-RUN if [ $(command -v apt-get) ]; then apt-get update && apt-get install -y python sudo bash ca-certificates iproute2 && apt-get clean; \
- elif [ $(command -v dnf) ]; then dnf makecache && dnf --assumeyes install python sudo python-devel python*-dnf bash iproute && dnf clean all; \
- elif [ $(command -v yum) ]; then yum makecache fast && yum install -y python sudo yum-plugin-ovl bash iproute ca-certificates && sed -i 's/plugins=0/plugins=1/g' /etc/yum.conf && yum clean all; \
- elif [ $(command -v zypper) ]; then zypper refresh && zypper install -y python sudo bash python-xml iproute2 && zypper clean -a; \
- elif [ $(command -v apk) ]; then apk update && apk add --no-cache python sudo bash ca-certificates; \
- elif [ $(command -v xbps-install) ]; then xbps-install -Syu && xbps-install -y python sudo bash ca-certificates iproute2 && xbps-remove -O; fi
diff --git a/molecule/omero-training-server/molecule.yml b/molecule/omero-training-server/molecule.yml
deleted file mode 100644
index af145c55..00000000
--- a/molecule/omero-training-server/molecule.yml
+++ /dev/null
@@ -1,59 +0,0 @@
----
-dependency:
- name: galaxy
- options:
- role-file: requirements.yml
-driver:
- name: docker
-lint: |
- yamllint .
- ansible-lint
- flake8
-platforms:
- - name: ome-outreach
- image: centos/systemd
- command: /sbin/init
- privileged: true
- groups:
- - docker-hosts
- - omero-server
- - omero-web
- - monitored
-provisioner:
- name: ansible
- inventory:
- group_vars:
- all:
- molecule_test: true
- postgresql_version: "13"
- docker-hosts:
- # This should allow docker-in-docker to work
- docker_storage_driver: vfs
- # Latest version 17.12.1.ce-1.el7.centos has a bug that prevents
- # testing on travis: https://github.com/docker/for-linux/issues/219
- docker_version: 17.09.1.ce-1.el7.centos
- # firewalld isn't installed, don't attempt to disable
- iptables_raw_disable_firewalld: false
- playbooks:
- prepare: ../resources/prepare-iproute.yml
- converge: ../../site.yml
- lint:
- name: ansible-lint
-scenario:
- name: omero-training-server
- test_sequence:
- - destroy
- # dependency must come first so that ansible-lint will see a custom module
- # This might be fixed by https://github.com/ansible/molecule/pull/1739
- - dependency
- - lint
- - syntax
- - create
- - prepare
- - converge
- # FIXME: Some tasks are not idempotent
- # - idempotence
- - verify
- - destroy
-verifier:
- name: testinfra
diff --git a/molecule/omero-training-server/tests/test_default.py b/molecule/omero-training-server/tests/test_default.py
deleted file mode 100644
index 1660aff8..00000000
--- a/molecule/omero-training-server/tests/test_default.py
+++ /dev/null
@@ -1,63 +0,0 @@
-import os
-import pytest
-import testinfra.utils.ansible_runner
-
-testinfra_hosts = testinfra.utils.ansible_runner.AnsibleRunner(
- os.environ['MOLECULE_INVENTORY_FILE']).get_hosts('all')
-
-OMERO = '/opt/omero/server/OMERO.server/bin/omero'
-OMERO_LOGIN = '-C -s localhost -u root -w omero'
-
-
-@pytest.mark.parametrize("name", [
- 'nginx',
- 'omero-server',
- 'omero-web',
- 'postgresql-13',
- 'prometheus-node-exporter',
- 'prometheus-omero-exporter',
- 'prometheus-postgres-exporter',
-])
-def test_service_running_and_enabled(host, name):
- service = host.service(name)
- assert service.is_running
- assert service.is_enabled
-
-
-def test_omero_login(host):
- with host.sudo('importer1'):
- host.check_output(
- '/opt/omero/server/OMERO.server/bin/omero '
- 'login -C -s localhost -u root -w omero')
-
-
-@pytest.mark.parametrize("curl", [
- 'localhost:9449/metrics',
- '-u monitoring:monitoring -k https://localhost/metrics/9449',
-])
-def test_omero_metrics(host, curl):
- out = host.check_output('curl -f %s' % curl)
- assert 'omero_sessions_active' in out
-
-
-def test_omero_metrics_auth_fail(host):
- out = host.run(
- 'curl -f -u monitoring:incorrect -k https://localhost/metrics/9449')
- assert out.rc == 22
- assert '401' in out.stderr
-
-
-def test_omero_nginx_ssl(host):
- out = host.check_output('curl -fkI https://localhost/')
- assert 'Location: /webclient/' in out
-
-
-def test_local_ldap(host):
- initialised = host.check_output(
- '/home/ldap/ldapmanager get dc=openmicroscopy,dc=org')
- if len(initialised.strip()) == 0:
- host.check_output('/home/ldap/ldapmanager init')
-
- out = host.check_output(
- '/home/ldap/ldapmanager get dc=openmicroscopy,dc=org')
- assert 'dn: dc=openmicroscopy,dc=org' in out
diff --git a/molecule/release/molecule.yml b/molecule/release/molecule.yml
deleted file mode 100644
index add768eb..00000000
--- a/molecule/release/molecule.yml
+++ /dev/null
@@ -1,37 +0,0 @@
----
-dependency:
- name: galaxy
-driver:
- name: docker
-lint: |
- yamllint .
- ansible-lint
- flake8
-platforms:
- - name: release
- image: centos:7
- groups:
- - idr0-slot3.openmicroscopy.org
- - name: prerelease
- image: centos:7
- groups:
- - idr0-slot3.openmicroscopy.org
-provisioner:
- name: ansible
- playbooks:
- converge: ../../release/release-acceptance.yml
- inventory:
- group_vars:
- idr0-slot3.openmicroscopy.org:
- product: component
- host_vars:
- prerelease:
- version: '3.2.0-rc1'
- release:
- version: 3.2.0
- lint:
- name: ansible-lint
-scenario:
- name: release
-verifier:
- name: testinfra
diff --git a/molecule/release/prepare.yml b/molecule/release/prepare.yml
deleted file mode 100644
index 7f4743fd..00000000
--- a/molecule/release/prepare.yml
+++ /dev/null
@@ -1,72 +0,0 @@
----
-- name: Prepare release
- hosts: all
- vars:
- www_folders:
- - /uod/idr/www/docs.openmicroscopy.org
- - /uod/idr/www/downloads.openmicroscopy.org
- releases:
- - 3.2.0
- - 3.2.0-rc1
- tasks:
- - name: Create existing released components
- ansible.builtin.file:
- path: "{{ item }}/component/3.1.8"
- state: directory
- owner: root
- group: root
- mode: 01555
- with_items: "{{ www_folders }}"
- - name: Create minor version directory
- ansible.builtin.file:
- path: "{{ item }}/component/3.1"
- state: directory
- with_items: "{{ www_folders }}"
- - name: Create minor version redirects
- ansible.builtin.copy:
- dest: "{{ item }}/component/3.1/.htaccess"
- content: "Redirect 301 /component/3.1 /component/3.1.8"
- with_items: "{{ www_folders }}"
- - name: Create major version directory
- ansible.builtin.file:
- path: "{{ item }}/component/3"
- state: directory
- with_items: "{{ www_folders }}"
- - name: Create major version redirects
- ansible.builtin.copy:
- dest: "{{ item }}/component/3/.htaccess"
- content: "Redirect 301 /component/3 /component/3.1.8"
- with_items: "{{ www_folders }}"
- - name: Create latest version directory
- ansible.builtin.file:
- path: "{{ item }}/component/latest"
- state: directory
- with_items: "{{ www_folders }}"
- - name: Create latest version redirects
- ansible.builtin.copy:
- dest: "{{ item }}/component/latest/.htaccess"
- content: "Redirect 301 /component/latest /component/3.1.8"
- with_items: "{{ www_folders }}"
- - name: Create new release components
- ansible.builtin.file:
- path: "{{ item[0] }}/component/{{ item[1] }}"
- state: directory
- mode: 01777
- with_nested:
- - "{{ www_folders }}"
- - "{{ releases }}"
- - name: Create .htaccess file
- ansible.builtin.file:
- path: "{{ item[0] }}/component/{{ item[1] }}/.htaccess"
- state: touch
- with_nested:
- - "{{ www_folders }}"
- - "{{ releases }}"
- - name: Create mock content
- ansible.builtin.file:
- path: "{{ item[0] }}/component/{{ item[1] }}/test"
- state: touch
- mode: 01777
- with_nested:
- - "{{ www_folders }}"
- - "{{ releases }}"
diff --git a/molecule/release/tests/test_default.py b/molecule/release/tests/test_default.py
deleted file mode 100644
index b1961472..00000000
--- a/molecule/release/tests/test_default.py
+++ /dev/null
@@ -1,49 +0,0 @@
-import os
-import pytest
-import testinfra.utils.ansible_runner
-
-testinfra_hosts = testinfra.utils.ansible_runner.AnsibleRunner(
- os.environ['MOLECULE_INVENTORY_FILE']).get_hosts('all')
-
-DOWNLOADS_URL = "/uod/idr/www/downloads.openmicroscopy.org"
-DOCS_URL = "/uod/idr/www/docs.openmicroscopy.org"
-
-
-@pytest.mark.parametrize('base_folder', [DOWNLOADS_URL, DOCS_URL])
-def test_permissions(host, base_folder):
- v = host.ansible.get_variables()
- assert not host.file('%s/component/%s/.htaccess' % (
- base_folder, v['version'])).exists
-
- f = host.file('%s/component/%s' % (base_folder, v['version']))
- assert f.exists
- assert f.user == 'root'
- assert oct(f.mode) == '0o1555'
-
-
-@pytest.mark.parametrize('base_folder', [DOWNLOADS_URL, DOCS_URL])
-def test_redirects(host, base_folder):
- v = host.ansible.get_variables()
- hostname = host.backend.get_hostname()
- f = host.file('%s/component/3.2/.htaccess' % base_folder)
- if hostname == 'release':
- assert f.exists
- assert f.content_string == (
- 'Redirect 301 /component/3.2 /component/%s' % v['version'])
- elif hostname == 'prelease':
- assert not f.exists
- f = host.file('%s/component/3/.htaccess' % base_folder)
- assert f.exists
- if hostname == 'release':
- assert f.content_string == (
- 'Redirect 301 /component/3 /component/%s' % v['version'])
- elif hostname == 'prelease':
- assert f.content_string == 'Redirect 301 /component/3 /component/3.1.8'
- f = host.file('%s/component/latest/.htaccess' % base_folder)
- assert f.exists
- if hostname == 'release':
- assert f.content_string == (
- 'Redirect 301 /component/latest /component/%s' % v['version'])
- elif hostname == 'prelease':
- assert (f.content_string ==
- 'Redirect 301 /component/latest /component/3.1.8')
diff --git a/molecule/resources/Dockerfile.j2 b/molecule/resources/Dockerfile.j2
deleted file mode 100644
index 00b7fd61..00000000
--- a/molecule/resources/Dockerfile.j2
+++ /dev/null
@@ -1,22 +0,0 @@
-# Molecule managed
-
-{% if item.registry is defined %}
-FROM {{ item.registry.url }}/{{ item.image }}
-{% else %}
-FROM {{ item.image }}
-{% endif %}
-
-{% if item.env is defined %}
-{% for var, value in item.env.items() %}
-{% if value %}
-ENV {{ var }} {{ value }}
-{% endif %}
-{% endfor %}
-{% endif %}
-
-RUN if [ $(command -v apt-get) ]; then apt-get update && apt-get install -y python sudo bash ca-certificates iproute2 && apt-get clean; \
- elif [ $(command -v dnf) ]; then dnf makecache && dnf --assumeyes install python sudo python-devel python*-dnf bash iproute && dnf clean all; \
- elif [ $(command -v yum) ]; then yum makecache fast && yum install -y python sudo yum-plugin-ovl bash iproute ca-certificates && sed -i 's/plugins=0/plugins=1/g' /etc/yum.conf && yum clean all; \
- elif [ $(command -v zypper) ]; then zypper refresh && zypper install -y python sudo bash python-xml iproute2 && zypper clean -a; \
- elif [ $(command -v apk) ]; then apk update && apk add --no-cache python sudo bash ca-certificates; \
- elif [ $(command -v xbps-install) ]; then xbps-install -Syu && xbps-install -y python sudo bash ca-certificates iproute2 && xbps-remove -O; fi
diff --git a/molecule/resources/prepare-iproute.yml b/molecule/resources/prepare-iproute.yml
deleted file mode 100644
index 1730ad08..00000000
--- a/molecule/resources/prepare-iproute.yml
+++ /dev/null
@@ -1,15 +0,0 @@
-# Install iproute for ansible network vars
-- name: Prepare iproute in resources
- hosts: all
- tasks:
- - name: Install iproute
- become: true
- ansible.builtin.yum:
- name: iproute
- state: present
-
- - name: Install cron
- become: true
- ansible.builtin.yum:
- name: cronie
- state: present
diff --git a/molecule/web-proxy/molecule.yml b/molecule/web-proxy/molecule.yml
deleted file mode 100644
index 8bdf86d9..00000000
--- a/molecule/web-proxy/molecule.yml
+++ /dev/null
@@ -1,30 +0,0 @@
----
-dependency:
- name: galaxy
- options:
- role-file: requirements.yml
-driver:
- name: docker
-lint: |
- yamllint .
- ansible-lint
- flake8
-platforms:
- - name: web-proxy
- image: centos:7
- groups:
- - web-proxies
-provisioner:
- name: ansible
- playbooks:
- converge: ../../web-proxy/playbook.yml
- lint:
- name: ansible-lint
-scenario:
- name: web-proxy
- test_sequence:
- - lint
- - dependency
- - syntax
-verifier:
- name: testinfra
diff --git a/molecule/www/Dockerfile.j2 b/molecule/www/Dockerfile.j2
deleted file mode 120000
index 0e9184b4..00000000
--- a/molecule/www/Dockerfile.j2
+++ /dev/null
@@ -1 +0,0 @@
-../resources/Dockerfile.j2
\ No newline at end of file
diff --git a/molecule/www/tests/test_default.py b/molecule/www/tests/test_default.py
deleted file mode 100644
index d61e82f1..00000000
--- a/molecule/www/tests/test_default.py
+++ /dev/null
@@ -1,25 +0,0 @@
-import os
-import pytest
-import testinfra.utils.ansible_runner
-
-testinfra_hosts = testinfra.utils.ansible_runner.AnsibleRunner(
- os.environ['MOLECULE_INVENTORY_FILE']).get_hosts('all')
-
-
-@pytest.mark.skip(reason="Causes Travis CI to exceed 10min timeout")
-@pytest.mark.parametrize("address", [
- "http://localhost/",
- "https://localhost/",
-])
-def test_web(host, address):
- out = host.check_output('curl -k %s' % address)
- assert 'Home | Open Microscopy Environment (OME)' in out
-
-
-@pytest.mark.skip(reason="Causes Travis CI to exceed 10min timeout")
-def test_archived_community(host):
- out = host.check_output('curl -kL https://localhost/community')
- assert 'Powered by phpBB' in out
-
- out = host.check_output('curl -kIL https://localhost/community')
- assert 'Set-Cookie: phpbb' not in out
diff --git a/molecule/www/tests/test_redirects.py b/molecule/www/tests/test_redirects.py
deleted file mode 100644
index 3dfa70ac..00000000
--- a/molecule/www/tests/test_redirects.py
+++ /dev/null
@@ -1,116 +0,0 @@
-import os
-import testinfra.utils.ansible_runner
-import pytest
-
-testinfra_hosts = testinfra.utils.ansible_runner.AnsibleRunner(
- os.environ['MOLECULE_INVENTORY_FILE']).get_hosts('all')
-
-external_uris = [
- ('/forums', 'https://forum.image.sc/c/data-management'),
- ('/omero-blog', 'http://blog.openmicroscopy.org'),
- ('/site/about/development-teams/glencoe-software',
- 'https://www.glencoesoftware.com/team.html'),
- ('/site/community/scripts',
- 'https://docs.openmicroscopy.org/latest/omero/developers/'
- 'scripts/index.html'),
- ('/site/support/bio-formats',
- 'https://docs.openmicroscopy.org/latest/bio-formats/'),
- ('/site/support/omero',
- 'https://docs.openmicroscopy.org/latest/omero/'),
- ('/site/support/ome-model',
- 'https://docs.openmicroscopy.org/latest/ome-model/'),
- ('/site/support/file-formats',
- 'https://docs.openmicroscopy.org/latest/ome-model/'),
- ('/site/support/file-formats/schemas/specifications/'
- 'compliant-file-specification',
- 'https://docs.openmicroscopy.org/latest/ome-model/specifications/'),
- ('/site/support/ome-tiff',
- 'https://docs.openmicroscopy.org/latest/ome-model/ome-tiff/'),
- ('/site/support/ome-files-cpp',
- 'https://docs.openmicroscopy.org/latest/ome-files-cpp/'),
- ('/site/support/contributing',
- 'https://docs.openmicroscopy.org/contributing/'),
- ('/info/flimfit', 'http://flimfit.org'),
- ('/info/scripts',
- 'https://docs.openmicroscopy.org/latest/omero/developers/'
- 'scripts/index.html'),
- ('/info/bio-formats',
- 'https://docs.openmicroscopy.org/latest/bio-formats/'),
- ('/info/slidebook',
- 'https://www.intelligent-imaging.com/technical-answers'),
-]
-
-redirect_uris = [
- ('/site', '/'),
- ('/site/about', '/about'),
- ('/site/about/licensing', '/licensing'),
- ('/site/about/licensing-attribution', '/licensing'),
- ('/site/about/licensing-attribution/licensing', '/licensing'),
- ('/site/about/ome-contributors', '/contributors'),
- ('/site/about/partners', '/commercial-partners'),
- ('/site/about/development-teams', '/teams'),
- ('/site/about/publications', '/citing-ome'),
- ('/site/about/who-ome', '/teams'),
- ('/site/about/what-omero/overview', '/omero'),
- ('/site/about/roadmap', '/about'),
- ('/site/about/project-history', '/about'),
-
- ('/site/community', '/support'),
- ('/site/community/mailing-lists', '/support'),
- ('/site/events', '/events'),
- ('/site/community/minutes/conference-calls', '/on-the-web'),
- ('/site/community/minutes/meetings/12th-annual-users-meeting-2017',
- '/events/12th-annual-users-meeting-2017.html'),
- ('/site/community/minutes/meetings/11th-annual-users-meeting-2016',
- '/events/11th-annual-users-meeting-2016.html'),
- ('/site/community/minutes/meetings/10th-annual-users-meeting-june-2015',
- '/events/10th-annual-users-meeting-june-2015.html'),
- ('/site/community/minutes/meetings/9th-annual-users-meeting-june-2014',
- '/events/9th-annual-users-meeting-june-2014.html'),
- ('/site/community/jobs', '/careers'),
-
- ('/site/products', '/products'),
- ('/site/products/bio-formats', '/bio-formats'),
- ('/site/products/bio-formats/downloads', '/bio-formats/downloads/'),
- ('/site/products/omero', '/omero'),
- ('/site/products/omero/downloads', '/omero/downloads/'),
- ('/site/products/omero/feature-list', '/omero/features/'),
- ('/site/products/omero/secvuln', '/security/advisories/'),
- ('/site/products/ome5/secvuln', '/security/advisories/'),
- ('/site/products/omero/secvuln/2014-SV3-csrf',
- '/security/advisories/2014-SV3-csrf/'),
-
- ('/site/support', '/docs'),
- ('/site/support/ome-artwork', '/artwork'),
- ('/site/support/ome-artwork/artwork-usage', '/artwork'),
- ('/site/news', '/announcements'),
-
- ('/info/vulnerabilities', '/security/advisories/'),
- ('/info/vulnerabilities/2014-SV3-csrf',
- '/security/advisories/2014-SV3-csrf/'),
- ('/info/omero', '/omero'),
- ('/info/cls', '/omero/downloads/'),
- ('/info/download', '/omero/downloads/'),
- ('/info/downloads', '/omero/downloads/'),
- ('/info/attribution', '/licensing/'),
-]
-
-
-@pytest.mark.skip(reason="Causes Travis CI to exceed 10min timeout")
-@pytest.mark.parametrize('path,redirect', redirect_uris)
-def test_internal_redirects(host, path, redirect):
- out = host.check_output('curl -I http://localhost%s' % path)
- assert 'HTTP/1.1 302' in out
- assert 'Location: http://localhost%s' % redirect in out
-
-
-@pytest.mark.skip(reason="Causes Travis CI to exceed 10min timeout")
-@pytest.mark.parametrize('path,redirect', external_uris)
-def test_external_redirects(host, path, redirect):
- out = host.check_output('curl -I http://localhost%s' % path)
- assert 'HTTP/1.1 302' in out
- assert 'Location: %s' % redirect in out
-
- out = host.check_output('curl -I http://localhost%s/' % path)
- assert 'HTTP/1.1 302' in out
- assert 'Location: %s' % redirect in out
diff --git a/omero/ome-demoserver.yml b/omero/ome-demoserver.yml
index 99fc6085..6b727b3f 100644
--- a/omero/ome-demoserver.yml
+++ b/omero/ome-demoserver.yml
@@ -8,106 +8,105 @@
pre_tasks:
- name: Install open-vm-tools if system is a VMware vm
become: true
- ansible.builtin.yum:
+ ansible.builtin.dnf:
name: open-vm-tools
state: installed
when: >
((ansible_virtualization_type is defined)
and (ansible_virtualization_type == "VMware"))
- # Perhaps alter the role at
- # https://github.com/openmicroscopy/ansible-role-lvm-partition/
- # to make some of the variables non-required.
- - name: Resize root FS without altering mount options
- tags: lvm
- become: true
- lvol:
- lv: root
- vg: VolGroup00
- size: "{{ provision_root_lvsize }}"
- shrink: false
-
- - name: Install Make Movie script Prerequisite | MEncoder - Repo
- become: true
- ansible.builtin.yum:
- name: "http://li.nux.ro/download/nux/dextop/el7\
- /x86_64/nux-dextop-release-0-5.el7.nux.noarch.rpm"
- state: present
-
- - name: Install Make Movie script Prerequisite | MEncoder - Package
- become: true
- ansible.builtin.yum:
- name: mencoder
- state: present
-
- - name: Server-side script prerequisites
- become: true
- ansible.builtin.yum:
- name: "{{ item }}"
- state: present
- with_items:
- - mencoder # For the 'make movie' script
+ # # Perhaps alter the role at
+ # # https://github.com/openmicroscopy/ansible-role-lvm-partition/
+ # # to make some of the variables non-required.
+ # - name: Resize root FS without altering mount options
+ # tags: lvm
+ # become: true
+ # lvol:
+ # lv: root
+ # vg: VolGroup00
+ # size: "{{ provision_root_lvsize }}"
+ # shrink: false
+
+ # - name: Install Make Movie script Prerequisite | MEncoder - Repo
+ # become: true
+ # ansible.builtin.yum:
+ # name: "http://li.nux.ro/download/nux/dextop/el7\
+ # /x86_64/nux-dextop-release-0-5.el7.nux.noarch.rpm"
+ # state: present
+
+ # - name: Install Make Movie script Prerequisite | MEncoder - Package
+ # become: true
+ # ansible.builtin.yum:
+ # name: mencoder
+ # state: present
+
+ # - name: Server-side script prerequisites
+ # become: true
+ # ansible.builtin.yum:
+ # name: "{{ item }}"
+ # state: present
+ # with_items:
+ # - mencoder # For the 'make movie' script
roles:
# Now OME are using RHEL without Spacewalk, the current best-method of
# checking `is server deployed in Dundee/SLS` is
# checking for the SLS nameservers.
- - role: ome.system_monitor_agent
- tags: monitoring
- when: "'10.1.255.216' in ansible_dns.nameservers"
+ # - role: ome.system_monitor_agent
+ # tags: monitoring
+ # when: "'10.1.255.216' in ansible_dns.nameservers"
# Disk Layout - PostgreSQL | data dir on separate VG (SSD)
- - role: ome.lvm_partition
- tags: lvm
- lvm_lvname: pgdata
- lvm_vgname: "{{ provision_postgres_vgname }}"
- lvm_lvmount: /var/lib/pgsql
- lvm_lvsize: "{{ provision_postgres_lvsize }}"
- lvm_lvfilesystem: "{{ filesystem }}"
- lvm_shrink: false
-
- # Disk Layout - OMERO | VG and LV (separate disk) for Binary Repository
- - role: ome.lvm_partition
- tags: lvm
- lvm_lvname: datadir
- lvm_vgname: "{{ provision_omero_server_datadir_vgname }}"
- lvm_lvmount: "{{ omero_server_datadir }}"
- lvm_lvsize: "{{ provision_omero_server_datadir_lvsize }}"
- lvm_lvfilesystem: "{{ filesystem }}"
- lvm_shrink: false
-
- # Disk Layout - OMERO.server | LV for dist & logs
- - role: ome.lvm_partition
- tags: lvm
- lvm_lvname: omero_server_basedir
- lvm_vgname: VolGroup00
- lvm_lvmount: "{{ omero_server_basedir }}"
- lvm_lvsize: "{{ provision_omero_server_basedir_lvsize }}"
- lvm_lvfilesystem: "{{ filesystem }}"
- lvm_shrink: false
-
- # Disk Layout - OMERO.web | LV for dist & logs
- - role: ome.lvm_partition
- tags: lvm
- lvm_lvname: omero_web_basedir
- lvm_vgname: VolGroup00
- lvm_lvmount: "{{ omero_web_basedir }}"
- lvm_lvsize: "{{ provision_omero_web_basedir_lvsize }}"
- lvm_lvfilesystem: "{{ filesystem }}"
- lvm_shrink: false
-
- - role: ome.nginx
- nginx_version: 1.16.1
+ # - role: ome.lvm_partition
+ # tags: lvm
+ # lvm_lvname: pgdata
+ # lvm_vgname: "{{ provision_postgres_vgname }}"
+ # lvm_lvmount: /var/lib/pgsql
+ # lvm_lvsize: "{{ provision_postgres_lvsize }}"
+ # lvm_lvfilesystem: "{{ filesystem }}"
+ # lvm_shrink: false
+
+ # # Disk Layout - OMERO | VG and LV (separate disk) for Binary Repository
+ # - role: ome.lvm_partition
+ # tags: lvm
+ # lvm_lvname: datadir
+ # lvm_vgname: "{{ provision_omero_server_datadir_vgname }}"
+ # lvm_lvmount: "{{ omero_server_datadir }}"
+ # lvm_lvsize: "{{ provision_omero_server_datadir_lvsize }}"
+ # lvm_lvfilesystem: "{{ filesystem }}"
+ # lvm_shrink: false
+
+ # # Disk Layout - OMERO.server | LV for dist & logs
+ # - role: ome.lvm_partition
+ # tags: lvm
+ # lvm_lvname: omero_server_basedir
+ # lvm_vgname: VolGroup00
+ # lvm_lvmount: "{{ omero_server_basedir }}"
+ # lvm_lvsize: "{{ provision_omero_server_basedir_lvsize }}"
+ # lvm_lvfilesystem: "{{ filesystem }}"
+ # lvm_shrink: false
+
+ # # Disk Layout - OMERO.web | LV for dist & logs
+ # - role: ome.lvm_partition
+ # tags: lvm
+ # lvm_lvname: omero_web_basedir
+ # lvm_vgname: VolGroup00
+ # lvm_lvmount: "{{ omero_web_basedir }}"
+ # lvm_lvsize: "{{ provision_omero_web_basedir_lvsize }}"
+ # lvm_lvfilesystem: "{{ filesystem }}"
+ # lvm_shrink: false
+
+ # nginx_version: 1.16.1
- role: ome.postgresql
# no_log: true
postgresql_databases:
- - name: omero
+ - name: omero
+ owner: demo
postgresql_users:
- - user: "{{ omero_server_dbuser | default('omero') }}"
- password: "{{ omero_server_dbpassword | default('omero') }}"
- databases:
- - omero
+ - user: "{{ omero_server_dbuser | default('omero') }}"
+ password: "{{ omero_server_dbpassword | default('omero') }}"
+ databases: []
- role: ome.omero_server
# Defaults overridden in private configuration
@@ -117,15 +116,17 @@
omero_server_dbname: omero
omero_server_systemd_limit_nofile: 16384
+ - role: ome.nginx
+
- role: ome.omero_web
# Defaults overridden in private configuration
omero_web_systemd_limit_nofile: 16384
omero_web_python_addons:
- "omero-figure=={{ omero_figure_release }}"
- "omero-fpbioimage=={{ omero_fpbioimage_release }}"
- - "omero-webtagging-autotag=={{ omero_webtagging_autotag_release }}"
- - "omero-webtagging-tagsearch==\
- {{ omero_webtagging_tagsearch_release }}"
+ - "omero-autotag=={{ omero_autotag_release }}"
+ - "omero-tagsearch==\
+ {{ omero_tagsearch_release }}"
- "omero-iviewer=={{ omero_iviewer_release }}"
- "omero-parade=={{ omero_parade_release }}"
- "omero-signup=={{ omero_signup_release }}"
@@ -156,7 +157,6 @@
postgresql_backup_compress: true
postgresql_backup_dir: /OMERO/pgbackup
postgresql_backup_filename_format: "nightly-omero-%a.pgdump.gz"
- postgresql_backup_minimum_expected_size: 100000000
handlers:
- name: Reload web server
@@ -168,6 +168,10 @@
post_tasks:
+ - name: Allow nginx to connect to omero-web
+ become: yes
+ command: setsebool -P httpd_can_network_connect on
+
- name: NGINX - Performance tuning - worker processes
become: true
ansible.builtin.replace:
@@ -256,50 +260,50 @@
- restart omero-web
no_log: true
- - name: Check_MK postgres plugin | check for plugin existence
- tags: monitoring
- ansible.builtin.stat:
- path: "{{ check_mk_agent_plugin_path }}/mk_postgres"
- register: check_mk_postgres_plugin_st
-
- - name: Check_MK postgres plugin | activate the plugin
- tags: monitoring
- become: true
- command: >
- cp "{{ check_mk_agent_plugin_path }}/mk_postgres"
- /usr/share/check-mk-agent/plugins/
- creates=/usr/share/check-mk-agent/plugins/mk_postgres
- when: check_mk_postgres_plugin_st.stat.exists
-
- - name: Check_MK logwatch plugin | check for plugin existence
- tags: monitoring
- ansible.builtin.stat:
- path: "{{ check_mk_agent_plugin_path }}/mk_logwatch"
- register: check_mk_logwatch_plugin_st
-
- - name: Check_MK logwatch plugin | activate the plugin
- tags: monitoring
- become: true
- command: >
- cp "{{ check_mk_agent_plugin_path }}/mk_logwatch"
- /usr/share/check-mk-agent/plugins/
- creates=/usr/share/check-mk-agent/plugins/mk_logwatch
- when: check_mk_logwatch_plugin_st.stat.exists
-
- - name: Check_MK logwatch plugin | check for default config file
- tags: monitoring
- ansible.builtin.stat:
- path: "{{ check_mk_agent_config_example_path }}/logwatch.cfg"
- register: check_mk_logwatch_plugin_conf_st
-
- - name: Check_MK logwatch plugin | copy the default config
- tags: monitoring
- become: true
- command: >
- cp "{{ check_mk_agent_config_example_path }}/logwatch.cfg"
- "{{ check_mk_agent_config_path }}/logwatch.cfg"
- creates="{{ check_mk_agent_config_path }}/logwatch.cfg"
- when: check_mk_logwatch_plugin_conf_st.stat.exists
+ # - name: Check_MK postgres plugin | check for plugin existence
+ # tags: monitoring
+ # ansible.builtin.stat:
+ # path: "{{ check_mk_agent_plugin_path }}/mk_postgres"
+ # register: check_mk_postgres_plugin_st
+
+ # - name: Check_MK postgres plugin | activate the plugin
+ # tags: monitoring
+ # become: true
+ # command: >
+ # cp "{{ check_mk_agent_plugin_path }}/mk_postgres"
+ # /usr/share/check-mk-agent/plugins/
+ # creates=/usr/share/check-mk-agent/plugins/mk_postgres
+ # when: check_mk_postgres_plugin_st.stat.exists
+
+ # - name: Check_MK logwatch plugin | check for plugin existence
+ # tags: monitoring
+ # ansible.builtin.stat:
+ # path: "{{ check_mk_agent_plugin_path }}/mk_logwatch"
+ # register: check_mk_logwatch_plugin_st
+
+ # - name: Check_MK logwatch plugin | activate the plugin
+ # tags: monitoring
+ # become: true
+ # command: >
+ # cp "{{ check_mk_agent_plugin_path }}/mk_logwatch"
+ # /usr/share/check-mk-agent/plugins/
+ # creates=/usr/share/check-mk-agent/plugins/mk_logwatch
+ # when: check_mk_logwatch_plugin_st.stat.exists
+
+ # - name: Check_MK logwatch plugin | check for default config file
+ # tags: monitoring
+ # ansible.builtin.stat:
+ # path: "{{ check_mk_agent_config_example_path }}/logwatch.cfg"
+ # register: check_mk_logwatch_plugin_conf_st
+
+ # - name: Check_MK logwatch plugin | copy the default config
+ # tags: monitoring
+ # become: true
+ # command: >
+ # cp "{{ check_mk_agent_config_example_path }}/logwatch.cfg"
+ # "{{ check_mk_agent_config_path }}/logwatch.cfg"
+ # creates="{{ check_mk_agent_config_path }}/logwatch.cfg"
+ # when: check_mk_logwatch_plugin_conf_st.stat.exists
- name: PostgreSQL Nightly Backups | Remove old cron job
become: true
@@ -332,26 +336,26 @@
vars:
omero_figure_release: >-
- {{ omero_figure_release_override | default('6.0.1') }}
+ {{ omero_figure_release_override | default('6.2.2') }}
omero_figure_script_release: >-
- {{ omero_figure_script_release_override | default('v6.0.1') }}
+ {{ omero_figure_script_release_override | default('v6.2.2') }}
omero_fpbioimage_release: >-
{{ omero_fpbioimage_release_override | default('0.4.1') }}
omero_iviewer_release: >-
- {{ omero_iviewer_release_override | default('0.13.0') }}
+ {{ omero_iviewer_release_override | default('0.14.0') }}
omero_parade_release: >-
{{ omero_parade_release_override | default('0.2.4') }}
- omero_webtagging_autotag_release: >-
- {{ omero_webtagging_autotag_release_override | default('3.2.0') }}
- omero_webtagging_tagsearch_release: >-
- {{ omero_webtagging_tagsearch_release_override | default('3.2.0') }}
+ omero_autotag_release: >-
+ {{ omero_autotag_release_override | default('4.0.1') }}
+ omero_tagsearch_release: >-
+ {{ omero_tagsearch_release_override | default('4.1.1') }}
omero_signup_release: >-
- {{ omero_signup_release_override | default('0.3.2') }}
+ {{ omero_signup_release_override | default('0.3.3') }}
omero_server_release: >-
- {{ omero_server_release_override | default('5.6.8') }}
- omero_web_release: "{{ omero_web_release_override | default('5.22.1') }}"
- omero_py_release: "{{ omero_py_release_override | default('5.15.0') }}"
+ {{ omero_server_release_override | default('5.6.11') }}
+ omero_web_release: "{{ omero_web_release_override | default('5.26.0') }}"
+ omero_py_release: "{{ omero_py_release_override | default('5.19.2') }}"
# For https://github.com/openmicroscopy/ansible-role-java,
# which is a dependency.
java_jdk_install: true
@@ -388,7 +392,7 @@
[1] In your browser, go to omero-guides.readthedocs.io/en/latest
and click on OMERO walkthrough example under Getting started.'
- postgresql_version: "11"
+ postgresql_version: "16"
filesystem: "xfs"
omero_server_config_set:
@@ -445,7 +449,7 @@
omero.web.wsgi_workers: >-
{{ (2 * (ansible_processor_count *
ansible_processor_cores)) + 1 }}
- omero.web.admins: "{{ omero_web_admins }}"
+ # omero.web.admins: "{{ omero_web_admins }}"
# https://pypi.org/project/omero-iviewer/ - set iviewer to default viewer
omero.web.viewer.view: omero_iviewer.views.index
omero.web.nginx_server_extra_config:
diff --git a/omero/requirements.yml b/omero/requirements.yml
new file mode 100644
index 00000000..1fb4f79c
--- /dev/null
+++ b/omero/requirements.yml
@@ -0,0 +1,24 @@
+---
+- role: ome.omero_common
+- role: ome.basedeps
+- role: ome.java
+- role: ome.python3_virtualenv
+- role: ome.ice
+- role: ome.postgresql
+- role: ome.postgresql_client
+- role: ome.deploy_archive
+- role: ome.omero_server
+- role: ome.omero_web
+- role: ome.nginx
+- role: ome.redis
+- role: ome.selinux_utils
+- role: ome.versioncontrol_utils
+- role: ome.ssl_certificate
+- role: nfs_mount
+- role: nfs_share
+- role: iptables_raw
+- role: ome.cli_utils
+- role: ome.docker
+- role: ome.postgresql_backup
+- role: ome.omero_user
+- role: ome.lvm_partition
diff --git a/omero/roles/iptables_raw/.github/workflows/molecule.yml b/omero/roles/iptables_raw/.github/workflows/molecule.yml
new file mode 100644
index 00000000..0f92a0cb
--- /dev/null
+++ b/omero/roles/iptables_raw/.github/workflows/molecule.yml
@@ -0,0 +1,59 @@
+---
+name: Molecule
+# yamllint disable-line rule:truthy
+on:
+ push:
+ pull_request:
+ schedule:
+ - cron: '29 21 * * 0'
+
+jobs:
+
+ list-scenarios:
+ runs-on: ubuntu-22.04
+ outputs:
+ matrix: ${{ steps.listscenarios.outputs.scenarios }}
+ steps:
+ - uses: actions/checkout@v4
+ - id: listscenarios
+ uses: ome/action-ansible-molecule-list-scenarios@main
+
+ test:
+ name: Test
+ needs:
+ - list-scenarios
+ runs-on: ubuntu-22.04
+ strategy:
+ # Keep running so we can see if other tests pass
+ fail-fast: false
+ matrix:
+ scenario: ${{fromJson(needs.list-scenarios.outputs.matrix)}}
+ steps:
+ - uses: actions/checkout@v4
+ - uses: actions/setup-python@v4
+ with:
+ python-version: '3.8'
+ - name: Install Ansible & Molecule
+ run: |
+ pip install "ansible<8" "ansible-lint<6.13" flake8
+ pip install "molecule<5" "ansible-compat<4"
+ pip install molecule-plugins[docker] pytest-testinfra
+ - name: Run molecule
+ run: molecule test -s "${{ matrix.scenario }}"
+
+ publish:
+ name: Galaxy
+ if: startsWith(github.ref, 'refs/tags')
+ needs:
+ - test
+ runs-on: ubuntu-20.04
+ steps:
+ - name: galaxy
+ uses: robertdebock/galaxy-action@1.2.1
+ with:
+ galaxy_api_key: ${{ secrets.GALAXY_API_KEY }}
+ git_branch: ${{ github.ref_name }}
+
+
+# notifications:
+# webhooks: https://galaxy.ansible.com/api/v1/notifications/
diff --git a/omero/roles/iptables_raw/LICENSE.md b/omero/roles/iptables_raw/LICENSE.md
new file mode 100644
index 00000000..a3181283
--- /dev/null
+++ b/omero/roles/iptables_raw/LICENSE.md
@@ -0,0 +1,22 @@
+Copyright (c) 2019, University of Dundee
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/omero/roles/iptables_raw/README.md b/omero/roles/iptables_raw/README.md
new file mode 100644
index 00000000..12710939
--- /dev/null
+++ b/omero/roles/iptables_raw/README.md
@@ -0,0 +1,68 @@
+Iptables Raw
+============
+
+[![Actions Status](https://github.com/ome/ansible-role-iptables-raw/workflows/Molecule/badge.svg)](https://github.com/ome/ansible-role-iptables-raw/actions)
+[![Ansible Role](https://img.shields.io/badge/ansible--galaxy-iptables_raw-blue.svg)](https://galaxy.ansible.com/ui/standalone/roles/ome/iptables_raw/)
+
+Import the Iptables Raw library and make it available as a task.
+Ensure iptables is active.
+
+See these links for full documentation on the `iptables_raw` module:
+- https://nordeus.com/blog/engineering/managing-iptables-with-ansible-the-easy-way/
+- https://github.com/Nordeus/ansible_iptables_raw
+- https://github.com/ansible/ansible/pull/21054
+
+
+Parameters
+----------
+
+Optional:
+- `iptables_raw_disable_firewalld`: Disable the firewalld service (if installed and enabled it will conflict), default `True`
+
+
+Development
+-----------
+The [`library/iptables_raw.py`](library/iptables_raw.py) version is https://github.com/Nordeus/ansible_iptables_raw/tree/34672590224f393016ad086f82054319108e67ad (2018-02-18) with the following change to prevent ansible-lint/flake8 failing:
+
+```diff
+diff --git a/library/iptables_raw.py b/library/iptables_raw.py
+index 71dfc0d..978a6c7 100644
+--- a/library/iptables_raw.py
++++ b/library/iptables_raw.py
+@@ -344,7 +344,7 @@ class Iptables:
+ def _is_debian(self):
+ return os.path.isfile('/etc/debian_version')
+
+- # If /etc/arch-release exist, this means this is an ArchLinux OS
++ # If /etc/arch-release exist, this means this is an ArchLinux OS
+ def _is_arch_linux(self):
+ return os.path.isfile('/etc/arch-release')
+
+```
+
+
+Example Playbook
+----------------
+
+ - hosts: localhost
+ roles:
+ - role: ome.iptables-raw
+
+ tasks:
+ # Block all incoming connections apart from ssh
+ - ome.iptables_raw:
+ name: test_rules
+ keep_unmanaged: no
+ rules: |
+ -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
+ -A INPUT -p tcp -m tcp --dport 22 -j ACCEPT
+ -A INPUT -j REJECT
+ -A FORWARD -j REJECT
+ -A OUTPUT -j ACCEPT
+ state: present
+
+
+Author Information
+------------------
+
+ome-devel@lists.openmicroscopy.org.uk
diff --git a/omero/roles/iptables_raw/library/iptables_raw_25.py b/omero/roles/iptables_raw/library/iptables_raw_25.py
new file mode 100644
index 00000000..0162e5b2
--- /dev/null
+++ b/omero/roles/iptables_raw/library/iptables_raw_25.py
@@ -0,0 +1,1107 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# Make coding more python3-ish
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+
+"""
+(c) 2016, Strahinja Kustudic
+(c) 2016, Damir Markovic
+
+This file is part of Ansible
+
+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.
+
+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 Ansible. If not, see .
+"""
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible.module_utils.basic import json
+
+import time
+import fcntl
+import re
+import shlex
+import os
+import tempfile
+
+ANSIBLE_METADATA = {
+ 'metadata_version': '1.1',
+ 'status': ['preview'],
+ 'supported_by': 'community'
+}
+
+DOCUMENTATION = '''
+---
+module: iptables_raw
+short_description: Manage iptables rules
+version_added: "2.5"
+description:
+ - Add/remove iptables rules while keeping state.
+options:
+ backup:
+ description:
+ - Create a backup of the iptables state file before overwriting it.
+ required: false
+ choices: ["yes", "no"]
+ default: "no"
+ ipversion:
+ description:
+ - Target the IP version this rule is for.
+ required: false
+ default: "4"
+ choices: ["4", "6"]
+ keep_unmanaged:
+ description:
+ - If set to C(yes) keeps active iptables (unmanaged) rules for the target
+ C(table) and gives them C(weight=90). This means these rules will be
+ ordered after most of the rules, since default priority is 40, so they
+ shouldn't be able to block any allow rules. If set to C(no) deletes all
+ rules which are not set by this module.
+ - "WARNING: Be very careful when running C(keep_unmanaged=no) for the
+ first time, since if you don't specify correct rules, you can block
+ yourself out of the managed host."
+ required: false
+ choices: ["yes", "no"]
+ default: "yes"
+ name:
+ description:
+ - Name that will be used as an identifier for these rules. It can contain
+ alphanumeric characters, underscore, hyphen, dot, or a space; has to be
+ UNIQUE for a specified C(table). You can also pass C(name=*) with
+ C(state=absent) to flush all rules in the selected table, or even all
+ tables with C(table=*).
+ required: true
+ rules:
+ description:
+ - The rules that we want to add. Accepts multiline values.
+ - "Note: You can only use C(-A)/C(--append), C(-N)/C(--new-chain), and
+ C(-P)/C(--policy) to specify rules."
+ required: false
+ state:
+ description:
+ - The state this rules fragment should be in.
+ choices: ["present", "absent"]
+ required: false
+ default: present
+ table:
+ description:
+ - The table this rule applies to. You can specify C(table=*) only with
+ with C(name=*) and C(state=absent) to flush all rules in all tables.
+ choices: ["filter", "nat", "mangle", "raw", "security", "*"]
+ required: false
+ default: filter
+ weight:
+ description:
+ - Determines the order of the rules. Lower C(weight) means higher
+ priority. Supported range is C(0 - 99)
+ choices: ["0 - 99"]
+ required: false
+ default: 40
+notes:
+ - Requires C(iptables) package. Debian-based distributions additionally
+ require C(iptables-persistent).
+ - "Depending on the distribution, iptables rules are saved in different
+ locations, so that they can be loaded on boot. Red Hat distributions (RHEL,
+ CentOS, etc): C(/etc/sysconfig/iptables) and C(/etc/sysconfig/ip6tables);
+ Debian distributions (Debian, Ubuntu, etc): C(/etc/iptables/rules.v4) and
+ C(/etc/iptables/rules.v6); other distributions: C(/etc/sysconfig/iptables)
+ and C(/etc/sysconfig/ip6tables)."
+ - This module saves state in C(/etc/ansible-iptables) directory, so don't
+ modify this directory!
+author:
+ - "Strahinja Kustudic (@kustodian)"
+ - "Damir Markovic (@damirda)"
+'''
+
+EXAMPLES = '''
+# Allow all IPv4 traffic coming in on port 80 (http)
+- iptables_raw:
+ name: allow_tcp_80
+ rules: '-A INPUT -p tcp -m tcp --dport 80 -j ACCEPT'
+
+# Set default rules with weight 10 and disregard all unmanaged rules
+- iptables_raw:
+ name: default_rules
+ weight: 10
+ keep_unmanaged: no
+ rules: |
+ -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
+ -A INPUT -i lo -j ACCEPT
+ -A INPUT -p tcp -m state --state NEW -m tcp --dport 22 -j ACCEPT
+ -P INPUT DROP
+ -P FORWARD DROP
+ -P OUTPUT ACCEPT
+
+# Allow all IPv6 traffic coming in on port 443 (https) with weight 50
+- iptables_raw:
+ ipversion: 6
+ weight: 50
+ name: allow_tcp_443
+ rules: '-A INPUT -p tcp -m tcp --dport 443 -j ACCEPT'
+
+# Remove the above rule
+- iptables_raw:
+ state: absent
+ ipversion: 6
+ name: allow_tcp_443
+
+# Define rules with a custom chain
+- iptables_raw:
+ name: custom1_rules
+ rules: |
+ -N CUSTOM1
+ -A CUSTOM1 -s 192.168.0.0/24 -j ACCEPT
+
+# Reset all IPv4 iptables rules in all tables and allow all traffic
+- iptables_raw:
+ name: '*'
+ table: '*'
+ state: absent
+'''
+
+RETURN = '''
+state:
+ description: state of the rules
+ returned: success
+ type: string
+ sample: present
+name:
+ description: name of the rules
+ returned: success
+ type: string
+ sample: open_tcp_80
+weight:
+ description: weight of the rules
+ returned: success
+ type: int
+ sample: 40
+ipversion:
+ description: IP version of iptables used
+ returned: success
+ type: int
+ sample: 6
+rules:
+ description: passed rules
+ returned: success
+ type: string
+ sample: "-A INPUT -p tcp -m tcp --dport 80 -j ACCEPT"
+table:
+ description: iptables table used
+ returned: success
+ type: string
+ sample: filter
+backup:
+ description: if the iptables file should backed up
+ returned: success
+ type: boolean
+ sample: False
+keep_unmanaged:
+ description: if it should keep unmanaged rules
+ returned: success
+ type: boolean
+ sample: True
+'''
+
+try:
+ from collections import defaultdict
+except ImportError:
+ # This is a workaround for Python 2.4 which doesn't have defaultdict.
+ class defaultdict(dict):
+ def __init__(self, default_factory, *args, **kwargs):
+ super(defaultdict, self).__init__(*args, **kwargs)
+ self.default_factory = default_factory
+
+ def __getitem__(self, key):
+ try:
+ return super(defaultdict, self).__getitem__(key)
+ except KeyError:
+ return self.__missing__(key)
+
+ def __missing__(self, key):
+ try:
+ self[key] = self.default_factory()
+ except TypeError:
+ raise KeyError("Missing key %s" % (key, ))
+ else:
+ return self[key]
+
+
+# Genereates a diff dictionary from an old and new table dump.
+def generate_diff(dump_old, dump_new):
+ diff = dict()
+ if dump_old != dump_new:
+ diff['before'] = dump_old
+ diff['after'] = dump_new
+ return diff
+
+
+def compare_dictionaries(dict1, dict2):
+ if dict1 is None or dict2 is None:
+ return False
+ if not (isinstance(dict1, dict) and isinstance(dict2, dict)):
+ return False
+ shared_keys = set(dict2.keys()) & set(dict2.keys())
+ if not (len(shared_keys) == len(dict1.keys()) and len(shared_keys) == len(dict2.keys())): # noqa
+ return False
+ dicts_are_equal = True
+ for key in dict1.keys():
+ if isinstance(dict1[key], dict):
+ dicts_are_equal = dicts_are_equal and compare_dictionaries(dict1[key], dict2[key]) # noqa
+ else:
+ dicts_are_equal = dicts_are_equal and (dict1[key] == dict2[key])
+ if not dicts_are_equal:
+ break
+ return dicts_are_equal
+
+
+class Iptables:
+
+ # Default chains for each table
+ DEFAULT_CHAINS = {
+ 'filter': ['INPUT', 'FORWARD', 'OUTPUT'],
+ 'raw': ['PREROUTING', 'OUTPUT'],
+ 'nat': ['PREROUTING', 'INPUT', 'OUTPUT', 'POSTROUTING'],
+ 'mangle': ['PREROUTING', 'INPUT', 'FORWARD', 'OUTPUT', 'POSTROUTING'],
+ 'security': ['INPUT', 'FORWARD', 'OUTPUT']
+ }
+
+ # List of tables
+ TABLES = list(DEFAULT_CHAINS.copy().keys())
+
+ # Directory which will store the state file.
+ STATE_DIR = '/etc/ansible-iptables'
+
+ # Key used for unmanaged rules
+ UNMANAGED_RULES_KEY_NAME = '$unmanaged_rules$'
+
+ # Only allow alphanumeric characters, underscore, hyphen, dots,
+ # or a space for
+ # now. We don't want to have problems while parsing comments using regular
+ # expressions.
+ RULE_NAME_ALLOWED_CHARS = 'a-zA-Z0-9_ .-'
+
+ module = None
+
+ def __init__(self, module, ipversion):
+ # Create directory for json files.
+ if not os.path.exists(self.STATE_DIR):
+ os.makedirs(self.STATE_DIR)
+ if Iptables.module is None:
+ Iptables.module = module
+ self.state_save_path = self._get_state_save_path(ipversion)
+ self.system_save_path = self._get_system_save_path(ipversion)
+ self.state_dict = self._read_state_file()
+ self.bins = self._get_bins(ipversion)
+ self.iptables_names_file = self._get_iptables_names_file(ipversion)
+ # Check if we have a required iptables version.
+ self._check_compatibility()
+ # Save active iptables rules for all tables, so that we don't
+ # need to fetch them every time using 'iptables-save' command.
+ self._active_rules = {}
+ self._refresh_active_rules(table='*')
+
+ def __eq__(self, other):
+ return (isinstance(other, self.__class__) and compare_dictionaries(other.state_dict, self.state_dict)) # noqa
+
+ def __ne__(self, other):
+ return not self.__eq__(other)
+
+ def _get_bins(self, ipversion):
+ if ipversion == '4':
+ return {'iptables': Iptables.module.get_bin_path('iptables'),
+ 'iptables-save': Iptables.module.get_bin_path('iptables-save'), # noqa
+ 'iptables-restore': Iptables.module.get_bin_path('iptables-restore')} # noqa
+ else:
+ return {'iptables': Iptables.module.get_bin_path('ip6tables'),
+ 'iptables-save': Iptables.module.get_bin_path('ip6tables-save'), # noqa
+ 'iptables-restore': Iptables.module.get_bin_path('ip6tables-restore')} # noqa
+
+ def _get_iptables_names_file(self, ipversion):
+ if ipversion == '4':
+ return '/proc/net/ip_tables_names'
+ else:
+ return '/proc/net/ip6_tables_names'
+
+ # Return a list of active iptables tables
+ def _get_list_of_active_tables(self):
+ if os.path.isfile(self.iptables_names_file):
+ table_names = open(self.iptables_names_file, 'r').read()
+ return table_names.splitlines()
+ else:
+ return []
+
+ # If /etc/debian_version exist, this means this is a
+ # debian based OS (Ubuntu, Mint, etc...)
+ def _is_debian(self):
+ return os.path.isfile('/etc/debian_version')
+
+ # If /etc/arch-release exist, this means this is an ArchLinux OS
+ def _is_arch_linux(self):
+ return os.path.isfile('/etc/arch-release')
+
+ # If /etc/gentoo-release exist, this means this is Gentoo
+ def _is_gentoo(self):
+ return os.path.isfile('/etc/gentoo-release')
+
+ # Get the iptables system save path.
+ # Supports RHEL/CentOS '/etc/sysconfig/' location.
+ # Supports Debian/Ubuntu/Mint, '/etc/iptables/' location.
+ # Supports Gentoo, '/var/lib/iptables/' location.
+ def _get_system_save_path(self, ipversion):
+ # distro detection, path setting should be added
+ if self._is_debian():
+ # Check if iptables-persistent packages is installed
+ if not os.path.isdir('/etc/iptables'):
+ Iptables.module.fail_json(msg="This module requires 'iptables-persistent' package!") # noqa
+ if ipversion == '4':
+ return '/etc/iptables/rules.v4'
+ else:
+ return '/etc/iptables/rules.v6'
+ elif self._is_arch_linux():
+ if ipversion == '4':
+ return '/etc/iptables/iptables.rules'
+ else:
+ return '/etc/iptables/ip6tables.rules'
+ elif self._is_gentoo():
+ if ipversion == '4':
+ return '/var/lib/iptables/rules-save'
+ else:
+ return '/var/lib/ip6tables/rules-save'
+ else:
+ if ipversion == '4':
+ return '/etc/sysconfig/iptables'
+ else:
+ return '/etc/sysconfig/ip6tables'
+
+ # Return path to json state file.
+ def _get_state_save_path(self, ipversion):
+ if ipversion == '4':
+ return self.STATE_DIR + '/iptables.json'
+ else:
+ return self.STATE_DIR + '/ip6tables.json'
+
+ # Checks if iptables is installed and if we have a correct version.
+ def _check_compatibility(self):
+ from distutils.version import StrictVersion
+ cmd = [self.bins['iptables'], '--version']
+ rc, stdout, stderr = Iptables.module.run_command(cmd, check_rc=False)
+ if rc == 0:
+ result = re.search(r'^ip6tables\s+v(\d+\.\d+)\.\d+$', stdout)
+ if result:
+ version = result.group(1)
+ # CentOS 5 ip6tables (v1.3.x) doesn't support comments,
+ # which means it cannot be used with this module.
+ if StrictVersion(version) < StrictVersion('1.4'):
+ Iptables.module.fail_json(msg="This module isn't compatible with ip6tables versions older than 1.4.x") # noqa
+ else:
+ Iptables.module.fail_json(msg="Could not fetch iptables version! Is iptables installed?") # noqa
+
+ # Read rules from the json state file and return a dict.
+ def _read_state_file(self):
+ json_str = '{}'
+ if os.path.isfile(self.state_save_path):
+ try:
+ json_str = open(self.state_save_path, 'r').read()
+ except: # noqa
+ Iptables.module.fail_json(msg="Could not read the state file '%s'!" % self.state_save_path) # noqa
+ try:
+ read_dict = defaultdict(lambda: dict(dump='', rules_dict={}), json.loads(json_str)) # noqa
+ except: # noqa
+ Iptables.module.fail_json(msg="Could not parse the state file '%s'! Please manually delete it to continue." % self.state_save_path) # noqa
+ return read_dict
+
+ # Checks if a table exists in the state_dict.
+ def _has_table(self, tbl):
+ return tbl in self.state_dict
+
+ # Deletes table from the state_dict.
+ def _delete_table(self, tbl):
+ if self._has_table(tbl):
+ del self.state_dict[tbl]
+
+ # Acquires lock or exits after wait_for_seconds if it cannot be acquired.
+ def acquire_lock_or_exit(self, wait_for_seconds=10):
+ lock_file = self.STATE_DIR + '/.iptables.lock'
+ i = 0
+ f = open(lock_file, 'w+')
+ while i < wait_for_seconds:
+ try:
+ fcntl.flock(f, fcntl.LOCK_EX | fcntl.LOCK_NB)
+ return
+ except IOError:
+ i += 1
+ time.sleep(1)
+ Iptables.module.fail_json(msg="Could not acquire lock to "
+ "continue execution! "
+ "Probably another instance "
+ "of this module is running.")
+
+ # Check if a table has anything to flush
+ # (to check all tables pass table='*').
+ def table_needs_flush(self, table):
+ needs_flush = False
+ if table == '*':
+ for tbl in Iptables.TABLES:
+ # If the table exists or if it needs
+ # to be flushed that means will make changes.
+ if self._has_table(tbl) or self._single_table_needs_flush(tbl): # noqa
+ needs_flush = True
+ break
+ # Only flush the specified table
+ else:
+ if self._has_table(table) or self._single_table_needs_flush(table): # noqa
+ needs_flush = True
+ return needs_flush
+
+ # Check if a passed table needs to be flushed.
+ def _single_table_needs_flush(self, table):
+ needs_flush = False
+ active_rules = self._get_active_rules(table)
+ if active_rules:
+ policies = self._filter_default_chain_policies(active_rules, table) # noqa
+ chains = self._filter_custom_chains(active_rules, table)
+ rules = self._filter_rules(active_rules, table)
+ # Go over default policies and check if they are all ACCEPT.
+ for line in policies.splitlines():
+ if not re.search(r'\bACCEPT\b', line):
+ needs_flush = True
+ break
+ # If there is at least one rule or custom chain,
+ # that means we need flush.
+ if len(chains) > 0 or len(rules) > 0:
+ needs_flush = True
+ return needs_flush
+
+ # Returns a copy of the rules dict of a passed table.
+ def _get_table_rules_dict(self, table):
+ return self.state_dict[table]['rules_dict'].copy()
+
+ # Returns saved table dump.
+ def get_saved_table_dump(self, table):
+ return self.state_dict[table]['dump']
+
+ # Sets saved table dump.
+ def _set_saved_table_dump(self, table, dump):
+ self.state_dict[table]['dump'] = dump
+
+ # Updates saved table dump from the active rules.
+ def refresh_saved_table_dump(self, table):
+ active_rules = self._get_active_rules(table)
+ self._set_saved_table_dump(table, active_rules)
+
+ # Sets active rules of the passed table.
+ def _set_active_rules(self, table, rules):
+ self._active_rules[table] = rules
+
+ # Return active rules of the passed table.
+ def _get_active_rules(self, table, clean=True):
+ active_rules = ''
+ if table == '*':
+ all_rules = []
+ for tbl in Iptables.TABLES:
+ if tbl in self._active_rules:
+ all_rules.append(self._active_rules[tbl])
+ active_rules = '\n'.join(all_rules)
+ else:
+ active_rules = self._active_rules[table]
+ if clean:
+ return self._clean_save_dump(active_rules)
+ else:
+ return active_rules
+
+ # Refresh active rules of a table ('*' for all tables).
+ def _refresh_active_rules(self, table):
+ if table == '*':
+ for tbl in Iptables.TABLES:
+ self._set_active_rules(tbl, self._get_system_active_rules(tbl)) # noqa
+ else:
+ self._set_active_rules(table, self._get_system_active_rules(table)) # noqa
+
+ # Get iptables-save dump of active rules of
+ # one or all tables (pass '*') and return it as a string.
+ def _get_system_active_rules(self, table):
+ active_tables = self._get_list_of_active_tables()
+ if table == '*':
+ cmd = [self.bins['iptables-save']]
+ # If there are no active tables, that means there are no rules
+ if not active_tables:
+ return ""
+ else:
+ cmd = [self.bins['iptables-save'], '-t', table]
+ # If the table is not active, that means it has no rules
+ if table not in active_tables:
+ return ""
+ rc, stdout, stderr = Iptables.module.run_command(cmd, check_rc=True)
+ return stdout
+
+ # Splits a rule into tokens
+ def _split_rule_into_tokens(self, rule):
+ try:
+ return shlex.split(rule, comments=True)
+ except: # noqa
+ msg = "Could not parse the iptables rule:\n%s" % rule
+ Iptables.module.fail_json(msg=msg)
+
+ # Removes comment lines and empty lines from rules.
+ @staticmethod
+ def clean_up_rules(rules):
+ cleaned_rules = []
+ for line in rules.splitlines():
+ # Remove lines with comments and empty lines.
+ if not (Iptables.is_comment(line) or Iptables.is_empty_line(line)): # noqa
+ cleaned_rules.append(line)
+ return '\n'.join(cleaned_rules)
+
+ # Checks if the line is a custom chain in specific iptables table.
+ @staticmethod
+ def is_custom_chain(line, table):
+ default_chains = Iptables.DEFAULT_CHAINS[table]
+ if re.match(r'\s*(:|(-N|--new-chain)\s+)[^\s]+', line) \
+ and not re.match(r'\s*(:|(-N|--new-chain)\s+)\b(' + '|'.join(default_chains) + r')\b', line): # noqa
+ return True
+ else:
+ return False
+
+ # Checks if the line is a default chain of an iptables table.
+ @staticmethod
+ def is_default_chain(line, table):
+ default_chains = Iptables.DEFAULT_CHAINS[table]
+ if re.match(r'\s*(:|(-P|--policy)\s+)\b(' + '|'.join(default_chains) + r')\b\s+(ACCEPT|DROP)', line): # noqa
+ return True
+ else:
+ return False
+
+ # Checks if a line is an iptables rule.
+ @staticmethod
+ def is_rule(line):
+ # We should only allow adding rules with '-A/--append',
+ # since others don't make any sense.
+ if re.match(r'\s*(-A|--append)\s+[^\s]+', line):
+ return True
+ else:
+ return False
+
+ # Checks if a line starts with '#'.
+ @staticmethod
+ def is_comment(line):
+ if re.match(r'\s*#', line):
+ return True
+ else:
+ return False
+
+ # Checks if a line is empty.
+ @staticmethod
+ def is_empty_line(line):
+ if re.match(r'^$', line.strip()):
+ return True
+ else:
+ return False
+
+ # Return name of custom chain from the rule.
+ def _get_custom_chain_name(self, line, table):
+ if Iptables.is_custom_chain(line, table):
+ return re.match(r'\s*(:|(-N|--new-chain)\s+)([^\s]+)', line).group(3) # noqa
+ else:
+ return ''
+
+ # Return name of default chain from the rule.
+ def _get_default_chain_name(self, line, table):
+ if Iptables.is_default_chain(line, table):
+ return re.match(r'\s*(:|(-N|--new-chain)\s+)([^\s]+)', line).group(3) # noqa
+ else:
+ return ''
+
+ # Return target of the default chain from the rule.
+ def _get_default_chain_target(self, line, table):
+ if Iptables.is_default_chain(line, table):
+ return re.match(r'\s*(:|(-N|--new-chain)\s+)([^\s]+)\s+([A-Z]+)', line).group(4) # noqa
+ else:
+ return ''
+
+ # Removes duplicate custom chains from the table rules.
+ def _remove_duplicate_custom_chains(self, rules, table):
+ all_rules = []
+ custom_chain_names = []
+ for line in rules.splitlines():
+ # Extract custom chains.
+ if Iptables.is_custom_chain(line, table):
+ chain_name = self._get_custom_chain_name(line, table)
+ if chain_name not in custom_chain_names:
+ custom_chain_names.append(chain_name)
+ all_rules.append(line)
+ else:
+ all_rules.append(line)
+ return '\n'.join(all_rules)
+
+ # Returns current iptables-save dump cleaned
+ # from comments and packet/byte counters.
+ def _clean_save_dump(self, simple_rules):
+ cleaned_dump = []
+ for line in simple_rules.splitlines():
+ # Ignore comments.
+ if Iptables.is_comment(line):
+ continue
+ # Reset counters for chains (begin with ':'),
+ # for easier comparing later on.
+ if re.match(r'\s*:', line):
+ cleaned_dump.append(re.sub(r'\[([0-9]+):([0-9]+)\]', '[0:0]', line)) # noqa
+ else:
+ cleaned_dump.append(line)
+ cleaned_dump.append('\n')
+ return '\n'.join(cleaned_dump)
+
+ # Returns lines with default chain policies.
+ def _filter_default_chain_policies(self, rules, table):
+ chains = []
+ for line in rules.splitlines():
+ if Iptables.is_default_chain(line, table):
+ chains.append(line)
+ return '\n'.join(chains)
+
+ # Returns lines with iptables rules from an iptables-save table dump
+ # (removes chain policies, custom chains, comments and everything else).
+ # By default returns all rules, if 'only_unmanged=True'
+ # returns rules which are not managed by Ansible.
+ def _filter_rules(self, rules, table, only_unmanaged=False):
+ filtered_rules = []
+ for line in rules.splitlines():
+ if Iptables.is_rule(line):
+ if only_unmanaged:
+ tokens = self._split_rule_into_tokens(line)
+ # We need to check if a rule has a comment
+ # which starts with 'ansible[name]'
+ if '--comment' in tokens:
+ comment_index = tokens.index('--comment') + 1
+ if comment_index < len(tokens):
+ # Fetch the comment
+ comment = tokens[comment_index]
+ # Skip the rule if the comment
+ # starts with 'ansible[name]'
+ if not re.match(r'ansible\[[' + Iptables.RULE_NAME_ALLOWED_CHARS + r']+\]', comment): # noqa
+ filtered_rules.append(line)
+ else:
+ # Fail if there is no comment after
+ # the --comment parameter
+ msg = "Iptables rule is missing a comment after the '--comment' parameter:\n%s" % line # noqa
+ Iptables.module.fail_json(msg=msg)
+ # If it doesn't have comment, this means
+ # it is not managed by Ansible and we should append it.
+ else:
+ filtered_rules.append(line)
+ else:
+ filtered_rules.append(line)
+ return '\n'.join(filtered_rules)
+
+ # Same as _filter_rules(), but returns custom chains
+ def _filter_custom_chains(self, rules, table, only_unmanaged=False):
+ filtered_chains = []
+ # Get list of managed custom chains,
+ # which is needed to detect unmanaged custom chains
+ managed_custom_chains_list = self._get_custom_chains_list(table)
+ for line in rules.splitlines():
+ if Iptables.is_custom_chain(line, table):
+ if only_unmanaged:
+ # The chain is not managed by this module
+ # if it's not in the list of managed custom chains.
+ chain_name = self._get_custom_chain_name(line, table)
+ if chain_name not in managed_custom_chains_list:
+ filtered_chains.append(line)
+ else:
+ filtered_chains.append(line)
+ return '\n'.join(filtered_chains)
+
+ # Returns list of custom chains of a table.
+ def _get_custom_chains_list(self, table):
+ custom_chains_list = []
+ for key, value in self._get_table_rules_dict(table).items():
+ # Ignore UNMANAGED_RULES_KEY_NAME key, since
+ # we only want managed custom chains.
+ if key != Iptables.UNMANAGED_RULES_KEY_NAME:
+ for line in value['rules'].splitlines():
+ if Iptables.is_custom_chain(line, table):
+ chain_name = self._get_custom_chain_name(line, table)
+ if chain_name not in custom_chains_list:
+ custom_chains_list.append(chain_name)
+ return custom_chains_list
+
+ # Prepends 'ansible[name]: ' to iptables rule '--comment' argument,
+ # or adds 'ansible[name]' as a comment if there is no comment.
+ def _prepend_ansible_comment(self, rules, name):
+ commented_lines = []
+ for line in rules.splitlines():
+ # Extract rules only since we cannot
+ # add comments to custom chains.
+ if Iptables.is_rule(line):
+ tokens = self._split_rule_into_tokens(line)
+ if '--comment' in tokens:
+ # If there is a comment parameter,
+ # we need to prepand 'ansible[name]: '.
+ comment_index = tokens.index('--comment') + 1
+ if comment_index < len(tokens):
+ # We need to remove double quotes
+ # from comments, since there
+ # is an incompatiblity with older iptables versions
+ comment_text = tokens[comment_index].replace('"', '')
+ tokens[comment_index] = 'ansible[' + name + ']: ' + comment_text # noqa
+ else:
+ # Fail if there is no comment
+ # after the --comment parameter
+ msg = "Iptables rule is missing a comment after the '--comment' parameter:\n%s" % line # noqa
+ Iptables.module.fail_json(msg=msg)
+ else:
+ # If comment doesn't exist, we
+ # add a comment 'ansible[name]'
+ tokens += ['-m', 'comment', '--comment', 'ansible[' + name + ']'] # noqa
+ # Escape and quote tokens in case they have spaces
+ tokens = [self._escape_and_quote_string(x) for x in tokens]
+ commented_lines.append(" ".join(tokens))
+ # Otherwise it's a chain, and we should just return it.
+ else:
+ commented_lines.append(line)
+ return '\n'.join(commented_lines)
+
+ # Double quote a string if it contains a space and escape double quotes.
+ def _escape_and_quote_string(self, s):
+ escaped = s.replace('"', r'\"')
+ if re.search(r'\s', escaped):
+ return '"' + escaped + '"'
+ else:
+ return escaped
+
+ # Add table rule to the state_dict.
+ def add_table_rule(self, table, name, weight, rules, prepend_ansible_comment=True): # noqa
+ self._fail_on_bad_rules(rules, table)
+ if prepend_ansible_comment:
+ self.state_dict[table]['rules_dict'][name] = {'weight': weight, 'rules': self._prepend_ansible_comment(rules, name)} # noqa
+ else:
+ self.state_dict[table]['rules_dict'][name] = {'weight': weight, 'rules': rules} # noqa
+
+ # Remove table rule from the state_dict.
+ def remove_table_rule(self, table, name):
+ if name in self.state_dict[table]['rules_dict']:
+ del self.state_dict[table]['rules_dict'][name]
+
+ # TODO: Add sorting of rules so that diffs in check_
+ # mode look nicer and easier to follow.
+ # Sorting would be done from top to bottom like this:
+ # * default chain policies
+ # * custom chains
+ # * rules
+ #
+ # Converts rules from a state_dict to an iptables-save readable format.
+ def get_table_rules(self, table):
+ generated_rules = ''
+ # We first add a header e.g. '*filter'.
+ generated_rules += '*' + table + '\n'
+ rules_list = []
+ custom_chains_list = []
+ default_chain_policies = []
+ dict_rules = self._get_table_rules_dict(table)
+ # Return list of rule names sorted by ('weight', 'rules') tuple.
+ for rule_name in sorted(dict_rules, key=lambda x: (dict_rules[x]['weight'], dict_rules[x]['rules'])): # noqa
+ rules = dict_rules[rule_name]['rules']
+ # Fail if some of the rules are bad
+ self._fail_on_bad_rules(rules, table)
+ rules_list.append(self._filter_rules(rules, table))
+ custom_chains_list.append(self._filter_custom_chains(rules, table)) # noqa
+ default_chain_policies.append(self._filter_default_chain_policies(rules, table)) # noqa
+ # Clean up empty strings from these two lists.
+ rules_list = list(filter(None, rules_list))
+ custom_chains_list = list(filter(None, custom_chains_list))
+ default_chain_policies = list(filter(None, default_chain_policies))
+ if default_chain_policies:
+ # Since iptables-restore applies
+ # the last chain policy it reads, we
+ # have to reverse the order of chain
+ # policies so that those with
+ # the lowest weight (higher priority) are read last.
+ generated_rules += '\n'.join(reversed(default_chain_policies)) + '\n' # noqa
+ if custom_chains_list:
+ # We remove duplicate custom chains so that iptables-restore
+ # doesn't fail because of that.
+ generated_rules += self._remove_duplicate_custom_chains('\n'.join(sorted(custom_chains_list)), table) + '\n' # noqa
+ if rules_list:
+ generated_rules += '\n'.join(rules_list) + '\n'
+ generated_rules += 'COMMIT\n'
+ return generated_rules
+
+ # Sets unmanaged rules for the passed table in the state_dict.
+ def _set_unmanaged_rules(self, table, rules):
+ self.add_table_rule(table, Iptables.UNMANAGED_RULES_KEY_NAME, 90, rules, prepend_ansible_comment=False) # noqa
+
+ # Clears unmanaged rules of a table.
+ def clear_unmanaged_rules(self, table):
+ self._set_unmanaged_rules(table, '')
+
+ # Updates unmanaged rules of a table from the active rules.
+ def refresh_unmanaged_rules(self, table):
+ # Get active iptables rules and clean them up.
+ active_rules = self._get_active_rules(table)
+ unmanaged_chains_and_rules = []
+ unmanaged_chains_and_rules.append(self._filter_custom_chains(active_rules, table, only_unmanaged=True)) # noqa
+ unmanaged_chains_and_rules.append(self._filter_rules(active_rules, table, only_unmanaged=True)) # noqa
+ # Clean items which are empty strings
+ unmanaged_chains_and_rules = list(filter(None, unmanaged_chains_and_rules)) # noqa
+ self._set_unmanaged_rules(table, '\n'.join(unmanaged_chains_and_rules)) # noqa
+
+ # Check if there are bad lines in the specified rules.
+ def _fail_on_bad_rules(self, rules, table):
+ for line in rules.splitlines():
+ tokens = self._split_rule_into_tokens(line)
+ if '-t' in tokens or '--table' in tokens:
+ msg = ("Iptables rules cannot contain '-t/--table' parameter. " # noqa
+ "You should use the 'table' parameter of the module to set rules " # noqa
+ "for a specific table.")
+ Iptables.module.fail_json(msg=msg)
+ # Fail if the parameter --comment doesn't have a comment after
+ if '--comment' in tokens and len(tokens) <= tokens.index('--comment') + 1: # noqa
+ msg = "Iptables rule is missing a comment after the '--comment' parameter:\n%s" % line # noqa
+ Iptables.module.fail_json(msg=msg)
+ if not (Iptables.is_rule(line) or
+ Iptables.is_custom_chain(line, table) or
+ Iptables.is_default_chain(line, table) or
+ Iptables.is_comment(line)):
+ msg = ("Bad iptables rule '%s'! You can only use -A/--append, -N/--new-chain " # noqa
+ "and -P/--policy to specify rules." % line)
+ Iptables.module.fail_json(msg=msg)
+
+ # Write rules to dest path.
+ def _write_rules_to_file(self, rules, dest):
+ tmp_path = self._write_to_temp_file(rules)
+ Iptables.module.atomic_move(tmp_path, dest)
+
+ # Write text to a temp file and return path to that file.
+ def _write_to_temp_file(self, text):
+ fd, path = tempfile.mkstemp()
+ Iptables.module.add_cleanup_file(path) # add file for cleanup later
+ tmp = os.fdopen(fd, 'w')
+ tmp.write(text)
+ tmp.close()
+ return path
+
+ #
+ # Public and private methods which make changes on the system
+ # are named 'system_*' and '_system_*', respectively.
+ #
+
+ # Flush all rules in a passed table.
+ def _system_flush_single_table_rules(self, table):
+ # Set all default chain policies to ACCEPT.
+ for chain in Iptables.DEFAULT_CHAINS[table]:
+ cmd = [self.bins['iptables'], '-t', table, '-P', chain, 'ACCEPT']
+ Iptables.module.run_command(cmd, check_rc=True)
+ # Then flush all rules.
+ cmd = [self.bins['iptables'], '-t', table, '-F']
+ Iptables.module.run_command(cmd, check_rc=True)
+ # And delete custom chains.
+ cmd = [self.bins['iptables'], '-t', table, '-X']
+ Iptables.module.run_command(cmd, check_rc=True)
+ # Update active rules in the object.
+ self._refresh_active_rules(table)
+
+ # Save active iptables rules to the system path.
+ def _system_save_active(self, backup=False):
+ # Backup if needed
+ if backup:
+ Iptables.module.backup_local(self.system_save_path)
+ # Get iptables-save dump of all tables
+ all_active_rules = self._get_active_rules(table='*', clean=False)
+ # Move iptables-save dump of all tables to the iptables_save_path
+ self._write_rules_to_file(all_active_rules, self.system_save_path)
+
+ # Apply table dict rules to the system.
+ def system_apply_table_rules(self, table, test=False):
+ dump_path = self._write_to_temp_file(self.get_table_rules(table))
+ if test:
+ cmd = [self.bins['iptables-restore'], '-t', dump_path]
+ else:
+ cmd = [self.bins['iptables-restore'], dump_path]
+ rc, stdout, stderr = Iptables.module.run_command(cmd, check_rc=False)
+ if rc != 0:
+ if test:
+ dump_contents_file = open(dump_path, 'r')
+ dump_contents = dump_contents_file.read()
+ dump_contents_file.close()
+ msg = "There is a problem with the iptables rules:" \
+ + '\n\nError message:\n' \
+ + stderr \
+ + '\nGenerated rules:\n#######\n' \
+ + dump_contents + '#####'
+ else:
+ msg = "Could not load iptables rules:\n\n" + stderr
+ Iptables.module.fail_json(msg=msg)
+ self._refresh_active_rules(table)
+
+ # Flush one or all tables (to flush all tables pass table='*').
+ def system_flush_table_rules(self, table):
+ if table == '*':
+ for tbl in Iptables.TABLES:
+ self._delete_table(tbl)
+ if self._single_table_needs_flush(tbl):
+ self._system_flush_single_table_rules(tbl)
+ # Only flush the specified table.
+ else:
+ self._delete_table(table)
+ if self._single_table_needs_flush(table):
+ self._system_flush_single_table_rules(table)
+
+ # Saves state file and system iptables rules.
+ def system_save(self, backup=False):
+ self._system_save_active(backup=backup)
+ rules = json.dumps(self.state_dict, sort_keys=True, indent=4, separators=(',', ': ')) # noqa
+ self._write_rules_to_file(rules, self.state_save_path)
+
+
+def main():
+
+ module = AnsibleModule(
+ argument_spec=dict(
+ ipversion=dict(required=False, choices=["4", "6"], type='str', default="4"), # noqa
+ state=dict(required=False, choices=['present', 'absent'], default='present', type='str'), # noqa
+ weight=dict(required=False, type='int', default=40),
+ name=dict(required=True, type='str'),
+ table=dict(required=False, choices=Iptables.TABLES + ['*'], default="filter", type='str'), # noqa
+ rules=dict(required=False, type='str', default=""),
+ backup=dict(required=False, type='bool', default=False),
+ keep_unmanaged=dict(required=False, type='bool', default=True),
+ ),
+ supports_check_mode=True,
+ )
+
+ check_mode = module.check_mode
+ changed = False
+ ipversion = module.params['ipversion']
+ state = module.params['state']
+ weight = module.params['weight']
+ name = module.params['name']
+ table = module.params['table']
+ rules = module.params['rules']
+ backup = module.params['backup']
+ keep_unmanaged = module.params['keep_unmanaged']
+
+ kw = dict(state=state, name=name, rules=rules, weight=weight, ipversion=ipversion, # noqa
+ table=table, backup=backup, keep_unmanaged=keep_unmanaged)
+
+ iptables = Iptables(module, ipversion)
+
+ # Acquire lock so that only one instance of this object can exist.
+ # Fail if the lock cannot be acquired within 10 seconds.
+ iptables.acquire_lock_or_exit(wait_for_seconds=10)
+
+ # Clean up rules of comments and empty lines.
+ rules = Iptables.clean_up_rules(rules)
+
+ # Check additional parameter requirements
+ if state == 'present' and name == '*':
+ module.fail_json(msg="Parameter 'name' can only be '*' if 'state=absent'") # noqa
+ if state == 'present' and table == '*':
+ module.fail_json(msg="Parameter 'table' can only be '*' if 'name=*' and 'state=absent'") # noqa
+ if state == 'present' and not name:
+ module.fail_json(msg="Parameter 'name' cannot be empty")
+ if state == 'present' and not re.match('^[' + Iptables.RULE_NAME_ALLOWED_CHARS + ']+$', name): # noqa
+ module.fail_json(msg="Parameter 'name' not valid! It can only contain alphanumeric characters, " # noqa
+ "underscore, hyphen, or a space, got: '%s'" % name) # noqa
+ if weight < 0 or weight > 99:
+ module.fail_json(msg="Parameter 'weight' can be 0-99, got: %d" % weight) # noqa
+ if state == 'present' and rules == '':
+ module.fail_json(msg="Parameter 'rules' cannot be empty when 'state=present'") # noqa
+
+ # Flush rules of one or all tables
+ if state == 'absent' and name == '*':
+ # Check if table(s) need to be flushed
+ if iptables.table_needs_flush(table):
+ changed = True
+ if not check_mode:
+ # Flush table(s)
+ iptables.system_flush_table_rules(table)
+ # Save state and system iptables rules
+ iptables.system_save(backup=backup)
+ # Exit since there is nothing else to do
+ kw['changed'] = changed
+ module.exit_json(**kw)
+
+ # Initialize new iptables object which will store new rules
+ iptables_new = Iptables(module, ipversion)
+
+ if state == 'present':
+ iptables_new.add_table_rule(table, name, weight, rules)
+ else:
+ iptables_new.remove_table_rule(table, name)
+
+ if keep_unmanaged:
+ iptables_new.refresh_unmanaged_rules(table)
+ else:
+ iptables_new.clear_unmanaged_rules(table)
+
+ # Refresh saved table dump with active iptables rules
+ iptables_new.refresh_saved_table_dump(table)
+
+ # Check if there are changes in iptables, and if yes load new rules
+ if iptables != iptables_new:
+
+ changed = True
+
+ # Test generated rules
+ iptables_new.system_apply_table_rules(table, test=True)
+
+ if check_mode:
+ # Create a predicted diff for check_mode.
+ # Diff will be created from rules generated
+ # from the state dictionary.
+ if hasattr(module, '_diff') and module._diff:
+ # Update unmanaged rules in the old
+ # object so the generated diff
+ # from the rules dictionaries is more accurate.
+ iptables.refresh_unmanaged_rules(table)
+ # Generate table rules from rules dictionaries.
+ table_rules_old = iptables.get_table_rules(table)
+ table_rules_new = iptables_new.get_table_rules(table)
+ # If rules generated from dicts are not equal
+ # we generate a diff from them.
+ if table_rules_old != table_rules_new:
+ kw['diff'] = generate_diff(table_rules_old, table_rules_new) # noqa
+ else:
+ # TODO: Update this comment to be better.
+ kw['diff'] = {'prepared': "System rules were not changed (e.g. rule " # noqa
+ "weight changed, redundant rule, etc)"} # noqa
+ else:
+ # We need to fetch active table dump before we apply new rules
+ # since we will need them to generate a diff.
+ table_active_rules = iptables_new.get_saved_table_dump(table)
+
+ # Apply generated rules.
+ iptables_new.system_apply_table_rules(table)
+
+ # Refresh saved table dump with active iptables rules.
+ iptables_new.refresh_saved_table_dump(table)
+
+ # Save state and system iptables rules.
+ iptables_new.system_save(backup=backup)
+
+ # Generate a diff.
+ if hasattr(module, '_diff') and module._diff:
+ table_active_rules_new = iptables_new.get_saved_table_dump(table) # noqa
+ if table_active_rules != table_active_rules_new:
+ kw['diff'] = generate_diff(table_active_rules, table_active_rules_new) # noqa
+ else:
+ # TODO: Update this comment to be better.
+ kw['diff'] = {'prepared': "System rules were not changed (e.g. rule " # noqa
+ "weight changed, redundant rule, etc)"} # noqa
+
+ kw['changed'] = changed
+ module.exit_json(**kw)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/omero/roles/iptables_raw/meta/.galaxy_install_info b/omero/roles/iptables_raw/meta/.galaxy_install_info
new file mode 100644
index 00000000..c4e5fc53
--- /dev/null
+++ b/omero/roles/iptables_raw/meta/.galaxy_install_info
@@ -0,0 +1,2 @@
+install_date: Wed 13 Nov 17:33:59 2024
+version: 0.4.0
diff --git a/omero/roles/iptables_raw/meta/main.yml b/omero/roles/iptables_raw/meta/main.yml
new file mode 100644
index 00000000..50bd4bdf
--- /dev/null
+++ b/omero/roles/iptables_raw/meta/main.yml
@@ -0,0 +1,16 @@
+---
+galaxy_info:
+ author: ome-devel@lists.openmicroscopy.org.uk
+ description: Import iptables_raw library
+ company: Open Microscopy Environment
+ license: BSD
+ min_ansible_version: 2.6
+ platforms:
+ - name: EL
+ versions:
+ - 9
+ namespace: ome
+ role_name: iptables_raw
+ galaxy_tags: []
+
+dependencies: []
diff --git a/molecule/www/molecule.yml b/omero/roles/iptables_raw/molecule/default/molecule.yml
similarity index 62%
rename from molecule/www/molecule.yml
rename to omero/roles/iptables_raw/molecule/default/molecule.yml
index e6b232df..bcbb4fec 100644
--- a/molecule/www/molecule.yml
+++ b/omero/roles/iptables_raw/molecule/default/molecule.yml
@@ -1,8 +1,6 @@
---
dependency:
name: galaxy
- options:
- role-file: requirements.yml
driver:
name: docker
lint: |
@@ -10,18 +8,19 @@ lint: |
ansible-lint
flake8
platforms:
- - name: www
- image: centos/systemd
+ - name: iptables-raw
+ image: eniocarboni/docker-rockylinux-systemd:9
image_version: latest
command: /sbin/init
privileged: true
+ cgroupns_mode: host
+ tmpfs:
+ - /sys/fs/cgroup
+ groups:
+ - extra_options
provisioner:
name: ansible
- playbooks:
- converge: ../../www/www-deploy.yml
lint:
name: ansible-lint
-scenario:
- name: www
verifier:
name: testinfra
diff --git a/omero/roles/iptables_raw/molecule/default/playbook.yml b/omero/roles/iptables_raw/molecule/default/playbook.yml
new file mode 100644
index 00000000..b60fad35
--- /dev/null
+++ b/omero/roles/iptables_raw/molecule/default/playbook.yml
@@ -0,0 +1,27 @@
+---
+- name: Converge
+ hosts: all
+ roles:
+ - role: ome.iptables_raw
+
+ tasks:
+ # - allow established/related inbound/outbound
+ # - allow ssh inbound
+ # - block http outbound
+ # - allow all other outbound
+ - name: Create iptables test rules
+ iptables_raw_25:
+ name: test_rules
+ keep_unmanaged: false
+ rules: |
+ -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
+ -A OUTPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
+ -A INPUT -p tcp -m tcp --dport 22 -j ACCEPT
+ -A OUTPUT -p tcp -m tcp --dport 80 -j REJECT
+ -A INPUT -j REJECT
+ -A FORWARD -j REJECT
+ -A OUTPUT -j ACCEPT
+ state: present
+
+# TODO: For a full test we should reboot and check the iptables rules
+# are still active
diff --git a/omero/roles/iptables_raw/molecule/default/tests/test_default.py b/omero/roles/iptables_raw/molecule/default/tests/test_default.py
new file mode 100644
index 00000000..fab4ad0f
--- /dev/null
+++ b/omero/roles/iptables_raw/molecule/default/tests/test_default.py
@@ -0,0 +1,26 @@
+import os
+
+import testinfra.utils.ansible_runner
+
+testinfra_hosts = testinfra.utils.ansible_runner.AnsibleRunner(
+ os.environ['MOLECULE_INVENTORY_FILE']
+).get_hosts('all')
+
+
+def test_hosts_file(host):
+ f = host.file('/etc/hosts')
+
+ assert f.exists
+ assert f.user == 'root'
+ assert f.group == 'root'
+
+
+def test_http_block(host):
+ out = host.run('curl -f -I http://www.openmicroscopy.org')
+ assert out.rc == 7
+ assert 'Connection refused' in out.stderr
+
+
+def test_https_allow(host):
+ out = host.check_output('curl -f -I https://www.openmicroscopy.org')
+ assert 'HTTP/2 200' in out
diff --git a/omero/roles/iptables_raw/tasks/main.yml b/omero/roles/iptables_raw/tasks/main.yml
new file mode 100644
index 00000000..22bbf64b
--- /dev/null
+++ b/omero/roles/iptables_raw/tasks/main.yml
@@ -0,0 +1,32 @@
+---
+# tasks file for iptables-raw
+
+- name: iptables-raw | install iptables services
+ become: true
+ ansible.builtin.dnf:
+ update_cache: true
+ name: iptables-services
+ state: present
+
+- name: iptables-raw | get list of services
+ become: true
+ service_facts:
+
+- name: iptables-raw | disable firewalld
+ become: true
+ service:
+ name: firewalld
+ state: stopped
+ enabled: false
+ when: >-
+ (iptables_raw_disable_firewalld | default(True)) and (
+ ('firewalld' in ansible_facts.services) or
+ ('firewalld.service' in ansible_facts.services)
+ )
+
+- name: iptables-raw | enable iptables
+ become: true
+ service:
+ name: iptables
+ state: started
+ enabled: true
diff --git a/omero/roles/nfs_mount/.gitignore b/omero/roles/nfs_mount/.gitignore
new file mode 100644
index 00000000..a52f8697
--- /dev/null
+++ b/omero/roles/nfs_mount/.gitignore
@@ -0,0 +1,175 @@
+# Created by https://www.gitignore.io/api/linux,macos,python,ansible
+# Edit at https://www.gitignore.io/?templates=linux,macos,python,ansible
+
+### Ansible ###
+*.retry
+
+### Linux ###
+*~
+
+# temporary files which can be created if a process still has a handle open of a deleted file
+.fuse_hidden*
+
+# KDE directory preferences
+.directory
+
+# Linux trash folder which might appear on any partition or disk
+.Trash-*
+
+# .nfs files are created when an open file is removed but is still being accessed
+.nfs*
+
+### macOS ###
+# General
+.DS_Store
+.AppleDouble
+.LSOverride
+
+# Icon must end with two \r
+Icon
+
+# Thumbnails
+._*
+
+# Files that might appear in the root of a volume
+.DocumentRevisions-V100
+.fseventsd
+.Spotlight-V100
+.TemporaryItems
+.Trashes
+.VolumeIcon.icns
+.com.apple.timemachine.donotpresent
+
+# Directories potentially created on remote AFP share
+.AppleDB
+.AppleDesktop
+Network Trash Folder
+Temporary Items
+.apdisk
+
+### Python ###
+# Byte-compiled / optimized / DLL files
+__pycache__/
+*.py[cod]
+*$py.class
+
+# C extensions
+*.so
+
+# Distribution / packaging
+.Python
+build/
+develop-eggs/
+dist/
+downloads/
+eggs/
+.eggs/
+lib/
+lib64/
+parts/
+sdist/
+var/
+wheels/
+pip-wheel-metadata/
+share/python-wheels/
+*.egg-info/
+.installed.cfg
+*.egg
+MANIFEST
+
+# PyInstaller
+# Usually these files are written by a python script from a template
+# before PyInstaller builds the exe, so as to inject date/other infos into it.
+*.manifest
+*.spec
+
+# Installer logs
+pip-log.txt
+pip-delete-this-directory.txt
+
+# Unit test / coverage reports
+htmlcov/
+.tox/
+.nox/
+.coverage
+.coverage.*
+.cache
+nosetests.xml
+coverage.xml
+*.cover
+.hypothesis/
+.pytest_cache/
+
+# Translations
+*.mo
+*.pot
+
+# Django stuff:
+*.log
+local_settings.py
+db.sqlite3
+
+# Flask stuff:
+instance/
+.webassets-cache
+
+# Scrapy stuff:
+.scrapy
+
+# Sphinx documentation
+docs/_build/
+
+# PyBuilder
+target/
+
+# Jupyter Notebook
+.ipynb_checkpoints
+
+# IPython
+profile_default/
+ipython_config.py
+
+# pyenv
+.python-version
+
+# pipenv
+# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
+# However, in case of collaboration, if having platform-specific dependencies or dependencies
+# having no cross-platform support, pipenv may install dependencies that don’t work, or not
+# install all needed dependencies.
+#Pipfile.lock
+
+# celery beat schedule file
+celerybeat-schedule
+
+# SageMath parsed files
+*.sage.py
+
+# Environments
+.env
+.venv
+env/
+venv/
+ENV/
+env.bak/
+venv.bak/
+
+# Spyder project settings
+.spyderproject
+.spyproject
+
+# Rope project settings
+.ropeproject
+
+# mkdocs documentation
+/site
+
+# mypy
+.mypy_cache/
+.dmypy.json
+dmypy.json
+
+# Pyre type checker
+.pyre/
+
+# End of https://www.gitignore.io/api/linux,macos,python,ansible
diff --git a/omero/roles/nfs_mount/.travis.yml b/omero/roles/nfs_mount/.travis.yml
new file mode 100644
index 00000000..07a3a207
--- /dev/null
+++ b/omero/roles/nfs_mount/.travis.yml
@@ -0,0 +1,35 @@
+---
+language: python
+services: docker
+
+env:
+ global:
+ - ROLE_NAME: nfs-mount
+ matrix:
+ - MOLECULE_DISTRO: centos7
+ - MOLECULE_DISTRO: centos8
+ - MOLECULE_DISTRO: ubuntu1604
+ - MOLECULE_DISTRO: ubuntu1804
+ - MOLECULE_DISTRO: debian9
+ - MOLECULE_DISTRO: debian10
+
+install:
+ # Install test dependencies.
+ - pip install molecule docker testinfra ansible-lint flake8 yamllint
+
+before_script:
+ # Use actual Ansible Galaxy role name for the project directory.
+ - cd ../
+ - mv ansible-$ROLE_NAME $ROLE_NAME
+ - cd $ROLE_NAME
+
+script:
+ - molecule --version
+ - ansible --version
+ - molecule test
+
+notifications:
+ webhooks:
+ urls:
+ - "https://galaxy.ansible.com/api/v1/notifications/"
+ on_success: change
diff --git a/omero/roles/nfs_mount/README.md b/omero/roles/nfs_mount/README.md
new file mode 100644
index 00000000..ef5f418e
--- /dev/null
+++ b/omero/roles/nfs_mount/README.md
@@ -0,0 +1,38 @@
+NFS Mount
+=========
+
+Manage NFS mounts.
+
+Role Variables
+--------------
+
+- `nfs_version`: NFS version to use (default: nfs)
+- `nfs_mount_opts`: Default NFS mount options (default: defaults)
+- `nfs_share_mounts`: List of dictionaries of NFS shares:
+ - `path`: mount-point
+ - `mount`: nfs server path
+ - `opts`: mount options (optional)
+ - `nfs_version`: NFS version to use (optional; default to {{ nfs_version }})
+
+Example Playbook
+----------------
+
+```yaml
+- hosts: localhost
+ roles:
+ - role: nfs-mount
+ nfs_share_mounts:
+ - path: /mnt/remote
+ location: nfs.example.org:/data
+ nfs_version: nfs
+ - path: /mnt/readonly
+ location: nfs.example.org:/read-only
+ opts: "{{ nfs_mount_opts }},ro"
+ nfs_version: nfs4
+```
+
+Author Information
+------------------
+
+ome-devel@lists.openmicroscopy.org.uk
+sonic@justereseau.ca
diff --git a/omero/roles/nfs_mount/defaults/main.yml b/omero/roles/nfs_mount/defaults/main.yml
new file mode 100644
index 00000000..6f447574
--- /dev/null
+++ b/omero/roles/nfs_mount/defaults/main.yml
@@ -0,0 +1,11 @@
+---
+# defaults file for roles/nfs-mount
+
+# List of NFS shares
+nfs_share_mounts: []
+
+# Default NFS4 mount options
+nfs_mount_opts: defaults
+
+# Default NFS version (nfs for v3)
+nfs_version: nfs
diff --git a/omero/roles/nfs_mount/licence.txt b/omero/roles/nfs_mount/licence.txt
new file mode 100644
index 00000000..1fe1f85c
--- /dev/null
+++ b/omero/roles/nfs_mount/licence.txt
@@ -0,0 +1,13 @@
+ DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
+ Version 2, December 2004
+
+ Copyright (C) 2020 Lucas Maurice (Sonic)
+
+ Everyone is permitted to copy and distribute verbatim or modified
+ copies of this license document, and changing it is allowed as long
+ as the name is changed.
+
+ DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. You just DO WHAT THE FUCK YOU WANT TO.
diff --git a/omero/roles/nfs_mount/meta/.galaxy_install_info b/omero/roles/nfs_mount/meta/.galaxy_install_info
new file mode 100644
index 00000000..cac2b511
--- /dev/null
+++ b/omero/roles/nfs_mount/meta/.galaxy_install_info
@@ -0,0 +1,2 @@
+install_date: Wed 13 Nov 17:33:57 2024
+version: 2.0.0
diff --git a/omero/roles/nfs_mount/meta/main.yml b/omero/roles/nfs_mount/meta/main.yml
new file mode 100644
index 00000000..a33ddc59
--- /dev/null
+++ b/omero/roles/nfs_mount/meta/main.yml
@@ -0,0 +1,33 @@
+---
+galaxy_info:
+ role_name: nfs-mount
+
+ author: Lucas Maurice (Sonic)
+ description: Manage NFS mounts
+ company: Justereseau
+
+ license: WTFPL
+
+ min_ansible_version: 2.9
+
+ platforms:
+ - name: EL
+ versions:
+ - 7
+ - 8
+ - name: Debian
+ versions:
+ - 9
+ - 10
+ - name: Ubuntu
+ versions:
+ - 16.04
+ - 18.04
+
+ galaxy_tags:
+ - nfs
+ - share
+ - nfs3
+ - nfs4
+
+dependencies: []
diff --git a/omero/roles/nfs_mount/molecule/default/molecule.yml b/omero/roles/nfs_mount/molecule/default/molecule.yml
new file mode 100644
index 00000000..fa4175bb
--- /dev/null
+++ b/omero/roles/nfs_mount/molecule/default/molecule.yml
@@ -0,0 +1,36 @@
+---
+dependency:
+ name: galaxy
+driver:
+ name: docker
+lint: |
+ yamllint --config-file molecule/default/yaml-lint.yml .
+ ansible-lint
+ flake8
+platforms:
+ - name: instance
+ image: "geerlingguy/docker-${MOLECULE_DISTRO:-debian10}-ansible:latest"
+ command: ${MOLECULE_DOCKER_COMMAND:-""}
+ volumes:
+ - /sys/fs/cgroup:/sys/fs/cgroup:ro
+ privileged: true
+ pre_build_image: true
+provisioner:
+ name: ansible
+ lint: ansible-lint
+ playbooks:
+ converge: playbook.yml
+scenario:
+ name: default
+ test_sequence:
+ - lint
+ - destroy
+ - create
+ - syntax
+ - converge
+ - idempotence
+ - verify
+ - destroy
+verifier:
+ name: testinfra
+ lint: flake8
diff --git a/omero/roles/nfs_mount/molecule/default/playbook.yml b/omero/roles/nfs_mount/molecule/default/playbook.yml
new file mode 100644
index 00000000..1bcdda94
--- /dev/null
+++ b/omero/roles/nfs_mount/molecule/default/playbook.yml
@@ -0,0 +1,18 @@
+---
+- name: Converge
+ hosts: all
+ become: true
+
+ vars:
+ pip_executable: pip
+ pip_install_packages:
+ - docker-compose
+ - docker
+
+ pre_tasks:
+ - name: Update apt cache.
+ apt: update_cache=yes
+ when: ansible_os_family == 'Debian'
+
+ roles:
+ - nfs-mount
diff --git a/omero/roles/nfs_mount/molecule/default/yaml-lint.yml b/omero/roles/nfs_mount/molecule/default/yaml-lint.yml
new file mode 100644
index 00000000..db22c42e
--- /dev/null
+++ b/omero/roles/nfs_mount/molecule/default/yaml-lint.yml
@@ -0,0 +1,6 @@
+---
+extends: default
+rules:
+ line-length:
+ max: 160
+ level: warning
diff --git a/omero/roles/nfs_mount/tasks/main.yml b/omero/roles/nfs_mount/tasks/main.yml
new file mode 100644
index 00000000..2edde34e
--- /dev/null
+++ b/omero/roles/nfs_mount/tasks/main.yml
@@ -0,0 +1,23 @@
+---
+- name: Include family related vars
+ include_vars: "{{ ansible_os_family }}.yml"
+
+- name: Install NFS mount utility
+ become: true
+ package:
+ name: "{{ nfs_package }}"
+ state: present
+
+# Do not create mountpoint using file, the mount module will create it
+# automatically. This avoids problems where the module tries to change
+# permissions on an existing directory
+
+- name: Mount NFS share
+ become: true
+ mount:
+ fstype: "{{ item.nfs_version | default(nfs_version) }}"
+ name: "{{ item.path }}"
+ opts: "{{ item.opts | default(nfs_mount_opts) }}"
+ src: "{{ item.location }}"
+ state: mounted
+ with_items: "{{ nfs_share_mounts }}"
diff --git a/omero/roles/nfs_mount/vars/Debian.yml b/omero/roles/nfs_mount/vars/Debian.yml
new file mode 100644
index 00000000..bfa5146f
--- /dev/null
+++ b/omero/roles/nfs_mount/vars/Debian.yml
@@ -0,0 +1,2 @@
+---
+nfs_package: nfs-common
diff --git a/omero/roles/nfs_mount/vars/RedHat.yml b/omero/roles/nfs_mount/vars/RedHat.yml
new file mode 100644
index 00000000..6b6897ac
--- /dev/null
+++ b/omero/roles/nfs_mount/vars/RedHat.yml
@@ -0,0 +1,2 @@
+---
+nfs_package: nfs-utils
diff --git a/omero/roles/nfs_share/.gitignore b/omero/roles/nfs_share/.gitignore
new file mode 100644
index 00000000..b6261c93
--- /dev/null
+++ b/omero/roles/nfs_share/.gitignore
@@ -0,0 +1 @@
+.*~
diff --git a/omero/roles/nfs_share/.travis.yml b/omero/roles/nfs_share/.travis.yml
new file mode 100644
index 00000000..49e7e1c5
--- /dev/null
+++ b/omero/roles/nfs_share/.travis.yml
@@ -0,0 +1,29 @@
+---
+language: python
+python: "2.7"
+
+# Use the new container infrastructure
+sudo: false
+
+# Install ansible
+addons:
+ apt:
+ packages:
+ - python-pip
+
+install:
+ # Install ansible
+ - pip install ansible
+
+ # Check ansible version
+ - ansible --version
+
+ # Create ansible.cfg with correct roles_path
+ - printf '[defaults]\nroles_path=../' >ansible.cfg
+
+script:
+ # Basic role syntax check
+ - ansible-playbook tests/test.yml -i tests/inventory --syntax-check
+
+notifications:
+ webhooks: https://galaxy.ansible.com/api/v1/notifications/
diff --git a/omero/roles/nfs_share/README.md b/omero/roles/nfs_share/README.md
new file mode 100644
index 00000000..c4b50b00
--- /dev/null
+++ b/omero/roles/nfs_share/README.md
@@ -0,0 +1,39 @@
+NFS Share
+=========
+
+Manage NFS file shares (no authentication).
+
+Note if SELinux is enabled you may need modify the configure of the the shared directories (not handled by this role).
+
+
+Role Variables
+--------------
+
+`nfs_shares`: A dictionary of lists of hosts and options `{/exported/directory: [{ host: host-pattern, options: options (optional) }, ...] }`
+
+Note the default NFS share options in this role (`ro,all_squash`) are slightly more restrictive than the original NFS defaults.
+
+
+Example Playbook
+----------------
+
+ - hosts: localhost
+ roles:
+ - role: nfs-share
+ nfs_shares:
+ # Allow access from *.example.org with default options
+ /srv/share1:
+ - host: "*.example.org"
+
+ # Allow read-write access from two subnets, squash all except root
+ /srv/share2:
+ - host: 192.168.1.0/25
+ options: rw,root_squash
+ - host: 172.16.0.0/20
+ options: rw,root_squash
+
+
+Author Information
+------------------
+
+ome-devel@lists.openmicroscopy.org.uk
diff --git a/omero/roles/nfs_share/defaults/main.yml b/omero/roles/nfs_share/defaults/main.yml
new file mode 100644
index 00000000..30117f1f
--- /dev/null
+++ b/omero/roles/nfs_share/defaults/main.yml
@@ -0,0 +1,5 @@
+---
+# defaults file for roles/nfs-share
+
+# A dictionary of shares, see README.md
+nfs_shares: {}
diff --git a/omero/roles/nfs_share/handlers/main.yml b/omero/roles/nfs_share/handlers/main.yml
new file mode 100644
index 00000000..fff52ad1
--- /dev/null
+++ b/omero/roles/nfs_share/handlers/main.yml
@@ -0,0 +1,6 @@
+---
+# Handlers for nfs-share
+
+- name: refresh nfs
+ become: yes
+ command: exportfs -ar
diff --git a/omero/roles/nfs_share/meta/.galaxy_install_info b/omero/roles/nfs_share/meta/.galaxy_install_info
new file mode 100644
index 00000000..c5dfece1
--- /dev/null
+++ b/omero/roles/nfs_share/meta/.galaxy_install_info
@@ -0,0 +1,2 @@
+install_date: Wed 13 Nov 17:33:58 2024
+version: 1.0.0
diff --git a/omero/roles/nfs_share/meta/main.yml b/omero/roles/nfs_share/meta/main.yml
new file mode 100644
index 00000000..5f69087a
--- /dev/null
+++ b/omero/roles/nfs_share/meta/main.yml
@@ -0,0 +1,13 @@
+galaxy_info:
+ author: ome-devel@lists.openmicroscopy.org.uk
+ description: Manage NFS file shares
+ company: Open Microscopy Environment
+ license: BSD
+ min_ansible_version: 2.1
+ platforms:
+ - name: EL
+ versions:
+ - 7
+ galaxy_tags: []
+
+dependencies: []
diff --git a/omero/roles/nfs_share/tasks/main.yml b/omero/roles/nfs_share/tasks/main.yml
new file mode 100644
index 00000000..7fa740a4
--- /dev/null
+++ b/omero/roles/nfs_share/tasks/main.yml
@@ -0,0 +1,24 @@
+---
+# tasks file for roles/nfs-share
+
+- name: system packages | nfs server
+ become: yes
+ yum:
+ name: nfs-utils
+ state: present
+
+- name: nfs server | configure shares
+ become: yes
+ template:
+ backup: yes
+ dest: /etc/exports
+ src: exports.j2
+ notify:
+ - refresh nfs
+
+- name: nfs server | enable nfs
+ become: yes
+ service:
+ enabled: yes
+ name: nfs-server
+ state: started
diff --git a/omero/roles/nfs_share/templates/exports.j2 b/omero/roles/nfs_share/templates/exports.j2
new file mode 100644
index 00000000..55ef0cb9
--- /dev/null
+++ b/omero/roles/nfs_share/templates/exports.j2
@@ -0,0 +1,3 @@
+{% for key, value in nfs_shares.iteritems() %}
+{{ key }} {% for access in value %} {{ access.host }}({{ access.options | default("ro,all_squash") }}) {% endfor %}
+{% endfor %}
diff --git a/omero/roles/nfs_share/tests/inventory b/omero/roles/nfs_share/tests/inventory
new file mode 100644
index 00000000..2fbb50c4
--- /dev/null
+++ b/omero/roles/nfs_share/tests/inventory
@@ -0,0 +1 @@
+localhost
diff --git a/omero/roles/nfs_share/tests/test.yml b/omero/roles/nfs_share/tests/test.yml
new file mode 100644
index 00000000..99c212a6
--- /dev/null
+++ b/omero/roles/nfs_share/tests/test.yml
@@ -0,0 +1,5 @@
+---
+- hosts: localhost
+ remote_user: root
+ roles:
+ - role: ansible-role-nfs-share
diff --git a/omero/roles/ome.basedeps/.github/workflows/molecule.yml b/omero/roles/ome.basedeps/.github/workflows/molecule.yml
new file mode 100644
index 00000000..e55180a9
--- /dev/null
+++ b/omero/roles/ome.basedeps/.github/workflows/molecule.yml
@@ -0,0 +1,55 @@
+---
+name: Molecule
+# yamllint disable-line rule:truthy
+on:
+ push:
+ pull_request:
+ schedule:
+ - cron: '29 21 * * 0'
+
+jobs:
+
+ list-scenarios:
+ runs-on: ubuntu-latest
+ outputs:
+ matrix: ${{ steps.listscenarios.outputs.scenarios }}
+ steps:
+ - uses: actions/checkout@v3
+ - id: listscenarios
+ uses: ome/action-ansible-molecule-list-scenarios@main
+
+ test:
+ name: Test
+ needs:
+ - list-scenarios
+ runs-on: ubuntu-22.04
+ strategy:
+ # Keep running so we can see if other tests pass
+ fail-fast: false
+ matrix:
+ scenario: ${{fromJson(needs.list-scenarios.outputs.matrix)}}
+ steps:
+ - uses: actions/checkout@v4
+ - uses: actions/setup-python@v4
+ with:
+ python-version: '3.9'
+ - name: Install Ansible & Molecule
+ run: |
+ pip install "ansible<8" "ansible-lint<6.13" flake8
+ pip install "molecule<5" "ansible-compat<4"
+ pip install molecule-plugins[docker] pytest-testinfra
+ - name: Run molecule
+ run: molecule test -s "${{ matrix.scenario }}"
+
+ publish:
+ name: Galaxy
+ if: startsWith(github.ref, 'refs/tags')
+ needs:
+ - test
+ runs-on: ubuntu-22.04
+ steps:
+ - name: galaxy
+ uses: ansible-actions/ansible-galaxy-action@v1.2.0
+ with:
+ galaxy_api_key: ${{ secrets.GALAXY_API_KEY }}
+ galaxy_version: ${{ github.ref_name }}
diff --git a/omero/roles/ome.basedeps/.gitignore b/omero/roles/ome.basedeps/.gitignore
new file mode 100644
index 00000000..d35ab669
--- /dev/null
+++ b/omero/roles/ome.basedeps/.gitignore
@@ -0,0 +1,2 @@
+.*~
+*.pyc
diff --git a/omero/roles/ome.basedeps/LICENSE.md b/omero/roles/ome.basedeps/LICENSE.md
new file mode 100644
index 00000000..a3181283
--- /dev/null
+++ b/omero/roles/ome.basedeps/LICENSE.md
@@ -0,0 +1,22 @@
+Copyright (c) 2019, University of Dundee
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/omero/roles/ome.basedeps/README.md b/omero/roles/ome.basedeps/README.md
new file mode 100644
index 00000000..e45a3a0d
--- /dev/null
+++ b/omero/roles/ome.basedeps/README.md
@@ -0,0 +1,28 @@
+Base Dependencies
+=================
+
+[![Actions Status](https://github.com/ome/ansible-role-basedeps/workflows/Molecule/badge.svg)](https://github.com/ome/ansible-role-basedeps/actions)
+[![Ansible Role](https://img.shields.io/badge/ansible--galaxy-basedeps-blue.svg)](https://galaxy.ansible.com/ui/standalone/roles/ome/basedeps/)
+
+Base dependencies for most servers.
+
+These dependencies should be suitable for installation on a minimal production server.
+
+Example Playbook
+----------------
+
+ - hosts: servers
+ roles:
+ - { role: ome.basedeps }
+
+
+Author Information
+------------------
+
+ome-devel@lists.openmicroscopy.org.uk
+
+
+License
+-------
+
+BSD
diff --git a/omero/roles/ome.basedeps/meta/.galaxy_install_info b/omero/roles/ome.basedeps/meta/.galaxy_install_info
new file mode 100644
index 00000000..982fcdc9
--- /dev/null
+++ b/omero/roles/ome.basedeps/meta/.galaxy_install_info
@@ -0,0 +1,2 @@
+install_date: Wed 13 Nov 17:33:40 2024
+version: 1.3.2
diff --git a/omero/roles/ome.basedeps/meta/main.yml b/omero/roles/ome.basedeps/meta/main.yml
new file mode 100644
index 00000000..cb7e055e
--- /dev/null
+++ b/omero/roles/ome.basedeps/meta/main.yml
@@ -0,0 +1,19 @@
+---
+galaxy_info:
+ role_name: basedeps
+ namespace: ome
+ author: ome-devel@lists.openmicroscopy.org.uk
+ description: Base dependencies for most servers.
+ company: Open Microscopy Environment
+ license: BSD
+ min_ansible_version: 2.1
+ platforms:
+ - name: EL
+ versions:
+ - 9
+ - name: Ubuntu
+ versions:
+ - jammy
+ galaxy_tags:
+ - cloud
+ - system
diff --git a/omero/roles/ome.basedeps/molecule/default/converge.yml b/omero/roles/ome.basedeps/molecule/default/converge.yml
new file mode 100644
index 00000000..f8b1b048
--- /dev/null
+++ b/omero/roles/ome.basedeps/molecule/default/converge.yml
@@ -0,0 +1,5 @@
+---
+- name: Converge
+ hosts: all
+ roles:
+ - role: ome.basedeps
diff --git a/omero/roles/ome.basedeps/molecule/default/molecule.yml b/omero/roles/ome.basedeps/molecule/default/molecule.yml
new file mode 100644
index 00000000..1ff011fa
--- /dev/null
+++ b/omero/roles/ome.basedeps/molecule/default/molecule.yml
@@ -0,0 +1,22 @@
+---
+dependency:
+ name: galaxy
+driver:
+ name: docker
+lint: |
+ yamllint .
+ ansible-lint
+ flake8
+platforms:
+ - name: rockylinux-9
+ image: rockylinux:9
+ - name: ubuntu-2204
+ image: ubuntu:22.04
+provisioner:
+ name: ansible
+ lint:
+ name: ansible-lint
+scenario:
+ name: default
+verifier:
+ name: testinfra
diff --git a/omero/roles/ome.basedeps/molecule/default/tests/test_default.py b/omero/roles/ome.basedeps/molecule/default/tests/test_default.py
new file mode 100644
index 00000000..420559a9
--- /dev/null
+++ b/omero/roles/ome.basedeps/molecule/default/tests/test_default.py
@@ -0,0 +1,15 @@
+import os
+
+import pytest
+import testinfra.utils.ansible_runner
+
+testinfra_hosts = testinfra.utils.ansible_runner.AnsibleRunner(
+ os.environ["MOLECULE_INVENTORY_FILE"]
+).get_hosts("all")
+
+
+@pytest.mark.parametrize(
+ "package", ["curl", "patch", "rsync", "tar", "unzip", "wget", "zip"]
+)
+def test_command(host, package):
+ assert host.exists(package)
diff --git a/omero/roles/ome.basedeps/tasks/main.yml b/omero/roles/ome.basedeps/tasks/main.yml
new file mode 100644
index 00000000..8d14cfc3
--- /dev/null
+++ b/omero/roles/ome.basedeps/tasks/main.yml
@@ -0,0 +1,32 @@
+---
+# tasks file for roles/basedeps
+- name: Import a key for epel
+ ansible.builtin.rpm_key:
+ state: present
+ key: https://dl.fedoraproject.org/pub/epel/RPM-GPG-KEY-EPEL-9
+ when: ansible_os_family == 'RedHat'
+
+- name: epel | setup dnf repository
+ become: true
+ ansible.builtin.dnf:
+ update_cache: true
+ name:
+ https://dl.fedoraproject.org/pub/epel/epel-release-latest-9.noarch.rpm
+ state: present
+ when: ansible_os_family == 'RedHat'
+
+# use separate yum/apt tasks because package only installs one item at a time
+- name: system packages | basic system utils (rockylinux)
+ become: true
+ ansible.builtin.dnf:
+ name: "{{ basedeps_packages.redhat }}"
+ state: present
+ when: ansible_os_family == 'RedHat'
+
+- name: system packages | basic system utils (ubuntu)
+ become: true
+ ansible.builtin.apt:
+ update_cache: true
+ name: "{{ basedeps_packages.debian }}"
+ state: present
+ when: ansible_os_family == 'Debian'
diff --git a/omero/roles/ome.basedeps/vars/main.yml b/omero/roles/ome.basedeps/vars/main.yml
new file mode 100644
index 00000000..bac93125
--- /dev/null
+++ b/omero/roles/ome.basedeps/vars/main.yml
@@ -0,0 +1,19 @@
+---
+
+
+basedeps_packages:
+ redhat:
+ - patch
+ - rsync
+ - tar
+ - unzip
+ - wget
+ - zip
+ debian:
+ - curl
+ - patch
+ - rsync
+ - tar
+ - unzip
+ - wget
+ - zip
diff --git a/omero/roles/ome.cli_utils/.github/workflows/molecule.yml b/omero/roles/ome.cli_utils/.github/workflows/molecule.yml
new file mode 100644
index 00000000..f1ea4c82
--- /dev/null
+++ b/omero/roles/ome.cli_utils/.github/workflows/molecule.yml
@@ -0,0 +1,55 @@
+---
+name: Molecule
+# yamllint disable-line rule:truthy
+on:
+ push:
+ pull_request:
+ schedule:
+ - cron: '29 21 * * 0'
+
+jobs:
+
+ list-scenarios:
+ runs-on: ubuntu-22.04
+ outputs:
+ matrix: ${{ steps.listscenarios.outputs.scenarios }}
+ steps:
+ - uses: actions/checkout@v4
+ - id: listscenarios
+ uses: ome/action-ansible-molecule-list-scenarios@main
+
+ test:
+ name: Test
+ needs:
+ - list-scenarios
+ runs-on: ubuntu-22.04
+ strategy:
+ # Keep running so we can see if other tests pass
+ fail-fast: false
+ matrix:
+ scenario: ${{fromJson(needs.list-scenarios.outputs.matrix)}}
+ steps:
+ - uses: actions/checkout@v4
+ - uses: actions/setup-python@v5
+ with:
+ python-version: '3.9'
+ - name: Install Ansible & Molecule
+ run: |
+ pip install "ansible<8" "ansible-lint<6.13" flake8
+ pip install "molecule<5" "ansible-compat<4"
+ pip install molecule-plugins[docker] pytest-testinfra
+ - name: Run molecule
+ run: molecule test -s "${{ matrix.scenario }}"
+
+ publish:
+ name: Galaxy
+ if: startsWith(github.ref, 'refs/tags')
+ needs:
+ - test
+ runs-on: ubuntu-22.04
+ steps:
+ - name: galaxy
+ uses: ansible-actions/ansible-galaxy-action@v1.2.0
+ with:
+ galaxy_api_key: ${{ secrets.GALAXY_API_KEY }}
+ galaxy_version: ${{ github.ref_name }}
diff --git a/omero/roles/ome.cli_utils/.gitignore b/omero/roles/ome.cli_utils/.gitignore
new file mode 100644
index 00000000..b6261c93
--- /dev/null
+++ b/omero/roles/ome.cli_utils/.gitignore
@@ -0,0 +1 @@
+.*~
diff --git a/omero/roles/ome.cli_utils/LICENSE.md b/omero/roles/ome.cli_utils/LICENSE.md
new file mode 100644
index 00000000..a3181283
--- /dev/null
+++ b/omero/roles/ome.cli_utils/LICENSE.md
@@ -0,0 +1,22 @@
+Copyright (c) 2019, University of Dundee
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/omero/roles/ome.cli_utils/README.md b/omero/roles/ome.cli_utils/README.md
new file mode 100644
index 00000000..e9b6f945
--- /dev/null
+++ b/omero/roles/ome.cli_utils/README.md
@@ -0,0 +1,21 @@
+CLI utils
+=========
+
+[![Actions Status](https://github.com/ome/ansible-role-cli-utils/workflows/Molecule/badge.svg)](https://github.com/ome/ansible-role-cli-utils/actions)
+[![Ansible Role](https://img.shields.io/badge/ansible--galaxy-cli_utils-blue.svg)](https://galaxy.ansible.com/ui/standalone/roles/ome/cli_utils/)
+
+Useful command-line utilities for administering a server.
+
+This is intended for utilities that are generally useful but are not strictly necessary for a production server.
+
+
+Dependencies
+------------
+
+Depends on the `ome.basedeps` role.
+
+
+Author Information
+------------------
+
+ome-devel@lists.openmicroscopy.org.uk
diff --git a/omero/roles/ome.cli_utils/meta/.galaxy_install_info b/omero/roles/ome.cli_utils/meta/.galaxy_install_info
new file mode 100644
index 00000000..2ed0dd04
--- /dev/null
+++ b/omero/roles/ome.cli_utils/meta/.galaxy_install_info
@@ -0,0 +1,2 @@
+install_date: Wed 13 Nov 17:34:00 2024
+version: 1.2.5
diff --git a/omero/roles/ome.cli_utils/meta/main.yml b/omero/roles/ome.cli_utils/meta/main.yml
new file mode 100644
index 00000000..67d36bb9
--- /dev/null
+++ b/omero/roles/ome.cli_utils/meta/main.yml
@@ -0,0 +1,14 @@
+---
+galaxy_info:
+ author: ome-devel@lists.openmicroscopy.org.uk
+ description: Useful command-line utilities for administering a server
+ company: Open Microscopy Environment
+ license: BSD
+ min_ansible_version: 2.10
+ platforms:
+ - name: EL
+ versions:
+ - 9
+ namespace: ome
+ role_name: cli_utils
+ galaxy_tags: []
diff --git a/omero/roles/ome.cli_utils/meta/requirements.yml b/omero/roles/ome.cli_utils/meta/requirements.yml
new file mode 100644
index 00000000..1d87f749
--- /dev/null
+++ b/omero/roles/ome.cli_utils/meta/requirements.yml
@@ -0,0 +1,2 @@
+---
+- src: ome.basedeps
diff --git a/omero/roles/ome.cli_utils/molecule/default/molecule.yml b/omero/roles/ome.cli_utils/molecule/default/molecule.yml
new file mode 100644
index 00000000..93ad4166
--- /dev/null
+++ b/omero/roles/ome.cli_utils/molecule/default/molecule.yml
@@ -0,0 +1,20 @@
+---
+dependency:
+ name: galaxy
+driver:
+ name: docker
+lint: |
+ yamllint .
+ ansible-lint
+ flake8
+platforms:
+ - name: instance
+ image: rockylinux:9
+provisioner:
+ name: ansible
+ lint:
+ name: ansible-lint
+scenario:
+ name: default
+verifier:
+ name: testinfra
diff --git a/omero/roles/ome.cli_utils/molecule/default/playbook.yml b/omero/roles/ome.cli_utils/molecule/default/playbook.yml
new file mode 100644
index 00000000..c627ecba
--- /dev/null
+++ b/omero/roles/ome.cli_utils/molecule/default/playbook.yml
@@ -0,0 +1,5 @@
+---
+- name: Converge
+ hosts: all
+ roles:
+ - role: ome.cli_utils
diff --git a/omero/roles/ome.cli_utils/molecule/default/requirements.yml b/omero/roles/ome.cli_utils/molecule/default/requirements.yml
new file mode 100644
index 00000000..9312f4cc
--- /dev/null
+++ b/omero/roles/ome.cli_utils/molecule/default/requirements.yml
@@ -0,0 +1,2 @@
+---
+- role: ome.basedeps
\ No newline at end of file
diff --git a/omero/roles/ome.cli_utils/molecule/default/tests/test_default.py b/omero/roles/ome.cli_utils/molecule/default/tests/test_default.py
new file mode 100644
index 00000000..7ab61fe5
--- /dev/null
+++ b/omero/roles/ome.cli_utils/molecule/default/tests/test_default.py
@@ -0,0 +1,11 @@
+import os
+import pytest
+import testinfra.utils.ansible_runner
+
+testinfra_hosts = testinfra.utils.ansible_runner.AnsibleRunner(
+ os.environ['MOLECULE_INVENTORY_FILE']).get_hosts('all')
+
+
+@pytest.mark.parametrize("package", ["bzip2", "screen", "tmux", "zsh"])
+def test_analysis_packages(host, package):
+ assert host.package(package).is_installed
diff --git a/omero/roles/ome.cli_utils/tasks/main.yml b/omero/roles/ome.cli_utils/tasks/main.yml
new file mode 100644
index 00000000..4a08818e
--- /dev/null
+++ b/omero/roles/ome.cli_utils/tasks/main.yml
@@ -0,0 +1,28 @@
+---
+# tasks file for roles/cli-utils
+
+- name: Import a key for epel
+ become: true
+ ansible.builtin.rpm_key:
+ state: present
+ key: https://dl.fedoraproject.org/pub/epel/RPM-GPG-KEY-EPEL-9
+
+- name: epel | setup dnf repository
+ become: true
+ ansible.builtin.dnf:
+ update_cache: true
+ name:
+ https://dl.fedoraproject.org/pub/epel/epel-release-latest-9.noarch.rpm
+ state: present
+
+- name: system packages | cli user utils
+ become: true
+ ansible.builtin.dnf:
+ update_cache: true
+ name:
+ - bzip2
+ - screen
+ - tmux
+ - zsh
+ state: present
+ when: ansible_os_family == 'RedHat'
diff --git a/omero/roles/ome.cli_utils/tests/inventory b/omero/roles/ome.cli_utils/tests/inventory
new file mode 100644
index 00000000..2fbb50c4
--- /dev/null
+++ b/omero/roles/ome.cli_utils/tests/inventory
@@ -0,0 +1 @@
+localhost
diff --git a/omero/roles/ome.cli_utils/tests/test.yml b/omero/roles/ome.cli_utils/tests/test.yml
new file mode 100644
index 00000000..c1902f6d
--- /dev/null
+++ b/omero/roles/ome.cli_utils/tests/test.yml
@@ -0,0 +1,5 @@
+---
+- hosts: localhost
+ remote_user: root
+ roles:
+ - role: ome.cli_utils
diff --git a/omero/roles/ome.cli_utils/tests/test_default.py b/omero/roles/ome.cli_utils/tests/test_default.py
new file mode 100644
index 00000000..2cf53164
--- /dev/null
+++ b/omero/roles/ome.cli_utils/tests/test_default.py
@@ -0,0 +1,11 @@
+import testinfra.utils.ansible_runner
+
+testinfra_hosts = testinfra.utils.ansible_runner.AnsibleRunner(
+ '.molecule/ansible_inventory').get_hosts('all')
+
+
+def test_analysis_packages(Package):
+ assert Package('bzip2')
+ assert Package('zsh')
+ assert Package('screen')
+ assert Package('tmux')
diff --git a/omero/roles/ome.deploy_archive/.github/workflows/molecule.yml b/omero/roles/ome.deploy_archive/.github/workflows/molecule.yml
new file mode 100644
index 00000000..1bde8477
--- /dev/null
+++ b/omero/roles/ome.deploy_archive/.github/workflows/molecule.yml
@@ -0,0 +1,55 @@
+---
+name: Molecule
+# yamllint disable-line rule:truthy
+on:
+ push:
+ pull_request:
+ schedule:
+ - cron: '29 21 * * 0'
+
+jobs:
+
+ list-scenarios:
+ runs-on: ubuntu-22.04
+ outputs:
+ matrix: ${{ steps.listscenarios.outputs.scenarios }}
+ steps:
+ - uses: actions/checkout@v4
+ - id: listscenarios
+ uses: ome/action-ansible-molecule-list-scenarios@main
+
+ test:
+ name: Test
+ needs:
+ - list-scenarios
+ runs-on: ubuntu-22.04
+ strategy:
+ # Keep running so we can see if other tests pass
+ fail-fast: false
+ matrix:
+ scenario: ${{fromJson(needs.list-scenarios.outputs.matrix)}}
+ steps:
+ - uses: actions/checkout@v4
+ - uses: actions/setup-python@v4
+ with:
+ python-version: '3.8'
+ - name: Install Ansible & Molecule
+ run: |
+ pip install "ansible<8" "ansible-lint<6.13" flake8
+ pip install "molecule<5" "ansible-compat<4"
+ pip install molecule-plugins[docker] pytest-testinfra
+ - name: Run molecule
+ run: molecule test -s "${{ matrix.scenario }}"
+
+ publish:
+ name: Galaxy
+ if: startsWith(github.ref, 'refs/tags')
+ needs:
+ - test
+ runs-on: ubuntu-22.04
+ steps:
+ - name: galaxy
+ uses: robertdebock/galaxy-action@1.2.1
+ with:
+ galaxy_api_key: ${{ secrets.GALAXY_API_KEY }}
+ git_branch: ${{ github.ref_name }}
diff --git a/omero/roles/ome.deploy_archive/.gitignore b/omero/roles/ome.deploy_archive/.gitignore
new file mode 100644
index 00000000..d35ab669
--- /dev/null
+++ b/omero/roles/ome.deploy_archive/.gitignore
@@ -0,0 +1,2 @@
+.*~
+*.pyc
diff --git a/omero/roles/ome.deploy_archive/LICENSE.md b/omero/roles/ome.deploy_archive/LICENSE.md
new file mode 100644
index 00000000..a3181283
--- /dev/null
+++ b/omero/roles/ome.deploy_archive/LICENSE.md
@@ -0,0 +1,22 @@
+Copyright (c) 2019, University of Dundee
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/omero/roles/ome.deploy_archive/README.md b/omero/roles/ome.deploy_archive/README.md
new file mode 100644
index 00000000..a62b0d55
--- /dev/null
+++ b/omero/roles/ome.deploy_archive/README.md
@@ -0,0 +1,43 @@
+Deploy Archive
+==============
+
+[![Actions Status](https://github.com/ome/ansible-role-deploy-archive/workflows/Molecule/badge.svg)](https://github.com/ome/ansible-role-deploy-archive/actions)
+[![Ansible Role](https://img.shields.io/badge/ansible--galaxy-deploy_archive-blue.svg)](https://galaxy.ansible.com/ui/standalone/roles/ome/deploy_archive/)
+
+Deploys an archive.
+Downloads, extracts and optionally creates a symlink.
+
+
+Prerequisites
+-------------
+
+This role does not install tools such as `unzip` or `bzip` that may be required for extracting archives.
+
+This role does not set `become: yes` on tasks. If you do not include this in your playbook you must ensure the Ansible user has write permissions on the destination directory.
+
+
+Role Variables
+--------------
+
+Required:
+- `deploy_archive_dest_dir`: The destination directory, will be automatically created
+- `deploy_archive_src_url`: URL to the archive
+
+Optional:
+- `deploy_archive_sha256`: SHA256 checksum, ignored if empty
+- `deploy_archive_filename`: Name of the downloaded file, default is the basename of the URL path.
+- `deploy_archive_symlink`: Absolute path of a symlink to the unarchived deployment, ignored if empty
+- `deploy_archive_internal_root`: Target of the symlink within the destination directory
+- `deploy_archive_notifies`: List of handlers to notify if the downloaded archive changes
+
+
+Example playbook
+----------------
+
+See [`playbook.yml`](molecule/default/playbook.yml)
+
+
+Author Information
+------------------
+
+ome-devel@lists.openmicroscopy.org.uk
diff --git a/omero/roles/ome.deploy_archive/defaults/main.yml b/omero/roles/ome.deploy_archive/defaults/main.yml
new file mode 100644
index 00000000..51d416f1
--- /dev/null
+++ b/omero/roles/ome.deploy_archive/defaults/main.yml
@@ -0,0 +1,18 @@
+---
+# defaults for deploy-archive
+
+# Destination directory for the archive, required
+# deploy_archive_dest_dir:
+# URL to the archive, required
+# deploy_archive_src_url:
+
+# SHA256 checksum, ignored if empty
+deploy_archive_sha256: ""
+# Name of the downloaded file, default is the basename of the URL path
+deploy_archive_filename: "{{ deploy_archive_src_url | basename }}"
+# Absolute path of a symlink to the unarchived deployment, ignored if empty
+deploy_archive_symlink: ""
+# Target of the symlink within the archive, default empty (top level directory)
+deploy_archive_internal_root: ""
+# List of handlers to notify if the downlaoded archive changes, default empty
+deploy_archive_notifies: []
diff --git a/omero/roles/ome.deploy_archive/meta/.galaxy_install_info b/omero/roles/ome.deploy_archive/meta/.galaxy_install_info
new file mode 100644
index 00000000..47f9239b
--- /dev/null
+++ b/omero/roles/ome.deploy_archive/meta/.galaxy_install_info
@@ -0,0 +1,2 @@
+install_date: Wed 13 Nov 17:33:47 2024
+version: 0.2.0
diff --git a/omero/roles/ome.deploy_archive/meta/main.yml b/omero/roles/ome.deploy_archive/meta/main.yml
new file mode 100644
index 00000000..fdb47bd0
--- /dev/null
+++ b/omero/roles/ome.deploy_archive/meta/main.yml
@@ -0,0 +1,17 @@
+---
+galaxy_info:
+ role_name: deploy_archive
+ author: ome-devel@lists.openmicroscopy.org.uk
+ description: Download and deploy an archive
+ company: Open Microscopy Environment
+ license: BSD
+ min_ansible_version: 2.4
+ platforms:
+ - name: EL
+ versions:
+ - 9
+ - name: Ubuntu
+ versions:
+ - jammy
+ namespace: ome
+ galaxy_tags: []
diff --git a/omero/roles/ome.deploy_archive/molecule/default/molecule.yml b/omero/roles/ome.deploy_archive/molecule/default/molecule.yml
new file mode 100644
index 00000000..ae2df164
--- /dev/null
+++ b/omero/roles/ome.deploy_archive/molecule/default/molecule.yml
@@ -0,0 +1,22 @@
+---
+dependency:
+ name: galaxy
+driver:
+ name: docker
+lint: |
+ yamllint .
+ ansible-lint
+ flake8
+platforms:
+ - name: deploy-archive-r9
+ image: rockylinux:9
+ - name: deploy-archive-u2204
+ image: ubuntu:22.04
+provisioner:
+ name: ansible
+ lint:
+ name: ansible-lint
+scenario:
+ name: default
+verifier:
+ name: testinfra
diff --git a/omero/roles/ome.deploy_archive/molecule/default/playbook.yml b/omero/roles/ome.deploy_archive/molecule/default/playbook.yml
new file mode 100644
index 00000000..586c490a
--- /dev/null
+++ b/omero/roles/ome.deploy_archive/molecule/default/playbook.yml
@@ -0,0 +1,25 @@
+---
+- name: Converge
+ hosts: all
+ become: true
+ pre_tasks:
+ - name: Install unzip
+ package:
+ update_cache: true
+ name: unzip
+ state: present
+ roles:
+ - role: ome.deploy_archive
+ deploy_archive_dest_dir: /opt/deploy-archive-test
+ deploy_archive_src_url: >
+ https://github.com/IDR/idr.openmicroscopy.org/archive/IDR-0.5.0.zip
+ deploy_archive_sha256: >
+ c71033292a19f305f2372730733b18a4149ac4fbe23165a2156e4680883bab8b
+ deploy_archive_symlink: /opt/idr-web-src
+ deploy_archive_internal_root: idr.openmicroscopy.org-IDR-0.5.0
+ deploy_archive_notifies:
+ - deploy-archive test handler
+
+ handlers:
+ - name: deploy-archive test handler
+ shell: echo triggered >> /opt/deploy-archive-test/handler-triggered
diff --git a/omero/roles/ome.deploy_archive/molecule/default/tests/test_default.py b/omero/roles/ome.deploy_archive/molecule/default/tests/test_default.py
new file mode 100644
index 00000000..898dad0b
--- /dev/null
+++ b/omero/roles/ome.deploy_archive/molecule/default/tests/test_default.py
@@ -0,0 +1,32 @@
+import os
+
+import testinfra.utils.ansible_runner
+
+testinfra_hosts = testinfra.utils.ansible_runner.AnsibleRunner(
+ os.environ['MOLECULE_INVENTORY_FILE']).get_hosts('all')
+
+
+def test_dir(host):
+ f = host.file('/opt/deploy-archive-test/idr.openmicroscopy.org-IDR-0.5.0')
+ assert f.exists
+ assert f.is_directory
+
+
+def test_file(host):
+ f = host.file(
+ '/opt/deploy-archive-test/idr.openmicroscopy.org-IDR-0.5.0/index.html')
+ assert f.exists
+ assert f.is_file
+
+
+def test_symlink(host):
+ f = host.file('/opt/idr-web-src')
+ assert f.exists
+ assert f.is_symlink
+ assert f.linked_to == (
+ '/opt/deploy-archive-test/idr.openmicroscopy.org-IDR-0.5.0')
+
+
+def test_handler_trigger_once(host):
+ f = host.file('/opt/deploy-archive-test/handler-triggered')
+ assert f.content.strip() == b'triggered'
diff --git a/omero/roles/ome.deploy_archive/tasks/main.yml b/omero/roles/ome.deploy_archive/tasks/main.yml
new file mode 100644
index 00000000..7410151b
--- /dev/null
+++ b/omero/roles/ome.deploy_archive/tasks/main.yml
@@ -0,0 +1,43 @@
+---
+# tasks file for deploy-archive
+
+- name: deploy archive | create directory
+ file:
+ path: "{{ deploy_archive_dest_dir }}"
+ state: directory
+ serole: _default
+ setype: _default
+ seuser: _default
+ mode: 0755
+
+- name: deploy archive | download
+ get_url:
+ url: "{{ deploy_archive_src_url }}"
+ checksum: >-
+ {{ (deploy_archive_sha256 | length > 0) |
+ ternary('sha256:' + deploy_archive_sha256, omit) }}
+ dest: "{{ deploy_archive_dest_dir }}/{{ deploy_archive_filename }}"
+
+- name: deploy archive | unarchive
+ unarchive:
+ src: "{{ deploy_archive_dest_dir }}/{{ deploy_archive_filename }}"
+ dest: "{{ deploy_archive_dest_dir }}"
+ group: root
+ owner: root
+ remote_src: true
+
+- name: deploy archive | create symlink parent directory
+ file:
+ path: "{{ deploy_archive_symlink | dirname }}"
+ state: directory
+ mode: 0755
+ when: deploy_archive_symlink | dirname | length > 0
+
+- name: deploy archive | symlink
+ file:
+ path: "{{ deploy_archive_symlink }}"
+ src: "{{ deploy_archive_dest_dir }}/{{ deploy_archive_internal_root }}"
+ force: true
+ state: link
+ when: deploy_archive_symlink | length > 0
+ notify: "{{ deploy_archive_notifies }}"
diff --git a/omero/roles/ome.docker/.github/workflows/molecule.yml b/omero/roles/ome.docker/.github/workflows/molecule.yml
new file mode 100644
index 00000000..2a8b703c
--- /dev/null
+++ b/omero/roles/ome.docker/.github/workflows/molecule.yml
@@ -0,0 +1,56 @@
+---
+name: Molecule
+# yamllint disable-line rule:truthy
+on:
+ push:
+ pull_request:
+ schedule:
+ - cron: '29 21 * * 0'
+
+jobs:
+
+ list-scenarios:
+ runs-on: ubuntu-22.04
+ outputs:
+ matrix: ${{ steps.listscenarios.outputs.scenarios }}
+ steps:
+ - uses: actions/checkout@v4
+ - id: listscenarios
+ uses: ome/action-ansible-molecule-list-scenarios@main
+
+ test:
+ name: Test
+ needs:
+ - list-scenarios
+ runs-on: ubuntu-22.04
+ strategy:
+ # Keep running so we can see if other tests pass
+ fail-fast: false
+ matrix:
+ scenario: ${{fromJson(needs.list-scenarios.outputs.matrix)}}
+ steps:
+ - uses: actions/checkout@v4
+ - uses: actions/setup-python@v4
+ with:
+ python-version: '3.9'
+ - name: Install Ansible & Molecule
+ run: |
+ pip install "ansible<8" "ansible-lint<6.13" flake8
+ pip install "molecule<5" "ansible-compat<4"
+ pip install molecule-plugins[docker] pytest-testinfra
+ - name: Run molecule
+ run: molecule test -s "${{ matrix.scenario }}"
+
+
+ publish:
+ name: Galaxy
+ if: startsWith(github.ref, 'refs/tags')
+ needs:
+ - test
+ runs-on: ubuntu-22.04
+ steps:
+ - name: galaxy
+ uses: ansible-actions/ansible-galaxy-action@v1.2.0
+ with:
+ galaxy_api_key: ${{ secrets.GALAXY_API_KEY }}
+ galaxy_version: ${{ github.ref_name }}
diff --git a/omero/roles/ome.docker/.gitignore b/omero/roles/ome.docker/.gitignore
new file mode 100644
index 00000000..3d8d5649
--- /dev/null
+++ b/omero/roles/ome.docker/.gitignore
@@ -0,0 +1,2 @@
+.*~
+*.pyc
\ No newline at end of file
diff --git a/omero/roles/ome.docker/CHANGES.md b/omero/roles/ome.docker/CHANGES.md
new file mode 100644
index 00000000..b027b9bb
--- /dev/null
+++ b/omero/roles/ome.docker/CHANGES.md
@@ -0,0 +1,13 @@
+# Changes in Version 3
+
+## Summary of breaking changes
+- Removed support for installing distribution docker
+- Removed `docker_install_upstream` parameter
+
+
+# Changes in Version 2
+
+## Summary of breaking changes
+- Switched to Docker Community Edition (uses overlay as the default storage driver).
+- Use `/etc/docker/daemon.json` for configuration instead of modifying the package's systemd service.
+- Removed `docker_repo_version` parameter.
diff --git a/omero/roles/ome.docker/LICENSE.md b/omero/roles/ome.docker/LICENSE.md
new file mode 100644
index 00000000..a3181283
--- /dev/null
+++ b/omero/roles/ome.docker/LICENSE.md
@@ -0,0 +1,22 @@
+Copyright (c) 2019, University of Dundee
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/omero/roles/ome.docker/README.md b/omero/roles/ome.docker/README.md
new file mode 100644
index 00000000..cdce12a2
--- /dev/null
+++ b/omero/roles/ome.docker/README.md
@@ -0,0 +1,114 @@
+Docker
+======
+
+[![Actions Status](https://github.com/ome/ansible-role-docker/workflows/Molecule/badge.svg)](https://github.com/ome/ansible-role-docker/actions)
+[![Ansible Role](https://img.shields.io/badge/ansible--galaxy-docker-blue.svg)](https://galaxy.ansible.com/ui/standalone/roles/ome/docker/)
+
+Setup Docker, provides options for using an advanced storage or networking configuration.
+Installs the latest official upstream Docker (Community Edition).
+
+This role should work with RedHat Enterprise Linux 7, but [this is not supported by Docker](https://docs.docker.com/install/linux/docker-ee/rhel/).
+
+If you want the distribution supplied Docker package do not use this role.
+
+
+Role Variables
+--------------
+
+Optional variables:
+- `docker_version`: Install a particular version of docker, e.g. `17.12.1.ce-1.el7.centos`, default current
+- `docker_groupmembers`: A list of users who will be added to the `docker` system group, allows docker to be run without sudo
+- `docker_use_ipv4_nic_mtu`: Force Docker to use the MTU set by the main IPV4 interface. This may be necessary on virtualised hosts, see comment in `defaults/main.yml`.
+- `docker_additional_options`: Dictionary of additional Docker configuration options.
+- `docker_use_custom_storage`: If `True` use a custom storage configuration, default `False`
+- `docker_use_custom_network`: If `True` use a custom network configuration, default `False`
+- `docker_systemd_setup`: Set this to False to disable automatic systemd configuration, default `False`.
+ You may wish to use this when building virtualisation images.
+- `docker_repo_force_releasever`: The repo config uses the `$releasever` variables. On some systems this may not work, if necessary you can forcibly override it by setting this variable.
+
+
+### Custom storage
+
+If `docker_use_custom_storage` is `True` thin-pool logical volumes will be created for Docker, and a separate logical volume will be created for the Docker volume (`/var/lib/docker`).
+
+- `docker_basefs`: Filesystem to use for the Docker containers (default xfs)
+- `docker_lvfilesystem`: Filesystem for the Docker volume (default xfs)
+- `docker_lvopts`: Additional arguments to be used when creating logical volumes
+
+The following variables must be defined when using custom storage:
+
+- `docker_vgname`: LVM volume group for the logical volumes
+- `docker_poolsize`: Size of the Docker thin-pool partition
+- `docker_metadatasize`: Size of the Docker thin-pool metadata partition (try 1% of the poolsize)
+- `docker_volumesize`: Size of the Docker volume
+
+If `docker_use_custom_storage` is `False` you may wish to mount `/var/lib/docker` on a separate partition or volume before applying this role.
+
+
+### Custom networking
+
+If `docker_use_custom_network` is `True` a custom network bridge will be used, this must be created outside of this role.
+The following variables must be defined:
+
+- `docker_bridge_name`: The name of a custom network bridge for docker
+- `docker_bridge_ips`: The custom IP range that docker should use for allocating IPs
+
+
+Dependencies
+------------
+
+Depends on lvm-partition.
+
+
+Development
+-----------
+
+This role is partially tested by Travis-CI, which requires running Docker-in-Docker.
+When testing changes you should therefore run a full test using molecule with the Vagrant driver as part of any review:
+
+ molecule test --driver vagrant
+
+
+Example Playbook
+----------------
+
+Simple example (uses default storage overlay driver):
+
+ - hosts: localhost
+ roles:
+ - role: ome.docker
+
+Example using the default storage driver, with a dedicated logical volume for docker:
+
+ - hosts: localhost
+ roles:
+ - role: ome.lvm_partition
+ lvm_lvname: var_lib_docker
+ lvm_lvmount: /var/lib/docker
+ lvm_lvsize: 100g
+ lvm_lvfilesystem: ext4
+ - role: ome.docker
+
+Advanced example using custom storage and listening on external port 4243 (insecure).
+The LVM volume group `VolGroup00` must already exist:
+
+ - hosts: localhost
+ roles:
+ - role: ome.docker
+ docker_use_ipv4_nic_mtu: True
+ docker_use_custom_storage: True
+ docker_vgname: VolGroup00
+ docker_poolsize: 10g
+ docker_metadatasize: 100m
+ docker_volumesize: 5g
+ docker_groupmembers: [centos]
+ docker_additional_options:
+ hosts:
+ - tcp://0.0.0.0:4243
+ - unix:///var/run/docker.sock
+
+
+Author Information
+------------------
+
+ome-devel@lists.openmicroscopy.org.uk
diff --git a/omero/roles/ome.docker/defaults/main.yml b/omero/roles/ome.docker/defaults/main.yml
new file mode 100644
index 00000000..c37425ee
--- /dev/null
+++ b/omero/roles/ome.docker/defaults/main.yml
@@ -0,0 +1,48 @@
+---
+# defaults file for roles/docker
+
+# Docker version to be installed
+docker_version: ''
+
+# Set to True if using a custom storage configuration
+docker_use_custom_storage: false
+
+# Set to True if using a custom network configuration
+docker_use_custom_network: false
+
+# Filesystem to use for the Docker containers
+docker_basefs: xfs
+
+# Filesystem for the Docker volume
+docker_lvfilesystem: xfs
+
+# Docker 1.10+ attempts to find the MTU using Path MTU Discovery, but may
+# fail. This can be a problem on virtualised docker hosts. Set to True to
+# use the MTU from the `ansible_default_ipv4` variable.
+# - https://github.com/docker/docker/issues/22028
+# - https://github.com/docker/docker/issues/12565
+# TODO: Add an option to explicitly set the MTU in case auto-detection fails
+docker_use_ipv4_nic_mtu: false
+
+# Dictionary of additional docker options
+docker_additional_options: {}
+
+# Users who can run docker
+docker_groupmembers: []
+
+# Setup systemd services
+docker_systemd_setup: true
+
+# Override the releasever in the yum repo configuration
+docker_repo_force_releasever: '$releasever'
+
+
+######################################################################
+# Expert users only!
+######################################################################
+
+# Use a custom storage driver
+docker_storage_driver: ''
+
+# Enable debug mode on server
+docker_debug: false
diff --git a/omero/roles/ome.docker/handlers/main.yml b/omero/roles/ome.docker/handlers/main.yml
new file mode 100644
index 00000000..cabd7321
--- /dev/null
+++ b/omero/roles/ome.docker/handlers/main.yml
@@ -0,0 +1,9 @@
+---
+# Handlers for docker
+
+- name: restart docker
+ become: true
+ service:
+ name: docker
+ state: restarted
+ when: docker_systemd_setup
diff --git a/omero/roles/ome.docker/meta/.galaxy_install_info b/omero/roles/ome.docker/meta/.galaxy_install_info
new file mode 100644
index 00000000..87b960bd
--- /dev/null
+++ b/omero/roles/ome.docker/meta/.galaxy_install_info
@@ -0,0 +1,2 @@
+install_date: Wed 13 Nov 17:34:01 2024
+version: 3.2.2
diff --git a/omero/roles/ome.docker/meta/main.yml b/omero/roles/ome.docker/meta/main.yml
new file mode 100644
index 00000000..2adaf5dc
--- /dev/null
+++ b/omero/roles/ome.docker/meta/main.yml
@@ -0,0 +1,14 @@
+---
+galaxy_info:
+ author: ome-devel@lists.openmicroscopy.org.uk
+ description: Install upstream Docker
+ company: Open Microscopy Environment
+ license: BSD
+ min_ansible_version: 2.10
+ platforms:
+ - name: EL
+ versions:
+ - 9
+ role_name: docker
+ namespace: ome
+ galaxy_tags: []
diff --git a/omero/roles/ome.docker/meta/requirements.yml b/omero/roles/ome.docker/meta/requirements.yml
new file mode 100644
index 00000000..2d40150a
--- /dev/null
+++ b/omero/roles/ome.docker/meta/requirements.yml
@@ -0,0 +1,2 @@
+---
+- src: ome.lvm_partition
diff --git a/omero/roles/ome.docker/molecule/default/molecule.yml b/omero/roles/ome.docker/molecule/default/molecule.yml
new file mode 100644
index 00000000..83f5e3ef
--- /dev/null
+++ b/omero/roles/ome.docker/molecule/default/molecule.yml
@@ -0,0 +1,50 @@
+---
+dependency:
+ name: galaxy
+driver:
+ name: docker
+lint: |
+ yamllint .
+ ansible-lint
+ flake8
+platforms:
+ # For mysterious reasons naming something "docker" with the current
+ # ome-ansible-molecule versions fails
+ - name: xdockerx
+ image: eniocarboni/docker-rockylinux-systemd:9
+ privileged: true
+ command: /sbin/init
+ groups:
+ - docker-hosts
+ cgroupns_mode: host
+ tmpfs:
+ - /sys/fs/cgroup
+ - name: docker-inactive
+ image: rockylinux:9
+ groups:
+ - docker-hosts
+ privileged: true
+ cgroupns_mode: host
+ tmpfs:
+ - /sys/fs/cgroup
+provisioner:
+ name: ansible
+ lint:
+ name: ansible-lint
+ inventory:
+ group_vars:
+ docker-hosts:
+ # This should allow docker-in-docker to work
+ docker_storage_driver: vfs
+ host_vars:
+ xdockerx:
+ docker_use_ipv4_nic_mtu: true
+ # Latest version 17.12.1.ce-1.el7.centos has a bug that prevents
+ # testing on travis: https://github.com/docker/for-linux/issues/219
+ #docker_version: 17.09.1.ce-1.el7.centos
+ docker-inactive:
+ docker_systemd_setup: false
+scenario:
+ name: default
+verifier:
+ name: testinfra
diff --git a/omero/roles/ome.docker/molecule/default/playbook.yml b/omero/roles/ome.docker/molecule/default/playbook.yml
new file mode 100644
index 00000000..47fe44a9
--- /dev/null
+++ b/omero/roles/ome.docker/molecule/default/playbook.yml
@@ -0,0 +1,16 @@
+---
+- name: Converge
+ hosts: all
+ roles:
+ - role: ome.docker
+ docker_additional_options:
+ hosts:
+ - tcp://127.0.0.1:4243
+ - unix:///var/run/docker.sock
+
+ tasks:
+ - name: create unprivileged user
+ become: true
+ user:
+ name: test
+ state: present
diff --git a/omero/roles/ome.docker/molecule/default/requirements.yml b/omero/roles/ome.docker/molecule/default/requirements.yml
new file mode 100644
index 00000000..1bf542aa
--- /dev/null
+++ b/omero/roles/ome.docker/molecule/default/requirements.yml
@@ -0,0 +1,3 @@
+---
+
+- src: ome.lvm_partition
diff --git a/omero/roles/ome.docker/molecule/default/tests/test_docker-inactive.py b/omero/roles/ome.docker/molecule/default/tests/test_docker-inactive.py
new file mode 100644
index 00000000..d76d5fb7
--- /dev/null
+++ b/omero/roles/ome.docker/molecule/default/tests/test_docker-inactive.py
@@ -0,0 +1,13 @@
+import os
+
+import testinfra.utils.ansible_runner
+
+testinfra_hosts = testinfra.utils.ansible_runner.AnsibleRunner(
+ os.environ['MOLECULE_INVENTORY_FILE']).get_hosts('docker-inactive')
+
+
+def test_docker_installed(host):
+ with host.sudo():
+ cmd = host.command('docker info')
+ assert cmd.rc == 1
+ assert 'ERROR: Cannot connect to the Docker daemon' in cmd.stdout
diff --git a/omero/roles/ome.docker/molecule/default/tests/test_docker.py b/omero/roles/ome.docker/molecule/default/tests/test_docker.py
new file mode 100644
index 00000000..3c677cd0
--- /dev/null
+++ b/omero/roles/ome.docker/molecule/default/tests/test_docker.py
@@ -0,0 +1,40 @@
+import os
+
+import testinfra.utils.ansible_runner
+
+testinfra_hosts = testinfra.utils.ansible_runner.AnsibleRunner(
+ os.environ['MOLECULE_INVENTORY_FILE']).get_hosts('xdockerx')
+
+
+def test_service_running_and_enabled(host):
+ assert host.service('docker').is_running
+ assert host.service('docker').is_enabled
+
+
+def test_docker_info(host):
+ with host.sudo():
+ host.command.check_output('docker info')
+
+
+def test_docker_socket_unprivileged(host):
+ with host.sudo('test'):
+ r = host.command('docker info')
+ assert r.rc > 0
+ assert 'permission denied' in r.stdout
+
+
+def test_docker_tcp_unprivileged(host):
+ with host.sudo('test'):
+ host.command.check_output(
+ 'DOCKER_HOST=tcp://127.0.0.1:4243 docker info')
+
+
+def test_docker_run(host):
+ with host.sudo():
+ out = host.command.check_output('docker run busybox id')
+ assert out == 'uid=0(root) gid=0(root) groups=0(root),10(wheel)'
+
+
+def test_docker_package(host):
+ assert host.package('docker-ce').is_installed
+ assert not host.package('docker').is_installed
diff --git a/omero/roles/ome.docker/tasks/main.yml b/omero/roles/ome.docker/tasks/main.yml
new file mode 100644
index 00000000..6dc5c31b
--- /dev/null
+++ b/omero/roles/ome.docker/tasks/main.yml
@@ -0,0 +1,83 @@
+---
+# Setup a Docker node
+- ansible.builtin.import_tasks: pre_tasks.yml
+
+- name: docker | Import a key for docker
+ become: true
+ ansible.builtin.rpm_key:
+ state: present
+ key: https://download.docker.com/linux/centos/gpg
+
+- name: docker | setup dnf repository
+ become: true
+ ansible.builtin.dnf:
+ update_cache: true
+ name:
+ - https://download.docker.com/linux/centos/9/x86_64/stable/Packages/containerd.io-1.6.24-3.1.el9.x86_64.rpm
+ - https://download.docker.com/linux/centos/9/x86_64/stable/Packages/docker-ce-20.10.24-3.el9.x86_64.rpm
+ - https://download.docker.com/linux/centos/9/x86_64/stable/Packages/docker-ce-cli-20.10.24-3.el9.x86_64.rpm
+ - https://download.docker.com/linux/centos/9/x86_64/stable/Packages/docker-scan-plugin-0.23.0-3.el9.x86_64.rpm
+ - https://download.docker.com/linux/centos/9/x86_64/stable/Packages/docker-ce-rootless-extras-24.0.7-1.el9.x86_64.rpm
+ state: present
+
+- name: docker | install docker
+ become: true
+ ansible.builtin.yum:
+ update_cache: true
+ name:
+ - docker-ce
+ state: present
+
+- name: docker | setup lvm docker-pool
+ become: true
+ lvol:
+ vg: "{{ docker_vgname }}"
+ lv: docker-pool
+ size: "{{ docker_poolsize }}"
+ opts: >
+ {{ docker_lvopts | default(None) }} --thin --poolmetadatasize
+ {{ docker_metadatasize }}
+ when: docker_use_custom_storage
+
+- name: docker | configuration directory
+ become: true
+ file:
+ path: /etc/docker
+ state: directory
+ mode: 0755
+
+# https://docs.docker.com/engine/reference/commandline/dockerd/#linux-configuration-file
+- name: docker | configure docker options
+ become: true
+ template:
+ src: etc-docker-daemon-json.j2
+ dest: /etc/docker/daemon.json
+ backup: true
+ mode: 0644
+ notify:
+ - restart docker
+
+# change service file to remove _H options from service file to be able to use daemon.json
+- name: docker | remove options from service
+ become: true
+ ansible.builtin.replace:
+ path: /usr/lib/systemd/system/docker.service
+ regexp: ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock
+ replace: ExecStart=/usr/bin/dockerd
+
+- name: docker | enable
+ become: true
+ ansible.builtin.service:
+ name: docker
+ state: started
+ enabled: true
+ # ignore_errors: true
+ when: docker_systemd_setup
+
+- name: docker | group members
+ become: true
+ ansible.builtin.user:
+ name: "{{ item }}"
+ groups: docker
+ append: true
+ with_items: "{{ docker_groupmembers }}"
diff --git a/omero/roles/ome.docker/tasks/pre_tasks.yml b/omero/roles/ome.docker/tasks/pre_tasks.yml
new file mode 100644
index 00000000..1c38b289
--- /dev/null
+++ b/omero/roles/ome.docker/tasks/pre_tasks.yml
@@ -0,0 +1,13 @@
+---
+
+- name: Set up partition
+ ansible.builtin.include_role:
+ name: ome.lvm_partition
+ vars:
+ lvm_vgname: "{{ docker_vgname }}"
+ lvm_lvname: docker-volume
+ lvm_lvmount: /var/lib/docker
+ lvm_lvsize: "{{ docker_volumesize }}"
+ lvm_lvfilesystem: "{{ docker_lvfilesystem }}"
+ lvm_lvopts: "{{ docker_lvopts | default(None) }}"
+ when: docker_use_custom_storage
diff --git a/omero/roles/ome.docker/templates/etc-docker-daemon-json.j2 b/omero/roles/ome.docker/templates/etc-docker-daemon-json.j2
new file mode 100644
index 00000000..6ee32576
--- /dev/null
+++ b/omero/roles/ome.docker/templates/etc-docker-daemon-json.j2
@@ -0,0 +1,27 @@
+{
+{% if docker_use_custom_storage %}
+ "storage-driver": "devicemapper",
+ "storage-opts": [
+ "dm.fs={{ docker_basefs }}",
+ "dm.thinpooldev=/dev/mapper/{{ docker_vgname }}-docker--pool"
+ ],
+{% endif %}
+{% if docker_storage_driver | length > 0 %}
+ "storage-driver": "{{ docker_storage_driver }}",
+{% endif %}
+
+{% if docker_use_custom_network %}
+ "bridge": "{{ docker_bridge_name }}",
+ "fixed-cidr": "{{ docker_bridge_ips }}",
+{% endif %}
+
+{% if docker_use_ipv4_nic_mtu %}
+ "mtu": {{ ansible_default_ipv4.mtu }},
+{% endif %}
+
+{% for key in docker_additional_options %}
+ "{{ key }}": {{ docker_additional_options[key] | to_json }},
+{% endfor %}
+
+ "debug": {{ docker_debug | bool | to_json }}
+}
diff --git a/omero/roles/ome.ice/.github/workflows/molecule.yml b/omero/roles/ome.ice/.github/workflows/molecule.yml
new file mode 100644
index 00000000..f1ea4c82
--- /dev/null
+++ b/omero/roles/ome.ice/.github/workflows/molecule.yml
@@ -0,0 +1,55 @@
+---
+name: Molecule
+# yamllint disable-line rule:truthy
+on:
+ push:
+ pull_request:
+ schedule:
+ - cron: '29 21 * * 0'
+
+jobs:
+
+ list-scenarios:
+ runs-on: ubuntu-22.04
+ outputs:
+ matrix: ${{ steps.listscenarios.outputs.scenarios }}
+ steps:
+ - uses: actions/checkout@v4
+ - id: listscenarios
+ uses: ome/action-ansible-molecule-list-scenarios@main
+
+ test:
+ name: Test
+ needs:
+ - list-scenarios
+ runs-on: ubuntu-22.04
+ strategy:
+ # Keep running so we can see if other tests pass
+ fail-fast: false
+ matrix:
+ scenario: ${{fromJson(needs.list-scenarios.outputs.matrix)}}
+ steps:
+ - uses: actions/checkout@v4
+ - uses: actions/setup-python@v5
+ with:
+ python-version: '3.9'
+ - name: Install Ansible & Molecule
+ run: |
+ pip install "ansible<8" "ansible-lint<6.13" flake8
+ pip install "molecule<5" "ansible-compat<4"
+ pip install molecule-plugins[docker] pytest-testinfra
+ - name: Run molecule
+ run: molecule test -s "${{ matrix.scenario }}"
+
+ publish:
+ name: Galaxy
+ if: startsWith(github.ref, 'refs/tags')
+ needs:
+ - test
+ runs-on: ubuntu-22.04
+ steps:
+ - name: galaxy
+ uses: ansible-actions/ansible-galaxy-action@v1.2.0
+ with:
+ galaxy_api_key: ${{ secrets.GALAXY_API_KEY }}
+ galaxy_version: ${{ github.ref_name }}
diff --git a/omero/roles/ome.ice/.gitignore b/omero/roles/ome.ice/.gitignore
new file mode 100644
index 00000000..d35ab669
--- /dev/null
+++ b/omero/roles/ome.ice/.gitignore
@@ -0,0 +1,2 @@
+.*~
+*.pyc
diff --git a/omero/roles/ome.ice/LICENSE.md b/omero/roles/ome.ice/LICENSE.md
new file mode 100644
index 00000000..a3181283
--- /dev/null
+++ b/omero/roles/ome.ice/LICENSE.md
@@ -0,0 +1,22 @@
+Copyright (c) 2019, University of Dundee
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/omero/roles/ome.ice/README.md b/omero/roles/ome.ice/README.md
new file mode 100644
index 00000000..74f6a4b0
--- /dev/null
+++ b/omero/roles/ome.ice/README.md
@@ -0,0 +1,39 @@
+Role Name
+=========
+
+[![Actions Status](https://github.com/ome/ansible-role-ice/workflows/Molecule/badge.svg)](https://github.com/ome/ansible-role-ice/actions)
+[![Ansible Role](https://img.shields.io/badge/ansible--galaxy-ice-blue.svg)](https://galaxy.ansible.com/ui/standalone/roles/ome/ice/)
+
+Install Zeroc Ice.
+
+On Ubuntu this only installs the Ice binaries and required libraries under `/opt/ice/bin` (note this is a symlink).
+
+
+Role Variables
+--------------
+
+Optional (expert users only):
+- `ice_install_devel`: Install Ice development packages, default `True`
+- `ice_install_python`: Install Ice Python globally, default `True`, ignored on Ubuntu and CentOS 8 (always `False`)
+- `ice_python_wheel`: URL to a python wheel package to be installed, ignored on Ubuntu and CentOS 8.
+ You can use this to provide a precompiled ice-py package for 3.6 as an alternative to automatically compiling from the source package.
+- `ice_binaries_symlink_dest`: Symlink the Ice binaries required by OMERO into this directory e.g. `/usr/local/bin` (Ubuntu and CentOS 8 only, must exist, if empty don't create symlinks)
+
+
+Notes
+-----
+Note that 3.6 requires ice-python to be installed using pip, and will result in the installation of several development tools and libraries unless `ice_python_wheel` is provided.
+
+
+Example Playbook
+----------------
+
+ - hosts: localhost
+ roles:
+ - role: ome.ice
+
+
+Author Information
+------------------
+
+ome-devel@lists.openmicroscopy.org.uk
diff --git a/omero/roles/ome.ice/defaults/main.yml b/omero/roles/ome.ice/defaults/main.yml
new file mode 100644
index 00000000..7aed77d1
--- /dev/null
+++ b/omero/roles/ome.ice/defaults/main.yml
@@ -0,0 +1,22 @@
+---
+# defaults file for ice
+
+ice_install_devel: true
+ice_install_python: true
+ice_python_wheel: ''
+
+# Symlink the main ice binaries into this directory
+ice_binaries_symlink_dest: ''
+
+
+######################################################################
+# Expert users only!
+######################################################################
+
+ice_zeroc_ice_ubuntu2204_version: '20221004'
+ice_zeroc_ice_ubuntu2204_sha256: >-
+ 05e5148f7df0dfc6f06ee2e4445419f95d5b19e4fc8c99660d93af13fb14e709
+
+ice_zeroc_ice_rockylinux9_version: '20231130'
+ice_zeroc_ice_rockylinux9_sha256: >-
+ 9da10544e46dc95197e670dda4df18200d80991c404b2a2692d635ab59896c5d
diff --git a/omero/roles/ome.ice/meta/.galaxy_install_info b/omero/roles/ome.ice/meta/.galaxy_install_info
new file mode 100644
index 00000000..71fb6383
--- /dev/null
+++ b/omero/roles/ome.ice/meta/.galaxy_install_info
@@ -0,0 +1,2 @@
+install_date: Wed 13 Nov 17:33:44 2024
+version: 4.4.4
diff --git a/omero/roles/ome.ice/meta/main.yml b/omero/roles/ome.ice/meta/main.yml
new file mode 100644
index 00000000..73d5b8f4
--- /dev/null
+++ b/omero/roles/ome.ice/meta/main.yml
@@ -0,0 +1,17 @@
+---
+galaxy_info:
+ author: ome-devel@lists.openmicroscopy.org.uk
+ description: ZeroC Ice including Python
+ company: Open Microscopy Environment
+ license: BSD
+ min_ansible_version: 2.10
+ platforms:
+ - name: EL
+ versions:
+ - 9
+ - name: Ubuntu
+ versions:
+ - jammy
+ namespace: ome
+ role_name: ice
+ galaxy_tags: []
diff --git a/omero/roles/ome.ice/meta/requirements.yml b/omero/roles/ome.ice/meta/requirements.yml
new file mode 100644
index 00000000..e47c05a3
--- /dev/null
+++ b/omero/roles/ome.ice/meta/requirements.yml
@@ -0,0 +1,4 @@
+---
+- src: ome.deploy_archive
+- src: ome.basedeps
+- src: ome.java
diff --git a/omero/roles/ome.ice/molecule/ice36-ubuntu2204/molecule.yml b/omero/roles/ome.ice/molecule/ice36-ubuntu2204/molecule.yml
new file mode 100644
index 00000000..c47e650e
--- /dev/null
+++ b/omero/roles/ome.ice/molecule/ice36-ubuntu2204/molecule.yml
@@ -0,0 +1,32 @@
+---
+dependency:
+ name: galaxy
+ options:
+ role-file: molecule/resources/requirements.yml
+driver:
+ name: docker
+lint: |
+ yamllint .
+ ansible-lint
+ flake8
+platforms:
+ - name: ice36-ubuntu2204
+ image: ubuntu:22.04
+provisioner:
+ name: ansible
+ lint:
+ name: ansible-lint
+ playbooks:
+ converge: ../resources/playbook.yml
+ inventory:
+ host_vars:
+ ice36-ubuntu2204:
+ ice_install_devel: false
+ ice_binaries_symlink_dest: /usr/local/bin
+scenario:
+ name: ice36-ubuntu2204
+ converge_sequence:
+ - converge
+verifier:
+ name: testinfra
+ directory: ../resources/tests-built/
diff --git a/omero/roles/ome.ice/molecule/ice36all/molecule.yml b/omero/roles/ome.ice/molecule/ice36all/molecule.yml
new file mode 100644
index 00000000..88387525
--- /dev/null
+++ b/omero/roles/ome.ice/molecule/ice36all/molecule.yml
@@ -0,0 +1,28 @@
+---
+dependency:
+ name: galaxy
+ options:
+ role-file: molecule/resources/requirements.yml
+driver:
+ name: docker
+lint: |
+ yamllint .
+ ansible-lint
+ flake8
+platforms:
+ - name: ice36all
+ image: rockylinux:9
+provisioner:
+ name: ansible
+ lint:
+ name: ansible-lint
+ playbooks:
+ converge: ../resources/playbook.yml
+ inventory:
+ host_vars:
+ ice36all:
+ ice_binaries_symlink_dest: /usr/local/bin
+scenario:
+ name: ice36all
+verifier:
+ name: testinfra
diff --git a/omero/roles/ome.ice/molecule/ice36all/tests/test_default.py b/omero/roles/ome.ice/molecule/ice36all/tests/test_default.py
new file mode 100644
index 00000000..9cc1a128
--- /dev/null
+++ b/omero/roles/ome.ice/molecule/ice36all/tests/test_default.py
@@ -0,0 +1,22 @@
+import os
+
+import testinfra.utils.ansible_runner
+
+testinfra_hosts = testinfra.utils.ansible_runner.AnsibleRunner(
+ os.environ['MOLECULE_INVENTORY_FILE']).get_hosts('all')
+
+
+def test_ice_version(host):
+ assert host.exists('icegridnode')
+ c = host.run('icegridnode --version')
+ assert c.rc == 0
+ assert c.stderr.startswith('3.6.')
+
+
+def test_icepy_version(host):
+ c = host.run('python -c "import Ice; print (Ice.stringVersion())"')
+ assert c.stdout.startswith('3.6.')
+
+
+def test_ice_devel(host):
+ assert not host.package('ice-all-devel').is_installed
diff --git a/omero/roles/ome.ice/molecule/ice36minimal/molecule.yml b/omero/roles/ome.ice/molecule/ice36minimal/molecule.yml
new file mode 100644
index 00000000..6ee2792e
--- /dev/null
+++ b/omero/roles/ome.ice/molecule/ice36minimal/molecule.yml
@@ -0,0 +1,30 @@
+---
+dependency:
+ name: galaxy
+ options:
+ role-file: molecule/resources/requirements.yml
+driver:
+ name: docker
+lint: |
+ yamllint .
+ ansible-lint
+ flake8
+platforms:
+ - name: ice36minimal
+ image: rockylinux:9
+provisioner:
+ name: ansible
+ lint:
+ name: ansible-lint
+ playbooks:
+ converge: ../resources/playbook.yml
+ inventory:
+ host_vars:
+ ice36minimal:
+ ice_install_devel: false
+ ice_install_python: false
+ ice_binaries_symlink_dest: /usr/local/bin
+scenario:
+ name: ice36minimal
+verifier:
+ name: testinfra
diff --git a/omero/roles/ome.ice/molecule/ice36minimal/tests/test_default.py b/omero/roles/ome.ice/molecule/ice36minimal/tests/test_default.py
new file mode 100644
index 00000000..a324a8df
--- /dev/null
+++ b/omero/roles/ome.ice/molecule/ice36minimal/tests/test_default.py
@@ -0,0 +1,23 @@
+import os
+
+import testinfra.utils.ansible_runner
+
+testinfra_hosts = testinfra.utils.ansible_runner.AnsibleRunner(
+ os.environ['MOLECULE_INVENTORY_FILE']).get_hosts('all')
+
+
+def test_ice_version(host):
+ assert host.exists('icegridnode')
+ c = host.run('icegridnode --version')
+ assert c.rc == 0
+ assert c.stderr.startswith('3.6.')
+
+
+def test_icepy_version(host):
+ c = host.run('python -c "import Ice"')
+ assert c.rc == 1
+ assert "No module named \'Ice\'" in c.stderr
+
+
+def test_ice_devel(host):
+ assert not host.package('ice-all-devel').is_installed
diff --git a/omero/roles/ome.ice/molecule/resources/playbook.yml b/omero/roles/ome.ice/molecule/resources/playbook.yml
new file mode 100644
index 00000000..f7fd5655
--- /dev/null
+++ b/omero/roles/ome.ice/molecule/resources/playbook.yml
@@ -0,0 +1,5 @@
+---
+- name: Converge
+ hosts: all
+ roles:
+ - role: ome.ice
diff --git a/omero/roles/ome.ice/molecule/resources/requirements.yml b/omero/roles/ome.ice/molecule/resources/requirements.yml
new file mode 100755
index 00000000..e47c05a3
--- /dev/null
+++ b/omero/roles/ome.ice/molecule/resources/requirements.yml
@@ -0,0 +1,4 @@
+---
+- src: ome.deploy_archive
+- src: ome.basedeps
+- src: ome.java
diff --git a/omero/roles/ome.ice/molecule/resources/tests-built/test_default.py b/omero/roles/ome.ice/molecule/resources/tests-built/test_default.py
new file mode 100644
index 00000000..892c5121
--- /dev/null
+++ b/omero/roles/ome.ice/molecule/resources/tests-built/test_default.py
@@ -0,0 +1,19 @@
+import os
+import pytest
+
+import testinfra.utils.ansible_runner
+
+testinfra_hosts = testinfra.utils.ansible_runner.AnsibleRunner(
+ os.environ['MOLECULE_INVENTORY_FILE']).get_hosts('all')
+
+
+@pytest.mark.parametrize('binary', [
+ 'glacier2router',
+ 'icegridadmin',
+ 'icegridnode',
+])
+def test_ice_version(host, binary):
+ assert host.exists('icegridnode')
+ c = host.run('icegridnode --version')
+ assert c.rc == 0
+ assert c.stderr.startswith('3.6.5')
diff --git a/omero/roles/ome.ice/tasks/debian.yml b/omero/roles/ome.ice/tasks/debian.yml
new file mode 100644
index 00000000..d4ec1c24
--- /dev/null
+++ b/omero/roles/ome.ice/tasks/debian.yml
@@ -0,0 +1,15 @@
+---
+# tasks file for roles/ice
+
+# Note download is handled by the ome.deploy_archive dependency
+
+- name: zeroc ice | dependencies
+ become: true
+ ansible.builtin.apt:
+ update_cache: true
+ name:
+ - libdb5.3
+ - libdb5.3++
+ - libexpat1
+ - libmcpp0
+ - openssl
diff --git a/omero/roles/ome.ice/tasks/main.yml b/omero/roles/ome.ice/tasks/main.yml
new file mode 100644
index 00000000..9d4d40a4
--- /dev/null
+++ b/omero/roles/ome.ice/tasks/main.yml
@@ -0,0 +1,28 @@
+---
+- import_tasks: pre_tasks.yml
+
+- import_tasks: redhat9.yml
+ when: >-
+ (ansible_os_family | lower == 'redhat') and
+ (ansible_distribution_major_version == '9')
+
+- import_tasks: debian.yml
+ when: ansible_os_family | lower == 'debian'
+
+- name: zeroc ice | Display all variables/facts known for a host
+ ansible.builtin.debug:
+ msg: System {{ inventory_hostname }} has {{ ice_binaries_symlink_dest }}
+
+
+- name: zeroc ice | symlink binaries
+ become: true
+ file:
+ force: true
+ path: "{{ ice_binaries_symlink_dest }}/{{ item }}"
+ src: /opt/ice/bin/{{ item }}
+ state: link
+ when: 'ice_binaries_symlink_dest | length > 0'
+ with_items:
+ - glacier2router
+ - icegridadmin
+ - icegridnode
diff --git a/omero/roles/ome.ice/tasks/pre_tasks.yml b/omero/roles/ome.ice/tasks/pre_tasks.yml
new file mode 100644
index 00000000..254c83c3
--- /dev/null
+++ b/omero/roles/ome.ice/tasks/pre_tasks.yml
@@ -0,0 +1,51 @@
+---
+
+- name: zeroc ice | Include ome.basedeps role
+ include_role:
+ name: ome.basedeps
+ when: >-
+ (ansible_os_family | lower == 'redhat') and
+ (ansible_distribution_major_version == '9')
+
+- name: zeroc ice | Include ome.java
+ include_role:
+ name: ome.java
+ when: >-
+ (ansible_os_family | lower == 'redhat') and
+ (ansible_distribution_major_version == '9')
+
+
+- name: zeroc ice | Include ome.jadeploy_archiveva debian
+ include_role:
+ name: ome.deploy_archive
+ vars:
+ ansible_become: yes
+ deploy_archive_dest_dir: /opt
+ deploy_archive_src_url: "https://github.com/\
+ glencoesoftware/zeroc-ice-ubuntu2204-x86_64/releases/\
+ download/{{ ice_zeroc_ice_ubuntu2204_version }}/Ice-3.6.5-ubuntu2204-x86_64.tar.gz"
+ deploy_archive_sha256: "{{ ice_zeroc_ice_ubuntu2204_sha256 }}"
+ deploy_archive_symlink: /opt/ice
+ deploy_archive_internal_root: >-
+ Ice-3.6.5
+ when: >-
+ (ansible_os_family | lower == 'debian') and
+ (ansible_distribution_major_version == '22')
+
+
+- name: zeroc ice | Include ome.jadeploy_archiveva redhat
+ include_role:
+ name: ome.deploy_archive
+ vars:
+ ansible_become: yes
+ deploy_archive_dest_dir: /opt
+ deploy_archive_src_url: "https://github.com/\
+glencoesoftware/zeroc-ice-rhel9-x86_64/releases/\
+download/{{ ice_zeroc_ice_rockylinux9_version }}/Ice-3.6.5-rhel9-x86_64.tar.gz"
+ deploy_archive_sha256: "{{ ice_zeroc_ice_rockylinux9_sha256 }}"
+ deploy_archive_symlink: /opt/ice
+ deploy_archive_internal_root: >-
+ Ice-3.6.5
+ when: >-
+ (ansible_os_family | lower == 'redhat') and
+ (ansible_distribution_major_version == '9')
diff --git a/omero/roles/ome.ice/tasks/redhat9.yml b/omero/roles/ome.ice/tasks/redhat9.yml
new file mode 100644
index 00000000..56cecbec
--- /dev/null
+++ b/omero/roles/ome.ice/tasks/redhat9.yml
@@ -0,0 +1,100 @@
+---
+# tasks file for roles/ice
+
+
+
+#- name: zeroc ice | set-enabled crb
+# become: true
+# ansible.builtin.command: dnf config-manager --set-enabled crb
+# state: present
+
+- name : zeroc ice | check rocky.repo file
+ stat:
+ path: /etc/yum.repos.d/rocky.repo
+ register: rockyrepo_name
+
+- name: zeroc ice | install config-manager in rocky
+ become: true
+ ansible.builtin.dnf:
+ update_cache: true
+ name:
+ - 'dnf-command(config-manager)'
+ state: present
+ when: rockyrepo_name.stat.exists
+
+- name: zeroc ice | Enable crp repo in rocky
+ become: true
+ ansible.builtin.replace:
+ path: /etc/yum.repos.d/rocky.repo
+ # create: false
+ regexp: enabled=0
+ replace: enabled=1
+ # state: present
+ when: rockyrepo_name.stat.exists
+
+- name: zeroc ice | Add CRB repository for RHEL
+ become: true
+ ansible.builtin.command:
+ subscription-manager repos --enable codeready-builder-for-rhel-9-x86_64-rpms
+ when: ansible_os_family == 'RedHat' and not rockyrepo_name.stat.exists
+
+- name: zeroc ice | install libdb
+ become: true
+ ansible.builtin.dnf:
+ update_cache: true
+ name:
+ - libdb-cxx
+ state: present
+
+# - name: zeroc ice | command echo lib64
+# ansible.builtin.command: echo /opt/ice-3.6.5/lib64 > /etc/ld.so.conf.d/ice-x86_64.conf
+# become: true
+
+# - name: zeroc ice | command ldconfig
+# ansible.builtin.command: ldconfig
+# become: true
+
+- name: zero ice | install python
+ become: true
+ ansible.builtin.dnf:
+ update_cache: true
+ name: python
+ state: present
+
+- name: zeroc ice | install ice pip dependencies
+ become: true
+ ansible.builtin.dnf:
+ update_cache: true
+ name:
+ - gcc
+ - gcc-c++
+ - libdb-utils
+ - bzip2-devel
+ - expat-devel
+ - openssl-devel
+ - python-devel
+ - python-pip
+ state: present
+ when: ice_install_python and (ice_python_wheel | length < 1)
+
+- name: zeroc ice | pip install packages
+ become: true
+ pip:
+ name:
+ - "zeroc-ice>=3.6,<3.7"
+ state: present
+ when: ice_install_python and (ice_python_wheel | length < 1)
+
+- name: zeroc ice | install python-pip for wheel
+ become: true
+ yum:
+ name: python-pip
+ state: present
+ when: ice_install_python and (ice_python_wheel | length >= 1)
+
+- name: zeroc ice | install ice wheel package
+ become: true
+ pip:
+ name: "{{ ice_python_wheel }}"
+ state: present
+ when: ice_install_python and (ice_python_wheel | length >= 1)
diff --git a/omero/roles/ome.java/.github/workflows/molecule.yml b/omero/roles/ome.java/.github/workflows/molecule.yml
new file mode 100644
index 00000000..2b19bf44
--- /dev/null
+++ b/omero/roles/ome.java/.github/workflows/molecule.yml
@@ -0,0 +1,58 @@
+---
+name: Molecule
+# yamllint disable-line rule:truthy
+on:
+ push:
+ pull_request:
+ schedule:
+ - cron: '29 21 * * 0'
+
+jobs:
+
+ list-scenarios:
+ runs-on: ubuntu-latest
+ outputs:
+ matrix: ${{ steps.listscenarios.outputs.scenarios }}
+ steps:
+ - uses: actions/checkout@v4
+ - id: listscenarios
+ uses: ome/action-ansible-molecule-list-scenarios@main
+
+ test:
+ name: Test
+ needs:
+ - list-scenarios
+ runs-on: ubuntu-20.04
+ strategy:
+ # Keep running so we can see if other tests pass
+ fail-fast: false
+ matrix:
+ scenario: ${{fromJson(needs.list-scenarios.outputs.matrix)}}
+ steps:
+ - uses: actions/checkout@v4
+ - uses: actions/setup-python@v4
+ with:
+ python-version: '3.8'
+ - name: Install Ansible & Molecule
+ run: |
+ pip install "ansible<8" "ansible-lint<6.13" flake8
+ pip install "molecule<5" "ansible-compat<4"
+ pip install molecule-plugins[docker] pytest-testinfra
+ - name: Run molecule
+ run: molecule test -s "${{ matrix.scenario }}"
+
+ publish:
+ name: Galaxy
+ if: startsWith(github.ref, 'refs/tags')
+ needs:
+ - test
+ runs-on: ubuntu-20.04
+ steps:
+ - name: galaxy
+ uses: robertdebock/galaxy-action@1.2.1
+ with:
+ galaxy_api_key: ${{ secrets.GALAXY_API_KEY }}
+ git_branch: ${{ github.ref_name }}
+
+# notifications:
+# webhooks: https://galaxy.ansible.com/api/v1/notifications/
diff --git a/omero/roles/ome.java/.gitignore b/omero/roles/ome.java/.gitignore
new file mode 100644
index 00000000..0bf56261
--- /dev/null
+++ b/omero/roles/ome.java/.gitignore
@@ -0,0 +1,2 @@
+.*~
+__pycache__
diff --git a/omero/roles/ome.java/LICENSE.md b/omero/roles/ome.java/LICENSE.md
new file mode 100644
index 00000000..a3181283
--- /dev/null
+++ b/omero/roles/ome.java/LICENSE.md
@@ -0,0 +1,22 @@
+Copyright (c) 2019, University of Dundee
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/omero/roles/ome.java/README.md b/omero/roles/ome.java/README.md
new file mode 100644
index 00000000..dea4e7ac
--- /dev/null
+++ b/omero/roles/ome.java/README.md
@@ -0,0 +1,35 @@
+Java
+====
+
+[![Actions Status](https://github.com/ome/ansible-role-java/workflows/Molecule/badge.svg)](https://github.com/ome/ansible-role-java/actions)
+[![Ansible Role](https://img.shields.io/badge/ansible--galaxy-java-blue.svg)](https://galaxy.ansible.com/ui/standalone/roles/ome/java/)
+
+Install Java JREs and optionally JDKs.
+
+
+Role Variables
+--------------
+
+Optional variables:
+- `java_versions`: A list of Java versions to install, default `["8"]`,
+ versions other than `"8"` and `"11"` may work but are not supported
+- `java_jdk_install`: If `True` install JDKs corresponding to the JRE versions, default `False`
+
+
+Example Playbook
+----------------
+
+ - hosts: servers
+ roles:
+ - { role: ome.java }
+
+
+Author Information
+------------------
+
+ome-devel@lists.openmicroscopy.org.uk
+
+License
+-------
+
+BSD
diff --git a/omero/roles/ome.java/defaults/main.yml b/omero/roles/ome.java/defaults/main.yml
new file mode 100644
index 00000000..44083277
--- /dev/null
+++ b/omero/roles/ome.java/defaults/main.yml
@@ -0,0 +1,9 @@
+---
+# defaults file for roles/java
+
+# List of Java versions to install
+java_versions:
+ - "8"
+
+# Install JDKs corresponding to JREs?
+java_jdk_install: false
diff --git a/omero/roles/ome.java/meta/.galaxy_install_info b/omero/roles/ome.java/meta/.galaxy_install_info
new file mode 100644
index 00000000..17229187
--- /dev/null
+++ b/omero/roles/ome.java/meta/.galaxy_install_info
@@ -0,0 +1,2 @@
+install_date: Wed 13 Nov 17:33:42 2024
+version: 2.2.0
diff --git a/omero/roles/ome.java/meta/main.yml b/omero/roles/ome.java/meta/main.yml
new file mode 100644
index 00000000..6d21b103
--- /dev/null
+++ b/omero/roles/ome.java/meta/main.yml
@@ -0,0 +1,21 @@
+---
+galaxy_info:
+ role_name: java
+ author: ome-devel@lists.openmicroscopy.org.uk
+ description: Install Java JREs and optionally JDKs.
+ company: Open Microscopy Environment
+ license: BSD
+ min_ansible_version: 2.4
+ platforms:
+ - name: EL
+ versions:
+ - 9
+ - name: Ubuntu
+ versions:
+ - jammy
+ namespace: ome
+ galaxy_tags:
+ - system
+ - java
+ - jdk
+ - openjdk
diff --git a/omero/roles/ome.java/molecule/resources/playbook.yml b/omero/roles/ome.java/molecule/resources/playbook.yml
new file mode 100644
index 00000000..20520b93
--- /dev/null
+++ b/omero/roles/ome.java/molecule/resources/playbook.yml
@@ -0,0 +1,5 @@
+---
+- name: Converge
+ hosts: all
+ roles:
+ - role: ome.java
diff --git a/omero/roles/ome.java/molecule/resources/tests/test_jdk.py b/omero/roles/ome.java/molecule/resources/tests/test_jdk.py
new file mode 100644
index 00000000..a24268b5
--- /dev/null
+++ b/omero/roles/ome.java/molecule/resources/tests/test_jdk.py
@@ -0,0 +1,20 @@
+import os
+
+import testinfra.utils.ansible_runner
+
+testinfra_hosts = testinfra.utils.ansible_runner.AnsibleRunner(
+ os.environ['MOLECULE_INVENTORY_FILE']).get_hosts('java-jdk')
+
+
+def test_jre_version(host):
+ assert host.exists('java')
+ c = host.run('java -version')
+ assert c.rc == 0
+ assert c.stderr.startswith('openjdk version "1.8.0')
+
+
+def test_jdk_version(host):
+ assert host.exists('javac')
+ c = host.run('javac -version')
+ assert c.rc == 0
+ assert c.stderr.startswith('javac 1.8.0')
diff --git a/omero/roles/ome.java/molecule/resources/tests/test_jdk11.py b/omero/roles/ome.java/molecule/resources/tests/test_jdk11.py
new file mode 100644
index 00000000..25232734
--- /dev/null
+++ b/omero/roles/ome.java/molecule/resources/tests/test_jdk11.py
@@ -0,0 +1,21 @@
+import os
+
+import testinfra.utils.ansible_runner
+
+testinfra_hosts = testinfra.utils.ansible_runner.AnsibleRunner(
+ os.environ['MOLECULE_INVENTORY_FILE']).get_hosts('java-jdk11')
+
+
+def test_jre_version(host):
+ assert host.exists('java')
+ c = host.run('java -version')
+ assert c.rc == 0
+ assert c.stderr.startswith('openjdk version "11.0')
+
+
+def test_jdk_version(host):
+ assert host.exists('javac')
+ c = host.run('javac -version')
+ assert c.rc == 0
+ # Output has changed from stderr to stdout for javac 11 only
+ assert c.stdout.startswith('javac 11.0')
diff --git a/omero/roles/ome.java/molecule/resources/tests/test_jre.py b/omero/roles/ome.java/molecule/resources/tests/test_jre.py
new file mode 100644
index 00000000..2148a183
--- /dev/null
+++ b/omero/roles/ome.java/molecule/resources/tests/test_jre.py
@@ -0,0 +1,17 @@
+import os
+
+import testinfra.utils.ansible_runner
+
+testinfra_hosts = testinfra.utils.ansible_runner.AnsibleRunner(
+ os.environ['MOLECULE_INVENTORY_FILE']).get_hosts('java-jre')
+
+
+def test_jre_version(host):
+ assert host.exists('java')
+ c = host.run('java -version')
+ assert c.rc == 0
+ assert c.stderr.startswith('openjdk version "1.8.0')
+
+
+def test_jdk_version(host):
+ assert not host.exists('javac')
diff --git a/omero/roles/ome.java/molecule/resources/tests/test_jre11.py b/omero/roles/ome.java/molecule/resources/tests/test_jre11.py
new file mode 100644
index 00000000..c47ed7bb
--- /dev/null
+++ b/omero/roles/ome.java/molecule/resources/tests/test_jre11.py
@@ -0,0 +1,17 @@
+import os
+
+import testinfra.utils.ansible_runner
+
+testinfra_hosts = testinfra.utils.ansible_runner.AnsibleRunner(
+ os.environ['MOLECULE_INVENTORY_FILE']).get_hosts('java-jre11')
+
+
+def test_jre_version(host):
+ assert host.exists('java')
+ c = host.run('java -version')
+ assert c.rc == 0
+ assert c.stderr.startswith('openjdk version "11.0.')
+
+
+def test_jdk_version(host):
+ assert not host.exists('javac')
diff --git a/omero/roles/ome.java/molecule/rockylinux-9/molecule.yml b/omero/roles/ome.java/molecule/rockylinux-9/molecule.yml
new file mode 100644
index 00000000..a14d3885
--- /dev/null
+++ b/omero/roles/ome.java/molecule/rockylinux-9/molecule.yml
@@ -0,0 +1,41 @@
+---
+dependency:
+ name: galaxy
+driver:
+ name: docker
+lint: |
+ yamllint .
+ ansible-lint
+ flake8
+platforms:
+ - name: java-jre
+ image: rockylinux:9
+ - name: java-jdk
+ image: rockylinux:9
+ - name: java-jre11
+ image: rockylinux:9
+ - name: java-jdk11
+ image: rockylinux:9
+provisioner:
+ name: ansible
+ playbooks:
+ converge: ../resources/playbook.yml
+ lint:
+ name: ansible-lint
+ inventory:
+ host_vars:
+ java-jdk:
+ java_jdk_install: true
+ java-jre11:
+ java_versions:
+ - "11"
+ java-jdk11:
+ java_versions:
+ - "11"
+ java_jdk_install: true
+
+scenario:
+ name: rockylinux-9
+verifier:
+ name: testinfra
+ directory: ../resources/tests/
diff --git a/omero/roles/ome.java/molecule/ubuntu-2204/molecule.yml b/omero/roles/ome.java/molecule/ubuntu-2204/molecule.yml
new file mode 100644
index 00000000..86e0571b
--- /dev/null
+++ b/omero/roles/ome.java/molecule/ubuntu-2204/molecule.yml
@@ -0,0 +1,41 @@
+---
+dependency:
+ name: galaxy
+driver:
+ name: docker
+lint: |
+ yamllint .
+ ansible-lint
+ flake8
+platforms:
+ - name: java-jre
+ image: ubuntu:22.04
+ - name: java-jdk
+ image: ubuntu:22.04
+ - name: java-jre11
+ image: ubuntu:22.04
+ - name: java-jdk11
+ image: ubuntu:22.04
+provisioner:
+ name: ansible
+ playbooks:
+ converge: ../resources/playbook.yml
+ lint:
+ name: ansible-lint
+ inventory:
+ host_vars:
+ java-jdk:
+ java_jdk_install: true
+ java-jre11:
+ java_versions:
+ - "11"
+ java-jdk11:
+ java_versions:
+ - "11"
+ java_jdk_install: true
+
+scenario:
+ name: ubuntu-2204
+verifier:
+ name: testinfra
+ directory: ../resources/tests/
diff --git a/omero/roles/ome.java/tasks/debian.yml b/omero/roles/ome.java/tasks/debian.yml
new file mode 100644
index 00000000..8b06c3d4
--- /dev/null
+++ b/omero/roles/ome.java/tasks/debian.yml
@@ -0,0 +1,22 @@
+---
+# tasks file for roles/java
+
+- name: system packages | install java jre (ubuntu)
+ become: true
+ ansible.builtin.apt:
+ update_cache: true
+ name: >-
+ openjdk-{{ java_backwards_compatibility_version.debian[item] |
+ default(item) }}-jre
+ state: present
+ with_items: "{{ java_versions }}"
+
+- name: system packages | install java jdk (ubuntu)
+ become: true
+ ansible.builtin.apt:
+ name: >-
+ openjdk-{{ java_backwards_compatibility_version.debian[item] |
+ default(item) }}-jdk
+ state: present
+ when: java_jdk_install
+ with_items: "{{ java_versions }}"
diff --git a/omero/roles/ome.java/tasks/main.yml b/omero/roles/ome.java/tasks/main.yml
new file mode 100644
index 00000000..87b3f77a
--- /dev/null
+++ b/omero/roles/ome.java/tasks/main.yml
@@ -0,0 +1,24 @@
+---
+# tasks file for roles/java
+
+- name: java | manage legacy java version
+ block:
+
+ - name: java | warn about old java_jre_versions variables
+ ansible.builtin.fail:
+ msg: java_jre_versions variable is deprecated, use java_versions instead
+ ignore_errors: true
+
+ - name: java | set java_versions from old java_jre_versions variables
+ ansible.builtin.set_fact:
+ java_versions: "{{ java_jre_versions }}"
+
+ when: java_jre_versions is defined
+
+- name: java | import tasks for redhat OS family
+ ansible.builtin.import_tasks: redhat.yml
+ when: ansible_os_family | lower == 'redhat'
+
+- name: java | import tasks for debian OS family
+ ansible.builtin.import_tasks: debian.yml
+ when: ansible_os_family | lower == 'debian'
diff --git a/omero/roles/ome.java/tasks/redhat.yml b/omero/roles/ome.java/tasks/redhat.yml
new file mode 100644
index 00000000..474a1e4c
--- /dev/null
+++ b/omero/roles/ome.java/tasks/redhat.yml
@@ -0,0 +1,21 @@
+---
+# tasks file for roles/java
+
+- name: system packages | install java jre (rockylinux)
+ become: true
+ ansible.builtin.dnf:
+ name: >-
+ java-{{ java_backwards_compatibility_version.redhat[item] |
+ default(item) }}-openjdk
+ state: present
+ with_items: "{{ java_versions }}"
+
+- name: system packages | install java jdk (rockylinux)
+ become: true
+ ansible.builtin.dnf:
+ name: >-
+ java-{{ java_backwards_compatibility_version.redhat[item] |
+ default(item) }}-openjdk-devel
+ state: present
+ when: java_jdk_install
+ with_items: "{{ java_versions }}"
diff --git a/omero/roles/ome.java/vars/main.yml b/omero/roles/ome.java/vars/main.yml
new file mode 100644
index 00000000..1263fcc8
--- /dev/null
+++ b/omero/roles/ome.java/vars/main.yml
@@ -0,0 +1,8 @@
+---
+# internal vars, may be changed at any point
+
+java_backwards_compatibility_version:
+ debian:
+ 1.8.0: "8"
+ redhat:
+ "8": "1.8.0"
diff --git a/omero/roles/ome.lvm_partition/.github/workflows/molecule.yml b/omero/roles/ome.lvm_partition/.github/workflows/molecule.yml
new file mode 100644
index 00000000..1bde8477
--- /dev/null
+++ b/omero/roles/ome.lvm_partition/.github/workflows/molecule.yml
@@ -0,0 +1,55 @@
+---
+name: Molecule
+# yamllint disable-line rule:truthy
+on:
+ push:
+ pull_request:
+ schedule:
+ - cron: '29 21 * * 0'
+
+jobs:
+
+ list-scenarios:
+ runs-on: ubuntu-22.04
+ outputs:
+ matrix: ${{ steps.listscenarios.outputs.scenarios }}
+ steps:
+ - uses: actions/checkout@v4
+ - id: listscenarios
+ uses: ome/action-ansible-molecule-list-scenarios@main
+
+ test:
+ name: Test
+ needs:
+ - list-scenarios
+ runs-on: ubuntu-22.04
+ strategy:
+ # Keep running so we can see if other tests pass
+ fail-fast: false
+ matrix:
+ scenario: ${{fromJson(needs.list-scenarios.outputs.matrix)}}
+ steps:
+ - uses: actions/checkout@v4
+ - uses: actions/setup-python@v4
+ with:
+ python-version: '3.8'
+ - name: Install Ansible & Molecule
+ run: |
+ pip install "ansible<8" "ansible-lint<6.13" flake8
+ pip install "molecule<5" "ansible-compat<4"
+ pip install molecule-plugins[docker] pytest-testinfra
+ - name: Run molecule
+ run: molecule test -s "${{ matrix.scenario }}"
+
+ publish:
+ name: Galaxy
+ if: startsWith(github.ref, 'refs/tags')
+ needs:
+ - test
+ runs-on: ubuntu-22.04
+ steps:
+ - name: galaxy
+ uses: robertdebock/galaxy-action@1.2.1
+ with:
+ galaxy_api_key: ${{ secrets.GALAXY_API_KEY }}
+ git_branch: ${{ github.ref_name }}
diff --git a/omero/roles/ome.lvm_partition/.gitignore b/omero/roles/ome.lvm_partition/.gitignore
new file mode 100644
index 00000000..b6261c93
--- /dev/null
+++ b/omero/roles/ome.lvm_partition/.gitignore
@@ -0,0 +1 @@
+.*~
diff --git a/omero/roles/ome.lvm_partition/LICENSE.md b/omero/roles/ome.lvm_partition/LICENSE.md
new file mode 100644
index 00000000..a3181283
--- /dev/null
+++ b/omero/roles/ome.lvm_partition/LICENSE.md
@@ -0,0 +1,22 @@
+Copyright (c) 2019, University of Dundee
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/omero/roles/ome.lvm_partition/README.md b/omero/roles/ome.lvm_partition/README.md
new file mode 100644
index 00000000..42c4a8e0
--- /dev/null
+++ b/omero/roles/ome.lvm_partition/README.md
@@ -0,0 +1,30 @@
+LVM Partition
+=============
+
+[![Actions Status](https://github.com/ome/ansible-role-lvm-partition/workflows/Molecule/badge.svg)](https://github.com/ome/ansible-role-lvm-partition/actions)
+[![Ansible Role](https://img.shields.io/badge/ansible--galaxy-lvm_partition-blue.svg)](https://galaxy.ansible.com/ui/standalone/roles/ome/lvm_partition/)
+
+Additional LVM logical volumes to be created and mounted.
+
+Existing LVs will be resized.
+
+Role Variables
+--------------
+
+These variables must be defined (defaults aren't provided):
+
+- `lvm_vgname`: LVM volume group for the logical volume
+- `lvm_lvname`: Logical volume name, use `[A-Aa-z0-9]+` to avoid problems
+- `lvm_lvmount`: Where the partition should be mounted
+- `lvm_lvsize`: Size of the partition
+- `lvm_lvfilesystem`: filesystem for partitions which need to be formatted
+
+Optional variables:
+
+- `lvm_shrink`: Shrink if current size is higher than size requested (default: True)
+- `lvm_lvopts`: Logical volume creation options (default: None)
+
+Author Information
+------------------
+
+ome-devel@lists.openmicroscopy.org.uk
diff --git a/omero/roles/ome.lvm_partition/defaults/main.yml b/omero/roles/ome.lvm_partition/defaults/main.yml
new file mode 100644
index 00000000..2222885d
--- /dev/null
+++ b/omero/roles/ome.lvm_partition/defaults/main.yml
@@ -0,0 +1,4 @@
+---
+# defaults file for roles/lvm-partitions
+
+lvm_lvopts:
diff --git a/omero/roles/ome.lvm_partition/meta/.galaxy_install_info b/omero/roles/ome.lvm_partition/meta/.galaxy_install_info
new file mode 100644
index 00000000..5df6d224
--- /dev/null
+++ b/omero/roles/ome.lvm_partition/meta/.galaxy_install_info
@@ -0,0 +1,2 @@
+install_date: Wed 13 Nov 17:34:05 2024
+version: 1.2.0
diff --git a/omero/roles/ome.lvm_partition/meta/main.yml b/omero/roles/ome.lvm_partition/meta/main.yml
new file mode 100644
index 00000000..56497673
--- /dev/null
+++ b/omero/roles/ome.lvm_partition/meta/main.yml
@@ -0,0 +1,16 @@
+---
+galaxy_info:
+ author: ome-devel@lists.openmicroscopy.org.uk
+ description: Create a formatted LVM volume
+ company: Open Microscopy Environment
+ license: BSD
+ min_ansible_version: 2.2
+ platforms:
+ - name: EL
+ versions:
+ - 9
+ role_name: lvm_partition
+ namespace: ome
+ galaxy_tags: ['lvm', 'system']
+
+dependencies: []
diff --git a/molecule/bootstrap/molecule.yml b/omero/roles/ome.lvm_partition/molecule/default/molecule.yml
similarity index 53%
rename from molecule/bootstrap/molecule.yml
rename to omero/roles/ome.lvm_partition/molecule/default/molecule.yml
index 359f74fe..0ceebfa1 100644
--- a/molecule/bootstrap/molecule.yml
+++ b/omero/roles/ome.lvm_partition/molecule/default/molecule.yml
@@ -1,28 +1,24 @@
---
dependency:
name: galaxy
- options:
- role-file: requirements.yml
driver:
name: docker
lint: |
- yamllint .
- ansible-lint
- flake8
+ yamllint .
+ ansible-lint
+ flake8
platforms:
- - name: omedev
- image: centos:7
+ - name: instance
+ image: rockylinux:9
provisioner:
name: ansible
- playbooks:
- converge: ../../bootstrap/playbook.yml
lint:
name: ansible-lint
+verifier:
+ name: testinfra
scenario:
- name: bootstrap
test_sequence:
- lint
- dependency
+ - cleanup
- syntax
-verifier:
- name: testinfra
diff --git a/omero/roles/ome.lvm_partition/molecule/default/playbook.yml b/omero/roles/ome.lvm_partition/molecule/default/playbook.yml
new file mode 100644
index 00000000..7b9de325
--- /dev/null
+++ b/omero/roles/ome.lvm_partition/molecule/default/playbook.yml
@@ -0,0 +1,5 @@
+---
+- name: Converge
+ hosts: all
+ roles:
+ - role: ome.lvm_partition
diff --git a/omero/roles/ome.lvm_partition/molecule/default/tests/test_default.py b/omero/roles/ome.lvm_partition/molecule/default/tests/test_default.py
new file mode 100644
index 00000000..9e0e1890
--- /dev/null
+++ b/omero/roles/ome.lvm_partition/molecule/default/tests/test_default.py
@@ -0,0 +1,15 @@
+import os
+
+import testinfra.utils.ansible_runner
+
+testinfra_hosts = testinfra.utils.ansible_runner.AnsibleRunner(
+ os.environ['MOLECULE_INVENTORY_FILE']
+).get_hosts('all')
+
+
+def test_hosts_file(host):
+ f = host.file('/etc/hosts')
+
+ assert f.exists
+ assert f.user == 'root'
+ assert f.group == 'root'
diff --git a/omero/roles/ome.lvm_partition/tasks/main.yml b/omero/roles/ome.lvm_partition/tasks/main.yml
new file mode 100644
index 00000000..4a231d69
--- /dev/null
+++ b/omero/roles/ome.lvm_partition/tasks/main.yml
@@ -0,0 +1,28 @@
+---
+# Setup LVM partition
+
+- name: storage | create logical volume
+ become: true
+ lvol:
+ vg: "{{ lvm_vgname }}"
+ lv: "{{ lvm_lvname }}"
+ size: "{{ lvm_lvsize }}"
+ shrink: "{{ lvm_shrink | default(True) }}"
+ opts: "{{ lvm_lvopts | default(None) }}"
+
+- name: storage | filesystem
+ become: true
+ filesystem:
+ fstype: "{{ lvm_lvfilesystem }}"
+ dev: /dev/{{ lvm_vgname }}/{{ lvm_lvname }}
+ resizefs: true
+
+- name: storage | mount
+ become: true
+ mount:
+ name: "{{ lvm_lvmount }}"
+ src: /dev/{{ lvm_vgname }}/{{ lvm_lvname }}
+ dump: "1"
+ passno: "2"
+ fstype: "{{ lvm_lvfilesystem }}"
+ state: mounted
diff --git a/omero/roles/ome.lvm_partition/tests/inventory b/omero/roles/ome.lvm_partition/tests/inventory
new file mode 100644
index 00000000..2fbb50c4
--- /dev/null
+++ b/omero/roles/ome.lvm_partition/tests/inventory
@@ -0,0 +1 @@
+localhost
diff --git a/omero/roles/ome.lvm_partition/tests/test.yml b/omero/roles/ome.lvm_partition/tests/test.yml
new file mode 100644
index 00000000..6f1a9df8
--- /dev/null
+++ b/omero/roles/ome.lvm_partition/tests/test.yml
@@ -0,0 +1,5 @@
+---
+- hosts: localhost
+ remote_user: root
+ roles:
+ - role: ome.lvm_partition
diff --git a/omero/roles/ome.nginx/.github/workflows/molecule.yml b/omero/roles/ome.nginx/.github/workflows/molecule.yml
new file mode 100644
index 00000000..77ad5981
--- /dev/null
+++ b/omero/roles/ome.nginx/.github/workflows/molecule.yml
@@ -0,0 +1,56 @@
+---
+name: Molecule
+# yamllint disable-line rule:truthy
+on:
+ push:
+ pull_request:
+ schedule:
+ - cron: '29 21 * * 0'
+
+jobs:
+
+ list-scenarios:
+ runs-on: ubuntu-22.04
+ outputs:
+ matrix: ${{ steps.listscenarios.outputs.scenarios }}
+ steps:
+ - uses: actions/checkout@v4
+ - id: listscenarios
+ uses: ome/action-ansible-molecule-list-scenarios@main
+
+ test:
+ name: Test
+ needs:
+ - list-scenarios
+ runs-on: ubuntu-22.04
+ strategy:
+ # Keep running so we can see if other tests pass
+ fail-fast: false
+ matrix:
+ scenario: ${{fromJson(needs.list-scenarios.outputs.matrix)}}
+ steps:
+ - uses: actions/checkout@v4
+ - uses: actions/setup-python@v5
+ with:
+ python-version: '3.8'
+ - name: Install Ansible & Molecule
+ run: |
+ pip install "ansible<8" "ansible-lint<6.13" flake8
+ pip install "molecule<5" "ansible-compat<4"
+ pip install molecule-plugins[docker] pytest-testinfra
+ - name: Run molecule
+ run: molecule test -s "${{ matrix.scenario }}"
+
+ publish:
+ name: Galaxy
+ if: startsWith(github.ref, 'refs/tags')
+ needs:
+ - test
+ runs-on: ubuntu-22.04
+ steps:
+ - name: galaxy
+ uses: ansible-actions/ansible-galaxy-action@v1.2.0
+ with:
+ galaxy_api_key: ${{ secrets.GALAXY_API_KEY }}
+ galaxy_version: ${{ github.ref_name }}
+
diff --git a/omero/roles/ome.nginx/.gitignore b/omero/roles/ome.nginx/.gitignore
new file mode 100644
index 00000000..3d8d5649
--- /dev/null
+++ b/omero/roles/ome.nginx/.gitignore
@@ -0,0 +1,2 @@
+.*~
+*.pyc
\ No newline at end of file
diff --git a/omero/roles/ome.nginx/LICENSE.md b/omero/roles/ome.nginx/LICENSE.md
new file mode 100644
index 00000000..a3181283
--- /dev/null
+++ b/omero/roles/ome.nginx/LICENSE.md
@@ -0,0 +1,22 @@
+Copyright (c) 2019, University of Dundee
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/omero/roles/ome.nginx/README.md b/omero/roles/ome.nginx/README.md
new file mode 100644
index 00000000..446ae983
--- /dev/null
+++ b/omero/roles/ome.nginx/README.md
@@ -0,0 +1,30 @@
+Nginx
+=====
+
+[![Actions Status](https://github.com/ome/ansible-role-nginx/workflows/Molecule/badge.svg)](https://github.com/ome/ansible-role-nginx/actions)
+[![Ansible Role](https://img.shields.io/badge/ansible--galaxy-nginx-blue.svg)](https://galaxy.ansible.com/ui/standalone/roles/ome/nginx/)
+
+Install upstream Nginx.
+
+TODO: Add configuration options.
+
+
+Role Variables
+--------------
+
+- `nginx_keep_default_configs`: If `true` keep the default site configuration files in `nginx/conf.d`, default `false` (disable them)
+- `nginx_stable_repo`: If `false` use the mainline instead of stable repo, default `true`
+- `nginx_version`: The packaged version of Nginx, optional, available versions depends on `nginx_stable_repo`. Not supported on Ubuntu.
+- `nginx_systemd_setup`: Start/restart nginx using systemd, default `true`
+, if you want to manage Nginx yourself set this to `false`
+
+Log rotation:
+
+- `nginx_logrotate_interval`: Rotate log files at this interval, default `daily`
+- `nginx_logrotate_backlog_size`: Number of backlog files to keep, default `366`
+
+
+Author Information
+------------------
+
+ome-devel@lists.openmicroscopy.org.uk
diff --git a/omero/roles/ome.nginx/defaults/main.yml b/omero/roles/ome.nginx/defaults/main.yml
new file mode 100644
index 00000000..e5a11d8b
--- /dev/null
+++ b/omero/roles/ome.nginx/defaults/main.yml
@@ -0,0 +1,20 @@
+---
+# defaults file for roles/nginx
+
+# Not supported on Ubuntu
+nginx_version: ''
+
+# Rotate log files at this interval
+nginx_logrotate_interval: daily
+
+# Number of backlog files to keep
+nginx_logrotate_backlog_size: 366
+
+# Use stable or mainline?
+nginx_stable_repo: true
+
+# Keep default configurations under /etc/nginx
+nginx_keep_default_configs: false
+
+# Start/restart nginx using systemd
+nginx_systemd_setup: true
diff --git a/omero/roles/ome.nginx/files/nginx-mainline.repo b/omero/roles/ome.nginx/files/nginx-mainline.repo
new file mode 100644
index 00000000..9b8af3b0
--- /dev/null
+++ b/omero/roles/ome.nginx/files/nginx-mainline.repo
@@ -0,0 +1,5 @@
+[nginx]
+name=nginx repo
+baseurl=http://nginx.org/packages/mainline/centos/$releasever/$basearch/
+gpgcheck=0
+enabled=1
diff --git a/omero/roles/ome.nginx/handlers/main.yml b/omero/roles/ome.nginx/handlers/main.yml
new file mode 100644
index 00000000..2867ac9c
--- /dev/null
+++ b/omero/roles/ome.nginx/handlers/main.yml
@@ -0,0 +1,9 @@
+---
+# Handler for nginx
+
+- name: restart nginx
+ become: true
+ service:
+ name: nginx
+ state: restarted
+ when: nginx_systemd_setup
diff --git a/omero/roles/ome.nginx/meta/.galaxy_install_info b/omero/roles/ome.nginx/meta/.galaxy_install_info
new file mode 100644
index 00000000..4532a6ec
--- /dev/null
+++ b/omero/roles/ome.nginx/meta/.galaxy_install_info
@@ -0,0 +1,2 @@
+install_date: Wed 13 Nov 17:33:51 2024
+version: 2.2.1
diff --git a/omero/roles/ome.nginx/meta/main.yml b/omero/roles/ome.nginx/meta/main.yml
new file mode 100644
index 00000000..b2c02857
--- /dev/null
+++ b/omero/roles/ome.nginx/meta/main.yml
@@ -0,0 +1,16 @@
+---
+galaxy_info:
+ author: ome-devel@lists.openmicroscopy.org.uk
+ description: Install upstream Nginx
+ company: Open Microscopy Environment
+ license: BSD
+ min_ansible_version: 2.6
+ platforms:
+ - name: EL
+ versions:
+ - 9
+ namespace: ome
+ role_name: nginx
+ galaxy_tags: ['nginx']
+
+dependencies: []
diff --git a/omero/roles/ome.nginx/molecule/resources/playbook.yml b/omero/roles/ome.nginx/molecule/resources/playbook.yml
new file mode 100644
index 00000000..178201f2
--- /dev/null
+++ b/omero/roles/ome.nginx/molecule/resources/playbook.yml
@@ -0,0 +1,5 @@
+---
+- name: Converge
+ hosts: all
+ roles:
+ - role: ome.nginx
diff --git a/omero/roles/ome.nginx/molecule/rockylinux9/molecule.yml b/omero/roles/ome.nginx/molecule/rockylinux9/molecule.yml
new file mode 100644
index 00000000..d4bf3216
--- /dev/null
+++ b/omero/roles/ome.nginx/molecule/rockylinux9/molecule.yml
@@ -0,0 +1,59 @@
+---
+dependency:
+ name: galaxy
+driver:
+ name: docker
+lint: |
+ yamllint .
+ ansible-lint
+ flake8
+platforms:
+ - name: nginx-default
+ image: eniocarboni/docker-rockylinux-systemd:9
+ command: /sbin/init
+ privileged: true
+ cgroupns_mode: host
+ tmpfs:
+ - /sys/fs/cgroup
+ groups:
+ - extra_options
+ - name: nginx-custom
+ image: eniocarboni/docker-rockylinux-systemd:9
+ command: /sbin/init
+ privileged: true
+ cgroupns_mode: host
+ tmpfs:
+ - /sys/fs/cgroup
+ groups:
+ - extra_options
+ - name: nginx-disabled
+ image: eniocarboni/docker-rockylinux-systemd:9
+ command: /sbin/init
+ privileged: true
+ cgroupns_mode: host
+ tmpfs:
+ - /sys/fs/cgroup
+ groups:
+ - extra_options
+provisioner:
+ name: ansible
+ playbooks:
+ converge: ../resources/playbook.yml
+ # prepare: prepare.yml
+ lint:
+ name: ansible-lint
+ inventory:
+ host_vars:
+ nginx-custom:
+ nginx_keep_default_configs: true
+ nginx_stable_repo: false
+ # Current mainline is 1.15.9
+ nginx_version: 1.20.2
+ nginx_logrotate_interval: weekly
+ nginx_logrotate_backlog_size: 5
+ nginx-disabled:
+ nginx_systemd_setup: false
+scenario:
+ name: rockylinux9
+verifier:
+ name: testinfra
diff --git a/omero/roles/ome.nginx/molecule/rockylinux9/tests/test_default.py b/omero/roles/ome.nginx/molecule/rockylinux9/tests/test_default.py
new file mode 100644
index 00000000..34d6e0f9
--- /dev/null
+++ b/omero/roles/ome.nginx/molecule/rockylinux9/tests/test_default.py
@@ -0,0 +1,56 @@
+import os
+import pytest
+import testinfra.utils.ansible_runner
+
+testinfra_hosts = testinfra.utils.ansible_runner.AnsibleRunner(
+ os.environ['MOLECULE_INVENTORY_FILE']).get_hosts('all')
+
+
+def test_package(host):
+ assert host.package('nginx').is_installed
+
+
+def test_service(host):
+ hostname = host.backend.get_hostname()
+ service = host.service('nginx')
+ if hostname == 'nginx-disabled':
+ assert not service.is_running
+ else:
+ assert service.is_running
+
+
+@pytest.mark.parametrize("configfile", ["example_ssl.conf", "default.conf"])
+def test_configuration(host, configfile):
+ c = host.file('/etc/nginx/conf.d/%s' % configfile)
+ hostname = host.backend.get_hostname()
+ if hostname != 'nginx-custom':
+ assert (c.content_string ==
+ "# This file is intentionally blank (Ansible)")
+
+
+def test_logrotate(host):
+ log = host.file("/etc/logrotate.d/nginx")
+ hostname = host.backend.get_hostname()
+ if hostname != 'nginx-custom':
+ assert log.contains("daily")
+ assert log.contains("rotate 366")
+ else:
+ assert log.contains("weekly")
+ assert log.contains("rotate 5")
+
+
+def test_version(host):
+ hostname = host.backend.get_hostname()
+ r = host.run('nginx -v')
+ assert r.rc == 0
+ ver = r.stderr.strip()
+ if hostname == 'nginx-custom':
+ assert ver == ('nginx version: nginx/1.20.2')
+ else:
+ assert ver.startswith('nginx version: nginx/1.24.0')
+
+
+def test_nginx_configuration(host):
+ c = host.file('/etc/nginx/nginx.conf')
+ assert 'http {' in c.content_string
+ assert 'server {' not in c.content_string
diff --git a/omero/roles/ome.nginx/tasks/debian.yaml b/omero/roles/ome.nginx/tasks/debian.yaml
new file mode 100755
index 00000000..91a9b7cd
--- /dev/null
+++ b/omero/roles/ome.nginx/tasks/debian.yaml
@@ -0,0 +1,26 @@
+---
+# - name: system packages | add nginx apt key
+# become: true
+# ansible.builtin.apt_key:
+# keyring: /usr/share/keyrings/nginx-archive-keyring.gpg
+# url: https://nginx.org/keys/nginx_signing.key
+# id: ABF5BD827BD9BF62
+
+
+# - name: system packages | Add nginx stable repository into sources list
+# ansible.builtin.apt_repository:
+# repo: "deb [arch=amd64,arm64 signed-by=/usr/share/keyrings/nginx-archive-keyring.gpg] http://nginx.org/packages/ubuntu jammy nginx"
+# state: present
+
+- name: system packages | install nginx
+ become: true
+ ansible.builtin.apt:
+ update_cache: true
+ name: nginx
+ state: present
+
+- name: nginx | remove default config
+ become: true
+ file:
+ path: /etc/nginx/sites-enabled/default
+ state: absent
diff --git a/omero/roles/ome.nginx/tasks/main.yml b/omero/roles/ome.nginx/tasks/main.yml
new file mode 100644
index 00000000..80b7a129
--- /dev/null
+++ b/omero/roles/ome.nginx/tasks/main.yml
@@ -0,0 +1,23 @@
+---
+# tasks file for roles/nginx
+
+- import_tasks: redhat.yaml
+ when: ansible_os_family | lower == 'redhat'
+
+- import_tasks: debian.yaml
+ when: ansible_os_family | lower == 'debian'
+
+- name: nginx | running
+ become: true
+ service:
+ name: nginx
+ state: started
+ when: nginx_systemd_setup
+
+- name: nginx | configure logrotate
+ become: true
+ template:
+ backup: false
+ dest: /etc/logrotate.d/nginx
+ src: logrotated-nginx.j2
+ mode: 0644
diff --git a/omero/roles/ome.nginx/tasks/redhat.yaml b/omero/roles/ome.nginx/tasks/redhat.yaml
new file mode 100644
index 00000000..b42d0313
--- /dev/null
+++ b/omero/roles/ome.nginx/tasks/redhat.yaml
@@ -0,0 +1,45 @@
+---
+- name: Import a key for nginx
+ become: true
+ ansible.builtin.rpm_key:
+ state: present
+ key: https://nginx.org/keys/nginx_signing.key
+
+- name: system packages | setup upstream nginx stable repo
+ become: true
+ ansible.builtin.dnf:
+ update_cache: true
+ name: "https://nginx.org/packages/centos/9/x86_64/RPMS/nginx-1.24.0-1.el9.ngx.x86_64.rpm"
+ state: present
+ when: not nginx_version
+
+- name: system packages | setup upstream nginx stable repo
+ become: true
+ ansible.builtin.dnf:
+ update_cache: true
+ name: "https://nginx.org/packages/centos/9/x86_64/RPMS/nginx-{{ nginx_version }}-1.el9.ngx.x86_64.rpm"
+ state: present
+ when: nginx_version
+
+- name: system packages | install nginx
+ become: true
+ ansible.builtin.dnf:
+ update_cache: true
+ name: >-
+ nginx
+ state: present
+
+# example_ssl.conf is in some distro versions but not upstream, disable it
+# just in case
+- name: nginx | remove default config
+ become: true
+ copy:
+ content: "# This file is intentionally blank (Ansible)"
+ dest: /etc/nginx/conf.d/{{ item }}
+ mode: 0644
+ with_items:
+ - example_ssl.conf
+ - default.conf
+ when: not (nginx_keep_default_configs | default(False))
+ notify:
+ - restart nginx
diff --git a/omero/roles/ome.nginx/templates/logrotated-nginx.j2 b/omero/roles/ome.nginx/templates/logrotated-nginx.j2
new file mode 100644
index 00000000..335224da
--- /dev/null
+++ b/omero/roles/ome.nginx/templates/logrotated-nginx.j2
@@ -0,0 +1,19 @@
+# Managed by Ansible
+/var/log/nginx/*.log {
+ {{ nginx_logrotate_interval }}
+ missingok
+ rotate {{ nginx_logrotate_backlog_size }}
+ compress
+ delaycompress
+ notifempty
+ create 640 nginx adm
+ sharedscripts
+ postrotate
+{% if ansible_os_family == 'RedHat' %}
+ [ -f /var/run/nginx.pid ] && kill -USR1 `cat /var/run/nginx.pid`
+{% endif %}
+{% if ansible_os_family == 'Debian' %}
+ invoke-rc.d nginx rotate >/dev/null 2>&1
+{% endif %}
+ endscript
+}
diff --git a/omero/roles/ome.omero_common/.github/workflows/molecule.yml b/omero/roles/ome.omero_common/.github/workflows/molecule.yml
new file mode 100644
index 00000000..e279a58c
--- /dev/null
+++ b/omero/roles/ome.omero_common/.github/workflows/molecule.yml
@@ -0,0 +1,56 @@
+---
+name: Molecule
+# yamllint disable-line rule:truthy
+on:
+ push:
+ pull_request:
+ schedule:
+ - cron: '29 21 * * 0'
+
+jobs:
+
+ list-scenarios:
+ runs-on: ubuntu-20.04
+ outputs:
+ matrix: ${{ steps.listscenarios.outputs.scenarios }}
+ steps:
+ - uses: actions/checkout@v4
+ - id: listscenarios
+ uses: ome/action-ansible-molecule-list-scenarios@main
+
+ test:
+ name: Test
+ needs:
+ - list-scenarios
+ runs-on: ubuntu-20.04
+ strategy:
+ # Keep running so we can see if other tests pass
+ fail-fast: false
+ matrix:
+ scenario: ${{fromJson(needs.list-scenarios.outputs.matrix)}}
+ steps:
+ - uses: actions/checkout@v4
+ - uses: actions/setup-python@v4
+ with:
+ python-version: '3.8'
+ - name: Install Ansible & Molecule
+ run: |
+ pip install "ansible<8" "ansible-lint<6.13" flake8
+ pip install "molecule<5" "ansible-compat<4"
+ pip install molecule-plugins[docker] pytest-testinfra
+ - name: Run molecule
+ run: molecule test -s "${{ matrix.scenario }}"
+
+
+ publish:
+ name: Galaxy
+ if: startsWith(github.ref, 'refs/tags')
+ needs:
+ - test
+ runs-on: ubuntu-20.04
+ steps:
+ - name: galaxy
+ uses: robertdebock/galaxy-action@1.2.1
+ with:
+ galaxy_api_key: ${{ secrets.GALAXY_API_KEY }}
+ git_branch: ${{ github.ref_name }}
diff --git a/omero/roles/ome.omero_common/.gitignore b/omero/roles/ome.omero_common/.gitignore
new file mode 100644
index 00000000..dba1d4d9
--- /dev/null
+++ b/omero/roles/ome.omero_common/.gitignore
@@ -0,0 +1,3 @@
+.*~
+*.pyc
+.molecule
diff --git a/omero/roles/ome.omero_common/LICENSE.md b/omero/roles/ome.omero_common/LICENSE.md
new file mode 100644
index 00000000..a3181283
--- /dev/null
+++ b/omero/roles/ome.omero_common/LICENSE.md
@@ -0,0 +1,22 @@
+Copyright (c) 2019, University of Dundee
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/omero/roles/ome.omero_common/README.md b/omero/roles/ome.omero_common/README.md
new file mode 100644
index 00000000..1b533322
--- /dev/null
+++ b/omero/roles/ome.omero_common/README.md
@@ -0,0 +1,40 @@
+OMERO Common
+============
+
+[![Actions Status](https://github.com/ome/ansible-role-omero-common/workflows/Molecule/badge.svg)](https://github.com/ome/ansible-role-omero-common/actions)
+[![Ansible Role](https://img.shields.io/badge/ansible--galaxy-omero_common-blue.svg)](https://galaxy.ansible.com/ui/standalone/roles/ome/omero_common/)
+
+Common variables and handlers for other OMERO application Ansible roles.
+
+
+Role Variables
+--------------
+
+All variables are optional.
+You should use the defaults unless you have a good reason not to.
+- `omero_common_basedir`: The parent directory for OMERO applications.
+
+
+Handlers
+--------
+
+This role includes standalone handlers which can be use to restart `omero-server` and `omero-web` without depending on the corresponding Ansible roles.
+This may be useful when modifying the configuration of OMERO after installation.
+
+If you know that the component you wish to restart is installed you can notify:
+- `restart omero-server`
+- `restart omero-web`
+- `restart nginx`
+
+If the component may or may not be installed you can notify:
+- `restart omero-server if installed`
+- `restart omero-web if installed`
+- `restart nginx if installed`
+
+Note that in the latter case the installation check is done when this role is run, not when the handlers are run, so there is a potential race condition.
+
+
+Author Information
+------------------
+
+ome-devel@lists.openmicroscopy.org.uk
diff --git a/omero/roles/ome.omero_common/defaults/main.yml b/omero/roles/ome.omero_common/defaults/main.yml
new file mode 100644
index 00000000..59ea5fce
--- /dev/null
+++ b/omero/roles/ome.omero_common/defaults/main.yml
@@ -0,0 +1,4 @@
+---
+# defaults for omero-common
+
+omero_common_basedir: /opt/omero
diff --git a/omero/roles/ome.omero_common/handlers/main.yml b/omero/roles/ome.omero_common/handlers/main.yml
new file mode 100644
index 00000000..0e0b6e58
--- /dev/null
+++ b/omero/roles/ome.omero_common/handlers/main.yml
@@ -0,0 +1,50 @@
+---
+# Handlers for omero-common
+
+# The conditional means you can safely notify this on non-systemd servers
+- name: reload systemd
+ become: true
+ command: systemctl daemon-reload
+ when: ansible_service_mgr == 'systemd'
+ tags:
+ # [ANSIBLE0006] systemctl used in place of systemd module
+ - skip_ansible_lint
+
+- name: restart omero-server
+ become: true
+ service:
+ name: omero-server
+ state: restarted
+
+- name: restart omero-web
+ become: true
+ service:
+ name: omero-web
+ state: restarted
+
+- name: restart nginx
+ become: true
+ service:
+ name: nginx
+ state: restarted
+
+- name: restart omero-server if installed
+ become: true
+ service:
+ name: omero-server
+ state: restarted
+ when: _omero_common_services_installed.results.0.stat.exists
+
+- name: restart omero-web if installed
+ become: true
+ service:
+ name: omero-web
+ state: restarted
+ when: _omero_common_services_installed.results.1.stat.exists
+
+- name: restart nginx if installed
+ become: true
+ service:
+ name: nginx
+ state: restarted
+ when: _omero_common_services_installed.results.2.stat.exists
diff --git a/omero/roles/ome.omero_common/meta/.galaxy_install_info b/omero/roles/ome.omero_common/meta/.galaxy_install_info
new file mode 100644
index 00000000..a5c11846
--- /dev/null
+++ b/omero/roles/ome.omero_common/meta/.galaxy_install_info
@@ -0,0 +1,2 @@
+install_date: Wed 13 Nov 17:33:39 2024
+version: 0.4.0
diff --git a/omero/roles/ome.omero_common/meta/main.yml b/omero/roles/ome.omero_common/meta/main.yml
new file mode 100644
index 00000000..dd077968
--- /dev/null
+++ b/omero/roles/ome.omero_common/meta/main.yml
@@ -0,0 +1,19 @@
+---
+galaxy_info:
+ role_name: omero_common
+ author: ome-devel@lists.openmicroscopy.org.uk
+ description: Common variables and tasks for OMERO applications
+ company: Open Microscopy Environment
+ license: BSD
+ min_ansible_version: 2.2
+ platforms:
+ - name: EL
+ versions:
+ - 9
+ - name: Ubuntu
+ versions:
+ - Jammy
+ namespace: ome
+ galaxy_tags: []
+
+dependencies: []
diff --git a/omero/roles/ome.omero_common/molecule/default/molecule.yml b/omero/roles/ome.omero_common/molecule/default/molecule.yml
new file mode 100644
index 00000000..9840d2a5
--- /dev/null
+++ b/omero/roles/ome.omero_common/molecule/default/molecule.yml
@@ -0,0 +1,26 @@
+---
+dependency:
+ name: galaxy
+driver:
+ name: docker
+lint: |
+ yamllint .
+ ansible-lint
+ flake8
+platforms:
+ - name: rockylinux
+ image: rockylinux:9
+ - name: ubuntu-22.04
+ image: ubuntu:22.04
+ - name: ubuntu-systemd-22.04
+ image: eniocarboni/docker-ubuntu-systemd:22.04
+ - name: rockylinux-systemd-22.04
+ image: eniocarboni/docker-rockylinux-systemd:9
+provisioner:
+ name: ansible
+ lint:
+ name: ansible-lint
+scenario:
+ name: default
+verifier:
+ name: testinfra
diff --git a/omero/roles/ome.omero_common/molecule/default/playbook.yml b/omero/roles/ome.omero_common/molecule/default/playbook.yml
new file mode 100644
index 00000000..c15f7644
--- /dev/null
+++ b/omero/roles/ome.omero_common/molecule/default/playbook.yml
@@ -0,0 +1,35 @@
+---
+- name: Converge
+ hosts: all
+ roles:
+ - role: ome.omero_common
+
+ tasks:
+
+ - name: test reload systemd handler
+ file:
+ path: /etc/systemd/system
+ state: directory
+ mode: 0755
+ notify:
+ - reload systemd
+
+ # Should be a noop
+ - name: test restart handler without omero-server
+ file:
+ path: /etc/systemd/notify-omero-server-handler
+ state: directory
+ mode: 0755
+ notify:
+ - restart omero-server if installed
+
+ post_tasks:
+ - name: Check omero-common service variable
+ assert:
+ that:
+ # omero-server
+ - not _omero_common_services_installed.results.0.stat.exists
+ # omero-web
+ - not _omero_common_services_installed.results.1.stat.exists
+ # nginx
+ - not _omero_common_services_installed.results.2.stat.exists
diff --git a/omero/roles/ome.omero_common/molecule/default/tests/test_default.py b/omero/roles/ome.omero_common/molecule/default/tests/test_default.py
new file mode 100644
index 00000000..188281ea
--- /dev/null
+++ b/omero/roles/ome.omero_common/molecule/default/tests/test_default.py
@@ -0,0 +1,13 @@
+import os
+
+import testinfra.utils.ansible_runner
+
+testinfra_hosts = testinfra.utils.ansible_runner.AnsibleRunner(
+ os.environ['MOLECULE_INVENTORY_FILE']).get_hosts('all')
+
+
+def test_directory(host):
+ d = host.file('/opt/omero')
+ assert d.is_directory
+ assert d.user == 'root'
+ assert d.group == 'root'
diff --git a/omero/roles/ome.omero_common/tasks/main.yml b/omero/roles/ome.omero_common/tasks/main.yml
new file mode 100644
index 00000000..69da8c02
--- /dev/null
+++ b/omero/roles/ome.omero_common/tasks/main.yml
@@ -0,0 +1,21 @@
+---
+# tasks for omero-common
+
+- name: omero-common | create parent directory
+ become: true
+ file:
+ path: "{{ omero_common_basedir }}"
+ state: directory
+ mode: 0755
+
+# This lets us make handlers conditional on existence of the service
+- name: omero-common | check if services installed
+ stat:
+ path: "{{ item }}"
+ check_mode: false
+ register: _omero_common_services_installed
+ with_items:
+ # Order must match the indicies in handlers
+ - /etc/systemd/system/omero-server.service
+ - /etc/systemd/system/omero-web.service
+ - /usr/lib/systemd/system/nginx.service
diff --git a/omero/roles/ome.omero_server/.github/workflows/molecule.yml b/omero/roles/ome.omero_server/.github/workflows/molecule.yml
new file mode 100644
index 00000000..543b1e7f
--- /dev/null
+++ b/omero/roles/ome.omero_server/.github/workflows/molecule.yml
@@ -0,0 +1,62 @@
+---
+name: Molecule
+# yamllint disable-line rule:truthy
+on:
+ push:
+ pull_request:
+ schedule:
+ - cron: '29 21 * * 0'
+
+jobs:
+
+ list-scenarios:
+ runs-on: ubuntu-22.04
+ outputs:
+ matrix: ${{ steps.listscenarios.outputs.scenarios }}
+ steps:
+ - uses: actions/checkout@v4
+ - id: listscenarios
+ uses: ome/action-ansible-molecule-list-scenarios@main
+
+ test:
+ name: Test
+ needs:
+ - list-scenarios
+ runs-on: ubuntu-22.04
+ strategy:
+ # Keep running so we can see if other tests pass
+ fail-fast: false
+ matrix:
+ scenario: ${{fromJson(needs.list-scenarios.outputs.matrix)}}
+ steps:
+ - uses: actions/checkout@v4
+ - uses: actions/setup-python@v4
+ with:
+ python-version: '3.9'
+ - name: Install Ansible & Molecule
+ run: |
+ pip install "ansible<8" "ansible-lint<6.13" flake8
+ pip install "molecule<5" "ansible-compat<4"
+ pip install molecule-plugins[docker] pytest-testinfra
+ - name: Run molecule
+ run: molecule test -s "${{ matrix.scenario }}"
+
+ publish:
+ name: Galaxy
+ if: startsWith(github.ref, 'refs/tags')
+ needs:
+ - test
+ runs-on: ubuntu-22.04
+ steps:
+ - uses: actions/checkout@v4
+ - name: Read the role name
+ id: role-name
+ run: |
+ name=$(grep 'role_name' meta/main.yml | sed -r 's/^[^:]*:(.*)$/\1/' | tr -d '[:space:]') # noqa
+ echo "rolename=$name" >> "$GITHUB_OUTPUT"
+ - name: Publish to Galaxy
+ uses: ome/action-ansible-galaxy-publish@main
+ with:
+ galaxy-api-key: ${{ secrets.GALAXY_API_KEY }}
+ galaxy-version: ${{ github.ref_name }}
+ role-name: ${{ steps.role-name.outputs.rolename }}
diff --git a/omero/roles/ome.omero_server/.gitignore b/omero/roles/ome.omero_server/.gitignore
new file mode 100644
index 00000000..dba1d4d9
--- /dev/null
+++ b/omero/roles/ome.omero_server/.gitignore
@@ -0,0 +1,3 @@
+.*~
+*.pyc
+.molecule
diff --git a/omero/roles/ome.omero_server/CHANGES.md b/omero/roles/ome.omero_server/CHANGES.md
new file mode 100644
index 00000000..39f57c97
--- /dev/null
+++ b/omero/roles/ome.omero_server/CHANGES.md
@@ -0,0 +1,106 @@
+# Changes in Version 6
+
+## Summary of breaking changes
+
+- Install self-signed certificates by default
+
+# Changes in Version 5
+
+## Summary of breaking changes
+
+- Remove support for CentOS 7 and Ubuntu 20.04
+- Add support for RHEL/RockyLinux 9 and Ubuntu 202.04
+
+# Changes in Version 4
+
+## Summary of breaking changes
+
+- Python 2 support is now dropped
+- `omero_server_python_requirements_ice_package` is now a nested dictionary
+ to support multiple versions per distribution
+
+## Removed variables
+
+- `omero_server_python3`: the role only installs the server with Python 3
+- `omero_server_virtualenv`: a virtual environment is created unconditionally
+- `omero_server_systemd_require_network`
+
+# Changes in Version 3
+
+## Summary of breaking changes
+- Default to installing and running under Python 3.6.
+ Set `omero_server_python3: false` to use Python 2.7.
+- The server always uses a virtualenv `/opt/omero/server/venv3` and does not include system-site-packages.
+
+## Removed variables
+- `omero_server_ice_version`: This is now an internal variable and must always be `3.6`.
+
+
+# Changes in Version 2
+
+## Summary of breaking changes
+- All variables are prefixed with `omero_server`.
+- OMERO.web has been moved to an independent role `omero-web`, it is no longer setup by this role.
+- The OMERO data directory creation logic is simplified.
+- Some configuration variables and handlers have been moved to a dependent role `omero-common`.
+- `omego` is in a dependent role.
+- The `omero` system user is renamed `omero-server` and has a minimal home directory `/opt/omero/server`.
+- The `omero` systemd service is renamed to `omero-server`.
+- Systemd is setup by default.
+- If you disable systemd setup OMERO.server is not automatically started.
+- PostgreSQL server is not installed by this role (the clients are still installed).
+- The database is not backed-up by default since you probably want the backup to go to a custom path (set `omero_server_database_backupdir`).
+- Manual configuration changes are not copied when the server is upgraded.
+- Configuration should be done using a conf.d style directory.
+- This role requires Ansible 2.2.
+
+## Removed variables
+- `omero_datadir_create`: OMERO data directories are always created and the top-level owner/group/permissions reset
+- `omero_db_create`: A PostgreSQL database must be setup independently of this role
+- `omero_omego_venv`: Replaced by `omero_server_omego` which is the path to the executable
+- `omero_prestart_file`: Replaced by a config directory
+- `omero_reinstall_on_error`: Never implemented
+- `omero_selinux_setup`: Only used by the OMERO.web tasks
+- `omero_serverdir`: Same as `omero_server_basedir`
+- `omero_systemd_restart`: The systemd restart policy is now always `no`
+- `omero_web_install`: OMERO.web is no longer managed by this role
+
+## Renamed variables
+- `omero_basedir`: `omero_server_basedir`
+
+- `omero_database_backupdir`: `omero_server_database_backupdir`
+
+- `omero_datadir_managedrepo_mode`: `omero_server_datadir_managedrepo_mode`
+- `omero_datadir`: `omero_server_datadir`
+- `omero_datadir_chown`: `omero_server_datadir_chown`
+- `omero_datadir_managedrepo`: `omero_server_datadir_managedrepo`
+- `omero_datadir_mode`: `omero_server_datadir_mode`
+
+- `omero_dbhost`: `omero_server_dbhost`
+- `omero_dbuser`: `omero_server_dbuser`
+- `omero_dbname`: `omero_server_dbname`
+- `omero_dbpassword`: `omero_server_dbpassword`
+
+- `omero_omego_additional_args`: `omero_server_omego_additional_args`
+- `omero_omego_verbosity`: `omero_server_omego_verbosity`
+
+- `omero_release`: `omero_server_release`
+
+- `omero_rootpassword`: `omero_server_rootpassword`
+
+- `omero_system_uid`: `omero_server_system_uid`
+- `omero_system_user`: `omero_server_system_user`
+- `omero_system_umask`: `omero_server_system_umask`
+- `omero_system_managedrepo_group`: `omero_server_system_managedrepo_group`
+
+- `omero_systemd_setup`: `omero_server_systemd_setup`
+- `omero_server_limit_nofile`: `omero_server_systemd_limit_nofile`
+
+- `omero_server_config`: `omero_server_config_set`
+
+- `omero_upgrade`: `omero_server_upgrade`
+
+
+
+## Handlers
+- Handlers that are intended to be used outside this role have been moved to the `omero-common` role so they can be used in other playbooks and roles without depending on this role.
diff --git a/omero/roles/ome.omero_server/LICENSE.md b/omero/roles/ome.omero_server/LICENSE.md
new file mode 100644
index 00000000..a3181283
--- /dev/null
+++ b/omero/roles/ome.omero_server/LICENSE.md
@@ -0,0 +1,22 @@
+Copyright (c) 2019, University of Dundee
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/omero/roles/ome.omero_server/README.md b/omero/roles/ome.omero_server/README.md
new file mode 100644
index 00000000..bd682672
--- /dev/null
+++ b/omero/roles/ome.omero_server/README.md
@@ -0,0 +1,119 @@
+OMERO Server
+============
+
+[![Actions Status](https://github.com/ome/ansible-role-omero-server/workflows/Molecule/badge.svg)](https://github.com/ome/ansible-role-omero-server/actions)
+[![Ansible Role](https://img.shields.io/badge/ansible--galaxy-omero_server-blue.svg)](https://galaxy.ansible.com/ui/standalone/roles/ome/omero_server/)
+
+Installs and configures OMERO.server.
+
+**Warning:** Only supports Python 3.
+See [`CHANGES.md`](./CHANGES.md) for details.
+
+
+Dependencies
+------------
+
+A PostgreSQL server is required.
+
+
+
+Role Variables
+--------------
+
+All variables are optional, see `defaults/main.yml` for the full list
+
+OMERO.server version.
+- `omero_server_release`: The OMERO release, e.g. `5.6.0`.
+ The default is `present` which will install the latest server if no server is installed, but will not modify an existing server.
+ Use `latest` to automatically upgrade when a new version is released.
+
+Database connection parameters and initialisation.
+- `omero_server_dbhost`: Database host
+- `omero_server_dbuser`: Database user
+- `omero_server_dbname`: Database name
+- `omero_server_dbpassword`: Database password
+- `omero_server_rootpassword`: OMERO root password, default `omero`.
+ This is only used when initialising a new database.
+
+OMERO.server configuration.
+- `omero_server_config_set`: A dictionary of `config-key: value` which will be used for the initial OMERO.server configuration, default empty.
+ `value` can be a string, or an object (list, dictionary) that will be automatically converted to quoted JSON.
+ Note configuration can also be done pre/post installation using the `server/config` conf.d style directory.
+
+OMERO system user, group, permissions, and data directory.
+You may need to change these for in-place imports.
+- `omero_server_system_user`: OMERO.server system user, default `omero-server`.
+- `omero_server_system_user_manage`: Create or update the OMERO.server system user if necessary, default `True`.
+- `omero_server_system_uid`: OMERO system user ID (default automatic)
+- `omero_server_system_umask`: OMERO system user umask, may need to be changed for in-place imports
+- `omero_server_system_managedrepo_group`: OMERO system group for the `ManagedRepository`
+- `omero_server_datadir_mode`: Permissions for OMERO data directories apart from `ManagedRepository`
+- `omero_server_datadir_managedrepo_mode`: Permissions for OMERO `ManagedRepository`
+- `omero_server_datadir`: OMERO data directory, default `/OMERO`
+- `omero_server_datadir_managedrepo`: OMERO ManagedRepository directory
+- `omero_server_selfsigned_certificates`: Generate self-signed certificates instead of using anonymous ciphers, default `True`, use this if your system does not support insecure ciphers
+
+OMERO.server systemd configuration.
+- `omero_server_systemd_setup`: Create and start the `omero-server` systemd service, default `True`
+- `omero_server_systemd_limit_nofile`: Systemd limit for number of open files (default ignore)
+- `omero_server_systemd_after`: A list of strings with additional service names to appear in systemd unit file "After" statements. Default empty/none.
+- `omero_server_systemd_requires`: A list of strings with additional service names to appear in systemd unit file "Requires" statements. Default empty/none.
+- `omero_server_systemd_environment`: Dictionary of additional environment variables.
+
+Python virtualenv
+- `omero_server_python_addons`: List of additional Python packages to be installed into virtualenv.
+ Alternatively you can install packages into `/opt/omero/server/venv3` independently from this role.
+
+Backups
+- `omero_server_database_backupdir`: Dump the OMERO database to this directory before upgrading, default empty (disabled)
+
+
+Configuring OMERO.server
+------------------------
+
+This role regenerates the OMERO configuration file using the configuration files and helper script in `/opt/omero/server/config`.
+`omero_server_config_set` can be used for simple configurations, for anything more complex consider creating one or more configuration files under: `/opt/omero/server/config/` with the extension `.omero`.
+
+Manual configuration changes (`omero config ...`) will be lost following a restart of `omero-server` with systemd, you can disable this by setting `omero_server_always_reset_config: false`.
+Manual configuration changes will never be copied during an upgrade.
+
+See https://github.com/ome/design/issues/70 for a proposal to add support for a conf.d style directory directly into OMERO.
+
+
+Example Playbooks
+-----------------
+
+ # Install the latest release, including PostgreSQL on the same server
+ - hosts: localhost
+ roles:
+
+ - role: ome.postgresql
+ postgresql_version: "13"
+ postgresql_databases:
+ - name: omero
+ owner: omero
+ postgresql_users:
+ - user: omero
+ password: omero
+ databases: [omero]
+
+ - role: ome.omero_server
+
+
+ # Install or upgrade to a particular version, with an external database
+ - hosts: localhost
+ roles:
+ - ome.omero_server
+ omero_server_release: 5.6.0
+ omero_server_dbhost: postgres.example.org
+ omero_server_dbuser: db_user
+ omero_server_dbname: db_name
+ omero_server_dbpassword: db_password
+ # Version required for the psql client
+ postgresql_version: "13"
+
+
+Author Information
+------------------
+
+ome-devel@lists.openmicroscopy.org.uk
diff --git a/omero/roles/ome.omero_server/defaults/main.yml b/omero/roles/ome.omero_server/defaults/main.yml
new file mode 100644
index 00000000..abc66e30
--- /dev/null
+++ b/omero/roles/ome.omero_server/defaults/main.yml
@@ -0,0 +1,195 @@
+---
+# defaults for omero-server
+
+omero_server_release: present
+
+# OMERO database connection parameters
+omero_server_dbhost: localhost
+omero_server_dbuser: omero
+omero_server_dbname: omero
+omero_server_dbpassword: omero
+
+# OMERO root password
+omero_server_rootpassword: omero
+
+# Dictionary of additional OMERO configuration options
+omero_server_config_set: {}
+
+# OMERO system user
+omero_server_system_user: omero-server
+omero_server_system_user_manage: true
+
+# OMERO system user ID (If not defined one is chosen automatically)
+# omero_server_system_uid:
+
+# OMERO system user umask
+omero_server_system_umask: "0002"
+
+# system group for the ManagedRepository
+omero_server_system_managedrepo_group: omero
+
+# OMERO data directory
+omero_server_datadir: /OMERO
+
+# OMERO ManagedRepository directory
+omero_server_datadir_managedrepo: "{{ omero_server_datadir }}/ManagedRepository"
+
+# Permissions for OMERO data directories apart from ManagedRepository
+omero_server_datadir_mode: "u=rwX,g=rX,o=rX"
+
+# Permissions for OMERO ManagedRepository
+omero_server_datadir_managedrepo_mode: "u=rwX,g=srwX,o=rX"
+
+# Setup systemd services
+omero_server_systemd_setup: true
+
+# Change the systemd limit for number of open files (default ignore)
+omero_server_systemd_limit_nofile:
+
+
+# Services which OMERO server needs to be running before it can start,
+# such as remote storage.
+omero_server_systemd_after: []
+
+# Services which OMERO server needs to be concurrently running.
+omero_server_systemd_requires: []
+
+# Dictionary of additional environment variables
+omero_server_systemd_environment: {}
+
+# List of additional Python packages to be installed into virtualenv
+omero_server_python_addons: []
+
+# If non-empty dump the OMERO database to this directory before upgrading
+omero_server_database_backupdir: ""
+
+# If true disable anonymous ciphers and use self-signed certificates
+omero_server_selfsigned_certificates: true
+
+
+######################################################################
+# Expert users only!
+######################################################################
+
+omero_server_ice_version: "3.6"
+
+omero_server_python_requirements_ice_package:
+ RedHat:
+ 9: "https://github.com/glencoesoftware/zeroc-ice-py-rhel9-x86_64/releases/download/\
+ 20230830/zeroc_ice-3.6.5-cp39-cp39-linux_x86_64.whl"
+ Debian:
+ 22: "https://github.com/glencoesoftware/zeroc-ice-py-ubuntu2204-x86_64/releases/download/\
+ 20221004/zeroc_ice-3.6.5-cp310-cp310-linux_x86_64.whl"
+
+
+# TODO: sort this out
+# ? pip install omero-server-dependencies=={{omero_server_release}}
+# ? get_url https://downloads.openmicroscopy.org/omero/
+# {{omero_server_release}}/artifacts/
+# OMERO.server-{{omero_server_release}}.requirements.txt
+# List of Python3 packages to install
+# _omero_* are temporary overrides pending a final release
+# Can't leave this unset because pip won't install the Python 3 pre-releases
+_omero_py_version: '>=5.6.0'
+_omero_dropbox_version: '>=5.6.1'
+omero_server_python_requirements:
+ - omego==0.7.0
+ # TODO: make the use of our non-standard wheel optional
+ - "{{ omero_server_python_requirements_ice_package[ansible_os_family][
+ ansible_distribution_major_version | int ] |
+ default('zeroc-ice')}}"
+ - "omero-py{{ _omero_py_version | default('') }}"
+ - "omero-dropbox{{ _omero_dropbox_version | default('') }}"
+ # TODO: keep or ditch ipython? It's a big dependency and mostly useful for
+ # clients
+ # - ipython
+ - jinja2
+ # Includes optional dependencies: always installed but activation may be
+ # optional
+ - omero-server
+
+# Path to virtualenv
+omero_server_virtualenv_basedir: "{{ omero_server_basedir + '/venv3' }}"
+
+# OMERODIR
+omero_server_omerodir: "{{ omero_server_basedir }}/{{ omero_server_symlink }}"
+
+# How to run omero
+omero_server_omero_command: "{{ omero_server_omerodir }}/bin/omero"
+
+# Config update command
+omero_server_config_update: >-
+ {{
+ omero_server_omero_command + ' load --glob ' + omero_server_basedir +
+ '/config/*.omero'
+ }}
+
+# Need to set OMERODIR when omego runs omero
+omero_server_omego_environment: "{{ {'OMERODIR': omero_server_omerodir} }}"
+
+# Recursively set the owner on the OMERO data directory, use if the directory
+# has been copied with an incorrect owner
+omero_server_datadir_chown: false
+
+# OMERO BioFormatsCache directory
+omero_server_datadir_bioformatscache: >-
+ {{ omero_server_datadir }}/BioFormatsCache
+
+# DEVELOPMENT: If True clear the existing configuration before regenerating
+omero_server_always_reset_config: true
+
+# DEVELOPMENT: Automatically start systemd omero
+omero_server_systemd_start: true
+
+# DEVELOPMENT: Automatically init/upgrade and configure the OMERO database
+omero_server_database_manage: true
+
+# DEVELOPMENT: Automatically create and configure OMERO data directories
+omero_server_datadir_manage: true
+
+# Base directory for the server installation
+omero_server_basedir: "{{ omero_common_basedir }}/server"
+
+# Symlink to the currently installed OMERO.server
+omero_server_symlink: OMERO.server
+
+# Path of omego
+omero_server_omego: "{{ omero_server_virtualenv_basedir + '/bin/omego' }}"
+
+# Control verbosity of omego
+omero_server_omego_verbosity: "-qq"
+
+# Additional omego aguments passed to upgrade or install
+omero_server_omego_additional_args: ""
+
+# If True and server is already installed then upgrade to the version in
+# omero_server_release, otherwise don't upgrade an existing server.
+# This should not be needed if version are correctly compared.
+omero_server_upgrade: true
+
+# DEVELOPMENT: Operator for comparing current-version against
+# omero_server_release, e.g. '!='. Default is to upgrade when
+# current-version < omero_server_release
+omero_server_checkupgrade_comparator: '<'
+
+
+# _omero_server_new_version is set in tasks/omero-install.yml
+# We can't just use omero_server_release because if it is "present"
+# it needs to be substituted with a value that omego will accept
+omero_server_omego_options: >
+ --release {{ _omero_server_new_version }}
+ --sym {{ omero_server_symlink }}
+ --ice {{ omero_server_ice_version }}
+ --no-start
+ --no-web
+ --ignoreconfig
+ --omerocli {{ omero_server_virtualenv_basedir + '/bin/omero' }}
+ {{ omero_server_omego_verbosity }}
+ {{ omero_server_omego_additional_args }}
+
+omero_server_omego_db_options: >
+ --dbhost {{ omero_server_dbhost | quote }}
+ --dbuser {{ omero_server_dbuser | quote }}
+ --dbname {{ omero_server_dbname | quote }}
+ --dbpass {{ omero_server_dbpassword | quote }}
+ {{ omero_server_database_manage | ternary('--managedb', '') }}
diff --git a/omero/roles/ome.omero_server/handlers/main.yml b/omero/roles/ome.omero_server/handlers/main.yml
new file mode 100644
index 00000000..e740b169
--- /dev/null
+++ b/omero/roles/ome.omero_server/handlers/main.yml
@@ -0,0 +1,17 @@
+---
+# handlers for omero-server
+# Don't use omero-common handlers because systemd might be disabled.
+# This also avoids problems with ordering of handlers:
+# http://stackoverflow.com/a/35130254
+
+- name: omero-server rewrite omero-server configuration
+ become: true
+ become_user: "{{ omero_server_system_user }}"
+ command: "{{ omero_server_config_update }}"
+
+- name: omero-server restart omero-server
+ become: true
+ service:
+ name: omero-server
+ state: restarted
+ when: omero_server_systemd_setup and omero_server_systemd_start
diff --git a/omero/roles/ome.omero_server/meta/.galaxy_install_info b/omero/roles/ome.omero_server/meta/.galaxy_install_info
new file mode 100644
index 00000000..cd303ee8
--- /dev/null
+++ b/omero/roles/ome.omero_server/meta/.galaxy_install_info
@@ -0,0 +1,2 @@
+install_date: Wed 13 Nov 17:33:48 2024
+version: 6.1.0
diff --git a/omero/roles/ome.omero_server/meta/main.yml b/omero/roles/ome.omero_server/meta/main.yml
new file mode 100644
index 00000000..a490e5d2
--- /dev/null
+++ b/omero/roles/ome.omero_server/meta/main.yml
@@ -0,0 +1,17 @@
+---
+galaxy_info:
+ author: ome-devel@lists.openmicroscopy.org.uk
+ description: Install and configure OMERO.server, and optionally PostgreSQL
+ company: Open Microscopy Environment
+ license: BSD
+ min_ansible_version: 2.10
+ platforms:
+ - name: EL
+ versions:
+ - 9
+ - name: Ubuntu
+ versions:
+ - jammy
+ namespace: ome
+ role_name: omero_server
+ galaxy_tags: ['omero', 'server']
diff --git a/omero/roles/ome.omero_server/meta/requirements.yml b/omero/roles/ome.omero_server/meta/requirements.yml
new file mode 100644
index 00000000..233eba49
--- /dev/null
+++ b/omero/roles/ome.omero_server/meta/requirements.yml
@@ -0,0 +1,7 @@
+---
+- src: ome.omero_common
+- src: ome.basedeps
+- src: ome.java
+- src: ome.python3_virtualenv
+- src: ome.ice
+- src: ome.postgresql_client
diff --git a/omero/roles/ome.omero_server/molecule/resources/playbook.yml b/omero/roles/ome.omero_server/molecule/resources/playbook.yml
new file mode 100644
index 00000000..c50ea741
--- /dev/null
+++ b/omero/roles/ome.omero_server/molecule/resources/playbook.yml
@@ -0,0 +1,61 @@
+---
+
+- name: Converge
+ hosts: all
+
+ roles:
+
+ - role: ome.postgresql
+ postgresql_databases:
+ - name: omero
+ owner: omero
+ postgresql_users:
+ - user: omero
+ password: omero
+ databases: [omero]
+
+ - role: ansible-role-omero-server
+ omero_server_system_managedrepo_group: importer
+ omero_server_config_set:
+ Ice.IPv6: "0"
+ omero.client.ui.tree.type_order:
+ - screen
+ - plate
+ - project
+ - dataset
+ omero_server_release: latest
+ omero_server_python_addons:
+ - omero-upload
+
+ tasks:
+
+ - name: additional config file
+ copy:
+ content: >
+ config set omero.policy.binary_access -- "-read,-write,-image,-plate"
+ dest: /opt/omero/server/config/molecule-additional-config.omero
+ mode: 0644
+ notify:
+ - restart omero-server
+
+
+- name: Additional tasks for setting up tests
+ hosts: all
+ tasks:
+
+ - name: create import user
+ user:
+ name: data-importer
+ group: importer
+
+ - name: create data directory
+ file:
+ path: /data/import
+ state: directory
+ mode: 0755
+
+ - name: create fake image
+ copy:
+ content: ''
+ dest: /data/import/test.fake
+ mode: 0644
diff --git a/omero/roles/ome.omero_server/molecule/resources/requirements.yml b/omero/roles/ome.omero_server/molecule/resources/requirements.yml
new file mode 100644
index 00000000..2643cc7a
--- /dev/null
+++ b/omero/roles/ome.omero_server/molecule/resources/requirements.yml
@@ -0,0 +1,8 @@
+---
+- src: ome.omero_common
+- src: ome.basedeps
+- src: ome.java
+- src: ome.python3_virtualenv
+- src: ome.ice
+- src: ome.postgresql
+- src: ome.postgresql_client
diff --git a/omero/roles/ome.omero_server/molecule/resources/tests/test_default.py b/omero/roles/ome.omero_server/molecule/resources/tests/test_default.py
new file mode 100644
index 00000000..b1a5507c
--- /dev/null
+++ b/omero/roles/ome.omero_server/molecule/resources/tests/test_default.py
@@ -0,0 +1,76 @@
+import os
+import pytest
+import testinfra.utils.ansible_runner
+from time import time
+
+testinfra_hosts = testinfra.utils.ansible_runner.AnsibleRunner(
+ os.environ['MOLECULE_INVENTORY_FILE']).get_hosts('all')
+
+# Ubuntu sudo doesn't set HOME so it tries to write to /root
+ENV = 'OMERO_USERDIR=/home/data-importer/omero-{}'.format(time())
+OMERO = '/opt/omero/server/OMERO.server/bin/omero'
+OMERO_LOGIN = '-C -s localhost -u root -w omero'
+
+
+def test_service_running_and_enabled(host):
+ assert host.service('omero-server').is_running
+ assert host.service('omero-server').is_enabled
+
+
+def test_omero_root_login(host):
+ with host.sudo('data-importer'):
+ host.check_output('%s %s login %s' % (ENV, OMERO, OMERO_LOGIN))
+
+
+@pytest.mark.parametrize("key,value", [
+ ('omero.data.dir', '/OMERO'),
+ ('omero.client.ui.tree.type_order',
+ '["screen", "plate", "project", "dataset"]'),
+ ('omero.policy.binary_access', '-read,-write,-image,-plate'),
+])
+def test_omero_server_config(host, key, value):
+ with host.sudo('omero-server'):
+ cfg = host.check_output('%s %s config get %s' % (ENV, OMERO, key))
+ assert cfg == value
+
+
+def test_omero_datadir(host):
+ d = host.file('/OMERO')
+ assert d.is_directory
+ assert d.user == 'omero-server'
+ assert d.group == 'root'
+ assert d.mode == 0o755
+
+
+def test_omero_managedrepo(host):
+ d = host.file('/OMERO/ManagedRepository')
+ assert d.is_directory
+ assert d.user == 'omero-server'
+ assert d.group == 'importer'
+ assert d.mode == 0o2775
+
+
+def test_inplace_import(host):
+ fake_file = '/data/import/test.fake'
+ with host.sudo('data-importer'):
+ outimport = host.check_output(
+ '%s %s %s import --skip=upgrade --transfer=ln_s %s' %
+ (ENV, OMERO, OMERO_LOGIN, fake_file))
+
+ imageid = int(outimport.split(':', 1)[1])
+ assert imageid
+
+ query = ('SELECT concat(ofile.path, ofile.name) '
+ 'FROM FilesetEntry AS fse '
+ 'JOIN fse.fileset AS fileset '
+ 'JOIN fse.originalFile AS ofile '
+ 'JOIN fileset.images AS image '
+ 'WHERE image.id = %d' % imageid)
+ with host.sudo('data-importer'):
+ outhql = host.check_output(
+ '%s %s %s hql -q --style plain "%s"' %
+ (ENV, OMERO, OMERO_LOGIN, query))
+
+ f = host.file('/OMERO/ManagedRepository/%s' % outhql.split(',', 1)[1])
+ assert f.is_symlink
+ assert f.linked_to == fake_file
diff --git a/omero/roles/ome.omero_server/molecule/resources/tests/test_python3.py b/omero/roles/ome.omero_server/molecule/resources/tests/test_python3.py
new file mode 100644
index 00000000..9019c43c
--- /dev/null
+++ b/omero/roles/ome.omero_server/molecule/resources/tests/test_python3.py
@@ -0,0 +1,50 @@
+import os
+import re
+import testinfra.utils.ansible_runner
+
+testinfra_hosts = testinfra.utils.ansible_runner.AnsibleRunner(
+ os.environ['MOLECULE_INVENTORY_FILE']).get_hosts('omero-py3')
+
+OMERO = '/opt/omero/server/OMERO.server/bin/omero'
+# Need to match 5.6.dev2
+# VERSION_PATTERN = re.compile('(\d+)\.(\d+)\.(\d+)-ice36-')
+VERSION_PATTERN = re.compile(r'(\d+)\.(\d+)\.(\w+)')
+
+
+def test_omero_version(host):
+ with host.sudo('data-importer'):
+ ver = host.check_output("%s version" % OMERO)
+ m = VERSION_PATTERN.match(ver)
+ assert m is not None
+ assert int(m.group(1)) >= 5
+ assert int(m.group(2)) >= 6
+
+
+def test_postgres_version(host):
+ ver = host.check_output("psql --version")
+ assert ver.startswith('psql (PostgreSQL) 13')
+
+
+def test_additional_python(host):
+ piplist = host.check_output("/opt/omero/server/venv3/bin/pip list")
+ assert "omero-upload" in piplist
+
+
+def test_running_in_venv(host):
+ # host.process may use `ps -Aww -o ...` which truncates some fields
+ # https://github.com/philpep/testinfra/blob/3.2.0/testinfra/modules/process.py#L127-L148
+ count = 0
+ for line in host.check_output('ps -Aww -o pid,comm,user').splitlines()[1:]:
+ pid, command, user = line.split()
+ if command == 'python' and user == 'omero-server':
+ try:
+ f = host.file('/proc/%s/environ' % pid)
+ env = dict(item.split('=', 1) for item in
+ f.content_string.split('\0') if item)
+ assert env.get('PATH').startswith(
+ '/opt/omero/server/venv3/bin:')
+ count += 1
+ except RuntimeError:
+ # Might be a transient unrelated process
+ pass
+ assert count > 1
diff --git a/omero/roles/ome.omero_server/molecule/resources/upgrade-omero.yml b/omero/roles/ome.omero_server/molecule/resources/upgrade-omero.yml
new file mode 100644
index 00000000..135a1996
--- /dev/null
+++ b/omero/roles/ome.omero_server/molecule/resources/upgrade-omero.yml
@@ -0,0 +1,53 @@
+---
+# omero_server_release is defined in molecule.yml host_vars so:
+# - omero-server-newdep: latest: upgraded
+# - omero-server-olddep: present: unchanged at 5.2
+- name: Upgrade OMERO if required
+ hosts: all
+ roles:
+
+ - role: ansible-role-omero-server
+ omero_server_system_managedrepo_group: importer
+ omero_server_config_set:
+ Ice.IPv6: "0"
+ omero.client.ui.tree.type_order:
+ - screen
+ - plate
+ - project
+ - dataset
+ omero_server_python3: false
+
+
+- name: Additional tasks for setting up tests
+ hosts: all
+ tasks:
+
+ - name: create import user
+ user:
+ name: data-importer
+ group: importer
+
+ - name: create data directory
+ file:
+ path: /data/import
+ state: directory
+ mode: 0755
+
+ - name: create fake image
+ copy:
+ content: ''
+ dest: /data/import/test.fake
+ mode: 0644
+
+
+# testinfra Sudo doesn't use the `-i` flag, so the working directory needs
+# to be accessible
+- hosts: vagrant-hosts
+ tasks:
+ - name: make vagrant home accessible
+ file:
+ owner: vagrant
+ group: vagrant
+ mode: "0711"
+ path: /home/vagrant
+ state: directory
diff --git a/omero/roles/ome.omero_server/molecule/rockylinux9/molecule.yml b/omero/roles/ome.omero_server/molecule/rockylinux9/molecule.yml
new file mode 100644
index 00000000..6ff2efd0
--- /dev/null
+++ b/omero/roles/ome.omero_server/molecule/rockylinux9/molecule.yml
@@ -0,0 +1,50 @@
+---
+dependency:
+ name: galaxy
+ options:
+ role-file: molecule/resources/requirements.yml
+driver:
+ name: docker
+lint: |
+ yamllint .
+ ansible-lint
+ flake8
+platforms:
+ - name: omero-server-rockylinux9
+ image: eniocarboni/docker-rockylinux-systemd:9
+ image_version: latest
+ command: /sbin/init
+ privileged: true
+ cgroupns_mode: host
+ tmpfs:
+ - /sys/fs/cgroup
+ groups:
+ - docker-hosts
+ - omero-py3
+provisioner:
+ name: ansible
+ lint:
+ name: ansible-lint
+ # To test the upgrade process without breaking the idempotence check
+ # - install omero during the prepare step which is only run once
+ # - attempt to upgrade in the converge step
+ options:
+ v: true
+ diff: true
+ # tags: [x]
+ playbooks:
+ # TODO: Use shared test playbooks
+ converge: ../resources/playbook.yml
+ inventory:
+ host_vars:
+ omero-server-rockylinux9:
+ postgresql_version: "13"
+ omero_server_selfsigned_certificates: true
+scenario:
+ name: rockylinux9
+ converge_sequence:
+ - converge
+
+verifier:
+ name: testinfra
+ directory: ../resources/tests/
diff --git a/omero/roles/ome.omero_server/molecule/rockylinux9/prepare.yml b/omero/roles/ome.omero_server/molecule/rockylinux9/prepare.yml
new file mode 100644
index 00000000..dc498ace
--- /dev/null
+++ b/omero/roles/ome.omero_server/molecule/rockylinux9/prepare.yml
@@ -0,0 +1,12 @@
+---
+
+- name: Prepare all
+ hosts: all
+ tasks:
+ - name: Upgrade ca-certificates
+ ansible.builtin.dnf:
+ update_cache: true
+ pkg:
+ - ca-certificates
+ - procps
+ state: latest
diff --git a/omero/roles/ome.omero_server/molecule/ubuntu2204/molecule.yml b/omero/roles/ome.omero_server/molecule/ubuntu2204/molecule.yml
new file mode 100644
index 00000000..80cefd79
--- /dev/null
+++ b/omero/roles/ome.omero_server/molecule/ubuntu2204/molecule.yml
@@ -0,0 +1,50 @@
+---
+dependency:
+ name: galaxy
+ options:
+ role-file: molecule/resources/requirements.yml
+driver:
+ name: docker
+lint: |
+ yamllint .
+ ansible-lint
+ flake8
+platforms:
+ - name: omero-server-ubuntu2204
+ image: eniocarboni/docker-ubuntu-systemd:22.04
+ command: /sbin/init
+ privileged: true
+ cgroupns_mode: host
+ tmpfs:
+ - /sys/fs/cgroup
+ groups:
+ - docker-hosts
+ - omero-py3
+provisioner:
+ name: ansible
+ lint:
+ name: ansible-lint
+ # To test the upgrade process without breaking the idempotence check
+ # - install omero during the prepare step which is only run once
+ # - attempt to upgrade in the converge step
+ options:
+ v: true
+ diff: true
+ # tags: [x]
+ playbooks:
+ # TODO: Use shared test playbooks
+ converge: ../resources/playbook.yml
+ inventory:
+ host_vars:
+ omero-server-ubuntu2204:
+ postgresql_version: "13"
+ ansible_python_interpreter: /usr/bin/python3
+ omero_server_selfsigned_certificates: true
+scenario:
+ name: ubuntu2204
+ converge_sequence:
+ - converge
+
+verifier:
+ name: testinfra
+ directory: ../resources/tests/
diff --git a/omero/roles/ome.omero_server/molecule/ubuntu2204/prepare.yml b/omero/roles/ome.omero_server/molecule/ubuntu2204/prepare.yml
new file mode 100644
index 00000000..6397a7f0
--- /dev/null
+++ b/omero/roles/ome.omero_server/molecule/ubuntu2204/prepare.yml
@@ -0,0 +1,7 @@
+---
+
+- name: Prepare all
+ hosts: all
+ tasks:
+ - ansible.builtin.apt:
+ update_cache: true
diff --git a/omero/roles/ome.omero_server/tasks/main.yml b/omero/roles/ome.omero_server/tasks/main.yml
new file mode 100644
index 00000000..c32c8d09
--- /dev/null
+++ b/omero/roles/ome.omero_server/tasks/main.yml
@@ -0,0 +1,16 @@
+---
+# tasks for omero-server
+
+- include: pre-tasks.yml
+
+- include: omero-user.yml
+
+- include: omero-datadir.yml
+ when: omero_server_datadir_manage
+
+- include: omero-configfiles.yml
+
+- include: omero-install.yml
+
+- include: omero-systemd.yml
+ when: omero_server_systemd_setup
diff --git a/omero/roles/ome.omero_server/tasks/omero-configfiles.yml b/omero/roles/ome.omero_server/tasks/omero-configfiles.yml
new file mode 100644
index 00000000..da226fe2
--- /dev/null
+++ b/omero/roles/ome.omero_server/tasks/omero-configfiles.yml
@@ -0,0 +1,19 @@
+---
+# OMERO template configuration files
+
+- name: omero server | remove old configuration script
+ become: true
+ file:
+ path: "{{ omero_server_basedir }}/config/omero-server-config-update.sh"
+ state: absent
+
+- name: omero server | configuration 00-omero-server.omero
+ become: true
+ template:
+ dest: "{{ omero_server_basedir }}/config/00-omero-server.omero"
+ force: true
+ src: 00-omero-server-omero.j2
+ mode: 0644
+ notify:
+ - omero-server rewrite omero-server configuration
+ - omero-server restart omero-server
diff --git a/omero/roles/ome.omero_server/tasks/omero-datadir.yml b/omero/roles/ome.omero_server/tasks/omero-datadir.yml
new file mode 100644
index 00000000..25f5a191
--- /dev/null
+++ b/omero/roles/ome.omero_server/tasks/omero-datadir.yml
@@ -0,0 +1,56 @@
+---
+# OMERO data directories
+
+- name: omero server | create omero datadir base directory
+ become: true
+ file:
+ owner: "{{ omero_server_system_user }}"
+ mode: "{{ omero_server_datadir_mode }}"
+ path: "{{ omero_server_datadir }}"
+ recurse: false
+ state: directory
+
+- name: omero server | create omero datadir subdirectories
+ become: true
+ file:
+ owner: "{{ omero_server_system_user }}"
+ mode: "{{ omero_server_datadir_mode }}"
+ path: "{{ omero_server_datadir }}/{{ item }}"
+ recurse: "{{ omero_server_datadir_chown }}"
+ state: directory
+ with_items:
+ - Files
+ - Thumbnails
+ - DropBox
+ - FullText
+ - Pixels
+
+- name: omero server | create omero BioFormatsCache
+ become: true
+ file:
+ owner: "{{ omero_server_system_user }}"
+ mode: "{{ omero_server_datadir_mode }}"
+ path: "{{ omero_server_datadir_bioformatscache }}"
+ recurse: "{{ omero_server_datadir_chown }}"
+ state: directory
+
+- name: omero server | create omero BioFormatsCache symlink
+ become: true
+ file:
+ src: "{{ omero_server_datadir_bioformatscache }}"
+ path: "{{ omero_server_datadir }}/BioFormatsCache"
+ state: link
+ force: true
+ when: >-
+ omero_server_datadir_bioformatscache !=
+ (omero_server_datadir + "/BioFormatsCache")
+
+- name: omero server | create omero ManagedRepository
+ become: true
+ file:
+ owner: "{{ omero_server_system_user }}"
+ group: "{{ omero_server_system_managedrepo_group }}"
+ mode: "{{ omero_server_datadir_managedrepo_mode }}"
+ path: "{{ omero_server_datadir_managedrepo }}"
+ recurse: "{{ omero_server_datadir_chown }}"
+ state: directory
diff --git a/omero/roles/ome.omero_server/tasks/omero-install.yml b/omero/roles/ome.omero_server/tasks/omero-install.yml
new file mode 100644
index 00000000..f31d29cf
--- /dev/null
+++ b/omero/roles/ome.omero_server/tasks/omero-install.yml
@@ -0,0 +1,248 @@
+---
+# install/upgrade OMERO.server
+
+- name: omero server | is server symlink present
+ become: true
+ stat:
+ path: "{{ omero_server_basedir }}/{{ omero_server_symlink }}"
+ register: omero_server_symlink_st
+
+- name: omero server | is virtualenv present
+ become: true
+ stat:
+ path: "{{ omero_server_virtualenv_basedir }}"
+ register: omero_server_venv_st
+
+# Is there an existing OMERO.server that uses the requested virtualenv?
+- name: omero server | does omero server match requested virtualenv
+ set_fact:
+ _omero_server_matches_virtualenv: >-
+ {{
+ omero_server_symlink_st.stat.exists and
+ omero_server_venv_st.stat.exists
+ }}
+
+# Previously this used the output of 'omero version'
+# Python modules are now decoupled so get the version from the folder name
+- name: omero server | get server version
+ set_fact:
+ omero_server_version: >-
+ {{
+ omero_server_symlink_st.stat.lnk_target | basename |
+ regex_replace('OMERO.server-(.+)-ice3.+', '\1')
+ }}
+ when: _omero_server_matches_virtualenv
+
+- name: omero server | check omero version could be obtained
+ assert:
+ msg: >-
+ OMERO.server found but unable to get version,
+ you may have a corrupt installation
+ that: >-
+ not _omero_server_matches_virtualenv or
+ (omero_server_version | default('') | length > 0)
+
+# TODO: If server was started by systemd but stopped directly you may end up
+# with a hanging process
+
+# Check whether an upgrade is available since `omego upgrade` always
+# restarts the server
+- name: omero server | get latest downloads url
+ uri:
+ url: https://downloads.openmicroscopy.org/latest/omero
+ method: HEAD
+ register: _omero_server_downloads_latest
+ check_mode: false
+
+# omego supports --release "latest" but not "present"
+# It's easiest to lookup a concrete version and use this for all omego
+# operations instead
+- name: omero server | get latest version
+ set_fact:
+ _omero_server_new_version: "{{
+ (omero_server_release in ('latest', 'present')) | ternary(
+ _omero_server_downloads_latest.url.strip('/').split('/')[-1],
+ omero_server_release
+ )
+ }}"
+
+- block:
+ - name: omero server | checkupgrade
+ set_fact:
+ # If _omero_server_new_version does not begin with a number assume
+ # it's a custom build, always upgrade
+ # If we're switching from a non-venv to a venv treat it as an upgrade
+ _omero_server_update_needed: >-
+ {{
+ not _omero_server_matches_virtualenv or
+ not (_omero_server_new_version | regex_search('^[0-9]')) or
+ (
+ (omero_server_version | default('') | length > 0) and
+ (omero_server_version != _omero_server_new_version) and
+ (omero_server_version is version(
+ _omero_server_new_version,
+ omero_server_checkupgrade_comparator))
+ )
+ }}
+ rescue:
+ # For example, comparing 5.6.dev2 with 5.6.0-m1 leads to
+ # '<' not supported between instances of 'str' and 'int'
+ # Assume this is a dev or pre-release and upgrade
+ # Ansible will exit 0 but show a failed task in the summary
+
+ - debug:
+ msg: >-
+ Error comparing current version
+ ({{ omero_server_version | default('') }})
+ and new version
+ ({{ _omero_server_new_version }}), upgrading
+
+ - name: omero server | checkupgrade failed
+ set_fact:
+ _omero_server_update_needed: true
+
+- debug:
+ msg: >-
+ Upgrade needed: {{ omero_server_version | default('UNKNOWN') }} ->
+ {{ omero_server_release }}
+ when: _omero_server_update_needed
+
+# If the OMERO.server symlink doesn't exist don't upgrade, this is a new
+# installation
+- name: omero server | set upgrade flag
+ set_fact:
+ _omero_server_execute_upgrade: "{{
+ omero_server_upgrade and
+ _omero_server_update_needed and
+ (omero_server_release != 'present') and
+ omero_server_symlink_st.stat.exists
+ }}"
+
+
+# TODO: figure out dependencies
+- name: omero server | setup virtualenv3
+ become: true
+ pip:
+ name: "pip>=21"
+ state: present
+ virtualenv: "{{ omero_server_virtualenv_basedir }}"
+ virtualenv_command: /usr/local/bin/ome-python3-virtualenv
+
+- name: omero server | install requirements
+ become: true
+ pip:
+ name: "{{ omero_server_python_requirements + omero_server_python_addons }}"
+ state: present
+ virtualenv: "{{ omero_server_virtualenv_basedir }}"
+ virtualenv_command: /usr/local/bin/ome-python3-virtualenv
+ notify:
+ - omero-server rewrite omero-server configuration
+ - omero-server restart omero-server
+
+- name: omero server | install omero
+ become: true
+ become_user: "{{ omero_server_system_user }}"
+ command: >
+ {{ omero_server_omego }}
+ install
+ {{ omero_server_omego_options }}
+ {{ omero_server_omego_db_options }}
+ --rootpass {{ omero_server_rootpassword | quote }}
+ args:
+ chdir: "{{ omero_server_basedir }}"
+ creates: "{{ omero_server_basedir }}/{{ omero_server_symlink }}"
+ environment: "{{ omero_server_omego_environment }}"
+ notify:
+ - omero-server rewrite omero-server configuration
+ - omero-server restart omero-server
+
+# Backup database
+
+- name: omero server | create omero backup directory
+ become: true
+ file:
+ mode: 0700
+ owner: "{{ omero_server_system_user }}"
+ path: "{{ omero_server_database_backupdir }}"
+ state: directory
+ when: >-
+ (omero_server_database_backupdir | length > 0) and
+ _omero_server_execute_upgrade
+
+- name: omero server | backup database
+ become: true
+ become_user: "{{ omero_server_system_user }}"
+ command: >
+ {{ omero_server_omego }}
+ db dump
+ {{ omero_server_omego_db_options }}
+ --serverdir {{ omero_server_basedir }}/{{ omero_server_symlink }}
+ args:
+ chdir: "{{ omero_server_database_backupdir }}"
+ environment: "{{ omero_server_omego_environment }}"
+ when: >-
+ (omero_server_database_backupdir | length > 0) and
+ _omero_server_execute_upgrade
+
+# Upgrade
+- name: omero server | upgrade
+ become: true
+ become_user: "{{ omero_server_system_user }}"
+ command: >
+ {{ omero_server_omego }}
+ install --upgrade
+ {{ omero_server_omego_options }}
+ {{ omero_server_omego_db_options }}
+ args:
+ chdir: "{{ omero_server_basedir }}"
+ environment: "{{ omero_server_omego_environment }}"
+ when: _omero_server_execute_upgrade
+ notify:
+ - omero-server rewrite omero-server configuration
+ - omero-server restart omero-server
+
+- name: omero server | delete OMERO.server/lib/python
+ become: true
+ file:
+ path: "{{ omero_server_omerodir }}/lib/python"
+ state: absent
+
+# Remembering to set OMERODIR everywhere is prone to error
+- name: omero server | create bin directory for wrapper
+ become: true
+ file:
+ path: "{{ omero_server_omerodir }}/bin/"
+ state: directory
+ mode: 0555
+
+- name: omero server | create omero server wrapper
+ become: true
+ template:
+ dest: "{{ omero_server_omero_command }}"
+ src: bin-omero.j2
+ mode: 0555
+ notify:
+ - omero-server rewrite omero-server configuration
+ - omero-server restart omero-server
+
+- name: system packages | install openssl
+ become: true
+ package:
+ name: openssl
+ state: present
+
+- name: server package | install redhat blosc
+ become: true
+ ansible.builtin.dnf:
+ update_cache: true
+ name: blosc
+ state: present
+ when: ansible_os_family | lower == 'redhat'
+
+- name: server package | install debian blosc
+ become: true
+ ansible.builtin.apt:
+ update_cache: true
+ name: libblosc1
+ state: present
+ when: ansible_os_family | lower == 'debian'
diff --git a/omero/roles/ome.omero_server/tasks/omero-systemd.yml b/omero/roles/ome.omero_server/tasks/omero-systemd.yml
new file mode 100644
index 00000000..a3e0977e
--- /dev/null
+++ b/omero/roles/ome.omero_server/tasks/omero-systemd.yml
@@ -0,0 +1,25 @@
+---
+# Setup systemd files
+
+- name: omero server | systemd service
+ become: true
+ template:
+ dest: /etc/systemd/system/omero-server.service
+ force: true
+ src: systemd-system-omero-server-service.j2
+ mode: 0644
+ notify:
+ - reload systemd
+ - omero-server restart omero-server
+
+# Flush handlers to ensure systemd is reloaded
+- name: omero server | flush systemd handlers
+ meta: flush_handlers
+
+- name: omero server | enable and start server systemd
+ become: true
+ service:
+ enabled: true
+ name: omero-server.service
+ state: started
+ when: omero_server_systemd_start
diff --git a/omero/roles/ome.omero_server/tasks/omero-user.yml b/omero/roles/ome.omero_server/tasks/omero-user.yml
new file mode 100644
index 00000000..9f87f332
--- /dev/null
+++ b/omero/roles/ome.omero_server/tasks/omero-user.yml
@@ -0,0 +1,31 @@
+---
+# omero system user
+
+- name: omero server | create system user
+ become: true
+ user:
+ name: "{{ omero_server_system_user }}"
+ home: "{{ omero_server_basedir }}"
+ createhome: false
+ state: present
+ system: true
+ uid: "{{ omero_server_system_uid | default(omit) }}"
+ when: omero_server_system_user_manage
+
+# TODO: Ideally everything should be read-only apart from the var directory
+- name: omero server | create omero directories
+ become: true
+ file:
+ path: "{{ item }}"
+ state: directory
+ owner: "{{ omero_server_system_user }}"
+ mode: 0755
+ with_items:
+ - "{{ omero_server_basedir }}"
+ - "{{ omero_server_basedir }}/config"
+
+- name: omero server | create managedrepo system group
+ become: true
+ group:
+ name: "{{ omero_server_system_managedrepo_group }}"
+ state: present
diff --git a/omero/roles/ome.omero_server/tasks/pre-tasks.yml b/omero/roles/ome.omero_server/tasks/pre-tasks.yml
new file mode 100644
index 00000000..0011741d
--- /dev/null
+++ b/omero/roles/ome.omero_server/tasks/pre-tasks.yml
@@ -0,0 +1,30 @@
+---
+# omero_common must be imported to prevent
+# undeclared variable failure
+- name: omero server | Import ome.omero_common role
+ import_role:
+ name: ome.omero_common
+
+- name: omero server | Include ome.basedeps role
+ include_role:
+ name: ome.basedeps
+
+- name: omero server | Include ome.java
+ include_role:
+ name: ome.java
+
+- name: omero server | Include ome.python3_virtualenv
+ include_role:
+ name: ome.python3_virtualenv
+
+- name: omero server | Include ome.ice role
+ include_role:
+ name: ome.ice
+ vars:
+ ice_version: "{{ omero_server_ice_version }}"
+ ice_install_devel: false
+ ice_install_python: false
+
+- name: omero server | Include ome.postgresql_client
+ include_role:
+ name: ome.postgresql_client
diff --git a/omero/roles/ome.omero_server/templates/00-omero-server-omero.j2 b/omero/roles/ome.omero_server/templates/00-omero-server-omero.j2
new file mode 100644
index 00000000..60970e5d
--- /dev/null
+++ b/omero/roles/ome.omero_server/templates/00-omero-server-omero.j2
@@ -0,0 +1,29 @@
+# {{ ansible_managed }}
+
+{% if omero_server_always_reset_config %}
+config drop default
+{% endif %}
+
+{% if omero_server_database_manage %}
+config set omero.db.host {{ omero_server_dbhost | quote }}
+config set omero.db.user {{ omero_server_dbuser | quote }}
+config set omero.db.name {{ omero_server_dbname | quote }}
+config set omero.db.pass {{ omero_server_dbpassword | quote }}
+{% endif %}
+
+{% if omero_server_datadir_manage %}
+config set omero.data.dir {{ omero_server_datadir | quote }}
+{% endif %}
+
+# Additional custom options
+{% for key in (omero_server_config_set | sort) %}
+config set -- {{ key | quote }} {{
+ ((omero_server_config_set[key] | string) == omero_server_config_set[key]) |
+ ternary(omero_server_config_set[key], omero_server_config_set[key] | to_json) |
+ quote
+}}
+{% endfor %}
+
+{% if omero_server_selfsigned_certificates %}
+certificates -v
+{% endif %}
diff --git a/omero/roles/ome.omero_server/templates/bin-omero.j2 b/omero/roles/ome.omero_server/templates/bin-omero.j2
new file mode 100644
index 00000000..91bf2dfa
--- /dev/null
+++ b/omero/roles/ome.omero_server/templates/bin-omero.j2
@@ -0,0 +1,22 @@
+#!{{ omero_server_virtualenv_basedir }}/bin/python
+
+import os
+from subprocess import run
+import sys
+
+if not os.getenv('OMERODIR'):
+ os.environ['OMERODIR'] = '{{ omero_server_omerodir }}'
+
+paths = os.getenv('PATH', '').split(os.pathsep)
+venv_bin = '{{ omero_server_virtualenv_basedir }}/bin'
+ice_bin = '/opt/ice/bin'
+
+if os.path.isdir(ice_bin) and ice_bin not in paths:
+ paths.insert(0, ice_bin)
+if venv_bin not in paths:
+ paths.insert(0, venv_bin)
+
+os.environ['PATH'] = os.pathsep.join(paths)
+
+p = run(['{{ omero_server_virtualenv_basedir }}/bin/omero'] + sys.argv[1:])
+sys.exit(p.returncode)
diff --git a/omero/roles/ome.omero_server/templates/omero-server-config-update-sh.j2 b/omero/roles/ome.omero_server/templates/omero-server-config-update-sh.j2
new file mode 100644
index 00000000..85fba976
--- /dev/null
+++ b/omero/roles/ome.omero_server/templates/omero-server-config-update-sh.j2
@@ -0,0 +1,9 @@
+#!/bin/sh
+# {{ ansible_managed }}
+# Regenerate the omero-server configuration:
+
+set -e
+
+for f in {{ omero_server_basedir }}/config/*.omero; do
+ {{ omero_server_omero_command }} load "$f"
+done
diff --git a/omero/roles/ome.omero_server/templates/systemd-system-omero-server-service.j2 b/omero/roles/ome.omero_server/templates/systemd-system-omero-server-service.j2
new file mode 100644
index 00000000..78e49b52
--- /dev/null
+++ b/omero/roles/ome.omero_server/templates/systemd-system-omero-server-service.j2
@@ -0,0 +1,36 @@
+[Unit]
+Description=OMERO.server
+# Requires: forces the dependency to be started
+# After: ensures this service starts after the dependency, but only if the
+# dependency is also started (PostgreSQL may be on a different server)
+After=postgresql-11.service
+After=postgresql-12.service
+After=postgresql-13.service
+After=postgresql-14.service
+After=network.service
+{% for value in omero_server_systemd_after %}After={{ value }}
+{% endfor %}
+{% for value in omero_server_systemd_requires %}Requires={{ value }}
+{% endfor %}
+
+[Service]
+User={{ omero_server_system_user }}
+UMask={{ omero_server_system_umask }}
+Type=forking
+PIDFile={{ omero_server_omerodir }}/var/master/master.pid
+Restart=no
+RestartSec=10
+# Allow up to 5 min for start/stop
+TimeoutSec=300
+{% for name in (omero_server_systemd_environment | sort) %}
+Environment={{ name }}={{ omero_server_systemd_environment[name] | quote }}
+{% endfor %}
+ExecStartPre={{ omero_server_config_update }}
+ExecStart={{ omero_server_omero_command }} admin start
+ExecStop={{ omero_server_omero_command }} admin stop
+{% if omero_server_systemd_limit_nofile %}
+LimitNOFILE={{ omero_server_systemd_limit_nofile }}
+{% endif %}
+
+[Install]
+WantedBy=multi-user.target
diff --git a/omero/roles/ome.omero_user/.github/workflows/molecule.yml b/omero/roles/ome.omero_user/.github/workflows/molecule.yml
new file mode 100644
index 00000000..ed869c6f
--- /dev/null
+++ b/omero/roles/ome.omero_user/.github/workflows/molecule.yml
@@ -0,0 +1,62 @@
+---
+name: Molecule
+# yamllint disable-line rule:truthy
+on:
+ push:
+ pull_request:
+ schedule:
+ - cron: '29 21 * * 0'
+
+jobs:
+
+ list-scenarios:
+ runs-on: ubuntu-22.04
+ outputs:
+ matrix: ${{ steps.listscenarios.outputs.scenarios }}
+ steps:
+ - uses: actions/checkout@v4
+ - id: listscenarios
+ uses: ome/action-ansible-molecule-list-scenarios@main
+
+ test:
+ name: Test
+ needs:
+ - list-scenarios
+ runs-on: ubuntu-20.04
+ strategy:
+ # Keep running so we can see if other tests pass
+ fail-fast: false
+ matrix:
+ scenario: ${{fromJson(needs.list-scenarios.outputs.matrix)}}
+ steps:
+ - uses: actions/checkout@v4
+ - uses: actions/setup-python@v4
+ with:
+ python-version: '3.9'
+ - name: Install Ansible & Molecule
+ run: |
+ pip install "ansible<8" "ansible-lint<6.13" flake8
+ pip install "molecule<5" "ansible-compat<4"
+ pip install molecule-plugins[docker] pytest-testinfra
+ - name: Run molecule
+ run: molecule test -s "${{ matrix.scenario }}"
+
+ publish:
+ name: Galaxy
+ if: startsWith(github.ref, 'refs/tags')
+ needs:
+ - test
+ runs-on: ubuntu-22.04
+ steps:
+ - uses: actions/checkout@v4
+ - name: Read the role name
+ id: role-name
+ run: |
+ name=$(grep 'role_name' meta/main.yml | sed -r 's/^[^:]*:(.*)$/\1/' | tr -d '[:space:]')
+ echo "rolename=$name" >> "$GITHUB_OUTPUT"
+ - name: Publish to Galaxy
+ uses: ome/action-ansible-galaxy-publish@main
+ with:
+ galaxy-api-key: ${{ secrets.GALAXY_API_KEY }}
+ galaxy-version: ${{ github.ref_name }}
+ role-name: ${{ steps.role-name.outputs.rolename }}
diff --git a/omero/roles/ome.omero_user/.gitignore b/omero/roles/ome.omero_user/.gitignore
new file mode 100644
index 00000000..b6261c93
--- /dev/null
+++ b/omero/roles/ome.omero_user/.gitignore
@@ -0,0 +1 @@
+.*~
diff --git a/omero/roles/ome.omero_user/LICENSE.md b/omero/roles/ome.omero_user/LICENSE.md
new file mode 100644
index 00000000..a3181283
--- /dev/null
+++ b/omero/roles/ome.omero_user/LICENSE.md
@@ -0,0 +1,22 @@
+Copyright (c) 2019, University of Dundee
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/omero/roles/ome.omero_user/README.md b/omero/roles/ome.omero_user/README.md
new file mode 100644
index 00000000..6b418231
--- /dev/null
+++ b/omero/roles/ome.omero_user/README.md
@@ -0,0 +1,84 @@
+OMERO user
+==========
+
+[![Actions Status](https://github.com/ome/ansible-role-omero-user/workflows/Molecule/badge.svg)](https://github.com/ome/ansible-role-omero-user/actions)
+[![Ansible Role](https://img.shields.io/badge/ansible--galaxy-omero_user-blue.svg)](https://galaxy.ansible.com/ui/standalone/roles/ome/omero_user/)
+
+Create OMERO user accounts and groups.
+This will not modify existing users or groups, apart from the user password if `force` is set.
+
+
+Dependencies
+------------
+
+Assumes the `ome.omero_server` role is installed with defaults, if not you will have to set most of the role variables below.
+
+This requires features present in OMERO 5.X.
+
+
+Role Variables
+--------------
+
+All variables are optional, see `defaults/main.yml`
+
+Create OMERO user accounts and groups:
+- `omero_user_bin_omero`: The full path to `bin/omero` application, default `/opt/omero/server/OMERO.server/bin/omero`
+- `omero_user_system`: Run the `omero` CLI as this user, default `omero` (must not be `root`)
+- `omero_user_admin_user`: Login to OMERO as this admin user, default `root`
+- `omero_user_admin_pass`: Password for `omero_user_admin_user`
+
+- `omero_user_create`: List of dictionaries of OMERO users to create with fields:
+ - `login`: OMERO user-name
+ - `firstname`: First name
+ - `lastname`: Last name
+ - `password`: Password
+ - `groups`: String containing group arguments (see `bin/omero user add --help`), this must be quoted if the group-names contain spaces or other special characters
+ - `force`: Forcibly reset password, default `False` (requires direct database access)
+- `omero_user_reset_root_password`: The new OMERO `root` password (requires direct database access)
+- `omero_group_create`: List of dictionaries of OMERO groups to create with fields:
+ - `name`: Group name
+ - `type`: Group type
+
+Database connection parameters (required if forcibly resetting OMERO user passwords):
+- `omero_user_dbhost`: Database host
+- `omero_user_dbuser`: Database user
+- `omero_user_dbname`: Database name
+- `omero_user_dbpassword`: Database password
+
+
+Example
+-------
+
+Create the user account `public` and group `demo` if it doesn't exist
+
+ - hosts: omero-servers
+ roles:
+ - ome.omero_user
+ vars:
+ - omero_group_create:
+ - name: demo
+ type: read-only
+ - omero_user_create:
+ - login: public
+ firstname: public
+ lastname: user
+ password: public
+ groups: "--group-name demo"
+
+Reset the OMERO `root` password:
+
+ - hosts: omero-servers
+ roles:
+ - ome.omero_user
+ vars:
+ - omero_user_reset_root_password: "omero root password"
+ #- omero_user_dbhost: localhost
+ #- omero_user_dbuser: omero
+ #- omero_user_dbname: omero
+ #- omero_user_dbpassword: omero
+
+
+Author Information
+------------------
+
+ome-devel@lists.openmicroscopy.org.uk
diff --git a/omero/roles/ome.omero_user/defaults/main.yml b/omero/roles/ome.omero_user/defaults/main.yml
new file mode 100644
index 00000000..3aa7026d
--- /dev/null
+++ b/omero/roles/ome.omero_user/defaults/main.yml
@@ -0,0 +1,17 @@
+---
+# defaults file for roles/omero-user
+
+omero_user_bin_omero: /opt/omero/server/OMERO.server/bin/omero
+omero_user_system: omero
+omero_user_admin_user: root
+omero_user_admin_pass: omero
+
+omero_user_create: []
+omero_group_create: []
+
+omero_user_reset_root_password: ''
+
+omero_user_dbhost: localhost
+omero_user_dbuser: omero
+omero_user_dbname: omero
+omero_user_dbpassword: omero
diff --git a/omero/roles/ome.omero_user/meta/.galaxy_install_info b/omero/roles/ome.omero_user/meta/.galaxy_install_info
new file mode 100644
index 00000000..4f8f9305
--- /dev/null
+++ b/omero/roles/ome.omero_user/meta/.galaxy_install_info
@@ -0,0 +1,2 @@
+install_date: Wed 13 Nov 17:34:03 2024
+version: 0.4.0
diff --git a/omero/roles/ome.omero_user/meta/main.yml b/omero/roles/ome.omero_user/meta/main.yml
new file mode 100644
index 00000000..c6e7eccc
--- /dev/null
+++ b/omero/roles/ome.omero_user/meta/main.yml
@@ -0,0 +1,19 @@
+---
+galaxy_info:
+ author: ome-devel@lists.openmicroscopy.org.uk
+ description: Create OMERO user accounts and groups
+ company: Open Microscopy Environment
+ license: BSD
+ min_ansible_version: 2.10
+ platforms:
+ - name: EL
+ versions:
+ - 9
+ - name: Ubuntu
+ versions:
+ - jammy
+ namespace: ome
+ role_name: omero_user
+ galaxy_tags: []
+
+dependencies: []
diff --git a/omero/roles/ome.omero_user/molecule/default/molecule.yml b/omero/roles/ome.omero_user/molecule/default/molecule.yml
new file mode 100644
index 00000000..59be675b
--- /dev/null
+++ b/omero/roles/ome.omero_user/molecule/default/molecule.yml
@@ -0,0 +1,36 @@
+---
+dependency:
+ name: galaxy
+driver:
+ name: docker
+lint: |
+ yamllint .
+ ansible-lint
+ flake8
+platforms:
+ - name: rockylinux-9
+ image: eniocarboni/docker-rockylinux-systemd:9
+ command: /sbin/init
+ privileged: true
+ cgroupns_mode: host
+ tmpfs:
+ - /sys/fs/cgroup
+ groups:
+ - extra_options
+ - name: ubuntu-2204
+ image: eniocarboni/docker-ubuntu-systemd:22.04
+ command: /sbin/init
+ privileged: true
+ cgroupns_mode: host
+ tmpfs:
+ - /sys/fs/cgroup
+ groups:
+ - extra_options
+provisioner:
+ name: ansible
+ lint:
+ name: ansible-lint
+scenario:
+ name: default
+verifier:
+ name: testinfra
diff --git a/omero/roles/ome.omero_user/molecule/default/playbook.yml b/omero/roles/ome.omero_user/molecule/default/playbook.yml
new file mode 100644
index 00000000..0c0afafc
--- /dev/null
+++ b/omero/roles/ome.omero_user/molecule/default/playbook.yml
@@ -0,0 +1,5 @@
+---
+- name: Converge
+ hosts: all
+ roles:
+ - role: ome.omero_user
diff --git a/omero/roles/ome.omero_user/molecule/default/tests/test_default.py b/omero/roles/ome.omero_user/molecule/default/tests/test_default.py
new file mode 100644
index 00000000..eedd64a1
--- /dev/null
+++ b/omero/roles/ome.omero_user/molecule/default/tests/test_default.py
@@ -0,0 +1,14 @@
+import os
+
+import testinfra.utils.ansible_runner
+
+testinfra_hosts = testinfra.utils.ansible_runner.AnsibleRunner(
+ os.environ['MOLECULE_INVENTORY_FILE']).get_hosts('all')
+
+
+def test_hosts_file(host):
+ f = host.file('/etc/hosts')
+
+ assert f.exists
+ assert f.user == 'root'
+ assert f.group == 'root'
diff --git a/omero/roles/ome.omero_user/tasks/groups.yml b/omero/roles/ome.omero_user/tasks/groups.yml
new file mode 100644
index 00000000..184e32f7
--- /dev/null
+++ b/omero/roles/ome.omero_user/tasks/groups.yml
@@ -0,0 +1,39 @@
+---
+# tasks file for roles/omero-user
+
+# Always run including in check mode
+- name: omero user | current group list
+ become: true
+ become_user: "{{ omero_user_system }}"
+ command: >
+ {{ omero_user_bin_omero }} group -C
+ -s localhost
+ -u {{ omero_user_admin_user }}
+ -w {{ omero_user_admin_pass | quote }}
+ list
+ -q
+ --style json
+ register: groupcmd
+ check_mode: false
+ changed_when: false
+
+- name: omero user | set group list var
+ set_fact:
+ omero_group_list: "{{ groupcmd.stdout | from_json }}"
+
+- name: omero user | create group
+ become: true
+ become_user: "{{ omero_user_system }}"
+ command: >
+ {{ omero_user_bin_omero }} group -C
+ -s localhost
+ -u {{ omero_user_admin_user }}
+ -w {{ omero_user_admin_pass | quote }}
+ add
+ {{ item.name | quote }}
+ --type {{ item.type }}
+ when: >
+ (omero_group_list | selectattr('name', 'equalto', item.name) |
+ list | length) < 1
+ with_items:
+ - "{{ omero_group_create }}"
diff --git a/omero/roles/ome.omero_user/tasks/main.yml b/omero/roles/ome.omero_user/tasks/main.yml
new file mode 100644
index 00000000..a956c04d
--- /dev/null
+++ b/omero/roles/ome.omero_user/tasks/main.yml
@@ -0,0 +1,14 @@
+---
+# tasks file for roles/omero-user
+
+- include: reset-root-password.yml
+ when: "omero_user_reset_root_password | length > 0"
+
+- include: groups.yml
+ when: "omero_group_create | length > 0"
+
+- include: users.yml
+ when: "omero_user_create | length > 0"
+
+- include: reset-password.yml
+ when: "omero_user_create | length > 0"
diff --git a/omero/roles/ome.omero_user/tasks/reset-password.yml b/omero/roles/ome.omero_user/tasks/reset-password.yml
new file mode 100644
index 00000000..e4f4d1a4
--- /dev/null
+++ b/omero/roles/ome.omero_user/tasks/reset-password.yml
@@ -0,0 +1,34 @@
+---
+# tasks file for roles/omero-user
+
+# If user isn't in `omero_user_list` then skip, since user should be newly
+# created and therefore shouldn't need a password reset
+- name: omero user | get password update command
+ become: true
+ become_user: "{{ omero_user_system }}"
+ command: >
+ {{ omero_user_bin_omero }} db password
+ --user-id
+ {{ (omero_user_list |
+ selectattr('login', 'equalto', item.login) | first).id }}
+ {{ item.password | quote }}
+ when: >
+ ((omero_user_list | selectattr('login', 'equalto', item.login) |
+ list | length ) > 0) and
+ (item.force | default(False))
+ with_items:
+ - "{{ omero_user_create }}"
+ register: pwdupdate
+
+- name: omero user | force reset omero password
+ command: >
+ psql
+ -h {{ omero_user_dbhost }}
+ -U {{ omero_user_dbuser }}
+ -d {{ omero_user_dbname }}
+ -w -c
+ {{ item.stdout | quote }}
+ when: "item is changed"
+ with_items: "{{ pwdupdate.results }}"
+ environment:
+ PGPASSWORD: "{{ omero_user_dbpassword }}"
diff --git a/omero/roles/ome.omero_user/tasks/reset-root-password.yml b/omero/roles/ome.omero_user/tasks/reset-root-password.yml
new file mode 100644
index 00000000..421b3a21
--- /dev/null
+++ b/omero/roles/ome.omero_user/tasks/reset-root-password.yml
@@ -0,0 +1,29 @@
+---
+# tasks file for roles/omero-user
+
+# Forcibly reset OMERO root password
+- name: omero user | get root password update command
+ become: true
+ become_user: "{{ omero_user_system }}"
+ command: >
+ {{ omero_user_bin_omero }} db password
+ --user-id 0
+ {{ omero_user_reset_root_password | quote }}
+ register: rootpwdupdate
+ # TODO: Is there a way to make this idempotent?
+ tags:
+ - skip_ansible_lint
+
+# WARNING: this will always run
+- name: omero user | force reset omero root password
+ command: >
+ psql
+ -h {{ omero_user_dbhost }}
+ -U {{ omero_user_dbuser }}
+ -d {{ omero_user_dbname }}
+ -w -c
+ {{ rootpwdupdate.stdout | quote }}
+ environment:
+ PGPASSWORD: "{{ omero_user_dbpassword }}"
+ # This conditional ensures this task won't fail in check-mode
+ when: "rootpwdupdate is changed"
diff --git a/omero/roles/ome.omero_user/tasks/users.yml b/omero/roles/ome.omero_user/tasks/users.yml
new file mode 100644
index 00000000..f68be8d4
--- /dev/null
+++ b/omero/roles/ome.omero_user/tasks/users.yml
@@ -0,0 +1,42 @@
+---
+# tasks file for roles/omero-user
+
+# Always run including in check mode
+- name: omero user | current user list
+ become: true
+ become_user: "{{ omero_user_system }}"
+ command: >
+ {{ omero_user_bin_omero }} user -C
+ -s localhost
+ -u {{ omero_user_admin_user }}
+ -w {{ omero_user_admin_pass | quote }}
+ list
+ -q
+ --style json
+ register: usercmd
+ check_mode: false
+ changed_when: false
+
+- name: omero user | set user list var
+ set_fact:
+ omero_user_list: "{{ usercmd.stdout | from_json }}"
+
+- name: omero user | create user
+ become: true
+ become_user: "{{ omero_user_system }}"
+ command: >
+ {{ omero_user_bin_omero }} user -C
+ -s localhost
+ -u {{ omero_user_admin_user }}
+ -w {{ omero_user_admin_pass | quote }}
+ add
+ {{ item.login | quote }}
+ {{ item.firstname | quote }}
+ {{ item.lastname | quote }}
+ --userpassword {{ item.password | quote }}
+ {{ item.groups }}
+ when: >
+ (omero_user_list | selectattr('login', 'equalto', item.login) |
+ list | length) < 1
+ with_items:
+ - "{{ omero_user_create }}"
diff --git a/omero/roles/ome.omero_user/tests/inventory b/omero/roles/ome.omero_user/tests/inventory
new file mode 100644
index 00000000..2fbb50c4
--- /dev/null
+++ b/omero/roles/ome.omero_user/tests/inventory
@@ -0,0 +1 @@
+localhost
diff --git a/omero/roles/ome.omero_user/tests/test.yml b/omero/roles/ome.omero_user/tests/test.yml
new file mode 100644
index 00000000..096bd853
--- /dev/null
+++ b/omero/roles/ome.omero_user/tests/test.yml
@@ -0,0 +1,5 @@
+---
+- hosts: localhost
+ remote_user: root
+ roles:
+ - role: ome.omero_user
diff --git a/omero/roles/ome.omero_web/.github/workflows/molecule.yml b/omero/roles/ome.omero_web/.github/workflows/molecule.yml
new file mode 100644
index 00000000..c3ac276b
--- /dev/null
+++ b/omero/roles/ome.omero_web/.github/workflows/molecule.yml
@@ -0,0 +1,62 @@
+---
+name: Molecule
+# yamllint disable-line rule:truthy
+on:
+ push:
+ pull_request:
+ schedule:
+ - cron: '29 21 * * 0'
+
+jobs:
+
+ list-scenarios:
+ runs-on: ubuntu-22.04
+ outputs:
+ matrix: ${{ steps.listscenarios.outputs.scenarios }}
+ steps:
+ - uses: actions/checkout@v4
+ - id: listscenarios
+ uses: ome/action-ansible-molecule-list-scenarios@main
+
+ test:
+ name: Test
+ needs:
+ - list-scenarios
+ runs-on: ubuntu-22.04
+ strategy:
+ # Keep running so we can see if other tests pass
+ fail-fast: false
+ matrix:
+ scenario: ${{fromJson(needs.list-scenarios.outputs.matrix)}}
+ steps:
+ - uses: actions/checkout@v4
+ - uses: actions/setup-python@v5
+ with:
+ python-version: '3.9'
+ - name: Install Ansible & Molecule
+ run: |
+ pip install "ansible<8" "ansible-lint<6.13" flake8
+ pip install "molecule<5" "ansible-compat<4"
+ pip install molecule-plugins[docker] pytest-testinfra
+ - name: Run molecule
+ run: molecule test -s "${{ matrix.scenario }}"
+
+ publish:
+ name: Galaxy
+ if: startsWith(github.ref, 'refs/tags')
+ needs:
+ - test
+ runs-on: ubuntu-22.04
+ steps:
+ - uses: actions/checkout@v4
+ - name: Read the role name
+ id: role-name
+ run: |
+ name=$(grep 'role_name' meta/main.yml | sed -r 's/^[^:]*:(.*)$/\1/' | tr -d '[:space:]') # noqa
+ echo "rolename=$name" >> "$GITHUB_OUTPUT"
+ - name: Publish to Galaxy
+ uses: ome/action-ansible-galaxy-publish@main
+ with:
+ galaxy-api-key: ${{ secrets.GALAXY_API_KEY }}
+ galaxy-version: ${{ github.ref_name }}
+ role-name: ${{ steps.role-name.outputs.rolename }}
diff --git a/omero/roles/ome.omero_web/.gitignore b/omero/roles/ome.omero_web/.gitignore
new file mode 100644
index 00000000..dba1d4d9
--- /dev/null
+++ b/omero/roles/ome.omero_web/.gitignore
@@ -0,0 +1,3 @@
+.*~
+*.pyc
+.molecule
diff --git a/omero/roles/ome.omero_web/CHANGES.md b/omero/roles/ome.omero_web/CHANGES.md
new file mode 100644
index 00000000..f4d5fbc6
--- /dev/null
+++ b/omero/roles/ome.omero_web/CHANGES.md
@@ -0,0 +1,33 @@
+# Changes in Version 4
+
+## Summary of breaking changes
+
+- Python 2 support is now dropped
+- `omero_web_python_requirements_ice_package` is now a nested dictionary to
+ support multiple versions per distribution
+
+## Removed variables
+
+- `omero_web_python3`: the role only installs OMERO.web with Python 3
+
+# Changes in Version 3
+
+## Summary of breaking changes
+- Default to installing and running under Python 3.6.
+ Set `omero_web_python3: false` to use Python 2.7.
+- `/opt/omero/web/OMERO.web/` is a directory not a symlink.
+- The virtualenv path is `/opt/omero/web/venv3` and does not include system-site-packages.
+- Home directory of `omero_web_system_user` is changed from `/opt/omero/web` to `/opt/omero/web/OMERO.web/var`.
+ This increases security by restricting the directories that are writeable by `omero_web_system_user`.
+- The [omero-web-apps](https://galaxy.ansible.com/ome/omero_web_apps) role has been merged into this role.
+- `omero_web_release` does not support `latest`, only `present` and full versions.
+
+## Removed variables
+- `omero_web_ice_version`: This is now an internal variable and must always be `3.6`.
+
+
+# Changes in Version 2
+
+## Removed variables
+- `omero_web_upgrade`: This variable is now an internal variable.
+ Upgrades are automatically executed depending on the value of `omero_web_release` which can be set to `present`, `latest` or a fixed version.
diff --git a/omero/roles/ome.omero_web/LICENSE.md b/omero/roles/ome.omero_web/LICENSE.md
new file mode 100644
index 00000000..a3181283
--- /dev/null
+++ b/omero/roles/ome.omero_web/LICENSE.md
@@ -0,0 +1,22 @@
+Copyright (c) 2019, University of Dundee
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/omero/roles/ome.omero_web/README.md b/omero/roles/ome.omero_web/README.md
new file mode 100644
index 00000000..7e6bb484
--- /dev/null
+++ b/omero/roles/ome.omero_web/README.md
@@ -0,0 +1,132 @@
+OMERO Web
+=========
+
+[![Actions Status](https://github.com/ome/ansible-role-omero-web/workflows/Molecule/badge.svg)](https://github.com/ome/ansible-role-omero-web/actions)
+[![Ansible Role](https://img.shields.io/badge/ansible--galaxy-omero_web-blue.svg)](https://galaxy.ansible.com/ui/standalone/roles/ome/omero_web/)
+
+Installs and configures OMERO.web and Nginx.
+Uses a conf.d style configuration directory for managing the OMERO.web configuration.
+
+**Warning:** Python 2 support is dropped.
+See [`CHANGES.md`](./CHANGES.md) for details.
+
+
+Role Variables
+--------------
+
+All variables are optional, see `defaults/main.yml` for the full list
+
+OMERO.web version and installation.
+- `omero_web_release`: The OMERO.web release, e.g. `5.9.1`.
+ The default is `present` which will install the latest version if web is not already installed, but will not modify an existing web.
+ Use `latest` to automatically upgrade when a new version is released.
+- `omero_web_system_user`: OMERO.web system user, default `omero-web`.
+- `omero_web_system_uid`: OMERO.web system user ID (default automatic)
+
+OMERO.web configuration.
+- `omero_web_config_set`: A dictionary of `config-key: value` which will be used for the initial OMERO.web configuration, default empty.
+ `value` can be a string, or an object (list, dictionary) that will be automatically converted to quoted JSON.
+ Note configuration can also be done pre/post installation using the `web/config` conf.d style directory.
+- `omero_web_setup_nginx`: Install and configure Nginx, default `True`.
+
+OMERO.web systemd configuration
+- `omero_web_systemd_setup`: Create and start the `omero-web` systemd service, default `True`
+- `omero_web_systemd_limit_nofile`: Systemd limit for number of open files (default ignore)
+- `omero_web_systemd_after`: A list of strings with additional service names to appear in systemd unit file "After" statements. Default empty/none.
+- `omero_web_systemd_requires`: A list of strings with additional service names to appear in systemd unit file "Requires" statements. Default empty/none.
+
+Python 3 only parameters.
+- `omero_web_python_addons`: List of additional Python packages to be installed into virtualenv
+
+The [omero-web-apps](https://galaxy.ansible.com/ome/omero_web_apps) role has been merged into this role for Python 3 deployments.
+All variables are optional:
+- `omero_web_apps_names`: List of web application names to be appended to `omero.web.apps`
+- `omero_web_apps_packages`: List of pip installable packages
+- `omero_web_apps_top_links`: Lists of top link dictionaries to be appended to `omero.web.ui.top_links`, of the form:
+ - `label`: Label
+ - `link`: URL or a dict
+ - `attrs`: Dictionary of attributes (optional)
+- `omero_web_apps_ui_metadata_panes`: Items to be appended to `omero.web.ui.metadata_panes`
+- `omero_web_apps_config_append`: Dictionary of other key-[list of values] pairs to be appended (multiple values can be appended to the same key)
+- `omero_web_apps_config_set`: Dictionary of other key-value pairs to be set
+- `omero_web_apps_config_name`: The basename of the configuration file (`web/config/{{ omero_web_apps_config_name }}.omero`)
+
+
+
+Unstable features
+-----------------
+
+Variables :
+- `omero_web_systemd_start`: Automatically enable and start/restart systemd omero-web service, default `True`.
+ This is intended for use in server images where installation may be separate from configuration and execution.
+- `omero_web_always_reset_config`: Clear the existing configuration before regenerating, default `True`.
+
+It should be safe to use this role to deploy OMERO.web inside a standard `centos:7` Docker container without systemd (`omero_web_systemd_setup: False`).
+
+
+
+Configuring OMERO.web
+---------------------
+
+This role regenerates the OMERO.web configuration file using the configuration files and helper script in `/opt/omero/web/config`.
+`omero_web_config_set` can be used for simple configurations, for anything more complex consider creating one or more configuration files under: `/opt/omero/web/config/` with the extension `.omero`.
+
+Manual configuration changes (`omero config ...`) will be lost following a restart of `omero-web` with systemd, you can disable this by setting `omero_web_always_reset_config: False`.
+Manual configuration changes will never be copied during an upgrade.
+
+See https://github.com/ome/design/issues/70 for a proposal to add support for a conf.d style directory directly into OMERO.
+
+
+Example Playbooks
+-----------------
+
+OMERO.web with the default backend server, `localhost:4064`:
+
+ - hosts: localhost
+ roles:
+ - role: ome.omero_web
+
+OMERO.web with a custom configuration using `omero_web_config_set`:
+
+ - hosts: localhost
+ roles:
+ - role: ome.omero_web
+ omero_web_config_set:
+ omero.web.server_list:
+ - [omero.example.org, 4064, omero-example]
+ omero.web.public.enabled: True
+ omero.web.public.server_id: 1
+ omero.web.public.user: public
+ omero.web.public.password: secret-password
+
+OMERO.web with the redis session engine
+
+ hosts: localhost
+ roles:
+ - role: ome.omero_web
+ omero_web_setup_redis_session: true
+ omero_web_config_set:
+ "omero.web.caches":
+ "default":
+ "BACKEND": "django_redis.cache.RedisCache"
+ "LOCATION": "redis://127.0.0.1:6379/0"
+ "omero.web.session_engine": "django.contrib.sessions.backends.cache"
+
+OMERO.web with a custom configuration using a configuration file `web-custom-config.omero`:
+
+ - hosts: localhost
+ roles:
+ - role: ome.omero_web
+ tasks:
+ - copy:
+ content: >
+ config set omero.web.server_list '[["omero.example.org", 4064, "omero-example"]'
+ dest: /opt/omero/web/config/web-custom-config.omero
+ notify:
+ - restart omero-web
+
+
+Author Information
+------------------
+
+ome-devel@lists.openmicroscopy.org.uk
diff --git a/omero/roles/ome.omero_web/defaults/main.yml b/omero/roles/ome.omero_web/defaults/main.yml
new file mode 100644
index 00000000..b3e3340c
--- /dev/null
+++ b/omero/roles/ome.omero_web/defaults/main.yml
@@ -0,0 +1,139 @@
+---
+# defaults for omero-web
+
+omero_web_release: present
+
+# OMERO.web system user
+omero_web_system_user: omero-web
+
+# OMERO.web system user ID (If not defined one is chosen automatically)
+# omero_web_system_uid:
+
+# Setup systemd services
+omero_web_systemd_setup: true
+
+# Change the systemd limit for number of open files (default ignore)
+omero_web_systemd_limit_nofile:
+
+# Services which OMERO web needs to be running before it can start, such as
+# remote storage.
+omero_web_systemd_after: []
+
+# Services which OMERO web needs to be concurrently running.
+omero_web_systemd_requires: []
+
+# Configuration key-values to be set
+omero_web_config_set: {}
+
+# Install and configure Nginx
+omero_web_setup_nginx: true
+
+# Install required package to deploy redis session engine
+omero_web_setup_redis_session: false
+
+# List of additional Python packages to be installed into virtualenv
+omero_web_python_addons: []
+
+
+######################################################################
+# defaults from the old omero-web-apps
+######################################################################
+
+# The pip installable package
+omero_web_apps_packages: []
+
+# The name of the web application, used for configuration
+omero_web_apps_names: []
+
+# Top links
+omero_web_apps_top_links: []
+
+# UI metadata panes
+omero_web_apps_ui_metadata_panes: []
+
+# Other list value properties (dict, append)
+omero_web_apps_config_append: {}
+
+# Other key value properties (dict, set)
+omero_web_apps_config_set: {}
+
+# The basename of the configuration file
+omero_web_apps_config_name: omero-web-apps
+
+
+######################################################################
+# Expert users only!
+######################################################################
+
+omero_web_ice_version: "3.6"
+
+omero_web_python_requirements_ice_package:
+ RedHat:
+ 9: "https://github.com/glencoesoftware/zeroc-ice-py-rhel9-x86_64/releases/download/\
+ 20230830/zeroc_ice-3.6.5-cp39-cp39-linux_x86_64.whl"
+ Debian:
+ 22: "https://github.com/glencoesoftware/zeroc-ice-py-ubuntu2204-x86_64/releases/download/\
+ 20221004/zeroc_ice-3.6.5-cp310-cp310-linux_x86_64.whl"
+
+# List of python3 packages to install
+omero_web_python_requirements:
+ # TODO: make the use of our non-standard wheel optional
+ - "{{ omero_web_python_requirements_ice_package[ansible_os_family][
+ ansible_distribution_major_version | int ] |
+ default('zeroc-ice')}}"
+ # Let omero-web bring this in
+ # - omero-py=={{ _omero_py_version | default('5.6.dev4') }}
+ - "omero-web{{ (omero_web_release == 'present') |
+ ternary('', '==' + omero_web_release) }}"
+ - redis
+
+# Path to virtualenv
+omero_web_virtualenv_basedir: "{{ omero_web_basedir + '/venv3' }}"
+
+# OMERODIR
+omero_web_omerodir: "{{ omero_web_basedir }}/{{ omero_web_symlink }}"
+
+# How to run omero
+omero_web_omero_command: "{{ omero_web_omerodir + '/bin/omero' }}"
+
+# Config update command
+omero_web_config_update: >-
+ {{
+ omero_web_omero_command + ' load --glob ' + omero_web_basedir +
+ '/config/*.omero'
+ }}
+
+# DEVELOPMENT: If True clear the existing configuration before regenerating
+omero_web_always_reset_config: true
+
+# DEVELOPMENT: Automatically start systemd omero-web
+omero_web_systemd_start: true
+
+# Base directory for the web installation
+omero_web_basedir: "{{ omero_common_basedir }}/web"
+
+# Symlink to the currently installed OMERO.web
+omero_web_symlink: OMERO.web
+
+# Path of omego
+omero_web_omego: "{{ omero_common_basedir }}/omego/bin/omego"
+
+# Additional omego aguments passed to upgrade or install
+omero_web_omego_additional_args: ""
+
+# If True and web is already installed then upgrade to the version in
+# omero_web_release, otherwise don't upgrade an existing web.
+# This should not be needed if versions are correctly compared.
+omero_web_upgrade: true
+
+# DEVELOPMENT: Operator for comparing current-version against
+# omero_web_release, e.g. '!='. Default is to upgrade when
+# current-version < omero_web_release
+omero_web_checkupgrade_comparator: '<'
+
+omero_web_omego_options: >
+ --release {{ _omero_web_new_version }}
+ --sym {{ omero_web_symlink }}
+ --ice {{ omero_web_ice_version }}
+ -qq
+ {{ omero_web_omego_additional_args }}
diff --git a/omero/roles/ome.omero_web/handlers/main.yml b/omero/roles/ome.omero_web/handlers/main.yml
new file mode 100644
index 00000000..0afe19de
--- /dev/null
+++ b/omero/roles/ome.omero_web/handlers/main.yml
@@ -0,0 +1,22 @@
+---
+# handlers for omero-web
+# Don't use omero-common handlers because systemd might be disabled.
+# This also avoids problems with ordering of handlers:
+# http://stackoverflow.com/a/35130254
+
+- name: omero-web rewrite omero-web configuration
+ become: true
+ become_user: "{{ omero_web_system_user }}"
+ command: "{{ omero_web_config_update }}"
+
+- name: omero-web restart omero-web
+ become: true
+ service:
+ name: omero-web
+ state: restarted
+ when: omero_web_systemd_setup and omero_web_systemd_start
+
+- name: omero-web Reload SELinux
+ become: true
+ command: load_policy
+ when: selinux_enabled
diff --git a/omero/roles/ome.omero_web/meta/.galaxy_install_info b/omero/roles/ome.omero_web/meta/.galaxy_install_info
new file mode 100644
index 00000000..50b5f512
--- /dev/null
+++ b/omero/roles/ome.omero_web/meta/.galaxy_install_info
@@ -0,0 +1,2 @@
+install_date: Wed 13 Nov 17:33:50 2024
+version: 5.1.1
diff --git a/omero/roles/ome.omero_web/meta/main.yml b/omero/roles/ome.omero_web/meta/main.yml
new file mode 100644
index 00000000..37b9b2bb
--- /dev/null
+++ b/omero/roles/ome.omero_web/meta/main.yml
@@ -0,0 +1,17 @@
+---
+galaxy_info:
+ author: ome-devel@lists.openmicroscopy.org.uk
+ description: Install and configure OMERO.web
+ company: Open Microscopy Environment
+ license: BSD
+ min_ansible_version: 2.10
+ platforms:
+ - name: EL
+ versions:
+ - 9
+ - name: Ubuntu
+ versions:
+ - jammy
+ role_name: omero_web
+ namespace: ome
+ galaxy_tags: []
diff --git a/omero/roles/ome.omero_web/meta/requirements.yml b/omero/roles/ome.omero_web/meta/requirements.yml
new file mode 100644
index 00000000..ae5e6709
--- /dev/null
+++ b/omero/roles/ome.omero_web/meta/requirements.yml
@@ -0,0 +1,8 @@
+---
+
+- src: ome.omero_common
+- src: ome.nginx
+- src: ome.python3_virtualenv
+- src: ome.selinux_utils
+- src: ome.redis
+- src: ome.basedeps
diff --git a/omero/roles/ome.omero_web/molecule/active-rockylinux9/molecule.yml b/omero/roles/ome.omero_web/molecule/active-rockylinux9/molecule.yml
new file mode 100755
index 00000000..df5902a2
--- /dev/null
+++ b/omero/roles/ome.omero_web/molecule/active-rockylinux9/molecule.yml
@@ -0,0 +1,41 @@
+---
+dependency:
+ name: galaxy
+ options:
+ role-file: molecule/resources/requirements.yml
+driver:
+ name: docker
+lint: |
+ yamllint .
+ ansible-lint
+ flake8
+platforms:
+ - name: omero-web-active-rockylinux9
+ image: eniocarboni/docker-rockylinux-systemd:9
+ image_version: latest
+ command: /sbin/init
+ privileged: true
+ cgroupns_mode: host
+ tmpfs:
+ - /sys/fs/cgroup
+ groups:
+ - extra_options
+provisioner:
+ name: ansible
+ lint:
+ name: ansible-lint
+ playbooks:
+ converge: ../resources/playbook.yml
+ inventory:
+ host_vars:
+ omero-web-active-rockylinux9:
+ options:
+ v: true
+ diff: true
+scenario:
+ name: active-rockylinux9
+ converge_sequence:
+ - converge
+verifier:
+ name: testinfra
+ directory: ../resources/tests/
diff --git a/omero/roles/ome.omero_web/molecule/active-ubuntu2204/molecule.yml b/omero/roles/ome.omero_web/molecule/active-ubuntu2204/molecule.yml
new file mode 100755
index 00000000..945f4817
--- /dev/null
+++ b/omero/roles/ome.omero_web/molecule/active-ubuntu2204/molecule.yml
@@ -0,0 +1,43 @@
+---
+dependency:
+ name: galaxy
+ options:
+ role-file: molecule/resources/requirements.yml
+driver:
+ name: docker
+lint: |
+ yamllint .
+ ansible-lint
+ flake8
+platforms:
+ - name: omero-web-active-ubuntu2204
+ image: eniocarboni/docker-ubuntu-systemd:22.04
+ command: /sbin/init
+ privileged: true
+ cgroupns_mode: host
+ tmpfs:
+ - /sys/fs/cgroup
+ groups:
+ - extra_options
+
+provisioner:
+ name: ansible
+ lint:
+ name: ansible-lint
+ playbooks:
+ # TODO: Install curl in basedeps instead
+ prepare: prepare.yml
+ converge: ../resources/playbook.yml
+ inventory:
+ host_vars:
+ omero-web-active-ubuntu2204:
+ options:
+ v: true
+ diff: true
+scenario:
+ name: active-ubuntu2204
+ converge_sequence:
+ - converge
+verifier:
+ name: testinfra
+ directory: ../resources/tests/
diff --git a/omero/roles/ome.omero_web/molecule/active-ubuntu2204/prepare.yml b/omero/roles/ome.omero_web/molecule/active-ubuntu2204/prepare.yml
new file mode 100644
index 00000000..412fb7aa
--- /dev/null
+++ b/omero/roles/ome.omero_web/molecule/active-ubuntu2204/prepare.yml
@@ -0,0 +1,19 @@
+---
+# Curl isn't installed, needed for tests
+
+- name: Prepare
+ hosts: all
+
+ tasks:
+ - name: Install packages
+ become: true
+ ansible.builtin.apt:
+ update_cache: true
+ name:
+ - curl
+ - gnupg
+ - ca-certificates
+ - python3
+ - sudo
+ - bash
+ state: present
diff --git a/omero/roles/ome.omero_web/molecule/inactive/molecule.yml b/omero/roles/ome.omero_web/molecule/inactive/molecule.yml
new file mode 100644
index 00000000..e7ea9983
--- /dev/null
+++ b/omero/roles/ome.omero_web/molecule/inactive/molecule.yml
@@ -0,0 +1,41 @@
+---
+dependency:
+ name: galaxy
+ options:
+ role-file: molecule/resources/requirements.yml
+driver:
+ name: docker
+lint: |
+ yamllint .
+ ansible-lint
+ flake8
+platforms:
+ - name: omero-web-inactive
+ image: eniocarboni/docker-rockylinux-systemd:9
+ command: /sbin/init
+ privileged: true
+ cgroupns_mode: host
+ tmpfs:
+ - /sys/fs/cgroup
+ groups:
+ - extra_options
+provisioner:
+ name: ansible
+ lint:
+ name: ansible-lint
+ playbooks:
+ converge: ../resources/playbook.yml
+ inventory:
+ host_vars:
+ omero-web-inactive:
+ # This tests a hypothetical instance where systemd is inactive but might
+ # be activated in future
+ # It also tests this role can be used to install omero.web on a system
+ # that will never have systemd, e.g. a simple docker container
+ omero_web_systemd_start: false
+ omero_web_setup_nginx: false
+ omero_web_release: present
+scenario:
+ name: inactive
+verifier:
+ name: testinfra
diff --git a/omero/roles/ome.omero_web/molecule/inactive/tests/test_default.py b/omero/roles/ome.omero_web/molecule/inactive/tests/test_default.py
new file mode 120000
index 00000000..f4a3648e
--- /dev/null
+++ b/omero/roles/ome.omero_web/molecule/inactive/tests/test_default.py
@@ -0,0 +1 @@
+../../resources/tests/test_default.py
\ No newline at end of file
diff --git a/omero/roles/ome.omero_web/molecule/resources/playbook.yml b/omero/roles/ome.omero_web/molecule/resources/playbook.yml
new file mode 100644
index 00000000..30028288
--- /dev/null
+++ b/omero/roles/ome.omero_web/molecule/resources/playbook.yml
@@ -0,0 +1,93 @@
+---
+# - name: Converge
+# hosts: all
+
+# pre_tasks:
+# # To test the upgrade process without breaking the idempotence check
+# # we need to add a flag so the first omero-web installation is
+# # only run once
+# - name: check whether omero-web has been installed at least once
+# stat:
+# path: /opt/omero/web/OMERO.web
+# register: molecule_test_omero_web_installed_1
+
+# roles:
+
+# - role: ansible-role-omero-web
+# omero_web_config_set:
+# omero.web.server_list:
+# - [localhost, 12345, molecule-test]
+# omero_web_release: "5.3"
+# when: not molecule_test_omero_web_installed_1.stat.exists
+
+# tasks:
+# - name: get omero-web version
+# become: true
+# become_user: omero-web
+# command: /opt/omero/web/OMERO.web/bin/omero version
+# register: molecule_test_omero_web_version
+# changed_when: false
+
+# - name: check version
+# assert:
+# that: molecule_test_omero_web_version.stdout.startswith('5.3.')
+# when: not molecule_test_omero_web_installed_1.stat.exists
+
+# vars:
+# ice_python_wheel: "https://github.com/ome/zeroc-ice-py-centos7/releases/\
+# download/0.1.0/zeroc_ice-3.6.4-cp27-cp27mu-linux_x86_64.whl"
+
+
+# Attempt to upgrade
+# omero_web_release is defined in molecule.yml host_vars so:
+# - omero-web: latest: upgraded
+# - omero-web-inactive: present: unchanged at 5.3
+- name: Converge upgrade
+ hosts: all
+ roles:
+ - role: ome.omero_web
+ omero_web_config_set:
+ omero.web.server_list:
+ - [localhost, 12345, molecule-test]
+
+ omero_web_apps_packages:
+ - omero-mapr
+ omero_web_apps_names:
+ - omero_mapr
+ omero_web_apps_top_links:
+ - label: OMERO
+ link:
+ viewname: webindex
+ query_string: {experimenter: -1}
+ attrs:
+ title: Image Data Repository
+ - label: Genes
+ link:
+ viewname: maprindex_gene
+ query_string: {experimenter: -1}
+ attrs:
+ title: Genes browser
+ omero_web_apps_config_append:
+ omero.web.mapr.config:
+ - menu: "gene"
+ config:
+ default:
+ - "Gene Symbol"
+ all:
+ - "Gene Symbol"
+ - "Gene Identifier"
+ ns:
+ - "openmicroscopy.org/mapr/gene"
+ label: "Gene"
+ case_sensitive: true
+ - menu: "genesupplementary"
+ config:
+ default: []
+ all: []
+ ns:
+ - "openmicroscopy.org/mapr/gene/supplementary"
+ label: "Gene supplementary"
+ omero_web_apps_config_set:
+ example.string: example value
+ example.boolean: true
+ example.integer: 2
diff --git a/omero/roles/ome.omero_web/molecule/resources/requirements.yml b/omero/roles/ome.omero_web/molecule/resources/requirements.yml
new file mode 100644
index 00000000..ae5e6709
--- /dev/null
+++ b/omero/roles/ome.omero_web/molecule/resources/requirements.yml
@@ -0,0 +1,8 @@
+---
+
+- src: ome.omero_common
+- src: ome.nginx
+- src: ome.python3_virtualenv
+- src: ome.selinux_utils
+- src: ome.redis
+- src: ome.basedeps
diff --git a/omero/roles/ome.omero_web/molecule/resources/tests/test_default.py b/omero/roles/ome.omero_web/molecule/resources/tests/test_default.py
new file mode 100644
index 00000000..0a6ae562
--- /dev/null
+++ b/omero/roles/ome.omero_web/molecule/resources/tests/test_default.py
@@ -0,0 +1,40 @@
+import os
+import re
+
+import testinfra.utils.ansible_runner
+
+testinfra_hosts = testinfra.utils.ansible_runner.AnsibleRunner(
+ os.environ['MOLECULE_INVENTORY_FILE']).get_hosts('all')
+
+
+OMERO = '/opt/omero/web/OMERO.web/bin/omero'
+# Need to match 5.6.dev2
+# VERSION_PATTERN = re.compile('(\d+)\.(\d+)\.(\d+)-ice36-')
+VERSION_PATTERN = re.compile(r'(\d+)\.(\d+)\.(\w+)')
+
+
+def test_omero_web_config(host):
+ with host.sudo('omero-web'):
+ cfg = host.check_output("%s config get omero.web.server_list" % OMERO)
+ assert cfg == '[["localhost", 12345, "molecule-test"]]'
+
+ with host.sudo('omero-web'):
+ keys = host.check_output("%s config keys" % OMERO)
+ assert sorted(keys.split()) == [
+ 'example.boolean',
+ 'example.integer',
+ 'example.string',
+ 'omero.web.apps',
+ 'omero.web.mapr.config',
+ 'omero.web.server_list',
+ 'omero.web.ui.top_links',
+ ]
+
+
+def test_omero_version(host):
+ with host.sudo('omero-web'):
+ ver = host.check_output("%s version" % OMERO)
+ m = VERSION_PATTERN.match(ver)
+ assert m is not None
+ assert int(m.group(1)) >= 5
+ assert int(m.group(2)) > 3
diff --git a/omero/roles/ome.omero_web/molecule/resources/tests/test_nginx.py b/omero/roles/ome.omero_web/molecule/resources/tests/test_nginx.py
new file mode 100644
index 00000000..36df8b53
--- /dev/null
+++ b/omero/roles/ome.omero_web/molecule/resources/tests/test_nginx.py
@@ -0,0 +1,24 @@
+import os
+import pytest
+
+import testinfra.utils.ansible_runner
+
+testinfra_hosts = testinfra.utils.ansible_runner.AnsibleRunner(
+ os.environ['MOLECULE_INVENTORY_FILE']).get_hosts('all')
+
+
+@pytest.mark.parametrize("name", ["omero-web", "nginx"])
+def test_services_running_and_enabled(host, name):
+ service = host.service(name)
+ assert service.is_running
+ assert service.is_enabled
+
+
+def test_nginx_gateway(host):
+ out = host.check_output('curl -L localhost')
+ assert 'OMERO.web - Login' in out
+
+
+def test_omero_web_config_applied(host):
+ out = host.check_output('curl -L localhost')
+ assert 'molecule-test:12345' in out
diff --git a/omero/roles/ome.omero_web/molecule/resources/tests/test_sepolicy.py b/omero/roles/ome.omero_web/molecule/resources/tests/test_sepolicy.py
new file mode 100644
index 00000000..6eb56f93
--- /dev/null
+++ b/omero/roles/ome.omero_web/molecule/resources/tests/test_sepolicy.py
@@ -0,0 +1,11 @@
+import os
+
+import testinfra.utils.ansible_runner
+
+testinfra_hosts = testinfra.utils.ansible_runner.AnsibleRunner(
+ os.environ['MOLECULE_INVENTORY_FILE']).get_hosts('all')
+
+
+def test_selinux_custom_policy(host):
+ out = host.check_output('semodule -l')
+ assert 'django' in out
diff --git a/omero/roles/ome.omero_web/molecule/resources/tests/test_webapps.py b/omero/roles/ome.omero_web/molecule/resources/tests/test_webapps.py
new file mode 100644
index 00000000..8cc9fef2
--- /dev/null
+++ b/omero/roles/ome.omero_web/molecule/resources/tests/test_webapps.py
@@ -0,0 +1,109 @@
+import os
+import json
+import pytest
+import testinfra.utils.ansible_runner
+
+testinfra_hosts = testinfra.utils.ansible_runner.AnsibleRunner(
+ os.environ['MOLECULE_INVENTORY_FILE']).get_hosts('all')
+
+OMERO = '/opt/omero/web/OMERO.web/bin/omero'
+
+
+def assert_jcfg(host, key, value, isjson):
+ with host.sudo('omero-web'):
+ cfg = host.check_output("%s config get %s", OMERO, key)
+ if isjson:
+ cfg = json.loads(cfg)
+ assert cfg == value
+
+
+@pytest.mark.parametrize("key,value", [
+ ('example.string', 'example value'),
+])
+# ('example.boolean', True),
+# ('example.integer', 2),
+def test_example_config(host, key, value):
+ assert_jcfg(host, key, value, False)
+
+
+def test_omero_web_apps(host):
+ assert_jcfg(host, 'omero.web.apps', ["omero_mapr"], True)
+
+
+def test_omero_web_mapr_config(host):
+ expected = [
+ {
+ "menu": "gene", "config": {
+ "default": ["Gene Symbol"],
+ "case_sensitive": True,
+ "all": ["Gene Symbol", "Gene Identifier"],
+ "ns": ["openmicroscopy.org/mapr/gene"],
+ "label": "Gene"
+ }
+ },
+ {
+ "menu": "genesupplementary",
+ "config": {
+ "default": [],
+ "all": [],
+ "ns": ["openmicroscopy.org/mapr/gene/supplementary"],
+ "label": "Gene supplementary"
+ }
+ }
+ ]
+ assert_jcfg(host, 'omero.web.mapr.config', expected, True)
+
+
+def test_omero_web_ui_toplinks(host):
+ expected = [
+ [
+ "Data",
+ "webindex",
+ {"title": "Browse Data via Projects, Tags etc"}
+ ],
+ [
+ "History",
+ "history",
+ {"title": "History"}
+ ],
+ [
+ "Help",
+ "https://help.openmicroscopy.org/",
+ {"target": "new", "title": "Open OMERO user guide in a new tab"}
+ ],
+ [
+ "OMERO",
+ {
+ "query_string": {"experimenter": -1},
+ "viewname": "webindex"
+ },
+ {"title": "Image Data Repository"}
+ ],
+ [
+ "Genes",
+ {
+ "query_string": {"experimenter": -1},
+ "viewname": "maprindex_gene"
+ },
+ {"title": "Genes browser"}
+ ]
+ ]
+ assert_jcfg(host, 'omero.web.ui.top_links', expected, True)
+
+
+def test_mapr_config(host):
+ config = {
+ 'gene': {
+ 'all': ['Gene Symbol', 'Gene Identifier'],
+ 'case_sensitive': True,
+ 'default': ['Gene Symbol'],
+ 'label': 'Gene',
+ 'ns': ['openmicroscopy.org/mapr/gene']},
+ 'genesupplementary': {
+ 'all': [],
+ 'default': [],
+ 'label': 'Gene supplementary',
+ 'ns': ['openmicroscopy.org/mapr/gene/supplementary']}
+ }
+ out = host.check_output('curl -L http://localhost/mapr/api/config/')
+ assert json.loads(out) == config
diff --git a/omero/roles/ome.omero_web/tasks/main.yml b/omero/roles/ome.omero_web/tasks/main.yml
new file mode 100644
index 00000000..59ef76d6
--- /dev/null
+++ b/omero/roles/ome.omero_web/tasks/main.yml
@@ -0,0 +1,14 @@
+---
+# tasks for omero-web
+-
+- include: pre_tasks.yml
+
+- include: web-dependencies.yml
+
+- include: web-install-py3.yml
+
+- include: web-systemd.yml
+ when: omero_web_systemd_setup
+
+- include: web-nginx.yml
+ when: omero_web_setup_nginx
diff --git a/omero/roles/ome.omero_web/tasks/pre_tasks.yml b/omero/roles/ome.omero_web/tasks/pre_tasks.yml
new file mode 100644
index 00000000..3f95743d
--- /dev/null
+++ b/omero/roles/ome.omero_web/tasks/pre_tasks.yml
@@ -0,0 +1,21 @@
+---
+
+- ansible.builtin.import_role:
+ name: ome.omero_common
+
+- name: omero-web | Include ome.selinux_utils
+ ansible.builtin.include_role:
+ name: ome.selinux_utils
+
+- name: omero-web | Include ome.python3_virtualenv role
+ ansible.builtin.include_role:
+ name: ome.python3_virtualenv
+
+- name: omero-web | Include ome.nginx role
+ ansible.builtin.include_role:
+ name: ome.nginx
+ when: omero_web_setup_nginx
+
+- name: omero-web | Include ome.basedeps role
+ ansible.builtin.include_role:
+ name: ome.basedeps
diff --git a/omero/roles/ome.omero_web/tasks/web-dependencies.yml b/omero/roles/ome.omero_web/tasks/web-dependencies.yml
new file mode 100755
index 00000000..53323d2d
--- /dev/null
+++ b/omero/roles/ome.omero_web/tasks/web-dependencies.yml
@@ -0,0 +1,93 @@
+---
+# selinux
+- name: omero web | selinux booleans
+ become: true
+ seboolean:
+ name: "{{ item }}"
+ state: true
+ persistent: true
+ with_items:
+ - httpd_read_user_content
+ - httpd_enable_homedirs
+ - httpd_can_network_relay
+ when: selinux_enabled
+
+# Alternatively set httpd_can_network_connect=yes to allow all ports
+- name: omero web | selinux ports
+ become: true
+ seport:
+ ports: "4080"
+ proto: tcp
+ setype: http_port_t
+ state: present
+ when: selinux_enabled
+
+- name: omero web | register pp file
+ stat:
+ path: /tmp/django.pp
+ register: pp_file_name
+
+- name: omero web | copy te file
+ become: true
+ template:
+ dest: /tmp/django.te
+ force: true
+ src: django.te.j2
+ mode: 0644
+
+- name: omero web | install checkpolicy
+ become: true
+ ansible.builtin.dnf:
+ update_cache: true
+ name:
+ - checkpolicy
+ - policycoreutils
+ - ipa-selinux
+ # - selinux-policy-targeted
+ state: present
+ when: ansible_os_family | lower == 'redhat'
+
+- name: omero web | install checkpolicy for ubuntu
+ become: true
+ ansible.builtin.apt:
+ update_cache: true
+ name:
+ - checkpolicy
+ - policycoreutils
+ - semodule-utils
+ - selinux-basics
+ state: present
+ when: ansible_os_family | lower != 'redhat'
+
+- name: omero web | Compile into a policy module
+ become: true
+ command: "checkmodule -M -m -o /tmp/django.mod /tmp/django.te"
+ args:
+ creates: /tmp/django.mod
+ when: not pp_file_name.stat.exists
+
+- name: omero web | register mod file
+ stat:
+ path: /tmp/django.mod
+ register: mod_file_name
+
+- name: omero web | Package the policy module
+ become: true
+ command: "semodule_package -o /tmp/django.pp -m /tmp/django.mod"
+ args:
+ creates: /tmp/mypolicy.pp
+
+ when: not pp_file_name.stat.exists
+
+- name: omero web | Load the policy module
+ become: true
+ command: "semodule -i /tmp/django.pp"
+ notify:
+ - omero-web Reload SELinux
+ when: mod_file_name.stat.exists
+
+- name: omero web | delete mod file
+ become: true
+ ansible.builtin.file:
+ path: /tmp/django.mod
+ state: absent
diff --git a/omero/roles/ome.omero_web/tasks/web-install-py3.yml b/omero/roles/ome.omero_web/tasks/web-install-py3.yml
new file mode 100644
index 00000000..d3b05558
--- /dev/null
+++ b/omero/roles/ome.omero_web/tasks/web-install-py3.yml
@@ -0,0 +1,169 @@
+---
+# install OMERO.web
+
+# TODO: allow version to be specified, handle latest/present etc
+# E.g. get version info from https://pypi.org/pypi/omero-web/json
+# and parse using
+# https://stackoverflow.com/questions/54025894/how-to-sort-version-numbers-in-ansible
+
+- name: omero web | display omero_common_basedir value
+ debug:
+ msg: "default: {{ omero_common_basedir }}"
+
+- name: omero web | is web symlink present
+ become: true
+ stat:
+ path: "{{ omero_web_omerodir }}"
+ register: omero_web_symlink_st
+
+# Symlink ⇒ Old Python 2 installation: stop OMERO before doing anything else
+- name: omero web | stop omero web
+ become: true
+ service:
+ name: omero-web
+ state: stopped
+ when: omero_web_symlink_st.stat.exists and omero_web_symlink_st.stat.islnk
+
+- name: omero web | remove symlink
+ become: true
+ file:
+ path: "{{ omero_web_omerodir }}"
+ state: absent
+ when: omero_web_symlink_st.stat.exists and omero_web_symlink_st.stat.islnk
+
+- name: omero web | create OMERODIR readonly
+ become: true
+ file:
+ path: "{{ item }}"
+ state: directory
+ mode: 0755
+ with_items:
+ - "{{ omero_web_basedir }}"
+ - "{{ omero_web_basedir }}/config"
+ - "{{ omero_web_omerodir }}"
+ - "{{ omero_web_omerodir }}/bin"
+
+# omero-web system user
+# TODO: Check is home directory location be modified for an upgrade?
+- name: omero web | create system user
+ become: true
+ user:
+ name: "{{ omero_web_system_user }}"
+ home: "{{ omero_web_omerodir }}/var"
+ createhome: false
+ state: present
+ system: true
+ uid: "{{ omero_web_system_uid | default(omit) }}"
+
+- name: omero web | create OMERODIR writeable
+ become: true
+ file:
+ path: "{{ omero_web_omerodir }}{{ item }}"
+ state: directory
+ owner: "{{ omero_web_system_user }}"
+ mode: 0755
+ with_items:
+ - /etc/grid
+ - /var
+
+- name: omero web | remove old configuration script
+ become: true
+ file:
+ path: "{{ omero_web_basedir }}/config/omero-web-config-update.sh"
+ state: absent
+
+- name: omero web | configuration 00-omero-web.omero
+ become: true
+ template:
+ dest: "{{ omero_web_basedir }}/config/00-omero-web.omero"
+ force: true
+ src: 00-omero-web-omero.j2
+ mode: 0644
+ notify:
+ - omero-web rewrite omero-web configuration
+ - omero-web restart omero-web
+
+- name: omero web | add application web configuration
+ become: true
+ template:
+ dest: "{{ omero_web_basedir }}/config/\
+ {{ omero_web_apps_config_name }}.omero"
+ force: true
+ src: omero-web-apps-omero.j2
+ mode: 0644
+ notify:
+ - omero-web rewrite omero-web configuration
+ - omero-web restart omero-web
+
+- name: omero web | setup virtualenv3
+ become: true
+ pip:
+ name: "pip>=21"
+ state: present
+ virtualenv: "{{ omero_web_virtualenv_basedir }}"
+ virtualenv_command: /usr/local/bin/ome-python3-virtualenv
+
+- name: omero web | install tzdata
+ become: true
+ pip:
+ name: tzdata
+ state: present
+ virtualenv: "{{ omero_web_virtualenv_basedir }}"
+ virtualenv_command: /usr/local/bin/ome-python3-virtualenv
+ notify:
+ - omero-web rewrite omero-web configuration
+ - omero-web restart omero-web
+
+- name: omero web | install django_redis
+ become: true
+ pip:
+ name: django_redis
+ state: present
+ virtualenv: "{{ omero_web_virtualenv_basedir }}"
+ virtualenv_command: /usr/local/bin/ome-python3-virtualenv
+ notify:
+ - omero-web rewrite omero-web configuration
+ - omero-web restart omero-web
+ when: omero_web_setup_redis_session
+
+# TODO: figure out dependencies, use omero-web version
+# Install/upgrade OMERO.web after the configuration files are updated
+# This should mean that if OMERO.web fails to start due to a configuration
+# error it will be updated before a restart
+- name: omero web | install requirements
+ become: true
+ pip:
+ name: >-
+ {{
+ omero_web_python_requirements +
+ omero_web_python_addons +
+ omero_web_apps_packages
+ }}
+ state: present
+ virtualenv: "{{ omero_web_virtualenv_basedir }}"
+ virtualenv_command: /usr/local/bin/ome-python3-virtualenv
+ notify:
+ - omero-web rewrite omero-web configuration
+ - omero-web restart omero-web
+
+# Remembering to set OMERODIR everywhere is prone to error
+- name: omero web | create omero web wrapper
+ become: true
+ template:
+ dest: "{{ omero_web_omero_command }}"
+ src: bin-omero.j2
+ mode: 0555
+ notify:
+ - omero-web rewrite omero-web configuration
+ - omero-web restart omero-web
+
+- name: omero web | selinux restorecon
+ become: true
+ command: >-
+ /usr/sbin/restorecon -R -v
+ {{ ansible_check_mode | ternary('-n', '') }}
+ {{ omero_web_basedir }}/OMERO.web
+ register: result
+ check_mode: false
+ changed_when: result.stdout | length > 0
+ when: selinux_enabled
diff --git a/omero/roles/ome.omero_web/tasks/web-nginx.yml b/omero/roles/ome.omero_web/tasks/web-nginx.yml
new file mode 100644
index 00000000..698ce050
--- /dev/null
+++ b/omero/roles/ome.omero_web/tasks/web-nginx.yml
@@ -0,0 +1,43 @@
+---
+# setup nginx
+
+# Flush handlers to ensure configuration is reloaded
+- name: omero web | flush systemd handlers
+ meta: flush_handlers
+
+- block:
+
+ - name: omero web | generate nginx config
+ become: true
+ become_user: "{{ omero_web_system_user }}"
+ command: "{{ omero_web_omero_command }} web config nginx"
+ register: _omero_web_config_nginx
+ # This should be safe to always run since nothing is written
+ # The subsequent copy task will handle changed/unchanged
+ changed_when: false
+ check_mode: false
+
+ - name: omero web | write nginx config
+ become: true
+ copy:
+ content: "{{ _omero_web_config_nginx.stdout }}"
+ dest: /etc/nginx/conf.d/omero-web.conf
+ mode: 0644
+ notify: restart nginx
+
+
+- name: omero web | nginx remove system defaults
+ become: true
+ replace:
+ backup: true
+ dest: /etc/nginx/nginx.conf
+ regexp: '80\s*default_server\s*;'
+ replace: '80; # default_server;'
+ notify: restart nginx
+
+- name: omero web | enable nginx
+ become: true
+ service:
+ enabled: true
+ name: nginx
+ state: started
diff --git a/omero/roles/ome.omero_web/tasks/web-systemd.yml b/omero/roles/ome.omero_web/tasks/web-systemd.yml
new file mode 100644
index 00000000..01e3674f
--- /dev/null
+++ b/omero/roles/ome.omero_web/tasks/web-systemd.yml
@@ -0,0 +1,25 @@
+---
+# Setup systemd files
+
+- name: omero web | systemd service
+ become: true
+ template:
+ dest: /etc/systemd/system/omero-web.service
+ force: true
+ src: systemd-system-omero-web-service.j2
+ mode: 0644
+ notify:
+ - reload systemd
+ - omero-web restart omero-web
+
+# Flush handlers to ensure systemd is reloaded
+- name: omero web | flush systemd handlers
+ meta: flush_handlers
+
+- name: omero web | enable and start web systemd
+ become: true
+ service:
+ enabled: true
+ name: omero-web.service
+ state: started
+ when: omero_web_systemd_start
diff --git a/omero/roles/ome.omero_web/templates/00-omero-web-omero.j2 b/omero/roles/ome.omero_web/templates/00-omero-web-omero.j2
new file mode 100644
index 00000000..0a66d811
--- /dev/null
+++ b/omero/roles/ome.omero_web/templates/00-omero-web-omero.j2
@@ -0,0 +1,14 @@
+# {{ ansible_managed }}
+
+{% if omero_web_always_reset_config %}
+config drop default
+{% endif %}
+
+# Additional custom options
+{% for key in (omero_web_config_set | sort) %}
+config set -- {{ key | quote }} {{
+ ((omero_web_config_set[key] | string) == omero_web_config_set[key]) |
+ ternary(omero_web_config_set[key], omero_web_config_set[key] | to_json) |
+ quote
+}}
+{% endfor %}
diff --git a/omero/roles/ome.omero_web/templates/bin-omero.j2 b/omero/roles/ome.omero_web/templates/bin-omero.j2
new file mode 100644
index 00000000..7db480de
--- /dev/null
+++ b/omero/roles/ome.omero_web/templates/bin-omero.j2
@@ -0,0 +1,16 @@
+#!{{ omero_web_virtualenv_basedir }}/bin/python
+
+import os
+from subprocess import run
+import sys
+
+if not os.getenv('OMERODIR'):
+ os.environ['OMERODIR'] = '{{ omero_web_omerodir }}'
+
+current_path = os.getenv('PATH', '')
+venv_bin = '{{ omero_web_virtualenv_basedir }}/bin'
+if not current_path.startswith(venv_bin + os.pathsep):
+ os.environ['PATH'] = '{}{}{}'.format(venv_bin, os.pathsep, current_path)
+
+p = run(['{{ omero_web_virtualenv_basedir }}/bin/omero'] + sys.argv[1:])
+sys.exit(p.returncode)
diff --git a/omero/roles/ome.omero_web/templates/django.te.j2 b/omero/roles/ome.omero_web/templates/django.te.j2
new file mode 100755
index 00000000..698aa146
--- /dev/null
+++ b/omero/roles/ome.omero_web/templates/django.te.j2
@@ -0,0 +1,15 @@
+
+module django 1.0;
+
+require {
+ type var_t;
+ type httpd_t;
+ type init_t;
+ class file { getattr ioctl open read };
+}
+
+#============= httpd_t ==============
+allow httpd_t var_t:file { getattr open read };
+
+#============= init_t ==============
+allow init_t var_t:file { ioctl open read };
diff --git a/omero/roles/ome.omero_web/templates/omero-web-apps-omero.j2 b/omero/roles/ome.omero_web/templates/omero-web-apps-omero.j2
new file mode 100644
index 00000000..d408f0d2
--- /dev/null
+++ b/omero/roles/ome.omero_web/templates/omero-web-apps-omero.j2
@@ -0,0 +1,36 @@
+# {{ ansible_managed }}
+
+# add application
+{% for item in omero_web_apps_names %}
+config append -- omero.web.apps {{ item | to_json | quote }}
+{% endfor %}
+
+# add top links
+{% for item in omero_web_apps_top_links %}
+{% if 'attrs' in item %}
+config append -- omero.web.ui.top_links {{ [item.label, item.link, item.attrs] | to_json | quote }}
+{% else %}
+config append -- omero.web.ui.top_links {{ [item.label, item.link] | to_json | quote }}
+{% endif %}
+{% endfor %}
+
+# ui metadata panes
+{% for item in omero_web_apps_ui_metadata_panes %}
+config append -- omero.web.ui.metadata_panes {{ item | to_json | quote }}
+{% endfor %}
+
+# Other list value properties (append)
+{% for key in omero_web_apps_config_append %}
+{% for value in omero_web_apps_config_append[key] %}
+config append -- {{ key | quote }} {{ value | to_json | quote }}
+{% endfor %}
+{% endfor %}
+
+# Other key value properties (set)
+{% for key in omero_web_apps_config_set %}
+config set -- {{ key | quote }} {{
+ ((omero_web_apps_config_set[key] | string) == omero_web_apps_config_set[key]) |
+ ternary(omero_web_apps_config_set[key], omero_web_apps_config_set[key] | to_json) |
+ quote
+}}
+{% endfor %}
diff --git a/omero/roles/ome.omero_web/templates/omero-web-config-update-sh.j2 b/omero/roles/ome.omero_web/templates/omero-web-config-update-sh.j2
new file mode 100644
index 00000000..b82e2e59
--- /dev/null
+++ b/omero/roles/ome.omero_web/templates/omero-web-config-update-sh.j2
@@ -0,0 +1,9 @@
+#!/bin/sh
+# {{ ansible_managed }}
+# Regenerate the omero-web configuration
+
+set -e
+
+for f in {{ omero_web_basedir }}/config/*.omero; do
+ {{ omero_web_omero_command }} load "$f"
+done
diff --git a/omero/roles/ome.omero_web/templates/systemd-system-omero-web-service.j2 b/omero/roles/ome.omero_web/templates/systemd-system-omero-web-service.j2
new file mode 100644
index 00000000..4a2656f8
--- /dev/null
+++ b/omero/roles/ome.omero_web/templates/systemd-system-omero-web-service.j2
@@ -0,0 +1,25 @@
+[Unit]
+Description=OMERO.web
+# Not mandatory, Nginx may be running on a different server
+#Requires=nginx.service
+After=network.service
+{% for value in omero_web_systemd_after %}After={{ value }}
+{% endfor %}
+{% for value in omero_web_systemd_requires %}Requires={{ value }}
+{% endfor %}
+
+[Service]
+User={{ omero_web_system_user }}
+Type=forking
+PIDFile={{ omero_web_omerodir }}/var/django.pid
+Restart=no
+RestartSec=10
+ExecStartPre={{ omero_web_config_update }}
+ExecStart={{ omero_web_omero_command }} web start
+ExecStop={{ omero_web_omero_command }} web stop
+{% if omero_web_systemd_limit_nofile %}
+LimitNOFILE={{ omero_web_systemd_limit_nofile }}
+{% endif %}
+
+[Install]
+WantedBy=multi-user.target
diff --git a/omero/roles/ome.postgresql/.github/workflows/molecule.yml b/omero/roles/ome.postgresql/.github/workflows/molecule.yml
new file mode 100644
index 00000000..1fa548c2
--- /dev/null
+++ b/omero/roles/ome.postgresql/.github/workflows/molecule.yml
@@ -0,0 +1,54 @@
+---
+name: Molecule
+# yamllint disable-line rule:truthy
+on:
+ push:
+ pull_request:
+ schedule:
+ - cron: '29 21 * * 0'
+
+jobs:
+
+ list-scenarios:
+ runs-on: ubuntu-22.04
+ outputs:
+ matrix: ${{ steps.listscenarios.outputs.scenarios }}
+ steps:
+ - uses: actions/checkout@v3
+ - id: listscenarios
+ uses: ome/action-ansible-molecule-list-scenarios@main
+
+ test:
+ name: Test
+ needs:
+ - list-scenarios
+ runs-on: ubuntu-22.04
+ strategy:
+ # Keep running so we can see if other tests pass
+ fail-fast: false
+ matrix:
+ scenario: ${{fromJson(needs.list-scenarios.outputs.matrix)}}
+ steps:
+ - uses: actions/checkout@v4
+ - uses: actions/setup-python@v4
+ with:
+ python-version: '3.9'
+ - name: Install Ansible & Molecule
+ run: pip install "ansible<8" ansible-lint flake8 \
+ "molecule<5" molecule-plugins[docker] pytest-testinfra \
+ "ansible-compat<4"
+ - name: Run molecule
+ run: molecule test -s "${{ matrix.scenario }}"
+
+ publish:
+ name: Galaxy
+ if: startsWith(github.ref, 'refs/tags')
+ needs:
+ - test
+ runs-on: ubuntu-22.04
+ steps:
+ - name: galaxy
+ uses: ansible-actions/ansible-galaxy-action@v1.2.0
+ with:
+ galaxy_api_key: ${{ secrets.GALAXY_API_KEY }}
+ galaxy_version: ${{ github.ref_name }}
diff --git a/omero/roles/ome.postgresql/.gitignore b/omero/roles/ome.postgresql/.gitignore
new file mode 100644
index 00000000..97752ea8
--- /dev/null
+++ b/omero/roles/ome.postgresql/.gitignore
@@ -0,0 +1,4 @@
+.*~
+*.pyc
+.molecule
+.vagrant
diff --git a/omero/roles/ome.postgresql/CHANGES.md b/omero/roles/ome.postgresql/CHANGES.md
new file mode 100644
index 00000000..5de3cdc0
--- /dev/null
+++ b/omero/roles/ome.postgresql/CHANGES.md
@@ -0,0 +1,35 @@
+# Changes in Version 5
+
+## Summary of breaking changes
+
+- PostgreSQL 11 is no longer supported by this role.
+- PostgreSQL 9.5 is no longer supported by this role.
+- `postgresql_install_server` is removed, the server is always configured, use `ome.postgresql_client` to install just the client.
+- `postgresql_install_extensions` is removed, extension packages are always installed.
+
+
+# Changes in Version 4
+
+## Summary of breaking changes
+
+- `postgresql_version` is now a required variable. The previous default of "9.4" is no longer supported.
+
+ See [README.md](README.md) for full documentation
+
+# Changes in Version 3
+
+## Summary of breaking changes
+
+- `postgresql.conf` is templated instead of making in-line modifications to the distribution configuration
+- `CONNECT` and `public` schema `USAGE` privileges are explicitly granted to database users
+- `postgresql_users_databases` has been replaced by two variables which are both required
+ - `postgresql_databases`: The databases to be created
+ - `postgresql_users`: The users to be created, and the databases they have access to
+
+ See [README.md](README.md) for full documentation
+
+
+# Changes in Version 2
+
+## Summary of breaking changes
+- PostgreSQL 9.3 is no longer supported
diff --git a/omero/roles/ome.postgresql/LICENSE.md b/omero/roles/ome.postgresql/LICENSE.md
new file mode 100644
index 00000000..a3181283
--- /dev/null
+++ b/omero/roles/ome.postgresql/LICENSE.md
@@ -0,0 +1,22 @@
+Copyright (c) 2019, University of Dundee
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/omero/roles/ome.postgresql/README.md b/omero/roles/ome.postgresql/README.md
new file mode 100644
index 00000000..4ec4136d
--- /dev/null
+++ b/omero/roles/ome.postgresql/README.md
@@ -0,0 +1,112 @@
+Postgresql
+==========
+
+[![Actions Status](https://github.com/ome/ansible-role-postgresql/workflows/Molecule/badge.svg)](https://github.com/ome/ansible-role-postgresql/actions)
+[![Ansible Role](https://img.shields.io/badge/ansible--galaxy-postgresql-blue.svg)](https://galaxy.ansible.com/ui/standalone/roles/ome/postgresql/)
+
+Install upstream PostgreSQL server.
+
+Optionally creates users and databases.
+If you wish to use your distribution's packages then do not use this role.
+This role revokes default `PUBLIC` privileges from database and `public` schema for all supported versions of PostgreSQL.
+This is to be inline with the breaking change made in PostgreSQL 15.
+
+Role Variables
+--------------
+
+Defaults: `defaults/main.yml`
+
+- `postgresql_version`: The PostgreSQL major version: `12`, `13`, `14`, `15`, `16`
+- `postgresql_package_version`: The PostgreSQL full version, leave this empty to use the latest minor release from `postgresql_version`, ignored on Ubuntu
+- `postgresql_dist_redhat` or `postgresql_dist_debian`: Object that define configuration attributes for PostgreSQL on each specific OS, these variables allow to change the interaction between variables defined at [ome.postgresql](https://galaxy.ansible.com/ome/postgresql) and [ome.postgresql_client](https://github.com/ome/ansible-role-postgresql-client)
+
+The following parameters will be ignored if `postgresql_install_server: False`:
+- `postgresql_databases`: List of dictionaries of databases.
+ Items should be of the form:
+ - `name`: Database name
+ - `owner`: Owner role (optional)
+ - `lc_collate`: Collation order (LC_COLLATE) to use in the database
+ - `lc_ctype`: Character classification (LC_CTYPE) to use in the database
+ - `encoding`: Encoding of the database, default `UTF-8`
+ - `template`: Template used to create the database
+- `postgresql_users`: List of dictionaries of users.
+ Items should be of the form:
+ - `user`: Database username
+ - `password`: Database user password
+ - `databases`: List of databases that user can connect to, required but can be empty `[]`
+ - `roles`: Role attribute flags, optional
+ If you want the user to have restricted access see the section below on Restricted users.
+- `postgresql_server_listen`: Listen on these interfaces, default `localhost`, use `'*'` for all
+- `postgresql_server_conf`: Dictionary of additional postgresql.conf options
+- `postgresql_server_auth_local`: Whether to allow the default postgres local authentication (default `True`)
+- `postgresql_server_auth`: List of dictionaries of authorisation parameters, if omitted the default local authentication only will be enabled. Items should be of the form:
+ - `database`: Name of the database
+ - `user`: Username
+ - `address`: Address from which connections will be made
+ - `method`: Ignore this unless you really know what you are doing
+- `postgresql_server_chown_datadir`: If `True` recursively reset the owner and group of the postgres datadir, default `False`, use this when you have an existing datadir with incorrect owner/group
+
+
+Restricted databases
+--------------------
+
+In general it is not possible to create users with restricted access (e.g. read-only users) until a schema has been populated.
+This role removes the default PUBLIC privileges from all databases, then grants:
+- `ALL` privileges to the database owner if specified (`postgresql_databases[].owner`)
+- `CONNECT` privilege to the database, and `USAGE` privilege on the `public` schema, to databases listed for each user (`postgresql_users[].databases`)
+
+If you wish to created a restricted user set the `databases` field in `postgresql_users` to `[]`, and use the [Ansible `postgresql_privs`](http://docs.ansible.com/ansible/latest/postgresql_privs_module.html) module to grant access after the database schema has been created.
+
+An example can be seen in [`playbook.yml`](playbook.yml).
+
+Limitations
+-----------
+
+The role assumes the PSQL cluster will be installed in the default data directory
+of the Linux distribution and this directory is not configurable as of the current
+version.
+
+Example Playbook
+----------------
+
+ # Simple example relying on the default Postgres PUBLIC privileges
+ # which allow access to all users
+ - hosts: localhost
+ roles:
+ - role: postgresql
+ postgresql_server_listen: "'*'"
+ postgresql_server_auth:
+ - database: publicdb
+ user: alice
+ address: 192.168.1.0/24
+ postgresql_databases:
+ - name: publicdb
+ postgresql_users:
+ - user: alice
+ password: alice123
+ databases: []
+
+
+ # Advanced example with no default access to databases
+ # This sets up minimal privileges for `bob`, you will need to configure
+ # additional privileges yourself
+ - hosts: localhost
+ roles:
+ - postgresql
+ vars:
+ - postgresql_databases:
+ - name: secretdb
+ owner: alice
+ - postgresql_users:
+ - user: alice
+ password: alice123
+ databases: [secretdb]
+ - user: bob
+ password: bob123
+ databases: []
+
+
+Author Information
+------------------
+
+ome-devel@lists.openmicroscopy.org.uk
diff --git a/omero/roles/ome.postgresql/defaults/main.yml b/omero/roles/ome.postgresql/defaults/main.yml
new file mode 100644
index 00000000..c3eb0e22
--- /dev/null
+++ b/omero/roles/ome.postgresql/defaults/main.yml
@@ -0,0 +1,64 @@
+---
+# defaults file for ome.postgresql
+
+# The PostgreSQL major version, required
+# postgresql_version
+
+# Full package version of postgres, see ome.postgresql_client
+postgresql_package_version: ''
+
+# OS system user to become when become_user: is defined
+postgresql_become_user: postgres
+
+# List of dictionaries of databases
+postgresql_databases: []
+
+# List of dictionaries of users
+postgresql_users: []
+
+# Network interfaces to listen on
+postgresql_server_listen: localhost
+
+# Dictionary of additional postgresql.conf options
+postgresql_server_conf: {}
+
+# Whether to enable the default local authentication methods
+postgresql_server_auth_local: true
+
+# List of dictionaries of client authorisation lines
+postgresql_server_auth: []
+
+# Recursively reset the owner/group of the postgres datadir?
+postgresql_server_chown_datadir: false
+
+######################################################################
+# Internal role variables, do not modify
+######################################################################
+
+# Attributes are parsed and used to set facts at tasks/redhat.yml.
+# Overriding the default values allow to configure future versions of
+# PostgreSQL, e.g. different paths according to the version, config, etc.
+postgresql_dist_redhat:
+ bindir: /usr/pgsql-{{ postgresql_version }}/bin
+ confdir: /var/lib/pgsql/{{ postgresql_version }}/data
+ conf_postgresql_src: postgresql-conf.j2
+ datadir: /var/lib/pgsql/{{ postgresql_version }}/data
+ basename: postgresql{{ postgresql_version }}
+ repoid: pgdg{{ postgresql_version }}
+ setupname: postgresql-{{ postgresql_version }}-setup
+ service: postgresql-{{ postgresql_version }}
+ version_suffix: >-
+ {{
+ (postgresql_package_version | length > 0) |
+ ternary('-' + postgresql_package_version, '')
+ }}
+
+# Attributes are parsed and used to set facts at tasks/debian.yml.
+# Debian variation, following the same principles of postgresql_dist_redhat
+postgresql_dist_debian:
+ bindir: /usr/lib/postgresql/{{ postgresql_version }}/bin
+ confdir: /etc/postgresql/{{ postgresql_version }}/main
+ conf_postgresql_src: postgresql-conf-10-ubuntu.j2
+ datadir: /var/lib/postgresql/{{ postgresql_version }}/main
+ basename: postgresql-{{ postgresql_version }}
+ service: postgresql
diff --git a/omero/roles/ome.postgresql/handlers/main.yml b/omero/roles/ome.postgresql/handlers/main.yml
new file mode 100644
index 00000000..0dd0ae3f
--- /dev/null
+++ b/omero/roles/ome.postgresql/handlers/main.yml
@@ -0,0 +1,8 @@
+---
+# Handler for postgresql
+
+- name: restart postgresql
+ become: true
+ service:
+ name: "{{ postgresql_dist_service }}"
+ state: restarted
diff --git a/omero/roles/ome.postgresql/meta/.galaxy_install_info b/omero/roles/ome.postgresql/meta/.galaxy_install_info
new file mode 100644
index 00000000..6e156878
--- /dev/null
+++ b/omero/roles/ome.postgresql/meta/.galaxy_install_info
@@ -0,0 +1,2 @@
+install_date: Wed 13 Nov 17:33:45 2024
+version: 5.4.0
diff --git a/omero/roles/ome.postgresql/meta/main.yml b/omero/roles/ome.postgresql/meta/main.yml
new file mode 100644
index 00000000..5370fb34
--- /dev/null
+++ b/omero/roles/ome.postgresql/meta/main.yml
@@ -0,0 +1,17 @@
+---
+galaxy_info:
+ author: ome-devel@lists.openmicroscopy.org.uk
+ description: Upstream PostgreSQL server
+ company: Open Microscopy Environment
+ license: BSD
+ min_ansible_version: 2.10
+ platforms:
+ - name: EL
+ versions:
+ - 9
+ - name: Ubuntu
+ versions:
+ - jammy
+ namespace: ome
+ role_name: postgresql
+ galaxy_tags: ['postgresql', 'database']
diff --git a/omero/roles/ome.postgresql/meta/requirements.yml b/omero/roles/ome.postgresql/meta/requirements.yml
new file mode 100644
index 00000000..e2a700a1
--- /dev/null
+++ b/omero/roles/ome.postgresql/meta/requirements.yml
@@ -0,0 +1,2 @@
+---
+- src: ome.postgresql_client
diff --git a/omero/roles/ome.postgresql/molecule/resources/playbook.yml b/omero/roles/ome.postgresql/molecule/resources/playbook.yml
new file mode 100644
index 00000000..208d4f8c
--- /dev/null
+++ b/omero/roles/ome.postgresql/molecule/resources/playbook.yml
@@ -0,0 +1,71 @@
+---
+- name: Converge
+ hosts: all
+ roles:
+ - role: ome.postgresql
+ postgresql_databases:
+ - name: publicdb
+ - name: secretdb
+ owner: alice
+ postgresql_users:
+ - user: alice
+ password: alice123
+ # alice is the owner of secretdb so already has access
+ databases: [publicdb]
+ - user: bob
+ password: bob123
+ databases: [publicdb, secretdb]
+ roles: "CREATEDB,NOSUPERUSER"
+ - user: charles
+ password: charles123
+ databases: []
+ - user: tester
+ password: tester123
+ databases: []
+ roles: "SUPERUSER"
+
+- hosts: all
+ tasks:
+ - name: create test tables publicdb
+ command: >
+ env PGPASSWORD=tester123 psql -h localhost -U tester publicdb
+ -c "{{ item }}"
+ register: result
+ changed_when: >
+ (not ('skipping' in result.stderr or '0 0' in result.stdout))
+ with_items:
+ - "create table if not exists regular (text text primary key);"
+ - >
+ insert into regular select ('clear text') where not exists
+ (select text from regular);
+
+ - name: create test tables secretdb
+ command: >-
+ env PGPASSWORD=alice123 psql -h localhost -U alice secretdb
+ -c "{{ item }}"
+ register: result
+ changed_when: >
+ (not ('skipping' in result.stderr or '0 0' in result.stdout))
+ with_items:
+ - "create table if not exists regular (text text primary key);"
+ - >
+ insert into regular select ('clear text') where not exists
+ (select text from regular);
+ - "create table if not exists password (text text primary key);"
+ - >
+ insert into password select ('PRIVATE!') where not exists
+ (select text from password);
+
+ - name: set privileges on secretdb tables
+ become: true
+ become_user: postgres
+ postgresql_privs:
+ database: "{{ item }}"
+ obj: regular
+ privs: SELECT
+ roles: bob
+ state: present
+ type: table
+ with_items:
+ - publicdb
+ - secretdb
diff --git a/omero/roles/ome.postgresql/molecule/resources/requirements.yml b/omero/roles/ome.postgresql/molecule/resources/requirements.yml
new file mode 100644
index 00000000..e2a700a1
--- /dev/null
+++ b/omero/roles/ome.postgresql/molecule/resources/requirements.yml
@@ -0,0 +1,2 @@
+---
+- src: ome.postgresql_client
diff --git a/omero/roles/ome.postgresql/molecule/resources/tests/test_default.py b/omero/roles/ome.postgresql/molecule/resources/tests/test_default.py
new file mode 100644
index 00000000..35d9a648
--- /dev/null
+++ b/omero/roles/ome.postgresql/molecule/resources/tests/test_default.py
@@ -0,0 +1,188 @@
+import os
+import pytest
+import testinfra.utils.ansible_runner
+import uuid
+from re import match
+from utils import get_version
+
+testinfra_hosts = testinfra.utils.ansible_runner.AnsibleRunner(
+ os.environ['MOLECULE_INVENTORY_FILE']).get_hosts('all')
+
+
+# Server
+
+@pytest.mark.parametrize("name,expected_db", [
+ ('publicdb', 'publicdb|{lang}.UTF-8|{lang}.UTF-8'),
+ ('secretdb', 'secretdb|{lang}.UTF-8|{lang}.UTF-8')
+])
+def test_databases(host, name, expected_db):
+ sql = ("SELECT datname,datcollate,datctype FROM pg_database "
+ "WHERE datname='%s'" % name)
+ with host.sudo('postgres'):
+ out = host.check_output('psql postgres -c "%s" -At' % sql)
+
+ if host.system_info.distribution == 'rocky':
+ lang = 'en_US'
+ else:
+ lang = 'C'
+ assert out == expected_db.format(lang=lang)
+
+
+def test_server_listen(host):
+ version = get_version(host)
+ if host.system_info.distribution == 'rocky':
+ configfile = '/var/lib/pgsql/{version}/data/postgresql.conf'
+ else:
+ configfile = '/etc/postgresql/{version}/main/postgresql.conf'
+ with host.sudo():
+ value = configfile.format(version=version)
+ f = host.file(value).content_string
+
+ count_listen_addresses = 0
+ for line in f.split('\n'):
+ if match(r'\s*listen_addresses', line):
+ count_listen_addresses += 1
+ listen_addresses = line
+ assert count_listen_addresses == 1
+
+ assert listen_addresses == "listen_addresses = localhost"
+
+
+def test_psql_version(host):
+ ver = get_version(host)
+ out = host.check_output('psql --version')
+ assert out.startswith('psql (PostgreSQL) {}.'.format(ver))
+
+
+# Create
+
+def createdb(host, db, should_pass, password, name):
+ try:
+ host.check_output(
+ 'env PGPASSWORD=%s createdb -h localhost -U %s "%s"' %
+ (password, name, db))
+ assert should_pass
+ except Exception:
+ assert not should_pass
+
+
+@pytest.mark.parametrize("name,password,should_pass", [
+ ('tester', 'tester123', True),
+ ('alice', 'alice123', False),
+ ('bob', 'bob123', True),
+ ('charles', 'charles123', False),
+])
+def test_create(host, name, password, should_pass):
+ rnd = str(uuid.uuid4())
+ createdb(host, rnd, should_pass, password, name)
+
+
+# Privileges
+
+def psql(host, database, sql, name):
+ password = name + '123'
+ return host.run(
+ 'env PGPASSWORD=%s psql %s -h localhost -U %s -c "%s" -At' %
+ (password, database, name, sql))
+
+
+@pytest.mark.parametrize("name,expected_roles", [
+ ('tester', 'tester|t|t|f|f|t|f|-1|********||f||'),
+ ('alice', 'alice|f|t|f|f|t|f|-1|********||f||'),
+ ('bob', 'bob|f|t|f|t|t|f|-1|********||f||'),
+])
+def test_user_roles(host, name, expected_roles):
+ sql = "SELECT * FROM pg_roles WHERE rolname='%s'" % name
+ with host.sudo('postgres'):
+ out = host.check_output('psql postgres -c "%s" -At' % sql)
+ # Everything except the UID at the end
+ assert out.startswith(expected_roles)
+
+
+# Owner and users with SELECT privileges can read
+@pytest.mark.parametrize("name,database,table,should_pass", [
+ ('tester', 'publicdb', "regular", True),
+ ('tester', 'secretdb', "regular", True),
+ ('tester', 'secretdb', "password", True),
+
+ ('alice', 'publicdb', "regular", False),
+ ('alice', 'secretdb', "regular", True),
+ ('alice', 'secretdb', "password", True),
+
+ ('bob', 'publicdb', "regular", True),
+ ('bob', 'secretdb', "regular", True),
+ ('bob', 'secretdb', "password", False),
+
+ ('charles', 'publicdb', "regular", False),
+ ('charles', 'secretdb', "regular", False),
+ ('charles', 'secretdb', "password", False),
+])
+def test_select(host, name, database, table, should_pass):
+ sql = "SELECT * FROM " + table
+ c = psql(host, database, sql, name)
+ if should_pass:
+ assert c.rc == 0
+ else:
+ assert c.rc > 0
+ assert 'permission denied' in c.stderr
+
+
+@pytest.mark.parametrize("name,database,should_pass", [
+ ('tester', 'publicdb', True),
+ ('tester', 'secretdb', True),
+
+ ('alice', 'publicdb', False),
+ ('alice', 'secretdb', True),
+
+ ('bob', 'publicdb', False),
+ ('bob', 'secretdb', False),
+
+ ('charles', 'publicdb', False),
+ ('charles', 'secretdb', False),
+])
+def test_create_table(host, name, database, should_pass):
+ rnd = 'table_' + str(uuid.uuid4()).replace('-', '')
+ sql = "CREATE TABLE %s (text text primary key);" % rnd
+ c = psql(host, database, sql, name)
+ if should_pass:
+ assert c.rc == 0
+ assert 'CREATE TABLE' in c.stdout
+ else:
+ assert c.rc > 0
+
+
+@pytest.mark.parametrize("name,database,table,should_pass", [
+ ('tester', 'publicdb', "regular", True),
+ ('tester', 'secretdb', "regular", True),
+ ('tester', 'secretdb', "password", True),
+
+ ('alice', 'publicdb', "regular", False),
+ ('alice', 'secretdb', "regular", True),
+ ('alice', 'secretdb', "password", True),
+
+ ('bob', 'publicdb', "regular", False),
+ ('bob', 'secretdb', "regular", False),
+ ('bob', 'secretdb', "password", False),
+
+ ('charles', 'publicdb', "regular", False),
+ ('charles', 'secretdb', "regular", False),
+ ('charles', 'secretdb', "password", False),
+])
+def test_modify(host, name, database, table, should_pass):
+ rnd = str(uuid.uuid4())
+
+ sql = "insert into %s values ('%s')" % (table, rnd)
+ c = psql(host, database, sql, name)
+ if should_pass:
+ assert c.rc == 0
+ else:
+ assert c.rc > 0
+ assert 'permission denied' in c.stderr
+
+ sql = "delete from %s" % table
+ c = psql(host, database, sql, name)
+ if should_pass:
+ assert c.rc == 0
+ else:
+ assert c.rc > 0
+ assert 'permission denied' in c.stderr
diff --git a/omero/roles/ome.postgresql/molecule/resources/tests/test_extra_options.py b/omero/roles/ome.postgresql/molecule/resources/tests/test_extra_options.py
new file mode 100644
index 00000000..4e6feb71
--- /dev/null
+++ b/omero/roles/ome.postgresql/molecule/resources/tests/test_extra_options.py
@@ -0,0 +1,34 @@
+import os
+import testinfra.utils.ansible_runner
+from datetime import datetime, timedelta
+from utils import get_version
+
+testinfra_hosts = testinfra.utils.ansible_runner.AnsibleRunner(
+ os.environ['MOLECULE_INVENTORY_FILE']).get_hosts('extra_options')
+
+
+def test_server_additional_config(host):
+ version = get_version(host)
+ if host.system_info.distribution == 'rocky':
+ configfile = '/var/lib/pgsql/{version}/data/postgresql.conf'
+ else:
+ configfile = '/etc/postgresql/{version}/main/postgresql.conf'
+ f = host.file(configfile.format(version=version)).content_string
+ lines = f.split('\n')
+ assert "shared_preload_libraries = 'pg_stat_statements'" in lines
+ assert "log_filename = 'postgresql-%F.log'" in lines
+
+
+def test_server_log_file_name(host):
+ # Check previous day too in case this is run at midnight
+ version = get_version(host)
+ if host.system_info.distribution == 'rocky':
+ logdir = '/var/lib/pgsql/{version}/data/pg_log'
+ else:
+ logdir = '/var/lib/postgresql/{version}/main/pg_log'
+ date1 = datetime.today()
+ date0 = date1 - timedelta(days=1)
+ logdir = logdir.format(version=version)
+ file1 = '%s/postgresql-%s.log' % (logdir, date1.strftime('%F'))
+ file0 = '%s/postgresql-%s.log' % (logdir, date0.strftime('%F'))
+ assert host.file(file1).is_file or host.file(file0).is_file
diff --git a/omero/roles/ome.postgresql/molecule/resources/tests/utils.py b/omero/roles/ome.postgresql/molecule/resources/tests/utils.py
new file mode 100644
index 00000000..d2368726
--- /dev/null
+++ b/omero/roles/ome.postgresql/molecule/resources/tests/utils.py
@@ -0,0 +1,3 @@
+def get_version(host):
+ variables = host.ansible.get_variables()
+ return variables["postgresql_version"]
diff --git a/omero/roles/ome.postgresql/molecule/rockylinux9/molecule.yml b/omero/roles/ome.postgresql/molecule/rockylinux9/molecule.yml
new file mode 100644
index 00000000..8334ce49
--- /dev/null
+++ b/omero/roles/ome.postgresql/molecule/rockylinux9/molecule.yml
@@ -0,0 +1,90 @@
+---
+dependency:
+ name: galaxy
+ options:
+ role-file: molecule/resources/requirements.yml
+driver:
+ name: docker
+lint: |
+ yamllint .
+ ansible-lint
+ flake8
+platforms:
+ - name: postgresql-12-r9
+ image: eniocarboni/docker-rockylinux-systemd:9
+ image_version: latest
+ command: /sbin/init
+ privileged: true
+ cgroupns_mode: host
+ tmpfs:
+ - /sys/fs/cgroup
+ groups:
+ - extra_options
+ - name: postgresql-13-r9
+ image: eniocarboni/docker-rockylinux-systemd:9
+ image_version: latest
+ command: /sbin/init
+ privileged: true
+ cgroupns_mode: host
+ tmpfs:
+ - /sys/fs/cgroup
+ groups:
+ - extra_options
+ - name: postgresql-14-r9
+ image: eniocarboni/docker-rockylinux-systemd:9
+ image_version: latest
+ command: /sbin/init
+ privileged: true
+ cgroupns_mode: host
+ tmpfs:
+ - /sys/fs/cgroup
+ groups:
+ - extra_options
+ - name: postgresql-15-r9
+ image: eniocarboni/docker-rockylinux-systemd:9
+ image_version: latest
+ command: /sbin/init
+ privileged: true
+ cgroupns_mode: host
+ tmpfs:
+ - /sys/fs/cgroup
+ groups:
+ - extra_options
+ - name: postgresql-16-r9
+ image: eniocarboni/docker-rockylinux-systemd:9
+ image_version: latest
+ command: /sbin/init
+ privileged: true
+ cgroupns_mode: host
+ tmpfs:
+ - /sys/fs/cgroup
+ groups:
+ - extra_options
+provisioner:
+ name: ansible
+ lint:
+ name: ansible-lint
+ inventory:
+ host_vars:
+ postgresql-12-r9:
+ postgresql_version: "12"
+ postgresql-13-r9:
+ postgresql_version: "13"
+ postgresql-14-r9:
+ postgresql_version: "14"
+ postgresql-15-r9:
+ postgresql_version: "15"
+ postgresql-16-r9:
+ postgresql_version: "16"
+ group_vars:
+ extra_options:
+ postgresql_server_conf:
+ shared_preload_libraries: "'pg_stat_statements'"
+ log_filename: "'postgresql-%F.log'"
+ playbooks:
+ converge: ../resources/playbook.yml
+scenario:
+ name: rockylinux9
+verifier:
+ name: testinfra
+ directory: ../resources/tests/
diff --git a/omero/roles/ome.postgresql/molecule/rockylinux9/prepare.yml b/omero/roles/ome.postgresql/molecule/rockylinux9/prepare.yml
new file mode 100644
index 00000000..faebe092
--- /dev/null
+++ b/omero/roles/ome.postgresql/molecule/rockylinux9/prepare.yml
@@ -0,0 +1,11 @@
+---
+
+- name: Prepare all
+ hosts: all
+ tasks:
+ - name: Upgrade ca-certificates
+ ansible.builtin.dnf:
+ update_cache: true
+ pkg:
+ - ca-certificates
+ state: latest
diff --git a/omero/roles/ome.postgresql/molecule/ubuntu2204/molecule.yml b/omero/roles/ome.postgresql/molecule/ubuntu2204/molecule.yml
new file mode 100644
index 00000000..89a9e5ed
--- /dev/null
+++ b/omero/roles/ome.postgresql/molecule/ubuntu2204/molecule.yml
@@ -0,0 +1,80 @@
+---
+dependency:
+ name: galaxy
+ options:
+ role-file: molecule/resources/requirements.yml
+driver:
+ name: docker
+lint: |
+ yamllint .
+ ansible-lint
+ flake8
+platforms:
+ - name: postgresql-12-u2204
+ image: eniocarboni/docker-ubuntu-systemd:22.04
+ command: /sbin/init
+ privileged: true
+ cgroupns_mode: host
+ tmpfs:
+ - /sys/fs/cgroup
+ - name: postgresql-13-u2204
+ image: eniocarboni/docker-ubuntu-systemd:22.04
+ command: /sbin/init
+ privileged: true
+ cgroupns_mode: host
+ tmpfs:
+ - /sys/fs/cgroup
+ - name: postgresql-14-u2204
+ image: eniocarboni/docker-ubuntu-systemd:22.04
+ command: /sbin/init
+ privileged: true
+ cgroupns_mode: host
+ tmpfs:
+ - /sys/fs/cgroup
+ - name: postgresql-15-u2204
+ image: eniocarboni/docker-ubuntu-systemd:22.04
+ command: /sbin/init
+ privileged: true
+ cgroupns_mode: host
+ tmpfs:
+ - /sys/fs/cgroup
+ - name: postgresql-16-u2204
+ image: eniocarboni/docker-ubuntu-systemd:22.04
+ command: /sbin/init
+ privileged: true
+ cgroupns_mode: host
+ tmpfs:
+ - /sys/fs/cgroup
+provisioner:
+ name: ansible
+ lint:
+ name: ansible-lint
+ inventory:
+ host_vars:
+ postgresql-12-u2204:
+ postgresql_version: "12"
+ ansible_python_interpreter: /usr/bin/python3
+ postgresql-13-u2204:
+ postgresql_version: "13"
+ ansible_python_interpreter: /usr/bin/python3
+ postgresql-14-u2204:
+ postgresql_version: "14"
+ ansible_python_interpreter: /usr/bin/python3
+ postgresql-15-u2204:
+ postgresql_version: "15"
+ ansible_python_interpreter: /usr/bin/python3
+ postgresql-16-u2204:
+ postgresql_version: "16"
+ ansible_python_interpreter: /usr/bin/python3
+ group_vars:
+ extra_options:
+ postgresql_server_conf:
+ shared_preload_libraries: "'pg_stat_statements'"
+ log_filename: "'postgresql-%F.log'"
+ playbooks:
+ converge: ../resources/playbook.yml
+scenario:
+ name: ubuntu2204
+verifier:
+ name: testinfra
+ directory: ../resources/tests/
diff --git a/omero/roles/ome.postgresql/molecule/ubuntu2204/prepare.yml b/omero/roles/ome.postgresql/molecule/ubuntu2204/prepare.yml
new file mode 100644
index 00000000..6397a7f0
--- /dev/null
+++ b/omero/roles/ome.postgresql/molecule/ubuntu2204/prepare.yml
@@ -0,0 +1,7 @@
+---
+
+- name: Prepare all
+ hosts: all
+ tasks:
+ - ansible.builtin.apt:
+ update_cache: true
diff --git a/omero/roles/ome.postgresql/tasks/databases.yml b/omero/roles/ome.postgresql/tasks/databases.yml
new file mode 100644
index 00000000..eb9a9be9
--- /dev/null
+++ b/omero/roles/ome.postgresql/tasks/databases.yml
@@ -0,0 +1,113 @@
+---
+# Manage local databases and users
+# This only works where the local system `postgres` user has admin rights
+
+- block:
+ - name: postgres | create users
+ postgresql_user:
+ encrypted: true
+ name: "{{ item.user }}"
+ password: "{{ item.password }}"
+ role_attr_flags: "{{ item.roles | default(omit) }}"
+ state: present
+ with_items:
+ - "{{ postgresql_users }}"
+
+ - name: postgres | create databases
+ postgresql_db:
+ name: "{{ item.name }}"
+ owner: "{{ item.owner | default(omit) }}"
+ state: present
+ lc_collate: "{{ item.lc_collate | default(omit) }}"
+ lc_ctype: "{{ item.lc_ctype | default(omit) }}"
+ encoding: "{{ item.encoding | default('UTF-8') }}"
+ template: "{{ item.template | default(omit) }}"
+ with_items:
+ - "{{ postgresql_databases }}"
+
+ # Setting privileges is complicated:
+ # - https://stackoverflow.com/a/39029296
+
+ # From https://www.postgresql.org/docs/9.6/static/sql-grant.html:
+ #
+ # "The key word PUBLIC indicates that the privileges are to be granted to
+ # all roles, including those that might be created later. PUBLIC can be
+ # thought of as an implicitly defined group that always includes all roles.
+ # Any particular role will have the sum of privileges granted directly to
+ # it, privileges granted to any role it is presently a member of, and
+ # privileges granted to PUBLIC."
+ #
+ # "There is no need to grant privileges to the owner of an object (usually
+ # the user that created it), as the owner has all privileges by default.
+ # (The owner could, however, choose to revoke some of their own privileges
+ # for safety.)"
+ #
+ # "PostgreSQL grants default privileges on some types of objects to PUBLIC.
+ # No privileges are granted to PUBLIC by default on tables, columns, schemas
+ # or tablespaces. For other types, the default privileges granted to PUBLIC
+ # are as follows: CONNECT and CREATE TEMP TABLE for databases; EXECUTE
+ # privilege for functions; and USAGE privilege for languages."
+
+ - name: postgres | revoke default permissions
+ postgresql_privs:
+ database: "{{ item.name }}"
+ privs: ALL
+ roles: PUBLIC
+ state: absent
+ type: database
+ with_items:
+ - "{{ postgresql_databases }}"
+ changed_when: false
+
+ # Revoke the default permissions on the public schema
+ - name: postgres | revoke default schema permissions
+ postgresql_privs:
+ database: "{{ item.name }}"
+ obj: public
+ privs: ALL
+ roles: PUBLIC
+ state: absent
+ type: schema
+ with_items:
+ - "{{ postgresql_databases }}"
+ changed_when: false
+
+ # The default public schema is owned by postgres, and since the PUBLIC
+ # privileges are revoked we must grant them back to the owner
+ - name: postgres | grant database owner public schema privileges
+ postgresql_privs:
+ database: "{{ item.name }}"
+ obj: public
+ privs: ALL
+ roles: "{{ item.owner }}"
+ state: present
+ type: schema
+ when: item.owner is defined
+ with_items:
+ - "{{ postgresql_databases }}"
+
+ - name: postgres | grant connect privileges
+ postgresql_privs:
+ database: "{{ item.1 }}"
+ privs: CONNECT
+ roles: "{{ item.0.user }}"
+ state: present
+ type: database
+ with_subelements:
+ - "{{ postgresql_users }}"
+ - databases
+
+ - name: postgres | grant usage privileges on default public schema
+ postgresql_privs:
+ database: "{{ item.1 }}"
+ objs: public
+ privs: USAGE
+ roles: "{{ item.0.user }}"
+ state: present
+ type: schema
+ with_subelements:
+ - "{{ postgresql_users }}"
+ - databases
+
+ become: true
+ become_user: "{{ postgresql_become_user }}"
diff --git a/omero/roles/ome.postgresql/tasks/debian.yml b/omero/roles/ome.postgresql/tasks/debian.yml
new file mode 100644
index 00000000..b980694d
--- /dev/null
+++ b/omero/roles/ome.postgresql/tasks/debian.yml
@@ -0,0 +1,31 @@
+---
+# tasks file for ome.postgresql ubuntu
+
+- name: postgres | install packages
+ become: true
+ ansible.builtin.apt:
+ update_cache: true
+ name: >-
+ {{ postgresql_dist_debian.basename }}
+ state: present
+
+- name: postgres | install ansible prerequisites
+ become: true
+ ansible.builtin.apt:
+ update_cache: true
+ # Needs to match the Ansible interpreter
+ name: >-
+ python{{
+ ansible_python_version is version('3.0.0', '<') | ternary('', '3')
+ }}-psycopg2
+ state: present
+
+- name: postgres | set debian dist variables
+ set_fact:
+ postgresql_dist_datadir: "{{ postgresql_dist_debian.datadir }}"
+ postgresql_dist_bindir: "{{ postgresql_dist_debian.bindir }}"
+ postgresql_dist_confdir: "{{ postgresql_dist_debian.confdir }}"
+ postgresql_dist_setup: "{{ postgresql_dist_debian.bindir }}/initdb"
+ postgresql_dist_service: "{{ postgresql_dist_debian.service }}"
+ postgresql_dist_conf_postgresql_src: >-
+ {{ postgresql_dist_debian.conf_postgresql_src }}
diff --git a/omero/roles/ome.postgresql/tasks/initialise.yml b/omero/roles/ome.postgresql/tasks/initialise.yml
new file mode 100644
index 00000000..bfff92bf
--- /dev/null
+++ b/omero/roles/ome.postgresql/tasks/initialise.yml
@@ -0,0 +1,52 @@
+---
+# tasks file for ome.postgresql
+
+- block:
+ - name: postgres | set permissions on data directory
+ file:
+ owner: postgres
+ group: postgres
+ path: "{{ postgresql_dist_datadir }}"
+ state: directory
+ mode: 0700
+ when: postgresql_server_chown_datadir
+
+ - name: >-
+ postgres | initialise PostgreSQL cluster (skip if data directory
+ already exists)
+ command: "{{ postgresql_dist_setup }}"
+ args:
+ creates: "{{ postgresql_dist_datadir }}/PG_VERSION"
+
+ environment:
+ PGSETUP_INITDB_OPTIONS: >-
+ --encoding=UTF8 --locale=en_US.UTF-8 --auth-host=md5
+
+ - name: postgres | postgresql config file
+ template:
+ dest: >-
+ {{ postgresql_dist_confdir }}/postgresql.conf
+ src: "{{ postgresql_dist_conf_postgresql_src }}"
+ mode: 0644
+ notify:
+ - restart postgresql
+
+ become_user: "{{ postgresql_become_user }}"
+
+ - name: postgres | configure client authorisation
+ template:
+ dest: "{{ postgresql_dist_confdir }}/pg_hba.conf"
+ src: pg_hba-conf.j2
+ mode: 0640
+ notify:
+ - restart postgresql
+
+ become_user: "{{ postgresql_become_user }}"
+
+ - name: postgres | start service
+ service:
+ enabled: true
+ name: "{{ postgresql_dist_service }}"
+ state: started
+
+ become: true
diff --git a/omero/roles/ome.postgresql/tasks/main.yml b/omero/roles/ome.postgresql/tasks/main.yml
new file mode 100644
index 00000000..ed0f6761
--- /dev/null
+++ b/omero/roles/ome.postgresql/tasks/main.yml
@@ -0,0 +1,29 @@
+---
+- name: postgres | Include ome.postgresql_client role
+ include_role:
+ name: ome.postgresql_client
+
+# tasks file for ome.postgresql
+- name: postgres | fail if postgresql_users_databases defined
+ fail:
+ msg: >
+ Variable 'postgresql_users_databases' has been replaced by
+ 'postgresql_databases' and 'postgresql_users'
+ when: "postgresql_users_databases | default(False)"
+
+- name: postgres | fail if postgresql_install_server true
+ fail:
+ msg: >
+ Variable 'postgresql_install_server=false' has been replaced by the
+ 'ome.postgresql_client' role
+ when: "not (postgresql_install_server | default(True))"
+
+- import_tasks: redhat.yml
+ when: ansible_os_family | lower == 'redhat'
+
+- import_tasks: debian.yml
+ when: ansible_os_family | lower == 'debian'
+
+- import_tasks: initialise.yml
+
+- import_tasks: databases.yml
diff --git a/omero/roles/ome.postgresql/tasks/redhat.yml b/omero/roles/ome.postgresql/tasks/redhat.yml
new file mode 100644
index 00000000..4f6d9733
--- /dev/null
+++ b/omero/roles/ome.postgresql/tasks/redhat.yml
@@ -0,0 +1,50 @@
+---
+# tasks file for ome.postgresql rocky
+
+- name: postgres | install server packages
+ become: true
+ ansible.builtin.dnf:
+ update_cache: true
+ name: >-
+ {{ postgresql_dist_redhat.basename }}-server{{
+ postgresql_dist_redhat.version_suffix }}
+ state: present
+
+- name: postgres | install extension packages
+ become: true
+ ansible.builtin.dnf:
+ update_cache: true
+ name: >-
+ {{ postgresql_dist_redhat.basename }}-contrib{{
+ postgresql_dist_redhat.version_suffix }}
+ state: present
+
+- name: postgres | install ansible prerequisites
+ become: true
+ ansible.builtin.dnf:
+ update_cache: true
+ # Needs to match the Ansible interpreter
+ name: >-
+ python{{
+ ansible_python_version is version('3.0.0', '<') | ternary('2', '3')
+ }}-psycopg2
+ state: present
+
+- name: get langpack for en
+ become: true
+ ansible.builtin.dnf:
+ update_cache: true
+ name: glibc-langpack-en
+ state: present
+
+- name: postgres | set redhat dist variables
+ set_fact:
+ postgresql_dist_datadir: "{{ postgresql_dist_redhat.datadir }}"
+ postgresql_dist_bindir: "{{ postgresql_dist_redhat.bindir }}"
+ postgresql_dist_confdir: "{{ postgresql_dist_redhat.confdir }}"
+ postgresql_dist_setup: >-
+ {{ postgresql_dist_redhat.bindir }}/{{ postgresql_dist_redhat.setupname }}
+ initdb
+ postgresql_dist_service: "{{ postgresql_dist_redhat.service }}"
+ postgresql_dist_conf_postgresql_src: >-
+ {{ postgresql_dist_redhat.conf_postgresql_src }}
diff --git a/omero/roles/ome.postgresql/templates/pg_hba-conf.j2 b/omero/roles/ome.postgresql/templates/pg_hba-conf.j2
new file mode 100644
index 00000000..328c05fe
--- /dev/null
+++ b/omero/roles/ome.postgresql/templates/pg_hba-conf.j2
@@ -0,0 +1,11 @@
+# PostgreSQL Client Authentication Configuration File (Ansible)
+# TYPE DATABASE USER ADDRESS METHOD
+{% if postgresql_server_auth_local %}
+local all all peer
+host all all 127.0.0.1/32 md5
+host all all ::1/128 md5
+{% endif %}
+
+{% for item in postgresql_server_auth %}
+host {{ item.database }} {{ item.user }} {{ item.address }} {{ item.method | default("md5") }}
+{% endfor %}
diff --git a/omero/roles/ome.postgresql/templates/postgresql-conf-10-ubuntu.j2 b/omero/roles/ome.postgresql/templates/postgresql-conf-10-ubuntu.j2
new file mode 100644
index 00000000..06dc4aaa
--- /dev/null
+++ b/omero/roles/ome.postgresql/templates/postgresql-conf-10-ubuntu.j2
@@ -0,0 +1,673 @@
+# -----------------------------
+# PostgreSQL configuration file
+# -----------------------------
+#
+# This file consists of lines of the form:
+#
+# name = value
+#
+# (The "=" is optional.) Whitespace may be used. Comments are introduced with
+# "#" anywhere on a line. The complete list of parameter names and allowed
+# values can be found in the PostgreSQL documentation.
+#
+# The commented-out settings shown in this file represent the default values.
+# Re-commenting a setting is NOT sufficient to revert it to the default value;
+# you need to reload the server.
+#
+# This file is read on server startup and when the server receives a SIGHUP
+# signal. If you edit the file on a running system, you have to SIGHUP the
+# server for the changes to take effect, run "pg_ctl reload", or execute
+# "SELECT pg_reload_conf()". Some parameters, which are marked below,
+# require a server shutdown and restart to take effect.
+#
+# Any parameter can also be given as a command-line option to the server, e.g.,
+# "postgres -c log_connections=on". Some parameters can be changed at run time
+# with the "SET" SQL command.
+#
+# Memory units: kB = kilobytes Time units: ms = milliseconds
+# MB = megabytes s = seconds
+# GB = gigabytes min = minutes
+# TB = terabytes h = hours
+# d = days
+
+
+#------------------------------------------------------------------------------
+# FILE LOCATIONS
+#------------------------------------------------------------------------------
+
+# The default values of these variables are driven from the -D command-line
+# option or PGDATA environment variable, represented here as ConfigDir.
+
+data_directory = '/var/lib/postgresql/{{ postgresql_version }}/main' # use data in another directory
+ # (change requires restart)
+hba_file = '/etc/postgresql/{{ postgresql_version }}/main/pg_hba.conf' # host-based authentication file
+ # (change requires restart)
+ident_file = '/etc/postgresql/{{ postgresql_version }}/main/pg_ident.conf' # ident configuration file
+ # (change requires restart)
+
+# If external_pid_file is not explicitly set, no extra PID file is written.
+external_pid_file = '/var/run/postgresql/{{ postgresql_version }}-main.pid' # write an extra PID file
+ # (change requires restart)
+
+
+#------------------------------------------------------------------------------
+# CONNECTIONS AND AUTHENTICATION
+#------------------------------------------------------------------------------
+
+# - Connection Settings -
+
+#listen_addresses = 'localhost' # what IP address(es) to listen on;
+ # comma-separated list of addresses;
+ # defaults to 'localhost'; use '*' for all
+ # (change requires restart)
+port = 5432 # (change requires restart)
+max_connections = 100 # (change requires restart)
+#superuser_reserved_connections = 3 # (change requires restart)
+unix_socket_directories = '/var/run/postgresql' # comma-separated list of directories
+ # (change requires restart)
+#unix_socket_group = '' # (change requires restart)
+#unix_socket_permissions = 0777 # begin with 0 to use octal notation
+ # (change requires restart)
+#bonjour = off # advertise server via Bonjour
+ # (change requires restart)
+#bonjour_name = '' # defaults to the computer name
+ # (change requires restart)
+
+# - Security and Authentication -
+
+#authentication_timeout = 1min # 1s-600s
+ssl = on
+#ssl_ciphers = 'HIGH:MEDIUM:+3DES:!aNULL' # allowed SSL ciphers
+#ssl_prefer_server_ciphers = on
+#ssl_ecdh_curve = 'prime256v1'
+#ssl_dh_params_file = ''
+ssl_cert_file = '/etc/ssl/certs/ssl-cert-snakeoil.pem'
+ssl_key_file = '/etc/ssl/private/ssl-cert-snakeoil.key'
+#ssl_ca_file = ''
+#ssl_crl_file = ''
+#password_encryption = md5 # md5 or scram-sha-256
+#db_user_namespace = off
+#row_security = on
+
+# GSSAPI using Kerberos
+#krb_server_keyfile = ''
+#krb_caseins_users = off
+
+# - TCP Keepalives -
+# see "man 7 tcp" for details
+
+#tcp_keepalives_idle = 0 # TCP_KEEPIDLE, in seconds;
+ # 0 selects the system default
+#tcp_keepalives_interval = 0 # TCP_KEEPINTVL, in seconds;
+ # 0 selects the system default
+#tcp_keepalives_count = 0 # TCP_KEEPCNT;
+ # 0 selects the system default
+
+
+#------------------------------------------------------------------------------
+# RESOURCE USAGE (except WAL)
+#------------------------------------------------------------------------------
+
+# - Memory -
+
+shared_buffers = 128MB # min 128kB
+ # (change requires restart)
+#huge_pages = try # on, off, or try
+ # (change requires restart)
+#temp_buffers = 8MB # min 800kB
+#max_prepared_transactions = 0 # zero disables the feature
+ # (change requires restart)
+# Caution: it is not advisable to set max_prepared_transactions nonzero unless
+# you actively intend to use prepared transactions.
+#work_mem = 4MB # min 64kB
+#maintenance_work_mem = 64MB # min 1MB
+#replacement_sort_tuples = 150000 # limits use of replacement selection sort
+#autovacuum_work_mem = -1 # min 1MB, or -1 to use maintenance_work_mem
+#max_stack_depth = 2MB # min 100kB
+dynamic_shared_memory_type = posix # the default is the first option
+ # supported by the operating system:
+ # posix
+ # sysv
+ # windows
+ # mmap
+ # use none to disable dynamic shared memory
+ # (change requires restart)
+
+# - Disk -
+
+#temp_file_limit = -1 # limits per-process temp file space
+ # in kB, or -1 for no limit
+
+# - Kernel Resource Usage -
+
+#max_files_per_process = 1000 # min 25
+ # (change requires restart)
+#shared_preload_libraries = '' # (change requires restart)
+
+# - Cost-Based Vacuum Delay -
+
+#vacuum_cost_delay = 0 # 0-100 milliseconds
+#vacuum_cost_page_hit = 1 # 0-10000 credits
+#vacuum_cost_page_miss = 10 # 0-10000 credits
+#vacuum_cost_page_dirty = 20 # 0-10000 credits
+#vacuum_cost_limit = 200 # 1-10000 credits
+
+# - Background Writer -
+
+#bgwriter_delay = 200ms # 10-10000ms between rounds
+#bgwriter_lru_maxpages = 100 # 0-1000 max buffers written/round
+#bgwriter_lru_multiplier = 2.0 # 0-10.0 multiplier on buffers scanned/round
+#bgwriter_flush_after = 512kB # measured in pages, 0 disables
+
+# - Asynchronous Behavior -
+
+#effective_io_concurrency = 1 # 1-1000; 0 disables prefetching
+#max_worker_processes = 8 # (change requires restart)
+#max_parallel_workers_per_gather = 2 # taken from max_parallel_workers
+#max_parallel_workers = 8 # maximum number of max_worker_processes that
+ # can be used in parallel queries
+#old_snapshot_threshold = -1 # 1min-60d; -1 disables; 0 is immediate
+ # (change requires restart)
+#backend_flush_after = 0 # measured in pages, 0 disables
+
+
+#------------------------------------------------------------------------------
+# WRITE AHEAD LOG
+#------------------------------------------------------------------------------
+
+# - Settings -
+
+#wal_level = replica # minimal, replica, or logical
+ # (change requires restart)
+#fsync = on # flush data to disk for crash safety
+ # (turning this off can cause
+ # unrecoverable data corruption)
+#synchronous_commit = on # synchronization level;
+ # off, local, remote_write, remote_apply, or on
+#wal_sync_method = fsync # the default is the first option
+ # supported by the operating system:
+ # open_datasync
+ # fdatasync (default on Linux)
+ # fsync
+ # fsync_writethrough
+ # open_sync
+#full_page_writes = on # recover from partial page writes
+#wal_compression = off # enable compression of full-page writes
+#wal_log_hints = off # also do full page writes of non-critical updates
+ # (change requires restart)
+#wal_buffers = -1 # min 32kB, -1 sets based on shared_buffers
+ # (change requires restart)
+#wal_writer_delay = 200ms # 1-10000 milliseconds
+#wal_writer_flush_after = 1MB # measured in pages, 0 disables
+
+#commit_delay = 0 # range 0-100000, in microseconds
+#commit_siblings = 5 # range 1-1000
+
+# - Checkpoints -
+
+#checkpoint_timeout = 5min # range 30s-1d
+#max_wal_size = 1GB
+#min_wal_size = 80MB
+#checkpoint_completion_target = 0.5 # checkpoint target duration, 0.0 - 1.0
+#checkpoint_flush_after = 256kB # measured in pages, 0 disables
+#checkpoint_warning = 30s # 0 disables
+
+# - Archiving -
+
+#archive_mode = off # enables archiving; off, on, or always
+ # (change requires restart)
+#archive_command = '' # command to use to archive a logfile segment
+ # placeholders: %p = path of file to archive
+ # %f = file name only
+ # e.g. 'test ! -f /mnt/server/archivedir/%f && cp %p /mnt/server/archivedir/%f'
+#archive_timeout = 0 # force a logfile segment switch after this
+ # number of seconds; 0 disables
+
+
+#------------------------------------------------------------------------------
+# REPLICATION
+#------------------------------------------------------------------------------
+
+# - Sending Server(s) -
+
+# Set these on the master and on any standby that will send replication data.
+
+#max_wal_senders = 10 # max number of walsender processes
+ # (change requires restart)
+#wal_keep_segments = 0 # in logfile segments, 16MB each; 0 disables
+#wal_sender_timeout = 60s # in milliseconds; 0 disables
+
+#max_replication_slots = 10 # max number of replication slots
+ # (change requires restart)
+#track_commit_timestamp = off # collect timestamp of transaction commit
+ # (change requires restart)
+
+# - Master Server -
+
+# These settings are ignored on a standby server.
+
+#synchronous_standby_names = '' # standby servers that provide sync rep
+ # method to choose sync standbys, number of sync standbys,
+ # and comma-separated list of application_name
+ # from standby(s); '*' = all
+#vacuum_defer_cleanup_age = 0 # number of xacts by which cleanup is delayed
+
+# - Standby Servers -
+
+# These settings are ignored on a master server.
+
+#hot_standby = on # "off" disallows queries during recovery
+ # (change requires restart)
+#max_standby_archive_delay = 30s # max delay before canceling queries
+ # when reading WAL from archive;
+ # -1 allows indefinite delay
+#max_standby_streaming_delay = 30s # max delay before canceling queries
+ # when reading streaming WAL;
+ # -1 allows indefinite delay
+#wal_receiver_status_interval = 10s # send replies at least this often
+ # 0 disables
+#hot_standby_feedback = off # send info from standby to prevent
+ # query conflicts
+#wal_receiver_timeout = 60s # time that receiver waits for
+ # communication from master
+ # in milliseconds; 0 disables
+#wal_retrieve_retry_interval = 5s # time to wait before retrying to
+ # retrieve WAL after a failed attempt
+
+# - Subscribers -
+
+# These settings are ignored on a publisher.
+
+#max_logical_replication_workers = 4 # taken from max_worker_processes
+ # (change requires restart)
+#max_sync_workers_per_subscription = 2 # taken from max_logical_replication_workers
+
+
+#------------------------------------------------------------------------------
+# QUERY TUNING
+#------------------------------------------------------------------------------
+
+# - Planner Method Configuration -
+
+#enable_bitmapscan = on
+#enable_hashagg = on
+#enable_hashjoin = on
+#enable_indexscan = on
+#enable_indexonlyscan = on
+#enable_material = on
+#enable_mergejoin = on
+#enable_nestloop = on
+#enable_seqscan = on
+#enable_sort = on
+#enable_tidscan = on
+
+# - Planner Cost Constants -
+
+#seq_page_cost = 1.0 # measured on an arbitrary scale
+#random_page_cost = 4.0 # same scale as above
+#cpu_tuple_cost = 0.01 # same scale as above
+#cpu_index_tuple_cost = 0.005 # same scale as above
+#cpu_operator_cost = 0.0025 # same scale as above
+#parallel_tuple_cost = 0.1 # same scale as above
+#parallel_setup_cost = 1000.0 # same scale as above
+#min_parallel_table_scan_size = 8MB
+#min_parallel_index_scan_size = 512kB
+#effective_cache_size = 4GB
+
+# - Genetic Query Optimizer -
+
+#geqo = on
+#geqo_threshold = 12
+#geqo_effort = 5 # range 1-10
+#geqo_pool_size = 0 # selects default based on effort
+#geqo_generations = 0 # selects default based on effort
+#geqo_selection_bias = 2.0 # range 1.5-2.0
+#geqo_seed = 0.0 # range 0.0-1.0
+
+# - Other Planner Options -
+
+#default_statistics_target = 100 # range 1-10000
+#constraint_exclusion = partition # on, off, or partition
+#cursor_tuple_fraction = 0.1 # range 0.0-1.0
+#from_collapse_limit = 8
+#join_collapse_limit = 8 # 1 disables collapsing of explicit
+ # JOIN clauses
+#force_parallel_mode = off
+
+
+#------------------------------------------------------------------------------
+# ERROR REPORTING AND LOGGING
+#------------------------------------------------------------------------------
+
+# - Where to Log -
+
+log_destination = 'stderr' # Valid values are combinations of
+ # stderr, csvlog, syslog, and eventlog,
+ # depending on platform. csvlog
+ # requires logging_collector to be on.
+
+# This is used when logging to stderr:
+logging_collector = on # Enable capturing of stderr and csvlog
+ # into log files. Required to be on for
+ # csvlogs.
+ # (change requires restart)
+
+# These are only used if logging_collector is on:
+log_directory = 'pg_log' # directory where log files are written,
+ # can be absolute or relative to PGDATA
+log_filename = 'postgresql-%a.log' # log file name pattern,
+ # can include strftime() escapes
+#log_file_mode = 0600 # creation mode for log files,
+ # begin with 0 to use octal notation
+log_truncate_on_rotation = on # If on, an existing log file with the
+ # same name as the new log file will be
+ # truncated rather than appended to.
+ # But such truncation only occurs on
+ # time-driven rotation, not on restarts
+ # or size-driven rotation. Default is
+ # off, meaning append to existing files
+ # in all cases.
+log_rotation_age = 1d # Automatic rotation of logfiles will
+ # happen after that time. 0 disables.
+log_rotation_size = 0 # Automatic rotation of logfiles will
+ # happen after that much log output.
+ # 0 disables.
+
+# These are relevant when logging to syslog:
+#syslog_facility = 'LOCAL0'
+#syslog_ident = 'postgres'
+#syslog_sequence_numbers = on
+#syslog_split_messages = on
+
+# This is only relevant when logging to eventlog (win32):
+# (change requires restart)
+#event_source = 'PostgreSQL'
+
+# - When to Log -
+
+#client_min_messages = notice # values in order of decreasing detail:
+ # debug5
+ # debug4
+ # debug3
+ # debug2
+ # debug1
+ # log
+ # notice
+ # warning
+ # error
+
+#log_min_messages = warning # values in order of decreasing detail:
+ # debug5
+ # debug4
+ # debug3
+ # debug2
+ # debug1
+ # info
+ # notice
+ # warning
+ # error
+ # log
+ # fatal
+ # panic
+
+#log_min_error_statement = error # values in order of decreasing detail:
+ # debug5
+ # debug4
+ # debug3
+ # debug2
+ # debug1
+ # info
+ # notice
+ # warning
+ # error
+ # log
+ # fatal
+ # panic (effectively off)
+
+#log_min_duration_statement = -1 # -1 is disabled, 0 logs all statements
+ # and their durations, > 0 logs only
+ # statements running at least this number
+ # of milliseconds
+
+
+# - What to Log -
+
+#debug_print_parse = off
+#debug_print_rewritten = off
+#debug_print_plan = off
+#debug_pretty_print = on
+#log_checkpoints = off
+#log_connections = off
+#log_disconnections = off
+#log_duration = off
+#log_error_verbosity = default # terse, default, or verbose messages
+#log_hostname = off
+log_line_prefix = '%m [%p] %q%u@%d ' # special values:
+ # %a = application name
+ # %u = user name
+ # %d = database name
+ # %r = remote host and port
+ # %h = remote host
+ # %p = process ID
+ # %t = timestamp without milliseconds
+ # %m = timestamp with milliseconds
+ # %n = timestamp with milliseconds (as a Unix epoch)
+ # %i = command tag
+ # %e = SQL state
+ # %c = session ID
+ # %l = session line number
+ # %s = session start timestamp
+ # %v = virtual transaction ID
+ # %x = transaction ID (0 if none)
+ # %q = stop here in non-session
+ # processes
+ # %% = '%'
+ # e.g. '<%u%%%d> '
+#log_lock_waits = off # log lock waits >= deadlock_timeout
+#log_statement = 'none' # none, ddl, mod, all
+#log_replication_commands = off
+#log_temp_files = -1 # log temporary files equal or larger
+ # than the specified size in kilobytes;
+ # -1 disables, 0 logs all temp files
+log_timezone = 'Etc/UTC'
+
+
+# - Process Title -
+
+cluster_name = '{{ postgresql_version }}/main' # added to process titles if nonempty
+ # (change requires restart)
+#update_process_title = on
+
+
+#------------------------------------------------------------------------------
+# RUNTIME STATISTICS
+#------------------------------------------------------------------------------
+
+# - Query/Index Statistics Collector -
+
+#track_activities = on
+#track_counts = on
+#track_io_timing = off
+#track_functions = none # none, pl, all
+#track_activity_query_size = 1024 # (change requires restart)
+{% if postgresql_version < '15' %}stats_temp_directory = '/var/run/postgresql/{{ postgresql_version }}-main.pg_stat_tmp'{% endif %}
+
+
+# - Statistics Monitoring -
+
+#log_parser_stats = off
+#log_planner_stats = off
+#log_executor_stats = off
+#log_statement_stats = off
+
+
+#------------------------------------------------------------------------------
+# AUTOVACUUM PARAMETERS
+#------------------------------------------------------------------------------
+
+#autovacuum = on # Enable autovacuum subprocess? 'on'
+ # requires track_counts to also be on.
+#log_autovacuum_min_duration = -1 # -1 disables, 0 logs all actions and
+ # their durations, > 0 logs only
+ # actions running at least this number
+ # of milliseconds.
+#autovacuum_max_workers = 3 # max number of autovacuum subprocesses
+ # (change requires restart)
+#autovacuum_naptime = 1min # time between autovacuum runs
+#autovacuum_vacuum_threshold = 50 # min number of row updates before
+ # vacuum
+#autovacuum_analyze_threshold = 50 # min number of row updates before
+ # analyze
+#autovacuum_vacuum_scale_factor = 0.2 # fraction of table size before vacuum
+#autovacuum_analyze_scale_factor = 0.1 # fraction of table size before analyze
+#autovacuum_freeze_max_age = 200000000 # maximum XID age before forced vacuum
+ # (change requires restart)
+#autovacuum_multixact_freeze_max_age = 400000000 # maximum multixact age
+ # before forced vacuum
+ # (change requires restart)
+#autovacuum_vacuum_cost_delay = 20ms # default vacuum cost delay for
+ # autovacuum, in milliseconds;
+ # -1 means use vacuum_cost_delay
+#autovacuum_vacuum_cost_limit = -1 # default vacuum cost limit for
+ # autovacuum, -1 means use
+ # vacuum_cost_limit
+
+
+#------------------------------------------------------------------------------
+# CLIENT CONNECTION DEFAULTS
+#------------------------------------------------------------------------------
+
+# - Statement Behavior -
+
+#search_path = '"$user", public' # schema names
+#default_tablespace = '' # a tablespace name, '' uses the default
+#temp_tablespaces = '' # a list of tablespace names, '' uses
+ # only default tablespace
+#check_function_bodies = on
+#default_transaction_isolation = 'read committed'
+#default_transaction_read_only = off
+#default_transaction_deferrable = off
+#session_replication_role = 'origin'
+#statement_timeout = 0 # in milliseconds, 0 is disabled
+#lock_timeout = 0 # in milliseconds, 0 is disabled
+#idle_in_transaction_session_timeout = 0 # in milliseconds, 0 is disabled
+#vacuum_freeze_min_age = 50000000
+#vacuum_freeze_table_age = 150000000
+#vacuum_multixact_freeze_min_age = 5000000
+#vacuum_multixact_freeze_table_age = 150000000
+#bytea_output = 'hex' # hex, escape
+#xmlbinary = 'base64'
+#xmloption = 'content'
+#gin_fuzzy_search_limit = 0
+#gin_pending_list_limit = 4MB
+
+# - Locale and Formatting -
+
+datestyle = 'iso, mdy'
+#intervalstyle = 'postgres'
+timezone = 'Etc/UTC'
+#timezone_abbreviations = 'Default' # Select the set of available time zone
+ # abbreviations. Currently, there are
+ # Default
+ # Australia (historical usage)
+ # India
+ # You can create your own file in
+ # share/timezonesets/.
+#extra_float_digits = 0 # min -15, max 3
+#client_encoding = sql_ascii # actually, defaults to database
+ # encoding
+
+# These settings are initialized by initdb, but they can be changed.
+lc_messages = 'C.UTF-8' # locale for system error message
+ # strings
+lc_monetary = 'C.UTF-8' # locale for monetary formatting
+lc_numeric = 'C.UTF-8' # locale for number formatting
+lc_time = 'C.UTF-8' # locale for time formatting
+
+# default configuration for text search
+default_text_search_config = 'pg_catalog.english'
+
+# - Other Defaults -
+
+#dynamic_library_path = '$libdir'
+#local_preload_libraries = ''
+#session_preload_libraries = ''
+
+
+#------------------------------------------------------------------------------
+# LOCK MANAGEMENT
+#------------------------------------------------------------------------------
+
+#deadlock_timeout = 1s
+#max_locks_per_transaction = 64 # min 10
+ # (change requires restart)
+#max_pred_locks_per_transaction = 64 # min 10
+ # (change requires restart)
+#max_pred_locks_per_relation = -2 # negative values mean
+ # (max_pred_locks_per_transaction
+ # / -max_pred_locks_per_relation) - 1
+#max_pred_locks_per_page = 2 # min 0
+
+
+#------------------------------------------------------------------------------
+# VERSION/PLATFORM COMPATIBILITY
+#------------------------------------------------------------------------------
+
+# - Previous PostgreSQL Versions -
+
+#array_nulls = on
+#backslash_quote = safe_encoding # on, off, or safe_encoding
+#default_with_oids = off
+#escape_string_warning = on
+#lo_compat_privileges = off
+#operator_precedence_warning = off
+#quote_all_identifiers = off
+#standard_conforming_strings = on
+#synchronize_seqscans = on
+
+# - Other Platforms and Clients -
+
+#transform_null_equals = off
+
+
+#------------------------------------------------------------------------------
+# ERROR HANDLING
+#------------------------------------------------------------------------------
+
+#exit_on_error = off # terminate session on any error?
+#restart_after_crash = on # reinitialize after backend crash?
+#data_sync_retry = off # retry or panic on failure to fsync
+ # data?
+ # (change requires restart)
+
+
+#------------------------------------------------------------------------------
+# CONFIG FILE INCLUDES
+#------------------------------------------------------------------------------
+
+# These options allow settings to be loaded from files other than the
+# default postgresql.conf. Note that these are directives, not variable
+# assignments, so they can usefully be given more than once.
+
+include_dir = 'conf.d' # include files ending in '.conf' from
+ # a directory, e.g., 'conf.d'
+#include_if_exists = '...' # include file only if it exists
+#include = '...' # include file
+
+
+#------------------------------------------------------------------------------
+# CUSTOMIZED OPTIONS
+#------------------------------------------------------------------------------
+
+# Add settings for extensions here
+
+
+#------------------------------------------------------------------------------
+# ANSIBLE OPTIONS
+#------------------------------------------------------------------------------
+
+listen_addresses = {{ postgresql_server_listen }}
+
+{% for option in (postgresql_server_conf | sort) %}
+{{ option }} = {{ postgresql_server_conf[option] }}
+{% endfor %}
diff --git a/omero/roles/ome.postgresql/templates/postgresql-conf.j2 b/omero/roles/ome.postgresql/templates/postgresql-conf.j2
new file mode 100644
index 00000000..0f1193d6
--- /dev/null
+++ b/omero/roles/ome.postgresql/templates/postgresql-conf.j2
@@ -0,0 +1,654 @@
+# {{ ansible_managed }}
+
+# -----------------------------
+# PostgreSQL configuration file
+# -----------------------------
+#
+# This file consists of lines of the form:
+#
+# name = value
+#
+# (The "=" is optional.) Whitespace may be used. Comments are introduced with
+# "#" anywhere on a line. The complete list of parameter names and allowed
+# values can be found in the PostgreSQL documentation.
+#
+# The commented-out settings shown in this file represent the default values.
+# Re-commenting a setting is NOT sufficient to revert it to the default value;
+# you need to reload the server.
+#
+# This file is read on server startup and when the server receives a SIGHUP
+# signal. If you edit the file on a running system, you have to SIGHUP the
+# server for the changes to take effect, or use "pg_ctl reload". Some
+# parameters, which are marked below, require a server shutdown and restart to
+# take effect.
+#
+# Any parameter can also be given as a command-line option to the server, e.g.,
+# "postgres -c log_connections=on". Some parameters can be changed at run time
+# with the "SET" SQL command.
+#
+# Memory units: kB = kilobytes Time units: ms = milliseconds
+# MB = megabytes s = seconds
+# GB = gigabytes min = minutes
+# TB = terabytes h = hours
+# d = days
+
+
+#------------------------------------------------------------------------------
+# FILE LOCATIONS
+#------------------------------------------------------------------------------
+
+# The default values of these variables are driven from the -D command-line
+# option or PGDATA environment variable, represented here as ConfigDir.
+
+#data_directory = 'ConfigDir' # use data in another directory
+ # (change requires restart)
+#hba_file = 'ConfigDir/pg_hba.conf' # host-based authentication file
+ # (change requires restart)
+#ident_file = 'ConfigDir/pg_ident.conf' # ident configuration file
+ # (change requires restart)
+
+# If external_pid_file is not explicitly set, no extra PID file is written.
+#external_pid_file = '' # write an extra PID file
+ # (change requires restart)
+
+
+#------------------------------------------------------------------------------
+# CONNECTIONS AND AUTHENTICATION
+#------------------------------------------------------------------------------
+
+# - Connection Settings -
+
+#listen_addresses = 'localhost' # what IP address(es) to listen on;
+ # comma-separated list of addresses;
+ # defaults to 'localhost'; use '*' for all
+ # (change requires restart)
+#port = 5432 # (change requires restart)
+max_connections = 100 # (change requires restart)
+#superuser_reserved_connections = 3 # (change requires restart)
+#unix_socket_directories = '/var/run/postgresql, /tmp' # comma-separated list of directories
+ # (change requires restart)
+#unix_socket_group = '' # (change requires restart)
+#unix_socket_permissions = 0777 # begin with 0 to use octal notation
+ # (change requires restart)
+#bonjour = off # advertise server via Bonjour
+ # (change requires restart)
+#bonjour_name = '' # defaults to the computer name
+ # (change requires restart)
+
+# - Security and Authentication -
+
+#authentication_timeout = 1min # 1s-600s
+#ssl = off # (change requires restart)
+#ssl_ciphers = 'HIGH:MEDIUM:+3DES:!aNULL' # allowed SSL ciphers
+ # (change requires restart)
+#ssl_prefer_server_ciphers = on # (change requires restart)
+#ssl_ecdh_curve = 'prime256v1' # (change requires restart)
+#ssl_cert_file = 'server.crt' # (change requires restart)
+#ssl_key_file = 'server.key' # (change requires restart)
+#ssl_ca_file = '' # (change requires restart)
+#ssl_crl_file = '' # (change requires restart)
+#password_encryption = on
+#db_user_namespace = off
+#row_security = on
+
+# GSSAPI using Kerberos
+#krb_server_keyfile = ''
+#krb_caseins_users = off
+
+# - TCP Keepalives -
+# see "man 7 tcp" for details
+
+#tcp_keepalives_idle = 0 # TCP_KEEPIDLE, in seconds;
+ # 0 selects the system default
+#tcp_keepalives_interval = 0 # TCP_KEEPINTVL, in seconds;
+ # 0 selects the system default
+#tcp_keepalives_count = 0 # TCP_KEEPCNT;
+ # 0 selects the system default
+
+
+#------------------------------------------------------------------------------
+# RESOURCE USAGE (except WAL)
+#------------------------------------------------------------------------------
+
+# - Memory -
+
+shared_buffers = 128MB # min 128kB
+ # (change requires restart)
+#huge_pages = try # on, off, or try
+ # (change requires restart)
+#temp_buffers = 8MB # min 800kB
+#max_prepared_transactions = 0 # zero disables the feature
+ # (change requires restart)
+# Caution: it is not advisable to set max_prepared_transactions nonzero unless
+# you actively intend to use prepared transactions.
+#work_mem = 4MB # min 64kB
+#maintenance_work_mem = 64MB # min 1MB
+#replacement_sort_tuples = 150000 # limits use of replacement selection sort
+#autovacuum_work_mem = -1 # min 1MB, or -1 to use maintenance_work_mem
+#max_stack_depth = 2MB # min 100kB
+dynamic_shared_memory_type = posix # the default is the first option
+ # supported by the operating system:
+ # posix
+ # sysv
+ # windows
+ # mmap
+ # use none to disable dynamic shared memory
+
+# - Disk -
+
+#temp_file_limit = -1 # limits per-process temp file space
+ # in kB, or -1 for no limit
+
+# - Kernel Resource Usage -
+
+#max_files_per_process = 1000 # min 25
+ # (change requires restart)
+#shared_preload_libraries = '' # (change requires restart)
+
+# - Cost-Based Vacuum Delay -
+
+#vacuum_cost_delay = 0 # 0-100 milliseconds
+#vacuum_cost_page_hit = 1 # 0-10000 credits
+#vacuum_cost_page_miss = 10 # 0-10000 credits
+#vacuum_cost_page_dirty = 20 # 0-10000 credits
+#vacuum_cost_limit = 200 # 1-10000 credits
+
+# - Background Writer -
+
+#bgwriter_delay = 200ms # 10-10000ms between rounds
+#bgwriter_lru_maxpages = 100 # 0-1000 max buffers written/round
+#bgwriter_lru_multiplier = 2.0 # 0-10.0 multiplier on buffers scanned/round
+#bgwriter_flush_after = 512kB # measured in pages, 0 disables
+
+# - Asynchronous Behavior -
+
+#effective_io_concurrency = 1 # 1-1000; 0 disables prefetching
+#max_worker_processes = 8 # (change requires restart)
+#max_parallel_workers_per_gather = 0 # taken from max_worker_processes
+#old_snapshot_threshold = -1 # 1min-60d; -1 disables; 0 is immediate
+ # (change requires restart)
+#backend_flush_after = 0 # measured in pages, 0 disables
+
+
+#------------------------------------------------------------------------------
+# WRITE AHEAD LOG
+#------------------------------------------------------------------------------
+
+# - Settings -
+
+#wal_level = minimal # minimal, replica, or logical
+ # (change requires restart)
+#fsync = on # flush data to disk for crash safety
+ # (turning this off can cause
+ # unrecoverable data corruption)
+#synchronous_commit = on # synchronization level;
+ # off, local, remote_write, remote_apply, or on
+#wal_sync_method = fsync # the default is the first option
+ # supported by the operating system:
+ # open_datasync
+ # fdatasync (default on Linux)
+ # fsync
+ # fsync_writethrough
+ # open_sync
+#full_page_writes = on # recover from partial page writes
+#wal_compression = off # enable compression of full-page writes
+#wal_log_hints = off # also do full page writes of non-critical updates
+ # (change requires restart)
+#wal_buffers = -1 # min 32kB, -1 sets based on shared_buffers
+ # (change requires restart)
+#wal_writer_delay = 200ms # 1-10000 milliseconds
+#wal_writer_flush_after = 1MB # measured in pages, 0 disables
+
+#commit_delay = 0 # range 0-100000, in microseconds
+#commit_siblings = 5 # range 1-1000
+
+# - Checkpoints -
+
+#checkpoint_timeout = 5min # range 30s-1d
+#max_wal_size = 1GB
+#min_wal_size = 80MB
+#checkpoint_completion_target = 0.5 # checkpoint target duration, 0.0 - 1.0
+#checkpoint_flush_after = 256kB # measured in pages, 0 disables
+#checkpoint_warning = 30s # 0 disables
+
+# - Archiving -
+
+#archive_mode = off # enables archiving; off, on, or always
+ # (change requires restart)
+#archive_command = '' # command to use to archive a logfile segment
+ # placeholders: %p = path of file to archive
+ # %f = file name only
+ # e.g. 'test ! -f /mnt/server/archivedir/%f && cp %p /mnt/server/archivedir/%f'
+#archive_timeout = 0 # force a logfile segment switch after this
+ # number of seconds; 0 disables
+
+
+#------------------------------------------------------------------------------
+# REPLICATION
+#------------------------------------------------------------------------------
+
+# - Sending Server(s) -
+
+# Set these on the master and on any standby that will send replication data.
+
+#max_wal_senders = 0 # max number of walsender processes
+ # (change requires restart)
+#wal_keep_segments = 0 # in logfile segments, 16MB each; 0 disables
+#wal_sender_timeout = 60s # in milliseconds; 0 disables
+
+#max_replication_slots = 0 # max number of replication slots
+ # (change requires restart)
+#track_commit_timestamp = off # collect timestamp of transaction commit
+ # (change requires restart)
+
+# - Master Server -
+
+# These settings are ignored on a standby server.
+
+#synchronous_standby_names = '' # standby servers that provide sync rep
+ # number of sync standbys and comma-separated list of application_name
+ # from standby(s); '*' = all
+#vacuum_defer_cleanup_age = 0 # number of xacts by which cleanup is delayed
+
+# - Standby Servers -
+
+# These settings are ignored on a master server.
+
+#hot_standby = off # "on" allows queries during recovery
+ # (change requires restart)
+#max_standby_archive_delay = 30s # max delay before canceling queries
+ # when reading WAL from archive;
+ # -1 allows indefinite delay
+#max_standby_streaming_delay = 30s # max delay before canceling queries
+ # when reading streaming WAL;
+ # -1 allows indefinite delay
+#wal_receiver_status_interval = 10s # send replies at least this often
+ # 0 disables
+#hot_standby_feedback = off # send info from standby to prevent
+ # query conflicts
+#wal_receiver_timeout = 60s # time that receiver waits for
+ # communication from master
+ # in milliseconds; 0 disables
+#wal_retrieve_retry_interval = 5s # time to wait before retrying to
+ # retrieve WAL after a failed attempt
+
+
+#------------------------------------------------------------------------------
+# QUERY TUNING
+#------------------------------------------------------------------------------
+
+# - Planner Method Configuration -
+
+#enable_bitmapscan = on
+#enable_hashagg = on
+#enable_hashjoin = on
+#enable_indexscan = on
+#enable_indexonlyscan = on
+#enable_material = on
+#enable_mergejoin = on
+#enable_nestloop = on
+#enable_seqscan = on
+#enable_sort = on
+#enable_tidscan = on
+
+# - Planner Cost Constants -
+
+#seq_page_cost = 1.0 # measured on an arbitrary scale
+#random_page_cost = 4.0 # same scale as above
+#cpu_tuple_cost = 0.01 # same scale as above
+#cpu_index_tuple_cost = 0.005 # same scale as above
+#cpu_operator_cost = 0.0025 # same scale as above
+#parallel_tuple_cost = 0.1 # same scale as above
+#parallel_setup_cost = 1000.0 # same scale as above
+#min_parallel_relation_size = 8MB
+#effective_cache_size = 4GB
+
+# - Genetic Query Optimizer -
+
+#geqo = on
+#geqo_threshold = 12
+#geqo_effort = 5 # range 1-10
+#geqo_pool_size = 0 # selects default based on effort
+#geqo_generations = 0 # selects default based on effort
+#geqo_selection_bias = 2.0 # range 1.5-2.0
+#geqo_seed = 0.0 # range 0.0-1.0
+
+# - Other Planner Options -
+
+#default_statistics_target = 100 # range 1-10000
+#constraint_exclusion = partition # on, off, or partition
+#cursor_tuple_fraction = 0.1 # range 0.0-1.0
+#from_collapse_limit = 8
+#join_collapse_limit = 8 # 1 disables collapsing of explicit
+ # JOIN clauses
+#force_parallel_mode = off
+
+
+#------------------------------------------------------------------------------
+# ERROR REPORTING AND LOGGING
+#------------------------------------------------------------------------------
+
+# - Where to Log -
+
+log_destination = 'stderr' # Valid values are combinations of
+ # stderr, csvlog, syslog, and eventlog,
+ # depending on platform. csvlog
+ # requires logging_collector to be on.
+
+# This is used when logging to stderr:
+logging_collector = on # Enable capturing of stderr and csvlog
+ # into log files. Required to be on for
+ # csvlogs.
+ # (change requires restart)
+
+# These are only used if logging_collector is on:
+log_directory = 'pg_log' # directory where log files are written,
+ # can be absolute or relative to PGDATA
+log_filename = 'postgresql-%a.log' # log file name pattern,
+ # can include strftime() escapes
+#log_file_mode = 0600 # creation mode for log files,
+ # begin with 0 to use octal notation
+log_truncate_on_rotation = on # If on, an existing log file with the
+ # same name as the new log file will be
+ # truncated rather than appended to.
+ # But such truncation only occurs on
+ # time-driven rotation, not on restarts
+ # or size-driven rotation. Default is
+ # off, meaning append to existing files
+ # in all cases.
+log_rotation_age = 1d # Automatic rotation of logfiles will
+ # happen after that time. 0 disables.
+log_rotation_size = 0 # Automatic rotation of logfiles will
+ # happen after that much log output.
+ # 0 disables.
+
+# These are relevant when logging to syslog:
+#syslog_facility = 'LOCAL0'
+#syslog_ident = 'postgres'
+#syslog_sequence_numbers = on
+#syslog_split_messages = on
+
+# This is only relevant when logging to eventlog (win32):
+#event_source = 'PostgreSQL'
+
+# - When to Log -
+
+#client_min_messages = notice # values in order of decreasing detail:
+ # debug5
+ # debug4
+ # debug3
+ # debug2
+ # debug1
+ # log
+ # notice
+ # warning
+ # error
+
+#log_min_messages = warning # values in order of decreasing detail:
+ # debug5
+ # debug4
+ # debug3
+ # debug2
+ # debug1
+ # info
+ # notice
+ # warning
+ # error
+ # log
+ # fatal
+ # panic
+
+#log_min_error_statement = error # values in order of decreasing detail:
+ # debug5
+ # debug4
+ # debug3
+ # debug2
+ # debug1
+ # info
+ # notice
+ # warning
+ # error
+ # log
+ # fatal
+ # panic (effectively off)
+
+#log_min_duration_statement = -1 # -1 is disabled, 0 logs all statements
+ # and their durations, > 0 logs only
+ # statements running at least this number
+ # of milliseconds
+
+
+# - What to Log -
+
+#debug_print_parse = off
+#debug_print_rewritten = off
+#debug_print_plan = off
+#debug_pretty_print = on
+#log_checkpoints = off
+#log_connections = off
+#log_disconnections = off
+#log_duration = off
+#log_error_verbosity = default # terse, default, or verbose messages
+#log_hostname = off
+log_line_prefix = '< %m > ' # special values:
+ # %a = application name
+ # %u = user name
+ # %d = database name
+ # %r = remote host and port
+ # %h = remote host
+ # %p = process ID
+ # %t = timestamp without milliseconds
+ # %m = timestamp with milliseconds
+ # %n = timestamp with milliseconds (as a Unix epoch)
+ # %i = command tag
+ # %e = SQL state
+ # %c = session ID
+ # %l = session line number
+ # %s = session start timestamp
+ # %v = virtual transaction ID
+ # %x = transaction ID (0 if none)
+ # %q = stop here in non-session
+ # processes
+ # %% = '%'
+ # e.g. '<%u%%%d> '
+#log_lock_waits = off # log lock waits >= deadlock_timeout
+#log_statement = 'none' # none, ddl, mod, all
+#log_replication_commands = off
+#log_temp_files = -1 # log temporary files equal or larger
+ # than the specified size in kilobytes;
+ # -1 disables, 0 logs all temp files
+log_timezone = 'UTC'
+
+
+# - Process Title -
+
+#cluster_name = '' # added to process titles if nonempty
+ # (change requires restart)
+#update_process_title = on
+
+
+#------------------------------------------------------------------------------
+# RUNTIME STATISTICS
+#------------------------------------------------------------------------------
+
+# - Query/Index Statistics Collector -
+
+#track_activities = on
+#track_counts = on
+#track_io_timing = off
+#track_functions = none # none, pl, all
+#track_activity_query_size = 1024 # (change requires restart)
+#stats_temp_directory = 'pg_stat_tmp'
+
+
+# - Statistics Monitoring -
+
+#log_parser_stats = off
+#log_planner_stats = off
+#log_executor_stats = off
+#log_statement_stats = off
+
+
+#------------------------------------------------------------------------------
+# AUTOVACUUM PARAMETERS
+#------------------------------------------------------------------------------
+
+#autovacuum = on # Enable autovacuum subprocess? 'on'
+ # requires track_counts to also be on.
+#log_autovacuum_min_duration = -1 # -1 disables, 0 logs all actions and
+ # their durations, > 0 logs only
+ # actions running at least this number
+ # of milliseconds.
+#autovacuum_max_workers = 3 # max number of autovacuum subprocesses
+ # (change requires restart)
+#autovacuum_naptime = 1min # time between autovacuum runs
+#autovacuum_vacuum_threshold = 50 # min number of row updates before
+ # vacuum
+#autovacuum_analyze_threshold = 50 # min number of row updates before
+ # analyze
+#autovacuum_vacuum_scale_factor = 0.2 # fraction of table size before vacuum
+#autovacuum_analyze_scale_factor = 0.1 # fraction of table size before analyze
+#autovacuum_freeze_max_age = 200000000 # maximum XID age before forced vacuum
+ # (change requires restart)
+#autovacuum_multixact_freeze_max_age = 400000000 # maximum multixact age
+ # before forced vacuum
+ # (change requires restart)
+#autovacuum_vacuum_cost_delay = 20ms # default vacuum cost delay for
+ # autovacuum, in milliseconds;
+ # -1 means use vacuum_cost_delay
+#autovacuum_vacuum_cost_limit = -1 # default vacuum cost limit for
+ # autovacuum, -1 means use
+ # vacuum_cost_limit
+
+
+#------------------------------------------------------------------------------
+# CLIENT CONNECTION DEFAULTS
+#------------------------------------------------------------------------------
+
+# - Statement Behavior -
+
+#search_path = '"$user", public' # schema names
+#default_tablespace = '' # a tablespace name, '' uses the default
+#temp_tablespaces = '' # a list of tablespace names, '' uses
+ # only default tablespace
+#check_function_bodies = on
+#default_transaction_isolation = 'read committed'
+#default_transaction_read_only = off
+#default_transaction_deferrable = off
+#session_replication_role = 'origin'
+#statement_timeout = 0 # in milliseconds, 0 is disabled
+#lock_timeout = 0 # in milliseconds, 0 is disabled
+#idle_in_transaction_session_timeout = 0 # in milliseconds, 0 is disabled
+#vacuum_freeze_min_age = 50000000
+#vacuum_freeze_table_age = 150000000
+#vacuum_multixact_freeze_min_age = 5000000
+#vacuum_multixact_freeze_table_age = 150000000
+#bytea_output = 'hex' # hex, escape
+#xmlbinary = 'base64'
+#xmloption = 'content'
+#gin_fuzzy_search_limit = 0
+#gin_pending_list_limit = 4MB
+
+# - Locale and Formatting -
+
+datestyle = 'iso, mdy'
+#intervalstyle = 'postgres'
+timezone = 'UTC'
+#timezone_abbreviations = 'Default' # Select the set of available time zone
+ # abbreviations. Currently, there are
+ # Default
+ # Australia (historical usage)
+ # India
+ # You can create your own file in
+ # share/timezonesets/.
+#extra_float_digits = 0 # min -15, max 3
+#client_encoding = sql_ascii # actually, defaults to database
+ # encoding
+
+# These settings are initialized by initdb, but they can be changed.
+lc_messages = 'en_US.UTF-8' # locale for system error message
+ # strings
+lc_monetary = 'en_US.UTF-8' # locale for monetary formatting
+lc_numeric = 'en_US.UTF-8' # locale for number formatting
+lc_time = 'en_US.UTF-8' # locale for time formatting
+
+# default configuration for text search
+default_text_search_config = 'pg_catalog.english'
+
+# - Other Defaults -
+
+#dynamic_library_path = '$libdir'
+#local_preload_libraries = ''
+#session_preload_libraries = ''
+
+
+#------------------------------------------------------------------------------
+# LOCK MANAGEMENT
+#------------------------------------------------------------------------------
+
+#deadlock_timeout = 1s
+#max_locks_per_transaction = 64 # min 10
+ # (change requires restart)
+#max_pred_locks_per_transaction = 64 # min 10
+ # (change requires restart)
+
+
+#------------------------------------------------------------------------------
+# VERSION/PLATFORM COMPATIBILITY
+#------------------------------------------------------------------------------
+
+# - Previous PostgreSQL Versions -
+
+#array_nulls = on
+#backslash_quote = safe_encoding # on, off, or safe_encoding
+#default_with_oids = off
+#escape_string_warning = on
+#lo_compat_privileges = off
+#operator_precedence_warning = off
+#quote_all_identifiers = off
+#sql_inheritance = on
+#standard_conforming_strings = on
+#synchronize_seqscans = on
+
+# - Other Platforms and Clients -
+
+#transform_null_equals = off
+
+
+#------------------------------------------------------------------------------
+# ERROR HANDLING
+#------------------------------------------------------------------------------
+
+#exit_on_error = off # terminate session on any error?
+#restart_after_crash = on # reinitialize after backend crash?
+
+
+#------------------------------------------------------------------------------
+# CONFIG FILE INCLUDES
+#------------------------------------------------------------------------------
+
+# These options allow settings to be loaded from files other than the
+# default postgresql.conf.
+
+#include_dir = 'conf.d' # include files ending in '.conf' from
+ # directory 'conf.d'
+#include_if_exists = 'exists.conf' # include file only if it exists
+#include = 'special.conf' # include file
+
+
+#------------------------------------------------------------------------------
+# CUSTOMIZED OPTIONS
+#------------------------------------------------------------------------------
+
+# Add settings for extensions here
+
+
+#------------------------------------------------------------------------------
+# ANSIBLE OPTIONS
+#------------------------------------------------------------------------------
+
+listen_addresses = {{ postgresql_server_listen }}
+
+{% for option in (postgresql_server_conf | sort) %}
+{{ option }} = {{ postgresql_server_conf[option] }}
+{% endfor %}
diff --git a/omero/roles/ome.postgresql_backup/.github/workflows/molecule.yml b/omero/roles/ome.postgresql_backup/.github/workflows/molecule.yml
new file mode 100644
index 00000000..ad1f62a4
--- /dev/null
+++ b/omero/roles/ome.postgresql_backup/.github/workflows/molecule.yml
@@ -0,0 +1,55 @@
+---
+name: Molecule
+# yamllint disable-line rule:truthy
+on:
+ push:
+ pull_request:
+ schedule:
+ - cron: '29 21 * * 0'
+
+jobs:
+
+ list-scenarios:
+ runs-on: ubuntu-22.04
+ outputs:
+ matrix: ${{ steps.listscenarios.outputs.scenarios }}
+ steps:
+ - uses: actions/checkout@v4
+ - id: listscenarios
+ uses: ome/action-ansible-molecule-list-scenarios@main
+
+ test:
+ name: Test
+ needs:
+ - list-scenarios
+ runs-on: ubuntu-22.04
+ strategy:
+ # Keep running so we can see if other tests pass
+ fail-fast: false
+ matrix:
+ scenario: ${{fromJson(needs.list-scenarios.outputs.matrix)}}
+ steps:
+ - uses: actions/checkout@v4
+ - uses: actions/setup-python@v4
+ with:
+ python-version: '3.9'
+ - name: Install Ansible & Molecule
+ run: |
+ pip install "ansible<8" "ansible-lint<6.13" flake8
+ pip install "molecule<5" "ansible-compat<4"
+ pip install molecule-plugins[docker] pytest-testinfra
+ - name: Run molecule
+ run: molecule test -s "${{ matrix.scenario }}"
+
+ publish:
+ name: Galaxy
+ if: startsWith(github.ref, 'refs/tags')
+ needs:
+ - test
+ runs-on: ubuntu-22.04
+ steps:
+ - name: galaxy
+ uses: robertdebock/galaxy-action@1.2.1
+ with:
+ galaxy_api_key: ${{ secrets.GALAXY_API_KEY }}
+ git_branch: ${{ github.ref_name }}
diff --git a/omero/roles/ome.postgresql_backup/LICENSE.md b/omero/roles/ome.postgresql_backup/LICENSE.md
new file mode 100644
index 00000000..a3181283
--- /dev/null
+++ b/omero/roles/ome.postgresql_backup/LICENSE.md
@@ -0,0 +1,22 @@
+Copyright (c) 2019, University of Dundee
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/omero/roles/ome.postgresql_backup/README.md b/omero/roles/ome.postgresql_backup/README.md
new file mode 100644
index 00000000..2fa217fe
--- /dev/null
+++ b/omero/roles/ome.postgresql_backup/README.md
@@ -0,0 +1,51 @@
+PostgreSQL Backup
+=================
+
+[![Actions Status](https://github.com/ome/ansible-role-postgresql-backup/workflows/Molecule/badge.svg)](https://github.com/ome/ansible-role-postgresql-backup/actions)
+[![Ansible Role](https://img.shields.io/badge/ansible--galaxy-postgresql_backup-blue.svg)](https://galaxy.ansible.com/ui/standalone/roles/ome/postgresql_backup/)
+
+Setup a cron job for regular full PostgreSQL database dumps.
+
+Assumes the local `postgres` has password-less access to all databases (this is the default when installing PostgreSQL server).
+
+
+Dependencies
+------------
+
+This requires a cron daemon to already be running.
+This should be the default on most systems.
+
+
+Role Variables
+--------------
+
+Required:
+- `postgresql_backup_dir`: Save backups in this directory
+
+Optional:
+- `postgresql_backup_filename_format`: A filename containing unix `date` format sequences, default `{{ ansible_hostname }}-%Y%m%d-%H%M%S.pgdump` (or `{{ ansible_hostname }}-%Y%m%d-%H%M%S.pgdump.gz` if `postgresql_backup_compress: true`).
+ This can be used to automatically overwrite backups on a rolling basis.
+- `postgresql_backup_frequency`: This must match one of the standard `/etc/cron.*` directories, typically either `daily` (default), `hourly`, `weekly` or `monthly`.
+- `postgresql_backup_minimum_expected_size`: The minimum size in bytes of the backup file.
+ The cron job will return an error if the file is smaller than this.
+- `postgresql_backup_compress`: If `true` compress the output using gzip, default `false`.
+
+
+Example playbook
+----------------
+
+ # This will name the backup file /nfs/backups/HOSTNAME-Mon.pgdump
+ # where Mon will be replaced by the abbreviated day of the week, resulting
+ # in daily backups on a rolling weekly cycle
+ - hosts: postgresql-servers
+ roles:
+ - role: ome.postgresql_backup
+ postgresql_backup_dir: /nfs/backups
+ postgresql_backup_filename_format: "{{ ansible_hostname }}-%a.pgdump"
+ postgresql_backup_minimum_expected_size: 100000
+
+
+Author Information
+------------------
+
+ome-devel@lists.openmicroscopy.org.uk
diff --git a/omero/roles/ome.postgresql_backup/defaults/main.yml b/omero/roles/ome.postgresql_backup/defaults/main.yml
new file mode 100644
index 00000000..076ef161
--- /dev/null
+++ b/omero/roles/ome.postgresql_backup/defaults/main.yml
@@ -0,0 +1,11 @@
+---
+# defaults file for postgresql-backup
+
+# postgresql_backup_dir:
+postgresql_backup_filename_format: >-
+ {{ ansible_hostname }}-%Y%m%d-%H%M%S.pgdump{{
+ postgresql_backup_compress | ternary('.gz', '')
+ }}
+postgresql_backup_frequency: daily
+postgresql_backup_minimum_expected_size: 1
+postgresql_backup_compress: false
diff --git a/omero/roles/ome.postgresql_backup/meta/.galaxy_install_info b/omero/roles/ome.postgresql_backup/meta/.galaxy_install_info
new file mode 100644
index 00000000..8b844111
--- /dev/null
+++ b/omero/roles/ome.postgresql_backup/meta/.galaxy_install_info
@@ -0,0 +1,2 @@
+install_date: Wed 13 Nov 17:34:02 2024
+version: 0.3.0
diff --git a/omero/roles/ome.postgresql_backup/meta/main.yml b/omero/roles/ome.postgresql_backup/meta/main.yml
new file mode 100644
index 00000000..14ec9ed2
--- /dev/null
+++ b/omero/roles/ome.postgresql_backup/meta/main.yml
@@ -0,0 +1,14 @@
+---
+galaxy_info:
+ role_name: postgresql_backup
+ author: ome-devel@lists.openmicroscopy.org.uk
+ description: Setup a cron job for regular full PostgreSQL database dumps
+ company: Open Microscopy Environment
+ license: BSD
+ min_ansible_version: 2.3
+ platforms:
+ - name: EL
+ versions:
+ - 9
+ namespace: ome
+ galaxy_tags: []
diff --git a/omero/roles/ome.postgresql_backup/molecule/default/molecule.yml b/omero/roles/ome.postgresql_backup/molecule/default/molecule.yml
new file mode 100644
index 00000000..bfecaa1c
--- /dev/null
+++ b/omero/roles/ome.postgresql_backup/molecule/default/molecule.yml
@@ -0,0 +1,36 @@
+---
+dependency:
+ name: galaxy
+driver:
+ name: docker
+lint: |
+ yamllint .
+ ansible-lint
+ flake8
+platforms:
+ - name: postgresql-backup
+ image: eniocarboni/docker-rockylinux-systemd:9
+ command: /sbin/init
+ privileged: true
+ cgroupns_mode: host
+ tmpfs:
+ - /sys/fs/cgroup
+ - name: postgresql-backupgz
+ image: eniocarboni/docker-rockylinux-systemd:9
+ command: /sbin/init
+ privileged: true
+ cgroupns_mode: host
+ tmpfs:
+ - /sys/fs/cgroup
+provisioner:
+ name: ansible
+ lint:
+ name: ansible-lint
+ inventory:
+ host_vars:
+ postgresql-backupgz:
+ postgresql_backup_compress: true
+scenario:
+ name: default
+verifier:
+ name: testinfra
diff --git a/omero/roles/ome.postgresql_backup/molecule/default/playbook.yml b/omero/roles/ome.postgresql_backup/molecule/default/playbook.yml
new file mode 100644
index 00000000..85e62086
--- /dev/null
+++ b/omero/roles/ome.postgresql_backup/molecule/default/playbook.yml
@@ -0,0 +1,24 @@
+---
+- name: Converge
+ hosts: all
+
+ # If testing in Docker cron won't be installed
+ pre_tasks:
+ - name: Install cron
+ become: true
+ ansible.builtin.dnf:
+ update_cache: true
+ name: cronie
+ state: present
+
+ roles:
+
+ - role: ome.postgresql
+ postgresql_version: "13"
+
+ - role: ome.postgresql_backup
+ postgresql_backup_dir: /backup/postgresql
+ postgresql_backup_filename_format: >-
+ {{ ansible_hostname }}-%Y%m%d.pgdump{{
+ postgresql_backup_compress | ternary('.gz', '')
+ }}
diff --git a/omero/roles/ome.postgresql_backup/molecule/default/requirements.yml b/omero/roles/ome.postgresql_backup/molecule/default/requirements.yml
new file mode 100644
index 00000000..2f29355e
--- /dev/null
+++ b/omero/roles/ome.postgresql_backup/molecule/default/requirements.yml
@@ -0,0 +1,3 @@
+---
+
+- src: ome.postgresql
diff --git a/omero/roles/ome.postgresql_backup/molecule/default/tests/test_compress.py b/omero/roles/ome.postgresql_backup/molecule/default/tests/test_compress.py
new file mode 100644
index 00000000..15f10330
--- /dev/null
+++ b/omero/roles/ome.postgresql_backup/molecule/default/tests/test_compress.py
@@ -0,0 +1,28 @@
+import os
+from datetime import datetime
+
+import testinfra.utils.ansible_runner
+
+testinfra_hosts = testinfra.utils.ansible_runner.AnsibleRunner(
+ os.environ['MOLECULE_INVENTORY_FILE']).get_hosts('postgresql-backupgz')
+
+
+def test_run_backup_script(host):
+ # Note there's a small chance this test will incorrectly fail if it's run
+ # run at midnight
+ d = datetime.now()
+ expected = ('/backup/postgresql/postgresql-backupgz-%s.pgdump.gz' %
+ d.strftime('%Y%m%d'))
+
+ with host.sudo():
+ out = host.run('/etc/cron.daily/postgresql-backup')
+ assert out.rc == 0
+ assert out.stdout == ''
+
+ f = host.file(expected)
+ assert f.is_file
+
+ with host.sudo():
+ content = host.check_output('gunzip -c %s', expected)
+ assert content.startswith(
+ '--\n-- PostgreSQL database cluster dump\n--\n')
diff --git a/omero/roles/ome.postgresql_backup/molecule/default/tests/test_default.py b/omero/roles/ome.postgresql_backup/molecule/default/tests/test_default.py
new file mode 100644
index 00000000..daa67418
--- /dev/null
+++ b/omero/roles/ome.postgresql_backup/molecule/default/tests/test_default.py
@@ -0,0 +1,27 @@
+import os
+from datetime import datetime
+
+import testinfra.utils.ansible_runner
+
+testinfra_hosts = testinfra.utils.ansible_runner.AnsibleRunner(
+ os.environ['MOLECULE_INVENTORY_FILE']).get_hosts(
+ 'all,!postgresql-backupgz')
+
+
+def test_run_backup_script(host):
+ # Note there's a small chance this test will incorrectly fail if it's run
+ # run at midnight
+ d = datetime.now()
+ expected = ('/backup/postgresql/postgresql-backup-%s.pgdump' %
+ d.strftime('%Y%m%d'))
+
+ with host.sudo():
+ out = host.run('/etc/cron.daily/postgresql-backup')
+ assert out.rc == 0
+ assert out.stdout == ''
+
+ f = host.file(expected)
+ assert f.is_file
+ assert f.size > 2000
+ assert f.content_string.startswith(
+ '--\n-- PostgreSQL database cluster dump\n--\n')
diff --git a/omero/roles/ome.postgresql_backup/tasks/main.yml b/omero/roles/ome.postgresql_backup/tasks/main.yml
new file mode 100644
index 00000000..da3e0f76
--- /dev/null
+++ b/omero/roles/ome.postgresql_backup/tasks/main.yml
@@ -0,0 +1,17 @@
+---
+# tasks file for postgresql-backup
+
+- name: postgresql backup | create backup directory
+ become: true
+ file:
+ path: "{{ postgresql_backup_dir }}"
+ state: directory
+ owner: postgres
+ mode: 0700
+
+- name: postgresql backup | create cron job
+ become: true
+ template:
+ src: etc-crond-postgresql-backup.j2
+ dest: /etc/cron.{{ postgresql_backup_frequency }}/postgresql-backup
+ mode: 0755
diff --git a/omero/roles/ome.postgresql_backup/templates/etc-crond-postgresql-backup.j2 b/omero/roles/ome.postgresql_backup/templates/etc-crond-postgresql-backup.j2
new file mode 100644
index 00000000..00fa5024
--- /dev/null
+++ b/omero/roles/ome.postgresql_backup/templates/etc-crond-postgresql-backup.j2
@@ -0,0 +1,17 @@
+#!/usr/bin/env bash
+# Dump all PostgreSQL databases
+
+OUTPUT="{{ postgresql_backup_dir | quote }}/$(date +{{ postgresql_backup_filename_format | quote }})"
+{% if postgresql_backup_compress %}
+su postgres -c "/usr/bin/pg_dumpall | gzip -c > \"$OUTPUT.tmp\""
+{% else %}
+su postgres -c "/usr/bin/pg_dumpall -f \"$OUTPUT.tmp\""
+{% endif %}
+
+size=$(stat -c%s "$OUTPUT.tmp")
+if [ $size -lt {{ postgresql_backup_minimum_expected_size }} ]; then
+ echo "ERROR: Database backup '$OUTPUT.tmp' is less than {{ postgresql_backup_minimum_expected_size }} bytes"
+ exit 2
+fi
+
+mv "$OUTPUT.tmp" "$OUTPUT"
diff --git a/omero/roles/ome.postgresql_client/.github/workflows/molecule.yml b/omero/roles/ome.postgresql_client/.github/workflows/molecule.yml
new file mode 100644
index 00000000..34fa2d47
--- /dev/null
+++ b/omero/roles/ome.postgresql_client/.github/workflows/molecule.yml
@@ -0,0 +1,62 @@
+---
+name: Molecule
+# yamllint disable-line rule:truthy
+on:
+ push:
+ pull_request:
+ schedule:
+ - cron: '29 21 * * 0'
+
+jobs:
+
+ list-scenarios:
+ runs-on: ubuntu-22.04
+ outputs:
+ matrix: ${{ steps.listscenarios.outputs.scenarios }}
+ steps:
+ - uses: actions/checkout@v4
+ - id: listscenarios
+ uses: ome/action-ansible-molecule-list-scenarios@main
+
+ test:
+ name: Test
+ needs:
+ - list-scenarios
+ runs-on: ubuntu-22.04
+ strategy:
+ # Keep running so we can see if other tests pass
+ fail-fast: false
+ matrix:
+ scenario: ${{fromJson(needs.list-scenarios.outputs.matrix)}}
+ steps:
+ - uses: actions/checkout@v4
+ - uses: actions/setup-python@v5
+ with:
+ python-version: '3.9'
+ - name: Install Ansible & Molecule
+ run: |
+ pip install "ansible<8" "ansible-lint<6.13" flake8
+ pip install "molecule<5" "ansible-compat<4"
+ pip install molecule-plugins[docker] pytest-testinfra
+ - name: Run molecule
+ run: molecule test -s "${{ matrix.scenario }}"
+
+ publish:
+ name: Galaxy
+ if: startsWith(github.ref, 'refs/tags')
+ needs:
+ - test
+ runs-on: ubuntu-22.04
+ steps:
+ - uses: actions/checkout@v4
+ - name: Read the role name
+ id: role-name
+ run: |
+ name=$(grep 'role_name' meta/main.yml | sed -r 's/^[^:]*:(.*)$/\1/' | tr -d '[:space:]')
+ echo "rolename=$name" >> "$GITHUB_OUTPUT"
+ - name: Publish to Galaxy
+ uses: ome/action-ansible-galaxy-publish@main
+ with:
+ galaxy-api-key: ${{ secrets.GALAXY_API_KEY }}
+ galaxy-version: ${{ github.ref_name }}
+ role-name: ${{ steps.role-name.outputs.rolename }}
diff --git a/omero/roles/ome.postgresql_client/.gitignore b/omero/roles/ome.postgresql_client/.gitignore
new file mode 100644
index 00000000..97752ea8
--- /dev/null
+++ b/omero/roles/ome.postgresql_client/.gitignore
@@ -0,0 +1,4 @@
+.*~
+*.pyc
+.molecule
+.vagrant
diff --git a/omero/roles/ome.postgresql_client/LICENSE.md b/omero/roles/ome.postgresql_client/LICENSE.md
new file mode 100644
index 00000000..a3181283
--- /dev/null
+++ b/omero/roles/ome.postgresql_client/LICENSE.md
@@ -0,0 +1,22 @@
+Copyright (c) 2019, University of Dundee
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/omero/roles/ome.postgresql_client/README.md b/omero/roles/ome.postgresql_client/README.md
new file mode 100644
index 00000000..ad579eef
--- /dev/null
+++ b/omero/roles/ome.postgresql_client/README.md
@@ -0,0 +1,37 @@
+Postgresql Client
+=================
+
+[![Actions Status](https://github.com/ome/ansible-role-postgresql-client/workflows/Molecule/badge.svg)](https://github.com/ome/ansible-role-postgresql-client/actions)
+[![Ansible Role](https://img.shields.io/badge/ansible--galaxy-postgresql_client-blue.svg)](https://galaxy.ansible.com/ui/standalone/roles/ome/postgresql_client/)
+
+Install PostgreSQL clients from the upstream distribution.
+
+If you wish to use your distributions packages do not use this role.
+
+
+Role Variables
+--------------
+
+Required:
+
+- `postgresql_version`: The PostgreSQL major version, e.g. `11`, `12`, `13`, `14`, `15`, `16`
+
+Optional:
+- `postgresql_package_version`: The PostgreSQL full version, ignored on Ubuntu, e.g. `12.11`
+
+
+Example Playbook
+----------------
+
+ # Simple example relying on the default Postgres PUBLIC privileges
+ # which allow access to all users
+ - hosts: localhost
+ roles:
+ - role: ome.postgresql_client
+ postgresql_version: "12"
+
+
+Author Information
+------------------
+
+ome-devel@lists.openmicroscopy.org.uk
diff --git a/omero/roles/ome.postgresql_client/defaults/main.yml b/omero/roles/ome.postgresql_client/defaults/main.yml
new file mode 100644
index 00000000..5fc77a02
--- /dev/null
+++ b/omero/roles/ome.postgresql_client/defaults/main.yml
@@ -0,0 +1,8 @@
+---
+# defaults file for ome.postgresql-client
+
+# The PostgreSQL major version, required
+# postgresql_version
+
+# Full package version of postgres
+postgresql_package_version: ""
diff --git a/omero/roles/ome.postgresql_client/meta/.galaxy_install_info b/omero/roles/ome.postgresql_client/meta/.galaxy_install_info
new file mode 100644
index 00000000..2cd74862
--- /dev/null
+++ b/omero/roles/ome.postgresql_client/meta/.galaxy_install_info
@@ -0,0 +1,2 @@
+install_date: Wed 13 Nov 17:33:46 2024
+version: 0.4.3
diff --git a/omero/roles/ome.postgresql_client/meta/main.yml b/omero/roles/ome.postgresql_client/meta/main.yml
new file mode 100644
index 00000000..c757aee7
--- /dev/null
+++ b/omero/roles/ome.postgresql_client/meta/main.yml
@@ -0,0 +1,17 @@
+---
+galaxy_info:
+ author: ome-devel@lists.openmicroscopy.org.uk
+ description: Upstream PostgreSQL client
+ company: Open Microscopy Environment
+ license: BSD
+ min_ansible_version: '2.10'
+ platforms:
+ - name: EL
+ versions:
+ - 9
+ - name: Ubuntu
+ versions:
+ - jammy
+ namespace: ome
+ role_name: postgresql_client
+ galaxy_tags: ['postgresql', 'database']
diff --git a/omero/roles/ome.postgresql_client/molecule/resources/playbook.yml b/omero/roles/ome.postgresql_client/molecule/resources/playbook.yml
new file mode 100644
index 00000000..d6d47023
--- /dev/null
+++ b/omero/roles/ome.postgresql_client/molecule/resources/playbook.yml
@@ -0,0 +1,5 @@
+---
+- name: Converge
+ hosts: all
+ roles:
+ - role: ome.postgresql_client
diff --git a/omero/roles/ome.postgresql_client/molecule/resources/tests/test_default.py b/omero/roles/ome.postgresql_client/molecule/resources/tests/test_default.py
new file mode 100644
index 00000000..eff7d17e
--- /dev/null
+++ b/omero/roles/ome.postgresql_client/molecule/resources/tests/test_default.py
@@ -0,0 +1,12 @@
+import testinfra.utils.ansible_runner
+import os
+
+testinfra_hosts = testinfra.utils.ansible_runner.AnsibleRunner(
+ os.environ['MOLECULE_INVENTORY_FILE']).get_hosts('all')
+
+
+def test_psql_version(host):
+ variables = host.ansible.get_variables()
+ version = variables["postgresql_version"]
+ out = host.check_output('psql --version')
+ assert out.startswith('psql (PostgreSQL) {}.'.format(version))
diff --git a/omero/roles/ome.postgresql_client/molecule/resources/tests/test_exactversion.py b/omero/roles/ome.postgresql_client/molecule/resources/tests/test_exactversion.py
new file mode 100644
index 00000000..b5cad7f1
--- /dev/null
+++ b/omero/roles/ome.postgresql_client/molecule/resources/tests/test_exactversion.py
@@ -0,0 +1,10 @@
+import testinfra.utils.ansible_runner
+import os
+
+testinfra_hosts = testinfra.utils.ansible_runner.AnsibleRunner(
+ os.environ['MOLECULE_INVENTORY_FILE']).get_hosts('exactversion')
+
+
+def test_psql_version(host):
+ out = host.check_output('psql --version')
+ assert out == 'psql (PostgreSQL) 12.11'
diff --git a/omero/roles/ome.postgresql_client/molecule/rockylinux9/molecule.yml b/omero/roles/ome.postgresql_client/molecule/rockylinux9/molecule.yml
new file mode 100644
index 00000000..88870a4c
--- /dev/null
+++ b/omero/roles/ome.postgresql_client/molecule/rockylinux9/molecule.yml
@@ -0,0 +1,44 @@
+---
+dependency:
+ name: galaxy
+driver:
+ name: docker
+lint: |
+ yamllint .
+ ansible-lint
+ flake8
+platforms:
+ - name: postgresql12
+ image: rockylinux:9
+ - name: postgresql13
+ image: rockylinux:9
+ - name: postgresql14
+ image: rockylinux:9
+ - name: postgresql15
+ image: rockylinux:9
+ - name: postgresql16
+ image: rockylinux:9
+provisioner:
+ name: ansible
+ lint:
+ name: ansible-lint
+ inventory:
+ host_vars:
+ postgresql_exact:
+ postgresql12:
+ postgresql_version: "12"
+ postgresql13:
+ postgresql_version: "13"
+ postgresql14:
+ postgresql_version: "14"
+ postgresql15:
+ postgresql_version: "15"
+ postgresql16:
+ postgresql_version: "16"
+ playbooks:
+ converge: ../resources/playbook.yml
+scenario:
+ name: rockylinux9
+verifier:
+ name: testinfra
+ directory: ../resources/tests/
diff --git a/omero/roles/ome.postgresql_client/molecule/ubuntu2204/molecule.yml b/omero/roles/ome.postgresql_client/molecule/ubuntu2204/molecule.yml
new file mode 100644
index 00000000..06e3e6db
--- /dev/null
+++ b/omero/roles/ome.postgresql_client/molecule/ubuntu2204/molecule.yml
@@ -0,0 +1,43 @@
+---
+dependency:
+ name: galaxy
+driver:
+ name: docker
+lint: |
+ yamllint .
+ ansible-lint
+ flake8
+platforms:
+ - name: postgresql12
+ image: ubuntu:22.04
+ - name: postgresql13
+ image: ubuntu:22.04
+ - name: postgresql14
+ image: ubuntu:22.04
+ - name: postgresql15
+ image: ubuntu:22.04
+ - name: postgresql16
+ image: ubuntu:22.04
+provisioner:
+ name: ansible
+ lint:
+ name: ansible-lint
+ inventory:
+ host_vars:
+ postgresql12:
+ postgresql_version: "12"
+ postgresql13:
+ postgresql_version: "13"
+ postgresql14:
+ postgresql_version: "14"
+ postgresql15:
+ postgresql_version: "15"
+ postgresql16:
+ postgresql_version: "16"
+ playbooks:
+ converge: ../resources/playbook.yml
+scenario:
+ name: ubuntu2204
+verifier:
+ name: testinfra
+ directory: ../resources/tests/
diff --git a/omero/roles/ome.postgresql_client/tasks/debian.yml b/omero/roles/ome.postgresql_client/tasks/debian.yml
new file mode 100644
index 00000000..e6a4b847
--- /dev/null
+++ b/omero/roles/ome.postgresql_client/tasks/debian.yml
@@ -0,0 +1,28 @@
+---
+
+- name: Postgres | install gpg for apt repo
+ become: true
+ ansible.builtin.apt:
+ update_cache: true
+ name: gnupg
+ state: present
+
+- name: Postgres | get postgresql apt key
+ become: true
+ ansible.builtin.apt_key:
+ url: https://www.postgresql.org/media/keys/ACCC4CF8.asc
+ state: present
+
+- name: Postgres | setup apt repo
+ become: true
+ ansible.builtin.apt_repository:
+ codename: "{{ ansible_distribution_release }}"
+ repo: deb http://apt.postgresql.org/pub/repos/apt/
+ {{ ansible_distribution_release }}-pgdg main
+
+- name: Postgres | install client packages
+ become: true
+ ansible.builtin.apt:
+ update_cache: true
+ name: postgresql-client-{{ postgresql_version }}
+ state: present
diff --git a/omero/roles/ome.postgresql_client/tasks/main.yml b/omero/roles/ome.postgresql_client/tasks/main.yml
new file mode 100644
index 00000000..9922bbfe
--- /dev/null
+++ b/omero/roles/ome.postgresql_client/tasks/main.yml
@@ -0,0 +1,9 @@
+---
+
+- name: Import tasks for redhat
+ ansible.builtin.import_tasks: redhat.yml
+ when: ansible_os_family | lower == 'redhat'
+
+- name: Import tasks for debian
+ ansible.builtin.import_tasks: debian.yml
+ when: ansible_os_family | lower == 'debian'
diff --git a/omero/roles/ome.postgresql_client/tasks/redhat.yml b/omero/roles/ome.postgresql_client/tasks/redhat.yml
new file mode 100644
index 00000000..0365f4e1
--- /dev/null
+++ b/omero/roles/ome.postgresql_client/tasks/redhat.yml
@@ -0,0 +1,44 @@
+---
+
+- name: Import a key for postgres (x86_64)
+ become: true
+ ansible.builtin.rpm_key:
+ state: present
+ key: https://download.postgresql.org/pub/repos/yum/keys/PGDG-RPM-GPG-KEY-RHEL
+ when: ansible_facts.get('architecture') is search("x86_64")
+
+- name: Import a key for postgres (aarch64)
+ become: true
+ ansible.builtin.rpm_key:
+ state: present
+ key: https://download.postgresql.org/pub/repos/yum/keys/PGDG-RPM-GPG-KEY-AARCH64-RHEL
+ when: ansible_facts.get('architecture') is search("aarch64")
+
+- name: Postgres | setup dnf repository for arch64
+ become: true
+ ansible.builtin.dnf:
+ update_cache: true
+ name:
+ https://download.postgresql.org/pub/repos/yum/reporpms/EL-9-aarch64/pgdg-redhat-repo-latest.noarch.rpm
+ state: present
+ when: ansible_facts.get('architecture') is search("aarch64")
+
+- name: Postgres | setup dnf repository for amd64
+ become: true
+ ansible.builtin.dnf:
+ update_cache: true
+ name:
+ https://download.postgresql.org/pub/repos/yum/reporpms/EL-9-x86_64/pgdg-redhat-repo-latest.noarch.rpm
+ state: present
+ when: ansible_facts.get('architecture') is search("x86_64")
+
+- name: Postgres | install client packages
+ become: true
+ ansible.builtin.dnf:
+ update_cache: true
+ name: >-
+ postgresql{{ postgresql_version }}{{
+ (postgresql_package_version | length > 0) |
+ ternary('-' + postgresql_package_version, '')
+ }}
+ state: present
diff --git a/omero/roles/ome.postgresql_client/templates/pg_hba-conf.j2 b/omero/roles/ome.postgresql_client/templates/pg_hba-conf.j2
new file mode 100644
index 00000000..328c05fe
--- /dev/null
+++ b/omero/roles/ome.postgresql_client/templates/pg_hba-conf.j2
@@ -0,0 +1,11 @@
+# PostgreSQL Client Authentication Configuration File (Ansible)
+# TYPE DATABASE USER ADDRESS METHOD
+{% if postgresql_server_auth_local %}
+local all all peer
+host all all 127.0.0.1/32 md5
+host all all ::1/128 md5
+{% endif %}
+
+{% for item in postgresql_server_auth %}
+host {{ item.database }} {{ item.user }} {{ item.address }} {{ item.method | default("md5") }}
+{% endfor %}
diff --git a/omero/roles/ome.postgresql_client/templates/postgresql-conf-10-ubuntu.j2 b/omero/roles/ome.postgresql_client/templates/postgresql-conf-10-ubuntu.j2
new file mode 100644
index 00000000..c8da6439
--- /dev/null
+++ b/omero/roles/ome.postgresql_client/templates/postgresql-conf-10-ubuntu.j2
@@ -0,0 +1,673 @@
+# -----------------------------
+# PostgreSQL configuration file
+# -----------------------------
+#
+# This file consists of lines of the form:
+#
+# name = value
+#
+# (The "=" is optional.) Whitespace may be used. Comments are introduced with
+# "#" anywhere on a line. The complete list of parameter names and allowed
+# values can be found in the PostgreSQL documentation.
+#
+# The commented-out settings shown in this file represent the default values.
+# Re-commenting a setting is NOT sufficient to revert it to the default value;
+# you need to reload the server.
+#
+# This file is read on server startup and when the server receives a SIGHUP
+# signal. If you edit the file on a running system, you have to SIGHUP the
+# server for the changes to take effect, run "pg_ctl reload", or execute
+# "SELECT pg_reload_conf()". Some parameters, which are marked below,
+# require a server shutdown and restart to take effect.
+#
+# Any parameter can also be given as a command-line option to the server, e.g.,
+# "postgres -c log_connections=on". Some parameters can be changed at run time
+# with the "SET" SQL command.
+#
+# Memory units: kB = kilobytes Time units: ms = milliseconds
+# MB = megabytes s = seconds
+# GB = gigabytes min = minutes
+# TB = terabytes h = hours
+# d = days
+
+
+#------------------------------------------------------------------------------
+# FILE LOCATIONS
+#------------------------------------------------------------------------------
+
+# The default values of these variables are driven from the -D command-line
+# option or PGDATA environment variable, represented here as ConfigDir.
+
+data_directory = '/var/lib/postgresql/10/main' # use data in another directory
+ # (change requires restart)
+hba_file = '/etc/postgresql/10/main/pg_hba.conf' # host-based authentication file
+ # (change requires restart)
+ident_file = '/etc/postgresql/10/main/pg_ident.conf' # ident configuration file
+ # (change requires restart)
+
+# If external_pid_file is not explicitly set, no extra PID file is written.
+external_pid_file = '/var/run/postgresql/10-main.pid' # write an extra PID file
+ # (change requires restart)
+
+
+#------------------------------------------------------------------------------
+# CONNECTIONS AND AUTHENTICATION
+#------------------------------------------------------------------------------
+
+# - Connection Settings -
+
+#listen_addresses = 'localhost' # what IP address(es) to listen on;
+ # comma-separated list of addresses;
+ # defaults to 'localhost'; use '*' for all
+ # (change requires restart)
+port = 5432 # (change requires restart)
+max_connections = 100 # (change requires restart)
+#superuser_reserved_connections = 3 # (change requires restart)
+unix_socket_directories = '/var/run/postgresql' # comma-separated list of directories
+ # (change requires restart)
+#unix_socket_group = '' # (change requires restart)
+#unix_socket_permissions = 0777 # begin with 0 to use octal notation
+ # (change requires restart)
+#bonjour = off # advertise server via Bonjour
+ # (change requires restart)
+#bonjour_name = '' # defaults to the computer name
+ # (change requires restart)
+
+# - Security and Authentication -
+
+#authentication_timeout = 1min # 1s-600s
+ssl = on
+#ssl_ciphers = 'HIGH:MEDIUM:+3DES:!aNULL' # allowed SSL ciphers
+#ssl_prefer_server_ciphers = on
+#ssl_ecdh_curve = 'prime256v1'
+#ssl_dh_params_file = ''
+ssl_cert_file = '/etc/ssl/certs/ssl-cert-snakeoil.pem'
+ssl_key_file = '/etc/ssl/private/ssl-cert-snakeoil.key'
+#ssl_ca_file = ''
+#ssl_crl_file = ''
+#password_encryption = md5 # md5 or scram-sha-256
+#db_user_namespace = off
+#row_security = on
+
+# GSSAPI using Kerberos
+#krb_server_keyfile = ''
+#krb_caseins_users = off
+
+# - TCP Keepalives -
+# see "man 7 tcp" for details
+
+#tcp_keepalives_idle = 0 # TCP_KEEPIDLE, in seconds;
+ # 0 selects the system default
+#tcp_keepalives_interval = 0 # TCP_KEEPINTVL, in seconds;
+ # 0 selects the system default
+#tcp_keepalives_count = 0 # TCP_KEEPCNT;
+ # 0 selects the system default
+
+
+#------------------------------------------------------------------------------
+# RESOURCE USAGE (except WAL)
+#------------------------------------------------------------------------------
+
+# - Memory -
+
+shared_buffers = 128MB # min 128kB
+ # (change requires restart)
+#huge_pages = try # on, off, or try
+ # (change requires restart)
+#temp_buffers = 8MB # min 800kB
+#max_prepared_transactions = 0 # zero disables the feature
+ # (change requires restart)
+# Caution: it is not advisable to set max_prepared_transactions nonzero unless
+# you actively intend to use prepared transactions.
+#work_mem = 4MB # min 64kB
+#maintenance_work_mem = 64MB # min 1MB
+#replacement_sort_tuples = 150000 # limits use of replacement selection sort
+#autovacuum_work_mem = -1 # min 1MB, or -1 to use maintenance_work_mem
+#max_stack_depth = 2MB # min 100kB
+dynamic_shared_memory_type = posix # the default is the first option
+ # supported by the operating system:
+ # posix
+ # sysv
+ # windows
+ # mmap
+ # use none to disable dynamic shared memory
+ # (change requires restart)
+
+# - Disk -
+
+#temp_file_limit = -1 # limits per-process temp file space
+ # in kB, or -1 for no limit
+
+# - Kernel Resource Usage -
+
+#max_files_per_process = 1000 # min 25
+ # (change requires restart)
+#shared_preload_libraries = '' # (change requires restart)
+
+# - Cost-Based Vacuum Delay -
+
+#vacuum_cost_delay = 0 # 0-100 milliseconds
+#vacuum_cost_page_hit = 1 # 0-10000 credits
+#vacuum_cost_page_miss = 10 # 0-10000 credits
+#vacuum_cost_page_dirty = 20 # 0-10000 credits
+#vacuum_cost_limit = 200 # 1-10000 credits
+
+# - Background Writer -
+
+#bgwriter_delay = 200ms # 10-10000ms between rounds
+#bgwriter_lru_maxpages = 100 # 0-1000 max buffers written/round
+#bgwriter_lru_multiplier = 2.0 # 0-10.0 multiplier on buffers scanned/round
+#bgwriter_flush_after = 512kB # measured in pages, 0 disables
+
+# - Asynchronous Behavior -
+
+#effective_io_concurrency = 1 # 1-1000; 0 disables prefetching
+#max_worker_processes = 8 # (change requires restart)
+#max_parallel_workers_per_gather = 2 # taken from max_parallel_workers
+#max_parallel_workers = 8 # maximum number of max_worker_processes that
+ # can be used in parallel queries
+#old_snapshot_threshold = -1 # 1min-60d; -1 disables; 0 is immediate
+ # (change requires restart)
+#backend_flush_after = 0 # measured in pages, 0 disables
+
+
+#------------------------------------------------------------------------------
+# WRITE AHEAD LOG
+#------------------------------------------------------------------------------
+
+# - Settings -
+
+#wal_level = replica # minimal, replica, or logical
+ # (change requires restart)
+#fsync = on # flush data to disk for crash safety
+ # (turning this off can cause
+ # unrecoverable data corruption)
+#synchronous_commit = on # synchronization level;
+ # off, local, remote_write, remote_apply, or on
+#wal_sync_method = fsync # the default is the first option
+ # supported by the operating system:
+ # open_datasync
+ # fdatasync (default on Linux)
+ # fsync
+ # fsync_writethrough
+ # open_sync
+#full_page_writes = on # recover from partial page writes
+#wal_compression = off # enable compression of full-page writes
+#wal_log_hints = off # also do full page writes of non-critical updates
+ # (change requires restart)
+#wal_buffers = -1 # min 32kB, -1 sets based on shared_buffers
+ # (change requires restart)
+#wal_writer_delay = 200ms # 1-10000 milliseconds
+#wal_writer_flush_after = 1MB # measured in pages, 0 disables
+
+#commit_delay = 0 # range 0-100000, in microseconds
+#commit_siblings = 5 # range 1-1000
+
+# - Checkpoints -
+
+#checkpoint_timeout = 5min # range 30s-1d
+#max_wal_size = 1GB
+#min_wal_size = 80MB
+#checkpoint_completion_target = 0.5 # checkpoint target duration, 0.0 - 1.0
+#checkpoint_flush_after = 256kB # measured in pages, 0 disables
+#checkpoint_warning = 30s # 0 disables
+
+# - Archiving -
+
+#archive_mode = off # enables archiving; off, on, or always
+ # (change requires restart)
+#archive_command = '' # command to use to archive a logfile segment
+ # placeholders: %p = path of file to archive
+ # %f = file name only
+ # e.g. 'test ! -f /mnt/server/archivedir/%f && cp %p /mnt/server/archivedir/%f'
+#archive_timeout = 0 # force a logfile segment switch after this
+ # number of seconds; 0 disables
+
+
+#------------------------------------------------------------------------------
+# REPLICATION
+#------------------------------------------------------------------------------
+
+# - Sending Server(s) -
+
+# Set these on the master and on any standby that will send replication data.
+
+#max_wal_senders = 10 # max number of walsender processes
+ # (change requires restart)
+#wal_keep_segments = 0 # in logfile segments, 16MB each; 0 disables
+#wal_sender_timeout = 60s # in milliseconds; 0 disables
+
+#max_replication_slots = 10 # max number of replication slots
+ # (change requires restart)
+#track_commit_timestamp = off # collect timestamp of transaction commit
+ # (change requires restart)
+
+# - Master Server -
+
+# These settings are ignored on a standby server.
+
+#synchronous_standby_names = '' # standby servers that provide sync rep
+ # method to choose sync standbys, number of sync standbys,
+ # and comma-separated list of application_name
+ # from standby(s); '*' = all
+#vacuum_defer_cleanup_age = 0 # number of xacts by which cleanup is delayed
+
+# - Standby Servers -
+
+# These settings are ignored on a master server.
+
+#hot_standby = on # "off" disallows queries during recovery
+ # (change requires restart)
+#max_standby_archive_delay = 30s # max delay before canceling queries
+ # when reading WAL from archive;
+ # -1 allows indefinite delay
+#max_standby_streaming_delay = 30s # max delay before canceling queries
+ # when reading streaming WAL;
+ # -1 allows indefinite delay
+#wal_receiver_status_interval = 10s # send replies at least this often
+ # 0 disables
+#hot_standby_feedback = off # send info from standby to prevent
+ # query conflicts
+#wal_receiver_timeout = 60s # time that receiver waits for
+ # communication from master
+ # in milliseconds; 0 disables
+#wal_retrieve_retry_interval = 5s # time to wait before retrying to
+ # retrieve WAL after a failed attempt
+
+# - Subscribers -
+
+# These settings are ignored on a publisher.
+
+#max_logical_replication_workers = 4 # taken from max_worker_processes
+ # (change requires restart)
+#max_sync_workers_per_subscription = 2 # taken from max_logical_replication_workers
+
+
+#------------------------------------------------------------------------------
+# QUERY TUNING
+#------------------------------------------------------------------------------
+
+# - Planner Method Configuration -
+
+#enable_bitmapscan = on
+#enable_hashagg = on
+#enable_hashjoin = on
+#enable_indexscan = on
+#enable_indexonlyscan = on
+#enable_material = on
+#enable_mergejoin = on
+#enable_nestloop = on
+#enable_seqscan = on
+#enable_sort = on
+#enable_tidscan = on
+
+# - Planner Cost Constants -
+
+#seq_page_cost = 1.0 # measured on an arbitrary scale
+#random_page_cost = 4.0 # same scale as above
+#cpu_tuple_cost = 0.01 # same scale as above
+#cpu_index_tuple_cost = 0.005 # same scale as above
+#cpu_operator_cost = 0.0025 # same scale as above
+#parallel_tuple_cost = 0.1 # same scale as above
+#parallel_setup_cost = 1000.0 # same scale as above
+#min_parallel_table_scan_size = 8MB
+#min_parallel_index_scan_size = 512kB
+#effective_cache_size = 4GB
+
+# - Genetic Query Optimizer -
+
+#geqo = on
+#geqo_threshold = 12
+#geqo_effort = 5 # range 1-10
+#geqo_pool_size = 0 # selects default based on effort
+#geqo_generations = 0 # selects default based on effort
+#geqo_selection_bias = 2.0 # range 1.5-2.0
+#geqo_seed = 0.0 # range 0.0-1.0
+
+# - Other Planner Options -
+
+#default_statistics_target = 100 # range 1-10000
+#constraint_exclusion = partition # on, off, or partition
+#cursor_tuple_fraction = 0.1 # range 0.0-1.0
+#from_collapse_limit = 8
+#join_collapse_limit = 8 # 1 disables collapsing of explicit
+ # JOIN clauses
+#force_parallel_mode = off
+
+
+#------------------------------------------------------------------------------
+# ERROR REPORTING AND LOGGING
+#------------------------------------------------------------------------------
+
+# - Where to Log -
+
+log_destination = 'stderr' # Valid values are combinations of
+ # stderr, csvlog, syslog, and eventlog,
+ # depending on platform. csvlog
+ # requires logging_collector to be on.
+
+# This is used when logging to stderr:
+logging_collector = on # Enable capturing of stderr and csvlog
+ # into log files. Required to be on for
+ # csvlogs.
+ # (change requires restart)
+
+# These are only used if logging_collector is on:
+log_directory = 'pg_log' # directory where log files are written,
+ # can be absolute or relative to PGDATA
+log_filename = 'postgresql-%a.log' # log file name pattern,
+ # can include strftime() escapes
+#log_file_mode = 0600 # creation mode for log files,
+ # begin with 0 to use octal notation
+log_truncate_on_rotation = on # If on, an existing log file with the
+ # same name as the new log file will be
+ # truncated rather than appended to.
+ # But such truncation only occurs on
+ # time-driven rotation, not on restarts
+ # or size-driven rotation. Default is
+ # off, meaning append to existing files
+ # in all cases.
+log_rotation_age = 1d # Automatic rotation of logfiles will
+ # happen after that time. 0 disables.
+log_rotation_size = 0 # Automatic rotation of logfiles will
+ # happen after that much log output.
+ # 0 disables.
+
+# These are relevant when logging to syslog:
+#syslog_facility = 'LOCAL0'
+#syslog_ident = 'postgres'
+#syslog_sequence_numbers = on
+#syslog_split_messages = on
+
+# This is only relevant when logging to eventlog (win32):
+# (change requires restart)
+#event_source = 'PostgreSQL'
+
+# - When to Log -
+
+#client_min_messages = notice # values in order of decreasing detail:
+ # debug5
+ # debug4
+ # debug3
+ # debug2
+ # debug1
+ # log
+ # notice
+ # warning
+ # error
+
+#log_min_messages = warning # values in order of decreasing detail:
+ # debug5
+ # debug4
+ # debug3
+ # debug2
+ # debug1
+ # info
+ # notice
+ # warning
+ # error
+ # log
+ # fatal
+ # panic
+
+#log_min_error_statement = error # values in order of decreasing detail:
+ # debug5
+ # debug4
+ # debug3
+ # debug2
+ # debug1
+ # info
+ # notice
+ # warning
+ # error
+ # log
+ # fatal
+ # panic (effectively off)
+
+#log_min_duration_statement = -1 # -1 is disabled, 0 logs all statements
+ # and their durations, > 0 logs only
+ # statements running at least this number
+ # of milliseconds
+
+
+# - What to Log -
+
+#debug_print_parse = off
+#debug_print_rewritten = off
+#debug_print_plan = off
+#debug_pretty_print = on
+#log_checkpoints = off
+#log_connections = off
+#log_disconnections = off
+#log_duration = off
+#log_error_verbosity = default # terse, default, or verbose messages
+#log_hostname = off
+log_line_prefix = '%m [%p] %q%u@%d ' # special values:
+ # %a = application name
+ # %u = user name
+ # %d = database name
+ # %r = remote host and port
+ # %h = remote host
+ # %p = process ID
+ # %t = timestamp without milliseconds
+ # %m = timestamp with milliseconds
+ # %n = timestamp with milliseconds (as a Unix epoch)
+ # %i = command tag
+ # %e = SQL state
+ # %c = session ID
+ # %l = session line number
+ # %s = session start timestamp
+ # %v = virtual transaction ID
+ # %x = transaction ID (0 if none)
+ # %q = stop here in non-session
+ # processes
+ # %% = '%'
+ # e.g. '<%u%%%d> '
+#log_lock_waits = off # log lock waits >= deadlock_timeout
+#log_statement = 'none' # none, ddl, mod, all
+#log_replication_commands = off
+#log_temp_files = -1 # log temporary files equal or larger
+ # than the specified size in kilobytes;
+ # -1 disables, 0 logs all temp files
+log_timezone = 'Etc/UTC'
+
+
+# - Process Title -
+
+cluster_name = '10/main' # added to process titles if nonempty
+ # (change requires restart)
+#update_process_title = on
+
+
+#------------------------------------------------------------------------------
+# RUNTIME STATISTICS
+#------------------------------------------------------------------------------
+
+# - Query/Index Statistics Collector -
+
+#track_activities = on
+#track_counts = on
+#track_io_timing = off
+#track_functions = none # none, pl, all
+#track_activity_query_size = 1024 # (change requires restart)
+stats_temp_directory = '/var/run/postgresql/10-main.pg_stat_tmp'
+
+
+# - Statistics Monitoring -
+
+#log_parser_stats = off
+#log_planner_stats = off
+#log_executor_stats = off
+#log_statement_stats = off
+
+
+#------------------------------------------------------------------------------
+# AUTOVACUUM PARAMETERS
+#------------------------------------------------------------------------------
+
+#autovacuum = on # Enable autovacuum subprocess? 'on'
+ # requires track_counts to also be on.
+#log_autovacuum_min_duration = -1 # -1 disables, 0 logs all actions and
+ # their durations, > 0 logs only
+ # actions running at least this number
+ # of milliseconds.
+#autovacuum_max_workers = 3 # max number of autovacuum subprocesses
+ # (change requires restart)
+#autovacuum_naptime = 1min # time between autovacuum runs
+#autovacuum_vacuum_threshold = 50 # min number of row updates before
+ # vacuum
+#autovacuum_analyze_threshold = 50 # min number of row updates before
+ # analyze
+#autovacuum_vacuum_scale_factor = 0.2 # fraction of table size before vacuum
+#autovacuum_analyze_scale_factor = 0.1 # fraction of table size before analyze
+#autovacuum_freeze_max_age = 200000000 # maximum XID age before forced vacuum
+ # (change requires restart)
+#autovacuum_multixact_freeze_max_age = 400000000 # maximum multixact age
+ # before forced vacuum
+ # (change requires restart)
+#autovacuum_vacuum_cost_delay = 20ms # default vacuum cost delay for
+ # autovacuum, in milliseconds;
+ # -1 means use vacuum_cost_delay
+#autovacuum_vacuum_cost_limit = -1 # default vacuum cost limit for
+ # autovacuum, -1 means use
+ # vacuum_cost_limit
+
+
+#------------------------------------------------------------------------------
+# CLIENT CONNECTION DEFAULTS
+#------------------------------------------------------------------------------
+
+# - Statement Behavior -
+
+#search_path = '"$user", public' # schema names
+#default_tablespace = '' # a tablespace name, '' uses the default
+#temp_tablespaces = '' # a list of tablespace names, '' uses
+ # only default tablespace
+#check_function_bodies = on
+#default_transaction_isolation = 'read committed'
+#default_transaction_read_only = off
+#default_transaction_deferrable = off
+#session_replication_role = 'origin'
+#statement_timeout = 0 # in milliseconds, 0 is disabled
+#lock_timeout = 0 # in milliseconds, 0 is disabled
+#idle_in_transaction_session_timeout = 0 # in milliseconds, 0 is disabled
+#vacuum_freeze_min_age = 50000000
+#vacuum_freeze_table_age = 150000000
+#vacuum_multixact_freeze_min_age = 5000000
+#vacuum_multixact_freeze_table_age = 150000000
+#bytea_output = 'hex' # hex, escape
+#xmlbinary = 'base64'
+#xmloption = 'content'
+#gin_fuzzy_search_limit = 0
+#gin_pending_list_limit = 4MB
+
+# - Locale and Formatting -
+
+datestyle = 'iso, mdy'
+#intervalstyle = 'postgres'
+timezone = 'Etc/UTC'
+#timezone_abbreviations = 'Default' # Select the set of available time zone
+ # abbreviations. Currently, there are
+ # Default
+ # Australia (historical usage)
+ # India
+ # You can create your own file in
+ # share/timezonesets/.
+#extra_float_digits = 0 # min -15, max 3
+#client_encoding = sql_ascii # actually, defaults to database
+ # encoding
+
+# These settings are initialized by initdb, but they can be changed.
+lc_messages = 'C.UTF-8' # locale for system error message
+ # strings
+lc_monetary = 'C.UTF-8' # locale for monetary formatting
+lc_numeric = 'C.UTF-8' # locale for number formatting
+lc_time = 'C.UTF-8' # locale for time formatting
+
+# default configuration for text search
+default_text_search_config = 'pg_catalog.english'
+
+# - Other Defaults -
+
+#dynamic_library_path = '$libdir'
+#local_preload_libraries = ''
+#session_preload_libraries = ''
+
+
+#------------------------------------------------------------------------------
+# LOCK MANAGEMENT
+#------------------------------------------------------------------------------
+
+#deadlock_timeout = 1s
+#max_locks_per_transaction = 64 # min 10
+ # (change requires restart)
+#max_pred_locks_per_transaction = 64 # min 10
+ # (change requires restart)
+#max_pred_locks_per_relation = -2 # negative values mean
+ # (max_pred_locks_per_transaction
+ # / -max_pred_locks_per_relation) - 1
+#max_pred_locks_per_page = 2 # min 0
+
+
+#------------------------------------------------------------------------------
+# VERSION/PLATFORM COMPATIBILITY
+#------------------------------------------------------------------------------
+
+# - Previous PostgreSQL Versions -
+
+#array_nulls = on
+#backslash_quote = safe_encoding # on, off, or safe_encoding
+#default_with_oids = off
+#escape_string_warning = on
+#lo_compat_privileges = off
+#operator_precedence_warning = off
+#quote_all_identifiers = off
+#standard_conforming_strings = on
+#synchronize_seqscans = on
+
+# - Other Platforms and Clients -
+
+#transform_null_equals = off
+
+
+#------------------------------------------------------------------------------
+# ERROR HANDLING
+#------------------------------------------------------------------------------
+
+#exit_on_error = off # terminate session on any error?
+#restart_after_crash = on # reinitialize after backend crash?
+#data_sync_retry = off # retry or panic on failure to fsync
+ # data?
+ # (change requires restart)
+
+
+#------------------------------------------------------------------------------
+# CONFIG FILE INCLUDES
+#------------------------------------------------------------------------------
+
+# These options allow settings to be loaded from files other than the
+# default postgresql.conf. Note that these are directives, not variable
+# assignments, so they can usefully be given more than once.
+
+include_dir = 'conf.d' # include files ending in '.conf' from
+ # a directory, e.g., 'conf.d'
+#include_if_exists = '...' # include file only if it exists
+#include = '...' # include file
+
+
+#------------------------------------------------------------------------------
+# CUSTOMIZED OPTIONS
+#------------------------------------------------------------------------------
+
+# Add settings for extensions here
+
+
+#------------------------------------------------------------------------------
+# ANSIBLE OPTIONS
+#------------------------------------------------------------------------------
+
+listen_addresses = {{ postgresql_server_listen }}
+
+{% for option in (postgresql_server_conf | sort) %}
+{{ option }} = {{ postgresql_server_conf[option] }}
+{% endfor %}
diff --git a/omero/roles/ome.postgresql_client/templates/postgresql-conf.j2 b/omero/roles/ome.postgresql_client/templates/postgresql-conf.j2
new file mode 100644
index 00000000..0f1193d6
--- /dev/null
+++ b/omero/roles/ome.postgresql_client/templates/postgresql-conf.j2
@@ -0,0 +1,654 @@
+# {{ ansible_managed }}
+
+# -----------------------------
+# PostgreSQL configuration file
+# -----------------------------
+#
+# This file consists of lines of the form:
+#
+# name = value
+#
+# (The "=" is optional.) Whitespace may be used. Comments are introduced with
+# "#" anywhere on a line. The complete list of parameter names and allowed
+# values can be found in the PostgreSQL documentation.
+#
+# The commented-out settings shown in this file represent the default values.
+# Re-commenting a setting is NOT sufficient to revert it to the default value;
+# you need to reload the server.
+#
+# This file is read on server startup and when the server receives a SIGHUP
+# signal. If you edit the file on a running system, you have to SIGHUP the
+# server for the changes to take effect, or use "pg_ctl reload". Some
+# parameters, which are marked below, require a server shutdown and restart to
+# take effect.
+#
+# Any parameter can also be given as a command-line option to the server, e.g.,
+# "postgres -c log_connections=on". Some parameters can be changed at run time
+# with the "SET" SQL command.
+#
+# Memory units: kB = kilobytes Time units: ms = milliseconds
+# MB = megabytes s = seconds
+# GB = gigabytes min = minutes
+# TB = terabytes h = hours
+# d = days
+
+
+#------------------------------------------------------------------------------
+# FILE LOCATIONS
+#------------------------------------------------------------------------------
+
+# The default values of these variables are driven from the -D command-line
+# option or PGDATA environment variable, represented here as ConfigDir.
+
+#data_directory = 'ConfigDir' # use data in another directory
+ # (change requires restart)
+#hba_file = 'ConfigDir/pg_hba.conf' # host-based authentication file
+ # (change requires restart)
+#ident_file = 'ConfigDir/pg_ident.conf' # ident configuration file
+ # (change requires restart)
+
+# If external_pid_file is not explicitly set, no extra PID file is written.
+#external_pid_file = '' # write an extra PID file
+ # (change requires restart)
+
+
+#------------------------------------------------------------------------------
+# CONNECTIONS AND AUTHENTICATION
+#------------------------------------------------------------------------------
+
+# - Connection Settings -
+
+#listen_addresses = 'localhost' # what IP address(es) to listen on;
+ # comma-separated list of addresses;
+ # defaults to 'localhost'; use '*' for all
+ # (change requires restart)
+#port = 5432 # (change requires restart)
+max_connections = 100 # (change requires restart)
+#superuser_reserved_connections = 3 # (change requires restart)
+#unix_socket_directories = '/var/run/postgresql, /tmp' # comma-separated list of directories
+ # (change requires restart)
+#unix_socket_group = '' # (change requires restart)
+#unix_socket_permissions = 0777 # begin with 0 to use octal notation
+ # (change requires restart)
+#bonjour = off # advertise server via Bonjour
+ # (change requires restart)
+#bonjour_name = '' # defaults to the computer name
+ # (change requires restart)
+
+# - Security and Authentication -
+
+#authentication_timeout = 1min # 1s-600s
+#ssl = off # (change requires restart)
+#ssl_ciphers = 'HIGH:MEDIUM:+3DES:!aNULL' # allowed SSL ciphers
+ # (change requires restart)
+#ssl_prefer_server_ciphers = on # (change requires restart)
+#ssl_ecdh_curve = 'prime256v1' # (change requires restart)
+#ssl_cert_file = 'server.crt' # (change requires restart)
+#ssl_key_file = 'server.key' # (change requires restart)
+#ssl_ca_file = '' # (change requires restart)
+#ssl_crl_file = '' # (change requires restart)
+#password_encryption = on
+#db_user_namespace = off
+#row_security = on
+
+# GSSAPI using Kerberos
+#krb_server_keyfile = ''
+#krb_caseins_users = off
+
+# - TCP Keepalives -
+# see "man 7 tcp" for details
+
+#tcp_keepalives_idle = 0 # TCP_KEEPIDLE, in seconds;
+ # 0 selects the system default
+#tcp_keepalives_interval = 0 # TCP_KEEPINTVL, in seconds;
+ # 0 selects the system default
+#tcp_keepalives_count = 0 # TCP_KEEPCNT;
+ # 0 selects the system default
+
+
+#------------------------------------------------------------------------------
+# RESOURCE USAGE (except WAL)
+#------------------------------------------------------------------------------
+
+# - Memory -
+
+shared_buffers = 128MB # min 128kB
+ # (change requires restart)
+#huge_pages = try # on, off, or try
+ # (change requires restart)
+#temp_buffers = 8MB # min 800kB
+#max_prepared_transactions = 0 # zero disables the feature
+ # (change requires restart)
+# Caution: it is not advisable to set max_prepared_transactions nonzero unless
+# you actively intend to use prepared transactions.
+#work_mem = 4MB # min 64kB
+#maintenance_work_mem = 64MB # min 1MB
+#replacement_sort_tuples = 150000 # limits use of replacement selection sort
+#autovacuum_work_mem = -1 # min 1MB, or -1 to use maintenance_work_mem
+#max_stack_depth = 2MB # min 100kB
+dynamic_shared_memory_type = posix # the default is the first option
+ # supported by the operating system:
+ # posix
+ # sysv
+ # windows
+ # mmap
+ # use none to disable dynamic shared memory
+
+# - Disk -
+
+#temp_file_limit = -1 # limits per-process temp file space
+ # in kB, or -1 for no limit
+
+# - Kernel Resource Usage -
+
+#max_files_per_process = 1000 # min 25
+ # (change requires restart)
+#shared_preload_libraries = '' # (change requires restart)
+
+# - Cost-Based Vacuum Delay -
+
+#vacuum_cost_delay = 0 # 0-100 milliseconds
+#vacuum_cost_page_hit = 1 # 0-10000 credits
+#vacuum_cost_page_miss = 10 # 0-10000 credits
+#vacuum_cost_page_dirty = 20 # 0-10000 credits
+#vacuum_cost_limit = 200 # 1-10000 credits
+
+# - Background Writer -
+
+#bgwriter_delay = 200ms # 10-10000ms between rounds
+#bgwriter_lru_maxpages = 100 # 0-1000 max buffers written/round
+#bgwriter_lru_multiplier = 2.0 # 0-10.0 multiplier on buffers scanned/round
+#bgwriter_flush_after = 512kB # measured in pages, 0 disables
+
+# - Asynchronous Behavior -
+
+#effective_io_concurrency = 1 # 1-1000; 0 disables prefetching
+#max_worker_processes = 8 # (change requires restart)
+#max_parallel_workers_per_gather = 0 # taken from max_worker_processes
+#old_snapshot_threshold = -1 # 1min-60d; -1 disables; 0 is immediate
+ # (change requires restart)
+#backend_flush_after = 0 # measured in pages, 0 disables
+
+
+#------------------------------------------------------------------------------
+# WRITE AHEAD LOG
+#------------------------------------------------------------------------------
+
+# - Settings -
+
+#wal_level = minimal # minimal, replica, or logical
+ # (change requires restart)
+#fsync = on # flush data to disk for crash safety
+ # (turning this off can cause
+ # unrecoverable data corruption)
+#synchronous_commit = on # synchronization level;
+ # off, local, remote_write, remote_apply, or on
+#wal_sync_method = fsync # the default is the first option
+ # supported by the operating system:
+ # open_datasync
+ # fdatasync (default on Linux)
+ # fsync
+ # fsync_writethrough
+ # open_sync
+#full_page_writes = on # recover from partial page writes
+#wal_compression = off # enable compression of full-page writes
+#wal_log_hints = off # also do full page writes of non-critical updates
+ # (change requires restart)
+#wal_buffers = -1 # min 32kB, -1 sets based on shared_buffers
+ # (change requires restart)
+#wal_writer_delay = 200ms # 1-10000 milliseconds
+#wal_writer_flush_after = 1MB # measured in pages, 0 disables
+
+#commit_delay = 0 # range 0-100000, in microseconds
+#commit_siblings = 5 # range 1-1000
+
+# - Checkpoints -
+
+#checkpoint_timeout = 5min # range 30s-1d
+#max_wal_size = 1GB
+#min_wal_size = 80MB
+#checkpoint_completion_target = 0.5 # checkpoint target duration, 0.0 - 1.0
+#checkpoint_flush_after = 256kB # measured in pages, 0 disables
+#checkpoint_warning = 30s # 0 disables
+
+# - Archiving -
+
+#archive_mode = off # enables archiving; off, on, or always
+ # (change requires restart)
+#archive_command = '' # command to use to archive a logfile segment
+ # placeholders: %p = path of file to archive
+ # %f = file name only
+ # e.g. 'test ! -f /mnt/server/archivedir/%f && cp %p /mnt/server/archivedir/%f'
+#archive_timeout = 0 # force a logfile segment switch after this
+ # number of seconds; 0 disables
+
+
+#------------------------------------------------------------------------------
+# REPLICATION
+#------------------------------------------------------------------------------
+
+# - Sending Server(s) -
+
+# Set these on the master and on any standby that will send replication data.
+
+#max_wal_senders = 0 # max number of walsender processes
+ # (change requires restart)
+#wal_keep_segments = 0 # in logfile segments, 16MB each; 0 disables
+#wal_sender_timeout = 60s # in milliseconds; 0 disables
+
+#max_replication_slots = 0 # max number of replication slots
+ # (change requires restart)
+#track_commit_timestamp = off # collect timestamp of transaction commit
+ # (change requires restart)
+
+# - Master Server -
+
+# These settings are ignored on a standby server.
+
+#synchronous_standby_names = '' # standby servers that provide sync rep
+ # number of sync standbys and comma-separated list of application_name
+ # from standby(s); '*' = all
+#vacuum_defer_cleanup_age = 0 # number of xacts by which cleanup is delayed
+
+# - Standby Servers -
+
+# These settings are ignored on a master server.
+
+#hot_standby = off # "on" allows queries during recovery
+ # (change requires restart)
+#max_standby_archive_delay = 30s # max delay before canceling queries
+ # when reading WAL from archive;
+ # -1 allows indefinite delay
+#max_standby_streaming_delay = 30s # max delay before canceling queries
+ # when reading streaming WAL;
+ # -1 allows indefinite delay
+#wal_receiver_status_interval = 10s # send replies at least this often
+ # 0 disables
+#hot_standby_feedback = off # send info from standby to prevent
+ # query conflicts
+#wal_receiver_timeout = 60s # time that receiver waits for
+ # communication from master
+ # in milliseconds; 0 disables
+#wal_retrieve_retry_interval = 5s # time to wait before retrying to
+ # retrieve WAL after a failed attempt
+
+
+#------------------------------------------------------------------------------
+# QUERY TUNING
+#------------------------------------------------------------------------------
+
+# - Planner Method Configuration -
+
+#enable_bitmapscan = on
+#enable_hashagg = on
+#enable_hashjoin = on
+#enable_indexscan = on
+#enable_indexonlyscan = on
+#enable_material = on
+#enable_mergejoin = on
+#enable_nestloop = on
+#enable_seqscan = on
+#enable_sort = on
+#enable_tidscan = on
+
+# - Planner Cost Constants -
+
+#seq_page_cost = 1.0 # measured on an arbitrary scale
+#random_page_cost = 4.0 # same scale as above
+#cpu_tuple_cost = 0.01 # same scale as above
+#cpu_index_tuple_cost = 0.005 # same scale as above
+#cpu_operator_cost = 0.0025 # same scale as above
+#parallel_tuple_cost = 0.1 # same scale as above
+#parallel_setup_cost = 1000.0 # same scale as above
+#min_parallel_relation_size = 8MB
+#effective_cache_size = 4GB
+
+# - Genetic Query Optimizer -
+
+#geqo = on
+#geqo_threshold = 12
+#geqo_effort = 5 # range 1-10
+#geqo_pool_size = 0 # selects default based on effort
+#geqo_generations = 0 # selects default based on effort
+#geqo_selection_bias = 2.0 # range 1.5-2.0
+#geqo_seed = 0.0 # range 0.0-1.0
+
+# - Other Planner Options -
+
+#default_statistics_target = 100 # range 1-10000
+#constraint_exclusion = partition # on, off, or partition
+#cursor_tuple_fraction = 0.1 # range 0.0-1.0
+#from_collapse_limit = 8
+#join_collapse_limit = 8 # 1 disables collapsing of explicit
+ # JOIN clauses
+#force_parallel_mode = off
+
+
+#------------------------------------------------------------------------------
+# ERROR REPORTING AND LOGGING
+#------------------------------------------------------------------------------
+
+# - Where to Log -
+
+log_destination = 'stderr' # Valid values are combinations of
+ # stderr, csvlog, syslog, and eventlog,
+ # depending on platform. csvlog
+ # requires logging_collector to be on.
+
+# This is used when logging to stderr:
+logging_collector = on # Enable capturing of stderr and csvlog
+ # into log files. Required to be on for
+ # csvlogs.
+ # (change requires restart)
+
+# These are only used if logging_collector is on:
+log_directory = 'pg_log' # directory where log files are written,
+ # can be absolute or relative to PGDATA
+log_filename = 'postgresql-%a.log' # log file name pattern,
+ # can include strftime() escapes
+#log_file_mode = 0600 # creation mode for log files,
+ # begin with 0 to use octal notation
+log_truncate_on_rotation = on # If on, an existing log file with the
+ # same name as the new log file will be
+ # truncated rather than appended to.
+ # But such truncation only occurs on
+ # time-driven rotation, not on restarts
+ # or size-driven rotation. Default is
+ # off, meaning append to existing files
+ # in all cases.
+log_rotation_age = 1d # Automatic rotation of logfiles will
+ # happen after that time. 0 disables.
+log_rotation_size = 0 # Automatic rotation of logfiles will
+ # happen after that much log output.
+ # 0 disables.
+
+# These are relevant when logging to syslog:
+#syslog_facility = 'LOCAL0'
+#syslog_ident = 'postgres'
+#syslog_sequence_numbers = on
+#syslog_split_messages = on
+
+# This is only relevant when logging to eventlog (win32):
+#event_source = 'PostgreSQL'
+
+# - When to Log -
+
+#client_min_messages = notice # values in order of decreasing detail:
+ # debug5
+ # debug4
+ # debug3
+ # debug2
+ # debug1
+ # log
+ # notice
+ # warning
+ # error
+
+#log_min_messages = warning # values in order of decreasing detail:
+ # debug5
+ # debug4
+ # debug3
+ # debug2
+ # debug1
+ # info
+ # notice
+ # warning
+ # error
+ # log
+ # fatal
+ # panic
+
+#log_min_error_statement = error # values in order of decreasing detail:
+ # debug5
+ # debug4
+ # debug3
+ # debug2
+ # debug1
+ # info
+ # notice
+ # warning
+ # error
+ # log
+ # fatal
+ # panic (effectively off)
+
+#log_min_duration_statement = -1 # -1 is disabled, 0 logs all statements
+ # and their durations, > 0 logs only
+ # statements running at least this number
+ # of milliseconds
+
+
+# - What to Log -
+
+#debug_print_parse = off
+#debug_print_rewritten = off
+#debug_print_plan = off
+#debug_pretty_print = on
+#log_checkpoints = off
+#log_connections = off
+#log_disconnections = off
+#log_duration = off
+#log_error_verbosity = default # terse, default, or verbose messages
+#log_hostname = off
+log_line_prefix = '< %m > ' # special values:
+ # %a = application name
+ # %u = user name
+ # %d = database name
+ # %r = remote host and port
+ # %h = remote host
+ # %p = process ID
+ # %t = timestamp without milliseconds
+ # %m = timestamp with milliseconds
+ # %n = timestamp with milliseconds (as a Unix epoch)
+ # %i = command tag
+ # %e = SQL state
+ # %c = session ID
+ # %l = session line number
+ # %s = session start timestamp
+ # %v = virtual transaction ID
+ # %x = transaction ID (0 if none)
+ # %q = stop here in non-session
+ # processes
+ # %% = '%'
+ # e.g. '<%u%%%d> '
+#log_lock_waits = off # log lock waits >= deadlock_timeout
+#log_statement = 'none' # none, ddl, mod, all
+#log_replication_commands = off
+#log_temp_files = -1 # log temporary files equal or larger
+ # than the specified size in kilobytes;
+ # -1 disables, 0 logs all temp files
+log_timezone = 'UTC'
+
+
+# - Process Title -
+
+#cluster_name = '' # added to process titles if nonempty
+ # (change requires restart)
+#update_process_title = on
+
+
+#------------------------------------------------------------------------------
+# RUNTIME STATISTICS
+#------------------------------------------------------------------------------
+
+# - Query/Index Statistics Collector -
+
+#track_activities = on
+#track_counts = on
+#track_io_timing = off
+#track_functions = none # none, pl, all
+#track_activity_query_size = 1024 # (change requires restart)
+#stats_temp_directory = 'pg_stat_tmp'
+
+
+# - Statistics Monitoring -
+
+#log_parser_stats = off
+#log_planner_stats = off
+#log_executor_stats = off
+#log_statement_stats = off
+
+
+#------------------------------------------------------------------------------
+# AUTOVACUUM PARAMETERS
+#------------------------------------------------------------------------------
+
+#autovacuum = on # Enable autovacuum subprocess? 'on'
+ # requires track_counts to also be on.
+#log_autovacuum_min_duration = -1 # -1 disables, 0 logs all actions and
+ # their durations, > 0 logs only
+ # actions running at least this number
+ # of milliseconds.
+#autovacuum_max_workers = 3 # max number of autovacuum subprocesses
+ # (change requires restart)
+#autovacuum_naptime = 1min # time between autovacuum runs
+#autovacuum_vacuum_threshold = 50 # min number of row updates before
+ # vacuum
+#autovacuum_analyze_threshold = 50 # min number of row updates before
+ # analyze
+#autovacuum_vacuum_scale_factor = 0.2 # fraction of table size before vacuum
+#autovacuum_analyze_scale_factor = 0.1 # fraction of table size before analyze
+#autovacuum_freeze_max_age = 200000000 # maximum XID age before forced vacuum
+ # (change requires restart)
+#autovacuum_multixact_freeze_max_age = 400000000 # maximum multixact age
+ # before forced vacuum
+ # (change requires restart)
+#autovacuum_vacuum_cost_delay = 20ms # default vacuum cost delay for
+ # autovacuum, in milliseconds;
+ # -1 means use vacuum_cost_delay
+#autovacuum_vacuum_cost_limit = -1 # default vacuum cost limit for
+ # autovacuum, -1 means use
+ # vacuum_cost_limit
+
+
+#------------------------------------------------------------------------------
+# CLIENT CONNECTION DEFAULTS
+#------------------------------------------------------------------------------
+
+# - Statement Behavior -
+
+#search_path = '"$user", public' # schema names
+#default_tablespace = '' # a tablespace name, '' uses the default
+#temp_tablespaces = '' # a list of tablespace names, '' uses
+ # only default tablespace
+#check_function_bodies = on
+#default_transaction_isolation = 'read committed'
+#default_transaction_read_only = off
+#default_transaction_deferrable = off
+#session_replication_role = 'origin'
+#statement_timeout = 0 # in milliseconds, 0 is disabled
+#lock_timeout = 0 # in milliseconds, 0 is disabled
+#idle_in_transaction_session_timeout = 0 # in milliseconds, 0 is disabled
+#vacuum_freeze_min_age = 50000000
+#vacuum_freeze_table_age = 150000000
+#vacuum_multixact_freeze_min_age = 5000000
+#vacuum_multixact_freeze_table_age = 150000000
+#bytea_output = 'hex' # hex, escape
+#xmlbinary = 'base64'
+#xmloption = 'content'
+#gin_fuzzy_search_limit = 0
+#gin_pending_list_limit = 4MB
+
+# - Locale and Formatting -
+
+datestyle = 'iso, mdy'
+#intervalstyle = 'postgres'
+timezone = 'UTC'
+#timezone_abbreviations = 'Default' # Select the set of available time zone
+ # abbreviations. Currently, there are
+ # Default
+ # Australia (historical usage)
+ # India
+ # You can create your own file in
+ # share/timezonesets/.
+#extra_float_digits = 0 # min -15, max 3
+#client_encoding = sql_ascii # actually, defaults to database
+ # encoding
+
+# These settings are initialized by initdb, but they can be changed.
+lc_messages = 'en_US.UTF-8' # locale for system error message
+ # strings
+lc_monetary = 'en_US.UTF-8' # locale for monetary formatting
+lc_numeric = 'en_US.UTF-8' # locale for number formatting
+lc_time = 'en_US.UTF-8' # locale for time formatting
+
+# default configuration for text search
+default_text_search_config = 'pg_catalog.english'
+
+# - Other Defaults -
+
+#dynamic_library_path = '$libdir'
+#local_preload_libraries = ''
+#session_preload_libraries = ''
+
+
+#------------------------------------------------------------------------------
+# LOCK MANAGEMENT
+#------------------------------------------------------------------------------
+
+#deadlock_timeout = 1s
+#max_locks_per_transaction = 64 # min 10
+ # (change requires restart)
+#max_pred_locks_per_transaction = 64 # min 10
+ # (change requires restart)
+
+
+#------------------------------------------------------------------------------
+# VERSION/PLATFORM COMPATIBILITY
+#------------------------------------------------------------------------------
+
+# - Previous PostgreSQL Versions -
+
+#array_nulls = on
+#backslash_quote = safe_encoding # on, off, or safe_encoding
+#default_with_oids = off
+#escape_string_warning = on
+#lo_compat_privileges = off
+#operator_precedence_warning = off
+#quote_all_identifiers = off
+#sql_inheritance = on
+#standard_conforming_strings = on
+#synchronize_seqscans = on
+
+# - Other Platforms and Clients -
+
+#transform_null_equals = off
+
+
+#------------------------------------------------------------------------------
+# ERROR HANDLING
+#------------------------------------------------------------------------------
+
+#exit_on_error = off # terminate session on any error?
+#restart_after_crash = on # reinitialize after backend crash?
+
+
+#------------------------------------------------------------------------------
+# CONFIG FILE INCLUDES
+#------------------------------------------------------------------------------
+
+# These options allow settings to be loaded from files other than the
+# default postgresql.conf.
+
+#include_dir = 'conf.d' # include files ending in '.conf' from
+ # directory 'conf.d'
+#include_if_exists = 'exists.conf' # include file only if it exists
+#include = 'special.conf' # include file
+
+
+#------------------------------------------------------------------------------
+# CUSTOMIZED OPTIONS
+#------------------------------------------------------------------------------
+
+# Add settings for extensions here
+
+
+#------------------------------------------------------------------------------
+# ANSIBLE OPTIONS
+#------------------------------------------------------------------------------
+
+listen_addresses = {{ postgresql_server_listen }}
+
+{% for option in (postgresql_server_conf | sort) %}
+{{ option }} = {{ postgresql_server_conf[option] }}
+{% endfor %}
diff --git a/omero/roles/ome.postgresql_client/vars/main.yml b/omero/roles/ome.postgresql_client/vars/main.yml
new file mode 100644
index 00000000..573edb16
--- /dev/null
+++ b/omero/roles/ome.postgresql_client/vars/main.yml
@@ -0,0 +1,26 @@
+---
+# vars file for ome.postgresql_client
+
+# DEPRECATED
+# This variable includes information used by the postgresql (server) role
+postgresql_distribution_redhat:
+ "12":
+ repo: pgdg12
+ basename: postgresql12
+ setup: postgresql-12-setup
+ "13":
+ repo: pgdg13
+ basename: postgresql13
+ setup: postgresql-13-setup
+ "14":
+ repo: pgdg14
+ basename: postgresql14
+ setup: postgresql-14-setup
+ "15":
+ repo: pgdg15
+ basename: postgresql15
+ setup: postgresql-15-setup
+ "16":
+ repo: pgdg16
+ basename: postgresql16
+ setup: postgresql-16-setup
diff --git a/omero/roles/ome.python3_virtualenv/.github/workflows/molecule.yml b/omero/roles/ome.python3_virtualenv/.github/workflows/molecule.yml
new file mode 100644
index 00000000..89f3b866
--- /dev/null
+++ b/omero/roles/ome.python3_virtualenv/.github/workflows/molecule.yml
@@ -0,0 +1,55 @@
+---
+name: Molecule
+# yamllint disable-line rule:truthy
+on:
+ push:
+ pull_request:
+ schedule:
+ - cron: '29 21 * * 0'
+
+jobs:
+
+ list-scenarios:
+ runs-on: ubuntu-latest
+ outputs:
+ matrix: ${{ steps.listscenarios.outputs.scenarios }}
+ steps:
+ - uses: actions/checkout@v4
+ - id: listscenarios
+ uses: ome/action-ansible-molecule-list-scenarios@main
+
+ test:
+ name: Test
+ needs:
+ - list-scenarios
+ runs-on: ubuntu-22.04
+ strategy:
+ # Keep running so we can see if other tests pass
+ fail-fast: false
+ matrix:
+ scenario: ${{fromJson(needs.list-scenarios.outputs.matrix)}}
+ steps:
+ - uses: actions/checkout@v4
+ - uses: actions/setup-python@v4
+ with:
+ python-version: '3.8'
+ - name: Install Ansible & Molecule
+ run: |
+ pip install "ansible<8" "ansible-lint<6.13" flake8
+ pip install "molecule<5" "ansible-compat<4"
+ pip install molecule-plugins[docker] pytest-testinfra
+ - name: Run molecule
+ run: molecule test -s "${{ matrix.scenario }}"
+
+ publish:
+ name: Galaxy
+ if: startsWith(github.ref, 'refs/tags')
+ needs:
+ - test
+ runs-on: ubuntu-22.04
+ steps:
+ - name: galaxy
+ uses: robertdebock/galaxy-action@1.2.1
+ with:
+ galaxy_api_key: ${{ secrets.GALAXY_API_KEY }}
+ git_branch: ${{ github.ref_name }}
diff --git a/omero/roles/ome.python3_virtualenv/.gitignore b/omero/roles/ome.python3_virtualenv/.gitignore
new file mode 100644
index 00000000..0bf56261
--- /dev/null
+++ b/omero/roles/ome.python3_virtualenv/.gitignore
@@ -0,0 +1,2 @@
+.*~
+__pycache__
diff --git a/omero/roles/ome.python3_virtualenv/LICENSE.md b/omero/roles/ome.python3_virtualenv/LICENSE.md
new file mode 100644
index 00000000..a3181283
--- /dev/null
+++ b/omero/roles/ome.python3_virtualenv/LICENSE.md
@@ -0,0 +1,22 @@
+Copyright (c) 2019, University of Dundee
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/omero/roles/ome.python3_virtualenv/README.md b/omero/roles/ome.python3_virtualenv/README.md
new file mode 100644
index 00000000..6ce05dfc
--- /dev/null
+++ b/omero/roles/ome.python3_virtualenv/README.md
@@ -0,0 +1,24 @@
+Python3 Virtualenv
+==================
+
+[![Actions Status](https://github.com/ome/ansible-role-python3-virtualenv/workflows/Molecule/badge.svg)](https://github.com/ome/ansible-role-python3-virtualenv/actions)
+[![Ansible Role](https://img.shields.io/badge/ansible--galaxy-python3_virtualenv-blue.svg)](https://galaxy.ansible.com/ui/standalone/roles/ome/python3_virtualenv/)
+
+Install Python3 virtualenv dependencies and a wrapper script for the Ansible `pip` module.
+
+There are multiple ways of creating Python virtualenvs including `virtualenv`, `python3 -mvenv`, but these may take different parameters.
+In some situations, particularly if `ansible_python_interpreter` is set, the Ansible `pip` modules pass unrecognised parameters.
+This role installs a wrapper script `/usr/local/bin/ome-python3-virtualenv` should work in all cases.
+
+
+The compatibility with Python 2 has been removed in version 0.2.0.
+
+Author Information
+------------------
+
+ome-devel@lists.openmicroscopy.org.uk
+
+License
+-------
+
+BSD
diff --git a/omero/roles/ome.python3_virtualenv/files/ome-python3-virtualenv.sh b/omero/roles/ome.python3_virtualenv/files/ome-python3-virtualenv.sh
new file mode 100644
index 00000000..3170f7d2
--- /dev/null
+++ b/omero/roles/ome.python3_virtualenv/files/ome-python3-virtualenv.sh
@@ -0,0 +1,19 @@
+#!/usr/bin/env bash
+# There are multiple ways of creating Python virtualenvs including
+# virtualenv, python3 -mvenv, but these may take different parameters.
+# In some situations, particularly if ansible_python_interpreter is set,
+# the Ansible pip modules passes unrecognised parameters.
+# This wrapper script should work in all cases.
+
+set -eu
+
+for arg in "$@"; do
+ # Ignore -p argument
+ if [[ $arg = -p ]]; then
+ shift
+ shift
+ elif [[ "$arg" = -p* ]]; then
+ shift
+ fi
+done
+python3 -mvenv "$@"
diff --git a/omero/roles/ome.python3_virtualenv/meta/.galaxy_install_info b/omero/roles/ome.python3_virtualenv/meta/.galaxy_install_info
new file mode 100644
index 00000000..603cf8ec
--- /dev/null
+++ b/omero/roles/ome.python3_virtualenv/meta/.galaxy_install_info
@@ -0,0 +1,2 @@
+install_date: Wed 13 Nov 17:33:43 2024
+version: 0.2.0
diff --git a/omero/roles/ome.python3_virtualenv/meta/main.yml b/omero/roles/ome.python3_virtualenv/meta/main.yml
new file mode 100644
index 00000000..4c979ff2
--- /dev/null
+++ b/omero/roles/ome.python3_virtualenv/meta/main.yml
@@ -0,0 +1,19 @@
+---
+galaxy_info:
+ role_name: python3_virtualenv
+ author: ome-devel@lists.openmicroscopy.org.uk
+ description: Install Python3 virtualenv dependencies
+ company: Open Microscopy Environment
+ license: BSD
+ min_ansible_version: 2.6
+ platforms:
+ - name: EL
+ versions:
+ - 9
+ - name: Ubuntu
+ versions:
+ - jammy
+ namespace: ome
+ galaxy_tags:
+ - system
+ - python
diff --git a/omero/roles/ome.python3_virtualenv/molecule/default/Dockerfile.j2 b/omero/roles/ome.python3_virtualenv/molecule/default/Dockerfile.j2
new file mode 100644
index 00000000..53cd6824
--- /dev/null
+++ b/omero/roles/ome.python3_virtualenv/molecule/default/Dockerfile.j2
@@ -0,0 +1,12 @@
+# Molecule managed
+
+{% if item.registry is defined %}
+FROM {{ item.registry.url }}/{{ item.image }}
+{% else %}
+FROM {{ item.image }}
+{% endif %}
+
+RUN if [ $(command -v apt-get) ]; then apt-get update && apt-get install -y python3 sudo bash ca-certificates && apt-get clean; \
+ elif [ $(command -v dnf) ]; then dnf makecache && dnf --assumeyes install python3 sudo bash && dnf clean all; \
+ elif [ $(command -v yum) ]; then yum makecache fast && yum install -y python3 sudo yum-plugin-ovl bash && sed -i 's/plugins=0/plugins=1/g' /etc/yum.conf && yum clean all; \
+ fi
diff --git a/omero/roles/ome.python3_virtualenv/molecule/default/molecule.yml b/omero/roles/ome.python3_virtualenv/molecule/default/molecule.yml
new file mode 100644
index 00000000..b2dfeb09
--- /dev/null
+++ b/omero/roles/ome.python3_virtualenv/molecule/default/molecule.yml
@@ -0,0 +1,35 @@
+---
+dependency:
+ name: galaxy
+driver:
+ name: docker
+lint: |
+ yamllint .
+ ansible-lint
+ flake8
+platforms:
+ - name: rockylinux-9
+ image: rockylinux:9
+ groups:
+ - py3
+ - name: ubuntu-2204
+ image: ubuntu:22.04
+ groups:
+ - py3
+
+provisioner:
+ name: ansible
+ playbooks:
+ converge: ../resources/playbook.yml
+ lint:
+ name: ansible-lint
+ inventory:
+ group_vars:
+ py3:
+ ansible_python_interpreter: python3
+
+scenario:
+ name: default
+verifier:
+ name: testinfra
+ directory: ../resources/tests/
diff --git a/omero/roles/ome.python3_virtualenv/molecule/resources/playbook.yml b/omero/roles/ome.python3_virtualenv/molecule/resources/playbook.yml
new file mode 100644
index 00000000..89468203
--- /dev/null
+++ b/omero/roles/ome.python3_virtualenv/molecule/resources/playbook.yml
@@ -0,0 +1,24 @@
+---
+- name: Converge
+ hosts: all
+ roles:
+ - role: ome.python3_virtualenv
+
+ tasks:
+ - name: test | upgrade pip
+ become: true
+ pip:
+ name: "pip>=21"
+ state: present
+ virtualenv: /opt/test/python3-virtualenv
+ virtualenv_command: /usr/local/bin/ome-python3-virtualenv
+
+ - name: test | install packages
+ become: true
+ pip:
+ name:
+ - omego
+ - scc
+ state: present
+ virtualenv: /opt/test/python3-virtualenv
+ virtualenv_command: /usr/local/bin/ome-python3-virtualenv
diff --git a/omero/roles/ome.python3_virtualenv/molecule/resources/tests/test_default.py b/omero/roles/ome.python3_virtualenv/molecule/resources/tests/test_default.py
new file mode 100644
index 00000000..6f4d7be7
--- /dev/null
+++ b/omero/roles/ome.python3_virtualenv/molecule/resources/tests/test_default.py
@@ -0,0 +1,18 @@
+import os
+import testinfra.utils.ansible_runner
+
+testinfra_hosts = testinfra.utils.ansible_runner.AnsibleRunner(
+ os.environ['MOLECULE_INVENTORY_FILE']).get_hosts('all')
+
+
+def test_virtualenv(host):
+ venv = '/opt/test/python3-virtualenv'
+ host.check_output(venv + '/bin/python --version').startswith('Python 3.')
+ host.check_output(venv + '/bin/pip --version').startswith('Python 3.')
+ assert host.file(venv + '/bin/python').is_file
+ assert host.file(venv + '/bin/python3').is_file
+ assert host.file(venv + '/bin/pip').is_file
+ assert host.file(venv + '/bin/pip3').is_file
+ packages = host.check_output(venv + '/bin/pip freeze')
+ assert 'omego==' in packages
+ assert 'scc==' in packages
diff --git a/omero/roles/ome.python3_virtualenv/tasks/debian.yml b/omero/roles/ome.python3_virtualenv/tasks/debian.yml
new file mode 100644
index 00000000..10419dc0
--- /dev/null
+++ b/omero/roles/ome.python3_virtualenv/tasks/debian.yml
@@ -0,0 +1,13 @@
+---
+
+- name: python3 venv | install python3
+ become: true
+ apt:
+ update_cache: true
+ name:
+ - python3
+ - python3-distutils
+ - python3-venv
+ - python3-pip
+ - python3-setuptools
+ state: present
diff --git a/omero/roles/ome.python3_virtualenv/tasks/main.yml b/omero/roles/ome.python3_virtualenv/tasks/main.yml
new file mode 100644
index 00000000..20bb7b2f
--- /dev/null
+++ b/omero/roles/ome.python3_virtualenv/tasks/main.yml
@@ -0,0 +1,14 @@
+---
+
+- import_tasks: redhat.yml
+ when: ansible_os_family | lower == 'redhat'
+
+- import_tasks: debian.yml
+ when: ansible_os_family | lower == 'debian'
+
+- name: python3 venv | create python virtualenv wrapper
+ become: true
+ copy:
+ dest: /usr/local/bin/ome-python3-virtualenv
+ src: ome-python3-virtualenv.sh
+ mode: 'u=rwx,g=rx,o=rx'
diff --git a/omero/roles/ome.python3_virtualenv/tasks/redhat.yml b/omero/roles/ome.python3_virtualenv/tasks/redhat.yml
new file mode 100644
index 00000000..bd5760c0
--- /dev/null
+++ b/omero/roles/ome.python3_virtualenv/tasks/redhat.yml
@@ -0,0 +1,11 @@
+---
+
+- name: python3 venv | install packages
+ become: true
+ ansible.builtin.dnf:
+ update_cache: true
+ name:
+ - python3
+ - python3-pip
+ - python3-setuptools
+ state: present
diff --git a/omero/roles/ome.redis/.github/workflows/molecule.yml b/omero/roles/ome.redis/.github/workflows/molecule.yml
new file mode 100644
index 00000000..af6569b9
--- /dev/null
+++ b/omero/roles/ome.redis/.github/workflows/molecule.yml
@@ -0,0 +1,55 @@
+---
+name: Molecule
+# yamllint disable-line rule:truthy
+on:
+ push:
+ pull_request:
+ schedule:
+ - cron: '29 21 * * 0'
+
+jobs:
+
+ list-scenarios:
+ runs-on: ubuntu-22.04
+ outputs:
+ matrix: ${{ steps.listscenarios.outputs.scenarios }}
+ steps:
+ - uses: actions/checkout@v4
+ - id: listscenarios
+ uses: ome/action-ansible-molecule-list-scenarios@main
+
+ test:
+ name: Test
+ needs:
+ - list-scenarios
+ runs-on: ubuntu-22.04
+ strategy:
+ # Keep running so we can see if other tests pass
+ fail-fast: false
+ matrix:
+ scenario: ${{fromJson(needs.list-scenarios.outputs.matrix)}}
+ steps:
+ - uses: actions/checkout@v4
+ - uses: actions/setup-python@v4
+ with:
+ python-version: '3.9'
+ - name: Install Ansible & Molecule
+ run: |
+ pip install "ansible<8" "ansible-lint<6.13" flake8
+ pip install "molecule<5" "ansible-compat<4"
+ pip install molecule-plugins[docker] pytest-testinfra
+ - name: Run molecule
+ run: molecule test -s "${{ matrix.scenario }}"
+
+ publish:
+ name: Galaxy
+ if: startsWith(github.ref, 'refs/tags')
+ needs:
+ - test
+ runs-on: ubuntu-22.04
+ steps:
+ - name: galaxy
+ uses: ansible-actions/ansible-galaxy-action@v1.2.0
+ with:
+ galaxy_api_key: ${{ secrets.GALAXY_API_KEY }}
+ galaxy_version: ${{ github.ref_name }}
diff --git a/omero/roles/ome.redis/.gitignore b/omero/roles/ome.redis/.gitignore
new file mode 100644
index 00000000..b6261c93
--- /dev/null
+++ b/omero/roles/ome.redis/.gitignore
@@ -0,0 +1 @@
+.*~
diff --git a/omero/roles/ome.redis/LICENSE.md b/omero/roles/ome.redis/LICENSE.md
new file mode 100644
index 00000000..a3181283
--- /dev/null
+++ b/omero/roles/ome.redis/LICENSE.md
@@ -0,0 +1,22 @@
+Copyright (c) 2019, University of Dundee
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/omero/roles/ome.redis/README.md b/omero/roles/ome.redis/README.md
new file mode 100644
index 00000000..80f3d0b3
--- /dev/null
+++ b/omero/roles/ome.redis/README.md
@@ -0,0 +1,20 @@
+Redis
+=====
+
+[![Actions Status](https://github.com/ome/ansible-role-redis/workflows/Molecule/badge.svg)](https://github.com/ome/ansible-role-redis/actions)
+[![Ansible Role](https://img.shields.io/badge/ansible--galaxy-redis-blue.svg)](https://galaxy.ansible.com/ui/standalone/roles/ome/redis/)
+
+Install Redis.
+
+
+Role Variables
+--------------
+
+All variables are optional.
+- `redis_listen`: String containing interfaces to bind to, default `127.0.0.1`
+
+
+Author Information
+------------------
+
+ome-devel@lists.openmicroscopy.org.uk
diff --git a/omero/roles/ome.redis/handlers/main.yml b/omero/roles/ome.redis/handlers/main.yml
new file mode 100644
index 00000000..a6389d66
--- /dev/null
+++ b/omero/roles/ome.redis/handlers/main.yml
@@ -0,0 +1,8 @@
+---
+# Handlers for redis
+
+- name: restart redis
+ become: true
+ service:
+ name: redis
+ state: restarted
diff --git a/omero/roles/ome.redis/meta/.galaxy_install_info b/omero/roles/ome.redis/meta/.galaxy_install_info
new file mode 100644
index 00000000..11d88fb9
--- /dev/null
+++ b/omero/roles/ome.redis/meta/.galaxy_install_info
@@ -0,0 +1,2 @@
+install_date: Wed 13 Nov 17:33:52 2024
+version: 1.3.0
diff --git a/omero/roles/ome.redis/meta/main.yml b/omero/roles/ome.redis/meta/main.yml
new file mode 100644
index 00000000..7e9fa42a
--- /dev/null
+++ b/omero/roles/ome.redis/meta/main.yml
@@ -0,0 +1,19 @@
+---
+galaxy_info:
+ author: ome-devel@lists.openmicroscopy.org.uk
+ description: Install Redis
+ company: Open Microscopy Environment
+ license: BSD
+ min_ansible_version: 2.10
+ platforms:
+ - name: EL
+ versions:
+ - 9
+ - name: Ubuntu
+ versions:
+ - jammy
+ namespace: ome
+ role_name: redis
+ galaxy_tags: []
+
+dependencies: []
diff --git a/omero/roles/ome.redis/molecule/default/molecule.yml b/omero/roles/ome.redis/molecule/default/molecule.yml
new file mode 100644
index 00000000..59be675b
--- /dev/null
+++ b/omero/roles/ome.redis/molecule/default/molecule.yml
@@ -0,0 +1,36 @@
+---
+dependency:
+ name: galaxy
+driver:
+ name: docker
+lint: |
+ yamllint .
+ ansible-lint
+ flake8
+platforms:
+ - name: rockylinux-9
+ image: eniocarboni/docker-rockylinux-systemd:9
+ command: /sbin/init
+ privileged: true
+ cgroupns_mode: host
+ tmpfs:
+ - /sys/fs/cgroup
+ groups:
+ - extra_options
+ - name: ubuntu-2204
+ image: eniocarboni/docker-ubuntu-systemd:22.04
+ command: /sbin/init
+ privileged: true
+ cgroupns_mode: host
+ tmpfs:
+ - /sys/fs/cgroup
+ groups:
+ - extra_options
+provisioner:
+ name: ansible
+ lint:
+ name: ansible-lint
+scenario:
+ name: default
+verifier:
+ name: testinfra
diff --git a/omero/roles/ome.redis/molecule/default/playbook.yml b/omero/roles/ome.redis/molecule/default/playbook.yml
new file mode 100644
index 00000000..2f3b85b1
--- /dev/null
+++ b/omero/roles/ome.redis/molecule/default/playbook.yml
@@ -0,0 +1,6 @@
+---
+- name: Converge
+ hosts: all
+ roles:
+ - role: ome.redis
+ redis_listen: 0.0.0.0
diff --git a/omero/roles/ome.redis/molecule/default/tests/test_default.py b/omero/roles/ome.redis/molecule/default/tests/test_default.py
new file mode 100644
index 00000000..b7aa5149
--- /dev/null
+++ b/omero/roles/ome.redis/molecule/default/tests/test_default.py
@@ -0,0 +1,15 @@
+import os
+
+import testinfra.utils.ansible_runner
+
+testinfra_hosts = testinfra.utils.ansible_runner.AnsibleRunner(
+ os.environ['MOLECULE_INVENTORY_FILE']).get_hosts('all')
+
+
+def test_service_running_and_enabled(host):
+ assert host.service('redis').is_running
+ assert host.service('redis').is_enabled
+
+
+def test_redis_config(host):
+ assert host.file('/etc/redis/redis.conf').contains('bind 0.0.0.0')
diff --git a/omero/roles/ome.redis/tasks/main.yml b/omero/roles/ome.redis/tasks/main.yml
new file mode 100644
index 00000000..b8725b7e
--- /dev/null
+++ b/omero/roles/ome.redis/tasks/main.yml
@@ -0,0 +1,51 @@
+---
+# tasks file for roles/redis
+
+- name: Import a key for epel
+ ansible.builtin.rpm_key:
+ state: present
+ key: https://dl.fedoraproject.org/pub/epel/RPM-GPG-KEY-EPEL-9
+ when: ansible_os_family == 'RedHat'
+
+- name: Setup dnf repository, epel
+ become: true
+ ansible.builtin.dnf:
+ update_cache: true
+ name:
+ https://dl.fedoraproject.org/pub/epel/epel-release-latest-9.noarch.rpm
+ state: present
+ when: ansible_os_family == 'RedHat'
+
+- name: install redis
+ become: true
+ ansible.builtin.dnf:
+ name: redis
+ state: present
+ when: ansible_os_family == 'RedHat'
+
+- name: Install redis
+ become: true
+ ansible.builtin.apt:
+ update_cache: true
+ name: redis
+ state: present
+ when: ansible_os_family == 'Debian'
+
+# Note: Fails in --check mode with /etc/redis/redis.conf
+# does not exist
+- name: configure redis for RHEL
+ become: true
+ lineinfile:
+ dest: /etc/redis/redis.conf
+ line: "bind {{ redis_listen | default('127.0.0.1') }}"
+ regexp: "^bind\\s.*"
+ state: present
+ notify:
+ - restart redis
+
+- name: set redis to start on startup
+ become: true
+ service:
+ name: redis
+ state: started
+ enabled: true
diff --git a/omero/roles/ome.selinux_utils/.github/workflows/molecule.yml b/omero/roles/ome.selinux_utils/.github/workflows/molecule.yml
new file mode 100644
index 00000000..543b1e7f
--- /dev/null
+++ b/omero/roles/ome.selinux_utils/.github/workflows/molecule.yml
@@ -0,0 +1,62 @@
+---
+name: Molecule
+# yamllint disable-line rule:truthy
+on:
+ push:
+ pull_request:
+ schedule:
+ - cron: '29 21 * * 0'
+
+jobs:
+
+ list-scenarios:
+ runs-on: ubuntu-22.04
+ outputs:
+ matrix: ${{ steps.listscenarios.outputs.scenarios }}
+ steps:
+ - uses: actions/checkout@v4
+ - id: listscenarios
+ uses: ome/action-ansible-molecule-list-scenarios@main
+
+ test:
+ name: Test
+ needs:
+ - list-scenarios
+ runs-on: ubuntu-22.04
+ strategy:
+ # Keep running so we can see if other tests pass
+ fail-fast: false
+ matrix:
+ scenario: ${{fromJson(needs.list-scenarios.outputs.matrix)}}
+ steps:
+ - uses: actions/checkout@v4
+ - uses: actions/setup-python@v4
+ with:
+ python-version: '3.9'
+ - name: Install Ansible & Molecule
+ run: |
+ pip install "ansible<8" "ansible-lint<6.13" flake8
+ pip install "molecule<5" "ansible-compat<4"
+ pip install molecule-plugins[docker] pytest-testinfra
+ - name: Run molecule
+ run: molecule test -s "${{ matrix.scenario }}"
+
+ publish:
+ name: Galaxy
+ if: startsWith(github.ref, 'refs/tags')
+ needs:
+ - test
+ runs-on: ubuntu-22.04
+ steps:
+ - uses: actions/checkout@v4
+ - name: Read the role name
+ id: role-name
+ run: |
+ name=$(grep 'role_name' meta/main.yml | sed -r 's/^[^:]*:(.*)$/\1/' | tr -d '[:space:]') # noqa
+ echo "rolename=$name" >> "$GITHUB_OUTPUT"
+ - name: Publish to Galaxy
+ uses: ome/action-ansible-galaxy-publish@main
+ with:
+ galaxy-api-key: ${{ secrets.GALAXY_API_KEY }}
+ galaxy-version: ${{ github.ref_name }}
+ role-name: ${{ steps.role-name.outputs.rolename }}
diff --git a/omero/roles/ome.selinux_utils/.gitignore b/omero/roles/ome.selinux_utils/.gitignore
new file mode 100644
index 00000000..b6261c93
--- /dev/null
+++ b/omero/roles/ome.selinux_utils/.gitignore
@@ -0,0 +1 @@
+.*~
diff --git a/omero/roles/ome.selinux_utils/LICENSE.md b/omero/roles/ome.selinux_utils/LICENSE.md
new file mode 100644
index 00000000..a3181283
--- /dev/null
+++ b/omero/roles/ome.selinux_utils/LICENSE.md
@@ -0,0 +1,22 @@
+Copyright (c) 2019, University of Dundee
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/omero/roles/ome.selinux_utils/README.md b/omero/roles/ome.selinux_utils/README.md
new file mode 100644
index 00000000..430df72c
--- /dev/null
+++ b/omero/roles/ome.selinux_utils/README.md
@@ -0,0 +1,32 @@
+SELinux Utils
+=============
+
+[![Actions Status](https://github.com/ome/ansible-role-selinux-utils/workflows/Molecule/badge.svg)](https://github.com/ome/ansible-role-selinux-utils/actions)
+[![Ansible Role](https://img.shields.io/badge/ansible--galaxy-selinux_utils-blue.svg)](https://galaxy.ansible.com/ui/standalone/roles/ome/selinux_utils/)
+
+Sets a host variable indicating whether SELinux is enabled or not.
+Installs utilities for interacting with SELinux if it is.
+
+These utilities may be required by some Ansible modules when SELinux is enabled, and are not always present in CentOS 7 base images.
+
+This role will set the host variable `selinux_enabled: {True,False}` which can be used in later roles.
+
+Ideally this role should be included as a dependency in `meta/main.yml` of any roles that need to know whether SELinux is enabled.
+
+
+Example Playbook
+----------------
+
+ - hosts: localhost
+ roles:
+ - ome.selinux_utils
+ tasks:
+ debug:
+ msg: "SELinux is enabled or permissive"
+ when: selinux_enabled
+
+
+Author Information
+------------------
+
+ome-devel@lists.openmicroscopy.org.uk
diff --git a/omero/roles/ome.selinux_utils/meta/.galaxy_install_info b/omero/roles/ome.selinux_utils/meta/.galaxy_install_info
new file mode 100644
index 00000000..6e9df54f
--- /dev/null
+++ b/omero/roles/ome.selinux_utils/meta/.galaxy_install_info
@@ -0,0 +1,2 @@
+install_date: Wed 13 Nov 17:33:53 2024
+version: 2.1.1
diff --git a/omero/roles/ome.selinux_utils/meta/main.yml b/omero/roles/ome.selinux_utils/meta/main.yml
new file mode 100644
index 00000000..9a124c15
--- /dev/null
+++ b/omero/roles/ome.selinux_utils/meta/main.yml
@@ -0,0 +1,16 @@
+---
+galaxy_info:
+ author: ome-devel@lists.openmicroscopy.org.uk
+ description: Installs utilities and sets variable for interacting with SELinux
+ company: Open Microscopy Environment
+ license: BSD
+ min_ansible_version: 2.10
+ platforms:
+ - name: EL
+ versions:
+ - 9
+ namespace: ome
+ role_name: selinux_utils
+ galaxy_tags: ['selinux']
+
+dependencies: []
diff --git a/omero/roles/ome.selinux_utils/molecule/default/molecule.yml b/omero/roles/ome.selinux_utils/molecule/default/molecule.yml
new file mode 100644
index 00000000..2ed6d5d9
--- /dev/null
+++ b/omero/roles/ome.selinux_utils/molecule/default/molecule.yml
@@ -0,0 +1,25 @@
+---
+dependency:
+ name: galaxy
+driver:
+ name: docker
+lint: |
+ yamllint .
+ ansible-lint
+ flake8
+platforms:
+ - name: selinux-utils-docker
+ image: eniocarboni/docker-rockylinux-systemd:9
+ cgroupns_mode: host
+ tmpfs:
+ - /sys/fs/cgroup
+ groups:
+ - extra_options
+provisioner:
+ name: ansible
+ lint:
+ name: ansible-lint
+verifier:
+ name: testinfra
+
+# TODO: Add a vagrant test scenario
diff --git a/omero/roles/ome.selinux_utils/molecule/default/playbook.yml b/omero/roles/ome.selinux_utils/molecule/default/playbook.yml
new file mode 100644
index 00000000..6fd9b4ca
--- /dev/null
+++ b/omero/roles/ome.selinux_utils/molecule/default/playbook.yml
@@ -0,0 +1,5 @@
+---
+- name: Converge
+ hosts: all
+ roles:
+ - role: ome.selinux_utils
diff --git a/omero/roles/ome.selinux_utils/molecule/default/tests/test_default.py b/omero/roles/ome.selinux_utils/molecule/default/tests/test_default.py
new file mode 100644
index 00000000..96c8b36b
--- /dev/null
+++ b/omero/roles/ome.selinux_utils/molecule/default/tests/test_default.py
@@ -0,0 +1,22 @@
+import os
+import testinfra.utils.ansible_runner
+
+testinfra_hosts = testinfra.utils.ansible_runner.AnsibleRunner(
+ os.environ['MOLECULE_INVENTORY_FILE']
+).get_hosts('all')
+
+
+# The behaviour of this test depends on whether it's running with a docker
+# container or full VM
+def test_selinux_utils(host):
+ # We could do this by having separate test_files, but by keeping it
+ # in one we can guarantee we always match one of the test conditions
+ hostname = host.backend.get_hostname()
+
+ if hostname == 'selinux-utils-docker':
+ assert not host.exists('/usr/sbin/getenforce')
+ assert not host.package('policycoreutils-python').is_installed
+ else:
+ getenforce = host.check_output('/usr/sbin/getenforce')
+ assert getenforce == 'Enforcing'
+ assert host.package('policycoreutils-python').is_installed
diff --git a/omero/roles/ome.selinux_utils/tasks/main.yml b/omero/roles/ome.selinux_utils/tasks/main.yml
new file mode 100644
index 00000000..e69d682f
--- /dev/null
+++ b/omero/roles/ome.selinux_utils/tasks/main.yml
@@ -0,0 +1,27 @@
+---
+# tasks file for roles/selinux-utils
+
+- name: system packages | set selinux variable
+ set_fact:
+ selinux_enabled: "{{ ansible_facts.selinux.status == 'enabled' }}"
+
+- name : check rocky.repo file
+ stat:
+ path: /etc/yum.repos.d/rocky.repo
+ register: rockyrepo_name
+
+- name: system packages | Add CRB repository for RHEL
+ become: true
+ ansible.builtin.command:
+ subscription-manager repos --enable codeready-builder-for-rhel-9-x86_64-rpms
+ when: ansible_os_family == 'RedHat' and not rockyrepo_name.stat.exists
+
+- name: system packages | install selinux utilities
+ become: true
+ package:
+ name:
+ - libselinux-python3
+ - libsemanage-python3
+ - policycoreutils-python3
+ state: present
+ when: selinux_enabled
diff --git a/omero/roles/ome.ssl_certificate/.github/workflows/molecule.yml b/omero/roles/ome.ssl_certificate/.github/workflows/molecule.yml
new file mode 100644
index 00000000..79a2b20d
--- /dev/null
+++ b/omero/roles/ome.ssl_certificate/.github/workflows/molecule.yml
@@ -0,0 +1,63 @@
+---
+name: Molecule
+# yamllint disable-line rule:truthy
+on:
+ push:
+ pull_request:
+ schedule:
+ - cron: '29 21 * * 0'
+
+jobs:
+
+ list-scenarios:
+ runs-on: ubuntu-22.04
+ outputs:
+ matrix: ${{ steps.listscenarios.outputs.scenarios }}
+ steps:
+ - uses: actions/checkout@v4
+ - id: listscenarios
+ uses: ome/action-ansible-molecule-list-scenarios@main
+
+ test:
+ name: Test
+ needs:
+ - list-scenarios
+ runs-on: ubuntu-22.04
+ strategy:
+ # Keep running so we can see if other tests pass
+ fail-fast: false
+ matrix:
+ scenario: ${{fromJson(needs.list-scenarios.outputs.matrix)}}
+ steps:
+ - uses: actions/checkout@v4
+ - uses: actions/setup-python@v4
+ with:
+ python-version: '3.9'
+ - name: Install Ansible & Molecule
+ run: |
+ pip install "ansible<8" "ansible-lint<6.13" flake8
+ pip install "molecule<5" "ansible-compat<4"
+ pip install molecule-plugins[docker] pytest-testinfra
+ - name: Run molecule
+ run: molecule test -s "${{ matrix.scenario }}"
+
+ publish:
+ name: Galaxy
+ if: startsWith(github.ref, 'refs/tags')
+ needs:
+ - test
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ - name: Read the role name
+ id: role-name
+ run: |
+ name=$(grep 'role_name' meta/main.yml)
+ name=$(echo $name | sed -r 's/^[^:]*:(.*)$/\1/' | tr -d '[:space:]')
+ echo "rolename=$name" >> "$GITHUB_OUTPUT"
+ - name: Publish to Galaxy
+ uses: ome/action-ansible-galaxy-publish@main
+ with:
+ galaxy-api-key: ${{ secrets.GALAXY_API_KEY }}
+ galaxy-version: ${{ github.ref_name }}
+ role-name: ${{ steps.role-name.outputs.rolename }}
diff --git a/omero/roles/ome.ssl_certificate/.gitignore b/omero/roles/ome.ssl_certificate/.gitignore
new file mode 100644
index 00000000..0d20b648
--- /dev/null
+++ b/omero/roles/ome.ssl_certificate/.gitignore
@@ -0,0 +1 @@
+*.pyc
diff --git a/omero/roles/ome.ssl_certificate/LICENSE.md b/omero/roles/ome.ssl_certificate/LICENSE.md
new file mode 100644
index 00000000..a3181283
--- /dev/null
+++ b/omero/roles/ome.ssl_certificate/LICENSE.md
@@ -0,0 +1,22 @@
+Copyright (c) 2019, University of Dundee
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/omero/roles/ome.ssl_certificate/README.md b/omero/roles/ome.ssl_certificate/README.md
new file mode 100644
index 00000000..8e9b8076
--- /dev/null
+++ b/omero/roles/ome.ssl_certificate/README.md
@@ -0,0 +1,69 @@
+SSL Certificates
+================
+
+[![Actions Status](https://github.com/ome/ansible-role-ssl-certificate/workflows/Molecule/badge.svg)](https://github.com/ome/ansible-role-ssl-certificate/actions)
+[![Ansible Role](https://img.shields.io/badge/ansible--galaxy-ssl_certificate-blue.svg)](https://galaxy.ansible.com/ui/standalone/roles/ome/ssl_certificate/)
+
+Manage SSL certificates for web-servers.
+
+Optionally generate self-signed SSL certificates for internal testing.
+
+
+Role Variables
+--------------
+
+Defaults: `defaults/main.yml`
+
+Optional variables:
+
+- `ssl_certificate_public_path`: Server path to SSL public certificate
+- `ssl_certificate_intermediate_path`: Server path to SSL intermediate certificate(s)
+- `ssl_certificate_bundled_path`: Server path to SSL bundled public and intermediate certificates (e.g. for Nginx)
+- `ssl_certificate_key_path`: Server path to SSL certificate key
+- `ssl_certificate_combined_path`: Server path to SSL combined certificate and key (e.g. for Haproxy), set to empty to disable
+- `ssl_certificate_public_content`: Text content of the certificate, for instance from vault
+- `ssl_certificate_intermediate_content`: Text content of the intermediate certificate(s)
+- `ssl_certificate_key_content`: Text content of the certificate key
+- `ssl_certificate_selfsigned_create`: Create a self-signed certificate if necessary, default `True`
+- `ssl_certificate_selfsigned_subject`: Self-signed certificate subject
+- `ssl_certificate_selfsigned_days`: Self-signed certificate validity (days)
+- `ssl_certificate_install_openssl`: Install OpenSSL, default `True`
+
+Listeners/Handlers
+------------------
+
+This role notifies a listener `ssl certificate changed` when any changes are made.
+This should be used to trigger a restart of any services dependent on the certificates.
+
+
+Example Playbooks
+-----------------
+
+Create a self-signed certificate with defaults and restart Nginx (assumed to be already installed and configured):
+
+ - hosts: all
+ roles:
+ - role: ome.ssl_certificate
+ handlers:
+ - name: restart nginx
+ listen: ssl certificate changed
+ service:
+ name: nginx
+ state: restarted
+
+Install certificates stored locally on machine running Ansible:
+
+ - hosts: all
+ roles:
+ - role: ssl-certificate
+ ssl_certificate_public_content: "{{ lookup('file', '/path/to/server.crt') + '\n' }}"
+ ssl_certificate_key_content: "{{ lookup('file', '/path/to/server.key') + '\n' }}"
+ ssl_certificate_selfsigned_create: False
+
+
+Note: the additional newline being added after the lookup content is to correct Ansible bug https://github.com/ansible/ansible/issues/30829.
+
+Author Information
+------------------
+
+ome-devel@lists.openmicroscopy.org.uk
diff --git a/omero/roles/ome.ssl_certificate/defaults/main.yml b/omero/roles/ome.ssl_certificate/defaults/main.yml
new file mode 100644
index 00000000..774c14fa
--- /dev/null
+++ b/omero/roles/ome.ssl_certificate/defaults/main.yml
@@ -0,0 +1,39 @@
+---
+# defaults file for roles/ssl-certificates
+
+# Server path to SSL public certificate
+ssl_certificate_public_path: /etc/ssl/localcerts/server.crt
+
+# Server path to SSL intermediate certificate(s)
+ssl_certificate_intermediate_path: /etc/ssl/localcerts/intermediate.crt
+
+# Server path to SSL bundled public and intermediate certificates
+ssl_certificate_bundled_path: /etc/ssl/localcerts/bundled.crt
+
+# Server path to SSL certificate key
+ssl_certificate_key_path: /etc/ssl/localcerts/server.key
+
+# Server path to SSL combined certificate and key, set to empty to disable
+ssl_certificate_combined_path: /etc/ssl/localcerts/combined.pem
+
+# Text content of the public certificate
+ssl_certificate_public_content: ''
+
+# Text content of the intermediate certificate(s)
+ssl_certificate_intermediate_content: ''
+
+# Text content of the certificate key
+ssl_certificate_key_content: ''
+
+# Create a self-signed certificate if necessary
+ssl_certificate_selfsigned_create: true
+
+# Certificate subject
+ssl_certificate_selfsigned_subject: |-
+ /C=UK/ST=Scotland/L=Dundee/O=OME/CN={{ ansible_fqdn }}
+
+# Certificate validity (days)
+ssl_certificate_selfsigned_days: 365
+
+# Whether or not to install OpenSSL
+ssl_certificate_install_openssl: true
diff --git a/omero/roles/ome.ssl_certificate/handlers/main.yml b/omero/roles/ome.ssl_certificate/handlers/main.yml
new file mode 100644
index 00000000..137b3e14
--- /dev/null
+++ b/omero/roles/ome.ssl_certificate/handlers/main.yml
@@ -0,0 +1,7 @@
+---
+# Handlers for ssl-certificate
+
+# Ensures there is always at least one listener to prevent Ansible errors
+- name: ssl certificate changed
+ debug:
+ msg: ssl certificate changed
diff --git a/omero/roles/ome.ssl_certificate/meta/.galaxy_install_info b/omero/roles/ome.ssl_certificate/meta/.galaxy_install_info
new file mode 100644
index 00000000..1a3050a2
--- /dev/null
+++ b/omero/roles/ome.ssl_certificate/meta/.galaxy_install_info
@@ -0,0 +1,2 @@
+install_date: Wed 13 Nov 17:33:56 2024
+version: 0.5.0
diff --git a/omero/roles/ome.ssl_certificate/meta/main.yml b/omero/roles/ome.ssl_certificate/meta/main.yml
new file mode 100644
index 00000000..c412a9b2
--- /dev/null
+++ b/omero/roles/ome.ssl_certificate/meta/main.yml
@@ -0,0 +1,19 @@
+---
+galaxy_info:
+ author: ome-devel@lists.openmicroscopy.org.uk
+ description: Manage SSL certificates for web-servers
+ company: Open Microscopy Environment
+ license: BSD
+ min_ansible_version: 2.10
+ platforms:
+ - name: EL
+ versions:
+ - 9
+ - name: Ubuntu
+ versions:
+ - jammy
+ role_name: ssl_certificate
+ namespace: ome
+ galaxy_tags: []
+
+dependencies: []
diff --git a/omero/roles/ome.ssl_certificate/molecule/default/converge.yml b/omero/roles/ome.ssl_certificate/molecule/default/converge.yml
new file mode 100644
index 00000000..8a2f9ba2
--- /dev/null
+++ b/omero/roles/ome.ssl_certificate/molecule/default/converge.yml
@@ -0,0 +1,15 @@
+---
+- name: Converge
+ hosts: all
+ roles:
+ - role: ome.ssl_certificate
+
+ handlers:
+ - name: Create file when ssl certificate changed
+ listen: ssl certificate changed
+ # Use file so that we get an error if the handler is triggered during
+ # the idempotence check
+ file:
+ path: /tmp/ssl-certificate-changed
+ state: touch
+ mode: 0644
diff --git a/omero/roles/ome.ssl_certificate/molecule/default/intermediate.pem b/omero/roles/ome.ssl_certificate/molecule/default/intermediate.pem
new file mode 100644
index 00000000..a4995835
--- /dev/null
+++ b/omero/roles/ome.ssl_certificate/molecule/default/intermediate.pem
@@ -0,0 +1,11 @@
+-----BEGIN CERTIFICATE-----
+MIIBhjCCATACCQDxto+F3JFJoTANBgkqhkiG9w0BAQsFADBKMQswCQYDVQQGEwJV
+SzEOMAwGA1UECAwFRHVtbXkxFTATBgNVBAcMDEludGVybWVkaWF0ZTEUMBIGA1UE
+CgwLQ2VydGlmaWNhdGUwHhcNMTgwMzIyMTgyNzQ1WhcNMTkwMzIyMTgyNzQ1WjBK
+MQswCQYDVQQGEwJVSzEOMAwGA1UECAwFRHVtbXkxFTATBgNVBAcMDEludGVybWVk
+aWF0ZTEUMBIGA1UECgwLQ2VydGlmaWNhdGUwXDANBgkqhkiG9w0BAQEFAANLADBI
+AkEA3wcqKdD2kuKfQfy1Sy5gZvWE1onOPLGVlK8TXDrdTZOYHVSQrkZqEJaDFwQ4
+hAb8DS5dQCi5Tz62y1Q2k446bQIDAQABMA0GCSqGSIb3DQEBCwUAA0EAbY5+BsTY
+s0ZRsu44+j8atIlj4XkfgrP7OZQ+SYZUclVEZIZEz3W5fyJ2UuzLBqkwe9flbZUb
+9tEh5pv9vAsgaw==
+-----END CERTIFICATE-----
diff --git a/omero/roles/ome.ssl_certificate/molecule/default/molecule.yml b/omero/roles/ome.ssl_certificate/molecule/default/molecule.yml
new file mode 100644
index 00000000..04203ee6
--- /dev/null
+++ b/omero/roles/ome.ssl_certificate/molecule/default/molecule.yml
@@ -0,0 +1,25 @@
+---
+dependency:
+ name: galaxy
+driver:
+ name: docker
+lint: |
+ yamllint .
+ ansible-lint
+ flake8
+platforms:
+ - name: rockylinux-9
+ image: rockylinux:9
+ - name: ubuntu-2204
+ image: ubuntu:22.04
+provisioner:
+ name: ansible
+ lint:
+ name: ansible-lint
+ inventory:
+ host_vars:
+ ssl-certificate-u1604:
+ ssl_certificate_intermediate_content: |
+ {{ lookup('file', 'intermediate.pem') }}
+verifier:
+ name: testinfra
diff --git a/omero/roles/ome.ssl_certificate/molecule/default/tests/test_default.py b/omero/roles/ome.ssl_certificate/molecule/default/tests/test_default.py
new file mode 100644
index 00000000..244836a5
--- /dev/null
+++ b/omero/roles/ome.ssl_certificate/molecule/default/tests/test_default.py
@@ -0,0 +1,18 @@
+import os
+
+import testinfra.utils.ansible_runner
+
+testinfra_hosts = testinfra.utils.ansible_runner.AnsibleRunner(
+ os.environ['MOLECULE_INVENTORY_FILE']).get_hosts('all')
+
+
+def test_certificate(host):
+ with host.sudo():
+ out = host.check_output(
+ 'openssl x509 -in /etc/ssl/localcerts/server.crt -noout -subject')
+ assert out.startswith(
+ 'subject=C = UK, ST = Scotland, L = Dundee, O = OME, CN =')
+
+
+def test_listener_trigger(host):
+ assert host.file('/tmp/ssl-certificate-changed').exists
diff --git a/omero/roles/ome.ssl_certificate/molecule/default/tests/test_intermediate.py b/omero/roles/ome.ssl_certificate/molecule/default/tests/test_intermediate.py
new file mode 100644
index 00000000..79c343cc
--- /dev/null
+++ b/omero/roles/ome.ssl_certificate/molecule/default/tests/test_intermediate.py
@@ -0,0 +1,41 @@
+import os
+
+import testinfra.utils.ansible_runner
+import pytest
+
+testinfra_hosts = testinfra.utils.ansible_runner.AnsibleRunner(
+ os.environ['MOLECULE_INVENTORY_FILE']).get_hosts('ssl-certificate-u1604')
+
+
+@pytest.mark.parametrize("cert", [
+ 'bundled.crt',
+ 'combined.pem',
+])
+def test_intermediate(host, cert):
+ with host.sudo():
+ out = host.check_output(
+ 'openssl crl2pkcs7 -nocrl -certfile /etc/ssl/localcerts/%s | '
+ 'openssl pkcs7 -print_certs -text -noout |grep Subject:'
+ % cert)
+ lines = out.splitlines()
+ assert lines[0].strip().startswith(
+ 'Subject: C=UK, ST=Scotland, L=Dundee, O=OME, CN=')
+ assert lines[1].strip().startswith(
+ 'Subject: C=UK, ST=Dummy, L=Intermediate, O=Certificate')
+
+
+@pytest.mark.parametrize("cert", [
+ 'bundled.crt',
+ 'combined.pem',
+])
+def test_combined(host, cert):
+ with host.sudo():
+ out = host.check_output('grep BEGIN /etc/ssl/localcerts/%s' % cert)
+ lines = out.splitlines()
+ assert lines[0] == '-----BEGIN CERTIFICATE-----'
+ assert lines[1] == '-----BEGIN CERTIFICATE-----'
+ if cert == 'bundled.crt':
+ assert len(lines) == 2
+ else:
+ assert len(lines) == 3
+ assert lines[2] == '-----BEGIN PRIVATE KEY-----'
diff --git a/omero/roles/ome.ssl_certificate/tasks/main.yml b/omero/roles/ome.ssl_certificate/tasks/main.yml
new file mode 100644
index 00000000..6e388df3
--- /dev/null
+++ b/omero/roles/ome.ssl_certificate/tasks/main.yml
@@ -0,0 +1,116 @@
+---
+# tasks file for roles/ssl-certificate
+
+- name: check parameters
+ fail:
+ msg: ssl_certificate_public_content or ssl_certificate_key_content is empty
+ when: >-
+ (not ssl_certificate_selfsigned_create) and
+ (not ssl_certificate_public_content or not ssl_certificate_key_content)
+
+- name: install openssl
+ become: true
+ package:
+ name: openssl
+ state: present
+ when: ssl_certificate_install_openssl
+
+- name: create certificates directory
+ become: true
+ file:
+ path: "{{ item | dirname }}"
+ state: directory
+ mode: 0755
+ with_items:
+ - "{{ ssl_certificate_public_path }}"
+ - "{{ ssl_certificate_intermediate_path }}"
+ - "{{ ssl_certificate_bundled_path }}"
+ - "{{ ssl_certificate_key_path }}"
+
+# Create from content of variable e.g. from vault
+# Only the key needs to be kept private
+
+- name: write SSL public certificate
+ become: true
+ copy:
+ content: "{{ ssl_certificate_public_content }}"
+ dest: "{{ ssl_certificate_public_path }}"
+ mode: 0444
+ when: 'ssl_certificate_public_content | length > 0'
+ notify: ssl certificate changed
+
+- name: write SSL intermediate certificate
+ become: true
+ copy:
+ content: "{{ ssl_certificate_intermediate_content }}"
+ dest: "{{ ssl_certificate_intermediate_path }}"
+ mode: 0444
+ when: 'ssl_certificate_intermediate_content | length > 0'
+ notify: ssl certificate changed
+
+- name: write SSL certificate key
+ become: true
+ copy:
+ content: "{{ ssl_certificate_key_content }}"
+ dest: "{{ ssl_certificate_key_path }}"
+ mode: 0400
+ no_log: true
+ when: 'ssl_certificate_key_content | length > 0'
+ notify: ssl certificate changed
+
+# Self-signed
+# http://serialized.net/2013/04/simply-generating-self-signed-ssl-certs-with-ansible/
+
+- name: generate self-signed SSL certificate
+ become: true
+ command: >
+ openssl req -new -nodes -x509 -subj
+ {{ ssl_certificate_selfsigned_subject }}
+ -days {{ ssl_certificate_selfsigned_days }}
+ -keyout {{ ssl_certificate_key_path }}
+ -out {{ ssl_certificate_public_path }}
+ -extensions v3_ca
+ args:
+ # Don't overwrite existing certificate
+ creates: "{{ ssl_certificate_key_path }}"
+ when: ssl_certificate_selfsigned_create
+ notify: ssl certificate changed
+
+# Create combined certificate and key
+
+- name: read public certificate
+ become: true
+ slurp:
+ src: "{{ ssl_certificate_public_path }}"
+ register: _ssl_certificate_public_content
+
+- name: read certificate key
+ become: true
+ slurp:
+ src: "{{ ssl_certificate_key_path }}"
+ register: _ssl_certificate_key_content
+ no_log: true
+
+- name: write bundled certificate
+ become: true
+ copy:
+ content: |-
+ {{ _ssl_certificate_public_content.content | b64decode | trim }}
+ {{ ssl_certificate_intermediate_content }}
+ dest: "{{ ssl_certificate_bundled_path }}"
+ mode: 0444
+ when: "ssl_certificate_bundled_path | length > 0"
+ notify: ssl certificate changed
+
+- name: write SSL combined certificate key
+ become: true
+ copy:
+ content: |-
+ {{ _ssl_certificate_public_content.content | b64decode | trim }}
+ {{ ssl_certificate_intermediate_content | trim }}
+ {{ _ssl_certificate_key_content.content | b64decode }}
+ dest: "{{ ssl_certificate_combined_path }}"
+ mode: 0400
+ no_log: true
+ when: "ssl_certificate_combined_path | length > 0"
+ notify: ssl certificate changed
diff --git a/omero/roles/ome.versioncontrol_utils/.github/workflows/molecule.yml b/omero/roles/ome.versioncontrol_utils/.github/workflows/molecule.yml
new file mode 100644
index 00000000..3fa7ef7c
--- /dev/null
+++ b/omero/roles/ome.versioncontrol_utils/.github/workflows/molecule.yml
@@ -0,0 +1,55 @@
+---
+name: Molecule
+# yamllint disable-line rule:truthy
+on:
+ push:
+ pull_request:
+ schedule:
+ - cron: '29 21 * * 0'
+
+jobs:
+
+ list-scenarios:
+ runs-on: ubuntu-22.04
+ outputs:
+ matrix: ${{ steps.listscenarios.outputs.scenarios }}
+ steps:
+ - uses: actions/checkout@v4
+ - id: listscenarios
+ uses: ome/action-ansible-molecule-list-scenarios@main
+
+ test:
+ name: Test
+ needs:
+ - list-scenarios
+ runs-on: ubuntu-22.04
+ strategy:
+ # Keep running so we can see if other tests pass
+ fail-fast: false
+ matrix:
+ scenario: ${{fromJson(needs.list-scenarios.outputs.matrix)}}
+ steps:
+ - uses: actions/checkout@v4
+ - uses: actions/setup-python@v4
+ with:
+ python-version: '3.8'
+ - name: Install Ansible & Molecule
+ run: |
+ pip install "ansible<8" "ansible-lint<6.13" flake8
+ pip install "molecule<5" "ansible-compat<4"
+ pip install molecule-plugins[docker] pytest-testinfra
+ - name: Run molecule
+ run: molecule test -s "${{ matrix.scenario }}"
+
+ publish:
+ name: Galaxy
+ if: startsWith(github.ref, 'refs/tags')
+ needs:
+ - test
+ runs-on: ubuntu-20.04
+ steps:
+ - name: galaxy
+ uses: robertdebock/galaxy-action@1.2.1
+ with:
+ galaxy_api_key: ${{ secrets.GALAXY_API_KEY }}
+ git_branch: ${{ github.ref_name }}
diff --git a/omero/roles/ome.versioncontrol_utils/.gitignore b/omero/roles/ome.versioncontrol_utils/.gitignore
new file mode 100644
index 00000000..d35ab669
--- /dev/null
+++ b/omero/roles/ome.versioncontrol_utils/.gitignore
@@ -0,0 +1,2 @@
+.*~
+*.pyc
diff --git a/omero/roles/ome.versioncontrol_utils/LICENSE.md b/omero/roles/ome.versioncontrol_utils/LICENSE.md
new file mode 100644
index 00000000..a3181283
--- /dev/null
+++ b/omero/roles/ome.versioncontrol_utils/LICENSE.md
@@ -0,0 +1,22 @@
+Copyright (c) 2019, University of Dundee
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/omero/roles/ome.versioncontrol_utils/README.md b/omero/roles/ome.versioncontrol_utils/README.md
new file mode 100644
index 00000000..2e7bb874
--- /dev/null
+++ b/omero/roles/ome.versioncontrol_utils/README.md
@@ -0,0 +1,12 @@
+Version control utilities
+=========================
+
+[![Actions Status](https://github.com/ome/ansible-role-versioncontrol-utils/workflows/Molecule/badge.svg)](https://github.com/ome/ansible-role-versioncontrol-utils/actions)
+[![Ansible Role](https://img.shields.io/badge/ansible--galaxy-versioncontrol_utils-blue.svg)](https://galaxy.ansible.com/ui/standalone/roles/ome/versioncontrol_utils/)
+
+Utilities for managing source code.
+
+Author Information
+------------------
+
+ome-devel@lists.openmicroscopy.org.uk
diff --git a/omero/roles/ome.versioncontrol_utils/meta/.galaxy_install_info b/omero/roles/ome.versioncontrol_utils/meta/.galaxy_install_info
new file mode 100644
index 00000000..b7f1fcb9
--- /dev/null
+++ b/omero/roles/ome.versioncontrol_utils/meta/.galaxy_install_info
@@ -0,0 +1,2 @@
+install_date: Wed 13 Nov 17:33:54 2024
+version: 1.1.0
diff --git a/omero/roles/ome.versioncontrol_utils/meta/main.yml b/omero/roles/ome.versioncontrol_utils/meta/main.yml
new file mode 100644
index 00000000..e76bc9f5
--- /dev/null
+++ b/omero/roles/ome.versioncontrol_utils/meta/main.yml
@@ -0,0 +1,16 @@
+---
+galaxy_info:
+ role_name: versioncontrol_utils
+ author: ome-devel@lists.openmicroscopy.org.uk
+ description: Utilities for managing source code
+ company: Open Microscopy Environment
+ license: BSD
+ min_ansible_version: 2.1
+ platforms:
+ - name: EL
+ versions:
+ - 9
+ namespace: ome
+ galaxy_tags: []
+
+dependencies: []
diff --git a/molecule/ome-pg-prod/molecule_disabled.yml b/omero/roles/ome.versioncontrol_utils/molecule/default/molecule.yml
similarity index 52%
rename from molecule/ome-pg-prod/molecule_disabled.yml
rename to omero/roles/ome.versioncontrol_utils/molecule/default/molecule.yml
index b2feddfb..73026c50 100644
--- a/molecule/ome-pg-prod/molecule_disabled.yml
+++ b/omero/roles/ome.versioncontrol_utils/molecule/default/molecule.yml
@@ -1,8 +1,6 @@
---
dependency:
name: galaxy
- options:
- role-file: requirements.yml
driver:
name: docker
lint: |
@@ -10,19 +8,20 @@ lint: |
ansible-lint
flake8
platforms:
- - name: ome-pg-prod1.openmicroscopy.org
- image: centos/systemd
+ - name: instance
+ image: rockylinux:9
image_version: latest
- command: /sbin/init
privileged: true
+ cgroupns_mode: host
+ tmpfs:
+ - /sys/fs/cgroup
+ groups:
+ - extra_options
provisioner:
name: ansible
- playbooks:
- prepare: prepare.yml
- converge: ../../postgres/ome-pg-prod.yml
lint:
name: ansible-lint
scenario:
- name: ome-pg-prod
+ name: default
verifier:
name: testinfra
diff --git a/omero/roles/ome.versioncontrol_utils/molecule/default/playbook.yml b/omero/roles/ome.versioncontrol_utils/molecule/default/playbook.yml
new file mode 100644
index 00000000..86756ec2
--- /dev/null
+++ b/omero/roles/ome.versioncontrol_utils/molecule/default/playbook.yml
@@ -0,0 +1,5 @@
+---
+- name: Converge
+ hosts: all
+ roles:
+ - role: ome.versioncontrol_utils
diff --git a/omero/roles/ome.versioncontrol_utils/molecule/default/tests/test_default.py b/omero/roles/ome.versioncontrol_utils/molecule/default/tests/test_default.py
new file mode 100644
index 00000000..73bed884
--- /dev/null
+++ b/omero/roles/ome.versioncontrol_utils/molecule/default/tests/test_default.py
@@ -0,0 +1,10 @@
+import os
+
+import testinfra.utils.ansible_runner
+
+testinfra_hosts = testinfra.utils.ansible_runner.AnsibleRunner(
+ os.environ['MOLECULE_INVENTORY_FILE']).get_hosts('all')
+
+
+def test_hosts_file(host):
+ assert host.package('git').is_installed
diff --git a/omero/roles/ome.versioncontrol_utils/tasks/main.yml b/omero/roles/ome.versioncontrol_utils/tasks/main.yml
new file mode 100644
index 00000000..cf32af0c
--- /dev/null
+++ b/omero/roles/ome.versioncontrol_utils/tasks/main.yml
@@ -0,0 +1,18 @@
+---
+# tasks file for roles/versioncontrol-utils
+
+- name: system packages | install epel repo
+ become: true
+ ansible.builtin.dnf:
+ update_cache: true
+ name: epel-release
+ state: present
+
+- name: system packages | basic system utils
+ become: true
+ ansible.builtin.dnf:
+ update_cache: true
+ name: "{{ item }}"
+ state: present
+ with_items:
+ - git
diff --git a/requirements.yml b/requirements.yml
index a1759a69..1fb4f79c 100644
--- a/requirements.yml
+++ b/requirements.yml
@@ -1,107 +1,24 @@
---
-
-- name: ome.certbot
- src: https://github.com/ome/ansible-role-certbot/archive/0.1.0.tar.gz
- version: 0.1.0
-
-- src: ome.cli_utils
- version: 1.1.1
-
-- src: ome.deploy_archive
- version: 0.1.4
-
-- src: ome.docker
- version: 3.1.1
-
-- src: ome.ice
- version: 4.3.0
-
-- src: ome.java
- version: 2.1.0
-
-- name: ome.iptables_raw
- version: 0.3.1
-
-- src: ome.lvm_partition
- version: 1.1.1
-
-- name: ome.network
- version: 1.1.4
-
-- src: ome.nginx
- version: 2.1.1
-
-- name: ome.nginx_proxy
- version: 1.15.2
-
-- src: ome.nfs_mount
- version: 1.3.0
-
-- src: ome.omero_common
- version: 0.3.4
-
-- src: ome.basedeps
- version: 1.2.0
-
-- name: ome.omero_prometheus_exporter
- version: 0.3.6
-
-- name: ome.omero_server
- version: 4.0.2
-
-- src: ome.omero_user
- version: 0.3.0
-
-- name: ome.omero_web
- version: 4.0.1
-
-- src: ome.python3_virtualenv
- version: 0.1.2
-
-- src: ome.omero_web_django_prometheus
- version: 0.3.0
-
-- src: ome.postgresql
- version: 5.2.0
-
-- src: ome.postgresql_backup
- version: 0.2.1
-
-- src: ome.postgresql_client
- version: 0.2.0
-
-- src: ome.prometheus
- version: 0.5.1
-
-- src: ome.prometheus_jmx
- version: 0.2.2
-
-- src: ome.prometheus_node
- version: 0.2.2
-
-- src: ome.prometheus_postgres
- version: 0.4.2
-
-- src: ome.redis
- version: 1.1.1
-
-- name: ome.selinux_utils
- version: 1.0.3
-
-- src: ome.ssl_certificate
- version: 0.4.0
-
-- src: ome.sudoers
- version: 1.0.4
-
-- src: ome.system_monitor_agent
- version: 0.1.1
-
-- src: ome.upgrade_distpackages
- version: 1.1.3
-
-- src: ome.versioncontrol_utils
- version: 1.0.2
-
-- src: idr.redmine_tracker
- version: 0.1.0
+- role: ome.omero_common
+- role: ome.basedeps
+- role: ome.java
+- role: ome.python3_virtualenv
+- role: ome.ice
+- role: ome.postgresql
+- role: ome.postgresql_client
+- role: ome.deploy_archive
+- role: ome.omero_server
+- role: ome.omero_web
+- role: ome.nginx
+- role: ome.redis
+- role: ome.selinux_utils
+- role: ome.versioncontrol_utils
+- role: ome.ssl_certificate
+- role: nfs_mount
+- role: nfs_share
+- role: iptables_raw
+- role: ome.cli_utils
+- role: ome.docker
+- role: ome.postgresql_backup
+- role: ome.omero_user
+- role: ome.lvm_partition