Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[FabricRuntime] Support Package change notification callbacks #96

Merged
merged 18 commits into from
Dec 28, 2024
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 30 additions & 4 deletions crates/libs/core/src/client/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,22 @@
// ------------------------------------------------------------

use connection::{ClientConnectionEventHandlerBridge, LambdaClientConnectionNotificationHandler};
use mssf_com::FabricClient::{
FabricCreateClient3, FabricCreateLocalClient3, FabricCreateLocalClient4,
IFabricClientConnectionEventHandler, IFabricPropertyManagementClient2, IFabricQueryClient10,
IFabricServiceManagementClient6, IFabricServiceNotificationEventHandler,
use mssf_com::{
FabricClient::{
FabricCreateClient3, FabricCreateLocalClient3, FabricCreateLocalClient4,
IFabricClientConnectionEventHandler, IFabricPropertyManagementClient2,
IFabricQueryClient10, IFabricServiceManagementClient6,
IFabricServiceNotificationEventHandler,
},
FabricRuntime::IFabricConfigurationPackageChangeHandler,
};
use notification::{
LambdaServiceNotificationHandler, ServiceNotificationEventHandler,
ServiceNotificationEventHandlerBridge,
};
use package_change::config::{
ConfigurationPackageChangeEventHandlerBridge, LambdaConfigurationPackageEventHandler,
};
use windows_core::Interface;

use crate::types::ClientRole;
Expand All @@ -21,12 +28,14 @@ use self::{query_client::QueryClient, svc_mgmt_client::ServiceManagementClient};

mod connection;
mod notification;
mod package_change;
pub mod query_client;
pub mod svc_mgmt_client;

// reexport
pub use connection::GatewayInformationResult;
pub use notification::ServiceNotification;
pub use package_change::{config::ConfigurationPackageChangeEvent, PackageChangeType};

#[cfg(test)]
mod tests;
Expand Down Expand Up @@ -93,6 +102,7 @@ fn create_local_client_internal<T: Interface>(
pub struct FabricClientBuilder {
sn_handler: Option<IFabricServiceNotificationEventHandler>,
cc_handler: Option<LambdaClientConnectionNotificationHandler>,
conf_pkg_handler: Option<IFabricConfigurationPackageChangeHandler>,
client_role: ClientRole,
connection_strings: Option<Vec<crate::HSTRING>>,
}
Expand All @@ -109,6 +119,7 @@ impl FabricClientBuilder {
Self {
sn_handler: None,
cc_handler: None,
conf_pkg_handler: None,
cgettys-microsoft marked this conversation as resolved.
Show resolved Hide resolved
client_role: ClientRole::Unknown,
connection_strings: None,
}
Expand Down Expand Up @@ -136,6 +147,21 @@ impl FabricClientBuilder {
self.with_service_notification_handler(handler)
}

/// Configures the configuration package change handler.
///
/// Notified on addition, removal, or modification of configuration packages
///
pub fn with_on_configuration_package_change<T>(mut self, f: T) -> Self
where
T: Fn(&ConfigurationPackageChangeEvent) -> crate::Result<()> + 'static,
{
let handler = LambdaConfigurationPackageEventHandler::new(f);
self.conf_pkg_handler = Some(ConfigurationPackageChangeEventHandlerBridge::new_com(
handler,
));
self
}

/// When FabricClient connects to the SF cluster, this callback is invoked.
pub fn with_on_client_connect<T>(mut self, f: T) -> Self
where
Expand Down
2 changes: 2 additions & 0 deletions crates/libs/core/src/client/notification.rs
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@ impl PartialOrd for ServiceEndpointsVersion {

// Bridge implementation for the notification handler to turn rust code into SF com object.
#[windows_core::implement(IFabricServiceNotificationEventHandler)]
#[allow(non_camel_case_types)] // Suppress lint for _Impl struct
pub struct ServiceNotificationEventHandlerBridge<T>
where
T: ServiceNotificationEventHandler,
Expand Down Expand Up @@ -173,6 +174,7 @@ where
/// Lambda implemnentation of ServiceNotificationEventHandler trait.
/// This is used in FabricClientBuilder to build function into handler.
/// Not exposed to user.
/// This isn't strictly required by the implementation as written. But it leaves open the door to non-lambda implementations in future.
pub struct LambdaServiceNotificationHandler<T>
where
T: Fn(&ServiceNotification) -> crate::Result<()> + 'static,
Expand Down
146 changes: 146 additions & 0 deletions crates/libs/core/src/client/package_change/config.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
// ------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License (MIT). See License.txt in the repo root for license information.
// ------------------------------------------------------------
//! Handle callbacks for configuration package changes
//! TODO: We probably should also provide a helpful callback to use in conjunction with the config-rs support (so that it processes configuration changes)
use mssf_com::FabricRuntime::{
IFabricConfigurationPackageChangeHandler, IFabricConfigurationPackageChangeHandler_Impl,
};

use crate::runtime::config::ConfigurationPackage;

use super::PackageChangeType;

#[derive(Debug, Clone)]
pub struct ConfigurationPackageChangeEvent {
pub change_type: PackageChangeType,
cgettys-microsoft marked this conversation as resolved.
Show resolved Hide resolved
pub config_package: Option<ConfigurationPackage>,
pub previous_config_package: Option<ConfigurationPackage>,
}

impl ConfigurationPackageChangeEvent {
fn from_com(
change_type: PackageChangeType,
_source: Option<&mssf_com::FabricRuntime::IFabricCodePackageActivationContext>,
previous_configpackage: Option<&mssf_com::FabricRuntime::IFabricConfigurationPackage>,
configpackage: Option<&mssf_com::FabricRuntime::IFabricConfigurationPackage>,
) -> Self {
let config_package: Option<ConfigurationPackage> =
configpackage.map(|c| ConfigurationPackage::from_com(c.clone()));
let previous_config_package: Option<ConfigurationPackage> =
previous_configpackage.map(|c| ConfigurationPackage::from_com(c.clone()));
Self {
change_type,
config_package,
previous_config_package,
}
}
}

/// Rust trait to turn rust code into IFabricConfigurationPackageChangeHandler.
/// Not exposed to user
pub trait ConfigurationPackageChangeEventHandler: 'static {
fn on_change(&self, change: &ConfigurationPackageChangeEvent) -> crate::Result<()>;
}

// Bridge implementation for the change handler to turn rust code into SF com object.
#[windows_core::implement(IFabricConfigurationPackageChangeHandler)]
#[allow(non_camel_case_types)] // Suppress lint for _Impl struct
pub struct ConfigurationPackageChangeEventHandlerBridge<T>
where
T: ConfigurationPackageChangeEventHandler,
{
inner: T,
}

impl<T> ConfigurationPackageChangeEventHandlerBridge<T>
where
T: ConfigurationPackageChangeEventHandler,
{
pub fn new(inner: T) -> Self {
Self { inner }
}

pub fn new_com(inner: T) -> IFabricConfigurationPackageChangeHandler {
Self::new(inner).into()
}
}

impl<T> IFabricConfigurationPackageChangeHandler_Impl
for ConfigurationPackageChangeEventHandlerBridge<T>
where
T: ConfigurationPackageChangeEventHandler,
{
fn OnPackageAdded(
&self,
source: Option<&mssf_com::FabricRuntime::IFabricCodePackageActivationContext>,
configpackage: Option<&mssf_com::FabricRuntime::IFabricConfigurationPackage>,
) {
let event = ConfigurationPackageChangeEvent::from_com(
PackageChangeType::Addition,
source,
configpackage,
None,
);
// TODO: unwrap, or should we change the return type of the lambda to be the empty type?
self.inner.on_change(&event).unwrap();
}

fn OnPackageRemoved(
&self,
source: Option<&mssf_com::FabricRuntime::IFabricCodePackageActivationContext>,
configpackage: Option<&mssf_com::FabricRuntime::IFabricConfigurationPackage>,
) {
let event = ConfigurationPackageChangeEvent::from_com(
PackageChangeType::Removal,
source,
configpackage,
None,
);
self.inner.on_change(&event).unwrap();
}

fn OnPackageModified(
&self,
source: Option<&mssf_com::FabricRuntime::IFabricCodePackageActivationContext>,
previousconfigpackage: Option<&mssf_com::FabricRuntime::IFabricConfigurationPackage>,
configpackage: Option<&mssf_com::FabricRuntime::IFabricConfigurationPackage>,
) {
let event = ConfigurationPackageChangeEvent::from_com(
PackageChangeType::Modification,
source,
configpackage,
previousconfigpackage,
);
self.inner.on_change(&event).unwrap();
}
}

/// Lambda implementation of ConfigurationPackageChangeEventHandler trait.
/// This is used in FabricClientBuilder to build function into handler.
/// Not exposed to user.
pub(crate) struct LambdaConfigurationPackageEventHandler<T>
where
T: Fn(&ConfigurationPackageChangeEvent) -> crate::Result<()> + 'static,
{
f: T,
}

impl<T> LambdaConfigurationPackageEventHandler<T>
where
T: Fn(&ConfigurationPackageChangeEvent) -> crate::Result<()> + 'static,
{
pub fn new(f: T) -> Self {
Self { f }
}
}

impl<T> ConfigurationPackageChangeEventHandler for LambdaConfigurationPackageEventHandler<T>
where
T: Fn(&ConfigurationPackageChangeEvent) -> crate::Result<()> + 'static,
{
fn on_change(&self, change: &ConfigurationPackageChangeEvent) -> crate::Result<()> {
(self.f)(change)
}
}
15 changes: 15 additions & 0 deletions crates/libs/core/src/client/package_change/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// ------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License (MIT). See License.txt in the repo root for license information.
// ------------------------------------------------------------
//! This module supports implementing callbacks when Service Fabric Packages are changed
//!
pub(super) mod config;

/// The ways a given Service Fabric Package (e.g. ConfigurationPackage or DataPackage) can change
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
pub enum PackageChangeType {
Addition,
Removal,
Modification,
}
18 changes: 17 additions & 1 deletion crates/samples/no_default_features/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,27 @@
//!
//! This sample demonstrates it is possible to use the library with default-features = false and ensures that that scenario remains compiling as PRs go into the repository.
//!
use mssf_core::runtime::CodePackageActivationContext;
use std::borrow::Borrow;

use mssf_core::{client::FabricClientBuilder, runtime::CodePackageActivationContext};
#[no_mangle]
fn test_fn() {
// Make sure we link something
//
let my_ctx = CodePackageActivationContext::create();
my_ctx.unwrap();

// One might wish to use such a callback to e.g. trigger custom handling of configuration changes
// This doesn't require the config feature to be enabled
let _client = FabricClientBuilder::new()
.with_on_configuration_package_change(|c|
{
let change_type = c.change_type;
let changed_package_name = c.config_package.as_ref().map(|x |x.get_description().name.to_string_lossy());
let changed_package_str = changed_package_name.borrow().as_deref().unwrap_or("Unknown package name");
println!("Received config package change of type {change_type:?} to package {changed_package_str}");
Ok(())
}
)
.build();
}