daytona_client/
volume.rs

1//! Volume management functionality
2
3use tracing::{debug, info};
4use uuid::Uuid;
5
6use crate::{
7    client::DaytonaClient,
8    error::Result,
9    models::volume::{CreateVolumeRequest, VolumeDto},
10};
11
12/// Volume manager for volume operations
13pub struct VolumeManager<'a> {
14    client: &'a DaytonaClient,
15}
16
17impl<'a> VolumeManager<'a> {
18    pub(crate) fn new(client: &'a DaytonaClient) -> Self {
19        Self { client }
20    }
21
22    /// Create a new volume
23    pub async fn create(&self, name: String) -> Result<VolumeDto> {
24        info!("Creating new volume: {}", name);
25
26        let request = CreateVolumeRequest { name };
27
28        let response = self
29            .client
30            .build_request(reqwest::Method::POST, "/volumes")
31            .json(&request)
32            .send()
33            .await?;
34
35        self.client.handle_response(response).await
36    }
37
38    /// Delete a volume by ID
39    pub async fn delete(&self, volume_id: &Uuid) -> Result<()> {
40        info!("Deleting volume: {}", volume_id);
41
42        let response = self
43            .client
44            .build_request(reqwest::Method::DELETE, &format!("/volumes/{}", volume_id))
45            .send()
46            .await?;
47
48        if response.status().is_success() {
49            Ok(())
50        } else {
51            let error_text = response.text().await?;
52            Err(crate::error::DaytonaError::RequestFailed(format!(
53                "Failed to delete volume: {}",
54                error_text
55            )))
56        }
57    }
58
59    /// Get volume details by ID
60    pub async fn get(&self, volume_id: &Uuid) -> Result<VolumeDto> {
61        debug!("Getting volume: {}", volume_id);
62
63        let response = self
64            .client
65            .build_request(reqwest::Method::GET, &format!("/volumes/{}", volume_id))
66            .send()
67            .await?;
68
69        self.client.handle_response(response).await
70    }
71
72    /// Get volume details by name
73    pub async fn get_by_name(&self, name: &str) -> Result<VolumeDto> {
74        debug!("Getting volume by name: {}", name);
75
76        let response = self
77            .client
78            .build_request(reqwest::Method::GET, &format!("/volumes/by-name/{}", name))
79            .send()
80            .await?;
81
82        self.client.handle_response(response).await
83    }
84
85    /// List all volumes
86    pub async fn list(&self, include_deleted: bool) -> Result<Vec<VolumeDto>> {
87        debug!("Listing volumes (include_deleted: {})", include_deleted);
88
89        let mut url = "/volumes".to_string();
90        if include_deleted {
91            url.push_str("?includeDeleted=true");
92        }
93
94        let response = self
95            .client
96            .build_request(reqwest::Method::GET, &url)
97            .send()
98            .await?;
99
100        self.client.handle_response(response).await
101    }
102
103    /// Wait for volume to reach a specific state
104    pub async fn wait_for_state(
105        &self,
106        volume_id: &Uuid,
107        target_state: crate::models::volume::VolumeState,
108        max_wait_seconds: u64,
109    ) -> Result<VolumeDto> {
110        let start = std::time::Instant::now();
111        let max_duration = std::time::Duration::from_secs(max_wait_seconds);
112
113        loop {
114            let volume = self.get(volume_id).await?;
115
116            if volume.state == target_state {
117                return Ok(volume);
118            }
119
120            // Check for error state
121            if volume.state == crate::models::volume::VolumeState::Error {
122                return Err(crate::error::DaytonaError::RequestFailed(format!(
123                    "Volume entered error state: {}",
124                    volume
125                        .error_reason
126                        .unwrap_or_else(|| "Unknown error".to_string())
127                )));
128            }
129
130            // Check timeout
131            if start.elapsed() > max_duration {
132                return Err(crate::error::DaytonaError::RequestFailed(format!(
133                    "Timeout waiting for volume to reach {:?} state",
134                    target_state
135                )));
136            }
137
138            // Wait before retrying
139            tokio::time::sleep(tokio::time::Duration::from_secs(2)).await;
140        }
141    }
142}
143
144impl DaytonaClient {
145    /// Get volume manager
146    pub fn volumes(&self) -> VolumeManager<'_> {
147        VolumeManager::new(self)
148    }
149}