Hi @eyeveebe
Answer to Your some of your question :
- Yes it works perfectly fine in my local
2.It fails in docker
- All the scripts are similar only the tps(transaction per second) will be different for different scripts
I am attaching all the files.
Folder Structure:
Lets Starts with k6webapp (this is module which has node)
File : app.js
const express=require(“express”);
const k6Router=require(“./k6exector”);
const sampleRouter=require(“./sample”);
const app = express();
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use("/execute/", k6Router);
app.use("/execute/", sampleRouter);
module.exports = app;
File : k6executor.js
const express = require("express");
const router = express.Router();
const { spawn } = require('child_process');
const { ok } = require('assert');
const paths = ['/apps/k6perf/testapi/priceunlock/priceUnlock.js',
'/apps/k6perf/testapi/cancelorder/cancelOrderMainFinal.js',
'/apps/k6perf/testapi/modifypickupcontact/modifyPickupContactFinal.js']
const k6Command = 'k6';
var flag = true;
router.get(
"/all",
function (req, res, next) {
try {
if (flag) {
let k6;
flag=false;
var count=0;
console.log("Path :"+paths)
paths.forEach(
path => {
k6 = spawn(k6Command, ['run', path]);
k6.stdout.on('data', (data) => {
console.log(`stdout from 'k6': ${data}`);
});
k6.stderr.on('data', (data) => {
console.error(`stderr from 'k6': ${data}`);
});
k6.on('close', (code) => {
count=count+1;
console.log("legth: "+paths.length+" count: "+count)
if(paths.length===count){
flag=true;
}
console.log(`child process exited kclea: ${code}`);
});
}
)
res.status(200).send("Perf Test is Triggered")
}else{
res.status(500).send("Perf Test is already triggered It is in Progress Status Wait to run again")
}
} catch (e) {
res.status(500).send("Some Error occured while running perf test")
}
}
);
module.exports = router;
File : sample.js
const express = require("express");
const router = express.Router();
router.get('/sample', (req, res) => {
return res.send(200, { message: 'ok' });
});
module.exports = router;
File : index.js
#!/usr/bin/env node
/**
* Module dependencies.
*/
//import app from "./src/app";
//import http from "http";
const http= require("http");
const app=require("./src/app");
// process.env["NODE_TLS_REJECT_UNAUTHORIZED"] = 0;
/**
* Get port from environment and store in Express.
*/
var port = normalizePort(process.env.PORT || "8080");
app.set("port", port);
/**
* Create HTTP server.
*/
var server = http.createServer(app);
/**
* Listen on provided port, on all network interfaces.
*/
server.listen(port);
server.on("error", onError);
server.on("listening", onListening);
/**
* Normalize a port into a number, string, or false.
*/
function normalizePort(val) {
var port = parseInt(val, 10);
if (isNaN(port)) {
// named pipe
return val;
}
if (port >= 0) {
// port number
return port;
}
return false;
}
function onError(error) {
if (error.syscall !== "listen") {
throw error;
}
var bind = typeof port === "string" ? "Pipe " + port : "Port " + port;
// handle specific listen errors with friendly messages
switch (error.code) {
case "EACCES":
console.error(bind + " requires elevated privileges");
process.exit(1);
break;
case "EADDRINUSE":
console.error(bind + " is already in use");
process.exit(1);
break;
default:
console.error(error);
throw error;
}
}
async function onListening() {
try {
const addr = server.address();
const bind =
typeof addr === "string" ? "pipe " + addr : "port " + addr.port;
console.log(`Server running on ${bind}`);
console.log("info", `"Server running on ${bind}"`);
} catch (e) {
console.error("error", e);
}
}
Files : public->index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="theme-color" content="#000000">
<!--
manifest.json provides metadata used when your web app is added to the
homescreen on Android. See https://developers.google.com/web/fundamentals/engage-and-retain/web-app-manifest/
-->
<link rel="manifest" href="%PUBLIC_URL%/manifest.json">
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
<!--
Notice the use of %PUBLIC_URL% in the tags above.
It will be replaced with the URL of the `public` folder during the build.
Only files inside the `public` folder can be referenced from the HTML.
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>React App</title>
</head>
<body>
<noscript>
You need to enable JavaScript to run this app.
</noscript>
<div id="root"></div>
<!--
This HTML file is a template.
If you open it directly in the browser, you will see an empty page.
You can add webfonts, meta tags, or analytics to this file.
The build step will place the bundled scripts into the <body> tag.
To begin the development, run `npm start` or `yarn start`.
To create a production bundle, use `npm run build` or `yarn build`.
-->
</body>
</html>
File : package.json
{
"name": "k6webapp",
"packageManager": "yarn@3.5.0",
"scripts": {
"dev": "node k6webapp/index.js"
},
"dependencies": {
"child_process": "^1.0.2",
"express": "^4.18.2",
"https": "^1.0.0",
"inquirer": "^9.1.5",
"inquirer-tree-prompt": "^1.1.2",
"prop-types": "^15.8.1",
"react": "16.8.6",
"react-dom": "16.8.6",
"react-scripts": "3.0.1",
"react-select": "^5.7.2"
},
"devDependencies": {
"typescript": "3.3.3"
},
"main": "src/index.js",
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
}
}
Now k6perf :
File : input-> additem-> addPLItemApply.json
{
"order_number": "{P_OrderNumber}",
"order_header_key": "{C_OrderHeaderKey}",
"salesman_id": "{P_SalesmanID}",
"login_id": "{P_LoginID}",
"logged_in_store": "{P_Node}",
"document_type": "0001",
"enterprise_code": "LOWES",
"application_id": "OmniaStore",
"modification_type": "ADD_ITEM",
"transaction_id": "{P_Transanction_Id}",
"notes": {
"note": [
{
"contact_user": "test user",
"note_text": "add #1971085 Performance Test",
"reason_code": "Add PL SOS Item #1971085, qty: 2",
"note_type": "User Action"
}
]
},
"payments": [
{
"action": null,
"credit_card_no": null,
"credit_card_type": null,
"svc_no": null,
"credit_card_name": null,
"payment_type": null,
"first_name": null,
"last_name": null,
"payment_reference_1": null,
"payment_reference_2": null,
"payment_reference_3": null,
"auth_source": "5",
"payment_key": "2023051507541719601175213",
"max_charge_limit": "2159.00",
"authorization_mode": "INIT_AUTH",
"amount": "2077.47",
"reversal_id": "0XX807538XX190851XXXX0XXXXA0001908510413",
"return_code": "9013",
"gc_card_category": null,
"status": "Approved",
"sequence_number": "635687807538",
"card_receive_code": "3",
"transaction_id": "0001908510413",
"pos_identification_code": "3",
"net_return_code": "00",
"authorization_code": "190851",
"prepaid_balance": "0",
"invoice_number": "807538",
"avs_response": "U",
"master_card_classification_id": "U0",
"payment_service_flag": "A",
"cvv2_response": "M",
"commercial_indicator": null,
"suspend_any_more_charges": null,
"sqi": null,
"validation_code": null,
"reason": null,
"person_info_bill_to": null
}
],
"max_prime_line_no": "{P_MaxPrimeLineNo}",
"order_lines": {
"order_line": [
{
"order_line_key": "4742f4cd-d202-42c6-a5f3-338f2bc5fb4a",
"action": "Create",
"condition_variable_2": "",
"delivery_method": "PICK",
"gift_flag": "N",
"item_group_code": "PROD",
"item_type": "SOS",
"ordered_quantity": "2",
"prime_line_no": "{P_PrimeLineNo}",
"fulfillment_location": "6557",
"fulfillment_details": [
{
"product_availability_date": "2023-02-04T00:00:00-06:00",
"estimated_ship_date": "2023-05-15T04:02:02-04:00",
"sequence_number": "1",
"quantity": "1",
"node": {
"node_id": "46",
"node_type": "vendor"
},
"atp_details": [],
"path_location_type": "vendor",
"path_location_company": "vendor"
},
{
"estimated_ship_date": "2023-06-06T00:00:00-06:00",
"sequence_number": "2",
"quantity": "0",
"node": {
"node_id": "6557",
"node_type": "store"
},
"atp_details": [],
"path_location_type": "store",
"path_location_company": "lowes"
}
],
"associate_login_id": "80086390",
"is_epp": "N",
"is_eligible_for_epp": "Y",
"item_classification_type": "SOS",
"model_no": "WTW5105HC",
"item_id": "1971085",
"supplier_item": "WTW5105HC",
"unit_of_measure": "",
"item_desc": "Whirlpool 4.7-cu ft High-Efficiency Top-Load Washer with Pretreat Station - Chrome Shadow",
"unit_price": "943",
"fulfillment_address": {
"is_address_verified": "Y",
"address_key": null,
"address_line_1": "2700 Rainier Avenue South",
"address_line_2": "",
"city": "Seattle",
"state": "WA",
"zipcode": "98144",
"country": "US",
"latitude": "",
"longitude": ""
},
"shipnode_address": {
"address_line_1": "2700 Rainier Avenue South",
"address_line_2": "",
"city": "Seattle",
"state": "WA",
"zipcode": "98144",
"country": "US",
"latitude": "",
"longitude": "",
"extension": null,
"address_type": null,
"first_name": null,
"last_name": null,
"tax_geo_code": null,
"is_address_verified": "Y",
"company_name": null,
"day_phone": null,
"email": null
},
"shipnode_name": "LOWE'S OF Wilkesboro",
"selling_location": "6391",
"vendor": "46",
"brand": "Whirlpool",
"omni_item_id": "1001854990",
"base_price": "943",
"final_price": "943",
"retail_price": "943",
"order_line_reservations": {
"order_line_reservation": [
{
"reservation_id": "20230203152040805_2",
"item_id": "1971085",
"model_no": "WTW5105HC",
"item_type": "SOS",
"unit_of_measure": "",
"node": "6557",
"quantity": "2",
"procurements": []
}
]
},
"tax_exemption_step_id": "",
"tax_exemption_valid_flag": "N",
"epp_applied_quantity": "",
"epp_declined_quantity": "2"
}
]
},
"payment_methods": {
"payment_method": null
}
}
File : input->additem->addPLItemCompute.json
{
"order_number": "{P_OrderNumber}",
"order_header_key": "{C_OrderHeaderKey}",
"salesman_id": "{P_SalesmanID}",
"login_id": "{P_LoginID}",
"logged_in_store": "{P_Node}",
"document_type": "0001",
"enterprise_code": "LOWES",
"application_id": "OmniaStore",
"modification_type": "ADD_ITEM",
"notes": {
"note": [
{
"contact_user": "test user",
"note_text": "add #1971085 Performance Test",
"reason_code": "Add PL SOS Item #1971085, qty: 2",
"note_type": "User Action"
}
]
},
"payments": [],
"max_prime_line_no": "{P_MaxPrimeLineNo}",
"order_lines": {
"order_line": [
{
"order_line_key": "4742f4cd-d202-42c6-a5f3-338f2bc5fb4a",
"action": "Create",
"condition_variable_2": "",
"delivery_method": "PICK",
"gift_flag": "N",
"item_group_code": "PROD",
"item_type": "SOS",
"ordered_quantity": "2",
"prime_line_no": "{P_PrimeLineNo}",
"fulfillment_location": "6557",
"fulfillment_details": [
{
"product_availability_date": "2023-02-04T00:00:00-06:00",
"estimated_ship_date": "2023-05-15T04:02:02-04:00",
"sequence_number": "1",
"quantity": "1",
"node": {
"node_id": "46",
"node_type": "vendor"
},
"atp_details": [],
"path_location_type": "vendor",
"path_location_company": "vendor"
},
{
"estimated_ship_date": "2023-06-06T00:00:00-06:00",
"sequence_number": "2",
"quantity": "0",
"node": {
"node_id": "6557",
"node_type": "store"
},
"atp_details": [],
"path_location_type": "store",
"path_location_company": "lowes"
}
],
"associate_login_id": "80086390",
"is_epp": "N",
"is_eligible_for_epp": "Y",
"item_classification_type": "SOS",
"model_no": "WTW5105HC",
"item_id": "1971085",
"supplier_item": "WTW5105HC",
"unit_of_measure": "",
"item_desc": "Whirlpool 4.7-cu ft High-Efficiency Top-Load Washer with Pretreat Station - Chrome Shadow",
"unit_price": "943",
"fulfillment_address": {
"is_address_verified": "Y",
"address_key": null,
"address_line_1": "2700 Rainier Avenue South",
"address_line_2": "",
"city": "Seattle",
"state": "WA",
"zipcode": "98144",
"country": "US",
"latitude": "",
"longitude": ""
},
"shipnode_address": {
"address_line_1": "2700 Rainier Avenue South",
"address_line_2": "",
"city": "Seattle",
"state": "WA",
"zipcode": "98144",
"country": "US",
"latitude": "",
"longitude": "",
"extension": null,
"address_type": null,
"first_name": null,
"last_name": null,
"tax_geo_code": null,
"is_address_verified": "Y",
"company_name": null,
"day_phone": null,
"email": null
},
"shipnode_name": "LOWE'S OF Wilkesboro",
"selling_location": "6391",
"vendor": "46",
"brand": "Whirlpool",
"omni_item_id": "1001854990",
"base_price": "943",
"final_price": "943",
"retail_price": "943",
"order_line_reservations": {
"order_line_reservation": [
{
"reservation_id": "20230203152040805_2",
"item_id": "1971085",
"model_no": "WTW5105HC",
"item_type": "SOS",
"unit_of_measure": "",
"node": "6557",
"quantity": "2",
"procurements": []
}
]
},
"tax_exemption_step_id": "",
"tax_exemption_valid_flag": "N",
"epp_applied_quantity": "",
"epp_declined_quantity": "2"
}
]
},
"payment_methods": {
"payment_method": null
}
}
File : input->additem->applyHoldAddItemMod.json
{
"order_number": "{P_OrderNumber}",
"enterprise_code": "LOWES",
"document_type": "0001",
"application_id": "OmniaStore",
"login_id": "{P_LoginID}",
"salesman_id": "{P_SalesmanID}",
"logged_in_store": "{P_Node}",
"action": "APPLY_HOLD",
"modification_type" : "ADD_ITEM",
"order_hold_types": ["MODIFICATION_HOLD"],
"isHistory": "Y"
}
File : testapi->additem-> addPLItem.js
import exec from "k6/execution";
import { SharedArray } from "k6/data";
import http from "k6/http";
import papaparse from "https://jslib.k6.io/papaparse/5.1.1/index.js";
import { check } from 'k6';
import create3LineOrder from '../createorder/create3LinesOrder.js';
import create5LineOrder from '../createorder/create5LineOrder.js';
const applyHoldRawPayload = JSON.parse(open('../../resource/input/additem/applyHoldAddItemMod.json'));
const addItemRawPayLoad = JSON.parse(open('../../resource/input/additem/addPLItemCompute.json'));
const applyAddItemRawPayLoad = JSON.parse(open('../../resource/input/additem/addPLItemApply.json'));
const loginSalesStore = new SharedArray("loginSalesStore", function () {
console.log("Input CSV File Name = ", "loginSalesStore");
let feed_file_data = papaparse.parse(open("../../resource/input/commons/loginSalesStore.csv"), { header: true }).data;
console.log("Reading Order from csv file");
return feed_file_data;
});
const uniqueIdentiferForAddItemPerfTest = "4000";
export const options = {
insecureSkipTLSVerify: true,
scenarios: {
contacts: {
executor: 'constant-arrival-rate',
// Our test should last 30 seconds in total
duration: '10s',
// It should start 30 iterations per `timeUnit`. Note that iterations starting points
// will be evenly spread across the `timeUnit` period.
rate: 1,
// It should start `rate` iterations per second
timeUnit: '2s',
// It should preallocate 2 VUs before starting the test
preAllocatedVUs: 4,
// It is allowed to spin up to 50 maximum VUs to sustain the defined
// constant arrival rate.
maxVUs: 10,
},
},
};
function preparePayload(payload, holdAppliedOrderDetails, storesDetails, orderNumber) {
payload.login_id = storesDetails.LoginID;
payload.salesman_id = storesDetails.SalesmanID;
payload.logged_in_store = storesDetails.Node;
payload.order_number = orderNumber;
payload.transaction_id = holdAppliedOrderDetails.order_hold_types.order_hold_type[0].transaction_id;
payload.order_header_key = holdAppliedOrderDetails.order_header_key;
payload.max_prime_line_no = 1+parseInt(holdAppliedOrderDetails.max_prime_line_no);
payload.order_lines.order_line[0].prime_line_no =1+parseInt(holdAppliedOrderDetails.max_prime_line_no);
const formattedDateTime = new Date().toISOString().replace('Z', '-06:00');
payload.order_lines.order_line[0].fulfillment_details[0].estimated_ship_date =formattedDateTime
payload.order_lines.order_line[0].fulfillment_details[1].estimated_ship_date =formattedDateTime
payload.order_lines.order_line[0].fulfillment_details[0].product_availability_date =formattedDateTime
return payload;
}
function createOrder(vu) {
switch ((Math.floor(Math.random() * 1000)) % 2) {
case 0:
return create3LineOrder(uniqueIdentiferForAddItemPerfTest.concat(vu.toString()));
case 1:
return create3LineOrder(uniqueIdentiferForAddItemPerfTest.concat(vu.toString()));
}
}
export default function () {
console.log("In Add Item");
const cartResponse = createOrder(exec.vu.idInInstance);
if (cartResponse.status === 200) {
console.log("Order was successfully created");
// fetching store and login details
let index = Math.floor(Math.random() * (loginSalesStore.length - 1 - 0 + 1)) + 0;
const storesDetails = loginSalesStore[index];
console.log(storesDetails.Node);
applyHoldRawPayload.login_id = storesDetails.LoginID;
applyHoldRawPayload.salesman_id = storesDetails.SalesmanID;
applyHoldRawPayload.logged_in_store = storesDetails.Node;
const orderNumber = cartResponse.json().orderNumber;
applyHoldRawPayload.order_number = orderNumber;
//Adding params to request
const params = {
headers: {
'Content-Type': 'application/json',
'Authorization': 'Basic YXV0aHRlc3Q6YXV0aHBhc3N3b3Jk',
'Order-Source': 'Orbit'
},
};
const applyHoldResponse = http.post('https://internal-sadc-stage.carbon.lowes.com/rise-eoms/api/oms/getOrderDetails', JSON.stringify(applyHoldRawPayload), params);
const holdAppliedOrderDetails = applyHoldResponse.json();
console.log("number of orderline from formula :" + holdAppliedOrderDetails.order_lines.order_line.length);
// enriching compute payload
const computeAddItemPayload = preparePayload(addItemRawPayLoad, holdAppliedOrderDetails, storesDetails, orderNumber);
// compute call made and compute response gets in return
const computeResponse = http.post('https://internal-sadc-stage.carbon.lowes.com/rise-eoms/api/oms/addItem/compute/v2', JSON.stringify(computeAddItemPayload), params);
check(computeResponse, {
'is status 200 for add item compute response': (r) => r.status === 200
})
const computeJsonResponse=computeResponse.json();
// enriching apply payload
const applyAddItemPayload = preparePayload(applyAddItemRawPayLoad, holdAppliedOrderDetails, storesDetails, orderNumber);
applyAddItemPayload.payments[0].max_charge_limit=computeJsonResponse.payment_total;
applyAddItemPayload.payments[0].amount=computeJsonResponse.amount_to_be_charged;
applyAddItemPayload.payments[0].payment_key=computeJsonResponse.allowed_credit_cards_for_upcharge[0].payment_key;
// apply add item call
const applyResponse = http.post('https://internal-sadc-stage.carbon.lowes.com/rise-eoms/api/oms/addItem/apply', JSON.stringify(applyAddItemPayload), params);
//console.log(applyResponse.json());
check(applyResponse, {
'is status 200 for add item apply response': (r) => r.status === 200,
}
)
}
}
File : Dockerfile
FROM e-dpiac-docker-local.docker.lowes.com/irs-image-node:14.15.5-alpine
FROM e-mnp-docker-local.docker.lowes.com/loadimpact/k6
USER root
RUN mkdir -p /apps/k6webapp
RUN mkdir -p /apps/k6perf
WORKDIR /apps/k6webapp
COPY k6webapp /apps/k6webapp
COPY k6perf /apps/k6perf
RUN apk add curl
RUN yarn install
EXPOSE 8080
RUN chown -R 11011 /apps/k6webapp
USER 11011
CMD ["node","index.js"]
USER root
WORKDIR /apps/k6perf
RUN chown -R 10101 /apps/k6perf
USER 10101
RUN pwd
RUN echo "PWD is: $PWD"
ENTRYPOINT ["k6"]