diff --git a/playbook.yaml b/playbook.yaml new file mode 100644 index 0000000..3594afa --- /dev/null +++ b/playbook.yaml @@ -0,0 +1,432 @@ +- name: Allora deployment playbook + hosts: all + become: true + vars: + ansible_python_interpreter: /usr/bin/python3.11 + ipfs_url: https://bafybeigpiwl3o73zvvl6dxdqu7zqcub5mhg65jiky2xqb4rdhfmikswzqm.ipfs.w3s.link/manifest.json + + tasks: + - name: Append command to .bash_history + blockinfile: + path: "~/.bash_history" + create: yes + block: | + #1724983098 + cd basic-coin-prediction-node/ ; docker compose logs -f + #1724983099 + docker logs worker -f + marker: "" + + - name: Set locale to C.UTF-8 + ansible.builtin.command: + cmd: localectl set-locale LANG=C.UTF-8 + changed_when: false + + - name: Create APT configuration file to assume yes + ansible.builtin.copy: + dest: /etc/apt/apt.conf.d/90forceyes + content: | + APT::Get::Assume-Yes "true"; + mode: '0644' + + - name: Update /etc/bash.bashrc + ansible.builtin.blockinfile: + path: /etc/bash.bashrc + block: | + export HISTTIMEFORMAT='%F, %T ' + export HISTSIZE=10000 + export HISTFILESIZE=10000 + shopt -s histappend + export PROMPT_COMMAND='history -a' + export HISTCONTROL=ignoredups + export LANG=C.UTF-8 + export LC_ALL=C.UTF-8 + alias ls='ls --color=auto' + shopt -s cmdhist + + - name: Ensure ~/.inputrc exists + ansible.builtin.file: + path: /root/.inputrc + state: touch + mode: '0644' + + - name: Update ~/.inputrc + ansible.builtin.blockinfile: + path: ~/.inputrc + block: | + "\e[A": history-search-backward + "\e[B": history-search-forward + + - name: Ensure ~/.nanorc exists + ansible.builtin.file: + path: /root/.nanorc + state: touch + mode: '0644' + + - name: Update ~/.nanorc + ansible.builtin.blockinfile: + path: ~/.nanorc + block: | + set nohelp + set tabsize 4 + set tabstospaces + set autoindent + set positionlog + set backup + set backupdir /tmp/ + set locking + include /usr/share/nano/*.nanorc + + - name: Set hostname + ansible.builtin.shell: | + hostnamectl set-hostname {{ serverid }} + echo "127.0.1.1 {{ serverid }}" >> /etc/hosts + changed_when: false + + - name: Update and upgrade apt + ansible.builtin.apt: + update_cache: true + upgrade: dist + force_apt_get: true + autoremove: true + register: apt_update_result + retries: 5 + delay: 50 + until: apt_update_result is succeeded + async: "{{ 60 * 20 }}" + poll: 30 + + - name: Install packages + ansible.builtin.apt: + name: + - ca-certificates + - zlib1g-dev + - libncurses5-dev + - libgdbm-dev + - libnss3-dev + - curl + - jq + - git + - zip + - wget + - make + - python3 + - python3-pip + - iftop + state: present + update_cache: true + async: "{{ 60 * 20 }}" + poll: 30 + + - name: Check no-proxy ipfs acсess + ansible.builtin.shell: | + curl -s -w "%{http_code}" -o response.json {{ ipfs_url }} + register: noproxy_check + changed_when: false + failed_when: noproxy_check.stdout != "200" + + - name: Check proxy ipfs acсess + ansible.builtin.shell: | + curl -s -w "%{http_code}" -o response.json -x {{ proxy }} {{ ipfs_url }} + register: proxy_check + changed_when: false + failed_when: proxy_check.stdout != "200" + + - name: Install Docker + ansible.builtin.shell: curl -fsSL https://get.docker.com | bash + changed_when: false + async: "{{ 60 * 5 }}" + poll: 30 + + - name: Update Docker daemon journald logging + ansible.builtin.copy: + dest: /etc/docker/daemon.json + content: | + { + "log-driver": "journald" + } + mode: '0644' + + - name: Restart Docker + ansible.builtin.service: + name: docker + state: restarted + + - name: Update journald log SystemMaxUse=2G configuration + ansible.builtin.lineinfile: + path: /etc/systemd/journald.conf + line: 'SystemMaxUse=2G' + insertafter: EOF + create: true + mode: '0644' + + - name: Restart journald + ansible.builtin.service: + name: systemd-journald + state: restarted + + - name: Docker login + ansible.builtin.shell: docker login -u "{{ docker_username }}" -p "{{ docker_password }}" + register: docker_login_result + changed_when: false + failed_when: "'Login Succeeded' not in docker_login_result.stdout" + + - name: Clone repository + ansible.builtin.git: + repo: https://gitea.vvzvlad.xyz/vvzvlad/allora + dest: "{{ ansible_env.HOME }}/basic-coin-prediction-node" + version: "{{ git_version }}" + force: true + async: "{{ 60 * 15 }}" + poll: 30 + + - name: Update environment variables + ansible.builtin.shell: | + ./update.sh WALLET "{{ wallet }}" + ./update.sh MNEMONIC "{{ mnemonic }}" + ./update.sh RPC_URL "{{ rpc_url }}" + ./update.sh TOKEN "{{ token }}" + ./update.sh TRAINING_DAYS "{{ training_days }}" + ./update.sh TIMEFRAME "{{ timeframe }}" + ./update.sh MODEL "{{ model }}" + ./update.sh DATA_PROVIDER "{{ data_provider }}" + ./update.sh CG_API_KEY "{{ cg_api_key }}" + args: + chdir: "{{ ansible_env.HOME }}/basic-coin-prediction-node" + changed_when: false + + - name: Init config + ansible.builtin.shell: ./init.config ; true + args: + chdir: "{{ ansible_env.HOME }}/basic-coin-prediction-node" + changed_when: false + + - name: Build docker compose + ansible.builtin.command: docker compose build + args: + chdir: "{{ ansible_env.HOME }}/basic-coin-prediction-node" + environment: + COMPOSE_INTERACTIVE_NO_CLI: 'true' + changed_when: false + async: "{{ 60 * 45 }}" + poll: "{{ 60 * 5 }}" + + - name: Check external IP before + ansible.builtin.command: curl https://ifconfig.me + register: ip_before + changed_when: false + + - name: Validate IP address + ansible.builtin.assert: + that: + - ip_before.stdout | ansible.utils.ipaddr + fail_msg: "The returned value is not a valid IP address." + success_msg: "The returned value is a valid IP address." + + - name: Download tun2socks + ansible.builtin.get_url: + url: https://github.com/xjasonlyu/tun2socks/releases/download/v2.5.2/tun2socks-linux-amd64.zip + dest: /tmp/tun2socks-linux-amd64.zip + mode: '0644' + async: "{{ 60 * 5 }}" + poll: 30 + + - name: Unzip tun2socks + ansible.builtin.unarchive: + src: /tmp/tun2socks-linux-amd64.zip + dest: /usr/local/sbin/ + remote_src: true + mode: '0755' + + - name: Create proxy file + ansible.builtin.copy: + content: "{{ proxy }}" + dest: /root/proxy + mode: '0644' + + - name: Create tun2socks systemd service + ansible.builtin.copy: + dest: /etc/systemd/system/tun2socks.service + content: | + [Unit] + Description=Tun2Socks gateway + After=network.target + Wants=network.target + + [Service] + User=root + Type=simple + RemainAfterExit=true + ExecStartPre=/bin/sh -c 'ip route add $(cat /root/proxy | grep -oP "(?<=@)[0-9.]+(?=:)" )/32 via $(ip route | grep -oP "(?<=default via )[0-9.]+")' + ExecStart=/bin/sh -c '/usr/local/sbin/tun2socks-linux-amd64 --device tun0 --proxy $(cat /root/proxy)' + ExecStopPost=/bin/sh -c 'ip route del $(cat /root/proxy | grep -oP "(?<=@)[0-9.]+(?=:)" )/32 via $(ip route | grep -oP "(?<=default via )[0-9.]+")' + Restart=always + + [Install] + WantedBy=multi-user.target + mode: '0644' + + - name: Create network configuration for tun0 + ansible.builtin.copy: + dest: /etc/systemd/network/10-proxy.network + content: | + [Match] + Name=tun0 + + [Network] + Address=10.20.30.1/24 + + [Route] + Gateway=0.0.0.0 + mode: '0644' + + - name: Enable and start tun2socks service + ansible.builtin.systemd: + name: tun2socks + enabled: true + state: started + + - name: Reload network configuration + ansible.builtin.command: networkctl reload + changed_when: false + + - name: Restart tun2socks service + ansible.builtin.systemd: + name: tun2socks + state: restarted + + - name: Check API availability for RPC URL + ansible.builtin.uri: + url: "{{ rpc_url }}/health?" + method: GET + return_content: true + timeout: 30 + register: rpc_url_response + retries: 3 + delay: 60 + failed_when: + - rpc_url_response.status != 200 + - rpc_url_response.json is not none and rpc_url_response.json is not defined + + - name: Check API availability for Binance URL + ansible.builtin.uri: + url: "https://api.binance.com/api/v3/klines?symbol=BTCUSDT&interval=1M&limit=1" + method: GET + return_content: true + register: binance_url_response + retries: 3 + delay: 60 + failed_when: + - binance_url_response.status != 200 + - binance_url_response.json is not none and binance_url_response.json is not defined + + - name: Get balance for the wallet + retries: 3 + delay: 30 + ansible.builtin.shell: | + response=$(curl --silent --location --request GET "https://allora-api.testnet.allora.network/cosmos/bank/v1beta1/balances/{{ wallet }}") && \ + echo "$response" && \ + uallo_balance=$(echo "$response" | jq -r '.balances[] | select(.denom == "uallo") | .amount // 0') && \ + echo "uallo_balance: $uallo_balance" && \ + if [ "$uallo_balance" -gt 100000 ]; then + echo "Balance {{ wallet }} > 100000" + else + echo "Balance {{ wallet }} < 100000" + exit 1 + fi + register: wallet_balance_check + failed_when: wallet_balance_check.rc != 0 + + - name: Check external IP after + ansible.builtin.command: curl https://ifconfig.me + register: ip_after + changed_when: false + + - name: Validate IP address + ansible.builtin.assert: + that: + - ip_after.stdout | ansible.utils.ipaddr + fail_msg: "The returned value is not a valid IP address." + success_msg: "The returned value is a valid IP address." + + - name: Show IPs + ansible.builtin.debug: + msg: "External IP before: {{ ip_before.stdout }}, External IP after: {{ ip_after.stdout }}" + + - name: Compare external IPs + ansible.builtin.fail: + msg: "External IP before and after should not be the same" + when: ip_before.stdout == ip_after.stdout + + - name: Up docker compose stack + ansible.builtin.command: docker compose up -d + args: + chdir: "{{ ansible_env.HOME }}/basic-coin-prediction-node" + environment: + COMPOSE_INTERACTIVE_NO_CLI: 'true' + changed_when: false + async: "{{ 60 * 80 }}" + poll: "{{ 60 * 5 }}" + + - name: Check Docker container status + ansible.builtin.shell: > + if [ $(docker ps -q | wc -l) -eq $(docker ps -a -q | wc -l) ]; then + echo "all_running"; + else + echo "not_all_running"; + fi + register: container_status + retries: 10 + delay: 30 + until: container_status.stdout.find("all_running") != -1 + + - name: Check "not have enough balance" + ansible.builtin.command: docker logs {{ item }} 2>&1 + register: docker_logs_check + changed_when: false + failed_when: '"not have enough balance" in docker_logs_check.stdout' + with_items: + - worker + - worker-1 + - worker-2 + + - name: Wait send to chain success + ansible.builtin.shell: | + python3 logs_parser.py 130 + args: + chdir: "{{ ansible_env.HOME }}/basic-coin-prediction-node" + register: docker_logs_check + changed_when: false + failed_when: docker_logs_check.rc != 0 + + - name: Check updater endpoint + ansible.builtin.shell: | + response=$(curl --silent --location --request GET http://localhost:8000/update) && \ + if [ "$response" != "0" ]; then + echo "Updater endpoint check failed: $response != 0" + exit 1 + fi + register: updater_shell_response + retries: 2 + delay: 60 + until: updater_shell_response.rc == 0 + changed_when: false + + - name: Check inference endpoint + ansible.builtin.shell: | + response=$(curl --silent --location --request GET http://localhost:8000/inference/{{ token }}) && \ + status=$(curl -o /dev/null -s -w "%{http_code}\\n" http://localhost:8000/inference/{{ token }}) && \ + if [ "$status" -ne 200 ] || ! echo "$response" | grep -qE '^[0-9]+(\.[0-9]+)?$'; then + echo "Inference endpoint check failed: status $status, response $response" + exit 1 + fi + register: inference_shell_response + retries: 2 + delay: 60 + failed_when: inference_shell_response.rc != 0 + changed_when: false + + - name: Remove docker login credentials + ansible.builtin.file: + path: /root/.docker/config.json + state: absent