Skip to content

Commit

Permalink
[Test] Add a feature to start workspaces in separate namespaces (#22901)
Browse files Browse the repository at this point in the history
  • Loading branch information
SkorikSergey committed Apr 2, 2024
1 parent 817ddaf commit bc658b5
Show file tree
Hide file tree
Showing 6 changed files with 172 additions and 43 deletions.
25 changes: 13 additions & 12 deletions tests/performance/load-tests/README.md
Original file line number Diff line number Diff line change
@@ -1,19 +1,20 @@
# Overview
This script tests how well OpenShift environment can handle running simultaneously many of workspaces. It evaluates the performance of the system under test by checking the average results across all pods and identifying failures that occur during the testing process.
This script tests the performance of an OpenShift environment by running multiple workspaces simultaneously. It evaluates the system under test by checking the average results across all pods and identifying any failures that occur during the testing process.

## Prerequisites
What do you need to run those tests
- `kubectl` client installed
- Openshift cluster with running Openshift DevSpaces
- test user logged into DevSpaces Dashboard(this quaranies that user namespaces are created)
To run these tests, you will need:
- The `kubectl` client installed
- An OpenShift cluster with running OpenShift DevSpaces
- A test user logged into the DevSpaces Dashboard (this ensures that user namespaces are created)

## Running load tests
1. Log in to Openshift cluster with Openshift DevSpaces or Eclipse Che deployed from terminal
2. Start `load-test.sh` script from `test/e2e/performance/load-tests`. Set number of started workspaces by -c parameter(like ./load-test.sh -c 5). Set timeout for waiting workspaces to start by -t parameter in seconds(like ./load-test.sh -t 240).
3. This script uses local dewvorspace.yaml to starts workspaces.
4. Also it is possible to get test devworkspace yam file by link using -l option(like `./load-test.sh -l https://gist.githubusercontent.com/SkorikSergey/1856af20514ecce6c0dbb71f44fc0bcb/raw/3f6a38f0f6adf017dcecf6486ffe507ebe6cfc31/load-test-devworkspace.yaml)`.
5. As results there are average time of workspace starting and number of failed workspaces.

Follow these steps to run the load tests:
1. Log in to the OpenShift cluster with OpenShift DevSpaces or Eclipse Che deployed from the terminal.
2. Start the `load-test.sh` script from `test/e2e/performance/load-tests`. Set the number of workspaces to start using the `-c` parameter (e.g., `./load-test.sh -c 5`). Set the timeout for waiting for workspaces to start using the `-t` parameter in seconds (e.g., `./load-test.sh -t 240`).
3. This script uses the local `example.yaml` file to start the workspaces.
4. Alternatively, you can provide a link to the test devworkspace YAML file using the `-l` argument (e.g., `./load-test.sh -l https://raw.githubusercontent.com/eclipse/che/main/tests/performance/load-tests/samples/simple-with-editor-pvc.yaml`).
5. If you want to start workspaces in separate namespaces (one workspace per namespace), use the `-s` as flag option (e.g., `./load-test.sh -s `).
6. The script will provide the average time for workspace starting and the number of failed workspaces.

## Results and logs
If workspace failed to start, logs are saved in current directory.
If a workspace fails to start, the logs will be saved in the `logs` directory.
137 changes: 106 additions & 31 deletions tests/performance/load-tests/load-test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,16 @@ OS="$(uname)"

# Capture start time
start=$(date +%s)
# Current namespace where the script is running
current_namespace=$(kubectl config view --minify --output 'jsonpath={..namespace}')
start_separately=false
# Default value for test namespace name
test_namespace_name=load-test-namespace-
# Default value for test devworkspace name
dw_name=load-test-dw-
# default values for label selector
label_type="test-type"
label_key="load-test"

function print() {
echo -e "${GREEN}$1${NC}"
Expand All @@ -22,25 +32,29 @@ function print_error() {
echo -e "${RED}$1${NC}"
}

function cleanup() {
echo "Clean up the environment"
kubectl delete dw --all
kubectl delete dwt --all
}

function getDwStartingTime() {
start_time=$(kubectl get dw dw$1 --template='{{range .status.conditions}}{{if eq .type "Started"}}{{.lastTransitionTime}}{{end}}{{end}}')
end_time=$(kubectl get dw dw$1 --template='{{range .status.conditions}}{{if eq .type "Ready"}}{{.lastTransitionTime}}{{end}}{{end}}')
start_timestamp=$(getTimestamp $start_time)
end_timestamp=$(getTimestamp $end_time)
dw_starting_time=$((end_timestamp - start_timestamp))

print "Devworkspace dw$1 starting time: $dw_starting_time seconds"
echo $dw_starting_time >>logs/sum.log
# Function to display help information
function display_help() {
echo "Usage: $0 [OPTIONS]"
echo "Options:"
echo " -t <SECONDS> Set the timeout in seconds (default: 120)"
echo " -c <COUNT> Set the number of workspaces to start (default: 3)"
echo " -l Set the link to the devworkspace.yaml file"
echo " -s Start workspaces in separate namespaces(one workspace per namespace)"
echo " --help Display this help message"
exit 0
}

function parseArguments() {
while getopts "t:c:l:" opt; do
for arg in "$@"; do
case $arg in
# Check for --help argument
--help)
display_help
;;
esac
done

while getopts "t:c:l:sh" opt; do
case $opt in
t)
export WORKSPACE_IDLE_TIMEOUT=$OPTARG
Expand All @@ -55,6 +69,12 @@ function parseArguments() {
l)
export DEVWORKSPACE_LINK=$OPTARG
;;
s)
export start_separately=true
;;
h)
display_help
;;
\?)
print_error "Invalid option -c. Try for example something like ./load-test.sh -c 7"
exit 1
Expand All @@ -63,6 +83,17 @@ function parseArguments() {
done
}

function cleanup() {
echo "Clean up the environment"

if [ $start_separately = true ]; then
echo "Delete test namespaces"
kubectl delete namespace --selector=$label_type=$label_key >/dev/null 2>&1 || true
else
kubectl delete dw --selector=$label_type=$label_key -n $current_namespace >/dev/null 2>&1 || true
fi
}

function checkScriptVariables() {
# Set the number of workspaces to start
if [ -z $COMPLETITIONS_COUNT ]; then
Expand All @@ -80,7 +111,7 @@ function checkScriptVariables() {
fi

# Get devworkspace yaml from link if it is set
# example - https://gist.githubusercontent.com/SkorikSergey/1856af20514ecce6c0dbb71f44fc0bcb/raw/3f6a38f0f6adf017dcecf6486ffe507ebe6cfc31/load-test-devworkspace.yaml
# example - https://raw.githubusercontent.com/eclipse/che/main/tests/performance/load-tests/samples/simple-ephemeral.yaml
if [ -n "$DEVWORKSPACE_LINK" ]; then
if curl --fail --insecure "$DEVWORKSPACE_LINK" -o devworkspace.yaml; then
echo "Download succeeded, saved to devworkspace.yaml file."
Expand All @@ -96,7 +127,30 @@ function checkScriptVariables() {
fi
else
print "Local devworkspace.yaml file will be used."
cp -f example.yaml devworkspace.yaml
cp -f samples/simple-ephemeral.yaml devworkspace.yaml
fi
}

function getDwStartingTime() {
start_time=$(kubectl get dw $dw_name$1 -n $2 --template='{{range .status.conditions}}{{if eq .type "Started"}}{{.lastTransitionTime}}{{end}}{{end}}')
end_time=$(kubectl get dw $dw_name$1 -n $2 --template='{{range .status.conditions}}{{if eq .type "Ready"}}{{.lastTransitionTime}}{{end}}{{end}}')
start_timestamp=$(getTimestamp $start_time)
end_timestamp=$(getTimestamp $end_time)
dw_starting_time=$((end_timestamp - start_timestamp))

print "Devworkspace $dw_name$1 in $2 namespace starting time: $dw_starting_time seconds"
echo $dw_starting_time >>logs/sum.log
kubectl delete dw $dw_name$1 -n $2 >/dev/null 2>&1
}

function precreateNamespaces() {
if [ $start_separately = true ]; then
echo "Create test namespaces"
for ((i = 1; i <= $COMPLETITIONS_COUNT; i++)); do
namespace=$test_namespace_name$i
kubectl create namespace $namespace
oc label namespace $namespace $label_type=$label_key >/dev/null 2>&1
done
fi
}

Expand All @@ -110,16 +164,27 @@ function getTimestamp() {
}

function runTest() {
# add label to devworkspace.yaml
cat devworkspace.yaml | awk -v key="$label_type" -v value=" $label_key" '/metadata:/{$0=$0"\n labels:\n "key":"value}1' >patched-dw.yaml

# start COMPLETITIONS_COUNT workspaces in parallel
namespace=$current_namespace
for ((i = 1; i <= $COMPLETITIONS_COUNT; i++)); do
awk '/name:/ && !modif { sub(/name: .*/, "name: '"dw$i"'"); modif=1 } {print}' devworkspace.yaml | kubectl apply -f - &
if [ $start_separately = true ]; then
namespace=$test_namespace_name$i
fi
awk '/name:/ && !modif { sub(/name: .*/, "name: '"$dw_name$i"'"); modif=1 } {print}' patched-dw.yaml | kubectl apply -n $namespace -f - &
done
wait

# wait for all workspaces to be started
echo "Wait for all workspaces are started"
namespace=$current_namespace
for ((i = 1; i <= $COMPLETITIONS_COUNT; i++)); do
kubectl wait --for=condition=Ready "dw/dw$i" --timeout=${WORKSPACE_IDLE_TIMEOUT}s || true &
if [ $start_separately = true ]; then
namespace=$test_namespace_name$i
fi
kubectl wait --for=condition=Ready "dw/$dw_name$i" --timeout=${WORKSPACE_IDLE_TIMEOUT}s -n $namespace || true &
done
wait

Expand All @@ -129,21 +194,29 @@ function runTest() {
mkdir logs || true
touch logs/sum.log

# Get all events
kubectl get events --field-selector involvedObject.kind=Pod >logs/events.log
# Get events from all cluster in start_separately mode and only from current namespace in default mode
if [ $start_separately = true ]; then
kubectl get events --field-selector involvedObject.kind=Pod --all-namespaces >logs/events.log
else
kubectl get events --field-selector involvedObject.kind=Pod >logs/events.log
fi

total_time=0
succeeded=0
echo "Calculate average workspaces starting time"
namespace=$current_namespace
for ((i = 1; i <= $COMPLETITIONS_COUNT; i++)); do
if [ "$(kubectl get dw dw$i --template='{{.status.phase}}')" == "Running" ]; then
getDwStartingTime $i & # >>logs/sum.log &
if [ $start_separately = true ]; then
namespace=$test_namespace_name$i
fi
if [ "$(kubectl get dw $dw_name$i -n $namespace --template='{{.status.phase}}')" == "Running" ]; then
getDwStartingTime $i $namespace &
succeeded=$((succeeded + 1))
else
print_error "Timeout waiting for dw$i to become ready or an error occurred."
devworkspace_id=$(kubectl get dw dw$i --template='{{.status.devworkspaceId}}')
kubectl describe dw dw$i >logs/dw$i-describe.log
cat logs/events.log | grep $devworkspace_id >logs/dw$i-$devworkspace_id-events.log
print_error "Timeout waiting for $dw_name$i to become ready or an error occurred."
devworkspace_id=$(kubectl get dw $dw_name$i -n $namespace --template='{{.status.devworkspaceId}}')
kubectl describe dw $dw_name$i -n $namespace >logs/$dw_name$i-describe.log
cat logs/events.log | grep $devworkspace_id >logs/$dw_name$i-$devworkspace_id-events.log || true
fi
done

Expand All @@ -156,10 +229,9 @@ function printResults() {
((total_time += line))
done <"logs/sum.log"

print "==================== Test results ===================="
echo "==================== Test results ===================="
if [ $succeeded -eq 0 ]; then
print_error "No workspaces started successfully."
exit 1
else
print "Average workspace starting time for $succeeded workspaces from $COMPLETITIONS_COUNT started: $((total_time / succeeded)) seconds"
fi
Expand All @@ -171,8 +243,11 @@ function printResults() {
}

parseArguments "$@"
checkScriptVariables
checkScriptVariables "$@"
cleanup

precreateNamespaces
runTest
printResults

cleanup
File renamed without changes.
11 changes: 11 additions & 0 deletions tests/performance/load-tests/samples/simple-pvc.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
kind: DevWorkspace
apiVersion: workspace.devfile.io/v1alpha2
metadata:
name: code-latest
spec:
started: true
template:
components:
- name: dev
container:
image: quay.io/devfile/universal-developer-image:latest
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
kind: DevWorkspace
apiVersion: workspace.devfile.io/v1alpha2
metadata:
name: code-latest
spec:
started: true
template:
attributes:
controller.devfile.io/storage-type: ephemeral
components:
- name: dev
container:
image: quay.io/devfile/universal-developer-image:latest
contributions:
- name: che-code
uri: https://eclipse-che.github.io/che-plugin-registry/main/v3/plugins/che-incubator/che-code/latest/devfile.yaml
components:
- name: che-code-runtime-description
container:
env:
- name: CODE_HOST
value: 0.0.0.0
20 changes: 20 additions & 0 deletions tests/performance/load-tests/samples/simple-with-editor-pvc.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
kind: DevWorkspace
apiVersion: workspace.devfile.io/v1alpha2
metadata:
name: code-latest
spec:
started: true
template:
components:
- name: dev
container:
image: quay.io/devfile/universal-developer-image:latest
contributions:
- name: che-code
uri: https://eclipse-che.github.io/che-plugin-registry/main/v3/plugins/che-incubator/che-code/latest/devfile.yaml
components:
- name: che-code-runtime-description
container:
env:
- name: CODE_HOST
value: 0.0.0.0

0 comments on commit bc658b5

Please sign in to comment.