Support remote zilla configuration with change detection#1071
Support remote zilla configuration with change detection#1071jfallows merged 47 commits intoaklivity:developfrom
Conversation
b6efc03 to
d9601d8
Compare
runtime/engine/src/main/java/io/aklivity/zilla/runtime/engine/config/EngineConfigReader.java
Fixed
Show fixed
Hide fixed
348e69f to
e1d5642
Compare
e1d5642 to
c02e5c4
Compare
...rc/main/java/io/aklivity/zilla/runtime/vault/filesystem/internal/FileSystemVaultHandler.java
Outdated
Show resolved
Hide resolved
...g-kafka/src/main/java/io/aklivity/zilla/runtime/binding/kafka/config/KafkaOptionsConfig.java
Outdated
Show resolved
Hide resolved
...ine/src/main/java/io/aklivity/zilla/runtime/engine/internal/watcher/EngineConfigWatcher.java
Outdated
Show resolved
Hide resolved
runtime/engine/src/main/java/io/aklivity/zilla/runtime/engine/Engine.java
Outdated
Show resolved
Hide resolved
runtime/engine/src/main/java/io/aklivity/zilla/runtime/engine/Engine.java
Outdated
Show resolved
Hide resolved
runtime/engine/src/main/java/io/aklivity/zilla/runtime/engine/config/ResourceResolver.java
Outdated
Show resolved
Hide resolved
runtime/engine/src/main/java/io/aklivity/zilla/runtime/engine/Engine.java
Outdated
Show resolved
Hide resolved
...e/engine/src/main/java/io/aklivity/zilla/runtime/engine/internal/registry/EngineManager.java
Outdated
Show resolved
Hide resolved
...e/engine/src/main/java/io/aklivity/zilla/runtime/engine/internal/registry/EngineManager.java
Outdated
Show resolved
Hide resolved
...e/engine/src/main/java/io/aklivity/zilla/runtime/engine/internal/registry/EngineManager.java
Outdated
Show resolved
Hide resolved
...e/engine/src/main/java/io/aklivity/zilla/runtime/engine/internal/registry/EngineManager.java
Outdated
Show resolved
Hide resolved
This reverts commit a370a7b.
|
|
||
| this.composite = namespaceGenerator.generate(binding, openapis, asyncapis, openapiSchemaIdsByApiId::get); | ||
| this.composite.readURL = binding.readURL; | ||
| this.composite.readLocation = binding.readLocation; |
There was a problem hiding this comment.
Is it possible to get rid of composite.readURL / composite.readLocation entirely?
runtime/engine/src/main/java/io/aklivity/zilla/runtime/engine/EngineConfiguration.java
Outdated
Show resolved
Hide resolved
| public transient long entryId; | ||
| public transient ToLongFunction<String> resolveId; | ||
| public transient Function<String, String> readURL; | ||
| public transient Function<String, String> readLocation; |
There was a problem hiding this comment.
Is it possible to remove this completely?
| { | ||
| public transient long id; | ||
| public transient Function<String, String> readURL; | ||
| public transient Function<Path, String> readPath; |
|
|
||
| public transient int id; | ||
| public transient Function<String, String> readURL; | ||
| public transient Function<String, String> readLocation; |
There was a problem hiding this comment.
Can we remove this one too?
| public ConfigWatcherTask( | ||
| FileSystem fileSystem, | ||
| Function<String, EngineConfig> configChangeListener, | ||
| Function<Path, String> readPath) |
There was a problem hiding this comment.
Can we remove readPath and use Files.readString(path) directly instead?
| String scheme = watchedPath.getFileSystem().provider().getScheme(); | ||
| if ("file".equals(scheme)) | ||
| { | ||
| registerFilePath(); | ||
| } | ||
| else if ("http".equals(scheme)) | ||
| { | ||
| watchKeys.add(watchedPath.register(watchService)); | ||
| } |
There was a problem hiding this comment.
Is this more a question of optimization than specific schemes?
There was a problem hiding this comment.
There is different behaviour here.
In case of the file filesystem we want to watch files, but actually we can only watch directories. But our file can be a symlink or the directory can be a symlink or its parent etc... So Barnabas created a very sophisticated logic to cover all those cases, which means in a generic case we have to watch more than one object (directory) so we can make sure we are watching the changes of our file.
In case of http filesystem we just watch a URL and that's it. So it's a 1:1 mapping hence just one line of code here.
| { | ||
| JwtOptionsConfig options = (JwtOptionsConfig) guard.options; | ||
| JwtGuardHandler handler = new JwtGuardHandler(options, context, supplyAuthorizedId, guard.readURL); | ||
| JwtGuardHandler handler = new JwtGuardHandler(options, context, supplyAuthorizedId, guard.readPath); |
There was a problem hiding this comment.
Can JwtGuardHandler use EngineContext.readPath(...) instead of guard.readPath?
...me/guard-jwt/src/main/java/io/aklivity/zilla/runtime/guard/jwt/internal/JwtGuardHandler.java
Outdated
Show resolved
Hide resolved
...m/src/test/java/io/aklivity/zilla/runtime/vault/filesystem/internal/FileSystemVaultTest.java
Outdated
Show resolved
Hide resolved
| WatchService watchService = null; | ||
| if (!"jar".equals(fileSystem.provider().getScheme())) // we can't watch in jar fs | ||
| { | ||
| try | ||
| { | ||
| watchService = fileSystem.newWatchService(); | ||
| } | ||
| catch (Exception ex) | ||
| { | ||
| rethrowUnchecked(ex); | ||
| } | ||
| } | ||
| this.watchService = watchService; |
There was a problem hiding this comment.
The idea of using a FileSystem abstraction is to move away from implementation specific knowledge of the filesystem as much as is practical in the engine.
The FileSystem.newWatchService() method is specified as an optional method, throwing UnsupportedOperationException on implementations not supporting it.
So we should be tolerant of that both now for jar scheme (used during tests) and later for other filesystems that also may not support newWatchService(), without needing to mention jar explicitly.
| WatchService watchService = null; | |
| if (!"jar".equals(fileSystem.provider().getScheme())) // we can't watch in jar fs | |
| { | |
| try | |
| { | |
| watchService = fileSystem.newWatchService(); | |
| } | |
| catch (Exception ex) | |
| { | |
| rethrowUnchecked(ex); | |
| } | |
| } | |
| this.watchService = watchService; | |
| this.watchService = newWatchService(fileSystem); |
private static WatchService newWatchService(
FileSystem fileSystem)
{
WatchService watcher = null;
try
{
watcher = fileSystem.newWatchService();
}
catch (UnsupportedOperationException ex)
{
// no watcher
}
catch (Exception ex)
{
rethrowUnchecked(ex);
}
return watcher;
}
| private String readLocation( | ||
| String location) | ||
| { | ||
| return readPath(resolvePath(location)); |
There was a problem hiding this comment.
Let's remove readLocation everywhere, and use Files.readString(path) instead at the call sites.
We should no longer need BindingConfig.readLocation directly given that EngineWorker.resolvePath(location) is available to all handlers, so BindingConfig.readLocation can be removed as well.
| public Path resolvePath( | ||
| String location) | ||
| { | ||
| return configPath.resolveSibling(location); | ||
| } |
There was a problem hiding this comment.
This method implementation should move to EngineWorker instead of passing a lambda from Engine.
Note: EngineWorker already has EngineConfig and can call config.configPath() in EngineWorker constructor instead of passing as an explicit constructor parameter.
| this.configPath, | ||
| this::readPath, | ||
| this::readLocation); |
There was a problem hiding this comment.
None of these need to be passed to EngineManager constructor.
Only configPath is needed and can be obtained from EngineConfiguration (already passed).
| catch (Exception ex) | ||
| { | ||
| output = ""; | ||
| result = ""; |
There was a problem hiding this comment.
I think we don't want to default to empty string here.
After moving Files.readString(path) to each call site, handling an IOException will let the caller decide the best remedy instead of assuming empty string is always appropriate.
| { | ||
| EngineWorker worker = | ||
| new EngineWorker(config, tasks, labels, errorHandler, tuning::affinity, bindings, exporters, | ||
| new EngineWorker(config, tasks, labels, errorHandler, tuning::affinity, this::resolvePath, bindings, exporters, |
There was a problem hiding this comment.
| new EngineWorker(config, tasks, labels, errorHandler, tuning::affinity, this::resolvePath, bindings, exporters, | |
| new EngineWorker(config, tasks, labels, errorHandler, tuning::affinity, bindings, exporters, |
Move resolvePath to EngineWorker implementation instead.
| final Map<String, Guard> guardsByType = guards.stream() | ||
| .collect(Collectors.toMap(g -> g.name(), g -> g)); | ||
|
|
||
| this.configPath = config.configPath(); |
There was a problem hiding this comment.
Looks like configPath is no longer needed as Engine field after resovlePath(...) method is moved to EngineWorker implementation directly.
| public Path configPath() | ||
| { | ||
| return ENGINE_CONFIG_PATH.get(this); | ||
| } | ||
|
|
There was a problem hiding this comment.
This configPath() method looks good.
Let's keep the ENGINE_CONFIG_URL constant in place for now, to support zilla.properties defining zilla.engine.config.url property, but remove all calls to EngineConfiguration.configURL() from the code, and replace with corresponding EngineConfiguration.configPath() method calls instead.
|
|
||
| public transient int id; | ||
| public transient Function<String, String> readURL; | ||
| public transient Function<String, String> readLocation; |
| private List<String> resolveResources() | ||
| { | ||
| List<String> resources = new LinkedList<>(); | ||
| for (CatalogConfig catalog : catalogs) | ||
| { | ||
| if (FILESYSTEM.equals(catalog.type)) | ||
| { | ||
| resources.addAll(catalog.options.resources); | ||
| } | ||
| } | ||
| for (VaultConfig vault : vaults) | ||
| { | ||
| if (FILESYSTEM.equals(vault.type)) | ||
| { | ||
| resources.addAll(vault.options.resources); | ||
| } | ||
| } | ||
| return resources; |
There was a problem hiding this comment.
This approach assumes global knowledge of all possible implementations and assumes their behavior, such that a catalog or vault other than filesystem cannot contribute resources to be watched.
We need a more general strategy instead.
Please include all resources from any binding, guard, vault, catalog or telemetry exporter.
Note: instead of calling a non-static method from the constructor that relies on constructor field assignment, please make this method static and pass all required state as parameters, such as bindings, guards, vaults, catalogs, and telemetry.
Remove GuardedConfig.readPath Rename ConfigAdapterContext.readLocation to ConfigAdapterContext.readResource Deprecate ConfigAdapterContext for removal Add EngineConfiguration.configURI() to resolve absolute file path Remove EngineConfiguration.configURL() but use to default EngineConfiguration.configURI() Simplify EngineManager Gather resources on NamespaceConfig from member configs Consolidate config and resource watcher as EngineConfigWatcher Resolve watched paths based on path filesystem provider scheme Configure HttpFileSystem poll internval duration via Map<String,?> env Simplify ReconfigureHttpIT scripts and include /zilla.yaml in request path HttpFileSystem per origin (root path) not per individual path Track HttpPath change count vs read count to implement simple caching
Description
This change moves away from URL to Path abstraction and introduces the http file system, so the config and the resources can be watched over file and http protocol and opens up the potential for further extension.
Fixes #1061