Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(rust): custom branding config allows you to specify which commands are included #8769

Merged
merged 4 commits into from
Jan 24, 2025
Merged
Show file tree
Hide file tree
Changes from all 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
26 changes: 13 additions & 13 deletions Cargo.lock

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

Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ pub(crate) const OCKAM_OPENTELEMETRY_EXPORT: &str = "OCKAM_OPENTELEMETRY_EXPORT"
pub(crate) const OCKAM_TELEMETRY_EXPORT_VIA_PORTAL: &str = "OCKAM_TELEMETRY_EXPORT_VIA_PORTAL";

/// Boolean set to true if the current user is an Ockam developer
pub(crate) const OCKAM_DEVELOPER: &str = "OCKAM_DEVELOPER";
pub const OCKAM_DEVELOPER: &str = "OCKAM_DEVELOPER";

/// If this variable is true, print statements will debug the setting of the OpenTelemetry export
pub(crate) const OCKAM_OPENTELEMETRY_EXPORT_DEBUG: &str = "OCKAM_OPENTELEMETRY_EXPORT_DEBUG";
Expand Down
2 changes: 1 addition & 1 deletion implementations/rust/ockam/ockam_api/src/logs/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
///
mod current_span;
mod default_values;
mod env_variables;
pub mod env_variables;
pub mod exporting_configuration;
mod log_exporters;
pub mod logging_configuration;
Expand Down
8 changes: 8 additions & 0 deletions implementations/rust/ockam/ockam_command/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ fn hash() {
}

fn binary_name() {
let is_developer = env::var("OCKAM_DEVELOPER").unwrap_or("false".to_string());
println!("cargo:rustc-env=OCKAM_DEVELOPER={is_developer}");
println!("cargo:rerun-if-env-changed=OCKAM_DEVELOPER");

let bin_name = env::var("OCKAM_COMMAND_BIN_NAME").unwrap_or("ockam".to_string());
println!("cargo:rustc-env=OCKAM_COMMAND_BIN_NAME={bin_name}");
println!("cargo:rerun-if-env-changed=OCKAM_COMMAND_BIN_NAME");
Expand All @@ -29,6 +33,10 @@ fn binary_name() {
println!("cargo:rustc-env=OCKAM_HOME={home_dir}");
println!("cargo:rerun-if-env-changed=OCKAM_HOME");

let commands = env::var("OCKAM_COMMANDS").unwrap_or("".to_string());
println!("cargo:rustc-env=OCKAM_COMMANDS={commands}");
println!("cargo:rerun-if-env-changed=OCKAM_COMMANDS");

let orchestrator_identifier =
env::var("OCKAM_CONTROLLER_IDENTITY_ID").unwrap_or("".to_string());
println!("cargo:rustc-env=OCKAM_CONTROLLER_IDENTITY_ID={orchestrator_identifier}");
Expand Down
69 changes: 58 additions & 11 deletions implementations/rust/ockam/ockam_command/src/bin/brand.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@ use ockam_api::colors::color_primary;
use ockam_api::fmt_log;
use ockam_api::orchestrator::{OCKAM_CONTROLLER_ADDRESS, OCKAM_CONTROLLER_IDENTIFIER};
use ockam_api::terminal::PADDING;
use ockam_command::{
OCKAM_COMMAND_BIN_NAME, OCKAM_COMMAND_BRAND_NAME, OCKAM_COMMAND_SUPPORT_EMAIL,
use ockam_command::environment::compile_time_vars::{
OCKAM_COMMANDS, OCKAM_COMMAND_BIN_NAME, OCKAM_COMMAND_BRAND_NAME, OCKAM_COMMAND_SUPPORT_EMAIL,
};
use once_cell::sync::Lazy;
use serde::{Deserialize, Serialize};
use std::collections::BTreeMap;
use std::collections::{BTreeMap, HashMap};
use std::fmt::Display;
use std::path::{Path, PathBuf};

Expand All @@ -21,9 +21,8 @@ static CRATE_DIR: Lazy<PathBuf> = Lazy::new(|| {

static BIN_DIR: Lazy<PathBuf> = Lazy::new(|| CRATE_DIR.join("src/bin"));

/// Builds the binaries with the passed configuration
/// How to run:
/// `cargo run --bin brand ./path/to/config.yaml --release`
/// Builds the binaries with the passed configuration:
/// `cargo run --bin brand ./path/to/config.yaml`
/// `cargo run --bin brand "{bin1: {brand_name: "Name"}}"`
fn main() -> Result<()> {
// first argument: inline config or path to config file
Expand Down Expand Up @@ -70,8 +69,30 @@ fn build_binary(bin_name: &str, brand_settings: Brand) -> Result<()> {
if let Some(orchestrator_address) = brand_settings.orchestrator_address {
cmd.env(OCKAM_CONTROLLER_ADDRESS, orchestrator_address);
}
if let Some(commands) = brand_settings.commands {
let process_command_name = |c: &str| {
// replace _ and - with space to support writing
// commands as "node create", "node-create" or "node_create
c.replace("_", " ").replace("-", " ")
};

// A comma separated list of commands in the format `command1=customName,command2,command3`
let env_value = commands
.iter()
.map(|c| match c {
Command::Simple(c) => process_command_name(c),
Command::Mapped(map) => map
.iter()
.map(|(k, v)| process_command_name(&format!("{}={}", k, v)))
.collect::<Vec<String>>()
.join(","),
})
.collect::<Vec<String>>()
.join(",");
cmd.env(OCKAM_COMMANDS, env_value);
}
if let Some(build_args) = brand_settings.build_args {
cmd.args(build_args.split_whitespace());
cmd.args(build_args);
}

let res = cmd
Expand Down Expand Up @@ -131,7 +152,8 @@ struct Brand {
home_dir: Option<String>,
orchestrator_identifier: Option<String>,
orchestrator_address: Option<String>,
build_args: Option<String>,
commands: Option<Vec<Command>>,
build_args: Option<Vec<String>>,
}

impl Display for Brand {
Expand Down Expand Up @@ -182,13 +204,23 @@ impl Display for Brand {
writeln!(
f,
"{}",
fmt_log!("{PADDING}build args {}", color_primary(build_args))
fmt_log!(
"{PADDING}build args {}",
color_primary(build_args.join(" "))
)
)?;
}
Ok(())
}
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(untagged)]
enum Command {
Simple(String),
Mapped(HashMap<String, String>),
}

#[cfg(test)]
mod tests {
use super::*;
Expand All @@ -202,25 +234,40 @@ mod tests {
home_dir: /home/brand1
orchestrator_identifier: brand1
orchestrator_address: brand1.network
commands:
- node_list
- "node create": "init"
bin2:
support_email: [email protected]
brand_name: Brand2
build_args:
- --release
- --target
- armv7-unknown-linux-gnueabihf
"#;
let parsed: Config = serde_yaml::from_str(config).unwrap();
assert_eq!(parsed.items.len(), 2);
assert_eq!(parsed.items["bin1"].brand_name.as_deref(), Some("Brand1"));
assert_eq!(parsed.items["bin2"].support_email, "[email protected]");
assert_eq!(parsed.items["bin2"].brand_name.as_deref(), Some("Brand2"));

let mut processed = parsed.clone();
processed.process_defaults().unwrap();
assert_eq!(parsed.items["bin1"], processed.items["bin1"]);

// No defaults used, should be the same as parsed
let bin1 = &processed.items["bin1"];
assert_eq!(&parsed.items["bin1"], bin1);

// Check bin2 defaults
let bin2 = &processed.items["bin2"];
assert_eq!(bin2.support_email, "[email protected]");
assert_eq!(bin2.brand_name.as_deref(), Some("Brand2"));
assert_eq!(bin2.home_dir.as_ref().unwrap(), "$HOME/.bin2");
assert_eq!(bin2.orchestrator_identifier.as_deref(), None);
assert_eq!(bin2.orchestrator_address.as_deref(), None);
assert_eq!(bin2.commands.as_deref(), None);
assert_eq!(
bin2.build_args.clone().unwrap(),
vec!["--release", "--target", "armv7-unknown-linux-gnueabihf",]
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,9 @@ bin:
home_dir: /home/brand1 # if not set, will default to $HOME/.bin
orchestrator_identifier: brand1 # if not set, will default to the OCKAM_CONTROLLER_IDENTITY_ID env var
orchestrator_address: brand1.network # if not set, will default to the OCKAM_CONTROLLER_ADDR env var
build_args: # if not set, will default to empty string
- --release
commands:
- node: "host"
- node_list
- node_create: "init"
108 changes: 108 additions & 0 deletions implementations/rust/ockam/ockam_command/src/branding.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
use crate::environment::compile_time_vars::COMMANDS;
use crate::Result;
use once_cell::sync::Lazy;
use std::fmt::{Debug, Formatter};

pub(crate) fn name(name: &str) -> &'static str {
CUSTOM_COMMANDS.name(name)
}

pub(crate) fn hide(name: &str) -> bool {
CUSTOM_COMMANDS.hide(name)
}

pub(crate) static CUSTOM_COMMANDS: Lazy<Commands> =
Lazy::new(|| Commands::from_env().expect("Failed to load custom commands"));

pub(crate) struct Commands {
commands: Vec<Command>,
}

pub(crate) struct Command {
name: String,
custom_name: String,
}

impl Debug for Command {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Command")
.field("name", &self.name)
.field("custom_name", &self.custom_name)
.finish()
}
}

impl Commands {
fn new(commands: &str) -> Result<Self> {
let commands = commands
.split(',')
.filter_map(|c| {
if c.is_empty() {
return None;
}
let mut parts = c.split('=');
let name = match parts.next() {
Some(name) => name,
None => return None,
};
let custom_name = parts.next().unwrap_or(name);
Some(Command {
name: name.to_string(),
custom_name: custom_name.to_string(),
})
})
.collect();
Ok(Self { commands })
}

pub fn from_env() -> Result<Self> {
Self::new(COMMANDS)
}

pub fn hide(&self, command_name: &str) -> bool {
if self.commands.is_empty() {
return false;
}
!self.commands.iter().any(|c| c.name == command_name)
}

pub fn name(&self, command_name: &str) -> &'static str {
if self.commands.is_empty() {
return Box::leak(command_name.to_string().into_boxed_str());
}
self.commands
.iter()
.find(|c| c.name == command_name)
.map(|c| Box::leak(c.custom_name.clone().into_boxed_str()))
.unwrap_or(Box::leak(command_name.to_string().into_boxed_str()))
}
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn test_hide() {
let commands = Commands::new("node create=host create,project,enroll").unwrap();
assert!(!commands.hide("node create"));
assert!(!commands.hide("project"));
assert!(!commands.hide("enroll"));
assert!(commands.hide("command4"));

let commands = Commands::new("").unwrap();
assert!(!commands.hide("command1"));
}

#[test]
fn test_commands() {
let commands = Commands::new("node create=host create,project,enroll").unwrap();
assert_eq!(commands.name("node create"), "host create");
assert_eq!(commands.name("project"), "project");
assert_eq!(commands.name("enroll"), "enroll");
assert_eq!(commands.name("command4"), "command4");

let commands = Commands::new("").unwrap();
assert_eq!(commands.name("command1"), "command1");
}
}
Loading
Loading