diff --git a/LICENSE b/LICENSE index 44e4d67..8e54586 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2020 Robert Kaussow +Copyright (c) 2021 Robert Kaussow Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/defaults/main.yml b/defaults/main.yml index 7552d44..199ff34 100644 --- a/defaults/main.yml +++ b/defaults/main.yml @@ -1,42 +1,78 @@ --- -homeassistant_version: 0.92.1 +homeassistant_version: latest +homeassistant_image: "homeassistant/home-assistant:{{ homeassistant_version }}" -homeassistant_user: homeassistant -homeassistant_user_home: "/home/{{ homeassistant_user }}" -homeassistant_group: "{{ homeassistant_user }}" -homeassistant_extra_groups: - - tty - - dialout +homeassistant_service_directory: /var/lib/docker/services/homeassistant +homeassistant_container_name: homeassistant +homeassistant_restart_policy: always +homeassistant_service_stopped: False +# @var homeassistant_networks:example: > +# homeassistant_networks: +# - name: default +# # optional network driver, defaults to 'bride' +# driver: host +# external: false +# @end +homeassistant_networks: + - name: default -homeassistant_systemd_after: - - network-online.target +homeassistant_networks_applied: + - default -homeassistant_base_dir: /opt/homeassistant -homeassistant_conf_dir: "{{ homeassistant_base_dir }}/config" +# @var homeassistant_volumes:description: Define required docker volumes. +# @var homeassistant_volumes:example: > +# homeassistant_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 +homeassistant_volumes: + - name: /opt/homeassistant/config + dest: /config + bind: True -homeassistant_packages: [] +# @var homeassistant_devices:description: Define required devices e.g. `/dev/ttyACM0`. +# @var homeassistant_devices:example: > +# homeassistant_devices: +# - src: "{{ homeassistant_serial_port }}" +# dest: "{{ homeassistant_serial_port }}" +# opt: z +# @end -homeassistant_http_bind_port: 8123 -homeassistant_client_url: https://hassio.example.com +homeassistant_exposed_ports: [] +homeassistant_extra_hosts: [] -homeassistant_tls_enabled: False -homeassistant_tls_dhparam_path: "{{ homeassistant_base_dir }}/tls/dhparam.pem" -homeassistant_tls_dhparam_size: 2048 -homeassistant_tls_cert_path: "{{ homeassistant_base_dir }}/tls/certs/mycert.pem" -homeassistant_tls_key_path: "{{ homeassistant_base_dir }}/tls/private/mykey.pem" -homeassistant_tls_cert_source: mycert.pem -homeassistant_tls_key_source: mykey.pem +# @var homeassistant_memory_limit: $ "_unset_" +# @var homeassistant_memory_limit:example: $ "512m" +# @var homeassistant_memory_reservation: $ "_unset_" +# @var homeassistant_memory_reservation:example: $ "256m" +# @var homeassistant_cpu_shares: $ "_unset_" +# @var homeassistant_cpu_shares:example: $ "1024" + +homeassistant_cap_add: [] +homeassistant_cap_drop: [] +homeassistant_security_opt: [] +# @var homeassistant_pids_limit: $ "_unset_" + +homeassistant_timezone: Europe/Berlin # @var homeassistant_cmdline_override_enabled:description: > -# Override `/boot/cmdline.txt` with given conten. This can be necessary -# if you use searial hardware, but be careful! Wrong configuration +# Override `/boot/cmdline.txt` with given conten. This can be necessary +# if you use searial hardware, but be careful! Wrong configuration # may leave your system unusable! # @end homeassistant_cmdline_override_enabled: False homeassistant_cmdline_content: "console=tty1 root=/dev/mmcblk0p3 rootfstype=ext4 elevator=deadline rootwait" # @var homeassistant_exclude_modemmanager:description: > -# Prevent ModemManger from binding to serial devices and therefor +# Prevent ModemManger from binding to serial devices and therefor # blocking controllers like a `CC2531`. Devices can be skipped by a udev rule. # @end homeassistant_exclude_modemmanager: False diff --git a/meta/main.yml b/meta/main.yml index b77cfeb..8ed59a3 100644 --- a/meta/main.yml +++ b/meta/main.yml @@ -1,4 +1,4 @@ -# Standards: 0.1 +# Standards: 0.2 --- galaxy_info: # @meta author:value: [Robert Kaussow](https://gitea.rknet.org/xoxys) diff --git a/molecule/centos7/converge.yml b/molecule/centos7/converge.yml index 61256c9..660d991 100644 --- a/molecule/centos7/converge.yml +++ b/molecule/centos7/converge.yml @@ -1,5 +1,22 @@ -- name: Converge +--- +- name: Converge (Stage 1) hosts: all + vars: + dockerengine_packages_extra: + - epel-release + - python-pip + - python-virtualenv + roles: - - role: xoxys.python3 - - role: xoxys.homeassistant + - role: xoxys.docker_engine + +- name: Converge (Stage 2) + hosts: all + environment: + PYTHONPATH: /opt/python2/ansible-deps/lib/python2.7/site-packages + vars: + homeassistant_exposed_ports: + - "127.0.0.1:8123:8123" + + roles: + - role: xoxys.homeassistant_docker diff --git a/molecule/centos7/create.yml b/molecule/centos7/create.yml index 54acd05..8b945cd 100644 --- a/molecule/centos7/create.yml +++ b/molecule/centos7/create.yml @@ -103,7 +103,7 @@ content: | # Molecule managed - {{ instance_conf | to_json | from_json | to_yaml }} + {{ instance_conf | to_nice_yaml(indent=2) }} dest: "{{ molecule_instance_config }}" when: server.changed | bool diff --git a/molecule/centos7/destroy.yml b/molecule/centos7/destroy.yml index 7a1ffbb..6454c71 100644 --- a/molecule/centos7/destroy.yml +++ b/molecule/centos7/destroy.yml @@ -73,6 +73,6 @@ content: | # Molecule managed - {{ instance_conf | to_json | from_json | to_yaml }} + {{ 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 index 1ea04d5..7de7024 100644 --- a/molecule/centos7/molecule.yml +++ b/molecule/centos7/molecule.yml @@ -19,5 +19,6 @@ provisioner: env: ANSIBLE_FILTER_PLUGINS: ${ANSIBLE_FILTER_PLUGINS:-./plugins/filter} ANSIBLE_LIBRARY: ${ANSIBLE_LIBRARY:-./library} + log: False verifier: name: testinfra diff --git a/molecule/centos7/tests/test_default.py b/molecule/centos7/tests/test_default.py index 40300ab..752f7b1 100644 --- a/molecule/centos7/tests/test_default.py +++ b/molecule/centos7/tests/test_default.py @@ -10,17 +10,25 @@ testinfra_hosts = testinfra.utils.ansible_runner.AnsibleRunner( os.environ['MOLECULE_INVENTORY_FILE']).get_hosts('all') -def test_hassio_running_and_enabled(host): - hassio = host.service("homeassistant") - assert hassio.is_running - assert hassio.is_enabled +def test_homeassistant_running(host): + homeassistant = host.docker("homeassistant") + + assert homeassistant.is_running -def test_hassio_socket(host): +def test_homeassistant_socket(host): # Verify the socket is listening for HTTP traffic for _ in range(30): - if host.socket("tcp://0.0.0.0:8123").is_listening: + if host.socket("tcp://127.0.0.1:8123").is_listening: break time.sleep(1) - assert host.socket("tcp://0.0.0.0:8123").is_listening + assert host.socket("tcp://127.0.0.1:8123").is_listening + + +def test_homeassistant_conn_error(host): + code = int(host.run("curl -sL -w '%{http_code}' http://127.0.0.1:8123/ -o /dev/null").stdout) + body = host.run("curl -sLX GET http://127.0.0.1:8123/").stdout + + assert code == 200 + assert "Home Assistant" in body diff --git a/molecule/requirements.yml b/molecule/requirements.yml index df26a8f..73e35dc 100644 --- a/molecule/requirements.yml +++ b/molecule/requirements.yml @@ -4,7 +4,7 @@ collections: - name: community.general roles: - - src: https://gitea.rknet.org/ansible/xoxys.python3.git - name: xoxys.python3 + - src: https://gitea.rknet.org/ansible/xoxys.docker_engine.git + name: xoxys.docker_engine scm: git version: master diff --git a/tasks/install.yml b/tasks/install.yml deleted file mode 100644 index efa9d2e..0000000 --- a/tasks/install.yml +++ /dev/null @@ -1,57 +0,0 @@ ---- -- name: Prepare base folders - file: - path: "{{ item }}" - state: directory - owner: "{{ homeassistant_user }}" - group: "{{ homeassistant_user }}" - mode: 0750 - loop: - - "{{ homeassistant_base_dir }}" - - "{{ homeassistant_conf_dir }}" - become: True - become_user: root - -- block: - - name: Upgrade python dependencies - pip: - name: "{{ item }}" - virtualenv: "{{ homeassistant_base_dir }}/venv" - virtualenv_command: /usr/bin/python3 -m venv - extra_args: --upgrade - loop: - - pip - - setuptools - - wheel - - - name: Install homeassistant - pip: - name: homeassistant - version: "{{ homeassistant_version }}" - virtualenv: "{{ homeassistant_base_dir }}/venv" - virtualenv_command: /usr/bin/python3 -m venv - notify: __homeassistant_restart - become: True - become_user: "{{ homeassistant_user }}" - -- block: - - name: Patch /boot/cmdline - copy: - content: "{{ homeassistant_cmdline_content }}" - dest: /boot/cmdline.txt - when: homeassistant_cmdline_override_enabled | bool - - - name: Exclude serial devices from ModemManager - template: - src: "etc/udev/rules.d/99-mm-disable.rules.j2" - dest: "/etc/udev/rules.d/99-mm-disable.rules" - when: homeassistant_exclude_modemmanager | bool - notify: __udev_reload - - - name: Copy systemd unit file - template: - src: "etc/systemd/system/homeassistant.service.j2" - dest: "/etc/systemd/system/homeassistant.service" - notify: __homeassistant_restart - become: True - become_user: root diff --git a/tasks/main.yml b/tasks/main.yml index 9adfb9a..6422299 100644 --- a/tasks/main.yml +++ b/tasks/main.yml @@ -1,7 +1,2 @@ --- -- import_tasks: prepare.yml -- import_tasks: install.yml -- import_tasks: tls.yml - when: homeassistant_tls_enabled | bool - tags: tls_renewal -- import_tasks: post_tasks.yml +- import_tasks: setup.yml diff --git a/tasks/post_tasks.yml b/tasks/post_tasks.yml deleted file mode 100644 index b21bfe0..0000000 --- a/tasks/post_tasks.yml +++ /dev/null @@ -1,9 +0,0 @@ ---- -- name: Ensure homeassistant service is up and running - systemd: - state: started - daemon_reload: yes - enabled: yes - name: homeassistant - become: True - become_user: root diff --git a/tasks/prepare.yml b/tasks/prepare.yml deleted file mode 100644 index 6f3a4df..0000000 --- a/tasks/prepare.yml +++ /dev/null @@ -1,22 +0,0 @@ ---- -- block: - - name: Create group '{{ homeassistant_group }}' - group: - name: "{{ homeassistant_group }}" - state: present - - - name: Create user '{{ homeassistant_user }}' - user: - comment: homeassistant - name: "{{ homeassistant_user }}" - home: "{{ homeassistant_user_home }}" - group: "{{ homeassistant_group }}" - groups: "{{ homeassistant_extra_groups | join(',') }}" - - - name: Install dependencies - package: - name: "{{ item }}" - state: present - loop: "{{ homeassistant_packages }}" - become: True - become_user: root diff --git a/tasks/setup.yml b/tasks/setup.yml new file mode 100644 index 0000000..8e97b70 --- /dev/null +++ b/tasks/setup.yml @@ -0,0 +1,49 @@ +--- +- block: + - name: Ensure service directory exists + file: + path: "{{ homeassistant_service_directory }}" + mode: 0750 + state: directory + + - name: Create bind mount source directory + file: + path: "{{ item.name }}" + mode: 0750 + state: directory + loop: "{{ homeassistant_volumes }}" + loop_control: + label: "{{ item.name }}" + when: item.bind | bool + + - name: Deploy compose file to '{{ homeassistant_service_directory }}' + template: + src: "services/homeassistant_compose.yml.j2" + dest: "{{ homeassistant_service_directory }}/docker-compose.yml" + owner: root + group: root + mode: 0640 + validate: "docker-compose -f %s config -q" + + - name: Patch /boot/cmdline + copy: + content: "{{ homeassistant_cmdline_content }}" + dest: /boot/cmdline.txt + when: homeassistant_cmdline_override_enabled | bool + + - name: Exclude serial devices from ModemManager + template: + src: "etc/udev/rules.d/99-mm-disable.rules.j2" + dest: "/etc/udev/rules.d/99-mm-disable.rules" + when: homeassistant_exclude_modemmanager | bool + notify: __udev_reload + + - name: Ensure service is up and running + docker_compose: + project_src: "{{ homeassistant_service_directory }}" + pull: yes + remove_orphans: yes + stopped: "{{ homeassistant_service_stopped }}" + state: present + become: True + become_user: root diff --git a/tasks/tls.yml b/tasks/tls.yml deleted file mode 100644 index 3244330..0000000 --- a/tasks/tls.yml +++ /dev/null @@ -1,37 +0,0 @@ ---- -- block: - - name: Create tls folder structure - file: - path: "{{ item }}" - state: directory - owner: "{{ homeassistant_user }}" - group: "{{ homeassistant_group }}" - recurse: True - loop: - - "{{ homeassistant_tls_dhparam_path | dirname }}" - - "{{ homeassistant_tls_cert_path | dirname }}" - - "{{ homeassistant_tls_key_path | dirname }}" - become: True - become_user: root - -- block: - - name: Copy certs and private key - copy: - src: "{{ item.src }}" - dest: "{{ item.dest }}" - mode: "{{ item.mode }}" - loop: - - { src: "{{ homeassistant_tls_key_source }}", dest: '{{ homeassistant_tls_key_path }}', mode: '0600' } - - { src: "{{ homeassistant_tls_cert_source }}", dest: '{{ homeassistant_tls_cert_path }}', mode: '0650' } - loop_control: - label: "{{ item.dest }}" - notify: __homeassistant_restart - - - name: Create Diffie-Hellman Parameter - openssl_dhparam: - path: "{{ homeassistant_tls_dhparam_path }}" - size: "{{ homeassistant_tls_dhparam_size }}" - when: homeassistant_tls_dhparam_path is defined - notify: __homeassistant_restart - become: True - become_user: "{{ homeassistant_user }}" diff --git a/templates/etc/systemd/system/homeassistant.service.j2 b/templates/etc/systemd/system/homeassistant.service.j2 deleted file mode 100644 index 690a7e6..0000000 --- a/templates/etc/systemd/system/homeassistant.service.j2 +++ /dev/null @@ -1,19 +0,0 @@ -#jinja2: lstrip_blocks: True -{{ ansible_managed | comment }} -[Unit] -Description=Home Assistant -Wants=network-online.target -{% for item in homeassistant_systemd_after %} -After={{ item }} -{% endfor %} - -[Service] -Type=simple -User={{ homeassistant_user }} -Group={{ homeassistant_group }} -ExecStart={{ homeassistant_base_dir }}/venv/bin/hass -c {{ homeassistant_conf_dir }} -Restart=on-failure -RestartSec=5s - -[Install] -WantedBy=multi-user.target diff --git a/templates/services/homeassistant_compose.yml.j2 b/templates/services/homeassistant_compose.yml.j2 new file mode 100644 index 0000000..efe9f7a --- /dev/null +++ b/templates/services/homeassistant_compose.yml.j2 @@ -0,0 +1,90 @@ +#jinja2:lstrip_blocks: True +{{ ansible_managed | comment }} +version: "2.4" + +services: + homeassistant: + container_name: {{ homeassistant_container_name }} + image: {{ homeassistant_image }} + restart: {{ homeassistant_restart_policy }} + {% if homeassistant_exposed_ports | default([]) %} + ports: + {% for port in homeassistant_exposed_ports %} + - {{ port | quote }} + {% endfor %} + {% endif %} + {% if homeassistant_volumes | default([]) %} + volumes: + {% for volume in homeassistant_volumes %} + - "{{ volume.name }}:{{ volume.dest }}{% if volume.bind_opt is defined %}:{{ volume.bind_opt }}{% endif %}" + {% endfor %} + {% endif %} + {% if homeassistant_devices | default([]) %} + devices: + {% for device in homeassistant_devices %} + - "{{ device.src }}:{{ device.dest }}{% if device.opt is defined %}:{{ device.opt }}{% endif %}" + {% endfor %} + {% endif %} + {% if homeassistant_networks_applied | default([]) %} + networks: + {% for network in homeassistant_networks_applied %} + - {{ network }} + {% endfor %} + {% endif %} + {% if homeassistant_extra_hosts | default([]) %} + extra_hosts: + {% for host in homeassistant_extra_hosts %} + - {{ host | quote }} + {% endfor %} + {% endif %} + environment: + - TZ={{ homeassistant_timezone }} + {% if homeassistant_memory_limit is defined %} + mem_limit: {{ homeassistant_memory_limit }} + {% endif %} + {% if homeassistant_memory_reservation is defined %} + mem_reservation: {{ homeassistant_memory_reservation }} + {% endif %} + {% if homeassistant_cpu_shares is defined %} + cpu_shares: {{ homeassistant_cpu_shares }} + {% endif %} + {% if not homeassistant_cap_add | length == 0 %} + cap_add: + {% for item in homeassistant_cap_add %} + - {{ item }} + {% endfor %} + {% endif %} + {% if not homeassistant_cap_drop | length == 0 %} + cap_drop: + {% for item in homeassistant_cap_drop %} + - {{ item }} + {% endfor %} + {% endif %} + {% if not homeassistant_security_opt | length == 0 %} + security_opt: + {% for item in homeassistant_security_opt %} + - {{ item }} + {% endfor %} + {% endif %} + {% if homeassistant_pids_limit is defined %} + pids_limit: {{ homeassistant_pids_limit }} + {% endif %} +{% if homeassistant_volumes | default([]) | rejectattr("bind") | list | length > 0 %} + +volumes: + {% for volume in homeassistant_volumes | rejectattr("bind") %} + {{ volume.name }}: + {% endfor %} +{% endif %} +{% if homeassistant_networks | default([]) | length > 0 %} + +networks: + {% for network in homeassistant_networks %} + {{ network.name }}: + {% if network.external | default(False) | bool %} + external: true + {% else %} + driver: {{ network.backend | default("bridge") }} + {% endif %} + {% endfor %} +{% endif %}