mod api_error;
mod builder;
mod data;
mod scaleway_error;
pub use api_error::ScalewayApiError;
use builder::list_marketplace_image_versions_builder::ScalewayListMarketplaceImageVersionsBuilder;
use data::availability::ScalewayAvailabilityRoot;
use data::instance::ScalewayInstanceRoot;
use data::server_type::ScalewayServerTypeRoot;
use serde::Serialize;
use serde_json::json;
use std::collections::HashMap;
pub use builder::{
create_instance_builder::ScalewayCreateInstanceBuilder,
list_instance_builder::ScalewayListInstanceBuilder,
list_instance_images_builder::ScalewayListInstanceImagesBuilder,
list_marketplace_images_builder::ScalewayListMarketplaceImagesBuilder,
list_marketplace_local_images_builder::LocalImageListType,
list_marketplace_local_images_builder::ScalewayListMarketplaceLocalImagesBuilder,
};
pub use data::image::{
ScalewayImage, ScalewayImageBootscript, ScalewayImageExtraVolume,
ScalewayImageExtraVolumeServer, ScalewayImageExtraVolumes, ScalewayImageRootVolume,
};
pub use data::instance::{
ScalewayInstance, ScalewayInstanceLocation, ScalewayIpv6, ScalewayMaintenance,
ScalewayPlacementGroup, ScalewayPrivateNic, ScalewayPublicIP, ScalewaySecurityGroup,
};
pub use data::marketplace_image::ScalewayMarketplaceImage;
pub use data::server_type::ServerType;
pub use data::user_data::ScalewayUserData;
pub use data::user_data::ScalewayUserDataKeyList;
pub use scaleway_error::ScalewayError;
#[derive(Clone)]
pub struct ScalewayApi {
secret_key: String,
}
impl<'a> ScalewayApi {
pub fn new<S>(secret_key: S) -> ScalewayApi
where
S: Into<String>,
{
ScalewayApi {
secret_key: secret_key.into(),
}
}
async fn get_async(
&self,
url: &str,
query: Vec<(&'static str, String)>,
) -> Result<reqwest::Response, ScalewayError> {
let client = reqwest::Client::new();
let resp = client
.get(url)
.header("X-Auth-Token", &self.secret_key)
.query(&query)
.send()
.await
.map_err(|e| ScalewayError::Reqwest(e))?;
let status = resp.status();
if status.is_client_error() {
let result: ScalewayApiError = resp.json().await?;
Err(ScalewayError::Api(result))
} else {
Ok(resp.error_for_status()?)
}
}
#[cfg(feature = "blocking")]
fn get(
&self,
url: &str,
query: Vec<(&'static str, String)>,
) -> Result<reqwest::blocking::Response, ScalewayError> {
let client = reqwest::blocking::Client::new();
let resp = client
.get(url)
.header("X-Auth-Token", &self.secret_key)
.query(&query)
.send()?;
let status = resp.status();
if status.is_client_error() {
let result: ScalewayApiError = resp.json()?;
Err(ScalewayError::Api(result))
} else {
Ok(resp.error_for_status()?)
}
}
async fn post_async<T>(&self, url: &str, json: T) -> Result<reqwest::Response, ScalewayError>
where
T: Serialize + Sized,
{
let client = reqwest::Client::new();
let resp = client
.post(url)
.header("X-Auth-Token", &self.secret_key)
.json(&json)
.send()
.await?;
let status = resp.status();
if status.is_client_error() {
let result: ScalewayApiError = resp.json().await?;
Err(ScalewayError::Api(result))
} else {
Ok(resp.error_for_status()?)
}
}
#[cfg(feature = "blocking")]
fn post<T>(&self, url: &str, json: T) -> Result<reqwest::blocking::Response, ScalewayError>
where
T: Serialize + Sized,
{
let client = reqwest::blocking::Client::new();
let resp = client
.post(url)
.header("X-Auth-Token", &self.secret_key)
.json(&json)
.send()?;
let status = resp.status();
if status.is_client_error() {
let result: ScalewayApiError = resp.json()?;
Err(ScalewayError::Api(result))
} else {
Ok(resp.error_for_status()?)
}
}
async fn patch_async(
&self,
url: &str,
content: &str,
) -> Result<reqwest::Response, ScalewayError> {
let client = reqwest::Client::new();
let resp = client
.patch(url)
.header("X-Auth-Token", &self.secret_key)
.body(content.to_string())
.send()
.await?;
let status = resp.status();
if status.is_client_error() {
let result: ScalewayApiError = resp.json().await?;
Err(ScalewayError::Api(result))
} else {
Ok(resp.error_for_status()?)
}
}
#[cfg(feature = "blocking")]
fn patch(
&self,
url: &str,
content: &str,
) -> Result<reqwest::blocking::Response, ScalewayError> {
let client = reqwest::blocking::Client::new();
let resp = client
.patch(url)
.header("X-Auth-Token", &self.secret_key)
.body(content.to_string())
.send()?;
let status = resp.status();
if status.is_client_error() {
let result: ScalewayApiError = resp.json()?;
Err(ScalewayError::Api(result))
} else {
Ok(resp.error_for_status()?)
}
}
async fn delete_async(&self, url: &str) -> Result<reqwest::Response, ScalewayError> {
let client = reqwest::Client::new();
let resp = client
.delete(url)
.header("X-Auth-Token", &self.secret_key)
.send()
.await?;
let status = resp.status();
if status.is_client_error() {
let result: ScalewayApiError = resp.json().await?;
Err(ScalewayError::Api(result))
} else {
Ok(resp.error_for_status()?)
}
}
#[cfg(feature = "blocking")]
fn delete(&self, url: &str) -> Result<reqwest::blocking::Response, ScalewayError> {
let client = reqwest::blocking::Client::new();
let resp = client
.delete(url)
.header("X-Auth-Token", &self.secret_key)
.send()?;
let status = resp.status();
if status.is_client_error() {
let result: ScalewayApiError = resp.json()?;
Err(ScalewayError::Api(result))
} else {
Ok(resp.error_for_status()?)
}
}
pub fn az_list() -> Vec<&'static str> {
vec![
"fr-par-1", "fr-par-2", "fr-par-3", "nl-ams-1", "nl-ams-2", "pl-waw-1", "pl-waw-2",
]
}
#[cfg(feature = "blocking")]
pub fn get_server_types(&self, zone: &str) -> Result<Vec<ServerType>, ScalewayError> {
let types: Vec<ServerType> = self
.get(
&format!(
"https://api.scaleway.com/instance/v1/zones/{zone}/products/servers",
zone = zone
),
vec![],
)?
.json::<ScalewayServerTypeRoot>()?
.servers
.servers
.into_iter()
.map(|(id, item)| ServerType {
id,
location: zone.to_string(),
alt_names: item.alt_names,
arch: item.arch,
ncpus: item.ncpus,
ram: item.ram,
gpu: item.gpu,
baremetal: item.baremetal,
monthly_price: item.monthly_price,
hourly_price: item.hourly_price,
network: item.network,
})
.collect();
Ok(types)
}
pub async fn get_server_types_async(
&self,
zone: &str,
) -> Result<Vec<ServerType>, ScalewayError> {
let types: Vec<ServerType> = self
.get_async(
&format!(
"https://api.scaleway.com/instance/v1/zones/{zone}/products/servers",
zone = zone
),
vec![],
)
.await?
.json::<ScalewayServerTypeRoot>()
.await?
.servers
.servers
.into_iter()
.map(|(id, item)| ServerType {
id,
location: zone.to_string(),
alt_names: item.alt_names,
arch: item.arch,
ncpus: item.ncpus,
ram: item.ram,
gpu: item.gpu,
baremetal: item.baremetal,
monthly_price: item.monthly_price,
hourly_price: item.hourly_price,
network: item.network,
})
.collect();
Ok(types)
}
pub fn list_images(&self, zone: &str) -> ScalewayListInstanceImagesBuilder {
ScalewayListInstanceImagesBuilder::new(self.clone(), zone)
}
pub fn list_instances(&self, zone: &str) -> ScalewayListInstanceBuilder {
ScalewayListInstanceBuilder::new(self.clone(), zone)
}
pub fn list_marketplace_instances(&self) -> ScalewayListMarketplaceImagesBuilder {
ScalewayListMarketplaceImagesBuilder::new(self.clone())
}
pub fn list_marketplace_instance_versions(
&self,
image_id: &str,
) -> ScalewayListMarketplaceImageVersionsBuilder {
ScalewayListMarketplaceImageVersionsBuilder::new(self.clone(), image_id)
}
pub fn list_marketplace_local_images(
&self,
list_type: LocalImageListType,
) -> ScalewayListMarketplaceLocalImagesBuilder {
ScalewayListMarketplaceLocalImagesBuilder::new(self.clone(), list_type)
}
pub fn create_instance(
&self,
zone: &str,
name: &str,
commercial_type: &str,
) -> ScalewayCreateInstanceBuilder {
ScalewayCreateInstanceBuilder::new(self.clone(), zone, name, commercial_type)
}
#[cfg(feature = "blocking")]
pub fn get_instance(
&self,
zone: &str,
server_id: &str,
) -> Result<ScalewayInstance, ScalewayError> {
Ok(self
.get(
&format!(
"https://api.scaleway.com/instance/v1/zones/{zone}/servers/{server_id}",
zone = zone,
server_id = server_id
),
vec![],
)?
.json::<ScalewayInstanceRoot>()?
.server)
}
pub async fn get_instance_async(
&self,
zone: &str,
server_id: &str,
) -> Result<ScalewayInstance, ScalewayError> {
Ok(self
.get_async(
&format!(
"https://api.scaleway.com/instance/v1/zones/{zone}/servers/{server_id}",
zone = zone,
server_id = server_id
),
vec![],
)
.await?
.json::<ScalewayInstanceRoot>()
.await?
.server)
}
#[cfg(feature = "blocking")]
pub fn delete_instance(&self, zone: &str, server_id: &str) -> Result<(), ScalewayError> {
self.delete(&format!(
"https://api.scaleway.com/instance/v1/zones/{zone}/servers/{server_id}",
zone = zone,
server_id = server_id
))?
.error_for_status()?;
Ok(())
}
pub async fn delete_instance_async(
&self,
zone: &str,
server_id: &str,
) -> Result<(), ScalewayError> {
self.delete_async(&format!(
"https://api.scaleway.com/instance/v1/zones/{zone}/servers/{server_id}",
zone = zone,
server_id = server_id
))
.await?
.error_for_status()?;
Ok(())
}
#[cfg(feature = "blocking")]
pub fn perform_instance_action(
&self,
zone: &str,
server_id: &str,
action: &str,
) -> Result<(), ScalewayError> {
self.post(
&format!(
"https://api.scaleway.com/instance/v1/zones/{zone}/servers/{server_id}/action",
zone = zone,
server_id = server_id
),
json!({"action": action}),
)?
.error_for_status()?;
Ok(())
}
pub async fn perform_instance_action_async(
&self,
zone: &str,
server_id: &str,
action: &str,
) -> Result<(), ScalewayError> {
self.post_async(
&format!(
"https://api.scaleway.com/instance/v1/zones/{zone}/servers/{server_id}/action",
zone = zone,
server_id = server_id
),
json!({"action": action}),
)
.await?
.error_for_status()?;
Ok(())
}
#[cfg(feature = "blocking")]
pub fn list_availability(&self,zone: &str) -> Result<HashMap<String, bool>, ScalewayError> {
let servers = self
.get(
&format!(
"https://api.scaleway.com/instance/v1/zones/{zone}/products/servers/availability",
zone = zone
),
vec![],
)?
.json::<ScalewayAvailabilityRoot>()?
.servers
.servers
.into_iter()
.map(|(id, available)| (id, available.availability == "available"))
.collect();
Ok(servers)
}
pub async fn list_availability_async(
&self,
zone: &str,
) -> Result<HashMap<String, bool>, ScalewayError> {
let servers = self
.get_async(
&format!(
"https://api.scaleway.com/instance/v1/zones/{zone}/products/servers/availability",
zone = zone
),
vec![],
)
.await?
.json::<ScalewayAvailabilityRoot>()
.await?
.servers
.servers
.into_iter()
.map(|(id, available)| (id, available.availability == "available"))
.collect();
Ok(servers)
}
#[cfg(feature = "blocking")]
pub fn list_userdata_keys(&self, zone: &str, machine_id: &str) -> Result<Vec<String>, ScalewayError> {
let user_data = self
.get(
&format!(
"https://api.scaleway.com/instance/v1/zones/{zone}/servers/{server_id}/user_data",
zone = zone,
server_id = machine_id,
),
vec![],
)?
.json::<ScalewayUserDataKeyList>()?
.user_data;
Ok(user_data)
}
pub async fn list_userdata_keys_async(
&self,
zone: &str,
machine_id: &str,
) -> Result<Vec<String>, ScalewayError> {
let user_data = self
.get_async(
&format!(
"https://api.scaleway.com/instance/v1/zones/{zone}/servers/{server_id}/user_data",
zone = zone,
server_id = machine_id,
),
vec![],
)
.await?
.json::<ScalewayUserDataKeyList>()
.await?
.user_data;
Ok(user_data)
}
#[cfg(feature = "blocking")]
pub fn get_userdata(
&self,
zone: &str,
machine_id: &str,
key: &str,
) -> Result<ScalewayUserData, ScalewayError> {
let servers = self
.get(
&format!(
"https://api.scaleway.com/instance/v1/zones/{zone}/servers/{server_id}/user_data/{key}",
zone = zone,
server_id = machine_id,
key = key,
),
vec![],
)?
.json::<ScalewayUserData>()?;
Ok(servers)
}
pub async fn get_userdata_async(
&self,
zone: &str,
machine_id: &str,
key: &str,
) -> Result<ScalewayUserData, ScalewayError> {
let user_data = self
.get_async(
&format!(
"https://api.scaleway.com/instance/v1/zones/{zone}/servers/{server_id}/user_data/{key}",
zone = zone,
server_id = machine_id,
key = key,
),
vec![],
)
.await?
.json::<ScalewayUserData>()
.await?;
Ok(user_data)
}
#[cfg(feature = "blocking")]
pub fn set_userdata(
&self,
zone: &str,
machine_id: &str,
key: &str,
value: &str,
) -> Result<(), ScalewayError> {
self
.patch(
&format!(
"https://api.scaleway.com/instance/v1/zones/{zone}/servers/{server_id}/user_data/{key}",
zone = zone,
server_id = machine_id,
key = key,
), value)?
.error_for_status()?;
Ok(())
}
pub async fn set_userdata_async(
&self,
zone: &str,
machine_id: &str,
key: &str,
value: &str,
) -> Result<(), ScalewayError> {
self
.patch_async(
&format!(
"https://api.scaleway.com/instance/v1/zones/{zone}/servers/{server_id}/user_data/{key}",
zone = zone,
server_id = machine_id,
key = key,
), value)
.await?.error_for_status()?;
Ok(())
}
#[cfg(feature = "blocking")]
pub fn delete_userdata(&self, zone: &str, machine_id: &str, key: &str) -> Result<(), ScalewayError> {
self.delete(&format!(
"https://api.scaleway.com/instance/v1/zones/{zone}/servers/{server_id}/user_data/{key}",
zone = zone,
server_id = machine_id,
key = key,
))?
.error_for_status()?;
Ok(())
}
pub async fn delete_userdata_async(
&self,
zone: &str,
machine_id: &str,
key: &str,
) -> Result<(), ScalewayError> {
self.delete_async(&format!(
"https://api.scaleway.com/instance/v1/zones/{zone}/servers/{server_id}/user_data/{key}",
zone = zone,
server_id = machine_id,
key = key,
))
.await?
.error_for_status()?;
Ok(())
}
}