use std::collections::BTreeMap;
use std::sync::Arc;
use parking_lot::RwLock;
use serde::{Deserialize, Serialize};
use serde_json::{json, Value};
use fakecloud_core::multi_account::{AccountState, MultiAccountState};
use crate::segment::StoredSegment;
pub const XRAY_SNAPSHOT_SCHEMA_VERSION: u32 = 1;
pub const DEFAULT_SAMPLING_RULE: &str = "Default";
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct XrayData {
#[serde(default)]
pub groups: BTreeMap<String, Value>,
#[serde(default)]
pub sampling_rules: BTreeMap<String, Value>,
#[serde(default)]
pub encryption_config: Option<Value>,
#[serde(default)]
pub resource_policies: BTreeMap<String, Value>,
#[serde(default = "default_destination")]
pub trace_segment_destination: String,
#[serde(default)]
pub traces: BTreeMap<String, Vec<StoredSegment>>,
#[serde(default)]
pub retrievals: BTreeMap<String, Value>,
#[serde(default)]
pub tags: BTreeMap<String, BTreeMap<String, String>>,
}
fn default_destination() -> String {
"XRay".to_string()
}
impl Default for XrayData {
fn default() -> Self {
Self {
groups: BTreeMap::new(),
sampling_rules: BTreeMap::new(),
encryption_config: None,
resource_policies: BTreeMap::new(),
trace_segment_destination: default_destination(),
traces: BTreeMap::new(),
retrievals: BTreeMap::new(),
tags: BTreeMap::new(),
}
}
}
impl XrayData {
fn seed_default_rule(&mut self, region: &str, account: &str) {
if self.sampling_rules.contains_key(DEFAULT_SAMPLING_RULE) {
return;
}
let arn = format!("arn:aws:xray:{region}:{account}:sampling-rule/{DEFAULT_SAMPLING_RULE}");
let now = now_epoch();
let record = json!({
"SamplingRule": {
"RuleName": DEFAULT_SAMPLING_RULE,
"RuleARN": arn,
"ResourceARN": "*",
"Priority": 10000,
"FixedRate": 0.05,
"ReservoirSize": 1,
"ServiceName": "*",
"ServiceType": "*",
"Host": "*",
"HTTPMethod": "*",
"URLPath": "*",
"Version": 1,
"Attributes": {},
},
"CreatedAt": now,
"ModifiedAt": now,
});
self.sampling_rules
.insert(DEFAULT_SAMPLING_RULE.to_string(), record);
}
}
pub fn now_epoch() -> f64 {
chrono::Utc::now().timestamp_millis() as f64 / 1000.0
}
impl AccountState for XrayData {
fn new_for_account(account_id: &str, region: &str, _endpoint: &str) -> Self {
let mut data = Self::default();
data.seed_default_rule(region, account_id);
data
}
}
pub type SharedXrayState = Arc<RwLock<MultiAccountState<XrayData>>>;
#[derive(Debug, Serialize, Deserialize)]
pub struct XraySnapshot {
pub schema_version: u32,
pub accounts: MultiAccountState<XrayData>,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn new_account_has_default_sampling_rule() {
let data = XrayData::new_for_account("000000000000", "us-east-1", "");
assert!(data.sampling_rules.contains_key(DEFAULT_SAMPLING_RULE));
let rule = &data.sampling_rules[DEFAULT_SAMPLING_RULE]["SamplingRule"];
assert_eq!(rule["ResourceARN"], json!("*"));
assert_eq!(rule["Priority"], json!(10000));
}
#[test]
fn default_destination_is_xray() {
let data = XrayData::default();
assert_eq!(data.trace_segment_destination, "XRay");
}
}