Commit e4572e13 authored by nobodyiam's avatar nobodyiam

allow users to create missing private namespaces

parent 9655981d
...@@ -31,20 +31,26 @@ public class AppNamespaceController { ...@@ -31,20 +31,26 @@ public class AppNamespaceController {
private NamespaceService namespaceService; private NamespaceService namespaceService;
@RequestMapping(value = "/apps/{appId}/appnamespaces", method = RequestMethod.POST) @RequestMapping(value = "/apps/{appId}/appnamespaces", method = RequestMethod.POST)
public AppNamespaceDTO create(@RequestBody AppNamespaceDTO appNamespace) { public AppNamespaceDTO create(@RequestBody AppNamespaceDTO appNamespace,
@RequestParam(defaultValue = "false") boolean silentCreation) {
AppNamespace entity = BeanUtils.transfrom(AppNamespace.class, appNamespace); AppNamespace entity = BeanUtils.transfrom(AppNamespace.class, appNamespace);
AppNamespace managedEntity = appNamespaceService.findOne(entity.getAppId(), entity.getName()); AppNamespace managedEntity = appNamespaceService.findOne(entity.getAppId(), entity.getName());
if (managedEntity != null) { if (managedEntity == null) {
throw new BadRequestException("app namespaces already exist.");
}
if (StringUtils.isEmpty(entity.getFormat())){ if (StringUtils.isEmpty(entity.getFormat())){
entity.setFormat(ConfigFileFormat.Properties.getValue()); entity.setFormat(ConfigFileFormat.Properties.getValue());
} }
entity = appNamespaceService.createAppNamespace(entity); entity = appNamespaceService.createAppNamespace(entity);
} else if (silentCreation) {
appNamespaceService.createNamespaceForAppNamespaceInAllCluster(appNamespace.getAppId(), appNamespace.getName(),
appNamespace.getDataChangeCreatedBy());
entity = managedEntity;
} else {
throw new BadRequestException("app namespaces already exist.");
}
return BeanUtils.transfrom(AppNamespaceDTO.class, entity); return BeanUtils.transfrom(AppNamespaceDTO.class, entity);
} }
...@@ -72,4 +78,11 @@ public class AppNamespaceController { ...@@ -72,4 +78,11 @@ public class AppNamespaceController {
return namespaceService.countPublicAppNamespaceAssociatedNamespaces(publicNamespaceName); return namespaceService.countPublicAppNamespaceAssociatedNamespaces(publicNamespaceName);
} }
@RequestMapping(value = "/apps/{appId}/appnamespaces", method = RequestMethod.GET)
public List<AppNamespaceDTO> getAppNamespaces(@PathVariable("appId") String appId) {
List<AppNamespace> appNamespaces = appNamespaceService.findByAppId(appId);
return BeanUtils.batchTransform(AppNamespaceDTO.class, appNamespaces);
}
} }
...@@ -110,7 +110,7 @@ public class AppNamespaceService { ...@@ -110,7 +110,7 @@ public class AppNamespaceService {
appNamespace = appNamespaceRepository.save(appNamespace); appNamespace = appNamespaceRepository.save(appNamespace);
instanceOfAppNamespaceInAllCluster(appNamespace.getAppId(), appNamespace.getName(), createBy); createNamespaceForAppNamespaceInAllCluster(appNamespace.getAppId(), appNamespace.getName(), createBy);
auditService.audit(AppNamespace.class.getSimpleName(), appNamespace.getId(), Audit.OP.INSERT, createBy); auditService.audit(AppNamespace.class.getSimpleName(), appNamespace.getId(), Audit.OP.INSERT, createBy);
return appNamespace; return appNamespace;
...@@ -127,7 +127,7 @@ public class AppNamespaceService { ...@@ -127,7 +127,7 @@ public class AppNamespaceService {
return managedNs; return managedNs;
} }
private void instanceOfAppNamespaceInAllCluster(String appId, String namespaceName, String createBy) { public void createNamespaceForAppNamespaceInAllCluster(String appId, String namespaceName, String createBy) {
List<Cluster> clusters = clusterService.findParentClusters(appId); List<Cluster> clusters = clusterService.findParentClusters(appId);
for (Cluster cluster : clusters) { for (Cluster cluster : clusters) {
......
...@@ -105,6 +105,17 @@ public class AdminServiceAPI { ...@@ -105,6 +105,17 @@ public class AdminServiceAPI {
.post(env, "apps/{appId}/appnamespaces", appNamespace, AppNamespaceDTO.class, appNamespace.getAppId()); .post(env, "apps/{appId}/appnamespaces", appNamespace, AppNamespaceDTO.class, appNamespace.getAppId());
} }
public AppNamespaceDTO createMissingAppNamespace(Env env, AppNamespaceDTO appNamespace) {
return restTemplate
.post(env, "apps/{appId}/appnamespaces?silentCreation=true", appNamespace, AppNamespaceDTO.class,
appNamespace.getAppId());
}
public List<AppNamespaceDTO> getAppNamespaces(String appId, Env env) {
AppNamespaceDTO[] appNamespaceDTOs = restTemplate.get(env, "apps/{appId}/appnamespaces", AppNamespaceDTO[].class, appId);
return Arrays.asList(appNamespaceDTOs);
}
public void deleteNamespace(Env env, String appId, String clusterName, String namespaceName, String operator) { public void deleteNamespace(Env env, String appId, String clusterName, String namespaceName, String operator) {
restTemplate restTemplate
.delete(env, "apps/{appId}/clusters/{clusterName}/namespaces/{namespaceName}?operator={operator}", appId, .delete(env, "apps/{appId}/clusters/{clusterName}/namespaces/{namespaceName}?operator={operator}", appId,
......
package com.ctrip.framework.apollo.portal.controller; package com.ctrip.framework.apollo.portal.controller;
import com.ctrip.framework.apollo.common.dto.AppNamespaceDTO; import com.ctrip.framework.apollo.common.dto.AppNamespaceDTO;
import com.ctrip.framework.apollo.common.http.MultiResponseEntity;
import com.ctrip.framework.apollo.common.http.RichResponseEntity;
import com.ctrip.framework.apollo.common.utils.BeanUtils; import com.ctrip.framework.apollo.common.utils.BeanUtils;
import com.ctrip.framework.apollo.portal.api.AdminServiceAPI;
import com.ctrip.framework.apollo.portal.component.PermissionValidator; import com.ctrip.framework.apollo.portal.component.PermissionValidator;
import com.ctrip.framework.apollo.portal.listener.AppNamespaceDeletionEvent; import com.ctrip.framework.apollo.portal.listener.AppNamespaceDeletionEvent;
import com.google.common.collect.Sets; import com.google.common.collect.Sets;
...@@ -25,6 +28,8 @@ import com.ctrip.framework.apollo.portal.spi.UserInfoHolder; ...@@ -25,6 +28,8 @@ import com.ctrip.framework.apollo.portal.spi.UserInfoHolder;
import com.ctrip.framework.apollo.portal.util.RoleUtils; import com.ctrip.framework.apollo.portal.util.RoleUtils;
import com.ctrip.framework.apollo.tracer.Tracer; import com.ctrip.framework.apollo.tracer.Tracer;
import java.util.Set;
import java.util.stream.Collectors;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
...@@ -65,6 +70,8 @@ public class NamespaceController { ...@@ -65,6 +70,8 @@ public class NamespaceController {
private PortalConfig portalConfig; private PortalConfig portalConfig;
@Autowired @Autowired
private PermissionValidator permissionValidator; private PermissionValidator permissionValidator;
@Autowired
private AdminServiceAPI.NamespaceAPI namespaceAPI;
@RequestMapping(value = "/appnamespaces/public", method = RequestMethod.GET) @RequestMapping(value = "/appnamespaces/public", method = RequestMethod.GET)
...@@ -222,6 +229,61 @@ public class NamespaceController { ...@@ -222,6 +229,61 @@ public class NamespaceController {
} }
@RequestMapping(value = "/apps/{appId}/envs/{env}/clusters/{clusterName}/missing-namespaces", method = RequestMethod.GET)
public MultiResponseEntity<String> findMissingNamespaces(@PathVariable String appId, @PathVariable String env, @PathVariable String clusterName) {
MultiResponseEntity<String> response = MultiResponseEntity.ok();
Set<String> missingNamespaces = findMissingNamespaceNames(appId, env, clusterName);
for (String missingNamespace : missingNamespaces) {
response.addResponseEntity(RichResponseEntity.ok(missingNamespace));
}
return response;
}
@RequestMapping(value = "/apps/{appId}/envs/{env}/clusters/{clusterName}/missing-namespaces", method = RequestMethod.POST)
public ResponseEntity<Void> createMissingNamespaces(@PathVariable String appId, @PathVariable String env, @PathVariable String clusterName) {
Set<String> missingNamespaces = findMissingNamespaceNames(appId, env, clusterName);
for (String missingNamespace : missingNamespaces) {
namespaceAPI.createMissingAppNamespace(Env.fromString(env), findAppNamespace(appId, missingNamespace));
}
return ResponseEntity.ok().build();
}
private Set<String> findMissingNamespaceNames(String appId, String env, String clusterName) {
List<AppNamespaceDTO> configDbAppNamespaces = namespaceAPI.getAppNamespaces(appId, Env.fromString(env));
List<NamespaceDTO> configDbNamespaces = namespaceService.findNamespaces(appId, Env.fromString(env), clusterName);
List<AppNamespace> portalDbAppNamespaces = appNamespaceService.findByAppId(appId);
Set<String> configDbAppNamespaceNames = configDbAppNamespaces.stream().map(AppNamespaceDTO::getName)
.collect(Collectors.toSet());
Set<String> configDbNamespaceNames = configDbNamespaces.stream().map(NamespaceDTO::getNamespaceName)
.collect(Collectors.toSet());
Set<String> portalDbAllAppNamespaceNames = Sets.newHashSet();
Set<String> portalDbPrivateAppNamespaceNames = Sets.newHashSet();
for (AppNamespace appNamespace : portalDbAppNamespaces) {
portalDbAllAppNamespaceNames.add(appNamespace.getName());
if (!appNamespace.isPublic()) {
portalDbPrivateAppNamespaceNames.add(appNamespace.getName());
}
}
// AppNamespaces should be the same
Set<String> missingAppNamespaceNames = Sets.difference(portalDbAllAppNamespaceNames, configDbAppNamespaceNames);
// Private namespaces should all exist
Set<String> missingNamespaceNames = Sets.difference(portalDbPrivateAppNamespaceNames, configDbNamespaceNames);
return Sets.union(missingAppNamespaceNames, missingNamespaceNames);
}
private void assignNamespaceRoleToOperator(String appId, String namespaceName) { private void assignNamespaceRoleToOperator(String appId, String namespaceName) {
//default assign modify、release namespace role to namespace creator //default assign modify、release namespace role to namespace creator
String operator = userInfoHolder.getUser().getUserId(); String operator = userInfoHolder.getUser().getUserId();
......
...@@ -16,6 +16,8 @@ public interface AppNamespaceRepository extends PagingAndSortingRepository<AppNa ...@@ -16,6 +16,8 @@ public interface AppNamespaceRepository extends PagingAndSortingRepository<AppNa
List<AppNamespace> findByIsPublicTrue(); List<AppNamespace> findByIsPublicTrue();
List<AppNamespace> findByAppId(String appId);
@Modifying @Modifying
@Query("UPDATE AppNamespace SET IsDeleted=1,DataChange_LastModifiedBy=?2 WHERE AppId=?1") @Query("UPDATE AppNamespace SET IsDeleted=1,DataChange_LastModifiedBy=?2 WHERE AppId=?1")
int batchDeleteByAppId(String appId, String operator); int batchDeleteByAppId(String appId, String operator);
......
...@@ -62,6 +62,10 @@ public class AppNamespaceService { ...@@ -62,6 +62,10 @@ public class AppNamespaceService {
return appNamespaceRepository.findByAppIdAndName(appId, namespaceName); return appNamespaceRepository.findByAppIdAndName(appId, namespaceName);
} }
public List<AppNamespace> findByAppId(String appId) {
return appNamespaceRepository.findByAppId(appId);
}
@Transactional @Transactional
public void createDefaultAppNamespace(String appId) { public void createDefaultAppNamespace(String appId) {
if (!isAppNamespaceNameUnique(appId, ConfigConsts.NAMESPACE_APPLICATION)) { if (!isAppNamespaceNameUnique(appId, ConfigConsts.NAMESPACE_APPLICATION)) {
......
...@@ -133,6 +133,10 @@ public class NamespaceService { ...@@ -133,6 +133,10 @@ public class NamespaceService {
return namespaceBOs; return namespaceBOs;
} }
public List<NamespaceDTO> findNamespaces(String appId, Env env, String clusterName) {
return namespaceAPI.findNamespaceByCluster(appId, env, clusterName);
}
public List<NamespaceDTO> getPublicAppNamespaceAllNamespaces(Env env, String publicNamespaceName, int page, public List<NamespaceDTO> getPublicAppNamespaceAllNamespaces(Env env, String publicNamespaceName, int page,
int size) { int size) {
return namespaceAPI.getPublicAppNamespaceAllNamespaces(env, publicNamespaceName, page, size); return namespaceAPI.getPublicAppNamespaceAllNamespaces(env, publicNamespaceName, page, size);
......
...@@ -99,6 +99,13 @@ ...@@ -99,6 +99,13 @@
</span> </span>
</td> </td>
</tr> </tr>
<tr ng-show="missingNamespaces.length > 0">
<th>缺失的Namespace:</th>
<td>
<span ng-repeat="namespace in missingNamespaces" ng-bind="namespace">
</span>
</td>
</tr>
</tbody> </tbody>
</table> </table>
...@@ -116,6 +123,12 @@ ...@@ -116,6 +123,12 @@
</div> </div>
</a> </a>
<a class="list-group-item" ng-show="missingNamespaces.length > 0" ng-click="createMissingNamespaces()">
<div class="row icon-text icon-plus-orange">
<p class="btn-title ng-binding">补缺Namespace</p>
</div>
</a>
<apolloentrance apollo-title="'添加集群'" apollo-img-src="'plus-orange'" <apolloentrance apollo-title="'添加集群'" apollo-img-src="'plus-orange'"
apollo-href="'cluster.html?#/appid=' + pageContext.appId" apollo-href="'cluster.html?#/appid=' + pageContext.appId"
ng-show="hasCreateClusterPermission"></apolloentrance> ng-show="hasCreateClusterPermission"></apolloentrance>
......
...@@ -89,13 +89,41 @@ function ConfigBaseInfoController($rootScope, $scope, $window, $location, toastr ...@@ -89,13 +89,41 @@ function ConfigBaseInfoController($rootScope, $scope, $window, $location, toastr
}); });
}; };
$scope.createMissingNamespaces = function () {
AppService.create_missing_namespaces($rootScope.pageContext.appId, $rootScope.pageContext.env,
$rootScope.pageContext.clusterName).then(function (result) {
toastr.success("创建成功");
location.reload(true);
}, function (result) {
toastr.error(AppUtil.errorMsg(result), "创建失败");
}
);
};
function findMissEnvs() { function findMissEnvs() {
$scope.missEnvs = []; $scope.missEnvs = [];
AppService.find_miss_envs($rootScope.pageContext.appId).then(function (result) { AppService.find_miss_envs($rootScope.pageContext.appId).then(function (result) {
$scope.missEnvs = AppUtil.collectData(result); $scope.missEnvs = AppUtil.collectData(result);
$scope.findMissingNamespaces();
});
}
EventManager.subscribe(EventManager.EventType.CHANGE_ENV_CLUSTER, function () {
$scope.findMissingNamespaces();
}); });
$scope.findMissingNamespaces = function () {
$scope.missingNamespaces = [];
// only check missing private namespaces when app exists in current env
if ($scope.missEnvs.indexOf($rootScope.pageContext.env) === -1) {
AppService.find_missing_namespaces($rootScope.pageContext.appId, $rootScope.pageContext.env,
$rootScope.pageContext.clusterName).then(function (result) {
$scope.missingNamespaces = AppUtil.collectData(result);
});
} }
};
function recordVisitApp() { function recordVisitApp() {
//save user recent visited apps //save user recent visited apps
var VISITED_APPS_STORAGE_KEY = "VisitedAppsV2"; var VISITED_APPS_STORAGE_KEY = "VisitedAppsV2";
...@@ -229,6 +257,7 @@ function ConfigBaseInfoController($rootScope, $scope, $window, $location, toastr ...@@ -229,6 +257,7 @@ function ConfigBaseInfoController($rootScope, $scope, $window, $location, toastr
+ "&cluster=" + $rootScope.pageContext.clusterName; + "&cluster=" + $rootScope.pageContext.clusterName;
EventManager.emit(EventManager.EventType.REFRESH_NAMESPACE); EventManager.emit(EventManager.EventType.REFRESH_NAMESPACE);
EventManager.emit(EventManager.EventType.CHANGE_ENV_CLUSTER);
$rootScope.showSideBar = false; $rootScope.showSideBar = false;
} }
}); });
......
...@@ -35,6 +35,14 @@ appService.service('AppService', ['$resource', '$q', function ($resource, $q) { ...@@ -35,6 +35,14 @@ appService.service('AppService', ['$resource', '$q', function ($resource, $q) {
method: 'GET', method: 'GET',
url: '/apps/:appId/miss_envs' url: '/apps/:appId/miss_envs'
}, },
create_missing_namespaces: {
method: 'POST',
url: '/apps/:appId/envs/:env/clusters/:clusterName/missing-namespaces'
},
find_missing_namespaces: {
method: 'GET',
url: '/apps/:appId/envs/:env/clusters/:clusterName/missing-namespaces'
},
delete_app: { delete_app: {
method: 'DELETE', method: 'DELETE',
isArray: false isArray: false
...@@ -128,6 +136,32 @@ appService.service('AppService', ['$resource', '$q', function ($resource, $q) { ...@@ -128,6 +136,32 @@ appService.service('AppService', ['$resource', '$q', function ($resource, $q) {
}); });
return d.promise; return d.promise;
}, },
create_missing_namespaces: function (appId, env, clusterName) {
var d = $q.defer();
app_resource.create_missing_namespaces({
appId: appId,
env: env,
clusterName: clusterName
}, null, function (result) {
d.resolve(result);
}, function (result) {
d.reject(result);
});
return d.promise;
},
find_missing_namespaces: function (appId, env, clusterName) {
var d = $q.defer();
app_resource.find_missing_namespaces({
appId: appId,
env: env,
clusterName: clusterName
}, function (result) {
d.resolve(result);
}, function (result) {
d.reject(result);
});
return d.promise;
},
delete_app: function (appId) { delete_app: function (appId) {
var d = $q.defer(); var d = $q.defer();
app_resource.delete_app({ app_resource.delete_app({
......
...@@ -136,7 +136,8 @@ appService.service('EventManager', [function () { ...@@ -136,7 +136,8 @@ appService.service('EventManager', [function () {
EMERGENCY_PUBLISH: 'emergency_publish', EMERGENCY_PUBLISH: 'emergency_publish',
PRE_DELETE_NAMESPACE: 'pre_delete_namespace', PRE_DELETE_NAMESPACE: 'pre_delete_namespace',
DELETE_NAMESPACE: 'delete_namespace', DELETE_NAMESPACE: 'delete_namespace',
DELETE_NAMESPACE_FAILED: 'delete_namespace_failed' DELETE_NAMESPACE_FAILED: 'delete_namespace_failed',
CHANGE_ENV_CLUSTER: "change_env_cluster"
} }
} }
......
...@@ -298,7 +298,7 @@ table th { ...@@ -298,7 +298,7 @@ table th {
.project-info th { .project-info th {
text-align: right; text-align: right;
padding: 4px 6px; padding: 4px 6px;
white-space: nowrap; width: 5em;
} }
.project-info td { .project-info td {
...@@ -306,6 +306,10 @@ table th { ...@@ -306,6 +306,10 @@ table th {
padding: 4px 6px; padding: 4px 6px;
} }
.project-info td span {
margin-right: 5px;
}
#config-info { #config-info {
min-height: 500px; min-height: 500px;
} }
......
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