(control) Node configuration

This commit is contained in:
Viktor Lofgren 2023-10-14 16:47:52 +02:00
parent 4baf9527d7
commit 6308a8dfcd
3 changed files with 80 additions and 9 deletions

View File

@ -1,5 +1,6 @@
package nu.marginalia.nodecfg; package nu.marginalia.nodecfg;
import com.google.inject.Inject;
import com.zaxxer.hikari.HikariDataSource; import com.zaxxer.hikari.HikariDataSource;
import nu.marginalia.nodecfg.model.NodeConfiguration; import nu.marginalia.nodecfg.model.NodeConfiguration;
@ -11,6 +12,7 @@ public class NodeConfigurationService {
private final HikariDataSource dataSource; private final HikariDataSource dataSource;
@Inject
public NodeConfigurationService(HikariDataSource dataSource) { public NodeConfigurationService(HikariDataSource dataSource) {
this.dataSource = dataSource; this.dataSource = dataSource;
} }
@ -48,7 +50,9 @@ public class NodeConfigurationService {
FROM NODE_CONFIGURATION FROM NODE_CONFIGURATION
""")) { """)) {
var rs = qs.executeQuery(); var rs = qs.executeQuery();
List<NodeConfiguration> ret = new ArrayList<>(); List<NodeConfiguration> ret = new ArrayList<>();
while (rs.next()) { while (rs.next()) {
ret.add(new NodeConfiguration( ret.add(new NodeConfiguration(
rs.getInt("ID"), rs.getInt("ID"),

View File

@ -10,6 +10,8 @@ import nu.marginalia.control.node.model.*;
import nu.marginalia.control.sys.model.EventLogEntry; import nu.marginalia.control.sys.model.EventLogEntry;
import nu.marginalia.control.sys.svc.EventLogService; import nu.marginalia.control.sys.svc.EventLogService;
import nu.marginalia.control.sys.svc.HeartbeatService; import nu.marginalia.control.sys.svc.HeartbeatService;
import nu.marginalia.nodecfg.NodeConfigurationService;
import nu.marginalia.nodecfg.model.NodeConfiguration;
import nu.marginalia.storage.FileStorageService; import nu.marginalia.storage.FileStorageService;
import nu.marginalia.executor.client.ExecutorClient; import nu.marginalia.executor.client.ExecutorClient;
import nu.marginalia.executor.model.crawl.RecrawlParameters; import nu.marginalia.executor.model.crawl.RecrawlParameters;
@ -37,6 +39,7 @@ public class ControlNodeService {
private final ExecutorClient executorClient; private final ExecutorClient executorClient;
private final HikariDataSource dataSource; private final HikariDataSource dataSource;
private final ServiceMonitors monitors; private final ServiceMonitors monitors;
private final NodeConfigurationService nodeConfigurationService;
private final Logger logger = LoggerFactory.getLogger(getClass()); private final Logger logger = LoggerFactory.getLogger(getClass());
@ -48,7 +51,7 @@ public class ControlNodeService {
HeartbeatService heartbeatService, HeartbeatService heartbeatService,
ExecutorClient executorClient, ExecutorClient executorClient,
HikariDataSource dataSource, HikariDataSource dataSource,
ServiceMonitors monitors) ServiceMonitors monitors, NodeConfigurationService nodeConfigurationService)
{ {
this.fileStorageService = fileStorageService; this.fileStorageService = fileStorageService;
this.rendererFactory = rendererFactory; this.rendererFactory = rendererFactory;
@ -57,6 +60,7 @@ public class ControlNodeService {
this.executorClient = executorClient; this.executorClient = executorClient;
this.dataSource = dataSource; this.dataSource = dataSource;
this.monitors = monitors; this.monitors = monitors;
this.nodeConfigurationService = nodeConfigurationService;
} }
public void register() throws IOException { public void register() throws IOException {
@ -82,7 +86,9 @@ public class ControlNodeService {
Spark.post("/public/nodes/:id/storage/new-specs", this::createNewSpecsAction); Spark.post("/public/nodes/:id/storage/new-specs", this::createNewSpecsAction);
Spark.get("/public/nodes/:id/storage/:view", this::nodeStorageListModel, storageListRenderer::render); Spark.get("/public/nodes/:id/storage/:view", this::nodeStorageListModel, storageListRenderer::render);
Spark.get("/public/nodes/:id/configuration", this::nodeConfigModel, configRenderer::render); Spark.get("/public/nodes/:id/configuration", this::nodeConfigModel, configRenderer::render);
Spark.post("/public/nodes/:id/configuration", this::updateConfigModel, configRenderer::render);
Spark.post("/public/nodes/:id/storage/recrawl-auto", this::triggerAutoRecrawl); Spark.post("/public/nodes/:id/storage/recrawl-auto", this::triggerAutoRecrawl);
Spark.post("/public/nodes/:id/storage/process-auto", this::triggerAutoProcess); Spark.post("/public/nodes/:id/storage/process-auto", this::triggerAutoProcess);
@ -284,11 +290,45 @@ public class ControlNodeService {
"storage", storage); "storage", storage);
} }
private Object nodeConfigModel(Request request, Response response) { private Object nodeConfigModel(Request request, Response response) throws SQLException {
int nodeId = Integer.parseInt(request.params("id")); int nodeId = Integer.parseInt(request.params("id"));
Map<String, Path> storage = new HashMap<>();
for (var baseType : List.of(FileStorageBaseType.CURRENT, FileStorageBaseType.WORK, FileStorageBaseType.BACKUP, FileStorageBaseType.STORAGE)) {
Optional.ofNullable(fileStorageService.getStorageBase(baseType, nodeId))
.map(FileStorageBase::asPath)
.ifPresent(path -> storage.put(baseType.toString().toLowerCase(), path));
}
return Map.of( return Map.of(
"node", new IndexNode(nodeId) "node", new IndexNode(nodeId),
); "config", Objects.requireNonNull(nodeConfigurationService.get(nodeId), "Failed to fetch configuration"),
"storage", storage);
}
private Object updateConfigModel(Request request, Response response) throws SQLException {
int nodeId = Integer.parseInt(request.params("id"));
String act = request.queryParams("act");
if ("config".equals(act)) {
var newConfig = new NodeConfiguration(
nodeId,
request.queryParams("description"),
"on".equalsIgnoreCase(request.queryParams("acceptQueries")),
"on".equalsIgnoreCase(request.queryParams("disabled"))
);
nodeConfigurationService.save(newConfig);
}
else if ("storage".equals(act)) {
throw new UnsupportedOperationException();
}
else {
Spark.halt(400);
}
return nodeConfigModel(request, response);
} }
private Object nodeOverviewModel(Request request, Response response) throws SQLException { private Object nodeOverviewModel(Request request, Response response) throws SQLException {

View File

@ -29,14 +29,41 @@
</nav> </nav>
<div class="mt-2"> <h1 class="my-5">Node Configuration</h1>
{{> control/partials/processes-table }}
</div> <div class="m-4 p-4 border">
<div class="mt-2"> <h2>Settings</h2>
{{> control/partials/events-table-summary }}
<form method="post" action="?act=config">
<div class="mb-3">
<label for="description" class="form-label">Description</label>
<input class="form-control" type="text" name="description" value="{{config.description}}"/>
</div>
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" role="switch" name="acceptQueries" {{#if config.acceptQueries}}checked{{/if}}>
<label class="form-check-label" for="acceptQueries">Accept queries</label>
<div class="form-text">Sets whether queries will be routed to this node</div>
</div>
<div class="form-check form-switch mb-3">
<input class="form-check-input" type="checkbox" role="switch" name="disabled" {{#if config.disabled}}checked{{/if}}>
<label class="form-check-label" for="disabled">Disabled</label>
<div class="form-text">Disabling a node is a soft delete that prevents the index and
control service from starting</div>
</div>
<button type="submit" class="btn btn-primary">Save</button>
</form>
</div> </div>
</div> </div>
</div>
</body> </body>
{{> control/partials/foot-includes }} {{> control/partials/foot-includes }}
</html> </html>