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

Instantiate instance when adding through admin interface #1067

Merged
merged 37 commits into from
Mar 17, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
a0f4341
Start instance when adding through admin interface
maackle Feb 28, 2019
2c92d8f
Instantiate new instance when adding
maackle Feb 28, 2019
24510c6
Merge branch 'develop' into auto-start-instance
zippy Mar 1, 2019
9ad07fc
Merge branch 'develop' into auto-start-instance
zippy Mar 1, 2019
792e2b5
Merge branch 'develop' into auto-start-instance
zippy Mar 1, 2019
a5251a2
Merge branch 'develop' into auto-start-instance
Mar 1, 2019
0876202
Add extra integrity check
maackle Mar 2, 2019
8b28b9f
Use JSON content-type for wormhole request
maackle Mar 2, 2019
45e3d5c
More thorough duplicate ID check
maackle Mar 3, 2019
824793c
Merge remote-tracking branch 'origin/develop' into auto-start-instance
maackle Mar 6, 2019
0df19bd
Merge branch 'develop' into auto-start-instance
maackle Mar 7, 2019
d4af81b
Merge branch 'agent-state-snapshot-option' into auto-start-instance
maackle Mar 7, 2019
762b76d
Merge remote-tracking branch 'origin/develop' into auto-start-instance
maackle Mar 7, 2019
9129e54
AgentStateSnapshot::try_from -> ::from
maackle Mar 8, 2019
529db4a
Merge branch 'agent-state-snapshot-option' into auto-start-instance
maackle Mar 8, 2019
6900808
Better error
maackle Mar 8, 2019
a6c70f3
Add sleep as last resort
maackle Mar 8, 2019
6949787
Merge branch 'develop' into auto-start-instance
Mar 12, 2019
bda4519
In get_entry_with_meta, treat entry without CRUD status as non-existant
lucksus Mar 12, 2019
490c30f
Merge remote-tracking branch 'origin/auto-start-instance' into auto-s…
lucksus Mar 12, 2019
7a4e2f9
Merge branch 'develop' into auto-start-instance
Mar 12, 2019
3aa542d
rustfmt
lucksus Mar 12, 2019
b424f59
Make instance name unique in conductor test
lucksus Mar 12, 2019
e5eb26c
Remove unnecessary sleep in test
lucksus Mar 12, 2019
f5658a2
Merge branch 'develop' into auto-start-instance
zippy Mar 13, 2019
d14190a
Remove unnecessary sleep in test
lucksus Mar 13, 2019
54fc544
Make sure storage directory is clean in failing test
lucksus Mar 13, 2019
3149a10
Merge remote-tracking branch 'origin/auto-start-instance' into auto-s…
lucksus Mar 13, 2019
1557c21
Merge branch 'develop' into auto-start-instance
Mar 13, 2019
137feee
Make sure storage directory is clean in failing test
lucksus Mar 13, 2019
722f94a
Ignore errors when cleaning test storage
lucksus Mar 13, 2019
6e38f0c
Make test_add_instance_to_interface match test_add_instance by adding…
lucksus Mar 13, 2019
81dfb09
Testing to see if not starting interfaces fixes failing windows test
lucksus Mar 13, 2019
8b7ca6a
skip problem test on windows
willemolding Mar 14, 2019
55dbecf
Merge branch 'develop' into auto-start-instance
Connoropolous Mar 14, 2019
bb05e5a
Update admin.rs
willemolding Mar 16, 2019
3625b86
Merge branch 'develop' into auto-start-instance
thedavidmeister Mar 17, 2019
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
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(
willemolding marked this conversation as resolved.
Show resolved Hide resolved
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