mirror of
https://github.com/LuckPerms/LuckPerms.git
synced 2024-11-17 08:05:35 +01:00
Add healthcheck for standalone app
This commit is contained in:
parent
fe0cb2df25
commit
b1953632c5
@ -26,9 +26,11 @@
|
||||
package me.lucko.luckperms.standalone.app;
|
||||
|
||||
import me.lucko.luckperms.standalone.app.integration.CommandExecutor;
|
||||
import me.lucko.luckperms.standalone.app.integration.DockerCommandSocket;
|
||||
import me.lucko.luckperms.standalone.app.integration.HealthReporter;
|
||||
import me.lucko.luckperms.standalone.app.integration.ShutdownCallback;
|
||||
import me.lucko.luckperms.standalone.app.integration.TerminalInterface;
|
||||
import me.lucko.luckperms.standalone.app.utils.DockerCommandSocket;
|
||||
import me.lucko.luckperms.standalone.app.utils.HeartbeatHttpServer;
|
||||
import me.lucko.luckperms.standalone.app.utils.TerminalInterface;
|
||||
|
||||
import net.luckperms.api.LuckPerms;
|
||||
|
||||
@ -55,12 +57,16 @@ public class LuckPermsApplication implements AutoCloseable {
|
||||
private LuckPerms luckPermsApi;
|
||||
/** A command executor interface to run LuckPerms commands */
|
||||
private CommandExecutor commandExecutor;
|
||||
/** An interface that can poll the health of the application */
|
||||
private HealthReporter healthReporter;
|
||||
|
||||
/** If the application is running */
|
||||
private final AtomicBoolean running = new AtomicBoolean(true);
|
||||
|
||||
/** The docker command socket */
|
||||
private DockerCommandSocket dockerCommandSocket;
|
||||
/** The heartbeat http server */
|
||||
private HeartbeatHttpServer heartbeatHttpServer;
|
||||
|
||||
public LuckPermsApplication(ShutdownCallback shutdownCallback) {
|
||||
this.shutdownCallback = shutdownCallback;
|
||||
@ -75,6 +81,7 @@ public class LuckPermsApplication implements AutoCloseable {
|
||||
List<String> arguments = Arrays.asList(args);
|
||||
if (arguments.contains("--docker")) {
|
||||
this.dockerCommandSocket = DockerCommandSocket.createAndStart(3000, terminal);
|
||||
this.heartbeatHttpServer = HeartbeatHttpServer.createAndStart(3001, this.healthReporter);
|
||||
}
|
||||
|
||||
terminal.start(); // blocking
|
||||
@ -95,6 +102,14 @@ public class LuckPermsApplication implements AutoCloseable {
|
||||
LOGGER.warn(e);
|
||||
}
|
||||
}
|
||||
|
||||
if (this.heartbeatHttpServer != null) {
|
||||
try {
|
||||
this.heartbeatHttpServer.close();
|
||||
} catch (Exception e) {
|
||||
LOGGER.warn(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public AtomicBoolean runningState() {
|
||||
@ -111,6 +126,11 @@ public class LuckPermsApplication implements AutoCloseable {
|
||||
this.commandExecutor = commandExecutor;
|
||||
}
|
||||
|
||||
// called before start()
|
||||
public void setHealthReporter(HealthReporter healthReporter) {
|
||||
this.healthReporter = healthReporter;
|
||||
}
|
||||
|
||||
public String getVersion() {
|
||||
return "@version@";
|
||||
}
|
||||
|
@ -0,0 +1,77 @@
|
||||
/*
|
||||
* This file is part of LuckPerms, licensed under the MIT License.
|
||||
*
|
||||
* Copyright (c) lucko (Luck) <luck@lucko.me>
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package me.lucko.luckperms.standalone.app.integration;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* An interface able to provide information about the application/plugin health.
|
||||
*/
|
||||
public interface HealthReporter {
|
||||
|
||||
/**
|
||||
* Polls the current health status.
|
||||
*
|
||||
* @return the health status
|
||||
*/
|
||||
Health poll();
|
||||
|
||||
final class Health {
|
||||
private static final Gson GSON = new Gson();
|
||||
|
||||
private final boolean up;
|
||||
private final Map<String, String> details;
|
||||
|
||||
Health(boolean up, Map<String, String> details) {
|
||||
this.up = up;
|
||||
this.details = details;
|
||||
}
|
||||
|
||||
public boolean isUp() {
|
||||
return this.up;
|
||||
}
|
||||
|
||||
public Map<String, String> details() {
|
||||
return this.details;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return GSON.toJson(this);
|
||||
}
|
||||
|
||||
public static Health up(Map<String, String> details) {
|
||||
return new Health(true, details);
|
||||
}
|
||||
|
||||
public static Health down(Map<String, String> details) {
|
||||
return new Health(false, details);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -23,7 +23,7 @@
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package me.lucko.luckperms.standalone.app.integration;
|
||||
package me.lucko.luckperms.standalone.app.utils;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
@ -56,7 +56,7 @@ public class DockerCommandSocket extends ServerSocket implements Runnable {
|
||||
thread.setDaemon(true);
|
||||
thread.start();
|
||||
|
||||
LOGGER.info("Created Docker command socket on port 3000");
|
||||
LOGGER.info("Created Docker command socket on port " + port);
|
||||
} catch (Exception e) {
|
||||
LOGGER.error("Error starting docker command socket", e);
|
||||
}
|
@ -0,0 +1,96 @@
|
||||
/*
|
||||
* This file is part of LuckPerms, licensed under the MIT License.
|
||||
*
|
||||
* Copyright (c) lucko (Luck) <luck@lucko.me>
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package me.lucko.luckperms.standalone.app.utils;
|
||||
|
||||
import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
||||
import com.sun.net.httpserver.HttpExchange;
|
||||
import com.sun.net.httpserver.HttpHandler;
|
||||
import com.sun.net.httpserver.HttpServer;
|
||||
|
||||
import me.lucko.luckperms.standalone.app.integration.HealthReporter;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
/**
|
||||
* Provides a tiny http server indicating the current status of the app
|
||||
*/
|
||||
public class HeartbeatHttpServer implements HttpHandler, AutoCloseable {
|
||||
private static final Logger LOGGER = LogManager.getLogger(HeartbeatHttpServer.class);
|
||||
|
||||
private static final ExecutorService EXECUTOR = Executors.newCachedThreadPool(new ThreadFactoryBuilder()
|
||||
.setDaemon(true)
|
||||
.setNameFormat("heartbeat-http-server-%d")
|
||||
.build()
|
||||
);
|
||||
|
||||
public static HeartbeatHttpServer createAndStart(int port, HealthReporter healthReporter) {
|
||||
HeartbeatHttpServer socket = null;
|
||||
|
||||
try {
|
||||
socket = new HeartbeatHttpServer(healthReporter, port);
|
||||
LOGGER.info("Created Heartbeat HTTP server on port " + port);
|
||||
} catch (Exception e) {
|
||||
LOGGER.error("Error starting Heartbeat HTTP server", e);
|
||||
}
|
||||
|
||||
return socket;
|
||||
}
|
||||
|
||||
private final HealthReporter healthReporter;
|
||||
private final HttpServer server;
|
||||
|
||||
public HeartbeatHttpServer(HealthReporter healthReporter, int port) throws IOException {
|
||||
this.healthReporter = healthReporter;
|
||||
this.server = HttpServer.create(new InetSocketAddress(port), 50);
|
||||
this.server.createContext("/health", this);
|
||||
this.server.setExecutor(EXECUTOR);
|
||||
this.server.start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handle(HttpExchange exchange) throws IOException {
|
||||
HealthReporter.Health health = this.healthReporter.poll();
|
||||
byte[] response = health.toString().getBytes(StandardCharsets.UTF_8);
|
||||
|
||||
exchange.sendResponseHeaders(health.isUp() ? 200 : 503, response.length);
|
||||
try (OutputStream responseBody = exchange.getResponseBody()) {
|
||||
responseBody.write(response);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
this.server.stop(0);
|
||||
}
|
||||
}
|
@ -23,9 +23,10 @@
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package me.lucko.luckperms.standalone.app.integration;
|
||||
package me.lucko.luckperms.standalone.app.utils;
|
||||
|
||||
import me.lucko.luckperms.standalone.app.LuckPermsApplication;
|
||||
import me.lucko.luckperms.standalone.app.integration.CommandExecutor;
|
||||
|
||||
import net.minecrell.terminalconsole.SimpleTerminalConsole;
|
||||
|
@ -20,3 +20,6 @@ RUN mkdir data
|
||||
VOLUME ["/opt/luckperms/data"]
|
||||
|
||||
CMD ["java", "-jar", "luckperms-standalone.jar", "--docker"]
|
||||
|
||||
HEALTHCHECK --interval=30s --timeout=15s --start-period=20s \
|
||||
CMD wget http://localhost:3001/health -q -O - | grep -c '"up":true' || exit 1
|
||||
|
@ -38,11 +38,11 @@ import me.lucko.luckperms.common.model.manager.user.StandardUserManager;
|
||||
import me.lucko.luckperms.common.plugin.AbstractLuckPermsPlugin;
|
||||
import me.lucko.luckperms.common.plugin.util.AbstractConnectionListener;
|
||||
import me.lucko.luckperms.common.sender.Sender;
|
||||
import me.lucko.luckperms.standalone.app.integration.SingletonPlayer;
|
||||
import me.lucko.luckperms.standalone.app.LuckPermsApplication;
|
||||
import me.lucko.luckperms.standalone.dummy.StandaloneContextManager;
|
||||
import me.lucko.luckperms.standalone.dummy.StandaloneDummyConnectionListener;
|
||||
import me.lucko.luckperms.standalone.dummy.StandaloneEventBus;
|
||||
import me.lucko.luckperms.standalone.app.integration.SingletonPlayer;
|
||||
import me.lucko.luckperms.standalone.stub.StandaloneContextManager;
|
||||
import me.lucko.luckperms.standalone.stub.StandaloneDummyConnectionListener;
|
||||
import me.lucko.luckperms.standalone.stub.StandaloneEventBus;
|
||||
|
||||
import net.luckperms.api.LuckPerms;
|
||||
import net.luckperms.api.query.QueryOptions;
|
||||
@ -57,6 +57,8 @@ import java.util.stream.Stream;
|
||||
public class LPStandalonePlugin extends AbstractLuckPermsPlugin {
|
||||
private final LPStandaloneBootstrap bootstrap;
|
||||
|
||||
private boolean running = false;
|
||||
|
||||
private StandaloneSenderFactory senderFactory;
|
||||
private StandaloneDummyConnectionListener connectionListener;
|
||||
private StandaloneCommandManager commandManager;
|
||||
@ -78,6 +80,10 @@ public class LPStandalonePlugin extends AbstractLuckPermsPlugin {
|
||||
return this.bootstrap.getLoader();
|
||||
}
|
||||
|
||||
public boolean isRunning() {
|
||||
return this.running;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setupSenderFactory() {
|
||||
this.senderFactory = new StandaloneSenderFactory(this);
|
||||
@ -144,16 +150,17 @@ public class LPStandalonePlugin extends AbstractLuckPermsPlugin {
|
||||
@Override
|
||||
protected void registerApiOnPlatform(LuckPerms api) {
|
||||
this.bootstrap.getLoader().setApi(api);
|
||||
this.bootstrap.getLoader().setHealthReporter(new StandaloneHealthReporter(this));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void performFinalSetup() {
|
||||
|
||||
this.running = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void removePlatformHooks() {
|
||||
|
||||
this.running = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -0,0 +1,70 @@
|
||||
/*
|
||||
* This file is part of LuckPerms, licensed under the MIT License.
|
||||
*
|
||||
* Copyright (c) lucko (Luck) <luck@lucko.me>
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package me.lucko.luckperms.standalone;
|
||||
|
||||
import me.lucko.luckperms.common.locale.TranslationManager;
|
||||
import me.lucko.luckperms.standalone.app.integration.HealthReporter;
|
||||
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class StandaloneHealthReporter implements HealthReporter {
|
||||
private final LPStandalonePlugin plugin;
|
||||
|
||||
public StandaloneHealthReporter(LPStandalonePlugin plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Health poll() {
|
||||
if (!this.plugin.isRunning()) {
|
||||
return Health.down(Collections.emptyMap());
|
||||
}
|
||||
|
||||
Map<String, String> meta = this.plugin.getStorage().getMeta().entrySet().stream()
|
||||
.collect(Collectors.toMap(
|
||||
e -> render(e.getKey()).toLowerCase(Locale.ROOT),
|
||||
e -> render(e.getValue())
|
||||
));
|
||||
|
||||
if ("false".equals(meta.get("connected"))) {
|
||||
return Health.down(Collections.singletonMap("reason", "storage disconnected"));
|
||||
}
|
||||
|
||||
return Health.up(meta);
|
||||
}
|
||||
|
||||
private static String render(Component component) {
|
||||
return PlainTextComponentSerializer.plainText().serialize(
|
||||
TranslationManager.render(component)
|
||||
);
|
||||
}
|
||||
}
|
@ -23,7 +23,7 @@
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package me.lucko.luckperms.standalone.dummy;
|
||||
package me.lucko.luckperms.standalone.stub;
|
||||
|
||||
import me.lucko.luckperms.common.config.ConfigKeys;
|
||||
import me.lucko.luckperms.common.context.manager.ContextManager;
|
@ -23,7 +23,7 @@
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package me.lucko.luckperms.standalone.dummy;
|
||||
package me.lucko.luckperms.standalone.stub;
|
||||
|
||||
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
|
||||
import me.lucko.luckperms.common.plugin.util.AbstractConnectionListener;
|
@ -23,7 +23,7 @@
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package me.lucko.luckperms.standalone.dummy;
|
||||
package me.lucko.luckperms.standalone.stub;
|
||||
|
||||
import me.lucko.luckperms.common.api.LuckPermsApiProvider;
|
||||
import me.lucko.luckperms.common.event.AbstractEventBus;
|
Loading…
Reference in New Issue
Block a user