Commit 40c5e478 authored by Jason Song's avatar Jason Song Committed by GitHub

add cluster related and rollback release open api (#2631)

* add cluster open api client
* add toString for open api dtos
* add rollback release open api
parent 35e13020
......@@ -17,6 +17,13 @@ public class InputValidator {
private static final Pattern APP_NAMESPACE_PATTERN =
Pattern.compile(APP_NAMESPACE_VALIDATOR);
public static boolean isValidClusterNamespace(String name) {
if (StringUtils.isEmpty(name)){
return false;
}
return CLUSTER_NAMESPACE_PATTERN.matcher(name).matches();
}
public static boolean isValidAppNamespace(String name){
if (StringUtils.isEmpty(name)){
return false;
......
......@@ -2,12 +2,14 @@ package com.ctrip.framework.apollo.openapi.client;
import com.ctrip.framework.apollo.openapi.client.constant.ApolloOpenApiConstants;
import com.ctrip.framework.apollo.openapi.client.service.AppOpenApiService;
import com.ctrip.framework.apollo.openapi.client.service.ClusterOpenApiService;
import com.ctrip.framework.apollo.openapi.client.service.ItemOpenApiService;
import com.ctrip.framework.apollo.openapi.client.service.NamespaceOpenApiService;
import com.ctrip.framework.apollo.openapi.client.service.ReleaseOpenApiService;
import com.ctrip.framework.apollo.openapi.dto.NamespaceReleaseDTO;
import com.ctrip.framework.apollo.openapi.dto.OpenAppDTO;
import com.ctrip.framework.apollo.openapi.dto.OpenAppNamespaceDTO;
import com.ctrip.framework.apollo.openapi.dto.OpenClusterDTO;
import com.ctrip.framework.apollo.openapi.dto.OpenEnvClusterDTO;
import com.ctrip.framework.apollo.openapi.dto.OpenItemDTO;
import com.ctrip.framework.apollo.openapi.dto.OpenNamespaceDTO;
......@@ -38,6 +40,7 @@ public class ApolloOpenApiClient {
private final ItemOpenApiService itemService;
private final ReleaseOpenApiService releaseService;
private final NamespaceOpenApiService namespaceService;
private final ClusterOpenApiService clusterService;
private ApolloOpenApiClient(String portalUrl, String token, RequestConfig requestConfig) {
this.portalUrl = portalUrl;
......@@ -48,6 +51,7 @@ public class ApolloOpenApiClient {
String baseUrl = this.portalUrl + ApolloOpenApiConstants.OPEN_API_V1_PREFIX;
appService = new AppOpenApiService(client, baseUrl, gson);
clusterService = new ClusterOpenApiService(client, baseUrl, gson);
namespaceService = new NamespaceOpenApiService(client, baseUrl, gson);
itemService = new ItemOpenApiService(client, baseUrl, gson);
releaseService = new ReleaseOpenApiService(client, baseUrl, gson);
......@@ -81,6 +85,24 @@ public class ApolloOpenApiClient {
return namespaceService.getNamespaces(appId, env, clusterName);
}
/**
* Get the cluster
*
* @since 1.5.0
*/
public OpenClusterDTO getCluster(String appId, String env, String clusterName) {
return clusterService.getCluster(appId, env, clusterName);
}
/**
* Create the cluster
*
* @since 1.5.0
*/
public OpenClusterDTO createCluster(String env, OpenClusterDTO openClusterDTO) {
return clusterService.createCluster(env, openClusterDTO);
}
/**
* Get the namespace
*/
......@@ -162,6 +184,15 @@ public class ApolloOpenApiClient {
return releaseService.getLatestActiveRelease(appId, env, clusterName, namespaceName);
}
/**
* Rollback the release
*
* @since 1.5.0
*/
public void rollbackRelease(String env, long releaseId) {
releaseService.rollbackRelease(env, releaseId);
}
public String getPortalUrl() {
return portalUrl;
......
package com.ctrip.framework.apollo.openapi.client.service;
import com.ctrip.framework.apollo.openapi.dto.OpenClusterDTO;
import com.google.gson.Gson;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.util.EntityUtils;
public class ClusterOpenApiService extends AbstractOpenApiService {
public ClusterOpenApiService(CloseableHttpClient client, String baseUrl, Gson gson) {
super(client, baseUrl, gson);
}
public OpenClusterDTO getCluster(String appId, String env, String clusterName) {
checkNotEmpty(appId, "App id");
checkNotEmpty(env, "Env");
checkNotEmpty(clusterName, "Cluster name");
String path = String.format("envs/%s/apps/%s/clusters/%s", escapePath(env), escapePath(appId),
escapePath(clusterName));
try (CloseableHttpResponse response = get(path)) {
return gson.fromJson(EntityUtils.toString(response.getEntity()), OpenClusterDTO.class);
} catch (Throwable ex) {
throw new RuntimeException(String
.format("Get cluster for appId: %s, cluster: %s in env: %s failed", appId, clusterName, env), ex);
}
}
public OpenClusterDTO createCluster(String env, OpenClusterDTO openClusterDTO) {
checkNotEmpty(openClusterDTO.getAppId(), "App id");
checkNotEmpty(env, "Env");
checkNotEmpty(openClusterDTO.getName(), "Cluster name");
checkNotEmpty(openClusterDTO.getDataChangeCreatedBy(), "Created by");
String path = String.format("envs/%s/apps/%s/clusters", escapePath(env), escapePath(openClusterDTO.getAppId()));
try (CloseableHttpResponse response = post(path, openClusterDTO)) {
return gson.fromJson(EntityUtils.toString(response.getEntity()), OpenClusterDTO.class);
} catch (Throwable ex) {
throw new RuntimeException(String
.format("Create cluster: %s for appId: %s in env: %s failed", openClusterDTO.getName(),
openClusterDTO.getAppId(), env), ex);
}
}
}
......@@ -64,4 +64,14 @@ public class ReleaseOpenApiService extends AbstractOpenApiService {
}
}
public void rollbackRelease(String env, long releaseId) {
checkNotEmpty(env, "Env");
String path = String.format("envs/%s/releases/%s/rollback", escapePath(env), releaseId);
try (CloseableHttpResponse ignored = put(path, null)) {
} catch (Throwable ex) {
throw new RuntimeException(String.format("Rollback release: %s in env: %s failed", releaseId, env), ex);
}
}
}
......@@ -61,4 +61,20 @@ public class OpenAppNamespaceDTO extends BaseDTO {
public void setComment(String comment) {
this.comment = comment;
}
@Override
public String toString() {
return "OpenAppNamespaceDTO{" +
"name='" + name + '\'' +
", appId='" + appId + '\'' +
", format='" + format + '\'' +
", isPublic=" + isPublic +
", appendNamespacePrefix=" + appendNamespacePrefix +
", comment='" + comment + '\'' +
", dataChangeCreatedBy='" + dataChangeCreatedBy + '\'' +
", dataChangeLastModifiedBy='" + dataChangeLastModifiedBy + '\'' +
", dataChangeCreatedTime=" + dataChangeCreatedTime +
", dataChangeLastModifiedTime=" + dataChangeLastModifiedTime +
'}';
}
}
......@@ -21,4 +21,16 @@ public class OpenClusterDTO extends BaseDTO {
public void setAppId(String appId) {
this.appId = appId;
}
@Override
public String toString() {
return "OpenClusterDTO{" +
"name='" + name + '\'' +
", appId='" + appId + '\'' +
", dataChangeCreatedBy='" + dataChangeCreatedBy + '\'' +
", dataChangeLastModifiedBy='" + dataChangeLastModifiedBy + '\'' +
", dataChangeCreatedTime=" + dataChangeCreatedTime +
", dataChangeLastModifiedTime=" + dataChangeLastModifiedTime +
'}';
}
}
......@@ -22,4 +22,12 @@ public class OpenEnvClusterDTO {
public void setClusters(Set<String> clusters) {
this.clusters = clusters;
}
@Override
public String toString() {
return "OpenEnvClusterDTO{" +
"env='" + env + '\'' +
", clusters=" + clusters +
'}';
}
}
......@@ -31,4 +31,17 @@ public class OpenItemDTO extends BaseDTO {
public void setComment(String comment) {
this.comment = comment;
}
@Override
public String toString() {
return "OpenItemDTO{" +
"key='" + key + '\'' +
", value='" + value + '\'' +
", comment='" + comment + '\'' +
", dataChangeCreatedBy='" + dataChangeCreatedBy + '\'' +
", dataChangeLastModifiedBy='" + dataChangeLastModifiedBy + '\'' +
", dataChangeCreatedTime=" + dataChangeCreatedTime +
", dataChangeLastModifiedTime=" + dataChangeLastModifiedTime +
'}';
}
}
......@@ -73,4 +73,21 @@ public class OpenNamespaceDTO extends BaseDTO {
public void setItems(List<OpenItemDTO> items) {
this.items = items;
}
@Override
public String toString() {
return "OpenNamespaceDTO{" +
"appId='" + appId + '\'' +
", clusterName='" + clusterName + '\'' +
", namespaceName='" + namespaceName + '\'' +
", comment='" + comment + '\'' +
", format='" + format + '\'' +
", isPublic=" + isPublic +
", items=" + items +
", dataChangeCreatedBy='" + dataChangeCreatedBy + '\'' +
", dataChangeLastModifiedBy='" + dataChangeLastModifiedBy + '\'' +
", dataChangeCreatedTime=" + dataChangeCreatedTime +
", dataChangeLastModifiedTime=" + dataChangeLastModifiedTime +
'}';
}
}
......@@ -29,4 +29,13 @@ public class OpenNamespaceLockDTO {
public void setLockedBy(String lockedBy) {
this.lockedBy = lockedBy;
}
@Override
public String toString() {
return "OpenNamespaceLockDTO{" +
"namespaceName='" + namespaceName + '\'' +
", isLocked=" + isLocked +
", lockedBy='" + lockedBy + '\'' +
'}';
}
}
......@@ -4,6 +4,8 @@ import java.util.Map;
public class OpenReleaseDTO extends BaseDTO {
private long id;
private String appId;
private String clusterName;
......@@ -16,6 +18,14 @@ public class OpenReleaseDTO extends BaseDTO {
private String comment;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getName() {
return name;
}
......@@ -63,4 +73,21 @@ public class OpenReleaseDTO extends BaseDTO {
public void setComment(String comment) {
this.comment = comment;
}
@Override
public String toString() {
return "OpenReleaseDTO{" +
"id=" + id +
", appId='" + appId + '\'' +
", clusterName='" + clusterName + '\'' +
", namespaceName='" + namespaceName + '\'' +
", name='" + name + '\'' +
", configurations=" + configurations +
", comment='" + comment + '\'' +
", dataChangeCreatedBy='" + dataChangeCreatedBy + '\'' +
", dataChangeLastModifiedBy='" + dataChangeLastModifiedBy + '\'' +
", dataChangeCreatedTime=" + dataChangeCreatedTime +
", dataChangeLastModifiedTime=" + dataChangeLastModifiedTime +
'}';
}
}
package com.ctrip.framework.apollo.openapi.client.service;
import static org.junit.Assert.*;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import com.ctrip.framework.apollo.openapi.dto.OpenClusterDTO;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.util.EntityUtils;
import org.junit.Before;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
public class ClusterOpenApiServiceTest extends AbstractOpenApiServiceTest {
private ClusterOpenApiService clusterOpenApiService;
private String someAppId;
private String someEnv;
@Before
public void setUp() throws Exception {
super.setUp();
someAppId = "someAppId";
someEnv = "someEnv";
StringEntity responseEntity = new StringEntity("{}");
when(someHttpResponse.getEntity()).thenReturn(responseEntity);
clusterOpenApiService = new ClusterOpenApiService(httpClient, someBaseUrl, gson);
}
@Test
public void testGetCluster() throws Exception {
String someCluster = "someCluster";
final ArgumentCaptor<HttpGet> request = ArgumentCaptor.forClass(HttpGet.class);
clusterOpenApiService.getCluster(someAppId, someEnv, someCluster);
verify(httpClient, times(1)).execute(request.capture());
HttpGet get = request.getValue();
assertEquals(String
.format("%s/envs/%s/apps/%s/clusters/%s", someBaseUrl, someEnv, someAppId, someCluster),
get.getURI().toString());
}
@Test(expected = RuntimeException.class)
public void testGetClusterWithError() throws Exception {
String someCluster = "someCluster";
when(statusLine.getStatusCode()).thenReturn(404);
clusterOpenApiService.getCluster(someAppId, someEnv, someCluster);
}
@Test
public void testCreateCluster() throws Exception {
String someCluster = "someCluster";
String someCreatedBy = "someCreatedBy";
OpenClusterDTO clusterDTO = new OpenClusterDTO();
clusterDTO.setAppId(someAppId);
clusterDTO.setName(someCluster);
clusterDTO.setDataChangeCreatedBy(someCreatedBy);
final ArgumentCaptor<HttpPost> request = ArgumentCaptor.forClass(HttpPost.class);
clusterOpenApiService.createCluster(someEnv, clusterDTO);
verify(httpClient, times(1)).execute(request.capture());
HttpPost post = request.getValue();
assertEquals(String
.format("%s/envs/%s/apps/%s/clusters", someBaseUrl, someEnv, someAppId), post.getURI().toString());
StringEntity entity = (StringEntity) post.getEntity();
assertEquals(ContentType.APPLICATION_JSON.toString(), entity.getContentType().getValue());
assertEquals(gson.toJson(clusterDTO), EntityUtils.toString(entity));
}
@Test(expected = RuntimeException.class)
public void testCreateClusterWithError() throws Exception {
String someCluster = "someCluster";
String someCreatedBy = "someCreatedBy";
OpenClusterDTO clusterDTO = new OpenClusterDTO();
clusterDTO.setAppId(someAppId);
clusterDTO.setName(someCluster);
clusterDTO.setDataChangeCreatedBy(someCreatedBy);
when(statusLine.getStatusCode()).thenReturn(400);
clusterOpenApiService.createCluster(someEnv, clusterDTO);
}
}
......@@ -8,6 +8,7 @@ import static org.mockito.Mockito.when;
import com.ctrip.framework.apollo.openapi.dto.NamespaceReleaseDTO;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.entity.StringEntity;
import org.junit.Before;
import org.junit.Test;
......@@ -95,4 +96,29 @@ public class ReleaseOpenApiServiceTest extends AbstractOpenApiServiceTest {
releaseOpenApiService.getLatestActiveRelease(someAppId, someEnv, someCluster, someNamespace);
}
@Test
public void testRollbackRelease() throws Exception {
long someReleaseId = 1L;
final ArgumentCaptor<HttpPut> request = ArgumentCaptor.forClass(HttpPut.class);
releaseOpenApiService.rollbackRelease(someEnv, someReleaseId);
verify(httpClient, times(1)).execute(request.capture());
HttpPut put = request.getValue();
assertEquals(String
.format("%s/envs/%s/releases/%s/rollback", someBaseUrl, someEnv, someReleaseId), put.getURI().toString());
}
@Test(expected = RuntimeException.class)
public void testRollbackReleaseWithError() throws Exception {
long someReleaseId = 1L;
when(statusLine.getStatusCode()).thenReturn(400);
releaseOpenApiService.rollbackRelease(someEnv, someReleaseId);
}
}
......@@ -57,10 +57,9 @@ public class ClusterController {
RequestPrecondition.checkArguments(!StringUtils.isContainEmpty(clusterName, operator),
"name and dataChangeCreatedBy should not be null or empty");
if (!InputValidator.isValidAppNamespace(clusterName)) {
if (!InputValidator.isValidClusterNamespace(clusterName)) {
throw new BadRequestException(
String.format("Cluster Name 格式错误: %s", InputValidator.INVALID_CLUSTER_NAMESPACE_MESSAGE
+ " & " + InputValidator.INVALID_NAMESPACE_NAMESPACE_MESSAGE));
String.format("Cluster Name 格式错误: %s", InputValidator.INVALID_CLUSTER_NAMESPACE_MESSAGE));
}
if (userService.findByUserId(operator) == null) {
......@@ -68,7 +67,7 @@ public class ClusterController {
}
ClusterDTO toCreate = OpenApiBeanUtils.transformToClusterDTO(cluster);
ClusterDTO createdClusterDTO = clusterService.createCluster(Env.valueOf(env), toCreate);
ClusterDTO createdClusterDTO = clusterService.createCluster(Env.fromString(env), toCreate);
return OpenApiBeanUtils.transformFromClusterDTO(createdClusterDTO);
}
......
......@@ -161,7 +161,7 @@ public class ReleaseController {
@PutMapping(path = "/releases/{releaseId}/rollback")
public void rollback(@PathVariable String env,
@PathVariable long releaseId, HttpServletRequest request) {
ReleaseDTO release = releaseService.findReleaseById(Env.valueOf(env), releaseId);
ReleaseDTO release = releaseService.findReleaseById(Env.fromString(env), releaseId);
if (release == null) {
throw new BadRequestException("release not found");
......@@ -171,7 +171,7 @@ public class ReleaseController {
throw new AccessDeniedException("Forbidden operation. you don't have release permission");
}
releaseService.rollback(Env.valueOf(env), releaseId);
releaseService.rollback(Env.fromString(env), releaseId);
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment