diff --git a/.github/renovate.json b/.github/renovate.json new file mode 100644 index 0000000..9f082cf --- /dev/null +++ b/.github/renovate.json @@ -0,0 +1,7 @@ +{ + "$schema": "https://docs.renovatebot.com/renovate-schema.json", + "extends": [ + "config:base" + ], + "enabledManagers": ["pip_requirements", "github-actions"] +} diff --git a/.github/workflows/ansible.yml b/.github/workflows/ansible.yml new file mode 100644 index 0000000..5464118 --- /dev/null +++ b/.github/workflows/ansible.yml @@ -0,0 +1,73 @@ +name: ansible + +on: + pull_request: + paths: + - "ansible/**" + - ".github/workflows/ansible.yml" + - "!.github/**" + - "!ansible/**.md" + +concurrency: + group: "molecule-${{ github.ref }}" + cancel-in-progress: true + +env: + PY_COLORS: 1 + ANSIBLE_FORCE_COLOR: 1 + FORCE_COLOR: 1 + +jobs: + lint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Run yamllint + uses: frenck/action-yamllint@v1 + with: + config: "ansible/.yamllint" + path: "ansible" + + # - name: Run ansible-later + # uses: patrickjahns/ansible-later-action@v1.3.0 + # with: + # path: ansible/** + + molecule: + runs-on: ubuntu-latest + + needs: lint + + strategy: + fail-fast: false + matrix: + scenario: + - ubuntu2204 + + defaults: + run: + working-directory: ansible + + steps: + - uses: actions/checkout@v4 + + - name: Prepare for KVM usage + run: | + sudo apt-get install -y mkisofs qemu-system-x86 qemu-utils + echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules + sudo udevadm control --reload-rules + sudo udevadm trigger --name-match=kvm + sudo usermod -a -G kvm $USER + + - name: Setup python + uses: actions/setup-python@v4 + with: + python-version: "3.x" + cache: 'pip' + + - name: Install python dependencies + run: python -m pip install -r requirements.txt + + - name: Run ${{ matrix.scenario }} scenario + run: molecule test --scenario-name "${{ matrix.scenario }}" diff --git a/ansible/.gitignore b/ansible/.gitignore new file mode 100644 index 0000000..9e9f451 --- /dev/null +++ b/ansible/.gitignore @@ -0,0 +1,3 @@ +files/** +**.pyc +**/__pycache__/ diff --git a/ansible/.yamllint b/ansible/.yamllint new file mode 100644 index 0000000..7975869 --- /dev/null +++ b/ansible/.yamllint @@ -0,0 +1,32 @@ +# vim:ft=yaml +extends: default + +rules: + braces: + max-spaces-inside: 1 + level: error + brackets: + max-spaces-inside: 1 + level: error + colons: + max-spaces-after: -1 + level: error + commas: + max-spaces-after: -1 + level: error + comments: disable + comments-indentation: disable + document-start: disable + empty-lines: + max: 3 + level: error + hyphens: + level: error + indentation: disable + key-duplicates: enable + line-length: disable + new-line-at-end-of-file: disable + new-lines: + type: unix + trailing-spaces: disable + truthy: disable diff --git a/ansible/README.md b/ansible/README.md new file mode 100644 index 0000000..620f2ff --- /dev/null +++ b/ansible/README.md @@ -0,0 +1,264 @@ +# Nox + +Install and configure [Nox](https://github.com/fluencelabs/nox/) - Rust +implementation of the Fluence network peer. + +## Usage + +### Generate provider config and nox configs + +- Install + [Fluence CLI](https://github.com/fluencelabs/cli?tab=readme-ov-file#installation-and-usage) +- Generate sample provider config in directory with ansible playbook: + ```bash + mkdir files/playground -p && cd files/playground + fluence provider gen --env local --noxes 3 --no-input + ``` + +- Adapt provider config in `playground/provider.yml` for your setup. + For example: + ```yaml + # yaml-language-server: $schema=../.fluence/schemas/provider.json + + # Defines config used for provider set up + + # Documentation: https://github.com/fluencelabs/cli/tree/main/docs/configs/provider.md + + version: 0 + + env: local + + computePeers: + nox-0: + computeUnits: 1 + nox-1: + computeUnits: 1 + nox-2: + computeUnits: 1 + + offers: + offer: + maxCollateralPerWorker: 1 + minPricePerWorkerEpoch: 0.1 + computePeers: + - nox-0 + - nox-1 + - nox-2 + + nox: + # you can write config overrides with yaml syntax using camelCase + systemServices: + enabled: + - aqua-apfs + - decider + - trust-graph + + # or you can write config in toml + # some options can be set only with rawConfig + # this has highest priority when merging + rawConfig: | + allowed_binaries = [ + "/usr/bin/curl", + # we need to set path to ipfs binary that was downloaded by role + "{{ nox_dir }}/ipfs", + ] + + [system_services] + [aqua-apfs] + external_api_multiaddres = "/ip4/ipfs.playground.com/tcp/5001" + local_api_multiaddres = "/ip4/ipfs.service.consul/tcp/5001" + decider: + worker_ipfs_multiaddr = "/dns4/ipfs.playground.com/tcp/5001" + network_api_endpoint = "https://somechain.com" + ``` + +- Have a look at secrets config at `.fluence/provider-secrets.yaml` - it should be made private and not commited to git + +- Regenerate nox configs + ```bash + fluence provider gen + ``` + +### Install and configure nox using ansible role + +- Populate ansible inventory with required variables + - Create `inventory/group_vars/all/nox.yml` with common for all nox instances + variables: + ```yml + nox_version: "0.16.13" + nox_ipfs_version: "0.25.0" + nox_project_dir: "playground" + ``` + - Assign nox instances to hosts, for example by using `host_vars`: + - `inventory/host_vars/instance-0/nox.yml` + ```yml + nox_instances: [0, 1] + ``` + - `inventory/host_vars/instance-1/nox.yml` + ```yml + nox_instances: [2] + ``` + +- Download role and prepare playbook: + - Create `requirements.yml` + ```yml + roles: + - name: fluencelabs.nox + version: 0.1.0 + name: nox + ``` + - Install nox role + ```bash + ansible-galaxy install -r requirements.yml --force + ``` + - Create `nox.yml` playbook: + ```yml + - hosts: "all" + become: true + roles: + - "nox" + ``` +- Install nox + ```bash + ansible-playbook nox.yml + ``` + +This will run `nox-0` and `nox-1` on `instance-0` and `nox-2` on `instance-1`. + +### Cleanup nox state + +Rerun playbook with `nox_cleanup_state` set to `true`: +```bash +ansible-playbook nox.yml -e "nox_cleanup_state=true" +``` + +### Reassign instances to different host + +`nox-0` and `nox-1` runs on `instance-0` and `nox-2` on `instance-1`. + +- `inventory/host_vars/instance-0/nox.yml` + ```yml + nox_instances: [0, 1] + ``` +- `inventory/host_vars/instance-1/nox.yml` + ```yml + nox_instances: [2] + ``` + +Reassign `nox-0` to `instance-1` + +- `inventory/host_vars/instance-0/nox.yml` + ```yml + nox_instances: [1] + ``` +- `inventory/host_vars/instance-1/nox.yml` + ```yml + nox_instances: [2, 0] + ``` + +and run playbook: +```bash +ansible-playbook nox.yml +``` + +This will stop `nox-0` at `instance-0` and will run it on `instance-1`. + +### Install nox snapshot from PR + +Only for Fluence Labs members. + +- Go to GitHub to e2e run from your PR, for example + https://github.com/fluencelabs/nox/actions/runs/7409293504 - `7409293504` is + run id +- Rerun role providing your `GITHUB_TOKEN` as env variable: + ```bash + GITHUB_TOKEN= ansible-playbook nox.yml -e "nox_run_id=7409293504" + ``` + +## Role Variables + +See [defaults/](https://github.com/fluencelabs/deployment/blob/main/ansible/defaults) for details and examples. + +#### `nox_version` + +- version of nox +- type: string + +#### `nox_ipfs_version` + +- version of IPFS binary that is used by nox +- type: string + +#### `nox_instances` + +- list of nox instances to install on target +- type: list + +#### `nox_dir` + +- root nox directory +- type: string +- default: + ```yml + nox_dir: "/opt/nox" + ``` + +It will contain everything this role creates: IPFS binary, nox instances +subdirectories (`nox-1`, `nox-foo` etc) with nox binaries, configs and state. + +#### `nox_project_dir` + +- directory that contains configs and secrets generated by + [Fluence CLI](https://github.com/fluencelabs/cli) from `provider.yaml` config. +- type: string +- default: + ```yml + nox_project_dir: ".fluence" + ``` + +Should be put to `files/` directory where you run this role. + +#### `nox_unit_file` + +- systemd unit file +- type: string +- default: see [defaults/main.yml](https://github.com/fluencelabs/deployment/blob/main/ansible/defaults/main.yml) + +#### `nox_user` + +- owner of Nox process and files +- type: string +- default: + ```yml + nox_user: "nox" + ``` + +#### `nox_group` + +- group of `nox_user` +- type: string +- default: + ```yml + nox_group: "nox" + ``` + +#### `nox_cleanup_state` + +- whether to cleanup nox state. +- type: bool +- default: + ```yml + nox_cleanup_state: false + ``` + +#### `nox_run_id` + +- GitHub actions run id of workflow in + [nox e2e run](https://github.com/fluencelabs/nox/actions/workflows/e2e.yml). + Used by Fluence Labs internally to install a snapshot version of nox for + testing. `GITHUB_TOKEN` is required. +- type: string + +## Author + +- **Anatolios Laskaris** - [nahsi](https://github.com/nahsi) diff --git a/ansible/defaults/example.yml b/ansible/defaults/example.yml new file mode 100644 index 0000000..845a587 --- /dev/null +++ b/ansible/defaults/example.yml @@ -0,0 +1,4 @@ +nox_version: "0.16.13" +nox_ipfs_version: "0.25.0" +nox_project_dir: "playground" +nox_instances: [0, 1, 2] diff --git a/ansible/defaults/main.yml b/ansible/defaults/main.yml new file mode 100644 index 0000000..b473158 --- /dev/null +++ b/ansible/defaults/main.yml @@ -0,0 +1,26 @@ +nox_version: "" +nox_ipfs_version: "" +nox_instances: [] +nox_dir: "/opt/nox" +nox_project_dir: ".fluence" +nox_group: "nox" +nox_user: "nox" +nox_run_id: "" +nox_cleanup_state: false +nox_unit_file: | + [Unit] + Description=Nox - Rust implementation of the Fluence network peer + After=network.target + + [Service] + Type=simple + Environment="FLUENCE_CONFIG={{ nox_dir }}/nox-%i/Config.toml" + Environment="FLUENCE_BASE_DIR={{ nox_dir }}/nox-%i/state" + Environment="FLUENCE_ROOT_KEY_PAIR__PATH={{ nox_dir }}/nox-%i/state/root_secret_key.ed25519" + ExecStart={{ nox_dir }}/nox-%i/nox + Restart=on-failure + User={{ nox_user }} + Group={{ nox_group }} + + [Install] + WantedBy=multi-user.target diff --git a/ansible/handlers/main.yml b/ansible/handlers/main.yml new file mode 100644 index 0000000..ad36b38 --- /dev/null +++ b/ansible/handlers/main.yml @@ -0,0 +1,9 @@ +- name: restart nox + systemd: + daemon_reload: true + name: nox-@{{ instance }} + state: restarted + loop: "{{ _instances_to_restart }}" + loop_control: + loop_var: instance + label: "nox-{{ instance }}" diff --git a/ansible/meta/main.yml b/ansible/meta/main.yml new file mode 100644 index 0000000..616911a --- /dev/null +++ b/ansible/meta/main.yml @@ -0,0 +1,18 @@ +galaxy_info: + namespace: fluencelabs + role_name: nox + author: Anatolios Laskaris + description: Install and setup Nox - Rust implementation of the Fluence network peer + issue_tracker_url: https://github.com/fluencelabs/deployments + min_ansible_version: 2.12 + + platforms: + - name: Ubuntu + versions: + - jammy + + galaxy_tags: + - p2p + - fluence + +dependencies: [] diff --git a/ansible/molecule/converge.yml b/ansible/molecule/converge.yml new file mode 100644 index 0000000..0883d19 --- /dev/null +++ b/ansible/molecule/converge.yml @@ -0,0 +1,9 @@ +- name: nox role + hosts: all + become: true + vars: + nox_version: "0.16.13" + nox_ipfs_version: "0.25.0" + nox_project_dir: "molecule" + roles: + - ansible diff --git a/ansible/molecule/files/molecule/configs/nox-0_Config.toml b/ansible/molecule/files/molecule/configs/nox-0_Config.toml new file mode 100644 index 0000000..0bef93e --- /dev/null +++ b/ansible/molecule/files/molecule/configs/nox-0_Config.toml @@ -0,0 +1,25 @@ +aquavm_pool_size = 2 +tcp_port = 7_773 +websocket_port = 9_991 +http_port = 18_080 + +allowed_binaries = [ + "/usr/bin/curl", + "{{ nox_dir }}/ipfs", +] + +[system_services] +enable = [ "aqua-ipfs", "decider" ] + + [system_services.aqua_ipfs] + external_api_multiaddr = "/ip4/127.0.0.1/tcp/5001" + local_api_multiaddr = "/dns4/ipfs/tcp/5001" + + [system_services.decider] + decider_period_sec = 10 + worker_ipfs_multiaddr = "/dns4/ipfs/tcp/5001" + network_api_endpoint = "http://chain:8545" + network_id = 31_337 + start_block = "earliest" + matcher_address = "0x0e1F3B362E22B2Dc82C9E35d6e62998C7E8e2349" + wallet_key = "0x3cc23e0227bd17ea5d6ea9d42b5eaa53ad41b1974de4755c79fe236d361a6fd5" diff --git a/ansible/molecule/files/molecule/configs/nox-1_Config.toml b/ansible/molecule/files/molecule/configs/nox-1_Config.toml new file mode 100644 index 0000000..5cfcb5d --- /dev/null +++ b/ansible/molecule/files/molecule/configs/nox-1_Config.toml @@ -0,0 +1,25 @@ +aquavm_pool_size = 2 +tcp_port = 7_772 +websocket_port = 9_992 +http_port = 18_081 + +allowed_binaries = [ + "/usr/bin/curl", + "{{ nox_dir }}/ipfs", +] + +[system_services] +enable = [ "aqua-ipfs", "decider" ] + + [system_services.aqua_ipfs] + external_api_multiaddr = "/ip4/127.0.0.1/tcp/5001" + local_api_multiaddr = "/dns4/ipfs/tcp/5001" + + [system_services.decider] + decider_period_sec = 10 + worker_ipfs_multiaddr = "/dns4/ipfs/tcp/5001" + network_api_endpoint = "http://chain:8545" + network_id = 31_337 + start_block = "earliest" + matcher_address = "0x0e1F3B362E22B2Dc82C9E35d6e62998C7E8e2349" + wallet_key = "0x089162470bcfc93192b95bff0a1860d063266875c782af9d882fcca125323b41" diff --git a/ansible/molecule/files/molecule/configs/nox-2_Config.toml b/ansible/molecule/files/molecule/configs/nox-2_Config.toml new file mode 100644 index 0000000..c1033e2 --- /dev/null +++ b/ansible/molecule/files/molecule/configs/nox-2_Config.toml @@ -0,0 +1,25 @@ +aquavm_pool_size = 2 +tcp_port = 7_773 +websocket_port = 9_993 +http_port = 18_082 + +allowed_binaries = [ + "/usr/bin/curl", + "{{ nox_dir }}/ipfs", +] + +[system_services] +enable = [ "aqua-ipfs", "decider" ] + + [system_services.aqua_ipfs] + external_api_multiaddr = "/ip4/127.0.0.1/tcp/5001" + local_api_multiaddr = "/dns4/ipfs/tcp/5001" + + [system_services.decider] + decider_period_sec = 10 + worker_ipfs_multiaddr = "/dns4/ipfs/tcp/5001" + network_api_endpoint = "http://chain:8545" + network_id = 31_337 + start_block = "earliest" + matcher_address = "0x0e1F3B362E22B2Dc82C9E35d6e62998C7E8e2349" + wallet_key = "0xdacd4b197ee7e9efdd5db1921c6c558d88e2c8b69902b8bafc812fb226a6b5e0" diff --git a/ansible/molecule/files/molecule/provider.yaml b/ansible/molecule/files/molecule/provider.yaml new file mode 100644 index 0000000..5399aae --- /dev/null +++ b/ansible/molecule/files/molecule/provider.yaml @@ -0,0 +1,26 @@ +# yaml-language-server: $schema=../.fluence/schemas/provider.json + +# Defines config used for provider set up + +# Documentation: https://github.com/fluencelabs/cli/tree/main/docs/configs/provider.md + +version: 0 + +env: local + +computePeers: + nox-0: + computeUnits: 1 + nox-1: + computeUnits: 1 + nox-2: + computeUnits: 1 + +offers: + offer: + maxCollateralPerWorker: 1 + minPricePerWorkerEpoch: 0.1 + computePeers: + - nox-0 + - nox-1 + - nox-2 diff --git a/ansible/molecule/files/molecule/secrets/nox-0.txt b/ansible/molecule/files/molecule/secrets/nox-0.txt new file mode 100644 index 0000000..b33d97c --- /dev/null +++ b/ansible/molecule/files/molecule/secrets/nox-0.txt @@ -0,0 +1 @@ +2WDYG1Ae573dglTN6ctyNTaYO2ZlcrjjQjkeFjpihcE= \ No newline at end of file diff --git a/ansible/molecule/files/molecule/secrets/nox-1.txt b/ansible/molecule/files/molecule/secrets/nox-1.txt new file mode 100644 index 0000000..feceb23 --- /dev/null +++ b/ansible/molecule/files/molecule/secrets/nox-1.txt @@ -0,0 +1 @@ +hda9vRX8eR3VFlTVNqZ8jhvw1TnNrKVPB+lCxHefOGQ= \ No newline at end of file diff --git a/ansible/molecule/files/molecule/secrets/nox-2.txt b/ansible/molecule/files/molecule/secrets/nox-2.txt new file mode 100644 index 0000000..75d4133 --- /dev/null +++ b/ansible/molecule/files/molecule/secrets/nox-2.txt @@ -0,0 +1 @@ +vO8ost9xZRCsQWiMc/ttsVjcUteV8BqJnojnkBVsJ+Y= \ No newline at end of file diff --git a/ansible/molecule/prepare.yml b/ansible/molecule/prepare.yml new file mode 100644 index 0000000..1dd42d5 --- /dev/null +++ b/ansible/molecule/prepare.yml @@ -0,0 +1,10 @@ +- name: prepare + hosts: all + become: true + vars: + nox_version: "0.16.13" + nox_ipfs_version: "0.25.0" + nox_project_dir: "molecule" + nox_instances: [0] + roles: + - ansible diff --git a/ansible/molecule/tests/test_default.py b/ansible/molecule/tests/test_default.py new file mode 100644 index 0000000..fb180ee --- /dev/null +++ b/ansible/molecule/tests/test_default.py @@ -0,0 +1,50 @@ +import testinfra.utils.ansible_runner +import pytest +import os + +testinfra_hosts = testinfra.utils.ansible_runner.AnsibleRunner( + os.environ["MOLECULE_INVENTORY_FILE"]).get_hosts("all") + + +@pytest.mark.parametrize("directory", [ + "/opt/nox/nox-1", + "/opt/nox/nox-2", +]) +def test_nox_directories(host, directory): + d = host.file(directory) + + assert d.is_directory + assert d.exists + assert d.user == "nox" + assert d.mode == 493 + +def test_nox_cleanup(host): + directory = host.file("/opt/nox/nox-0") + service = host.service("nox-@0") + + assert not service.is_running + assert not service.is_enabled + assert not directory.exists + + +def test_ipfs_download(host): + f = host.file("/opt/nox/ipfs") + + assert f.is_file + assert f.exists + + +@pytest.mark.parametrize("service", [ + "nox-@1", + "nox-@2", +]) +def test_nox_is_running(host, service): + service = host.service(service) + + assert service.is_running + assert service.is_enabled + + +def test_user_created(host): + assert host.user("nox").exists + assert "nox" in host.user("nox").groups diff --git a/ansible/molecule/ubuntu2204/molecule.yml b/ansible/molecule/ubuntu2204/molecule.yml new file mode 100644 index 0000000..90256c0 --- /dev/null +++ b/ansible/molecule/ubuntu2204/molecule.yml @@ -0,0 +1,29 @@ +driver: + name: molecule-qemu + +platforms: + - name: ubuntu2204 + image_url: https://cloud-images.ubuntu.com/jammy/current/jammy-server-cloudimg-amd64.img + image_checksum: sha256:https://cloud-images.ubuntu.com/jammy/current/SHA256SUMS + network_ssh_port: 2228 + +provisioner: + name: ansible + playbooks: + prepare: ../prepare.yml + converge: ../converge.yml + inventory: + group_vars: + all: + nox_instances: [1, 2] + +verifier: + name: testinfra + directory: ../tests + env: + # get rid of the DeprecationWarning messages of third-party libs, + # see https://docs.pytest.org/en/latest/warnings.html#deprecationwarning-and-pendingdeprecationwarning + PYTHONWARNINGS: "ignore:.*U.*mode is deprecated:DeprecationWarning" + options: + # show which tests where executed in test output + v: 1 diff --git a/ansible/requirements.txt b/ansible/requirements.txt new file mode 100644 index 0000000..877738f --- /dev/null +++ b/ansible/requirements.txt @@ -0,0 +1,7 @@ +ansible==8.6.1 + +# molecule +pytest==7.2.1 +pytest-testinfra==7.0.0 +molecule==6.0.2 +molecule-qemu==0.5.7 diff --git a/ansible/tasks/00-preflight.yml b/ansible/tasks/00-preflight.yml new file mode 100644 index 0000000..3ef9296 --- /dev/null +++ b/ansible/tasks/00-preflight.yml @@ -0,0 +1,74 @@ +- name: check that GITHUB_TOKEN is defined + tags: always + vars: + github_token: "{{ lookup('env', 'GITHUB_TOKEN') }}" + assert: + that: + - github_token is string + - github_token | length + quiet: true + when: nox_run_id | string | length + +- name: check "nox_instances" variable + tags: always + assert: + that: + - nox_instances is iterable + - nox_instances is not string + quiet: true + +- name: convert all elements in nox_instances to strings + set_fact: + nox_instances: "{{ nox_instances | map('string') | list }}" + +- name: check "nox_version" variable + tags: always + assert: + that: + - nox_version is defined + - nox_version is string + - nox_version | length + - nox_version is regex(_semver_regex) + quiet: true + +- name: check "nox_unit_file" variable + tags: always + assert: + that: + - nox_unit_file is defined + - nox_unit_file is string + - nox_unit_file | length + quiet: true + +- name: gather stats about nox configs + tags: always + become: false + delegate_to: localhost + stat: + path: "files/{{ nox_project_dir }}/configs/nox-{{ instance }}_Config.toml" + register: _config_stat + loop: "{{ nox_instances }}" + loop_control: + loop_var: instance + label: "nox-{{ instance }}" + +- name: check if config file exists + tags: always + vars: + _config_path: "files/{{ nox_project_dir }}/configs/nox-{{ item.instance }}_Config.toml" + debug: + msg: "Checking existence of nox-{{ item.instance }} config at {{ _config_path }}" + loop: "{{ _config_stat.results }}" + loop_control: + label: "nox-{{ item.instance }}" + failed_when: not item.stat.exists + +- name: check "nox_ipfs_version" variable + tags: always + assert: + that: + - nox_ipfs_version is defined + - nox_ipfs_version is string + - nox_ipfs_version | length + - nox_ipfs_version is regex(_semver_regex) + quiet: true diff --git a/ansible/tasks/01-cleanup.yml b/ansible/tasks/01-cleanup.yml new file mode 100644 index 0000000..d5eea93 --- /dev/null +++ b/ansible/tasks/01-cleanup.yml @@ -0,0 +1,39 @@ +- name: get list of all running nox instances + command: systemctl list-units --type=service --no-legend --no-pager nox-@* + register: _running_instances + changed_when: false + +- name: extract running nox instance names + vars: + _present_instances: "{{ _running_instances.stdout_lines + | map('regex_replace', '.*?nox-@([^\\s]+)\\.service.*', '\\1') + | list }}" + set_fact: + _cleanup_instances: "{{ _present_instances | difference(nox_instances) | default([]) }}" + changed_when: false + +- name: instances to cleanup + debug: + msg: "{{ _cleanup_instances }}" + +- name: cleanup nox instances + when: _cleanup_instances | length + block: + - name: stop and disable noxes + systemd: + name: nox-@{{ instance }} + enabled: false + state: stopped + loop: "{{ _cleanup_instances }}" + loop_control: + loop_var: instance + label: "nox-{{ instance }}" + + - name: cleanup nox directories + file: + path: "{{ nox_dir }}/nox-{{ instance }}" + state: absent + loop: "{{ _cleanup_instances }}" + loop_control: + loop_var: instance + label: "nox-{{ instance }}" diff --git a/ansible/tasks/02-install.yml b/ansible/tasks/02-install.yml new file mode 100644 index 0000000..aaefaeb --- /dev/null +++ b/ansible/tasks/02-install.yml @@ -0,0 +1,204 @@ +- name: create nox system group + group: + name: "{{ nox_group }}" + system: true + state: present + +- name: create nox system user + user: + name: "{{ nox_user }}" + system: true + shell: "/sbin/nologin" + group: "{{ nox_group }}" + create_home: false + state: present + +- name: cleanup nox state + file: + path: "{{ nox_dir }}/nox-{{ instance }}" + state: absent + when: nox_cleanup_state + loop: "{{ nox_instances }}" + loop_control: + loop_var: instance + label: "nox-{{ instance }}" + +- name: create nox directories + file: + path: "{{ nox_dir }}/nox-{{ instance }}/state" + state: directory + owner: "{{ nox_user }}" + group: "{{ nox_group }}" + mode: 0755 + loop: "{{ nox_instances }}" + loop_control: + loop_var: instance + label: "nox-{{ instance }}" + +- name: download nox release to localhost + become: false + run_once: true + delegate_to: localhost + when: nox_run_id | string | length == 0 + block: + - name: create files directory + become: false + run_once: true + delegate_to: localhost + file: + path: "{{ role_path }}/files/nox/{{ _nox_version }}" + state: directory + + - name: download nox binary + get_url: + url: "{{ _nox_download_url }}/{{ _nox_bin }}" + dest: "{{ role_path }}/files/nox/{{ _nox_version }}/{{ _nox_bin }}" + checksum: "{{ _nox_checksums }}" + register: _download_bin + until: _download_bin is succeeded + retries: 5 + delay: 2 + + - name: propagate nox binary + become: true + run_once: false + delegate_to: "{{ inventory_hostname }}" + copy: + src: "{{ role_path }}/files/nox/{{ _nox_version }}/{{ _nox_bin }}" + dest: "{{ nox_dir }}/nox-{{ instance }}/nox" + owner: "{{ nox_user }}" + group: "{{ nox_group }}" + mode: 0555 + loop: "{{ nox_instances }}" + loop_control: + loop_var: instance + label: "nox-{{ instance }}" + notify: restart nox + register: _binary_release + +- name: download nox snapshot to localhost + become: false + run_once: true + delegate_to: localhost + vars: + github_token: "{{ lookup('env', 'GITHUB_TOKEN') }}" + when: nox_run_id | string | length + block: + - name: get list of artifacts from GitHub API + uri: + url: "https://api.github.com/repos/fluencelabs/nox/actions/runs/{{ nox_run_id }}/artifacts" + method: GET + headers: + Accept: "application/vnd.github.v3+json" + return_content: true + register: _artifacts_response + + - name: parse the artifact download URL + set_fact: + _artifact_download_url: "{{ item.archive_download_url }}" + loop: "{{ _artifacts_response.json.artifacts }}" + when: "item.name == _bin" + loop_control: + label: "{{ item.url }}" + + - name: create a temporary directory + tempfile: + state: directory + register: _artifact_temp_dir + + - name: download the artifact + get_url: + url: "{{ _artifact_download_url }}" + dest: "{{ _artifact_temp_dir.path }}/{{ _nox_bin }}.zip" + headers: + Authorization: "token {{ github_token }}" + register: _download_artifact + until: _download_artifact is succeeded + retries: 5 + delay: 2 + + - name: unarchive the downloaded artifact + unarchive: + src: "{{ _artifact_temp_dir.path }}/{{ _nox_bin }}.zip" + dest: "{{ _artifact_temp_dir.path }}/" + + - name: propagate nox binary + become: true + run_once: false + delegate_to: "{{ inventory_hostname }}" + copy: + src: "{{ _artifact_temp_dir.path }}/nox" + dest: "{{ nox_dir }}/nox-{{ instance }}/nox" + owner: "{{ nox_user }}" + group: "{{ nox_group }}" + mode: 0555 + loop: "{{ nox_instances }}" + loop_control: + loop_var: instance + label: "nox-{{ instance }}" + notify: restart nox + register: _binary_snapshot + + always: + - name: remove temporary directory + file: + path: "{{ _artifact_temp_dir.path }}" + state: absent + when: _artifact_temp_dir.path is defined + +- name: copy nox config + template: + src: "files/{{ nox_project_dir }}/configs/nox-{{ instance }}_Config.toml" + dest: "{{ nox_dir }}/nox-{{ instance }}/Config.toml" + owner: "{{ nox_user }}" + group: "{{ nox_group }}" + mode: 0640 + loop: "{{ nox_instances }}" + loop_control: + loop_var: instance + label: "nox-{{ instance }}" + notify: restart nox + register: _config + +- name: copy nox secret + template: + src: "files/{{ nox_project_dir }}/secrets/nox-{{ instance }}.txt" + dest: "{{ nox_dir }}/nox-{{ instance }}/state/root_secret_key.ed25519" + owner: "{{ nox_user }}" + group: "{{ nox_group }}" + mode: 0600 + loop: "{{ nox_instances }}" + loop_control: + loop_var: instance + label: "nox-{{ instance }}" + notify: restart nox + register: _secret + +- name: create nox unit file + copy: + content: "{{ nox_unit_file }}" + dest: "/etc/systemd/system/nox-@.service" + owner: root + group: root + mode: 0755 + register: _systemd + +- name: enable noxes + systemd: + name: nox-@{{ instance }} + enabled: true + daemon_reload: true + loop: "{{ nox_instances }}" + loop_control: + loop_var: instance + label: "nox-{{ instance }}" + +- name: determine which instances to restart + vars: + _binary_release_changed: "{{ _binary_release.results | selectattr('changed', 'equalto', true) | map(attribute='instance') | list }}" + _binary_snapshot_changed: "{{ _binary_snapshot.results | selectattr('changed', 'equalto', true) | map(attribute='instance') | list }}" + _config_changed: "{{ _config.results | selectattr('changed', 'equalto', true) | map(attribute='instance') | list }}" + _secret_changed: "{{ _secret.results | selectattr('changed', 'equalto', true) | map(attribute='instance') | list }}" + _instances_changed: "{{ (_systemd is changed) | ternary(nox_instances, _binary_release_changed + _binary_snapshot_changed + _config_changed + _secret_changed) }}" + set_fact: + _instances_to_restart: "{{ (nox_instances | select('in', _instances_changed) | list) | unique }}" diff --git a/ansible/tasks/03-ipfs.yml b/ansible/tasks/03-ipfs.yml new file mode 100644 index 0000000..67d7c61 --- /dev/null +++ b/ansible/tasks/03-ipfs.yml @@ -0,0 +1,36 @@ +- name: download IPFS CLI to localhost + become: false + run_once: true + delegate_to: localhost + block: + - name: create IPFS directory on localhost + file: + path: "{{ role_path }}/files/ipfs/{{ _ipfs_version }}/{{ _arch }}" + state: directory + + - name: download IPFS archive + get_url: + url: "{{ _ipfs_download_url }}" + dest: "{{ role_path }}/files/ipfs/{{ _ipfs_version }}/{{ _ipfs_archive }}" + checksum: "{{ _ipfs_checksums }}" + register: _download_ipfs + until: _download_ipfs is succeeded + retries: 5 + delay: 2 + + - name: extract IPFS binary + unarchive: + src: "{{ role_path }}/files/ipfs/{{ _ipfs_version }}/{{ _ipfs_archive }}" + dest: "{{ role_path }}/files/ipfs/{{ _ipfs_version }}/{{ _arch }}" + creates: "{{ role_path }}/files/ipfs/{{ _ipfs_version }}/{{ _arch }}/ipfs" + extra_opts: + - "--strip-components=1" + - "kubo/ipfs" + +- name: propagate IPFS binary + copy: + src: "{{ role_path }}/files/ipfs/{{ _ipfs_version }}/{{ _arch }}/ipfs" + dest: "{{ nox_dir }}/ipfs" + owner: "{{ nox_user }}" + group: "{{ nox_group }}" + mode: 0755 diff --git a/ansible/tasks/main.yml b/ansible/tasks/main.yml new file mode 100644 index 0000000..5c150d8 --- /dev/null +++ b/ansible/tasks/main.yml @@ -0,0 +1,12 @@ +- name: Preflight + tags: always + include_tasks: 00-preflight.yml + +- name: Cleanup Nox + include_tasks: 01-cleanup.yml + +- name: Install Nox + include_tasks: 02-install.yml + +- name: Download IPFS CLI + include_tasks: 03-ipfs.yml diff --git a/ansible/vars/main.yml b/ansible/vars/main.yml new file mode 100644 index 0000000..9628641 --- /dev/null +++ b/ansible/vars/main.yml @@ -0,0 +1,20 @@ +# common vars +_arch_map: + amd64: amd64 + x86_64: amd64 + aarch64: arm64 + 64-bit: amd64 +_arch: "{{ _arch_map[ansible_architecture] }}" +_semver_regex: "^v?\\d+\\.\\d+\\.\\d+$" + +# nox vars +_nox_version: "{{ nox_version | regex_replace('^(?!v)', 'v') }}" +_nox_download_url: "https://github.com/fluencelabs/nox/releases/download/nox-{{ _nox_version }}" +_nox_checksums: "sha256:{{ _nox_download_url + '/nox_SHA256_SUMS' }}" +_nox_bin: "nox-{{ _arch }}" + +# ipfs vars +_ipfs_version: "{{ nox_ipfs_version | regex_replace('^(?!v)', 'v') }}" +_ipfs_archive: "kubo_{{ _ipfs_version }}_linux-{{ _arch }}.tar.gz" +_ipfs_download_url: "https://dist.ipfs.io/kubo/{{ _ipfs_version }}/{{ _ipfs_archive }}" +_ipfs_checksums: "sha512:{{ _ipfs_download_url + '.sha512' }}"