SHiNE-server/src/test/java/test/it/IT_DeployBackupCleanAndRunRemoteMain.java

191 lines
8.0 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package test.it;
import test.it.runner.IT_RunAllMain;
import java.io.BufferedReader;
import java.io.File;
import java.io.InputStreamReader;
import java.util.Enumeration;
import java.util.Objects;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
/**
* Деплой сервера с ПОЛНОЙ очисткой data, но только после:
* 1) явного интерактивного подтверждения;
* 2) бэкапа data в backup/data_YYYYMMDD-HHMMSS-SSS.
*
* После запуска сервиса выполняет полный IT-прогон.
*/
public class IT_DeployBackupCleanAndRunRemoteMain {
private static final String REMOTE_HOST = System.getProperty("it.remoteHost", "194.87.0.247");
private static final String REMOTE_USER = System.getProperty("it.remoteUser", "user");
private static final String REMOTE_DIR = System.getProperty("it.remoteDir", "/home/user/docker/shine-server");
private static final String REMOTE_JAR = REMOTE_DIR + "/shine-server.jar";
private static final String REMOTE_DATA = System.getProperty("it.remoteDataDir", REMOTE_DIR + "/data");
private static final String REMOTE_BACKUP_DIR = System.getProperty("it.remoteBackupDir", REMOTE_DIR + "/backup");
private static final String SERVICE_NAME = System.getProperty("it.service", "shine-server");
private static final String LOCAL_JAR = System.getProperty("it.localJar", "build/libs/shine-server.jar");
private static final String WS_URI_SERVER = System.getProperty("it.wsUri", "wss://shineup.me/ws");
public static void main(String[] args) {
requireDangerousConfirmation();
boolean serviceStopped = false;
boolean serviceStarted = false;
try {
sshStrict("sudo systemctl stop " + SERVICE_NAME + " || true");
serviceStopped = true;
validateLocalFatJarOrThrow(LOCAL_JAR);
sshStrict("mkdir -p " + q(REMOTE_DIR));
scpStrict(LOCAL_JAR, REMOTE_JAR + ".new");
verifyRemoteNewJarOrThrow(REMOTE_JAR + ".new");
sshStrict("mv -f " + q(REMOTE_JAR + ".new") + " " + q(REMOTE_JAR));
backupAndCleanRemoteDataOrThrow();
sshStrict("sudo systemctl start " + SERVICE_NAME);
serviceStarted = true;
waitRemotePort7070();
System.setProperty("it.wsUri", WS_URI_SERVER);
int failed = IT_RunAllMain.runAll();
System.exit(failed);
} catch (Throwable error) {
// На случай сбоя стараемся поднять сервис обратно.
if (serviceStopped && !serviceStarted) {
try {
sshStrict("sudo systemctl start " + SERVICE_NAME + " || true");
} catch (Throwable ignored) {
// ignore
}
}
throw error;
}
}
private static void requireDangerousConfirmation() {
String warning = ""
+ "\n============================================================\n"
+ "ВНИМАНИЕ: будет очищена серверная БД после бэкапа.\n"
+ "HOST: " + REMOTE_USER + "@" + REMOTE_HOST + "\n"
+ "DATA: " + REMOTE_DATA + "\n"
+ "BACKUP DIR: " + REMOTE_BACKUP_DIR + "\n"
+ "Для продолжения введите: DELETE\n"
+ "============================================================\n";
System.out.print(warning);
try {
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
String answer = reader.readLine();
if (!"DELETE".equals(String.valueOf(answer).trim())) {
throw new RuntimeException("Операция отменена: подтверждение не получено.");
}
} catch (RuntimeException e) {
throw e;
} catch (Exception e) {
throw new RuntimeException("Не удалось прочитать подтверждение из stdin", e);
}
}
private static void backupAndCleanRemoteDataOrThrow() {
String cmd = ""
+ "mkdir -p " + q(REMOTE_DATA) + " " + q(REMOTE_BACKUP_DIR) + " && "
+ "stamp=$(date +%Y%m%d-%H%M%S)-$(date +%3N) && "
+ "backup=" + q(REMOTE_BACKUP_DIR) + "/data_$stamp && "
+ "cp -a " + q(REMOTE_DATA) + " \"$backup\" && "
+ "echo backup_dir=$backup && "
+ "find " + q(REMOTE_DATA) + " -mindepth 1 -maxdepth 1 -exec rm -rf {} +";
sshStrict(cmd);
}
private static void waitRemotePort7070() {
for (int i = 0; i < 50; i++) {
int code = ssh("ss -ltnp | grep -q ':7070'");
if (code == 0) return;
sleepMs(200);
}
throw new RuntimeException("Remote port 7070 did not start in time on " + REMOTE_HOST);
}
private static void sshStrict(String remoteCmd) {
int code = ssh(remoteCmd);
if (code != 0) throw new RuntimeException("SSH command failed (" + code + "): " + remoteCmd);
}
private static int ssh(String remoteCmd) {
String cmd = "ssh " + REMOTE_USER + "@" + REMOTE_HOST + " " + q("bash -lc " + q(remoteCmd));
return sh(cmd);
}
private static void scpStrict(String local, String remote) {
Objects.requireNonNull(local);
Objects.requireNonNull(remote);
int code = sh("scp -p " + q(local) + " " + REMOTE_USER + "@" + REMOTE_HOST + ":" + q(remote));
if (code != 0) throw new RuntimeException("SCP failed (" + code + ")");
}
private static int sh(String cmd) {
try {
Process p = new ProcessBuilder("bash", "-lc", cmd).inheritIO().start();
return p.waitFor();
} catch (Exception e) {
throw new RuntimeException("Command error: " + cmd, e);
}
}
private static String q(String s) {
return "'" + s.replace("'", "'\"'\"'") + "'";
}
private static void sleepMs(long ms) {
try {
Thread.sleep(ms);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
private static void validateLocalFatJarOrThrow(String localJarPath) {
File jar = new File(localJarPath);
if (!jar.isFile()) {
throw new RuntimeException("Local jar not found: " + localJarPath);
}
long size = jar.length();
if (size < 10L * 1024L * 1024L) {
throw new RuntimeException("Local jar is too small for fat-jar: " + size + " bytes (" + localJarPath + ")");
}
try (JarFile jf = new JarFile(jar)) {
boolean hasJetty = false;
boolean hasBc = false;
Enumeration<JarEntry> entries = jf.entries();
while (entries.hasMoreElements()) {
String name = entries.nextElement().getName();
if (!hasJetty && "org/eclipse/jetty/server/Handler.class".equals(name)) hasJetty = true;
if (!hasBc && "org/bouncycastle/jce/provider/BouncyCastleProvider.class".equals(name)) hasBc = true;
if (hasJetty && hasBc) break;
}
if (!hasJetty || !hasBc) {
throw new RuntimeException(
"Local jar doesn't look like fat-jar (missing deps). hasJetty=" + hasJetty + ", hasBC=" + hasBc
);
}
} catch (Exception e) {
throw new RuntimeException("Failed to inspect local jar: " + localJarPath, e);
}
}
private static void verifyRemoteNewJarOrThrow(String remoteJarNewPath) {
String cmd = "test -f " + q(remoteJarNewPath) + " && " +
"sz=$(stat -c %s " + q(remoteJarNewPath) + ") && " +
"echo remote_new_size=$sz && test \"$sz\" -ge 10485760";
int code = ssh(cmd);
if (code != 0) {
throw new RuntimeException("Remote uploaded jar is missing or too small: " + remoteJarNewPath);
}
}
}