mssf_core/client/
health_client.rs

1// ------------------------------------------------------------
2// Copyright (c) Microsoft Corporation.  All rights reserved.
3// Licensed under the MIT License (MIT). See License.txt in the repo root for license information.
4// ------------------------------------------------------------
5
6use std::time::Duration;
7
8use mssf_com::{
9    FabricClient::{IFabricHealthClient4, IFabricNodeHealthResult},
10    FabricTypes::{
11        FABRIC_APPLICATION_HEALTH_REPORT, FABRIC_CLUSTER_HEALTH_POLICY,
12        FABRIC_CLUSTER_HEALTH_QUERY_DESCRIPTION, FABRIC_CLUSTER_HEALTH_REPORT,
13        FABRIC_DEPLOYED_APPLICATION_HEALTH_REPORT, FABRIC_DEPLOYED_SERVICE_PACKAGE_HEALTH_REPORT,
14        FABRIC_HEALTH_INFORMATION, FABRIC_HEALTH_REPORT, FABRIC_HEALTH_REPORT_KIND_APPLICATION,
15        FABRIC_HEALTH_REPORT_KIND_CLUSTER, FABRIC_HEALTH_REPORT_KIND_DEPLOYED_APPLICATION,
16        FABRIC_HEALTH_REPORT_KIND_DEPLOYED_SERVICE_PACKAGE, FABRIC_HEALTH_REPORT_KIND_INVALID,
17        FABRIC_HEALTH_REPORT_KIND_NODE, FABRIC_HEALTH_REPORT_KIND_PARTITION,
18        FABRIC_HEALTH_REPORT_KIND_SERVICE, FABRIC_HEALTH_REPORT_KIND_STATEFUL_SERVICE_REPLICA,
19        FABRIC_HEALTH_REPORT_KIND_STATELESS_SERVICE_INSTANCE, FABRIC_NODE_HEALTH_QUERY_DESCRIPTION,
20        FABRIC_NODE_HEALTH_REPORT, FABRIC_PARTITION_HEALTH_REPORT, FABRIC_SERVICE_HEALTH_REPORT,
21        FABRIC_STATEFUL_SERVICE_REPLICA_HEALTH_REPORT,
22        FABRIC_STATELESS_SERVICE_INSTANCE_HEALTH_REPORT, FABRIC_URI,
23    },
24};
25
26use crate::{
27    mem::{BoxPool, GetRawWithBoxPool},
28    runtime::executor::BoxedCancelToken,
29    sync::{FabricReceiver, fabric_begin_end_proxy},
30    types::{ClusterHealth, HealthReport, NodeHealthQueryDescription, NodeHealthResult},
31};
32
33/// Provides functionality to perform health related operations, like report and query health.
34/// See C# API [here](https://docs.microsoft.com/en-us/dotnet/api/system.fabric.fabricclient.healthclient?view=azure-dotnet).
35///
36/// TODO: Implement full functionality of the HealthClient.
37#[derive(Debug, Clone)]
38pub struct HealthClient {
39    com: IFabricHealthClient4,
40}
41
42impl From<IFabricHealthClient4> for HealthClient {
43    fn from(value: IFabricHealthClient4) -> Self {
44        Self { com: value }
45    }
46}
47
48impl From<HealthClient> for IFabricHealthClient4 {
49    fn from(value: HealthClient) -> Self {
50        value.com
51    }
52}
53
54// Public implementation block
55impl HealthClient {
56    /// Reports health on a Service Fabric entity. See C# API [here](https://docs.microsoft.com/en-us/dotnet/api/system.fabric.fabricclient.healthclient.reporthealth?view=azure-dotnet).
57    ///
58    /// Remarks:
59    /// When a cluster is secured, the health client needs administrator permission to be able to send the reports.
60    /// Read more about [connecting to a cluster using the FabricClient APIs](https://learn.microsoft.com/en-us/azure/service-fabric/service-fabric-connect-to-secure-cluster).
61    /// For more information about health reporting, see [Service Fabric health monitoring](https://learn.microsoft.com/en-us/azure/service-fabric/service-fabric-health-introduction).
62    pub fn report_health(&self, health_report: &HealthReport) -> crate::Result<()> {
63        match health_report {
64            HealthReport::Invalid => {
65                let fabric_health_report = FABRIC_HEALTH_REPORT {
66                    Kind: FABRIC_HEALTH_REPORT_KIND_INVALID,
67                    Value: std::ptr::null_mut(),
68                };
69                unsafe { self.com.ReportHealth(&fabric_health_report) }
70            }
71            HealthReport::StatefulServiceReplica(health_report) => {
72                let fabric_health_info =
73                    FABRIC_HEALTH_INFORMATION::from(&health_report.health_information);
74                let fabric_health_report_value = FABRIC_STATEFUL_SERVICE_REPLICA_HEALTH_REPORT {
75                    PartitionId: health_report.partition_id,
76                    ReplicaId: health_report.replica_id,
77                    HealthInformation: &fabric_health_info,
78                    Reserved: std::ptr::null_mut(),
79                };
80                let fabric_health_report = FABRIC_HEALTH_REPORT {
81                    Kind: FABRIC_HEALTH_REPORT_KIND_STATEFUL_SERVICE_REPLICA,
82                    Value: &fabric_health_report_value as *const _ as *mut _,
83                };
84                unsafe { self.com.ReportHealth(&fabric_health_report) }
85            }
86            HealthReport::StatelessServiceInstance(health_report) => {
87                let fabric_health_info =
88                    FABRIC_HEALTH_INFORMATION::from(&health_report.health_information);
89                let fabric_health_report_value = FABRIC_STATELESS_SERVICE_INSTANCE_HEALTH_REPORT {
90                    PartitionId: health_report.partition_id,
91                    InstanceId: health_report.instance_id,
92                    HealthInformation: &fabric_health_info,
93                    Reserved: std::ptr::null_mut(),
94                };
95                let fabric_health_report = FABRIC_HEALTH_REPORT {
96                    Kind: FABRIC_HEALTH_REPORT_KIND_STATELESS_SERVICE_INSTANCE,
97                    Value: &fabric_health_report_value as *const _ as *mut _,
98                };
99                unsafe { self.com.ReportHealth(&fabric_health_report) }
100            }
101            HealthReport::Partition(health_report) => {
102                let fabric_health_info =
103                    FABRIC_HEALTH_INFORMATION::from(&health_report.health_information);
104                let fabric_health_report_value = FABRIC_PARTITION_HEALTH_REPORT {
105                    PartitionId: health_report.partition_id,
106                    HealthInformation: &fabric_health_info,
107                    Reserved: std::ptr::null_mut(),
108                };
109                let fabric_health_report = FABRIC_HEALTH_REPORT {
110                    Kind: FABRIC_HEALTH_REPORT_KIND_PARTITION,
111                    Value: &fabric_health_report_value as *const _ as *mut _,
112                };
113                unsafe { self.com.ReportHealth(&fabric_health_report) }
114            }
115            HealthReport::Node(health_report) => {
116                let fabric_health_info =
117                    FABRIC_HEALTH_INFORMATION::from(&health_report.health_information);
118                let fabric_health_report_value = FABRIC_NODE_HEALTH_REPORT {
119                    NodeName: health_report.node_name.as_pcwstr(),
120                    HealthInformation: &fabric_health_info,
121                    Reserved: std::ptr::null_mut(),
122                };
123                let fabric_health_report = FABRIC_HEALTH_REPORT {
124                    Kind: FABRIC_HEALTH_REPORT_KIND_NODE,
125                    Value: &fabric_health_report_value as *const _ as *mut _,
126                };
127                unsafe { self.com.ReportHealth(&fabric_health_report) }
128            }
129            HealthReport::Service(health_report) => {
130                let fabric_health_info =
131                    FABRIC_HEALTH_INFORMATION::from(&health_report.health_information);
132                let fabric_health_report_value = FABRIC_SERVICE_HEALTH_REPORT {
133                    ServiceName: FABRIC_URI(health_report.service_name.as_ptr() as *mut u16),
134                    HealthInformation: &fabric_health_info,
135                    Reserved: std::ptr::null_mut(),
136                };
137                let fabric_health_report = FABRIC_HEALTH_REPORT {
138                    Kind: FABRIC_HEALTH_REPORT_KIND_SERVICE,
139                    Value: &fabric_health_report_value as *const _ as *mut _,
140                };
141                unsafe { self.com.ReportHealth(&fabric_health_report) }
142            }
143            HealthReport::Application(health_report) => {
144                let fabric_health_info =
145                    FABRIC_HEALTH_INFORMATION::from(&health_report.health_information);
146                let fabric_health_report_value = FABRIC_APPLICATION_HEALTH_REPORT {
147                    ApplicationName: FABRIC_URI(health_report.application_name.as_ptr() as *mut u16),
148                    HealthInformation: &fabric_health_info,
149                    Reserved: std::ptr::null_mut(),
150                };
151                let fabric_health_report = FABRIC_HEALTH_REPORT {
152                    Kind: FABRIC_HEALTH_REPORT_KIND_APPLICATION,
153                    Value: &fabric_health_report_value as *const _ as *mut _,
154                };
155                unsafe { self.com.ReportHealth(&fabric_health_report) }
156            }
157            HealthReport::DeployedApplication(health_report) => {
158                let fabric_health_info =
159                    FABRIC_HEALTH_INFORMATION::from(&health_report.health_information);
160                let fabric_health_report_value = FABRIC_DEPLOYED_APPLICATION_HEALTH_REPORT {
161                    ApplicationName: FABRIC_URI(health_report.application_name.as_ptr() as *mut u16),
162                    NodeName: health_report.node_name.as_pcwstr(),
163                    HealthInformation: &fabric_health_info,
164                    Reserved: std::ptr::null_mut(),
165                };
166                let fabric_health_report = FABRIC_HEALTH_REPORT {
167                    Kind: FABRIC_HEALTH_REPORT_KIND_DEPLOYED_APPLICATION,
168                    Value: &fabric_health_report_value as *const _ as *mut _,
169                };
170                unsafe { self.com.ReportHealth(&fabric_health_report) }
171            }
172            HealthReport::DeployedServicePackage(health_report) => {
173                let fabric_health_info =
174                    FABRIC_HEALTH_INFORMATION::from(&health_report.health_information);
175                let fabric_health_report_value = FABRIC_DEPLOYED_SERVICE_PACKAGE_HEALTH_REPORT {
176                    ApplicationName: FABRIC_URI(health_report.application_name.as_ptr() as *mut u16),
177                    ServiceManifestName: health_report.service_manifest_name.as_pcwstr(),
178                    NodeName: health_report.node_name.as_pcwstr(),
179                    HealthInformation: &fabric_health_info,
180                    Reserved: std::ptr::null_mut(),
181                };
182                let fabric_health_report = FABRIC_HEALTH_REPORT {
183                    Kind: FABRIC_HEALTH_REPORT_KIND_DEPLOYED_SERVICE_PACKAGE,
184                    Value: &fabric_health_report_value as *const _ as *mut _,
185                };
186                unsafe { self.com.ReportHealth(&fabric_health_report) }
187            }
188            HealthReport::Cluster(health_report) => {
189                let fabric_health_info =
190                    FABRIC_HEALTH_INFORMATION::from(&health_report.health_information);
191                let fabric_health_report_value = FABRIC_CLUSTER_HEALTH_REPORT {
192                    HealthInformation: &fabric_health_info,
193                    Reserved: std::ptr::null_mut(),
194                };
195                let fabric_health_report = FABRIC_HEALTH_REPORT {
196                    Kind: FABRIC_HEALTH_REPORT_KIND_CLUSTER,
197                    Value: &fabric_health_report_value as *const _ as *mut _,
198                };
199                unsafe { self.com.ReportHealth(&fabric_health_report) }
200            }
201        }.map_err(crate::Error::from)
202    }
203}
204
205impl HealthClient {
206    fn get_node_health_internal(
207        &self,
208        desc: &FABRIC_NODE_HEALTH_QUERY_DESCRIPTION,
209        timeout_milliseconds: u32,
210        cancellation_token: Option<BoxedCancelToken>,
211    ) -> FabricReceiver<crate::WinResult<IFabricNodeHealthResult>> {
212        let com1 = &self.com;
213        let com2 = self.com.clone();
214        fabric_begin_end_proxy(
215            move |callback| unsafe {
216                com1.BeginGetNodeHealth2(desc, timeout_milliseconds, callback)
217            },
218            move |ctx| unsafe { com2.EndGetNodeHealth2(ctx) },
219            cancellation_token,
220        )
221    }
222
223    fn get_cluster_health_internal(
224        &self,
225        desc: &FABRIC_CLUSTER_HEALTH_QUERY_DESCRIPTION,
226        timeout_milliseconds: u32,
227        cancellation_token: Option<BoxedCancelToken>,
228    ) -> FabricReceiver<crate::WinResult<mssf_com::FabricClient::IFabricClusterHealthResult>> {
229        let com1 = &self.com;
230        let com2 = self.com.clone();
231        fabric_begin_end_proxy(
232            move |callback| unsafe {
233                com1.BeginGetClusterHealth2(desc, timeout_milliseconds, callback)
234            },
235            move |ctx| unsafe { com2.EndGetClusterHealth2(ctx) },
236            cancellation_token,
237        )
238    }
239
240    fn get_application_health_internal(
241        &self,
242        desc: &mssf_com::FabricTypes::FABRIC_APPLICATION_HEALTH_QUERY_DESCRIPTION,
243        timeout_milliseconds: u32,
244        cancellation_token: Option<BoxedCancelToken>,
245    ) -> FabricReceiver<crate::WinResult<mssf_com::FabricClient::IFabricApplicationHealthResult>>
246    {
247        let com1 = &self.com;
248        let com2 = self.com.clone();
249        fabric_begin_end_proxy(
250            move |callback| unsafe {
251                com1.BeginGetApplicationHealth2(desc, timeout_milliseconds, callback)
252            },
253            move |ctx| unsafe { com2.EndGetApplicationHealth2(ctx) },
254            cancellation_token,
255        )
256    }
257    fn get_partition_health_internal(
258        &self,
259        desc: &mssf_com::FabricTypes::FABRIC_PARTITION_HEALTH_QUERY_DESCRIPTION,
260        timeout_milliseconds: u32,
261        cancellation_token: Option<BoxedCancelToken>,
262    ) -> FabricReceiver<crate::WinResult<mssf_com::FabricClient::IFabricPartitionHealthResult>>
263    {
264        let com1 = &self.com;
265        let com2 = self.com.clone();
266        fabric_begin_end_proxy(
267            move |callback| unsafe {
268                com1.BeginGetPartitionHealth2(desc, timeout_milliseconds, callback)
269            },
270            move |ctx| unsafe { com2.EndGetPartitionHealth2(ctx) },
271            cancellation_token,
272        )
273    }
274    fn get_service_health_internal(
275        &self,
276        desc: &mssf_com::FabricTypes::FABRIC_SERVICE_HEALTH_QUERY_DESCRIPTION,
277        timeout_milliseconds: u32,
278        cancellation_token: Option<BoxedCancelToken>,
279    ) -> FabricReceiver<crate::WinResult<mssf_com::FabricClient::IFabricServiceHealthResult>> {
280        let com1 = &self.com;
281        let com2 = self.com.clone();
282        fabric_begin_end_proxy(
283            move |callback| unsafe {
284                com1.BeginGetServiceHealth2(desc, timeout_milliseconds, callback)
285            },
286            move |ctx| unsafe { com2.EndGetServiceHealth2(ctx) },
287            cancellation_token,
288        )
289    }
290    fn get_replica_health_internal(
291        &self,
292        desc: &mssf_com::FabricTypes::FABRIC_REPLICA_HEALTH_QUERY_DESCRIPTION,
293        timeout_milliseconds: u32,
294        cancellation_token: Option<BoxedCancelToken>,
295    ) -> FabricReceiver<crate::WinResult<mssf_com::FabricClient::IFabricReplicaHealthResult>> {
296        let com1 = &self.com;
297        let com2 = self.com.clone();
298        fabric_begin_end_proxy(
299            move |callback| unsafe {
300                com1.BeginGetReplicaHealth2(desc, timeout_milliseconds, callback)
301            },
302            move |ctx| unsafe { com2.EndGetReplicaHealth2(ctx) },
303            cancellation_token,
304        )
305    }
306}
307
308impl HealthClient {
309    /// Gets the health of a node.
310    ///
311    /// See C# API [here](https://learn.microsoft.com/en-us/dotnet/api/system.fabric.fabricclient.healthclient.getnodehealthasync?view=azure-dotnet).
312    pub async fn get_node_health(
313        &self,
314        desc: &NodeHealthQueryDescription,
315        timeout: Duration,
316        cancellation_token: Option<BoxedCancelToken>,
317    ) -> crate::Result<NodeHealthResult> {
318        let com = {
319            let health_policy_raw =
320                desc.health_policy
321                    .as_ref()
322                    .map(|h| FABRIC_CLUSTER_HEALTH_POLICY {
323                        ConsiderWarningAsError: h.consider_warning_as_error,
324                        MaxPercentUnhealthyNodes: h.max_percent_unhealthy_nodes,
325                        MaxPercentUnhealthyApplications: h.max_percent_unhealthy_applications,
326                        Reserved: std::ptr::null_mut(),
327                    });
328            let event_filter_raw = desc.events_filter.as_ref().map(|f| {
329                mssf_com::FabricTypes::FABRIC_HEALTH_EVENTS_FILTER {
330                    HealthStateFilter: f.health_state_filter.bits() as u32,
331                    Reserved: std::ptr::null_mut(),
332                }
333            });
334            let desc_raw = mssf_com::FabricTypes::FABRIC_NODE_HEALTH_QUERY_DESCRIPTION {
335                NodeName: desc.node_name.as_pcwstr(),
336                HealthPolicy: health_policy_raw
337                    .as_ref()
338                    .map_or(std::ptr::null_mut(), |h| h as *const _ as *mut _),
339                EventsFilter: event_filter_raw
340                    .as_ref()
341                    .map_or(std::ptr::null_mut(), |f| f as *const _ as *mut _),
342                Reserved: std::ptr::null_mut(),
343            };
344            self.get_node_health_internal(&desc_raw, timeout.as_millis() as u32, cancellation_token)
345        }
346        .await??;
347        Ok(NodeHealthResult::from_com(&com))
348    }
349
350    /// Gets the health of the cluster.
351    pub async fn get_cluster_health(
352        &self,
353        desc: &crate::types::ClusterHealthQueryDescription,
354        timeout: Duration,
355        cancellation_token: Option<BoxedCancelToken>,
356    ) -> crate::Result<ClusterHealth> {
357        let com = {
358            // Build the app policy map
359            let mut pool = BoxPool::new();
360            let desc_raw = desc.get_raw_with_pool(&mut pool);
361            self.get_cluster_health_internal(
362                &desc_raw,
363                timeout.as_millis() as u32,
364                cancellation_token,
365            )
366        }
367        .await??;
368        Ok(ClusterHealth::from(&com))
369    }
370
371    /// Gets the health of an application.
372    pub async fn get_application_health(
373        &self,
374        desc: &crate::types::ApplicationHealthQueryDescription,
375        timeout: Duration,
376        cancellation_token: Option<BoxedCancelToken>,
377    ) -> crate::Result<crate::types::ApplicationHealth> {
378        let com = {
379            let mut pool = BoxPool::new();
380            let desc_raw = desc.get_raw_with_pool(&mut pool);
381            self.get_application_health_internal(
382                &desc_raw,
383                timeout.as_millis() as u32,
384                cancellation_token,
385            )
386        }
387        .await??;
388        Ok(crate::types::ApplicationHealth::from(&com))
389    }
390
391    pub async fn get_partition_health(
392        &self,
393        desc: &crate::types::PartitionHealthQueryDescription,
394        timeout: Duration,
395        cancellation_token: Option<BoxedCancelToken>,
396    ) -> crate::Result<crate::types::PartitionHealthResult> {
397        let com = {
398            let mut pool = BoxPool::new();
399            let desc_raw = desc.get_raw_with_pool(&mut pool);
400            self.get_partition_health_internal(
401                &desc_raw,
402                timeout.as_millis() as u32,
403                cancellation_token,
404            )
405        }
406        .await??;
407        Ok(crate::types::PartitionHealthResult::from(&com))
408    }
409
410    /// Gets the health of a service.
411    pub async fn get_service_health(
412        &self,
413        desc: &crate::types::ServiceHealthQueryDescription,
414        timeout: Duration,
415        cancellation_token: Option<BoxedCancelToken>,
416    ) -> crate::Result<crate::types::ServiceHealthResult> {
417        let com = {
418            let mut pool = BoxPool::new();
419            let desc_raw = desc.get_raw_with_pool(&mut pool);
420            self.get_service_health_internal(
421                &desc_raw,
422                timeout.as_millis() as u32,
423                cancellation_token,
424            )
425        }
426        .await??;
427        Ok(crate::types::ServiceHealthResult::from(&com))
428    }
429
430    /// Gets the health of a replica.
431    pub async fn get_replica_health(
432        &self,
433        desc: &crate::types::ReplicaHealthQueryDescription,
434        timeout: Duration,
435        cancellation_token: Option<BoxedCancelToken>,
436    ) -> crate::Result<crate::types::ReplicaHealthResult> {
437        let com = {
438            let mut pool = BoxPool::new();
439            let desc_raw = desc.get_raw_with_pool(&mut pool);
440            self.get_replica_health_internal(
441                &desc_raw,
442                timeout.as_millis() as u32,
443                cancellation_token,
444            )
445        }
446        .await??;
447        Ok(crate::types::ReplicaHealthResult::from(&com))
448    }
449}