diff --git a/src/interactive.rs b/src/interactive.rs index 0bac1e882..06c53c77b 100644 --- a/src/interactive.rs +++ b/src/interactive.rs @@ -577,8 +577,7 @@ fn get_kafka_prompts() -> Vec { /// /// Must be called **before** `PARSEABLE` is accessed and before license /// verification, so that `.parseable.env` values are loaded and the user -/// gets a chance to provide missing vars like `P_CLUSTER_SECRET` and -/// the license file paths. +/// gets a chance to provide missing vars like `P_CLUSTER_SECRET`. /// /// Returns collected `(env_var, value)` pairs. Call [`save_collected_envs`] /// after validation succeeds to persist them. @@ -597,7 +596,7 @@ pub fn prompt_enterprise_envs() -> Vec<(String, String)> { let is_interactive = io::stdin().is_terminal(); let mut collected: Vec<(String, String)> = Vec::new(); - // Phase 1: base enterprise vars (license, cluster secret, mode) + // Phase 1: base enterprise vars (cluster secret and mode) let base_prompts = get_enterprise_base_prompts(); collect_prompts( &base_prompts, @@ -696,34 +695,14 @@ fn collect_prompts( println!(); } -/// Returns base enterprise env var prompts (license, cluster secret, mode). +/// Returns base enterprise env var prompts (cluster secret and mode). fn get_enterprise_base_prompts() -> Vec { - let mut prompts = vec![ - EnvPrompt { - env_var: "P_LICENSE_DATA_FILE_PATH", - display_name: "License Data File Path", - required: true, - is_secret: false, - }, - EnvPrompt { - env_var: "P_LICENSE_SIGNATURE_BASE64", - display_name: "License Signature Base64 (priority)", - required: false, - is_secret: false, - }, - EnvPrompt { - env_var: "P_LICENSE_SIGNATURE_FILE_PATH", - display_name: "License Signature File Path", - required: false, - is_secret: false, - }, - EnvPrompt { - env_var: "P_CLUSTER_SECRET", - display_name: "Cluster Secret", - required: true, - is_secret: true, - }, - ]; + let mut prompts = vec![EnvPrompt { + env_var: "P_CLUSTER_SECRET", + display_name: "Cluster Secret", + required: true, + is_secret: true, + }]; // Enterprise rejects "all" mode, so prompt if not explicitly set if detect_mode().is_none() { diff --git a/src/lib.rs b/src/lib.rs index 5ab009230..58abee54d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -35,6 +35,7 @@ pub mod event; pub mod handlers; pub mod hottier; pub mod interactive; +pub mod license; mod livetail; pub mod metadata; pub mod metastore; diff --git a/src/license.rs b/src/license.rs new file mode 100644 index 000000000..e6efa420a --- /dev/null +++ b/src/license.rs @@ -0,0 +1,67 @@ +use chrono::{DateTime, Utc}; +use serde::{Deserialize, Serialize}; + +use crate::storage::{IngestionQuotaType, QuotaPeriod}; + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] +pub enum LicenseState { + Valid = 0, + Invalid = 1, + Expired = 2, + IngestionQuotaExceeded = 3, +} + +impl LicenseState { + pub fn as_str(self) -> &'static str { + match self { + Self::Valid => "Valid", + Self::Invalid => "Invalid", + Self::Expired => "Expired", + Self::IngestionQuotaExceeded => "IngestionQuotaExceeded", + } + } +} + +impl From for LicenseState { + fn from(value: u8) -> Self { + match value { + 0 => Self::Valid, + 2 => Self::Expired, + 3 => Self::IngestionQuotaExceeded, + _ => Self::Invalid, + } + } +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct LicenseStatus { + pub state: LicenseState, + pub reason: String, + pub checked_at: DateTime, + pub quota: Option, +} + +impl LicenseStatus { + pub fn new(state: LicenseState, reason: impl Into) -> Self { + Self { + state, + reason: reason.into(), + checked_at: Utc::now(), + quota: None, + } + } +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct QuotaStatus { + pub quota_type: IngestionQuotaType, + pub period: QuotaPeriod, + pub used: u64, + pub limit: u64, + pub remaining: u64, + pub exceeded: bool, + pub period_start: Option>, + pub period_end: Option>, +} diff --git a/src/sse/mod.rs b/src/sse/mod.rs index 3b8b36345..16cfa3b27 100644 --- a/src/sse/mod.rs +++ b/src/sse/mod.rs @@ -32,6 +32,7 @@ use tokio::sync::{RwLock, mpsc}; use tokio_stream::wrappers::ReceiverStream; use ulid::Ulid; +use crate::license::LicenseStatus; use crate::{ alerts::AlertState, parseable::DEFAULT_TENANT, @@ -214,9 +215,10 @@ pub enum Message { AlertEvent(SSEAlertInfo), ControlPlaneEvent(ControlPlaneEvent), Consent(Consent), + LicenseEvent(LicenseEvent), } -#[derive(Serialize, Debug, Deserialize)] +#[derive(Clone, Copy, Serialize, Debug, Deserialize)] #[serde(rename_all = "camelCase")] pub enum Criticality { Info, @@ -243,3 +245,10 @@ pub struct ControlPlaneEvent { pub struct Consent { pub given: bool, } + +#[derive(Serialize, Debug, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct LicenseEvent { + pub status: LicenseStatus, + pub time_tampered: bool, +}