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

Container Admin Functions (DNA/instance management) #840

Merged
merged 69 commits into from
Jan 17, 2019

Conversation

lucksus
Copy link
Collaborator

@lucksus lucksus commented Jan 9, 2019

Everything in grey is part of this PR:
screenshot 2019-01-12 at 01 40 20

Added container API functions

  • admin/dna/install_from_file({id, path})
  • admin/dna/uninstall({id})
  • admin/dna/list()
  • admin/instance/add({id, dna_id, agent_id})
  • admin/instance/remove({id})
  • admin/instance/start({id})
  • admin/instance/stop({id})
  • admin/instance/list()
  • admin/instance/running()

Which get added to an interface if admin = true.

Prework / Refactoring

  • Had to make the container live in a static place so the interface threads can get a mutable reference for calling admin functions. Note though that I left this lazy_static! CONTAINER reference as something optional to use so tests and holochain_nodejs can own a Container instance and don't run into problems as @maackle pointed out..

  • Interfaces can now be shutdown which happens now in Container::shutdown() but not (yet !) between admin calls. We will need that to have changes (like new installed DNA) be reflected in the registered JSON RPC functions but I've left this as a follow-up since this PR starts to get big..

Follow-ups

  1. The big thing that is missing from this is updating interfaces after changes to the config. I almost added that to this PR already but want to think about a good solution first though. Easy catch would be to completely reload the config after any change but I think we can be more intelligent than that. I currently favor adding functions for handling interfaces (which we need anyway - like adding/removing interfaces and adding/removing instances from/to interfaces) and include a restart/update function there. Note that this would transfer responsibility for deciding when to update which interface over to the UI, i.e. HCadmin. (@zo-el, @JettTech)

  2. File/storage management. Currently the path to the DNA file to be installed is left as it is and added to the config. I would like to have admin/dna/install_from_file copy the the DNA file into a managed directory (~/.holochain/dnas/, or that is /dnas). Next to that we could then also configure instances to have their storage in ~/.holochain/instances//storage (or /instance//storage) - currently the storage is hard-coded to memory (!).

@lucksus lucksus added the review label Jan 9, 2019
@maackle
Copy link
Member

maackle commented Jan 10, 2019

I'm concerned about introducing a singleton for this. Singletons and tests do not mix well. As long as we can keep this singleton completely out of tests, it should be OK. But the moment we want two Containers in one process, we are screwed.

I think the way to do this without a singleton would be to build the IoHandler and the Container at the same time. ContainerApiBuilder might become ContainerBuilder. The ContainerBuilder would build the Container and return it as an Arc<Mutex<Container>>, then clone that Arc and move each IoHandler closure that needs it, to construct the API. Then the Container becomes tightly coupled to its API, and you can only interact with the Container through the mutex, but you can have multiple containers in multiple mutexes.

@willemolding
Copy link
Collaborator

Making this visible, from Lucksus:

"admin/dna/install_from_file should work and I tried to confirm by actually using hc-web-client to connect to an admin interface (used container/example-configs/basic.toml for that) but I get "Method not found" for the admin function and also for info/instances which is strange. Tried to debug a bit but couldn't find the reason yet."

@willemolding
Copy link
Collaborator

In response to the above I can call info/instances and admin/dna/install_from_file using cURL with no problems. To recreate

holochain_container -c ./example-config/empty-container.toml

and

curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","id":"0","method":"info/instances"}' http://localhost:4000

curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","id":"0","method":"admin/dna/install_from_file"}' http://localhost:4000

@willemolding
Copy link
Collaborator

willemolding commented Jan 11, 2019

admin/dna/install is now working great. I started on admin/instance/start but I've come up against an issue. It is possible to add a new instance to the config and get the container to start the instance but it also needs to rebuild its rpc interface to allow calls to the new instance. I am not sure of the best way to go about that yet.

@lucksus
Copy link
Collaborator Author

lucksus commented Jan 11, 2019

@willemolding, yeah, after the config was changed, the container actually needs to reload the config in a proper way.. I was anticipating that.

/// To be consistent with holochain function calls the expected output of the admin calls
/// is stringified JSON. This performs that stringification and then wraps it in the format expected
/// by the jsonrpc add_method closure
fn format_response(response: &serde_json::Value) -> jsonrpc_core::Result<serde_json::Value> {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added this to produce the json string format that is required

let id = Self::get_as_string("id", &params_map)?;
let path = Self::get_as_string("path", &params_map)?;
container_call!(|c| c.install_dna_from_file(PathBuf::from(path), id.to_string()))?;
Self::format_response(&json!({"success": true}))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This produces a result as expected by hc-web-client/hc-redux-middleware and consistent with the other container functions.

Copy link
Member

@zippy zippy left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This all looks very good as an important initial step. I approve of it, but we need to keep track of the followups that come from it. The big one is the singleton issue. The small one is a quick decision on using https://docs.rs/directories/1.0.2/directories/struct.ProjectDirs.html for getting our project directory, and then implementing it sooner rather than later in a way that all parts of the code that want to put things there can make use of. I will put this in the tre...

file = "example-config/app_spec.hcpkg"
hash = "Qm328wyq38924y"
id = "app spec rust"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's not a big point but I think the name "app spec rust" is probably a little confusing here in the container crate, and we should just use something like "test-dna-instance-1" or something like that. Or even, as I read the example more, "instance-for-http-testing" and "intance-for-websocket-testing" because it looks like they are hooked up to different interfaces, and that would be semantically clearer.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

.collect();
interface
})
.collect();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice. I like retain!

container_api/src/container.rs Outdated Show resolved Hide resolved
@@ -73,10 +103,15 @@ impl Container {
/// Creates a new instance with the default DnaLoader that actually loads files.
pub fn from_config(config: Configuration) -> Self {
let rules = config.logger.rules.clone();
let config_path = dirs::home_dir()
.expect("No home dir defined. Don't know where to store config file")
.join(std::path::PathBuf::from(".holochain/container-config.toml"));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we probably should start using the directories crate project dir for storing these things. But I'll do that in a separate PR.

@@ -638,6 +708,29 @@ pub mod tests {
container.stop_all_instances().unwrap();
}

//#[test]
// Default config path ~/.holochain/container-config.toml won't work in CI
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

but it will if we use the directories crate...


[logger]
type = ""
[[logger.rules.rules]]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmm. I don't like that the default logger rules is being populated here when the default type isn't "debug" because the rules don't even apply if the type isn't "debug".. but this is a problem with the logger, not with this code...

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This needs to be here because the defaults get added when saving the config. So we need to add it to the fixture. I will post my 2nd PR on ContainerAdmin in a few minutes that includes a DRY out of these fixtures..

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@zippy @lucksus In my scenario-tests branch I make "debug" the default logger_type because I assume that's what we want, would you agree?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah probably but this indicates that we need to think through different default conditions. For running scenario tests, and end user probably wants the default logging just to show debug/dna messages, and maybe err/* For the container, the default is likely no debug messages. For us as holochain core devs, the default is what it currently is: All messages.

@@ -212,4 +409,24 @@ pub mod tests {
assert!(result.contains(r#""happ-store/greeter/public/hello""#));
assert!(!result.contains(r#""test-instance-1//test/test""#));
}

/// The below test cannot be extented to test the other RPC methods due to the singleton design of the container
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm this speaks to the singleton problem, in that it's also an issue in our own container test code, not just the nodejs scenario tests....

# Conflicts:
#	container_api/src/error.rs
@willemolding
Copy link
Collaborator

Format for all admin calls is not unstringified json and all holochain calls return just a string. I am happy now and will update hc-web-client/hc-redux-middleware

@lucksus
Copy link
Collaborator Author

lucksus commented Jan 16, 2019

@maackle I've implemented the refactoring we talked about. Visibility of Container structs is now restricted to a sub-module:

pub struct Container {
    instances: InstanceMap,
    pub(in crate::container) instances: InstanceMap,
    config: Configuration,
    pub(in crate::container) config: Configuration,
    interface_threads: HashMap<String, InterfaceThreadHandle>,
    pub(in crate::container) config_path: PathBuf,
    dna_loader: DnaLoader,
    interface_threads: HashMap<String, Sender<()>>,
    pub(in crate::container) dna_loader: DnaLoader,

Please go ahead and merge if you approve :)

@maackle
Copy link
Member

maackle commented Jan 16, 2019

That's kind of a cool pattern, module-level pub. As long as the module doesn't get huge.

pub fn shutdown(&mut self) {
let _ = self
.stop_all_instances()
.map_err(|error| notify(format!("Error during shutdown: {}", error)));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why did we change the this from returning a result? stop_all_instances() seems to be mapping an error

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because if one instance errors during shutdown this function would exit early and not reset self.instances.

Logically, shutdown should always work. If an instance complains when being shutdown, it should only show in the log.

@@ -246,8 +300,7 @@ impl Container {
}

let config = self.config.clone();
self.shutdown().map_err(|e| e.to_string())?;
self.instances = HashMap::new();
self.shutdown();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

shutdown could propagate the error up here

@lucksus lucksus merged commit 8a6644f into develop Jan 17, 2019
@lucksus lucksus removed the review label Jan 17, 2019
maackle pushed a commit that referenced this pull request Jan 17, 2019
Container Admin Functions (DNA/instance management)
@zippy zippy deleted the container-admin-functions branch July 4, 2019 19:58
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants