use std::collections::BTreeMap;
use std::path::PathBuf;
use serde::{Deserialize, Serialize};
use super::CloudSecretsConfig;
use crate::domain::{
DiskImageFormat, HostPermissions, MountOptions, NetworkPolicy, OwnedVolumeStorage, Patch,
PullPolicy, Rlimit, RlimitResource, SandboxLogLevel, StatVirtualization, VolumeMount,
default_private, default_strict,
};
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "ts", derive(ts_rs::TS))]
#[serde(rename_all = "snake_case")]
pub enum CloudPullPolicy {
#[default]
IfMissing,
Always,
Never,
}
impl From<PullPolicy> for CloudPullPolicy {
fn from(policy: PullPolicy) -> Self {
match policy {
PullPolicy::IfMissing => Self::IfMissing,
PullPolicy::Always => Self::Always,
PullPolicy::Never => Self::Never,
}
}
}
impl From<CloudPullPolicy> for PullPolicy {
fn from(policy: CloudPullPolicy) -> Self {
match policy {
CloudPullPolicy::IfMissing => Self::IfMissing,
CloudPullPolicy::Always => Self::Always,
CloudPullPolicy::Never => Self::Never,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "ts", derive(ts_rs::TS))]
#[serde(rename_all = "snake_case")]
pub enum CloudDiskImageFormat {
Qcow2,
Raw,
Vmdk,
}
impl From<DiskImageFormat> for CloudDiskImageFormat {
fn from(format: DiskImageFormat) -> Self {
match format {
DiskImageFormat::Qcow2 => Self::Qcow2,
DiskImageFormat::Raw => Self::Raw,
DiskImageFormat::Vmdk => Self::Vmdk,
}
}
}
impl From<CloudDiskImageFormat> for DiskImageFormat {
fn from(format: CloudDiskImageFormat) -> Self {
match format {
CloudDiskImageFormat::Qcow2 => Self::Qcow2,
CloudDiskImageFormat::Raw => Self::Raw,
CloudDiskImageFormat::Vmdk => Self::Vmdk,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "ts", derive(ts_rs::TS))]
#[serde(rename_all = "snake_case")]
pub enum CloudRlimitResource {
Cpu,
Fsize,
Data,
Stack,
Core,
Rss,
Nproc,
Nofile,
Memlock,
As,
Locks,
Sigpending,
Msgqueue,
Nice,
Rtprio,
Rttime,
}
impl From<RlimitResource> for CloudRlimitResource {
fn from(resource: RlimitResource) -> Self {
match resource {
RlimitResource::Cpu => Self::Cpu,
RlimitResource::Fsize => Self::Fsize,
RlimitResource::Data => Self::Data,
RlimitResource::Stack => Self::Stack,
RlimitResource::Core => Self::Core,
RlimitResource::Rss => Self::Rss,
RlimitResource::Nproc => Self::Nproc,
RlimitResource::Nofile => Self::Nofile,
RlimitResource::Memlock => Self::Memlock,
RlimitResource::As => Self::As,
RlimitResource::Locks => Self::Locks,
RlimitResource::Sigpending => Self::Sigpending,
RlimitResource::Msgqueue => Self::Msgqueue,
RlimitResource::Nice => Self::Nice,
RlimitResource::Rtprio => Self::Rtprio,
RlimitResource::Rttime => Self::Rttime,
}
}
}
impl From<CloudRlimitResource> for RlimitResource {
fn from(resource: CloudRlimitResource) -> Self {
match resource {
CloudRlimitResource::Cpu => Self::Cpu,
CloudRlimitResource::Fsize => Self::Fsize,
CloudRlimitResource::Data => Self::Data,
CloudRlimitResource::Stack => Self::Stack,
CloudRlimitResource::Core => Self::Core,
CloudRlimitResource::Rss => Self::Rss,
CloudRlimitResource::Nproc => Self::Nproc,
CloudRlimitResource::Nofile => Self::Nofile,
CloudRlimitResource::Memlock => Self::Memlock,
CloudRlimitResource::As => Self::As,
CloudRlimitResource::Locks => Self::Locks,
CloudRlimitResource::Sigpending => Self::Sigpending,
CloudRlimitResource::Msgqueue => Self::Msgqueue,
CloudRlimitResource::Nice => Self::Nice,
CloudRlimitResource::Rtprio => Self::Rtprio,
CloudRlimitResource::Rttime => Self::Rttime,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "ts", derive(ts_rs::TS))]
pub struct CloudRlimit {
pub resource: CloudRlimitResource,
pub soft: u64,
pub hard: u64,
}
impl From<Rlimit> for CloudRlimit {
fn from(rlimit: Rlimit) -> Self {
Self {
resource: rlimit.resource.into(),
soft: rlimit.soft,
hard: rlimit.hard,
}
}
}
impl From<CloudRlimit> for Rlimit {
fn from(rlimit: CloudRlimit) -> Self {
Self {
resource: rlimit.resource.into(),
soft: rlimit.soft,
hard: rlimit.hard,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "ts", derive(ts_rs::TS))]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum CloudPatch {
Text {
path: String,
content: String,
mode: Option<u32>,
replace: bool,
},
File {
path: String,
content: Vec<u8>,
mode: Option<u32>,
replace: bool,
},
CopyFile {
#[cfg_attr(feature = "ts", ts(type = "string"))]
#[cfg_attr(feature = "utoipa", schema(value_type = String))]
src: PathBuf,
dst: String,
mode: Option<u32>,
replace: bool,
},
CopyDir {
#[cfg_attr(feature = "ts", ts(type = "string"))]
#[cfg_attr(feature = "utoipa", schema(value_type = String))]
src: PathBuf,
dst: String,
replace: bool,
},
Symlink {
target: String,
link: String,
replace: bool,
},
Mkdir {
path: String,
mode: Option<u32>,
},
Remove {
path: String,
},
Append {
path: String,
content: String,
},
}
impl From<Patch> for CloudPatch {
fn from(patch: Patch) -> Self {
match patch {
Patch::Text {
path,
content,
mode,
replace,
} => Self::Text {
path,
content,
mode,
replace,
},
Patch::File {
path,
content,
mode,
replace,
} => Self::File {
path,
content,
mode,
replace,
},
Patch::CopyFile {
src,
dst,
mode,
replace,
} => Self::CopyFile {
src,
dst,
mode,
replace,
},
Patch::CopyDir { src, dst, replace } => Self::CopyDir { src, dst, replace },
Patch::Symlink {
target,
link,
replace,
} => Self::Symlink {
target,
link,
replace,
},
Patch::Mkdir { path, mode } => Self::Mkdir { path, mode },
Patch::Remove { path } => Self::Remove { path },
Patch::Append { path, content } => Self::Append { path, content },
}
}
}
impl From<CloudPatch> for Patch {
fn from(patch: CloudPatch) -> Self {
match patch {
CloudPatch::Text {
path,
content,
mode,
replace,
} => Self::Text {
path,
content,
mode,
replace,
},
CloudPatch::File {
path,
content,
mode,
replace,
} => Self::File {
path,
content,
mode,
replace,
},
CloudPatch::CopyFile {
src,
dst,
mode,
replace,
} => Self::CopyFile {
src,
dst,
mode,
replace,
},
CloudPatch::CopyDir { src, dst, replace } => Self::CopyDir { src, dst, replace },
CloudPatch::Symlink {
target,
link,
replace,
} => Self::Symlink {
target,
link,
replace,
},
CloudPatch::Mkdir { path, mode } => Self::Mkdir { path, mode },
CloudPatch::Remove { path } => Self::Remove { path },
CloudPatch::Append { path, content } => Self::Append { path, content },
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "ts", derive(ts_rs::TS))]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum CloudRootfsSource {
Bind {
#[cfg_attr(feature = "ts", ts(type = "string"))]
path: PathBuf,
},
Oci {
reference: String,
},
DiskImage {
#[cfg_attr(feature = "ts", ts(type = "string"))]
path: PathBuf,
format: CloudDiskImageFormat,
fstype: Option<String>,
},
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "ts", derive(ts_rs::TS))]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum CloudVolumeMount {
Owned {
guest: String,
storage: OwnedVolumeStorage,
options: MountOptions,
stat_virtualization: StatVirtualization,
host_permissions: HostPermissions,
},
Bind {
#[cfg_attr(feature = "ts", ts(type = "string"))]
#[cfg_attr(feature = "utoipa", schema(value_type = String))]
host: PathBuf,
guest: String,
#[serde(default)]
options: MountOptions,
#[serde(default = "default_strict")]
stat_virtualization: StatVirtualization,
#[serde(default = "default_private")]
host_permissions: HostPermissions,
#[serde(default)]
quota_mib: Option<u32>,
},
Named {
name: String,
guest: String,
#[serde(default)]
options: MountOptions,
#[serde(default = "default_strict")]
stat_virtualization: StatVirtualization,
#[serde(default = "default_private")]
host_permissions: HostPermissions,
},
Tmpfs {
guest: String,
#[serde(default)]
size_mib: Option<u32>,
#[serde(default)]
options: MountOptions,
},
DiskImage {
#[cfg_attr(feature = "ts", ts(type = "string"))]
#[cfg_attr(feature = "utoipa", schema(value_type = String))]
host: PathBuf,
guest: String,
format: CloudDiskImageFormat,
#[serde(default)]
fstype: Option<String>,
#[serde(default)]
options: MountOptions,
},
}
impl From<CloudVolumeMount> for VolumeMount {
fn from(m: CloudVolumeMount) -> Self {
match m {
CloudVolumeMount::Owned {
guest,
storage,
options,
stat_virtualization,
host_permissions,
} => VolumeMount::Owned {
guest,
storage,
options,
stat_virtualization,
host_permissions,
},
CloudVolumeMount::Bind {
host,
guest,
options,
stat_virtualization,
host_permissions,
quota_mib,
} => VolumeMount::Bind {
host,
guest,
options,
stat_virtualization,
host_permissions,
follow_root_symlinks: false,
quota_mib,
},
CloudVolumeMount::Named {
name,
guest,
options,
stat_virtualization,
host_permissions,
} => VolumeMount::Named {
name,
guest,
create: None,
options,
stat_virtualization,
host_permissions,
follow_root_symlinks: false,
},
CloudVolumeMount::Tmpfs {
guest,
size_mib,
options,
} => VolumeMount::Tmpfs {
guest,
size_mib,
options,
},
CloudVolumeMount::DiskImage {
host,
guest,
format,
fstype,
options,
} => VolumeMount::DiskImage {
host,
guest,
format: format.into(),
fstype,
options,
},
}
}
}
impl From<VolumeMount> for CloudVolumeMount {
fn from(m: VolumeMount) -> Self {
match m {
VolumeMount::Owned {
guest,
storage,
options,
stat_virtualization,
host_permissions,
} => CloudVolumeMount::Owned {
guest,
storage,
options,
stat_virtualization,
host_permissions,
},
VolumeMount::Bind {
host,
guest,
options,
stat_virtualization,
host_permissions,
follow_root_symlinks: _,
quota_mib,
} => CloudVolumeMount::Bind {
host,
guest,
options,
stat_virtualization,
host_permissions,
quota_mib,
},
VolumeMount::Named {
name,
guest,
create: _,
options,
stat_virtualization,
host_permissions,
follow_root_symlinks: _,
} => CloudVolumeMount::Named {
name,
guest,
options,
stat_virtualization,
host_permissions,
},
VolumeMount::Tmpfs {
guest,
size_mib,
options,
} => CloudVolumeMount::Tmpfs {
guest,
size_mib,
options,
},
VolumeMount::DiskImage {
host,
guest,
format,
fstype,
options,
} => CloudVolumeMount::DiskImage {
host,
guest,
format: format.into(),
fstype,
options,
},
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "ts", derive(ts_rs::TS))]
#[serde(default)]
pub struct CloudNetworkSpec {
pub enabled: bool,
#[serde(skip_serializing_if = "Option::is_none")]
pub policy: Option<NetworkPolicy>,
#[serde(skip_serializing_if = "Option::is_none")]
pub secrets: Option<CloudSecretsConfig>,
pub strict: bool,
#[serde(default, skip_serializing_if = "Option::is_none")]
#[serde(rename = "max_connections", alias = "max_tcp_connections")]
pub max_tcp_connections: Option<usize>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub max_udp_connections: Option<usize>,
}
impl Default for CloudNetworkSpec {
fn default() -> Self {
Self {
enabled: true,
policy: None,
secrets: None,
strict: false,
max_tcp_connections: None,
max_udp_connections: None,
}
}
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "ts", derive(ts_rs::TS))]
#[serde(default)]
pub struct CloudSandboxRuntimeOptions {
pub workdir: Option<String>,
pub shell: Option<String>,
pub scripts: BTreeMap<String, String>,
pub entrypoint: Option<Vec<String>>,
pub cmd: Option<Vec<String>>,
pub user: Option<String>,
pub log_level: Option<SandboxLogLevel>,
}