Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add admin page to delete app, cluster and app namespace #1279

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Expand Up @@ -52,9 +52,14 @@ public ClusterDTO createCluster(@PathVariable String appId, @PathVariable String
@RequestMapping(value = "apps/{appId}/envs/{env}/clusters/{clusterName:.+}", method = RequestMethod.DELETE)
public ResponseEntity<Void> deleteCluster(@PathVariable String appId, @PathVariable String env,
@PathVariable String clusterName){
clusterService.deleteCluster(Env.valueOf(env), appId, clusterName);
clusterService.deleteCluster(Env.fromString(env), appId, clusterName);
return ResponseEntity.ok().build();
}

@RequestMapping(value = "apps/{appId}/envs/{env}/clusters/{clusterName:.+}", method = RequestMethod.GET)
public ClusterDTO loadCluster(@PathVariable("appId") String appId, @PathVariable String env, @PathVariable("clusterName") String clusterName) {

return clusterService.loadCluster(appId, Env.fromString(env), clusterName);
}

}
@@ -1,5 +1,7 @@
package com.ctrip.framework.apollo.portal.controller;

import com.ctrip.framework.apollo.common.dto.AppNamespaceDTO;
import com.ctrip.framework.apollo.common.utils.BeanUtils;
import com.ctrip.framework.apollo.portal.listener.AppNamespaceDeletionEvent;
import com.google.common.collect.Sets;

Expand Down Expand Up @@ -144,6 +146,18 @@ public ResponseEntity<Void> deleteAppNamespace(@PathVariable String appId, @Path
return ResponseEntity.ok().build();
}

@RequestMapping(value = "/apps/{appId}/appnamespaces/{namespaceName:.+}", method = RequestMethod.GET)
public AppNamespaceDTO findAppNamespace(@PathVariable String appId, @PathVariable String namespaceName) {
AppNamespace appNamespace = appNamespaceService.findByAppIdAndName(appId, namespaceName);

if (appNamespace == null) {
throw new BadRequestException(
String.format("AppNamespace not exists. AppId = %s, NamespaceName = %s", appId, namespaceName));
}

return BeanUtils.transfrom(AppNamespaceDTO.class, appNamespace);
}

@PreAuthorize(value = "@permissionValidator.hasCreateAppNamespacePermission(#appId, #appNamespace)")
@RequestMapping(value = "/apps/{appId}/appnamespaces", method = RequestMethod.POST)
public AppNamespace createAppNamespace(@PathVariable String appId, @RequestBody AppNamespace appNamespace) {
Expand Down
Expand Up @@ -74,26 +74,22 @@ public NamespaceDTO createNamespace(Env env, NamespaceDTO namespace) {
@Transactional
public void deleteNamespace(String appId, Env env, String clusterName, String namespaceName) {

//1. check private namespace
AppNamespace appNamespace = appNamespaceService.findByAppIdAndName(appId, namespaceName);
if (appNamespace != null && !appNamespace.isPublic()) {
throw new BadRequestException("Private namespace can not be deleted");
}

//2. check parent namespace has not instances
//1. check parent namespace has not instances
if (namespaceHasInstances(appId, env, clusterName, namespaceName)) {
throw new BadRequestException("Can not delete namespace because namespace has active instances");
}

//3. check child namespace has not instances
//2. check child namespace has not instances
NamespaceDTO childNamespace = branchService.findBranchBaseInfo(appId, env, clusterName, namespaceName);
if (childNamespace != null &&
namespaceHasInstances(appId, env, childNamespace.getClusterName(), namespaceName)) {
throw new BadRequestException("Can not delete namespace because namespace's branch has active instances");
}

//4. check public namespace has not associated namespace
if (appNamespace != null && publicAppNamespaceHasAssociatedNamespace(namespaceName, env)) {
//3. check public namespace has not associated namespace
if (appNamespace != null && appNamespace.isPublic() && publicAppNamespaceHasAssociatedNamespace(namespaceName, env)) {
throw new BadRequestException("Can not delete public namespace which has associated namespaces");
}

Expand Down
6 changes: 2 additions & 4 deletions apollo-portal/src/main/resources/static/config.html
Expand Up @@ -288,8 +288,7 @@ <h4 class="text-center" ng-show="viewMode == 2">
apollo-detail="'发现有 <b>' + deleteNamespaceContext.namespace.instancesCount +
'</b> 个实例正在使用Namespace(' + deleteNamespaceContext.namespace.baseInfo.namespaceName +
'),删除Namespace将导致实例获取不到配置。<br>
请到 <ins>“实例列表”</ins> 确认实例信息,如已确认删除Namespace将不会导致实例异常,
请联系Apollo相关负责人删除Namespace'"
请到 <ins>“实例列表”</ins> 确认实例信息,如确认相关实例都已经不再使用该Namespace配置,可以联系Apollo相关负责人删除实例信息(InstanceConfig)或等待实例24小时自动过期后再来删除。'"
apollo-confirm="continueDeleteNamespace">
</apolloconfirmdialog>

Expand All @@ -298,8 +297,7 @@ <h4 class="text-center" ng-show="viewMode == 2">
apollo-detail="'发现有 <b>' + deleteNamespaceContext.namespace.branch.latestReleaseInstances.total
+ '</b> 个实例正在使用Namespace(' + deleteNamespaceContext.namespace.baseInfo.namespaceName +
')灰度版本的配置,删除Namespace将导致实例获取不到配置。<br>
请到 <ins>“灰度版本” => “实例列表”</ins> 确认实例信息,如已确认删除Namespace将不会导致实例异常,
请联系Apollo相关负责人删除Namespace'"
请到 <ins>“灰度版本” => “实例列表”</ins> 确认实例信息,如确认相关实例都已经不再使用该Namespace配置,可以联系Apollo相关负责人删除实例信息(InstanceConfig)或等待实例24小时自动过期后再来删除。'"
apollo-confirm="continueDeleteNamespace">
</apolloconfirmdialog>

Expand Down
@@ -0,0 +1,223 @@
<!doctype html>
<html ng-app="delete_app_cluster_namespace">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<link rel="icon" href="../img/config.png">
<!-- styles -->
<link rel="stylesheet" type="text/css" href="../vendor/bootstrap/css/bootstrap.min.css">
<link rel="stylesheet" type="text/css" href="../vendor/angular/angular-toastr-1.4.1.min.css">
<link rel="stylesheet" type="text/css" media='all' href="../vendor/angular/loading-bar.min.css">
<link rel="stylesheet" type="text/css" href="../styles/common-style.css">
<link rel="stylesheet" type="text/css" href="../vendor/select2/select2.min.css">
<title>删除应用、集群、AppNamespace</title>
</head>

<body>

<apollonav></apollonav>


<div class="container-fluid" ng-controller="DeleteAppClusterNamespaceController">
<div class="col-md-10 col-md-offset-1 panel">

<section class="panel-body" ng-show="isRootUser">
<!-- delete app -->
<section class="row">
<h5>删除应用
<small>
(由于删除应用影响面较大,所以现在暂时只允许系统管理员删除,请确保没有客户端读取该应用的配置后再做删除动作)
</small>
</h5>
<hr>
<form class="form-horizontal">
<div class="form-group" valdr-form-group>
<label class="col-sm-2 control-label">
<apollorequiredfield></apollorequiredfield>
应用AppId</label>
<div class="col-sm-5">
<input type="text" class="form-control" ng-model="app.appId">
<small>(删除前请先查询应用信息)</small>
</div>
<div class="col-sm-1">
<button class="btn btn-info" ng-click="getAppInfo()">查询</button>
</div>
</div>
<div class="form-group" valdr-form-group>
<label class="col-sm-2 control-label">
应用信息</label>
<div class="col-sm-5">
<h5 ng-show="app.info" ng-bind="app.info"></h5>
</div>
</div>

<div class="form-group">
<div class="col-sm-offset-2 col-sm-9">
<button type="submit" class="btn btn-primary"
ng-disabled="deleteAppBtnDisabled"
ng-click="deleteApp()">
删除应用
</button>
</div>
</div>
</form>
</section>

<!-- delete cluster -->
<section class="row">
<h5>删除集群
<small>
(由于删除集群影响面较大,所以现在暂时只允许系统管理员删除,请确保没有客户端读取该集群的配置后再做删除动作)
</small>
</h5>
<hr>
<form class="form-horizontal">
<div class="form-group" valdr-form-group>
<label class="col-sm-2 control-label">
<apollorequiredfield></apollorequiredfield>
应用AppId</label>
<div class="col-sm-5">
<input type="text" class="form-control" ng-model="cluster.appId">
</div>
</div>
<div class="form-group" valdr-form-group>
<label class="col-sm-2 control-label">
<apollorequiredfield></apollorequiredfield>
环境名称</label>
<div class="col-sm-5">
<input type="text" class="form-control" ng-model="cluster.env">
</div>
</div>
<div class="form-group" valdr-form-group>
<label class="col-sm-2 control-label">
<apollorequiredfield></apollorequiredfield>
集群名称</label>
<div class="col-sm-5">
<input type="text" class="form-control" ng-model="cluster.name">
<small>(删除前请先查询应用集群信息)</small>
</div>
<div class="col-sm-1">
<button class="btn btn-info" ng-click="getClusterInfo()">查询</button>
</div>
</div>
<div class="form-group" viv clform-group>
<label class="col-sm-2 control-label">
集群信息</label>
<div class="col-sm-5">
<h5 ng-show="cluster.info" ng-bind="cluster.info"></h5>
</div>
</div>

<div class="form-group">
<div class="col-sm-offset-2 col-sm-9">
<button type="submit" class="btn btn-primary"
ng-disabled="deleteClusterBtnDisabled"
ng-click="deleteCluster()">
删除集群
</button>
</div>
</div>
</form>
</section>

<!-- delete app namespace -->
<section class="row">
<h5>删除AppNamespace
<small>(注意,所有环境的Namespace和AppNamespace都会被删除!如果只是要删除某个环境的Namespace,让用户到项目页面中自行删除!)</small>
</h5>
<small>
目前用户可以自行删除关联的Namespace和私有的Namespace,不过无法删除AppNamespace元信息,因为删除AppNamespace影响面较大,所以现在暂时只允许系统管理员删除,对于公共Namespace需要确保没有应用关联了该AppNamespace。
</small>
<hr>
<form class="form-horizontal">
<div class="form-group" valdr-form-group>
<label class="col-sm-2 control-label">
<apollorequiredfield></apollorequiredfield>
应用AppId</label>
<div class="col-sm-5">
<input type="text" class="form-control" ng-model="appNamespace.appId">
</div>
</div>
<div class="form-group" valdr-form-group>
<label class="col-sm-2 control-label">
<apollorequiredfield></apollorequiredfield>
AppNamespace名称</label>
<div class="col-sm-5">
<input type="text" class="form-control" ng-model="appNamespace.name">
<small>(非properties类型的namespace请加上类型后缀,例如apollo.xml)</small>
</div>
<div class="col-sm-1">
<button class="btn btn-info" ng-click="getAppNamespaceInfo()">查询</button>
</div>
</div>
<div class="form-group" viv clform-group>
<label class="col-sm-2 control-label">
集群信息</label>
<div class="col-sm-5">
<h5 ng-show="appNamespace.info" ng-bind="appNamespace.info"></h5>
</div>
</div>

<div class="form-group">
<div class="col-sm-offset-2 col-sm-9">
<button type="submit" class="btn btn-primary"
ng-disabled="deleteAppNamespaceBtnDisabled"
ng-click="deleteAppNamespace()">
删除AppNamespace
</button>
</div>
</div>
</form>
</section>
</section>

<section class="panel-body text-center" ng-if="!isRootUser">
<h4>当前页面只对Apollo管理员开放</h4>
</section>

</div>
</div>

<div ng-include="'../views/common/footer.html'"></div>

<!-- jquery.js -->
<script src="../vendor/jquery.min.js" type="text/javascript"></script>

<!--angular-->
<script src="../vendor/angular/angular.min.js"></script>
<script src="../vendor/angular/angular-route.min.js"></script>
<script src="../vendor/angular/angular-resource.min.js"></script>
<script src="../vendor/angular/angular-toastr-1.4.1.tpls.min.js"></script>
<script src="../vendor/angular/loading-bar.min.js"></script>

<!--valdr-->
<script src="../vendor/valdr/valdr.min.js" type="text/javascript"></script>
<script src="../vendor/valdr/valdr-message.min.js" type="text/javascript"></script>

<!-- bootstrap.js -->
<script src="../vendor/bootstrap/js/bootstrap.min.js" type="text/javascript"></script>

<!--nicescroll-->
<script src="../vendor/jquery.nicescroll.min.js"></script>
<script src="../vendor/lodash.min.js"></script>

<script src="../vendor/select2/select2.min.js" type="text/javascript"></script>
<!--biz-->
<!--must import-->
<script type="application/javascript" src="../scripts/app.js"></script>
<script type="application/javascript" src="../scripts/services/AppService.js"></script>
<script type="application/javascript" src="../scripts/services/EnvService.js"></script>
<script type="application/javascript" src="../scripts/services/UserService.js"></script>
<script type="application/javascript" src="../scripts/services/CommonService.js"></script>
<script type="application/javascript" src="../scripts/services/PermissionService.js"></script>
<script type="application/javascript" src="../scripts/services/ClusterService.js"></script>
<script type="application/javascript" src="../scripts/services/NamespaceService.js"></script>

<script type="application/javascript" src="../scripts/AppUtils.js"></script>

<script type="application/javascript" src="../scripts/PageCommon.js"></script>
<script type="application/javascript" src="../scripts/directive/directive.js"></script>
<script type="application/javascript" src="../scripts/valdr.js"></script>

<script type="application/javascript" src="../scripts/controller/DeleteAppClusterNamespaceController.js"></script>
</body>
</html>
5 changes: 2 additions & 3 deletions apollo-portal/src/main/resources/static/scripts/app.js
Expand Up @@ -34,6 +34,5 @@ var open_manage_module = angular.module('open_manage', ['app.service', 'apollo.d
var user_module = angular.module('user', ['apollo.directive', 'toastr', 'app.service', 'app.util', 'angular-loading-bar', 'valdr']);
//login
var login_module = angular.module('login', ['toastr', 'app.util']);



//delete app cluster namespace
var delete_app_cluster_namespace_module = angular.module('delete_app_cluster_namespace', ['app.service', 'apollo.directive', 'app.util', 'toastr', 'angular-loading-bar']);