openstack_sdk/api/compute/v2/server/volume_attachment/
set_20.rs

1// Licensed under the Apache License, Version 2.0 (the "License");
2// you may not use this file except in compliance with the License.
3// You may obtain a copy of the License at
4//
5//     http://www.apache.org/licenses/LICENSE-2.0
6//
7// Unless required by applicable law or agreed to in writing, software
8// distributed under the License is distributed on an "AS IS" BASIS,
9// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10// See the License for the specific language governing permissions and
11// limitations under the License.
12//
13// SPDX-License-Identifier: Apache-2.0
14//
15// WARNING: This file is automatically generated from OpenAPI schema using
16// `openstack-codegenerator`.
17
18//! Update a volume attachment.
19//!
20//! Policy default role is ‘rule:system_admin_or_owner’, its scope is \[system,
21//! project\], which allow project members or system admins to change the
22//! fields of an attached volume of a server. Policy defaults enable only users
23//! with the administrative role to change `volumeId` via this operation. Cloud
24//! providers can change these permissions through the `policy.json` file.
25//!
26//! Updating, or what is commonly referred to as “swapping”, volume attachments
27//! with volumes that have more than one read/write attachment, is not
28//! supported.
29//!
30//! Normal response codes: 202
31//!
32//! Error response codes: badRequest(400), unauthorized(401), forbidden(403),
33//! itemNotFound(404), conflict(409)
34//!
35use derive_builder::Builder;
36use http::{HeaderMap, HeaderName, HeaderValue};
37
38use crate::api::rest_endpoint_prelude::*;
39
40use serde::Deserialize;
41use serde::Serialize;
42use std::borrow::Cow;
43
44/// A dictionary representation of a volume attachment containing the field
45/// `volumeId` which is the UUID of the replacement volume, and other fields to
46/// update in the attachment.
47#[derive(Builder, Debug, Deserialize, Clone, Serialize)]
48#[builder(setter(strip_option))]
49pub struct VolumeAttachment<'a> {
50    /// The UUID of the volume to attach instead of the attached volume.
51    #[serde(rename = "volumeId")]
52    #[builder(setter(into))]
53    pub(crate) volume_id: Cow<'a, str>,
54}
55
56#[derive(Builder, Debug, Clone)]
57#[builder(setter(strip_option))]
58pub struct Request<'a> {
59    /// A dictionary representation of a volume attachment containing the field
60    /// `volumeId` which is the UUID of the replacement volume, and other
61    /// fields to update in the attachment.
62    #[builder(setter(into))]
63    pub(crate) volume_attachment: VolumeAttachment<'a>,
64
65    /// id parameter for /v2.1/servers/{server_id}/os-volume_attachments/{id}
66    /// API
67    #[builder(default, setter(into))]
68    id: Cow<'a, str>,
69
70    /// server_id parameter for
71    /// /v2.1/servers/{server_id}/os-volume_attachments/{id} API
72    #[builder(default, setter(into))]
73    server_id: Cow<'a, str>,
74
75    #[builder(setter(name = "_headers"), default, private)]
76    _headers: Option<HeaderMap>,
77}
78impl<'a> Request<'a> {
79    /// Create a builder for the endpoint.
80    pub fn builder() -> RequestBuilder<'a> {
81        RequestBuilder::default()
82    }
83}
84
85impl RequestBuilder<'_> {
86    /// Add a single header to the Volume_Attachment.
87    pub fn header(&mut self, header_name: &'static str, header_value: &'static str) -> &mut Self
88where {
89        self._headers
90            .get_or_insert(None)
91            .get_or_insert_with(HeaderMap::new)
92            .insert(header_name, HeaderValue::from_static(header_value));
93        self
94    }
95
96    /// Add multiple headers.
97    pub fn headers<I, T>(&mut self, iter: I) -> &mut Self
98    where
99        I: Iterator<Item = T>,
100        T: Into<(Option<HeaderName>, HeaderValue)>,
101    {
102        self._headers
103            .get_or_insert(None)
104            .get_or_insert_with(HeaderMap::new)
105            .extend(iter.map(Into::into));
106        self
107    }
108}
109
110impl RestEndpoint for Request<'_> {
111    fn method(&self) -> http::Method {
112        http::Method::PUT
113    }
114
115    fn endpoint(&self) -> Cow<'static, str> {
116        format!(
117            "servers/{server_id}/os-volume_attachments/{id}",
118            server_id = self.server_id.as_ref(),
119            id = self.id.as_ref(),
120        )
121        .into()
122    }
123
124    fn parameters(&self) -> QueryParams {
125        QueryParams::default()
126    }
127
128    fn body(&self) -> Result<Option<(&'static str, Vec<u8>)>, BodyError> {
129        let mut params = JsonBodyParams::default();
130
131        params.push(
132            "volumeAttachment",
133            serde_json::to_value(&self.volume_attachment)?,
134        );
135
136        params.into_body()
137    }
138
139    fn service_type(&self) -> ServiceType {
140        ServiceType::Compute
141    }
142
143    fn response_key(&self) -> Option<Cow<'static, str>> {
144        Some("volumeAttachment".into())
145    }
146
147    /// Returns headers to be set into the request
148    fn request_headers(&self) -> Option<&HeaderMap> {
149        self._headers.as_ref()
150    }
151
152    /// Returns required API version
153    fn api_version(&self) -> Option<ApiVersion> {
154        Some(ApiVersion::new(2, 0))
155    }
156}
157
158#[cfg(test)]
159mod tests {
160    use super::*;
161    #[cfg(feature = "sync")]
162    use crate::api::Query;
163    use crate::test::client::FakeOpenStackClient;
164    use crate::types::ServiceType;
165    use http::{HeaderName, HeaderValue};
166    use httpmock::MockServer;
167    use serde_json::json;
168
169    #[test]
170    fn test_service_type() {
171        assert_eq!(
172            Request::builder()
173                .volume_attachment(
174                    VolumeAttachmentBuilder::default()
175                        .volume_id("foo")
176                        .build()
177                        .unwrap()
178                )
179                .build()
180                .unwrap()
181                .service_type(),
182            ServiceType::Compute
183        );
184    }
185
186    #[test]
187    fn test_response_key() {
188        assert_eq!(
189            Request::builder()
190                .volume_attachment(
191                    VolumeAttachmentBuilder::default()
192                        .volume_id("foo")
193                        .build()
194                        .unwrap()
195                )
196                .build()
197                .unwrap()
198                .response_key()
199                .unwrap(),
200            "volumeAttachment"
201        );
202    }
203
204    #[cfg(feature = "sync")]
205    #[test]
206    fn endpoint() {
207        let server = MockServer::start();
208        let client = FakeOpenStackClient::new(server.base_url());
209        let mock = server.mock(|when, then| {
210            when.method(httpmock::Method::PUT).path(format!(
211                "/servers/{server_id}/os-volume_attachments/{id}",
212                server_id = "server_id",
213                id = "id",
214            ));
215
216            then.status(200)
217                .header("content-type", "application/json")
218                .json_body(json!({ "volumeAttachment": {} }));
219        });
220
221        let endpoint = Request::builder()
222            .server_id("server_id")
223            .id("id")
224            .volume_attachment(
225                VolumeAttachmentBuilder::default()
226                    .volume_id("foo")
227                    .build()
228                    .unwrap(),
229            )
230            .build()
231            .unwrap();
232        let _: serde_json::Value = endpoint.query(&client).unwrap();
233        mock.assert();
234    }
235
236    #[cfg(feature = "sync")]
237    #[test]
238    fn endpoint_headers() {
239        let server = MockServer::start();
240        let client = FakeOpenStackClient::new(server.base_url());
241        let mock = server.mock(|when, then| {
242            when.method(httpmock::Method::PUT)
243                .path(format!(
244                    "/servers/{server_id}/os-volume_attachments/{id}",
245                    server_id = "server_id",
246                    id = "id",
247                ))
248                .header("foo", "bar")
249                .header("not_foo", "not_bar");
250            then.status(200)
251                .header("content-type", "application/json")
252                .json_body(json!({ "volumeAttachment": {} }));
253        });
254
255        let endpoint = Request::builder()
256            .server_id("server_id")
257            .id("id")
258            .volume_attachment(
259                VolumeAttachmentBuilder::default()
260                    .volume_id("foo")
261                    .build()
262                    .unwrap(),
263            )
264            .headers(
265                [(
266                    Some(HeaderName::from_static("foo")),
267                    HeaderValue::from_static("bar"),
268                )]
269                .into_iter(),
270            )
271            .header("not_foo", "not_bar")
272            .build()
273            .unwrap();
274        let _: serde_json::Value = endpoint.query(&client).unwrap();
275        mock.assert();
276    }
277}