diff --git a/docs/sources/v0.47.x/examples/oauth-authentication.md b/docs/sources/v0.47.x/examples/oauth-authentication.md index e2f4154bdf..d360eaca65 100644 --- a/docs/sources/v0.47.x/examples/oauth-authentication.md +++ b/docs/sources/v0.47.x/examples/oauth-authentication.md @@ -61,6 +61,207 @@ export function authenticateUsingAzure(tenantId, clientId, clientSecret, scope, {{< /code >}} +### Azure B2C + +The following example shows how you can authenticate with Azure B2C using the [Client Credentials Flow](https://docs.microsoft.com/en-us/azure/active-directory-b2c/active-directory-b2c-reference-oauth-code#client-credentials-flow). + +This example is based on a JMeter example found at the [azure-ad-b2c/load-tests](https://github.com/azure-ad-b2c/load-tests) repository. + +To use this script, you need to: + +1. [Set up your own Azure B2C tenant](https://learn.microsoft.com/en-us/azure/active-directory-b2c/tutorial-create-tenant) + * Copy the tenant name, it will be used in your test script. +1. [Register a web application](https://learn.microsoft.com/en-us/azure/active-directory-b2c/tutorial-register-applications?tabs=app-reg-ga) + * Register a single page application with the redirect URL of: https://jwt.ms. That's needed for the flow to receive a token. + * After the creation, you can get the Application (client) ID, and the Directory (tenant) ID. Copy both of them, they'll be used in your test script. +1. [Create a user flow so that you can sign up and create a user](https://docs.microsoft.com/en-us/azure/active-directory-b2c/tutorial-create-user-flows) + * Create a new user, and copy the username and password. They'll be used in the test script. + +You can find the settings in the B2C settings in the Azure portal if you need to refer to them later on. Make sure to fill out all the variables for the `B2CGraphSettings` object, as well as replace `USERNAME` and `PASSWORD` in `export default function`. + +{{< code >}} + +```javascript +import http from "k6/http"; +import crypto from "k6/crypto"; +import { randomString } from "https://jslib.k6.io/k6-utils/1.2.0/index.js"; + +const B2CGraphSettings = { + B2C: { + client_id: "", // Application ID in Azure + user_flow_name: "", + tenant_id: "", // Directory ID in Azure + tenant_name: "", + scope: "openid", + redirect_url: "https://jwt.ms", + }, +}; + +/** + * Authenticate using OAuth against Azure B2C + * @function + * @param {string} username - Username of the user to authenticate + * @param {string} password + * @return {string} id_token + */ +export function GetB2cIdToken(username, password) { + const state = GetState(); + SelfAsserted(state, username, password); + const code = CombinedSigninAndSignup(state); + return GetToken(code, state.codeVerifier); +} + +/** + * @typedef {object} b2cStateProperties + * @property {string} csrfToken + * @property {string} stateProperty + * @property {string} codeVerifier + * + */ + +/** + * Get the id token from Azure B2C + * @function + * @param {string} code + * @returns {string} id_token + */ +const GetToken = (code, codeVerifier) => { + const url = + `https://${B2CGraphSettings.B2C.tenant_name}.b2clogin.com/${B2CGraphSettings.B2C.tenant_id}` + + `/oauth2/v2.0/token` + + `?p=${B2CGraphSettings.B2C.user_flow_name}` + + `&client_id=${B2CGraphSettings.B2C.client_id}` + + `&grant_type=authorization_code` + + `&scope=${B2CGraphSettings.B2C.scope}` + + `&code=${code}` + + `&redirect_uri=${B2CGraphSettings.B2C.redirect_url}` + + `&code_verifier=${codeVerifier}`; + + const response = http.post(url, "", { + tags: { + b2c_login: "GetToken", + }, + }); + + return JSON.parse(response.body).id_token; +}; + +/** + * Signs in the user using the CombinedSigninAndSignup policy + * extraqct B2C code from response + * @function + * @param {b2cStateProperties} state + * @returns {string} code + */ +const CombinedSigninAndSignup = (state) => { + const url = + `https://${B2CGraphSettings.B2C.tenant_name}.b2clogin.com/${B2CGraphSettings.B2C.tenant_name}.onmicrosoft.com` + + `/${B2CGraphSettings.B2C.user_flow_name}/api/CombinedSigninAndSignup/confirmed` + + `?csrf_token=${state.csrfToken}` + + `&rememberMe=false` + + `&tx=StateProperties=${state.stateProperty}` + + `&p=${B2CGraphSettings.B2C.user_flow_name}`; + + const response = http.get(url, "", { + tags: { + b2c_login: "CombinedSigninAndSignup", + }, + }); + const codeRegex = '.*code=([^"]*)'; + return response.url.match(codeRegex)[1]; +}; + +/** + * Signs in the user using the SelfAsserted policy + * @function + * @param {b2cStateProperties} state + * @param {string} username + * @param {string} password + */ +const SelfAsserted = (state, username, password) => { + const url = + `https://${B2CGraphSettings.B2C.tenant_name}.b2clogin.com/${B2CGraphSettings.B2C.tenant_id}` + + `/${B2CGraphSettings.B2C.user_flow_name}/SelfAsserted` + + `?tx=StateProperties=${state.stateProperty}` + + `&p=${B2CGraphSettings.B2C.user_flow_name}` + + `&request_type=RESPONSE` + + `&email=${username}` + + `&password=${password}`; + + const params = { + headers: { + "X-CSRF-TOKEN": `${state.csrfToken}`, + }, + tags: { + b2c_login: "SelfAsserted", + }, + }; + http.post(url, "", params); +}; + +/** + * Calls the B2C login page to get the state property + * @function + * @returns {b2cStateProperties} b2cState + */ +const GetState = () => { + const nonce = randomString(50); + const challenge = crypto.sha256(nonce.toString(), "base64rawurl"); + + const url = + `https://${B2CGraphSettings.B2C.tenant_name}.b2clogin.com` + + `/${B2CGraphSettings.B2C.tenant_id}/oauth2/v2.0/authorize?` + + `p=${B2CGraphSettings.B2C.user_flow_name}` + + `&client_id=${B2CGraphSettings.B2C.client_id}` + + `&nonce=${nonce}` + + `&redirect_uri=${B2CGraphSettings.B2C.redirect_url}` + + `&scope=${B2CGraphSettings.B2C.scope}` + + `&response_type=code` + + `&prompt=login` + + `&code_challenge_method=S256` + + `&code_challenge=${challenge}` + + `&response_mode=fragment`; + + const response = http.get(url, "", { + tags: { + b2c_login: "GetCookyAndState", + }, + }); + + const vuJar = http.cookieJar(); + const responseCookies = vuJar.cookiesForURL(response.url); + + const b2cState = {}; + b2cState.codeVerifier = nonce; + b2cState.csrfToken = responseCookies["x-ms-cpim-csrf"][0]; + b2cState.stateProperty = response.body.match('.*StateProperties=([^"]*)')[1]; + return b2cState; +}; + +/** + * Helper function to get the authorization header for a user + * @param {user} user + * @returns {object} httpsOptions + */ +export const GetAuthorizationHeaderForUser = (user) => { + const token = GetB2cIdToken(user.username, user.password); + + return { + headers: { + "Content-Type": "application/json", + Authorization: "Bearer " + token, + }, + }; +}; + +export default function () { + const token = GetB2cIdToken("USERNAME", "PASSWORD"); + console.log(token); +} +``` + +{{< /code >}} + ### Okta {{< code >}} diff --git a/docs/sources/v0.47.x/javascript-api/xk6-disruptor/faults/_index.md b/docs/sources/v0.47.x/javascript-api/xk6-disruptor/faults/_index.md index 0dd1d0a6f0..248ae68233 100644 --- a/docs/sources/v0.47.x/javascript-api/xk6-disruptor/faults/_index.md +++ b/docs/sources/v0.47.x/javascript-api/xk6-disruptor/faults/_index.md @@ -1,7 +1,7 @@ --- title: 'Faults' excerpt: 'xk6-disruptor: Fault Description' -weight: 01 +weight: 100 --- # Faults @@ -12,3 +12,4 @@ A fault is as an abnormal condition that affects a system component and which ma | ------------------------------------------------------------------------------------ | ------------------------------------------- | | [gRPC Fault](https://grafana.com/docs/k6//javascript-api/xk6-disruptor/faults/grpc) | Fault affecting gRPC requests from a target | | [HTTP Fault](https://grafana.com/docs/k6//javascript-api/xk6-disruptor/faults/http) | Fault affecting HTTP requests from a target | +| [Pod Termination Fault](https://grafana.com/docs/k6//javascript-api/xk6-disruptor/faults/pod-termination) | Fault terminating a number of target Pods | diff --git a/docs/sources/v0.47.x/javascript-api/xk6-disruptor/faults/pod-termination.md b/docs/sources/v0.47.x/javascript-api/xk6-disruptor/faults/pod-termination.md new file mode 100644 index 0000000000..03c234e764 --- /dev/null +++ b/docs/sources/v0.47.x/javascript-api/xk6-disruptor/faults/pod-termination.md @@ -0,0 +1,33 @@ +--- +title: 'Pod Termination' +description: 'xk6-disruptor: Pod Termination Fault attributes' +weight: 03 +--- + +# Pod Termination + +A Pod Termination Fault allows terminating either a fixed number or a percentage of the pods that matching a selector or back a service. + +A Pod Termination fault is defined by the following attributes: + +| Attribute | Type | Description | +| ------------- | ------ | --------| +| count | integer or percentage | the number of pods to be terminated. It can be specified as a integer number or as a percentage, for example `30%`, that defines the fraction of target pods to be terminated| + +{{% admonition type="note" %}} + +If the count is a percentage and there are no enough elements in the target pod list, the number is rounded up. +For example '25%' of a list of 2 target pods will terminate one pod. +If the list of target pods is not empty, at least one pod is always terminated. + +{{% /admonition %}} + +## Example + +This example defines a PorTermination fault that will terminate `30%` of target pods + +```javascript +const fault = { + count: '30%' +}; +``` diff --git a/docs/sources/v0.47.x/javascript-api/xk6-disruptor/get-started/_index.md b/docs/sources/v0.47.x/javascript-api/xk6-disruptor/get-started/_index.md index f1ce26ed41..c2203dd5df 100644 --- a/docs/sources/v0.47.x/javascript-api/xk6-disruptor/get-started/_index.md +++ b/docs/sources/v0.47.x/javascript-api/xk6-disruptor/get-started/_index.md @@ -2,7 +2,6 @@ title: 'Get started' excerpt: 'xk6-disruptor is an extension that adds fault injection capabilities to k6. Start here to learn the basics and how to use the disruptor' weight: 01 -weight: 01 --- # Get started diff --git a/docs/sources/v0.47.x/javascript-api/xk6-disruptor/poddisruptor/_index.md b/docs/sources/v0.47.x/javascript-api/xk6-disruptor/poddisruptor/_index.md index 9230046217..f906e4c55d 100644 --- a/docs/sources/v0.47.x/javascript-api/xk6-disruptor/poddisruptor/_index.md +++ b/docs/sources/v0.47.x/javascript-api/xk6-disruptor/poddisruptor/_index.md @@ -1,7 +1,7 @@ --- title: 'PodDisruptor' excerpt: 'xk6-disruptor: PodDisruptor class' -weight: 02 +weight: 200 --- # PodDisruptor @@ -17,6 +17,7 @@ To construct a `PodDisruptor`, use the [PodDisruptor() constructor](https://graf | [PodDisruptor.injectGrpcFaults()](https://grafana.com/docs/k6//javascript-api/xk6-disruptor/poddisruptor/injectgrpcfaults) | Inject [gRPC faults](https://grafana.com/docs/k6//javascript-api/xk6-disruptor/faults/grpc) in the target Pods | | [PodDisruptor.injectHTTPFaults()](https://grafana.com/docs/k6//javascript-api/xk6-disruptor/poddisruptor/injecthttpfaults) | Inject [HTTP faults](https://grafana.com/docs/k6//javascript-api/xk6-disruptor/faults/http) in the target Pods | | PodDisruptor.targets() | Returns the list of target Pods of the PodDisruptor | +| [PodDisruptor.terminatePods()](https://grafana.com/docs/k6//javascript-api/xk6-disruptor/poddisruptor/terminate-pods) | executes a [Pod Termination fault](https://grafana.com/docs/k6//javascript-api/xk6-disruptor/faults/pod-termination) in the target Pods | ## Example diff --git a/docs/sources/v0.47.x/javascript-api/xk6-disruptor/poddisruptor/terminate-pods.md b/docs/sources/v0.47.x/javascript-api/xk6-disruptor/poddisruptor/terminate-pods.md new file mode 100644 index 0000000000..1ba8758ed2 --- /dev/null +++ b/docs/sources/v0.47.x/javascript-api/xk6-disruptor/poddisruptor/terminate-pods.md @@ -0,0 +1,23 @@ +--- +title: 'terminatePods()' +description: 'xk6-disruptor: PodDisruptor.terminatePods method' +--- + +# terminatePods() + +`terminatePods` terminates a number of the pods matching the selector configured in the PodDisruptor. + +| Parameter | Type | Description | +| --------- | ------ |------- | +| fault | object | description of the [Pod Termination fault](https://grafana.com/docs/k6//javascript-api/xk6-disruptor/faults/pod-termination) | + +## Example + + + +```javascript + const fault = { + count: 2, + } + disruptor.terminatePods(fault) +``` diff --git a/docs/sources/v0.47.x/javascript-api/xk6-disruptor/servicedisruptor/_index.md b/docs/sources/v0.47.x/javascript-api/xk6-disruptor/servicedisruptor/_index.md index fbc0f82ddd..2d6df244ee 100644 --- a/docs/sources/v0.47.x/javascript-api/xk6-disruptor/servicedisruptor/_index.md +++ b/docs/sources/v0.47.x/javascript-api/xk6-disruptor/servicedisruptor/_index.md @@ -1,7 +1,7 @@ --- title: 'ServiceDisruptor' excerpt: 'xk6-disruptor: ServiceDisruptor class' -weight: 03 +weight: 300 --- # ServiceDisruptor @@ -16,6 +16,8 @@ To construct a `ServiceDisruptor`, use the [ServiceDisruptor() constructor](http | ----------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------- | | [ServiceDisruptor.injectGrpcFaults()](https://grafana.com/docs/k6//javascript-api/xk6-disruptor/servicedisruptor/injectgrpcfaults) | Inject [gRPC faults](https://grafana.com/docs/k6//javascript-api/xk6-disruptor/faults/grpc) in the target Pods | | [ServiceDisruptor.injectHTTPFaults()](https://grafana.com/docs/k6//javascript-api/xk6-disruptor/servicedisruptor/injecthttpfaults) | Inject [HTTTP faults](https://grafana.com/docs/k6//javascript-api/xk6-disruptor/faults/http) in the target Pods | +| ServiceDisruptor.targets() | Returns the list of target Pods of the ServiceDisruptor | +| [ServiceDisruptor.terminatePods()](https://grafana.com/docs/k6//javascript-api/xk6-disruptor/servicedisruptor/terminate-pods) | executes a [Pod Termination fault](https://grafana.com/docs/k6//javascript-api/xk6-disruptor/faults/pod-termination) in the target Pods| ## Example diff --git a/docs/sources/v0.47.x/javascript-api/xk6-disruptor/servicedisruptor/terminate-pods.md b/docs/sources/v0.47.x/javascript-api/xk6-disruptor/servicedisruptor/terminate-pods.md new file mode 100644 index 0000000000..73034656db --- /dev/null +++ b/docs/sources/v0.47.x/javascript-api/xk6-disruptor/servicedisruptor/terminate-pods.md @@ -0,0 +1,23 @@ +--- +title: 'terminatePods()' +description: 'xk6-disruptor: ServiceDisruptor.terminatePods method' +--- + +# terminatePods() + +`terminatePods` terminates a number of pods that belong to the service specified in the ServiceDisruptor. + +| Parameter | Type | Description | +| --------- | ------ |------- | +| fault | object | description of the [Pod Termination fault](https://grafana.com/docs/k6//javascript-api/xk6-disruptor/faults/pod-termination) | + +## Example + + + +```javascript + const fault = { + count: 2, + } + disruptor.terminatePods(fault) +``` diff --git a/docs/sources/v0.47.x/results-output/end-of-test/custom-summary.md b/docs/sources/v0.47.x/results-output/end-of-test/custom-summary.md index 098e4d35ec..6fd2c13bc3 100644 --- a/docs/sources/v0.47.x/results-output/end-of-test/custom-summary.md +++ b/docs/sources/v0.47.x/results-output/end-of-test/custom-summary.md @@ -72,7 +72,7 @@ They determine where k6 displays or saves the content: The value of a key can have a type of either `string` or [`ArrayBuffer`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ArrayBuffer). -You can return mutiple summary outputs in a script. +You can return multiple summary outputs in a script. As an example, this `return` statement sends a report to standard output and writes the `data` object to a JSON file. {{< code >}} diff --git a/docs/sources/v0.47.x/using-k6/scenarios/executors/shared-iterations.md b/docs/sources/v0.47.x/using-k6/scenarios/executors/shared-iterations.md index 9ee6a3687d..c52a22b86a 100644 --- a/docs/sources/v0.47.x/using-k6/scenarios/executors/shared-iterations.md +++ b/docs/sources/v0.47.x/using-k6/scenarios/executors/shared-iterations.md @@ -9,7 +9,7 @@ weight: 01 The `shared-iterations` executor shares iterations between the number of VUs. The test ends once k6 executes all iterations. -For a shortcut to this executor, use the [vus](https://grafana.com/docs/k6//using-k6/k6-options/reference#vus) and [iterations](https://grafana.com/docs/k6//using-k6/k6-options/reference#iterations) options. +For a shortcut to this executor, use the [`vus`](https://grafana.com/docs/k6//using-k6/k6-options/reference#vus) and [`iterations`](https://grafana.com/docs/k6//using-k6/k6-options/reference#iterations) options. {{% admonition type="note" %}} @@ -37,7 +37,7 @@ This executor is suitable when you want a specific number of VUs to complete a f number of total iterations, and the amount of iterations per VU is unimportant. If the **time to complete** a number of test iterations is your concern, this executor should perform best. -An example use case is for quick performance tests in the developement build cycle. +An example use case is for quick performance tests in the development build cycle. As developers make changes, they might run the test against the local code to test for performance regressions. Thus the executor works well with a _shift-left_ policy, where emphasizes testing performance early in the development cycle, when the cost of a fix is lowest.