Create & Init Project...

This commit is contained in:
2019-04-22 18:49:16 +08:00
commit fc4fa37393
25440 changed files with 4054998 additions and 0 deletions

View File

@@ -0,0 +1,75 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
"go_test",
)
go_library(
name = "go_default_library",
srcs = [
"audit.go",
"bilihub.go",
"cluster.go",
"dashboard.go",
"devicefarm.go",
"image.go",
"machine.go",
"machineV2.go",
"machine_log.go",
"mail.go",
"node.go",
"service.go",
"task.go",
"tree.go",
"user.go",
],
importpath = "go-common/app/admin/ep/merlin/service",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/admin/ep/merlin/conf:go_default_library",
"//app/admin/ep/merlin/dao:go_default_library",
"//app/admin/ep/merlin/model:go_default_library",
"//library/ecode:go_default_library",
"//library/log:go_default_library",
"//library/sync/pipeline/fanout:go_default_library",
"//vendor/github.com/robfig/cron:go_default_library",
"//vendor/github.com/satori/go.uuid:go_default_library",
"//vendor/gopkg.in/gomail.v2:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)
go_test(
name = "go_default_test",
srcs = [
"machine_test.go",
"service_test.go",
"tree_test.go",
"user_test.go",
],
embed = [":go_default_library"],
rundir = ".",
tags = ["automanaged"],
deps = [
"//app/admin/ep/merlin/conf:go_default_library",
"//app/admin/ep/merlin/model:go_default_library",
"//vendor/github.com/smartystreets/goconvey/convey:go_default_library",
"//vendor/gopkg.in/h2non/gock.v1:go_default_library",
],
)

View File

@@ -0,0 +1,247 @@
package service
import (
"context"
"time"
"go-common/app/admin/ep/merlin/model"
"go-common/library/ecode"
)
// DelayMachineEndTime Delay Machine End Time.
func (s *Service) DelayMachineEndTime(c context.Context, machineID int64, username string) (status int, err error) {
var (
machine *model.Machine
machineLog = &model.MachineLog{}
)
status = -1
//查询机器延期状态
if machine, err = s.dao.QueryMachine(machineID); err != nil {
return
}
//机器必须存在机器状态必须是有效的延期状态必须为可自动延期结束时间必须是前后7天之内
if machine == nil || machine.DelayStatus != model.DelayStatusAuto || machine.Status < 0 || machine.EndTime.Before(time.Now().AddDate(0, 0, -8)) || machine.EndTime.After(time.Now().AddDate(0, 0, 8)) {
err = ecode.MerlinDelayMachineErr
return
}
//执行延期操作
if err = s.dao.UpdateMachineEndTime(machineID, model.DelayStatusApply, machine.EndTime.AddDate(0, 0, 30)); err != nil {
machineLog.OperateResult = model.OperationFailedForMachineLog
} else {
machineLog.OperateResult = model.OperationSuccessForMachineLog
status = 0
}
//记录操作日志
machineLog.OperateType = model.DelayMachineEndTime
machineLog.Username = username
machineLog.MachineID = machineID
err = s.dao.InsertMachineLog(machineLog)
return
}
// ApplyDelayMachineEndTime Apply Delay Machine End Time.
func (s *Service) ApplyDelayMachineEndTime(c context.Context, username string, applyEndTime *model.ApplyEndTimeRequest) (status int, err error) {
var (
machine *model.Machine
machineLog = &model.MachineLog{}
)
status = -1
//查询机器延期状态
if machine, err = s.dao.QueryMachine(applyEndTime.MachineID); err != nil {
return
}
//申请延期时,机器必须手动申请延期过,且机器状态为正常
if machine == nil || machine.DelayStatus != model.DelayStatusApply || machine.Status < 0 {
err = ecode.MerlinApplyMachineErr
return
}
machineLog.OperateResult = model.OperationFailedForMachineLog
var applyET time.Time
if applyET, err = time.ParseInLocation(model.TimeFormat, applyEndTime.ApplyEndTime, time.Local); err != nil {
return
}
//延长日期超过过期时间三个月或小于当前过期时间,驳回
if machine.EndTime.AddDate(0, 3, 1).Before(applyET) || machine.EndTime.After(applyET) {
err = ecode.MerlinApplyMachineByApplyEndTimeMore3MErr
return
}
ar := &model.ApplicationRecord{
Applicant: username,
MachineID: machine.ID,
ApplyEndTime: applyET,
Status: model.ApplyDelayInit,
Auditor: applyEndTime.Auditor,
}
//添加延期申请记录
if err = s.dao.InsertApplicationRecordAndUpdateMachineDelayStatus(ar, machine.ID, model.DelayStatusDisable); err == nil {
machineLog.OperateResult = model.OperationSuccessForMachineLog
status = 0
//发送邮件通知
s.SendMailApplyDelayMachineEndTime(c, applyEndTime.Auditor, username, machine.ID, machine.EndTime, applyET)
}
//记录操作日志
machineLog.OperateType = model.DelayMachineEndTime
machineLog.Username = username
machineLog.MachineID = machine.ID
if err = s.dao.InsertMachineLog(machineLog); err != nil {
return
}
return
}
// CancelMachineEndTime Cancel Machine End Time.
func (s *Service) CancelMachineEndTime(c context.Context, auditID int64, username string) (status int, err error) {
var (
applicationRecord *model.ApplicationRecord
machineLog = &model.MachineLog{}
machine *model.Machine
)
status = -1
if applicationRecord, err = s.dao.FindApplicationRecordsByID(auditID); err != nil {
return
}
//查询机器延期状态
if machine, err = s.dao.QueryMachine(applicationRecord.MachineID); err != nil {
return
}
//取消延期时,审批状态应为 ApplyDelayInit且用户名是当时申请延期名,机器状态为正常
if applicationRecord == nil || applicationRecord.Status != model.ApplyDelayInit || applicationRecord.Applicant != username || machine.Status < 0 {
err = ecode.MerlinCancelMachineErr
return
}
machineLog.OperateResult = model.OperationFailedForMachineLog
if err = s.dao.UpdateAuditStatusAndUpdateMachineDelayStatus(applicationRecord.MachineID, auditID, model.DelayStatusApply, model.ApplyDelayCancel); err == nil {
machineLog.OperateResult = model.OperationSuccessForMachineLog
status = 0
}
//记录操作日志
machineLog.OperateType = model.CancelDelayMachineEndTime
machineLog.Username = username
machineLog.MachineID = applicationRecord.MachineID
if err = s.dao.InsertMachineLog(machineLog); err != nil {
return
}
return
}
// AuditMachineEndTime Audit Machine End Time.
func (s *Service) AuditMachineEndTime(c context.Context, auditID int64, username string, auditResult bool, comment string) (status int, err error) {
var (
applicationRecord *model.ApplicationRecord
machineLog = &model.MachineLog{}
machine *model.Machine
)
status = -1
if applicationRecord, err = s.dao.FindApplicationRecordsByID(auditID); err != nil {
return
}
//查询机器延期状态
if machine, err = s.dao.QueryMachine(applicationRecord.MachineID); err != nil {
return
}
//审核时延期状态必须为ApplyDelayInit同时用户名为审核者名一致,机器状态为正常
if applicationRecord == nil || applicationRecord.Status != model.ApplyDelayInit || applicationRecord.Auditor != username || machine.Status < 0 {
err = ecode.MerlinAuditMachineErr
return
}
machineLog.OperateResult = model.OperationFailedForMachineLog
if auditResult {
//批准 //修改机器状态和延期时间,审批状态
err = s.dao.UpdateAuditStatusAndUpdateMachineEndTime(applicationRecord.MachineID, auditID, model.DelayStatusApply, model.ApplyDelayApprove, applicationRecord.ApplyEndTime, comment)
} else {
//驳回 //修改机器状态,审批状态
err = s.dao.UpdateAuditStatusAndUpdateMachineDelayStatusComment(applicationRecord.MachineID, auditID, model.DelayStatusApply, model.ApplyDelayDecline, comment)
}
//发送邮件通知
if err == nil {
machineLog.OperateResult = model.OperationSuccessForMachineLog
status = 0
s.SendMailAuditResult(c, applicationRecord.Auditor, applicationRecord.Applicant, applicationRecord.MachineID, auditResult)
}
//记录操作日志
machineLog.OperateType = model.AuditDelayMachineEndTime
machineLog.Username = username
machineLog.MachineID = applicationRecord.MachineID
if err = s.dao.InsertMachineLog(machineLog); err != nil {
return
}
return
}
// GetApplicationRecordsByApplicant Get Application Records By Applicant.
func (s *Service) GetApplicationRecordsByApplicant(c context.Context, username string, pn, ps int) (p *model.PaginateApplicationRecord, err error) {
var (
total int64
applicationRecords []*model.ApplicationRecord
)
if total, applicationRecords, err = s.dao.FindApplicationRecordsByApplicant(username, pn, ps); err != nil {
return
}
p = &model.PaginateApplicationRecord{
PageNum: pn,
PageSize: ps,
Total: total,
ApplicationRecords: applicationRecords,
}
return
}
// GetApplicationRecordsByAuditor Get Application Records By Auditor.
func (s *Service) GetApplicationRecordsByAuditor(c context.Context, username string, pn, ps int) (p *model.PaginateApplicationRecord, err error) {
var (
total int64
applicationRecords []*model.ApplicationRecord
)
if total, applicationRecords, err = s.dao.FindApplicationRecordsByAuditor(username, pn, ps); err != nil {
return
}
p = &model.PaginateApplicationRecord{
PageNum: pn,
PageSize: ps,
Total: total,
ApplicationRecords: applicationRecords,
}
return
}
// GetApplicationRecordsByMachineID Get Application Records By MachineID.
func (s *Service) GetApplicationRecordsByMachineID(c context.Context, machineID int64, pn, ps int) (p *model.PaginateApplicationRecord, err error) {
var (
total int64
applicationRecords []*model.ApplicationRecord
)
if total, applicationRecords, err = s.dao.FindApplicationRecordsByMachineID(machineID, pn, ps); err != nil {
return
}
p = &model.PaginateApplicationRecord{
PageNum: pn,
PageSize: ps,
Total: total,
ApplicationRecords: applicationRecords,
}
return
}

View File

@@ -0,0 +1,667 @@
package service
import (
"context"
"encoding/json"
"strconv"
"strings"
"time"
"go-common/app/admin/ep/merlin/model"
"go-common/library/ecode"
"go-common/library/log"
)
// AuthHub Auth Hub.
func (s *Service) AuthHub(c context.Context, sessionID string) (err error) {
return s.dao.AuthHub(c, sessionID)
}
// AccessAuthHub Access Auth Hub.
func (s *Service) AccessAuthHub(c context.Context, username string) (accessHub bool, err error) {
var (
personProjectID int
)
if personProjectID, err = s.ProjectID(c, username); err != nil {
return
}
if personProjectID > 0 {
accessHub = true
}
return
}
// ProjectID Get Project ID.
func (s *Service) ProjectID(c context.Context, projectName string) (projectID int, err error) {
var hubProjects []*model.HubProject
if hubProjects, err = s.dao.HubProjects(c, projectName); err != nil {
return
}
for _, hubProject := range hubProjects {
if projectName == hubProject.Name {
projectID = hubProject.ProjectID
return
}
}
return
}
// ProjectRepositories Get Project Repositories.
func (s *Service) ProjectRepositories(c context.Context, projectID, pn, ps int, keyWord string) (p *model.PaginateProjectRepoRecord, err error) {
var (
total int
projectRepos []*model.ProjectRepository
)
if total, err = s.dao.ProjectRepoCount(c, projectID); err != nil {
return
}
if projectRepos, err = s.dao.ProjectRepos(c, projectID, pn, ps, keyWord); err != nil {
return
}
p = &model.PaginateProjectRepoRecord{
PageNum: pn,
PageSize: ps,
Total: total,
ProjectRepository: projectRepos,
}
return
}
// AccessPullProjects Access Pull Projects.
func (s *Service) AccessPullProjects(c context.Context, username string) (projectsName []string, err error) {
var accessToHub bool
if accessToHub, err = s.AccessAuthHub(c, username); err != nil {
return
}
if accessToHub {
projectsName = append(projectsName, s.c.BiliHub.HostName+"/"+username)
}
projectsName = append(projectsName, s.c.BiliHub.HostName+"/"+s.c.BiliHub.SharePub)
return
}
// Projects Get Projects.
func (s *Service) Projects(c context.Context, username string) (projects map[string]int, err error) {
var (
personProjectID int
merlinProjectID int
publicProjectID int
)
projects = make(map[string]int)
if personProjectID, err = s.ProjectID(c, username); err != nil {
return
}
projects[username] = personProjectID
if merlinProjectID, err = s.ProjectID(c, s.c.BiliHub.MerlinPub); err != nil {
return
}
projects[s.c.BiliHub.MerlinPub] = merlinProjectID
if publicProjectID, err = s.ProjectID(c, s.c.BiliHub.SharePub); err != nil {
return
}
projects[s.c.BiliHub.SharePub] = publicProjectID
return
}
// RepositoryTags Get Repository Tags.
func (s *Service) RepositoryTags(c context.Context, repoName string) (repoTags []*model.RepositoryTag, err error) {
var (
repoTagsRet []*model.RepositoryTagResponse
imageFullNamePrefix = s.c.BiliHub.HostName + "/" + repoName + ":"
)
if repoTagsRet, err = s.dao.RepoTags(c, repoName); err != nil {
return
}
for _, repoTagRet := range repoTagsRet {
// ignore image that size is 0
if repoTagRet.Size == 0 {
continue
}
repoTag := &model.RepositoryTag{
RepositoryTagResponse: *repoTagRet,
ImageFullName: imageFullNamePrefix + repoTagRet.Name,
}
repoTags = append(repoTags, repoTag)
}
return
}
// DeleteRepositoryTag Delete Repository Tag.
func (s *Service) DeleteRepositoryTag(c context.Context, username, repoName, tagName string) (status int, err error) {
var hasRight bool
if hasRight = s.hasOperateHubRight(username, repoName); !hasRight {
status = -1
err = ecode.MerlinHubNoRight
return
}
if err = s.dao.DeleteRepoTag(c, repoName, tagName); err != nil {
status = -1
}
return
}
// DeleteRepository Delete Repository.
func (s *Service) DeleteRepository(c context.Context, username, repoName string) (status int, err error) {
var hasRight bool
if hasRight = s.hasOperateHubRight(username, repoName); !hasRight {
status = -1
err = ecode.MerlinHubNoRight
return
}
if err = s.dao.DeleteRepo(c, repoName); err != nil {
status = -1
}
return
}
// AddRepositoryTag Add Repository Tag.
func (s *Service) AddRepositoryTag(c context.Context, username, repoName, tagName, newRepoName, newTagName string) (status int, err error) {
//操作src image 权限认证
var hasRight bool
if hasRight = s.hasOperateHubRight(username, repoName); !hasRight {
status = -1
err = ecode.MerlinHubNoRight
return
}
//上传的src image权限认证。如果上传的路径 只能为公共路径和个人路径
if strings.Split(newRepoName, "/")[0] != s.c.BiliHub.SharePub {
if hasRight = s.hasOperateHubRight(username, newRepoName); !hasRight {
status = -1
err = ecode.MerlinHubNoRight
return
}
}
imageSrcName := s.getFullRepoName(repoName, tagName)
imageTagName := s.getFullRepoName(newRepoName, newTagName)
// pull and push image
s.dao.ImageTask(func() {
s.PullAndPush(username, imageSrcName, imageTagName, 0)
})
return
}
// AddTagToMachine Add Tag To Machine.
func (s *Service) AddTagToMachine(c context.Context, username, imageSrcName string, machineIDs []int64) (machineImageMap map[int64]string, err error) {
machineImageMap = map[int64]string{}
for _, machineID := range machineIDs {
repoName := strings.Split(imageSrcName, ":")[0]
absRepoName := strings.Replace(repoName, "/", "-", -1)
newRepoName := s.c.BiliHub.MachineTagPri + "/" + absRepoName
newTagName := strconv.FormatInt(machineID, 10) + "-" + time.Now().Format("20060102150405")
imageTagName := s.getFullRepoName(newRepoName, newTagName)
if _, err = s.PullAndPush(username, imageSrcName, imageTagName, machineID); err != nil {
return
}
machineImageMap[machineID] = imageTagName
}
return
}
// GetAllImagesInDocker Get All Images In Docker.
func (s *Service) GetAllImagesInDocker() (imageNames []string, err error) {
return s.dao.ImageGetAll()
}
// hasOperateHubRight has Operate HubRight.
func (s *Service) hasOperateHubRight(username, repoName string) (hasRight bool) {
//判断是否又权限执行操作
if username == strings.Split(repoName, "/")[0] {
hasRight = true
return
}
for _, super := range s.c.BiliHub.SuperOwner {
if username == super {
hasRight = true
return
}
}
return
}
// get Full RepoName getFullRepoName.
func (s *Service) getFullRepoName(repoName, tagName string) string {
return s.c.BiliHub.HostName + "/" + repoName + ":" + tagName
}
//Push Push.
func (s *Service) Push(c context.Context, username, repoName, tagName string, machineID int64) (status int, err error) {
imageSrcName := s.getFullRepoName(repoName, tagName)
log.Info("start Push target %s", imageSrcName)
if err = s.dao.ImagePush(imageSrcName); err != nil {
status = model.ImagePushErr
log.Error("ImagePush target %s,err (%+v)", imageSrcName, err)
}
hubImageLog := &model.HubImageLog{
UserName: username,
MachineID: machineID,
ImageSrc: "",
ImageTag: imageSrcName,
Status: status,
OperateType: model.ImagePush,
}
s.dao.InsertHubImageLog(hubImageLog)
log.Info("end Push target %s", imageSrcName)
return
}
// ReTag ReTag.
func (s *Service) ReTag(c context.Context, username, repoName, tagName, newRepoName, newTagName string, machineID int64) (status int, err error) {
imageSrcName := s.getFullRepoName(repoName, tagName)
imageTagName := s.getFullRepoName(newRepoName, newTagName)
log.Info("start ReTag source %s tag %s", imageSrcName, imageTagName)
if err = s.dao.ImageTag(imageSrcName, imageTagName); err != nil {
status = model.ImageReTagErr
log.Error("ImageTag source %s, target %s,err (%+v)", imageSrcName, imageTagName, err)
}
hubImageLog := &model.HubImageLog{
UserName: username,
MachineID: machineID,
ImageSrc: imageSrcName,
ImageTag: imageTagName,
Status: status,
OperateType: model.ImageTag,
}
s.dao.InsertHubImageLog(hubImageLog)
log.Info("end ReTag source %s, tag %s", imageSrcName, imageTagName)
return
}
// Pull Pull.
func (s *Service) Pull(c context.Context, username, repoName, tagName string, machineID int64) (status int, err error) {
imageSrcName := s.getFullRepoName(repoName, tagName)
log.Info("start Pull source %s", imageSrcName)
if err = s.dao.ImagePull(imageSrcName); err != nil {
status = model.ImagePullErr
log.Error("ImagePull source %s,err (%+v)", imageSrcName, err)
}
hubImageLog := &model.HubImageLog{
UserName: username,
MachineID: machineID,
ImageSrc: imageSrcName,
ImageTag: "",
Status: status,
OperateType: model.ImagePull,
}
s.dao.InsertHubImageLog(hubImageLog)
log.Info("end Pull source %s", imageSrcName)
return
}
// CreateSnapShot CreateSnapShot.
func (s *Service) CreateSnapShot(c context.Context, username string, machineID int64) (status int, err error) {
status = -1
//获取镜像名称
var (
machine *model.Machine
passMachineDetail *model.PaasMachineDetail
pqadmr *model.PaasQueryAndDelMachineRequest
tmpSnapshotRecord *model.SnapshotRecord
)
if machine, err = s.dao.QueryMachine(machineID); err != nil {
return
}
pqadmr = machine.ToPaasQueryAndDelMachineRequest()
if passMachineDetail, err = s.dao.QueryPaasMachine(c, pqadmr); err != nil {
return
}
snapshotRecord := &model.SnapshotRecord{
MachineID: machineID,
ImageName: passMachineDetail.Image,
Username: username,
Status: model.SnapshotInit,
}
if tmpSnapshotRecord, err = s.dao.FindSnapshotRecord(machineID); err != nil {
return
}
if tmpSnapshotRecord.ID == 0 {
//首次快照 插入
if err = s.dao.InsertSnapshotRecord(snapshotRecord); err != nil {
return
}
} else if tmpSnapshotRecord.Status == model.SnapshotDoing {
//有快照记录,查看是否正在进行中
err = ecode.MerlinSnapshotInDoingErr
return
} else if err = s.dao.UpdateSnapshotRecord(snapshotRecord); err != nil {
return
}
//创建快照
resultStatus := model.SnapshotDoing
if _, err = s.dao.SnapshotPaasMachineStatus(c, pqadmr); err != nil {
resultStatus = model.SnapShotFailed
}
if err = s.dao.UpdateSnapshotRecordStatus(machineID, resultStatus); err != nil {
return
}
status = 0
return
}
// QuerySnapShot Query SnapShot.
func (s *Service) QuerySnapShot(c context.Context, machineID int64) (snapshotRecord *model.SnapshotRecord, err error) {
return s.dao.FindSnapshotRecord(machineID)
}
// QueryMachine2ImageLog Query Machine to ImageLog.
func (s *Service) QueryMachine2ImageLog(c context.Context, queryRequest *model.QueryMachine2ImageLogRequest) (p *model.PaginateHubImageLog, err error) {
var (
hubImageLogs []*model.HubImageLog
total int64
)
if total, hubImageLogs, err = s.dao.FindHubMachine2ImageLog(queryRequest); err != nil {
return
}
p = &model.PaginateHubImageLog{
PageNum: queryRequest.PageNum,
PageSize: queryRequest.PageSize,
Total: total,
HubImageLogs: hubImageLogs,
}
return
}
// CallBackSnapShot Call Back SnapShot.
func (s *Service) CallBackSnapShot(c context.Context, machineName, imageName, msg string, resultStatus bool) (err error) {
var (
machine *model.Machine
snapshotResultStatus string
)
if machine, err = s.dao.QueryOnlineMachineByName(machineName); err != nil {
return
}
if resultStatus {
snapshotResultStatus = model.SnapshotSuccess
} else {
snapshotResultStatus = model.SnapShotFailed
}
err = s.dao.UpdateSnapshotRecordStatus(machine.ID, snapshotResultStatus)
return
}
// Machine2Image Machine to Image.
func (s *Service) Machine2Image(c context.Context, username, imageName, newImageName string, machineID int64) (err error) {
var (
accessToHub bool
hubImageLog *model.HubImageLog
hubImageLogs []*model.HubImageLog
machine *model.Machine
passMachineDetail *model.PaasMachineDetail
)
//判断镜像和机器是否一致
if machine, err = s.dao.QueryMachine(machineID); err != nil {
return
}
if passMachineDetail, err = s.dao.QueryPaasMachine(c, machine.ToPaasQueryAndDelMachineRequest()); err != nil {
return
}
if passMachineDetail.Image != imageName {
err = ecode.MerlinMachineImageNotSameErr
return
}
//判断有无授权hub
if accessToHub, err = s.AccessAuthHub(c, username); err != nil {
return
}
if !accessToHub {
err = ecode.MerlinHubNoRight
return
}
//判断new image name是否重名
if hubImageLog, err = s.dao.FindHubImageLogByImageTag(newImageName); err != nil {
return
}
if hubImageLog.ID > 0 {
err = ecode.MerlinDuplicateImageNameErr
return
}
//判断该机器是否有正在进行的机器转镜像任务
if hubImageLogs, err = s.dao.FindHubImageLogByMachineID(machineID); err != nil {
return
}
for _, hil := range hubImageLogs {
if hil.OperateType == model.ImageMachine2Image && hil.Status == model.ImageInit {
err = ecode.MerlinMachine2ImageInDoingErr
return
}
}
status := model.ImageInit
newHubImageLog := &model.HubImageLog{
UserName: username,
MachineID: machineID,
ImageSrc: imageName,
ImageTag: newImageName,
Status: status,
OperateType: model.ImageMachine2Image,
}
if err = s.dao.InsertHubImageLog(newHubImageLog); err != nil {
return
}
s.dao.ImageTask(func() {
s.PullAndPushWithMachine2Image(username, imageName, newImageName, machineID, newHubImageLog.ID)
})
return
}
// PullAndPush pull And Push.
func (s *Service) PullAndPush(username, imageSrcName, imageTagName string, machineID int64) (status int, err error) {
log.Info("start pullAndPush source %s, target %s", imageSrcName, imageTagName)
//pull image
if err = s.dao.ImagePull(imageSrcName); err != nil {
status = model.ImagePullErr
log.Error("ImagePull source %s,err (%+v)", imageSrcName, err)
} else if err = s.dao.ImageTag(imageSrcName, imageTagName); err != nil {
status = model.ImageReTagErr
log.Error("ImageTag source %s, target %s,err (%+v)", imageSrcName, imageTagName, err)
} else if err = s.dao.ImagePush(imageTagName); err != nil {
status = model.ImagePushErr
log.Error("ImagePush target %s,err (%+v)", imageTagName, err)
}
hubImageLog := &model.HubImageLog{
UserName: username,
MachineID: machineID,
ImageSrc: imageSrcName,
ImageTag: imageTagName,
Status: status,
OperateType: model.ImagePullAndPush,
}
s.dao.InsertHubImageLog(hubImageLog)
log.Info("end pullAndPush source %s, target %s", imageSrcName, imageTagName)
return
}
// PullAndPushWithMachine2Image Pull And Push With Machine to Image.
func (s *Service) PullAndPushWithMachine2Image(username, imageSrcName, imageTagName string, machineID, hubImageLogID int64) (status int, err error) {
log.Info("start PullAndPushWithMachine2Image source %s, target %s", imageSrcName, imageTagName)
status = model.ImageSuccess
//pull image
if err = s.dao.ImagePull(imageSrcName); err != nil {
status = model.ImagePullErr
log.Error("ImagePull source %s,err (%+v)", imageSrcName, err)
} else if err = s.dao.ImageTag(imageSrcName, imageTagName); err != nil {
status = model.ImageReTagErr
log.Error("ImageTag source %s, target %s,err (%+v)", imageSrcName, imageTagName, err)
} else if err = s.dao.ImagePush(imageTagName); err != nil {
status = model.ImagePushErr
log.Error("ImagePush target %s,err (%+v)", imageTagName, err)
}
err = s.dao.UpdateHubImageLogStatus(hubImageLogID, status)
log.Info("end PullAndPushWithMachine2Image source %s, target %s", imageSrcName, imageTagName)
return
}
// Machine2ImageForceFailed Machine to Image Force Failed.
func (s *Service) Machine2ImageForceFailed(c context.Context, machineID int64) (status int, err error) {
if err = s.dao.UpdateHubImageLogStatusInDoingStatus(machineID, model.ImagePullErr); err != nil {
status = -1
}
return
}
// UpdateImageConf Update Image Conf.
func (s *Service) UpdateImageConf(c context.Context, username string, ic *model.ImageConfiguration) (status int, err error) {
var (
hubImageConf *model.HubImageConf
hasRight bool
envsJson []byte
hostsJson []byte
)
status = -1
// 超级用户才能改MerlinPub 配置模板
ret := strings.Split(ic.ImageFullName, "/")
if len(ret) > 1 && ret[1] == s.c.BiliHub.MerlinPub {
for _, super := range s.c.BiliHub.SuperOwner {
if username == super {
hasRight = true
continue
}
}
if !hasRight {
err = ecode.MerlinHubNoRight
return
}
}
if envsJson, err = json.Marshal(ic.Envs); err != nil {
return
}
if hostsJson, err = json.Marshal(ic.HostAlias); err != nil {
return
}
if hubImageConf, err = s.dao.FindHubImageConfByImageName(ic.ImageFullName); err != nil {
return
}
newHubImageConf := &model.HubImageConf{
ImageName: ic.ImageFullName,
UpdateBy: username,
Command: strings.TrimSpace(ic.Command),
Envs: string(envsJson),
Hosts: string(hostsJson),
}
if hubImageConf.ID == 0 {
if err = s.dao.InsertHubImageConf(newHubImageConf); err != nil {
return
}
} else {
if err = s.dao.UpdateHubImageConf(newHubImageConf); err != nil {
return
}
}
status = 0
return
}
// QueryImageConf Query Image Conf.
func (s *Service) QueryImageConf(c context.Context, imageName string) (ic *model.ImageConfiguration, err error) {
var (
hubImageConf *model.HubImageConf
envs []*model.EnvVariable
hostAlias []*model.Host
)
if hubImageConf, err = s.dao.FindHubImageConfByImageName(imageName); err != nil || hubImageConf.ID == 0 {
return
}
if err = json.Unmarshal([]byte(hubImageConf.Envs), &envs); err != nil {
return
}
if err = json.Unmarshal([]byte(hubImageConf.Hosts), &hostAlias); err != nil {
return
}
ic = &model.ImageConfiguration{
ImageFullName: imageName,
PaasMachineSystem: model.PaasMachineSystem{
Command: hubImageConf.Command,
Envs: envs,
HostAlias: hostAlias,
},
}
if len(ic.Envs) == 0 && len(ic.HostAlias) == 0 && strings.TrimSpace(ic.Command) == "" {
ic = nil
}
return
}

View File

@@ -0,0 +1,32 @@
package service
import (
"context"
"strings"
"go-common/app/admin/ep/merlin/model"
)
// QueryCluster query cluster.
func (s *Service) QueryCluster(c context.Context) (clusters []*model.Cluster, err error) {
var tmpClusters []*model.Cluster
if tmpClusters, err = s.dao.QueryClusters(c); err != nil {
return
}
for _, tmpCluster := range tmpClusters {
if !strings.Contains(tmpCluster.Name, "uat") {
clusters = append(clusters, tmpCluster)
}
}
for _, cluster := range clusters {
for _, supportName := range s.c.BiliHub.SupportNetWork {
if strings.Contains(cluster.Name, supportName) {
cluster.IsSupportSnapShot = true
continue
}
}
}
return
}

View File

@@ -0,0 +1,288 @@
package service
import (
"context"
"math"
"time"
"go-common/app/admin/ep/merlin/model"
"go-common/library/log"
)
// QueryMachineLifeCycle Query Machine Life Cycle.
func (s *Service) QueryMachineLifeCycle(c context.Context) (machineResponse map[string]interface{}, err error) {
var machineLifeCycles []*model.MachineLifeCycle
machineResponse = make(map[string]interface{})
if machineLifeCycles, err = s.dao.MachineLifeCycle(); err != nil {
return
}
machineResponse["machine_life_cycle"] = machineLifeCycles
return
}
// QueryMachineCount Query Machine Count.
func (s *Service) QueryMachineCount(c context.Context) (machineResponse *model.MachineCountGroupResponse, err error) {
var (
machinesCount []*model.MachineCountGroupByBusiness
machinesCountInRunning []*model.MachineCountGroupByBusiness
businessUnits []string
machineCntData []int
machineCntInRunData []int
)
if machinesCount, err = s.dao.MachineCountGroupByBusiness(); err != nil {
return
}
if machinesCountInRunning, err = s.dao.MachineCountGroupByBusinessInRunning(); err != nil {
return
}
for _, machineCount := range machinesCount {
var runningCount int
businessUnits = append(businessUnits, machineCount.BusinessUnit)
machineCntData = append(machineCntData, machineCount.Count)
for _, machineCountInRunning := range machinesCountInRunning {
if machineCountInRunning.BusinessUnit == machineCount.BusinessUnit {
runningCount = machineCountInRunning.Count
break
}
}
machineCntInRunData = append(machineCntInRunData, runningCount)
}
machineCountItem := &model.MachineCountGroupItem{
Type: "已创建机器数量",
Data: machineCntData,
}
machineCountInRunningItem := &model.MachineCountGroupItem{
Type: "正在运行机器数量",
Data: machineCntInRunData,
}
machineResponse = &model.MachineCountGroupResponse{
BusinessUnits: businessUnits,
Items: []*model.MachineCountGroupItem{machineCountItem, machineCountInRunningItem},
}
return
}
// QueryMachineCreatedAndEndTime Query Machine Created And End Time.
func (s *Service) QueryMachineCreatedAndEndTime(c context.Context) (machineResponse map[string]interface{}, err error) {
var (
machineCreatedTime []*model.MachineCreatedAndEndTime
machineExpiredTime []*model.MachineCreatedAndEndTime
)
machineResponse = make(map[string]interface{})
if machineCreatedTime, err = s.dao.MachineLatestCreated(); err != nil {
return
}
if machineExpiredTime, err = s.dao.MachineWillBeExpired(); err != nil {
return
}
machineResponse["machine_created_top"] = machineCreatedTime
machineResponse["machine_expired_top"] = machineExpiredTime
return
}
// QueryMachineUsage Query Machine Usage.
func (s *Service) QueryMachineUsage(c context.Context) (machineResponse map[string]interface{}, err error) {
var (
machines []*model.Machine
pmds []*model.PaasMachineDetail
pqadmrs []*model.PaasQueryAndDelMachineRequest
totalCPU float32
totalMemory float32
totalMachine int
totalMachineInRunnint int
)
machineResponse = make(map[string]interface{})
if totalMachine, err = s.dao.QueryMachineCount(); err != nil {
return
}
if machines, err = s.dao.QueryMachineInRunning(); err != nil {
log.Error("query MachineInRunning err(%v)", err)
return
}
totalMachineInRunnint = len(machines)
for _, machine := range machines {
pqadmrs = append(pqadmrs, machine.ToPaasQueryAndDelMachineRequest())
}
if pmds, err = s.dao.QueryMachineUsageSummaryFromCache(c, pqadmrs); err != nil {
return
}
for _, pmd := range pmds {
pmd.ConvertUnits()
totalCPU = totalCPU + pmd.CPULimit/model.CPURatio
totalMemory = totalMemory + pmd.MemoryLimit/model.MemoryRatio
}
machineResponse["total_machine"] = totalMachine
machineResponse["total_machine_in_running"] = totalMachineInRunnint
machineResponse["total_cpu_usage"] = totalCPU
machineResponse["total_memory_usage"] = totalMemory
return
}
// QueryMobileMachineUsageCount Query Mobile Machines Count.
func (s *Service) QueryMobileMachineUsageCount(c context.Context) (res map[string]interface{}, err error) {
var (
mobileMachinesUserUsageCount []*model.MobileMachineUserUsageCount
mobileMachinesUserLendCount []*model.MobileMachineUserUsageCount
mobileMachinesUsageCount []*model.MobileMachineUsageCount
mobileMachinesLendCount []*model.MobileMachineUsageCount
)
if mobileMachinesUserUsageCount, err = s.dao.MobileMachineUserUsageCount(); err != nil {
return
}
if mobileMachinesUserLendCount, err = s.dao.MobileMachineUserLendCount(); err != nil {
return
}
if mobileMachinesUsageCount, err = s.dao.MobileMachineUsageCount(); err != nil {
return
}
if mobileMachinesLendCount, err = s.dao.MobileMachineLendCount(); err != nil {
return
}
res = make(map[string]interface{})
res["user_usage_count"] = mobileMachinesUserUsageCount
res["user_lend_count"] = mobileMachinesUserLendCount
res["mobile_machine_usage_count"] = mobileMachinesUsageCount
res["mobile_machine_lend_count"] = mobileMachinesLendCount
return
}
// QueryMobileMachineModeCount Query Mobile Machine Mode Count.
func (s *Service) QueryMobileMachineModeCount(c context.Context) (res map[string]interface{}, err error) {
var mobileMachinesTypeCount []*model.MobileMachineTypeCount
if mobileMachinesTypeCount, err = s.dao.MobileMachineModeCount(); err != nil {
return
}
res = make(map[string]interface{})
res["mobile_machine_mode_count"] = mobileMachinesTypeCount
return
}
// QueryMobileMachineUsageTime Query Mobile Machine Usage Time.
func (s *Service) QueryMobileMachineUsageTime(c context.Context) (ret []*model.MobileMachineUsageTimeResponse, err error) {
var (
mobileMachineLogs []*model.MobileMachineLog
mobileMachineLogsMap = make(map[int64][]*model.MobileMachineLog)
)
if mobileMachineLogs, err = s.dao.MobileMachineUseRecord(); err != nil {
return
}
//按机器id 分组
for _, mobileMachineLog := range mobileMachineLogs {
mobileMachineLogsMap[mobileMachineLog.MachineID] = append(mobileMachineLogsMap[mobileMachineLog.MachineID], mobileMachineLog)
}
//按机器计算 使用时长
for machineID := range mobileMachineLogsMap {
var (
mobileMachine *model.MobileMachine
isStartTimeFound bool
isEndTimeFound bool
startTime time.Time
endTime time.Time
username string
preMobileMachinesUsageTime []*model.MobileMachineUsageTime
totalDuration float64
)
if mobileMachine, err = s.dao.FindMobileMachineByID(machineID); err != nil {
continue
}
for index, preMobileMachineLog := range mobileMachineLogsMap[machineID] {
if preMobileMachineLog.OperateType == model.MBBindLog {
startTime = preMobileMachineLog.OperateTime
username = preMobileMachineLog.Username
isStartTimeFound = true
}
if preMobileMachineLog.OperateType == model.MBReleaseLog && isStartTimeFound {
endTime = preMobileMachineLog.OperateTime
isEndTimeFound = true
}
//处理中间绑定和解绑 计算使用时间
if isStartTimeFound && isEndTimeFound {
duration := math.Trunc(endTime.Sub(startTime).Minutes()*1e2+0.5) * 1e-2
MobileMachineUsageTime := &model.MobileMachineUsageTime{
Username: username,
StartTime: startTime,
EndTime: endTime,
Duration: duration,
}
preMobileMachinesUsageTime = append(preMobileMachinesUsageTime, MobileMachineUsageTime)
isStartTimeFound = false
isEndTimeFound = false
totalDuration = totalDuration + duration
}
// 最后次记录和绑定记录且机器在相应人名下 计算实时机器使用时长
if index == (len(mobileMachineLogsMap[machineID])-1) && isStartTimeFound && !isEndTimeFound && mobileMachine.Username != "" {
timeNow := time.Now()
duration := math.Trunc(timeNow.Sub(startTime).Minutes()*1e2+0.5) * 1e-2
MobileMachineUsageTime := &model.MobileMachineUsageTime{
Username: username,
StartTime: timeNow,
EndTime: endTime,
Duration: duration,
}
preMobileMachinesUsageTime = append(preMobileMachinesUsageTime, MobileMachineUsageTime)
isStartTimeFound = false
isEndTimeFound = false
totalDuration = totalDuration + duration
}
}
mobileMachineUsageTimeResponse := &model.MobileMachineUsageTimeResponse{
MobileMachineID: machineID,
MobileMachineName: mobileMachine.Name,
ModeName: mobileMachine.Mode,
TotalDuration: totalDuration,
MobileMachinesUsageTime: preMobileMachinesUsageTime,
}
ret = append(ret, mobileMachineUsageTimeResponse)
}
return
}

View File

@@ -0,0 +1,717 @@
package service
import (
"context"
"fmt"
"time"
"go-common/app/admin/ep/merlin/model"
"go-common/library/ecode"
"go-common/library/log"
"github.com/satori/go.uuid"
)
//SyncMobileDeviceList Sync Device Farm List .
func (s *Service) SyncMobileDeviceList(c context.Context) (uid string, err error) {
uid = uuid.NewV4().String()
s.deviceChan.Do(c, func(c context.Context) {
var (
startCnt int
status int
totalCnt int
addCnt int
updateCnt int
delCnt int
err error
)
if startCnt, err = s.dao.FindMobileSyncLogStartStatus(); err != nil {
return
}
if startCnt > 0 {
return
}
mobileSyncLog := &model.MobileSyncLog{
UUID: uid,
}
s.dao.InsertMobileSyncLog(mobileSyncLog)
if totalCnt, addCnt, updateCnt, delCnt, err = s.SyncMobileDeviceListInChan(uid); err != nil {
status = -1
} else {
status = 1
}
mobileSyncLog = &model.MobileSyncLog{
UUID: uid,
TotalCnt: totalCnt,
AddCnt: addCnt,
UpdateCnt: updateCnt,
DeleteCnt: delCnt,
Status: status,
}
s.dao.UpdateMobileSyncLog(mobileSyncLog)
})
return
}
// SyncMobileDeviceListInChan Sync Mobile Device List In Chan.
func (s *Service) SyncMobileDeviceListInChan(uuid string) (totalCnt, addCnt, updateCnt, delCnt int, err error) {
var (
resTotal map[string][]*model.Device
)
if resTotal, err = s.dao.MobileDeviceList(context.Background()); err != nil {
log.Error("SyncMobileDeviceListInChan httpClient err(%+v)", err)
err = nil
}
if len(resTotal) == 0 {
return
}
for host, devices := range resTotal {
totalCnt = totalCnt + len(devices)
for _, device := range devices {
var deviceType int
var tmpMobileMachine *model.MobileMachine
if device.IsSimulator {
deviceType = model.MBSimulator
}
if tmpMobileMachine, err = s.dao.FindMobileMachineBySerial(device.Serial); err != nil {
log.Error("d.SyncMobileDeviceList FindMobileMachineBySerial err(%+v)", err)
continue
}
mobileMachine := &model.MobileMachine{
Serial: device.Serial,
Name: device.Name,
CPU: device.CPU,
Version: device.Version,
Mode: device.Mode,
State: device.State,
Host: host,
Type: deviceType,
Action: model.MBOnline,
UUID: uuid,
}
if tmpMobileMachine.ID == 0 {
if err = s.dao.InsertMobileMachine(mobileMachine); err != nil {
log.Error("d.SyncMobileDeviceList InsertMobileMachine err(%+v)", err)
} else {
addCnt = addCnt + 1
}
} else {
if err = s.dao.UpdateMobileMachine(mobileMachine); err != nil {
log.Error("d.SyncMobileDeviceList UpdateMobileMachine err(%+v)", err)
} else {
updateCnt = updateCnt + 1
}
}
}
}
s.dao.DeleteMobileMachineNotInHost(s.c.DeviceFarm.HostList)
delCnt, err = s.dao.DeleteMobileMachineByUUID(uuid)
return
}
// QueryMobileDeviceList Query Mobile Device List.
func (s *Service) QueryMobileDeviceList(c context.Context, queryRequest *model.QueryMobileDeviceRequest) (p *model.PaginateMobileMachines, err error) {
var (
total int64
mobileMachines []*model.MobileMachine
mobileMachinesResponse []*model.MobileMachineResponse
)
if total, mobileMachines, err = s.dao.FindMobileMachines(queryRequest); err != nil {
return
}
for _, mobileMachine := range mobileMachines {
var mobileMachineResponse model.MobileMachineResponse
mobileMachineResponse.MobileMachine = mobileMachine
mobileMachinesResponse = append(mobileMachinesResponse, &mobileMachineResponse)
}
if err = s.getMobileImages(mobileMachinesResponse); err != nil {
return
}
p = &model.PaginateMobileMachines{
PageNum: queryRequest.PageNum,
PageSize: queryRequest.PageSize,
Total: total,
MobileMachines: mobileMachinesResponse,
}
return
}
// RefreshMobileDeviceDetail Refresh Mobile Device Detail.
func (s *Service) RefreshMobileDeviceDetail(c context.Context, deviceID int64) (mobileMachineResponse *model.MobileMachineResponse, err error) {
var (
device *model.Device
mobileMachine *model.MobileMachine
)
if mobileMachine, err = s.dao.FindMobileMachineByID(deviceID); err != nil {
return
}
if device, err = s.dao.MobileDeviceDetail(c, mobileMachine.Host, mobileMachine.Serial); err != nil {
return
}
//update db
mobileMachine.State = device.State
mobileMachine.Action = 1
if err = s.dao.UpdateMobileMachine(mobileMachine); err != nil {
log.Error("d.RefreshMobileDeviceDetail err(%+v)", err)
return
}
mobileMachineResponse = &model.MobileMachineResponse{}
mobileMachineResponse.MobileMachine = mobileMachine
if err = s.getMobileImage(mobileMachineResponse); err != nil {
return
}
return
}
// MobileMachineCategory Mobile Machine Category.
func (s *Service) MobileMachineCategory(c context.Context, isShowOffline bool) (categoryList []*model.MobileDeviceCategoryResponse, err error) {
var mobileCategory *model.MobileCategory
if mobileCategory, err = s.dao.FindMobileMachineCategory(isShowOffline); err != nil {
return
}
interfaceCPU := make([]interface{}, len(mobileCategory.CPUs))
for i, v := range mobileCategory.CPUs {
interfaceCPU[i] = v
}
categoryCPU := &model.MobileDeviceCategoryResponse{
Name: "cpu",
Label: "CPU",
Values: interfaceCPU,
}
interfaceVersion := make([]interface{}, len(mobileCategory.Versions))
for i, v := range mobileCategory.Versions {
interfaceVersion[i] = v
}
categoryVersion := &model.MobileDeviceCategoryResponse{
Name: "version",
Label: "版本",
Values: interfaceVersion,
}
interfaceMode := make([]interface{}, len(mobileCategory.Modes))
for i, v := range mobileCategory.Modes {
interfaceMode[i] = v
}
categoryMode := &model.MobileDeviceCategoryResponse{
Name: "mode",
Label: "型号",
Values: interfaceMode,
}
interfaceType := make([]interface{}, len(mobileCategory.Types))
for i, v := range mobileCategory.Types {
interfaceType[i] = v
}
categoryType := &model.MobileDeviceCategoryResponse{
Name: "type",
Label: "是否真机",
Values: interfaceType,
}
interfaceState := make([]interface{}, len(mobileCategory.States))
for i, v := range mobileCategory.States {
interfaceState[i] = v
}
categoryState := &model.MobileDeviceCategoryResponse{
Name: "state",
Label: "状态",
Values: interfaceState,
}
interfaceUsage := make([]interface{}, len(mobileCategory.Usages))
for i, v := range mobileCategory.Usages {
interfaceUsage[i] = v
}
categoryUsage := &model.MobileDeviceCategoryResponse{
Name: "usage",
Label: "使用情况",
Values: interfaceUsage,
}
categoryList = append(categoryList, categoryCPU)
categoryList = append(categoryList, categoryVersion)
categoryList = append(categoryList, categoryMode)
categoryList = append(categoryList, categoryType)
categoryList = append(categoryList, categoryState)
categoryList = append(categoryList, categoryUsage)
return
}
// BindMobileDevice Bind Mobile Device.
func (s *Service) BindMobileDevice(c context.Context, deviceID int64, username string) (status int, err error) {
var mobileMachine *model.MobileMachine
status = -1
if mobileMachine, err = s.dao.FindMobileMachineByID(deviceID); err != nil {
return
}
if mobileMachine.Action < model.MBOnline {
err = ecode.MerlinDeviceFarmMachineStatusErr
return
}
//真机借出状态, 无法操作
if mobileMachine.Type == model.MBReal && mobileMachine.IsLendOut == model.MBLendOut {
err = ecode.MerlinDeviceIsLendOut
return
}
if mobileMachine.Username != "" {
return
}
mobileMachine.Username = username
mobileMachine.EndTime = time.Now().AddDate(0, s.c.DeviceFarm.ApplyMonthTime, 0)
mobileMachine.LastBindTime = time.Now()
if err = s.dao.UpdateMobileMachine(mobileMachine); err != nil {
return
}
if err = s.addMobileDeviceLog(deviceID, username, model.MBBindLog); err != nil {
return
}
status = 0
return
}
// ReleaseMobileDevice Release Mobile Device.
func (s *Service) ReleaseMobileDevice(c context.Context, deviceID int64, username string) (status int, err error) {
var mobileMachine *model.MobileMachine
status = -1
if mobileMachine, err = s.isAccessToMobileDevice(deviceID, username); err != nil {
return
}
mobileMachine.Username = ""
if mobileMachine.EndTime, err = time.ParseInLocation(model.TimeFormat, "1990-01-01 01:01:01", time.Local); err != nil {
return
}
if err = s.dao.UpdateMobileMachineByRelease(mobileMachine); err != nil {
return
}
if err = s.addMobileDeviceLog(deviceID, username, model.MBReleaseLog); err != nil {
return
}
status = 0
return
}
// LendOutMobileDevice Pull Out Mobile Device.
func (s *Service) LendOutMobileDevice(c context.Context, deviceID int64, username string) (status int, err error) {
var (
mobileMachine *model.MobileMachine
)
status = -1
if mobileMachine, err = s.isAccessToMobileDevice(deviceID, username); err != nil {
return
}
//只有真机支持借出 真机type=0
if mobileMachine.Type != model.MBReal {
err = ecode.MerlinDeviceIsNotRealMachineErr
return
}
if err = s.dao.LendOutMobileMachine(deviceID, mobileMachine.Serial, username); err != nil {
return
}
status = 0
return
}
// ReturnMobileDevice Return Mobile Device.
func (s *Service) ReturnMobileDevice(c context.Context, deviceID int64, username string) (status int, err error) {
var (
mobileMachine *model.MobileMachine
isSuperOwner = false
)
status = -1
for _, superOwner := range s.c.DeviceFarm.SuperOwner {
if username == superOwner {
isSuperOwner = true
break
}
}
if !isSuperOwner {
err = ecode.MerlinDeviceNoRight
return
}
if mobileMachine, err = s.dao.FindMobileMachineByID(deviceID); err != nil {
return
}
if mobileMachine.IsLendOut == model.MBOnSite {
err = ecode.MerlinDeviceFarmErr
return
}
if err = s.dao.ReturnMobileMachine(deviceID, mobileMachine.Serial, username); err != nil {
return
}
status = 0
return
}
// StartMobileDevice Start Mobile Device.
func (s *Service) StartMobileDevice(c context.Context, deviceID int64, username string) (deviceBootData *model.DeviceBootData, err error) {
var (
mobileMachine *model.MobileMachine
device *model.Device
)
if mobileMachine, err = s.isAccessToMobileDevice(deviceID, username); err != nil {
return
}
if deviceBootData, err = s.dao.BootMobileDevice(c, mobileMachine.Host, mobileMachine.Serial); err != nil {
return
}
if device, err = s.dao.MobileDeviceDetail(c, mobileMachine.Host, mobileMachine.Serial); err != nil {
return
}
mobileMachine.State = device.State
mobileMachine.WsURL = deviceBootData.WSRUL
mobileMachine.UploadURL = deviceBootData.UploadURL
if err = s.dao.UpdateMobileMachineWsurlAndState(mobileMachine); err != nil {
return
}
err = s.addMobileDeviceLog(deviceID, username, model.MBStartLog)
return
}
// ShutdownMobileDevice Shutdown Mobile Device.
func (s *Service) ShutdownMobileDevice(c context.Context, deviceID int64, username string) (status int, err error) {
var (
mobileMachine *model.MobileMachine
device *model.Device
)
status = -1
if mobileMachine, err = s.isAccessToMobileDevice(deviceID, username); err != nil {
return
}
if err = s.dao.ShutdownMobileDevice(c, mobileMachine.Host, mobileMachine.Serial); err != nil {
return
}
if device, err = s.dao.MobileDeviceDetail(c, mobileMachine.Host, mobileMachine.Serial); err != nil {
return
}
mobileMachine.State = device.State
mobileMachine.WsURL = ""
mobileMachine.UploadURL = ""
if err = s.dao.UpdateMobileMachineWsurlAndState(mobileMachine); err != nil {
return
}
if err = s.addMobileDeviceLog(deviceID, username, model.MBShutDownLog); err != nil {
return
}
status = 0
return
}
func (s *Service) isAccessToMobileDevice(deviceID int64, username string) (mobileMachine *model.MobileMachine, err error) {
if mobileMachine, err = s.dao.FindMobileMachineByID(deviceID); err != nil {
return
}
if mobileMachine.Action < model.MBOnline {
err = ecode.MerlinDeviceFarmMachineStatusErr
return
}
//真机借出状态, 无法操作
if mobileMachine.Type == model.MBReal && mobileMachine.IsLendOut == model.MBLendOut {
err = ecode.MerlinDeviceIsLendOut
return
}
for _, superOwner := range s.c.DeviceFarm.SuperOwner {
if username == superOwner {
return
}
}
if mobileMachine.Username != username {
err = ecode.MerlinDeviceNotBind
}
return
}
func (s *Service) addMobileDeviceLog(deviceID int64, username, operateType string) (err error) {
machineLog := &model.MobileMachineLog{
OperateType: operateType,
Username: username,
MachineID: deviceID,
OperateResult: model.OperationSuccessForMachineLog,
}
return s.dao.InsertMobileMachineLog(machineLog)
}
func (s *Service) getMobileImages(mobileMachinesResponse []*model.MobileMachineResponse) (err error) {
var mobileImages []*model.MobileImage
if mobileImages, err = s.dao.FindAllMobileImages(); err != nil {
return
}
for _, mobileMachineResponse := range mobileMachinesResponse {
for _, mobileImage := range mobileImages {
if mobileImage.Mode == mobileMachineResponse.Mode {
mobileMachineResponse.ImageSrc = mobileImage.ImageSrc
continue
}
}
}
return
}
func (s *Service) getMobileImage(mobileMachinesResponse *model.MobileMachineResponse) (err error) {
var mobileImage *model.MobileImage
if mobileImage, err = s.dao.FindMobileImageByMode(mobileMachinesResponse.Mode); err != nil {
return
}
mobileMachinesResponse.ImageSrc = mobileImage.ImageSrc
return
}
// QueryMobileMachineLogs Query Mobile Machine Logs.
func (s *Service) QueryMobileMachineLogs(c context.Context, queryRequest *model.QueryMobileMachineLogRequest) (p *model.PaginateMobileMachineLog, err error) {
var (
total int64
machineLogs []*model.AboundMobileMachineLog
)
if total, machineLogs, err = s.dao.FindMobileMachineLogs(queryRequest); err != nil {
return
}
p = &model.PaginateMobileMachineLog{
PageNum: queryRequest.PageNum,
PageSize: queryRequest.PageSize,
Total: total,
MachineLogs: machineLogs,
}
return
}
// QueryMobileMachineLendOutLogs Query Mobile Machine Lend Out Logs.
func (s *Service) QueryMobileMachineLendOutLogs(c context.Context, username string, queryRequest *model.QueryMobileMachineLogRequest) (p *model.PaginateMobileMachineLendOutLog, err error) {
var (
total int64
machineLogs []*model.AboundMobileMachineLog
machineLendOutRecords []*model.MachineLendOutRecord
mobileMachine *model.MobileMachine
existMachineIDs []int64
isSuperUser bool
)
isSuperUser = s.isSuperUser(username)
queryRequest.OperateType = model.MBLendOutLog
if total, machineLogs, err = s.dao.FindMobileMachineLogs(queryRequest); err != nil {
return
}
for _, machineLog := range machineLogs {
var (
enableReturn bool
isExistLendRecord bool
)
if mobileMachine, err = s.dao.FindMobileMachineByID(machineLog.MachineID); err != nil {
return
}
for _, existMachineID := range existMachineIDs {
if existMachineID == machineLog.MachineID {
isExistLendRecord = true
break
}
}
//移动设备借出状态 且 用户是超级用户 才有资格归还手机 只有最新一条记录 才能点击返还
if isExistLendRecord {
machineLendOutRecord := &model.MachineLendOutRecord{
MachineID: machineLog.MachineID,
Lender: machineLog.Username,
LendTime: machineLog.OperateTime,
EnableReturn: false,
Status: model.MBOnSite,
}
machineLendOutRecords = append(machineLendOutRecords, machineLendOutRecord)
} else {
if isSuperUser && (mobileMachine.IsLendOut == model.MBLendOut) {
enableReturn = true
}
machineLendOutRecord := &model.MachineLendOutRecord{
MachineID: machineLog.MachineID,
Lender: machineLog.Username,
LendTime: machineLog.OperateTime,
EnableReturn: enableReturn,
Status: mobileMachine.IsLendOut,
}
machineLendOutRecords = append(machineLendOutRecords, machineLendOutRecord)
existMachineIDs = append(existMachineIDs, machineLog.MachineID)
}
}
p = &model.PaginateMobileMachineLendOutLog{
PageNum: queryRequest.PageNum,
PageSize: queryRequest.PageSize,
Total: total,
MachineLendOutRecords: machineLendOutRecords,
}
return
}
// QueryMobileMachineErrorLogs Query Mobile Machine Error Logs.
func (s *Service) QueryMobileMachineErrorLogs(c context.Context, queryRequest *model.QueryMobileMachineErrorLogRequest) (p *model.PaginateMobileMachineErrorLog, err error) {
var (
total int64
machineLogs []*model.MobileMachineErrorLog
)
if total, machineLogs, err = s.dao.FindMobileMachineErrorLog(queryRequest); err != nil {
return
}
p = &model.PaginateMobileMachineErrorLog{
PageNum: queryRequest.PageNum,
PageSize: queryRequest.PageSize,
Total: total,
MachineLogs: machineLogs,
}
return
}
// CallBackMobileDeviceError Call Back Mobile Device Error.
func (s *Service) CallBackMobileDeviceError(c context.Context, serialName, errMsg string, errCode int) (err error) {
var mobileMachine *model.MobileMachine
if mobileMachine, err = s.dao.FindMobileMachineBySerial(serialName); err != nil {
return
}
mobileMachineErrorLog := &model.MobileMachineErrorLog{
MachineID: mobileMachine.ID,
SerialName: serialName,
ErrorMessage: errMsg,
ErrorCode: errCode,
}
err = s.dao.InsertMobileMachineErrorLog(mobileMachineErrorLog)
return
}
// ReportMobileDeviceError Report Mobile Device Error.
func (s *Service) ReportMobileDeviceError(c context.Context, serialName, errMsg string, errCode int) (err error) {
var mobileMachine *model.MobileMachine
if mobileMachine, err = s.dao.FindMobileMachineBySerial(serialName); err != nil {
return
}
if mobileMachine.ID == 0 {
err = ecode.NothingFound
return
}
content := fmt.Sprintf("通知\n设备ID: %d\n系列号: %s\n型号: %s\n系统版本: %s\n使用者: %s\n错误信息: %s\n错误码: %d", mobileMachine.ID, serialName, mobileMachine.Mode, mobileMachine.Version, mobileMachine.Username, errMsg, errCode)
msgSendReq := &model.MsgSendReq{
Chatid: s.c.WeChat.WeChatDeviceFarm.ChatID,
Msgtype: s.c.WeChat.WeChatDeviceFarm.MsgType,
Text: model.MsgSendReqText{Content: content},
Safe: s.c.WeChat.WeChatDeviceFarm.Safe,
}
if _, err = s.dao.WeChatSendMessage(c, msgSendReq); err != nil {
return
}
mobileMachineErrorLog := &model.MobileMachineErrorLog{
MachineID: mobileMachine.ID,
SerialName: serialName,
ErrorMessage: errMsg,
ErrorCode: errCode,
}
err = s.dao.InsertMobileMachineErrorLog(mobileMachineErrorLog)
return
}
// DeviceFarmSuperUser Device Farm Super User.
func (s *Service) DeviceFarmSuperUser() (responseMap map[string]interface{}) {
responseMap = make(map[string]interface{})
responseMap["superuser_list"] = s.c.DeviceFarm.SuperOwner
return
}
// IsBindMobileDeviceByTheUser Is Bind Mobile Device By The User.
func (s *Service) IsBindMobileDeviceByTheUser(c context.Context, deviceID int64, username string) (isBind bool, err error) {
var mobileMachine *model.MobileMachine
if mobileMachine, err = s.dao.FindMobileMachineByID(deviceID); err != nil {
return
}
if mobileMachine.Username == username {
isBind = true
}
return
}
func (s *Service) isSuperUser(username string) bool {
for _, superOwner := range s.c.DeviceFarm.SuperOwner {
if username == superOwner {
return true
}
}
return false
}

View File

@@ -0,0 +1,25 @@
package service
import (
"go-common/app/admin/ep/merlin/model"
)
// QueryImage query image.
func (s *Service) QueryImage() ([]*model.Image, error) {
return s.dao.Images()
}
// AddImage add image.
func (s *Service) AddImage(image *model.Image) error {
return s.dao.AddImage(image)
}
// UpdateImage update image.
func (s *Service) UpdateImage(image *model.Image) error {
return s.dao.UpdateImage(image)
}
// DeleteImage delete image.
func (s *Service) DeleteImage(iID int64) error {
return s.dao.DelImage(iID)
}

View File

@@ -0,0 +1,357 @@
package service
import (
"context"
"fmt"
"strings"
"go-common/app/admin/ep/merlin/model"
"go-common/library/ecode"
"go-common/library/log"
)
// GenMachines create multiple machines.
func (s *Service) GenMachines(c context.Context, gmr *model.GenMachinesRequest, u string) (err error) {
var (
ins []*model.CreateInstance
cluster *model.Cluster
hasMachine bool
ms []*model.Machine
)
if hasMachine, err = s.dao.HasMachine(gmr.Name); err != nil {
return
}
if hasMachine {
err = ecode.MerlinDuplicateMachineNameErr
return
}
if cluster, err = s.dao.QueryCluster(c, gmr.NetworkID); err != nil {
return
}
if cluster == nil {
err = ecode.MerlinInvalidClusterErr
return
}
if err = cluster.Verify(); err != nil {
return
}
pgmr := gmr.ToPaasGenMachineRequest(s.c.Paas.MachineLimitRatio)
if ms, err = s.dao.InsertMachinesV2(u, gmr, pgmr); err != nil {
return
}
//insert image log
for _, m := range ms {
hubImageLog := &model.HubImageLog{
UserName: u,
MachineID: m.ID,
ImageSrc: gmr.Image,
ImageTag: "",
Status: model.ImageSuccess,
OperateType: model.ImageNoSnapshot,
}
s.dao.InsertHubImageLog(hubImageLog)
}
if ins, err = s.dao.GenPaasMachines(c, pgmr); err != nil {
return
}
for _, in := range ins {
if in.InstanceCreateStatus == model.CreateFailedMachineInPaas {
s.dao.UpdateMachineStatusByName(model.ImmediatelyFailedMachineInMerlin, in.InstanceName)
}
}
return
}
// DelMachineWhenCanBeDel Del Machine When Can Be Del.
func (s *Service) DelMachineWhenCanBeDel(c context.Context, id int64, username string) (instance *model.ReleaseInstance, err error) {
var (
machine *model.Machine
)
if machine, err = s.dao.QueryMachine(id); err != nil {
return
}
if machine.IsCreating() {
err = ecode.MerlinCanNotBeDel
return
}
return s.DelMachine(c, id, username)
}
// DelMachine delete machine by giving id.
func (s *Service) DelMachine(c context.Context, id int64, username string, beforeDelMachineFuncs ...model.BeforeDelMachineFunc) (instance *model.ReleaseInstance, err error) {
for _, beforeDelMachine := range beforeDelMachineFuncs {
if err = beforeDelMachine(c, id, username); err != nil {
return
}
}
return s.delMachineWithoutComponent(c, id, username)
}
func (s *Service) delMachineWithoutComponent(c context.Context, mID int64, username string) (instance *model.ReleaseInstance, err error) {
var (
machine *model.Machine
)
if machine, err = s.dao.QueryMachine(mID); err != nil {
return
}
machineLog := &model.MachineLog{
OperateType: model.DeleteForMachineLog,
Username: username,
MachineID: mID,
}
if instance, err = s.delMachineWithoutLog(c, machine, username); err != nil {
machineLog.OperateResult = model.OperationFailedForMachineLog
} else {
machineLog.OperateResult = model.OperationSuccessForMachineLog
}
machineLog.OperateType = model.DeleteForMachineLog
machineLog.Username = username
machineLog.MachineID = machine.ID
err = s.dao.InsertMachineLog(machineLog)
return
}
func (s *Service) delMachineWithoutLog(c context.Context, machine *model.Machine, username string) (instance *model.ReleaseInstance, err error) {
if instance, err = s.dao.DelPaasMachine(c, machine.ToPaasQueryAndDelMachineRequest()); err != nil {
return
}
if err = s.dao.UpdateTaskStatusByMachines([]int64{machine.ID}, model.TaskDone); err != nil {
return
}
if err = s.dao.DelMachine(machine.ID, username); err != nil {
return
}
if err = s.SendMailDeleteMachine(username, machine); err != nil {
err = nil
log.Error("Send mail failed (%v)", err)
}
return
}
// QueryMachineDetail query detail information of machine.
func (s *Service) QueryMachineDetail(c context.Context, mID int64) (detail model.MachineDetail, err error) {
var (
machine *model.Machine
passMachineDetail *model.PaasMachineDetail
cluster *model.Cluster
nodes []*model.MachineNode
isSnapShot bool
)
if machine, err = s.dao.QueryMachine(mID); err != nil {
return
}
if passMachineDetail, err = s.dao.QueryPaasMachine(c, machine.ToPaasQueryAndDelMachineRequest()); err != nil {
return
}
if cluster, err = s.dao.QueryCluster(c, machine.NetworkID); err != nil {
return
}
if cluster == nil {
err = ecode.MerlinInvalidClusterErr
return
}
if err = cluster.Verify(); err != nil {
return
}
if nodes, err = s.dao.QueryMachineNodes(mID); err != nil {
return
}
isSnapShot = strings.Contains(passMachineDetail.Image, s.c.BiliHub.MachineTagPri)
detail = model.MachineDetail{
Machine: *machine,
Nodes: nodes,
PaasMachineDetail: passMachineDetail.ConvertUnits(),
Name: machine.Name,
NetworkName: cluster.Networks[0].Name,
IsSnapShot: isSnapShot,
}
return
}
// QueryMachinePackages query packages of machine.
func (s *Service) QueryMachinePackages(c context.Context) (mp []*model.MachinePackage, err error) {
return s.dao.FindAllMachinePackages()
}
//QueryMachines query multiple machines and update machine status
func (s *Service) QueryMachines(c context.Context, session string, qmr *model.QueryMachineRequest) (p *model.PaginateMachine, err error) {
var (
machines []*model.Machine
total int64
cluster *model.Cluster
podNames []string
treeInstance *model.TreeInstance
mapping map[string]*model.TreeInstance
)
if mapping, err = s.QueryTreeInstanceForMerlin(c, session, &qmr.TreeNode); err != nil {
return
}
for k := range mapping {
podNames = append(podNames, k)
}
if total, machines, err = s.dao.QueryMachines(podNames, qmr); err != nil {
return
}
genMachines := make([]model.GenMachine, len(machines))
for i, m := range machines {
if cluster, err = s.dao.QueryCluster(c, m.NetworkID); err != nil {
return
}
genMachines[i] = model.GenMachine{
Machine: *m,
ClusterName: cluster.Name,
NetworkName: cluster.Networks[0].Name,
}
treeInstance = mapping[m.PodName]
if treeInstance != nil {
genMachines[i].IP = treeInstance.InternalIP
}
}
p = &model.PaginateMachine{
PageNum: qmr.PageNum,
PageSize: qmr.PageSize,
Total: total,
Machines: genMachines,
}
return
}
// QueryMachineLogs query machine logs.
func (s *Service) QueryMachineLogs(c context.Context, queryRequest *model.QueryMachineLogRequest) (p *model.PaginateMachineLog, err error) {
var (
total int64
machineLogs []*model.AboundMachineLog
)
if total, machineLogs, err = s.dao.FindMachineLogs(queryRequest); err != nil {
return
}
p = &model.PaginateMachineLog{
PageNum: queryRequest.PageNum,
PageSize: queryRequest.PageSize,
Total: total,
MachineLogs: machineLogs,
}
return
}
// QueryMachineStatus query the status of machine.
func (s *Service) QueryMachineStatus(c context.Context, machineID int64) (msr *model.MachineStatusResponse, err error) {
var m *model.Machine
if m, err = s.dao.QueryMachine(machineID); err != nil {
return
}
if m.IsFailed() || m.IsDeleted() {
return
}
if msr, err = s.verifyPassStatus(c, m, true); err != nil {
return
}
return
}
// TransferMachine Transfer Machine.
func (s *Service) TransferMachine(c context.Context, machineID int64, username, receiver string) (status int, err error) {
var (
machine *model.Machine
treePath string
treeRoles []*model.TreeRole
isReceiverAccessTreeNode bool
)
if _, err = s.dao.FindUserByUserName(receiver); err != nil {
err = ecode.MerlinUserNotExist
return
}
if machine, err = s.dao.QueryMachine(machineID); err != nil {
return
}
// 查看接受者有无机器所在服务树节点权限
treePath = machine.BusinessUnit + "." + machine.Project + "." + machine.App
if treeRoles, err = s.dao.TreeRolesAsPlatform(c, treePath); err != nil {
return
}
for _, treeRole := range treeRoles {
if treeRole.UserName == receiver {
isReceiverAccessTreeNode = true
break
}
}
if !isReceiverAccessTreeNode {
err = ecode.MerlinUserNoAccessTreeNode
return
}
machineLog := &model.MachineLog{
OperateType: model.TransferForMachineLog,
Username: username,
MachineID: machineID,
}
if err = s.dao.UpdateMachineUser(machineID, receiver); err != nil {
machineLog.OperateResult = model.OperationFailedForMachineLog
} else {
machineLog.OperateResult = model.OperationSuccessForMachineLog
//send mail
mailHeader := fmt.Sprintf("机器:[%s] 被用户[%s]从用户[%s]名下转移至用户[%s]", machine.Name, username, machine.Username, receiver)
var sendMailUsers []string
sendMailUsers = append(sendMailUsers, username)
sendMailUsers = append(sendMailUsers, receiver)
sendMailUsers = append(sendMailUsers, machine.Username)
s.SendMailForMultiUsers(c, sendMailUsers, mailHeader)
}
err = s.dao.InsertMachineLog(machineLog)
return
}
func (s *Service) verifyPassStatus(c context.Context, m *model.Machine, targetCreating bool) (msr *model.MachineStatusResponse, err error) {
var (
ms *model.MachineStatus
b bool
status int
shadow *model.MachineStatusResponse
)
if msr = model.InstanceMachineStatusResponse(m.Status); msr != nil {
return
}
if ms, err = s.dao.QueryPaasMachineStatus(c, m.ToPaasQueryAndDelMachineRequest()); err != nil {
return
}
if b, err = s.TreeMachineIsExist(c, m.PodName, m.ToTreeNode()); err != nil {
return
}
shadow = ms.ToMachineStatusResponse()
if b {
shadow.SynTree = model.True
} else {
shadow.SynTree = model.False
}
if targetCreating {
status = shadow.CreatingMachineStatus()
} else {
status = shadow.FailedMachineStatus()
}
if err = s.dao.UpdateMachineStatus(m.ID, status); err != nil {
return
}
msr = shadow
return
}

View File

@@ -0,0 +1,72 @@
package service
import (
"context"
"go-common/app/admin/ep/merlin/model"
"go-common/library/ecode"
"go-common/library/log"
)
// GenMachinesV2 create multiple machines for v2.
func (s *Service) GenMachinesV2(c context.Context, gmr *model.GenMachinesRequest, u string) (err error) {
var (
ins []*model.CreateInstance
cluster *model.Cluster
hasMachine bool
ms []*model.Machine
)
if hasMachine, err = s.dao.HasMachine(gmr.Name); err != nil {
return
}
if hasMachine {
err = ecode.MerlinDuplicateMachineNameErr
return
}
if cluster, err = s.dao.QueryCluster(c, gmr.NetworkID); err != nil {
return
}
if cluster == nil {
err = ecode.MerlinInvalidClusterErr
return
}
if err = cluster.Verify(); err != nil {
return
}
pgmr := gmr.ToPaasGenMachineRequest(s.c.Paas.MachineLimitRatio)
if ms, err = s.dao.InsertMachinesV2(u, gmr, pgmr); err != nil {
return
}
go func() {
var (
mIDs []int64
mim map[int64]string
nmm = make(map[string]*model.Machine)
cTODO = context.TODO()
)
for _, m := range ms {
mIDs = append(mIDs, m.ID)
nmm[m.Name] = m
}
if mim, err = s.AddTagToMachine(cTODO, u, gmr.Image, mIDs); err != nil {
if err = s.dao.UpdateStatusForMachines(model.CreateTagFailedMachineInMerlin, mIDs); err != nil {
log.Error("Update the status of machines(%v) err(%v)", mIDs, err)
}
return
}
for i, pm := range pgmr.Machines {
pgmr.Machines[i].Image = mim[nmm[pm.Name].ID]
pgmr.Machines[i].Snapshot = true
pgmr.Machines[i].ForcePullImage = true
}
if ins, err = s.dao.GenPaasMachines(cTODO, pgmr); err != nil {
return
}
for _, in := range ins {
if in.InstanceCreateStatus == model.CreateFailedMachineInPaas {
s.dao.UpdateMachineStatusByName(model.ImmediatelyFailedMachineInMerlin, in.InstanceName)
}
}
}()
return
}

View File

@@ -0,0 +1,15 @@
package service
import "go-common/app/admin/ep/merlin/model"
// AddMachineLog add machine log.
func (s *Service) AddMachineLog(username string, machineID int64, operateType string, operateResult string) (err error) {
machineLog := &model.MachineLog{}
machineLog.OperateType = operateType
machineLog.Username = username
machineLog.MachineID = machineID
machineLog.OperateResult = operateResult
err = s.dao.InsertMachineLog(machineLog)
return
}

View File

@@ -0,0 +1,13 @@
package service
import (
"testing"
. "github.com/smartystreets/goconvey/convey"
)
func TestGenMachines(t *testing.T) {
Convey("test UserTree", t, func() {
//TODO
})
}

View File

@@ -0,0 +1,213 @@
package service
import (
"context"
"fmt"
"time"
"go-common/app/admin/ep/merlin/model"
"gopkg.in/gomail.v2"
)
const (
_merlinUIAddr = "http://merlin.bilibili.co"
)
// SendMail send mail.
func (s *Service) SendMail(mailType int, machine *model.Machine) (err error) {
var (
mailSendHead = ""
mailSendContext = ""
m = gomail.NewMessage()
user *model.User
)
switch mailType {
//将要过期提醒
case model.MailTypeMachineWillExpired:
if user, err = s.dao.FindUserByUserName(machine.Username); err != nil {
return
}
m.SetHeader("To", user.EMail)
mailSendHead = fmt.Sprintf("machine named [%s] will be expired on %s", machine.Name, machine.EndTime.Format(model.TimeFormat))
m.SetHeader("Subject", mailSendHead)
delayLink := fmt.Sprintf("http://merlin.bilibili.co/#/machine-list?machine_name=%s&username=&page_num=1&page_size=10", machine.Name)
mailSendContext = fmt.Sprintf("<br>可前往Merlin平台申请延期</br><br>链接: <a href=%s>点击</a></br>", delayLink)
//机器删除提醒
case model.MailTypeMachineDeleted:
if user, err = s.dao.FindUserByUserName(machine.Username); err != nil {
return
}
m.SetHeader("To", user.EMail)
mailSendHead = fmt.Sprintf("machine named [%s] has been deleted on %s", machine.Name, time.Now().Format(model.TimeFormat))
m.SetHeader("Subject", mailSendHead)
//机器删除失败提醒
case model.MailTypeTaskDeleteMachineFailed:
m.SetHeader("To", s.c.Mail.NoticeOwner[0])
mailSendHead = fmt.Sprintf("machine named [%s] has been failed to delete by task on %s", machine.Name, time.Now().Format(model.TimeFormat))
m.SetHeader("Subject", mailSendHead)
}
if mailSendHead != "" {
ml := &model.MailLog{
ReceiverName: m.GetHeader("To")[0],
MailType: mailType,
SendHead: mailSendHead,
SendContext: mailSendContext,
}
s.dao.SendMail(m)
s.dao.InsertMailLog(ml)
}
return
}
// SendMailDeleteMachine Send Mail Delete Machine.
func (s *Service) SendMailDeleteMachine(username string, machine *model.Machine) (err error) {
var (
mailSendHead = ""
m = gomail.NewMessage()
user *model.User
)
if user, err = s.dao.FindUserByUserName(machine.Username); err != nil {
return
}
//是不是删自己机器
if username == machine.Username {
m.SetHeader("To", user.EMail)
mailSendHead = fmt.Sprintf("Machine named [%s] has been deleted on %s", machine.Name, time.Now().Format(model.TimeFormat))
m.SetHeader("Subject", mailSendHead)
} else {
var delUser *model.User
if delUser, err = s.dao.FindUserByUserName(username); err != nil {
m.SetHeader("To", user.EMail)
} else {
m.SetHeader("To", user.EMail, delUser.EMail)
}
mailSendHead = fmt.Sprintf("Machine named [%s] has been deleted on %s by %s", machine.Name, time.Now().Format(model.TimeFormat), username)
m.SetHeader("Subject", mailSendHead)
}
for _, header := range m.GetHeader("To") {
ml := &model.MailLog{
ReceiverName: header,
MailType: model.MailTypeMachineDeleted,
SendHead: mailSendHead,
SendContext: "",
}
s.dao.SendMail(m)
s.dao.InsertMailLog(ml)
}
return
}
// SendMailApplyDelayMachineEndTime send mail apply delay machine end time.
func (s *Service) SendMailApplyDelayMachineEndTime(ctx context.Context, auditor, applicant string, machineID int64, currentEndTime, applyEndTime time.Time) (err error) {
var (
userInfo *model.User
machine *model.Machine
)
if userInfo, err = s.QueryUserInfo(ctx, auditor); err != nil {
return
}
if machine, err = s.dao.QueryMachine(machineID); err != nil {
return
}
timeNow := time.Now().Format(model.TimeFormat)
currentET := currentEndTime.Format(model.TimeFormat)
applyET := applyEndTime.Format(model.TimeFormat)
mailSendHead, mailSendContext := applyDelayMachineEndTimeHeadAndContext(machine.Name, applicant, timeNow, currentET, applyET, _merlinUIAddr)
err = s.sendMail(userInfo.EMail, mailSendHead, mailSendContext, model.MailTypeApplyDelayMachineEndTime)
return
}
// SendMailAuditResult send mail audit result.
func (s *Service) SendMailAuditResult(ctx context.Context, auditor, applicant string, machineID int64, auditResult bool) (err error) {
var (
userInfo *model.User
machine *model.Machine
)
if userInfo, err = s.QueryUserInfo(ctx, applicant); err != nil {
return
}
if machine, err = s.dao.QueryMachine(machineID); err != nil {
return
}
timeNow := time.Now().Format(model.TimeFormat)
mailSendHead, mailSendContext := auditResultHeadAndContext(machine.Name, timeNow, auditor, _merlinUIAddr, auditResult)
err = s.sendMail(userInfo.EMail, mailSendHead, mailSendContext, model.MailTypeAuditDelayMachineEndTime)
return
}
func (s *Service) sendMail(receiver, header, body string, mailType int) (err error) {
m := gomail.NewMessage()
m.SetHeader("To", receiver)
m.SetHeader("Subject", header)
m.SetBody("text/html", body)
s.dao.SendMail(m)
ml := &model.MailLog{
ReceiverName: receiver,
MailType: mailType,
SendHead: header,
SendContext: body,
}
err = s.dao.InsertMailLog(ml)
return
}
func applyDelayMachineEndTimeHeadAndContext(machineName, applicant, timeNow, currentEndTime, applyEndTime, operateLink string) (head, context string) {
head = fmt.Sprintf("申请延期机器 机器名:[%s] %s", machineName, timeNow)
context = fmt.Sprintf("<br>申请延期机器: %s</br>"+
"<br>申请者: %s</br>"+
"<br>当前过期时间: %s</br>"+
"<br>申请延期时间: %s</br>"+
"<br>操作链接: <a href=%s>点击</a></br>", machineName, applicant, currentEndTime, applyEndTime, operateLink)
return
}
func auditResultHeadAndContext(machineName, timeNow, auditor, operateLink string, auditResult bool) (head, context string) {
auditResultStr := "驳回"
if auditResult {
auditResultStr = "通过"
}
head = fmt.Sprintf("申请延期机器审批结果 机器名:[%s] %s", machineName, timeNow)
context = fmt.Sprintf("<br>申请延期机器: %s</br>"+
"<br>审批者: %s</br>"+
"<br>申请结果: %s</br>"+
"<br>查看链接: <a href=%s>点击</a></br>", machineName, auditor, auditResultStr, operateLink)
return
}
// SendMailForMultiUsers Send Mail For Multi Users.
func (s *Service) SendMailForMultiUsers(ctx context.Context, receivers []string, mailSendHead string) (err error) {
for _, receiver := range receivers {
var userInfo *model.User
if userInfo, err = s.QueryUserInfo(ctx, receiver); err != nil {
continue
}
err = s.sendMail(userInfo.EMail, mailSendHead, "", model.MailTypeMachineTransfer)
}
return
}

View File

@@ -0,0 +1,38 @@
package service
import (
"context"
"go-common/app/admin/ep/merlin/model"
"go-common/library/ecode"
)
// UpdateMachineNode update the node of machine
func (s *Service) UpdateMachineNode(c context.Context, umnr *model.UpdateMachineNodeRequest) (err error) {
var (
m *model.Machine
res string
)
if m, err = s.dao.QueryMachine(umnr.MachineID); err != nil {
return
}
if res, err = s.dao.UpdatePaasMachineNode(c, model.NewPaasUpdateMachineNodeRequest(m.ToPaasQueryAndDelMachineRequest(), umnr.Nodes)); err != nil {
return
}
if res != model.Success {
err = ecode.MerlinUpdateNodeErr
return
}
if err = s.dao.DelMachineNodeByMachineID(umnr.MachineID); err != nil {
return
}
if err = s.dao.GenMachineNodes(umnr.ToMachineNodes()); err != nil {
return
}
return
}
// QueryMachineNodes query nodes by mID
func (s *Service) QueryMachineNodes(mID int64) ([]*model.MachineNode, error) {
return s.dao.QueryMachineNodes(mID)
}

View File

@@ -0,0 +1,68 @@
package service
import (
"context"
"go-common/app/admin/ep/merlin/conf"
"go-common/app/admin/ep/merlin/dao"
"go-common/library/sync/pipeline/fanout"
"github.com/robfig/cron"
)
// Service struct
type Service struct {
c *conf.Config
dao *dao.Dao
cron *cron.Cron
deviceChan *fanout.Fanout
}
// New init.
func New(c *conf.Config) (s *Service) {
s = &Service{
c: c,
dao: dao.New(c),
deviceChan: fanout.New("deviceChan", fanout.Worker(1), fanout.Buffer(1024)),
}
scheduler := c.Scheduler
if scheduler.Active {
s.cron = cron.New()
if err := s.cron.AddFunc(scheduler.GetExpiredMachinesTime, s.taskGetExpiredMachinesIntoTask); err != nil {
panic(err)
}
if err := s.cron.AddFunc(scheduler.SendTaskMailMachinesWillExpiredTime, s.taskSendTaskMailMachinesWillExpired); err != nil {
panic(err)
}
if err := s.cron.AddFunc(scheduler.DeleteExpiredMachinesInTask, s.taskDeleteExpiredMachines); err != nil {
panic(err)
}
if err := s.cron.AddFunc(scheduler.CheckMachinesStatusInTask, s.taskMachineStatus); err != nil {
panic(err)
}
if err := s.cron.AddFunc(scheduler.UpdateMobileDeviceInTask, s.taskSyncMobileDeviceList); err != nil {
panic(err)
}
if err := s.cron.AddFunc(scheduler.UpdateSnapshotStatusInDoing, s.taskUpdateSnapshotStatusInDoing); err != nil {
panic(err)
}
s.cron.Start()
}
return
}
// Close Service.
func (s *Service) Close() {
s.dao.Close()
}
// Ping check server ok.
func (s *Service) Ping(c context.Context) (err error) {
err = s.dao.Ping(c)
return
}
// ConfVersion Conf Version.
func (s *Service) ConfVersion(c context.Context) string {
return conf.Conf.Version
}

View File

@@ -0,0 +1,34 @@
package service
import (
"context"
"flag"
"path/filepath"
"strings"
"time"
"go-common/app/admin/ep/merlin/conf"
"gopkg.in/h2non/gock.v1"
)
var (
s *Service
c context.Context
)
func init() {
dir, _ := filepath.Abs("../cmd/convey-test.toml")
flag.Set("conf", dir)
conf.Init()
s = New(conf.Conf)
c = context.Background()
time.Sleep(time.Second)
s.dao.SetProxy()
}
func httpMock(method, url string) *gock.Request {
r := gock.New(url)
r.Method = strings.ToUpper(method)
return r
}

View File

@@ -0,0 +1,151 @@
package service
import (
"context"
"go-common/app/admin/ep/merlin/model"
"go-common/library/log"
)
func (s *Service) taskGetExpiredMachinesIntoTask() {
machines, err := s.dao.FindExpiredMachine()
if err != nil {
log.Error("Task get expired machines into task (%v)", err)
return
}
if machines != nil {
log.Info("machines will be expired on tomorrow and add into task")
s.dao.InsertDeleteMachinesTasks(machines)
}
}
// 定时发邮件通知将要过期机器
func (s *Service) taskSendTaskMailMachinesWillExpired() {
var (
machines []*model.Machine
err error
)
if machines, err = s.dao.FindExpiredMachineByDay(s.c.Scheduler.ExpiredDate); err != nil {
log.Error("Task send task mail machines will expired (%v)", err)
return
}
for _, machine := range machines {
log.Info("Machine named [%s] will be expired on next week and send a mail", machine.Name)
if machine.DelayStatus == model.DelayStatusInit {
s.dao.UpdateMachineDelayStatus(machine.ID, model.DelayStatusAuto)
}
if err = s.SendMail(model.MailTypeMachineWillExpired, machine); err != nil {
log.Error("Send mail failed (%v)", err)
}
}
}
// 定时删除过期机器
func (s *Service) taskDeleteExpiredMachines() {
var (
tasks []*model.Task
instance *model.ReleaseInstance
err error
machine *model.Machine
)
if tasks, err = s.dao.FindDeleteMachineTasks(); err != nil {
log.Error("Task delete expired machines error (%v)", err)
return
}
for _, taskEle := range tasks {
if instance, err = s.DelMachine(context.TODO(), taskEle.MachineID, "机器删除"); err != nil {
log.Error("Task delete expired machines error (%v)", err)
continue
}
if instance.InstanceReleaseStatus != model.SuccessDeletePaasMachines {
if machine, err = s.dao.QueryMachine(taskEle.MachineID); err != nil {
log.Error("Task delete expired machines error (%v)", err)
continue
}
if err = s.SendMail(model.MailTypeTaskDeleteMachineFailed, machine); err != nil {
log.Error("Send mail failed (%v)", err)
}
s.dao.UpdateTaskStatusByTaskID(taskEle.ID, model.TaskFailed)
}
}
}
func (s *Service) taskMachineStatus() {
var (
pathAndPodNames map[string][]string
machineStatuses map[string]bool
createdPodNames []string
creatingPodNames []string
creatingMachines []*model.Machine
err error
c = context.TODO()
)
if pathAndPodNames, err = s.dao.QueryPathAndPodNamesMapping(); err != nil {
log.Error("Query creating machines in db err(%v)", err)
return
}
if len(pathAndPodNames) == 0 {
return
}
log.Info("Get pathAndPodNames(%v) from Service Tree", pathAndPodNames)
if machineStatuses, err = s.TreeMachinesIsExist(c, pathAndPodNames); err != nil {
log.Error("Query service tree machine status err(%v)", err)
return
}
for k, v := range machineStatuses {
if v {
createdPodNames = append(createdPodNames, k)
} else {
creatingPodNames = append(creatingPodNames, k)
}
}
if len(createdPodNames) > 0 {
if s.dao.UpdateMachineStatusByPodNames(createdPodNames, model.BootMachineInMerlin); err != nil {
log.Error("update creating machines to boot in db err(%v)", err)
return
}
}
if len(creatingPodNames) <= 0 {
return
}
if creatingMachines, err = s.dao.QueryMachinesByPodNames(creatingPodNames); err != nil {
log.Error("Query creating machines in db err(%v)", err)
return
}
for _, m := range creatingMachines {
log.Info("Create machine(%v) deadline exceeded", m)
if _, err = s.verifyPassStatus(c, m, false); err != nil {
log.Error("Del verify machine(%v) in db err(%v)", m, err)
continue
}
if _, err = s.dao.DelPaasMachine(c, m.ToPaasQueryAndDelMachineRequest()); err != nil {
log.Error("Del creating machine(%v) in db err(%v)", m, err)
continue
}
}
}
// 同步ios移动设备状态
func (s *Service) taskSyncMobileDeviceList() {
s.SyncMobileDeviceList(context.Background())
}
// 定时清理由于回调失败,而在进行中的快照
func (s *Service) taskUpdateSnapshotStatusInDoing() {
var (
err error
snapshotRecords []*model.SnapshotRecord
)
if snapshotRecords, err = s.dao.FindSnapshotStatusInDoingOver2Hours(); err != nil {
return
}
for _, snapshotRecord := range snapshotRecords {
if err = s.dao.UpdateSnapshotRecordStatus(snapshotRecord.MachineID, model.SnapShotFailed); err != nil {
continue
}
}
}

View File

@@ -0,0 +1,177 @@
package service
import (
"context"
"go-common/app/admin/ep/merlin/model"
"go-common/library/ecode"
)
const (
_startsWith = "startswith"
_merlinHostnameRegex = ".*-[0-9]-0"
)
// UserTreeAsOption get user tree as option.
func (s *Service) UserTreeAsOption(c context.Context, sessionID string) (firstRetMap []map[string]interface{}, err error) {
var treeMap *model.UserTree
if treeMap, err = s.dao.UserTree(c, sessionID); err != nil {
err = ecode.MerlinGetUserTreeFailed
return
}
if treeMap.Bilibili == nil {
return
}
firstLevelMapTmp := treeMap.Bilibili
if firstLevelMapTmp["children"] == nil {
return
}
firstLevelMap := firstLevelMapTmp["children"].(map[string]interface{})
for firstLevelKey, firstLevelChildren := range firstLevelMap {
secondLevelMapTmp := firstLevelChildren.(map[string]interface{})
if secondLevelMapTmp["children"] == nil {
continue
}
secondLevelMap := secondLevelMapTmp["children"].(map[string]interface{})
var secondRetMap []map[string]interface{}
for secondLevelKey, secondLevelChildren := range secondLevelMap {
thirdLevelMapTmp := secondLevelChildren.(map[string]interface{})
if thirdLevelMapTmp["children"] == nil {
continue
}
thirdLevelMap := thirdLevelMapTmp["children"].(map[string]interface{})
var thirdRetMap []map[string]interface{}
for thirdLevelKey, thirdLevelChildren := range thirdLevelMap {
if thirdLevelKey != "" && thirdLevelChildren != nil {
childID := int64(thirdLevelChildren.(map[string]interface{})["id"].(float64))
thirdTmp := make(map[string]interface{})
thirdTmp["label"] = thirdLevelKey
v := new(struct {
ID int64 `json:"id"`
Name string `json:"name"`
})
v.ID = childID
v.Name = thirdLevelKey
thirdTmp["value"] = v
thirdRetMap = append(thirdRetMap, thirdTmp)
}
}
if secondLevelKey != "" {
secondTmp := make(map[string]interface{})
secondTmp["value"] = secondLevelKey
secondTmp["label"] = secondLevelKey
secondTmp["children"] = thirdRetMap
secondRetMap = append(secondRetMap, secondTmp)
}
}
if firstLevelKey != "" {
firstTmp := make(map[string]interface{})
firstTmp["value"] = firstLevelKey
firstTmp["label"] = firstLevelKey
firstTmp["children"] = secondRetMap
firstRetMap = append(firstRetMap, firstTmp)
}
}
return
}
// VerifyTreeContainerNode verify tree containers.
func (s *Service) VerifyTreeContainerNode(c context.Context, sessionID string, tnr *model.TreeNode) (err error) {
var (
sonMap map[string]interface{}
ok bool
)
if sonMap, err = s.dao.TreeSon(c, sessionID, tnr.TreePath()); err != nil {
err = ecode.MerlinGetUserTreeFailed
return
}
if sonMap, ok = sonMap["dev"].(map[string]interface{}); !ok {
err = ecode.MerlinLoseTreeContainerNodeErr
return
}
if sonMap, ok = sonMap["children"].(map[string]interface{}); !ok {
err = ecode.MerlinLoseTreeContainerNodeErr
return
}
if sonMap["containers"] == nil {
err = ecode.MerlinLoseTreeContainerNodeErr
}
return
}
// TreeRoleAsAuditor tree role as auditor.
func (s *Service) TreeRoleAsAuditor(c context.Context, sessionID, firstNode string) (auditors []string, err error) {
var treeRoles []*model.TreeRole
if treeRoles, err = s.dao.TreeRoles(c, sessionID, firstNode); err != nil {
return
}
for _, treeRole := range treeRoles {
if treeRole.Role == model.TreeRoleAdmin {
auditors = append(auditors, treeRole.UserName)
}
}
return
}
// TreeMachineIsExist tree machine is exist.
func (s *Service) TreeMachineIsExist(c context.Context, podName string, tn *model.TreeNode) (b bool, err error) {
var (
pathAndPodNameMap = make(map[string][]string)
result map[string]bool
)
pathAndPodNameMap[tn.TreePath()] = []string{podName}
if result, err = s.TreeMachinesIsExist(c, pathAndPodNameMap); err != nil {
return
}
b = result[podName]
return
}
// TreeMachinesIsExist tree machines is exist.
func (s *Service) TreeMachinesIsExist(c context.Context, pathAndMachinesMap map[string][]string) (machinesIsExist map[string]bool, err error) {
var (
treeAppInstances map[string][]*model.TreeAppInstance
paths []string
)
machinesIsExist = make(map[string]bool)
for path, machineNames := range pathAndMachinesMap {
paths = append(paths, path)
for _, machineName := range machineNames {
machinesIsExist[machineName] = false
}
}
if treeAppInstances, err = s.dao.TreeAppInstance(c, paths); err != nil {
return
}
for _, machinesInTree := range treeAppInstances {
for _, machineInTree := range machinesInTree {
if _, ok := machinesIsExist[machineInTree.HostName]; ok {
machinesIsExist[machineInTree.HostName] = true
}
}
}
return
}
// QueryTreeInstanceForMerlin query TreeInstance for merlin.
func (s *Service) QueryTreeInstanceForMerlin(c context.Context, sessionID string, tn *model.TreeNode) (res map[string]*model.TreeInstance, err error) {
tir := &model.TreeInstanceRequest{
HostnameRegex: _merlinHostnameRegex,
}
if path := tn.TreePathWithoutEmptyField(); path != "" {
tir.Path = path
tir.PathFuzzy = _startsWith
}
res, err = s.dao.QueryTreeInstances(c, sessionID, tir)
return
}

View File

@@ -0,0 +1,188 @@
package service
import (
"testing"
"go-common/app/admin/ep/merlin/model"
. "github.com/smartystreets/goconvey/convey"
)
var (
sessionID = "37556f106c8eb7b495c7986e6339fabc"
)
func Test_Tree(t *testing.T) {
Convey("test TreeSon", t, func() {
treeURI := s.c.ServiceTree.Host + "/v1/node/extree" + "/bilibili.test.benchmark.jemeter"
httpMock("GET", treeURI).Reply(200).JSON(getTreeSonJSONResponse)
err := s.VerifyTreeContainerNode(c, sessionID, &model.TreeNode{BusinessUnit: "test", Project: "benchmark", App: "jemeter"})
So(err, ShouldBeNil)
})
}
var (
getTreeSonJSONResponse = `{
"code": 90000,
"data": {
"dev": {
"id": 19024,
"name": "dev",
"alias": "研发环境",
"uuid": "",
"type": 5,
"path": "bilibili.test.benchmark.jemeter.dev",
"tags": {},
"children": {
"containers": {
"id": 19029,
"name": "containers",
"alias": "容器",
"uuid": "",
"type": 6,
"path": "bilibili.test.benchmark.jemeter.dev.containers",
"tags": {},
"children": null
},
"servers": {
"id": 19028,
"name": "servers",
"alias": "物理机/虚机",
"uuid": "",
"type": 6,
"path": "bilibili.test.benchmark.jemeter.dev.servers",
"tags": {},
"children": null
}
}
},
"fat1": {
"id": 19023,
"name": "fat1",
"alias": "功能环境1",
"uuid": "",
"type": 5,
"path": "bilibili.test.benchmark.jemeter.fat1",
"tags": {},
"children": {
"containers": {
"id": 19031,
"name": "containers",
"alias": "容器",
"uuid": "",
"type": 6,
"path": "bilibili.test.benchmark.jemeter.fat1.containers",
"tags": {},
"children": null
},
"servers": {
"id": 19030,
"name": "servers",
"alias": "物理机/虚机",
"uuid": "",
"type": 6,
"path": "bilibili.test.benchmark.jemeter.fat1.servers",
"tags": {},
"children": null
}
}
},
"pre": {
"id": 19025,
"name": "pre",
"alias": "预发布环境",
"uuid": "",
"type": 5,
"path": "bilibili.test.benchmark.jemeter.pre",
"tags": {},
"children": {
"containers": {
"id": 19033,
"name": "containers",
"alias": "容器",
"uuid": "",
"type": 6,
"path": "bilibili.test.benchmark.jemeter.pre.containers",
"tags": {},
"children": null
},
"servers": {
"id": 19032,
"name": "servers",
"alias": "物理机/虚机",
"uuid": "",
"type": 6,
"path": "bilibili.test.benchmark.jemeter.pre.servers",
"tags": {},
"children": null
}
}
},
"prod": {
"id": 19026,
"name": "prod",
"alias": "生产环境",
"uuid": "",
"type": 5,
"path": "bilibili.test.benchmark.jemeter.prod",
"tags": {},
"children": {
"containers": {
"id": 19035,
"name": "containers",
"alias": "容器",
"uuid": "",
"type": 6,
"path": "bilibili.test.benchmark.jemeter.prod.containers",
"tags": {},
"children": null
},
"servers": {
"id": 19034,
"name": "servers",
"alias": "物理机/虚机",
"uuid": "",
"type": 6,
"path": "bilibili.test.benchmark.jemeter.prod.servers",
"tags": {},
"children": null
}
}
},
"uat": {
"id": 19027,
"name": "uat",
"alias": "集成环境",
"uuid": "",
"type": 5,
"path": "bilibili.test.benchmark.jemeter.uat",
"tags": {},
"children": {
"containers": {
"id": 19037,
"name": "containers",
"alias": "容器",
"uuid": "",
"type": 6,
"path": "bilibili.test.benchmark.jemeter.uat.containers",
"tags": {},
"children": null
},
"servers": {
"id": 19036,
"name": "servers",
"alias": "物理机/虚机",
"uuid": "",
"type": 6,
"path": "bilibili.test.benchmark.jemeter.uat.servers",
"tags": {},
"children": null
}
}
}
},
"message": "success",
"status": 200
}`
)

View File

@@ -0,0 +1,17 @@
package service
import (
"context"
"go-common/app/admin/ep/merlin/model"
)
// QueryUserInfo query user info.
func (s *Service) QueryUserInfo(c context.Context, username string) (userInfo *model.User, err error) {
if userInfo, err = s.dao.FindUserByUserName(username); err != nil {
user := model.User{Name: username, EMail: username + "@bilibili.com"}
s.dao.CreateUser(&user)
userInfo, err = s.dao.FindUserByUserName(username)
}
return
}

View File

@@ -0,0 +1,16 @@
package service
import (
"context"
"testing"
. "github.com/smartystreets/goconvey/convey"
)
func Test_QueryUserInfo(t *testing.T) {
Convey("query user when user exists", t, func() {
userInfo, err := s.QueryUserInfo(context.Background(), "fengyifeng")
So(err, ShouldBeNil)
So(userInfo.EMail, ShouldEqual, "fengyifeng@bilibili.com")
})
}