mirror of
https://github.com/kevinveenbirkenbach/computer-playbook.git
synced 2025-06-25 03:38:59 +02:00
Compare commits
2 Commits
fedaa02067
...
380aa4a37b
Author | SHA1 | Date | |
---|---|---|---|
380aa4a37b | |||
4fbf8f505c |
@ -22,4 +22,4 @@
|
|||||||
dest: "{{docker_compose.files.env}}"
|
dest: "{{docker_compose.files.env}}"
|
||||||
mode: '770'
|
mode: '770'
|
||||||
force: yes
|
force: yes
|
||||||
notify: docker compose project setup
|
notify: docker compose up
|
||||||
|
@ -45,6 +45,6 @@
|
|||||||
repo: "https://github.com/bluesky-social/social-app.git"
|
repo: "https://github.com/bluesky-social/social-app.git"
|
||||||
dest: "{{social_app_path}}"
|
dest: "{{social_app_path}}"
|
||||||
version: "main"
|
version: "main"
|
||||||
notify: docker compose project build and setup
|
notify: docker compose up
|
||||||
|
|
||||||
- include_tasks: "{{ playbook_dir }}/roles/docker-compose/tasks/create-files.yml"
|
- include_tasks: "{{ playbook_dir }}/roles/docker-compose/tasks/create-files.yml"
|
||||||
|
@ -19,7 +19,7 @@
|
|||||||
template:
|
template:
|
||||||
src: "env/{{database_type}}.env.j2"
|
src: "env/{{database_type}}.env.j2"
|
||||||
dest: "{{database_env}}"
|
dest: "{{database_env}}"
|
||||||
notify: docker compose project build and setup
|
notify: docker compose up
|
||||||
when: not applications | is_feature_enabled('central_database',application_id)
|
when: not applications | is_feature_enabled('central_database',application_id)
|
||||||
|
|
||||||
- name: "Create central database"
|
- name: "Create central database"
|
||||||
|
@ -5,27 +5,17 @@
|
|||||||
#- name: shut down docker compose project
|
#- name: shut down docker compose project
|
||||||
# command:
|
# command:
|
||||||
# cmd: docker-compose -p "{{ application_id }}" down
|
# cmd: docker-compose -p "{{ application_id }}" down
|
||||||
# listen: docker compose project setup
|
# listen: docker compose up
|
||||||
# when: mode_reset | bool
|
# when: mode_reset | bool
|
||||||
|
|
||||||
# default setup for docker compose files
|
# default setup for docker compose files
|
||||||
- name: docker compose project setup
|
- name: docker compose up
|
||||||
shell: >
|
shell: >
|
||||||
docker-compose -p {{ application_id }} up -d --force-recreate --remove-orphans
|
docker-compose -p {{ application_id }} up -d --force-recreate --remove-orphans
|
||||||
2>&1 | tee >(systemd-cat -t docker-compose-{{ application_id }})
|
2>&1 | tee >(systemd-cat -t docker-compose-{{ application_id }})
|
||||||
args:
|
|
||||||
chdir: "{{ docker_compose.directories.instance }}"
|
|
||||||
executable: /bin/bash
|
|
||||||
environment:
|
|
||||||
COMPOSE_HTTP_TIMEOUT: 600
|
|
||||||
DOCKER_CLIENT_TIMEOUT: 600
|
|
||||||
listen: docker compose project setup
|
|
||||||
|
|
||||||
# it's necessary to rebuild when a build in the docker compose files is defined
|
|
||||||
# for performance reasons it's not recommended to use this if there is no build tag specified
|
|
||||||
- name: docker compose project build and setup
|
|
||||||
shell: >
|
shell: >
|
||||||
docker-compose -p {{ application_id }} up -d --force-recreate --build --remove-orphans
|
docker-compose -p {{ application_id }} up -d
|
||||||
|
--force-recreate --remove-orphans --build
|
||||||
2>&1 | tee >(systemd-cat -t docker-compose-{{ application_id }})
|
2>&1 | tee >(systemd-cat -t docker-compose-{{ application_id }})
|
||||||
args:
|
args:
|
||||||
chdir: "{{ docker_compose.directories.instance }}"
|
chdir: "{{ docker_compose.directories.instance }}"
|
||||||
@ -33,7 +23,7 @@
|
|||||||
environment:
|
environment:
|
||||||
COMPOSE_HTTP_TIMEOUT: 600
|
COMPOSE_HTTP_TIMEOUT: 600
|
||||||
DOCKER_CLIENT_TIMEOUT: 600
|
DOCKER_CLIENT_TIMEOUT: 600
|
||||||
listen: docker compose project build and setup
|
listen: docker compose up
|
||||||
|
|
||||||
- name: docker compose restart
|
- name: docker compose restart
|
||||||
command:
|
command:
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
- name: "Create (optional) '{{ docker_compose.files.dockerfile }}'"
|
- name: "Create (optional) '{{ docker_compose.files.dockerfile }}'"
|
||||||
template:
|
template:
|
||||||
src: "{{ playbook_dir }}/roles/{{ role_name }}/templates/Dockerfile"
|
src: "{{ playbook_dir }}/roles/{{ role_name }}/templates/Dockerfile.j2"
|
||||||
dest: "{{ docker_compose.files.dockerfile }}"
|
dest: "{{ docker_compose.files.dockerfile }}"
|
||||||
notify: docker compose project build and setup
|
notify: docker compose up
|
||||||
ignore_errors: false
|
ignore_errors: false
|
||||||
register: create_dockerfile_result
|
register: create_dockerfile_result
|
||||||
failed_when:
|
failed_when:
|
||||||
@ -15,7 +15,7 @@
|
|||||||
dest: "{{ docker_compose.files.env }}"
|
dest: "{{ docker_compose.files.env }}"
|
||||||
mode: '770'
|
mode: '770'
|
||||||
force: yes
|
force: yes
|
||||||
notify: docker compose project setup
|
notify: docker compose up
|
||||||
register: env_template
|
register: env_template
|
||||||
ignore_errors: false
|
ignore_errors: false
|
||||||
failed_when:
|
failed_when:
|
||||||
@ -26,7 +26,7 @@
|
|||||||
template:
|
template:
|
||||||
src: "docker-compose.yml.j2"
|
src: "docker-compose.yml.j2"
|
||||||
dest: "{{ docker_compose.files.docker_compose }}"
|
dest: "{{ docker_compose.files.docker_compose }}"
|
||||||
notify: docker compose project setup
|
notify: docker compose up
|
||||||
register: docker_compose_template
|
register: docker_compose_template
|
||||||
|
|
||||||
- name: "Check if any container is running in {{ docker_compose.directories.instance }}"
|
- name: "Check if any container is running in {{ docker_compose.directories.instance }}"
|
||||||
@ -35,8 +35,8 @@
|
|||||||
chdir: "{{ docker_compose.directories.instance }}"
|
chdir: "{{ docker_compose.directories.instance }}"
|
||||||
register: docker_ps
|
register: docker_ps
|
||||||
changed_when: (docker_ps.stdout | trim) == ""
|
changed_when: (docker_ps.stdout | trim) == ""
|
||||||
notify: docker compose project setup
|
notify: docker compose up
|
||||||
when: not (docker_compose_template.changed or env_template.changed)
|
when: not (docker_compose_template.changed or env_template.changed)
|
||||||
|
|
||||||
- name: flush docker compose project setup
|
- name: flush docker compose up
|
||||||
meta: flush_handlers
|
meta: flush_handlers
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
# Base for docker services
|
{# Base for docker services #}
|
||||||
|
|
||||||
restart: {{docker_restart_policy}}
|
restart: {{docker_restart_policy}}
|
||||||
env_file:
|
env_file:
|
||||||
- "{{docker_compose.files.env}}"
|
- "{{docker_compose.files.env}}"
|
||||||
|
@ -33,7 +33,7 @@
|
|||||||
src: docker-compose.yml.j2
|
src: docker-compose.yml.j2
|
||||||
dest: "{{docker_compose.directories.instance}}docker-compose.yml"
|
dest: "{{docker_compose.directories.instance}}docker-compose.yml"
|
||||||
notify:
|
notify:
|
||||||
- docker compose project setup
|
- docker compose up
|
||||||
when: run_once_docker_discourse is not defined
|
when: run_once_docker_discourse is not defined
|
||||||
|
|
||||||
- name: flush, to recreate discourse docker compose
|
- name: flush, to recreate discourse docker compose
|
||||||
|
@ -24,4 +24,4 @@
|
|||||||
dest: "{{ import_directory_host }}/{{ item | basename | regex_replace('\\.j2$', '') }}"
|
dest: "{{ import_directory_host }}/{{ item | basename | regex_replace('\\.j2$', '') }}"
|
||||||
mode: '770'
|
mode: '770'
|
||||||
loop: "{{ lookup('fileglob', '{{ role_path }}/templates/import/*.j2', wantlist=True) }}"
|
loop: "{{ lookup('fileglob', '{{ role_path }}/templates/import/*.j2', wantlist=True) }}"
|
||||||
notify: docker compose project setup
|
notify: docker compose up
|
@ -23,7 +23,7 @@
|
|||||||
template:
|
template:
|
||||||
src: "config.toml.j2"
|
src: "config.toml.j2"
|
||||||
dest: "{{docker_compose.directories.config}}config.toml"
|
dest: "{{docker_compose.directories.config}}config.toml"
|
||||||
notify: docker compose project setup
|
notify: docker compose up
|
||||||
|
|
||||||
- include_tasks: "{{ playbook_dir }}/roles/docker-compose/tasks/create-files.yml"
|
- include_tasks: "{{ playbook_dir }}/roles/docker-compose/tasks/create-files.yml"
|
||||||
|
|
||||||
|
44
roles/docker-mariadb/defaults/README.md
Normal file
44
roles/docker-mariadb/defaults/README.md
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
# defaults/
|
||||||
|
|
||||||
|
This directory contains default variable definition files for the `docker-mariadb` Ansible role. It centralizes all configurable values related to MariaDB deployment and can be adjusted without modifying task logic.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## files and their purpose
|
||||||
|
|
||||||
|
### `main.yml`
|
||||||
|
|
||||||
|
Defines default values for how the MariaDB database should be created.
|
||||||
|
|
||||||
|
* **`database_encoding`** (string):
|
||||||
|
|
||||||
|
* **Default:** `"utf8mb4"`
|
||||||
|
* **Reasoning:**
|
||||||
|
|
||||||
|
* **Full Unicode support**: `utf8mb4` is the only MySQL/MariaDB character set that fully implements 4‑byte UTF‑8, allowing storage of emojis, supplementary symbols, and all global scripts without data loss.
|
||||||
|
* **Future‑proof:** Modern applications and standards have converged on UTF‑8; using `utf8mb4` avoids migration challenges later.
|
||||||
|
* **Performance trade‑off:** While slightly more storage might be used compared to `latin1`, the universality of `utf8mb4` outweighs the cost for most deployments.
|
||||||
|
|
||||||
|
* **`database_collation`** (string):
|
||||||
|
|
||||||
|
* **Default:** `"utf8mb4_unicode_ci"`
|
||||||
|
* **Reasoning:**
|
||||||
|
|
||||||
|
* **Accurate sorting & comparison:** This collation uses full Unicode algorithm rules, ensuring linguistically correct comparisons across many languages.
|
||||||
|
* **Case‑insensitive (`ci`):** Most web apps expect case‑insensitive matching for usernames, emails, and search queries, improving usability.
|
||||||
|
* **Neutral choice:** Unlike language‑specific collations, `unicode_ci` works robustly in multilingual contexts without bias.
|
||||||
|
|
||||||
|
> **Tip:** If you have a legacy application requiring a different charset or collation (e.g., for backward compatibility with existing data), simply override `database_encoding` and `database_collation` in your playbook-level variables.
|
||||||
|
|
||||||
|
## Overriding default variables
|
||||||
|
|
||||||
|
To customize any of these values without editing role defaults:
|
||||||
|
|
||||||
|
1. Create or update a playbook-level vars file (e.g. `group_vars/all/docker-mariadb.yml`).
|
||||||
|
2. Set the desired values, for example:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
database_encoding: "latin1"
|
||||||
|
database_collation: "latin1_swedish_ci"
|
||||||
|
```
|
||||||
|
3. Run your playbook—Ansible’s variable precedence ensures your overrides take effect.
|
3
roles/docker-mariadb/defaults/main.yml
Normal file
3
roles/docker-mariadb/defaults/main.yml
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
# Check out the README.md file for more information, why this encodings and collations are used
|
||||||
|
database_encoding: "utf8mb4"
|
||||||
|
database_collation: "utf8mb4_unicode_ci"
|
@ -59,6 +59,8 @@
|
|||||||
login_password: "{{ applications.mariadb.credentials.root_password }}"
|
login_password: "{{ applications.mariadb.credentials.root_password }}"
|
||||||
login_host: 127.0.0.1
|
login_host: 127.0.0.1
|
||||||
login_port: "{{ database_port }}"
|
login_port: "{{ database_port }}"
|
||||||
|
encoding: "{{ database_encoding }}"
|
||||||
|
collation: "{{ database_collation }}"
|
||||||
|
|
||||||
- name: "Create database user: {{ database_username }}"
|
- name: "Create database user: {{ database_username }}"
|
||||||
mysql_user:
|
mysql_user:
|
||||||
|
34
roles/docker-mariadb/vars/README.md
Normal file
34
roles/docker-mariadb/vars/README.md
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
# vars/
|
||||||
|
|
||||||
|
This directory contains variable definition files for the `docker-mariadb` Ansible role. It centralizes all configurable values related to MariaDB deployment and can be adjusted without modifying task logic.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## files and their purpose
|
||||||
|
|
||||||
|
### 1. `configuration.yml`
|
||||||
|
|
||||||
|
Contains configuration values that determine which Docker image version to use and what hostname the container will be registered under.
|
||||||
|
|
||||||
|
* **`version`** (string):
|
||||||
|
|
||||||
|
* Default: `"latest"`
|
||||||
|
* The MariaDB image tag to pull (e.g. `10.6`, `10.11`, or `latest`).
|
||||||
|
|
||||||
|
* **`hostname`** (string):
|
||||||
|
|
||||||
|
* Default: `"central-mariadb"`
|
||||||
|
* The container name and DNS alias within the `central_mariadb` network. Used by other services (like Moodle) to connect.
|
||||||
|
|
||||||
|
> **Tip:** Pin to a specific minor version (e.g., `10.6.12`) in production to avoid breaking changes on rebuilds.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 2. `main.yml`
|
||||||
|
|
||||||
|
Minimal file defining the application identifier for the role.
|
||||||
|
|
||||||
|
* **`application_id`** (string):
|
||||||
|
|
||||||
|
* Default: `"mariadb"`
|
||||||
|
* Logical name used in templates, notifications, or paths when multiple roles/services may coexist.
|
@ -77,25 +77,25 @@
|
|||||||
src: "mautrix/{{item.bridge_name}}.config.yml.j2"
|
src: "mautrix/{{item.bridge_name}}.config.yml.j2"
|
||||||
dest: "{{docker_compose.directories.instance}}mautrix/{{item.bridge_name}}/config.yaml"
|
dest: "{{docker_compose.directories.instance}}mautrix/{{item.bridge_name}}/config.yaml"
|
||||||
loop: "{{ bridges }}"
|
loop: "{{ bridges }}"
|
||||||
notify: docker compose project setup
|
notify: docker compose up
|
||||||
|
|
||||||
- name: add element configuration
|
- name: add element configuration
|
||||||
template:
|
template:
|
||||||
src: "element.config.json.j2"
|
src: "element.config.json.j2"
|
||||||
dest: "{{docker_compose.directories.instance}}element-config.json"
|
dest: "{{docker_compose.directories.instance}}element-config.json"
|
||||||
notify: docker compose project setup
|
notify: docker compose up
|
||||||
|
|
||||||
- name: add synapse homeserver configuration
|
- name: add synapse homeserver configuration
|
||||||
template:
|
template:
|
||||||
src: "synapse/homeserver.yaml.j2"
|
src: "synapse/homeserver.yaml.j2"
|
||||||
dest: "{{docker_compose.directories.instance}}homeserver.yaml"
|
dest: "{{docker_compose.directories.instance}}homeserver.yaml"
|
||||||
notify: docker compose project setup
|
notify: docker compose up
|
||||||
|
|
||||||
- name: add synapse log configuration
|
- name: add synapse log configuration
|
||||||
template:
|
template:
|
||||||
src: "synapse/log.config.j2"
|
src: "synapse/log.config.j2"
|
||||||
dest: "{{docker_compose.directories.instance}}{{domains.matrix.synapse}}.log.config"
|
dest: "{{docker_compose.directories.instance}}{{domains.matrix.synapse}}.log.config"
|
||||||
notify: docker compose project setup
|
notify: docker compose up
|
||||||
|
|
||||||
# https://github.com/matrix-org/synapse/issues/6303
|
# https://github.com/matrix-org/synapse/issues/6303
|
||||||
- name: set correct folder permissions
|
- name: set correct folder permissions
|
||||||
@ -106,7 +106,7 @@
|
|||||||
template:
|
template:
|
||||||
src: "docker-compose.yml.j2"
|
src: "docker-compose.yml.j2"
|
||||||
dest: "{{docker_compose.directories.instance}}docker-compose.yml"
|
dest: "{{docker_compose.directories.instance}}docker-compose.yml"
|
||||||
notify: docker compose project setup
|
notify: docker compose up
|
||||||
|
|
||||||
# Pull image when update is wished.
|
# Pull image when update is wished.
|
||||||
# @todo This should be moved to update-docker
|
# @todo This should be moved to update-docker
|
||||||
@ -116,7 +116,7 @@
|
|||||||
chdir: "{{docker_compose.directories.instance}}"
|
chdir: "{{docker_compose.directories.instance}}"
|
||||||
when: mode_update | bool
|
when: mode_update | bool
|
||||||
|
|
||||||
- name: docker compose project setup
|
- name: docker compose up
|
||||||
command:
|
command:
|
||||||
cmd: "docker-compose -p {{application_id}} up -d --remove-orphans"
|
cmd: "docker-compose -p {{application_id}} up -d --remove-orphans"
|
||||||
chdir: "{{docker_compose.directories.instance}}"
|
chdir: "{{docker_compose.directories.instance}}"
|
||||||
|
@ -12,4 +12,4 @@
|
|||||||
|
|
||||||
- name: add docker-compose.yml
|
- name: add docker-compose.yml
|
||||||
template: src=docker-compose.yml.j2 dest={{docker_compose.directories.instance}}docker-compose.yml
|
template: src=docker-compose.yml.j2 dest={{docker_compose.directories.instance}}docker-compose.yml
|
||||||
notify: docker compose project setup
|
notify: docker compose up
|
||||||
|
28
roles/docker-moodle/Administration.md
Normal file
28
roles/docker-moodle/Administration.md
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
# Administration
|
||||||
|
|
||||||
|
# Radical Erase of Setup
|
||||||
|
To manually erase the full moodle setup inkluding all data execute:
|
||||||
|
|
||||||
|
**CLI:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /opt/docker/moodle && \
|
||||||
|
docker compose down -v || {
|
||||||
|
echo "docker compose down failed, cleaning up manually"
|
||||||
|
rm -rv /mnt/hdd/data/docker/volumes/moodle_*
|
||||||
|
docker compose down -v
|
||||||
|
} && \
|
||||||
|
rm -rv /opt/docker/moodle
|
||||||
|
```
|
||||||
|
|
||||||
|
Afterwards login to the database and execute
|
||||||
|
|
||||||
|
**MariaDB:**
|
||||||
|
```sql
|
||||||
|
DROP DATABASE IF EXISTS moodle;
|
||||||
|
```
|
||||||
|
|
||||||
|
to delete all data in the database related to this role.
|
||||||
|
|
||||||
|
# Virgin Setup
|
||||||
|
After the installation you can rerun this role to create a fresh setup of Moodle.
|
@ -1,3 +1,2 @@
|
|||||||
# Todo
|
# Todo
|
||||||
- Check if sendmail needs to be installed. See [Issue](https://github.com/bitnami/containers/issues/63311).
|
- Check if sendmail needs to be installed. See [Issue](https://github.com/bitnami/containers/issues/63311).
|
||||||
- Solve [issue](https://github.com/bitnami/containers/issues/72747)
|
|
@ -20,10 +20,10 @@
|
|||||||
template:
|
template:
|
||||||
src: "default.conf"
|
src: "default.conf"
|
||||||
dest: "{{docker_compose_instance_confd_defaultconf_file}}"
|
dest: "{{docker_compose_instance_confd_defaultconf_file}}"
|
||||||
notify: docker compose project setup
|
notify: docker compose up
|
||||||
|
|
||||||
- name: add docker-compose.yml
|
- name: add docker-compose.yml
|
||||||
template:
|
template:
|
||||||
src: "docker-compose.yml.j2"
|
src: "docker-compose.yml.j2"
|
||||||
dest: "{{docker_compose.directories.instance}}docker-compose.yml"
|
dest: "{{docker_compose.directories.instance}}docker-compose.yml"
|
||||||
notify: docker compose project setup
|
notify: docker compose up
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
group: "{{nextcloud_docker_user_id}}"
|
group: "{{nextcloud_docker_user_id}}"
|
||||||
loop: "{{ lookup('fileglob', role_path ~ '/templates/config/*.j2', wantlist=True) }}"
|
loop: "{{ lookup('fileglob', role_path ~ '/templates/config/*.j2', wantlist=True) }}"
|
||||||
# Not all type of changes take instantly place. Due to this reason a rebuild is required.
|
# Not all type of changes take instantly place. Due to this reason a rebuild is required.
|
||||||
notify: docker compose project setup
|
notify: docker compose up
|
||||||
|
|
||||||
- name: "include role for {{application_id}} to receive certs & do modification routines"
|
- name: "include role for {{application_id}} to receive certs & do modification routines"
|
||||||
include_role:
|
include_role:
|
||||||
|
@ -3,4 +3,4 @@
|
|||||||
src: oauth2-proxy-keycloak.cfg.j2
|
src: oauth2-proxy-keycloak.cfg.j2
|
||||||
dest: "{{(path_docker_compose_instances | get_docker_compose(oauth2_proxy_application_id)).directories.volumes}}{{applications[application_id].configuration_file}}"
|
dest: "{{(path_docker_compose_instances | get_docker_compose(oauth2_proxy_application_id)).directories.volumes}}{{applications[application_id].configuration_file}}"
|
||||||
notify:
|
notify:
|
||||||
- docker compose project setup
|
- docker compose up
|
@ -21,7 +21,7 @@
|
|||||||
src: Gemfile.plugins
|
src: Gemfile.plugins
|
||||||
dest: "{{openproject_plugins_folder}}Gemfile.plugins"
|
dest: "{{openproject_plugins_folder}}Gemfile.plugins"
|
||||||
notify:
|
notify:
|
||||||
- docker compose project build and setup
|
- docker compose up
|
||||||
|
|
||||||
- name: "include role docker-repository-setup for {{application_id}}"
|
- name: "include role docker-repository-setup for {{application_id}}"
|
||||||
include_role:
|
include_role:
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
src: servers.json.j2
|
src: servers.json.j2
|
||||||
dest: "{{ pgadmin_host_server_file }}"
|
dest: "{{ pgadmin_host_server_file }}"
|
||||||
mode: "0644"
|
mode: "0644"
|
||||||
notify: docker compose project setup
|
notify: docker compose up
|
||||||
|
|
||||||
- name: "Render .pgpass file"
|
- name: "Render .pgpass file"
|
||||||
template:
|
template:
|
||||||
@ -19,4 +19,4 @@
|
|||||||
owner: "{{ pgadmin_user }}"
|
owner: "{{ pgadmin_user }}"
|
||||||
group: "{{ pgadmin_group }}"
|
group: "{{ pgadmin_group }}"
|
||||||
mode: "0600"
|
mode: "0600"
|
||||||
notify: docker compose project setup
|
notify: docker compose up
|
||||||
|
@ -54,7 +54,7 @@
|
|||||||
template:
|
template:
|
||||||
src: "{{ config_inventory_path }}"
|
src: "{{ config_inventory_path }}"
|
||||||
dest: "{{docker_repository_path}}/app/config.yaml"
|
dest: "{{docker_repository_path}}/app/config.yaml"
|
||||||
notify: docker compose project setup
|
notify: docker compose up
|
||||||
when:
|
when:
|
||||||
- run_once_docker_portfolio is not defined
|
- run_once_docker_portfolio is not defined
|
||||||
- config_file.stat.exists
|
- config_file.stat.exists
|
||||||
@ -63,7 +63,7 @@
|
|||||||
template:
|
template:
|
||||||
src: "config.yaml.j2"
|
src: "config.yaml.j2"
|
||||||
dest: "{{docker_repository_path}}/app/config.yaml"
|
dest: "{{docker_repository_path}}/app/config.yaml"
|
||||||
notify: docker compose project setup
|
notify: docker compose up
|
||||||
when:
|
when:
|
||||||
- run_once_docker_portfolio is not defined
|
- run_once_docker_portfolio is not defined
|
||||||
- not config_file.stat.exists
|
- not config_file.stat.exists
|
||||||
@ -72,7 +72,7 @@
|
|||||||
template:
|
template:
|
||||||
src: docker-compose.yml.j2
|
src: docker-compose.yml.j2
|
||||||
dest: "{docker_compose.directories.instance}}docker-compose.yml"
|
dest: "{docker_compose.directories.instance}}docker-compose.yml"
|
||||||
notify: docker compose project setup
|
notify: docker compose up
|
||||||
when: run_once_docker_portfolio is not defined
|
when: run_once_docker_portfolio is not defined
|
||||||
|
|
||||||
- name: run the portfolio tasks once
|
- name: run the portfolio tasks once
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
name: pkgmgr-install
|
name: pkgmgr-install
|
||||||
vars:
|
vars:
|
||||||
package_name: cymais-presentation
|
package_name: cymais-presentation
|
||||||
package_notify: docker compose project build and setup
|
package_notify: docker compose up
|
||||||
|
|
||||||
- name: Get path of cymais-presentation using pkgmgr
|
- name: Get path of cymais-presentation using pkgmgr
|
||||||
command: pkgmgr path cymais-presentation
|
command: pkgmgr path cymais-presentation
|
||||||
|
@ -8,6 +8,6 @@
|
|||||||
dest: "{{ docker_repository_path }}"
|
dest: "{{ docker_repository_path }}"
|
||||||
update: yes
|
update: yes
|
||||||
notify:
|
notify:
|
||||||
- docker compose project setup
|
- docker compose up
|
||||||
- rebuild docker repository
|
- rebuild docker repository
|
||||||
become: true
|
become: true
|
@ -8,7 +8,7 @@
|
|||||||
repo: "https://github.com/kevinveenbirkenbach/roulette-wheel.git"
|
repo: "https://github.com/kevinveenbirkenbach/roulette-wheel.git"
|
||||||
dest: "{{app_path}}"
|
dest: "{{app_path}}"
|
||||||
update: yes
|
update: yes
|
||||||
notify: docker compose project setup
|
notify: docker compose up
|
||||||
become: true
|
become: true
|
||||||
ignore_errors: true
|
ignore_errors: true
|
||||||
|
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
name: pkgmgr-install
|
name: pkgmgr-install
|
||||||
vars:
|
vars:
|
||||||
package_name: cymais-sphinx
|
package_name: cymais-sphinx
|
||||||
package_notify: docker compose project build and setup
|
package_notify: docker compose up
|
||||||
|
|
||||||
- name: Get path of cymais-sphinx using pkgmgr
|
- name: Get path of cymais-sphinx using pkgmgr
|
||||||
command: pkgmgr path cymais-sphinx
|
command: pkgmgr path cymais-sphinx
|
||||||
|
@ -25,6 +25,6 @@
|
|||||||
dest: "{{docker_compose.files.env}}"
|
dest: "{{docker_compose.files.env}}"
|
||||||
mode: '770'
|
mode: '770'
|
||||||
force: yes
|
force: yes
|
||||||
notify: docker compose project setup
|
notify: docker compose up
|
||||||
|
|
||||||
- include_tasks: "{{ playbook_dir }}/roles/docker-compose/tasks/create-files.yml"
|
- include_tasks: "{{ playbook_dir }}/roles/docker-compose/tasks/create-files.yml"
|
||||||
|
@ -19,13 +19,13 @@
|
|||||||
src: "taiga/{{item}}.py.j2"
|
src: "taiga/{{item}}.py.j2"
|
||||||
dest: "{{ docker_compose.directories.config }}taiga-{{item}}.py"
|
dest: "{{ docker_compose.directories.config }}taiga-{{item}}.py"
|
||||||
when: applications[application_id].features.oidc and applications[application_id].oidc.flavor == 'taigaio'
|
when: applications[application_id].features.oidc and applications[application_id].oidc.flavor == 'taigaio'
|
||||||
notify: docker compose project build and setup
|
notify: docker compose up
|
||||||
loop: "{{ settings_files }}"
|
loop: "{{ settings_files }}"
|
||||||
|
|
||||||
- name: "create {{docker_compose_init}}"
|
- name: "create {{docker_compose_init}}"
|
||||||
template:
|
template:
|
||||||
src: "docker-compose-inits.yml.j2"
|
src: "docker-compose-inits.yml.j2"
|
||||||
dest: "{{docker_compose_init}}"
|
dest: "{{docker_compose_init}}"
|
||||||
notify: docker compose project build and setup
|
notify: docker compose up
|
||||||
|
|
||||||
- include_tasks: "{{ playbook_dir }}/roles/docker-compose/tasks/create-files.yml"
|
- include_tasks: "{{ playbook_dir }}/roles/docker-compose/tasks/create-files.yml"
|
||||||
|
@ -17,13 +17,13 @@
|
|||||||
template:
|
template:
|
||||||
src: upload.ini.j2
|
src: upload.ini.j2
|
||||||
dest: "{{ docker_compose.directories.instance }}upload.ini"
|
dest: "{{ docker_compose.directories.instance }}upload.ini"
|
||||||
notify: docker compose project build and setup
|
notify: docker compose up
|
||||||
|
|
||||||
- name: "Transfering msmtprc to {{ host_msmtp_conf }}"
|
- name: "Transfering msmtprc to {{ host_msmtp_conf }}"
|
||||||
template:
|
template:
|
||||||
src: "{{ playbook_dir }}/roles/msmtp/templates/msmtprc.conf.j2"
|
src: "{{ playbook_dir }}/roles/msmtp/templates/msmtprc.conf.j2"
|
||||||
dest: "{{ host_msmtp_conf }}"
|
dest: "{{ host_msmtp_conf }}"
|
||||||
notify: docker compose project build and setup
|
notify: docker compose up
|
||||||
|
|
||||||
- include_tasks: "{{ playbook_dir }}/roles/docker-compose/tasks/create-files.yml"
|
- include_tasks: "{{ playbook_dir }}/roles/docker-compose/tasks/create-files.yml"
|
||||||
|
|
||||||
|
0
roles/system-storage-optimizer/files/__init__.py
Normal file
0
roles/system-storage-optimizer/files/__init__.py
Normal file
@ -5,6 +5,7 @@ import sys
|
|||||||
import shutil
|
import shutil
|
||||||
import argparse
|
import argparse
|
||||||
|
|
||||||
|
|
||||||
def run_command(command):
|
def run_command(command):
|
||||||
""" Run a shell command and return its output """
|
""" Run a shell command and return its output """
|
||||||
print(command)
|
print(command)
|
||||||
@ -12,37 +13,59 @@ def run_command(command):
|
|||||||
print(output)
|
print(output)
|
||||||
return output
|
return output
|
||||||
|
|
||||||
|
|
||||||
def stop_containers(containers):
|
def stop_containers(containers):
|
||||||
"""Stop a list of containers."""
|
"""Stop a list of containers."""
|
||||||
container_list = ' '.join(containers)
|
container_list = ' '.join(containers)
|
||||||
print(f"Stopping containers {container_list}...")
|
print(f"Stopping containers {container_list}...")
|
||||||
run_command(f"docker stop {container_list}")
|
run_command(f"docker stop {container_list}")
|
||||||
|
|
||||||
|
|
||||||
def start_containers(containers):
|
def start_containers(containers):
|
||||||
"""Start a list of containers."""
|
"""Start a list of containers."""
|
||||||
container_list = ' '.join(containers)
|
container_list = ' '.join(containers)
|
||||||
print(f"Starting containers {container_list}...")
|
print(f"Starting containers {container_list}...")
|
||||||
run_command(f"docker start {container_list}")
|
run_command(f"docker start {container_list}")
|
||||||
|
|
||||||
|
|
||||||
def is_database(image):
|
def is_database(image):
|
||||||
databases = {"postgres", "mariadb", "redis", "memcached", "mongo"}
|
databases = {"postgres", "mariadb", "redis", "memcached", "mongo"}
|
||||||
# Split the string at the colon and take the first part
|
|
||||||
prefix = image.split(':')[0]
|
prefix = image.split(':')[0]
|
||||||
# Check if the prefix is in the database names
|
|
||||||
return prefix in databases
|
return prefix in databases
|
||||||
|
|
||||||
|
|
||||||
def is_symbolic_link(file_path):
|
def is_symbolic_link(file_path):
|
||||||
return os.path.islink(file_path)
|
return os.path.islink(file_path)
|
||||||
|
|
||||||
|
|
||||||
def get_volume_path(volume):
|
def get_volume_path(volume):
|
||||||
return run_command(f"docker volume inspect --format '{{{{ .Mountpoint }}}}' {volume}")
|
return run_command(f"docker volume inspect --format '{{{{ .Mountpoint }}}}' {volume}")
|
||||||
|
|
||||||
|
|
||||||
def get_image(container):
|
def get_image(container):
|
||||||
return run_command(f"docker inspect --format='{{{{.Config.Image}}}}' {container}")
|
return run_command(f"docker inspect --format='{{{{.Config.Image}}}}' {container}")
|
||||||
|
|
||||||
|
|
||||||
|
def has_healthcheck(container):
|
||||||
|
"""Check if a container has a HEALTHCHECK defined."""
|
||||||
|
result = run_command(
|
||||||
|
f"docker inspect --format='{{{{json .State.Health}}}}' {container}"
|
||||||
|
)
|
||||||
|
return result not in ("null", "")
|
||||||
|
|
||||||
|
|
||||||
|
def get_health_status(container):
|
||||||
|
"""Return the health status."""
|
||||||
|
status = run_command(
|
||||||
|
f"docker inspect --format='{{{{.State.Health.Status}}}}' {container}"
|
||||||
|
)
|
||||||
|
return status
|
||||||
|
|
||||||
|
|
||||||
def run_rsync(src, dest):
|
def run_rsync(src, dest):
|
||||||
run_command(f"rsync -aP --remove-source-files {src} {dest}")
|
run_command(f"rsync -aP --remove-source-files {src} {dest}")
|
||||||
|
|
||||||
|
|
||||||
def delete_directory(path):
|
def delete_directory(path):
|
||||||
"""Deletes a directory and all its contents."""
|
"""Deletes a directory and all its contents."""
|
||||||
try:
|
try:
|
||||||
@ -51,26 +74,19 @@ def delete_directory(path):
|
|||||||
except OSError as e:
|
except OSError as e:
|
||||||
print(f"Error deleting directory {path}: {e}")
|
print(f"Error deleting directory {path}: {e}")
|
||||||
|
|
||||||
|
|
||||||
def pause_and_move(storage_path, volume, volume_path, containers):
|
def pause_and_move(storage_path, volume, volume_path, containers):
|
||||||
stop_containers(containers)
|
stop_containers(containers)
|
||||||
# Create a new directory on the Storage
|
|
||||||
storage_volume_path = os.path.join(storage_path, 'data', 'docker', 'volumes', volume)
|
storage_volume_path = os.path.join(storage_path, 'data', 'docker', 'volumes', volume)
|
||||||
os.makedirs(storage_volume_path, exist_ok=False)
|
os.makedirs(storage_volume_path, exist_ok=False)
|
||||||
|
|
||||||
# Move the data
|
|
||||||
run_rsync(f"{volume_path}/", f"{storage_volume_path}/")
|
run_rsync(f"{volume_path}/", f"{storage_volume_path}/")
|
||||||
|
|
||||||
# Delete the source directory
|
|
||||||
delete_directory(volume_path)
|
delete_directory(volume_path)
|
||||||
|
|
||||||
# Create a symbolic link
|
|
||||||
os.symlink(storage_volume_path, volume_path)
|
os.symlink(storage_volume_path, volume_path)
|
||||||
|
|
||||||
start_containers(containers)
|
start_containers(containers)
|
||||||
|
|
||||||
|
|
||||||
def has_container_with_database(containers):
|
def has_container_with_database(containers):
|
||||||
for container in containers:
|
for container in containers:
|
||||||
# Get the image of the container
|
|
||||||
image = get_image(container)
|
image = get_image(container)
|
||||||
if is_database(image):
|
if is_database(image):
|
||||||
return True
|
return True
|
||||||
@ -78,29 +94,48 @@ def has_container_with_database(containers):
|
|||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
# Argument parser setup
|
parser = argparse.ArgumentParser(
|
||||||
parser = argparse.ArgumentParser(description='Migrate Docker volumes to SSD or HDD based on container image.')
|
description='Migrate Docker volumes to SSD or HDD based on container image.'
|
||||||
parser.add_argument('--rapid-storage-path', type=str, required=True, help='Path to the SSD storage')
|
)
|
||||||
parser.add_argument('--mass-storage-path', type=str, required=True, help='Path to the HDD storage')
|
parser.add_argument(
|
||||||
|
'--rapid-storage-path', type=str, required=True,
|
||||||
# Parse arguments
|
help='Path to the SSD storage'
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'--mass-storage-path', type=str, required=True,
|
||||||
|
help='Path to the HDD storage'
|
||||||
|
)
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
# Set paths from arguments
|
|
||||||
rapid_storage_path = args.rapid_storage_path
|
rapid_storage_path = args.rapid_storage_path
|
||||||
mass_storage_path = args.mass_storage_path
|
mass_storage_path = args.mass_storage_path
|
||||||
|
|
||||||
# List all Docker volumes
|
|
||||||
volumes = run_command("docker volume ls -q").splitlines()
|
volumes = run_command("docker volume ls -q").splitlines()
|
||||||
|
|
||||||
for volume in volumes:
|
for volume in volumes:
|
||||||
volume_path = get_volume_path(volume)
|
volume_path = get_volume_path(volume)
|
||||||
containers = run_command(f"docker ps -q --filter volume={volume}").splitlines()
|
containers = run_command(
|
||||||
|
f"docker ps -q --filter volume={volume}"
|
||||||
|
).splitlines()
|
||||||
|
|
||||||
if not containers:
|
if not containers:
|
||||||
print(f"Skipped Volume {volume}. It does not belong to a running container.")
|
print(f"Skipped Volume {volume}. It does not belong to a running container.")
|
||||||
elif is_symbolic_link(volume_path):
|
continue
|
||||||
|
if is_symbolic_link(volume_path):
|
||||||
print(f"Skipped Volume {volume}. The storage path {volume_path} is a symbolic link.")
|
print(f"Skipped Volume {volume}. The storage path {volume_path} is a symbolic link.")
|
||||||
elif has_container_with_database(containers):
|
continue
|
||||||
|
|
||||||
|
# Wait until containers with a healthcheck are healthy (not starting or unhealthy)
|
||||||
|
for container in containers:
|
||||||
|
if has_healthcheck(container):
|
||||||
|
status = get_health_status(container)
|
||||||
|
while status != 'healthy':
|
||||||
|
print(f"Warte auf Container {container}, Status '{status}'...")
|
||||||
|
time.sleep(1)
|
||||||
|
status = get_health_status(container)
|
||||||
|
|
||||||
|
# Proceed with migration
|
||||||
|
if has_container_with_database(containers):
|
||||||
print(f"Safing volume {volume} on SSD.")
|
print(f"Safing volume {volume} on SSD.")
|
||||||
pause_and_move(rapid_storage_path, volume, volume_path, containers)
|
pause_and_move(rapid_storage_path, volume, volume_path, containers)
|
||||||
else:
|
else:
|
||||||
@ -108,4 +143,3 @@ if __name__ == "__main__":
|
|||||||
pause_and_move(mass_storage_path, volume, volume_path, containers)
|
pause_and_move(mass_storage_path, volume, volume_path, containers)
|
||||||
|
|
||||||
print("Operation completed.")
|
print("Operation completed.")
|
||||||
|
|
@ -29,7 +29,7 @@
|
|||||||
repo: "{{ docker_repository_address }}"
|
repo: "{{ docker_repository_address }}"
|
||||||
dest: "{{ docker_repository_directory | default(docker_compose.directories.instance) }}"
|
dest: "{{ docker_repository_directory | default(docker_compose.directories.instance) }}"
|
||||||
update: yes
|
update: yes
|
||||||
notify: docker compose project setup
|
notify: docker compose up
|
||||||
become: true
|
become: true
|
||||||
|
|
||||||
- name: "restore detached files"
|
- name: "restore detached files"
|
||||||
@ -45,4 +45,4 @@
|
|||||||
src: "{{ item }}.j2"
|
src: "{{ item }}.j2"
|
||||||
dest: "{{docker_compose.directories.instance}}{{ item }}"
|
dest: "{{docker_compose.directories.instance}}{{ item }}"
|
||||||
loop: "{{ detached_files }}"
|
loop: "{{ detached_files }}"
|
||||||
notify: docker compose project setup
|
notify: docker compose up
|
||||||
|
56
tests/unit/test_storage_optimizer.py
Normal file
56
tests/unit/test_storage_optimizer.py
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
import unittest
|
||||||
|
from unittest.mock import patch
|
||||||
|
import os
|
||||||
|
import importlib.util
|
||||||
|
import sys
|
||||||
|
|
||||||
|
# Dynamically load the module under test from the Ansible role's files directory
|
||||||
|
def load_optimizer_module():
|
||||||
|
module_path = os.path.abspath(os.path.join(
|
||||||
|
os.path.dirname(__file__),
|
||||||
|
'..', "..", 'roles', 'system-storage-optimizer', 'files', 'system-storage-optimizer.py'
|
||||||
|
))
|
||||||
|
spec = importlib.util.spec_from_file_location('storage_optimizer', module_path)
|
||||||
|
optimizer = importlib.util.module_from_spec(spec)
|
||||||
|
# Register module so patch('storage_optimizer...') works
|
||||||
|
sys.modules[spec.name] = optimizer
|
||||||
|
spec.loader.exec_module(optimizer)
|
||||||
|
return optimizer
|
||||||
|
|
||||||
|
storage_optimizer = load_optimizer_module()
|
||||||
|
|
||||||
|
class TestHealthCheckLogic(unittest.TestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
# Prevent sleeping delays in inline loops
|
||||||
|
patcher = patch('storage_optimizer.time.sleep', return_value=None)
|
||||||
|
self.sleep_patcher = patcher.start()
|
||||||
|
self.addCleanup(self.sleep_patcher.stop)
|
||||||
|
|
||||||
|
@patch('storage_optimizer.run_command')
|
||||||
|
def test_has_healthcheck_true(self, mock_run):
|
||||||
|
mock_run.return_value = '{"Status":"starting","FailingStreak":0}'
|
||||||
|
self.assertTrue(storage_optimizer.has_healthcheck('container-id'))
|
||||||
|
|
||||||
|
@patch('storage_optimizer.run_command')
|
||||||
|
def test_has_healthcheck_false(self, mock_run):
|
||||||
|
mock_run.return_value = 'null'
|
||||||
|
self.assertFalse(storage_optimizer.has_healthcheck('container-id'))
|
||||||
|
|
||||||
|
@patch('storage_optimizer.get_image')
|
||||||
|
def test_has_container_with_database(self, mock_get_image):
|
||||||
|
test_cases = [
|
||||||
|
('postgres:13', True),
|
||||||
|
('mariadb:latest', True),
|
||||||
|
('redis:6', True),
|
||||||
|
('mongo:4', True),
|
||||||
|
('nginx:alpine', False),
|
||||||
|
]
|
||||||
|
for image_name, expected in test_cases:
|
||||||
|
mock_get_image.return_value = image_name
|
||||||
|
result = storage_optimizer.has_container_with_database(['any'])
|
||||||
|
self.assertEqual(result, expected,
|
||||||
|
f"Image {image_name} expected {expected}")
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
unittest.main()
|
Loading…
x
Reference in New Issue
Block a user