6 Commits

18 changed files with 119 additions and 193 deletions
+1 -1
View File
@@ -39,7 +39,7 @@ val detag by configurations.creating {
} }
dependencies { dependencies {
detag("com.r35157.tools:detag-impl_ref:0.1-dev") detag("com.r35157.tools:detag-impl_ref:0.1.0")
compileOnly("org.jetbrains:annotations:26.1.0") compileOnly("org.jetbrains:annotations:26.1.0")
runtimeOnly("org.apache.logging.log4j:log4j-core:2.26.0") runtimeOnly("org.apache.logging.log4j:log4j-core:2.26.0")
runtimeOnly("org.apache.logging.log4j:log4j-slf4j2-impl:2.26.0") runtimeOnly("org.apache.logging.log4j:log4j-slf4j2-impl:2.26.0")
+4 -4
View File
@@ -1,4 +1,4 @@
# Asset Direction Target TRIGGER SEVERITY NOTE # ID Asset Direction Target TRIGGER SEVERITY NOTE
############################################################# ################################################################################################################
SOL BELOW 70.0 ONETIME 2 "ALARM: Risiko for Perps Solana long LIKVIDERING!" 1 SOL BELOW 170.0 ONETIME INFO "EMERGENCY: Risiko for Perps Solana long LIKVIDERING!"
SOL BELOW 71.4 ONETIME 2 "ALARM: Risiko for Solana Raydium LÅN LIKVIDERING!" 2 SOL BELOW 71.4 ONETIME CRITICAL "CRITICAL: Risiko for Solana Raydium LÅN LIKVIDERING!"
-59
View File
@@ -1,59 +0,0 @@
#!/usr/bin/env bash
set -euo pipefail
JREVERSION=25.0.2-0
export VERSION=$(git describe --tags --exact-match 2>/dev/null \
|| git symbolic-ref --short -q HEAD \
|| git rev-parse --short HEAD)
GITHASH=$(git rev-parse --short=8 HEAD)
echo "Preparing Docker image for '${VERSION}'..."
mkdir -p build/docker/{conf,libs}
cp src/main/docker/run.sh build/docker/run.sh
cp libs/*.jar build/docker/libs/
cp conf/* build/docker/conf/
sed \
-e "s|_VERSION_|${VERSION}|g" \
-e "s|_JREVERSION_|${JREVERSION}|g" \
src/main/docker/Dockerfile_template \
> build/docker/Dockerfile
GROUP=r35157
NAME=nenjimhub
BASETAG=dockerreg.r35157.com/${GROUP}/${NAME}
HASHTAG=${BASETAG}:${GITHASH}_amd64
VERSIONTAG=${BASETAG}:${VERSION}_amd64
LATESTTAG=${BASETAG}:latest_amd64
echo "Building ${HASHTAG}..."
docker buildx build \
--load \
-t ${HASHTAG} \
build/docker
docker tag ${HASHTAG} ${VERSIONTAG}
docker tag ${HASHTAG} ${LATESTTAG}
sed \
-e "s|_HASHTAG_|${HASHTAG}|g" \
-e "s|_VERSIONTAG_|${VERSIONTAG}|g" \
-e "s|_LATESTTAG_|${LATESTTAG}|g" \
src/main/docker/publish_template.sh \
> build/docker/publish.sh
chmod 755 build/docker/publish.sh
echo ""
echo "Docker image ready:"
echo " ${HASHTAG}"
echo " ${VERSIONTAG}"
echo " ${LATESTTAG}"
-5
View File
@@ -5,10 +5,5 @@ export VERSION=$(git describe --tags --exact-match 2>/dev/null \
|| git symbolic-ref --short -q HEAD \ || git symbolic-ref --short -q HEAD \
|| git rev-parse --short HEAD) || git rev-parse --short HEAD)
./docker.sh
echo "Publishing artifact to local Maven repo ($VERSION)..." echo "Publishing artifact to local Maven repo ($VERSION)..."
./gradlew -Pversion=$VERSION publishToMavenLocal ./gradlew -Pversion=$VERSION publishToMavenLocal
echo "Publishing docker container to public 'dockerreg.r35157.com' ($VERSION)..."
./build/docker/publish.sh
-1
View File
@@ -1 +0,0 @@
src/main/docker/run.sh
Executable
+18
View File
@@ -0,0 +1,18 @@
#!/usr/bin/env bash
set -euo pipefail
shopt -s nullglob
jars=(libs/*.jar)
if (( ${#jars[@]} == 0 )); then
echo "ERROR: No JARs found in libs/" >&2
exit 1
fi
CLASSPATH=$(IFS=:; echo "${jars[*]}")
exec java \
--enable-preview \
-Dlog4j.configurationFile=conf/log4j2.xml \
-cp "$CLASSPATH" \
com.r35157.nenjim.hubd.impl.ref.Main
-43
View File
@@ -1,43 +0,0 @@
FROM dockerreg.r35157.com/r35157/jre:_JREVERSION__amd64
LABEL maintainer="Minimons <minimons@r35157.com>"
# Setup environment
ENV APP_HOME=/usr/local/software/nenjimhub
WORKDIR /usr/local/software
USER root
RUN mkdir nenjimhub-_VERSION_ \
&& ln -s nenjimhub-_VERSION_ nenjimhub
WORKDIR nenjimhub
RUN mkdir libs
# These dirs are expected to be overshadowed by host mounts
RUN mkdir conf logs && chown user:user logs
# Set timezone
ENV TZ=Europe/Copenhagen
RUN apt-get update
RUN apt-get install -y tzdata \
&& ln -snf /usr/share/zoneinfo/$TZ /etc/localtime \
&& echo $TZ > /etc/timezone \
&& dpkg-reconfigure -f noninteractive tzdata
# Clean-up
RUN apt-get autoremove -y \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*
# Install application and configuration (Do this as late as possible to be able to reuse layers between builds)
COPY run.sh .
COPY conf/* conf/
COPY libs/*.jar libs/
RUN chown root:root -R conf
RUN chmod 755 ./run.sh \
&& chmod -R a+rX /usr/local/software/nenjimhub-_VERSION_
USER user:user
CMD ["./run.sh"]
-9
View File
@@ -1,9 +0,0 @@
#!/usr/bin/env bash
set -euo pipefail
echo "Publishing '_HASHTAG_', '_VERSIONTAG_' and '_LATESTTAG_'..."
docker push _HASHTAG_
docker push _VERSIONTAG_
docker push _LATESTTAG_
echo "Publishing completed!"
-18
View File
@@ -1,18 +0,0 @@
#!/usr/bin/env bash
set -euo pipefail
shopt -s nullglob
jars=(libs/*.jar)
if (( ${#jars[@]} == 0 )); then
echo "ERROR: No JARs found in libs/" >&2
exit 1
fi
CLASSPATH=$(IFS=:; echo "${jars[*]}")
exec java \
--enable-preview \
-Dlog4j.configurationFile=conf/log4j2.xml \
-cp "$CLASSPATH" \
com.r35157.nenjim.hubd.impl.ref.Main
-19
View File
@@ -1,19 +0,0 @@
version: '3.9'
services:
nenjimhub:
hostname: nenjimhub
image: dockerreg.r35157.com/r35157/nenjimhub:latest
deploy:
replicas: 1
resources:
limits:
memory: 128m
restart_policy:
condition: any
delay: 5s
max_attempts: 3
window: 120s
volumes:
- /home/op/nenjimhub/conf:/usr/local/software/nenjimhub/conf
- /home/op/nenjimhub/logs:/usr/local/software/nenjimhub/logs
-3
View File
@@ -1,3 +0,0 @@
#!/bin/bash
docker stack deploy --compose-file /home/op/stack.yml stack --detach=true
@@ -0,0 +1,10 @@
package com.r35157.jupiterperpsalarm;
public enum AlarmSeverity {
EMERGENCY, // Repeated wake-up alarm - sound always
CRITICAL, // One-shot wake-up alarm - sound always
WARN, // Audible warning - respecting quiet hours though
INFO, // Normal notification with visual and audible feedback
SILENT, // Low-priority notification - visual feedback without sound/vibration
GHOST // No visual/audible notification - only visible inside the Pushover app
}
@@ -1,5 +1,7 @@
package com.r35157.jupiterperpsalarm.impl.ref; package com.r35157.jupiterperpsalarm.impl.ref;
import com.r35157.jupiterperpsalarm.AlarmSeverity;
import java.io.IOException; import java.io.IOException;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.nio.file.Files; import java.nio.file.Files;
@@ -41,18 +43,26 @@ public final class AlarmConfigurationParser {
static PriceAlarmDefinition parseLine(String line) { static PriceAlarmDefinition parseLine(String line) {
Cursor cursor = new Cursor(line); Cursor cursor = new Cursor(line);
int id = Integer.parseInt(cursor.nextToken("id"));
JupiterPerpsAsset asset = JupiterPerpsAsset.valueOf( JupiterPerpsAsset asset = JupiterPerpsAsset.valueOf(
cursor.nextToken("asset").toUpperCase(Locale.ROOT) cursor.nextToken("asset").toUpperCase(Locale.ROOT)
); );
PriceDirection direction = PriceDirection.valueOf( PriceDirection direction = PriceDirection.valueOf(
cursor.nextToken("direction").toUpperCase(Locale.ROOT) cursor.nextToken("direction").toUpperCase(Locale.ROOT)
); );
BigDecimal target = new BigDecimal(cursor.nextToken("target")); BigDecimal target = new BigDecimal(cursor.nextToken("target"));
AlarmTrigger trigger = AlarmTrigger.valueOf( AlarmTrigger trigger = AlarmTrigger.valueOf(
cursor.nextToken("trigger").toUpperCase(Locale.ROOT) cursor.nextToken("trigger").toUpperCase(Locale.ROOT)
); );
int severity = Integer.parseInt(cursor.nextToken("severity"));
AlarmSeverity severity = AlarmSeverity.valueOf(
cursor.nextToken("severity").toUpperCase()
);
String note = cursor.nextQuotedString("note"); String note = cursor.nextQuotedString("note");
cursor.skipWhitespace(); cursor.skipWhitespace();
@@ -63,6 +73,7 @@ public final class AlarmConfigurationParser {
} }
return new PriceAlarmDefinition( return new PriceAlarmDefinition(
id,
asset, asset,
direction, direction,
target, target,
@@ -16,10 +16,9 @@ public final class JupiterPerpsAlarmImpl {
try { try {
config = Config.parse(args, System.getenv()); config = Config.parse(args, System.getenv());
} catch (IllegalArgumentException exception) { } catch (IllegalArgumentException exception) {
System.err.println(exception.getMessage());
printUsage(); printUsage();
System.exit(2); String errMsg = "Could not parse configuration for JupiterPerpsAlarm: " + exception.getMessage() + "!";
return; throw new IllegalStateException(errMsg, exception);
} }
if (config.selfTest()) { if (config.selfTest()) {
@@ -32,9 +31,8 @@ public final class JupiterPerpsAlarmImpl {
try { try {
definitions = AlarmConfigurationParser.parse(config.alarmConfiguration()); definitions = AlarmConfigurationParser.parse(config.alarmConfiguration());
} catch (Exception exception) { } catch (Exception exception) {
System.err.println("Could not load alarm configuration: " + exception.getMessage()); String errMsg = "Could not load alarm configuration: " + exception.getMessage() + "!";
System.exit(2); throw new IllegalStateException(errMsg, exception);
return;
} }
List<AlarmAction> actions = new ArrayList<>(); List<AlarmAction> actions = new ArrayList<>();
@@ -91,7 +89,7 @@ public final class JupiterPerpsAlarmImpl {
asset.oracleAccount() asset.oracleAccount()
); );
assetDefinitions.forEach(definition -> System.out.printf( assetDefinitions.forEach(definition -> System.out.printf(
" %s %s USD, %s, severity=%d%n", " %s %s USD, %s, severity=%s%n",
definition.direction(), definition.direction(),
definition.target().toPlainString(), definition.target().toPlainString(),
definition.trigger(), definition.trigger(),
@@ -1,14 +1,17 @@
package com.r35157.jupiterperpsalarm.impl.ref; package com.r35157.jupiterperpsalarm.impl.ref;
import com.r35157.jupiterperpsalarm.AlarmSeverity;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.util.Objects; import java.util.Objects;
public record PriceAlarmDefinition( public record PriceAlarmDefinition(
int id,
JupiterPerpsAsset asset, JupiterPerpsAsset asset,
PriceDirection direction, PriceDirection direction,
BigDecimal target, BigDecimal target,
AlarmTrigger trigger, AlarmTrigger trigger,
int severity, AlarmSeverity severity,
String note String note
) { ) {
public PriceAlarmDefinition { public PriceAlarmDefinition {
@@ -18,11 +21,8 @@ public record PriceAlarmDefinition(
Objects.requireNonNull(trigger, "trigger"); Objects.requireNonNull(trigger, "trigger");
Objects.requireNonNull(note, "note"); Objects.requireNonNull(note, "note");
if (target.signum() <= 0) { if (target.signum() < 0) {
throw new IllegalArgumentException("Target price must be positive"); throw new IllegalArgumentException("Target price cannot be negative (was: " + target.signum() + ")!");
}
if (severity < 0) {
throw new IllegalArgumentException("Severity must be zero or positive");
} }
} }
} }
@@ -1,5 +1,7 @@
package com.r35157.jupiterperpsalarm.impl.ref; package com.r35157.jupiterperpsalarm.impl.ref;
import com.r35157.jupiterperpsalarm.AlarmSeverity;
import java.net.URI; import java.net.URI;
import java.net.URLEncoder; import java.net.URLEncoder;
import java.net.http.HttpClient; import java.net.http.HttpClient;
@@ -28,13 +30,13 @@ public final class PushoverAlarmAction implements AlarmAction {
price.slot() price.slot()
); );
// Severity and note are intentionally parsed and retained in the model, // TODO: Note is intentionally parsed and retained in the model, but are not used by Pushover yet.
// but are not used by Pushover yet. // https://git.r35157.com/r35157/com_r35157_nenjim-hubd-impl_ref/issues/7
String body = form("token", applicationToken) + "&" + String body = form("token", applicationToken) + "&" +
form("user", userKey) + "&" + form("user", userKey) + "&" +
form("title", title) + "&" + form("title", title) + "&" +
form("message", message) + "&" + form("message", message) + "&" +
"priority=2&retry=30&expire=10800&sound=persistent"; createPushoverSeverityParameters(alarm.severity());;
HttpRequest request = HttpRequest.newBuilder(PUSHOVER_URI) HttpRequest request = HttpRequest.newBuilder(PUSHOVER_URI)
.timeout(Duration.ofSeconds(15)) .timeout(Duration.ofSeconds(15))
@@ -56,7 +58,7 @@ public final class PushoverAlarmAction implements AlarmAction {
response.body() response.body()
); );
} else { } else {
System.out.println("Pushover emergency alarm sent."); System.out.println("Pushover alarm sent: " + alarm.severity());
} }
}); });
} }
@@ -66,6 +68,17 @@ public final class PushoverAlarmAction implements AlarmAction {
URLEncoder.encode(value, StandardCharsets.UTF_8); URLEncoder.encode(value, StandardCharsets.UTF_8);
} }
private static String createPushoverSeverityParameters(AlarmSeverity severity) {
return switch (severity) {
case EMERGENCY -> "priority=2&retry=30&expire=10800&sound=persistent";
case CRITICAL -> "priority=1&sound=spacealarm";
case WARN -> "priority=0&sound=siren";
case INFO -> "priority=0";
case SILENT -> "priority=-1";
case GHOST -> "priority=-2";
};
}
private final HttpClient httpClient = HttpClient.newHttpClient(); private final HttpClient httpClient = HttpClient.newHttpClient();
private final String applicationToken; private final String applicationToken;
private final String userKey; private final String userKey;
@@ -1,5 +1,7 @@
package com.r35157.jupiterperpsalarm.impl.ref; package com.r35157.jupiterperpsalarm.impl.ref;
import com.r35157.jupiterperpsalarm.AlarmSeverity;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.time.Instant; import java.time.Instant;
import java.util.ArrayList; import java.util.ArrayList;
@@ -17,14 +19,14 @@ public final class SelfTest {
private static void configurationParserTest() { private static void configurationParserTest() {
PriceAlarmDefinition alarm = AlarmConfigurationParser.parseLine( PriceAlarmDefinition alarm = AlarmConfigurationParser.parseLine(
"SOL ABOVE 75.7 ONETIME 2 \"ALARM: Risiko for likvidering!\"" "SOL ABOVE 75.7 ONETIME CRITICAL \"ALARM: Risiko for likvidering!\""
); );
require(alarm.asset() == JupiterPerpsAsset.SOL, "Asset parsing failed"); require(alarm.asset() == JupiterPerpsAsset.SOL, "Asset parsing failed");
require(alarm.direction() == PriceDirection.ABOVE, "Direction parsing failed"); require(alarm.direction() == PriceDirection.ABOVE, "Direction parsing failed");
require(alarm.target().compareTo(new BigDecimal("75.7")) == 0, "Target parsing failed"); require(alarm.target().compareTo(new BigDecimal("75.7")) == 0, "Target parsing failed");
require(alarm.trigger() == AlarmTrigger.ONETIME, "Trigger parsing failed"); require(alarm.trigger() == AlarmTrigger.ONETIME, "Trigger parsing failed");
require(alarm.severity() == 2, "Severity parsing failed"); require(alarm.severity() == AlarmSeverity.CRITICAL, "Severity parsing failed");
require(alarm.note().equals("ALARM: Risiko for likvidering!"), "Note parsing failed"); require(alarm.note().equals("ALARM: Risiko for likvidering!"), "Note parsing failed");
} }
@@ -85,11 +87,12 @@ public final class SelfTest {
AlarmTrigger trigger AlarmTrigger trigger
) { ) {
return new PriceAlarmDefinition( return new PriceAlarmDefinition(
123,
JupiterPerpsAsset.SOL, JupiterPerpsAsset.SOL,
direction, direction,
new BigDecimal(target), new BigDecimal(target),
trigger, trigger,
2, AlarmSeverity.CRITICAL,
"ignored for now" "ignored for now"
); );
} }
@@ -14,10 +14,13 @@ import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
public class Main { public class Main {
static void main(String[] args) throws Exception {
NenjimHub nenjimHub = new NenjimHubImpl();
/* try { // TODO: Consider if we really need a Main class or we just need to move the main method to NenjimHubImpl?
static void main(String[] args) throws Exception {
NenjimHubImpl nenjimHub = new NenjimHubImpl();
nenjimHub.awaitShutdown();
/* try {
log.info("Auto-starting 2 Nenjim application(s)..."); log.info("Auto-starting 2 Nenjim application(s)...");
log.info(" Starting AssetAZCore..."); log.info(" Starting AssetAZCore...");
//com.r35157.; //com.r35157.;
@@ -1,5 +1,6 @@
package com.r35157.nenjim.hubd.impl.ref; package com.r35157.nenjim.hubd.impl.ref;
import com.r35157.jupiterperpsalarm.impl.ref.JupiterPerpsAlarmImpl;
import com.r35157.nenjim.hubd.NenjimHub; import com.r35157.nenjim.hubd.NenjimHub;
import com.r35157.nenjim.hubd.journal.Journal; import com.r35157.nenjim.hubd.journal.Journal;
import crypto.r35157.nenjim.NenjimProcess; import crypto.r35157.nenjim.NenjimProcess;
@@ -8,6 +9,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import java.util.HashMap; import java.util.HashMap;
import java.util.concurrent.CountDownLatch;
public class NenjimHubImpl implements NenjimHub { public class NenjimHubImpl implements NenjimHub {
public NenjimHubImpl() throws Exception { public NenjimHubImpl() throws Exception {
@@ -38,19 +40,24 @@ public class NenjimHubImpl implements NenjimHub {
} }
private void startAutoRunProcesses() throws Exception { private void startAutoRunProcesses() throws Exception {
String[] processesToAutoStart = { startJupiterPerpsAlarm(); // TODO: Hardcoded/hacky way to auto-start but good enough for now.
"crypto.r35157.nenjim.NenjimHubSocketAdminAdapter", // TODO: Old but more correct way to auto start plugins - but it is currently broken.
"crypto.r35157.nenjim.NenjimHubRestAdminAdapter", /*
"crypto.r35157.nenjim.NenjimHubRPCAdminAdapter", String[] processesToAutoStart = {
"crypto.r35157.nenjim.SuwimoHub", //"com.r35157.jupiterperpsalarm.impl.ref.JupiterPerpsAlarmImpl"
"crypto.r35157.nenjim.SodaTaskManager", //"crypto.r35157.nenjim.NenjimHubSocketAdminAdapter",
"crypto.r35157.assetaz.hub.AssetAZHub" //"crypto.r35157.nenjim.NenjimHubRestAdminAdapter",
//"crypto.r35157.nenjim.NenjimHubRPCAdminAdapter",
//"crypto.r35157.nenjim.SuwimoHub",
//"crypto.r35157.nenjim.SodaTaskManager",
//"crypto.r35157.assetaz.hub.AssetAZHub"
}; };
for (String processInterfaceName : processesToAutoStart) { for (String processInterfaceName : processesToAutoStart) {
startProcess(processInterfaceName); startProcess(processInterfaceName);
} }
*/
} }
@Override @Override
@@ -90,7 +97,27 @@ public class NenjimHubImpl implements NenjimHub {
System.out.println("NenjimHub command: 'noop'"); System.out.println("NenjimHub command: 'noop'");
} }
public void awaitShutdown() throws InterruptedException {
shutdownLatch.await();
}
private void startJupiterPerpsAlarm() {
Thread thread = new Thread(() -> {
try {
JupiterPerpsAlarmImpl.main(new String[] {
"--config=conf/alarms.conf"
});
} catch (Throwable throwable) {
throwable.printStackTrace();
}
}, "Nenjim Plugin - JupiterPerpsAlarm");
thread.setDaemon(false);
thread.start();
}
private static final Logger log = LoggerFactory.getLogger(NenjimHubImpl.class); private static final Logger log = LoggerFactory.getLogger(NenjimHubImpl.class);
private final CountDownLatch shutdownLatch = new CountDownLatch(1);
private HashMap<Integer, NenjimProcess> processes; private HashMap<Integer, NenjimProcess> processes;
//private StructuredTaskScope.ShutdownOnFailure processesScope; //private StructuredTaskScope.ShutdownOnFailure processesScope;