Skip to content
Draft
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
5 changes: 5 additions & 0 deletions .pipelines/templates/stages/trident_rpms/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,10 @@ steps:
version=$(echo $full_version | cut -d'-' -f1)
prerelease=$(echo $full_version | cut -d'-' -f2-)

# Application Insights connection string identifying best-effort
# telemetry as coming from Trident's own CI/CD pipeline builds
AZURE_MONITOR_CONNECTION_STRING="InstrumentationKey=e32fc20f-2cc6-4d86-9e12-ab5d24b366f7;IngestionEndpoint=https://eastus2-3.in.applicationinsights.azure.com/;LiveEndpoint=https://eastus2.livediagnostics.monitor.azure.com/;ApplicationId=fb8e8afb-99bd-4143-ae31-22f9070005e2"

# Build RPMs and export only the artifact tarball (no image load/unpack).
# CARGO_REGISTRIES_BMP_PUBLICPACKAGES_TOKEN is populated by the CargoAuthenticate task.
outdir="/tmp/_rpm_artifacts"
Expand All @@ -80,6 +84,7 @@ steps:
--build-arg RPM_PACKAGES="$RPM_PACKAGES" \
--build-arg RUST_PACKAGE="$RUST_PACKAGE" \
--build-arg RPM_DEST="$RPM_DEST" \
--build-arg AZURE_MONITOR_CONNECTION_STRING="$AZURE_MONITOR_CONNECTION_STRING" \
--target artifact \
--output type=local,dest="$outdir" \
.
Expand Down
3 changes: 2 additions & 1 deletion crates/trident/build.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
fn main() -> Result<(), Box<dyn std::error::Error>> {
println!("cargo:rerun-if-env-changed=TRIDENT_VERSION");
println!("cargo:rerun-if-env-changed=AZURE_MONITOR_CONNECTION_STRING");
Ok(())
}
}
130 changes: 123 additions & 7 deletions crates/trident/src/agentconfig.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,30 +7,65 @@ use trident_api::{
error::TridentError,
};

/// Whether Trident should attempt to send tracing data to Application
/// Insights (best-effort, and only when a connection string was compiled
/// into the binary -- see [`crate::AZURE_MONITOR_CONNECTION_STRING`]).
///
/// Defaults to [`TelemetryPreference::OptOut`]: telemetry is disabled unless
/// a user has explicitly opted in via the Agent Configuration file.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum TelemetryPreference {
/// Telemetry is disabled. Trident will not send any tracing data off
/// the host.
#[default]
OptOut,

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we want OptOut as default?

/// Telemetry is enabled, best-effort, provided a connection string was
/// compiled into this Trident binary.
OptIn,
}

pub struct AgentConfig {
datastore: PathBuf,
telemetry: TelemetryPreference,
}

impl AgentConfig {
/// Load the AgentConfig from the default configuration file.
pub fn load() -> Result<Self, TridentError> {
Self::load_from_path(AGENT_CONFIG_PATH)
}

/// Load the AgentConfig from an arbitrary path. Split out from [`load`]
/// so the parsing logic can be unit tested without touching
/// [`AGENT_CONFIG_PATH`].
fn load_from_path(path: &str) -> Result<Self, TridentError> {
let mut config = Self {
datastore: TRIDENT_DATASTORE_PATH_DEFAULT.into(),
telemetry: TelemetryPreference::default(),
};

if let Ok(contents) = std::fs::read_to_string(AGENT_CONFIG_PATH) {
if let Ok(contents) = std::fs::read_to_string(path) {
for line in contents.lines() {
if let Some(path) = line.strip_prefix("DatastorePath=") {
config.datastore = path.trim().into();
if let Some(value) = line.strip_prefix("DatastorePath=") {
config.datastore = value.trim().into();
} else if let Some(value) = line.strip_prefix("Telemetry=") {
config.telemetry = match value.trim().to_ascii_lowercase().as_str() {
Comment thread
bfjelds marked this conversation as resolved.
"optin" => TelemetryPreference::OptIn,
"optout" => TelemetryPreference::OptOut,
other => {
debug!(
"Unrecognized Telemetry setting '{other}' in agent \
configuration file, defaulting to OptOut"
);
TelemetryPreference::OptOut
}
};
}
}
} else {
// If the config file does not exist, we proceed with defaults.
// Only log this at debug level to avoid alarming users unnecessarily.
debug!(
"Agent configuration file not found at {}, using defaults",
AGENT_CONFIG_PATH
);
debug!("Agent configuration file not found at {path}, using defaults");
}

Ok(config)
Expand All @@ -40,4 +75,85 @@ impl AgentConfig {
pub fn datastore_path(&self) -> &Path {
&self.datastore
}

/// Whether telemetry (best-effort tracing to Application Insights) is
/// enabled per the agent configuration file. Defaults to `false`
/// (opt-out) when unset or unrecognized.
pub fn telemetry_enabled(&self) -> bool {
matches!(self.telemetry, TelemetryPreference::OptIn)
}
}

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

#[test]
fn test_defaults_when_file_missing() {
let config = AgentConfig::load_from_path("/nonexistent/path/for/trident-tests.conf")
.expect("load_from_path should not fail even if the file is missing");
assert_eq!(
config.datastore_path(),
Path::new(TRIDENT_DATASTORE_PATH_DEFAULT)
);
assert!(
!config.telemetry_enabled(),
"telemetry must default to OptOut"
);
}

#[test]
fn test_telemetry_optin() {
let dir = tempfile::tempdir().unwrap();
let path = dir.path().join("trident.conf");
std::fs::write(&path, "Telemetry=OptIn\n").unwrap();

let config = AgentConfig::load_from_path(path.to_str().unwrap()).unwrap();
assert!(config.telemetry_enabled());
}

#[test]
fn test_telemetry_optout_explicit() {
let dir = tempfile::tempdir().unwrap();
let path = dir.path().join("trident.conf");
std::fs::write(&path, "Telemetry=OptOut\n").unwrap();

let config = AgentConfig::load_from_path(path.to_str().unwrap()).unwrap();
assert!(!config.telemetry_enabled());
}

#[test]
fn test_telemetry_is_case_insensitive() {
let dir = tempfile::tempdir().unwrap();
let path = dir.path().join("trident.conf");
std::fs::write(&path, "Telemetry=OPTIN\n").unwrap();

let config = AgentConfig::load_from_path(path.to_str().unwrap()).unwrap();
assert!(config.telemetry_enabled());
}

#[test]
fn test_telemetry_unrecognized_value_defaults_optout() {
let dir = tempfile::tempdir().unwrap();
let path = dir.path().join("trident.conf");
std::fs::write(&path, "Telemetry=maybe\n").unwrap();

let config = AgentConfig::load_from_path(path.to_str().unwrap()).unwrap();
assert!(!config.telemetry_enabled());
}

#[test]
fn test_datastore_and_telemetry_together() {
let dir = tempfile::tempdir().unwrap();
let path = dir.path().join("trident.conf");
std::fs::write(
&path,
"DatastorePath=/custom/path.sqlite\nTelemetry=OptIn\n",
)
.unwrap();

let config = AgentConfig::load_from_path(path.to_str().unwrap()).unwrap();
assert!(config.telemetry_enabled());
assert_eq!(config.datastore_path(), Path::new("/custom/path.sqlite"));
}
}
Loading