diff --git a/arklet/README.md b/arklet/README.md index cc5a431aa..339f2c850 100644 --- a/arklet/README.md +++ b/arklet/README.md @@ -1,8 +1,34 @@ -# API +# Overview + +Arklet provides an operational interface for delivery of SofaArk bases and modules. With Arklet, the release and operation of Ark Biz can be easily and flexibly operated. + +Arklet is internally constructed by **ArkletComponent** + +![image](https://github.com/sofastack/sofa-serverless/assets/11410549/a2740422-569e-4dd3-9c9a-1503996bd2f1) +- ApiClient: The core components responsible for interacting with the outside world +- CommandService: Arklet exposes capability instruction definition and extension +- OperationService: Ark Biz interacts with SofaArk to add, delete, modify, and encapsulate basic capabilities +- HealthService: Based on health and stability, base, Biz, system and other indicators are calculated + +The collaboration between them is shown in the figure +![overview](https://user-images.githubusercontent.com/11410549/266193839-7865e417-6909-4e89-bd48-c926162eaf83.jpg) + + +Of course, you can also extend Arklet's component capabilities by implementing the **ArkletComponent** interface + +# Command Extension +The Arklet exposes the instruction API externally and handles the instruction internally through a CommandHandler mapped from each API. +> CommandHandler related extensions belong to the unified management of the CommandService component + +You can customize extension commands by inheriting **AbstractCommandHandler** + +## Build-in Command API All of the following instruction apis access the arklet using the POST(application/json) request format The http protocol is enabled and the default port is 1238 +> You can set `sofa.serverless.arklet.http.port` JVM startup parameters override the default port + ## Query the supported commands - URL: 127.0.0.1:1238/help @@ -46,23 +72,46 @@ The http protocol is enabled and the default port is 1238 { "bizName": "test", "bizVersion": "1.0.0", - "arkBizFilePath": "/Users/jaimezhang/workspace/github/sofa-ark-dynamic-guides/dynamic-provider/target/dynamic-provider-1.0.0-ark-biz.jar" + // local path should start with file://, alse support remote url which can be downloaded + "bizUrl": "file:///Users/jaimezhang/workspace/github/sofa-ark-dynamic-guides/dynamic-provider/target/dynamic-provider-1.0.0-ark-biz.jar" } ``` -- output sample: + +- output sample(success): ```json { "code":"SUCCESS", - "data":[ - { - "bizName":"dynamic-provider", - "bizVersion":"1.0.0", - ... - } - ] + "data":{ + "bizInfos":[ + { + "bizName":"dynamic-provider", + "bizState":"ACTIVATED", + "bizVersion":"1.0.0", + "declaredMode":true, + "identity":"dynamic-provider:1.0.0", + "mainClass":"io.sofastack.dynamic.provider.ProviderApplication", + "priority":100, + "webContextPath":"provider" + } + ], + "code":"SUCCESS", + "message":"Install Biz: dynamic-provider:1.0.0 success, cost: 1092 ms, started at: 16:07:47,769" + } } ``` +- output sample(failed): +```json +{ + "code":"FAILED", + "data":{ + "code":"REPEAT_BIZ", + "message":"Biz: dynamic-provider:1.0.0 has been installed or registered." + } +} +``` + + ## Uninstall a biz - URL: 127.0.0.1:1238/uninstallBiz - input sample: @@ -72,13 +121,24 @@ The http protocol is enabled and the default port is 1238 "bizVersion":"1.0.0" } ``` -- output sample: +- output sample(success): ```json { "code":"SUCCESS" } ``` +- output sample(failed): +```json +{ + "code":"FAILED", + "data":{ + "code":"NOT_FOUND_BIZ", + "message":"Uninstall biz: test:1.0.0 not found." + } +} +``` + ## Switch a biz - URL: 127.0.0.1:1238/switchBiz - input sample: @@ -122,4 +182,247 @@ The http protocol is enabled and the default port is 1238 } ] } -``` \ No newline at end of file +``` + +## Query Health +- URL: 127.0.0.1:1238/health + +### Query All Health Info +- input sample: +```json +{} +``` +- output sample: +```json +{ + "code": "SUCCESS", + "data": { + "healthData": { + "jvm": { + "max non heap memory(M)": -9.5367431640625E-7, + "java version": "1.8.0_331", + "max memory(M)": 885.5, + "max heap memory(M)": 885.5, + "used heap memory(M)": 137.14127349853516, + "used non heap memory(M)": 62.54662322998047, + "loaded class count": 10063, + "init non heap memory(M)": 2.4375, + "total memory(M)": 174.5, + "free memory(M)": 37.358726501464844, + "unload class count": 0, + "total class count": 10063, + "committed heap memory(M)": 174.5, + "java home": "****\\jre", + "init heap memory(M)": 64.0, + "committed non heap memory(M)": 66.203125, + "run time(s)": 34.432 + }, + "cpu": { + "count": 4, + "total used (%)": 131749.0, + "type": "****", + "user used (%)": 9.926451054656962, + "free (%)": 81.46475495070172, + "system used (%)": 6.249762806548817 + }, + "masterBizInfo": { + "webContextPath": "/", + "bizName": "bookstore-manager", + "bizState": "ACTIVATED", + "bizVersion": "1.0.0" + }, + "pluginListInfo": [ + { + "artifactId": "web-ark-plugin", + "groupId": "com.alipay.sofa", + "pluginActivator": "com.alipay.sofa.ark.web.embed.WebPluginActivator", + "pluginName": "web-ark-plugin", + "pluginUrl": "file:/****/2.2.3-SNAPSHOT/web-ark-plugin-2.2.3-20230901.090402-2.jar!/", + "pluginVersion": "2.2.3-SNAPSHOT" + }, + { + "artifactId": "runtime-sofa-boot-plugin", + "groupId": "com.alipay.sofa", + "pluginActivator": "com.alipay.sofa.runtime.ark.plugin.SofaRuntimeActivator", + "pluginName": "runtime-sofa-boot-plugin", + "pluginUrl": "file:/****/runtime-sofa-boot-plugin-3.11.0.jar!/", + "pluginVersion": "3.11.0" + } + ], + "masterBizHealth": { + "readinessState": "ACCEPTING_TRAFFIC" + }, + "bizListInfo": [ + { + "bizName": "bookstore-manager", + "bizState": "ACTIVATED", + "bizVersion": "1.0.0", + "webContextPath": "/" + } + ] + } + } +} +``` + +### Query System Health Info +- input sample: + +```json +{ + "type": "system", + // [OPTIONAL] if metrics is null -> query all system health info + "metrics": ["cpu", "jvm"] +} +``` +- output sample: +```json +{ + "code": "SUCCESS", + "data": { + "healthData": { + "jvm": {...}, + "cpu": {...}, +// "masterBizHealth": {...} + } + } +} +``` + +### Query Biz Health Info +- input sample: + +```json +{ + "type": "biz", + // [OPTIONAL] if moduleName is null and moduleVersion is null -> query all biz + "moduleName": "bookstore-manager", + // [OPTIONAL] if moduleVersion is null -> query all biz named moduleName + "moduleVersion": "1.0.0" +} +``` +- output sample: +```json +{ + "code": "SUCCESS", + "data": { + "healthData": { + "bizInfo": { + "bizName": "bookstore-manager", + "bizState": "ACTIVATED", + "bizVersion": "1.0.0", + "webContextPath": "/" + } +// "bizListInfo": [ +// { +// "bizName": "bookstore-manager", +// "bizState": "ACTIVATED", +// "bizVersion": "1.0.0", +// "webContextPath": "/" +// } +// ] + } + } +} +``` + +### Query Plugin Health Info +- input sample: + +```json +{ + "type": "plugin", + // [OPTIONAL] if moduleName is null -> query all biz + "moduleName": "web-ark-plugin" +} +``` +- output sample: +```json +{ + "code": "SUCCESS", + "data": { + "healthData": { + "pluginListInfo": [ + { + "artifactId": "web-ark-plugin", + "groupId": "com.alipay.sofa", + "pluginActivator": "com.alipay.sofa.ark.web.embed.WebPluginActivator", + "pluginName": "web-ark-plugin", + "pluginUrl": "file:/****/web-ark-plugin-2.2.3-20230901.090402-2.jar!/", + "pluginVersion": "2.2.3-SNAPSHOT" + } + ] + } + } +} +``` + +### Query Health Using Endpoint + +use endpoint for k8s module to get helath info + +**default config** +* endpoints exposure include: `*` +* endpoints base path: `/` +* endpoints sever port: `8080` + +**http code result** +* `HEALTHY(200)`: get health if all health indicator is healthy +* `UNHEALTHY(400)`: get health once a health indicator is unhealthy +* `ENDPOINT_NOT_FOUND(404)`: endpoint path or params not found +* `ENDPOINT_PROCESS_INTERNAL_ERROR(500)`: get health process throw an error + +### query all health info +- url: 127.0.0.1:8080/arkletHealth +- method: GET +- output sample + +```json +{ + "healthy": true, + "code": 200, + "codeType": "HEALTHY", + "data": { + "jvm": {...}, + "masterBizHealth": {...}, + "cpu": {...}, + "masterBizInfo": {...}, + "bizListInfo": [...], + "pluginListInfo": [...] + } +} +``` +### query all biz/plugin health info +- url: 127.0.0.1:8080/arkletHealth/{moduleType} (moduleType must in ['biz', 'plugin']) +- method: GET +- output sample + ```json +{ + "healthy": true, + "code": 200, + "codeType": "HEALTHY", + "data": { + "bizListInfo": [...], + // "pluginListInfo": [...] + } +} +``` + ### query single biz/plugin health info +- url: 127.0.0.1:8080/arkletHealth/{moduleType}/moduleName/moduleVersion (moduleType must in ['biz', 'plugin']) +- method: GET +- output sample + + ```json +{ + "healthy": true, + "code": 200, + "codeType": "HEALTHY", + "data": { + "bizInfo": {...}, + // "pluginInfo": {...} + } +} +``` + + + diff --git a/arklet/arklet-core/pom.xml b/arklet/arklet-core/pom.xml index cbc7434de..a21b972a8 100644 --- a/arklet/arklet-core/pom.xml +++ b/arklet/arklet-core/pom.xml @@ -18,6 +18,11 @@ sofa-ark-api + + com.github.oshi + oshi-core + + com.google.inject guice @@ -63,6 +68,17 @@ junit test + + + org.mockito + mockito-core + test + + + + org.apache.commons + commons-lang3 + \ No newline at end of file diff --git a/arklet/arklet-core/src/main/java/com/alipay/sofa/serverless/arklet/core/ArkletComponent.java b/arklet/arklet-core/src/main/java/com/alipay/sofa/serverless/arklet/core/ArkletComponent.java index bb469bc2d..52f4e1878 100644 --- a/arklet/arklet-core/src/main/java/com/alipay/sofa/serverless/arklet/core/ArkletComponent.java +++ b/arklet/arklet-core/src/main/java/com/alipay/sofa/serverless/arklet/core/ArkletComponent.java @@ -17,12 +17,22 @@ package com.alipay.sofa.serverless.arklet.core; /** + * * Arklet component interface, managed by registry + * * @see ArkletComponentRegistry * @author mingmen * @date 2023/6/8 */ public interface ArkletComponent { + /** + * ArkletComponent init method, called when arklet try to start + * the extended custom component should use this method to do some initialization + */ void init(); + /** + * ArkletComponent destroy method, called when arklet try to stop + * the extended custom component should use this method to destroy itself + */ void destroy(); } diff --git a/arklet/arklet-core/src/main/java/com/alipay/sofa/serverless/arklet/core/ArkletComponentRegistry.java b/arklet/arklet-core/src/main/java/com/alipay/sofa/serverless/arklet/core/ArkletComponentRegistry.java index 6e00bd619..ec7702f52 100644 --- a/arklet/arklet-core/src/main/java/com/alipay/sofa/serverless/arklet/core/ArkletComponentRegistry.java +++ b/arklet/arklet-core/src/main/java/com/alipay/sofa/serverless/arklet/core/ArkletComponentRegistry.java @@ -21,6 +21,8 @@ import java.util.concurrent.atomic.AtomicBoolean; import java.util.stream.Collectors; +import com.alipay.sofa.serverless.arklet.core.health.HealthService; +import com.alipay.sofa.serverless.arklet.core.health.HealthServiceImpl; import com.alipay.sofa.serverless.arklet.core.api.ApiClient; import com.alipay.sofa.serverless.arklet.core.command.CommandService; import com.alipay.sofa.serverless.arklet.core.command.CommandServiceImpl; @@ -92,6 +94,10 @@ public static CommandService getCommandServiceInstance() { return componentInjector.getInstance(CommandService.class); } + public static HealthService getHealthServiceInstance() { + return componentInjector.getInstance(HealthService.class); + } + public static ApiClient getApiClientInstance() { return componentInjector.getInstance(ApiClient.class); } @@ -104,10 +110,11 @@ protected void configure() { componentMultibinder.addBinding().to(CommandServiceImpl.class); componentMultibinder.addBinding().to(ApiClient.class); componentMultibinder.addBinding().to(UnifiedOperationServiceImpl.class); + componentMultibinder.addBinding().to(HealthServiceImpl.class); bind(CommandService.class).to(CommandServiceImpl.class); bind(UnifiedOperationService.class).to(UnifiedOperationServiceImpl.class); - + bind(HealthService.class).to(HealthServiceImpl.class); } } } diff --git a/arklet/arklet-core/src/main/java/com/alipay/sofa/serverless/arklet/core/api/ApiClient.java b/arklet/arklet-core/src/main/java/com/alipay/sofa/serverless/arklet/core/api/ApiClient.java index 9274b4561..3bc096a2d 100644 --- a/arklet/arklet-core/src/main/java/com/alipay/sofa/serverless/arklet/core/api/ApiClient.java +++ b/arklet/arklet-core/src/main/java/com/alipay/sofa/serverless/arklet/core/api/ApiClient.java @@ -19,6 +19,7 @@ import java.util.ArrayList; import java.util.List; +import com.alipay.sofa.ark.api.ArkClient; import com.alipay.sofa.serverless.arklet.core.api.tunnel.Tunnel; import com.alipay.sofa.serverless.arklet.core.api.tunnel.http.HttpTunnel; import com.alipay.sofa.serverless.arklet.core.command.CommandService; diff --git a/arklet/arklet-core/src/main/java/com/alipay/sofa/serverless/arklet/core/command/CommandService.java b/arklet/arklet-core/src/main/java/com/alipay/sofa/serverless/arklet/core/command/CommandService.java index 3be3f3dd0..b1d3a34d7 100644 --- a/arklet/arklet-core/src/main/java/com/alipay/sofa/serverless/arklet/core/command/CommandService.java +++ b/arklet/arklet-core/src/main/java/com/alipay/sofa/serverless/arklet/core/command/CommandService.java @@ -22,7 +22,6 @@ import com.alipay.sofa.serverless.arklet.core.command.meta.AbstractCommandHandler; import com.alipay.sofa.serverless.arklet.core.ArkletComponent; import com.alipay.sofa.serverless.arklet.core.command.meta.Command; -import com.alipay.sofa.serverless.arklet.core.command.meta.CommandType; import com.alipay.sofa.serverless.arklet.core.command.meta.Output; /** diff --git a/arklet/arklet-core/src/main/java/com/alipay/sofa/serverless/arklet/core/command/CommandServiceImpl.java b/arklet/arklet-core/src/main/java/com/alipay/sofa/serverless/arklet/core/command/CommandServiceImpl.java index 27e7e4eda..b566a3c75 100644 --- a/arklet/arklet-core/src/main/java/com/alipay/sofa/serverless/arklet/core/command/CommandServiceImpl.java +++ b/arklet/arklet-core/src/main/java/com/alipay/sofa/serverless/arklet/core/command/CommandServiceImpl.java @@ -20,20 +20,29 @@ import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ThreadPoolExecutor; import com.alibaba.fastjson.JSONObject; - +import com.alipay.sofa.ark.common.util.BizIdentityUtils; import com.alipay.sofa.common.utils.StringUtil; +import com.alipay.sofa.serverless.arklet.core.api.model.ResponseCode; import com.alipay.sofa.serverless.arklet.core.command.builtin.handler.HelpHandler; import com.alipay.sofa.serverless.arklet.core.command.builtin.handler.InstallBizHandler; import com.alipay.sofa.serverless.arklet.core.command.builtin.handler.QueryAllBizHandler; +import com.alipay.sofa.serverless.arklet.core.command.builtin.handler.QueryBizOpsHandler; import com.alipay.sofa.serverless.arklet.core.command.builtin.handler.SwitchBizHandler; import com.alipay.sofa.serverless.arklet.core.command.builtin.handler.UninstallBizHandler; -import com.alipay.sofa.serverless.arklet.core.command.coordinate.CommandMutexException; +import com.alipay.sofa.serverless.arklet.core.command.builtin.handler.HealthHandler; +import com.alipay.sofa.serverless.arklet.core.command.coordinate.BizOpsCommandCoordinator; +import com.alipay.sofa.serverless.arklet.core.command.executor.ExecutorServiceManager; import com.alipay.sofa.serverless.arklet.core.command.meta.AbstractCommandHandler; +import com.alipay.sofa.serverless.arklet.core.command.meta.bizops.ArkBizMeta; +import com.alipay.sofa.serverless.arklet.core.command.meta.bizops.ArkBizOps; import com.alipay.sofa.serverless.arklet.core.command.meta.Command; import com.alipay.sofa.serverless.arklet.core.command.meta.InputMeta; import com.alipay.sofa.serverless.arklet.core.command.meta.Output; +import com.alipay.sofa.serverless.arklet.core.command.record.ProcessRecord; +import com.alipay.sofa.serverless.arklet.core.command.record.ProcessRecordHolder; import com.alipay.sofa.serverless.arklet.core.common.exception.ArkletInitException; import com.alipay.sofa.serverless.arklet.core.common.exception.CommandValidationException; import com.alipay.sofa.serverless.arklet.core.common.log.ArkletLogger; @@ -64,6 +73,9 @@ public void registerCommandHandler(AbstractCommandHandler handler) { throw new ArkletInitException("handler id (" + handler.command().getId() + ") duplicated"); } + if (isBizOpsHandler(handler)) { + validateBizOpsHandler(handler); + } handlerMap.put(handler.command().getId(), handler); LOGGER.info("registered command:{}", handler.command().getId()); } @@ -73,17 +85,89 @@ public void init() { registerBuiltInCommands(); } + private void registerBuiltInCommands() { + registerCommandHandler(new InstallBizHandler()); + registerCommandHandler(new HelpHandler()); + registerCommandHandler(new QueryAllBizHandler()); + registerCommandHandler(new UninstallBizHandler()); + registerCommandHandler(new SwitchBizHandler()); + registerCommandHandler(new HealthHandler()); + registerCommandHandler(new QueryBizOpsHandler()); + } + @Override public void destroy() { handlerMap.clear(); } @Override - public Output process(String cmd, Map content) throws CommandValidationException, - CommandMutexException { + public Output process(String cmd, Map content) throws CommandValidationException { AbstractCommandHandler handler = getHandler(cmd); InputMeta input = toJavaBean(handler.getInputClass(), content); handler.validate(input); + + if (isBizOpsHandler(handler)) { + ArkBizMeta arkBizMeta = (ArkBizMeta) input; + AssertUtils.assertNotNull(arkBizMeta, + "when execute bizOpsHandler, arkBizMeta should not be null"); + if (arkBizMeta.isAsync()) { + String requestId = arkBizMeta.getRequestId(); + if (ProcessRecordHolder.getProcessRecord(requestId) != null) { + // 该requestId对应指令,已经有执行记录,不可重复执行 + return Output.ofFailed(String.format("The request corresponding to the requestId(%s) has been executed.", requestId)); + } + final ProcessRecord processRecord = ProcessRecordHolder.createProcessRecord(requestId, arkBizMeta); + ThreadPoolExecutor executor = ExecutorServiceManager.getArkBizOpsExecutor(); + executor.submit(() -> { + try { + processRecord.setThreadName(Thread.currentThread().getName()); + boolean canProcess = BizOpsCommandCoordinator.checkAndLock(arkBizMeta.getBizName(), + arkBizMeta.getBizVersion(), handler.command()); + if (!canProcess) { + processRecord.fail("command conflict, exist unfinished command for this biz"); + } else { + processRecord.start(); + Output output = handler.handle(input); + if (output.success()) { + processRecord.success(); + } else { + processRecord.fail(output.getMessage()); + } + } + } catch (Throwable throwable) { + processRecord.fail(throwable.getMessage(), throwable); + LOGGER.error("Error happened when handling command, requestId=" + requestId, throwable); + } finally { + processRecord.markFinishTime(); + BizOpsCommandCoordinator + .unlock(arkBizMeta.getBizName(), arkBizMeta.getBizVersion()); + } + }); + return Output.ofSuccess(processRecord); + } else { + boolean canProcess = BizOpsCommandCoordinator.checkAndLock(arkBizMeta.getBizName(), + arkBizMeta.getBizVersion(), handler.command()); + if (!canProcess) { + return Output + .ofFailed(ResponseCode.FAILED.name() + + ":" + + String.format( + "%s %s conflict, exist unfinished command(%s) for this biz", + BizIdentityUtils.generateBizIdentity(arkBizMeta.getBizName(), + arkBizMeta.getBizVersion()), + handler.command().getId(), + BizOpsCommandCoordinator.getCurrentProcessingCommand( + arkBizMeta.getBizName(), arkBizMeta.getBizVersion()).getId())); + } + try { + return handler.handle(input); + } finally { + BizOpsCommandCoordinator + .unlock(arkBizMeta.getBizName(), arkBizMeta.getBizVersion()); + + } + } + } return handler.handle(input); } @@ -117,11 +201,18 @@ public AbstractCommandHandler getHandler(String commandId) { return handler; } - private void registerBuiltInCommands() { - registerCommandHandler(new InstallBizHandler()); - registerCommandHandler(new HelpHandler()); - registerCommandHandler(new QueryAllBizHandler()); - registerCommandHandler(new UninstallBizHandler()); - registerCommandHandler(new SwitchBizHandler()); + private boolean isBizOpsHandler(AbstractCommandHandler handler) { + return handler instanceof ArkBizOps; } + + private void validateBizOpsHandler(AbstractCommandHandler handler) { + Class inputClass = handler.getInputClass(); + if (!ArkBizMeta.class.isAssignableFrom(inputClass)) { + throw new ArkletInitException( + "handler id (" + handler.command().getId() + + ") is a bizOpsHandler, its input class should inherited from " + + ArkBizMeta.class.getCanonicalName()); + } + } + } diff --git a/arklet/arklet-core/src/main/java/com/alipay/sofa/serverless/arklet/core/command/builtin/BuiltinCommand.java b/arklet/arklet-core/src/main/java/com/alipay/sofa/serverless/arklet/core/command/builtin/BuiltinCommand.java index 20a41f6b4..f24508201 100644 --- a/arklet/arklet-core/src/main/java/com/alipay/sofa/serverless/arklet/core/command/builtin/BuiltinCommand.java +++ b/arklet/arklet-core/src/main/java/com/alipay/sofa/serverless/arklet/core/command/builtin/BuiltinCommand.java @@ -32,7 +32,11 @@ public enum BuiltinCommand implements Command { SWITCH_BIZ("switchBiz", "switch one ark biz"), - QUERY_ALL_BIZ("queryAllBiz", "query all ark biz(including master biz)"); + QUERY_ALL_BIZ("queryAllBiz", "query all ark biz(including master biz)"), + + HEALTH("health", "get all health info"), + + QUERY_BIZ_OPS("queryBizOps", "query ark biz ops"); private final String id; private final String desc; diff --git a/arklet/arklet-core/src/main/java/com/alipay/sofa/serverless/arklet/core/command/builtin/handler/HealthHandler.java b/arklet/arklet-core/src/main/java/com/alipay/sofa/serverless/arklet/core/command/builtin/handler/HealthHandler.java new file mode 100644 index 000000000..b8c3d3357 --- /dev/null +++ b/arklet/arklet-core/src/main/java/com/alipay/sofa/serverless/arklet/core/command/builtin/handler/HealthHandler.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.serverless.arklet.core.command.builtin.handler; + +import com.alipay.sofa.ark.common.util.StringUtils; +import com.alipay.sofa.serverless.arklet.core.health.model.Constants; +import com.alipay.sofa.serverless.arklet.core.health.model.Health; +import com.alipay.sofa.serverless.arklet.core.health.model.Health.HealthBuilder; +import com.alipay.sofa.serverless.arklet.core.command.builtin.BuiltinCommand; +import com.alipay.sofa.serverless.arklet.core.command.meta.AbstractCommandHandler; +import com.alipay.sofa.serverless.arklet.core.command.meta.Command; +import com.alipay.sofa.serverless.arklet.core.command.meta.Output; +import com.alipay.sofa.serverless.arklet.core.command.meta.bizops.ArkBizMeta; +import com.alipay.sofa.serverless.arklet.core.common.exception.CommandValidationException; +import lombok.Getter; +import lombok.Setter; + +/** + * @author Lunarscave + */ +public class HealthHandler extends AbstractCommandHandler { + @Override + public void validate(Input input) throws CommandValidationException { + if (input != null) { + String type = input.getType(); + if (!StringUtils.isEmpty(input.getType())) { + isTrue(Constants.typeOfQuery(type), "type: %s can not be found", type); + } + } + } + + @Override + public Output handle(Input input) { + + input = input == null ? new Input() : input; + String type = input.getType(); + HealthBuilder builder = new HealthBuilder(); + + // set query strategy + if (StringUtils.isEmpty(type)) { + builder.putAllHealthData(getHealthService().queryMasterBiz()); + } + if (StringUtils.isEmpty(type) || Constants.SYSTEM.equals(type)) { + builder.putAllHealthData(getHealthService().getHealth(input.getMetrics())); + } + if (StringUtils.isEmpty(type) || Constants.typeOfInfo(type)) { + String name = input.getModuleName(); + String version = input.getModuleVersion(); + builder.putAllHealthData(getHealthService().queryModuleInfo(type, name, version)); + } + Health health = builder.build(); + + if (health.containsError(Constants.HEALTH_ERROR)) { + return Output.ofFailed(health.getHealthData().get(Constants.HEALTH_ERROR).toString()); + } else { + return Output.ofSuccess(health); + } + } + + @Override + public Command command() { + return BuiltinCommand.HEALTH; + } + + @Getter + @Setter + public static class Input extends ArkBizMeta { + private String type; + private String moduleName; + private String moduleVersion; + private String[] metrics; + } +} diff --git a/arklet/arklet-core/src/main/java/com/alipay/sofa/serverless/arklet/core/command/builtin/handler/HelpHandler.java b/arklet/arklet-core/src/main/java/com/alipay/sofa/serverless/arklet/core/command/builtin/handler/HelpHandler.java index fa54697a8..c4cd2103f 100644 --- a/arklet/arklet-core/src/main/java/com/alipay/sofa/serverless/arklet/core/command/builtin/handler/HelpHandler.java +++ b/arklet/arklet-core/src/main/java/com/alipay/sofa/serverless/arklet/core/command/builtin/handler/HelpHandler.java @@ -33,10 +33,10 @@ */ @SuppressWarnings("rawtypes") -public class HelpHandler extends AbstractCommandHandler { +public class HelpHandler extends AbstractCommandHandler> { @Override - public Output handle(InputMeta inputMeta) { + public Output> handle(InputMeta inputMeta) { List list = getCommandService().listAllHandlers(); List models = new ArrayList<>(list.size()); for (AbstractCommandHandler handler : list) { diff --git a/arklet/arklet-core/src/main/java/com/alipay/sofa/serverless/arklet/core/command/builtin/handler/InstallBizHandler.java b/arklet/arklet-core/src/main/java/com/alipay/sofa/serverless/arklet/core/command/builtin/handler/InstallBizHandler.java index 0ee6b888c..1fb20cacd 100644 --- a/arklet/arklet-core/src/main/java/com/alipay/sofa/serverless/arklet/core/command/builtin/handler/InstallBizHandler.java +++ b/arklet/arklet-core/src/main/java/com/alipay/sofa/serverless/arklet/core/command/builtin/handler/InstallBizHandler.java @@ -16,19 +16,15 @@ */ package com.alipay.sofa.serverless.arklet.core.command.builtin.handler; -import java.util.Set; - 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.spi.model.BizInfo; +import com.alipay.sofa.ark.common.util.StringUtils; import com.alipay.sofa.serverless.arklet.core.command.builtin.BuiltinCommand; -import com.alipay.sofa.serverless.arklet.core.command.builtin.handler.InstallBizHandler.Input; -import com.alipay.sofa.serverless.arklet.core.command.coordinate.BizCommandCoordinator; import com.alipay.sofa.serverless.arklet.core.command.meta.AbstractCommandHandler; import com.alipay.sofa.serverless.arklet.core.command.meta.Command; -import com.alipay.sofa.serverless.arklet.core.command.meta.InputMeta; import com.alipay.sofa.serverless.arklet.core.command.meta.Output; +import com.alipay.sofa.serverless.arklet.core.command.meta.bizops.ArkBizMeta; +import com.alipay.sofa.serverless.arklet.core.command.meta.bizops.ArkBizOps; import com.alipay.sofa.serverless.arklet.core.common.exception.ArkletRuntimeException; import com.alipay.sofa.serverless.arklet.core.common.exception.CommandValidationException; import lombok.Getter; @@ -38,35 +34,22 @@ * @author mingmen * @date 2023/6/8 */ -public class InstallBizHandler extends AbstractCommandHandler> { +public class InstallBizHandler extends + AbstractCommandHandler + implements + ArkBizOps { @Override - public Output> handle(Input input) { - boolean conflict = BizCommandCoordinator.existBizProcessing(input.getBizName(), - input.getBizVersion()); - if (conflict) { - return Output - .ofFailed(ResponseCode.FAILED.name() - + ":" - + String.format( - "%s install conflict, exist unfinished command for this biz", - BizIdentityUtils.generateBizIdentity(input.getBizName(), - input.getBizVersion()))); - } + public Output handle(Input input) { try { - BizCommandCoordinator.putBizExecution(input.getBizName(), input.getBizVersion(), - command()); - String bizFile = input.getArkBizFilePath(); - ClientResponse res = getOperationService().install(bizFile); + ClientResponse res = getOperationService().install(input.getBizUrl()); if (ResponseCode.SUCCESS.equals(res.getCode())) { - return Output.ofSuccess(res.getBizInfos()); + return Output.ofSuccess(res); } else { - return Output.ofFailed(res.getCode().name() + ":" + res.getMessage()); + return Output.ofFailed(res, "install biz not success!"); } } catch (Throwable e) { throw new ArkletRuntimeException(e); - } finally { - BizCommandCoordinator.popBizExecution(input.bizName, input.getBizVersion()); } } @@ -79,16 +62,15 @@ public Command command() { public void validate(Input input) throws CommandValidationException { notBlank(input.getBizName(), "bizName should not be blank"); notBlank(input.getBizVersion(), "bizVersion should not be blank"); - notBlank(input.getArkBizFilePath(), "arkBizFilePath should not be blank"); + isTrue(!input.isAsync() || !StringUtils.isEmpty(input.getRequestId()), + "requestId should not be blank when async is true"); + notBlank(input.getBizUrl(), "bizUrl should not be blank"); } @Getter @Setter - public static class Input extends InputMeta { - private String arkBizFilePath; - private String bizName; - private String bizVersion; - private String downloadUrl; + public static class Input extends ArkBizMeta { + private String bizUrl; } } diff --git a/arklet/arklet-core/src/main/java/com/alipay/sofa/serverless/arklet/core/command/builtin/handler/QueryAllBizHandler.java b/arklet/arklet-core/src/main/java/com/alipay/sofa/serverless/arklet/core/command/builtin/handler/QueryAllBizHandler.java index d2b00c23e..4018e9969 100644 --- a/arklet/arklet-core/src/main/java/com/alipay/sofa/serverless/arklet/core/command/builtin/handler/QueryAllBizHandler.java +++ b/arklet/arklet-core/src/main/java/com/alipay/sofa/serverless/arklet/core/command/builtin/handler/QueryAllBizHandler.java @@ -21,7 +21,7 @@ import com.alipay.sofa.ark.spi.model.Biz; import com.alipay.sofa.serverless.arklet.core.command.builtin.BuiltinCommand; -import com.alipay.sofa.serverless.arklet.core.command.builtin.model.BizModel; +import com.alipay.sofa.serverless.arklet.core.command.builtin.model.BizInfo; import com.alipay.sofa.serverless.arklet.core.command.meta.AbstractCommandHandler; import com.alipay.sofa.serverless.arklet.core.command.meta.Command; import com.alipay.sofa.serverless.arklet.core.command.meta.Output; @@ -32,22 +32,22 @@ * @author mingmen * @date 2023/6/14 */ -public class QueryAllBizHandler extends AbstractCommandHandler> { +public class QueryAllBizHandler extends AbstractCommandHandler> { @Override - public Output> handle(InputMeta inputMeta) { + public Output> handle(InputMeta inputMeta) { List bizList = getOperationService().queryBizList(); - List bizModels = new ArrayList<>(bizList.size()); + List bizInfos = new ArrayList<>(bizList.size()); for (Biz biz : bizList) { - BizModel model = new BizModel(); + BizInfo model = new BizInfo(); model.setBizName(biz.getBizName()); model.setBizVersion(biz.getBizVersion()); model.setBizState(biz.getBizState()); model.setMainClass(biz.getMainClass()); model.setWebContextPath(biz.getWebContextPath()); - bizModels.add(model); + bizInfos.add(model); } - return Output.ofSuccess(bizModels); + return Output.ofSuccess(bizInfos); } @Override @@ -57,6 +57,6 @@ public Command command() { @Override public void validate(InputMeta input) throws CommandValidationException { - + // no need } } diff --git a/arklet/arklet-core/src/main/java/com/alipay/sofa/serverless/arklet/core/command/builtin/handler/QueryBizOpsHandler.java b/arklet/arklet-core/src/main/java/com/alipay/sofa/serverless/arklet/core/command/builtin/handler/QueryBizOpsHandler.java new file mode 100644 index 000000000..3e81fd4b4 --- /dev/null +++ b/arklet/arklet-core/src/main/java/com/alipay/sofa/serverless/arklet/core/command/builtin/handler/QueryBizOpsHandler.java @@ -0,0 +1,64 @@ +/* + * 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.serverless.arklet.core.command.builtin.handler; + +import com.alipay.sofa.serverless.arklet.core.command.meta.AbstractCommandHandler; +import com.alipay.sofa.serverless.arklet.core.command.meta.Command; +import com.alipay.sofa.serverless.arklet.core.command.meta.InputMeta; +import com.alipay.sofa.serverless.arklet.core.command.meta.Output; +import com.alipay.sofa.serverless.arklet.core.command.record.ProcessRecord; +import com.alipay.sofa.serverless.arklet.core.command.record.ProcessRecordHolder; +import com.alipay.sofa.serverless.arklet.core.common.exception.CommandValidationException; +import lombok.Getter; +import lombok.Setter; + +import static com.alipay.sofa.serverless.arklet.core.command.builtin.BuiltinCommand.QUERY_BIZ_OPS; + +/** + * @author: yuanyuan + * @date: 2023/9/4 9:50 下午 + */ +public class QueryBizOpsHandler extends + AbstractCommandHandler { + + @Override + public void validate(Input input) throws CommandValidationException { + notNull(input, "request is null"); + notBlank(input.getRequestId(), "requestId is blank"); + } + + @Override + public Output handle(Input input) { + String requestId = input.getRequestId(); + ProcessRecord processRecord = ProcessRecordHolder.getProcessRecord(requestId); + if (processRecord == null) { + return Output.ofFailed("Not found the corresponding ops record."); + } + return Output.ofSuccess(processRecord); + } + + @Override + public Command command() { + return QUERY_BIZ_OPS; + } + + @Getter + @Setter + public static class Input extends InputMeta { + private String requestId; + } +} diff --git a/arklet/arklet-core/src/main/java/com/alipay/sofa/serverless/arklet/core/command/builtin/handler/SwitchBizHandler.java b/arklet/arklet-core/src/main/java/com/alipay/sofa/serverless/arklet/core/command/builtin/handler/SwitchBizHandler.java index a2406f1be..a1183d493 100644 --- a/arklet/arklet-core/src/main/java/com/alipay/sofa/serverless/arklet/core/command/builtin/handler/SwitchBizHandler.java +++ b/arklet/arklet-core/src/main/java/com/alipay/sofa/serverless/arklet/core/command/builtin/handler/SwitchBizHandler.java @@ -18,52 +18,36 @@ 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.serverless.arklet.core.command.builtin.BuiltinCommand; import com.alipay.sofa.serverless.arklet.core.command.builtin.handler.SwitchBizHandler.Input; -import com.alipay.sofa.serverless.arklet.core.command.coordinate.BizCommandCoordinator; import com.alipay.sofa.serverless.arklet.core.command.meta.AbstractCommandHandler; import com.alipay.sofa.serverless.arklet.core.command.meta.Command; import com.alipay.sofa.serverless.arklet.core.command.meta.Output; -import com.alipay.sofa.serverless.arklet.core.common.exception.CommandValidationException; -import com.alipay.sofa.serverless.arklet.core.command.meta.InputMeta; +import com.alipay.sofa.serverless.arklet.core.command.meta.bizops.ArkBizMeta; +import com.alipay.sofa.serverless.arklet.core.command.meta.bizops.ArkBizOps; import com.alipay.sofa.serverless.arklet.core.common.exception.ArkletRuntimeException; -import lombok.Getter; -import lombok.Setter; +import com.alipay.sofa.serverless.arklet.core.common.exception.CommandValidationException; /** * @author mingmen * @date 2023/6/14 */ -public class SwitchBizHandler extends AbstractCommandHandler { +public class SwitchBizHandler extends AbstractCommandHandler implements + ArkBizOps { @Override - public Output handle(Input input) { + public Output handle(Input input) { try { - boolean conflict = BizCommandCoordinator.existBizProcessing(input.getBizName(), - input.getBizVersion()); - if (conflict) { - return Output - .ofFailed(ResponseCode.FAILED.name() - + ":" - + String.format( - "%s switch conflict, exist unfinished command for this biz", - BizIdentityUtils.generateBizIdentity(input.getBizName(), - input.getBizVersion()))); - } - BizCommandCoordinator.putBizExecution(input.getBizName(), input.getBizVersion(), - command()); ClientResponse res = getOperationService().switchBiz(input.getBizName(), input.getBizVersion()); if (ResponseCode.SUCCESS.equals(res.getCode())) { - return Output.ofSuccess(null); + return Output.ofSuccess(res); } else { - return Output.ofFailed(res.getCode().name() + ":" + res.getMessage()); + return Output.ofFailed(res, "switch biz not success!"); } } catch (Throwable e) { throw new ArkletRuntimeException(e); - } finally { - BizCommandCoordinator.popBizExecution(input.getBizName(), input.getBizVersion()); } } @@ -76,13 +60,11 @@ public Command command() { 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"); } - @Setter - @Getter - public static class Input extends InputMeta { - private String bizName; - private String bizVersion; + public static class Input extends ArkBizMeta { } } diff --git a/arklet/arklet-core/src/main/java/com/alipay/sofa/serverless/arklet/core/command/builtin/handler/UninstallBizHandler.java b/arklet/arklet-core/src/main/java/com/alipay/sofa/serverless/arklet/core/command/builtin/handler/UninstallBizHandler.java index f90da4891..3b9e35d24 100644 --- a/arklet/arklet-core/src/main/java/com/alipay/sofa/serverless/arklet/core/command/builtin/handler/UninstallBizHandler.java +++ b/arklet/arklet-core/src/main/java/com/alipay/sofa/serverless/arklet/core/command/builtin/handler/UninstallBizHandler.java @@ -18,52 +18,36 @@ 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.serverless.arklet.core.command.builtin.BuiltinCommand; import com.alipay.sofa.serverless.arklet.core.command.builtin.handler.UninstallBizHandler.Input; -import com.alipay.sofa.serverless.arklet.core.command.coordinate.BizCommandCoordinator; import com.alipay.sofa.serverless.arklet.core.command.meta.AbstractCommandHandler; import com.alipay.sofa.serverless.arklet.core.command.meta.Command; import com.alipay.sofa.serverless.arklet.core.command.meta.Output; -import com.alipay.sofa.serverless.arklet.core.common.exception.CommandValidationException; -import com.alipay.sofa.serverless.arklet.core.command.meta.InputMeta; +import com.alipay.sofa.serverless.arklet.core.command.meta.bizops.ArkBizMeta; +import com.alipay.sofa.serverless.arklet.core.command.meta.bizops.ArkBizOps; import com.alipay.sofa.serverless.arklet.core.common.exception.ArkletRuntimeException; -import lombok.Getter; -import lombok.Setter; +import com.alipay.sofa.serverless.arklet.core.common.exception.CommandValidationException; /** * @author mingmen * @date 2023/6/14 */ -public class UninstallBizHandler extends AbstractCommandHandler { +public class UninstallBizHandler extends AbstractCommandHandler implements + ArkBizOps { @Override - public Output handle(Input input) { + public Output handle(Input input) { try { - boolean conflict = BizCommandCoordinator.existBizProcessing(input.getBizName(), - input.getBizVersion()); - if (conflict) { - return Output - .ofFailed(ResponseCode.FAILED.name() - + ":" - + String.format( - "%s uninstall conflict, exist unfinished command for this biz", - BizIdentityUtils.generateBizIdentity(input.getBizName(), - input.getBizVersion()))); - } - BizCommandCoordinator.putBizExecution(input.getBizName(), input.getBizVersion(), - command()); ClientResponse res = getOperationService().uninstall(input.getBizName(), input.getBizVersion()); if (ResponseCode.SUCCESS.equals(res.getCode())) { - return Output.ofSuccess(null); + return Output.ofSuccess(res); } else { - return Output.ofFailed(res.getCode().name() + ":" + res.getMessage()); + return Output.ofFailed(res, "uninstall biz not success!"); } } catch (Throwable e) { throw new ArkletRuntimeException(e); - } finally { - BizCommandCoordinator.popBizExecution(input.getBizName(), input.getBizVersion()); } } @@ -76,13 +60,11 @@ public Command command() { 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"); } - @Setter - @Getter - public static class Input extends InputMeta { - private String bizName; - private String bizVersion; + public static class Input extends ArkBizMeta { } } diff --git a/arklet/arklet-core/src/main/java/com/alipay/sofa/serverless/arklet/core/command/builtin/model/BizModel.java b/arklet/arklet-core/src/main/java/com/alipay/sofa/serverless/arklet/core/command/builtin/model/BizInfo.java similarity index 98% rename from arklet/arklet-core/src/main/java/com/alipay/sofa/serverless/arklet/core/command/builtin/model/BizModel.java rename to arklet/arklet-core/src/main/java/com/alipay/sofa/serverless/arklet/core/command/builtin/model/BizInfo.java index 5cd4f36af..8d5130a95 100644 --- a/arklet/arklet-core/src/main/java/com/alipay/sofa/serverless/arklet/core/command/builtin/model/BizModel.java +++ b/arklet/arklet-core/src/main/java/com/alipay/sofa/serverless/arklet/core/command/builtin/model/BizInfo.java @@ -22,7 +22,7 @@ * @author mingmen * @date 2023/6/14 */ -public class BizModel { +public class BizInfo { private String bizName; private String bizVersion; diff --git a/arklet/arklet-core/src/main/java/com/alipay/sofa/serverless/arklet/core/command/builtin/model/PluginModel.java b/arklet/arklet-core/src/main/java/com/alipay/sofa/serverless/arklet/core/command/builtin/model/PluginModel.java new file mode 100644 index 000000000..e22540171 --- /dev/null +++ b/arklet/arklet-core/src/main/java/com/alipay/sofa/serverless/arklet/core/command/builtin/model/PluginModel.java @@ -0,0 +1,84 @@ +/* + * 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.serverless.arklet.core.command.builtin.model; + +/** + * @author Lunarscave + */ +public class PluginModel { + + private String pluginName; + + private String groupId; + + private String artifactId; + + private String pluginVersion; + + private String pluginUrl; + + private String pluginActivator; + + public String getPluginName() { + return pluginName; + } + + public String getGroupId() { + return groupId; + } + + public String getArtifactId() { + return artifactId; + } + + public String getPluginVersion() { + return pluginVersion; + } + + public String getPluginActivator() { + return pluginActivator; + } + + public String getPluginUrl() { + return pluginUrl; + } + + public void setPluginName(String pluginName) { + this.pluginName = pluginName; + } + + public void setGroupId(String groupId) { + this.groupId = groupId; + } + + public void setArtifactId(String artifactId) { + this.artifactId = artifactId; + } + + public void setPluginVersion(String pluginVersion) { + this.pluginVersion = pluginVersion; + } + + public void setPluginUrl(String pluginUrl) { + this.pluginUrl = pluginUrl; + } + + public void setPluginActivator(String pluginActivator) { + this.pluginActivator = pluginActivator; + } + +} diff --git a/arklet/arklet-core/src/main/java/com/alipay/sofa/serverless/arklet/core/command/coordinate/BizCommandCoordinator.java b/arklet/arklet-core/src/main/java/com/alipay/sofa/serverless/arklet/core/command/coordinate/BizOpsCommandCoordinator.java similarity index 68% rename from arklet/arklet-core/src/main/java/com/alipay/sofa/serverless/arklet/core/command/coordinate/BizCommandCoordinator.java rename to arklet/arklet-core/src/main/java/com/alipay/sofa/serverless/arklet/core/command/coordinate/BizOpsCommandCoordinator.java index 726fc0368..983776010 100644 --- a/arklet/arklet-core/src/main/java/com/alipay/sofa/serverless/arklet/core/command/coordinate/BizCommandCoordinator.java +++ b/arklet/arklet-core/src/main/java/com/alipay/sofa/serverless/arklet/core/command/coordinate/BizOpsCommandCoordinator.java @@ -26,21 +26,21 @@ * @author mingmen * @date 2023/6/14 */ -public class BizCommandCoordinator { +public class BizOpsCommandCoordinator { private static final Map bizIdentityLockMap = new ConcurrentHashMap<>(16); - public static void putBizExecution(String bizName, String bizVersion, Command command) { + public synchronized static boolean checkAndLock(String bizName, String bizVersion, + Command command) { String identity = BizIdentityUtils.generateBizIdentity(bizName, bizVersion); - if (bizIdentityLockMap.containsKey(identity)) { - throw new CommandMutexException( - "biz {} execution meet mutex lock, conflict command:%s is processing and not finish yet", - identity, bizIdentityLockMap.get(identity)); + if (existBizProcessing(identity)) { + return false; } bizIdentityLockMap.put(identity, command); + return true; } - public static void popBizExecution(String bizName, String bizVersion) { + public static void unlock(String bizName, String bizVersion) { String identity = BizIdentityUtils.generateBizIdentity(bizName, bizVersion); bizIdentityLockMap.remove(identity); } @@ -50,4 +50,17 @@ public static boolean existBizProcessing(String bizName, String bizVersion) { return bizIdentityLockMap.containsKey(identity); } + public static boolean existBizProcessing(String identity) { + return bizIdentityLockMap.containsKey(identity); + } + + public static Command getCurrentProcessingCommand(String bizName, String bizVersion) { + String identity = BizIdentityUtils.generateBizIdentity(bizName, bizVersion); + return bizIdentityLockMap.get(identity); + } + + public static void clear() { + bizIdentityLockMap.clear(); + } + } diff --git a/arklet/arklet-core/src/main/java/com/alipay/sofa/serverless/arklet/core/command/coordinate/CommandMutexException.java b/arklet/arklet-core/src/main/java/com/alipay/sofa/serverless/arklet/core/command/coordinate/CommandMutexException.java deleted file mode 100644 index 0275e5881..000000000 --- a/arklet/arklet-core/src/main/java/com/alipay/sofa/serverless/arklet/core/command/coordinate/CommandMutexException.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * 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.serverless.arklet.core.command.coordinate; - -import com.alipay.sofa.serverless.arklet.core.common.exception.ArkletRuntimeException; - -/** - * @author mingmen - * @date 2023/6/14 - */ -public class CommandMutexException extends ArkletRuntimeException { - public CommandMutexException() { - } - - public CommandMutexException(String message) { - super(message); - } - - public CommandMutexException(String message, Throwable cause) { - super(message, cause); - } - - public CommandMutexException(Throwable cause) { - super(cause); - } - - public CommandMutexException(String message, Throwable cause, boolean enableSuppression, - boolean writableStackTrace) { - super(message, cause, enableSuppression, writableStackTrace); - } - - public CommandMutexException(String format, Object... args) { - super(format, args); - } - - public CommandMutexException(Throwable cause, String format, Object... args) { - super(cause, format, args); - } -} diff --git a/arklet/arklet-core/src/main/java/com/alipay/sofa/serverless/arklet/core/command/executor/ExecutorServiceManager.java b/arklet/arklet-core/src/main/java/com/alipay/sofa/serverless/arklet/core/command/executor/ExecutorServiceManager.java new file mode 100644 index 000000000..cd88af0be --- /dev/null +++ b/arklet/arklet-core/src/main/java/com/alipay/sofa/serverless/arklet/core/command/executor/ExecutorServiceManager.java @@ -0,0 +1,41 @@ +/* + * 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.serverless.arklet.core.command.executor; + +import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; + +/** + * @author: yuanyuan + * @date: 2023/8/31 4:12 下午 + */ +public class ExecutorServiceManager { + + private static ThreadPoolExecutor ARK_BIZ_OPS_EXECUTOR = new ThreadPoolExecutor( + 20, + 50, + 30, + TimeUnit.SECONDS, + new ArrayBlockingQueue<>(100), + new NamedThreadFactory("ark-biz-ops"), + new ThreadPoolExecutor.CallerRunsPolicy()); + + public static ThreadPoolExecutor getArkBizOpsExecutor() { + return ARK_BIZ_OPS_EXECUTOR; + } +} diff --git a/arklet/arklet-core/src/main/java/com/alipay/sofa/serverless/arklet/core/command/executor/NamedThreadFactory.java b/arklet/arklet-core/src/main/java/com/alipay/sofa/serverless/arklet/core/command/executor/NamedThreadFactory.java new file mode 100644 index 000000000..fc662cb51 --- /dev/null +++ b/arklet/arklet-core/src/main/java/com/alipay/sofa/serverless/arklet/core/command/executor/NamedThreadFactory.java @@ -0,0 +1,86 @@ +/* + * 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.serverless.arklet.core.command.executor; + +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * @author: yuanyuan + * @date: 2023/9/1 12:02 下午 + */ +public class NamedThreadFactory implements ThreadFactory { + + private static final AtomicInteger POOL_COUNT = new AtomicInteger(); + + /** + * The current thread pool counter + */ + private final AtomicInteger threadCount = new AtomicInteger(1); + + /** + * Thread group + */ + private final ThreadGroup group; + + /** + * Thread name prefix + */ + private final String namePrefix; + + /** + * Thread daemon option + */ + private final boolean isDaemon; + + /** + * The first default prefix of thread name + */ + private final static String FIRST_PREFIX = "ARKLET-"; + + /** + * specify the second prefix of thread name, default the thread created is non-daemon + * + * @param secondPrefix second prefix of thread name + */ + public NamedThreadFactory(String secondPrefix) { + this(secondPrefix, false); + } + + /** + * Construct a named thread factory + * + * @param secondPrefix second prefix of thread name + * @param daemon thread daemon option + */ + public NamedThreadFactory(String secondPrefix, boolean daemon) { + SecurityManager sm = System.getSecurityManager(); + group = (sm != null) ? sm.getThreadGroup() : Thread.currentThread().getThreadGroup(); + namePrefix = FIRST_PREFIX + secondPrefix + "-" + POOL_COUNT.getAndIncrement() + "-T"; + isDaemon = daemon; + } + + @Override + public Thread newThread(Runnable r) { + Thread t = new Thread(group, r, namePrefix + threadCount.getAndIncrement(), 0); + t.setDaemon(isDaemon); + if (t.getPriority() != Thread.NORM_PRIORITY) { + t.setPriority(Thread.NORM_PRIORITY); + } + return t; + } +} diff --git a/arklet/arklet-core/src/main/java/com/alipay/sofa/serverless/arklet/core/command/meta/AbstractCommandHandler.java b/arklet/arklet-core/src/main/java/com/alipay/sofa/serverless/arklet/core/command/meta/AbstractCommandHandler.java index 8364e741d..4efe83a5b 100644 --- a/arklet/arklet-core/src/main/java/com/alipay/sofa/serverless/arklet/core/command/meta/AbstractCommandHandler.java +++ b/arklet/arklet-core/src/main/java/com/alipay/sofa/serverless/arklet/core/command/meta/AbstractCommandHandler.java @@ -19,6 +19,7 @@ import java.lang.reflect.ParameterizedType; import com.alipay.sofa.common.utils.StringUtil; +import com.alipay.sofa.serverless.arklet.core.health.HealthService; import com.alipay.sofa.serverless.arklet.core.command.CommandService; import com.alipay.sofa.serverless.arklet.core.ArkletComponentRegistry; import com.alipay.sofa.serverless.arklet.core.common.exception.CommandValidationException; @@ -36,6 +37,8 @@ public abstract class AbstractCommandHandler

{ .getOperationServiceInstance(); private final CommandService commandService = ArkletComponentRegistry .getCommandServiceInstance(); + private final HealthService healthService = ArkletComponentRegistry + .getHealthServiceInstance(); public abstract void validate(P p) throws CommandValidationException; @@ -51,6 +54,10 @@ public CommandService getCommandService() { return commandService; } + public HealthService getHealthService() { + return healthService; + } + public Class

getInputClass() { ParameterizedType parameterizedType = (ParameterizedType) getClass().getGenericSuperclass(); return (Class

) parameterizedType.getActualTypeArguments()[0]; diff --git a/arklet/arklet-core/src/main/java/com/alipay/sofa/serverless/arklet/core/command/meta/Output.java b/arklet/arklet-core/src/main/java/com/alipay/sofa/serverless/arklet/core/command/meta/Output.java index 2bd95a24d..c997ed464 100644 --- a/arklet/arklet-core/src/main/java/com/alipay/sofa/serverless/arklet/core/command/meta/Output.java +++ b/arklet/arklet-core/src/main/java/com/alipay/sofa/serverless/arklet/core/command/meta/Output.java @@ -45,6 +45,22 @@ public static Output ofFailed(String message) { return output; } + public boolean success() { + return ResponseCode.SUCCESS.equals(code); + } + + public boolean failed() { + return ResponseCode.FAILED.equals(code); + } + + public static Output ofFailed(T data, String message) { + Output output = new Output<>(); + output.code = ResponseCode.FAILED; + output.data = data; + output.message = message; + return output; + } + public String getMessage() { return message; } diff --git a/arklet/arklet-core/src/main/java/com/alipay/sofa/serverless/arklet/core/command/meta/bizops/ArkBizMeta.java b/arklet/arklet-core/src/main/java/com/alipay/sofa/serverless/arklet/core/command/meta/bizops/ArkBizMeta.java new file mode 100644 index 000000000..9e3a21908 --- /dev/null +++ b/arklet/arklet-core/src/main/java/com/alipay/sofa/serverless/arklet/core/command/meta/bizops/ArkBizMeta.java @@ -0,0 +1,62 @@ +/* + * 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.serverless.arklet.core.command.meta.bizops; + +import com.alipay.sofa.serverless.arklet.core.command.meta.InputMeta; + +/** + * @author mingmen + * @date 2023/8/21 + */ +public class ArkBizMeta extends InputMeta { + private String bizName; + private String bizVersion; + private String requestId; + private boolean async; + + public String getBizName() { + return bizName; + } + + public void setBizName(String bizName) { + this.bizName = bizName; + } + + public String getBizVersion() { + return bizVersion; + } + + public void setBizVersion(String bizVersion) { + this.bizVersion = bizVersion; + } + + public String getRequestId() { + return requestId; + } + + public void setRequestId(String requestId) { + this.requestId = requestId; + } + + public boolean isAsync() { + return async; + } + + public void setAsync(boolean async) { + this.async = async; + } +} diff --git a/arklet/arklet-core/src/main/java/com/alipay/sofa/serverless/arklet/core/command/meta/CommandType.java b/arklet/arklet-core/src/main/java/com/alipay/sofa/serverless/arklet/core/command/meta/bizops/ArkBizOps.java similarity index 78% rename from arklet/arklet-core/src/main/java/com/alipay/sofa/serverless/arklet/core/command/meta/CommandType.java rename to arklet/arklet-core/src/main/java/com/alipay/sofa/serverless/arklet/core/command/meta/bizops/ArkBizOps.java index a38724736..6549f15a0 100644 --- a/arklet/arklet-core/src/main/java/com/alipay/sofa/serverless/arklet/core/command/meta/CommandType.java +++ b/arklet/arklet-core/src/main/java/com/alipay/sofa/serverless/arklet/core/command/meta/bizops/ArkBizOps.java @@ -14,12 +14,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.alipay.sofa.serverless.arklet.core.command.meta; +package com.alipay.sofa.serverless.arklet.core.command.meta.bizops; /** * @author mingmen - * @date 2023/6/14 + * @date 2023/8/21 + * An interface that requires manipulation of ark biz changes + * command handler needs to implement this interface */ -public enum CommandType { - READ, WRITE +public interface ArkBizOps { } diff --git a/arklet/arklet-core/src/main/java/com/alipay/sofa/serverless/arklet/core/command/record/ProcessRecord.java b/arklet/arklet-core/src/main/java/com/alipay/sofa/serverless/arklet/core/command/record/ProcessRecord.java new file mode 100644 index 000000000..d3e1f62ff --- /dev/null +++ b/arklet/arklet-core/src/main/java/com/alipay/sofa/serverless/arklet/core/command/record/ProcessRecord.java @@ -0,0 +1,134 @@ +/* + * 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.serverless.arklet.core.command.record; + +import com.alipay.sofa.serverless.arklet.core.command.meta.bizops.ArkBizMeta; +import com.alipay.sofa.serverless.arklet.core.common.log.ArkletLogger; +import com.alipay.sofa.serverless.arklet.core.common.log.ArkletLoggerFactory; +import lombok.Getter; +import lombok.Setter; + +import java.util.Date; + +import static com.alipay.sofa.serverless.arklet.core.command.record.ProcessRecord.Status.EXECUTING; +import static com.alipay.sofa.serverless.arklet.core.command.record.ProcessRecord.Status.FAILED; +import static com.alipay.sofa.serverless.arklet.core.command.record.ProcessRecord.Status.INITIALIZED; +import static com.alipay.sofa.serverless.arklet.core.command.record.ProcessRecord.Status.SUCCEEDED; + +/** + * @author: yuanyuan + * @date: 2023/8/31 3:27 下午 + */ +@Getter +@Setter +public class ProcessRecord { + + private static final ArkletLogger LOGGER = ArkletLoggerFactory.getDefaultLogger(); + + private String requestId; + + private ArkBizMeta arkBizMeta; + + private String threadName; + + private Status status; + + private Throwable throwable; + + private String errorCode; + + private String message; + + private Date startTime; + + private long startTimestamp; + + private Date endTime; + + private long endTimestamp; + + private long elapsedTime; + + public enum Status { + + INITIALIZED("INITIALIZED"), + + EXECUTING("EXECUTING"), + + SUCCEEDED("SUCCEEDED"), + + FAILED("FAILED"); + + private String name; + + Status(String name) { + this.name = name; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + } + + public void markFinishTime() { + Date date = new Date(); + setEndTime(date); + setEndTimestamp(date.getTime()); + setElapsedTime(date.getTime() - startTimestamp); + } + + public boolean finished() { + return SUCCEEDED.equals(getStatus()) || FAILED.equals(getStatus()); + } + + public void start() { + if (INITIALIZED.equals(getStatus())) { + setStatus(EXECUTING); + LOGGER.info("Command execution status change: INIT -> EXECUTING"); + } + } + + public void success() { + if (EXECUTING.equals(getStatus())) { + setStatus(SUCCEEDED); + LOGGER.info("Command execution status change: EXECUTING -> SUCCESS"); + } + } + + public void fail() { + if (EXECUTING.equals(getStatus())) { + setStatus(FAILED); + LOGGER.info("Command execution status change: EXECUTING -> FAIL"); + } + } + + public void fail(String message) { + fail(); + setMessage(message); + } + + public void fail(String message, Throwable throwable) { + fail(); + setMessage(message); + this.throwable = throwable; + } + +} diff --git a/arklet/arklet-core/src/main/java/com/alipay/sofa/serverless/arklet/core/command/record/ProcessRecordHolder.java b/arklet/arklet-core/src/main/java/com/alipay/sofa/serverless/arklet/core/command/record/ProcessRecordHolder.java new file mode 100644 index 000000000..01ccd4b75 --- /dev/null +++ b/arklet/arklet-core/src/main/java/com/alipay/sofa/serverless/arklet/core/command/record/ProcessRecordHolder.java @@ -0,0 +1,71 @@ +/* + * 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.serverless.arklet.core.command.record; + +import com.alipay.sofa.serverless.arklet.core.command.meta.InputMeta; +import com.alipay.sofa.serverless.arklet.core.command.meta.bizops.ArkBizMeta; +import org.apache.commons.lang3.StringUtils; + +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Collectors; + +import static com.alipay.sofa.serverless.arklet.core.command.record.ProcessRecord.Status.INITIALIZED; + +/** + * @author: yuanyuan + * @date: 2023/8/31 3:28 下午 + */ +public class ProcessRecordHolder { + + private static Map processRecords = new ConcurrentHashMap<>(); + + public static ProcessRecord getProcessRecord(String rid) { + if (StringUtils.isNotBlank(rid)) { + return processRecords.get(rid); + } + return null; + } + + public static List getAllProcessRecords() { + return new ArrayList<>(processRecords.values()); + } + + public static List getAllExecutingProcessRecords() { + return processRecords.values().stream().filter(record -> !record.finished()).collect(Collectors.toList()); + } + + public static List getProcessRecordsByStatus(String status) { + return processRecords.values().stream().filter(record -> StringUtils.equals(record.getStatus().name(), status)).collect(Collectors.toList()); + } + + public static ProcessRecord createProcessRecord(String rid, ArkBizMeta arkBizMeta) { + ProcessRecord pr = new ProcessRecord(); + pr.setRequestId(rid); + pr.setArkBizMeta(arkBizMeta); + pr.setStatus(INITIALIZED); + Date date = new Date(); + pr.setStartTime(date); + pr.setStartTimestamp(date.getTime()); + processRecords.put(rid, pr); + return pr; + } + +} diff --git a/arklet/arklet-core/src/main/java/com/alipay/sofa/serverless/arklet/core/common/log/ArkletLogger.java b/arklet/arklet-core/src/main/java/com/alipay/sofa/serverless/arklet/core/common/log/ArkletLogger.java index 2fa8cb093..74e853e41 100644 --- a/arklet/arklet-core/src/main/java/com/alipay/sofa/serverless/arklet/core/common/log/ArkletLogger.java +++ b/arklet/arklet-core/src/main/java/com/alipay/sofa/serverless/arklet/core/common/log/ArkletLogger.java @@ -20,8 +20,6 @@ import org.slf4j.Marker; /** - * Logger Implementation for SOFAArk - /** * @author mingmen * @date 2023/6/14 */ diff --git a/arklet/arklet-core/src/main/java/com/alipay/sofa/serverless/arklet/core/common/log/ArkletLoggerFactory.java b/arklet/arklet-core/src/main/java/com/alipay/sofa/serverless/arklet/core/common/log/ArkletLoggerFactory.java index d39e3ad5a..3effb5e5a 100644 --- a/arklet/arklet-core/src/main/java/com/alipay/sofa/serverless/arklet/core/common/log/ArkletLoggerFactory.java +++ b/arklet/arklet-core/src/main/java/com/alipay/sofa/serverless/arklet/core/common/log/ArkletLoggerFactory.java @@ -19,8 +19,6 @@ import com.alipay.sofa.common.log.LoggerSpaceManager; /** - * LoggerFactory for SOFAArk - /** * @author mingmen * @date 2023/6/14 */ diff --git a/arklet/arklet-core/src/main/java/com/alipay/sofa/serverless/arklet/core/health/HealthService.java b/arklet/arklet-core/src/main/java/com/alipay/sofa/serverless/arklet/core/health/HealthService.java new file mode 100644 index 000000000..5716525e7 --- /dev/null +++ b/arklet/arklet-core/src/main/java/com/alipay/sofa/serverless/arklet/core/health/HealthService.java @@ -0,0 +1,49 @@ +/* + * 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.serverless.arklet.core.health; + +import com.alipay.sofa.serverless.arklet.core.ArkletComponent; +import com.alipay.sofa.serverless.arklet.core.health.indicator.ArkletBaseIndicator; +import com.alipay.sofa.serverless.arklet.core.health.model.Health; +import com.alipay.sofa.serverless.arklet.core.command.builtin.model.BizInfo; +import com.alipay.sofa.serverless.arklet.core.command.builtin.model.PluginModel; + +/** + * @author Lunarscave + */ +public interface HealthService extends ArkletComponent { + + Health getHealth(); + + Health getHealth(String indicatorId); + + Health getHealth(String[] indicatorIds); + + Health queryModuleInfo(); + + Health queryModuleInfo(String type, String name, String version); + + Health queryModuleInfo(BizInfo bizInfo); + + Health queryModuleInfo(PluginModel pluginModel); + + Health queryMasterBiz(); + + ArkletBaseIndicator getIndicator(String indicatorId); + + void registerIndicator(ArkletBaseIndicator arkletBaseIndicator); +} diff --git a/arklet/arklet-core/src/main/java/com/alipay/sofa/serverless/arklet/core/health/HealthServiceImpl.java b/arklet/arklet-core/src/main/java/com/alipay/sofa/serverless/arklet/core/health/HealthServiceImpl.java new file mode 100644 index 000000000..ceee9a51c --- /dev/null +++ b/arklet/arklet-core/src/main/java/com/alipay/sofa/serverless/arklet/core/health/HealthServiceImpl.java @@ -0,0 +1,204 @@ +/* + * 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.serverless.arklet.core.health; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; +import com.alipay.sofa.ark.api.ArkClient; +import com.alipay.sofa.ark.common.util.AssertUtils; +import com.alipay.sofa.ark.common.util.StringUtils; +import com.alipay.sofa.ark.spi.model.Biz; +import com.alipay.sofa.common.utils.ArrayUtil; +import com.alipay.sofa.serverless.arklet.core.command.builtin.model.BizInfo; +import com.alipay.sofa.serverless.arklet.core.health.indicator.ArkletBaseIndicator; +import com.alipay.sofa.serverless.arklet.core.health.indicator.CpuIndicator; +import com.alipay.sofa.serverless.arklet.core.health.indicator.JvmIndicator; +import com.alipay.sofa.serverless.arklet.core.health.model.BizHealthMeta; +import com.alipay.sofa.serverless.arklet.core.health.model.Constants; +import com.alipay.sofa.serverless.arklet.core.health.model.Health; +import com.alipay.sofa.serverless.arklet.core.health.model.Health.HealthBuilder; +import com.alipay.sofa.serverless.arklet.core.health.model.PluginHealthMeta; +import com.alipay.sofa.serverless.arklet.core.command.builtin.model.PluginModel; +import com.alipay.sofa.serverless.arklet.core.common.log.ArkletLogger; +import com.alipay.sofa.serverless.arklet.core.common.log.ArkletLoggerFactory; +import com.google.inject.Singleton; + +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import static com.alibaba.fastjson.JSON.toJSONString; + +/** + * @author Lunarscave + */ +@Singleton +public class HealthServiceImpl implements HealthService { + + private static final ArkletLogger LOGGER = ArkletLoggerFactory + .getDefaultLogger(); + private final HealthBuilder healthBuilder = new HealthBuilder(); + + private final Map indicators = new ConcurrentHashMap<>(3); + + @Override + public void init() { + initIndicators(); + healthBuilder.init(); + } + + @Override + public void destroy() { + } + + @Override + public Health getHealth() { + HealthBuilder builder = new HealthBuilder(); + for (ArkletBaseIndicator indicator : this.indicators.values()) { + builder.putAllHealthData(indicator.getHealthModel(healthBuilder)); + } + return builder.build(); + } + + @Override + public Health getHealth(String indicatorId) { + try { + healthBuilder.init(); + AssertUtils.assertNotNull(indicators.get(indicatorId), "indicator not registered"); + healthBuilder.putAllHealthData(indicators.get(indicatorId) + .getHealthModel(healthBuilder)); + } catch (Throwable e) { + healthBuilder.putErrorData(Constants.HEALTH_ERROR, e.getMessage()); + } + return healthBuilder.build(); + } + + @Override + public Health getHealth(String[] indicatorIds) { + HealthBuilder builder = new HealthBuilder(); + if (ArrayUtil.isEmpty(indicatorIds)) { + builder.putAllHealthData(getHealth()); + } else { + for (String indicatorId : indicatorIds) { + builder.putAllHealthData(getHealth(indicatorId)); + } + } + return builder.build(); + } + + @Override + public Health queryModuleInfo() { + HealthBuilder builder = new HealthBuilder(); + return builder.init().putAllHealthData(queryMasterBiz()) + .putAllHealthData(queryModuleInfo(new BizInfo())) + .putAllHealthData(queryModuleInfo(new PluginModel())).build(); + } + + @Override + public Health queryModuleInfo(String type, String name, String version) { + HealthBuilder builder = new HealthBuilder(); + try { + AssertUtils.isTrue(StringUtils.isEmpty(type) || Constants.typeOfInfo(type), + "illegal type: %s", type); + if (StringUtils.isEmpty(type) || Constants.BIZ.equals(type)) { + BizInfo bizInfo = new BizInfo(); + bizInfo.setBizName(name); + bizInfo.setBizVersion(version); + builder.putAllHealthData(queryModuleInfo(bizInfo)); + } + if (StringUtils.isEmpty(type) || Constants.PLUGIN.equals(type)) { + PluginModel pluginModel = new PluginModel(); + pluginModel.setPluginName(name); + pluginModel.setPluginVersion(version); + builder.putAllHealthData(queryModuleInfo(pluginModel)); + } + } catch (Throwable e) { + builder.putErrorData(Constants.HEALTH_ERROR, e.getMessage()); + } + return builder.build(); + } + + @Override + public Health queryModuleInfo(BizInfo bizInfo) { + String bizName = bizInfo.getBizName(), bizVersion = bizInfo.getBizVersion(); + healthBuilder.init(); + try { + if (StringUtils.isEmpty(bizName) && StringUtils.isEmpty(bizVersion)) { + List bizHealthMetaList = BizHealthMeta.createBizMetaList(ArkClient + .getBizManagerService().getBizInOrder()); + healthBuilder.putHealthData(Constants.BIZ_LIST_INFO, bizHealthMetaList); + } else if (StringUtils.isEmpty(bizVersion)) { + List bizList = ArkClient.getBizManagerService().getBiz(bizName); + AssertUtils.isTrue(bizList.size() > 0, "can not find biz: %s", bizName); + List bizHealthMetaList = BizHealthMeta.createBizMetaList(bizList); + healthBuilder.putHealthData(Constants.BIZ_LIST_INFO, bizHealthMetaList); + } else { + BizHealthMeta bizHealthMeta = BizHealthMeta.createBizMeta(ArkClient + .getBizManagerService().getBiz(bizName, bizVersion)); + healthBuilder.putHealthData(Constants.BIZ_INFO, bizHealthMeta); + } + } catch (Throwable e) { + healthBuilder.putErrorData(Constants.HEALTH_ERROR, e.getMessage()); + } + return healthBuilder.build(); + } + + @Override + public Health queryModuleInfo(PluginModel pluginModel) { + String pluginName = pluginModel.getPluginName(); + healthBuilder.init(); + try { + if (StringUtils.isEmpty(pluginName)) { + List pluginHealthMetaList = PluginHealthMeta + .createPluginMetaList(ArkClient.getPluginManagerService().getPluginsInOrder()); + healthBuilder.putHealthData(Constants.PLUGIN_LIST_INFO, pluginHealthMetaList); + } else { + PluginHealthMeta pluginHealthMeta = PluginHealthMeta.createPluginMeta(ArkClient + .getPluginManagerService().getPluginByName(pluginName)); + healthBuilder.putHealthData(Constants.PLUGIN_INFO, pluginHealthMeta); + } + } catch (Throwable e) { + healthBuilder.putErrorData(Constants.HEALTH_ERROR, e.getMessage()); + } + return healthBuilder.build(); + } + + @Override + public Health queryMasterBiz() { + BizHealthMeta bizHealthMeta = BizHealthMeta.createBizMeta(ArkClient.getMasterBiz()); + return healthBuilder + .init() + .putHealthData(Constants.MASTER_BIZ_INFO, + JSON.parseObject(toJSONString(bizHealthMeta), JSONObject.class)).build(); + } + + @Override + public ArkletBaseIndicator getIndicator(String indicatorId) { + return indicators.get(indicatorId); + } + + @Override + public void registerIndicator(ArkletBaseIndicator indicator) { + this.indicators.put(indicator.getIndicatorId(), indicator); + LOGGER.info("register indicator " + indicator.getIndicatorId()); + } + + private void initIndicators() { + registerIndicator(new CpuIndicator()); + registerIndicator(new JvmIndicator()); + } +} diff --git a/arklet/arklet-core/src/main/java/com/alipay/sofa/serverless/arklet/core/health/indicator/ArkletBaseIndicator.java b/arklet/arklet-core/src/main/java/com/alipay/sofa/serverless/arklet/core/health/indicator/ArkletBaseIndicator.java new file mode 100644 index 000000000..c0deaad59 --- /dev/null +++ b/arklet/arklet-core/src/main/java/com/alipay/sofa/serverless/arklet/core/health/indicator/ArkletBaseIndicator.java @@ -0,0 +1,44 @@ +/* + * 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.serverless.arklet.core.health.indicator; + +import com.alipay.sofa.serverless.arklet.core.health.model.Health; +import com.alipay.sofa.serverless.arklet.core.health.model.Health.HealthBuilder; + +import java.util.Map; + +/** + * @author Lunarscave + */ +public abstract class ArkletBaseIndicator { + + private final String indicatorId; + + public ArkletBaseIndicator(String indicatorId) { + this.indicatorId = indicatorId; + } + + protected abstract Map getHealthDetails(); + + public String getIndicatorId() { + return indicatorId; + } + + public Health getHealthModel(HealthBuilder builder) { + return builder.init().putHealthData(getIndicatorId(), getHealthDetails()).build(); + } +} diff --git a/arklet/arklet-core/src/main/java/com/alipay/sofa/serverless/arklet/core/health/indicator/CpuIndicator.java b/arklet/arklet-core/src/main/java/com/alipay/sofa/serverless/arklet/core/health/indicator/CpuIndicator.java new file mode 100644 index 000000000..42949c489 --- /dev/null +++ b/arklet/arklet-core/src/main/java/com/alipay/sofa/serverless/arklet/core/health/indicator/CpuIndicator.java @@ -0,0 +1,136 @@ +/* + * 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.serverless.arklet.core.health.indicator; + +import com.alipay.sofa.serverless.arklet.core.health.model.Constants; +import oshi.SystemInfo; +import oshi.hardware.CentralProcessor; +import oshi.util.GlobalConfig; +import oshi.util.Util; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.Arrays; + +/** + * @author Lunarscave + */ +public class CpuIndicator extends ArkletBaseIndicator { + + private final CpuIndicatorHandler cpuIndicatorHandler; + + private static final String CPU_INDICATOR_ID = Constants.CPU; + + public CpuIndicator() { + super(CPU_INDICATOR_ID); + cpuIndicatorHandler = new CpuIndicatorHandler(); + } + + @Override + protected Map getHealthDetails() { + Map cpuHealthDetails = new HashMap<>(6); + + cpuIndicatorHandler.collectTicks(); + cpuHealthDetails.put(CpuMetrics.CPU_COUNT.getId(), cpuIndicatorHandler.getCpuCount()); + cpuHealthDetails.put(CpuMetrics.CPU_TYPE.getId(), cpuIndicatorHandler.getCpuType()); + cpuHealthDetails.put(CpuMetrics.CPU_TOTAL_USED.getId(), cpuIndicatorHandler.getTotalUsed()); + cpuHealthDetails.put(CpuMetrics.CPU_USER_USED.getId(), cpuIndicatorHandler.getUserUsed()); + cpuHealthDetails.put(CpuMetrics.CPU_SYSTEM_USED.getId(), + cpuIndicatorHandler.getSystemUsed()); + cpuHealthDetails.put(CpuMetrics.CPU_FREE.getId(), cpuIndicatorHandler.getFree()); + return cpuHealthDetails; + } + + static class CpuIndicatorHandler { + + private final CentralProcessor cpu; + + private long[] prevTicks; + + private long[] nextTicks; + + public CpuIndicatorHandler() { + this.cpu = new SystemInfo().getHardware().getProcessor(); + prevTicks = cpu.getSystemCpuLoadTicks(); + } + + public void collectTicks() { + nextTicks = cpu.getSystemCpuLoadTicks(); + } + + public double getTotalUsed() { + Set tickTypeSet = new HashSet( + Arrays.asList(CentralProcessor.TickType.class.getEnumConstants())); + double totalUsed = 0; + for (CentralProcessor.TickType tickType : tickTypeSet) { + totalUsed += nextTicks[tickType.getIndex()] - prevTicks[tickType.getIndex()]; + } + return totalUsed; + } + + public double getUserUsed() { + return getUsed(CentralProcessor.TickType.USER); + } + + public double getSystemUsed() { + return getUsed(CentralProcessor.TickType.SYSTEM); + } + + public double getFree() { + return getUsed(CentralProcessor.TickType.IDLE); + } + + public int getCpuCount() { + return cpu.getLogicalProcessorCount(); + } + + public String getCpuType() { + return cpu.getProcessorIdentifier().getName(); + } + + private double getUsed(CentralProcessor.TickType tickType) { + double totalUsed = getTotalUsed(); + double used = nextTicks[tickType.getIndex()] - prevTicks[tickType.getIndex()]; + if (totalUsed == 0 || used < 0) { + used = 0; + } else { + used = 100d * used / totalUsed; + } + return used; + } + } + + enum CpuMetrics { + + CPU_COUNT("count"), CPU_TYPE("type"), CPU_TOTAL_USED("total used (%)"), CPU_USER_USED( + "user used (%)"), CPU_SYSTEM_USED( + "system used (%)"), CPU_FREE( + "free (%)"); + + private final String id; + + CpuMetrics(String desc) { + this.id = desc; + } + + public String getId() { + return id; + }; + } +} diff --git a/arklet/arklet-core/src/main/java/com/alipay/sofa/serverless/arklet/core/health/indicator/JvmIndicator.java b/arklet/arklet-core/src/main/java/com/alipay/sofa/serverless/arklet/core/health/indicator/JvmIndicator.java new file mode 100644 index 000000000..3255219eb --- /dev/null +++ b/arklet/arklet-core/src/main/java/com/alipay/sofa/serverless/arklet/core/health/indicator/JvmIndicator.java @@ -0,0 +1,209 @@ +/* + * 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.serverless.arklet.core.health.indicator; + +import com.alipay.sofa.serverless.arklet.core.health.model.Constants; +import com.alipay.sofa.serverless.arklet.core.util.ConvertUtils; + +import java.lang.management.ClassLoadingMXBean; +import java.lang.management.ManagementFactory; +import java.lang.management.MemoryUsage; +import java.lang.management.RuntimeMXBean; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; +import java.util.Properties; + +/** + * @author Lunarscave + */ +public class JvmIndicator extends ArkletBaseIndicator { + + private final JvmIndicatorHandler jvmIndicatorHandler; + + private static final String JVM_INDICATOR_ID = Constants.JVM; + + public JvmIndicator() { + super(JVM_INDICATOR_ID); + jvmIndicatorHandler = new JvmIndicatorHandler(); + } + + @Override + protected Map getHealthDetails() { + Map jvmHealthDetails = new HashMap<>(6); + + jvmIndicatorHandler.updateHandler(); + jvmHealthDetails.put(JvmMetrics.JAVA_VERSION.getId(), jvmIndicatorHandler.getJvmVersion()); + jvmHealthDetails.put(JvmMetrics.JAVA_HOME.getId(), jvmIndicatorHandler.getJavaHome()); + jvmHealthDetails.put(JvmMetrics.JAVA_TOTAL_MEMORY.getId(), + ConvertUtils.bytes2Megabyte(jvmIndicatorHandler.getTotalMemory())); + jvmHealthDetails.put(JvmMetrics.JAVA_MAX_MEMORY.getId(), + ConvertUtils.bytes2Megabyte(jvmIndicatorHandler.getMaxMemory())); + jvmHealthDetails.put(JvmMetrics.JAVA_FREE_MEMORY.getId(), + ConvertUtils.bytes2Megabyte(jvmIndicatorHandler.getFreeMemory())); + jvmHealthDetails.put(JvmMetrics.JAVA_INIT_HEAP.getId(), + ConvertUtils.bytes2Megabyte(jvmIndicatorHandler.getInitHeapMemory())); + jvmHealthDetails.put(JvmMetrics.JAVA_USED_HEAP.getId(), + ConvertUtils.bytes2Megabyte(jvmIndicatorHandler.getUsedHeapMemory())); + jvmHealthDetails.put(JvmMetrics.JAVA_COMMITTED_HEAP.getId(), + ConvertUtils.bytes2Megabyte(jvmIndicatorHandler.getCommittedHeapMemory())); + jvmHealthDetails.put(JvmMetrics.JAVA_MAX_HEAP.getId(), + ConvertUtils.bytes2Megabyte(jvmIndicatorHandler.getMaxHeapMemory())); + jvmHealthDetails.put(JvmMetrics.JAVA_INIT_NON_HEAP.getId(), + ConvertUtils.bytes2Megabyte(jvmIndicatorHandler.getInitNonHeapMemory())); + jvmHealthDetails.put(JvmMetrics.JAVA_USED_NON_HEAP.getId(), + ConvertUtils.bytes2Megabyte(jvmIndicatorHandler.getUsedNonHeapMemory())); + jvmHealthDetails.put(JvmMetrics.JAVA_COMMITTED_NON_HEAP.getId(), + ConvertUtils.bytes2Megabyte(jvmIndicatorHandler.getCommittedNonHeapMemory())); + jvmHealthDetails.put(JvmMetrics.JAVA_MAX_NON_HEAP.getId(), + ConvertUtils.bytes2Megabyte(jvmIndicatorHandler.getMaxNonHeapMemory())); + jvmHealthDetails.put(JvmMetrics.JAVA_LOADED_CLASS_COUNT.getId(), + jvmIndicatorHandler.getLoadedClassCount()); + jvmHealthDetails.put(JvmMetrics.JAVA_UNLOAD_CLASS_COUNT.getId(), + jvmIndicatorHandler.getUnloadClassCount()); + jvmHealthDetails.put(JvmMetrics.JAVA_TOTAL_CLASS_COUNT.getId(), + jvmIndicatorHandler.getTotalClassCount()); + jvmHealthDetails.put(JvmMetrics.JAVA_RUN_TIMES.getId(), jvmIndicatorHandler.getDuration()); + return jvmHealthDetails; + } + + static class JvmIndicatorHandler { + + private Properties properties; + + private Runtime runtime; + + private RuntimeMXBean runtimeMxBean; + + private MemoryUsage heapMemoryUsed; + + private MemoryUsage nonHeapMemoryUsed; + + private ClassLoadingMXBean classLoadingMxBean; + + public JvmIndicatorHandler() { + updateHandler(); + } + + public void updateHandler() { + this.properties = System.getProperties(); + this.runtime = Runtime.getRuntime(); + this.runtimeMxBean = ManagementFactory.getRuntimeMXBean(); + this.heapMemoryUsed = ManagementFactory.getMemoryMXBean().getHeapMemoryUsage(); + this.nonHeapMemoryUsed = ManagementFactory.getMemoryMXBean().getNonHeapMemoryUsage(); + this.classLoadingMxBean = ManagementFactory.getClassLoadingMXBean(); + } + + public String getJvmVersion() { + return this.properties.getProperty("java.version"); + } + + public String getJavaHome() { + return this.properties.getProperty("java.home"); + } + + public long getTotalMemory() { + return this.runtime.totalMemory(); + } + + public long getMaxMemory() { + return this.runtime.maxMemory(); + } + + public long getFreeMemory() { + return this.runtime.freeMemory(); + } + + public double getDuration() { + return ConvertUtils.millisecond2Second(new Date(runtimeMxBean.getStartTime())); + } + + public long getInitHeapMemory() { + return this.heapMemoryUsed.getInit(); + } + + public long getUsedHeapMemory() { + return this.heapMemoryUsed.getUsed(); + } + + public long getCommittedHeapMemory() { + return this.heapMemoryUsed.getCommitted(); + } + + public long getMaxHeapMemory() { + return this.heapMemoryUsed.getMax(); + } + + public long getInitNonHeapMemory() { + return this.nonHeapMemoryUsed.getInit(); + } + + public long getUsedNonHeapMemory() { + return this.nonHeapMemoryUsed.getUsed(); + } + + public long getCommittedNonHeapMemory() { + return this.nonHeapMemoryUsed.getCommitted(); + } + + public long getMaxNonHeapMemory() { + return this.nonHeapMemoryUsed.getMax(); + } + + public long getLoadedClassCount() { + return this.classLoadingMxBean.getLoadedClassCount(); + } + + public long getUnloadClassCount() { + return this.classLoadingMxBean.getUnloadedClassCount(); + } + + public long getTotalClassCount() { + return this.classLoadingMxBean.getTotalLoadedClassCount(); + } + + } + + public enum JvmMetrics { + + JAVA_VERSION("java version"), JAVA_HOME("java home"), JAVA_TOTAL_MEMORY("total memory(M)"), JAVA_MAX_MEMORY( + "max memory(M)"), JAVA_FREE_MEMORY( + "free memory(M)"), JAVA_RUN_TIMES( + "run time(s)"), JAVA_INIT_HEAP( + "init heap memory(M)"), JAVA_USED_HEAP( + "used heap memory(M)"), JAVA_COMMITTED_HEAP( + "committed heap memory(M)"), JAVA_MAX_HEAP( + "max heap memory(M)"), JAVA_INIT_NON_HEAP( + "init non heap memory(M)"), JAVA_USED_NON_HEAP( + "used non heap memory(M)"), JAVA_COMMITTED_NON_HEAP( + "committed non heap memory(M)"), JAVA_MAX_NON_HEAP( + "max non heap memory(M)"), JAVA_LOADED_CLASS_COUNT( + "loaded class count"), JAVA_UNLOAD_CLASS_COUNT( + "unload class count"), JAVA_TOTAL_CLASS_COUNT( + "total class count"); + + private final String id; + + JvmMetrics(String desc) { + this.id = desc; + } + + public String getId() { + return id; + }; + } +} diff --git a/arklet/arklet-core/src/main/java/com/alipay/sofa/serverless/arklet/core/health/model/BizHealthMeta.java b/arklet/arklet-core/src/main/java/com/alipay/sofa/serverless/arklet/core/health/model/BizHealthMeta.java new file mode 100644 index 000000000..3e5214e27 --- /dev/null +++ b/arklet/arklet-core/src/main/java/com/alipay/sofa/serverless/arklet/core/health/model/BizHealthMeta.java @@ -0,0 +1,95 @@ +/* + * 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.serverless.arklet.core.health.model; + +import com.alipay.sofa.ark.spi.model.Biz; +import com.alipay.sofa.ark.spi.model.BizState; +import com.alipay.sofa.serverless.arklet.core.util.AssertUtils; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author Lunarscave + */ +public class BizHealthMeta { + + private String bizName; + + private String bizVersion; + + private BizState bizState; + + private String webContextPath; + + public String getBizName() { + return bizName; + } + + public void setBizName(String bizName) { + this.bizName = bizName; + } + + public String getBizVersion() { + return bizVersion; + } + + public void setBizVersion(String bizVersion) { + this.bizVersion = bizVersion; + } + + public BizState getBizState() { + return bizState; + } + + public void setBizState(BizState bizState) { + this.bizState = bizState; + } + + public String getWebContextPath() { + return webContextPath; + } + + public void setWebContextPath(String webContextPath) { + this.webContextPath = webContextPath; + } + + public static BizHealthMeta createBizMeta(Biz biz) { + AssertUtils.assertNotNull(biz, "can not find biz"); + BizHealthMeta bizHealthMeta = createBizMeta(biz.getBizName(), biz.getBizVersion()); + bizHealthMeta.bizState = biz.getBizState(); + bizHealthMeta.webContextPath = biz.getWebContextPath(); + return bizHealthMeta; + } + + public static BizHealthMeta createBizMeta(String bizName, String bizVersion) { + BizHealthMeta bizHealthMeta = new BizHealthMeta(); + bizHealthMeta.bizName = bizName; + bizHealthMeta.bizVersion = bizVersion; + return bizHealthMeta; + } + + public static List createBizMetaList(List bizList) { + AssertUtils.isTrue(bizList.size() > 0, "no biz found"); + List bizHealthMetaList = new ArrayList<>(); + for (Biz biz : bizList) { + bizHealthMetaList.add(createBizMeta(biz)); + } + return bizHealthMetaList; + } + +} diff --git a/arklet/arklet-core/src/main/java/com/alipay/sofa/serverless/arklet/core/health/model/Constants.java b/arklet/arklet-core/src/main/java/com/alipay/sofa/serverless/arklet/core/health/model/Constants.java new file mode 100644 index 000000000..4566601f1 --- /dev/null +++ b/arklet/arklet-core/src/main/java/com/alipay/sofa/serverless/arklet/core/health/model/Constants.java @@ -0,0 +1,59 @@ +/* + * 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.serverless.arklet.core.health.model; + +/** + * @author Lunarscave + */ +public class Constants { + + public static final String SYSTEM = "system"; + + public static final String BIZ = "biz"; + + public static final String PLUGIN = "plugin"; + + public static final String CPU = "cpu"; + + public static final String JVM = "jvm"; + + public static final String MASTER_BIZ_HEALTH = "masterBizHealth"; + + public static final String MASTER_BIZ_INFO = "masterBizInfo"; + + public static final String HEALTH_ERROR = "error"; + + public static final String HEALTH_ENDPOINT_ERROR = "endpointError"; + + public static final String BIZ_INFO = "bizInfo"; + + public static final String BIZ_LIST_INFO = "bizListInfo"; + + public static final String PLUGIN_INFO = "pluginInfo"; + + public static final String PLUGIN_LIST_INFO = "pluginListInfo"; + + public static final String READINESS_HEALTHY = "ACCEPTING_TRAFFIC"; + + public static boolean typeOfQuery(String type) { + return SYSTEM.equals(type) || BIZ.equals(type) || PLUGIN.equals(type); + } + + public static boolean typeOfInfo(String type) { + return Constants.BIZ.equals(type) || Constants.PLUGIN.equals(type); + } +} diff --git a/arklet/arklet-core/src/main/java/com/alipay/sofa/serverless/arklet/core/health/model/Health.java b/arklet/arklet-core/src/main/java/com/alipay/sofa/serverless/arklet/core/health/model/Health.java new file mode 100644 index 000000000..f7e086e7a --- /dev/null +++ b/arklet/arklet-core/src/main/java/com/alipay/sofa/serverless/arklet/core/health/model/Health.java @@ -0,0 +1,110 @@ +/* + * 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.serverless.arklet.core.health.model; + +import com.alipay.sofa.serverless.arklet.core.util.AssertUtils; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +/** + * @author Lunarscave + */ +public class Health { + + private final Map healthData; + + public Health(Map healthData) { + AssertUtils.assertNotNull(healthData, "health data must not null"); + this.healthData = healthData; + } + + public Health(HealthBuilder builder) { + this.healthData = Collections.unmodifiableMap(builder.healthData); + } + + public Map getHealthData() { + return healthData; + } + + public void setHealthData(Map healthData) { + AssertUtils.assertNotNull(healthData, "health data must not null"); + this.healthData.clear(); + this.healthData.putAll(healthData); + } + + public boolean containsError(String errorCode) { + return this.healthData.containsKey(errorCode); + } + + public boolean containsUnhealthy(String healthyCode) { + Map healthData = this.getHealthData(); + boolean isUnhealthy = false; + for (String key : healthData.keySet()) { + Object value = healthData.get(key); + if (value instanceof Map) { + if (((Map) value).containsKey("masterBizHealth")) { + Object health = ((Map) value).get("masterBizHealth"); + if (health instanceof Map) { + String code = (String) ((Map) health).get("readinessState"); + if (!code.equals(healthyCode)) { + isUnhealthy = true; + break; + } + } + } + } + } + return isUnhealthy; + } + + public static class HealthBuilder { + private final Map healthData = new HashMap<>(); + + public Health build() { + return new Health(this); + } + + public HealthBuilder init() { + this.healthData.clear(); + return this; + } + + public HealthBuilder putAllHealthData(Health healthData) { + this.healthData.putAll(healthData.getHealthData()); + return this; + } + + public HealthBuilder putHealthData(String key, Object value) { + this.healthData.put(key, value); + return this; + } + + public HealthBuilder putErrorData(String errorCode, String message) { + this.healthData.clear(); + this.healthData.put(errorCode, message); + return this; + } + + public HealthBuilder putAllHealthData(Map healthData) { + AssertUtils.assertNotNull(healthData, "health data must not null"); + this.healthData.putAll(healthData); + return this; + } + } +} diff --git a/arklet/arklet-core/src/main/java/com/alipay/sofa/serverless/arklet/core/health/model/PluginHealthMeta.java b/arklet/arklet-core/src/main/java/com/alipay/sofa/serverless/arklet/core/health/model/PluginHealthMeta.java new file mode 100644 index 000000000..a9beca96b --- /dev/null +++ b/arklet/arklet-core/src/main/java/com/alipay/sofa/serverless/arklet/core/health/model/PluginHealthMeta.java @@ -0,0 +1,115 @@ +/* + * 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.serverless.arklet.core.health.model; + +import com.alipay.sofa.ark.spi.model.Plugin; +import com.alipay.sofa.serverless.arklet.core.util.AssertUtils; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author Lunarscave + */ +public class PluginHealthMeta { + + private String pluginName; + + private String groupId; + + private String artifactId; + + private String pluginVersion; + + private String pluginUrl; + + private String pluginActivator; + + public String getPluginName() { + return pluginName; + } + + public String getGroupId() { + return groupId; + } + + public String getArtifactId() { + return artifactId; + } + + public String getPluginVersion() { + return pluginVersion; + } + + public String getPluginActivator() { + return pluginActivator; + } + + public String getPluginUrl() { + return pluginUrl; + } + + public void setPluginName(String pluginName) { + this.pluginName = pluginName; + } + + public void setGroupId(String groupId) { + this.groupId = groupId; + } + + public void setArtifactId(String artifactId) { + this.artifactId = artifactId; + } + + public void setPluginVersion(String pluginVersion) { + this.pluginVersion = pluginVersion; + } + + public void setPluginUrl(String pluginUrl) { + this.pluginUrl = pluginUrl; + } + + public void setPluginActivator(String pluginActivator) { + this.pluginActivator = pluginActivator; + } + + public static PluginHealthMeta createPluginMeta(Plugin plugin) { + AssertUtils.assertNotNull(plugin, "can not find plugin"); + PluginHealthMeta pluginHealthMeta = PluginHealthMeta.createPluginMeta( + plugin.getPluginName(), plugin.getVersion()); + pluginHealthMeta.setGroupId(plugin.getGroupId()); + pluginHealthMeta.setArtifactId(plugin.getArtifactId()); + pluginHealthMeta.setPluginActivator(plugin.getPluginActivator()); + pluginHealthMeta.setPluginUrl(plugin.getPluginURL().getPath()); + return pluginHealthMeta; + } + + public static PluginHealthMeta createPluginMeta(String pluginName, String pluginVersion) { + PluginHealthMeta pluginHealthMeta = new PluginHealthMeta(); + pluginHealthMeta.setPluginName(pluginName); + pluginHealthMeta.setPluginVersion(pluginVersion); + return pluginHealthMeta; + } + + public static List createPluginMetaList(List pluginList) { + List pluginHealthMetaList = new ArrayList<>(); + for (Plugin plugin : pluginList) { + pluginHealthMetaList.add(createPluginMeta(plugin)); + } + return pluginHealthMetaList; + } +} diff --git a/arklet/arklet-core/src/main/java/com/alipay/sofa/serverless/arklet/core/ops/UnifiedOperationService.java b/arklet/arklet-core/src/main/java/com/alipay/sofa/serverless/arklet/core/ops/UnifiedOperationService.java index 15692aae4..167755e1f 100644 --- a/arklet/arklet-core/src/main/java/com/alipay/sofa/serverless/arklet/core/ops/UnifiedOperationService.java +++ b/arklet/arklet-core/src/main/java/com/alipay/sofa/serverless/arklet/core/ops/UnifiedOperationService.java @@ -19,22 +19,46 @@ import java.util.List; import com.alipay.sofa.ark.api.ClientResponse; -import com.alipay.sofa.ark.api.ResponseCode; import com.alipay.sofa.ark.spi.model.Biz; import com.alipay.sofa.serverless.arklet.core.ArkletComponent; /** + * Unified operation service interface, mainly interacts with the sofa-ark container * @author mingmen * @date 2023/6/14 */ public interface UnifiedOperationService extends ArkletComponent { - ClientResponse install(String bizPath) throws Throwable; + /** + * install biz + * @param bizUrl biz URL + * @return response + * @throws Throwable error + */ + ClientResponse install(String bizUrl) throws Throwable; + /** + * uninstall biz + * @param bizName bizName + * @param bizVersion bizVersion + * @return response + * @throws Throwable error + */ ClientResponse uninstall(String bizName, String bizVersion) throws Throwable; + /** + * query biz list + * @return biz list + */ List queryBizList(); + /** + * switch biz + * @param bizName bizName + * @param bizVersion bizVersion + * @return response + * @throws Throwable error + */ ClientResponse switchBiz(String bizName, String bizVersion) throws Throwable; } diff --git a/arklet/arklet-core/src/main/java/com/alipay/sofa/serverless/arklet/core/ops/UnifiedOperationServiceImpl.java b/arklet/arklet-core/src/main/java/com/alipay/sofa/serverless/arklet/core/ops/UnifiedOperationServiceImpl.java index 5ccf3100c..bce808af4 100644 --- a/arklet/arklet-core/src/main/java/com/alipay/sofa/serverless/arklet/core/ops/UnifiedOperationServiceImpl.java +++ b/arklet/arklet-core/src/main/java/com/alipay/sofa/serverless/arklet/core/ops/UnifiedOperationServiceImpl.java @@ -20,7 +20,6 @@ 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.spi.constant.Constants; import com.alipay.sofa.ark.spi.model.Biz; import com.alipay.sofa.ark.spi.model.BizOperation; @@ -43,10 +42,11 @@ public void destroy() { } - public ClientResponse install(String bizPath) throws Throwable { + @Override + public ClientResponse install(String bizUrl) throws Throwable { BizOperation bizOperation = new BizOperation() .setOperationType(BizOperation.OperationType.INSTALL); - bizOperation.putParameter(Constants.CONFIG_BIZ_URL, "file://" + bizPath); + bizOperation.putParameter(Constants.CONFIG_BIZ_URL, bizUrl); return ArkClient.installOperation(bizOperation); } diff --git a/arklet/arklet-core/src/main/java/com/alipay/sofa/serverless/arklet/core/command/coordinate/CoordinatorConfig.java b/arklet/arklet-core/src/main/java/com/alipay/sofa/serverless/arklet/core/util/ConvertUtils.java similarity index 58% rename from arklet/arklet-core/src/main/java/com/alipay/sofa/serverless/arklet/core/command/coordinate/CoordinatorConfig.java rename to arklet/arklet-core/src/main/java/com/alipay/sofa/serverless/arklet/core/util/ConvertUtils.java index adbbe786c..7129449c9 100644 --- a/arklet/arklet-core/src/main/java/com/alipay/sofa/serverless/arklet/core/command/coordinate/CoordinatorConfig.java +++ b/arklet/arklet-core/src/main/java/com/alipay/sofa/serverless/arklet/core/util/ConvertUtils.java @@ -14,26 +14,27 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.alipay.sofa.serverless.arklet.core.command.coordinate; +package com.alipay.sofa.serverless.arklet.core.util; -import java.util.ArrayList; -import java.util.List; - -import com.alipay.sofa.serverless.arklet.core.command.meta.Command; +import java.text.SimpleDateFormat; +import java.util.Date; /** - * @author mingmen - * @date 2023/6/14 + * @author Lunarscave */ -public class CoordinatorConfig { - private List> mutexCmds = new ArrayList<>(); +public class ConvertUtils { + + public static double bytes2Megabyte(Long bytes) { + return ((double) bytes) / 1024 / 1024; + } - public List> getMutexCmds() { - return mutexCmds; + public static String endDate2Duration(Date date) { + long duration = System.currentTimeMillis() - date.getTime(); + return new SimpleDateFormat("HH-mm-ss").format(duration); } - public CoordinatorConfig addMutex(List commands) { - mutexCmds.add(commands); - return this; + public static double millisecond2Second(Date date) { + return ((double) System.currentTimeMillis() - date.getTime()) / 1000; } + } diff --git a/arklet/arklet-core/src/test/java/com/alipay/sofa/serverless/arklet/core/BaseTest.java b/arklet/arklet-core/src/test/java/com/alipay/sofa/serverless/arklet/core/BaseTest.java new file mode 100644 index 000000000..b15cd13eb --- /dev/null +++ b/arklet/arklet-core/src/test/java/com/alipay/sofa/serverless/arklet/core/BaseTest.java @@ -0,0 +1,46 @@ +/* + * 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.serverless.arklet.core; + +import com.alipay.sofa.serverless.arklet.core.command.CommandService; +import com.alipay.sofa.serverless.arklet.core.health.HealthService; +import com.alipay.sofa.serverless.arklet.core.health.model.Health; +import com.alipay.sofa.serverless.arklet.core.ops.UnifiedOperationService; +import org.junit.Before; + +/** + * @author mingmen + * @date 2023/9/5 + */ +public class BaseTest { + public static ArkletComponentRegistry componentRegistry; + public static CommandService commandService; + public static UnifiedOperationService operationService; + public static HealthService healthService; + + @Before + public void setup() { + if (componentRegistry == null) { + ArkletComponentRegistry registry = new ArkletComponentRegistry(); + registry.initComponents(); + componentRegistry = registry; + commandService = ArkletComponentRegistry.getCommandServiceInstance(); + operationService = ArkletComponentRegistry.getOperationServiceInstance(); + healthService = ArkletComponentRegistry.getHealthServiceInstance(); + } + } +} diff --git a/arklet/arklet-core/src/test/java/com/alipay/sofa/serverless/arklet/core/command/BizOpsCommandCoordinatorTests.java b/arklet/arklet-core/src/test/java/com/alipay/sofa/serverless/arklet/core/command/BizOpsCommandCoordinatorTests.java new file mode 100644 index 000000000..83ad38c9f --- /dev/null +++ b/arklet/arklet-core/src/test/java/com/alipay/sofa/serverless/arklet/core/command/BizOpsCommandCoordinatorTests.java @@ -0,0 +1,123 @@ +/* + * 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.serverless.arklet.core.command; + +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; + +import com.alipay.sofa.serverless.arklet.core.command.builtin.BuiltinCommand; +import com.alipay.sofa.serverless.arklet.core.command.coordinate.BizOpsCommandCoordinator; +import com.alipay.sofa.serverless.arklet.core.command.meta.Command; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.mockito.MockitoAnnotations; + +public class BizOpsCommandCoordinatorTests { + + private final BuiltinCommand command = BuiltinCommand.INSTALL_BIZ; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + BizOpsCommandCoordinator.clear(); + } + + /** + * 测试putBizExecution方法,验证当bizIdentityLockMap中已存在相同的identity时,会检查失败。 + */ + @Test + public void testPutBizExecutionWithExistingIdentity() { + String bizName = "biz"; + String bizVersion = "1.0"; + BizOpsCommandCoordinator.checkAndLock(bizName, bizVersion, command); + Assert.assertFalse(BizOpsCommandCoordinator.checkAndLock(bizName, bizVersion, command)); + } + + /** + * 测试putBizExecution方法,验证当bizIdentityLockMap中不存在相同的identity时,会成功将command放入bizIdentityLockMap中。 + */ + @Test + public void testPutBizExecutionWithNonExistingIdentity() { + String bizName = "biz"; + String bizVersionV1 = "1.0"; + String bizVersionV2 = "2.0"; + BizOpsCommandCoordinator.checkAndLock(bizName, bizVersionV1, command); + BizOpsCommandCoordinator.checkAndLock(bizName, bizVersionV2, command); + Assert.assertTrue(BizOpsCommandCoordinator.existBizProcessing(bizName, bizVersionV1)); + Assert.assertTrue(BizOpsCommandCoordinator.existBizProcessing(bizName, bizVersionV2)); + } + + /** + * 测试popBizExecution方法,验证当bizIdentityLockMap中存在相同的identity时,会成功将该identity从bizIdentityLockMap中移除。 + */ + @Test + public void testPopBizExecutionWithExistingIdentity() { + String bizName = "biz"; + String bizVersion = "1.0"; + BizOpsCommandCoordinator.checkAndLock(bizName, bizVersion, command); + BizOpsCommandCoordinator.unlock(bizName, bizVersion); + Assert.assertFalse(BizOpsCommandCoordinator.existBizProcessing(bizName, bizVersion)); + } + + /** + * 测试popBizExecution方法,验证当bizIdentityLockMap中不存在相同的identity时,不会有任何影响。 + */ + @Test + public void testPopBizExecutionWithNonExistingIdentity() { + String bizName = "biz"; + String bizVersion = "1.0"; + BizOpsCommandCoordinator.unlock(bizName, bizVersion); + } + + /** + * 测试getCurrentProcessingCommand方法,验证当bizIdentityLockMap中存在相同的identity时,返回对应的command。 + */ + @Test + public void testGetCurrentProcessingCommandWithExistingIdentity() { + String bizName = "biz"; + String bizVersion = "1.0"; + BizOpsCommandCoordinator.checkAndLock(bizName, bizVersion, command); + Command result = BizOpsCommandCoordinator.getCurrentProcessingCommand(bizName, bizVersion); + Assert.assertEquals(command, result); + } + + /** + * 验证在高并发场景下,`checkAndLock`方法是否能够正常执行,不会出现锁异常 + */ + @Test + public void testCheckAndLock_Concurrency() throws InterruptedException { + // 创建并发线程池 + ExecutorService executor = Executors.newFixedThreadPool(10); + + // 模拟高并发场景,同时调用putBizExecution方法 + for (int i = 0; i < 1000; i++) { + executor.execute(() -> { + try { + BizOpsCommandCoordinator.checkAndLock("bizName", "bizVersion", command); + } finally { + BizOpsCommandCoordinator.unlock("bizName", "bizVersion"); + } + }); + } + executor.shutdown(); + executor.awaitTermination(10, TimeUnit.SECONDS); + + Assert.assertFalse("存在锁异常", BizOpsCommandCoordinator.existBizProcessing("bizName", "bizVersion")); + } +} diff --git a/arklet/arklet-core/src/test/java/com/alipay/sofa/serverless/arklet/core/command/CommandTests.java b/arklet/arklet-core/src/test/java/com/alipay/sofa/serverless/arklet/core/command/CommandTests.java new file mode 100644 index 000000000..452294aea --- /dev/null +++ b/arklet/arklet-core/src/test/java/com/alipay/sofa/serverless/arklet/core/command/CommandTests.java @@ -0,0 +1,78 @@ +/* + * 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.serverless.arklet.core.command; + +import java.util.HashMap; +import java.util.Map; + +import com.alibaba.fastjson.JSONObject; + +import com.alipay.sofa.serverless.arklet.core.BaseTest; +import com.alipay.sofa.serverless.arklet.core.command.builtin.BuiltinCommand; +import com.alipay.sofa.serverless.arklet.core.command.builtin.handler.InstallBizHandler; +import com.alipay.sofa.serverless.arklet.core.command.builtin.handler.QueryBizOpsHandler; +import com.alipay.sofa.serverless.arklet.core.command.custom.CustomCommand; +import com.alipay.sofa.serverless.arklet.core.command.custom.CustomCommandHandler; +import com.alipay.sofa.serverless.arklet.core.command.meta.Output; +import com.alipay.sofa.serverless.arklet.core.command.record.ProcessRecord; +import org.junit.Assert; +import org.junit.Test; + +/** + * @author mingmen + * @date 2023/6/26 + */ +public class CommandTests extends BaseTest { + + @Test + public void registerCustomCommand() { + commandService.registerCommandHandler(new CustomCommandHandler()); + CustomCommandHandler handler = (CustomCommandHandler) commandService + .getHandler(CustomCommand.HELLO); + Assert.assertNotNull(handler); + } + + @Test + public void commandProcess() throws Exception { + Output output = commandService.process(BuiltinCommand.HELP.getId(), new HashMap()); + Assert.assertNotNull(output); + } + + @Test + public void testInstallHandler() throws InterruptedException { + String rid = "testRequestId"; + + InstallBizHandler.Input input = new InstallBizHandler.Input(); + input.setBizName("testBizName"); + input.setBizVersion("testBizVersion"); + input.setAsync(true); + input.setRequestId(rid); + input.setBizUrl("testBizUrl"); + Map map = JSONObject.parseObject(JSONObject.toJSONString(input), Map.class); + Output output = commandService.process(BuiltinCommand.INSTALL_BIZ.getId(), map); + Assert.assertNotNull(output); + ProcessRecord processRecord = (ProcessRecord) output.getData(); + Assert.assertNotNull(processRecord); + + QueryBizOpsHandler.Input input1 = new QueryBizOpsHandler.Input(); + input1.setRequestId(rid); + Map map1 = JSONObject.parseObject(JSONObject.toJSONString(input1), Map.class); + Output output1 = commandService.process(BuiltinCommand.QUERY_BIZ_OPS.getId(), map1); + Assert.assertNotNull(output1); + } + +} diff --git a/arklet/arklet-core/src/test/java/com/alipay/sofa/serverless/arklet/core/command/HelpHandlerTests.java b/arklet/arklet-core/src/test/java/com/alipay/sofa/serverless/arklet/core/command/HelpHandlerTests.java new file mode 100644 index 000000000..aed4e55f0 --- /dev/null +++ b/arklet/arklet-core/src/test/java/com/alipay/sofa/serverless/arklet/core/command/HelpHandlerTests.java @@ -0,0 +1,76 @@ +/* + * 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.serverless.arklet.core.command; + +import java.util.List; + +import com.alipay.sofa.serverless.arklet.core.BaseTest; +import com.alipay.sofa.serverless.arklet.core.command.builtin.BuiltinCommand; +import com.alipay.sofa.serverless.arklet.core.command.builtin.handler.HelpHandler; +import com.alipay.sofa.serverless.arklet.core.command.builtin.model.CommandModel; +import com.alipay.sofa.serverless.arklet.core.command.meta.Command; +import com.alipay.sofa.serverless.arklet.core.command.meta.InputMeta; +import com.alipay.sofa.serverless.arklet.core.command.meta.Output; +import com.alipay.sofa.serverless.arklet.core.common.exception.CommandValidationException; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.mockito.MockitoAnnotations; + +/** + * @author mingmen + * @date 2023/9/6 + */ +public class HelpHandlerTests extends BaseTest { + + private HelpHandler helpHandler; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + helpHandler = (HelpHandler) commandService.getHandler(BuiltinCommand.HELP); + } + + @Test + public void testHandle() { + Output> result = helpHandler.handle(new InputMeta()); + Assert.assertTrue(result.getData() != null && !result.getData().isEmpty()); + } + + @Test + public void testCommand() { + // Act + Command result = helpHandler.command(); + + // Assert + assert result == BuiltinCommand.HELP; + } + + @Test + public void testValidate() { + // Arrange + InputMeta input = new InputMeta(); + + // Act + try { + helpHandler.validate(input); + } catch (CommandValidationException e) { + assert false; + } + } + +} diff --git a/arklet/arklet-core/src/test/java/com/alipay/sofa/serverless/arklet/core/command/InstallBizHandlerTests.java b/arklet/arklet-core/src/test/java/com/alipay/sofa/serverless/arklet/core/command/InstallBizHandlerTests.java new file mode 100644 index 000000000..2078acfc6 --- /dev/null +++ b/arklet/arklet-core/src/test/java/com/alipay/sofa/serverless/arklet/core/command/InstallBizHandlerTests.java @@ -0,0 +1,140 @@ +/* + * 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.serverless.arklet.core.command; + +import com.alipay.sofa.ark.api.ClientResponse; +import com.alipay.sofa.ark.api.ResponseCode; +import com.alipay.sofa.ark.exception.ArkRuntimeException; +import com.alipay.sofa.serverless.arklet.core.BaseTest; +import com.alipay.sofa.serverless.arklet.core.command.builtin.BuiltinCommand; +import com.alipay.sofa.serverless.arklet.core.command.builtin.handler.InstallBizHandler; +import com.alipay.sofa.serverless.arklet.core.command.builtin.handler.InstallBizHandler.Input; +import com.alipay.sofa.serverless.arklet.core.command.meta.Output; +import com.alipay.sofa.serverless.arklet.core.common.exception.ArkletRuntimeException; +import com.alipay.sofa.serverless.arklet.core.common.exception.CommandValidationException; +import org.junit.Assert; +import org.junit.Test; + +import static org.mockito.Mockito.when; + +/** + * @author mingmen + * @date 2023/9/5 + */ +public class InstallBizHandlerTests extends BaseTest { + + //@Test + //public void testHandle_Success() throws Throwable { + // + // InstallBizHandler handler = (InstallBizHandler)commandService.getHandler(BuiltinCommand.INSTALL_BIZ); + // + // // 准备测试数据 + // Input input = new Input(); + // input.setBizUrl("testUrl"); + // + // ClientResponse response = new ClientResponse(); + // response.setCode(ResponseCode.SUCCESS); + // + // // 设置Mock行为 + // when(operationService.install(anyString())).thenReturn(response); + // + // // 执行测试 + // Output result = handler.handle(input); + // + // // 验证结果 + // assertSame(response, result.getData()); + // assertTrue(result.success()); + //} + + // @Test(expected = ArkRuntimeException.class) + // public void testHandle_Failure() throws Throwable { + // + // InstallBizHandler handler = (InstallBizHandler) commandService + // .getHandler(BuiltinCommand.INSTALL_BIZ); + // + // // 准备测试数据 + // Input input = new Input(); + // input.setBizUrl("testUrl"); + // + // ClientResponse response = new ClientResponse(); + // response.setCode(ResponseCode.FAILED); + // + // // 设置Mock行为 + // when(operationService.install(input.getBizUrl())).thenReturn(response); + // + // // 执行测试 + // Output result = handler.handle(input); + // + // // 验证结果 + // Assert.assertSame(response, result.getData()); + // Assert.assertTrue(result.failed()); + // } + + @Test(expected = CommandValidationException.class) + public void testValidate_BlankBizName() throws CommandValidationException { + + InstallBizHandler handler = (InstallBizHandler) commandService + .getHandler(BuiltinCommand.INSTALL_BIZ); + // 准备测试数据 + Input input = new Input(); + input.setBizName(""); + + // 执行测试 + handler.validate(input); + } + + @Test(expected = CommandValidationException.class) + public void testValidate_BlankBizVersion() throws CommandValidationException { + + InstallBizHandler handler = (InstallBizHandler) commandService + .getHandler(BuiltinCommand.INSTALL_BIZ); + // 准备测试数据 + Input input = new Input(); + input.setBizVersion(""); + + // 执行测试 + handler.validate(input); + } + + @Test(expected = CommandValidationException.class) + public void testValidate_BlankRequestId() throws CommandValidationException { + + InstallBizHandler handler = (InstallBizHandler) commandService + .getHandler(BuiltinCommand.INSTALL_BIZ); + + // 准备测试数据 + Input input = new Input(); + input.setAsync(true); + input.setRequestId(""); + + // 执行测试 + handler.validate(input); + } + + @Test(expected = CommandValidationException.class) + public void testValidate_BlankBizUrl() throws CommandValidationException { + InstallBizHandler handler = (InstallBizHandler) commandService + .getHandler(BuiltinCommand.INSTALL_BIZ); + + // 准备测试数据 + Input input = new Input(); + input.setBizUrl(""); + + // 执行测试 + handler.validate(input); + } +} diff --git a/arklet/arklet-core/src/test/java/com/alipay/sofa/serverless/arklet/core/command/CustomCommand.java b/arklet/arklet-core/src/test/java/com/alipay/sofa/serverless/arklet/core/command/custom/CustomCommand.java similarity index 95% rename from arklet/arklet-core/src/test/java/com/alipay/sofa/serverless/arklet/core/command/CustomCommand.java rename to arklet/arklet-core/src/test/java/com/alipay/sofa/serverless/arklet/core/command/custom/CustomCommand.java index 0b492d74b..6bf58e25d 100644 --- a/arklet/arklet-core/src/test/java/com/alipay/sofa/serverless/arklet/core/command/CustomCommand.java +++ b/arklet/arklet-core/src/test/java/com/alipay/sofa/serverless/arklet/core/command/custom/CustomCommand.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.alipay.sofa.serverless.arklet.core.command; +package com.alipay.sofa.serverless.arklet.core.command.custom; import com.alipay.sofa.serverless.arklet.core.command.meta.Command; diff --git a/arklet/arklet-core/src/test/java/com/alipay/sofa/serverless/arklet/core/command/CustomCommandHandler.java b/arklet/arklet-core/src/test/java/com/alipay/sofa/serverless/arklet/core/command/custom/CustomCommandHandler.java similarity index 96% rename from arklet/arklet-core/src/test/java/com/alipay/sofa/serverless/arklet/core/command/CustomCommandHandler.java rename to arklet/arklet-core/src/test/java/com/alipay/sofa/serverless/arklet/core/command/custom/CustomCommandHandler.java index 844c005b7..532e96236 100644 --- a/arklet/arklet-core/src/test/java/com/alipay/sofa/serverless/arklet/core/command/CustomCommandHandler.java +++ b/arklet/arklet-core/src/test/java/com/alipay/sofa/serverless/arklet/core/command/custom/CustomCommandHandler.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.alipay.sofa.serverless.arklet.core.command; +package com.alipay.sofa.serverless.arklet.core.command.custom; import com.alipay.sofa.ark.common.util.StringUtils; import com.alipay.sofa.serverless.arklet.core.command.meta.AbstractCommandHandler; diff --git a/arklet/arklet-core/src/test/java/com/alipay/sofa/serverless/arklet/core/command/Input.java b/arklet/arklet-core/src/test/java/com/alipay/sofa/serverless/arklet/core/command/custom/Input.java similarity index 94% rename from arklet/arklet-core/src/test/java/com/alipay/sofa/serverless/arklet/core/command/Input.java rename to arklet/arklet-core/src/test/java/com/alipay/sofa/serverless/arklet/core/command/custom/Input.java index b514db043..b850136b9 100644 --- a/arklet/arklet-core/src/test/java/com/alipay/sofa/serverless/arklet/core/command/Input.java +++ b/arklet/arklet-core/src/test/java/com/alipay/sofa/serverless/arklet/core/command/custom/Input.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.alipay.sofa.serverless.arklet.core.command; +package com.alipay.sofa.serverless.arklet.core.command.custom; import com.alipay.sofa.serverless.arklet.core.command.meta.InputMeta; diff --git a/arklet/arklet-core/src/test/java/com/alipay/sofa/serverless/arklet/core/component/ComponentRegistryTest.java b/arklet/arklet-core/src/test/java/com/alipay/sofa/serverless/arklet/core/component/ComponentRegistryTest.java index 0f867c726..f68cad083 100644 --- a/arklet/arklet-core/src/test/java/com/alipay/sofa/serverless/arklet/core/component/ComponentRegistryTest.java +++ b/arklet/arklet-core/src/test/java/com/alipay/sofa/serverless/arklet/core/component/ComponentRegistryTest.java @@ -33,6 +33,7 @@ public void run() { Assert.assertNotNull(ArkletComponentRegistry.getCommandServiceInstance()); Assert.assertNotNull(ArkletComponentRegistry.getOperationServiceInstance()); Assert.assertNotNull(ArkletComponentRegistry.getApiClientInstance()); + Assert.assertNotNull(ArkletComponentRegistry.getHealthServiceInstance()); Assert.assertTrue(ArkletComponentRegistry.getApiClientInstance().getTunnels().size() > 0); registry.destroyComponents(); } diff --git a/arklet/arklet-core/src/test/java/com/alipay/sofa/serverless/arklet/core/health/HealthTests.java b/arklet/arklet-core/src/test/java/com/alipay/sofa/serverless/arklet/core/health/HealthTests.java new file mode 100644 index 000000000..40be30153 --- /dev/null +++ b/arklet/arklet-core/src/test/java/com/alipay/sofa/serverless/arklet/core/health/HealthTests.java @@ -0,0 +1,85 @@ +/* + * 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.serverless.arklet.core.health; + +import com.alipay.sofa.serverless.arklet.core.ArkletComponentRegistry; +import com.alipay.sofa.serverless.arklet.core.BaseTest; +import com.alipay.sofa.serverless.arklet.core.health.custom.CustomIndicator; +import com.alipay.sofa.serverless.arklet.core.health.model.Constants; +import com.alipay.sofa.serverless.arklet.core.health.model.Health; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import java.io.IOException; +import java.util.Map; + +public class HealthTests extends BaseTest { + + private HealthService healthService; + + private void validateHealth(Health health, final String[] expectedMetrics) { + Assert.assertTrue(health != null && !health.getHealthData().isEmpty()); + Map healthData = health.getHealthData(); + for (String metric : expectedMetrics) { + Assert.assertTrue(healthData.containsKey(metric) + && !((Map) healthData.get(metric)).isEmpty()); + } + } + + private void validateHealth(Health health, String errorCode, String errorMessage) { + Assert.assertTrue(health != null && !health.getHealthData().isEmpty()); + Assert.assertTrue(health.getHealthData().containsKey(errorCode)); + Assert.assertEquals(health.getHealthData().get(errorCode), errorMessage); + } + + @Before + public void initHealthService() throws IOException { + this.healthService = ArkletComponentRegistry.getHealthServiceInstance(); + + // ClassLoader cl = Thread.currentThread().getContextClassLoader(); + // URL testBiz = cl.getResource("test-biz.jar"); + // BizOperation bizOperation = new BizOperation(); + // bizOperation.setBizVersion("test version"); + // ArkClient.getBizFactoryService().createBiz(bizOperation, new File(testBiz.getFile())); + } + + @Test + public void registerCustomCIndicator() { + healthService.registerIndicator(new CustomIndicator()); + CustomIndicator indicator = (CustomIndicator) healthService.getIndicator("custom"); + Assert.assertNotNull(indicator); + } + + @Test + public void testGetHealth() { + final String[] allMetrics = new String[] { Constants.CPU, Constants.JVM }; + final String[] testMetrics = new String[] { Constants.CPU }; + final String[] errorMetrics = new String[] { "nonMetrics" }; + validateHealth(healthService.getHealth(), allMetrics); + validateHealth(healthService.getHealth(new String[0]), allMetrics); + validateHealth(healthService.getHealth(testMetrics), testMetrics); + validateHealth(healthService.getHealth(errorMetrics), Constants.HEALTH_ERROR, + "indicator not registered"); + } + + @Test + public void testIndicators() { + Assert.assertNotNull(healthService.getIndicator(Constants.CPU)); + Assert.assertNotNull(healthService.getIndicator(Constants.JVM)); + } +} diff --git a/arklet/arklet-core/src/main/java/com/alipay/sofa/serverless/arklet/core/command/coordinate/ExecutionLock.java b/arklet/arklet-core/src/test/java/com/alipay/sofa/serverless/arklet/core/health/custom/CustomIndicator.java similarity index 61% rename from arklet/arklet-core/src/main/java/com/alipay/sofa/serverless/arklet/core/command/coordinate/ExecutionLock.java rename to arklet/arklet-core/src/test/java/com/alipay/sofa/serverless/arklet/core/health/custom/CustomIndicator.java index caa29866b..fdfef9642 100644 --- a/arklet/arklet-core/src/main/java/com/alipay/sofa/serverless/arklet/core/command/coordinate/ExecutionLock.java +++ b/arklet/arklet-core/src/test/java/com/alipay/sofa/serverless/arklet/core/health/custom/CustomIndicator.java @@ -14,24 +14,22 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.alipay.sofa.serverless.arklet.core.command.coordinate; +package com.alipay.sofa.serverless.arklet.core.health.custom; -import com.alipay.sofa.serverless.arklet.core.command.meta.Command; +import com.alipay.sofa.serverless.arklet.core.health.indicator.ArkletBaseIndicator; -/** - * @author mingmen - * @date 2023/6/14 - */ -public class ExecutionLock { - private Command command; +import java.util.HashMap; +import java.util.Map; - public static ExecutionLock newInstance(Command command) { - ExecutionLock executionLock = new ExecutionLock(); - executionLock.command = command; - return executionLock; +public class CustomIndicator extends ArkletBaseIndicator { + public CustomIndicator() { + super("custom"); } - public Command getCommand() { - return command; + @Override + protected Map getHealthDetails() { + Map cpuHealthDetails = new HashMap<>(); + cpuHealthDetails.put("key", "value"); + return cpuHealthDetails; } } diff --git a/arklet/arklet-core/src/test/java/com/alipay/sofa/serverless/arklet/core/health/indicator/IndicatorTests.java b/arklet/arklet-core/src/test/java/com/alipay/sofa/serverless/arklet/core/health/indicator/IndicatorTests.java new file mode 100644 index 000000000..3d01e1107 --- /dev/null +++ b/arklet/arklet-core/src/test/java/com/alipay/sofa/serverless/arklet/core/health/indicator/IndicatorTests.java @@ -0,0 +1,60 @@ +/* + * 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.serverless.arklet.core.health.indicator; + +import com.alipay.sofa.serverless.arklet.core.health.model.Health.HealthBuilder; +import org.junit.Assert; +import org.junit.Test; + +import java.util.Map; + +public class IndicatorTests { + + @Test + public void testCpuIndicator() { + ArkletBaseIndicator indicator = new CpuIndicator(); + final String[] indicatorMetrics = new String[] { "count", "type", "total used (%)", + "user used (%)", "system used (%)", "free (%)" }; + final String indicatorId = "cpu"; + Map indicatorData = indicator.getHealthDetails(); + Assert.assertEquals(indicator.getIndicatorId(), indicatorId); + Assert.assertNotNull(indicatorData); + Assert.assertNotNull(indicator.getHealthModel(new HealthBuilder())); + for (String indicatorMetric : indicatorMetrics) { + Assert.assertNotNull(indicatorData.get(indicatorMetric)); + } + } + + @Test + public void testJvmIndicator() { + ArkletBaseIndicator indicator = new JvmIndicator(); + final String[] indicatorMetrics = new String[] { "java version", "java home", + "total memory(M)", "max memory(M)", "free memory(M)", "run time(s)", + "init heap memory(M)", "used heap memory(M)", "committed heap memory(M)", + "max heap memory(M)", "init non heap memory(M)", "used non heap memory(M)", + "committed non heap memory(M)", "max non heap memory(M)", "loaded class count", + "unload class count", "total class count" }; + final String indicatorId = "jvm"; + Map indicatorData = indicator.getHealthDetails(); + Assert.assertEquals(indicator.getIndicatorId(), indicatorId); + Assert.assertNotNull(indicatorData); + Assert.assertNotNull(indicator.getHealthModel(new HealthBuilder())); + for (String indicatorMetric : indicatorMetrics) { + Assert.assertNotNull(indicatorData.get(indicatorMetric)); + } + } +} \ No newline at end of file diff --git a/arklet/arklet-core/src/test/resources/test-biz.jar b/arklet/arklet-core/src/test/resources/test-biz.jar new file mode 100644 index 000000000..d5cc1577f Binary files /dev/null and b/arklet/arklet-core/src/test/resources/test-biz.jar differ diff --git a/arklet/arklet-springboot-starter/pom.xml b/arklet/arklet-springboot-starter/pom.xml index 7f8f49c08..7ec92f8ea 100644 --- a/arklet/arklet-springboot-starter/pom.xml +++ b/arklet/arklet-springboot-starter/pom.xml @@ -12,11 +12,6 @@ arklet-springboot-starter - - 2.5.15 - - - com.alipay.sofa.serverless @@ -26,14 +21,18 @@ org.springframework.boot spring-boot-autoconfigure - ${spring.boot.version} provided org.springframework.boot spring-boot-loader - ${spring.boot.version} + provided + + + + org.springframework.boot + spring-boot-starter-actuator provided @@ -41,14 +40,12 @@ org.springframework.boot spring-boot-starter-test - ${spring.boot.version} test org.springframework.boot spring-boot-starter-logging - ${spring.boot.version} test @@ -72,7 +69,6 @@ com.alipay.sofa sofa-ark-springboot-starter - ${sofa.ark.version} test @@ -82,6 +78,7 @@ test + \ No newline at end of file diff --git a/arklet/arklet-springboot-starter/src/main/java/com/alipay/sofa/serverless/arklet/springboot/starter/ArkletAutoConfiguration.java b/arklet/arklet-springboot-starter/src/main/java/com/alipay/sofa/serverless/arklet/springboot/starter/ArkletAutoConfiguration.java index 0fa060ea6..e95915e31 100644 --- a/arklet/arklet-springboot-starter/src/main/java/com/alipay/sofa/serverless/arklet/springboot/starter/ArkletAutoConfiguration.java +++ b/arklet/arklet-springboot-starter/src/main/java/com/alipay/sofa/serverless/arklet/springboot/starter/ArkletAutoConfiguration.java @@ -32,7 +32,7 @@ public class ArkletAutoConfiguration { @Bean @ConditionalOnMasterBiz - public ArkletComponentRegistry componentRegistry() { + public ArkletComponentRegistry arkletComponentRegistry() { ArkletComponentRegistry registry = new ArkletComponentRegistry(); registry.initComponents(); return registry; @@ -40,7 +40,7 @@ public ArkletComponentRegistry componentRegistry() { @Bean @ConditionalOnMasterBiz - @DependsOn("componentRegistry") + @DependsOn("arkletComponentRegistry") public MasterBizCmdHandlerCollector masterBizCmdHandlerCollector() { return new MasterBizCmdHandlerCollector(); } diff --git a/arklet/arklet-springboot-starter/src/main/java/com/alipay/sofa/serverless/arklet/springboot/starter/command/MasterBizCmdHandlerCollector.java b/arklet/arklet-springboot-starter/src/main/java/com/alipay/sofa/serverless/arklet/springboot/starter/command/MasterBizCmdHandlerCollector.java index 26350db33..2a6d0b651 100644 --- a/arklet/arklet-springboot-starter/src/main/java/com/alipay/sofa/serverless/arklet/springboot/starter/command/MasterBizCmdHandlerCollector.java +++ b/arklet/arklet-springboot-starter/src/main/java/com/alipay/sofa/serverless/arklet/springboot/starter/command/MasterBizCmdHandlerCollector.java @@ -27,6 +27,7 @@ /** * @author mingmen * @date 2023/6/14 + * custom directive extension for master base application */ @SuppressWarnings("rawtypes") public class MasterBizCmdHandlerCollector implements ApplicationContextAware { @@ -35,6 +36,7 @@ public class MasterBizCmdHandlerCollector implements ApplicationContextAware { public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { Map map = applicationContext.getBeansOfType(AbstractCommandHandler.class); map.forEach((k, v) -> { + // find custom directive beans from master base's spring context ArkletComponentRegistry.getCommandServiceInstance().registerCommandHandler(v); }); } diff --git a/arklet/arklet-springboot-starter/src/main/java/com/alipay/sofa/serverless/arklet/springboot/starter/health/HealthAutoConfiguration.java b/arklet/arklet-springboot-starter/src/main/java/com/alipay/sofa/serverless/arklet/springboot/starter/health/HealthAutoConfiguration.java new file mode 100644 index 000000000..1fc02e4ef --- /dev/null +++ b/arklet/arklet-springboot-starter/src/main/java/com/alipay/sofa/serverless/arklet/springboot/starter/health/HealthAutoConfiguration.java @@ -0,0 +1,83 @@ +/* + * 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.serverless.arklet.springboot.starter.health; + +import com.alipay.sofa.serverless.arklet.core.ArkletComponentRegistry; +import com.alipay.sofa.serverless.arklet.springboot.starter.health.endpoint.ArkHealthCodeEndpoint; +import com.alipay.sofa.serverless.arklet.springboot.starter.health.endpoint.ArkHealthzEndpoint; +import com.alipay.sofa.serverless.arklet.springboot.starter.health.extension.indicator.MasterBizHealthIndicator; +import com.alipay.sofa.serverless.arklet.springboot.starter.environment.ConditionalOnMasterBiz; +import org.springframework.beans.BeansException; +import org.springframework.boot.actuate.autoconfigure.endpoint.condition.ConditionalOnAvailableEndpoint; +import org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointProperties; +import org.springframework.boot.availability.ApplicationAvailability; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.DependsOn; + +import java.util.Set; + +/** + * @author Lunarscave + */ +@Configuration +@ConditionalOnMasterBiz +public class HealthAutoConfiguration implements ApplicationContextAware { + + private ApplicationContext context; + + @Bean + public void initEndpoint() { + WebEndpointProperties webEndpointProperties = this.context + .getBean(WebEndpointProperties.class); + WebEndpointProperties.Exposure exposure = webEndpointProperties.getExposure(); + Set includePath = exposure.getInclude(); + includePath.add("*"); + webEndpointProperties.getExposure().setInclude(includePath); + webEndpointProperties.setBasePath("/"); + } + + @Bean + @ConditionalOnAvailableEndpoint + public ArkHealthzEndpoint arkHealthzEndpoint() { + return new ArkHealthzEndpoint(); + } + + @Bean + @ConditionalOnAvailableEndpoint + public ArkHealthCodeEndpoint arkHealthCodeEndpoint() { + return new ArkHealthCodeEndpoint(); + } + + @Bean + @DependsOn("componentRegistry") + public MasterBizHealthIndicator masterBizHealthIndicator() { + MasterBizHealthIndicator masterBizHealthIndicator = new MasterBizHealthIndicator(); + masterBizHealthIndicator.setApplicationAvailability(context + .getBean(ApplicationAvailability.class)); + ArkletComponentRegistry.getHealthServiceInstance().registerIndicator( + masterBizHealthIndicator); + return masterBizHealthIndicator; + } + + @Override + public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { + this.context = applicationContext; + } +} diff --git a/arklet/arklet-springboot-starter/src/main/java/com/alipay/sofa/serverless/arklet/springboot/starter/health/endpoint/ArkHealthCodeEndpoint.java b/arklet/arklet-springboot-starter/src/main/java/com/alipay/sofa/serverless/arklet/springboot/starter/health/endpoint/ArkHealthCodeEndpoint.java new file mode 100644 index 000000000..c88140431 --- /dev/null +++ b/arklet/arklet-springboot-starter/src/main/java/com/alipay/sofa/serverless/arklet/springboot/starter/health/endpoint/ArkHealthCodeEndpoint.java @@ -0,0 +1,69 @@ +/* + * 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.serverless.arklet.springboot.starter.health.endpoint; + +import com.alipay.sofa.serverless.arklet.core.ArkletComponentRegistry; +import com.alipay.sofa.serverless.arklet.core.health.HealthService; +import com.alipay.sofa.serverless.arklet.core.health.model.Constants; +import com.alipay.sofa.serverless.arklet.core.health.model.Health; +import com.alipay.sofa.serverless.arklet.core.health.model.Health.HealthBuilder; +import com.alipay.sofa.serverless.arklet.springboot.starter.health.endpoint.model.EndpointResponseCode; +import org.springframework.boot.actuate.endpoint.annotation.Endpoint; +import org.springframework.boot.actuate.endpoint.annotation.ReadOperation; +import org.springframework.boot.actuate.endpoint.annotation.Selector; + +/** + * @author Lunarscave + */ +@Endpoint(id = "arkHealthCode") +public class ArkHealthCodeEndpoint { + + private final HealthService healthService = ArkletComponentRegistry.getHealthServiceInstance(); + + @ReadOperation + public int healthCode() { + return ArkHealthCodeEndpoint.ofCode(new HealthBuilder().init() + .putAllHealthData(healthService.getHealth()) + .putAllHealthData(healthService.queryModuleInfo()).build()); + } + + @ReadOperation + public int getModuleInfoHealthCode1(@Selector String moduleType) { + return ArkHealthCodeEndpoint.ofCode(healthService.queryModuleInfo(moduleType, null, null)); + } + + @ReadOperation + public int getModuleInfoHealthCode2(@Selector String moduleType, @Selector String name, + @Selector String version) { + return ArkHealthCodeEndpoint.ofCode(healthService + .queryModuleInfo(moduleType, name, version)); + } + + public static int ofCode(Health health) { + int endpointCode; + if (health.containsError(Constants.HEALTH_ENDPOINT_ERROR)) { + endpointCode = EndpointResponseCode.ENDPOINT_NOT_FOUND.getCode(); + } else if (health.containsError(Constants.HEALTH_ERROR)) { + endpointCode = EndpointResponseCode.ENDPOINT_PROCESS_INTERNAL_ERROR.getCode(); + } else if (health.containsUnhealthy(Constants.READINESS_HEALTHY)) { + endpointCode = EndpointResponseCode.UNHEALTHY.getCode(); + } else { + endpointCode = EndpointResponseCode.HEALTHY.getCode(); + } + return endpointCode; + } +} diff --git a/arklet/arklet-springboot-starter/src/main/java/com/alipay/sofa/serverless/arklet/springboot/starter/health/endpoint/ArkHealthzEndpoint.java b/arklet/arklet-springboot-starter/src/main/java/com/alipay/sofa/serverless/arklet/springboot/starter/health/endpoint/ArkHealthzEndpoint.java new file mode 100644 index 000000000..2dfeece81 --- /dev/null +++ b/arklet/arklet-springboot-starter/src/main/java/com/alipay/sofa/serverless/arklet/springboot/starter/health/endpoint/ArkHealthzEndpoint.java @@ -0,0 +1,77 @@ +/* + * 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.serverless.arklet.springboot.starter.health.endpoint; + +import com.alipay.sofa.serverless.arklet.core.ArkletComponentRegistry; +import com.alipay.sofa.serverless.arklet.core.health.HealthService; +import com.alipay.sofa.serverless.arklet.core.health.model.Constants; +import com.alipay.sofa.serverless.arklet.core.health.model.Health; +import com.alipay.sofa.serverless.arklet.core.health.model.Health.HealthBuilder; +import com.alipay.sofa.serverless.arklet.springboot.starter.health.endpoint.model.EndpointResponse; +import com.alipay.sofa.serverless.arklet.springboot.starter.health.endpoint.model.EndpointResponseCode; +import org.springframework.boot.actuate.endpoint.annotation.Endpoint; +import org.springframework.boot.actuate.endpoint.annotation.ReadOperation; +import org.springframework.boot.actuate.endpoint.annotation.Selector; + +import java.util.Map; + +/** + * @author Lunarscave + */ +@Endpoint(id = "arkHealthz") +public class ArkHealthzEndpoint { + + private final HealthService healthService = ArkletComponentRegistry.getHealthServiceInstance(); + + @ReadOperation + public EndpointResponse> getHealth() { + return ArkHealthzEndpoint.ofResponse(new HealthBuilder().init() + .putAllHealthData(healthService.getHealth()) + .putAllHealthData(healthService.queryModuleInfo()).build()); + } + + @ReadOperation + public EndpointResponse> getModuleInfo1(@Selector String moduleType) { + return ArkHealthzEndpoint.ofResponse(healthService.queryModuleInfo(moduleType, null, null)); + } + + @ReadOperation + public EndpointResponse> getModuleInfo2(@Selector String moduleType, + @Selector String name, + @Selector String version) { + return ArkHealthzEndpoint.ofResponse(healthService.queryModuleInfo(moduleType, name, + version)); + } + + private static EndpointResponse> ofResponse(Health health) { + Map healthData = health.getHealthData(); + EndpointResponse> endpointResponse; + if (health.containsError(Constants.HEALTH_ENDPOINT_ERROR)) { + endpointResponse = EndpointResponse.ofFailed(EndpointResponseCode.ENDPOINT_NOT_FOUND, + healthData); + } else if (health.containsError(Constants.HEALTH_ERROR)) { + endpointResponse = EndpointResponse.ofFailed( + EndpointResponseCode.ENDPOINT_PROCESS_INTERNAL_ERROR, healthData); + } else if (health.containsUnhealthy(Constants.READINESS_HEALTHY)) { + endpointResponse = EndpointResponse + .ofFailed(EndpointResponseCode.UNHEALTHY, healthData); + } else { + endpointResponse = EndpointResponse.ofSuccess(healthData); + } + return endpointResponse; + } +} diff --git a/arklet/arklet-springboot-starter/src/main/java/com/alipay/sofa/serverless/arklet/springboot/starter/health/endpoint/model/EndpointResponse.java b/arklet/arklet-springboot-starter/src/main/java/com/alipay/sofa/serverless/arklet/springboot/starter/health/endpoint/model/EndpointResponse.java new file mode 100644 index 000000000..f2f303015 --- /dev/null +++ b/arklet/arklet-springboot-starter/src/main/java/com/alipay/sofa/serverless/arklet/springboot/starter/health/endpoint/model/EndpointResponse.java @@ -0,0 +1,81 @@ +/* + * 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.serverless.arklet.springboot.starter.health.endpoint.model; + +/** + * @author Lunarscave + */ +public class EndpointResponse { + + private boolean healthy; + private int code; + private EndpointResponseCode codeType; + private T data; + + private EndpointResponse() { + } + + public static EndpointResponse ofSuccess(T data) { + EndpointResponse endpointResponse = new EndpointResponse<>(); + endpointResponse.healthy = true; + endpointResponse.code = EndpointResponseCode.HEALTHY.getCode(); + endpointResponse.codeType = EndpointResponseCode.HEALTHY; + endpointResponse.data = data; + return endpointResponse; + } + + public static EndpointResponse ofFailed(EndpointResponseCode codeType, T data) { + EndpointResponse endpointResponse = new EndpointResponse<>(); + endpointResponse.healthy = false; + endpointResponse.codeType = codeType; + endpointResponse.code = codeType.getCode(); + endpointResponse.data = data; + return endpointResponse; + } + + public EndpointResponseCode getCodeType() { + return codeType; + } + + public void setCodeType(EndpointResponseCode codeType) { + this.codeType = codeType; + } + + public T getData() { + return data; + } + + public void setData(T data) { + this.data = data; + } + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public boolean isHealthy() { + return healthy; + } + + public void setHealthy(boolean healthy) { + this.healthy = healthy; + } +} diff --git a/arklet/arklet-springboot-starter/src/main/java/com/alipay/sofa/serverless/arklet/springboot/starter/health/endpoint/model/EndpointResponseCode.java b/arklet/arklet-springboot-starter/src/main/java/com/alipay/sofa/serverless/arklet/springboot/starter/health/endpoint/model/EndpointResponseCode.java new file mode 100644 index 000000000..fa20fe403 --- /dev/null +++ b/arklet/arklet-springboot-starter/src/main/java/com/alipay/sofa/serverless/arklet/springboot/starter/health/endpoint/model/EndpointResponseCode.java @@ -0,0 +1,59 @@ +/* + * 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.serverless.arklet.springboot.starter.health.endpoint.model; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + +/** + * @author Lunarscave + */ +public enum EndpointResponseCode { + + HEALTHY(200), UNHEALTHY(400), ENDPOINT_NOT_FOUND(404), ENDPOINT_PROCESS_INTERNAL_ERROR(500); + + private final int code; + + EndpointResponseCode(int code) { + this.code = code; + } + + public int getCode() { + return code; + } + + public static EndpointResponseCode getEndpointResponseCode(int code) { + Set codes = new HashSet<>(Arrays.asList(values())); + EndpointResponseCode endpointResponseCode = null; + for (EndpointResponseCode codeType : codes) { + if (codeType.getCode() == code) { + endpointResponseCode = codeType; + } + } + return endpointResponseCode; + } + + public static boolean existCode(int code) { + Set codes = new HashSet<>(Arrays.asList(values())); + boolean exists = false; + for (EndpointResponseCode codeType : codes) { + exists |= codeType.getCode() == code; + } + return exists; + } +} diff --git a/arklet/arklet-springboot-starter/src/main/java/com/alipay/sofa/serverless/arklet/springboot/starter/health/extension/indicator/MasterBizHealthIndicator.java b/arklet/arklet-springboot-starter/src/main/java/com/alipay/sofa/serverless/arklet/springboot/starter/health/extension/indicator/MasterBizHealthIndicator.java new file mode 100644 index 000000000..819b3fe48 --- /dev/null +++ b/arklet/arklet-springboot-starter/src/main/java/com/alipay/sofa/serverless/arklet/springboot/starter/health/extension/indicator/MasterBizHealthIndicator.java @@ -0,0 +1,67 @@ +/* + * 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.serverless.arklet.springboot.starter.health.extension.indicator; + +import com.alipay.sofa.serverless.arklet.core.health.indicator.ArkletBaseIndicator; +import com.alipay.sofa.serverless.arklet.core.health.model.Constants; +import com.alipay.sofa.serverless.arklet.core.util.AssertUtils; +import org.springframework.boot.availability.ApplicationAvailability; + +import java.util.HashMap; +import java.util.Map; + +/** + * @author Lunarscave + */ +public class MasterBizHealthIndicator extends ArkletBaseIndicator { + + private ApplicationAvailability applicationAvailability; + + private final static String MASTER_BIZ_HEALTH_INDICATOR_ID = Constants.MASTER_BIZ_HEALTH; + + public MasterBizHealthIndicator() { + super(MASTER_BIZ_HEALTH_INDICATOR_ID); + } + + @Override + protected Map getHealthDetails() { + AssertUtils.assertNotNull(applicationAvailability, "applicationAvailability must not null"); + Map masterBizHealthDetails = new HashMap<>(1); + masterBizHealthDetails.put(MasterBizHealthMetrics.READINESS_STATE.getId(), + applicationAvailability.getReadinessState()); + return masterBizHealthDetails; + } + + public void setApplicationAvailability(ApplicationAvailability applicationAvailability) { + this.applicationAvailability = applicationAvailability; + } + + enum MasterBizHealthMetrics { + + READINESS_STATE("readinessState"); + + private final String id; + + MasterBizHealthMetrics(String desc) { + this.id = desc; + } + + public String getId() { + return id; + }; + } +} diff --git a/arklet/arklet-springboot-starter/src/main/java/com/alipay/sofa/serverless/arklet/springboot/starter/listener/ArkletApplicationListener.java b/arklet/arklet-springboot-starter/src/main/java/com/alipay/sofa/serverless/arklet/springboot/starter/listener/ArkletApplicationListener.java index 6e35bf087..58d5e9b1d 100644 --- a/arklet/arklet-springboot-starter/src/main/java/com/alipay/sofa/serverless/arklet/springboot/starter/listener/ArkletApplicationListener.java +++ b/arklet/arklet-springboot-starter/src/main/java/com/alipay/sofa/serverless/arklet/springboot/starter/listener/ArkletApplicationListener.java @@ -54,8 +54,9 @@ public void onApplicationEvent(ApplicationContextEvent event) { LOGGER.info("total supported commands:{}", commands); } if (event instanceof ContextClosedEvent) { - ArkletComponentRegistry registry = event.getApplicationContext().getBean(ArkletComponentRegistry.class); - registry.destroyComponents(); + // destroy arklet components + ArkletComponentRegistry componentRegistry = event.getApplicationContext().getBean(ArkletComponentRegistry.class); + componentRegistry.destroyComponents(); } } diff --git a/arklet/arklet-springboot-starter/src/main/resources/META-INF/spring.factories b/arklet/arklet-springboot-starter/src/main/resources/META-INF/spring.factories index e0039ee85..50f34d36d 100644 --- a/arklet/arklet-springboot-starter/src/main/resources/META-INF/spring.factories +++ b/arklet/arklet-springboot-starter/src/main/resources/META-INF/spring.factories @@ -1,6 +1,7 @@ org.springframework.context.ApplicationListener=\ com.alipay.sofa.serverless.arklet.springboot.starter.listener.ArkletApplicationListener org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ - com.alipay.sofa.serverless.arklet.springboot.starter.ArkletAutoConfiguration + com.alipay.sofa.serverless.arklet.springboot.starter.ArkletAutoConfiguration,\ + com.alipay.sofa.serverless.arklet.springboot.starter.health.HealthAutoConfiguration diff --git a/arklet/arklet-springboot-starter/src/test/java/com/alipay/sofa/serverless/arklet/spring/BaseSpringApplication.java b/arklet/arklet-springboot-starter/src/test/java/com/alipay/sofa/serverless/arklet/spring/BaseSpringApplication.java index ee6f44bef..ccc23e6ea 100644 --- a/arklet/arklet-springboot-starter/src/test/java/com/alipay/sofa/serverless/arklet/spring/BaseSpringApplication.java +++ b/arklet/arklet-springboot-starter/src/test/java/com/alipay/sofa/serverless/arklet/spring/BaseSpringApplication.java @@ -16,9 +16,10 @@ */ package com.alipay.sofa.serverless.arklet.spring; +import com.alipay.sofa.serverless.arklet.spring.common.SpringbootUtil; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.context.annotation.ImportResource; +import org.springframework.context.ConfigurableApplicationContext; /** * @author mingmen @@ -28,6 +29,7 @@ public class BaseSpringApplication { public static void main(String[] args) { SpringApplication springApplication = new SpringApplication(BaseSpringApplication.class); - springApplication.run(args); + ConfigurableApplicationContext context = springApplication.run(args); + SpringbootUtil.initSpringbootUtil(context); } } \ No newline at end of file diff --git a/arklet/arklet-core/src/test/java/com/alipay/sofa/serverless/arklet/core/command/CommandTest.java b/arklet/arklet-springboot-starter/src/test/java/com/alipay/sofa/serverless/arklet/spring/SpringbootBaseTest.java similarity index 53% rename from arklet/arklet-core/src/test/java/com/alipay/sofa/serverless/arklet/core/command/CommandTest.java rename to arklet/arklet-springboot-starter/src/test/java/com/alipay/sofa/serverless/arklet/spring/SpringbootBaseTest.java index ce170dad7..90b5db6c6 100644 --- a/arklet/arklet-core/src/test/java/com/alipay/sofa/serverless/arklet/core/command/CommandTest.java +++ b/arklet/arklet-springboot-starter/src/test/java/com/alipay/sofa/serverless/arklet/spring/SpringbootBaseTest.java @@ -14,26 +14,27 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.alipay.sofa.serverless.arklet.core.command; +package com.alipay.sofa.serverless.arklet.spring; -import com.alipay.sofa.serverless.arklet.core.ArkletComponentRegistry; -import org.junit.Assert; -import org.junit.Test; +import com.alipay.sofa.ark.common.util.ClassLoaderUtils; +import com.alipay.sofa.ark.spi.constant.Constants; +import org.junit.AfterClass; +import org.junit.BeforeClass; /** - * @author mingmen - * @date 2023/6/26 + * @author Lunarscave */ -public class CommandTest { +public class SpringbootBaseTest { - @Test - public void registerCustomCommand() { - ArkletComponentRegistry registry = new ArkletComponentRegistry(); - registry.initComponents(); - ArkletComponentRegistry.getCommandServiceInstance().registerCommandHandler( - new CustomCommandHandler()); - CustomCommandHandler handler = (CustomCommandHandler) ArkletComponentRegistry - .getCommandServiceInstance().getHandler(CustomCommand.HELLO); - Assert.assertNotNull(handler); + @BeforeClass + public static void initSpringbootTest() { + ClassLoaderUtils.pushContextClassLoader(ClassLoader.getSystemClassLoader()); + System.setProperty(Constants.EMBED_ENABLE, "true"); + BaseSpringApplication.main(new String[] {}); + } + + @AfterClass + public static void destroySpringbootTest() { + System.setProperty(Constants.EMBED_ENABLE, ""); } } diff --git a/arklet/arklet-springboot-starter/src/test/java/com/alipay/sofa/serverless/arklet/spring/SpringbootRunnerTest.java b/arklet/arklet-springboot-starter/src/test/java/com/alipay/sofa/serverless/arklet/spring/SpringbootRunnerTest.java index 2b007c3d8..864623625 100644 --- a/arklet/arklet-springboot-starter/src/test/java/com/alipay/sofa/serverless/arklet/spring/SpringbootRunnerTest.java +++ b/arklet/arklet-springboot-starter/src/test/java/com/alipay/sofa/serverless/arklet/spring/SpringbootRunnerTest.java @@ -19,31 +19,21 @@ import com.alipay.sofa.ark.common.util.ClassLoaderUtils; import com.alipay.sofa.ark.spi.constant.Constants; import com.alipay.sofa.serverless.arklet.core.ArkletComponentRegistry; -import org.junit.After; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; +import org.junit.*; +import org.junit.jupiter.api.BeforeAll; +import org.junit.runner.RunWith; +import org.springframework.test.context.junit4.SpringRunner; /** * @author mingmen * @date 2023/6/26 */ -public class SpringbootRunnerTest { - - @Before - public void before() { - ClassLoaderUtils.pushContextClassLoader(ClassLoader.getSystemClassLoader()); - System.setProperty(Constants.EMBED_ENABLE, "true"); - } - - @After - public void after() { - System.setProperty(Constants.EMBED_ENABLE, ""); - } +public class SpringbootRunnerTest extends SpringbootBaseTest { @Test - public void test() { - BaseSpringApplication.main(new String[] {}); + public void testArkletService() { Assert.assertNotNull(ArkletComponentRegistry.getCommandServiceInstance()); + Assert.assertNotNull(ArkletComponentRegistry.getHealthServiceInstance()); + Assert.assertNotNull(ArkletComponentRegistry.getOperationServiceInstance()); } } diff --git a/arklet/arklet-springboot-starter/src/test/java/com/alipay/sofa/serverless/arklet/spring/common/SpringbootUtil.java b/arklet/arklet-springboot-starter/src/test/java/com/alipay/sofa/serverless/arklet/spring/common/SpringbootUtil.java new file mode 100644 index 000000000..be5634523 --- /dev/null +++ b/arklet/arklet-springboot-starter/src/test/java/com/alipay/sofa/serverless/arklet/spring/common/SpringbootUtil.java @@ -0,0 +1,48 @@ +/* + * 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.serverless.arklet.spring.common; + +import org.springframework.context.ApplicationContext; +import org.springframework.core.env.Environment; + +public class SpringbootUtil { + + private static ApplicationContext context; + + private static Environment environment; + + public static void initSpringbootUtil(ApplicationContext applicationContext) { + context = applicationContext; + environment = context.getEnvironment(); + } + + public static ApplicationContext getContext() { + return context; + } + + public static Environment getEnvironment() { + return environment; + } + + public static T getBean(Class clazz) { + return context.getBean(clazz); + } + + public static String getProperty(String key) { + return environment.getProperty(key); + } +} diff --git a/arklet/arklet-springboot-starter/src/test/java/com/alipay/sofa/serverless/arklet/spring/health/endpoint/ArkHealthCodeEndpointTests.java b/arklet/arklet-springboot-starter/src/test/java/com/alipay/sofa/serverless/arklet/spring/health/endpoint/ArkHealthCodeEndpointTests.java new file mode 100644 index 000000000..1c6b5e0d7 --- /dev/null +++ b/arklet/arklet-springboot-starter/src/test/java/com/alipay/sofa/serverless/arklet/spring/health/endpoint/ArkHealthCodeEndpointTests.java @@ -0,0 +1,91 @@ +/* + * 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.serverless.arklet.spring.health.endpoint; + +import com.alipay.sofa.serverless.arklet.core.health.model.Constants; +import com.alipay.sofa.serverless.arklet.spring.SpringbootBaseTest; +import com.alipay.sofa.serverless.arklet.spring.common.SpringbootUtil; +import com.alipay.sofa.serverless.arklet.springboot.starter.health.endpoint.ArkHealthCodeEndpoint; +import com.alipay.sofa.serverless.arklet.springboot.starter.health.endpoint.model.EndpointResponseCode; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; + +/** + * @author Lunarscave + */ +public class ArkHealthCodeEndpointTests extends SpringbootBaseTest { + private static ArkHealthCodeEndpoint arkHealthCodeEndpoint; + private static String bizName; + private static String bizVersion; + + @BeforeClass + public static void initHealthService() { + arkHealthCodeEndpoint = new ArkHealthCodeEndpoint(); + bizName = SpringbootUtil.getProperty("spring.application.name"); + bizVersion = "1.0.0"; + } + + @Test + public void testHealthCode() { + Assert.assertEquals(arkHealthCodeEndpoint.healthCode(), + EndpointResponseCode.HEALTHY.getCode()); + } + + @Test + public void testGetModuleInfoHealthCode1_BizType() { + Assert.assertEquals(arkHealthCodeEndpoint.getModuleInfoHealthCode1(Constants.BIZ), + EndpointResponseCode.HEALTHY.getCode()); + } + + @Test + public void testGetModuleInfoHealthCode1_PluginType() { + Assert.assertEquals(arkHealthCodeEndpoint.getModuleInfoHealthCode1(Constants.PLUGIN), + EndpointResponseCode.HEALTHY.getCode()); + } + + @Test + public void testGetModuleInfoHealthCode1_NonType() { + final String nonType = "non"; + Assert.assertEquals(arkHealthCodeEndpoint.getModuleInfoHealthCode1(nonType), + EndpointResponseCode.ENDPOINT_PROCESS_INTERNAL_ERROR.getCode()); + } + + @Test + public void testGetModuleInfoHealthCode2_BizSuccess() { + Assert.assertEquals( + arkHealthCodeEndpoint.getModuleInfoHealthCode2(Constants.BIZ, bizName, bizVersion), + EndpointResponseCode.HEALTHY.getCode()); + } + + @Test + public void testGetModuleInfo2_BizFailure() { + final String nonBizName = "non"; + Assert.assertEquals( + arkHealthCodeEndpoint.getModuleInfoHealthCode2(Constants.BIZ, nonBizName, bizVersion), + EndpointResponseCode.ENDPOINT_PROCESS_INTERNAL_ERROR.getCode()); + } + + @Test + public void testGetModuleInfo2_PluginFailure() { + final String nonPluginName = "non"; + final String nonPluginVersion = "x.x.x"; + Assert.assertEquals(arkHealthCodeEndpoint.getModuleInfoHealthCode2(Constants.BIZ, + nonPluginName, nonPluginVersion), EndpointResponseCode.ENDPOINT_PROCESS_INTERNAL_ERROR + .getCode()); + } +} diff --git a/arklet/arklet-springboot-starter/src/test/java/com/alipay/sofa/serverless/arklet/spring/health/endpoint/ArkHealthzEndpointTests.java b/arklet/arklet-springboot-starter/src/test/java/com/alipay/sofa/serverless/arklet/spring/health/endpoint/ArkHealthzEndpointTests.java new file mode 100644 index 000000000..6f1cd29f3 --- /dev/null +++ b/arklet/arklet-springboot-starter/src/test/java/com/alipay/sofa/serverless/arklet/spring/health/endpoint/ArkHealthzEndpointTests.java @@ -0,0 +1,158 @@ +/* + * 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.serverless.arklet.spring.health.endpoint; + +import com.alipay.sofa.serverless.arklet.core.health.model.Constants; +import com.alipay.sofa.serverless.arklet.spring.SpringbootBaseTest; +import com.alipay.sofa.serverless.arklet.spring.common.SpringbootUtil; +import com.alipay.sofa.serverless.arklet.springboot.starter.health.endpoint.ArkHealthzEndpoint; +import com.alipay.sofa.serverless.arklet.springboot.starter.health.endpoint.model.EndpointResponse; +import com.alipay.sofa.serverless.arklet.springboot.starter.health.endpoint.model.EndpointResponseCode; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.util.Map; + +/** + * @author Lunarscave + */ +public class ArkHealthzEndpointTests extends SpringbootBaseTest { + + private static ArkHealthzEndpoint arkHealthzEndpoint; + private static String bizName; + private static String bizVersion; + private final String[] indicatorIds = { Constants.CPU, Constants.JVM, + Constants.MASTER_BIZ_HEALTH, Constants.MASTER_BIZ_INFO, Constants.BIZ_LIST_INFO, + Constants.PLUGIN_LIST_INFO }; + + private void testEndpointHeader(EndpointResponse> response) { + Assert.assertNotNull(response); + Assert.assertTrue(EndpointResponseCode.existCode(response.getCode())); + Assert.assertNotNull(response.getCodeType()); + Map healthData = response.getData(); + Assert.assertTrue(healthData != null && !healthData.isEmpty()); + } + + private void testEndpointHeader(EndpointResponse> response, int code) { + testEndpointHeader(response); + Assert.assertEquals(response.getCode(), code); + Assert.assertEquals(response.getCodeType(), + EndpointResponseCode.getEndpointResponseCode(code)); + Assert + .assertEquals(response.isHealthy(), EndpointResponseCode.HEALTHY + .equals(EndpointResponseCode.getEndpointResponseCode(code))); + } + + private void testEndpointHeader(EndpointResponse> response, + EndpointResponseCode code) { + testEndpointHeader(response); + Assert.assertEquals(response.getCode(), code.getCode()); + Assert.assertEquals(response.getCodeType(), code); + Assert.assertEquals(response.isHealthy(), EndpointResponseCode.HEALTHY.equals(code)); + } + + private void testEndpointData(EndpointResponse> response, + String healthDataKey) { + Map healthData = response.getData(); + Assert.assertTrue(healthData.get(healthDataKey) != null); + if (healthData.get(healthDataKey) instanceof Map) { + Map healthMap = (Map) healthData.get(healthDataKey); + Assert.assertTrue(!healthMap.isEmpty()); + } + } + + private void testEndpointData(EndpointResponse> response, + String[] healthDataKeys) { + for (String healthDataKey : healthDataKeys) { + testEndpointData(response, healthDataKey); + } + } + + private void testEndpointData(EndpointResponse> response, String errorKey, + String errorMessage) { + testEndpointData(response, errorKey); + Map healthData = response.getData(); + Assert.assertEquals(healthData.get(errorKey), errorMessage); + } + + @BeforeClass + public static void initHealthService() { + arkHealthzEndpoint = new ArkHealthzEndpoint(); + bizName = SpringbootUtil.getProperty("spring.application.name"); + bizVersion = "1.0.0"; + } + + @Test + public void testGetHealth() { + EndpointResponse> response = arkHealthzEndpoint.getHealth(); + testEndpointHeader(response); + testEndpointData(response, indicatorIds); + } + + @Test + public void testGetModuleInfo1_BizType() { + EndpointResponse> response = arkHealthzEndpoint + .getModuleInfo1(Constants.BIZ); + testEndpointHeader(response); + testEndpointData(response, Constants.BIZ_LIST_INFO); + } + + @Test + public void testGetModuleInfo1_PluginType() { + EndpointResponse> response = arkHealthzEndpoint + .getModuleInfo1(Constants.PLUGIN); + testEndpointHeader(response); + testEndpointData(response, Constants.PLUGIN_LIST_INFO); + } + + @Test + public void testGetModuleInfo1_NonType() { + final String nonType = "non"; + EndpointResponse> response = arkHealthzEndpoint.getModuleInfo1(nonType); + testEndpointHeader(response, EndpointResponseCode.ENDPOINT_PROCESS_INTERNAL_ERROR); + testEndpointData(response, Constants.HEALTH_ERROR, + String.format("illegal type: %s", nonType)); + } + + @Test + public void testGetModuleInfo2_BizSuccess() { + EndpointResponse> response = arkHealthzEndpoint.getModuleInfo2( + Constants.BIZ, bizName, bizVersion); + testEndpointHeader(response); + testEndpointData(response, Constants.BIZ_INFO); + } + + @Test + public void testGetModuleInfo2_BizFailure() { + final String nonBizName = "non"; + EndpointResponse> response = arkHealthzEndpoint.getModuleInfo2( + Constants.BIZ, nonBizName, bizVersion); + testEndpointHeader(response, EndpointResponseCode.ENDPOINT_PROCESS_INTERNAL_ERROR); + testEndpointData(response, Constants.HEALTH_ERROR, "can not find biz"); + } + + @Test + public void testGetModuleInfo2_PluginFailure() { + final String nonPluginName = "non"; + final String nonPluginVersion = "x.x.x"; + EndpointResponse> response = arkHealthzEndpoint.getModuleInfo2( + Constants.PLUGIN, nonPluginName, nonPluginVersion); + testEndpointHeader(response, EndpointResponseCode.ENDPOINT_PROCESS_INTERNAL_ERROR); + testEndpointData(response, Constants.HEALTH_ERROR, "can not find plugin"); + } +} diff --git a/arklet/arklet-springboot-starter/src/test/java/com/alipay/sofa/serverless/arklet/spring/health/extension/indicator/MasterBizHealthIndicatorTests.java b/arklet/arklet-springboot-starter/src/test/java/com/alipay/sofa/serverless/arklet/spring/health/extension/indicator/MasterBizHealthIndicatorTests.java new file mode 100644 index 000000000..4f2ccb638 --- /dev/null +++ b/arklet/arklet-springboot-starter/src/test/java/com/alipay/sofa/serverless/arklet/spring/health/extension/indicator/MasterBizHealthIndicatorTests.java @@ -0,0 +1,64 @@ +/* + * 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.serverless.arklet.spring.health.extension.indicator; + +import com.alipay.sofa.serverless.arklet.core.ArkletComponentRegistry; +import com.alipay.sofa.serverless.arklet.core.health.HealthService; +import com.alipay.sofa.serverless.arklet.core.health.model.Health; +import com.alipay.sofa.serverless.arklet.spring.SpringbootBaseTest; +import com.alipay.sofa.serverless.arklet.spring.common.SpringbootUtil; +import com.alipay.sofa.serverless.arklet.springboot.starter.health.extension.indicator.MasterBizHealthIndicator; +import org.junit.Assert; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.springframework.boot.availability.ApplicationAvailability; +import org.springframework.boot.test.context.SpringBootTest; + +import java.util.Map; + +/** + * @author Lunarscave + */ +public class MasterBizHealthIndicatorTests extends SpringbootBaseTest { + + private static MasterBizHealthIndicator indicator; + + @BeforeClass + public static void before() { + indicator = new MasterBizHealthIndicator(); + indicator.setApplicationAvailability(SpringbootUtil.getBean(ApplicationAvailability.class)); + } + + @Test + public void testMasterBizIndicator() { + Health health = indicator.getHealthModel(new Health.HealthBuilder()); + Assert.assertNotNull(health); + Assert.assertTrue(health.getHealthData().get(indicator.getIndicatorId()) instanceof Map); + Map healthData = (Map) health.getHealthData().get(indicator.getIndicatorId()); + Assert.assertTrue(!healthData.isEmpty() && healthData.containsKey("readinessState")); + } + + @Test + public void testRegisterMasterBizIndicator() { + HealthService healthService = ArkletComponentRegistry.getHealthServiceInstance(); + Assert.assertNotNull(healthService.getIndicator(indicator.getIndicatorId())); + } + +} diff --git a/arklet/pom.xml b/arklet/pom.xml index b485d2e8d..0161e2e8a 100644 --- a/arklet/pom.xml +++ b/arklet/pom.xml @@ -6,10 +6,8 @@ arklet ${revision} 4.0.0 - pom - - 1.0.0-SNAPSHOT + 0.3-SNAPSHOT UTF-8 UTF-8 1.8 @@ -28,19 +26,28 @@ 1.2.9 1.18.22 4.13.1 + 4.8.1 1.5.0 - 2.1.3 + 2.2.3-SNAPSHOT + 2.5.12 + 6.4.5 + 3.12.0 + 16.0.1 + 1.2.69 ${project.groupId}:${project.artifactId} todo https://github.com/sofastack/sofa-serverless/arklet + pom + arklet-core arklet-springboot-starter + The Apache License, Version 2.0 @@ -57,6 +64,42 @@ ${sofa.ark.version} + + com.alipay.sofa + sofa-ark-springboot-starter + ${sofa.ark.version} + + + + org.springframework.boot + spring-boot-autoconfigure + ${spring.boot.version} + + + + org.springframework.boot + spring-boot-loader + ${spring.boot.version} + + + + org.springframework.boot + spring-boot-starter-actuator + ${spring.boot.version} + + + + org.springframework.boot + spring-boot-starter-test + ${spring.boot.version} + + + + org.springframework.boot + spring-boot-starter-logging + ${spring.boot.version} + + com.google.inject guice @@ -72,13 +115,13 @@ com.google.guava guava - 32.0.0 + ${guava.version} com.alibaba fastjson - 1.2.83 + ${fastjson.version} @@ -106,23 +149,48 @@ - junit - junit - ${junit.version} + com.github.oshi + oshi-core + ${oshi.version} + + + org.apache.commons + commons-lang3 + ${commons.lang3.version} + + + com.alipay.sofa.serverless arklet-core ${revision} + com.alipay.sofa.serverless - arklet-alipay-sofa-boot-starter + arklet-springboot-starter ${revision} + + + + junit + junit + ${junit.version} + test + + + + org.mockito + mockito-core + ${mockito.version} + test + + @@ -205,6 +273,9 @@ ${project.encoding} ${java.version} ${java.version} + + -parameters + @@ -222,25 +293,6 @@ - - - - - - - - - - - - - - - - - - - org.jacoco jacoco-maven-plugin @@ -373,4 +425,4 @@ - + \ No newline at end of file diff --git a/sofa-ark b/sofa-ark index e9c47721f..f1ce5a98f 160000 --- a/sofa-ark +++ b/sofa-ark @@ -1 +1 @@ -Subproject commit e9c47721fe9c0f0a7ef73e43c6571ad498b495de +Subproject commit f1ce5a98f4dac924fde73521eeca6cbbd476362d