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 { ...@@ -17,6 +17,13 @@ public class InputValidator {
private static final Pattern APP_NAMESPACE_PATTERN = private static final Pattern APP_NAMESPACE_PATTERN =
Pattern.compile(APP_NAMESPACE_VALIDATOR); 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){ public static boolean isValidAppNamespace(String name){
if (StringUtils.isEmpty(name)){ if (StringUtils.isEmpty(name)){
return false; return false;
......
...@@ -2,12 +2,14 @@ package com.ctrip.framework.apollo.openapi.client; ...@@ -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.constant.ApolloOpenApiConstants;
import com.ctrip.framework.apollo.openapi.client.service.AppOpenApiService; 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.ItemOpenApiService;
import com.ctrip.framework.apollo.openapi.client.service.NamespaceOpenApiService; import com.ctrip.framework.apollo.openapi.client.service.NamespaceOpenApiService;
import com.ctrip.framework.apollo.openapi.client.service.ReleaseOpenApiService; import com.ctrip.framework.apollo.openapi.client.service.ReleaseOpenApiService;
import com.ctrip.framework.apollo.openapi.dto.NamespaceReleaseDTO; import com.ctrip.framework.apollo.openapi.dto.NamespaceReleaseDTO;
import com.ctrip.framework.apollo.openapi.dto.OpenAppDTO; import com.ctrip.framework.apollo.openapi.dto.OpenAppDTO;
import com.ctrip.framework.apollo.openapi.dto.OpenAppNamespaceDTO; 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.OpenEnvClusterDTO;
import com.ctrip.framework.apollo.openapi.dto.OpenItemDTO; import com.ctrip.framework.apollo.openapi.dto.OpenItemDTO;
import com.ctrip.framework.apollo.openapi.dto.OpenNamespaceDTO; import com.ctrip.framework.apollo.openapi.dto.OpenNamespaceDTO;
...@@ -38,6 +40,7 @@ public class ApolloOpenApiClient { ...@@ -38,6 +40,7 @@ public class ApolloOpenApiClient {
private final ItemOpenApiService itemService; private final ItemOpenApiService itemService;
private final ReleaseOpenApiService releaseService; private final ReleaseOpenApiService releaseService;
private final NamespaceOpenApiService namespaceService; private final NamespaceOpenApiService namespaceService;
private final ClusterOpenApiService clusterService;
private ApolloOpenApiClient(String portalUrl, String token, RequestConfig requestConfig) { private ApolloOpenApiClient(String portalUrl, String token, RequestConfig requestConfig) {
this.portalUrl = portalUrl; this.portalUrl = portalUrl;
...@@ -48,6 +51,7 @@ public class ApolloOpenApiClient { ...@@ -48,6 +51,7 @@ public class ApolloOpenApiClient {
String baseUrl = this.portalUrl + ApolloOpenApiConstants.OPEN_API_V1_PREFIX; String baseUrl = this.portalUrl + ApolloOpenApiConstants.OPEN_API_V1_PREFIX;
appService = new AppOpenApiService(client, baseUrl, gson); appService = new AppOpenApiService(client, baseUrl, gson);
clusterService = new ClusterOpenApiService(client, baseUrl, gson);
namespaceService = new NamespaceOpenApiService(client, baseUrl, gson); namespaceService = new NamespaceOpenApiService(client, baseUrl, gson);
itemService = new ItemOpenApiService(client, baseUrl, gson); itemService = new ItemOpenApiService(client, baseUrl, gson);
releaseService = new ReleaseOpenApiService(client, baseUrl, gson); releaseService = new ReleaseOpenApiService(client, baseUrl, gson);
...@@ -81,6 +85,24 @@ public class ApolloOpenApiClient { ...@@ -81,6 +85,24 @@ public class ApolloOpenApiClient {
return namespaceService.getNamespaces(appId, env, clusterName); 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 * Get the namespace
*/ */
...@@ -162,6 +184,15 @@ public class ApolloOpenApiClient { ...@@ -162,6 +184,15 @@ public class ApolloOpenApiClient {
return releaseService.getLatestActiveRelease(appId, env, clusterName, namespaceName); 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() { public String getPortalUrl() {
return portalUrl; 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 { ...@@ -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 { ...@@ -61,4 +61,20 @@ public class OpenAppNamespaceDTO extends BaseDTO {
public void setComment(String comment) { public void setComment(String comment) {
this.comment = 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 { ...@@ -21,4 +21,16 @@ public class OpenClusterDTO extends BaseDTO {
public void setAppId(String appId) { public void setAppId(String appId) {
this.appId = 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 { ...@@ -22,4 +22,12 @@ public class OpenEnvClusterDTO {
public void setClusters(Set<String> clusters) { public void setClusters(Set<String> clusters) {
this.clusters = clusters; this.clusters = clusters;
} }
@Override
public String toString() {
return "OpenEnvClusterDTO{" +
"env='" + env + '\'' +
", clusters=" + clusters +
'}';
}
} }
...@@ -31,4 +31,17 @@ public class OpenItemDTO extends BaseDTO { ...@@ -31,4 +31,17 @@ public class OpenItemDTO extends BaseDTO {
public void setComment(String comment) { public void setComment(String comment) {
this.comment = 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 { ...@@ -73,4 +73,21 @@ public class OpenNamespaceDTO extends BaseDTO {
public void setItems(List<OpenItemDTO> items) { public void setItems(List<OpenItemDTO> items) {
this.items = 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 { ...@@ -29,4 +29,13 @@ public class OpenNamespaceLockDTO {
public void setLockedBy(String lockedBy) { public void setLockedBy(String lockedBy) {
this.lockedBy = lockedBy; this.lockedBy = lockedBy;
} }
@Override
public String toString() {
return "OpenNamespaceLockDTO{" +
"namespaceName='" + namespaceName + '\'' +
", isLocked=" + isLocked +
", lockedBy='" + lockedBy + '\'' +
'}';
}
} }
...@@ -4,6 +4,8 @@ import java.util.Map; ...@@ -4,6 +4,8 @@ import java.util.Map;
public class OpenReleaseDTO extends BaseDTO { public class OpenReleaseDTO extends BaseDTO {
private long id;
private String appId; private String appId;
private String clusterName; private String clusterName;
...@@ -16,6 +18,14 @@ public class OpenReleaseDTO extends BaseDTO { ...@@ -16,6 +18,14 @@ public class OpenReleaseDTO extends BaseDTO {
private String comment; private String comment;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getName() { public String getName() {
return name; return name;
} }
...@@ -63,4 +73,21 @@ public class OpenReleaseDTO extends BaseDTO { ...@@ -63,4 +73,21 @@ public class OpenReleaseDTO extends BaseDTO {
public void setComment(String comment) { public void setComment(String comment) {
this.comment = 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; ...@@ -8,6 +8,7 @@ import static org.mockito.Mockito.when;
import com.ctrip.framework.apollo.openapi.dto.NamespaceReleaseDTO; import com.ctrip.framework.apollo.openapi.dto.NamespaceReleaseDTO;
import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost; import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.entity.StringEntity; import org.apache.http.entity.StringEntity;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
...@@ -95,4 +96,29 @@ public class ReleaseOpenApiServiceTest extends AbstractOpenApiServiceTest { ...@@ -95,4 +96,29 @@ public class ReleaseOpenApiServiceTest extends AbstractOpenApiServiceTest {
releaseOpenApiService.getLatestActiveRelease(someAppId, someEnv, someCluster, someNamespace); 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 { ...@@ -57,10 +57,9 @@ public class ClusterController {
RequestPrecondition.checkArguments(!StringUtils.isContainEmpty(clusterName, operator), RequestPrecondition.checkArguments(!StringUtils.isContainEmpty(clusterName, operator),
"name and dataChangeCreatedBy should not be null or empty"); "name and dataChangeCreatedBy should not be null or empty");
if (!InputValidator.isValidAppNamespace(clusterName)) { if (!InputValidator.isValidClusterNamespace(clusterName)) {
throw new BadRequestException( throw new BadRequestException(
String.format("Cluster Name 格式错误: %s", InputValidator.INVALID_CLUSTER_NAMESPACE_MESSAGE String.format("Cluster Name 格式错误: %s", InputValidator.INVALID_CLUSTER_NAMESPACE_MESSAGE));
+ " & " + InputValidator.INVALID_NAMESPACE_NAMESPACE_MESSAGE));
} }
if (userService.findByUserId(operator) == null) { if (userService.findByUserId(operator) == null) {
...@@ -68,7 +67,7 @@ public class ClusterController { ...@@ -68,7 +67,7 @@ public class ClusterController {
} }
ClusterDTO toCreate = OpenApiBeanUtils.transformToClusterDTO(cluster); 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); return OpenApiBeanUtils.transformFromClusterDTO(createdClusterDTO);
} }
......
...@@ -161,7 +161,7 @@ public class ReleaseController { ...@@ -161,7 +161,7 @@ public class ReleaseController {
@PutMapping(path = "/releases/{releaseId}/rollback") @PutMapping(path = "/releases/{releaseId}/rollback")
public void rollback(@PathVariable String env, public void rollback(@PathVariable String env,
@PathVariable long releaseId, HttpServletRequest request) { @PathVariable long releaseId, HttpServletRequest request) {
ReleaseDTO release = releaseService.findReleaseById(Env.valueOf(env), releaseId); ReleaseDTO release = releaseService.findReleaseById(Env.fromString(env), releaseId);
if (release == null) { if (release == null) {
throw new BadRequestException("release not found"); throw new BadRequestException("release not found");
...@@ -171,7 +171,7 @@ public class ReleaseController { ...@@ -171,7 +171,7 @@ public class ReleaseController {
throw new AccessDeniedException("Forbidden operation. you don't have release permission"); 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