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_code_package_info(&self) -> CodePackageInfo {
86        CodePackageInfo {
87            context_id: WStringWrap::from(unsafe { self.com_impl.get_ContextId() }).into(),
88            code_package_name: WStringWrap::from(unsafe { self.com_impl.get_CodePackageName() })
89                .into(),
90            code_package_version: WStringWrap::from(unsafe {
91                self.com_impl.get_CodePackageVersion()
92            })
93            .into(),
94            work_directory: WStringWrap::from(unsafe { self.com_impl.get_WorkDirectory() }).into(),
95            log_directory: WStringWrap::from(unsafe { self.com_impl.get_LogDirectory() }).into(),
96            temp_directory: WStringWrap::from(unsafe { self.com_impl.get_TempDirectory() }).into(),
97            application_name: WStringWrap::from(PCWSTR(unsafe {
98                self.com_impl.get_ApplicationName().0
99            }))
100            .into(),
101            application_type_name: WStringWrap::from(unsafe {
102                self.com_impl.get_ApplicationTypeName()
103            })
104            .into(),
105            service_listen_address: WStringWrap::from(unsafe {
106                self.com_impl.get_ServiceListenAddress()
107            })
108            .into(),
109            service_publish_address: WStringWrap::from(unsafe {
110                self.com_impl.get_ServicePublishAddress()
111            })
112            .into(),
113        }
114    }
115
116    pub fn get_code_package_names(&self) -> Vec<WString> {
117        // cpp code never returns failure.
118        let com = unsafe {
119            self.com_impl
120                .GetCodePackageNames()
121                .expect("cannot get code package names")
122        };
123        crate::strings::WStringList::from(&com).into_vec()
124    }
125
126    pub fn get_code_package(&self, name: &WString) -> crate::Result<CodePackage> {
127        let com = unsafe { self.com_impl.GetCodePackage(name.as_pcwstr())? };
128        Ok(CodePackage::from(&com))
129    }
130
131    /// The health information describes the report details, like the source ID, the property,
132    /// the health state and other relevant details. The code package activation context uses an
133    /// internal health client to send the reports to the health store. The client optimizes messages to
134    /// Health Manager by batching reports per a configured duration (Default: 30 seconds).
135    /// If the report has high priority, you can specify send options to send it immediately.
136    ///
137    /// Possible Errors:
138    ///     FABRIC_E_HEALTH_STALE_REPORT:
139    ///         HealthReport already exist for the same entity,
140    ///         SourceId and Property with same or higher SequenceNumber.
141    ///     FABRIC_E_HEALTH_MAX_REPORTS_REACHED:
142    ///         HeathClient has reached the maximum number of health reports
143    ///         that can accept for processing. More reports will be accepted when progress is done
144    ///         with the currently accepted reports. By default, the FabricClient.HealthClient can accept 10000 different reports.
145    pub fn report_application_health(
146        &self,
147        healthinfo: &HealthInformation,
148        send_options: Option<&HealthReportSendOption>,
149    ) -> crate::Result<()> {
150        let raw: FABRIC_HEALTH_INFORMATION = healthinfo.into();
151        let send_options = send_options.map(FABRIC_HEALTH_REPORT_SEND_OPTIONS::from);
152        let raw_options = match send_options.as_ref() {
153            Some(opt) => opt as *const FABRIC_HEALTH_REPORT_SEND_OPTIONS,
154            None => std::ptr::null(),
155        };
156        unsafe { self.com_impl.ReportApplicationHealth2(&raw, raw_options) }
157            .map_err(crate::Error::from)
158    }
159
160    pub fn get_com(&self) -> IFabricCodePackageActivationContext6 {
161        self.com_impl.clone()
162    }
163
164    /// Register a configuration package change handler callback
165    /// Consider using [`AutoConfigurationPackageChangeCallbackHandle::new`] instead of this directly.
166    pub fn register_configuration_package_change_handler<T>(
167        &self,
168        handler: T,
169    ) -> crate::Result<ConfigurationPackageChangeCallbackHandle>
170    where
171        T: Fn(&ConfigurationPackageChangeEvent) + 'static,
172    {
173        let lambda_handler = LambdaConfigurationPackageEventHandler::new(handler);
174        let bridge = ConfigurationPackageChangeEventHandlerBridge::new(lambda_handler);
175        let callback: IFabricConfigurationPackageChangeHandler = bridge.into();
176        // SAFETY: bridge implements the required COM interface
177        let raw_handle = unsafe {
178            self.com_impl
179                .RegisterConfigurationPackageChangeHandler(&callback)
180        }?;
181        // SAFETY: raw_handle is a configuration package change handler id, not some other id.
182        Ok(unsafe { ConfigurationPackageChangeCallbackHandle::from(raw_handle) })
183    }
184
185    pub fn unregister_configuration_package_change_handler(
186        &self,
187        handle: ConfigurationPackageChangeCallbackHandle,
188    ) -> crate::Result<()> {
189        // SAFETY: we assume that only 1 activation context can be
190        unsafe {
191            self.com_impl
192                .UnregisterConfigurationPackageChangeHandler(handle.0)
193        }
194        .unwrap();
195        Ok(())
196    }
197}
198
199impl From<IFabricCodePackageActivationContext6> for CodePackageActivationContext {
200    fn from(value: IFabricCodePackageActivationContext6) -> Self {
201        CodePackageActivationContext { com_impl: value }
202    }
203}
204
205#[derive(Debug, Clone)]
206pub struct CodePackage {
207    // description section
208    pub name: WString,
209    pub version: WString,
210    pub service_manifest_name: WString,
211    pub service_manifest_version: WString,
212    pub is_shared: bool,
213    pub setup_entrypoint: Option<WString>, // TODO
214    pub entrypoint: Option<WString>,       // TODO
215
216    // standalone section
217    pub path: WString,
218    // TODO: ex2 fields
219}
220
221impl From<&IFabricCodePackage> for CodePackage {
222    fn from(value: &IFabricCodePackage) -> Self {
223        let desc = unsafe { value.get_Description().as_ref().unwrap() };
224        let path = unsafe { value.get_Path() };
225        Self {
226            name: desc.Name.into(),
227            version: desc.Version.into(),
228            service_manifest_name: desc.ServiceManifestName.into(),
229            service_manifest_version: desc.ServiceManifestVersion.into(),
230            is_shared: desc.IsShared,
231            setup_entrypoint: None,
232            entrypoint: None,
233            path: WString::from(path),
234        }
235    }
236}