From 1bdc7ea574ff377c0cb36068156462727616a5fd Mon Sep 17 00:00:00 2001
From: Aiden
Date: Sat, 16 Aug 2025 23:13:35 +0800
Subject: [PATCH 1/6] feat(biz-ops): implement BizOpsPodCoordinator for
managing biz identities
Add BizOpsPodCoordinator class to handle saving, removing, and accessing biz identities and their model versions. Update InstallBizHandler and UninstallBizHandler to utilize the new coordinator for managing biz identity access and lifecycle.
Also, introduce bizModelVersion in ArkBizMeta for better tracking of biz versions.
---
.../builtin/handler/InstallBizHandler.java | 27 +++---
.../builtin/handler/UninstallBizHandler.java | 15 +++-
.../coordinate/BizOpsPodCoordinator.java | 87 +++++++++++++++++++
.../core/command/meta/bizops/ArkBizMeta.java | 19 ++++
4 files changed, 133 insertions(+), 15 deletions(-)
create mode 100644 arklet-core/src/main/java/com/alipay/sofa/koupleless/arklet/core/command/coordinate/BizOpsPodCoordinator.java
diff --git a/arklet-core/src/main/java/com/alipay/sofa/koupleless/arklet/core/command/builtin/handler/InstallBizHandler.java b/arklet-core/src/main/java/com/alipay/sofa/koupleless/arklet/core/command/builtin/handler/InstallBizHandler.java
index 0419e3917..157ea41a5 100644
--- a/arklet-core/src/main/java/com/alipay/sofa/koupleless/arklet/core/command/builtin/handler/InstallBizHandler.java
+++ b/arklet-core/src/main/java/com/alipay/sofa/koupleless/arklet/core/command/builtin/handler/InstallBizHandler.java
@@ -19,11 +19,13 @@
import com.alipay.sofa.ark.api.ArkClient;
import com.alipay.sofa.ark.api.ClientResponse;
import com.alipay.sofa.ark.api.ResponseCode;
+import com.alipay.sofa.ark.common.util.BizIdentityUtils;
import com.alipay.sofa.ark.common.util.FileUtils;
import com.alipay.sofa.ark.common.util.StringUtils;
import com.alipay.sofa.ark.spi.model.Biz;
import com.alipay.sofa.ark.spi.service.biz.BizFactoryService;
import com.alipay.sofa.koupleless.arklet.core.command.builtin.BuiltinCommand;
+import com.alipay.sofa.koupleless.arklet.core.command.coordinate.BizOpsPodCoordinator;
import com.alipay.sofa.koupleless.arklet.core.command.meta.AbstractCommandHandler;
import com.alipay.sofa.koupleless.arklet.core.command.meta.Command;
import com.alipay.sofa.koupleless.arklet.core.command.meta.Output;
@@ -54,8 +56,8 @@
* @version 1.0.0
*/
public class InstallBizHandler extends
- AbstractCommandHandler
- implements ArkBizOps {
+ AbstractCommandHandler
+ implements ArkBizOps {
private static final ArkletLogger LOGGER = ArkletLoggerFactory.getDefaultLogger();
/** {@inheritDoc} */
@@ -65,10 +67,13 @@ public Output handle(Input input) {
long startSpace = metaSpaceMXBean.getUsage().getUsed();
try {
InstallBizClientResponse installBizClientResponse = convertClientResponse(
- getOperationService().install(convertInstallRequest(input)));
+ getOperationService().install(convertInstallRequest(input)));
installBizClientResponse
- .setElapsedSpace(metaSpaceMXBean.getUsage().getUsed() - startSpace);
+ .setElapsedSpace(metaSpaceMXBean.getUsage().getUsed() - startSpace);
if (ResponseCode.SUCCESS.equals(installBizClientResponse.getCode())) {
+ String bizIdentity = BizIdentityUtils.generateBizIdentity(input.getBizName(), input.getBizVersion());
+ String bizModelVersion = input.getBizModelVersion();
+ BizOpsPodCoordinator.save(bizIdentity, bizModelVersion);
return Output.ofSuccess(installBizClientResponse);
} else {
return Output.ofFailed(installBizClientResponse, "install biz not success!");
@@ -80,8 +85,8 @@ public Output handle(Input input) {
private InstallRequest convertInstallRequest(Input input) {
return InstallRequest.builder().bizName(input.getBizName())
- .bizVersion(input.getBizVersion()).bizUrl(input.getBizUrl()).args(input.getArgs())
- .envs(input.getEnvs()).installStrategy(input.getInstallStrategy()).build();
+ .bizVersion(input.getBizVersion()).bizUrl(input.getBizUrl()).args(input.getArgs())
+ .envs(input.getEnvs()).installStrategy(input.getInstallStrategy()).build();
}
private InstallBizClientResponse convertClientResponse(ClientResponse res) {
@@ -106,12 +111,12 @@ public void validate(Input input) throws CommandValidationException {
public static void validateInput(Input input) throws CommandValidationException {
isTrue(!input.isAsync() || !StringUtils.isEmpty(input.getRequestId()),
- "requestId should not be blank when async is true");
+ "requestId should not be blank when async is true");
notBlank(input.getBizUrl(), "bizUrl should not be blank");
if (StringUtils.isEmpty(input.getBizName()) || StringUtils.isEmpty(input.getBizVersion())) {
LOGGER.warn(
- "biz name and version should not be empty, or it will reduce the performance.");
+ "biz name and version should not be empty, or it will reduce the performance.");
}
if (StringUtils.isEmpty(input.getBizName()) && StringUtils.isEmpty(input.getBizVersion())) {
@@ -120,16 +125,16 @@ public static void validateInput(Input input) throws CommandValidationException
refreshBizInfoFromJar(input);
} catch (IOException e) {
throw new CommandValidationException(
- String.format("refresh biz info from jar failed: %s", e.getMessage()));
+ String.format("refresh biz info from jar failed: %s", e.getMessage()));
}
} else if (!StringUtils.isEmpty(input.getBizName())
- && !StringUtils.isEmpty(input.getBizVersion())) {
+ && !StringUtils.isEmpty(input.getBizVersion())) {
// if bizName and bizVersion is not blank, it means that we should install the biz with the given bizName and bizVersion.
// do nothing.
} else {
// if bizName or bizVersion is blank, it is invalid, throw exception.
throw new CommandValidationException(
- "bizName and bizVersion should be both blank or both not blank.");
+ "bizName and bizVersion should be both blank or both not blank.");
}
}
diff --git a/arklet-core/src/main/java/com/alipay/sofa/koupleless/arklet/core/command/builtin/handler/UninstallBizHandler.java b/arklet-core/src/main/java/com/alipay/sofa/koupleless/arklet/core/command/builtin/handler/UninstallBizHandler.java
index ce18b1732..db40a5a6d 100644
--- a/arklet-core/src/main/java/com/alipay/sofa/koupleless/arklet/core/command/builtin/handler/UninstallBizHandler.java
+++ b/arklet-core/src/main/java/com/alipay/sofa/koupleless/arklet/core/command/builtin/handler/UninstallBizHandler.java
@@ -18,9 +18,11 @@
import com.alipay.sofa.ark.api.ClientResponse;
import com.alipay.sofa.ark.api.ResponseCode;
+import com.alipay.sofa.ark.common.util.BizIdentityUtils;
import com.alipay.sofa.ark.common.util.StringUtils;
import com.alipay.sofa.koupleless.arklet.core.command.builtin.BuiltinCommand;
import com.alipay.sofa.koupleless.arklet.core.command.builtin.handler.UninstallBizHandler.Input;
+import com.alipay.sofa.koupleless.arklet.core.command.coordinate.BizOpsPodCoordinator;
import com.alipay.sofa.koupleless.arklet.core.command.meta.AbstractCommandHandler;
import com.alipay.sofa.koupleless.arklet.core.command.meta.Command;
import com.alipay.sofa.koupleless.arklet.core.command.meta.Output;
@@ -37,15 +39,20 @@
* @version 1.0.0
*/
public class UninstallBizHandler extends AbstractCommandHandler
- implements ArkBizOps {
+ implements ArkBizOps {
/** {@inheritDoc} */
@Override
public Output handle(Input input) {
try {
- ClientResponse res = getOperationService().uninstall(input.getBizName(),
- input.getBizVersion());
+ String bizIdentity = BizIdentityUtils.generateBizIdentity(input.getBizName(), input.getBizVersion());
+ String bizModelVersion = input.getBizModelVersion();
+ if (!BizOpsPodCoordinator.canAccess(bizIdentity, bizModelVersion)) {
+ return Output.ofFailed("can not access biz because the command is expired");
+ }
+ ClientResponse res = getOperationService().uninstall(input.getBizName(), input.getBizVersion());
if (ResponseCode.SUCCESS.equals(res.getCode())) {
+ BizOpsPodCoordinator.remove(bizIdentity, bizModelVersion);
return Output.ofSuccess(res);
} else {
return Output.ofFailed(res, "uninstall biz not success!");
@@ -67,7 +74,7 @@ public void validate(Input input) throws CommandValidationException {
notBlank(input.getBizName(), "bizName should not be blank");
notBlank(input.getBizVersion(), "bizVersion should not be blank");
isTrue(!input.isAsync() || !StringUtils.isEmpty(input.getRequestId()),
- "requestId should not be blank when async is true");
+ "requestId should not be blank when async is true");
}
public static class Input extends ArkBizMeta {
diff --git a/arklet-core/src/main/java/com/alipay/sofa/koupleless/arklet/core/command/coordinate/BizOpsPodCoordinator.java b/arklet-core/src/main/java/com/alipay/sofa/koupleless/arklet/core/command/coordinate/BizOpsPodCoordinator.java
new file mode 100644
index 000000000..2735c8a40
--- /dev/null
+++ b/arklet-core/src/main/java/com/alipay/sofa/koupleless/arklet/core/command/coordinate/BizOpsPodCoordinator.java
@@ -0,0 +1,87 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alipay.sofa.koupleless.arklet.core.command.coordinate;
+
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+import com.alipay.sofa.ark.common.util.StringUtils;
+
+/**
+ *
* 判断是否可以访问指定的业务模块,基于业务模块版本的协调机制
+ *
* @param bizIdentity 业务模块标识 (bizName:bizVersion)
* @param bizModelVersion 业务模块模型版本,用于命令协调和防止过期命令执行
* @return 是否允许访问该业务模块
*/
public static boolean canAccess(String bizIdentity, String bizModelVersion) {
// 判断逻辑说明:
- // Case 1: bizModelVersion 为空 - 兼容性处理,允许访问(兼容旧版本 module-controller,arktcl, pod-not-exist 和 pod 紧急删除场景)
- // Case 2: bizIdentityLockMap 中没有该 bizIdentity 的记录,允许访问(安装时不带 BizModelVersion,卸载时带上 BizModelVersion)
+ // Case 1: bizModelVersion 为空 - 兼容性处理,允许访问(兼容旧版本 module-controller,arktcl,
+ // pod-not-exist 和 pod 紧急删除场景)
+ // Case 2: bizIdentityLockMap 中没有该 bizIdentity 的记录,允许访问(安装时不带
+ // BizModelVersion,卸载时带上 BizModelVersion)
// Case 3: bizIdentityLockMap 中的版本与当前请求的版本匹配 - 版本一致,确认卸载的是该 Biz,允许访问
// 只有当 bizModelVersion 不为空且存在 bizModelVersion 且不匹配时,才拒绝访问(防止旧的卸载命令执行)
return StringUtils.isEmpty(bizModelVersion)
From 55f2fb7bdaba9a78be893e918918c442edfa3823 Mon Sep 17 00:00:00 2001
From: Aiden
Date: Fri, 19 Sep 2025 15:13:45 +0800
Subject: [PATCH 6/6] style: format code for better readability in
InstallBizHandler and UninstallBizHandler
---
.../builtin/handler/InstallBizHandler.java | 25 ++++++++++---------
.../builtin/handler/UninstallBizHandler.java | 10 +++++---
.../coordinate/BizOpsPodCoordinator.java | 4 +--
3 files changed, 21 insertions(+), 18 deletions(-)
diff --git a/arklet-core/src/main/java/com/alipay/sofa/koupleless/arklet/core/command/builtin/handler/InstallBizHandler.java b/arklet-core/src/main/java/com/alipay/sofa/koupleless/arklet/core/command/builtin/handler/InstallBizHandler.java
index 157ea41a5..28ac25bf8 100644
--- a/arklet-core/src/main/java/com/alipay/sofa/koupleless/arklet/core/command/builtin/handler/InstallBizHandler.java
+++ b/arklet-core/src/main/java/com/alipay/sofa/koupleless/arklet/core/command/builtin/handler/InstallBizHandler.java
@@ -56,8 +56,8 @@
* @version 1.0.0
*/
public class InstallBizHandler extends
- AbstractCommandHandler
- implements ArkBizOps {
+ AbstractCommandHandler
+ implements ArkBizOps {
private static final ArkletLogger LOGGER = ArkletLoggerFactory.getDefaultLogger();
/** {@inheritDoc} */
@@ -67,11 +67,12 @@ public Output handle(Input input) {
long startSpace = metaSpaceMXBean.getUsage().getUsed();
try {
InstallBizClientResponse installBizClientResponse = convertClientResponse(
- getOperationService().install(convertInstallRequest(input)));
+ getOperationService().install(convertInstallRequest(input)));
installBizClientResponse
- .setElapsedSpace(metaSpaceMXBean.getUsage().getUsed() - startSpace);
+ .setElapsedSpace(metaSpaceMXBean.getUsage().getUsed() - startSpace);
if (ResponseCode.SUCCESS.equals(installBizClientResponse.getCode())) {
- String bizIdentity = BizIdentityUtils.generateBizIdentity(input.getBizName(), input.getBizVersion());
+ String bizIdentity = BizIdentityUtils.generateBizIdentity(input.getBizName(),
+ input.getBizVersion());
String bizModelVersion = input.getBizModelVersion();
BizOpsPodCoordinator.save(bizIdentity, bizModelVersion);
return Output.ofSuccess(installBizClientResponse);
@@ -85,8 +86,8 @@ public Output handle(Input input) {
private InstallRequest convertInstallRequest(Input input) {
return InstallRequest.builder().bizName(input.getBizName())
- .bizVersion(input.getBizVersion()).bizUrl(input.getBizUrl()).args(input.getArgs())
- .envs(input.getEnvs()).installStrategy(input.getInstallStrategy()).build();
+ .bizVersion(input.getBizVersion()).bizUrl(input.getBizUrl()).args(input.getArgs())
+ .envs(input.getEnvs()).installStrategy(input.getInstallStrategy()).build();
}
private InstallBizClientResponse convertClientResponse(ClientResponse res) {
@@ -111,12 +112,12 @@ public void validate(Input input) throws CommandValidationException {
public static void validateInput(Input input) throws CommandValidationException {
isTrue(!input.isAsync() || !StringUtils.isEmpty(input.getRequestId()),
- "requestId should not be blank when async is true");
+ "requestId should not be blank when async is true");
notBlank(input.getBizUrl(), "bizUrl should not be blank");
if (StringUtils.isEmpty(input.getBizName()) || StringUtils.isEmpty(input.getBizVersion())) {
LOGGER.warn(
- "biz name and version should not be empty, or it will reduce the performance.");
+ "biz name and version should not be empty, or it will reduce the performance.");
}
if (StringUtils.isEmpty(input.getBizName()) && StringUtils.isEmpty(input.getBizVersion())) {
@@ -125,16 +126,16 @@ public static void validateInput(Input input) throws CommandValidationException
refreshBizInfoFromJar(input);
} catch (IOException e) {
throw new CommandValidationException(
- String.format("refresh biz info from jar failed: %s", e.getMessage()));
+ String.format("refresh biz info from jar failed: %s", e.getMessage()));
}
} else if (!StringUtils.isEmpty(input.getBizName())
- && !StringUtils.isEmpty(input.getBizVersion())) {
+ && !StringUtils.isEmpty(input.getBizVersion())) {
// if bizName and bizVersion is not blank, it means that we should install the biz with the given bizName and bizVersion.
// do nothing.
} else {
// if bizName or bizVersion is blank, it is invalid, throw exception.
throw new CommandValidationException(
- "bizName and bizVersion should be both blank or both not blank.");
+ "bizName and bizVersion should be both blank or both not blank.");
}
}
diff --git a/arklet-core/src/main/java/com/alipay/sofa/koupleless/arklet/core/command/builtin/handler/UninstallBizHandler.java b/arklet-core/src/main/java/com/alipay/sofa/koupleless/arklet/core/command/builtin/handler/UninstallBizHandler.java
index 0a4b550c8..b4d2cfe50 100644
--- a/arklet-core/src/main/java/com/alipay/sofa/koupleless/arklet/core/command/builtin/handler/UninstallBizHandler.java
+++ b/arklet-core/src/main/java/com/alipay/sofa/koupleless/arklet/core/command/builtin/handler/UninstallBizHandler.java
@@ -41,7 +41,7 @@
* @version 1.0.0
*/
public class UninstallBizHandler extends AbstractCommandHandler
- implements ArkBizOps {
+ implements ArkBizOps {
private static final ArkletLogger LOGGER = ArkletLoggerFactory.getDefaultLogger();
@@ -49,7 +49,8 @@ public class UninstallBizHandler extends AbstractCommandHandler handle(Input input) {
try {
- String bizIdentity = BizIdentityUtils.generateBizIdentity(input.getBizName(), input.getBizVersion());
+ String bizIdentity = BizIdentityUtils.generateBizIdentity(input.getBizName(),
+ input.getBizVersion());
String bizModelVersion = input.getBizModelVersion();
if (!BizOpsPodCoordinator.canAccess(bizIdentity, bizModelVersion)) {
LOGGER.error(
@@ -57,7 +58,8 @@ public Output handle(Input input) {
bizIdentity, bizModelVersion);
return Output.ofFailed("can not access biz because the command is expired");
}
- ClientResponse res = getOperationService().uninstall(input.getBizName(), input.getBizVersion());
+ ClientResponse res = getOperationService().uninstall(input.getBizName(),
+ input.getBizVersion());
if (ResponseCode.SUCCESS.equals(res.getCode())) {
BizOpsPodCoordinator.remove(bizIdentity, bizModelVersion);
return Output.ofSuccess(res);
@@ -81,7 +83,7 @@ public void validate(Input input) throws CommandValidationException {
notBlank(input.getBizName(), "bizName should not be blank");
notBlank(input.getBizVersion(), "bizVersion should not be blank");
isTrue(!input.isAsync() || !StringUtils.isEmpty(input.getRequestId()),
- "requestId should not be blank when async is true");
+ "requestId should not be blank when async is true");
}
public static class Input extends ArkBizMeta {
diff --git a/arklet-core/src/main/java/com/alipay/sofa/koupleless/arklet/core/command/coordinate/BizOpsPodCoordinator.java b/arklet-core/src/main/java/com/alipay/sofa/koupleless/arklet/core/command/coordinate/BizOpsPodCoordinator.java
index 511e4b1f0..aedd25539 100644
--- a/arklet-core/src/main/java/com/alipay/sofa/koupleless/arklet/core/command/coordinate/BizOpsPodCoordinator.java
+++ b/arklet-core/src/main/java/com/alipay/sofa/koupleless/arklet/core/command/coordinate/BizOpsPodCoordinator.java
@@ -96,7 +96,7 @@ public static boolean canAccess(String bizIdentity, String bizModelVersion) {
// Case 3: bizIdentityLockMap 中的版本与当前请求的版本匹配 - 版本一致,确认卸载的是该 Biz,允许访问
// 只有当 bizModelVersion 不为空且存在 bizModelVersion 且不匹配时,才拒绝访问(防止旧的卸载命令执行)
return StringUtils.isEmpty(bizModelVersion)
- || StringUtils.isEmpty(bizIdentityLockMap.get(bizIdentity))
- || bizIdentityLockMap.get(bizIdentity).equals(bizModelVersion);
+ || StringUtils.isEmpty(bizIdentityLockMap.get(bizIdentity))
+ || bizIdentityLockMap.get(bizIdentity).equals(bizModelVersion);
}
}