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

Fix: handle already installed cells #209

Open
wants to merge 13 commits into
base: feature-add-agent-pref-endpoint
Choose a base branch
from
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 5 additions & 4 deletions crates/configure-holochain/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,18 @@ edition = "2021"

[dependencies]
anyhow = "1.0"
serde = { workspace = true }
url = "2.2"
serde_json = "1.0"
tokio = { version = "1", features = ["full"] }
tracing = { version = "0.1", features = ["attributes"] }
tracing-subscriber = "0.2"
url = "2.2"
holo_happ_manager = { version = "0.1.0", path = "../holo_happ_manager" }
hpos_hc_connect = { path = "../hpos_connect_hc" }
holo_happ_manager = { version = "0.1.0", path = "../holo_happ_manager" }
serde = { workspace = true }
reqwest = { workspace = true }
hpos-config-core = { workspace = true }
holochain_types = { workspace = true }
reqwest = { workspace = true }
holochain_conductor_api = { workspace = true }

[dev-dependencies]
test-case = "2.2.2"
Expand Down
86 changes: 83 additions & 3 deletions crates/configure-holochain/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
use anyhow::{Context, Result};
use holochain_conductor_api::CellInfo;
pub use holochain_types::prelude::CellId;
pub use hpos_hc_connect::AdminWebsocket;
pub use hpos_hc_connect::{
holo_config::{Config, Happ, HappsFile, MembraneProofFile, ProofPayload},
Expand All @@ -7,7 +9,7 @@ pub use hpos_hc_connect::{
use hpos_hc_connect::{hpos_agent::Agent, hpos_membrane_proof};
use std::collections::HashMap;
use std::sync::Arc;
use tracing::{debug, info, instrument, warn};
use tracing::{debug, info, instrument, trace, warn};

mod utils;

Expand Down Expand Up @@ -55,7 +57,7 @@ pub async fn install_happs(happ_file: &HappsFile, config: &Config) -> Result<()>
admin_websocket
.list_enabled_apps()
.await
.context("failed to get installed hApps")?,
.context("failed to get active hApps")?,
);

let happs_to_install: Vec<&Happ> = happ_file
Expand All @@ -74,12 +76,90 @@ pub async fn install_happs(happ_file: &HappsFile, config: &Config) -> Result<()>
.await?;

if let Err(err) = admin_websocket
.install_and_activate_app(happ, Some(mem_proof_vec), agent.clone(), HashMap::new())
.install_and_activate_app(
happ,
Some(mem_proof_vec.clone()),
agent.clone(),
HashMap::new(),
)
.await
{
if err.to_string().contains("AppAlreadyInstalled") {
info!("app {} was previously installed, re-activating", &happ.id());
admin_websocket.activate_app(happ).await?;
} else if err.to_string().contains("CellAlreadyExists")
&& (happ.id().contains("core-app")
|| happ.id().contains("holofuel")
|| happ.id().contains("servicelogger"))
{
// TODO: Revisit this exception with team - are there any blindspots of making this exception?
// Note: We currently will only encounter the case of a happ's `installed-app-id` being updated alongside changes that *do not* cause a DNA integrity zome change
// for our core-app, holofuel, and root servicelogger instance as they are the only apps to use the version number of the happ in their id;
// whereas all other hosted happs are forced to have their installed app id be their hha happ id
// ...and currently there is no way to update ONLY the coordinator zome without creating a new happ.
// ^^^^ TODO: Make it possible to update ONLY the coordinator for hosted happs too / add to cloud console flow.
warn!("cells for app {} already exist", &happ.id());

// // TEMPORARY HACK:
// // Until we have an integrated holochain solution to only upating the conductor zome,
// // we simply will uninstall the app that shares the cells and re-attempt installation.
// // This is NOT a permenant solution as it does not allow for data to persist.
// info!(
// "de-activating prior app {} that uses shared cells",
// &happ.id()
// );
// admin_websocket
// .uninstall_app(<installed_app_id>, true)
// .await?;

debug!("Getting a list of all installed happs");
let installed_apps = Arc::new(
admin_websocket
.list_apps(None)
.await
.context("failed to get installed hApps")?,
);

let get_existing_cells = move |id: &str| -> HashMap<String, CellId> {
let mut cells = HashMap::new();
let core_installed_app = installed_apps
.iter()
.find(|a| a.installed_app_id.contains(id));

if let Some(app_info) = core_installed_app {
for cell in &app_info.cell_info {
let cell_role_name = cell.0.clone();
let maybe_cell_info = cell
.1
.iter()
.find(|i| matches!(i, CellInfo::Provisioned(_)));

if let Some(CellInfo::Provisioned(cell)) = maybe_cell_info {
cells.insert(cell_role_name, cell.cell_id.clone());
}
}
};
cells
};

let existing_cells = if happ.id().contains("core-app") {
get_existing_cells("core-app")
} else if happ.id().contains("holofuel") {
get_existing_cells("holofuel")
} else {
get_existing_cells("servicelogger")
};

trace!("App has existing_cells : {:#?}", existing_cells);

return admin_websocket
.install_and_activate_app(
happ,
Some(mem_proof_vec),
agent.clone(),
existing_cells,
)
.await;
} else {
return Err(err);
}
Expand Down
2 changes: 1 addition & 1 deletion crates/core_app_cli/src/actions/get_happ_details.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ pub async fn get(happ_id: String) -> Result<()> {
.await?;

println!("===================");
println!("Happ Details {:?}", happ);
println!("Happ Details {:#?}", happ);
println!("===================");

Ok(())
Expand Down
2 changes: 1 addition & 1 deletion crates/core_app_cli/src/actions/list_all_happs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ pub async fn get() -> Result<()> {

println!("===================");
println!("All Holo Happs: ");
println!("{:?}", happs);
println!("{:#?}", happs);
println!("===================");

Ok(())
Expand Down
2 changes: 1 addition & 1 deletion crates/core_app_cli/src/actions/register_happ.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ pub async fn get(

println!("===================");
println!("Your Published Happ Bundle is: ");
println!("{:?}", published_happ);
println!("{:#?}", published_happ);
println!("===================");

Ok(())
Expand Down
13 changes: 7 additions & 6 deletions crates/core_app_cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,14 @@ pub enum Opt {
/// Register happ
#[structopt(name = "register")]
RegisterHapp {
#[structopt(name = "hosted-urls")]
name: String,
#[structopt(name = "domains")]
hosted_urls: Vec<String>,
#[structopt(name = "bundle-url")]
bundle_url: String,
name: String,
#[structopt(name = "special-uid")]
uid: Option<String>,
#[structopt(name = "special-id")]
special_id: Option<String>,
uid: Option<String>,
},
/// List all happs published by me
#[structopt(name = "my-happs")]
Expand Down Expand Up @@ -83,11 +84,11 @@ impl Opt {
Opt::AllHapps => core_app_cli::list_all_happs::get().await?,
Opt::HappDetails { happ_id } => core_app_cli::get_happ_details::get(happ_id).await?,
Opt::RegisterHapp {
name,
hosted_urls,
bundle_url,
name,
uid,
special_id,
uid,
} => {
core_app_cli::register_happ::get(
hosted_urls,
Expand Down
8 changes: 7 additions & 1 deletion crates/hpos_connect_hc/src/admin_ws.rs
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,13 @@ impl AdminWebsocket {
agent: Agent,
existing_cells: HashMap<String, CellId>,
) -> Result<()> {
let source = app.source().await?;
let existing_cell_map = if existing_cells.is_empty() {
None
} else {
Some(&existing_cells)
};
let source = app.source(existing_cell_map).await?;
trace!("App has source : {:#?}", source);

let agent_key = if let Some(admin) = &app.agent_override_details().await? {
admin.key.clone()
Expand Down
22 changes: 19 additions & 3 deletions crates/hpos_connect_hc/src/holo_config.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
use super::hpos_agent::{read_hpos_config, Admin};
use anyhow::{anyhow, Context, Result};
use holochain_types::prelude::{AgentPubKey, AppBundleSource};
use holochain_types::prelude::{AgentPubKey, AppBundleSource, CellId, CellProvisioning};
use holochain_types::{app::AppManifest, prelude::YamlProperties};
use lair_keystore_api::{
dependencies::{serde_yaml, url::Url},
prelude::LairServerConfigInner,
};
use serde::Deserialize;
use std::collections::HashMap;
use std::env;
use std::path::{Path, PathBuf};
use structopt::StructOpt;
Expand Down Expand Up @@ -165,7 +166,10 @@ impl Happ {
}
}
// get the source of the happ by retrieving the happ and updating the properties if any
pub async fn source(&self) -> Result<AppBundleSource> {
pub async fn source(
&self,
maybe_exisiting_cell_map: Option<&HashMap<String, CellId>>,
) -> Result<AppBundleSource> {
let path = self.download().await?;
let mut source = AppBundleSource::Path(path);
if self.dnas.is_some() {
Expand All @@ -186,7 +190,18 @@ impl Happ {
properties =
Some(YamlProperties::new(serde_yaml::from_str(&prop).unwrap()));
}
role_manifest.dna.modifiers.properties = properties
role_manifest.dna.modifiers.properties = properties;

if let Some(exisiting_cell_map) = maybe_exisiting_cell_map {
role_manifest.provisioning =
Some(CellProvisioning::UseExisting { protected: false });

if let Some(cell) = exisiting_cell_map.get(&role_manifest.name) {
tracing::trace!("Adding installed hash from cell : {:#?}", cell);
role_manifest.dna.installed_hash =
Some(cell.dna_hash().to_owned().into());
};
}
}
}
source = AppBundleSource::Bundle(
Expand All @@ -199,6 +214,7 @@ impl Happ {
}
Ok(source)
}

// returns pub key is agent override exists
pub async fn agent_override_details(&self) -> Result<Option<Admin>> {
if let Some(agent_bundle_override) = &self.agent_bundle_override {
Expand Down