diff --git a/config.yml b/config.yml new file mode 100644 index 0000000..04a23ea --- /dev/null +++ b/config.yml @@ -0,0 +1,10 @@ +source: + host: source.example.com + user: source_user + port: 22 + +destination: + host: destination.example.com + user: destination_user + port: 22 + diff --git a/dokku-sync.sh b/dokku-sync.sh index a54b109..108bed1 100644 --- a/dokku-sync.sh +++ b/dokku-sync.sh @@ -4,51 +4,99 @@ set -e set -u -LOCAL="danaos.infra.glenux.net" -REMOTE="dinlas.infra.glenux.net" -SSH_REMOTE="root@$REMOTE" -SSH_KEY="$HOME/.ssh/dinlas-to-danaos_rsync2022_ed25519" +# Generate SSH keypair on control machine (in temporary directory) +TEMP_DIR=$(mktemp -d) +MIGRATION_SSH_KEY="$TEMP_DIR/dokku-sync-migration_ed25519" +SSH_KEY="/path/to/control/key" +ssh-keygen -t ed25519 -f "$SSH_KEY" -N "" -sshrun() { - ssh -i "$SSH_KEY" root@dinlas.infra.glenux.net "$*" +usage() { + echo "Usage: $0 --src USER@HOSTNAME --dest USER@HOSTNAME --privkey PATH_TO_PRIVATE_KEY" + echo + echo "Options:" + echo " --src Source SSH address in the format USER@HOSTNAME" + echo " --dest Destination SSH address in the format USER@HOSTNAME" + echo " --privkey Path to the private SSH key" + echo " -h, --help Show this help message" } -# mkdir -p /srv/backup -# APPS=$ (sshrun "dokku ls" |awk '/^[^--]/ { print $1; }') +dest_run() { + ssh -i "$SSH_KEY" "$SOURCE_SSH_ADDR" "$*" +} + +source_run() { + ssh -i "$SSH_KEY" "$SOURCE_SSH_ADDR" "$*" +} + +## ENTRYPOINT + +# Loop and parse command line arguments +while [ "$#" -gt 0 ]; do + case "$1" in + --src) + SRC_SSH_ADDR="$2" + shift 2 + ;; + --dest) + DEST_SSH_ADDR="$2" + shift 2 + ;; + --privkey) + SSH_KEY="$2" + shift 2 + ;; + -h|--help) + usage + exit 0 + ;; + *) + echo "Unknown option: $1" + usage + exit 1 + ;; + esac +done + +# Split values for SRC and DEST +DEST_HOST=$(echo "$DEST_SSH_ADDR" | cut -d'@' -f2) +DEST_USER=$(echo "$DEST_SSH_ADDR" | cut -d'@' -f1) +SOURCE_HOST=$(echo "$SRC_SSH_ADDR" | cut -d'@' -f2) +SOURCE_USER=$(echo "$SRC_SSH_ADDR" | cut -d'@' -f1) # Stop all remote services -sshrun "dokku ps:stop --all" +source_run "dokku ps:stop --all" -sshrun "dokku mariadb:list |tail -n+2 |xargs -iSERVICE -- dokku mariadb:stop SERVICE" -sshrun "dokku postgres:list |tail -n+2 |xargs -iSERVICE -- dokku postgres:stop SERVICE" -sshrun "dokku redis:list |tail -n+2 |xargs -iSERVICE -- dokku redis:stop SERVICE" -sshrun "dokku mongo:list |tail -n+2 |xargs -iSERVICE -- dokku mongo:stop SERVICE" +source_run "dokku mariadb:list |tail -n+2 |xargs -iSERVICE -- dokku mariadb:stop SERVICE" +source_run "dokku postgres:list |tail -n+2 |xargs -iSERVICE -- dokku postgres:stop SERVICE" +source_run "dokku redis:list |tail -n+2 |xargs -iSERVICE -- dokku redis:stop SERVICE" +source_run "dokku mongo:list |tail -n+2 |xargs -iSERVICE -- dokku mongo:stop SERVICE" -sshrun "systemctl stop docker" -sshrun "systemctl stop docker.socket" +source_run "systemctl stop docker" +source_run "systemctl stop docker.socket" # Stop all local services -dokku ps:stop --all -systemctl stop docker -systemctl stop docker.socket +dest_run "dokku ps:stop --all" +dest_run "systemctl stop docker" +dest_run "systemctl stop docker.socket" + +# Prepare directories +dest_run "mkdir -p /home/dokku/" +dest_run "mkdir -p /home/data/" +dest_run "mkdir -p /var/lib/dokku/" # Copy all data -mkdir -p /home/dokku/ -mkdir -p /home/data/ -mkdir -p /var/lib/dokku/ +rsync -avz --delete "$SOURCE_SSH_ADDR:/home/dokku/" "$DEST_SSH_ADDR:/home/dokku/" +rsync -avz --delete "$SOURCE_SSH_ADDR:/var/lib/dokku/" "$DEST_SSH_ADDR:/var/lib/dokku/" +rsync -avz --delete "$SOURCE_SSH_ADDR:/home/data/" "$DEST_SSH_ADDR:/home/data/" +rsync -avz --delete "$SOURCE_SSH_ADDR:/home/git/" "$DEST_SSH_ADDR:/home/git/" -rsync -avz --delete "$SSH_REMOTE:/home/dokku/" /home/dokku/ -rsync -avz --delete "$SSH_REMOTE:/var/lib/dokku/" /var/lib/dokku/ -rsync -avz --delete "$SSH_REMOTE:/home/data/" /home/data/ -rsync -avz --delete "$SSH_REMOTE:/home/git/" /home/git/ +# FIXME: check if rsync succeeded # Start all local services -dokku apps:list +dest_run "dokku apps:list" # Patch URLs from imported domains -find /home/dokku/ -name VHOST -or -name URLS \ - | xargs -iFILE -- \ - sed -i "s/$REMOTE/$LOCAL/g" FILE +dest_run "find /home/dokku/ -name VHOST -or -name URLS -print0 | xargs -0 -iFILE -- sed -i \"s/$SOURCE_HOST/$DEST_HOST/g\" FILE" echo "SUCCESS" diff --git a/main.yml b/main.yml new file mode 100644 index 0000000..e69de29 diff --git a/playbook.yml b/playbook.yml new file mode 100644 index 0000000..c8ae53f --- /dev/null +++ b/playbook.yml @@ -0,0 +1,134 @@ +--- +- name: Dokku migration playbook + hosts: localhost + gather_facts: 'no' + vars_files: + - config.yml + + tasks: + - name: Create temporary directory for SSH keypair + tempfile: + state: directory + suffix: dokku_sync + register: temp_dir + + - name: Generate SSH keypair + community.crypto.openssh_keypair: + path: "{{ temp_dir.path }}/dokku_sync_migration" + type: ed25519 + owner: "{{ lookup('env', 'USER') }}" + group: "{{ lookup('env', 'USER') }}" + mode: '0600' + +- name: Handle Source Server + hosts: source + gather_facts: 'yes' + vars_files: + - config.yml + tasks: + - name: Stop all Dokku apps + command: dokku ps:stop --all + + - name: Stop all MariaDB services + shell: dokku mariadb:list | tail -n+2 | xargs -iSERVICE dokku mariadb:stop SERVICE + + - name: Stop all PostgreSQL services + shell: dokku postgres:list | tail -n+2 | xargs -iSERVICE dokku postgres:stop SERVICE + + - name: Stop all Redis services + shell: dokku redis:list | tail -n+2 | xargs -iSERVICE dokku redis:stop SERVICE + + - name: Stop all MongoDB services + shell: dokku mongo:list | tail -n+2 | xargs -iSERVICE dokku mongo:stop SERVICE + + - name: Stop Docker service + service: + name: docker + state: stopped + + - name: Stop Docker socket service + service: + name: docker.socket + state: stopped + +- name: Handle Destination Server + hosts: destination + gather_facts: yes + vars_files: + - config.yml + tasks: + - name: Stop all Dokku apps + command: dokku ps:stop --all + + - name: Stop Docker service + service: + name: docker + state: stopped + + - name: Stop Docker socket service + service: + name: docker.socket + state: stopped + + - name: Create necessary directories + file: + path: "{{ item }}" + state: directory + owner: dokku + group: dokku + mode: '0755' + loop: + - /home/dokku/ + - /home/data/ + - /var/lib/dokku/ + + - name: Synchronize /home/dokku + synchronize: + src: "{{ source_host }}:/home/dokku/" + dest: /home/dokku/ + archive: 'yes' + delete: 'yes' + + - name: Synchronize /var/lib/dokku + synchronize: + src: "{{ source_host }}:/var/lib/dokku/" + dest: /var/lib/dokku/ + archive: 'yes' + delete: 'yes' + + - name: Synchronize /home/data + synchronize: + src: "{{ source_host }}:/home/data/" + dest: /home/data/ + archive: 'yes' + delete: 'yes' + + - name: Synchronize /home/git + synchronize: + src: "{{ source_host }}:/home/git/" + dest: /home/git/ + archive: 'yes' + delete: 'yes' + + - name: List Dokku apps + command: dokku apps:list + + - name: Patch URLs in Dokku configuration files + find: + paths: + - /home/dokku/ + patterns: ['VHOST', 'URLS'] + recurse: 'yes' + register: dokku_config_files + + - name: Replace source host with destination host in configuration files + lineinfile: + path: "{{ item.path }}" + regexp: "{{ source_host }}" + line: "{{ destination_host }}" + loop: "{{ dokku_config_files.files }}" + + - name: Display success message + debug: + msg: "SUCCESS" + diff --git a/playbook2.yml b/playbook2.yml new file mode 100644 index 0000000..f36b680 --- /dev/null +++ b/playbook2.yml @@ -0,0 +1,194 @@ +--- +- name: Dokku migration playbook + hosts: localhost + gather_facts: no + vars_files: + - config.yml + + tasks: + - name: Create temporary directory for SSH keypair + tempfile: + state: directory + suffix: dokku_sync + register: temp_dir + + - name: Generate SSH keypair + community.crypto.openssh_keypair: + path: "{{ temp_dir.path }}/dokku_sync_migration" + type: ed25519 + owner: "{{ lookup('env', 'USER') }}" + group: "{{ lookup('env', 'USER') }}" + mode: '0600' + +- name: Handle Source Server + hosts: source + gather_facts: yes + vars_files: + - config.yml + tasks: + - name: Get list of Dokku MariaDB services + shell: dokku mariadb:list | tail -n+2 + register: mariadb_services + + - name: Get list of Dokku PostgreSQL services + shell: dokku postgres:list | tail -n+2 + register: postgres_services + + - name: Get list of Dokku Redis services + shell: dokku redis:list | tail -n+2 + register: redis_services + + - name: Get list of Dokku MongoDB services + shell: dokku mongo:list | tail -n+2 + register: mongo_services + + - name: Save Dokku service lists to disk + copy: + content: | + mariadb_services: {{ mariadb_services.stdout_lines }} + postgres_services: {{ postgres_services.stdout_lines }} + redis_services: {{ redis_services.stdout_lines }} + mongo_services: {{ mongo_services.stdout_lines }} + dest: /tmp/dokku_services.yml + + - name: Stop all Dokku apps + command: dokku ps:stop --all + + - name: Stop all MariaDB services + command: dokku mariadb:stop {{ item }} + loop: "{{ mariadb_services.stdout_lines }}" + + - name: Stop all PostgreSQL services + command: dokku postgres:stop {{ item }} + loop: "{{ postgres_services.stdout_lines }}" + + - name: Stop all Redis services + command: dokku redis:stop {{ item }} + loop: "{{ redis_services.stdout_lines }}" + + - name: Stop all MongoDB services + command: dokku mongo:stop {{ item }} + loop: "{{ mongo_services.stdout_lines }}" + + - name: Stop Docker service + service: + name: docker + state: stopped + + - name: Stop Docker socket service + service: + name: docker.socket + state: stopped + + - name: Fetch Dokku service lists to local + fetch: + src: /tmp/dokku_services.yml + dest: /tmp/dokku_services.yml + flat: yes + +- name: Handle Destination Server + hosts: destination + gather_facts: yes + vars_files: + - config.yml + tasks: + - name: Copy Dokku service lists to destination + copy: + src: /tmp/dokku_services.yml + dest: /tmp/dokku_services.yml + + - name: Load Dokku service lists + include_vars: + file: /tmp/dokku_services.yml + + - name: Stop all Dokku apps + command: dokku ps:stop --all + + - name: Stop Docker service + service: + name: docker + state: stopped + + - name: Stop Docker socket service + service: + name: docker.socket + state: stopped + + - name: Create necessary directories + file: + path: "{{ item }}" + state: directory + owner: dokku + group: dokku + mode: '0755' + loop: + - /home/dokku/ + - /home/data/ + - /var/lib/dokku/ + + - name: Synchronize /home/dokku + synchronize: + src: "{{ source_host }}:/home/dokku/" + dest: /home/dokku/ + archive: yes + delete: yes + + - name: Synchronize /var/lib/dokku + synchronize: + src: "{{ source_host }}:/var/lib/dokku/" + dest: /var/lib/dokku/ + archive: yes + delete: yes + + - name: Synchronize /home/data + synchronize: + src: "{{ source_host }}:/home/data/" + dest: /home/data/ + archive: yes + delete: yes + + - name: Synchronize /home/git + synchronize: + src: "{{ source_host }}:/home/git/" + dest: /home/git/ + archive: yes + delete: yes + + - name: Start all MariaDB services + command: dokku mariadb:start {{ item }} + loop: "{{ mariadb_services }}" + + - name: Start all PostgreSQL services + command: dokku postgres:start {{ item }} + loop: "{{ postgres_services }}" + + - name: Start all Redis services + command: dokku redis:start {{ item }} + loop: "{{ redis_services }}" + + - name: Start all MongoDB services + command: dokku mongo:start {{ item }} + loop: "{{ mongo_services }}" + + - name: List Dokku apps + command: dokku apps:list + + - name: Patch URLs in Dokku configuration files + find: + paths: + - /home/dokku/ + patterns: ['VHOST', 'URLS'] + recurse: yes + register: dokku_config_files + + - name: Replace source host with destination host in configuration files + lineinfile: + path: "{{ item.path }}" + regexp: "{{ source_host }}" + line: "{{ destination_host }}" + loop: "{{ dokku_config_files.files }}" + + - name: Display success message + debug: + msg: "SUCCESS" +