Skip to main content

alien_core/
heartbeat.rs

1use std::collections::BTreeMap;
2
3use crate::{Platform, ResourceType};
4use chrono::{DateTime, Utc};
5use serde::{Deserialize, Serialize};
6use serde_json::Value as JsonValue;
7
8#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
9#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
10#[serde(rename_all = "camelCase")]
11pub struct ResourceHeartbeat {
12    pub deployment_id: Option<String>,
13    pub resource_id: String,
14    pub resource_type: ResourceType,
15    pub controller_platform: Platform,
16    pub backend: HeartbeatBackend,
17    pub observed_at: DateTime<Utc>,
18    pub data: ResourceHeartbeatData,
19    pub raw: Vec<RawHeartbeatSnippet>,
20}
21
22#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
23#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
24#[serde(rename_all = "camelCase")]
25pub enum HeartbeatBackend {
26    Aws,
27    Gcp,
28    Azure,
29    Kubernetes,
30    Local,
31    Managed,
32    External,
33    Test,
34}
35
36#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
37#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
38#[serde(rename_all = "kebab-case")]
39pub enum HeartbeatCollectionIssueReason {
40    Forbidden,
41    NotInstalled,
42    ApiUnavailable,
43    CollectionFailed,
44    TimedOut,
45}
46
47#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
48#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
49#[serde(tag = "resourceType", content = "data")]
50pub enum ResourceHeartbeatData {
51    #[serde(rename = "storage")]
52    Storage(StorageHeartbeatData),
53    #[serde(rename = "worker")]
54    Worker(WorkerHeartbeatData),
55    #[serde(rename = "container")]
56    Container(ContainerHeartbeatData),
57    #[serde(rename = "daemon")]
58    Daemon(DaemonHeartbeatData),
59    #[serde(rename = "compute-cluster")]
60    ComputeCluster(ComputeClusterHeartbeatData),
61    #[serde(rename = "kubernetes-cluster")]
62    KubernetesCluster(KubernetesClusterHeartbeatData),
63    #[serde(rename = "queue")]
64    Queue(QueueHeartbeatData),
65    #[serde(rename = "kv")]
66    Kv(KvHeartbeatData),
67    #[serde(rename = "vault")]
68    Vault(VaultHeartbeatData),
69    #[serde(rename = "service-account")]
70    ServiceAccount(ServiceAccountHeartbeatData),
71    #[serde(rename = "network")]
72    Network(NetworkHeartbeatData),
73    #[serde(rename = "remote-stack-management")]
74    RemoteStackManagement(RemoteStackManagementHeartbeatData),
75    #[serde(rename = "artifact-registry")]
76    ArtifactRegistry(ArtifactRegistryHeartbeatData),
77    #[serde(rename = "build")]
78    Build(BuildHeartbeatData),
79    #[serde(rename = "service_activation")]
80    ServiceActivation(ServiceActivationHeartbeatData),
81    #[serde(rename = "azure_resource_group")]
82    AzureResourceGroup(AzureResourceGroupHeartbeatData),
83    #[serde(rename = "azure_storage_account")]
84    AzureStorageAccount(AzureStorageAccountHeartbeatData),
85    #[serde(rename = "azure_container_apps_environment")]
86    AzureContainerAppsEnvironment(AzureContainerAppsEnvironmentHeartbeatData),
87    #[serde(rename = "azure_service_bus_namespace")]
88    AzureServiceBusNamespace(AzureServiceBusNamespaceHeartbeatData),
89}
90
91#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
92#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
93#[serde(rename_all = "kebab-case")]
94pub enum ObservedHealth {
95    Unknown,
96    Healthy,
97    Degraded,
98    Unhealthy,
99}
100
101#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
102#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
103#[serde(rename_all = "kebab-case")]
104pub enum ProviderLifecycleState {
105    Unknown,
106    Creating,
107    Updating,
108    Running,
109    Scaling,
110    Stopping,
111    Stopped,
112    Deleting,
113    Deleted,
114    Failed,
115}
116
117#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
118#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
119#[serde(rename_all = "camelCase")]
120pub struct HeartbeatCollectionIssue {
121    pub source: String,
122    pub reason: HeartbeatCollectionIssueReason,
123    pub severity: HeartbeatIssueSeverity,
124    pub message: String,
125}
126
127#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
128#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
129#[serde(rename_all = "kebab-case")]
130pub enum HeartbeatIssueSeverity {
131    Info,
132    Warning,
133    Error,
134}
135
136#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
137#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
138#[serde(rename_all = "camelCase")]
139pub struct KubernetesEventSnapshot {
140    pub reason: String,
141    #[serde(rename = "type")]
142    pub type_: Option<String>,
143    pub message: String,
144    pub count: Option<i32>,
145    pub first_timestamp: Option<DateTime<Utc>>,
146    pub last_timestamp: Option<DateTime<Utc>>,
147    pub event_time: Option<DateTime<Utc>>,
148    pub source: Option<KubernetesEventSource>,
149    pub involved_object: Option<KubernetesEventInvolvedObject>,
150    pub raw: Option<JsonValue>,
151}
152
153#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
154#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
155#[serde(rename_all = "camelCase")]
156pub struct KubernetesEventSource {
157    pub component: Option<String>,
158    pub host: Option<String>,
159}
160
161#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
162#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
163#[serde(rename_all = "camelCase")]
164pub struct KubernetesEventInvolvedObject {
165    pub kind: Option<String>,
166    pub namespace: Option<String>,
167    pub name: Option<String>,
168    pub uid: Option<String>,
169    pub api_version: Option<String>,
170    pub resource_version: Option<String>,
171    pub field_path: Option<String>,
172}
173
174#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
175#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
176#[serde(rename_all = "camelCase")]
177pub struct ManagedRuntimeEventSnapshot {
178    pub event_id: Option<String>,
179    pub reason: String,
180    #[serde(rename = "type")]
181    pub type_: Option<String>,
182    pub message: String,
183    pub count: Option<i32>,
184    pub first_timestamp: Option<DateTime<Utc>>,
185    pub last_timestamp: Option<DateTime<Utc>>,
186    pub event_time: Option<DateTime<Utc>>,
187    pub source: Option<ManagedRuntimeEventSource>,
188    pub involved_object: Option<ManagedRuntimeEventInvolvedObject>,
189    pub details: Option<JsonValue>,
190    pub raw: Option<JsonValue>,
191}
192
193#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
194#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
195#[serde(rename_all = "camelCase")]
196pub struct ManagedRuntimeEventSource {
197    pub component: Option<String>,
198    pub host: Option<String>,
199}
200
201#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
202#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
203#[serde(rename_all = "camelCase")]
204pub struct ManagedRuntimeEventInvolvedObject {
205    pub kind: Option<String>,
206    pub id: Option<String>,
207    pub name: Option<String>,
208    pub replica_id: Option<String>,
209    pub machine_id: Option<String>,
210    pub details: Option<JsonValue>,
211}
212
213#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
214#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
215#[serde(rename_all = "camelCase")]
216pub struct LocalRuntimeEventSnapshot {
217    pub timestamp: DateTime<Utc>,
218    pub severity: HeartbeatIssueSeverity,
219    pub kind: String,
220    pub message: String,
221    pub subject: Option<LocalRuntimeEventSubject>,
222    pub raw: Option<JsonValue>,
223}
224
225#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
226#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
227#[serde(rename_all = "camelCase")]
228pub struct LocalRuntimeEventSubject {
229    pub kind: String,
230    pub id: Option<String>,
231    pub name: Option<String>,
232}
233
234#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
235#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
236#[serde(rename_all = "camelCase")]
237pub struct RawHeartbeatSnippet {
238    pub source: String,
239    pub format: RawHeartbeatSnippetFormat,
240    pub collected_at: DateTime<Utc>,
241    pub body: String,
242    pub truncated: bool,
243}
244
245#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
246#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
247#[serde(rename_all = "kebab-case")]
248pub enum RawHeartbeatSnippetFormat {
249    Json,
250    Yaml,
251    Text,
252}
253
254#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
255#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
256#[serde(rename_all = "camelCase")]
257pub struct ObservedCounts {
258    pub desired: Option<u32>,
259    pub current: Option<u32>,
260    pub ready: Option<u32>,
261}
262
263#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
264#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
265#[serde(rename_all = "camelCase")]
266pub struct MetricSample {
267    pub value: f64,
268    pub unit: MetricUnit,
269}
270
271#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
272#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
273#[serde(rename_all = "kebab-case")]
274pub enum MetricUnit {
275    Count,
276    Percent,
277    Bytes,
278    Cores,
279    Milliseconds,
280    RequestsPerSecond,
281}
282
283#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
284#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
285#[serde(tag = "backend", rename_all = "camelCase")]
286pub enum StorageHeartbeatData {
287    AwsS3(AwsS3StorageHeartbeatData),
288    GcpCloudStorage(GcpCloudStorageHeartbeatData),
289    AzureBlob(AzureBlobStorageHeartbeatData),
290    Local(LocalStorageHeartbeatData),
291}
292
293#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
294#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
295#[serde(rename_all = "camelCase")]
296pub struct StorageHeartbeatStatus {
297    pub health: ObservedHealth,
298    pub lifecycle: ProviderLifecycleState,
299    pub message: Option<String>,
300    pub stale: bool,
301    pub partial: bool,
302    pub collection_issues: Vec<HeartbeatCollectionIssue>,
303}
304
305#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
306#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
307#[serde(rename_all = "camelCase")]
308pub struct AwsS3StorageHeartbeatData {
309    pub status: StorageHeartbeatStatus,
310    pub name: String,
311    pub region: Option<String>,
312    pub bucket_location: Option<String>,
313    pub versioning_status: Option<String>,
314    pub versioning_enabled: Option<bool>,
315    pub lifecycle_present: bool,
316    pub lifecycle_rule_count: Option<u64>,
317    pub encryption_config_present: bool,
318    pub encryption_enabled: Option<bool>,
319    pub public_access_block_present: bool,
320    pub block_public_acls: Option<bool>,
321    pub ignore_public_acls: Option<bool>,
322    pub block_public_policy: Option<bool>,
323    pub restrict_public_buckets: Option<bool>,
324    pub bucket_policy_present: Option<bool>,
325    pub bucket_acl_present: Option<bool>,
326}
327
328#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
329#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
330#[serde(rename_all = "camelCase")]
331pub struct AzureBlobStorageHeartbeatData {
332    pub status: StorageHeartbeatStatus,
333    pub name: String,
334    pub storage_account_name: Option<String>,
335    pub resource_group: Option<String>,
336    pub location: Option<String>,
337    pub account_kind: Option<String>,
338    pub sku_name: Option<String>,
339    pub sku_tier: Option<String>,
340    pub access_tier: Option<String>,
341    pub provisioning_state: Option<String>,
342    pub primary_location: Option<String>,
343    pub secondary_location: Option<String>,
344    pub status_of_primary: Option<String>,
345    pub status_of_secondary: Option<String>,
346    pub public_network_access: Option<String>,
347    pub allow_blob_public_access: Option<bool>,
348    pub encryption_key_source: Option<String>,
349    pub blob_encryption_enabled: Option<bool>,
350    pub file_encryption_enabled: Option<bool>,
351    pub queue_encryption_enabled: Option<bool>,
352    pub table_encryption_enabled: Option<bool>,
353    pub blob_versioning_enabled: Option<bool>,
354    pub blob_delete_retention_enabled: Option<bool>,
355    pub blob_delete_retention_days: Option<u64>,
356    pub container_delete_retention_enabled: Option<bool>,
357    pub container_delete_retention_days: Option<u64>,
358    pub change_feed_enabled: Option<bool>,
359    pub change_feed_retention_days: Option<u64>,
360    pub container_public_access: Option<String>,
361}
362
363#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
364#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
365#[serde(rename_all = "camelCase")]
366pub struct GcpCloudStorageHeartbeatData {
367    pub status: StorageHeartbeatStatus,
368    pub name: String,
369    pub bucket_id: Option<String>,
370    pub location: Option<String>,
371    pub location_type: Option<String>,
372    pub storage_class: Option<String>,
373    pub versioning_enabled: Option<bool>,
374    pub lifecycle_present: bool,
375    pub lifecycle_rule_count: Option<u64>,
376    pub retention_policy_effective_time: Option<String>,
377    pub retention_policy_is_locked: Option<bool>,
378    pub retention_period: Option<String>,
379    pub soft_delete_retention_duration_seconds: Option<String>,
380    pub soft_delete_effective_time: Option<String>,
381    pub uniform_bucket_level_access_enabled: Option<bool>,
382    pub uniform_bucket_level_access_locked_time: Option<String>,
383    pub public_access_prevention: Option<String>,
384    pub encryption_config_present: bool,
385    pub default_kms_key_name: Option<String>,
386}
387
388#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
389#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
390#[serde(rename_all = "camelCase")]
391pub struct LocalStorageHeartbeatData {
392    pub status: StorageHeartbeatStatus,
393    pub path: String,
394    pub path_exists: bool,
395    pub is_directory: Option<bool>,
396    pub readonly: Option<bool>,
397    pub modified_at: Option<DateTime<Utc>>,
398}
399
400#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
401#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
402#[serde(tag = "backend", rename_all = "camelCase")]
403pub enum WorkerHeartbeatData {
404    AwsLambda(AwsLambdaWorkerHeartbeatData),
405    GcpCloudRun(GcpCloudRunWorkerHeartbeatData),
406    AzureContainerApps(AzureContainerAppsWorkerHeartbeatData),
407    Kubernetes(KubernetesWorkerHeartbeatData),
408    Local(LocalWorkerHeartbeatData),
409}
410
411#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
412#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
413#[serde(tag = "backend", rename_all = "camelCase")]
414pub enum ContainerHeartbeatData {
415    HorizonPlatform(HorizonContainerHeartbeatData),
416    Kubernetes(KubernetesContainerHeartbeatData),
417    Local(LocalContainerHeartbeatData),
418}
419
420#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
421#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
422#[serde(tag = "backend", rename_all = "camelCase")]
423pub enum DaemonHeartbeatData {
424    Aws(AwsDaemonHeartbeatData),
425    Gcp(GcpDaemonHeartbeatData),
426    Azure(AzureDaemonHeartbeatData),
427    Kubernetes(KubernetesDaemonHeartbeatData),
428    Local(LocalDaemonHeartbeatData),
429}
430
431#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
432#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
433#[serde(rename_all = "camelCase")]
434pub struct WorkloadHeartbeatStatus {
435    pub health: ObservedHealth,
436    pub lifecycle: ProviderLifecycleState,
437    pub message: Option<String>,
438    pub stale: bool,
439    pub partial: bool,
440    pub collection_issues: Vec<HeartbeatCollectionIssue>,
441}
442
443#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
444#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
445#[serde(rename_all = "camelCase")]
446pub struct WorkloadReplicaStatus {
447    pub desired: Option<u32>,
448    pub current: Option<u32>,
449    pub ready: Option<u32>,
450    pub available: Option<u32>,
451    pub updated: Option<u32>,
452    pub misscheduled: Option<u32>,
453}
454
455#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
456#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
457#[serde(rename_all = "camelCase")]
458pub struct AwsLambdaWorkerHeartbeatData {
459    pub status: WorkloadHeartbeatStatus,
460    pub function_name: String,
461    pub runtime: Option<String>,
462    pub package_type: Option<String>,
463    pub memory_size_mb: Option<i64>,
464    pub timeout_seconds: Option<i64>,
465    pub version: Option<String>,
466    pub revision_id: Option<String>,
467    pub last_modified: Option<String>,
468    pub state: Option<String>,
469    pub state_reason: Option<String>,
470    pub state_reason_code: Option<String>,
471    pub last_update_status: Option<String>,
472    pub last_update_status_reason: Option<String>,
473    pub last_update_status_reason_code: Option<String>,
474    pub code_sha256: Option<String>,
475    pub layer_count: u32,
476    pub function_url_auth_type: Option<String>,
477    pub function_url_cors_present: bool,
478    pub trigger_count: u32,
479}
480
481#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
482#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
483#[serde(rename_all = "camelCase")]
484pub struct GcpCloudRunWorkerHeartbeatData {
485    pub status: WorkloadHeartbeatStatus,
486    pub service: String,
487    pub region: Option<String>,
488    pub uri: Option<String>,
489    pub urls: Vec<String>,
490    pub latest_created_revision: Option<String>,
491    pub latest_ready_revision: Option<String>,
492    pub generation: Option<i64>,
493    pub observed_generation: Option<i64>,
494    pub traffic_count: u32,
495    pub min_instance_count: Option<i32>,
496    pub max_instance_count: Option<i32>,
497    pub container_image: Option<String>,
498    pub cpu_limit: Option<String>,
499    pub memory_limit: Option<String>,
500}
501
502#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
503#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
504#[serde(rename_all = "camelCase")]
505pub struct AzureContainerAppsWorkerHeartbeatData {
506    pub status: WorkloadHeartbeatStatus,
507    pub app_name: String,
508    pub revision: Option<String>,
509    pub environment_name: Option<String>,
510    pub provisioning_state: Option<String>,
511    pub running_status: Option<String>,
512    pub ingress_fqdn: Option<String>,
513    pub min_replicas: Option<i32>,
514    pub max_replicas: Option<i32>,
515    pub cpu: Option<f64>,
516    pub memory: Option<String>,
517}
518
519#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
520#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
521#[serde(rename_all = "camelCase")]
522pub struct KubernetesWorkerHeartbeatData {
523    pub status: WorkloadHeartbeatStatus,
524    pub namespace: String,
525    pub name: String,
526    pub workload_kind: KubernetesWorkloadKind,
527    pub replicas: WorkloadReplicaStatus,
528    pub restarts: Option<u32>,
529    pub cpu: Option<MetricSample>,
530    pub memory: Option<MetricSample>,
531    pub workload: Option<KubernetesWorkloadStatus>,
532    pub pods: Vec<KubernetesPodRuntimeUnitStatus>,
533    pub trigger_count: u32,
534    pub events: Vec<KubernetesEventSnapshot>,
535}
536
537#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
538#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
539#[serde(rename_all = "camelCase")]
540pub struct LocalWorkerHeartbeatData {
541    pub status: WorkloadHeartbeatStatus,
542    pub pid: Option<u32>,
543    pub command_supported: bool,
544    pub image_path_present: bool,
545    pub readiness_probe_ok: Option<bool>,
546    pub trigger_count: u32,
547    pub cpu: Option<MetricSample>,
548    pub memory: Option<MetricSample>,
549    pub process: Option<LocalRuntimeUnitStatus>,
550    pub events: Vec<LocalRuntimeEventSnapshot>,
551}
552
553#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
554#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
555#[serde(rename_all = "camelCase")]
556pub struct HorizonContainerHeartbeatData {
557    pub status: WorkloadHeartbeatStatus,
558    pub container_id: String,
559    pub image: Option<String>,
560    pub scheduling_mode: HorizonWorkloadSchedulingMode,
561    pub replicas: WorkloadReplicaStatus,
562    pub cpu: Option<MetricSample>,
563    pub memory: Option<MetricSample>,
564    pub attention_count: u32,
565    pub replica_units: Vec<ManagedRuntimeUnitStatus>,
566    pub events: Vec<ManagedRuntimeEventSnapshot>,
567}
568
569#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
570#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
571#[serde(rename_all = "camelCase")]
572pub enum HorizonWorkloadSchedulingMode {
573    Replicated,
574    Stateful,
575    Daemon,
576}
577
578#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
579#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
580#[serde(rename_all = "camelCase")]
581pub struct KubernetesContainerHeartbeatData {
582    pub status: WorkloadHeartbeatStatus,
583    pub namespace: String,
584    pub name: String,
585    pub workload_kind: KubernetesWorkloadKind,
586    pub replicas: WorkloadReplicaStatus,
587    pub restarts: Option<u32>,
588    pub cpu: Option<MetricSample>,
589    pub memory: Option<MetricSample>,
590    pub workload: Option<KubernetesWorkloadStatus>,
591    pub pods: Vec<KubernetesPodRuntimeUnitStatus>,
592    pub events: Vec<KubernetesEventSnapshot>,
593}
594
595#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
596#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
597#[serde(rename_all = "camelCase")]
598pub struct LocalContainerHeartbeatData {
599    pub status: WorkloadHeartbeatStatus,
600    pub container_id: Option<String>,
601    pub name: Option<String>,
602    pub image: Option<String>,
603    pub runtime_status: Option<String>,
604    pub restart_count: Option<u32>,
605    pub port_count: u32,
606    pub bind_mount_count: u32,
607    pub local_url: Option<String>,
608    pub runtime_reachable: bool,
609    pub cpu: Option<MetricSample>,
610    pub memory: Option<MetricSample>,
611    pub container_unit: Option<LocalRuntimeUnitStatus>,
612    pub events: Vec<LocalRuntimeEventSnapshot>,
613}
614
615#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
616#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
617#[serde(rename_all = "camelCase")]
618pub struct AwsDaemonHeartbeatData {
619    pub status: WorkloadHeartbeatStatus,
620    pub horizon_cluster_id: String,
621    pub daemon_name: String,
622    pub horizon_status: String,
623    pub horizon_status_reason: Option<String>,
624    pub horizon_status_message: Option<String>,
625    pub capacity_group: String,
626    pub desired_machines: u32,
627    pub assigned_machines: u32,
628    pub healthy_instances: u32,
629    pub unavailable_instances: u32,
630    pub command_supported: bool,
631    pub latest_update_timestamp: String,
632    pub daemon_instances: Vec<ManagedRuntimeUnitStatus>,
633    pub events: Vec<ManagedRuntimeEventSnapshot>,
634}
635
636#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
637#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
638#[serde(rename_all = "camelCase")]
639pub struct GcpDaemonHeartbeatData {
640    pub status: WorkloadHeartbeatStatus,
641    pub horizon_cluster_id: String,
642    pub daemon_name: String,
643    pub horizon_status: String,
644    pub horizon_status_reason: Option<String>,
645    pub horizon_status_message: Option<String>,
646    pub capacity_group: String,
647    pub desired_machines: u32,
648    pub assigned_machines: u32,
649    pub healthy_instances: u32,
650    pub unavailable_instances: u32,
651    pub command_supported: bool,
652    pub latest_update_timestamp: String,
653    pub daemon_instances: Vec<ManagedRuntimeUnitStatus>,
654    pub events: Vec<ManagedRuntimeEventSnapshot>,
655}
656
657#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
658#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
659#[serde(rename_all = "camelCase")]
660pub struct AzureDaemonHeartbeatData {
661    pub status: WorkloadHeartbeatStatus,
662    pub horizon_cluster_id: String,
663    pub daemon_name: String,
664    pub horizon_status: String,
665    pub horizon_status_reason: Option<String>,
666    pub horizon_status_message: Option<String>,
667    pub capacity_group: String,
668    pub desired_machines: u32,
669    pub assigned_machines: u32,
670    pub healthy_instances: u32,
671    pub unavailable_instances: u32,
672    pub command_supported: bool,
673    pub latest_update_timestamp: String,
674    pub daemon_instances: Vec<ManagedRuntimeUnitStatus>,
675    pub events: Vec<ManagedRuntimeEventSnapshot>,
676}
677
678#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
679#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
680#[serde(rename_all = "camelCase")]
681pub struct ManagedRuntimeUnitStatus {
682    pub replica_id: String,
683    pub name: String,
684    pub machine_id: Option<String>,
685    pub node_name: Option<String>,
686    pub ip: Option<String>,
687    pub ready: bool,
688    pub phase: Option<String>,
689    pub status: Option<String>,
690    pub reason: Option<String>,
691    pub message: Option<String>,
692    pub restart_count: Option<u32>,
693    pub waiting_reason: Option<String>,
694    pub terminated_reason: Option<String>,
695    pub metrics_healthy: Option<bool>,
696    pub metrics_status: Option<String>,
697    pub metrics_last_updated: Option<String>,
698    pub cpu: Option<MetricSample>,
699    pub memory: Option<MetricSample>,
700}
701
702#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
703#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
704#[serde(rename_all = "camelCase")]
705pub struct KubernetesDaemonHeartbeatData {
706    pub status: WorkloadHeartbeatStatus,
707    pub namespace: String,
708    pub name: String,
709    pub replicas: WorkloadReplicaStatus,
710    pub restarts: Option<u32>,
711    pub command_supported: bool,
712    pub cpu: Option<MetricSample>,
713    pub memory: Option<MetricSample>,
714    pub workload: Option<KubernetesWorkloadStatus>,
715    pub pods: Vec<KubernetesPodRuntimeUnitStatus>,
716    pub events: Vec<KubernetesEventSnapshot>,
717}
718
719#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
720#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
721#[serde(rename_all = "camelCase")]
722pub struct LocalDaemonHeartbeatData {
723    pub status: WorkloadHeartbeatStatus,
724    pub daemon_name: String,
725    pub runtime_id: String,
726    pub pid: Option<u32>,
727    pub command_supported: bool,
728    pub image_path_present: bool,
729    pub restart_count: Option<u32>,
730    pub exit_reason: Option<String>,
731    pub daemon_instance: Option<LocalRuntimeUnitStatus>,
732    pub events: Vec<LocalRuntimeEventSnapshot>,
733}
734
735#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
736#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
737#[serde(rename_all = "camelCase")]
738pub struct LocalRuntimeUnitStatus {
739    pub unit_id: String,
740    pub name: String,
741    pub kind: LocalRuntimeUnitKind,
742    pub ready: bool,
743    pub phase: Option<String>,
744    pub pid: Option<u32>,
745    pub restart_count: Option<u32>,
746    pub cpu: Option<MetricSample>,
747    pub memory: Option<MetricSample>,
748}
749
750#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
751#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
752#[serde(rename_all = "kebab-case")]
753pub enum LocalRuntimeUnitKind {
754    Container,
755    Process,
756    Daemon,
757}
758
759#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
760#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
761#[serde(rename_all = "camelCase")]
762pub enum KubernetesWorkloadKind {
763    Deployment,
764    StatefulSet,
765    DaemonSet,
766    ReplicaSet,
767    Pod,
768}
769
770#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
771#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
772#[serde(rename_all = "camelCase")]
773pub struct KubernetesWorkloadStatus {
774    pub desired_replicas: Option<u32>,
775    pub ready_replicas: Option<u32>,
776    pub available_replicas: Option<u32>,
777    pub updated_replicas: Option<u32>,
778    pub observed_generation: Option<i64>,
779    pub desired_generation: Option<i64>,
780    pub rollout_reason: Option<String>,
781    pub conditions: Vec<KubernetesWorkloadCondition>,
782}
783
784#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
785#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
786#[serde(rename_all = "camelCase")]
787pub struct KubernetesWorkloadCondition {
788    #[serde(rename = "type")]
789    pub condition_type: String,
790    pub status: String,
791    pub reason: Option<String>,
792    pub message: Option<String>,
793    pub last_transition_time: Option<DateTime<Utc>>,
794}
795
796#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
797#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
798#[serde(rename_all = "camelCase")]
799pub struct KubernetesPodRuntimeUnitStatus {
800    pub name: String,
801    pub uid: Option<String>,
802    pub phase: Option<String>,
803    pub ready: bool,
804    pub restart_count: u32,
805    pub waiting_reason: Option<String>,
806    pub terminated_reason: Option<String>,
807    pub node_name: Option<String>,
808    pub pod_ip: Option<String>,
809    pub owner_references: Vec<KubernetesOwnerReference>,
810    pub cpu: Option<MetricSample>,
811    pub memory: Option<MetricSample>,
812}
813
814#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
815#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
816#[serde(rename_all = "camelCase")]
817pub struct KubernetesOwnerReference {
818    pub kind: String,
819    pub name: String,
820    pub uid: String,
821    pub controller: bool,
822}
823
824#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
825#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
826#[serde(tag = "backend", rename_all = "camelCase")]
827pub enum ComputeClusterHeartbeatData {
828    Aws(AwsComputeClusterHeartbeatData),
829    Gcp(GcpComputeClusterHeartbeatData),
830    Azure(AzureComputeClusterHeartbeatData),
831    Local(LocalComputeClusterHeartbeatData),
832}
833
834#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
835#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
836#[serde(rename_all = "camelCase")]
837pub struct ComputeClusterHeartbeatStatus {
838    pub health: ObservedHealth,
839    pub lifecycle: ProviderLifecycleState,
840    pub message: Option<String>,
841    pub stale: bool,
842    pub partial: bool,
843    pub collection_issues: Vec<HeartbeatCollectionIssue>,
844}
845
846#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
847#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
848#[serde(rename_all = "camelCase")]
849pub struct LocalComputeClusterHeartbeatData {
850    pub status: ComputeClusterHeartbeatStatus,
851    pub nodes: ObservedCounts,
852    pub name: String,
853    pub host_identifier: Option<String>,
854    pub docker_available: bool,
855    pub docker_version: Option<String>,
856    pub docker_api_version: Option<String>,
857    pub docker_os: Option<String>,
858    pub docker_arch: Option<String>,
859    pub network_name: Option<String>,
860    pub network_available: bool,
861    pub tracked_containers: Option<u32>,
862    pub running_containers: Option<u32>,
863}
864
865#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
866#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
867#[serde(rename_all = "camelCase")]
868pub struct AwsComputeClusterHeartbeatData {
869    pub status: ComputeClusterHeartbeatStatus,
870    pub nodes: ObservedCounts,
871    pub cpu: Option<MetricSample>,
872    pub memory: Option<MetricSample>,
873    pub name: String,
874    pub region: Option<String>,
875    pub backend_cluster_id: Option<String>,
876    pub capacity_groups: Vec<ComputeCapacityGroupStatus>,
877    pub provider_fleets: Vec<ProviderFleetStatus>,
878}
879
880#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
881#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
882#[serde(rename_all = "camelCase")]
883pub struct GcpComputeClusterHeartbeatData {
884    pub status: ComputeClusterHeartbeatStatus,
885    pub nodes: ObservedCounts,
886    pub cpu: Option<MetricSample>,
887    pub memory: Option<MetricSample>,
888    pub name: String,
889    pub region: Option<String>,
890    pub backend_cluster_id: Option<String>,
891    pub capacity_groups: Vec<ComputeCapacityGroupStatus>,
892    pub provider_fleets: Vec<ProviderFleetStatus>,
893}
894
895#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
896#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
897#[serde(rename_all = "camelCase")]
898pub struct AzureComputeClusterHeartbeatData {
899    pub status: ComputeClusterHeartbeatStatus,
900    pub nodes: ObservedCounts,
901    pub cpu: Option<MetricSample>,
902    pub memory: Option<MetricSample>,
903    pub name: String,
904    pub region: Option<String>,
905    pub backend_cluster_id: Option<String>,
906    pub capacity_groups: Vec<ComputeCapacityGroupStatus>,
907    pub provider_fleets: Vec<ProviderFleetStatus>,
908}
909
910#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
911#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
912#[serde(rename_all = "camelCase")]
913pub struct ComputeCapacityGroupStatus {
914    pub group_id: String,
915    pub current_machines: u32,
916    pub desired_machines: u32,
917    pub min_machines: Option<u32>,
918    pub max_machines: Option<u32>,
919    pub instance_type: Option<String>,
920    pub recommendation: Option<ComputeCapacityRecommendation>,
921    pub capacity_blocker: Option<ComputeCapacityBlocker>,
922}
923
924#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
925#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
926#[serde(rename_all = "camelCase")]
927pub enum ComputeCapacityBlockerCategory {
928    Quota,
929    Capacity,
930    Allocation,
931    Other,
932}
933
934#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
935#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
936#[serde(rename_all = "camelCase")]
937pub struct ComputeCapacityBlocker {
938    pub category: ComputeCapacityBlockerCategory,
939    pub provider_code: Option<String>,
940    pub message: String,
941    pub provider_reference: Option<String>,
942    pub observed_at: DateTime<Utc>,
943}
944
945#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
946#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
947#[serde(rename_all = "camelCase")]
948pub struct ComputeCapacityRecommendation {
949    pub desired_machines: u32,
950    pub reason: Option<String>,
951    pub utilization: Option<MetricSample>,
952    pub unschedulable_replicas: Option<u32>,
953}
954
955#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
956#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
957#[serde(rename_all = "camelCase")]
958pub struct ProviderFleetStatus {
959    pub group_id: String,
960    pub provider_id: String,
961    pub location: Option<String>,
962    pub current_machines: u32,
963    pub desired_machines: u32,
964}
965
966#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
967#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
968#[serde(rename_all = "camelCase")]
969pub struct KubernetesClusterHeartbeatData {
970    pub status: WorkloadHeartbeatStatus,
971    pub node_counts: ObservedCounts,
972    pub pod_counts: ObservedCounts,
973    pub cpu: Option<MetricSample>,
974    pub memory: Option<MetricSample>,
975    pub name: String,
976    pub region: Option<String>,
977    pub namespace: Option<String>,
978    pub version: Option<String>,
979    #[serde(default, skip_serializing_if = "Vec::is_empty")]
980    pub node_statuses: Vec<KubernetesClusterNodeStatus>,
981    pub events: Vec<KubernetesEventSnapshot>,
982}
983
984#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
985#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
986#[serde(rename_all = "camelCase")]
987pub struct KubernetesClusterNodeStatus {
988    pub name: String,
989    pub uid: Option<String>,
990    pub ready: bool,
991    #[serde(default, skip_serializing_if = "Vec::is_empty")]
992    pub conditions: Vec<KubernetesNodeConditionStatus>,
993    pub roles: Vec<String>,
994    pub labels: BTreeMap<String, String>,
995    pub allocatable: KubernetesNodeResources,
996    pub capacity: KubernetesNodeResources,
997    pub usage: Option<KubernetesNodeUsage>,
998    pub kubelet_version: Option<String>,
999    pub container_runtime_version: Option<String>,
1000}
1001
1002#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1003#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1004#[serde(rename_all = "camelCase")]
1005pub struct KubernetesNodeConditionStatus {
1006    pub type_: String,
1007    pub status: String,
1008    pub reason: Option<String>,
1009    pub message: Option<String>,
1010}
1011
1012#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
1013#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1014#[serde(rename_all = "camelCase")]
1015pub struct KubernetesNodeResources {
1016    pub cpu: Option<MetricSample>,
1017    pub memory: Option<MetricSample>,
1018    pub pods: Option<u64>,
1019}
1020
1021#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1022#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1023#[serde(rename_all = "camelCase")]
1024pub struct KubernetesNodeUsage {
1025    pub cpu: Option<MetricSample>,
1026    pub memory: Option<MetricSample>,
1027}
1028
1029#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1030#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1031#[serde(tag = "backend", rename_all = "camelCase")]
1032pub enum QueueHeartbeatData {
1033    AwsSqs(AwsSqsQueueHeartbeatData),
1034    GcpPubSub(GcpPubSubQueueHeartbeatData),
1035    AzureServiceBus(AzureServiceBusQueueHeartbeatData),
1036    Local(LocalQueueHeartbeatData),
1037}
1038
1039#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1040#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1041#[serde(rename_all = "camelCase")]
1042pub struct QueueHeartbeatStatus {
1043    pub health: ObservedHealth,
1044    pub lifecycle: ProviderLifecycleState,
1045    pub message: Option<String>,
1046    pub stale: bool,
1047    pub partial: bool,
1048    pub collection_issues: Vec<HeartbeatCollectionIssue>,
1049}
1050
1051impl Default for QueueHeartbeatStatus {
1052    fn default() -> Self {
1053        Self {
1054            health: ObservedHealth::Unknown,
1055            lifecycle: ProviderLifecycleState::Unknown,
1056            message: None,
1057            stale: false,
1058            partial: false,
1059            collection_issues: vec![],
1060        }
1061    }
1062}
1063
1064#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
1065#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1066#[serde(rename_all = "camelCase")]
1067pub struct LocalQueueHeartbeatData {
1068    pub status: QueueHeartbeatStatus,
1069    pub name: String,
1070    pub path: Option<String>,
1071    pub service_status: Option<String>,
1072}
1073
1074#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
1075#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1076#[serde(rename_all = "camelCase")]
1077pub struct GcpPubSubQueueHeartbeatData {
1078    pub status: QueueHeartbeatStatus,
1079    pub topic_name: String,
1080    pub subscription_name: Option<String>,
1081    pub project_id: Option<String>,
1082    pub topic_full_name: Option<String>,
1083    pub subscription_full_name: Option<String>,
1084    pub endpoint: Option<String>,
1085    pub topic_labels: BTreeMap<String, String>,
1086    pub subscription_labels: BTreeMap<String, String>,
1087    pub message_storage_allowed_persistence_regions: Vec<String>,
1088    pub message_storage_enforce_in_transit: Option<bool>,
1089    pub kms_key_name: Option<String>,
1090    pub schema_name: Option<String>,
1091    pub schema_encoding: Option<String>,
1092    pub schema_first_revision_id: Option<String>,
1093    pub schema_last_revision_id: Option<String>,
1094    pub topic_message_retention_duration: Option<String>,
1095    pub topic_state: Option<String>,
1096    pub subscription_ack_deadline_seconds: Option<u32>,
1097    pub subscription_message_retention_duration: Option<String>,
1098    pub subscription_retain_acked_messages: Option<bool>,
1099    pub subscription_enable_message_ordering: Option<bool>,
1100    pub subscription_filter: Option<String>,
1101    pub subscription_detached: Option<bool>,
1102    pub subscription_state: Option<String>,
1103    pub subscription_push_config_present: Option<bool>,
1104    pub subscription_push_endpoint: Option<String>,
1105    pub subscription_push_attributes: BTreeMap<String, String>,
1106    pub subscription_push_oidc_service_account_email: Option<String>,
1107    pub subscription_push_oidc_audience: Option<String>,
1108    pub subscription_push_pubsub_wrapper_write_metadata: Option<bool>,
1109    pub subscription_push_no_wrapper_write_metadata: Option<bool>,
1110    pub subscription_dead_letter_topic: Option<String>,
1111    pub subscription_dead_letter_max_delivery_attempts: Option<u32>,
1112}
1113
1114#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1115#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1116#[serde(rename_all = "camelCase")]
1117pub struct AzureServiceBusQueueHeartbeatData {
1118    pub status: QueueHeartbeatStatus,
1119    pub name: String,
1120    pub namespace_name: String,
1121    pub resource_group: Option<String>,
1122    pub resource_id: Option<String>,
1123    pub endpoint: Option<String>,
1124    pub queue_status: Option<String>,
1125    pub lock_duration: Option<String>,
1126    pub max_delivery_count: Option<u32>,
1127    pub requires_duplicate_detection: Option<bool>,
1128    pub duplicate_detection_history_time_window: Option<String>,
1129    pub requires_session: Option<bool>,
1130    pub dead_lettering_on_message_expiration: Option<bool>,
1131    pub forward_dead_lettered_messages_to: Option<String>,
1132    pub forward_to: Option<String>,
1133    pub default_message_time_to_live: Option<String>,
1134    pub auto_delete_on_idle: Option<String>,
1135    pub enable_batched_operations: Option<bool>,
1136    pub enable_express: Option<bool>,
1137    pub enable_partitioning: Option<bool>,
1138    pub max_message_size_in_kilobytes: Option<u64>,
1139    pub max_size_in_megabytes: Option<u32>,
1140    pub message_count: Option<u64>,
1141    pub active_message_count: Option<u64>,
1142    pub dead_letter_message_count: Option<u64>,
1143    pub scheduled_message_count: Option<u64>,
1144    pub transfer_message_count: Option<u64>,
1145    pub transfer_dead_letter_message_count: Option<u64>,
1146    pub size_in_bytes: Option<u64>,
1147    pub accessed_at: Option<String>,
1148    pub created_at: Option<String>,
1149    pub updated_at: Option<String>,
1150}
1151
1152#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1153#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1154#[serde(rename_all = "camelCase")]
1155pub struct AwsSqsQueueHeartbeatData {
1156    pub status: QueueHeartbeatStatus,
1157    pub name: String,
1158    pub region: Option<String>,
1159    pub queue_url: Option<String>,
1160    pub queue_arn: Option<String>,
1161    pub visibility_timeout_seconds: Option<u32>,
1162    pub message_retention_period_seconds: Option<u32>,
1163    pub delay_seconds: Option<u32>,
1164    pub receive_message_wait_time_seconds: Option<u32>,
1165    pub maximum_message_size: Option<u32>,
1166    pub redrive_policy: Option<String>,
1167    pub redrive_allow_policy: Option<String>,
1168    pub fifo_queue: Option<bool>,
1169    pub content_based_deduplication: Option<bool>,
1170    pub deduplication_scope: Option<String>,
1171    pub fifo_throughput_limit: Option<String>,
1172    pub sse_enabled: Option<bool>,
1173    pub kms_master_key_id: Option<String>,
1174    pub kms_data_key_reuse_period_seconds: Option<u32>,
1175    pub sqs_managed_sse_enabled: Option<bool>,
1176    pub approximate_visible_messages: Option<u64>,
1177    pub approximate_in_flight_messages: Option<u64>,
1178    pub approximate_delayed_messages: Option<u64>,
1179    pub approximate_counts: bool,
1180}
1181
1182#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1183#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1184#[serde(tag = "backend", rename_all = "camelCase")]
1185pub enum KvHeartbeatData {
1186    AwsDynamoDb(AwsDynamoDbKvHeartbeatData),
1187    GcpFirestore(GcpFirestoreKvHeartbeatData),
1188    AzureTable(AzureTableKvHeartbeatData),
1189    Local(LocalKvHeartbeatData),
1190}
1191
1192#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1193#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1194#[serde(rename_all = "camelCase")]
1195pub struct KvHeartbeatStatus {
1196    pub health: ObservedHealth,
1197    pub lifecycle: ProviderLifecycleState,
1198    pub message: Option<String>,
1199    pub stale: bool,
1200    pub partial: bool,
1201    pub collection_issues: Vec<HeartbeatCollectionIssue>,
1202}
1203
1204impl Default for KvHeartbeatStatus {
1205    fn default() -> Self {
1206        Self {
1207            health: ObservedHealth::Unknown,
1208            lifecycle: ProviderLifecycleState::Unknown,
1209            message: None,
1210            stale: false,
1211            partial: false,
1212            collection_issues: vec![],
1213        }
1214    }
1215}
1216
1217#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1218#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1219#[serde(rename_all = "camelCase")]
1220pub struct AwsDynamoDbKvHeartbeatData {
1221    pub status: KvHeartbeatStatus,
1222    pub name: String,
1223    pub region: Option<String>,
1224    pub table_arn: Option<String>,
1225    pub table_status: Option<String>,
1226    pub billing_mode: Option<String>,
1227    pub key_schema: Vec<AwsDynamoDbKeySchemaElement>,
1228    pub global_secondary_index_count: Option<u32>,
1229    pub local_secondary_index_count: Option<u32>,
1230    pub item_count: Option<u64>,
1231    pub table_size_bytes: Option<u64>,
1232    pub stream_enabled: Option<bool>,
1233    pub stream_view_type: Option<String>,
1234    pub ttl_status: Option<String>,
1235    pub ttl_attribute_name: Option<String>,
1236    pub deletion_protection_enabled: Option<bool>,
1237    pub sse_status: Option<String>,
1238    pub sse_type: Option<String>,
1239    pub table_class: Option<String>,
1240    pub replica_count: Option<u32>,
1241    pub restore_in_progress: Option<bool>,
1242}
1243
1244#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1245#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1246#[serde(rename_all = "camelCase")]
1247pub struct AwsDynamoDbKeySchemaElement {
1248    pub attribute_name: String,
1249    pub key_type: String,
1250}
1251
1252#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1253#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1254#[serde(rename_all = "camelCase")]
1255pub struct GcpFirestoreKvHeartbeatData {
1256    pub status: KvHeartbeatStatus,
1257    pub database_name: String,
1258    pub project_id: Option<String>,
1259    pub endpoint: Option<String>,
1260    pub location_id: Option<String>,
1261    pub database_type: Option<String>,
1262    pub concurrency_mode: Option<String>,
1263    pub app_engine_integration_mode: Option<String>,
1264    pub delete_protection_state: Option<String>,
1265    pub point_in_time_recovery_enablement: Option<String>,
1266    pub version_retention_period: Option<String>,
1267    pub earliest_version_time: Option<String>,
1268    pub create_time: Option<String>,
1269    pub update_time: Option<String>,
1270    pub delete_time: Option<String>,
1271    pub database_edition: Option<String>,
1272    pub cmek_enabled: bool,
1273    pub source_info_present: bool,
1274}
1275
1276#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1277#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1278#[serde(rename_all = "camelCase")]
1279pub struct AzureTableKvHeartbeatData {
1280    pub status: KvHeartbeatStatus,
1281    pub table_name: String,
1282    pub storage_account_name: String,
1283    pub resource_group: Option<String>,
1284    pub endpoint: Option<String>,
1285    pub storage_account_resource_id: Option<String>,
1286    pub storage_account_location: Option<String>,
1287    pub storage_account_kind: Option<String>,
1288    pub storage_account_provisioning_state: Option<String>,
1289    pub storage_account_primary_status: Option<String>,
1290    pub table_exists: bool,
1291    pub signed_identifier_count: Option<u32>,
1292}
1293
1294#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1295#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1296#[serde(rename_all = "camelCase")]
1297pub struct LocalKvHeartbeatData {
1298    pub status: KvHeartbeatStatus,
1299    pub name: String,
1300    pub path: String,
1301    pub path_exists: bool,
1302    pub is_directory: Option<bool>,
1303    pub cloud_metadata_supported: bool,
1304}
1305
1306#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1307#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1308#[serde(tag = "backend", rename_all = "camelCase")]
1309pub enum VaultHeartbeatData {
1310    AwsParameterStore(AwsParameterStoreVaultHeartbeatData),
1311    GcpSecretManager(GcpSecretManagerVaultHeartbeatData),
1312    AzureKeyVault(AzureKeyVaultHeartbeatData),
1313    KubernetesSecret(KubernetesSecretVaultHeartbeatData),
1314    Local(LocalVaultHeartbeatData),
1315}
1316
1317#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1318#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1319#[serde(rename_all = "camelCase")]
1320pub struct VaultHeartbeatStatus {
1321    pub health: ObservedHealth,
1322    pub lifecycle: ProviderLifecycleState,
1323    pub message: Option<String>,
1324    pub stale: bool,
1325    pub partial: bool,
1326    pub collection_issues: Vec<HeartbeatCollectionIssue>,
1327}
1328
1329impl Default for VaultHeartbeatStatus {
1330    fn default() -> Self {
1331        Self {
1332            health: ObservedHealth::Healthy,
1333            lifecycle: ProviderLifecycleState::Running,
1334            message: None,
1335            stale: false,
1336            partial: false,
1337            collection_issues: vec![],
1338        }
1339    }
1340}
1341
1342#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1343#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1344#[serde(rename_all = "camelCase")]
1345pub struct AwsParameterStoreVaultHeartbeatData {
1346    pub status: VaultHeartbeatStatus,
1347    pub account_id: String,
1348    pub region: String,
1349    pub prefix: String,
1350    pub parameter_metadata_sampled: bool,
1351    pub sampled_parameter_count: Option<u32>,
1352    pub sampled_secure_string_count: Option<u32>,
1353    pub sampled_string_count: Option<u32>,
1354    pub sampled_string_list_count: Option<u32>,
1355    pub sampled_advanced_tier_count: Option<u32>,
1356    pub sampled_kms_key_metadata_present_count: Option<u32>,
1357    pub latest_modified_at: Option<DateTime<Utc>>,
1358    pub has_more_parameters: Option<bool>,
1359}
1360
1361#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1362#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1363#[serde(rename_all = "camelCase")]
1364pub struct GcpSecretManagerVaultHeartbeatData {
1365    pub status: VaultHeartbeatStatus,
1366    pub project_id: String,
1367    pub location: String,
1368    pub prefix: String,
1369    pub secret_metadata_listed: bool,
1370}
1371
1372#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1373#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1374#[serde(rename_all = "camelCase")]
1375pub struct AzureKeyVaultHeartbeatData {
1376    pub status: VaultHeartbeatStatus,
1377    pub name: String,
1378    pub resource_group: Option<String>,
1379    pub resource_id: Option<String>,
1380    pub location: Option<String>,
1381    pub vault_uri: Option<String>,
1382    pub provisioning_state: Option<String>,
1383    pub sku_family: Option<String>,
1384    pub sku_name: Option<String>,
1385    pub soft_delete_enabled: bool,
1386    pub soft_delete_retention_days: i32,
1387    pub purge_protection_enabled: Option<bool>,
1388    pub rbac_authorization_enabled: bool,
1389    pub public_network_access: String,
1390    pub access_policy_count: u32,
1391    pub private_endpoint_connection_count: u32,
1392    pub secret_metadata_listed: bool,
1393}
1394
1395#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1396#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1397#[serde(rename_all = "camelCase")]
1398pub struct KubernetesSecretVaultHeartbeatData {
1399    pub status: VaultHeartbeatStatus,
1400    pub namespace: String,
1401    pub prefix: String,
1402    pub secret_metadata_listed: bool,
1403}
1404
1405#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1406#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1407#[serde(rename_all = "camelCase")]
1408pub struct LocalVaultHeartbeatData {
1409    pub status: VaultHeartbeatStatus,
1410    pub path: String,
1411    pub path_exists: bool,
1412    pub is_directory: Option<bool>,
1413    pub readonly: Option<bool>,
1414    pub modified_at: Option<DateTime<Utc>>,
1415    pub secret_metadata_listed: bool,
1416}
1417
1418#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1419#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1420#[serde(tag = "backend", rename_all = "camelCase")]
1421pub enum ServiceAccountHeartbeatData {
1422    AwsIamRole(AwsIamRoleServiceAccountHeartbeatData),
1423    GcpServiceAccount(GcpServiceAccountHeartbeatData),
1424    AzureManagedIdentity(AzureManagedIdentityServiceAccountHeartbeatData),
1425    Local(LocalServiceAccountHeartbeatData),
1426}
1427
1428#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1429#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1430#[serde(rename_all = "camelCase")]
1431pub struct ServiceAccountHeartbeatStatus {
1432    pub health: ObservedHealth,
1433    pub lifecycle: ProviderLifecycleState,
1434    pub message: Option<String>,
1435    pub stale: bool,
1436    pub partial: bool,
1437    pub collection_issues: Vec<HeartbeatCollectionIssue>,
1438}
1439
1440#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1441#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1442#[serde(rename_all = "camelCase")]
1443pub struct AwsIamRoleServiceAccountHeartbeatData {
1444    pub status: ServiceAccountHeartbeatStatus,
1445    pub role_name: String,
1446    pub role_arn: String,
1447    pub role_id: String,
1448    pub path: String,
1449    pub create_date: String,
1450    pub description: Option<String>,
1451    pub max_session_duration: Option<i32>,
1452    pub assume_role_policy_present: bool,
1453    pub permissions_boundary_type: Option<String>,
1454    pub permissions_boundary_arn: Option<String>,
1455    pub tag_count: u32,
1456    pub managed_tag_count: u32,
1457    pub attached_policy_count: u32,
1458    pub attached_policy_names: Vec<String>,
1459    pub inline_policy_count: u32,
1460    pub inline_policy_names: Vec<String>,
1461    pub stack_permissions_applied: bool,
1462    pub last_used_date: Option<String>,
1463    pub last_used_region: Option<String>,
1464}
1465
1466#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1467#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1468#[serde(rename_all = "camelCase")]
1469pub struct GcpServiceAccountHeartbeatData {
1470    pub status: ServiceAccountHeartbeatStatus,
1471    pub name: Option<String>,
1472    pub project_id: Option<String>,
1473    pub unique_id: Option<String>,
1474    pub email: String,
1475    pub display_name: Option<String>,
1476    pub description: Option<String>,
1477    pub oauth2_client_id: Option<String>,
1478    pub disabled: Option<bool>,
1479    pub etag: Option<String>,
1480    pub project_binding_count: u32,
1481    pub project_roles: Vec<String>,
1482    pub service_account_binding_count: u32,
1483    pub service_account_roles: Vec<String>,
1484}
1485
1486#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1487#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1488#[serde(rename_all = "camelCase")]
1489pub struct AzureManagedIdentityServiceAccountHeartbeatData {
1490    pub status: ServiceAccountHeartbeatStatus,
1491    pub name: String,
1492    pub resource_id: String,
1493    pub resource_group: String,
1494    pub location: String,
1495    pub type_: Option<String>,
1496    pub client_id: Option<String>,
1497    pub principal_id: Option<String>,
1498    pub tenant_id: Option<String>,
1499    pub isolation_scope: Option<String>,
1500    pub managed_tag_count: u32,
1501    pub role_assignment_count: u32,
1502    pub role_assignment_ids: Vec<String>,
1503    pub custom_role_definition_count: u32,
1504    pub custom_role_definition_ids: Vec<String>,
1505    pub stack_permissions_applied: bool,
1506}
1507
1508#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1509#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1510#[serde(rename_all = "camelCase")]
1511pub struct LocalServiceAccountHeartbeatData {
1512    pub status: ServiceAccountHeartbeatStatus,
1513    pub identity: String,
1514    pub configured: bool,
1515}
1516
1517#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1518#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1519#[serde(tag = "backend", rename_all = "camelCase")]
1520pub enum NetworkHeartbeatData {
1521    AwsVpc(AwsVpcNetworkHeartbeatData),
1522    GcpVpc(GcpVpcNetworkHeartbeatData),
1523    AzureVnet(AzureVnetNetworkHeartbeatData),
1524}
1525
1526#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1527#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1528#[serde(rename_all = "camelCase")]
1529pub struct NetworkHeartbeatStatus {
1530    pub health: ObservedHealth,
1531    pub lifecycle: ProviderLifecycleState,
1532    pub message: Option<String>,
1533    pub stale: bool,
1534    pub partial: bool,
1535    pub collection_issues: Vec<HeartbeatCollectionIssue>,
1536}
1537
1538#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1539#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1540#[serde(rename_all = "camelCase")]
1541pub struct AwsVpcNetworkHeartbeatData {
1542    pub status: NetworkHeartbeatStatus,
1543    pub vpc_id: Option<String>,
1544    pub vpc_state: Option<String>,
1545    pub cidr_block: Option<String>,
1546    pub public_subnet_ids: Vec<String>,
1547    pub private_subnet_ids: Vec<String>,
1548    pub availability_zones: Vec<String>,
1549    pub internet_gateway_id: Option<String>,
1550    pub nat_gateway_id: Option<String>,
1551    pub route_table_count: u32,
1552    pub security_group_id: Option<String>,
1553    pub is_byo_vpc: bool,
1554}
1555
1556#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1557#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1558#[serde(rename_all = "camelCase")]
1559pub struct GcpVpcNetworkHeartbeatData {
1560    pub status: NetworkHeartbeatStatus,
1561    pub network_name: Option<String>,
1562    pub network_self_link: Option<String>,
1563    pub subnetwork_name: Option<String>,
1564    pub subnetwork_self_link: Option<String>,
1565    pub region: Option<String>,
1566    pub cidr_block: Option<String>,
1567    pub router_name: Option<String>,
1568    pub cloud_nat_name: Option<String>,
1569    pub firewall_name: Option<String>,
1570    pub is_byo_vpc: bool,
1571}
1572
1573#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1574#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1575#[serde(rename_all = "camelCase")]
1576pub struct AzureVnetNetworkHeartbeatData {
1577    pub status: NetworkHeartbeatStatus,
1578    pub vnet_name: Option<String>,
1579    pub vnet_resource_id: Option<String>,
1580    pub resource_group: Option<String>,
1581    pub location: Option<String>,
1582    pub cidr_block: Option<String>,
1583    pub public_subnet_name: Option<String>,
1584    pub private_subnet_name: Option<String>,
1585    pub application_gateway_subnet_name: Option<String>,
1586    pub nat_gateway_id: Option<String>,
1587    pub public_ip_id: Option<String>,
1588    pub nsg_id: Option<String>,
1589    pub is_byo_vnet: bool,
1590    pub last_byo_vnet_verification_error_code: Option<String>,
1591}
1592
1593#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1594#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1595#[serde(tag = "backend", rename_all = "camelCase")]
1596pub enum RemoteStackManagementHeartbeatData {
1597    AwsIamRole(AwsRemoteStackManagementHeartbeatData),
1598    GcpServiceAccount(GcpRemoteStackManagementHeartbeatData),
1599    AzureManagedIdentity(AzureRemoteStackManagementHeartbeatData),
1600}
1601
1602#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1603#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1604#[serde(rename_all = "camelCase")]
1605pub struct RemoteStackManagementHeartbeatStatus {
1606    pub health: ObservedHealth,
1607    pub lifecycle: ProviderLifecycleState,
1608    pub message: Option<String>,
1609    pub stale: bool,
1610    pub partial: bool,
1611    pub collection_issues: Vec<HeartbeatCollectionIssue>,
1612}
1613
1614#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1615#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1616#[serde(rename_all = "camelCase")]
1617pub struct AwsRemoteStackManagementHeartbeatData {
1618    pub status: RemoteStackManagementHeartbeatStatus,
1619    pub role_name: Option<String>,
1620    pub role_arn: Option<String>,
1621    pub management_permissions_applied: bool,
1622}
1623
1624#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1625#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1626#[serde(rename_all = "camelCase")]
1627pub struct GcpRemoteStackManagementHeartbeatData {
1628    pub status: RemoteStackManagementHeartbeatStatus,
1629    pub service_account_email: Option<String>,
1630    pub service_account_unique_id: Option<String>,
1631    pub role_bound: bool,
1632    pub impersonation_granted: bool,
1633}
1634
1635#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1636#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1637#[serde(rename_all = "camelCase")]
1638pub struct AzureRemoteStackManagementHeartbeatData {
1639    pub status: RemoteStackManagementHeartbeatStatus,
1640    pub uami_resource_id: Option<String>,
1641    pub uami_client_id: Option<String>,
1642    pub uami_principal_id: Option<String>,
1643    pub tenant_id: Option<String>,
1644    pub fic_name: Option<String>,
1645    pub role_definition_id: Option<String>,
1646    pub role_assignment_ids: Vec<String>,
1647}
1648
1649#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1650#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1651#[serde(tag = "backend", rename_all = "camelCase")]
1652pub enum ArtifactRegistryHeartbeatData {
1653    AwsEcr(AwsEcrArtifactRegistryHeartbeatData),
1654    GcpArtifactRegistry(GcpArtifactRegistryHeartbeatData),
1655    AzureContainerRegistry(AzureContainerRegistryHeartbeatData),
1656    Local(LocalArtifactRegistryHeartbeatData),
1657}
1658
1659#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1660#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1661#[serde(rename_all = "camelCase")]
1662pub struct ArtifactRegistryHeartbeatStatus {
1663    pub health: ObservedHealth,
1664    pub lifecycle: ProviderLifecycleState,
1665    pub message: Option<String>,
1666    pub stale: bool,
1667    pub partial: bool,
1668    pub collection_issues: Vec<HeartbeatCollectionIssue>,
1669}
1670
1671#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1672#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1673#[serde(rename_all = "camelCase")]
1674pub struct AwsEcrArtifactRegistryHeartbeatData {
1675    pub status: ArtifactRegistryHeartbeatStatus,
1676    pub registry_id: String,
1677    pub region: String,
1678    pub registry_uri: String,
1679    pub repository_prefix: String,
1680    pub pull_role_arn: Option<String>,
1681    pub push_role_arn: Option<String>,
1682    pub repository_count: u32,
1683    pub repositories_truncated: bool,
1684    pub repositories: Vec<AwsEcrRepositoryHeartbeatData>,
1685}
1686
1687#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1688#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1689#[serde(rename_all = "camelCase")]
1690pub struct AwsEcrRepositoryHeartbeatData {
1691    pub repository_arn: String,
1692    pub registry_id: String,
1693    pub repository_name: String,
1694    pub repository_uri: String,
1695    pub created_at: f64,
1696    pub image_tag_mutability: Option<String>,
1697    pub scan_on_push: Option<bool>,
1698    pub encryption_type: Option<String>,
1699    pub kms_key_present: bool,
1700}
1701
1702#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1703#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1704#[serde(rename_all = "camelCase")]
1705pub struct GcpArtifactRegistryHeartbeatData {
1706    pub status: ArtifactRegistryHeartbeatStatus,
1707    pub project_id: String,
1708    pub location: String,
1709    pub repository_id: String,
1710    pub name: Option<String>,
1711    pub format: Option<String>,
1712    pub mode: Option<String>,
1713    pub description: Option<String>,
1714    pub label_count: u32,
1715    pub cleanup_policy_count: u32,
1716    pub cleanup_policy_dry_run: Option<bool>,
1717    pub kms_key_name_present: bool,
1718    pub size_bytes: Option<String>,
1719    pub satisfies_pzs: Option<bool>,
1720    pub create_time: Option<String>,
1721    pub update_time: Option<String>,
1722    pub iam_policy_etag_present: bool,
1723    pub iam_binding_count: u32,
1724    pub iam_roles: Vec<String>,
1725    pub pull_service_account_email: Option<String>,
1726    pub push_service_account_email: Option<String>,
1727}
1728
1729#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1730#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1731#[serde(rename_all = "camelCase")]
1732pub struct AzureContainerRegistryHeartbeatData {
1733    pub status: ArtifactRegistryHeartbeatStatus,
1734    pub name: String,
1735    pub resource_id: Option<String>,
1736    pub resource_group: String,
1737    pub location: String,
1738    pub type_: Option<String>,
1739    pub login_server: Option<String>,
1740    pub sku_name: String,
1741    pub sku_tier: Option<String>,
1742    pub provisioning_state: Option<String>,
1743    pub admin_user_enabled: bool,
1744    pub anonymous_pull_enabled: bool,
1745    pub public_network_access: String,
1746    pub network_rule_bypass_options: String,
1747    pub network_rule_default_action: Option<String>,
1748    pub ip_rule_count: u32,
1749    pub encryption_status: Option<String>,
1750    pub encryption_key_vault_uri_present: bool,
1751    pub encryption_key_identifier_present: bool,
1752    pub policies_present: bool,
1753    pub policy_count: u32,
1754    pub private_endpoint_connection_count: u32,
1755    pub data_endpoint_enabled: Option<bool>,
1756    pub data_endpoint_host_names: Vec<String>,
1757    pub zone_redundancy: String,
1758    pub creation_date: Option<String>,
1759    pub managed_tag_count: u32,
1760}
1761
1762#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1763#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1764#[serde(rename_all = "camelCase")]
1765pub struct LocalArtifactRegistryHeartbeatData {
1766    pub status: ArtifactRegistryHeartbeatStatus,
1767    pub registry_url: String,
1768    pub reachable: bool,
1769}
1770
1771#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1772#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1773#[serde(tag = "backend", rename_all = "camelCase")]
1774pub enum BuildHeartbeatData {
1775    AwsCodeBuild(AwsCodeBuildHeartbeatData),
1776    GcpCloudBuild(GcpCloudBuildHeartbeatData),
1777    AzureContainerApps(AzureContainerAppsBuildHeartbeatData),
1778    KubernetesJob(KubernetesBuildHeartbeatData),
1779}
1780
1781#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1782#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1783#[serde(rename_all = "camelCase")]
1784pub struct BuildHeartbeatStatus {
1785    pub health: ObservedHealth,
1786    pub lifecycle: ProviderLifecycleState,
1787    pub message: Option<String>,
1788    pub stale: bool,
1789    pub partial: bool,
1790    pub collection_issues: Vec<HeartbeatCollectionIssue>,
1791}
1792
1793#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1794#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1795#[serde(rename_all = "camelCase")]
1796pub struct AwsCodeBuildHeartbeatData {
1797    pub status: BuildHeartbeatStatus,
1798    pub project_name: String,
1799    pub project_arn: Option<String>,
1800    pub description: Option<String>,
1801    pub source_type: Option<String>,
1802    pub artifacts_type: Option<String>,
1803    pub artifacts_encryption_disabled: Option<bool>,
1804    pub environment_type: Option<String>,
1805    pub environment_image: Option<String>,
1806    pub compute_type: Option<String>,
1807    pub image_pull_credentials_type: Option<String>,
1808    pub privileged_mode: Option<bool>,
1809    pub environment_variable_count: u32,
1810    pub service_role_present: bool,
1811    pub encryption_key_present: bool,
1812    pub cloud_watch_logs_status: Option<String>,
1813    pub s3_logs_status: Option<String>,
1814    pub timeout_in_minutes: Option<i32>,
1815    pub queued_timeout_in_minutes: Option<i32>,
1816    pub created: Option<f64>,
1817    pub last_modified: Option<f64>,
1818}
1819
1820#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1821#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1822#[serde(rename_all = "camelCase")]
1823pub struct GcpCloudBuildHeartbeatData {
1824    pub status: BuildHeartbeatStatus,
1825    pub project_id: String,
1826    pub location: String,
1827    pub build_config_id: String,
1828    pub service_account: Option<String>,
1829    pub environment_variable_count: u32,
1830}
1831
1832#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1833#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1834#[serde(rename_all = "camelCase")]
1835pub struct AzureContainerAppsBuildHeartbeatData {
1836    pub status: BuildHeartbeatStatus,
1837    pub managed_environment_id: String,
1838    pub resource_group_name: String,
1839    pub managed_identity_id: Option<String>,
1840    pub resource_prefix: Option<String>,
1841    pub environment_variable_count: u32,
1842}
1843
1844#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1845#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1846#[serde(rename_all = "camelCase")]
1847pub struct KubernetesBuildHeartbeatData {
1848    pub status: BuildHeartbeatStatus,
1849    pub job_name: String,
1850    pub namespace: String,
1851    pub active: Option<i32>,
1852    pub succeeded: Option<i32>,
1853    pub failed: Option<i32>,
1854    pub start_time: Option<DateTime<Utc>>,
1855    pub completion_time: Option<DateTime<Utc>>,
1856    pub condition_count: u32,
1857    pub image_digest: Option<String>,
1858    pub events: Vec<KubernetesEventSnapshot>,
1859}
1860
1861#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1862#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1863#[serde(tag = "backend", rename_all = "camelCase")]
1864pub enum ServiceActivationHeartbeatData {
1865    GcpServiceUsage(GcpServiceUsageActivationHeartbeatData),
1866    AzureResourceProvider(AzureResourceProviderActivationHeartbeatData),
1867}
1868
1869#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1870#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1871#[serde(rename_all = "camelCase")]
1872pub struct ServiceActivationHeartbeatStatus {
1873    pub health: ObservedHealth,
1874    pub lifecycle: ProviderLifecycleState,
1875    pub message: Option<String>,
1876    pub stale: bool,
1877    pub partial: bool,
1878    pub collection_issues: Vec<HeartbeatCollectionIssue>,
1879}
1880
1881impl Default for ServiceActivationHeartbeatStatus {
1882    fn default() -> Self {
1883        Self {
1884            health: ObservedHealth::Healthy,
1885            lifecycle: ProviderLifecycleState::Running,
1886            message: None,
1887            stale: false,
1888            partial: false,
1889            collection_issues: vec![],
1890        }
1891    }
1892}
1893
1894#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1895#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1896#[serde(rename_all = "camelCase")]
1897pub struct GcpServiceUsageActivationHeartbeatData {
1898    pub status: ServiceActivationHeartbeatStatus,
1899    pub project_id: String,
1900    pub service_name: String,
1901    pub service_resource_name: Option<String>,
1902    pub title: Option<String>,
1903    pub state: Option<String>,
1904    pub enabled: bool,
1905    pub last_operation_name: Option<String>,
1906}
1907
1908#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1909#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1910#[serde(rename_all = "camelCase")]
1911pub struct AzureResourceProviderActivationHeartbeatData {
1912    pub status: ServiceActivationHeartbeatStatus,
1913    pub namespace: String,
1914    pub provider_id: Option<String>,
1915    pub registration_state: Option<String>,
1916    pub registration_policy: Option<String>,
1917    pub resource_type_count: u32,
1918    pub registered: bool,
1919}
1920
1921#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1922#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1923#[serde(rename_all = "camelCase")]
1924pub struct AzureResourceGroupHeartbeatData {
1925    pub status: AzureResourceGroupHeartbeatStatus,
1926    pub name: String,
1927    pub resource_id: Option<String>,
1928    pub location: Option<String>,
1929    pub provisioning_state: Option<String>,
1930    pub managed_tags: BTreeMap<String, String>,
1931}
1932
1933#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1934#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1935#[serde(rename_all = "camelCase")]
1936pub struct AzureResourceGroupHeartbeatStatus {
1937    pub health: ObservedHealth,
1938    pub lifecycle: ProviderLifecycleState,
1939    pub message: Option<String>,
1940    pub stale: bool,
1941    pub partial: bool,
1942    pub collection_issues: Vec<HeartbeatCollectionIssue>,
1943}
1944
1945impl Default for AzureResourceGroupHeartbeatStatus {
1946    fn default() -> Self {
1947        Self {
1948            health: ObservedHealth::Healthy,
1949            lifecycle: ProviderLifecycleState::Running,
1950            message: None,
1951            stale: false,
1952            partial: false,
1953            collection_issues: vec![],
1954        }
1955    }
1956}
1957
1958#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1959#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1960#[serde(rename_all = "camelCase")]
1961pub struct AzureStorageAccountHeartbeatData {
1962    pub status: StorageHeartbeatStatus,
1963    pub name: String,
1964    pub resource_id: Option<String>,
1965    pub resource_group: Option<String>,
1966    pub location: Option<String>,
1967    pub kind: Option<String>,
1968    pub sku_name: Option<String>,
1969    pub sku_tier: Option<String>,
1970    pub provisioning_state: Option<String>,
1971    pub primary_endpoints: AzureStorageAccountEndpoints,
1972    pub secondary_endpoints: AzureStorageAccountEndpoints,
1973    pub public_network_access: Option<String>,
1974    pub allow_blob_public_access: Option<bool>,
1975    pub allow_shared_key_access: Option<bool>,
1976    pub minimum_tls_version: Option<String>,
1977    pub supports_https_traffic_only: Option<bool>,
1978    pub encryption_key_source: Option<String>,
1979    pub require_infrastructure_encryption: Option<bool>,
1980    pub network_default_action: Option<String>,
1981    pub network_bypass: Option<String>,
1982    pub network_ip_rule_count: Option<u32>,
1983    pub network_virtual_network_rule_count: Option<u32>,
1984    pub network_resource_access_rule_count: Option<u32>,
1985}
1986
1987#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
1988#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1989#[serde(rename_all = "camelCase")]
1990pub struct AzureStorageAccountEndpoints {
1991    pub blob: Option<String>,
1992    pub dfs: Option<String>,
1993    pub file: Option<String>,
1994    pub queue: Option<String>,
1995    pub table: Option<String>,
1996    pub web: Option<String>,
1997}
1998
1999#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2000#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
2001#[serde(rename_all = "camelCase")]
2002pub struct AzureContainerAppsEnvironmentHeartbeatData {
2003    pub status: AzureContainerAppsEnvironmentHeartbeatStatus,
2004    pub name: String,
2005    pub resource_id: Option<String>,
2006    pub resource_group: Option<String>,
2007    pub location: Option<String>,
2008    pub kind: Option<String>,
2009    pub provisioning_state: Option<String>,
2010    pub default_domain: Option<String>,
2011    pub static_ip: Option<String>,
2012    pub custom_domain_verification_id: Option<String>,
2013    pub infrastructure_resource_group: Option<String>,
2014    pub event_stream_endpoint: Option<String>,
2015    pub zone_redundant: Option<bool>,
2016    pub workload_profile_count: u32,
2017    pub workload_profiles: Vec<AzureContainerAppsEnvironmentWorkloadProfile>,
2018}
2019
2020#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2021#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
2022#[serde(rename_all = "camelCase")]
2023pub struct AzureContainerAppsEnvironmentHeartbeatStatus {
2024    pub health: ObservedHealth,
2025    pub lifecycle: ProviderLifecycleState,
2026    pub message: Option<String>,
2027    pub stale: bool,
2028    pub partial: bool,
2029    pub collection_issues: Vec<HeartbeatCollectionIssue>,
2030}
2031
2032#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
2033#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
2034#[serde(rename_all = "camelCase")]
2035pub struct AzureContainerAppsEnvironmentWorkloadProfile {
2036    pub name: String,
2037    pub workload_profile_type: String,
2038    pub minimum_count: Option<i32>,
2039    pub maximum_count: Option<i32>,
2040}
2041
2042#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2043#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
2044#[serde(rename_all = "camelCase")]
2045pub struct AzureServiceBusNamespaceHeartbeatData {
2046    pub status: QueueHeartbeatStatus,
2047    pub name: String,
2048    pub resource_id: Option<String>,
2049    pub resource_group: Option<String>,
2050    pub location: Option<String>,
2051    pub sku_name: Option<String>,
2052    pub sku_tier: Option<String>,
2053    pub sku_capacity: Option<i32>,
2054    pub namespace_status: Option<String>,
2055    pub provisioning_state: Option<String>,
2056    pub service_bus_endpoint: Option<String>,
2057    pub metric_id: Option<String>,
2058    pub public_network_access: Option<String>,
2059    pub disable_local_auth: Option<bool>,
2060    pub minimum_tls_version: Option<String>,
2061    pub premium_messaging_partitions: Option<i32>,
2062    pub private_endpoint_connection_count: u32,
2063    pub zone_redundant: Option<bool>,
2064    pub created_at: Option<String>,
2065    pub updated_at: Option<String>,
2066}
2067
2068#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
2069#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
2070#[serde(rename_all = "camelCase")]
2071pub struct NamedResourceDetail {
2072    pub name: String,
2073    pub region: Option<String>,
2074}
2075
2076#[cfg(test)]
2077mod tests {
2078    use super::*;
2079    use chrono::TimeZone as _;
2080    use serde_json::json;
2081
2082    fn observed_at() -> DateTime<Utc> {
2083        Utc.with_ymd_and_hms(2026, 5, 28, 10, 30, 0).unwrap()
2084    }
2085
2086    fn workload_status() -> WorkloadHeartbeatStatus {
2087        WorkloadHeartbeatStatus {
2088            health: ObservedHealth::Healthy,
2089            lifecycle: ProviderLifecycleState::Running,
2090            message: None,
2091            stale: false,
2092            partial: false,
2093            collection_issues: vec![],
2094        }
2095    }
2096
2097    fn workload_replicas() -> WorkloadReplicaStatus {
2098        WorkloadReplicaStatus {
2099            desired: Some(2),
2100            current: Some(2),
2101            ready: Some(1),
2102            available: Some(1),
2103            updated: Some(2),
2104            misscheduled: None,
2105        }
2106    }
2107
2108    fn heartbeat(data: ResourceHeartbeatData, resource_type: &str) -> ResourceHeartbeat {
2109        ResourceHeartbeat {
2110            deployment_id: Some("dep_123".to_string()),
2111            resource_id: "api".to_string(),
2112            resource_type: ResourceType::from(resource_type),
2113            controller_platform: Platform::Kubernetes,
2114            backend: HeartbeatBackend::Kubernetes,
2115            observed_at: observed_at(),
2116            data,
2117            raw: vec![RawHeartbeatSnippet {
2118                source: "kubernetes/apps/v1/deployments/api".to_string(),
2119                format: RawHeartbeatSnippetFormat::Json,
2120                collected_at: observed_at(),
2121                body: r#"{"readyReplicas":1}"#.to_string(),
2122                truncated: false,
2123            }],
2124        }
2125    }
2126
2127    #[test]
2128    fn container_heartbeat_serializes_resource_first_data() {
2129        let heartbeat = heartbeat(
2130            ResourceHeartbeatData::Container(ContainerHeartbeatData::Kubernetes(
2131                KubernetesContainerHeartbeatData {
2132                    status: workload_status(),
2133                    namespace: "default".to_string(),
2134                    name: "api".to_string(),
2135                    workload_kind: KubernetesWorkloadKind::Deployment,
2136                    replicas: workload_replicas(),
2137                    restarts: Some(1),
2138                    cpu: Some(MetricSample {
2139                        value: 0.5,
2140                        unit: MetricUnit::Cores,
2141                    }),
2142                    memory: None,
2143                    workload: None,
2144                    pods: vec![],
2145                    events: vec![],
2146                },
2147            )),
2148            "container",
2149        );
2150
2151        let value = serde_json::to_value(&heartbeat).unwrap();
2152
2153        assert_eq!(value["resourceType"], "container");
2154        assert_eq!(value["data"]["resourceType"], "container");
2155        assert_eq!(value["data"]["data"]["backend"], "kubernetes");
2156        assert_eq!(value["raw"][0]["body"], r#"{"readyReplicas":1}"#);
2157        assert!(value.get("collection").is_none());
2158        assert!(value["data"]["data"].get("summary").is_none());
2159        assert!(value["data"]["data"].get("detail").is_none());
2160    }
2161
2162    #[test]
2163    fn representative_workload_data_has_stable_tags() {
2164        let daemon = serde_json::to_value(ResourceHeartbeatData::Daemon(
2165            DaemonHeartbeatData::Kubernetes(KubernetesDaemonHeartbeatData {
2166                status: workload_status(),
2167                namespace: "default".to_string(),
2168                name: "agent".to_string(),
2169                replicas: workload_replicas(),
2170                restarts: Some(0),
2171                command_supported: true,
2172                cpu: None,
2173                memory: None,
2174                workload: None,
2175                pods: vec![],
2176                events: vec![],
2177            }),
2178        ))
2179        .unwrap();
2180        let worker = serde_json::to_value(ResourceHeartbeatData::Worker(
2181            WorkerHeartbeatData::AwsLambda(AwsLambdaWorkerHeartbeatData {
2182                status: workload_status(),
2183                function_name: "handler".to_string(),
2184                runtime: Some("nodejs22.x".to_string()),
2185                package_type: None,
2186                memory_size_mb: None,
2187                timeout_seconds: None,
2188                version: None,
2189                revision_id: None,
2190                last_modified: None,
2191                state: None,
2192                state_reason: None,
2193                state_reason_code: None,
2194                last_update_status: None,
2195                last_update_status_reason: None,
2196                last_update_status_reason_code: None,
2197                code_sha256: None,
2198                layer_count: 0,
2199                function_url_auth_type: None,
2200                function_url_cors_present: false,
2201                trigger_count: 0,
2202            }),
2203        ))
2204        .unwrap();
2205
2206        assert_eq!(daemon["resourceType"], "daemon");
2207        assert_eq!(daemon["data"]["backend"], "kubernetes");
2208        assert_eq!(worker["resourceType"], "worker");
2209        assert_eq!(worker["data"]["backend"], "awsLambda");
2210    }
2211
2212    #[test]
2213    fn representative_cluster_and_data_variants_have_optional_counts() {
2214        let cluster = serde_json::to_value(ResourceHeartbeatData::KubernetesCluster(
2215            KubernetesClusterHeartbeatData {
2216                status: WorkloadHeartbeatStatus {
2217                    health: ObservedHealth::Healthy,
2218                    lifecycle: ProviderLifecycleState::Running,
2219                    message: None,
2220                    stale: false,
2221                    partial: false,
2222                    collection_issues: vec![],
2223                },
2224                node_counts: ObservedCounts {
2225                    desired: Some(3),
2226                    current: Some(3),
2227                    ready: None,
2228                },
2229                pod_counts: ObservedCounts {
2230                    desired: None,
2231                    current: Some(12),
2232                    ready: Some(11),
2233                },
2234                cpu: None,
2235                memory: None,
2236                name: "prod".to_string(),
2237                region: Some("us-east-1".to_string()),
2238                namespace: None,
2239                version: Some("1.33".to_string()),
2240                node_statuses: vec![],
2241                events: vec![],
2242            },
2243        ))
2244        .unwrap();
2245        let queue = serde_json::to_value(ResourceHeartbeatData::Queue(QueueHeartbeatData::AwsSqs(
2246            AwsSqsQueueHeartbeatData {
2247                status: QueueHeartbeatStatus {
2248                    health: ObservedHealth::Healthy,
2249                    lifecycle: ProviderLifecycleState::Running,
2250                    message: None,
2251                    stale: false,
2252                    partial: false,
2253                    collection_issues: vec![],
2254                },
2255                name: "jobs".to_string(),
2256                region: Some("us-east-1".to_string()),
2257                queue_url: Some("https://sqs.us-east-1.amazonaws.com/123/jobs".to_string()),
2258                queue_arn: Some("arn:aws:sqs:us-east-1:123:jobs".to_string()),
2259                visibility_timeout_seconds: Some(30),
2260                message_retention_period_seconds: Some(345600),
2261                delay_seconds: Some(0),
2262                receive_message_wait_time_seconds: Some(0),
2263                maximum_message_size: Some(262144),
2264                redrive_policy: None,
2265                redrive_allow_policy: None,
2266                fifo_queue: Some(false),
2267                content_based_deduplication: None,
2268                deduplication_scope: None,
2269                fifo_throughput_limit: None,
2270                sse_enabled: Some(false),
2271                kms_master_key_id: None,
2272                kms_data_key_reuse_period_seconds: None,
2273                sqs_managed_sse_enabled: Some(false),
2274                approximate_visible_messages: Some(42),
2275                approximate_in_flight_messages: Some(1),
2276                approximate_delayed_messages: Some(0),
2277                approximate_counts: true,
2278            },
2279        )))
2280        .unwrap();
2281        let storage = serde_json::to_value(ResourceHeartbeatData::Storage(
2282            StorageHeartbeatData::AwsS3(AwsS3StorageHeartbeatData {
2283                status: StorageHeartbeatStatus {
2284                    health: ObservedHealth::Healthy,
2285                    lifecycle: ProviderLifecycleState::Running,
2286                    message: None,
2287                    stale: false,
2288                    partial: false,
2289                    collection_issues: vec![],
2290                },
2291                name: "assets".to_string(),
2292                region: Some("us-east-1".to_string()),
2293                bucket_location: Some("us-east-1".to_string()),
2294                versioning_status: Some("Enabled".to_string()),
2295                versioning_enabled: Some(true),
2296                lifecycle_present: false,
2297                lifecycle_rule_count: Some(0),
2298                encryption_config_present: true,
2299                encryption_enabled: Some(true),
2300                public_access_block_present: true,
2301                block_public_acls: Some(true),
2302                ignore_public_acls: Some(true),
2303                block_public_policy: Some(true),
2304                restrict_public_buckets: Some(true),
2305                bucket_policy_present: Some(false),
2306                bucket_acl_present: Some(true),
2307            }),
2308        ))
2309        .unwrap();
2310        let gcp_storage = serde_json::to_value(ResourceHeartbeatData::Storage(
2311            StorageHeartbeatData::GcpCloudStorage(GcpCloudStorageHeartbeatData {
2312                status: StorageHeartbeatStatus {
2313                    health: ObservedHealth::Healthy,
2314                    lifecycle: ProviderLifecycleState::Running,
2315                    message: None,
2316                    stale: false,
2317                    partial: false,
2318                    collection_issues: vec![],
2319                },
2320                name: "assets".to_string(),
2321                bucket_id: Some("project/assets".to_string()),
2322                location: Some("US".to_string()),
2323                location_type: Some("multi-region".to_string()),
2324                storage_class: Some("STANDARD".to_string()),
2325                versioning_enabled: Some(true),
2326                lifecycle_present: false,
2327                lifecycle_rule_count: Some(0),
2328                retention_policy_effective_time: None,
2329                retention_policy_is_locked: None,
2330                retention_period: None,
2331                soft_delete_retention_duration_seconds: None,
2332                soft_delete_effective_time: None,
2333                uniform_bucket_level_access_enabled: Some(true),
2334                uniform_bucket_level_access_locked_time: None,
2335                public_access_prevention: Some("enforced".to_string()),
2336                encryption_config_present: true,
2337                default_kms_key_name: Some(
2338                    "projects/p/locations/l/keyRings/r/cryptoKeys/k".to_string(),
2339                ),
2340            }),
2341        ))
2342        .unwrap();
2343        let kv = serde_json::to_value(ResourceHeartbeatData::Kv(KvHeartbeatData::AwsDynamoDb(
2344            AwsDynamoDbKvHeartbeatData {
2345                status: KvHeartbeatStatus {
2346                    health: ObservedHealth::Healthy,
2347                    lifecycle: ProviderLifecycleState::Running,
2348                    message: None,
2349                    stale: false,
2350                    partial: false,
2351                    collection_issues: vec![],
2352                },
2353                name: "state".to_string(),
2354                region: Some("us-east-1".to_string()),
2355                table_arn: None,
2356                table_status: Some("ACTIVE".to_string()),
2357                billing_mode: Some("PAY_PER_REQUEST".to_string()),
2358                key_schema: vec![AwsDynamoDbKeySchemaElement {
2359                    attribute_name: "pk".to_string(),
2360                    key_type: "HASH".to_string(),
2361                }],
2362                global_secondary_index_count: Some(0),
2363                local_secondary_index_count: Some(0),
2364                item_count: None,
2365                table_size_bytes: None,
2366                stream_enabled: Some(false),
2367                stream_view_type: None,
2368                ttl_status: Some("ENABLED".to_string()),
2369                ttl_attribute_name: Some("ttl".to_string()),
2370                deletion_protection_enabled: Some(false),
2371                sse_status: Some("ENABLED".to_string()),
2372                sse_type: Some("KMS".to_string()),
2373                table_class: None,
2374                replica_count: Some(0),
2375                restore_in_progress: None,
2376            },
2377        )))
2378        .unwrap();
2379
2380        assert_eq!(cluster["resourceType"], "kubernetes-cluster");
2381        assert!(cluster["data"].get("summary").is_none());
2382        assert!(cluster["data"].get("detail").is_none());
2383        assert_eq!(cluster["data"]["name"], "prod");
2384        assert_eq!(queue["data"]["backend"], "awsSqs");
2385        assert_eq!(queue["data"]["approximateVisibleMessages"], 42);
2386        assert!(queue["data"].get("summary").is_none());
2387        assert_eq!(storage["data"]["backend"], "awsS3");
2388        assert!(storage["data"].get("summary").is_none());
2389        assert_eq!(gcp_storage["data"]["backend"], "gcpCloudStorage");
2390        assert_eq!(gcp_storage["data"]["publicAccessPrevention"], "enforced");
2391        assert_eq!(kv["data"]["backend"], "awsDynamoDb");
2392        assert!(kv["data"].get("summary").is_none());
2393        assert_eq!(kv["data"]["itemCount"], json!(null));
2394    }
2395}