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    pub scheduling_mode: HorizonWorkloadSchedulingMode,
632    pub replicas: WorkloadReplicaStatus,
633    pub cpu: Option<MetricSample>,
634    pub memory: Option<MetricSample>,
635    pub attention_count: u32,
636    pub replica_units: Vec<ManagedRuntimeUnitStatus>,
637    pub events: Vec<ManagedRuntimeEventSnapshot>,
638}
639
640#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
641#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
642#[serde(rename_all = "camelCase")]
643pub enum HorizonWorkloadSchedulingMode {
644    Replicated,
645    Stateful,
646    Daemon,
647}
648
649#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
650#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
651#[serde(rename_all = "camelCase")]
652pub struct KubernetesContainerHeartbeatData {
653    pub status: WorkloadHeartbeatStatus,
654    pub namespace: String,
655    pub name: String,
656    pub workload_kind: KubernetesWorkloadKind,
657    pub replicas: WorkloadReplicaStatus,
658    pub restarts: Option<u32>,
659    pub cpu: Option<MetricSample>,
660    pub memory: Option<MetricSample>,
661    pub workload: Option<KubernetesWorkloadStatus>,
662    pub pods: Vec<KubernetesPodRuntimeUnitStatus>,
663    pub events: Vec<KubernetesEventSnapshot>,
664}
665
666#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
667#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
668#[serde(rename_all = "camelCase")]
669pub struct LocalContainerHeartbeatData {
670    pub status: WorkloadHeartbeatStatus,
671    pub container_id: Option<String>,
672    pub name: Option<String>,
673    pub image: Option<String>,
674    pub runtime_status: Option<String>,
675    pub restart_count: Option<u32>,
676    pub port_count: u32,
677    pub bind_mount_count: u32,
678    pub local_url: Option<String>,
679    pub runtime_reachable: bool,
680    pub cpu: Option<MetricSample>,
681    pub memory: Option<MetricSample>,
682    pub container_unit: Option<LocalRuntimeUnitStatus>,
683    pub events: Vec<LocalRuntimeEventSnapshot>,
684}
685
686#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
687#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
688#[serde(rename_all = "camelCase")]
689pub struct AwsDaemonHeartbeatData {
690    pub status: WorkloadHeartbeatStatus,
691    pub horizon_cluster_id: String,
692    #[serde(default)]
693    pub daemon_name: String,
694    pub horizon_status: String,
695    pub horizon_status_reason: Option<String>,
696    pub horizon_status_message: Option<String>,
697    pub capacity_group: String,
698    pub desired_machines: u32,
699    pub assigned_machines: u32,
700    pub healthy_instances: u32,
701    pub unavailable_instances: u32,
702    pub command_supported: bool,
703    pub latest_update_timestamp: String,
704    pub daemon_instances: Vec<ManagedRuntimeUnitStatus>,
705    pub events: Vec<ManagedRuntimeEventSnapshot>,
706}
707
708#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
709#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
710#[serde(rename_all = "camelCase")]
711pub struct GcpDaemonHeartbeatData {
712    pub status: WorkloadHeartbeatStatus,
713    pub horizon_cluster_id: String,
714    #[serde(default)]
715    pub daemon_name: String,
716    pub horizon_status: String,
717    pub horizon_status_reason: Option<String>,
718    pub horizon_status_message: Option<String>,
719    pub capacity_group: String,
720    pub desired_machines: u32,
721    pub assigned_machines: u32,
722    pub healthy_instances: u32,
723    pub unavailable_instances: u32,
724    pub command_supported: bool,
725    pub latest_update_timestamp: String,
726    pub daemon_instances: Vec<ManagedRuntimeUnitStatus>,
727    pub events: Vec<ManagedRuntimeEventSnapshot>,
728}
729
730#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
731#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
732#[serde(rename_all = "camelCase")]
733pub struct AzureDaemonHeartbeatData {
734    pub status: WorkloadHeartbeatStatus,
735    pub horizon_cluster_id: String,
736    #[serde(default)]
737    pub daemon_name: String,
738    pub horizon_status: String,
739    pub horizon_status_reason: Option<String>,
740    pub horizon_status_message: Option<String>,
741    pub capacity_group: String,
742    pub desired_machines: u32,
743    pub assigned_machines: u32,
744    pub healthy_instances: u32,
745    pub unavailable_instances: u32,
746    pub command_supported: bool,
747    pub latest_update_timestamp: String,
748    pub daemon_instances: Vec<ManagedRuntimeUnitStatus>,
749    pub events: Vec<ManagedRuntimeEventSnapshot>,
750}
751
752#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
753#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
754#[serde(rename_all = "camelCase")]
755pub struct MachinesDaemonHeartbeatData {
756    pub status: WorkloadHeartbeatStatus,
757    pub horizon_cluster_id: String,
758    #[serde(default)]
759    pub daemon_name: String,
760    pub horizon_status: String,
761    pub horizon_status_reason: Option<String>,
762    pub horizon_status_message: Option<String>,
763    pub capacity_group: String,
764    pub desired_machines: u32,
765    pub assigned_machines: u32,
766    pub healthy_instances: u32,
767    pub unavailable_instances: u32,
768    pub command_supported: bool,
769    pub latest_update_timestamp: String,
770    pub daemon_instances: Vec<ManagedRuntimeUnitStatus>,
771    pub events: Vec<ManagedRuntimeEventSnapshot>,
772}
773
774#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
775#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
776#[serde(rename_all = "camelCase")]
777pub struct ManagedRuntimeUnitStatus {
778    pub replica_id: String,
779    pub name: String,
780    pub machine_id: Option<String>,
781    pub node_name: Option<String>,
782    pub ip: Option<String>,
783    pub ready: bool,
784    pub phase: Option<String>,
785    pub status: Option<String>,
786    pub reason: Option<String>,
787    pub message: Option<String>,
788    pub restart_count: Option<u32>,
789    pub waiting_reason: Option<String>,
790    pub terminated_reason: Option<String>,
791    pub metrics_healthy: Option<bool>,
792    pub metrics_status: Option<String>,
793    pub metrics_last_updated: Option<String>,
794    pub cpu: Option<MetricSample>,
795    pub memory: Option<MetricSample>,
796}
797
798#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
799#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
800#[serde(rename_all = "camelCase")]
801pub struct KubernetesDaemonHeartbeatData {
802    pub status: WorkloadHeartbeatStatus,
803    pub namespace: String,
804    pub name: String,
805    pub replicas: WorkloadReplicaStatus,
806    pub restarts: Option<u32>,
807    pub command_supported: bool,
808    pub cpu: Option<MetricSample>,
809    pub memory: Option<MetricSample>,
810    pub workload: Option<KubernetesWorkloadStatus>,
811    pub pods: Vec<KubernetesPodRuntimeUnitStatus>,
812    pub events: Vec<KubernetesEventSnapshot>,
813}
814
815#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
816#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
817#[serde(rename_all = "camelCase")]
818pub struct LocalDaemonHeartbeatData {
819    pub status: WorkloadHeartbeatStatus,
820    #[serde(default)]
821    pub daemon_name: String,
822    pub runtime_id: String,
823    pub pid: Option<u32>,
824    pub command_supported: bool,
825    pub image_path_present: bool,
826    pub restart_count: Option<u32>,
827    pub exit_reason: Option<String>,
828    pub daemon_instance: Option<LocalRuntimeUnitStatus>,
829    pub events: Vec<LocalRuntimeEventSnapshot>,
830}
831
832#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
833#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
834#[serde(rename_all = "camelCase")]
835pub struct LocalRuntimeUnitStatus {
836    pub unit_id: String,
837    pub name: String,
838    pub kind: LocalRuntimeUnitKind,
839    pub ready: bool,
840    pub phase: Option<String>,
841    pub pid: Option<u32>,
842    pub restart_count: Option<u32>,
843    pub cpu: Option<MetricSample>,
844    pub memory: Option<MetricSample>,
845}
846
847#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
848#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
849#[serde(rename_all = "kebab-case")]
850pub enum LocalRuntimeUnitKind {
851    Container,
852    Process,
853    Daemon,
854}
855
856#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
857#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
858#[serde(rename_all = "camelCase")]
859pub enum KubernetesWorkloadKind {
860    Deployment,
861    StatefulSet,
862    DaemonSet,
863    ReplicaSet,
864    Pod,
865}
866
867#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
868#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
869#[serde(rename_all = "camelCase")]
870pub struct KubernetesWorkloadStatus {
871    pub desired_replicas: Option<u32>,
872    pub ready_replicas: Option<u32>,
873    pub available_replicas: Option<u32>,
874    pub updated_replicas: Option<u32>,
875    pub observed_generation: Option<i64>,
876    pub desired_generation: Option<i64>,
877    pub rollout_reason: Option<String>,
878    pub conditions: Vec<KubernetesWorkloadCondition>,
879}
880
881#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
882#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
883#[serde(rename_all = "camelCase")]
884pub struct KubernetesWorkloadCondition {
885    #[serde(rename = "type")]
886    pub condition_type: String,
887    pub status: String,
888    pub reason: Option<String>,
889    pub message: Option<String>,
890    pub last_transition_time: Option<DateTime<Utc>>,
891}
892
893#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
894#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
895#[serde(rename_all = "camelCase")]
896pub struct KubernetesPodRuntimeUnitStatus {
897    pub name: String,
898    pub uid: Option<String>,
899    pub phase: Option<String>,
900    pub ready: bool,
901    pub restart_count: u32,
902    pub waiting_reason: Option<String>,
903    pub terminated_reason: Option<String>,
904    pub node_name: Option<String>,
905    pub pod_ip: Option<String>,
906    pub owner_references: Vec<KubernetesOwnerReference>,
907    pub cpu: Option<MetricSample>,
908    pub memory: Option<MetricSample>,
909}
910
911#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
912#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
913#[serde(rename_all = "camelCase")]
914pub struct KubernetesOwnerReference {
915    pub kind: String,
916    pub name: String,
917    pub uid: String,
918    pub controller: bool,
919}
920
921#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
922#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
923#[serde(tag = "backend", rename_all = "camelCase")]
924pub enum ComputeClusterHeartbeatData {
925    Aws(AwsComputeClusterHeartbeatData),
926    Gcp(GcpComputeClusterHeartbeatData),
927    Azure(AzureComputeClusterHeartbeatData),
928    Machines(MachinesComputeClusterHeartbeatData),
929    Local(LocalComputeClusterHeartbeatData),
930}
931
932#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
933#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
934#[serde(rename_all = "camelCase")]
935pub struct ComputeClusterHeartbeatStatus {
936    pub health: ObservedHealth,
937    pub lifecycle: ProviderLifecycleState,
938    pub message: Option<String>,
939    pub stale: bool,
940    pub partial: bool,
941    pub collection_issues: Vec<HeartbeatCollectionIssue>,
942}
943
944#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
945#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
946#[serde(rename_all = "camelCase")]
947pub struct LocalComputeClusterHeartbeatData {
948    pub status: ComputeClusterHeartbeatStatus,
949    pub nodes: ObservedCounts,
950    pub name: String,
951    pub host_identifier: Option<String>,
952    pub docker_available: bool,
953    pub docker_version: Option<String>,
954    pub docker_api_version: Option<String>,
955    pub docker_os: Option<String>,
956    pub docker_arch: Option<String>,
957    pub network_name: Option<String>,
958    pub network_available: bool,
959    pub tracked_containers: Option<u32>,
960    pub running_containers: Option<u32>,
961}
962
963#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
964#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
965#[serde(rename_all = "camelCase")]
966pub struct AwsComputeClusterHeartbeatData {
967    pub status: ComputeClusterHeartbeatStatus,
968    pub nodes: ObservedCounts,
969    pub cpu: Option<MetricSample>,
970    pub memory: Option<MetricSample>,
971    pub name: String,
972    pub region: Option<String>,
973    pub backend_cluster_id: Option<String>,
974    pub capacity_groups: Vec<ComputeCapacityGroupStatus>,
975    pub provider_fleets: Vec<ProviderFleetStatus>,
976}
977
978#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
979#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
980#[serde(rename_all = "camelCase")]
981pub struct GcpComputeClusterHeartbeatData {
982    pub status: ComputeClusterHeartbeatStatus,
983    pub nodes: ObservedCounts,
984    pub cpu: Option<MetricSample>,
985    pub memory: Option<MetricSample>,
986    pub name: String,
987    pub region: Option<String>,
988    pub backend_cluster_id: Option<String>,
989    pub capacity_groups: Vec<ComputeCapacityGroupStatus>,
990    pub provider_fleets: Vec<ProviderFleetStatus>,
991}
992
993#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
994#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
995#[serde(rename_all = "camelCase")]
996pub struct AzureComputeClusterHeartbeatData {
997    pub status: ComputeClusterHeartbeatStatus,
998    pub nodes: ObservedCounts,
999    pub cpu: Option<MetricSample>,
1000    pub memory: Option<MetricSample>,
1001    pub name: String,
1002    pub region: Option<String>,
1003    pub backend_cluster_id: Option<String>,
1004    pub capacity_groups: Vec<ComputeCapacityGroupStatus>,
1005    pub provider_fleets: Vec<ProviderFleetStatus>,
1006}
1007
1008#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1009#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1010#[serde(rename_all = "camelCase")]
1011pub struct MachinesComputeClusterHeartbeatData {
1012    pub status: ComputeClusterHeartbeatStatus,
1013    pub nodes: ObservedCounts,
1014    pub cpu: Option<MetricSample>,
1015    pub memory: Option<MetricSample>,
1016    pub name: String,
1017    pub backend_cluster_id: Option<String>,
1018    pub capacity_groups: Vec<ComputeCapacityGroupStatus>,
1019    pub machines: Vec<MachinesComputeMachineStatus>,
1020}
1021
1022#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1023#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1024#[serde(rename_all = "camelCase")]
1025pub struct MachinesComputeMachineStatus {
1026    pub machine_id: String,
1027    pub status: String,
1028    pub capacity_group: String,
1029    pub zone: String,
1030    pub public_ip: Option<String>,
1031    pub overlay_ip: Option<String>,
1032    pub last_heartbeat: String,
1033    pub horizond_version: Option<String>,
1034    pub replica_count: i64,
1035    pub cpu_cores: Option<f64>,
1036    pub memory_bytes: Option<i64>,
1037    pub drain_force: bool,
1038    pub drain_requested_at: Option<String>,
1039    pub drain_deadline_at: Option<String>,
1040    pub drained_at: Option<String>,
1041    #[serde(default, skip_serializing_if = "Vec::is_empty")]
1042    pub drain_blockers: Vec<ComputeDrainBlocker>,
1043}
1044
1045#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1046#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1047#[serde(rename_all = "camelCase")]
1048pub struct ComputeCapacityGroupStatus {
1049    pub group_id: String,
1050    pub current_machines: u32,
1051    pub desired_machines: u32,
1052    pub min_machines: Option<u32>,
1053    pub max_machines: Option<u32>,
1054    pub instance_type: Option<String>,
1055    pub recommendation: Option<ComputeCapacityRecommendation>,
1056    pub capacity_blocker: Option<ComputeCapacityBlocker>,
1057    #[serde(default, skip_serializing_if = "Option::is_none")]
1058    pub drain_progress: Option<ComputeDrainProgress>,
1059}
1060
1061#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
1062#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1063#[serde(rename_all = "camelCase")]
1064pub enum ComputeCapacityBlockerCategory {
1065    Quota,
1066    Capacity,
1067    Allocation,
1068    Other,
1069}
1070
1071#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1072#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1073#[serde(rename_all = "camelCase")]
1074pub struct ComputeCapacityBlocker {
1075    pub category: ComputeCapacityBlockerCategory,
1076    pub provider_code: Option<String>,
1077    pub message: String,
1078    pub provider_reference: Option<String>,
1079    pub observed_at: DateTime<Utc>,
1080}
1081
1082#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1083#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1084#[serde(rename_all = "camelCase")]
1085pub struct ComputeCapacityRecommendation {
1086    pub desired_machines: u32,
1087    pub reason: Option<String>,
1088    pub utilization: Option<MetricSample>,
1089    pub unschedulable_replicas: Option<u32>,
1090}
1091
1092#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
1093#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1094#[serde(rename_all = "camelCase")]
1095pub enum ComputeDrainProgressStatus {
1096    Draining,
1097    Drained,
1098    Terminating,
1099}
1100
1101#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1102#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1103#[serde(rename_all = "camelCase")]
1104pub struct ComputeDrainBlocker {
1105    pub workload_name: String,
1106    pub replica_id: String,
1107    pub scheduling_mode: String,
1108    pub state: String,
1109    pub reason: String,
1110}
1111
1112#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1113#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1114#[serde(rename_all = "camelCase")]
1115pub struct ComputeDrainProgress {
1116    pub machine_id: String,
1117    pub status: ComputeDrainProgressStatus,
1118    pub replica_count: i64,
1119    pub force: bool,
1120    pub stalled: bool,
1121    pub drain_requested_at: Option<String>,
1122    pub drain_deadline_at: Option<String>,
1123    pub drained_at: Option<String>,
1124    #[serde(default, skip_serializing_if = "Vec::is_empty")]
1125    pub blockers: Vec<ComputeDrainBlocker>,
1126}
1127
1128#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1129#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1130#[serde(rename_all = "camelCase")]
1131pub struct ProviderFleetStatus {
1132    pub group_id: String,
1133    pub provider_id: String,
1134    pub location: Option<String>,
1135    pub current_machines: u32,
1136    pub desired_machines: u32,
1137}
1138
1139#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1140#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1141#[serde(rename_all = "camelCase")]
1142pub struct KubernetesClusterHeartbeatData {
1143    pub status: WorkloadHeartbeatStatus,
1144    pub node_counts: ObservedCounts,
1145    pub pod_counts: ObservedCounts,
1146    pub cpu: Option<MetricSample>,
1147    pub memory: Option<MetricSample>,
1148    pub name: String,
1149    pub region: Option<String>,
1150    pub namespace: Option<String>,
1151    pub version: Option<String>,
1152    #[serde(default, skip_serializing_if = "Vec::is_empty")]
1153    pub node_statuses: Vec<KubernetesClusterNodeStatus>,
1154    pub events: Vec<KubernetesEventSnapshot>,
1155}
1156
1157#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1158#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1159#[serde(rename_all = "camelCase")]
1160pub struct KubernetesClusterNodeStatus {
1161    pub name: String,
1162    pub uid: Option<String>,
1163    pub ready: bool,
1164    #[serde(default, skip_serializing_if = "Vec::is_empty")]
1165    pub conditions: Vec<KubernetesNodeConditionStatus>,
1166    pub roles: Vec<String>,
1167    pub labels: BTreeMap<String, String>,
1168    pub allocatable: KubernetesNodeResources,
1169    pub capacity: KubernetesNodeResources,
1170    pub usage: Option<KubernetesNodeUsage>,
1171    pub kubelet_version: Option<String>,
1172    pub container_runtime_version: Option<String>,
1173}
1174
1175#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1176#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1177#[serde(rename_all = "camelCase")]
1178pub struct KubernetesNodeConditionStatus {
1179    pub type_: String,
1180    pub status: String,
1181    pub reason: Option<String>,
1182    pub message: Option<String>,
1183}
1184
1185#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
1186#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1187#[serde(rename_all = "camelCase")]
1188pub struct KubernetesNodeResources {
1189    pub cpu: Option<MetricSample>,
1190    pub memory: Option<MetricSample>,
1191    pub pods: Option<u64>,
1192}
1193
1194#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1195#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1196#[serde(rename_all = "camelCase")]
1197pub struct KubernetesNodeUsage {
1198    pub cpu: Option<MetricSample>,
1199    pub memory: Option<MetricSample>,
1200}
1201
1202#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1203#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1204#[serde(tag = "backend", rename_all = "camelCase")]
1205pub enum QueueHeartbeatData {
1206    AwsSqs(AwsSqsQueueHeartbeatData),
1207    GcpPubSub(GcpPubSubQueueHeartbeatData),
1208    AzureServiceBus(AzureServiceBusQueueHeartbeatData),
1209    Local(LocalQueueHeartbeatData),
1210}
1211
1212#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1213#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1214#[serde(rename_all = "camelCase")]
1215pub struct QueueHeartbeatStatus {
1216    pub health: ObservedHealth,
1217    pub lifecycle: ProviderLifecycleState,
1218    pub message: Option<String>,
1219    pub stale: bool,
1220    pub partial: bool,
1221    pub collection_issues: Vec<HeartbeatCollectionIssue>,
1222}
1223
1224impl Default for QueueHeartbeatStatus {
1225    fn default() -> Self {
1226        Self {
1227            health: ObservedHealth::Unknown,
1228            lifecycle: ProviderLifecycleState::Unknown,
1229            message: None,
1230            stale: false,
1231            partial: false,
1232            collection_issues: vec![],
1233        }
1234    }
1235}
1236
1237#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
1238#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1239#[serde(rename_all = "camelCase")]
1240pub struct LocalQueueHeartbeatData {
1241    pub status: QueueHeartbeatStatus,
1242    pub name: String,
1243    pub path: Option<String>,
1244    pub service_status: Option<String>,
1245}
1246
1247#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
1248#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1249#[serde(rename_all = "camelCase")]
1250pub struct GcpPubSubQueueHeartbeatData {
1251    pub status: QueueHeartbeatStatus,
1252    pub topic_name: String,
1253    pub subscription_name: Option<String>,
1254    pub project_id: Option<String>,
1255    pub topic_full_name: Option<String>,
1256    pub subscription_full_name: Option<String>,
1257    pub endpoint: Option<String>,
1258    pub topic_labels: BTreeMap<String, String>,
1259    pub subscription_labels: BTreeMap<String, String>,
1260    pub message_storage_allowed_persistence_regions: Vec<String>,
1261    pub message_storage_enforce_in_transit: Option<bool>,
1262    pub kms_key_name: Option<String>,
1263    pub schema_name: Option<String>,
1264    pub schema_encoding: Option<String>,
1265    pub schema_first_revision_id: Option<String>,
1266    pub schema_last_revision_id: Option<String>,
1267    pub topic_message_retention_duration: Option<String>,
1268    pub topic_state: Option<String>,
1269    pub subscription_ack_deadline_seconds: Option<u32>,
1270    pub subscription_message_retention_duration: Option<String>,
1271    pub subscription_retain_acked_messages: Option<bool>,
1272    pub subscription_enable_message_ordering: Option<bool>,
1273    pub subscription_filter: Option<String>,
1274    pub subscription_detached: Option<bool>,
1275    pub subscription_state: Option<String>,
1276    pub subscription_push_config_present: Option<bool>,
1277    pub subscription_push_endpoint: Option<String>,
1278    pub subscription_push_attributes: BTreeMap<String, String>,
1279    pub subscription_push_oidc_service_account_email: Option<String>,
1280    pub subscription_push_oidc_audience: Option<String>,
1281    pub subscription_push_pubsub_wrapper_write_metadata: Option<bool>,
1282    pub subscription_push_no_wrapper_write_metadata: Option<bool>,
1283    pub subscription_dead_letter_topic: Option<String>,
1284    pub subscription_dead_letter_max_delivery_attempts: Option<u32>,
1285}
1286
1287#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1288#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1289#[serde(rename_all = "camelCase")]
1290pub struct AzureServiceBusQueueHeartbeatData {
1291    pub status: QueueHeartbeatStatus,
1292    pub name: String,
1293    pub namespace_name: String,
1294    pub resource_group: Option<String>,
1295    pub resource_id: Option<String>,
1296    pub endpoint: Option<String>,
1297    pub queue_status: Option<String>,
1298    pub lock_duration: Option<String>,
1299    pub max_delivery_count: Option<u32>,
1300    pub requires_duplicate_detection: Option<bool>,
1301    pub duplicate_detection_history_time_window: Option<String>,
1302    pub requires_session: Option<bool>,
1303    pub dead_lettering_on_message_expiration: Option<bool>,
1304    pub forward_dead_lettered_messages_to: Option<String>,
1305    pub forward_to: Option<String>,
1306    pub default_message_time_to_live: Option<String>,
1307    pub auto_delete_on_idle: Option<String>,
1308    pub enable_batched_operations: Option<bool>,
1309    pub enable_express: Option<bool>,
1310    pub enable_partitioning: Option<bool>,
1311    pub max_message_size_in_kilobytes: Option<u64>,
1312    pub max_size_in_megabytes: Option<u32>,
1313    pub message_count: Option<u64>,
1314    pub active_message_count: Option<u64>,
1315    pub dead_letter_message_count: Option<u64>,
1316    pub scheduled_message_count: Option<u64>,
1317    pub transfer_message_count: Option<u64>,
1318    pub transfer_dead_letter_message_count: Option<u64>,
1319    pub size_in_bytes: Option<u64>,
1320    pub accessed_at: Option<String>,
1321    pub created_at: Option<String>,
1322    pub updated_at: Option<String>,
1323}
1324
1325#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1326#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1327#[serde(rename_all = "camelCase")]
1328pub struct AwsSqsQueueHeartbeatData {
1329    pub status: QueueHeartbeatStatus,
1330    pub name: String,
1331    pub region: Option<String>,
1332    pub queue_url: Option<String>,
1333    pub queue_arn: Option<String>,
1334    pub visibility_timeout_seconds: Option<u32>,
1335    pub message_retention_period_seconds: Option<u32>,
1336    pub delay_seconds: Option<u32>,
1337    pub receive_message_wait_time_seconds: Option<u32>,
1338    pub maximum_message_size: Option<u32>,
1339    pub redrive_policy: Option<String>,
1340    pub redrive_allow_policy: Option<String>,
1341    pub fifo_queue: Option<bool>,
1342    pub content_based_deduplication: Option<bool>,
1343    pub deduplication_scope: Option<String>,
1344    pub fifo_throughput_limit: Option<String>,
1345    pub sse_enabled: Option<bool>,
1346    pub kms_master_key_id: Option<String>,
1347    pub kms_data_key_reuse_period_seconds: Option<u32>,
1348    pub sqs_managed_sse_enabled: Option<bool>,
1349    pub approximate_visible_messages: Option<u64>,
1350    pub approximate_in_flight_messages: Option<u64>,
1351    pub approximate_delayed_messages: Option<u64>,
1352    pub approximate_counts: bool,
1353}
1354
1355#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1356#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1357#[serde(tag = "backend", rename_all = "camelCase")]
1358pub enum KvHeartbeatData {
1359    AwsDynamoDb(AwsDynamoDbKvHeartbeatData),
1360    GcpFirestore(GcpFirestoreKvHeartbeatData),
1361    AzureTable(AzureTableKvHeartbeatData),
1362    Local(LocalKvHeartbeatData),
1363}
1364
1365#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1366#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1367#[serde(rename_all = "camelCase")]
1368pub struct KvHeartbeatStatus {
1369    pub health: ObservedHealth,
1370    pub lifecycle: ProviderLifecycleState,
1371    pub message: Option<String>,
1372    pub stale: bool,
1373    pub partial: bool,
1374    pub collection_issues: Vec<HeartbeatCollectionIssue>,
1375}
1376
1377impl Default for KvHeartbeatStatus {
1378    fn default() -> Self {
1379        Self {
1380            health: ObservedHealth::Unknown,
1381            lifecycle: ProviderLifecycleState::Unknown,
1382            message: None,
1383            stale: false,
1384            partial: false,
1385            collection_issues: vec![],
1386        }
1387    }
1388}
1389
1390#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1391#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1392#[serde(rename_all = "camelCase")]
1393pub struct AwsDynamoDbKvHeartbeatData {
1394    pub status: KvHeartbeatStatus,
1395    pub name: String,
1396    pub region: Option<String>,
1397    pub table_arn: Option<String>,
1398    pub table_status: Option<String>,
1399    pub billing_mode: Option<String>,
1400    pub key_schema: Vec<AwsDynamoDbKeySchemaElement>,
1401    pub global_secondary_index_count: Option<u32>,
1402    pub local_secondary_index_count: Option<u32>,
1403    pub item_count: Option<u64>,
1404    pub table_size_bytes: Option<u64>,
1405    pub stream_enabled: Option<bool>,
1406    pub stream_view_type: Option<String>,
1407    pub ttl_status: Option<String>,
1408    pub ttl_attribute_name: Option<String>,
1409    pub deletion_protection_enabled: Option<bool>,
1410    pub sse_status: Option<String>,
1411    pub sse_type: Option<String>,
1412    pub table_class: Option<String>,
1413    pub replica_count: Option<u32>,
1414    pub restore_in_progress: Option<bool>,
1415}
1416
1417#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1418#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1419#[serde(rename_all = "camelCase")]
1420pub struct AwsDynamoDbKeySchemaElement {
1421    pub attribute_name: String,
1422    pub key_type: String,
1423}
1424
1425#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1426#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1427#[serde(rename_all = "camelCase")]
1428pub struct GcpFirestoreKvHeartbeatData {
1429    pub status: KvHeartbeatStatus,
1430    pub database_name: String,
1431    pub project_id: Option<String>,
1432    pub endpoint: Option<String>,
1433    pub location_id: Option<String>,
1434    pub database_type: Option<String>,
1435    pub concurrency_mode: Option<String>,
1436    pub app_engine_integration_mode: Option<String>,
1437    pub delete_protection_state: Option<String>,
1438    pub point_in_time_recovery_enablement: Option<String>,
1439    pub version_retention_period: Option<String>,
1440    pub earliest_version_time: Option<String>,
1441    pub create_time: Option<String>,
1442    pub update_time: Option<String>,
1443    pub delete_time: Option<String>,
1444    pub database_edition: Option<String>,
1445    pub cmek_enabled: bool,
1446    pub source_info_present: bool,
1447}
1448
1449#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1450#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1451#[serde(rename_all = "camelCase")]
1452pub struct AzureTableKvHeartbeatData {
1453    pub status: KvHeartbeatStatus,
1454    pub table_name: String,
1455    pub storage_account_name: String,
1456    pub resource_group: Option<String>,
1457    pub endpoint: Option<String>,
1458    pub storage_account_resource_id: Option<String>,
1459    pub storage_account_location: Option<String>,
1460    pub storage_account_kind: Option<String>,
1461    pub storage_account_provisioning_state: Option<String>,
1462    pub storage_account_primary_status: Option<String>,
1463    pub table_exists: bool,
1464    pub signed_identifier_count: Option<u32>,
1465}
1466
1467#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1468#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1469#[serde(rename_all = "camelCase")]
1470pub struct LocalKvHeartbeatData {
1471    pub status: KvHeartbeatStatus,
1472    pub name: String,
1473    pub path: String,
1474    pub path_exists: bool,
1475    pub is_directory: Option<bool>,
1476    pub cloud_metadata_supported: bool,
1477}
1478
1479#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1480#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1481#[serde(tag = "backend", rename_all = "camelCase")]
1482pub enum PostgresHeartbeatData {
1483    /// AWS Aurora Serverless v2 backend.
1484    Aurora(AuroraPostgresHeartbeatData),
1485    /// GCP Cloud SQL backend.
1486    CloudSql(GcpCloudSqlPostgresHeartbeatData),
1487    /// Azure Flexible Server backend.
1488    FlexibleServer(AzureFlexibleServerPostgresHeartbeatData),
1489    /// Local embedded Postgres backend.
1490    Local(LocalPostgresHeartbeatData),
1491}
1492
1493#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1494#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1495#[serde(rename_all = "camelCase")]
1496pub struct PostgresHeartbeatStatus {
1497    pub health: ObservedHealth,
1498    pub lifecycle: ProviderLifecycleState,
1499    pub message: Option<String>,
1500    pub stale: bool,
1501    pub partial: bool,
1502    pub collection_issues: Vec<HeartbeatCollectionIssue>,
1503}
1504
1505impl Default for PostgresHeartbeatStatus {
1506    fn default() -> Self {
1507        Self {
1508            health: ObservedHealth::Unknown,
1509            lifecycle: ProviderLifecycleState::Unknown,
1510            message: None,
1511            stale: false,
1512            partial: false,
1513            collection_issues: vec![],
1514        }
1515    }
1516}
1517
1518#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1519#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1520#[serde(rename_all = "camelCase")]
1521pub struct LocalPostgresHeartbeatData {
1522    pub status: PostgresHeartbeatStatus,
1523    pub name: String,
1524    pub port: Option<u16>,
1525    pub version: String,
1526    pub process_running: bool,
1527}
1528
1529#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1530#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1531#[serde(rename_all = "camelCase")]
1532pub struct AuroraPostgresHeartbeatData {
1533    pub status: PostgresHeartbeatStatus,
1534    pub cluster_identifier: String,
1535    #[serde(skip_serializing_if = "Option::is_none")]
1536    pub engine_version: Option<String>,
1537    #[serde(skip_serializing_if = "Option::is_none")]
1538    pub endpoint: Option<String>,
1539    /// Latest sampled `ServerlessDatabaseCapacity` (ACU).
1540    #[serde(skip_serializing_if = "Option::is_none")]
1541    pub serverless_capacity: Option<f64>,
1542    /// True when a `minCapacity: 0` instance has not reached 0 ACU over the observation
1543    /// window — it is silently paying always-on prices (auto-pause verification).
1544    pub never_pauses: bool,
1545}
1546
1547#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1548#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1549#[serde(rename_all = "camelCase")]
1550pub struct GcpCloudSqlPostgresHeartbeatData {
1551    pub status: PostgresHeartbeatStatus,
1552    pub instance_name: String,
1553    #[serde(skip_serializing_if = "Option::is_none")]
1554    pub state: Option<String>,
1555    #[serde(skip_serializing_if = "Option::is_none")]
1556    pub database_version: Option<String>,
1557}
1558
1559#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1560#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1561#[serde(rename_all = "camelCase")]
1562pub struct AzureFlexibleServerPostgresHeartbeatData {
1563    pub status: PostgresHeartbeatStatus,
1564    pub server_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 version: Option<String>,
1569}
1570
1571#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1572#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1573#[serde(tag = "backend", rename_all = "camelCase")]
1574pub enum VaultHeartbeatData {
1575    AwsParameterStore(AwsParameterStoreVaultHeartbeatData),
1576    GcpSecretManager(GcpSecretManagerVaultHeartbeatData),
1577    AzureKeyVault(AzureKeyVaultHeartbeatData),
1578    KubernetesSecret(KubernetesSecretVaultHeartbeatData),
1579    Local(LocalVaultHeartbeatData),
1580}
1581
1582#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1583#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1584#[serde(rename_all = "camelCase")]
1585pub struct VaultHeartbeatStatus {
1586    pub health: ObservedHealth,
1587    pub lifecycle: ProviderLifecycleState,
1588    pub message: Option<String>,
1589    pub stale: bool,
1590    pub partial: bool,
1591    pub collection_issues: Vec<HeartbeatCollectionIssue>,
1592}
1593
1594impl Default for VaultHeartbeatStatus {
1595    fn default() -> Self {
1596        Self {
1597            health: ObservedHealth::Healthy,
1598            lifecycle: ProviderLifecycleState::Running,
1599            message: None,
1600            stale: false,
1601            partial: false,
1602            collection_issues: vec![],
1603        }
1604    }
1605}
1606
1607#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1608#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1609#[serde(rename_all = "camelCase")]
1610pub struct AwsParameterStoreVaultHeartbeatData {
1611    pub status: VaultHeartbeatStatus,
1612    pub account_id: String,
1613    pub region: String,
1614    pub prefix: String,
1615    pub parameter_metadata_sampled: bool,
1616    pub sampled_parameter_count: Option<u32>,
1617    pub sampled_secure_string_count: Option<u32>,
1618    pub sampled_string_count: Option<u32>,
1619    pub sampled_string_list_count: Option<u32>,
1620    pub sampled_advanced_tier_count: Option<u32>,
1621    pub sampled_kms_key_metadata_present_count: Option<u32>,
1622    pub latest_modified_at: Option<DateTime<Utc>>,
1623    pub has_more_parameters: Option<bool>,
1624}
1625
1626#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1627#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1628#[serde(rename_all = "camelCase")]
1629pub struct GcpSecretManagerVaultHeartbeatData {
1630    pub status: VaultHeartbeatStatus,
1631    pub project_id: String,
1632    pub location: String,
1633    pub prefix: String,
1634    pub secret_metadata_listed: bool,
1635}
1636
1637#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1638#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1639#[serde(rename_all = "camelCase")]
1640pub struct AzureKeyVaultHeartbeatData {
1641    pub status: VaultHeartbeatStatus,
1642    pub name: String,
1643    pub resource_group: Option<String>,
1644    pub resource_id: Option<String>,
1645    pub location: Option<String>,
1646    pub vault_uri: Option<String>,
1647    pub provisioning_state: Option<String>,
1648    pub sku_family: Option<String>,
1649    pub sku_name: Option<String>,
1650    pub soft_delete_enabled: bool,
1651    pub soft_delete_retention_days: i32,
1652    pub purge_protection_enabled: Option<bool>,
1653    pub rbac_authorization_enabled: bool,
1654    pub public_network_access: String,
1655    pub access_policy_count: u32,
1656    pub private_endpoint_connection_count: u32,
1657    pub secret_metadata_listed: bool,
1658}
1659
1660#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1661#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1662#[serde(rename_all = "camelCase")]
1663pub struct KubernetesSecretVaultHeartbeatData {
1664    pub status: VaultHeartbeatStatus,
1665    pub namespace: String,
1666    pub prefix: String,
1667    pub secret_metadata_listed: bool,
1668}
1669
1670#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1671#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1672#[serde(rename_all = "camelCase")]
1673pub struct LocalVaultHeartbeatData {
1674    pub status: VaultHeartbeatStatus,
1675    pub path: String,
1676    pub path_exists: bool,
1677    pub is_directory: Option<bool>,
1678    pub readonly: Option<bool>,
1679    pub modified_at: Option<DateTime<Utc>>,
1680    pub secret_metadata_listed: bool,
1681}
1682
1683#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1684#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1685#[serde(tag = "backend", rename_all = "camelCase")]
1686pub enum ServiceAccountHeartbeatData {
1687    AwsIamRole(AwsIamRoleServiceAccountHeartbeatData),
1688    GcpServiceAccount(GcpServiceAccountHeartbeatData),
1689    AzureManagedIdentity(AzureManagedIdentityServiceAccountHeartbeatData),
1690    Local(LocalServiceAccountHeartbeatData),
1691}
1692
1693#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1694#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1695#[serde(rename_all = "camelCase")]
1696pub struct ServiceAccountHeartbeatStatus {
1697    pub health: ObservedHealth,
1698    pub lifecycle: ProviderLifecycleState,
1699    pub message: Option<String>,
1700    pub stale: bool,
1701    pub partial: bool,
1702    pub collection_issues: Vec<HeartbeatCollectionIssue>,
1703}
1704
1705#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1706#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1707#[serde(rename_all = "camelCase")]
1708pub struct AwsIamRoleServiceAccountHeartbeatData {
1709    pub status: ServiceAccountHeartbeatStatus,
1710    pub role_name: String,
1711    pub role_arn: String,
1712    pub role_id: String,
1713    pub path: String,
1714    pub create_date: String,
1715    pub description: Option<String>,
1716    pub max_session_duration: Option<i32>,
1717    pub assume_role_policy_present: bool,
1718    pub permissions_boundary_type: Option<String>,
1719    pub permissions_boundary_arn: Option<String>,
1720    pub tag_count: u32,
1721    pub managed_tag_count: u32,
1722    pub attached_policy_count: u32,
1723    pub attached_policy_names: Vec<String>,
1724    pub inline_policy_count: u32,
1725    pub inline_policy_names: Vec<String>,
1726    pub stack_permissions_applied: bool,
1727    pub last_used_date: Option<String>,
1728    pub last_used_region: Option<String>,
1729}
1730
1731#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1732#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1733#[serde(rename_all = "camelCase")]
1734pub struct GcpServiceAccountHeartbeatData {
1735    pub status: ServiceAccountHeartbeatStatus,
1736    pub name: Option<String>,
1737    pub project_id: Option<String>,
1738    pub unique_id: Option<String>,
1739    pub email: String,
1740    pub display_name: Option<String>,
1741    pub description: Option<String>,
1742    pub oauth2_client_id: Option<String>,
1743    pub disabled: Option<bool>,
1744    pub etag: Option<String>,
1745    pub project_binding_count: u32,
1746    pub project_roles: Vec<String>,
1747    pub service_account_binding_count: u32,
1748    pub service_account_roles: Vec<String>,
1749}
1750
1751#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1752#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1753#[serde(rename_all = "camelCase")]
1754pub struct AzureManagedIdentityServiceAccountHeartbeatData {
1755    pub status: ServiceAccountHeartbeatStatus,
1756    pub name: String,
1757    pub resource_id: String,
1758    pub resource_group: String,
1759    pub location: String,
1760    pub type_: Option<String>,
1761    pub client_id: Option<String>,
1762    pub principal_id: Option<String>,
1763    pub tenant_id: Option<String>,
1764    pub isolation_scope: Option<String>,
1765    pub managed_tag_count: u32,
1766    pub role_assignment_count: u32,
1767    pub role_assignment_ids: Vec<String>,
1768    pub custom_role_definition_count: u32,
1769    pub custom_role_definition_ids: Vec<String>,
1770    pub stack_permissions_applied: bool,
1771}
1772
1773#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1774#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1775#[serde(rename_all = "camelCase")]
1776pub struct LocalServiceAccountHeartbeatData {
1777    pub status: ServiceAccountHeartbeatStatus,
1778    pub identity: String,
1779    pub configured: bool,
1780}
1781
1782#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1783#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1784#[serde(tag = "backend", rename_all = "camelCase")]
1785pub enum NetworkHeartbeatData {
1786    AwsVpc(AwsVpcNetworkHeartbeatData),
1787    GcpVpc(GcpVpcNetworkHeartbeatData),
1788    AzureVnet(AzureVnetNetworkHeartbeatData),
1789}
1790
1791#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1792#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1793#[serde(rename_all = "camelCase")]
1794pub struct NetworkHeartbeatStatus {
1795    pub health: ObservedHealth,
1796    pub lifecycle: ProviderLifecycleState,
1797    pub message: Option<String>,
1798    pub stale: bool,
1799    pub partial: bool,
1800    pub collection_issues: Vec<HeartbeatCollectionIssue>,
1801}
1802
1803#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1804#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1805#[serde(rename_all = "camelCase")]
1806pub struct AwsVpcNetworkHeartbeatData {
1807    pub status: NetworkHeartbeatStatus,
1808    pub vpc_id: Option<String>,
1809    pub vpc_state: Option<String>,
1810    pub cidr_block: Option<String>,
1811    pub public_subnet_ids: Vec<String>,
1812    pub private_subnet_ids: Vec<String>,
1813    pub availability_zones: Vec<String>,
1814    pub internet_gateway_id: Option<String>,
1815    pub nat_gateway_id: Option<String>,
1816    pub route_table_count: u32,
1817    pub security_group_id: Option<String>,
1818    pub is_byo_vpc: bool,
1819}
1820
1821#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1822#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1823#[serde(rename_all = "camelCase")]
1824pub struct GcpVpcNetworkHeartbeatData {
1825    pub status: NetworkHeartbeatStatus,
1826    pub network_name: Option<String>,
1827    pub network_self_link: Option<String>,
1828    pub subnetwork_name: Option<String>,
1829    pub subnetwork_self_link: Option<String>,
1830    pub region: Option<String>,
1831    pub cidr_block: Option<String>,
1832    pub router_name: Option<String>,
1833    pub cloud_nat_name: Option<String>,
1834    pub firewall_name: Option<String>,
1835    pub is_byo_vpc: bool,
1836}
1837
1838#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1839#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1840#[serde(rename_all = "camelCase")]
1841pub struct AzureVnetNetworkHeartbeatData {
1842    pub status: NetworkHeartbeatStatus,
1843    pub vnet_name: Option<String>,
1844    pub vnet_resource_id: Option<String>,
1845    pub resource_group: Option<String>,
1846    pub location: Option<String>,
1847    pub cidr_block: Option<String>,
1848    pub public_subnet_name: Option<String>,
1849    pub private_subnet_name: Option<String>,
1850    pub application_gateway_subnet_name: Option<String>,
1851    pub private_endpoint_subnet_name: Option<String>,
1852    pub nat_gateway_id: Option<String>,
1853    pub public_ip_id: Option<String>,
1854    pub nsg_id: Option<String>,
1855    pub is_byo_vnet: bool,
1856    pub last_byo_vnet_verification_error_code: Option<String>,
1857}
1858
1859#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1860#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1861#[serde(tag = "backend", rename_all = "camelCase")]
1862pub enum RemoteStackManagementHeartbeatData {
1863    AwsIamRole(AwsRemoteStackManagementHeartbeatData),
1864    GcpServiceAccount(GcpRemoteStackManagementHeartbeatData),
1865    AzureManagedIdentity(AzureRemoteStackManagementHeartbeatData),
1866}
1867
1868#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1869#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1870#[serde(rename_all = "camelCase")]
1871pub struct RemoteStackManagementHeartbeatStatus {
1872    pub health: ObservedHealth,
1873    pub lifecycle: ProviderLifecycleState,
1874    pub message: Option<String>,
1875    pub stale: bool,
1876    pub partial: bool,
1877    pub collection_issues: Vec<HeartbeatCollectionIssue>,
1878}
1879
1880#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1881#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1882#[serde(rename_all = "camelCase")]
1883pub struct AwsRemoteStackManagementHeartbeatData {
1884    pub status: RemoteStackManagementHeartbeatStatus,
1885    pub role_name: Option<String>,
1886    pub role_arn: Option<String>,
1887    pub management_permissions_applied: bool,
1888}
1889
1890#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1891#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1892#[serde(rename_all = "camelCase")]
1893pub struct GcpRemoteStackManagementHeartbeatData {
1894    pub status: RemoteStackManagementHeartbeatStatus,
1895    pub service_account_email: Option<String>,
1896    pub service_account_unique_id: Option<String>,
1897    pub role_bound: bool,
1898    pub impersonation_granted: bool,
1899}
1900
1901#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1902#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1903#[serde(rename_all = "camelCase")]
1904pub struct AzureRemoteStackManagementHeartbeatData {
1905    pub status: RemoteStackManagementHeartbeatStatus,
1906    pub uami_resource_id: Option<String>,
1907    pub uami_client_id: Option<String>,
1908    pub uami_principal_id: Option<String>,
1909    pub tenant_id: Option<String>,
1910    pub fic_name: Option<String>,
1911    pub role_definition_id: Option<String>,
1912    pub role_assignment_ids: Vec<String>,
1913}
1914
1915#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1916#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1917#[serde(tag = "backend", rename_all = "camelCase")]
1918pub enum ArtifactRegistryHeartbeatData {
1919    AwsEcr(AwsEcrArtifactRegistryHeartbeatData),
1920    GcpArtifactRegistry(GcpArtifactRegistryHeartbeatData),
1921    AzureContainerRegistry(AzureContainerRegistryHeartbeatData),
1922    Local(LocalArtifactRegistryHeartbeatData),
1923}
1924
1925#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1926#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1927#[serde(rename_all = "camelCase")]
1928pub struct ArtifactRegistryHeartbeatStatus {
1929    pub health: ObservedHealth,
1930    pub lifecycle: ProviderLifecycleState,
1931    pub message: Option<String>,
1932    pub stale: bool,
1933    pub partial: bool,
1934    pub collection_issues: Vec<HeartbeatCollectionIssue>,
1935}
1936
1937#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1938#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1939#[serde(rename_all = "camelCase")]
1940pub struct AwsEcrArtifactRegistryHeartbeatData {
1941    pub status: ArtifactRegistryHeartbeatStatus,
1942    pub registry_id: String,
1943    pub region: String,
1944    pub registry_uri: String,
1945    pub repository_prefix: String,
1946    pub pull_role_arn: Option<String>,
1947    pub push_role_arn: Option<String>,
1948    pub repository_count: u32,
1949    pub repositories_truncated: bool,
1950    pub repositories: Vec<AwsEcrRepositoryHeartbeatData>,
1951}
1952
1953#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1954#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1955#[serde(rename_all = "camelCase")]
1956pub struct AwsEcrRepositoryHeartbeatData {
1957    pub repository_arn: String,
1958    pub registry_id: String,
1959    pub repository_name: String,
1960    pub repository_uri: String,
1961    pub created_at: f64,
1962    pub image_tag_mutability: Option<String>,
1963    pub scan_on_push: Option<bool>,
1964    pub encryption_type: Option<String>,
1965    pub kms_key_present: bool,
1966}
1967
1968#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1969#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1970#[serde(rename_all = "camelCase")]
1971pub struct GcpArtifactRegistryHeartbeatData {
1972    pub status: ArtifactRegistryHeartbeatStatus,
1973    pub project_id: String,
1974    pub location: String,
1975    pub repository_id: String,
1976    pub name: Option<String>,
1977    pub format: Option<String>,
1978    pub mode: Option<String>,
1979    pub description: Option<String>,
1980    pub label_count: u32,
1981    pub cleanup_policy_count: u32,
1982    pub cleanup_policy_dry_run: Option<bool>,
1983    pub kms_key_name_present: bool,
1984    pub size_bytes: Option<String>,
1985    pub satisfies_pzs: Option<bool>,
1986    pub create_time: Option<String>,
1987    pub update_time: Option<String>,
1988    pub iam_policy_etag_present: bool,
1989    pub iam_binding_count: u32,
1990    pub iam_roles: Vec<String>,
1991    pub pull_service_account_email: Option<String>,
1992    pub push_service_account_email: Option<String>,
1993}
1994
1995#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1996#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1997#[serde(rename_all = "camelCase")]
1998pub struct AzureContainerRegistryHeartbeatData {
1999    pub status: ArtifactRegistryHeartbeatStatus,
2000    pub name: String,
2001    pub resource_id: Option<String>,
2002    pub resource_group: String,
2003    pub location: String,
2004    pub type_: Option<String>,
2005    pub login_server: Option<String>,
2006    pub sku_name: String,
2007    pub sku_tier: Option<String>,
2008    pub provisioning_state: Option<String>,
2009    pub admin_user_enabled: bool,
2010    pub anonymous_pull_enabled: bool,
2011    pub public_network_access: String,
2012    pub network_rule_bypass_options: String,
2013    pub network_rule_default_action: Option<String>,
2014    pub ip_rule_count: u32,
2015    pub encryption_status: Option<String>,
2016    pub encryption_key_vault_uri_present: bool,
2017    pub encryption_key_identifier_present: bool,
2018    pub policies_present: bool,
2019    pub policy_count: u32,
2020    pub private_endpoint_connection_count: u32,
2021    pub data_endpoint_enabled: Option<bool>,
2022    pub data_endpoint_host_names: Vec<String>,
2023    pub zone_redundancy: String,
2024    pub creation_date: Option<String>,
2025    pub managed_tag_count: u32,
2026}
2027
2028#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2029#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
2030#[serde(rename_all = "camelCase")]
2031pub struct LocalArtifactRegistryHeartbeatData {
2032    pub status: ArtifactRegistryHeartbeatStatus,
2033    pub registry_url: String,
2034    pub reachable: bool,
2035}
2036
2037#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2038#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
2039#[serde(tag = "backend", rename_all = "camelCase")]
2040pub enum BuildHeartbeatData {
2041    AwsCodeBuild(AwsCodeBuildHeartbeatData),
2042    GcpCloudBuild(GcpCloudBuildHeartbeatData),
2043    AzureContainerApps(AzureContainerAppsBuildHeartbeatData),
2044    KubernetesJob(KubernetesBuildHeartbeatData),
2045}
2046
2047#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2048#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
2049#[serde(rename_all = "camelCase")]
2050pub struct BuildHeartbeatStatus {
2051    pub health: ObservedHealth,
2052    pub lifecycle: ProviderLifecycleState,
2053    pub message: Option<String>,
2054    pub stale: bool,
2055    pub partial: bool,
2056    pub collection_issues: Vec<HeartbeatCollectionIssue>,
2057}
2058
2059#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2060#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
2061#[serde(rename_all = "camelCase")]
2062pub struct AwsCodeBuildHeartbeatData {
2063    pub status: BuildHeartbeatStatus,
2064    pub project_name: String,
2065    pub project_arn: Option<String>,
2066    pub description: Option<String>,
2067    pub source_type: Option<String>,
2068    pub artifacts_type: Option<String>,
2069    pub artifacts_encryption_disabled: Option<bool>,
2070    pub environment_type: Option<String>,
2071    pub environment_image: Option<String>,
2072    pub compute_type: Option<String>,
2073    pub image_pull_credentials_type: Option<String>,
2074    pub privileged_mode: Option<bool>,
2075    pub environment_variable_count: u32,
2076    pub service_role_present: bool,
2077    pub encryption_key_present: bool,
2078    pub cloud_watch_logs_status: Option<String>,
2079    pub s3_logs_status: Option<String>,
2080    pub timeout_in_minutes: Option<i32>,
2081    pub queued_timeout_in_minutes: Option<i32>,
2082    pub created: Option<f64>,
2083    pub last_modified: Option<f64>,
2084}
2085
2086#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2087#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
2088#[serde(rename_all = "camelCase")]
2089pub struct GcpCloudBuildHeartbeatData {
2090    pub status: BuildHeartbeatStatus,
2091    pub project_id: String,
2092    pub location: String,
2093    pub build_config_id: String,
2094    pub service_account: Option<String>,
2095    pub environment_variable_count: u32,
2096}
2097
2098#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2099#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
2100#[serde(rename_all = "camelCase")]
2101pub struct AzureContainerAppsBuildHeartbeatData {
2102    pub status: BuildHeartbeatStatus,
2103    pub managed_environment_id: String,
2104    pub resource_group_name: String,
2105    pub managed_identity_id: Option<String>,
2106    pub resource_prefix: 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 KubernetesBuildHeartbeatData {
2114    pub status: BuildHeartbeatStatus,
2115    pub job_name: String,
2116    pub namespace: String,
2117    pub active: Option<i32>,
2118    pub succeeded: Option<i32>,
2119    pub failed: Option<i32>,
2120    pub start_time: Option<DateTime<Utc>>,
2121    pub completion_time: Option<DateTime<Utc>>,
2122    pub condition_count: u32,
2123    pub image_digest: Option<String>,
2124    pub events: Vec<KubernetesEventSnapshot>,
2125}
2126
2127#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2128#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
2129#[serde(tag = "backend", rename_all = "camelCase")]
2130pub enum ServiceActivationHeartbeatData {
2131    GcpServiceUsage(GcpServiceUsageActivationHeartbeatData),
2132    AzureResourceProvider(AzureResourceProviderActivationHeartbeatData),
2133}
2134
2135#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2136#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
2137#[serde(rename_all = "camelCase")]
2138pub struct ServiceActivationHeartbeatStatus {
2139    pub health: ObservedHealth,
2140    pub lifecycle: ProviderLifecycleState,
2141    pub message: Option<String>,
2142    pub stale: bool,
2143    pub partial: bool,
2144    pub collection_issues: Vec<HeartbeatCollectionIssue>,
2145}
2146
2147impl Default for ServiceActivationHeartbeatStatus {
2148    fn default() -> Self {
2149        Self {
2150            health: ObservedHealth::Healthy,
2151            lifecycle: ProviderLifecycleState::Running,
2152            message: None,
2153            stale: false,
2154            partial: false,
2155            collection_issues: vec![],
2156        }
2157    }
2158}
2159
2160#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2161#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
2162#[serde(rename_all = "camelCase")]
2163pub struct GcpServiceUsageActivationHeartbeatData {
2164    pub status: ServiceActivationHeartbeatStatus,
2165    pub project_id: String,
2166    pub service_name: String,
2167    pub service_resource_name: Option<String>,
2168    pub title: Option<String>,
2169    pub state: Option<String>,
2170    pub enabled: bool,
2171    pub last_operation_name: Option<String>,
2172}
2173
2174#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2175#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
2176#[serde(rename_all = "camelCase")]
2177pub struct AzureResourceProviderActivationHeartbeatData {
2178    pub status: ServiceActivationHeartbeatStatus,
2179    pub namespace: String,
2180    pub provider_id: Option<String>,
2181    pub registration_state: Option<String>,
2182    pub registration_policy: Option<String>,
2183    pub resource_type_count: u32,
2184    pub registered: bool,
2185}
2186
2187#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2188#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
2189#[serde(rename_all = "camelCase")]
2190pub struct AzureResourceGroupHeartbeatData {
2191    pub status: AzureResourceGroupHeartbeatStatus,
2192    pub name: String,
2193    pub resource_id: Option<String>,
2194    pub location: Option<String>,
2195    pub provisioning_state: Option<String>,
2196    pub managed_tags: BTreeMap<String, String>,
2197}
2198
2199#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2200#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
2201#[serde(rename_all = "camelCase")]
2202pub struct AzureResourceGroupHeartbeatStatus {
2203    pub health: ObservedHealth,
2204    pub lifecycle: ProviderLifecycleState,
2205    pub message: Option<String>,
2206    pub stale: bool,
2207    pub partial: bool,
2208    pub collection_issues: Vec<HeartbeatCollectionIssue>,
2209}
2210
2211impl Default for AzureResourceGroupHeartbeatStatus {
2212    fn default() -> Self {
2213        Self {
2214            health: ObservedHealth::Healthy,
2215            lifecycle: ProviderLifecycleState::Running,
2216            message: None,
2217            stale: false,
2218            partial: false,
2219            collection_issues: vec![],
2220        }
2221    }
2222}
2223
2224#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2225#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
2226#[serde(rename_all = "camelCase")]
2227pub struct AzureStorageAccountHeartbeatData {
2228    pub status: StorageHeartbeatStatus,
2229    pub name: String,
2230    pub resource_id: Option<String>,
2231    pub resource_group: Option<String>,
2232    pub location: Option<String>,
2233    pub kind: Option<String>,
2234    pub sku_name: Option<String>,
2235    pub sku_tier: Option<String>,
2236    pub provisioning_state: Option<String>,
2237    pub primary_endpoints: AzureStorageAccountEndpoints,
2238    pub secondary_endpoints: AzureStorageAccountEndpoints,
2239    pub public_network_access: Option<String>,
2240    pub allow_blob_public_access: Option<bool>,
2241    pub allow_shared_key_access: Option<bool>,
2242    pub minimum_tls_version: Option<String>,
2243    pub supports_https_traffic_only: Option<bool>,
2244    pub encryption_key_source: Option<String>,
2245    pub require_infrastructure_encryption: Option<bool>,
2246    pub network_default_action: Option<String>,
2247    pub network_bypass: Option<String>,
2248    pub network_ip_rule_count: Option<u32>,
2249    pub network_virtual_network_rule_count: Option<u32>,
2250    pub network_resource_access_rule_count: Option<u32>,
2251}
2252
2253#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
2254#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
2255#[serde(rename_all = "camelCase")]
2256pub struct AzureStorageAccountEndpoints {
2257    pub blob: Option<String>,
2258    pub dfs: Option<String>,
2259    pub file: Option<String>,
2260    pub queue: Option<String>,
2261    pub table: Option<String>,
2262    pub web: Option<String>,
2263}
2264
2265#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2266#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
2267#[serde(rename_all = "camelCase")]
2268pub struct AzureContainerAppsEnvironmentHeartbeatData {
2269    pub status: AzureContainerAppsEnvironmentHeartbeatStatus,
2270    pub name: String,
2271    pub resource_id: Option<String>,
2272    pub resource_group: Option<String>,
2273    pub location: Option<String>,
2274    pub kind: Option<String>,
2275    pub provisioning_state: Option<String>,
2276    pub default_domain: Option<String>,
2277    pub static_ip: Option<String>,
2278    pub custom_domain_verification_id: Option<String>,
2279    pub infrastructure_resource_group: Option<String>,
2280    pub event_stream_endpoint: Option<String>,
2281    pub zone_redundant: Option<bool>,
2282    pub workload_profile_count: u32,
2283    pub workload_profiles: Vec<AzureContainerAppsEnvironmentWorkloadProfile>,
2284}
2285
2286#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2287#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
2288#[serde(rename_all = "camelCase")]
2289pub struct AzureContainerAppsEnvironmentHeartbeatStatus {
2290    pub health: ObservedHealth,
2291    pub lifecycle: ProviderLifecycleState,
2292    pub message: Option<String>,
2293    pub stale: bool,
2294    pub partial: bool,
2295    pub collection_issues: Vec<HeartbeatCollectionIssue>,
2296}
2297
2298#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
2299#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
2300#[serde(rename_all = "camelCase")]
2301pub struct AzureContainerAppsEnvironmentWorkloadProfile {
2302    pub name: String,
2303    pub workload_profile_type: String,
2304    pub minimum_count: Option<i32>,
2305    pub maximum_count: Option<i32>,
2306}
2307
2308#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2309#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
2310#[serde(rename_all = "camelCase")]
2311pub struct AzureServiceBusNamespaceHeartbeatData {
2312    pub status: QueueHeartbeatStatus,
2313    pub name: String,
2314    pub resource_id: Option<String>,
2315    pub resource_group: Option<String>,
2316    pub location: Option<String>,
2317    pub sku_name: Option<String>,
2318    pub sku_tier: Option<String>,
2319    pub sku_capacity: Option<i32>,
2320    pub namespace_status: Option<String>,
2321    pub provisioning_state: Option<String>,
2322    pub service_bus_endpoint: Option<String>,
2323    pub metric_id: Option<String>,
2324    pub public_network_access: Option<String>,
2325    pub disable_local_auth: Option<bool>,
2326    pub minimum_tls_version: Option<String>,
2327    pub premium_messaging_partitions: Option<i32>,
2328    pub private_endpoint_connection_count: u32,
2329    pub zone_redundant: Option<bool>,
2330    pub created_at: Option<String>,
2331    pub updated_at: Option<String>,
2332}
2333
2334#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
2335#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
2336#[serde(rename_all = "camelCase")]
2337pub struct NamedResourceDetail {
2338    pub name: String,
2339    pub region: Option<String>,
2340}
2341
2342#[cfg(test)]
2343mod tests {
2344    use super::*;
2345    use chrono::TimeZone as _;
2346    use serde_json::json;
2347
2348    fn observed_at() -> DateTime<Utc> {
2349        Utc.with_ymd_and_hms(2026, 5, 28, 10, 30, 0).unwrap()
2350    }
2351
2352    fn workload_status() -> WorkloadHeartbeatStatus {
2353        WorkloadHeartbeatStatus {
2354            health: ObservedHealth::Healthy,
2355            lifecycle: ProviderLifecycleState::Running,
2356            message: None,
2357            stale: false,
2358            partial: false,
2359            collection_issues: vec![],
2360        }
2361    }
2362
2363    fn workload_replicas() -> WorkloadReplicaStatus {
2364        WorkloadReplicaStatus {
2365            desired: Some(2),
2366            current: Some(2),
2367            ready: Some(1),
2368            available: Some(1),
2369            updated: Some(2),
2370            misscheduled: None,
2371        }
2372    }
2373
2374    fn heartbeat(data: ResourceHeartbeatData, resource_type: &str) -> ResourceHeartbeat {
2375        ResourceHeartbeat {
2376            deployment_id: Some("dep_123".to_string()),
2377            resource_id: "api".to_string(),
2378            resource_type: ResourceType::from(resource_type),
2379            controller_platform: Platform::Kubernetes,
2380            backend: HeartbeatBackend::Kubernetes,
2381            observed_at: observed_at(),
2382            data,
2383            raw: vec![RawHeartbeatSnippet {
2384                source: "kubernetes/apps/v1/deployments/api".to_string(),
2385                format: RawHeartbeatSnippetFormat::Json,
2386                collected_at: observed_at(),
2387                body: r#"{"readyReplicas":1}"#.to_string(),
2388                truncated: false,
2389            }],
2390        }
2391    }
2392
2393    #[test]
2394    fn resource_heartbeat_roundtrips_managed_resource_data() {
2395        let heartbeat_json = serde_json::to_value(heartbeat(
2396            ResourceHeartbeatData::Container(ContainerHeartbeatData::Kubernetes(
2397                KubernetesContainerHeartbeatData {
2398                    status: workload_status(),
2399                    namespace: "default".to_string(),
2400                    name: "api".to_string(),
2401                    workload_kind: KubernetesWorkloadKind::Deployment,
2402                    replicas: workload_replicas(),
2403                    restarts: None,
2404                    cpu: None,
2405                    memory: None,
2406                    workload: None,
2407                    pods: vec![],
2408                    events: vec![],
2409                },
2410            )),
2411            "container",
2412        ))
2413        .unwrap();
2414
2415        let parsed: ResourceHeartbeat = serde_json::from_value(heartbeat_json).unwrap();
2416        assert_eq!(parsed.resource_id, "api");
2417        assert!(matches!(
2418            parsed.data,
2419            ResourceHeartbeatData::Container(ContainerHeartbeatData::Kubernetes(_))
2420        ));
2421    }
2422
2423    #[test]
2424    fn local_daemon_heartbeat_defaults_missing_daemon_name() {
2425        let mut value =
2426            serde_json::to_value(DaemonHeartbeatData::Local(LocalDaemonHeartbeatData {
2427                status: workload_status(),
2428                daemon_name: "agent".to_string(),
2429                runtime_id: "local-agent".to_string(),
2430                pid: None,
2431                command_supported: false,
2432                image_path_present: true,
2433                restart_count: None,
2434                exit_reason: None,
2435                daemon_instance: None,
2436                events: vec![],
2437            }))
2438            .unwrap();
2439
2440        value.as_object_mut().unwrap().remove("daemonName");
2441
2442        let parsed: DaemonHeartbeatData = serde_json::from_value(value).unwrap();
2443        match parsed {
2444            DaemonHeartbeatData::Local(data) => assert_eq!(data.daemon_name, ""),
2445            other => panic!("expected local daemon heartbeat, got {other:?}"),
2446        }
2447    }
2448
2449    #[test]
2450    fn container_heartbeat_serializes_resource_first_data() {
2451        let heartbeat = heartbeat(
2452            ResourceHeartbeatData::Container(ContainerHeartbeatData::Kubernetes(
2453                KubernetesContainerHeartbeatData {
2454                    status: workload_status(),
2455                    namespace: "default".to_string(),
2456                    name: "api".to_string(),
2457                    workload_kind: KubernetesWorkloadKind::Deployment,
2458                    replicas: workload_replicas(),
2459                    restarts: Some(1),
2460                    cpu: Some(MetricSample {
2461                        value: 0.5,
2462                        unit: MetricUnit::Cores,
2463                    }),
2464                    memory: None,
2465                    workload: None,
2466                    pods: vec![],
2467                    events: vec![],
2468                },
2469            )),
2470            "container",
2471        );
2472
2473        let value = serde_json::to_value(&heartbeat).unwrap();
2474
2475        assert_eq!(value["resourceType"], "container");
2476        assert_eq!(value["data"]["resourceType"], "container");
2477        assert_eq!(value["data"]["data"]["backend"], "kubernetes");
2478        assert_eq!(value["raw"][0]["body"], r#"{"readyReplicas":1}"#);
2479        assert!(value.get("collection").is_none());
2480        assert!(value["data"]["data"].get("summary").is_none());
2481        assert!(value["data"]["data"].get("detail").is_none());
2482    }
2483
2484    #[test]
2485    fn representative_workload_data_has_stable_tags() {
2486        let daemon = serde_json::to_value(ResourceHeartbeatData::Daemon(
2487            DaemonHeartbeatData::Kubernetes(KubernetesDaemonHeartbeatData {
2488                status: workload_status(),
2489                namespace: "default".to_string(),
2490                name: "agent".to_string(),
2491                replicas: workload_replicas(),
2492                restarts: Some(0),
2493                command_supported: true,
2494                cpu: None,
2495                memory: None,
2496                workload: None,
2497                pods: vec![],
2498                events: vec![],
2499            }),
2500        ))
2501        .unwrap();
2502        let worker = serde_json::to_value(ResourceHeartbeatData::Worker(
2503            WorkerHeartbeatData::AwsLambda(AwsLambdaWorkerHeartbeatData {
2504                status: workload_status(),
2505                function_name: "handler".to_string(),
2506                runtime: Some("nodejs22.x".to_string()),
2507                package_type: None,
2508                memory_size_mb: None,
2509                timeout_seconds: None,
2510                version: None,
2511                revision_id: None,
2512                last_modified: None,
2513                state: None,
2514                state_reason: None,
2515                state_reason_code: None,
2516                last_update_status: None,
2517                last_update_status_reason: None,
2518                last_update_status_reason_code: None,
2519                code_sha256: None,
2520                layer_count: 0,
2521                function_url_auth_type: None,
2522                function_url_cors_present: false,
2523                trigger_count: 0,
2524            }),
2525        ))
2526        .unwrap();
2527
2528        assert_eq!(daemon["resourceType"], "daemon");
2529        assert_eq!(daemon["data"]["backend"], "kubernetes");
2530        assert_eq!(worker["resourceType"], "worker");
2531        assert_eq!(worker["data"]["backend"], "awsLambda");
2532    }
2533
2534    #[test]
2535    fn representative_cluster_and_data_variants_have_optional_counts() {
2536        let cluster = serde_json::to_value(ResourceHeartbeatData::KubernetesCluster(
2537            KubernetesClusterHeartbeatData {
2538                status: WorkloadHeartbeatStatus {
2539                    health: ObservedHealth::Healthy,
2540                    lifecycle: ProviderLifecycleState::Running,
2541                    message: None,
2542                    stale: false,
2543                    partial: false,
2544                    collection_issues: vec![],
2545                },
2546                node_counts: ObservedCounts {
2547                    desired: Some(3),
2548                    current: Some(3),
2549                    ready: None,
2550                },
2551                pod_counts: ObservedCounts {
2552                    desired: None,
2553                    current: Some(12),
2554                    ready: Some(11),
2555                },
2556                cpu: None,
2557                memory: None,
2558                name: "prod".to_string(),
2559                region: Some("us-east-1".to_string()),
2560                namespace: None,
2561                version: Some("1.33".to_string()),
2562                node_statuses: vec![],
2563                events: vec![],
2564            },
2565        ))
2566        .unwrap();
2567        let queue = serde_json::to_value(ResourceHeartbeatData::Queue(QueueHeartbeatData::AwsSqs(
2568            AwsSqsQueueHeartbeatData {
2569                status: QueueHeartbeatStatus {
2570                    health: ObservedHealth::Healthy,
2571                    lifecycle: ProviderLifecycleState::Running,
2572                    message: None,
2573                    stale: false,
2574                    partial: false,
2575                    collection_issues: vec![],
2576                },
2577                name: "jobs".to_string(),
2578                region: Some("us-east-1".to_string()),
2579                queue_url: Some("https://sqs.us-east-1.amazonaws.com/123/jobs".to_string()),
2580                queue_arn: Some("arn:aws:sqs:us-east-1:123:jobs".to_string()),
2581                visibility_timeout_seconds: Some(30),
2582                message_retention_period_seconds: Some(345600),
2583                delay_seconds: Some(0),
2584                receive_message_wait_time_seconds: Some(0),
2585                maximum_message_size: Some(262144),
2586                redrive_policy: None,
2587                redrive_allow_policy: None,
2588                fifo_queue: Some(false),
2589                content_based_deduplication: None,
2590                deduplication_scope: None,
2591                fifo_throughput_limit: None,
2592                sse_enabled: Some(false),
2593                kms_master_key_id: None,
2594                kms_data_key_reuse_period_seconds: None,
2595                sqs_managed_sse_enabled: Some(false),
2596                approximate_visible_messages: Some(42),
2597                approximate_in_flight_messages: Some(1),
2598                approximate_delayed_messages: Some(0),
2599                approximate_counts: true,
2600            },
2601        )))
2602        .unwrap();
2603        let storage = serde_json::to_value(ResourceHeartbeatData::Storage(
2604            StorageHeartbeatData::AwsS3(AwsS3StorageHeartbeatData {
2605                status: StorageHeartbeatStatus {
2606                    health: ObservedHealth::Healthy,
2607                    lifecycle: ProviderLifecycleState::Running,
2608                    message: None,
2609                    stale: false,
2610                    partial: false,
2611                    collection_issues: vec![],
2612                },
2613                name: "assets".to_string(),
2614                region: Some("us-east-1".to_string()),
2615                bucket_location: Some("us-east-1".to_string()),
2616                versioning_status: Some("Enabled".to_string()),
2617                versioning_enabled: Some(true),
2618                lifecycle_present: false,
2619                lifecycle_rule_count: Some(0),
2620                encryption_config_present: true,
2621                encryption_enabled: Some(true),
2622                public_access_block_present: true,
2623                block_public_acls: Some(true),
2624                ignore_public_acls: Some(true),
2625                block_public_policy: Some(true),
2626                restrict_public_buckets: Some(true),
2627                bucket_policy_present: Some(false),
2628                bucket_acl_present: Some(true),
2629            }),
2630        ))
2631        .unwrap();
2632        let gcp_storage = serde_json::to_value(ResourceHeartbeatData::Storage(
2633            StorageHeartbeatData::GcpCloudStorage(GcpCloudStorageHeartbeatData {
2634                status: StorageHeartbeatStatus {
2635                    health: ObservedHealth::Healthy,
2636                    lifecycle: ProviderLifecycleState::Running,
2637                    message: None,
2638                    stale: false,
2639                    partial: false,
2640                    collection_issues: vec![],
2641                },
2642                name: "assets".to_string(),
2643                bucket_id: Some("project/assets".to_string()),
2644                location: Some("US".to_string()),
2645                location_type: Some("multi-region".to_string()),
2646                storage_class: Some("STANDARD".to_string()),
2647                versioning_enabled: Some(true),
2648                lifecycle_present: false,
2649                lifecycle_rule_count: Some(0),
2650                retention_policy_effective_time: None,
2651                retention_policy_is_locked: None,
2652                retention_period: None,
2653                soft_delete_retention_duration_seconds: None,
2654                soft_delete_effective_time: None,
2655                uniform_bucket_level_access_enabled: Some(true),
2656                uniform_bucket_level_access_locked_time: None,
2657                public_access_prevention: Some("enforced".to_string()),
2658                encryption_config_present: true,
2659                default_kms_key_name: Some(
2660                    "projects/p/locations/l/keyRings/r/cryptoKeys/k".to_string(),
2661                ),
2662            }),
2663        ))
2664        .unwrap();
2665        let kv = serde_json::to_value(ResourceHeartbeatData::Kv(KvHeartbeatData::AwsDynamoDb(
2666            AwsDynamoDbKvHeartbeatData {
2667                status: KvHeartbeatStatus {
2668                    health: ObservedHealth::Healthy,
2669                    lifecycle: ProviderLifecycleState::Running,
2670                    message: None,
2671                    stale: false,
2672                    partial: false,
2673                    collection_issues: vec![],
2674                },
2675                name: "state".to_string(),
2676                region: Some("us-east-1".to_string()),
2677                table_arn: None,
2678                table_status: Some("ACTIVE".to_string()),
2679                billing_mode: Some("PAY_PER_REQUEST".to_string()),
2680                key_schema: vec![AwsDynamoDbKeySchemaElement {
2681                    attribute_name: "pk".to_string(),
2682                    key_type: "HASH".to_string(),
2683                }],
2684                global_secondary_index_count: Some(0),
2685                local_secondary_index_count: Some(0),
2686                item_count: None,
2687                table_size_bytes: None,
2688                stream_enabled: Some(false),
2689                stream_view_type: None,
2690                ttl_status: Some("ENABLED".to_string()),
2691                ttl_attribute_name: Some("ttl".to_string()),
2692                deletion_protection_enabled: Some(false),
2693                sse_status: Some("ENABLED".to_string()),
2694                sse_type: Some("KMS".to_string()),
2695                table_class: None,
2696                replica_count: Some(0),
2697                restore_in_progress: None,
2698            },
2699        )))
2700        .unwrap();
2701
2702        assert_eq!(cluster["resourceType"], "kubernetes-cluster");
2703        assert!(cluster["data"].get("summary").is_none());
2704        assert!(cluster["data"].get("detail").is_none());
2705        assert_eq!(cluster["data"]["name"], "prod");
2706        assert_eq!(queue["data"]["backend"], "awsSqs");
2707        assert_eq!(queue["data"]["approximateVisibleMessages"], 42);
2708        assert!(queue["data"].get("summary").is_none());
2709        assert_eq!(storage["data"]["backend"], "awsS3");
2710        assert!(storage["data"].get("summary").is_none());
2711        assert_eq!(gcp_storage["data"]["backend"], "gcpCloudStorage");
2712        assert_eq!(gcp_storage["data"]["publicAccessPrevention"], "enforced");
2713        assert_eq!(kv["data"]["backend"], "awsDynamoDb");
2714        assert!(kv["data"].get("summary").is_none());
2715        assert_eq!(kv["data"]["itemCount"], json!(null));
2716    }
2717}