From 08e654ccb835e79f4a8378f9a4daeb438e25b28d Mon Sep 17 00:00:00 2001 From: Robert Kaussow Date: Wed, 16 Oct 2024 22:27:11 +0200 Subject: [PATCH] refacor: migrate to systemd --- .drone.jsonnet | 159 ------------------ .drone.yml | 152 ----------------- .gitignore | 4 +- .later.yml | 19 --- .markdownlint.yml | 7 + .prettierignore | 1 + .woodpecker/docs.yaml | 47 ++++++ .woodpecker/lint.yaml | 30 ++++ .woodpecker/notify.yml | 26 +++ .woodpecker/test.yaml | 24 +++ .yamllint | 20 +++ README.md | 11 -- defaults/main.yml | 63 ++----- handlers/main.yml | 14 +- meta/main.yml | 9 +- molecule/centos7/converge.yml | 23 --- molecule/centos7/create.yml | 120 ------------- molecule/centos7/destroy.yml | 78 --------- molecule/centos7/molecule.yml | 24 --- molecule/centos7/prepare.yml | 15 -- molecule/centos7/tests/test_default.py | 15 -- molecule/default | 1 - molecule/default/converge.yml | 8 + molecule/default/molecule.yml | 17 ++ molecule/default/prepare.yml | 11 ++ molecule/pytest.ini | 3 - molecule/requirements.yml | 15 -- pyproject.toml | 17 ++ requirements.yml | 9 + setup.cfg | 12 -- tasks/main.yml | 58 ++++++- tasks/setup.yml | 54 ------ templates/etc/sysconfig/zigbee2mqtt.j2 | 3 + .../etc/systemd/system/zigbee2mqtt.service.j2 | 55 ++++++ templates/services/zigbee2mqtt_compose.yml.j2 | 90 ---------- .../{data => }/configuration.yaml.j2 | 2 + vars/main.yml | 12 -- 37 files changed, 359 insertions(+), 869 deletions(-) delete mode 100644 .drone.jsonnet delete mode 100644 .drone.yml delete mode 100644 .later.yml create mode 100644 .markdownlint.yml create mode 100644 .prettierignore create mode 100644 .woodpecker/docs.yaml create mode 100644 .woodpecker/lint.yaml create mode 100644 .woodpecker/notify.yml create mode 100644 .woodpecker/test.yaml create mode 100644 .yamllint delete mode 100644 molecule/centos7/converge.yml delete mode 100644 molecule/centos7/create.yml delete mode 100644 molecule/centos7/destroy.yml delete mode 100644 molecule/centos7/molecule.yml delete mode 100644 molecule/centos7/prepare.yml delete mode 100644 molecule/centos7/tests/test_default.py delete mode 120000 molecule/default create mode 100644 molecule/default/converge.yml create mode 100644 molecule/default/molecule.yml create mode 100644 molecule/default/prepare.yml delete mode 100644 molecule/pytest.ini delete mode 100644 molecule/requirements.yml create mode 100644 pyproject.toml create mode 100644 requirements.yml delete mode 100644 setup.cfg delete mode 100644 tasks/setup.yml create mode 100644 templates/etc/sysconfig/zigbee2mqtt.j2 create mode 100644 templates/etc/systemd/system/zigbee2mqtt.service.j2 delete mode 100644 templates/services/zigbee2mqtt_compose.yml.j2 rename templates/zigbee2mqtt/{data => }/configuration.yaml.j2 (98%) delete mode 100644 vars/main.yml diff --git a/.drone.jsonnet b/.drone.jsonnet deleted file mode 100644 index bf6e5ab..0000000 --- a/.drone.jsonnet +++ /dev/null @@ -1,159 +0,0 @@ -local PipelineLinting = { - kind: 'pipeline', - name: 'linting', - platform: { - os: 'linux', - arch: 'amd64', - }, - steps: [ - { - name: 'ansible-later', - image: 'thegeeklab/ansible-later', - commands: [ - 'ansible-later', - ], - }, - { - name: 'python-format', - image: 'python:3.11', - environment: { - PY_COLORS: 1, - }, - commands: [ - 'pip install -qq yapf', - '[ -z "$(find . -type f -name *.py)" ] || (yapf -rd ./)', - ], - }, - { - name: 'python-flake8', - image: 'python:3.11', - environment: { - PY_COLORS: 1, - }, - commands: [ - 'pip install -qq flake8', - 'flake8', - ], - }, - ], - trigger: { - ref: ['refs/heads/main', 'refs/tags/**', 'refs/pull/**'], - }, -}; - -local PipelineDeployment(scenario='centos7') = { - kind: 'pipeline', - name: 'testing-' + scenario, - platform: { - os: 'linux', - arch: 'amd64', - }, - concurrency: { - limit: 1, - }, - workspace: { - base: '/drone/src', - path: '${DRONE_REPO_NAME}', - }, - steps: [ - { - name: 'ansible-molecule', - image: 'thegeeklab/molecule:4', - environment: { - HCLOUD_TOKEN: { from_secret: 'hcloud_token' }, - }, - commands: [ - 'molecule test -s ' + scenario, - ], - }, - ], - depends_on: [ - 'linting', - ], - trigger: { - ref: ['refs/heads/main', 'refs/tags/**'], - }, -}; - -local PipelineDocumentation = { - kind: 'pipeline', - name: 'documentation', - platform: { - os: 'linux', - arch: 'amd64', - }, - steps: [ - { - name: 'generate', - image: 'thegeeklab/ansible-doctor', - environment: { - ANSIBLE_DOCTOR_LOG_LEVEL: 'INFO', - ANSIBLE_DOCTOR_FORCE_OVERWRITE: true, - ANSIBLE_DOCTOR_EXCLUDE_FILES: 'molecule/', - ANSIBLE_DOCTOR_TEMPLATE: 'hugo-book', - ANSIBLE_DOCTOR_ROLE_NAME: '${DRONE_REPO_NAME#*.}', - ANSIBLE_DOCTOR_OUTPUT_DIR: '_docs/', - }, - }, - { - name: 'publish', - image: 'plugins/gh-pages', - settings: { - remote_url: 'https://gitea.rknet.org/ansible/${DRONE_REPO_NAME}', - netrc_machine: 'gitea.rknet.org', - username: { from_secret: 'gitea_username' }, - password: { from_secret: 'gitea_token' }, - pages_directory: '_docs/', - target_branch: 'docs', - }, - when: { - ref: ['refs/heads/main'], - }, - }, - ], - trigger: { - ref: ['refs/heads/main', 'refs/tags/**', 'refs/pull/**'], - }, - depends_on: [ - 'testing-centos7', - ], -}; - -local PipelineNotification = { - kind: 'pipeline', - name: 'notification', - platform: { - os: 'linux', - arch: 'amd64', - }, - clone: { - disable: true, - }, - steps: [ - { - name: 'matrix', - image: 'thegeeklab/drone-matrix', - settings: { - homeserver: { from_secret: 'matrix_homeserver' }, - roomid: { from_secret: 'matrix_roomid' }, - template: 'Status: **{{ .Build.Status }}**
Build: [{{ .Repo.Owner }}/{{ .Repo.Name }}]({{ .Build.Link }}){{ if .Build.Branch }} ({{ .Build.Branch }}){{ end }} by {{ .Commit.Author }}
Message: {{ .Commit.Message.Title }}', - username: { from_secret: 'matrix_username' }, - password: { from_secret: 'matrix_password' }, - }, - }, - ], - depends_on: [ - 'documentation', - ], - trigger: { - status: ['success', 'failure'], - ref: ['refs/heads/main', 'refs/tags/**'], - }, -}; - -[ - PipelineLinting, - PipelineDeployment(scenario='centos7'), - PipelineDocumentation, - PipelineNotification, -] diff --git a/.drone.yml b/.drone.yml deleted file mode 100644 index 3c168db..0000000 --- a/.drone.yml +++ /dev/null @@ -1,152 +0,0 @@ ---- -kind: pipeline -name: linting - -platform: - os: linux - arch: amd64 - -steps: - - name: ansible-later - image: thegeeklab/ansible-later - commands: - - ansible-later - - - name: python-format - image: python:3.11 - commands: - - pip install -qq yapf - - "[ -z \"$(find . -type f -name *.py)\" ] || (yapf -rd ./)" - environment: - PY_COLORS: 1 - - - name: python-flake8 - image: python:3.11 - commands: - - pip install -qq flake8 - - flake8 - environment: - PY_COLORS: 1 - -trigger: - ref: - - refs/heads/main - - refs/tags/** - - refs/pull/** - ---- -kind: pipeline -name: testing-centos7 - -platform: - os: linux - arch: amd64 - -concurrency: - limit: 1 - -workspace: - base: /drone/src - path: ${DRONE_REPO_NAME} - -steps: - - name: ansible-molecule - image: thegeeklab/molecule:4 - commands: - - molecule test -s centos7 - environment: - HCLOUD_TOKEN: - from_secret: hcloud_token - -trigger: - ref: - - refs/heads/main - - refs/tags/** - -depends_on: - - linting - ---- -kind: pipeline -name: documentation - -platform: - os: linux - arch: amd64 - -steps: - - name: generate - image: thegeeklab/ansible-doctor - environment: - ANSIBLE_DOCTOR_EXCLUDE_FILES: molecule/ - ANSIBLE_DOCTOR_FORCE_OVERWRITE: true - ANSIBLE_DOCTOR_LOG_LEVEL: INFO - ANSIBLE_DOCTOR_OUTPUT_DIR: _docs/ - ANSIBLE_DOCTOR_ROLE_NAME: ${DRONE_REPO_NAME#*.} - ANSIBLE_DOCTOR_TEMPLATE: hugo-book - - - name: publish - image: plugins/gh-pages - settings: - netrc_machine: gitea.rknet.org - pages_directory: _docs/ - password: - from_secret: gitea_token - remote_url: https://gitea.rknet.org/ansible/${DRONE_REPO_NAME} - target_branch: docs - username: - from_secret: gitea_username - when: - ref: - - refs/heads/main - -trigger: - ref: - - refs/heads/main - - refs/tags/** - - refs/pull/** - -depends_on: - - testing-centos7 - ---- -kind: pipeline -name: notification - -platform: - os: linux - arch: amd64 - -clone: - disable: true - -steps: - - name: matrix - image: thegeeklab/drone-matrix - settings: - homeserver: - from_secret: matrix_homeserver - password: - from_secret: matrix_password - roomid: - from_secret: matrix_roomid - template: "Status: **{{ .Build.Status }}**
Build: [{{ .Repo.Owner }}/{{ .Repo.Name }}]({{ .Build.Link }}){{ if .Build.Branch }} ({{ .Build.Branch }}){{ end }} by {{ .Commit.Author }}
Message: {{ .Commit.Message.Title }}" - username: - from_secret: matrix_username - -trigger: - ref: - - refs/heads/main - - refs/tags/** - status: - - success - - failure - -depends_on: - - documentation - ---- -kind: signature -hmac: 8deae8ced5d7f9c3945e28632266d5573cf0ce26d1a21b1e494e34128c05e2a1 - -... diff --git a/.gitignore b/.gitignore index 9d13a2b..d97b7cd 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,6 @@ # ---> Ansible *.retry -filter/plugins/ +plugins library # ---> Python @@ -9,5 +9,3 @@ __pycache__/ *.py[cod] *$py.class -# ---> Docs -/_docs diff --git a/.later.yml b/.later.yml deleted file mode 100644 index 0efe5d5..0000000 --- a/.later.yml +++ /dev/null @@ -1,19 +0,0 @@ ---- -ansible: - custom_modules: - - iptables_raw - - openssl_pkcs12 - - proxmox_kvm - - ucr - - corenetworks_dns - - corenetworks_token - -rules: - exclude_files: - - molecule/ - - "LICENSE*" - - "**/*.md" - - "**/*.ini" - - exclude_filter: - - LINT0009 diff --git a/.markdownlint.yml b/.markdownlint.yml new file mode 100644 index 0000000..da116c7 --- /dev/null +++ b/.markdownlint.yml @@ -0,0 +1,7 @@ +--- +default: True +MD013: False +MD041: False +MD024: False +MD004: + style: dash diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..6b1d0bf --- /dev/null +++ b/.prettierignore @@ -0,0 +1 @@ +LICENSE diff --git a/.woodpecker/docs.yaml b/.woodpecker/docs.yaml new file mode 100644 index 0000000..857444b --- /dev/null +++ b/.woodpecker/docs.yaml @@ -0,0 +1,47 @@ +--- +when: + - event: [pull_request] + - event: [push, manual] + branch: + - ${CI_REPO_DEFAULT_BRANCH} + +steps: + - name: generate + image: quay.io/thegeeklab/ansible-doctor + environment: + ANSIBLE_DOCTOR_EXCLUDE_FILES: "['molecule/']" + ANSIBLE_DOCTOR_RENDERER__FORCE_OVERWRITE: "true" + ANSIBLE_DOCTOR_LOGGING__LEVEL: info + ANSIBLE_DOCTOR_ROLE__NAME: ${CI_REPO_NAME} + ANSIBLE_DOCTOR_TEMPLATE__NAME: readme + + - name: format + image: quay.io/thegeeklab/alpine-tools + commands: + - prettier -w README.md + + - name: diff + image: quay.io/thegeeklab/alpine-tools + commands: + - git diff --color=always README.md + + - name: publish + image: quay.io/thegeeklab/wp-git-action + settings: + action: + - commit + - push + author_email: ci-bot@rknet.org + author_name: ci-bot + branch: main + message: "[skip ci] automated docs update" + netrc_machine: gitea.rknet.org + netrc_password: + from_secret: gitea_token + when: + - event: [push, manual] + branch: + - ${CI_REPO_DEFAULT_BRANCH} + +depends_on: + - test diff --git a/.woodpecker/lint.yaml b/.woodpecker/lint.yaml new file mode 100644 index 0000000..c48a8e4 --- /dev/null +++ b/.woodpecker/lint.yaml @@ -0,0 +1,30 @@ +--- +when: + - event: [pull_request, tag] + - event: [push, manual] + branch: + - ${CI_REPO_DEFAULT_BRANCH} + +steps: + - name: ansible-lint + image: quay.io/thegeeklab/ansible-dev-tools:1 + commands: + - ansible-lint + environment: + FORCE_COLOR: "1" + + - name: python-format + image: docker.io/python:3.12 + commands: + - pip install -qq ruff + - ruff format --check --diff . + environment: + PY_COLORS: "1" + + - name: python-lint + image: docker.io/python:3.12 + commands: + - pip install -qq ruff + - ruff check . + environment: + PY_COLORS: "1" diff --git a/.woodpecker/notify.yml b/.woodpecker/notify.yml new file mode 100644 index 0000000..45bc21e --- /dev/null +++ b/.woodpecker/notify.yml @@ -0,0 +1,26 @@ +--- +when: + - event: [tag] + - event: [push, manual] + branch: + - ${CI_REPO_DEFAULT_BRANCH} + +runs_on: [success, failure] + +steps: + - name: matrix + image: quay.io/thegeeklab/wp-matrix + settings: + homeserver: + from_secret: matrix_homeserver + room_id: + from_secret: matrix_room_id + user_id: + from_secret: matrix_user_id + access_token: + from_secret: matrix_access_token + when: + - status: [failure] + +depends_on: + - docs diff --git a/.woodpecker/test.yaml b/.woodpecker/test.yaml new file mode 100644 index 0000000..661dc8b --- /dev/null +++ b/.woodpecker/test.yaml @@ -0,0 +1,24 @@ +--- +when: + - event: [pull_request, tag] + - event: [push, manual] + branch: + - ${CI_REPO_DEFAULT_BRANCH} + +variables: + - &molecule_base + image: quay.io/thegeeklab/ansible-dev-tools:1 + group: molecule + environment: + PY_COLORS: "1" + HCLOUD_TOKEN: + from_secret: molecule_hcloud_token + +steps: + - name: molecule-default + <<: *molecule_base + commands: + - molecule test -s default + +depends_on: + - lint diff --git a/.yamllint b/.yamllint new file mode 100644 index 0000000..df1d39e --- /dev/null +++ b/.yamllint @@ -0,0 +1,20 @@ +--- +extends: default + +rules: + truthy: + allowed-values: ["True", "False"] + comments: + min-spaces-from-content: 1 + comments-indentation: False + line-length: disable + braces: + min-spaces-inside: 0 + max-spaces-inside: 1 + brackets: + min-spaces-inside: 0 + max-spaces-inside: 0 + indentation: enable + octal-values: + forbid-implicit-octal: True + forbid-explicit-octal: True diff --git a/README.md b/README.md index fd358dc..cab5e35 100644 --- a/README.md +++ b/README.md @@ -1,12 +1 @@ # xoxys.zigbee2mqtt - -[![Build Status](https://img.shields.io/drone/build/ansible/xoxys.zigbee2mqtt?logo=drone&server=https%3A%2F%2Fdrone.rknet.org)](https://drone.rknet.org/ansible/xoxys.zigbee2mqtt) -[![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE) - -Setup [zigbee2mqtt](https://github.com/Koenkk/zigbee2mqtt). Zigbee2mqtt allows you to use your Zigbee devices without the vendors bridge or gateway. It bridges events and allows you to control your Zigbee devices via MQTT. - -You can find the full documentation at [https://galaxy.geekdocs.de](https://galaxy.geekdocs.de/roles/IoT/zigbee2mqtt/). - -## License - -This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details. diff --git a/defaults/main.yml b/defaults/main.yml index a6c7e24..e2753c2 100644 --- a/defaults/main.yml +++ b/defaults/main.yml @@ -1,44 +1,27 @@ --- -zigbee2mqtt_version: latest -zigbee2mqtt_image: "koenkk/zigbee2mqtt:{{ zigbee2mqtt_version }}" +zigbee2mqtt_image: "docker.io/koenkk/zigbee2mqtt:latest" -zigbee2mqtt_service_directory: /var/lib/docker/services/zigbee2mqtt -zigbee2mqtt_container_name: zigbee2mqtt -zigbee2mqtt_restart_policy: always -zigbee2mqtt_service_stopped: False -# @var zigbee2mqtt_networks:example: > -# zigbee2mqtt_networks: -# - name: default -# # optional network driver, defaults to 'bride' -# driver: host -# external: false -# @end -zigbee2mqtt_networks: - - name: default +zigbee2mqtt_service_started: True -zigbee2mqtt_networks_applied: - - default +zigbee2mqtt_data_volume: zigbee2mqtt-data # @var zigbee2mqtt_volumes:description: Define required docker volumes. -# @var zigbee2mqtt_volumes:example: > -# zigbee2mqtt_volumes: -# # Instead of the name you could specify a path on the container host system, -# # but you also have to enable bind mount for this volume -# - name: data -# # target location inside the container -# dest: /var/www/app/data -# # enable bind mount, if false volume will be configured as named volume -# # keep in mind you MUST set bind in any case -# bind: True -# # Options for bind mounts -# bind_opt: "ro,z" -# @end zigbee2mqtt_volumes: - name: "{{ zigbee2mqtt_data_volume }}" dest: /app/data - bind: True -zigbee2mqtt_data_volume: "/opt/zigbee2mqtt/data" +# @var zigbee2mqtt_network:description: Use a custom docker network for grafana. +# @var zigbee2mqtt_network:value: $ "_unset_" + +# @var zigbee2mqtt_exposed_ports:description: Ports you want to publish outside of docker. +zigbee2mqtt_exposed_ports: [] + +zigbee2mqtt_cap_add: [] +zigbee2mqtt_cap_drop: [] +zigbee2mqtt_security_opts: [] + +zigbee2mqtt_docker_args: + - --pids-limit=-1 # @var zigbee2mqtt_devices:description: Define required devices e.g. `/dev/ttyACM0`. # @var zigbee2mqtt_devices:example: > @@ -47,21 +30,7 @@ zigbee2mqtt_data_volume: "/opt/zigbee2mqtt/data" # dest: "{{ zigbee2mqtt_serial_port }}" # opt: z # @end - -zigbee2mqtt_exposed_ports: [] -zigbee2mqtt_extra_hosts: [] - -# @var zigbee2mqtt_memory_limit: $ "_unset_" -# @var zigbee2mqtt_memory_limit:example: $ "512m" -# @var zigbee2mqtt_memory_reservation: $ "_unset_" -# @var zigbee2mqtt_memory_reservation:example: $ "256m" -# @var zigbee2mqtt_cpu_shares: $ "_unset_" -# @var zigbee2mqtt_cpu_shares:example: $ "1024" - -zigbee2mqtt_cap_add: [] -zigbee2mqtt_cap_drop: [] -zigbee2mqtt_security_opt: [] -# @var zigbee2mqtt_pids_limit: $ "_unset_" +zigbee2mqtt_devices: [] zigbee2mqtt_log_level: info diff --git a/handlers/main.yml b/handlers/main.yml index 5e5c5ad..582e116 100644 --- a/handlers/main.yml +++ b/handlers/main.yml @@ -1,11 +1,7 @@ --- -- name: Restart zigbee2mqtt service - docker_compose: - project_src: "{{ zigbee2mqtt_service_directory }}" - pull: yes - remove_orphans: yes - stopped: "{{ zigbee2mqtt_service_stopped }}" - restarted: "{{ not zigbee2mqtt_service_stopped }}" +- name: Restart zigbee2mqtt + ansible.builtin.service: + name: "zigbee2mqtt.service" + state: "{{ zigbee2mqtt_service_started | ternary('restarted', 'stopped', 'restarted') }}" + daemon_reload: True listen: __zigbee2mqtt_restart - become: True - become_user: root diff --git a/meta/main.yml b/meta/main.yml index 8f363b1..10dc60d 100644 --- a/meta/main.yml +++ b/meta/main.yml @@ -4,7 +4,7 @@ galaxy_info: # @meta author:value: [Robert Kaussow](https://gitea.rknet.org/xoxys) author: Robert Kaussow namespace: xoxys - role_name: zigbee2mqtt_docker + role_name: zigbee2mqtt # @meta description: > # [![Source Code](https://img.shields.io/badge/gitea-source%20code-blue?logo=gitea&logoColor=white)](https://gitea.rknet.org/ansible/xoxys.zigbee2mqtt) # [![Build Status](https://img.shields.io/drone/build/ansible/xoxys.zigbee2mqtt?logo=drone&server=https%3A%2F%2Fdrone.rknet.org)](https://drone.rknet.org/ansible/xoxys.zigbee2mqtt) @@ -16,15 +16,12 @@ galaxy_info: # @end description: Setup zigbee2mqtt license: MIT - min_ansible_version: 2.10 + min_ansible_version: "2.10" platforms: - name: EL versions: - - 7 + - "9" galaxy_tags: - zigbee - zigbee2mqtt dependencies: [] -collections: - - xoxys.general - - community.general diff --git a/molecule/centos7/converge.yml b/molecule/centos7/converge.yml deleted file mode 100644 index b12556c..0000000 --- a/molecule/centos7/converge.yml +++ /dev/null @@ -1,23 +0,0 @@ ---- -- name: Converge (Stage 1) - hosts: all - vars: - dockerengine_packages_extra: - - epel-release - - python-pip - - python-virtualenv - - roles: - - role: xoxys.docker_engine - -- name: Converge (Stage 2) - hosts: all - environment: - PYTHONPATH: /opt/python2/ansible-deps/lib/python2.7/site-packages - vars: - mosquitto_bind_address: "127.0.0.1" - zigbee2mqtt_service_stopped: True - - roles: - - role: xoxys.mosquitto - - role: xoxys.zigbee2mqtt_docker diff --git a/molecule/centos7/create.yml b/molecule/centos7/create.yml deleted file mode 100644 index 8b945cd..0000000 --- a/molecule/centos7/create.yml +++ /dev/null @@ -1,120 +0,0 @@ ---- -- name: Create - hosts: localhost - connection: local - gather_facts: false - no_log: "{{ molecule_no_log }}" - vars: - ssh_port: 22 - ssh_user: root - ssh_path: "{{ lookup('env', 'MOLECULE_EPHEMERAL_DIRECTORY') }}/ssh_key" - tasks: - - name: Create SSH key - user: - name: "{{ lookup('env', 'USER') }}" - generate_ssh_key: true - ssh_key_file: "{{ ssh_path }}" - force: true - register: generated_ssh_key - - - name: Register the SSH key name - set_fact: - ssh_key_name: "molecule-generated-{{ 12345 | random | to_uuid }}" - - - name: Register SSH key for test instance(s) - hcloud_ssh_key: - name: "{{ ssh_key_name }}" - public_key: "{{ generated_ssh_key.ssh_public_key }}" - state: present - - - name: Create molecule instance(s) - hcloud_server: - name: "{{ item.name }}" - server_type: "{{ item.server_type }}" - ssh_keys: - - "{{ ssh_key_name }}" - image: "{{ item.image }}" - location: "{{ item.location | default(omit) }}" - datacenter: "{{ item.datacenter | default(omit) }}" - user_data: "{{ item.user_data | default(omit) }}" - api_token: "{{ lookup('env', 'HCLOUD_TOKEN') }}" - state: present - register: server - loop: "{{ molecule_yml.platforms }}" - async: 7200 - poll: 0 - - - name: Wait for instance(s) creation to complete - async_status: - jid: "{{ item.ansible_job_id }}" - register: hetzner_jobs - until: hetzner_jobs.finished - retries: 300 - loop: "{{ server.results }}" - - - name: Create volume(s) - hcloud_volume: - name: "{{ item.name }}" - server: "{{ item.name }}" - location: "{{ item.location | default(omit) }}" - size: "{{ item.volume_size | default(10) }}" - api_token: "{{ lookup('env', 'HCLOUD_TOKEN') }}" - state: "present" - loop: "{{ molecule_yml.platforms }}" - when: item.volume | default(False) | bool - register: volumes - async: 7200 - poll: 0 - - - name: Wait for volume(s) creation to complete - async_status: - jid: "{{ item.ansible_job_id }}" - register: hetzner_volumes - until: hetzner_volumes.finished - retries: 300 - when: volumes.changed - loop: "{{ volumes.results }}" - - # Mandatory configuration for Molecule to function. - - - name: Populate instance config dict - set_fact: - instance_conf_dict: - { - "instance": "{{ item.hcloud_server.name }}", - "ssh_key_name": "{{ ssh_key_name }}", - "address": "{{ item.hcloud_server.ipv4_address }}", - "user": "{{ ssh_user }}", - "port": "{{ ssh_port }}", - "identity_file": "{{ ssh_path }}", - "volume": "{{ item.item.item.volume | default(False) | bool }}", - } - loop: "{{ hetzner_jobs.results }}" - register: instance_config_dict - when: server.changed | bool - - - name: Convert instance config dict to a list - set_fact: - instance_conf: "{{ instance_config_dict.results | map(attribute='ansible_facts.instance_conf_dict') | list }}" - when: server.changed | bool - - - name: Dump instance config - copy: - content: | - # Molecule managed - - {{ instance_conf | to_nice_yaml(indent=2) }} - dest: "{{ molecule_instance_config }}" - when: server.changed | bool - - - name: Wait for SSH - wait_for: - port: "{{ ssh_port }}" - host: "{{ item.address }}" - search_regex: SSH - delay: 10 - loop: "{{ lookup('file', molecule_instance_config) | from_yaml }}" - - - name: Wait for VM to settle down - pause: - seconds: 30 diff --git a/molecule/centos7/destroy.yml b/molecule/centos7/destroy.yml deleted file mode 100644 index 6454c71..0000000 --- a/molecule/centos7/destroy.yml +++ /dev/null @@ -1,78 +0,0 @@ ---- -- name: Destroy - hosts: localhost - connection: local - gather_facts: false - no_log: "{{ molecule_no_log }}" - tasks: - - name: Check existing instance config file - stat: - path: "{{ molecule_instance_config }}" - register: cfg - - - name: Populate the instance config - set_fact: - instance_conf: "{{ (lookup('file', molecule_instance_config) | from_yaml) if cfg.stat.exists else [] }}" - - - name: Destroy molecule instance(s) - hcloud_server: - name: "{{ item.instance }}" - api_token: "{{ lookup('env', 'HCLOUD_TOKEN') }}" - state: absent - register: server - loop: "{{ instance_conf }}" - async: 7200 - poll: 0 - - - name: Wait for instance(s) deletion to complete - async_status: - jid: "{{ item.ansible_job_id }}" - register: hetzner_jobs - until: hetzner_jobs.finished - retries: 300 - loop: "{{ server.results }}" - - - pause: - seconds: 5 - - - name: Destroy volume(s) - hcloud_volume: - name: "{{ item.instance }}" - server: "{{ item.instance }}" - api_token: "{{ lookup('env', 'HCLOUD_TOKEN') }}" - state: "absent" - register: volumes - loop: "{{ instance_conf }}" - when: item.volume | default(False) | bool - async: 7200 - poll: 0 - - - name: Wait for volume(s) deletion to complete - async_status: - jid: "{{ item.ansible_job_id }}" - register: hetzner_volumes - until: hetzner_volumes.finished - retries: 300 - when: volumes.changed - loop: "{{ volumes.results }}" - - - name: Remove registered SSH key - hcloud_ssh_key: - name: "{{ instance_conf[0].ssh_key_name }}" - state: absent - when: (instance_conf | default([])) | length > 0 - - # Mandatory configuration for Molecule to function. - - - name: Populate instance config - set_fact: - instance_conf: {} - - - name: Dump instance config - copy: - content: | - # Molecule managed - - {{ instance_conf | to_nice_yaml(indent=2) }} - dest: "{{ molecule_instance_config }}" - when: server.changed | bool diff --git a/molecule/centos7/molecule.yml b/molecule/centos7/molecule.yml deleted file mode 100644 index 4ba9390..0000000 --- a/molecule/centos7/molecule.yml +++ /dev/null @@ -1,24 +0,0 @@ ---- -dependency: - name: galaxy - options: - role-file: molecule/requirements.yml - requirements-file: molecule/requirements.yml - env: - ANSIBLE_GALAXY_DISPLAY_PROGRESS: "false" -driver: - name: delegated -platforms: - - name: centos7-zigbee2mqtt - image: centos-7 - server_type: cx11 -lint: | - /usr/local/bin/flake8 -provisioner: - name: ansible - env: - ANSIBLE_FILTER_PLUGINS: ${ANSIBLE_FILTER_PLUGINS:-./plugins/filter} - ANSIBLE_LIBRARY: ${ANSIBLE_LIBRARY:-./library} - log: False -verifier: - name: testinfra diff --git a/molecule/centos7/prepare.yml b/molecule/centos7/prepare.yml deleted file mode 100644 index 183f4d3..0000000 --- a/molecule/centos7/prepare.yml +++ /dev/null @@ -1,15 +0,0 @@ ---- -- name: Prepare - hosts: all - gather_facts: false - tasks: - - name: Bootstrap python for Ansible - raw: | - command -v python3 python || ( - (test -e /usr/bin/dnf && sudo dnf install -y python3) || - (test -e /usr/bin/apt && (apt -y update && apt install -y python-minimal)) || - (test -e /usr/bin/yum && sudo yum -y -qq install python3) || - echo "Warning: Python not boostrapped due to unknown platform." - ) - become: true - changed_when: false diff --git a/molecule/centos7/tests/test_default.py b/molecule/centos7/tests/test_default.py deleted file mode 100644 index 00c8dca..0000000 --- a/molecule/centos7/tests/test_default.py +++ /dev/null @@ -1,15 +0,0 @@ -import os - -import testinfra.utils.ansible_runner - -testinfra_hosts = testinfra.utils.ansible_runner.AnsibleRunner( - os.environ["MOLECULE_INVENTORY_FILE"] -).get_hosts("all") - - -# TODO: add some tests if zigbee2mqtt can work with a -# dummy interface -def test_hosts_file(host): - f = host.file('/etc/hosts') - - assert f.exists diff --git a/molecule/default b/molecule/default deleted file mode 120000 index 2fdf3e8..0000000 --- a/molecule/default +++ /dev/null @@ -1 +0,0 @@ -centos7 \ No newline at end of file diff --git a/molecule/default/converge.yml b/molecule/default/converge.yml new file mode 100644 index 0000000..40c619f --- /dev/null +++ b/molecule/default/converge.yml @@ -0,0 +1,8 @@ +--- +- name: Converge + hosts: all + vars: + zigbee2mqtt_service_started: False + roles: + - role: xoxys.dockerengine + - role: xoxys.zigbee2mqtt diff --git a/molecule/default/molecule.yml b/molecule/default/molecule.yml new file mode 100644 index 0000000..83b7369 --- /dev/null +++ b/molecule/default/molecule.yml @@ -0,0 +1,17 @@ +--- +driver: + name: molecule_hetznercloud +dependency: + name: galaxy + options: + role-file: requirements.yml + requirements-file: requirements.yml +platforms: + - name: "rocky9-zigbee2mqtt" + server_type: "cx22" + image: "rocky-9" +provisioner: + name: ansible + log: True +verifier: + name: testinfra diff --git a/molecule/default/prepare.yml b/molecule/default/prepare.yml new file mode 100644 index 0000000..0df1d77 --- /dev/null +++ b/molecule/default/prepare.yml @@ -0,0 +1,11 @@ +--- +- name: Prepare + hosts: all + gather_facts: False + tasks: + - name: Bootstrap Python for Ansible + ansible.builtin.raw: | + command -v python3 python || + ((test -e /usr/bin/apt && (apt -y update && apt install -y python-minimal)) || + echo "Warning: Python not boostrapped due to unknown platform.") + changed_when: False diff --git a/molecule/pytest.ini b/molecule/pytest.ini deleted file mode 100644 index c24fe5b..0000000 --- a/molecule/pytest.ini +++ /dev/null @@ -1,3 +0,0 @@ -[pytest] -filterwarnings = - ignore::DeprecationWarning diff --git a/molecule/requirements.yml b/molecule/requirements.yml deleted file mode 100644 index 35dde5b..0000000 --- a/molecule/requirements.yml +++ /dev/null @@ -1,15 +0,0 @@ ---- -collections: - - name: https://gitea.rknet.org/ansible/xoxys.general/releases/download/v2.1.1/xoxys-general-2.1.1.tar.gz - - name: community.general - -roles: - - src: https://gitea.rknet.org/ansible/xoxys.docker_engine.git - name: xoxys.docker_engine - scm: git - version: main - - - src: https://gitea.rknet.org/ansible/xoxys.mosquitto.git - name: xoxys.mosquitto - scm: git - version: main diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..7193140 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,17 @@ +[tool.ruff] +exclude = [".git", "__pycache__"] + +line-length = 99 +indent-width = 4 + +[tool.ruff.lint] +ignore = ["W191", "E111", "E114", "E117", "S101", "S105"] +select = ["F", "E", "I", "W", "S"] + +[tool.ruff.format] +quote-style = "double" +indent-style = "space" +line-ending = "lf" + +[tool.pytest.ini_options] +filterwarnings = ["ignore::FutureWarning", "ignore::DeprecationWarning"] diff --git a/requirements.yml b/requirements.yml new file mode 100644 index 0000000..5300ab9 --- /dev/null +++ b/requirements.yml @@ -0,0 +1,9 @@ +--- +collections: + - name: community.docker + +roles: + - src: https://gitea.rknet.org/ansible/xoxys.dockerengine.git + name: xoxys.dockerengine + scm: git + version: main diff --git a/setup.cfg b/setup.cfg deleted file mode 100644 index 2bb8674..0000000 --- a/setup.cfg +++ /dev/null @@ -1,12 +0,0 @@ -[flake8] -ignore = D100, D101, D102, D103, D105, D107, E402, W503 -max-line-length = 99 -inline-quotes = double -exclude = .git,.tox,__pycache__,build,dist,tests,*.pyc,*.egg-info,.cache,.eggs,env* - -[yapf] -based_on_style = google -column_limit = 99 -dedent_closing_brackets = true -coalesce_brackets = true -split_before_logical_operator = true diff --git a/tasks/main.yml b/tasks/main.yml index 1f69f7a..fedbac6 100644 --- a/tasks/main.yml +++ b/tasks/main.yml @@ -1,2 +1,58 @@ --- -- include_tasks: setup.yml +- name: Create container volumes + community.docker.docker_volume: + name: "{{ item.name }}" + driver_options: "{{ item.options | default(omit) }}" + state: "{{ item.state | default('present') }}" + loop: "{{ zigbee2mqtt_volumes }}" + loop_control: + label: "{{ item.name }}" + when: item.type | default("volume") | lower == "volume" + register: __zigbee2mqtt_volumes_raw + +- name: Register container volumes map + ansible.builtin.set_fact: + __zigbee2mqtt_volumes_map: "{{ __zigbee2mqtt_volumes_raw.results | json_query('[].volume') | items2dict(key_name='Name', value_name='Mountpoint') }}" + +- name: Deploy env file + ansible.builtin.template: + src: etc/sysconfig/zigbee2mqtt.j2 + dest: "/etc/sysconfig/zigbee2mqtt" + owner: root + group: root + mode: "0600" + notify: __zigbee2mqtt_restart + +- name: Create container specs + ansible.builtin.template: + src: etc/systemd/system/zigbee2mqtt.service.j2 + dest: "/etc/systemd/system/zigbee2mqtt.service" + owner: root + group: root + mode: "0640" + notify: __zigbee2mqtt_restart + +- name: Deploy configuration file + template: + src: "zigbee2mqtt/configuration.yaml.j2" + dest: "{{ __zigbee2mqtt_volumes_map[zigbee2mqtt_data_volume] }}/configuration.yaml" + mode: 0600 + notify: __zigbee2mqtt_restart + +- name: Ensure device files exists + file: + path: "{{ item }}" + mode: 0640 + state: touch + access_time: preserve + modification_time: preserve + loop: + - "{{ __zigbee2mqtt_volumes_map[zigbee2mqtt_data_volume] }}/devices.yaml" + - "{{ __zigbee2mqtt_volumes_map[zigbee2mqtt_data_volume] }}/groups.yaml" + +- name: Ensure service state + ansible.builtin.service: + name: "zigbee2mqtt.service" + state: "{{ zigbee2mqtt_service_started | ternary('started', 'stopped', 'started') }}" + daemon_reload: True + enabled: True diff --git a/tasks/setup.yml b/tasks/setup.yml deleted file mode 100644 index 805ce75..0000000 --- a/tasks/setup.yml +++ /dev/null @@ -1,54 +0,0 @@ ---- -- block: - - name: Ensure service directory exists - file: - path: "{{ zigbee2mqtt_service_directory }}" - mode: 0750 - state: directory - - - name: Create bind mount source directory - file: - path: "{{ item.name }}" - mode: 0750 - state: directory - loop: "{{ zigbee2mqtt_volumes }}" - loop_control: - label: "{{ item.name }}" - when: item.bind | bool - - - name: Deploy compose file to '{{ zigbee2mqtt_service_directory }}' - template: - src: "services/zigbee2mqtt_compose.yml.j2" - dest: "{{ zigbee2mqtt_service_directory }}/docker-compose.yml" - owner: root - group: root - mode: 0640 - validate: "docker-compose -f %s config -q" - - - name: Deploy configuration file - template: - src: "zigbee2mqtt/data/configuration.yaml.j2" - dest: "{{ zigbee2mqtt_data_volume }}/configuration.yaml" - mode: 0600 - notify: __zigbee2mqtt_restart - - - name: Ensure device files exists - file: - path: "{{ item }}" - mode: 0640 - state: touch - access_time: preserve - modification_time: preserve - loop: - - "{{ zigbee2mqtt_data_volume }}/devices.yaml" - - "{{ zigbee2mqtt_data_volume }}/groups.yaml" - - - name: Ensure service is up and running - docker_compose: - project_src: "{{ zigbee2mqtt_service_directory }}" - pull: yes - remove_orphans: yes - stopped: "{{ zigbee2mqtt_service_stopped }}" - state: present - become: True - become_user: root diff --git a/templates/etc/sysconfig/zigbee2mqtt.j2 b/templates/etc/sysconfig/zigbee2mqtt.j2 new file mode 100644 index 0000000..b715059 --- /dev/null +++ b/templates/etc/sysconfig/zigbee2mqtt.j2 @@ -0,0 +1,3 @@ +#jinja2: lstrip_blocks: True +{{ ansible_managed | comment }} +TZ={{ zigbee2mqtt_timezone }} diff --git a/templates/etc/systemd/system/zigbee2mqtt.service.j2 b/templates/etc/systemd/system/zigbee2mqtt.service.j2 new file mode 100644 index 0000000..be85beb --- /dev/null +++ b/templates/etc/systemd/system/zigbee2mqtt.service.j2 @@ -0,0 +1,55 @@ +#jinja2: lstrip_blocks: True +{{ ansible_managed | comment }} +[Unit] +Description=Zigbee2mqtt + +Wants=docker.service +After=docker.service + +[Service] +Restart=on-failure +RestartSec=5s +EnvironmentFile=/etc/environment + +ExecStop=/usr/bin/docker pull {{ zigbee2mqtt_image }} +ExecStop=/bin/sh -c '/usr/bin/docker ps | /bin/grep %p 1> /dev/null && /usr/bin/docker stop %p || true' + +ExecStartPre=/bin/sh -c '/usr/bin/docker ps | /bin/grep %p 1> /dev/null && /usr/bin/docker kill %p || true' +ExecStartPre=/bin/sh -c '/usr/bin/docker ps -a | /bin/grep %p 1> /dev/null && /usr/bin/docker rm %p || true' +ExecStartPre=/usr/bin/docker pull {{ zigbee2mqtt_image }} + +ExecStart=/usr/bin/docker run --rm \ + --name %p \ + --hostname %p \ + --env-file /etc/sysconfig/zigbee2mqtt \ + {% if zigbee2mqtt_network is defined and zigbee2mqtt_network %} + --network {{ zigbee2mqtt_network }} \ + {% endif %} + {% for cap in zigbee2mqtt_cap_add %} + --cap-add {{ cap }} \ + {% endfor %} + {% for cap in zigbee2mqtt_cap_drop %} + --cap-drop {{ cap }} \ + {% endfor %} + {% for opt in zigbee2mqtt_security_opts %} + --security-opt "{{ opt }}" \ + {% endfor %} + {% for volume in zigbee2mqtt_volumes %} + --mount '{{ "type=bind," if (volume.type | default(False) | lower == "bind") else "" }}src={{ volume.name }},target={{ volume.dest }}' \ + {% endfor %} + {% for device in zigbee2mqtt_devices %} + --device "{{ device.src }}:{{ device.dest }}{% if device.opt is defined %}:{{ device.opt }}{% endif %}" \ + {% endfor %} + {% for port in zigbee2mqtt_exposed_ports %} + --publish {{ port }} \ + {% endfor %} + {% for item in inst.docker_args | default(zigbee2mqtt_docker_args) %} + {{ item }} \ + {% endfor %} + --health-interval 5s \ + --health-retries 5 \ + --health-timeout 10s \ + {{ zigbee2mqtt_image }} + +[Install] +WantedBy=multi-user.target diff --git a/templates/services/zigbee2mqtt_compose.yml.j2 b/templates/services/zigbee2mqtt_compose.yml.j2 deleted file mode 100644 index 55b50dc..0000000 --- a/templates/services/zigbee2mqtt_compose.yml.j2 +++ /dev/null @@ -1,90 +0,0 @@ -#jinja2:lstrip_blocks: True -{{ ansible_managed | comment }} -version: "2.4" - -services: - zigbee2mqtt: - container_name: {{ zigbee2mqtt_container_name }} - image: {{ zigbee2mqtt_image }} - restart: {{ zigbee2mqtt_restart_policy }} - {% if zigbee2mqtt_exposed_ports | default([]) %} - ports: - {% for port in zigbee2mqtt_exposed_ports %} - - {{ port | quote }} - {% endfor %} - {% endif %} - {% if zigbee2mqtt_volumes | default([]) %} - volumes: - {% for volume in zigbee2mqtt_volumes %} - - "{{ volume.name }}:{{ volume.dest }}{% if volume.bind_opt is defined %}:{{ volume.bind_opt }}{% endif %}" - {% endfor %} - {% endif %} - {% if zigbee2mqtt_devices | default([]) %} - devices: - {% for device in zigbee2mqtt_devices %} - - "{{ device.src }}:{{ device.dest }}{% if device.opt is defined %}:{{ device.opt }}{% endif %}" - {% endfor %} - {% endif %} - {% if zigbee2mqtt_networks_applied | default([]) %} - networks: - {% for network in zigbee2mqtt_networks_applied %} - - {{ network }} - {% endfor %} - {% endif %} - {% if zigbee2mqtt_extra_hosts | default([]) %} - extra_hosts: - {% for host in zigbee2mqtt_extra_hosts %} - - {{ host | quote }} - {% endfor %} - {% endif %} - environment: - - TZ={{ zigbee2mqtt_timezone }} - {% if zigbee2mqtt_memory_limit is defined %} - mem_limit: {{ zigbee2mqtt_memory_limit }} - {% endif %} - {% if zigbee2mqtt_memory_reservation is defined %} - mem_reservation: {{ zigbee2mqtt_memory_reservation }} - {% endif %} - {% if zigbee2mqtt_cpu_shares is defined %} - cpu_shares: {{ zigbee2mqtt_cpu_shares }} - {% endif %} - {% if not zigbee2mqtt_cap_add | length == 0 %} - cap_add: - {% for item in zigbee2mqtt_cap_add %} - - {{ item }} - {% endfor %} - {% endif %} - {% if not zigbee2mqtt_cap_drop | length == 0 %} - cap_drop: - {% for item in zigbee2mqtt_cap_drop %} - - {{ item }} - {% endfor %} - {% endif %} - {% if not zigbee2mqtt_security_opt | length == 0 %} - security_opt: - {% for item in zigbee2mqtt_security_opt %} - - {{ item }} - {% endfor %} - {% endif %} - {% if zigbee2mqtt_pids_limit is defined %} - pids_limit: {{ zigbee2mqtt_pids_limit }} - {% endif %} -{% if zigbee2mqtt_volumes | default([]) | rejectattr("bind") | list | length > 0 %} - -volumes: - {% for volume in zigbee2mqtt_volumes | rejectattr("bind") %} - {{ volume.name }}: - {% endfor %} -{% endif %} -{% if zigbee2mqtt_networks | default([]) | length > 0 %} - -networks: - {% for network in zigbee2mqtt_networks %} - {{ network.name }}: - {% if network.external | default(False) | bool %} - external: true - {% else %} - driver: {{ network.backend | default("bridge") }} - {% endif %} - {% endfor %} -{% endif %} diff --git a/templates/zigbee2mqtt/data/configuration.yaml.j2 b/templates/zigbee2mqtt/configuration.yaml.j2 similarity index 98% rename from templates/zigbee2mqtt/data/configuration.yaml.j2 rename to templates/zigbee2mqtt/configuration.yaml.j2 index 216c3e4..744c8c4 100644 --- a/templates/zigbee2mqtt/data/configuration.yaml.j2 +++ b/templates/zigbee2mqtt/configuration.yaml.j2 @@ -53,6 +53,8 @@ advanced: channel: {{ zigbee2mqtt_channel }} cache_state: {{ zigbee2mqtt_cache_state }} log_level: "{{ zigbee2mqtt_log_level }}" + log_output: + - console last_seen: "{{ zigbee2mqtt_last_seen }}" elapsed: {{ zigbee2mqtt_elapsed }} diff --git a/vars/main.yml b/vars/main.yml deleted file mode 100644 index cb7b30b..0000000 --- a/vars/main.yml +++ /dev/null @@ -1,12 +0,0 @@ ---- -__zigbee2mqtt_npm_executable: /usr/local/bin/npm -__zigbee2mqtt_release_exclude: - - data/ - - docker/ - - docs/ - - images/ - - LICENSE - - README.md - - scripts/ - - test/ - - update.sh