From 121800c5c85486ac7ce5634523bd2ee2bb3837fb Mon Sep 17 00:00:00 2001 From: Ubuntu Date: Thu, 28 May 2026 18:59:58 +0000 Subject: [PATCH 01/10] Modernize Ansible deployment for 2.19+ - use ipam_pool_id for AWS EIP allocation from IPAM pools - make masternode roles compatible with strict boolean conditionals - handle empty regular masternode lists and HPMN-only networks - default dashd wallet enablement safely and add start-at-task support - add required Dashmate Tenderdash p2p allowlistOnly setting - harden ZeroSSL SSM restore for missing values and suppress key logging --- ansible/deploy.yml | 576 +++++++++--------- ansible/roles/dashd/tasks/main.yml | 8 +- ansible/roles/dashmate/defaults/main.yml | 1 + ansible/roles/dashmate/tasks/ssl/zerossl.yml | 25 +- .../roles/dashmate/templates/dashmate.json.j2 | 3 +- ansible/roles/mn_createprotx/tasks/main.yml | 4 +- .../roles/mn_find_collateral/tasks/main.yml | 2 +- ansible/roles/mn_init/tasks/main.yml | 8 +- ansible/roles/mn_protx_config/tasks/main.yml | 16 +- ansible/roles/mn_unban/tasks/main.yml | 4 +- lib/cli/ansible.sh | 1 + terraform/aws/.terraform.lock.hcl | 60 +- terraform/aws/instances.tf | 56 +- terraform/aws/main.tf | 14 +- 14 files changed, 404 insertions(+), 374 deletions(-) diff --git a/ansible/deploy.yml b/ansible/deploy.yml index 60633a9a3..141432121 100644 --- a/ansible/deploy.yml +++ b/ansible/deploy.yml @@ -1,294 +1,302 @@ --- -# Bootstrap - -- name: Remove latency on masternodes before deployment starts - hosts: masternodes,hp_masternodes - become: true - roles: - - remove_fake_latency - tags: - - full_deploy - - maintenance - -- name: Set up swap and aws environment vars - hosts: all - gather_facts: true - become: true - pre_tasks: - - name: Check if inside AWS - ansible.builtin.uri: - url: http://169.254.169.254/latest/meta-data - timeout: 2 - register: aws_uri_check - failed_when: false - - name: Set AWS environment variable - ansible.builtin.set_fact: - is_aws_environment: '{{ aws_uri_check.status == 200 }}' - roles: - - role: aws - when: is_aws_environment - - swap - tags: - - full_deploy - - infra_setup - -- name: Setup VPN - hosts: vpn - become: true - roles: - - role: openvpn - when: openvpn_enabled - tags: - - full_deploy - - infra_setup - - vpn - -- name: Setup jq, Python, CWAgent and Docker - hosts: all - become: true - pre_tasks: - - name: Update apt cache and install jq - ansible.builtin.apt: - pkg: - - acl - - jq - - unzip - update_cache: true - vars: - pip_package: python3-pip - pip_install_packages: - - name: docker - version: "6.0.1" - - name: docker-compose - version: "1.29.2" - - name: requests - version: "2.31.0" - roles: - - geerlingguy.pip - - role: geerlingguy.docker - vars: - docker_apt_arch: "{{ 'amd64' if ansible_architecture == 'x86_64' else 'arm64' }}" - docker_install_compose: false - docker_users: - - ubuntu - - docker_options - - eternal_terminal - - cwagent - tags: - - full_deploy - - infra_setup - -- name: Configure tcpdump - hosts: all - become: true - roles: - - role: tcpdumpd - when: dashd_network_logging != 0 - tags: - - full_deploy - - infra_setup - -- name: Setup logs - hosts: logs_nodes - become: true - roles: - - elastic_stack - - metricbeat - tags: - - full_deploy - - infra_setup - -- name: Setup load tester - hosts: load_test - become: true - roles: - - role: protobuf_compiler - - role: load_tool - tags: - - full_deploy - - infra_setup - -- name: Setup metrics - hosts: metrics - become: true - roles: - - metrics - - metricbeat - tags: - - full_deploy - - infra_setup - -- name: Set up miners - hosts: miners - become: true - roles: - - role: dash_cli - - role: dashd - tags: - - dashd - - core_filebeat - - metricbeat - tags: - - full_deploy - -- name: Set up mixers - hosts: mixer_nodes - become: true - roles: - - role: dash_cli - - role: dashd - tags: - - dashd - - core_filebeat - - metricbeat - tags: - - full_deploy - -- name: Set up core and tenderdash on seed nodes - hosts: seed_nodes - become: true - pre_tasks: - - name: Set node variables for seed nodes - ansible.builtin.set_fact: - node: "{{ seed_nodes[inventory_hostname] }}" - mode: "seed" - tags: always - when: inventory_hostname in seed_nodes - roles: - - role: dash_cli - - role: dashd - dashd_listen: true - dashd_zmq: true - dashd_indexes: true - tags: - - dashd - - role: tenderdash - - core_filebeat - - metricbeat - tags: - - full_deploy - - update_seed_nodes - -- name: Set up core on masternodes - hosts: masternodes - become: true - pre_tasks: - - name: Check inventory for masternodes - ansible.builtin.set_fact: - masternode: "{{ masternodes[inventory_hostname] }}" - tags: always - when: inventory_hostname in masternodes - - name: Fail if no masternodes present - ansible.builtin.fail: - msg: Masternode not defined in network config - when: masternode is not defined - roles: - - role: dash_cli - - role: dashd - dashd_listen: true - dashd_zmq: true - dashd_indexes: true - tags: - - dashd - - mn_status_report - - core_filebeat - - metricbeat - tags: - - full_deploy - - core_update - - update_masternodes - - -# Start network - -- name: Generate first block to leave IBD mode - hosts: seed-1 - become: true - roles: - - role: generate_firstblock - when: dash_network == "devnet" or dash_network == "regtest" - tags: - - full_deploy - -- name: Start miner - hosts: miners - become: true - roles: - - role: dashd_generate_miner - when: dash_network != "mainnet" - tags: - - full_deploy - -- name: Setup faucet and insight - hosts: web - become: true - roles: - - multifaucet - - role: dash_cli - - role: dashd - dashd_indexes: true - dashd_zmq: true - dashd_listen: true - - insight - - role: status_dashboard - when: dash_network == "devnet" - - core_filebeat - - metricbeat - tags: - - full_deploy - - web +# RESUME MODE: earlier completed plays commented out for devnet-rescue resume. +# Restore from ansible/deploy.yml.pre-resume-* after this run. + +# --- +# +# # Bootstrap +# +# - name: Remove latency on masternodes before deployment starts +# hosts: masternodes,hp_masternodes +# become: true +# roles: +# - remove_fake_latency +# tags: +# - full_deploy +# - maintenance +# +# - name: Set up swap and aws environment vars +# hosts: all +# gather_facts: true +# become: true +# pre_tasks: +# - name: Check if inside AWS +# ansible.builtin.uri: +# url: http://169.254.169.254/latest/meta-data +# timeout: 2 +# register: aws_uri_check +# failed_when: false +# - name: Set AWS environment variable +# ansible.builtin.set_fact: +# is_aws_environment: '{{ aws_uri_check.status == 200 }}' +# roles: +# - role: aws +# when: is_aws_environment +# - swap +# tags: +# - full_deploy +# - infra_setup +# +# - name: Setup VPN +# hosts: vpn +# become: true +# roles: +# - role: openvpn +# when: openvpn_enabled +# tags: +# - full_deploy +# - infra_setup +# - vpn +# +# - name: Setup jq, Python, CWAgent and Docker +# hosts: all +# become: true +# pre_tasks: +# - name: Update apt cache and install jq +# ansible.builtin.apt: +# pkg: +# - acl +# - jq +# - unzip +# update_cache: true +# vars: +# pip_package: python3-pip +# pip_install_packages: +# - name: docker +# version: "6.0.1" +# - name: docker-compose +# version: "1.29.2" +# - name: requests +# version: "2.31.0" +# roles: +# - geerlingguy.pip +# - role: geerlingguy.docker +# vars: +# docker_apt_arch: "{{ 'amd64' if ansible_architecture == 'x86_64' else 'arm64' }}" +# docker_install_compose: false +# docker_users: +# - ubuntu +# - docker_options +# - eternal_terminal +# - cwagent +# tags: +# - full_deploy +# - infra_setup +# +# - name: Configure tcpdump +# hosts: all +# become: true +# roles: +# - role: tcpdumpd +# when: dashd_network_logging != 0 +# tags: +# - full_deploy +# - infra_setup +# +# - name: Setup logs +# hosts: logs_nodes +# become: true +# roles: +# - elastic_stack +# - metricbeat +# tags: +# - full_deploy +# - infra_setup +# +# - name: Setup load tester +# hosts: load_test +# become: true +# roles: +# - role: protobuf_compiler +# - role: load_tool +# tags: +# - full_deploy +# - infra_setup +# +# - name: Setup metrics +# hosts: metrics +# become: true +# roles: +# - metrics +# - metricbeat +# tags: +# - full_deploy +# - infra_setup + +# RESUME MODE: completed miner/seed/start-network plays commented out after successful run. +# - name: Set up miners +# hosts: miners +# become: true +# roles: +# - role: dash_cli +# - role: dashd +# enable_wallet: true +# tags: +# - dashd +# - core_filebeat +# - metricbeat +# tags: +# - full_deploy +# +# - name: Set up mixers +# hosts: mixer_nodes +# become: true +# roles: +# - role: dash_cli +# - role: dashd +# tags: +# - dashd +# - core_filebeat +# - metricbeat +# tags: +# - full_deploy +# +# - name: Set up core and tenderdash on seed nodes +# hosts: seed_nodes +# become: true +# pre_tasks: +# - name: Set node variables for seed nodes +# ansible.builtin.set_fact: +# node: "{{ seed_nodes[inventory_hostname] }}" +# mode: "seed" +# tags: always +# when: inventory_hostname in seed_nodes +# roles: +# - role: dash_cli +# - role: dashd +# dashd_listen: true +# dashd_zmq: true +# dashd_indexes: true +# tags: +# - dashd +# - role: tenderdash +# - core_filebeat +# - metricbeat +# tags: +# - full_deploy +# - update_seed_nodes +# +# - name: Set up core on masternodes +# hosts: masternodes +# become: true +# pre_tasks: +# - name: Check inventory for masternodes +# ansible.builtin.set_fact: +# masternode: "{{ masternodes[inventory_hostname] }}" +# tags: always +# when: inventory_hostname in masternodes +# - name: Fail if no masternodes present +# ansible.builtin.fail: +# msg: Masternode not defined in network config +# when: masternode is not defined +# roles: +# - role: dash_cli +# - role: dashd +# dashd_listen: true +# dashd_zmq: true +# dashd_indexes: true +# tags: +# - dashd +# - mn_status_report +# - core_filebeat +# - metricbeat +# tags: +# - full_deploy +# - core_update +# - update_masternodes +# +# +# # Start network +# +# - name: Generate first block to leave IBD mode +# hosts: seed-1 +# become: true +# roles: +# - role: generate_firstblock +# when: dash_network == "devnet" or dash_network == "regtest" +# tags: +# - full_deploy +# +# - name: Start miner +# hosts: miners +# become: true +# roles: +# - role: dashd_generate_miner +# when: dash_network != "mainnet" +# tags: +# - full_deploy -- name: Deploy status monitoring to masternodes - hosts: masternodes,hp_masternodes - become: true - gather_facts: false - strategy: free - roles: - - status_monitoring - tags: - - full_deploy - - dashmate_deploy - - status_dashboard +# - name: Setup faucet and insight +# hosts: web +# become: true +# roles: + # multifaucet skipped for devnet-rescue resume; not needed and DB wait failed. + # - multifaucet +# - role: dash_cli +# - role: dashd +# dashd_indexes: true +# dashd_zmq: true +# dashd_listen: true +# - insight +# - role: status_dashboard +# when: dash_network == "devnet" +# - core_filebeat +# - metricbeat +# tags: +# - full_deploy +# - web + +# - name: Deploy status monitoring to masternodes +# hosts: masternodes,hp_masternodes +# become: true +# gather_facts: false +# strategy: free +# roles: +# - status_monitoring +# tags: +# - full_deploy +# - dashmate_deploy +# - status_dashboard -- name: Set up wallets - hosts: wallet_nodes - become: true - roles: - - role: dash_cli - - role: dashd - dashd_indexes: true - dashd_zmq: true - enable_wallet: true - tags: - - dashd - - core_filebeat - - metricbeat - tags: - - full_deploy - - core_update +# - name: Set up wallets +# hosts: wallet_nodes +# become: true +# roles: +# - role: dash_cli +# - role: dashd +# dashd_indexes: true +# dashd_zmq: true +# enable_wallet: true +# tags: +# - dashd +# - core_filebeat +# - metricbeat +# tags: +# - full_deploy +# - core_update # Register masternodes and set sporks -- name: Register masternodes - hosts: wallet_nodes - become: true - roles: - - role: mn_init - mnlist: "{{ masternodes }}" - funding_amount: "{{ masternode_collaterals.mn | int }}" - tags: - - full_deploy - - unban_masternodes - -- name: Update inventory with protx values - hosts: wallet_nodes - roles: - - role: mn_protx_config - mnlist: "{{ masternodes }}" - tags: - - full_deploy - - unban_masternodes +# - name: Register masternodes +# hosts: wallet_nodes +# become: true +# roles: +# - role: mn_init +# mnlist: "{{ masternodes }}" +# funding_amount: "{{ masternode_collaterals.mn | int }}" +# tags: +# - full_deploy +# - unban_masternodes + +# - name: Update inventory with protx values +# hosts: wallet_nodes +# roles: +# - role: mn_protx_config +# mnlist: "{{ masternodes }}" +# tags: +# - full_deploy +# - unban_masternodes - name: Register HP masternodes hosts: wallet_nodes diff --git a/ansible/roles/dashd/tasks/main.yml b/ansible/roles/dashd/tasks/main.yml index 43ed00c7e..5967223cd 100644 --- a/ansible/roles/dashd/tasks/main.yml +++ b/ansible/roles/dashd/tasks/main.yml @@ -101,7 +101,7 @@ block: | wallet={{ wallet_rpc_wallet_faucet }} wallet={{ wallet_rpc_wallet_mno }} - when: enable_wallet is true and wallet_exists.stat.exists is true + when: (enable_wallet | default(false) | bool) and wallet_exists.stat.exists is true # TODO: why does this always take exactly 30 seconds on first deploy? - name: Start dash core @@ -126,14 +126,14 @@ cmd: dash-cli createwallet {{ wallet_rpc_wallet_faucet }} creates: "{{ dashd_home }}/.dashcore/{{ dash_network_name if dash_network == 'devnet' else 'testnet3' }}/wallets/{{ wallet_rpc_wallet_faucet }}/wallet.dat" register: create_faucet_wallet_result - when: enable_wallet is true + when: enable_wallet | default(false) | bool - name: Create mno wallet ansible.builtin.command: cmd: dash-cli createwallet {{ wallet_rpc_wallet_mno }} creates: "{{ dashd_home }}/.dashcore/{{ dash_network_name if dash_network == 'devnet' else 'testnet3' }}/wallets/{{ wallet_rpc_wallet_mno }}/wallet.dat" register: create_mno_wallet_result - when: enable_wallet is true + when: enable_wallet | default(false) | bool - name: Load wallets on startup ansible.builtin.blockinfile: @@ -147,5 +147,5 @@ - name: Import faucet privkey ansible.builtin.command: dash-cli -rpcwallet={{ wallet_rpc_wallet_faucet }} importprivkey {{ faucet_privkey }} register: result - when: enable_wallet is true + when: enable_wallet | default(false) | bool changed_when: result.rc == 0 diff --git a/ansible/roles/dashmate/defaults/main.yml b/ansible/roles/dashmate/defaults/main.yml index 3418fa238..fc77c4419 100644 --- a/ansible/roles/dashmate/defaults/main.yml +++ b/ansible/roles/dashmate/defaults/main.yml @@ -55,6 +55,7 @@ dashmate_platform_drive_tenderdash_p2p_send_rate: 5120000 dashmate_platform_drive_tenderdash_p2p_recv_rate: 5120000 dashmate_platform_drive_tenderdash_p2p_max_connections: 64 dashmate_platform_drive_tenderdash_p2p_max_outgoing_connections: 30 +dashmate_platform_drive_tenderdash_p2p_allowlist_only: false dashmate_platform_drive_tenderdash_mempool_size: 5000 dashmate_platform_drive_tenderdash_mempool_cache_size: 10000 dashmate_platform_drive_tenderdash_mempool_max_txs_bytes: 536870912 # 500Mb, default: 1073741824 diff --git a/ansible/roles/dashmate/tasks/ssl/zerossl.yml b/ansible/roles/dashmate/tasks/ssl/zerossl.yml index eb33d5641..eff308fbc 100644 --- a/ansible/roles/dashmate/tasks/ssl/zerossl.yml +++ b/ansible/roles/dashmate/tasks/ssl/zerossl.yml @@ -23,7 +23,7 @@ delegate_to: localhost become: false ansible.builtin.set_fact: - dashmate_zerossl_ssm_certificate_id: "{{ lookup('aws_ssm', '{{ dashmate_zerossl_ssm_path }}-id', on_missing='skip') }}" + dashmate_zerossl_ssm_certificate_id: "{{ lookup('aws_ssm', dashmate_zerossl_ssm_path ~ '-id', on_missing='skip') | default('', true) }}" - name: Set ZeroSSL certificate ID to dashmate config from SSM if not set ansible.builtin.command: >- @@ -37,7 +37,7 @@ register: dashmate_zerossl_id changed_when: dashmate_zerossl_id.rc == 0 when: - - dashmate_zerossl_ssm_certificate_id != '' + - (dashmate_zerossl_ssm_certificate_id | default('', true) | length) > 0 - dashmate_zerossl_config_certificate_id is not defined # Copy ZeroSSL files if they are not present @@ -48,9 +48,10 @@ register: zero_ssl_files - name: Get ZeroSSL CSR and private key from SSM + no_log: true ansible.builtin.copy: dest: '{{ dashmate_zerossl_keys_path }}/{{ item }}' - content: "{{ lookup('aws_ssm', '{{ dashmate_zerossl_ssm_path }}-{{ item }}', on_missing='skip') }}" + content: "{{ lookup('aws_ssm', dashmate_zerossl_ssm_path ~ '-' ~ item, on_missing='skip') | default('', true) }}" owner: '{{ dashmate_user }}' group: '{{ dashmate_group }}' mode: "0644" @@ -59,7 +60,7 @@ - '{{ dashmate_zerossl_csr_file_name }}' when: > not zero_ssl_files.stat.exists and - dashmate_zerossl_ssm_certificate_id != '' + (dashmate_zerossl_ssm_certificate_id | default('', true) | length) > 0 # Create a new ZeroSSL certificate if it is not present # or download bundle if it's not exist @@ -79,7 +80,7 @@ register: dashmate_obtain changed_when: dashmate_obtain.rc == 0 when: > - dashmate_zerossl_ssm_certificate_id == '' or + (dashmate_zerossl_ssm_certificate_id | default('', true) | length) == 0 or not zero_ssl_bundle_file.stat.exists # Save new ZeroSSL information to SSM @@ -108,25 +109,28 @@ value: '{{ dashmate_zerossl_config_certificate_id }}' when: - dashmate_zerossl_config_certificate_id is defined - - dashmate_zerossl_ssm_certificate_id != dashmate_zerossl_config_certificate_id + - (dashmate_zerossl_ssm_certificate_id | default('', true)) != dashmate_zerossl_config_certificate_id - name: Read new generated ZeroSSL private key file to variable + no_log: true ansible.builtin.slurp: src: '{{ dashmate_zerossl_keys_path }}/{{ dashmate_zerossl_private_key_file_name }}' register: dashmate_zerossl_private_key_file when: - dashmate_zerossl_config_certificate_id is defined - - dashmate_zerossl_ssm_certificate_id != dashmate_zerossl_config_certificate_id + - (dashmate_zerossl_ssm_certificate_id | default('', true)) != dashmate_zerossl_config_certificate_id - name: Read new generated ZeroSSL CSR file to variable + no_log: true ansible.builtin.slurp: src: '{{ dashmate_zerossl_keys_path }}/{{ dashmate_zerossl_csr_file_name }}' register: dashmate_zerossl_csr_file when: - dashmate_zerossl_config_certificate_id is defined - - dashmate_zerossl_ssm_certificate_id != dashmate_zerossl_config_certificate_id + - (dashmate_zerossl_ssm_certificate_id | default('', true)) != dashmate_zerossl_config_certificate_id - name: Set new generated ZeroSSL CSR and private key files + no_log: true ansible.builtin.set_fact: dashmate_zerossl_files: - name: "{{ dashmate_zerossl_private_key_file_name }}" @@ -135,9 +139,10 @@ content: '{{ dashmate_zerossl_csr_file.content | b64decode }}' when: - dashmate_zerossl_config_certificate_id is defined - - dashmate_zerossl_ssm_certificate_id != dashmate_zerossl_config_certificate_id + - (dashmate_zerossl_ssm_certificate_id | default('', true)) != dashmate_zerossl_config_certificate_id - name: Update ZeroSSL private key and CSR files in AWS SSM parameter store + no_log: true delegate_to: localhost become: false community.aws.ssm_parameter: @@ -146,4 +151,4 @@ loop: '{{ dashmate_zerossl_files }}' when: - dashmate_zerossl_config_certificate_id is defined - - dashmate_zerossl_ssm_certificate_id != dashmate_zerossl_config_certificate_id + - (dashmate_zerossl_ssm_certificate_id | default('', true)) != dashmate_zerossl_config_certificate_id diff --git a/ansible/roles/dashmate/templates/dashmate.json.j2 b/ansible/roles/dashmate/templates/dashmate.json.j2 index 8cfcfc937..1f4017edd 100644 --- a/ansible/roles/dashmate/templates/dashmate.json.j2 +++ b/ansible/roles/dashmate/templates/dashmate.json.j2 @@ -384,7 +384,8 @@ "sendRate": {{dashmate_platform_drive_tenderdash_p2p_send_rate}}, "recvRate": {{dashmate_platform_drive_tenderdash_p2p_recv_rate}}, "maxConnections": {{dashmate_platform_drive_tenderdash_p2p_max_connections}}, - "maxOutgoingConnections": {{dashmate_platform_drive_tenderdash_p2p_max_outgoing_connections}} + "maxOutgoingConnections": {{dashmate_platform_drive_tenderdash_p2p_max_outgoing_connections}}, + "allowlistOnly": {{ dashmate_platform_drive_tenderdash_p2p_allowlist_only | default(false) | to_json }} }, "mempool": { "cacheSize": {{dashmate_platform_drive_tenderdash_mempool_cache_size}}, diff --git a/ansible/roles/mn_createprotx/tasks/main.yml b/ansible/roles/mn_createprotx/tasks/main.yml index 36d421620..9460e474a 100644 --- a/ansible/roles/mn_createprotx/tasks/main.yml +++ b/ansible/roles/mn_createprotx/tasks/main.yml @@ -35,7 +35,7 @@ - name: Get names of registered masternodes ansible.builtin.set_fact: registered_masternode_names: "{{ registered_masternode_names + [item] }}" - when: get_protx_list_result.stdout | from_json | json_query("[?state.ownerAddress=='" + mnlist[item].owner.address + "']") + when: (get_protx_list_result.stdout | from_json | json_query("[?state.ownerAddress=='" + mnlist[item].owner.address + "']") | length) > 0 with_items: '{{ masternode_names }}' - name: List of registered masternodes @@ -45,4 +45,4 @@ - name: Fail if masternodes were not registered ansible.builtin.fail: msg: ProTX transactions were not created - when: masternode_names | difference(registered_masternode_names) + when: (masternode_names | difference(registered_masternode_names) | length) > 0 diff --git a/ansible/roles/mn_find_collateral/tasks/main.yml b/ansible/roles/mn_find_collateral/tasks/main.yml index 3bcf4bcba..46e8016f8 100644 --- a/ansible/roles/mn_find_collateral/tasks/main.yml +++ b/ansible/roles/mn_find_collateral/tasks/main.yml @@ -15,7 +15,7 @@ cmd: 'find-collateral.py "-rpcwallet={{ wallet_rpc_wallet_mno }}" {{ masternode.collateral.address }} {{ funding_amount }} {{ find_protx }}' register: r failed_when: false - changed_when: r.rc + changed_when: r.rc != 0 - name: Update collateral_ok ansible.builtin.set_fact: diff --git a/ansible/roles/mn_init/tasks/main.yml b/ansible/roles/mn_init/tasks/main.yml index 9a8ff529a..40d3303e0 100644 --- a/ansible/roles/mn_init/tasks/main.yml +++ b/ansible/roles/mn_init/tasks/main.yml @@ -121,7 +121,7 @@ - name: Get names of registered masternodes ansible.builtin.set_fact: registered_masternode_names: '{{ registered_masternode_names + [item] }}' - when: get_protx_list_result.stdout | from_json | json_query("[?state.ownerAddress=='" + mnlist[item].owner.address + "']") + when: (get_protx_list_result.stdout | from_json | json_query("[?state.ownerAddress=='" + mnlist[item].owner.address + "']") | length) > 0 with_items: '{{ mn_names }}' - name: Registered masternodes list @@ -151,7 +151,7 @@ - name: Get names of banned masternodes ansible.builtin.set_fact: banned_masternode_names: '{{ banned_masternode_names + [item] }}' - when: banned_masternodes_list | json_query("[?state.ownerAddress=='" + mnlist[item].owner.address + "']") + when: (banned_masternodes_list | json_query("[?state.ownerAddress=='" + mnlist[item].owner.address + "']") | length) > 0 with_items: '{{ registered_masternode_names }}' - name: Banned masternodes list @@ -168,9 +168,9 @@ ansible.builtin.set_fact: misconfigured_masternode_names: '{{ misconfigured_masternode_names + [item] }}' when: > - get_protx_list_result.stdout | from_json | + ((get_protx_list_result.stdout | from_json | json_query("[?state.ownerAddress=='" + mnlist[item].owner.address + "']") | - json_query("[?state.service!='" + hostvars[item].public_ip + ":" + dashd_port | string + "']") + json_query("[?state.service!='" + hostvars[item].public_ip + ":" + dashd_port | string + "']")) | length) > 0 with_items: '{{ registered_masternode_names }}' # We should probably subtract the banned masternodes from this list so we don't unban twice diff --git a/ansible/roles/mn_protx_config/tasks/main.yml b/ansible/roles/mn_protx_config/tasks/main.yml index 475e97d05..33c997fc5 100644 --- a/ansible/roles/mn_protx_config/tasks/main.yml +++ b/ansible/roles/mn_protx_config/tasks/main.yml @@ -4,16 +4,28 @@ ansible.builtin.set_fact: registered_masternode_names: [] +- name: Build masternode names list from mnlist + ansible.builtin.set_fact: + mn_names: >- + {{ + (mnlist | dict2items | map(attribute='key') | list) + if (mnlist is mapping) + else [] + }} + - name: Get list of ProTx transactions from the wallet ansible.builtin.command: dash-cli -rpcwallet={{ wallet_rpc_wallet_mno }} protx list wallet true register: get_protx_list_result changed_when: get_protx_list_result.stdout | from_json | length > 0 + when: mn_names | length > 0 - name: Get names of registered masternodes ansible.builtin.set_fact: registered_masternode_names: "{{ registered_masternode_names + [item] }}" - when: get_protx_list_result.stdout | from_json | json_query("[?state.ownerAddress=='" + mnlist[item].owner.address + "']") - with_items: '{{ mnlist }}' + when: + - mn_names | length > 0 + - (get_protx_list_result.stdout | from_json | json_query("[?state.ownerAddress=='" + mnlist[item].owner.address + "']") | length) > 0 + with_items: '{{ mn_names }}' - name: Set empty outer scope variable for protx list ansible.builtin.set_fact: diff --git a/ansible/roles/mn_unban/tasks/main.yml b/ansible/roles/mn_unban/tasks/main.yml index bc7af81c3..b08c0d34f 100644 --- a/ansible/roles/mn_unban/tasks/main.yml +++ b/ansible/roles/mn_unban/tasks/main.yml @@ -44,10 +44,10 @@ - name: Get names of registered masternodes ansible.builtin.set_fact: registered_masternode_names: '{{ registered_masternode_names + [item] }}' - when: get_protx_list_result.stdout | from_json | json_query("[?state.ownerAddress=='" + mnlist[item].owner.address + "']") + when: (get_protx_list_result.stdout | from_json | json_query("[?state.ownerAddress=='" + mnlist[item].owner.address + "']") | length) > 0 with_items: '{{ masternode_names }}' - name: Fail if masternodes were not successfully registered ansible.builtin.fail: msg: ProTX transactions were not created - when: masternode_names | difference(registered_masternode_names) + when: (masternode_names | difference(registered_masternode_names) | length) > 0 diff --git a/lib/cli/ansible.sh b/lib/cli/ansible.sh index 3b3ba092c..bb97865f0 100644 --- a/lib/cli/ansible.sh +++ b/lib/cli/ansible.sh @@ -24,6 +24,7 @@ function ansible_run_playbook() { -e "dash_network_name=$NETWORK_NAME" \ -e "dash_network=$NETWORK" \ -e "dash_devnet_name=$NETWORK_DEVNET_NAME" \ + ${ANSIBLE_START_AT_TASK:+--start-at-task "$ANSIBLE_START_AT_TASK"} \ ${ANSIBLE_ARGS} \ $@ diff --git a/terraform/aws/.terraform.lock.hcl b/terraform/aws/.terraform.lock.hcl index 22651e57c..3b17f27b7 100644 --- a/terraform/aws/.terraform.lock.hcl +++ b/terraform/aws/.terraform.lock.hcl @@ -2,44 +2,45 @@ # Manual edits may be lost in future updates. provider "registry.terraform.io/hashicorp/aws" { - version = "5.21.0" + version = "5.100.0" constraints = "~> 5.21" hashes = [ - "h1:N8sP6VZjHbtgmaCU6BKPox51UIypWXQRal7JMecEXQw=", - "zh:1ba1411e4f8c047950db94c236f146d4590790320c68320b4e56082d8746a507", - "zh:3185e4a34cfcad35dcf11439290a4bd0ad52d462eca2ab5d4940488a2db72833", - "zh:3c6b901f874b4d9a85301a653d0bd507b052992bd84fc81100f4e5f73b1adab7", - "zh:45d3fdbbc5804f295576b7155fdca527dedff17a014ed40c215af3bc60c329db", - "zh:47b64b453d2c373062e47a54f3df33335dc29bce6ddbbf2da9e7be768c560abe", - "zh:5cdf57ffd465288d9732d14ba13b377a8d389e0ba0ce3ac4773fd6fdfc09d6a1", - "zh:81ec4c662581a2446c78da7b27d7e0d5c2e4d50925294789ec13661817f4b5a4", + "h1:wOhTPz6apLBuF7/FYZuCoXRK/MLgrNprZ3vXmq83g5k=", + "zh:054b8dd49f0549c9a7cc27d159e45327b7b65cf404da5e5a20da154b90b8a644", + "zh:0b97bf8d5e03d15d83cc40b0530a1f84b459354939ba6f135a0086c20ebbe6b2", + "zh:1589a2266af699cbd5d80737a0fe02e54ec9cf2ca54e7e00ac51c7359056f274", + "zh:6330766f1d85f01ae6ea90d1b214b8b74cc8c1badc4696b165b36ddd4cc15f7b", + "zh:7c8c2e30d8e55291b86fcb64bdf6c25489d538688545eb48fd74ad622e5d3862", + "zh:99b1003bd9bd32ee323544da897148f46a527f622dc3971af63ea3e251596342", "zh:9b12af85486a96aedd8d7984b0ff811a4b42e3d88dad1a3fb4c0b580d04fa425", - "zh:ac248464fd4ce1f020c05f27e3182532a7d1af4b8185a4b4be8b906b30b0ca5a", - "zh:bbbedc6b6eaffcce0b31b397d607464f0c21c1b9406182163d504d3f392cc68d", - "zh:c2afc111f9503829ed055e2ae91d873670c57bd16acc1a3246ac3957f6998d4e", - "zh:cd3c8175b2152848113482da70e5b9c7cb4c951f2046fc0b832715300bd88b97", - "zh:cf89b0c09d426d489f9477209d4084e64ad1b598036284fa688b41de626b58e6", - "zh:d9d127637c3b9ff6e2d0a2c30f54bd48ab1de34f725a5df1a6a3d039b021e636", - "zh:dccca1090e4054d6558218406385fb0421ab4ac3b75e121641973be481a81f01", + "zh:9f8b909d3ec50ade83c8062290378b1ec553edef6a447c56dadc01a99f4eaa93", + "zh:aaef921ff9aabaf8b1869a86d692ebd24fbd4e12c21205034bb679b9caf883a2", + "zh:ac882313207aba00dd5a76dbd572a0ddc818bb9cbf5c9d61b28fe30efaec951e", + "zh:bb64e8aff37becab373a1a0cc1080990785304141af42ed6aa3dd4913b000421", + "zh:dfe495f6621df5540d9c92ad40b8067376350b005c637ea6efac5dc15028add4", + "zh:f0ddf0eaf052766cfe09dea8200a946519f653c384ab4336e2a4a64fdd6310e9", + "zh:f1b7e684f4c7ae1eed272b6de7d2049bb87a0275cb04dbb7cda6636f600699c9", + "zh:ff461571e3f233699bf690db319dfe46aec75e58726636a0d97dd9ac6e32fb70", ] } provider "registry.terraform.io/hashicorp/null" { - version = "3.2.4" + version = "3.3.0" hashes = [ - "h1:hkf5w5B6q8e2A42ND2CjAvgvSN3puAosDmOJb3zCVQM=", - "zh:59f6b52ab4ff35739647f9509ee6d93d7c032985d9f8c6237d1f8a59471bbbe2", + "h1:kncZNn+Pz/CbjPNyvvFogaXuYNQre69RN8CnApzY3ac=", + "zh:021748b5ea3b5f6956f2e75c42c5cdc113b391fb98ac71364a4965d23b37000f", + "zh:3b27956f8541d46704fda234e0d535c2ae2a4b33411848b1ee262a1ec03568b0", + "zh:3de4ed47d6d0f4d8edba4a5092c7c9799950eda63989d8d0d2586e6afcb0aa20", + "zh:57ed8935c7d56dbc91cf2673534582cacfaab7a2f105f51d9f797e99df0c0c47", + "zh:58e176ba1d142827089e30e0711e007309a9f2726e8881986da5026e9778fdf4", + "zh:5949c4a3d4a93f841f155cdb7e991c087e637145c1630572e21948224f8f4923", + "zh:76d60f366b743003c1b085afa769b45b2198ee919927e45807d7d44fb42c067d", "zh:78d5eefdd9e494defcb3c68d282b8f96630502cac21d1ea161f53cfe9bb483b3", - "zh:795c897119ff082133150121d39ff26cb5f89a730a2c8c26f3a9c1abf81a9c43", - "zh:7b9c7b16f118fbc2b05a983817b8ce2f86df125857966ad356353baf4bff5c0a", - "zh:85e33ab43e0e1726e5f97a874b8e24820b6565ff8076523cc2922ba671492991", - "zh:9d32ac3619cfc93eb3c4f423492a8e0f79db05fec58e449dee9b2d5873d5f69f", - "zh:9e15c3c9dd8e0d1e3731841d44c34571b6c97f5b95e8296a45318b94e5287a6e", - "zh:b4c2ab35d1b7696c30b64bf2c0f3a62329107bd1a9121ce70683dec58af19615", - "zh:c43723e8cc65bcdf5e0c92581dcbbdcbdcf18b8d2037406a5f2033b1e22de442", - "zh:ceb5495d9c31bfb299d246ab333f08c7fb0d67a4f82681fbf47f2a21c3e11ab5", - "zh:e171026b3659305c558d9804062762d168f50ba02b88b231d20ec99578a6233f", - "zh:ed0fe2acdb61330b01841fa790be00ec6beaac91d41f311fb8254f74eb6a711f", + "zh:79cd1bab1261a07f84e917191d7ddc4340ac5f5524283767256f7ffd7f87caf0", + "zh:8ec9083038cf710b30e319eaa467c9df7fa52bbd9969b61053a35bc2cdd2e0a6", + "zh:a6e502cb579685ab7aeb886c2bb11ddd9cfed74b41008592d57cbc3351a9218b", + "zh:acb74d6b4f66ff6acfcda315df802a7432170ef3955c9b432cb4580767004006", + "zh:f0ce55d8d9ffdb33dab612b1246f9bab060a9d54fc32ce2b4a038646155660af", ] } @@ -47,6 +48,7 @@ provider "registry.terraform.io/hashicorp/random" { version = "3.5.1" constraints = "~> 3.5.1" hashes = [ + "h1:6FVyQ/aG6tawPam6B+oFjgdidKd83uG9n7dOSQ66HBA=", "h1:VSnd9ZIPyfKHOObuQCaKfnjIHRtR7qTw19Rz8tJxm+k=", "zh:04e3fbd610cb52c1017d282531364b9c53ef72b6bc533acb2a90671957324a64", "zh:119197103301ebaf7efb91df8f0b6e0dd31e6ff943d231af35ee1831c599188d", diff --git a/terraform/aws/instances.tf b/terraform/aws/instances.tf index 57be76ca0..f63f0899f 100644 --- a/terraform/aws/instances.tf +++ b/terraform/aws/instances.tf @@ -239,9 +239,9 @@ resource "aws_instance" "masternode_arm" { } resource "aws_eip" "hpmn_arm_eip" { - instance = null - count = (var.create_eip || var.byoip_pool_id != "") ? var.hp_masternode_arm_count : 0 - public_ipv4_pool = var.byoip_pool_id != "" ? var.byoip_pool_id : null + instance = null + count = (var.create_eip || var.byoip_pool_id != "") ? var.hp_masternode_arm_count : 0 + ipam_pool_id = var.byoip_pool_id != "" ? var.byoip_pool_id : null tags = { Name = "dn-${terraform.workspace}-hp-masternode-arm-${count.index + 1}" DashNetwork = terraform.workspace @@ -249,9 +249,9 @@ resource "aws_eip" "hpmn_arm_eip" { } resource "aws_eip" "hpmn_amd_eip" { - instance = null - count = (var.create_eip || var.byoip_pool_id != "") ? var.hp_masternode_amd_count : 0 - public_ipv4_pool = var.byoip_pool_id != "" ? var.byoip_pool_id : null + instance = null + count = (var.create_eip || var.byoip_pool_id != "") ? var.hp_masternode_amd_count : 0 + ipam_pool_id = var.byoip_pool_id != "" ? var.byoip_pool_id : null tags = { Name = "dn-${terraform.workspace}-hp-masternode-amd-${count.index + 1}" DashNetwork = terraform.workspace @@ -261,10 +261,10 @@ resource "aws_eip" "hpmn_amd_eip" { resource "aws_instance" "hp_masternode_amd" { count = var.hp_masternode_amd_count - ami = data.aws_ami.ubuntu_amd.id - instance_type = "t3.medium" - key_name = aws_key_pair.auth.id - iam_instance_profile = aws_iam_instance_profile.monitoring.name + ami = data.aws_ami.ubuntu_amd.id + instance_type = "t3.medium" + key_name = aws_key_pair.auth.id + iam_instance_profile = aws_iam_instance_profile.monitoring.name associate_public_ip_address = true vpc_security_group_ids = [ @@ -302,10 +302,10 @@ resource "aws_instance" "hp_masternode_amd" { resource "aws_instance" "hp_masternode_arm" { count = var.hp_masternode_arm_count - ami = data.aws_ami.ubuntu_arm.id - instance_type = "t4g.medium" - key_name = aws_key_pair.auth.id - iam_instance_profile = aws_iam_instance_profile.monitoring.name + ami = data.aws_ami.ubuntu_arm.id + instance_type = "t4g.medium" + key_name = aws_key_pair.auth.id + iam_instance_profile = aws_iam_instance_profile.monitoring.name associate_public_ip_address = true vpc_security_group_ids = [ @@ -316,7 +316,7 @@ resource "aws_instance" "hp_masternode_arm" { subnet_id = element(aws_subnet.public.*.id, count.index) - + root_block_device { volume_size = var.hpmn_node_disk_size volume_type = var.volume_type @@ -336,7 +336,7 @@ resource "aws_instance" "hp_masternode_arm" { lifecycle { ignore_changes = [ami, root_block_device[0].volume_size] - + } } @@ -358,8 +358,8 @@ resource "aws_eip_association" "amd_eip_assoc" { # BYOIP EIPs for non-HPMN instance types resource "aws_eip" "mn_amd_eip" { - count = var.byoip_pool_id != "" ? var.masternode_amd_count : 0 - public_ipv4_pool = var.byoip_pool_id + count = var.byoip_pool_id != "" ? var.masternode_amd_count : 0 + ipam_pool_id = var.byoip_pool_id tags = { Name = "dn-${terraform.workspace}-masternode-amd-${count.index + 1}" DashNetwork = terraform.workspace @@ -373,8 +373,8 @@ resource "aws_eip_association" "mn_amd_eip_assoc" { } resource "aws_eip" "mn_arm_eip" { - count = var.byoip_pool_id != "" ? var.masternode_arm_count : 0 - public_ipv4_pool = var.byoip_pool_id + count = var.byoip_pool_id != "" ? var.masternode_arm_count : 0 + ipam_pool_id = var.byoip_pool_id tags = { Name = "dn-${terraform.workspace}-masternode-arm-${count.index + 1}" DashNetwork = terraform.workspace @@ -388,8 +388,8 @@ resource "aws_eip_association" "mn_arm_eip_assoc" { } resource "aws_eip" "web_eip" { - count = var.byoip_pool_id != "" ? var.web_count : 0 - public_ipv4_pool = var.byoip_pool_id + count = var.byoip_pool_id != "" ? var.web_count : 0 + ipam_pool_id = var.byoip_pool_id tags = { Name = "dn-${terraform.workspace}-web-${count.index + 1}" DashNetwork = terraform.workspace @@ -403,8 +403,8 @@ resource "aws_eip_association" "web_eip_assoc" { } resource "aws_eip" "wallet_eip" { - count = var.byoip_pool_id != "" ? var.wallet_count : 0 - public_ipv4_pool = var.byoip_pool_id + count = var.byoip_pool_id != "" ? var.wallet_count : 0 + ipam_pool_id = var.byoip_pool_id tags = { Name = "dn-${terraform.workspace}-dashd-wallet-${count.index + 1}" DashNetwork = terraform.workspace @@ -418,8 +418,8 @@ resource "aws_eip_association" "wallet_eip_assoc" { } resource "aws_eip" "seed_eip" { - count = var.byoip_pool_id != "" ? var.seed_count : 0 - public_ipv4_pool = var.byoip_pool_id + count = var.byoip_pool_id != "" ? var.seed_count : 0 + ipam_pool_id = var.byoip_pool_id tags = { Name = "dn-${terraform.workspace}-seed-${count.index + 1}" DashNetwork = terraform.workspace @@ -433,8 +433,8 @@ resource "aws_eip_association" "seed_eip_assoc" { } resource "aws_eip" "miner_eip" { - count = var.byoip_pool_id != "" ? var.miner_count : 0 - public_ipv4_pool = var.byoip_pool_id + count = var.byoip_pool_id != "" ? var.miner_count : 0 + ipam_pool_id = var.byoip_pool_id tags = { Name = "dn-${terraform.workspace}-miner-${count.index + 1}" DashNetwork = terraform.workspace diff --git a/terraform/aws/main.tf b/terraform/aws/main.tf index 3845f5ad8..386150daf 100644 --- a/terraform/aws/main.tf +++ b/terraform/aws/main.tf @@ -86,11 +86,11 @@ resource "aws_subnet" "public" { cidr_block = var.subnet_public_cidr[count.index] map_public_ip_on_launch = true availability_zone = data.aws_availability_zones.available.names[count.index] - + # IPv6 support ipv6_cidr_block = var.enable_ipv6 ? cidrsubnet(aws_vpc.default.ipv6_cidr_block, 8, count.index) : null assign_ipv6_address_on_creation = var.enable_ipv6 - + tags = { Name = "${terraform.workspace}-public${count.index}" DashNetwork = terraform.workspace @@ -249,8 +249,8 @@ resource "aws_route53_record" "seeds" { type = "A" alias { - name = aws_lb.seed.dns_name - zone_id = aws_lb.seed.zone_id + name = aws_lb.seed.dns_name + zone_id = aws_lb.seed.zone_id # TODO: enable health checks # https://www.envoyproxy.io/docs/envoy/latest/operations/admin#get--ready evaluate_target_health = false @@ -446,7 +446,7 @@ resource "random_shuffle" "dns_ips" { (var.create_eip || var.byoip_pool_id != "") ? aws_eip.hpmn_arm_eip.*.public_ip : aws_instance.hp_masternode_arm.*.public_ip, (var.create_eip || var.byoip_pool_id != "") ? aws_eip.hpmn_amd_eip.*.public_ip : aws_instance.hp_masternode_amd.*.public_ip ) - ) > local.dns_record_length ? local.dns_record_length : length( + ) > local.dns_record_length ? local.dns_record_length : length( concat( (var.create_eip || var.byoip_pool_id != "") ? aws_eip.hpmn_arm_eip.*.public_ip : aws_instance.hp_masternode_arm.*.public_ip, (var.create_eip || var.byoip_pool_id != "") ? aws_eip.hpmn_amd_eip.*.public_ip : aws_instance.hp_masternode_amd.*.public_ip @@ -507,8 +507,8 @@ resource "aws_eip" "vpn" { count = var.vpn_enabled ? 1 : 0 - instance = aws_instance.vpn[0].id - public_ipv4_pool = var.byoip_pool_id != "" ? var.byoip_pool_id : null + instance = aws_instance.vpn[0].id + ipam_pool_id = var.byoip_pool_id != "" ? var.byoip_pool_id : null tags = { Name = "dn-${terraform.workspace}-vpn" From 098e541f9c2f66a306195f1809ce6b0ddf9c6404 Mon Sep 17 00:00:00 2001 From: Ubuntu Date: Thu, 28 May 2026 21:24:54 +0000 Subject: [PATCH 02/10] Add reusable pre-baked base AMI support --- .gitignore | 4 + README.md | 30 +- ansible/deploy.yml | 582 +++++++++--------- ansible/group_vars/all | 4 + ansible/prebake-common.yml | 35 ++ bin/build-base-image | 70 +++ bin/deploy | 5 + flake.nix | 1 + .../generateTerraformConfig.js | 2 + packer/dash-network-base.pkr.hcl | 109 ++++ terraform/aws/instances.tf | 26 +- terraform/aws/main.tf | 6 + terraform/aws/variables.tf | 12 + 13 files changed, 581 insertions(+), 305 deletions(-) create mode 100644 ansible/prebake-common.yml create mode 100755 bin/build-base-image create mode 100644 packer/dash-network-base.pkr.hcl diff --git a/.gitignore b/.gitignore index 664736298..19d4ac2c7 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ # Ansible /ansible/*.retry +/ansible/deploy.yml.pre-* # Node.JS node_modules @@ -10,6 +11,9 @@ node_modules # Terraform /terraform/aws/.terraform +# Local run logs +/logs/ + # Jet Brains .idea diff --git a/README.md b/README.md index 4213297c6..6ade72286 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,8 @@ Dash Core developers to assist in Dash Platform development. ## Installation 1. [Install Docker](https://docs.docker.com/install/) -2. Download tool: +2. [Install Packer](https://developer.hashicorp.com/packer/install) if you plan to build pre-baked base AMIs. +3. Download tool: Using `wget`: @@ -208,3 +209,30 @@ aws route53 create-hosted-zone --name networks.domain.tld --caller-reference 123 ``` Please note the values of these, as they will be needed in the network config files. + +## Pre-baked base AMIs + +Deployments can use architecture-specific base AMIs with common host setup already installed. This avoids repeating the slow, identical bootstrap work on every deploy. + +Build both AMIs with Packer: + +```bash +bin/build-base-image --profile= --region=us-west-2 +``` + +Then set the generated AMI IDs in the network tfvars: + +```hcl +base_ami_amd64_id = "ami-..." +base_ami_arm64_id = "ami-..." +``` + +When provisioning instances launched from those AMIs, skip the baked common setup: + +```bash +./bin/deploy -p --prebaked-common-setup +``` + +The baked image includes swap, common packages, Python/pip Docker dependencies, Docker, Docker daemon options, and Eternal Terminal. Per-host runtime configuration still runs during deploy, including hostnames, `/etc/hosts`, CloudWatch Agent configuration, VPN, logs, metrics, and node-specific Dash services. + +Changing AMI IDs does not replace existing instances automatically because Terraform ignores AMI drift for instance resources. Recreate or taint instances to adopt a new base image. diff --git a/ansible/deploy.yml b/ansible/deploy.yml index 141432121..708fb46fc 100644 --- a/ansible/deploy.yml +++ b/ansible/deploy.yml @@ -1,302 +1,302 @@ --- -# RESUME MODE: earlier completed plays commented out for devnet-rescue resume. -# Restore from ansible/deploy.yml.pre-resume-* after this run. - -# --- -# -# # Bootstrap -# -# - name: Remove latency on masternodes before deployment starts -# hosts: masternodes,hp_masternodes -# become: true -# roles: -# - remove_fake_latency -# tags: -# - full_deploy -# - maintenance -# -# - name: Set up swap and aws environment vars -# hosts: all -# gather_facts: true -# become: true -# pre_tasks: -# - name: Check if inside AWS -# ansible.builtin.uri: -# url: http://169.254.169.254/latest/meta-data -# timeout: 2 -# register: aws_uri_check -# failed_when: false -# - name: Set AWS environment variable -# ansible.builtin.set_fact: -# is_aws_environment: '{{ aws_uri_check.status == 200 }}' -# roles: -# - role: aws -# when: is_aws_environment -# - swap -# tags: -# - full_deploy -# - infra_setup -# -# - name: Setup VPN -# hosts: vpn -# become: true -# roles: -# - role: openvpn -# when: openvpn_enabled -# tags: -# - full_deploy -# - infra_setup -# - vpn -# -# - name: Setup jq, Python, CWAgent and Docker -# hosts: all -# become: true -# pre_tasks: -# - name: Update apt cache and install jq -# ansible.builtin.apt: -# pkg: -# - acl -# - jq -# - unzip -# update_cache: true -# vars: -# pip_package: python3-pip -# pip_install_packages: -# - name: docker -# version: "6.0.1" -# - name: docker-compose -# version: "1.29.2" -# - name: requests -# version: "2.31.0" -# roles: -# - geerlingguy.pip -# - role: geerlingguy.docker -# vars: -# docker_apt_arch: "{{ 'amd64' if ansible_architecture == 'x86_64' else 'arm64' }}" -# docker_install_compose: false -# docker_users: -# - ubuntu -# - docker_options -# - eternal_terminal -# - cwagent -# tags: -# - full_deploy -# - infra_setup -# -# - name: Configure tcpdump -# hosts: all -# become: true -# roles: -# - role: tcpdumpd -# when: dashd_network_logging != 0 -# tags: -# - full_deploy -# - infra_setup -# -# - name: Setup logs -# hosts: logs_nodes -# become: true -# roles: -# - elastic_stack -# - metricbeat -# tags: -# - full_deploy -# - infra_setup -# -# - name: Setup load tester -# hosts: load_test -# become: true -# roles: -# - role: protobuf_compiler -# - role: load_tool -# tags: -# - full_deploy -# - infra_setup -# -# - name: Setup metrics -# hosts: metrics -# become: true -# roles: -# - metrics -# - metricbeat -# tags: -# - full_deploy -# - infra_setup - -# RESUME MODE: completed miner/seed/start-network plays commented out after successful run. -# - name: Set up miners -# hosts: miners -# become: true -# roles: -# - role: dash_cli -# - role: dashd -# enable_wallet: true -# tags: -# - dashd -# - core_filebeat -# - metricbeat -# tags: -# - full_deploy -# -# - name: Set up mixers -# hosts: mixer_nodes -# become: true -# roles: -# - role: dash_cli -# - role: dashd -# tags: -# - dashd -# - core_filebeat -# - metricbeat -# tags: -# - full_deploy -# -# - name: Set up core and tenderdash on seed nodes -# hosts: seed_nodes -# become: true -# pre_tasks: -# - name: Set node variables for seed nodes -# ansible.builtin.set_fact: -# node: "{{ seed_nodes[inventory_hostname] }}" -# mode: "seed" -# tags: always -# when: inventory_hostname in seed_nodes -# roles: -# - role: dash_cli -# - role: dashd -# dashd_listen: true -# dashd_zmq: true -# dashd_indexes: true -# tags: -# - dashd -# - role: tenderdash -# - core_filebeat -# - metricbeat -# tags: -# - full_deploy -# - update_seed_nodes -# -# - name: Set up core on masternodes -# hosts: masternodes -# become: true -# pre_tasks: -# - name: Check inventory for masternodes -# ansible.builtin.set_fact: -# masternode: "{{ masternodes[inventory_hostname] }}" -# tags: always -# when: inventory_hostname in masternodes -# - name: Fail if no masternodes present -# ansible.builtin.fail: -# msg: Masternode not defined in network config -# when: masternode is not defined -# roles: -# - role: dash_cli -# - role: dashd -# dashd_listen: true -# dashd_zmq: true -# dashd_indexes: true -# tags: -# - dashd -# - mn_status_report -# - core_filebeat -# - metricbeat -# tags: -# - full_deploy -# - core_update -# - update_masternodes -# -# -# # Start network -# -# - name: Generate first block to leave IBD mode -# hosts: seed-1 -# become: true -# roles: -# - role: generate_firstblock -# when: dash_network == "devnet" or dash_network == "regtest" -# tags: -# - full_deploy -# -# - name: Start miner -# hosts: miners -# become: true -# roles: -# - role: dashd_generate_miner -# when: dash_network != "mainnet" -# tags: -# - full_deploy +# Bootstrap -# - name: Setup faucet and insight -# hosts: web -# become: true -# roles: - # multifaucet skipped for devnet-rescue resume; not needed and DB wait failed. +- name: Remove latency on masternodes before deployment starts + hosts: masternodes,hp_masternodes + become: true + roles: + - remove_fake_latency + tags: + - full_deploy + - maintenance + +- name: Set up swap and aws environment vars + hosts: all + gather_facts: true + become: true + pre_tasks: + - name: Check if inside AWS + ansible.builtin.uri: + url: http://169.254.169.254/latest/meta-data + timeout: 2 + register: aws_uri_check + failed_when: false + - name: Set AWS environment variable + ansible.builtin.set_fact: + is_aws_environment: '{{ aws_uri_check.status == 200 }}' + roles: + - role: aws + when: is_aws_environment + - role: swap + when: not (prebaked_common_setup | default(false) | bool) + tags: + - full_deploy + - infra_setup + +- name: Setup VPN + hosts: vpn + become: true + roles: + - role: openvpn + when: openvpn_enabled + tags: + - full_deploy + - infra_setup + - vpn + +- name: Setup jq, Python, CWAgent and Docker + hosts: all + become: true + pre_tasks: + - name: Update apt cache and install common packages + ansible.builtin.apt: + pkg: + - acl + - jq + - unzip + update_cache: true + when: not (prebaked_common_setup | default(false) | bool) + vars: + pip_package: python3-pip + pip_install_packages: + - name: docker + version: "6.0.1" + - name: docker-compose + version: "1.29.2" + - name: requests + version: "2.31.0" + roles: + - role: geerlingguy.pip + when: not (prebaked_common_setup | default(false) | bool) + - role: geerlingguy.docker + when: not (prebaked_common_setup | default(false) | bool) + vars: + docker_apt_arch: "{{ 'amd64' if ansible_facts['architecture'] == 'x86_64' else 'arm64' }}" + docker_install_compose: false + docker_users: + - ubuntu + - role: docker_options + when: not (prebaked_common_setup | default(false) | bool) + - role: eternal_terminal + when: not (prebaked_common_setup | default(false) | bool) + - cwagent + tags: + - full_deploy + - infra_setup + +- name: Configure tcpdump + hosts: all + become: true + roles: + - role: tcpdumpd + when: dashd_network_logging != 0 + tags: + - full_deploy + - infra_setup + +- name: Setup logs + hosts: logs_nodes + become: true + roles: + - elastic_stack + - metricbeat + tags: + - full_deploy + - infra_setup + +- name: Setup load tester + hosts: load_test + become: true + roles: + - role: protobuf_compiler + - role: load_tool + tags: + - full_deploy + - infra_setup + +- name: Setup metrics + hosts: metrics + become: true + roles: + - metrics + - metricbeat + tags: + - full_deploy + - infra_setup + +- name: Set up miners + hosts: miners + become: true + roles: + - role: dash_cli + - role: dashd + enable_wallet: true + tags: + - dashd + - core_filebeat + - metricbeat + tags: + - full_deploy + +- name: Set up mixers + hosts: mixer_nodes + become: true + roles: + - role: dash_cli + - role: dashd + tags: + - dashd + - core_filebeat + - metricbeat + tags: + - full_deploy + +- name: Set up core and tenderdash on seed nodes + hosts: seed_nodes + become: true + pre_tasks: + - name: Set node variables for seed nodes + ansible.builtin.set_fact: + node: "{{ seed_nodes[inventory_hostname] }}" + mode: "seed" + tags: always + when: inventory_hostname in seed_nodes + roles: + - role: dash_cli + - role: dashd + dashd_listen: true + dashd_zmq: true + dashd_indexes: true + tags: + - dashd + - role: tenderdash + - core_filebeat + - metricbeat + tags: + - full_deploy + - update_seed_nodes + +- name: Set up core on masternodes + hosts: masternodes + become: true + pre_tasks: + - name: Check inventory for masternodes + ansible.builtin.set_fact: + masternode: "{{ masternodes[inventory_hostname] }}" + tags: always + when: inventory_hostname in masternodes + - name: Fail if no masternodes present + ansible.builtin.fail: + msg: Masternode not defined in network config + when: masternode is not defined + roles: + - role: dash_cli + - role: dashd + dashd_listen: true + dashd_zmq: true + dashd_indexes: true + tags: + - dashd + - mn_status_report + - core_filebeat + - metricbeat + tags: + - full_deploy + - core_update + - update_masternodes + + +# Start network + +- name: Generate first block to leave IBD mode + hosts: seed-1 + become: true + roles: + - role: generate_firstblock + when: dash_network == "devnet" or dash_network == "regtest" + tags: + - full_deploy + +- name: Start miner + hosts: miners + become: true + roles: + - role: dashd_generate_miner + when: dash_network != "mainnet" + tags: + - full_deploy + +- name: Setup faucet and insight + hosts: web + become: true + roles: + # Multifaucet is intentionally skipped; current devnets do not need it. # - multifaucet -# - role: dash_cli -# - role: dashd -# dashd_indexes: true -# dashd_zmq: true -# dashd_listen: true -# - insight -# - role: status_dashboard -# when: dash_network == "devnet" -# - core_filebeat -# - metricbeat -# tags: -# - full_deploy -# - web - -# - name: Deploy status monitoring to masternodes -# hosts: masternodes,hp_masternodes -# become: true -# gather_facts: false -# strategy: free -# roles: -# - status_monitoring -# tags: -# - full_deploy -# - dashmate_deploy -# - status_dashboard + - role: dash_cli + - role: dashd + dashd_indexes: true + dashd_zmq: true + dashd_listen: true + - insight + - role: status_dashboard + when: dash_network == "devnet" + - core_filebeat + - metricbeat + tags: + - full_deploy + - web -# - name: Set up wallets -# hosts: wallet_nodes -# become: true -# roles: -# - role: dash_cli -# - role: dashd -# dashd_indexes: true -# dashd_zmq: true -# enable_wallet: true -# tags: -# - dashd -# - core_filebeat -# - metricbeat -# tags: -# - full_deploy -# - core_update +- name: Deploy status monitoring to masternodes + hosts: masternodes,hp_masternodes + become: true + gather_facts: false + strategy: free + roles: + - status_monitoring + tags: + - full_deploy + - dashmate_deploy + - status_dashboard + +- name: Set up wallets + hosts: wallet_nodes + become: true + roles: + - role: dash_cli + - role: dashd + dashd_indexes: true + dashd_zmq: true + enable_wallet: true + tags: + - dashd + - core_filebeat + - metricbeat + tags: + - full_deploy + - core_update # Register masternodes and set sporks -# - name: Register masternodes -# hosts: wallet_nodes -# become: true -# roles: -# - role: mn_init -# mnlist: "{{ masternodes }}" -# funding_amount: "{{ masternode_collaterals.mn | int }}" -# tags: -# - full_deploy -# - unban_masternodes - -# - name: Update inventory with protx values -# hosts: wallet_nodes -# roles: -# - role: mn_protx_config -# mnlist: "{{ masternodes }}" -# tags: -# - full_deploy -# - unban_masternodes +- name: Register masternodes + hosts: wallet_nodes + become: true + roles: + - role: mn_init + mnlist: "{{ masternodes }}" + funding_amount: "{{ masternode_collaterals.mn | int }}" + tags: + - full_deploy + - unban_masternodes + +- name: Update inventory with protx values + hosts: wallet_nodes + roles: + - role: mn_protx_config + mnlist: "{{ masternodes }}" + tags: + - full_deploy + - unban_masternodes - name: Register HP masternodes hosts: wallet_nodes diff --git a/ansible/group_vars/all b/ansible/group_vars/all index 6aa72b711..3fdd72927 100644 --- a/ansible/group_vars/all +++ b/ansible/group_vars/all @@ -4,6 +4,10 @@ evo_services: false node_environment: "production" +# Set to true when instances are launched from an AMI built by ansible/prebake-common.yml. +# This skips slow common package/Docker/swap setup while keeping per-host configuration. +prebaked_common_setup: false + platform_initial_core_chain_locked_height: "" # Install OpenVPN diff --git a/ansible/prebake-common.yml b/ansible/prebake-common.yml new file mode 100644 index 000000000..932ce5e64 --- /dev/null +++ b/ansible/prebake-common.yml @@ -0,0 +1,35 @@ +--- + +- name: Build Dash Network common base image + hosts: all + become: true + gather_facts: true + vars: + swap_space: 2G + pip_package: python3-pip + pip_install_packages: + - name: docker + version: "6.0.1" + - name: docker-compose + version: "1.29.2" + - name: requests + version: "2.31.0" + pre_tasks: + - name: Update apt cache and install common packages + ansible.builtin.apt: + pkg: + - acl + - jq + - unzip + update_cache: true + roles: + - swap + - geerlingguy.pip + - role: geerlingguy.docker + vars: + docker_apt_arch: "{{ 'amd64' if ansible_facts['architecture'] == 'x86_64' else 'arm64' }}" + docker_install_compose: false + docker_users: + - ubuntu + - docker_options + - eternal_terminal diff --git a/bin/build-base-image b/bin/build-base-image new file mode 100755 index 000000000..5872a82f8 --- /dev/null +++ b/bin/build-base-image @@ -0,0 +1,70 @@ +#!/usr/bin/env bash + +set -euo pipefail + +CMD_USAGE="Build pre-baked Dash Network Deploy base AMIs + +Usage: build-base-image [options] + +Options: + --region= AWS region. Default: AWS_REGION/AWS_DEFAULT_REGION/us-west-2 + --profile= AWS profile to pass to Packer + --ami-prefix= AMI name prefix. Default: dash-network-base + --only= Build only amd64 or arm64. Default: both + -h, --help Show help" + +REGION="${AWS_REGION:-${AWS_DEFAULT_REGION:-us-west-2}}" +PROFILE="${AWS_PROFILE:-}" +AMI_PREFIX="dash-network-base" +ONLY="" + +for arg in "$@"; do + case "$arg" in + --region=*) REGION="${arg#*=}" ;; + --profile=*) PROFILE="${arg#*=}" ;; + --ami-prefix=*) AMI_PREFIX="${arg#*=}" ;; + --only=*) ONLY="${arg#*=}" ;; + -h|--help) + echo "$CMD_USAGE" + exit 0 + ;; + *) + echo "Error: invalid argument '$arg'" >&2 + echo "$CMD_USAGE" >&2 + exit 1 + ;; + esac +done + +case "$ONLY" in + "") PACKER_TARGETS=(amd64 arm64) ;; + amd64|arm64) PACKER_TARGETS=("$ONLY") ;; + *) + echo "Error: --only must be amd64 or arm64" >&2 + exit 1 + ;; +esac + +if ! command -v packer >/dev/null 2>&1; then + echo "Error: packer is required to build base AMIs" >&2 + exit 1 +fi + +if [[ -n "$PROFILE" ]]; then + export AWS_PROFILE="$PROFILE" +fi + +packer init packer/dash-network-base.pkr.hcl + +for target in "${PACKER_TARGETS[@]}"; do + case "$target" in + amd64) packer_only=(-only=dash-network-base.amazon-ebs.ubuntu_jammy_amd64) ;; + arm64) packer_only=(-only=dash-network-base.amazon-ebs.ubuntu_jammy_arm64) ;; + esac + + packer build \ + "${packer_only[@]}" \ + -var "region=${REGION}" \ + -var "ami_prefix=${AMI_PREFIX}" \ + packer/dash-network-base.pkr.hcl +done diff --git a/bin/deploy b/bin/deploy index 2c3dd9e2f..c1d7ae858 100755 --- a/bin/deploy +++ b/bin/deploy @@ -21,6 +21,7 @@ Options: --ansible-playbook= - Specify custom playbook. Default: deploy --fast - Fast deployment mode (skip non-critical operations) --skip-image-update - Skip Docker image updates + --prebaked-common-setup - Skip common setup provided by a pre-baked base AMI --core-restart - Include core services in restart (default: platform only) -h --help - Show help" @@ -60,6 +61,10 @@ case ${i} in --skip-image-update) EXTRA_VARS="$EXTRA_VARS -e skip_dashmate_image_update=true" ;; + --prebaked-common-setup) + EXTRA_VARS="$EXTRA_VARS -e prebaked_common_setup=true" + echo "Pre-baked common setup enabled - skipping common package/Docker/swap setup" + ;; --core-restart) EXTRA_VARS="$EXTRA_VARS -e include_core_restart=true" echo "Core restart enabled - will restart both core and platform services" diff --git a/flake.nix b/flake.nix index d72670aa2..0f787fc55 100644 --- a/flake.nix +++ b/flake.nix @@ -19,6 +19,7 @@ # Infrastructure terraform + packer ansible docker-client diff --git a/lib/configGenerator/generateTerraformConfig.js b/lib/configGenerator/generateTerraformConfig.js index 235a62ed3..fe1611319 100644 --- a/lib/configGenerator/generateTerraformConfig.js +++ b/lib/configGenerator/generateTerraformConfig.js @@ -20,6 +20,8 @@ async function generateTerraformConfig( hp_masternode_arm_count: hpMasternodesArmCount, main_domain: '"networks.dash.org"', main_host_arch: '"arm64"', + base_ami_amd64_id: '""', + base_ami_arm64_id: '""', create_eip: false, }; diff --git a/packer/dash-network-base.pkr.hcl b/packer/dash-network-base.pkr.hcl new file mode 100644 index 000000000..f040134ee --- /dev/null +++ b/packer/dash-network-base.pkr.hcl @@ -0,0 +1,109 @@ +packer { + required_plugins { + amazon = { + version = ">= 1.3.0" + source = "github.com/hashicorp/amazon" + } + ansible = { + version = ">= 1.1.0" + source = "github.com/hashicorp/ansible" + } + } +} + +variable "region" { + type = string + default = "us-west-2" +} + + +variable "ami_prefix" { + type = string + default = "dash-network-base" +} + +variable "instance_type_amd64" { + type = string + default = "t3.small" +} + +variable "instance_type_arm64" { + type = string + default = "t4g.small" +} + +locals { + timestamp = regex_replace(timestamp(), "[- TZ:]", "") +} + +source "amazon-ebs" "ubuntu_jammy_amd64" { + region = var.region + instance_type = var.instance_type_amd64 + ssh_username = "ubuntu" + ami_name = "${var.ami_prefix}-amd64-${local.timestamp}" + ami_description = "Dash Network Deploy common base image for amd64" + + source_ami_filter { + filters = { + name = "ubuntu/images/hvm-ssd/ubuntu-jammy-22.04-amd64-server*" + root-device-type = "ebs" + virtualization-type = "hvm" + } + owners = ["099720109477"] + most_recent = true + } + + tags = { + Name = "${var.ami_prefix}-amd64" + Project = "dash-network-deploy" + Architecture = "amd64" + } +} + +source "amazon-ebs" "ubuntu_jammy_arm64" { + region = var.region + instance_type = var.instance_type_arm64 + ssh_username = "ubuntu" + ami_name = "${var.ami_prefix}-arm64-${local.timestamp}" + ami_description = "Dash Network Deploy common base image for arm64" + + source_ami_filter { + filters = { + name = "ubuntu/images/hvm-ssd/ubuntu-jammy-22.04-arm64-server*" + root-device-type = "ebs" + virtualization-type = "hvm" + } + owners = ["099720109477"] + most_recent = true + } + + tags = { + Name = "${var.ami_prefix}-arm64" + Project = "dash-network-deploy" + Architecture = "arm64" + } +} + +build { + name = "dash-network-base" + sources = [ + "source.amazon-ebs.ubuntu_jammy_amd64", + "source.amazon-ebs.ubuntu_jammy_arm64", + ] + + provisioner "ansible" { + playbook_file = "ansible/prebake-common.yml" + galaxy_file = "ansible/requirements.yml" + user = "ubuntu" + use_proxy = false + ansible_env_vars = [ + "ANSIBLE_HOST_KEY_CHECKING=False", + "ANSIBLE_BECOME_TIMEOUT=60", + "ANSIBLE_TIMEOUT=60", + ] + extra_arguments = [ + "--extra-vars", + "ansible_become_timeout=60 ansible_ssh_timeout=60", + ] + } +} diff --git a/terraform/aws/instances.tf b/terraform/aws/instances.tf index f63f0899f..2f2dca007 100644 --- a/terraform/aws/instances.tf +++ b/terraform/aws/instances.tf @@ -7,7 +7,7 @@ resource "aws_instance" "web" { user = "ubuntu" } - ami = var.main_host_arch == "arm64" ? data.aws_ami.ubuntu_arm.id : data.aws_ami.ubuntu_amd.id + ami = local.main_host_ami_id instance_type = var.main_host_arch == "arm64" ? "t4g.small" : "t3.small" key_name = aws_key_pair.auth.id iam_instance_profile = aws_iam_instance_profile.monitoring.name @@ -45,7 +45,7 @@ resource "aws_instance" "web" { resource "aws_instance" "dashd_wallet" { count = var.wallet_count - ami = var.main_host_arch == "arm64" ? data.aws_ami.ubuntu_arm.id : data.aws_ami.ubuntu_amd.id + ami = local.main_host_ami_id instance_type = join(".", [var.main_host_arch == "arm64" ? "t4g" : "t3", var.wallet_node_instance_size]) key_name = aws_key_pair.auth.id iam_instance_profile = aws_iam_instance_profile.monitoring.name @@ -84,7 +84,7 @@ resource "aws_instance" "dashd_wallet" { resource "aws_instance" "seed_node" { count = var.seed_count - ami = var.main_host_arch == "arm64" ? data.aws_ami.ubuntu_arm.id : data.aws_ami.ubuntu_amd.id + ami = local.main_host_ami_id instance_type = var.main_host_arch == "arm64" ? "t4g.small" : "t3.small" key_name = aws_key_pair.auth.id iam_instance_profile = aws_iam_instance_profile.monitoring.name @@ -124,7 +124,7 @@ resource "aws_instance" "seed_node" { resource "aws_instance" "miner" { count = var.miner_count - ami = var.main_host_arch == "arm64" ? data.aws_ami.ubuntu_arm.id : data.aws_ami.ubuntu_amd.id + ami = local.main_host_ami_id instance_type = var.main_host_arch == "arm64" ? "t4g.small" : "t3.small" key_name = aws_key_pair.auth.id iam_instance_profile = aws_iam_instance_profile.monitoring.name @@ -163,7 +163,7 @@ resource "aws_instance" "miner" { resource "aws_instance" "masternode_amd" { count = var.masternode_amd_count - ami = data.aws_ami.ubuntu_amd.id + ami = local.ubuntu_amd_ami_id instance_type = "t3.small" key_name = aws_key_pair.auth.id iam_instance_profile = aws_iam_instance_profile.monitoring.name @@ -203,7 +203,7 @@ resource "aws_instance" "masternode_amd" { resource "aws_instance" "masternode_arm" { count = var.masternode_arm_count - ami = data.aws_ami.ubuntu_arm.id + ami = local.ubuntu_arm_ami_id instance_type = "t4g.small" key_name = aws_key_pair.auth.id iam_instance_profile = aws_iam_instance_profile.monitoring.name @@ -261,7 +261,7 @@ resource "aws_eip" "hpmn_amd_eip" { resource "aws_instance" "hp_masternode_amd" { count = var.hp_masternode_amd_count - ami = data.aws_ami.ubuntu_amd.id + ami = local.ubuntu_amd_ami_id instance_type = "t3.medium" key_name = aws_key_pair.auth.id iam_instance_profile = aws_iam_instance_profile.monitoring.name @@ -302,7 +302,7 @@ resource "aws_instance" "hp_masternode_amd" { resource "aws_instance" "hp_masternode_arm" { count = var.hp_masternode_arm_count - ami = data.aws_ami.ubuntu_arm.id + ami = local.ubuntu_arm_ami_id instance_type = "t4g.medium" key_name = aws_key_pair.auth.id iam_instance_profile = aws_iam_instance_profile.monitoring.name @@ -451,7 +451,7 @@ resource "aws_eip_association" "miner_eip_assoc" { resource "aws_instance" "vpn" { count = var.vpn_enabled ? 1 : 0 - ami = var.main_host_arch == "arm64" ? data.aws_ami.ubuntu_arm.id : data.aws_ami.ubuntu_amd.id + ami = local.main_host_ami_id instance_type = var.main_host_arch == "arm64" ? "t4g.nano" : "t3.nano" key_name = aws_key_pair.auth.id iam_instance_profile = aws_iam_instance_profile.monitoring.name @@ -483,7 +483,7 @@ resource "aws_instance" "vpn" { resource "aws_instance" "mixer" { count = var.mixer_count - ami = var.main_host_arch == "arm64" ? data.aws_ami.ubuntu_arm.id : data.aws_ami.ubuntu_amd.id + ami = local.main_host_ami_id instance_type = var.main_host_arch == "arm64" ? "t4g.medium" : "t3.medium" key_name = aws_key_pair.auth.id iam_instance_profile = aws_iam_instance_profile.monitoring.name @@ -521,7 +521,7 @@ resource "aws_instance" "mixer" { resource "aws_instance" "logs" { count = var.logs_count - ami = data.aws_ami.ubuntu_arm.id + ami = local.ubuntu_arm_ami_id instance_type = join(".", [var.logs_node_instance_type, var.logs_node_instance_size]) key_name = aws_key_pair.auth.id iam_instance_profile = aws_iam_instance_profile.monitoring.name @@ -558,7 +558,7 @@ resource "aws_instance" "logs" { resource "aws_instance" "load_test" { count = var.load_test_count - ami = data.aws_ami.ubuntu_arm.id + ami = local.ubuntu_arm_ami_id instance_type = join(".", [var.load_test_instance_type, var.load_test_instance_size]) key_name = aws_key_pair.auth.id iam_instance_profile = aws_iam_instance_profile.monitoring.name @@ -595,7 +595,7 @@ resource "aws_instance" "load_test" { resource "aws_instance" "metrics" { count = var.metrics_count - ami = data.aws_ami.ubuntu_arm.id + ami = local.ubuntu_arm_ami_id instance_type = join(".", [var.metrics_instance_type, var.metrics_instance_size]) key_name = aws_key_pair.auth.id iam_instance_profile = aws_iam_instance_profile.monitoring.name diff --git a/terraform/aws/main.tf b/terraform/aws/main.tf index 386150daf..1f5779619 100644 --- a/terraform/aws/main.tf +++ b/terraform/aws/main.tf @@ -43,6 +43,12 @@ data "aws_ami" "ubuntu_arm" { # Canonical } +locals { + ubuntu_amd_ami_id = var.base_ami_amd64_id != "" ? var.base_ami_amd64_id : data.aws_ami.ubuntu_amd.id + ubuntu_arm_ami_id = var.base_ami_arm64_id != "" ? var.base_ami_arm64_id : data.aws_ami.ubuntu_arm.id + main_host_ami_id = var.main_host_arch == "arm64" ? local.ubuntu_arm_ami_id : local.ubuntu_amd_ami_id +} + # Create a VPC to launch our instances into resource "aws_vpc" "default" { cidr_block = var.vpc_cidr diff --git a/terraform/aws/variables.tf b/terraform/aws/variables.tf index 2d81b7a4b..94c354e0a 100644 --- a/terraform/aws/variables.tf +++ b/terraform/aws/variables.tf @@ -1,6 +1,18 @@ variable "public_key_path" { } +variable "base_ami_amd64_id" { + description = "Optional pre-baked amd64 AMI ID. Defaults to latest Canonical Ubuntu Jammy amd64 when empty." + type = string + default = "" +} + +variable "base_ami_arm64_id" { + description = "Optional pre-baked arm64 AMI ID. Defaults to latest Canonical Ubuntu Jammy arm64 when empty." + type = string + default = "" +} + variable "dashd_port" { description = "Port for Dash Core nodes" default = 20001 From 79bf96501ca0368fbdcd9aa41990121d20f3bed1 Mon Sep 17 00:00:00 2001 From: Ubuntu Date: Fri, 29 May 2026 05:25:22 +0000 Subject: [PATCH 03/10] Add scheduled base AMI build workflow --- .github/workflows/build-base-images.yml | 130 ++++++++++++++++++++++++ README.md | 2 + 2 files changed, 132 insertions(+) create mode 100644 .github/workflows/build-base-images.yml diff --git a/.github/workflows/build-base-images.yml b/.github/workflows/build-base-images.yml new file mode 100644 index 000000000..b2d274e33 --- /dev/null +++ b/.github/workflows/build-base-images.yml @@ -0,0 +1,130 @@ +name: Build Base AMIs + +on: + workflow_dispatch: + inputs: + region: + description: "AWS region for AMI builds. Defaults to AWS_REGION secret." + required: false + type: string + default: "" + ami_prefix: + description: "AMI name prefix" + required: true + type: string + default: dash-network-base + arch: + description: "Architecture(s) to build" + required: true + type: choice + options: + - both + - amd64 + - arm64 + default: both + schedule: + # Refresh base AMIs weekly so new deploys can use recent Ubuntu/package updates. + - cron: '20 4 * * 0' + +permissions: + contents: read + +jobs: + build: + name: Build Dash Network base AMIs + runs-on: ubuntu-22.04 + timeout-minutes: 120 + concurrency: + group: build-base-amis-${{ inputs.region || 'scheduled' }} + cancel-in-progress: false + + env: + AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + AWS_SESSION_TOKEN: ${{ secrets.AWS_SESSION_TOKEN }} + AWS_REGION: ${{ inputs.region || secrets.AWS_REGION }} + AWS_DEFAULT_REGION: ${{ inputs.region || secrets.AWS_REGION }} + AMI_PREFIX: ${{ inputs.ami_prefix || 'dash-network-base' }} + AMI_ARCH: ${{ inputs.arch || 'both' }} + ANSIBLE_HOST_KEY_CHECKING: "false" + + steps: + - name: Checkout dash-network-deploy + uses: actions/checkout@v4 + + - name: Set up Packer + uses: hashicorp/setup-packer@v3 + + - name: Install controller dependencies + run: | + sudo apt-get update + sudo apt-get install -y python3-pip python3-netaddr jq + python3 -m pip install --upgrade pip + python3 -m pip install ansible-core==2.16.3 jmespath + + - name: Validate AWS configuration + run: | + test -n "$AWS_ACCESS_KEY_ID" + test -n "$AWS_SECRET_ACCESS_KEY" + test -n "$AWS_REGION" + aws sts get-caller-identity --output json >/dev/null + + - name: Initialize and validate Packer template + run: | + packer init packer/dash-network-base.pkr.hcl + packer fmt -check packer/dash-network-base.pkr.hcl + packer validate \ + -var "region=${AWS_REGION}" \ + -var "ami_prefix=${AMI_PREFIX}" \ + packer/dash-network-base.pkr.hcl + + - name: Build base AMIs + run: | + args=(--region="${AWS_REGION}" --ami-prefix="${AMI_PREFIX}") + case "$AMI_ARCH" in + both) ;; + amd64|arm64) args+=(--only="$AMI_ARCH") ;; + *) + echo "Unsupported arch: $AMI_ARCH" >&2 + exit 1 + ;; + esac + + ./bin/build-base-image "${args[@]}" + + - name: Summarize latest base AMIs + run: | + { + echo "## Latest base AMIs" + echo + echo "Region: \`${AWS_REGION}\`" + echo + echo "| Architecture | AMI ID | Name | Created |" + echo "| --- | --- | --- | --- |" + for arch in amd64 arm64; do + if [[ "$AMI_ARCH" != "both" && "$AMI_ARCH" != "$arch" ]]; then + continue + fi + + image_json=$(aws ec2 describe-images \ + --owners self \ + --filters \ + "Name=tag:Project,Values=dash-network-deploy" \ + "Name=tag:Architecture,Values=${arch}" \ + "Name=name,Values=${AMI_PREFIX}-${arch}-*" \ + --query 'sort_by(Images, &CreationDate)[-1]' \ + --output json) + + image_id=$(jq -r '.ImageId // ""' <<< "$image_json") + name=$(jq -r '.Name // ""' <<< "$image_json") + created=$(jq -r '.CreationDate // ""' <<< "$image_json") + + if [[ -z "$image_id" ]]; then + echo "| ${arch} | not found | | |" + else + echo "| ${arch} | \`${image_id}\` | \`${name}\` | ${created} |" + fi + done + echo + echo "Set these in a network tfvars file as \`base_ami_amd64_id\` and \`base_ami_arm64_id\` when you want a deploy to adopt them." + } >> "$GITHUB_STEP_SUMMARY" diff --git a/README.md b/README.md index 6ade72286..03af4577f 100644 --- a/README.md +++ b/README.md @@ -220,6 +220,8 @@ Build both AMIs with Packer: bin/build-base-image --profile= --region=us-west-2 ``` +The `Build Base AMIs` GitHub Actions workflow also refreshes these AMIs weekly and can be run manually for a specific region, prefix, or architecture. It uses the same AWS repository secrets as the deploy workflows: `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, optional `AWS_SESSION_TOKEN`, and `AWS_REGION`. + Then set the generated AMI IDs in the network tfvars: ```hcl From 9dbf2034d920d9992870d73d6609d21ad85a0bb9 Mon Sep 17 00:00:00 2001 From: Ubuntu Date: Fri, 29 May 2026 05:31:04 +0000 Subject: [PATCH 04/10] Fix and speed up fast deploy mode --- README.md | 2 + ansible/deploy.yml | 64 ++++++++++++------- ansible/group_vars/all | 5 ++ ansible/roles/dashmate/defaults/main.yml | 4 +- ansible/roles/dashmate/tasks/main.yml | 80 ++++++++++++------------ bin/deploy | 4 +- 6 files changed, 95 insertions(+), 64 deletions(-) diff --git a/README.md b/README.md index 03af4577f..5cc9ee79f 100644 --- a/README.md +++ b/README.md @@ -79,6 +79,8 @@ dash-network deploy You may pass the `--only-infrastructure` or `--only-provisioning` option to target either infrastructure or software provisioning workflows. +For faster updates to an existing deployment, pass `--fast`. Fast mode skips Docker image updates plus slow observability/logging setup such as CloudWatch Agent, Elastic/logs, filebeat, metricbeat, metrics, and status dashboards. + To destroy an available Dash Network, use the `destroy` command: ```bash diff --git a/ansible/deploy.yml b/ansible/deploy.yml index 708fb46fc..65254f037 100644 --- a/ansible/deploy.yml +++ b/ansible/deploy.yml @@ -80,7 +80,8 @@ when: not (prebaked_common_setup | default(false) | bool) - role: eternal_terminal when: not (prebaked_common_setup | default(false) | bool) - - cwagent + - role: cwagent + when: not (skip_observability_setup | default(false) | bool) tags: - full_deploy - infra_setup @@ -99,8 +100,10 @@ hosts: logs_nodes become: true roles: - - elastic_stack - - metricbeat + - role: elastic_stack + when: not (skip_observability_setup | default(false) | bool) + - role: metricbeat + when: not (skip_observability_setup | default(false) | bool) tags: - full_deploy - infra_setup @@ -119,8 +122,10 @@ hosts: metrics become: true roles: - - metrics - - metricbeat + - role: metrics + when: not (skip_observability_setup | default(false) | bool) + - role: metricbeat + when: not (skip_observability_setup | default(false) | bool) tags: - full_deploy - infra_setup @@ -134,8 +139,10 @@ enable_wallet: true tags: - dashd - - core_filebeat - - metricbeat + - role: core_filebeat + when: not (skip_observability_setup | default(false) | bool) + - role: metricbeat + when: not (skip_observability_setup | default(false) | bool) tags: - full_deploy @@ -147,8 +154,10 @@ - role: dashd tags: - dashd - - core_filebeat - - metricbeat + - role: core_filebeat + when: not (skip_observability_setup | default(false) | bool) + - role: metricbeat + when: not (skip_observability_setup | default(false) | bool) tags: - full_deploy @@ -171,8 +180,10 @@ tags: - dashd - role: tenderdash - - core_filebeat - - metricbeat + - role: core_filebeat + when: not (skip_observability_setup | default(false) | bool) + - role: metricbeat + when: not (skip_observability_setup | default(false) | bool) tags: - full_deploy - update_seed_nodes @@ -199,8 +210,10 @@ tags: - dashd - mn_status_report - - core_filebeat - - metricbeat + - role: core_filebeat + when: not (skip_observability_setup | default(false) | bool) + - role: metricbeat + when: not (skip_observability_setup | default(false) | bool) tags: - full_deploy - core_update @@ -240,9 +253,13 @@ dashd_listen: true - insight - role: status_dashboard - when: dash_network == "devnet" - - core_filebeat - - metricbeat + when: + - dash_network == "devnet" + - not (skip_observability_setup | default(false) | bool) + - role: core_filebeat + when: not (skip_observability_setup | default(false) | bool) + - role: metricbeat + when: not (skip_observability_setup | default(false) | bool) tags: - full_deploy - web @@ -253,7 +270,8 @@ gather_facts: false strategy: free roles: - - status_monitoring + - role: status_monitoring + when: not (skip_observability_setup | default(false) | bool) tags: - full_deploy - dashmate_deploy @@ -270,8 +288,10 @@ enable_wallet: true tags: - dashd - - core_filebeat - - metricbeat + - role: core_filebeat + when: not (skip_observability_setup | default(false) | bool) + - role: metricbeat + when: not (skip_observability_setup | default(false) | bool) tags: - full_deploy - core_update @@ -371,12 +391,12 @@ when: inventory_hostname in hp_masternodes roles: - role: dash_cli - when: not skip_dashmate_image_update | default(false) + when: not (fast_mode | default(false) | bool) - role: dashmate - role: mn_status_report - when: not skip_dashmate_image_update | default(false) + when: not (fast_mode | default(false) | bool) - role: metricbeat - when: not skip_dashmate_image_update | default(false) + when: not (skip_observability_setup | default(false) | bool) tags: - full_deploy - dashmate_deploy diff --git a/ansible/group_vars/all b/ansible/group_vars/all index 3fdd72927..6c84efd71 100644 --- a/ansible/group_vars/all +++ b/ansible/group_vars/all @@ -8,6 +8,11 @@ node_environment: "production" # This skips slow common package/Docker/swap setup while keeping per-host configuration. prebaked_common_setup: false +# Fast deploys are intended for existing hosts and skip slow/non-critical setup. +# These are set by `bin/deploy --fast` and can also be passed manually. +fast_mode: false +skip_observability_setup: false + platform_initial_core_chain_locked_height: "" # Install OpenVPN diff --git a/ansible/roles/dashmate/defaults/main.yml b/ansible/roles/dashmate/defaults/main.yml index fc77c4419..b68d0b514 100644 --- a/ansible/roles/dashmate/defaults/main.yml +++ b/ansible/roles/dashmate/defaults/main.yml @@ -110,6 +110,7 @@ restart_chunk_size: 10 restart_delay_minutes: 10 # Optimization flags - set these to skip certain operations +fast_mode: false # Skip slow existing-host setup and use direct platform restarts force_dashmate_rebuild: false # Force rebuild dashmate from source force_dashmate_reinstall: false # Force reinstall dashmate package force_ssl_regenerate: false # Force regenerate SSL certificates @@ -121,4 +122,5 @@ force_filebeat_restart: false # Force restart filebeat force_rust_deps: false # Force reinstall Rust dependencies force_git_clone: false # Force re-clone git repository force_build: false # Force rebuild from source -skip_dashmate_image_update: false # Skip Docker image updates (use with caution) +skip_dashmate_image_update: false # Skip Docker image updates +skip_observability_setup: false # Skip logs/filebeat/metricbeat/monitoring setup diff --git a/ansible/roles/dashmate/tasks/main.yml b/ansible/roles/dashmate/tasks/main.yml index 8f801b36b..3a4dcd827 100644 --- a/ansible/roles/dashmate/tasks/main.yml +++ b/ansible/roles/dashmate/tasks/main.yml @@ -58,19 +58,19 @@ register: dashmate_group_check failed_when: false changed_when: false - when: not (skip_dashmate_image_update | default(false)) + when: not (fast_mode | default(false) | bool) - name: Check if dashmate user exists ansible.builtin.command: "id {{ dashmate_user }}" register: dashmate_user_check failed_when: false changed_when: false - when: not (skip_dashmate_image_update | default(false)) + when: not (fast_mode | default(false) | bool) - name: Set user/group existence facts ansible.builtin.set_fact: dashmate_group_exists: >- - {%- if skip_dashmate_image_update | default(false) -%} + {%- if fast_mode | default(false) | bool -%} true {%- elif dashmate_group_check is defined and dashmate_group_check.rc is defined -%} {{ dashmate_group_check.rc == 0 }} @@ -78,7 +78,7 @@ false {%- endif -%} dashmate_user_exists: >- - {%- if skip_dashmate_image_update | default(false) -%} + {%- if fast_mode | default(false) | bool -%} true {%- elif dashmate_user_check is defined and dashmate_user_check.rc is defined -%} {{ dashmate_user_check.rc == 0 }} @@ -93,16 +93,17 @@ User check failed: {{ dashmate_user_check.failed | default('undefined') }} Group check msg: {{ dashmate_group_check.msg | default('no msg') }} User check msg: {{ dashmate_user_check.msg | default('no msg') }} + fast_mode: {{ fast_mode | default('undefined') }} skip_dashmate_image_update: {{ skip_dashmate_image_update | default('undefined') }} dashmate_group_exists: {{ dashmate_group_exists }} dashmate_user_exists: {{ dashmate_user_exists }} - when: not (skip_dashmate_image_update | default(false)) + when: not (fast_mode | default(false) | bool) - name: Create dashmate group ansible.builtin.group: name: '{{ dashmate_group }}' when: - - not (skip_dashmate_image_update | default(false)) + - not (fast_mode | default(false) | bool) - not dashmate_group_exists - name: Create dashmate user @@ -115,7 +116,7 @@ shell: /bin/bash umask: '0002' when: - - not (skip_dashmate_image_update | default(false)) + - not (fast_mode | default(false) | bool) - not dashmate_user_exists - name: Update dashmate existence facts after user creation @@ -123,7 +124,7 @@ dashmate_group_exists: true dashmate_user_exists: true when: - - not (skip_dashmate_image_update | default(false)) + - not (fast_mode | default(false) | bool) - not dashmate_user_exists - name: Add dashmate user to docker group @@ -132,7 +133,7 @@ groups: docker append: true when: - - not (skip_dashmate_image_update | default(false)) + - not (fast_mode | default(false) | bool) - dashmate_user_exists # ============================================================================ @@ -153,13 +154,13 @@ - '{{ dashmate_config_dir }}' - '{{ dashmate_logs_dir }}' when: - - not (skip_dashmate_image_update | default(false)) + - not (fast_mode | default(false) | bool) - dashmate_user_exists - name: Fix ownership of any existing files in dashmate directories ansible.builtin.command: chown -R {{ dashmate_user }}:{{ dashmate_group }} {{ dashmate_home }} when: - - not (skip_dashmate_image_update | default(false)) + - not (fast_mode | default(false) | bool) - dashmate_user_exists changed_when: false @@ -233,7 +234,7 @@ - name: Configure logs ansible.builtin.import_tasks: ./logs.yml when: - - not (skip_dashmate_image_update | default(false)) + - not (skip_observability_setup | default(false) | bool) - not (logrotate_config_stat.stat.exists | default(false)) or force_logs_config | default(false) - dashmate_user_exists # Only configure logs if user exists @@ -275,7 +276,7 @@ register: dashmate_config_result ignore_errors: true changed_when: true - when: not (skip_dashmate_image_update | default(false)) + when: not (fast_mode | default(false) | bool) - name: Get ZeroSSL certificate ID from config ansible.builtin.command: "{{ dashmate_cmd }} config get platform.gateway.ssl.providerConfigs.zerossl.id" @@ -287,7 +288,7 @@ register: dashmate_zerossl_id_result changed_when: dashmate_zerossl_id_result.rc == 0 when: - - not (skip_dashmate_image_update | default(false)) + - not (fast_mode | default(false) | bool) - dashmate_platform_gateway_ssl_provider == 'zerossl' - dashmate_config_result is defined - dashmate_config_result.rc is defined @@ -297,7 +298,7 @@ ansible.builtin.set_fact: dashmate_zerossl_config_certificate_id: "{{ dashmate_zerossl_id_result.stdout }}" when: - - not (skip_dashmate_image_update | default(false)) + - not (fast_mode | default(false) | bool) - dashmate_platform_gateway_ssl_provider == 'zerossl' - dashmate_config_result is defined - dashmate_config_result.rc is defined @@ -317,7 +318,7 @@ changed_when: dashmate_zerossl_id_result_fast.rc == 0 failed_when: false when: - - skip_dashmate_image_update | default(false) + - fast_mode | default(false) | bool - dashmate_platform_enable - dashmate_platform_gateway_ssl_provider == 'zerossl' @@ -325,7 +326,7 @@ ansible.builtin.set_fact: dashmate_zerossl_config_certificate_id: "{{ dashmate_zerossl_id_result_fast.stdout }}" when: - - skip_dashmate_image_update | default(false) + - fast_mode | default(false) | bool - dashmate_platform_enable - dashmate_platform_gateway_ssl_provider == 'zerossl' - dashmate_zerossl_id_result_fast is defined @@ -375,7 +376,7 @@ register: template_result_fast when: - dashmate_user_exists - - skip_dashmate_image_update | default(false) + - fast_mode | default(false) | bool - name: Generate new config content for comparison (regular mode) vars: @@ -384,20 +385,20 @@ src: dashmate.json.j2 dest: /tmp/new_dashmate_config.json mode: '0644' - when: not (skip_dashmate_image_update | default(false)) + when: not (fast_mode | default(false) | bool) - name: Read generated config content (regular mode) ansible.builtin.slurp: src: /tmp/new_dashmate_config.json register: new_config_slurp - when: not (skip_dashmate_image_update | default(false)) + when: not (fast_mode | default(false) | bool) - name: Read existing config if it exists (regular mode) ansible.builtin.slurp: src: '{{ dashmate_config_dir }}/config.json' register: existing_config_slurp when: - - not (skip_dashmate_image_update | default(false)) + - not (fast_mode | default(false) | bool) - existing_dashmate_config_stat.stat.exists failed_when: false @@ -408,7 +409,7 @@ not existing_dashmate_config_stat.stat.exists or (existing_config_slurp.content | b64decode) != (new_config_slurp.content | b64decode) }} - when: not (skip_dashmate_image_update | default(false)) + when: not (fast_mode | default(false) | bool) - name: Write dashmate config file only if changed (regular mode) ansible.builtin.copy: @@ -419,7 +420,7 @@ mode: "0644" register: template_result_regular when: - - not (skip_dashmate_image_update | default(false)) + - not (fast_mode | default(false) | bool) - dashmate_user_exists - config_needs_update @@ -427,12 +428,12 @@ ansible.builtin.file: path: /tmp/new_dashmate_config.json state: absent - when: not (skip_dashmate_image_update | default(false)) + when: not (fast_mode | default(false) | bool) - name: Set template_result for compatibility ansible.builtin.set_fact: template_result: - changed: "{{ template_result_fast.changed if (skip_dashmate_image_update | default(false)) else (config_needs_update | default(false)) }}" + changed: "{{ template_result_fast.changed if (fast_mode | default(false) | bool) else (config_needs_update | default(false)) }}" dest: "{{ dashmate_config_dir }}/config.json" - name: Debug config change details @@ -480,9 +481,10 @@ dashmate_platform_gateway_ssl_provider: {{ dashmate_platform_gateway_ssl_provider | default('undefined') }} ssl_cert_stat.stat.exists: {{ ssl_cert_stat.stat.exists | default('undefined') }} force_ssl_regenerate: {{ force_ssl_regenerate | default('undefined') }} + fast_mode: {{ fast_mode | default('undefined') }} skip_dashmate_image_update: {{ skip_dashmate_image_update | default('undefined') }} SSL cert condition: {{ not (ssl_cert_stat.stat.exists | default(false)) or force_ssl_regenerate | default(false) }} - Skip condition: {{ not (skip_dashmate_image_update | default(false)) }} + Fast mode skip condition: {{ not (fast_mode | default(false) | bool) }} - name: Generate self-signed SSL certificate for DAPI ansible.builtin.import_tasks: ./ssl/self_signed.yml @@ -490,7 +492,7 @@ - dashmate_platform_enable - dashmate_platform_gateway_ssl_provider == 'self-signed' - not (ssl_cert_stat.stat.exists | default(false)) or force_ssl_regenerate | default(false) - - not (skip_dashmate_image_update | default(false)) + - not (fast_mode | default(false) | bool) - name: Obtain ZeroSSL certificate for DAPI ansible.builtin.import_tasks: ./ssl/zerossl.yml @@ -498,7 +500,7 @@ - dashmate_platform_enable - dashmate_platform_gateway_ssl_provider == 'zerossl' - not (ssl_cert_stat.stat.exists | default(false)) or force_ssl_regenerate | default(false) - - not (skip_dashmate_image_update | default(false)) + - not (fast_mode | default(false) | bool) - name: Obtain Let's Encrypt certificate for DAPI ansible.builtin.import_tasks: ./ssl/letsencrypt.yml @@ -506,7 +508,7 @@ - dashmate_platform_enable - dashmate_platform_gateway_ssl_provider == 'letsencrypt' - not (ssl_cert_stat.stat.exists | default(false)) or force_ssl_regenerate | default(false) - - not (skip_dashmate_image_update | default(false)) + - not (fast_mode | default(false) | bool) # ============================================================================ # PHASE 7: Environment and Docker images @@ -542,7 +544,7 @@ - name: Check if images need update ansible.builtin.set_fact: - skip_image_update: "{{ skip_dashmate_image_update | default(false) }}" + skip_image_update: "{{ skip_dashmate_image_update | default(false) | bool }}" - name: Update dashmate images ansible.builtin.command: "{{ dashmate_cmd }} update --format=json" @@ -553,7 +555,7 @@ args: chdir: '{{ dashmate_cwd }}' changed_when: dashmate_update.rc == 0 - when: not (skip_dashmate_image_update | default(false)) + when: not (skip_dashmate_image_update | default(false) | bool) # ============================================================================ # PHASE 8: Service management @@ -587,14 +589,14 @@ }} is_core_images_updated: >- {{ - false if skip_dashmate_image_update | default(false) else + false if skip_dashmate_image_update | default(false) | bool else ((dashmate_update.stdout | default('[]') | from_json | json_query('[?name==`core`]') | sort(attribute='updated', reverse=true) | first | default({})).updated | default(false)) }} is_platform_images_updated: >- {{ - false if skip_dashmate_image_update | default(false) else + false if skip_dashmate_image_update | default(false) | bool else ((dashmate_update.stdout | default('[]') | from_json | json_query('[?name!=`core`]') | sort(attribute='updated', reverse=true) | first | default({})).updated | default(false)) @@ -646,7 +648,7 @@ when: - not (dashmate_start_all.changed | default(false)) - is_dashmate_package_changed or is_dashmate_config_changed - - skip_dashmate_image_update | default(false) + - fast_mode | default(false) | bool changed_when: dashmate_restart_all.rc == 0 # Regular deployment: coordinated chunked restarts @@ -655,7 +657,7 @@ when: - not (dashmate_start_all.changed | default(false)) - is_dashmate_package_changed or is_dashmate_config_changed - - not (skip_dashmate_image_update | default(false)) + - not (fast_mode | default(false) | bool) - name: Force synchronization before coordinated restart ansible.builtin.debug: @@ -663,7 +665,7 @@ when: - not (dashmate_start_all.changed | default(false)) - is_dashmate_package_changed or is_dashmate_config_changed - - not (skip_dashmate_image_update | default(false)) + - not (fast_mode | default(false) | bool) - name: Create host chunks to restart ansible.builtin.set_fact: @@ -671,7 +673,7 @@ when: - not (dashmate_start_all.changed | default(false)) - is_dashmate_package_changed or is_dashmate_config_changed - - not (skip_dashmate_image_update | default(false)) + - not (fast_mode | default(false) | bool) - inventory_hostname == play_hosts[0] - name: Print chunks info @@ -680,7 +682,7 @@ when: - not (dashmate_start_all.changed | default(false)) - is_dashmate_package_changed or is_dashmate_config_changed - - not (skip_dashmate_image_update | default(false)) + - not (fast_mode | default(false) | bool) - inventory_hostname == play_hosts[0] - name: Restart all dashmate services by chunks (regular mode) @@ -693,7 +695,7 @@ when: - not (dashmate_start_all.changed | default(false)) - is_dashmate_package_changed or is_dashmate_config_changed - - not (skip_dashmate_image_update | default(false)) + - not (fast_mode | default(false) | bool) - inventory_hostname == play_hosts[0] # Force start logic diff --git a/bin/deploy b/bin/deploy index c1d7ae858..28c115689 100755 --- a/bin/deploy +++ b/bin/deploy @@ -55,8 +55,8 @@ case ${i} in ansible_playbook="${i#*=}.yml" ;; --fast) - EXTRA_VARS="$EXTRA_VARS -e skip_dashmate_image_update=true -e force_logs_config=false" - echo "Fast mode enabled - skipping Docker image updates and non-critical operations" + EXTRA_VARS="$EXTRA_VARS -e fast_mode=true -e skip_dashmate_image_update=true -e skip_observability_setup=true -e metricbeat_enabled=false -e force_logs_config=false -e force_filebeat_restart=false" + echo "Fast mode enabled - skipping Docker image updates and observability/logging setup" ;; --skip-image-update) EXTRA_VARS="$EXTRA_VARS -e skip_dashmate_image_update=true" From f4639ac3aefab5a1ad9e236e5137764dac839926 Mon Sep 17 00:00:00 2001 From: Ubuntu Date: Fri, 29 May 2026 05:47:17 +0000 Subject: [PATCH 05/10] Harden fast mode boolean conditionals --- ansible/deploy.yml | 8 +- ansible/roles/dashmate/tasks/build.yml | 22 ++-- ansible/roles/dashmate/tasks/logs.yml | 4 +- ansible/roles/dashmate/tasks/main.yml | 102 +++++++++--------- .../roles/dashmate/tasks/rolling_restart.yml | 2 +- .../roles/dashmate/templates/dashmate.json.j2 | 22 ++-- ansible/roles/load_tool/tasks/main.yml | 2 +- ansible/roles/metricbeat/tasks/main.yml | 2 +- ansible/roles/status_dashboard/tasks/main.yml | 4 +- 9 files changed, 84 insertions(+), 84 deletions(-) diff --git a/ansible/deploy.yml b/ansible/deploy.yml index 65254f037..23fc4e1aa 100644 --- a/ansible/deploy.yml +++ b/ansible/deploy.yml @@ -27,7 +27,7 @@ is_aws_environment: '{{ aws_uri_check.status == 200 }}' roles: - role: aws - when: is_aws_environment + when: is_aws_environment | bool - role: swap when: not (prebaked_common_setup | default(false) | bool) tags: @@ -39,7 +39,7 @@ become: true roles: - role: openvpn - when: openvpn_enabled + when: openvpn_enabled | bool tags: - full_deploy - infra_setup @@ -416,9 +416,9 @@ become: true roles: - role: add_fake_latency - when: masternode_network_latency + when: masternode_network_latency | bool - role: remove_fake_latency - when: not masternode_network_latency + when: not (masternode_network_latency | bool) tags: - full_deploy - maintenance diff --git a/ansible/roles/dashmate/tasks/build.yml b/ansible/roles/dashmate/tasks/build.yml index 25436b07c..40274f899 100644 --- a/ansible/roles/dashmate/tasks/build.yml +++ b/ansible/roles/dashmate/tasks/build.yml @@ -15,26 +15,26 @@ - docker-compose-plugin failed_when: false changed_when: false - when: dashmate_platform_enable + when: dashmate_platform_enable | bool - name: Get list of missing packages ansible.builtin.set_fact: missing_packages: "{{ package_check.results | selectattr('rc', 'ne', 0) | map(attribute='item') | list }}" - when: dashmate_platform_enable + when: dashmate_platform_enable | bool - name: Install system dependencies ansible.builtin.package: name: "{{ missing_packages }}" state: present when: - - dashmate_platform_enable + - dashmate_platform_enable | bool - missing_packages | length > 0 - name: Check if Rust is installed ansible.builtin.stat: path: '{{ dashmate_home }}/.cargo/bin/rustc' register: rust_installed - when: dashmate_platform_enable + when: dashmate_platform_enable | bool - name: Download Rustup ansible.builtin.get_url: @@ -45,7 +45,7 @@ mode: '0755' register: rustup when: - - dashmate_platform_enable + - dashmate_platform_enable | bool - not rust_installed.stat.exists - name: Install Rust @@ -55,7 +55,7 @@ become_flags: '-H' when: - rustup.changed - - dashmate_platform_enable + - dashmate_platform_enable | bool - not rust_installed.stat.exists changed_when: true @@ -63,7 +63,7 @@ ansible.builtin.stat: path: '{{ dashmate_home }}/.cargo/bin/wasm-bindgen' register: wasm_bindgen_installed - when: dashmate_platform_enable + when: dashmate_platform_enable | bool # TODO: it should work without sourcing cargo env - name: Install Rust dependencies @@ -79,8 +79,8 @@ executable: /bin/bash changed_when: true when: - - dashmate_platform_enable - - not wasm_bindgen_installed.stat.exists or force_rust_deps | default(false) + - dashmate_platform_enable | bool + - not wasm_bindgen_installed.stat.exists or force_rust_deps | default(false) | bool - name: Check if Node.js is installed with correct version ansible.builtin.command: node --version @@ -120,7 +120,7 @@ single_branch: true depth: 1 register: git - when: not dashmate_source_exists.stat.exists or current_branch.stdout != dashmate_branch or force_git_clone | default(false) + when: not dashmate_source_exists.stat.exists or current_branch.stdout != dashmate_branch or force_git_clone | default(false) | bool - name: Enable corepack ansible.builtin.shell: | @@ -156,7 +156,7 @@ args: chdir: '{{ dashmate_source_dir }}' executable: /bin/bash - when: (git is defined and git.changed) or not dashmate_build_exists.stat.exists or force_build | default(false) + when: (git is defined and git.changed) or not dashmate_build_exists.stat.exists or force_build | default(false) | bool changed_when: true - name: Check if default config is set diff --git a/ansible/roles/dashmate/tasks/logs.yml b/ansible/roles/dashmate/tasks/logs.yml index fc3087473..4cd1e7b5b 100644 --- a/ansible/roles/dashmate/tasks/logs.yml +++ b/ansible/roles/dashmate/tasks/logs.yml @@ -44,7 +44,7 @@ owner: root group: root mode: '0644' - when: not logrotate_stat.stat.exists or force_logrotate_config | default(false) + when: not (logrotate_stat.stat.exists | default(false) | bool) or (force_logrotate_config | default(false) | bool) - name: Check if logrotate timer is already hourly ansible.builtin.command: grep -q '^OnCalendar=hourly' /lib/systemd/system/logrotate.timer @@ -104,4 +104,4 @@ name: filebeat state: restarted enabled: true - when: filebeat_config_changed | default(false) or force_filebeat_restart | default(false) + when: (filebeat_config_changed | default(false) | bool) or (force_filebeat_restart | default(false) | bool) diff --git a/ansible/roles/dashmate/tasks/main.yml b/ansible/roles/dashmate/tasks/main.yml index 3a4dcd827..2a9d7ec86 100644 --- a/ansible/roles/dashmate/tasks/main.yml +++ b/ansible/roles/dashmate/tasks/main.yml @@ -104,7 +104,7 @@ name: '{{ dashmate_group }}' when: - not (fast_mode | default(false) | bool) - - not dashmate_group_exists + - not (dashmate_group_exists | bool) - name: Create dashmate user ansible.builtin.user: @@ -117,7 +117,7 @@ umask: '0002' when: - not (fast_mode | default(false) | bool) - - not dashmate_user_exists + - not (dashmate_user_exists | bool) - name: Update dashmate existence facts after user creation ansible.builtin.set_fact: @@ -125,7 +125,7 @@ dashmate_user_exists: true when: - not (fast_mode | default(false) | bool) - - not dashmate_user_exists + - not (dashmate_user_exists | bool) - name: Add dashmate user to docker group ansible.builtin.user: @@ -134,7 +134,7 @@ append: true when: - not (fast_mode | default(false) | bool) - - dashmate_user_exists + - dashmate_user_exists | bool # ============================================================================ # EARLY PERMISSION FIXES - Ensure all dashmate directories have correct ownership @@ -155,13 +155,13 @@ - '{{ dashmate_logs_dir }}' when: - not (fast_mode | default(false) | bool) - - dashmate_user_exists + - dashmate_user_exists | bool - name: Fix ownership of any existing files in dashmate directories ansible.builtin.command: chown -R {{ dashmate_user }}:{{ dashmate_group }} {{ dashmate_home }} when: - not (fast_mode | default(false) | bool) - - dashmate_user_exists + - dashmate_user_exists | bool changed_when: false # ============================================================================ @@ -220,7 +220,7 @@ when: - dash_conf_changed is defined - dash_conf_changed is changed - - dashmate_user_exists + - dashmate_user_exists | bool # ============================================================================ # PHASE 4: Logging setup (skip in fast mode) @@ -235,8 +235,8 @@ ansible.builtin.import_tasks: ./logs.yml when: - not (skip_observability_setup | default(false) | bool) - - not (logrotate_config_stat.stat.exists | default(false)) or force_logs_config | default(false) - - dashmate_user_exists # Only configure logs if user exists + - not (logrotate_config_stat.stat.exists | default(false)) or force_logs_config | default(false) | bool + - dashmate_user_exists | bool # Only configure logs if user exists # ============================================================================ # PHASE 5: Dashmate installation and configuration (always run) @@ -248,13 +248,13 @@ ansible.builtin.import_tasks: ./build.yml when: - dashmate_branch is defined - - dashmate_installed.rc != 0 or force_dashmate_rebuild | default(false) + - dashmate_installed.rc != 0 or force_dashmate_rebuild | default(false) | bool - name: Install dashmate ansible.builtin.import_tasks: ./install.yml when: - dashmate_version is defined - - (dashmate_needs_update | default(true)) or force_dashmate_reinstall | default(false) + - (dashmate_needs_update | default(true) | bool) or force_dashmate_reinstall | default(false) | bool - name: Set dashmate config version ansible.builtin.set_fact: @@ -319,7 +319,7 @@ failed_when: false when: - fast_mode | default(false) | bool - - dashmate_platform_enable + - dashmate_platform_enable | bool - dashmate_platform_gateway_ssl_provider == 'zerossl' - name: Set ZeroSSL certificate ID from config (fast mode) @@ -327,7 +327,7 @@ dashmate_zerossl_config_certificate_id: "{{ dashmate_zerossl_id_result_fast.stdout }}" when: - fast_mode | default(false) | bool - - dashmate_platform_enable + - dashmate_platform_enable | bool - dashmate_platform_gateway_ssl_provider == 'zerossl' - dashmate_zerossl_id_result_fast is defined - dashmate_zerossl_id_result_fast.rc == 0 @@ -362,7 +362,7 @@ - name: Set core restart flag ansible.builtin.set_fact: - needs_core_restart: "{{ include_core_restart | default(false) or core_image_changed or not existing_dashmate_config_stat.stat.exists }}" + needs_core_restart: "{{ (include_core_restart | default(false) | bool) or (core_image_changed | default(false) | bool) or not (existing_dashmate_config_stat.stat.exists | default(false) | bool) }}" - name: Write dashmate config file (fast mode - always write) vars: @@ -375,7 +375,7 @@ mode: "0644" register: template_result_fast when: - - dashmate_user_exists + - dashmate_user_exists | bool - fast_mode | default(false) | bool - name: Generate new config content for comparison (regular mode) @@ -421,8 +421,8 @@ register: template_result_regular when: - not (fast_mode | default(false) | bool) - - dashmate_user_exists - - config_needs_update + - dashmate_user_exists | bool + - config_needs_update | bool - name: Clean up temp file (regular mode) ansible.builtin.file: @@ -433,7 +433,7 @@ - name: Set template_result for compatibility ansible.builtin.set_fact: template_result: - changed: "{{ template_result_fast.changed if (fast_mode | default(false) | bool) else (config_needs_update | default(false)) }}" + changed: "{{ template_result_fast.changed if (fast_mode | default(false) | bool) else (config_needs_update | default(false) | bool) }}" dest: "{{ dashmate_config_dir }}/config.json" - name: Debug config change details @@ -444,7 +444,7 @@ Dest: {{ template_result.dest | default('undefined') }} when: - template_result is defined - - template_result.changed + - template_result.changed | default(false) | bool - name: Check if service configs need rendering ansible.builtin.stat: @@ -462,7 +462,7 @@ chdir: '{{ dashmate_cwd }}' register: dashmate_render_configs_result changed_when: dashmate_render_configs_result.rc == 0 - when: not (docker_compose_stat.stat.exists | default(false)) or template_result.changed or force_config_render | default(false) + when: not (docker_compose_stat.stat.exists | default(false)) or (template_result.changed | default(false) | bool) or (force_config_render | default(false) | bool) # ============================================================================ # PHASE 6: SSL certificates (only skip in fast mode) @@ -472,7 +472,7 @@ ansible.builtin.stat: path: '{{ dashmate_home }}/.dashmate/ssl/bundle.crt' register: ssl_cert_stat - when: dashmate_platform_enable + when: dashmate_platform_enable | bool - name: Debug SSL conditions ansible.builtin.debug: @@ -483,31 +483,31 @@ force_ssl_regenerate: {{ force_ssl_regenerate | default('undefined') }} fast_mode: {{ fast_mode | default('undefined') }} skip_dashmate_image_update: {{ skip_dashmate_image_update | default('undefined') }} - SSL cert condition: {{ not (ssl_cert_stat.stat.exists | default(false)) or force_ssl_regenerate | default(false) }} + SSL cert condition: {{ not (ssl_cert_stat.stat.exists | default(false)) or force_ssl_regenerate | default(false) | bool }} Fast mode skip condition: {{ not (fast_mode | default(false) | bool) }} - name: Generate self-signed SSL certificate for DAPI ansible.builtin.import_tasks: ./ssl/self_signed.yml when: - - dashmate_platform_enable + - dashmate_platform_enable | bool - dashmate_platform_gateway_ssl_provider == 'self-signed' - - not (ssl_cert_stat.stat.exists | default(false)) or force_ssl_regenerate | default(false) + - not (ssl_cert_stat.stat.exists | default(false)) or force_ssl_regenerate | default(false) | bool - not (fast_mode | default(false) | bool) - name: Obtain ZeroSSL certificate for DAPI ansible.builtin.import_tasks: ./ssl/zerossl.yml when: - - dashmate_platform_enable + - dashmate_platform_enable | bool - dashmate_platform_gateway_ssl_provider == 'zerossl' - - not (ssl_cert_stat.stat.exists | default(false)) or force_ssl_regenerate | default(false) + - not (ssl_cert_stat.stat.exists | default(false)) or force_ssl_regenerate | default(false) | bool - not (fast_mode | default(false) | bool) - name: Obtain Let's Encrypt certificate for DAPI ansible.builtin.import_tasks: ./ssl/letsencrypt.yml when: - - dashmate_platform_enable + - dashmate_platform_enable | bool - dashmate_platform_gateway_ssl_provider == 'letsencrypt' - - not (ssl_cert_stat.stat.exists | default(false)) or force_ssl_regenerate | default(false) + - not (ssl_cert_stat.stat.exists | default(false)) or force_ssl_regenerate | default(false) | bool - not (fast_mode | default(false) | bool) # ============================================================================ @@ -528,7 +528,7 @@ chdir: '{{ dashmate_cwd }}' register: dashmate_envs_output changed_when: false - when: not (env_file_stat.stat.exists | default(false)) or template_result.changed or force_env_export | default(false) + when: not (env_file_stat.stat.exists | default(false)) or (template_result.changed | default(false) | bool) or (force_env_export | default(false) | bool) - name: Write .env file ansible.builtin.copy: @@ -538,7 +538,7 @@ group: "{{ dashmate_group }}" mode: '0644' when: - - not (env_file_stat.stat.exists | default(false)) or template_result.changed or force_env_export | default(false) + - not (env_file_stat.stat.exists | default(false)) or (template_result.changed | default(false) | bool) or (force_env_export | default(false) | bool) - dashmate_envs_output is defined - dashmate_envs_output.stdout is defined @@ -601,8 +601,8 @@ json_query('[?name!=`core`]') | sort(attribute='updated', reverse=true) | first | default({})).updated | default(false)) }} - is_dashmate_package_changed: "{{ dashmate_install_result.changed if dashmate_install_result is defined else false }}" - is_dashmate_config_changed: "{{ template_result.changed if template_result is defined else false }}" + is_dashmate_package_changed: "{{ dashmate_install_result.changed | default(false) | bool }}" + is_dashmate_config_changed: "{{ template_result.changed | default(false) | bool }}" - name: Print status ansible.builtin.debug: @@ -627,8 +627,8 @@ chdir: '{{ dashmate_cwd }}' register: dashmate_start_all when: - - not is_core_started - - not is_platform_started + - not (is_core_started | bool) + - not (is_platform_started | bool) changed_when: dashmate_start_all.rc == 0 # Restart logic @@ -638,7 +638,7 @@ # Fast deployment: each node restarts itself independently - name: Restart dashmate services (fast mode - individual restart) ansible.builtin.command: >- - {{ dashmate_cmd }} restart --safe --verbose{{ '' if needs_core_restart | default(false) else ' --platform' }} + {{ dashmate_cmd }} restart --safe --verbose{{ '' if (needs_core_restart | default(false) | bool) else ' --platform' }} become: true become_user: dashmate become_flags: '-H' @@ -646,8 +646,8 @@ chdir: '{{ dashmate_cwd }}' register: dashmate_restart_all when: - - not (dashmate_start_all.changed | default(false)) - - is_dashmate_package_changed or is_dashmate_config_changed + - not (dashmate_start_all.changed | default(false) | bool) + - (is_dashmate_package_changed | bool) or (is_dashmate_config_changed | bool) - fast_mode | default(false) | bool changed_when: dashmate_restart_all.rc == 0 @@ -655,24 +655,24 @@ - name: Synchronization point - wait for all nodes to complete individual tasks ansible.builtin.meta: flush_handlers when: - - not (dashmate_start_all.changed | default(false)) - - is_dashmate_package_changed or is_dashmate_config_changed + - not (dashmate_start_all.changed | default(false) | bool) + - (is_dashmate_package_changed | bool) or (is_dashmate_config_changed | bool) - not (fast_mode | default(false) | bool) - name: Force synchronization before coordinated restart ansible.builtin.debug: msg: "All nodes ready for coordinated restart" when: - - not (dashmate_start_all.changed | default(false)) - - is_dashmate_package_changed or is_dashmate_config_changed + - not (dashmate_start_all.changed | default(false) | bool) + - (is_dashmate_package_changed | bool) or (is_dashmate_config_changed | bool) - not (fast_mode | default(false) | bool) - name: Create host chunks to restart ansible.builtin.set_fact: host_chunks: "{{ play_hosts | batch(restart_chunk_size) }}" when: - - not (dashmate_start_all.changed | default(false)) - - is_dashmate_package_changed or is_dashmate_config_changed + - not (dashmate_start_all.changed | default(false) | bool) + - (is_dashmate_package_changed | bool) or (is_dashmate_config_changed | bool) - not (fast_mode | default(false) | bool) - inventory_hostname == play_hosts[0] @@ -680,8 +680,8 @@ ansible.builtin.debug: msg: "Split {{ play_hosts | length }} into {{ host_chunks | default([]) | length }} chunks to restart all services" when: - - not (dashmate_start_all.changed | default(false)) - - is_dashmate_package_changed or is_dashmate_config_changed + - not (dashmate_start_all.changed | default(false) | bool) + - (is_dashmate_package_changed | bool) or (is_dashmate_config_changed | bool) - not (fast_mode | default(false) | bool) - inventory_hostname == play_hosts[0] @@ -693,8 +693,8 @@ index_var: current_chunk_index label: "{{ current_chunk }}" when: - - not (dashmate_start_all.changed | default(false)) - - is_dashmate_package_changed or is_dashmate_config_changed + - not (dashmate_start_all.changed | default(false) | bool) + - (is_dashmate_package_changed | bool) or (is_dashmate_config_changed | bool) - not (fast_mode | default(false) | bool) - inventory_hostname == play_hosts[0] @@ -703,7 +703,7 @@ - name: Start not started services and replace updated services ansible.builtin.command: >- - {{ dashmate_cmd }} start --force --verbose{{ '' if needs_core_restart | default(false) else ' --platform' }} + {{ dashmate_cmd }} start --force --verbose{{ '' if (needs_core_restart | default(false) | bool) else ' --platform' }} become: true become_user: dashmate become_flags: '-H' @@ -711,9 +711,9 @@ chdir: '{{ dashmate_cwd }}' register: dashmate_force_start when: - - not (dashmate_start_all.changed | default(false)) - - not (dashmate_restart_all.changed | default(false)) - - not (is_dashmate_package_changed or is_dashmate_config_changed) + - not (dashmate_start_all.changed | default(false) | bool) + - not (dashmate_restart_all.changed | default(false) | bool) + - not ((is_dashmate_package_changed | bool) or (is_dashmate_config_changed | bool)) changed_when: dashmate_force_start.rc == 0 - name: Disable dashmate helper build diff --git a/ansible/roles/dashmate/tasks/rolling_restart.yml b/ansible/roles/dashmate/tasks/rolling_restart.yml index e215872fa..ceeaeaa6c 100644 --- a/ansible/roles/dashmate/tasks/rolling_restart.yml +++ b/ansible/roles/dashmate/tasks/rolling_restart.yml @@ -1,7 +1,7 @@ --- - name: Restart dashmate services for current chunk - ansible.builtin.command: "{{ dashmate_cmd }} restart --safe --verbose{{ '' if needs_core_restart | default(false) else ' --platform' }}" + ansible.builtin.command: "{{ dashmate_cmd }} restart --safe --verbose{{ '' if (needs_core_restart | default(false) | bool) else ' --platform' }}" become: true become_user: dashmate become_flags: '-H' diff --git a/ansible/roles/dashmate/templates/dashmate.json.j2 b/ansible/roles/dashmate/templates/dashmate.json.j2 index 1f4017edd..308f73329 100644 --- a/ansible/roles/dashmate/templates/dashmate.json.j2 +++ b/ansible/roles/dashmate/templates/dashmate.json.j2 @@ -158,9 +158,9 @@ } }, "platform": { - "enable": {% if dashmate_platform_enable %}true{% else %}false{% endif %}, + "enable": {% if dashmate_platform_enable | bool %}true{% else %}false{% endif %}, "quorumList": { - "enabled": {% if dashmate_platform_quorum_list_enabled | default(false) %}true{% else %}false{% endif %}, + "enabled": {% if dashmate_platform_quorum_list_enabled | default(false) | bool %}true{% else %}false{% endif %}, "docker": { "image": "{{ dashmate_platform_quorum_list_docker_image | default('dashpay/quorum-list-server:latest') }}" }, @@ -190,12 +190,12 @@ } }, "metrics": { - "enabled": {% if dashmate_platform_gateway_metrics_enabled %}true{% else %}false{% endif %}, + "enabled": {% if dashmate_platform_gateway_metrics_enabled | bool %}true{% else %}false{% endif %}, "host": "{{ private_ip }}", "port": {{ dashmate_platform_gateway_metrics_port }} }, "admin": { - "enabled": {% if dashmate_platform_gateway_metrics_enabled %}true{% else %}false{% endif %}, + "enabled": {% if dashmate_platform_gateway_metrics_enabled | bool %}true{% else %}false{% endif %}, "host": "127.0.0.1", "port": 9901 }, @@ -214,7 +214,7 @@ "image": "{{ dashmate_platform_gateway_rate_limiter_docker_image }}" }, "metrics": { - "enabled": {% if dashmate_platform_gateway_rate_limiter_metrics_enabled %}true{% else %}false{% endif %}, + "enabled": {% if dashmate_platform_gateway_rate_limiter_metrics_enabled | bool %}true{% else %}false{% endif %}, "docker": { "image": "{{ dashmate_platform_gateway_rate_limiter_metrics_docker_image }}" }, @@ -225,7 +225,7 @@ "requestsPerUnit": {{ dashmate_platform_gateway_rate_limiter_requests_per_unit }}, "blacklist": [], "whitelist": [], - "enabled": {% if dashmate_platform_gateway_rate_limiter_enabled %}true{% else %}false{% endif %} + "enabled": {% if dashmate_platform_gateway_rate_limiter_enabled | bool %}true{% else %}false{% endif %} }, "log": { "level": "{{ dashmate_platform_gateway_log_level }}", @@ -266,13 +266,13 @@ } }, "metrics": { - "enabled": {% if dashmate_platform_dapi_rs_dapi_metrics_enabled | default(false) %}true{% else %}false{% endif %}, + "enabled": {% if dashmate_platform_dapi_rs_dapi_metrics_enabled | default(false) | bool %}true{% else %}false{% endif %}, "host": "{{ dashmate_platform_dapi_rs_dapi_metrics_host | default('127.0.0.1') }}", "port": {{ dashmate_platform_dapi_rs_dapi_metrics_port | default(9091) }} }, "logs": { "level": "{{ dashmate_platform_dapi_rs_dapi_log_level | default('debug') }}", - "jsonFormat": {% if dashmate_platform_dapi_rs_dapi_log_json_format | default(false) %}true{% else %}false{% endif %}, + "jsonFormat": {% if dashmate_platform_dapi_rs_dapi_log_json_format | default(false) | bool %}true{% else %}false{% endif %}, "accessLogPath": {% if dashmate_platform_dapi_rs_dapi_access_log_path is defined and dashmate_platform_dapi_rs_dapi_access_log_path %}"{{ dashmate_platform_dapi_rs_dapi_access_log_path }}"{% else %}null{% endif %}, "accessLogFormat": "{{ dashmate_platform_dapi_rs_dapi_access_log_format | default('combined') }}" }, @@ -352,7 +352,7 @@ } }, "metrics": { - "enabled": {% if dashmate_platform_drive_abci_metrics_enabled %}true{% else %}false{% endif %}, + "enabled": {% if dashmate_platform_drive_abci_metrics_enabled | bool %}true{% else %}false{% endif %}, "host": "{{ private_ip }}", "port": {{ dashmate_platform_drive_abci_metrics_port }} }, @@ -385,7 +385,7 @@ "recvRate": {{dashmate_platform_drive_tenderdash_p2p_recv_rate}}, "maxConnections": {{dashmate_platform_drive_tenderdash_p2p_max_connections}}, "maxOutgoingConnections": {{dashmate_platform_drive_tenderdash_p2p_max_outgoing_connections}}, - "allowlistOnly": {{ dashmate_platform_drive_tenderdash_p2p_allowlist_only | default(false) | to_json }} + "allowlistOnly": {{ dashmate_platform_drive_tenderdash_p2p_allowlist_only | default(false) | bool | to_json }} }, "mempool": { "cacheSize": {{dashmate_platform_drive_tenderdash_mempool_cache_size}}, @@ -406,7 +406,7 @@ "timeoutBroadcastTx": "{{dashmate_platform_drive_tenderdash_rpc_timeout_broadcast_tx}}" }, "pprof": { - "enabled": {% if dashmate_platform_tenderdash_pprof_enable %}true{% else %}false{% endif %}, + "enabled": {% if dashmate_platform_tenderdash_pprof_enable | bool %}true{% else %}false{% endif %}, "port": 6060 }, "metrics": { diff --git a/ansible/roles/load_tool/tasks/main.yml b/ansible/roles/load_tool/tasks/main.yml index 5d8cb4ad2..5df8a80b8 100644 --- a/ansible/roles/load_tool/tasks/main.yml +++ b/ansible/roles/load_tool/tasks/main.yml @@ -17,7 +17,7 @@ - libssl-dev - pkg-config state: present - when: dashmate_platform_enable + when: dashmate_platform_enable | bool - name: Download Rustup ansible.builtin.get_url: diff --git a/ansible/roles/metricbeat/tasks/main.yml b/ansible/roles/metricbeat/tasks/main.yml index 2fc5bbdaa..0c81fb15f 100644 --- a/ansible/roles/metricbeat/tasks/main.yml +++ b/ansible/roles/metricbeat/tasks/main.yml @@ -87,4 +87,4 @@ }} username: "{{ elastic_username }}" password: "{{ elastic_password }}" - when: metricbeat_enabled + when: metricbeat_enabled | bool diff --git a/ansible/roles/status_dashboard/tasks/main.yml b/ansible/roles/status_dashboard/tasks/main.yml index 2a386b1ae..8802ee133 100644 --- a/ansible/roles/status_dashboard/tasks/main.yml +++ b/ansible/roles/status_dashboard/tasks/main.yml @@ -50,6 +50,6 @@ community.docker.docker_compose_v2: project_src: "{{ status_dashboard_path }}" state: present - pull: "{{ 'always' if (force_dashmate_reinstall | default(false) or not (skip_dashmate_image_update | default(false))) else 'policy' }}" - recreate: "{{ 'always' if (force_dashmate_rebuild | default(false)) else 'auto' }}" + pull: "{{ 'always' if (force_dashmate_reinstall | default(false) | bool or not (skip_dashmate_image_update | default(false) | bool)) else 'policy' }}" + recreate: "{{ 'always' if (force_dashmate_rebuild | default(false) | bool) else 'auto' }}" when: status_dashboard_ssh_key_stat.stat.exists From 372aa19a4c4aa7f2b5c03ab0d61f6bd26d4c8e92 Mon Sep 17 00:00:00 2001 From: Ubuntu Date: Fri, 29 May 2026 08:07:18 +0000 Subject: [PATCH 06/10] Ensure dashmate user exists in fast deploys --- ansible/prebake-common.yml | 18 +++++++++++++ ansible/roles/dashmate/tasks/main.yml | 37 ++++++++++----------------- 2 files changed, 31 insertions(+), 24 deletions(-) diff --git a/ansible/prebake-common.yml b/ansible/prebake-common.yml index 932ce5e64..f702e536e 100644 --- a/ansible/prebake-common.yml +++ b/ansible/prebake-common.yml @@ -14,6 +14,9 @@ version: "1.29.2" - name: requests version: "2.31.0" + dashmate_group: dashmate + dashmate_user: dashmate + dashmate_home: /home/dashmate pre_tasks: - name: Update apt cache and install common packages ansible.builtin.apt: @@ -22,6 +25,20 @@ - jq - unzip update_cache: true + + - name: Create dashmate group + ansible.builtin.group: + name: "{{ dashmate_group }}" + + - name: Create dashmate user + ansible.builtin.user: + name: "{{ dashmate_user }}" + comment: Dashmate user + group: "{{ dashmate_group }}" + home: "{{ dashmate_home }}" + create_home: true + shell: /bin/bash + umask: "0002" roles: - swap - geerlingguy.pip @@ -31,5 +48,6 @@ docker_install_compose: false docker_users: - ubuntu + - "{{ dashmate_user }}" - docker_options - eternal_terminal diff --git a/ansible/roles/dashmate/tasks/main.yml b/ansible/roles/dashmate/tasks/main.yml index 2a9d7ec86..4878ab939 100644 --- a/ansible/roles/dashmate/tasks/main.yml +++ b/ansible/roles/dashmate/tasks/main.yml @@ -50,7 +50,7 @@ msg: "Installed: {{ installed_dashmate_version }}, Required: {{ dashmate_version }}, Needs update: {{ dashmate_needs_update }}" # ============================================================================ -# PHASE 2: User and system setup (skip in fast mode) +# PHASE 2: User and system setup # ============================================================================ - name: Check if dashmate group exists @@ -58,29 +58,23 @@ register: dashmate_group_check failed_when: false changed_when: false - when: not (fast_mode | default(false) | bool) - name: Check if dashmate user exists ansible.builtin.command: "id {{ dashmate_user }}" register: dashmate_user_check failed_when: false changed_when: false - when: not (fast_mode | default(false) | bool) - name: Set user/group existence facts ansible.builtin.set_fact: dashmate_group_exists: >- - {%- if fast_mode | default(false) | bool -%} - true - {%- elif dashmate_group_check is defined and dashmate_group_check.rc is defined -%} + {%- if dashmate_group_check is defined and dashmate_group_check.rc is defined -%} {{ dashmate_group_check.rc == 0 }} {%- else -%} false {%- endif -%} dashmate_user_exists: >- - {%- if fast_mode | default(false) | bool -%} - true - {%- elif dashmate_user_check is defined and dashmate_user_check.rc is defined -%} + {%- if dashmate_user_check is defined and dashmate_user_check.rc is defined -%} {{ dashmate_user_check.rc == 0 }} {%- else -%} false @@ -97,14 +91,13 @@ skip_dashmate_image_update: {{ skip_dashmate_image_update | default('undefined') }} dashmate_group_exists: {{ dashmate_group_exists }} dashmate_user_exists: {{ dashmate_user_exists }} - when: not (fast_mode | default(false) | bool) + when: not (fast_mode | default(false) | bool) or not (dashmate_group_exists | bool) or not (dashmate_user_exists | bool) - name: Create dashmate group ansible.builtin.group: name: '{{ dashmate_group }}' - when: - - not (fast_mode | default(false) | bool) - - not (dashmate_group_exists | bool) + register: dashmate_group_create + when: not (dashmate_group_exists | bool) - name: Create dashmate user ansible.builtin.user: @@ -115,26 +108,22 @@ create_home: true shell: /bin/bash umask: '0002' - when: - - not (fast_mode | default(false) | bool) - - not (dashmate_user_exists | bool) + register: dashmate_user_create + when: not (dashmate_user_exists | bool) - name: Update dashmate existence facts after user creation ansible.builtin.set_fact: dashmate_group_exists: true dashmate_user_exists: true - when: - - not (fast_mode | default(false) | bool) - - not (dashmate_user_exists | bool) + when: (dashmate_group_create.changed | default(false) | bool) or (dashmate_user_create.changed | default(false) | bool) - name: Add dashmate user to docker group ansible.builtin.user: name: 'dashmate' groups: docker append: true - when: - - not (fast_mode | default(false) | bool) - - dashmate_user_exists | bool + register: dashmate_docker_group_update + when: dashmate_user_exists | bool # ============================================================================ # EARLY PERMISSION FIXES - Ensure all dashmate directories have correct ownership @@ -154,13 +143,13 @@ - '{{ dashmate_config_dir }}' - '{{ dashmate_logs_dir }}' when: - - not (fast_mode | default(false) | bool) + - not (fast_mode | default(false) | bool) or (dashmate_user_create.changed | default(false) | bool) - dashmate_user_exists | bool - name: Fix ownership of any existing files in dashmate directories ansible.builtin.command: chown -R {{ dashmate_user }}:{{ dashmate_group }} {{ dashmate_home }} when: - - not (fast_mode | default(false) | bool) + - not (fast_mode | default(false) | bool) or (dashmate_user_create.changed | default(false) | bool) - dashmate_user_exists | bool changed_when: false From 5d10bb749cdaf76a2ea5bdf81ba0dcbe8a5bac87 Mon Sep 17 00:00:00 2001 From: infraclaw Date: Fri, 29 May 2026 18:26:52 +0000 Subject: [PATCH 07/10] Address CodeRabbit review comments --- .github/workflows/build-base-images.yml | 6 ++++-- ansible/roles/dashmate/tasks/main.yml | 1 + ansible/roles/dashmate/tasks/ssl/zerossl.yml | 2 +- bin/build-base-image | 5 +++++ terraform/aws/main.tf | 7 ++++--- terraform/aws/variables.tf | 11 ++++++++++- 6 files changed, 25 insertions(+), 7 deletions(-) diff --git a/.github/workflows/build-base-images.yml b/.github/workflows/build-base-images.yml index b2d274e33..fc590b76c 100644 --- a/.github/workflows/build-base-images.yml +++ b/.github/workflows/build-base-images.yml @@ -50,10 +50,12 @@ jobs: steps: - name: Checkout dash-network-deploy - uses: actions/checkout@v4 + uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 + with: + persist-credentials: false - name: Set up Packer - uses: hashicorp/setup-packer@v3 + uses: hashicorp/setup-packer@1aa358be5cf73883762b302a3a03abd66e75b232 - name: Install controller dependencies run: | diff --git a/ansible/roles/dashmate/tasks/main.yml b/ansible/roles/dashmate/tasks/main.yml index 4878ab939..9db5b86fc 100644 --- a/ansible/roles/dashmate/tasks/main.yml +++ b/ansible/roles/dashmate/tasks/main.yml @@ -223,6 +223,7 @@ - name: Configure logs ansible.builtin.import_tasks: ./logs.yml when: + - not (fast_mode | default(false) | bool) - not (skip_observability_setup | default(false) | bool) - not (logrotate_config_stat.stat.exists | default(false)) or force_logs_config | default(false) | bool - dashmate_user_exists | bool # Only configure logs if user exists diff --git a/ansible/roles/dashmate/tasks/ssl/zerossl.yml b/ansible/roles/dashmate/tasks/ssl/zerossl.yml index eff308fbc..eb10213e9 100644 --- a/ansible/roles/dashmate/tasks/ssl/zerossl.yml +++ b/ansible/roles/dashmate/tasks/ssl/zerossl.yml @@ -54,7 +54,7 @@ content: "{{ lookup('aws_ssm', dashmate_zerossl_ssm_path ~ '-' ~ item, on_missing='skip') | default('', true) }}" owner: '{{ dashmate_user }}' group: '{{ dashmate_group }}' - mode: "0644" + mode: "{{ '0600' if item == dashmate_zerossl_private_key_file_name else '0644' }}" loop: - '{{ dashmate_zerossl_private_key_file_name }}' - '{{ dashmate_zerossl_csr_file_name }}' diff --git a/bin/build-base-image b/bin/build-base-image index 5872a82f8..017f5bdd0 100755 --- a/bin/build-base-image +++ b/bin/build-base-image @@ -50,6 +50,11 @@ if ! command -v packer >/dev/null 2>&1; then exit 1 fi +if ! command -v ansible-playbook >/dev/null 2>&1; then + echo "Error: ansible-playbook is required to build base AMIs" >&2 + exit 1 +fi + if [[ -n "$PROFILE" ]]; then export AWS_PROFILE="$PROFILE" fi diff --git a/terraform/aws/main.tf b/terraform/aws/main.tf index 1f5779619..1f11dbb80 100644 --- a/terraform/aws/main.tf +++ b/terraform/aws/main.tf @@ -10,6 +10,7 @@ data "aws_availability_zones" "available" { } data "aws_ami" "ubuntu_amd" { + count = var.base_ami_amd64_id == "" ? 1 : 0 most_recent = true filter { @@ -27,6 +28,7 @@ data "aws_ami" "ubuntu_amd" { } data "aws_ami" "ubuntu_arm" { + count = var.base_ami_arm64_id == "" ? 1 : 0 most_recent = true filter { @@ -44,8 +46,8 @@ data "aws_ami" "ubuntu_arm" { } locals { - ubuntu_amd_ami_id = var.base_ami_amd64_id != "" ? var.base_ami_amd64_id : data.aws_ami.ubuntu_amd.id - ubuntu_arm_ami_id = var.base_ami_arm64_id != "" ? var.base_ami_arm64_id : data.aws_ami.ubuntu_arm.id + ubuntu_amd_ami_id = var.base_ami_amd64_id != "" ? var.base_ami_amd64_id : one(data.aws_ami.ubuntu_amd[*].id) + ubuntu_arm_ami_id = var.base_ami_arm64_id != "" ? var.base_ami_arm64_id : one(data.aws_ami.ubuntu_arm[*].id) main_host_ami_id = var.main_host_arch == "arm64" ? local.ubuntu_arm_ami_id : local.ubuntu_amd_ami_id } @@ -521,4 +523,3 @@ resource "aws_eip" "vpn" { DashNetwork = terraform.workspace } } - diff --git a/terraform/aws/variables.tf b/terraform/aws/variables.tf index 94c354e0a..bc925595a 100644 --- a/terraform/aws/variables.tf +++ b/terraform/aws/variables.tf @@ -5,12 +5,22 @@ variable "base_ami_amd64_id" { description = "Optional pre-baked amd64 AMI ID. Defaults to latest Canonical Ubuntu Jammy amd64 when empty." type = string default = "" + + validation { + condition = var.base_ami_amd64_id == "" || can(regex("^ami-[0-9a-fA-F]+$", var.base_ami_amd64_id)) + error_message = "base_ami_amd64_id must be empty or a valid AMI ID such as ami-0123456789abcdef0." + } } variable "base_ami_arm64_id" { description = "Optional pre-baked arm64 AMI ID. Defaults to latest Canonical Ubuntu Jammy arm64 when empty." type = string default = "" + + validation { + condition = var.base_ami_arm64_id == "" || can(regex("^ami-[0-9a-fA-F]+$", var.base_ami_arm64_id)) + error_message = "base_ami_arm64_id must be empty or a valid AMI ID such as ami-0123456789abcdef0." + } } variable "dashd_port" { @@ -309,4 +319,3 @@ variable "byoip_pool_id" { description = "IPAM pool ID for BYOIP address allocation. When set, all instances get EIPs from this pool." default = "" } - From be37457c0682c10bfede8153bfb509171d465128 Mon Sep 17 00:00:00 2001 From: infraclaw Date: Fri, 29 May 2026 18:56:49 +0000 Subject: [PATCH 08/10] Fix ansible lint line lengths --- ansible/roles/dashmate/tasks/main.yml | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/ansible/roles/dashmate/tasks/main.yml b/ansible/roles/dashmate/tasks/main.yml index 9db5b86fc..321e8a4d9 100644 --- a/ansible/roles/dashmate/tasks/main.yml +++ b/ansible/roles/dashmate/tasks/main.yml @@ -352,7 +352,12 @@ - name: Set core restart flag ansible.builtin.set_fact: - needs_core_restart: "{{ (include_core_restart | default(false) | bool) or (core_image_changed | default(false) | bool) or not (existing_dashmate_config_stat.stat.exists | default(false) | bool) }}" + needs_core_restart: >- + {{ + (include_core_restart | default(false) | bool) + or (core_image_changed | default(false) | bool) + or not (existing_dashmate_config_stat.stat.exists | default(false) | bool) + }} - name: Write dashmate config file (fast mode - always write) vars: @@ -452,7 +457,10 @@ chdir: '{{ dashmate_cwd }}' register: dashmate_render_configs_result changed_when: dashmate_render_configs_result.rc == 0 - when: not (docker_compose_stat.stat.exists | default(false)) or (template_result.changed | default(false) | bool) or (force_config_render | default(false) | bool) + when: > + not (docker_compose_stat.stat.exists | default(false)) or + (template_result.changed | default(false) | bool) or + (force_config_render | default(false) | bool) # ============================================================================ # PHASE 6: SSL certificates (only skip in fast mode) From bb1dd8603f37d25cf70b86ddbf660581f1444266 Mon Sep 17 00:00:00 2001 From: infraclaw Date: Fri, 29 May 2026 20:16:48 +0000 Subject: [PATCH 09/10] Speed up fast devnet deploy path --- README.md | 6 ++++- ansible/deploy.yml | 1 + ansible/group_vars/all | 4 +++ ansible/roles/dashd/tasks/main.yml | 9 +++++-- .../tasks/fund_collateral.yml | 27 ++++++++++++------- ansible/roles/mn_init/tasks/main.yml | 12 ++++----- bin/deploy | 10 ++++--- 7 files changed, 48 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index 5cc9ee79f..1842e425b 100644 --- a/README.md +++ b/README.md @@ -79,7 +79,11 @@ dash-network deploy You may pass the `--only-infrastructure` or `--only-provisioning` option to target either infrastructure or software provisioning workflows. -For faster updates to an existing deployment, pass `--fast`. Fast mode skips Docker image updates plus slow observability/logging setup such as CloudWatch Agent, Elastic/logs, filebeat, metricbeat, metrics, and status dashboards. +For faster updates to an existing deployment, pass `--fast`. Fast mode skips Docker +image updates, final Docker cleanup, and slow observability/logging setup such as +CloudWatch Agent, Elastic/logs, filebeat, metricbeat, metrics, and status +dashboards. It also polls masternode sync more frequently while preserving the +same maximum wait time. To destroy an available Dash Network, use the `destroy` command: diff --git a/ansible/deploy.yml b/ansible/deploy.yml index 23fc4e1aa..8c74a28f2 100644 --- a/ansible/deploy.yml +++ b/ansible/deploy.yml @@ -443,6 +443,7 @@ networks: true volumes: false builder_cache: true + when: not (skip_deploy_cleanup | default(false) | bool) tags: - full_deploy - cleanup diff --git a/ansible/group_vars/all b/ansible/group_vars/all index 6c84efd71..5283e2dca 100644 --- a/ansible/group_vars/all +++ b/ansible/group_vars/all @@ -11,7 +11,11 @@ prebaked_common_setup: false # Fast deploys are intended for existing hosts and skip slow/non-critical setup. # These are set by `bin/deploy --fast` and can also be passed manually. fast_mode: false +skip_dashd_image_update: false +skip_deploy_cleanup: false skip_observability_setup: false +mn_sync_poll_delay: 60 +mn_sync_retries: 100 platform_initial_core_chain_locked_height: "" diff --git a/ansible/roles/dashd/tasks/main.yml b/ansible/roles/dashd/tasks/main.yml index 5967223cd..dceb4bc44 100644 --- a/ansible/roles/dashd/tasks/main.yml +++ b/ansible/roles/dashd/tasks/main.yml @@ -89,6 +89,11 @@ path: /etc/cron.daily/logrotate mode: "+x" +- name: Set dash core compose update policy + ansible.builtin.set_fact: + dashd_compose_pull_policy: "{{ 'missing' if (skip_dashd_image_update | default(false) | bool) else 'always' }}" + dashd_compose_recreate_policy: "{{ 'auto' if (skip_dashd_image_update | default(false) | bool) else 'always' }}" + - name: Check if wallet exists ansible.builtin.stat: path: "{{ dashd_home }}/.dashcore/{{ dash_network_name if dash_network == 'devnet' else 'testnet3' }}/wallets/{{ wallet_rpc_wallet_faucet }}/wallet.dat" @@ -108,8 +113,8 @@ community.docker.docker_compose_v2: project_src: '{{ dashd_compose_path }}' state: present - pull: always - recreate: always + pull: "{{ dashd_compose_pull_policy }}" + recreate: "{{ dashd_compose_recreate_policy }}" timeout: 30 - name: Wait for rpc to be available diff --git a/ansible/roles/mn_fund_collateral/tasks/fund_collateral.yml b/ansible/roles/mn_fund_collateral/tasks/fund_collateral.yml index 70b5f6abf..989481f61 100644 --- a/ansible/roles/mn_fund_collateral/tasks/fund_collateral.yml +++ b/ansible/roles/mn_fund_collateral/tasks/fund_collateral.yml @@ -4,21 +4,30 @@ - name: Initialize array for payments ansible.builtin.set_fact: - payments: [] + payments: {} - name: Populate payment addresses and values ansible.builtin.set_fact: - payments: "{{ payments | default({}) | combine({item: amount}) }}" - with_items: - - '{{ payment_targets }}' + payments: "{{ payments | combine({item: amount | float}) }}" + loop: "{{ payment_targets }}" - name: Show list of payments to be made ansible.builtin.debug: var: payments - name: Fund listed masternodes with {{ amount ~ ' Dash'}} - ansible.builtin.include_tasks: fund_one_collateral.yml - with_items: - - '{{ payment_targets }}' - loop_control: - loop_var: payment_target + ansible.builtin.command: "dash-cli -rpcwallet={{ wallet_rpc_wallet_faucet }} sendmany '' '{{ payments | to_json }}'" + register: fund_result + changed_when: fund_result.stdout | length == 64 + when: payment_targets | length > 0 + +- name: Confirm collateral funding transactions + ansible.builtin.include_role: + name: generate_blocks + vars: + generate: confirmations + txid_list: "{{ [fund_result.stdout] }}" + tx_source: "{{ wallet_rpc_wallet_faucet }}" + when: + - fund_result.stdout is defined + - fund_result.stdout | length > 0 diff --git a/ansible/roles/mn_init/tasks/main.yml b/ansible/roles/mn_init/tasks/main.yml index 40d3303e0..15f053cb7 100644 --- a/ansible/roles/mn_init/tasks/main.yml +++ b/ansible/roles/mn_init/tasks/main.yml @@ -5,8 +5,8 @@ - name: Wait for wallet sync register: blockchain_status ansible.builtin.command: dash-cli getblockchaininfo - retries: 100 - delay: 60 + retries: "{{ mn_sync_retries }}" + delay: "{{ mn_sync_poll_delay }}" until: blockchain_status.stdout | from_json | json_query('blocks') == blockchain_status.stdout | from_json | json_query('headers') changed_when: blockchain_status.stdout | length > 0 @@ -18,8 +18,8 @@ - name: Wait for blockchain sync ansible.builtin.command: dash-cli mnsync status register: mnsync_status - retries: 100 - delay: 60 + retries: "{{ mn_sync_retries }}" + delay: "{{ mn_sync_poll_delay }}" until: mnsync_status.stdout | from_json | json_query("IsBlockchainSynced") == true changed_when: mnsync_status | length > 0 @@ -30,8 +30,8 @@ - name: Wait for wallet to sync with blockchain ansible.builtin.command: dash-cli mnsync status register: mnsync_status - retries: 100 - delay: 60 + retries: "{{ mn_sync_retries }}" + delay: "{{ mn_sync_poll_delay }}" until: mnsync_status.stdout | from_json | json_query('IsSynced') == true changed_when: mnsync_status.stdout | from_json | json_query('IsSynced') == true diff --git a/bin/deploy b/bin/deploy index 28c115689..3102417bc 100755 --- a/bin/deploy +++ b/bin/deploy @@ -55,11 +55,15 @@ case ${i} in ansible_playbook="${i#*=}.yml" ;; --fast) - EXTRA_VARS="$EXTRA_VARS -e fast_mode=true -e skip_dashmate_image_update=true -e skip_observability_setup=true -e metricbeat_enabled=false -e force_logs_config=false -e force_filebeat_restart=false" - echo "Fast mode enabled - skipping Docker image updates and observability/logging setup" + EXTRA_VARS="$EXTRA_VARS -e fast_mode=true -e skip_dashmate_image_update=true" + EXTRA_VARS="$EXTRA_VARS -e skip_dashd_image_update=true -e skip_deploy_cleanup=true" + EXTRA_VARS="$EXTRA_VARS -e skip_observability_setup=true -e metricbeat_enabled=false" + EXTRA_VARS="$EXTRA_VARS -e force_logs_config=false -e force_filebeat_restart=false" + EXTRA_VARS="$EXTRA_VARS -e mn_sync_poll_delay=10 -e mn_sync_retries=600" + echo "Fast mode enabled - skipping image updates, cleanup, and observability/logging setup" ;; --skip-image-update) - EXTRA_VARS="$EXTRA_VARS -e skip_dashmate_image_update=true" + EXTRA_VARS="$EXTRA_VARS -e skip_dashmate_image_update=true -e skip_dashd_image_update=true" ;; --prebaked-common-setup) EXTRA_VARS="$EXTRA_VARS -e prebaked_common_setup=true" From 3d84a7317d4d2f48600669481b19b6fb999e76c8 Mon Sep 17 00:00:00 2001 From: infraclaw Date: Fri, 29 May 2026 20:31:41 +0000 Subject: [PATCH 10/10] Keep fast-mode registration miner running --- README.md | 3 ++- ansible/deploy.yml | 18 ++++++++++++++++++ ansible/group_vars/all | 1 + .../roles/dashd_generate_miner/tasks/main.yml | 2 +- ansible/roles/generate_blocks/tasks/main.yml | 6 +++++- bin/deploy | 1 + 6 files changed, 28 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 1842e425b..422ba7c8a 100644 --- a/README.md +++ b/README.md @@ -83,7 +83,8 @@ For faster updates to an existing deployment, pass `--fast`. Fast mode skips Doc image updates, final Docker cleanup, and slow observability/logging setup such as CloudWatch Agent, Elastic/logs, filebeat, metricbeat, metrics, and status dashboards. It also polls masternode sync more frequently while preserving the -same maximum wait time. +same maximum wait time, and keeps the registration block generator running +through collateral/protx/spork waits instead of restarting it for each wait. To destroy an available Dash Network, use the `destroy` command: diff --git a/ansible/deploy.yml b/ansible/deploy.yml index 8c74a28f2..323896cd2 100644 --- a/ansible/deploy.yml +++ b/ansible/deploy.yml @@ -305,6 +305,7 @@ - role: mn_init mnlist: "{{ masternodes }}" funding_amount: "{{ masternode_collaterals.mn | int }}" + when: masternodes is defined and masternodes | length > 0 tags: - full_deploy - unban_masternodes @@ -314,6 +315,7 @@ roles: - role: mn_protx_config mnlist: "{{ masternodes }}" + when: masternodes is defined and masternodes | length > 0 tags: - full_deploy - unban_masternodes @@ -367,6 +369,22 @@ tags: - full_deploy +- name: Stop fast-mode registration miner + hosts: wallet_nodes + become: true + tasks: + - name: Stop persistent registration block generator + ansible.builtin.systemd: + name: 'dashd-generate-miner' + state: stopped + enabled: false + masked: false + when: + - generate_blocks_keep_miner_running | default(false) | bool + - dash_network == "devnet" or dash_network == "regtest" + tags: + - full_deploy + # todo: partially working code causes errors in deploy, comment out for now and fix later # - name: Create governance proposals # hosts: wallet_nodes diff --git a/ansible/group_vars/all b/ansible/group_vars/all index 5283e2dca..5b3423630 100644 --- a/ansible/group_vars/all +++ b/ansible/group_vars/all @@ -14,6 +14,7 @@ fast_mode: false skip_dashd_image_update: false skip_deploy_cleanup: false skip_observability_setup: false +generate_blocks_keep_miner_running: false mn_sync_poll_delay: 60 mn_sync_retries: 100 diff --git a/ansible/roles/dashd_generate_miner/tasks/main.yml b/ansible/roles/dashd_generate_miner/tasks/main.yml index ab68acd76..7afea7bf2 100644 --- a/ansible/roles/dashd_generate_miner/tasks/main.yml +++ b/ansible/roles/dashd_generate_miner/tasks/main.yml @@ -21,7 +21,7 @@ - name: Enable dashd-generate-miner ansible.builtin.systemd: name: 'dashd-generate-miner' - state: restarted + state: "{{ (dashd_generate_miner_restart | default(true) | bool) | ternary('restarted', 'started') }}" enabled: true masked: false daemon_reload: '{{ service_result.changed }}' diff --git a/ansible/roles/generate_blocks/tasks/main.yml b/ansible/roles/generate_blocks/tasks/main.yml index e95890a02..aa31a4f04 100644 --- a/ansible/roles/generate_blocks/tasks/main.yml +++ b/ansible/roles/generate_blocks/tasks/main.yml @@ -28,6 +28,8 @@ - name: Start generating blocks ansible.builtin.include_role: name: dashd_generate_miner + vars: + dashd_generate_miner_restart: "{{ not (generate_blocks_keep_miner_running | default(false) | bool) }}" when: dash_network in generate_networks # Option: blocks @@ -83,4 +85,6 @@ state: stopped enabled: false masked: false - when: dash_network in generate_networks + when: + - dash_network in generate_networks + - not (generate_blocks_keep_miner_running | default(false) | bool) diff --git a/bin/deploy b/bin/deploy index 3102417bc..455dcdead 100755 --- a/bin/deploy +++ b/bin/deploy @@ -59,6 +59,7 @@ case ${i} in EXTRA_VARS="$EXTRA_VARS -e skip_dashd_image_update=true -e skip_deploy_cleanup=true" EXTRA_VARS="$EXTRA_VARS -e skip_observability_setup=true -e metricbeat_enabled=false" EXTRA_VARS="$EXTRA_VARS -e force_logs_config=false -e force_filebeat_restart=false" + EXTRA_VARS="$EXTRA_VARS -e generate_blocks_keep_miner_running=true" EXTRA_VARS="$EXTRA_VARS -e mn_sync_poll_delay=10 -e mn_sync_retries=600" echo "Fast mode enabled - skipping image updates, cleanup, and observability/logging setup" ;;