use serde::{Deserialize, Serialize};
use time::OffsetDateTime;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum SailboxStatus {
Running,
Paused,
Sleeping,
Failed,
Terminated,
Other(String),
}
impl SailboxStatus {
pub fn as_str(&self) -> &str {
match self {
SailboxStatus::Running => "running",
SailboxStatus::Paused => "paused",
SailboxStatus::Sleeping => "sleeping",
SailboxStatus::Failed => "failed",
SailboxStatus::Terminated => "terminated",
SailboxStatus::Other(s) => s,
}
}
}
impl From<&str> for SailboxStatus {
fn from(s: &str) -> SailboxStatus {
match s {
"running" => SailboxStatus::Running,
"paused" => SailboxStatus::Paused,
"sleeping" => SailboxStatus::Sleeping,
"failed" => SailboxStatus::Failed,
"terminated" => SailboxStatus::Terminated,
other => SailboxStatus::Other(other.to_string()),
}
}
}
impl std::fmt::Display for SailboxStatus {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
impl Serialize for SailboxStatus {
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
serializer.serialize_str(self.as_str())
}
}
impl<'de> Deserialize<'de> for SailboxStatus {
fn deserialize<D: serde::Deserializer<'de>>(
deserializer: D,
) -> Result<SailboxStatus, D::Error> {
Ok(SailboxStatus::from(
String::deserialize(deserializer)?.as_str(),
))
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SailboxStatusFilter {
Running,
Paused,
Sleeping,
Failed,
Terminated,
}
impl SailboxStatusFilter {
pub fn as_str(&self) -> &'static str {
match self {
SailboxStatusFilter::Running => "running",
SailboxStatusFilter::Paused => "paused",
SailboxStatusFilter::Sleeping => "sleeping",
SailboxStatusFilter::Failed => "failed",
SailboxStatusFilter::Terminated => "terminated",
}
}
pub fn parse(s: &str) -> Option<SailboxStatusFilter> {
match s {
"running" => Some(SailboxStatusFilter::Running),
"paused" => Some(SailboxStatusFilter::Paused),
"sleeping" => Some(SailboxStatusFilter::Sleeping),
"failed" => Some(SailboxStatusFilter::Failed),
"terminated" => Some(SailboxStatusFilter::Terminated),
_ => None,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Protocol {
Tcp,
Http,
Other(String),
}
impl Protocol {
pub fn as_str(&self) -> &str {
match self {
Protocol::Tcp => "tcp",
Protocol::Http => "http",
Protocol::Other(s) => s,
}
}
}
impl From<&str> for Protocol {
fn from(s: &str) -> Protocol {
match s {
"tcp" => Protocol::Tcp,
"http" => Protocol::Http,
other => Protocol::Other(other.to_string()),
}
}
}
impl std::fmt::Display for Protocol {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
impl Serialize for Protocol {
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
serializer.serialize_str(self.as_str())
}
}
impl<'de> Deserialize<'de> for Protocol {
fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Protocol, D::Error> {
Ok(Protocol::from(String::deserialize(deserializer)?.as_str()))
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ListenerRouteStatus {
Unspecified,
Pending,
Active,
Restoring,
Unavailable,
Other(String),
}
impl ListenerRouteStatus {
pub fn as_str(&self) -> &str {
match self {
ListenerRouteStatus::Unspecified => "LISTENER_ROUTE_STATUS_UNSPECIFIED",
ListenerRouteStatus::Pending => "LISTENER_ROUTE_STATUS_PENDING",
ListenerRouteStatus::Active => "LISTENER_ROUTE_STATUS_ACTIVE",
ListenerRouteStatus::Restoring => "LISTENER_ROUTE_STATUS_RESTORING",
ListenerRouteStatus::Unavailable => "LISTENER_ROUTE_STATUS_UNAVAILABLE",
ListenerRouteStatus::Other(s) => s,
}
}
}
impl From<&str> for ListenerRouteStatus {
fn from(s: &str) -> ListenerRouteStatus {
match s {
"LISTENER_ROUTE_STATUS_UNSPECIFIED" => ListenerRouteStatus::Unspecified,
"LISTENER_ROUTE_STATUS_PENDING" => ListenerRouteStatus::Pending,
"LISTENER_ROUTE_STATUS_ACTIVE" => ListenerRouteStatus::Active,
"LISTENER_ROUTE_STATUS_RESTORING" => ListenerRouteStatus::Restoring,
"LISTENER_ROUTE_STATUS_UNAVAILABLE" => ListenerRouteStatus::Unavailable,
other => ListenerRouteStatus::Other(other.to_string()),
}
}
}
impl std::fmt::Display for ListenerRouteStatus {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
impl Serialize for ListenerRouteStatus {
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
serializer.serialize_str(self.as_str())
}
}
impl<'de> Deserialize<'de> for ListenerRouteStatus {
fn deserialize<D: serde::Deserializer<'de>>(
deserializer: D,
) -> Result<ListenerRouteStatus, D::Error> {
Ok(ListenerRouteStatus::from(
String::deserialize(deserializer)?.as_str(),
))
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum IngressProtocol {
Tcp,
Http,
}
impl IngressProtocol {
pub fn as_str(&self) -> &'static str {
match self {
IngressProtocol::Tcp => "tcp",
IngressProtocol::Http => "http",
}
}
pub fn parse(s: &str) -> Option<IngressProtocol> {
match s {
"tcp" => Some(IngressProtocol::Tcp),
"http" => Some(IngressProtocol::Http),
_ => None,
}
}
}
impl std::fmt::Display for IngressProtocol {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
impl Serialize for IngressProtocol {
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
serializer.serialize_str(self.as_str())
}
}
#[derive(Debug, Clone, Serialize)]
pub struct SailboxHandle {
pub sailbox_id: String,
pub name: String,
pub status: SailboxStatus,
pub worker_address: String,
pub exec_endpoint: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SailboxInfo {
pub sailbox_id: String,
pub app_id: String,
pub app_name: String,
pub image_id: String,
pub name: String,
pub status: SailboxStatus,
pub memory_mib: i64,
pub vcpu_count: i64,
pub state_disk_size_gib: i64,
pub cpu_requested_vcpu: i64,
pub cpu_used_vcpu: f64,
pub memory_requested_bytes: i64,
pub memory_used_bytes: i64,
pub disk_requested_bytes: i64,
pub disk_used_bytes: i64,
pub architecture: String,
#[serde(default)]
pub guest_schema_version: Option<i64>,
#[serde(default)]
pub error_message: Option<String>,
pub checkpoint_generation: i64,
#[serde(with = "time::serde::rfc3339::option", default)]
pub started_at: Option<OffsetDateTime>,
#[serde(with = "time::serde::rfc3339::option", default)]
pub last_checkpointed_at: Option<OffsetDateTime>,
#[serde(with = "time::serde::rfc3339")]
pub created_at: OffsetDateTime,
#[serde(with = "time::serde::rfc3339")]
pub updated_at: OffsetDateTime,
}
#[derive(Debug, Clone, Serialize)]
pub struct SailboxPage {
pub items: Vec<SailboxInfo>,
pub limit: i64,
pub offset: i64,
pub total: i64,
pub has_more: bool,
}
#[derive(Debug, Clone, Serialize)]
pub struct SailboxCheckpoint {
pub checkpoint_id: String,
pub sailbox_id: String,
pub checkpoint_generation: i64,
pub status: SailboxStatus,
}
#[derive(Debug, Clone)]
pub struct ListQuery {
pub app: Option<String>,
pub status: Option<SailboxStatusFilter>,
pub search: Option<String>,
pub max_guest_schema_version: Option<i64>,
pub limit: i64,
pub offset: i64,
}
pub const DEFAULT_LIST_LIMIT: i64 = 50;
pub const MAX_SAILBOX_VCPUS: i64 = 4;
pub const MIN_SAILBOX_MEMORY_MIB: i64 = 1024;
pub const MAX_SAILBOX_MEMORY_MIB: i64 = 8192;
pub const MAX_SAILBOX_DISK_GIB: i64 = 32;
impl Default for ListQuery {
fn default() -> ListQuery {
ListQuery {
app: None,
status: None,
search: None,
max_guest_schema_version: None,
limit: DEFAULT_LIST_LIMIT,
offset: 0,
}
}
}
#[derive(Debug, Clone, Serialize)]
pub struct IngressPort {
pub guest_port: u32,
pub protocol: IngressProtocol,
pub allowlist: Vec<String>,
}
#[derive(Debug, Clone, Serialize)]
pub struct VolumeMount {
pub volume_id: String,
pub mount_path: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct NfsVolume {
pub volume_id: String,
pub name: String,
pub backend: String,
pub status: String,
#[serde(with = "time::serde::rfc3339::option", default)]
pub created_at: Option<OffsetDateTime>,
#[serde(with = "time::serde::rfc3339::option", default)]
pub updated_at: Option<OffsetDateTime>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AddListenerResponse {
pub guest_port: u32,
pub protocol: Protocol,
#[serde(default)]
pub public_url: String,
#[serde(default)]
pub tcp_public_host: String,
#[serde(default)]
pub tcp_public_port: u32,
}
impl AddListenerResponse {
pub fn endpoint(&self) -> Option<String> {
if !self.public_url.is_empty() {
return Some(self.public_url.clone());
}
if !self.tcp_public_host.is_empty() && self.tcp_public_port != 0 {
return Some(format!("{}:{}", self.tcp_public_host, self.tcp_public_port));
}
None
}
}
#[derive(Debug, Clone)]
pub struct CreateSailboxRequest {
pub app_id: String,
pub name: String,
pub ingress_ports: Vec<IngressPort>,
pub volume_mounts: Vec<VolumeMount>,
pub image: crate::image::ImageSpec,
pub cpu: Option<i64>,
pub memory_mib: Option<i64>,
pub state_disk_size_gib: Option<i64>,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn list_query_default_uses_a_valid_nonzero_limit() {
let query = ListQuery::default();
assert_eq!(query.limit, DEFAULT_LIST_LIMIT);
assert!(query.limit > 0);
assert_eq!(query.offset, 0);
}
}