mssf_core/runtime/
activation_context.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 mssf_com::{
7    FabricRuntime::{
8        IFabricCodePackage, IFabricCodePackageActivationContext6,
9        IFabricConfigurationPackageChangeHandler,
10    },
11    FabricTypes::{FABRIC_HEALTH_INFORMATION, FABRIC_HEALTH_REPORT_SEND_OPTIONS},
12};
13
14use crate::{
15    Error, PCWSTR, WString,
16    strings::WStringWrap,
17    types::{EndpointResourceDescription, HealthInformation, HealthReportSendOption},
18};
19
20use super::{
21    config::ConfigurationPackage,
22    package_change::{
23        ConfigurationPackageChangeEvent,
24        config::{
25            ConfigurationPackageChangeCallbackHandle, ConfigurationPackageChangeEventHandlerBridge,
26            LambdaConfigurationPackageEventHandler,
27        },
28    },
29};
30
31#[derive(Debug, Clone)]
32pub struct CodePackageActivationContext {
33    com_impl: IFabricCodePackageActivationContext6,
34}
35
36/// Code package information is propagated here from the ActivationContext
37/// provided when the service is started. This information can be used to setup
38/// log directories and other resources needed which require information
39/// about the code package and the local runtime environment.
40#[derive(Debug, Clone)]
41pub struct CodePackageInfo {
42    pub context_id: WString,
43    pub code_package_name: WString,
44    pub code_package_version: WString,
45    pub work_directory: WString,
46    pub log_directory: WString,
47    pub temp_directory: WString,
48    pub application_name: WString,
49    pub application_type_name: WString,
50    pub service_listen_address: WString,
51    pub service_publish_address: WString,
52}
53
54impl CodePackageActivationContext {
55    pub fn create() -> Result<CodePackageActivationContext, Error> {
56        let com = super::get_com_activation_context::<IFabricCodePackageActivationContext6>()?;
57        Ok(Self::from(com))
58    }
59
60    pub fn get_endpoint_resource(
61        &self,
62        serviceendpointresourcename: &WString,
63    ) -> crate::Result<EndpointResourceDescription> {
64        let rs = unsafe {
65            self.com_impl.GetServiceEndpointResource(PCWSTR::from_raw(
66                serviceendpointresourcename.as_ptr(),
67            ))?
68        };
69        let res_ref = unsafe { rs.as_ref().unwrap() };
70        let desc = EndpointResourceDescription::from(res_ref);
71        Ok(desc)
72    }
73
74    pub fn get_configuration_package(
75        &self,
76        configpackagename: &WString,
77    ) -> crate::Result<ConfigurationPackage> {
78        let c = unsafe {
79            self.com_impl
80                .GetConfigurationPackage(configpackagename.as_pcwstr())
81        }?;
82        Ok(ConfigurationPackage::from(c))
83    }
84
85    pub fn get_config_package_names(&self) -> Vec<WString> {
86        // cpp code never returns failure.
87        let com = unsafe {
88            self.com_impl
89                .GetConfigurationPackageNames()
90                .expect("cannot get config package names")
91        };
92        crate::strings::WStringList::from(&com).into_vec()
93    }
94
95    pub fn get_code_package_info(&self) -> CodePackageInfo {
96        CodePackageInfo {
97            context_id: WStringWrap::from(unsafe { self.com_impl.get_ContextId() }).into(),
98            code_package_name: WStringWrap::from(unsafe { self.com_impl.get_CodePackageName() })
99                .into(),
100            code_package_version: WStringWrap::from(unsafe {
101                self.com_impl.get_CodePackageVersion()
102            })
103            .into(),
104            work_directory: WStringWrap::from(unsafe { self.com_impl.get_WorkDirectory() }).into(),
105            log_directory: WStringWrap::from(unsafe { self.com_impl.get_LogDirectory() }).into(),
106            temp_directory: WStringWrap::from(unsafe { self.com_impl.get_TempDirectory() }).into(),
107            application_name: WStringWrap::from(PCWSTR(unsafe {
108                self.com_impl.get_ApplicationName().0
109            }))
110            .into(),
111            application_type_name: WStringWrap::from(unsafe {
112                self.com_impl.get_ApplicationTypeName()
113            })
114            .into(),
115            service_listen_address: WStringWrap::from(unsafe {
116                self.com_impl.get_ServiceListenAddress()
117            })
118            .into(),
119            service_publish_address: WStringWrap::from(unsafe {
120                self.com_impl.get_ServicePublishAddress()
121            })
122            .into(),
123        }
124    }
125
126    pub fn get_code_package_names(&self) -> Vec<WString> {
127        // cpp code never returns failure.
128        let com = unsafe {
129            self.com_impl
130                .GetCodePackageNames()
131                .expect("cannot get code package names")
132        };
133        crate::strings::WStringList::from(&com).into_vec()
134    }
135
136    pub fn get_code_package(&self, name: &WString) -> crate::Result<CodePackage> {
137        let com = unsafe { self.com_impl.GetCodePackage(name.as_pcwstr())? };
138        Ok(CodePackage::from(&com))
139    }
140
141    /// The health information describes the report details, like the source ID, the property,
142    /// the health state and other relevant details. The code package activation context uses an
143    /// internal health client to send the reports to the health store. The client optimizes messages to
144    /// Health Manager by batching reports per a configured duration (Default: 30 seconds).
145    /// If the report has high priority, you can specify send options to send it immediately.
146    ///
147    /// Possible Errors:
148    ///     FABRIC_E_HEALTH_STALE_REPORT:
149    ///         HealthReport already exist for the same entity,
150    ///         SourceId and Property with same or higher SequenceNumber.
151    ///     FABRIC_E_HEALTH_MAX_REPORTS_REACHED:
152    ///         HeathClient has reached the maximum number of health reports
153    ///         that can accept for processing. More reports will be accepted when progress is done
154    ///         with the currently accepted reports. By default, the FabricClient.HealthClient can accept 10000 different reports.
155    pub fn report_application_health(
156        &self,
157        healthinfo: &HealthInformation,
158        send_options: Option<&HealthReportSendOption>,
159    ) -> crate::Result<()> {
160        let raw: FABRIC_HEALTH_INFORMATION = healthinfo.into();
161        let send_options = send_options.map(FABRIC_HEALTH_REPORT_SEND_OPTIONS::from);
162        let raw_options = match send_options.as_ref() {
163            Some(opt) => opt as *const FABRIC_HEALTH_REPORT_SEND_OPTIONS,
164            None => std::ptr::null(),
165        };
166        unsafe { self.com_impl.ReportApplicationHealth2(&raw, raw_options) }
167            .map_err(crate::Error::from)
168    }
169
170    pub fn get_com(&self) -> IFabricCodePackageActivationContext6 {
171        self.com_impl.clone()
172    }
173
174    /// Register a configuration package change handler callback
175    /// Consider using [`AutoConfigurationPackageChangeCallbackHandle::new`] instead of this directly.
176    pub fn register_configuration_package_change_handler<T>(
177        &self,
178        handler: T,
179    ) -> crate::Result<ConfigurationPackageChangeCallbackHandle>
180    where
181        T: Fn(&ConfigurationPackageChangeEvent) + 'static,
182    {
183        let lambda_handler = LambdaConfigurationPackageEventHandler::new(handler);
184        let bridge = ConfigurationPackageChangeEventHandlerBridge::new(lambda_handler);
185        let callback: IFabricConfigurationPackageChangeHandler = bridge.into();
186        // SAFETY: bridge implements the required COM interface
187        let raw_handle = unsafe {
188            self.com_impl
189                .RegisterConfigurationPackageChangeHandler(&callback)
190        }?;
191        // SAFETY: raw_handle is a configuration package change handler id, not some other id.
192        Ok(unsafe { ConfigurationPackageChangeCallbackHandle::from(raw_handle) })
193    }
194
195    pub fn unregister_configuration_package_change_handler(
196        &self,
197        handle: ConfigurationPackageChangeCallbackHandle,
198    ) -> crate::Result<()> {
199        // SAFETY: we assume that only 1 activation context can be
200        unsafe {
201            self.com_impl
202                .UnregisterConfigurationPackageChangeHandler(handle.0)
203        }
204        .unwrap();
205        Ok(())
206    }
207
208    pub fn get_data_package_names(&self) -> Vec<WString> {
209        // cpp code never returns failure.
210        let com = unsafe {
211            self.com_impl
212                .GetDataPackageNames()
213                .expect("cannot get data package names")
214        };
215        crate::strings::WStringList::from(&com).into_vec()
216    }
217}
218
219impl From<IFabricCodePackageActivationContext6> for CodePackageActivationContext {
220    fn from(value: IFabricCodePackageActivationContext6) -> Self {
221        CodePackageActivationContext { com_impl: value }
222    }
223}
224
225#[derive(Debug, Clone)]
226pub struct CodePackage {
227    // description section
228    pub name: WString,
229    pub version: WString,
230    pub service_manifest_name: WString,
231    pub service_manifest_version: WString,
232    pub is_shared: bool,
233    pub setup_entrypoint: Option<WString>, // TODO
234    pub entrypoint: Option<WString>,       // TODO
235
236    // standalone section
237    pub path: WString,
238    // TODO: ex2 fields
239}
240
241impl From<&IFabricCodePackage> for CodePackage {
242    fn from(value: &IFabricCodePackage) -> Self {
243        let desc = unsafe { value.get_Description().as_ref().unwrap() };
244        let path = unsafe { value.get_Path() };
245        Self {
246            name: desc.Name.into(),
247            version: desc.Version.into(),
248            service_manifest_name: desc.ServiceManifestName.into(),
249            service_manifest_version: desc.ServiceManifestVersion.into(),
250            is_shared: desc.IsShared,
251            setup_entrypoint: None,
252            entrypoint: None,
253            path: WString::from(path),
254        }
255    }
256}