use std::collections::{BTreeMap, BTreeSet};
use serde::{Deserialize, Serialize};
use time::UtcDateTime;
use uuid::Uuid;
use super::capabilities::{Capability, EnrollmentStatus};
use super::shared_types::{DisconnectReason, UpdateFinalStatus};
use crate::serde_helpers::{duration_seconds, option_duration_seconds, utc_datetime_millis};
use uptrakit_shared_types::{
DiscoveredSoftware, OutputStreamType, PluginTypeId, ReleaseInfo, SecretString, UpdateCategory,
};
#[non_exhaustive]
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
pub struct PingPayload {
pub service_ts: super::shared_types::Timestamp,
}
impl PingPayload {
pub fn new(service_ts: super::shared_types::Timestamp) -> Self {
Self { service_ts }
}
}
#[non_exhaustive]
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
pub struct PongPayload {
pub service_ts: super::shared_types::Timestamp,
pub controller_ts: super::shared_types::Timestamp,
}
impl PongPayload {
pub fn new(
service_ts: super::shared_types::Timestamp,
controller_ts: super::shared_types::Timestamp,
) -> Self {
Self {
service_ts,
controller_ts,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
pub struct HostInfo {
pub machine_id: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub os_type: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub os_version: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub architecture: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub hostname: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub ip_address: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub agent_host_id: Option<Uuid>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub features: Option<Vec<String>>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
pub struct EnrollPayload {
pub hostname: String,
pub friendly_name: String,
#[serde(skip_serializing_if = "Option::is_none")]
#[cfg_attr(feature = "schema", schemars(with = "Option<String>"))]
pub enrollment_token: Option<SecretString>,
pub capabilities: BTreeSet<Capability>,
pub service_app_name: String,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
pub struct RequestCertificatePayload {
pub csr_pem: String,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
pub struct RenewCertificatePayload {
pub csr_pem: String,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
pub struct ReportHostsPayload {
pub hosts: Vec<HostInfo>,
pub agent_version: String,
#[serde(default, skip_serializing_if = "BTreeSet::is_empty")]
pub capabilities: BTreeSet<Capability>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
pub struct EnrolledPayload {
pub service_id: Uuid,
#[cfg_attr(feature = "schema", schemars(with = "String"))]
pub enrollment_secret: SecretString,
pub status: EnrollmentStatus,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
pub struct ApprovedPayload {
pub service_id: Uuid,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
pub struct RejectedPayload {
pub service_id: Uuid,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
pub struct CertificatePayload {
pub cert_pem: String,
#[serde(with = "utc_datetime_millis")]
#[cfg_attr(feature = "schema", schemars(with = "i64"))]
pub not_after: UtcDateTime,
}
#[non_exhaustive]
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
pub struct ServiceSettingsPayload {
pub renewal_window_hours: u16,
#[serde(default)]
pub ca_bundle_hash: String,
#[serde(default, skip_serializing_if = "BTreeSet::is_empty")]
pub capabilities: BTreeSet<Capability>,
#[serde(default, skip_serializing_if = "ReportPageLimits::is_default")]
pub report_page_limits: ReportPageLimits,
#[serde(
default,
skip_serializing_if = "Option::is_none",
with = "option_duration_seconds",
rename = "shutdown_timeout_seconds"
)]
#[cfg_attr(feature = "schema", schemars(with = "Option<u32>"))]
pub shutdown_timeout: Option<std::time::Duration>,
#[serde(with = "duration_seconds")]
#[cfg_attr(feature = "schema", schemars(with = "u32"))]
pub ping_interval: std::time::Duration,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub tenant_id: Option<Uuid>,
#[serde(default, skip_serializing_if = "String::is_empty")]
pub trust_domain: String,
}
impl ServiceSettingsPayload {
pub fn new(renewal_window_hours: u16, ping_interval: std::time::Duration) -> Self {
Self {
renewal_window_hours,
ca_bundle_hash: String::new(),
capabilities: std::collections::BTreeSet::new(),
report_page_limits: ReportPageLimits::default(),
shutdown_timeout: None,
ping_interval,
tenant_id: None,
trust_domain: String::new(),
}
}
#[must_use]
pub fn with_ca_bundle_hash(mut self, ca_bundle_hash: String) -> Self {
self.ca_bundle_hash = ca_bundle_hash;
self
}
#[must_use]
pub fn with_capabilities(mut self, capabilities: impl IntoIterator<Item = Capability>) -> Self {
self.capabilities = capabilities.into_iter().collect();
self
}
#[must_use]
pub fn with_report_page_limits(mut self, report_page_limits: ReportPageLimits) -> Self {
self.report_page_limits = report_page_limits;
self
}
#[must_use]
pub fn with_shutdown_timeout(mut self, shutdown_timeout: std::time::Duration) -> Self {
self.shutdown_timeout = Some(shutdown_timeout);
self
}
#[must_use]
pub fn with_tenant_id(mut self, tenant_id: Uuid) -> Self {
self.tenant_id = Some(tenant_id);
self
}
#[must_use]
pub fn with_trust_domain(mut self, trust_domain: String) -> Self {
self.trust_domain = trust_domain;
self
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
pub struct ReportPageLimits {
pub report_hosts: u32,
pub version_check_results: u32,
pub discovery_results: u32,
pub batch_update_results: u32,
}
impl ReportPageLimits {
pub fn is_default(&self) -> bool {
self == &Self::default()
}
}
impl Default for ReportPageLimits {
fn default() -> Self {
Self {
report_hosts: crate::limits::MAX_REPORT_HOSTS as u32,
version_check_results: crate::limits::MAX_VERSION_CHECK_RESULTS as u32,
discovery_results: crate::limits::MAX_DISCOVERY_PLUGIN_RESULTS as u32,
batch_update_results: crate::limits::MAX_BATCH_UPDATE_RESULTS as u32,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
pub struct CaBundleUpdatedPayload {
pub ca_bundle_pem: String,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
pub struct RequestCertRenewalPayload {
pub reason: String,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
pub struct ServerRestartingPayload {
pub reason: String,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
pub struct CheckVersionsPayload {
pub host_machine_id: String,
pub assignments: Vec<VersionCheckAssignment>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
pub struct PluginAssignment {
pub plugin_type: PluginTypeId,
pub package_identifier: String,
pub config: serde_json::Value,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
pub struct VersionCheckAssignment {
pub software_item_id: Uuid,
pub name: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub detect_version: Option<PluginAssignment>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub fetch_releases: Option<PluginAssignment>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub host_software_item_id: Option<Uuid>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
pub struct VersionCheckResultsPayload {
pub results: Vec<VersionCheckResult>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
pub struct VersionCheckResult {
pub software_item_id: Uuid,
#[serde(skip_serializing_if = "Option::is_none")]
pub installed_version: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub latest_version: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub error: Option<String>,
#[serde(default)]
pub update_category: UpdateCategory,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub host_software_item_id: Option<Uuid>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub installed_display_version: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub not_ready: Option<bool>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
pub struct ExecuteUpdatePayload {
pub host_machine_id: String,
pub update_history_id: Uuid,
pub software_item_id: Uuid,
pub software_item_name: String,
pub to_version: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub detect_version_plugin: Option<PluginAssignment>,
pub execute_update_plugin: PluginAssignment,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub pre_update_hook_plugins: Vec<PluginAssignment>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub post_update_hook_plugins: Vec<PluginAssignment>,
#[serde(skip_serializing_if = "Option::is_none")]
pub release_info: Option<ReleaseInfo>,
#[serde(
with = "duration_seconds",
rename = "timeout_seconds",
default = "super::shared_types::default_update_timeout"
)]
#[cfg_attr(feature = "schema", schemars(with = "u32"))]
pub timeout: std::time::Duration,
#[serde(default)]
pub interactive: bool,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
pub struct UpdateStartedPayload {
pub update_history_id: Uuid,
#[serde(skip_serializing_if = "Option::is_none")]
pub from_version: Option<String>,
#[serde(default)]
pub interactive: bool,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
pub struct UpdateOutputPayload {
pub update_history_id: Uuid,
pub output: String,
#[serde(default)]
pub stream: OutputStreamType,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
pub struct UpdateResultPayload {
pub update_history_id: Uuid,
pub status: UpdateFinalStatus,
#[serde(skip_serializing_if = "Option::is_none")]
pub from_version: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub to_version: Option<String>,
pub output: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub error: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub resumable: Option<bool>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
pub struct ExecuteBatchUpdatePayload {
pub host_machine_id: String,
pub batch_id: Uuid,
pub plugin_type: PluginTypeId,
pub plugin_config: serde_json::Value,
pub updates: Vec<BatchUpdateItem>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub pre_update_hook_plugins: Vec<PluginAssignment>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub post_update_hook_plugins: Vec<PluginAssignment>,
#[serde(
with = "duration_seconds",
rename = "timeout_seconds",
default = "super::shared_types::default_update_timeout"
)]
#[cfg_attr(feature = "schema", schemars(with = "u32"))]
pub timeout: std::time::Duration,
#[serde(default)]
pub interactive: bool,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
pub struct BatchUpdateItem {
pub host_software_item_id: Uuid,
pub update_history_id: Uuid,
pub package_identifier: String,
pub to_version: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub release_info: Option<ReleaseInfo>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
pub struct BatchUpdateResultPayload {
pub batch_id: Uuid,
pub results: Vec<BatchUpdateItemResult>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
pub struct BatchUpdateItemResult {
pub host_software_item_id: Uuid,
pub update_history_id: Uuid,
pub status: UpdateFinalStatus,
pub output: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub installed_version: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub error: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
pub struct SetUpdateFreezePayload {
pub enabled: bool,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub reason: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
pub struct DisconnectingPayload {
pub reason: DisconnectReason,
}
impl DisconnectingPayload {
pub fn new(reason: DisconnectReason) -> Self {
Self { reason }
}
}
#[non_exhaustive]
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
pub struct RegisterPayload {
#[serde(default, skip_serializing_if = "BTreeSet::is_empty")]
pub capabilities: BTreeSet<Capability>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub runtime_instance_id: Option<Uuid>,
}
impl RegisterPayload {
pub fn new(capabilities: impl IntoIterator<Item = Capability>) -> Self {
Self {
capabilities: capabilities.into_iter().collect(),
runtime_instance_id: None,
}
}
#[must_use]
pub fn with_runtime_instance_id(mut self, runtime_instance_id: Uuid) -> Self {
self.runtime_instance_id = Some(runtime_instance_id);
self
}
}
#[non_exhaustive]
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
pub struct ServiceConfigEntry {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub tenant_id: Option<Uuid>,
pub key: String,
pub value: serde_json::Value,
}
impl ServiceConfigEntry {
pub fn new(tenant_id: Option<Uuid>, key: String, value: serde_json::Value) -> Self {
Self {
tenant_id,
key,
value,
}
}
}
#[non_exhaustive]
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
pub struct ServiceConfigKey {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub tenant_id: Option<Uuid>,
pub key: String,
}
impl ServiceConfigKey {
pub fn new(tenant_id: Option<Uuid>, key: String) -> Self {
Self { tenant_id, key }
}
}
#[non_exhaustive]
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
pub struct StoreServiceConfigPayload {
pub request_id: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub tenant_id: Option<Uuid>,
pub key: String,
pub value: serde_json::Value,
#[serde(default)]
pub sensitive: bool,
}
impl StoreServiceConfigPayload {
pub fn new(
request_id: String,
tenant_id: Option<Uuid>,
key: String,
value: serde_json::Value,
sensitive: bool,
) -> Self {
Self {
request_id,
tenant_id,
key,
value,
sensitive,
}
}
}
#[non_exhaustive]
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
pub struct DeleteServiceConfigPayload {
pub request_id: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub tenant_id: Option<Uuid>,
pub key: String,
}
impl DeleteServiceConfigPayload {
pub fn new(request_id: String, tenant_id: Option<Uuid>, key: String) -> Self {
Self {
request_id,
tenant_id,
key,
}
}
}
#[non_exhaustive]
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
pub struct ServiceConfigAckPayload {
pub request_id: String,
pub success: bool,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub error: Option<String>,
}
impl ServiceConfigAckPayload {
pub fn success(request_id: String) -> Self {
Self {
request_id,
success: true,
error: None,
}
}
pub fn error(request_id: String, error: String) -> Self {
Self {
request_id,
success: false,
error: Some(error),
}
}
}
#[non_exhaustive]
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
pub struct ServiceConfigDeliveryPayload {
pub entries: Vec<ServiceConfigEntry>,
}
impl ServiceConfigDeliveryPayload {
pub fn new(entries: Vec<ServiceConfigEntry>) -> Self {
Self { entries }
}
}
#[non_exhaustive]
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
pub struct ServiceConfigUpdatedPayload {
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub changed: Vec<ServiceConfigEntry>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub deleted: Vec<ServiceConfigKey>,
}
impl ServiceConfigUpdatedPayload {
pub fn new(changed: Vec<ServiceConfigEntry>, deleted: Vec<ServiceConfigKey>) -> Self {
Self { changed, deleted }
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
pub struct ServiceCredentialsPayload {
#[serde(skip_serializing_if = "Option::is_none")]
#[cfg_attr(feature = "schema", schemars(with = "Option<String>"))]
pub db_url: Option<SecretString>,
#[serde(skip_serializing_if = "Option::is_none")]
#[cfg_attr(feature = "schema", schemars(with = "Option<String>"))]
pub master_key_hex: Option<SecretString>,
#[serde(skip_serializing_if = "Option::is_none")]
pub nats_url: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
pub struct RequestCaRotationPayload {
pub reason: String,
}
#[non_exhaustive]
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
pub struct RequestCrlRenewalPayload {}
#[non_exhaustive]
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
pub struct SoftwareStatesChangedPayload {
pub tenant_id: uuid::Uuid,
}
impl SoftwareStatesChangedPayload {
pub fn new(tenant_id: uuid::Uuid) -> Self {
Self { tenant_id }
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
pub struct TokenRevokedPayload {
#[serde(skip_serializing_if = "Option::is_none")]
pub jti: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub exp: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub user_id: Option<Uuid>,
#[serde(skip_serializing_if = "Option::is_none")]
pub iat_cutoff: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub purge_after: Option<i64>,
}
#[non_exhaustive]
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
pub struct AccessInvalidatedPayload {
pub user_ids: Vec<Uuid>,
pub role_ids: Vec<Uuid>,
}
impl AccessInvalidatedPayload {
pub fn new(user_ids: Vec<Uuid>, role_ids: Vec<Uuid>) -> Self {
Self { user_ids, role_ids }
}
}
#[non_exhaustive]
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
pub struct HostStateMetadata {
pub host_id: Uuid,
pub hostname: String,
pub friendly_name: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub os_type: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub os_version: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub architecture: Option<String>,
#[serde(default)]
pub tags: Vec<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub agent_version: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub agent_last_seen_at: Option<String>,
}
impl HostStateMetadata {
pub fn new(host_id: Uuid, hostname: String, friendly_name: String) -> Self {
Self {
host_id,
hostname,
friendly_name,
os_type: None,
os_version: None,
architecture: None,
tags: Vec::new(),
agent_version: None,
agent_last_seen_at: None,
}
}
}
#[non_exhaustive]
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
pub struct HostConnectivityUpdate {
pub host_id: Uuid,
pub online: bool,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub last_seen_at: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub agent_version: Option<String>,
}
impl HostConnectivityUpdate {
pub fn online(
host_id: Uuid,
last_seen_at: Option<String>,
agent_version: Option<String>,
) -> Self {
Self {
host_id,
online: true,
last_seen_at,
agent_version,
}
}
pub fn offline(host_id: Uuid, last_seen_at: Option<String>) -> Self {
Self {
host_id,
online: false,
last_seen_at,
agent_version: None,
}
}
}
#[non_exhaustive]
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
pub struct HostConnectivityUpdatedPayload {
pub tenant_id: Uuid,
pub updates: Vec<HostConnectivityUpdate>,
}
impl HostConnectivityUpdatedPayload {
pub fn new(tenant_id: Uuid, updates: Vec<HostConnectivityUpdate>) -> Self {
Self { tenant_id, updates }
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
pub struct SoftwareStatesPage {
pub page_index: u32,
pub total_pages: u32,
}
impl SoftwareStatesPage {
pub fn single() -> Self {
Self {
page_index: 0,
total_pages: 1,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
pub struct SoftwareStatesPayload {
pub tenant_id: Uuid,
pub items: Vec<SoftwareStateItem>,
#[serde(default)]
pub host_summaries: Vec<HostPackageSummary>,
#[serde(default)]
pub hosts: Vec<HostStateMetadata>,
pub page: SoftwareStatesPage,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
pub struct SoftwareStateItem {
pub software_item_id: Uuid,
pub name: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub icon_url: Option<String>,
pub hosts: Vec<SoftwareStateHostEntry>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
pub struct SoftwareStateHostEntry {
pub host_id: Uuid,
pub hostname: String,
pub friendly_name: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub installed_version: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub latest_version: Option<String>,
pub update_available: bool,
#[serde(default)]
pub update_in_progress: bool,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub release_url: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub release_notes: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub update_category: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub release_date: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub last_checked_at: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
pub struct ServiceUpdateTriggerPayload {
pub tenant_id: Uuid,
pub software_item_id: Uuid,
pub host_id: Uuid,
pub to_version: String,
pub actor_service_id: Uuid,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
pub struct HostPackageSummary {
pub host_id: Uuid,
pub hostname: String,
#[serde(default)]
pub friendly_name: String,
pub pending_count: u32,
pub security_pending_count: u32,
pub total_count: u32,
pub update_in_progress: bool,
#[serde(default)]
pub bugfix_count: u32,
#[serde(default)]
pub feature_count: u32,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
pub struct ServiceHostBatchUpdateTriggerPayload {
pub tenant_id: Uuid,
pub host_id: Uuid,
pub actor_service_id: Uuid,
#[serde(default)]
pub security_only: bool,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
pub struct AuditEventPayload {
pub action_type: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub tenant_id: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub target_type: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub target_id: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub target_display: Option<String>,
pub outcome: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub details_json: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub request_id: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub correlation_id: Option<uuid::Uuid>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
pub struct DiscoverSoftwarePayload {
pub host_machine_id: String,
pub plugins: Vec<DiscoveryPluginAssignment>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
pub struct DiscoveryPluginAssignment {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub plugin_config_id: Option<Uuid>,
pub plugin_type: PluginTypeId,
pub config: serde_json::Value,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
pub struct DiscoveryResultsPayload {
pub host_machine_id: String,
pub results: Vec<DiscoveryPluginResult>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
pub struct DiscoveryPluginResult {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub plugin_config_id: Option<Uuid>,
pub plugin_type: PluginTypeId,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub discoveries: Vec<DiscoveredSoftware>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub error: Option<String>,
}
#[non_exhaustive]
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
pub struct ReportPluginConfigPayload {
pub request_id: String,
pub plugin_type: String,
pub name: String,
pub config: serde_json::Value,
}
#[non_exhaustive]
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
pub struct ReportPluginConfigResponsePayload {
pub request_id: String,
pub success: bool,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub plugin_config_id: Option<Uuid>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub error: Option<String>,
}
#[non_exhaustive]
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
pub struct UpdateStdinDataPayload {
pub update_history_id: Uuid,
pub data: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub signal: Option<i32>,
}
impl UpdateStdinDataPayload {
pub fn new(update_history_id: Uuid, data: String) -> Self {
Self {
update_history_id,
data,
signal: None,
}
}
pub fn with_signal(update_history_id: Uuid, signal: i32) -> Self {
Self {
update_history_id,
data: String::new(),
signal: Some(signal),
}
}
}
#[non_exhaustive]
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
pub struct StdinAttentionPayload {
pub update_history_id: Uuid,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub hint: Option<String>,
}
impl StdinAttentionPayload {
pub fn new(update_history_id: Uuid) -> Self {
Self {
update_history_id,
hint: None,
}
}
pub fn with_hint(update_history_id: Uuid, hint: String) -> Self {
Self {
update_history_id,
hint: Some(hint),
}
}
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
pub struct BroadcastAdminEventPayload {
pub tenant_id: Option<Uuid>,
pub event_json: String,
}
#[non_exhaustive]
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
pub struct WorkloadClaimPayload {
pub claims: BTreeMap<String, Uuid>,
}
impl WorkloadClaimPayload {
pub fn new(claims: BTreeMap<String, Uuid>) -> Self {
Self { claims }
}
}
#[non_exhaustive]
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
pub struct WorkloadClaimResultPayload {
#[serde(default, skip_serializing_if = "BTreeSet::is_empty")]
pub granted: BTreeSet<String>,
#[serde(default, skip_serializing_if = "BTreeSet::is_empty")]
pub rejected: BTreeSet<String>,
}
impl WorkloadClaimResultPayload {
pub fn new(granted: BTreeSet<String>, rejected: BTreeSet<String>) -> Self {
Self { granted, rejected }
}
}
#[non_exhaustive]
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
pub struct WorkloadReleasePayload {
pub keys: BTreeSet<String>,
}
impl WorkloadReleasePayload {
pub fn new(keys: BTreeSet<String>) -> Self {
Self { keys }
}
}
#[non_exhaustive]
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
pub struct WorkloadClaimAnnouncementPayload {
pub service_id: Uuid,
pub controller_id: Uuid,
#[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
pub claimed: BTreeMap<String, Uuid>,
#[serde(default, skip_serializing_if = "BTreeSet::is_empty")]
pub released: BTreeSet<String>,
pub claimed_at: String,
}
impl WorkloadClaimAnnouncementPayload {
pub fn new(
service_id: Uuid,
controller_id: Uuid,
claimed: BTreeMap<String, Uuid>,
released: BTreeSet<String>,
claimed_at: String,
) -> Self {
Self {
service_id,
controller_id,
claimed,
released,
claimed_at,
}
}
}
#[non_exhaustive]
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
pub struct WorkloadClaimSyncRequestPayload {
pub controller_id: Uuid,
}
impl WorkloadClaimSyncRequestPayload {
pub fn new(controller_id: Uuid) -> Self {
Self { controller_id }
}
}
#[non_exhaustive]
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
pub struct WorkloadClaimSyncResponsePayload {
pub controller_id: Uuid,
pub claims: BTreeMap<String, WorkloadClaimSyncEntry>,
}
impl WorkloadClaimSyncResponsePayload {
pub fn new(controller_id: Uuid, claims: BTreeMap<String, WorkloadClaimSyncEntry>) -> Self {
Self {
controller_id,
claims,
}
}
}
#[non_exhaustive]
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
pub struct WorkloadClaimSyncEntry {
pub service_id: Uuid,
pub tenant_id: Uuid,
pub claimed_at: String,
}
impl WorkloadClaimSyncEntry {
pub fn new(service_id: Uuid, tenant_id: Uuid, claimed_at: String) -> Self {
Self {
service_id,
tenant_id,
claimed_at,
}
}
}
pub use uptrakit_shared_types::ConfigTestKind;
#[non_exhaustive]
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
pub struct TestPluginConfigPayload {
pub request_id: String,
pub host_machine_id: String,
pub test_kind: ConfigTestKind,
pub plugin_type: String,
pub config: serde_json::Value,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub package_identifier: Option<String>,
}
impl TestPluginConfigPayload {
pub fn new(
request_id: String,
host_machine_id: String,
test_kind: ConfigTestKind,
plugin_type: String,
config: serde_json::Value,
) -> Self {
Self {
request_id,
host_machine_id,
test_kind,
plugin_type,
config,
package_identifier: None,
}
}
}
#[non_exhaustive]
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
pub struct TestPluginConfigResultPayload {
pub request_id: String,
pub success: bool,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub output: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub error: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub detected_version: Option<String>,
pub duration_ms: u64,
}
impl TestPluginConfigResultPayload {
pub fn new(request_id: String, success: bool, duration_ms: u64) -> Self {
Self {
request_id,
success,
output: None,
error: None,
detected_version: None,
duration_ms,
}
}
}
#[cfg(test)]
mod resumable_tests {
use super::*;
#[test]
fn test_update_result_payload_resumable_defaults_none() {
let json = r#"{"update_history_id":"00000000-0000-0000-0000-000000000001","status":"completed","output":""}"#;
let p: UpdateResultPayload = serde_json::from_str(json).unwrap();
assert_eq!(p.resumable, None);
}
#[test]
fn test_update_result_payload_resumable_true_round_trips() {
let p = UpdateResultPayload {
update_history_id: uuid::Uuid::nil(),
status: crate::UpdateFinalStatus::Completed,
from_version: None,
to_version: None,
output: String::new(),
error: None,
resumable: Some(true),
};
let json = serde_json::to_string(&p).unwrap();
assert!(json.contains("\"resumable\":true"));
let back: UpdateResultPayload = serde_json::from_str(&json).unwrap();
assert_eq!(back.resumable, Some(true));
}
#[test]
fn test_version_check_result_not_ready_defaults_none() {
let json = r#"{"software_item_id":"00000000-0000-0000-0000-000000000001","update_category":"none"}"#;
let r: VersionCheckResult = serde_json::from_str(json).unwrap();
assert_eq!(r.not_ready, None);
}
#[test]
fn test_version_check_result_not_ready_true_round_trips() {
let r = VersionCheckResult {
software_item_id: uuid::Uuid::nil(),
installed_version: None,
latest_version: None,
error: None,
update_category: crate::UpdateCategory::default(),
host_software_item_id: None,
installed_display_version: None,
not_ready: Some(true),
};
let json = serde_json::to_string(&r).unwrap();
assert!(json.contains("\"not_ready\":true"));
let back: VersionCheckResult = serde_json::from_str(&json).unwrap();
assert_eq!(back.not_ready, Some(true));
}
}