Skip to content
Open
27 changes: 27 additions & 0 deletions pmm_qa/pmm-framework.py
Original file line number Diff line number Diff line change
Expand Up @@ -788,6 +788,31 @@ def setup_dockerclients(db_type, db_version=None, db_config=None, args=None):
# Call the function to run the setup_docker_client_images script
execute_shell_scripts(shell_scripts, shell_scripts_path, env_vars, args)

def setup_valkey(db_type, db_version=None, db_config=None, args=None):

# Check if PMM server is running
container_name = get_running_container_name()
if container_name is None and args.pmm_server_ip is None:
print(f"Check if PMM Server is Up and Running..Exiting")
exit()

# Gather Version details
valkey_version = os.getenv('VALKEY_VERSION') or db_version or database_configs[db_type]["versions"][-1]

# Define environment variables for playbook
env_vars = {
'PMM_SERVER_IP': args.pmm_server_ip or container_name or '127.0.0.1',
'VALKEY_VERSION': valkey_version,
'CLIENT_VERSION': get_value('CLIENT_VERSION', db_type, args, db_config),
'ADMIN_PASSWORD': os.getenv('ADMIN_PASSWORD') or args.pmm_server_password or 'admin',
'PMM_QA_GIT_BRANCH': os.getenv('PMM_QA_GIT_BRANCH') or 'v3'
}

# Ansible playbook filename
playbook_filename = 'valkey/valkey.yml'

# Call the function to run the Ansible playbook
run_ansible_playbook(playbook_filename, env_vars, args)

# Set up databases based on arguments received
def setup_database(db_type, db_version=None, db_config=None, args=None):
Expand Down Expand Up @@ -834,6 +859,8 @@ def setup_database(db_type, db_version=None, db_config=None, args=None):
setup_ssl_mlaunch(db_type, db_version, db_config, args)
elif db_type == 'BUCKET':
setup_bucket(db_type, db_version, db_config, args)
elif db_type == 'VALKEY':
setup_valkey(db_type, db_version, db_config, args)
else:
print(f"Database type {db_type} is not recognised, Exiting...")
exit(1)
Expand Down
4 changes: 4 additions & 0 deletions pmm_qa/scripts/database_options.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,5 +76,9 @@
},
"BUCKET": {
"configurations": {"BUCKET_NAMES": 'bcp'}
},
"VALKEY": {
"versions": ["7", "8"],
"configurations": {"CLIENT_VERSION": "3-dev-latest", "SETUP_TYPE": "", "TARBALL": ""}
}
}
15 changes: 15 additions & 0 deletions pmm_qa/valkey/cleanup.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#!/bin/bash -e

docker exec -it pmm-server pmm-admin remove valkey valkey-primary-svc || :
docker exec -it pmm-server pmm-admin remove valkey valkey-replica1-svc || :
docker exec -it pmm-server pmm-admin remove valkey valkey-replica2-svc || :
docker exec -it pmm-server pmm-admin remove valkey sentinel1-svc || :
docker exec -it pmm-server pmm-admin remove valkey sentinel2-svc || :
docker exec -it pmm-server pmm-admin remove valkey sentinel3-svc || :

docker rm -vf valkey-primary valkey-replica-1 valkey-replica-2 || :
docker rm -vf sentinel-1 sentinel-2 sentinel-3 || :

docker volume rm -f valkey-primary-data valkey-replica-1-data valkey-replica-2-data || :

rm -rf "$HOME/valkey"
22 changes: 22 additions & 0 deletions pmm_qa/valkey/sentinel.conf.j2
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# sentinel.conf
bind 0.0.0.0

port 26379

# Monitor the master
sentinel monitor valkey-primary valkey-primary 6379 {{ sentinel_quorum }}
sentinel auth-user valkey-primary default
sentinel auth-pass valkey-primary "{{ valkey_password }}"
sentinel resolve-hostnames yes

# Failover timeouts
sentinel down-after-milliseconds valkey-primary 5000
sentinel failover-timeout valkey-primary 10000
sentinel parallel-syncs valkey-primary 1

# Security
protected-mode no

# Logging
loglevel notice
logfile ""
27 changes: 27 additions & 0 deletions pmm_qa/valkey/valkey-primary.conf.j2
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Basic Valkey configuration for primary
bind 0.0.0.0
port 6379

requirepass "{{ valkey_password }}"
masterauth "{{ valkey_password }}"

# Persistence
save 900 1
save 300 10
save 60 10000

# Replication
replica-serve-stale-data yes
replica-read-only yes
repl-diskless-sync no
repl-diskless-sync-delay 5

# Security
protected-mode no

# Logging
loglevel notice
logfile ""

# Memory management
maxmemory-policy allkeys-lru
28 changes: 28 additions & 0 deletions pmm_qa/valkey/valkey-replica.conf.j2
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Basic Valkey configuration for replica
bind 0.0.0.0
port 6379

requirepass "{{ valkey_password }}"
masterauth "{{ valkey_password }}"

# Replication
replicaof valkey-primary 6379
replica-serve-stale-data yes
replica-read-only yes
repl-diskless-sync no
repl-diskless-sync-delay 5

# Persistence
save 900 1
save 300 10
save 60 10000

# Security
protected-mode no

# Logging
loglevel notice
logfile ""

# Memory management
maxmemory-policy allkeys-lru
221 changes: 221 additions & 0 deletions pmm_qa/valkey/valkey.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,221 @@
---
- name: Deploy Valkey Cluster with Sentinel
hosts: localhost
gather_facts: false
vars:
pmm_server_ip: "{{ lookup('vars', 'extra_pmm_server_ip', default=lookup('env','PMM_SERVER_IP') | default('127.0.0.1', true) ) }}"
metrics_mode: "{{ lookup('env', 'metrics_mode') }}"
client_version: "{{ lookup('vars', 'extra_client_version', default=lookup('env','CLIENT_VERSION') | default('3-dev-latest', true) ) }}"
admin_password: "{{ lookup('vars', 'extra_admin_password', default=lookup('env','ADMIN_PASSWORD') | default('admin', true) ) }}"
valkey_version_input: "{{ lookup('env', 'VALKEY_VERSION') | default('7', true) }}"
valkey_version_map:
'7': '7-alpine'
'8': '8-bookworm'
valkey_version: "{{ valkey_version_map[valkey_version_input] }}"
valkey_image: "valkey/valkey:{{ valkey_version }}"
valkey_network_name: "pmm-qa"
valkey_password: "VKvl41568AsE"
valkey_data_dir: "{{ lookup('env', 'HOME') }}/valkey/data"
valkey_config_dir: "{{ lookup('env', 'HOME') }}/valkey/config"
valkey_primary_port: 6379
valkey_replica_count: 2
valkey_replica_start_port: 6380
sentinel_count: 3
sentinel_start_port: 26379
sentinel_quorum: 2
random_number: "{{ lookup('pipe', 'shuf -i 1-99999 -n 1') }}"

pmm_server_name: "pmm-server"

tasks:
- name: Create Docker network
community.docker.docker_network:
name: "{{ valkey_network_name }}"
driver: bridge
state: present

- name: Create config directory
file:
path: "{{ valkey_config_dir }}"
state: directory
mode: '0755'

- name: Create a config directory per Sentinel
file:
path: "{{ valkey_config_dir }}/sentinel-{{ item }}"
state: directory
mode: '0755'
loop: "{{ range(1, sentinel_count + 1) | list }}"

- name: Create data directory
file:
path: "{{ valkey_data_dir }}"
state: directory
mode: '0755'

- name: Generate Valkey primary configuration
template:
src: valkey-primary.conf.j2
dest: "{{ valkey_config_dir }}/valkey-primary.conf"
mode: '0644'

- name: Generate Valkey replica configurations
template:
src: valkey-replica.conf.j2
dest: "{{ valkey_config_dir }}/valkey-replica-{{ item }}.conf"
mode: '0644'
loop: "{{ range(1, valkey_replica_count + 1) | list }}"

- name: Generate Sentinel configurations
template:
src: sentinel.conf.j2
dest: "{{ valkey_config_dir }}/sentinel-{{ item }}/sentinel.conf"
mode: '0664'
loop: "{{ range(1, sentinel_count + 1) | list }}"

- name: Create Docker volume for primary data
community.docker.docker_volume:
name: "valkey-primary-data"
state: present

- name: Create Docker volumes for replica data
community.docker.docker_volume:
name: "valkey-replica-{{ item }}-data"
state: present
loop: "{{ range(1, valkey_replica_count + 1) | list }}"

- name: Start Valkey primary container
community.docker.docker_container:
name: "valkey-primary"
image: "{{ valkey_image }}"
state: started
restart_policy: unless-stopped
networks:
- name: "{{ valkey_network_name }}"
ports:
- "{{ valkey_primary_port }}:6379"
volumes:
- "valkey-primary-data:/data"
- "{{ valkey_config_dir }}/valkey-primary.conf:/usr/local/etc/valkey/valkey.conf:ro"
command: ["valkey-server", "/usr/local/etc/valkey/valkey.conf"]
healthcheck:
test: ["CMD", "valkey-cli", "-a", "{{ valkey_password }}", "ping"]
interval: 10s
timeout: 5s
retries: 5

- name: Wait for the primary to be ready
wait_for:
host: localhost
port: "{{ valkey_primary_port }}"
timeout: 30
delay: 1

- name: Start Valkey replica containers
community.docker.docker_container:
name: "valkey-replica-{{ item }}"
image: "{{ valkey_image }}"
state: started
restart_policy: unless-stopped
networks:
- name: "{{ valkey_network_name }}"
ports:
- "{{ valkey_replica_start_port + item - 1 }}:6379"
volumes:
- "valkey-replica-{{ item }}-data:/data"
- "{{ valkey_config_dir }}/valkey-replica-{{ item }}.conf:/usr/local/etc/valkey/valkey.conf:ro"
command: ["valkey-server", "/usr/local/etc/valkey/valkey.conf"]
healthcheck:
test: ["CMD", "valkey-cli", "-a", "{{ valkey_password }}", "ping"]
interval: 10s
timeout: 5s
retries: 5
loop: "{{ range(1, valkey_replica_count + 1) | list }}"

- name: Wait for replicas to be ready
wait_for:
host: localhost
port: "{{ valkey_replica_start_port + item - 1 }}"
timeout: 30
delay: 1
loop: "{{ range(1, valkey_replica_count + 1) | list }}"

- name: Start Sentinel containers
community.docker.docker_container:
name: "sentinel-{{ item }}"
image: "{{ valkey_image }}"
state: started
restart_policy: unless-stopped
networks:
- name: "{{ valkey_network_name }}"
ports:
- "{{ sentinel_start_port + item - 1 }}:26379"
volumes:
- "{{ valkey_config_dir }}/sentinel-{{ item }}:/usr/local/etc/valkey"
command: ["valkey-sentinel", "/usr/local/etc/valkey/sentinel.conf"]
healthcheck:
test: ["CMD", "valkey-cli", "-p", "{{ sentinel_start_port }}", "ping"]
interval: 10s
timeout: 5s
retries: 5
loop: "{{ range(1, sentinel_count + 1) | list }}"

- name: Wait for Sentinels to be ready
wait_for:
host: localhost
port: "{{ sentinel_start_port + item - 1 }}"
timeout: 30
delay: 1
loop: "{{ range(1, sentinel_count + 1) | list }}"

- name: Verify cluster status
community.docker.docker_container_exec:
container: "valkey-primary"
command: valkey-cli -a "{{ valkey_password }}" info replication
register: cluster_status

- name: Display cluster status
debug:
msg: "{{ cluster_status.stdout_lines }}"

- name: Run Sentinel status command
community.docker.docker_container_exec:
container: "sentinel-1"
command: valkey-cli -p {{ sentinel_start_port }} sentinel masters
register: sentinel_status

- name: Display Sentinel status
debug:
msg: "{{ sentinel_status.stdout_lines }}"

- name: Install PMM Client in each container
ansible.builtin.include_tasks: ../tasks/install_pmm_client.yml
loop: >-
{{ ['valkey-primary']
+ (range(1, valkey_replica_count + 1) | map('string') | map('regex_replace', '^(.*)$', 'valkey-replica-\1') | list)
+ (range(1, sentinel_count + 1) | map('string') | map('regex_replace', '^(.*)$', 'sentinel-\1') | list)
}}
loop_control:
loop_var: current_container_name
vars:
container_name: "{{ current_container_name }}"

- name: Add the primary to monitoring
community.docker.docker_container_exec:
container: "valkey-primary"
command: pmm-admin add valkey --cluster=valkey-cluster --replication-set=valkey-repl --environment=valkey-test --username=default --password="{{ valkey_password }}" --service-name=valkey-primary-svc-{{ random_number }} --host=valkey-primary --port=6379 --custom-labels='role=primary'
ignore_errors: yes

- name: Add the replicas to monitoring
community.docker.docker_container_exec:
container: "valkey-replica-{{ item }}"
command: pmm-admin add valkey --cluster=valkey-cluster --replication-set=valkey-repl --environment=valkey-test --username=default --password="{{ valkey_password }}" --service-name=valkey-replica{{ item }}-svc-{{ random_number }} --host=valkey-replica-{{ item }} --port=6379 --custom-labels='role=replica'
loop: "{{ range(1, valkey_replica_count + 1) | list }}"
ignore_errors: yes

- name: Add Sentinels to monitoring
community.docker.docker_container_exec:
container: "sentinel-{{ item }}"
command: pmm-admin add valkey --cluster=valkey-cluster --environment=valkey-test --username=default --password="{{ valkey_password }}" --service-name=sentinel{{ item }}-svc-{{ random_number }} --host=sentinel-{{ item }} --port={{ sentinel_start_port }} --custom-labels='role=sentinel'
loop: "{{ range(1, sentinel_count + 1) | list }}"
ignore_errors: yes
Loading