Skip to content

Commit 5646fcb

Browse files
Regenerate client from spec repo
1 parent 6347a25 commit 5646fcb

4 files changed

Lines changed: 88 additions & 101 deletions

File tree

src/auth/storage.rs

Lines changed: 7 additions & 100 deletions
Original file line numberDiff line numberDiff line change
@@ -223,19 +223,9 @@ impl KeychainStorage {
223223
.delete_credential()
224224
.map_err(|e| anyhow::anyhow!("keychain probe cleanup failed: {e}"))?;
225225
}
226-
// On Linux, also perform a read probe so we fail fast when the
227-
// Secret Service DBus name is unavailable and can fall back to file
228-
// storage instead of erroring later during auth flows.
229-
#[cfg(target_os = "linux")]
230-
{
231-
let entry = keyring::Entry::new(SERVICE_NAME, "__pup_probe__")
232-
.map_err(|e| anyhow::anyhow!("keychain not available: {e}"))?;
233-
linux_keychain_probe_result(entry.get_password())?;
234-
}
235-
// On macOS and other non-Windows targets, constructing an Entry is
236-
// sufficient to confirm the backend is present; avoid a spurious macOS
237-
// authorization dialog.
238-
#[cfg(all(not(target_os = "windows"), not(target_os = "linux")))]
226+
// On macOS and Linux, constructing an Entry is sufficient to confirm the
227+
// backend is present; avoid a spurious macOS authorization dialog.
228+
#[cfg(not(target_os = "windows"))]
239229
keyring::Entry::new(SERVICE_NAME, "__pup_probe__")
240230
.map_err(|e| anyhow::anyhow!("keychain not available: {e}"))?;
241231
Ok(Self {
@@ -244,16 +234,6 @@ impl KeychainStorage {
244234
}
245235
}
246236

247-
#[cfg(all(not(target_arch = "wasm32"), target_os = "linux"))]
248-
fn linux_keychain_probe_result(
249-
probe_result: std::result::Result<String, keyring::Error>,
250-
) -> Result<()> {
251-
match probe_result {
252-
Ok(_) | Err(keyring::Error::NoEntry) => Ok(()),
253-
Err(e) => Err(anyhow::anyhow!("keychain not available: {e}")),
254-
}
255-
}
256-
257237
/// Combined per-site state stored in a single keychain entry.
258238
/// Consolidating tokens + client credentials into one entry reduces macOS
259239
/// authorization dialogs from 2 → 1 per site on first access. KeychainStorage
@@ -722,32 +702,10 @@ fn detect_backend_with(try_keychain: impl Fn() -> Result<KeychainStorage>) -> Bo
722702
match try_keychain() {
723703
Ok(ks) => Box::new(ks),
724704
Err(e) => {
725-
// On Linux, the default backend is Secret Service (DBus). If that is
726-
// unavailable, fall back to the kernel keyring (keyutils) which does
727-
// not require a desktop session or running daemon.
728-
#[cfg(target_os = "linux")]
729-
{
730-
eprintln!("Warning: Secret Service not available ({e}), trying kernel keyring");
731-
keyring::set_default_credential_builder(
732-
keyring::keyutils::default_credential_builder(),
733-
);
734-
match try_keychain() {
735-
Ok(ks) => Box::new(ks),
736-
Err(e2) => {
737-
eprintln!(
738-
"Warning: kernel keyring also unavailable ({e2}), using file storage (~/.config/pup/)"
739-
);
740-
Box::new(FileStorage::new().expect("failed to create file storage"))
741-
}
742-
}
743-
}
744-
#[cfg(not(target_os = "linux"))]
745-
{
746-
eprintln!(
747-
"Warning: OS keychain not available ({e}), using file storage (~/.config/pup/)"
748-
);
749-
Box::new(FileStorage::new().expect("failed to create file storage"))
750-
}
705+
eprintln!(
706+
"Warning: OS keychain not available ({e}), using file storage (~/.config/pup/)"
707+
);
708+
Box::new(FileStorage::new().expect("failed to create file storage"))
751709
}
752710
}
753711
}
@@ -1817,35 +1775,8 @@ mod tests {
18171775

18181776
// --- detect_backend ---------------------------------------------------------
18191777

1820-
#[test]
1821-
#[cfg(all(not(target_arch = "wasm32"), target_os = "linux"))]
1822-
fn test_linux_keychain_probe_result_accepts_no_entry() {
1823-
let result = linux_keychain_probe_result(Err(keyring::Error::NoEntry));
1824-
assert!(result.is_ok());
1825-
}
1826-
1827-
#[test]
1828-
#[cfg(all(not(target_arch = "wasm32"), target_os = "linux"))]
1829-
fn test_linux_keychain_probe_result_rejects_platform_failure() {
1830-
let err = linux_keychain_probe_result(Err(keyring::Error::PlatformFailure(Box::new(
1831-
std::io::Error::new(
1832-
std::io::ErrorKind::NotFound,
1833-
"org.freedesktop.DBus.Error.ServiceUnknown",
1834-
),
1835-
))))
1836-
.unwrap_err()
1837-
.to_string();
1838-
assert!(err.contains("keychain not available"));
1839-
assert!(
1840-
err.contains("ServiceUnknown"),
1841-
"expected dbus service error in message, got: {err}"
1842-
);
1843-
}
1844-
18451778
// Exercises the FileStorage fallback when the auto-detect keychain probe fails,
18461779
// without requiring OS-level credential-store mocking.
1847-
// On Linux this also exercises the kernel-keyring intermediate fallback (which
1848-
// also fails because the injected probe always returns Err).
18491780
#[test]
18501781
#[cfg(not(target_arch = "wasm32"))]
18511782
fn test_detect_backend_with_probe_failure_falls_back_to_file() {
@@ -1858,30 +1789,6 @@ mod tests {
18581789
assert_eq!(backend.backend_type(), BackendType::File);
18591790
}
18601791

1861-
// On Linux, when the Secret Service probe fails but kernel keyring succeeds,
1862-
// the backend should be Keychain (keyutils-backed).
1863-
#[test]
1864-
#[cfg(all(not(target_arch = "wasm32"), target_os = "linux"))]
1865-
fn test_detect_backend_with_secret_service_failure_falls_back_to_keyutils() {
1866-
let _lock = crate::test_utils::ENV_LOCK.blocking_lock();
1867-
let tmp = TempDir::new("detect_keyutils");
1868-
std::env::set_var("PUP_CONFIG_DIR", tmp.path());
1869-
std::env::remove_var("DD_TOKEN_STORAGE");
1870-
let call_count = std::sync::atomic::AtomicU32::new(0);
1871-
let backend = detect_backend_with(|| {
1872-
let n = call_count.fetch_add(1, std::sync::atomic::Ordering::SeqCst);
1873-
if n == 0 {
1874-
// First call: simulate Secret Service unavailable
1875-
Err(anyhow::anyhow!("secret service unavailable"))
1876-
} else {
1877-
// Second call: keyutils backend probe succeeds
1878-
KeychainStorage::new()
1879-
}
1880-
});
1881-
std::env::remove_var("PUP_CONFIG_DIR");
1882-
assert_eq!(backend.backend_type(), BackendType::Keychain);
1883-
}
1884-
18851792
// When DD_TOKEN_STORAGE=keychain is explicitly set but the backend is
18861793
// unavailable, the process panics with a clear message rather than silently
18871794
// falling back (explicit opt-in should fail loudly).

src/auth/types.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -235,7 +235,6 @@ pub fn default_scopes() -> Vec<&'static str> {
235235
"slos_read",
236236
"slos_write",
237237
// Status Pages
238-
"status_pages_notice_write",
239238
"status_pages_settings_read",
240239
"status_pages_settings_write",
241240
// Synthetics

src/downtime.rs

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
use anyhow::Result;
2+
use datadog_api_client::datadogV2::api_downtimes::{DowntimesAPI, GetDowntimeOptionalParams};
3+
4+
use crate::config::Config;
5+
use crate::formatter;
6+
7+
#[derive(clap::Subcommand)]
8+
pub enum Command {
9+
/// Get a downtime
10+
///
11+
/// Get downtime detail by `downtime_id`.
12+
Get {
13+
/// ID of the downtime to fetch.
14+
downtime_id: String,
15+
/// Comma-separated list of resource paths for related resources to include in the response. Supported resource
16+
/// paths are `created_by` and `monitor`.
17+
#[arg(long)] include: Option<String>,
18+
},
19+
}
20+
21+
pub async fn run(cfg: &Config, command: Command) -> Result<()> {
22+
match command {
23+
Command::Get { downtime_id, include } => get(cfg, downtime_id, include).await,
24+
}
25+
}
26+
27+
/// Get a downtime
28+
///
29+
/// Get downtime detail by `downtime_id`.
30+
pub async fn get(cfg: &Config, downtime_id: String, include: Option<String>) -> Result<()> {
31+
let api = crate::make_api!(DowntimesAPI, cfg);
32+
let mut params = GetDowntimeOptionalParams::default();
33+
if let Some(v) = include {
34+
params = params.include(v);
35+
}
36+
let resp = api
37+
.get_downtime(downtime_id, params)
38+
.await
39+
.map_err(|e| anyhow::anyhow!("failed to get_downtime: {:?}", e))?;
40+
formatter::output(cfg, &resp)
41+
}
42+
43+
#[cfg(test)]
44+
mod tests {
45+
use crate::test_support::*;
46+
47+
#[tokio::test]
48+
async fn test_get_ok() {
49+
let _lock = lock_env().await;
50+
let mut server = mockito::Server::new_async().await;
51+
let cfg = test_config(&server.url());
52+
let _mock = mock_any(&mut server, "GET", r##"{"data": {"attributes": {"created": "2024-01-01T00:00:00+00:00", "display_timezone": "America/New_York", "message": "Message about the downtime", "modified": "2024-01-01T00:00:00+00:00", "monitor_identifier": {"monitor_tags": ["*"]}, "mute_first_recovery_notification": false, "notify_end_states": ["alert", "warn"], "notify_end_types": ["canceled", "expired"], "scope": "env:(staging OR prod) AND datacenter:us-east-1", "status": "active"}, "id": "00000000-0000-1234-0000-000000000000", "type": "downtime"}}"##).await;
53+
let result = super::get(&cfg, "test".to_string(), None).await;
54+
assert!(result.is_ok(), "get failed: {:?}", result.err());
55+
cleanup_env();
56+
}
57+
}

src/mod.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
pub mod downtime;
2+
3+
use anyhow::Result;
4+
5+
use crate::config::Config;
6+
7+
/// All spec-generated pup commands.
8+
///
9+
/// This file is generated — do not edit by hand. Re-run the generator to add
10+
/// or remove tags; pup never needs manual changes for new commands.
11+
#[derive(clap::Subcommand)]
12+
pub enum GeneratedCommand {
13+
/// Manage downtime resources
14+
Downtime {
15+
#[command(subcommand)]
16+
action: downtime::Command,
17+
},
18+
}
19+
20+
pub async fn run(cfg: &Config, command: GeneratedCommand) -> Result<()> {
21+
match command {
22+
GeneratedCommand::Downtime { action } => downtime::run(cfg, action).await,
23+
}
24+
}

0 commit comments

Comments
 (0)