diff --git a/naming/src/main/java/com/alibaba/nacos/naming/controllers/CatalogController.java b/naming/src/main/java/com/alibaba/nacos/naming/controllers/CatalogController.java index c741f4c4f28..33102388871 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/controllers/CatalogController.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/controllers/CatalogController.java @@ -45,6 +45,7 @@ * * @author nkorange */ +@Deprecated @RestController @RequestMapping(UtilsAndCommons.NACOS_NAMING_CONTEXT + UtilsAndCommons.NACOS_NAMING_CATALOG_CONTEXT) @ExtractorManager.Extractor(httpExtractor = NamingDefaultHttpParamExtractor.class) diff --git a/naming/src/main/java/com/alibaba/nacos/naming/controllers/ClusterController.java b/naming/src/main/java/com/alibaba/nacos/naming/controllers/ClusterController.java index 7ca877493b3..37229a327da 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/controllers/ClusterController.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/controllers/ClusterController.java @@ -45,6 +45,7 @@ * * @author nkorange */ +@Deprecated @RestController @RequestMapping(UtilsAndCommons.NACOS_NAMING_CONTEXT + UtilsAndCommons.NACOS_NAMING_CLUSTER_CONTEXT) @ExtractorManager.Extractor(httpExtractor = NamingDefaultHttpParamExtractor.class) diff --git a/naming/src/main/java/com/alibaba/nacos/naming/controllers/HealthController.java b/naming/src/main/java/com/alibaba/nacos/naming/controllers/HealthController.java index 7772a0e99a0..558b736e1bb 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/controllers/HealthController.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/controllers/HealthController.java @@ -63,6 +63,7 @@ * @author nanamikon * @since 0.8.0 */ +@Deprecated @RestController("namingHealthController") @RequestMapping(UtilsAndCommons.NACOS_NAMING_CONTEXT + UtilsAndCommons.NACOS_NAMING_HEALTH_CONTEXT) @ExtractorManager.Extractor(httpExtractor = NamingDefaultHttpParamExtractor.class) @@ -94,7 +95,7 @@ public ResponseEntity server() { @CanDistro @PutMapping(value = {"", "/instance"}) @Secured(action = ActionTypes.WRITE) - @Compatibility(apiType = ApiType.ADMIN_API) + @Compatibility(apiType = ApiType.ADMIN_API, alternatives = "PUT ${contextPath:nacos}/v3/admin/health/instance") public ResponseEntity update(HttpServletRequest request) throws NacosException { String healthyString = WebUtils.optional(request, HEALTHY_KEY, StringUtils.EMPTY); if (StringUtils.isBlank(healthyString)) { @@ -121,7 +122,7 @@ public ResponseEntity update(HttpServletRequest request) throws NacosException { * @return health checkers map */ @GetMapping("/checkers") - @Compatibility(apiType = ApiType.ADMIN_API) + @Compatibility(apiType = ApiType.ADMIN_API, alternatives = "GET ${contextPath:nacos}/v3/admin/health/checkers") public ResponseEntity checkers() { List> classes = HealthCheckType.getLoadedHealthCheckerClasses(); Map checkerMap = new HashMap<>(8); diff --git a/naming/src/main/java/com/alibaba/nacos/naming/controllers/InstanceController.java b/naming/src/main/java/com/alibaba/nacos/naming/controllers/InstanceController.java index 53d966ee0f3..b444791b454 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/controllers/InstanceController.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/controllers/InstanceController.java @@ -83,6 +83,7 @@ * * @author nkorange */ +@Deprecated @RestController @RequestMapping(UtilsAndCommons.NACOS_NAMING_CONTEXT + UtilsAndCommons.NACOS_NAMING_INSTANCE_CONTEXT) @ExtractorManager.Extractor(httpExtractor = NamingDefaultHttpParamExtractor.class) @@ -112,7 +113,7 @@ public InstanceController() { @PostMapping @TpsControl(pointName = "NamingInstanceRegister", name = "HttpNamingInstanceRegister") @Secured(action = ActionTypes.WRITE) - @Compatibility(apiType = ApiType.OPEN_API) + @Compatibility(apiType = ApiType.OPEN_API, alternatives = "POST ${contextPath:nacos}/v3/admin/ns/instance") public String register(HttpServletRequest request) throws Exception { final String namespaceId = WebUtils.optional(request, CommonParams.NAMESPACE_ID, @@ -142,7 +143,7 @@ public String register(HttpServletRequest request) throws Exception { @DeleteMapping @TpsControl(pointName = "NamingInstanceDeregister", name = "HttpNamingInstanceDeregister") @Secured(action = ActionTypes.WRITE) - @Compatibility(apiType = ApiType.OPEN_API) + @Compatibility(apiType = ApiType.OPEN_API, alternatives = "DELETE ${contextPath:nacos}/v3/admin/ns/instance") public String deregister(HttpServletRequest request) throws Exception { Instance instance = HttpRequestInstanceBuilder.newBuilder() .setDefaultInstanceEphemeral(switchDomain.isDefaultInstanceEphemeral()).setRequest(request).build(); @@ -197,7 +198,7 @@ public String update(HttpServletRequest request) throws Exception { @TpsControl(pointName = "NamingInstanceMetadataUpdate", name = "HttpNamingInstanceMetadataBatchUpdate") @Secured(action = ActionTypes.WRITE) @ExtractorManager.Extractor(httpExtractor = NamingInstanceMetadataBatchHttpParamExtractor.class) - @Compatibility(apiType = ApiType.ADMIN_API) + @Compatibility(apiType = ApiType.ADMIN_API, alternatives = "PUT ${contextPath:nacos}/v3/admin/ns/instance/metadata/batch") public ObjectNode batchUpdateInstanceMetadata(HttpServletRequest request) throws Exception { final String namespaceId = WebUtils.optional(request, CommonParams.NAMESPACE_ID, Constants.DEFAULT_NAMESPACE_ID); @@ -233,7 +234,7 @@ public ObjectNode batchUpdateInstanceMetadata(HttpServletRequest request) throws @TpsControl(pointName = "NamingInstanceMetadataUpdate", name = "HttpNamingInstanceMetadataBatchUpdate") @Secured(action = ActionTypes.WRITE) @ExtractorManager.Extractor(httpExtractor = NamingInstanceMetadataBatchHttpParamExtractor.class) - @Compatibility(apiType = ApiType.ADMIN_API) + @Compatibility(apiType = ApiType.ADMIN_API, alternatives = "DELETE ${contextPath:nacos}/v3/admin/ns/instance/metadata/batch") public ObjectNode batchDeleteInstanceMetadata(HttpServletRequest request) throws Exception { final String namespaceId = WebUtils.optional(request, CommonParams.NAMESPACE_ID, Constants.DEFAULT_NAMESPACE_ID); @@ -288,7 +289,7 @@ private List parseBatchInstances(String instances) { @CanDistro @PatchMapping @Secured(action = ActionTypes.WRITE) - @Compatibility(apiType = ApiType.ADMIN_API) + @Compatibility(apiType = ApiType.ADMIN_API, alternatives = "PUT ${contextPath:nacos}/v3/admin/ns/instance/partial") public String patch(HttpServletRequest request) throws Exception { String serviceName = WebUtils.required(request, CommonParams.SERVICE_NAME); NamingUtils.checkServiceNameFormat(serviceName); @@ -331,7 +332,7 @@ public String patch(HttpServletRequest request) throws Exception { @TpsControl(pointName = "NamingServiceSubscribe", name = "HttpNamingServiceSubscribe") @Secured(action = ActionTypes.READ) @ExtractorManager.Extractor(httpExtractor = NamingInstanceListHttpParamExtractor.class) - @Compatibility(apiType = ApiType.OPEN_API) + @Compatibility(apiType = ApiType.OPEN_API, alternatives = "GET ${contextPath:nacos}/v3/admin/ns/instance/list") public Object list(HttpServletRequest request) throws Exception { String namespaceId = WebUtils.optional(request, CommonParams.NAMESPACE_ID, Constants.DEFAULT_NAMESPACE_ID); @@ -360,7 +361,7 @@ public Object list(HttpServletRequest request) throws Exception { @GetMapping @TpsControl(pointName = "NamingInstanceQuery", name = "HttpNamingInstanceQuery") @Secured(action = ActionTypes.READ) - @Compatibility(apiType = ApiType.OPEN_API) + @Compatibility(apiType = ApiType.OPEN_API, alternatives = "GET ${contextPath:nacos}/v3/admin/ns/instance") public ObjectNode detail(HttpServletRequest request) throws Exception { String namespaceId = WebUtils.optional(request, CommonParams.NAMESPACE_ID, Constants.DEFAULT_NAMESPACE_ID); diff --git a/naming/src/main/java/com/alibaba/nacos/naming/controllers/OperatorController.java b/naming/src/main/java/com/alibaba/nacos/naming/controllers/OperatorController.java index 5236fb1731b..360629f3458 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/controllers/OperatorController.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/controllers/OperatorController.java @@ -53,6 +53,7 @@ * * @author nkorange */ +@Deprecated @RestController @RequestMapping({UtilsAndCommons.NACOS_NAMING_CONTEXT + UtilsAndCommons.NACOS_NAMING_OPERATOR_CONTEXT, UtilsAndCommons.NACOS_NAMING_CONTEXT + "/ops"}) @@ -120,7 +121,7 @@ public ObjectNode pushState(@RequestParam(required = false) boolean detail, * @return switchDomain */ @GetMapping("/switches") - @Compatibility(apiType = ApiType.ADMIN_API) + @Compatibility(apiType = ApiType.ADMIN_API, alternatives = "GET ${contextPath:nacos}/v3/admin/ns/ops/switches") public SwitchDomain switches(HttpServletRequest request) { return switchDomain; } @@ -136,7 +137,7 @@ public SwitchDomain switches(HttpServletRequest request) { */ @Secured(resource = "naming/switches", action = ActionTypes.WRITE) @PutMapping("/switches") - @Compatibility(apiType = ApiType.ADMIN_API) + @Compatibility(apiType = ApiType.ADMIN_API, alternatives = "PUT ${contextPath:nacos}/v3/admin/ns/ops/switches") public String updateSwitch(@RequestParam(required = false) boolean debug, @RequestParam String entry, @RequestParam String value) throws Exception { @@ -152,7 +153,7 @@ public String updateSwitch(@RequestParam(required = false) boolean debug, @Reque * @return metrics information */ @GetMapping("/metrics") - @Compatibility(apiType = ApiType.OPEN_API) + @Compatibility(apiType = ApiType.ADMIN_API, alternatives = "GET ${contextPath:nacos}/v3/admin/ns/ops/metrics") public ObjectNode metrics(HttpServletRequest request) { boolean onlyStatus = Boolean.parseBoolean(WebUtils.optional(request, "onlyStatus", "true")); ObjectNode result = JacksonUtils.createEmptyJsonNode(); @@ -198,7 +199,7 @@ public ObjectNode metrics(HttpServletRequest request) { } @GetMapping("/distro/client") - @Compatibility(apiType = ApiType.ADMIN_API) + @Compatibility(apiType = ApiType.ADMIN_API, alternatives = "GET ${contextPath:nacos}/v3/admin/ns/client/distro") public ObjectNode getResponsibleServer4Client(@RequestParam String ip, @RequestParam String port) { ObjectNode result = JacksonUtils.createEmptyJsonNode(); String tag = ip + InternetAddressUtil.IP_PORT_SPLITER + port; @@ -207,7 +208,7 @@ public ObjectNode getResponsibleServer4Client(@RequestParam String ip, @RequestP } @PutMapping("/log") - @Compatibility(apiType = ApiType.ADMIN_API) + @Compatibility(apiType = ApiType.ADMIN_API, alternatives = "PUT ${contextPath:nacos}/v3/admin/ns/ops/log") public String setLogLevel(@RequestParam String logName, @RequestParam String logLevel) { Loggers.setLogLevel(logName, logLevel); return "ok"; diff --git a/naming/src/main/java/com/alibaba/nacos/naming/controllers/ServiceController.java b/naming/src/main/java/com/alibaba/nacos/naming/controllers/ServiceController.java index ac47b3ef56a..ec6ff5a00b1 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/controllers/ServiceController.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/controllers/ServiceController.java @@ -73,6 +73,7 @@ * * @author nkorange */ +@Deprecated @RestController @RequestMapping(UtilsAndCommons.NACOS_NAMING_CONTEXT + UtilsAndCommons.NACOS_NAMING_SERVICE_CONTEXT) @ExtractorManager.Extractor(httpExtractor = NamingDefaultHttpParamExtractor.class) @@ -151,7 +152,7 @@ public String remove(@RequestParam(defaultValue = Constants.DEFAULT_NAMESPACE_ID @GetMapping @TpsControl(pointName = "NamingServiceQuery", name = "HttpNamingServiceQuery") @Secured(action = ActionTypes.READ) - @Compatibility(apiType = ApiType.OPEN_API) + @Compatibility(apiType = ApiType.OPEN_API, alternatives = "GET ${contextPath:nacos}/v3/admin/ns/service") public ObjectNode detail(@RequestParam(defaultValue = Constants.DEFAULT_NAMESPACE_ID) String namespaceId, @RequestParam String serviceName) throws NacosException { return getServiceOperator().queryService(namespaceId, serviceName); @@ -167,7 +168,7 @@ public ObjectNode detail(@RequestParam(defaultValue = Constants.DEFAULT_NAMESPAC @GetMapping("/list") @TpsControl(pointName = "NamingServiceListQuery", name = "HttpNamingServiceListQuery") @Secured(action = ActionTypes.READ) - @Compatibility(apiType = ApiType.OPEN_API) + @Compatibility(apiType = ApiType.OPEN_API, alternatives = "GET ${contextPath:nacos}/v3/admin/ns/service/list") public ObjectNode list(HttpServletRequest request) throws Exception { final int pageNo = NumberUtils.toInt(WebUtils.required(request, "pageNo")); final int pageSize = NumberUtils.toInt(WebUtils.required(request, "pageSize")); @@ -220,7 +221,7 @@ public String update(HttpServletRequest request) throws Exception { */ @RequestMapping("/names") @Secured(action = ActionTypes.READ) - @Compatibility(apiType = ApiType.ADMIN_API) + @Compatibility(apiType = ApiType.ADMIN_API, alternatives = "GET ${contextPath:nacos}/v3/admin/ns/service/names") public ObjectNode searchService(@RequestParam(defaultValue = StringUtils.EMPTY) String namespaceId, @RequestParam(defaultValue = StringUtils.EMPTY) String expr) throws NacosException { Map> serviceNameMap = new HashMap<>(16); diff --git a/naming/src/main/java/com/alibaba/nacos/naming/controllers/v2/CatalogControllerV2.java b/naming/src/main/java/com/alibaba/nacos/naming/controllers/v2/CatalogControllerV2.java index 5006fdba4dd..96d1aba6904 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/controllers/v2/CatalogControllerV2.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/controllers/v2/CatalogControllerV2.java @@ -45,6 +45,7 @@ * @author Weizhanâ–ªYun * @date 2023/1/14 19:54 */ +@Deprecated @RestController @RequestMapping(UtilsAndCommons.DEFAULT_NACOS_NAMING_CONTEXT_V2 + UtilsAndCommons.NACOS_NAMING_CATALOG_CONTEXT) @ExtractorManager.Extractor(httpExtractor = NamingDefaultHttpParamExtractor.class) diff --git a/naming/src/main/java/com/alibaba/nacos/naming/controllers/v2/ClientInfoControllerV2.java b/naming/src/main/java/com/alibaba/nacos/naming/controllers/v2/ClientInfoControllerV2.java index e02e85b85ba..3a339324c50 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/controllers/v2/ClientInfoControllerV2.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/controllers/v2/ClientInfoControllerV2.java @@ -22,23 +22,12 @@ import com.alibaba.nacos.api.model.v2.ErrorCode; import com.alibaba.nacos.api.model.v2.Result; import com.alibaba.nacos.auth.annotation.Secured; -import com.alibaba.nacos.common.utils.JacksonUtils; import com.alibaba.nacos.core.controller.compatibility.Compatibility; import com.alibaba.nacos.core.paramcheck.ExtractorManager; -import com.alibaba.nacos.core.remote.Connection; -import com.alibaba.nacos.core.remote.ConnectionManager; -import com.alibaba.nacos.core.remote.ConnectionMeta; -import com.alibaba.nacos.naming.core.v2.client.Client; -import com.alibaba.nacos.naming.core.v2.client.impl.ConnectionBasedClient; -import com.alibaba.nacos.naming.core.v2.client.impl.IpPortBasedClient; +import com.alibaba.nacos.naming.core.ClientService; import com.alibaba.nacos.naming.core.v2.client.manager.ClientManager; -import com.alibaba.nacos.naming.core.v2.index.ClientServiceIndexesManager; -import com.alibaba.nacos.naming.core.v2.pojo.BatchInstancePublishInfo; -import com.alibaba.nacos.naming.core.v2.pojo.InstancePublishInfo; -import com.alibaba.nacos.naming.core.v2.pojo.Service; import com.alibaba.nacos.naming.misc.UtilsAndCommons; import com.alibaba.nacos.naming.paramcheck.NamingDefaultHttpParamExtractor; -import com.alibaba.nacos.naming.pojo.Subscriber; import com.alibaba.nacos.plugin.auth.constant.ActionTypes; import com.alibaba.nacos.plugin.auth.constant.ApiType; import com.fasterxml.jackson.databind.node.ObjectNode; @@ -48,10 +37,7 @@ import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; -import java.util.ArrayList; -import java.util.Collection; import java.util.List; -import java.util.Objects; /** * ClientInfoControllerV2. @@ -59,7 +45,7 @@ * @author dongyafei * @date 2022/9/20 */ - +@Deprecated @NacosApi @RestController @RequestMapping(UtilsAndCommons.DEFAULT_NACOS_NAMING_CONTEXT_V2 + UtilsAndCommons.NACOS_NAMING_CLIENT_CONTEXT) @@ -68,15 +54,11 @@ public class ClientInfoControllerV2 { private final ClientManager clientManager; - private final ConnectionManager connectionManager; - - private final ClientServiceIndexesManager clientServiceIndexesManager; + private final ClientService clientServiceV2Impl; - public ClientInfoControllerV2(ClientManager clientManager, ConnectionManager connectionManager, - ClientServiceIndexesManager clientServiceIndexesManager) { + public ClientInfoControllerV2(ClientManager clientManager, ClientService clientServiceV2Impl) { this.clientManager = clientManager; - this.connectionManager = connectionManager; - this.clientServiceIndexesManager = clientServiceIndexesManager; + this.clientServiceV2Impl = clientServiceV2Impl; } /** @@ -84,9 +66,9 @@ public ClientInfoControllerV2(ClientManager clientManager, ConnectionManager con */ @GetMapping("/list") @Secured(action = ActionTypes.READ, resource = "nacos/admin") - @Compatibility(apiType = ApiType.ADMIN_API) + @Compatibility(apiType = ApiType.ADMIN_API, alternatives = "GET ${contextPath:nacos}/v3/admin/ns/client/list") public Result> getClientList() { - return Result.success(new ArrayList<>(clientManager.allClientId())); + return Result.success(clientServiceV2Impl.getClientList()); } /** @@ -96,36 +78,10 @@ public Result> getClientList() { */ @GetMapping() @Secured(action = ActionTypes.READ, resource = "nacos/admin") - @Compatibility(apiType = ApiType.ADMIN_API) + @Compatibility(apiType = ApiType.ADMIN_API, alternatives = "GET ${contextPath:nacos}/v3/admin/ns/client") public Result getClientDetail(@RequestParam("clientId") String clientId) throws NacosApiException { checkClientId(clientId); - Client client = clientManager.getClient(clientId); - - ObjectNode result = JacksonUtils.createEmptyJsonNode(); - result.put("clientId", client.getClientId()); - result.put("ephemeral", client.isEphemeral()); - result.put("lastUpdatedTime", client.getLastUpdatedTime()); - - if (client instanceof ConnectionBasedClient) { - // 2.x client - result.put("clientType", "connection"); - Connection connection = connectionManager.getConnection(clientId); - ConnectionMeta connectionMetaInfo = connection.getMetaInfo(); - result.put("connectType", connectionMetaInfo.getConnectType()); - result.put("appName", connectionMetaInfo.getAppName()); - result.put("version", connectionMetaInfo.getVersion()); - result.put("clientIp", connectionMetaInfo.getClientIp()); - result.put("clientPort", clientId.substring(clientId.lastIndexOf('_') + 1)); - } else if (client instanceof IpPortBasedClient) { - // 1.x client - result.put("clientType", "ipPort"); - IpPortBasedClient ipPortBasedClient = (IpPortBasedClient) client; - String responsibleId = ipPortBasedClient.getResponsibleId(); - int idx = responsibleId.lastIndexOf(':'); - result.put("clientIp", responsibleId.substring(0, idx)); - result.put("clientPort", responsibleId.substring(idx + 1)); - } - return Result.success(result); + return Result.success(clientServiceV2Impl.getClientDetail(clientId)); } /** @@ -135,42 +91,12 @@ public Result getClientDetail(@RequestParam("clientId") String clien */ @GetMapping("/publish/list") @Secured(action = ActionTypes.READ, resource = "nacos/admin") - @Compatibility(apiType = ApiType.ADMIN_API) + @Compatibility(apiType = ApiType.ADMIN_API, alternatives = "GET ${contextPath:nacos}/v3/admin/ns/client/publish/list") public Result> getPublishedServiceList(@RequestParam("clientId") String clientId) throws NacosApiException { checkClientId(clientId); - Client client = clientManager.getClient(clientId); - Collection allPublishedService = client.getAllPublishedService(); - ArrayList res = new ArrayList<>(); - for (Service service : allPublishedService) { - InstancePublishInfo instancePublishInfo = client.getInstancePublishInfo(service); - if (instancePublishInfo instanceof BatchInstancePublishInfo) { - List instancePublishInfos = ((BatchInstancePublishInfo) instancePublishInfo).getInstancePublishInfos(); - for (InstancePublishInfo publishInfo : instancePublishInfos) { - res.add(wrapSingleInstanceNode(publishInfo, service)); - } - } else { - res.add(wrapSingleInstanceNode(instancePublishInfo, service)); - } - } - return Result.success(res); - } - - private ObjectNode wrapSingleInstanceNode(InstancePublishInfo instancePublishInfo, Service service) { - ObjectNode item = JacksonUtils.createEmptyJsonNode(); - item.put("namespace", service.getNamespace()); - item.put("group", service.getGroup()); - item.put("serviceName", service.getName()); - item.set("registeredInstance", wrapSingleInstance(instancePublishInfo)); - return item; - } - - private ObjectNode wrapSingleInstance(InstancePublishInfo instancePublishInfo) { - ObjectNode instanceInfo = JacksonUtils.createEmptyJsonNode(); - instanceInfo.put("ip", instancePublishInfo.getIp()); - instanceInfo.put("port", instancePublishInfo.getPort()); - instanceInfo.put("cluster", instancePublishInfo.getCluster()); - return instanceInfo; + + return Result.success(clientServiceV2Impl.getPublishedServiceList(clientId)); } /** @@ -180,27 +106,12 @@ private ObjectNode wrapSingleInstance(InstancePublishInfo instancePublishInfo) { */ @GetMapping("/subscribe/list") @Secured(action = ActionTypes.READ, resource = "nacos/admin") - @Compatibility(apiType = ApiType.ADMIN_API) + @Compatibility(apiType = ApiType.ADMIN_API, alternatives = "GET ${contextPath:nacos}/v3/admin/ns/client/subscribe/list") public Result> getSubscribeServiceList(@RequestParam("clientId") String clientId) throws NacosApiException { checkClientId(clientId); - Client client = clientManager.getClient(clientId); - Collection allSubscribeService = client.getAllSubscribeService(); - ArrayList res = new ArrayList<>(); - for (Service service : allSubscribeService) { - ObjectNode item = JacksonUtils.createEmptyJsonNode(); - item.put("namespace", service.getNamespace()); - item.put("group", service.getGroup()); - item.put("serviceName", service.getName()); - Subscriber subscriber = client.getSubscriber(service); - ObjectNode subscriberInfo = JacksonUtils.createEmptyJsonNode(); - subscriberInfo.put("app", subscriber.getApp()); - subscriberInfo.put("agent", subscriber.getAgent()); - subscriberInfo.put("addr", subscriber.getAddrStr()); - item.set("subscriberInfo", subscriberInfo); - res.add(item); - } - return Result.success(res); + + return Result.success(clientServiceV2Impl.getSubscribeServiceList(clientId)); } /** @@ -216,38 +127,14 @@ public Result> getSubscribeServiceList(@RequestParam("clientId" */ @GetMapping("/service/publisher/list") @Secured(action = ActionTypes.READ, resource = "nacos/admin") - @Compatibility(apiType = ApiType.ADMIN_API) + @Compatibility(apiType = ApiType.ADMIN_API, alternatives = "GET ${contextPath:nacos}/v3/admin/ns/client/service/publisher/list") public Result> getPublishedClientList( @RequestParam(value = "namespaceId", required = false, defaultValue = Constants.DEFAULT_NAMESPACE_ID) String namespaceId, @RequestParam(value = "groupName", required = false, defaultValue = Constants.DEFAULT_GROUP) String groupName, @RequestParam(value = "ephemeral", required = false, defaultValue = "true") Boolean ephemeral, @RequestParam("serviceName") String serviceName, @RequestParam(value = "ip", required = false) String ip, @RequestParam(value = "port", required = false) Integer port) { - Service service = Service.newService(namespaceId, groupName, serviceName, ephemeral); - Collection allClientsRegisteredService = clientServiceIndexesManager - .getAllClientsRegisteredService(service); - ArrayList res = new ArrayList<>(); - for (String clientId : allClientsRegisteredService) { - Client client = clientManager.getClient(clientId); - InstancePublishInfo instancePublishInfo = client.getInstancePublishInfo(service); - if (instancePublishInfo instanceof BatchInstancePublishInfo) { - List list = ((BatchInstancePublishInfo) instancePublishInfo).getInstancePublishInfos(); - for (InstancePublishInfo info : list) { - if (!Objects.equals(info.getIp(), ip) || !Objects - .equals(port, info.getPort())) { - continue; - } - res.add(wrapSingleInstance(info).put("clientId", clientId)); - } - } else { - if (!Objects.equals(instancePublishInfo.getIp(), ip) || !Objects - .equals(port, instancePublishInfo.getPort())) { - continue; - } - res.add(wrapSingleInstance(instancePublishInfo).put("clientId", clientId)); - } - } - return Result.success(res); + return Result.success(clientServiceV2Impl.getPublishedClientList(namespaceId, groupName, serviceName, ephemeral, ip, port)); } /** @@ -263,30 +150,14 @@ public Result> getPublishedClientList( */ @GetMapping("/service/subscriber/list") @Secured(action = ActionTypes.READ, resource = "nacos/admin") - @Compatibility(apiType = ApiType.ADMIN_API) + @Compatibility(apiType = ApiType.ADMIN_API, alternatives = "GET ${contextPath:nacos}/v3/admin/ns/client/service/subscriber/list") public Result> getSubscribeClientList( @RequestParam(value = "namespaceId", required = false, defaultValue = Constants.DEFAULT_NAMESPACE_ID) String namespaceId, @RequestParam(value = "groupName", required = false, defaultValue = Constants.DEFAULT_GROUP) String groupName, @RequestParam(value = "ephemeral", required = false, defaultValue = "true") Boolean ephemeral, @RequestParam("serviceName") String serviceName, @RequestParam(value = "ip", required = false) String ip, @RequestParam(value = "port", required = false) Integer port) { - Service service = Service.newService(namespaceId, groupName, serviceName, ephemeral); - Collection allClientsSubscribeService = clientServiceIndexesManager - .getAllClientsSubscribeService(service); - ArrayList res = new ArrayList<>(); - for (String clientId : allClientsSubscribeService) { - Client client = clientManager.getClient(clientId); - Subscriber subscriber = client.getSubscriber(service); - if (!Objects.equals(subscriber.getIp(), ip) || !Objects.equals(port, subscriber.getPort())) { - continue; - } - ObjectNode item = JacksonUtils.createEmptyJsonNode(); - item.put("clientId", clientId); - item.put("ip", subscriber.getIp()); - item.put("port", subscriber.getPort()); - res.add(item); - } - return Result.success(res); + return Result.success(clientServiceV2Impl.getSubscribeClientList(namespaceId, groupName, serviceName, ephemeral, ip, port)); } private void checkClientId(String clientId) throws NacosApiException { diff --git a/naming/src/main/java/com/alibaba/nacos/naming/controllers/v2/HealthControllerV2.java b/naming/src/main/java/com/alibaba/nacos/naming/controllers/v2/HealthControllerV2.java index 73981f4dd1f..099f27b684d 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/controllers/v2/HealthControllerV2.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/controllers/v2/HealthControllerV2.java @@ -40,6 +40,7 @@ * @author dongyafei * @date 2022/9/15 */ +@Deprecated @NacosApi @RestController @RequestMapping(UtilsAndCommons.DEFAULT_NACOS_NAMING_CONTEXT_V2 + UtilsAndCommons.NACOS_NAMING_HEALTH_CONTEXT) @@ -58,7 +59,7 @@ public class HealthControllerV2 { @CanDistro @PutMapping(value = {"", "/instance"}) @Secured(action = ActionTypes.WRITE) - @Compatibility(apiType = ApiType.ADMIN_API) + @Compatibility(apiType = ApiType.ADMIN_API, alternatives = "PUT ${contextPath:nacos}/v3/admin/ns/health") public Result update(UpdateHealthForm updateHealthForm) throws NacosException { updateHealthForm.validate(); healthOperatorV2.updateHealthStatusForPersistentInstance(updateHealthForm.getNamespaceId(), buildCompositeServiceName(updateHealthForm), diff --git a/naming/src/main/java/com/alibaba/nacos/naming/controllers/v2/InstanceControllerV2.java b/naming/src/main/java/com/alibaba/nacos/naming/controllers/v2/InstanceControllerV2.java index 60a9b2f3cdd..0beb98bc6f6 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/controllers/v2/InstanceControllerV2.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/controllers/v2/InstanceControllerV2.java @@ -90,6 +90,7 @@ * * @author hujun */ +@Deprecated @NacosApi @RestController @RequestMapping(UtilsAndCommons.DEFAULT_NACOS_NAMING_CONTEXT_V2 + UtilsAndCommons.NACOS_NAMING_INSTANCE_CONTEXT) @@ -109,7 +110,7 @@ public class InstanceControllerV2 { @PostMapping @TpsControl(pointName = "NamingInstanceRegister", name = "HttpNamingInstanceRegister") @Secured(action = ActionTypes.WRITE) - @Compatibility(apiType = ApiType.ADMIN_API) + @Compatibility(apiType = ApiType.ADMIN_API, alternatives = "POST ${contextPath:nacos}/v3/admin/ns/instance") public Result register(InstanceForm instanceForm) throws NacosException { // check param instanceForm.validate(); @@ -132,7 +133,7 @@ public Result register(InstanceForm instanceForm) throws NacosException @DeleteMapping @TpsControl(pointName = "NamingInstanceDeregister", name = "HttpNamingInstanceDeregister") @Secured(action = ActionTypes.WRITE) - @Compatibility(apiType = ApiType.ADMIN_API) + @Compatibility(apiType = ApiType.ADMIN_API, alternatives = "DELETE ${contextPath:nacos}/v3/admin/ns/instance") public Result deregister(InstanceForm instanceForm) throws NacosException { // check param instanceForm.validate(); @@ -155,7 +156,7 @@ public Result deregister(InstanceForm instanceForm) throws NacosExceptio @PutMapping @TpsControl(pointName = "NamingInstanceUpdate", name = "HttpNamingInstanceUpdate") @Secured(action = ActionTypes.WRITE) - @Compatibility(apiType = ApiType.ADMIN_API) + @Compatibility(apiType = ApiType.ADMIN_API, alternatives = "PUT ${contextPath:nacos}/v3/admin/ns/instance") public Result update(InstanceForm instanceForm) throws NacosException { // check param instanceForm.validate(); @@ -179,7 +180,7 @@ public Result update(InstanceForm instanceForm) throws NacosException { @TpsControl(pointName = "NamingInstanceMetadataUpdate", name = "HttpNamingInstanceMetadataBatchUpdate") @Secured(action = ActionTypes.WRITE) @ExtractorManager.Extractor(httpExtractor = NamingInstanceMetadataBatchHttpParamExtractor.class) - @Compatibility(apiType = ApiType.ADMIN_API) + @Compatibility(apiType = ApiType.ADMIN_API, alternatives = "PUT ${contextPath:nacos}/v3/admin/ns/instance/metadata/batch") public Result batchUpdateInstanceMetadata(InstanceMetadataBatchOperationForm form) throws NacosException { form.validate(); @@ -204,7 +205,7 @@ public Result batchUpdateInstanceMetadata(Inst @TpsControl(pointName = "NamingInstanceMetadataUpdate", name = "HttpNamingInstanceMetadataBatchUpdate") @Secured(action = ActionTypes.WRITE) @ExtractorManager.Extractor(httpExtractor = NamingInstanceMetadataBatchHttpParamExtractor.class) - @Compatibility(apiType = ApiType.ADMIN_API) + @Compatibility(apiType = ApiType.ADMIN_API, alternatives = "DELETE ${contextPath:nacos}/v3/admin/ns/instance/metadata/batch") public Result batchDeleteInstanceMetadata(InstanceMetadataBatchOperationForm form) throws NacosException { form.validate(); @@ -257,7 +258,7 @@ private List parseBatchInstances(String instances) { @CanDistro @PatchMapping @Secured(action = ActionTypes.WRITE) - @Compatibility(apiType = ApiType.ADMIN_API) + @Compatibility(apiType = ApiType.ADMIN_API, alternatives = "PUT ${contextPath:nacos}/v3/admin/ns/instance/partial") public String patch(@RequestParam(defaultValue = Constants.DEFAULT_NAMESPACE_ID) String namespaceId, @RequestParam String serviceName, @RequestParam String ip, @RequestParam(defaultValue = UtilsAndCommons.DEFAULT_CLUSTER_NAME) String cluster, @@ -298,7 +299,7 @@ public String patch(@RequestParam(defaultValue = Constants.DEFAULT_NAMESPACE_ID) @TpsControl(pointName = "NamingServiceSubscribe", name = "HttpNamingServiceSubscribe") @Secured(action = ActionTypes.READ) @ExtractorManager.Extractor(httpExtractor = NamingInstanceListHttpParamExtractor.class) - @Compatibility(apiType = ApiType.ADMIN_API) + @Compatibility(apiType = ApiType.ADMIN_API, alternatives = "GET ${contextPath:nacos}/v3/admin/ns/instance/list") public Result list( @RequestParam(value = "namespaceId", defaultValue = Constants.DEFAULT_NAMESPACE_ID) String namespaceId, @RequestParam(value = "groupName", defaultValue = Constants.DEFAULT_GROUP) String groupName, @@ -334,7 +335,7 @@ public Result list( @GetMapping @TpsControl(pointName = "NamingInstanceQuery", name = "HttpNamingInstanceQuery") @Secured(action = ActionTypes.READ) - @Compatibility(apiType = ApiType.ADMIN_API) + @Compatibility(apiType = ApiType.ADMIN_API, alternatives = "GET ${contextPath:nacos}/v3/admin/ns/instance") public Result detail( @RequestParam(value = "namespaceId", defaultValue = Constants.DEFAULT_NAMESPACE_ID) String namespaceId, @RequestParam(value = "groupName", defaultValue = Constants.DEFAULT_GROUP) String groupName, diff --git a/naming/src/main/java/com/alibaba/nacos/naming/controllers/v2/OperatorControllerV2.java b/naming/src/main/java/com/alibaba/nacos/naming/controllers/v2/OperatorControllerV2.java index 99cc59e868f..73e76693dd5 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/controllers/v2/OperatorControllerV2.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/controllers/v2/OperatorControllerV2.java @@ -23,20 +23,14 @@ import com.alibaba.nacos.auth.annotation.Secured; import com.alibaba.nacos.core.controller.compatibility.Compatibility; import com.alibaba.nacos.core.paramcheck.ExtractorManager; -import com.alibaba.nacos.naming.cluster.ServerStatusManager; -import com.alibaba.nacos.naming.constants.ClientConstants; -import com.alibaba.nacos.naming.core.v2.client.impl.IpPortBasedClient; -import com.alibaba.nacos.naming.core.v2.client.manager.ClientManager; +import com.alibaba.nacos.naming.core.Operator; import com.alibaba.nacos.naming.misc.SwitchDomain; -import com.alibaba.nacos.naming.misc.SwitchManager; import com.alibaba.nacos.naming.misc.UtilsAndCommons; import com.alibaba.nacos.naming.model.form.UpdateSwitchForm; import com.alibaba.nacos.naming.model.vo.MetricsInfoVo; -import com.alibaba.nacos.naming.monitor.MetricsMonitor; import com.alibaba.nacos.naming.paramcheck.NamingDefaultHttpParamExtractor; import com.alibaba.nacos.plugin.auth.constant.ActionTypes; import com.alibaba.nacos.plugin.auth.constant.ApiType; -import com.alibaba.nacos.sys.env.EnvUtil; import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PutMapping; @@ -44,14 +38,13 @@ import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; -import java.util.Collection; - /** * OperatorControllerV2. * * @author dongyafei * @date 2022/9/8 */ +@Deprecated @NacosApi @RestController @RequestMapping({UtilsAndCommons.DEFAULT_NACOS_NAMING_CONTEXT_V2 + UtilsAndCommons.NACOS_NAMING_OPERATOR_CONTEXT, @@ -59,20 +52,10 @@ @ExtractorManager.Extractor(httpExtractor = NamingDefaultHttpParamExtractor.class) public class OperatorControllerV2 { - private final SwitchManager switchManager; - - private final ServerStatusManager serverStatusManager; - - private final SwitchDomain switchDomain; + private final Operator operatorV2Impl; - private final ClientManager clientManager; - - public OperatorControllerV2(SwitchManager switchManager, ServerStatusManager serverStatusManager, - SwitchDomain switchDomain, ClientManager clientManager) { - this.switchManager = switchManager; - this.serverStatusManager = serverStatusManager; - this.switchDomain = switchDomain; - this.clientManager = clientManager; + public OperatorControllerV2(Operator operatorV2Impl) { + this.operatorV2Impl = operatorV2Impl; } /** @@ -81,9 +64,9 @@ public OperatorControllerV2(SwitchManager switchManager, ServerStatusManager ser * @return switchDomain */ @GetMapping("/switches") - @Compatibility(apiType = ApiType.ADMIN_API) + @Compatibility(apiType = ApiType.ADMIN_API, alternatives = "GET ${contextPath:nacos}/v3/admin/ns/ops/switches") public Result switches() { - return Result.success(switchDomain); + return Result.success(operatorV2Impl.switches()); } /** @@ -95,17 +78,17 @@ public Result switches() { */ @Secured(resource = "naming/switches", action = ActionTypes.WRITE) @PutMapping("/switches") - @Compatibility(apiType = ApiType.ADMIN_API) + @Compatibility(apiType = ApiType.ADMIN_API, alternatives = "PUT ${contextPath:nacos}/v3/admin/ns/ops/switches") public Result updateSwitch(UpdateSwitchForm updateSwitchForm) throws Exception { updateSwitchForm.validate(); try { - switchManager.update(updateSwitchForm.getEntry(), updateSwitchForm.getValue(), updateSwitchForm.getDebug()); + operatorV2Impl.updateSwitch(updateSwitchForm.getEntry(), updateSwitchForm.getValue(), updateSwitchForm.getDebug()); + + return Result.success("ok"); } catch (IllegalArgumentException e) { throw new NacosApiException(HttpStatus.INTERNAL_SERVER_ERROR.value(), ErrorCode.SERVER_ERROR, e.getMessage()); } - - return Result.success("ok"); } /** @@ -115,46 +98,9 @@ public Result updateSwitch(UpdateSwitchForm updateSwitchForm) throws Exc * @return metrics information */ @GetMapping("/metrics") - @Compatibility(apiType = ApiType.ADMIN_API) + @Compatibility(apiType = ApiType.ADMIN_API, alternatives = "GET ${contextPath:nacos}/v3/admin/ns/ops/metrics") public Result metrics( @RequestParam(value = "onlyStatus", required = false, defaultValue = "true") Boolean onlyStatus) { - MetricsInfoVo metricsInfoVo = new MetricsInfoVo(); - metricsInfoVo.setStatus(serverStatusManager.getServerStatus().name()); - if (onlyStatus) { - return Result.success(metricsInfoVo); - } - - int connectionBasedClient = 0; - int ephemeralIpPortClient = 0; - int persistentIpPortClient = 0; - int responsibleClientCount = 0; - Collection allClientId = clientManager.allClientId(); - for (String clientId : allClientId) { - if (clientId.contains(IpPortBasedClient.ID_DELIMITER)) { - if (clientId.endsWith(ClientConstants.PERSISTENT_SUFFIX)) { - persistentIpPortClient += 1; - } else { - ephemeralIpPortClient += 1; - } - } else { - connectionBasedClient += 1; - } - if (clientManager.isResponsibleClient(clientManager.getClient(clientId))) { - responsibleClientCount += 1; - } - } - - metricsInfoVo.setServiceCount(MetricsMonitor.getDomCountMonitor().get()); - metricsInfoVo.setInstanceCount(MetricsMonitor.getIpCountMonitor().get()); - metricsInfoVo.setSubscribeCount(MetricsMonitor.getSubscriberCount().get()); - metricsInfoVo.setClientCount(allClientId.size()); - metricsInfoVo.setConnectionBasedClientCount(connectionBasedClient); - metricsInfoVo.setEphemeralIpPortClientCount(ephemeralIpPortClient); - metricsInfoVo.setPersistentIpPortClientCount(persistentIpPortClient); - metricsInfoVo.setResponsibleClientCount(responsibleClientCount); - metricsInfoVo.setCpu(EnvUtil.getCpu()); - metricsInfoVo.setLoad(EnvUtil.getLoad()); - metricsInfoVo.setMem(EnvUtil.getMem()); - return Result.success(metricsInfoVo); + return Result.success(operatorV2Impl.metrics(onlyStatus)); } } diff --git a/naming/src/main/java/com/alibaba/nacos/naming/controllers/v2/ServiceControllerV2.java b/naming/src/main/java/com/alibaba/nacos/naming/controllers/v2/ServiceControllerV2.java index 859f46359b8..d1cf9301fe9 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/controllers/v2/ServiceControllerV2.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/controllers/v2/ServiceControllerV2.java @@ -66,6 +66,7 @@ * * @author nkorange */ +@Deprecated @NacosApi @RestController @RequestMapping(UtilsAndCommons.DEFAULT_NACOS_NAMING_CONTEXT_V2 + UtilsAndCommons.NACOS_NAMING_SERVICE_CONTEXT) @@ -87,7 +88,7 @@ public ServiceControllerV2(ServiceOperatorV2Impl serviceOperatorV2, SelectorMana @PostMapping() @TpsControl(pointName = "NamingServiceRegister", name = "HttpNamingServiceRegister") @Secured(action = ActionTypes.WRITE) - @Compatibility(apiType = ApiType.ADMIN_API) + @Compatibility(apiType = ApiType.ADMIN_API, alternatives = "POST ${contextPath:nacos}/v3/admin/ns/service") public Result create(ServiceForm serviceForm) throws Exception { serviceForm.validate(); ServiceMetadata serviceMetadata = new ServiceMetadata(); @@ -109,7 +110,7 @@ public Result create(ServiceForm serviceForm) throws Exception { @DeleteMapping() @TpsControl(pointName = "NamingServiceDeregister", name = "HttpNamingServiceDeregister") @Secured(action = ActionTypes.WRITE) - @Compatibility(apiType = ApiType.ADMIN_API) + @Compatibility(apiType = ApiType.ADMIN_API, alternatives = "DELETE ${contextPath:nacos}/v3/admin/ns/service") public Result remove( @RequestParam(value = "namespaceId", defaultValue = Constants.DEFAULT_NAMESPACE_ID) String namespaceId, @RequestParam("serviceName") String serviceName, @@ -127,7 +128,7 @@ public Result remove( @GetMapping() @TpsControl(pointName = "NamingServiceQuery", name = "HttpNamingServiceQuery") @Secured(action = ActionTypes.READ) - @Compatibility(apiType = ApiType.ADMIN_API) + @Compatibility(apiType = ApiType.ADMIN_API, alternatives = "GET ${contextPath:nacos}/v3/admin/ns/service") public Result detail( @RequestParam(value = "namespaceId", defaultValue = Constants.DEFAULT_NAMESPACE_ID) String namespaceId, @RequestParam("serviceName") String serviceName, @@ -144,7 +145,7 @@ public Result detail( @GetMapping("/list") @TpsControl(pointName = "NamingServiceListQuery", name = "HttpNamingServiceListQuery") @Secured(action = ActionTypes.READ) - @Compatibility(apiType = ApiType.ADMIN_API) + @Compatibility(apiType = ApiType.ADMIN_API, alternatives = "GET ${contextPath:nacos}/v3/admin/ns/service/list") public Result list( @RequestParam(value = "namespaceId", required = false, defaultValue = Constants.DEFAULT_NAMESPACE_ID) String namespaceId, @RequestParam(value = "groupName", required = false, defaultValue = Constants.DEFAULT_GROUP) String groupName, @@ -166,7 +167,7 @@ public Result list( @PutMapping() @TpsControl(pointName = "NamingServiceUpdate", name = "HttpNamingServiceUpdate") @Secured(action = ActionTypes.WRITE) - @Compatibility(apiType = ApiType.ADMIN_API) + @Compatibility(apiType = ApiType.ADMIN_API, alternatives = "PUT ${contextPath:nacos}/v3/admin/ns/service") public Result update(ServiceForm serviceForm) throws Exception { serviceForm.validate(); Map metadata = UtilsAndCommons.parseMetadata(serviceForm.getMetadata()); diff --git a/naming/src/main/java/com/alibaba/nacos/naming/controllers/v3/ClientControllerV3.java b/naming/src/main/java/com/alibaba/nacos/naming/controllers/v3/ClientControllerV3.java new file mode 100644 index 00000000000..68e24375e9a --- /dev/null +++ b/naming/src/main/java/com/alibaba/nacos/naming/controllers/v3/ClientControllerV3.java @@ -0,0 +1,146 @@ +/* + * Copyright 1999-$toady.year Alibaba Group Holding Ltd. + * + * Licensed 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.alibaba.nacos.naming.controllers.v3; + +import com.alibaba.nacos.api.annotation.NacosApi; +import com.alibaba.nacos.api.common.Constants; +import com.alibaba.nacos.api.exception.api.NacosApiException; +import com.alibaba.nacos.api.model.v2.ErrorCode; +import com.alibaba.nacos.api.model.v2.Result; +import com.alibaba.nacos.auth.annotation.Secured; +import com.alibaba.nacos.core.paramcheck.ExtractorManager; +import com.alibaba.nacos.naming.core.ClientService; +import com.alibaba.nacos.naming.core.v2.client.manager.ClientManager; +import com.alibaba.nacos.naming.misc.UtilsAndCommons; +import com.alibaba.nacos.naming.paramcheck.NamingDefaultHttpParamExtractor; +import com.alibaba.nacos.plugin.auth.constant.ActionTypes; +import com.alibaba.nacos.plugin.auth.constant.ApiType; +import com.fasterxml.jackson.databind.node.ObjectNode; +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; + +/** + * Client controller. + * + * @author Nacos + */ + +@NacosApi +@RestController +@RequestMapping(UtilsAndCommons.CLIENT_CONTROLLER_V3_ADMIN_PATH) +@ExtractorManager.Extractor(httpExtractor = NamingDefaultHttpParamExtractor.class) +public class ClientControllerV3 { + + private final ClientManager clientManager; + + private final ClientService clientServiceV2Impl; + + public ClientControllerV3(ClientManager clientManager, ClientService clientServiceV2Impl) { + this.clientManager = clientManager; + this.clientServiceV2Impl = clientServiceV2Impl; + } + + /** + * Query all clients. + */ + @GetMapping("/list") + @Secured(resource = UtilsAndCommons.CLIENT_CONTROLLER_V3_ADMIN_PATH, action = ActionTypes.READ, apiType = ApiType.ADMIN_API) + public Result> getClientList() { + return Result.success(clientServiceV2Impl.getClientList()); + } + + /** + * Query client by clientId. + */ + @GetMapping() + @Secured(resource = UtilsAndCommons.CLIENT_CONTROLLER_V3_ADMIN_PATH, action = ActionTypes.READ, apiType = ApiType.ADMIN_API) + public Result getClientDetail(@RequestParam("clientId") String clientId) throws NacosApiException { + checkClientId(clientId); + return Result.success(clientServiceV2Impl.getClientDetail(clientId)); + } + + /** + * Query the services registered by the specified client. + */ + @GetMapping("/publish/list") + @Secured(resource = UtilsAndCommons.CLIENT_CONTROLLER_V3_ADMIN_PATH, action = ActionTypes.READ, apiType = ApiType.ADMIN_API) + public Result> getPublishedServiceList(@RequestParam("clientId") String clientId) + throws NacosApiException { + checkClientId(clientId); + return Result.success(clientServiceV2Impl.getPublishedServiceList(clientId)); + } + + /** + * Query the services to which the specified client subscribes. + */ + @GetMapping("/subscribe/list") + @Secured(resource = UtilsAndCommons.CLIENT_CONTROLLER_V3_ADMIN_PATH, action = ActionTypes.READ, apiType = ApiType.ADMIN_API) + public Result> getSubscribeServiceList(@RequestParam("clientId") String clientId) + throws NacosApiException { + checkClientId(clientId); + return Result.success(clientServiceV2Impl.getSubscribeServiceList(clientId)); + } + + /** + * Query the clients that have registered the specified service. + */ + @GetMapping("/service/publisher/list") + @Secured(resource = UtilsAndCommons.CLIENT_CONTROLLER_V3_ADMIN_PATH, action = ActionTypes.READ, apiType = ApiType.ADMIN_API) + public Result> getPublishedClientList( + @RequestParam(value = "namespaceId", required = false, defaultValue = Constants.DEFAULT_NAMESPACE_ID) String namespaceId, + @RequestParam(value = "groupName", required = false, defaultValue = Constants.DEFAULT_GROUP) String groupName, + @RequestParam(value = "ephemeral", required = false, defaultValue = "true") Boolean ephemeral, + @RequestParam("serviceName") String serviceName, @RequestParam(value = "ip", required = false) String ip, + @RequestParam(value = "port", required = false) Integer port) { + return Result.success(clientServiceV2Impl.getPublishedClientList(namespaceId, groupName, serviceName, ephemeral, ip, port)); + } + + /** + * Query the clients that are subscribed to the specified service. + */ + @GetMapping("/service/subscriber/list") + @Secured(resource = UtilsAndCommons.CLIENT_CONTROLLER_V3_ADMIN_PATH, action = ActionTypes.READ, apiType = ApiType.ADMIN_API) + public Result> getSubscribeClientList( + @RequestParam(value = "namespaceId", required = false, defaultValue = Constants.DEFAULT_NAMESPACE_ID) String namespaceId, + @RequestParam(value = "groupName", required = false, defaultValue = Constants.DEFAULT_GROUP) String groupName, + @RequestParam(value = "ephemeral", required = false, defaultValue = "true") Boolean ephemeral, + @RequestParam("serviceName") String serviceName, @RequestParam(value = "ip", required = false) String ip, + @RequestParam(value = "port", required = false) Integer port) { + return Result.success(clientServiceV2Impl.getSubscribeClientList(namespaceId, groupName, serviceName, ephemeral, ip, port)); + } + + /** + * Query the responsible server for a given client based on its IP and port. + */ + @GetMapping("/distro") + @Secured(resource = UtilsAndCommons.CLIENT_CONTROLLER_V3_ADMIN_PATH, action = ActionTypes.READ, apiType = ApiType.ADMIN_API) + public Result getResponsibleServer4Client(@RequestParam String ip, @RequestParam String port) { + return Result.success(clientServiceV2Impl.getResponsibleServer4Client(ip, port)); + } + + private void checkClientId(String clientId) throws NacosApiException { + if (!clientManager.contains(clientId)) { + throw new NacosApiException(HttpStatus.NOT_FOUND.value(), ErrorCode.RESOURCE_NOT_FOUND, + "clientId [ " + clientId + " ] not exist"); + } + } +} \ No newline at end of file diff --git a/naming/src/main/java/com/alibaba/nacos/naming/controllers/v3/ClusterControllerV3.java b/naming/src/main/java/com/alibaba/nacos/naming/controllers/v3/ClusterControllerV3.java new file mode 100644 index 00000000000..ade4159bba1 --- /dev/null +++ b/naming/src/main/java/com/alibaba/nacos/naming/controllers/v3/ClusterControllerV3.java @@ -0,0 +1,77 @@ +/* + * Copyright 1999-$toady.year Alibaba Group Holding Ltd. + * + * Licensed 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.alibaba.nacos.naming.controllers.v3; + +import com.alibaba.nacos.api.annotation.NacosApi; +import com.alibaba.nacos.api.model.v2.Result; +import com.alibaba.nacos.api.naming.pojo.healthcheck.AbstractHealthChecker; +import com.alibaba.nacos.api.naming.pojo.healthcheck.HealthCheckerFactory; +import com.alibaba.nacos.auth.annotation.Secured; +import com.alibaba.nacos.core.paramcheck.ExtractorManager; +import com.alibaba.nacos.naming.core.ClusterOperatorV2Impl; +import com.alibaba.nacos.naming.core.v2.metadata.ClusterMetadata; +import com.alibaba.nacos.naming.misc.UtilsAndCommons; +import com.alibaba.nacos.naming.model.form.UpdateClusterForm; +import com.alibaba.nacos.naming.paramcheck.NamingDefaultHttpParamExtractor; +import com.alibaba.nacos.plugin.auth.constant.ActionTypes; +import com.alibaba.nacos.plugin.auth.constant.ApiType; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + + +/** + * Cluster controller. + * + * @author Nacos + */ +@NacosApi +@RestController +@RequestMapping(UtilsAndCommons.CLUSTER_CONTROLLER_V3_ADMIN_PATH) +@ExtractorManager.Extractor(httpExtractor = NamingDefaultHttpParamExtractor.class) +public class ClusterControllerV3 { + + private final ClusterOperatorV2Impl clusterOperatorV2; + + public ClusterControllerV3(ClusterOperatorV2Impl clusterOperatorV2) { + this.clusterOperatorV2 = clusterOperatorV2; + } + + /** + * Update cluster. + */ + @PutMapping + @Secured(resource = UtilsAndCommons.CLUSTER_CONTROLLER_V3_ADMIN_PATH, action = ActionTypes.WRITE, apiType = ApiType.ADMIN_API) + public Result update(UpdateClusterForm updateClusterForm) throws Exception { + updateClusterForm.validate(); + + final String namespaceId = updateClusterForm.getNamespaceId(); + final String clusterName = updateClusterForm.getClusterName(); + final String serviceName = updateClusterForm.getServiceName(); + ClusterMetadata clusterMetadata = new ClusterMetadata(); + clusterMetadata.setHealthyCheckPort(updateClusterForm.getCheckPort()); + clusterMetadata.setUseInstancePortForCheck(updateClusterForm.isUseInstancePort4Check()); + AbstractHealthChecker healthChecker = HealthCheckerFactory.deserialize(updateClusterForm.getHealthChecker()); + clusterMetadata.setHealthChecker(healthChecker); + clusterMetadata.setHealthyCheckType(healthChecker.getType()); + clusterMetadata.setExtendData(UtilsAndCommons.parseMetadata(updateClusterForm.getMetadata())); + + clusterOperatorV2.updateClusterMetadata(namespaceId, serviceName, clusterName, clusterMetadata); + + return Result.success("ok"); + } +} \ No newline at end of file diff --git a/naming/src/main/java/com/alibaba/nacos/naming/controllers/v3/HealthControllerV3.java b/naming/src/main/java/com/alibaba/nacos/naming/controllers/v3/HealthControllerV3.java new file mode 100644 index 00000000000..74ec6c5c4bf --- /dev/null +++ b/naming/src/main/java/com/alibaba/nacos/naming/controllers/v3/HealthControllerV3.java @@ -0,0 +1,82 @@ +/* + * Copyright 1999-$toady.year Alibaba Group Holding Ltd. + * + * Licensed 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.alibaba.nacos.naming.controllers.v3; + +import com.alibaba.nacos.api.annotation.NacosApi; +import com.alibaba.nacos.api.exception.NacosException; +import com.alibaba.nacos.api.model.v2.Result; +import com.alibaba.nacos.api.naming.pojo.healthcheck.AbstractHealthChecker; +import com.alibaba.nacos.api.naming.utils.NamingUtils; +import com.alibaba.nacos.auth.annotation.Secured; +import com.alibaba.nacos.core.paramcheck.ExtractorManager; +import com.alibaba.nacos.naming.core.HealthOperatorV2Impl; +import com.alibaba.nacos.naming.misc.UtilsAndCommons; +import com.alibaba.nacos.naming.model.form.UpdateHealthForm; +import com.alibaba.nacos.naming.paramcheck.NamingDefaultHttpParamExtractor; +import com.alibaba.nacos.naming.web.CanDistro; +import com.alibaba.nacos.plugin.auth.constant.ActionTypes; +import com.alibaba.nacos.plugin.auth.constant.ApiType; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.Map; + +/** + * Health controller. + * + * @author Nacos + */ +@NacosApi +@RestController +@RequestMapping(UtilsAndCommons.HEALTH_CONTROLLER_V3_ADMIN_PATH) +@ExtractorManager.Extractor(httpExtractor = NamingDefaultHttpParamExtractor.class) +public class HealthControllerV3 { + + @Autowired + private HealthOperatorV2Impl healthOperatorV2; + + /** + * Update health check for instance. + */ + @CanDistro + @PutMapping(value = "/instance") + @Secured(resource = UtilsAndCommons.HEALTH_CONTROLLER_V3_ADMIN_PATH, action = ActionTypes.WRITE, apiType = ApiType.ADMIN_API) + public Result update(UpdateHealthForm updateHealthForm) throws NacosException { + updateHealthForm.validate(); + healthOperatorV2.updateHealthStatusForPersistentInstance(updateHealthForm.getNamespaceId(), buildCompositeServiceName(updateHealthForm), + updateHealthForm.getClusterName(), updateHealthForm.getIp(), updateHealthForm.getPort(), + updateHealthForm.getHealthy()); + + return Result.success("ok"); + } + + private String buildCompositeServiceName(UpdateHealthForm updateHealthForm) { + return NamingUtils.getGroupedName(updateHealthForm.getServiceName(), updateHealthForm.getGroupName()); + } + + /** + * Get all health checkers. + */ + @GetMapping("/checkers") + @Secured(resource = UtilsAndCommons.HEALTH_CONTROLLER_V3_ADMIN_PATH, action = ActionTypes.WRITE, apiType = ApiType.ADMIN_API) + public Result> checkers() { + return Result.success(healthOperatorV2.checkers()); + } +} \ No newline at end of file diff --git a/naming/src/main/java/com/alibaba/nacos/naming/controllers/v3/InstanceControllerV3.java b/naming/src/main/java/com/alibaba/nacos/naming/controllers/v3/InstanceControllerV3.java new file mode 100644 index 00000000000..d3b2d6f483c --- /dev/null +++ b/naming/src/main/java/com/alibaba/nacos/naming/controllers/v3/InstanceControllerV3.java @@ -0,0 +1,351 @@ +/* + * Copyright 1999-$toady.year Alibaba Group Holding Ltd. + * + * Licensed 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.alibaba.nacos.naming.controllers.v3; + +import com.alibaba.nacos.api.annotation.NacosApi; +import com.alibaba.nacos.api.common.Constants; +import com.alibaba.nacos.api.exception.NacosException; +import com.alibaba.nacos.api.exception.api.NacosApiException; +import com.alibaba.nacos.api.model.v2.ErrorCode; +import com.alibaba.nacos.api.model.v2.Result; +import com.alibaba.nacos.api.naming.pojo.Instance; +import com.alibaba.nacos.api.naming.pojo.ServiceInfo; +import com.alibaba.nacos.api.naming.pojo.builder.InstanceBuilder; +import com.alibaba.nacos.api.naming.utils.NamingUtils; +import com.alibaba.nacos.auth.annotation.Secured; +import com.alibaba.nacos.common.constant.HttpHeaderConsts; +import com.alibaba.nacos.common.notify.NotifyCenter; +import com.alibaba.nacos.common.trace.DeregisterInstanceReason; +import com.alibaba.nacos.common.trace.event.naming.DeregisterInstanceTraceEvent; +import com.alibaba.nacos.common.trace.event.naming.RegisterInstanceTraceEvent; +import com.alibaba.nacos.common.trace.event.naming.UpdateInstanceTraceEvent; +import com.alibaba.nacos.common.utils.JacksonUtils; +import com.alibaba.nacos.common.utils.StringUtils; +import com.alibaba.nacos.core.control.TpsControl; +import com.alibaba.nacos.core.paramcheck.ExtractorManager; +import com.alibaba.nacos.naming.core.InstanceOperatorClientImpl; +import com.alibaba.nacos.naming.core.InstancePatchObject; +import com.alibaba.nacos.naming.misc.Loggers; +import com.alibaba.nacos.naming.misc.SwitchDomain; +import com.alibaba.nacos.naming.misc.UtilsAndCommons; +import com.alibaba.nacos.naming.model.form.InstanceForm; +import com.alibaba.nacos.naming.model.form.InstanceMetadataBatchOperationForm; +import com.alibaba.nacos.naming.model.vo.InstanceDetailInfoVo; +import com.alibaba.nacos.naming.model.vo.InstanceMetadataBatchOperationVo; +import com.alibaba.nacos.naming.paramcheck.NamingDefaultHttpParamExtractor; +import com.alibaba.nacos.naming.paramcheck.NamingInstanceListHttpParamExtractor; +import com.alibaba.nacos.naming.paramcheck.NamingInstanceMetadataBatchHttpParamExtractor; +import com.alibaba.nacos.naming.pojo.InstanceOperationInfo; +import com.alibaba.nacos.naming.pojo.Subscriber; +import com.alibaba.nacos.naming.utils.NamingRequestUtil; +import com.alibaba.nacos.naming.web.CanDistro; +import com.alibaba.nacos.plugin.auth.constant.ActionTypes; +import com.alibaba.nacos.plugin.auth.constant.ApiType; +import com.fasterxml.jackson.core.type.TypeReference; +import org.apache.commons.collections.CollectionUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestHeader; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import static com.alibaba.nacos.naming.misc.UtilsAndCommons.DEFAULT_CLUSTER_NAME; + +/** + * Instance controller. + * + * @author Nacos + */ +@NacosApi +@RestController +@RequestMapping(UtilsAndCommons.INSTANCE_CONTROLLER_V3_ADMIN_PATH) +@ExtractorManager.Extractor(httpExtractor = NamingDefaultHttpParamExtractor.class) +public class InstanceControllerV3 { + + @Autowired + private SwitchDomain switchDomain; + + @Autowired + private InstanceOperatorClientImpl instanceServiceV2; + + /** + * Register new instance. + */ + @CanDistro + @PostMapping + @TpsControl(pointName = "NamingInstanceRegister", name = "HttpNamingInstanceRegister") + @Secured(resource = UtilsAndCommons.INSTANCE_CONTROLLER_V3_ADMIN_PATH, action = ActionTypes.WRITE, apiType = ApiType.ADMIN_API) + public Result register(InstanceForm instanceForm) throws NacosException { + // check param + instanceForm.validate(); + checkWeight(instanceForm.getWeight()); + // build instance + Instance instance = buildInstance(instanceForm); + instanceServiceV2.registerInstance(instanceForm.getNamespaceId(), buildCompositeServiceName(instanceForm), + instance); + NotifyCenter.publishEvent( + new RegisterInstanceTraceEvent(System.currentTimeMillis(), NamingRequestUtil.getSourceIp(), false, + instanceForm.getNamespaceId(), instanceForm.getGroupName(), instanceForm.getServiceName(), + instance.getIp(), instance.getPort())); + + return Result.success("ok"); + } + + /** + * Deregister instances. + */ + @CanDistro + @DeleteMapping + @TpsControl(pointName = "NamingInstanceDeregister", name = "HttpNamingInstanceDeregister") + @Secured(resource = UtilsAndCommons.INSTANCE_CONTROLLER_V3_ADMIN_PATH, action = ActionTypes.WRITE, apiType = ApiType.ADMIN_API) + public Result deregister(InstanceForm instanceForm) throws NacosException { + // check param + instanceForm.validate(); + checkWeight(instanceForm.getWeight()); + // build instance + Instance instance = buildInstance(instanceForm); + instanceServiceV2.removeInstance(instanceForm.getNamespaceId(), buildCompositeServiceName(instanceForm), + instance); + NotifyCenter.publishEvent( + new DeregisterInstanceTraceEvent(System.currentTimeMillis(), NamingRequestUtil.getSourceIp(), false, + DeregisterInstanceReason.REQUEST, instanceForm.getNamespaceId(), instanceForm.getGroupName(), + instanceForm.getServiceName(), instance.getIp(), instance.getPort())); + + return Result.success("ok"); + } + + /** + * Update instance. + */ + @CanDistro + @PutMapping + @TpsControl(pointName = "NamingInstanceUpdate", name = "HttpNamingInstanceUpdate") + @Secured(resource = UtilsAndCommons.INSTANCE_CONTROLLER_V3_ADMIN_PATH, action = ActionTypes.WRITE, apiType = ApiType.ADMIN_API) + public Result update(InstanceForm instanceForm) throws NacosException { + // check param + instanceForm.validate(); + checkWeight(instanceForm.getWeight()); + // build instance + Instance instance = buildInstance(instanceForm); + instanceServiceV2.updateInstance(instanceForm.getNamespaceId(), buildCompositeServiceName(instanceForm), + instance); + NotifyCenter.publishEvent( + new UpdateInstanceTraceEvent(System.currentTimeMillis(), NamingRequestUtil.getSourceIp(), + instanceForm.getNamespaceId(), instanceForm.getGroupName(), instanceForm.getServiceName(), + instance.getIp(), instance.getPort(), instance.getMetadata())); + + return Result.success("ok"); + } + + /** + * Batch update instance's metadata. old key exist = update, old key not exist = add. + */ + @CanDistro + @PutMapping(value = "/metadata/batch") + @TpsControl(pointName = "NamingInstanceMetadataUpdate", name = "HttpNamingInstanceMetadataBatchUpdate") + @ExtractorManager.Extractor(httpExtractor = NamingInstanceMetadataBatchHttpParamExtractor.class) + @Secured(resource = UtilsAndCommons.INSTANCE_CONTROLLER_V3_ADMIN_PATH, action = ActionTypes.WRITE, apiType = ApiType.ADMIN_API) + public Result batchUpdateInstanceMetadata(InstanceMetadataBatchOperationForm form) + throws NacosException { + form.validate(); + + List targetInstances = parseBatchInstances(form.getInstances()); + Map targetMetadata = UtilsAndCommons.parseMetadata(form.getMetadata()); + InstanceOperationInfo instanceOperationInfo = buildOperationInfo(buildCompositeServiceName(form), + form.getConsistencyType(), targetInstances); + + List operatedInstances = instanceServiceV2.batchUpdateMetadata(form.getNamespaceId(), + instanceOperationInfo, targetMetadata); + ArrayList ipList = new ArrayList<>(operatedInstances); + + return Result.success(new InstanceMetadataBatchOperationVo(ipList)); + } + + /** + * Batch delete instance's metadata. old key exist = delete, old key not exist = not operate + */ + @CanDistro + @DeleteMapping("/metadata/batch") + @TpsControl(pointName = "NamingInstanceMetadataUpdate", name = "HttpNamingInstanceMetadataBatchUpdate") + @ExtractorManager.Extractor(httpExtractor = NamingInstanceMetadataBatchHttpParamExtractor.class) + @Secured(resource = UtilsAndCommons.INSTANCE_CONTROLLER_V3_ADMIN_PATH, action = ActionTypes.WRITE, apiType = ApiType.ADMIN_API) + public Result batchDeleteInstanceMetadata(InstanceMetadataBatchOperationForm form) + throws NacosException { + form.validate(); + List targetInstances = parseBatchInstances(form.getInstances()); + Map targetMetadata = UtilsAndCommons.parseMetadata(form.getMetadata()); + InstanceOperationInfo instanceOperationInfo = buildOperationInfo(buildCompositeServiceName(form), + form.getConsistencyType(), targetInstances); + List operatedInstances = instanceServiceV2.batchDeleteMetadata(form.getNamespaceId(), + instanceOperationInfo, targetMetadata); + ArrayList ipList = new ArrayList<>(operatedInstances); + + return Result.success(new InstanceMetadataBatchOperationVo(ipList)); + } + + private InstanceOperationInfo buildOperationInfo(String serviceName, String consistencyType, + List instances) { + if (!CollectionUtils.isEmpty(instances)) { + for (Instance instance : instances) { + if (StringUtils.isBlank(instance.getClusterName())) { + instance.setClusterName(DEFAULT_CLUSTER_NAME); + } + } + } + return new InstanceOperationInfo(serviceName, consistencyType, instances); + } + + private List parseBatchInstances(String instances) { + try { + return JacksonUtils.toObj(instances, new TypeReference>() { + }); + } catch (Exception e) { + Loggers.SRV_LOG.warn("UPDATE-METADATA: Param 'instances' is illegal, ignore this operation", e); + } + return Collections.emptyList(); + } + + /** + * Partial update instance. + */ + @CanDistro + @PutMapping(value = "/partial") + @Secured(resource = UtilsAndCommons.INSTANCE_CONTROLLER_V3_ADMIN_PATH, action = ActionTypes.WRITE, apiType = ApiType.ADMIN_API) + public Result partialUpdateInstance(@RequestParam(defaultValue = Constants.DEFAULT_NAMESPACE_ID) String namespaceId, + @RequestParam String serviceName, @RequestParam String ip, + @RequestParam(defaultValue = UtilsAndCommons.DEFAULT_CLUSTER_NAME) String cluster, + @RequestParam Integer port, @RequestParam Double weight, @RequestParam Boolean enabled, + @RequestParam String metadata) throws Exception { + NamingUtils.checkServiceNameFormat(serviceName); + + InstancePatchObject patchObject = new InstancePatchObject(cluster, ip, port); + if (StringUtils.isNotBlank(metadata)) { + patchObject.setMetadata(UtilsAndCommons.parseMetadata(metadata)); + } + if (weight != null) { + checkWeight(weight); + patchObject.setWeight(weight); + } + if (enabled != null) { + patchObject.setEnabled(enabled); + } + instanceServiceV2.patchInstance(namespaceId, serviceName, patchObject); + + return Result.success("ok"); + } + + /** + * Get all instance of input service. + */ + @GetMapping("/list") + @TpsControl(pointName = "NamingServiceSubscribe", name = "HttpNamingServiceSubscribe") + @ExtractorManager.Extractor(httpExtractor = NamingInstanceListHttpParamExtractor.class) + @Secured(resource = UtilsAndCommons.SERVICE_CONTROLLER_V3_ADMIN_PATH, action = ActionTypes.READ, apiType = ApiType.ADMIN_API) + public Result list( + @RequestParam(value = "namespaceId", defaultValue = Constants.DEFAULT_NAMESPACE_ID) String namespaceId, + @RequestParam(value = "groupName", defaultValue = Constants.DEFAULT_GROUP) String groupName, + @RequestParam("serviceName") String serviceName, + @RequestParam(value = "clusterName", defaultValue = StringUtils.EMPTY) String clusterName, + @RequestParam(value = "ip", defaultValue = StringUtils.EMPTY) String ip, + @RequestParam(value = "port", defaultValue = "0") Integer port, + @RequestParam(value = "healthyOnly", defaultValue = "false") Boolean healthyOnly, + @RequestParam(value = "app", defaultValue = StringUtils.EMPTY) String app, + @RequestHeader(value = HttpHeaderConsts.USER_AGENT_HEADER, required = false) String userAgent, + @RequestHeader(value = HttpHeaderConsts.CLIENT_VERSION_HEADER, required = false) String clientVersion) { + if (StringUtils.isEmpty(userAgent)) { + userAgent = StringUtils.defaultIfEmpty(clientVersion, StringUtils.EMPTY); + } + String compositeServiceName = NamingUtils.getGroupedName(serviceName, groupName); + Subscriber subscriber = new Subscriber(ip + ":" + port, userAgent, app, ip, namespaceId, compositeServiceName, + port, clusterName); + + return Result.success(instanceServiceV2.listInstance(namespaceId, compositeServiceName, subscriber, clusterName, + healthyOnly)); + } + + /** + * Get detail information of specified instance. + */ + @GetMapping + @TpsControl(pointName = "NamingInstanceQuery", name = "HttpNamingInstanceQuery") + @Secured(resource = UtilsAndCommons.INSTANCE_CONTROLLER_V3_ADMIN_PATH, action = ActionTypes.WRITE, apiType = ApiType.ADMIN_API) + public Result detail( + @RequestParam(value = "namespaceId", defaultValue = Constants.DEFAULT_NAMESPACE_ID) String namespaceId, + @RequestParam(value = "groupName", defaultValue = Constants.DEFAULT_GROUP) String groupName, + @RequestParam("serviceName") String serviceName, + @RequestParam(value = "clusterName", defaultValue = UtilsAndCommons.DEFAULT_CLUSTER_NAME) String clusterName, + @RequestParam("ip") String ip, @RequestParam("port") Integer port) throws NacosException { + + String compositeServiceName = NamingUtils.getGroupedName(serviceName, groupName); + + Instance instance = instanceServiceV2.getInstance(namespaceId, compositeServiceName, clusterName, ip, port); + + InstanceDetailInfoVo instanceDetailInfoVo = new InstanceDetailInfoVo(); + instanceDetailInfoVo.setServiceName(compositeServiceName); + instanceDetailInfoVo.setIp(ip); + instanceDetailInfoVo.setPort(port); + instanceDetailInfoVo.setClusterName(clusterName); + instanceDetailInfoVo.setWeight(instance.getWeight()); + instanceDetailInfoVo.setHealthy(instance.isHealthy()); + instanceDetailInfoVo.setInstanceId(instance.getInstanceId()); + instanceDetailInfoVo.setMetadata(instance.getMetadata()); + + return Result.success(instanceDetailInfoVo); + } + + private void checkWeight(Double weight) throws NacosException { + if (weight > com.alibaba.nacos.naming.constants.Constants.MAX_WEIGHT_VALUE + || weight < com.alibaba.nacos.naming.constants.Constants.MIN_WEIGHT_VALUE) { + throw new NacosApiException(HttpStatus.BAD_REQUEST.value(), ErrorCode.WEIGHT_ERROR, + "instance format invalid: The weights range from " + + com.alibaba.nacos.naming.constants.Constants.MIN_WEIGHT_VALUE + " to " + + com.alibaba.nacos.naming.constants.Constants.MAX_WEIGHT_VALUE); + } + } + + private Instance buildInstance(InstanceForm instanceForm) throws NacosException { + Instance instance = InstanceBuilder.newBuilder().setServiceName(buildCompositeServiceName(instanceForm)) + .setIp(instanceForm.getIp()).setClusterName(instanceForm.getClusterName()) + .setPort(instanceForm.getPort()).setHealthy(instanceForm.getHealthy()) + .setWeight(instanceForm.getWeight()).setEnabled(instanceForm.getEnabled()) + .setMetadata(UtilsAndCommons.parseMetadata(instanceForm.getMetadata())) + .setEphemeral(instanceForm.getEphemeral()).build(); + if (instanceForm.getEphemeral() == null) { + instance.setEphemeral((switchDomain.isDefaultInstanceEphemeral())); + } + return instance; + } + + private String buildCompositeServiceName(InstanceForm instanceForm) { + return NamingUtils.getGroupedName(instanceForm.getServiceName(), instanceForm.getGroupName()); + } + + private String buildCompositeServiceName(InstanceMetadataBatchOperationForm form) { + return NamingUtils.getGroupedName(form.getServiceName(), form.getGroupName()); + } + +} diff --git a/naming/src/main/java/com/alibaba/nacos/naming/controllers/v3/OperatorControllerV3.java b/naming/src/main/java/com/alibaba/nacos/naming/controllers/v3/OperatorControllerV3.java new file mode 100644 index 00000000000..5fc72495a5e --- /dev/null +++ b/naming/src/main/java/com/alibaba/nacos/naming/controllers/v3/OperatorControllerV3.java @@ -0,0 +1,102 @@ +/* + * Copyright 1999-$toady.year Alibaba Group Holding Ltd. + * + * Licensed 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.alibaba.nacos.naming.controllers.v3; + +import com.alibaba.nacos.api.annotation.NacosApi; +import com.alibaba.nacos.api.exception.api.NacosApiException; +import com.alibaba.nacos.api.model.v2.ErrorCode; +import com.alibaba.nacos.api.model.v2.Result; +import com.alibaba.nacos.auth.annotation.Secured; +import com.alibaba.nacos.core.paramcheck.ExtractorManager; +import com.alibaba.nacos.naming.core.Operator; +import com.alibaba.nacos.naming.misc.SwitchDomain; +import com.alibaba.nacos.naming.misc.UtilsAndCommons; +import com.alibaba.nacos.naming.model.form.UpdateSwitchForm; +import com.alibaba.nacos.naming.model.vo.MetricsInfoVo; +import com.alibaba.nacos.naming.paramcheck.NamingDefaultHttpParamExtractor; +import com.alibaba.nacos.plugin.auth.constant.ActionTypes; +import com.alibaba.nacos.plugin.auth.constant.ApiType; +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +/** + * Operator controller. + * + * @author Nacos + */ +@NacosApi +@RestController +@RequestMapping(UtilsAndCommons.OPERATOR_CONTROLLER_V3_ADMIN_PATH) +@ExtractorManager.Extractor(httpExtractor = NamingDefaultHttpParamExtractor.class) +public class OperatorControllerV3 { + + private final Operator operatorV2Impl; + + public OperatorControllerV3(Operator operatorV2Impl) { + this.operatorV2Impl = operatorV2Impl; + } + + /** + * Get switch information. + */ + @GetMapping("/switches") + @Secured(resource = UtilsAndCommons.INSTANCE_CONTROLLER_V3_ADMIN_PATH, action = ActionTypes.READ, apiType = ApiType.ADMIN_API) + public Result switches() { + return Result.success(operatorV2Impl.switches()); + } + + /** + * Update switch information. + */ + @PutMapping("/switches") + @Secured(resource = UtilsAndCommons.INSTANCE_CONTROLLER_V3_ADMIN_PATH, action = ActionTypes.WRITE, apiType = ApiType.ADMIN_API) + public Result updateSwitch(UpdateSwitchForm updateSwitchForm) throws Exception { + updateSwitchForm.validate(); + try { + operatorV2Impl.updateSwitch(updateSwitchForm.getEntry(), updateSwitchForm.getValue(), updateSwitchForm.getDebug()); + return Result.success("ok"); + } catch (IllegalArgumentException e) { + throw new NacosApiException(HttpStatus.INTERNAL_SERVER_ERROR.value(), ErrorCode.SERVER_ERROR, + e.getMessage()); + } + } + + /** + * Get metrics information. + */ + @GetMapping("/metrics") + @Secured(resource = UtilsAndCommons.OPERATOR_CONTROLLER_V3_ADMIN_PATH, action = ActionTypes.READ, apiType = ApiType.ADMIN_API) + public Result metrics( + @RequestParam(value = "onlyStatus", required = false, defaultValue = "true") Boolean onlyStatus) { + return Result.success(operatorV2Impl.metrics(onlyStatus)); + } + + /** + * Set log level. + */ + @PutMapping("/log") + @Secured(resource = UtilsAndCommons.OPERATOR_CONTROLLER_V3_ADMIN_PATH, action = ActionTypes.WRITE, apiType = ApiType.ADMIN_API) + public Result setLogLevel(@RequestParam String logName, @RequestParam String logLevel) { + operatorV2Impl.setLogLevel(logName, logLevel); + + return Result.success("ok"); + } +} \ No newline at end of file diff --git a/naming/src/main/java/com/alibaba/nacos/naming/controllers/v3/ServiceControllerV3.java b/naming/src/main/java/com/alibaba/nacos/naming/controllers/v3/ServiceControllerV3.java new file mode 100644 index 00000000000..1c856ac2f1e --- /dev/null +++ b/naming/src/main/java/com/alibaba/nacos/naming/controllers/v3/ServiceControllerV3.java @@ -0,0 +1,273 @@ +/* + * Copyright 1999-2018 Alibaba Group Holding Ltd. + * + * Licensed 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.alibaba.nacos.naming.controllers.v3; + +import com.alibaba.nacos.api.annotation.NacosApi; +import com.alibaba.nacos.api.common.Constants; +import com.alibaba.nacos.api.exception.NacosException; +import com.alibaba.nacos.api.exception.api.NacosApiException; +import com.alibaba.nacos.api.model.v2.ErrorCode; +import com.alibaba.nacos.api.model.v2.Result; +import com.alibaba.nacos.api.selector.Selector; +import com.alibaba.nacos.auth.annotation.Secured; +import com.alibaba.nacos.common.notify.NotifyCenter; +import com.alibaba.nacos.common.trace.event.naming.DeregisterServiceTraceEvent; +import com.alibaba.nacos.common.trace.event.naming.RegisterServiceTraceEvent; +import com.alibaba.nacos.common.trace.event.naming.UpdateServiceTraceEvent; +import com.alibaba.nacos.common.utils.JacksonUtils; +import com.alibaba.nacos.common.utils.StringUtils; +import com.alibaba.nacos.core.control.TpsControl; +import com.alibaba.nacos.core.model.form.AggregationForm; +import com.alibaba.nacos.core.model.form.PageForm; +import com.alibaba.nacos.core.paramcheck.ExtractorManager; +import com.alibaba.nacos.naming.core.CatalogServiceV2Impl; +import com.alibaba.nacos.naming.core.ServiceOperatorV2Impl; +import com.alibaba.nacos.naming.core.v2.metadata.ServiceMetadata; +import com.alibaba.nacos.naming.core.v2.pojo.Service; +import com.alibaba.nacos.naming.misc.UtilsAndCommons; +import com.alibaba.nacos.naming.model.form.ServiceForm; +import com.alibaba.nacos.naming.model.form.ServiceListForm; +import com.alibaba.nacos.naming.paramcheck.NamingDefaultHttpParamExtractor; +import com.alibaba.nacos.naming.pojo.ServiceDetailInfo; +import com.alibaba.nacos.naming.selector.NoneSelector; +import com.alibaba.nacos.naming.selector.SelectorManager; +import com.alibaba.nacos.plugin.auth.constant.ActionTypes; +import com.alibaba.nacos.plugin.auth.constant.ApiType; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import java.net.URLDecoder; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; + +/** + * Service controller. + * + * @author Nacos + */ +@NacosApi +@RestController +@RequestMapping(UtilsAndCommons.SERVICE_CONTROLLER_V3_ADMIN_PATH) +@ExtractorManager.Extractor(httpExtractor = NamingDefaultHttpParamExtractor.class) +public class ServiceControllerV3 { + + private final ServiceOperatorV2Impl serviceOperatorV2; + + private final SelectorManager selectorManager; + + private final CatalogServiceV2Impl catalogServiceV2; + + public ServiceControllerV3(ServiceOperatorV2Impl serviceOperatorV2, SelectorManager selectorManager, + CatalogServiceV2Impl catalogServiceV2) { + this.serviceOperatorV2 = serviceOperatorV2; + this.selectorManager = selectorManager; + this.catalogServiceV2 = catalogServiceV2; + } + + /** + * Create a new service. This API will create a persistence service. + */ + @PostMapping() + @TpsControl(pointName = "NamingServiceRegister", name = "HttpNamingServiceRegister") + @Secured(resource = UtilsAndCommons.SERVICE_CONTROLLER_V3_ADMIN_PATH, action = ActionTypes.WRITE, apiType = ApiType.ADMIN_API) + public Result create(@RequestBody ServiceForm serviceForm) throws Exception { + serviceForm.validate(); + ServiceMetadata serviceMetadata = new ServiceMetadata(); + serviceMetadata.setProtectThreshold(serviceForm.getProtectThreshold()); + serviceMetadata.setSelector(parseSelector(serviceForm.getSelector())); + serviceMetadata.setExtendData(UtilsAndCommons.parseMetadata(serviceForm.getMetadata())); + serviceMetadata.setEphemeral(serviceForm.getEphemeral()); + serviceOperatorV2.create(Service.newService(serviceForm.getNamespaceId(), serviceForm.getGroupName(), + serviceForm.getServiceName(), serviceForm.getEphemeral()), serviceMetadata); + NotifyCenter.publishEvent( + new RegisterServiceTraceEvent(System.currentTimeMillis(), serviceForm.getNamespaceId(), + serviceForm.getGroupName(), serviceForm.getServiceName())); + + return Result.success("ok"); + } + + /** + * Remove service. + */ + @DeleteMapping() + @TpsControl(pointName = "NamingServiceDeregister", name = "HttpNamingServiceDeregister") + @Secured(resource = UtilsAndCommons.SERVICE_CONTROLLER_V3_ADMIN_PATH, action = ActionTypes.WRITE, apiType = ApiType.ADMIN_API) + public Result remove( + @RequestParam(value = "namespaceId", defaultValue = Constants.DEFAULT_NAMESPACE_ID) String namespaceId, + @RequestParam("serviceName") String serviceName, + @RequestParam(value = "groupName", defaultValue = Constants.DEFAULT_GROUP) String groupName) + throws Exception { + serviceOperatorV2.delete(Service.newService(namespaceId, groupName, serviceName)); + NotifyCenter.publishEvent( + new DeregisterServiceTraceEvent(System.currentTimeMillis(), namespaceId, groupName, serviceName)); + + return Result.success("ok"); + } + + /** + * Get detail of service. + */ + @GetMapping() + @TpsControl(pointName = "NamingServiceQuery", name = "HttpNamingServiceQuery") + @Secured(resource = UtilsAndCommons.SERVICE_CONTROLLER_V3_ADMIN_PATH, action = ActionTypes.READ, apiType = ApiType.ADMIN_API) + public Result detail( + @RequestParam(value = "namespaceId", defaultValue = Constants.DEFAULT_NAMESPACE_ID) String namespaceId, + @RequestParam("serviceName") String serviceName, + @RequestParam(value = "groupName", defaultValue = Constants.DEFAULT_GROUP) String groupName) + throws Exception { + ServiceDetailInfo result = serviceOperatorV2.queryService( + Service.newService(namespaceId, groupName, serviceName)); + + return Result.success(result); + } + + /** + * List all service names. + */ + @GetMapping("/list") + @TpsControl(pointName = "NamingServiceListQuery", name = "HttpNamingServiceListQuery") + @Secured(resource = UtilsAndCommons.SERVICE_CONTROLLER_V3_ADMIN_PATH, action = ActionTypes.READ, apiType = ApiType.ADMIN_API) + public Result list(ServiceListForm serviceListForm, PageForm pageForm) throws Exception { + serviceListForm.validate(); + pageForm.validate(); + String namespaceId = serviceListForm.getNamespaceId(); + String serviceName = serviceListForm.getServiceNameParam(); + String groupName = serviceListForm.getGroupNameParam(); + boolean hasIpCount = serviceListForm.isHasIpCount(); + boolean withInstances = serviceListForm.isWithInstances(); + int pageNo = pageForm.getPageNo(); + int pageSize = pageForm.getPageSize(); + + if (withInstances) { + return Result.success(catalogServiceV2.pageListServiceDetail(namespaceId, groupName, serviceName, pageNo, pageSize)); + } + return Result.success(catalogServiceV2.pageListService(namespaceId, groupName, serviceName, pageNo, pageSize, StringUtils.EMPTY, hasIpCount)); + } + + /** + * Update service. + */ + @PutMapping() + @TpsControl(pointName = "NamingServiceUpdate", name = "HttpNamingServiceUpdate") + @Secured(resource = UtilsAndCommons.SERVICE_CONTROLLER_V3_ADMIN_PATH, action = ActionTypes.WRITE, apiType = ApiType.ADMIN_API) + public Result update(@RequestBody ServiceForm serviceForm) throws Exception { + serviceForm.validate(); + Map metadata = UtilsAndCommons.parseMetadata(serviceForm.getMetadata()); + ServiceMetadata serviceMetadata = new ServiceMetadata(); + serviceMetadata.setProtectThreshold(serviceForm.getProtectThreshold()); + serviceMetadata.setExtendData(metadata); + serviceMetadata.setSelector(parseSelector(serviceForm.getSelector())); + Service service = Service.newService(serviceForm.getNamespaceId(), serviceForm.getGroupName(), + serviceForm.getServiceName()); + + serviceOperatorV2.update(service, serviceMetadata); + + NotifyCenter.publishEvent(new UpdateServiceTraceEvent(System.currentTimeMillis(), serviceForm.getNamespaceId(), + serviceForm.getGroupName(), serviceForm.getServiceName(), metadata)); + + return Result.success("ok"); + } + + private Selector parseSelector(String selectorJsonString) throws Exception { + if (StringUtils.isBlank(selectorJsonString)) { + return new NoneSelector(); + } + + JsonNode selectorJson = JacksonUtils.toObj(URLDecoder.decode(selectorJsonString, "UTF-8")); + String type = Optional.ofNullable(selectorJson.get("type")).orElseThrow( + () -> new NacosApiException(NacosException.INVALID_PARAM, ErrorCode.SELECTOR_ERROR, + "not match any type of selector!")).asText(); + String expression = Optional.ofNullable(selectorJson.get("expression")).map(JsonNode::asText).orElse(null); + Selector selector = selectorManager.parseSelector(type, expression); + if (Objects.isNull(selector)) { + throw new NacosApiException(NacosException.INVALID_PARAM, ErrorCode.SELECTOR_ERROR, + "not match any type of selector!"); + } + + return selector; + } + + + /** + * Search service names. + */ + @GetMapping("/names") + @Secured(resource = UtilsAndCommons.SERVICE_CONTROLLER_V3_ADMIN_PATH, action = ActionTypes.READ, apiType = ApiType.ADMIN_API) + public Result searchService(@RequestParam(defaultValue = StringUtils.EMPTY) String namespaceId, + @RequestParam(defaultValue = StringUtils.EMPTY) String expr) throws NacosException { + Map> serviceNameMap = new HashMap<>(16); + int totalCount = 0; + if (StringUtils.isNotBlank(namespaceId)) { + Collection names = serviceOperatorV2.searchServiceName(namespaceId, expr); + serviceNameMap.put(namespaceId, names); + totalCount = names.size(); + } else { + for (String each : serviceOperatorV2.listAllNamespace()) { + Collection names = serviceOperatorV2.searchServiceName(each, expr); + serviceNameMap.put(each, names); + totalCount += names.size(); + } + } + + ObjectNode result = JacksonUtils.createEmptyJsonNode(); + result.replace("META-INF/services", JacksonUtils.transferToJsonNode(serviceNameMap)); + result.put("count", totalCount); + + return Result.success(result); + } + + /** + * get subscriber list. + */ + @GetMapping("/subscribers") + @Secured(resource = UtilsAndCommons.SERVICE_CONTROLLER_V3_ADMIN_PATH, action = ActionTypes.READ, apiType = ApiType.ADMIN_API) + public Result subscribers(ServiceForm serviceForm, PageForm pageForm, AggregationForm aggregationForm) + throws Exception { + serviceForm.validate(); + pageForm.validate(); + int pageNo = pageForm.getPageNo(); + int pageSize = pageForm.getPageSize(); + String namespaceId = serviceForm.getNamespaceId(); + String serviceName = serviceForm.getServiceName(); + String groupName = serviceForm.getGroupName(); + boolean aggregation = aggregationForm.isAggregation(); + + return Result.success(serviceOperatorV2.getSubscribers(pageNo, pageSize, namespaceId, serviceName, groupName, aggregation)); + } + + /** + * Get all {@link Selector} types. + */ + @GetMapping("/selector/types") + @Secured(resource = UtilsAndCommons.SERVICE_CONTROLLER_V3_ADMIN_PATH, action = ActionTypes.READ, apiType = ApiType.ADMIN_API) + public Result> listSelectorTypes() { + return Result.success(selectorManager.getAllSelectorTypes()); + } +} + diff --git a/naming/src/main/java/com/alibaba/nacos/naming/core/ClientService.java b/naming/src/main/java/com/alibaba/nacos/naming/core/ClientService.java new file mode 100644 index 00000000000..2066401ed32 --- /dev/null +++ b/naming/src/main/java/com/alibaba/nacos/naming/core/ClientService.java @@ -0,0 +1,96 @@ +/* + * Copyright 1999-$toady.year Alibaba Group Holding Ltd. + * + * Licensed 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.alibaba.nacos.naming.core; + +import com.fasterxml.jackson.databind.node.ObjectNode; + +import java.util.List; + +/** + * Client service. + * + * @author Nacos + */ + +public interface ClientService { + + /** + * Retrieves a list of all connected clients. + * + * @return A list of client identifiers. + */ + List getClientList(); + + /** + * Retrieves detailed information about a specific client. + * + * @param clientId The unique identifier of the client. + * @return Detailed information about the client in JSON format. + */ + ObjectNode getClientDetail(String clientId); + + /** + * Retrieves a list of services published by a specific client. + * + * @param clientId The unique identifier of the client. + * @return A list of published services in JSON format. + */ + List getPublishedServiceList(String clientId); + + /** + * Retrieves a list of services subscribed to by a specific client. + * + * @param clientId The unique identifier of the client. + * @return A list of subscribed services in JSON format. + */ + List getSubscribeServiceList(String clientId); + + /** + * Retrieves a list of clients that have published a specific service. + * + * @param namespaceId The namespace of the service. + * @param groupName The group name of the service. + * @param serviceName The name of the service. + * @param ephemeral Whether the service is ephemeral (temporary). + * @param ip The IP address of the client (optional filter). + * @param port The port number of the client (optional filter). + * @return A list of clients that published the service in JSON format. + */ + List getPublishedClientList(String namespaceId, String groupName, String serviceName, boolean ephemeral, String ip, Integer port); + + /** + * Retrieves a list of clients that have subscribed to a specific service. + * + * @param namespaceId The namespace of the service. + * @param groupName The group name of the service. + * @param serviceName The name of the service. + * @param ephemeral Whether the service is ephemeral (temporary). + * @param ip The IP address of the client (optional filter). + * @param port The port number of the client (optional filter). + * @return A list of clients that subscribed to the service in JSON format. + */ + List getSubscribeClientList(String namespaceId, String groupName, String serviceName, boolean ephemeral, String ip, Integer port); + + /** + * Determines the responsible server for handling requests from a specific client based on its IP and port. + * + * @param ip The IP address of the client. + * @param port The port number of the client. + * @return The responsible server information in JSON format. + */ + ObjectNode getResponsibleServer4Client(String ip, String port); +} \ No newline at end of file diff --git a/naming/src/main/java/com/alibaba/nacos/naming/core/ClientServiceV2Impl.java b/naming/src/main/java/com/alibaba/nacos/naming/core/ClientServiceV2Impl.java new file mode 100644 index 00000000000..6e48a1fe8ad --- /dev/null +++ b/naming/src/main/java/com/alibaba/nacos/naming/core/ClientServiceV2Impl.java @@ -0,0 +1,213 @@ +/* + * Copyright 1999-$toady.year Alibaba Group Holding Ltd. + * + * Licensed 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.alibaba.nacos.naming.core; + +import com.alibaba.nacos.common.utils.InternetAddressUtil; +import com.alibaba.nacos.common.utils.JacksonUtils; +import com.alibaba.nacos.core.remote.Connection; +import com.alibaba.nacos.core.remote.ConnectionManager; +import com.alibaba.nacos.core.remote.ConnectionMeta; +import com.alibaba.nacos.naming.core.v2.client.Client; +import com.alibaba.nacos.naming.core.v2.client.impl.ConnectionBasedClient; +import com.alibaba.nacos.naming.core.v2.client.manager.ClientManager; +import com.alibaba.nacos.naming.core.v2.index.ClientServiceIndexesManager; +import com.alibaba.nacos.naming.core.v2.pojo.BatchInstancePublishInfo; +import com.alibaba.nacos.naming.core.v2.pojo.InstancePublishInfo; +import com.alibaba.nacos.naming.core.v2.pojo.Service; +import com.alibaba.nacos.naming.pojo.Subscriber; +import com.fasterxml.jackson.databind.node.ObjectNode; +import org.springframework.stereotype.Component; + +import javax.annotation.Resource; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Objects; + +/** + * Client controller. + * + * @author Nacos + */ + +@Component +public class ClientServiceV2Impl implements ClientService { + + @Resource + private ClientManager clientManager; + + @Resource + private ConnectionManager connectionManager; + + @Resource + private ClientServiceIndexesManager clientServiceIndexesManager; + + @Resource + private DistroMapper distroMapper; + + @Override + public List getClientList() { + return new ArrayList<>(clientManager.allClientId()); + } + + @Override + public ObjectNode getClientDetail(String clientId) { + Client client = clientManager.getClient(clientId); + + ObjectNode result = JacksonUtils.createEmptyJsonNode(); + result.put("clientId", client.getClientId()); + result.put("ephemeral", client.isEphemeral()); + result.put("lastUpdatedTime", client.getLastUpdatedTime()); + + if (client instanceof ConnectionBasedClient) { + // 2.x client + result.put("clientType", "connection"); + Connection connection = connectionManager.getConnection(clientId); + ConnectionMeta connectionMetaInfo = connection.getMetaInfo(); + result.put("connectType", connectionMetaInfo.getConnectType()); + result.put("appName", connectionMetaInfo.getAppName()); + result.put("version", connectionMetaInfo.getVersion()); + result.put("clientIp", connectionMetaInfo.getClientIp()); + result.put("clientPort", clientId.substring(clientId.lastIndexOf('_') + 1)); + } + + return result; + } + + @Override + public List getPublishedServiceList(String clientId) { + Client client = clientManager.getClient(clientId); + Collection allPublishedService = client.getAllPublishedService(); + List res = new ArrayList<>(); + for (Service service : allPublishedService) { + InstancePublishInfo instancePublishInfo = client.getInstancePublishInfo(service); + if (instancePublishInfo instanceof BatchInstancePublishInfo) { + List instancePublishInfos = ((BatchInstancePublishInfo) instancePublishInfo).getInstancePublishInfos(); + for (InstancePublishInfo publishInfo : instancePublishInfos) { + res.add(wrapSingleInstanceNode(publishInfo, service)); + } + } else { + res.add(wrapSingleInstanceNode(instancePublishInfo, service)); + } + } + + return res; + } + + private ObjectNode wrapSingleInstanceNode(InstancePublishInfo instancePublishInfo, Service service) { + ObjectNode item = JacksonUtils.createEmptyJsonNode(); + item.put("namespace", service.getNamespace()); + item.put("group", service.getGroup()); + item.put("serviceName", service.getName()); + item.set("registeredInstance", wrapSingleInstance(instancePublishInfo)); + return item; + } + + private ObjectNode wrapSingleInstance(InstancePublishInfo instancePublishInfo) { + ObjectNode instanceInfo = JacksonUtils.createEmptyJsonNode(); + instanceInfo.put("ip", instancePublishInfo.getIp()); + instanceInfo.put("port", instancePublishInfo.getPort()); + instanceInfo.put("cluster", instancePublishInfo.getCluster()); + return instanceInfo; + } + + @Override + public List getSubscribeServiceList(String clientId) { + Client client = clientManager.getClient(clientId); + Collection allSubscribeService = client.getAllSubscribeService(); + ArrayList res = new ArrayList<>(); + for (Service service : allSubscribeService) { + ObjectNode item = JacksonUtils.createEmptyJsonNode(); + item.put("namespace", service.getNamespace()); + item.put("group", service.getGroup()); + item.put("serviceName", service.getName()); + Subscriber subscriber = client.getSubscriber(service); + ObjectNode subscriberInfo = JacksonUtils.createEmptyJsonNode(); + subscriberInfo.put("app", subscriber.getApp()); + subscriberInfo.put("agent", subscriber.getAgent()); + subscriberInfo.put("addr", subscriber.getAddrStr()); + item.set("subscriberInfo", subscriberInfo); + res.add(item); + } + + return res; + } + + @Override + public List getPublishedClientList(String namespaceId, String groupName, String serviceName, + boolean ephemeral, String ip, Integer port) { + Service service = Service.newService(namespaceId, groupName, serviceName, ephemeral); + Collection allClientsRegisteredService = clientServiceIndexesManager.getAllClientsRegisteredService(service); + ArrayList res = new ArrayList<>(); + for (String clientId : allClientsRegisteredService) { + Client client = clientManager.getClient(clientId); + InstancePublishInfo instancePublishInfo = client.getInstancePublishInfo(service); + if (instancePublishInfo instanceof BatchInstancePublishInfo) { + List list = ((BatchInstancePublishInfo) instancePublishInfo).getInstancePublishInfos(); + for (InstancePublishInfo info : list) { + if (!Objects.equals(info.getIp(), ip) || !Objects + .equals(port, info.getPort())) { + continue; + } + res.add(wrapSingleInstance(info).put("clientId", clientId)); + } + } else { + if (!Objects.equals(instancePublishInfo.getIp(), ip) || !Objects + .equals(port, instancePublishInfo.getPort())) { + continue; + } + res.add(wrapSingleInstance(instancePublishInfo).put("clientId", clientId)); + } + } + + return res; + } + + @Override + public List getSubscribeClientList(String namespaceId, String groupName, String serviceName, + boolean ephemeral, String ip, Integer port) { + Service service = Service.newService(namespaceId, groupName, serviceName, ephemeral); + Collection allClientsSubscribeService = clientServiceIndexesManager + .getAllClientsSubscribeService(service); + ArrayList res = new ArrayList<>(); + for (String clientId : allClientsSubscribeService) { + Client client = clientManager.getClient(clientId); + Subscriber subscriber = client.getSubscriber(service); + boolean ipMatch = Objects.isNull(ip) || Objects.equals(ip, subscriber.getIp()); + boolean portMatch = Objects.isNull(port) || Objects.equals(port, subscriber.getPort()); + if (!ipMatch || !portMatch) { + continue; + } + ObjectNode item = JacksonUtils.createEmptyJsonNode(); + item.put("clientId", clientId); + item.put("ip", subscriber.getIp()); + item.put("port", subscriber.getPort()); + res.add(item); + } + + return res; + } + + @Override + public ObjectNode getResponsibleServer4Client(String ip, String port) { + ObjectNode result = JacksonUtils.createEmptyJsonNode(); + String tag = ip + InternetAddressUtil.IP_PORT_SPLITER + port; + result.put("responsibleServer", distroMapper.mapSrv(tag)); + + return result; + } +} \ No newline at end of file diff --git a/naming/src/main/java/com/alibaba/nacos/naming/core/HealthOperator.java b/naming/src/main/java/com/alibaba/nacos/naming/core/HealthOperator.java index ca89961c31e..520e1090cba 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/core/HealthOperator.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/core/HealthOperator.java @@ -17,6 +17,9 @@ package com.alibaba.nacos.naming.core; import com.alibaba.nacos.api.exception.NacosException; +import com.alibaba.nacos.api.naming.pojo.healthcheck.AbstractHealthChecker; + +import java.util.Map; /** * Persistent Health operator. @@ -40,4 +43,15 @@ public interface HealthOperator { */ void updateHealthStatusForPersistentInstance(String namespace, String fullServiceName, String clusterName, String ip, int port, boolean healthy) throws NacosException; + + /** + * Retrieves a map of available health checkers. + * + *

Each key in the map represents the type of health checker, and the corresponding value is an instance + * of {@link AbstractHealthChecker} that implements the specific health check logic. + * + * @return a map of health checkers, where the key is the health checker type and the value is the + * corresponding {@link AbstractHealthChecker} instance + */ + Map checkers(); } diff --git a/naming/src/main/java/com/alibaba/nacos/naming/core/HealthOperatorV2Impl.java b/naming/src/main/java/com/alibaba/nacos/naming/core/HealthOperatorV2Impl.java index 76235b635e4..c7aef5171a4 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/core/HealthOperatorV2Impl.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/core/HealthOperatorV2Impl.java @@ -18,6 +18,7 @@ import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.naming.pojo.Instance; +import com.alibaba.nacos.api.naming.pojo.healthcheck.AbstractHealthChecker; import com.alibaba.nacos.api.naming.pojo.healthcheck.HealthCheckType; import com.alibaba.nacos.api.naming.utils.NamingUtils; import com.alibaba.nacos.common.utils.InternetAddressUtil; @@ -32,9 +33,13 @@ import com.alibaba.nacos.naming.core.v2.pojo.Service; import com.alibaba.nacos.naming.core.v2.service.ClientOperationService; import com.alibaba.nacos.naming.core.v2.service.ClientOperationServiceProxy; +import com.alibaba.nacos.naming.misc.Loggers; import com.alibaba.nacos.naming.utils.InstanceUtil; import org.springframework.stereotype.Component; +import java.util.HashMap; +import java.util.List; +import java.util.Map; import java.util.Optional; /** @@ -86,6 +91,22 @@ public void updateHealthStatusForPersistentInstance(String namespace, String ful clientOperationService.registerInstance(service, newInstance, clientId); } + @Override + public Map checkers() { + List> classes = HealthCheckType.getLoadedHealthCheckerClasses(); + Map checkerMap = new HashMap<>(8); + for (Class clazz : classes) { + try { + AbstractHealthChecker checker = clazz.newInstance(); + checkerMap.put(checker.getType(), checker); + } catch (InstantiationException | IllegalAccessException e) { + Loggers.EVT_LOG.error("checkers error ", e); + } + } + + return checkerMap; + } + private void throwHealthCheckerException(String fullServiceName, String clusterName) throws NacosException { String errorInfo = String .format("health check is still working, service: %s, cluster: %s", fullServiceName, clusterName); diff --git a/naming/src/main/java/com/alibaba/nacos/naming/core/Operator.java b/naming/src/main/java/com/alibaba/nacos/naming/core/Operator.java new file mode 100644 index 00000000000..0ea46af2693 --- /dev/null +++ b/naming/src/main/java/com/alibaba/nacos/naming/core/Operator.java @@ -0,0 +1,59 @@ +/* + * Copyright 1999-$toady.year Alibaba Group Holding Ltd. + * + * Licensed 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.alibaba.nacos.naming.core; + +import com.alibaba.nacos.naming.misc.SwitchDomain; +import com.alibaba.nacos.naming.model.vo.MetricsInfoVo; + +/** + * Operator service. + */ +public interface Operator { + + /** + * Retrieves the current state of system switches. + * + * @return A {@link SwitchDomain} object containing the current switch configurations. + */ + SwitchDomain switches(); + + /** + * Updates a specific system switch with a new value. + * + * @param entry The name of the switch entry to update. + * @param value The new value to set for the switch. + * @param debug If true, enables debug mode for the operation. + * @throws Exception If an error occurs during the update process. + */ + void updateSwitch(String entry, String value, boolean debug) throws Exception; + + /** + * Retrieves system metrics information. + * + * @param onlyStatus If true, returns only the status information; otherwise, returns full metrics. + * @return A {@link MetricsInfoVo} object containing the requested metrics data. + */ + MetricsInfoVo metrics(boolean onlyStatus); + + /** + * Sets the log level for a specified logger. + * + * @param logName The name of the logger to configure. + * @param logLevel The log level to set (e.g., "DEBUG", "INFO", "WARN", "ERROR"). + */ + void setLogLevel(String logName, String logLevel); +} \ No newline at end of file diff --git a/naming/src/main/java/com/alibaba/nacos/naming/core/OperatorV2Impl.java b/naming/src/main/java/com/alibaba/nacos/naming/core/OperatorV2Impl.java new file mode 100644 index 00000000000..d03111aedcc --- /dev/null +++ b/naming/src/main/java/com/alibaba/nacos/naming/core/OperatorV2Impl.java @@ -0,0 +1,112 @@ +/* + * Copyright 1999-$toady.year Alibaba Group Holding Ltd. + * + * Licensed 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.alibaba.nacos.naming.core; + +import com.alibaba.nacos.naming.cluster.ServerStatusManager; +import com.alibaba.nacos.naming.constants.ClientConstants; +import com.alibaba.nacos.naming.core.v2.client.impl.IpPortBasedClient; +import com.alibaba.nacos.naming.core.v2.client.manager.ClientManager; +import com.alibaba.nacos.naming.misc.Loggers; +import com.alibaba.nacos.naming.misc.SwitchDomain; +import com.alibaba.nacos.naming.misc.SwitchManager; +import com.alibaba.nacos.naming.model.vo.MetricsInfoVo; +import com.alibaba.nacos.naming.monitor.MetricsMonitor; +import com.alibaba.nacos.sys.env.EnvUtil; +import org.springframework.stereotype.Service; + +import javax.annotation.Resource; +import java.util.Collection; + +/** + * OperatorV2Impl. + * + * @author Nacos + */ + +@Service +public class OperatorV2Impl implements Operator { + + @Resource + private SwitchDomain switchDomain; + + @Resource + private SwitchManager switchManager; + + @Resource + private ServerStatusManager serverStatusManager; + + @Resource + private ClientManager clientManager; + + @Override + public SwitchDomain switches() { + return switchDomain; + } + + @Override + public void updateSwitch(String entry, String value, boolean debug) throws Exception { + switchManager.update(entry, value, debug); + } + + @Override + public MetricsInfoVo metrics(boolean onlyStatus) { + MetricsInfoVo metricsInfoVo = new MetricsInfoVo(); + metricsInfoVo.setStatus(serverStatusManager.getServerStatus().name()); + if (onlyStatus) { + return metricsInfoVo; + } + + int connectionBasedClient = 0; + int ephemeralIpPortClient = 0; + int persistentIpPortClient = 0; + int responsibleClientCount = 0; + Collection allClientId = clientManager.allClientId(); + for (String clientId : allClientId) { + if (clientId.contains(IpPortBasedClient.ID_DELIMITER)) { + if (clientId.endsWith(ClientConstants.PERSISTENT_SUFFIX)) { + persistentIpPortClient += 1; + } else { + ephemeralIpPortClient += 1; + } + } else { + connectionBasedClient += 1; + } + if (clientManager.isResponsibleClient(clientManager.getClient(clientId))) { + responsibleClientCount += 1; + } + } + + metricsInfoVo.setServiceCount(MetricsMonitor.getDomCountMonitor().get()); + metricsInfoVo.setInstanceCount(MetricsMonitor.getIpCountMonitor().get()); + metricsInfoVo.setSubscribeCount(MetricsMonitor.getSubscriberCount().get()); + metricsInfoVo.setClientCount(allClientId.size()); + metricsInfoVo.setConnectionBasedClientCount(connectionBasedClient); + metricsInfoVo.setEphemeralIpPortClientCount(ephemeralIpPortClient); + metricsInfoVo.setPersistentIpPortClientCount(persistentIpPortClient); + metricsInfoVo.setResponsibleClientCount(responsibleClientCount); + metricsInfoVo.setCpu(EnvUtil.getCpu()); + metricsInfoVo.setLoad(EnvUtil.getLoad()); + metricsInfoVo.setMem(EnvUtil.getMem()); + + return metricsInfoVo; + } + + @Override + public void setLogLevel(String logName, String logLevel) { + Loggers.setLogLevel(logName, logLevel); + } +} \ No newline at end of file diff --git a/naming/src/main/java/com/alibaba/nacos/naming/core/ServiceOperator.java b/naming/src/main/java/com/alibaba/nacos/naming/core/ServiceOperator.java index 19131aa6051..4e92b58cb2e 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/core/ServiceOperator.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/core/ServiceOperator.java @@ -70,7 +70,7 @@ public interface ServiceOperator { ObjectNode queryService(String namespaceId, String serviceName) throws NacosException; /** - * Page list service name. + * List service detail information. * * @param namespaceId namespace id of services * @param groupName group name of services @@ -96,4 +96,19 @@ public interface ServiceOperator { * @throws NacosException nacos exception during query */ Collection searchServiceName(String namespaceId, String expr) throws NacosException; + + /** + * Get the list of subscribers for a service. + * + * @param pageNo the page number + * @param pageSize the size of the page + * @param namespaceId the namespace ID + * @param serviceName the service name + * @param groupName the group name + * @param aggregation whether to aggregate the results + * @return a JSON node containing the list of subscribers + * @throws Exception if an error occurs during fetching subscribers + */ + ObjectNode getSubscribers(int pageNo, int pageSize, String namespaceId, String serviceName, String groupName, + boolean aggregation) throws Exception; } diff --git a/naming/src/main/java/com/alibaba/nacos/naming/core/ServiceOperatorV2Impl.java b/naming/src/main/java/com/alibaba/nacos/naming/core/ServiceOperatorV2Impl.java index 738710f3c8c..893dceab2b3 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/core/ServiceOperatorV2Impl.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/core/ServiceOperatorV2Impl.java @@ -32,8 +32,10 @@ import com.alibaba.nacos.naming.core.v2.metadata.NamingMetadataOperateService; import com.alibaba.nacos.naming.core.v2.metadata.ServiceMetadata; import com.alibaba.nacos.naming.core.v2.pojo.Service; +import com.alibaba.nacos.naming.misc.Loggers; import com.alibaba.nacos.naming.pojo.ClusterInfo; import com.alibaba.nacos.naming.pojo.ServiceDetailInfo; +import com.alibaba.nacos.naming.pojo.Subscriber; import com.fasterxml.jackson.databind.node.ArrayNode; import com.fasterxml.jackson.databind.node.ObjectNode; import org.springframework.stereotype.Component; @@ -42,6 +44,7 @@ import java.util.Collections; import java.util.HashMap; import java.util.HashSet; +import java.util.List; import java.util.Map; import java.util.Objects; @@ -59,11 +62,14 @@ public class ServiceOperatorV2Impl implements ServiceOperator { private final ServiceStorage serviceStorage; + private final SubscribeManager subscribeManager; + public ServiceOperatorV2Impl(NamingMetadataOperateService metadataOperateService, - NamingMetadataManager metadataManager, ServiceStorage serviceStorage) { + NamingMetadataManager metadataManager, ServiceStorage serviceStorage, SubscribeManager subscribeManager) { this.metadataOperateService = metadataOperateService; this.metadataManager = metadataManager; this.serviceStorage = serviceStorage; + this.subscribeManager = subscribeManager; } @Override @@ -253,4 +259,40 @@ public Collection searchServiceName(String namespaceId, String expr) thr } return result; } + + @Override + public ObjectNode getSubscribers(int pageNo, int pageSize, String namespaceId, String serviceName, String groupName, + boolean aggregation) throws Exception { + + Service service = Service.newService(namespaceId, groupName, serviceName); + + ObjectNode result = JacksonUtils.createEmptyJsonNode(); + + int count = 0; + + try { + List subscribers = subscribeManager.getSubscribers(service, aggregation); + + int start = (pageNo - 1) * pageSize; + if (start < 0) { + start = 0; + } + + int end = start + pageSize; + count = subscribers.size(); + if (end > count) { + end = count; + } + + result.replace("subscribers", JacksonUtils.transferToJsonNode(subscribers.subList(start, end))); + result.put("count", count); + + return result; + } catch (Exception e) { + Loggers.SRV_LOG.warn("query subscribers failed!", e); + result.replace("subscribers", JacksonUtils.createEmptyArrayNode()); + result.put("count", count); + return result; + } + } } \ No newline at end of file diff --git a/naming/src/main/java/com/alibaba/nacos/naming/misc/UtilsAndCommons.java b/naming/src/main/java/com/alibaba/nacos/naming/misc/UtilsAndCommons.java index 0f9a3bb3d79..246c6f31d76 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/misc/UtilsAndCommons.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/misc/UtilsAndCommons.java @@ -51,10 +51,14 @@ public class UtilsAndCommons { public static final String NACOS_SERVER_VERSION_2 = "/v2"; + public static final String NACOS_SERVER_VERSION_3 = "/v3"; + public static final String DEFAULT_NACOS_NAMING_CONTEXT = NACOS_SERVER_VERSION + "/ns"; public static final String DEFAULT_NACOS_NAMING_CONTEXT_V2 = NACOS_SERVER_VERSION_2 + "/ns"; + public static final String DEFAULT_NACOS_NAMING_ADMIN_CONTEXT_V3 = NACOS_SERVER_VERSION_3 + "/admin/ns"; + public static final String NACOS_NAMING_CONTEXT = DEFAULT_NACOS_NAMING_CONTEXT; public static final String NACOS_NAMING_CATALOG_CONTEXT = "/catalog"; @@ -71,6 +75,20 @@ public class UtilsAndCommons { public static final String NACOS_NAMING_OPERATOR_CONTEXT = "/operator"; + public static final String NACOS_NAMING_OPERATOR_ADMIN_CONTEXT_V3 = "/ops"; + + public static final String SERVICE_CONTROLLER_V3_ADMIN_PATH = DEFAULT_NACOS_NAMING_ADMIN_CONTEXT_V3 + NACOS_NAMING_SERVICE_CONTEXT; + + public static final String INSTANCE_CONTROLLER_V3_ADMIN_PATH = DEFAULT_NACOS_NAMING_ADMIN_CONTEXT_V3 + NACOS_NAMING_INSTANCE_CONTEXT; + + public static final String CLUSTER_CONTROLLER_V3_ADMIN_PATH = DEFAULT_NACOS_NAMING_ADMIN_CONTEXT_V3 + NACOS_NAMING_CLUSTER_CONTEXT; + + public static final String HEALTH_CONTROLLER_V3_ADMIN_PATH = DEFAULT_NACOS_NAMING_ADMIN_CONTEXT_V3 + NACOS_NAMING_HEALTH_CONTEXT; + + public static final String OPERATOR_CONTROLLER_V3_ADMIN_PATH = DEFAULT_NACOS_NAMING_ADMIN_CONTEXT_V3 + NACOS_NAMING_OPERATOR_ADMIN_CONTEXT_V3; + + public static final String CLIENT_CONTROLLER_V3_ADMIN_PATH = DEFAULT_NACOS_NAMING_ADMIN_CONTEXT_V3 + NACOS_NAMING_CLIENT_CONTEXT; + // ********************** Nacos HTTP Context ************************ // public static final String NACOS_SERVER_HEADER = Constants.NACOS_SERVER_HEADER; diff --git a/naming/src/test/java/com/alibaba/nacos/naming/controllers/v2/ClientInfoControllerV2Test.java b/naming/src/test/java/com/alibaba/nacos/naming/controllers/v2/ClientInfoControllerV2Test.java index 5eae88e39a9..6c953aed149 100644 --- a/naming/src/test/java/com/alibaba/nacos/naming/controllers/v2/ClientInfoControllerV2Test.java +++ b/naming/src/test/java/com/alibaba/nacos/naming/controllers/v2/ClientInfoControllerV2Test.java @@ -19,15 +19,17 @@ import com.alibaba.nacos.common.utils.JacksonUtils; import com.alibaba.nacos.core.remote.ConnectionManager; import com.alibaba.nacos.naming.BaseTest; +import com.alibaba.nacos.naming.core.ClientService; import com.alibaba.nacos.naming.core.v2.client.impl.ConnectionBasedClient; import com.alibaba.nacos.naming.core.v2.client.impl.IpPortBasedClient; import com.alibaba.nacos.naming.core.v2.client.manager.ClientManager; import com.alibaba.nacos.naming.core.v2.index.ClientServiceIndexesManager; -import com.alibaba.nacos.naming.core.v2.pojo.BatchInstancePublishInfo; import com.alibaba.nacos.naming.core.v2.pojo.InstancePublishInfo; import com.alibaba.nacos.naming.core.v2.pojo.Service; import com.alibaba.nacos.naming.misc.UtilsAndCommons; import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ObjectNode; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -44,6 +46,7 @@ import org.springframework.test.web.servlet.setup.MockMvcBuilders; import java.util.Arrays; +import java.util.List; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.ArgumentMatchers.anyString; @@ -65,6 +68,9 @@ class ClientInfoControllerV2Test extends BaseTest { @Mock private ConnectionManager connectionManager; + @Mock + private ClientService clientServiceV2Impl; + @Mock private ClientServiceIndexesManager clientServiceIndexesManager; @@ -89,7 +95,7 @@ void testGetClientList() throws Exception { MockHttpServletResponse response = mockmvc.perform(mockHttpServletRequestBuilder).andReturn().getResponse(); assertEquals(200, response.getStatus()); JsonNode jsonNode = JacksonUtils.toObj(response.getContentAsString()).get("data"); - assertEquals(2, jsonNode.size()); + assertEquals(0, jsonNode.size()); } @Test @@ -102,44 +108,42 @@ void testGetClientDetail() throws Exception { @Test void testGetPublishedServiceList() throws Exception { - // single instance + ObjectMapper objectMapper = new ObjectMapper(); + ObjectNode objectNode = objectMapper.createObjectNode(); + objectNode.put("serviceName", "test"); + List serviceList = Arrays.asList(objectNode); + when(clientManager.getClient("test1")).thenReturn(connectionBasedClient); + when(clientServiceV2Impl.getPublishedServiceList("test1")) + .thenReturn(serviceList); + Service service = Service.newService("test", "test", "test"); connectionBasedClient.addServiceInstance(service, new InstancePublishInfo("127.0.0.1", 8848)); MockHttpServletRequestBuilder mockHttpServletRequestBuilder = MockMvcRequestBuilders.get(URL + "/publish/list") .param("clientId", "test1"); mockmvc.perform(mockHttpServletRequestBuilder).andExpect(MockMvcResultMatchers.jsonPath("$.data.length()").value(1)); - // batch instances - BatchInstancePublishInfo instancePublishInfo = new BatchInstancePublishInfo(); - instancePublishInfo.setInstancePublishInfos( - Arrays.asList(new InstancePublishInfo("127.0.0.1", 8848), new InstancePublishInfo("127.0.0.1", 8849))); - connectionBasedClient.addServiceInstance(service, instancePublishInfo); - mockHttpServletRequestBuilder = MockMvcRequestBuilders.get(URL + "/publish/list").param("clientId", "test1"); - mockmvc.perform(mockHttpServletRequestBuilder).andExpect(MockMvcResultMatchers.jsonPath("$.data.length()").value(2)); } @Test void testGetPublishedClientList() throws Exception { String baseTestKey = "nacos-getPublishedClientList-test"; // single instance - Service service = Service.newService(baseTestKey, baseTestKey, baseTestKey); - when(clientServiceIndexesManager.getAllClientsRegisteredService(service)).thenReturn(Arrays.asList("test")); + final Service service = Service.newService(baseTestKey, baseTestKey, baseTestKey); + + ObjectMapper objectMapper = new ObjectMapper(); + ObjectNode objectNode = objectMapper.createObjectNode(); + objectNode.put("serviceName", "test"); + final List serviceList = Arrays.asList(objectNode); + + when(clientManager.getClient("test1")).thenReturn(connectionBasedClient); when(clientManager.getClient("test")).thenReturn(connectionBasedClient); connectionBasedClient.addServiceInstance(service, new InstancePublishInfo("127.0.0.1", 8848)); + + when(clientServiceV2Impl.getPublishedClientList(baseTestKey, baseTestKey, baseTestKey, true, "127.0.0.1", 8848)) + .thenReturn(serviceList); MockHttpServletRequestBuilder mockHttpServletRequestBuilder = MockMvcRequestBuilders.get(URL + "/service/publisher/list") .param("namespaceId", baseTestKey).param("groupName", baseTestKey).param("serviceName", baseTestKey) .param("ip", "127.0.0.1").param("port", "8848"); mockmvc.perform(mockHttpServletRequestBuilder).andExpect(MockMvcResultMatchers.jsonPath("$.data.length()").value(1)); - - // batch instances - when(clientServiceIndexesManager.getAllClientsRegisteredService(service)).thenReturn(Arrays.asList("test")); - when(clientManager.getClient("test")).thenReturn(connectionBasedClient); - BatchInstancePublishInfo instancePublishInfo = new BatchInstancePublishInfo(); - instancePublishInfo.setInstancePublishInfos( - Arrays.asList(new InstancePublishInfo("127.0.0.1", 8848), new InstancePublishInfo("127.0.0.1", 8849))); - connectionBasedClient.addServiceInstance(service, instancePublishInfo); - mockHttpServletRequestBuilder = MockMvcRequestBuilders.get(URL + "/service/publisher/list").param("namespaceId", baseTestKey) - .param("groupName", baseTestKey).param("serviceName", baseTestKey).param("ip", "127.0.0.1").param("port", "8848"); - mockmvc.perform(mockHttpServletRequestBuilder).andExpect(MockMvcResultMatchers.jsonPath("$.data.length()").value(1)); } } diff --git a/naming/src/test/java/com/alibaba/nacos/naming/controllers/v2/OperatorControllerV2Test.java b/naming/src/test/java/com/alibaba/nacos/naming/controllers/v2/OperatorControllerV2Test.java index b1673ab6b5a..a4d96abad6f 100644 --- a/naming/src/test/java/com/alibaba/nacos/naming/controllers/v2/OperatorControllerV2Test.java +++ b/naming/src/test/java/com/alibaba/nacos/naming/controllers/v2/OperatorControllerV2Test.java @@ -20,6 +20,7 @@ import com.alibaba.nacos.api.model.v2.Result; import com.alibaba.nacos.naming.cluster.ServerStatus; import com.alibaba.nacos.naming.cluster.ServerStatusManager; +import com.alibaba.nacos.naming.core.Operator; import com.alibaba.nacos.naming.core.v2.client.manager.ClientManager; import com.alibaba.nacos.naming.misc.SwitchDomain; import com.alibaba.nacos.naming.misc.SwitchManager; @@ -35,9 +36,6 @@ import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.mock.env.MockEnvironment; -import java.util.Collection; -import java.util.HashSet; - import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.fail; @@ -65,9 +63,12 @@ class OperatorControllerV2Test { @Mock private ClientManager clientManager; + @Mock + private Operator operatorV2Impl; + @BeforeEach void setUp() { - this.operatorControllerV2 = new OperatorControllerV2(switchManager, serverStatusManager, switchDomain, clientManager); + this.operatorControllerV2 = new OperatorControllerV2(operatorV2Impl); MockEnvironment environment = new MockEnvironment(); environment.setProperty(Constants.SUPPORT_UPGRADE_FROM_1X, "true"); EnvUtil.setEnvironment(environment); @@ -75,9 +76,14 @@ void setUp() { @Test void testSwitches() { + SwitchDomain switchDomain = new SwitchDomain(); + switchDomain.setDefaultInstanceEphemeral(true); + switchDomain.setDefaultPushCacheMillis(1000L); + Mockito.when(operatorV2Impl.switches()).thenReturn(switchDomain); Result result = operatorControllerV2.switches(); assertEquals(ErrorCode.SUCCESS.getCode(), result.getCode()); - assertEquals(this.switchDomain, result.getData()); + assertEquals(1000L, result.getData().getDefaultPushCacheMillis()); + assertEquals(true, result.getData().isDefaultInstanceEphemeral()); } @Test @@ -99,24 +105,11 @@ void testUpdateSwitches() { @Test void testMetrics() { - Mockito.when(serverStatusManager.getServerStatus()).thenReturn(ServerStatus.UP); - Collection clients = new HashSet<>(); - clients.add("1628132208793_127.0.0.1_8080"); - clients.add("127.0.0.1:8081#true"); - clients.add("127.0.0.1:8082#false"); - Mockito.when(clientManager.allClientId()).thenReturn(clients); - Mockito.when(clientManager.isResponsibleClient(null)).thenReturn(Boolean.TRUE); - + MetricsInfoVo metricsInfoVo = new MetricsInfoVo(); + metricsInfoVo.setStatus(ServerStatus.UP.toString()); + Mockito.when(operatorV2Impl.metrics(false)).thenReturn(metricsInfoVo); Result result = operatorControllerV2.metrics(false); assertEquals(ErrorCode.SUCCESS.getCode(), result.getCode()); - - MetricsInfoVo metricsInfoVo = result.getData(); - - assertEquals(ServerStatus.UP.toString(), metricsInfoVo.getStatus()); - assertEquals(3, metricsInfoVo.getClientCount().intValue()); - assertEquals(1, metricsInfoVo.getConnectionBasedClientCount().intValue()); - assertEquals(1, metricsInfoVo.getEphemeralIpPortClientCount().intValue()); - assertEquals(1, metricsInfoVo.getPersistentIpPortClientCount().intValue()); - assertEquals(3, metricsInfoVo.getResponsibleClientCount().intValue()); + assertEquals(ServerStatus.UP.toString(), result.getData().getStatus()); } } diff --git a/naming/src/test/java/com/alibaba/nacos/naming/controllers/v3/ClientControllerV3Test.java b/naming/src/test/java/com/alibaba/nacos/naming/controllers/v3/ClientControllerV3Test.java new file mode 100644 index 00000000000..6e80d835db2 --- /dev/null +++ b/naming/src/test/java/com/alibaba/nacos/naming/controllers/v3/ClientControllerV3Test.java @@ -0,0 +1,145 @@ +/* + * Copyright 1999-2022 Alibaba Group Holding Ltd. + * + * Licensed 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.alibaba.nacos.naming.controllers.v3; + +import com.alibaba.nacos.common.utils.JacksonUtils; +import com.alibaba.nacos.naming.BaseTest; +import com.alibaba.nacos.naming.core.ClientService; +import com.alibaba.nacos.naming.core.v2.client.impl.ConnectionBasedClient; +import com.alibaba.nacos.naming.core.v2.client.impl.IpPortBasedClient; +import com.alibaba.nacos.naming.core.v2.client.manager.ClientManager; +import com.alibaba.nacos.naming.core.v2.pojo.InstancePublishInfo; +import com.alibaba.nacos.naming.core.v2.pojo.Service; +import com.alibaba.nacos.naming.misc.UtilsAndCommons; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ObjectNode; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; +import org.springframework.mock.web.MockHttpServletResponse; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder; +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; +import org.springframework.test.web.servlet.result.MockMvcResultMatchers; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; + +import java.util.Arrays; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.when; + +/** + * ClientControllerV3Test. + * + * @author Nacos + */ +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.LENIENT) +class ClientControllerV3Test extends BaseTest { + + private static final String URL = UtilsAndCommons.CLIENT_CONTROLLER_V3_ADMIN_PATH; + + @InjectMocks + ClientControllerV3 clientControllerV3; + + @Mock + private ClientManager clientManager; + + @Mock + private ClientService clientServiceV2Impl; + + private MockMvc mockmvc; + + private IpPortBasedClient ipPortBasedClient; + + private ConnectionBasedClient connectionBasedClient; + + @BeforeEach + public void before() { + when(clientManager.allClientId()).thenReturn(Arrays.asList("127.0.0.1:8080#test1", "test2#test2")); + when(clientManager.contains(anyString())).thenReturn(true); + mockmvc = MockMvcBuilders.standaloneSetup(clientControllerV3).build(); + ipPortBasedClient = new IpPortBasedClient("127.0.0.1:8080#test1", false); + connectionBasedClient = new ConnectionBasedClient("test2", true, 1L); + } + + @Test + void testGetClientList() throws Exception { + MockHttpServletRequestBuilder mockHttpServletRequestBuilder = MockMvcRequestBuilders.get(URL + "/list"); + MockHttpServletResponse response = mockmvc.perform(mockHttpServletRequestBuilder).andReturn().getResponse(); + assertEquals(200, response.getStatus()); + JsonNode jsonNode = JacksonUtils.toObj(response.getContentAsString()).get("data"); + assertEquals(0, jsonNode.size()); + } + + @Test + void testGetClientDetail() throws Exception { + when(clientManager.getClient("test1")).thenReturn(ipPortBasedClient); + MockHttpServletRequestBuilder mockHttpServletRequestBuilder = MockMvcRequestBuilders.get(URL).param("clientId", "test1"); + MockHttpServletResponse response = mockmvc.perform(mockHttpServletRequestBuilder).andReturn().getResponse(); + assertEquals(200, response.getStatus()); + } + + @Test + void testGetPublishedServiceList() throws Exception { + ObjectMapper objectMapper = new ObjectMapper(); + ObjectNode objectNode = objectMapper.createObjectNode(); + objectNode.put("serviceName", "test"); + List serviceList = Arrays.asList(objectNode); + + when(clientManager.getClient("test1")).thenReturn(connectionBasedClient); + when(clientServiceV2Impl.getPublishedServiceList("test1")) + .thenReturn(serviceList); + + Service service = Service.newService("test", "test", "test"); + connectionBasedClient.addServiceInstance(service, new InstancePublishInfo("127.0.0.1", 8848)); + MockHttpServletRequestBuilder mockHttpServletRequestBuilder = MockMvcRequestBuilders.get(URL + "/publish/list") + .param("clientId", "test1"); + mockmvc.perform(mockHttpServletRequestBuilder).andExpect(MockMvcResultMatchers.jsonPath("$.data.length()").value(1)); + } + + @Test + void testGetPublishedClientList() throws Exception { + String baseTestKey = "nacos-getPublishedClientList-test"; + // single instance + final Service service = Service.newService(baseTestKey, baseTestKey, baseTestKey); + + ObjectMapper objectMapper = new ObjectMapper(); + ObjectNode objectNode = objectMapper.createObjectNode(); + objectNode.put("serviceName", "test"); + final List serviceList = Arrays.asList(objectNode); + + when(clientManager.getClient("test1")).thenReturn(connectionBasedClient); + when(clientManager.getClient("test")).thenReturn(connectionBasedClient); + connectionBasedClient.addServiceInstance(service, new InstancePublishInfo("127.0.0.1", 8848)); + + when(clientServiceV2Impl.getPublishedClientList(baseTestKey, baseTestKey, baseTestKey, true, "127.0.0.1", 8848)) + .thenReturn(serviceList); + MockHttpServletRequestBuilder mockHttpServletRequestBuilder = MockMvcRequestBuilders.get(URL + "/service/publisher/list") + .param("namespaceId", baseTestKey).param("groupName", baseTestKey).param("serviceName", baseTestKey) + .param("ip", "127.0.0.1").param("port", "8848"); + mockmvc.perform(mockHttpServletRequestBuilder).andExpect(MockMvcResultMatchers.jsonPath("$.data.length()").value(1)); + } +} diff --git a/naming/src/test/java/com/alibaba/nacos/naming/controllers/v3/ClusterControllerV3Test.java b/naming/src/test/java/com/alibaba/nacos/naming/controllers/v3/ClusterControllerV3Test.java new file mode 100644 index 00000000000..f2417dd48c0 --- /dev/null +++ b/naming/src/test/java/com/alibaba/nacos/naming/controllers/v3/ClusterControllerV3Test.java @@ -0,0 +1,80 @@ +/* + * Copyright 1999-2018 Alibaba Group Holding Ltd. + * + * Licensed 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.alibaba.nacos.naming.controllers.v3; + +import com.alibaba.nacos.naming.BaseTest; +import com.alibaba.nacos.naming.core.ClusterOperatorV2Impl; +import com.alibaba.nacos.naming.core.v2.metadata.ClusterMetadata; +import com.alibaba.nacos.naming.model.form.UpdateClusterForm; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; + +import javax.servlet.http.HttpServletRequest; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +/** + * ClusterControllerV3Test. + * + * @author Nacos + */ +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.LENIENT) +class ClusterControllerV3Test extends BaseTest { + + @Mock + private ClusterOperatorV2Impl clusterOperatorV2; + + @Mock + private HttpServletRequest request; + + private ClusterControllerV3 clusterControllerV3; + + @BeforeEach + public void before() { + super.before(); + clusterControllerV3 = new ClusterControllerV3(clusterOperatorV2); + } + + @Test + void testUpdate() throws Exception { + UpdateClusterForm updateClusterForm = new UpdateClusterForm(); + updateClusterForm.setNamespaceId("test-namespace"); + updateClusterForm.setClusterName(TEST_CLUSTER_NAME); + updateClusterForm.setServiceName(TEST_SERVICE_NAME); + updateClusterForm.setCheckPort(1); + updateClusterForm.setUseInstancePort4Check(true); + updateClusterForm.setHealthChecker("{\"type\":\"HTTP\"}"); + + assertEquals("ok", clusterControllerV3.update(updateClusterForm).getData()); + verify(clusterOperatorV2).updateClusterMetadata(eq("test-namespace"), eq(TEST_SERVICE_NAME), eq(TEST_CLUSTER_NAME), + any(ClusterMetadata.class)); + } + + private void mockRequestParameter(String paramKey, String value) { + when(request.getParameter(paramKey)).thenReturn(value); + } +} diff --git a/naming/src/test/java/com/alibaba/nacos/naming/controllers/v3/HealthControllerV3Test.java b/naming/src/test/java/com/alibaba/nacos/naming/controllers/v3/HealthControllerV3Test.java new file mode 100644 index 00000000000..c35cbdcef1c --- /dev/null +++ b/naming/src/test/java/com/alibaba/nacos/naming/controllers/v3/HealthControllerV3Test.java @@ -0,0 +1,94 @@ +/* + * Copyright 1999-2022 Alibaba Group Holding Ltd. + * + * Licensed 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.alibaba.nacos.naming.controllers.v3; + +import com.alibaba.nacos.api.model.v2.Result; +import com.alibaba.nacos.api.naming.pojo.healthcheck.AbstractHealthChecker; +import com.alibaba.nacos.api.naming.utils.NamingUtils; +import com.alibaba.nacos.common.utils.JacksonUtils; +import com.alibaba.nacos.naming.BaseTest; +import com.alibaba.nacos.naming.core.HealthOperatorV2Impl; +import com.alibaba.nacos.naming.misc.UtilsAndCommons; +import com.alibaba.nacos.naming.model.form.UpdateHealthForm; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.mock.web.MockHttpServletResponse; +import org.springframework.test.util.ReflectionTestUtils; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder; +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; + +import java.util.Map; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.Mockito.doNothing; + +/** + * HealthControllerV3Test. + * + * @author Nacos + */ +@ExtendWith(MockitoExtension.class) +class HealthControllerV3Test extends BaseTest { + + @Mock + private HealthOperatorV2Impl healthOperatorV2; + + @InjectMocks + private HealthControllerV3 healthControllerV3; + + private MockMvc mockmvc; + + private UpdateHealthForm updateHealthForm; + + @BeforeEach + public void before() { + ReflectionTestUtils.setField(healthControllerV3, "healthOperatorV2", healthOperatorV2); + mockmvc = MockMvcBuilders.standaloneSetup(healthControllerV3).build(); + updateHealthForm = new UpdateHealthForm(); + updateHealthForm.setHealthy(true); + updateHealthForm.setNamespaceId(TEST_NAMESPACE); + updateHealthForm.setClusterName(TEST_CLUSTER_NAME); + updateHealthForm.setGroupName(TEST_GROUP_NAME); + updateHealthForm.setServiceName(TEST_SERVICE_NAME); + updateHealthForm.setIp("123.123.123.123"); + updateHealthForm.setPort(8888); + } + + @Test + void testUpdate() throws Exception { + doNothing().when(healthOperatorV2).updateHealthStatusForPersistentInstance(TEST_NAMESPACE, + NamingUtils.getGroupedName(updateHealthForm.getServiceName(), updateHealthForm.getGroupName()), TEST_CLUSTER_NAME, + "123.123.123.123", 8888, true); + MockHttpServletRequestBuilder builder = convert(updateHealthForm, + MockMvcRequestBuilders.put(UtilsAndCommons.HEALTH_CONTROLLER_V3_ADMIN_PATH + "/instance")); + MockHttpServletResponse response = mockmvc.perform(builder).andReturn().getResponse(); + assertEquals(200, response.getStatus()); + assertEquals("ok", JacksonUtils.toObj(response.getContentAsString()).get("data").asText()); + } + + @Test + void testCheckers() throws Exception { + Result> checkers = healthControllerV3.checkers(); + assertEquals(0, checkers.getCode()); + } +} diff --git a/naming/src/test/java/com/alibaba/nacos/naming/controllers/v3/InstanceControllerV3Test.java b/naming/src/test/java/com/alibaba/nacos/naming/controllers/v3/InstanceControllerV3Test.java new file mode 100644 index 00000000000..50634507739 --- /dev/null +++ b/naming/src/test/java/com/alibaba/nacos/naming/controllers/v3/InstanceControllerV3Test.java @@ -0,0 +1,253 @@ +/* + * Copyright 1999-2022 Alibaba Group Holding Ltd. + * + * Licensed 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.alibaba.nacos.naming.controllers.v3; + +import com.alibaba.nacos.api.model.v2.ErrorCode; +import com.alibaba.nacos.api.model.v2.Result; +import com.alibaba.nacos.api.naming.pojo.Instance; +import com.alibaba.nacos.api.naming.pojo.ServiceInfo; +import com.alibaba.nacos.common.notify.Event; +import com.alibaba.nacos.common.notify.NotifyCenter; +import com.alibaba.nacos.common.notify.listener.SmartSubscriber; +import com.alibaba.nacos.common.trace.event.naming.UpdateInstanceTraceEvent; +import com.alibaba.nacos.common.utils.JacksonUtils; +import com.alibaba.nacos.naming.BaseTest; +import com.alibaba.nacos.naming.core.InstanceOperatorClientImpl; +import com.alibaba.nacos.naming.misc.UtilsAndCommons; +import com.alibaba.nacos.naming.model.form.InstanceForm; +import com.alibaba.nacos.naming.model.form.InstanceMetadataBatchOperationForm; +import com.alibaba.nacos.naming.model.vo.InstanceDetailInfoVo; +import com.alibaba.nacos.naming.model.vo.InstanceMetadataBatchOperationVo; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.test.util.ReflectionTestUtils; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder; +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; + +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; +import java.util.concurrent.TimeUnit; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +/** + * InstanceControllerV3Test. + * + * @author Nacos + */ + +@ExtendWith(MockitoExtension.class) +class InstanceControllerV3Test extends BaseTest { + + @InjectMocks + private InstanceControllerV3 instanceControllerV3; + + @Mock + private InstanceOperatorClientImpl instanceServiceV2; + + private MockMvc mockmvc; + + private SmartSubscriber subscriber; + + private volatile Class eventReceivedClass; + + @BeforeEach + public void before() { + super.before(); + ReflectionTestUtils.setField(instanceControllerV3, "instanceServiceV2", instanceServiceV2); + mockmvc = MockMvcBuilders.standaloneSetup(instanceControllerV3).build(); + + subscriber = new SmartSubscriber() { + @Override + public List> subscribeTypes() { + List> result = new LinkedList<>(); + result.add(UpdateInstanceTraceEvent.class); + return result; + } + + @Override + public void onEvent(Event event) { + eventReceivedClass = event.getClass(); + } + }; + NotifyCenter.registerSubscriber(subscriber); + } + + @AfterEach + void tearDown() throws Exception { + NotifyCenter.deregisterSubscriber(subscriber); + NotifyCenter.deregisterPublisher(UpdateInstanceTraceEvent.class); + eventReceivedClass = null; + } + + @Test + void registerInstance() throws Exception { + + InstanceForm instanceForm = new InstanceForm(); + instanceForm.setNamespaceId(TEST_NAMESPACE); + instanceForm.setGroupName("DEFAULT_GROUP"); + instanceForm.setServiceName("test-service"); + instanceForm.setIp(TEST_IP); + instanceForm.setClusterName(TEST_CLUSTER_NAME); + instanceForm.setPort(9999); + instanceForm.setHealthy(true); + instanceForm.setWeight(1.0); + instanceForm.setEnabled(true); + instanceForm.setMetadata(TEST_METADATA); + instanceForm.setEphemeral(true); + + Result result = instanceControllerV3.register(instanceForm); + + verify(instanceServiceV2).registerInstance(eq(TEST_NAMESPACE), eq(TEST_SERVICE_NAME), any()); + + assertEquals(ErrorCode.SUCCESS.getCode(), result.getCode()); + assertEquals("ok", result.getData()); + } + + @Test + void deregisterInstance() throws Exception { + + InstanceForm instanceForm = new InstanceForm(); + instanceForm.setNamespaceId(TEST_NAMESPACE); + instanceForm.setGroupName("DEFAULT_GROUP"); + instanceForm.setServiceName("test-service"); + instanceForm.setIp(TEST_IP); + instanceForm.setClusterName(TEST_CLUSTER_NAME); + instanceForm.setPort(9999); + instanceForm.setHealthy(true); + instanceForm.setWeight(1.0); + instanceForm.setEnabled(true); + instanceForm.setMetadata(TEST_METADATA); + instanceForm.setEphemeral(true); + + Result result = instanceControllerV3.deregister(instanceForm); + + verify(instanceServiceV2).removeInstance(eq(TEST_NAMESPACE), eq(TEST_SERVICE_NAME), any()); + + assertEquals(ErrorCode.SUCCESS.getCode(), result.getCode()); + assertEquals("ok", result.getData()); + + } + + @Test + void updateInstance() throws Exception { + InstanceForm instanceForm = new InstanceForm(); + instanceForm.setNamespaceId(TEST_NAMESPACE); + instanceForm.setGroupName("DEFAULT_GROUP"); + instanceForm.setServiceName("test-service"); + instanceForm.setIp(TEST_IP); + instanceForm.setClusterName(TEST_CLUSTER_NAME); + instanceForm.setPort(9999); + instanceForm.setHealthy(true); + instanceForm.setWeight(1.0); + instanceForm.setEnabled(true); + instanceForm.setMetadata(TEST_METADATA); + instanceForm.setEphemeral(true); + + Result result = instanceControllerV3.update(instanceForm); + + verify(instanceServiceV2).updateInstance(eq(TEST_NAMESPACE), eq(TEST_SERVICE_NAME), any()); + + assertEquals(ErrorCode.SUCCESS.getCode(), result.getCode()); + assertEquals("ok", result.getData()); + TimeUnit.SECONDS.sleep(1); + assertEquals(UpdateInstanceTraceEvent.class, eventReceivedClass); + } + + @Test + void batchUpdateInstanceMetadata() throws Exception { + + InstanceMetadataBatchOperationForm form = new InstanceMetadataBatchOperationForm(); + form.setNamespaceId(TEST_NAMESPACE); + form.setGroupName("DEFAULT"); + form.setServiceName("test-service"); + form.setConsistencyType("ephemeral"); + form.setInstances(TEST_INSTANCE_INFO_LIST); + form.setMetadata(TEST_METADATA); + + ArrayList ipList = new ArrayList<>(); + ipList.add(TEST_IP); + when(instanceServiceV2.batchUpdateMetadata(eq(TEST_NAMESPACE), any(), any())).thenReturn(ipList); + + InstanceMetadataBatchOperationVo expectUpdate = new InstanceMetadataBatchOperationVo(ipList); + + Result result = instanceControllerV3.batchUpdateInstanceMetadata(form); + verify(instanceServiceV2).batchUpdateMetadata(eq(TEST_NAMESPACE), any(), any()); + + assertEquals(ErrorCode.SUCCESS.getCode(), result.getCode()); + assertEquals(expectUpdate.getUpdated().size(), result.getData().getUpdated().size()); + assertEquals(expectUpdate.getUpdated().get(0), result.getData().getUpdated().get(0)); + } + + @Test + void partialUpdateInstance() throws Exception { + MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.put( + UtilsAndCommons.INSTANCE_CONTROLLER_V3_ADMIN_PATH) + .param("namespaceId", TEST_NAMESPACE).param("serviceName", TEST_SERVICE_NAME).param("ip", TEST_IP) + .param("cluster", TEST_CLUSTER_NAME).param("port", "9999").param("healthy", "true").param("weight", "2.0") + .param("enabled", "true").param("metadata", TEST_METADATA).param("ephemeral", "false"); + String actualValue = mockmvc.perform(builder).andReturn().getResponse().getContentAsString(); + assertEquals("ok", JacksonUtils.toObj(actualValue).get("data").asText()); + } + + @Test + void listInstance() throws Exception { + + ServiceInfo serviceInfo = new ServiceInfo(); + serviceInfo.setName("serviceInfo"); + + when(instanceServiceV2.listInstance(eq(TEST_NAMESPACE), eq(TEST_SERVICE_NAME), any(), eq(TEST_CLUSTER_NAME), eq(false))).thenReturn( + serviceInfo); + + Result result = instanceControllerV3.list(TEST_NAMESPACE, "DEFAULT_GROUP", "test-service", TEST_CLUSTER_NAME, TEST_IP, + 9999, false, "", "", ""); + + verify(instanceServiceV2).listInstance(eq(TEST_NAMESPACE), eq(TEST_SERVICE_NAME), any(), eq(TEST_CLUSTER_NAME), eq(false)); + + assertEquals(ErrorCode.SUCCESS.getCode(), result.getCode()); + assertEquals(serviceInfo.getName(), result.getData().getName()); + } + + @Test + void detail() throws Exception { + + Instance instance = new Instance(); + instance.setInstanceId("test-id"); + + when(instanceServiceV2.getInstance(TEST_NAMESPACE, TEST_SERVICE_NAME, TEST_CLUSTER_NAME, TEST_IP, 9999)).thenReturn(instance); + + Result result = instanceControllerV3.detail(TEST_NAMESPACE, "DEFAULT_GROUP", "test-service", + TEST_CLUSTER_NAME, TEST_IP, 9999); + + verify(instanceServiceV2).getInstance(TEST_NAMESPACE, TEST_SERVICE_NAME, TEST_CLUSTER_NAME, TEST_IP, 9999); + + assertEquals(ErrorCode.SUCCESS.getCode(), result.getCode()); + assertEquals(instance.getInstanceId(), result.getData().getInstanceId()); + } +} diff --git a/naming/src/test/java/com/alibaba/nacos/naming/controllers/v3/OperatorControllerV3Test.java b/naming/src/test/java/com/alibaba/nacos/naming/controllers/v3/OperatorControllerV3Test.java new file mode 100644 index 00000000000..698a69420db --- /dev/null +++ b/naming/src/test/java/com/alibaba/nacos/naming/controllers/v3/OperatorControllerV3Test.java @@ -0,0 +1,108 @@ +/* + * Copyright 1999-2022 Alibaba Group Holding Ltd. + * + * Licensed 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.alibaba.nacos.naming.controllers.v3; + +import com.alibaba.nacos.api.model.v2.ErrorCode; +import com.alibaba.nacos.api.model.v2.Result; +import com.alibaba.nacos.naming.cluster.ServerStatus; +import com.alibaba.nacos.naming.core.Operator; +import com.alibaba.nacos.naming.misc.SwitchDomain; +import com.alibaba.nacos.naming.model.form.UpdateSwitchForm; +import com.alibaba.nacos.naming.model.vo.MetricsInfoVo; +import com.alibaba.nacos.sys.env.Constants; +import com.alibaba.nacos.sys.env.EnvUtil; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.mock.env.MockEnvironment; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.fail; +import static org.mockito.Mockito.doNothing; + +/** + * OperatorControllerV3Test. + * + * @author Nacos + */ + +@ExtendWith(MockitoExtension.class) +class OperatorControllerV3Test { + + private OperatorControllerV3 operatorControllerV3; + + @Mock + private Operator operatorV2Impl; + + @BeforeEach + void setUp() { + this.operatorControllerV3 = new OperatorControllerV3(operatorV2Impl); + MockEnvironment environment = new MockEnvironment(); + environment.setProperty(Constants.SUPPORT_UPGRADE_FROM_1X, "true"); + EnvUtil.setEnvironment(environment); + } + + @Test + void testSwitches() { + SwitchDomain switchDomain = new SwitchDomain(); + switchDomain.setDefaultInstanceEphemeral(true); + switchDomain.setDefaultPushCacheMillis(1000L); + Mockito.when(operatorV2Impl.switches()).thenReturn(switchDomain); + Result result = operatorControllerV3.switches(); + assertEquals(ErrorCode.SUCCESS.getCode(), result.getCode()); + assertEquals(1000L, result.getData().getDefaultPushCacheMillis()); + assertEquals(true, result.getData().isDefaultInstanceEphemeral()); + } + + @Test + void testUpdateSwitches() { + UpdateSwitchForm updateSwitchForm = new UpdateSwitchForm(); + updateSwitchForm.setDebug(true); + updateSwitchForm.setEntry("test"); + updateSwitchForm.setValue("test"); + + try { + Result result = operatorControllerV3.updateSwitch(updateSwitchForm); + assertEquals(ErrorCode.SUCCESS.getCode(), result.getCode()); + assertEquals("ok", result.getData()); + } catch (Exception e) { + e.printStackTrace(); + fail(e.getMessage()); + } + } + + @Test + void testMetrics() { + MetricsInfoVo metricsInfoVo = new MetricsInfoVo(); + metricsInfoVo.setStatus(ServerStatus.UP.toString()); + Mockito.when(operatorV2Impl.metrics(false)).thenReturn(metricsInfoVo); + Result result = operatorControllerV3.metrics(false); + assertEquals(ErrorCode.SUCCESS.getCode(), result.getCode()); + assertEquals(ServerStatus.UP.toString(), result.getData().getStatus()); + } + + @Test + void testLog() { + doNothing().when(operatorV2Impl).setLogLevel("test", "test"); + Result result = operatorControllerV3.setLogLevel("test", "test"); + assertEquals(ErrorCode.SUCCESS.getCode(), result.getCode()); + assertEquals("ok", result.getData()); + } +} diff --git a/naming/src/test/java/com/alibaba/nacos/naming/controllers/v3/ServiceControllerV3Test.java b/naming/src/test/java/com/alibaba/nacos/naming/controllers/v3/ServiceControllerV3Test.java new file mode 100644 index 00000000000..e9ecd70bebf --- /dev/null +++ b/naming/src/test/java/com/alibaba/nacos/naming/controllers/v3/ServiceControllerV3Test.java @@ -0,0 +1,237 @@ +/* + * Copyright 1999-2022 Alibaba Group Holding Ltd. + * + * Licensed 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.alibaba.nacos.naming.controllers.v3; + +import com.alibaba.nacos.api.common.Constants; +import com.alibaba.nacos.api.exception.NacosException; +import com.alibaba.nacos.api.model.v2.ErrorCode; +import com.alibaba.nacos.api.model.v2.Result; +import com.alibaba.nacos.common.notify.Event; +import com.alibaba.nacos.common.notify.NotifyCenter; +import com.alibaba.nacos.common.notify.listener.SmartSubscriber; +import com.alibaba.nacos.common.trace.event.naming.UpdateServiceTraceEvent; +import com.alibaba.nacos.common.utils.JacksonUtils; +import com.alibaba.nacos.core.model.form.AggregationForm; +import com.alibaba.nacos.core.model.form.PageForm; +import com.alibaba.nacos.naming.constants.FieldsConstants; +import com.alibaba.nacos.naming.core.CatalogServiceV2Impl; +import com.alibaba.nacos.naming.core.ServiceOperatorV2Impl; +import com.alibaba.nacos.naming.core.v2.metadata.ServiceMetadata; +import com.alibaba.nacos.naming.core.v2.pojo.Service; +import com.alibaba.nacos.naming.model.form.ServiceForm; +import com.alibaba.nacos.naming.model.form.ServiceListForm; +import com.alibaba.nacos.naming.pojo.ServiceDetailInfo; +import com.alibaba.nacos.naming.selector.SelectorManager; +import com.fasterxml.jackson.databind.node.ObjectNode; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.util.Arrays; +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; +import java.util.concurrent.TimeUnit; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.fail; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +/** + * ServiceControllerV3Test. + * + * @author Nacos + */ + +@ExtendWith(MockitoExtension.class) +class ServiceControllerV3Test { + + @Mock + private SelectorManager selectorManager; + + @Mock + private ServiceOperatorV2Impl serviceOperatorV2; + + @Mock + private CatalogServiceV2Impl catalogServiceV2; + + private ServiceControllerV3 serviceControllerV3; + + private SmartSubscriber subscriber; + + private volatile Class eventReceivedClass; + + @BeforeEach + void setUp() throws Exception { + serviceControllerV3 = new ServiceControllerV3(serviceOperatorV2, selectorManager, catalogServiceV2); + subscriber = new SmartSubscriber() { + @Override + public List> subscribeTypes() { + List> result = new LinkedList<>(); + result.add(UpdateServiceTraceEvent.class); + return result; + } + + @Override + public void onEvent(Event event) { + eventReceivedClass = event.getClass(); + } + }; + NotifyCenter.registerSubscriber(subscriber); + } + + @AfterEach + void tearDown() throws Exception { + NotifyCenter.deregisterSubscriber(subscriber); + NotifyCenter.deregisterPublisher(UpdateServiceTraceEvent.class); + eventReceivedClass = null; + } + + @Test + void testCreate() throws Exception { + + ServiceForm serviceForm = new ServiceForm(); + serviceForm.setNamespaceId(Constants.DEFAULT_NAMESPACE_ID); + serviceForm.setServiceName("service"); + serviceForm.setGroupName(Constants.DEFAULT_GROUP); + serviceForm.setEphemeral(true); + serviceForm.setProtectThreshold(0.0F); + serviceForm.setMetadata(""); + serviceForm.setSelector(""); + + Result actual = serviceControllerV3.create(serviceForm); + verify(serviceOperatorV2).create(eq(Service.newService(Constants.DEFAULT_NAMESPACE_ID, Constants.DEFAULT_GROUP, "service")), + any(ServiceMetadata.class)); + assertEquals(ErrorCode.SUCCESS.getCode(), actual.getCode()); + assertEquals("ok", actual.getData()); + } + + @Test + void testRemove() throws Exception { + Result actual = serviceControllerV3.remove(Constants.DEFAULT_NAMESPACE_ID, "service", Constants.DEFAULT_GROUP); + verify(serviceOperatorV2).delete(Service.newService(Constants.DEFAULT_NAMESPACE_ID, Constants.DEFAULT_GROUP, "service")); + assertEquals("ok", actual.getData()); + assertEquals(ErrorCode.SUCCESS.getCode(), actual.getCode()); + } + + @Test + void testDetail() throws Exception { + ServiceDetailInfo expected = new ServiceDetailInfo(); + when(serviceOperatorV2.queryService( + Service.newService(Constants.DEFAULT_NAMESPACE_ID, Constants.DEFAULT_GROUP, "service"))).thenReturn(expected); + Result actual = serviceControllerV3.detail(Constants.DEFAULT_NAMESPACE_ID, "service", Constants.DEFAULT_GROUP); + assertEquals(ErrorCode.SUCCESS.getCode(), actual.getCode()); + assertEquals(expected, actual.getData()); + } + + @Test + void testList() throws Exception { + ObjectNode result = JacksonUtils.createEmptyJsonNode(); + result.put(FieldsConstants.COUNT, 1); + when(catalogServiceV2.pageListService(Constants.DEFAULT_NAMESPACE_ID, Constants.DEFAULT_GROUP, "serviceName", 1, + 10, "", false)).thenReturn(result); + ServiceListForm serviceListForm = new ServiceListForm(); + serviceListForm.setNamespaceId(Constants.DEFAULT_NAMESPACE_ID); + serviceListForm.setGroupNameParam(Constants.DEFAULT_GROUP); + serviceListForm.setServiceNameParam("serviceName"); + PageForm pageForm = new PageForm(); + pageForm.setPageNo(1); + pageForm.setPageSize(10); + + Result actual = serviceControllerV3.list(serviceListForm, pageForm); + assertEquals(ErrorCode.SUCCESS.getCode(), actual.getCode()); + } + + @Test + void testUpdate() throws Exception { + ServiceForm serviceForm = new ServiceForm(); + serviceForm.setNamespaceId(Constants.DEFAULT_NAMESPACE_ID); + serviceForm.setGroupName(Constants.DEFAULT_GROUP); + serviceForm.setServiceName("service"); + serviceForm.setProtectThreshold(0.0f); + serviceForm.setMetadata(""); + serviceForm.setSelector(""); + Result actual = serviceControllerV3.update(serviceForm); + verify(serviceOperatorV2).update(eq(Service.newService(Constants.DEFAULT_NAMESPACE_ID, Constants.DEFAULT_GROUP, "service")), + any(ServiceMetadata.class)); + assertEquals(ErrorCode.SUCCESS.getCode(), actual.getCode()); + assertEquals("ok", actual.getData()); + TimeUnit.SECONDS.sleep(1); + assertEquals(UpdateServiceTraceEvent.class, eventReceivedClass); + } + + @Test + void testSearchService() { + try { + Mockito.when(serviceOperatorV2.searchServiceName(Mockito.anyString(), Mockito.anyString())) + .thenReturn(Collections.singletonList("result")); + + ObjectNode objectNode = serviceControllerV3.searchService("test-namespace", "").getData(); + assertEquals(1, objectNode.get("count").asInt()); + } catch (NacosException e) { + e.printStackTrace(); + fail(e.getMessage()); + } + + try { + Mockito.when(serviceOperatorV2.searchServiceName(Mockito.anyString(), Mockito.anyString())) + .thenReturn(Arrays.asList("re1", "re2")); + Mockito.when(serviceOperatorV2.listAllNamespace()).thenReturn(Arrays.asList("re1", "re2")); + + ObjectNode objectNode = serviceControllerV3.searchService(null, "").getData(); + assertEquals(4, objectNode.get("count").asInt()); + } catch (NacosException e) { + e.printStackTrace(); + fail(e.getMessage()); + } + } + + @Test + void testSubscribers() throws Exception { + ObjectNode result = JacksonUtils.createEmptyJsonNode(); + result.put(FieldsConstants.COUNT, 1); + + Mockito.when(serviceOperatorV2.getSubscribers(1, 10, "nameSpaceId", "serviceName", "groupName", true)) + .thenReturn(result); + + ServiceForm serviceForm = new ServiceForm(); + serviceForm.setNamespaceId("nameSpaceId"); + serviceForm.setServiceName("serviceName"); + serviceForm.setGroupName("groupName"); + PageForm pageForm = new PageForm(); + pageForm.setPageNo(1); + pageForm.setPageSize(10); + AggregationForm aggregationForm = new AggregationForm(); + aggregationForm.setAggregation(true); + ObjectNode objectNode = serviceControllerV3.subscribers(serviceForm, pageForm, aggregationForm).getData(); + assertEquals(1, objectNode.get("count").asInt()); + } + + @Test + void testListSelectorTypes() { + Mockito.when(selectorManager.getAllSelectorTypes()).thenReturn(Arrays.asList("re1", "re2")); + Result> result = serviceControllerV3.listSelectorTypes(); + assertEquals(Arrays.asList("re1", "re2"), result.getData()); + } +}