digitalocean_api/api/
droplet.rs

1use self::droplet_fields::{Kernel, Networks, NextBackupWindow};
2use super::snapshot::Snapshot;
3use super::{ApiLinks, ApiMeta};
4use super::{HasPagination, HasResponse, HasValue};
5use super::{Image, Region, Size};
6use crate::method::{Create, Delete, Get, List};
7use crate::request::Request;
8use crate::request::{DropletRequest, SnapshotRequest};
9use crate::{ROOT_URL, STATIC_URL_ERROR};
10use chrono::{DateTime, Utc};
11use getset::{Getters, Setters};
12use serde::Deserialize;
13use serde::Serialize;
14use std::fmt::Display;
15use url::Url;
16
17const DROPLETS_SEGMENT: &str = "droplets";
18const REPORTS_SEGMENT: &str = "reports";
19const DROPLET_NEIGHBORS_SEGMENT: &str = "droplet_neighbors";
20const NEIGHBORS_SEGMENT: &str = "neighbors";
21const SNAPSHOTS_SEGMENT: &str = "snapshots";
22const BACKUPS_SEGMENT: &str = "backups";
23
24/// A Droplet is a DigitalOcean virtual machine. By sending requests to the
25/// Droplet endpoint, you can list, create, or delete Droplets.
26///
27/// Some of the attributes will have an object value. The region and image
28/// objects will all contain the standard attributes of their associated types.
29/// Find more information about each of these objects in their respective
30/// sections.
31///
32/// [Digital Ocean Documentation.](https://developers.digitalocean.com/documentation/v2/#domains)
33#[derive(Deserialize, Serialize, Debug, Clone, Getters, Setters)]
34#[get = "pub"]
35pub struct Droplet {
36    /// A unique identifier for each Droplet instance. This is automatically
37    /// generated upon Droplet creation.
38    id: usize,
39
40    /// The human-readable name set for the Droplet instance.
41    name: String,
42
43    /// Memory of the Droplet in megabytes.
44    memory: usize,
45
46    /// The number of virtual CPUs.
47    vcpus: usize,
48
49    /// The size of the Droplet's disk in gigabytes.
50    disk: usize,
51
52    /// A boolean value indicating whether the Droplet has been locked,
53    /// preventing actions by users.
54    locked: bool,
55
56    /// A time value given in ISO8601 combined date and time format that
57    /// represents when the Droplet was created.
58    created_at: DateTime<Utc>,
59
60    /// A status string indicating the state of the Droplet instance. This may
61    /// be "new", "active", "off", or "archive".
62    status: String,
63
64    /// An array of backup IDs of any backups that have been taken of the
65    /// Droplet instance. Droplet backups are enabled at the time of the
66    /// instance creation.
67    backup_ids: Vec<usize>,
68
69    /// An array of snapshot IDs of any snapshots created from the Droplet
70    /// instance.
71    snapshot_ids: Vec<usize>,
72
73    /// An array of features enabled on this Droplet.
74    features: Vec<String>,
75
76    /// The region that the Droplet instance is deployed in. When setting a
77    /// region, the value should be the slug identifier for the region. When
78    /// you query a Droplet, the entire region object will be returned.
79    region: Region,
80
81    /// The base image used to create the Droplet instance. When setting an
82    /// image, the value is set to the image id or slug. When querying the
83    /// Droplet, the entire image object will be returned.
84    image: Image,
85
86    /// The current size object describing the Droplet. When setting a size,
87    /// the value is set to the size slug. When querying the Droplet, the
88    /// entire size object will be returned. Note that the disk volume of a
89    /// Droplet may not match the size's disk due to Droplet resize actions.
90    /// The disk attribute on the Droplet should always be referenced.
91    size: Size,
92
93    /// The unique slug identifier for the size of this Droplet.
94    size_slug: String,
95
96    /// The details of the network that are configured for the Droplet
97    /// instance. This is an object that contains keys for IPv4 and IPv6.
98    /// The value of each of these is an array that contains objects describing
99    /// an individual IP resource allocated to the Droplet. These will define
100    /// attributes like the IP address, netmask, and gateway of the specific
101    /// network depending on the type of network it is.
102    networks: Networks,
103
104    /// The current kernel. This will initially be set to the kernel of the
105    /// base image when the Droplet is created.
106    kernel: Option<Kernel>,
107
108    /// The details of the Droplet's backups feature, if backups are configured
109    /// for the Droplet. This object contains keys for the start and end times
110    /// of the window during which the backup will start.
111    next_backup_window: Option<NextBackupWindow>,
112
113    /// An array of Tags the Droplet has been tagged with.
114    tags: Vec<String>,
115
116    /// A flat array including the unique identifier for each Block Storage
117    /// volume attached to the Droplet.
118    volume_ids: Vec<String>,
119}
120
121/// Fields which exists inside Droplets.
122pub mod droplet_fields {
123    use chrono::{DateTime, Utc};
124    use serde::Deserialize;
125    use serde::Serialize;
126    use std::net::{Ipv4Addr, Ipv6Addr};
127
128    /// This exists in the `networks` field of a droplet.
129    #[derive(Deserialize, Serialize, Debug, Clone)]
130    pub struct Networks {
131        pub v4: Vec<NetworkV4>,
132        pub v6: Vec<NetworkV6>,
133    }
134
135    /// These exist in the `networks` field of a droplet.
136    #[derive(Deserialize, Serialize, Debug, Clone)]
137    pub struct NetworkV4 {
138        pub gateway: Ipv4Addr,
139        pub ip_address: Ipv4Addr,
140        pub netmask: Ipv4Addr,
141        /// *Note:* Since `type` is a keyword in Rust `kind` is used instead.
142        #[serde(rename = "type")]
143        pub kind: String,
144    }
145
146    /// These exist in the `networks` field of a droplet.
147    #[derive(Deserialize, Serialize, Debug, Clone)]
148    pub struct NetworkV6 {
149        pub gateway: Ipv6Addr,
150        pub ip_address: Ipv6Addr,
151        pub netmask: usize,
152        /// *Note:* Since `type` is a keyword in Rust `kind` is used instead.
153        #[serde(rename = "type")]
154        pub kind: String,
155    }
156
157    /// This exists in the `next_backup_window` field of a droplet.
158    #[derive(Deserialize, Serialize, Debug, Clone)]
159    pub struct NextBackupWindow {
160        pub end: DateTime<Utc>,
161        pub start: DateTime<Utc>,
162    }
163
164    /// This exists in the `kernel` field of a droplet.
165    #[derive(Deserialize, Serialize, Debug, Clone)]
166    pub struct Kernel {
167        pub id: usize,
168        pub name: String,
169        pub version: String,
170    }
171}
172
173impl Droplet {
174    /// [Digital Ocean Documentation.](https://developers.digitalocean.com/documentation/v2/#create-a-new-droplet)
175    pub fn create<S, D>(name: S, region: S, size: S, image: D) -> DropletRequest<Create, Droplet>
176    where
177        S: AsRef<str> + Serialize + Display,
178        D: Serialize + Display,
179    {
180        let mut url = ROOT_URL.clone();
181        url.path_segments_mut()
182            .expect(STATIC_URL_ERROR)
183            .push(DROPLETS_SEGMENT);
184
185        let mut req = Request::new(url);
186        req.set_body(json!({
187            "name": name,
188            "region": region,
189            "size": size,
190            "image": format!("{}", image),
191        }));
192        req
193    }
194
195    /// [Digital Ocean Documentation.](https://developers.digitalocean.com/documentation/v2/#create-multiple-droplets)
196    pub fn create_multiple<S, D>(
197        names: Vec<S>,
198        region: S,
199        size: S,
200        image: D,
201    ) -> DropletRequest<Create, Vec<Droplet>>
202    where
203        S: AsRef<str> + Serialize + Display,
204        D: Serialize + Display,
205    {
206        let mut url = ROOT_URL.clone();
207        url.path_segments_mut()
208            .expect(STATIC_URL_ERROR)
209            .push(DROPLETS_SEGMENT);
210
211        let mut req = Request::new(url);
212        req.set_body(json!({
213            "names": names,
214            "region": region,
215            "size": size,
216            "image": format!("{}", image),
217        }));
218        req
219    }
220
221    /// [Digital Ocean Documentation.](https://developers.digitalocean.com/documentation/v2/#retrieve-an-existing-droplet-by-id)
222    pub fn get(id: usize) -> DropletRequest<Get, Droplet> {
223        let mut url = ROOT_URL.clone();
224        url.path_segments_mut()
225            .expect(STATIC_URL_ERROR)
226            .push(DROPLETS_SEGMENT)
227            .push(&id.to_string());
228
229        Request::new(url)
230    }
231
232    /// [Digital Ocean Documentation.](https://developers.digitalocean.com/documentation/v2/#list-all-droplets)
233    pub fn list() -> DropletRequest<List, Vec<Droplet>> {
234        let mut url = ROOT_URL.clone();
235        url.path_segments_mut()
236            .expect(STATIC_URL_ERROR)
237            .push(DROPLETS_SEGMENT);
238
239        Request::new(url)
240    }
241
242    /// [Digital Ocean Documentation.](https://developers.digitalocean.com/documentation/v2/#listing-droplets-by-tag)
243    pub fn list_by_tag<S: AsRef<str> + Serialize>(name: S) -> DropletRequest<List, Vec<Droplet>> {
244        let mut url = ROOT_URL.clone();
245        url.path_segments_mut()
246            .expect(STATIC_URL_ERROR)
247            .push(DROPLETS_SEGMENT);
248
249        url.query_pairs_mut().append_pair("tag_name", name.as_ref());
250
251        Request::new(url)
252    }
253
254    /// [Digital Ocean Documentation.](https://developers.digitalocean.com/documentation/v2/#delete-a-droplet)
255    pub fn delete(id: usize) -> DropletRequest<Delete, ()> {
256        let mut url = ROOT_URL.clone();
257        url.path_segments_mut()
258            .expect(STATIC_URL_ERROR)
259            .push(DROPLETS_SEGMENT)
260            .push(&id.to_string());
261
262        Request::new(url)
263    }
264
265    /// [Digital Ocean Documentation.](https://developers.digitalocean.com/documentation/v2/#deleting-droplets-by-tag)
266    pub fn delete_by_tag<S: AsRef<str> + Serialize>(name: S) -> DropletRequest<Delete, ()> {
267        let mut url = ROOT_URL.clone();
268        url.path_segments_mut()
269            .expect(STATIC_URL_ERROR)
270            .push(DROPLETS_SEGMENT);
271
272        url.query_pairs_mut().append_pair("tag_name", name.as_ref());
273
274        Request::new(url)
275    }
276
277    /// [Digital Ocean Documentation.](https://developers.digitalocean.com/documentation/v2/#list-all-droplet-neighbors)
278    pub fn neighbors() -> DropletRequest<Get, Vec<Vec<Droplet>>> {
279        let mut url = ROOT_URL.clone();
280        url.path_segments_mut()
281            .expect(STATIC_URL_ERROR)
282            .push(REPORTS_SEGMENT)
283            .push(DROPLET_NEIGHBORS_SEGMENT);
284
285        Request::new(url)
286    }
287}
288
289impl DropletRequest<Create, Droplet> {
290    /// An array containing the IDs or fingerprints of the SSH keys that you
291    /// wish to embed in the Droplet's root account upon creation.
292    ///
293    /// [Digital Ocean Documentation.](https://developers.digitalocean.com/documentation/v2/#create-a-new-droplet)
294    pub fn ssh_keys<D>(mut self, val: Vec<D>) -> Self
295    where
296        D: Display + Serialize,
297    {
298        self.body_mut()["ssh_keys"] = json!(val);
299        self
300    }
301
302    /// A boolean indicating whether automated backups should be enabled for
303    /// the Droplet. Automated backups can only be enabled when the Droplet is
304    /// created.
305    ///
306    /// [Digital Ocean Documentation.](https://developers.digitalocean.com/documentation/v2/#create-a-new-droplet)
307    pub fn backups(mut self, val: bool) -> Self {
308        self.body_mut()["backups"] = json!(val);
309        self
310    }
311
312    /// A boolean indicating whether IPv6 is enabled on the Droplet.
313    ///
314    /// [Digital Ocean Documentation.](https://developers.digitalocean.com/documentation/v2/#create-a-new-droplet)
315    pub fn ipv6(mut self, val: bool) -> Self {
316        self.body_mut()["ipv6"] = json!(val);
317        self
318    }
319
320    /// A boolean indicating whether private networking is enabled for the
321    /// Droplet. Private networking is currently only available in certain
322    /// regions.
323    ///
324    /// [Digital Ocean Documentation.](https://developers.digitalocean.com/documentation/v2/#create-a-new-droplet)
325    pub fn private_networking(mut self, val: bool) -> Self {
326        self.body_mut()["private_networking"] = json!(val);
327        self
328    }
329
330    /// A string containing 'user data' which may be used to configure the
331    /// Droplet on first boot, often a 'cloud-config' file or Bash script.
332    /// It must be plain text and may not exceed 64 KiB in size.
333    ///
334    /// [Digital Ocean Documentation.](https://developers.digitalocean.com/documentation/v2/#create-a-new-droplet)
335    pub fn user_data(mut self, val: bool) -> Self {
336        self.body_mut()["user_data"] = json!(val);
337        self
338    }
339
340    /// A boolean indicating whether to install the DigitalOcean agent
341    /// for monitoring.
342    ///
343    /// [Digital Ocean Documentation.](https://developers.digitalocean.com/documentation/v2/#create-a-new-droplet)
344    pub fn monitoring(mut self, val: bool) -> Self {
345        self.body_mut()["monitoring"] = json!(val);
346        self
347    }
348
349    /// A flat array including the unique string identifier for each Block
350    /// Storage volume to be attached to the Droplet. At the moment a volume
351    /// can only be attached to a single Droplet.
352    ///
353    /// [Digital Ocean Documentation.](https://developers.digitalocean.com/documentation/v2/#create-a-new-droplet)
354    pub fn volumes(mut self, val: Vec<String>) -> Self {
355        self.body_mut()["volumes"] = json!(val);
356        self
357    }
358
359    /// A flat array of tag names as strings to apply to the Droplet after it
360    /// is created. Tag names can either be existing or new tags.
361    ///
362    /// [Digital Ocean Documentation.](https://developers.digitalocean.com/documentation/v2/#create-a-new-droplet)
363    pub fn tags(mut self, val: Vec<String>) -> Self {
364        self.body_mut()["tags"] = json!(val);
365        self
366    }
367}
368
369impl DropletRequest<Create, Vec<Droplet>> {
370    /// An array containing the IDs or fingerprints of the SSH keys that you
371    /// wish to embed in the Droplet's root account upon creation.
372    ///
373    /// [Digital Ocean Documentation.](https://developers.digitalocean.com/documentation/v2/#create-a-new-droplet)
374    pub fn ssh_keys<D>(mut self, val: Vec<D>) -> Self
375    where
376        D: Display + Serialize,
377    {
378        self.body_mut()["ssh_keys"] = json!(val);
379        self
380    }
381
382    /// A boolean indicating whether automated backups should be enabled for
383    /// the Droplet. Automated backups can only be enabled when the Droplet is
384    /// created.
385    ///
386    /// [Digital Ocean Documentation.](https://developers.digitalocean.com/documentation/v2/#create-a-new-droplet)
387    pub fn backups(mut self, val: bool) -> Self {
388        self.body_mut()["backups"] = json!(val);
389        self
390    }
391
392    /// A boolean indicating whether IPv6 is enabled on the Droplet.
393    ///
394    /// [Digital Ocean Documentation.](https://developers.digitalocean.com/documentation/v2/#create-a-new-droplet)
395    pub fn ipv6(mut self, val: bool) -> Self {
396        self.body_mut()["ipv6"] = json!(val);
397        self
398    }
399
400    /// A boolean indicating whether private networking is enabled for the
401    /// Droplet. Private networking is currently only available in certain
402    /// regions.
403    ///
404    /// [Digital Ocean Documentation.](https://developers.digitalocean.com/documentation/v2/#create-a-new-droplet)
405    pub fn private_networking(mut self, val: bool) -> Self {
406        self.body_mut()["private_networking"] = json!(val);
407        self
408    }
409
410    /// A string containing 'user data' which may be used to configure the
411    /// Droplet on first boot, often a 'cloud-config' file or Bash script.
412    /// It must be plain text and may not exceed 64 KiB in size.
413    ///
414    /// [Digital Ocean Documentation.](https://developers.digitalocean.com/documentation/v2/#create-a-new-droplet)
415    pub fn user_data(mut self, val: bool) -> Self {
416        self.body_mut()["user_data"] = json!(val);
417        self
418    }
419
420    /// A boolean indicating whether to install the DigitalOcean agent
421    /// for monitoring.
422    ///
423    /// [Digital Ocean Documentation.](https://developers.digitalocean.com/documentation/v2/#create-a-new-droplet)
424    pub fn monitoring(mut self, val: bool) -> Self {
425        self.body_mut()["monitoring"] = json!(val);
426        self
427    }
428
429    /// A flat array including the unique string identifier for each Block
430    /// Storage volume to be attached to the Droplet. At the moment a volume
431    /// can only be attached to a single Droplet.
432    ///
433    /// [Digital Ocean Documentation.](https://developers.digitalocean.com/documentation/v2/#create-a-new-droplet)
434    pub fn volumes(mut self, val: Vec<String>) -> Self {
435        self.body_mut()["volumes"] = json!(val);
436        self
437    }
438
439    /// A flat array of tag names as strings to apply to the Droplet after it
440    /// is created. Tag names can either be existing or new tags.
441    ///
442    /// [Digital Ocean Documentation.](https://developers.digitalocean.com/documentation/v2/#create-a-new-droplet)
443    pub fn tags(mut self, val: Vec<String>) -> Self {
444        self.body_mut()["tags"] = json!(val);
445        self
446    }
447}
448
449impl DropletRequest<Get, Droplet> {
450    /// [Digital Ocean Documentation.](https://developers.digitalocean.com/documentation/v2/#list-snapshots-for-a-droplet)
451    pub fn snapshots(mut self) -> SnapshotRequest<List, Vec<Snapshot>> {
452        self.url_mut()
453            .path_segments_mut()
454            .expect(STATIC_URL_ERROR)
455            .push(SNAPSHOTS_SEGMENT);
456
457        self.transmute()
458    }
459
460    /// [Digital Ocean Documentation.](https://developers.digitalocean.com/documentation/v2/#list-backups-for-a-droplet)
461    pub fn backups(mut self) -> SnapshotRequest<List, Vec<Snapshot>> {
462        self.url_mut()
463            .path_segments_mut()
464            .expect(STATIC_URL_ERROR)
465            .push(BACKUPS_SEGMENT);
466
467        self.transmute()
468    }
469
470    /// [Digital Ocean Documentation.](https://developers.digitalocean.com/documentation/v2/#list-neighbors-for-a-droplet)
471    pub fn neighbors(mut self) -> DropletRequest<List, Vec<Droplet>> {
472        self.url_mut()
473            .path_segments_mut()
474            .expect(STATIC_URL_ERROR)
475            .push(NEIGHBORS_SEGMENT);
476
477        self.transmute()
478    }
479}
480
481/// Response type returned from Digital Ocean.
482#[derive(Deserialize, Serialize, Debug, Clone)]
483pub struct DropletResponse {
484    droplet: Droplet,
485}
486
487impl HasResponse for Droplet {
488    type Response = DropletResponse;
489}
490
491impl HasValue for DropletResponse {
492    type Value = Droplet;
493
494    fn value(self) -> Droplet {
495        self.droplet
496    }
497}
498
499/// Response type returned from Digital Ocean.
500#[derive(Deserialize, Serialize, Debug, Clone)]
501pub struct DropletListResponse {
502    droplets: Vec<Droplet>,
503    links: ApiLinks,
504    meta: ApiMeta,
505}
506
507impl HasResponse for Vec<Droplet> {
508    type Response = DropletListResponse;
509}
510
511impl HasPagination for DropletListResponse {
512    fn next_page(&self) -> Option<Url> {
513        self.links.next()
514    }
515}
516
517impl HasValue for DropletListResponse {
518    type Value = Vec<Droplet>;
519
520    fn value(self) -> Vec<Droplet> {
521        self.droplets
522    }
523}
524
525/// Response type returned from Digital Ocean
526#[derive(Deserialize, Serialize, Debug, Clone)]
527pub struct DropletNeighborsResponse {
528    neighbors: Vec<Vec<Droplet>>,
529}
530
531impl HasResponse for Vec<Vec<Droplet>> {
532    type Response = DropletNeighborsResponse;
533}
534
535impl HasValue for DropletNeighborsResponse {
536    type Value = Vec<Vec<Droplet>>;
537
538    fn value(self) -> Vec<Vec<Droplet>> {
539        self.neighbors
540    }
541}