Skip to content

Commit

Permalink
feat: dfx sns deploy (#2571)
Browse files Browse the repository at this point in the history
# Description
This PR implements the previously agreed-upon command:
```
dfx sns deploy
```
The implementation covers only the core use case and does not cover enhancements such as specifying the location of the config file.

# Changes
- Implemented `dfx sns deploy`
- Fixed doc lints; requiring documentation was not previously enforced so there were quite a few functions with missing documentation.  I have filled them in, somewhat minimally but hopefully sufficiently to be useful.
  • Loading branch information
bitdivine authored Sep 20, 2022
1 parent be94446 commit 4fa74b9
Show file tree
Hide file tree
Showing 21 changed files with 194 additions and 15 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ If you want to disable this behavior, you can config it in dfx.json:
}
}

### feat: Add dfx sns deploy

This allows users to deploy a set of SNS canisters.

### fix: `cargo run -p dfx -- --version` prints correct version

### feat: add --mode=auto
Expand Down
49 changes: 46 additions & 3 deletions docs/cli-reference/dfx-sns.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,9 @@ Depending on the `dfx sns` subcommand you specify, additional arguments, options

| Command | Description |
|-------------------------------------|-------------------------------------------------------------------------------|
| [`create`](#_dfx_sns_create) | Creates an SNS configuration template |
| [`create`](#_dfx_sns_create) | Creates an SNS configuration template. |
| [`validate`](#_dfx_sns_validate) | Checks whether the sns config file is valid. |
| [`deploy`](#_dfx_sns_deploy) | Deploys SNS canisters according to the local config. |
| `help` | Displays usage information message for a specified subcommand. |

To view usage information for a specific subcommand, specify the subcommand and the `--help` flag. For example, to see usage information for `dfx sns validate`, you can run the following command:
Expand Down Expand Up @@ -74,6 +75,48 @@ You can use the following optional flags with the `dfx sns validate` command.
You can use the `dfx sns validate` command to verify that a configuration template is valid. It is not; it needs details such as token name:

``` bash
dfx sns create
dfx sns validate
dfx sns config create
```
Fill in the blank fields, then:
``` bash
dfx sns config validate
```

## dfx sns deploy

Use the `dfx sns deploy` command to create SNS canisters according to the local configuration file.

Note: Deploying SNS canisters does not require a proposal, however there is a hefty fee. Please don't create canisters on mainnet until you have tested your configuration locally and are sure that you are happy with it.

### Basic usage

``` bash
dfx sns deploy
```

### Flags

You can use the following optional flags with the `dfx sns deploy` command.

| Flag | Description |
|-------------------|-------------------------------|
| `-h`, `--help` | Displays usage information. |
| `-V`, `--version` | Displays version information. |

### Examples

Create an SNS on the local testnet:
``` bash
dfx sns config create
```
Fill in the blank fields, then:
``` bash
dfx sns config validate
dfx sns deploy
```
You can now verify that the sns canisters have been created. E.g.:
```
dfx canister info sns_root
dfx canister info sns_ledger
```

File renamed without changes
File renamed without changes.
38 changes: 34 additions & 4 deletions e2e/tests-dfx/sns.bash
Original file line number Diff line number Diff line change
Expand Up @@ -34,16 +34,46 @@ SNS_CONFIG_FILE_NAME="sns.yml"

@test "sns config validate approves a valid configuration" {
dfx_new
cp "${BATS_TEST_DIRNAME}/../assets/sns/valid_sns_init_config.yaml" "$SNS_CONFIG_FILE_NAME"
cp "${BATS_TEST_DIRNAME}/../assets/sns/logo.svg" .
install_asset sns/valid
assert_command dfx sns config validate
assert_match 'SNS config file is valid'
}

@test "sns config validate identifies a missing key" {
dfx_new
grep -v token_name "${BATS_TEST_DIRNAME}/../assets/sns/valid_sns_init_config.yaml" > "$SNS_CONFIG_FILE_NAME"
cp "${BATS_TEST_DIRNAME}/../assets/sns/logo.svg" .
install_asset sns/valid
grep -v token_name "${SNS_CONFIG_FILE_NAME}" | sponge "$SNS_CONFIG_FILE_NAME"
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 "Error encountered when generating the SnsInitPayload: Couldn't open initial parameters file"
}

@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="qaa6y-5yaaa-aaaaa-aaafa-cai"' dfx.json | sponge dfx.json
ls candid
cat dfx.json
dfx nns import --network-mapping local
ls candid
cat dfx.json
install_asset sns/valid
dfx sns config validate
dfx sns deploy
}
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";
Loading

0 comments on commit 4fa74b9

Please sign in to comment.