diff --git a/.github/workflows/build-base-images.yml b/.github/workflows/build-base-images.yml new file mode 100644 index 000000000..fc590b76c --- /dev/null +++ b/.github/workflows/build-base-images.yml @@ -0,0 +1,132 @@ +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@34e114876b0b11c390a56381ad16ebd13914f8d5 + with: + persist-credentials: false + + - name: Set up Packer + uses: hashicorp/setup-packer@1aa358be5cf73883762b302a3a03abd66e75b232 + + - 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/.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..422ba7c8a 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`: @@ -78,6 +79,13 @@ 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, 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, 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: ```bash @@ -208,3 +216,32 @@ 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 +``` + +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 +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 60633a9a3..323896cd2 100644 --- a/ansible/deploy.yml +++ b/ansible/deploy.yml @@ -27,8 +27,9 @@ is_aws_environment: '{{ aws_uri_check.status == 200 }}' roles: - role: aws - when: is_aws_environment - - swap + when: is_aws_environment | bool + - role: swap + when: not (prebaked_common_setup | default(false) | bool) tags: - full_deploy - infra_setup @@ -38,7 +39,7 @@ become: true roles: - role: openvpn - when: openvpn_enabled + when: openvpn_enabled | bool tags: - full_deploy - infra_setup @@ -48,13 +49,14 @@ hosts: all become: true pre_tasks: - - name: Update apt cache and install jq + - 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: @@ -65,16 +67,21 @@ - name: requests version: "2.31.0" roles: - - geerlingguy.pip + - 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_architecture == 'x86_64' else 'arm64' }}" + docker_apt_arch: "{{ 'amd64' if ansible_facts['architecture'] == 'x86_64' else 'arm64' }}" docker_install_compose: false docker_users: - ubuntu - - docker_options - - eternal_terminal - - cwagent + - role: docker_options + when: not (prebaked_common_setup | default(false) | bool) + - role: eternal_terminal + when: not (prebaked_common_setup | default(false) | bool) + - role: cwagent + when: not (skip_observability_setup | default(false) | bool) tags: - full_deploy - infra_setup @@ -93,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 @@ -113,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 @@ -125,10 +136,13 @@ roles: - role: dash_cli - role: dashd + 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 @@ -140,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 @@ -164,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 @@ -192,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 @@ -224,7 +244,8 @@ hosts: web become: true roles: - - multifaucet + # Multifaucet is intentionally skipped; current devnets do not need it. + # - multifaucet - role: dash_cli - role: dashd dashd_indexes: true @@ -232,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 @@ -245,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 @@ -262,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 @@ -277,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 @@ -286,6 +315,7 @@ roles: - role: mn_protx_config mnlist: "{{ masternodes }}" + when: masternodes is defined and masternodes | length > 0 tags: - full_deploy - unban_masternodes @@ -339,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 @@ -363,12 +409,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 @@ -388,9 +434,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 @@ -415,6 +461,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 6aa72b711..5b3423630 100644 --- a/ansible/group_vars/all +++ b/ansible/group_vars/all @@ -4,6 +4,20 @@ 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 + +# 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 +generate_blocks_keep_miner_running: false +mn_sync_poll_delay: 60 +mn_sync_retries: 100 + 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..f702e536e --- /dev/null +++ b/ansible/prebake-common.yml @@ -0,0 +1,53 @@ +--- + +- 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" + dashmate_group: dashmate + dashmate_user: dashmate + dashmate_home: /home/dashmate + pre_tasks: + - name: Update apt cache and install common packages + ansible.builtin.apt: + pkg: + - acl + - 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 + - role: geerlingguy.docker + vars: + docker_apt_arch: "{{ 'amd64' if ansible_facts['architecture'] == 'x86_64' else 'arm64' }}" + docker_install_compose: false + docker_users: + - ubuntu + - "{{ dashmate_user }}" + - docker_options + - eternal_terminal diff --git a/ansible/roles/dashd/tasks/main.yml b/ansible/roles/dashd/tasks/main.yml index 43ed00c7e..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" @@ -101,15 +106,15 @@ 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 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 @@ -126,14 +131,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 +152,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/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/dashmate/defaults/main.yml b/ansible/roles/dashmate/defaults/main.yml index 3418fa238..b68d0b514 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 @@ -109,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 @@ -120,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/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 8f801b36b..321e8a4d9 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 (skip_dashmate_image_update | default(false)) - 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)) - name: Set user/group existence facts ansible.builtin.set_fact: dashmate_group_exists: >- - {%- if skip_dashmate_image_update | default(false) -%} - 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 skip_dashmate_image_update | default(false) -%} - 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 @@ -93,17 +87,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) or not (dashmate_group_exists | bool) or not (dashmate_user_exists | bool) - name: Create dashmate group ansible.builtin.group: name: '{{ dashmate_group }}' - when: - - not (skip_dashmate_image_update | default(false)) - - not dashmate_group_exists + register: dashmate_group_create + when: not (dashmate_group_exists | bool) - name: Create dashmate user ansible.builtin.user: @@ -114,26 +108,22 @@ create_home: true shell: /bin/bash umask: '0002' - when: - - not (skip_dashmate_image_update | default(false)) - - not dashmate_user_exists + 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 (skip_dashmate_image_update | default(false)) - - not dashmate_user_exists + 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 (skip_dashmate_image_update | default(false)) - - dashmate_user_exists + register: dashmate_docker_group_update + when: dashmate_user_exists | bool # ============================================================================ # EARLY PERMISSION FIXES - Ensure all dashmate directories have correct ownership @@ -153,14 +143,14 @@ - '{{ dashmate_config_dir }}' - '{{ dashmate_logs_dir }}' when: - - not (skip_dashmate_image_update | default(false)) - - dashmate_user_exists + - 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 (skip_dashmate_image_update | default(false)) - - dashmate_user_exists + - not (fast_mode | default(false) | bool) or (dashmate_user_create.changed | default(false) | bool) + - dashmate_user_exists | bool changed_when: false # ============================================================================ @@ -219,7 +209,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) @@ -233,9 +223,10 @@ - name: Configure logs ansible.builtin.import_tasks: ./logs.yml when: - - not (skip_dashmate_image_update | default(false)) - - not (logrotate_config_stat.stat.exists | default(false)) or force_logs_config | default(false) - - dashmate_user_exists # Only configure logs if user exists + - 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 # ============================================================================ # PHASE 5: Dashmate installation and configuration (always run) @@ -247,13 +238,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: @@ -275,7 +266,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 +278,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 +288,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,16 +308,16 @@ changed_when: dashmate_zerossl_id_result_fast.rc == 0 failed_when: false when: - - skip_dashmate_image_update | default(false) - - dashmate_platform_enable + - fast_mode | default(false) | bool + - dashmate_platform_enable | bool - dashmate_platform_gateway_ssl_provider == 'zerossl' - name: Set ZeroSSL certificate ID from config (fast mode) ansible.builtin.set_fact: dashmate_zerossl_config_certificate_id: "{{ dashmate_zerossl_id_result_fast.stdout }}" when: - - skip_dashmate_image_update | default(false) - - dashmate_platform_enable + - fast_mode | default(false) | bool + - dashmate_platform_enable | bool - dashmate_platform_gateway_ssl_provider == 'zerossl' - dashmate_zerossl_id_result_fast is defined - dashmate_zerossl_id_result_fast.rc == 0 @@ -361,7 +352,12 @@ - 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: @@ -374,8 +370,8 @@ mode: "0644" register: template_result_fast when: - - dashmate_user_exists - - skip_dashmate_image_update | default(false) + - dashmate_user_exists | bool + - fast_mode | default(false) | bool - name: Generate new config content for comparison (regular mode) vars: @@ -384,20 +380,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 +404,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,20 +415,20 @@ mode: "0644" register: template_result_regular when: - - not (skip_dashmate_image_update | default(false)) - - dashmate_user_exists - - config_needs_update + - not (fast_mode | default(false) | bool) + - dashmate_user_exists | bool + - config_needs_update | bool - name: Clean up temp file (regular mode) 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) | bool) }}" dest: "{{ dashmate_config_dir }}/config.json" - name: Debug config change details @@ -443,7 +439,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: @@ -461,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 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) @@ -471,7 +470,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: @@ -480,33 +479,34 @@ 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)) }} + 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 (skip_dashmate_image_update | 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 (skip_dashmate_image_update | 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 (skip_dashmate_image_update | default(false)) + - not (ssl_cert_stat.stat.exists | default(false)) or force_ssl_regenerate | default(false) | bool + - not (fast_mode | default(false) | bool) # ============================================================================ # PHASE 7: Environment and Docker images @@ -526,7 +526,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: @@ -536,13 +536,13 @@ 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 - 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 +553,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,20 +587,20 @@ }} 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)) }} - 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: @@ -625,8 +625,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 @@ -636,7 +636,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' @@ -644,43 +644,43 @@ chdir: '{{ dashmate_cwd }}' register: dashmate_restart_all when: - - not (dashmate_start_all.changed | default(false)) - - is_dashmate_package_changed or is_dashmate_config_changed - - skip_dashmate_image_update | default(false) + - 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 # Regular deployment: coordinated chunked restarts - 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 (skip_dashmate_image_update | default(false)) + - 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 (skip_dashmate_image_update | default(false)) + - 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 (skip_dashmate_image_update | default(false)) + - 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] - name: Print chunks info 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 (skip_dashmate_image_update | default(false)) + - 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] - name: Restart all dashmate services by chunks (regular mode) @@ -691,9 +691,9 @@ 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 (skip_dashmate_image_update | default(false)) + - 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] # Force start logic @@ -701,7 +701,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' @@ -709,9 +709,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/tasks/ssl/zerossl.yml b/ansible/roles/dashmate/tasks/ssl/zerossl.yml index eb33d5641..eb10213e9 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,18 +48,19 @@ 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" + mode: "{{ '0600' if item == dashmate_zerossl_private_key_file_name else '0644' }}" loop: - '{{ dashmate_zerossl_private_key_file_name }}' - '{{ 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..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 }} }, @@ -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) | bool | to_json }} }, "mempool": { "cacheSize": {{dashmate_platform_drive_tenderdash_mempool_cache_size}}, @@ -405,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/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/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/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_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 9a8ff529a..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 @@ -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/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 diff --git a/bin/build-base-image b/bin/build-base-image new file mode 100755 index 000000000..017f5bdd0 --- /dev/null +++ b/bin/build-base-image @@ -0,0 +1,75 @@ +#!/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 ! 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 + +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..455dcdead 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" @@ -54,11 +55,20 @@ 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" + 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" ;; --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" + echo "Pre-baked common setup enabled - skipping common package/Docker/swap setup" ;; --core-restart) EXTRA_VARS="$EXTRA_VARS -e include_core_restart=true" 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/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/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/.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..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 @@ -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 = 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 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 = 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 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 @@ -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 3845f5ad8..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 { @@ -43,6 +45,12 @@ data "aws_ami" "ubuntu_arm" { # Canonical } +locals { + 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 +} + # Create a VPC to launch our instances into resource "aws_vpc" "default" { cidr_block = var.vpc_cidr @@ -86,11 +94,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 +257,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 +454,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,12 +515,11 @@ 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" DashNetwork = terraform.workspace } } - diff --git a/terraform/aws/variables.tf b/terraform/aws/variables.tf index 2d81b7a4b..bc925595a 100644 --- a/terraform/aws/variables.tf +++ b/terraform/aws/variables.tf @@ -1,6 +1,28 @@ 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 = "" + + 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" { description = "Port for Dash Core nodes" default = 20001 @@ -297,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 = "" } -