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