diff --git a/.github/actions/setup-molecule/action.yml b/.github/actions/setup-molecule/action.yml new file mode 100644 index 000000000..4c12633d7 --- /dev/null +++ b/.github/actions/setup-molecule/action.yml @@ -0,0 +1,35 @@ +--- +# Copyright 2022 Samsung Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +name: Ansible Molecule test execution +inputs: + ansible-role: + description: 'Ansible Role' + required: true + +runs: + using: "composite" + steps: + - uses: actions/setup-python@v3 + with: + python-version: '3.x' + - name: install dependencies + shell: bash + run: pip install -r deployment/test-requirements.txt + - name: Run molecule tests + shell: bash + run: | + cd deployment/ansible/roles/${{ inputs.ansible-role }}/ + molecule --debug test diff --git a/.github/workflows/on-demand_molecule.yml b/.github/workflows/on-demand_molecule.yml index 9952e0830..9f1acde81 100644 --- a/.github/workflows/on-demand_molecule.yml +++ b/.github/workflows/on-demand_molecule.yml @@ -30,21 +30,72 @@ on: - deployment/ansible/roles/** jobs: - check-molecule: - name: Check Ansible Molecule role tests - strategy: - fail-fast: false - matrix: - role: [bootstrap, genesis] + changes: + runs-on: ubuntu-latest + outputs: + bootstrap: ${{ steps.filter.outputs.bootstrap }} + genesis: ${{ steps.filter.outputs.genesis }} + add-accounts: ${{ steps.filter.outputs.add-accounts }} + configure: ${{ steps.filter.outputs.configure }} + steps: + - uses: actions/checkout@v3 + - uses: dorny/paths-filter@v2 + if: ${{ !env.ACT }} + id: filter + with: + token: ${{ secrets.GITHUB_TOKEN }} + filters: | + reqs: &reqs + - 'deployment/test-requirements.*' + bootstrap: + - *reqs + - 'deployment/ansible/roles/bootstrap/**' + genesis: + - *reqs + - 'deployment/ansible/roles/genesis/**' + add-accounts: + - *reqs + - 'deployment/ansible/roles/add-accounts/**' + configure: + - *reqs + - 'deployment/ansible/roles/configure/**' + check-bootstrap: + needs: changes + if: needs.changes.outputs.bootstrap == 'true' + name: Check Bootstrap Ansible role + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: ./.github/actions/setup-molecule + with: + ansible-role: bootstrap + check-genesis: + needs: changes + if: needs.changes.outputs.genesis == 'true' + name: Check Genesis Ansible role + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: ./.github/actions/setup-molecule + with: + ansible-role: genesis + check-add-accounts: + needs: changes + if: needs.changes.outputs.add-accounts == 'true' + name: Check Add accounts Ansible role + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: ./.github/actions/setup-molecule + with: + ansible-role: add-accounts + check-configure: + needs: changes + if: needs.changes.outputs.configure == 'true' + name: Check Configure Ansible role runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - - uses: actions/setup-python@v3 + - uses: ./.github/actions/setup-molecule with: - python-version: '3.x' - - name: install dependencies - run: pip install -r deployment/test-requirements.txt - - name: Run molecule tests - run: | - cd deployment/ansible/roles/${{ matrix.role }}/ - molecule --debug test + ansible-role: configure diff --git a/deployment/ansible/roles/add-accounts/README.md b/deployment/ansible/roles/add-accounts/README.md new file mode 100644 index 000000000..89ca23699 --- /dev/null +++ b/deployment/ansible/roles/add-accounts/README.md @@ -0,0 +1,74 @@ +# Add accounts + +This role creates keys defined on `accounts` list variable and set up the +command-line interface. + +## Requirements + +None + +## Role Variables + +```yaml +accounts: + - name: user1 + passphrase: password123 + roles: + - NodeAdmin + - Trustee +``` + +A list of DCL accounts to be created on a specific target node. + +## Dependencies + +None + +## Example Playbook + +example inventory.yaml + +```yaml +all: + vars: + chain_id: dev-net + hosts: + node0: + accounts: + - name: jack + passphrase: test1234 + roles: + - NodeAdmin + - Trustee + node1: + accounts: + - name: alice + passphrase: s3cr3t123 + roles: + - NodeAdmin + - Trustee + node2: + accounts: + - name: bob + passphrase: admin1234 + roles: + - NodeAdmin + - Trustee + node3: + accounts: + - name: anna + passphrase: test1234 + roles: + - NodeAdmin +``` + +in your playbook: + +```yaml +- name: bootstrap DCL nodes + hosts: all + become: true + roles: + - bootstrap + - add-accounts +``` diff --git a/deployment/ansible/roles/add-accounts/defaults/main.yml b/deployment/ansible/roles/add-accounts/defaults/main.yml new file mode 100644 index 000000000..d326205c8 --- /dev/null +++ b/deployment/ansible/roles/add-accounts/defaults/main.yml @@ -0,0 +1,21 @@ +--- +# Copyright 2022 Samsung Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# NOTE: Every host must define their own DCL accounts +accounts: [] + +dcl_home: /var/lib/dcl/.dcl +dcld: + path: "{{ dcl_home }}/cosmovisor/genesis/bin/dcld" diff --git a/deployment/ansible/roles/add-accounts/molecule/default/converge.yml b/deployment/ansible/roles/add-accounts/molecule/default/converge.yml new file mode 100644 index 000000000..1c50dde2b --- /dev/null +++ b/deployment/ansible/roles/add-accounts/molecule/default/converge.yml @@ -0,0 +1,19 @@ +--- +# Copyright 2022 Samsung Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +- name: converge + hosts: all + roles: + - add-accounts diff --git a/deployment/ansible/roles/add-accounts/molecule/default/molecule.yml b/deployment/ansible/roles/add-accounts/molecule/default/molecule.yml new file mode 100644 index 000000000..946a98338 --- /dev/null +++ b/deployment/ansible/roles/add-accounts/molecule/default/molecule.yml @@ -0,0 +1,49 @@ +--- +# Copyright 2022 Samsung Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +driver: + name: docker +lint: | + set -e + yamllint -c ../../../../.yaml-lint.yml . +platforms: + - name: node0 + image: geerlingguy/docker-ubuntu2004-ansible:latest + pre_build_image: true + command: /sbin/init + tmpfs: + - /run + - /tmp + - /run/lock + volumes: + - /sys/fs/cgroup:/sys/fs/cgroup:ro +provisioner: + name: ansible + inventory: + group_vars: + all: + chain_id: test-net + host_vars: + node0: + accounts: + - name: jack + passphrase: test1234 + roles: + - NodeAdmin + - Trustee +verifier: + name: testinfra + lint: + name: flake8 diff --git a/deployment/ansible/roles/add-accounts/molecule/default/prepare.yml b/deployment/ansible/roles/add-accounts/molecule/default/prepare.yml new file mode 100644 index 000000000..3f01bcb01 --- /dev/null +++ b/deployment/ansible/roles/add-accounts/molecule/default/prepare.yml @@ -0,0 +1,19 @@ +--- +# Copyright 2022 Samsung Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +- name: prepare + hosts: all + roles: + - bootstrap diff --git a/deployment/ansible/roles/add-accounts/molecule/default/tests/test_default.py b/deployment/ansible/roles/add-accounts/molecule/default/tests/test_default.py new file mode 100644 index 000000000..83409a6fd --- /dev/null +++ b/deployment/ansible/roles/add-accounts/molecule/default/tests/test_default.py @@ -0,0 +1,44 @@ +# Copyright 2022 Samsung Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import json +import os + +import testinfra.utils.ansible_runner + +testinfra_hosts = testinfra.utils.ansible_runner.AnsibleRunner( + os.environ["MOLECULE_INVENTORY_FILE"] +).get_hosts("all") +DCLD_HOME = "/var/lib/dcl/.dcl/" + + +def test_accounts_creation(host): + all_variables = host.ansible.get_variables() + assert "accounts" in all_variables + for account in all_variables["accounts"]: + assert "passphrase" in account + assert "name" in account + cmd = host.run( + f"echo {account['passphrase']}" + f" | /var/lib/dcl/.dcl/cosmovisor/genesis/bin/dcld keys show {account['name']}" + f" --home {DCLD_HOME} --output json" + ) + assert cmd.succeeded + assert len(cmd.stdout) > 0 + key_name = json.loads(cmd.stdout) + for key in ["name", "type", "address", "pubkey"]: + assert key in key_name + assert key_name["name"] == account["name"] + assert key_name["type"] == "local" + assert host.file(f"{DCLD_HOME}{account['name']}.info").exists diff --git a/deployment/ansible/roles/add-accounts/tasks/main.yml b/deployment/ansible/roles/add-accounts/tasks/main.yml new file mode 100644 index 000000000..1c75dad01 --- /dev/null +++ b/deployment/ansible/roles/add-accounts/tasks/main.yml @@ -0,0 +1,30 @@ +--- +# Copyright 2022 Samsung Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +- name: create directory for key name facts + file: + state: directory + recurse: true + path: /etc/ansible/facts.d + +- name: prepare keys + include_tasks: prepare-keys.yml + vars: + key_name: "{{ account.name }}" + passphrase: "{{ account.passphrase }}" + loop: "{{ accounts }}" + loop_control: + loop_var: account + no_log: true diff --git a/deployment/ansible/roles/bootstrap/tasks/prepare-keys.yml b/deployment/ansible/roles/add-accounts/tasks/prepare-keys.yml similarity index 94% rename from deployment/ansible/roles/bootstrap/tasks/prepare-keys.yml rename to deployment/ansible/roles/add-accounts/tasks/prepare-keys.yml index 24ace786d..37b4f4307 100644 --- a/deployment/ansible/roles/bootstrap/tasks/prepare-keys.yml +++ b/deployment/ansible/roles/add-accounts/tasks/prepare-keys.yml @@ -39,5 +39,5 @@ - name: prepare-keys | persist key name local fact changed_when: false - shell: "echo {{ passphrase }} | {{ dcld.path }} keys show {{ key_name }} --home {{ dcl_home }} | tee /etc/ansible/facts.d/{{ key_name}}.fact" + shell: "echo {{ passphrase }} | {{ dcld.path }} keys show {{ key_name }} --home {{ dcl_home }} --output json | tee /etc/ansible/facts.d/{{ key_name}}.fact" no_log: true diff --git a/deployment/ansible/roles/bootstrap/README.md b/deployment/ansible/roles/bootstrap/README.md index b876167c1..72f94bc16 100644 --- a/deployment/ansible/roles/bootstrap/README.md +++ b/deployment/ansible/roles/bootstrap/README.md @@ -1,8 +1,6 @@ # Bootstrap -An Ansible role that fetches DCL binary and configures the service. This role -creates keys defined on `accounts` list variable and set up the command-line -interface. +An Ansible role that fetches DCL binary and configures the service ## Requirements @@ -11,38 +9,20 @@ None ## Role Variables ```yaml -chain_id: test-net -``` - -The unique chain ID to identify the network. - -```yaml -accounts: - - name: user1 - passphrase: password123 - roles: - - NodeAdmin - - Trustee -``` - -A list of DCL accounts to be created on a specific target node. - -```yaml -dcld: - version: 0.7.0 +dcl_version: 0.9.0 ``` The DCL binary version to be used for the deployment. ```yaml -dcld: - user: dcl +dcl_home: /var/lib/dcl/.dcl +cosmovisor: + user: cosmovisor group: dcl - home: /var/lib/dcl ``` -The *user* and *group* to be used by OS to run the DCL service. The *home* var -specifies the path to store DCL config information. +The *user* and *group* to be used by OS to run the cosmovisor service. The +*dcl_home* var specifies the path to store DCL config information. ## Dependencies @@ -55,35 +35,7 @@ example inventory.yaml ```yaml all: vars: - chain_id: dev-net - hosts: - node0: - accounts: - - name: jack - passphrase: test1234 - roles: - - NodeAdmin - - Trustee - node1: - accounts: - - name: alice - passphrase: s3cr3t123 - roles: - - NodeAdmin - - Trustee - node2: - accounts: - - name: bob - passphrase: admin1234 - roles: - - NodeAdmin - - Trustee - node3: - accounts: - - name: anna - passphrase: test1234 - roles: - - NodeAdmin + dcl_version: 0.9.0 ``` in your playbook: diff --git a/deployment/ansible/roles/bootstrap/defaults/main.yml b/deployment/ansible/roles/bootstrap/defaults/main.yml index af5164ee9..812e1ddce 100644 --- a/deployment/ansible/roles/bootstrap/defaults/main.yml +++ b/deployment/ansible/roles/bootstrap/defaults/main.yml @@ -13,17 +13,6 @@ # See the License for the specific language governing permissions and # limitations under the License. -chain_id: dclchain -moniker: -# NOTE: Every host must define their own DCL accounts -accounts: [] -config_values: - - chain-id "{{ chain_id }}" - - output json - - node "tcp://localhost:26657" - - keyring-backend test - - broadcast-mode block - dcl_home: /var/lib/dcl/.dcl dcl_version: 0.9.0 dcld: diff --git a/deployment/ansible/roles/bootstrap/molecule/default/molecule.yml b/deployment/ansible/roles/bootstrap/molecule/default/molecule.yml index 946a98338..f5c94151c 100644 --- a/deployment/ansible/roles/bootstrap/molecule/default/molecule.yml +++ b/deployment/ansible/roles/bootstrap/molecule/default/molecule.yml @@ -35,14 +35,6 @@ provisioner: group_vars: all: chain_id: test-net - host_vars: - node0: - accounts: - - name: jack - passphrase: test1234 - roles: - - NodeAdmin - - Trustee verifier: name: testinfra lint: diff --git a/deployment/ansible/roles/bootstrap/molecule/default/tests/test_default.py b/deployment/ansible/roles/bootstrap/molecule/default/tests/test_default.py index 2424e90af..2f581140a 100644 --- a/deployment/ansible/roles/bootstrap/molecule/default/tests/test_default.py +++ b/deployment/ansible/roles/bootstrap/molecule/default/tests/test_default.py @@ -24,27 +24,7 @@ def test_binary_version(host): - assert host.run_test( - "/var/lib/dcl/.dcl/cosmovisor/genesis/bin/dcld version" - ).succeeded - - -def test_configuration(host): - all_variables = host.ansible.get_variables() - config = host.file(DCLD_HOME + "/config/") - assert config.exists - assert config.is_directory - assert config.user == "cosmovisor" - - config_files = host.file(DCLD_HOME + "/config").listdir() - for filename in ["app", "client", "config"]: - assert (filename + ".toml") in config_files - - assert "chain_id" in all_variables - assert ( - all_variables["chain_id"] - in host.file(DCLD_HOME + "/config/client.toml").content_string - ) + assert host.run_test(DCLD_HOME + "cosmovisor/genesis/bin/dcld version").succeeded def test_service(host): @@ -57,24 +37,3 @@ def test_service(host): "ExecStart=/usr/bin/cosmovisor start", ]: assert prop in svc.content_string - - -def test_accounts_creation(host): - all_variables = host.ansible.get_variables() - assert "accounts" in all_variables - for account in all_variables["accounts"]: - assert "passphrase" in account - assert "name" in account - cmd = host.run( - f"echo {account['passphrase']}" - f" | /var/lib/dcl/.dcl/cosmovisor/genesis/bin/dcld keys show {account['name']}" - f" --home {DCLD_HOME}" - ) - assert cmd.succeeded - assert len(cmd.stdout) > 0 - key_name = json.loads(cmd.stdout) - for key in ["name", "type", "address", "pubkey"]: - assert key in key_name - assert key_name["name"] == account["name"] - assert key_name["type"] == "local" - assert host.file(f"{DCLD_HOME}/keyring-test/{account['name']}.info").exists diff --git a/deployment/ansible/roles/bootstrap/tasks/main.yml b/deployment/ansible/roles/bootstrap/tasks/main.yml index d5b4b3625..2a910c356 100644 --- a/deployment/ansible/roles/bootstrap/tasks/main.yml +++ b/deployment/ansible/roles/bootstrap/tasks/main.yml @@ -33,37 +33,6 @@ - name: setup DCL service import_tasks: setup-service.yml -- name: read genesis file - stat: - path: "{{ dcl_home }}/config/genesis.json" - register: genesis_file - -- name: initialize the layout - changed_when: false - command: "{{ dcld.path }} init '{{ moniker | default(ansible_hostname) }}' --chain-id '{{ chain_id }}' --home {{ dcl_home }}" - when: not genesis_file.stat.exists - -- name: configure CLI - changed_when: false - command: "{{ dcld.path }} config {{ item }} --home {{ dcl_home }}" - loop: "{{ config_values }}" - -- name: create directory for key name facts - file: - state: directory - recurse: true - path: /etc/ansible/facts.d - -- name: prepare keys - include_tasks: prepare-keys.yml - vars: - key_name: "{{ account.name }}" - passphrase: "{{ account.passphrase }}" - loop: "{{ accounts }}" - loop_control: - loop_var: account - no_log: true - - name: ensure user permissions file: recurse: true diff --git a/deployment/ansible/roles/configure/README.md b/deployment/ansible/roles/configure/README.md new file mode 100644 index 000000000..303481e50 --- /dev/null +++ b/deployment/ansible/roles/configure/README.md @@ -0,0 +1,50 @@ +# Configure + +An Ansible role that configures DCL client, app and config files + +## Requirements + +None + +## Role Variables + +```yaml +chain_id: test-net +``` + +The unique chain ID to identify the network. + +## Dependencies + +None + +## Example Playbook + +example inventory.yaml + +```yaml +all: + vars: + chain_id: dev-net +``` + +in your playbook: + +```yaml +- name: setup validators + hosts: validators + roles: + - bootstrap + - role: configure + config: + p2p: + pex: false + persistent_peers: + addr_book_strict: false + statesync: + rpc_servers: + app: + state-sync: + snapshot-interval: snapshot-interval + snapshot-keep-recent: snapshot-keep-recent +``` diff --git a/deployment/ansible/roles/configure/defaults/main.yml b/deployment/ansible/roles/configure/defaults/main.yml new file mode 100644 index 000000000..56d2d7cde --- /dev/null +++ b/deployment/ansible/roles/configure/defaults/main.yml @@ -0,0 +1,33 @@ +--- +# Copyright 2022 Samsung Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +dcl_home: /var/lib/dcl/.dcl +dcld: + path: "{{ dcl_home }}/cosmovisor/genesis/bin/dcld" +chain_id: dclchain +moniker: +client: + # The network chain ID + chain-id: "{{ chain_id }}" + # The keyring's backend, where the keys are stored (os|file|kwallet|pass|test|memory) + keyring-backend: test + # CLI output format (text|json) + output: json + # : to Tendermint RPC interface for this chain + node: "tcp://localhost:26657" + # Transaction broadcasting mode (sync|async|block) + broadcast-mode: block +config: {} +app: {} diff --git a/deployment/ansible/roles/configure/molecule/default/converge.yml b/deployment/ansible/roles/configure/molecule/default/converge.yml new file mode 100644 index 000000000..7d6a54b72 --- /dev/null +++ b/deployment/ansible/roles/configure/molecule/default/converge.yml @@ -0,0 +1,44 @@ +--- +# Copyright 2022 Samsung Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +- name: setup validators + hosts: validators + roles: + - role: configure + type: validator + +- name: setup private sentries + hosts: private-sentries + roles: + - role: configure + type: private-sentry + +- name: setup public sentries + hosts: public-sentries + roles: + - role: configure + type: public-sentry + +- name: setup observers + hosts: observers + roles: + - role: configure + type: observer + +- name: setup seeds + hosts: seeds + roles: + - role: configure + type: seed diff --git a/deployment/ansible/roles/configure/molecule/default/molecule.yml b/deployment/ansible/roles/configure/molecule/default/molecule.yml new file mode 100644 index 000000000..9d176f9ed --- /dev/null +++ b/deployment/ansible/roles/configure/molecule/default/molecule.yml @@ -0,0 +1,95 @@ +--- +# Copyright 2022 Samsung Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +dependency: + name: galaxy + options: + requirements-file: ../../../galaxy-requirements.yml +driver: + name: docker +lint: | + set -e + yamllint -c ../../../../.yaml-lint.yml . +platforms: + - name: node0 + image: geerlingguy/docker-ubuntu2004-ansible:latest + pre_build_image: true + command: /sbin/init + tmpfs: + - /run + - /tmp + - /run/lock + volumes: + - /sys/fs/cgroup:/sys/fs/cgroup:ro + groups: + - validators + - name: node1 + image: geerlingguy/docker-ubuntu2004-ansible:latest + pre_build_image: true + command: /sbin/init + tmpfs: + - /run + - /tmp + - /run/lock + volumes: + - /sys/fs/cgroup:/sys/fs/cgroup:ro + groups: + - private-sentries + - name: node2 + image: geerlingguy/docker-ubuntu2004-ansible:latest + pre_build_image: true + command: /sbin/init + tmpfs: + - /run + - /tmp + - /run/lock + volumes: + - /sys/fs/cgroup:/sys/fs/cgroup:ro + groups: + - observers + - name: node3 + image: geerlingguy/docker-ubuntu2004-ansible:latest + pre_build_image: true + command: /sbin/init + tmpfs: + - /run + - /tmp + - /run/lock + volumes: + - /sys/fs/cgroup:/sys/fs/cgroup:ro + groups: + - public-sentries + - name: node4 + image: geerlingguy/docker-ubuntu2004-ansible:latest + pre_build_image: true + command: /sbin/init + tmpfs: + - /run + - /tmp + - /run/lock + volumes: + - /sys/fs/cgroup:/sys/fs/cgroup:ro + groups: + - seeds +provisioner: + name: ansible + inventory: + group_vars: + all: + chain_id: test-net +verifier: + name: testinfra + lint: + name: flake8 diff --git a/deployment/ansible/roles/configure/molecule/default/prepare.yml b/deployment/ansible/roles/configure/molecule/default/prepare.yml new file mode 100644 index 000000000..3f01bcb01 --- /dev/null +++ b/deployment/ansible/roles/configure/molecule/default/prepare.yml @@ -0,0 +1,19 @@ +--- +# Copyright 2022 Samsung Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +- name: prepare + hosts: all + roles: + - bootstrap diff --git a/deployment/ansible/roles/configure/molecule/default/tests/test_default.py b/deployment/ansible/roles/configure/molecule/default/tests/test_default.py new file mode 100644 index 000000000..a6b43e502 --- /dev/null +++ b/deployment/ansible/roles/configure/molecule/default/tests/test_default.py @@ -0,0 +1,40 @@ +# Copyright 2022 Samsung Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import json +import os + +import testinfra.utils.ansible_runner + +testinfra_hosts = testinfra.utils.ansible_runner.AnsibleRunner( + os.environ["MOLECULE_INVENTORY_FILE"] +).get_hosts("all") +DCLD_HOME = "/var/lib/dcl/.dcl/" + + +def test_configuration(host): + all_variables = host.ansible.get_variables() + config = host.file(DCLD_HOME + "/config/") + assert config.exists + assert config.is_directory + + config_files = host.file(DCLD_HOME + "/config").listdir() + for filename in ["app", "client", "config"]: + assert (filename + ".toml") in config_files + + assert "chain_id" in all_variables + assert ( + all_variables["chain_id"] + in host.file(DCLD_HOME + "/config/client.toml").content_string + ) diff --git a/deployment/ansible/roles/configure/tasks/main.yml b/deployment/ansible/roles/configure/tasks/main.yml new file mode 100644 index 000000000..892ad0234 --- /dev/null +++ b/deployment/ansible/roles/configure/tasks/main.yml @@ -0,0 +1,41 @@ +--- +# Copyright 2022 Samsung Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +- name: read genesis file + stat: + path: "{{ dcl_home }}/config/genesis.json" + register: genesis_file + +- name: initialize the layout + changed_when: false + command: "{{ dcld.path }} init '{{ moniker | default(ansible_hostname) }}' --chain-id '{{ chain_id }}' --home {{ dcl_home }}" + when: not genesis_file.stat.exists + +- name: configure CLI + changed_when: false + command: "{{ dcld.path }} config {{ item.key }} {{ item.value }} --home {{ dcl_home }}" + with_dict: "{{ client }}" + +- name: setup node type specific values + include_tasks: "{{ type }}.yml" + when: type is defined + +- name: setup TOMLs files + include_tasks: setup.yml + loop_control: + loop_var: toml + loop: + - config + - app diff --git a/deployment/ansible/roles/configure/tasks/observer.yml b/deployment/ansible/roles/configure/tasks/observer.yml new file mode 100644 index 000000000..bad85fd0d --- /dev/null +++ b/deployment/ansible/roles/configure/tasks/observer.yml @@ -0,0 +1,28 @@ +--- +# Copyright 2022 Samsung Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +- name: observer | discover IPv4 addresses information + setup: + filter: + - 'ansible_default_ipv4' + delegate_to: "{{ item }}" + register: default_ipv4_facts + loop: "{{ groups['private-sentries'] | list }}" + +- name: observer | set dynamic values + set_fact: + config: + p2p: + persistent_peers: "{{ default_ipv4_facts.results | map(attribute='ansible_facts.ansible_default_ipv4.address') | join(',') }}" diff --git a/deployment/ansible/roles/configure/tasks/private-sentry.yml b/deployment/ansible/roles/configure/tasks/private-sentry.yml new file mode 100644 index 000000000..2b994b170 --- /dev/null +++ b/deployment/ansible/roles/configure/tasks/private-sentry.yml @@ -0,0 +1,28 @@ +--- +# Copyright 2022 Samsung Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +- name: private-sentry | discover IPv4 addresses information + setup: + filter: + - 'ansible_default_ipv4' + delegate_to: "{{ item }}" + register: default_ipv4_facts + loop: "{{ groups['validators'] | list }}" + +- name: private-sentry | set dynamic values + set_fact: + config: + p2p: + persistent_peers: "{{ default_ipv4_facts.results | map(attribute='ansible_facts.ansible_default_ipv4.address') | join(',') }}" diff --git a/deployment/ansible/roles/configure/tasks/public-sentry.yml b/deployment/ansible/roles/configure/tasks/public-sentry.yml new file mode 100644 index 000000000..697e35abe --- /dev/null +++ b/deployment/ansible/roles/configure/tasks/public-sentry.yml @@ -0,0 +1,28 @@ +--- +# Copyright 2022 Samsung Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +- name: public-sentry | discover IPv4 addresses information + setup: + filter: + - 'ansible_default_ipv4' + delegate_to: "{{ item }}" + register: default_ipv4_facts + loop: "{{ groups['private-sentries'] | list }}" + +- name: public-sentry | set dynamic values + set_fact: + config: + p2p: + persistent_peers: "{{ default_ipv4_facts.results | map(attribute='ansible_facts.ansible_default_ipv4.address') | join(',') }}" diff --git a/deployment/ansible/roles/configure/tasks/seed.yml b/deployment/ansible/roles/configure/tasks/seed.yml new file mode 100644 index 000000000..5f1e747b2 --- /dev/null +++ b/deployment/ansible/roles/configure/tasks/seed.yml @@ -0,0 +1,28 @@ +--- +# Copyright 2022 Samsung Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +- name: seed | discover IPv4 addresses information + setup: + filter: + - 'ansible_default_ipv4' + delegate_to: "{{ item }}" + register: default_ipv4_facts + loop: "{{ groups['public-sentries'] | list }}" + +- name: seed | set dynamic values + set_fact: + config: + p2p: + persistent_peers: "{{ default_ipv4_facts.results | map(attribute='ansible_facts.ansible_default_ipv4.address') | join(',') }}" diff --git a/deployment/ansible/roles/configure/tasks/setup.yml b/deployment/ansible/roles/configure/tasks/setup.yml new file mode 100644 index 000000000..a4b3e1d05 --- /dev/null +++ b/deployment/ansible/roles/configure/tasks/setup.yml @@ -0,0 +1,45 @@ +--- +# Copyright 2022 Samsung Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +- name: setup | node type variables + include_vars: + file: "{{ type }}.yml" + hash_behaviour: merge + when: type is defined + +- name: setup | combine user with existing config values + set_fact: + filename: "{{ dcl_home }}/config/{{ toml }}.toml" + +- name: setup | read config file + changed_when: false + command: "cat {{ filename }}" + register: filename_content + +- name: setup | combine user with existing config values + set_fact: + existing_values: "{{ filename_content.stdout | default({}) | sivel.toiletwater.from_toml }}" + +- name: setup | viewing config content + debug: + var: "{{ item }}" + loop: + - "{{ existing_values }}" + - "{{ vars[toml] }}" + +- name: setup | create TOMLs files + copy: + dest: "{{ filename }}" + content: "{{ existing_values | combine(vars[toml]) | sivel.toiletwater.to_toml }}" diff --git a/deployment/ansible/roles/configure/tasks/validator.yml b/deployment/ansible/roles/configure/tasks/validator.yml new file mode 100644 index 000000000..25e90199b --- /dev/null +++ b/deployment/ansible/roles/configure/tasks/validator.yml @@ -0,0 +1,28 @@ +--- +# Copyright 2022 Samsung Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +- name: validator | discover IPv4 addresses information + setup: + filter: + - 'ansible_default_ipv4' + delegate_to: "{{ item }}" + register: default_ipv4_facts + loop: "{{ groups['public-sentries'] | list }}" + +- name: validator | set dynamic values + set_fact: + config: + p2p: + persistent_peers: "{{ default_ipv4_facts.results | map(attribute='ansible_facts.ansible_default_ipv4.address') | join(',') }}" diff --git a/deployment/ansible/roles/configure/vars/observer.yml b/deployment/ansible/roles/configure/vars/observer.yml new file mode 100644 index 000000000..0887b1a40 --- /dev/null +++ b/deployment/ansible/roles/configure/vars/observer.yml @@ -0,0 +1,28 @@ +--- +# Copyright 2022 Samsung Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +config: + p2p: + pex: true + persistent_peers: + addr_book_strict: false + statesync: + enable: true + rpc_servers: + # trust_height: trust-height + trust_hash: trust-hash +app: + api: + enable: true diff --git a/deployment/ansible/roles/configure/vars/private-sentry.yml b/deployment/ansible/roles/configure/vars/private-sentry.yml new file mode 100644 index 000000000..d1436fd93 --- /dev/null +++ b/deployment/ansible/roles/configure/vars/private-sentry.yml @@ -0,0 +1,31 @@ +--- +# Copyright 2022 Samsung Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +config: + p2p: + pex: true + persistent_peers: + private_peer_ids: + unconditional_peers: + addr_book_strict: false + statesync: + enable: true + rpc_servers: + # trust_height: trust-height + trust_hash: trust-hash +app: + state-sync: + snapshot-interval: snapshot-interval + snapshot-keep-recent: snapshot-keep-recent diff --git a/deployment/ansible/roles/configure/vars/public-sentry.yml b/deployment/ansible/roles/configure/vars/public-sentry.yml new file mode 100644 index 000000000..3a8acbc7f --- /dev/null +++ b/deployment/ansible/roles/configure/vars/public-sentry.yml @@ -0,0 +1,28 @@ +--- +# Copyright 2022 Samsung Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +config: + p2p: + pex: true + persistent_peers: + statesync: + enable: true + rpc_servers: + # trust_height: trust-height + trust_hash: trust-hash +app: + state-sync: + snapshot-interval: snapshot-interval + snapshot-keep-recent: snapshot-keep-recent diff --git a/deployment/ansible/roles/configure/vars/seed.yml b/deployment/ansible/roles/configure/vars/seed.yml new file mode 100644 index 000000000..99f304047 --- /dev/null +++ b/deployment/ansible/roles/configure/vars/seed.yml @@ -0,0 +1,25 @@ +--- +# Copyright 2022 Samsung Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +config: + p2p: + pex: true + seed_mode: true + persistent_peers: + statesync: + enable: true + rpc_servers: + # trust_height: trust-height + trust_hash: trust-hash diff --git a/deployment/ansible/roles/configure/vars/validator.yml b/deployment/ansible/roles/configure/vars/validator.yml new file mode 100644 index 000000000..bfbfb1bd7 --- /dev/null +++ b/deployment/ansible/roles/configure/vars/validator.yml @@ -0,0 +1,29 @@ +--- +# Copyright 2022 Samsung Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +config: + p2p: + pex: false + persistent_peers: + addr_book_strict: false + statesync: + enable: true + rpc_servers: + # trust_height: trust-height + trust_hash: trust-hash +app: + state-sync: + snapshot-interval: snapshot-interval + snapshot-keep-recent: snapshot-keep-recent diff --git a/deployment/ansible/roles/genesis/molecule/default/molecule.yml b/deployment/ansible/roles/genesis/molecule/default/molecule.yml index 079eef84b..9e8d49818 100644 --- a/deployment/ansible/roles/genesis/molecule/default/molecule.yml +++ b/deployment/ansible/roles/genesis/molecule/default/molecule.yml @@ -13,6 +13,10 @@ # See the License for the specific language governing permissions and # limitations under the License. +dependency: + name: galaxy + options: + requirements-file: ../../../galaxy-requirements.yml driver: name: docker lint: | diff --git a/deployment/ansible/roles/genesis/molecule/default/prepare.yml b/deployment/ansible/roles/genesis/molecule/default/prepare.yml index 3f01bcb01..c34779122 100644 --- a/deployment/ansible/roles/genesis/molecule/default/prepare.yml +++ b/deployment/ansible/roles/genesis/molecule/default/prepare.yml @@ -17,3 +17,5 @@ hosts: all roles: - bootstrap + - configure + - add-accounts diff --git a/deployment/ansible/roles/genesis/tasks/prepare-genesis.yml b/deployment/ansible/roles/genesis/tasks/prepare-genesis.yml index 2bc5339de..3ab031c71 100644 --- a/deployment/ansible/roles/genesis/tasks/prepare-genesis.yml +++ b/deployment/ansible/roles/genesis/tasks/prepare-genesis.yml @@ -51,7 +51,7 @@ set_fact: admin: "{{ item }}" when: "'NodeAdmin' in item.roles" - loop: "{{ accounts }}" + loop: "{{ accounts | default([]) }}" no_log: true - name: prepare-genesis | create genesis transactions @@ -60,6 +60,12 @@ no_log: true ignore_errors: true +- name: prepare-genesis | ensure gentx folder + file: + recurse: true + state: directory + path: "{{ dcl_home }}/config/gentx/" + - name: prepare-genesis | collect gentx files changed_when: false command: "find {{ dcl_home }}/config/gentx/ -maxdepth 1 -type f -name '*.json'" @@ -75,6 +81,7 @@ - name: prepare-genesis | collect genesis transactions changed_when: false command: "{{ dcld.path }} collect-gentxs --home {{ dcl_home }}" + when: gentx_files.stdout_lines | length > 0 - name: prepare-genesis | validate genesis file changed_when: false diff --git a/deployment/galaxy-requirements.yml b/deployment/galaxy-requirements.yml new file mode 100644 index 000000000..b07a116fd --- /dev/null +++ b/deployment/galaxy-requirements.yml @@ -0,0 +1,18 @@ +--- +# Copyright 2022 Samsung Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +collections: + # For to_toml filter + - name: sivel.toiletwater diff --git a/deployment/test-requirements.in b/deployment/test-requirements.in index c509fdb6d..2c4f7cc99 100644 --- a/deployment/test-requirements.in +++ b/deployment/test-requirements.in @@ -19,3 +19,4 @@ molecule-docker # MIT pytest-testinfra # Apache-2.0 yamllint # GPLv3 jmespath # MIT +toml # MIT diff --git a/deployment/test-requirements.txt b/deployment/test-requirements.txt index 63a810f80..0988d03ea 100644 --- a/deployment/test-requirements.txt +++ b/deployment/test-requirements.txt @@ -4,14 +4,14 @@ # # pip-compile --output-file=test-requirements.txt test-requirements.in # -ansible==5.5.0 +ansible==5.6.0 # via -r test-requirements.in ansible-compat==2.0.2 # via # ansible-lint # molecule # molecule-docker -ansible-core==2.12.3 +ansible-core==2.12.4 # via # ansible # ansible-lint @@ -40,7 +40,7 @@ chardet==4.0.0 # via binaryornot charset-normalizer==2.0.12 # via requests -click==8.0.4 +click==8.1.2 # via # click-help-colors # cookiecutter @@ -67,7 +67,7 @@ idna==3.3 # via requests iniconfig==1.1.1 # via pytest -jinja2==3.0.3 +jinja2==3.1.1 # via # ansible-core # cookiecutter @@ -109,11 +109,11 @@ pygments==2.11.2 # via rich pynacl==1.5.0 # via paramiko -pyparsing==3.0.7 +pyparsing==3.0.8 # via packaging pytest==7.1.1 # via pytest-testinfra -pytest-testinfra==6.6.0 +pytest-testinfra==6.7.0 # via -r test-requirements.in python-dateutil==2.8.2 # via arrow @@ -133,7 +133,7 @@ requests==2.27.1 # molecule-docker resolvelib==0.5.4 # via ansible-core -rich==12.0.1 +rich==12.2.0 # via # ansible-lint # enrich @@ -154,13 +154,17 @@ subprocess-tee==0.3.5 # via ansible-compat text-unidecode==1.3 # via python-slugify +toml==0.10.2 + # via -r test-requirements.in tomli==2.0.1 # via pytest +typing-extensions==4.1.1 + # via rich urllib3==1.26.9 # via requests wcmatch==8.3 # via ansible-lint -websocket-client==1.3.1 +websocket-client==1.3.2 # via docker yamllint==1.26.3 # via