objectscale_client/
tenant.rs

1//! Define the tenant details.
2//!
3use crate::client::ObjectstoreClient;
4use crate::response::get_content_text;
5use anyhow::{bail, Context as _, Result};
6use derive_builder::Builder;
7use reqwest::header::{ACCEPT, AUTHORIZATION, CONTENT_TYPE};
8use serde::{Deserialize, Serialize};
9use serde_aux::field_attributes::deserialize_default_from_null;
10
11/// A tenant is a logical construct resulting from the binding of an account to an object store.
12#[derive(Builder, Clone, Debug, Default, Deserialize, Serialize)]
13#[builder(setter(skip))]
14#[serde(
15    rename_all(serialize = "snake_case", deserialize = "camelCase"),
16    rename(serialize = "tenant_create")
17)]
18pub struct Tenant {
19    /// Name assigned to this resource in ECS. The resource name is set by a user and can be changed at any time. It is not a unique identifier.
20    #[serde(deserialize_with = "deserialize_default_from_null")]
21    pub name: String,
22    /// Identifier that is generated by ECS when the resource is created. The resource Id is guaranteed to be unique and immutable across all virtual data centers for all time.
23    #[builder(setter(into))]
24    #[serde(rename(serialize = "account_id"))]
25    pub id: String,
26    /// Hyperlink to the details for this resource
27    #[serde(deserialize_with = "deserialize_default_from_null")]
28    pub link: String,
29    /// Timestamp that shows when this resource was created in ECS
30    #[serde(deserialize_with = "deserialize_default_from_null")]
31    pub creation_time: String,
32    /// Indicates whether the resource is inactive. When a user removes a resource, the resource is put in this state before it is removed from the ECS database.
33    #[serde(deserialize_with = "deserialize_default_from_null")]
34    pub inactive: bool,
35    /// Indicates whether the resource is global.
36    #[serde(deserialize_with = "deserialize_default_from_null")]
37    pub global: bool,
38    /// Indicates whether the resource is remote.
39    #[serde(deserialize_with = "deserialize_default_from_null")]
40    pub remote: bool,
41    //pub vdc: String,
42    /// Indicated whether the resource is an internal resource
43    #[serde(deserialize_with = "deserialize_default_from_null")]
44    pub internal: bool,
45    pub tenant_default_vpool: String,
46    /// tag to enable encryption for the tenant
47    #[builder(setter(skip = false), default = "false")]
48    pub is_encryption_enabled: bool,
49    /// Default bucket quota size.
50    #[builder(setter(skip = false), default)]
51    pub default_bucket_block_size: i64,
52    /// Tag to enable compliance compliance
53    #[builder(setter(skip = false), default = "false")]
54    pub is_compliance_enabled: bool,
55    pub hard_quota_in_g_b: i64,
56    pub soft_quota_in_g_b: i64,
57    pub hard_quota_in_count: i64,
58    pub soft_quota_in_count: i64,
59    //pub retention_classes: RetentionClasses,
60    /// Alias of tenant
61    #[builder(setter(into))]
62    pub alias: String,
63}
64
65#[derive(Debug, Serialize, Deserialize)]
66#[serde(
67    rename_all(serialize = "snake_case", deserialize = "camelCase"),
68    rename(serialize = "tenant_update")
69)]
70struct TenantUpdate {
71    pub alias: String,
72    pub default_bucket_block_size: i64,
73}
74
75#[derive(Debug, Deserialize)]
76#[serde(rename_all = "camelCase")]
77struct ListTenantsResponse {
78    pub tenant: Vec<Tenant>,
79    #[serde(rename = "Filter")]
80    pub filter: String,
81    #[serde(rename = "NextMarker")]
82    pub next_marker: Option<String>,
83    #[serde(rename = "MaxTenants")]
84    pub max_tenants: Option<u32>,
85    pub next_page_link: Option<String>,
86}
87
88impl Tenant {
89    pub(crate) fn create(client: &mut ObjectstoreClient, tenant: Tenant) -> Result<Tenant> {
90        let request_url = format!("{}object/tenants/tenant", client.endpoint,);
91        let body = quick_xml::se::to_string(&tenant)?;
92        let resp = client
93            .management_client
94            .http_client
95            .post(request_url)
96            .header(ACCEPT, "application/json")
97            .header(
98                AUTHORIZATION,
99                client.management_client.access_token.as_ref().unwrap(),
100            )
101            .header(CONTENT_TYPE, "application/xml")
102            .header("X-EMC-Override", "true")
103            .body(body)
104            .send()?;
105        let text = get_content_text(resp)?;
106        let resp: Tenant = serde_json::from_str(&text).with_context(|| {
107            format!("Unable to deserialise CreateTenant. Body was: \"{}\"", text)
108        })?;
109        Ok(resp)
110    }
111
112    pub(crate) fn update(client: &mut ObjectstoreClient, tenant: Tenant) -> Result<()> {
113        let request_url = format!("{}object/tenants/tenant/{}", client.endpoint, tenant.id);
114        let tenant_update = TenantUpdate {
115            alias: tenant.alias,
116            default_bucket_block_size: tenant.default_bucket_block_size,
117        };
118        let body = quick_xml::se::to_string(&tenant_update)?;
119        let resp = client
120            .management_client
121            .http_client
122            .put(request_url)
123            .header(ACCEPT, "application/json")
124            .header(
125                AUTHORIZATION,
126                client.management_client.access_token.as_ref().unwrap(),
127            )
128            .header(CONTENT_TYPE, "application/xml")
129            .header("X-EMC-Override", "true")
130            .body(body)
131            .send()?;
132        if !resp.status().is_success() {
133            bail!("Request failed: {}", resp.text()?);
134        }
135        Ok(())
136    }
137
138    pub(crate) fn get(client: &mut ObjectstoreClient, name: &str) -> Result<Tenant> {
139        let request_url = format!("{}object/tenants/tenant/{}", client.endpoint, name,);
140        let resp = client
141            .management_client
142            .http_client
143            .get(request_url)
144            .header(ACCEPT, "application/json")
145            .header(
146                AUTHORIZATION,
147                client.management_client.access_token.as_ref().unwrap(),
148            )
149            .send()?;
150        let text = get_content_text(resp)?;
151        let resp: Tenant = serde_json::from_str(&text)
152            .with_context(|| format!("Unable to deserialise GetTenant. Body was: \"{}\"", text))?;
153        Ok(resp)
154    }
155
156    pub(crate) fn delete(client: &mut ObjectstoreClient, name: &str) -> Result<()> {
157        let request_url = format!("{}object/tenants/tenant/{}/delete", client.endpoint, name,);
158        let resp = client
159            .management_client
160            .http_client
161            .post(request_url)
162            .header(ACCEPT, "application/json")
163            .header(
164                AUTHORIZATION,
165                client.management_client.access_token.as_ref().unwrap(),
166            )
167            .header("X-EMC-Override", "true")
168            .send()?;
169        if !resp.status().is_success() {
170            bail!("Request failed: {}", resp.text()?);
171        }
172        Ok(())
173    }
174
175    pub(crate) fn list(client: &mut ObjectstoreClient, name_prefix: &str) -> Result<Vec<Tenant>> {
176        let request_url = format!("{}object/tenants", client.endpoint,);
177        let mut req = client
178            .management_client
179            .http_client
180            .get(request_url)
181            .header(ACCEPT, "application/json")
182            .header(
183                AUTHORIZATION,
184                client.management_client.access_token.as_ref().unwrap(),
185            );
186        if !name_prefix.is_empty() {
187            req = req.query(&[("name", name_prefix)]);
188        }
189        let response = req.send()?;
190        let text = get_content_text(response)?;
191        let mut resp: ListTenantsResponse = serde_json::from_str(&text).with_context(|| {
192            format!(
193                "Unable to deserialise ListTenantsResponse. Body was: \"{}\"",
194                text
195            )
196        })?;
197        let mut tenants: Vec<Tenant> = vec![];
198        tenants.extend(resp.tenant);
199        while let Some(marker) = resp.next_marker {
200            let request_url = format!("{}object/tenants?marker={}", client.endpoint, marker,);
201            let mut req = client
202                .management_client
203                .http_client
204                .get(request_url)
205                .header(ACCEPT, "application/json")
206                .header(
207                    AUTHORIZATION,
208                    client.management_client.access_token.as_ref().unwrap(),
209                );
210            if !name_prefix.is_empty() {
211                req = req.query(&[("name", name_prefix)]);
212            }
213            let response = req.send()?;
214            let text = get_content_text(response)?;
215            resp = serde_json::from_str(&text).with_context(|| {
216                format!(
217                    "Unable to deserialise ListTenantsResponse. Body was: \"{}\"",
218                    text
219                )
220            })?;
221            tenants.extend(resp.tenant);
222        }
223        Ok(tenants)
224    }
225}