diff --git a/implementations/rust/ockam/ockam_api/src/cli_state/cli_state.rs b/implementations/rust/ockam/ockam_api/src/cli_state/cli_state.rs index 18e04a50169..81400915588 100644 --- a/implementations/rust/ockam/ockam_api/src/cli_state/cli_state.rs +++ b/implementations/rust/ockam/ockam_api/src/cli_state/cli_state.rs @@ -4,7 +4,7 @@ use tokio::sync::broadcast::{channel, Receiver, Sender}; use ockam::SqlxDatabase; use ockam_core::env::get_env_with_default; -use ockam_node::database::{DatabaseConfiguration, OCKAM_SQLITE_IN_MEMORY}; +use ockam_node::database::{DatabaseConfiguration, DatabaseType, OCKAM_SQLITE_IN_MEMORY}; use ockam_node::Executor; use crate::cli_state::error::Result; @@ -136,11 +136,19 @@ impl CliState { } /// Stop nodes and remove all the directories storing state + /// Don't touch the database data if Postgres is used and reset was called accidentally. pub async fn reset(&self) -> Result<()> { - self.delete_all_named_identities().await?; - self.delete_all_nodes().await?; - self.delete_all_named_vaults().await?; - self.delete().await + if Self::make_database_configuration(&self.mode)?.database_type() == DatabaseType::Postgres + { + Err(CliStateError::InvalidOperation( + "Cannot reset the database when using Postgres".to_string(), + )) + } else { + self.delete_all_named_identities().await?; + self.delete_all_nodes().await?; + self.delete_all_named_vaults().await?; + self.delete().await + } } /// Removes all the directories storing state without loading the current state @@ -152,7 +160,6 @@ impl CliState { /// Delete the local database and log files pub async fn delete(&self) -> Result<()> { - self.database.drop_postgres_node_tables().await?; self.delete_local_data() } @@ -363,82 +370,76 @@ impl CliStateMode { mod tests { use super::*; use itertools::Itertools; - use ockam_node::database::DatabaseType; - use sqlx::any::AnyRow; - use sqlx::Row; + use ockam_node::database::{skip_if_postgres, DatabaseType}; use std::fs; use tempfile::NamedTempFile; #[tokio::test] async fn test_reset() -> Result<()> { - let db_file = NamedTempFile::new().unwrap(); - let cli_state_directory = db_file.path().parent().unwrap().join(random_name()); - let mode = CliStateMode::Persistent(cli_state_directory.clone()); - let db = SqlxDatabase::create(&CliState::make_database_configuration(&mode)?).await?; - db.drop_all_postgres_tables().await?; - let cli = CliState::create(mode).await?; - - // create 2 vaults - // the second vault is using a separate file - let _vault1 = cli.get_or_create_named_vault("vault1").await?; - let _vault2 = cli.get_or_create_named_vault("vault2").await?; - - // create 2 identities - let identity1 = cli - .create_identity_with_name_and_vault("identity1", "vault1") - .await?; - let identity2 = cli - .create_identity_with_name_and_vault("identity2", "vault2") - .await?; - - // create 2 nodes - let _node1 = cli - .create_node_with_identifier("node1", &identity1.identifier()) - .await?; - let _node2 = cli - .create_node_with_identifier("node2", &identity2.identifier()) - .await?; - - let file_names = list_file_names(&cli_state_directory); - let expected = match cli.database_configuration()?.database_type() { - DatabaseType::Sqlite => vec![ - "vault-vault2".to_string(), - "application_database.sqlite3".to_string(), - "database.sqlite3".to_string(), - ], - DatabaseType::Postgres => vec!["vault-vault2".to_string()], - }; - - assert_eq!( - file_names.iter().sorted().as_slice(), - expected.iter().sorted().as_slice() - ); - - // reset the local state - cli.reset().await?; - let result = fs::read_dir(&cli_state_directory); - assert!(result.is_ok(), "the cli state directory is not deleted"); - - match cli.database_configuration()?.database_type() { - DatabaseType::Sqlite => { - // When the database is SQLite, only the application database must remain - let file_names = list_file_names(&cli_state_directory); - let expected = vec!["application_database.sqlite3".to_string()]; - assert_eq!(file_names, expected); - } - DatabaseType::Postgres => { - // When the database is Postgres, only the journey tables must remain - let tables: Vec = sqlx::query( - "SELECT tablename::text FROM pg_tables WHERE schemaname = 'public'", - ) - .fetch_all(&*db.pool) - .await - .unwrap(); - let actual: Vec = tables.iter().map(|r| r.get(0)).sorted().collect(); - assert_eq!(actual, vec!["host_journey", "project_journey"]); - } - }; - Ok(()) + // We don't need to test reset with Postgres since we don't want reset be used accidentally + // with a Postgres database (resetting in that case throws an error). + skip_if_postgres(|| async { + let db_file = NamedTempFile::new().unwrap(); + let cli_state_directory = db_file.path().parent().unwrap().join(random_name()); + let mode = CliStateMode::Persistent(cli_state_directory.clone()); + let cli = CliState::create(mode).await?; + + // create 2 vaults + // the second vault is using a separate file + let _vault1 = cli.get_or_create_named_vault("vault1").await?; + let _vault2 = cli.get_or_create_named_vault("vault2").await?; + + // create 2 identities + let identity1 = cli + .create_identity_with_name_and_vault("identity1", "vault1") + .await?; + let identity2 = cli + .create_identity_with_name_and_vault("identity2", "vault2") + .await?; + + // create 2 nodes + let _node1 = cli + .create_node_with_identifier("node1", &identity1.identifier()) + .await?; + let _node2 = cli + .create_node_with_identifier("node2", &identity2.identifier()) + .await?; + + let file_names = list_file_names(&cli_state_directory); + + // this test is not executed with Postgres + let expected = match cli.database_configuration()?.database_type() { + DatabaseType::Sqlite => vec![ + "vault-vault2".to_string(), + "application_database.sqlite3".to_string(), + "database.sqlite3".to_string(), + ], + DatabaseType::Postgres => vec![], + }; + + assert_eq!( + file_names.iter().sorted().as_slice(), + expected.iter().sorted().as_slice() + ); + + // reset the local state + cli.reset().await?; + let result = fs::read_dir(&cli_state_directory); + assert!(result.is_ok(), "the cli state directory is not deleted"); + + // this test is not executed with Postgres + match cli.database_configuration()?.database_type() { + DatabaseType::Sqlite => { + // When the database is SQLite, only the application database must remain + let file_names = list_file_names(&cli_state_directory); + let expected = vec!["application_database.sqlite3".to_string()]; + assert_eq!(file_names, expected); + } + DatabaseType::Postgres => (), + }; + Ok(()) + }) + .await } /// HELPERS diff --git a/implementations/rust/ockam/ockam_api/src/cli_state/nodes.rs b/implementations/rust/ockam/ockam_api/src/cli_state/nodes.rs index a1b0bd9bb89..b66170641b8 100644 --- a/implementations/rust/ockam/ockam_api/src/cli_state/nodes.rs +++ b/implementations/rust/ockam/ockam_api/src/cli_state/nodes.rs @@ -18,7 +18,6 @@ use sysinfo::{Pid, ProcessStatus, ProcessesToUpdate, System}; use crate::cli_state::{random_name, NamedVault, Result}; use crate::cli_state::{CliState, CliStateError}; -use crate::cloud::project::Project; use crate::colors::color_primary; use crate::config::lookup::InternetAddress; @@ -27,17 +26,15 @@ use crate::{fmt_warn, ConnectionStatus}; /// The methods below support the creation and update of local nodes impl CliState { /// Create a node, with some optional associated values, and start it - #[instrument(skip_all, fields(node_name = node_name, identity_name = identity_name.clone(), project_name = project_name.clone() - ))] + #[instrument(skip_all, fields(node_name = node_name, identity_name = identity_name.clone()))] pub async fn start_node_with_optional_values( &self, node_name: &str, identity_name: &Option, - project_name: &Option, tcp_listener: Option<&TcpListener>, ) -> Result { let mut node = self - .create_node_with_optional_values(node_name, identity_name, project_name) + .create_node_with_optional_identity(node_name, identity_name) .await?; if node.pid.is_none() { let pid = process::id(); @@ -53,17 +50,12 @@ impl CliState { Ok(node) } - /// Create a node, with some optional associated values: - /// - /// - an identity name. That identity is used by the `NodeManager` to create secure channels - /// - a project name. It is used to create policies on resources provisioned on a node (like a TCP outlet for example) - #[instrument(skip_all, fields(node_name = node_name, identity_name = identity_name.clone(), project_name = project_name.clone() - ))] - pub async fn create_node_with_optional_values( + /// Create a node, with an optional identity name. That identity is used by the `NodeManager` to create secure channels + #[instrument(skip_all, fields(node_name = node_name, identity_name = identity_name.clone()))] + pub async fn create_node_with_optional_identity( &self, node_name: &str, identity_name: &Option, - project_name: &Option, ) -> Result { let identity = match identity_name { Some(name) => self.get_named_identity(name).await?, @@ -72,7 +64,6 @@ impl CliState { let node = self .create_node_with_identifier(node_name, &identity.identifier()) .await?; - self.set_node_project(node_name, project_name).await?; Ok(node) } @@ -134,27 +125,6 @@ impl CliState { Ok(()) } - /// This method can be used to start a local node first - /// then create a project, and associate it to the node - #[instrument(skip_all, fields(node_name = node_name, project_name = project_name.clone()))] - pub async fn set_node_project( - &self, - node_name: &str, - project_name: &Option, - ) -> Result<()> { - let project = match project_name { - Some(name) => Some(self.projects().get_project_by_name(name).await?), - None => self.projects().get_default_project().await.ok(), - }; - - if let Some(project) = project { - self.nodes_repository() - .set_node_project_name(node_name, project.name()) - .await? - }; - Ok(()) - } - /// Remove a node: /// /// - remove it from the repository @@ -381,23 +351,6 @@ impl CliState { } } - /// Return the project associated to a node if there is one - #[instrument(skip_all, fields(node_name = node_name))] - pub async fn get_node_project(&self, node_name: &str) -> Result { - match self - .nodes_repository() - .get_node_project_name(node_name) - .await? - { - Some(project_name) => self.projects().get_project_by_name(&project_name).await, - None => Err(Error::new( - Origin::Api, - Kind::NotFound, - format!("there is no project associated to node {node_name}"), - ))?, - } - } - /// Return the stdout log file used by a node #[instrument(skip_all, fields(node_name = node_name))] pub fn stdout_logs(&self, node_name: &str) -> Result { @@ -701,7 +654,6 @@ impl NodeInfo { #[cfg(test)] mod tests { use super::*; - use crate::cloud::project::models::ProjectModel; use crate::config::lookup::InternetAddress; use std::net::SocketAddr; use std::str::FromStr; @@ -799,12 +751,12 @@ mod tests { } #[tokio::test] - async fn test_create_node_with_optional_values() -> Result<()> { + async fn test_create_node_with_optional_identity() -> Result<()> { let cli = CliState::test().await?; // a node can be created with just a name let node = cli - .create_node_with_optional_values("node-1", &None, &None) + .create_node_with_optional_identity("node-1", &None) .await?; let result = cli.get_node(&node.name()).await?; assert_eq!(result.name(), node.name()); @@ -812,44 +764,11 @@ mod tests { // a node can be created with a name and an existing identity let identity = cli.create_identity_with_name("name").await?; let node = cli - .create_node_with_optional_values("node-2", &Some(identity.name()), &None) + .create_node_with_optional_identity("node-2", &Some(identity.name())) .await?; let result = cli.get_node(&node.name()).await?; assert_eq!(result.identifier(), identity.identifier()); - // a node can be created with a name, an existing identity and an existing project - let project = ProjectModel { - id: "project_id".to_string(), - name: "project_name".to_string(), - space_name: "1".to_string(), - access_route: "".to_string(), - users: vec![], - space_id: "1".to_string(), - identity: None, - project_change_history: None, - authority_access_route: None, - authority_identity: None, - okta_config: None, - kafka_config: None, - version: None, - running: None, - operation_id: None, - user_roles: vec![], - }; - cli.projects() - .import_and_store_project(project.clone()) - .await?; - - let node = cli - .create_node_with_optional_values( - "node-4", - &Some(identity.name()), - &Some(project.name.clone()), - ) - .await?; - let result = cli.get_node_project(&node.name()).await?; - assert_eq!(result.name(), &project.name); - Ok(()) } } diff --git a/implementations/rust/ockam/ockam_api/src/cli_state/spaces.rs b/implementations/rust/ockam/ockam_api/src/cli_state/spaces.rs index 55cb111af67..30edf49d79b 100644 --- a/implementations/rust/ockam/ockam_api/src/cli_state/spaces.rs +++ b/implementations/rust/ockam/ockam_api/src/cli_state/spaces.rs @@ -82,36 +82,40 @@ impl CliState { mod test { use super::*; use crate::cloud::subscription::SubscriptionName; + use ockam_node::database::skip_if_postgres; #[tokio::test] async fn test_cli_spaces() -> Result<()> { - let cli = CliState::test().await?; + skip_if_postgres(|| async { + let cli = CliState::test().await?; - // the first created space becomes the default - let space1 = cli - .store_space( - "1", - "name1", - vec!["me@ockam.io", "you@ockam.io"], - Some(&Subscription::new( - SubscriptionName::Gold, - false, - None, - None, - None, - )), - ) - .await?; - let result = cli.get_default_space().await?; - assert_eq!(result, space1); + // the first created space becomes the default + let space1 = cli + .store_space( + "1", + "name1", + vec!["me@ockam.io", "you@ockam.io"], + Some(&Subscription::new( + SubscriptionName::Gold, + false, + None, + None, + None, + )), + ) + .await?; + let result = cli.get_default_space().await?; + assert_eq!(result, space1); - // the store method can be used to update a space - let updated_space1 = cli - .store_space("1", "name1", vec!["them@ockam.io"], None) - .await?; - let result = cli.get_default_space().await?; - assert_eq!(result, updated_space1); + // the store method can be used to update a space + let updated_space1 = cli + .store_space("1", "name1", vec!["them@ockam.io"], None) + .await?; + let result = cli.get_default_space().await?; + assert_eq!(result, updated_space1); - Ok(()) + Ok(()) + }) + .await } } diff --git a/implementations/rust/ockam/ockam_api/src/cli_state/storage/nodes_repository.rs b/implementations/rust/ockam/ockam_api/src/cli_state/storage/nodes_repository.rs index a1baa338361..c48ec4da73b 100644 --- a/implementations/rust/ockam/ockam_api/src/cli_state/storage/nodes_repository.rs +++ b/implementations/rust/ockam/ockam_api/src/cli_state/storage/nodes_repository.rs @@ -71,12 +71,6 @@ pub trait NodesRepository: Send + Sync + 'static { /// Unset the process id of a node async fn set_no_node_pid(&self, node_name: &str) -> Result<()>; - - /// Associate a node to a project - async fn set_node_project_name(&self, node_name: &str, project_name: &str) -> Result<()>; - - /// Return the name of the project associated to a node - async fn get_node_project_name(&self, node_name: &str) -> Result>; } #[async_trait] @@ -151,12 +145,4 @@ impl NodesRepository for AutoRetry { async fn set_no_node_pid(&self, node_name: &str) -> Result<()> { retry!(self.wrapped.set_no_node_pid(node_name)) } - - async fn set_node_project_name(&self, node_name: &str, project_name: &str) -> Result<()> { - retry!(self.wrapped.set_node_project_name(node_name, project_name)) - } - - async fn get_node_project_name(&self, node_name: &str) -> Result> { - retry!(self.wrapped.get_node_project_name(node_name)) - } } diff --git a/implementations/rust/ockam/ockam_api/src/cli_state/storage/nodes_repository_sql.rs b/implementations/rust/ockam/ockam_api/src/cli_state/storage/nodes_repository_sql.rs index f7b8da3a724..f60290e3677 100644 --- a/implementations/rust/ockam/ockam_api/src/cli_state/storage/nodes_repository_sql.rs +++ b/implementations/rust/ockam/ockam_api/src/cli_state/storage/nodes_repository_sql.rs @@ -161,9 +161,6 @@ impl NodesRepository for NodesSqlxDatabase { sqlx::query("DELETE FROM tcp_outlet_status WHERE node_name = $1").bind(node_name); query.execute(&mut *transaction).await.void()?; - let query = sqlx::query("DELETE FROM node_project WHERE node_name = $1").bind(node_name); - query.execute(&mut *transaction).await.void()?; - transaction.commit().await.void() } @@ -224,30 +221,6 @@ impl NodesRepository for NodesSqlxDatabase { let query = query("UPDATE node SET pid=NULL WHERE name = $1").bind(node_name); query.execute(&*self.database.pool).await.void() } - - async fn set_node_project_name(&self, node_name: &str, project_name: &str) -> Result<()> { - let query = query( - r#" - INSERT INTO node_project (node_name, project_name) - VALUES ($1, $2) - ON CONFLICT (node_name) - DO UPDATE SET project_name = $2"#, - ) - .bind(node_name) - .bind(project_name); - Ok(query.execute(&*self.database.pool).await.void()?) - } - - async fn get_node_project_name(&self, node_name: &str) -> Result> { - let query = - query("SELECT project_name FROM node_project WHERE node_name = $1").bind(node_name); - let row: Option = query - .fetch_optional(&*self.database.pool) - .await - .into_core()?; - let project_name: Option = row.map(|r| r.get(0)); - Ok(project_name) - } } // Database serialization / deserialization @@ -420,23 +393,6 @@ mod test { .await } - #[tokio::test] - async fn test_node_project() -> Result<()> { - with_dbs(|db| async move { - let repository: Arc = Arc::new(NodesSqlxDatabase::new(db)); - - // a node can be associated to a project name - repository - .set_node_project_name("node_name", "project1") - .await?; - let result = repository.get_node_project_name("node_name").await?; - assert_eq!(result, Some("project1".into())); - - Ok(()) - }) - .await - } - /// HELPERS async fn create_identity() -> Result { let identities = identities().await?; diff --git a/implementations/rust/ockam/ockam_api/src/cli_state/storage/projects_repository_sql.rs b/implementations/rust/ockam/ockam_api/src/cli_state/storage/projects_repository_sql.rs index 3f154578c00..a6a1366c7cd 100644 --- a/implementations/rust/ockam/ockam_api/src/cli_state/storage/projects_repository_sql.rs +++ b/implementations/rust/ockam/ockam_api/src/cli_state/storage/projects_repository_sql.rs @@ -635,12 +635,12 @@ mod test { SpacesRepository, SpacesSqlxDatabase, UsersRepository, UsersSqlxDatabase, }; use crate::cloud::enroll::auth0::UserInfo; - use ockam_node::database::with_dbs; + use ockam_node::database::with_sqlite_dbs; use std::sync::Arc; #[tokio::test] async fn test_repository() -> Result<()> { - with_dbs(|db| async move { + with_sqlite_dbs(|db| async move { let repository: Arc = Arc::new(UsersSqlxDatabase::new(db.clone())); repository .store_user(&UserInfo { @@ -736,7 +736,7 @@ mod test { #[tokio::test] async fn test_store_project_space() -> Result<()> { - with_dbs(|db| async move { + with_sqlite_dbs(|db| async move { let projects_repository: Arc = Arc::new(ProjectsSqlxDatabase::new(db.clone())); diff --git a/implementations/rust/ockam/ockam_api/src/cli_state/storage/spaces_repository_sql.rs b/implementations/rust/ockam/ockam_api/src/cli_state/storage/spaces_repository_sql.rs index 3a08b821c8a..3e272f72ccc 100644 --- a/implementations/rust/ockam/ockam_api/src/cli_state/storage/spaces_repository_sql.rs +++ b/implementations/rust/ockam/ockam_api/src/cli_state/storage/spaces_repository_sql.rs @@ -333,13 +333,13 @@ impl SubscriptionRow { mod test { use super::*; use crate::cloud::subscription::SubscriptionName; - use ockam_node::database::with_dbs; + use ockam_node::database::with_sqlite_dbs; use std::ops::Add; use time::ext::NumericalDuration; #[tokio::test] async fn test_repository() -> Result<()> { - with_dbs(|db| async move { + with_sqlite_dbs(|db| async move { let repository = SpacesSqlxDatabase::new(db); // create and store 2 spaces diff --git a/implementations/rust/ockam/ockam_api/src/cli_state/storage/users_repository_sql.rs b/implementations/rust/ockam/ockam_api/src/cli_state/storage/users_repository_sql.rs index 89214e1dde2..7f6b628ec90 100644 --- a/implementations/rust/ockam/ockam_api/src/cli_state/storage/users_repository_sql.rs +++ b/implementations/rust/ockam/ockam_api/src/cli_state/storage/users_repository_sql.rs @@ -218,12 +218,12 @@ impl UserRow { mod test { use super::*; - use ockam_node::database::with_dbs; + use ockam_node::database::with_sqlite_dbs; use std::sync::Arc; #[tokio::test] async fn test_repository() -> Result<()> { - with_dbs(|db| async move { + with_sqlite_dbs(|db| async move { let repository: Arc = Arc::new(UsersSqlxDatabase::new(db)); let my_email_address: EmailAddress = "me@ockam.io".try_into().unwrap(); diff --git a/implementations/rust/ockam/ockam_api/src/cli_state/vaults.rs b/implementations/rust/ockam/ockam_api/src/cli_state/vaults.rs index a1f0af37f64..bd624f34233 100644 --- a/implementations/rust/ockam/ockam_api/src/cli_state/vaults.rs +++ b/implementations/rust/ockam/ockam_api/src/cli_state/vaults.rs @@ -556,6 +556,7 @@ mod tests { use super::*; use ockam::identity::models::{PurposeKeyAttestation, PurposeKeyAttestationSignature}; use ockam::identity::Purpose; + use ockam_node::database::skip_if_postgres; use ockam_vault::{ ECDSASHA256CurveP256SecretKey, ECDSASHA256CurveP256Signature, HandleToSecret, SigningSecret, SigningSecretKeyHandle, X25519SecretKey, X25519SecretKeyHandle, @@ -731,14 +732,24 @@ mod tests { assert!(result.path_as_string().unwrap().contains("vault-secrets")); // if we reset, we can check that the first vault gets the user defined name - // instead of default - cli.reset().await?; - let cli = CliState::test().await?; - let result = cli - .create_named_vault(Some("secrets".to_string()), None, UseAwsKms::No) - .await?; - assert_eq!(result.name(), "secrets".to_string()); - assert_eq!(result.vault_type(), VaultType::database(UseAwsKms::No)); + // instead of default. + // We only test this for sqlite since we can't reset with postgres. + + skip_if_postgres(move || { + let cli_clone = cli.clone(); + async move { + cli_clone.reset().await?; + let cli = CliState::test().await?; + let result = cli + .create_named_vault(Some("secrets".to_string()), None, UseAwsKms::No) + .await?; + assert_eq!(result.name(), "secrets".to_string()); + assert_eq!(result.vault_type(), VaultType::database(UseAwsKms::No)); + let result: Result<()> = Ok(()); + result + } + }) + .await?; Ok(()) } diff --git a/implementations/rust/ockam/ockam_api/src/nodes/service/in_memory_node.rs b/implementations/rust/ockam/ockam_api/src/nodes/service/in_memory_node.rs index eaf2aa29929..cbdea5a9cc2 100644 --- a/implementations/rust/ockam/ockam_api/src/nodes/service/in_memory_node.rs +++ b/implementations/rust/ockam/ockam_api/src/nodes/service/in_memory_node.rs @@ -147,7 +147,6 @@ impl InMemoryNode { .start_node_with_optional_values( &defaults.node_name, &Some(identity_name.to_string()), - &project_name, Some(&tcp_listener), ) .await diff --git a/implementations/rust/ockam/ockam_api/src/test_utils/mod.rs b/implementations/rust/ockam/ockam_api/src/test_utils/mod.rs index ccdba1ed21e..6a0beabfe64 100644 --- a/implementations/rust/ockam/ockam_api/src/test_utils/mod.rs +++ b/implementations/rust/ockam/ockam_api/src/test_utils/mod.rs @@ -70,7 +70,7 @@ pub async fn start_manager_for_tests( let node_name = random_name(); cli_state - .start_node_with_optional_values(&node_name, &None, &None, Some(&tcp_listener)) + .start_node_with_optional_values(&node_name, &None, Some(&tcp_listener)) .await .unwrap(); diff --git a/implementations/rust/ockam/ockam_app_lib/src/enroll/enroll_user.rs b/implementations/rust/ockam/ockam_app_lib/src/enroll/enroll_user.rs index 6f42593f85f..cefb03d0784 100644 --- a/implementations/rust/ockam/ockam_app_lib/src/enroll/enroll_user.rs +++ b/implementations/rust/ockam/ockam_app_lib/src/enroll/enroll_user.rs @@ -208,10 +208,6 @@ impl AppState { .projects() .set_default_project(project.project_id()) .await?; - self.state() - .await - .set_node_project(&node_manager.node_name(), &Some(project.name().to_string())) - .await?; Ok(project) } } diff --git a/implementations/rust/ockam/ockam_app_lib/src/state/mod.rs b/implementations/rust/ockam/ockam_app_lib/src/state/mod.rs index 09f7968a74f..82f339c0aef 100644 --- a/implementations/rust/ockam/ockam_app_lib/src/state/mod.rs +++ b/implementations/rust/ockam/ockam_app_lib/src/state/mod.rs @@ -693,7 +693,7 @@ pub(crate) async fn make_node_manager( .into_diagnostic()?; let _ = cli_state - .start_node_with_optional_values(NODE_NAME, &None, &None, Some(&listener)) + .start_node_with_optional_values(NODE_NAME, &None, Some(&listener)) .await?; let trust_options = cli_state diff --git a/implementations/rust/ockam/ockam_app_lib/src/state/model_state_repository_sql.rs b/implementations/rust/ockam/ockam_app_lib/src/state/model_state_repository_sql.rs index 4e94ab6b524..36b77ed6990 100644 --- a/implementations/rust/ockam/ockam_app_lib/src/state/model_state_repository_sql.rs +++ b/implementations/rust/ockam/ockam_app_lib/src/state/model_state_repository_sql.rs @@ -157,13 +157,13 @@ impl PersistentIncomingServiceRow { #[cfg(test)] mod tests { use super::*; - use ockam::with_dbs; use ockam_api::nodes::models::portal::OutletStatus; use ockam_core::Address; + use ockam_node::database::with_sqlite_dbs; #[tokio::test] async fn store_and_load() -> ockam_core::Result<()> { - with_dbs(|db| async move { + with_sqlite_dbs(|db| async move { let repository: Arc = Arc::new(ModelStateSqlxDatabase::new(db.clone())); diff --git a/implementations/rust/ockam/ockam_command/src/authority/create.rs b/implementations/rust/ockam/ockam_command/src/authority/create.rs index 7223f546463..2122f365042 100644 --- a/implementations/rust/ockam/ockam_command/src/authority/create.rs +++ b/implementations/rust/ockam/ockam_command/src/authority/create.rs @@ -146,7 +146,7 @@ impl CreateCommand { }; opts.state - .create_node_with_optional_values(&self.node_name, &self.identity, &None) + .create_node_with_optional_identity(&self.node_name, &self.identity) .await?; // Construct the arguments list and re-execute the ockam @@ -303,7 +303,7 @@ impl CreateCommand { }; let node = state - .start_node_with_optional_values(&self.node_name, &Some(identity_name), &None, None) + .start_node_with_optional_values(&self.node_name, &Some(identity_name), None) .await?; state .set_tcp_listener_address(&node.name(), &self.tcp_listener_address) diff --git a/implementations/rust/ockam/ockam_command/src/node/create/foreground.rs b/implementations/rust/ockam/ockam_command/src/node/create/foreground.rs index aa7ff73a700..ffe6820664a 100644 --- a/implementations/rust/ockam/ockam_command/src/node/create/foreground.rs +++ b/implementations/rust/ockam/ockam_command/src/node/create/foreground.rs @@ -66,12 +66,7 @@ impl CreateCommand { }; let node_info = opts .state - .start_node_with_optional_values( - &node_name, - &self.identity, - &self.trust_opts.project_name, - Some(&tcp_listener), - ) + .start_node_with_optional_values(&node_name, &self.identity, Some(&tcp_listener)) .await?; debug!("node info persisted {node_info:?}"); diff --git a/implementations/rust/ockam/ockam_identity/src/identities/storage/change_history_repository_sql.rs b/implementations/rust/ockam/ockam_identity/src/identities/storage/change_history_repository_sql.rs index 047ce45afd6..1ea8ed0ce4c 100644 --- a/implementations/rust/ockam/ockam_identity/src/identities/storage/change_history_repository_sql.rs +++ b/implementations/rust/ockam/ockam_identity/src/identities/storage/change_history_repository_sql.rs @@ -198,21 +198,11 @@ mod tests { use crate::{identities, Identity}; use ockam_core::compat::sync::Arc; - use ockam_node::database::with_dbs; - - fn orchestrator_identity() -> (Identifier, ChangeHistory) { - let identifier = Identifier::from_str( - "I84502ce0d9a0a91bae29026b84e19be69fb4203a6bdd1424c85a43c812772a00", - ) - .unwrap(); - let change_history = ChangeHistory::import_from_string("81825858830101585385f6820181584104ebf9d78281a04f180029c12a74e994386c7c9fee24903f3bfe351497a9952758ee5f4b57d7ed6236ab5082ed85e1ae8c07d5600e0587f652d36727904b3e310df41a656a365d1a7836395d820181584050bf79071ecaf08a966228c712295a17da53994dc781a22103602afe656276ef83ba83a1004845b1e979e0944abff3cd8c7ceef834a8f5eeeca0e8f720fa38f4").unwrap(); - - (identifier, change_history) - } + use ockam_node::database::{with_dbs, with_sqlite_dbs}; #[tokio::test] async fn test_identities_repository_has_orchestrator_history() -> Result<()> { - with_dbs(|db| async move { + with_sqlite_dbs(|db| async move { // Clean repository should already have the Orchestrator change history let repository: Arc = Arc::new(ChangeHistorySqlxDatabase::new(db)); @@ -259,19 +249,12 @@ mod tests { assert_eq!(result, None); // the repository can return the list of all change histories - let (_orchestrator_identifier, orchestrator_change_history) = orchestrator_identity(); repository .store_change_history(identity2.identifier(), identity2.change_history().clone()) .await?; let result = repository.get_change_histories().await?; - assert_eq!( - result, - vec![ - orchestrator_change_history, - identity1.change_history().clone(), - identity2.change_history().clone(), - ] - ); + assert!(result.contains(&identity1.change_history().clone())); + assert!(result.contains(&identity2.change_history().clone())); // a change history can also be deleted from the repository repository .delete_change_history(identity2.identifier()) @@ -333,4 +316,14 @@ mod tests { let identifier = identities.identities_creation().create_identity().await?; identities.get_identity(&identifier).await } + + fn orchestrator_identity() -> (Identifier, ChangeHistory) { + let identifier = Identifier::from_str( + "I84502ce0d9a0a91bae29026b84e19be69fb4203a6bdd1424c85a43c812772a00", + ) + .unwrap(); + let change_history = ChangeHistory::import_from_string("81825858830101585385f6820181584104ebf9d78281a04f180029c12a74e994386c7c9fee24903f3bfe351497a9952758ee5f4b57d7ed6236ab5082ed85e1ae8c07d5600e0587f652d36727904b3e310df41a656a365d1a7836395d820181584050bf79071ecaf08a966228c712295a17da53994dc781a22103602afe656276ef83ba83a1004845b1e979e0944abff3cd8c7ceef834a8f5eeeca0e8f720fa38f4").unwrap(); + + (identifier, change_history) + } } diff --git a/implementations/rust/ockam/ockam_node/src/storage/database/migrations/node_migrations/sql/postgres/20240613100000_create_database.sql b/implementations/rust/ockam/ockam_node/src/storage/database/migrations/node_migrations/sql/postgres/20240613100000_create_database.sql index dbbdced58cd..992759ae22c 100644 --- a/implementations/rust/ockam/ockam_node/src/storage/database/migrations/node_migrations/sql/postgres/20240613100000_create_database.sql +++ b/implementations/rust/ockam/ockam_node/src/storage/database/migrations/node_migrations/sql/postgres/20240613100000_create_database.sql @@ -24,9 +24,6 @@ CREATE TABLE identity change_history TEXT NOT NULL ); --- Insert the controller identity -INSERT INTO identity VALUES ('I84502ce0d9a0a91bae29026b84e19be69fb4203a6bdd1424c85a43c812772a00', '81825858830101585385f6820181584104ebf9d78281a04f180029c12a74e994386c7c9fee24903f3bfe351497a9952758ee5f4b57d7ed6236ab5082ed85e1ae8c07d5600e0587f652d36727904b3e310df41a656a365d1a7836395d820181584050bf79071ecaf08a966228c712295a17da53994dc781a22103602afe656276ef83ba83a1004845b1e979e0944abff3cd8c7ceef834a8f5eeeca0e8f720fa38f4'); - -- This table some local metadata about identities CREATE TABLE named_identity ( @@ -36,14 +33,6 @@ CREATE TABLE named_identity is_default BOOLEAN DEFAULT FALSE -- boolean indicating if this identity is the default one ); --- This table stores the time when a given identity was enrolled --- In the current project -CREATE TABLE identity_enrollment -( - identifier TEXT NOT NULL UNIQUE, -- Identifier of the identity - enrolled_at INTEGER NOT NULL, -- UNIX timestamp in seconds - email TEXT -- Enrollment email -); -- This table lists attributes associated to a given identity CREATE TABLE identity_attributes @@ -66,12 +55,25 @@ CREATE INDEX identity_identifier_index ON identity_attributes (identifier); CREATE INDEX identity_node_name_index ON identity_attributes (node_name); +-- This table stores credentials as received by the application +CREATE TABLE credential +( + subject_identifier TEXT NOT NULL, + issuer_identifier TEXT NOT NULL, + scope TEXT NOT NULL, + credential BYTEA NOT NULL, + expires_at INTEGER, + node_name TEXT NOT NULL -- node name to isolate credential that each node has +); + +CREATE UNIQUE INDEX credential_issuer_subject_scope_index ON credential (issuer_identifier, subject_identifier, scope); +CREATE UNIQUE INDEX credential_issuer_subject_index ON credential(issuer_identifier, subject_identifier); -- This table stores purpose keys that have been created by a given identity CREATE TABLE purpose_key ( - identifier TEXT NOT NULL, -- Identity identifier - purpose TEXT NOT NULL, -- Purpose of the key: SecureChannels, or Credentials + identifier TEXT NOT NULL, -- Identity identifier + purpose TEXT NOT NULL, -- Purpose of the key: SecureChannels, or Credentials purpose_key_attestation BYTEA NOT NULL -- Encoded attestation: attestation data and attestation signature ); @@ -85,7 +87,7 @@ CREATE UNIQUE INDEX purpose_key_index ON purpose_key (identifier, purpose); CREATE TABLE vault ( name TEXT PRIMARY KEY, -- User-specified name for a vault - path TEXT NULL, -- Path where the vault is saved, This path can the current database path. In that case the vault data is stored in the *-secrets table below + path TEXT NULL, -- If the path is specified, then the secrets are stored in a SQLite file. Otherwise secrets are stored in the *-secrets tables below. is_default BOOLEAN, -- boolean indicating if this vault is the default one (0 means true) is_kms BOOLEAN -- boolean indicating if this vault is a KMS one (0 means true). In that case only key handles are stored in the database ); @@ -94,7 +96,7 @@ CREATE TABLE vault CREATE TABLE signing_secret ( handle BYTEA PRIMARY KEY, -- Secret handle - secret_type TEXT NOT NULL, -- Secret type (EdDSACurve25519 or ECDSASHA256CurveP256) + secret_type TEXT NOT NULL, -- Secret type (EdDSACurve25519 or ECDSASHA256CurveP256) secret BYTEA NOT NULL -- Secret binary ); @@ -105,28 +107,9 @@ CREATE TABLE x25519_secret secret BYTEA NOT NULL -- Secret binary ); - ---------------- --- CREDENTIALS ---------------- - --- This table stores credentials as received by the application -CREATE TABLE credential -( - subject_identifier TEXT NOT NULL, - issuer_identifier TEXT NOT NULL, - scope TEXT NOT NULL, - credential BYTEA NOT NULL, - expires_at INTEGER, - node_name TEXT NOT NULL -- node name to isolate credential that each node has -); - -CREATE UNIQUE INDEX credential_issuer_subject_scope_index ON credential (issuer_identifier, subject_identifier, scope); -CREATE UNIQUE INDEX credential_issuer_subject_index ON credential(issuer_identifier, subject_identifier); - ------------------- +------------- -- AUTHORITY ------------------- +------------- CREATE TABLE authority_member ( @@ -156,6 +139,10 @@ CREATE TABLE authority_enrollment_token CREATE UNIQUE INDEX authority_enrollment_token_one_time_code_index ON authority_enrollment_token(one_time_code); CREATE INDEX authority_enrollment_token_expires_at_index ON authority_enrollment_token(expires_at); +------------ +-- SERVICES +------------ + -- This table stores policies. A policy is an expression which -- can be evaluated against an environment (a list of name/value pairs) -- to assess if a given action can be performed on a given resource @@ -188,6 +175,25 @@ CREATE TABLE resource ); CREATE UNIQUE INDEX resource_index ON resource (node_name, resource_name, resource_type); +-- This table stores the current state of a TCP outlet +CREATE TABLE tcp_outlet_status +( + node_name TEXT NOT NULL, -- Node where that tcp outlet has been created + socket_addr TEXT NOT NULL, -- Socket address that the outlet connects to + worker_addr TEXT NOT NULL, -- Worker address for the outlet itself + payload TEXT, -- Optional status payload + privileged BOOLEAN DEFAULT FALSE -- boolean indicating if the outlet is operating in privileged mode +); + +-- This table stores the current state of a TCP inlet +CREATE TABLE tcp_inlet +( + node_name TEXT NOT NULL, -- Node where that tcp inlet has been created + bind_addr TEXT NOT NULL, -- Input address to connect to + outlet_addr TEXT NOT NULL, -- MultiAddress to the outlet + alias TEXT NOT NULL, -- Alias for that inlet + privileged BOOLEAN DEFAULT FALSE -- boolean indicating if the inlet is operating in privileged mode +); --------- -- NODES @@ -206,80 +212,6 @@ CREATE TABLE node http_server_address TEXT -- Address of the server supporting the HTTP status endpoint for the node ); --- This table stores the project name to use for a given node -CREATE TABLE node_project -( - node_name TEXT PRIMARY KEY, -- Node name - project_name TEXT NOT NULL -- Project name -); - ---------------------------- --- PROJECTS, SPACES, USERS ---------------------------- - --- This table store data about projects as returned by the Controller -CREATE TABLE project -( - project_id TEXT PRIMARY KEY, -- Identifier of the project - project_name TEXT NOT NULL, -- Name of the project - is_default BOOLEAN NOT NULL, -- boolean indicating if this project is the default one (0 means true) - space_id TEXT NOT NULL, -- Identifier of the space associated to the project - space_name TEXT NOT NULL, -- Name of the space associated to the project - project_identifier TEXT, -- optional: identifier of the project identity - access_route TEXT NOT NULL, -- Route used to create a secure channel to the project - authority_change_history TEXT, -- Change history for the authority identity - authority_access_route TEXT, -- Route te the authority associated to the project - version TEXT, -- Orchestrator software version - running BOOLEAN, -- boolean indicating if this project is currently accessible - operation_id TEXT, -- optional id of the operation currently creating the project on the Controller side - project_change_history TEXT -- Change history for the project identity -); - --- This table provides the list of users associated to a given project -CREATE TABLE user_project -( - user_email TEXT NOT NULL, -- User email - project_id TEXT NOT NULL -- Project id -); - --- This table provides additional information for users associated to a project or a space -CREATE TABLE user_role -( - user_id INTEGER NOT NULL, -- User id - project_id TEXT NOT NULL, -- Project id - user_email TEXT NOT NULL, -- User email - role TEXT NOT NULL, -- Role of the user: admin or member - scope TEXT NOT NULL -- Scope of the role: space, project, or service -); - --- This table stores data about spaces as returned by the controller -CREATE TABLE space -( - space_id TEXT PRIMARY KEY, -- Identifier of the space - space_name TEXT NOT NULL, -- Name of the space - is_default BOOLEAN NOT NULL -- boolean indicating if this project is the default one (0 means true) -); - --- This table provides the list of users associated to a given project -CREATE TABLE user_space -( - user_email TEXT NOT NULL, -- User email - space_id TEXT NOT NULL -- Space id -); - --- This table provides additional information for users after they have been authenticated -CREATE TABLE "user" -( - email TEXT PRIMARY KEY, -- User email - sub TEXT NOT NULL, -- (Sub)ject: unique identifier for the user - nickname TEXT NOT NULL, -- User nickname (or handle) - name TEXT NOT NULL, -- User name - picture TEXT NOT NULL, -- Link to a user picture - updated_at TEXT NOT NULL, -- ISO-8601 date: when this user information was last update - email_verified BOOLEAN NOT NULL, -- boolean indicating if the user email has been verified (0 means true) - is_default BOOLEAN NOT NULL -- boolean indicating if this user is the default user locally (0 means true) -); - ------------------- -- SECURE CHANNELS ------------------- @@ -305,55 +237,3 @@ CREATE TABLE aead_secret type TEXT NOT NULL, -- Secret type secret BYTEA NOT NULL -- Secret binary ); - ---------------- --- APPLICATION ---------------- - --- This table stores the current state of an outlet created to expose a service with the desktop application -CREATE TABLE tcp_outlet_status -( - node_name TEXT NOT NULL, -- Node where that tcp outlet has been created - socket_addr TEXT NOT NULL, -- Socket address that the outlet connects to - worker_addr TEXT NOT NULL, -- Worker address for the outlet itself - payload TEXT -- Optional status payload -); - --- This table stores the current state of an inlet created to expose a service with the desktop application -CREATE TABLE tcp_inlet -( - node_name TEXT NOT NULL, -- Node where that tcp inlet has been created - bind_addr TEXT NOT NULL, -- Input address to connect to - outlet_addr TEXT NOT NULL, -- MultiAddress to the outlet - alias TEXT NOT NULL -- Alias for that inlet -); - --- This table stores the list of services that a user has been invited to connect to --- via the desktop application -CREATE TABLE incoming_service -( - invitation_id TEXT PRIMARY KEY, -- Invitation id - enabled BOOLEAN NOT NULL, -- boolean indicating if the user wants to service to be accessible (0 means true) - name TEXT NULL -- Optional user-defined name for the service -); - ----------- --- ADDONS ----------- - --- This table stores the data necessary to configure the Okta addon -CREATE TABLE okta_config -( - project_id TEXT NOT NULL, -- Project id of the project using the addon - tenant_base_url TEXT NOT NULL, -- Base URL of the tenant - client_id TEXT NOT NULL, -- Client id - certificate TEXT NOT NULL, -- Certificate - attributes TEXT -- Comma-separated list of attribute names -); - --- This table stores the data necessary to configure the Kafka addons -CREATE TABLE kafka_config -( - project_id TEXT NOT NULL, -- Project id of the project using the addon - bootstrap_server TEXT NOT NULL -- URL of the bootstrap server -); diff --git a/implementations/rust/ockam/ockam_node/src/storage/database/migrations/node_migrations/sql/postgres/20240703100000_add_subscriptions.sql b/implementations/rust/ockam/ockam_node/src/storage/database/migrations/node_migrations/sql/postgres/20240703100000_add_subscriptions.sql deleted file mode 100644 index 28ecd731a0a..00000000000 --- a/implementations/rust/ockam/ockam_node/src/storage/database/migrations/node_migrations/sql/postgres/20240703100000_add_subscriptions.sql +++ /dev/null @@ -1,9 +0,0 @@ --- This migration adds the subscriptions table based on the following rust struct -CREATE TABLE subscription ( - space_id TEXT PRIMARY KEY, - name TEXT NOT NULL, - is_free_trial BOOLEAN NOT NULL, - marketplace TEXT, - start_date INTEGER, - end_date INTEGER -); diff --git a/implementations/rust/ockam/ockam_node/src/storage/database/migrations/node_migrations/sql/postgres/20241112100000_add_privileged_portals.sql b/implementations/rust/ockam/ockam_node/src/storage/database/migrations/node_migrations/sql/postgres/20241112100000_add_privileged_portals.sql deleted file mode 100644 index 57400cb0b2c..00000000000 --- a/implementations/rust/ockam/ockam_node/src/storage/database/migrations/node_migrations/sql/postgres/20241112100000_add_privileged_portals.sql +++ /dev/null @@ -1,7 +0,0 @@ --- Add privileged column to the tcp_outlet_status table -ALTER TABLE tcp_outlet_status - ADD privileged BOOLEAN DEFAULT FALSE; -- boolean indicating if the outlet is operating in privileged mode - --- Add privileged column to the tcp_inlet table -ALTER TABLE tcp_inlet - ADD privileged BOOLEAN DEFAULT FALSE; -- boolean indicating if the inlet is operating in privileged mode diff --git a/implementations/rust/ockam/ockam_node/src/storage/database/migrations/node_migrations/sql/sqlite/20250110100000_remove_node_project.sql b/implementations/rust/ockam/ockam_node/src/storage/database/migrations/node_migrations/sql/sqlite/20250110100000_remove_node_project.sql new file mode 100644 index 00000000000..ef18c0d1fab --- /dev/null +++ b/implementations/rust/ockam/ockam_node/src/storage/database/migrations/node_migrations/sql/sqlite/20250110100000_remove_node_project.sql @@ -0,0 +1,2 @@ +-- This table is not used anymore +DROP TABLE node_project; diff --git a/implementations/rust/ockam/ockam_node/src/storage/database/sqlx_database.rs b/implementations/rust/ockam/ockam_node/src/storage/database/sqlx_database.rs index 4d1f715a07e..ebacbf8ea0b 100644 --- a/implementations/rust/ockam/ockam_node/src/storage/database/sqlx_database.rs +++ b/implementations/rust/ockam/ockam_node/src/storage/database/sqlx_database.rs @@ -11,6 +11,7 @@ use sqlx::pool::PoolOptions; use sqlx::{Any, ConnectOptions, Pool}; use sqlx_core::any::AnyConnection; use sqlx_core::executor::Executor; +use sqlx_core::row::Row; use tempfile::NamedTempFile; use tokio_retry::strategy::{jitter, FixedInterval}; use tokio_retry::Retry; @@ -196,9 +197,18 @@ impl SqlxDatabase { }) .await?; - if let Some(migration_set) = migration_set { - let migrator = migration_set.create_migrator()?; - migrator.migrate(&database.pool).await?; + let database_schema_already_created: bool = sqlx::query("SELECT EXISTS (SELECT 1 FROM information_schema.tables WHERE table_schema = 'public' AND table_name = 'identity')") + .fetch_one(&*database.pool) + .await.into_core()?.get(0); + + // Only run the postgres migrations if the database has never been created. + // This is mostly for tests. In production the database schema must be created separately + // during the first deployment. + if !database_schema_already_created { + if let Some(migration_set) = migration_set { + let migrator = migration_set.create_migrator()?; + migrator.migrate(&database.pool).await?; + } } database @@ -426,6 +436,21 @@ impl Clean { } } +/// This function can be used to run some test code with the 2 SQLite databases implementations +pub async fn with_sqlite_dbs(f: F) -> Result<()> +where + F: Fn(SqlxDatabase) -> Fut + Send + Sync + 'static, + Fut: Future> + Send + 'static, +{ + let db = SqlxDatabase::in_memory("test").await?; + rethrow("SQLite in memory", f(db)).await?; + + let db_file = NamedTempFile::new().unwrap(); + let db = SqlxDatabase::create_sqlite(db_file.path()).await?; + rethrow("SQLite on disk", f(db)).await?; + Ok(()) +} + /// This function can be used to run some test code with the 3 different databases implementations pub async fn with_dbs(f: F) -> Result<()> where @@ -439,7 +464,7 @@ where let db = SqlxDatabase::create_sqlite(db_file.path()).await?; rethrow("SQLite on disk", f(db)).await?; - // only run the postgres tests if the OCKAM_POSTGRES_* environment variables are set + // only run the postgres tests if the OCKAM_DATABASE_CONNECTION_URL environment variables is set if let Ok(db) = SqlxDatabase::create_new_postgres().await { rethrow("Postgres local", f(db.clone())).await?; db.drop_all_postgres_tables().await?; @@ -447,6 +472,33 @@ where Ok(()) } +/// This function can be used to run some test code with a postgres database +pub async fn with_postgres_db(f: F) -> Result<()> +where + F: Fn(SqlxDatabase) -> Fut + Send + Sync + 'static, + Fut: Future> + Send + 'static, +{ + // only run the postgres tests if the OCKAM_DATABASE_CONNECTION_URL environment variables is set + if let Ok(db) = SqlxDatabase::create_new_postgres().await { + rethrow("Postgres local", f(db.clone())).await?; + db.drop_all_postgres_tables().await?; + }; + Ok(()) +} + +/// This function can be used to avoid running a test if the postgres database is used. +pub async fn skip_if_postgres(f: F) -> std::result::Result<(), R> +where + F: Fn() -> Fut + Send + Sync + 'static, + Fut: Future> + Send + 'static, +{ + // only run the postgres tests if the OCKAM_DATABASE_CONNECTION_URL environment variables is not set + if SqlxDatabase::create_new_postgres().await.is_err() { + f().await? + }; + Ok(()) +} + /// This function can be used to run some test code with the 3 different databases implementations /// of the application database pub async fn with_application_dbs(f: F) -> Result<()> diff --git a/tools/stress-test/src/main.rs b/tools/stress-test/src/main.rs index 8cdd1c732ec..96fefde840c 100644 --- a/tools/stress-test/src/main.rs +++ b/tools/stress-test/src/main.rs @@ -172,7 +172,7 @@ impl State { let listener = tcp.listen(&"127.0.0.1:0", options).await?; let _ = cli_state - .start_node_with_optional_values(NODE_NAME, &None, &None, Some(&listener)) + .start_node_with_optional_values(NODE_NAME, &None, Some(&listener)) .await?; let trust_options = cli_state