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

feat(core): make a repository dir for each network #239

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
Open
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
52 changes: 51 additions & 1 deletion coffee_cmd/src/coffee_term/command_show.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@ use term::Element;

use coffee_lib::error;
use coffee_lib::errors::CoffeeError;
use coffee_lib::types::response::{CoffeeList, CoffeeNurse, CoffeeRemote, CoffeeTip, NurseStatus};
use coffee_lib::types::response::{
ChainOfResponsibilityStatus, CoffeeList, CoffeeNurse, CoffeeRemote, CoffeeTip, Defect,
NurseStatus,
};

pub fn show_list(coffee_list: Result<CoffeeList, CoffeeError>) -> Result<(), CoffeeError> {
let remotes = coffee_list?;
Expand Down Expand Up @@ -86,6 +89,49 @@ pub fn show_remote_list(remote_list: Result<CoffeeRemote, CoffeeError>) -> Resul
Ok(())
}

pub fn show_nurse_verify(nurse_verify: &ChainOfResponsibilityStatus) -> Result<(), CoffeeError> {
if nurse_verify.defects.is_empty() {
term::success!("Coffee configuration is not corrupt! No need to run coffee nurse");
} else {
let mut table = radicle_term::Table::new(TableOptions::bordered());
table.push([
term::format::dim(String::from("●")),
term::format::bold(String::from("Defects")),
term::format::bold(String::from("Affected")),
]);
table.divider();

for defect in nurse_verify.defects.iter() {
match defect {
Defect::RepositoryLocallyAbsent(repos) => {
let defect = "Repository missing locally";
for repo in repos {
table.push([
term::format::positive("●").into(),
term::format::bold(defect.to_owned()),
term::format::highlight(repo.clone()),
]);
}
}
Defect::CoffeeGlobalRepoCleanup(networks) => {
let defect = "Network specific repository missing";
let networks = networks
.iter()
.map(|(network, _)| network.clone())
.collect::<Vec<String>>();
table.push([
term::format::positive("●").into(),
term::format::bold(defect.to_owned()),
term::format::highlight(networks.join(", ")),
]);
}
}
}
table.print();
}
Ok(())
}

pub fn show_nurse_result(
nurse_result: Result<CoffeeNurse, CoffeeError>,
) -> Result<(), CoffeeError> {
Expand All @@ -111,10 +157,14 @@ pub fn show_nurse_result(
NurseStatus::RepositoryLocallyRemoved(_) => {
"Removed from local storage".to_string()
}
NurseStatus::MovingGlobalRepostoryTo(_) => {
"Moving Global repository directory".to_string()
}
};
let repos_str = match status {
NurseStatus::RepositoryLocallyRestored(repos)
| NurseStatus::RepositoryLocallyRemoved(repos) => repos.join(", "),
NurseStatus::MovingGlobalRepostoryTo(network) => network.to_owned(),
};

table.push([
Expand Down
6 changes: 3 additions & 3 deletions coffee_cmd/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ async fn run(args: CoffeeArgs, mut coffee: CoffeeManager) -> Result<(), CoffeeEr
match action {
Some(RemoteAction::Add { name, url }) => {
let mut spinner = term::spinner(format!("Fetch remote from {url}"));
let result = coffee.add_remote(&name, &url).await;
let result = coffee.add_remote(&name, &url, false).await;
if let Err(err) = &result {
spinner.error(format!("Error while add remote: {err}"));
return result;
Expand Down Expand Up @@ -154,9 +154,9 @@ async fn run(args: CoffeeArgs, mut coffee: CoffeeManager) -> Result<(), CoffeeEr
CoffeeCommand::Nurse { verify } => {
if verify {
let result = coffee.nurse_verify().await?;
term::info!("{}", result);
coffee_term::show_nurse_verify(&result)?;
if !result.is_sane() {
term::info!("Coffee local directory is damaged, please run `coffee nurse` to try to fix it");
term::warning(term::style("Coffee local directory is damaged, please run `coffee nurse` to try to fix it").bold());
}
} else {
let nurse_result = coffee.nurse().await;
Expand Down
77 changes: 63 additions & 14 deletions coffee_core/src/coffee.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
use std::collections::HashMap;
use std::fmt::Debug;
use std::vec::Vec;
use tokio::fs;

use async_trait::async_trait;
use clightningrpc_common::client::Client;
Expand All @@ -12,6 +11,7 @@ use log;
use serde::de::DeserializeOwned;
use serde::{Deserialize, Serialize};
use serde_json::json;
use tokio::fs;
use tokio::process::Command;

use coffee_github::repository::Github;
Expand All @@ -20,6 +20,7 @@ use coffee_lib::plugin_manager::PluginManager;
use coffee_lib::repository::Repository;
use coffee_lib::types::response::*;
use coffee_lib::url::URL;
use coffee_lib::utils::rm_dir_if_exist;
use coffee_lib::{commit_id, error, get_repo_info, sh};
use coffee_storage::model::repository::{Kind, Repository as RepositoryInfo};
use coffee_storage::nosql_db::NoSQlStorage;
Expand Down Expand Up @@ -78,8 +79,8 @@ pub struct CoffeeManager {
}

impl CoffeeManager {
pub async fn new(conf: &dyn CoffeeArgs) -> Result<Self, CoffeeError> {
let conf = CoffeeConf::new(conf).await?;
pub async fn new(conf_args: &dyn CoffeeArgs) -> Result<Self, CoffeeError> {
let conf = CoffeeConf::new(conf_args).await?;
let mut coffee = CoffeeManager {
config: conf.clone(),
coffee_cln_config: CLNConf::new(conf.config_path, true),
Expand All @@ -96,15 +97,15 @@ impl CoffeeManager {
/// when coffee is configured, run an inventory to collect all the necessary information
/// about the coffee ecosystem.
async fn inventory(&mut self) -> Result<(), CoffeeError> {
let skip_verify = self.config.skip_verify;
let _ = self
.storage
.load::<CoffeeStorageInfo>(&self.config.network)
.await
.map(|store| {
self.config = store.config;
});
// FIXME: check if this exist in a better wai
let _ = self
let global_repositories = self
.storage
.load::<HashMap<RepoName, RepositoryInfo>>("repositories")
.await
Expand All @@ -118,10 +119,38 @@ impl CoffeeManager {
});
});

if let Ok(_) = global_repositories {
// HACK: this should be done with the nurse command, but
// due that currently migrating the database with the nurse
// logic is a little bit tricky we do this hack and we try
// to move on, but if you are looking something to do in coffee
// it is possible to take this problem and design a solution.
// FIXME: add the drop method inside nosql_db
}

let local_repositories = self
.storage
.load::<HashMap<RepoName, RepositoryInfo>>(&format!(
"{}/repositories",
self.config.network
))
.await;
if let Ok(repos) = local_repositories {
// FIXME: till we are not able to remove a key from
// the database
self.repos.clear();
repos.iter().for_each(|repo| match repo.1.kind {
Kind::Git => {
let repo = Github::from(repo.1);
self.repos.insert(repo.name(), Box::new(repo));
}
});
}
vincenzopalazzo marked this conversation as resolved.
Show resolved Hide resolved

if let Err(err) = self.coffee_cln_config.parse() {
log::error!("{}", err.cause);
}
if !self.config.skip_verify {
if !skip_verify {
// Check for the chain of responsibility
let status = self.recovery_strategies.scan(self).await?;
log::debug!("Chain of responsibility status: {:?}", status);
Expand Down Expand Up @@ -190,7 +219,10 @@ impl CoffeeManager {
.store(&self.config.network, &store_info)
.await?;
self.storage
.store("repositories", &store_info.repositories)
.store(
&format!("{}/repositories", self.config.network),
&store_info.repositories,
)
.await?;
Ok(())
}
Expand Down Expand Up @@ -424,15 +456,11 @@ impl PluginManager for CoffeeManager {
Ok(())
}

async fn add_remote(&mut self, name: &str, url: &str) -> Result<(), CoffeeError> {
// FIXME: we should allow some error here like
// for the add remote command the no found error for the `repository`
// directory is fine.

if self.repos.contains_key(name) {
async fn add_remote(&mut self, name: &str, url: &str, force: bool) -> Result<(), CoffeeError> {
if !force && self.repos.contains_key(name) {
return Err(error!("repository with name: {name} already exists"));
}
let url = URL::new(&self.config.root_path, url, name);
let url = URL::new(&self.config.path(), url, name);
log::debug!("remote adding: {} {}", name, &url.url_string);
let mut repo = Github::new(name, &url);
repo.init().await?;
Expand Down Expand Up @@ -545,12 +573,32 @@ impl PluginManager for CoffeeManager {
let mut actions = self.patch_repository_locally_absent(repos.to_vec()).await?;
nurse_actions.append(&mut actions);
}

Defect::CoffeeGlobalRepoCleanup(networks) => {
let iter = self
.repos
.iter()
.map(|(name, repo)| (name.to_owned(), repo.url()))
.collect::<Vec<(String, URL)>>();
for (network, _) in networks {
log::debug!("reindexing repository for the network `{:?}`", network);
for (name, url) in &iter {
self.add_remote(&name, &url.url_string, true).await?;
}
nurse_actions
.push(NurseStatus::MovingGlobalRepostoryTo(network.to_owned()));
}
let global_repo = format!("{}/repositories", self.config.root_path);
rm_dir_if_exist(&global_repo).await?;
}
}
}
let mut nurse = CoffeeNurse {
status: nurse_actions,
};
nurse.organize();
// Refesh the status
self.flush().await?;
Ok(nurse)
}

Expand All @@ -573,6 +621,7 @@ impl PluginManager for CoffeeManager {
.get_mut(repo_name)
.ok_or_else(|| error!("repository with name: {repo_name} not found"))?;

repo.change_root_path(&self.config.path());
vincenzopalazzo marked this conversation as resolved.
Show resolved Hide resolved
vincenzopalazzo marked this conversation as resolved.
Show resolved Hide resolved
match repo.recover().await {
Ok(_) => {
log::info!("repository {} recovered", repo_name.clone());
Expand Down
29 changes: 16 additions & 13 deletions coffee_core/src/config.rs
Original file line number Diff line number Diff line change
@@ -1,37 +1,40 @@
//! Coffee configuration utils.
use log::info;
use serde::{Deserialize, Serialize};
use std::env;

use crate::CoffeeOperation;
use serde::{Deserialize, Serialize};

use coffee_lib::utils::check_dir_or_make_if_missing;
use coffee_lib::{errors::CoffeeError, plugin::Plugin};

use crate::CoffeeArgs;
use crate::CoffeeOperation;

/// Custom coffee configuration, given by a command line list of arguments
/// or a coffee configuration file.
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct CoffeeConf {
/// Network configuration related
/// to core lightning network
pub network: String,
/// root path plugin manager
pub root_path: String,
/// path of core lightning configuration file
/// managed by coffee
pub config_path: String,
/// path of the core lightning configuration file
/// not managed by core lightning
/// (this file included the file managed by coffee)
///
/// This file included the file managed by coffee
pub cln_config_path: Option<String>,
/// root cln directory path
pub cln_root: Option<String>,
/// root path plugin manager
pub root_path: String,
/// all plugins that are installed
/// with the plugin manager.
pub plugins: Vec<Plugin>,
/// A flag that indicates if the
/// user wants to skip the verification
/// of nurse.
#[serde(skip)]
pub skip_verify: bool,
}

Expand All @@ -47,7 +50,7 @@ impl CoffeeConf {
def_path = def_path.strip_suffix('/').unwrap_or(&def_path).to_string();
def_path += "/.coffee";
check_dir_or_make_if_missing(def_path.to_string()).await?;
info!("creating coffee home at {def_path}");
log::info!("creating coffee home at {def_path}");

let mut coffee = CoffeeConf {
network: "bitcoin".to_owned(),
Expand All @@ -62,10 +65,8 @@ impl CoffeeConf {
// check the command line arguments and bind them
// inside the coffee conf
coffee.bind_cmd_line_params(conf)?;

check_dir_or_make_if_missing(format!("{def_path}/{}", coffee.network)).await?;
check_dir_or_make_if_missing(format!("{def_path}/{}/plugins", coffee.network)).await?;
check_dir_or_make_if_missing(format!("{def_path}/repositories")).await?;
// after we know all the information regarding
// the configuration we try to see if there is
// something stored already to the disk.
Expand Down Expand Up @@ -101,10 +102,12 @@ impl CoffeeConf {
}
}
}

// FIXME: be able to put the directory also in another place!
// for now it is fixed in the Home/.coffee but another good place
// will be, the .lightning dir
Ok(())
}

/// Return the root path of the coffee manager instance
/// this include also the network path.
pub fn path(&self) -> String {
format!("{}/{}", self.root_path, self.network)
}
}
7 changes: 5 additions & 2 deletions coffee_core/src/nurse/chain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ use async_trait::async_trait;
use coffee_lib::errors::CoffeeError;
use coffee_lib::types::response::{ChainOfResponsibilityStatus, Defect};

use super::strategy::GitRepositoryLocallyAbsentStrategy;
use super::strategy::{CoffeeRepositoryDirCleanUpStrategy, GitRepositoryLocallyAbsentStrategy};
use crate::coffee::CoffeeManager;

#[async_trait]
Expand All @@ -52,7 +52,10 @@ impl RecoveryChainOfResponsibility {
/// Create a new instance of the chain of responsibility
pub async fn new() -> Result<Self, CoffeeError> {
Ok(Self {
handlers: vec![Arc::new(GitRepositoryLocallyAbsentStrategy)],
handlers: vec![
Arc::new(CoffeeRepositoryDirCleanUpStrategy),
Arc::new(GitRepositoryLocallyAbsentStrategy),
],
})
}

Expand Down
Loading
Loading