diff --git a/Cargo.lock b/Cargo.lock index a01cafe..4d36683 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -862,8 +862,8 @@ checksum = "a4ae5f15dda3c708c0ade84bfee31ccab44a3da4f88015ed22f63732abe300c8" [[package]] name = "datadog-api-client" -version = "0.31.0" -source = "git+https://github.com/DataDog/datadog-api-client-rust?rev=d4954b117c5451c0a8932dd0ad0db450fcb0989c#d4954b117c5451c0a8932dd0ad0db450fcb0989c" +version = "0.32.0" +source = "git+https://github.com/DataDog/datadog-api-client-rust?rev=b769778d0c58806b615165fb444e6f02dc2e86c0#b769778d0c58806b615165fb444e6f02dc2e86c0" dependencies = [ "async-stream", "chrono", diff --git a/Cargo.toml b/Cargo.toml index 5ee75cc..cc992cf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -127,9 +127,9 @@ ssh-key = { version = "0.6", features = ["p256", "std"], optional = true } futures = { version = "0.3", optional = true } tokio-util = { version = "0.7", features = ["compat", "io"], optional = true } -# Datadog API client — pinned to master HEAD 2026-05-29 +# Datadog API client — pinned to 0.32.0 tag # Use default-features = false; feature sets are activated per-target via features above -datadog-api-client = { git = "https://github.com/DataDog/datadog-api-client-rust", rev = "d4954b117c5451c0a8932dd0ad0db450fcb0989c", optional = true, default-features = false } +datadog-api-client = { git = "https://github.com/DataDog/datadog-api-client-rust", rev = "b769778d0c58806b615165fb444e6f02dc2e86c0", optional = true, default-features = false } # HTTP middleware (version-matched to DD client; compiles on all targets) reqwest-middleware = "0.5" diff --git a/src/commands/feature_flags.rs b/src/commands/feature_flags.rs index fcbc07f..151362a 100644 --- a/src/commands/feature_flags.rs +++ b/src/commands/feature_flags.rs @@ -35,10 +35,10 @@ pub async fn flags_list( params = params.is_archived(v); } if let Some(v) = limit { - params = params.limit(v); + params = params.limit(v as i64); } if let Some(v) = offset { - params = params.offset(v); + params = params.offset(v as i64); } let resp = api .list_feature_flags(params) @@ -128,10 +128,10 @@ pub async fn envs_list( params = params.key(v); } if let Some(v) = limit { - params = params.limit(v); + params = params.limit(v as i64); } if let Some(v) = offset { - params = params.offset(v); + params = params.offset(v as i64); } let resp = api .list_feature_flags_environments(params) diff --git a/src/commands/fleet.rs b/src/commands/fleet.rs index e9da0b8..8b06350 100644 --- a/src/commands/fleet.rs +++ b/src/commands/fleet.rs @@ -1,8 +1,8 @@ use anyhow::Result; use datadog_api_client::datadogV2::api_fleet_automation::{ FleetAutomationAPI, GetFleetDeploymentOptionalParams, ListFleetAgentTracersOptionalParams, - ListFleetAgentsOptionalParams, ListFleetClustersOptionalParams, - ListFleetDeploymentsOptionalParams, ListFleetTracersOptionalParams, + ListFleetAgentsOptionalParams, ListFleetDeploymentsOptionalParams, + ListFleetTracersOptionalParams, }; use crate::config::Config; @@ -218,47 +218,6 @@ pub async fn agents_tracers_list( formatter::output(cfg, &resp) } -pub async fn clusters_list( - cfg: &Config, - filter: Option, - page_size: Option, - page_number: Option, - sort_attribute: Option, - sort_descending: bool, -) -> Result<()> { - let api = crate::make_api!(FleetAutomationAPI, cfg); - let mut params = ListFleetClustersOptionalParams::default(); - if let Some(f) = filter { - params = params.filter(f); - } - if let Some(ps) = page_size { - params = params.page_size(ps); - } - if let Some(pn) = page_number { - params = params.page_number(pn); - } - if let Some(sa) = sort_attribute { - params = params.sort_attribute(sa); - } - if sort_descending { - params = params.sort_descending(true); - } - let resp = api - .list_fleet_clusters(params) - .await - .map_err(|e| anyhow::anyhow!("failed to list fleet clusters: {e:?}"))?; - formatter::output(cfg, &resp) -} - -pub async fn instrumented_pods_list(cfg: &Config, cluster_name: String) -> Result<()> { - let api = crate::make_api!(FleetAutomationAPI, cfg); - let resp = api - .list_fleet_instrumented_pods(cluster_name) - .await - .map_err(|e| anyhow::anyhow!("failed to list instrumented pods: {e:?}"))?; - formatter::output(cfg, &resp) -} - #[cfg(test)] mod tests { @@ -375,55 +334,6 @@ mod tests { cleanup_env(); } - #[tokio::test] - async fn test_fleet_instrumented_pods_list() { - let _lock = lock_env().await; - let mut server = mockito::Server::new_async().await; - let cfg = test_config(&server.url()); - - let mock = server - .mock( - "GET", - "/api/unstable/fleet/clusters/my-cluster/instrumented_pods", - ) - .with_status(200) - .with_header("content-type", "application/json") - .with_body( - r#"{"data":{"type":"cluster_name","id":"my-cluster","attributes":{"groups":[]}}}"#, - ) - .create_async() - .await; - - let result = super::instrumented_pods_list(&cfg, "my-cluster".into()).await; - assert!( - result.is_ok(), - "instrumented_pods_list failed: {:?}", - result.err() - ); - mock.assert_async().await; - cleanup_env(); - } - - #[tokio::test] - async fn test_fleet_clusters_list() { - let _lock = lock_env().await; - let mut server = mockito::Server::new_async().await; - let cfg = test_config(&server.url()); - - let mock = server - .mock("GET", "/api/unstable/fleet/clusters") - .with_status(200) - .with_header("content-type", "application/json") - .with_body(r#"{"data":{"type":"status","id":"done","attributes":{"clusters":[]}}}"#) - .create_async() - .await; - - let result = super::clusters_list(&cfg, None, None, None, None, false).await; - assert!(result.is_ok(), "clusters_list failed: {:?}", result.err()); - mock.assert_async().await; - cleanup_env(); - } - #[tokio::test] async fn test_fleet_deployments_list() { let _lock = lock_env().await; diff --git a/src/commands/incidents.rs b/src/commands/incidents.rs index 4d13bfe..b4a4e06 100644 --- a/src/commands/incidents.rs +++ b/src/commands/incidents.rs @@ -1,7 +1,4 @@ use anyhow::{bail, Result}; -use datadog_api_client::datadogV2::api_incident_services::{ - GetIncidentServiceOptionalParams, IncidentServicesAPI, ListIncidentServicesOptionalParams, -}; use datadog_api_client::datadogV2::api_incidents::{ CreateGlobalIncidentHandleOptionalParams, GetIncidentOptionalParams, ImportIncidentOptionalParams, IncidentsAPI, ListGlobalIncidentHandlesOptionalParams, @@ -223,64 +220,6 @@ pub async fn postmortem_templates_delete(cfg: &Config, template_id: &str) -> Res Ok(()) } -// --------------------------------------------------------------------------- -// Incident services -// --------------------------------------------------------------------------- - -fn make_services_api(cfg: &Config) -> IncidentServicesAPI { - crate::make_api!(IncidentServicesAPI, cfg) -} - -pub async fn services_list(cfg: &Config) -> Result<()> { - let api = make_services_api(cfg); - let resp = api - .list_incident_services(ListIncidentServicesOptionalParams::default()) - .await - .map_err(|e| anyhow::anyhow!("failed to list incident services: {:?}", e))?; - formatter::output(cfg, &resp) -} - -pub async fn services_get(cfg: &Config, service_id: &str) -> Result<()> { - let api = make_services_api(cfg); - let resp = api - .get_incident_service( - service_id.to_string(), - GetIncidentServiceOptionalParams::default(), - ) - .await - .map_err(|e| anyhow::anyhow!("failed to get incident service: {:?}", e))?; - formatter::output(cfg, &resp) -} - -pub async fn services_create(cfg: &Config, file: &str) -> Result<()> { - let body = util::read_json_file(file)?; - let api = make_services_api(cfg); - let resp = api - .create_incident_service(body) - .await - .map_err(|e| anyhow::anyhow!("failed to create incident service: {:?}", e))?; - formatter::output(cfg, &resp) -} - -pub async fn services_update(cfg: &Config, service_id: &str, file: &str) -> Result<()> { - let body = util::read_json_file(file)?; - let api = make_services_api(cfg); - let resp = api - .update_incident_service(service_id.to_string(), body) - .await - .map_err(|e| anyhow::anyhow!("failed to update incident service: {:?}", e))?; - formatter::output(cfg, &resp) -} - -pub async fn services_delete(cfg: &Config, service_id: &str) -> Result<()> { - let api = make_services_api(cfg); - api.delete_incident_service(service_id.to_string()) - .await - .map_err(|e| anyhow::anyhow!("failed to delete incident service: {:?}", e))?; - println!("Incident service {service_id} deleted."); - Ok(()) -} - // ---- Import ---- pub async fn import(cfg: &Config, file: &str) -> Result<()> { @@ -477,41 +416,4 @@ mod tests { let _ = super::postmortem_templates_list(&cfg).await; cleanup_env(); } - - #[tokio::test] - async fn test_incident_services_list() { - let _lock = lock_env().await; - std::env::set_var("DD_TOKEN_STORAGE", "file"); - let mut server = mockito::Server::new_async().await; - let cfg = test_config(&server.url()); - let _mock = mock_any(&mut server, "GET", r#"{"data":[],"meta":{}}"#).await; - let result = super::services_list(&cfg).await; - assert!( - result.is_ok(), - "incident services list failed: {:?}", - result.err() - ); - cleanup_env(); - std::env::remove_var("DD_TOKEN_STORAGE"); - } - - #[tokio::test] - async fn test_incident_services_list_error() { - let _lock = lock_env().await; - std::env::set_var("DD_TOKEN_STORAGE", "file"); - let mut server = mockito::Server::new_async().await; - let cfg = test_config(&server.url()); - let _mock = server - .mock("GET", mockito::Matcher::Any) - .match_query(mockito::Matcher::Any) - .with_status(403) - .with_header("content-type", "application/json") - .with_body(r#"{"errors":["Forbidden"]}"#) - .create_async() - .await; - let result = super::services_list(&cfg).await; - assert!(result.is_err(), "incident services list should fail on 403"); - cleanup_env(); - std::env::remove_var("DD_TOKEN_STORAGE"); - } } diff --git a/src/commands/rum.rs b/src/commands/rum.rs index a6a8a20..69f956c 100644 --- a/src/commands/rum.rs +++ b/src/commands/rum.rs @@ -282,7 +282,7 @@ pub async fn playlists_list(cfg: &Config) -> Result<()> { pub async fn playlists_get(cfg: &Config, playlist_id: i32) -> Result<()> { let api = crate::make_api!(RumReplayPlaylistsAPI, cfg); let resp = api - .get_rum_replay_playlist(playlist_id) + .get_rum_replay_playlist(playlist_id as i64) .await .map_err(|e| anyhow::anyhow!("failed to get RUM playlist: {e:?}"))?; formatter::output(cfg, &resp) diff --git a/src/commands/seats.rs b/src/commands/seats.rs index a1978c6..449889b 100644 --- a/src/commands/seats.rs +++ b/src/commands/seats.rs @@ -12,7 +12,7 @@ fn make_api(cfg: &Config) -> SeatsAPI { pub async fn users_list(cfg: &Config, product: &str, limit: i32) -> Result<()> { let api = make_api(cfg); - let params = GetSeatsUsersOptionalParams::default().page_limit(limit); + let params = GetSeatsUsersOptionalParams::default().page_limit(limit as i64); let resp = api .get_seats_users(product.to_string(), params) .await diff --git a/src/commands/security.rs b/src/commands/security.rs index 72fa945..18d7081 100644 --- a/src/commands/security.rs +++ b/src/commands/security.rs @@ -11,8 +11,9 @@ use datadog_api_client::datadogV2::api_restriction_policies::{ RestrictionPoliciesAPI, UpdateRestrictionPolicyOptionalParams, }; use datadog_api_client::datadogV2::api_security_monitoring::{ - ListFindingsOptionalParams, ListIndicatorsOfCompromiseOptionalParams, - ListSecurityMonitoringRulesOptionalParams, ListSecurityMonitoringSuppressionsOptionalParams, + GetIndicatorOfCompromiseOptionalParams, ListFindingsOptionalParams, + ListIndicatorsOfCompromiseOptionalParams, ListSecurityMonitoringRulesOptionalParams, + ListSecurityMonitoringSuppressionsOptionalParams, SearchSecurityMonitoringSignalsOptionalParams, SecurityMonitoringAPI, }; use datadog_api_client::datadogV2::model::{ @@ -471,7 +472,10 @@ pub async fn iocs_list( pub async fn iocs_get(cfg: &Config, indicator: &str) -> Result<()> { let api = crate::make_api!(SecurityMonitoringAPI, cfg); let resp = api - .get_indicator_of_compromise(indicator.to_string()) + .get_indicator_of_compromise( + indicator.to_string(), + GetIndicatorOfCompromiseOptionalParams::default(), + ) .await .map_err(|e| anyhow::anyhow!("failed to get indicator of compromise: {e:?}"))?; formatter::output(cfg, &resp) diff --git a/src/commands/widgets.rs b/src/commands/widgets.rs index fbf1f9a..6836ad7 100644 --- a/src/commands/widgets.rs +++ b/src/commands/widgets.rs @@ -63,10 +63,10 @@ pub async fn list( params = params.sort(s); } if let Some(n) = page_number { - params = params.page_number(n); + params = params.page_number(n as i64); } if let Some(n) = page_size { - params = params.page_size(n); + params = params.page_size(n as i64); } let resp = api diff --git a/src/main.rs b/src/main.rs index a56d064..09901fc 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3132,11 +3132,6 @@ enum IncidentActions { #[command(subcommand)] action: IncidentPostmortemActions, }, - /// Manage incident services - Services { - #[command(subcommand)] - action: IncidentServiceActions, - }, /// Import an incident Import { #[arg(long, help = "JSON file with request body (required)")] @@ -3144,27 +3139,6 @@ enum IncidentActions { }, } -#[derive(Subcommand)] -enum IncidentServiceActions { - /// List incident services - List, - /// Get incident service details - Get { service_id: String }, - /// Create an incident service from JSON - Create { - #[arg(long, help = "JSON file with service data (required)")] - file: String, - }, - /// Update an incident service - Update { - service_id: String, - #[arg(long, help = "JSON file with service data (required)")] - file: String, - }, - /// Delete an incident service - Delete { service_id: String }, -} - #[derive(Subcommand)] enum IncidentAttachmentActions { /// List incident attachments @@ -6658,22 +6632,11 @@ enum FleetActions { #[command(subcommand)] action: FleetScheduleActions, }, - /// Manage fleet clusters - Clusters { - #[command(subcommand)] - action: FleetClusterActions, - }, /// Manage fleet tracers Tracers { #[command(subcommand)] action: FleetTracerActions, }, - /// Manage fleet instrumented pods - #[command(name = "instrumented-pods")] - InstrumentedPods { - #[command(subcommand)] - action: FleetInstrumentedPodsActions, - }, } #[derive(Subcommand)] @@ -6776,38 +6739,6 @@ enum FleetTracerActions { }, } -#[derive(Subcommand)] -enum FleetClusterActions { - /// List Kubernetes clusters in the fleet. - /// - /// Returns clusters with node counts, agent versions, enabled products, and services. - /// Use this to discover cluster names for use with instrumented-pods. - List { - #[arg(long, help = "Filter query (e.g. cluster_name:production, env:prod)")] - filter: Option, - #[arg(long)] - page_size: Option, - #[arg(long, help = "Page number (0-indexed)")] - page_number: Option, - #[arg(long, help = "Sort by attribute (e.g. cluster_name, node_count)")] - sort_attribute: Option, - #[arg(long, default_value_t = false, help = "Sort descending")] - sort_descending: bool, - }, -} - -#[derive(Subcommand)] -enum FleetInstrumentedPodsActions { - /// List instrumented pods in a Kubernetes cluster. - /// - /// Returns pod groups with namespace, owner, injection annotations, and pod names. - /// Use this to verify the Admission Controller targeted pods for SSI injection. - List { - #[arg(help = "Kubernetes cluster name (required)")] - cluster_name: String, - }, -} - // ---- Data Deletion ---- #[derive(Subcommand)] enum DataDeletionActions { @@ -11278,23 +11209,6 @@ async fn main_inner() -> anyhow::Result<()> { .await?; } }, - IncidentActions::Services { action } => match action { - IncidentServiceActions::List => { - commands::incidents::services_list(&cfg).await?; - } - IncidentServiceActions::Get { service_id } => { - commands::incidents::services_get(&cfg, &service_id).await?; - } - IncidentServiceActions::Create { file } => { - commands::incidents::services_create(&cfg, &file).await?; - } - IncidentServiceActions::Update { service_id, file } => { - commands::incidents::services_update(&cfg, &service_id, &file).await?; - } - IncidentServiceActions::Delete { service_id } => { - commands::incidents::services_delete(&cfg, &service_id).await?; - } - }, IncidentActions::Import { file } => { commands::incidents::import(&cfg, &file).await?; } @@ -13208,25 +13122,6 @@ async fn main_inner() -> anyhow::Result<()> { commands::fleet::schedules_trigger(&cfg, &schedule_id).await?; } }, - FleetActions::Clusters { action } => match action { - FleetClusterActions::List { - filter, - page_size, - page_number, - sort_attribute, - sort_descending, - } => { - commands::fleet::clusters_list( - &cfg, - filter, - page_size, - page_number, - sort_attribute, - sort_descending, - ) - .await?; - } - }, FleetActions::Tracers { action } => match action { FleetTracerActions::List { filter, @@ -13246,11 +13141,6 @@ async fn main_inner() -> anyhow::Result<()> { .await?; } }, - FleetActions::InstrumentedPods { action } => match action { - FleetInstrumentedPodsActions::List { cluster_name } => { - commands::fleet::instrumented_pods_list(&cfg, cluster_name).await?; - } - }, } } // --- DBM ---