fix:升级tms

This commit is contained in:
waner 2026-04-22 15:12:05 +08:00
parent 80e553fb23
commit 1fc32dbcf0
6 changed files with 120 additions and 10 deletions

View File

@ -28,7 +28,7 @@ public class UpgradeController {
@PostMapping("/upgrade-packages")
@Operation(summary = "上传离线升级包", description = "上传离线升级包,返回 fileId 供升级预检和任务创建使用。")
@ReplayProtected
// @ReplayProtected
public ApiResponse<FileUploadResponse> uploadPackage(
@Parameter(description = "待上传的离线升级包")
@RequestParam("file") MultipartFile file
@ -44,7 +44,7 @@ public class UpgradeController {
@PostMapping("/upgrades")
@Operation(summary = "创建离线升级任务", description = "根据 fileId 创建离线升级任务,状态初始为 PENDING_CONFIRM。")
@ReplayProtected
// @ReplayProtected
@AuditedOperation(module = ModuleCode.UPGRADE, action = ActionType.CREATE, summary = "创建离线升级任务")
public ApiResponse<UpgradeCreateTaskResponse> create(@Valid @RequestBody UpgradeCreateTaskRequest request) {
return ApiResponse.success(upgradeService.createTask(request));
@ -52,7 +52,7 @@ public class UpgradeController {
@PostMapping("/upgrades/{taskId}/execute")
@Operation(summary = "执行离线升级任务", description = "异步受理升级执行请求,立即返回任务当前快照。")
@ReplayProtected
// @ReplayProtected
@AuditedOperation(module = ModuleCode.UPGRADE, action = ActionType.EXECUTE, summary = "执行离线升级任务")
public ApiResponse<UpgradeTaskResponse> execute(
@Parameter(description = "升级任务号")
@ -63,7 +63,7 @@ public class UpgradeController {
@PostMapping("/upgrades/{taskId}/rollback")
@Operation(summary = "回滚离线升级任务", description = "异步受理升级回滚请求,立即返回任务当前快照。")
@ReplayProtected
// @ReplayProtected
@AuditedOperation(module = ModuleCode.UPGRADE, action = ActionType.ROLLBACK, summary = "回滚离线升级任务")
public ApiResponse<UpgradeTaskResponse> rollback(
@Parameter(description = "升级任务号")

View File

@ -110,6 +110,7 @@ public class UpgradeTaskRunner {
startedAt,
LocalDateTime.now()
);
scheduleDetachedRestartIfRequired(task, logPath, false);
} catch (Exception ex) {
try {
Files.createDirectories(logPath.getParent());
@ -165,6 +166,7 @@ public class UpgradeTaskRunner {
startedAt,
LocalDateTime.now()
);
scheduleDetachedRestartIfRequired(task, logPath, true);
} catch (Exception ex) {
try {
Files.createDirectories(logPath.getParent());
@ -207,6 +209,52 @@ public class UpgradeTaskRunner {
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) {
return switch (trim(componentCode)) {
case "TMS" -> "设备管理软件";
@ -237,4 +285,8 @@ public class UpgradeTaskRunner {
private String trim(String value) {
return value == null ? "" : value.trim();
}
private String shellQuote(String value) {
return "'" + trim(value).replace("'", "'\"'\"'") + "'";
}
}

View File

@ -154,7 +154,8 @@ public class UpgradeService {
return toResponse(loadTask(taskId));
}
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(() -> {
@ -183,7 +184,8 @@ public class UpgradeService {
if (activeExecutions.containsKey(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(() -> {
try {
upgradeTaskRunner.rollback(loadTask(taskId));
@ -292,6 +294,20 @@ public class UpgradeService {
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) {
try {
return LocalDate.parse(upgradeDate, UPGRADE_DATE_FORMAT);

View File

@ -29,6 +29,8 @@ public class InternalApiAuthInterceptor implements HandlerInterceptor {
public static final String ATTR_SESSION_TOKEN = "CURRENT_SESSION_TOKEN";
private static final String AUTHORIZATION_HEADER = "Authorization";
private static final String SESSION_HEADER = "X-Session-Token";
//会话空闲实效时间
private static final int IDLE_TIMEOUT_MINUTES = 10;
private final AuthSessionRepository authSessionRepository;

View File

@ -38,6 +38,7 @@ class UpgradeTaskRunnerTest {
fileRepository.save(fileRecord);
InMemoryUpgradeTaskRepository taskRepository = new InMemoryUpgradeTaskRepository();
InMemoryDeviceSoftwareVersionRepository versionRepository = new InMemoryDeviceSoftwareVersionRepository();
List<String> restartRequests = new ArrayList<>();
UpgradeTaskRunner runner = new UpgradeTaskRunner(
taskRepository,
@ -46,18 +47,25 @@ class UpgradeTaskRunnerTest {
stagingService(tempDir.resolve("staging"), tempDir.resolve("logs")),
properties(tempDir.resolve("staging"), tempDir.resolve("logs")),
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"));
Assertions.assertEquals("SUCCESS", taskRepository.status);
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));
Assertions.assertTrue(log.contains("precheck"));
Assertions.assertTrue(log.contains("execute"));
Assertions.assertTrue(log.contains("verify"));
Assertions.assertTrue(log.indexOf("precheck") < log.indexOf("execute"));
Assertions.assertTrue(log.indexOf("execute") < log.indexOf("verify"));
Assertions.assertTrue(log.contains("scheduling detached TMS restart"));
}
@Test
@ -140,6 +148,7 @@ class UpgradeTaskRunnerTest {
InMemoryFileRecordRepository fileRepository = new InMemoryFileRecordRepository();
fileRepository.save(fileRecord);
InMemoryUpgradeTaskRepository taskRepository = new InMemoryUpgradeTaskRepository();
List<String> restartRequests = new ArrayList<>();
UpgradeTaskRunner runner = new UpgradeTaskRunner(
taskRepository,
@ -148,13 +157,20 @@ class UpgradeTaskRunnerTest {
stagingService(tempDir.resolve("staging"), tempDir.resolve("logs")),
properties(tempDir.resolve("staging"), tempDir.resolve("logs")),
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(List.of("UPG-003-rollback.log:true"), restartRequests);
String log = Files.readString(Path.of(taskRepository.logPath));
Assertions.assertTrue(log.contains("rollback"));
Assertions.assertTrue(log.contains("scheduling detached TMS restart"));
}
private UpgradeProperties properties(Path stagingRoot, Path logDir) {

View File

@ -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.executor.UpgradeTaskRunner;
import com.cisd.tms.modules.upgrade.repository.UpgradeTaskRepository;
import java.nio.file.Path;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Comparator;
@ -131,6 +132,29 @@ class UpgradeServiceTest {
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
void shouldPageUpgradeTasks() {
InMemoryUpgradeTaskRepository repository = new InMemoryUpgradeTaskRepository();
@ -235,7 +259,7 @@ class UpgradeServiceTest {
private static UpgradeProperties properties() {
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;
}