Skip to content

Commit 7b734a7

Browse files
Merge pull request #236 from knusbaum/knusbaum/troubleshooting
Add instrumentation telemetry endpoint
2 parents 8ef45e2 + 1af1742 commit 7b734a7

6 files changed

Lines changed: 160 additions & 6 deletions

File tree

docs/COMMANDS.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ pup <domain> <subgroup> <action> [options] # Nested commands
5050
| error-tracking | issues (search, get) | src/commands/error_tracking.rs ||
5151
| scorecards | list, get | src/commands/scorecards.rs ||
5252
| usage | summary, hourly | src/commands/usage.rs ||
53-
| apm | services (list, stats, operations, resources), entities (list), dependencies (list), flow-map | src/commands/apm.rs ||
53+
| apm | services (list, stats, operations, resources), entities (list), dependencies (list), flow-map, troubleshooting (list) | src/commands/apm.rs ||
5454
| cost | projected, attribution, by-org, aws-config (list, get, create, delete), azure-config (list, get, create, delete), gcp-config (list, get, create, delete) | src/commands/cost.rs ||
5555
| product-analytics | events send | src/commands/product_analytics.rs ||
5656
| data-governance | scanner-rules (list) | src/commands/data_governance.rs ||

docs/EXAMPLES.md

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -285,6 +285,17 @@ pup security findings search \
285285
--query="@severity:high"
286286
```
287287

288+
## APM Troubleshooting
289+
290+
### List Instrumentation Errors for a Host
291+
```bash
292+
# Show APM instrumentation errors for a specific host
293+
pup apm troubleshooting list --hostname my-host
294+
295+
# Narrow results to a specific time window
296+
pup apm troubleshooting list --hostname my-host --timeframe 4h
297+
```
298+
288299
## Infrastructure
289300

290301
### List Hosts
@@ -296,6 +307,20 @@ pup infrastructure hosts list
296307
pup infrastructure hosts list --filter="env:production"
297308
```
298309

310+
## Fleet
311+
312+
### List Fleet Agents
313+
```bash
314+
# Filter agents by hostname
315+
pup fleet agents list --filter "hostname:my-host"
316+
317+
# Filter by IP address
318+
pup fleet agents list --filter "ip_address:1.2.3.4"
319+
320+
# Boolean filter expression
321+
pup fleet agents list --filter "(hostname:host-a OR hostname:host-b) AND env:prod"
322+
```
323+
299324
### Get Host
300325
```bash
301326
pup infrastructure hosts get "host-name"

src/commands/apm.rs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,3 +200,34 @@ pub async fn flow_map(
200200
let data = crate::api::get(cfg, "/api/ui/apm/flow-map", &q).await?;
201201
crate::formatter::output(cfg, &data)
202202
}
203+
204+
#[cfg(not(target_arch = "wasm32"))]
205+
pub async fn troubleshooting_list(
206+
cfg: &Config,
207+
hostname: String,
208+
timeframe: Option<String>,
209+
) -> Result<()> {
210+
let path = "/api/unstable/apm/instrumentation-errors";
211+
let mut query = vec![("hostname", hostname.as_str())];
212+
let tf_owned;
213+
if let Some(tf) = &timeframe {
214+
tf_owned = tf.clone();
215+
query.push(("timeframe", tf_owned.as_str()));
216+
}
217+
let data = client::raw_get(cfg, path, &query).await?;
218+
formatter::output(cfg, &data)
219+
}
220+
221+
#[cfg(target_arch = "wasm32")]
222+
pub async fn troubleshooting_list(
223+
cfg: &Config,
224+
hostname: String,
225+
timeframe: Option<String>,
226+
) -> Result<()> {
227+
let mut query = vec![("hostname", hostname)];
228+
if let Some(tf) = timeframe {
229+
query.push(("timeframe", tf));
230+
}
231+
let data = crate::api::get(cfg, "/api/unstable/apm/instrumentation-errors", &query).await?;
232+
crate::formatter::output(cfg, &data)
233+
}

src/commands/fleet.rs

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,11 @@ use crate::formatter;
1212
use crate::util;
1313

1414
#[cfg(not(target_arch = "wasm32"))]
15-
pub async fn agents_list(cfg: &Config, page_size: Option<i64>) -> Result<()> {
15+
pub async fn agents_list(
16+
cfg: &Config,
17+
page_size: Option<i64>,
18+
filter: Option<String>,
19+
) -> Result<()> {
1620
let dd_cfg = client::make_dd_config(cfg);
1721
let api = match client::make_bearer_client(cfg) {
1822
Some(c) => FleetAutomationAPI::with_client_and_config(dd_cfg, c),
@@ -22,6 +26,9 @@ pub async fn agents_list(cfg: &Config, page_size: Option<i64>) -> Result<()> {
2226
if let Some(ps) = page_size {
2327
params = params.page_size(ps);
2428
}
29+
if let Some(f) = filter {
30+
params = params.filter(f);
31+
}
2532
let resp = api
2633
.list_fleet_agents(params)
2734
.await
@@ -30,11 +37,18 @@ pub async fn agents_list(cfg: &Config, page_size: Option<i64>) -> Result<()> {
3037
}
3138

3239
#[cfg(target_arch = "wasm32")]
33-
pub async fn agents_list(cfg: &Config, page_size: Option<i64>) -> Result<()> {
40+
pub async fn agents_list(
41+
cfg: &Config,
42+
page_size: Option<i64>,
43+
filter: Option<String>,
44+
) -> Result<()> {
3445
let mut query: Vec<(&str, String)> = Vec::new();
3546
if let Some(ps) = page_size {
3647
query.push(("page[size]", ps.to_string()));
3748
}
49+
if let Some(f) = filter {
50+
query.push(("filter", f));
51+
}
3852
let data = crate::api::get(cfg, "/api/v2/fleet/agents", &query).await?;
3953
crate::formatter::output(cfg, &data)
4054
}

src/main.rs

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4136,6 +4136,11 @@ enum FleetAgentActions {
41364136
List {
41374137
#[arg(long)]
41384138
page_size: Option<i64>,
4139+
#[arg(
4140+
long,
4141+
help = "Filter query (e.g. ip_address:1.2.3.4, hostname:my-host)"
4142+
)]
4143+
filter: Option<String>,
41394144
},
41404145
/// Get fleet agent details
41414146
Get { agent_key: String },
@@ -4756,6 +4761,11 @@ enum ApmActions {
47564761
#[arg(long, help = "Environment filter")]
47574762
env: Option<String>,
47584763
},
4764+
/// Troubleshoot APM instrumentation issues
4765+
Troubleshooting {
4766+
#[command(subcommand)]
4767+
action: ApmTroubleshootingActions,
4768+
},
47594769
}
47604770

47614771
#[derive(Subcommand)]
@@ -4854,6 +4864,17 @@ enum ApmDependencyActions {
48544864
},
48554865
}
48564866

4867+
#[derive(Subcommand)]
4868+
enum ApmTroubleshootingActions {
4869+
/// List instrumentation errors for a host
4870+
List {
4871+
#[arg(long, help = "Hostname to query (required)")]
4872+
hostname: String,
4873+
#[arg(long, help = "Time window (e.g. 4h, 24h, 1h30m)")]
4874+
timeframe: Option<String>,
4875+
},
4876+
}
4877+
48574878
// ---- Investigations ----
48584879
#[derive(Subcommand)]
48594880
enum InvestigationActions {
@@ -7400,8 +7421,8 @@ async fn main_inner() -> anyhow::Result<()> {
74007421
cfg.validate_auth()?;
74017422
match action {
74027423
FleetActions::Agents { action } => match action {
7403-
FleetAgentActions::List { page_size } => {
7404-
commands::fleet::agents_list(&cfg, page_size).await?;
7424+
FleetAgentActions::List { page_size, filter } => {
7425+
commands::fleet::agents_list(&cfg, page_size, filter).await?;
74057426
}
74067427
FleetAgentActions::Get { agent_key } => {
74077428
commands::fleet::agents_get(&cfg, &agent_key).await?;
@@ -7894,6 +7915,14 @@ async fn main_inner() -> anyhow::Result<()> {
78947915
} => {
78957916
commands::apm::flow_map(&cfg, query, limit, from, to).await?;
78967917
}
7918+
ApmActions::Troubleshooting { action } => match action {
7919+
ApmTroubleshootingActions::List {
7920+
hostname,
7921+
timeframe,
7922+
} => {
7923+
commands::apm::troubleshooting_list(&cfg, hostname, timeframe).await?;
7924+
}
7925+
},
78977926
}
78987927
}
78997928
// --- DDSQL ---

src/test_commands.rs

Lines changed: 56 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1427,7 +1427,7 @@ async fn test_fleet_agents_list() {
14271427
let mut s = mockito::Server::new_async().await;
14281428
let cfg = test_config(&s.url());
14291429
mock_all(&mut s, r#"{"data": []}"#).await;
1430-
let _ = crate::commands::fleet::agents_list(&cfg, None).await;
1430+
let _ = crate::commands::fleet::agents_list(&cfg, None, None).await;
14311431
cleanup_env();
14321432
}
14331433
#[tokio::test]
@@ -2054,6 +2054,61 @@ async fn test_apm_services_list() {
20542054
crate::commands::apm::services_list(&cfg, "prod".into(), "1h".into(), "now".into()).await;
20552055
cleanup_env();
20562056
}
2057+
#[tokio::test]
2058+
async fn test_apm_troubleshooting_list() {
2059+
let _lock = lock_env();
2060+
let mut server = mockito::Server::new_async().await;
2061+
let cfg = test_config(&server.url());
2062+
2063+
let mock = server
2064+
.mock("GET", "/api/unstable/apm/instrumentation-errors")
2065+
.match_query(mockito::Matcher::UrlEncoded(
2066+
"hostname".into(),
2067+
"my-host".into(),
2068+
))
2069+
.with_status(200)
2070+
.with_header("content-type", "application/json")
2071+
.with_body(r#"{"data": []}"#)
2072+
.create_async()
2073+
.await;
2074+
2075+
let result = crate::commands::apm::troubleshooting_list(&cfg, "my-host".into(), None).await;
2076+
assert!(
2077+
result.is_ok(),
2078+
"troubleshooting list failed: {:?}",
2079+
result.err()
2080+
);
2081+
mock.assert_async().await;
2082+
cleanup_env();
2083+
}
2084+
#[tokio::test]
2085+
async fn test_apm_troubleshooting_list_with_timeframe() {
2086+
let _lock = lock_env();
2087+
let mut server = mockito::Server::new_async().await;
2088+
let cfg = test_config(&server.url());
2089+
2090+
let mock = server
2091+
.mock("GET", "/api/unstable/apm/instrumentation-errors")
2092+
.match_query(mockito::Matcher::AllOf(vec![
2093+
mockito::Matcher::UrlEncoded("hostname".into(), "my-host".into()),
2094+
mockito::Matcher::UrlEncoded("timeframe".into(), "4h".into()),
2095+
]))
2096+
.with_status(200)
2097+
.with_header("content-type", "application/json")
2098+
.with_body(r#"{"data": []}"#)
2099+
.create_async()
2100+
.await;
2101+
2102+
let result =
2103+
crate::commands::apm::troubleshooting_list(&cfg, "my-host".into(), Some("4h".into())).await;
2104+
assert!(
2105+
result.is_ok(),
2106+
"troubleshooting list with timeframe failed: {:?}",
2107+
result.err()
2108+
);
2109+
mock.assert_async().await;
2110+
cleanup_env();
2111+
}
20572112

20582113
// -------------------------------------------------------------------------
20592114
// Read-only mode

0 commit comments

Comments
 (0)