mod common;
use std::env;
use std::sync::Arc;
use std::time::Duration;
use common::LogRecord;
use policy_rs::{
ContentType, EvaluateResult, HttpProvider, HttpProviderConfig, PolicyEngine, PolicyRegistry,
otel_common::{AnyValue, KeyValue, any_value},
proto::tero::policy::v1::ClientMetadata,
};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
dotenvy::dotenv().ok();
let access_token =
env::var("TERO_ACCESS_TOKEN").expect("TERO_ACCESS_TOKEN environment variable is required");
fn kv(key: &str, value: &str) -> KeyValue {
KeyValue {
key: key.to_string(),
value: Some(AnyValue {
value: Some(any_value::Value::StringValue(value.to_string())),
}),
}
}
let client_metadata = ClientMetadata {
supported_policy_stages: vec![],
labels: vec![kv("workspace.id", "example-workspace")],
resource_attributes: vec![
kv("service.instance.id", "http-provider-example-1"),
kv("service.name", "http-provider-example"),
kv("service.namespace", "examples"),
kv("service.version", "0.1.0"),
],
};
let config = HttpProviderConfig::new("http://control-plane-sync.orb.local:8090/v1/policy/sync")
.poll_interval(Duration::from_secs(30))
.header("Authorization", format!("Bearer {}", access_token))
.content_type(ContentType::Json)
.client_metadata(client_metadata);
let provider = Arc::new(HttpProvider::new_with_initial_fetch(config).await?);
let registry = Arc::new(PolicyRegistry::new());
registry.subscribe(provider.as_ref())?;
println!(
"Loaded {} policies from HTTP endpoint",
registry.snapshot().len()
);
let engine = PolicyEngine::new();
let snapshot = registry.snapshot();
let logs = vec![
LogRecord::new("Application started successfully", "INFO"),
LogRecord::new("Error: Connection timeout", "ERROR"),
LogRecord::new("Debug: Cache miss for key xyz", "DEBUG"),
];
for (i, log) in logs.iter().enumerate() {
let result = engine.evaluate(&snapshot, log).await?;
println!(
"\nLog {}: [{}] {}",
i + 1,
log.severity.as_deref().unwrap_or(""),
log.body.as_deref().unwrap_or("")
);
match result {
EvaluateResult::NoMatch => {
println!(" -> No policy matched, pass through");
}
EvaluateResult::Keep {
policy_id,
transformed,
} => {
let suffix = if transformed { " (transformed)" } else { "" };
println!(" -> KEEP (policy: {}){}", policy_id, suffix);
}
EvaluateResult::Drop { policy_id } => {
println!(" -> DROP (policy: {})", policy_id);
}
EvaluateResult::Sample {
policy_id,
percentage,
keep,
transformed,
} => {
let suffix = if transformed { " (transformed)" } else { "" };
println!(
" -> SAMPLE {}% (policy: {}) - {}{}",
percentage,
policy_id,
if keep { "kept" } else { "dropped" },
suffix
);
}
EvaluateResult::RateLimit {
policy_id,
allowed,
transformed,
} => {
let suffix = if transformed { " (transformed)" } else { "" };
println!(
" -> RATE LIMIT (policy: {}) - {}{}",
policy_id,
if allowed { "allowed" } else { "throttled" },
suffix
);
}
}
}
Ok(())
}