Skip to content
This repository has been archived by the owner on Feb 3, 2023. It is now read-only.

Commit

Permalink
Merge pull request #1067 from holochain/auto-start-instance
Browse files Browse the repository at this point in the history
Instantiate instance when adding through admin interface
  • Loading branch information
thedavidmeister authored Mar 17, 2019
2 parents ca7adc6 + 3625b86 commit a762d23
Show file tree
Hide file tree
Showing 9 changed files with 140 additions and 52 deletions.
99 changes: 70 additions & 29 deletions conductor_api/src/conductor/admin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,11 @@ use holochain_core_types::{
cas::content::AddressableContent, error::HolochainError, hash::HashString,
};
use json_patch;
use std::{fs, path::PathBuf, sync::Arc};
use std::{
fs,
path::PathBuf,
sync::{Arc, RwLock},
};

pub trait ConductorAdmin {
fn install_dna_from_file(
Expand Down Expand Up @@ -174,7 +178,7 @@ impl ConductorAdmin for Conductor {
let mut new_config = self.config.clone();
let storage_path = self.instance_storage_dir_path().join(id.clone());
fs::create_dir_all(&storage_path)?;
let new_instance = InstanceConfiguration {
let new_instance_config = InstanceConfiguration {
id: id.to_string(),
dna: dna_id.to_string(),
agent: agent_id.to_string(),
Expand All @@ -187,8 +191,11 @@ impl ConductorAdmin for Conductor {
.into(),
},
};
new_config.instances.push(new_instance);
new_config.instances.push(new_instance_config);
new_config.check_consistency()?;
let instance = self.instantiate_from_config(id, &new_config, None)?;
self.instances
.insert(id.clone(), Arc::new(RwLock::new(instance)));
self.config = new_config;
self.save_config()?;
Ok(())
Expand Down Expand Up @@ -512,7 +519,12 @@ pub mod tests {
};
use holochain_common::paths::DNA_EXTENSION;
use holochain_core_types::{agent::AgentId, dna::Dna, json::JsonString};
use std::{convert::TryFrom, env::current_dir, fs::File, io::Read};
use std::{
convert::TryFrom,
env::current_dir,
fs::{remove_dir_all, File},
io::Read,
};

pub fn test_dna_loader() -> DnaLoader {
let loader = Box::new(|_: &PathBuf| {
Expand Down Expand Up @@ -899,6 +911,16 @@ id = 'new-dna'"#,
let test_name = "test_add_instance";
let mut conductor = create_test_conductor(test_name, 3001);

let storage_path = current_dir()
.expect("Could not get current dir")
.join("tmp-test")
.join(test_name)
.join("storage")
.join("new-instance");

// Make sure storage is clean
let _ = remove_dir_all(storage_path.clone());

let mut new_dna_path = PathBuf::new();
new_dna_path.push("new-dna.dna.json");
conductor
Expand Down Expand Up @@ -950,13 +972,6 @@ id = 'new-instance'"#,
),
);

let storage_path = current_dir()
.expect("Could not get current dir")
.join("tmp-test")
.join(test_name)
.join("storage")
.join("new-instance");

let storage_path_string = storage_path.to_str().unwrap().to_owned();
toml = add_block(
toml,
Expand Down Expand Up @@ -1182,28 +1197,52 @@ type = 'http'"#,
}

#[test]
#[cfg(any(not(windows), feature = "broken-tests"))]
fn test_add_instance_to_interface() {
let test_name = "test_add_instance_to_interface";
let mut conductor = create_test_conductor(test_name, 3007);

conductor.start_all_interfaces();
assert!(conductor
.interface_threads
.get("websocket interface")
.is_some());
let storage_path = current_dir()
.expect("Could not get current dir")
.join("tmp-test")
.join(test_name)
.join("storage")
.join("new-instance-2");

// Make sure storage is clean
let _ = remove_dir_all(storage_path.clone());

//conductor.start_all_interfaces();
//assert!(conductor
// .interface_threads
// .get("websocket interface")
// .is_some());

let mut new_dna_path = PathBuf::new();
new_dna_path.push("new-dna.dna.json");
conductor
.install_dna_from_file(
new_dna_path.clone(),
String::from("new-dna"),
false,
None,
None,
)
.expect("Could not install DNA");

assert_eq!(
conductor.add_instance(
&String::from("new-instance"),
&String::from("test-dna"),
&String::from("new-instance-2"),
&String::from("new-dna"),
&String::from("test-agent-1")
),
Ok(())
);

assert_eq!(
conductor.add_instance_to_interface(
&String::from("websocket interface"),
&String::from("new-instance")
&String::from("new-instance-2")
),
Ok(())
);
Expand All @@ -1218,25 +1257,27 @@ type = 'http'"#,
toml = add_block(toml, agent1());
toml = add_block(toml, agent2());
toml = add_block(toml, dna());
toml = add_block(
toml,
String::from(
r#"[[dnas]]
file = 'new-dna.dna.json'
hash = 'QmQVLgFxUpd1ExVkBzvwASshpG6fmaJGxDEgf1cFf7S73a'
id = 'new-dna'"#,
),
);
toml = add_block(toml, instance1());
toml = add_block(toml, instance2());
toml = add_block(
toml,
String::from(
r#"[[instances]]
agent = 'test-agent-1'
dna = 'test-dna'
id = 'new-instance'"#,
dna = 'new-dna'
id = 'new-instance-2'"#,
),
);

let storage_path = current_dir()
.expect("Could not get current dir")
.join("tmp-test")
.join(test_name)
.join("storage")
.join("new-instance");

let storage_path_string = storage_path.to_str().unwrap().to_owned();
toml = add_block(
toml,
Expand All @@ -1259,7 +1300,7 @@ id = 'test-instance-1'
id = 'test-instance-2'
[[interfaces.instances]]
id = 'new-instance'
id = 'new-instance-2'
[interfaces.driver]
port = 3007
Expand Down
4 changes: 2 additions & 2 deletions conductor_api/src/conductor/base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -895,7 +895,7 @@ pub mod tests {
type = "memory"
[[interfaces]]
id = "test-interface"
id = "test-interface-1"
admin = true
[interfaces.driver]
type = "websocket"
Expand All @@ -906,7 +906,7 @@ pub mod tests {
id = "test-instance-2"
[[interfaces]]
id = "test-interface"
id = "test-interface-2"
[interfaces.driver]
type = "http"
port = 4000
Expand Down
39 changes: 37 additions & 2 deletions conductor_api/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,14 @@ use holochain_core_types::{
};
use petgraph::{algo::toposort, graph::DiGraph, prelude::NodeIndex};
use serde::Deserialize;
use std::{collections::HashMap, convert::TryFrom, env, fs::File, io::prelude::*, path::PathBuf};
use std::{
collections::{HashMap, HashSet},
convert::TryFrom,
env,
fs::File,
io::prelude::*,
path::PathBuf,
};
use toml;

/// Main conductor configuration struct
Expand Down Expand Up @@ -97,10 +104,38 @@ impl Default for LoggerConfiguration {
}
}

/// Check for duplicate items in a list of strings
fn detect_dupes<'a, I: Iterator<Item = &'a String>>(
name: &'static str,
items: I,
) -> Result<(), String> {
let mut set = HashSet::<&str>::new();
let mut dupes = Vec::<String>::new();
for item in items {
if !set.insert(item) {
dupes.push(item.to_string())
}
}
if !dupes.is_empty() {
Err(format!(
"Duplicate {} IDs detected: {}",
name,
dupes.join(", ")
))
} else {
Ok(())
}
}

impl Configuration {
/// This function basically checks if self is a semantically valid configuration.
/// This mainly means checking for consistency between config structs that reference others.
pub fn check_consistency(&self) -> Result<(), String> {
pub fn check_consistency<'a>(&'a self) -> Result<(), String> {
detect_dupes("agent", self.agents.iter().map(|c| &c.id))?;
detect_dupes("dna", self.dnas.iter().map(|c| &c.id))?;
detect_dupes("instance", self.instances.iter().map(|c| &c.id))?;
detect_dupes("interface", self.interfaces.iter().map(|c| &c.id))?;

for ref instance in self.instances.iter() {
self.agent_by_id(&instance.agent).is_some().ok_or_else(|| {
format!(
Expand Down
6 changes: 6 additions & 0 deletions conductor_api/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,12 @@ impl From<HolochainError> for HolochainInstanceError {
}
}

impl From<HolochainInstanceError> for HolochainError {
fn from(error: HolochainInstanceError) -> Self {
HolochainError::new(&error.to_string())
}
}

impl From<NoneError> for HolochainInstanceError {
fn from(_: NoneError) -> Self {
HolochainInstanceError::NoSuchInstance
Expand Down
8 changes: 4 additions & 4 deletions conductor_api/src/holo_signing_service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,23 +9,23 @@ pub fn request_signing_service(
signing_service_uri: &String,
) -> Result<String, HolochainError> {
let body_json = json!({"agent_id": agent_id.address(), "payload": payload});
let body = serde_json::to_string(&body_json).unwrap();
let client = reqwest::Client::new();
let url = reqwest::Url::parse(signing_service_uri).map_err(|_| {
HolochainError::ConfigError(format!(
"Can't parse signing service URI: '{}'",
signing_service_uri
))
})?;
let mut response = client.post(url).body(body).send().map_err(|e| {
// NB: .json sets content-type: application/json
let mut response = client.post(url).json(&body_json).send().map_err(|e| {
HolochainError::ErrorGeneric(format!("Error during signing request: {:?}", e))
})?;
response
.status()
.is_success()
.ok_or(HolochainError::new(&format!(
"Status of response from signing service is not success, but: {:?}",
response.status()
"Status of response from signing service is not success: {:#?}",
response
)))?;
response
.text()
Expand Down
6 changes: 5 additions & 1 deletion conductor_api/src/holochain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,11 @@ impl Holochain {

pub fn load(context: Arc<Context>) -> Result<Self, HolochainError> {
let persister = SimplePersister::new(context.dht_storage.clone());
let loaded_state = persister.load(context.clone())??;
let loaded_state = persister
.load(context.clone())?
.ok_or(HolochainError::ErrorGeneric(
"State could not be loaded due to NoneError".to_string(),
))?;
let mut instance = Instance::from_state(loaded_state.clone());
let new_context = instance.initialize(None, context.clone())?;
Ok(Holochain {
Expand Down
8 changes: 3 additions & 5 deletions core/src/agent/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -123,13 +123,11 @@ impl AgentStateSnapshot {
}
}

impl TryFrom<&State> for AgentStateSnapshot {
type Error = HolochainError;

fn try_from(state: &State) -> Result<Self, Self::Error> {
impl From<&State> for AgentStateSnapshot {
fn from(state: &State) -> Self {
let agent = &*(state.agent());
let top_chain = agent.top_chain_header();
Ok(AgentStateSnapshot::new(top_chain))
AgentStateSnapshot::new(top_chain)
}
}

Expand Down
15 changes: 11 additions & 4 deletions core/src/nucleus/actions/get_entry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ pub(crate) fn get_entry_from_dht(

pub(crate) fn get_entry_crud_meta_from_dht(
context: &Arc<Context>,
address: Address,
address: &Address,
) -> Result<Option<(CrudStatus, Option<Address>)>, HolochainError> {
let dht = context.state().unwrap().dht().meta_storage();
let storage = &dht.clone();
Expand Down Expand Up @@ -116,7 +116,7 @@ pub(crate) fn get_entry_crud_meta_from_dht(
// Get crud-link
let mut maybe_link_update_delete = None;
let link_eavs = (*storage.read().unwrap()).fetch_eavi(&EaviQuery::new(
Some(address).into(),
Some(address.clone()).into(),
Some(Attribute::CrudLink).into(),
None.into(),
IndexFilter::LatestByAttribute,
Expand Down Expand Up @@ -148,8 +148,15 @@ pub fn get_entry_with_meta<'a>(
Ok(Some(entry)) => entry,
};
// 2. try to get the entry's metadata
let (crud_status, maybe_link_update_delete) = get_entry_crud_meta_from_dht(context, address)?
.expect("Entry should have crud-status metadata");
let (crud_status, maybe_link_update_delete) =
match get_entry_crud_meta_from_dht(context, &address)? {
Some(crud_info) => crud_info,
None => return Ok(None), //If we cannot get the CRUD status for above entry it is not an
//entry that is held by this DHT. It might be in the DHT CAS
//because DHT and chain share the same CAS or it maybe just got
//added by a concurrent process but the CRUD status is still about
//to get set. Either way, we should treat it as not existent (yet).
};
let item = EntryWithMeta {
entry,
crud_status,
Expand Down
7 changes: 2 additions & 5 deletions core/src/persister.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,7 @@ use holochain_core_types::{
},
error::HolochainError,
};
use std::{
convert::TryFrom,
sync::{Arc, RwLock},
};
use std::sync::{Arc, RwLock};

/// trait that defines the persistence functionality that holochain_core requires
pub trait Persister: Send {
Expand Down Expand Up @@ -43,7 +40,7 @@ impl Persister for SimplePersister {
let mut store = lock
.try_write()
.map_err(|_| HolochainError::new("Could not get write lock on storage"))?;
let agent_snapshot = AgentStateSnapshot::try_from(state)?;
let agent_snapshot = AgentStateSnapshot::from(state);
let nucleus_snapshot = NucleusStateSnapshot::from(state);
store.add(&agent_snapshot)?;
store.add(&nucleus_snapshot)?;
Expand Down

0 comments on commit a762d23

Please sign in to comment.