blob: 0a7b28440bcb89f0b25b9337d8d30d309b75ca98 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.dubbo.admin.controller;
import org.apache.dubbo.admin.AbstractSpringIntegrationTest;
import org.apache.dubbo.admin.common.util.YamlParser;
import org.apache.dubbo.admin.model.dto.ConditionRouteDTO;
import org.apache.dubbo.admin.model.store.RoutingRule;
import org.apache.dubbo.admin.service.ProviderService;
import org.junit.After;
import org.junit.Test;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;
import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.hasSize;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.when;
public class ConditionRoutesControllerTest extends AbstractSpringIntegrationTest {
private final String env = "whatever";
@MockBean
private ProviderService providerService;
@After
public void tearDown() throws Exception {
if (zkClient.checkExists().forPath("/dubbo/config/dubbo") != null) {
zkClient.delete().deletingChildrenIfNeeded().forPath("/dubbo/config/dubbo");
}
}
@Test
public void shouldThrowWhenParamInvalid() {
String uuid = UUID.randomUUID().toString();
ConditionRouteDTO dto = new ConditionRouteDTO();
ResponseEntity<String> responseEntity = restTemplate.postForEntity(
url("/api/{env}/rules/route/condition"), dto, String.class, env
);
assertThat(responseEntity.getStatusCode(), is(HttpStatus.BAD_REQUEST));
assertThat(responseEntity.getBody(), containsString("serviceName and app is Empty!"));
dto.setApplication("application" + uuid);
when(providerService.findVersionInApplication(dto.getApplication())).thenReturn("2.6");
responseEntity = restTemplate.postForEntity(
url("/api/{env}/rules/route/condition"), dto, String.class, env
);
assertThat(responseEntity.getStatusCode(), is(HttpStatus.INTERNAL_SERVER_ERROR));
assertThat(responseEntity.getBody(), containsString("dubbo 2.6 does not support application scope routing rule"));
}
@Test
public void shouldCreateRule() {
String uuid = UUID.randomUUID().toString();
String application = "application" + uuid;
String service = "service" + uuid;
String serviceVersion = "version" + uuid;
String serviceGroup = "group" + uuid;
List<String> conditions = Collections.singletonList("=> host != 172.22.3.91");
ConditionRouteDTO dto = new ConditionRouteDTO();
dto.setService(service);
dto.setConditions(conditions);
ResponseEntity<String> responseEntity = restTemplate.postForEntity(
url("/api/{env}/rules/route/condition" + "?serviceVersion=" + serviceVersion + "&serviceGroup=" + serviceGroup), dto, String.class, env
);
assertThat(responseEntity.getStatusCode(), is(HttpStatus.CREATED));
dto.setApplication(application);
when(providerService.findVersionInApplication(dto.getApplication())).thenReturn("2.7");
responseEntity = restTemplate.postForEntity(
url("/api/{env}/rules/route/condition"), dto, String.class, env
);
assertThat(responseEntity.getStatusCode(), is(HttpStatus.CREATED));
}
@Test
public void shouldUpdateRule() throws Exception {
String service = "org.apache.dubbo.demo.DemoService";
String content = "conditions:\n"
+ "- => host != 172.22.3.111\n"
+ "- => host != 172.22.3.112\n"
+ "enabled: true\n"
+ "force: true\n"
+ "key: " + service + "\n"
+ "priority: 0\n"
+ "runtime: false\n"
+ "scope: service";
String path = "/dubbo/config/dubbo/" + service + "::.condition-router";
zkClient.create().creatingParentContainersIfNeeded().forPath(path);
zkClient.setData().forPath(path, content.getBytes());
List<String> newConditions = Arrays.asList("=> host != 172.22.3.211", "=> host != 172.22.3.212");
ConditionRouteDTO dto = new ConditionRouteDTO();
dto.setConditions(newConditions);
dto.setService(service);
ResponseEntity<String> responseEntity = restTemplate.exchange(
url("/api/{env}/rules/route/condition/{service}"), HttpMethod.PUT,
new HttpEntity<>(dto, null), String.class, env, service
);
assertThat(responseEntity.getStatusCode(), is(HttpStatus.OK));
byte[] bytes = zkClient.getData().forPath(path);
String updatedConfig = new String(bytes);
RoutingRule rule = YamlParser.loadObject(updatedConfig, RoutingRule.class);
assertThat(rule.getConditions(), containsInAnyOrder(newConditions.toArray()));
}
@Test
public void shouldGetServiceRule() throws Exception {
String service = "org.apache.dubbo.demo.DemoService";
String content = "conditions:\n"
+ "- => host != 172.22.3.111\n"
+ "- => host != 172.22.3.112\n"
+ "enabled: true\n"
+ "force: true\n"
+ "key: " + service + "\n"
+ "priority: 0\n"
+ "runtime: false\n"
+ "scope: service";
String path = "/dubbo/config/dubbo/" + service + "::.condition-router";
zkClient.create().creatingParentContainersIfNeeded().forPath(path);
zkClient.setData().forPath(path, content.getBytes());
ResponseEntity<List<ConditionRouteDTO>> responseEntity = restTemplate.exchange(
url("/api/{env}/rules/route/condition/?service={service}"), HttpMethod.GET,
null, new ParameterizedTypeReference<List<ConditionRouteDTO>>() {
}, env, service
);
assertThat(responseEntity.getStatusCode(), is(HttpStatus.OK));
assertThat(responseEntity.getBody(), hasSize(1));
List<String> conditions = responseEntity.getBody()
.stream()
.flatMap(it -> it.getConditions().stream())
.collect(Collectors.toList());
assertThat(conditions, hasSize(2));
assertThat(conditions, containsInAnyOrder("=> host != 172.22.3.111", "=> host != 172.22.3.112"));
}
@Test
public void serviceShouldDeleteRule() throws Exception {
String service = "org.apache.dubbo.demo.DemoService";
String serviceContent = "conditions:\n"
+ "- => host != 172.22.3.111\n"
+ "- => host != 172.22.3.112\n"
+ "enabled: true\n"
+ "force: true\n"
+ "key: " + service + "\n"
+ "priority: 0\n"
+ "runtime: false\n"
+ "scope: service";
String path = "/dubbo/config/dubbo/" + service + "::.condition-router";
zkClient.create().creatingParentContainersIfNeeded().forPath(path);
zkClient.setData().forPath(path, serviceContent.getBytes());
assertNotNull("zk path should not be null before deleting", zkClient.checkExists().forPath(path));
ResponseEntity<String> responseEntity = restTemplate.exchange(
url("/api/{env}/rules/route/condition/{service}" + "?scope=service"), HttpMethod.DELETE,
null, String.class, env, service
);
assertThat(responseEntity.getStatusCode(), is(HttpStatus.OK));
assertNull(zkClient.checkExists().forPath(path));
}
@Test
public void applicationShouldDeleteRule() throws Exception {
String application = "test-application";
String serviceContent = "conditions:\n"
+ "- => host != 172.22.3.111\n"
+ "- => host != 172.22.3.112\n"
+ "enabled: true\n"
+ "force: true\n"
+ "key: " + application + "\n"
+ "priority: 0\n"
+ "runtime: false\n"
+ "scope: application";
String path = "/dubbo/config/dubbo/" + application + ".condition-router";
zkClient.create().creatingParentContainersIfNeeded().forPath(path);
zkClient.setData().forPath(path, serviceContent.getBytes());
assertNotNull("zk path should not be null before deleting", zkClient.checkExists().forPath(path));
ResponseEntity<String> responseEntity = restTemplate.exchange(
url("/api/{env}/rules/route/condition/{service}" + "?scope=application"), HttpMethod.DELETE,
null, String.class, env, application
);
assertThat(responseEntity.getStatusCode(), is(HttpStatus.OK));
assertNull(zkClient.checkExists().forPath(path));
}
@Test
public void shouldThrowWhenDetailRouteWithUnknownId() {
ResponseEntity<String> responseEntity = restTemplate.getForEntity(
url("/api/{env}/rules/route/condition/{id}" + "?scope=service"), String.class, env, "non-existed-service"
);
assertThat(responseEntity.getStatusCode(), is(HttpStatus.NOT_FOUND));
}
@Test
public void serviceShouldGetRouteDetail() throws Exception {
String service = "org.apache.dubbo.demo.DemoService";
String content = "conditions:\n"
+ "- => host != 172.22.3.111\n"
+ "- => host != 172.22.3.112\n"
+ "enabled: true\n"
+ "force: true\n"
+ "key: " + service + "\n"
+ "priority: 0\n"
+ "runtime: false\n"
+ "scope: service";
String path = "/dubbo/config/dubbo/" + service + "::.condition-router";
zkClient.create().creatingParentContainersIfNeeded().forPath(path);
zkClient.setData().forPath(path, content.getBytes());
ResponseEntity<ConditionRouteDTO> responseEntity = restTemplate.getForEntity(
url("/api/{env}/rules/route/condition/{id}" + "?scope=service"), ConditionRouteDTO.class, env, service
);
assertThat(responseEntity.getStatusCode(), is(HttpStatus.OK));
ConditionRouteDTO conditionRouteDTO = responseEntity.getBody();
assertNotNull(conditionRouteDTO);
assertThat(conditionRouteDTO.getConditions(), hasSize(2));
assertThat(conditionRouteDTO.getConditions(), containsInAnyOrder("=> host != 172.22.3.111", "=> host != 172.22.3.112"));
}
@Test
public void applicationShouldGetRouteDetail() throws Exception {
String application = "test-application";
String content = "conditions:\n"
+ "- => host != 172.22.3.111\n"
+ "- => host != 172.22.3.112\n"
+ "enabled: true\n"
+ "force: true\n"
+ "key: " + application + "\n"
+ "priority: 0\n"
+ "runtime: false\n"
+ "scope: application";
String path = "/dubbo/config/dubbo/" + application + ".condition-router";
zkClient.create().creatingParentContainersIfNeeded().forPath(path);
zkClient.setData().forPath(path, content.getBytes());
ResponseEntity<ConditionRouteDTO> responseEntity = restTemplate.getForEntity(
url("/api/{env}/rules/route/condition/{id}" + "?scope=application"), ConditionRouteDTO.class, env, application
);
assertThat(responseEntity.getStatusCode(), is(HttpStatus.OK));
ConditionRouteDTO conditionRouteDTO = responseEntity.getBody();
assertNotNull(conditionRouteDTO);
assertThat(conditionRouteDTO.getConditions(), hasSize(2));
assertThat(conditionRouteDTO.getConditions(), containsInAnyOrder("=> host != 172.22.3.111", "=> host != 172.22.3.112"));
}
@Test
public void serviceShouldEnableRoute() throws Exception {
String service = "org.apache.dubbo.demo.DemoService";
String content = "conditions:\n"
+ "- => host != 172.22.3.111\n"
+ "- => host != 172.22.3.112\n"
+ "enabled: false\n"
+ "force: true\n"
+ "key: " + service + "\n"
+ "priority: 0\n"
+ "runtime: false\n"
+ "scope: service";
String path = "/dubbo/config/dubbo/" + service + "::.condition-router";
zkClient.create().creatingParentContainersIfNeeded().forPath(path);
zkClient.setData().forPath(path, content.getBytes());
byte[] bytes = zkClient.getData().forPath(path);
String updatedConfig = new String(bytes);
RoutingRule rule = YamlParser.loadObject(updatedConfig, RoutingRule.class);
assertFalse(rule.isEnabled());
restTemplate.put(url("/api/{env}/rules/route/condition/enable/{id}" + "?scope=service"), null, env, service);
bytes = zkClient.getData().forPath(path);
updatedConfig = new String(bytes);
rule = YamlParser.loadObject(updatedConfig, RoutingRule.class);
assertTrue(rule.isEnabled());
}
@Test
public void applicationShouldEnableRoute() throws Exception {
String application = "test-application";
String content = "conditions:\n"
+ "- => host != 172.22.3.111\n"
+ "- => host != 172.22.3.112\n"
+ "enabled: false\n"
+ "force: true\n"
+ "key: " + application + "\n"
+ "priority: 0\n"
+ "runtime: false\n"
+ "scope: service";
String path = "/dubbo/config/dubbo/" + application + ".condition-router";
zkClient.create().creatingParentContainersIfNeeded().forPath(path);
zkClient.setData().forPath(path, content.getBytes());
byte[] bytes = zkClient.getData().forPath(path);
String updatedConfig = new String(bytes);
RoutingRule rule = YamlParser.loadObject(updatedConfig, RoutingRule.class);
assertFalse(rule.isEnabled());
restTemplate.put(url("/api/{env}/rules/route/condition/enable/{id}" + "?scope=application"), null, env, application);
bytes = zkClient.getData().forPath(path);
updatedConfig = new String(bytes);
rule = YamlParser.loadObject(updatedConfig, RoutingRule.class);
assertTrue(rule.isEnabled());
}
@Test
public void serviceShouldDisableRoute() throws Exception {
String service = "org.apache.dubbo.demo.DemoService";
String content = "conditions:\n"
+ "- => host != 172.22.3.111\n"
+ "- => host != 172.22.3.112\n"
+ "enabled: true\n"
+ "force: false\n"
+ "key: " + service + "\n"
+ "priority: 0\n"
+ "runtime: false\n"
+ "scope: service";
String path = "/dubbo/config/dubbo/" + service + "::.condition-router";
zkClient.create().creatingParentContainersIfNeeded().forPath(path);
zkClient.setData().forPath(path, content.getBytes());
byte[] bytes = zkClient.getData().forPath(path);
String updatedConfig = new String(bytes);
RoutingRule rule = YamlParser.loadObject(updatedConfig, RoutingRule.class);
assertTrue(rule.isEnabled());
restTemplate.put(url("/api/{env}/rules/route/condition/disable/{id}" + "?scope=service"), null, env, service);
bytes = zkClient.getData().forPath(path);
updatedConfig = new String(bytes);
rule = YamlParser.loadObject(updatedConfig, RoutingRule.class);
assertFalse(rule.isEnabled());
}
@Test
public void applicationShouldDisableRoute() throws Exception {
String application = "test-application";
String content = "conditions:\n"
+ "- => host != 172.22.3.111\n"
+ "- => host != 172.22.3.112\n"
+ "enabled: true\n"
+ "force: false\n"
+ "key: " + application + "\n"
+ "priority: 0\n"
+ "runtime: false\n"
+ "scope: application";
String path = "/dubbo/config/dubbo/" + application + ".condition-router";
zkClient.create().creatingParentContainersIfNeeded().forPath(path);
zkClient.setData().forPath(path, content.getBytes());
byte[] bytes = zkClient.getData().forPath(path);
String updatedConfig = new String(bytes);
RoutingRule rule = YamlParser.loadObject(updatedConfig, RoutingRule.class);
assertTrue(rule.isEnabled());
restTemplate.put(url("/api/{env}/rules/route/condition/disable/{id}" + "?scope=application"), null, env, application);
bytes = zkClient.getData().forPath(path);
updatedConfig = new String(bytes);
rule = YamlParser.loadObject(updatedConfig, RoutingRule.class);
assertFalse(rule.isEnabled());
}
}