Feat: Added element call setup and configuration.

This commit is contained in:
wjbeckett
2024-09-25 14:53:48 +10:00
parent 482861fce1
commit 6594cce570
20 changed files with 676 additions and 0 deletions

View File

@ -0,0 +1,34 @@
---
# roles/custom/matrix-element-call/defaults/main.yml
# Enable or disable matrix-element-call deployment
matrix_element_call_enabled: false
# Base path configuration
matrix_element_call_base_path: "/home/{{ matrix_user_username }}/dockerdata/volumes/elementcall"
matrix_element_call_config_path: "{{ matrix_element_call_base_path }}/config"
matrix_element_call_backend_path: "{{ matrix_element_call_base_path }}/backend"
# Docker network configuration
matrix_element_call_container_network: "matrix_element_call_network"
# Docker images
matrix_element_call_image: "ghcr.io/element-hq/element-call:latest"
matrix_jwt_service_image: "ghcr.io/element-hq/lk-jwt-service:latest-ci"
matrix_livekit_image: "livekit/livekit-server:latest"
matrix_redis_image: "redis:6-alpine"
# Ports
matrix_element_call_port: "8093"
matrix_jwt_service_port: "8881"
matrix_redis_port: "6379"
# LiveKit configuration (should be set in host_vars or globally)
matrix_element_call_livekit_dev_key: "{{ matrix_livekit_dev_key | default('unset-livekit-key') }}"
matrix_element_call_livekit_service_url: "wss://sfu.{{ matrix_base_domain }}:443"
# Well-known paths and domains (derived from matrix_base_domain)
matrix_element_call_domain: "call.{{ matrix_base_domain }}"
matrix_element_call_well_known_client_path: "/var/www/.well-known/matrix/client"
matrix_element_call_well_known_element_path: "/var/www/.well-known/element/element.json"
matrix_element_call_base_url: "https://{{ matrix_element_call_domain }}"

View File

@ -0,0 +1,22 @@
---
---
# Create the element.json file to point to Element Call
- name: Ensure .well-known/element directory exists
ansible.builtin.file:
path: "{{ matrix_element_call_well_known_element_path | dirname }}"
state: directory
mode: 0755
- name: Create or update the element.json file with Element Call config
ansible.builtin.copy:
dest: "{{ matrix_element_call_well_known_element_path }}"
content: |
{
"call": {
"widget_url": "{{ matrix_element_call_base_url }}"
}
}
mode: '0644'
owner: "{{ matrix_user_username }}"
group: "{{ matrix_user_groupname }}"

View File

@ -0,0 +1,17 @@
---
# Main install task for matrix-element-call
- name: Install Docker containers for Element Call
include_tasks: install_docker_containers.yml
- name: Update .well-known/matrix/client
include_tasks: update_well_known_client.yml
- name: Create .well-known/element/element.json
include_tasks: create_element_json.yml
- name: Update homeserver.yaml for Element Call
include_tasks: update_homeserver_config.yml
- name: Update Element-Web config.json for Element Call
include_tasks: update_element_web_config.yml

View File

@ -0,0 +1,158 @@
---
# Ensure Required Directories Exist
- name: Ensure matrix-element-call paths exist
ansible.builtin.file:
path: "{{ item.path }}"
state: directory
mode: 0750
owner: "{{ matrix_user_username }}"
group: "{{ matrix_user_groupname }}"
loop:
- path: "{{ matrix_element_call_base_path }}"
- path: "{{ matrix_element_call_base_path }}/data"
- path: "{{ matrix_element_call_base_path }}/config"
- path: "{{ matrix_element_call_base_path }}/backend" # For LiveKit and Redis config
# Ensure Configuration Files are in Place
- name: Ensure Element Call config.json is in place
ansible.builtin.template:
src: "{{ role_path }}/templates/config.json.j2"
dest: "{{ matrix_element_call_base_path }}/config/config.json"
mode: 0640
owner: "{{ matrix_user_username }}"
group: "{{ matrix_user_groupname }}"
- name: Ensure LiveKit livekit.yaml is in place
ansible.builtin.template:
src: "{{ role_path }}/templates/livekit.yaml.j2"
dest: "{{ matrix_element_call_base_path }}/backend/livekit.yaml"
mode: 0640
owner: "{{ matrix_user_username }}"
group: "{{ matrix_user_groupname }}"
- name: Ensure Redis redis.conf is in place
ansible.builtin.template:
src: "{{ role_path }}/templates/redis.conf.j2"
dest: "{{ matrix_element_call_base_path }}/backend/redis.conf"
mode: 0640
owner: "{{ matrix_user_username }}"
group: "{{ matrix_user_groupname }}"
- name: Ensure matrix-element-call environment file is in place
ansible.builtin.template:
src: "{{ role_path }}/templates/env.j2"
dest: "{{ matrix_element_call_base_path }}/config/env"
mode: 0640
owner: "{{ matrix_user_username }}"
group: "{{ matrix_user_groupname }}"
- name: Ensure matrix-element-call Docker labels file is in place
ansible.builtin.template:
src: "{{ role_path }}/templates/labels.j2"
dest: "{{ matrix_element_call_base_path }}/config/labels"
mode: 0640
owner: "{{ matrix_user_username }}"
group: "{{ matrix_user_groupname }}"
# Ensure Docker Images are Pulled
- name: Ensure matrix-element-call Docker image is pulled
community.docker.docker_image:
name: "{{ matrix_element_call_image }}"
source: pull
force_source: "{{ matrix_element_call_container_image_force_pull }}"
register: element_call_image_result
retries: "{{ devture_playbook_help_container_retries_count }}"
delay: "{{ devture_playbook_help_container_retries_delay }}"
until: element_call_image_result is not failed
- name: Ensure jwt-service Docker image is pulled
community.docker.docker_image:
name: "ghcr.io/element-hq/lk-jwt-service:latest-ci"
source: pull
register: jwt_image_result
retries: 3
delay: 10
until: jwt_image_result is not failed
- name: Ensure livekit Docker image is pulled
community.docker.docker_image:
name: "livekit/livekit-server:latest"
source: pull
register: livekit_image_result
retries: 3
delay: 10
until: livekit_image_result is not failed
- name: Ensure redis Docker image is pulled
community.docker.docker_image:
name: "redis:6-alpine"
source: pull
register: redis_image_result
retries: 3
delay: 10
until: redis_image_result is not failed
# Ensure Docker Containers are Running
- name: Run matrix-element-call Docker container
community.docker.docker_container:
name: "matrix-element-call"
image: "{{ matrix_element_call_image }}"
state: started
restart_policy: unless-stopped
env_file: "{{ matrix_element_call_base_path }}/config/env"
labels: "{{ lookup('file', matrix_element_call_base_path ~ '/config/labels') | from_yaml }}"
networks:
- name: "{{ matrix_element_call_container_network }}"
volumes:
- "{{ matrix_element_call_base_path }}/config/config.json:/app/config.json"
- "{{ matrix_element_call_base_path }}/data:/data"
- name: Run jwt-service Docker container
community.docker.docker_container:
name: "matrix-jwt-service"
image: "ghcr.io/element-hq/lk-jwt-service:latest-ci"
state: started
restart_policy: unless-stopped
environment:
LIVEKIT_SECRET: "{{ matrix_element_call_livekit_dev_key }}" # User-specified key
LIVEKIT_URL: "{{ matrix_element_call_livekit_service_url }}"
LIVEKIT_KEY: "devkey"
networks:
- name: "{{ matrix_element_call_container_network }}"
- name: Run livekit Docker container
community.docker.docker_container:
name: "matrix-livekit"
image: "livekit/livekit-server:latest"
state: started
restart_policy: unless-stopped
command: "--dev --config /etc/livekit.yaml"
volumes:
- "{{ matrix_element_call_base_path }}/backend/livekit.yaml:/etc/livekit.yaml"
network_mode: "host"
- name: Run redis Docker container
community.docker.docker_container:
name: "matrix-redis"
image: "redis:6-alpine"
state: started
restart_policy: unless-stopped
command: redis-server /etc/redis.conf
volumes:
- "{{ matrix_element_call_base_path }}/backend/redis.conf:/etc/redis.conf"
networks:
- name: "{{ matrix_element_call_container_network }}"
# Ensure Systemd Services are Set Up
- name: Ensure matrix-element-call systemd service is installed
ansible.builtin.template:
src: "{{ role_path }}/templates/element-call.service.j2"
dest: "{{ devture_systemd_docker_base_systemd_path }}/matrix-element-call.service"
mode: 0644
- name: Ensure matrix-element-call systemd service is enabled and started
ansible.builtin.systemd:
name: matrix-element-call
enabled: true
state: started
daemon_reload: true

View File

@ -0,0 +1,14 @@
---
# roles/custom/matrix-element-call/tasks/main.yml
- name: Ensure Element Call dependencies are valid and present
when: matrix_element_call_enabled | bool
ansible.builtin.include_tasks: "{{ role_path }}/tasks/validate_config.yml"
- name: Install Element Call and its related services
when: matrix_element_call_enabled | bool
ansible.builtin.include_tasks: "{{ role_path }}/tasks/install.yml"
- name: Uninstall Element Call and clean up resources
when: not matrix_element_call_enabled | bool
ansible.builtin.include_tasks: "{{ role_path }}/tasks/uninstall.yml"

View File

@ -0,0 +1,51 @@
---
---
# roles/custom/matrix-element-call/tasks/uninstall.yml
# Stop and remove Docker containers
- name: Stop and remove matrix-element-call Docker container
community.docker.docker_container:
name: "matrix-element-call"
state: absent
when: matrix_element_call_enabled | bool == false
- name: Stop and remove jwt-service Docker container
community.docker.docker_container:
name: "matrix-jwt-service"
state: absent
when: matrix_element_call_enabled | bool == false
- name: Stop and remove livekit Docker container
community.docker.docker_container:
name: "matrix-livekit"
state: absent
when: matrix_element_call_enabled | bool == false
- name: Stop and remove redis Docker container
community.docker.docker_container:
name: "matrix-redis"
state: absent
when: matrix_element_call_enabled | bool == false
# Remove matrix-element-call directories
- name: Remove matrix-element-call directories
ansible.builtin.file:
path: "{{ item }}"
state: absent
loop:
- "{{ matrix_element_call_base_path }}"
when: matrix_element_call_enabled | bool == false
# Disable and remove systemd service for matrix-element-call
- name: Stop and disable matrix-element-call systemd service
ansible.builtin.systemd:
name: matrix-element-call
state: stopped
enabled: false
when: matrix_element_call_enabled | bool == false
- name: Remove matrix-element-call systemd service file
ansible.builtin.file:
path: "{{ devture_systemd_docker_base_systemd_path }}/matrix-element-call.service"
state: absent
when: matrix_element_call_enabled | bool == false

View File

@ -0,0 +1,29 @@
---
# Update Element-Web config.json with Element Call features
- name: Ensure Element-Web config.json exists
ansible.builtin.file:
path: "{{ matrix_element_web_config_path }}"
state: file
mode: 0644
- name: Update Element-Web config.json with Element Call features
ansible.builtin.blockinfile:
path: "{{ matrix_element_web_config_path }}"
block: |
"features": {
"feature_video_rooms": true,
"feature_new_room_decoration_ui": true,
"feature_group_calls": true,
"feature_element_call_video_rooms": true
},
"element_call": {
"url": "{{ matrix_element_call_base_url }}",
"participant_limit": 8,
"brand": "Element Call",
"use_exclusively": true
}
marker: "# ANSIBLE MANAGED BLOCK - Element Call features"
mode: '0644'
owner: "{{ matrix_user_username }}"
group: "{{ matrix_user_groupname }}"

View File

@ -0,0 +1,36 @@
---
# Update the homeserver.yaml file with Element Call config
- name: Ensure homeserver.yaml exists
ansible.builtin.file:
path: "{{ matrix_homeserver_config_path }}"
state: file
mode: 0644
- name: Add listeners section for Element Call to homeserver.yaml
ansible.builtin.blockinfile:
path: "{{ matrix_homeserver_config_path }}"
block: |
listeners:
- port: 8008
tls: false
type: http
x_forwarded: true
resources:
- names: [client, federation, openid]
compress: false
marker: "# ANSIBLE MANAGED BLOCK - Element Call listeners"
mode: '0644'
owner: "{{ matrix_user_username }}"
group: "{{ matrix_user_groupname }}"
- name: Ensure serve_server_wellknown is enabled in homeserver.yaml
ansible.builtin.lineinfile:
path: "{{ matrix_homeserver_config_path }}"
line: "serve_server_wellknown: true"
insertafter: EOF
state: present
mode: '0644'
owner: "{{ matrix_user_username }}"
group: "{{ matrix_user_groupname }}"

View File

@ -0,0 +1,24 @@
---
---
# Update the .well-known/matrix/client file with Element Call config
- name: Ensure .well-known directory exists
ansible.builtin.file:
path: "{{ matrix_element_call_well_known_client_path | dirname }}"
state: directory
mode: 0755
- name: Update .well-known/matrix/client file with Element Call config
ansible.builtin.blockinfile:
path: "{{ matrix_element_call_well_known_client_path }}"
block: |
"org.matrix.msc4143.rtc_foci": [
{
"type": "livekit",
"livekit_service_url": "{{ matrix_element_call_jwt_service_url }}"
}
]
create: yes
mode: '0644'
owner: "{{ matrix_user_username }}"
group: "{{ matrix_user_groupname }}"

View File

@ -0,0 +1,79 @@
---
# roles/custom/matrix-element-call/tasks/validate_config.yml
- name: Ensure required variables for Element Call are defined
ansible.builtin.fail:
msg: "The variable '{{ item }}' is required and must be set."
when: vars[item] is not defined or vars[item] == ''
loop:
- matrix_element_call_enabled
- matrix_element_call_version
- matrix_element_call_hostname
- matrix_element_call_path_prefix
- matrix_element_call_base_path
- matrix_element_call_container_image
- matrix_element_call_container_network
- matrix_element_call_container_labels_traefik_hostname
- matrix_element_call_jwt_service_url
- matrix_element_call_livekit_service_url
- matrix_element_call_livekit_dev_key
- name: Validate that the Element Call hostname is properly formatted
ansible.builtin.assert:
that:
- "'{{ matrix_element_call_hostname }}' is match('^([a-zA-Z0-9][-a-zA-Z0-9]*[a-zA-Z0-9])$')"
fail_msg: "The hostname '{{ matrix_element_call_hostname }}' is not valid. It should be a valid domain or subdomain."
success_msg: "The hostname '{{ matrix_element_call_hostname }}' is valid."
- name: Validate that the Element Call version is specified correctly
ansible.builtin.assert:
that:
- matrix_element_call_version is string
- matrix_element_call_version != ''
fail_msg: "The Element Call version must be a non-empty string."
success_msg: "The Element Call version is set correctly."
- name: Ensure LiveKit dev key is set
ansible.builtin.assert:
that:
- matrix_element_call_livekit_dev_key is string
- matrix_element_call_livekit_dev_key != ''
fail_msg: "The LiveKit dev key (matrix_element_call_livekit_dev_key) must be a non-empty string."
success_msg: "The LiveKit dev key is set correctly."
- name: Ensure JWT service URL is valid
ansible.builtin.assert:
that:
- matrix_element_call_jwt_service_url is match('^https?://[a-zA-Z0-9.-]+$')
fail_msg: "The JWT service URL '{{ matrix_element_call_jwt_service_url }}' is not valid."
success_msg: "The JWT service URL is valid."
- name: Ensure LiveKit service URL is valid
ansible.builtin.assert:
that:
- matrix_element_call_livekit_service_url is match('^https?://[a-zA-Z0-9.-]+$')
fail_msg: "The LiveKit service URL '{{ matrix_element_call_livekit_service_url }}' is not valid."
success_msg: "The LiveKit service URL is valid."
- name: Ensure matrix-element-call base path is set and exists
ansible.builtin.assert:
that:
- matrix_element_call_base_path is string
- matrix_element_call_base_path != ''
fail_msg: "The base path for Element Call (matrix_element_call_base_path) must be a non-empty string."
success_msg: "The base path for Element Call is set correctly."
- name: Ensure required paths for configurations are accessible
ansible.builtin.file:
path: "{{ item }}"
state: directory
with_items:
- "{{ matrix_element_call_base_path }}/config"
- "{{ matrix_element_call_base_path }}/backend"
register: config_paths_check
- name: Fail if any required paths for configurations are not accessible
ansible.builtin.fail:
msg: "The required configuration path '{{ item.path }}' does not exist or is not accessible."
when: config_paths_check.results is defined and config_paths_check.results | selectattr('failed', 'eq', True) | list | length > 0
loop: "{{ config_paths_check.results | selectattr('failed', 'eq', True) | list }}"

View File

@ -0,0 +1,11 @@
{
"default_server_config": {
"m.homeserver": {
"base_url": "{{ matrix_homeserver_url }}",
"server_name": "{{ matrix_server_name }}"
}
},
"livekit": {
"livekit_service_url": "{{ matrix_element_call_livekit_service_url }}"
}
}

View File

@ -0,0 +1,9 @@
# Environment variables for Element Call
ELEMENT_CALL_SERVER_URL=https://{{ matrix_element_call_domain }}
LIVEKIT_SERVICE_URL={{ matrix_element_call_livekit_service_url }}
JWT_SERVICE_URL={{ matrix_element_call_jwt_service_url }}
# Optional additional environment variables provided by the user
{% for key, value in matrix_element_call_environment_variables_additional.items() %}
{{ key }}={{ value }}
{% endfor %}

View File

@ -0,0 +1,11 @@
# Docker labels for Traefik or other routing
traefik.enable: "true"
traefik.http.routers.{{ matrix_element_call_hostname | replace('.', '_') }}-router.rule: "Host(`{{ matrix_element_call_hostname }}`)"
traefik.http.routers.{{ matrix_element_call_hostname | replace('.', '_') }}-router.entrypoints: "{{ matrix_element_call_container_labels_traefik_entrypoints }}"
traefik.http.routers.{{ matrix_element_call_hostname | replace('.', '_') }}-router.tls.certresolver: "{{ matrix_element_call_container_labels_traefik_tls_certResolver }}"
traefik.http.services.{{ matrix_element_call_hostname | replace('.', '_') }}-service.loadbalancer.server.port: "8080"
# Additional labels (if any) specified by the user
{% for key, value in matrix_element_call_container_extra_arguments.items() %}
{{ key }}: "{{ value }}"
{% endfor %}

View File

@ -0,0 +1,21 @@
port: 7880
bind_addresses:
- "0.0.0.0"
rtc:
tcp_port: 7881
port_range_start: 50100
port_range_end: 50200
use_external_ip: false
turn:
enabled: false
domain: localhost
cert_file: ""
key_file: ""
tls_port: 5349
udp_port: 443
external_tls: true
keys:
devkey: "{{ matrix_element_call_livekit_dev_key }}"
logging:

View File

@ -0,0 +1,5 @@
bind 0.0.0.0
protected-mode yes
port 6379
timeout 0
tcp-keepalive 300

View File

@ -0,0 +1,17 @@
[Unit]
Description=Matrix Element Call Service
After=docker.service
Requires=docker.service
[Service]
Type=simple
ExecStart=/usr/bin/docker start -a matrix-element-call
ExecStop=/usr/bin/docker stop matrix-element-call
Restart=always
User={{ matrix_user_username }}
Group={{ matrix_user_groupname }}
TimeoutStartSec=0
RestartSec=10
[Install]
WantedBy=multi-user.target

View File

@ -0,0 +1,5 @@
---
# roles/custom/matrix-element-call/vars/main.yml
# Variables specific to matrix-element-call, like service configurations, can go here.
# Any environment-specific values can be overridden here.