Skip to content

Commit

Permalink
FO 3.1.14 - Plugin loader fix (non-breaking) for *native* deps.
Browse files Browse the repository at this point in the history
  • Loading branch information
Charles Torre committed Jun 18, 2021
1 parent 5e09940 commit 68a0657
Show file tree
Hide file tree
Showing 4 changed files with 39 additions and 43 deletions.
2 changes: 1 addition & 1 deletion Documentation/Using.md
Original file line number Diff line number Diff line change
Expand Up @@ -539,7 +539,7 @@ $appParams = @{ "FabricSystemObserverEnabled" = "true"; "FabricSystemObserverMem
Then execute the application upgrade with

```Powershell
Start-ServiceFabricApplicationUpgrade -ApplicationName fabric:/FabricObserver -ApplicationTypeVersion 3.1.13 -ApplicationParameter $appParams -Monitored -FailureAction rollback
Start-ServiceFabricApplicationUpgrade -ApplicationName fabric:/FabricObserver -ApplicationTypeVersion 3.1.14 -ApplicationParameter $appParams -Monitored -FailureAction rollback
```

Note: On *Linux*, this will restart FO processes (one at a time, UD Walk with safety checks) due to the way Linux Capabilites work. In a nutshell, for any kind of application upgrade, we have to re-run the FO setup script to get the Capabilities in place. For Windows, FO processes will NOT be restarted.
60 changes: 27 additions & 33 deletions FabricObserver/FabricObserver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -90,12 +90,13 @@ private void LoadObserversFromPlugins(IServiceCollection services)
}

var pluginLoaders = new List<PluginLoader>(pluginDlls.Length);

Type[] sharedTypes = { typeof(FabricObserverStartupAttribute), typeof(IFabricObserverStartup), typeof(IServiceCollection) };

foreach (string pluginDll in pluginDlls)
foreach (string dll in pluginDlls)
{
PluginLoader loader = PluginLoader.CreateFromAssemblyFile(pluginDll, sharedTypes);
// This does not create an Assembly. It creates a PluginLoader instance for each dll in the Plugins folder.
// TODO: Figure out how to only load the plugin dll in an efficient way. For now, this is fine. This is not resource intensive.
PluginLoader loader = PluginLoader.CreateFromAssemblyFile(dll, sharedTypes);
pluginLoaders.Add(loader);
}

Expand All @@ -105,42 +106,35 @@ private void LoadObserversFromPlugins(IServiceCollection services)

try
{
// If your plugin has native deps, this will fail at this call. That's OK.
// We only want to load your plugin eventually, anyway (see startupObject code below).
// Since your plugin's deps (managed and native) all live in the same folder, there will be no dll resolution failures
// when your plugin needs to access some dependency (and it needs to access some dependency...).
// If your plugin has native library dependencies (that's fine), then we will land in the catch (BadImageFormatException).
// This is by design. The Managed FO plugin assembly will successfully load, of course.
pluginAssembly = pluginLoader.LoadDefaultAssembly();
}
catch (Exception e)
{
string s = e.Message;
if (e.HResult == -2147024885 || e.Message.ToLower().Contains("bad il"))
{
continue;
}
else
{
throw;
}
}

// Note: This is a micro-optimization (including the for below it). It could just as well be left as IEnumerable and then foreach'd over.
// It is unlikely that there will be 1000s of plugins...
FabricObserverStartupAttribute[] startupAttributes = pluginAssembly.GetCustomAttributes<FabricObserverStartupAttribute>().ToArray();
FabricObserverStartupAttribute[] startupAttributes = pluginAssembly.GetCustomAttributes<FabricObserverStartupAttribute>().ToArray();

for (int i = 0; i < startupAttributes.Length; ++i)
{
object startupObject = Activator.CreateInstance(startupAttributes[i].StartupType);

if (startupObject is IFabricObserverStartup fabricObserverStartup)
{
fabricObserverStartup.ConfigureServices(services, fabricClient, Context);
}
else
for (int i = 0; i < startupAttributes.Length; ++i)
{
throw new InvalidOperationException($"{startupAttributes[i].StartupType.FullName} must implement IFabricObserverStartup.");
object startupObject = Activator.CreateInstance(startupAttributes[i].StartupType);

if (startupObject is IFabricObserverStartup fabricObserverStartup)
{
fabricObserverStartup.ConfigureServices(services, fabricClient, Context);
}
else
{
// This will bring down FO, which it should: This means your plugin is not supported. Fix your bug.
throw new InvalidOperationException($"{startupAttributes[i].StartupType.FullName} must implement IFabricObserverStartup.");
}
}
}
catch (BadImageFormatException)
{
continue;
}
finally
{
pluginLoader?.Dispose();
}
}
}
}
Expand Down
10 changes: 6 additions & 4 deletions FabricObserver/PackageRoot/Data/Plugins/Readme.txt
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,9 @@ so you just program as you did before and everything will work as it used to.

Install .Net Core 3.1.

Grab the latest FO nupkg from Releases section that suits your target OS (Linux or Windows).
In general, you will want the SelfContained package, not the FrameworkDependent (for example, Azure OS images do not ship with .NET Core 3.1 aboard,
so you need self-contained build which has all the binaries needed to run a .NET Core 3.1 app.)
Grab the latest FabricObserver nupkg from https://www.nuget.org/profiles/ServiceFabricApps that suits your target OS (Linux or Windows).
In general, you will want the SelfContained package, not FrameworkDependent (for example, Azure OS images do not ship with .NET Core 3.1 aboard,
so you need SelfContained build which has all the binaries needed to run a .NET Core 3.1 app.)

If you want to build your own nupkgs from FO source, then:

Expand All @@ -43,7 +43,9 @@ Navigate to top level directory (where the SLN lives, for example), then:
Target OS - Framework-dependent = .NET Core 3.1 is already installed on target server
Target OS - Self-contained = includes all the files necessary for running .NET Core 3.1 applications
4. Write an observer plugin!
5. Build your observer project, drop the output dll into the Data/Plugins folder in FabricObserver/PackageRoot.
5. Build your observer project, drop the output dll and *ALL* of its dependencies, both managed and native (this is *very* important), into the Config/Data/Plugins folder in FabricObserver/PackageRoot.
You can place your plugin dll and all of its dependencies in its own (*same*) folder under the Plugins directory (useful if you have multiple plugins).
Again, ALL plugin dll dependencies need to live in the *same* folder as the plugin dll. :)
6. Add a new config section for your observer in FabricObserver/PackageRoot/Config/Settings.xml (see example at bottom of that file)
Update ApplicationManifest.xml with Parameters if you want to support Application Parameter Updates for your plugin.
(Look at both FabricObserver/PackageRoot/Config/Settings.xml and FabricObserverApp/ApplicationPackageRoot/ApplicationManifest.xml for several examples of how to do this.)
Expand Down
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# FabricObserver 3.1.13
# FabricObserver 3.1.14

[**FabricObserver (FO)**](https://github.com/microsoft/service-fabric-observer/releases) is a complete implementation of a generic resource usage watchdog service written as a stateless, singleton Service Fabric .NET Core 3.1 application that
1. Monitors a broad range of machine resources that tend to be very important to all Service Fabric applications, like disk space consumption, CPU use, memory use, endpoint availability, ephemeral TCP port use, and app/cluster certificate health out-of-the-box.
Expand Down Expand Up @@ -135,19 +135,19 @@ Connect-ServiceFabricCluster -ConnectionEndpoint @('sf-win-cluster.westus2.cloud
#Copy $path contents (FO app package) to server:
Copy-ServiceFabricApplicationPackage -ApplicationPackagePath $path -CompressPackage -ApplicationPackagePathInImageStore FO3113 -TimeoutSec 1800
Copy-ServiceFabricApplicationPackage -ApplicationPackagePath $path -CompressPackage -ApplicationPackagePathInImageStore FO3114 -TimeoutSec 1800
#Register FO ApplicationType:
Register-ServiceFabricApplicationType -ApplicationPathInImageStore FO3113
Register-ServiceFabricApplicationType -ApplicationPathInImageStore FO3114
#Create FO application (if not already deployed at lesser version):
New-ServiceFabricApplication -ApplicationName fabric:/FabricObserver -ApplicationTypeName FabricObserverType -ApplicationTypeVersion 3.1.13
New-ServiceFabricApplication -ApplicationName fabric:/FabricObserver -ApplicationTypeName FabricObserverType -ApplicationTypeVersion 3.1.14
#OR if updating existing version:
Start-ServiceFabricApplicationUpgrade -ApplicationName fabric:/FabricObserver -ApplicationTypeVersion 3.1.13 -Monitored -FailureAction rollback
Start-ServiceFabricApplicationUpgrade -ApplicationName fabric:/FabricObserver -ApplicationTypeVersion 3.1.14 -Monitored -FailureAction rollback
```

## Observer Model
Expand Down

0 comments on commit 68a0657

Please sign in to comment.