chore: remove local service runners

This commit is contained in:
lingniu
2026-07-01 16:56:46 +08:00
parent ec4aed3ca9
commit 37844e4425
11 changed files with 83 additions and 935 deletions

View File

@@ -402,7 +402,7 @@ class MavenModuleProfileTest {
assertThat(runbook)
.contains("Session state requires Redis")
.contains("REDIS_HOST=127.0.0.1")
.contains("REDIS_HOST")
.contains("REDIS_DATABASE=50")
.doesNotContain("SESSION_STORE=memory")
.doesNotContain("without Redis");
@@ -447,7 +447,6 @@ class MavenModuleProfileTest {
repositoryRoot().resolve("README.md"),
repositoryRoot().resolve("pom.xml"),
repositoryRoot().resolve("deploy/portainer/docker-compose.yml"),
repositoryRoot().resolve("deploy/local/launchctl/README.md"),
repositoryRoot().resolve("docs/operations/gb32960-service-split-runbook.md"),
repositoryRoot().resolve("docs/operations/vehicle-ingest-tdengine-verification.md"),
repositoryRoot().resolve("docs/operations/jt808-daily-mileage-runbook.md"),

View File

@@ -5,7 +5,6 @@ import org.springframework.beans.factory.config.YamlPropertiesFactoryBean;
import org.springframework.core.io.FileSystemResource;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
@@ -123,141 +122,17 @@ class PortainerComposeResourceLimitsTest {
}
@Test
void localLaunchctlReadmeChecksLivenessAndReadinessForManagedApps() throws IOException {
String readme = Files.readString(repositoryRoot()
.resolve("deploy/local/launchctl/README.md"));
for (String httpPort : List.of("20100", "20400", "20500", "20200", "20300")) {
assertThat(readme)
.contains("curl -sS http://127.0.0.1:" + httpPort + "/actuator/health/liveness")
.contains("curl -sS http://127.0.0.1:" + httpPort + "/actuator/health/readiness");
}
}
@Test
void localLaunchctlReadmeDocumentsYutongMqttProductionTuning() throws IOException {
String readme = Files.readString(repositoryRoot()
.resolve("deploy/local/launchctl/README.md"));
assertThat(readme)
.contains("export YUTONG_MQTT_KEEP_ALIVE_SECONDS='20'")
.contains("export YUTONG_MQTT_CONNECTION_TIMEOUT_SECONDS='10'")
.contains("export YUTONG_MQTT_TLS_CA_PEM=''")
.contains("export YUTONG_MQTT_TLS_CLIENT_PEM=''")
.contains("export YUTONG_MQTT_TLS_CLIENT_KEY=''")
.contains("export YUTONG_MQTT_TLS_HOSTNAME_VERIFICATION_ENABLED='true'")
.contains("keepAlive、连接超时和 TLS PEM 参数");
}
@Test
void localLaunchctlTemplatesExposeHistoryConsumerDeadLetterTopic() throws IOException {
Path launchctl = repositoryRoot().resolve("deploy/local/launchctl");
String readme = Files.readString(launchctl.resolve("README.md"));
String renderer = Files.readString(launchctl.resolve("render.py"));
String historyTemplate = Files.readString(launchctl.resolve("com.lingniu.vehicle-history.plist.template"));
assertThat(readme)
.contains("export KAFKA_TOPIC_HISTORY_DLQ='vehicle.dlq.history.v1'");
assertThat(renderer)
.contains("\"__KAFKA_TOPIC_HISTORY_DLQ__\": env(\"KAFKA_TOPIC_HISTORY_DLQ\", \"vehicle.dlq.history.v1\")");
assertThat(historyTemplate)
.contains("<key>KAFKA_TOPIC_HISTORY_DLQ</key>\n <string>__KAFKA_TOPIC_HISTORY_DLQ__</string>");
}
@Test
void localLaunchctlTemplatesCoverActiveProductionApps() throws IOException {
Path launchctl = repositoryRoot().resolve("deploy/local/launchctl");
String readme = Files.readString(launchctl.resolve("README.md"));
assertThat(readme)
.contains("python3 deploy/local/launchctl/render.py")
.contains("gb32960 jt808 yutong-mqtt vehicle-history vehicle-analytics")
.contains("mkdir -p /tmp/lingniu-gb32960-live /tmp/lingniu-jt808-live /tmp/lingniu-yutong-mqtt-live /tmp/lingniu-vehicle-history-live /tmp/lingniu-vehicle-analytics-live")
.contains("launchctl bootstrap \"gui/$(id -u)\" \"$HOME/Library/LaunchAgents/com.lingniu.yutong-mqtt.plist\"")
.contains("launchctl bootstrap \"gui/$(id -u)\" \"$HOME/Library/LaunchAgents/com.lingniu.vehicle-analytics.plist\"")
.contains("launchctl bootout \"gui/$(id -u)\" \"$HOME/Library/LaunchAgents/com.lingniu.yutong-mqtt.plist\" || true")
.contains("launchctl bootout \"gui/$(id -u)\" \"$HOME/Library/LaunchAgents/com.lingniu.vehicle-analytics.plist\" || true")
.contains("launchctl list | rg 'com.lingniu.(gb32960|jt808|yutong-mqtt|vehicle-history|vehicle-analytics)'")
.contains("Yutong MQTT ingest health: `http://127.0.0.1:20500/actuator/health`")
.contains("Analytics metrics: `http://127.0.0.1:20300/swagger-ui/index.html`")
.doesNotContain("Yutong MQTT ingest: `http://127.0.0.1:20500/swagger-ui/index.html`")
.doesNotContain("sed \\")
.doesNotContain("三个服务");
assertLaunchctlTemplate(launchctl, "gb32960", "gb32960-ingest-app", "20100");
assertLaunchctlTemplate(launchctl, "jt808", "jt808-ingest-app", "20400");
assertLaunchctlTemplate(launchctl, "yutong-mqtt", "yutong-mqtt-app", "20500");
assertLaunchctlTemplate(launchctl, "vehicle-history", "vehicle-history-app", "20200");
assertLaunchctlTemplate(launchctl, "vehicle-analytics", "vehicle-analytics-app", "20300");
}
@Test
void localLaunchctlRendererPreservesSpecialCharactersSafely() throws Exception {
void defaultProductionSurfaceDoesNotShipLocalLaunchctlRunners() throws IOException {
Path root = repositoryRoot();
Path launchctl = root.resolve("deploy/local/launchctl");
Path home = Files.createTempDirectory("lingniu-launchctl-home");
String runbook = Files.readString(root.resolve("docs/operations/gb32960-service-split-runbook.md"));
ProcessBuilder processBuilder = new ProcessBuilder(
"python3",
launchctl.resolve("render.py").toString());
processBuilder.directory(root.toFile());
processBuilder.environment().put("HOME", home.toString());
processBuilder.environment().put("PROJECT_ROOT", "/tmp/lingniu project & live");
processBuilder.environment().put("JAVA_BIN", "/usr/bin/java");
processBuilder.environment().put("INGEST_ARCHIVE_PATH", "/tmp/archive&live__v1");
processBuilder.environment().put("TDENGINE_JDBC_URL", "jdbc:TAOS-WS://host:6041/vehicle_ts");
processBuilder.environment().put("TDENGINE_USERNAME", "root");
processBuilder.environment().put("TDENGINE_PASSWORD", "td&engine#pwd");
processBuilder.environment().put("VEHICLE_IDENTITY_MYSQL_JDBC_URL",
"jdbc:mysql://host:3306/vehicle_ingest?useSSL=false&serverTimezone=Asia/Shanghai");
processBuilder.environment().put("VEHICLE_IDENTITY_MYSQL_USERNAME", "lingniu_vehicle");
processBuilder.environment().put("VEHICLE_IDENTITY_MYSQL_PASSWORD", "id&pwd#1");
processBuilder.environment().put("MYSQL_JDBC_URL",
"jdbc:mysql://host:3306/vehicle_stat?useSSL=false&serverTimezone=Asia/Shanghai");
processBuilder.environment().put("MYSQL_USERNAME", "stat_user");
processBuilder.environment().put("MYSQL_PASSWORD", "stat&pwd#2");
processBuilder.environment().put("YUTONG_MQTT_ENABLED", "true");
processBuilder.environment().put("YUTONG_MQTT_URI", "ssl://mqtt.example.com:8883");
processBuilder.environment().put("YUTONG_MQTT_TOPIC", "#");
processBuilder.environment().put("YUTONG_MQTT_USERNAME", "mqtt_user");
processBuilder.environment().put("YUTONG_MQTT_PASSWORD", "mqtt&pwd#3");
processBuilder.environment().put("YUTONG_MQTT_KEEP_ALIVE_SECONDS", "45");
processBuilder.environment().put("YUTONG_MQTT_CONNECTION_TIMEOUT_SECONDS", "12");
processBuilder.environment().put("YUTONG_MQTT_TLS_CA_PEM", "ca&pem");
processBuilder.environment().put("YUTONG_MQTT_TLS_CLIENT_PEM", "client<pem>");
processBuilder.environment().put("YUTONG_MQTT_TLS_CLIENT_KEY", "key&value");
processBuilder.environment().put("YUTONG_MQTT_TLS_HOSTNAME_VERIFICATION_ENABLED", "false");
Process process = processBuilder.start();
String output = new String(process.getInputStream().readAllBytes(), StandardCharsets.UTF_8)
+ new String(process.getErrorStream().readAllBytes(), StandardCharsets.UTF_8);
assertThat(process.waitFor()).as(output).isZero();
Path launchAgents = home.resolve("Library/LaunchAgents");
String mqtt = Files.readString(launchAgents.resolve("com.lingniu.yutong-mqtt.plist"));
String analytics = Files.readString(launchAgents.resolve("com.lingniu.vehicle-analytics.plist"));
assertThat(mqtt)
.contains("<string>#</string>")
.contains("<string>mqtt&amp;pwd#3</string>")
.contains("<key>YUTONG_MQTT_KEEP_ALIVE_SECONDS</key>\n <string>45</string>")
.contains("<key>YUTONG_MQTT_CONNECTION_TIMEOUT_SECONDS</key>\n <string>12</string>")
.contains("<key>YUTONG_MQTT_TLS_CA_PEM</key>\n <string>ca&amp;pem</string>")
.contains("<key>YUTONG_MQTT_TLS_CLIENT_PEM</key>\n <string>client&lt;pem&gt;</string>")
.contains("<key>YUTONG_MQTT_TLS_CLIENT_KEY</key>\n <string>key&amp;value</string>")
.contains("<key>YUTONG_MQTT_TLS_HOSTNAME_VERIFICATION_ENABLED</key>\n <string>false</string>")
.contains("<string>/tmp/archive&amp;live__v1</string>")
.doesNotContain("__YUTONG_MQTT_TOPIC__")
.doesNotContain("__YUTONG_MQTT_PASSWORD__")
.doesNotContain("__YUTONG_MQTT_TLS");
assertThat(analytics)
.contains("<string>stat&amp;pwd#2</string>")
.doesNotContain("__MYSQL_PASSWORD__");
assertThat(Files.list(launchAgents)
.filter(path -> path.getFileName().toString().endsWith(".plist"))
.count())
.isEqualTo(5);
assertThat(root.resolve("deploy/local/launchctl")).doesNotExist();
assertThat(runbook)
.contains("Production services run on ECS through Portainer/Docker.")
.doesNotContain("Local launchctl Deployment")
.doesNotContain("deploy/local/launchctl")
.doesNotContain("launchctl bootstrap")
.doesNotContain("launchctl bootout");
}
@Test
@@ -440,39 +315,6 @@ class PortainerComposeResourceLimitsTest {
assertThat(compose).contains("\n yutong-mqtt-data:\n");
}
@Test
void localLaunchctlHistoryRuntimeDoesNotRequireSharedArchiveRoot() throws IOException {
Path launchctl = repositoryRoot().resolve("deploy/local/launchctl");
String historyTemplate = Files.readString(launchctl.resolve("com.lingniu.vehicle-history.plist.template"));
String readme = Files.readString(launchctl.resolve("README.md"));
assertThat(historyTemplate)
.doesNotContain("SINK_ARCHIVE_PATH")
.doesNotContain("__SHARED_ARCHIVE_PATH__");
assertThat(readme)
.doesNotContain("三个服务必须使用同一个 `SINK_ARCHIVE_PATH`")
.doesNotContain("history 服务按同一个 archive root 读取原始帧")
.doesNotContain("EventFileStore")
.doesNotContain("/api/event-history/records");
}
@Test
void localLaunchctlJt808UsesCurrentHttpPort() throws IOException {
Path launchctl = repositoryRoot().resolve("deploy/local/launchctl");
String jt808Template = Files.readString(launchctl.resolve("com.lingniu.jt808.plist.template"));
String readme = Files.readString(launchctl.resolve("README.md"));
assertThat(jt808Template)
.contains("<key>HTTP_PORT</key>\n <string>20400</string>")
.doesNotContain("20482");
assertThat(readme)
.contains("`jt808-ingest-app`: TCP `808`HTTP `20400`")
.contains("curl -sS http://127.0.0.1:20400/actuator/health")
.contains("JT808 ingest health: `http://127.0.0.1:20400/actuator/health`")
.doesNotContain("JT808 ingest: `http://127.0.0.1:20400/swagger-ui/index.html`")
.doesNotContain("20482");
}
@Test
void operationRunbooksDescribeTdengineRawJsonHotPath() throws IOException {
Path operations = repositoryRoot().resolve("docs/operations");
@@ -746,24 +588,6 @@ class PortainerComposeResourceLimitsTest {
.doesNotContain(" start_period: 60s\n");
}
private static void assertLaunchctlTemplate(Path launchctl,
String serviceName,
String appName,
String httpPort) throws IOException {
Path template = launchctl.resolve("com.lingniu." + serviceName + ".plist.template");
assertThat(template).exists();
assertThat(Files.readString(template))
.contains("<string>com.lingniu." + serviceName + "</string>")
.contains("__PROJECT_ROOT__/modules/apps/" + appName + "/target/" + appName + ".jar")
.contains("<key>HTTP_PORT</key>\n <string>" + httpPort + "</string>")
.contains("<key>NACOS_CONFIG_ENABLED</key>\n <string>false</string>")
.contains("<key>MANAGEMENT_HEALTH_REDIS_ENABLED</key>\n <string>false</string>")
.contains("/tmp/lingniu-" + serviceName + "-live/")
.doesNotContain("<key>VEHICLE_IDENTITY_STORE</key>")
.doesNotContain("<key>KAFKA_ENABLED</key>")
.doesNotContain("<key>TDENGINE_INITIALIZE_SCHEMA</key>");
}
private static String serviceBlock(String compose, String serviceName) {
String marker = "\n " + serviceName + ":\n";
int start = compose.indexOf(marker);