Daily test using Terraform (#29)

1. Use terraform automate buy instance and run test
2. Support Aliyun and Opensource Redis-Like Database
This commit is contained in:
Kitty 2024-10-29 13:56:27 +08:00 committed by GitHub
parent e3bb089970
commit e13c4b9e96
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 589 additions and 58 deletions

79
.github/workflows/terraform.yaml vendored Normal file
View File

@ -0,0 +1,79 @@
name: Terraform in Daily Test
on:
schedule:
- cron: '0 18 * * *' # 每天 UTC 时间 18:00即中国时间凌晨 2 点
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Check GitHub Secrets
run: |
if [ -z "${{ secrets.MYAPP_GITHUB_TOKEN }}" ]; then
echo "GitHub Token is missing"
else
echo "GitHub Token is set"
fi
if [ -z "${{ secrets.MYAPP_USER_EMAIL }}" ]; then
echo "User Email is missing"
else
echo "User Email is set"
fi
if [ -z "${{ secrets.MYAPP_USER_NAME }}" ]; then
echo "User Name is missing"
else
echo "User Name is set"
fi
- name: Checkout repository
uses: actions/checkout@v2
with:
ref: dailyTest
- name: Setup Terraform
uses: hashicorp/setup-terraform@v2
with:
terraform_version: 1.9.4
- name: Initialize Terraform
env:
TF_VAR_access_key: ${{ secrets.ALI_ACCESS_KEY }}
TF_VAR_secret_key: ${{ secrets.ALI_SECRET_KEY }}
TF_VAR_github_token: ${{ secrets.MYAPP_GITHUB_TOKEN }}
TF_VAR_user_name: ${{ secrets.MYAPP_USER_NAME }}
TF_VAR_user_email: ${{ secrets.MYAPP_USER_EMAIL }}
run: terraform -chdir=./Terraform/Aliyun init
- name: Apply Terraform Configuration
env:
TF_VAR_access_key: ${{ secrets.ALI_ACCESS_KEY }}
TF_VAR_secret_key: ${{ secrets.ALI_SECRET_KEY }}
TF_VAR_github_token: ${{ secrets.MYAPP_GITHUB_TOKEN }}
TF_VAR_user_name: ${{ secrets.MYAPP_USER_NAME }}
TF_VAR_user_email: ${{ secrets.MYAPP_USER_EMAIL }}
run: terraform -chdir=./Terraform/Aliyun apply -auto-approve
- name: Print current directory and file list
run: |
cd ./Terraform/Aliyun
echo "Current directory:"
pwd
echo "Files in the current directory:"
ls
- name: Wait for 20 minutes before destroying resources
run: sleep 1200 # 等待 20 分钟
- name: Destroy Terraform Configuration
env:
TF_VAR_access_key: ${{ secrets.ALI_ACCESS_KEY }}
TF_VAR_secret_key: ${{ secrets.ALI_SECRET_KEY }}
TF_VAR_github_token: ${{ secrets.MYAPP_GITHUB_TOKEN }}
TF_VAR_user_name: ${{ secrets.MYAPP_USER_NAME }}
TF_VAR_user_email: ${{ secrets.MYAPP_USER_EMAIL }}
run: terraform -chdir=./Terraform/Aliyun destroy -auto-approve

338
Terraform/Aliyun/main.tf Normal file
View File

@ -0,0 +1,338 @@
variable "access_key" {
description = "Access key for Alicloud provider"
type = string
}
variable "secret_key" {
description = "Secret key for Alicloud provider"
type = string
}
variable "github_token" {
description = "GitHub token for accessing GitHub API"
type = string
}
variable "user_name" {
description = "GitHub user name"
type = string
}
variable "user_email" {
description = "GitHub user email"
type = string
}
locals {
selected_zone_id = "cn-hongkong-b"
}
provider "alicloud" {
access_key = var.access_key
secret_key = var.secret_key
region = "cn-hongkong"
}
# VPC
resource "alicloud_vpc" "my_vpc" {
vpc_name = "MyVPC"
cidr_block = "172.16.0.0/24"
}
# VSwitch
resource "alicloud_vswitch" "my_vswitch" {
vswitch_name = "glcc_vswitch"
vpc_id = alicloud_vpc.my_vpc.id
cidr_block = "172.16.0.0/24"
zone_id = local.selected_zone_id
}
resource "alicloud_security_group" "my_sg" {
name = "glcc_test_security_group"
vpc_id = alicloud_vpc.my_vpc.id
description = "Security Group for testing"
}
resource "alicloud_security_group_rule" "allow_ssh" {
security_group_id = alicloud_security_group.my_sg.id
type = "ingress"
ip_protocol = "tcp"
nic_type = "intranet"
policy = "accept"
port_range = "22/22"
priority = 1
cidr_ip = "0.0.0.0/0"
}
resource "alicloud_security_group_rule" "allow_http" {
security_group_id = alicloud_security_group.my_sg.id
type = "ingress"
ip_protocol = "tcp"
nic_type = "intranet"
policy = "accept"
port_range = "80/80"
priority = 100
cidr_ip = "0.0.0.0/0"
}
resource "alicloud_security_group_rule" "allow_https_outbound" {
type = "egress"
security_group_id = alicloud_security_group.my_sg.id
ip_protocol = "tcp"
nic_type = "intranet"
policy = "accept"
port_range = "443/443"
cidr_ip = "0.0.0.0/0"
description = "Allow outbound HTTPS traffic to external services"
}
resource "alicloud_security_group_rule" "allow_redis" {
type = "ingress"
security_group_id = alicloud_security_group.my_sg.id
ip_protocol = "tcp"
nic_type = "intranet"
policy = "accept"
port_range = "6379/17005"
cidr_ip = "172.16.0.10/16" # ECS IP 访
description = "Allow inbound Redis traffic"
}
# 1GB Tair 6.0
resource "alicloud_kvstore_instance" "my_tair_standard" {
db_instance_name = "glcc_tair_standard"
instance_class = "tair.rdb.1g"
instance_type = "Redis"
engine_version = "7.0"
zone_id = local.selected_zone_id
vswitch_id = alicloud_vswitch.my_vswitch.id
payment_type = "PostPaid"
password = "T123456@*"
security_ips = ["172.16.0.10"]
}
# 1GB Tair 6.0
resource "alicloud_kvstore_instance" "my_tair_cluster" {
db_instance_name = "glcc_tair_cluster"
instance_class = "tair.rdb.with.proxy.1g"
instance_type = "Redis"
engine_version = "7.0"
shard_count = "8"
zone_id = local.selected_zone_id
vswitch_id = alicloud_vswitch.my_vswitch.id
payment_type = "PostPaid"
password = "T123456@*"
security_ips = ["172.16.0.10"]
}
# 1GB Redis 6.0
resource "alicloud_kvstore_instance" "my_redis_standard" {
db_instance_name = "glcc_redis_standard"
instance_class = "redis.shard.small.2.ce"
instance_type = "Redis"
engine_version = "7.0"
zone_id = local.selected_zone_id
vswitch_id = alicloud_vswitch.my_vswitch.id
payment_type = "PostPaid"
password = "T123456@*"
security_ips = ["172.16.0.10"]
}
# 1GB Redis 6.0
resource "alicloud_kvstore_instance" "my_redis_cluster" {
db_instance_name = "glcc_redis_cluster"
instance_class = "redis.shard.with.proxy.small.ce"
instance_type = "Redis"
engine_version = "7.0"
shard_count = "8"
zone_id = local.selected_zone_id
vswitch_id = alicloud_vswitch.my_vswitch.id
payment_type = "PostPaid"
password = "T123456@*"
security_ips = ["172.16.0.10"]
}
# ,
output "tair_standard_instance_address" {
value = alicloud_kvstore_instance.my_tair_standard.connection_domain
}
output "tair_standard_instance_port" {
value = alicloud_kvstore_instance.my_tair_standard.private_connection_port
}
output "tair_standard_instance_password" {
value = alicloud_kvstore_instance.my_tair_standard.password
sensitive = true
}
output "tair_cluster_instance_address" {
value = alicloud_kvstore_instance.my_tair_cluster.connection_domain
}
output "tair_cluster_instance_port" {
value = alicloud_kvstore_instance.my_tair_cluster.private_connection_port
}
output "tair_cluster_instance_password" {
value = alicloud_kvstore_instance.my_tair_cluster.password
sensitive = true
}
output "redis_standard_instance_address" {
value = alicloud_kvstore_instance.my_redis_standard.connection_domain
}
output "redis_standard_instance_port" {
value = alicloud_kvstore_instance.my_redis_standard.private_connection_port
}
output "redis_standard_instance_password" {
value = alicloud_kvstore_instance.my_redis_standard.password
sensitive = true
}
output "redis_cluster_instance_address" {
value = alicloud_kvstore_instance.my_redis_cluster.connection_domain
}
output "redis_cluster_instance_port" {
value = alicloud_kvstore_instance.my_redis_cluster.private_connection_port
}
output "redis_cluster_instance_password" {
value = alicloud_kvstore_instance.my_redis_cluster.password
sensitive = true
}
# ECS
resource "alicloud_instance" "my_ecs" {
private_ip = "172.16.0.10"
instance_type = "ecs.g6.xlarge"
security_groups = [alicloud_security_group.my_sg.id]
instance_charge_type = "PostPaid"
internet_charge_type = "PayByTraffic"
internet_max_bandwidth_out = 10
image_id = "ubuntu_22_04_x64_20G_alibase_20240130.vhd"
instance_name = "glcc_ecs"
vswitch_id = alicloud_vswitch.my_vswitch.id
system_disk_category = "cloud_efficiency"
password = "T123456@*"
lifecycle {
create_before_destroy = true
}
user_data = <<EOF
#!/bin/bash
export HOME=/root
apt-get update
apt-get install -y python3-pip git docker.io
systemctl start docker
pip install "pyyaml>=6.0"
git config --global credential.helper 'store'
source /etc/profile
#
cat <<EOT >> /root/db_config.yml
AliyunTair:
host: ${alicloud_kvstore_instance.my_tair_standard.connection_domain}
port: ${alicloud_kvstore_instance.my_tair_standard.private_connection_port}
password: T123456@*
ssl: false
cluster: false
version: 7.0
AliyunTairCluster:
host: ${alicloud_kvstore_instance.my_tair_cluster.connection_domain}
port: ${alicloud_kvstore_instance.my_tair_cluster.private_connection_port}
password: T123456@*
ssl: false
cluster: true
version: 7.0
AliyunRedis:
host: ${alicloud_kvstore_instance.my_redis_standard.connection_domain}
port: ${alicloud_kvstore_instance.my_redis_standard.private_connection_port}
password: T123456@*
ssl: false
cluster: false
version: 7.0
AliyunRedisCluster:
host: ${alicloud_kvstore_instance.my_redis_cluster.connection_domain}
port: ${alicloud_kvstore_instance.my_redis_cluster.private_connection_port}
password: T123456@*
ssl: false
cluster: true
version: 7.0
EOT
#
docker pull docker.io/redis:latest
docker run --name redis -d -p 6379:6379 redis
docker pull docker.dragonflydb.io/dragonflydb/dragonfly:latest
docker run --name dragonflydb -d -p 6380:6379 docker.dragonflydb.io/dragonflydb/dragonfly
docker pull apache/kvrocks:latest
docker run --name kvrocks -d -p 6381:6666 apache/kvrocks
docker pull docker.io/eqalpha/keydb:latest
docker run --name keydb -d -p 6382:6379 eqalpha/keydb
docker pull docker.io/pikadb/pika:latest
docker run --name pika -d -p 6383:6383 pikadb/pika
docker pull valkey/valkey:latest
docker run --name valkey -d -p 6384:6379 valkey/valkey
# Git
echo "https://${var.github_token}:x-oauth-basic@github.com" > ~/.git-credentials
git config --global user.name "${var.user_name}"
git config --global user.email "${var.user_email}"
git clone https://github.com/redis/redis.git
cd redis
make -j
cd utils/create-cluster
./create-cluster start
yes yes | ./create-cluster create
# dailyTest分支仓库的url
REPO_URL="https://github.com/tair-opensource/resp-compatibility.git"
RETRY_COUNT=0
MAX_RETRIES=10
SLEEP_DURATION=30
while [ $RETRY_COUNT -lt $MAX_RETRIES ]; do
if git clone $REPO_URL; then
echo "Git clone succeeded"
break
else
RETRY_COUNT=$((RETRY_COUNT + 1))
echo "Git clone failed, attempt $RETRY_COUNT/$MAX_RETRIES"
sleep $SLEEP_DURATION
fi
done
if [ $RETRY_COUNT -eq $MAX_RETRIES ]; then
echo "Git clone failed after $MAX_RETRIES attempts" >&2
exit 1
fi
cd resp-compatibility
git checkout dailyTest
python3 conn.py
EOF
}
output "ecs_ip_address" {
value = alicloud_instance.my_ecs.private_ip
}

View File

@ -14,31 +14,39 @@ SpecificVersion:
# the host, port, password of all the db to be tested
Database:
Redis:
host: 127.0.0.1
host: 172.16.0.10
port: 6379
password:
ssl: false
cluster: false
version: unstable
version: latest
DragonflyDB:
RedisCluster:
host: 127.0.0.1
port: 6380
port: 30001
password:
ssl: false
cluster: true
version: latest
Valkey:
host: 172.16.0.10
port: 6384
password:
ssl: false
cluster: false
version: latest
Kvrocks:
host: 127.0.0.1
host: 172.16.0.10
port: 6381
password:
ssl: false
cluster: false
version: 2.2.0
version: latest
KeyDB:
host: 127.0.0.1
host: 172.16.0.10
port: 6382
password:
ssl: false
@ -46,29 +54,13 @@ Database:
version: latest
Pika:
host: 127.0.0.1
port: 6383
host: 172.17.0.6
port: 9221
password:
ssl: false
cluster: false
version: latest
Tair:
host:
port:
password:
ssl:
cluster: false
version:
Kvstore:
host:
port:
password:
ssl:
cluster: false
version:
ElastiCache:
host:
port:

102
conn.py Normal file
View File

@ -0,0 +1,102 @@
import os
import threading
import time
import yaml
import subprocess
# 更新配置文件
try:
yml_file_path = '/root/db_config.yml'
config_file_path = 'config.yaml'
with open(yml_file_path, 'r') as yml_file, open(config_file_path, 'r') as config_file:
yml_data = yaml.safe_load(yml_file)
config_data = yaml.safe_load(config_file)
for db_name, db_config in yml_data.items():
if db_name in config_data['Database']:
for key, value in db_config.items():
config_data['Database'][db_name][key] = value
else:
config_data['Database'][db_name] = db_config
with open(config_file_path, 'w') as config_file:
yaml.dump(config_data, config_file, default_flow_style=False)
print("Config file updated successfully.")
except FileNotFoundError as e:
print(f"Error: {e}. Please check the file paths.")
except yaml.YAMLError as e:
print(f"Error in YAML processing: {e}")
except Exception as e:
print(f"An unexpected error occurred: {e}")
# 执行命令并判断是否成功
def execute_command(commands):
for command in commands:
result = subprocess.run(command, shell=True, capture_output=True, text=True)
if result.returncode != 0:
print(f"Error executing command '{command}': {result.stderr}")
return False
else:
print(f"Successfully executed command '{command}': {result.stdout}")
return True
commands = [
"apt-get update",
"apt-get install -y python3-pip",
]
if not execute_command(commands):
print("Failed to update or install packages. Exiting...")
exit(1)
# 运行测试命令
run_test_command = [
"pip3 install -r requirements.txt",
"python3 resp_compatibility.py --testfile cts.json --genhtml --show-failed",
]
def run_test():
if not execute_command(run_test_command):
print("Test failed. Exiting...")
exit(1)
else:
print("Test completed successfully.")
# 启动测试脚本的线程
test_thread = threading.Thread(target=run_test)
test_thread.start()
time.sleep(300)
# 提交和推送测试结果
commit_and_push_commands = [
"mv html /root",
"git stash -u",
"git checkout gh-pages || git checkout -b gh-pages",
"git pull origin gh-pages",
"cp -r /root/html/* .",
"git add .",
"git commit -m 'Update test results'",
]
if not execute_command(commit_and_push_commands):
print("Failed to commit and push changes. Exiting...")
exit(1)
# 推送到 GitHub 并重试
def git_push_with_retry():
while True:
result = subprocess.run("git push -u origin gh-pages", shell=True, capture_output=True, text=True)
if result.returncode == 0:
print("Successfully pushed to GitHub.")
break
else:
print(f"Git push failed: {result.stderr}. Retrying in 5 seconds...")
time.sleep(5)
git_push_with_retry()

View File

@ -220,6 +220,20 @@ def generate_html_report(logdir, configs):
html.write("This page is automatically generated by <a href=\"https://github.com/tair-opensource/"
"compatibility-test-suite-for-redis\">compatibility-test-suite-for-redis</a> "
"to show the compatibility of the following Redis-Like systems and different versions of Redis.<br><br>")
# Separate databases into cluster and standalone
cluster_databases = []
standalone_databases = []
for config in configs['Database']:
if configs['Database'][config]['cluster']:
cluster_databases.append(config)
else:
standalone_databases.append(config)
# Function to generate a table
def generate_table(databases, title):
html.write(f"<h3>{title}</h3>")
html.write("<table>")
# generate header
html.write("<thead>")
@ -231,7 +245,7 @@ def generate_html_report(logdir, configs):
html.write("</thead>")
# generate body
html.write("<tbody>")
for config in configs['Database']:
for config in databases:
html.write("<tr>")
html.write(f"<td>{config}({configs['Database'][config]['version']})</td>")
for version in configs['SpecificVersion']:
@ -252,8 +266,14 @@ def generate_html_report(logdir, configs):
html.write(f"<td style=\"background:{color}\">{rate}% <a href=\"{config}-{version}.html\">detail</a></td>")
html.write("</tr>")
html.write("</tbody>")
html.write("</table>")
html.write("<br>")
html.write("</table><br>")
# Generate standalone table
generate_table(standalone_databases, "Standalone Databases")
# Generate cluster table
generate_table(cluster_databases, "Cluster Databases")
time = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
html.write(f"This report was generated on {time}.")
html.write("<style>table {border-collapse: collapse;} th, td {border: 1px solid black; padding: 8px;}</style>")