From 1dba0d476f810dbcc1a424b8a5ba0147d5a6939c Mon Sep 17 00:00:00 2001 From: kr172t Date: Tue, 9 Jan 2024 00:04:52 +0200 Subject: [PATCH 1/6] add message size parameter to api and curl --- api/src/api/analyze_api.py | 7 ++-- api/src/services/analyze_service.py | 37 +++++++++---------- api/src/services/test_suites_service.py | 4 +-- api/tests/test_analyze_api.py | 45 ++++++++++++++++++------ curl/package-lock.json | 39 +++++++++++++++----- curl/package.json | 7 ++-- curl/scripts/run-curl-loop.sh | 5 +-- curl/src/curl/curl.controller.spec.ts | 3 ++ curl/src/curl/curl.service.spec.ts | 13 +++++-- curl/src/curl/curl.service.ts | 12 ++++--- curl/src/dto/curl-request.dto.spec.ts | 31 ++++++++++++++-- curl/src/dto/curl-request.dto.ts | 7 +++- curl/src/utils/message.generator.spec.ts | 28 +++++++++++++++ curl/src/utils/message.generator.ts | 10 ++++++ run/docker/docker-compose.yml | 4 +-- 15 files changed, 189 insertions(+), 63 deletions(-) create mode 100644 curl/src/utils/message.generator.spec.ts create mode 100644 curl/src/utils/message.generator.ts diff --git a/api/src/api/analyze_api.py b/api/src/api/analyze_api.py index f430356e..9a576b2a 100644 --- a/api/src/api/analyze_api.py +++ b/api/src/api/analyze_api.py @@ -36,11 +36,14 @@ def analyze(): return jsonify({'error': 'An error occurred while processing the request', 'message':''}), HTTP_STATUS_INTERNAL_SERVER_ERROR def __validate(data): - if not data or 'algorithms' not in data or 'iterationsCount' not in data or 'experimentName' not in data or 'description' not in data: - raise ApiException('Missing properties, required properties: algorithms, iterationsCount, experimentName, description', INVALID_DATA_MESSAGE, HTTP_STATUS_BAD_REQUEST) + if not data or 'algorithms' not in data or 'iterationsCount' not in data or 'experimentName' not in data or 'description' not in data or 'messageSizes' not in data: + raise ApiException('Missing properties, required properties: algorithms, iterationsCount, experimentName, description, messageSizes', INVALID_DATA_MESSAGE, HTTP_STATUS_BAD_REQUEST) for iterations in data['iterationsCount']: if iterations <= 0: raise ApiException('The number of iterations should be greater than 0', INVALID_DATA_MESSAGE, HTTP_STATUS_BAD_REQUEST) + for message_size in data['messageSizes']: + if message_size <= 0: + raise ApiException('The message size should be greater than 0', INVALID_DATA_MESSAGE, HTTP_STATUS_BAD_REQUEST) if process_is_running: raise ApiException('The previous test is still running. Please try again in few minutes', 'Current test is still running', HTTP_STATUS_LOCKED) for algorithm in data['algorithms']: diff --git a/api/src/services/analyze_service.py b/api/src/services/analyze_service.py index 3e89fcaf..650b34d6 100644 --- a/api/src/services/analyze_service.py +++ b/api/src/services/analyze_service.py @@ -1,5 +1,3 @@ -import os -import uuid import time import requests import logging @@ -9,13 +7,7 @@ from flask import jsonify, current_app import src.services.test_suites_service as tests_service import src.services.metrics_service as metrics_service -from src.models.env_info import EnvInfo -from src.models.test_suite import TestSuite -from src.models.test_run import TestRun from src.enums.status import Status -from src.exceptions.exceptions import ApiException - - # constants WAIT_MS = 15 @@ -26,14 +18,16 @@ def analyze(data): start_time = int(datetime.timestamp(datetime.now() - timedelta(seconds=60)) * 1000) iterations_count = data['iterationsCount'] algorithms = data['algorithms'] + message_sizes = data['messageSizes'] first_run = True for algorithm in algorithms: for iterations in iterations_count: - if not first_run: - time.sleep(WAIT_MS) - else: - first_run = False - __create_test_run(algorithm, iterations, test_suite.id) + for message_size in message_sizes: + if not first_run: + time.sleep(WAIT_MS) + else: + first_run = False + __create_test_run(algorithm, iterations, message_size, test_suite.id) # end time is now + 90 sec, to show the graph after the test for sure finished running end_time = int(datetime.timestamp(datetime.now() + timedelta(seconds=90)) * 1000) @@ -45,22 +39,23 @@ def analyze(data): return jsonify({'test_suite_id': test_suite.id}) -def __create_test_run(algorithm, iterations, test_suite_id): - start_time=datetime.now() +def __create_test_run(algorithm, iterations, message_size, test_suite_id): + start_time = datetime.now() metrics_service.start_collecting() - status, status_message = __run(algorithm, iterations) + status, status_message = __run(algorithm, iterations, message_size) metrics_service.stop_collecting() end_time=datetime.now() - tests_service.create_test_run(start_time, end_time, algorithm, iterations, test_suite_id, status, status_message, *metrics_service.get_metrics()) - + tests_service.create_test_run(start_time, end_time, algorithm, iterations, message_size, test_suite_id, status, status_message, *metrics_service.get_metrics()) + -def __run(algorithm, iterations): +def __run(algorithm, iterations, message_sizes): logging.debug('Running test for algorithm: %s ', algorithm) payload = { 'algorithm': algorithm, - 'iterationsCount': iterations + 'iterationsCount': iterations, + 'messageSizes': message_sizes } - headers = { 'Content-Type': 'application/json' } + headers = {'Content-Type': 'application/json'} response = requests.post(current_app.configurations.curl_url + "/curl", headers=headers, json=payload, timeout=int(current_app.configurations.request_timeout)) return __validate_response(response) diff --git a/api/src/services/test_suites_service.py b/api/src/services/test_suites_service.py index d8ee1cc9..9f4de261 100644 --- a/api/src/services/test_suites_service.py +++ b/api/src/services/test_suites_service.py @@ -34,7 +34,7 @@ def create_test_suite(data): current_app.database_manager.create(test_suite) return test_suite -def create_test_run(start_time, end_time, algorithm, iterations, test_suite_id, status, status_message, client_metrics, server_metrics): +def create_test_run(start_time, end_time, algorithm, iterations, message_size, test_suite_id, status, status_message, client_metrics, server_metrics): test_run = TestRun( start_time=start_time, end_time=end_time, @@ -42,7 +42,7 @@ def create_test_run(start_time, end_time, algorithm, iterations, test_suite_id, iterations=iterations, status=status, status_message=status_message, - # message_size=1024, + message_size=message_size, test_suite_id=test_suite_id ) current_app.database_manager.create(test_run) diff --git a/api/tests/test_analyze_api.py b/api/tests/test_analyze_api.py index 9003567d..86943031 100644 --- a/api/tests/test_analyze_api.py +++ b/api/tests/test_analyze_api.py @@ -39,7 +39,8 @@ def test_analyze(self, mock_start_collecting, mock_stop_collecting, mock_get_met "algorithms":["kyber512"], "iterationsCount": [1000, 2000], "experimentName": "name", - "description": "name" + "description": "name", + "messageSizes": [100] } # Mock the requests.post call with patch(POST_REQUEST) as mock_post: @@ -78,7 +79,8 @@ def test_analyze_return_general_error(self, mock_start_collecting, mock_stop_col "algorithms":["kyber512"], "iterationsCount": [1000], "experimentName": "name", - "description": "name" + "description": "name", + "messageSizes": [100] } # Mock the requests.post call to raise an exception @@ -98,7 +100,8 @@ def test_analyze_with_invalid_iterations_count(self, mock_start_collecting, mock "algorithms": ["kyber512"], "iterationsCount": [-1], "experimentName": "name", - "description": "name" + "description": "name", + "messageSizes": [100] } response = self.client.post(PATH, data=json.dumps(input_data), @@ -108,13 +111,30 @@ def test_analyze_with_invalid_iterations_count(self, mock_start_collecting, mock self.assertEqual(response_json["error"], INVALID_DATA_PROVIDED) self.assertEqual(response_json["message"], "The number of iterations should be greater than 0") + def test_analyze_with_invalid_message_sizes(self, mock_start_collecting, mock_stop_collecting, mock_get_metrics): + input_data = { + "algorithms": ["kyber512"], + "iterationsCount": [10], + "experimentName": "name", + "description": "name", + "messageSizes": [0] + } + response = self.client.post(PATH, + data=json.dumps(input_data), + content_type=CONTENT_TYPE) + self.assertEqual(response.status_code, 400) + response_json = json.loads(response.data) + self.assertEqual(response_json["error"], INVALID_DATA_PROVIDED) + self.assertEqual(response_json["message"], "The message size should be greater than 0") + def test_analyze_with_invalid_algorithm(self, mock_start_collecting, mock_stop_collecting, mock_get_metrics): input_data = { "algorithms":["invalid_algorithm"], "iterationsCount": [1000], "experimentName": "name", - "description": "name" + "description": "name", + "messageSizes": [100] } response = self.client.post(PATH, data=json.dumps(input_data), @@ -137,14 +157,15 @@ def test_analyze_with_invalid_body(self, mock_start_collecting, mock_stop_collec self.assertEqual(response.status_code, 400) response_json = json.loads(response.data) self.assertEqual(response_json["error"], INVALID_DATA_PROVIDED) - self.assertEqual(response_json["message"], "Missing properties, required properties: algorithms, iterationsCount, experimentName, description") + self.assertEqual(response_json["message"], "Missing properties, required properties: algorithms, iterationsCount, experimentName, description, messageSizes") def test_analyze_with_curl_failure(self, mock_start_collecting, mock_stop_collecting, mock_get_metrics): input_data = { "algorithms":["kyber512"], "iterationsCount": [1000], "experimentName": "name", - "description": "name" + "description": "name", + "messageSizes": [100] } # Mock the requests.post call with patch(POST_REQUEST) as mock_post: @@ -160,13 +181,13 @@ def test_analyze_with_curl_failure(self, mock_start_collecting, mock_stop_collec self.assertEqual(actual_test_run[0].status_message, '{"result": "failed"}') - def test_analyze_with_missing_env_info(self, mock_start_collecting, mock_stop_collecting, mock_get_metrics): input_data = { - "algorithms":["kyber512"], + "algorithms": ["kyber512"], "iterationsCount": [1000], "experimentName": "name", - "description": "name" + "description": "name", + "messageSizes": [100] } self.app.database_manager.get_latest.return_value = None response = self.client.post(PATH, @@ -185,7 +206,8 @@ def test_analyze_with_423(self, mock_start_collecting, mock_stop_collecting, moc "algorithms":["kyber512"], "iterationsCount": [1000], "experimentName": "name", - "description": "name" + "description": "name", + "messageSizes": [100] } analyze_api.process_is_running = True # Mock the requests.post call @@ -203,7 +225,8 @@ def test_analyze_sleep_between_tests(self, mock_start_collecting, mock_stop_coll "algorithms":["kyber512","frodo640aes"], "iterationsCount": [1000], "experimentName": "name", - "description": "name" + "description": "name", + "messageSizes": [100] } with patch(GET_REQUEST) as mock_get: mock_get.return_value.status_code = 200 diff --git a/curl/package-lock.json b/curl/package-lock.json index 776a55a5..f591d6e0 100644 --- a/curl/package-lock.json +++ b/curl/package-lock.json @@ -9,13 +9,14 @@ "version": "0.0.1", "license": "UNLICENSED", "dependencies": { - "@nestjs/common": "^9.0.0", - "@nestjs/config": "3.0.0", + "@nestjs/common": "^9.4.3", + "@nestjs/config": "^3.0.0", "@nestjs/core": "^9.0.0", "@nestjs/mapped-types": "*", "@nestjs/platform-express": "^9.0.0", "class-transformer": "^0.5.1", "class-validator": "^0.14.0", + "crypto": "^1.0.1", "reflect-metadata": "^0.1.13", "rxjs": "^7.2.0" }, @@ -25,7 +26,7 @@ "@nestjs/testing": "^9.4.3", "@types/express": "^4.17.13", "@types/jest": "29.2.4", - "@types/node": "18.11.18", + "@types/node": "^18.11.18", "@types/supertest": "^2.0.11", "@typescript-eslint/eslint-plugin": "^5.0.0", "@typescript-eslint/parser": "^5.0.0", @@ -1544,13 +1545,13 @@ } }, "node_modules/@nestjs/common": { - "version": "9.3.12", - "resolved": "https://registry.npmjs.org/@nestjs/common/-/common-9.3.12.tgz", - "integrity": "sha512-NtrUG2VgCbhmZEO1yRt/Utq16uFRV+xeHAOtdYIsfHGG0ssAV2lVLlvFFAQYh0SQ+KuYY1Gsxd3GK2JFoJCNqQ==", + "version": "9.4.3", + "resolved": "https://registry.npmjs.org/@nestjs/common/-/common-9.4.3.tgz", + "integrity": "sha512-Gd6D4IaYj01o14Bwv81ukidn4w3bPHCblMUq+SmUmWLyosK+XQmInCS09SbDDZyL8jy86PngtBLTdhJ2bXSUig==", "dependencies": { "iterare": "1.2.1", - "tslib": "2.5.0", - "uid": "2.0.1" + "tslib": "2.5.3", + "uid": "2.0.2" }, "funding": { "type": "opencollective", @@ -1575,6 +1576,22 @@ } } }, + "node_modules/@nestjs/common/node_modules/tslib": { + "version": "2.5.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.3.tgz", + "integrity": "sha512-mSxlJJwl3BMEQCUNnxXBU9jP4JBktcEGhURcPR6VQVlnP0FdDEsIaz0C35dXNGLyRfrATNofF0F5p2KPxQgB+w==" + }, + "node_modules/@nestjs/common/node_modules/uid": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/uid/-/uid-2.0.2.tgz", + "integrity": "sha512-u3xV3X7uzvi5b1MncmZo3i2Aw222Zk1keqLA1YkHldREkAhAqi65wuPfe7lHx8H/Wzy+8CE7S7uS3jekIM5s8g==", + "dependencies": { + "@lukeed/csprng": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/@nestjs/config": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/@nestjs/config/-/config-3.0.0.tgz", @@ -3484,6 +3501,12 @@ "node": ">= 8" } }, + "node_modules/crypto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/crypto/-/crypto-1.0.1.tgz", + "integrity": "sha512-VxBKmeNcqQdiUQUW2Tzq0t377b54N2bMtXO/qiLa+6eRRmmC4qT3D4OnTGoT/U6O9aklQ/jTwbOtRMTTY8G0Ig==", + "deprecated": "This package is no longer supported. It's now a built-in Node module. If you've depended on crypto, you should switch to the one that's built-in." + }, "node_modules/debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", diff --git a/curl/package.json b/curl/package.json index 3188ee74..d3803f75 100644 --- a/curl/package.json +++ b/curl/package.json @@ -19,13 +19,14 @@ "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand" }, "dependencies": { - "@nestjs/common": "^9.0.0", - "@nestjs/config": "3.0.0", + "@nestjs/common": "^9.4.3", + "@nestjs/config": "^3.0.0", "@nestjs/core": "^9.0.0", "@nestjs/mapped-types": "*", "@nestjs/platform-express": "^9.0.0", "class-transformer": "^0.5.1", "class-validator": "^0.14.0", + "crypto": "^1.0.1", "reflect-metadata": "^0.1.13", "rxjs": "^7.2.0" }, @@ -35,7 +36,7 @@ "@nestjs/testing": "^9.4.3", "@types/express": "^4.17.13", "@types/jest": "29.2.4", - "@types/node": "18.11.18", + "@types/node": "^18.11.18", "@types/supertest": "^2.0.11", "@typescript-eslint/eslint-plugin": "^5.0.0", "@typescript-eslint/parser": "^5.0.0", diff --git a/curl/scripts/run-curl-loop.sh b/curl/scripts/run-curl-loop.sh index 8cf367cf..52b590e7 100755 --- a/curl/scripts/run-curl-loop.sh +++ b/curl/scripts/run-curl-loop.sh @@ -1,10 +1,11 @@ #!/bin/bash -# This script expects four arguments +# This script expects five arguments nginx_host="$1" nginx_port="$2" iteration_count="$3" algorithm="$4" +payload="$5" num_processes=$(($(getconf _NPROCESSORS_ONLN) * 2)) -seq ${iteration_count} | xargs -P $num_processes -n 1 -I % curl https://${nginx_host}:${nginx_port} -k --curves ${algorithm} -so /dev/null \ No newline at end of file +seq ${iteration_count} | xargs -P $num_processes -n 1 -I % curl https://${nginx_host}:${nginx_port} -k --curves ${algorithm} -XPOST -d "$payload" -H "Content-Type: text/plain" -o /dev/null \ No newline at end of file diff --git a/curl/src/curl/curl.controller.spec.ts b/curl/src/curl/curl.controller.spec.ts index 308f8a45..3f5b4f41 100644 --- a/curl/src/curl/curl.controller.spec.ts +++ b/curl/src/curl/curl.controller.spec.ts @@ -29,6 +29,7 @@ describe('CurlController', () => { const curlRequest: CurlRequest = { algorithm: 'kyber512', iterationsCount: 500, + messageSize: 10 }; const runSpy = jest.spyOn(curlService, 'run'); await curlController.create(curlRequest); @@ -38,6 +39,7 @@ describe('CurlController', () => { const curlRequest: CurlRequest = { algorithm: 'kyber512', iterationsCount: 500, + messageSize: 10 }; const expectedResult = undefined; jest.spyOn(curlService, 'run').mockResolvedValue(expectedResult); @@ -48,6 +50,7 @@ describe('CurlController', () => { const curlRequest: CurlRequest = { algorithm: 'kyber512', iterationsCount: 500, + messageSize: 10 }; const error = new HttpException('Exception', 409); jest.spyOn(curlService, 'run').mockRejectedValue(error); diff --git a/curl/src/curl/curl.service.spec.ts b/curl/src/curl/curl.service.spec.ts index 7c18540e..af771882 100644 --- a/curl/src/curl/curl.service.spec.ts +++ b/curl/src/curl/curl.service.spec.ts @@ -5,6 +5,7 @@ import { ConfigModule, ConfigService } from '@nestjs/config'; import { HttpException, HttpStatus } from '@nestjs/common'; import * as shellJS from 'shelljs'; +import {MessageGenerator} from "../utils/message.generator"; jest.mock('shelljs', () => ({ exec: jest.fn(), })); @@ -49,18 +50,20 @@ describe('CurlService', () => { const curlRequest: CurlRequest = { algorithm: 'kyber512', iterationsCount: 1000, + messageSize: 10 }; const validateSpy = jest.spyOn(curlService, 'validate'); const runCurlsSpy = jest.spyOn(curlService, 'runCurls').mockResolvedValue(undefined); await curlService.run(curlRequest); expect(validateSpy).toHaveBeenCalledWith(curlRequest); - expect(runCurlsSpy).toHaveBeenCalledWith(curlRequest.iterationsCount, curlRequest.algorithm); + expect(runCurlsSpy).toHaveBeenCalledWith(curlRequest.iterationsCount, curlRequest.algorithm, expect.any(String)); }); it('should throw an HttpException with status INTERNAL_SERVER_ERROR when runCurls throws an error', async () => { const curlRequest: CurlRequest = { algorithm: 'kyber512', iterationsCount: 1000, + messageSize: 10 }; jest.spyOn(curlService, 'runCurls').mockRejectedValue(new HttpException('runCurls error', 500)); await expect(curlService.run(curlRequest)).rejects.toThrow(HttpException); @@ -74,6 +77,7 @@ describe('CurlService', () => { const curlRequest: CurlRequest = { algorithm: 'unsupported_algorithm', iterationsCount: 1000, + messageSize: 10 }; jest.spyOn(configService, 'get').mockReturnValue(['test_algorithm']); await expect(curlService.run(curlRequest)).rejects.toThrow(HttpException); @@ -86,6 +90,7 @@ describe('CurlService', () => { const curlRequest: CurlRequest = { algorithm: 'kyber512', iterationsCount: 1000, + messageSize: 10 }; jest.spyOn(configService, 'get').mockReturnValue(['test_algorithm']); jest.spyOn(curlService, 'runCurls').mockImplementation(() => { @@ -108,6 +113,7 @@ describe('CurlService', () => { const curlRequest: CurlRequest = { algorithm: 'kyber512', iterationsCount: 1000, + messageSize: 10 }; jest.spyOn(configService, 'get').mockReturnValue(['test_algorithm']); expect(() => curlService['validate'](curlRequest)).not.toThrow(); @@ -118,9 +124,10 @@ describe('CurlService', () => { it('should call execAsync with the correct command', async () => { const iterationsCount = 1000; const algorithm = 'kyber512'; + const message = MessageGenerator.generate(8); const execAsyncSpy = jest.spyOn(curlService, 'execAsync').mockResolvedValue(undefined); - await curlService['runCurls'](iterationsCount, algorithm); - const expectedCommand = curlService['format'](`./scripts/run-curl-loop.sh ${configService.get('nginx.host')} ${configService.get('nginx.port')} ${iterationsCount} ${algorithm}`); + await curlService['runCurls'](iterationsCount, algorithm, message); + const expectedCommand = curlService['format'](`./scripts/run-curl-loop.sh ${configService.get('nginx.host')} ${configService.get('nginx.port')} ${iterationsCount} ${algorithm} ${message}`); expect(execAsyncSpy).toHaveBeenCalledWith(expectedCommand); }); // Add more test cases for error handling in runCurls. diff --git a/curl/src/curl/curl.service.ts b/curl/src/curl/curl.service.ts index 35267fc3..0fe1bdfe 100644 --- a/curl/src/curl/curl.service.ts +++ b/curl/src/curl/curl.service.ts @@ -2,7 +2,8 @@ import { Injectable } from '@nestjs/common'; import * as shellJS from 'shelljs'; import { CurlRequest } from '../dto/curl-request.dto'; import { ConfigService } from '@nestjs/config'; -import { HttpException, HttpStatus, NotFoundException } from '@nestjs/common'; +import { HttpException, HttpStatus } from '@nestjs/common'; +import {MessageGenerator} from '../utils/message.generator'; @Injectable() export class CurlService { @@ -19,8 +20,9 @@ export class CurlService { async run(curlRequest: CurlRequest): Promise { this.validate(curlRequest); - try { - await this.runCurls(curlRequest.iterationsCount, curlRequest.algorithm); + try { + const message = MessageGenerator.generate(curlRequest.messageSize); + await this.runCurls(curlRequest.iterationsCount, curlRequest.algorithm, message); } catch (err) { this.processIsRunning = false; console.error('[CurlService:run] Error occurred: ', err); @@ -39,8 +41,8 @@ export class CurlService { } } - private async runCurls(iterationsCount: number, algorithm: String) { - const curlCommand = this.format(`${this.CURL_SCRIPT_PATH} ${this.configService.get('nginx.host')} ${this.configService.get('nginx.port')} ${iterationsCount} ${algorithm}`); + private async runCurls(iterationsCount: number, algorithm: String, message: String) { + const curlCommand = this.format(`${this.CURL_SCRIPT_PATH} ${this.configService.get('nginx.host')} ${this.configService.get('nginx.port')} ${iterationsCount} ${algorithm} ${message}`); this.processIsRunning = true; await this.execAsync(curlCommand); console.log('[CurlService:run] Finished taking all curl samples'); diff --git a/curl/src/dto/curl-request.dto.spec.ts b/curl/src/dto/curl-request.dto.spec.ts index ddfffb6a..4d110e00 100644 --- a/curl/src/dto/curl-request.dto.spec.ts +++ b/curl/src/dto/curl-request.dto.spec.ts @@ -1,10 +1,12 @@ import { CurlRequest } from './curl-request.dto'; import { validate } from 'class-validator'; + describe('CurlRequest', () => { it('should pass validation when all properties are valid', async () => { const curlRequest = new CurlRequest(); curlRequest.algorithm = 'ExampleAlgorithm'; curlRequest.iterationsCount = 1000; + curlRequest.messageSize = 10; const validationErrors = await validate(curlRequest); expect(validationErrors).toHaveLength(0); }); @@ -13,6 +15,7 @@ describe('CurlRequest', () => { const curlRequest = new CurlRequest(); curlRequest.algorithm = ''; curlRequest.iterationsCount = 1000; + curlRequest.messageSize = 10; const validationErrors = await validate(curlRequest); expect(validationErrors).toHaveLength(1); expect(validationErrors[0].constraints.isNotEmpty).toBeDefined(); @@ -21,18 +24,40 @@ describe('CurlRequest', () => { it('should fail validation when iterationsCount is not a number', async () => { const curlRequest = new CurlRequest(); curlRequest.algorithm = 'ExampleAlgorithm'; - (curlRequest.iterationsCount as any) = 'not a number'; + curlRequest.iterationsCount = 'not a number' as any; + curlRequest.messageSize = 10; const validationErrors = await validate(curlRequest); expect(validationErrors).toHaveLength(1); expect(validationErrors[0].constraints.isNumber).toBeDefined(); }); - + it('should fail validation when iterationsCount is not from the list', async () => { const curlRequest = new CurlRequest(); curlRequest.algorithm = 'ExampleAlgorithm'; curlRequest.iterationsCount = -3; + curlRequest.messageSize = 10; + const validationErrors = await validate(curlRequest); + expect(validationErrors).toHaveLength(1); + expect(validationErrors[0].constraints).toBeDefined(); + }); + + it('should fail validation when messageSize is not a number', async () => { + const curlRequest = new CurlRequest(); + curlRequest.algorithm = 'ExampleAlgorithm'; + curlRequest.iterationsCount = 1000; + curlRequest.messageSize = 'not a number' as any; + const validationErrors = await validate(curlRequest); + expect(validationErrors).toHaveLength(1); + expect(validationErrors[0].constraints.isNumber).toBeDefined(); + }); + + it('should fail validation when messageSize is not from the list', async () => { + const curlRequest = new CurlRequest(); + curlRequest.algorithm = 'ExampleAlgorithm'; + curlRequest.iterationsCount = 1000; + curlRequest.messageSize = -3; const validationErrors = await validate(curlRequest); expect(validationErrors).toHaveLength(1); expect(validationErrors[0].constraints).toBeDefined(); }); -}); \ No newline at end of file +}); diff --git a/curl/src/dto/curl-request.dto.ts b/curl/src/dto/curl-request.dto.ts index 1b6fbae3..269b4287 100644 --- a/curl/src/dto/curl-request.dto.ts +++ b/curl/src/dto/curl-request.dto.ts @@ -1,4 +1,4 @@ -import { IsIn, IsNotEmpty, IsNumber, Min } from 'class-validator'; +import { IsNotEmpty, IsNumber, Min } from 'class-validator'; export class CurlRequest { @@ -9,4 +9,9 @@ export class CurlRequest { @IsNumber() @Min(1) iterationsCount: number; + + @IsNotEmpty() + @IsNumber() + @Min(1) + messageSize: number; } diff --git a/curl/src/utils/message.generator.spec.ts b/curl/src/utils/message.generator.spec.ts new file mode 100644 index 00000000..084bd7c1 --- /dev/null +++ b/curl/src/utils/message.generator.spec.ts @@ -0,0 +1,28 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { MessageGenerator } from './message.generator'; + +describe('MessageGenerator', () => { + let service: MessageGenerator; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + providers: [MessageGenerator], + }).compile(); + + service = module.get(MessageGenerator); + }); + + it('should be defined', () => { + expect(service).toBeDefined(); + }); + + describe('generate', () => { + it('should generate a message with the specified size', () => { + const sizeInBytes = 10; + const generatedMessage = MessageGenerator.generate(sizeInBytes); + + // The length of the generated message in hex should be double the size in bytes + expect(generatedMessage.length).toBe(sizeInBytes * 2); + }); + }); +}); diff --git a/curl/src/utils/message.generator.ts b/curl/src/utils/message.generator.ts new file mode 100644 index 00000000..dab42852 --- /dev/null +++ b/curl/src/utils/message.generator.ts @@ -0,0 +1,10 @@ +import { Injectable } from '@nestjs/common'; +import * as crypto from 'crypto'; + +@Injectable() +export class MessageGenerator { + static generate(sizeInBytes: number) { + const randomBytes = crypto.randomBytes(sizeInBytes); + return randomBytes.toString('hex'); + } +} diff --git a/run/docker/docker-compose.yml b/run/docker/docker-compose.yml index 51cbd20e..9c1ce1f1 100644 --- a/run/docker/docker-compose.yml +++ b/run/docker/docker-compose.yml @@ -23,7 +23,7 @@ services: - DEFAULT_GROUPS=prime256v1:secp384r1:frodo640aes:frodo640shake:frodo976aes:frodo976shake:frodo1344aes:frodo1344shake:kyber512:p256_kyber512:kyber768:p384_kyber768:x25519_kyber768:kyber1024:bikel1:bikel3:bikel5:hqc128:hqc192:hqc256 curl: - image: qujata/curl:1.1.0 + image: qujata/curl:1.2.0 container_name: qujata-curl ports: - 3010:3010 @@ -31,7 +31,7 @@ services: - DEFAULT_GROUPS=prime256v1:secp384r1:frodo640aes:frodo640shake:frodo976aes:frodo976shake:frodo1344aes:frodo1344shake:kyber512:p256_kyber512:kyber768:p384_kyber768:x25519_kyber768:kyber1024:bikel1:bikel3:bikel5:hqc128:hqc192:hqc256 api: - image: qujata/api:1.1.0 + image: qujata/api:1.2.0 container_name: qujata-api ports: - 3020:3020 From e76120b428da5ff4305b1638ea80304837761a37 Mon Sep 17 00:00:00 2001 From: kr172t Date: Sun, 14 Jan 2024 13:27:38 +0200 Subject: [PATCH 2/6] change api+curl versions in charts --- api/main.py | 2 +- api/src/services/analyze_service.py | 8 +++++--- api/src/services/metrics_service.py | 2 -- curl/src/dto/curl-request.dto.ts | 2 +- curl/tsconfig.json | 4 +++- run/kubernetes/charts/api/values.yaml | 2 +- run/kubernetes/charts/curl/values.yaml | 2 +- 7 files changed, 12 insertions(+), 10 deletions(-) diff --git a/api/main.py b/api/main.py index e4500af3..5a79c5de 100644 --- a/api/main.py +++ b/api/main.py @@ -5,7 +5,7 @@ from src.api.tests_api import api as tests_blueprint import src.services.k8s_service as k8s_service import src.services.cadvisor_service as cadvisor_service -from src.enums.environemnt import Environment +from src.enums.environment import Environment from config.settings import load_config from flask_cors import CORS from src.utils.database_manager import DatabaseManager diff --git a/api/src/services/analyze_service.py b/api/src/services/analyze_service.py index 0ff556e6..46aeaadf 100644 --- a/api/src/services/analyze_service.py +++ b/api/src/services/analyze_service.py @@ -20,7 +20,9 @@ def analyze(data): start_time = int(datetime.timestamp(datetime.now() - timedelta(seconds=60)) * 1000) iterations_count = data['iterationsCount'] algorithms = data['algorithms'] - message_sizes = data['messageSizes'] + message_sizes = [0] + if 'messageSizes' in data: + message_sizes = data['messageSizes'] first_run = True for algorithm in algorithms: for iterations in iterations_count: @@ -50,12 +52,12 @@ def __create_test_run(algorithm, iterations, message_size, test_suite_id): test_suites_service.create_test_run(start_time, end_time, algorithm, iterations, message_size, test_suite_id, status, status_message, *metrics_service.get_metrics()) -def __run(algorithm, iterations, message_sizes): +def __run(algorithm, iterations, message_size): logging.debug('Running test for algorithm: %s ', algorithm) payload = { 'algorithm': algorithm, 'iterationsCount': iterations, - 'messageSizes': message_sizes + 'messageSize': message_size } headers = { 'Content-Type': 'application/json' } response = requests.post(current_app.configurations.curl_url + "/curl", headers=headers, json=payload, timeout=int(current_app.configurations.request_timeout)) diff --git a/api/src/services/metrics_service.py b/api/src/services/metrics_service.py index a53f51a1..37f0a8d8 100644 --- a/api/src/services/metrics_service.py +++ b/api/src/services/metrics_service.py @@ -1,5 +1,3 @@ -from flask import current_app -from src.models.test_run_metric import TestRunMetric from src.utils.metrics_collector import MetricsCollector import logging diff --git a/curl/src/dto/curl-request.dto.ts b/curl/src/dto/curl-request.dto.ts index 2685753c..f30b7ed8 100644 --- a/curl/src/dto/curl-request.dto.ts +++ b/curl/src/dto/curl-request.dto.ts @@ -12,6 +12,6 @@ export class CurlRequest { @IsNotEmpty() @IsNumber() - @Min(1) + @Min(0) messageSize: number; } diff --git a/curl/tsconfig.json b/curl/tsconfig.json index adb614ca..fd6d15a5 100644 --- a/curl/tsconfig.json +++ b/curl/tsconfig.json @@ -17,5 +17,7 @@ "strictBindCallApply": false, "forceConsistentCasingInFileNames": false, "noFallthroughCasesInSwitch": false - } + }, + "include": ["src/**/*.ts"], + "exclude": ["node_modules"] } diff --git a/run/kubernetes/charts/api/values.yaml b/run/kubernetes/charts/api/values.yaml index cfe564e8..8f8fab8e 100644 --- a/run/kubernetes/charts/api/values.yaml +++ b/run/kubernetes/charts/api/values.yaml @@ -6,7 +6,7 @@ replicaCount: 1 image: repository: qujata/api - tag: "1.1.0" + tag: "1.2.0" pullPolicy: Always imagePullSecrets: [] diff --git a/run/kubernetes/charts/curl/values.yaml b/run/kubernetes/charts/curl/values.yaml index 5d71536d..8347c9a0 100644 --- a/run/kubernetes/charts/curl/values.yaml +++ b/run/kubernetes/charts/curl/values.yaml @@ -6,7 +6,7 @@ replicaCount: 1 image: repository: qujata/curl - tag: "1.1.0" + tag: "1.2.0" pullPolicy: Always From e92e1431c82afc23d8d9cae94f3f5f51d5662bd3 Mon Sep 17 00:00:00 2001 From: kr172t Date: Mon, 15 Jan 2024 10:38:10 +0200 Subject: [PATCH 3/6] fix tests --- api/tests/test_analyze_api.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/api/tests/test_analyze_api.py b/api/tests/test_analyze_api.py index 97c9f790..3a9b325d 100644 --- a/api/tests/test_analyze_api.py +++ b/api/tests/test_analyze_api.py @@ -117,7 +117,7 @@ def test_analyze_with_invalid_message_sizes(self, mock_start_collecting, mock_st "iterationsCount": [10], "experimentName": "name", "description": "name", - "messageSizes": [0] + "messageSizes": [-1] } response = self.client.post(PATH, data=json.dumps(input_data), @@ -125,7 +125,7 @@ def test_analyze_with_invalid_message_sizes(self, mock_start_collecting, mock_st self.assertEqual(response.status_code, 400) response_json = json.loads(response.data) self.assertEqual(response_json["error"], INVALID_DATA_PROVIDED) - self.assertEqual(response_json["message"], "The message size should be greater than 0") + self.assertEqual(response_json["message"], "The message size should be greater than -1") def test_analyze_with_invalid_algorithm(self, mock_start_collecting, mock_stop_collecting, mock_get_metrics): From 74d50024a0b69912a307c59bba562a51d95dd3bd Mon Sep 17 00:00:00 2001 From: kr172t Date: Tue, 23 Jan 2024 10:50:11 +0200 Subject: [PATCH 4/6] change message generator method --- curl/src/utils/message.generator.spec.ts | 4 +--- curl/src/utils/message.generator.ts | 9 ++++----- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/curl/src/utils/message.generator.spec.ts b/curl/src/utils/message.generator.spec.ts index 084bd7c1..a687fba4 100644 --- a/curl/src/utils/message.generator.spec.ts +++ b/curl/src/utils/message.generator.spec.ts @@ -20,9 +20,7 @@ describe('MessageGenerator', () => { it('should generate a message with the specified size', () => { const sizeInBytes = 10; const generatedMessage = MessageGenerator.generate(sizeInBytes); - - // The length of the generated message in hex should be double the size in bytes - expect(generatedMessage.length).toBe(sizeInBytes * 2); + expect(generatedMessage.length).toBe(sizeInBytes); }); }); }); diff --git a/curl/src/utils/message.generator.ts b/curl/src/utils/message.generator.ts index dab42852..05c19fc7 100644 --- a/curl/src/utils/message.generator.ts +++ b/curl/src/utils/message.generator.ts @@ -1,10 +1,9 @@ -import { Injectable } from '@nestjs/common'; -import * as crypto from 'crypto'; +import {Injectable} from '@nestjs/common'; @Injectable() export class MessageGenerator { static generate(sizeInBytes: number) { - const randomBytes = crypto.randomBytes(sizeInBytes); - return randomBytes.toString('hex'); - } + // Generate the string by repeating the character 'a' + return 'a'.repeat(sizeInBytes); } + } From 314020428d968e024b527d122d581a01e9986b38 Mon Sep 17 00:00:00 2001 From: kr172t Date: Wed, 24 Jan 2024 11:16:29 +0200 Subject: [PATCH 5/6] update example in README.md --- api/README.md | 5 ++++- api/src/services/analyze_service.py | 4 +--- api/src/utils/test_suite_serializer.py | 4 ++-- api/tests/test_tests_api.py | 2 +- 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/api/README.md b/api/README.md index bd2a2ca4..9e6e8a8d 100644 --- a/api/README.md +++ b/api/README.md @@ -31,8 +31,11 @@ python3 -m src.main curl --location 'http://localhost:3020/qujata-api/analyze' \ --header 'Content-Type: application/json' \ --data '{ + "experimentName": "name", + "description" : "test description", "algorithms": ["kyber512"], - "iterationsCount": 5 + "iterationsCount": [5], + "messageSizes": [10] }' ``` diff --git a/api/src/services/analyze_service.py b/api/src/services/analyze_service.py index 46aeaadf..ef07c43d 100644 --- a/api/src/services/analyze_service.py +++ b/api/src/services/analyze_service.py @@ -20,9 +20,7 @@ def analyze(data): start_time = int(datetime.timestamp(datetime.now() - timedelta(seconds=60)) * 1000) iterations_count = data['iterationsCount'] algorithms = data['algorithms'] - message_sizes = [0] - if 'messageSizes' in data: - message_sizes = data['messageSizes'] + message_sizes = data['messageSizes'] if 'messageSizes' in data else [0] first_run = True for algorithm in algorithms: for iterations in iterations_count: diff --git a/api/src/utils/test_suite_serializer.py b/api/src/utils/test_suite_serializer.py index eca362f9..502aba23 100644 --- a/api/src/utils/test_suite_serializer.py +++ b/api/src/utils/test_suite_serializer.py @@ -5,11 +5,11 @@ def serialize(test_suite): "id": test_suite.id, "name": test_suite.name, "description": test_suite.description, - "codeRelease": test_suite.code_release, + "code_release": test_suite.code_release, "start_time": test_suite.start_time, "end_time": test_suite.end_time, "environment_info": __get_environment_info(test_suite.env_info), - "testRuns": __get_test_runs_metrics(test_suite.test_runs) + "test_runs": __get_test_runs_metrics(test_suite.test_runs) } return response_data diff --git a/api/tests/test_tests_api.py b/api/tests/test_tests_api.py index ff1e1bd5..08989c3e 100644 --- a/api/tests/test_tests_api.py +++ b/api/tests/test_tests_api.py @@ -70,7 +70,7 @@ def test_get_test_suite(self): self.app.database_manager.get_by_id.return_value = test_suite response = self.client.get(TEST_SUITES_GET_URL) result = json.loads(response.data) - expected = {'codeRelease': '1.1.0', 'description': 'description', 'end_time': None, 'environment_info': {'cpu': None, 'cpuArchitecture': None, 'cpuClockSpeed': None, 'cpuCores': None, 'nodeSize': None, 'operatingSystem': None, 'resourceName': None}, 'id': None, 'name': 'name', 'start_time': None, 'testRuns': [{'algorithm': None, 'id': 1, 'iterations': None, 'results': {'averageCPU': 9.0, 'averageMemory': 14}}]} + expected = {'code_release': '1.1.0', 'description': 'description', 'end_time': None, 'environment_info': {'cpu': None, 'cpuArchitecture': None, 'cpuClockSpeed': None, 'cpuCores': None, 'nodeSize': None, 'operatingSystem': None, 'resourceName': None}, 'id': None, 'name': 'name', 'start_time': None, 'test_runs': [{'algorithm': None, 'id': 1, 'iterations': None, 'results': {'averageCPU': 9.0, 'averageMemory': 14}}]} self.assertEqual(result, expected) def test_get_test_suite_return_not_found(self): From 210e933470f85188941c393dc8a804eea134e4a7 Mon Sep 17 00:00:00 2001 From: kr172t Date: Wed, 31 Jan 2024 10:54:54 +0200 Subject: [PATCH 6/6] add message_size to test suites api response --- api/src/utils/test_suite_serializer.py | 1 + api/tests/test_tests_api.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/api/src/utils/test_suite_serializer.py b/api/src/utils/test_suite_serializer.py index 502aba23..1d13f018 100644 --- a/api/src/utils/test_suite_serializer.py +++ b/api/src/utils/test_suite_serializer.py @@ -35,6 +35,7 @@ def __get_test_runs_metrics(test_runs): "id": test_run.id, "algorithm": test_run.algorithm, "iterations": test_run.iterations, + "message_size": test_run.message_size, "results": { "averageCPU": round(cpu_avg, 2), "averageMemory": int(memory_avg), diff --git a/api/tests/test_tests_api.py b/api/tests/test_tests_api.py index 08989c3e..8e52b5bb 100644 --- a/api/tests/test_tests_api.py +++ b/api/tests/test_tests_api.py @@ -70,7 +70,7 @@ def test_get_test_suite(self): self.app.database_manager.get_by_id.return_value = test_suite response = self.client.get(TEST_SUITES_GET_URL) result = json.loads(response.data) - expected = {'code_release': '1.1.0', 'description': 'description', 'end_time': None, 'environment_info': {'cpu': None, 'cpuArchitecture': None, 'cpuClockSpeed': None, 'cpuCores': None, 'nodeSize': None, 'operatingSystem': None, 'resourceName': None}, 'id': None, 'name': 'name', 'start_time': None, 'test_runs': [{'algorithm': None, 'id': 1, 'iterations': None, 'results': {'averageCPU': 9.0, 'averageMemory': 14}}]} + expected = {'code_release': '1.1.0', 'description': 'description', 'end_time': None, 'environment_info': {'cpu': None, 'cpuArchitecture': None, 'cpuClockSpeed': None, 'cpuCores': None, 'nodeSize': None, 'operatingSystem': None, 'resourceName': None}, 'id': None, 'name': 'name', 'start_time': None, 'test_runs': [{'algorithm': None, 'id': 1, 'iterations': None, 'message_size': None, 'results': {'averageCPU': 9.0, 'averageMemory': 14}}]} self.assertEqual(result, expected) def test_get_test_suite_return_not_found(self):