diff --git a/README.md b/README.md index 138f1f5..6195bf7 100644 --- a/README.md +++ b/README.md @@ -10,3 +10,7 @@ - [`mariadb`](roles/mariadb/README.md): deploy mariadb in a docker container + +- [`valkey`](roles/valkey/README.md): deploy and configure valkey, + an open source in-memory data store under BSD license, forked + from redis. diff --git a/galaxy.yml b/galaxy.yml index 6be2f77..5f215ec 100644 --- a/galaxy.yml +++ b/galaxy.yml @@ -16,4 +16,5 @@ tags: - elasticsearch - redis - mariadb + - valkey - docker diff --git a/playbooks/valkey.yml b/playbooks/valkey.yml new file mode 100644 index 0000000..d5d943e --- /dev/null +++ b/playbooks/valkey.yml @@ -0,0 +1,6 @@ +--- +- name: Deploy and configure valkey + hosts: "{{ valkey_hosts | default('valkey', true) }}" + become: "{{ valkey_become | default(true, true) }}" + roles: + - finallycoffee.databases.valkey diff --git a/roles/valkey/README.md b/roles/valkey/README.md new file mode 100644 index 0000000..341eb02 --- /dev/null +++ b/roles/valkey/README.md @@ -0,0 +1,13 @@ +# `finallycoffee.databases.valkey` ansible role + +Valkey is an open source (BSD 3 licensed), high-performance in-memory key/value +data store, ideal for workloads like caching or message queues. It has been +forked from redis 7.2.4 before redis license was changed to SSPL. + +Valkey offers compatibility to redis and can be used as a drop-in replacement +for redis. + +## Configuration + +For the configuration, see the [`redis` role configuration](../redis/README.md#configuration), +and swap the `redis_` prefix of all variables for the `valkey_` prefix. diff --git a/roles/valkey/defaults/main/config.yml b/roles/valkey/defaults/main/config.yml new file mode 100644 index 0000000..4238bae --- /dev/null +++ b/roles/valkey/defaults/main/config.yml @@ -0,0 +1,41 @@ +--- +valkey_config_bind: + - "127.0.0.1" + - "-::1" +valkey_config_protected_mode: true +valkey_config_port: 6379 +valkey_config_unixsocket: ~ +valkey_config_unixsocketperm: '700' +valkey_config_user: + - "default on +@all -DEBUG ~* nopass" +valkey_config_databases: 16 +valkey_config_supervised: false +valkey_config_daemonize: false +valkey_config_dbfilename: dump.rdb +valkey_config_dir: "{{ valkey_data_path }}" +valkey_config_save: "3600 1 300 100 60 10000" +valkey_config_appendfsync: everysec + +valkey_base_config: + bind: "{{ valkey_config_bind | join(' ') }}" + "protected-mode": "{{ valkey_config_protected_mode | bool | ternary('yes', 'no') }}" + port: "{{ valkey_config_port }}" + user: "{{ valkey_config_user }}" + databases: "{{ valkey_config_databases }}" + daemonize: "{{ valkey_config_daemonize | bool | ternary('yes', 'no') }}" + supervised: "{{ valkey_config_supervised | bool | ternary('yes', 'no') }}" + save: "{{ valkey_config_save }}" + dbfilename: "{{ valkey_config_dbfilename }}" + dir: "{{ valkey_config_dir }}" + appendfsync: "{{ valkey_config_appendfsync }}" + +valkey_config: ~ +valkey_merged_config: >-2 + {{ valkey_base_config + | combine(({ + 'unixsocket': valkey_config_unixsocket, + 'unixsocketperm': valkey_config_unixsocketperm, + }) + if (valkey_config_unixsocket | default(false, true)) else {}, + recursive=True) + | combine(valkey_config | default({}, true), recursive=True) }} diff --git a/roles/valkey/defaults/main/container.yml b/roles/valkey/defaults/main/container.yml new file mode 100644 index 0000000..a52366b --- /dev/null +++ b/roles/valkey/defaults/main/container.yml @@ -0,0 +1,49 @@ +--- +valkey_container_image_registry: docker.io +valkey_container_image_namespace: valkey +valkey_container_image_name: valkey +valkey_container_image_tag: ~ +valkey_container_image_flavour: alpine +valkey_container_image_source: pull +valkey_container_image_force_source: >-2 + {{ valkey_container_image_tag | default(false, true) | bool }} +valkey_container_image: >-2 + {{ + ([ + valkey_container_image_registry | default([], true), + valkey_container_image_namespace | default([], true), + valkey_container_image_name, + ] | flatten | join('/')) + + ':' + + (valkey_container_image_tag | default( + valkey_version + ( + ((valkey_container_image_flavour is string) + and (valkey_container_image_flavour | length > 0)) + | ternary('-' + (valkey_container_image_flavour | default('')), '') + ), + true, + )) + }} + +valkey_container_name: "valkey{{ valkey_instance_suffix }}" +valkey_container_env: ~ +valkey_container_user: >-2 + {{ valkey_run_user_id }}:{{ valkey_run_group_id }} +valkey_container_ports: ~ +valkey_container_labels: ~ +valkey_container_volumes: ~ +valkey_container_merged_volumes: >-2 + {{ valkey_container_base_volumes + + valkey_container_volumes | default([], true) }} +valkey_container_command: + - "valkey-server" + - "{{ valkey_config_file }}" +valkey_container_networks: ~ +valkey_container_etc_hosts: ~ +valkey_container_dns_servers: ~ +valkey_container_restart_policy: "unless-stopped" +valkey_container_state: >-2 + {{ (valkey_state == 'present') | ternary('started', 'absent') }} +valkey_container_base_volumes: + - "{{ valkey_config_file }}:{{ valkey_config_file }}:ro" + - "{{ valkey_data_path }}:{{ valkey_data_path }}:rw" diff --git a/roles/valkey/defaults/main/main.yml b/roles/valkey/defaults/main/main.yml new file mode 100644 index 0000000..ebd96f8 --- /dev/null +++ b/roles/valkey/defaults/main/main.yml @@ -0,0 +1,15 @@ +--- +valkey_version: "8.0.1" +valkey_state: "present" +valkey_instance: ~ +valkey_instance_suffix: >-2 + {{ ((valkey_instance is string) and (valkey_instance | length > 0)) + | ternary('-' + (valkey_instance | default('', true)), '') }} +valkey_user: >-2 + valkey{{ valkey_instance_suffix }} + +valkey_config_path: "/etc/valkey" +valkey_config_file: >-2 + {{ valkey_config_path }}/valkey{{ valkey_instance_suffix }}.conf +valkey_data_path: "/var/lib/valkey{{ valkey_instance_suffix }}" +valkey_deployment_method: docker diff --git a/roles/valkey/defaults/main/user.yml b/roles/valkey/defaults/main/user.yml new file mode 100644 index 0000000..4ba27dd --- /dev/null +++ b/roles/valkey/defaults/main/user.yml @@ -0,0 +1,10 @@ +--- +valkey_run_user_id: >-2 + {{ valkey_user_info.uid | default(valkey_user, true) }} +valkey_run_group_id: >-2 + {{ valkey_user_info.group | default(valkey_user, true) }} +valkey_user_system: true +valkey_user_create_home: false +valkey_user_groups: ~ +valkey_user_append_groups: >-2 + {{ valkey_user_groups | default(true, false) | bool }} diff --git a/roles/valkey/handlers/main.yml b/roles/valkey/handlers/main.yml new file mode 100644 index 0000000..c5e9afd --- /dev/null +++ b/roles/valkey/handlers/main.yml @@ -0,0 +1,11 @@ +--- +- name: Ensure valkey container '{{ valkey_container_name }}' is restarted + community.docker.docker_container: + name: "{{ valkey_container_name }}" + state: "{{ valkey_container_state }}" + restart: true + listen: valkey-restart + when: + - valkey_deployment_method == 'docker' + - valkey_state == 'present' + ignore_errors: "{{ ansible_check_mode }}" diff --git a/roles/valkey/meta/main.yml b/roles/valkey/meta/main.yml new file mode 100644 index 0000000..c52c733 --- /dev/null +++ b/roles/valkey/meta/main.yml @@ -0,0 +1,11 @@ +--- +allow_duplicates: true +dependencies: [] +galaxy_info: + role_name: valkey + description: >-2 + An open source, in-memory datastore under BSD 3 license + galaxy_tags: + - valkey + - redis + - docker diff --git a/roles/valkey/tasks/deploy-docker.yml b/roles/valkey/tasks/deploy-docker.yml new file mode 100644 index 0000000..724155e --- /dev/null +++ b/roles/valkey/tasks/deploy-docker.yml @@ -0,0 +1,26 @@ +--- +- name: Ensure container image '{{ valkey_container_image }}' is {{ valkey_state }} + community.docker.docker_image: + name: "{{ valkey_container_image }}" + state: "{{ valkey_state }}" + source: "{{ valkey_container_image_source }}" + force_source: "{{ valkey_container_image_force_source }}" + register: valkey_container_image_info + until: valkey_container_image_info is success + retries: 5 + delay: 3 + +- name: Ensure container '{{ valkey_container_name }}' is {{ valkey_container_state }} + community.docker.docker_container: + name: "{{ valkey_container_name }}" + image: "{{ valkey_container_image }}" + env: "{{ valkey_container_env | default(omit, true) }}" + user: "{{ valkey_container_user }}" + ports: "{{ valkey_container_ports | default(omit, true) }}" + labels: "{{ valkey_container_labels | default(omit, true) }}" + command: "{{ valkey_container_command }}" + volumes: "{{ valkey_container_merged_volumes }}" + networks: "{{ valkey_container_networks | default(omit, true) }}" + etc_hosts: "{{ valkey_container_etc_hosts | default(omit, true) }}" + dns_servers: "{{ valkey_container_dns_servers | default(omit, true) }}" + state: "{{ valkey_container_state }}" diff --git a/roles/valkey/tasks/main.yml b/roles/valkey/tasks/main.yml new file mode 100644 index 0000000..b87ee81 --- /dev/null +++ b/roles/valkey/tasks/main.yml @@ -0,0 +1,68 @@ +--- +- name: Ensure state is valid + ansible.builtin.fail: + msg: >-2 + Unsupported state '{{ valkey_state }}'. + Supported states are {{ valkey_states | join(', ') }} + when: valkey_state not in valkey_states + +- name: Ensure deployment method is valid + ansible.builtin.fail: + msg: >-2 + Unsupported deployment method '{{ valkey_deployment_method }}'! + Supported methods are {{ valkey_deployment_method | join(', ') }} + when: valkey_deployment_method not in valkey_deployment_methods + +- name: Ensure valkey user '{{ valkey_user }}' is {{ valkey_state }} + ansible.builtin.user: + name: "{{ valkey_user }}" + state: "{{ valkey_state }}" + system: "{{ valkey_user_system }}" + create_home: "{{ valkey_user_create_home }}" + groups: "{{ valkey_user_groups | default(omit, true) }}" + append: "{{ valkey_user_append_groups | default(omit, true) }}" + register: valkey_user_info + +- name: Ensure valkey config file '{{ valkey_config_file }}' is {{ valkey_state }} + ansible.builtin.file: + path: "{{ valkey_config_file }}" + state: "{{ valkey_state }}" + when: valkey_state == 'absent' + +- name: Ensure valkey host directories are {{ valkey_state }} + ansible.builtin.file: + path: "{{ path.name }}" + state: >-2 + {{ (valkey_state == 'present') | ternary('directory', 'absent') }} + owner: "{{ path.owner | default(valkey_run_user_id) }}" + group: "{{ path.group | default(valkey_run_group_id) }}" + mode: "{{ path.mode | default('0755') }}" + loop: + - name: "{{ valkey_config_path }}" + - name: "{{ valkey_data_path }}" + loop_control: + loop_var: "path" + label: "{{ path.name }}" + +- name: Ensure valkey config file '{{ valkey_config_file }}' is {{ valkey_state }} + ansible.builtin.copy: + content: |+2 + {% for tuple in (valkey_merged_config | dict2items) %} + {% if tuple.value is string or tuple.value is number %} + {{ tuple.key }} {{ tuple.value }} + {% else %} + {% for value in tuple.value %} + {{ tuple.key }} {{ value }} + {% endfor %} + {% endif %} + {% endfor %} + dest: "{{ valkey_config_file }}" + owner: "{{ valkey_run_user_id }}" + group: "{{ valkey_run_group_id }}" + mode: "0640" + when: valkey_state == 'present' + notify: valkey-restart + +- name: Deploy valkey using {{ valkey_deployment_method }} + ansible.builtin.include_tasks: + file: "deploy-{{ valkey_deployment_method }}.yml" diff --git a/roles/valkey/vars/main.yml b/roles/valkey/vars/main.yml new file mode 100644 index 0000000..6303e85 --- /dev/null +++ b/roles/valkey/vars/main.yml @@ -0,0 +1,6 @@ +--- +valkey_states: + - present + - absent +valkey_deployment_methods: + - docker