diff --git a/tests/scripts/helpers/kruize.py b/tests/scripts/helpers/kruize.py index bcae2bbe5..0e8035073 100644 --- a/tests/scripts/helpers/kruize.py +++ b/tests/scripts/helpers/kruize.py @@ -15,9 +15,12 @@ """ import json +import requests import subprocess -import requests + +def get_kruize_url(): + return URL def form_kruize_url(cluster_type, SERVER_IP=None): @@ -28,6 +31,10 @@ def form_kruize_url(cluster_type, SERVER_IP=None): print("\nKRUIZE AUTOTUNE URL = ", URL) return + if (cluster_type == "local"): + AUTOTUNE_PORT = 8080 + SERVER_IP = '127.0.0.1' + URL = "http://" + str(SERVER_IP) + ":" + str(AUTOTUNE_PORT) if (cluster_type == "minikube"): port = subprocess.run( ['kubectl -n monitoring get svc kruize --no-headers -o=custom-columns=PORT:.spec.ports[*].nodePort'], @@ -43,11 +50,14 @@ def form_kruize_url(cluster_type, SERVER_IP=None): subprocess.run(['oc expose svc/kruize -n openshift-tuning'], shell=True, stdout=subprocess.PIPE) ip = subprocess.run( - ['oc status -n openshift-tuning | grep "kruize" | grep -v "kruize-ui" | grep -v "kruize-db" | grep port | cut -d " " -f1 | cut -d "/" -f3'], shell=True, + [ + 'oc status -n openshift-tuning | grep "kruize" | grep -v "kruize-ui" | grep -v "kruize-db" | grep port | cut -d " " -f1 | cut -d "/" -f3'], + shell=True, stdout=subprocess.PIPE) SERVER_IP = ip.stdout.decode('utf-8').strip('\n') print("IP = ", SERVER_IP) URL = "http://" + str(SERVER_IP) + print("\nKRUIZE AUTOTUNE URL = ", URL) @@ -453,3 +463,32 @@ def generate_recommendations(experiment_name): print(response.text) print("\n************************************************************") return response + +def post_bulk_api(input_json_file): + print("\n************************************************************") + print("Sending POST request to URL: ", f"{URL}/bulk") + print("Request Payload: ", input_json_file) + curl_command = f"curl -X POST {URL}/bulk -H 'Content-Type: application/json' -d '{json.dumps(input_json_file)}'" + print("Equivalent cURL command: ", curl_command) + + # Send the POST request + response = requests.post(f"{URL}/bulk", json=input_json_file) + print("Response Status Code: ", response.status_code) + print("Response JSON: ", response.json()) + return response + +def get_bulk_job_status(job_id,verbose=False): + print("\n************************************************************") + url_basic = f"{URL}/bulk?job_id={job_id}" + url_verbose = f"{URL}/bulk?job_id={job_id}&verbose={verbose}" + getJobIDURL = url_basic + if verbose: + getJobIDURL = url_verbose + print("Sending GET request to URL ( verbose=",verbose," ): ", getJobIDURL) + curl_command_verbose = f"curl -X GET '{getJobIDURL}'" + print("Equivalent cURL command : ", curl_command_verbose) + response = requests.get(url_verbose) + + print("Verbose GET Response Status Code: ", response.status_code) + print("Verbose GET Response JSON: ", response.json()) + return response \ No newline at end of file diff --git a/tests/scripts/helpers/utils.py b/tests/scripts/helpers/utils.py index 4eb2d0d23..6de593f08 100644 --- a/tests/scripts/helpers/utils.py +++ b/tests/scripts/helpers/utils.py @@ -21,6 +21,7 @@ import time import math import docker +from helpers.kruize import * from datetime import datetime, timedelta from kubernetes import client, config from pathlib import Path @@ -1582,6 +1583,30 @@ def validate_accelerator_recommendations_for_container(recommendations_json): perf_limits_map = term_obj["recommendation_engines"]["performance"]["config"]["limits"] validate_limits_map_for_accelerator(perf_limits_map) +#@pytest.mark.skip(reason="Not a test function") +def validate_job_status(job_id, base_url, caplog): + # Common keys expected in both responses + common_keys = { + "status", "total_experiments", "processed_experiments", "job_id", "job_start_time", "job_end_time" + } + + # Extra keys expected when verbose=true + verbose_keys = { + "experiments" + } + + # Make the GET request without verbose + response_basic = get_bulk_job_status(job_id,False) + # Verify common keys in the basic response + assert common_keys.issubset( + response_basic.json().keys()), f"Missing keys in response: {common_keys - response_basic.json().keys()}" + + response_verbose = get_bulk_job_status(job_id,True) + # Verify common and verbose keys in the verbose response + assert common_keys.issubset( + response_verbose.json().keys()), f"Missing keys in verbose response: {common_keys - response_verbose.json().keys()}" + assert verbose_keys.issubset( + response_verbose.json().keys()), f"Missing verbose keys in response: {verbose_keys - response_verbose.json().keys()}" diff --git a/tests/scripts/local_monitoring_tests/Local_monitoring_tests.md b/tests/scripts/local_monitoring_tests/Local_monitoring_tests.md index 5fa8d3f9f..236c80ea8 100644 --- a/tests/scripts/local_monitoring_tests/Local_monitoring_tests.md +++ b/tests/scripts/local_monitoring_tests/Local_monitoring_tests.md @@ -97,6 +97,16 @@ The above tests are developed using pytest framework and the tests are run using - Creates a resource optimization metric profile using the [createMetricProfile API](/design/MetricProfileAPI.md) - Runs the above tests using pytest +### **Bulk API tests** +This test script validates the functionality of a bulk POST [API](/design/MonitoringModeAPI.md) and associated GET job status API for a Kubernetes resource optimization service, focusing on the creation and monitoring of job IDs. + +Here are the test scenarios: +- Validate the bulk POST API's ability to generate a job_id for given payloads. + - Empty Payload: Ensures a job_id is generated even when no data is sent. + - Sample JSON Payload: Verifies the API correctly processes a structured payload and generates a job_id. +- Verify the response of the GET job status API for the generated job_id. + - Tests both verbose=false and verbose=true GET requests for comprehensive verification. + ## Prerequisites for running the tests: - Minikube setup or access to Openshift cluster - Tools like kubectl, oc, curl, jq, python diff --git a/tests/scripts/local_monitoring_tests/rest_apis/test_bulkAPI.py b/tests/scripts/local_monitoring_tests/rest_apis/test_bulkAPI.py new file mode 100644 index 000000000..ce993d1be --- /dev/null +++ b/tests/scripts/local_monitoring_tests/rest_apis/test_bulkAPI.py @@ -0,0 +1,67 @@ +""" +Copyright (c) 2024 Red Hat, IBM Corporation and others. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" +import logging +import pytest +import requests +import sys + +sys.path.append("../../") +from helpers.fixtures import * +from helpers.kruize import * +from helpers.utils import * + +# Set up logging +logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s') +logger = logging.getLogger(__name__) + + +@pytest.mark.sanity +@pytest.mark.parametrize("bulk_request_payload, expected_job_id_present", [ + ({}, True), # Test with an empty payload to check if a job_id is created. + ({ + "filter": { + "exclude": { + "namespace": [], + "workload": [], + "containers": [], + "labels": {} + }, + "include": { + "namespace": [], + "workload": [], + "containers": [], + "labels": {} + } + }, + "time_range": {} + }, True) # Test with a sample payload with some JSON content +]) +def test_bulk_post_request(cluster_type, bulk_request_payload, expected_job_id_present, caplog): + form_kruize_url(cluster_type) + URL = get_kruize_url() + + with caplog.at_level(logging.INFO): + # Log request payload and curl command for POST request + response = post_bulk_api(bulk_request_payload) + + # Check if job_id is present in the response + job_id_present = "job_id" in response.json() and isinstance(response.json()["job_id"], str) + assert job_id_present == expected_job_id_present, f"Expected job_id presence to be {expected_job_id_present} but was {job_id_present}" + + # If a job_id is generated, run the GET request test + if job_id_present: + validate_job_status(response.json()["job_id"], URL, caplog) +