diff --git a/.ci/chart_test.sh b/.ci/chart_test.sh index c61da42c..3cef385f 100755 --- a/.ci/chart_test.sh +++ b/.ci/chart_test.sh @@ -30,7 +30,8 @@ FUNCTION=${FUNCTION:-"false"} MANAGER=${MANAGER:-"false"} ALLOW_LOADBALANCERS=${ALLOW_LOADBALANCERS:-"false"} -source ${PULSAR_HOME}/.ci/helm.sh +# shellcheck source=.ci/helm.sh +source "${PULSAR_HOME}"/.ci/helm.sh # create cluster ci::create_cluster @@ -46,11 +47,11 @@ while [[ $# -gt 0 ]]; do shift done -if [[ "x${SYMMETRIC}" == "xtrue" ]]; then +if [[ "${SYMMETRIC}" == "true" ]]; then extra_opts+=("-s") fi -if [[ "x${EXTRA_SUPERUSERS}" != "x" ]]; then +if [[ "${EXTRA_SUPERUSERS}" != "" ]]; then extra_opts+=("--pulsar-superusers" "proxy-admin,broker-admin,admin,${EXTRA_SUPERUSERS}") fi @@ -62,9 +63,9 @@ if [[ "$UPGRADE_FROM_VERSION" != "" ]]; then PULSAR_CHART_VERSION="$UPGRADE_FROM_VERSION" # Install Prometheus Operator CRDs using the upgrade script since kube-prometheus-stack is now disabled before the upgrade - ${PULSAR_HOME}/scripts/kube-prometheus-stack/upgrade_prometheus_operator_crds.sh + "${PULSAR_HOME}"/scripts/kube-prometheus-stack/upgrade_prometheus_operator_crds.sh - ci::install_pulsar_chart install ${PULSAR_HOME}/.ci/values-common.yaml ${PULSAR_HOME}/${VALUES_FILE} --set kube-prometheus-stack.enabled=false "${extra_opts[@]}" + ci::install_pulsar_chart install "${PULSAR_HOME}"/.ci/values-common.yaml "${PULSAR_HOME}"/"${VALUES_FILE}" --set kube-prometheus-stack.enabled=false "${extra_opts[@]}" install_type="upgrade" echo "Wait 10 seconds" sleep 10 @@ -78,13 +79,13 @@ if [[ "$UPGRADE_FROM_VERSION" != "" ]]; then if [[ "$(ci::helm_values_for_deployment | yq .victoria-metrics-k8s-stack.enabled)" == "true" ]]; then echo "Upgrade Victoria Metrics Operator CRDs before upgrading the deployment" - ${PULSAR_HOME}/scripts/victoria-metrics-k8s-stack/upgrade_vm_operator_crds.sh + "${PULSAR_HOME}"/scripts/victoria-metrics-k8s-stack/upgrade_vm_operator_crds.sh fi fi PULSAR_CHART_VERSION="local" # install (or upgrade) pulsar chart -ci::install_pulsar_chart ${install_type} ${PULSAR_HOME}/.ci/values-common.yaml ${PULSAR_HOME}/${VALUES_FILE} "${extra_opts[@]}" +ci::install_pulsar_chart "${install_type}" "${PULSAR_HOME}"/.ci/values-common.yaml "${PULSAR_HOME}"/"${VALUES_FILE}" "${extra_opts[@]}" echo "Wait 10 seconds" sleep 10 diff --git a/.ci/configure_ci_runner_for_debugging.sh b/.ci/configure_ci_runner_for_debugging.sh index 9c61bca1..cca9b323 100755 --- a/.ci/configure_ci_runner_for_debugging.sh +++ b/.ci/configure_ci_runner_for_debugging.sh @@ -6,7 +6,7 @@ if [[ -z "${GITHUB_ACTIONS}" ]]; then exit 1 fi -cat >> $HOME/.bashrc <<'EOF' +cat >> "$HOME"/.bashrc <<'EOF' function use_kind_kubeconfig() { export KUBECONFIG=$(ls $HOME/kind/pulsar-ci-*/kubeconfig.yaml) } @@ -34,8 +34,8 @@ function k9s() { alias k=kubectl EOF -cat >> $HOME/.bash_profile <<'EOF' +cat >> "$HOME"/.bash_profile <<'EOF' if [ -f ~/.bashrc ]; then source ~/.bashrc fi -EOF \ No newline at end of file +EOF diff --git a/.ci/helm.sh b/.ci/helm.sh index 833d8230..8e5be40c 100755 --- a/.ci/helm.sh +++ b/.ci/helm.sh @@ -23,12 +23,13 @@ CHARTS_HOME=${PULSAR_HOME} PULSAR_CHART_LOCAL=${CHARTS_HOME}/charts/pulsar PULSAR_CHART_VERSION=${PULSAR_CHART_VERSION:-"local"} OUTPUT_BIN=${CHARTS_HOME}/output/bin +# shellcheck disable=SC2034 KIND_BIN=$OUTPUT_BIN/kind HELM=${OUTPUT_BIN}/helm -: ${KUBECTL:=$OUTPUT_BIN/kubectl} -: ${NAMESPACE:=pulsar} +: "${KUBECTL:=$OUTPUT_BIN/kubectl}" +: "${NAMESPACE:=pulsar}" CLUSTER=pulsar-ci -: ${CLUSTER_ID:=$(uuidgen)} +: "${CLUSTER_ID:=$(uuidgen)}" K8S_LOGS_DIR="${K8S_LOGS_DIR:-/tmp/k8s-logs}" export PATH="$OUTPUT_BIN:$PATH" @@ -56,23 +57,23 @@ fi function ci::create_cluster() { echo "Creating a kind cluster ..." - ${CHARTS_HOME}/hack/kind-cluster-build.sh --name pulsar-ci-${CLUSTER_ID} -c 1 -v 10 + "${CHARTS_HOME}"/hack/kind-cluster-build.sh --name pulsar-ci-"${CLUSTER_ID}" -c 1 -v 10 echo "Successfully created a kind cluster." } function ci::delete_cluster() { echo "Deleting a kind cluster ..." - kind delete cluster --name=pulsar-ci-${CLUSTER_ID} + kind delete cluster --name=pulsar-ci-"${CLUSTER_ID}" echo "Successfully delete a kind cluster." } function ci::install_cert_manager() { echo "Installing the cert-manager ..." ${KUBECTL} create namespace cert-manager - ${CHARTS_HOME}/scripts/cert-manager/install-cert-manager.sh + "${CHARTS_HOME}"/scripts/cert-manager/install-cert-manager.sh WC=$(${KUBECTL} get pods -n cert-manager --field-selector=status.phase=Running | wc -l) while [[ ${WC} -lt 3 ]]; do - echo ${WC}; + echo "${WC}"; sleep 15 ${KUBECTL} get pods -n cert-manager ${KUBECTL} get events --sort-by=.lastTimestamp -A | tail -n 30 || true @@ -92,22 +93,22 @@ function ci::helm_repo_add() { function ci::print_pod_logs() { echo "Logs for all containers:" - for k8sobject in $(${KUBECTL} get pods,jobs -n ${NAMESPACE} -o=name); do - ${KUBECTL} logs -n ${NAMESPACE} "$k8sobject" --all-containers=true --ignore-errors=true --prefix=true --tail=100 || true + for k8sobject in $(${KUBECTL} get pods,jobs -n "${NAMESPACE}" -o=name); do + ${KUBECTL} logs -n "${NAMESPACE}" "$k8sobject" --all-containers=true --ignore-errors=true --prefix=true --tail=100 || true done; } function ci::collect_k8s_logs() { - mkdir -p "${K8S_LOGS_DIR}" && cd "${K8S_LOGS_DIR}" + mkdir -p "${K8S_LOGS_DIR}" && cd "${K8S_LOGS_DIR}" || exit echo "Collecting k8s logs to ${K8S_LOGS_DIR}" - for k8sobject in $(${KUBECTL} get pods,jobs -n ${NAMESPACE} -o=name); do + for k8sobject in $(${KUBECTL} get pods,jobs -n "${NAMESPACE}" -o=name); do filebase="${k8sobject//\//_}" - ${KUBECTL} logs -n ${NAMESPACE} "$k8sobject" --all-containers=true --ignore-errors=true --prefix=true > "${filebase}.$$.log.txt" || true - ${KUBECTL} logs -n ${NAMESPACE} "$k8sobject" --all-containers=true --ignore-errors=true --prefix=true --previous=true > "${filebase}.previous.$$.log.txt" || true + ${KUBECTL} logs -n "${NAMESPACE}" "$k8sobject" --all-containers=true --ignore-errors=true --prefix=true > "${filebase}.$$.log.txt" || true + ${KUBECTL} logs -n "${NAMESPACE}" "$k8sobject" --all-containers=true --ignore-errors=true --prefix=true --previous=true > "${filebase}.previous.$$.log.txt" || true done; ${KUBECTL} get events --sort-by=.lastTimestamp -A > events.$$.log.txt || true ${KUBECTL} get events --sort-by=.lastTimestamp -A -o yaml > events.$$.log.yaml || true - ${KUBECTL} get -n ${NAMESPACE} all -o yaml > k8s_resources.$$.yaml || true + ${KUBECTL} get -n "${NAMESPACE}" all -o yaml > k8s_resources.$$.yaml || true } function ci::install_pulsar_chart() { @@ -129,14 +130,14 @@ function ci::install_pulsar_chart() { extra_opts+=("$arg") fi done - local install_args + local -a install_args if [[ "${install_type}" == "install" ]]; then echo "Installing the pulsar chart" - ${KUBECTL} create namespace ${NAMESPACE} + ${KUBECTL} create namespace "${NAMESPACE}" ci::install_cert_manager - echo ${CHARTS_HOME}/scripts/pulsar/prepare_helm_release.sh -k ${CLUSTER} -n ${NAMESPACE} "${extra_opts[@]}" - ${CHARTS_HOME}/scripts/pulsar/prepare_helm_release.sh -k ${CLUSTER} -n ${NAMESPACE} "${extra_opts[@]}" + echo "${CHARTS_HOME}"/scripts/pulsar/prepare_helm_release.sh -k "${CLUSTER}" -n "${NAMESPACE}" "${extra_opts[@]}" + "${CHARTS_HOME}"/scripts/pulsar/prepare_helm_release.sh -k "${CLUSTER}" -n "${NAMESPACE}" "${extra_opts[@]}" sleep 10 # install metallb for loadbalancer support @@ -148,46 +149,46 @@ function ci::install_pulsar_chart() { --selector=app=metallb \ --timeout=120s # configure metallb - ${KUBECTL} apply -f ${BINDIR}/metallb/metallb-config.yaml - install_args="" + ${KUBECTL} apply -f "${BINDIR}"/metallb/metallb-config.yaml # create auth resources - if [[ "x${AUTHENTICATION_PROVIDER}" == "xopenid" ]]; then + if [[ "${AUTHENTICATION_PROVIDER}" == "openid" ]]; then ci::create_openid_resources fi else - install_args="--wait --wait-for-jobs --timeout 360s --debug" + install_args+=("--wait" "--wait-for-jobs" "--timeout=360s" "--debug") fi - CHART_ARGS="" + declare -a CHART_ARGS if [[ "${PULSAR_CHART_VERSION}" == "local" ]]; then set -x - ${HELM} dependency update ${PULSAR_CHART_LOCAL} + ${HELM} dependency update "${PULSAR_CHART_LOCAL}" set +x - CHART_ARGS="${PULSAR_CHART_LOCAL}" + CHART_ARGS+=("${PULSAR_CHART_LOCAL}") else set -x ${HELM} repo add apache https://pulsar.apache.org/charts set +x - CHART_ARGS="apache/pulsar --dependency-update" + CHART_ARGS+=("apache/pulsar" "--dependency-update") if [[ "${PULSAR_CHART_VERSION}" != "latest" ]]; then - CHART_ARGS="${CHART_ARGS} --version ${PULSAR_CHART_VERSION}" + CHART_ARGS+=("--version=${PULSAR_CHART_VERSION}") fi fi set -x - ${HELM} template --values ${common_value_file} --values ${value_file} "${extra_values[@]}" ${CLUSTER} ${CHART_ARGS} - ${HELM} ${install_type} --values ${common_value_file} --values ${value_file} "${extra_values[@]}" --namespace=${NAMESPACE} ${CLUSTER} ${CHART_ARGS} ${install_args} + ${HELM} template --values "${common_value_file}" --values "${value_file}" "${extra_values[@]}" "${CLUSTER}" "${CHART_ARGS[@]}" + ${HELM} "${install_type}" --values "${common_value_file}" --values "${value_file}" "${extra_values[@]}" --namespace="${NAMESPACE}" "${CLUSTER}" "${CHART_ARGS[@]}" "${install_args[@]}" set +x if [[ "${install_type}" == "install" ]]; then echo "wait until broker is alive" - WC=$(${KUBECTL} get pods -n ${NAMESPACE} --field-selector=status.phase=Running | grep ${CLUSTER}-broker | wc -l) + # shellcheck disable=SC2126 + WC=$(${KUBECTL} get pods -n "${NAMESPACE}" --field-selector=status.phase=Running | grep "${CLUSTER}"-broker | wc -l) counter=1 while [[ ${WC} -lt 1 ]]; do ((counter++)) - echo ${WC}; + echo "${WC}"; sleep 15 - ${KUBECTL} get pods,jobs -n ${NAMESPACE} + ${KUBECTL} get pods,jobs -n "${NAMESPACE}" ${KUBECTL} get events --sort-by=.lastTimestamp -A | tail -n 30 || true if [[ $((counter % 20)) -eq 0 ]]; then ci::print_pod_logs @@ -196,23 +197,27 @@ function ci::install_pulsar_chart() { exit 1 fi fi - WC=$(${KUBECTL} get pods -n ${NAMESPACE} | grep ${CLUSTER}-broker | wc -l) + # shellcheck disable=SC2126 + WC=$(${KUBECTL} get pods -n "${NAMESPACE}" | grep "${CLUSTER}"-broker | wc -l) if [[ ${WC} -gt 1 ]]; then - ${KUBECTL} describe pod -n ${NAMESPACE} pulsar-ci-broker-0 - ${KUBECTL} logs -n ${NAMESPACE} pulsar-ci-broker-0 + ${KUBECTL} describe pod -n "${NAMESPACE}" pulsar-ci-broker-0 + ${KUBECTL} logs -n "${NAMESPACE}" pulsar-ci-broker-0 fi - WC=$(${KUBECTL} get pods -n ${NAMESPACE} --field-selector=status.phase=Running | grep ${CLUSTER}-broker | wc -l) + # shellcheck disable=SC2126 + WC=$(${KUBECTL} get pods -n "${NAMESPACE}" --field-selector=status.phase=Running | grep "${CLUSTER}"-broker | wc -l) done - timeout 300s ${KUBECTL} exec -n ${NAMESPACE} ${CLUSTER}-toolset-0 -- bash -c 'until nslookup pulsar-ci-broker; do sleep 3; done' || { echo >&2 "Timeout waiting..."; ci::print_pod_logs; exit 1; } - timeout 120s ${KUBECTL} exec -n ${NAMESPACE} ${CLUSTER}-toolset-0 -- bash -c 'until [ "$(curl -s -L http://pulsar-ci-broker:8080/status.html)" == "OK" ]; do sleep 3; done' || { echo >&2 "Timeout waiting..."; ci::print_pod_logs; exit 1; } + timeout 300s "${KUBECTL}" exec -n "${NAMESPACE}" "${CLUSTER}"-toolset-0 -- bash -c 'until nslookup pulsar-ci-broker; do sleep 3; done' || { echo >&2 "Timeout waiting..."; ci::print_pod_logs; exit 1; } + # shellcheck disable=SC2016 + timeout 120s "${KUBECTL}" exec -n "${NAMESPACE}" "${CLUSTER}"-toolset-0 -- bash -c 'until [ "$(curl -s -L http://pulsar-ci-broker:8080/status.html)" == "OK" ]; do sleep 3; done' || { echo >&2 "Timeout waiting..."; ci::print_pod_logs; exit 1; } - WC=$(${KUBECTL} get pods -n ${NAMESPACE} --field-selector=status.phase=Running | grep ${CLUSTER}-proxy | wc -l) + # shellcheck disable=SC2126 + WC=$(${KUBECTL} get pods -n "${NAMESPACE}" --field-selector=status.phase=Running | grep "${CLUSTER}"-proxy | wc -l) counter=1 while [[ ${WC} -lt 1 ]]; do ((counter++)) - echo ${WC}; + echo "${WC}"; sleep 15 - ${KUBECTL} get pods,jobs -n ${NAMESPACE} + ${KUBECTL} get pods,jobs -n "${NAMESPACE}" ${KUBECTL} get events --sort-by=.lastTimestamp -A | tail -n 30 || true if [[ $((counter % 8)) -eq 0 ]]; then ci::print_pod_logs @@ -221,16 +226,18 @@ function ci::install_pulsar_chart() { exit 1 fi fi - WC=$(${KUBECTL} get pods -n ${NAMESPACE} --field-selector=status.phase=Running | grep ${CLUSTER}-proxy | wc -l) + # shellcheck disable=SC2126 + WC=$(${KUBECTL} get pods -n "${NAMESPACE}" --field-selector=status.phase=Running | grep ${CLUSTER}-proxy | wc -l) done - timeout 300s ${KUBECTL} exec -n ${NAMESPACE} ${CLUSTER}-toolset-0 -- bash -c 'until nslookup pulsar-ci-proxy; do sleep 3; done' || { echo >&2 "Timeout waiting..."; ci::print_pod_logs; exit 1; } + timeout 300s "${KUBECTL}" exec -n "${NAMESPACE}" "${CLUSTER}"-toolset-0 -- bash -c 'until nslookup pulsar-ci-proxy; do sleep 3; done' || { echo >&2 "Timeout waiting..."; ci::print_pod_logs; exit 1; } echo "Install complete" else echo "wait until broker is alive" - timeout 300s ${KUBECTL} exec -n ${NAMESPACE} ${CLUSTER}-toolset-0 -- bash -c 'until nslookup pulsar-ci-broker; do sleep 3; done' || { echo >&2 "Timeout waiting..."; ci::print_pod_logs; exit 1; } - timeout 120s ${KUBECTL} exec -n ${NAMESPACE} ${CLUSTER}-toolset-0 -- bash -c 'until [ "$(curl -s -L http://pulsar-ci-broker:8080/status.html)" == "OK" ]; do sleep 3; done' || { echo >&2 "Timeout waiting..."; ci::print_pod_logs; exit 1; } + timeout 300s "${KUBECTL}" exec -n "${NAMESPACE}" "${CLUSTER}"-toolset-0 -- bash -c 'until nslookup pulsar-ci-broker; do sleep 3; done' || { echo >&2 "Timeout waiting..."; ci::print_pod_logs; exit 1; } + # shellcheck disable=SC2016 + timeout 120s "${KUBECTL}" exec -n "${NAMESPACE}" "${CLUSTER}"-toolset-0 -- bash -c 'until [ "$(curl -s -L http://pulsar-ci-broker:8080/status.html)" == "OK" ]; do sleep 3; done' || { echo >&2 "Timeout waiting..."; ci::print_pod_logs; exit 1; } echo "wait until proxy is alive" - timeout 300s ${KUBECTL} exec -n ${NAMESPACE} ${CLUSTER}-toolset-0 -- bash -c 'until nslookup pulsar-ci-proxy; do sleep 3; done' || { echo >&2 "Timeout waiting..."; ci::print_pod_logs; exit 1; } + timeout 300s "${KUBECTL}" exec -n "${NAMESPACE}" "${CLUSTER}"-toolset-0 -- bash -c 'until nslookup pulsar-ci-proxy; do sleep 3; done' || { echo >&2 "Timeout waiting..."; ci::print_pod_logs; exit 1; } echo "Upgrade complete" fi } @@ -239,27 +246,28 @@ helm_values_cached="" function ci::helm_values_for_deployment() { if [[ -z "${helm_values_cached}" ]]; then - helm_values_cached=$(helm get values -n ${NAMESPACE} ${CLUSTER} -a -o yaml) + helm_values_cached=$(helm get values -n "${NAMESPACE}" "${CLUSTER}" -a -o yaml) fi printf "%s" "${helm_values_cached}" } function ci::check_pulsar_environment() { echo "Wait until pulsar-ci-broker is ready" - ${KUBECTL} exec -n ${NAMESPACE} ${CLUSTER}-toolset-0 -- bash -c 'until nslookup pulsar-ci-broker; do sleep 3; done' + ${KUBECTL} exec -n "${NAMESPACE}" "${CLUSTER}"-toolset-0 -- bash -c 'until nslookup pulsar-ci-broker; do sleep 3; done' echo "Wait until pulsar-ci-proxy is ready" - ${KUBECTL} exec -n ${NAMESPACE} ${CLUSTER}-toolset-0 -- bash -c 'until nslookup pulsar-ci-proxy; do sleep 3; done' + ${KUBECTL} exec -n "${NAMESPACE}" "${CLUSTER}"-toolset-0 -- bash -c 'until nslookup pulsar-ci-proxy; do sleep 3; done' echo "bookie-0 disk usage" - ${KUBECTL} exec -n ${NAMESPACE} ${CLUSTER}-bookie-0 -- df -h + ${KUBECTL} exec -n "${NAMESPACE}" "${CLUSTER}"-bookie-0 -- df -h echo "bookie-0 bookkeeper.conf" - ${KUBECTL} exec -n ${NAMESPACE} ${CLUSTER}-bookie-0 -- cat conf/bookkeeper.conf + ${KUBECTL} exec -n "${NAMESPACE}" "${CLUSTER}"-bookie-0 -- cat conf/bookkeeper.conf echo "bookie-0 bookies list (rw)" - ${KUBECTL} exec -n ${NAMESPACE} ${CLUSTER}-toolset-0 -- bin/bookkeeper shell listbookies -rw | grep ListBookiesCommand + ${KUBECTL} exec -n "${NAMESPACE}" "${CLUSTER}"-toolset-0 -- bin/bookkeeper shell listbookies -rw | grep ListBookiesCommand echo "bookie-0 bookies list (ro)" - ${KUBECTL} exec -n ${NAMESPACE} ${CLUSTER}-toolset-0 -- bin/bookkeeper shell listbookies -ro | grep ListBookiesCommand + ${KUBECTL} exec -n "${NAMESPACE}" "${CLUSTER}"-toolset-0 -- bin/bookkeeper shell listbookies -ro | grep ListBookiesCommand } # function to retry a given commend 3 times with a backoff of 10 seconds in between +# shellcheck disable=SC2015 function ci::retry() { local n=1 local max=3 @@ -279,12 +287,12 @@ function ci::retry() { function ci::test_pulsar_admin_api_access() { echo "Test pulsar admin api access" - ci::retry ${KUBECTL} exec -n ${NAMESPACE} ${CLUSTER}-toolset-0 -- bin/pulsar-admin tenants list + ci::retry "${KUBECTL}" exec -n "${NAMESPACE}" "${CLUSTER}"-toolset-0 -- bin/pulsar-admin tenants list } function ci::test_create_test_namespace() { - ${KUBECTL} exec -n ${NAMESPACE} ${CLUSTER}-toolset-0 -- bin/pulsar-admin tenants create pulsar-ci - ${KUBECTL} exec -n ${NAMESPACE} ${CLUSTER}-toolset-0 -- bin/pulsar-admin namespaces create pulsar-ci/test + "${KUBECTL}" exec -n "${NAMESPACE}" "${CLUSTER}"-toolset-0 -- bin/pulsar-admin tenants create pulsar-ci + "${KUBECTL}" exec -n "${NAMESPACE}" "${CLUSTER}"-toolset-0 -- bin/pulsar-admin namespaces create pulsar-ci/test } function ci::test_pulsar_producer_consumer() { @@ -298,23 +306,24 @@ function ci::test_pulsar_producer_consumer() { set -x if [[ "${action}" == "produce" || "${action}" == "produce-consume" ]]; then ci::test_create_test_namespace - ${KUBECTL} exec -n ${NAMESPACE} ${CLUSTER}-toolset-0 -- bin/pulsar-admin topics create pulsar-ci/test/test-topic - ${KUBECTL} exec -n ${NAMESPACE} ${CLUSTER}-toolset-0 -- bin/pulsar-admin topics create-subscription -s test pulsar-ci/test/test-topic - ${KUBECTL} exec -n ${NAMESPACE} ${CLUSTER}-toolset-0 -- bin/pulsar-client produce -m "test-message" pulsar-ci/test/test-topic - ${KUBECTL} exec -n ${NAMESPACE} ${CLUSTER}-toolset-0 -- bin/pulsar-admin topics create-subscription -s test2 pulsar-ci/test/test-topic - ${KUBECTL} exec -n ${NAMESPACE} ${CLUSTER}-toolset-0 -- bin/pulsar-client --url "${PROXY_URL}" produce -m "test-message2" pulsar-ci/test/test-topic + "${KUBECTL}" exec -n "${NAMESPACE}" "${CLUSTER}"-toolset-0 -- bin/pulsar-admin topics create pulsar-ci/test/test-topic + "${KUBECTL}" exec -n "${NAMESPACE}" "${CLUSTER}"-toolset-0 -- bin/pulsar-admin topics create-subscription -s test pulsar-ci/test/test-topic + "${KUBECTL}" exec -n "${NAMESPACE}" "${CLUSTER}"-toolset-0 -- bin/pulsar-client produce -m "test-message" pulsar-ci/test/test-topic + "${KUBECTL}" exec -n "${NAMESPACE}" "${CLUSTER}"-toolset-0 -- bin/pulsar-admin topics create-subscription -s test2 pulsar-ci/test/test-topic + "${KUBECTL}" exec -n "${NAMESPACE}" "${CLUSTER}"-toolset-0 -- bin/pulsar-client --url "${PROXY_URL}" produce -m "test-message2" pulsar-ci/test/test-topic fi if [[ "${action}" == "consume" || "${action}" == "produce-consume" ]]; then - ${KUBECTL} exec -n ${NAMESPACE} ${CLUSTER}-toolset-0 -- bin/pulsar-client consume -s test pulsar-ci/test/test-topic - ${KUBECTL} exec -n ${NAMESPACE} ${CLUSTER}-toolset-0 -- bin/pulsar-client --url "${PROXY_URL}" consume -s test2 pulsar-ci/test/test-topic + "${KUBECTL}" exec -n "${NAMESPACE}" "${CLUSTER}"-toolset-0 -- bin/pulsar-client consume -s test pulsar-ci/test/test-topic + "${KUBECTL}" exec -n "${NAMESPACE}" "${CLUSTER}"-toolset-0 -- bin/pulsar-client --url "${PROXY_URL}" consume -s test2 pulsar-ci/test/test-topic fi set +x } function ci::wait_function_running() { - local function_status=$(${KUBECTL} exec -n ${NAMESPACE} ${CLUSTER}-toolset-0 -- bash -c 'bin/pulsar-admin functions status --tenant pulsar-ci --namespace test --name test-function | grep -v INFO') + local function_status + function_status=$(${KUBECTL} exec -n "${NAMESPACE}" "${CLUSTER}"-toolset-0 -- bash -c 'bin/pulsar-admin functions status --tenant pulsar-ci --namespace test --name test-function | grep -v INFO') echo "Function status: $function_status" - num_running=$(echo $function_status | jq .numRunning || echo 0) + num_running=$(echo "$function_status" | jq .numRunning || echo 0) counter=1 while [[ ${num_running} -lt 1 ]]; do ((counter++)) @@ -324,24 +333,24 @@ function ci::wait_function_running() { fi echo "Waiting 15 seconds for function to be running" sleep 15 - ${KUBECTL} get pods -n ${NAMESPACE} -l component=function || true + ${KUBECTL} get pods -n "${NAMESPACE}" -l component=function || true ${KUBECTL} get events --sort-by=.lastTimestamp -A | tail -n 30 || true - podname=$(${KUBECTL} get pods -l component=function -n ${NAMESPACE} --no-headers -o custom-columns=":metadata.name") || true + podname=$(${KUBECTL} get pods -l component=function -n "${NAMESPACE}" --no-headers -o custom-columns=":metadata.name") || true if [[ -n "$podname" ]]; then echo "Function pod is $podname" - ${KUBECTL} describe pod -n ${NAMESPACE} $podname + ${KUBECTL} describe pod -n "${NAMESPACE}" "$podname" echo "Function pod logs" - ${KUBECTL} logs -n ${NAMESPACE} $podname + ${KUBECTL} logs -n "${NAMESPACE}" "$podname" fi - function_status=$(${KUBECTL} exec -n ${NAMESPACE} ${CLUSTER}-toolset-0 -- bash -c 'bin/pulsar-admin functions status --tenant pulsar-ci --namespace test --name test-function | grep -v INFO') + function_status=$(${KUBECTL} exec -n "${NAMESPACE}" "${CLUSTER}"-toolset-0 -- bash -c 'bin/pulsar-admin functions status --tenant pulsar-ci --namespace test --name test-function | grep -v INFO') echo "Function status: $function_status" - num_running=$(echo $function_status | jq .numRunning || echo 0) + num_running=$(echo "$function_status" | jq .numRunning || echo 0) done } function ci::wait_message_processed() { - num_processed=$(${KUBECTL} exec -n ${NAMESPACE} ${CLUSTER}-toolset-0 -- bash -c 'bin/pulsar-admin functions stats --tenant pulsar-ci --namespace test --name test-function | grep -v INFO' | jq .processedSuccessfullyTotal) - podname=$(${KUBECTL} get pods -l component=function -n ${NAMESPACE} --no-headers -o custom-columns=":metadata.name") + num_processed=$(${KUBECTL} exec -n "${NAMESPACE}" "${CLUSTER}"-toolset-0 -- bash -c 'bin/pulsar-admin functions stats --tenant pulsar-ci --namespace test --name test-function | grep -v INFO' | jq .processedSuccessfullyTotal) + podname=$(${KUBECTL} get pods -l component=function -n "${NAMESPACE}" --no-headers -o custom-columns=":metadata.name") counter=1 while [[ ${num_processed} -lt 1 ]]; do ((counter++)) @@ -352,66 +361,66 @@ function ci::wait_message_processed() { echo "Waiting 15 seconds for message to be processed" sleep 15 echo "Function pod is $podname" - ${KUBECTL} describe pod -n ${NAMESPACE} $podname + ${KUBECTL} describe pod -n "${NAMESPACE}" "$podname" echo "Function pod logs" - ${KUBECTL} logs -n ${NAMESPACE} $podname - ${KUBECTL} exec -n ${NAMESPACE} ${CLUSTER}-toolset-0 -- bin/pulsar-admin functions stats --tenant pulsar-ci --namespace test --name test-function | grep -v INFO - num_processed=$(${KUBECTL} exec -n ${NAMESPACE} ${CLUSTER}-toolset-0 -- bash -c 'bin/pulsar-admin functions stats --tenant pulsar-ci --namespace test --name test-function | grep -v INFO' | jq .processedSuccessfullyTotal) + ${KUBECTL} logs -n "${NAMESPACE}" "$podname" + ${KUBECTL} exec -n "${NAMESPACE}" "${CLUSTER}"-toolset-0 -- bin/pulsar-admin functions stats --tenant pulsar-ci --namespace test --name test-function | grep -v INFO + num_processed=$(${KUBECTL} exec -n "${NAMESPACE}" "${CLUSTER}"-toolset-0 -- bash -c 'bin/pulsar-admin functions stats --tenant pulsar-ci --namespace test --name test-function | grep -v INFO' | jq .processedSuccessfullyTotal) done } function ci::test_pulsar_function() { echo "Testing functions" echo "Creating function" - ${KUBECTL} exec -n ${NAMESPACE} ${CLUSTER}-toolset-0 -- bin/pulsar-admin functions create --tenant pulsar-ci --namespace test --name test-function --inputs "pulsar-ci/test/test_input" --output "pulsar-ci/test/test_output" --parallelism 1 --classname org.apache.pulsar.functions.api.examples.ExclamationFunction --jar /pulsar/examples/api-examples.jar + ${KUBECTL} exec -n "${NAMESPACE}" "${CLUSTER}"-toolset-0 -- bin/pulsar-admin functions create --tenant pulsar-ci --namespace test --name test-function --inputs "pulsar-ci/test/test_input" --output "pulsar-ci/test/test_output" --parallelism 1 --classname org.apache.pulsar.functions.api.examples.ExclamationFunction --jar /pulsar/examples/api-examples.jar echo "Creating subscription for output topic" - ${KUBECTL} exec -n ${NAMESPACE} ${CLUSTER}-toolset-0 -- bin/pulsar-admin topics create-subscription -s test pulsar-ci/test/test_output + ${KUBECTL} exec -n "${NAMESPACE}" "${CLUSTER}"-toolset-0 -- bin/pulsar-admin topics create-subscription -s test pulsar-ci/test/test_output echo "Waiting for function to be ready" # wait until the function is running ci::wait_function_running echo "Sending input message" - ${KUBECTL} exec -n ${NAMESPACE} ${CLUSTER}-toolset-0 -- bin/pulsar-client produce -m 'hello pulsar function!' pulsar-ci/test/test_input + ${KUBECTL} exec -n "${NAMESPACE}" "${CLUSTER}"-toolset-0 -- bin/pulsar-client produce -m 'hello pulsar function!' pulsar-ci/test/test_input echo "Waiting for message to be processed" ci::wait_message_processed echo "Consuming output message" - ${KUBECTL} exec -n ${NAMESPACE} ${CLUSTER}-toolset-0 -- bin/pulsar-client consume -s test pulsar-ci/test/test_output + ${KUBECTL} exec -n "${NAMESPACE}" "${CLUSTER}"-toolset-0 -- bin/pulsar-client consume -s test pulsar-ci/test/test_output } function ci::test_pulsar_manager() { echo "Testing pulsar manager" - until ${KUBECTL} get jobs -n ${NAMESPACE} ${CLUSTER}-pulsar-manager-init -o json | jq -r '.status.conditions[] | select (.type | test("Complete")).status' | grep True; do sleep 3; done - ${KUBECTL} describe job -n ${NAMESPACE} ${CLUSTER}-pulsar-manager-init - ${KUBECTL} logs -n ${NAMESPACE} job.batch/${CLUSTER}-pulsar-manager-init - ${KUBECTL} exec -n ${NAMESPACE} ${CLUSTER}-pulsar-manager-0 -- cat /pulsar-manager/pulsar-manager.log + until ${KUBECTL} get jobs -n "${NAMESPACE}" "${CLUSTER}"-pulsar-manager-init -o json | jq -r '.status.conditions[] | select (.type | test("Complete")).status' | grep True; do sleep 3; done + ${KUBECTL} describe job -n "${NAMESPACE}" "${CLUSTER}"-pulsar-manager-init + ${KUBECTL} logs -n "${NAMESPACE}" job.batch/"${CLUSTER}"-pulsar-manager-init + ${KUBECTL} exec -n "${NAMESPACE}" "${CLUSTER}"-pulsar-manager-0 -- cat /pulsar-manager/pulsar-manager.log echo "Checking Podname" - podname=$(${KUBECTL} get pods -n ${NAMESPACE} -l component=pulsar-manager --no-headers -o custom-columns=":metadata.name") + podname=$(${KUBECTL} get pods -n "${NAMESPACE}" -l component=pulsar-manager --no-headers -o custom-columns=":metadata.name") echo "Getting pulsar manager UI password" - PASSWORD=$(${KUBECTL} get secret -n ${NAMESPACE} -l component=pulsar-manager -o=jsonpath="{.items[0].data.UI_PASSWORD}" | base64 --decode) + PASSWORD=$(${KUBECTL} get secret -n "${NAMESPACE}" -l component=pulsar-manager -o=jsonpath="{.items[0].data.UI_PASSWORD}" | base64 --decode) echo "Getting CSRF_TOKEN" - CSRF_TOKEN=$(${KUBECTL} exec -n ${NAMESPACE} ${podname} -- curl http://127.0.0.1:7750/pulsar-manager/csrf-token) + CSRF_TOKEN=$(${KUBECTL} exec -n "${NAMESPACE}" "${podname}" -- curl http://127.0.0.1:7750/pulsar-manager/csrf-token) echo "Performing login" - ${KUBECTL} exec -n ${NAMESPACE} ${podname} -- curl -X POST http://127.0.0.1:9527/pulsar-manager/login \ + ${KUBECTL} exec -n "${NAMESPACE}" "${podname}" -- curl -X POST http://127.0.0.1:9527/pulsar-manager/login \ -H 'Accept: application/json, text/plain, */*' \ -H 'Content-Type: application/json' \ -H "X-XSRF-TOKEN: $CSRF_TOKEN" \ -H "Cookie: XSRF-TOKEN=$CSRF_TOKEN" \ -sS -D headers.txt \ - -d '{"username": "pulsar", "password": "'${PASSWORD}'"}' - LOGIN_TOKEN=$(${KUBECTL} exec -n ${NAMESPACE} ${podname} -- grep "token:" headers.txt | sed 's/^.*: //') - LOGIN_JSESSIONID=$(${KUBECTL} exec -n ${NAMESPACE} ${podname} -- grep -o "JSESSIONID=[a-zA-Z0-9_]*" headers.txt | sed 's/^.*=//') + -d '{"username": "pulsar", "password": "'"${PASSWORD}"'"}' + LOGIN_TOKEN=$(${KUBECTL} exec -n "${NAMESPACE}" "${podname}" -- grep "token:" headers.txt | sed 's/^.*: //') + LOGIN_JSESSIONID=$(${KUBECTL} exec -n "${NAMESPACE}" "${podname}" -- grep -o "JSESSIONID=[a-zA-Z0-9_]*" headers.txt | sed 's/^.*=//') echo "Checking environment" - envs=$(${KUBECTL} exec -n ${NAMESPACE} ${podname} -- curl -X GET http://127.0.0.1:9527/pulsar-manager/environments \ + envs=$(${KUBECTL} exec -n "${NAMESPACE}" "${podname}" -- curl -X GET http://127.0.0.1:9527/pulsar-manager/environments \ -H 'Content-Type: application/json' \ -H "token: $LOGIN_TOKEN" \ -H "X-XSRF-TOKEN: $CSRF_TOKEN" \ -H "username: pulsar" \ -H "Cookie: XSRF-TOKEN=$CSRF_TOKEN; JSESSIONID=$LOGIN_JSESSIONID;") echo "$envs" - number_of_envs=$(echo $envs | jq '.total') + number_of_envs=$(echo "$envs" | jq '.total') if [ "$number_of_envs" -ne 1 ]; then echo "Error: Did not find expected environment" exit 1 @@ -419,8 +428,8 @@ function ci::test_pulsar_manager() { # Force manager to query broker for tenant info. This will require use of the manager's JWT, if JWT authentication is enabled. echo "Checking tenants" - pulsar_env=$(echo $envs | jq -r '.data[0].name') - tenants=$(${KUBECTL} exec -n ${NAMESPACE} ${podname} -- curl -X GET http://127.0.0.1:9527/pulsar-manager/admin/v2/tenants \ + pulsar_env=$(echo "$envs" | jq -r '.data[0].name') + tenants=$(${KUBECTL} exec -n "${NAMESPACE}" "${podname}" -- curl -X GET http://127.0.0.1:9527/pulsar-manager/admin/v2/tenants \ -H 'Content-Type: application/json' \ -H "token: $LOGIN_TOKEN" \ -H "X-XSRF-TOKEN: $CSRF_TOKEN" \ @@ -429,7 +438,7 @@ function ci::test_pulsar_manager() { -H "environment: ${pulsar_env}" \ -H "Cookie: XSRF-TOKEN=$CSRF_TOKEN; JSESSIONID=$LOGIN_JSESSIONID;") echo "$tenants" - number_of_tenants=$(echo $tenants | jq '.total') + number_of_tenants=$(echo "$tenants" | jq '.total') if [ "$number_of_tenants" -lt 1 ]; then echo "Error: Found no tenants!" exit 1 @@ -439,8 +448,7 @@ function ci::test_pulsar_manager() { function ci::check_loadbalancers() { ( set +e - ${KUBECTL} get services -n ${NAMESPACE} | grep LoadBalancer - if [ $? -eq 0 ]; then + if ${KUBECTL} get services -n "${NAMESPACE}" | grep -q LoadBalancer; then echo "Error: Found service with type LoadBalancer. This is not allowed because of security reasons." exit 1 fi @@ -454,21 +462,22 @@ function ci::validate_kustomize_yaml() { KUSTOMIZE_VERSION=5.6.0 KUSTOMIZE_DIR=$(mktemp -d) echo "Installing kustomize ${KUSTOMIZE_VERSION} to ${KUSTOMIZE_DIR}" - curl -s "https://raw.githubusercontent.com/kubernetes-sigs/kustomize/master/hack/install_kustomize.sh" | bash -s ${KUSTOMIZE_VERSION} ${KUSTOMIZE_DIR} + curl -s "https://raw.githubusercontent.com/kubernetes-sigs/kustomize/master/hack/install_kustomize.sh" | bash -s "${KUSTOMIZE_VERSION}" "${KUSTOMIZE_DIR}" export PATH=${KUSTOMIZE_DIR}:$PATH fi # prevent regression of https://github.com/apache/pulsar-helm-chart/issues/569 - local kustomize_yaml_dir=$(mktemp -d) - cp ${PULSAR_HOME}/.ci/kustomization.yaml ${kustomize_yaml_dir} - PULSAR_HOME=${PULSAR_HOME} yq -i '.helmGlobals.chartHome = env(PULSAR_HOME) + "/charts"' ${kustomize_yaml_dir}/kustomization.yaml + local kustomize_yaml_dir + kustomize_yaml_dir=$(mktemp -d) + cp "${PULSAR_HOME}"/.ci/kustomization.yaml "${kustomize_yaml_dir}" + PULSAR_HOME="${PULSAR_HOME}" yq -i '.helmGlobals.chartHome = env(PULSAR_HOME) + "/charts"' "${kustomize_yaml_dir}"/kustomization.yaml failures=0 # validate zookeeper init echo "Validating kustomize yaml output with zookeeper init" - _ci::validate_kustomize_yaml ${kustomize_yaml_dir} || ((failures++)) + _ci::validate_kustomize_yaml "${kustomize_yaml_dir}" || ((failures++)) # validate oxia init - yq -i '.helmCharts[0].valuesInline.components += {"zookeeper": false, "oxia": true}' ${kustomize_yaml_dir}/kustomization.yaml + yq -i '.helmCharts[0].valuesInline.components += {"zookeeper": false, "oxia": true}' "${kustomize_yaml_dir}"/kustomization.yaml echo "Validating kustomize yaml output with oxia init" - _ci::validate_kustomize_yaml ${kustomize_yaml_dir} || ((failures++)) + _ci::validate_kustomize_yaml "${kustomize_yaml_dir}" || ((failures++)) if [ $failures -gt 0 ]; then exit 1 fi @@ -476,7 +485,7 @@ function ci::validate_kustomize_yaml() { function _ci::validate_kustomize_yaml() { local kustomize_yaml_dir=$1 - kustomize build --enable-helm --helm-kube-version 1.23.0 --load-restrictor=LoadRestrictionsNone ${kustomize_yaml_dir} | yq 'select(.spec.template.spec.containers[0].args != null) | .spec.template.spec.containers[0].args' | \ + kustomize build --enable-helm --helm-kube-version 1.23.0 --load-restrictor=LoadRestrictionsNone "${kustomize_yaml_dir}" | yq 'select(.spec.template.spec.containers[0].args != null) | .spec.template.spec.containers[0].args' | \ awk '{ if (prev_line ~ /\\$/ && $0 ~ /^$/) { print "Found issue: backslash at end of line followed by empty line. Must use pipe character for multiline strings to support kustomize due to kubernetes-sigs/kustomize#4201."; @@ -499,16 +508,17 @@ function ci::create_openid_resources() { echo "Creating openid resources" - cp ${PULSAR_HOME}/.ci/auth/keycloak/0-realm-pulsar-partial-export.json /tmp/realm-pulsar.json + cp "${PULSAR_HOME}"/.ci/auth/keycloak/0-realm-pulsar-partial-export.json /tmp/realm-pulsar.json for component in broker proxy admin manager; do echo "Creating openid resources for ${component}" - local client_id=pulsar-${component} + local client_id=pulsar-"${component}" # Github action hang up when read string from /dev/urandom, so use python to generate a random string - local client_secret=$(python -c "import secrets; import string; length = 32; random_string = ''.join(secrets.choice(string.ascii_letters + string.digits) for _ in range(length)); print(random_string);") + local client_secret + client_secret=$(python -c "import secrets; import string; length = 32; random_string = ''.join(secrets.choice(string.ascii_letters + string.digits) for _ in range(length)); print(random_string);") if [[ "${component}" == "admin" ]]; then local sub_claim_value="admin" @@ -517,14 +527,14 @@ function ci::create_openid_resources() { fi # Create the client credentials file - jq -n --arg CLIENT_ID $client_id --arg CLIENT_SECRET "$client_secret" -f ${PULSAR_HOME}/.ci/auth/oauth2/credentials_file.json > /tmp/${component}-credentials_file.json + jq -n --arg CLIENT_ID "$client_id" --arg CLIENT_SECRET "$client_secret" -f "${PULSAR_HOME}"/.ci/auth/oauth2/credentials_file.json > /tmp/"${component}"-credentials_file.json # Create the secret for the client credentials local secret_name="pulsar-${component}-credentials" - ${KUBECTL} create secret generic ${secret_name} --from-file=credentials_file.json=/tmp/${component}-credentials_file.json -n ${NAMESPACE} + ${KUBECTL} create secret generic "${secret_name}" --from-file=credentials_file.json=/tmp/"${component}"-credentials_file.json -n "${NAMESPACE}" # Create the keycloak client file - jq -n --arg CLIENT_ID $client_id --arg CLIENT_SECRET "$client_secret" --arg SUB_CLAIM_VALUE "$sub_claim_value" -f ${PULSAR_HOME}/.ci/auth/keycloak/1-client-template.json > /tmp/${component}-keycloak-client.json + jq -n --arg CLIENT_ID "$client_id" --arg CLIENT_SECRET "$client_secret" --arg SUB_CLAIM_VALUE "$sub_claim_value" -f "${PULSAR_HOME}"/.ci/auth/keycloak/1-client-template.json > /tmp/"${component}"-keycloak-client.json # Merge the keycloak client file with the realm jq '.clients += [input]' /tmp/realm-pulsar.json /tmp/${component}-keycloak-client.json > /tmp/realm-pulsar.json.tmp @@ -533,19 +543,20 @@ function ci::create_openid_resources() { done echo "Create keycloak realm configuration" - ${KUBECTL} create secret generic keycloak-ci-realm-config --from-file=realm-pulsar.json=/tmp/realm-pulsar.json -n ${NAMESPACE} + ${KUBECTL} create secret generic keycloak-ci-realm-config --from-file=realm-pulsar.json=/tmp/realm-pulsar.json -n "${NAMESPACE}" echo "Installing keycloak helm chart" - ${HELM} install keycloak-ci codecentric/keycloakx --version 7.1.8 --values ${PULSAR_HOME}/.ci/auth/keycloak/values.yaml -n ${NAMESPACE} + ${HELM} install keycloak-ci codecentric/keycloakx --version 7.1.8 --values "${PULSAR_HOME}"/.ci/auth/keycloak/values.yaml -n "${NAMESPACE}" echo "Wait until keycloak is running" - WC=$(${KUBECTL} get pods -n ${NAMESPACE} --field-selector=status.phase=Running | grep keycloak-ci-keycloakx-0 | wc -l) + # shellcheck disable=SC2126 + WC=$(${KUBECTL} get pods -n "${NAMESPACE}" --field-selector=status.phase=Running | grep keycloak-ci-keycloakx-0 | wc -l) counter=1 while [[ ${WC} -lt 1 ]]; do ((counter++)) - echo ${WC}; + echo "${WC}"; sleep 15 - ${KUBECTL} get pods,jobs -n ${NAMESPACE} + ${KUBECTL} get pods,jobs -n "${NAMESPACE}" ${KUBECTL} get events --sort-by=.lastTimestamp -A | tail -n 30 || true if [[ $((counter % 20)) -eq 0 ]]; then ci::print_pod_logs @@ -554,11 +565,12 @@ function ci::create_openid_resources() { exit 1 fi fi - WC=$(${KUBECTL} get pods -n ${NAMESPACE} --field-selector=status.phase=Running | grep keycloak-ci-keycloakx-0 | wc -l) + # shellcheck disable=SC2126 + WC=$(${KUBECTL} get pods -n "${NAMESPACE}" --field-selector=status.phase=Running | grep keycloak-ci-keycloakx-0 | wc -l) done echo "Wait until keycloak is ready" - ${KUBECTL} wait --for=condition=Ready pod/keycloak-ci-keycloakx-0 -n ${NAMESPACE} --timeout 180s + ${KUBECTL} wait --for=condition=Ready pod/keycloak-ci-keycloakx-0 -n "${NAMESPACE}" --timeout 180s } # lists all available functions in this tool @@ -577,7 +589,7 @@ if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then ci_function_name="ci::$1" shift if [[ "$(LC_ALL=C type -t "${ci_function_name}")" == "function" ]]; then - eval "$ci_function_name" "$@" + "$ci_function_name" "$@" exit $? else echo "Invalid ci function" @@ -585,4 +597,4 @@ if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then ci::list_functions exit 1 fi -fi \ No newline at end of file +fi diff --git a/hack/common.sh b/hack/common.sh index d6c0f111..5f4f4ffa 100755 --- a/hack/common.sh +++ b/hack/common.sh @@ -69,15 +69,16 @@ function hack::ensure_kubectl() { fi echo "Installing kubectl v$KUBECTL_VERSION..." tmpfile=$(mktemp) - trap "test -f $tmpfile && rm $tmpfile" RETURN - curl --retry 10 -L -o $tmpfile https://dl.k8s.io/release/v${KUBECTL_VERSION}/bin/${OS}/${ARCH}/kubectl - mv $tmpfile $KUBECTL_BIN - chmod +x $KUBECTL_BIN + trap 'test -f "$tmpfile" && rm "$tmpfile"' RETURN + curl --retry 10 -L -o "$tmpfile" https://dl.k8s.io/release/v"${KUBECTL_VERSION}"/bin/"${OS}"/"${ARCH}"/kubectl + mv "$tmpfile" "$KUBECTL_BIN" + chmod +x "$KUBECTL_BIN" } function hack::verify_helm() { if test -x "$HELM_BIN"; then - local v=$($HELM_BIN version --short --client | grep -o -E '[0-9]+\.[0-9]+\.[0-9]+') + local v + v=$($HELM_BIN version --short --client | grep -o -E '[0-9]+\.[0-9]+\.[0-9]+') [[ "$v" == "$HELM_VERSION" ]] return fi @@ -88,8 +89,9 @@ function hack::ensure_helm() { if hack::verify_helm; then return 0 fi - local HELM_URL=https://get.helm.sh/helm-v${HELM_VERSION}-${OS}-${ARCH}.tar.gz - curl --retry 10 -L -s "$HELM_URL" | tar --strip-components 1 -C $OUTPUT_BIN -zxf - ${OS}-${ARCH}/helm + local HELM_URL + HELM_URL=https://get.helm.sh/helm-v${HELM_VERSION}-${OS}-${ARCH}.tar.gz + curl --retry 10 -L -s "$HELM_URL" | tar --strip-components 1 -C "$OUTPUT_BIN" -zxf - "${OS}"-"${ARCH}"/helm } function hack::verify_kind() { @@ -106,10 +108,10 @@ function hack::ensure_kind() { fi echo "Installing kind v$KIND_VERSION..." tmpfile=$(mktemp) - trap "test -f $tmpfile && rm $tmpfile" RETURN - curl --retry 10 -L -o $tmpfile https://github.com/kubernetes-sigs/kind/releases/download/v${KIND_VERSION}/kind-${OS}-${ARCH} - mv $tmpfile $KIND_BIN - chmod +x $KIND_BIN + trap 'test -f "$tmpfile" && rm "$tmpfile"' RETURN + curl --retry 10 -L -o "$tmpfile" https://github.com/kubernetes-sigs/kind/releases/download/v"${KIND_VERSION}"/kind-"${OS}"-"${ARCH}" + mv "$tmpfile" "$KIND_BIN" + chmod +x "$KIND_BIN" } # hack::version_ge "$v1" "$v2" checks whether "v1" is greater or equal to "v2" @@ -131,16 +133,16 @@ function hack::ensure_cr() { fi echo "Installing chart-releaser ${CR_VERSION} ..." tmpfile=$(mktemp) - trap "test -f $tmpfile && rm $tmpfile" RETURN - echo curl --retry 10 -L -o $tmpfile https://github.com/helm/chart-releaser/releases/download/v${CR_VERSION}/chart-releaser_${CR_VERSION}_${OS}_${ARCH}.tar.gz - curl --retry 10 -L -o $tmpfile https://github.com/helm/chart-releaser/releases/download/v${CR_VERSION}/chart-releaser_${CR_VERSION}_${OS}_${ARCH}.tar.gz - mv $tmpfile $CR_BIN - chmod +x $CR_BIN + trap 'test -f "$tmpfile" && rm "$tmpfile"' RETURN + echo curl --retry 10 -L -o "$tmpfile" https://github.com/helm/chart-releaser/releases/download/v"${CR_VERSION}"/chart-releaser_"${CR_VERSION}"_"${OS}"_"${ARCH}".tar.gz + curl --retry 10 -L -o "$tmpfile" https://github.com/helm/chart-releaser/releases/download/v"${CR_VERSION}"/chart-releaser_"${CR_VERSION}"_"${OS}"_"${ARCH}".tar.gz + mv "$tmpfile" "$CR_BIN" + chmod +x "$CR_BIN" $CR_BIN version } function hack::ensure_kubeconform() { echo "Installing kubeconform v$KUBECONFORM_VERSION..." - curl -s --retry 10 -L https://github.com/yannh/kubeconform/releases/download/v${KUBECONFORM_VERSION}/kubeconform-${OS}-${ARCH}.tar.gz | tar -xzO kubeconform > $KUBECONFORM_BIN - chmod +x $KUBECONFORM_BIN -} \ No newline at end of file + curl -s --retry 10 -L https://github.com/yannh/kubeconform/releases/download/v"${KUBECONFORM_VERSION}"/kubeconform-"${OS}"-"${ARCH}".tar.gz | tar -xzO kubeconform > "$KUBECONFORM_BIN" + chmod +x "$KUBECONFORM_BIN" +} diff --git a/hack/kind-cluster-build.sh b/hack/kind-cluster-build.sh index e7915e00..5dc86482 100755 --- a/hack/kind-cluster-build.sh +++ b/hack/kind-cluster-build.sh @@ -18,10 +18,11 @@ # under the License. # -PULSAR_CHART_HOME=$(unset CDPATH && cd $(dirname "${BASH_SOURCE[0]}")/.. && pwd) -cd ${PULSAR_CHART_HOME} +PULSAR_CHART_HOME=$(unset CDPATH && cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd) +cd "${PULSAR_CHART_HOME}" || exit -source ${PULSAR_CHART_HOME}/hack/common.sh +# shellcheck source=hack/common.sh +source "${PULSAR_CHART_HOME}/hack/common.sh" hack::ensure_kubectl hack::ensure_helm @@ -107,18 +108,18 @@ done echo "############# start create cluster:[${clusterName}] #############" workDir=${HOME}/kind/${clusterName} -mkdir -p ${workDir} +mkdir -p "${workDir}" data_dir=${workDir}/data echo "clean data dir: ${data_dir}" -if [ -d ${data_dir} ]; then - rm -rf ${data_dir} +if [ -d "${data_dir}" ]; then + rm -rf "${data_dir}" fi configFile=${workDir}/kind-config.yaml -cat < ${configFile} +cat < "${configFile}" kind: Cluster apiVersion: kind.x-k8s.io/v1alpha4 networking: @@ -132,39 +133,39 @@ nodes: protocol: TCP EOF -for ((i=0;i<${nodeNum};i++)) +for ((i=0;i> ${configFile} + mkdir -p "${data_dir}"/worker${i} + cat <> "${configFile}" - role: worker extraMounts: EOF - for ((k=1;k<=${volumeNum};k++)) + for ((k=1;k<=volumeNum;k++)) do - mkdir -p ${data_dir}/worker${i}/vol${k} - cat <> ${configFile} + mkdir -p "${data_dir}"/worker${i}/vol${k} + cat <> "${configFile}" - containerPath: /mnt/disks/vol${k} hostPath: ${data_dir}/worker${i}/vol${k} EOF done done -matchedCluster=$(kind get clusters | grep ${clusterName}) +matchedCluster=$(kind get clusters | grep "${clusterName}") if [[ "${matchedCluster}" == "${clusterName}" ]]; then echo "Kind cluster ${clusterName} already exists" - kind delete cluster --name=${clusterName} + kind delete cluster --name="${clusterName}" fi echo "start to create k8s cluster" -kind create cluster --config ${configFile} --image kindest/node:${k8sVersion} --name=${clusterName} --verbosity 3 +kind create cluster --config "${configFile}" --image kindest/node:"${k8sVersion}" --name="${clusterName}" --verbosity 3 export KUBECONFIG=${workDir}/kubeconfig.yaml -kind get kubeconfig --name=${clusterName} > ${KUBECONFIG} +kind get kubeconfig --name="${clusterName}" > "${KUBECONFIG}" echo "deploy docker registry in kind" registryNode=${clusterName}-control-plane -registryNodeIP=$($KUBECTL_BIN get nodes ${registryNode} -o template --template='{{range.status.addresses}}{{if eq .type "InternalIP"}}{{.address}}{{end}}{{end}}') +registryNodeIP=$($KUBECTL_BIN get nodes "${registryNode}" -o template --template='{{range.status.addresses}}{{if eq .type "InternalIP"}}{{.address}}{{end}}{{end}}') registryFile=${workDir}/registry.yaml -cat <${registryFile} +cat < "${registryFile}" apiVersion: apps/v1 kind: DaemonSet metadata: @@ -232,14 +233,14 @@ spec: - tcp-listen:5000,fork,reuseaddr - tcp-connect:${registryNodeIP}:5000 EOF -$KUBECTL_BIN apply -f ${registryFile} +$KUBECTL_BIN apply -f "${registryFile}" echo "############# success create cluster:[${clusterName}] #############" echo "To start using your cluster, run:" echo " export KUBECONFIG=${KUBECONFIG}" echo "" -echo < /dev/null 2>&1 || "need_${comm}" done - gcloud container clusters list --project $PROJECT >/dev/null 2>&1 || { echo >&2 "Gcloud seems to be configured incorrectly or authentication is unsuccessfull"; exit 1; } + gcloud container clusters list --project "$PROJECT" >/dev/null 2>&1 || { echo >&2 "Gcloud seems to be configured incorrectly or authentication is unsuccessfull"; exit 1; } } function cluster_admin_password_gke(){ - gcloud container clusters describe $CLUSTER_NAME --zone $ZONE --project $PROJECT --format='value(masterAuth.password)'; + gcloud container clusters describe "$CLUSTER_NAME" --zone "$ZONE" --project "$PROJECT" --format='value(masterAuth.password)'; } function validate_eks_required_tools(){ diff --git a/scripts/pulsar/common_auth.sh b/scripts/pulsar/common_auth.sh index fdc7eaf0..3b6ef31b 100755 --- a/scripts/pulsar/common_auth.sh +++ b/scripts/pulsar/common_auth.sh @@ -27,4 +27,5 @@ if [ -z "$PULSAR_VERSION" ]; then PULSAR_VERSION="4.0.3" fi fi -PULSAR_TOKENS_CONTAINER_IMAGE="apachepulsar/pulsar:${PULSAR_VERSION}" \ No newline at end of file +# shellcheck disable=SC2034 +PULSAR_TOKENS_CONTAINER_IMAGE="apachepulsar/pulsar:${PULSAR_VERSION}" diff --git a/scripts/pulsar/generate_token.sh b/scripts/pulsar/generate_token.sh index 45a5543f..70e8306d 100755 --- a/scripts/pulsar/generate_token.sh +++ b/scripts/pulsar/generate_token.sh @@ -22,8 +22,9 @@ set -e SCRIPT_DIR="$(unset CDPATH && cd "$(dirname "${BASH_SOURCE[0]}")" &>/dev/null && pwd)" CHART_HOME=$(unset CDPATH && cd "$SCRIPT_DIR/../.." && pwd) -cd ${CHART_HOME} +cd "${CHART_HOME}" +# shellcheck source=scripts/pulsar/common_auth.sh source "${SCRIPT_DIR}/common_auth.sh" usage() { @@ -83,7 +84,7 @@ case $key in esac done -if [[ "x${role}" == "x" ]]; then +if [[ "${role}" == "" ]]; then echo "No pulsar role is provided!" usage exit 1 @@ -96,11 +97,11 @@ function pulsar::jwt::get_secret() { local type=$1 local tmpfile=$2 local secret_name=$3 - echo ${secret_name} + echo "${secret_name}" if [[ "${local}" == "true" ]]; then - cp ${type} ${tmpfile} + cp "${type}" "${tmpfile}" else - kubectl get -n ${namespace} secrets ${secret_name} -o jsonpath="{.data['${type}']}" | base64 --decode > ${tmpfile} + kubectl get -n "${namespace}" secrets "${secret_name}" -o jsonpath="{.data['${type}']}" | base64 --decode > "${tmpfile}" fi } @@ -109,40 +110,42 @@ function pulsar::jwt::generate_symmetric_token() { local secret_name="${release}-token-symmetric-key" - local tmpdir=$(mktemp -d) - trap "test -d $tmpdir && rm -rf $tmpdir" RETURN + local tmpdir + tmpdir=$(mktemp -d) + trap 'test -d "$tmpdir" && rm -rf "$tmpdir"' RETURN secretkeytmpfile=${tmpdir}/secret.key tokentmpfile=${tmpdir}/token.jwt - pulsar::jwt::get_secret SECRETKEY ${secretkeytmpfile} ${secret_name} + pulsar::jwt::get_secret SECRETKEY "${secretkeytmpfile}" "${secret_name}" - docker run --user 0 --rm -v ${tmpdir}:/keydir ${PULSAR_TOKENS_CONTAINER_IMAGE} bin/pulsar tokens create -a HS256 --subject "${role}" --secret-key=file:/keydir/secret.key > ${tokentmpfile} + docker run --user 0 --rm -v "${tmpdir}":/keydir "${PULSAR_TOKENS_CONTAINER_IMAGE}" bin/pulsar tokens create -a HS256 --subject "${role}" --secret-key=file:/keydir/secret.key > "${tokentmpfile}" newtokentmpfile=${tmpdir}/token.jwt.new - tr -d '\n' < ${tokentmpfile} > ${newtokentmpfile} - kubectl create secret generic ${token_name} -n ${namespace} --from-file="TOKEN=${newtokentmpfile}" --from-literal="TYPE=symmetric" ${local:+ -o yaml --dry-run=client} - rm -rf $tmpdir + tr -d '\n' < "${tokentmpfile}" > "${newtokentmpfile}" + kubectl create secret generic "${token_name}" -n "${namespace}" --from-file="TOKEN=${newtokentmpfile}" --from-literal="TYPE=symmetric" ${local:+ -o yaml --dry-run=client} + rm -rf "$tmpdir" } function pulsar::jwt::generate_asymmetric_token() { local token_name="${release}-token-${role}" local secret_name="${release}-token-asymmetric-key" - local tmpdir=$(mktemp -d) - trap "test -d $tmpdir && rm -rf $tmpdir" RETURN + local tmpdir + tmpdir=$(mktemp -d) + trap 'test -d "$tmpdir" && rm -rf "$tmpdir"' RETURN privatekeytmpfile=${tmpdir}/privatekey.der tokentmpfile=${tmpdir}/token.jwt - pulsar::jwt::get_secret PRIVATEKEY ${privatekeytmpfile} ${secret_name} + pulsar::jwt::get_secret PRIVATEKEY "${privatekeytmpfile}" "${secret_name}" # Generate token - docker run --user 0 --rm -v ${tmpdir}:/keydir ${PULSAR_TOKENS_CONTAINER_IMAGE} bin/pulsar tokens create -a RS256 --subject "${role}" --private-key=file:/keydir/privatekey.der > ${tokentmpfile} + docker run --user 0 --rm -v "${tmpdir}":/keydir "${PULSAR_TOKENS_CONTAINER_IMAGE}" bin/pulsar tokens create -a RS256 --subject "${role}" --private-key=file:/keydir/privatekey.der > "${tokentmpfile}" newtokentmpfile=${tmpdir}/token.jwt.new - tr -d '\n' < ${tokentmpfile} > ${newtokentmpfile} - kubectl create secret generic ${token_name} -n ${namespace} --from-file="TOKEN=${newtokentmpfile}" --from-literal="TYPE=asymmetric" ${local:+ -o yaml --dry-run=client} - rm -rf $tmpdir + tr -d '\n' < "${tokentmpfile}" > "${newtokentmpfile}" + kubectl create secret generic "${token_name}" -n "${namespace}" --from-file="TOKEN=${newtokentmpfile}" --from-literal="TYPE=asymmetric" ${local:+ -o yaml --dry-run=client} + rm -rf "$tmpdir" } if [[ "${symmetric}" == "true" ]]; then diff --git a/scripts/pulsar/generate_token_secret_key.sh b/scripts/pulsar/generate_token_secret_key.sh index f61322a2..e084f904 100755 --- a/scripts/pulsar/generate_token_secret_key.sh +++ b/scripts/pulsar/generate_token_secret_key.sh @@ -22,8 +22,9 @@ set -e SCRIPT_DIR="$(unset CDPATH && cd "$(dirname "${BASH_SOURCE[0]}")" &>/dev/null && pwd)" CHART_HOME=$(unset CDPATH && cd "$SCRIPT_DIR/../.." && pwd) -cd ${CHART_HOME} +cd "${CHART_HOME}" +# shellcheck source=scripts/pulsar/common_auth.sh source "${SCRIPT_DIR}/common_auth.sh" usage() { @@ -79,43 +80,44 @@ done namespace=${namespace:-pulsar} release=${release:-pulsar-dev} -local_cmd=${file:+-o yaml --dry-run=client >secret.yaml} function pulsar::jwt::generate_symmetric_key() { local secret_name="${release}-token-symmetric-key" - local tmpdir=$(mktemp -d) - trap "test -d $tmpdir && rm -rf $tmpdir" RETURN + local tmpdir + tmpdir=$(mktemp -d) + trap 'test -d "$tmpdir" && rm -rf "$tmpdir"' RETURN local tmpfile=${tmpdir}/SECRETKEY - docker run --rm -t ${PULSAR_TOKENS_CONTAINER_IMAGE} bin/pulsar tokens create-secret-key > "${tmpfile}" - kubectl create secret generic ${secret_name} -n ${namespace} --from-file=$tmpfile ${local:+ -o yaml --dry-run=client} + docker run --rm -t "${PULSAR_TOKENS_CONTAINER_IMAGE}" bin/pulsar tokens create-secret-key > "${tmpfile}" + kubectl create secret generic "${secret_name}" -n "${namespace}" --from-file="$tmpfile" ${local:+ -o yaml --dry-run=client} # if local is true, keep the file available for debugging purposes if [[ "${local}" == "true" ]]; then - mv $tmpfile SECRETKEY + mv "$tmpfile" SECRETKEY fi - rm -rf $tmpdir + rm -rf "$tmpdir" } function pulsar::jwt::generate_asymmetric_key() { local secret_name="${release}-token-asymmetric-key" - local tmpdir=$(mktemp -d) - trap "test -d $tmpdir && rm -rf $tmpdir" RETURN + local tmpdir + tmpdir=$(mktemp -d) + trap 'test -d "$tmpdir" && rm -rf "$tmpdir"' RETURN privatekeytmpfile=${tmpdir}/PRIVATEKEY publickeytmpfile=${tmpdir}/PUBLICKEY # Generate key pair - docker run --user 0 --rm -t -v ${tmpdir}:/keydir ${PULSAR_TOKENS_CONTAINER_IMAGE} bin/pulsar tokens create-key-pair --output-private-key=/keydir/PRIVATEKEY --output-public-key=/keydir/PUBLICKEY + docker run --user 0 --rm -t -v "${tmpdir}":/keydir "${PULSAR_TOKENS_CONTAINER_IMAGE}" bin/pulsar tokens create-key-pair --output-private-key=/keydir/PRIVATEKEY --output-public-key=/keydir/PUBLICKEY - kubectl create secret generic ${secret_name} -n ${namespace} --from-file=$privatekeytmpfile --from-file=$publickeytmpfile ${local:+ -o yaml --dry-run=client} + kubectl create secret generic "${secret_name}" -n "${namespace}" --from-file="$privatekeytmpfile" --from-file="$publickeytmpfile" ${local:+ -o yaml --dry-run=client} # if local is true, keep the files available for debugging purposes if [[ "${local}" == "true" ]]; then - mv $privatekeytmpfile PRIVATEKEY - mv $publickeytmpfile PUBLICKEY + mv "$privatekeytmpfile" PRIVATEKEY + mv "$publickeytmpfile" PUBLICKEY fi - rm -rf $tmpdir + rm -rf "$tmpdir" } if [[ "${symmetric}" == "true" ]]; then diff --git a/scripts/pulsar/get_token.sh b/scripts/pulsar/get_token.sh index 31003adc..b6e9bed3 100755 --- a/scripts/pulsar/get_token.sh +++ b/scripts/pulsar/get_token.sh @@ -20,8 +20,8 @@ set -e -CHART_HOME=$(unset CDPATH && cd $(dirname "${BASH_SOURCE[0]}")/../.. && pwd) -cd ${CHART_HOME} +CHART_HOME=$(unset CDPATH && cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd) +cd "${CHART_HOME}" usage() { cat < /dev/null 2>&1 -if [ $? -ne 0 ]; then +if ! kubectl get namespace "${namespace}" > /dev/null 2>&1; then echo "error: failed to get namespace '${namespace}'" echo "please check that this namespace exists, or use the '-c' option to create it" exit 1 fi -extra_opts="" +declare -a extra_opts if [[ "${symmetric}" == "true" ]]; then - extra_opts="${extra_opts} -s" + extra_opts+=("-s") fi if [[ "${local}" == "true" ]]; then - extra_opts="${extra_opts} -l" + extra_opts+=("-l") fi echo "generate the token keys for the pulsar cluster" >&2 new_k8s_object -${CHART_HOME}/scripts/pulsar/generate_token_secret_key.sh -n ${namespace} -k ${release} ${extra_opts} +"${CHART_HOME}"/scripts/pulsar/generate_token_secret_key.sh -n "${namespace}" -k "${release}" "${extra_opts[@]}" echo "generate the tokens for the super-users: ${pulsar_superusers}" >&2 @@ -130,7 +129,7 @@ for user in "${superusers[@]}" do echo "generate the token for $user" >&2 new_k8s_object - ${CHART_HOME}/scripts/pulsar/generate_token.sh -n ${namespace} -k ${release} -r ${user} ${extra_opts} + "${CHART_HOME}"/scripts/pulsar/generate_token.sh -n "${namespace}" -k "${release}" -r "${user}" "${extra_opts[@]}" done echo "-------------------------------------" >&2 diff --git a/scripts/set-pulsar-version.sh b/scripts/set-pulsar-version.sh index 2c9b1475..54780115 100755 --- a/scripts/set-pulsar-version.sh +++ b/scripts/set-pulsar-version.sh @@ -29,7 +29,7 @@ set -e OLD_VERSION=${1} NEW_VERSION=${2} -if [[ "" == ${OLD_VERSION} || "" == ${NEW_VERSION} ]]; then +if [[ "" == "${OLD_VERSION}" || "" == "${NEW_VERSION}" ]]; then echo "You need to provide the old_version and new_version" exit 1 fi diff --git a/scripts/victoria-metrics-k8s-stack/resolve_vm_operator_version.sh b/scripts/victoria-metrics-k8s-stack/resolve_vm_operator_version.sh index ac31680d..a9031be9 100755 --- a/scripts/victoria-metrics-k8s-stack/resolve_vm_operator_version.sh +++ b/scripts/victoria-metrics-k8s-stack/resolve_vm_operator_version.sh @@ -27,7 +27,7 @@ fi SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" &>/dev/null && pwd)" # Run "helm dependency update" in charts/pulsar -cd "$SCRIPT_DIR/../../charts/pulsar" +cd "$SCRIPT_DIR/../../charts/pulsar" || exit helm dependency update 2>/dev/null 1>&2 tar -zxf charts/victoria-metrics-k8s-stack-*.tgz \ - --to-stdout victoria-metrics-k8s-stack/charts/victoria-metrics-operator/Chart.yaml | yq .appVersion \ No newline at end of file + --to-stdout victoria-metrics-k8s-stack/charts/victoria-metrics-operator/Chart.yaml | yq .appVersion