Move NS client to separate package under NS API (#5171)

* Move client code to NS API

* Move client to separate package

* Move things around

* Adjust run scripts

* rustfmt

* Add client to workspace
This commit is contained in:
dynco-nym
2024-11-26 15:59:42 +01:00
committed by GitHub
parent edd3f9108a
commit e7f806219c
85 changed files with 187 additions and 174 deletions
Generated
+17 -19
View File
@@ -5009,16 +5009,6 @@ dependencies = [
"nym-multisig-contract-common",
]
[[package]]
name = "nym-common-models"
version = "0.1.0"
dependencies = [
"anyhow",
"bincode",
"nym-crypto",
"serde",
]
[[package]]
name = "nym-compact-ecash"
version = "0.1.0"
@@ -6058,19 +6048,13 @@ name = "nym-node-status-agent"
version = "1.0.0-rc.1"
dependencies = [
"anyhow",
"bincode",
"chrono",
"clap 4.5.20",
"nym-bin-common",
"nym-common-models",
"nym-crypto",
"nym-node-status-client",
"rand",
"reqwest 0.12.4",
"serde",
"serde_json",
"tempfile",
"tokio",
"tokio-util",
"tracing",
"tracing-subscriber",
]
@@ -6081,7 +6065,6 @@ version = "1.0.0-rc.2"
dependencies = [
"anyhow",
"axum 0.7.7",
"bincode",
"chrono",
"clap 4.5.20",
"cosmwasm-std",
@@ -6089,11 +6072,11 @@ dependencies = [
"futures-util",
"moka",
"nym-bin-common",
"nym-common-models",
"nym-crypto",
"nym-explorer-client",
"nym-network-defaults",
"nym-node-requests",
"nym-node-status-client",
"nym-task",
"nym-validator-client",
"regex",
@@ -6116,6 +6099,21 @@ dependencies = [
"utoipauto",
]
[[package]]
name = "nym-node-status-client"
version = "0.1.0"
dependencies = [
"anyhow",
"bincode",
"chrono",
"nym-crypto",
"nym-http-api-client",
"reqwest 0.12.4",
"serde",
"serde_json",
"tracing",
]
[[package]]
name = "nym-node-tester-utils"
version = "0.1.0"
+5 -6
View File
@@ -61,7 +61,6 @@ members = [
"common/ip-packet-requests",
"common/ledger",
"common/mixnode-common",
"common/models",
"common/network-defaults",
"common/node-tester-utils",
"common/nonexhaustive-delayqueue",
@@ -126,8 +125,9 @@ members = [
"nym-node",
"nym-node/nym-node-http-api",
"nym-node/nym-node-requests",
"nym-node-status-api",
"nym-node-status-agent",
"nym-node-status-api/nym-node-status-agent",
"nym-node-status-api/nym-node-status-api",
"nym-node-status-api/nym-node-status-client",
"nym-outfox",
"nym-validator-rewarder",
"tools/echo-server",
@@ -155,7 +155,6 @@ members = [
default-members = [
"clients/native",
"clients/socks5",
"common/models",
"explorer-api",
"gateway",
"mixnode",
@@ -163,9 +162,9 @@ default-members = [
"nym-credential-proxy/nym-credential-proxy",
"nym-data-observatory",
"nym-node",
"nym-node-status-api",
"nym-node-status-api/nym-node-status-agent",
"nym-node-status-api/nym-node-status-api",
"nym-validator-rewarder",
"nym-node-status-api",
"service-providers/authenticator",
"service-providers/ip-packet-router",
"service-providers/network-requester",
-17
View File
@@ -1,17 +0,0 @@
[package]
name = "nym-common-models"
version = "0.1.0"
authors.workspace = true
repository.workspace = true
homepage.workspace = true
documentation.workspace = true
edition.workspace = true
license.workspace = true
rust-version.workspace = true
readme.workspace = true
[dependencies]
anyhow = { workspace = true }
bincode = { workspace = true }
nym-crypto = { path = "../crypto", features = ["asymmetric", "serde"] }
serde = { workspace = true, features = ["derive"] }
-1
View File
@@ -1 +0,0 @@
pub mod ns_api;
-2
View File
@@ -1,2 +0,0 @@
nym-gateway-probe
keys/
+2 -1
View File
@@ -1,6 +1,7 @@
nym-node-status-agent/nym-gateway-probe
nym-node-status-agent/keys/
data/
enter_db.sh
nym-gateway-probe
nym-node-status-api
*.sqlite
*.sqlite-journal
@@ -1,7 +1,6 @@
# Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
# SPDX-License-Identifier: Apache-2.0
[package]
name = "nym-node-status-agent"
version = "1.0.0-rc.1"
@@ -16,20 +15,14 @@ readme.workspace = true
[dependencies]
anyhow = { workspace = true}
bincode = { workspace = true }
chrono = { workspace = true }
clap = { workspace = true, features = ["derive", "env"] }
nym-bin-common = { path = "../common/bin-common", features = ["models"]}
nym-common-models = { path = "../common/models" }
nym-crypto = { path = "../common/crypto", features = ["asymmetric", "rand"] }
nym-bin-common = { path = "../../common/bin-common", features = ["models"]}
nym-node-status-client = { path = "../nym-node-status-client" }
nym-crypto = { path = "../../common/crypto", features = ["asymmetric", "rand"] }
rand = { workspace = true }
tokio = { workspace = true, features = ["macros", "rt-multi-thread", "process"] }
tokio-util = { workspace = true }
tracing = { workspace = true }
tracing-subscriber = { workspace = true, features = ["env-filter"] }
reqwest = { workspace = true, features = ["json"] }
serde = { workspace = true }
serde_json = { workspace = true }
[dev-dependencies]
tempfile = { workspace = true }
@@ -3,14 +3,14 @@
set -eu
export ENVIRONMENT=${ENVIRONMENT:-"sandbox"}
probe_git_ref="nym-vpn-core-v1.0.0-rc.6"
probe_git_ref="nym-vpn-core-v1.0.0-rc.14"
crate_root=$(dirname $(realpath "$0"))
monorepo_root=$(dirname "${crate_root}")
monorepo_root=$(realpath "${crate_root}/../..")
echo "Expecting nym-vpn-client repo at a sibling level of nym monorepo dir"
gateway_probe_src=$(dirname "${monorepo_root}")/nym-vpn-client/nym-vpn-core
echo "gateway_probe_src=$gateway_probe_src"
echo "crate_root=$crate_root"
set -a
source "${monorepo_root}/envs/${ENVIRONMENT}.env"
@@ -30,9 +30,11 @@ function copy_gw_probe() {
pushd $gateway_probe_src
git fetch -a
git checkout $probe_git_ref
cargo build --release --package nym-gateway-probe
cp target/release/nym-gateway-probe "$crate_root"
$crate_root/nym-gateway-probe --version
popd
}
@@ -44,7 +46,7 @@ function swarm() {
local workers=$1
for ((i = 1; i <= workers; i++)); do
../target/release/nym-node-status-agent run-probe &
${monorepo_root}/target/release/nym-node-status-agent run-probe &
done
wait
@@ -0,0 +1,32 @@
use crate::cli::GwProbe;
use anyhow::Context;
use nym_crypto::asymmetric::ed25519::PrivateKey;
pub(crate) async fn run_probe(
server_ip: &str,
server_port: u16,
ns_api_auth_key: &str,
probe_path: &str,
) -> anyhow::Result<()> {
let auth_key = PrivateKey::from_base58_string(ns_api_auth_key)
.context("Couldn't parse auth key, exiting")?;
let ns_api_client = nym_node_status_client::NsApiClient::new(server_ip, server_port, auth_key);
let probe = GwProbe::new(probe_path.to_string());
let version = probe.version().await;
tracing::info!("Probe version:\n{}", version);
if let Some(testrun) = ns_api_client.request_testrun().await? {
let log = probe.run_and_get_log(&Some(testrun.gateway_identity_key));
ns_api_client
.submit_results(testrun.testrun_id, log, testrun.assigned_at_utc)
.await?;
} else {
tracing::info!("No testruns available, exiting")
}
Ok(())
}
@@ -15,21 +15,20 @@ rust-version.workspace = true
[dependencies]
anyhow = { workspace = true }
axum = { workspace = true, features = ["tokio", "macros"] }
bincode = { workspace = true }
chrono = { workspace = true }
clap = { workspace = true, features = ["cargo", "derive", "env", "string"] }
cosmwasm-std = { workspace = true }
envy = { workspace = true }
futures-util = { workspace = true }
moka = { workspace = true, features = ["future"] }
nym-bin-common = { path = "../common/bin-common", features = ["models"]}
nym-common-models = { path = "../common/models" }
nym-crypto = { path = "../common/crypto", features = ["asymmetric", "serde"] }
nym-explorer-client = { path = "../explorer-api/explorer-client" }
nym-network-defaults = { path = "../common/network-defaults" }
nym-validator-client = { path = "../common/client-libs/validator-client" }
nym-task = { path = "../common/task" }
nym-node-requests = { path = "../nym-node/nym-node-requests", features = ["openapi"] }
nym-bin-common = { path = "../../common/bin-common", features = ["models"]}
nym-node-status-client = { path = "../nym-node-status-client" }
nym-crypto = { path = "../../common/crypto", features = ["asymmetric", "serde"] }
nym-explorer-client = { path = "../../explorer-api/explorer-client" }
nym-network-defaults = { path = "../../common/network-defaults" }
nym-validator-client = { path = "../../common/client-libs/validator-client" }
nym-task = { path = "../../common/task" }
nym-node-requests = { path = "../../nym-node/nym-node-requests", features = ["openapi"] }
regex = { workspace = true }
reqwest = { workspace = true }
serde = { workspace = true, features = ["derive"] }
@@ -2,29 +2,23 @@
set -e
export RUST_LOG=${RUST_LOG:-debug}
export NYM_API_CLIENT_TIMEOUT=60
export EXPLORER_CLIENT_TIMEOUT=60
export NODE_STATUS_API_TESTRUN_REFRESH_INTERVAL=120
# public keys corresponding to the agents NS API is expecting to be contacted from
export NODE_STATUS_API_AGENT_KEY_LIST="H4z8kx5Kkf5JMQHhxaW1MwYndjKCDHC7HsVhHTFfBZ4J,
5c2GW61135DEr73DxGrR4DR22BLEujvm1k8GYEjRB9at,
3PSFDH2iSJ61KoDNyJpAiw42xS5smV5iBXWnRGTmk2du,
2AH7pJL5PErbSFhZdu3uH8cKa1h1tyCUfSRUm6E5EBz8,
6wQ9ifPFm2EB73BrwpGSd3Ek7GFA5kiAMQDP2ox6JKZw,
G1tevJBnzaQ6zCUsFsxtGJf45BqCTDgzpEz6Sgxks8EH,
FwjL2nGrtgQQ48fPqAUzUZ8UkQZtMtgehqTqj4PQopvh,
Eujj4GmvwQBgHZaNSyqUbjMFSsnXWPSjEYUPgAsKmx1A,
5ZnfSGxW6EKcFxB8jftb9V3f897VpwpZtf7kCPYzB595,
H9kuRd8BGjEUD8Grh5U9YUPN5ZaQmSYz8U44R72AffKM"
# public counterpart of the agent's private key.
# For TESTING only. NOT used in any other environment
export NODE_STATUS_API_AGENT_KEY_LIST="H4z8kx5Kkf5JMQHhxaW1MwYndjKCDHC7HsVhHTFfBZ4J"
export ENVIRONMENT=${ENVIRONMENT:-"sandbox"}
script_dir=$(dirname $(realpath "$0"))
monorepo_root=$(realpath "${script_dir}/../..")
function run_bare() {
# export necessary env vars
set -a
source ../envs/${ENVIRONMENT}.env
source "${monorepo_root}/envs/${ENVIRONMENT}.env"
set +a
export RUST_LOG=debug
@@ -1,12 +1,3 @@
use axum::extract::DefaultBodyLimit;
use axum::Json;
use axum::{
extract::{Path, State},
Router,
};
use nym_common_models::ns_api::{get_testrun, submit_results, VerifiableRequest};
use reqwest::StatusCode;
use crate::db::models::TestRunStatus;
use crate::db::queries;
use crate::testruns::now_utc;
@@ -18,6 +9,17 @@ use crate::{
state::AppState,
},
};
use axum::extract::DefaultBodyLimit;
use axum::Json;
use axum::{
extract::{Path, State},
Router,
};
use nym_node_status_client::{
auth::VerifiableRequest,
models::{get_testrun, submit_results},
};
use reqwest::StatusCode;
// TODO dz consider adding endpoint to trigger testrun scan for a given gateway_id
// like in H< src/http/testruns.rs
@@ -5,7 +5,7 @@ use utoipauto::utoipauto;
// manually import external structs which are behind feature flags because they
// can't be automatically discovered
// https://github.com/ProbablyClem/utoipauto/issues/13#issuecomment-1974911829
#[utoipauto(paths = "./nym-node-status-api/src")]
#[utoipauto(paths = "./nym-node-status-api/nym-node-status-api/src")]
#[derive(OpenApi)]
#[openapi(
info(title = "Node Status API"),
@@ -2,7 +2,7 @@ use nym_node_requests::api::v1::node::models::NodeDescription;
use serde::{Deserialize, Serialize};
use utoipa::ToSchema;
pub(crate) use nym_common_models::ns_api::TestrunAssignment;
pub(crate) use nym_node_status_client::models::TestrunAssignment;
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct Gateway {
@@ -0,0 +1,25 @@
# Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
# SPDX-License-Identifier: Apache-2.0
[package]
name = "nym-node-status-client"
version = "0.1.0"
authors.workspace = true
repository.workspace = true
homepage.workspace = true
documentation.workspace = true
edition.workspace = true
license.workspace = true
rust-version.workspace = true
readme.workspace = true
[dependencies]
anyhow = { workspace = true }
chrono = { workspace = true }
bincode = { workspace = true }
nym-crypto = { path = "../../common/crypto", features = ["asymmetric", "serde"] }
nym-http-api-client = { path = "../../common/http-api-client" }
reqwest = { workspace = true }
serde = { workspace = true, features = ["derive"] }
serde_json = { workspace = true }
tracing = { workspace = true }
@@ -0,0 +1,18 @@
use std::fmt::Display;
pub(super) struct ApiPaths {
server_address: String,
}
impl ApiPaths {
pub(super) fn new(server_address: String) -> Self {
Self { server_address }
}
pub(super) fn request_testrun(&self) -> String {
format!("{}/internal/testruns", self.server_address)
}
pub(super) fn submit_results(&self, testrun_id: impl Display) -> String {
format!("{}/internal/testruns/{}", self.server_address, testrun_id)
}
}
@@ -0,0 +1,30 @@
use nym_crypto::asymmetric::ed25519::{PublicKey, Signature, SignatureError};
pub trait SignedRequest {
type Payload: serde::Serialize;
fn public_key(&self) -> &PublicKey;
fn signature(&self) -> &Signature;
fn payload(&self) -> &Self::Payload;
}
pub trait VerifiableRequest: SignedRequest {
type Error: From<bincode::Error> + From<SignatureError>;
fn verify_signature(&self) -> Result<(), Self::Error> {
bincode::serialize(self.payload())
.map_err(Self::Error::from)
.and_then(|serialized| {
self.public_key()
.verify(serialized, self.signature())
.map_err(Self::Error::from)
})
}
}
impl<T> VerifiableRequest for T
where
T: SignedRequest,
{
type Error = anyhow::Error;
}
@@ -1,62 +1,35 @@
use anyhow::{bail, Context};
use nym_common_models::ns_api::{get_testrun, submit_results, TestrunAssignment};
use crate::models::{get_testrun, submit_results, TestrunAssignment};
use anyhow::bail;
use api::ApiPaths;
use nym_crypto::asymmetric::ed25519::{PrivateKey, Signature};
use std::fmt::Display;
use tracing::instrument;
use crate::cli::GwProbe;
mod api;
pub mod auth;
pub mod models;
const INTERNAL_TESTRUNS: &str = "internal/testruns";
pub(crate) async fn run_probe(
server_ip: &str,
server_port: u16,
ns_api_auth_key: &str,
probe_path: &str,
) -> anyhow::Result<()> {
let auth_key = PrivateKey::from_base58_string(ns_api_auth_key)
.context("Couldn't parse auth key, exiting")?;
let ns_api_client = Client::new(server_ip, server_port, auth_key);
let probe = GwProbe::new(probe_path.to_string());
let version = probe.version().await;
tracing::info!("Probe version:\n{}", version);
if let Some(testrun) = ns_api_client.request_testrun().await? {
let log = probe.run_and_get_log(&Some(testrun.gateway_identity_key));
ns_api_client
.submit_results(testrun.testrun_id, log, testrun.assigned_at_utc)
.await?;
} else {
tracing::info!("No testruns available, exiting")
}
Ok(())
}
struct Client {
server_address: String,
pub struct NsApiClient {
api: ApiPaths,
client: reqwest::Client,
auth_key: PrivateKey,
}
impl Client {
impl NsApiClient {
pub fn new(server_ip: &str, server_port: u16, auth_key: PrivateKey) -> Self {
let server_address = format!("{}:{}", server_ip, server_port);
let api = ApiPaths::new(server_address);
let client = reqwest::Client::new();
Self {
server_address,
api,
client,
auth_key,
}
}
#[instrument(level = "debug", skip_all)]
pub(crate) async fn request_testrun(&self) -> anyhow::Result<Option<TestrunAssignment>> {
let target_url = self.api_with_subpath(None::<String>);
pub async fn request_testrun(&self) -> anyhow::Result<Option<TestrunAssignment>> {
let target_url = self.api.request_testrun();
let payload = get_testrun::Payload {
agent_public_key: self.auth_key.public_key(),
@@ -93,13 +66,13 @@ impl Client {
}
#[instrument(level = "debug", skip(self, probe_result))]
pub(crate) async fn submit_results(
pub async fn submit_results(
&self,
testrun_id: i64,
probe_result: String,
assigned_at_utc: i64,
) -> anyhow::Result<()> {
let target_url = self.api_with_subpath(Some(testrun_id));
let target_url = self.api.submit_results(testrun_id);
let payload = submit_results::Payload {
probe_result,
@@ -129,12 +102,4 @@ impl Client {
let signed = self.auth_key.sign(&serialized);
Ok(signed)
}
fn api_with_subpath(&self, subpath: Option<impl Display>) -> String {
if let Some(subpath) = subpath {
format!("{}/{}/{}", self.server_address, INTERNAL_TESTRUNS, subpath)
} else {
format!("{}/{}", self.server_address, INTERNAL_TESTRUNS)
}
}
}
@@ -1,7 +1,9 @@
use nym_crypto::asymmetric::ed25519::{PublicKey, Signature, SignatureError};
use nym_crypto::asymmetric::ed25519::{PublicKey, Signature};
use serde::{Deserialize, Serialize};
pub mod get_testrun {
use crate::auth::SignedRequest;
use super::*;
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct Payload {
@@ -40,6 +42,8 @@ pub struct TestrunAssignment {
}
pub mod submit_results {
use crate::auth::SignedRequest;
use super::*;
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct Payload {
@@ -70,32 +74,3 @@ pub mod submit_results {
}
}
}
pub trait SignedRequest {
type Payload: serde::Serialize;
fn public_key(&self) -> &PublicKey;
fn signature(&self) -> &Signature;
fn payload(&self) -> &Self::Payload;
}
pub trait VerifiableRequest: SignedRequest {
type Error: From<bincode::Error> + From<SignatureError>;
fn verify_signature(&self) -> Result<(), Self::Error> {
bincode::serialize(self.payload())
.map_err(Self::Error::from)
.and_then(|serialized| {
self.public_key()
.verify(serialized, self.signature())
.map_err(Self::Error::from)
})
}
}
impl<T> VerifiableRequest for T
where
T: SignedRequest,
{
type Error = anyhow::Error;
}