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