From e41504f0842ca7512adbcc02328e548c54b463e2 Mon Sep 17 00:00:00 2001 From: Prithvi Shahi Date: Sun, 21 Jun 2020 13:52:35 -0700 Subject: [PATCH 01/14] Avoid duplicate build output directories --- src/dfx/src/lib/canister_info.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/dfx/src/lib/canister_info.rs b/src/dfx/src/lib/canister_info.rs index 19b1bfb0c5..815036af78 100644 --- a/src/dfx/src/lib/canister_info.rs +++ b/src/dfx/src/lib/canister_info.rs @@ -61,11 +61,9 @@ impl CanisterInfo { let extras = canister_config.extras.clone(); let output_root = build_root.join(name); - // todo needs to be in child "canisters" dir of canister_root - // let temp_dir = env.get_temp_dir(); - let canisters_dir = canister_root.join("canisters"); - std::fs::create_dir_all(&canisters_dir)?; + let canisters_dir = build_root.clone(); + std::fs::create_dir_all(&canisters_dir)?; let manifest_path = canisters_dir.join("canister_manifest.json"); let canister_type = canister_config From f68d3159c1e36e2fe556db5ce2caa23718270bbe Mon Sep 17 00:00:00 2001 From: Prithvi Shahi Date: Sun, 21 Jun 2020 14:25:53 -0700 Subject: [PATCH 02/14] remove unused generate_id field --- src/dfx/src/commands/build.rs | 1 - src/dfx/src/lib/builders/mod.rs | 9 --------- 2 files changed, 10 deletions(-) diff --git a/src/dfx/src/commands/build.rs b/src/dfx/src/commands/build.rs index 92f19be854..4fe997602b 100644 --- a/src/dfx/src/commands/build.rs +++ b/src/dfx/src/commands/build.rs @@ -75,7 +75,6 @@ pub fn exec(env: &dyn Environment, args: &ArgMatches<'_>) -> DfxResult { // TODO: remove the forcing of generating canister id once we have an update flow. canister_pool.build_or_fail( BuildConfig::from_config(&config) - .with_generate_id(true) .with_skip_frontend(args.is_present("skip-frontend")) .with_skip_manifest(args.is_present("skip-manifest")), )?; diff --git a/src/dfx/src/lib/builders/mod.rs b/src/dfx/src/lib/builders/mod.rs index 2542c3573d..804305ce27 100644 --- a/src/dfx/src/lib/builders/mod.rs +++ b/src/dfx/src/lib/builders/mod.rs @@ -82,7 +82,6 @@ pub trait CanisterBuilder { #[derive(Clone)] pub struct BuildConfig { profile: Profile, - pub generate_id: bool, pub skip_frontend: bool, pub skip_manifest: bool, @@ -99,20 +98,12 @@ impl BuildConfig { BuildConfig { profile: config.profile.unwrap_or(Profile::Debug), - generate_id: false, skip_frontend: false, skip_manifest: false, idl_root: build_root.join("idl/"), } } - pub fn with_generate_id(self, generate_id: bool) -> Self { - Self { - generate_id, - ..self - } - } - pub fn with_skip_frontend(self, skip_frontend: bool) -> Self { Self { skip_frontend, From 5c2af5b8f063d0f65508e33e2ebf667243542c89 Mon Sep 17 00:00:00 2001 From: Prithvi Shahi Date: Sun, 21 Jun 2020 22:23:47 -0700 Subject: [PATCH 03/14] fail build if manifest does not exist --- src/dfx/src/commands/build.rs | 4 +- src/dfx/src/commands/canister/call.rs | 4 +- src/dfx/src/commands/canister/id.rs | 2 +- src/dfx/src/commands/canister/install.rs | 2 +- src/dfx/src/lib/canister_info.rs | 47 +++++++----- src/dfx/src/lib/error/build.rs | 7 ++ src/dfx/src/lib/models/canister.rs | 92 +++++++----------------- 7 files changed, 65 insertions(+), 93 deletions(-) diff --git a/src/dfx/src/commands/build.rs b/src/dfx/src/commands/build.rs index 4fe997602b..96fe70c3a8 100644 --- a/src/dfx/src/commands/build.rs +++ b/src/dfx/src/commands/build.rs @@ -61,9 +61,7 @@ pub fn exec(env: &dyn Environment, args: &ArgMatches<'_>) -> DfxResult { let canister_pool = CanisterPool::load(env)?; // Create canisters on the replica and associate canister ids locally. - if !args.is_present("skip-manifest") { - canister_pool.create_canisters(env)?; - } else { + if args.is_present("skip-manifest") { slog::warn!( env.get_logger(), "Skipping the build manifest. Canister IDs might be hard coded." diff --git a/src/dfx/src/commands/canister/call.rs b/src/dfx/src/commands/canister/call.rs index 8b830cd4fb..cd8dfe27b0 100644 --- a/src/dfx/src/commands/canister/call.rs +++ b/src/dfx/src/commands/canister/call.rs @@ -77,8 +77,8 @@ pub fn exec(env: &dyn Environment, args: &ArgMatches<'_>) -> DfxResult { Err(_) => { let canister_info = CanisterInfo::load(&config, canister_name)?; match canister_info.get_canister_id() { - Some(id) => (id, canister_info.get_output_idl_path()), - None => return Err(DfxError::InvalidArgument("canister_name".to_string())), + Ok(id) => (id, canister_info.get_output_idl_path()), + Err(_) => return Err(DfxError::InvalidArgument("canister_name".to_string())), } } }; diff --git a/src/dfx/src/commands/canister/id.rs b/src/dfx/src/commands/canister/id.rs index db9f9d4ecc..8036ad84ff 100644 --- a/src/dfx/src/commands/canister/id.rs +++ b/src/dfx/src/commands/canister/id.rs @@ -22,7 +22,7 @@ pub fn exec(env: &dyn Environment, args: &ArgMatches<'_>) -> DfxResult { .ok_or(DfxError::CommandMustBeRunInAProject)?; let canister_name = args.value_of("canister_name").unwrap(); let canister_info = CanisterInfo::load(&config, canister_name)?; - let canister_id = canister_info.get_canister_id().ok_or_else(|| { + let canister_id = canister_info.get_canister_id().map_err(|_| { DfxError::CannotFindBuildOutputForCanister(canister_info.get_name().to_owned()) })?; println!("{}", CanisterId::to_text(&canister_id)); diff --git a/src/dfx/src/commands/canister/install.rs b/src/dfx/src/commands/canister/install.rs index b4375a30f9..88dec9f107 100644 --- a/src/dfx/src/commands/canister/install.rs +++ b/src/dfx/src/commands/canister/install.rs @@ -61,7 +61,7 @@ async fn install_canister( mode: &str, ) -> DfxResult { let log = env.get_logger(); - let canister_id = canister_info.get_canister_id().ok_or_else(|| { + let canister_id = canister_info.get_canister_id().map_err(|_| { DfxError::CannotFindBuildOutputForCanister(canister_info.get_name().to_owned()) })?; diff --git a/src/dfx/src/lib/canister_info.rs b/src/dfx/src/lib/canister_info.rs index 815036af78..1d717b74d6 100644 --- a/src/dfx/src/lib/canister_info.rs +++ b/src/dfx/src/lib/canister_info.rs @@ -3,7 +3,7 @@ use crate::config::dfinity::Config; use crate::lib::canister_info::assets::AssetsCanisterInfo; use crate::lib::canister_info::custom::CustomCanisterInfo; use crate::lib::canister_info::motoko::MotokoCanisterInfo; -use crate::lib::error::{DfxError, DfxResult}; +use crate::lib::error::{BuildErrorKind, DfxError, DfxResult}; use crate::lib::models::canister::{CanManMetadata, CanisterManifest}; use ic_agent::CanisterId; use std::cell::RefCell; @@ -108,24 +108,33 @@ impl CanisterInfo { pub fn get_output_root(&self) -> &Path { &self.output_root } - pub fn get_canister_id(&self) -> Option { - if !self.get_manifest_path().exists() { - return Some(CanisterId::from_bytes(&[0, 1, 2, 3])); - } - - let file = std::fs::File::open(&self.get_manifest_path()).unwrap(); - let manifest: CanisterManifest = serde_json::from_reader(file).unwrap(); - let serde_value = &manifest.canisters[&self.name.clone()]; - let metadata: CanManMetadata = serde_json::from_value(serde_value.clone()).unwrap(); - - let canister_id = self - .canister_id - .replace(None) - .or_else(|| CanisterId::from_text(metadata.canister_id).ok()); - - self.canister_id.replace(canister_id.clone()); - - canister_id + pub fn get_canister_id(&self) -> DfxResult { + let canister_id = self.canister_id.replace(None); + let cid = match canister_id { + Some(canister_id) => { + self.canister_id.replace(Some(canister_id.clone())); + Some(canister_id) + } + None => { + let content = std::fs::read_to_string(&self.get_manifest_path()) + .map_err(|_| DfxError::BuildError(BuildErrorKind::NoManifestError()))?; + + let manifest: CanisterManifest = + serde_json::from_str(&content).map_err(DfxError::from)?; + let serde_value = &manifest.canisters[&self.name.clone()]; + let metadata: CanManMetadata = serde_json::from_value(serde_value.clone()).unwrap(); + + let canister_id = self + .canister_id + .replace(None) + .or_else(|| CanisterId::from_text(metadata.canister_id).ok()); + + self.canister_id.replace(canister_id.clone()); + + canister_id + } + }; + cid.ok_or_else(|| DfxError::Unknown(String::from("No canister id"))) } pub fn get_extra_value(&self, name: &str) -> Option { diff --git a/src/dfx/src/lib/error/build.rs b/src/dfx/src/lib/error/build.rs index 1fc595c75c..c00d1a5fdd 100644 --- a/src/dfx/src/lib/error/build.rs +++ b/src/dfx/src/lib/error/build.rs @@ -46,6 +46,9 @@ pub enum BuildErrorKind { /// A command line string was invalid. InvalidBuildCommand(String), + + /// The canister_manifest.json file does not exist. + NoManifestError(), } impl fmt::Display for BuildErrorKind { @@ -111,6 +114,10 @@ impl fmt::Display for BuildErrorKind { "Postbuild step failed for canister {} with error: {}", c, e )), + + NoManifestError() => f.write_fmt(format_args!( + "Failed to find canister manifest, please issue 'dfx canister create'." + )), } } } diff --git a/src/dfx/src/lib/models/canister.rs b/src/dfx/src/lib/models/canister.rs index 6532a15460..ca74a0171d 100644 --- a/src/dfx/src/lib/models/canister.rs +++ b/src/dfx/src/lib/models/canister.rs @@ -5,11 +5,11 @@ use crate::lib::builders::{ use crate::lib::canister_info::CanisterInfo; use crate::lib::environment::Environment; use crate::lib::error::{BuildErrorKind, DfxError, DfxResult}; -use crate::lib::waiter::create_waiter; use crate::util::assets; use chrono::Utc; -use ic_agent::CanisterId; +use ic_agent::{Blob, CanisterId}; use petgraph::graph::{DiGraph, NodeIndex}; +use rand::{thread_rng, RngCore}; use serde::{Deserialize, Serialize}; use serde_json::Map; use slog::Logger; @@ -18,7 +18,6 @@ use std::collections::BTreeMap; use std::io::Read; use std::path::{Path, PathBuf}; use std::sync::Arc; -use tokio::runtime::Runtime; /// Represents a canister from a DFX project. It can be a virtual Canister. /// Multiple canister instances can have the same info, but would be differentiated @@ -70,6 +69,14 @@ impl Canister { self.info.get_canister_id().unwrap() } + // this function is only ever used when build_config.skip_manifest is true + pub fn generate_and_set_canister_id(&self) -> DfxResult { + let mut rng = thread_rng(); + let mut v: Vec = std::iter::repeat(0u8).take(8).collect(); + rng.fill_bytes(v.as_mut_slice()); + self.info.set_canister_id(CanisterId::from(Blob(v))) + } + /// Get the build output of a build process. If the output isn't known at this time, /// will return [None]. pub fn get_build_output(&self) -> Option<&BuildOutput> { @@ -98,11 +105,6 @@ pub struct CanManMetadata { } impl CanisterManifest { - pub fn load(path: &Path) -> DfxResult { - let content = std::fs::read_to_string(path).map_err(DfxError::from)?; - serde_json::from_str(&content).map_err(DfxError::from) - } - pub fn save(&self, path: &Path) -> DfxResult<()> { let content = serde_json::to_string_pretty(self).map_err(DfxError::CouldNotSerializeConfiguration)?; @@ -160,57 +162,10 @@ impl CanisterPool { }) } - pub fn create_canisters(&self, env: &dyn Environment) -> DfxResult { - let agent = env - .get_agent() - .ok_or(DfxError::CommandMustBeRunInAProject)?; - let mut runtime = Runtime::new().expect("Unable to create a runtime"); - // check manifest first before getting new can id here - for canister in &self.canisters { - let waiter = create_waiter(); - let info = &canister.info; - - let manifest_path = info.get_manifest_path(); - // check if the canister_manifest.json file exists - if manifest_path.is_file() { - { - let mut manifest = CanisterManifest::load(info.get_manifest_path())?; - - match manifest.canisters.get(info.get_name()) { - Some(serde_value) => { - let metadata: CanManMetadata = - serde_json::from_value(serde_value.to_owned()).unwrap(); - CanisterId::from_text(metadata.canister_id).ok(); - } - None => { - let cid = runtime.block_on(agent.create_canister_and_wait(waiter))?; - info.set_canister_id(cid.clone())?; - manifest.add_entry(info, cid)?; - } - } - } - } else { - let cid = runtime.block_on(agent.create_canister_and_wait(waiter))?; - info.set_canister_id(cid.clone())?; - let mut manifest = CanisterManifest { - canisters: Map::new(), - }; - manifest.add_entry(info, cid)?; - } - slog::debug!( - self.logger, - " {} => {}", - canister.get_name(), - canister.canister_id().to_text() - ); - } - Ok(()) - } - pub fn get_canister(&self, canister_id: &CanisterId) -> Option<&Canister> { for c in &self.canisters { let info = &c.info; - if Some(canister_id) == info.get_canister_id().as_ref() { + if Some(canister_id) == info.get_canister_id().ok().as_ref() { return Some(c); } } @@ -280,11 +235,14 @@ impl CanisterPool { } } - fn step_prebuild_all( - &self, - _build_config: &BuildConfig, - _order: &mut Vec, - ) -> DfxResult<()> { + fn step_prebuild_all(&self, build_config: &BuildConfig) -> DfxResult<()> { + for canister in &self.canisters { + if build_config.skip_manifest { + canister.generate_and_set_canister_id()?; + } else if !canister.info.get_manifest_path().exists() { + return Err(DfxError::BuildError(BuildErrorKind::NoManifestError())); + } + } Ok(()) } @@ -377,7 +335,7 @@ impl CanisterPool { build_config: BuildConfig, ) -> DfxResult>> { let graph = self.build_dependencies_graph()?; - let mut order: Vec = petgraph::algo::toposort(&graph, None) + let order: Vec = petgraph::algo::toposort(&graph, None) .map_err(|cycle| match graph.node_weight(cycle.node_id()) { Some(canister_id) => DfxError::BuildError(BuildErrorKind::CircularDependency( match self.get_canister_info(canister_id) { @@ -394,11 +352,6 @@ impl CanisterPool { .map(|idx| graph.node_weight(*idx).unwrap().clone()) .collect(); - self.step_prebuild_all(&build_config, &mut order) - .map_err(|e| { - DfxError::BuildError(BuildErrorKind::PrebuildAllStepFailed(Box::new(e))) - })?; - let mut result = Vec::new(); for canister_id in &order { if let Some(canister) = self.get_canister(canister_id) { @@ -437,6 +390,11 @@ impl CanisterPool { /// Build all canisters, failing with the first that failed the build. Will return /// nothing if all succeeded. pub fn build_or_fail(&self, build_config: BuildConfig) -> DfxResult<()> { + // check for canister ids before building + self.step_prebuild_all(&build_config).map_err(|e| { + DfxError::BuildError(BuildErrorKind::PrebuildAllStepFailed(Box::new(e))) + })?; + let outputs = self.build(build_config)?; for output in outputs { From abe516aafce09f3c092fb16e37daed8e73832ee5 Mon Sep 17 00:00:00 2001 From: Prithvi Shahi Date: Sun, 21 Jun 2020 22:54:22 -0700 Subject: [PATCH 04/14] fail build if a canister id is missing --- src/dfx/src/lib/canister_info.rs | 6 +++++- src/dfx/src/lib/error/build.rs | 8 ++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/dfx/src/lib/canister_info.rs b/src/dfx/src/lib/canister_info.rs index 1d717b74d6..eb89247ce5 100644 --- a/src/dfx/src/lib/canister_info.rs +++ b/src/dfx/src/lib/canister_info.rs @@ -121,7 +121,11 @@ impl CanisterInfo { let manifest: CanisterManifest = serde_json::from_str(&content).map_err(DfxError::from)?; - let serde_value = &manifest.canisters[&self.name.clone()]; + + let serde_value = manifest.canisters.get(&self.name.clone()).ok_or_else(|| { + DfxError::BuildError(BuildErrorKind::CanisterIdNotFound(self.name.clone())) + })?; + let metadata: CanManMetadata = serde_json::from_value(serde_value.clone()).unwrap(); let canister_id = self diff --git a/src/dfx/src/lib/error/build.rs b/src/dfx/src/lib/error/build.rs index c00d1a5fdd..1d1400bb42 100644 --- a/src/dfx/src/lib/error/build.rs +++ b/src/dfx/src/lib/error/build.rs @@ -49,6 +49,9 @@ pub enum BuildErrorKind { /// The canister_manifest.json file does not exist. NoManifestError(), + + /// The canister id was not found in the canister_manifest.json file. + CanisterIdNotFound(String), } impl fmt::Display for BuildErrorKind { @@ -118,6 +121,11 @@ impl fmt::Display for BuildErrorKind { NoManifestError() => f.write_fmt(format_args!( "Failed to find canister manifest, please issue 'dfx canister create'." )), + + CanisterIdNotFound(name) => f.write_fmt(format_args!( + "Failed to find canister id for {}, please issue 'dfx canister create {}'.", + name, name + )), } } } From e96ca4cedc2c0baca7275d1b1fa23387db103239 Mon Sep 17 00:00:00 2001 From: Prithvi Shahi Date: Sun, 28 Jun 2020 12:25:58 -0700 Subject: [PATCH 05/14] update tests, add provider flag to create cmd, fix create help msg --- e2e/bats/assetscanister.bash | 1 + e2e/bats/base.bash | 2 ++ e2e/bats/basic-project.bash | 4 +++ e2e/bats/build.bash | 9 +++++++ e2e/bats/call.bash | 1 + e2e/bats/create.sh | 33 +++++++++++++++++++++++++ e2e/bats/frontend.bash | 2 ++ e2e/bats/id.bash | 1 + e2e/bats/identity.bash | 1 + e2e/bats/packtool.bash | 5 ++++ e2e/bats/print.bash | 1 + e2e/bats/usage.bash | 1 + src/dfx/src/commands/canister/create.rs | 30 +++++++++++++++++++--- src/dfx/src/lib/message.rs | 6 +++++ src/dfx/src/lib/models/canister.rs | 2 +- 15 files changed, 94 insertions(+), 5 deletions(-) create mode 100644 e2e/bats/create.sh diff --git a/e2e/bats/assetscanister.bash b/e2e/bats/assetscanister.bash index cf5256e156..7221c11de7 100644 --- a/e2e/bats/assetscanister.bash +++ b/e2e/bats/assetscanister.bash @@ -17,6 +17,7 @@ teardown() { install_asset assetscanister dfx_start + dfx canister create --all dfx build dfx canister install e2e_project_assets diff --git a/e2e/bats/base.bash b/e2e/bats/base.bash index d6e9812b61..4143532fa0 100644 --- a/e2e/bats/base.bash +++ b/e2e/bats/base.bash @@ -17,6 +17,7 @@ teardown() { install_asset base dfx_start + dfx canister create --all dfx build dfx canister install e2e_project @@ -32,6 +33,7 @@ teardown() { dfx config defaults/build/packtool "echo" dfx_start + dfx canister create --all assert_command_fail dfx build assert_match 'import error, package "base" not defined' } diff --git a/e2e/bats/basic-project.bash b/e2e/bats/basic-project.bash index 4309f6db9a..8d841ff638 100644 --- a/e2e/bats/basic-project.bash +++ b/e2e/bats/basic-project.bash @@ -17,6 +17,7 @@ teardown() { @test "build + install + call + request-status -- greet_mo" { install_asset greet dfx_start + dfx canister create --all dfx build INSTALL_REQUEST_ID=$(dfx canister install hello --async) dfx canister request-status $INSTALL_REQUEST_ID @@ -40,6 +41,7 @@ teardown() { @test "build + install + call + request-status -- counter_mo" { install_asset counter dfx_start + dfx canister create --all dfx build dfx canister install hello @@ -80,6 +82,7 @@ teardown() { @test "build + install + call -- counter_idl_mo" { install_asset counter_idl dfx_start + dfx canister create --all dfx build dfx canister install --all @@ -90,6 +93,7 @@ teardown() { @test "build + install + call -- matrix_multiply_mo" { install_asset matrix_multiply dfx_start + dfx canister create --all dfx build dfx canister install --all diff --git a/e2e/bats/build.bash b/e2e/bats/build.bash index afcec4a62d..f07bf802f7 100644 --- a/e2e/bats/build.bash +++ b/e2e/bats/build.bash @@ -17,6 +17,7 @@ teardown() { @test "build fails on invalid motoko" { install_asset invalid dfx_start + dfx canister create --all assert_command_fail dfx build assert_match "syntax error" } @@ -24,6 +25,7 @@ teardown() { @test "build supports relative imports" { install_asset import dfx_start + dfx canister create --all assert_command dfx build dfx canister install --all assert_command dfx canister call e2e_project greet World @@ -32,6 +34,7 @@ teardown() { @test "build succeeds on default project" { dfx_start + dfx canister create --all assert_command dfx build } @@ -39,6 +42,7 @@ teardown() { # Currently due to new canister ids, the wasm binary will be different for inter-canister calls. @test "build twice produces the same wasm binary" { dfx_start + dfx canister create --all assert_command dfx build cp canisters/e2e_project/main.wasm ./old.wasm assert_command dfx build @@ -47,6 +51,7 @@ teardown() { @test "build outputs the canister manifest" { dfx_start + dfx canister create --all assert_command dfx build [[ -f canisters/canister_manifest.json ]] } @@ -54,6 +59,7 @@ teardown() { @test "build outputs warning" { install_asset warning dfx_start + dfx canister create --all assert_command dfx build assert_match "warning, this pattern consuming type" } @@ -61,6 +67,7 @@ teardown() { @test "build fails on unknown imports" { install_asset import_error dfx_start + dfx canister create --all assert_command_fail dfx build assert_match 'import error, canister alias "random" not defined' } @@ -68,6 +75,7 @@ teardown() { @test "build fails if canister type is not supported" { dfx_start dfx config canisters.e2e_project.type unknown_canister_type + dfx canister create --all assert_command_fail dfx build assert_match "CouldNotFindBuilderForCanister" } @@ -75,6 +83,7 @@ teardown() { @test "can build a custom canister type" { dfx_start install_asset custom_canister + dfx canister create --all assert_command dfx build assert_match "CUSTOM_CANISTER_BUILD_DONE" diff --git a/e2e/bats/call.bash b/e2e/bats/call.bash index c9186c4e18..1005a44cc0 100644 --- a/e2e/bats/call.bash +++ b/e2e/bats/call.bash @@ -14,6 +14,7 @@ teardown() { @test "call subcommand accepts canister identifier as canister name" { install_asset greet dfx_start + dfx canister create --all dfx build dfx canister install hello assert_command dfx canister call $(dfx canister id hello) greet '("Names are difficult")' diff --git a/e2e/bats/create.sh b/e2e/bats/create.sh new file mode 100644 index 0000000000..ac1b09daac --- /dev/null +++ b/e2e/bats/create.sh @@ -0,0 +1,33 @@ +#!/usr/bin/env bats + +load utils/_ + +setup() { + # We want to work from a temporary directory, different for every test. + cd $(mktemp -d -t dfx-e2e-XXXXXXXX) + export RUST_BACKTRACE=1 + + dfx_new +} + +teardown() { + dfx_stop +} + +@test "create succeeds on default project" { + dfx_start + assert_command dfx canister create --all +} + +@test "build fails without create" { + dfx_start + assert_command_fail dfx build + assert_match 'Failed to find canister manifest' +} + +@test "build fails if all canisters in project are not created" { + dfx_start + assert_command dfx canister create e2e_project + assert_command_fail dfx build + assert_match 'Failed to find canister id for e2e_project_assets' +} \ No newline at end of file diff --git a/e2e/bats/frontend.bash b/e2e/bats/frontend.bash index 4f2add0270..4c529d6130 100644 --- a/e2e/bats/frontend.bash +++ b/e2e/bats/frontend.bash @@ -15,6 +15,7 @@ teardown() { @test "dfx start serves a frontend" { dfx_start + dfx canister create --all dfx build --skip-frontend sleep 1 @@ -26,6 +27,7 @@ teardown() { [ "$USE_IC_REF" ] && skip "dfx start cannot serve frontent when using ic-ref" dfx_start --host 127.0.0.1:12345 + dfx canister create --all --provider http://127.0.0.1:12345 dfx build --skip-frontend --provider http://127.0.0.1:12345 assert_command curl http://localhost:12345 # 8000 = default port. diff --git a/e2e/bats/id.bash b/e2e/bats/id.bash index 8473da67d6..0a02d595b0 100644 --- a/e2e/bats/id.bash +++ b/e2e/bats/id.bash @@ -14,6 +14,7 @@ teardown() { @test "id subcommand prints valid canister identifier" { install_asset id dfx_start + dfx canister create --all dfx build assert_command dfx canister id e2e_project assert_match $(cat canisters/canister_manifest.json | jq -r .canisters.e2e_project.canister_id) diff --git a/e2e/bats/identity.bash b/e2e/bats/identity.bash index e8a2c60cac..1d6aa0dec4 100644 --- a/e2e/bats/identity.bash +++ b/e2e/bats/identity.bash @@ -16,6 +16,7 @@ teardown() { @test "calls and query receive the same principal from dfx" { install_asset identity dfx_start + dfx canister create --all assert_command dfx build assert_command dfx canister install --all diff --git a/e2e/bats/packtool.bash b/e2e/bats/packtool.bash index 8085907a71..7e5cbd8256 100644 --- a/e2e/bats/packtool.bash +++ b/e2e/bats/packtool.bash @@ -17,6 +17,7 @@ teardown() { install_asset packtool dfx_start + dfx canister create --all assert_command_fail dfx build assert_match 'import error, package "(rate|describe)" not defined' } @@ -26,6 +27,7 @@ teardown() { source configure_packtool.bash dfx_start + dfx canister create --all dfx build } @@ -34,6 +36,7 @@ teardown() { source configure_packtool.bash dfx_start + dfx canister create --all dfx build dfx canister install e2e_project @@ -49,6 +52,7 @@ teardown() { dfx config defaults/build/packtool "./no-such-command that command cannot be invoked" dfx_start + dfx canister create --all assert_command_fail dfx build assert_match 'Failed to invoke the package tool' assert_match 'no-such-command.*that.*command.*cannot.*be.*invoked' @@ -60,6 +64,7 @@ teardown() { dfx config defaults/build/packtool "sh ./command-that-fails.bash" dfx_start + dfx canister create --all assert_command_fail dfx build assert_match 'Package tool.*reported an error' assert_match 'sh.*command-that-fails.bash' diff --git a/e2e/bats/print.bash b/e2e/bats/print.bash index 34d32a4b21..974897a6e4 100644 --- a/e2e/bats/print.bash +++ b/e2e/bats/print.bash @@ -18,6 +18,7 @@ teardown() { install_asset print dfx_start 2>stderr.txt + dfx canister create --all dfx build dfx canister install e2e_project dfx canister call e2e_project hello diff --git a/e2e/bats/usage.bash b/e2e/bats/usage.bash index 0237a95e32..0e7b6ff32e 100644 --- a/e2e/bats/usage.bash +++ b/e2e/bats/usage.bash @@ -30,5 +30,6 @@ teardown() { dfx new t --no-frontend cd t dfx_start + dfx canister create --all assert_command dfx build } diff --git a/src/dfx/src/commands/canister/create.rs b/src/dfx/src/commands/canister/create.rs index b1835cc264..abd4b219de 100644 --- a/src/dfx/src/commands/canister/create.rs +++ b/src/dfx/src/commands/canister/create.rs @@ -1,5 +1,5 @@ use crate::lib::canister_info::CanisterInfo; -use crate::lib::environment::Environment; +use crate::lib::environment::{AgentEnvironment, Environment}; use crate::lib::error::{DfxError, DfxResult}; use crate::lib::message::UserMessage; use crate::lib::models::canister::{CanManMetadata, CanisterManifest}; @@ -12,21 +12,32 @@ use tokio::runtime::Runtime; pub fn construct() -> App<'static, 'static> { SubCommand::with_name("create") - .about(UserMessage::InstallCanister.to_str()) + .about(UserMessage::CreateCanister.to_str()) .arg( Arg::with_name("canister_name") .takes_value(true) .required_unless("all") - // .help(UserMessage::InstallCanisterName.to_str()) + .help(UserMessage::CreateCanisterName.to_str()) .required(false), ) .arg( Arg::with_name("all") .long("all") .required_unless("canister_name") - // .help(UserMessage::InstallAll.to_str()) + .help(UserMessage::CreateAll.to_str()) .takes_value(false), ) + .arg( + Arg::with_name("provider") + .help(UserMessage::CanisterComputeProvider.to_str()) + .long("provider") + .validator(|v| { + reqwest::Url::parse(&v) + .map(|_| ()) + .map_err(|_| "should be a valid URL.".to_string()) + }) + .takes_value(true), + ) } fn create_canister(env: &dyn Environment, canister_name: &str) -> DfxResult { @@ -73,6 +84,17 @@ fn create_canister(env: &dyn Environment, canister_name: &str) -> DfxResult { } pub fn exec(env: &dyn Environment, args: &ArgMatches<'_>) -> DfxResult { + // Need storage for AgentEnvironment ownership. + let mut _agent_env: Option> = None; + let env = if args.is_present("provider") { + _agent_env = Some(AgentEnvironment::new( + env, + args.value_of("provider").expect("Could not find provider."), + )); + _agent_env.as_ref().unwrap() + } else { + env + }; let config = env .get_config() .ok_or(DfxError::CommandMustBeRunInAProject)?; diff --git a/src/dfx/src/lib/message.rs b/src/dfx/src/lib/message.rs index f643b3bb7d..c2bea2b032 100644 --- a/src/dfx/src/lib/message.rs +++ b/src/dfx/src/lib/message.rs @@ -43,6 +43,12 @@ user_message!( ArgumentType => "Specifies the data type for the argument when making the call using an argument.", ArgumentValue => "Specifies the argument to pass to the method.", + + // dfx canister create + CreateCanister => "Creates an empty canister on the Internet Computer and associates the Internet Computer assigned Canister ID to the canister name.", + CreateCanisterName => "Specifies the canister name. Either this or the --all flag are required.", + CreateAll => "Creates all canisters configured in dfx.json.", + // dfx canister install InstallCanister => "Installs compiled code as a canister on the replica.", InstallAll => "Install all canisters configured in dfx.json.", diff --git a/src/dfx/src/lib/models/canister.rs b/src/dfx/src/lib/models/canister.rs index ca74a0171d..141019a935 100644 --- a/src/dfx/src/lib/models/canister.rs +++ b/src/dfx/src/lib/models/canister.rs @@ -199,7 +199,7 @@ impl CanisterPool { // Add all the canisters as nodes. for canister in &self.canisters { - let canister_id = canister.canister_id(); + let canister_id = canister.info.get_canister_id()?; id_set.insert(canister_id.clone(), graph.add_node(canister_id.clone())); } From eef978be7b469168d613b566a7421ae5def11a0d Mon Sep 17 00:00:00 2001 From: Prithvi Shahi Date: Mon, 29 Jun 2020 12:45:16 -0700 Subject: [PATCH 06/14] remove provider for create --- e2e/bats/{create.sh => create.bash} | 0 e2e/bats/frontend.bash | 2 +- src/dfx/src/commands/canister/create.rs | 24 +----------------------- 3 files changed, 2 insertions(+), 24 deletions(-) rename e2e/bats/{create.sh => create.bash} (100%) diff --git a/e2e/bats/create.sh b/e2e/bats/create.bash similarity index 100% rename from e2e/bats/create.sh rename to e2e/bats/create.bash diff --git a/e2e/bats/frontend.bash b/e2e/bats/frontend.bash index 4c529d6130..529297b260 100644 --- a/e2e/bats/frontend.bash +++ b/e2e/bats/frontend.bash @@ -27,7 +27,7 @@ teardown() { [ "$USE_IC_REF" ] && skip "dfx start cannot serve frontent when using ic-ref" dfx_start --host 127.0.0.1:12345 - dfx canister create --all --provider http://127.0.0.1:12345 + dfx canister --provider http://127.0.0.1:12345 create --all dfx build --skip-frontend --provider http://127.0.0.1:12345 assert_command curl http://localhost:12345 # 8000 = default port. diff --git a/src/dfx/src/commands/canister/create.rs b/src/dfx/src/commands/canister/create.rs index abd4b219de..3f1069f83b 100644 --- a/src/dfx/src/commands/canister/create.rs +++ b/src/dfx/src/commands/canister/create.rs @@ -1,5 +1,5 @@ use crate::lib::canister_info::CanisterInfo; -use crate::lib::environment::{AgentEnvironment, Environment}; +use crate::lib::environment::Environment; use crate::lib::error::{DfxError, DfxResult}; use crate::lib::message::UserMessage; use crate::lib::models::canister::{CanManMetadata, CanisterManifest}; @@ -27,17 +27,6 @@ pub fn construct() -> App<'static, 'static> { .help(UserMessage::CreateAll.to_str()) .takes_value(false), ) - .arg( - Arg::with_name("provider") - .help(UserMessage::CanisterComputeProvider.to_str()) - .long("provider") - .validator(|v| { - reqwest::Url::parse(&v) - .map(|_| ()) - .map_err(|_| "should be a valid URL.".to_string()) - }) - .takes_value(true), - ) } fn create_canister(env: &dyn Environment, canister_name: &str) -> DfxResult { @@ -84,17 +73,6 @@ fn create_canister(env: &dyn Environment, canister_name: &str) -> DfxResult { } pub fn exec(env: &dyn Environment, args: &ArgMatches<'_>) -> DfxResult { - // Need storage for AgentEnvironment ownership. - let mut _agent_env: Option> = None; - let env = if args.is_present("provider") { - _agent_env = Some(AgentEnvironment::new( - env, - args.value_of("provider").expect("Could not find provider."), - )); - _agent_env.as_ref().unwrap() - } else { - env - }; let config = env .get_config() .ok_or(DfxError::CommandMustBeRunInAProject)?; From bb2dd541d42574e5ee00d0b43ef130a4bd5ac5db Mon Sep 17 00:00:00 2001 From: Prithvi Shahi Date: Wed, 24 Jun 2020 16:12:36 -0700 Subject: [PATCH 07/14] intial attempt --- nix/sources.json | 9 +- src/agent/rust/Cargo.toml | 2 + src/agent/rust/src/agent/agent_error.rs | 17 +++- src/agent/rust/src/agent/agent_test.rs | 41 -------- src/agent/rust/src/agent/mod.rs | 80 +--------------- src/agent/rust/src/agent/replica_api.rs | 39 -------- .../rust/src/canister/management_canister.rs | 95 +++++++++++++++++++ src/agent/rust/src/canister/mod.rs | 7 ++ src/agent/rust/src/lib.rs | 3 + src/dfx/src/commands/canister/create.rs | 14 +-- src/dfx/src/commands/canister/install.rs | 7 +- 11 files changed, 140 insertions(+), 174 deletions(-) create mode 100644 src/agent/rust/src/canister/management_canister.rs create mode 100644 src/agent/rust/src/canister/mod.rs diff --git a/nix/sources.json b/nix/sources.json index 08a99515e2..15e968657b 100644 --- a/nix/sources.json +++ b/nix/sources.json @@ -32,16 +32,15 @@ "type": "git" }, "dfinity": { - "ref": "v0.5.8", + "ref": "master", "repo": "ssh://git@github.com/dfinity-lab/dfinity", - "rev": "45c2b5022e1ae81677c5745e0f5d829228c3fb5a", + "rev": "d3e0ee8e28a39a82774f5b08d9dec3910cacfcf7", "type": "git" }, "ic-ref": { - "branch": "legacy3", - "ref": "legacy3", + "ref": "release-0.8", "repo": "ssh://git@github.com/dfinity-lab/ic-ref", - "rev": "6331356b20183bb9eda5589959ee18bf66becc06", + "rev": "d0166d45978b17e87f25a74f484ed872a897d8f9", "type": "git" }, "motoko": { diff --git a/src/agent/rust/Cargo.toml b/src/agent/rust/Cargo.toml index 6d517d38b3..0ef589d06f 100644 --- a/src/agent/rust/Cargo.toml +++ b/src/agent/rust/Cargo.toml @@ -9,6 +9,8 @@ edition = "2018" [dependencies] async-trait = "0.1.35" byteorder = "1.3.2" +candid = "0.3.1" +candid_derive = "0.2.2" crc8 = "0.1.1" delay = "0.1.1" hex = "0.4.0" diff --git a/src/agent/rust/src/agent/agent_error.rs b/src/agent/rust/src/agent/agent_error.rs index be49f482ef..c7021c2a66 100644 --- a/src/agent/rust/src/agent/agent_error.rs +++ b/src/agent/rust/src/agent/agent_error.rs @@ -1,4 +1,4 @@ -use crate::{Replied, RequestIdError}; +use crate::{Replied, RequestIdError, TextualCanisterIdError}; use serde_cbor::error::Error as SerdeError; #[derive(Debug)] @@ -27,6 +27,9 @@ pub enum AgentError { }, UnexpectedReply(Replied), + + CandidError(candid::Error), + CanisterIdTextError(TextualCanisterIdError), } impl From for AgentError { @@ -41,6 +44,12 @@ impl From for AgentError { } } +impl From for AgentError { + fn from(err: candid::Error) -> Self { + Self::CandidError(err) + } +} + impl From for AgentError { fn from(err: url::ParseError) -> Self { Self::UrlParseError(err) @@ -52,3 +61,9 @@ impl From for AgentError { Self::CannotCalculateRequestId(err) } } + +impl From for AgentError { + fn from(err: TextualCanisterIdError) -> Self { + Self::CanisterIdTextError(err) + } +} diff --git a/src/agent/rust/src/agent/agent_test.rs b/src/agent/rust/src/agent/agent_test.rs index f5509fa8fb..2cafd9073b 100644 --- a/src/agent/rust/src/agent/agent_test.rs +++ b/src/agent/rust/src/agent/agent_test.rs @@ -210,47 +210,6 @@ fn call_rejected() -> Result<(), AgentError> { Ok(()) } -#[test] -fn install() -> Result<(), AgentError> { - let canister_id = CanisterId::from_bytes(&[5u8]); - let module = Blob::from(&[1, 2]); - - let blob = Blob(Vec::from("Hello World")); - let response = QueryResponse::Replied { - reply: CallReply { arg: blob.clone() }, - }; - - let submit_mock = mock("POST", "/api/v1/submit").with_status(200).create(); - let status_mock = mock("POST", "/api/v1/read") - .with_status(200) - .with_header("content-type", "application/cbor") - .with_body(serde_cbor::to_vec(&response)?) - .create(); - - let agent = Agent::new(AgentConfig { - url: &mockito::server_url(), - ..AgentConfig::default() - })?; - - let mut runtime = tokio::runtime::Runtime::new().expect("Unable to create a runtime"); - let result = runtime.block_on(async { - let request_id = agent.install(&canister_id, &module, &Blob::empty()).await?; - agent.request_status_raw(&request_id).await - }); - - submit_mock.assert(); - status_mock.assert(); - - assert_eq!( - result?, - RequestStatusResponse::Replied { - reply: Replied::CallReplied(blob) - } - ); - - Ok(()) -} - #[test] fn ping() -> Result<(), AgentError> { let response = serde_cbor::Value::Map(BTreeMap::new()); diff --git a/src/agent/rust/src/agent/mod.rs b/src/agent/rust/src/agent/mod.rs index 8af7708985..c8dbca91d9 100644 --- a/src/agent/rust/src/agent/mod.rs +++ b/src/agent/rust/src/agent/mod.rs @@ -17,7 +17,7 @@ mod agent_test; use crate::agent::replica_api::{AsyncContent, Envelope, SyncContent}; use crate::identity::Identity; -use crate::{to_request_id, Blob, CanisterAttributes, CanisterId, Principal, RequestId}; +use crate::{to_request_id, Blob, CanisterId, Principal, RequestId}; use reqwest::Method; use serde::Serialize; @@ -111,9 +111,7 @@ impl Agent { async fn submit(&self, request: AsyncContent) -> Result { let request_id = to_request_id(&request)?; let sender = match request.clone() { - AsyncContent::CreateCanisterRequest { sender, .. } => sender, AsyncContent::CallRequest { sender, .. } => sender, - AsyncContent::InstallCodeRequest { sender, .. } => sender, }; let signature = self.identity.sign(&request_id, &sender)?; let _ = self @@ -176,12 +174,6 @@ impl Agent { replica_api::RequestStatusResponseReplied::CallReply(reply) => { Replied::CallReplied(reply.arg) } - replica_api::RequestStatusResponseReplied::CreateCanisterReply(reply) => { - Replied::CreateCanisterReplied(reply.canister_id) - } - replica_api::RequestStatusResponseReplied::InstallCodeReply(_) => { - Replied::InstallCodeReplied - } }; RequestStatusResponse::Replied { reply } @@ -269,76 +261,6 @@ impl Agent { .await } - pub async fn create_canister(&self) -> Result { - self.submit(AsyncContent::CreateCanisterRequest { - sender: self.identity.sender()?, - nonce: self.nonce_factory.generate().map(|b| b), - }) - .await - } - - pub async fn create_canister_and_wait( - &self, - waiter: W, - ) -> Result { - let request_id = self.create_canister().await?; - match self.request_status_and_wait(&request_id, waiter).await? { - Replied::CreateCanisterReplied(id) => Ok(id), - reply => Err(AgentError::UnexpectedReply(reply)), - } - } - - pub async fn install( - &self, - canister_id: &CanisterId, - module: &Blob, - arg: &Blob, - ) -> Result { - self.install_with_attrs(canister_id, "", module, arg, &CanisterAttributes::default()) - .await - } - - pub async fn install_and_wait( - &self, - canister_id: &CanisterId, - module: &Blob, - arg: &Blob, - waiter: W, - ) -> Result<(), AgentError> { - let request_id = self.install(canister_id, module, arg).await?; - match self.request_status_and_wait(&request_id, waiter).await? { - Replied::InstallCodeReplied => Ok(()), - reply => Err(AgentError::UnexpectedReply(reply)), - } - } - - pub async fn install_with_attrs( - &self, - canister_id: &CanisterId, - mode: &str, - module: &Blob, - arg: &Blob, - attributes: &CanisterAttributes, - ) -> Result { - let mode = match mode { - "install" => Some(mode.to_string()), - "reinstall" => Some(mode.to_string()), - "upgrade" => Some(mode.to_string()), - &_ => None, - }; - self.submit(AsyncContent::InstallCodeRequest { - nonce: self.nonce_factory.generate().map(|b| b.as_slice().into()), - sender: self.identity.sender()?, - canister_id: canister_id.clone(), - module: module.clone(), - arg: arg.clone(), - compute_allocation: attributes.compute_allocation.map(|x| x.into()), - memory_allocation: None, - mode, - }) - .await - } - pub async fn ping_once(&self) -> Result { let url = self.url.join("status")?; let mut http_request = reqwest::Request::new(Method::GET, url); diff --git a/src/agent/rust/src/agent/replica_api.rs b/src/agent/rust/src/agent/replica_api.rs index c9bf7638b3..662b3d55b5 100644 --- a/src/agent/rust/src/agent/replica_api.rs +++ b/src/agent/rust/src/agent/replica_api.rs @@ -12,27 +12,6 @@ pub struct Envelope { #[derive(Debug, Clone, Deserialize, Serialize)] #[serde(tag = "request_type")] pub enum AsyncContent { - #[serde(rename = "create_canister")] - CreateCanisterRequest { - #[serde(skip_serializing_if = "Option::is_none")] - nonce: Option, - sender: Principal, - }, - #[serde(rename = "install_code")] - InstallCodeRequest { - #[serde(skip_serializing_if = "Option::is_none")] - nonce: Option, - sender: Principal, - canister_id: CanisterId, - module: Blob, - arg: Blob, - #[serde(skip_serializing_if = "Option::is_none")] - compute_allocation: Option, - #[serde(skip_serializing_if = "Option::is_none")] - memory_allocation: Option, - #[serde(skip_serializing_if = "Option::is_none")] - mode: Option, - }, #[serde(rename = "call")] CallRequest { #[serde(skip_serializing_if = "Option::is_none")] @@ -42,14 +21,6 @@ pub enum AsyncContent { method_name: String, arg: Blob, }, - // #[serde(rename = "set_controller")] - // SetControllerRequest { - // #[serde(skip_serializing_if = "Option::is_none")] - // nonce: &'a Option, - // sender: &'a Principal, - // canister_id: &'a CanisterId, - // controller: Principal, - // }, } #[derive(Debug, Clone, Deserialize, Serialize)] @@ -88,18 +59,8 @@ pub enum RequestStatusResponse { #[serde(untagged)] pub enum RequestStatusResponseReplied { CallReply(CallReply), - CreateCanisterReply(CreateCanisterReply), - InstallCodeReply(InstallCodeReply), } -#[derive(Debug, Clone, Deserialize, Serialize)] -pub struct CreateCanisterReply { - pub canister_id: CanisterId, -} - -#[derive(Debug, Clone, Deserialize, Serialize)] -pub struct InstallCodeReply {} - #[derive(Debug, Clone, Deserialize, Serialize)] pub struct CallReply { pub arg: Blob, diff --git a/src/agent/rust/src/canister/management_canister.rs b/src/agent/rust/src/canister/management_canister.rs new file mode 100644 index 0000000000..1b9ba9732f --- /dev/null +++ b/src/agent/rust/src/canister/management_canister.rs @@ -0,0 +1,95 @@ +use crate::agent::agent_error::AgentError; +use crate::agent::response::Replied; +use crate::agent::Agent; +use crate::{Blob, CanisterAttributes, CanisterId, RequestId}; +use candid::{Decode, Encode}; + +const MANAGEMENT_CANISTER_ID: &str = "ic:00"; +const CREATE_CMD: &str = "create_canister"; +const INSTALL_CMD: &str = "install_code"; + +#[derive(candid::CandidType, candid::Deserialize)] +struct CreateResult { + canister_id: candid::Principal, +} + +#[derive(candid::CandidType, candid::Deserialize)] +enum InstallMode { + #[serde(rename = "install")] + Install, + #[serde(rename = "reinstall")] + Reinstall, + #[serde(rename = "upgrade")] + Upgrade, +} + +#[derive(candid::CandidType, candid::Deserialize)] +struct CanisterInstall { + mode: InstallMode, + canister_id: candid::Principal, + wasm_module: Vec, + arg: Vec, + compute_allocation: Option, +} + +pub struct ManagementCanister<'_a> { + pub agent: &'_a Agent, +} + +impl<'_a> ManagementCanister<'_a> { + pub async fn create_canister( + &self, + waiter: W, + ) -> Result { + let request_id = self + .agent + .call_raw( + &CanisterId::from_text(MANAGEMENT_CANISTER_ID).unwrap(), + CREATE_CMD, + &Blob::empty(), + ) + .await?; + match self + .agent + .request_status_and_wait(&request_id, waiter) + .await? + { + Replied::CallReplied(blob) => { + let cid = Decode!(blob.as_slice(), CreateResult)?; + Ok(CanisterId::from_text(cid.canister_id.to_text())?) + } + reply => Err(AgentError::UnexpectedReply(reply)), + } + } + + pub async fn install_code( + &self, + canister_id: &CanisterId, + mode: &str, + module: &Blob, + arg: &Blob, + attributes: &CanisterAttributes, + ) -> Result { + let mode = match mode { + "install" => InstallMode::Install, + "reinstall" => InstallMode::Reinstall, + "upgrade" => InstallMode::Upgrade, + &_ => InstallMode::Install, + }; + let canister_to_install = CanisterInstall { + mode, + canister_id: candid::Principal::from_text(canister_id.clone().to_text())?, + wasm_module: module.clone().as_slice().to_vec(), + arg: arg.clone().as_slice().to_vec(), + compute_allocation: attributes.compute_allocation.map(|x| x.into()), + }; + let bytes: Vec = candid::Encode!(&canister_to_install).unwrap(); + self.agent + .call_raw( + &CanisterId::from_text(MANAGEMENT_CANISTER_ID).unwrap(), + INSTALL_CMD, + &Blob::from(bytes), + ) + .await + } +} diff --git a/src/agent/rust/src/canister/mod.rs b/src/agent/rust/src/canister/mod.rs new file mode 100644 index 0000000000..57bed09c59 --- /dev/null +++ b/src/agent/rust/src/canister/mod.rs @@ -0,0 +1,7 @@ +pub(crate) mod management_canister; + +pub(crate) mod public { + use super::*; + + pub use management_canister::ManagementCanister; +} diff --git a/src/agent/rust/src/lib.rs b/src/agent/rust/src/lib.rs index c4cb5e0634..3e0c0e2797 100644 --- a/src/agent/rust/src/lib.rs +++ b/src/agent/rust/src/lib.rs @@ -6,3 +6,6 @@ pub use identity::public::*; mod types; pub use types::public::*; + +mod canister; +pub use canister::public::*; diff --git a/src/dfx/src/commands/canister/create.rs b/src/dfx/src/commands/canister/create.rs index 3f1069f83b..4e943bfcf3 100644 --- a/src/dfx/src/commands/canister/create.rs +++ b/src/dfx/src/commands/canister/create.rs @@ -6,7 +6,7 @@ use crate::lib::models::canister::{CanManMetadata, CanisterManifest}; use crate::lib::waiter::create_waiter; use clap::{App, Arg, ArgMatches, SubCommand}; -use ic_agent::CanisterId; +use ic_agent::{CanisterId, ManagementCanister}; use serde_json::Map; use tokio::runtime::Runtime; @@ -34,9 +34,11 @@ fn create_canister(env: &dyn Environment, canister_name: &str) -> DfxResult { .get_config() .ok_or(DfxError::CommandMustBeRunInAProject)?; - let agent = env - .get_agent() - .ok_or(DfxError::CommandMustBeRunInAProject)?; + let mgr = ManagementCanister { + agent: env + .get_agent() + .ok_or(DfxError::CommandMustBeRunInAProject)?, + }; let mut runtime = Runtime::new().expect("Unable to create a runtime"); let info = CanisterInfo::load(&config, canister_name)?; @@ -55,14 +57,14 @@ fn create_canister(env: &dyn Environment, canister_name: &str) -> DfxResult { CanisterId::from_text(metadata.canister_id).ok(); } None => { - let cid = runtime.block_on(agent.create_canister_and_wait(create_waiter()))?; + let cid = runtime.block_on(mgr.create_canister(create_waiter()))?; info.set_canister_id(cid.clone())?; manifest.add_entry(&info, cid)?; } } } } else { - let cid = runtime.block_on(agent.create_canister_and_wait(create_waiter()))?; + let cid = runtime.block_on(mgr.create_canister(create_waiter()))?; info.set_canister_id(cid.clone())?; let mut manifest = CanisterManifest { canisters: Map::new(), diff --git a/src/dfx/src/commands/canister/install.rs b/src/dfx/src/commands/canister/install.rs index 88dec9f107..2173f4cdf2 100644 --- a/src/dfx/src/commands/canister/install.rs +++ b/src/dfx/src/commands/canister/install.rs @@ -6,7 +6,7 @@ use crate::lib::message::UserMessage; use crate::lib::waiter::create_waiter; use clap::{App, Arg, ArgMatches, SubCommand}; -use ic_agent::{Agent, Blob, CanisterAttributes, ComputeAllocation, RequestId}; +use ic_agent::{Agent, Blob, CanisterAttributes, ComputeAllocation, ManagementCanister, RequestId}; use slog::info; use std::convert::TryFrom; use tokio::runtime::Runtime; @@ -60,6 +60,7 @@ async fn install_canister( compute_allocation: Option, mode: &str, ) -> DfxResult { + let mgr = ManagementCanister { agent }; let log = env.get_logger(); let canister_id = canister_info.get_canister_id().map_err(|_| { DfxError::CannotFindBuildOutputForCanister(canister_info.get_name().to_owned()) @@ -77,8 +78,8 @@ async fn install_canister( .expect("Cannot get WASM output path."); let wasm = std::fs::read(wasm_path)?; - let result = agent - .install_with_attrs( + let result = mgr + .install_code( &canister_id, &mode, &Blob::from(wasm), From 2f18ddad2802bb556ea2a8d8b8e8711071ada162 Mon Sep 17 00:00:00 2001 From: Prithvi Shahi Date: Sun, 28 Jun 2020 21:17:26 -0700 Subject: [PATCH 08/14] fixes --- Cargo.lock | 2 + src/agent/rust/src/agent/agent_error.rs | 2 + .../rust/src/canister/management_canister.rs | 50 ++++++++++++------- src/agent/rust/src/canister/mod.rs | 2 +- src/dfx/src/commands/canister/create.rs | 33 ++++++------ src/dfx/src/commands/canister/install.rs | 15 +++--- 6 files changed, 60 insertions(+), 44 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index bb33621866..b10e8cd27a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1466,6 +1466,8 @@ version = "0.5.9" dependencies = [ "async-trait 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)", "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "candid 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "candid_derive 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "crc8 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "delay 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "hex 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/src/agent/rust/src/agent/agent_error.rs b/src/agent/rust/src/agent/agent_error.rs index c7021c2a66..18263907b8 100644 --- a/src/agent/rust/src/agent/agent_error.rs +++ b/src/agent/rust/src/agent/agent_error.rs @@ -30,6 +30,8 @@ pub enum AgentError { CandidError(candid::Error), CanisterIdTextError(TextualCanisterIdError), + + InstallModeError(String), } impl From for AgentError { diff --git a/src/agent/rust/src/canister/management_canister.rs b/src/agent/rust/src/canister/management_canister.rs index 1b9ba9732f..71f0070e64 100644 --- a/src/agent/rust/src/canister/management_canister.rs +++ b/src/agent/rust/src/canister/management_canister.rs @@ -3,18 +3,19 @@ use crate::agent::response::Replied; use crate::agent::Agent; use crate::{Blob, CanisterAttributes, CanisterId, RequestId}; use candid::{Decode, Encode}; +use std::str::FromStr; const MANAGEMENT_CANISTER_ID: &str = "ic:00"; -const CREATE_CMD: &str = "create_canister"; -const INSTALL_CMD: &str = "install_code"; +const CREATE_METHOD_NAME: &str = "create_canister"; +const INSTALL_METHOD_NAME: &str = "install_code"; #[derive(candid::CandidType, candid::Deserialize)] struct CreateResult { canister_id: candid::Principal, } -#[derive(candid::CandidType, candid::Deserialize)] -enum InstallMode { +#[derive(Clone, candid::CandidType, candid::Deserialize)] +pub enum InstallMode { #[serde(rename = "install")] Install, #[serde(rename = "reinstall")] @@ -23,6 +24,19 @@ enum InstallMode { Upgrade, } +impl FromStr for InstallMode { + type Err = AgentError; + + fn from_str(s: &str) -> Result { + match s { + "install" => Ok(InstallMode::Install), + "reinstall" => Ok(InstallMode::Reinstall), + "upgrade" => Ok(InstallMode::Upgrade), + &_ => Err(AgentError::InstallModeError(s.to_string())), + } + } +} + #[derive(candid::CandidType, candid::Deserialize)] struct CanisterInstall { mode: InstallMode, @@ -32,11 +46,15 @@ struct CanisterInstall { compute_allocation: Option, } -pub struct ManagementCanister<'_a> { - pub agent: &'_a Agent, +pub struct ManagementCanister<'agent> { + agent: &'agent Agent, } -impl<'_a> ManagementCanister<'_a> { +impl<'agent> ManagementCanister<'agent> { + pub fn new(agent: &'agent Agent) -> Self { + ManagementCanister { agent } + } + pub async fn create_canister( &self, waiter: W, @@ -45,7 +63,7 @@ impl<'_a> ManagementCanister<'_a> { .agent .call_raw( &CanisterId::from_text(MANAGEMENT_CANISTER_ID).unwrap(), - CREATE_CMD, + CREATE_METHOD_NAME, &Blob::empty(), ) .await?; @@ -65,29 +83,23 @@ impl<'_a> ManagementCanister<'_a> { pub async fn install_code( &self, canister_id: &CanisterId, - mode: &str, + mode: InstallMode, module: &Blob, arg: &Blob, attributes: &CanisterAttributes, ) -> Result { - let mode = match mode { - "install" => InstallMode::Install, - "reinstall" => InstallMode::Reinstall, - "upgrade" => InstallMode::Upgrade, - &_ => InstallMode::Install, - }; let canister_to_install = CanisterInstall { mode, - canister_id: candid::Principal::from_text(canister_id.clone().to_text())?, - wasm_module: module.clone().as_slice().to_vec(), - arg: arg.clone().as_slice().to_vec(), + canister_id: candid::Principal::from_text(canister_id.to_text())?, + wasm_module: module.as_slice().to_vec(), + arg: arg.as_slice().to_vec(), compute_allocation: attributes.compute_allocation.map(|x| x.into()), }; let bytes: Vec = candid::Encode!(&canister_to_install).unwrap(); self.agent .call_raw( &CanisterId::from_text(MANAGEMENT_CANISTER_ID).unwrap(), - INSTALL_CMD, + INSTALL_METHOD_NAME, &Blob::from(bytes), ) .await diff --git a/src/agent/rust/src/canister/mod.rs b/src/agent/rust/src/canister/mod.rs index 57bed09c59..8459b7f835 100644 --- a/src/agent/rust/src/canister/mod.rs +++ b/src/agent/rust/src/canister/mod.rs @@ -3,5 +3,5 @@ pub(crate) mod management_canister; pub(crate) mod public { use super::*; - pub use management_canister::ManagementCanister; + pub use management_canister::{InstallMode, ManagementCanister}; } diff --git a/src/dfx/src/commands/canister/create.rs b/src/dfx/src/commands/canister/create.rs index 4e943bfcf3..22d400d21d 100644 --- a/src/dfx/src/commands/canister/create.rs +++ b/src/dfx/src/commands/canister/create.rs @@ -34,11 +34,10 @@ fn create_canister(env: &dyn Environment, canister_name: &str) -> DfxResult { .get_config() .ok_or(DfxError::CommandMustBeRunInAProject)?; - let mgr = ManagementCanister { - agent: env - .get_agent() + let mgr = ManagementCanister::new( + env.get_agent() .ok_or(DfxError::CommandMustBeRunInAProject)?, - }; + ); let mut runtime = Runtime::new().expect("Unable to create a runtime"); let info = CanisterInfo::load(&config, canister_name)?; @@ -46,21 +45,19 @@ fn create_canister(env: &dyn Environment, canister_name: &str) -> DfxResult { // check if the canister_manifest.json file exists if manifest_path.is_file() { - { - let file = std::fs::File::open(info.get_manifest_path()).unwrap(); - let mut manifest: CanisterManifest = serde_json::from_reader(file).unwrap(); + let file = std::fs::File::open(info.get_manifest_path()).unwrap(); + let mut manifest: CanisterManifest = serde_json::from_reader(file).unwrap(); - match manifest.canisters.get(info.get_name()) { - Some(serde_value) => { - let metadata: CanManMetadata = - serde_json::from_value(serde_value.to_owned()).unwrap(); - CanisterId::from_text(metadata.canister_id).ok(); - } - None => { - let cid = runtime.block_on(mgr.create_canister(create_waiter()))?; - info.set_canister_id(cid.clone())?; - manifest.add_entry(&info, cid)?; - } + match manifest.canisters.get(info.get_name()) { + Some(serde_value) => { + let metadata: CanManMetadata = + serde_json::from_value(serde_value.to_owned()).unwrap(); + CanisterId::from_text(metadata.canister_id).ok(); + } + None => { + let cid = runtime.block_on(mgr.create_canister(create_waiter()))?; + info.set_canister_id(cid.clone())?; + manifest.add_entry(&info, cid)?; } } } else { diff --git a/src/dfx/src/commands/canister/install.rs b/src/dfx/src/commands/canister/install.rs index 2173f4cdf2..d3ae9e76d9 100644 --- a/src/dfx/src/commands/canister/install.rs +++ b/src/dfx/src/commands/canister/install.rs @@ -6,9 +6,12 @@ use crate::lib::message::UserMessage; use crate::lib::waiter::create_waiter; use clap::{App, Arg, ArgMatches, SubCommand}; -use ic_agent::{Agent, Blob, CanisterAttributes, ComputeAllocation, ManagementCanister, RequestId}; +use ic_agent::{ + Agent, Blob, CanisterAttributes, ComputeAllocation, InstallMode, ManagementCanister, RequestId, +}; use slog::info; use std::convert::TryFrom; +use std::str::FromStr; use tokio::runtime::Runtime; pub fn construct() -> App<'static, 'static> { @@ -58,9 +61,9 @@ async fn install_canister( agent: &Agent, canister_info: &CanisterInfo, compute_allocation: Option, - mode: &str, + mode: InstallMode, ) -> DfxResult { - let mgr = ManagementCanister { agent }; + let mgr = ManagementCanister::new(agent); let log = env.get_logger(); let canister_id = canister_info.get_canister_id().map_err(|_| { DfxError::CannotFindBuildOutputForCanister(canister_info.get_name().to_owned()) @@ -81,7 +84,7 @@ async fn install_canister( let result = mgr .install_code( &canister_id, - &mode, + mode, &Blob::from(wasm), &Blob::empty(), &CanisterAttributes { compute_allocation }, @@ -121,7 +124,7 @@ pub fn exec(env: &dyn Environment, args: &ArgMatches<'_>) -> DfxResult { .expect("Compute Allocation must be a percentage.") }); - let mode = args.value_of("mode").unwrap(); + let mode = InstallMode::from_str(args.value_of("mode").unwrap())?; let mut runtime = Runtime::new().expect("Unable to create a runtime"); @@ -155,7 +158,7 @@ pub fn exec(env: &dyn Environment, args: &ArgMatches<'_>) -> DfxResult { &agent, &canister_info, compute_allocation, - mode, + mode.clone(), ))?; if args.is_present("async") { From e4df3b28df444325295b12554cf9b278aa7289a8 Mon Sep 17 00:00:00 2001 From: Prithvi Shahi Date: Sun, 28 Jun 2020 22:18:59 -0700 Subject: [PATCH 09/14] update replica commit --- nix/sources.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nix/sources.json b/nix/sources.json index 15e968657b..a5619fdaea 100644 --- a/nix/sources.json +++ b/nix/sources.json @@ -34,7 +34,7 @@ "dfinity": { "ref": "master", "repo": "ssh://git@github.com/dfinity-lab/dfinity", - "rev": "d3e0ee8e28a39a82774f5b08d9dec3910cacfcf7", + "rev": "34233cef571f088cd9c9fed8495ddf07a2ef2c9f", "type": "git" }, "ic-ref": { From 1f71bfe5118764f890874966c0d2c68534a05295 Mon Sep 17 00:00:00 2001 From: Prithvi Shahi Date: Mon, 29 Jun 2020 13:16:06 -0700 Subject: [PATCH 10/14] change to warn level logging --- nix/sources.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nix/sources.json b/nix/sources.json index a5619fdaea..70de4c3e68 100644 --- a/nix/sources.json +++ b/nix/sources.json @@ -32,9 +32,9 @@ "type": "git" }, "dfinity": { - "ref": "master", + "ref": "dsd/info_log_level_starter", "repo": "ssh://git@github.com/dfinity-lab/dfinity", - "rev": "34233cef571f088cd9c9fed8495ddf07a2ef2c9f", + "rev": "3e64d663d1c94909aab0c35d5cf78703f6926776", "type": "git" }, "ic-ref": { From 536d8579183981c17de955d817f7353a456fc83a Mon Sep 17 00:00:00 2001 From: Prithvi Shahi Date: Mon, 29 Jun 2020 13:18:57 -0700 Subject: [PATCH 11/14] use master branch --- nix/sources.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nix/sources.json b/nix/sources.json index 70de4c3e68..9ccc783531 100644 --- a/nix/sources.json +++ b/nix/sources.json @@ -32,9 +32,9 @@ "type": "git" }, "dfinity": { - "ref": "dsd/info_log_level_starter", + "ref": "master", "repo": "ssh://git@github.com/dfinity-lab/dfinity", - "rev": "3e64d663d1c94909aab0c35d5cf78703f6926776", + "rev": "b63315a1ae098e705cf66beb6eba82e9a0af0d3a", "type": "git" }, "ic-ref": { From 078836665a1f0d8d08058c4a9a3d75df6f986ea9 Mon Sep 17 00:00:00 2001 From: Prithvi Shahi <50885601+p-shahi@users.noreply.github.com> Date: Mon, 29 Jun 2020 13:19:56 -0700 Subject: [PATCH 12/14] Update nix/sources.json Co-authored-by: Joachim Breitner --- nix/sources.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nix/sources.json b/nix/sources.json index 9ccc783531..0a4ffe2993 100644 --- a/nix/sources.json +++ b/nix/sources.json @@ -38,7 +38,7 @@ "type": "git" }, "ic-ref": { - "ref": "release-0.8", + "ref": "0.8", "repo": "ssh://git@github.com/dfinity-lab/ic-ref", "rev": "d0166d45978b17e87f25a74f484ed872a897d8f9", "type": "git" From 1fb5525db6452345a27dfb5e31634fb98c769276 Mon Sep 17 00:00:00 2001 From: Akhi Singhania Date: Tue, 30 Jun 2020 09:39:14 +0200 Subject: [PATCH 13/14] Switch the dfinity revision that changes the default to wasmtime --- nix/sources.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nix/sources.json b/nix/sources.json index 0a4ffe2993..a03106fefd 100644 --- a/nix/sources.json +++ b/nix/sources.json @@ -32,9 +32,9 @@ "type": "git" }, "dfinity": { - "ref": "master", + "ref": "akhi3030/v0.5.8", "repo": "ssh://git@github.com/dfinity-lab/dfinity", - "rev": "b63315a1ae098e705cf66beb6eba82e9a0af0d3a", + "rev": "3045e224d47aff317e265ac66e37834c31637011", "type": "git" }, "ic-ref": { From 38d882ac967e579c5f93e0e28ad3a13c82efefea Mon Sep 17 00:00:00 2001 From: Joachim Breitner Date: Tue, 30 Jun 2020 09:59:18 +0200 Subject: [PATCH 14/14] Work around / in sources.json problem --- nix/sources.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nix/sources.json b/nix/sources.json index a03106fefd..9911d58f11 100644 --- a/nix/sources.json +++ b/nix/sources.json @@ -32,7 +32,7 @@ "type": "git" }, "dfinity": { - "ref": "akhi3030/v0.5.8", + "ref": "akhi3030-v0.5.8", "repo": "ssh://git@github.com/dfinity-lab/dfinity", "rev": "3045e224d47aff317e265ac66e37834c31637011", "type": "git"