Skip to main content

alien_core/
heartbeat.rs

1use std::collections::BTreeMap;
2
3use crate::{ai_catalog::ClientApi, 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    /// Distinct container images the resource's running instances report, when
89    /// the provider exposes them.
90    #[serde(default, skip_serializing_if = "Vec::is_empty")]
91    pub images: Vec<ContainerImageIdentity>,
92}
93
94/// Image a running container reports.
95#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
96#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
97#[serde(rename_all = "camelCase")]
98pub struct ContainerImageIdentity {
99    /// Container name.
100    pub name: String,
101    /// Image reference reported by the container runtime.
102    pub image: String,
103    /// Registry manifest digest in `sha256:<hex>` form, when the runtime
104    /// reports one.
105    #[serde(default, skip_serializing_if = "Option::is_none")]
106    pub digest: Option<String>,
107}
108
109#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
110#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
111#[serde(rename_all = "camelCase")]
112pub enum HeartbeatBackend {
113    Aws,
114    Gcp,
115    Azure,
116    Kubernetes,
117    Local,
118    Managed,
119    External,
120    Test,
121}
122
123#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
124#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
125#[serde(rename_all = "kebab-case")]
126pub enum HeartbeatCollectionIssueReason {
127    Forbidden,
128    NotInstalled,
129    ApiUnavailable,
130    CollectionFailed,
131    TimedOut,
132}
133
134#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
135#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
136#[serde(tag = "resourceType", content = "data")]
137pub enum ResourceHeartbeatData {
138    #[serde(rename = "storage")]
139    Storage(StorageHeartbeatData),
140    #[serde(rename = "worker")]
141    Worker(WorkerHeartbeatData),
142    #[serde(rename = "container")]
143    Container(ContainerHeartbeatData),
144    #[serde(rename = "daemon")]
145    Daemon(DaemonHeartbeatData),
146    #[serde(rename = "compute-cluster")]
147    ComputeCluster(ComputeClusterHeartbeatData),
148    #[serde(rename = "kubernetes-cluster")]
149    KubernetesCluster(KubernetesClusterHeartbeatData),
150    #[serde(rename = "queue")]
151    Queue(QueueHeartbeatData),
152    #[serde(rename = "kv")]
153    Kv(KvHeartbeatData),
154    #[serde(rename = "postgres")]
155    Postgres(PostgresHeartbeatData),
156    #[serde(rename = "redis")]
157    Redis(RedisHeartbeatData),
158    #[serde(rename = "vault")]
159    Vault(VaultHeartbeatData),
160    #[serde(rename = "service-account")]
161    ServiceAccount(ServiceAccountHeartbeatData),
162    #[serde(rename = "network")]
163    Network(NetworkHeartbeatData),
164    #[serde(rename = "remote-stack-management")]
165    RemoteStackManagement(RemoteStackManagementHeartbeatData),
166    #[serde(rename = "artifact-registry")]
167    ArtifactRegistry(ArtifactRegistryHeartbeatData),
168    #[serde(rename = "build")]
169    Build(BuildHeartbeatData),
170    #[serde(rename = "service_activation")]
171    ServiceActivation(ServiceActivationHeartbeatData),
172    #[serde(rename = "azure_resource_group")]
173    AzureResourceGroup(AzureResourceGroupHeartbeatData),
174    #[serde(rename = "azure_storage_account")]
175    AzureStorageAccount(AzureStorageAccountHeartbeatData),
176    #[serde(rename = "azure_container_apps_environment")]
177    AzureContainerAppsEnvironment(AzureContainerAppsEnvironmentHeartbeatData),
178    #[serde(rename = "azure_service_bus_namespace")]
179    AzureServiceBusNamespace(AzureServiceBusNamespaceHeartbeatData),
180    #[serde(rename = "ai")]
181    Ai(AiHeartbeatData),
182    #[serde(rename = "key")]
183    Key(KeyHeartbeatData),
184    #[serde(rename = "sandbox")]
185    Sandbox(SandboxHeartbeatData),
186}
187
188#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
189#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
190#[serde(rename_all = "kebab-case")]
191pub enum ObservedHealth {
192    Unknown,
193    Healthy,
194    Degraded,
195    Unhealthy,
196}
197
198#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
199#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
200#[serde(rename_all = "kebab-case")]
201pub enum ProviderLifecycleState {
202    Unknown,
203    Creating,
204    Updating,
205    Running,
206    Scaling,
207    Stopping,
208    Stopped,
209    Deleting,
210    Deleted,
211    Failed,
212}
213
214#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
215#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
216#[serde(rename_all = "camelCase")]
217pub struct KeyHeartbeatStatus {
218    pub health: ObservedHealth,
219    pub lifecycle: ProviderLifecycleState,
220    pub message: Option<String>,
221}
222
223#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
224#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
225#[serde(tag = "provider", content = "data", rename_all = "kebab-case")]
226pub enum KeyHeartbeatData {
227    AwsKms(AwsKmsKeyHeartbeatData),
228    GcpCloudKms(GcpCloudKmsKeyHeartbeatData),
229    AzureKeyVault(AzureKeyVaultKeyHeartbeatData),
230}
231
232#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
233#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
234#[serde(rename_all = "camelCase")]
235pub struct AwsKmsKeyHeartbeatData {
236    pub status: KeyHeartbeatStatus,
237    pub key_arn: String,
238    pub key_state: String,
239    pub enabled: bool,
240    pub key_spec: String,
241    pub key_usage: 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 GcpCloudKmsKeyHeartbeatData {
248    pub status: KeyHeartbeatStatus,
249    pub crypto_key_name: String,
250    pub purpose: String,
251    pub primary_version: Option<String>,
252    pub primary_state: Option<String>,
253    pub algorithm: Option<String>,
254}
255
256#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
257#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
258#[serde(rename_all = "camelCase")]
259pub struct AzureKeyVaultKeyHeartbeatData {
260    pub status: KeyHeartbeatStatus,
261    pub key_id: String,
262    pub enabled: Option<bool>,
263    pub key_type: String,
264    pub key_operations: Vec<String>,
265    pub recovery_level: Option<String>,
266}
267
268#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
269#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
270#[serde(rename_all = "camelCase")]
271pub struct HeartbeatCollectionIssue {
272    pub source: String,
273    pub reason: HeartbeatCollectionIssueReason,
274    pub severity: HeartbeatIssueSeverity,
275    pub message: String,
276}
277
278#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
279#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
280#[serde(rename_all = "kebab-case")]
281pub enum HeartbeatIssueSeverity {
282    Info,
283    Warning,
284    Error,
285}
286
287#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
288#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
289#[serde(rename_all = "camelCase")]
290pub struct KubernetesEventSnapshot {
291    pub reason: String,
292    #[serde(rename = "type")]
293    pub type_: Option<String>,
294    pub message: String,
295    pub count: Option<i32>,
296    pub first_timestamp: Option<DateTime<Utc>>,
297    pub last_timestamp: Option<DateTime<Utc>>,
298    pub event_time: Option<DateTime<Utc>>,
299    pub source: Option<KubernetesEventSource>,
300    pub involved_object: Option<KubernetesEventInvolvedObject>,
301    pub raw: Option<JsonValue>,
302}
303
304#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
305#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
306#[serde(rename_all = "camelCase")]
307pub struct KubernetesEventSource {
308    pub component: Option<String>,
309    pub host: Option<String>,
310}
311
312#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
313#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
314#[serde(rename_all = "camelCase")]
315pub struct KubernetesEventInvolvedObject {
316    pub kind: Option<String>,
317    pub namespace: Option<String>,
318    pub name: Option<String>,
319    pub uid: Option<String>,
320    pub api_version: Option<String>,
321    pub resource_version: Option<String>,
322    pub field_path: Option<String>,
323}
324
325#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
326#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
327#[serde(rename_all = "camelCase")]
328pub struct ManagedRuntimeEventSnapshot {
329    pub event_id: Option<String>,
330    pub reason: String,
331    #[serde(rename = "type")]
332    pub type_: Option<String>,
333    pub message: String,
334    pub count: Option<i32>,
335    pub first_timestamp: Option<DateTime<Utc>>,
336    pub last_timestamp: Option<DateTime<Utc>>,
337    pub event_time: Option<DateTime<Utc>>,
338    pub source: Option<ManagedRuntimeEventSource>,
339    pub involved_object: Option<ManagedRuntimeEventInvolvedObject>,
340    pub details: Option<JsonValue>,
341    pub raw: Option<JsonValue>,
342}
343
344#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
345#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
346#[serde(rename_all = "camelCase")]
347pub struct ManagedRuntimeEventSource {
348    pub component: Option<String>,
349    pub host: Option<String>,
350}
351
352#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
353#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
354#[serde(rename_all = "camelCase")]
355pub struct ManagedRuntimeEventInvolvedObject {
356    pub kind: Option<String>,
357    pub id: Option<String>,
358    pub name: Option<String>,
359    pub replica_id: Option<String>,
360    pub machine_id: Option<String>,
361    pub details: Option<JsonValue>,
362}
363
364#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
365#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
366#[serde(rename_all = "camelCase")]
367pub struct LocalRuntimeEventSnapshot {
368    pub timestamp: DateTime<Utc>,
369    pub severity: HeartbeatIssueSeverity,
370    pub kind: String,
371    pub message: String,
372    pub subject: Option<LocalRuntimeEventSubject>,
373    pub raw: Option<JsonValue>,
374}
375
376#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
377#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
378#[serde(rename_all = "camelCase")]
379pub struct LocalRuntimeEventSubject {
380    pub kind: String,
381    pub id: Option<String>,
382    pub name: Option<String>,
383}
384
385#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
386#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
387#[serde(rename_all = "camelCase")]
388pub struct RawHeartbeatSnippet {
389    pub source: String,
390    pub format: RawHeartbeatSnippetFormat,
391    pub collected_at: DateTime<Utc>,
392    pub body: String,
393    pub truncated: bool,
394}
395
396#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
397#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
398#[serde(rename_all = "kebab-case")]
399pub enum RawHeartbeatSnippetFormat {
400    Json,
401    Yaml,
402    Text,
403}
404
405#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
406#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
407#[serde(rename_all = "camelCase")]
408pub struct ObservedCounts {
409    pub desired: Option<u32>,
410    pub current: Option<u32>,
411    pub ready: Option<u32>,
412}
413
414#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
415#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
416#[serde(rename_all = "camelCase")]
417pub struct MetricSample {
418    pub value: f64,
419    pub unit: MetricUnit,
420}
421
422#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
423#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
424#[serde(rename_all = "kebab-case")]
425pub enum MetricUnit {
426    Count,
427    Percent,
428    Bytes,
429    Cores,
430    Milliseconds,
431    RequestsPerSecond,
432}
433
434#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
435#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
436#[serde(tag = "backend", rename_all = "camelCase")]
437pub enum StorageHeartbeatData {
438    AwsS3(AwsS3StorageHeartbeatData),
439    GcpCloudStorage(GcpCloudStorageHeartbeatData),
440    AzureBlob(AzureBlobStorageHeartbeatData),
441    Local(LocalStorageHeartbeatData),
442}
443
444#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
445#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
446#[serde(rename_all = "camelCase")]
447pub struct StorageHeartbeatStatus {
448    pub health: ObservedHealth,
449    pub lifecycle: ProviderLifecycleState,
450    pub message: Option<String>,
451    pub stale: bool,
452    pub partial: bool,
453    pub collection_issues: Vec<HeartbeatCollectionIssue>,
454}
455
456#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
457#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
458#[serde(rename_all = "camelCase")]
459pub struct AwsS3StorageHeartbeatData {
460    pub status: StorageHeartbeatStatus,
461    pub name: String,
462    pub region: Option<String>,
463    pub bucket_location: Option<String>,
464    pub versioning_status: Option<String>,
465    pub versioning_enabled: Option<bool>,
466    pub lifecycle_present: bool,
467    pub lifecycle_rule_count: Option<u64>,
468    pub encryption_config_present: bool,
469    pub encryption_enabled: Option<bool>,
470    pub public_access_block_present: bool,
471    pub block_public_acls: Option<bool>,
472    pub ignore_public_acls: Option<bool>,
473    pub block_public_policy: Option<bool>,
474    pub restrict_public_buckets: Option<bool>,
475    pub bucket_policy_present: Option<bool>,
476    pub bucket_acl_present: Option<bool>,
477}
478
479#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
480#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
481#[serde(rename_all = "camelCase")]
482pub struct AzureBlobStorageHeartbeatData {
483    pub status: StorageHeartbeatStatus,
484    pub name: String,
485    pub storage_account_name: Option<String>,
486    pub resource_group: Option<String>,
487    pub location: Option<String>,
488    pub account_kind: Option<String>,
489    pub sku_name: Option<String>,
490    pub sku_tier: Option<String>,
491    pub access_tier: Option<String>,
492    pub provisioning_state: Option<String>,
493    pub primary_location: Option<String>,
494    pub secondary_location: Option<String>,
495    pub status_of_primary: Option<String>,
496    pub status_of_secondary: Option<String>,
497    pub public_network_access: Option<String>,
498    pub allow_blob_public_access: Option<bool>,
499    pub encryption_key_source: Option<String>,
500    pub blob_encryption_enabled: Option<bool>,
501    pub file_encryption_enabled: Option<bool>,
502    pub queue_encryption_enabled: Option<bool>,
503    pub table_encryption_enabled: Option<bool>,
504    pub blob_versioning_enabled: Option<bool>,
505    pub blob_delete_retention_enabled: Option<bool>,
506    pub blob_delete_retention_days: Option<u64>,
507    pub container_delete_retention_enabled: Option<bool>,
508    pub container_delete_retention_days: Option<u64>,
509    pub change_feed_enabled: Option<bool>,
510    pub change_feed_retention_days: Option<u64>,
511    pub container_public_access: Option<String>,
512}
513
514#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
515#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
516#[serde(rename_all = "camelCase")]
517pub struct GcpCloudStorageHeartbeatData {
518    pub status: StorageHeartbeatStatus,
519    pub name: String,
520    pub bucket_id: Option<String>,
521    pub location: Option<String>,
522    pub location_type: Option<String>,
523    pub storage_class: Option<String>,
524    pub versioning_enabled: Option<bool>,
525    pub lifecycle_present: bool,
526    pub lifecycle_rule_count: Option<u64>,
527    pub retention_policy_effective_time: Option<String>,
528    pub retention_policy_is_locked: Option<bool>,
529    pub retention_period: Option<String>,
530    pub soft_delete_retention_duration_seconds: Option<String>,
531    pub soft_delete_effective_time: Option<String>,
532    pub uniform_bucket_level_access_enabled: Option<bool>,
533    pub uniform_bucket_level_access_locked_time: Option<String>,
534    pub public_access_prevention: Option<String>,
535    pub encryption_config_present: bool,
536    pub default_kms_key_name: Option<String>,
537}
538
539#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
540#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
541#[serde(rename_all = "camelCase")]
542pub struct LocalStorageHeartbeatData {
543    pub status: StorageHeartbeatStatus,
544    pub path: String,
545    pub path_exists: bool,
546    pub is_directory: Option<bool>,
547    pub readonly: Option<bool>,
548    pub modified_at: Option<DateTime<Utc>>,
549}
550
551#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
552#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
553#[serde(tag = "backend", rename_all = "camelCase")]
554pub enum WorkerHeartbeatData {
555    AwsLambda(AwsLambdaWorkerHeartbeatData),
556    GcpCloudRun(GcpCloudRunWorkerHeartbeatData),
557    AzureContainerApps(AzureContainerAppsWorkerHeartbeatData),
558    Kubernetes(KubernetesWorkerHeartbeatData),
559    Local(LocalWorkerHeartbeatData),
560}
561
562#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
563#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
564#[serde(tag = "backend", rename_all = "camelCase")]
565pub enum ContainerHeartbeatData {
566    HorizonPlatform(HorizonContainerHeartbeatData),
567    Kubernetes(KubernetesContainerHeartbeatData),
568    Local(LocalContainerHeartbeatData),
569}
570
571#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
572#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
573#[serde(tag = "backend", rename_all = "camelCase")]
574pub enum DaemonHeartbeatData {
575    Aws(AwsDaemonHeartbeatData),
576    Gcp(GcpDaemonHeartbeatData),
577    Azure(AzureDaemonHeartbeatData),
578    Machines(MachinesDaemonHeartbeatData),
579    Kubernetes(KubernetesDaemonHeartbeatData),
580    Local(LocalDaemonHeartbeatData),
581}
582
583#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
584#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
585#[serde(rename_all = "camelCase")]
586pub struct WorkloadHeartbeatStatus {
587    pub health: ObservedHealth,
588    pub lifecycle: ProviderLifecycleState,
589    pub message: Option<String>,
590    pub stale: bool,
591    pub partial: bool,
592    pub collection_issues: Vec<HeartbeatCollectionIssue>,
593}
594
595#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
596#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
597#[serde(rename_all = "camelCase")]
598pub struct WorkloadReplicaStatus {
599    pub desired: Option<u32>,
600    pub current: Option<u32>,
601    pub ready: Option<u32>,
602    pub available: Option<u32>,
603    pub updated: Option<u32>,
604    pub misscheduled: Option<u32>,
605}
606
607#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
608#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
609#[serde(rename_all = "camelCase")]
610pub struct AwsLambdaWorkerHeartbeatData {
611    pub status: WorkloadHeartbeatStatus,
612    pub function_name: String,
613    pub runtime: Option<String>,
614    pub package_type: Option<String>,
615    pub memory_size_mb: Option<i64>,
616    pub timeout_seconds: Option<i64>,
617    pub version: Option<String>,
618    pub revision_id: Option<String>,
619    pub last_modified: Option<String>,
620    pub state: Option<String>,
621    pub state_reason: Option<String>,
622    pub state_reason_code: Option<String>,
623    pub last_update_status: Option<String>,
624    pub last_update_status_reason: Option<String>,
625    pub last_update_status_reason_code: Option<String>,
626    pub code_sha256: Option<String>,
627    pub layer_count: u32,
628    pub function_url_auth_type: Option<String>,
629    pub function_url_cors_present: bool,
630    pub trigger_count: u32,
631}
632
633#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
634#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
635#[serde(rename_all = "camelCase")]
636pub struct GcpCloudRunWorkerHeartbeatData {
637    pub status: WorkloadHeartbeatStatus,
638    pub service: String,
639    pub region: Option<String>,
640    pub uri: Option<String>,
641    pub urls: Vec<String>,
642    pub latest_created_revision: Option<String>,
643    pub latest_ready_revision: Option<String>,
644    pub generation: Option<i64>,
645    pub observed_generation: Option<i64>,
646    pub traffic_count: u32,
647    pub min_instance_count: Option<i32>,
648    pub max_instance_count: Option<i32>,
649    pub container_image: Option<String>,
650    pub cpu_limit: Option<String>,
651    pub memory_limit: Option<String>,
652}
653
654#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
655#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
656#[serde(rename_all = "camelCase")]
657pub struct AzureContainerAppsWorkerHeartbeatData {
658    pub status: WorkloadHeartbeatStatus,
659    pub app_name: String,
660    pub revision: Option<String>,
661    pub environment_name: Option<String>,
662    pub provisioning_state: Option<String>,
663    pub running_status: Option<String>,
664    pub ingress_fqdn: Option<String>,
665    pub min_replicas: Option<i32>,
666    pub max_replicas: Option<i32>,
667    pub cpu: Option<f64>,
668    pub memory: Option<String>,
669}
670
671#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
672#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
673#[serde(rename_all = "camelCase")]
674pub struct KubernetesWorkerHeartbeatData {
675    pub status: WorkloadHeartbeatStatus,
676    pub namespace: String,
677    pub name: String,
678    pub workload_kind: KubernetesWorkloadKind,
679    pub replicas: WorkloadReplicaStatus,
680    pub restarts: Option<u32>,
681    pub cpu: Option<MetricSample>,
682    pub memory: Option<MetricSample>,
683    pub workload: Option<KubernetesWorkloadStatus>,
684    pub pods: Vec<KubernetesPodRuntimeUnitStatus>,
685    pub trigger_count: u32,
686    pub events: Vec<KubernetesEventSnapshot>,
687}
688
689#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
690#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
691#[serde(rename_all = "camelCase")]
692pub struct LocalWorkerHeartbeatData {
693    pub status: WorkloadHeartbeatStatus,
694    pub pid: Option<u32>,
695    pub command_supported: bool,
696    pub image_path_present: bool,
697    pub readiness_probe_ok: Option<bool>,
698    pub trigger_count: u32,
699    pub cpu: Option<MetricSample>,
700    pub memory: Option<MetricSample>,
701    pub process: Option<LocalRuntimeUnitStatus>,
702    pub events: Vec<LocalRuntimeEventSnapshot>,
703}
704
705#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
706#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
707#[serde(rename_all = "camelCase")]
708pub struct HorizonContainerHeartbeatData {
709    pub status: WorkloadHeartbeatStatus,
710    pub container_id: String,
711    pub image: Option<String>,
712    #[serde(default)]
713    pub observed_image: Option<String>,
714    #[serde(default)]
715    pub latest_update_timestamp: Option<String>,
716    pub scheduling_mode: HorizonWorkloadSchedulingMode,
717    pub replicas: WorkloadReplicaStatus,
718    pub cpu: Option<MetricSample>,
719    pub memory: Option<MetricSample>,
720    pub attention_count: u32,
721    pub replica_units: Vec<ManagedRuntimeUnitStatus>,
722    pub events: Vec<ManagedRuntimeEventSnapshot>,
723}
724
725#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
726#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
727#[serde(rename_all = "camelCase")]
728pub enum HorizonWorkloadSchedulingMode {
729    Replicated,
730    Stateful,
731    Daemon,
732}
733
734#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
735#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
736#[serde(rename_all = "camelCase")]
737pub struct KubernetesContainerHeartbeatData {
738    pub status: WorkloadHeartbeatStatus,
739    pub namespace: String,
740    pub name: String,
741    pub workload_kind: KubernetesWorkloadKind,
742    pub replicas: WorkloadReplicaStatus,
743    pub restarts: Option<u32>,
744    pub cpu: Option<MetricSample>,
745    pub memory: Option<MetricSample>,
746    pub workload: Option<KubernetesWorkloadStatus>,
747    pub pods: Vec<KubernetesPodRuntimeUnitStatus>,
748    pub events: Vec<KubernetesEventSnapshot>,
749}
750
751#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
752#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
753#[serde(rename_all = "camelCase")]
754pub struct LocalContainerHeartbeatData {
755    pub status: WorkloadHeartbeatStatus,
756    pub container_id: Option<String>,
757    pub name: Option<String>,
758    pub image: Option<String>,
759    pub runtime_status: Option<String>,
760    pub restart_count: Option<u32>,
761    pub port_count: u32,
762    pub bind_mount_count: u32,
763    pub local_url: Option<String>,
764    pub runtime_reachable: bool,
765    pub cpu: Option<MetricSample>,
766    pub memory: Option<MetricSample>,
767    pub container_unit: Option<LocalRuntimeUnitStatus>,
768    pub events: Vec<LocalRuntimeEventSnapshot>,
769}
770
771#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
772#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
773#[serde(rename_all = "camelCase")]
774pub struct AwsDaemonHeartbeatData {
775    pub status: WorkloadHeartbeatStatus,
776    pub horizon_cluster_id: String,
777    #[serde(default)]
778    pub daemon_name: String,
779    pub horizon_status: String,
780    pub horizon_status_reason: Option<String>,
781    pub horizon_status_message: Option<String>,
782    pub capacity_group: String,
783    pub desired_machines: u32,
784    pub assigned_machines: u32,
785    pub healthy_instances: u32,
786    pub unavailable_instances: u32,
787    pub command_supported: bool,
788    #[serde(default)]
789    pub observed_image: Option<String>,
790    pub latest_update_timestamp: String,
791    pub daemon_instances: Vec<ManagedRuntimeUnitStatus>,
792    pub events: Vec<ManagedRuntimeEventSnapshot>,
793}
794
795#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
796#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
797#[serde(rename_all = "camelCase")]
798pub struct GcpDaemonHeartbeatData {
799    pub status: WorkloadHeartbeatStatus,
800    pub horizon_cluster_id: String,
801    #[serde(default)]
802    pub daemon_name: String,
803    pub horizon_status: String,
804    pub horizon_status_reason: Option<String>,
805    pub horizon_status_message: Option<String>,
806    pub capacity_group: String,
807    pub desired_machines: u32,
808    pub assigned_machines: u32,
809    pub healthy_instances: u32,
810    pub unavailable_instances: u32,
811    pub command_supported: bool,
812    #[serde(default)]
813    pub observed_image: Option<String>,
814    pub latest_update_timestamp: String,
815    pub daemon_instances: Vec<ManagedRuntimeUnitStatus>,
816    pub events: Vec<ManagedRuntimeEventSnapshot>,
817}
818
819#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
820#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
821#[serde(rename_all = "camelCase")]
822pub struct AzureDaemonHeartbeatData {
823    pub status: WorkloadHeartbeatStatus,
824    pub horizon_cluster_id: String,
825    #[serde(default)]
826    pub daemon_name: String,
827    pub horizon_status: String,
828    pub horizon_status_reason: Option<String>,
829    pub horizon_status_message: Option<String>,
830    pub capacity_group: String,
831    pub desired_machines: u32,
832    pub assigned_machines: u32,
833    pub healthy_instances: u32,
834    pub unavailable_instances: u32,
835    pub command_supported: bool,
836    #[serde(default)]
837    pub observed_image: Option<String>,
838    pub latest_update_timestamp: String,
839    pub daemon_instances: Vec<ManagedRuntimeUnitStatus>,
840    pub events: Vec<ManagedRuntimeEventSnapshot>,
841}
842
843#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
844#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
845#[serde(rename_all = "camelCase")]
846pub struct MachinesDaemonHeartbeatData {
847    pub status: WorkloadHeartbeatStatus,
848    pub horizon_cluster_id: String,
849    #[serde(default)]
850    pub daemon_name: String,
851    pub horizon_status: String,
852    pub horizon_status_reason: Option<String>,
853    pub horizon_status_message: Option<String>,
854    pub capacity_group: String,
855    pub desired_machines: u32,
856    pub assigned_machines: u32,
857    pub healthy_instances: u32,
858    pub unavailable_instances: u32,
859    pub command_supported: bool,
860    #[serde(default)]
861    pub observed_image: Option<String>,
862    pub latest_update_timestamp: String,
863    pub daemon_instances: Vec<ManagedRuntimeUnitStatus>,
864    pub events: Vec<ManagedRuntimeEventSnapshot>,
865}
866
867#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
868#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
869#[serde(rename_all = "camelCase")]
870pub struct ManagedRuntimeUnitStatus {
871    pub replica_id: String,
872    pub name: String,
873    pub machine_id: Option<String>,
874    pub node_name: Option<String>,
875    pub ip: Option<String>,
876    pub ready: bool,
877    pub phase: Option<String>,
878    pub status: Option<String>,
879    pub reason: Option<String>,
880    pub message: Option<String>,
881    pub restart_count: Option<u32>,
882    pub waiting_reason: Option<String>,
883    pub terminated_reason: Option<String>,
884    pub metrics_healthy: Option<bool>,
885    pub metrics_status: Option<String>,
886    pub metrics_last_updated: Option<String>,
887    pub cpu: Option<MetricSample>,
888    pub memory: Option<MetricSample>,
889    /// Image this replica runs.
890    #[serde(default)]
891    pub image: Option<String>,
892}
893
894#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
895#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
896#[serde(rename_all = "camelCase")]
897pub struct KubernetesDaemonHeartbeatData {
898    pub status: WorkloadHeartbeatStatus,
899    pub namespace: String,
900    pub name: String,
901    pub replicas: WorkloadReplicaStatus,
902    pub restarts: Option<u32>,
903    pub command_supported: bool,
904    pub cpu: Option<MetricSample>,
905    pub memory: Option<MetricSample>,
906    pub workload: Option<KubernetesWorkloadStatus>,
907    pub pods: Vec<KubernetesPodRuntimeUnitStatus>,
908    pub events: Vec<KubernetesEventSnapshot>,
909}
910
911#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
912#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
913#[serde(rename_all = "camelCase")]
914pub struct LocalDaemonHeartbeatData {
915    pub status: WorkloadHeartbeatStatus,
916    #[serde(default)]
917    pub daemon_name: String,
918    pub runtime_id: String,
919    pub pid: Option<u32>,
920    pub command_supported: bool,
921    pub image_path_present: bool,
922    pub restart_count: Option<u32>,
923    pub exit_reason: Option<String>,
924    pub daemon_instance: Option<LocalRuntimeUnitStatus>,
925    pub events: Vec<LocalRuntimeEventSnapshot>,
926}
927
928#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
929#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
930#[serde(rename_all = "camelCase")]
931pub struct LocalRuntimeUnitStatus {
932    pub unit_id: String,
933    pub name: String,
934    pub kind: LocalRuntimeUnitKind,
935    pub ready: bool,
936    pub phase: Option<String>,
937    pub pid: Option<u32>,
938    pub restart_count: Option<u32>,
939    pub cpu: Option<MetricSample>,
940    pub memory: Option<MetricSample>,
941}
942
943#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
944#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
945#[serde(rename_all = "kebab-case")]
946pub enum LocalRuntimeUnitKind {
947    Container,
948    Process,
949    Daemon,
950}
951
952#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
953#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
954#[serde(rename_all = "camelCase")]
955pub enum KubernetesWorkloadKind {
956    Deployment,
957    StatefulSet,
958    DaemonSet,
959    ReplicaSet,
960    Pod,
961}
962
963#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
964#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
965#[serde(rename_all = "camelCase")]
966pub struct KubernetesWorkloadStatus {
967    pub desired_replicas: Option<u32>,
968    pub ready_replicas: Option<u32>,
969    pub available_replicas: Option<u32>,
970    pub updated_replicas: Option<u32>,
971    pub observed_generation: Option<i64>,
972    pub desired_generation: Option<i64>,
973    pub rollout_reason: Option<String>,
974    pub conditions: Vec<KubernetesWorkloadCondition>,
975}
976
977#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
978#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
979#[serde(rename_all = "camelCase")]
980pub struct KubernetesWorkloadCondition {
981    #[serde(rename = "type")]
982    pub condition_type: String,
983    pub status: String,
984    pub reason: Option<String>,
985    pub message: Option<String>,
986    pub last_transition_time: Option<DateTime<Utc>>,
987}
988
989#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
990#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
991#[serde(rename_all = "camelCase")]
992pub struct KubernetesPodRuntimeUnitStatus {
993    pub name: String,
994    pub uid: Option<String>,
995    pub phase: Option<String>,
996    pub ready: bool,
997    pub restart_count: u32,
998    pub waiting_reason: Option<String>,
999    pub terminated_reason: Option<String>,
1000    pub node_name: Option<String>,
1001    pub pod_ip: Option<String>,
1002    pub owner_references: Vec<KubernetesOwnerReference>,
1003    pub cpu: Option<MetricSample>,
1004    pub memory: Option<MetricSample>,
1005    #[serde(default, skip_serializing_if = "Vec::is_empty")]
1006    pub containers: Vec<ContainerImageIdentity>,
1007}
1008
1009#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1010#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1011#[serde(rename_all = "camelCase")]
1012pub struct KubernetesOwnerReference {
1013    pub kind: String,
1014    pub name: String,
1015    pub uid: String,
1016    pub controller: bool,
1017}
1018
1019#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1020#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1021#[serde(tag = "backend", rename_all = "camelCase")]
1022pub enum ComputeClusterHeartbeatData {
1023    Aws(AwsComputeClusterHeartbeatData),
1024    Gcp(GcpComputeClusterHeartbeatData),
1025    Azure(AzureComputeClusterHeartbeatData),
1026    Machines(MachinesComputeClusterHeartbeatData),
1027    Local(LocalComputeClusterHeartbeatData),
1028}
1029
1030#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1031#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1032#[serde(rename_all = "camelCase")]
1033pub struct ComputeClusterHeartbeatStatus {
1034    pub health: ObservedHealth,
1035    pub lifecycle: ProviderLifecycleState,
1036    pub message: Option<String>,
1037    pub stale: bool,
1038    pub partial: bool,
1039    pub collection_issues: Vec<HeartbeatCollectionIssue>,
1040}
1041
1042#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1043#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1044#[serde(rename_all = "camelCase")]
1045pub struct LocalComputeClusterHeartbeatData {
1046    pub status: ComputeClusterHeartbeatStatus,
1047    pub nodes: ObservedCounts,
1048    pub name: String,
1049    pub host_identifier: Option<String>,
1050    pub docker_available: bool,
1051    pub docker_version: Option<String>,
1052    pub docker_api_version: Option<String>,
1053    pub docker_os: Option<String>,
1054    pub docker_arch: Option<String>,
1055    pub network_name: Option<String>,
1056    pub network_available: bool,
1057    pub tracked_containers: Option<u32>,
1058    pub running_containers: Option<u32>,
1059}
1060
1061#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1062#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1063#[serde(rename_all = "camelCase")]
1064pub struct AwsComputeClusterHeartbeatData {
1065    pub status: ComputeClusterHeartbeatStatus,
1066    pub nodes: ObservedCounts,
1067    pub cpu: Option<MetricSample>,
1068    pub memory: Option<MetricSample>,
1069    pub name: String,
1070    pub region: Option<String>,
1071    pub backend_cluster_id: Option<String>,
1072    pub capacity_groups: Vec<ComputeCapacityGroupStatus>,
1073    pub provider_fleets: Vec<ProviderFleetStatus>,
1074}
1075
1076#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1077#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1078#[serde(rename_all = "camelCase")]
1079pub struct GcpComputeClusterHeartbeatData {
1080    pub status: ComputeClusterHeartbeatStatus,
1081    pub nodes: ObservedCounts,
1082    pub cpu: Option<MetricSample>,
1083    pub memory: Option<MetricSample>,
1084    pub name: String,
1085    pub region: Option<String>,
1086    pub backend_cluster_id: Option<String>,
1087    pub capacity_groups: Vec<ComputeCapacityGroupStatus>,
1088    pub provider_fleets: Vec<ProviderFleetStatus>,
1089}
1090
1091#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1092#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1093#[serde(rename_all = "camelCase")]
1094pub struct AzureComputeClusterHeartbeatData {
1095    pub status: ComputeClusterHeartbeatStatus,
1096    pub nodes: ObservedCounts,
1097    pub cpu: Option<MetricSample>,
1098    pub memory: Option<MetricSample>,
1099    pub name: String,
1100    pub region: Option<String>,
1101    pub backend_cluster_id: Option<String>,
1102    pub capacity_groups: Vec<ComputeCapacityGroupStatus>,
1103    pub provider_fleets: Vec<ProviderFleetStatus>,
1104}
1105
1106#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1107#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1108#[serde(rename_all = "camelCase")]
1109pub struct MachinesComputeClusterHeartbeatData {
1110    pub status: ComputeClusterHeartbeatStatus,
1111    pub nodes: ObservedCounts,
1112    pub cpu: Option<MetricSample>,
1113    pub memory: Option<MetricSample>,
1114    pub name: String,
1115    pub backend_cluster_id: Option<String>,
1116    pub capacity_groups: Vec<ComputeCapacityGroupStatus>,
1117    pub machines: Vec<MachinesComputeMachineStatus>,
1118}
1119
1120#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1121#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1122#[serde(rename_all = "camelCase")]
1123pub struct MachinesComputeMachineStatus {
1124    pub machine_id: String,
1125    pub status: String,
1126    pub capacity_group: String,
1127    pub zone: String,
1128    pub public_ip: Option<String>,
1129    pub overlay_ip: Option<String>,
1130    pub last_heartbeat: String,
1131    pub horizond_version: Option<String>,
1132    pub replica_count: i64,
1133    pub cpu_cores: Option<f64>,
1134    pub memory_bytes: Option<i64>,
1135    pub drain_force: bool,
1136    pub drain_requested_at: Option<String>,
1137    pub drain_deadline_at: Option<String>,
1138    pub drained_at: Option<String>,
1139    #[serde(default, skip_serializing_if = "Vec::is_empty")]
1140    pub drain_blockers: Vec<ComputeDrainBlocker>,
1141}
1142
1143#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1144#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1145#[serde(rename_all = "camelCase")]
1146pub struct ComputeCapacityGroupStatus {
1147    pub group_id: String,
1148    pub current_machines: u32,
1149    pub desired_machines: u32,
1150    pub min_machines: Option<u32>,
1151    pub max_machines: Option<u32>,
1152    pub instance_type: Option<String>,
1153    pub recommendation: Option<ComputeCapacityRecommendation>,
1154    pub capacity_blocker: Option<ComputeCapacityBlocker>,
1155    #[serde(default, skip_serializing_if = "Option::is_none")]
1156    pub drain_progress: Option<ComputeDrainProgress>,
1157}
1158
1159#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
1160#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1161#[serde(rename_all = "camelCase")]
1162pub enum ComputeCapacityBlockerCategory {
1163    Quota,
1164    Capacity,
1165    Allocation,
1166    Other,
1167}
1168
1169#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1170#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1171#[serde(rename_all = "camelCase")]
1172pub struct ComputeCapacityBlocker {
1173    pub category: ComputeCapacityBlockerCategory,
1174    pub provider_code: Option<String>,
1175    pub message: String,
1176    pub provider_reference: Option<String>,
1177    pub observed_at: DateTime<Utc>,
1178}
1179
1180#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1181#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1182#[serde(rename_all = "camelCase")]
1183pub struct ComputeCapacityRecommendation {
1184    pub desired_machines: u32,
1185    pub reason: Option<String>,
1186    pub utilization: Option<MetricSample>,
1187    pub unschedulable_replicas: Option<u32>,
1188}
1189
1190#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
1191#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1192#[serde(rename_all = "camelCase")]
1193pub enum ComputeDrainProgressStatus {
1194    Draining,
1195    Drained,
1196    Terminating,
1197}
1198
1199#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1200#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1201#[serde(rename_all = "camelCase")]
1202pub struct ComputeDrainBlocker {
1203    pub workload_name: String,
1204    pub replica_id: String,
1205    pub scheduling_mode: String,
1206    pub state: String,
1207    pub reason: String,
1208}
1209
1210#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1211#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1212#[serde(rename_all = "camelCase")]
1213pub struct ComputeDrainProgress {
1214    pub machine_id: String,
1215    pub status: ComputeDrainProgressStatus,
1216    pub replica_count: i64,
1217    pub force: bool,
1218    pub stalled: bool,
1219    pub drain_requested_at: Option<String>,
1220    pub drain_deadline_at: Option<String>,
1221    pub drained_at: Option<String>,
1222    #[serde(default, skip_serializing_if = "Vec::is_empty")]
1223    pub blockers: Vec<ComputeDrainBlocker>,
1224}
1225
1226#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1227#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1228#[serde(rename_all = "camelCase")]
1229pub struct ProviderFleetStatus {
1230    pub group_id: String,
1231    pub provider_id: String,
1232    pub location: Option<String>,
1233    pub current_machines: u32,
1234    pub desired_machines: u32,
1235}
1236
1237#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1238#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1239#[serde(rename_all = "camelCase")]
1240pub struct KubernetesClusterHeartbeatData {
1241    pub status: WorkloadHeartbeatStatus,
1242    pub node_counts: ObservedCounts,
1243    pub pod_counts: ObservedCounts,
1244    pub cpu: Option<MetricSample>,
1245    pub memory: Option<MetricSample>,
1246    pub name: String,
1247    pub region: Option<String>,
1248    pub namespace: Option<String>,
1249    pub version: Option<String>,
1250    #[serde(default, skip_serializing_if = "Vec::is_empty")]
1251    pub node_statuses: Vec<KubernetesClusterNodeStatus>,
1252    pub events: Vec<KubernetesEventSnapshot>,
1253}
1254
1255#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1256#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1257#[serde(rename_all = "camelCase")]
1258pub struct KubernetesClusterNodeStatus {
1259    pub name: String,
1260    pub uid: Option<String>,
1261    pub ready: bool,
1262    #[serde(default, skip_serializing_if = "Vec::is_empty")]
1263    pub conditions: Vec<KubernetesNodeConditionStatus>,
1264    pub roles: Vec<String>,
1265    pub labels: BTreeMap<String, String>,
1266    pub allocatable: KubernetesNodeResources,
1267    pub capacity: KubernetesNodeResources,
1268    pub usage: Option<KubernetesNodeUsage>,
1269    pub kubelet_version: Option<String>,
1270    pub container_runtime_version: Option<String>,
1271}
1272
1273#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1274#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1275#[serde(rename_all = "camelCase")]
1276pub struct KubernetesNodeConditionStatus {
1277    pub type_: String,
1278    pub status: String,
1279    pub reason: Option<String>,
1280    pub message: Option<String>,
1281}
1282
1283#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
1284#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1285#[serde(rename_all = "camelCase")]
1286pub struct KubernetesNodeResources {
1287    pub cpu: Option<MetricSample>,
1288    pub memory: Option<MetricSample>,
1289    pub pods: Option<u64>,
1290}
1291
1292#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1293#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1294#[serde(rename_all = "camelCase")]
1295pub struct KubernetesNodeUsage {
1296    pub cpu: Option<MetricSample>,
1297    pub memory: Option<MetricSample>,
1298}
1299
1300#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1301#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1302#[serde(tag = "backend", rename_all = "camelCase")]
1303pub enum QueueHeartbeatData {
1304    AwsSqs(AwsSqsQueueHeartbeatData),
1305    GcpPubSub(GcpPubSubQueueHeartbeatData),
1306    AzureServiceBus(AzureServiceBusQueueHeartbeatData),
1307    Local(LocalQueueHeartbeatData),
1308}
1309
1310#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1311#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1312#[serde(rename_all = "camelCase")]
1313pub struct QueueHeartbeatStatus {
1314    pub health: ObservedHealth,
1315    pub lifecycle: ProviderLifecycleState,
1316    pub message: Option<String>,
1317    pub stale: bool,
1318    pub partial: bool,
1319    pub collection_issues: Vec<HeartbeatCollectionIssue>,
1320}
1321
1322impl Default for QueueHeartbeatStatus {
1323    fn default() -> Self {
1324        Self {
1325            health: ObservedHealth::Unknown,
1326            lifecycle: ProviderLifecycleState::Unknown,
1327            message: None,
1328            stale: false,
1329            partial: false,
1330            collection_issues: vec![],
1331        }
1332    }
1333}
1334
1335#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
1336#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1337#[serde(rename_all = "camelCase")]
1338pub struct LocalQueueHeartbeatData {
1339    pub status: QueueHeartbeatStatus,
1340    pub name: String,
1341    pub path: Option<String>,
1342    pub service_status: Option<String>,
1343}
1344
1345#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
1346#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1347#[serde(rename_all = "camelCase")]
1348pub struct GcpPubSubQueueHeartbeatData {
1349    pub status: QueueHeartbeatStatus,
1350    pub topic_name: String,
1351    pub subscription_name: Option<String>,
1352    pub project_id: Option<String>,
1353    pub topic_full_name: Option<String>,
1354    pub subscription_full_name: Option<String>,
1355    pub endpoint: Option<String>,
1356    pub topic_labels: BTreeMap<String, String>,
1357    pub subscription_labels: BTreeMap<String, String>,
1358    pub message_storage_allowed_persistence_regions: Vec<String>,
1359    pub message_storage_enforce_in_transit: Option<bool>,
1360    pub kms_key_name: Option<String>,
1361    pub schema_name: Option<String>,
1362    pub schema_encoding: Option<String>,
1363    pub schema_first_revision_id: Option<String>,
1364    pub schema_last_revision_id: Option<String>,
1365    pub topic_message_retention_duration: Option<String>,
1366    pub topic_state: Option<String>,
1367    pub subscription_ack_deadline_seconds: Option<u32>,
1368    pub subscription_message_retention_duration: Option<String>,
1369    pub subscription_retain_acked_messages: Option<bool>,
1370    pub subscription_enable_message_ordering: Option<bool>,
1371    pub subscription_filter: Option<String>,
1372    pub subscription_detached: Option<bool>,
1373    pub subscription_state: Option<String>,
1374    pub subscription_push_config_present: Option<bool>,
1375    pub subscription_push_endpoint: Option<String>,
1376    pub subscription_push_attributes: BTreeMap<String, String>,
1377    pub subscription_push_oidc_service_account_email: Option<String>,
1378    pub subscription_push_oidc_audience: Option<String>,
1379    pub subscription_push_pubsub_wrapper_write_metadata: Option<bool>,
1380    pub subscription_push_no_wrapper_write_metadata: Option<bool>,
1381    pub subscription_dead_letter_topic: Option<String>,
1382    pub subscription_dead_letter_max_delivery_attempts: Option<u32>,
1383}
1384
1385#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1386#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1387#[serde(rename_all = "camelCase")]
1388pub struct AzureServiceBusQueueHeartbeatData {
1389    pub status: QueueHeartbeatStatus,
1390    pub name: String,
1391    pub namespace_name: String,
1392    pub resource_group: Option<String>,
1393    pub resource_id: Option<String>,
1394    pub endpoint: Option<String>,
1395    pub queue_status: Option<String>,
1396    pub lock_duration: Option<String>,
1397    pub max_delivery_count: Option<u32>,
1398    pub requires_duplicate_detection: Option<bool>,
1399    pub duplicate_detection_history_time_window: Option<String>,
1400    pub requires_session: Option<bool>,
1401    pub dead_lettering_on_message_expiration: Option<bool>,
1402    pub forward_dead_lettered_messages_to: Option<String>,
1403    pub forward_to: Option<String>,
1404    pub default_message_time_to_live: Option<String>,
1405    pub auto_delete_on_idle: Option<String>,
1406    pub enable_batched_operations: Option<bool>,
1407    pub enable_express: Option<bool>,
1408    pub enable_partitioning: Option<bool>,
1409    pub max_message_size_in_kilobytes: Option<u64>,
1410    pub max_size_in_megabytes: Option<u32>,
1411    pub message_count: Option<u64>,
1412    pub active_message_count: Option<u64>,
1413    pub dead_letter_message_count: Option<u64>,
1414    pub scheduled_message_count: Option<u64>,
1415    pub transfer_message_count: Option<u64>,
1416    pub transfer_dead_letter_message_count: Option<u64>,
1417    pub size_in_bytes: Option<u64>,
1418    pub accessed_at: Option<String>,
1419    pub created_at: Option<String>,
1420    pub updated_at: Option<String>,
1421}
1422
1423#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1424#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1425#[serde(rename_all = "camelCase")]
1426pub struct AwsSqsQueueHeartbeatData {
1427    pub status: QueueHeartbeatStatus,
1428    pub name: String,
1429    pub region: Option<String>,
1430    pub queue_url: Option<String>,
1431    pub queue_arn: Option<String>,
1432    pub visibility_timeout_seconds: Option<u32>,
1433    pub message_retention_period_seconds: Option<u32>,
1434    pub delay_seconds: Option<u32>,
1435    pub receive_message_wait_time_seconds: Option<u32>,
1436    pub maximum_message_size: Option<u32>,
1437    pub redrive_policy: Option<String>,
1438    pub redrive_allow_policy: Option<String>,
1439    pub fifo_queue: Option<bool>,
1440    pub content_based_deduplication: Option<bool>,
1441    pub deduplication_scope: Option<String>,
1442    pub fifo_throughput_limit: Option<String>,
1443    pub sse_enabled: Option<bool>,
1444    pub kms_master_key_id: Option<String>,
1445    pub kms_data_key_reuse_period_seconds: Option<u32>,
1446    pub sqs_managed_sse_enabled: Option<bool>,
1447    pub approximate_visible_messages: Option<u64>,
1448    pub approximate_in_flight_messages: Option<u64>,
1449    pub approximate_delayed_messages: Option<u64>,
1450    pub approximate_counts: bool,
1451}
1452
1453#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1454#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1455#[serde(tag = "backend", rename_all = "camelCase")]
1456pub enum KvHeartbeatData {
1457    AwsDynamoDb(AwsDynamoDbKvHeartbeatData),
1458    GcpFirestore(GcpFirestoreKvHeartbeatData),
1459    AzureTable(AzureTableKvHeartbeatData),
1460    Local(LocalKvHeartbeatData),
1461}
1462
1463#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1464#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1465#[serde(rename_all = "camelCase")]
1466pub struct KvHeartbeatStatus {
1467    pub health: ObservedHealth,
1468    pub lifecycle: ProviderLifecycleState,
1469    pub message: Option<String>,
1470    pub stale: bool,
1471    pub partial: bool,
1472    pub collection_issues: Vec<HeartbeatCollectionIssue>,
1473}
1474
1475impl Default for KvHeartbeatStatus {
1476    fn default() -> Self {
1477        Self {
1478            health: ObservedHealth::Unknown,
1479            lifecycle: ProviderLifecycleState::Unknown,
1480            message: None,
1481            stale: false,
1482            partial: false,
1483            collection_issues: vec![],
1484        }
1485    }
1486}
1487
1488#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1489#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1490#[serde(rename_all = "camelCase")]
1491pub struct AwsDynamoDbKvHeartbeatData {
1492    pub status: KvHeartbeatStatus,
1493    pub name: String,
1494    pub region: Option<String>,
1495    pub table_arn: Option<String>,
1496    pub table_status: Option<String>,
1497    pub billing_mode: Option<String>,
1498    pub key_schema: Vec<AwsDynamoDbKeySchemaElement>,
1499    pub global_secondary_index_count: Option<u32>,
1500    pub local_secondary_index_count: Option<u32>,
1501    pub item_count: Option<u64>,
1502    pub table_size_bytes: Option<u64>,
1503    pub stream_enabled: Option<bool>,
1504    pub stream_view_type: Option<String>,
1505    pub ttl_status: Option<String>,
1506    pub ttl_attribute_name: Option<String>,
1507    pub deletion_protection_enabled: Option<bool>,
1508    pub sse_status: Option<String>,
1509    pub sse_type: Option<String>,
1510    pub table_class: Option<String>,
1511    pub replica_count: Option<u32>,
1512    pub restore_in_progress: Option<bool>,
1513}
1514
1515#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1516#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1517#[serde(rename_all = "camelCase")]
1518pub struct AwsDynamoDbKeySchemaElement {
1519    pub attribute_name: String,
1520    pub key_type: String,
1521}
1522
1523#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1524#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1525#[serde(rename_all = "camelCase")]
1526pub struct GcpFirestoreKvHeartbeatData {
1527    pub status: KvHeartbeatStatus,
1528    pub database_name: String,
1529    pub project_id: Option<String>,
1530    pub endpoint: Option<String>,
1531    pub location_id: Option<String>,
1532    pub database_type: Option<String>,
1533    pub concurrency_mode: Option<String>,
1534    pub app_engine_integration_mode: Option<String>,
1535    pub delete_protection_state: Option<String>,
1536    pub point_in_time_recovery_enablement: Option<String>,
1537    pub version_retention_period: Option<String>,
1538    pub earliest_version_time: Option<String>,
1539    pub create_time: Option<String>,
1540    pub update_time: Option<String>,
1541    pub delete_time: Option<String>,
1542    pub database_edition: Option<String>,
1543    pub cmek_enabled: bool,
1544    pub source_info_present: 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 AzureTableKvHeartbeatData {
1551    pub status: KvHeartbeatStatus,
1552    pub table_name: String,
1553    pub storage_account_name: String,
1554    pub resource_group: Option<String>,
1555    pub endpoint: Option<String>,
1556    pub storage_account_resource_id: Option<String>,
1557    pub storage_account_location: Option<String>,
1558    pub storage_account_kind: Option<String>,
1559    pub storage_account_provisioning_state: Option<String>,
1560    pub storage_account_primary_status: Option<String>,
1561    pub table_exists: bool,
1562    pub signed_identifier_count: Option<u32>,
1563}
1564
1565#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1566#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1567#[serde(rename_all = "camelCase")]
1568pub struct LocalKvHeartbeatData {
1569    pub status: KvHeartbeatStatus,
1570    pub name: String,
1571    pub path: String,
1572    pub path_exists: bool,
1573    pub is_directory: Option<bool>,
1574    pub cloud_metadata_supported: bool,
1575}
1576
1577#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1578#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1579#[serde(tag = "backend", rename_all = "camelCase")]
1580pub enum PostgresHeartbeatData {
1581    /// AWS Aurora Serverless v2 backend.
1582    Aurora(AuroraPostgresHeartbeatData),
1583    /// GCP Cloud SQL backend.
1584    CloudSql(GcpCloudSqlPostgresHeartbeatData),
1585    /// Azure Flexible Server backend.
1586    FlexibleServer(AzureFlexibleServerPostgresHeartbeatData),
1587    /// Local embedded Postgres backend.
1588    Local(LocalPostgresHeartbeatData),
1589}
1590
1591#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1592#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1593#[serde(rename_all = "camelCase")]
1594pub struct PostgresHeartbeatStatus {
1595    pub health: ObservedHealth,
1596    pub lifecycle: ProviderLifecycleState,
1597    pub message: Option<String>,
1598    pub stale: bool,
1599    pub partial: bool,
1600    pub collection_issues: Vec<HeartbeatCollectionIssue>,
1601}
1602
1603impl Default for PostgresHeartbeatStatus {
1604    fn default() -> Self {
1605        Self {
1606            health: ObservedHealth::Unknown,
1607            lifecycle: ProviderLifecycleState::Unknown,
1608            message: None,
1609            stale: false,
1610            partial: false,
1611            collection_issues: vec![],
1612        }
1613    }
1614}
1615
1616/// Heartbeat data for a Redis resource. Everything comes from the cloud's
1617/// management and metrics APIs; the manager never opens a Redis connection.
1618#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1619#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1620#[serde(tag = "backend", rename_all = "camelCase")]
1621pub enum RedisHeartbeatData {
1622    /// AWS ElastiCache for Valkey.
1623    Elasticache(ElasticacheRedisHeartbeatData),
1624    /// GCP Memorystore for Valkey.
1625    Memorystore(MemorystoreRedisHeartbeatData),
1626    /// Azure Managed Redis.
1627    AzureManagedRedis(AzureManagedRedisHeartbeatData),
1628    /// Local Valkey container.
1629    Local(LocalRedisHeartbeatData),
1630}
1631
1632/// Same fields as every other resource heartbeat status.
1633#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1634#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1635#[serde(rename_all = "camelCase")]
1636pub struct RedisHeartbeatStatus {
1637    pub health: ObservedHealth,
1638    pub lifecycle: ProviderLifecycleState,
1639    pub message: Option<String>,
1640    pub stale: bool,
1641    pub partial: bool,
1642    pub collection_issues: Vec<HeartbeatCollectionIssue>,
1643}
1644
1645impl Default for RedisHeartbeatStatus {
1646    fn default() -> Self {
1647        Self {
1648            health: ObservedHealth::Unknown,
1649            lifecycle: ProviderLifecycleState::Unknown,
1650            message: None,
1651            stale: false,
1652            partial: false,
1653            collection_issues: vec![],
1654        }
1655    }
1656}
1657
1658/// One sample of the numbers that matter for a Redis server. A field is `None`
1659/// when the cloud did not return that metric in the window.
1660#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
1661#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1662#[serde(rename_all = "camelCase")]
1663pub struct RedisMetricsSample {
1664    /// Memory used as a percentage of the configured maximum (0-100).
1665    #[serde(skip_serializing_if = "Option::is_none")]
1666    pub memory_used_percent: Option<f64>,
1667    /// Keys evicted during the sampled window.
1668    #[serde(skip_serializing_if = "Option::is_none")]
1669    pub evictions: Option<f64>,
1670    /// Connected clients at the end of the window.
1671    #[serde(skip_serializing_if = "Option::is_none")]
1672    pub connected_clients: Option<f64>,
1673    /// Replication lag in seconds, when a replica exists.
1674    #[serde(skip_serializing_if = "Option::is_none")]
1675    pub replication_lag_seconds: Option<f64>,
1676}
1677
1678/// Configuration the controller reads back from the cloud, to compare with the
1679/// desired configuration.
1680#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
1681#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1682#[serde(rename_all = "camelCase")]
1683pub struct RedisObservedConfig {
1684    /// Engine name and version as the cloud reports it.
1685    #[serde(skip_serializing_if = "Option::is_none")]
1686    pub engine_version: Option<String>,
1687    /// Node type or SKU name.
1688    #[serde(skip_serializing_if = "Option::is_none")]
1689    pub node_type: Option<String>,
1690    #[serde(skip_serializing_if = "Option::is_none")]
1691    pub endpoint: Option<String>,
1692    #[serde(skip_serializing_if = "Option::is_none")]
1693    pub port: Option<u16>,
1694    #[serde(skip_serializing_if = "Option::is_none")]
1695    pub eviction_policy: Option<String>,
1696    #[serde(skip_serializing_if = "Option::is_none")]
1697    pub persistence_enabled: Option<bool>,
1698    /// Replicas behind the primary.
1699    #[serde(skip_serializing_if = "Option::is_none")]
1700    pub replica_count: Option<u32>,
1701    #[serde(skip_serializing_if = "Option::is_none")]
1702    pub tls_enabled: Option<bool>,
1703    #[serde(skip_serializing_if = "Option::is_none")]
1704    pub auth_enabled: Option<bool>,
1705    /// Azure only: true when the server accepts connections from the public internet.
1706    #[serde(skip_serializing_if = "Option::is_none")]
1707    pub public_network_access: Option<bool>,
1708}
1709
1710#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1711#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1712#[serde(rename_all = "camelCase")]
1713pub struct ElasticacheRedisHeartbeatData {
1714    pub status: RedisHeartbeatStatus,
1715    pub replication_group_id: String,
1716    #[serde(skip_serializing_if = "Option::is_none")]
1717    pub state: Option<String>,
1718    pub config: RedisObservedConfig,
1719    pub metrics: RedisMetricsSample,
1720}
1721
1722#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1723#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1724#[serde(rename_all = "camelCase")]
1725pub struct MemorystoreRedisHeartbeatData {
1726    pub status: RedisHeartbeatStatus,
1727    pub instance_name: String,
1728    #[serde(skip_serializing_if = "Option::is_none")]
1729    pub state: Option<String>,
1730    pub config: RedisObservedConfig,
1731    pub metrics: RedisMetricsSample,
1732}
1733
1734#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1735#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1736#[serde(rename_all = "camelCase")]
1737pub struct AzureManagedRedisHeartbeatData {
1738    pub status: RedisHeartbeatStatus,
1739    pub cluster_name: String,
1740    #[serde(skip_serializing_if = "Option::is_none")]
1741    pub state: Option<String>,
1742    pub config: RedisObservedConfig,
1743    pub metrics: RedisMetricsSample,
1744}
1745
1746#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1747#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1748#[serde(rename_all = "camelCase")]
1749pub struct LocalRedisHeartbeatData {
1750    pub status: RedisHeartbeatStatus,
1751    pub name: String,
1752    pub port: Option<u16>,
1753    pub version: String,
1754    pub process_running: bool,
1755}
1756
1757/// Content-free telemetry about what a sandbox resource is running.
1758///
1759/// Never anything from inside a sandbox. A controller reaches only the cloud's management APIs,
1760/// and the whole point of the resource is that the control plane cannot see what runs in it.
1761#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1762#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1763#[serde(tag = "backend", rename_all = "camelCase")]
1764pub enum SandboxHeartbeatData {
1765    AwsMicrovm(AwsMicrovmSandboxHeartbeatData),
1766    AzureSandboxGroup(AzureSandboxGroupHeartbeatData),
1767    GcpAgentPlatform(GcpAgentPlatformSandboxHeartbeatData),
1768    KubernetesPods(KubernetesSandboxHeartbeatData),
1769    Local(LocalSandboxHeartbeatData),
1770}
1771
1772#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1773#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1774#[serde(rename_all = "camelCase")]
1775pub struct SandboxHeartbeatStatus {
1776    pub health: ObservedHealth,
1777    pub lifecycle: ProviderLifecycleState,
1778    pub message: Option<String>,
1779    pub stale: bool,
1780    pub partial: bool,
1781    pub collection_issues: Vec<HeartbeatCollectionIssue>,
1782}
1783
1784impl Default for SandboxHeartbeatStatus {
1785    fn default() -> Self {
1786        Self {
1787            health: ObservedHealth::Healthy,
1788            lifecycle: ProviderLifecycleState::Running,
1789            message: None,
1790            stale: false,
1791            partial: false,
1792            collection_issues: Vec::new(),
1793        }
1794    }
1795}
1796
1797/// AWS: the image a sandbox runs from, and the lifecycle state AWS reports for it.
1798#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1799#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1800#[serde(rename_all = "camelCase")]
1801pub struct AwsMicrovmSandboxHeartbeatData {
1802    pub status: SandboxHeartbeatStatus,
1803    /// Image the sandboxes belong to.
1804    pub image_identifier: String,
1805    /// The image's own lifecycle state, which is where AWS surfaces base-image deprecation.
1806    ///
1807    /// No sandbox count sits beside it: counting means `lambda:ListMicrovms`, which authorizes
1808    /// against no resource type and so cannot be granted without an account-wide reach the
1809    /// permission sets refuse. A field only an over-broad grant could fill is a field whose
1810    /// implementer ships AccessDenied into a customer's account.
1811    pub image_state: Option<String>,
1812}
1813
1814/// Azure: the sandbox group's ARM state. The data plane has no list operation, so a sandbox count
1815/// is not available here.
1816#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1817#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1818#[serde(rename_all = "camelCase")]
1819pub struct AzureSandboxGroupHeartbeatData {
1820    pub status: SandboxHeartbeatStatus,
1821    pub sandbox_group: String,
1822    pub provisioning_state: Option<String>,
1823}
1824
1825/// GCP: the Agent Platform template sandboxes are cut from, and the engine it hangs under.
1826///
1827/// No sandbox count: that needs `aiplatform.sandboxEnvironments.list`, which only the management
1828/// permission set holds. No template state either — emission is gated on reading it `ACTIVE`,
1829/// which is what `status` already says.
1830#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1831#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1832#[serde(rename_all = "camelCase")]
1833pub struct GcpAgentPlatformSandboxHeartbeatData {
1834    pub status: SandboxHeartbeatStatus,
1835    /// Reasoning engine the template hangs under, without which the template id names nothing.
1836    pub engine: String,
1837    /// The template sandboxes are currently cut from.
1838    pub template_id: String,
1839}
1840
1841/// Kubernetes: pods carrying the sandbox label, in the deployment's namespace.
1842#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1843#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1844#[serde(rename_all = "camelCase")]
1845pub struct KubernetesSandboxHeartbeatData {
1846    pub status: SandboxHeartbeatStatus,
1847    pub namespace: String,
1848    pub active_sessions: u32,
1849    /// Claimed but unused pods waiting in the pool.
1850    pub idle_pods: u32,
1851}
1852
1853/// Local: containers Docker still holds for this sandbox.
1854#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1855#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1856#[serde(rename_all = "camelCase")]
1857pub struct LocalSandboxHeartbeatData {
1858    pub status: SandboxHeartbeatStatus,
1859    pub active_sessions: u32,
1860    /// Whether the loopback route is serving in this process. False after a manager restart until
1861    /// the next tick rebinds it.
1862    pub route_serving: bool,
1863}
1864
1865#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1866#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1867#[serde(tag = "backend", rename_all = "camelCase")]
1868pub enum AiHeartbeatData {
1869    AwsBedrock(AwsBedrockAiHeartbeatData),
1870    GcpVertex(GcpVertexAiHeartbeatData),
1871    AzureFoundry(AzureFoundryAiHeartbeatData),
1872    External(ExternalAiHeartbeatData),
1873}
1874
1875#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1876#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1877#[serde(rename_all = "camelCase")]
1878pub struct AiHeartbeatStatus {
1879    pub health: ObservedHealth,
1880    pub lifecycle: ProviderLifecycleState,
1881    pub message: Option<String>,
1882    pub stale: bool,
1883    pub partial: bool,
1884    pub collection_issues: Vec<HeartbeatCollectionIssue>,
1885}
1886
1887impl Default for AiHeartbeatStatus {
1888    fn default() -> Self {
1889        // A configured AI binding is not proof that its models are usable.
1890        Self {
1891            health: ObservedHealth::Unknown,
1892            lifecycle: ProviderLifecycleState::Unknown,
1893            message: None,
1894            stale: false,
1895            partial: false,
1896            collection_issues: vec![],
1897        }
1898    }
1899}
1900
1901/// Provider control plane used to observe model availability without invoking
1902/// a model, spending customer quota, or accepting provider terms.
1903#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
1904#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1905#[serde(rename_all = "kebab-case")]
1906pub enum AiAvailabilitySource {
1907    AwsBedrock,
1908    GcpVertex,
1909    AzureFoundry,
1910    Anthropic,
1911}
1912
1913#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
1914#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1915#[serde(rename_all = "kebab-case")]
1916pub enum AiModelAvailability {
1917    Available,
1918    Blocked,
1919    Unknown,
1920}
1921
1922#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
1923#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1924#[serde(rename_all = "kebab-case")]
1925pub enum AiAvailabilityBlocker {
1926    AgreementRequired,
1927    EntitlementRequired,
1928    ModelActivationRequired,
1929    DeploymentRequired,
1930    QuotaConfigurationRequired,
1931    RegionUnavailable,
1932    AccessDenied,
1933    ObservationFailed,
1934}
1935
1936#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
1937#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1938#[serde(rename_all = "kebab-case")]
1939pub enum AiAccessTest {
1940    Verified,
1941    Failed,
1942    NotChecked,
1943}
1944
1945#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1946#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1947#[serde(rename_all = "camelCase")]
1948pub struct AiModelAvailabilityObservation {
1949    pub public_model_id: String,
1950    pub client_apis: Vec<ClientApi>,
1951    pub availability: AiModelAvailability,
1952    pub blockers: Vec<AiAvailabilityBlocker>,
1953    pub access_test: AiAccessTest,
1954    #[serde(skip_serializing_if = "Option::is_none")]
1955    pub tested_at: Option<DateTime<Utc>>,
1956    #[serde(skip_serializing_if = "Option::is_none")]
1957    pub error_code: Option<String>,
1958}
1959
1960#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1961#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
1962#[serde(rename_all = "camelCase")]
1963pub struct AiAvailabilityObservation {
1964    pub source: AiAvailabilitySource,
1965    pub catalog_revision: String,
1966    #[serde(skip_serializing_if = "Option::is_none")]
1967    pub location: Option<String>,
1968    pub models: Vec<AiModelAvailabilityObservation>,
1969}
1970
1971impl AiAvailabilityObservation {
1972    /// Conservative observation used until a provider control-plane read has
1973    /// completed. It never claims that a configured model is usable.
1974    pub fn unobserved(
1975        source: AiAvailabilitySource,
1976        platform: Platform,
1977        location: Option<String>,
1978    ) -> Self {
1979        Self {
1980            source,
1981            catalog_revision: crate::ai_catalog::AI_CATALOG_REVISION.to_string(),
1982            location,
1983            models: crate::ai_catalog::models_for(platform)
1984                .into_iter()
1985                .map(|model| AiModelAvailabilityObservation {
1986                    public_model_id: model.public_id.to_string(),
1987                    client_apis: ClientApi::ALL.to_vec(),
1988                    availability: AiModelAvailability::Unknown,
1989                    blockers: vec![AiAvailabilityBlocker::ObservationFailed],
1990                    access_test: AiAccessTest::NotChecked,
1991                    tested_at: None,
1992                    error_code: None,
1993                })
1994                .collect(),
1995        }
1996    }
1997}
1998
1999#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2000#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
2001#[serde(rename_all = "camelCase")]
2002pub struct LocalPostgresHeartbeatData {
2003    pub status: PostgresHeartbeatStatus,
2004    pub name: String,
2005    pub port: Option<u16>,
2006    pub version: String,
2007    pub process_running: bool,
2008}
2009
2010#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2011#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
2012#[serde(rename_all = "camelCase")]
2013pub struct AuroraPostgresHeartbeatData {
2014    pub status: PostgresHeartbeatStatus,
2015    pub cluster_identifier: String,
2016    #[serde(skip_serializing_if = "Option::is_none")]
2017    pub engine_version: Option<String>,
2018    #[serde(skip_serializing_if = "Option::is_none")]
2019    pub endpoint: Option<String>,
2020    /// Latest sampled `ServerlessDatabaseCapacity` (ACU).
2021    #[serde(skip_serializing_if = "Option::is_none")]
2022    pub serverless_capacity: Option<f64>,
2023    /// True when a `minCapacity: 0` instance has not reached 0 ACU over the observation
2024    /// window — it is silently paying always-on prices (auto-pause verification).
2025    pub never_pauses: bool,
2026}
2027
2028#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2029#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
2030#[serde(rename_all = "camelCase")]
2031pub struct GcpCloudSqlPostgresHeartbeatData {
2032    pub status: PostgresHeartbeatStatus,
2033    pub instance_name: String,
2034    #[serde(skip_serializing_if = "Option::is_none")]
2035    pub state: Option<String>,
2036    #[serde(skip_serializing_if = "Option::is_none")]
2037    pub database_version: Option<String>,
2038}
2039
2040#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2041#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
2042#[serde(rename_all = "camelCase")]
2043pub struct AzureFlexibleServerPostgresHeartbeatData {
2044    pub status: PostgresHeartbeatStatus,
2045    pub server_name: String,
2046    #[serde(skip_serializing_if = "Option::is_none")]
2047    pub state: Option<String>,
2048    #[serde(skip_serializing_if = "Option::is_none")]
2049    pub version: Option<String>,
2050}
2051
2052#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2053#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
2054#[serde(rename_all = "camelCase")]
2055pub struct AwsBedrockAiHeartbeatData {
2056    pub status: AiHeartbeatStatus,
2057    pub region: String,
2058    pub availability: AiAvailabilityObservation,
2059}
2060
2061#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2062#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
2063#[serde(rename_all = "camelCase")]
2064pub struct GcpVertexAiHeartbeatData {
2065    pub status: AiHeartbeatStatus,
2066    pub project: String,
2067    pub location: String,
2068    pub availability: AiAvailabilityObservation,
2069}
2070
2071#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2072#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
2073#[serde(rename_all = "camelCase")]
2074pub struct AzureFoundryAiHeartbeatData {
2075    pub status: AiHeartbeatStatus,
2076    pub account_name: String,
2077    pub endpoint: Option<String>,
2078    pub resource_group: Option<String>,
2079    pub location: Option<String>,
2080    pub availability: AiAvailabilityObservation,
2081}
2082
2083#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2084#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
2085#[serde(rename_all = "camelCase")]
2086pub struct ExternalAiHeartbeatData {
2087    pub status: AiHeartbeatStatus,
2088    /// The BYO-key provider serving this binding (e.g. "openai"). Used on the Local
2089    /// platform, where the app brings its own provider key instead of an ambient cloud.
2090    pub provider: String,
2091    #[serde(skip_serializing_if = "Option::is_none")]
2092    pub availability: Option<AiAvailabilityObservation>,
2093}
2094
2095#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2096#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
2097#[serde(tag = "backend", rename_all = "camelCase")]
2098pub enum VaultHeartbeatData {
2099    AwsParameterStore(AwsParameterStoreVaultHeartbeatData),
2100    GcpSecretManager(GcpSecretManagerVaultHeartbeatData),
2101    AzureKeyVault(AzureKeyVaultHeartbeatData),
2102    KubernetesSecret(KubernetesSecretVaultHeartbeatData),
2103    Local(LocalVaultHeartbeatData),
2104}
2105
2106#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2107#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
2108#[serde(rename_all = "camelCase")]
2109pub struct VaultHeartbeatStatus {
2110    pub health: ObservedHealth,
2111    pub lifecycle: ProviderLifecycleState,
2112    pub message: Option<String>,
2113    pub stale: bool,
2114    pub partial: bool,
2115    pub collection_issues: Vec<HeartbeatCollectionIssue>,
2116}
2117
2118impl Default for VaultHeartbeatStatus {
2119    fn default() -> Self {
2120        Self {
2121            health: ObservedHealth::Healthy,
2122            lifecycle: ProviderLifecycleState::Running,
2123            message: None,
2124            stale: false,
2125            partial: false,
2126            collection_issues: vec![],
2127        }
2128    }
2129}
2130
2131#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2132#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
2133#[serde(rename_all = "camelCase")]
2134pub struct AwsParameterStoreVaultHeartbeatData {
2135    pub status: VaultHeartbeatStatus,
2136    pub account_id: String,
2137    pub region: String,
2138    pub prefix: String,
2139    pub parameter_metadata_sampled: bool,
2140    pub sampled_parameter_count: Option<u32>,
2141    pub sampled_secure_string_count: Option<u32>,
2142    pub sampled_string_count: Option<u32>,
2143    pub sampled_string_list_count: Option<u32>,
2144    pub sampled_advanced_tier_count: Option<u32>,
2145    pub sampled_kms_key_metadata_present_count: Option<u32>,
2146    pub latest_modified_at: Option<DateTime<Utc>>,
2147    pub has_more_parameters: Option<bool>,
2148}
2149
2150#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2151#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
2152#[serde(rename_all = "camelCase")]
2153pub struct GcpSecretManagerVaultHeartbeatData {
2154    pub status: VaultHeartbeatStatus,
2155    pub project_id: String,
2156    pub location: String,
2157    pub prefix: String,
2158    pub secret_metadata_listed: bool,
2159}
2160
2161#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2162#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
2163#[serde(rename_all = "camelCase")]
2164pub struct AzureKeyVaultHeartbeatData {
2165    pub status: VaultHeartbeatStatus,
2166    pub name: String,
2167    pub resource_group: Option<String>,
2168    pub resource_id: Option<String>,
2169    pub location: Option<String>,
2170    pub vault_uri: Option<String>,
2171    pub provisioning_state: Option<String>,
2172    pub sku_family: Option<String>,
2173    pub sku_name: Option<String>,
2174    pub soft_delete_enabled: bool,
2175    pub soft_delete_retention_days: i32,
2176    pub purge_protection_enabled: Option<bool>,
2177    pub rbac_authorization_enabled: bool,
2178    pub public_network_access: String,
2179    pub access_policy_count: u32,
2180    pub private_endpoint_connection_count: u32,
2181    pub secret_metadata_listed: bool,
2182}
2183
2184#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2185#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
2186#[serde(rename_all = "camelCase")]
2187pub struct KubernetesSecretVaultHeartbeatData {
2188    pub status: VaultHeartbeatStatus,
2189    pub namespace: String,
2190    pub prefix: String,
2191    pub secret_metadata_listed: bool,
2192}
2193
2194#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2195#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
2196#[serde(rename_all = "camelCase")]
2197pub struct LocalVaultHeartbeatData {
2198    pub status: VaultHeartbeatStatus,
2199    pub path: String,
2200    pub path_exists: bool,
2201    pub is_directory: Option<bool>,
2202    pub readonly: Option<bool>,
2203    pub modified_at: Option<DateTime<Utc>>,
2204    pub secret_metadata_listed: bool,
2205}
2206
2207#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2208#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
2209#[serde(tag = "backend", rename_all = "camelCase")]
2210pub enum ServiceAccountHeartbeatData {
2211    AwsIamRole(AwsIamRoleServiceAccountHeartbeatData),
2212    GcpServiceAccount(GcpServiceAccountHeartbeatData),
2213    AzureManagedIdentity(AzureManagedIdentityServiceAccountHeartbeatData),
2214    Local(LocalServiceAccountHeartbeatData),
2215}
2216
2217#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2218#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
2219#[serde(rename_all = "camelCase")]
2220pub struct ServiceAccountHeartbeatStatus {
2221    pub health: ObservedHealth,
2222    pub lifecycle: ProviderLifecycleState,
2223    pub message: Option<String>,
2224    pub stale: bool,
2225    pub partial: bool,
2226    pub collection_issues: Vec<HeartbeatCollectionIssue>,
2227}
2228
2229#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2230#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
2231#[serde(rename_all = "camelCase")]
2232pub struct AwsIamRoleServiceAccountHeartbeatData {
2233    pub status: ServiceAccountHeartbeatStatus,
2234    pub role_name: String,
2235    pub role_arn: String,
2236    pub role_id: String,
2237    pub path: String,
2238    pub create_date: String,
2239    pub description: Option<String>,
2240    pub max_session_duration: Option<i32>,
2241    pub assume_role_policy_present: bool,
2242    pub permissions_boundary_type: Option<String>,
2243    pub permissions_boundary_arn: Option<String>,
2244    pub tag_count: u32,
2245    pub managed_tag_count: u32,
2246    pub attached_policy_count: u32,
2247    pub attached_policy_names: Vec<String>,
2248    pub inline_policy_count: u32,
2249    pub inline_policy_names: Vec<String>,
2250    pub stack_permissions_applied: bool,
2251    pub last_used_date: Option<String>,
2252    pub last_used_region: Option<String>,
2253}
2254
2255#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2256#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
2257#[serde(rename_all = "camelCase")]
2258pub struct GcpServiceAccountHeartbeatData {
2259    pub status: ServiceAccountHeartbeatStatus,
2260    pub name: Option<String>,
2261    pub project_id: Option<String>,
2262    pub unique_id: Option<String>,
2263    pub email: String,
2264    pub display_name: Option<String>,
2265    pub description: Option<String>,
2266    pub oauth2_client_id: Option<String>,
2267    pub disabled: Option<bool>,
2268    pub etag: Option<String>,
2269    pub project_binding_count: u32,
2270    pub project_roles: Vec<String>,
2271    pub service_account_binding_count: u32,
2272    pub service_account_roles: Vec<String>,
2273}
2274
2275#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2276#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
2277#[serde(rename_all = "camelCase")]
2278pub struct AzureManagedIdentityServiceAccountHeartbeatData {
2279    pub status: ServiceAccountHeartbeatStatus,
2280    pub name: String,
2281    pub resource_id: String,
2282    pub resource_group: String,
2283    pub location: String,
2284    pub type_: Option<String>,
2285    pub client_id: Option<String>,
2286    pub principal_id: Option<String>,
2287    pub tenant_id: Option<String>,
2288    pub isolation_scope: Option<String>,
2289    pub managed_tag_count: u32,
2290    pub role_assignment_count: u32,
2291    pub role_assignment_ids: Vec<String>,
2292    pub custom_role_definition_count: u32,
2293    pub custom_role_definition_ids: Vec<String>,
2294    pub stack_permissions_applied: bool,
2295}
2296
2297#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2298#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
2299#[serde(rename_all = "camelCase")]
2300pub struct LocalServiceAccountHeartbeatData {
2301    pub status: ServiceAccountHeartbeatStatus,
2302    pub identity: String,
2303    pub configured: bool,
2304}
2305
2306#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2307#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
2308#[serde(tag = "backend", rename_all = "camelCase")]
2309pub enum NetworkHeartbeatData {
2310    AwsVpc(AwsVpcNetworkHeartbeatData),
2311    GcpVpc(GcpVpcNetworkHeartbeatData),
2312    AzureVnet(AzureVnetNetworkHeartbeatData),
2313}
2314
2315#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2316#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
2317#[serde(rename_all = "camelCase")]
2318pub struct NetworkHeartbeatStatus {
2319    pub health: ObservedHealth,
2320    pub lifecycle: ProviderLifecycleState,
2321    pub message: Option<String>,
2322    pub stale: bool,
2323    pub partial: bool,
2324    pub collection_issues: Vec<HeartbeatCollectionIssue>,
2325}
2326
2327#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2328#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
2329#[serde(rename_all = "camelCase")]
2330pub struct AwsVpcNetworkHeartbeatData {
2331    pub status: NetworkHeartbeatStatus,
2332    pub vpc_id: Option<String>,
2333    pub vpc_state: Option<String>,
2334    pub cidr_block: Option<String>,
2335    pub public_subnet_ids: Vec<String>,
2336    pub private_subnet_ids: Vec<String>,
2337    pub availability_zones: Vec<String>,
2338    pub internet_gateway_id: Option<String>,
2339    pub nat_gateway_id: Option<String>,
2340    pub route_table_count: u32,
2341    pub security_group_id: Option<String>,
2342    pub is_byo_vpc: bool,
2343}
2344
2345#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2346#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
2347#[serde(rename_all = "camelCase")]
2348pub struct GcpVpcNetworkHeartbeatData {
2349    pub status: NetworkHeartbeatStatus,
2350    pub network_name: Option<String>,
2351    pub network_self_link: Option<String>,
2352    pub subnetwork_name: Option<String>,
2353    pub subnetwork_self_link: Option<String>,
2354    pub region: Option<String>,
2355    pub cidr_block: Option<String>,
2356    pub router_name: Option<String>,
2357    pub cloud_nat_name: Option<String>,
2358    pub firewall_name: Option<String>,
2359    pub is_byo_vpc: bool,
2360}
2361
2362#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2363#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
2364#[serde(rename_all = "camelCase")]
2365pub struct AzureVnetNetworkHeartbeatData {
2366    pub status: NetworkHeartbeatStatus,
2367    pub vnet_name: Option<String>,
2368    pub vnet_resource_id: Option<String>,
2369    pub resource_group: Option<String>,
2370    pub location: Option<String>,
2371    pub cidr_block: Option<String>,
2372    pub public_subnet_name: Option<String>,
2373    pub private_subnet_name: Option<String>,
2374    pub application_gateway_subnet_name: Option<String>,
2375    pub private_endpoint_subnet_name: Option<String>,
2376    pub nat_gateway_id: Option<String>,
2377    pub public_ip_id: Option<String>,
2378    pub nsg_id: Option<String>,
2379    pub is_byo_vnet: bool,
2380    pub last_byo_vnet_verification_error_code: Option<String>,
2381}
2382
2383#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2384#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
2385#[serde(tag = "backend", rename_all = "camelCase")]
2386pub enum RemoteStackManagementHeartbeatData {
2387    AwsIamRole(AwsRemoteStackManagementHeartbeatData),
2388    GcpServiceAccount(GcpRemoteStackManagementHeartbeatData),
2389    AzureManagedIdentity(AzureRemoteStackManagementHeartbeatData),
2390}
2391
2392#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2393#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
2394#[serde(rename_all = "camelCase")]
2395pub struct RemoteStackManagementHeartbeatStatus {
2396    pub health: ObservedHealth,
2397    pub lifecycle: ProviderLifecycleState,
2398    pub message: Option<String>,
2399    pub stale: bool,
2400    pub partial: bool,
2401    pub collection_issues: Vec<HeartbeatCollectionIssue>,
2402}
2403
2404#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2405#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
2406#[serde(rename_all = "camelCase")]
2407pub struct AwsRemoteStackManagementHeartbeatData {
2408    pub status: RemoteStackManagementHeartbeatStatus,
2409    pub role_name: Option<String>,
2410    pub role_arn: Option<String>,
2411    pub management_permissions_applied: bool,
2412}
2413
2414#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2415#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
2416#[serde(rename_all = "camelCase")]
2417pub struct GcpRemoteStackManagementHeartbeatData {
2418    pub status: RemoteStackManagementHeartbeatStatus,
2419    pub service_account_email: Option<String>,
2420    pub service_account_unique_id: Option<String>,
2421    pub role_bound: bool,
2422    pub impersonation_granted: bool,
2423}
2424
2425#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2426#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
2427#[serde(rename_all = "camelCase")]
2428pub struct AzureRemoteStackManagementHeartbeatData {
2429    pub status: RemoteStackManagementHeartbeatStatus,
2430    pub uami_resource_id: Option<String>,
2431    pub uami_client_id: Option<String>,
2432    pub uami_principal_id: Option<String>,
2433    pub tenant_id: Option<String>,
2434    pub fic_name: Option<String>,
2435    pub role_definition_id: Option<String>,
2436    pub role_assignment_ids: Vec<String>,
2437}
2438
2439#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2440#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
2441#[serde(tag = "backend", rename_all = "camelCase")]
2442pub enum ArtifactRegistryHeartbeatData {
2443    AwsEcr(AwsEcrArtifactRegistryHeartbeatData),
2444    GcpArtifactRegistry(GcpArtifactRegistryHeartbeatData),
2445    AzureContainerRegistry(AzureContainerRegistryHeartbeatData),
2446    Local(LocalArtifactRegistryHeartbeatData),
2447}
2448
2449#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2450#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
2451#[serde(rename_all = "camelCase")]
2452pub struct ArtifactRegistryHeartbeatStatus {
2453    pub health: ObservedHealth,
2454    pub lifecycle: ProviderLifecycleState,
2455    pub message: Option<String>,
2456    pub stale: bool,
2457    pub partial: bool,
2458    pub collection_issues: Vec<HeartbeatCollectionIssue>,
2459}
2460
2461#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2462#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
2463#[serde(rename_all = "camelCase")]
2464pub struct AwsEcrArtifactRegistryHeartbeatData {
2465    pub status: ArtifactRegistryHeartbeatStatus,
2466    pub registry_id: String,
2467    pub region: String,
2468    pub registry_uri: String,
2469    pub repository_prefix: String,
2470    pub pull_role_arn: Option<String>,
2471    pub push_role_arn: Option<String>,
2472    pub repository_count: u32,
2473    pub repositories_truncated: bool,
2474    pub repositories: Vec<AwsEcrRepositoryHeartbeatData>,
2475}
2476
2477#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2478#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
2479#[serde(rename_all = "camelCase")]
2480pub struct AwsEcrRepositoryHeartbeatData {
2481    pub repository_arn: String,
2482    pub registry_id: String,
2483    pub repository_name: String,
2484    pub repository_uri: String,
2485    pub created_at: f64,
2486    pub image_tag_mutability: Option<String>,
2487    pub scan_on_push: Option<bool>,
2488    pub encryption_type: Option<String>,
2489    pub kms_key_present: bool,
2490}
2491
2492#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2493#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
2494#[serde(rename_all = "camelCase")]
2495pub struct GcpArtifactRegistryHeartbeatData {
2496    pub status: ArtifactRegistryHeartbeatStatus,
2497    pub project_id: String,
2498    pub location: String,
2499    pub repository_id: String,
2500    pub name: Option<String>,
2501    pub format: Option<String>,
2502    pub mode: Option<String>,
2503    pub description: Option<String>,
2504    pub label_count: u32,
2505    pub cleanup_policy_count: u32,
2506    pub cleanup_policy_dry_run: Option<bool>,
2507    pub kms_key_name_present: bool,
2508    pub size_bytes: Option<String>,
2509    pub satisfies_pzs: Option<bool>,
2510    pub create_time: Option<String>,
2511    pub update_time: Option<String>,
2512    pub iam_policy_etag_present: bool,
2513    pub iam_binding_count: u32,
2514    pub iam_roles: Vec<String>,
2515    pub pull_service_account_email: Option<String>,
2516    pub push_service_account_email: Option<String>,
2517}
2518
2519#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2520#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
2521#[serde(rename_all = "camelCase")]
2522pub struct AzureContainerRegistryHeartbeatData {
2523    pub status: ArtifactRegistryHeartbeatStatus,
2524    pub name: String,
2525    pub resource_id: Option<String>,
2526    pub resource_group: String,
2527    pub location: String,
2528    pub type_: Option<String>,
2529    pub login_server: Option<String>,
2530    pub sku_name: String,
2531    pub sku_tier: Option<String>,
2532    pub provisioning_state: Option<String>,
2533    pub admin_user_enabled: bool,
2534    pub anonymous_pull_enabled: bool,
2535    pub public_network_access: String,
2536    pub network_rule_bypass_options: String,
2537    pub network_rule_default_action: Option<String>,
2538    pub ip_rule_count: u32,
2539    pub encryption_status: Option<String>,
2540    pub encryption_key_vault_uri_present: bool,
2541    pub encryption_key_identifier_present: bool,
2542    pub policies_present: bool,
2543    pub policy_count: u32,
2544    pub private_endpoint_connection_count: u32,
2545    pub data_endpoint_enabled: Option<bool>,
2546    pub data_endpoint_host_names: Vec<String>,
2547    pub zone_redundancy: String,
2548    pub creation_date: Option<String>,
2549    pub managed_tag_count: u32,
2550}
2551
2552#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2553#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
2554#[serde(rename_all = "camelCase")]
2555pub struct LocalArtifactRegistryHeartbeatData {
2556    pub status: ArtifactRegistryHeartbeatStatus,
2557    pub registry_url: String,
2558    pub reachable: bool,
2559}
2560
2561#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2562#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
2563#[serde(tag = "backend", rename_all = "camelCase")]
2564pub enum BuildHeartbeatData {
2565    AwsCodeBuild(AwsCodeBuildHeartbeatData),
2566    GcpCloudBuild(GcpCloudBuildHeartbeatData),
2567    AzureContainerApps(AzureContainerAppsBuildHeartbeatData),
2568    KubernetesJob(KubernetesBuildHeartbeatData),
2569}
2570
2571#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2572#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
2573#[serde(rename_all = "camelCase")]
2574pub struct BuildHeartbeatStatus {
2575    pub health: ObservedHealth,
2576    pub lifecycle: ProviderLifecycleState,
2577    pub message: Option<String>,
2578    pub stale: bool,
2579    pub partial: bool,
2580    pub collection_issues: Vec<HeartbeatCollectionIssue>,
2581}
2582
2583#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2584#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
2585#[serde(rename_all = "camelCase")]
2586pub struct AwsCodeBuildHeartbeatData {
2587    pub status: BuildHeartbeatStatus,
2588    pub project_name: String,
2589    pub project_arn: Option<String>,
2590    pub description: Option<String>,
2591    pub source_type: Option<String>,
2592    pub artifacts_type: Option<String>,
2593    pub artifacts_encryption_disabled: Option<bool>,
2594    pub environment_type: Option<String>,
2595    pub environment_image: Option<String>,
2596    pub compute_type: Option<String>,
2597    pub image_pull_credentials_type: Option<String>,
2598    pub privileged_mode: Option<bool>,
2599    pub environment_variable_count: u32,
2600    pub service_role_present: bool,
2601    pub encryption_key_present: bool,
2602    pub cloud_watch_logs_status: Option<String>,
2603    pub s3_logs_status: Option<String>,
2604    pub timeout_in_minutes: Option<i32>,
2605    pub queued_timeout_in_minutes: Option<i32>,
2606    pub created: Option<f64>,
2607    pub last_modified: Option<f64>,
2608}
2609
2610#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2611#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
2612#[serde(rename_all = "camelCase")]
2613pub struct GcpCloudBuildHeartbeatData {
2614    pub status: BuildHeartbeatStatus,
2615    pub project_id: String,
2616    pub location: String,
2617    pub build_config_id: String,
2618    pub service_account: Option<String>,
2619    pub environment_variable_count: u32,
2620}
2621
2622#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2623#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
2624#[serde(rename_all = "camelCase")]
2625pub struct AzureContainerAppsBuildHeartbeatData {
2626    pub status: BuildHeartbeatStatus,
2627    pub managed_environment_id: String,
2628    pub resource_group_name: String,
2629    pub managed_identity_id: Option<String>,
2630    pub resource_prefix: Option<String>,
2631    pub environment_variable_count: u32,
2632}
2633
2634#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2635#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
2636#[serde(rename_all = "camelCase")]
2637pub struct KubernetesBuildHeartbeatData {
2638    pub status: BuildHeartbeatStatus,
2639    pub job_name: String,
2640    pub namespace: String,
2641    pub active: Option<i32>,
2642    pub succeeded: Option<i32>,
2643    pub failed: Option<i32>,
2644    pub start_time: Option<DateTime<Utc>>,
2645    pub completion_time: Option<DateTime<Utc>>,
2646    pub condition_count: u32,
2647    pub image_digest: Option<String>,
2648    pub events: Vec<KubernetesEventSnapshot>,
2649}
2650
2651#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2652#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
2653#[serde(tag = "backend", rename_all = "camelCase")]
2654pub enum ServiceActivationHeartbeatData {
2655    GcpServiceUsage(GcpServiceUsageActivationHeartbeatData),
2656    AzureResourceProvider(AzureResourceProviderActivationHeartbeatData),
2657}
2658
2659#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2660#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
2661#[serde(rename_all = "camelCase")]
2662pub struct ServiceActivationHeartbeatStatus {
2663    pub health: ObservedHealth,
2664    pub lifecycle: ProviderLifecycleState,
2665    pub message: Option<String>,
2666    pub stale: bool,
2667    pub partial: bool,
2668    pub collection_issues: Vec<HeartbeatCollectionIssue>,
2669}
2670
2671impl Default for ServiceActivationHeartbeatStatus {
2672    fn default() -> Self {
2673        Self {
2674            health: ObservedHealth::Healthy,
2675            lifecycle: ProviderLifecycleState::Running,
2676            message: None,
2677            stale: false,
2678            partial: false,
2679            collection_issues: vec![],
2680        }
2681    }
2682}
2683
2684#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2685#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
2686#[serde(rename_all = "camelCase")]
2687pub struct GcpServiceUsageActivationHeartbeatData {
2688    pub status: ServiceActivationHeartbeatStatus,
2689    pub project_id: String,
2690    pub service_name: String,
2691    pub service_resource_name: Option<String>,
2692    pub title: Option<String>,
2693    pub state: Option<String>,
2694    pub enabled: bool,
2695    pub last_operation_name: Option<String>,
2696}
2697
2698#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2699#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
2700#[serde(rename_all = "camelCase")]
2701pub struct AzureResourceProviderActivationHeartbeatData {
2702    pub status: ServiceActivationHeartbeatStatus,
2703    pub namespace: String,
2704    pub provider_id: Option<String>,
2705    pub registration_state: Option<String>,
2706    pub registration_policy: Option<String>,
2707    pub resource_type_count: u32,
2708    pub registered: bool,
2709}
2710
2711#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2712#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
2713#[serde(rename_all = "camelCase")]
2714pub struct AzureResourceGroupHeartbeatData {
2715    pub status: AzureResourceGroupHeartbeatStatus,
2716    pub name: String,
2717    pub resource_id: Option<String>,
2718    pub location: Option<String>,
2719    pub provisioning_state: Option<String>,
2720    pub managed_tags: BTreeMap<String, String>,
2721}
2722
2723#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2724#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
2725#[serde(rename_all = "camelCase")]
2726pub struct AzureResourceGroupHeartbeatStatus {
2727    pub health: ObservedHealth,
2728    pub lifecycle: ProviderLifecycleState,
2729    pub message: Option<String>,
2730    pub stale: bool,
2731    pub partial: bool,
2732    pub collection_issues: Vec<HeartbeatCollectionIssue>,
2733}
2734
2735impl Default for AzureResourceGroupHeartbeatStatus {
2736    fn default() -> Self {
2737        Self {
2738            health: ObservedHealth::Healthy,
2739            lifecycle: ProviderLifecycleState::Running,
2740            message: None,
2741            stale: false,
2742            partial: false,
2743            collection_issues: vec![],
2744        }
2745    }
2746}
2747
2748#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2749#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
2750#[serde(rename_all = "camelCase")]
2751pub struct AzureStorageAccountHeartbeatData {
2752    pub status: StorageHeartbeatStatus,
2753    pub name: String,
2754    pub resource_id: Option<String>,
2755    pub resource_group: Option<String>,
2756    pub location: Option<String>,
2757    pub kind: Option<String>,
2758    pub sku_name: Option<String>,
2759    pub sku_tier: Option<String>,
2760    pub provisioning_state: Option<String>,
2761    pub primary_endpoints: AzureStorageAccountEndpoints,
2762    pub secondary_endpoints: AzureStorageAccountEndpoints,
2763    pub public_network_access: Option<String>,
2764    pub allow_blob_public_access: Option<bool>,
2765    pub allow_shared_key_access: Option<bool>,
2766    pub minimum_tls_version: Option<String>,
2767    pub supports_https_traffic_only: Option<bool>,
2768    pub encryption_key_source: Option<String>,
2769    pub require_infrastructure_encryption: Option<bool>,
2770    pub network_default_action: Option<String>,
2771    pub network_bypass: Option<String>,
2772    pub network_ip_rule_count: Option<u32>,
2773    pub network_virtual_network_rule_count: Option<u32>,
2774    pub network_resource_access_rule_count: Option<u32>,
2775}
2776
2777#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
2778#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
2779#[serde(rename_all = "camelCase")]
2780pub struct AzureStorageAccountEndpoints {
2781    pub blob: Option<String>,
2782    pub dfs: Option<String>,
2783    pub file: Option<String>,
2784    pub queue: Option<String>,
2785    pub table: Option<String>,
2786    pub web: Option<String>,
2787}
2788
2789#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2790#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
2791#[serde(rename_all = "camelCase")]
2792pub struct AzureContainerAppsEnvironmentHeartbeatData {
2793    pub status: AzureContainerAppsEnvironmentHeartbeatStatus,
2794    pub name: String,
2795    pub resource_id: Option<String>,
2796    pub resource_group: Option<String>,
2797    pub location: Option<String>,
2798    pub kind: Option<String>,
2799    pub provisioning_state: Option<String>,
2800    pub default_domain: Option<String>,
2801    pub static_ip: Option<String>,
2802    pub custom_domain_verification_id: Option<String>,
2803    pub infrastructure_resource_group: Option<String>,
2804    pub event_stream_endpoint: Option<String>,
2805    pub zone_redundant: Option<bool>,
2806    pub workload_profile_count: u32,
2807    pub workload_profiles: Vec<AzureContainerAppsEnvironmentWorkloadProfile>,
2808}
2809
2810#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2811#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
2812#[serde(rename_all = "camelCase")]
2813pub struct AzureContainerAppsEnvironmentHeartbeatStatus {
2814    pub health: ObservedHealth,
2815    pub lifecycle: ProviderLifecycleState,
2816    pub message: Option<String>,
2817    pub stale: bool,
2818    pub partial: bool,
2819    pub collection_issues: Vec<HeartbeatCollectionIssue>,
2820}
2821
2822#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
2823#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
2824#[serde(rename_all = "camelCase")]
2825pub struct AzureContainerAppsEnvironmentWorkloadProfile {
2826    pub name: String,
2827    pub workload_profile_type: String,
2828    pub minimum_count: Option<i32>,
2829    pub maximum_count: Option<i32>,
2830}
2831
2832#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2833#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
2834#[serde(rename_all = "camelCase")]
2835pub struct AzureServiceBusNamespaceHeartbeatData {
2836    pub status: QueueHeartbeatStatus,
2837    pub name: String,
2838    pub resource_id: Option<String>,
2839    pub resource_group: Option<String>,
2840    pub location: Option<String>,
2841    pub sku_name: Option<String>,
2842    pub sku_tier: Option<String>,
2843    pub sku_capacity: Option<i32>,
2844    pub namespace_status: Option<String>,
2845    pub provisioning_state: Option<String>,
2846    pub service_bus_endpoint: Option<String>,
2847    pub metric_id: Option<String>,
2848    pub public_network_access: Option<String>,
2849    pub disable_local_auth: Option<bool>,
2850    pub minimum_tls_version: Option<String>,
2851    pub premium_messaging_partitions: Option<i32>,
2852    pub private_endpoint_connection_count: u32,
2853    pub zone_redundant: Option<bool>,
2854    pub created_at: Option<String>,
2855    pub updated_at: Option<String>,
2856}
2857
2858#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
2859#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
2860#[serde(rename_all = "camelCase")]
2861pub struct NamedResourceDetail {
2862    pub name: String,
2863    pub region: Option<String>,
2864}
2865
2866#[cfg(test)]
2867mod tests {
2868    use super::*;
2869    use chrono::TimeZone as _;
2870    use serde_json::json;
2871
2872    fn observed_at() -> DateTime<Utc> {
2873        Utc.with_ymd_and_hms(2026, 5, 28, 10, 30, 0).unwrap()
2874    }
2875
2876    fn workload_status() -> WorkloadHeartbeatStatus {
2877        WorkloadHeartbeatStatus {
2878            health: ObservedHealth::Healthy,
2879            lifecycle: ProviderLifecycleState::Running,
2880            message: None,
2881            stale: false,
2882            partial: false,
2883            collection_issues: vec![],
2884        }
2885    }
2886
2887    fn workload_replicas() -> WorkloadReplicaStatus {
2888        WorkloadReplicaStatus {
2889            desired: Some(2),
2890            current: Some(2),
2891            ready: Some(1),
2892            available: Some(1),
2893            updated: Some(2),
2894            misscheduled: None,
2895        }
2896    }
2897
2898    fn heartbeat(data: ResourceHeartbeatData, resource_type: &str) -> ResourceHeartbeat {
2899        ResourceHeartbeat {
2900            deployment_id: Some("dep_123".to_string()),
2901            resource_id: "api".to_string(),
2902            resource_type: ResourceType::from(resource_type),
2903            controller_platform: Platform::Kubernetes,
2904            backend: HeartbeatBackend::Kubernetes,
2905            observed_at: observed_at(),
2906            data,
2907            raw: vec![RawHeartbeatSnippet {
2908                source: "kubernetes/apps/v1/deployments/api".to_string(),
2909                format: RawHeartbeatSnippetFormat::Json,
2910                collected_at: observed_at(),
2911                body: r#"{"readyReplicas":1}"#.to_string(),
2912                truncated: false,
2913            }],
2914        }
2915    }
2916
2917    #[test]
2918    fn resource_heartbeat_roundtrips_managed_resource_data() {
2919        let heartbeat_json = serde_json::to_value(heartbeat(
2920            ResourceHeartbeatData::Container(ContainerHeartbeatData::Kubernetes(
2921                KubernetesContainerHeartbeatData {
2922                    status: workload_status(),
2923                    namespace: "default".to_string(),
2924                    name: "api".to_string(),
2925                    workload_kind: KubernetesWorkloadKind::Deployment,
2926                    replicas: workload_replicas(),
2927                    restarts: None,
2928                    cpu: None,
2929                    memory: None,
2930                    workload: None,
2931                    pods: vec![],
2932                    events: vec![],
2933                },
2934            )),
2935            "container",
2936        ))
2937        .unwrap();
2938
2939        let parsed: ResourceHeartbeat = serde_json::from_value(heartbeat_json).unwrap();
2940        assert_eq!(parsed.resource_id, "api");
2941        assert!(matches!(
2942            parsed.data,
2943            ResourceHeartbeatData::Container(ContainerHeartbeatData::Kubernetes(_))
2944        ));
2945    }
2946
2947    #[test]
2948    fn local_daemon_heartbeat_defaults_missing_daemon_name() {
2949        let mut value =
2950            serde_json::to_value(DaemonHeartbeatData::Local(LocalDaemonHeartbeatData {
2951                status: workload_status(),
2952                daemon_name: "agent".to_string(),
2953                runtime_id: "local-agent".to_string(),
2954                pid: None,
2955                command_supported: false,
2956                image_path_present: true,
2957                restart_count: None,
2958                exit_reason: None,
2959                daemon_instance: None,
2960                events: vec![],
2961            }))
2962            .unwrap();
2963
2964        value.as_object_mut().unwrap().remove("daemonName");
2965
2966        let parsed: DaemonHeartbeatData = serde_json::from_value(value).unwrap();
2967        match parsed {
2968            DaemonHeartbeatData::Local(data) => assert_eq!(data.daemon_name, ""),
2969            other => panic!("expected local daemon heartbeat, got {other:?}"),
2970        }
2971    }
2972
2973    #[test]
2974    fn container_heartbeat_serializes_resource_first_data() {
2975        let heartbeat = heartbeat(
2976            ResourceHeartbeatData::Container(ContainerHeartbeatData::Kubernetes(
2977                KubernetesContainerHeartbeatData {
2978                    status: workload_status(),
2979                    namespace: "default".to_string(),
2980                    name: "api".to_string(),
2981                    workload_kind: KubernetesWorkloadKind::Deployment,
2982                    replicas: workload_replicas(),
2983                    restarts: Some(1),
2984                    cpu: Some(MetricSample {
2985                        value: 0.5,
2986                        unit: MetricUnit::Cores,
2987                    }),
2988                    memory: None,
2989                    workload: None,
2990                    pods: vec![],
2991                    events: vec![],
2992                },
2993            )),
2994            "container",
2995        );
2996
2997        let value = serde_json::to_value(&heartbeat).unwrap();
2998
2999        assert_eq!(value["resourceType"], "container");
3000        assert_eq!(value["data"]["resourceType"], "container");
3001        assert_eq!(value["data"]["data"]["backend"], "kubernetes");
3002        assert_eq!(value["raw"][0]["body"], r#"{"readyReplicas":1}"#);
3003        assert!(value.get("collection").is_none());
3004        assert!(value["data"]["data"].get("summary").is_none());
3005        assert!(value["data"]["data"].get("detail").is_none());
3006    }
3007
3008    #[test]
3009    fn representative_workload_data_has_stable_tags() {
3010        let daemon = serde_json::to_value(ResourceHeartbeatData::Daemon(
3011            DaemonHeartbeatData::Kubernetes(KubernetesDaemonHeartbeatData {
3012                status: workload_status(),
3013                namespace: "default".to_string(),
3014                name: "agent".to_string(),
3015                replicas: workload_replicas(),
3016                restarts: Some(0),
3017                command_supported: true,
3018                cpu: None,
3019                memory: None,
3020                workload: None,
3021                pods: vec![],
3022                events: vec![],
3023            }),
3024        ))
3025        .unwrap();
3026        let worker = serde_json::to_value(ResourceHeartbeatData::Worker(
3027            WorkerHeartbeatData::AwsLambda(AwsLambdaWorkerHeartbeatData {
3028                status: workload_status(),
3029                function_name: "handler".to_string(),
3030                runtime: Some("nodejs22.x".to_string()),
3031                package_type: None,
3032                memory_size_mb: None,
3033                timeout_seconds: None,
3034                version: None,
3035                revision_id: None,
3036                last_modified: None,
3037                state: None,
3038                state_reason: None,
3039                state_reason_code: None,
3040                last_update_status: None,
3041                last_update_status_reason: None,
3042                last_update_status_reason_code: None,
3043                code_sha256: None,
3044                layer_count: 0,
3045                function_url_auth_type: None,
3046                function_url_cors_present: false,
3047                trigger_count: 0,
3048            }),
3049        ))
3050        .unwrap();
3051
3052        assert_eq!(daemon["resourceType"], "daemon");
3053        assert_eq!(daemon["data"]["backend"], "kubernetes");
3054        assert_eq!(worker["resourceType"], "worker");
3055        assert_eq!(worker["data"]["backend"], "awsLambda");
3056    }
3057
3058    /// A variant that compiles can still be unreadable on the wire: the transport round-trips every
3059    /// heartbeat through the generated manager type, which matches on the tag string alone.
3060    #[test]
3061    fn gcp_sandbox_heartbeat_keeps_its_wire_tag_and_camel_case_names() {
3062        let data = ResourceHeartbeatData::Sandbox(SandboxHeartbeatData::GcpAgentPlatform(
3063            GcpAgentPlatformSandboxHeartbeatData {
3064                status: SandboxHeartbeatStatus::default(),
3065                engine: "eng".to_string(),
3066                template_id: "tpl1".to_string(),
3067            },
3068        ));
3069
3070        let value = serde_json::to_value(&data).unwrap();
3071        assert_eq!(
3072            value,
3073            json!({
3074                "resourceType": "sandbox",
3075                "data": {
3076                    "backend": "gcpAgentPlatform",
3077                    "status": {
3078                        "health": "healthy",
3079                        "lifecycle": "running",
3080                        "message": null,
3081                        "stale": false,
3082                        "partial": false,
3083                        "collectionIssues": []
3084                    },
3085                    "engine": "eng",
3086                    "templateId": "tpl1"
3087                }
3088            })
3089        );
3090
3091        let parsed: ResourceHeartbeatData = serde_json::from_value(value).unwrap();
3092        assert_eq!(parsed, data);
3093    }
3094
3095    #[test]
3096    fn representative_cluster_and_data_variants_have_optional_counts() {
3097        let cluster = serde_json::to_value(ResourceHeartbeatData::KubernetesCluster(
3098            KubernetesClusterHeartbeatData {
3099                status: WorkloadHeartbeatStatus {
3100                    health: ObservedHealth::Healthy,
3101                    lifecycle: ProviderLifecycleState::Running,
3102                    message: None,
3103                    stale: false,
3104                    partial: false,
3105                    collection_issues: vec![],
3106                },
3107                node_counts: ObservedCounts {
3108                    desired: Some(3),
3109                    current: Some(3),
3110                    ready: None,
3111                },
3112                pod_counts: ObservedCounts {
3113                    desired: None,
3114                    current: Some(12),
3115                    ready: Some(11),
3116                },
3117                cpu: None,
3118                memory: None,
3119                name: "prod".to_string(),
3120                region: Some("us-east-1".to_string()),
3121                namespace: None,
3122                version: Some("1.33".to_string()),
3123                node_statuses: vec![],
3124                events: vec![],
3125            },
3126        ))
3127        .unwrap();
3128        let queue = serde_json::to_value(ResourceHeartbeatData::Queue(QueueHeartbeatData::AwsSqs(
3129            AwsSqsQueueHeartbeatData {
3130                status: QueueHeartbeatStatus {
3131                    health: ObservedHealth::Healthy,
3132                    lifecycle: ProviderLifecycleState::Running,
3133                    message: None,
3134                    stale: false,
3135                    partial: false,
3136                    collection_issues: vec![],
3137                },
3138                name: "jobs".to_string(),
3139                region: Some("us-east-1".to_string()),
3140                queue_url: Some("https://sqs.us-east-1.amazonaws.com/123/jobs".to_string()),
3141                queue_arn: Some("arn:aws:sqs:us-east-1:123:jobs".to_string()),
3142                visibility_timeout_seconds: Some(30),
3143                message_retention_period_seconds: Some(345600),
3144                delay_seconds: Some(0),
3145                receive_message_wait_time_seconds: Some(0),
3146                maximum_message_size: Some(262144),
3147                redrive_policy: None,
3148                redrive_allow_policy: None,
3149                fifo_queue: Some(false),
3150                content_based_deduplication: None,
3151                deduplication_scope: None,
3152                fifo_throughput_limit: None,
3153                sse_enabled: Some(false),
3154                kms_master_key_id: None,
3155                kms_data_key_reuse_period_seconds: None,
3156                sqs_managed_sse_enabled: Some(false),
3157                approximate_visible_messages: Some(42),
3158                approximate_in_flight_messages: Some(1),
3159                approximate_delayed_messages: Some(0),
3160                approximate_counts: true,
3161            },
3162        )))
3163        .unwrap();
3164        let storage = serde_json::to_value(ResourceHeartbeatData::Storage(
3165            StorageHeartbeatData::AwsS3(AwsS3StorageHeartbeatData {
3166                status: StorageHeartbeatStatus {
3167                    health: ObservedHealth::Healthy,
3168                    lifecycle: ProviderLifecycleState::Running,
3169                    message: None,
3170                    stale: false,
3171                    partial: false,
3172                    collection_issues: vec![],
3173                },
3174                name: "assets".to_string(),
3175                region: Some("us-east-1".to_string()),
3176                bucket_location: Some("us-east-1".to_string()),
3177                versioning_status: Some("Enabled".to_string()),
3178                versioning_enabled: Some(true),
3179                lifecycle_present: false,
3180                lifecycle_rule_count: Some(0),
3181                encryption_config_present: true,
3182                encryption_enabled: Some(true),
3183                public_access_block_present: true,
3184                block_public_acls: Some(true),
3185                ignore_public_acls: Some(true),
3186                block_public_policy: Some(true),
3187                restrict_public_buckets: Some(true),
3188                bucket_policy_present: Some(false),
3189                bucket_acl_present: Some(true),
3190            }),
3191        ))
3192        .unwrap();
3193        let gcp_storage = serde_json::to_value(ResourceHeartbeatData::Storage(
3194            StorageHeartbeatData::GcpCloudStorage(GcpCloudStorageHeartbeatData {
3195                status: StorageHeartbeatStatus {
3196                    health: ObservedHealth::Healthy,
3197                    lifecycle: ProviderLifecycleState::Running,
3198                    message: None,
3199                    stale: false,
3200                    partial: false,
3201                    collection_issues: vec![],
3202                },
3203                name: "assets".to_string(),
3204                bucket_id: Some("project/assets".to_string()),
3205                location: Some("US".to_string()),
3206                location_type: Some("multi-region".to_string()),
3207                storage_class: Some("STANDARD".to_string()),
3208                versioning_enabled: Some(true),
3209                lifecycle_present: false,
3210                lifecycle_rule_count: Some(0),
3211                retention_policy_effective_time: None,
3212                retention_policy_is_locked: None,
3213                retention_period: None,
3214                soft_delete_retention_duration_seconds: None,
3215                soft_delete_effective_time: None,
3216                uniform_bucket_level_access_enabled: Some(true),
3217                uniform_bucket_level_access_locked_time: None,
3218                public_access_prevention: Some("enforced".to_string()),
3219                encryption_config_present: true,
3220                default_kms_key_name: Some(
3221                    "projects/p/locations/l/keyRings/r/cryptoKeys/k".to_string(),
3222                ),
3223            }),
3224        ))
3225        .unwrap();
3226        let kv = serde_json::to_value(ResourceHeartbeatData::Kv(KvHeartbeatData::AwsDynamoDb(
3227            AwsDynamoDbKvHeartbeatData {
3228                status: KvHeartbeatStatus {
3229                    health: ObservedHealth::Healthy,
3230                    lifecycle: ProviderLifecycleState::Running,
3231                    message: None,
3232                    stale: false,
3233                    partial: false,
3234                    collection_issues: vec![],
3235                },
3236                name: "state".to_string(),
3237                region: Some("us-east-1".to_string()),
3238                table_arn: None,
3239                table_status: Some("ACTIVE".to_string()),
3240                billing_mode: Some("PAY_PER_REQUEST".to_string()),
3241                key_schema: vec![AwsDynamoDbKeySchemaElement {
3242                    attribute_name: "pk".to_string(),
3243                    key_type: "HASH".to_string(),
3244                }],
3245                global_secondary_index_count: Some(0),
3246                local_secondary_index_count: Some(0),
3247                item_count: None,
3248                table_size_bytes: None,
3249                stream_enabled: Some(false),
3250                stream_view_type: None,
3251                ttl_status: Some("ENABLED".to_string()),
3252                ttl_attribute_name: Some("ttl".to_string()),
3253                deletion_protection_enabled: Some(false),
3254                sse_status: Some("ENABLED".to_string()),
3255                sse_type: Some("KMS".to_string()),
3256                table_class: None,
3257                replica_count: Some(0),
3258                restore_in_progress: None,
3259            },
3260        )))
3261        .unwrap();
3262
3263        assert_eq!(cluster["resourceType"], "kubernetes-cluster");
3264        assert!(cluster["data"].get("summary").is_none());
3265        assert!(cluster["data"].get("detail").is_none());
3266        assert_eq!(cluster["data"]["name"], "prod");
3267        assert_eq!(queue["data"]["backend"], "awsSqs");
3268        assert_eq!(queue["data"]["approximateVisibleMessages"], 42);
3269        assert!(queue["data"].get("summary").is_none());
3270        assert_eq!(storage["data"]["backend"], "awsS3");
3271        assert!(storage["data"].get("summary").is_none());
3272        assert_eq!(gcp_storage["data"]["backend"], "gcpCloudStorage");
3273        assert_eq!(gcp_storage["data"]["publicAccessPrevention"], "enforced");
3274        assert_eq!(kv["data"]["backend"], "awsDynamoDb");
3275        assert!(kv["data"].get("summary").is_none());
3276        assert_eq!(kv["data"]["itemCount"], json!(null));
3277    }
3278
3279    #[test]
3280    fn ai_heartbeat_serializes_stable_tags() {
3281        let aws = serde_json::to_value(ResourceHeartbeatData::Ai(AiHeartbeatData::AwsBedrock(
3282            AwsBedrockAiHeartbeatData {
3283                status: AiHeartbeatStatus::default(),
3284                region: "us-east-1".to_string(),
3285                availability: AiAvailabilityObservation::unobserved(
3286                    AiAvailabilitySource::AwsBedrock,
3287                    Platform::Aws,
3288                    Some("us-east-1".to_string()),
3289                ),
3290            },
3291        )))
3292        .unwrap();
3293        let gcp = serde_json::to_value(ResourceHeartbeatData::Ai(AiHeartbeatData::GcpVertex(
3294            GcpVertexAiHeartbeatData {
3295                status: AiHeartbeatStatus::default(),
3296                project: "my-project".to_string(),
3297                location: "us-central1".to_string(),
3298                availability: AiAvailabilityObservation::unobserved(
3299                    AiAvailabilitySource::GcpVertex,
3300                    Platform::Gcp,
3301                    Some("us-central1".to_string()),
3302                ),
3303            },
3304        )))
3305        .unwrap();
3306        let azure = serde_json::to_value(ResourceHeartbeatData::Ai(AiHeartbeatData::AzureFoundry(
3307            AzureFoundryAiHeartbeatData {
3308                status: AiHeartbeatStatus::default(),
3309                account_name: "my-ai-account".to_string(),
3310                endpoint: Some("https://my-ai-account.openai.azure.com/".to_string()),
3311                resource_group: Some("my-rg".to_string()),
3312                location: Some("eastus".to_string()),
3313                availability: AiAvailabilityObservation::unobserved(
3314                    AiAvailabilitySource::AzureFoundry,
3315                    Platform::Azure,
3316                    Some("eastus".to_string()),
3317                ),
3318            },
3319        )))
3320        .unwrap();
3321
3322        assert_eq!(aws["resourceType"], "ai");
3323        assert_eq!(aws["data"]["backend"], "awsBedrock");
3324        assert_eq!(aws["data"]["region"], "us-east-1");
3325        assert!(aws["data"].get("summary").is_none());
3326
3327        assert_eq!(gcp["resourceType"], "ai");
3328        assert_eq!(gcp["data"]["backend"], "gcpVertex");
3329        assert_eq!(gcp["data"]["project"], "my-project");
3330        assert_eq!(gcp["data"]["location"], "us-central1");
3331
3332        assert_eq!(azure["resourceType"], "ai");
3333        assert_eq!(azure["data"]["backend"], "azureFoundry");
3334        assert_eq!(azure["data"]["accountName"], "my-ai-account");
3335        assert_eq!(
3336            azure["data"]["endpoint"],
3337            "https://my-ai-account.openai.azure.com/"
3338        );
3339    }
3340}