openstack/compute/
keypairs.rs

1// Copyright 2018 Dmitry Tantsur <divius.inside@gmail.com>
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! Key pair management via Compute API.
16
17use async_trait::async_trait;
18use futures::stream::{Stream, TryStreamExt};
19
20use super::super::common::{KeyPairRef, Refresh, ResourceIterator, ResourceQuery};
21use super::super::session::Session;
22use super::super::utils::Query;
23use super::super::{Error, ErrorKind, Result};
24use super::{api, protocol};
25
26/// Structure representing a key pair.
27#[derive(Clone, Debug)]
28pub struct KeyPair {
29    session: Session,
30    inner: protocol::KeyPair,
31}
32
33/// A query to server list.
34#[derive(Clone, Debug)]
35pub struct KeyPairQuery {
36    session: Session,
37    query: Query,
38    can_paginate: bool,
39}
40
41/// A request to create a key pair.
42#[derive(Clone, Debug)]
43pub struct NewKeyPair {
44    session: Session,
45    inner: protocol::KeyPairCreate,
46}
47
48impl KeyPair {
49    /// Load a KeyPair object.
50    pub(crate) async fn new<Id: AsRef<str>>(session: Session, id: Id) -> Result<KeyPair> {
51        let inner = api::get_keypair(&session, id).await?;
52        Ok(KeyPair { session, inner })
53    }
54
55    /// Delete the key pair.
56    pub async fn delete(self) -> Result<()> {
57        api::delete_keypair(&self.session, &self.inner.name).await
58    }
59
60    transparent_property! {
61        #[doc = "Key pair fingerprint."]
62        fingerprint: ref String
63    }
64
65    transparent_property! {
66        #[doc = "Key pair type, if available."]
67        key_type: Option<protocol::KeyPairType>
68    }
69
70    transparent_property! {
71        #[doc = "Key pair name."]
72        name: ref String
73    }
74
75    transparent_property! {
76        #[doc = "Public key."]
77        public_key: ref String
78    }
79}
80
81#[async_trait]
82impl Refresh for KeyPair {
83    /// Refresh the keypair.
84    async fn refresh(&mut self) -> Result<()> {
85        self.inner = api::get_keypair(&self.session, &self.inner.name).await?;
86        Ok(())
87    }
88}
89
90impl KeyPairQuery {
91    pub(crate) fn new(session: Session) -> KeyPairQuery {
92        KeyPairQuery {
93            session,
94            query: Query::new(),
95            can_paginate: true,
96        }
97    }
98
99    /// Add marker to the request.
100    ///
101    /// Using this disables automatic pagination.
102    pub fn with_marker<T: Into<String>>(mut self, marker: T) -> Self {
103        self.can_paginate = false;
104        self.query.push_str("marker", marker);
105        self
106    }
107
108    /// Add limit to the request.
109    ///
110    /// Using this disables automatic pagination.
111    pub fn with_limit(mut self, limit: usize) -> Self {
112        self.can_paginate = false;
113        self.query.push("limit", limit);
114        self
115    }
116
117    /// Convert this query into a stream executing the request.
118    ///
119    /// Returns a `TryStream`, which is a stream with each `next`
120    /// call returning a `Result`.
121    ///
122    /// Note that no requests are done until you start iterating.
123    pub fn into_stream(self) -> impl Stream<Item = Result<KeyPair>> {
124        debug!("Fetching key pairs with {:?}", self.query);
125        ResourceIterator::new(self).into_stream()
126    }
127
128    /// Execute this request and return all results.
129    ///
130    /// A convenience shortcut for `self.into_stream().try_collect().await`.
131    pub async fn all(self) -> Result<Vec<KeyPair>> {
132        self.into_stream().try_collect().await
133    }
134
135    /// Return one and exactly one result.
136    ///
137    /// Fails with `ResourceNotFound` if the query produces no results and
138    /// with `TooManyItems` if the query produces more than one result.
139    pub async fn one(mut self) -> Result<KeyPair> {
140        debug!("Fetching one key pair with {:?}", self.query);
141        if self.can_paginate {
142            // We need only one result. We fetch maximum two to be able
143            // to check if the query yieled more than one result.
144            self.query.push("limit", 2);
145        }
146
147        ResourceIterator::new(self).one().await
148    }
149}
150
151impl NewKeyPair {
152    /// Start creating a key pair.
153    pub(crate) fn new(session: Session, name: String) -> NewKeyPair {
154        NewKeyPair {
155            session,
156            inner: protocol::KeyPairCreate::new(name),
157        }
158    }
159
160    /// Request creation of a key pair.
161    ///
162    /// This call fails immediately if no public_key is provided.
163    pub async fn create(self) -> Result<KeyPair> {
164        if self.inner.public_key.is_none() {
165            return Err(Error::new(
166                ErrorKind::InvalidInput,
167                "Public key contents is required",
168            ));
169        };
170
171        let keypair = api::create_keypair(&self.session, self.inner).await?;
172        Ok(KeyPair {
173            session: self.session,
174            inner: keypair,
175        })
176    }
177
178    /// Create a key pair, generating its public key.
179    ///
180    /// Returns a new key pair and its private key.
181    pub async fn generate(mut self) -> Result<(KeyPair, String)> {
182        self.inner.public_key = None;
183
184        let mut keypair = api::create_keypair(&self.session, self.inner).await?;
185        if let Some(private_key) = keypair.private_key.take() {
186            let result = KeyPair {
187                session: self.session,
188                inner: keypair,
189            };
190
191            Ok((result, private_key))
192        } else {
193            Err(Error::new(
194                ErrorKind::InvalidResponse,
195                "Missing private key in the response",
196            ))
197        }
198    }
199
200    creation_inner_field! {
201        #[doc = "Set type of the key pair."]
202        set_key_type, with_key_type -> key_type: optional protocol::KeyPairType
203    }
204
205    creation_inner_field! {
206        #[doc = "Set name of the key pair."]
207        set_name, with_name -> name: String
208    }
209
210    creation_inner_field! {
211        #[doc = "Set name of the key pair."]
212        set_public_key, with_public_key -> public_key: optional String
213    }
214}
215
216#[async_trait]
217impl ResourceQuery for KeyPairQuery {
218    type Item = KeyPair;
219
220    const DEFAULT_LIMIT: usize = 50;
221
222    async fn can_paginate(&self) -> Result<bool> {
223        if self.can_paginate {
224            api::supports_keypair_pagination(&self.session).await
225        } else {
226            Ok(false)
227        }
228    }
229
230    fn extract_marker(&self, resource: &Self::Item) -> String {
231        resource.name().clone()
232    }
233
234    async fn fetch_chunk(
235        &self,
236        limit: Option<usize>,
237        marker: Option<String>,
238    ) -> Result<Vec<Self::Item>> {
239        let query = self.query.with_marker_and_limit(limit, marker);
240        Ok(api::list_keypairs(&self.session, &query)
241            .await?
242            .into_iter()
243            .map(|item| KeyPair {
244                session: self.session.clone(),
245                inner: item,
246            })
247            .collect())
248    }
249}
250
251impl From<KeyPair> for KeyPairRef {
252    fn from(value: KeyPair) -> KeyPairRef {
253        KeyPairRef::new_verified(value.inner.name)
254    }
255}
256
257#[cfg(feature = "compute")]
258impl KeyPairRef {
259    /// Verify this reference and convert to an ID, if possible.
260    pub(crate) async fn into_verified(self, session: &Session) -> Result<KeyPairRef> {
261        Ok(if self.verified {
262            self
263        } else {
264            KeyPairRef::new_verified(api::get_keypair(session, &self.value).await?.name)
265        })
266    }
267}