Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
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
28 changes: 28 additions & 0 deletions e2e/tests-dfx/sns.bash
Original file line number Diff line number Diff line change
Expand Up @@ -47,3 +47,31 @@ SNS_CONFIG_FILE_NAME="sns.yml"
assert_command_fail dfx sns config validate
assert_match token.name
}

@test "sns deploy exists" {
dfx sns deploy --help
}

@test "sns deploy fails without config file" {
dfx_new
rm -f sns.yml # Is not expected to be present anyway
assert_command_fail dfx sns deploy
assert_match
}

@test "sns deploy succeeds" {
dfx_new
install_shared_asset subnet_type/shared_network_settings/system
dfx start --clean --background --host 127.0.0.1:8080
sleep 1
dfx nns install
# There are no entries for "local" upstream yet, so we need a network mapping.
dfx nns import --network-mapping local=mainnet
# This canister ID is not included upstream .. yet.
jq '.canisters["nns-sns-wasm"].remote.id.local="qjdve-lqaaa-aaaaa-aaaeq-cai"' dfx.json | sponge dfx.json
ls candid
cat dfx.json
cp "${BATS_TEST_DIRNAME}/../assets/sns/valid_sns_init_config.yaml" "$SNS_CONFIG_FILE_NAME"
cp "${BATS_TEST_DIRNAME}/../assets/sns/logo.svg" .
Copy link

Choose a reason for hiding this comment

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

The install_asset function can help here. I'd recommend something like

install_asset sns/valid
install_asset sns/logo

and move these files to e2e/assets/sns/valid/sns.yml and e2e/assets/sns/logo/logo.svg

Copy link
Contributor Author

@bitdivine bitdivine Sep 19, 2022

Choose a reason for hiding this comment

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

Can I put these assets in a single directory, as they will normally be needed together? Then the call would be install_asset sns/valid ? I'll do that but we can change it if needed.

Copy link

Choose a reason for hiding this comment

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

Yes!

dfx sns deploy
}
2 changes: 2 additions & 0 deletions e2e/utils/_.bash
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ install_shared_asset() {
}

standard_setup() {
E2E_CACHE_ROOT="$(dfx cache show)"
export E2E_CACHE_ROOT
# We want to work from a temporary directory, different for every test.
x=$(mktemp -d -t dfx-e2e-XXXXXXXX)
export E2E_TEMP_DIR="$x"
Expand Down
2 changes: 2 additions & 0 deletions src/dfx/src/commands/nns/import.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
//! Code for the command line: `dfx nns import`
use crate::lib::error::DfxResult;
use crate::lib::info::replica_rev;
use crate::lib::project::import::import_canister_definitions;
Expand All @@ -19,6 +20,7 @@ pub struct ImportOpts {
network_mapping: Vec<String>,
}

/// Executes `dfx nns import`
pub async fn exec(env: &dyn Environment, opts: ImportOpts) -> DfxResult {
let config = env.get_config_or_anyhow()?;
let mut config = config.as_ref().clone();
Expand Down
2 changes: 2 additions & 0 deletions src/dfx/src/commands/nns/install.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
//! Code for the command line: `dfx nns install`
use crate::lib::error::DfxResult;
use crate::Environment;
use anyhow::anyhow;
Expand All @@ -23,6 +24,7 @@ use clap::Parser;
#[clap(about)]
pub struct InstallOpts {}

/// Executes `dfx nns install`.
pub async fn exec(env: &dyn Environment, _opts: InstallOpts) -> DfxResult {
let agent = env
.get_agent()
Expand Down
10 changes: 9 additions & 1 deletion src/dfx/src/commands/nns/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
//! Code for the command line `dfx nns`.
#![warn(clippy::missing_docs_in_private_items)]
use crate::lib::environment::Environment;
use crate::lib::error::DfxResult;
use crate::lib::provider::create_agent_environment;
Expand All @@ -9,25 +11,31 @@ use tokio::runtime::Runtime;
mod import;
mod install;

/// NNS commands.
/// Options for `dfx nns` and its subcommands.
#[derive(Parser)]
#[clap(name("nns"))]
pub struct NnsOpts {
/// `dfx nns` subcommand arguments.
#[clap(subcommand)]
subcmd: SubCommand,

/// An argument to choose the network from those specified in dfx.json.
#[clap(flatten)]
network: NetworkOpt,
}

/// Command line options for subcommands of `dfx nns`.
#[derive(Parser)]
enum SubCommand {
/// Options for importing NNS API definitions and canister IDs.
#[clap(hide(true))]
Import(import::ImportOpts),
/// Options for installing an NNS.
#[clap(hide(true))]
Install(install::InstallOpts),
}

/// Executes `dfx nns` and its subcommands.
pub fn exec(env: &dyn Environment, opts: NnsOpts) -> DfxResult {
let env = create_agent_environment(env, opts.network.network)?;
let runtime = Runtime::new().expect("Unable to create a runtime");
Expand Down
2 changes: 2 additions & 0 deletions src/dfx/src/commands/sns/config/create.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
//! Code for executing `dfx sns config create`
use crate::lib::error::DfxResult;
use crate::Environment;

Expand All @@ -9,6 +10,7 @@ use clap::Parser;
#[derive(Parser)]
pub struct CreateOpts {}

/// Executes `dfx sns config create`
pub fn exec(env: &dyn Environment, _opts: CreateOpts) -> DfxResult {
let config = env.get_config_or_anyhow()?;
let path = config.get_project_root().join(sns::CONFIG_FILE_NAME);
Expand Down
9 changes: 8 additions & 1 deletion src/dfx/src/commands/sns/config/mod.rs
Original file line number Diff line number Diff line change
@@ -1,26 +1,33 @@
//! Code for the command line `dfx sns config`.
use crate::lib::{environment::Environment, error::DfxResult};
use clap::Parser;

mod create;
mod validate;

/// Command line options for `sdx sns config`.
#[derive(Parser)]
pub struct ConfigOpts {}

/// SNS config commands.
/// SNS config command line arguments.
#[derive(Parser)]
#[clap(name("config"))]
pub struct SnsConfigOpts {
/// `dfx sns config` subcommand arguments.
#[clap(subcommand)]
subcmd: SubCommand,
}

/// Command line options for `sdx sns` subcommands.
#[derive(Parser)]
enum SubCommand {
/// Command line options for creating an SNS configuration.
Create(create::CreateOpts),
/// Command line options for validating an SNS configuration.
Validate(validate::ValidateOpts),
}

/// Executes `dfx sns config` and its subcommands.
pub fn exec(env: &dyn Environment, opts: SnsConfigOpts) -> DfxResult {
match opts.subcmd {
SubCommand::Create(v) => create::exec(env, v),
Expand Down
2 changes: 2 additions & 0 deletions src/dfx/src/commands/sns/config/validate.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
//! Code for executing `dfx sns config validate`
use crate::lib::error::DfxResult;
use crate::Environment;

Expand All @@ -9,6 +10,7 @@ use clap::Parser;
#[derive(Parser)]
pub struct ValidateOpts {}

/// Executes `dfx sns config validate`
pub fn exec(env: &dyn Environment, _opts: ValidateOpts) -> DfxResult {
let config = env.get_config_or_anyhow()?;
let path = config.get_project_root().join(sns::CONFIG_FILE_NAME);
Expand Down
24 changes: 24 additions & 0 deletions src/dfx/src/commands/sns/deploy.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
//! Code for the command line `dfx sns deploy`.
use crate::lib::error::DfxResult;
use crate::Environment;

use crate::lib::sns;
use crate::lib::sns::deploy::deploy_sns;
use clap::Parser;

/// Creates an SNS on a network.
///
/// # Arguments
/// - `env` - The execution environment, including the network to deploy to and connection credentials.
/// - `opts` - Deployment options.
#[derive(Parser)]
pub struct DeployOpts {}

/// Executes the command line `dfx sns deploy`.
pub fn exec(env: &dyn Environment, _opts: DeployOpts) -> DfxResult {
let config = env.get_config_or_anyhow()?;
let path = config.get_project_root().join(sns::CONFIG_FILE_NAME);

deploy_sns(env, &path)?;
Ok(())
}
2 changes: 2 additions & 0 deletions src/dfx/src/commands/sns/import.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
//! Code for the command line `dfx sns import`
use crate::lib::error::DfxResult;
use crate::lib::project::import::import_canister_definitions;
use crate::lib::project::network_mappings::get_network_mappings;
Expand All @@ -19,6 +20,7 @@ pub struct SnsImportOpts {
network_mapping: Vec<String>,
}

/// Executes the command line `dfx sns import`.
pub fn exec(env: &dyn Environment, opts: SnsImportOpts) -> DfxResult {
let config = env.get_config_or_anyhow()?;
let mut config = config.as_ref().clone();
Expand Down
14 changes: 13 additions & 1 deletion src/dfx/src/commands/sns/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
//! Command line interface for `dfx sns`.
#![warn(clippy::missing_docs_in_private_items)]
use crate::{
commands::sns::config::SnsConfigOpts,
commands::sns::import::SnsImportOpts,
Expand All @@ -7,27 +9,37 @@ use crate::{
use clap::Parser;

mod config;
mod deploy;
mod import;

/// SNS commands.
/// Options for `dfx sns`.
#[derive(Parser)]
#[clap(name("sns"))]
pub struct SnsOpts {
/// Arguments and flags for subcommands.
#[clap(subcommand)]
subcmd: SubCommand,
}

/// Subcommands of `dfx sns`
#[derive(Parser)]
enum SubCommand {
/// Subcommands for working with configuration.
#[clap(hide(true))]
Config(SnsConfigOpts),
/// Subcommand for creating an SNS.
#[clap(hide(true))]
Deploy(deploy::DeployOpts),
/// Subcommand for importing sns API definitions and canister IDs.
#[clap(hide(true))]
Import(SnsImportOpts),
}

/// Executes `dfx sns` and its subcommands.
pub fn exec(env: &dyn Environment, cmd: SnsOpts) -> DfxResult {
match cmd.subcmd {
SubCommand::Config(v) => config::exec(env, v),
SubCommand::Import(v) => import::exec(env, v),
SubCommand::Deploy(v) => deploy::exec(env, v),
}
}
3 changes: 3 additions & 0 deletions src/dfx/src/lib/nns/install_nns.rs
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,9 @@ async fn get_subnet_id(agent: &Agent) -> anyhow::Result<Principal> {
/// The NNS canisters use the very first few canister IDs; they must be available.
#[context("Failed to verify that the network is empty; dfx nns install must be run just after dfx start --clean")]
async fn verify_nns_canister_ids_are_available(agent: &Agent) -> anyhow::Result<()> {
/// Checks that the canister is unused on the given network.
///
/// The network is queried directly; local state such as canister_ids.json has no effect on this function.
async fn verify_canister_id_is_available(
agent: &Agent,
canister_id: &str,
Expand Down
2 changes: 2 additions & 0 deletions src/dfx/src/lib/nns/mod.rs
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
//! Code for managing the network nervous system.
#![warn(clippy::missing_docs_in_private_items)]
pub mod install_nns;
2 changes: 2 additions & 0 deletions src/dfx/src/lib/sns/create_config.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
//! Code for creating SNS configurations
use anyhow::{anyhow, Context};
use fn_error_context::context;
use std::path::Path;
Expand All @@ -6,6 +7,7 @@ use std::process::{self, Command};
use crate::lib::error::DfxResult;
use crate::Environment;

/// Ceates an SNS configuration template.
#[context("Failed to create sns config at {}.", path.display())]
pub fn create_config(env: &dyn Environment, path: &Path) -> DfxResult {
let cli_name = "sns";
Expand Down
19 changes: 19 additions & 0 deletions src/dfx/src/lib/sns/deploy.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
//! Code for creating an SNS.
use fn_error_context::context;
use std::ffi::OsString;
use std::path::Path;

use crate::lib::error::DfxResult;
use crate::lib::sns::sns_cli::call_sns_cli;
use crate::Environment;

/// Creates an SNS. This requires funds but no proposal.
#[context("Failed to deploy SNS with config: {}", path.display())]
pub fn deploy_sns(env: &dyn Environment, path: &Path) -> DfxResult<String> {
let args = vec![
OsString::from("deploy"),
OsString::from("--init-config-file"),
OsString::from(path),
];
call_sns_cli(env, &args).map(|stdout| format!("Deployed SNS: {}\n{}", path.display(), stdout))
}
4 changes: 4 additions & 0 deletions src/dfx/src/lib/sns/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
//! Code for decentralizing dapps
#![warn(clippy::missing_docs_in_private_items)]
pub mod create_config;
pub mod deploy;
pub mod sns_cli;
pub mod validate_config;

/// The default location of an SNS configuration file.
pub const CONFIG_FILE_NAME: &str = "sns.yml";
18 changes: 14 additions & 4 deletions src/dfx/src/lib/sns/sns_cli.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
//! Library for calling the `sns` command line tool.
use anyhow::{anyhow, Context};
use fn_error_context::context;
use std::ffi::OsStr;
use std::path::Path;
use std::process::{self, Command};

use crate::lib::error::DfxResult;
use crate::Environment;

/// Calls the sns cli tool from the SNS codebase in the ic repo.
/// Calls the sns command line tool from the SNS codebase in the ic repo.
#[context("Failed to call sns CLI.")]
pub fn call_sns_cli<S, I>(env: &dyn Environment, args: I) -> DfxResult<String>
where
Expand All @@ -18,8 +20,11 @@ where
.get_cache()
.get_binary_command_path(cli_name)
.with_context(|| format!("Could not find bundled binary '{cli_name}'."))?;
let mut command = Command::new(sns_cli);
let mut command = Command::new(&sns_cli);
command.args(args);
// The sns command line tool itself calls dfx; it should call this dfx.
// The sns command line tool should not rely on commands not packaged with dfx.
command.env("PATH", sns_cli.parent().unwrap_or_else(|| Path::new(".")));
command
.stdin(process::Stdio::null())
.output()
Expand All @@ -28,10 +33,15 @@ where
if output.status.success() {
Ok(String::from_utf8_lossy(&output.stdout).into_owned())
} else {
let args: Vec<_> = command
.get_args()
.into_iter()
.map(OsStr::to_string_lossy)
.collect();
Err(anyhow!(
"SNS cli call failed:\n{:?} {:?}\nStdout:\n{:?}\n\nStderr:\n{:?}",
"SNS cli call failed:\n{:?} {}\nStdout:\n{}\n\nStderr:\n{}",
command.get_program(),
command.get_args(),
args.join(" "),
String::from_utf8_lossy(&output.stdout),
String::from_utf8_lossy(&output.stderr)
))
Expand Down
3 changes: 2 additions & 1 deletion src/dfx/src/lib/sns/validate_config.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
//! Code for checking SNS config file validity
use fn_error_context::context;
use std::ffi::OsString;
use std::path::Path;
Expand All @@ -6,7 +7,7 @@ use crate::lib::error::DfxResult;
use crate::lib::sns::sns_cli::call_sns_cli;
use crate::Environment;

///
/// Checks whether an SNS configuration file is valid.
#[context("Failed to validate SNS config at {}.", path.display())]
pub fn validate_config(env: &dyn Environment, path: &Path) -> DfxResult<String> {
let args = vec![
Expand Down