Skip to content

Commit

Permalink
Merge pull request #144 from milliams/server_actions
Browse files Browse the repository at this point in the history
Implement server actions via an enum
  • Loading branch information
dtantsur committed Oct 30, 2023
2 parents a3b6412 + 6c82cd3 commit 20cd638
Show file tree
Hide file tree
Showing 4 changed files with 68 additions and 39 deletions.
36 changes: 5 additions & 31 deletions src/compute/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -314,48 +314,22 @@ pub async fn list_servers_detail<Q: Serialize + Sync + Debug>(
Ok(root.servers)
}

/// Run an action while providing some arguments.
pub async fn server_action_with_args<S1, S2, Q>(
session: &Session,
id: S1,
action: S2,
args: Q,
) -> Result<()>
/// Run an action on a server.
pub async fn server_action_with_args<S1, Q>(session: &Session, id: S1, action: Q) -> Result<()>
where
S1: AsRef<str>,
S2: AsRef<str>,
Q: Serialize + Send + Debug,
{
trace!(
"Running {} on server {} with args {:?}",
action.as_ref(),
id.as_ref(),
args
);
let mut body = HashMap::new();
let _ = body.insert(action.as_ref(), args);
trace!("Running {:?} on server {}", action, id.as_ref(),);
let _ = session
.post(COMPUTE, &["servers", id.as_ref(), "action"])
.json(&body)
.json(&action)
.send()
.await?;
debug!(
"Successfully ran {} on server {}",
action.as_ref(),
id.as_ref()
);
debug!("Successfully ran {:?} on server {}", action, id.as_ref());
Ok(())
}

/// Run an action on the server.
pub async fn server_simple_action<S1, S2>(session: &Session, id: S1, action: S2) -> Result<()>
where
S1: AsRef<str>,
S2: AsRef<str>,
{
server_action_with_args(session, id, action, serde_json::Value::Null).await
}

/// Whether key pair pagination is supported.
#[inline]
pub async fn supports_keypair_pagination(session: &Session) -> Result<bool> {
Expand Down
4 changes: 2 additions & 2 deletions src/compute/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,6 @@ pub use self::protocol::{
ServerSortKey, ServerStatus,
};
pub use self::servers::{
DetailedServerQuery, NewServer, Server, ServerCreationWaiter, ServerNIC, ServerQuery,
ServerStatusWaiter, ServerSummary,
DetailedServerQuery, NewServer, Server, ServerAction, ServerCreationWaiter, ServerNIC,
ServerQuery, ServerStatusWaiter, ServerSummary,
};
60 changes: 54 additions & 6 deletions src/compute/servers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ use async_trait::async_trait;
use chrono::{DateTime, FixedOffset};
use futures::stream::{Stream, TryStreamExt};
use osauth::common::IdAndName;
use serde::Serialize;

use super::super::common::{
FlavorRef, ImageRef, KeyPairRef, NetworkRef, PortRef, ProjectRef, Refresh, ResourceIterator,
Expand All @@ -30,7 +31,7 @@ use super::super::common::{
#[cfg(feature = "image")]
use super::super::image::Image;
use super::super::session::Session;
use super::super::utils::Query;
use super::super::utils::{unit_to_null, Query};
use super::super::waiter::{DeletionWaiter, Waiter};
use super::super::{Error, ErrorKind, Result, Sort};
use super::{api, protocol, BlockDevice, KeyPair};
Expand Down Expand Up @@ -306,9 +307,7 @@ impl Server {
&mut self,
reboot_type: protocol::RebootType,
) -> Result<ServerStatusWaiter<'_>> {
let mut args = HashMap::new();
let _ = args.insert("type", reboot_type);
api::server_action_with_args(&self.session, &self.inner.id, "reboot", args).await?;
self.action(ServerAction::Reboot { reboot_type }).await?;
Ok(ServerStatusWaiter {
server: self,
target: protocol::ServerStatus::Active,
Expand All @@ -317,7 +316,7 @@ impl Server {

/// Start the server, optionally wait for it to be active.
pub async fn start(&mut self) -> Result<ServerStatusWaiter<'_>> {
api::server_simple_action(&self.session, &self.inner.id, "os-start").await?;
self.action(ServerAction::Start).await?;
Ok(ServerStatusWaiter {
server: self,
target: protocol::ServerStatus::Active,
Expand All @@ -326,12 +325,37 @@ impl Server {

/// Stop the server, optionally wait for it to be powered off.
pub async fn stop(&mut self) -> Result<ServerStatusWaiter<'_>> {
api::server_simple_action(&self.session, &self.inner.id, "os-stop").await?;
self.action(ServerAction::Stop).await?;
Ok(ServerStatusWaiter {
server: self,
target: protocol::ServerStatus::ShutOff,
})
}

/// Run an action on the server.
pub async fn action(&mut self, action: ServerAction) -> Result<()> {
api::server_action_with_args(&self.session, &self.inner.id, action).await
}
}

/// An action to perform on a server.
#[derive(Clone, Debug, Serialize)]
#[non_exhaustive]
#[allow(missing_copy_implementations)]
pub enum ServerAction {
/// Reboots a server.
#[serde(rename = "reboot")]
Reboot {
/// The type of the reboot action.
#[serde(rename = "type")]
reboot_type: protocol::RebootType,
},
/// Starts a stopped server.
#[serde(rename = "os-start", serialize_with = "unit_to_null")]
Start,
/// Stops a running server.
#[serde(rename = "os-stop", serialize_with = "unit_to_null")]
Stop,
}

#[async_trait]
Expand Down Expand Up @@ -932,3 +956,27 @@ impl ServerCreationWaiter {
&self.server
}
}

#[cfg(test)]
mod test {
use super::*;

#[test]
fn test_action_json() {
assert_eq!(
serde_json::to_string(&ServerAction::Start).unwrap(),
"{\"os-start\":null}"
);
assert_eq!(
serde_json::to_string(&ServerAction::Stop).unwrap(),
"{\"os-stop\":null}"
);
assert_eq!(
serde_json::to_string(&ServerAction::Reboot {
reboot_type: protocol::RebootType::Hard
})
.unwrap(),
"{\"reboot\":{\"type\":\"HARD\"}}"
);
}
}
7 changes: 7 additions & 0 deletions src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,13 @@ where
}
}

/// Serialize an enum unit variant into a None
/// This is used to turn [ServerAction::Start] into
/// `"os-start": null` instead of just `"os-start"`
pub fn unit_to_null<S: Serializer>(s: S) -> std::result::Result<S::Ok, S::Error> {
s.serialize_none()
}

pub mod url {
//! Handy primitives for working with URLs.

Expand Down

0 comments on commit 20cd638

Please sign in to comment.