From 6d5a799d9077883e90e65d9271d628483c84e152 Mon Sep 17 00:00:00 2001 From: Ariel Ben-Yehuda Date: Tue, 28 Oct 2025 12:27:11 +0000 Subject: [PATCH 1/3] feat: add constructors for agent metadata types we want to allow adding fields to metadata, which requires making them `non_exhaustive`. However, users do create the metadatas, at least for testing. Add constructors to allow that to be done in a back-compat way. --- src/metadata/mod.rs | 53 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/src/metadata/mod.rs b/src/metadata/mod.rs index 7941ceb..821f95f 100644 --- a/src/metadata/mod.rs +++ b/src/metadata/mod.rs @@ -50,6 +50,59 @@ pub enum AgentMetadata { NoMetadata, } +impl AgentMetadata { + /// Create metadata corresponding to an EC2 instance running on AWS. + /// + /// This function is versioned to allow for extensibility. + /// + /// ```rust + /// # use async_profiler_agent::metadata::AgentMetadata; + /// let _metadata = AgentMetadata::ec2_agent_metadata_v1( + /// "123456789012".to_string(), + /// "us-east-1".to_string(), + /// "i-1234567890abcdef0".to_string(), + /// ); + /// ``` + pub fn ec2_agent_metadata_v1( + aws_account_id: String, + aws_region_id: String, + ec2_instance_id: String, + ) -> Self { + Self::Ec2AgentMetadata { + aws_account_id, + aws_region_id, + ec2_instance_id, + } + } + + /// Create metadata corresponding to a Fargate task running on AWS. + /// + /// This function is versioned to allow for extensibility. + /// + /// ```rust + /// # use async_profiler_agent::metadata::AgentMetadata; + /// let _metadata = AgentMetadata::fargate_agent_metadata_v1( + /// "123456789012".to_string(), + /// "us-east-1".to_string(), + /// "arn:aws:ecs:us-east-1:123456789012:task/cluster/5261e761e0e2a3d92da3f02c8e5bab1f".to_string(), + /// "arn:aws:ecs:us-east-1:123456789012:cluster/profiler-metadata-cluster".to_string(), + /// ); + /// ``` + pub fn fargate_agent_metadata_v1( + aws_account_id: String, + aws_region_id: String, + ecs_task_arn: String, + ecs_cluster_arn: String, + ) -> Self { + Self::FargateAgentMetadata { + aws_account_id, + aws_region_id, + ecs_task_arn, + ecs_cluster_arn, + } + } +} + /// Metadata associated with a specific individual profiling report #[derive(Debug, Clone, PartialEq, Eq)] pub struct ReportMetadata<'a> { From 3cbe51a0d3a8060b7e0a98f997d054dc9abbdf75 Mon Sep 17 00:00:00 2001 From: Ariel Ben-Yehuda Date: Thu, 13 Nov 2025 11:41:11 +0000 Subject: [PATCH 2/3] chore: cargo update --- Cargo.lock | 74 +++++++++++++++++++++++----------------------- decoder/Cargo.lock | 20 ++++++------- 2 files changed, 47 insertions(+), 47 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0b79023..a18eb6c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -201,9 +201,9 @@ dependencies = [ [[package]] name = "aws-credential-types" -version = "1.2.8" +version = "1.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "faf26925f4a5b59eb76722b63c2892b1d70d06fa053c72e4a100ec308c1d47bc" +checksum = "86590e57ea40121d47d3f2e131bfd873dea15d78dc2f4604f4734537ad9e56c4" dependencies = [ "aws-smithy-async", "aws-smithy-runtime-api", @@ -213,9 +213,9 @@ dependencies = [ [[package]] name = "aws-lc-rs" -version = "1.14.1" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "879b6c89592deb404ba4dc0ae6b58ffd1795c78991cbb5b8bc441c48a070440d" +checksum = "5932a7d9d28b0d2ea34c6b3779d35e3dd6f6345317c34e73438c4f1f29144151" dependencies = [ "aws-lc-sys", "zeroize", @@ -223,9 +223,9 @@ dependencies = [ [[package]] name = "aws-lc-sys" -version = "0.32.3" +version = "0.33.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "107a4e9d9cab9963e04e84bb8dee0e25f2a987f9a8bad5ed054abd439caa8f8c" +checksum = "1826f2e4cfc2cd19ee53c42fbf68e2f81ec21108e0b7ecf6a71cf062137360fc" dependencies = [ "bindgen", "cc", @@ -236,9 +236,9 @@ dependencies = [ [[package]] name = "aws-runtime" -version = "1.5.13" +version = "1.5.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f2402da1a5e16868ba98725e5d73f26b8116eaa892e56f2cd0bf5eec7985f70" +checksum = "8fe0fd441565b0b318c76e7206c8d1d0b0166b3e986cf30e890b61feb6192045" dependencies = [ "aws-credential-types", "aws-sigv4", @@ -261,9 +261,9 @@ dependencies = [ [[package]] name = "aws-sdk-s3" -version = "1.110.0" +version = "1.112.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a811ec867f77c01aa0f0abfaa9fedef647cc83608ad8e67949f95d30d04a7fd" +checksum = "eee73a27721035c46da0572b390a69fbdb333d0177c24f3d8f7ff952eeb96690" dependencies = [ "aws-credential-types", "aws-runtime", @@ -295,9 +295,9 @@ dependencies = [ [[package]] name = "aws-sdk-sso" -version = "1.88.0" +version = "1.89.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d05b276777560aa9a196dbba2e3aada4d8006d3d7eeb3ba7fe0c317227d933c4" +checksum = "a9c1b1af02288f729e95b72bd17988c009aa72e26dcb59b3200f86d7aea726c9" dependencies = [ "aws-credential-types", "aws-runtime", @@ -317,9 +317,9 @@ dependencies = [ [[package]] name = "aws-sdk-ssooidc" -version = "1.90.0" +version = "1.91.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9be14d6d9cd761fac3fd234a0f47f7ed6c0df62d83c0eeb7012750e4732879b" +checksum = "4e8122301558dc7c6c68e878af918880b82ff41897a60c8c4e18e4dc4d93e9f1" dependencies = [ "aws-credential-types", "aws-runtime", @@ -339,9 +339,9 @@ dependencies = [ [[package]] name = "aws-sdk-sts" -version = "1.90.0" +version = "1.92.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98a862d704c817d865c8740b62d8bbeb5adcb30965e93b471df8a5bcefa20a80" +checksum = "a0c7808adcff8333eaa76a849e6de926c6ac1a1268b9fd6afe32de9c29ef29d2" dependencies = [ "aws-credential-types", "aws-runtime", @@ -401,9 +401,9 @@ dependencies = [ [[package]] name = "aws-smithy-checksums" -version = "0.63.10" +version = "0.63.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb9a26b2831e728924ec0089e92697a78a2f9cdcf90d81e8cfcc6a6c85080369" +checksum = "95bd108f7b3563598e4dc7b62e1388c9982324a2abd622442167012690184591" dependencies = [ "aws-smithy-http", "aws-smithy-types", @@ -470,7 +470,7 @@ dependencies = [ "http-body 0.4.6", "http-body 1.0.1", "hyper 0.14.32", - "hyper 1.7.0", + "hyper 1.8.0", "hyper-rustls 0.24.2", "hyper-rustls 0.27.7", "hyper-util", @@ -759,9 +759,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.44" +version = "1.2.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37521ac7aabe3d13122dc382493e20c9416f299d2ccd5b3a5340a2570cdeb0f3" +checksum = "35900b6c8d709fb1d854671ae27aeaa9eec2f8b01b364e1619a40da3e6fe2afe" dependencies = [ "find-msvc-tools", "jobserver", @@ -954,15 +954,15 @@ checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" [[package]] name = "crc-fast" -version = "1.3.0" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bf62af4cc77d8fe1c22dde4e721d87f2f54056139d8c412e1366b740305f56f" +checksum = "6ddc2d09feefeee8bd78101665bd8645637828fa9317f9f292496dbbd8c65ff3" dependencies = [ "crc", "digest", - "libc", "rand", "regex", + "rustversion", ] [[package]] @@ -1004,9 +1004,9 @@ dependencies = [ [[package]] name = "crypto-common" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" dependencies = [ "generic-array", "typenum", @@ -1268,9 +1268,9 @@ dependencies = [ [[package]] name = "generic-array" -version = "0.14.9" +version = "0.14.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bb6743198531e02858aeaea5398fcc883e71851fcbcb5a2f773e2fb6cb1edf2" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ "typenum", "version_check", @@ -1507,9 +1507,9 @@ dependencies = [ [[package]] name = "hyper" -version = "1.7.0" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb3aa54a13a0dfe7fbe3a59e0c76093041720fdc77b110cc0fc260fafb4dc51e" +checksum = "1744436df46f0bde35af3eda22aeaba453aada65d8f1c171cd8a5f59030bd69f" dependencies = [ "atomic-waker", "bytes", @@ -1550,7 +1550,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58" dependencies = [ "http 1.3.1", - "hyper 1.7.0", + "hyper 1.8.0", "hyper-util", "rustls 0.23.35", "rustls-native-certs 0.8.2", @@ -1574,7 +1574,7 @@ dependencies = [ "futures-util", "http 1.3.1", "http-body 1.0.1", - "hyper 1.7.0", + "hyper 1.8.0", "ipnet", "libc", "percent-encoding", @@ -2181,9 +2181,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.41" +version = "1.0.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce25767e7b499d1b604768e7cde645d14cc8584231ea6b295e9c9eb22c02e1d1" +checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f" dependencies = [ "proc-macro2", ] @@ -2290,7 +2290,7 @@ dependencies = [ "http 1.3.1", "http-body 1.0.1", "http-body-util", - "hyper 1.7.0", + "hyper 1.8.0", "hyper-rustls 0.27.7", "hyper-util", "js-sys", @@ -2750,9 +2750,9 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "syn" -version = "2.0.108" +version = "2.0.110" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da58917d35242480a05c2897064da0a80589a2a0476c9a3f2fdc83b53502e917" +checksum = "a99801b5bd34ede4cf3fc688c5919368fea4e4814a4664359503e6015b280aea" dependencies = [ "proc-macro2", "quote", diff --git a/decoder/Cargo.lock b/decoder/Cargo.lock index dc6b6ad..73bb746 100644 --- a/decoder/Cargo.lock +++ b/decoder/Cargo.lock @@ -110,9 +110,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.44" +version = "1.2.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37521ac7aabe3d13122dc382493e20c9416f299d2ccd5b3a5340a2570cdeb0f3" +checksum = "35900b6c8d709fb1d854671ae27aeaa9eec2f8b01b364e1619a40da3e6fe2afe" dependencies = [ "find-msvc-tools", "jobserver", @@ -223,9 +223,9 @@ dependencies = [ [[package]] name = "crypto-common" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" dependencies = [ "generic-array", "typenum", @@ -293,9 +293,9 @@ dependencies = [ [[package]] name = "generic-array" -version = "0.14.9" +version = "0.14.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bb6743198531e02858aeaea5398fcc883e71851fcbcb5a2f773e2fb6cb1edf2" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ "typenum", "version_check", @@ -531,9 +531,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.41" +version = "1.0.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce25767e7b499d1b604768e7cde645d14cc8584231ea6b295e9c9eb22c02e1d1" +checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f" dependencies = [ "proc-macro2", ] @@ -643,9 +643,9 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "syn" -version = "2.0.108" +version = "2.0.110" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da58917d35242480a05c2897064da0a80589a2a0476c9a3f2fdc83b53502e917" +checksum = "a99801b5bd34ede4cf3fc688c5919368fea4e4814a4664359503e6015b280aea" dependencies = [ "proc-macro2", "quote", From b8cae268e5b80c1bdf2269c5468afd0eee7f8716 Mon Sep 17 00:00:00 2001 From: Ariel Ben-Yehuda Date: Tue, 28 Oct 2025 12:46:19 +0000 Subject: [PATCH 3/3] feat: add parsing of CPU and Memory fields --- Cargo.lock | 29 +++++++- Cargo.toml | 4 ++ src/metadata/aws.rs | 75 ++++++++++++++++++--- src/metadata/mod.rs | 161 ++++++++++++++++++++++++++++++++++++++++---- src/reporter/s3.rs | 24 ++++--- 5 files changed, 257 insertions(+), 36 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a18eb6c..3e488b8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -122,7 +122,8 @@ dependencies = [ "futures", "humantime", "libloading 0.9.0", - "rand", + "ordered-float", + "rand 0.9.2", "reqwest", "serde", "serde_json", @@ -960,7 +961,7 @@ checksum = "6ddc2d09feefeee8bd78101665bd8645637828fa9317f9f292496dbbd8c65ff3" dependencies = [ "crc", "digest", - "rand", + "rand 0.9.2", "regex", "rustversion", ] @@ -2003,6 +2004,17 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" +[[package]] +name = "ordered-float" +version = "5.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f4779c6901a562440c3786d08192c6fbda7c1c2060edd10006b05ee35d10f2d" +dependencies = [ + "num-traits", + "rand 0.8.5", + "serde", +] + [[package]] name = "outref" version = "0.5.2" @@ -2153,7 +2165,7 @@ dependencies = [ "bytes", "getrandom 0.3.4", "lru-slab", - "rand", + "rand 0.9.2", "ring", "rustc-hash", "rustls 0.23.35", @@ -2194,6 +2206,16 @@ version = "5.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "rand_core 0.6.4", + "serde", +] + [[package]] name = "rand" version = "0.9.2" @@ -2221,6 +2243,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ "getrandom 0.2.16", + "serde", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index b1a2173..2f052f2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,6 +19,7 @@ chrono = "0.4" futures = { version = "0.3", default-features = false, features = ["alloc"] } libloading = "0.9" reqwest = { version = "0.12", default-features = false, optional = true, features = ["charset", "http2"] } +ordered-float = { version = "5", features=["serde"]} serde_json = "1" serde = { version = "1", features = ["derive"] } tempfile = "3" @@ -49,6 +50,9 @@ s3-no-defaults = ["dep:aws-config", "dep:aws-sdk-s3"] aws-metadata = ["aws-metadata-no-defaults", "aws-config/default", "reqwest/rustls-tls"] # A version of the aws-metadata feature that does not enable AWS default features aws-metadata-no-defaults = ["dep:reqwest", "dep:aws-config", "dep:aws-arn"] +# *Unstable* feature to allow Fargate CPU count. The feature will be stabilized +# and removed soon. +__unstable-fargate-cpu-count = [] [package.metadata.docs.rs] all-features = true diff --git a/src/metadata/aws.rs b/src/metadata/aws.rs index 62f1ca7..0ee4693 100644 --- a/src/metadata/aws.rs +++ b/src/metadata/aws.rs @@ -9,6 +9,8 @@ use reqwest::Method; use serde::Deserialize; use thiserror::Error; +use crate::metadata::OrderedF64; + use super::AgentMetadata; /// An error converting Fargate IMDS metadata to Agent metadata. This error @@ -80,12 +82,25 @@ async fn read_ec2_metadata() -> Result, + #[serde(rename = "Memory")] + memory: Option, +} + #[derive(Deserialize, Debug, PartialEq, Eq)] struct FargateMetadata { #[serde(rename = "Cluster")] cluster: String, #[serde(rename = "TaskARN")] task_arn: String, + // According to + // Limits: The resource limits specified at the task levels such as CPU (expressed in vCPUs) and memory. + // This parameter is omitted if no resource limits are defined. + #[serde(rename = "Limits")] + limits: Option, } async fn read_fargate_metadata( @@ -144,6 +159,16 @@ impl super::AgentMetadata { .to_string(), ecs_task_arn: fargate_metadata.task_arn, ecs_cluster_arn: fargate_metadata.cluster, + #[cfg(feature = "__unstable-fargate-cpu-count")] + cpu_limit: fargate_metadata + .limits + .as_ref() + .and_then(|limits| limits.cpu), + #[cfg(feature = "__unstable-fargate-cpu-count")] + memory_limit: fargate_metadata + .limits + .as_ref() + .and_then(|limits| limits.memory), }) } } @@ -173,6 +198,7 @@ pub async fn load_agent_metadata() -> Result, + _expected_cpu_limit: Option, + _expected_memory_limit: Option, + ) { + let fargate_metadata: FargateMetadata = serde_json::from_str(json_str).unwrap(); assert_eq!( fargate_metadata, @@ -327,6 +381,7 @@ mod tests { cluster: "arn:aws:ecs:us-east-1:123456789012:cluster/profiler-metadata-cluster" .to_owned(), task_arn: "arn:aws:ecs:us-east-1:123456789012:task/profiler-metadata-cluster/5261e761e0e2a3d92da3f02c8e5bab1f".to_owned(), + limits: expected_limits, } ); @@ -339,6 +394,10 @@ mod tests { aws_region_id: "us-east-1".to_owned(), ecs_task_arn: "arn:aws:ecs:us-east-1:123456789012:task/profiler-metadata-cluster/5261e761e0e2a3d92da3f02c8e5bab1f".to_owned(), ecs_cluster_arn: "arn:aws:ecs:us-east-1:123456789012:cluster/profiler-metadata-cluster".to_owned(), + #[cfg(feature = "__unstable-fargate-cpu-count")] + cpu_limit: _expected_cpu_limit, + #[cfg(feature = "__unstable-fargate-cpu-count")] + memory_limit: _expected_memory_limit, } ) } diff --git a/src/metadata/mod.rs b/src/metadata/mod.rs index 821f95f..8248b69 100644 --- a/src/metadata/mod.rs +++ b/src/metadata/mod.rs @@ -3,8 +3,38 @@ //! This module includes ways to get metadata attached to profiling reports. +use ordered_float::OrderedFloat; pub use std::time::Duration; +/// An ordered float. Individual type to avoid public API dependencies. +#[derive(Debug, Copy, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)] +#[serde(transparent)] +pub struct OrderedF64(OrderedFloat); + +impl OrderedF64 { + /// Create a new `OrderedF64` + /// + /// ```rust + /// # use async_profiler_agent::metadata::OrderedF64; + /// let _v = OrderedF64::new(0.0); + /// ``` + pub const fn new(value: f64) -> Self { + Self(OrderedFloat(value)) + } +} + +impl From for OrderedF64 { + fn from(value: f64) -> Self { + Self(OrderedFloat(value)) + } +} + +impl From for f64 { + fn from(value: OrderedF64) -> Self { + value.0.0 + } +} + /// Host Metadata, which describes a host that runs a profiling agent. The current set of supported agent metadata is /// AWS-specific. If you are not running on AWS, you can use [AgentMetadata::NoMetadata]. #[derive(Debug, Clone, PartialEq, Eq)] @@ -13,6 +43,7 @@ pub enum AgentMetadata { /// Metadata for an [EC2] instance running on AWS /// /// [EC2]: https://aws.amazon.com/ec2 + #[cfg_attr(feature = "__unstable-fargate-cpu-count", non_exhaustive)] Ec2AgentMetadata { /// The AWS account id aws_account_id: String, @@ -24,6 +55,7 @@ pub enum AgentMetadata { /// Metadata for a [Fargate] task running on AWS. /// /// [Fargate]: https://aws.amazon.com/fargate + #[cfg_attr(feature = "__unstable-fargate-cpu-count", non_exhaustive)] FargateAgentMetadata { /// The AWS account id aws_account_id: String, @@ -41,6 +73,20 @@ pub enum AgentMetadata { /// /// See the ECS documentation for more details ecs_cluster_arn: String, + /// The CPU limit for the Fargate cluster + /// + /// For example, `Some(0.25)`. This will be `None` if the CPU limit is not specified. + /// + /// See the ECS documentation for more details + #[cfg(feature = "__unstable-fargate-cpu-count")] + cpu_limit: Option, + /// The memory limit for the Fargate cluster (in megabytes) + /// + /// For example, `Some(2048)`. This will be `None` if the memory limit is not specified. + /// + /// See the ECS documentation for more details + #[cfg(feature = "__unstable-fargate-cpu-count")] + memory_limit: Option, }, /// Metadata for a host that is neither an EC2 nor a Fargate #[deprecated = "Use AgentMetadata::NoMetadata"] @@ -51,54 +97,141 @@ pub enum AgentMetadata { } impl AgentMetadata { - /// Create metadata corresponding to an EC2 instance running on AWS. + /// Create a builder for EC2 agent metadata /// - /// This function is versioned to allow for extensibility. + /// Normally, for real-world use, you would get the metadata using + /// autodetection via + #[cfg_attr( + feature = "aws-metadata-no-defaults", + doc = "[aws::load_agent_metadata]," + )] + #[cfg_attr( + feature = "aws-metadata-no-defaults", + doc = "`aws::load_agent_metadata`," + )] + /// this function is intended for use in tests. + /// + /// ## Example /// /// ```rust /// # use async_profiler_agent::metadata::AgentMetadata; - /// let _metadata = AgentMetadata::ec2_agent_metadata_v1( + /// let metadata = AgentMetadata::ec2_agent_metadata( /// "123456789012".to_string(), /// "us-east-1".to_string(), /// "i-1234567890abcdef0".to_string(), - /// ); + /// ).build(); /// ``` - pub fn ec2_agent_metadata_v1( + pub fn ec2_agent_metadata( aws_account_id: String, aws_region_id: String, ec2_instance_id: String, - ) -> Self { - Self::Ec2AgentMetadata { + ) -> Ec2AgentMetadataBuilder { + Ec2AgentMetadataBuilder { aws_account_id, aws_region_id, ec2_instance_id, } } - /// Create metadata corresponding to a Fargate task running on AWS. + /// Create a builder for Fargate agent metadata + /// + /// Normally, for real-world use, you would get the metadata using + /// autodetection via + #[cfg_attr( + feature = "aws-metadata-no-defaults", + doc = "[aws::load_agent_metadata]," + )] + #[cfg_attr( + feature = "aws-metadata-no-defaults", + doc = "`aws::load_agent_metadata`," + )] + /// this function is intended for use in tests. /// - /// This function is versioned to allow for extensibility. + /// ## Example /// /// ```rust /// # use async_profiler_agent::metadata::AgentMetadata; - /// let _metadata = AgentMetadata::fargate_agent_metadata_v1( + /// let metadata = AgentMetadata::fargate_agent_metadata( /// "123456789012".to_string(), /// "us-east-1".to_string(), /// "arn:aws:ecs:us-east-1:123456789012:task/cluster/5261e761e0e2a3d92da3f02c8e5bab1f".to_string(), /// "arn:aws:ecs:us-east-1:123456789012:cluster/profiler-metadata-cluster".to_string(), - /// ); + /// ) + /// .with_cpu_limit(0.25) + /// .with_memory_limit(2048) + /// .build(); /// ``` - pub fn fargate_agent_metadata_v1( + pub fn fargate_agent_metadata( aws_account_id: String, aws_region_id: String, ecs_task_arn: String, ecs_cluster_arn: String, - ) -> Self { - Self::FargateAgentMetadata { + ) -> FargateAgentMetadataBuilder { + FargateAgentMetadataBuilder { aws_account_id, aws_region_id, ecs_task_arn, ecs_cluster_arn, + cpu_limit: None, + memory_limit: None, + } + } +} + +/// Builder for EC2 agent metadata +#[derive(Debug, Clone)] +pub struct Ec2AgentMetadataBuilder { + aws_account_id: String, + aws_region_id: String, + ec2_instance_id: String, +} + +impl Ec2AgentMetadataBuilder { + /// Build the AgentMetadata + pub fn build(self) -> AgentMetadata { + AgentMetadata::Ec2AgentMetadata { + aws_account_id: self.aws_account_id, + aws_region_id: self.aws_region_id, + ec2_instance_id: self.ec2_instance_id, + } + } +} + +/// Builder for Fargate agent metadata +#[derive(Debug, Clone)] +pub struct FargateAgentMetadataBuilder { + aws_account_id: String, + aws_region_id: String, + ecs_task_arn: String, + ecs_cluster_arn: String, + cpu_limit: Option, + memory_limit: Option, +} + +impl FargateAgentMetadataBuilder { + /// Set the CPU limit (in vCPUs) + pub fn with_cpu_limit(mut self, cpu_limit: f64) -> Self { + self.cpu_limit = Some(cpu_limit); + self + } + + /// Set the memory limit (in megabytes) + pub fn with_memory_limit(mut self, memory_limit: u64) -> Self { + self.memory_limit = Some(memory_limit); + self + } + + /// Build the AgentMetadata + pub fn build(self) -> AgentMetadata { + AgentMetadata::FargateAgentMetadata { + aws_account_id: self.aws_account_id, + aws_region_id: self.aws_region_id, + ecs_task_arn: self.ecs_task_arn, + ecs_cluster_arn: self.ecs_cluster_arn, + #[cfg(feature = "__unstable-fargate-cpu-count")] + cpu_limit: self.cpu_limit.map(Into::into), + #[cfg(feature = "__unstable-fargate-cpu-count")] + memory_limit: self.memory_limit, } } } diff --git a/src/reporter/s3.rs b/src/reporter/s3.rs index a4dfe29..ad11e02 100644 --- a/src/reporter/s3.rs +++ b/src/reporter/s3.rs @@ -132,6 +132,7 @@ fn make_s3_file_name( aws_account_id: _, aws_region_id: _, ec2_instance_id, + .. } => { let ec2_instance_id = ec2_instance_id.replace("/", "-").replace("_", "-"); format!("ec2_{ec2_instance_id}_") @@ -141,6 +142,7 @@ fn make_s3_file_name( aws_region_id: _, ecs_task_arn, ecs_cluster_arn: _, + .. } => { let task_arn = ecs_task_arn.replace("/", "-").replace("_", "-"); format!("ecs_{task_arn}_") @@ -256,17 +258,17 @@ mod test { #[test_case(#[allow(deprecated)] { AgentMetadata::Other }, "profile_pg_onprem____