... | ... | @@ -69,8 +69,10 @@ We probably need to implement something similar to the `LibraryManager` from Kea |
|
|
classDiagram
|
|
|
class IHook{
|
|
|
<<interface>>
|
|
|
+ string, string Version()
|
|
|
+ any, error Load()
|
|
|
+ string, string GetVersion()
|
|
|
+ any, error Load(cliOptions, SettingsObserver settings)
|
|
|
+ cli.Flags SetupCLI()
|
|
|
+ void SetupSettings(SettingsRegistry settings)
|
|
|
}
|
|
|
|
|
|
class FooCallout {
|
... | ... | @@ -90,8 +92,8 @@ classDiagram |
|
|
|
|
|
class FooHook{
|
|
|
<<plugin>>
|
|
|
+ string, string Version()
|
|
|
+ FooImpl, error Load()
|
|
|
+ string, string GetVersion()
|
|
|
+ FooImpl, error Load(cliOptions)
|
|
|
}
|
|
|
|
|
|
class FooImpl {
|
... | ... | @@ -101,8 +103,8 @@ classDiagram |
|
|
|
|
|
class BarBazHook{
|
|
|
<<plugin>>
|
|
|
+ string, string Version()
|
|
|
+ BarBazImpl, error Load()
|
|
|
+ string, string GetVersion()
|
|
|
+ BarBazImpl, error Load(cliOptions)
|
|
|
}
|
|
|
|
|
|
class BarBazImpl {
|
... | ... | @@ -118,8 +120,10 @@ classDiagram |
|
|
|
|
|
class LibraryManager{
|
|
|
+ LM, error New(string path)$
|
|
|
+ any, error Load()
|
|
|
+ string, string Version()
|
|
|
+ any, error Load(cliOptions)
|
|
|
+ string, string GetVersion()
|
|
|
+ cli.Flags SetupCLI()
|
|
|
+ cli.Flags SetupSettings(SettingsRegistry settings)
|
|
|
}
|
|
|
|
|
|
class AgentHookManager{
|
... | ... | @@ -143,6 +147,16 @@ classDiagram |
|
|
+ Call(type calloutType, func caller)
|
|
|
}
|
|
|
|
|
|
class SettingsObserver {
|
|
|
+ RegisterListener(name, func)
|
|
|
+ NotifyListeners(setting)
|
|
|
+ settings GetCurrentSettings()
|
|
|
}
|
|
|
|
|
|
class SettingsRegistry {
|
|
|
+ RegisterSetting(name, type, default, description)
|
|
|
}
|
|
|
|
|
|
class ServerCore{
|
|
|
<<external>>
|
|
|
}
|
... | ... | @@ -160,7 +174,9 @@ classDiagram |
|
|
ServerHookManager *-- HookExecutor
|
|
|
|
|
|
ServerCore ..> ServerHookManager
|
|
|
ServerCore ..> SettingsObserver
|
|
|
AgentCore ..> AgentHookManager
|
|
|
AgentCore ..> SettingsObserver
|
|
|
|
|
|
FooCallout <|.. AgentHookManager
|
|
|
BarCallout <|.. ServerHookManager
|
... | ... | @@ -169,8 +185,13 @@ classDiagram |
|
|
HookExecutor .. HookLoader
|
|
|
|
|
|
HookLoader ..> LibraryManager
|
|
|
HookLoader ..> SettingsObserver
|
|
|
HookLoader ..> SettingsRegistry
|
|
|
LibraryManager ..> IHook
|
|
|
|
|
|
IHook ..> SettingsObserver
|
|
|
IHook ..> SettingsRegistry
|
|
|
|
|
|
IHook <|.. FooHook
|
|
|
IHook <|.. BarBazHook
|
|
|
FooHook --> FooImpl
|
... | ... | @@ -189,6 +210,11 @@ The code is distributed over four packages: |
|
|
|
|
|
The hook module [`HookModule`] defines the primary hook interface [`IHook`]. It specifies that each hook should provide the `Version` and `Load` functions. The `Version` function returns the application name and version with which the plugin is compatible. The `Load` function produces an object that implements the callout points. The supported callout points are defined in the hook module as Go interfaces [`FooCallout`/`BarCallout`/`BazCallout`].
|
|
|
|
|
|
Additionally, the hook may implement the `SetupCLI()`. It will be called before loading the hook. It's responsible for preparing the hook-specific CLI parameters. They will be merged with the core parameters. The provided CLI arguments will be passed to the `Load` function.
|
|
|
The `SetupSettings` function accepts settings registry [`SettingsRegistry`]. This object allows registering the dynamic settings used by the hook. Users will have a possibility to change these values using UI (for server). Initially, it will not be used for the agent hooks.
|
|
|
|
|
|
The `Load` function accepts the observer of settings [`SettingsObserver`]. It may be used to register on settings notifications. The given function will be called on change of the specific setting.
|
|
|
|
|
|
The hook creator must pick one or more callout interfaces and implement them in a single structure [`FooImpl`/`BarBazImpl`]. The structure may provide the `Close` function. It will be called during a graceful shutdown. The structure instance must be created by the top-level `Load` function. The creator also must implement the top-level `Version` function using the current version [`CurrentVersion'] constant provided by the hook module.
|
|
|
|
|
|
The core needs to provide the hook manager [`AgentHookManager`/`ServerHookManager`] as a facade for the hooks. It is responsible for calling the callout points. The hook manager implements all supported callout points and defers calls to the executor [`HookExecutor`]. Executor contains all callout implementations from hooks and provides methods for calling them. The callouts can be called sequentially "one-by-one," to first success, to first fail, etc. The calling order is defined by the hook manager depending on the characteristics of the callout point. The construction of the executor has two phases. First, the hook manager creates an instance and registers all support callouts' types (interfaces). Next, the hook loader [`HookLoader`] retrieves the callout point objects and registers them in the executor. Now, the executor contains the mapping between a callout type (interface) and its implementations.
|
... | ... | @@ -197,6 +223,8 @@ The hook loader searches for the hook libraries in the directory specified by th |
|
|
|
|
|
The hook manager hides all details of the hook solution. The code that calls the callout point doesn't matter if a specific hook is registered or how many hooks implement it.
|
|
|
|
|
|
The hook loader doesn't load the hooks on the application startup. Instead, it fetches all CLI flags and merges them with the core flags. It allows to prompt the hook parameters on the standard output. The hooks are loaded when the CLI validation passes.
|
|
|
|
|
|
## Hook configuration
|
|
|
|
|
|
The section discusses the possibilities of the hook configuration. We need to analyze how easy and similar to the current solution the configuration will be. The configuration ways may have different capabilities, applicable for different kinds of hooks.
|
... | ... | @@ -221,6 +249,8 @@ The configuration parameters are read from the external configuration file. The |
|
|
|
|
|
The settings table is generic and may store integers, bools, or strings. We need only provide a solution to use the unique prefix for the setting names to avoid collisions. This solution can easily update the configuration parameters without reloading the hook.
|
|
|
|
|
|
The settings table is unavailable in the Stork Agent. We can store the data in memory. But there is no way to provide a new configuration in runtime. But I think that we can implement any gRPC endpoint if needed.
|
|
|
|
|
|
### Using API
|
|
|
|
|
|
The hook can provide a custom REST or gRPC endpoint to configure. It may be helpful for hooks that integrate Stork with third-party applications. They can dynamically control the hook behavior depending on external conditions.
|
... | ... | @@ -240,6 +270,7 @@ The hook provides a function that returns the CLI flags. This function is called |
|
|
| Integration with existing configuration | YES | YES | YES | NO | YES | NO | YES | MEDIUM |
|
|
|
|
|
|
### Summary
|
|
|
|
|
|
The above comparison shows that no one solution applies to all use cases. We should solve the problem in this way that will cover most of the topics and be easy to use by the authors of hooks.
|
|
|
Integrating the hook configuration with the standard configuration will be very convenient. It automatically handles the basic parsing and validation. It also prompts the possible options on standard output. The same solution to configure core and hooks should be the most user-friendly way.
|
|
|
We also need the possibility to re-configure the hooks in runtime. We should utilize the setting table. It was designed to store any configuration. It means that it should well handle multiple hook configurations. We can provide an observable pattern to encapsulate the database access and decrease the hook-side effort.
|
... | ... | |