提交 6cd90c16 编写于 作者: L lepdou

add cluster

上级 7b84e654
......@@ -5,6 +5,7 @@ import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.ctrip.framework.apollo.common.entity.App;
import com.ctrip.framework.apollo.core.ConfigConsts;
@Service
public class AdminService {
......@@ -32,7 +33,7 @@ public class AdminService {
clusterService.createDefaultCluster(appId, createBy);
namespaceService.createDefaultNamespace(appId, createBy);
namespaceService.createDefaultNamespace(appId, ConfigConsts.CLUSTER_NAME_DEFAULT, createBy);
return app;
}
......
......@@ -26,6 +26,9 @@ public class ClusterService {
@Autowired
private AuditService auditService;
@Autowired
private NamespaceService namespaceService;
public boolean isClusterNameUnique(String appId, String clusterName) {
Objects.requireNonNull(appId, "AppId must not be null");
Objects.requireNonNull(clusterName, "ClusterName must not be null");
......@@ -56,6 +59,8 @@ public class ClusterService {
entity.setId(0);//protection
Cluster cluster = clusterRepository.save(entity);
namespaceService.createDefaultNamespace(cluster.getAppId(), cluster.getName(), cluster.getDataChangeCreatedBy());
auditService.audit(Cluster.class.getSimpleName(), cluster.getId(), Audit.OP.INSERT,
cluster.getDataChangeCreatedBy());
......
......@@ -91,14 +91,14 @@ public class NamespaceService {
}
@Transactional
public void createDefaultNamespace(String appId, String createBy) {
if (!isNamespaceUnique(appId, ConfigConsts.CLUSTER_NAME_DEFAULT, appId)) {
public void createDefaultNamespace(String appId, String clusterName, String createBy) {
if (!isNamespaceUnique(appId, clusterName, appId)) {
throw new ServiceException("namespace not unique");
}
Namespace ns = new Namespace();
ns.setAppId(appId);
ns.setClusterName(ConfigConsts.CLUSTER_NAME_DEFAULT);
ns.setClusterName(clusterName);
ns.setNamespaceName(ConfigConsts.NAMESPACE_APPLICATION);
ns.setDataChangeCreatedBy(createBy);
ns.setDataChangeLastModifiedBy(createBy);
......
package com.ctrip.framework.apollo.common.controller;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
......@@ -34,6 +36,7 @@ import static org.springframework.http.MediaType.APPLICATION_JSON;
@ControllerAdvice
public class GlobalDefaultExceptionHandler {
private static final Logger logger = LoggerFactory.getLogger(GlobalDefaultExceptionHandler.class);
private Gson gson = new Gson();
......@@ -41,6 +44,7 @@ public class GlobalDefaultExceptionHandler {
@ExceptionHandler(Exception.class)
public ResponseEntity<Map<String, Object>> exception(HttpServletRequest request, Exception ex) {
logger.error("internal server error", ex);
return handleError(request, INTERNAL_SERVER_ERROR, ex);
}
......@@ -64,6 +68,7 @@ public class GlobalDefaultExceptionHandler {
@ExceptionHandler(HttpStatusCodeException.class)
public ResponseEntity<Map<String, Object>> restTemplateException(HttpServletRequest request,
HttpStatusCodeException ex) {
logger.error("rest template exception", ex);
Map<String, Object> errorAttributes = gson.fromJson(ex.getResponseBodyAsString(), mapType);
HttpHeaders headers = new HttpHeaders();
headers.setContentType(APPLICATION_JSON);
......
......@@ -126,6 +126,11 @@ public class AdminServiceAPI {
getAdminServiceHost(env), appId);
return Arrays.asList(clusterDTOs);
}
public ClusterDTO createOrUpdate(Env env, ClusterDTO cluster){
return restTemplate.postForObject("{host}/apps/{appId}/clusters", cluster, ClusterDTO.class,
getAdminServiceHost(env), cluster.getAppId());
}
}
@Service
......
package com.ctrip.framework.apollo.portal.controller;
import com.ctrip.framework.apollo.core.dto.ClusterDTO;
import com.ctrip.framework.apollo.core.enums.Env;
import com.ctrip.framework.apollo.portal.service.ClusterService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import static com.ctrip.framework.apollo.common.utils.RequestPrecondition.checkArgument;
@RestController
public class ClusterController {
@Autowired
private ClusterService clusterService;
@PreAuthorize(value = "@permissionValidator.hasCreateClusterPermission(#appId)")
@RequestMapping(value = "apps/{appId}/envs/{env}/clusters", method = RequestMethod.POST)
public ClusterDTO createCluster(@PathVariable String appId, @PathVariable String env,
@RequestBody ClusterDTO cluster){
checkArgument(cluster.getAppId(), cluster.getName());
return clusterService.createCluster(Env.valueOf(env), cluster);
}
}
......@@ -3,6 +3,7 @@ package com.ctrip.framework.apollo.portal.service;
import com.ctrip.framework.apollo.core.enums.Env;
import com.ctrip.framework.apollo.core.dto.ClusterDTO;
import com.ctrip.framework.apollo.portal.api.AdminServiceAPI;
import com.ctrip.framework.apollo.portal.auth.UserInfoHolder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
......@@ -12,6 +13,8 @@ import java.util.List;
@Service
public class ClusterService {
@Autowired
private UserInfoHolder userInfoHolder;
@Autowired
private AdminServiceAPI.ClusterAPI clusterAPI;
......@@ -19,4 +22,11 @@ public class ClusterService {
return clusterAPI.findClustersByApp(appId, env);
}
public ClusterDTO createCluster(Env env, ClusterDTO cluster){
String operator = userInfoHolder.getUser().getUserId();
cluster.setDataChangeLastModifiedBy(operator);
cluster.setDataChangeCreatedBy(operator);
return clusterAPI.createOrUpdate(env, cluster);
}
}
......@@ -93,6 +93,6 @@
<script type="application/javascript" src="scripts/services/OrganizationService.js"></script>
<script type="application/javascript" src="scripts/directive.js"></script>
<script type="application/javascript" src="scripts/controller/CreateAppController.js"></script>
<script type="application/javascript" src="scripts/controller/AppController.js"></script>
</body>
</html>
<!doctype html>
<html ng-app="cluster">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<!-- 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" href="vendor/select2/select2.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">
<title>新建集群</title>
</head>
<body>
<apollonav></apollonav>
<div class="container-fluid apollo-container" ng-controller="ClusterController">
<div class="row">
<div class="col-md-8 col-md-offset-2">
<div class="panel">
<header class="panel-heading">
<div class="row">
<div class="col-md-6">
<h4>创建集群</h4>
</div>
<div class="col-md-6 text-right">
<a type="button" class="btn btn-primary" href="/config.html?#/appid={{appId}}">返回
</a>
</div>
</div>
</header>
<div class="panel-body">
<form class="form-horizontal" ng-submit="create()">
<div class="form-group">
<label class="col-sm-2 control-label">
<apollorequiredfiled></apollorequiredfiled>
应用AppId</label>
<div class="col-sm-6" ng-bind="appId">
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label">
<apollorequiredfiled></apollorequiredfiled>
集群名称</label>
<div class="col-sm-6">
<input type="text" class="form-control" name="appName" ng-model="clusterName" required>
<small>(部署集群如:SHAJQ,SHAOY 或自定义集群如:SHAJQ-xx,SHAJQ-yy)</small>
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label">
<apollorequiredfiled></apollorequiredfiled>
选择环境</label>
<div class="col-sm-5">
<table class="table table-hover" style="width: 100px">
<tbody>
<tr style="cursor: pointer" ng-repeat="env in envs">
<td width="10%"><input type="checkbox" ng-click="switchChecked(env)"></td>
<td width="30%" ng-bind="env.name"></td>
</tr>
</tbody>
</table>
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<button type="submit" class="btn btn-primary">提交</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
<div ng-include="'views/common/footer.html'"></div>
<!--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>
<!-- jquery.js -->
<script src="vendor/jquery.min.js" type="text/javascript"></script>
<script src="vendor/select2/select2.min.js" type="text/javascript"></script>
<!-- bootstrap.js -->
<script src="vendor/bootstrap/js/bootstrap.min.js" type="text/javascript"></script>
<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/ClusterService.js"></script>
<script type="application/javascript" src="scripts/AppUtils.js"></script>
<script type="application/javascript" src="scripts/directive.js"></script>
<script type="application/javascript" src="scripts/controller/ClusterController.js"></script>
</body>
</html>
......@@ -65,41 +65,19 @@
</div>
</section>
<a class="list-group-item" ng-show="missEnvs.length > 0" ng-click="createAppInMissEnv()">
<div class="row">
<div class="col-md-2"><img src="img/plus-orange.png" class="i-20"></div>
<div class="col-md-10 hidden-xs">
<p class="btn-title">补缺环境</p>
</div>
</div>
</a>
<a class="list-group-item" href="#" disabled="disabled" ng-show="false">
<div class="row">
<div class="col-md-2"><img src="../img/plus-orange.png" class="i-20"></div>
<div class="col-md-10 hidden-xs">
<p class="btn-title">添加集群</p>
</div>
<apolloentrance apollo-title="'补缺环境'" apollo-img-src="'img/plus-orange.png'"
ng-show="missEnvs.length > 0" ng-click="createAppInMissEnv()"></apolloentrance>
</div>
</a>
<a class="list-group-item" href="namespace.html?#/appid={{pageContext.appId}}&type=link"
ng-show="hasCreateNamespacePermission">
<div class="row">
<div class="col-md-2"><img src="img/plus-orange.png" class="i-20"></div>
<div class="col-md-10 hidden-xs">
<p class="btn-title">添加Namespace</p>
</div>
</div>
</a>
<a class="list-group-item" href="/app/role.html?#/appid={{pageContext.appId}}"
ng-show="hasAssignUserPermission">
<div class="row">
<div class="col-md-2"><img src="img/user_manage.png" class="i-20"></div>
<div class="col-md-10 hidden-xs">
<p class="btn-title">项目负责人</p>
</div>
</div>
</a>
<apolloentrance apollo-title="'添加集群'" apollo-img-src="'img/plus-orange.png'"
apollo-href="'cluster.html?#/appid=' + pageContext.appId"></apolloentrance>
<apolloentrance apollo-title="'添加Namespace'" apollo-img-src="'img/plus-orange.png'"
apollo-href="'namespace.html?#/appid=' + pageContext.appId + '&type=link'"
ng-show="hasCreateNamespacePermission"></apolloentrance>
<apolloentrance apollo-title="'项目负责人'" apollo-img-src="'img/user_manage.png'"
apollo-href="'/app/role.html?#/appid=' + pageContext.appId"
ng-show="hasAssignUserPermission"></apolloentrance>
</section>
<!--create env modal-->
......@@ -301,13 +279,15 @@
</div>
<div class="media" ng-repeat="commits in namespace.commits">
<div class="media-left">
<h4 class="media-heading" ng-bind="commits.dataChangeCreatedBy"></h4>
</div>
<div class="media-body">
<h5 class="media-heading"
ng-bind="commits.dataChangeCreatedTime | date: 'yyyy-MM-dd HH:mm:ss'"></h5>
<table class="table table-bordered table-striped text-center table-hover">
<div class="row">
<div class="col-md-6"><h3 class="media-heading" ng-bind="commits.dataChangeCreatedBy"></h3></div>
<div class="col-md-6 text-right"><h5 class="media-heading"
ng-bind="commits.dataChangeCreatedTime | date: 'yyyy-MM-dd HH:mm:ss'"></h5></div>
</div>
<table class="table table-bordered table-striped text-center table-hover" style="margin-top: 5px;">
<thead>
<tr>
<th>
......@@ -329,7 +309,8 @@
</thead>
<tbody>
<tr ng-repeat="item in commits.changeSets.createItems" ng-show="item.key || item.comment">
<tr ng-repeat="item in commits.changeSets.createItems"
ng-show="item.key || item.comment">
<td width="2%" title="新增的配置">
<span class="label label-primary change-type-mark">&nbsp;</span>
</td>
......@@ -370,7 +351,8 @@
<span ng-bind="item.newItem.comment.length > 250 ?'...' : ''"></span>
</td>
</tr>
<tr ng-repeat="item in commits.changeSets.deleteItems" ng-show="item.key || item.comment">
<tr ng-repeat="item in commits.changeSets.deleteItems"
ng-show="item.key || item.comment">
<td width="2%" title="删除的配置">
<span class="label label-danger change-type-mark">&nbsp;</span>
</td>
......@@ -378,12 +360,12 @@
<span ng-bind="item.key | limitTo: 250"></span>
<span ng-bind="item.key.length > 250 ? '...' :''"></span>
</td>
<td width="30%">
</td>
<td width="30%" title="{{item.value}}">
<span ng-bind="item.value | limitTo: 250"></span>
<span ng-bind="item.value.length > 250 ? '...': ''"></span>
</td>
<td width="30%">
</td>
<td width="18%" title="{{item.comment}}">
<span ng-bind="item.comment | limitTo: 250"></span>
<span ng-bind="item.comment.length > 250 ?'...' : ''"></span>
......@@ -540,7 +522,7 @@
</label>
<div class="col-sm-10">
<textarea type="text" class="form-control" rows="6" ng-model="item.value"
ng-show="tableViewOperType != 'retrieve'">
ng-show="tableViewOperType != 'retrieve'">
</textarea>
<p ng-bind="item.value" ng-show="tableViewOperType == 'retrieve'"></p>
</div>
......
......@@ -40,7 +40,7 @@
</div>
<div class="form-horizontal" ng-show="type == 'link'">
<div class="form-group">
<label class="col-sm-3 control-label"><font style="color: red">*</font>选择集群</label>
<label class="col-sm-3 control-label"><apollorequiredfiled></apollorequiredfiled> 选择集群</label>
<div class="col-sm-6">
<apolloclusterselector apollo-app-id="appId" apollo-default-all-checked="true"
apollo-select="collectSelectedClusters"></apolloclusterselector>
......@@ -48,7 +48,7 @@
</div>
</div>
<div class="form-group" ng-show="type == 'create'">
<label class="col-sm-3 control-label"><font style="color: red">*</font>名称</label>
<label class="col-sm-3 control-label"><apollorequiredfiled></apollorequiredfiled> 名称</label>
<div class="col-sm-4">
<div class="input-group">
<span class="input-group-addon" ng-bind="appBaseInfo.namespacePrefix"></span>
......@@ -65,7 +65,7 @@
</div>
</div>
<div class="form-group" ng-show="type == 'link'">
<label class="col-sm-3 control-label"><font style="color: red">*</font>namespace</label>
<label class="col-sm-3 control-label"><apollorequiredfiled></apollorequiredfiled> namespace</label>
<div class="col-sm-4">
<select id="namespaces">
<option></option>
......
......@@ -22,6 +22,8 @@ var namespace_module = angular.module('namespace', ['app.service', 'apollo.direc
var server_config_module = angular.module('server_config', ['app.service', 'apollo.directive', 'app.util', 'toastr', 'angular-loading-bar']);
//role
var role_module = angular.module('role', ['app.service', 'apollo.directive', 'app.util', 'toastr', 'angular-loading-bar']);
//cluster
var cluster_module = angular.module('cluster', ['app.service', 'apollo.directive', 'app.util', 'toastr', 'angular-loading-bar']);
......
cluster_module.controller('ClusterController',
['$scope', '$location', '$window', 'toastr', 'AppService', 'EnvService', 'ClusterService',
'AppUtil',
function ($scope, $location, $window, toastr, AppService, EnvService, ClusterService,
AppUtil) {
var params = AppUtil.parseParams($location.$$url);
$scope.appId = params.appid;
EnvService.find_all_envs().then(function (result) {
$scope.envs = [];
result.forEach(function (env) {
$scope.envs.push({name: env, checked: false});
})
}, function (result) {
toastr.error(AppUtil.errorMsg(result), "加载环境信息出错");
});
$scope.clusterName = '';
$scope.switchChecked = function (env) {
env.checked = !env.checked;
};
$scope.create = function () {
$scope.envs.forEach(function (env) {
ClusterService.create_cluster($scope.appId, env.name,
{
name: $scope.clusterName,
appId: $scope.appId
}).then(function (result) {
toastr.success(env.name, "集群创建成功");
}, function (result) {
toastr.error(AppUtil.errorMsg(result), "集群创建失败");
})
})
};
}]);
......@@ -133,6 +133,13 @@ application_module.controller("ConfigBaseInfoController",
}, function (result) {
});
PermissionService.has_create_cluster_permission(appId).then(function (result) {
$scope.hasCreateClusterPermission = result.hasPermission;
}, function (result) {
});
PermissionService.has_assign_user_permission(appId).then(function (result) {
$scope.hasAssignUserPermission = result.hasPermission;
}, function (result) {
......
......@@ -197,6 +197,7 @@ directive_module.directive('apolloclusterselector', function ($compile, $window,
});
/** 必填项*/
directive_module.directive('apollorequiredfiled', function ($compile, $window) {
return {
restrict: 'E',
......@@ -206,6 +207,7 @@ directive_module.directive('apollorequiredfiled', function ($compile, $window) {
}
});
/** 确认框 */
directive_module.directive('apolloconfirmdialog', function ($compile, $window) {
return {
restrict: 'E',
......@@ -230,3 +232,23 @@ directive_module.directive('apolloconfirmdialog', function ($compile, $window) {
}
}
});
/** entrance */
directive_module.directive('apolloentrance', function ($compile, $window) {
return {
restrict: 'E',
templateUrl: '../views/component/entrance.html',
transclude: true,
replace: true,
scope: {
imgSrc: '=apolloImgSrc',
title: '=apolloTitle',
href: '=apolloHref'
},
link: function (scope, element, attrs) {
console.log(scope.title);
}
}
});
appService.service('ClusterService', ['$resource', '$q', function ($resource, $q) {
var cluster_resource = $resource('', {}, {
create_cluster: {
method: 'POST',
url: 'apps/:appId/envs/:env/clusters'
}
});
return {
create_cluster: function (appId, env, cluster) {
var d = $q.defer();
cluster_resource.create_cluster({
appId: appId,
env: env
}, cluster,
function (result) {
d.resolve(result);
}, function (result) {
d.reject(result);
});
return d.promise;
}
}
}]);
<a class="list-group-item" href="{{href}}">
<div class="row">
<div class="col-md-2"><img src="{{imgSrc}}" class="i-20"></div>
<div class="col-md-10 hidden-xs">
<p class="btn-title">{{title}}</p>
</div>
</div>
</a>
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册