fix:升级tms
This commit is contained in:
parent
80e553fb23
commit
1fc32dbcf0
@ -28,7 +28,7 @@ public class UpgradeController {
|
|||||||
|
|
||||||
@PostMapping("/upgrade-packages")
|
@PostMapping("/upgrade-packages")
|
||||||
@Operation(summary = "上传离线升级包", description = "上传离线升级包,返回 fileId 供升级预检和任务创建使用。")
|
@Operation(summary = "上传离线升级包", description = "上传离线升级包,返回 fileId 供升级预检和任务创建使用。")
|
||||||
@ReplayProtected
|
// @ReplayProtected
|
||||||
public ApiResponse<FileUploadResponse> uploadPackage(
|
public ApiResponse<FileUploadResponse> uploadPackage(
|
||||||
@Parameter(description = "待上传的离线升级包")
|
@Parameter(description = "待上传的离线升级包")
|
||||||
@RequestParam("file") MultipartFile file
|
@RequestParam("file") MultipartFile file
|
||||||
@ -44,7 +44,7 @@ public class UpgradeController {
|
|||||||
|
|
||||||
@PostMapping("/upgrades")
|
@PostMapping("/upgrades")
|
||||||
@Operation(summary = "创建离线升级任务", description = "根据 fileId 创建离线升级任务,状态初始为 PENDING_CONFIRM。")
|
@Operation(summary = "创建离线升级任务", description = "根据 fileId 创建离线升级任务,状态初始为 PENDING_CONFIRM。")
|
||||||
@ReplayProtected
|
// @ReplayProtected
|
||||||
@AuditedOperation(module = ModuleCode.UPGRADE, action = ActionType.CREATE, summary = "创建离线升级任务")
|
@AuditedOperation(module = ModuleCode.UPGRADE, action = ActionType.CREATE, summary = "创建离线升级任务")
|
||||||
public ApiResponse<UpgradeCreateTaskResponse> create(@Valid @RequestBody UpgradeCreateTaskRequest request) {
|
public ApiResponse<UpgradeCreateTaskResponse> create(@Valid @RequestBody UpgradeCreateTaskRequest request) {
|
||||||
return ApiResponse.success(upgradeService.createTask(request));
|
return ApiResponse.success(upgradeService.createTask(request));
|
||||||
@ -52,7 +52,7 @@ public class UpgradeController {
|
|||||||
|
|
||||||
@PostMapping("/upgrades/{taskId}/execute")
|
@PostMapping("/upgrades/{taskId}/execute")
|
||||||
@Operation(summary = "执行离线升级任务", description = "异步受理升级执行请求,立即返回任务当前快照。")
|
@Operation(summary = "执行离线升级任务", description = "异步受理升级执行请求,立即返回任务当前快照。")
|
||||||
@ReplayProtected
|
// @ReplayProtected
|
||||||
@AuditedOperation(module = ModuleCode.UPGRADE, action = ActionType.EXECUTE, summary = "执行离线升级任务")
|
@AuditedOperation(module = ModuleCode.UPGRADE, action = ActionType.EXECUTE, summary = "执行离线升级任务")
|
||||||
public ApiResponse<UpgradeTaskResponse> execute(
|
public ApiResponse<UpgradeTaskResponse> execute(
|
||||||
@Parameter(description = "升级任务号")
|
@Parameter(description = "升级任务号")
|
||||||
@ -63,7 +63,7 @@ public class UpgradeController {
|
|||||||
|
|
||||||
@PostMapping("/upgrades/{taskId}/rollback")
|
@PostMapping("/upgrades/{taskId}/rollback")
|
||||||
@Operation(summary = "回滚离线升级任务", description = "异步受理升级回滚请求,立即返回任务当前快照。")
|
@Operation(summary = "回滚离线升级任务", description = "异步受理升级回滚请求,立即返回任务当前快照。")
|
||||||
@ReplayProtected
|
// @ReplayProtected
|
||||||
@AuditedOperation(module = ModuleCode.UPGRADE, action = ActionType.ROLLBACK, summary = "回滚离线升级任务")
|
@AuditedOperation(module = ModuleCode.UPGRADE, action = ActionType.ROLLBACK, summary = "回滚离线升级任务")
|
||||||
public ApiResponse<UpgradeTaskResponse> rollback(
|
public ApiResponse<UpgradeTaskResponse> rollback(
|
||||||
@Parameter(description = "升级任务号")
|
@Parameter(description = "升级任务号")
|
||||||
|
|||||||
@ -110,6 +110,7 @@ public class UpgradeTaskRunner {
|
|||||||
startedAt,
|
startedAt,
|
||||||
LocalDateTime.now()
|
LocalDateTime.now()
|
||||||
);
|
);
|
||||||
|
scheduleDetachedRestartIfRequired(task, logPath, false);
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
try {
|
try {
|
||||||
Files.createDirectories(logPath.getParent());
|
Files.createDirectories(logPath.getParent());
|
||||||
@ -165,6 +166,7 @@ public class UpgradeTaskRunner {
|
|||||||
startedAt,
|
startedAt,
|
||||||
LocalDateTime.now()
|
LocalDateTime.now()
|
||||||
);
|
);
|
||||||
|
scheduleDetachedRestartIfRequired(task, logPath, true);
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
try {
|
try {
|
||||||
Files.createDirectories(logPath.getParent());
|
Files.createDirectories(logPath.getParent());
|
||||||
@ -207,6 +209,52 @@ public class UpgradeTaskRunner {
|
|||||||
return process.waitFor();
|
return process.waitFor();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void scheduleDetachedRestartIfRequired(UpgradeTaskEntity task, Path logPath, boolean rollback) {
|
||||||
|
if (!"TMS".equals(componentCode(task.getTaskType()))) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
writeLogLine(logPath, rollback ? "rollback success, scheduling detached TMS restart"
|
||||||
|
: "upgrade success, scheduling detached TMS restart");
|
||||||
|
triggerDetachedTmsRestart(logPath, rollback);
|
||||||
|
} catch (IOException ex) {
|
||||||
|
throw new BizException(ErrorCode.BIZ_ERROR.getCode(), "failed to schedule detached tms restart");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TMS 自升级不能继续依赖包内脚本 stop/start 当前 Web 进程,
|
||||||
|
* 否则执行器还未落库终态就会把自己杀掉。这里在状态写回后再异步拉起重启。
|
||||||
|
*/
|
||||||
|
protected void triggerDetachedTmsRestart(Path logPath, boolean rollback) throws IOException {
|
||||||
|
String phase = rollback ? "rollback" : "upgrade";
|
||||||
|
String command = "nohup /home/tms/scripts/tms.sh restart >> "
|
||||||
|
+ shellQuote(logPath.toString())
|
||||||
|
+ " 2>&1 < /dev/null &";
|
||||||
|
Process shell = new ProcessBuilder("bash", "-lc", command)
|
||||||
|
.redirectErrorStream(true)
|
||||||
|
.redirectOutput(Redirect.appendTo(logPath.toFile()))
|
||||||
|
.start();
|
||||||
|
try {
|
||||||
|
int exitCode = shell.waitFor();
|
||||||
|
if (exitCode != 0) {
|
||||||
|
throw new IOException("detached TMS " + phase + " restart command exited with " + exitCode);
|
||||||
|
}
|
||||||
|
} catch (InterruptedException ex) {
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
throw new IOException("interrupted while scheduling detached TMS restart", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void writeLogLine(Path logPath, String message) throws IOException {
|
||||||
|
Files.writeString(
|
||||||
|
logPath,
|
||||||
|
"[" + LocalDateTime.now() + "] " + trim(message) + System.lineSeparator(),
|
||||||
|
java.nio.file.StandardOpenOption.CREATE,
|
||||||
|
java.nio.file.StandardOpenOption.APPEND
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
private String componentName(String componentCode) {
|
private String componentName(String componentCode) {
|
||||||
return switch (trim(componentCode)) {
|
return switch (trim(componentCode)) {
|
||||||
case "TMS" -> "设备管理软件";
|
case "TMS" -> "设备管理软件";
|
||||||
@ -237,4 +285,8 @@ public class UpgradeTaskRunner {
|
|||||||
private String trim(String value) {
|
private String trim(String value) {
|
||||||
return value == null ? "" : value.trim();
|
return value == null ? "" : value.trim();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String shellQuote(String value) {
|
||||||
|
return "'" + trim(value).replace("'", "'\"'\"'") + "'";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -154,7 +154,8 @@ public class UpgradeService {
|
|||||||
return toResponse(loadTask(taskId));
|
return toResponse(loadTask(taskId));
|
||||||
}
|
}
|
||||||
if ("SUCCESS".equals(task.getStatus()) || "FAILED".equals(task.getStatus()) || "PENDING_CONFIRM".equals(task.getStatus())) {
|
if ("SUCCESS".equals(task.getStatus()) || "FAILED".equals(task.getStatus()) || "PENDING_CONFIRM".equals(task.getStatus())) {
|
||||||
upgradeTaskRepository.updateStatus(taskId, "RUNNING", "", task.getDetailLogPath(), LocalDateTime.now(), null);
|
Path logPath = prepareLogFile(taskId);
|
||||||
|
upgradeTaskRepository.updateStatus(taskId, "RUNNING", "", logPath.toString(), LocalDateTime.now(), null);
|
||||||
}
|
}
|
||||||
|
|
||||||
CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
|
CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
|
||||||
@ -183,7 +184,8 @@ public class UpgradeService {
|
|||||||
if (activeExecutions.containsKey(taskId)) {
|
if (activeExecutions.containsKey(taskId)) {
|
||||||
return toResponse(loadTask(taskId));
|
return toResponse(loadTask(taskId));
|
||||||
}
|
}
|
||||||
upgradeTaskRepository.updateStatus(taskId, "ROLLING_BACK", "", task.getDetailLogPath(), LocalDateTime.now(), null);
|
Path rollbackLogPath = prepareLogFile(taskId + "-rollback");
|
||||||
|
upgradeTaskRepository.updateStatus(taskId, "ROLLING_BACK", "", rollbackLogPath.toString(), LocalDateTime.now(), null);
|
||||||
CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
|
CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
|
||||||
try {
|
try {
|
||||||
upgradeTaskRunner.rollback(loadTask(taskId));
|
upgradeTaskRunner.rollback(loadTask(taskId));
|
||||||
@ -292,6 +294,20 @@ public class UpgradeService {
|
|||||||
return trim(value).isEmpty() ? fallback : trim(value);
|
return trim(value).isEmpty() ? fallback : trim(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Path prepareLogFile(String logTaskId) {
|
||||||
|
Path path = Path.of(trim(upgradeProperties.getLogDir())).normalize().toAbsolutePath()
|
||||||
|
.resolve(trim(logTaskId) + ".log");
|
||||||
|
try {
|
||||||
|
Files.createDirectories(path.getParent());
|
||||||
|
if (!Files.exists(path)) {
|
||||||
|
Files.writeString(path, "");
|
||||||
|
}
|
||||||
|
return path;
|
||||||
|
} catch (IOException ex) {
|
||||||
|
throw new BizException(ErrorCode.BIZ_ERROR.getCode(), "failed to prepare upgrade log file");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private LocalDate parseUpgradeDate(String upgradeDate) {
|
private LocalDate parseUpgradeDate(String upgradeDate) {
|
||||||
try {
|
try {
|
||||||
return LocalDate.parse(upgradeDate, UPGRADE_DATE_FORMAT);
|
return LocalDate.parse(upgradeDate, UPGRADE_DATE_FORMAT);
|
||||||
|
|||||||
@ -29,6 +29,8 @@ public class InternalApiAuthInterceptor implements HandlerInterceptor {
|
|||||||
public static final String ATTR_SESSION_TOKEN = "CURRENT_SESSION_TOKEN";
|
public static final String ATTR_SESSION_TOKEN = "CURRENT_SESSION_TOKEN";
|
||||||
private static final String AUTHORIZATION_HEADER = "Authorization";
|
private static final String AUTHORIZATION_HEADER = "Authorization";
|
||||||
private static final String SESSION_HEADER = "X-Session-Token";
|
private static final String SESSION_HEADER = "X-Session-Token";
|
||||||
|
|
||||||
|
//会话空闲实效时间
|
||||||
private static final int IDLE_TIMEOUT_MINUTES = 10;
|
private static final int IDLE_TIMEOUT_MINUTES = 10;
|
||||||
|
|
||||||
private final AuthSessionRepository authSessionRepository;
|
private final AuthSessionRepository authSessionRepository;
|
||||||
|
|||||||
@ -38,6 +38,7 @@ class UpgradeTaskRunnerTest {
|
|||||||
fileRepository.save(fileRecord);
|
fileRepository.save(fileRecord);
|
||||||
InMemoryUpgradeTaskRepository taskRepository = new InMemoryUpgradeTaskRepository();
|
InMemoryUpgradeTaskRepository taskRepository = new InMemoryUpgradeTaskRepository();
|
||||||
InMemoryDeviceSoftwareVersionRepository versionRepository = new InMemoryDeviceSoftwareVersionRepository();
|
InMemoryDeviceSoftwareVersionRepository versionRepository = new InMemoryDeviceSoftwareVersionRepository();
|
||||||
|
List<String> restartRequests = new ArrayList<>();
|
||||||
|
|
||||||
UpgradeTaskRunner runner = new UpgradeTaskRunner(
|
UpgradeTaskRunner runner = new UpgradeTaskRunner(
|
||||||
taskRepository,
|
taskRepository,
|
||||||
@ -46,18 +47,25 @@ class UpgradeTaskRunnerTest {
|
|||||||
stagingService(tempDir.resolve("staging"), tempDir.resolve("logs")),
|
stagingService(tempDir.resolve("staging"), tempDir.resolve("logs")),
|
||||||
properties(tempDir.resolve("staging"), tempDir.resolve("logs")),
|
properties(tempDir.resolve("staging"), tempDir.resolve("logs")),
|
||||||
objectMapper
|
objectMapper
|
||||||
);
|
) {
|
||||||
|
@Override
|
||||||
|
protected void triggerDetachedTmsRestart(Path logPath, boolean rollback) {
|
||||||
|
restartRequests.add(logPath.getFileName() + ":" + rollback);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
runner.run(task("UPG-001", fileRecord.getFileId(), "TMS", "V1.0.1"));
|
runner.run(task("UPG-001", fileRecord.getFileId(), "TMS", "V1.0.1"));
|
||||||
|
|
||||||
Assertions.assertEquals("SUCCESS", taskRepository.status);
|
Assertions.assertEquals("SUCCESS", taskRepository.status);
|
||||||
Assertions.assertEquals("V1.0.1", versionRepository.store.get("TMS").getCurrentVersion());
|
Assertions.assertEquals("V1.0.1", versionRepository.store.get("TMS").getCurrentVersion());
|
||||||
|
Assertions.assertEquals(List.of("UPG-001.log:false"), restartRequests);
|
||||||
String log = Files.readString(Path.of(taskRepository.logPath));
|
String log = Files.readString(Path.of(taskRepository.logPath));
|
||||||
Assertions.assertTrue(log.contains("precheck"));
|
Assertions.assertTrue(log.contains("precheck"));
|
||||||
Assertions.assertTrue(log.contains("execute"));
|
Assertions.assertTrue(log.contains("execute"));
|
||||||
Assertions.assertTrue(log.contains("verify"));
|
Assertions.assertTrue(log.contains("verify"));
|
||||||
Assertions.assertTrue(log.indexOf("precheck") < log.indexOf("execute"));
|
Assertions.assertTrue(log.indexOf("precheck") < log.indexOf("execute"));
|
||||||
Assertions.assertTrue(log.indexOf("execute") < log.indexOf("verify"));
|
Assertions.assertTrue(log.indexOf("execute") < log.indexOf("verify"));
|
||||||
|
Assertions.assertTrue(log.contains("scheduling detached TMS restart"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -140,6 +148,7 @@ class UpgradeTaskRunnerTest {
|
|||||||
InMemoryFileRecordRepository fileRepository = new InMemoryFileRecordRepository();
|
InMemoryFileRecordRepository fileRepository = new InMemoryFileRecordRepository();
|
||||||
fileRepository.save(fileRecord);
|
fileRepository.save(fileRecord);
|
||||||
InMemoryUpgradeTaskRepository taskRepository = new InMemoryUpgradeTaskRepository();
|
InMemoryUpgradeTaskRepository taskRepository = new InMemoryUpgradeTaskRepository();
|
||||||
|
List<String> restartRequests = new ArrayList<>();
|
||||||
|
|
||||||
UpgradeTaskRunner runner = new UpgradeTaskRunner(
|
UpgradeTaskRunner runner = new UpgradeTaskRunner(
|
||||||
taskRepository,
|
taskRepository,
|
||||||
@ -148,13 +157,20 @@ class UpgradeTaskRunnerTest {
|
|||||||
stagingService(tempDir.resolve("staging"), tempDir.resolve("logs")),
|
stagingService(tempDir.resolve("staging"), tempDir.resolve("logs")),
|
||||||
properties(tempDir.resolve("staging"), tempDir.resolve("logs")),
|
properties(tempDir.resolve("staging"), tempDir.resolve("logs")),
|
||||||
objectMapper
|
objectMapper
|
||||||
);
|
) {
|
||||||
|
@Override
|
||||||
|
protected void triggerDetachedTmsRestart(Path logPath, boolean rollback) {
|
||||||
|
restartRequests.add(logPath.getFileName() + ":" + rollback);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
runner.rollback(task("UPG-003", fileRecord.getFileId(), "FIRMWARE", "V3.0.0"));
|
runner.rollback(task("UPG-003", fileRecord.getFileId(), "TMS", "V3.0.0"));
|
||||||
|
|
||||||
Assertions.assertEquals("ROLLBACK_SUCCESS", taskRepository.status);
|
Assertions.assertEquals("ROLLBACK_SUCCESS", taskRepository.status);
|
||||||
|
Assertions.assertEquals(List.of("UPG-003-rollback.log:true"), restartRequests);
|
||||||
String log = Files.readString(Path.of(taskRepository.logPath));
|
String log = Files.readString(Path.of(taskRepository.logPath));
|
||||||
Assertions.assertTrue(log.contains("rollback"));
|
Assertions.assertTrue(log.contains("rollback"));
|
||||||
|
Assertions.assertTrue(log.contains("scheduling detached TMS restart"));
|
||||||
}
|
}
|
||||||
|
|
||||||
private UpgradeProperties properties(Path stagingRoot, Path logDir) {
|
private UpgradeProperties properties(Path stagingRoot, Path logDir) {
|
||||||
|
|||||||
@ -13,6 +13,7 @@ import com.cisd.tms.modules.upgrade.dto.UpgradeTaskResponse;
|
|||||||
import com.cisd.tms.modules.upgrade.entity.UpgradeTaskEntity;
|
import com.cisd.tms.modules.upgrade.entity.UpgradeTaskEntity;
|
||||||
import com.cisd.tms.modules.upgrade.executor.UpgradeTaskRunner;
|
import com.cisd.tms.modules.upgrade.executor.UpgradeTaskRunner;
|
||||||
import com.cisd.tms.modules.upgrade.repository.UpgradeTaskRepository;
|
import com.cisd.tms.modules.upgrade.repository.UpgradeTaskRepository;
|
||||||
|
import java.nio.file.Path;
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
@ -131,6 +132,29 @@ class UpgradeServiceTest {
|
|||||||
Assertions.assertEquals("another upgrade task is running", exception.getMessage());
|
Assertions.assertEquals("another upgrade task is running", exception.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldPrepareLogPathWhenExecuteStarts() {
|
||||||
|
InMemoryUpgradeTaskRepository repository = new InMemoryUpgradeTaskRepository();
|
||||||
|
UpgradeTaskEntity task = entity("UPG-001", "PENDING_CONFIRM");
|
||||||
|
repository.save(task);
|
||||||
|
|
||||||
|
UpgradeService service = new UpgradeService(
|
||||||
|
repository,
|
||||||
|
Mockito.mock(FileRecordRepository.class),
|
||||||
|
Mockito.mock(UpgradePackageService.class),
|
||||||
|
Mockito.mock(UpgradeTaskRunner.class),
|
||||||
|
properties(),
|
||||||
|
Runnable::run,
|
||||||
|
new ConcurrentHashMap<>()
|
||||||
|
);
|
||||||
|
|
||||||
|
UpgradeTaskResponse response = service.executeTask("UPG-001");
|
||||||
|
|
||||||
|
Assertions.assertEquals("RUNNING", response.getStatus());
|
||||||
|
Assertions.assertTrue(response.getDetailLogPath().endsWith("/UPG-001.log"));
|
||||||
|
Assertions.assertTrue(task.getStartedAt() != null);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void shouldPageUpgradeTasks() {
|
void shouldPageUpgradeTasks() {
|
||||||
InMemoryUpgradeTaskRepository repository = new InMemoryUpgradeTaskRepository();
|
InMemoryUpgradeTaskRepository repository = new InMemoryUpgradeTaskRepository();
|
||||||
@ -235,7 +259,7 @@ class UpgradeServiceTest {
|
|||||||
|
|
||||||
private static UpgradeProperties properties() {
|
private static UpgradeProperties properties() {
|
||||||
UpgradeProperties properties = new UpgradeProperties();
|
UpgradeProperties properties = new UpgradeProperties();
|
||||||
properties.setLogDir("/home/tms/tmp/tms-upgrade-logs");
|
properties.setLogDir(Path.of(System.getProperty("java.io.tmpdir"), "upgrade-service-test-logs").toString());
|
||||||
return properties;
|
return properties;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user