import http from 'k6/http'; import { check, fail, sleep } from 'k6'; import exec from 'k6/execution'; const BASE_URL = (__ENV.BASE_URL || 'https://127.0.0.1:2288').replace(/\/$/, ''); const HOST_HEADER = (__ENV.HOST_HEADER || 'stend.4mont.ru').trim(); const SERVICE_A = __ENV.SERVICE_A || 'termidesk'; const SERVICE_B = __ENV.SERVICE_B || 'vmmanager'; const CLOSE_SESSION = (__ENV.CLOSE_SESSION || '1') !== '0'; const REQ_TIMEOUT = __ENV.REQ_TIMEOUT || '20s'; const users = (__ENV.USERS_CSV || '') .split(';') .map((v) => v.trim()) .filter(Boolean) .map((pair) => { const i = pair.indexOf(':'); return i > 0 ? { username: pair.slice(0, i), password: pair.slice(i + 1) } : null; }) .filter(Boolean); export const options = { scenarios: { burst_100_users: { executor: 'per-vu-iterations', vus: 100, iterations: 1, maxDuration: '3m', }, }, insecureSkipTLSVerify: true, thresholds: { http_req_failed: ['rate<0.2'], }, }; function hdr(extra = {}) { return HOST_HEADER ? { ...extra, Host: HOST_HEADER } : extra; } function getCreds() { if (users.length < 100) fail(`Need at least 100 users in USERS_CSV, got ${users.length}`); const idx = (exec.vu.idInTest || 1) - 1; return users[idx % users.length]; } function login(username, password) { const jar = http.cookieJar(); jar.clear(BASE_URL); const landing = http.get(`${BASE_URL}/`, { redirects: 0, timeout: REQ_TIMEOUT, headers: hdr(), tags: { name: 'GET /' }, }); const csrfFromCookie = (jar.cookiesForURL(BASE_URL).csrf_token || [])[0] || ''; const csrf = csrfFromCookie || ((landing.cookies.csrf_token || [])[0] || {}).value || ''; if (!csrf) fail(`No CSRF for ${username}`); const payload = [ `username=${encodeURIComponent(username)}`, `password=${encodeURIComponent(password)}`, `csrf_token=${encodeURIComponent(csrf)}`, ].join('&'); const loginRes = http.post(`${BASE_URL}/login`, payload, { redirects: 0, timeout: REQ_TIMEOUT, headers: hdr({ 'Content-Type': 'application/x-www-form-urlencoded' }), tags: { name: 'POST /login' }, }); const ok = check(loginRes, { 'login status 303': (r) => r.status === 303 }); if (!ok) fail(`Login failed ${username}, status=${loginRes.status}`); } function openService(slug) { const res = http.get(`${BASE_URL}/go/${slug}`, { redirects: 0, timeout: REQ_TIMEOUT, headers: hdr(), tags: { name: 'GET /go/:slug' }, }); const ok = check(res, { [`open ${slug} -> 303`]: (r) => r.status === 303 }); const location = res.headers.Location || ''; const m = location.match(/\/s\/([0-9a-fA-F-]{36})\//); return { ok, status: res.status, sessionId: m ? m[1] : '', location }; } function closeSession(sessionId) { if (!sessionId || !CLOSE_SESSION) return; http.post(`${BASE_URL}/api/sessions/${sessionId}/close`, null, { redirects: 0, timeout: REQ_TIMEOUT, headers: hdr(), tags: { name: 'POST /api/sessions/:id/close' }, }); } export default function () { const { username, password } = getCreds(); login(username, password); const a = openService(SERVICE_A); sleep(0.1); const b = openService(SERVICE_B); closeSession(a.sessionId); closeSession(b.sessionId); if (!a.ok || !b.ok) { fail(`open failed user=${username} a=${a.status} b=${b.status}`); } }