From 9be63779b1471fdd287ae50a3226bef58bf1ec45 Mon Sep 17 00:00:00 2001 From: "bxf12315@gmail.com" Date: Thu, 20 Nov 2025 21:26:27 +0800 Subject: [PATCH 1/2] add scale test for sbom --- scenarios/full-20250604.json5 | 3 +- src/main.rs | 4 ++- src/restapi.rs | 67 ++++++++++++++++++++++++++++++++++- src/scenario/mod.rs | 23 ++++++++++++ 4 files changed, 94 insertions(+), 3 deletions(-) diff --git a/scenarios/full-20250604.json5 b/scenarios/full-20250604.json5 index 5ab8e87..f5f5c36 100644 --- a/scenarios/full-20250604.json5 +++ b/scenarios/full-20250604.json5 @@ -113,5 +113,6 @@ "urn:uuid:01973122-3bdd-78a3-898a-fb67c06387ed" ], "download_advisory": "24ae57c3-4b57-4f4e-82c1-83ae26059a89", - "get_advisory": "24ae57c3-4b57-4f4e-82c1-83ae26059a89" + "get_advisory": "24ae57c3-4b57-4f4e-82c1-83ae26059a89", + "count_by_package": "pkg:generic/redhat/commons-beanutils-commons-beanutils@" } diff --git a/src/main.rs b/src/main.rs index b11c30d..cef2b44 100644 --- a/src/main.rs +++ b/src/main.rs @@ -180,7 +180,8 @@ async fn main() -> Result<(), anyhow::Error> { .register_transaction(tx!(list_sboms_paginated)) .register_transaction(tx!(get_analysis_status)) .register_transaction(tx!(get_analysis_latest_cpe)) - .register_transaction(tx!(list_advisory_labels)); + .register_transaction(tx!(list_advisory_labels)) + .register_transaction(tx!(list_sbom_labels)); tx!(s.get_sbom?(scenario.get_sbom.clone())); tx!(s.get_sbom_advisories?(scenario.get_sbom_advisories.clone())); @@ -195,6 +196,7 @@ async fn main() -> Result<(), anyhow::Error> { tx!(s.download_advisory?(scenario.download_advisory.clone())); tx!(s.get_advisory?(scenario.get_advisory.clone())); + tx!(s.count_by_package?(scenario.count_by_package.clone())); s }) .register_scenario({ diff --git a/src/restapi.rs b/src/restapi.rs index a99de09..53909ba 100644 --- a/src/restapi.rs +++ b/src/restapi.rs @@ -1,5 +1,7 @@ use crate::utils::DisplayVec; -use goose::goose::{GooseUser, TransactionResult}; +use goose::goose::{GooseMethod, GooseRequest, GooseUser, TransactionResult}; +use reqwest::{Client, RequestBuilder}; +use serde_json::Value as JsonValue; use serde_json::json; use std::sync::{ Arc, @@ -7,6 +9,33 @@ use std::sync::{ }; use urlencoding::encode; +// send_request sends a request to the server +// +// path is the path of the request, e.g. "/api/v2/sbom/count-by-package" +// user is the GooseUser to send the request with +// method is the HTTP method to use, e.g. GooseMethod::Get +// value is the JSON value to send in the request body +// client_method is the reqwest method to use, e.g. Client::get +async fn send_request( + path: String, + user: &mut GooseUser, + method: GooseMethod, + value: JsonValue, + client_method: fn(&Client, String) -> RequestBuilder, +) -> TransactionResult { + let url = user.build_url(&path)?; + + let reqwest_request_builder = client_method(&user.client, url); + let goose_request = GooseRequest::builder() + .method(method) + .path(path.as_str()) + .set_request_builder(reqwest_request_builder.json(&value)) + .build(); + let _response = user.request(goose_request).await?; + + Ok(()) +} + pub async fn get_advisory(id: String, user: &mut GooseUser) -> TransactionResult { let uri = format!("/api/v2/advisory/{}", encode(&format!("urn:uuid:{}", id))); @@ -26,6 +55,42 @@ pub async fn download_advisory(id: String, user: &mut GooseUser) -> TransactionR Ok(()) } +pub async fn list_sbom_labels(user: &mut GooseUser) -> TransactionResult { + let filter_text = json!([ + { "key": "source"}, + ]); + + let uri = format!( + "/api/v2/sbom-labels?filter_text={}&limit={}", + encode(&filter_text.to_string()), + 1000 + ); + + let _response = user.get(&uri).await?; + + Ok(()) +} + +pub async fn count_by_package(purl: String, user: &mut GooseUser) -> TransactionResult { + let filter_text = json!([ + { + "purl": purl, + "cpe": null + } + ]); + + send_request( + "/api/v2/sbom/count-by-package".to_string(), + user, + GooseMethod::Get, + filter_text, + Client::get, + ) + .await?; + + Ok(()) +} + pub async fn list_advisory_labels(user: &mut GooseUser) -> TransactionResult { let uri = format!( "/api/v2/advisory-labels?filter_text={}&limit={}", diff --git a/src/scenario/mod.rs b/src/scenario/mod.rs index a370d2b..1f19635 100644 --- a/src/scenario/mod.rs +++ b/src/scenario/mod.rs @@ -102,6 +102,9 @@ pub(crate) struct Scenario { #[serde(with = "required")] pub get_advisory: Option, + + #[serde(with = "required")] + pub count_by_package: Option, } impl Scenario { @@ -143,6 +146,10 @@ impl Scenario { ); let download_advisory = Some(loader.download_advisory().await?); let get_advisory = Some(loader.download_advisory().await?); + let count_by_package_result = Some(loader.count_by_package().await?); + let count_by_package = + count_by_package_result.ok_or_else(|| anyhow!("no count-by-package result"))?; + let count_by_package = serde_json::from_str::(&count_by_package)?; Ok(Self { get_sbom: large_sbom_digest.clone(), @@ -160,6 +167,13 @@ impl Scenario { delete_sbom_pool, download_advisory, get_advisory, + count_by_package: Some(format!( + "pkg:{}/{}/{}@{}", + count_by_package["type"].as_str().unwrap_or(""), + count_by_package["namespace"].as_str().unwrap_or(""), + count_by_package["name"].as_str().unwrap_or(""), + count_by_package["version"].as_str().unwrap_or("") + )), }) } } @@ -376,6 +390,15 @@ FROM public.advisory order by modified desc limit 1;"#, ) .await } + + /// A purl for count-by-package query + pub async fn count_by_package(&self) -> anyhow::Result { + self.find( + r#" +select purl::text as result from qualified_purl qp order by qp.timestamp desc limit 1;"#, + ) + .await + } } #[cfg(test)] From b438741c69044f7587c64a71c27bfc53eb179d2d Mon Sep 17 00:00:00 2001 From: "bxf12315@gmail.com" Date: Fri, 21 Nov 2025 09:11:05 +0800 Subject: [PATCH 2/2] scale test for put and patch sbom lables --- scenarios/full-20250604.json5 | 46 ++++++++++++++++++++++++++++++- src/main.rs | 17 ++++++++++++ src/restapi.rs | 38 +++++++++++++++++++++++++ src/scenario/mod.rs | 52 +++++++++++++++++++++++++++++++++++ 4 files changed, 152 insertions(+), 1 deletion(-) diff --git a/scenarios/full-20250604.json5 b/scenarios/full-20250604.json5 index f5f5c36..fb41221 100644 --- a/scenarios/full-20250604.json5 +++ b/scenarios/full-20250604.json5 @@ -114,5 +114,49 @@ ], "download_advisory": "24ae57c3-4b57-4f4e-82c1-83ae26059a89", "get_advisory": "24ae57c3-4b57-4f4e-82c1-83ae26059a89", - "count_by_package": "pkg:generic/redhat/commons-beanutils-commons-beanutils@" + "count_by_package": "pkg:generic/redhat/commons-beanutils-commons-beanutils@", + "patch_sbom_lables": [ + "urn:uuid:0199413a-8792-7220-9690-aae13ffe6691", + "urn:uuid:0199413b-05e6-77f1-8dff-5c03486296c4", + "urn:uuid:0199413b-0145-7b03-92a9-69c18ff90406", + "urn:uuid:0199413a-f755-76d2-a9e2-8e05f73ef286", + "urn:uuid:0199413b-0d68-70c3-b733-6b4f7e8628ee", + "urn:uuid:0199413b-8389-7b32-91e7-c109400b584b", + "urn:uuid:0199413b-6db5-7162-958a-095039ab032d", + "urn:uuid:0199413c-0319-7231-af5c-1b04b500645c", + "urn:uuid:0199413c-8802-7dc2-8ec2-821ecf33ab36", + "urn:uuid:0199413c-8d8b-7f81-a848-f394c6772167", + "urn:uuid:0199413c-9470-73f1-ae40-69a64c099fe1", + "urn:uuid:0199412f-5359-7cf3-9390-2d1c6294345c", + "urn:uuid:01994138-48fe-7db2-a5eb-74c3e80f3271", + "urn:uuid:0199413a-ff9d-7563-a7aa-e72f5a7272e8", + "urn:uuid:0199413a-c5c7-7ec2-8583-2006df4eda8d", + "urn:uuid:0199413b-3075-7020-826b-cb3eb5a18713", + "urn:uuid:0199413b-386a-70d0-b9c9-e99305bc8c85", + "urn:uuid:0199413b-7f43-7803-bde8-8e8f8d5b299e", + "urn:uuid:0199413b-d18b-7781-8ab0-07cc429a0901", + "urn:uuid:0199413b-e7a2-7c92-8214-52e59375cdc4" + ], + "put_sbom_lables": [ + "urn:uuid:01994138-3f44-7c41-8879-ad7d7f2013ed", + "urn:uuid:0199412e-b3ec-70d3-aaae-a88e9eb25f92", + "urn:uuid:01994138-4479-7963-9c61-f3367c43b5b8", + "urn:uuid:0199412f-101d-7892-a82f-bdfe8b286ca6", + "urn:uuid:01994133-abd3-7bc3-b1a2-7a2dcae1d428", + "urn:uuid:01994130-62bd-71f2-870e-b1e678394ea2", + "urn:uuid:01994130-17d9-7641-91d2-1c977dd01424", + "urn:uuid:0199412f-c637-7c11-b6d6-b30281445b6d", + "urn:uuid:0199412e-5b37-78a3-b999-256ba95b2139", + "urn:uuid:01994138-f1d2-7dd3-b17a-9fbca167f650", + "urn:uuid:01994139-3448-7ac0-81be-f238be804471", + "urn:uuid:01994138-8ce7-7e40-b51a-3196e8c44aac", + "urn:uuid:0199412f-6f85-7970-ab1f-dfbacddd192b", + "urn:uuid:01994139-6eb1-79d2-b998-4a5bba02104e", + "urn:uuid:01994139-bc49-7521-9d8d-4572f6436451", + "urn:uuid:0199413a-0969-70d1-b54d-5cf687af0511", + "urn:uuid:0199413a-53de-7d91-afcb-708d31473bd6", + "urn:uuid:0199413a-106c-7f71-8de9-427c1fd89ebb", + "urn:uuid:0199413b-0363-7780-95e8-9f7f053710e9", + "urn:uuid:0199413a-6a5d-7853-ab62-c220322590da" + ] } diff --git a/src/main.rs b/src/main.rs index cef2b44..ebfd971 100644 --- a/src/main.rs +++ b/src/main.rs @@ -197,6 +197,23 @@ async fn main() -> Result<(), anyhow::Error> { tx!(s.download_advisory?(scenario.download_advisory.clone())); tx!(s.get_advisory?(scenario.get_advisory.clone())); tx!(s.count_by_package?(scenario.count_by_package.clone())); + + // Register put sbom labels transaction if pool is available + let put_sbom_labels_counter = Arc::new(std::sync::atomic::AtomicUsize::new(0)); + if let Some(_sbom_ids) = scenario.put_sbom_lables.clone() { + tx!(s.put_sbom_labels?(scenario.put_sbom_lables.clone(), + put_sbom_labels_counter.clone()), + name: format!("put_sbom_labels")); + } + + // Register patch sbom labels transaction if pool is available + let patch_sbom_labels_counter = Arc::new(std::sync::atomic::AtomicUsize::new(0)); + if let Some(_sbom_ids) = scenario.patch_sbom_lables.clone() { + tx!(s.patch_sbom_labels?(scenario.patch_sbom_lables.clone(), + patch_sbom_labels_counter.clone()), + name: format!("patch_sbom_labels")); + } + s }) .register_scenario({ diff --git a/src/restapi.rs b/src/restapi.rs index 53909ba..62fe6a7 100644 --- a/src/restapi.rs +++ b/src/restapi.rs @@ -36,6 +36,44 @@ async fn send_request( Ok(()) } +/// Send Sbom labels request using PUT method +pub async fn put_sbom_labels( + sbom_ids: Vec, + counter: Arc, + user: &mut GooseUser, +) -> TransactionResult { + let path = format!( + "/api/v2/sbom/{}/label", + sbom_ids[counter.fetch_add(1, Ordering::Relaxed) % sbom_ids.len()].clone() + ); + let json = json!({ + "source": "It's a put request", + "foo": "bar", + "space": "with space", + "empty": "", + }); + send_request(path, user, GooseMethod::Put, json, Client::put).await +} + +/// Send Sbom labels request using PATCH method +pub async fn patch_sbom_labels( + sbom_ids: Vec, + counter: Arc, + user: &mut GooseUser, +) -> TransactionResult { + let path = format!( + "/api/v2/sbom/{}/label", + sbom_ids[counter.fetch_add(1, Ordering::Relaxed) % sbom_ids.len()].clone() + ); + let json = json!({ + "source": "It's a patch request", + "foo": "bar", + "space": "with space", + "empty": "", + }); + send_request(path, user, GooseMethod::Patch, json, Client::patch).await +} + pub async fn get_advisory(id: String, user: &mut GooseUser) -> TransactionResult { let uri = format!("/api/v2/advisory/{}", encode(&format!("urn:uuid:{}", id))); diff --git a/src/scenario/mod.rs b/src/scenario/mod.rs index 1f19635..d191640 100644 --- a/src/scenario/mod.rs +++ b/src/scenario/mod.rs @@ -105,6 +105,12 @@ pub(crate) struct Scenario { #[serde(with = "required")] pub count_by_package: Option, + + #[serde(default, skip_serializing_if = "Option::is_none")] + pub patch_sbom_lables: Option>, + + #[serde(default, skip_serializing_if = "Option::is_none")] + pub put_sbom_lables: Option>, } impl Scenario { @@ -150,6 +156,22 @@ impl Scenario { let count_by_package = count_by_package_result.ok_or_else(|| anyhow!("no count-by-package result"))?; let count_by_package = serde_json::from_str::(&count_by_package)?; + let put_sbom_lables = Some( + loader + .put_sbom_lables() + .await? + .iter() + .map(|sbom_id| format!("urn:uuid:{sbom_id}")) + .collect(), + ); + let patch_sbom_lables = Some( + loader + .patch_sbom_lables() + .await? + .iter() + .map(|sbom_id| format!("urn:uuid:{sbom_id}")) + .collect(), + ); Ok(Self { get_sbom: large_sbom_digest.clone(), @@ -174,6 +196,8 @@ impl Scenario { count_by_package["name"].as_str().unwrap_or(""), count_by_package["version"].as_str().unwrap_or("") )), + patch_sbom_lables, + put_sbom_lables, }) } } @@ -399,6 +423,34 @@ select purl::text as result from qualified_purl qp order by qp.timestamp desc li ) .await } + + /// sbom IDs for put labels + pub async fn put_sbom_lables(&self) -> anyhow::Result> { + let mut db = crate::db::connect(&self.db).await?; + + let rows = sqlx::query( + "SELECT sbom_id::text as id FROM public.sbom order by published desc limit 20;", + ) + .fetch_all(&mut db) + .await?; + Ok(rows + .into_iter() + .map(|row| row.get::("id")) + .collect()) + } + + /// sbom IDs for patch labels + pub async fn patch_sbom_lables(&self) -> anyhow::Result> { + let mut db = crate::db::connect(&self.db).await?; + + let rows = sqlx::query("SELECT sbom_id::text as id FROM public.sbom order by published desc OFFSET 20 limit 20;") + .fetch_all(&mut db) + .await?; + Ok(rows + .into_iter() + .map(|row| row.get::("id")) + .collect()) + } } #[cfg(test)]