Skip to content

Commit

Permalink
Merge pull request #283 from monadicus/feat-new-actions
Browse files Browse the repository at this point in the history
feat(snops): add private_key, env, and binary config action flags
  • Loading branch information
gluax authored Sep 17, 2024
2 parents 96d1a6e + ed9c41b commit ae44e41
Show file tree
Hide file tree
Showing 9 changed files with 119 additions and 18 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions crates/snops-agent/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,14 @@ impl Cli {
std::process::exit(0);
}

pub fn get_local_ip(&self) -> IpAddr {
if self.bind_addr.is_unspecified() {
IpAddr::V4(Ipv4Addr::LOCALHOST)
} else {
self.bind_addr
}
}

pub fn endpoint_and_uri(&self) -> (String, Uri) {
// get the endpoint
let endpoint = self
Expand Down
9 changes: 2 additions & 7 deletions crates/snops-agent/src/metrics/mod.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,6 @@
pub mod tps;

use std::{
collections::HashMap,
net::{IpAddr, Ipv4Addr, SocketAddr},
sync::Arc,
time::Duration,
};
use std::{collections::HashMap, net::SocketAddr, sync::Arc, time::Duration};

use self::tps::TpsMetric;
use crate::state::GlobalState;
Expand Down Expand Up @@ -39,7 +34,7 @@ pub fn init(state: Arc<GlobalState>) {
let response = match client
.get(format!(
"http://{}/",
SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), state.cli.ports.metrics)
SocketAddr::new(state.cli.get_local_ip(), state.cli.ports.metrics)
))
.send()
.await
Expand Down
6 changes: 4 additions & 2 deletions crates/snops-agent/src/rpc/control.rs
Original file line number Diff line number Diff line change
Expand Up @@ -428,7 +428,8 @@ impl AgentService for AgentRpcServer {
.network;

let url = format!(
"http://127.0.0.1:{}/{network}{route}",
"http://{}:{}/{network}{route}",
self.state.cli.get_local_ip(),
self.state.cli.ports.rest
);
let response = reqwest::get(&url)
Expand Down Expand Up @@ -460,7 +461,8 @@ impl AgentService for AgentRpcServer {
.network;

let url = format!(
"http://127.0.0.1:{}/{network}/transaction/broadcast",
"http://{}:{}/{network}/transaction/broadcast",
self.state.cli.get_local_ip(),
self.state.cli.ports.rest
);
let response = reqwest::Client::new()
Expand Down
1 change: 1 addition & 0 deletions crates/snops-cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ clap-stdin.workspace = true
reqwest = { workspace = true, features = ["blocking", "json"] }
serde_json.workspace = true
snops-common = { workspace = true, features = ["aot_cmds"] }
thiserror.workspace = true

[build-dependencies]
anyhow.workspace = true
Expand Down
53 changes: 51 additions & 2 deletions crates/snops-cli/src/commands/env/action/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::str::FromStr;
use std::{collections::HashMap, str::FromStr};

use anyhow::Result;
use clap::Parser;
Expand All @@ -9,7 +9,7 @@ use snops_common::{
action_models::{AleoValue, WithTargets},
key_source::KeySource,
node_targets::{NodeTarget, NodeTargetError, NodeTargets},
state::{CannonId, DocHeightRequest, EnvId},
state::{CannonId, DocHeightRequest, EnvId, InternedId},
};

//scli env canary action online client/*
Expand Down Expand Up @@ -132,12 +132,42 @@ pub enum Action {
/// Configure the validators of the target nodes, or `none`.
#[clap(long, short)]
validators: Option<NodesOption>,
/// Set environment variables for a node: `--env FOO=bar`
#[clap(long, short, number_of_values = 1, value_parser = clap::value_parser!(KeyEqValue))]
env: Option<Vec<KeyEqValue>>,
// Remove environment variables from a node: `--del-env FOO,BAR`
#[clap(long, short, value_delimiter = ',', allow_hyphen_values = true)]
del_env: Option<Vec<String>>,
/// The nodes to configure.
#[clap(num_args = 1, value_delimiter = ' ')]
nodes: Vec<NodeTarget>,
/// Configure the binary for a node.
#[clap(long, short)]
binary: Option<InternedId>,
/// Configure the private key for a node.
#[clap(long, short)]
private_key: Option<KeySource>,
},
}

#[derive(Clone, Debug)]
pub struct KeyEqValue(pub String, pub String);

impl FromStr for KeyEqValue {
type Err = &'static str;

fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
let (l, r) = s.split_once('=').ok_or("missing = in `key=value`")?;
Ok(Self(l.to_owned(), r.to_owned()))
}
}

impl KeyEqValue {
pub fn pair(self) -> (String, String) {
(self.0, self.1)
}
}

impl Action {
pub fn execute(self, url: &str, env_id: EnvId, client: Client) -> Result<Response> {
use Action::*;
Expand Down Expand Up @@ -251,6 +281,10 @@ impl Action {
peers,
validators,
nodes,
env,
del_env,
binary,
private_key,
} => {
let ep = format!("{url}/api/v1/env/{env_id}/action/config");

Expand All @@ -270,6 +304,21 @@ impl Action {
if let Some(validators) = validators {
json["validators"] = json!(NodeTargets::from(validators));
}
if let Some(binary) = binary {
json["binary"] = json!(binary);
}
if let Some(private_key) = private_key {
json["private_key"] = json!(private_key);
}
if let Some(env) = env {
json["set_env"] = json!(env
.into_iter()
.map(KeyEqValue::pair)
.collect::<HashMap<_, _>>())
}
if let Some(del_env) = del_env {
json["del_env"] = json!(del_env)
}

// this api accepts a list of json objects
client.post(ep).json(&json!(vec![json])).send()?
Expand Down
14 changes: 10 additions & 4 deletions crates/snops-common/src/action_models.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
use std::str::FromStr;

use indexmap::{IndexMap, IndexSet};
use serde::{Deserialize, Serialize};

use crate::{
key_source::KeySource,
node_targets::{NodeTarget, NodeTargets},
state::{CannonId, DocHeightRequest},
state::{CannonId, DocHeightRequest, InternedId},
};

#[derive(Deserialize, Serialize, Clone)]
Expand Down Expand Up @@ -120,7 +121,12 @@ pub struct Reconfig {
pub peers: Option<NodeTargets>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub validators: Option<NodeTargets>,
// TODO: private key
// TODO: env
// TODO: binary
#[serde(default, skip_serializing_if = "Option::is_none")]
pub binary: Option<InternedId>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub private_key: Option<KeySource>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub set_env: Option<IndexMap<String, String>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub del_env: Option<IndexSet<String>>,
}
4 changes: 4 additions & 0 deletions crates/snops-common/src/state/node_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,10 @@ impl KeyState {
_ => None,
}
}

pub fn is_none(&self) -> bool {
matches!(self, KeyState::None)
}
}

#[derive(
Expand Down
41 changes: 38 additions & 3 deletions crates/snops/src/server/actions/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use axum::{
};
use snops_common::{
action_models::{Reconfig, WithTargets},
state::{AgentId, AgentState},
state::{AgentId, AgentState, InternedId},
};

use super::Env;
Expand All @@ -31,7 +31,7 @@ pub async fn config(
AgentState::Inventory => (),
AgentState::Node(_, ref mut n) => {
$({
let $key = &n.$key;
let $key = &mut n.$key;
n.$key = $val;
})*
}
Expand All @@ -43,7 +43,7 @@ pub async fn config(
$agent.client_owned(),
$agent.state().clone().map_node(|mut n| {
$({
let $key = &n.$key;
let $key = &mut n.$key;
n.$key = $val;
})*
n
Expand Down Expand Up @@ -78,6 +78,41 @@ pub async fn config(
.collect::<Vec<_>>();
set_node_field!(agent, validators = v.clone());
}

if let Some(b) = &data.binary {
set_node_field!(agent, binary = (*b != InternedId::default()).then_some(*b));
}

if let Some(k) = &data.private_key {
let key = env.storage.sample_keysource_pk(k);
if key.is_none() {
return ServerError::NotFound(format!("key source not found: `{k}`"))
.into_response();
}
set_node_field!(agent, private_key = key.clone());
}

// inject env fields
if let Some(e) = &data.set_env {
set_node_field!(
agent,
env = {
env.extend(e.clone());
std::mem::take(env)
}
)
}

// remove env fields
if let Some(e) = &data.del_env {
set_node_field!(
agent,
env = {
env.retain(|k, _| !e.contains(k));
std::mem::take(env)
}
)
}
}
}

Expand Down

0 comments on commit ae44e41

Please sign in to comment.