1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
use std::collections::HashMap;

use crate::{
    data::UpcloudServerRoot, UpcloudApi, UpcloudError, UpcloudLabelList,
    UpcloudServer,
};
use serde::Serialize;
use serde_json::{json, Value};

#[derive(Serialize, Debug)]
struct CreateInstanceConfig {
    region: String,
    plan: String,
    labels: Option<UpcloudLabelList>,
    #[serde(skip_serializing_if = "Option::is_none")]
    os_id: Option<String>,
}

/// Builder struct for creating instances.
///
/// A detailed documentation can be found at <https://www.vultr.com/api/#tag/instances/operation/create-instance>
pub struct CreateInstanceBuilder {
    api: UpcloudApi,
    config: Value,
}

impl CreateInstanceBuilder {
    pub fn new<S1, S2, S3, S4, S5>(
        api: UpcloudApi,
        region_id: S1,
        plan_id: S2,
        os_id: S3,
        title: S4,
        hostname: S5,
    ) -> Self
    where
        S1: Into<String> + Serialize,
        S2: Into<String> + Serialize,
        S3: Into<String> + Serialize,
        S4: Into<String> + Serialize,
        S5: Into<String> + Serialize,
    {
        let mut instancebuilder = CreateInstanceBuilder {
            api,
            config: json!(    {
                "server":{
                    "title": title,
                    "zone": region_id,
                    "hostname": hostname,
                    "plan": plan_id,
                    "metadata": "yes"
                }
            }),
        };
        instancebuilder.config["server"]["storage_devices"] = json!({
            "storage_device": [
                {
                    "action": "clone",
                    "storage": os_id,
                    "title": title,
                    "tier": "maxiops"
                }
            ]
        });
        instancebuilder
    }

    /// The user-supplied, base64 encoded user data to attach to this instance.
    pub fn user_data<S>(mut self, user_data: S) -> Self
    where
        S: Into<String>,
    {
        self.config["server"]["user_data"] = Value::String(user_data.into());
        self
    }

    /// Labels to apply to the instance
    pub fn labels(mut self, labels: HashMap<String, String>) -> Self {
        let labels_vec: Vec<Value> = labels
            .iter()
            .map(|(k, v)| json!({"key": k.to_string(), "value": v.to_string()}))
            .collect();
        self.config["server"]["labels"]["label"] = Value::Array(labels_vec);
        self
    }

    #[cfg(feature = "blocking")]
    pub fn run(self) -> Result<VultrInstance, UpcloudError> {
        let url = format!("https://api.Upcloud.com/1.3/server");
        Ok(self
            .api
            .post(&url, self.config)?
            .json::<VultrInstanceRoot>()?
            .instance)
    }

    pub async fn run_async(self) -> Result<UpcloudServer, UpcloudError> {
        let url = format!("https://api.Upcloud.com/1.3/server");
        Ok(self
            .api
            .post_async(&url, self.config)
            .await?
            .json::<UpcloudServerRoot>()
            .await?
            .server)
    }
}