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

chore(perfomance): Fixing github action to run performance tests in k8s #1739

Merged
merged 35 commits into from
Jan 29, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
f59a45b
Runs in k8s
dagfinno Jan 22, 2025
8f43869
Fixing azure login
dagfinno Jan 22, 2025
750ce24
Fixing subscription-id
dagfinno Jan 22, 2025
03c0a7d
Fixing permissions
dagfinno Jan 22, 2025
a9c4fef
Fixing permissions
dagfinno Jan 22, 2025
e43c9ef
Fixing permissions
dagfinno Jan 22, 2025
ccfc9a4
Fixing permissions
dagfinno Jan 22, 2025
ed0d7dd
Fixing permissions
dagfinno Jan 22, 2025
1223e80
Fixing permissions
dagfinno Jan 22, 2025
a48e0b5
Fixing permissions
dagfinno Jan 22, 2025
96031c2
Fixing permissions
dagfinno Jan 22, 2025
751a0f2
Fixing permissions
dagfinno Jan 22, 2025
751112d
Fixing permissions
dagfinno Jan 22, 2025
b17719c
Fixing permissions
dagfinno Jan 22, 2025
1b3c735
Fixing permissions
dagfinno Jan 22, 2025
4fc26cf
Fixing permissions
dagfinno Jan 22, 2025
dc1c3cb
Fixing permissions
dagfinno Jan 22, 2025
b82c6d1
setting name for test run in k8s
dagfinno Jan 24, 2025
8930121
setting name for test run in k8s
dagfinno Jan 24, 2025
08615d6
setting name for test run in k8s
dagfinno Jan 24, 2025
0f9727a
setting name for test run in k8s
dagfinno Jan 24, 2025
fa77158
setting name for test run in k8s
dagfinno Jan 24, 2025
3f692fb
setting name for test run in k8s
dagfinno Jan 24, 2025
ee4399a
split endusers between vus
dagfinno Jan 24, 2025
52e631f
make a common setup-function
dagfinno Jan 27, 2025
684187b
fix conflict
dagfinno Jan 27, 2025
5f49f31
remove export on endUsersPart
dagfinno Jan 27, 2025
ef9c705
implementing suggestions from coderabbit
dagfinno Jan 27, 2025
8c023f6
Merge branch 'main' into performance/github-runner
dagfinno Jan 27, 2025
fce49e2
add parallellism to name and logging
dagfinno Jan 27, 2025
ee782b7
Merge remote-tracking branch 'origin' into performance/github-runner
dagfinno Jan 27, 2025
14a0636
Merge remote-tracking branch 'refs/remotes/origin/performance/github-…
dagfinno Jan 27, 2025
fb9d0ab
remove logging from test run in k8s
dagfinno Jan 29, 2025
6aa7f8a
Merge branch 'main' into performance/github-runner
dagfinno Jan 29, 2025
4cf2032
Merge branch 'main' into performance/github-runner
dagfinno Jan 29, 2025
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
2 changes: 1 addition & 1 deletion .github/workflows/ci-cd-yt01.yml
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ jobs:
if: ${{ always() && !failure() && !cancelled() && (github.event_name == 'workflow_dispatch' || needs.check-for-changes.outputs.hasBackendChanges == 'true' || needs.check-for-changes.outputs.hasInfraChanges == 'true') }}
needs: [deploy-apps, deploy-infra, check-for-changes]
#needs: [deploy-apps, check-for-changes]
uses: ./.github/workflows/workflow-run-k6-performance.yml
uses: ./.github/workflows/workflow-run-k6-ci-cd-yt01.yml
secrets:
TOKEN_GENERATOR_USERNAME: ${{ secrets.TOKEN_GENERATOR_USERNAME }}
TOKEN_GENERATOR_PASSWORD: ${{ secrets.TOKEN_GENERATOR_PASSWORD }}
Expand Down
10 changes: 9 additions & 1 deletion .github/workflows/dispatch-k6-performance.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,11 @@ on:
required: true
default: 1m
type: string
parallelism:
description: 'Number of parallel test runs'
required: true
default: 1
type: number
testSuitePath:
description: 'Path to test suite to run'
required: true
Expand All @@ -44,18 +49,21 @@ on:
- 'tests/k6/tests/graphql/performance/graphql-search.js'
- 'tests/k6/tests/serviceowner/performance/create-transmissions.js'

run-name: ${{ inputs.tag }} vus ${{ inputs.vus }} duration ${{ inputs.duration }}
run-name: ${{ inputs.tag }} ${{ inputs.vus }}/${{ inputs.duration }}/${{ inputs.parallelism }}
jobs:
k6-performance:
name: "Run K6 performance test"
uses: ./.github/workflows/workflow-run-k6-performance.yml
secrets:
TOKEN_GENERATOR_USERNAME: ${{ secrets.TOKEN_GENERATOR_USERNAME }}
TOKEN_GENERATOR_PASSWORD: ${{ secrets.TOKEN_GENERATOR_PASSWORD }}
AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }}
AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }}
with:
environment: ${{ inputs.environment }}
apiVersion: ${{ inputs.apiVersion }}
testSuitePath: ${{ inputs.testSuitePath }}
vus: ${{ fromJson(inputs.vus) }}
duration: ${{ inputs.duration }}
parallelism: ${{ fromJson(inputs.parallelism) }}

48 changes: 48 additions & 0 deletions .github/workflows/workflow-run-k6-ci-cd-yt01.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
name: Run K6 performance tests ci/cd yt01

on:
workflow_call:
inputs:
apiVersion:
required: true
type: string
environment:
required: true
type: string
testSuitePath:
required: true
type: string
vus:
required: true
type: number
duration:
required: true
type: string
secrets:
TOKEN_GENERATOR_USERNAME:
required: true
TOKEN_GENERATOR_PASSWORD:
required: true
jobs:
k6-test:
runs-on: ubuntu-latest
environment: ${{ inputs.environment }}
dagfinno marked this conversation as resolved.
Show resolved Hide resolved
permissions:
checks: write
pull-requests: write
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup k6
uses: grafana/setup-k6-action@v1
- name: Run K6 tests (${{ inputs.testSuitePath }})
run: |
echo "Running k6 test suite ${{ inputs.testSuitePath }} with ${{ inputs.vus }} VUs for ${{ inputs.duration }}"
k6 run ${{ inputs.testSuitePath }} --quiet --log-output=stdout --include-system-env-vars \
--vus=${{ inputs.vus }} --duration=${{ inputs.duration }} --out csv=./results.csv
grep http_req_duration ./results.csv | sort --field-separator=',' --key=3 -nr | head -10
env:
API_ENVIRONMENT: ${{ inputs.environment }}
API_VERSION: ${{ inputs.apiVersion }}
TOKEN_GENERATOR_USERNAME: ${{ secrets.TOKEN_GENERATOR_USERNAME }}
TOKEN_GENERATOR_PASSWORD: ${{ secrets.TOKEN_GENERATOR_PASSWORD }}
45 changes: 38 additions & 7 deletions .github/workflows/workflow-run-k6-performance.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,29 +18,60 @@ on:
duration:
required: true
type: string
parallelism:
required: true
type: number
secrets:
TOKEN_GENERATOR_USERNAME:
required: true
TOKEN_GENERATOR_PASSWORD:
required: true
AZURE_CLIENT_ID:
required: true
AZURE_TENANT_ID:
required: true
permissions:
id-token: write
contents: read
jobs:
k6-test:
runs-on: ubuntu-latest
environment: ${{ inputs.environment }}
permissions:
checks: write
pull-requests: write
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: OIDC Login to Azure Public Cloud
uses: azure/login@v2
with:
client-id: ${{ secrets.AZURE_CLIENT_ID }}
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
allow-no-subscriptions: true
- name: Populate kubeconfig with k6 context
id: populate_kubeconfig_with_k6_context
shell: bash
run: |
if ! az aks install-cli; then
echo "Failed to install kubectl CLI"
exit 1
fi

if ! az aks get-credentials --resource-group k6tests-rg --name k6tests-cluster; then
echo "Failed to populate kubeconfig"
exit 1
fi

if ! kubelogin convert-kubeconfig -l azurecli; then
echo "Failed to convert kubeconfig"
exit 1
fi
- name: Setup k6
uses: grafana/setup-k6-action@v1
- name: Run K6 tests (${{ inputs.testSuitePath }})
run: |
echo "Running k6 test suite ${{ inputs.testSuitePath }} with ${{ inputs.vus }} VUs for ${{ inputs.duration }}"
k6 run ${{ inputs.testSuitePath }} --quiet --log-output=stdout --include-system-env-vars \
--vus=${{ inputs.vus }} --duration=${{ inputs.duration }} --out csv=./results.csv
grep http_req_duration ./results.csv | sort --field-separator=',' --key=3 -nr | head -10
echo "Running k6 test suite ${{ inputs.testSuitePath }} with ${{ inputs.vus }} VUs for ${{ inputs.duration }} on ${{ inputs.parallelism }} parallelism"
k6configName=$(basename "${{ inputs.testSuitePath }}" .js)
k6configName="k6-${k6configName}"
./tests/k6/tests/scripts/run-test-in-k8s.sh -f ${{ inputs.testSuitePath }} -c $k6configName -n $k6configName -v ${{ inputs.vus }} -d ${{ inputs.duration }} -p ${{ inputs.parallelism }}
dagfinno marked this conversation as resolved.
Show resolved Hide resolved
env:
API_ENVIRONMENT: ${{ inputs.environment }}
API_VERSION: ${{ inputs.apiVersion }}
Expand Down
20 changes: 5 additions & 15 deletions tests/k6/tests/enduser/performance/enduser-search.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { enduserSearch } from '../../performancetest_common/simpleSearch.js'
import { getDefaultThresholds } from '../../performancetest_common/getDefaultThresholds.js';
import { endUsers } from '../../performancetest_common/readTestdata.js';
import { validateTestData } from '../../performancetest_common/readTestdata.js';
export { setup as setup } from '../../performancetest_common/readTestdata.js';

const isSingleUserMode = (__ENV.isSingleUserMode ?? 'false') === 'true';
const traceCalls = (__ENV.traceCalls ?? 'false') === 'true';

export let options = {
Expand All @@ -19,18 +19,8 @@ export let options = {
])
};

export default function() {
if (!endUsers || endUsers.length === 0) {
throw new Error('No end users loaded for testing');
}

if (isSingleUserMode) {
enduserSearch(endUsers[0], traceCalls);
}
else {
for (let i = 0; i < endUsers.length; i++) {
enduserSearch(endUsers[i], traceCalls);
}
}
export default function(data) {
const { endUsers, index } = validateTestData(data);
enduserSearch(endUsers[index], traceCalls);
}

Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export { setup as setup } from '../../performancetest_common/readTestdata.js';
import { default as run } from "./enduser-search.js";

export let options = {
Expand Down
23 changes: 5 additions & 18 deletions tests/k6/tests/graphql/performance/graphql-search.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,11 @@
* The performance test for GraphQL search.
* Run: k6 run tests/k6/tests/graphql/performance/graphql-search.js --vus 1 --iterations 1 -e env=yt01
*/

import { getDefaultThresholds } from '../../performancetest_common/getDefaultThresholds.js';
import { endUsers } from '../../performancetest_common/readTestdata.js';
import { validateTestData } from '../../performancetest_common/readTestdata.js';
import { graphqlSearch } from "../../performancetest_common/simpleSearch.js";
export { setup as setup } from '../../performancetest_common/readTestdata.js';

const isSingleUserMode = (__ENV.isSingleUserMode ?? 'false') === 'true';
const traceCalls = (__ENV.traceCalls ?? 'false') === 'true';


Expand All @@ -22,21 +21,9 @@ export const options = {
thresholds: getDefaultThresholds(['http_req_duration', 'http_reqs'],['graphql search'])
};

/**
* The default function for the performance test for GraphQL search.
*/
export default function() {
if (!endUsers || endUsers.length === 0) {
throw new Error('No end users loaded for testing');
}
if (isSingleUserMode) {
graphqlSearch(endUsers[0], traceCalls);
}
else {
for (let i = 0; i < endUsers.length; i++) {
graphqlSearch(endUsers[i], traceCalls);
}
}
export default function(data) {
const { endUsers, index } = validateTestData(data);
graphqlSearch(endUsers[index], traceCalls);
}


50 changes: 48 additions & 2 deletions tests/k6/tests/performancetest_common/readTestdata.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
*/

import papaparse from 'https://jslib.k6.io/papaparse/5.1.1/index.js';
import { SharedArray } from "k6/data";

import { SharedArray } from 'k6/data';
import exec from 'k6/execution';
/**
* Function to read the CSV file specified by the filename parameter.
* @param {} filename
Expand Down Expand Up @@ -52,4 +52,50 @@ export const endUsers = new SharedArray('endUsers', function () {
return readCsv(filenameEndusers);
});

function endUsersPart(totalVus, vuId) {
const endUsersLength = endUsers.length;
if (totalVus == 1) {
return endUsers.slice(0, endUsersLength);
}
let usersPerVU = Math.floor(endUsersLength / totalVus);
let extras = endUsersLength % totalVus;
let ixStart = (vuId-1) * usersPerVU;
if (vuId <= extras) {
usersPerVU++;
ixStart += vuId - 1;
}
else {
ixStart += extras;
}
return endUsers.slice(ixStart, ixStart + usersPerVU);
}

export function setup() {
const totalVus = exec.test.options.scenarios.default.vus;
let parts = [];
for (let i = 1; i <= totalVus; i++) {
parts.push(endUsersPart(totalVus, i));
}
return parts;
}

export function validateTestData(data, serviceOwners=null) {
if (!Array.isArray(data) || !data[exec.vu.idInTest - 1]) {
throw new Error('Invalid data structure: expected array of end users');
}
const myEndUsers = data[exec.vu.idInTest - 1];
if (!Array.isArray(myEndUsers) || myEndUsers.length === 0) {
throw new Error('Invalid end users array: expected non-empty array');
}
if (serviceOwners !== null) {
if (!Array.isArray(serviceOwners) || serviceOwners.length === 0) {
throw new Error('Invalid service owners array: expected non-empty array');
}
}
return {
endUsers: myEndUsers,
index: exec.vu.iterationInInstance % myEndUsers.length
};
}


30 changes: 21 additions & 9 deletions tests/k6/tests/scripts/run-test-in-k8s.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

tokengenuser=${TOKEN_GENERATOR_USERNAME}
tokengenpasswd=${TOKEN_GENERATOR_PASSWORD}
failed=0

kubectl config set-context --current --namespace=dialogporten

# Validate required environment variables
if [ -z "$TOKEN_GENERATOR_USERNAME" ] || [ -z "$TOKEN_GENERATOR_PASSWORD" ]; then
Expand All @@ -27,17 +30,23 @@ print_logs() {
K8S_CONTEXT="${K8S_CONTEXT:-k6tests-cluster}"
K8S_NAMESPACE="${K8S_NAMESPACE:-default}"
LOG_TIMEOUT="${LOG_TIMEOUT:-60}"

# Verify kubectl access
if ! kubectl --context "$K8S_CONTEXT" -n "$K8S_NAMESPACE" get pods &>/dev/null; then
if ! kubectl get pods &>/dev/null; then
echo "Error: Failed to access Kubernetes cluster"
return 1
fi
dagfinno marked this conversation as resolved.
Show resolved Hide resolved
for pod in $(kubectl --context "$K8S_CONTEXT" -n "$K8S_NAMESPACE" get pods -l "$POD_LABEL" -o name); do
for pod in $(kubectl get pods -l "$POD_LABEL" -o name); do
if [[ $pod != *"initializer"* ]]; then
echo ---------------------------
echo $pod
echo ---------------------------
kubectl --context "$K8S_CONTEXT" -n "$K8S_NAMESPACE" logs --tail=-1 $pod
kubectl logs --tail=-1 $pod
status=`kubectl get $pod -o jsonpath='{.status.phase}'`
if [ "$status" != "Succeeded" ]; then
failed=1
fi
echo
fi
done
}
Expand Down Expand Up @@ -114,8 +123,9 @@ apiVersion: k6.io/v1alpha1
kind: TestRun
metadata:
name: $name
namespace: dialogporten
spec:
arguments: --out experimental-prometheus-rw --vus=$vus --duration=$duration
arguments: --out experimental-prometheus-rw --vus=$vus --duration=$duration --tag testid=$testid --log-output=none
parallelism: $parallelism
script:
configMap:
Expand All @@ -130,22 +140,24 @@ spec:
metadata:
labels:
k6-test: $name
resources:
requests:
memory: "200Mi"
dagfinno marked this conversation as resolved.
Show resolved Hide resolved

EOF
# Apply the config.yml configuration
kubectl apply -f config.yml

# Wait for the job to finish
wait_timeout="${duration}100s"
kubectl --context k6tests-cluster wait --for=jsonpath='{.status.stage}'=finished testrun/$name --timeout=$wait_timeout

kubectl wait --for=jsonpath='{.status.stage}'=finished testrun/$name --timeout=$wait_timeout
# Print the logs of the pods
print_logs

cleanup() {
local exit_code=$?
echo "Cleaning up resources..."

local exit_code=$failed
echo "Sleeping for 15s and then cleaning up resources..."
sleep 15
if [ -f "config.yml" ]; then
kubectl delete -f config.yml --ignore-not-found || true
rm -f config.yml
Expand Down
Loading
Loading