0
Fork 0
mirror of https://github.com/project-zot/zot.git synced 2024-12-16 21:56:37 -05:00
zot/test/scale-out/helpers_zot.bash
Vishwas R 5ae7a028d9
feat(cluster): Add support for request proxying for scale out (#2385)
* feat(cluster): initial commit for scale-out cluster

Signed-off-by: Ramkumar Chinchani <rchincha@cisco.com>

* feat(cluster): support shared storage scale out

This change introduces support for shared storage backed
zot cluster scale out.

New feature
Multiple stateless zot instances can run using the same shared
storage backend where each instance looks at a specific set
of repositories based on a siphash of the repository name to improve
scale as the load is distributed across multiple instances.
For a given config, there will only be one instance that can perform
dist-spec read/write on a given repository.

What's changed?
- introduced a transparent request proxy for dist-spec endpoints based on
siphash of repository name.
- new config for scale out cluster that specifies list of
cluster members.

Signed-off-by: Vishwas Rajashekar <vrajashe@cisco.com>

---------

Signed-off-by: Ramkumar Chinchani <rchincha@cisco.com>
Signed-off-by: Vishwas Rajashekar <vrajashe@cisco.com>
Co-authored-by: Ramkumar Chinchani <rchincha@cisco.com>
2024-05-20 09:05:21 -07:00

273 lines
8 KiB
Bash

ROOT_DIR=$(git rev-parse --show-toplevel)
OS=$(go env GOOS)
ARCH=$(go env GOARCH)
ZOT_PATH=${ROOT_DIR}/bin/zot-${OS}-${ARCH}
ZLI_PATH=${ROOT_DIR}/bin/zli-${OS}-${ARCH}
ZOT_MINIMAL_PATH=${ROOT_DIR}/bin/zot-${OS}-${ARCH}-minimal
# basic auth
ZOT_AUTH_USER=poweruser
ZOT_AUTH_PASS=sup*rSecr9T
ZOT_CREDS_PATH="${BATS_FILE_TMPDIR}/creds"
ZOT_HTPASSWD_PATH="${ZOT_CREDS_PATH}/htpasswd"
# zb
ZB_PATH=${ROOT_DIR}/bin/zb-${OS}-${ARCH}
ZB_RESULTS_PATH=${ROOT_DIR}/zb-results
ZB_CI_CD_OUTPUT_FILE=${ROOT_DIR}/ci-cd.json
# zot scale out cluster
ZOT_CLUSTER_MEMBERS_PATCH_FILE="${BATS_FILE_TMPDIR}/members-patch.json"
ZOT_ROOT_DIR="${BATS_FILE_TMPDIR}/zot"
ZOT_TLS_CERT_PATH="${ROOT_DIR}/test/data/server.cert"
ZOT_TLS_KEY_PATH="${ROOT_DIR}/test/data/server.key"
ZOT_TLS_CA_CERT_PATH="${ROOT_DIR}/test/data/ca.crt"
function verify_prerequisites {
if [ ! -f ${ZOT_PATH} ]; then
echo "you need to build ${ZOT_PATH} before running the tests" >&3
return 1
fi
if [ ! -f ${ZB_PATH} ]; then
echo "you need to build ${ZB_PATH} before running the tests" >&3
return 1
fi
if [ ! $(command -v skopeo) ]; then
echo "you need to install skopeo as a prerequisite to running the tests" >&3
return 1
fi
if [ ! $(command -v awslocal) ] &>/dev/null; then
echo "you need to install aws cli as a prerequisite to running the tests" >&3
return 1
fi
if [ ! $(command -v haproxy) ] &>/dev/null; then
echo "you need to install haproxy as a prerequisite to running the tests" >&3
return 1
fi
return 0
}
function get_free_port(){
while true
do
random_port=$(( ((RANDOM<<15)|RANDOM) % 49152 + 10000 ))
status="$(nc -z 127.0.0.1 $random_port < /dev/null &>/dev/null; echo $?)"
if [ "${status}" != "0" ]; then
free_port=${random_port};
break;
fi
done
echo ${free_port}
}
function zot_serve() {
local config_file=${1}
${ZOT_PATH} serve ${config_file} &
}
# stops all zot instances started by the test
function zot_stop_all() {
pkill zot
}
# waits for the zot server to be reachable
# leaving argument 2 blank or specifying "http" causes the function to use HTTP
# specifying "https" for argument 2 causes the function to use TLS
function wait_zot_reachable() {
local zot_port=${1}
local protocol=${2}
if [ -z "${protocol}" ]; then
protocol="http"
fi
local zot_url="${protocol}://127.0.0.1:${zot_port}/v2/_catalog"
local curl_opts=(
--connect-timeout 3
--max-time 5
--retry 20
--retry-delay 1
--retry-max-time 180
--retry-connrefused
)
# since this is only a reachability check, we can disable cert verification
if [ "${protocol}" == "https" ]; then
curl_opts=(--insecure "${curl_opts[@]}")
fi
curl "${curl_opts[@]}" ${zot_url}
}
function zb_run() {
local test_name=${1}
local zot_address=${2}
local concurrent_reqs=${3}
local num_requests=${4}
local credentials=${5}
if [ ! -d "${ZB_RESULTS_PATH}" ]; then
mkdir -p "${ZB_RESULTS_PATH}"
fi
local zb_args=(
-c ${concurrent_reqs}
-n ${num_requests}
--src-cidr 127.0.10.0/24
-o ci-cd
--skip-cleanup
)
if [ ! -z "${credentials}" ]; then
zb_args=(-A ${credentials} "${zb_args[@]}")
fi
start=$(date +%s)
${ZB_PATH} "${zb_args[@]}" ${zot_address}
stop=$(date +%s)
runtime=$((stop-start))
echo "Duration: ${runtime} seconds" >&3
if [ -f "${ZB_CI_CD_OUTPUT_FILE}" ]; then
mv "${ZB_CI_CD_OUTPUT_FILE}" "${ZB_RESULTS_PATH}/${test_name}-results.json"
fi
}
function setup_local_htpasswd() {
create_htpasswd_file "${ZOT_CREDS_PATH}" "${ZOT_HTPASSWD_PATH}" ${ZOT_AUTH_USER} ${ZOT_AUTH_PASS}
}
function create_htpasswd_file() {
local creds_dir_path="${1}"
local htpasswd_file_path="${2}"
local user=${3}
local password=${4}
mkdir -p "${creds_dir_path}"
htpasswd -b -c -B "${htpasswd_file_path}" ${user} ${password}
}
# given the number of zot instances, computes a list of cluster members
# and saves them as a JSON to a file that can be used with jq later.
function generate_zot_cluster_member_list() {
local num_zot_instances=${1}
local patch_file_path=${2}
local temp_file="${BATS_FILE_TMPDIR}/jq-member-dump.json"
echo "{\"cluster\":{\"members\":[]}}" > ${patch_file_path}
for ((i=0;i<${num_zot_instances};i++)); do
local member="127.0.0.1:$(( 10000 + $i ))"
jq ".cluster.members += [\"${member}\"]" ${patch_file_path} > ${temp_file} && \
mv ${temp_file} ${patch_file_path}
done
echo "cluster members patch file" >&3
cat ${patch_file_path} >&3
}
# patches an existing zot config file to add all the cluster members.
function update_zot_cluster_member_list_in_config_file() {
local zot_config_file=${1}
local zot_members_patch_file=${2}
local temp_file="${BATS_FILE_TMPDIR}/jq-mem-update-dump.json"
jq -s '.[0] * .[1]' ${zot_config_file} ${zot_members_patch_file} > ${temp_file} && \
mv ${temp_file} ${zot_config_file}
}
# generates and saves a base cloud config with shared storage
# given some basic parameters about the zot instance.
function create_zot_cloud_base_config_file() {
local zot_server_address=${1}
local zot_server_port=${2}
local zot_root_dir="${3}"
local zot_config_file="${4}"
local zot_log_file="${5}"
cat > ${zot_config_file}<<EOF
{
"distSpecVersion": "1.1.0",
"storage": {
"rootDirectory": "${zot_root_dir}",
"dedupe": false,
"remoteCache": true,
"storageDriver": {
"name": "s3",
"rootdirectory": "/zot",
"region": "us-east-2",
"regionendpoint": "localhost:4566",
"bucket": "zot-storage-test",
"secure": false,
"skipverify": false
},
"cacheDriver": {
"name": "dynamodb",
"endpoint": "http://localhost:4566",
"region": "us-east-2",
"cacheTablename": "BlobTable",
"repoMetaTablename": "RepoMetadataTable",
"imageMetaTablename": "ImageMetaTable",
"repoBlobsInfoTablename": "RepoBlobsInfoTable",
"userDataTablename": "UserDataTable",
"apiKeyTablename":"ApiKeyTable",
"versionTablename": "Version"
}
},
"http": {
"address": "${zot_server_address}",
"port": "${zot_server_port}",
"realm": "zot"
},
"cluster": {
"members": [],
"hashKey": "loremipsumdolors"
},
"log": {
"level": "debug",
"output": "${zot_log_file}"
}
}
EOF
}
# updates an existing zot config file that already has an HTTP config
# to include htpasswd auth settings.
# intended for use with create_zot_cloud_base_config_file() above.
function update_zot_cfg_set_htpasswd_auth() {
local zot_config_file="${1}"
local zot_htpasswd_path="${2}"
local temp_file="${BATS_FILE_TMPDIR}/jq-auth-dump.json"
# set zot htpasswd auth
jq --arg htpasswd_path "${zot_htpasswd_path}" \
'(.http) += {auth: {htpasswd: {path: $htpasswd_path}}}' \
${zot_config_file} > ${temp_file} && \
mv ${temp_file} ${zot_config_file}
}
# updates an existing zot config file that already has an HTTP config
# to include TLS configuration.
# intended for use with create_zot_cloud_base_config_file() above.
function update_zot_cfg_set_tls() {
local zot_config_file="${1}"
local zot_cert_path="${2}"
local zot_key_path="${3}"
local zot_cacert_path="${4}"
local temp_file="${BATS_FILE_TMPDIR}/jq-tls-dump.json"
# set zot TLS config
jq --arg zot_cert_path "${zot_cert_path}" --arg zot_key_path "${zot_key_path}" '(.http) += {tls: {cert: $zot_cert_path, key: $zot_key_path}}' \
${zot_config_file} > ${temp_file} && \
mv ${temp_file} ${zot_config_file}
jq --arg zot_cacert_path "${zot_cacert_path}" '(.cluster) += {tls: {cacert: $zot_cacert_path}}' \
${zot_config_file} > ${temp_file} && \
mv ${temp_file} ${zot_config_file}
}