Skip to content
New issue

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

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

Already on GitHub? # to your account

FLX-763: jeweler: Add Deployment trait #849

Merged
merged 1 commit into from
Oct 17, 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
94 changes: 94 additions & 0 deletions flecs-core/src/jeweler/app.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
pub use super::Result;
use crate::jeweler::deployment::{Deployment, DeploymentId};
use crate::jeweler::instance::{Instance, InstanceConfig, InstanceId, InstanceStatus};
use crate::vault::pouch::AppKey;
use anyhow::anyhow;
use async_trait::async_trait;
use flecs_app_manifest::AppManifest;
use std::collections::HashMap;
use std::sync::Arc;

type AppId = String;
#[async_trait]
pub trait AppDeployment {
async fn install_app(&self, manifest: &AppManifest) -> Result<AppId>;
async fn uninstall_app(&self, id: AppId) -> Result<()>;
async fn is_app_installed(&self, id: AppId) -> Result<bool>;
}
#[derive(Default)]
pub enum AppStatus {
#[default]
None,
Installed,
NotInstalled,
}

pub struct AppData {
desired: AppStatus,
instances: HashMap<InstanceId, Instance>,
id: Option<AppId>,
deployment: Arc<dyn Deployment>,
}

pub struct App {
key: AppKey,
properties: HashMap<DeploymentId, AppData>,
manifest: Option<Arc<AppManifest>>,
}

impl App {
pub async fn install(&mut self) -> Result<()> {
match &self.manifest {
None => Err(anyhow!(
"Can not install {:?}, no manifest present.",
self.key
))?,
Some(manifest) => {
for data in self.properties.values_mut() {
data.desired = AppStatus::Installed;
// TODO: Installing app in one deployment should not fail the whole install process for all deployments
if let Some(id) = &data.id {
if !data.deployment.is_app_installed(id.clone()).await? {
data.id = Some(data.deployment.install_app(manifest.as_ref()).await?);
}
}
}
}
}
Ok(())
}

pub async fn uninstall(&mut self) -> Result<()> {
for data in self.properties.values_mut() {
data.desired = AppStatus::NotInstalled;
// TODO: Uninstalling app in one deployment should not fail the whole uninstall process for all deployments
if let Some(id) = &data.id {
if data.deployment.is_app_installed(id.clone()).await? {
data.deployment.uninstall_app(id.clone()).await?;
data.id = None;
}
}
}
Ok(())
}

pub async fn create_instance(&mut self, instance_config: InstanceConfig) -> Result<()> {
for data in self.properties.values_mut() {
// TODO: Creating app in one deployment should not fail the whole creation process for all deployments
let id = data
.deployment
.create_instance(instance_config.clone())
.await?;
data.instances.insert(
id.clone(),
Instance::new(
id,
instance_config.clone(),
data.deployment.clone(),
InstanceStatus::Created,
),
);
}
Ok(())
}
}
11 changes: 11 additions & 0 deletions flecs-core/src/jeweler/deployment.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
use crate::jeweler::app::AppDeployment;
use crate::jeweler::instance::InstanceDeployment;
use crate::jeweler::network::NetworkDeployment;
use async_trait::async_trait;

pub type DeploymentId = String;

#[async_trait]
pub trait Deployment: Send + Sync + AppDeployment + InstanceDeployment + NetworkDeployment {
async fn id(&self) -> DeploymentId;
}
147 changes: 147 additions & 0 deletions flecs-core/src/jeweler/instance.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
use super::Result;
use crate::jeweler::deployment::Deployment;
use crate::jeweler::volume::VolumeDeployment;
use async_trait::async_trait;
use std::collections::HashMap;
use std::path::Path;
use std::sync::Arc;

#[async_trait]
pub trait InstanceDeployment: VolumeDeployment {
async fn create_instance(&self, config: InstanceConfig) -> Result<InstanceId>;
async fn delete_instance(&self, id: InstanceId) -> Result<()>;
async fn start_instance(&self, id: InstanceId) -> Result<()>;
async fn stop_instance(&self, id: InstanceId) -> Result<()>;
async fn ready_instance(&self, id: InstanceId) -> Result<()>;
async fn import_instance(&self, path: &Path) -> Result<InstanceId>;
async fn export_instance(&self, id: InstanceId, path: &Path) -> Result<()> {
let config = self.instance_config(id.clone()).await?;
self.export_config(config, path).await?;
self.export_volumes(id, path).await?;
Ok(())
}
async fn export_config(&self, config: InstanceConfig, path: &Path) -> Result<()>;
async fn instance_status(&self, id: InstanceId) -> Result<InstanceStatus>;
async fn instance_config(&self, id: InstanceId) -> Result<InstanceConfig>;
async fn instance(&self, id: InstanceId) -> Result<(InstanceConfig, InstanceStatus)> {
Ok((
self.instance_config(id.clone()).await?,
self.instance_status(id).await?,
))
}
async fn instances(&self) -> Result<HashMap<InstanceId, (InstanceConfig, InstanceStatus)>>;
async fn export_instances(&self, path: &Path) -> Result<()> {
for id in self.instances().await?.keys() {
self.export_instance(id.clone(), path).await?;
}
Ok(())
}
async fn copy_from_instance(&self, id: InstanceId, src: &Path, dst: &Path) -> Result<()>;
async fn copy_to_instance(&self, id: InstanceId, src: &Path, dst: &Path) -> Result<()>;
// TODO: Maybe move function to enum InstanceStatus
async fn is_instance_runnable(&self, id: InstanceId) -> Result<bool> {
Ok(self.instance_status(id).await? == InstanceStatus::Created)
}
// TODO: Maybe move function to enum InstanceStatus
async fn is_instance_running(&self, id: InstanceId) -> Result<bool> {
Ok(self.instance_status(id).await? == InstanceStatus::Running)
}
}

pub(crate) type InstanceId = String;
#[derive(Default, Clone)]
pub struct InstanceConfig {
// TBD
}

#[derive(Debug, PartialEq)]
pub enum InstanceStatus {
// TBD
NotCreated,
Requested,
ResourcesReady,
Created,
Stopped,
Running,
Orphaned,
Unknown,
}

pub struct Instance {
id: InstanceId,
pub config: InstanceConfig,
deployment: Arc<dyn Deployment>,
desired: InstanceStatus,
}

impl Instance {
pub(super) fn new(
id: InstanceId,
config: InstanceConfig,
deployment: Arc<dyn Deployment>,
desired: InstanceStatus,
) -> Self {
Self {
id,
config,
deployment,
desired,
}
}

pub async fn start(&mut self) -> Result<()> {
self.desired = InstanceStatus::Running;
match self.deployment.instance_status(self.id.clone()).await? {
InstanceStatus::Running => Ok(()),
_ => self.deployment.start_instance(self.id.clone()).await,
}
}

pub async fn stop(&mut self) -> Result<()> {
self.desired = InstanceStatus::Stopped;
match self.deployment.instance_status(self.id.clone()).await? {
InstanceStatus::Stopped => Ok(()),
_ => self.deployment.stop_instance(self.id.clone()).await,
}
}

pub async fn delete(self) -> Result<(), (anyhow::Error, Self)> {
self.deployment
.delete_instance(self.id.clone())
.await
.map_err(|e| (e, self))
}

pub async fn ready(&mut self) -> Result<()> {
// TODO: Check status, error handling
self.deployment.ready_instance(self.id.clone()).await
}

pub async fn export(&self, path: &Path) -> Result<()> {
self.deployment.export_instance(self.id.clone(), path).await
}

pub async fn status(&self) -> Result<InstanceStatus> {
self.deployment.instance_status(self.id.clone()).await
}

pub async fn copy_from(&self, src: &Path, dst: &Path) -> Result<()> {
self.deployment
.copy_from_instance(self.id.clone(), src, dst)
.await
}

pub async fn copy_to(&self, src: &Path, dst: &Path) -> Result<()> {
self.deployment
.copy_to_instance(self.id.clone(), src, dst)
.await
}

pub async fn is_runnable(&self) -> Result<bool> {
self.deployment.is_instance_runnable(self.id.clone()).await
}

pub async fn is_running(&self) -> Result<bool> {
self.deployment.is_instance_running(self.id.clone()).await
}
}
6 changes: 6 additions & 0 deletions flecs-core/src/jeweler/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
pub mod app;
pub mod deployment;
pub mod instance;
pub mod network;
pub mod volume;
pub use super::Result;
23 changes: 23 additions & 0 deletions flecs-core/src/jeweler/network.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
use super::Result;
use async_trait::async_trait;
use std::collections::HashMap;
use std::net::IpAddr;

pub type NetworkId = String;
pub struct Network {
// TBD
}
#[derive(Default)]
pub struct NetworkConfig {
// TBD
}

#[async_trait]
pub trait NetworkDeployment {
async fn create_network(&self, config: NetworkConfig) -> Result<NetworkId>;
async fn delete_network(&self, id: NetworkId) -> Result<()>;
async fn network(&self, id: NetworkId) -> Result<Network>;
async fn networks(&self) -> Result<HashMap<NetworkId, Network>>;
async fn connect_network(&self, id: NetworkId, address: IpAddr) -> Result<()>;
async fn disconnect_network(&self, id: NetworkId, address: IpAddr) -> Result<()>;
}
27 changes: 27 additions & 0 deletions flecs-core/src/jeweler/volume.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
use super::instance::InstanceId;
use super::Result;
use async_trait::async_trait;
use std::collections::HashMap;
use std::path::Path;

type VolumeId = String;
#[derive(Default)]
pub struct VolumeConfig {
// TBD
}

#[async_trait]
pub trait VolumeDeployment {
async fn create_volume(&self, config: VolumeConfig) -> Result<VolumeId>;
async fn delete_volume(&self, id: VolumeId) -> Result<()>;
async fn import_volume(&self, path: &Path) -> Result<VolumeId>;
async fn export_volume(&self, id: VolumeId, path: &Path) -> Result<()>; // TODO: Arguments, return type
async fn volumes(&self, instance_id: InstanceId) -> Result<HashMap<VolumeId, VolumeConfig>>;
async fn export_volumes(&self, instance_id: InstanceId, path: &Path) -> Result<()> {
// TODO: more logic
for volume_id in self.volumes(instance_id).await?.keys() {
self.export_volume(volume_id.clone(), path).await?;
}
Ok(())
}
}
2 changes: 2 additions & 0 deletions flecs-core/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
mod cellar;
mod flecs_rest;
pub mod fsm;
pub mod jeweler;
pub mod lore;
pub mod quest;
pub mod relic;
Expand All @@ -9,3 +10,4 @@ pub mod vault;

pub use anyhow::Error;
pub use anyhow::Result;
// TODO: Unify structs (App, Instance, Deployment, ...) with structs from Pouches and move them there