Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Validation #157

Merged
merged 1 commit into from
Feb 1, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions .github/workflows/validation.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
name: Validation

on:
pull_request:
branches: [ master ]

env:
CI_RUST_TOOLCHAIN: 1.61.0

jobs:
becnmark:
name: validation
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- run: sudo bash ./.github/scripts/install_deps.sh
- uses: actions-rs/toolchain@v1
with:
profile: default
toolchain: ${{ env.CI_RUST_TOOLCHAIN }}
override: true
- run: RUSTFLAGS="-C target-feature=+crt-static" cargo build --release --bin lock_client --target x86_64-unknown-linux-gnu
- uses: actions-rs/cargo@v1
with:
command: build
args: --release
- run: |
cd scripts
cp ../target/release/{xline,benchmark} .
cp ../target/x86_64-unknown-linux-gnu/release/lock_client .
cp ../xline/tests/{private,public}.pem .
docker build . -t datenlord/xline:latest
docker pull gcr.io/etcd-development/etcd:v3.5.5
bash ./validation_test.sh
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ scripts/*
!scripts/benchmark.sh
!scripts/quick_start.sh
!scripts/coverage.sh
!scripts/validation_test.sh

coverage
*.profraw
Expand Down
3 changes: 2 additions & 1 deletion .mergify.yml
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
pull_request_rules:
- name: Automatic merge on approval
conditions:
- "#approved-reviews-by>=1"
- "#approved-reviews-by>=2"
- check-success=Outdated
- check-success=Audit
- check-success=Check
- check-success=Format
- check-success=Test
- check-success=Clippy
- check-success=Coverage
- check-success=Validation
actions:
merge:
method: rebase
13 changes: 9 additions & 4 deletions scripts/quick_start.sh
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
#!/bin/bash
WORKDIR=$(pwd)
DIR=$(
cd "$(dirname "$0")"
pwd
)
SERVERS=("172.20.0.2" "172.20.0.3" "172.20.0.4" "172.20.0.5")
MEMBERS="node1=${SERVERS[1]}:2379,node2=${SERVERS[2]}:2379,node3=${SERVERS[3]}:2379"

Expand All @@ -9,7 +12,9 @@ MEMBERS="node1=${SERVERS[1]}:2379,node2=${SERVERS[2]}:2379,node3=${SERVERS[3]}:2
run_xline() {
cmd="/usr/local/bin/xline \
--name node${1} \
--members ${MEMBERS}"
--members ${MEMBERS} \
--auth-public-key /mnt/public.pem \
--auth-private-key /mnt/private.pem"

if [ ${1} -eq 1 ]; then
cmd="${cmd} --is-leader"
Expand Down Expand Up @@ -45,9 +50,9 @@ run_container() {
size=${1}
image="datenlord/xline:latest"
for ((i = 1; i <= ${size}; i++)); do
docker run -d -it --rm --name=node${i} --net=xline_net --ip=172.20.0.$((i + 2)) --cap-add=NET_ADMIN --cpu-shares=1024 -m=512M -v ${WORKDIR}:/mnt ${image} bash &
docker run -d -it --rm --name=node${i} --net=xline_net --ip=172.20.0.$((i + 2)) --cap-add=NET_ADMIN --cpu-shares=1024 -m=512M -v ${DIR}:/mnt ${image} bash &
done
docker run -d -it --rm --name=node4 --net=xline_net --ip=172.20.0.2 --cap-add=NET_ADMIN --cpu-shares=1024 -m=512M -v ${WORKDIR}:/mnt gcr.io/etcd-development/etcd:v3.5.5 bash &
docker run -d -it --rm --name=node4 --net=xline_net --ip=172.20.0.2 --cap-add=NET_ADMIN --cpu-shares=1024 -m=512M -v ${DIR}:/mnt gcr.io/etcd-development/etcd:v3.5.5 bash &
wait
echo container started
}
Expand Down
164 changes: 164 additions & 0 deletions scripts/validation_test.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
DIR="$(dirname $0)"
QUICK_START="${DIR}/quick_start.sh"
bash ${QUICK_START}
ETCDCTL="docker exec -i node4 etcdctl --endpoints=http://172.20.0.3:2379"
LOCK_CLIENT="docker exec -i node4 /mnt/lock_client --endpoints=http://172.20.0.3:2379"

# run a command with expect output
# args:
# $1: command to run
# $2: expect output
run_with_expect() {
cmd="${1}"
res=$(eval ${cmd})
# echo -e "res:\n${res}\n"
expect=$(echo -e ${2})
if [ "${res}" == "${expect}" ]; then
echo "command run success"
else
echo "command run failed"
echo "command: ${cmd}"
echo "expect: ${expect}"
echo "result: ${res}"
exit 1
fi
}

# validate kv requests
kv_validation() {
echo "kv validation test running..."
run_with_expect "${ETCDCTL} put key1 value1" "OK"
stdin='value(\"key1\") = \"value1\"\n\nput key2 success\n\nput key2 failure\n'
run_with_expect "echo -e \"${stdin}\" | ${ETCDCTL} txn" "SUCCESS\n\nOK"
run_with_expect "${ETCDCTL} get key2" "key2\nsuccess"
run_with_expect "${ETCDCTL} del key1" "1"
# TODO compact
echo "kv validation test passed"
}

# validate watch requests
watch_validation() {
echo "watch validation test running..."
command="${ETCDCTL} watch watch_key"
want=("PUT" "watch_key" "value" "DELETE" "watch_key" "value" "watch_key")
${command} | while read line; do
if [ "${line}" == "${want[0]}" ]; then
unset want[0]
want=("${want[@]}")
else
echo "command run failed"
echo "command: ${command}"
echo "expect: ${want[0]}"
echo "result: ${line}"
exit 1
fi
done &
sleep 0.1 # wait watch
run_with_expect "${ETCDCTL} put watch_key value" "OK"
run_with_expect "${ETCDCTL} del watch_key" "1"
echo "watch validation test passed"
}

# validate lease requests
lease_validation() {
echo "lease validation test running..."
command="${ETCDCTL} lease grant 5"
out=$(${command})
patten='lease [0-9a-z]+ granted with TTL\([0-9]+s\)'
if [[ ${out} =~ ${patten} ]]; then
echo "command run success"
else
echo "command run failed"
echo "command: ${command}"
echo "expect: ${patten}"
echo "result: ${out}"
exit 1
fi
lease_id=$(echo ${out} | awk '{print $2}')
run_with_expect "${ETCDCTL} lease list" "found 1 leases\n${lease_id}"
command="${ETCDCTL} lease keep-alive ${lease_id}"
${command} | while read line; do

if [ "${line}" != "lease ${lease_id} keepalived with TTL(5)" ]; then
if [ "${line}" != "lease ${lease_id} expired or revoked." ]; then
echo "command run failed"
echo "command: ${command}"
echo "expect: lease ${lease_id} keepalived with TTL(5) or lease ${lease_id} expired or revoked."
echo "result: ${line}"
exit 1
fi
fi
done &
run_with_expect "${ETCDCTL} put key1 value1 --lease=${lease_id}" "OK"
run_with_expect "${ETCDCTL} get key1" "key1\nvalue1"
command="${ETCDCTL} lease timetolive ${lease_id}"
remaining=$(${command} | sed -r "s/lease ${lease_id} granted with TTL\(5s\), remaining\(([0-9]+)s\)/\1/g")
if [ ${remaining} -le 5 ]; then
echo "command run success"
else
echo "command run failed"
echo "command: ${command}"
echo "remaining should be less than 5, but got ${remaining}"
exit 1
fi

run_with_expect "${ETCDCTL} lease revoke ${lease_id}" "lease ${lease_id} revoked"
run_with_expect "${ETCDCTL} get key1" ""
echo "lease validation test passed"
}

# validate auth requests
auth_validation() {
echo "auth validation test running..."
run_with_expect "${ETCDCTL} user add root:root" "User root created"
run_with_expect "${ETCDCTL} role add root" "Role root created"
run_with_expect "${ETCDCTL} user grant-role root root" "Role root is granted to user root"
run_with_expect "${ETCDCTL} auth enable" "Authentication Enabled"
run_with_expect "${ETCDCTL} --user root:root auth status" "Authentication Status: true\nAuthRevision: 5"
run_with_expect "${ETCDCTL} --user root:root user add u:u" "User u created"
run_with_expect "${ETCDCTL} --user root:root role add r" "Role r created"
run_with_expect "${ETCDCTL} --user root:root user grant-role u r" "Role r is granted to user u"
run_with_expect "${ETCDCTL} --user root:root role grant-permission r readwrite key1" "Role r updated"
run_with_expect "${ETCDCTL} --user u:u put key1 value1" "OK"
run_with_expect "${ETCDCTL} --user u:u get key1" "key1\nvalue1"
run_with_expect "${ETCDCTL} --user u:u role get r" "Role r\nKV Read:\n\tkey1\nKV Write:\n\tkey1"
run_with_expect "${ETCDCTL} --user u:u user get u" "User: u\nRoles: r"
run_with_expect "echo 'new_password' | ${ETCDCTL} --user root:root user passwd --interactive=false u" "Password updated"
run_with_expect "${ETCDCTL} --user root:root role revoke-permission r key1" "Permission of key key1 is revoked from role r"
run_with_expect "${ETCDCTL} --user root:root user revoke-role u r" "Role r is revoked from user u"
run_with_expect "${ETCDCTL} --user root:root user list" "root\nu"
run_with_expect "${ETCDCTL} --user root:root role list" "r\nroot"
run_with_expect "${ETCDCTL} --user root:root user delete u" "User u deleted"
run_with_expect "${ETCDCTL} --user root:root role delete r" "Role r deleted"
run_with_expect "${ETCDCTL} --user root:root auth disable" "Authentication Disabled"
echo "auth validation test passed"
}

lock_validation() {
echo "lock validation test running..."
run_with_expect "${ETCDCTL} lock mutex echo success" "success"
echo "lock validation test passed"
}

lock_rpc_validation() {
echo "lock rpc validation test running..."
command="${LOCK_CLIENT} lock mutex"
key=$(${command})
if [[ ${key} == mutex* ]]; then
echo "command run success"
else
echo "command run failed"
echo "command: ${command}"
echo "expect: mutex*"
echo "result: ${key}"
fi
run_with_expect "${LOCK_CLIENT} unlock ${key}" "unlock success"
echo "lock rpc validation test passed"
}

kv_validation
watch_validation
lease_validation
auth_validation
lock_validation
lock_rpc_validation
81 changes: 81 additions & 0 deletions xline/src/bin/lock_client.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
//! this binary is only used for the validation of lock service

use clap::{Parser, Subcommand};
use etcd_client::Client;

#[derive(Parser, Debug, Clone, PartialEq, Eq)]
struct ClientArgs {
#[clap(short, long, value_delimiter = ',')]
endpoints: Vec<String>,
#[clap(subcommand)]
command: Commands,
}

/// Types of sub command
#[derive(Subcommand, Debug, Clone, PartialEq, Eq)]
pub enum Commands {
/// Lock args
Lock {
/// Lock name
#[clap(value_parser)]
name: String,
},
/// UnLock args
Unlock {
/// Lock name
#[clap(value_parser)]
key: String,
},
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let args: ClientArgs = ClientArgs::parse();
let endpoints = if args.endpoints.is_empty() {
vec!["http://127.0.0.1:2379".to_owned()]
} else {
args.endpoints
};
let mut client = Client::connect(endpoints, None).await?;
match args.command {
Commands::Lock { name } => {
let lock_res = client.lock(name, None).await?;
println!("{}", String::from_utf8_lossy(lock_res.key()))
}
Commands::Unlock { key } => {
let _unlock_res = client.unlock(key).await?;
println!("unlock success");
}
};
Ok(())
}

#[cfg(test)]
mod test {
use super::*;
#[test]
fn it_works() {
let args: ClientArgs = ClientArgs::parse_from(&[
"lock_client",
"--endpoints",
"http://127.0.0.1:1234",
"lock",
"test",
]);
assert_eq!(args.endpoints, vec!["http://127.0.0.1:1234"]);
assert_eq!(
args.command,
Commands::Lock {
name: "test".to_owned()
}
);
let args2: ClientArgs = ClientArgs::parse_from(&["lock_client", "unlock", "test"]);
assert_eq!(args2.endpoints, Vec::<String>::new());
assert_eq!(
args2.command,
Commands::Unlock {
key: "test".to_owned()
}
);
}
}