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(performance): run tests in k8s #1660

Merged
merged 24 commits into from
Jan 14, 2025
Merged
Show file tree
Hide file tree
Changes from 18 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
2948ac7
utkast til test av post transmission
dagfinno Dec 17, 2024
c767619
Added script to run tests from k8s and file to generate tokens on the…
dagfinno Jan 7, 2025
5a11b91
export the fetchToken function
dagfinno Jan 7, 2025
b415481
Create tokens on the fly
dagfinno Jan 7, 2025
5b959ba
fix workflows
dagfinno Jan 7, 2025
43f145b
do not use endUsersWithTokens anymore
dagfinno Jan 7, 2025
63d32ca
do not use endUsersWithTokens anymore
dagfinno Jan 7, 2025
ba5ff24
removed some debug
dagfinno Jan 7, 2025
80c4801
TTL env variable and added create-transmissions to dispatch menu
dagfinno Jan 7, 2025
aa658af
configurable number og transmissions per thread
dagfinno Jan 8, 2025
96adc8d
Merge branch 'main' into performance/create-transmissions
dagfinno Jan 8, 2025
90f408b
fix according to sonarqube
dagfinno Jan 8, 2025
b98c04c
Merge branch 'performance/create-transmissions' of github.com:digdir/…
dagfinno Jan 8, 2025
776b76b
fixes suggested by coderabbit
dagfinno Jan 8, 2025
61ac853
fixes suggested by coderabbit
dagfinno Jan 8, 2025
0d9a87f
Merge branch 'main' into performance/create-transmissions
dagfinno Jan 8, 2025
6367576
added testid
dagfinno Jan 8, 2025
a485da2
Merge branch 'performance/create-transmissions' of github.com:digdir/…
dagfinno Jan 8, 2025
4b85a6d
Merge branch 'main' into performance/create-transmissions
dagfinno Jan 9, 2025
632ee73
Fix readmes and remove generate_tokens.sh
dagfinno Jan 14, 2025
69625ad
Merge branch 'main' into performance/create-transmissions
dagfinno Jan 14, 2025
9ca52eb
fixed relative paths
dagfinno Jan 14, 2025
d8d22f7
Merge branch 'main' into performance/create-transmissions
dagfinno Jan 14, 2025
2b61278
Merge branch 'main' into performance/create-transmissions
dagfinno Jan 14, 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
5 changes: 0 additions & 5 deletions .github/workflows/ci-cd-yt01.yml
Original file line number Diff line number Diff line change
Expand Up @@ -134,9 +134,6 @@ jobs:
secrets:
TOKEN_GENERATOR_USERNAME: ${{ secrets.TOKEN_GENERATOR_USERNAME }}
TOKEN_GENERATOR_PASSWORD: ${{ secrets.TOKEN_GENERATOR_PASSWORD }}
K6_CLOUD_TOKEN: ${{ secrets.K6_CLOUD_TOKEN }}
K6_CLOUD_PROJECT_ID: ${{ secrets.K6_CLOUD_PROJECT_ID }}

strategy:
max-parallel: 1
matrix:
Expand All @@ -150,8 +147,6 @@ jobs:
apiVersion: v1
vus: 1
duration: 30s
tokens: both
numberOfTokens: 100
testSuitePath: ${{ matrix.files }}
permissions:
checks: write
Expand Down
14 changes: 1 addition & 13 deletions .github/workflows/dispatch-k6-performance.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,6 @@ on:
- test
- staging
- yt01
tokens:
description: 'Tokens to generate; for create dialog, search, none, or both'
required: true
default: 'both'
type: choice
options:
- both
- enterprise
- personal
- none
tag:
description: 'tag the performance test'
required: true
Expand All @@ -52,6 +42,7 @@ on:
- 'tests/k6/tests/serviceowner/performance/serviceowner-search.js'
- 'tests/k6/tests/enduser/performance/enduser-search.js'
- '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 }}
jobs:
Expand All @@ -61,13 +52,10 @@ jobs:
secrets:
TOKEN_GENERATOR_USERNAME: ${{ secrets.TOKEN_GENERATOR_USERNAME }}
TOKEN_GENERATOR_PASSWORD: ${{ secrets.TOKEN_GENERATOR_PASSWORD }}
K6_CLOUD_TOKEN: ${{ secrets.K6_CLOUD_TOKEN }}
K6_CLOUD_PROJECT_ID: ${{ secrets.K6_CLOUD_PROJECT_ID }}
with:
environment: ${{ inputs.environment }}
apiVersion: ${{ inputs.apiVersion }}
testSuitePath: ${{ inputs.testSuitePath }}
vus: ${{ fromJson(inputs.vus) }}
duration: ${{ inputs.duration }}
tokens: ${{ inputs.tokens }}

19 changes: 0 additions & 19 deletions .github/workflows/workflow-run-k6-performance.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,27 +18,11 @@ on:
duration:
required: true
type: string
tokens:
required: true
type: string
numberOfTokens:
required: false
type: number
default: 0
ttl:
required: false
type: number
default: 3600
secrets:
TOKEN_GENERATOR_USERNAME:
required: true
TOKEN_GENERATOR_PASSWORD:
required: true
K6_CLOUD_TOKEN:
required: true
K6_CLOUD_PROJECT_ID:
required: true

jobs:
k6-test:
runs-on: ubuntu-latest
Expand All @@ -53,7 +37,6 @@ jobs:
uses: grafana/setup-k6-action@v1
- name: Run K6 tests (${{ inputs.testSuitePath }})
run: |
./tests/k6/tests/scripts/generate_tokens.sh ./tests/k6/tests/performancetest_data ${{ inputs.tokens }} ${{ inputs.numberOfTokens }} ${{ inputs.ttl }}
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
Expand All @@ -63,5 +46,3 @@ jobs:
API_VERSION: ${{ inputs.apiVersion }}
TOKEN_GENERATOR_USERNAME: ${{ secrets.TOKEN_GENERATOR_USERNAME }}
TOKEN_GENERATOR_PASSWORD: ${{ secrets.TOKEN_GENERATOR_PASSWORD }}
K6_CLOUD_TOKEN: ${{ secrets.K6_CLOUD_TOKEN }}
K6_CLOUD_PROJECT_ID: ${{ secrets.K6_CLOUD_PROJECT_ID }}
2 changes: 1 addition & 1 deletion tests/k6/common/token.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ function getCacheKey(tokenType, tokenOptions) {
return `${tokenType}|${tokenOptions.scopes}|${tokenOptions.orgName}|${tokenOptions.orgNo}|${tokenOptions.ssn}`;
}

function fetchToken(url, tokenOptions, type) {
export function fetchToken(url, tokenOptions, type) {
const currentTime = Math.floor(Date.now() / 1000);
const cacheKey = getCacheKey(type, tokenOptions);

Expand Down
10 changes: 5 additions & 5 deletions tests/k6/tests/enduser/performance/enduser-search.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { enduserSearch } from '../../performancetest_common/simpleSearch.js'
import { getDefaultThresholds } from '../../performancetest_common/getDefaultThresholds.js';
import { endUsersWithTokens } from '../../performancetest_common/readTestdata.js';
import { endUsers } from '../../performancetest_common/readTestdata.js';

const isSingleUserMode = (__ENV.isSingleUserMode ?? 'false') === 'true';
const traceCalls = (__ENV.traceCalls ?? 'false') === 'true';
Expand All @@ -20,16 +20,16 @@ export let options = {
};

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

if (isSingleUserMode) {
enduserSearch(endUsersWithTokens[0], traceCalls);
enduserSearch(endUsers[0], traceCalls);
}
else {
for (let i = 0; i < endUsersWithTokens.length; i++) {
enduserSearch(endUsersWithTokens[i], traceCalls);
for (let i = 0; i < endUsers.length; i++) {
enduserSearch(endUsers[i], traceCalls);
}
}
}
Expand Down
4 changes: 2 additions & 2 deletions tests/k6/tests/graphql/performance/graphql-search.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
*/

import { getDefaultThresholds } from '../../performancetest_common/getDefaultThresholds.js';
import { endUsersWithTokens as endUsers } from '../../performancetest_common/readTestdata.js';
import { endUsers } from '../../performancetest_common/readTestdata.js';
dagfinno marked this conversation as resolved.
Show resolved Hide resolved
import { graphqlSearch } from "../../performancetest_common/simpleSearch.js";

const isSingleUserMode = (__ENV.isSingleUserMode ?? 'false') === 'true';
Expand All @@ -17,7 +17,7 @@ const traceCalls = (__ENV.traceCalls ?? 'false') === 'true';
* @property {string[]} summaryTrendStats - The summary trend statistics to include in the test results.
* @property {object} thresholds - The thresholds for the test metrics.
*/
export let options = {
export const options = {
summaryTrendStats: ['avg', 'min', 'med', 'max', 'p(95)', 'p(99)', 'p(99.5)', 'p(99.9)', 'count'],
thresholds: getDefaultThresholds(['http_req_duration', 'http_reqs'],['graphql search'])
};
Expand Down
70 changes: 67 additions & 3 deletions tests/k6/tests/performancetest_common/createDialog.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,23 +6,27 @@ import { describe } from "../../common/describe.js";
import { postSO, purgeSO } from "../../common/request.js";
import { expect } from "../../common/testimports.js";
import dialogToInsert from "../performancetest_data/01-create-dialog.js";
import { default as transmissionToInsert } from "../performancetest_data/create-transmission.js";
import { getEnterpriseToken } from "./getTokens.js";

/**
* Creates a dialog.
*
* @param {Object} serviceOwner - The service owner object.
* @param {Object} endUser - The end user object.
*/

export function createDialog(serviceOwner, endUser, traceCalls) {
var traceparent = uuidv4();

var paramsWithToken = {
headers: {
Authorization: "Bearer " + serviceOwner.token,
Authorization: "Bearer " + getEnterpriseToken(serviceOwner),
traceparent: traceparent
},
tags: { name: 'create dialog' }
};

if (traceCalls) {
paramsWithToken.tags.traceparent = traceparent;
paramsWithToken.tags.enduser = endUser.ssn;
Expand All @@ -32,7 +36,6 @@ export function createDialog(serviceOwner, endUser, traceCalls) {
let r = postSO('dialogs', dialogToInsert(endUser.ssn, endUser.resource), paramsWithToken);
expect(r.status, 'response status').to.equal(201);
});

}

/**
Expand All @@ -45,7 +48,7 @@ export function createAndRemoveDialog(serviceOwner, endUser, traceCalls) {
var traceparent = uuidv4();
var paramsWithToken = {
headers: {
Authorization: "Bearer " + serviceOwner.token,
Authorization: "Bearer " + getEnterpriseToken(serviceOwner),
traceparent: traceparent
},
tags: { name: 'create dialog' }
Expand All @@ -72,3 +75,64 @@ export function createAndRemoveDialog(serviceOwner, endUser, traceCalls) {
}
});
}

/**
* Creates a dialog and add a number of transmissions
*
* @param {Object} serviceOwner - The service owner object.
* @param {Object} endUser - The end user object.
*/
export function createTransmissions(serviceOwner, endUser, traceCalls, numberOfTransmissions, maxTransmissionsInThread, testid) {
let traceparent = uuidv4();

let paramsWithToken = {
headers: {
Authorization: "Bearer " + getEnterpriseToken(serviceOwner),
traceparent: traceparent
},
tags: { name: 'create dialog', testid: testid }
};
if (traceCalls) {
paramsWithToken.tags.traceparent = traceparent;
paramsWithToken.tags.enduser = endUser.ssn;
}

let dialogId = 0;
describe('create dialog', () => {
let r = postSO('dialogs', dialogToInsert(endUser.ssn, endUser.resource), paramsWithToken);
dialogId = r.json();
expect(r.status, 'response status').to.equal(201);
});
dagfinno marked this conversation as resolved.
Show resolved Hide resolved

let relatedTransmissionId = 0;
for (let i = 0; i < numberOfTransmissions; i++) {

relatedTransmissionId = createTransmission(dialogId, relatedTransmissionId, serviceOwner, traceCalls, testid);
// Max transmissions in thread reached, start new thread
if (i%maxTransmissionsInThread === 0) {
relatedTransmissionId = 0;
}
}

}

export function createTransmission(dialogId, relatedTransmissionId, serviceOwner, traceCalls, testid) {
let traceparent = uuidv4();

let paramsWithToken = {
headers: {
Authorization: "Bearer " + getEnterpriseToken(serviceOwner),
traceparent: traceparent
},
tags: { name: 'create transmission', testid: testid }
};

let newRelatedTransmissionId;
describe('create transmission', () => {
let r = postSO('dialogs/' + dialogId + '/transmissions', transmissionToInsert(relatedTransmissionId), paramsWithToken);
expect(r.status, 'response status').to.equal(201);
newRelatedTransmissionId = r.json();
});
dagfinno marked this conversation as resolved.
Show resolved Hide resolved
return newRelatedTransmissionId;
}

23 changes: 23 additions & 0 deletions tests/k6/tests/performancetest_common/getTokens.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { fetchToken } from "../../common/token.js";

const tokenGeneratorEnv = __ENV.API_ENVIRONMENT || 'yt01';
const tokenTtl = __ENV.TTL || 3600;

export function getEnterpriseToken(serviceOwner) {
const tokenOptions = {
scopes: serviceOwner.scopes,
orgName: serviceOwner.org,
orgNo: serviceOwner.orgno
}
const url = `https://altinn-testtools-token-generator.azurewebsites.net/api/GetEnterpriseToken?env=${tokenGeneratorEnv}&scopes=${encodeURIComponent(tokenOptions.scopes)}&org=${tokenOptions.orgName}&orgNo=${tokenOptions.orgNo}&ttl=${tokenTtl}`;
return fetchToken(url, tokenOptions, `service owner (orgno:${tokenOptions.orgNo} orgName:${tokenOptions.orgName} tokenGeneratorEnv:${tokenGeneratorEnv})`);
}
dagfinno marked this conversation as resolved.
Show resolved Hide resolved

export function getPersonalToken(endUser) {
const tokenOptions = {
scopes: endUser.scopes,
ssn: endUser.ssn
}
const url = `https://altinn-testtools-token-generator.azurewebsites.net/api/GetPersonalToken?env=${tokenGeneratorEnv}&scopes=${encodeURIComponent(tokenOptions.scopes)}&pid=${tokenOptions.ssn}&ttl=${tokenTtl}`;
return fetchToken(url, tokenOptions, `end user (ssn:${tokenOptions.ssn}, tokenGeneratorEnv:${tokenGeneratorEnv})`);
}
dagfinno marked this conversation as resolved.
Show resolved Hide resolved
13 changes: 1 addition & 12 deletions tests/k6/tests/performancetest_common/readTestdata.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,11 @@ function readCsv(filename) {
}
}

const filenameServiceowners = '../performancetest_data/.serviceowners-with-tokens.csv';
if (!__ENV.API_ENVIRONMENT) {
throw new Error('API_ENVIRONMENT must be set');
}
const filenameEndusers = `../performancetest_data/endusers-${__ENV.API_ENVIRONMENT}.csv`;
const filenameEndusersWithTokens = '../performancetest_data/.endusers-with-tokens.csv';
const filenameServiceowners = `../performancetest_data/serviceowners-${__ENV.API_ENVIRONMENT}.csv`;

/**
* SharedArray variable that stores the service owners data.
Expand All @@ -53,14 +52,4 @@ export const endUsers = new SharedArray('endUsers', function () {
return readCsv(filenameEndusers);
});

/**
* SharedArray variable that stores the end users with tokens data.
* The data is parsed from the CSV file specified by the filenameEndusersWithTokens variable.
*
* @name endUsersWithTokens
* @type {SharedArray}
*/
export const endUsersWithTokens = new SharedArray('endUsersWithTokens', function () {
return readCsv(filenameEndusersWithTokens);
});

8 changes: 4 additions & 4 deletions tests/k6/tests/performancetest_common/simpleSearch.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { expect, expectStatusFor } from "../../common/testimports.js";
import { describe } from '../../common/describe.js';
import { getEU, postGQ, getSO } from '../../common/request.js';
import { getGraphqlParty } from '../performancetest_data/graphql-search.js';
import { getEnterpriseToken, getPersonalToken } from './getTokens.js';

/**
* Retrieves the content for a dialog.
Expand All @@ -20,7 +21,6 @@ function retrieveDialogContent(response, paramsWithToken, getFunction = getEU) {
if (!items?.length) return;
const dialogId = items[0].id;
if (!dialogId) return;

getContent(dialogId, paramsWithToken, 'get dialog', '', getFunction);
getContentChain(dialogId, paramsWithToken, 'get dialog activities', 'get dialog activity', '/activities/', getFunction);
getContentChain(dialogId, paramsWithToken, 'get seenlogs', 'get seenlog', '/seenlog/', getFunction);
Expand All @@ -45,7 +45,7 @@ export function enduserSearch(enduser, traceCalls) {
var traceparent = uuidv4();
let paramsWithToken = {
headers: {
Authorization: "Bearer " + enduser.token,
Authorization: "Bearer " + getPersonalToken(enduser),
traceparent: traceparent
},
tags: { name: 'enduser search' }
Expand Down Expand Up @@ -132,7 +132,7 @@ export function graphqlSearch(enduser, traceCalls) {
let traceparent = uuidv4();
let paramsWithToken = {
headers: {
Authorization: "Bearer " + enduser.token,
Authorization: "Bearer " + getPersonalToken(enduser),
traceparent: traceparent,
'User-Agent': 'dialogporten-k6-graphql-search'
},
Expand Down Expand Up @@ -160,7 +160,7 @@ export function serviceownerSearch(serviceowner, enduser, tag_name, traceCalls,
let traceparent = uuidv4();
let paramsWithToken = {
headers: {
Authorization: "Bearer " + serviceowner.token,
Authorization: "Bearer " + getEnterpriseToken(serviceowner),
traceparent: traceparent
},
tags: { name: tag_name }
Expand Down
1 change: 1 addition & 0 deletions tests/k6/tests/performancetest_data/01-create-dialog.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,5 +54,6 @@ export default function (endUser, resource) {
let payload = createDialogPayload();
payload.serviceResource = "urn:altinn:resource:" +resource;
payload.party = "urn:altinn:person:identifier-no:" + endUser;

return cleanUp(payload);
}
Loading
Loading