package com.ctrip.framework.apollo.portal.controller;
import com.google.common.collect.Sets;
import com.ctrip.framework.apollo.common.dto.NamespaceDTO;
import com.ctrip.framework.apollo.common.entity.AppNamespace;
import com.ctrip.framework.apollo.common.exception.BadRequestException;
import com.ctrip.framework.apollo.common.utils.InputValidator;
import com.ctrip.framework.apollo.common.utils.RequestPrecondition;
import com.ctrip.framework.apollo.core.enums.Env;
import com.ctrip.framework.apollo.portal.component.config.PortalConfig;
import com.ctrip.framework.apollo.portal.constant.RoleType;
import com.ctrip.framework.apollo.portal.entity.bo.NamespaceBO;
import com.ctrip.framework.apollo.portal.entity.model.NamespaceCreationModel;
import com.ctrip.framework.apollo.portal.listener.AppNamespaceCreationEvent;
import com.ctrip.framework.apollo.portal.service.AppNamespaceService;
import com.ctrip.framework.apollo.portal.service.NamespaceService;
import com.ctrip.framework.apollo.portal.service.RoleInitializationService;
import com.ctrip.framework.apollo.portal.service.RolePermissionService;
import com.ctrip.framework.apollo.portal.spi.UserInfoHolder;
import com.ctrip.framework.apollo.portal.util.RoleUtils;
import com.ctrip.framework.apollo.tracer.Tracer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.util.CollectionUtils;
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.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
import java.util.Map;
import static com.ctrip.framework.apollo.common.utils.RequestPrecondition.checkModel;
@RestController
public class NamespaceController {
private static final Logger logger = LoggerFactory.getLogger(NamespaceController.class);
@Autowired
private ApplicationEventPublisher publisher;
@Autowired
private UserInfoHolder userInfoHolder;
@Autowired
private NamespaceService namespaceService;
@Autowired
private AppNamespaceService appNamespaceService;
@Autowired
private RoleInitializationService roleInitializationService;
@Autowired
private RolePermissionService rolePermissionService;
@Autowired
private PortalConfig portalConfig;
@RequestMapping(value = "/appnamespaces/public", method = RequestMethod.GET)
public List<AppNamespace> findPublicAppNamespaces() {
return appNamespaceService.findPublicAppNamespaces();
}
@RequestMapping(value = "/apps/{appId}/envs/{env}/clusters/{clusterName}/namespaces", method = RequestMethod.GET)
public List<NamespaceBO> findNamespaces(@PathVariable String appId, @PathVariable String env,
@PathVariable String clusterName) {
return namespaceService.findNamespaceBOs(appId, Env.valueOf(env), clusterName);
}
@RequestMapping(value = "/apps/{appId}/envs/{env}/clusters/{clusterName}/namespaces/{namespaceName:.+}", method = RequestMethod.GET)
public NamespaceBO findNamespace(@PathVariable String appId, @PathVariable String env,
@PathVariable String clusterName, @PathVariable String namespaceName) {
return namespaceService.loadNamespaceBO(appId, Env.valueOf(env), clusterName, namespaceName);
}
@RequestMapping(value = "/envs/{env}/apps/{appId}/clusters/{clusterName}/namespaces/{namespaceName}/associated-public-namespace",
method = RequestMethod.GET)
public NamespaceBO findPublicNamespaceForAssociatedNamespace(@PathVariable String env,
@PathVariable String appId,
@PathVariable String namespaceName,
@PathVariable String clusterName) {
return namespaceService.findPublicNamespaceForAssociatedNamespace(Env.valueOf(env), appId, clusterName, namespaceName);
}
@PreAuthorize(value = "@permissionValidator.hasCreateNamespacePermission(#appId)")
@RequestMapping(value = "/apps/{appId}/namespaces", method = RequestMethod.POST)
public ResponseEntity<Void> createNamespace(@PathVariable String appId,
@RequestBody List<NamespaceCreationModel> models) {
checkModel(!CollectionUtils.isEmpty(models));
String namespaceName = models.get(0).getNamespace().getNamespaceName();
String operator = userInfoHolder.getUser().getUserId();
roleInitializationService.initNamespaceRoles(appId, namespaceName, operator);
for (NamespaceCreationModel model : models) {
NamespaceDTO namespace = model.getNamespace();
RequestPrecondition.checkArgumentsNotEmpty(model.getEnv(), namespace.getAppId(),
namespace.getClusterName(), namespace.getNamespaceName());
try {
namespaceService.createNamespace(Env.valueOf(model.getEnv()), namespace);
} catch (Exception e) {
logger.error("create namespace fail.", e);
Tracer.logError(
String.format("create namespace fail. (env=%s namespace=%s)", model.getEnv(),
namespace.getNamespaceName()), e);
}
}
assignNamespaceRoleToOperator(appId, namespaceName);
return ResponseEntity.ok().build();
}
@PreAuthorize(value = "@permissionValidator.hasDeleteNamespacePermission(#appId)")
@RequestMapping(value = "/apps/{appId}/envs/{env}/clusters/{clusterName}/namespaces/{namespaceName:.+}", method = RequestMethod.DELETE)
public ResponseEntity<Void> deleteNamespace(@PathVariable String appId, @PathVariable String env,
@PathVariable String clusterName, @PathVariable String namespaceName) {
namespaceService.deleteNamespace(appId, Env.valueOf(env), clusterName, namespaceName);
return ResponseEntity.ok().build();
}
@PreAuthorize(value = "@permissionValidator.hasCreateAppNamespacePermission(#appId, #appNamespace)")
@RequestMapping(value = "/apps/{appId}/appnamespaces", method = RequestMethod.POST)
public AppNamespace createAppNamespace(@PathVariable String appId, @RequestBody AppNamespace appNamespace) {
RequestPrecondition.checkArgumentsNotEmpty(appNamespace.getAppId(), appNamespace.getName());
if (!InputValidator.isValidAppNamespace(appNamespace.getName())) {
throw new BadRequestException(String.format("Namespace格式错误: %s",
InputValidator.INVALID_CLUSTER_NAMESPACE_MESSAGE + " & "
+ InputValidator.INVALID_NAMESPACE_NAMESPACE_MESSAGE));
}
AppNamespace createdAppNamespace = appNamespaceService.createAppNamespaceInLocal(appNamespace);
if (portalConfig.canAppAdminCreatePrivateNamespace() || createdAppNamespace.isPublic()) {
assignNamespaceRoleToOperator(appId, appNamespace.getName());
}
publisher.publishEvent(new AppNamespaceCreationEvent(createdAppNamespace));
return createdAppNamespace;
}
/**
* env -> cluster -> cluster has not published namespace?
* Example:
* dev ->
* default -> true (default cluster has not published namespace)
* customCluster -> false (customCluster cluster's all namespaces had published)
*/
@RequestMapping(value = "/apps/{appId}/namespaces/publish_info", method = RequestMethod.GET)
public Map<String, Map<String, Boolean>> getNamespacesPublishInfo(@PathVariable String appId) {
return namespaceService.getNamespacesPublishInfo(appId);
}
@RequestMapping(value = "/envs/{env}/appnamespaces/{publicNamespaceName}/namespaces", method = RequestMethod.GET)
public List<NamespaceDTO> getPublicAppNamespaceAllNamespaces(@PathVariable String env,
@PathVariable String publicNamespaceName,
@RequestParam(name = "page", defaultValue = "0") int page,
@RequestParam(name = "size", defaultValue = "10") int size) {
return namespaceService.getPublicAppNamespaceAllNamespaces(Env.fromString(env), publicNamespaceName, page, size);
}
private void assignNamespaceRoleToOperator(String appId, String namespaceName) {
//default assign modify、release namespace role to namespace creator
String operator = userInfoHolder.getUser().getUserId();
rolePermissionService
.assignRoleToUsers(RoleUtils.buildNamespaceRoleName(appId, namespaceName, RoleType.MODIFY_NAMESPACE),
Sets.newHashSet(operator), operator);
rolePermissionService
.assignRoleToUsers(RoleUtils.buildNamespaceRoleName(appId, namespaceName, RoleType.RELEASE_NAMESPACE),
Sets.newHashSet(operator), operator);
}
}