package qunar.tc.qconfig.admin.web.controller; import com.fasterxml.jackson.databind.JsonNode; import com.google.common.base.Preconditions; import com.google.common.base.Strings; import com.google.common.collect.Maps; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; 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.ResponseBody; import qunar.tc.qconfig.admin.cloud.controller.FileController; import qunar.tc.qconfig.admin.cloud.controller.ReferenceFileController; import qunar.tc.qconfig.admin.dao.ClientLogDao; import qunar.tc.qconfig.admin.dao.ReferenceDao; import qunar.tc.qconfig.admin.dto.CopyToDTO; import qunar.tc.qconfig.admin.model.ConfigInfo; import qunar.tc.qconfig.admin.model.Conflict; import qunar.tc.qconfig.admin.model.DbEnv; import qunar.tc.qconfig.admin.model.ProfileInfo; import qunar.tc.qconfig.admin.model.Reference; import qunar.tc.qconfig.admin.service.AdminOperateService; import qunar.tc.qconfig.admin.service.ApplyQueueService; import qunar.tc.qconfig.admin.service.CheckEnvConflictService; import qunar.tc.qconfig.admin.service.ListeningClientsService; import qunar.tc.qconfig.admin.service.ProfileService; import qunar.tc.qconfig.admin.service.UserContextService; import qunar.tc.qconfig.admin.web.security.ApplicationManager; import qunar.tc.qconfig.common.bean.JsonV2; import qunar.tc.qconfig.common.bean.StatusType; import qunar.tc.qconfig.common.support.Application; import qunar.tc.qconfig.common.util.ProfileUtil; import qunar.tc.qconfig.common.util.RefType; import qunar.tc.qconfig.servercommon.bean.ConfigMeta; import javax.annotation.Resource; import java.util.AbstractMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; /** * @author zhenyu.nie created on 2016 2016/5/31 22:02 */ @Controller @RequestMapping("/admin") public class AdminController extends AbstractControllerHelper { protected static final Logger logger = LoggerFactory.getLogger(AdminController.class); private AtomicBoolean deleteStatus = new AtomicBoolean(false); private Long deletedCount = 0l; @Autowired private ClientLogDao clientLogDao; @Resource private FileController fileController; @Resource private FileController configController; @Resource private ReferenceFileController referenceFileController; @Resource private ApplyQueueService applyQueueService; @Resource private CheckEnvConflictService checkEnvConflictService; @Resource private ProfileService profileService; @Resource private ReferenceDao referenceDao; @Resource private ListeningClientsService listeningClientsService; @Resource private ApplicationManager applicationManager; @Resource private UserContextService userContext; @Resource private AdminOperateService adminOperateService; @RequestMapping("/home") public String index() { return "admin/index"; } @RequestMapping("/deleteFile") public String deleteFile() { return "admin/deleteFile"; } @RequestMapping("/deleteBuildGroup") public String deleteBuildGroup() { return "admin/deleteBuildGroup"; } @RequestMapping("/deleteReference") public String deleteReference() { return "admin/deleteReference"; } @RequestMapping("/deletePublic") public String deletePublic() { return "admin/deletePublic"; } @RequestMapping(value = "/deleteServer", method = RequestMethod.GET) @ResponseBody public Object deleteServer(@RequestParam String ip, @RequestParam String env) { int result = 0; if (!Strings.isNullOrEmpty(ip) && !Strings.isNullOrEmpty(env)) { result = adminOperateService.deleteServer(ip); } return JsonV2.successOf(result); } @RequestMapping("/deleteclientlogs") @ResponseBody public Object deletecClientLogs(@RequestParam String endTime) { Preconditions.checkArgument(!Strings.isNullOrEmpty(endTime), "结束时间不能为空!"); logger.info("delete profile endTime: " + endTime); if (deleteStatus.compareAndSet(false, true)) { try { for (DbEnv env : DbEnv.values()) { while (true) { List<Long> ids = clientLogDao.selectIds(env, endTime, 1000); clientLogDao.delete(env, ids); deletedCount = deletedCount + ids.size(); if (ids.size() != 1000) { break; } } } return new JsonV2<>(0, null, "删除个数:" + deletedCount); } catch (Exception e) { logger.error("删除失败", e); return new JsonV2<>(1, e.getMessage(), null); } finally { deleteStatus.compareAndSet(true, false); deletedCount = 0l; } } else { return new JsonV2<>(0, null, "删除中: " + deletedCount); } } @RequestMapping("/referenceApp") @ResponseBody public Object referenceApp(@RequestParam String fromApp, @RequestParam String toApp) { Preconditions.checkArgument(!Strings.isNullOrEmpty(fromApp), "from app is empty"); Preconditions.checkArgument(!Strings.isNullOrEmpty(toApp), "to app is empty"); Preconditions.checkArgument(!fromApp.equals(toApp), "from app equals to app"); logger.info("copy app [{}] to [{}]", fromApp, toApp); try { Application app = applicationManager.getAppByCode(fromApp); } catch (Exception e) { return new JsonV2<>(1, "应用" + fromApp + "不存在", null); } try { Application app = applicationManager.getAppByCode(toApp); } catch (Exception e) { return new JsonV2<>(1, "应用" + toApp + "不存在", null); } try { adminOperateService.referenceGroup(fromApp, toApp); return true; } catch (Exception e) { logger.error("copy app error, [{}] to [{}]", fromApp, toApp, e); return new JsonV2<>(-1, e.getClass().getSimpleName() + ":" + e.getMessage(), null); } } private String replaceFatProfileEnv(String srcProfile) { if (Strings.isNullOrEmpty(srcProfile)) { return ""; } int indexOfColon = srcProfile.indexOf(":"); Preconditions.checkArgument(indexOfColon > 0, "illegal profile [%s]", srcProfile); String env = srcProfile.substring(0, indexOfColon); String subEnv; if (indexOfColon == srcProfile.length() - 1) { subEnv = ""; } else { subEnv = srcProfile.substring(indexOfColon + 1); } //非fat:% 则不变(如继承自resources:) if (!env.toLowerCase().equals("fat")) { return srcProfile; } if ("lpt".equalsIgnoreCase(subEnv)) { return "lpt:"; } return "lpt:" + subEnv; } @RequestMapping("/batchCopyFile") @ResponseBody public Object batchCopyFile(@RequestBody JsonNode rootNode, @RequestParam String fileType) { Preconditions.checkArgument("common".equals(fileType) || "reference".equals(fileType) || "inherit".equals(fileType), "fileType无效"); JsonNode groupsArrayNode = rootNode.get("groups"); Preconditions.checkArgument(groupsArrayNode.isArray(), "groups字段必须是array"); Iterator<JsonNode> it = groupsArrayNode.iterator(); Map<String, Map<String, String>> allGroupResult = Maps.newHashMap(); while (it.hasNext()) { JsonNode groupNode = it.next(); String group = groupNode.get("group").asText(); String srcProfile = groupNode.get("srcProfile").asText(); String destProfile = groupNode.get("destProfile").asText(); createSubEnv(group, destProfile); Map<String, String> groupResult = batchCopyWithPublish(group, srcProfile, destProfile, fileType); allGroupResult.put(group + "/" + srcProfile, groupResult); } return allGroupResult; } @RequestMapping("listeningClients") @ResponseBody public Object getListeningClients(@RequestParam String group, @RequestParam String profile, @RequestParam String dataId, @RequestParam(defaultValue = "10") int timeout) { ConfigMeta meta = new ConfigMeta(group, dataId, profile); try { return listeningClientsService.getListeningClients(meta).get(timeout, TimeUnit.SECONDS); } catch (Exception e) { throw new RuntimeException(e); } } private boolean createSubEnv(String group, String profile) { if (!profileService.exist(group, profile)) { profileService.create(group, profile); } return true; } private Map<String, String> batchCopyWithPublish(String group, String srcProfile, String destProfile, String fileType) { ProfileInfo profileInfo = (ProfileInfo) fileController.getProfileInfo(group, srcProfile, null, null); Map<String, String> totalResult = Maps.newHashMap(); if (!profileExist(group, srcProfile)) { totalResult.put("error", "profile: [" + srcProfile + "]不存在"); return totalResult; } if (!profileExist(group, destProfile)) { totalResult.put("error", "profile: [" + destProfile + "]不存在"); return totalResult; } for (ConfigInfo configInfo : profileInfo.getPublishedDatas()) { ConfigMeta srcMeta = configInfo.getConfigMeta(); //todo inherit file ConfigMeta refInheritConfigMeta = getInheritConfigMeta(configInfo); if (refInheritConfigMeta == null && "common".equals(fileType)) { Map.Entry<String, String> result = copyFileTo(srcMeta, destProfile, null); totalResult.put(result.getKey(), result.getValue()); } if (refInheritConfigMeta != null && "inherit".equals(fileType)) { Map.Entry<String, String> result = copyFileTo(srcMeta, destProfile, refInheritConfigMeta); totalResult.put(result.getKey(), result.getValue()); } } if ("reference".equals(fileType)) { for (ConfigInfo configInfo : profileInfo.getReferenceDatas()) { ConfigMeta srcMeta = configInfo.getConfigMeta(); Map.Entry<String, String> result = copyRefFileTo(srcMeta, destProfile, configInfo.getRefConfigMeta()); totalResult.put(result.getKey(), result.getValue()); } } return totalResult; } private Map.Entry<String, String> copyRefFileTo(ConfigMeta srcMeta, String destProfile, ConfigMeta refMeta) { Reference reference = new Reference(); reference.setGroup(srcMeta.getGroup()); reference.setAlias(srcMeta.getDataId()); reference.setProfile(destProfile); reference.setRefGroup(refMeta.getGroup()); reference.setRefProfile(refMeta.getProfile()); reference.setRefDataId(refMeta.getDataId()); try { referenceFileController.reference(reference); return new AbstractMap.SimpleEntry<>(srcMeta.getDataId(), "copy reference OK"); } catch (Exception e) { return new AbstractMap.SimpleEntry<>(srcMeta.getDataId(), "copy reference error: " + e.getMessage()); } } private Map.Entry<String, String> copyFileTo(ConfigMeta srcMeta, String destProfile, ConfigMeta refInheritMeta) { String sGroup = srcMeta.getGroup(); String sProfile = srcMeta.getProfile(); String sDataId = srcMeta.getDataId(); CopyToDTO copyToDTO = new CopyToDTO(); copyToDTO.setGroup(sGroup); copyToDTO.setSrc(sProfile); copyToDTO.setDataId(sDataId); copyToDTO.setProfile(destProfile); ConfigMeta destMeta = new ConfigMeta(sGroup, sDataId, destProfile); StringBuilder stringBuilder = new StringBuilder(); try { if (isFileExists(destMeta)) { return new AbstractMap.SimpleEntry<>(sDataId, "该文件已存在"); } stringBuilder.append("copy:").append(configController.copyTo(copyToDTO)).append(", "); stringBuilder.append("approve&publish:").append(applyQueueService.approveAndPublish(destMeta)).append(", "); // 处理继承文件 if (refInheritMeta != null) { Reference reference = new Reference(); reference.setType(RefType.INHERIT.value()); reference.setProfile(destProfile); reference.setAlias(sDataId); reference.setGroup(sGroup); reference.setOperator("system"); reference.setRefDataId(refInheritMeta.getDataId()); reference.setRefGroup(refInheritMeta.getGroup()); String destRefProfile = replaceFatProfileEnv(refInheritMeta.getProfile()); reference.setRefProfile(destRefProfile); referenceDao.create(reference); stringBuilder.append("add inherit ref, "); } stringBuilder.append("OK"); } catch (Exception e) { logger.info("copy error: {}", copyToDTO, e); stringBuilder.append("error: ").append(e.getMessage()); } return new AbstractMap.SimpleEntry<>(sDataId, stringBuilder.toString()); } private boolean isFileExists(ConfigMeta meta) { Optional<Conflict> conflictOptional = checkEnvConflictService.getConflict(meta); if (!conflictOptional.isPresent()) { return false; } Conflict conflict = conflictOptional.get(); return conflict.getType() != Conflict.Type.EXIST || conflict.getCandidate().getStatus() != StatusType.DELETE; } private boolean profileExist(String group, String profile) { String buildGroup = ProfileUtil.getBuildGroup(profile); return Strings.isNullOrEmpty(buildGroup) || profileService.exist(group, profile); } private ConfigMeta getInheritConfigMeta(ConfigInfo configInfo) { if (configInfo.getRefType() != RefType.INHERIT) { return null; } return configInfo.getRefConfigMeta(); } }