Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Clean up server actions API #166

Merged
merged 2 commits into from
Oct 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 22 additions & 16 deletions src/compute/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ use std::fmt::Debug;

use osauth::common::{IdAndName, Ref};
use osauth::services::COMPUTE;
use osauth::{Error, ErrorKind};
use osauth::ErrorKind;
use serde::de::DeserializeOwned;
use serde::Serialize;

use super::super::common::ApiVersion;
Expand Down Expand Up @@ -315,31 +316,36 @@ pub async fn list_servers_detail<Q: Serialize + Sync + Debug>(
}

/// Run an action on a server.
pub async fn server_action_with_args<S1, Q>(
session: &Session,
id: S1,
action: Q,
) -> Result<Option<serde_json::Value>>
pub async fn server_action<S1, Q>(session: &Session, id: S1, action: Q) -> Result<()>
where
S1: AsRef<str>,
Q: Serialize + Send + Debug,
{
trace!("Running {:?} on server {}", action, id.as_ref(),);
let response = session
let _ = session
.post(COMPUTE, &["servers", id.as_ref(), "action"])
.json(&action)
.send()
.await?;
debug!("Successfully ran {:?} on server {}", action, id.as_ref());
Ok(match response.content_length() {
Some(0) => None,
_ => Some(
response
.json::<serde_json::Value>()
.await
.map_err(|e| Error::new(ErrorKind::InvalidResponse, e.to_string()))?,
),
})
Ok(())
}

/// Run an action on a server and return result.
pub async fn server_action_with_result<S1, Q, R>(session: &Session, id: S1, action: Q) -> Result<R>
where
S1: AsRef<str>,
Q: Serialize + Send + Debug,
R: DeserializeOwned + Send,
{
trace!("Running {:?} on server {}", action, id.as_ref(),);
let response = session
.post(COMPUTE, &["servers", id.as_ref(), "action"])
.json(&action)
.fetch()
.await?;
debug!("Successfully ran {:?} on server {}", action, id.as_ref());
Ok(response)
}

/// Whether key pair pagination is supported.
Expand Down
6 changes: 6 additions & 0 deletions src/compute/protocol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -395,3 +395,9 @@ impl Default for ServerPowerState {
fn default_flavor_is_public() -> bool {
true
}

#[derive(Clone, Debug, Deserialize)]
pub struct GetConsoleOutput {
/// Output as a string.
pub output: String,
}
67 changes: 39 additions & 28 deletions src/compute/servers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,11 @@ impl Server {
updated_at: DateTime<FixedOffset>
}

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

/// Delete the server.
pub async fn delete(self) -> Result<DeletionWaiter<Server>> {
api::delete_server(&self.session, &self.inner.id).await?;
Expand All @@ -302,6 +307,17 @@ impl Server {
))
}

/// Get the console output as a string.
///
/// Length is the number of lines to fetch from the end of console log.
/// All lines will be returned if this is not specified.
pub async fn get_console_output(&self, length: Option<u64>) -> Result<String> {
let action = ServerAction::GetConsoleOutput { length };
let result: protocol::GetConsoleOutput =
api::server_action_with_result(&self.session, &self.inner.id, action).await?;
Ok(result.output)
}

/// Reboot the server.
pub async fn reboot(
&mut self,
Expand Down Expand Up @@ -331,17 +347,11 @@ impl Server {
target: protocol::ServerStatus::ShutOff,
})
}

/// Run an action on the server.
pub async fn action(&mut self, action: ServerAction) -> Result<Option<serde_json::Value>> {
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 {
/// Adds a security group to a server.
#[serde(rename = "addSecurityGroup")]
Expand Down Expand Up @@ -380,6 +390,17 @@ pub enum ServerAction {
#[serde(skip_serializing_if = "Option::is_none")]
metadata: Option<HashMap<String, String>>,
},
/// Force-deletes a server before deferred cleanup.
#[serde(rename = "forceDelete", serialize_with = "unit_to_null")]
ForceDelete,
/// Shows console output for a server.
#[serde(rename = "os-getConsoleOutput")]
#[doc(hidden)]
GetConsoleOutput {
/// The number of lines to fetch from the end of console log. All lines will be returned if this is not specified.
#[serde(skip_serializing_if = "Option::is_none")]
length: Option<u64>,
},
/// Pauses a server. Changes its status to PAUSED.
#[serde(rename = "pause", serialize_with = "unit_to_null")]
Pause,
Expand Down Expand Up @@ -416,12 +437,21 @@ pub enum ServerAction {
#[serde(rename = "OS-DCF:diskConfig")]
disk_config: String,
},
/// Restores a previously soft-deleted server instance.
#[serde(rename = "restore", serialize_with = "unit_to_null")]
Restore,
/// Resumes a suspended server and changes its status to ACTIVE.
#[serde(rename = "resume", serialize_with = "unit_to_null")]
Resume,
/// Cancels and reverts a pending resize action for a server.
#[serde(rename = "revertResize", serialize_with = "unit_to_null")]
RevertResize,
/// Shelves a server.
#[serde(rename = "shelve", serialize_with = "unit_to_null")]
Shelve,
/// Shelf-offloads, or removes, a shelved server.
#[serde(rename = "shelveOffload", serialize_with = "unit_to_null")]
ShelveOffload,
/// Starts a stopped server.
#[serde(rename = "os-start", serialize_with = "unit_to_null")]
Start,
Expand All @@ -431,6 +461,9 @@ pub enum ServerAction {
/// Suspends a server and changes its status to SUSPENDED.
#[serde(rename = "suspend", serialize_with = "unit_to_null")]
Suspend,
/// Trigger a crash dump in a server.
#[serde(rename = "trigger_crash_dump", serialize_with = "unit_to_null")]
TriggerCrashDump,
/// Unlocks a locked server.
#[serde(rename = "unlock", serialize_with = "unit_to_null")]
Unlock,
Expand All @@ -440,28 +473,6 @@ pub enum ServerAction {
/// Unrescues a server. Changes status to ACTIVE.
#[serde(rename = "unrescue", serialize_with = "unit_to_null")]
Unrescue,
/// Force-deletes a server before deferred cleanup.
#[serde(rename = "forceDelete", serialize_with = "unit_to_null")]
ForceDelete,
/// Restores a previously soft-deleted server instance.
#[serde(rename = "restore", serialize_with = "unit_to_null")]
Restore,
/// Shows console output for a server.
#[serde(rename = "os-getConsoleOutput")]
OsGetConsoleOutput {
/// The number of lines to fetch from the end of console log. All lines will be returned if this is not specified.
#[serde(skip_serializing_if = "Option::is_none")]
length: Option<u64>,
},
/// Shelves a server.
#[serde(rename = "shelve", serialize_with = "unit_to_null")]
Shelve,
/// Shelf-offloads, or removes, a shelved server.
#[serde(rename = "shelveOffload", serialize_with = "unit_to_null")]
ShelveOffload,
/// Trigger a crash dump in a server.
#[serde(rename = "trigger_crash_dump", serialize_with = "unit_to_null")]
TriggerCrashDump,
}

#[async_trait]
Expand Down
6 changes: 6 additions & 0 deletions tests/integration-create-delete-server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,12 @@ async fn test_basic_server_ops() {
.await
.expect("Failed to delete floating IP");

let console = server
.get_console_output(Some(10))
.await
.expect("Failed to get console output");
assert!(console.len() > 0);

server
.delete()
.await
Expand Down
Loading