summaryrefslogtreecommitdiff
path: root/roles/apps/nextcloud/instance
diff options
context:
space:
mode:
Diffstat (limited to 'roles/apps/nextcloud/instance')
-rw-r--r--roles/apps/nextcloud/instance/defaults/main.yml9
-rw-r--r--roles/apps/nextcloud/instance/tasks/custom-image.yml31
-rw-r--r--roles/apps/nextcloud/instance/tasks/main.yml196
-rw-r--r--roles/apps/nextcloud/instance/templates/apache-site.conf.j220
-rw-r--r--roles/apps/nextcloud/instance/templates/cron-.timer.j29
-rw-r--r--roles/apps/nextcloud/instance/templates/pod-spec-with-mariadb.yml.j2110
-rw-r--r--roles/apps/nextcloud/instance/templates/upgrade.sh.j277
7 files changed, 452 insertions, 0 deletions
diff --git a/roles/apps/nextcloud/instance/defaults/main.yml b/roles/apps/nextcloud/instance/defaults/main.yml
new file mode 100644
index 00000000..1a8a6d52
--- /dev/null
+++ b/roles/apps/nextcloud/instance/defaults/main.yml
@@ -0,0 +1,9 @@
+---
+nextcloud_app_uid: "950"
+nextcloud_app_gid: "950"
+
+nextcloud_db_uid: "951"
+nextcloud_db_gid: "951"
+
+nextcloud_redis_uid: "952"
+nextcloud_redis_gid: "952"
diff --git a/roles/apps/nextcloud/instance/tasks/custom-image.yml b/roles/apps/nextcloud/instance/tasks/custom-image.yml
new file mode 100644
index 00000000..f9e130a2
--- /dev/null
+++ b/roles/apps/nextcloud/instance/tasks/custom-image.yml
@@ -0,0 +1,31 @@
+---
+- name: create build directory for custom image
+ file:
+ path: "{{ nextcloud_instance_basepath }}/build"
+ state: directory
+
+- name: generate Dockerfile for custom image
+ copy:
+ content: |
+ FROM {{ nextcloud_instances[nextcloud_instance].custom_image.from | default('nextcloud:' + nextcloud_instances[nextcloud_instance].version) }}
+
+ RUN set -x \
+ && addgroup --gid {{ nextcloud_app_gid }} nc-app \
+ && adduser --uid {{ nextcloud_app_uid }} --gid {{ nextcloud_app_gid }} --system --no-create-home --home /var/www/html --disabled-login --disabled-password nc-app
+ {% if 'custom_image' in nextcloud_instances[nextcloud_instance] %}
+
+ {{ nextcloud_instances[nextcloud_instance].custom_image.dockerfile }}
+ {% endif %}
+ dest: "{{ nextcloud_instance_basepath }}/build/Dockerfile"
+ register: nextcloud_custom_image_docker
+
+- name: build custom image
+ docker_image:
+ name: "nextcloud/{{ nextcloud_instance }}:{{ nextcloud_instances[nextcloud_instance].version }}"
+ state: present
+ force_source: "{{ nextcloud_custom_image_docker is changed }}"
+ source: build
+ build:
+ path: "{{ nextcloud_instance_basepath }}/build"
+ network: host
+ pull: yes
diff --git a/roles/apps/nextcloud/instance/tasks/main.yml b/roles/apps/nextcloud/instance/tasks/main.yml
new file mode 100644
index 00000000..71a3ee79
--- /dev/null
+++ b/roles/apps/nextcloud/instance/tasks/main.yml
@@ -0,0 +1,196 @@
+---
+- name: prepare storage volume
+ vars:
+ storage_volume: "{{ nextcloud_instances[nextcloud_instance].storage }}"
+ include_role:
+ name: "storage/{{ nextcloud_instances[nextcloud_instance].storage.type }}/volume"
+
+- set_fact:
+ nextcloud_instance_basepath: "{{ storage_volume_mountpoint }}"
+
+- name: create nextcloud app subdirectory
+ file:
+ path: "{{ nextcloud_instance_basepath }}/nextcloud"
+ owner: "{{ nextcloud_app_uid }}"
+ group: "{{ nextcloud_app_gid }}"
+ state: directory
+
+- name: create nextcloud database subdirectory
+ file:
+ path: "{{ nextcloud_instance_basepath }}/{{ nextcloud_instances[nextcloud_instance].database.type }}"
+ owner: "{{ nextcloud_db_uid }}"
+ group: "{{ nextcloud_db_gid }}"
+ state: directory
+
+- name: create nextcloud redis subdirectory
+ file:
+ path: "{{ nextcloud_instance_basepath }}/redis"
+ owner: "{{ nextcloud_redis_uid }}"
+ group: "{{ nextcloud_redis_gid }}"
+ state: directory
+
+
+- name: create auxiliary config directory
+ file:
+ path: "{{ nextcloud_instance_basepath }}/config"
+ state: directory
+
+- name: create apache vhost config
+ template:
+ src: apache-site.conf.j2
+ dest: "{{ nextcloud_instance_basepath }}/config/apache-site.conf"
+
+- name: configure apache to run on port 8080 only
+ copy:
+ content: |
+ Listen 8080
+ dest: "{{ nextcloud_instance_basepath }}/config/ports.conf"
+
+
+- name: create tls directory
+ file:
+ path: "{{ nextcloud_instance_basepath }}/tls"
+ owner: "{{ nextcloud_app_uid }}"
+ group: "{{ nextcloud_app_gid }}"
+ mode: 0500
+ state: directory
+
+- name: generate/install TLS certificates for publishment
+ vars:
+ x509_certificate_name: "nextcloud-{{ nextcloud_instance }}_publish"
+ x509_certificate_hostnames: []
+ x509_certificate_config:
+ ca: "{{ nextcloud_instances[nextcloud_instance].publish.zone.certificate_ca_config }}"
+ cert:
+ common_name: "nextcloud-{{ nextcloud_instance }}.{{ inventory_hostname }}"
+ extended_key_usage:
+ - serverAuth
+ extended_key_usage_critical: yes
+ create_subject_key_identifier: yes
+ not_after: +100w
+ x509_certificate_renewal:
+ install:
+ - dest: "{{ nextcloud_instance_basepath }}/tls/cert.pem"
+ src:
+ - cert
+ owner: "{{ nextcloud_app_uid }}"
+ mode: "0400"
+ - dest: "{{ nextcloud_instance_basepath }}/tls/key.pem"
+ src:
+ - key
+ owner: "{{ nextcloud_app_uid }}"
+ mode: "0400"
+ include_role:
+ name: "x509/{{ nextcloud_instances[nextcloud_instance].publish.zone.certificate_provider }}/cert"
+
+
+- name: build custom image
+ # when: "'custom_image' in nextcloud_instances[nextcloud_instance]"
+ include_tasks: custom-image.yml
+
+- name: install pod manifest
+ vars:
+ kubernetes_standalone_pod:
+ name: "nextcloud-{{ nextcloud_instance }}"
+ spec: "{{ lookup('template', 'pod-spec-with-{{ nextcloud_instances[nextcloud_instance].database.type }}.yml.j2') }}"
+ mode: "0600"
+ config_hash_items:
+ - path: "{{ nextcloud_instance_basepath }}/config/apache-site.conf"
+ properties:
+ - checksum
+ - path: "{{ nextcloud_instance_basepath }}/config/ports.conf"
+ properties:
+ - checksum
+ - path: "{{ nextcloud_instance_basepath }}/build/Dockerfile"
+ properties:
+ - checksum
+ include_role:
+ name: kubernetes/standalone/pod
+
+
+- name: install upgrade helper script
+ template:
+ src: upgrade.sh.j2
+ dest: "{{ nextcloud_instance_basepath }}/upgrade.sh"
+ mode: 0755
+
+
+- name: install systemd timer unit
+ template:
+ src: cron-.timer.j2
+ dest: "/etc/systemd/system/nextcloud-cron-{{ nextcloud_instance }}.timer"
+
+- name: start/enable cron trigger systemd timer
+ systemd:
+ daemon_reload: yes
+ name: "nextcloud-cron-{{ nextcloud_instance }}.timer"
+ state: started
+ enabled: yes
+
+
+- name: configure nginx vhost for publishment
+ vars:
+ nginx_vhost__yaml: |
+ name: "nextcloud-{{ nextcloud_instance }}.{{ inventory_hostname }}"
+ template: generic
+ {% if 'tls' in nextcloud_instances[nextcloud_instance].publish %}
+ tls:
+ {{ nextcloud_instances[nextcloud_instance].publish.tls | to_nice_yaml(indent=2) | indent(2) }}
+ {% endif %}
+ hostnames:
+ {% for hostname in nextcloud_instances[nextcloud_instance].publish.hostnames %}
+ - {{ hostname }}
+ {% endfor %}
+ locations:
+ '/':
+ {% if nextcloud_instances[nextcloud_instance].publish.zone.publisher == inventory_hostname %}
+ proxy_pass: "https://127.0.0.1:{{ nextcloud_instances[nextcloud_instance].port }}"
+ {% else %}
+ proxy_pass: "https://{{ ansible_default_ipv4.address }}:{{ nextcloud_instances[nextcloud_instance].port }}"
+ {% endif %}
+ proxy_redirect:
+ - redirect: "https://$host:8080/"
+ replacement: "https://$host/"
+ proxy_ssl:
+ trusted_certificate: "/etc/ssl/apps-publish-{{ nextcloud_instances[nextcloud_instance].publish.zone.name }}/apps-publish-{{ nextcloud_instances[nextcloud_instance].publish.zone.name }}-ca-crt.pem"
+ verify: "on"
+ name: "nextcloud-{{ nextcloud_instance }}.{{ inventory_hostname }}"
+ protocols: "TLSv1.3"
+ extra_directives: |-
+ client_max_body_size 0;
+ types {
+ text/javascript js mjs;
+ }
+ nginx_vhost: "{{ nginx_vhost__yaml | from_yaml }}"
+ include_role:
+ name: nginx/vhost
+ apply:
+ delegate_to: "{{ nextcloud_instances[nextcloud_instance].publish.zone.publisher }}"
+
+
+# TODO:
+# do this automatically!
+- name: print info for new instance
+ when: "'new' in nextcloud_instances[nextcloud_instance] and nextcloud_instances[nextcloud_instance].new"
+ pause:
+ seconds: 5
+ prompt: |
+ ************* {{ nextcloud_instance }} is a new instance
+ **
+ ** Go to https://{{ nextcloud_instances[nextcloud_instance].publish.hostnames[0] }} and finalize the
+ ** installation. After that run the following commands:
+ **
+ ** $ nextcloud-occ {{ nextcloud_instance }} config:system:set default_phone_region --value='at'
+ ** $ nextcloud-occ {{ nextcloud_instance }} config:system:set memcache.locking --value '\OC\Memcache\Redis'
+ ** $ nextcloud-occ {{ nextcloud_instance }} config:system:set redis host --value '127.0.0.1'
+ ** $ nextcloud-occ {{ nextcloud_instance }} config:system:set redis port --type integer --value 6379
+ ** $ nextcloud-occ {{ nextcloud_instance }} config:system:set redis timeout --type float --value 0.0
+ ** $ nextcloud-occ {{ nextcloud_instance }} config:system:set redis password
+ ** $ nextcloud-occ {{ nextcloud_instance }} config:system:set maintenance_window_start --type integer --value 23
+ **
+ ** in case you want to use an exteranl collabora/code server:
+ **
+ ** $ nextcloud-occ {{ nextcloud_instance }} app:disable richdocumentscode
+ ** $ nextcloud-occ {{ nextcloud_instance }} app:remove richdocumentscode
+ **
+ ****************************************
diff --git a/roles/apps/nextcloud/instance/templates/apache-site.conf.j2 b/roles/apps/nextcloud/instance/templates/apache-site.conf.j2
new file mode 100644
index 00000000..8df06113
--- /dev/null
+++ b/roles/apps/nextcloud/instance/templates/apache-site.conf.j2
@@ -0,0 +1,20 @@
+IncludeOptional mods-available/socache_shmcb.load
+IncludeOptional mods-available/ssl.load
+IncludeOptional mods-available/ssl.conf
+
+<VirtualHost *:8080>
+ ServerAdmin webmaster@localhost
+ DocumentRoot /var/www/html
+
+ UseCanonicalName Off
+ UseCanonicalPhysicalPort Off
+
+ ServerName nextcloud-{{ nextcloud_instance }}.{{ inventory_hostname }}
+ SSLEngine on
+ SSLCertificateFile "/etc/ssl/publish/cert.pem"
+ SSLCertificateKeyFile "/etc/ssl/publish/key.pem"
+ SSLProtocol TLSv1.3
+
+ ErrorLog ${APACHE_LOG_DIR}/error.log
+ CustomLog ${APACHE_LOG_DIR}/access.log combined
+</VirtualHost>
diff --git a/roles/apps/nextcloud/instance/templates/cron-.timer.j2 b/roles/apps/nextcloud/instance/templates/cron-.timer.j2
new file mode 100644
index 00000000..eead0fd0
--- /dev/null
+++ b/roles/apps/nextcloud/instance/templates/cron-.timer.j2
@@ -0,0 +1,9 @@
+[Unit]
+Description=Nextcloud cron.php job timer for {{ nextcloud_instance }}
+
+[Timer]
+OnCalendar=*:{{ 5 | random(seed=nextcloud_instance) }}/5
+Unit=nextcloud-cron@{{ nextcloud_instance }}.service
+
+[Install]
+WantedBy=timers.target
diff --git a/roles/apps/nextcloud/instance/templates/pod-spec-with-mariadb.yml.j2 b/roles/apps/nextcloud/instance/templates/pod-spec-with-mariadb.yml.j2
new file mode 100644
index 00000000..c1a4f2ea
--- /dev/null
+++ b/roles/apps/nextcloud/instance/templates/pod-spec-with-mariadb.yml.j2
@@ -0,0 +1,110 @@
+securityContext:
+ allowPrivilegeEscalation: false
+containers:
+- name: nextcloud
+{# image: "nextcloud{% if 'custom_image' in nextcloud_instances[nextcloud_instance] %}/{{ nextcloud_instance }}{% endif %}:{{ nextcloud_instances[nextcloud_instance].version }}" #}
+ image: "nextcloud/{{ nextcloud_instance }}:{{ nextcloud_instances[nextcloud_instance].version }}"
+ securityContext:
+ runAsUser: {{ nextcloud_app_uid }}
+ runAsGroup: {{ nextcloud_app_gid }}
+ resources:
+ limits:
+ memory: "4Gi"
+{% if 'new' in nextcloud_instances[nextcloud_instance] and nextcloud_instances[nextcloud_instance].new %}
+ env:
+ - name: NEXTCLOUD_TRUSTED_DOMAINS
+ value: "{{ nextcloud_instances[nextcloud_instance].publish.hostnames | join(' ') }}"
+ - name: MYSQL_HOST
+ value: 127.0.0.1
+ - name: MYSQL_DATABASE
+ value: nextcloud
+ - name: MYSQL_USER
+ value: nextcloud
+ - name: MYSQL_PASSWORD
+ value: "{{ nextcloud_instances[nextcloud_instance].database.password }}"
+{% endif %}
+ volumeMounts:
+ - name: nextcloud
+ mountPath: /var/www/html
+ - name: config
+ mountPath: /etc/apache2/sites-available/000-default.conf
+ subPath: apache-site.conf
+ readOnly: true
+ - name: config
+ mountPath: /etc/apache2/ports.conf
+ subPath: ports.conf
+ readOnly: true
+ - name: tls
+ mountPath: /etc/ssl/publish
+ readOnly: true
+ ports:
+ - containerPort: 8080
+ hostPort: {{ nextcloud_instances[nextcloud_instance].port }}
+ hostIP: 127.0.0.1
+- name: redis
+ image: "redis:{{ nextcloud_instances[nextcloud_instance].redis.version }}"
+ args:
+ - --bind 127.0.0.1
+ securityContext:
+ runAsUser: {{ nextcloud_redis_uid }}
+ runAsGroup: {{ nextcloud_redis_gid }}
+ resources:
+ limits:
+ memory: "512Mi"
+ volumeMounts:
+ - name: redis
+ mountPath: /data
+- name: database
+ image: "mariadb:{{ nextcloud_instances[nextcloud_instance].database.version }}"
+ args:
+ - --transaction-isolation=READ-COMMITTED
+ - --log-bin=binlog
+ - --binlog-format=ROW
+{% for arg in (nextcloud_instances[nextcloud_instance].database.extra_args | default([])) %}
+ - {{ arg }}
+{% endfor %}
+ securityContext:
+ runAsUser: {{ nextcloud_db_uid }}
+ runAsGroup: {{ nextcloud_db_gid }}
+ resources:
+ limits:
+ memory: "2Gi"
+ env:
+ - name: MARIADB_AUTO_UPGRADE
+ value: "true"
+ - name: MARIADB_DISABLE_UPGRADE_BACKUP
+ value: "true"
+{% if 'new' in nextcloud_instances[nextcloud_instance] and nextcloud_instances[nextcloud_instance].new %}
+ - name: MARIADB_RANDOM_ROOT_PASSWORD
+ value: "true"
+ - name: MARIADB_DATABASE
+ value: nextcloud
+ - name: MARIADB_USER
+ value: nextcloud
+ - name: MARIADB_PASSWORD
+ value: "{{ nextcloud_instances[nextcloud_instance].database.password }}"
+{% endif %}
+ volumeMounts:
+ - name: database
+ mountPath: /var/lib/mysql
+volumes:
+- name: config
+ hostPath:
+ path: "{{ nextcloud_instance_basepath }}/config/"
+ type: Directory
+- name: tls
+ hostPath:
+ path: "{{ nextcloud_instance_basepath }}/tls/"
+ type: Directory
+- name: nextcloud
+ hostPath:
+ path: "{{ nextcloud_instance_basepath }}/nextcloud"
+ type: Directory
+- name: redis
+ hostPath:
+ path: "{{ nextcloud_instance_basepath }}/redis"
+ type: Directory
+- name: database
+ hostPath:
+ path: "{{ nextcloud_instance_basepath }}/{{ nextcloud_instances[nextcloud_instance].database.type }}"
+ type: Directory
diff --git a/roles/apps/nextcloud/instance/templates/upgrade.sh.j2 b/roles/apps/nextcloud/instance/templates/upgrade.sh.j2
new file mode 100644
index 00000000..62f6641e
--- /dev/null
+++ b/roles/apps/nextcloud/instance/templates/upgrade.sh.j2
@@ -0,0 +1,77 @@
+#!/bin/bash
+
+set -e
+
+if [ -z "$1" ] || [ -z "$2" ] || [ -z "$3" ]; then
+ echo "Usage: $0 (preapre|finalize) <old-version> <new-version>"
+ exit 1
+fi
+
+COMMAND="$1"
+OLD_VERSION="$2"
+NEW_VERSION="$3"
+POD_NAME="{{ nextcloud_instance }}-$(hostname)"
+
+maintenance_mode() {
+ POD_ID=$(crictl pods --name "$POD_NAME" --state ready -q)
+ CONTAINER_ID=$(crictl ps --pod "$POD_ID" --name nextcloud -q)
+ crictl exec "$CONTAINER_ID" php -f /var/www/html/occ maintenance:mode "$1"
+}
+
+wait_for_cronjobs() {
+ POD_ID=$(crictl pods --name "$POD_NAME" --state ready -q)
+ CONTAINER_ID=$(crictl ps --pod "$POD_ID" --name nextcloud -q)
+ crictl exec "$CONTAINER_ID" bash -c 'echo -n "waiting for running cron script "; while [ -n "$(pgrep -a php | grep cron.php)" ]; do echo -n "."; sleep 1; done; echo ""'
+}
+
+wait_for_upgrade_complete() {
+ NEW_VERSION="$1"
+
+ set +e
+ echo -n "waiting for new version to be ready "
+ while true; do
+ POD_ID=$(crictl pods --name "$POD_NAME" --state ready -q)
+ if [ -z $POD_ID ]; then continue; fi
+ CONTAINER_ID=$(crictl ps --pod "$POD_ID" --name nextcloud -q)
+ if [ -z $CONTAINER_ID ]; then continue; fi
+ STATUS_OUTPUT=$(crictl exec "$CONTAINER_ID" php -f /var/www/html/occ status -n --no-warnings --output plain)
+ if [ $? -eq 0 ]; then
+ RUNNING_VERSION=$(echo "$STATUS_OUTPUT" | awk -F : '/versionstring/ { print($2) }' | tr -d ' ')
+ if [ "$RUNNING_VERSION" = "$NEW_VERSION" ]; then
+ break
+ fi
+ echo -n "."
+ fi
+ sleep 1
+ done
+ echo ""
+ set -e
+ crictl exec "$CONTAINER_ID" bash -c 'echo -n "waiting for apache to start "; while [ -z "$(pgrep apache2)" ]; do echo -n "."; sleep 1; done; echo ""'
+}
+
+storage_snapshot() {
+ OLD_VERSION="$1"
+ NEW_VERSION="$2"
+
+{% if nextcloud_instances[nextcloud_instance].storage.type == 'zfs' %}
+ ZFS_VOLUME=$(findmnt -no source -T "{{ nextcloud_instance_basepath }}")
+ echo "creating snapshot for zfs volume: $ZFS_VOLUME"
+ zfs snapshot "$ZFS_VOLUME@upgrade_$OLD_VERSION-to-$NEW_VERSION""_$(date '+%Y-%m-%m_%H:%M:%S')"
+{% endif %}
+}
+
+case "$COMMAND" in
+ prepare)
+ maintenance_mode --on
+ wait_for_cronjobs
+ storage_snapshot "$OLD_VERSION" "$NEW_VERSION"
+ ;;
+ finalize)
+ wait_for_upgrade_complete "$NEW_VERSION"
+ maintenance_mode --off
+ ;;
+ *)
+ echo "unknown command: $COMMAND, must be prepare or finalize"
+ exit 1
+ ;;
+esac