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

22
app/admin/ep/merlin/BUILD Normal file
View File

@@ -0,0 +1,22 @@
package(default_visibility = ["//visibility:public"])
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [
":package-srcs",
"//app/admin/ep/merlin/cmd:all-srcs",
"//app/admin/ep/merlin/conf:all-srcs",
"//app/admin/ep/merlin/dao:all-srcs",
"//app/admin/ep/merlin/http:all-srcs",
"//app/admin/ep/merlin/model:all-srcs",
"//app/admin/ep/merlin/service:all-srcs",
],
tags = ["automanaged"],
)

View File

@@ -0,0 +1,126 @@
##### ep-merlin
##### Version 1.9.2
1.修改手动延期bug
##### Version 1.9.1
1.创建非快照机器 保存镜像名称
##### Version 1.8.9
1.不支持uat创建虚拟机
##### Version 1.8.8
1.创建中机器无法删除
##### Version 1.8.7
1.删除二进制文件
##### Version 1.8.6
1.镜像过滤size为0同时排序
##### Version 1.8.5
1.对接paas支持超配比配置
##### Version 1.8.4
1.移除配置文件mima
##### Version 1.8.3
1.增加dashboard接口
##### Version 1.8.2
1.创建机器接口更新
##### Version 1.8.1
1. 邮件收件人不存在 会通知noticeOwner
2. 轮询快照记录状态
##### Version 1.8.0
1. 增加设备农场借出归还流程
2. 虚拟机支持转移他人
##### Version 1.7.2
1. 配置文件去除密码
##### Version 1.7
1 添加镜像专属chan并增加多任务处理
2 增加微信通知
3 修改配置文件
##### Version 1.6
1 支持带快照的虚拟机 制作成镜像
2 创建机器镜像选择列表对接hub
##### Version 1.5.5.3
1 快照功能调优
##### Version 1.5.5
1 对接DeviceFarm机器轮询更新机制优化
2 对接DeviceFarm前端和修改接口
##### Version 1.5.1
1 对接DeviceFarm机器轮询更新机制加强
2 对接DeviceFarm前端修改接口
3 增加主动snapshot功能
##### Version 1.5.0
1 对接hub支持镜像查询删除等
2 支持docker api操作下载retag和上传镜像
##### Version 1.4.0
1 日志列表增加type类型移动设备日志也存该该表
2 对接DeviceFarm 管理移动设备,支持查询,绑定,解绑,开机,关机
##### Version 1.3.0
1 机器列表,默认按用户名查询
2 日志列表,支持多条件查询
3 删除别人名下机器后,会邮寄通知双方
4 机器列表,按机器名模糊查询
##### Version 1.2.0
1. 创建机器支持关联至服务树多节点
2. 创建机器支持配置启动命令, hosts, 环境变量参数
3. 机器的节点支持管理(新增, 删除, 更新)
4. 修改机器查询接口支持从多节点中过滤
5. 创建机器外接硬盘支持填写挂载目录
6. 修复节点树应该小于1 d
##### Version 1. 1. 0
1. Merlin 二期添加审批流程
2. 新增status 状态查询
3. 新增定时任务管理创建过久机器
4. 修改status取值范围
5. 修复machine log 总数错误问题
6. 修改数据库字段名定义, 将大写变为小写
7. 修复查询机器所有人都能看到创建失败机器bug
8. 增加镜像逻辑删除功能
9. 在新建机器之前, 验证当前是否包含containers子节点
10. 支持查询所有机器时, 返回机器 ip 地址
##### Version 1. 0. 1
1. 修改image的update_time
2. 增加定时任务验证机器是否创建成功
3. 新增删除机器Hook
4. 修改UpdateTime为Utime, CreateTime为Ctime
5. 新增机器POD_NAME字段
6. 修改机器内存的单位为: G
7. 将DelMachine 修改为支持中间件方法
8. 修改定时任务, 采用服务树接口确定状态
##### Version 1. 0. 0
1. 机器申请
2. 机器销毁
3. 修改 business/ecode 目录到 ecode 目录下面
4. 添加ping测试接口
5. 修改分页参数和删除 machine 前缀配置
6. 修改数据库返回, 改成使用指针
7. 修复数据库时间为空问题
8. 修复 treeId 层级错误问题
9. 修复查询机器中 username 需要模糊匹配问题
10. machine log 查询接口返回为中文

View File

@@ -0,0 +1,14 @@
# Owner
maojian
yuanmin
fengyifeng
xuneng
# Author
yuanmin
fengyifeng
xuneng
# Reviewer
zhapuyu
wangxu01

View File

@@ -0,0 +1,19 @@
# See the OWNERS docs at https://go.k8s.io/owners
approvers:
- fengyifeng
- maojian
- xuneng
- yuanmin
labels:
- admin
- admin/ep/merlin
- ep
options:
no_parent_owners: true
reviewers:
- fengyifeng
- wangxu01
- xuneng
- yuanmin
- zhapuyu

View File

@@ -0,0 +1,19 @@
# merlin
# 项目简介
### 背景/Background
* 测试环境的维护过往由少数同学兼任,由于权限过于集中、缺乏生命周期管理,导致测试环境的资源维护困难且集中。
* 接下来测试环境的维护工作将逐步交由研发,需要权限下放、生命周期管理。 Merlin/梅林 孕育而生。
### 概览Overview
* Merlin会基于Caster/PaaS的底层资源提供测试环境开发机的自助申请创建和管理支持资源归属与Tree/服务树保持一致,权限打通操作机/堡垒机。
# 编译环境
# 依赖包
# 编译执行

View File

@@ -0,0 +1,46 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_binary",
"go_library",
)
go_binary(
name = "cmd",
embed = [":go_default_library"],
tags = ["automanaged"],
)
go_library(
name = "go_default_library",
srcs = ["main.go"],
data = [
"convey-test.toml",
"merlin-test.toml",
],
importpath = "go-common/app/admin/ep/merlin/cmd",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/admin/ep/merlin/conf:go_default_library",
"//app/admin/ep/merlin/http:go_default_library",
"//app/admin/ep/merlin/service:go_default_library",
"//library/ecode/tip:go_default_library",
"//library/log: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"],
)

View File

@@ -0,0 +1,168 @@
# This is a TOML document. Boom
version = "1.0.0"
user = "nobody"
pid = "/tmp/merlin.pid"
dir = "./"
perf = "0.0.0.0:6420"
[log]
dir = "/data/merlin/log"
[bm]
[bm.inner]
addr = "0.0.0.0:9001"
maxListen = 10
timeout = "1s"
[bm.local]
addr = "0.0.0.0:9002"
maxListen = 10
timeout = "1s"
[httpClient]
key = "c05dd4e1638a8af0"
secret = "7daa7f8c06cd33c5c3067063c746fdcb"
dial = "2s"
timeout = "10s"
keepAlive = "60s"
timer = 1000
[httpClient.breaker]
window = "10s"
sleep = "2000ms"
bucket = 10
ratio = 0.5
request = 100
[identify]
whiteAccessKey = ""
whiteMid = 0
[identify.app]
key = "6a29f8ed87407c11"
secret = "d3c5a85f5b895a03735b5d20a273bc57"
[identify.memcache]
name = "go-business/identify"
proto = "tcp"
addr = "172.16.33.54:11211"
active = 5
idle = 1
dialTimeout = "2s"
readTimeout = "2s"
writeTimeout = "2s"
idleTimeout = "80s"
[identify.host]
auth = "http://passport.bilibili.com"
secret = "http://open.bilibili.com"
[identify.httpClient]
key = "f022126a8a365e20"
secret = "b7b86838145d634b487e67b811b8fab2"
dial = "3000ms"
timeout = "2000ms"
keepAlive = "60s"
[identify.httpClient.breaker]
window = "10s"
sleep = "100ms"
bucket = 10
ratio = 0.5
request = 100
[identify.httpClient.url]
"http://passport.bilibili.co/intranet/auth/tokenInfo" = {timeout = "100ms"}
"http://passport.bilibili.co/intranet/auth/cookieInfo" = {timeout = "100ms"}
"http://open.bilibili.co/api/getsecret" = {timeout = "500ms"}
[ecode]
domain = "uat-api.bilibili.co"
all = "1h"
diff = "5m"
[ecode.clientconfig]
key = "c1a1cb2d89c33794"
secret = "dda47eeca111e03e6845017505baea13"
dial = "2000ms"
timeout = "2s"
keepAlive = "10s"
timer = 128
[ecode.clientconfig.breaker]
window ="3s"
sleep ="100ms"
bucket = 10
ratio = 0.5
request = 100
[managerAuth]
servicetreeHost = "http://easyst.bilibili.co"
dashboardHost = "http://dashboard-mng.bilibili.co"
dashboardCaller = "merlin"
[managerAuth.dsHTTPClient]
key = "merlin"
secret = "4fb521f66dfd5efcf6e77d078ed2eb0a"
dial = "2s"
timeout = "3s"
keepAlive = "60s"
timer = 1000
[managerAuth.dsHTTPClient.breaker]
window ="3s"
sleep ="200ms"
bucket = 10
ratio = 0.5
request = 100
[managerAuth.stHTTPClient]
key = "merlin"
secret = "4fb521f66dfd5efcf6e77d078ed2eb0a"
dial = "2s"
timeout = "3s"
keepAlive = "60s"
timer = 1000
[managerAuth.stHTTPClient.breaker]
window ="3s"
sleep ="100ms"
bucket = 10
ratio = 0.5
request = 100
[paas]
host = "http://172.16.38.89"
token = "ngriEorfjzbdyyQ2cdqMYcNxovazPD6Cbte"
machineTimeout = "CURRENT_TIMESTAMP - INTERVAL 20 MINUTE"
[serviceTree]
host = "http://easyst.bilibili.co"
key = "merlin"
secret = "rhRp[Lfnfrp9Jypr7aaJMGn8NC.[E+Gvb9&nRazs6Mm{fEW98.z9yzV*phu)U97#"
[mail]
host = "smtp.exmail.qq.com"
port = 465
username = "merlin@bilibili.com"
password = ""
[Scheduler]
#每天晚上23点查找第二天将要过期机器放入任务表
GetExpiredMachinesTime = "0 0 23 * * ?"
#每天早上10点查找一周后要过期的机器并发送邮件
#SendTaskMailMachinesWillExpiredTime = "*/15 * * * * ?"
SendTaskMailMachinesWillExpiredTime = "0 0 10 * * ?"
#每隔五分钟删除机器
DeleteExpiredMachinesInTask = "0 */5 * * * ?"
#每隔二十分钟检测机器状态
CheckMachinesStatusInTask = "0 */20 * * * ?"
[orm]
dsn = "root:123456@tcp(172.18.33.130:3306)/merlin?timeout=2000ms&readTimeout=2000ms&writeTimeout=2000ms&parseTime=true&loc=Local&charset=utf8,utf8mb4"
active = 5
idle = 5
idleTimeout = "4h"
[memcache]
name = "merlin"
proto = "tcp"
addr = "172.16.33.54:11211"
idle = 5
active = 10
dialTimeout = "1s"
readTimeout = "1s"
writeTimeout = "1s"
idleTimeout = "10s"
expire = "12h"

View File

@@ -0,0 +1,54 @@
package main
import (
"flag"
"os"
"os/signal"
"syscall"
"time"
"go-common/app/admin/ep/merlin/conf"
"go-common/app/admin/ep/merlin/http"
"go-common/app/admin/ep/merlin/service"
ecode "go-common/library/ecode/tip"
"go-common/library/log"
)
const (
_durationForClosingServer = 2000
)
func main() {
flag.Parse()
if err := conf.Init(); err != nil {
log.Error("conf.Init() error(%v)", err)
panic(err)
}
// init log
log.Init(conf.Conf.Log)
defer log.Close()
log.Info("merlin start")
// ecode init
ecode.Init(conf.Conf.Ecode)
// service init
s := service.New(conf.Conf)
http.Init(conf.Conf, s)
// init pprof conf.Conf.Perf
// init signal
c := make(chan os.Signal, 1)
signal.Notify(c, syscall.SIGHUP, syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGINT)
for {
si := <-c
log.Info("merlin get a signal %s", si.String())
switch si {
case syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGINT:
log.Info("merlin exit")
s.Close()
time.Sleep(_durationForClosingServer)
return
case syscall.SIGHUP:
default:
return
}
}
}

View File

@@ -0,0 +1,196 @@
version = "2.0.0"
user = "nobody"
pid = "/tmp/merlin.pid"
dir = "./"
[bm]
addr = "0.0.0.0:9001"
maxListen = 10000
timeout = "1000s"
[httpClient]
key = "c05dd4e1638a8af0"
secret = "7daa7f8c06cd33c5c3067063c746fdcb"
dial = "2s"
timeout = "10s"
keepAlive = "60s"
timer = 1000
[httpClient.breaker]
window = "10s"
sleep = "2000ms"
bucket = 10
ratio = 0.5
request = 100
[deviceFarm]
hostList = ["10.23.35.200:3000","10.23.35.201:3000","10.23.35.202:3000","10.23.35.203:3000"]
applyMonthTime = 3
superOwner = ["fengyifeng","yuanmin"]
[identify]
whiteAccessKey = ""
whiteMid = 0
[identify.app]
key = "6a29f8ed87407c11"
secret = "d3c5a85f5b895a03735b5d20a273bc57"
[identify.memcache]
name = "go-business/identify"
proto = "tcp"
addr = "172.16.33.54:11211"
active = 5
idle = 1
dialTimeout = "2s"
readTimeout = "2s"
writeTimeout = "2s"
idleTimeout = "80s"
[identify.host]
auth = "http://passport.bilibili.com"
secret = "http://open.bilibili.com"
[identify.httpClient]
key = "f022126a8a365e20"
secret = "b7b86838145d634b487e67b811b8fab2"
dial = "3000ms"
timeout = "2000ms"
keepAlive = "60s"
[identify.httpClient.breaker]
window = "10s"
sleep = "100ms"
bucket = 10
ratio = 0.5
request = 100
[identify.httpClient.url]
"http://passport.bilibili.co/intranet/auth/tokenInfo" = {timeout = "100ms"}
"http://passport.bilibili.co/intranet/auth/cookieInfo" = {timeout = "100ms"}
"http://open.bilibili.co/api/getsecret" = {timeout = "500ms"}
[paas]
host = "http://10.23.26.21:8612"
token = "ngriEorfjzbdyyQ2cdqMYcNxovazPD6Cbte"
machineTimeout = "CURRENT_TIMESTAMP - INTERVAL 20 MINUTE"
machineLimitRatio = 4.0
[serviceTree]
host = "http://easyst.bilibili.co"
key = "merlin"
secret = "rhRp[Lfnfrp9Jypr7aaJMGn8NC.[E+Gvb9&nRazs6Mm{fEW98.z9yzV*phu)U97#"
[biliHub]
host = "https://hub.bilibili.co"
hostName = "hub.bilibili.co"
username = "merlin"
password = ""
merlinPub = "merlinlibrary"
sharePub = "merlinpubliclibrary"
machineTagPri = "merlinprivatelibrary"
supportNetWork = ["fat"]
superOwner = ["fengyifeng1","xuneng"]
[mail]
host = "smtp.exmail.qq.com"
port = 465
username = "merlin@bilibili.com"
password = "Quality1#"
noticeOwner = ["fengyifeng@bilibili.com"]
[Scheduler]
#每天晚上23点查找第二天将要过期机器放入任务表
#GetExpiredMachinesTime = "*/15 * * * * ?"
getExpiredMachinesTime = "0 0 23 * * ?"
#每天早上10点查找一周后要过期的机器并发送邮件
sendTaskMailMachinesWillExpiredTime = "0 0 10 * * ?"
expiredDate = 7
#每隔五分钟删除机器
deleteExpiredMachinesInTask = "*/20 * * * * ?"
#DeleteExpiredMachinesInTask = "0 */5 * * * ?"
#每隔二十分钟检测机器状态
checkMachinesStatusInTask = "* */50 * * * ?"
#每天10分钟更新device表
updateMobileDeviceInTask = "0 */10 * * * ?"
#每10分钟检查快照状态
updateSnapshotStatusInDoing = "0 */10 * * * ?"
active = false
[orm]
dsn = "root:123456@tcp(172.18.33.130:3306)/uat-merlin?timeout=200ms&readTimeout=2000ms&writeTimeout=2000ms&parseTime=true&loc=Local&charset=utf8,utf8mb4"
active = 5
idle = 5
idleTimeout = "4h"
[memcache]
name = "merlin"
proto = "tcp"
addr = "172.22.33.137:11216"
idle = 5
active = 10
dialTimeout = "1s"
readTimeout = "1s"
writeTimeout = "1s"
idleTimeout = "10s"
expire = "12h"
[auth]
managerHost = "http://uat-manager.bilibili.co"
dashboardHost = "http://dashboard-mng.bilibili.co"
dashboardCaller = "merlin"
[auth.DsHTTPClient]
key = "merlin"
secret = "4fb521f66dfd5efcf6e77d078ed2eb0a"
dial = "1s"
timeout = "1s"
keepAlive = "60s"
[auth.DsHTTPClient.breaker]
window = "3s"
sleep = "100ms"
bucket = 10
ratio = 0.5
request = 100
[auth.MaHTTPClient]
key = "f6433799dbd88751"
secret = "36f8ddb1806207fe07013ab6a77a3935"
dial = "1ms"
timeout = "1ms"
keepAlive = "60s"
[auth.MaHTTPClient.breaker]
window = "3s"
sleep = "100ms"
bucket = 10
ratio = 0.5
request = 100
[auth.session]
sessionIDLength = 32
cookieLifeTime = 1
cookieName = "mng-go"
domain = ".bilibili.co"
[auth.session.Memcache]
name = "go-business/auth"
proto = "tcp"
addr = "172.16.33.54:11211"
active = 10
idle = 10
dialTimeout = "1ms"
readTimeout = "1ms"
writeTimeout = "1ms"
idleTimeout = "80s"
[wechat]
wechatHost="http://172.16.63.43:8000"
[wechat.wechatdevicefarm]
chatid="devicefarm"
msgtype="text"
safe=0
senMessage=true

View File

@@ -0,0 +1,40 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = ["conf.go"],
importpath = "go-common/app/admin/ep/merlin/conf",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/admin/ep/merlin/model:go_default_library",
"//library/cache/memcache:go_default_library",
"//library/conf:go_default_library",
"//library/database/orm:go_default_library",
"//library/ecode/tip:go_default_library",
"//library/log:go_default_library",
"//library/net/http/blademaster:go_default_library",
"//library/net/http/blademaster/middleware/permit:go_default_library",
"//library/time:go_default_library",
"//vendor/github.com/BurntSushi/toml: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"],
)

View File

@@ -0,0 +1,162 @@
package conf
import (
"errors"
"flag"
"go-common/app/admin/ep/merlin/model"
"go-common/library/cache/memcache"
"go-common/library/conf"
"go-common/library/database/orm"
ecode "go-common/library/ecode/tip"
"go-common/library/log"
bm "go-common/library/net/http/blademaster"
"go-common/library/net/http/blademaster/middleware/permit"
"go-common/library/time"
"github.com/BurntSushi/toml"
)
// global var
var (
confPath string
client *conf.Client
// Conf config
Conf = &Config{}
)
// Memcache memcache.
type Memcache struct {
*memcache.Config
Expire time.Duration
}
// Config config set
type Config struct {
Version string `toml:"version"`
// base
// elk
Log *log.Config
// http
BM *bm.ServerConfig
// ecode
Ecode *ecode.Config
HTTPClient *bm.ClientConfig
Memcache *Memcache
// orm
ORM *orm.Config
Paas *model.PaasConf
ServiceTree *model.TreeConf
Mail *Mail
Scheduler *Scheduler
Auth *permit.Config
BiliHub *BiliHub
DeviceFarm *DeviceFarm
WeChat *WeChat
}
//WeChat WeChat config
type WeChat struct {
WeChatHost string
WeChatDeviceFarm *WeChatDeviceFarm
}
// WeChatDeviceFarm WeChatDeviceFarm.
type WeChatDeviceFarm struct {
ChatID string
MsgType string
Safe int
SendMessage bool
}
//DeviceFarm DeviceFarm
type DeviceFarm struct {
HostList []string
SuperOwner []string
ApplyMonthTime int
}
// Scheduler scheduler
type Scheduler struct {
GetExpiredMachinesTime string
SendTaskMailMachinesWillExpiredTime string
DeleteExpiredMachinesInTask string
CheckMachinesStatusInTask string
UpdateMobileDeviceInTask string
UpdateSnapshotStatusInDoing string
Active bool
ExpiredDate int
}
// Mail mail
type Mail struct {
Host string
Port int
Username string
Password string
NoticeOwner []string
}
// BiliHub BiliHub.
type BiliHub struct {
Host string
HostName string
Username string
Password string
MerlinPub string
SharePub string
MachineTagPri string
SupportNetWork []string
SuperOwner []string
}
func init() {
flag.StringVar(&confPath, "conf", "", "default config path")
}
// Init init conf
func Init() error {
if confPath != "" {
return local()
}
return remote()
}
func local() (err error) {
_, err = toml.DecodeFile(confPath, &Conf)
return
}
func remote() (err error) {
if client, err = conf.New(); err != nil {
return
}
return load()
}
func load() (err error) {
var (
s string
ok bool
tmpConf *Config
)
if s, ok = client.Toml2(); !ok {
return errors.New("load config center error")
}
if _, err = toml.Decode(s, &tmpConf); err != nil {
return errors.New("could not decode config")
}
*Conf = *tmpConf
return
}

View File

@@ -0,0 +1,102 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
"go_test",
)
go_library(
name = "go_default_library",
srcs = [
"bilihub.go",
"dao.go",
"dashboard.go",
"devicefarm.go",
"docker.go",
"mail.go",
"mysql_application_record.go",
"mysql_dashboard.go",
"mysql_hubimage_confs.go",
"mysql_hubimage_logs.go",
"mysql_image.go",
"mysql_machine.go",
"mysql_machineV2.go",
"mysql_machine_log.go",
"mysql_machine_node.go",
"mysql_machine_package.go",
"mysql_mail_log.go",
"mysql_mobile_machine.go",
"mysql_mobile_machine_error_log.go",
"mysql_mobile_machine_log.go",
"mysql_mobile_sync_log.go",
"mysql_snapshot_record.go",
"mysql_task.go",
"mysql_user.go",
"paas.go",
"tree.go",
"wechat.go",
],
importpath = "go-common/app/admin/ep/merlin/dao",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/admin/ep/merlin/conf:go_default_library",
"//app/admin/ep/merlin/model:go_default_library",
"//library/cache/memcache:go_default_library",
"//library/database/orm:go_default_library",
"//library/ecode:go_default_library",
"//library/log:go_default_library",
"//library/net/http/blademaster:go_default_library",
"//library/sync/pipeline/fanout:go_default_library",
"//vendor/github.com/docker/docker/api/types:go_default_library",
"//vendor/github.com/docker/docker/api/types/filters:go_default_library",
"//vendor/github.com/docker/docker/client:go_default_library",
"//vendor/github.com/jinzhu/gorm:go_default_library",
"//vendor/github.com/pkg/errors:go_default_library",
"//vendor/gopkg.in/gomail.v2:go_default_library",
"//vendor/gopkg.in/h2non/gock.v1: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 = [
"dao_test.go",
"mail_test.go",
"mysql_image_test.go",
"mysql_machine_log_test.go",
"mysql_machine_test.go",
"mysql_mail_log_test.go",
"mysql_task_test.go",
"mysql_user_test.go",
"paas_test.go",
"tree_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",
"//library/log:go_default_library",
"//vendor/github.com/go-sql-driver/mysql:go_default_library",
"//vendor/github.com/smartystreets/goconvey/convey:go_default_library",
"//vendor/gopkg.in/gomail.v2:go_default_library",
"//vendor/gopkg.in/h2non/gock.v1:go_default_library",
],
)

View File

@@ -0,0 +1,169 @@
package dao
import (
"context"
"fmt"
"net/http"
"strconv"
"go-common/app/admin/ep/merlin/model"
"go-common/library/ecode"
"go-common/library/log"
)
const (
_projectURI = "/api/projects"
_repositoryURI = "/api/repositories"
_authHubURI = "/api/platformUsers?role=2&session="
)
// AuthHub Auth Hub.
func (d *Dao) AuthHub(c context.Context, sessionID string) (err error) {
var (
req *http.Request
res interface{}
)
url := d.c.BiliHub.Host + _authHubURI + sessionID
if req, err = d.newRequest(http.MethodGet, url, nil); err != nil {
return
}
req.SetBasicAuth(d.c.BiliHub.Username, d.c.BiliHub.Password)
if err = d.httpClient.Do(c, req, &res); err != nil {
err = ecode.MerlinNoHubAccount
log.Error("d.AuthHub url(%s) err(%v)", url, err)
}
return
}
// HubProjects Get Hub Projects.
func (d *Dao) HubProjects(c context.Context, projectName string) (projects []*model.HubProject, err error) {
var req *http.Request
url := d.c.BiliHub.Host + _projectURI + "?page=1&page_size=15&name=" + projectName
if req, err = d.newRequest(http.MethodGet, url, nil); err != nil {
return
}
req.SetBasicAuth(d.c.BiliHub.Username, d.c.BiliHub.Password)
if err = d.httpClient.Do(c, req, &projects); err != nil {
log.Error("d.HubProjects url(%s) err(%v)", url, err)
err = ecode.MerlinHubRequestErr
return
}
return
}
// ProjectRepoCount Get Project RepoCount.
func (d *Dao) ProjectRepoCount(c context.Context, projectID int) (total int, err error) {
var (
req *http.Request
res = &model.GetHubProjectDetailResponse{}
)
url := d.c.BiliHub.Host + _projectURI + "/" + strconv.Itoa(projectID)
if req, err = d.newRequest(http.MethodGet, url, nil); err != nil {
return
}
req.SetBasicAuth(d.c.BiliHub.Username, d.c.BiliHub.Password)
if err = d.httpClient.Do(c, req, &res); err != nil {
log.Error("d.ProjectRepoCount url(%s) err(%v)", url, err)
err = ecode.MerlinHubRequestErr
return
}
return res.RepoCount, err
}
// ProjectRepos Get Project Repos.
func (d *Dao) ProjectRepos(c context.Context, projectID, pageNum, pageSize int, keyWord string) (projectRepos []*model.ProjectRepository, err error) {
var req *http.Request
host := d.c.BiliHub.Host + _repositoryURI + "?project_id=%s&page=%s&page_size=%s&q=%s"
url := fmt.Sprintf(host, strconv.Itoa(projectID), strconv.Itoa(pageNum), strconv.Itoa(pageSize), keyWord)
if req, err = d.newRequest(http.MethodGet, url, nil); err != nil {
return
}
req.SetBasicAuth(d.c.BiliHub.Username, d.c.BiliHub.Password)
if err = d.httpClient.Do(c, req, &projectRepos); err != nil {
log.Error("d.ProjectRepos url(%s) err(%v)", url, err)
err = ecode.MerlinHubRequestErr
}
return
}
// RepoTags Get Repo Tags.
func (d *Dao) RepoTags(c context.Context, repoName string) (repoTags []*model.RepositoryTagResponse, err error) {
var req *http.Request
host := d.c.BiliHub.Host + _repositoryURI + "/%s/tags"
url := fmt.Sprintf(host, repoName)
if req, err = d.newRequest(http.MethodGet, url, nil); err != nil {
return
}
req.SetBasicAuth(d.c.BiliHub.Username, d.c.BiliHub.Password)
if err = d.httpClient.Do(c, req, &repoTags); err != nil {
log.Error("d.RepoTags url(%s) err(%v)", url, err)
err = ecode.MerlinHubRequestErr
}
return
}
// DeleteRepoTag Delete Repo Tag.
func (d *Dao) DeleteRepoTag(c context.Context, repoName, tagName string) (err error) {
var req *http.Request
host := d.c.BiliHub.Host + _repositoryURI + "/%s/tags/%s"
url := fmt.Sprintf(host, repoName, tagName)
if req, err = d.newRequest(http.MethodDelete, url, nil); err != nil {
return
}
req.SetBasicAuth(d.c.BiliHub.Username, d.c.BiliHub.Password)
if err = d.httpClient.Do(c, req, nil); err != nil {
log.Error("d.DeleteRepoTag url(%s) err(%v)", url, err)
err = ecode.MerlinHubRequestErr
}
return
}
// DeleteRepo Delete Repo.
func (d *Dao) DeleteRepo(c context.Context, repoName string) (err error) {
var req *http.Request
url := d.c.BiliHub.Host + _repositoryURI + "/" + repoName
if req, err = d.newRequest(http.MethodDelete, url, nil); err != nil {
return
}
req.SetBasicAuth(d.c.BiliHub.Username, d.c.BiliHub.Password)
if err = d.httpClient.Do(c, req, nil); err != nil {
log.Error("d.DeleteRepo url(%s) err(%v)", url, err)
err = ecode.MerlinHubRequestErr
}
return
}
// ImageTask Image Task.
func (d *Dao) ImageTask(imageTask func()) {
d.hubCache.Do(context.Background(), func(c context.Context) {
imageTask()
})
}

View File

@@ -0,0 +1,114 @@
package dao
import (
"bytes"
"context"
"encoding/json"
"net/http"
"time"
"go-common/app/admin/ep/merlin/conf"
"go-common/library/cache/memcache"
"go-common/library/database/orm"
"go-common/library/log"
xhttp "go-common/library/net/http/blademaster"
"go-common/library/sync/pipeline/fanout"
dclient "github.com/docker/docker/client"
"github.com/jinzhu/gorm"
"gopkg.in/gomail.v2"
"gopkg.in/h2non/gock.v1"
)
// Dao dao.
type Dao struct {
c *conf.Config
httpClient *xhttp.Client
db *gorm.DB
email *gomail.Dialer
mc *memcache.Pool
cache *fanout.Fanout
hubCache *fanout.Fanout
expire int32
dockerClient *dclient.Client
}
// New init mysql db.
func New(c *conf.Config) *Dao {
var (
dc *dclient.Client
err error
)
if dc, err = dclient.NewClientWithOpts(dclient.FromEnv); err != nil {
log.Error("docker client init error(%v)", err)
panic(err)
}
return &Dao{
c: c,
httpClient: xhttp.NewClient(c.HTTPClient),
db: orm.NewMySQL(c.ORM),
email: gomail.NewDialer(c.Mail.Host, c.Mail.Port, c.Mail.Username, c.Mail.Password),
mc: memcache.NewPool(c.Memcache.Config),
cache: fanout.New("cache", fanout.Worker(10), fanout.Buffer(10240)),
hubCache: fanout.New("hubCache", fanout.Worker(10), fanout.Buffer(10240)),
expire: int32(time.Duration(c.Memcache.Expire) / time.Second),
dockerClient: dc,
}
}
// Close close the resource.
func (d *Dao) Close() {
if d.db != nil {
d.db.Close()
}
if d.mc != nil {
d.mc.Close()
}
}
// SetProxy set test proxy.
func (d *Dao) SetProxy() {
d.httpClient.SetTransport(gock.DefaultTransport)
}
func (d *Dao) newRequest(method, url string, v interface{}) (req *http.Request, err error) {
body := &bytes.Buffer{}
if method != http.MethodGet {
if err = json.NewEncoder(body).Encode(v); err != nil {
log.Error("json encode value(%s) err(?) ", v, err)
return
}
}
if req, err = http.NewRequest(method, url, body); err != nil {
log.Error("http new request url(?) err(?)", url, err)
}
return
}
// Ping verify server is ok.
func (d *Dao) Ping(c context.Context) (err error) {
if err = d.db.DB().Ping(); err != nil {
log.Info("dao.cloudDB.Ping() error(%v)", err)
}
return
}
// tokenCacheSave The err does not need to return, because this method is irrelevant.
func (d *Dao) tokenCacheSave(c context.Context, cacheItem *memcache.Item) {
var f = func(c context.Context) {
var (
conn = d.mc.Get(c)
err error
)
defer conn.Close()
if err = conn.Set(cacheItem); err != nil {
log.Error("AddCache conn.Set(%s) error(%v)", cacheItem.Key, err)
}
}
if err := d.cache.Do(c, f); err != nil {
log.Error("Token cache save err(%v)", err)
}
}

View File

@@ -0,0 +1,44 @@
package dao
import (
"context"
"flag"
"path/filepath"
"strings"
"go-common/app/admin/ep/merlin/conf"
_ "github.com/go-sql-driver/mysql"
"gopkg.in/h2non/gock.v1"
)
var (
d *Dao
c context.Context
)
func init() {
dir, _ := filepath.Abs("../cmd/convey-test.toml")
flag.Set("conf", dir)
conf.Init()
d = New(conf.Conf)
d.httpClient.SetTransport(gock.DefaultTransport)
c = ctx()
}
func httpMock(method, url string) *gock.Request {
r := gock.New(url)
r.Method = strings.ToUpper(method)
return r
}
func ctx() context.Context {
return context.Background()
}
func WithPaasToken(f func()) func() {
return func() {
d.paasToken(c)
f()
}
}

View File

@@ -0,0 +1,41 @@
package dao
import (
"context"
"go-common/app/admin/ep/merlin/model"
"go-common/library/cache/memcache"
)
//QueryMachineUsageSummaryFromCache Machine Usage Summary In Cache.
func (d *Dao) QueryMachineUsageSummaryFromCache(c context.Context, pqadmrs []*model.PaasQueryAndDelMachineRequest) (pmds []*model.PaasMachineDetail, err error) {
var (
conn = d.mc.Get(c)
item *memcache.Item
)
defer conn.Close()
for _, pqadmr := range pqadmrs {
pmd := &model.PaasMachineDetail{}
if item, err = conn.Get(pqadmr.Name); err == nil {
if err = conn.Scan(item, &pmd); err == nil {
pmds = append(pmds, pmd)
continue
}
}
if pmd, err = d.QueryPaasMachine(c, pqadmr); err != nil {
continue
}
pmds = append(pmds, pmd)
item = &memcache.Item{Key: pqadmr.Name, Object: pmd, Flags: memcache.FlagJSON, Expiration: d.expire}
d.tokenCacheSave(c, item)
}
return
}

View File

@@ -0,0 +1,143 @@
package dao
import (
"context"
"fmt"
"net/http"
"go-common/app/admin/ep/merlin/model"
"go-common/library/ecode"
"go-common/library/log"
)
const (
_deviceCode = 0
_devicesListURL = "/apis/devices/list"
_deviceURL = "/apis/devices/get"
_deviceBootURL = "/apis/devices/boot"
_deviceShutDownURL = "/apis/devices/shutdown"
)
// MobileDeviceList Get Device Farm List .
func (d *Dao) MobileDeviceList(c context.Context) (resTotal map[string][]*model.Device, err error) {
var (
req *http.Request
hostList = d.c.DeviceFarm.HostList
)
resTotal = make(map[string][]*model.Device)
for _, host := range hostList {
var res *model.DeviceListResponse
url := fmt.Sprintf("http://%s", host+_devicesListURL)
if req, err = d.newRequest(http.MethodGet, url, nil); err != nil {
log.Error("d.MobileDeviceList newRequest url(%s) err(%+v)", _devicesListURL, err)
continue
}
if err = d.httpClient.Do(c, req, &res); err != nil {
log.Error("d.MobileDeviceList httpClient url(%s) err(%+v)", _devicesListURL, err)
continue
}
if res.Status.Code != _deviceCode {
err = ecode.MerlinDeviceFarmErr
log.Error("Status url(%s) res(%s),err(%+v)", _devicesListURL, res, err)
continue
}
resTotal[host] = res.Data.Devices
}
return
}
// MobileDeviceDetail Get Mobile Device Detail.
func (d *Dao) MobileDeviceDetail(c context.Context, host, serial string) (device *model.Device, err error) {
var (
req *http.Request
res *model.DeviceListDetailResponse
)
url := fmt.Sprintf("http://%s?serial=%s", host+_deviceURL, serial)
if req, err = d.newRequest(http.MethodGet, url, nil); err != nil {
log.Error("d.MobileDeviceDetail newRequest url(%s) err(%+v)", _deviceURL, err)
return
}
if err = d.httpClient.Do(c, req, &res); err != nil {
log.Error("d.MobileDeviceDetail url(%s) err(%+v)", _deviceURL, err)
return
}
if res.Status.Code != _deviceCode {
err = ecode.MerlinDeviceFarmErr
log.Error("Status url(%s) res(%s) err(%+v)", _deviceURL, res, err)
return
}
device = res.Data.Devices
return
}
// BootMobileDevice Boot Mobile Device.
func (d *Dao) BootMobileDevice(c context.Context, host, serial string) (deviceBootData *model.DeviceBootData, err error) {
var (
req *http.Request
res *model.DeviceBootResponse
)
reqModel := &model.DeviceBootRequest{
Serial: serial,
}
url := fmt.Sprintf("http://%s", host+_deviceBootURL)
if req, err = d.newRequest(http.MethodPost, url, reqModel); err != nil {
log.Error("d.BootMobileDevice newRequest url(%s) err(%+v)", _deviceBootURL, err)
return
}
req.Header.Set("content-type", "application/json")
if err = d.httpClient.Do(c, req, &res); err != nil {
log.Error("d.BootMobileDevice url(%s) err(%+v)", _deviceBootURL, err)
return
}
if res.Status.Code != _deviceCode {
err = ecode.MerlinDeviceFarmErr
log.Error("Status url(%s) res(%s) err(%+v)", _deviceBootURL, res, err)
return
}
deviceBootData = res.Data
return
}
// ShutdownMobileDevice Shutdown Mobile Device.
func (d *Dao) ShutdownMobileDevice(c context.Context, host, serial string) (err error) {
var (
req *http.Request
res *model.DeviceShutDownResponse
)
reqModel := &model.DeviceBootRequest{
Serial: serial,
}
url := fmt.Sprintf("http://%s", host+_deviceShutDownURL)
if req, err = d.newRequest(http.MethodPost, url, reqModel); err != nil {
log.Error("d.ShutdownMobileDevice newRequest url(%s) err(%+v)", _deviceShutDownURL, err)
return
}
req.Header.Set("content-type", "application/json")
if err = d.httpClient.Do(c, req, &res); err != nil {
log.Error("d.ShutdownMobileDevice url(%s) err(%+v)", _deviceShutDownURL, err)
return
}
if res.Status.Code != _deviceCode {
err = ecode.MerlinDeviceFarmErr
log.Error("Status url(%s) res(%s) err(%+v)", _deviceShutDownURL, res, err)
return
}
return
}

View File

@@ -0,0 +1,124 @@
package dao
import (
"context"
"encoding/base64"
"encoding/json"
"io"
"os"
"strings"
"go-common/library/ecode"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/filters"
)
// authConfig get Auth Config.
func (d *Dao) authConfig() (authStr string, err error) {
authConfig := types.AuthConfig{
Username: d.c.BiliHub.Username,
Password: d.c.BiliHub.Password,
ServerAddress: d.c.BiliHub.HostName,
}
var encodedJSON []byte
if encodedJSON, err = json.Marshal(authConfig); err != nil {
return
}
authStr = base64.URLEncoding.EncodeToString(encodedJSON)
return
}
// ImagePull Image Pull.
func (d *Dao) ImagePull(imageName string) (err error) {
var (
out io.ReadCloser
authStr string
)
if authStr, err = d.authConfig(); err != nil {
return
}
if out, err = d.dockerClient.ImagePull(context.TODO(), imageName, types.ImagePullOptions{RegistryAuth: authStr}); err != nil {
err = ecode.MerlinImagePullErr
return
}
defer out.Close()
io.Copy(os.Stdout, out)
return
}
// ImagePush Image Push.
func (d *Dao) ImagePush(imageName string) (err error) {
var (
out io.ReadCloser
authStr string
)
if authStr, err = d.authConfig(); err != nil {
return
}
if out, err = d.dockerClient.ImagePush(context.TODO(), imageName, types.ImagePushOptions{RegistryAuth: authStr}); err != nil {
err = ecode.MerlinImagePushErr
return
}
defer out.Close()
io.Copy(os.Stdout, out)
return
}
// ImageTag Image Tag.
func (d *Dao) ImageTag(imageSrcName, imageTagName string) (err error) {
err = d.dockerClient.ImageTag(context.TODO(), imageSrcName, imageTagName)
return
}
// ImageRemove Image Remove.
func (d *Dao) ImageRemove(imageID string) (err error) {
_, err = d.dockerClient.ImageRemove(context.TODO(), imageID, types.ImageRemoveOptions{Force: true, PruneChildren: true})
return
}
// ImageGetID Image Get ID.
func (d *Dao) ImageGetID(imageName string) (imageID string, err error) {
var images []types.ImageSummary
if images, err = d.dockerClient.ImageList(context.TODO(), types.ImageListOptions{}); err != nil {
return
}
for _, image := range images {
for _, tag := range image.RepoTags {
if tag == imageName {
imageID = image.ID
return
}
}
}
return
}
// ImageGetAll Image Get All.
func (d *Dao) ImageGetAll() (imageNames []string, err error) {
var images []types.ImageSummary
if images, err = d.dockerClient.ImageList(context.Background(), types.ImageListOptions{Filters: filters.NewArgs()}); err != nil {
return
}
for _, image := range images {
for _, tag := range image.RepoTags {
if strings.Contains(tag, d.c.BiliHub.HostName) {
imageNames = append(imageNames, tag)
}
}
}
return
}

View File

@@ -0,0 +1,35 @@
package dao
import (
"strings"
"context"
"gopkg.in/gomail.v2"
)
const (
_MailBoxNotFound = "Mailbox not found"
)
// SendMail asynchronous send mail.
func (d *Dao) SendMail(message *gomail.Message) {
message.SetAddressHeader("From", d.email.Username, "merlin")
d.cache.Do(context.Background(), func(c context.Context) {
d.SendMailIfFailed(message)
})
}
// SendMailIfFailed Send Mail If Failed
func (d *Dao) SendMailIfFailed(message *gomail.Message) {
if err := d.email.DialAndSend(message); err != nil {
if strings.Contains(err.Error(), _MailBoxNotFound) {
headerMsg := message.GetHeader("Subject")
headerMsg = append(headerMsg, "Mail Send Error:"+err.Error()+",Receiver:")
headerMsg = append(headerMsg, message.GetHeader("To")...)
message.SetHeader("To", d.c.Mail.NoticeOwner...)
message.SetHeader("Subject", headerMsg...)
d.email.DialAndSend(message)
}
}
}

View File

@@ -0,0 +1,20 @@
package dao
import (
"testing"
. "github.com/smartystreets/goconvey/convey"
"gopkg.in/gomail.v2"
)
var (
m = gomail.NewMessage()
)
func Test_SendMail(t *testing.T) {
Convey("test send mail", t, func() {
m.SetHeader("To", "fengyifeng@bilibili.com")
m.SetHeader("Subject", "unit test")
d.SendMail(m)
})
}

View File

@@ -0,0 +1,150 @@
package dao
import (
"time"
"go-common/app/admin/ep/merlin/model"
pkgerr "github.com/pkg/errors"
)
// InsertApplicationRecord insert application record.
func (d *Dao) InsertApplicationRecord(ar *model.ApplicationRecord) (err error) {
return pkgerr.WithStack(d.db.Create(&ar).Error)
}
// FindApplicationRecordsByApplicant find application record by applicant.
func (d *Dao) FindApplicationRecordsByApplicant(applicant string, pn, ps int) (total int64, ars []*model.ApplicationRecord, err error) {
cdb := d.db.Model(&model.ApplicationRecord{}).Where("applicant=?", applicant).Order("ID desc").Offset((pn - 1) * ps).Limit(ps)
if err = pkgerr.WithStack(cdb.Find(&ars).Error); err != nil {
return
}
if err = pkgerr.WithStack(d.db.Model(&model.ApplicationRecord{}).Where("applicant=?", applicant).Count(&total).Error); err != nil {
return
}
return
}
// FindApplicationRecordsByAuditor find application record by auditor.
func (d *Dao) FindApplicationRecordsByAuditor(auditor string, pn, ps int) (total int64, ars []*model.ApplicationRecord, err error) {
cdb := d.db.Model(&model.ApplicationRecord{}).Where("auditor=?", auditor).Order("ID desc").Offset((pn - 1) * ps).Limit(ps)
if err = pkgerr.WithStack(cdb.Find(&ars).Error); err != nil {
return
}
if err = pkgerr.WithStack(d.db.Model(&model.ApplicationRecord{}).Where("auditor=?", auditor).Count(&total).Error); err != nil {
return
}
return
}
// FindApplicationRecordsByMachineID find application records by machineId.
func (d *Dao) FindApplicationRecordsByMachineID(machineID int64, pn, ps int) (total int64, ars []*model.ApplicationRecord, err error) {
cdb := d.db.Model(&model.ApplicationRecord{}).Where("machine_id=?", machineID).Order("ID desc").Offset((pn - 1) * ps).Limit(ps)
if err = pkgerr.WithStack(cdb.Find(&ars).Error); err != nil {
return
}
if err = pkgerr.WithStack(d.db.Model(&model.ApplicationRecord{}).Where("machine_id=?", machineID).Count(&total).Error); err != nil {
return
}
return
}
// FindApplicationRecordsByID find application records by id.
func (d *Dao) FindApplicationRecordsByID(auditID int64) (ar *model.ApplicationRecord, err error) {
ar = &model.ApplicationRecord{}
err = pkgerr.WithStack(d.db.Model(&model.ApplicationRecord{}).Where("ID=?", auditID).First(ar).Error)
return
}
// UpdateAuditStatus update audit status.
func (d *Dao) UpdateAuditStatus(auditID int64, status string) (err error) {
return pkgerr.WithStack(d.db.Model(&model.ApplicationRecord{}).Where("ID=?", auditID).Update("STATUS", status).Error)
}
// UpdateAuditStatusAndComment update audit status and comment.
func (d *Dao) UpdateAuditStatusAndComment(auditID int64, status, comment string) (err error) {
return pkgerr.WithStack(d.db.Model(&model.ApplicationRecord{}).Where("ID=?", auditID).Update("STATUS", status).Update("AUDITOR_COMMENT", comment).Error)
}
// InsertApplicationRecordAndUpdateMachineDelayStatus insert application record and update machine delay status.
func (d *Dao) InsertApplicationRecordAndUpdateMachineDelayStatus(ar *model.ApplicationRecord, machineID int64, delayStatus int) (err error) {
tx := d.db.Begin()
if err = tx.Error; err != nil {
return
}
if err = tx.Create(&ar).Error; err != nil {
tx.Rollback()
return
}
if err = tx.Model(&model.Machine{}).Where("ID=?", machineID).Update("DELAY_STATUS", delayStatus).Error; err != nil {
tx.Rollback()
return
}
err = tx.Commit().Error
return
}
// UpdateAuditStatusAndUpdateMachineDelayStatus update audit status and update machine delay status.
func (d *Dao) UpdateAuditStatusAndUpdateMachineDelayStatus(machineID, auditID int64, delayStatus int, applyStatus string) (err error) {
tx := d.db.Begin()
if err = tx.Error; err != nil {
return
}
if err = tx.Model(&model.ApplicationRecord{}).Where("ID=?", auditID).Update("STATUS", applyStatus).Error; err != nil {
tx.Rollback()
return
}
if err = tx.Model(&model.Machine{}).Where("ID=?", machineID).Update("DELAY_STATUS", delayStatus).Error; err != nil {
tx.Rollback()
return
}
err = tx.Commit().Error
return
}
// UpdateAuditStatusAndUpdateMachineDelayStatusComment update audit status and update machine delay status comment.
func (d *Dao) UpdateAuditStatusAndUpdateMachineDelayStatusComment(machineID, auditID int64, delayStatus int, applyStatus, comment string) (err error) {
tx := d.db.Begin()
if err = tx.Error; err != nil {
return
}
if err = tx.Model(&model.ApplicationRecord{}).Where("ID=?", auditID).Update("STATUS", applyStatus).Update("AUDITOR_COMMENT", comment).Error; err != nil {
tx.Rollback()
return
}
if err = tx.Model(&model.Machine{}).Where("ID=?", machineID).Update("DELAY_STATUS", delayStatus).Error; err != nil {
tx.Rollback()
return
}
err = tx.Commit().Error
return
}
// UpdateAuditStatusAndUpdateMachineEndTime update audit status and update machine end time.
func (d *Dao) UpdateAuditStatusAndUpdateMachineEndTime(machineID, auditID int64, delayStatus int, applyStatus string, applyEndTime time.Time, comment string) (err error) {
tx := d.db.Begin()
if err = tx.Error; err != nil {
return
}
if err = tx.Model(&model.ApplicationRecord{}).Where("ID=?", auditID).Update("STATUS", applyStatus).Update("AUDITOR_COMMENT", comment).Error; err != nil {
tx.Rollback()
return
}
if err = tx.Model(&model.Machine{}).Where("ID=?", machineID).Update("DELAY_STATUS", delayStatus).Update("END_TIME", applyEndTime).Error; err != nil {
tx.Rollback()
return
}
err = tx.Commit().Error
return
}

View File

@@ -0,0 +1,241 @@
package dao
import (
"database/sql"
"fmt"
"go-common/app/admin/ep/merlin/model"
)
// MachineLifeCycle Statistics Machine Life Cycle.
func (d *Dao) MachineLifeCycle() (machineLifeCycles []*model.MachineLifeCycle, err error) {
var rows *sql.Rows
SQL := "select CASE " +
"when TIMESTAMPDIFF(DAY,ctime,end_time)>0 and TIMESTAMPDIFF(DAY,ctime,end_time) <=100 THEN'0-100' " +
"when TIMESTAMPDIFF(DAY,ctime,end_time)>100 and TIMESTAMPDIFF(DAY,ctime,end_time) <=200 THEN'100-200' " +
"when TIMESTAMPDIFF(DAY,ctime,end_time)>200 THEN'200+' ELSE '0-100' " +
"End as lifecycle,count(-1)from machines GROUP BY lifecycle"
if rows, err = d.db.Raw(SQL).Rows(); err != nil {
return
}
defer rows.Close()
for rows.Next() {
mlc := &model.MachineLifeCycle{}
if err = rows.Scan(&mlc.Duration, &mlc.Count); err != nil {
return
}
machineLifeCycles = append(machineLifeCycles, mlc)
}
return
}
// MachineCountGroupByBusiness Machine Count Group By Business
func (d *Dao) MachineCountGroupByBusiness() (machinesCount []*model.MachineCountGroupByBusiness, err error) {
var rows *sql.Rows
SQL := "SELECT business_unit,count(-1) as count FROM machines GROUP BY business_unit ORDER BY count DESC"
if rows, err = d.db.Raw(SQL).Rows(); err != nil {
return
}
defer rows.Close()
for rows.Next() {
mc := &model.MachineCountGroupByBusiness{}
if err = rows.Scan(&mc.BusinessUnit, &mc.Count); err != nil {
return
}
machinesCount = append(machinesCount, mc)
}
return
}
// MachineCountGroupByBusinessInRunning Machine Count Group By Business In Running
func (d *Dao) MachineCountGroupByBusinessInRunning() (machinesCount []*model.MachineCountGroupByBusiness, err error) {
var rows *sql.Rows
SQL := "SELECT business_unit,count(-1) as count FROM machines where status>=100 GROUP BY business_unit ORDER BY count DESC"
if rows, err = d.db.Raw(SQL).Rows(); err != nil {
return
}
defer rows.Close()
for rows.Next() {
mc := &model.MachineCountGroupByBusiness{}
if err = rows.Scan(&mc.BusinessUnit, &mc.Count); err != nil {
return
}
machinesCount = append(machinesCount, mc)
}
return
}
// MachineWillBeExpired Statistics Machine Will Be Expired
func (d *Dao) MachineWillBeExpired() (machines []*model.MachineCreatedAndEndTime, err error) {
var rows *sql.Rows
SQL := "SELECT id,name,username,app,ctime,end_time from machines where `status`>=100 order by end_time limit 10"
if rows, err = d.db.Raw(SQL).Rows(); err != nil {
return
}
defer rows.Close()
for rows.Next() {
mc := &model.MachineCreatedAndEndTime{}
if err = rows.Scan(&mc.ID, &mc.MachineName, &mc.Username, &mc.App, &mc.CreateTime, &mc.EndTime); err != nil {
return
}
machines = append(machines, mc)
}
return
}
// MachineLatestCreated Machine Latest Created
func (d *Dao) MachineLatestCreated() (machines []*model.MachineCreatedAndEndTime, err error) {
var rows *sql.Rows
SQL := "SELECT id,name,username,app,ctime,end_time from machines where `status`>=100 order by ctime desc limit 10"
if rows, err = d.db.Raw(SQL).Rows(); err != nil {
return
}
defer rows.Close()
for rows.Next() {
mc := &model.MachineCreatedAndEndTime{}
if err = rows.Scan(&mc.ID, &mc.MachineName, &mc.Username, &mc.App, &mc.CreateTime, &mc.EndTime); err != nil {
return
}
machines = append(machines, mc)
}
return
}
// MobileMachineUserUsageCount Mobile Machine User Usage Count
func (d *Dao) MobileMachineUserUsageCount() (mobileMachinesUsageCount []*model.MobileMachineUserUsageCount, err error) {
var rows *sql.Rows
SQL := fmt.Sprintf("select username,count(username) as count from mobile_machine_logs "+
"where operation_type='%s' and operation_result ='%s' "+
"group by username order by count desc", model.MBBindLog, model.OperationSuccessForMachineLog)
if rows, err = d.db.Raw(SQL).Rows(); err != nil {
return
}
defer rows.Close()
for rows.Next() {
mc := &model.MobileMachineUserUsageCount{}
if err = rows.Scan(&mc.Username, &mc.Count); err != nil {
return
}
mobileMachinesUsageCount = append(mobileMachinesUsageCount, mc)
}
return
}
// MobileMachineUserLendCount Mobile Machine Lend Count
func (d *Dao) MobileMachineUserLendCount() (mobileMachinesUsageCount []*model.MobileMachineUserUsageCount, err error) {
var rows *sql.Rows
SQL := fmt.Sprintf("select username,count(username) as count from mobile_machine_logs "+
"where operation_type='%s' and operation_result ='%s' "+
"group by username order by count desc", model.MBLendOutLog, model.OperationSuccessForMachineLog)
if rows, err = d.db.Raw(SQL).Rows(); err != nil {
return
}
defer rows.Close()
for rows.Next() {
mc := &model.MobileMachineUserUsageCount{}
if err = rows.Scan(&mc.Username, &mc.Count); err != nil {
return
}
mobileMachinesUsageCount = append(mobileMachinesUsageCount, mc)
}
return
}
// MobileMachineUsageCount Mobile Machine Usage Count
func (d *Dao) MobileMachineUsageCount() (mobileMachinesUsageCount []*model.MobileMachineUsageCount, err error) {
var rows *sql.Rows
SQL := fmt.Sprintf("select b.id,b.name,count(b.name) as count from mobile_machine_logs as a "+
"left join mobile_machines as b on a.machine_id = b.id "+
"where a.operation_type='%s' and a.operation_result = '%s' "+
"group by b.id,b.`name` order by count desc", model.MBBindLog, model.OperationSuccessForMachineLog)
if rows, err = d.db.Raw(SQL).Rows(); err != nil {
return
}
defer rows.Close()
for rows.Next() {
mc := &model.MobileMachineUsageCount{}
if err = rows.Scan(&mc.MobileMachineID, &mc.MobileMachineName, &mc.Count); err != nil {
return
}
mobileMachinesUsageCount = append(mobileMachinesUsageCount, mc)
}
return
}
// MobileMachineLendCount Mobile Machine Lend Count
func (d *Dao) MobileMachineLendCount() (mobileMachinesUsageCount []*model.MobileMachineUsageCount, err error) {
var rows *sql.Rows
SQL := fmt.Sprintf("select b.id,b.name,count(b.name) as count from mobile_machine_logs as a "+
"left join mobile_machines as b on a.machine_id = b.id "+
"where a.operation_type='%s' and a.operation_result = '%s' "+
"group by b.id,b.`name` order by count desc", model.MBLendOutLog, model.OperationSuccessForMachineLog)
if rows, err = d.db.Raw(SQL).Rows(); err != nil {
return
}
defer rows.Close()
for rows.Next() {
mc := &model.MobileMachineUsageCount{}
if err = rows.Scan(&mc.MobileMachineID, &mc.MobileMachineName, &mc.Count); err != nil {
return
}
mobileMachinesUsageCount = append(mobileMachinesUsageCount, mc)
}
return
}
// MobileMachineModeCount Mobile Machine Mode Count.
func (d *Dao) MobileMachineModeCount() (mobileMachinesTypeCount []*model.MobileMachineTypeCount, err error) {
var rows *sql.Rows
SQL := "select mode,count(mode) as count from mobile_machines where action > 0 group by mode order by count desc"
if rows, err = d.db.Raw(SQL).Rows(); err != nil {
return
}
defer rows.Close()
for rows.Next() {
mc := &model.MobileMachineTypeCount{}
if err = rows.Scan(&mc.ModeName, &mc.Count); err != nil {
return
}
mobileMachinesTypeCount = append(mobileMachinesTypeCount, mc)
}
return
}
// MobileMachineUseRecord Mobile Machine Use Record
func (d *Dao) MobileMachineUseRecord() (mobileMachineLogs []*model.MobileMachineLog, err error) {
err = d.db.Where("operation_type= ? or operation_type = ?", model.MBBindLog, model.MBReleaseLog).Order("machine_id,id").Find(&mobileMachineLogs).Error
return
}

View File

@@ -0,0 +1,28 @@
package dao
import (
"go-common/app/admin/ep/merlin/model"
"go-common/library/ecode"
pkgerr "github.com/pkg/errors"
)
// InsertHubImageConf Insert Hub Image conf.
func (d *Dao) InsertHubImageConf(hubImageConf *model.HubImageConf) (err error) {
return pkgerr.WithStack(d.db.Create(hubImageConf).Error)
}
// UpdateHubImageConf Update Hub Image Conf.
func (d *Dao) UpdateHubImageConf(hubImageConf *model.HubImageConf) (err error) {
return pkgerr.WithStack(d.db.Model(&model.HubImageConf{}).Where("image_name=?", hubImageConf.ImageName).Updates(map[string]interface{}{
"update_by": hubImageConf.UpdateBy, "command": hubImageConf.Command, "environments": hubImageConf.Envs, "hosts": hubImageConf.Hosts}).Error)
}
// FindHubImageConfByImageName Find Hub Image Conf By Image Name.
func (d *Dao) FindHubImageConfByImageName(imageName string) (hubImageConf *model.HubImageConf, err error) {
hubImageConf = &model.HubImageConf{}
if err = d.db.Where("image_name = ?", imageName).First(hubImageConf).Error; err == ecode.NothingFound {
err = nil
}
return
}

View File

@@ -0,0 +1,59 @@
package dao
import (
"strconv"
"go-common/app/admin/ep/merlin/model"
"go-common/library/ecode"
pkgerr "github.com/pkg/errors"
)
// InsertHubImageLog Insert Hub Image Log.
func (d *Dao) InsertHubImageLog(hubImageLog *model.HubImageLog) (err error) {
return pkgerr.WithStack(d.db.Create(hubImageLog).Error)
}
// UpdateHubImageLog Update Hub Image Log.
func (d *Dao) UpdateHubImageLog(hubImageLog *model.HubImageLog) (err error) {
return pkgerr.WithStack(d.db.Model(&model.HubImageLog{}).Where("id=?", hubImageLog.ID).Updates(hubImageLog).Error)
}
// UpdateHubImageLogStatus Update Hub Image Log Status.
func (d *Dao) UpdateHubImageLogStatus(logID int64, status int) (err error) {
return pkgerr.WithStack(d.db.Model(&model.HubImageLog{}).Where("id=?", logID).Updates(map[string]interface{}{"status": status}).Error)
}
// FindHubImageLogByImageTag Find Hub Image Log By Image Tag.
func (d *Dao) FindHubImageLogByImageTag(imageTag string) (hubImageLog *model.HubImageLog, err error) {
hubImageLog = &model.HubImageLog{}
if err = d.db.Where("imagetag = ?", imageTag).First(hubImageLog).Error; err == ecode.NothingFound {
err = nil
}
return
}
// FindHubImageLogByMachineID Find Hub Image Log By MachineID .
func (d *Dao) FindHubImageLogByMachineID(machineID int64) (hubImageLogs []*model.HubImageLog, err error) {
if err = d.db.Where("machine_id = ? ", machineID).Order("id DESC").Find(&hubImageLogs).Error; err == ecode.NothingFound {
err = nil
}
return
}
// FindHubMachine2ImageLog Find Hub Machine to Image Log .
func (d *Dao) FindHubMachine2ImageLog(queryRequest *model.QueryMachine2ImageLogRequest) (total int64, hubImageLogs []*model.HubImageLog, err error) {
cdb := d.db.Model(&model.HubImageLog{}).Where("machine_id=? and operate_type = ?", queryRequest.MachineID, strconv.Itoa(model.ImageMachine2Image)).Order("ID desc").Offset((queryRequest.PageNum - 1) * queryRequest.PageSize).Limit(queryRequest.PageSize)
if err = pkgerr.WithStack(cdb.Find(&hubImageLogs).Error); err != nil {
return
}
if err = pkgerr.WithStack(d.db.Model(&model.HubImageLog{}).Where("machine_id=? and operate_type = ?", queryRequest.MachineID, strconv.Itoa(model.ImageMachine2Image)).Count(&total).Error); err != nil {
return
}
return
}
// UpdateHubImageLogStatusInDoingStatus Update Hub Image Log Status in doing status.
func (d *Dao) UpdateHubImageLogStatusInDoingStatus(machineID int64, status int) (err error) {
return pkgerr.WithStack(d.db.Model(&model.HubImageLog{}).Where("machine_id=? and status=? and operate_type = ?", machineID, model.ImageInit, model.ImageMachine2Image).Updates(map[string]interface{}{"status": status}).Error)
}

View File

@@ -0,0 +1,28 @@
package dao
import (
"go-common/app/admin/ep/merlin/model"
pkgerr "github.com/pkg/errors"
)
// Images Search all images in db.
func (d *Dao) Images() (images []*model.Image, err error) {
err = pkgerr.WithStack(d.db.Where("status = ?", model.AliveImageStatus).Find(&images).Error)
return
}
// AddImage Create new image in db.
func (d *Dao) AddImage(image *model.Image) error {
return pkgerr.WithStack(d.db.Create(image).Error)
}
// UpdateImage Update image in db.
func (d *Dao) UpdateImage(image *model.Image) error {
return pkgerr.WithStack(d.db.Model(&model.Image{}).Updates(image).Error)
}
// DelImage Delete image in db.
func (d *Dao) DelImage(iID int64) error {
return pkgerr.WithStack(d.db.Model(&model.Image{}).Where("id = ?", iID).Update("status", model.DeletedImageStatus).Error)
}

View File

@@ -0,0 +1,51 @@
package dao
import (
"testing"
"go-common/app/admin/ep/merlin/model"
. "github.com/smartystreets/goconvey/convey"
)
var (
imageAdd = model.Image{
Name: "docker-reg.bilibili.co/zccdebian:1.0",
Status: 1,
OS: "debian",
Version: "1.0 64位",
Description: "base",
CreatedBy: "ut",
}
imageUpdate = model.Image{
Name: "docker-reg.bilibili.co/zccdebian:1.0update",
Status: 2,
OS: "debian update",
Version: "1.0 64位 update",
Description: "base update",
CreatedBy: "ut update",
UpdatedBy: "ut",
}
)
func Test_Image(t *testing.T) {
Convey("add image", t, func() {
err := d.AddImage(&imageAdd)
So(err, ShouldBeNil)
})
Convey("get image", t, func() {
images, err := d.Images()
So(err, ShouldBeNil)
So(len(images), ShouldBeGreaterThan, 0)
})
Convey("update image", t, func() {
imageUpdate.ID = imageAdd.ID
err := d.UpdateImage(&imageUpdate)
So(err, ShouldBeNil)
})
Convey("delete image", t, func() {
imageDel := model.Image{ID: imageAdd.ID}
err := d.DelImage(imageDel.ID)
So(err, ShouldBeNil)
})
}

View File

@@ -0,0 +1,201 @@
package dao
import (
"database/sql"
"fmt"
"strings"
"time"
"go-common/app/admin/ep/merlin/model"
pkgerr "github.com/pkg/errors"
)
const (
_wildcards = "%"
_where = "WHERE"
_and = "AND"
_pathAndPodNamesMappingSQL = "SELECT CONCAT(business_unit, '.', project, '.', app) AS \"path\" , GROUP_CONCAT(pod_name) AS \"pod_names\" FROM machines WHERE status >=0 AND status < 100 AND ctime < %s GROUP BY business_unit, project, app;"
_nodeSQL = "SELECT DISTINCT(machine_id) FROM machine_nodes"
)
// FindExpiredMachineByDay find expired machines.
func (d *Dao) FindExpiredMachineByDay(day int) (ms []*model.Machine, err error) {
err = pkgerr.WithStack(d.db.Where("status >= 0 AND DATEDIFF(end_time,NOW())>=0 AND DATEDIFF(end_time,NOW())<= ?", day).Find(&ms).Error)
return
}
// FindExpiredMachine find a day-old machines.
func (d *Dao) FindExpiredMachine() (ms []*model.Machine, err error) {
err = pkgerr.WithStack(d.db.Where("status >= 0 AND DATEDIFF(NOW(),end_time) = ?", -1).Find(&ms).Error)
return
}
// QueryMachine query machine.
func (d *Dao) QueryMachine(id int64) (machine *model.Machine, err error) {
machine = &model.Machine{}
err = pkgerr.WithStack(d.db.Where("id = ?", id).First(machine).Error)
return
}
// QueryMachineByName Query Machine By Name.
func (d *Dao) QueryMachineByName(machineName string) (machine *model.Machine, err error) {
machine = &model.Machine{}
err = pkgerr.WithStack(d.db.Where("name = ?", machineName).First(machine).Error)
return
}
// QueryOnlineMachineByName Query Online Machine By Name.
func (d *Dao) QueryOnlineMachineByName(machineName string) (machine *model.Machine, err error) {
machine = &model.Machine{}
err = pkgerr.WithStack(d.db.Where("status >=100 and name = ?", machineName).First(machine).Error)
return
}
// DelMachine delete machine.
func (d *Dao) DelMachine(id int64, username string) (err error) {
return pkgerr.WithStack(d.db.Model(&model.Machine{}).Where("id = ?", id).Update("status", model.RemovedMachineInMerlin).Update("update_by", username).Error)
}
// HasMachine verify machine name.
func (d *Dao) HasMachine(name string) (b bool, err error) {
var size int64
if err = pkgerr.WithStack(d.db.Model(&model.Machine{}).Where("name LIKE ?", name+"-"+_wildcards).Where("status <> ?", model.RemovedMachineInMerlin).Count(&size).Error); err != nil {
return
}
b = size > 0
return
}
// UpdateMachineStatus update machine status by giving value.
func (d *Dao) UpdateMachineStatus(id int64, status int) error {
return pkgerr.WithStack(d.db.Model(&model.Machine{}).Where("id = ?", id).Update("status", status).Error)
}
// QueryMachines query machines and update machine in names status to boot.
func (d *Dao) QueryMachines(names []string, qmr *model.QueryMachineRequest) (total int64, machines []*model.Machine, err error) {
if err = pkgerr.WithStack(d.db.Model(&model.Machine{}).Where("status >= 0 AND status < 100 AND pod_name IN (?)", names).Update("status", model.BootMachineInMerlin).Error); err != nil {
return
}
gDB := d.db.Model(&model.Machine{}).Where("status >= 100 AND status < 300 AND pod_name IN (?) OR (status > -300 AND status <= -200 OR status >= 0 AND status < 100) AND username = ?", names, qmr.Requester)
if qmr.Username != "" {
gDB = gDB.Where("username LIKE ?", qmr.Username+_wildcards)
}
if qmr.MachineName != "" {
gDB = gDB.Where("BINARY name LIKE ?", _wildcards+qmr.MachineName+_wildcards)
}
if qmr.BusinessUnit != "" || qmr.Project != "" || qmr.App != "" {
var (
nSQL = _nodeSQL
logicalWord = _where
)
if qmr.BusinessUnit != "" {
nSQL = fmt.Sprintf("%s %s BINARY business_unit = '%s'", nSQL, logicalWord, qmr.BusinessUnit)
logicalWord = _and
}
if qmr.Project != "" {
nSQL = fmt.Sprintf("%s %s BINARY project = '%s'", nSQL, logicalWord, qmr.Project)
logicalWord = _and
}
if qmr.App != "" {
nSQL = fmt.Sprintf("%s %s BINARY app = '%s'", nSQL, logicalWord, qmr.App)
}
gDB = gDB.Where("ID IN ( " + nSQL + " )")
}
if err = pkgerr.WithStack(gDB.Count(&total).Error); err != nil {
return
}
err = pkgerr.WithStack(gDB.Order("ctime DESC").Offset((qmr.PageNum - 1) * qmr.PageSize).Limit(qmr.PageSize).Find(&machines).Error)
return
}
// UpdateMachineEndTime update machine end time.
func (d *Dao) UpdateMachineEndTime(id int64, delayStatus int, endTime time.Time) (err error) {
return pkgerr.WithStack(d.db.Model(&model.Machine{}).Where("id = ?", id).Update("delay_status", delayStatus).Update("end_time", endTime).Error)
}
// UpdateMachineDelayStatus update machine delay status.
func (d *Dao) UpdateMachineDelayStatus(id int64, delayStatus int) (err error) {
return pkgerr.WithStack(d.db.Model(&model.Machine{}).Where("id = ?", id).Update("delay_status", delayStatus).Error)
}
// QueryPathAndPodNamesMapping query path and pod names map.
func (d *Dao) QueryPathAndPodNamesMapping() (pathAndPodNames map[string][]string, err error) {
var (
rows *sql.Rows
path string
podNames string
)
if rows, err = d.db.Raw(fmt.Sprintf(_pathAndPodNamesMappingSQL, d.c.Paas.MachineTimeout)).Rows(); err != nil {
err = pkgerr.WithStack(err)
return
}
defer rows.Close()
pathAndPodNames = make(map[string][]string)
for rows.Next() {
if err = rows.Scan(&path, &podNames); err != nil {
return
}
pathAndPodNames[path] = strings.Split(podNames, ",")
}
return
}
// UpdateMachineStatusByPodNames update machine status by pod names.
func (d *Dao) UpdateMachineStatusByPodNames(podNames []string, target int) (err error) {
return pkgerr.WithStack(d.db.Model(&model.Machine{}).Where("pod_name IN (?)", podNames).Update("status", target).Error)
}
// UpdateMachineUser Update Machine User By Id.
func (d *Dao) UpdateMachineUser(machineID int64, username string) (err error) {
return pkgerr.WithStack(d.db.Model(&model.Machine{}).Where("id = ?", machineID).Update("username", username).Error)
}
// QueryMachinesByPodNames query machines by giving pod name slices.
func (d *Dao) QueryMachinesByPodNames(podNames []string) (machines []*model.Machine, err error) {
err = pkgerr.WithStack(d.db.Model(&model.Machine{}).Where("pod_name IN (?)", podNames).Find(&machines).Error)
return
}
// InsertMachines insert machines.
func (d *Dao) InsertMachines(u string, gmr *model.GenMachinesRequest, ins []*model.CreateInstance) (err error) {
var m *model.Machine
tx := d.db.Begin()
if err = tx.Error; err != nil {
return
}
for _, in := range ins {
m = in.ToMachine(u, gmr)
if err = tx.Create(m).Error; err != nil {
tx.Rollback()
return
}
for _, n := range gmr.ToMachineNode(m.ID) {
if err = tx.Create(n).Error; err != nil {
tx.Rollback()
return
}
}
if err = tx.Create(m.ToMachineLog()).Error; err != nil {
tx.Rollback()
return
}
}
if err = tx.Commit().Error; err != nil {
tx.Rollback()
}
return
}
// QueryMachineInRunning Query Machine In Running
func (d *Dao) QueryMachineInRunning() (ms []*model.Machine, err error) {
err = d.db.Where("status >= 100").Find(&ms).Error
return
}
// QueryMachineCount Query Machine Count
func (d *Dao) QueryMachineCount() (total int, err error) {
err = pkgerr.WithStack(d.db.Model(&model.Machine{}).Count(&total).Error)
return
}

View File

@@ -0,0 +1,47 @@
package dao
import (
"go-common/app/admin/ep/merlin/model"
pkgerr "github.com/pkg/errors"
)
// InsertMachinesV2 insert machines for v2.
func (d *Dao) InsertMachinesV2(u string, gmr *model.GenMachinesRequest, pgmr *model.PaasGenMachineRequest) (ms []*model.Machine, err error) {
tx := d.db.Begin()
if err = tx.Error; err != nil {
return
}
for _, pm := range pgmr.Machines {
m := pm.ToMachine(u, gmr)
if err = tx.Create(m).Error; err != nil {
tx.Rollback()
return
}
for _, n := range gmr.ToMachineNode(m.ID) {
if err = tx.Create(n).Error; err != nil {
tx.Rollback()
return
}
}
if err = tx.Create(m.ToMachineLog()).Error; err != nil {
tx.Rollback()
return
}
ms = append(ms, m)
}
if err = tx.Commit().Error; err != nil {
tx.Rollback()
}
return
}
// UpdateStatusForMachines update status for machines.
func (d *Dao) UpdateStatusForMachines(status int, ids []int64) (err error) {
return pkgerr.WithStack(d.db.Model(&model.Machine{}).Where("id IN (?)", ids).Update("status", status).Error)
}
// UpdateMachineStatusByName update status by name.
func (d *Dao) UpdateMachineStatusByName(status int, n string) error {
return pkgerr.WithStack(d.db.Model(&model.Machine{}).Where("name = ?", n).Update("status", status).Error)
}

View File

@@ -0,0 +1,111 @@
package dao
import (
"database/sql"
"fmt"
"strconv"
"go-common/app/admin/ep/merlin/model"
pkgerr "github.com/pkg/errors"
)
const (
_machineRightJoinLogSQL = "SELECT m.id,m.name,ml.username,ml.operation_type,ml.operation_result,ml.ctime FROM machines AS m INNER JOIN machine_logs AS ml ON m.id = ml.machine_id"
_machineRightCountSQL = "SELECT count(m.id) FROM machines AS m INNER JOIN machine_logs AS ml ON m.id = ml.machine_id"
)
// InsertMachineLog insert machine log.
func (d *Dao) InsertMachineLog(machineLog *model.MachineLog) (err error) {
return pkgerr.WithStack(d.db.Create(machineLog).Error)
}
// FindMachineLogsByMachineID find machine log by machineId.
func (d *Dao) FindMachineLogsByMachineID(machineID int64, pn, ps int) (total int64, machineLogs []*model.AboundMachineLog, err error) {
var (
qSQL = _machineRightJoinLogSQL
cSQL = _machineRightCountSQL
rows *sql.Rows
)
if machineID > 0 {
qSQL = _machineRightJoinLogSQL + " WHERE machine_id = " + strconv.FormatInt(machineID, 10)
cSQL = _machineRightCountSQL + " WHERE machine_id = " + strconv.FormatInt(machineID, 10)
}
cDB := d.db.Raw(cSQL)
if err = pkgerr.WithStack(cDB.Count(&total).Error); err != nil {
return
}
gDB := d.db.Raw(qSQL)
if rows, err = gDB.Order("ml.ctime DESC").Offset((pn - 1) * ps).Limit(ps).Rows(); err != nil {
return
}
defer rows.Close()
for rows.Next() {
ml := &model.AboundMachineLog{}
if err = rows.Scan(&ml.MachineID, &ml.Name, &ml.Username, &ml.OperateType, &ml.OperateResult, &ml.OperateTime); err != nil {
return
}
machineLogs = append(machineLogs, ml)
}
return
}
// FindMachineLogs Find Machine Logs.
func (d *Dao) FindMachineLogs(queryRequest *model.QueryMachineLogRequest) (total int64, machineLogs []*model.AboundMachineLog, err error) {
var (
qSQL = _machineRightJoinLogSQL
cSQL = _machineRightCountSQL
rows *sql.Rows
)
if queryRequest.MachineID > 0 || queryRequest.OperateType != "" || queryRequest.OperateUser != "" || queryRequest.MachineName != "" {
var (
strSQL = ""
logicalWord = _where
)
if queryRequest.MachineID > 0 {
strSQL = fmt.Sprintf("%s %s ml.machine_id = %s", strSQL, logicalWord, strconv.FormatInt(queryRequest.MachineID, 10))
logicalWord = _and
}
if queryRequest.OperateType != "" {
strSQL = fmt.Sprintf("%s %s ml.operation_type like '%s'", strSQL, logicalWord, _wildcards+queryRequest.OperateType+_wildcards)
logicalWord = _and
}
if queryRequest.MachineName != "" {
strSQL = fmt.Sprintf("%s %s m.name like '%s'", strSQL, logicalWord, _wildcards+queryRequest.MachineName+_wildcards)
logicalWord = _and
}
if queryRequest.OperateUser != "" {
strSQL = fmt.Sprintf("%s %s ml.username like '%s'", strSQL, logicalWord, _wildcards+queryRequest.OperateUser+_wildcards)
logicalWord = _and
}
qSQL = _machineRightJoinLogSQL + " " + strSQL
cSQL = _machineRightCountSQL + " " + strSQL
}
cDB := d.db.Raw(cSQL)
if err = pkgerr.WithStack(cDB.Count(&total).Error); err != nil {
return
}
gDB := d.db.Raw(qSQL)
if rows, err = gDB.Order("ml.ctime DESC").Offset((queryRequest.PageNum - 1) * queryRequest.PageSize).Limit(queryRequest.PageSize).Rows(); err != nil {
return
}
defer rows.Close()
for rows.Next() {
ml := &model.AboundMachineLog{}
if err = rows.Scan(&ml.MachineID, &ml.Name, &ml.Username, &ml.OperateType, &ml.OperateResult, &ml.OperateTime); err != nil {
return
}
machineLogs = append(machineLogs, ml)
}
return
}

View File

@@ -0,0 +1,36 @@
package dao
import (
"testing"
"time"
"go-common/app/admin/ep/merlin/model"
. "github.com/smartystreets/goconvey/convey"
)
var (
machineLog = model.MachineLog{
Username: "fengyifeng",
MachineID: 1,
OperateType: "DelTest",
OperateResult: "",
OperateTime: time.Now(),
}
)
func TestInsertMachineLog(t *testing.T) {
Convey("Everything goes well when names is slice with value", t, func() {
err := d.InsertMachineLog(&machineLog)
So(err, ShouldBeNil)
d.db.Where("OperateType=DelTest").Delete(machineLog)
})
}
func TestFindMachineLogsByMachineID(t *testing.T) {
Convey("Everything goes well when names is slice with value", t, func() {
_, _, err := d.FindMachineLogsByMachineID(0, 1, 5)
So(err, ShouldBeNil)
d.db.Where("OperateType=DelTest").Delete(machineLog)
})
}

View File

@@ -0,0 +1,36 @@
package dao
import (
"go-common/app/admin/ep/merlin/model"
pkgerr "github.com/pkg/errors"
)
// DelMachineNodeByMachineID delete node by machine id.
func (d *Dao) DelMachineNodeByMachineID(mID int64) error {
return pkgerr.WithStack(d.db.Where("machine_id = ?", mID).Delete(&model.MachineNode{}).Error)
}
// GenMachineNodes generate some machine nodes.
func (d *Dao) GenMachineNodes(nodes []*model.MachineNode) (err error) {
tx := d.db.Begin()
if err = tx.Error; err != nil {
return
}
for _, n := range nodes {
if err = tx.Create(n).Error; err != nil {
tx.Rollback()
return
}
}
if err = tx.Commit().Error; err != nil {
tx.Rollback()
}
return
}
// QueryMachineNodes query machine nodes by machine id.
func (d *Dao) QueryMachineNodes(mID int64) (nodes []*model.MachineNode, err error) {
err = pkgerr.WithStack(d.db.Model(&model.MachineNode{}).Where("machine_id = ?", mID).Find(&nodes).Error)
return
}

View File

@@ -0,0 +1,13 @@
package dao
import (
"go-common/app/admin/ep/merlin/model"
pkgerr "github.com/pkg/errors"
)
// FindAllMachinePackages find all machine packages.
func (d *Dao) FindAllMachinePackages() (machinePackages []*model.MachinePackage, err error) {
err = pkgerr.WithStack(d.db.Find(&machinePackages).Error)
return
}

View File

@@ -0,0 +1,565 @@
package dao
import (
"testing"
"time"
"go-common/app/admin/ep/merlin/model"
"go-common/library/log"
. "github.com/smartystreets/goconvey/convey"
)
var tMachine *model.Machine
func machineFunc(f func()) func() {
return func() {
beforeAllMT()
defer afterAllMT()
f()
}
}
func beforeAllMT() {
var err error
tMachine = &model.Machine{
Name: "test-machine-1",
PodName: "test-machine-0-0",
Status: 100,
Username: "seanan",
BusinessUnit: "test",
Project: "ep",
App: "merlin",
ClusterID: 3,
NetworkID: 5,
Comment: "Unit test for the dao of merlin.",
DelayStatus: 0,
}
if err = d.db.Create(tMachine).Error; err != nil {
log.Error("Failed to init machine(%v) for test, err(%v)", tMachine, err)
return
}
if err = d.db.Create(&model.MachineNode{MachineID: tMachine.ID, BusinessUnit: tMachine.BusinessUnit, Project: tMachine.Project, App: tMachine.App, TreeID: 1234}).Error; err != nil {
log.Error("Failed to init machine node for test, err(%v)", tMachine, err)
return
}
}
func afterAllMT() {
var err error
if err = d.db.Where("name = ?", tMachine.Name).Delete(&model.Machine{}).Error; err != nil {
log.Error("Failed to delete machine(%v) for test, err(%v)", tMachine, err)
return
}
if err = d.db.Where("machine_id = ?", tMachine.ID).Delete(&model.MachineNode{}).Error; err != nil {
log.Error("Failed to delete machine node for test, err(%v)", err)
return
}
}
func TestFindExpiredMachineByDay(t *testing.T) {
Convey("The actualMachines must contain tMachine when deadline day is 0", t, machineFunc(func() {
var err error
if err = d.db.Model(&model.Machine{}).Where("id = ?", tMachine.ID).Update("end_time", time.Now()).Error; err != nil {
log.Error("Cannot update the end_time of tMachine")
return
}
actualMachines, err := d.FindExpiredMachineByDay(0)
So(err, ShouldBeNil)
for _, m := range actualMachines {
if m.Name == tMachine.Name {
return
}
}
So(true, ShouldBeFalse)
}))
Convey("The actualMachines must not contain tMachine when deadline day is not 0", t, machineFunc(func() {
var err error
if err = d.db.Model(&model.Machine{}).Where("id = ?", tMachine.ID).Update("end_time", time.Now()).Error; err != nil {
log.Error("Cannot update the end_time of tMachine")
return
}
actualMachines, err := d.FindExpiredMachineByDay(1)
So(err, ShouldBeNil)
for _, m := range actualMachines {
if m.Name == tMachine.Name {
So(true, ShouldBeFalse)
return
}
}
}))
}
func TestFindExpiredMachine(t *testing.T) {
Convey("The actualMachines must contain tMachine", t, machineFunc(func() {
var err error
if err = d.db.Model(&model.Machine{}).Where("id = ?", tMachine.ID).Update("end_time", time.Now().AddDate(0, 0, 1)).Error; err != nil {
log.Error("Cannot update the end_time of tMachine")
return
}
actualMachines, err := d.FindExpiredMachine()
So(err, ShouldBeNil)
for _, m := range actualMachines {
if m.Name == tMachine.Name {
return
}
}
So(true, ShouldBeFalse)
}))
Convey("The actualMachines must not contain tMachine", t, machineFunc(func() {
actualMachines, err := d.FindExpiredMachine()
So(err, ShouldBeNil)
for _, m := range actualMachines {
if m.Name == tMachine.Name {
So(true, ShouldBeFalse)
return
}
}
}))
}
func TestQueryMachine(t *testing.T) {
Convey("The actualMachines must be tMachine when id is the id of tMachine", t, machineFunc(func() {
actualMachines, err := d.QueryMachine(tMachine.ID)
So(err, ShouldBeNil)
So(actualMachines.ID, ShouldEqual, tMachine.ID)
}))
Convey("The actualMachines must not be tMachine when id is not the id of tMachine", t, machineFunc(func() {
_, err := d.QueryMachine(0)
So(err, ShouldNotBeNil)
}))
}
// TestDelMachine this test only need positive test because negative case is filtered by service
func TestDelMachine(t *testing.T) {
Convey("The tMachine must be delete", t, machineFunc(func() {
var (
actual = &model.Machine{}
updateBy = "seanan"
)
err := d.DelMachine(tMachine.ID, updateBy)
So(err, ShouldBeNil)
d.db.Where("id = ?", tMachine.ID).Find(actual)
So(actual.Status, ShouldEqual, model.RemovedMachineInMerlin)
So(actual.UpdateBy, ShouldEqual, updateBy)
}))
}
// TestHasMachine HasMachine is fuzzy matching name which is end with -number
func TestHasMachine(t *testing.T) {
Convey("return true when machine existed", t, machineFunc(func() {
b, err := d.HasMachine("test-machine")
So(err, ShouldBeNil)
So(b, ShouldBeTrue)
}))
Convey("return false when machine does not exist", t, machineFunc(func() {
b, err := d.HasMachine("no-test-machine")
So(err, ShouldBeNil)
So(b, ShouldBeFalse)
}))
}
func TestUpdateMachineStatus(t *testing.T) {
Convey("The machine status must be boot", t, machineFunc(func() {
var (
err error
actualMachine = &model.Machine{}
)
if err = d.db.Model(&model.Machine{}).Where("id = ?", tMachine.ID).Update("status", model.CreatingMachineInMerlin).Error; err != nil {
return
}
err = d.UpdateMachineStatus(tMachine.ID, model.BootMachineInMerlin)
So(err, ShouldBeNil)
if err = d.db.Where("id = ?", tMachine.ID).First(actualMachine).Error; err != nil {
log.Error("find machine err(%v)", err)
return
}
So(actualMachine.Status, ShouldEqual, model.BootMachineInMerlin)
}))
}
func TestQueryMachines(t *testing.T) {
Convey("Find the boot machines by names", t, machineFunc(func() {
var (
err error
qmr = &model.QueryMachineRequest{
Pagination: model.Pagination{
PageSize: 5,
PageNum: 1,
},
}
names = []string{"test-machine-0-0"}
actualMachines []*model.Machine
)
_, actualMachines, err = d.QueryMachines(names, qmr)
So(err, ShouldBeNil)
for _, m := range actualMachines {
if m.Name == tMachine.Name {
return
}
}
So(true, ShouldBeFalse)
}))
Convey("Find the creating machines by requester", t, machineFunc(func() {
var (
err error
qmr = &model.QueryMachineRequest{
Pagination: model.Pagination{
PageSize: 5,
PageNum: 1,
},
Requester: "seanan",
}
actualMachines []*model.Machine
)
if err = d.db.Model(&model.Machine{}).Where("id = ?", tMachine.ID).Update("status", model.CreatingMachineInMerlin).Error; err != nil {
return
}
_, actualMachines, err = d.QueryMachines(nil, qmr)
So(err, ShouldBeNil)
for _, m := range actualMachines {
if m.Name == tMachine.Name {
return
}
}
So(true, ShouldBeFalse)
}))
Convey("Cannot find the creating machines by other requester", t, machineFunc(func() {
var (
err error
qmr = &model.QueryMachineRequest{
Pagination: model.Pagination{
PageSize: 5,
PageNum: 1,
},
Requester: "other",
}
actualMachines []*model.Machine
)
if err = d.db.Model(&model.Machine{}).Where("id = ?", tMachine.ID).Update("status", model.CreatingMachineInMerlin).Error; err != nil {
return
}
_, actualMachines, err = d.QueryMachines(nil, qmr)
So(err, ShouldBeNil)
for _, m := range actualMachines {
if m.Name == tMachine.Name {
So(true, ShouldBeFalse)
return
}
}
}))
Convey("Find the machines filter by username", t, machineFunc(func() {
var (
err error
qmr = &model.QueryMachineRequest{
Pagination: model.Pagination{
PageSize: 5,
PageNum: 1,
},
Username: "sean",
}
names = []string{"test-machine-0-0"}
actualMachines []*model.Machine
)
_, actualMachines, err = d.QueryMachines(names, qmr)
So(err, ShouldBeNil)
for _, m := range actualMachines {
if m.Name == tMachine.Name {
return
}
}
So(true, ShouldBeFalse)
}))
Convey("Find the machines filter by machine name", t, machineFunc(func() {
var (
err error
qmr = &model.QueryMachineRequest{
Pagination: model.Pagination{
PageSize: 5,
PageNum: 1,
},
MachineName: "test-machine",
}
names = []string{"test-machine-0-0"}
actualMachines []*model.Machine
)
_, actualMachines, err = d.QueryMachines(names, qmr)
So(err, ShouldBeNil)
for _, m := range actualMachines {
if m.Name == tMachine.Name {
return
}
}
So(true, ShouldBeFalse)
}))
Convey("Find the machines filter by BusinessUnit", t, machineFunc(func() {
var (
err error
qmr = &model.QueryMachineRequest{
Pagination: model.Pagination{
PageSize: 5,
PageNum: 1,
},
TreeNode: model.TreeNode{
BusinessUnit: "test",
},
}
names = []string{"test-machine-0-0"}
actualMachines []*model.Machine
)
_, actualMachines, err = d.QueryMachines(names, qmr)
So(err, ShouldBeNil)
for _, m := range actualMachines {
if m.Name == tMachine.Name {
return
}
}
So(true, ShouldBeFalse)
}))
Convey("Find the machines filter by BusinessUnit and Project", t, machineFunc(func() {
var (
err error
qmr = &model.QueryMachineRequest{
Pagination: model.Pagination{
PageSize: 5,
PageNum: 1,
},
TreeNode: model.TreeNode{
BusinessUnit: "test",
Project: "ep",
},
}
names = []string{"test-machine-0-0"}
actualMachines []*model.Machine
)
_, actualMachines, err = d.QueryMachines(names, qmr)
So(err, ShouldBeNil)
for _, m := range actualMachines {
if m.Name == tMachine.Name {
return
}
}
So(true, ShouldBeFalse)
}))
Convey("Find the machines filter by BusinessUnit, Project and App", t, machineFunc(func() {
var (
err error
qmr = &model.QueryMachineRequest{
Pagination: model.Pagination{
PageSize: 5,
PageNum: 1,
},
TreeNode: model.TreeNode{
BusinessUnit: "test",
Project: "ep",
App: "merlin",
},
}
names = []string{"test-machine-0-0"}
actualMachines []*model.Machine
)
_, actualMachines, err = d.QueryMachines(names, qmr)
So(err, ShouldBeNil)
for _, m := range actualMachines {
if m.Name == tMachine.Name {
return
}
}
So(true, ShouldBeFalse)
}))
}
func TestUpdateMachineEndTime(t *testing.T) {
Convey("The EndTime and DelayStatus should be updated", t, machineFunc(func() {
var (
actualMachine = &model.Machine{}
expectTime = time.Now().AddDate(0, 1, 0)
expectDelayStatus = 1
err error
)
err = d.UpdateMachineEndTime(tMachine.ID, expectDelayStatus, expectTime)
So(err, ShouldBeNil)
if err = d.db.Where("id = ?", tMachine.ID).First(actualMachine).Error; err != nil {
log.Error("find machine err(%v)", err)
return
}
So(actualMachine.EndTime.Format("2006-01-02 15:04:05"), ShouldEqual, expectTime.Format("2006-01-02 15:04:05"))
So(actualMachine.DelayStatus, ShouldEqual, expectDelayStatus)
}))
}
func TestUpdateMachineDelayStatus(t *testing.T) {
Convey("The DelayStatus should be updated", t, machineFunc(func() {
var (
actualMachine = &model.Machine{}
expectDelayStatus = 1
err error
)
err = d.UpdateMachineDelayStatus(tMachine.ID, expectDelayStatus)
So(err, ShouldBeNil)
if err = d.db.Where("id = ?", tMachine.ID).First(actualMachine).Error; err != nil {
log.Error("find machine err(%v)", err)
return
}
So(actualMachine.DelayStatus, ShouldEqual, expectDelayStatus)
}))
}
func TestQueryPathAndPodNamesMapping(t *testing.T) {
Convey("Find path and podNames mapping when the machine exceeds 20 minutes", t, machineFunc(func() {
var (
pathAndPodNames map[string][]string
err error
)
if err = d.db.Model(&model.Machine{}).Where("id = ?", tMachine.ID).Update("status", model.CreatingMachineInMerlin).Update("ctime", time.Now().AddDate(0, 0, -1)).Error; err != nil {
return
}
pathAndPodNames, err = d.QueryPathAndPodNamesMapping()
So(err, ShouldBeNil)
expectPath := tMachine.ToTreeNode().TreePath()
for _, pn := range pathAndPodNames[expectPath] {
if pn == tMachine.PodName {
return
}
}
So(true, ShouldBeFalse)
}))
Convey("cannot find path and podNames mapping when the machine does not exceed 20 minutes", t, machineFunc(func() {
var (
pathAndPodNames map[string][]string
err error
)
if err = d.db.Model(&model.Machine{}).Where("id = ?", tMachine.ID).Update("status", model.CreatingMachineInMerlin).Error; err != nil {
return
}
pathAndPodNames, err = d.QueryPathAndPodNamesMapping()
So(err, ShouldBeNil)
expectPath := tMachine.ToTreeNode().TreePath()
for _, pn := range pathAndPodNames[expectPath] {
if pn == tMachine.PodName {
So(true, ShouldBeFalse)
return
}
}
}))
}
func TestUpdateMachineStatusByPodNames(t *testing.T) {
Convey("cannot find path and podNames mapping when the machine does not exceed 20 minutes", t, machineFunc(func() {
var (
err error
actualMachine *model.Machine
expectStatus = model.BootMachineInMerlin
)
if err = d.db.Model(&model.Machine{}).Where("id = ?", tMachine.ID).Update("status", model.CreatingMachineInMerlin).Error; err != nil {
return
}
err = d.UpdateMachineStatusByPodNames([]string{tMachine.PodName}, expectStatus)
So(err, ShouldBeNil)
if err = d.db.Where("id = ?", tMachine.ID).First(actualMachine).Error; err != nil {
log.Error("find machine err(%v)", err)
return
}
So(actualMachine.Status, ShouldEqual, expectStatus)
}))
}
func TestQueryMachinesByPodNames(t *testing.T) {
Convey("cannot find path and podNames mapping when the machine does not exceed 20 minutes", t, machineFunc(func() {
var (
err error
actualMachines []*model.Machine
)
actualMachines, err = d.QueryMachinesByPodNames([]string{tMachine.PodName})
So(err, ShouldBeNil)
for _, m := range actualMachines {
if m.ID == tMachine.ID {
return
}
}
So(true, ShouldBeFalse)
}))
}
func TestInsertMachines(t *testing.T) {
Convey("Insert 2 machines", t, machineFunc(func() {
var (
testMachine2 = "test-machine-2"
testMachine3 = "test-machine-3"
ins = []*model.CreateInstance{
{
Instance: model.Instance{
InstanceName: testMachine2,
},
InstanceCreateStatus: model.CreatingMachineInPass,
}, {
Instance: model.Instance{
InstanceName: testMachine3,
},
InstanceCreateStatus: model.CreateFailedMachineInPaas,
},
}
u = "seanan"
gmr = &model.GenMachinesRequest{
Env: model.Env{
ClusterID: 1,
NetworkID: 1,
},
PaasMachine: model.PaasMachine{
Name: "test-machine",
Image: "test-Image",
CPURequest: 1000,
MemoryRequest: 1024,
DiskRequest: 20,
VolumnMount: "/data",
},
Nodes: []*model.Node{
{
BusinessUnit: "test",
Project: "ep",
App: "merlin",
TreeID: 1234,
},
},
Comment: "test",
Amount: 1,
}
err error
actualMachine []*model.Machine
flag = 0
)
err = d.InsertMachines(u, gmr, ins)
defer func() {
if err = d.db.Where("name IN (?)", []string{testMachine2, testMachine3}).Delete(&model.Machine{}).Error; err != nil {
log.Error("Failed to delete machine(%v) for test, err(%v)", tMachine, err)
return
}
}()
So(err, ShouldBeNil)
if err = d.db.Where("name IN (?)", []string{testMachine2, testMachine3}).Find(&actualMachine).Error; err != nil {
log.Error("Find machiens err(%v)", err)
return
}
for _, m := range actualMachine {
if m.Name == testMachine2 && m.Status == model.CreatingMachineInMerlin {
flag++
}
if m.Name == testMachine3 && m.Status == model.ImmediatelyFailedMachineInMerlin {
flag += 2
}
}
So(flag, ShouldEqual, 3)
}))
}

View File

@@ -0,0 +1,24 @@
package dao
import (
"go-common/app/admin/ep/merlin/model"
pkgerr "github.com/pkg/errors"
)
// InsertMailLog insert mail log.
func (d *Dao) InsertMailLog(ml *model.MailLog) (err error) {
return pkgerr.WithStack(d.db.Create(&ml).Error)
}
// DelMailLog delete mail log.
func (d *Dao) DelMailLog(receiverName string) (err error) {
err = pkgerr.WithStack(d.db.Where("receiver_name=?", receiverName).Delete(model.MailLog{}).Error)
return
}
// FindMailLog find mail log.
func (d *Dao) FindMailLog(receiverName string) (mailLogs []*model.MailLog, err error) {
err = pkgerr.WithStack(d.db.Where("receiver_name=?", receiverName).Find(&mailLogs).Error)
return
}

View File

@@ -0,0 +1,41 @@
package dao
import (
"go-common/app/admin/ep/merlin/model"
"testing"
. "github.com/smartystreets/goconvey/convey"
)
var (
username = "fengyifenguitest@bilibili.com"
)
func Test_Mail_Log(t *testing.T) {
Convey("test add mail log", t, func() {
ml := &model.MailLog{
ReceiverName: username,
MailType: 1,
SendContext: "test add mail log",
}
err := d.InsertMailLog(ml)
So(err, ShouldBeNil)
})
Convey("test find mail log", t, func() {
mailLogs, err := d.FindMailLog(username)
So(len(mailLogs), ShouldBeGreaterThan, 0)
So(err, ShouldBeNil)
})
Convey("test delete mail log", t, func() {
err := d.DelMailLog(username)
So(err, ShouldBeNil)
})
Convey("test find mail log", t, func() {
mailLogs, err := d.FindMailLog(username)
So(len(mailLogs), ShouldEqual, 0)
So(err, ShouldBeNil)
})
}

View File

@@ -0,0 +1,318 @@
package dao
import (
"database/sql"
"fmt"
"strconv"
"go-common/app/admin/ep/merlin/model"
"go-common/library/ecode"
pkgerr "github.com/pkg/errors"
)
// FindMobileMachineBySerial Find Device FarmBy Serial.
func (d *Dao) FindMobileMachineBySerial(serial string) (mobileMachine *model.MobileMachine, err error) {
mobileMachine = &model.MobileMachine{}
if err = d.db.Where("serial = ?", serial).Find(mobileMachine).Error; err == ecode.NothingFound {
err = nil
}
return
}
// DeleteMobileMachineByUUID Delete Mobile Machine By UUID.
func (d *Dao) DeleteMobileMachineByUUID(UUID string) (delCnt int, err error) {
err = pkgerr.WithStack(d.db.Model(&model.MobileMachine{}).Where("uuid <> ? and action <>?", UUID, model.MBHostDel).Count(&delCnt).Update("action", model.MBOffline).Error)
return
}
// DeleteMobileMachineNotInHost Delete Mobile Machine Not In Host.
func (d *Dao) DeleteMobileMachineNotInHost(hostList []string) (delCnt int, err error) {
err = pkgerr.WithStack(d.db.Model(&model.MobileMachine{}).Where("host not in (?)", hostList).Count(&delCnt).Update("action", model.MBHostDel).Error)
return
}
// FindMobileMachineByID Find Mobile Machine By Id.
func (d *Dao) FindMobileMachineByID(deviceID int64) (mobileMachine *model.MobileMachine, err error) {
mobileMachine = &model.MobileMachine{}
err = pkgerr.WithStack(d.db.Where("id = ?", deviceID).Find(mobileMachine).Error)
return
}
// InsertMobileMachine Insert Device Farm.
func (d *Dao) InsertMobileMachine(mobileMachine *model.MobileMachine) (err error) {
return pkgerr.WithStack(d.db.Create(&mobileMachine).Error)
}
// UpdateMobileMachineWsurlAndState Update Mobile Machine Wsurl And State.
func (d *Dao) UpdateMobileMachineWsurlAndState(mobileMachine *model.MobileMachine) (err error) {
return pkgerr.WithStack(d.db.Model(&model.MobileMachine{}).Where("serial=?", mobileMachine.Serial).Updates(map[string]interface{}{"wsurl": mobileMachine.WsURL, "state": mobileMachine.State, "upload_url": mobileMachine.UploadURL}).Error)
}
// UpdateMobileMachine Update Mobile Machine.
func (d *Dao) UpdateMobileMachine(mobileMachine *model.MobileMachine) (err error) {
return pkgerr.WithStack(d.db.Model(&model.MobileMachine{}).Where("serial=?", mobileMachine.Serial).Updates(mobileMachine).Error)
}
// UpdateMobileMachineByRelease Update Mobile Machine By Release.
func (d *Dao) UpdateMobileMachineByRelease(mobileMachine *model.MobileMachine) (err error) {
return pkgerr.WithStack(d.db.Model(&model.MobileMachine{}).Where("serial=?", mobileMachine.Serial).Updates(map[string]interface{}{"username": mobileMachine.Username, "end_time": mobileMachine.EndTime}).Error)
}
//FindMobileMachines Find Mobile Machines.
func (d *Dao) FindMobileMachines(queryRequest *model.QueryMobileDeviceRequest) (total int64, mobileMachines []*model.MobileMachine, err error) {
gDB := d.db.Model(&model.MobileMachine{}).Where("action> ? ", model.MBHostDel)
if queryRequest.MobileID > 0 {
gDB = gDB.Where("id=?", queryRequest.MobileID)
}
if queryRequest.Serial != "" {
gDB = gDB.Where("serial=?", queryRequest.Serial)
}
if queryRequest.Name != "" {
gDB = gDB.Where("name=?", queryRequest.Name)
}
if queryRequest.Username != "" {
gDB = gDB.Where("username=?", queryRequest.Username)
}
if queryRequest.OwnerName != "" {
gDB = gDB.Where("owner_name=?", queryRequest.OwnerName)
}
if queryRequest.CPU != "" {
gDB = gDB.Where("cpu=?", queryRequest.CPU)
}
if queryRequest.Version != "" {
gDB = gDB.Where("version=?", queryRequest.Version)
}
if queryRequest.Mode != "" {
gDB = gDB.Where("mode=?", queryRequest.Mode)
}
if queryRequest.Type > -1 {
gDB = gDB.Where("type=?", queryRequest.Type)
}
if queryRequest.State != "" {
gDB = gDB.Where("state=?", queryRequest.State)
}
if queryRequest.Online {
gDB = gDB.Where("action=?", model.MBOnline)
}
if queryRequest.Usage == model.MBFree {
gDB = gDB.Where("username='' and action=?", model.MBOnline)
}
if queryRequest.Usage == model.MBInUse {
gDB = gDB.Where("username<>'' and action=?", model.MBOnline)
}
if queryRequest.Usage == model.MBNoConnect {
gDB = gDB.Where("action=?", model.MBOffline)
}
if err = pkgerr.WithStack(gDB.Count(&total).Error); err != nil {
return
}
//先出action>0的未连接的排在后面
err = pkgerr.WithStack(gDB.Order("action desc,id desc").Offset((queryRequest.PageNum - 1) * queryRequest.PageSize).Limit(queryRequest.PageSize).Find(&mobileMachines).Error)
return
}
// FindMobileMachineCategory Find Mobile Machine Category.
func (d *Dao) FindMobileMachineCategory(isShowOffline bool) (mobileCategory *model.MobileCategory, err error) {
var (
rows *sql.Rows
cpus []string
versions []string
modes []string
states []string
types []int
usages []int
sqlGroupTpl string
inUseCnt int
freeCnt int
noConCnt int
)
if isShowOffline {
sqlGroupTpl = "select %s from mobile_machines where action> " + strconv.Itoa(model.MBHostDel) + " group by %s"
} else {
sqlGroupTpl = "select %s from mobile_machines where action>-1 group by %s"
}
tx := d.db.Begin()
if err = tx.Error; err != nil {
return
}
//cpu
if rows, err = tx.Raw(fmt.Sprintf(sqlGroupTpl, "cpu", "cpu")).Rows(); err != nil {
return
}
for rows.Next() {
var cpu string
if err = rows.Scan(&cpu); err != nil {
return
}
cpus = append(cpus, cpu)
}
//version
if rows, err = tx.Raw(fmt.Sprintf(sqlGroupTpl, "version", "version")).Rows(); err != nil {
return
}
for rows.Next() {
var version string
if err = rows.Scan(&version); err != nil {
return
}
versions = append(versions, version)
}
//mode
if rows, err = tx.Raw(fmt.Sprintf(sqlGroupTpl, "mode", "mode")).Rows(); err != nil {
return
}
for rows.Next() {
var mode string
if err = rows.Scan(&mode); err != nil {
return
}
modes = append(modes, mode)
}
//state
if rows, err = tx.Raw(fmt.Sprintf(sqlGroupTpl, "state", "state")).Rows(); err != nil {
return
}
for rows.Next() {
var state string
if err = rows.Scan(&state); err != nil {
return
}
states = append(states, state)
}
//type
if rows, err = tx.Raw(fmt.Sprintf(sqlGroupTpl, "type", "type")).Rows(); err != nil {
return
}
for rows.Next() {
var stype int
if err = rows.Scan(&stype); err != nil {
return
}
types = append(types, stype)
}
//usage
if err = tx.Model(&model.MobileMachine{}).Where("username<>'' and action>0").Count(&inUseCnt).Error; err != nil {
return
}
if err = tx.Model(&model.MobileMachine{}).Where("username='' and action>0").Count(&freeCnt).Error; err != nil {
return
}
if err = tx.Model(&model.MobileMachine{}).Where("action<0").Count(&noConCnt).Error; err != nil {
return
}
if inUseCnt > 0 {
usages = append(usages, model.MBInUse)
}
if freeCnt > 0 {
usages = append(usages, model.MBFree)
}
if noConCnt > 0 {
usages = append(usages, model.MBNoConnect)
}
mobileCategory = &model.MobileCategory{}
mobileCategory.CPUs = cpus
mobileCategory.Versions = versions
mobileCategory.Modes = modes
mobileCategory.States = states
mobileCategory.Types = types
mobileCategory.Usages = usages
if err = tx.Commit().Error; err != nil {
tx.Rollback()
}
return
}
// FindAllMobileImages Find All Mobile Images.
func (d *Dao) FindAllMobileImages() (mobileImages []*model.MobileImage, err error) {
err = pkgerr.WithStack(d.db.Model(&model.MobileImage{}).Find(&mobileImages).Error)
return
}
// FindMobileImageByMode Find Mobile Image By Mode.
func (d *Dao) FindMobileImageByMode(mode string) (mobileImage *model.MobileImage, err error) {
mobileImage = &model.MobileImage{}
if err = d.db.Where("mode=?", mode).First(mobileImage).Error; err == ecode.NothingFound {
err = nil
}
return
}
// LendOutMobileMachine Lend Out Mobile.
func (d *Dao) LendOutMobileMachine(deviceID int64, serial, username string) (err error) {
tx := d.db.Begin()
if err = tx.Error; err != nil {
return
}
mobileMachineLog := &model.MobileMachineLog{
OperateType: model.MBLendOutLog,
Username: username,
MachineID: deviceID,
OperateResult: model.OperationSuccessForMachineLog,
}
if err = tx.Create(mobileMachineLog).Error; err != nil {
tx.Rollback()
return
}
if err = tx.Model(&model.MobileMachine{}).Where("serial=?", serial).Update("is_lendout", model.MBLendOut).Error; err != nil {
tx.Rollback()
return
}
if err = tx.Commit().Error; err != nil {
tx.Rollback()
}
return
}
// ReturnMobileMachine return Mobile.
func (d *Dao) ReturnMobileMachine(deviceID int64, serial, username string) (err error) {
tx := d.db.Begin()
if err = tx.Error; err != nil {
return
}
mobileMachineLog := &model.MobileMachineLog{
OperateType: model.MBReturnLog,
Username: username,
MachineID: deviceID,
OperateResult: model.OperationSuccessForMachineLog,
}
if err = tx.Create(mobileMachineLog).Error; err != nil {
tx.Rollback()
return
}
if err = tx.Model(&model.MobileMachine{}).Where("serial=?", serial).Update("is_lendout", model.MBOnSite).Error; err != nil {
tx.Rollback()
return
}
if err = tx.Commit().Error; err != nil {
tx.Rollback()
}
return
}

View File

@@ -0,0 +1,24 @@
package dao
import (
"go-common/app/admin/ep/merlin/model"
pkgerr "github.com/pkg/errors"
)
// InsertMobileMachineErrorLog Insert Mobile Machine Error Log.
func (d *Dao) InsertMobileMachineErrorLog(mobileMachineErrorLog *model.MobileMachineErrorLog) (err error) {
return pkgerr.WithStack(d.db.Create(mobileMachineErrorLog).Error)
}
// FindMobileMachineErrorLog Find Mobile Machine Error Log.
func (d *Dao) FindMobileMachineErrorLog(queryRequest *model.QueryMobileMachineErrorLogRequest) (total int64, mobileMachineErrorLogs []*model.MobileMachineErrorLog, err error) {
cdb := d.db.Model(&model.MobileMachineErrorLog{}).Where("machine_id=?", queryRequest.MachineID).Order("ID desc").Offset((queryRequest.PageNum - 1) * queryRequest.PageSize).Limit(queryRequest.PageSize)
if err = pkgerr.WithStack(cdb.Find(&mobileMachineErrorLogs).Error); err != nil {
return
}
if err = pkgerr.WithStack(d.db.Model(&model.MobileMachineErrorLog{}).Where("machine_id=?", queryRequest.MachineID).Count(&total).Error); err != nil {
return
}
return
}

View File

@@ -0,0 +1,80 @@
package dao
import (
"database/sql"
"fmt"
"strconv"
"go-common/app/admin/ep/merlin/model"
pkgerr "github.com/pkg/errors"
)
const (
_mobileRightJoinLogSQL = "SELECT m.id,m.serial,ml.username,ml.operation_type,ml.operation_result,ml.ctime FROM mobile_machines AS m INNER JOIN mobile_machine_logs AS ml ON m.id = ml.machine_id"
_mobileRightCountSQL = "SELECT count(m.id) FROM mobile_machines AS m INNER JOIN mobile_machine_logs AS ml ON m.id = ml.machine_id"
)
// InsertMobileMachineLog Insert Mobile Machine Log.
func (d *Dao) InsertMobileMachineLog(mobileMachineLog *model.MobileMachineLog) (err error) {
return pkgerr.WithStack(d.db.Create(mobileMachineLog).Error)
}
// FindMobileMachineLogs Find Mobile Machine Logs.
func (d *Dao) FindMobileMachineLogs(queryRequest *model.QueryMobileMachineLogRequest) (total int64, mobileMachineLogs []*model.AboundMobileMachineLog, err error) {
var (
qSQL = _mobileRightJoinLogSQL
cSQL = _mobileRightCountSQL
rows *sql.Rows
)
if queryRequest.MachineID > 0 || queryRequest.OperateType != "" || queryRequest.OperateUser != "" || queryRequest.Serial != "" {
var (
strSQL = ""
logicalWord = _where
)
if queryRequest.MachineID > 0 {
strSQL = fmt.Sprintf("%s %s ml.machine_id = %s", strSQL, logicalWord, strconv.FormatInt(queryRequest.MachineID, 10))
logicalWord = _and
}
if queryRequest.OperateType != "" {
strSQL = fmt.Sprintf("%s %s ml.operation_type like '%s'", strSQL, logicalWord, _wildcards+queryRequest.OperateType+_wildcards)
logicalWord = _and
}
if queryRequest.Serial != "" {
strSQL = fmt.Sprintf("%s %s m.serial like '%s'", strSQL, logicalWord, _wildcards+queryRequest.Serial+_wildcards)
logicalWord = _and
}
if queryRequest.OperateUser != "" {
strSQL = fmt.Sprintf("%s %s ml.username like '%s'", strSQL, logicalWord, _wildcards+queryRequest.OperateUser+_wildcards)
logicalWord = _and
}
qSQL = _mobileRightJoinLogSQL + " " + strSQL
cSQL = _mobileRightCountSQL + " " + strSQL
}
cDB := d.db.Raw(cSQL)
if err = pkgerr.WithStack(cDB.Count(&total).Error); err != nil {
return
}
gDB := d.db.Raw(qSQL)
if rows, err = gDB.Order("ml.ctime DESC").Offset((queryRequest.PageNum - 1) * queryRequest.PageSize).Limit(queryRequest.PageSize).Rows(); err != nil {
return
}
defer rows.Close()
for rows.Next() {
ml := &model.AboundMobileMachineLog{}
if err = rows.Scan(&ml.MachineID, &ml.Serial, &ml.Username, &ml.OperateType, &ml.OperateResult, &ml.OperateTime); err != nil {
return
}
mobileMachineLogs = append(mobileMachineLogs, ml)
}
return
}

View File

@@ -0,0 +1,23 @@
package dao
import (
"go-common/app/admin/ep/merlin/model"
pkgerr "github.com/pkg/errors"
)
// InsertMobileSyncLog Insert Mobile Sync Log.
func (d *Dao) InsertMobileSyncLog(mobileSyncLog *model.MobileSyncLog) (err error) {
return pkgerr.WithStack(d.db.Create(mobileSyncLog).Error)
}
// UpdateMobileSyncLog Update Mobile SyncLog.
func (d *Dao) UpdateMobileSyncLog(mobileSyncLog *model.MobileSyncLog) (err error) {
return pkgerr.WithStack(d.db.Model(&model.MobileSyncLog{}).Where("uuid=?", mobileSyncLog.UUID).Update(mobileSyncLog).Error)
}
// FindMobileSyncLogStartStatus FindMobile SyncLog Start Status.
func (d *Dao) FindMobileSyncLogStartStatus() (startCount int, err error) {
err = pkgerr.WithStack(d.db.Model(&model.MobileSyncLog{}).Where("status=0 and mtime>=DATE_SUB(NOW(),INTERVAL 10 MINUTE)").Count(&startCount).Error)
return
}

View File

@@ -0,0 +1,40 @@
package dao
import (
"go-common/app/admin/ep/merlin/model"
"go-common/library/ecode"
pkgerr "github.com/pkg/errors"
)
// InsertSnapshotRecord Insert Snapshot Record.
func (d *Dao) InsertSnapshotRecord(snapshotRecord *model.SnapshotRecord) (err error) {
return pkgerr.WithStack(d.db.Create(snapshotRecord).Error)
}
// FindSnapshotRecord Find Snapshot Record.
func (d *Dao) FindSnapshotRecord(machineID int64) (snapshotRecord *model.SnapshotRecord, err error) {
snapshotRecord = &model.SnapshotRecord{}
if err = d.db.Where("machine_id = ?", machineID).First(snapshotRecord).Error; err == ecode.NothingFound {
err = nil
}
return
}
// UpdateSnapshotRecordStatus Update Snapshot Record Status.
func (d *Dao) UpdateSnapshotRecordStatus(machineID int64, status string) (err error) {
return pkgerr.WithStack(d.db.Model(&model.SnapshotRecord{}).Where("machine_id = ?", machineID).Update("status", status).Error)
}
// UpdateSnapshotRecord Update Snapshot Record.
func (d *Dao) UpdateSnapshotRecord(snapshotRecord *model.SnapshotRecord) (err error) {
return pkgerr.WithStack(d.db.Model(&model.SnapshotRecord{}).Where("machine_id = ?", snapshotRecord.MachineID).Update(snapshotRecord).Error)
}
// FindSnapshotStatusInDoingOver2Hours Find Snapshot Status In Doing Over 2 Hours.
func (d *Dao) FindSnapshotStatusInDoingOver2Hours() (snapshotRecords []*model.SnapshotRecord, err error) {
if err = d.db.Where("status = ? and unix_timestamp(now())> unix_timestamp(date_add(mtime, interval 2 hour))", model.SnapshotDoing).Find(&snapshotRecords).Error; err == ecode.NothingFound {
err = nil
}
return
}

View File

@@ -0,0 +1,41 @@
package dao
import (
"go-common/app/admin/ep/merlin/model"
pkgerr "github.com/pkg/errors"
)
// FindDeleteMachineTasks find delete machine tasks.
func (d *Dao) FindDeleteMachineTasks() (tasks []*model.Task, err error) {
tasks = []*model.Task{}
err = pkgerr.WithStack(d.db.Where("status=? AND type=? AND DATEDIFF(NOW(),execute_time)=0 AND execute_time < NOW()", model.TaskInit, model.DeleteMachine).Find(&tasks).Error)
return
}
// UpdateTaskStatusByMachines update task status by machines.
func (d *Dao) UpdateTaskStatusByMachines(machineIDs []int64, status int) (err error) {
return pkgerr.WithStack(d.db.Model(&model.Task{}).Where("machine_id IN (?)", machineIDs).Update("status", status).Error)
}
// UpdateTaskStatusByTaskID update task status by taskId.
func (d *Dao) UpdateTaskStatusByTaskID(taskID int64, status int) (err error) {
return pkgerr.WithStack(d.db.Model(&model.Task{}).Where("id IN (?)", taskID).Update("status", status).Error)
}
// InsertDeleteMachinesTasks insert delete machines tasks.
// TODO:这块逻辑不是很好如果一个写入失败希望继续写入失败的话最好返回失败list后续联调时优化
func (d *Dao) InsertDeleteMachinesTasks(ms []*model.Machine) (err error) {
for _, machine := range ms {
mrTask := &model.Task{
TYPE: model.DeleteMachine,
ExecuteTime: machine.EndTime,
MachineID: machine.ID,
Status: model.TaskInit,
}
if tempErr := d.db.Create(mrTask).Error; tempErr != nil {
err = pkgerr.WithStack(tempErr)
}
}
return
}

View File

@@ -0,0 +1,36 @@
package dao
import (
"testing"
"time"
"go-common/app/admin/ep/merlin/model"
. "github.com/smartystreets/goconvey/convey"
)
var (
testMachine = model.Machine{
EndTime: time.Now().Add((-1) * time.Hour),
ID: 123123,
}
)
func Test_Task(t *testing.T) {
Convey("test InsertDeleteMachinesTasks", t, func() {
err := d.InsertDeleteMachinesTasks([]*model.Machine{&testMachine})
So(err, ShouldBeNil)
})
Convey("test FindDeleteMachineTasks", t, func() {
tasks, err := d.FindDeleteMachineTasks()
So(err, ShouldBeNil)
So(len(tasks), ShouldBeGreaterThan, 0)
})
Convey("test UpdateTaskStatusByMachines", t, func() {
err := d.UpdateTaskStatusByMachines([]int64{testMachine.ID}, 2)
So(err, ShouldBeNil)
})
}

View File

@@ -0,0 +1,31 @@
package dao
import (
"go-common/app/admin/ep/merlin/model"
pkgerr "github.com/pkg/errors"
)
// FindUserByUserName find user by username.
func (d *Dao) FindUserByUserName(name string) (user *model.User, err error) {
user = &model.User{}
err = pkgerr.WithStack(d.db.Where("name = ?", name).First(user).Error)
return
}
// FindUserByID find user by id.
func (d *Dao) FindUserByID(ID int64) (user *model.User, err error) {
user = &model.User{}
err = pkgerr.WithStack(d.db.Where("id = ?", ID).First(user).Error)
return
}
// CreateUser create user.
func (d *Dao) CreateUser(user *model.User) (err error) {
return pkgerr.WithStack(d.db.Create(user).Error)
}
// DelUser delete user.
func (d *Dao) DelUser(user *model.User) (err error) {
return pkgerr.WithStack(d.db.Delete(user).Error)
}

View File

@@ -0,0 +1,43 @@
package dao
import (
"testing"
"time"
"go-common/app/admin/ep/merlin/model"
. "github.com/smartystreets/goconvey/convey"
)
var (
testTime = time.Now().Format("2006_01_02_15_04_05")
testUser = model.User{
Name: testTime,
EMail: testTime + "@bilibili.com"}
)
func Test_User(t *testing.T) {
Convey("test CreateUser", t, func() {
err := d.CreateUser(&testUser)
So(err, ShouldBeNil)
})
Convey("find user by user name", t, func() {
userInDb, err := d.FindUserByUserName(testUser.Name)
So(userInDb.EMail, ShouldEqual, testUser.EMail)
So(err, ShouldBeNil)
})
Convey("find user by id", t, func() {
userID := testUser.ID
userInDb, err := d.FindUserByID(userID)
So(userInDb.EMail, ShouldEqual, testUser.EMail)
So(err, ShouldBeNil)
})
Convey("delete user", t, func() {
err := d.DelUser(&testUser)
So(err, ShouldBeNil)
})
}

View File

@@ -0,0 +1,254 @@
package dao
import (
"context"
"encoding/json"
"net/http"
"strconv"
"go-common/app/admin/ep/merlin/model"
"go-common/library/cache/memcache"
"go-common/library/ecode"
"go-common/library/log"
)
const (
_genPaasMachines = "/api/merlin/machine/create"
_delPaasMachine = "/api/merlin/machine/free"
_queryPaasMachineStatus = "/api/merlin/machine/status"
_queryPaasMachine = "/api/merlin/machine/detail"
_updatePaasMachineNode = "/api/merlin/machine/update"
_updatePaasMachineSnapshot = "/api/merlin/machine/snapshot"
_queryPaasClusters = "/api/merlin/clusters"
_queryPaasClusterByNetwork = "/api/merlin/clusters/network/"
_auth = "/api/v1/auth"
_authHeader = "X-Authorization-Token"
)
// GenPaasMachines create machine in paas.
func (d *Dao) GenPaasMachines(c context.Context, mc *model.PaasGenMachineRequest) (instances []*model.CreateInstance, err error) {
var (
req *http.Request
res = &model.PaasGenMachineResponse{}
)
if req, err = d.newPaasRequest(c, http.MethodPost, _genPaasMachines, mc); err != nil {
return
}
if err = d.httpClient.Do(c, req, &res); err != nil {
log.Error("gen paas machine url(%s) err(%v)", _genPaasMachines, err)
err = ecode.MerlinPaasRequestErr
return
}
if err = res.CheckStatus(); err != nil {
return
}
instances = res.Data
return
}
// DelPaasMachine delete machine in paas.
func (d *Dao) DelPaasMachine(c context.Context, pqadmr *model.PaasQueryAndDelMachineRequest) (instance *model.ReleaseInstance, err error) {
var (
req *http.Request
res = &model.PaasDelMachineResponse{}
)
if req, err = d.newPaasRequest(c, http.MethodPost, _delPaasMachine, pqadmr); err != nil {
return
}
if err = d.httpClient.Do(c, req, &res); err != nil {
log.Error("delete paas machine url(%s) err(%v)", _delPaasMachine, err)
err = ecode.MerlinPaasRequestErr
return
}
if err = res.CheckStatus(); err != nil {
return
}
instance = &res.Data
return
}
// QueryPaasMachineStatus query status of machine in paas.
func (d *Dao) QueryPaasMachineStatus(c context.Context, pqadmr *model.PaasQueryAndDelMachineRequest) (machineStatus *model.MachineStatus, err error) {
var (
req *http.Request
res = &model.PaasQueryMachineStatusResponse{}
)
if req, err = d.newPaasRequest(c, http.MethodPost, _queryPaasMachineStatus, pqadmr); err != nil {
return
}
if err = d.httpClient.Do(c, req, &res); err != nil {
log.Error("query paas machine status url(%s) err(%v)", _queryPaasMachineStatus, err)
err = ecode.MerlinPaasRequestErr
return
}
if err = res.CheckStatus(); err != nil {
return
}
machineStatus = &res.Data
return
}
// SnapshotPaasMachineStatus Snapshot Paas Machine Status.
func (d *Dao) SnapshotPaasMachineStatus(c context.Context, pqadmr *model.PaasQueryAndDelMachineRequest) (status int, err error) {
var (
req *http.Request
res = &model.PaasSnapshotMachineResponse{}
)
if req, err = d.newPaasRequest(c, http.MethodPost, _updatePaasMachineSnapshot, pqadmr); err != nil {
return
}
if err = d.httpClient.Do(c, req, &res); err != nil {
log.Error("snapshot machine status url(%s) err(%v)", _updatePaasMachineSnapshot, err)
err = ecode.MerlinPaasRequestErr
return
}
if err = res.CheckStatus(); err != nil {
return
}
status = res.Status
return
}
// QueryPaasMachine query detail information of machine in paas.
func (d *Dao) QueryPaasMachine(c context.Context, pqadmr *model.PaasQueryAndDelMachineRequest) (md *model.PaasMachineDetail, err error) {
var (
req *http.Request
res = &model.PaasQueryMachineResponse{}
)
if req, err = d.newPaasRequest(c, http.MethodPost, _queryPaasMachine, pqadmr); err != nil {
return
}
if err = d.httpClient.Do(c, req, &res); err != nil {
log.Error("query paas machine url(%s) err(%v)", _queryPaasMachine, err)
err = ecode.MerlinPaasRequestErr
return
}
if err = res.CheckStatus(); err != nil {
return
}
md = &res.Data
return
}
// QueryClusters query cluster information in paas.
func (d *Dao) QueryClusters(c context.Context) (clusters []*model.Cluster, err error) {
var (
req *http.Request
res = &model.PaasQueryClustersResponse{}
)
if req, err = d.newPaasRequest(c, http.MethodGet, _queryPaasClusters, nil); err != nil {
return
}
if err = d.httpClient.Do(c, req, &res); err != nil {
log.Error("d.httpSearch url(%s) error(%v)", d.c.Paas.Host+"?"+_queryPaasClusters, err)
err = ecode.MerlinPaasRequestErr
return
}
if err = res.CheckStatus(); err != nil {
return
}
clusters = res.Data.Items
return
}
// QueryCluster query cluster information in paas by giving network.
func (d *Dao) QueryCluster(c context.Context, netWordID int64) (cluster *model.Cluster, err error) {
var (
req *http.Request
res = &model.PaasQueryClusterResponse{}
)
if req, err = d.newPaasRequest(c, http.MethodGet, _queryPaasClusterByNetwork+strconv.FormatInt(netWordID, 10), nil); err != nil {
log.Error("http new request err(%v)", err)
return
}
if err = d.httpClient.Do(c, req, &res); err != nil {
log.Error("d.httpSearch url(%s) error(%v)", d.c.Paas.Host+"?"+_queryPaasClusters, err)
err = ecode.MerlinPaasRequestErr
return
}
if err = res.CheckStatus(); err != nil {
return
}
cluster = res.Data
return
}
// UpdatePaasMachineNode update paas machine node.
func (d *Dao) UpdatePaasMachineNode(c context.Context, pumnr *model.PaasUpdateMachineNodeRequest) (data string, err error) {
var (
req *http.Request
res = &model.PaasUpdateMachineNodeResponse{}
)
if req, err = d.newPaasRequest(c, http.MethodPost, _updatePaasMachineNode, pumnr); err != nil {
log.Error("http new request err(%v)", err)
return
}
if err = d.httpClient.Do(c, req, &res); err != nil {
log.Error("d.httpSearch url(%s) error(%v)", d.c.Paas.Host+_updatePaasMachineNode, err)
err = ecode.MerlinPaasRequestErr
return
}
if err = res.CheckStatus(); err != nil {
return
}
data = res.Data
return
}
func (d *Dao) authPaas(c context.Context) (token string, err error) {
var (
req *http.Request
res = &model.PaasAuthResponse{}
authRequest = model.PaasAuthRequest{
APIToken: d.c.Paas.Token,
PlatformID: "merlin",
}
)
if req, err = d.newRequest(http.MethodPost, d.c.Paas.Host+_auth, authRequest); err != nil {
return
}
if err = d.httpClient.Do(c, req, &res); err != nil {
log.Error("query paas machine url(%s) err(%v)", _auth, err)
err = ecode.MerlinPaasRequestErr
return
}
if err = res.CheckStatus(); err != nil {
return
}
token = res.Data.Token
return
}
// paasToken TODO:当前放在dao层有点不规范放在service层封装上又不如这样更好后续再考虑一下.
func (d *Dao) paasToken(c context.Context) (authToken string, err error) {
var (
item *memcache.Item
conn = d.mc.Get(c)
)
defer conn.Close()
if item, err = conn.Get(d.c.Paas.Token); err == nil {
if err = json.Unmarshal(item.Value, &authToken); err != nil {
log.Error("Json unmarshal err(%v)", err)
}
return
}
if authToken, err = d.authPaas(c); err != nil {
return
}
item = &memcache.Item{Key: d.c.Paas.Token, Object: authToken, Flags: memcache.FlagJSON, Expiration: d.expire}
d.tokenCacheSave(c, item)
return
}
func (d *Dao) newPaasRequest(c context.Context, method, uri string, v interface{}) (req *http.Request, err error) {
var authToken string
if authToken, err = d.paasToken(c); err != nil {
return
}
if req, err = d.newRequest(method, d.c.Paas.Host+uri, v); err != nil {
return
}
req.Header.Set(_authHeader, authToken)
return
}

View File

@@ -0,0 +1,156 @@
package dao
import (
"testing"
"go-common/app/admin/ep/merlin/model"
. "github.com/smartystreets/goconvey/convey"
)
var (
gmRequest = model.PaasGenMachineRequest{
Env: model.Env{
ClusterID: 3,
NetworkID: 7,
},
Machines: []model.PaasMachine{
{
Name: "vm1",
Image: "docker-reg.bilibili.co/zccdebian:1.0",
CPURequest: 1000,
MemoryRequest: 500,
DiskRequest: 20,
VolumnMount: "",
},
{
Name: "vm2",
Image: "docker-reg.bilibili.co/debian:latest",
CPURequest: 1000,
MemoryRequest: 500,
DiskRequest: 20,
VolumnMount: "",
},
},
}
qdmRequest = model.PaasQueryAndDelMachineRequest{
BusinessUnit: "ops",
Project: "zcc",
App: "zcc",
ClusterID: 3,
Name: "vm1",
}
)
func Test_GenPaasMachines(t *testing.T) {
Convey("create two machines in paas", t, WithPaasToken(func() {
data := `{"status": 200,"message":"success","data":[{"instance_name": "vm1","instance_create_status": 2},{"instance_name": "vm2","instance_create_status": 1}]}`
gmURL := d.c.Paas.Host + _genPaasMachines
httpMock("POST", gmURL).Reply(200).JSON(data)
res, err := d.GenPaasMachines(ctx(), &gmRequest)
So(err, ShouldBeNil)
So(res, ShouldResemble, []*model.CreateInstance{
{
Instance: model.Instance{InstanceName: "vm1"},
InstanceCreateStatus: 2,
},
{
Instance: model.Instance{InstanceName: "vm2"},
InstanceCreateStatus: 1,
},
})
}))
}
func Test_DelPaasMachine(t *testing.T) {
Convey("Delete machine from paas", t, WithPaasToken(func() {
data := `{"status": 200,"message": "success","data": {"instance_name": "vm1","instance_release_status": 1}}`
dmURL := d.c.Paas.Host + _delPaasMachine
httpMock("POST", dmURL).Reply(200).JSON(data)
res, err := d.DelPaasMachine(ctx(), &qdmRequest)
So(err, ShouldBeNil)
So(res, ShouldResemble, &model.ReleaseInstance{
Instance: model.Instance{InstanceName: "vm1"},
InstanceReleaseStatus: 1,
})
}))
}
func Test_QueryPaasMachineStatus(t *testing.T) {
Convey("query machine status", t, WithPaasToken(func() {
data := `{"status": 200,"message": "success","data": {"condition": "Running","message": "","detail_conditions": {"Initialized": "True","PodScheduled": "True","Ready": "True"},"instance_ip": "172.16.62.84","restart_count": 0}}`
qmsURL := d.c.Paas.Host + _queryPaasMachineStatus
httpMock("POST", qmsURL).Reply(200).JSON(data)
res, err := d.QueryPaasMachineStatus(ctx(), &qdmRequest)
So(err, ShouldBeNil)
So(res, ShouldResemble, &model.MachineStatus{
Condition: "Running",
Message: "",
DetailCondition: model.DetailCondition{
Initialized: "True",
PodScheduled: "True",
Ready: "True",
},
InstanceIP: "172.16.62.84",
RestartCount: 0,
})
}))
}
func Test_QueryPaasMachine(t *testing.T) {
Convey("query machine details", t, WithPaasToken(func() {
data := `{"status": 200,"message": "success","data": {"condition": "Running","name": "vm1","image": "docker-reg.bilibili.co/zccdebian:1.0","cpu_request": 1000,"memory_request": 512,"disk_request": 0,"volumn_mount": "","cluster_name": "shyp-fat-k8s","env": "fat1","ip": "172.16.62.84"}}`
qmdURL := d.c.Paas.Host + _queryPaasMachine
httpMock("POST", qmdURL).Reply(200).JSON(data)
res, err := d.QueryPaasMachine(ctx(), &qdmRequest)
So(err, ShouldBeNil)
So(res, ShouldResemble, &model.PaasMachineDetail{
Condition: "Running",
Name: "vm1",
Image: "docker-reg.bilibili.co/zccdebian:1.0",
CPURequest: 1000,
MemoryRequest: 512,
DiskRequest: 0,
VolumnMount: "",
ClusterName: "shyp-fat-k8s",
Env: "fat1",
IP: "172.16.62.84",
})
}))
}
func Test_QueryClusters(t *testing.T) {
Convey("query clusters from paas", t, WithPaasToken(func() {
data := `{"status": 200,"message": "success","count": 4,"data": {"items": [{"id": 1,"name": "shylf-uat-k8s","desc": "上海云立集成环境k8s集群","idc": "shylf","networks": [{"id": 16,"name": "shylf_uat_vlan1035","subnet": "172.22.35.0/24","capacity": 0.3346774193548387}],"resources": {"cpu_usage": 0.5977777777777777,"mem_usage": 0.19947637451689315,"pod_total": 439,"pod_capacity": 990,"nodes_num": 9}}]}}`
qcURL := d.c.Paas.Host + _queryPaasClusters
httpMock("GET", qcURL).Reply(200).JSON(data)
res, err := d.QueryClusters(ctx())
So(err, ShouldBeNil)
So(res, ShouldResemble, []*model.Cluster{
{
ID: 1,
Name: "shylf-uat-k8s",
Desc: "上海云立集成环境k8s集群",
IDc: "shylf",
Networks: []model.Network{
{
ID: 16,
Name: "shylf_uat_vlan1035",
Subnet: "172.22.35.0/24",
Capacity: 0.3346774193548387,
},
},
Resources: model.Resource{
CPUUsage: 0.5977777777777777,
MemUsage: 0.19947637451689315,
PodTotal: 439,
PodCapacity: 990,
NodesNum: 9,
},
},
})
}))
}

View File

@@ -0,0 +1,306 @@
package dao
import (
"context"
"encoding/json"
"fmt"
"net/http"
"go-common/app/admin/ep/merlin/model"
"go-common/library/cache/memcache"
"go-common/library/ecode"
"go-common/library/log"
)
const (
_treeURI = "/v1/node/tree"
_treeMachines = "/v1/instance/hostnames"
_treeSon = "/v1/node/extree"
_treeRole = "/v1/node/role"
_treeAppInstance = "/v1/instance/app"
_authURI = "/v1/auth"
_authPlatformURI = "/v1/token"
_treeOkCode = 90000
_sessIDKey = "_AJSESSIONID"
_treeRootName = "bilibili."
_questionMark = "?"
)
// UserTree get user tree node.
func (d *Dao) UserTree(c context.Context, sessionID string) (tree *model.UserTree, err error) {
var (
req *http.Request
res = &model.TreeResponse{}
)
if req, err = d.newTreeRequest(c, http.MethodGet, _treeURI, sessionID, nil); err != nil {
return
}
if err = d.httpClient.Do(c, req, &res); err != nil {
log.Error("d.Token url(%s) err(%v)", _treeURI, err)
err = ecode.MerlinTreeRequestErr
return
}
if res.Code != _treeOkCode {
err = fmt.Errorf("error code :%d", res.Code)
log.Error("Status url(%s) res(%v)", _treeURI, res)
return
}
tree = &res.Data
return
}
// TreeSon get user tree node son node.
func (d *Dao) TreeSon(c context.Context, sessionID, treePath string) (treeSon map[string]interface{}, err error) {
var (
req *http.Request
res = &model.TreeSonResponse{}
)
if req, err = d.newTreeRequest(c, http.MethodGet, _treeSon+"/"+d.getTreeFullPath(treePath), sessionID, nil); err != nil {
return
}
if err = d.httpClient.Do(c, req, &res); err != nil {
log.Error("d.Token url(%s) err(%v)", _treeSon, err)
err = ecode.MerlinTreeRequestErr
return
}
if res.Code != _treeOkCode {
err = fmt.Errorf("error code :%d", res.Code)
log.Error("Status url(%s) res(%v)", _treeSon, res)
return
}
return res.Data, nil
}
// TreeRoles get tree roles.
func (d *Dao) TreeRoles(c context.Context, sessionID, treePath string) (treeRoles []*model.TreeRole, err error) {
var (
req *http.Request
res = &model.TreeRoleResponse{}
)
if req, err = d.newTreeRequest(c, http.MethodGet, _treeRole+"/"+d.getTreeFullPath(treePath), sessionID, nil); err != nil {
return
}
if err = d.httpClient.Do(c, req, &res); err != nil {
log.Error("d.Token url(%s) err(%v)", _treeRole, err)
err = ecode.MerlinTreeRequestErr
return
}
if res.Code != _treeOkCode {
err = fmt.Errorf("error code :%d", res.Code)
log.Error("Status url(%s) res(%v)", _treeRole, res)
return
}
return res.Data, nil
}
// TreeRolesAsPlatform get tree roles.
func (d *Dao) TreeRolesAsPlatform(c context.Context, treePath string) (treeRoles []*model.TreeRole, err error) {
var (
req *http.Request
res = &model.TreeRoleResponse{}
)
if req, err = d.newPlatformTreeRequest(c, http.MethodGet, _treeRole+"/"+d.getTreeFullPath(treePath), nil); err != nil {
return
}
if err = d.httpClient.Do(c, req, &res); err != nil {
log.Error("d.Token url(%s) err(%v)", _treeRole, err)
err = ecode.MerlinTreeRequestErr
return
}
if res.Code != _treeOkCode {
err = fmt.Errorf("error code :%d", res.Code)
log.Error("Status url(%s) res(%v)", _treeRole, res)
return
}
return res.Data, nil
}
//QueryTreeInstances query tree instances
func (d *Dao) QueryTreeInstances(c context.Context, sessionID string, tir *model.TreeInstanceRequest) (tid map[string]*model.TreeInstance, err error) {
var (
req *http.Request
res = &model.TreeInstancesResponse{}
)
if req, err = d.newTreeRequest(c, http.MethodGet, _treeMachines+_questionMark+tir.ToQueryURI(), sessionID, nil); err != nil {
return
}
if err = d.httpClient.Do(c, req, &res); err != nil {
log.Error("d.Token url(%s) err(%v)", _treeRole, err)
err = ecode.MerlinTreeRequestErr
return
}
if res.Code != _treeOkCode {
log.Error("Status url(%s) res(%v)", _treeRole, res)
err = ecode.MerlinTreeResponseErr
return
}
tid = res.Data
return
}
// TreeAppInstance get user tree node app instance.
func (d *Dao) TreeAppInstance(c context.Context, treePaths []string) (treeAppInstances map[string][]*model.TreeAppInstance, err error) {
var (
req *http.Request
res = &model.TreeAppInstanceResponse{}
fullTreePaths []string
)
for _, treePath := range treePaths {
fullTreePaths = append(fullTreePaths, d.getTreeFullPath(treePath))
}
treeAppInstanceRequest := &model.TreeAppInstanceRequest{
Paths: fullTreePaths,
}
if req, err = d.newPlatformTreeRequest(c, http.MethodPost, _treeAppInstance+"?type=container", treeAppInstanceRequest); err != nil {
return
}
if err = d.httpClient.Do(c, req, &res); err != nil {
log.Error("d.Token url(%s) err(%v)", _treeRole, err)
err = ecode.MerlinTreeRequestErr
return
}
if res.Code != _treeOkCode {
err = fmt.Errorf("error code :%d", res.Code)
log.Error("Status url(%s) res(%v)", _treeRole, res)
return
}
return res.Data, nil
}
func (d *Dao) treeToken(c context.Context, sessionID string) (authToken string, err error) {
var (
conn = d.mc.Get(c)
item *memcache.Item
)
defer conn.Close()
if item, err = conn.Get(sessionID); err == nil {
if err = json.Unmarshal(item.Value, &authToken); err != nil {
log.Error("treePlatformToken json parse error(%v)", err)
}
return
}
if authToken, err = d.authTree(c, sessionID); err != nil {
return
}
item = &memcache.Item{Key: sessionID, Object: authToken, Flags: memcache.FlagJSON, Expiration: d.expire}
d.tokenCacheSave(c, item)
return
}
func (d *Dao) authTree(c context.Context, sessionID string) (authToken string, err error) {
var (
req *http.Request
tokenURL = d.c.ServiceTree.Host + _authURI
res struct {
Code int `json:"code"`
Data map[string]interface{} `json:"data"`
Message string `json:"message"`
Status int `json:"status"`
}
)
if req, err = d.newRequest(http.MethodGet, tokenURL, nil); err != nil {
return
}
req.Header.Set("Cookie", _sessIDKey+"="+sessionID)
if err = d.httpClient.Do(c, req, &res); err != nil {
err = ecode.MerlinTreeRequestErr
log.Error("d.Token url(%s) res($s) err(%v)", tokenURL, res, err)
return
}
if res.Code != _treeOkCode {
log.Error("Status url(%s) res(%v)", tokenURL, res)
return
}
authToken = res.Data["token"].(string)
return
}
func (d *Dao) newTreeRequest(c context.Context, method, uri, sessionID string, v interface{}) (req *http.Request, err error) {
var authToken string
if authToken, err = d.treeToken(c, sessionID); err != nil {
return
}
if req, err = d.newRequest(method, d.c.ServiceTree.Host+uri, v); err != nil {
return
}
req.Header.Set(_authHeader, authToken)
return
}
func (d *Dao) treePlatformToken(c context.Context) (authToken string, err error) {
var (
conn = d.mc.Get(c)
item *memcache.Item
username = d.c.ServiceTree.Key
secret = d.c.ServiceTree.Secret
)
defer conn.Close()
if item, err = conn.Get(secret); err == nil {
if err = json.Unmarshal(item.Value, &authToken); err != nil {
log.Error("treePlatformToken json parse error(%v)", err)
}
return
}
if authToken, err = d.authPlatformTree(c, username, secret); err != nil {
return
}
item = &memcache.Item{Key: secret, Object: authToken, Flags: memcache.FlagJSON, Expiration: d.expire}
d.tokenCacheSave(c, item)
return
}
func (d *Dao) authPlatformTree(c context.Context, username, platformID string) (authToken string, err error) {
var (
req *http.Request
tokenURL = d.c.ServiceTree.Host + _authPlatformURI
res struct {
Code int `json:"code"`
Data map[string]interface{} `json:"data"`
Message string `json:"message"`
Status int `json:"status"`
}
)
treePlatformTokenRequest := &model.TreePlatformTokenRequest{
UserName: username,
PlatformID: platformID,
}
if req, err = d.newRequest(http.MethodPost, tokenURL, treePlatformTokenRequest); err != nil {
return
}
if err = d.httpClient.Do(c, req, &res); err != nil {
err = ecode.MerlinTreeRequestErr
log.Error("d.Token url(%s) res($s) err(%v)", tokenURL, res, err)
return
}
if res.Code != _treeOkCode {
log.Error("Status url(%s) res(%v)", tokenURL, res)
return
}
authToken = res.Data["token"].(string)
return
}
func (d *Dao) newPlatformTreeRequest(c context.Context, method, uri string, v interface{}) (req *http.Request, err error) {
var authToken string
if authToken, err = d.treePlatformToken(c); err != nil {
return
}
if req, err = d.newRequest(method, d.c.ServiceTree.Host+uri, v); err != nil {
return
}
req.Header.Set(_authHeader, authToken)
return
}
func (d *Dao) getTreeFullPath(path string) string {
return _treeRootName + path
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,41 @@
package dao
import (
"context"
"encoding/json"
"net/http"
"go-common/app/admin/ep/merlin/conf"
"go-common/app/admin/ep/merlin/model"
"go-common/library/log"
)
const (
_wechatGroup = "/ep/admin/saga/v2/wechat/appchat/send"
)
//WeChatSendMessage We Chat Send Message
func (d *Dao) WeChatSendMessage(c context.Context, msgSendReq *model.MsgSendReq) (msgSendRes *model.MsgSendRes, err error) {
var (
url = conf.Conf.WeChat.WeChatHost + _wechatGroup
req *http.Request
res = &model.MsgSendRes{}
)
msgSendRequest, _ := json.Marshal(msgSendReq)
log.Info("url:(%s)", url)
log.Info("msgSendRequest:(%s)", string(msgSendRequest))
if req, err = d.newRequest(http.MethodPost, url, msgSendReq); err != nil {
return
}
req.Header.Set("Content-Type", "application/json")
if err = d.httpClient.Do(c, req, &res); err != nil {
log.Error("d.AddWechatSend url(%s) res($s) error(%v)", url, res, err)
return
}
msgSendRes = res
rsp, _ := json.Marshal(msgSendRes)
log.Info("wechat send message response :(%s)", string(rsp))
return
}

View File

@@ -0,0 +1,51 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = [
"audit.go",
"bilihub.go",
"cluster.go",
"dashboard.go",
"devicefarm.go",
"http.go",
"image.go",
"machine.go",
"machineV2.go",
"node.go",
"tree.go",
"user.go",
],
importpath = "go-common/app/admin/ep/merlin/http",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/admin/ep/merlin/conf:go_default_library",
"//app/admin/ep/merlin/model:go_default_library",
"//app/admin/ep/merlin/service:go_default_library",
"//library/ecode:go_default_library",
"//library/log:go_default_library",
"//library/net/http/blademaster:go_default_library",
"//library/net/http/blademaster/binding:go_default_library",
"//library/net/http/blademaster/middleware/permit: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"],
)

View File

@@ -0,0 +1,141 @@
package http
import (
"go-common/app/admin/ep/merlin/model"
bm "go-common/library/net/http/blademaster"
"go-common/library/net/http/blademaster/binding"
)
func delayMachineEndTime(c *bm.Context) {
var (
username string
v = new(struct {
MachineID int64 `form:"machine_id"`
})
err error
)
if username, err = getUsername(c); err != nil {
return
}
if err = c.Bind(v); err != nil {
return
}
c.JSON(svc.DelayMachineEndTime(c, v.MachineID, username))
}
func applyMachineEndTime(c *bm.Context) {
var (
applyEndTimeRequest = &model.ApplyEndTimeRequest{}
err error
username string
)
if username, err = getUsername(c); err != nil {
return
}
if err = c.BindWith(applyEndTimeRequest, binding.JSON); err != nil {
return
}
c.JSON(svc.ApplyDelayMachineEndTime(c, username, applyEndTimeRequest))
}
func cancelMachineEndTime(c *bm.Context) {
var (
username string
v = new(struct {
AuditID int64 `form:"audit_id"`
})
err error
)
if username, err = getUsername(c); err != nil {
return
}
if err = c.Bind(v); err != nil {
return
}
c.JSON(svc.CancelMachineEndTime(c, v.AuditID, username))
}
func auditMachineEndTime(c *bm.Context) {
var (
username string
auditEndTimeRequest = &model.AuditEndTimeRequest{}
err error
)
if username, err = getUsername(c); err != nil {
return
}
if err = c.BindWith(auditEndTimeRequest, binding.JSON); err != nil {
return
}
c.JSON(svc.AuditMachineEndTime(c, auditEndTimeRequest.AuditID, username, auditEndTimeRequest.AuditResult, auditEndTimeRequest.Comment))
}
func queryApplicationRecordsByApplicant(c *bm.Context) {
var (
v = &model.Pagination{}
err error
username string
)
if username, err = getUsername(c); err != nil {
return
}
if err = c.Bind(v); err != nil {
return
}
if err = v.Verify(); err != nil {
c.JSON(nil, err)
return
}
c.JSON(svc.GetApplicationRecordsByApplicant(c, username, v.PageNum, v.PageSize))
}
func queryApplicationRecordsByAuditor(c *bm.Context) {
var (
v = &model.Pagination{}
err error
username string
)
if username, err = getUsername(c); err != nil {
return
}
if err = c.Bind(v); err != nil {
return
}
if err = v.Verify(); err != nil {
c.JSON(nil, err)
return
}
c.JSON(svc.GetApplicationRecordsByAuditor(c, username, v.PageNum, v.PageSize))
}
func queryApplicationRecordsByMachineID(c *bm.Context) {
v := new(struct {
model.Pagination
MachineID int64 `form:"machine_id"`
})
if err := c.Bind(v); err != nil {
return
}
if err := v.Verify(); err != nil {
c.JSON(nil, err)
return
}
c.JSON(svc.GetApplicationRecordsByMachineID(c, v.MachineID, v.PageNum, v.PageSize))
}

View File

@@ -0,0 +1,337 @@
package http
import (
"net/http"
"go-common/app/admin/ep/merlin/model"
bm "go-common/library/net/http/blademaster"
"go-common/library/net/http/blademaster/binding"
)
func projects(c *bm.Context) {
var (
username string
err error
)
if username, err = getUsername(c); err != nil {
return
}
c.JSON(svc.Projects(c, username))
}
func accessPullProjects(c *bm.Context) {
var (
username string
err error
)
if username, err = getUsername(c); err != nil {
return
}
c.JSON(svc.AccessPullProjects(c, username))
}
func authHub(c *bm.Context) {
var (
err error
session *http.Cookie
)
if session, err = c.Request.Cookie(_sessIDKey); err != nil {
return
}
c.JSON(nil, svc.AuthHub(c, session.Value))
}
func accessAuthHub(c *bm.Context) {
var (
username string
err error
)
if username, err = getUsername(c); err != nil {
return
}
c.JSON(svc.AccessAuthHub(c, username))
}
func repos(c *bm.Context) {
v := new(struct {
model.Pagination
ProjectID int `form:"project_id"`
KeyWord string `form:"key_word"`
})
if err := c.Bind(v); err != nil {
return
}
c.JSON(svc.ProjectRepositories(c, v.ProjectID, v.PageNum, v.PageSize, v.KeyWord))
}
func tags(c *bm.Context) {
v := new(struct {
RepoName string `form:"repository_name"`
})
if err := c.Bind(v); err != nil {
return
}
c.JSON(svc.RepositoryTags(c, v.RepoName))
}
func deleteRepoTag(c *bm.Context) {
var (
v = new(struct {
RepoName string `form:"repository_name"`
TagName string `form:"tag_name"`
})
username string
err error
)
if username, err = getUsername(c); err != nil {
return
}
if err = c.Bind(v); err != nil {
return
}
c.JSON(svc.DeleteRepositoryTag(c, username, v.RepoName, v.TagName))
}
func deleteRepo(c *bm.Context) {
var (
v = new(struct {
RepoName string `form:"repository_name"`
})
username string
err error
)
if username, err = getUsername(c); err != nil {
return
}
if err = c.Bind(v); err != nil {
return
}
c.JSON(svc.DeleteRepository(c, username, v.RepoName))
}
func allImage(c *bm.Context) {
c.JSON(svc.GetAllImagesInDocker())
}
func addTag(c *bm.Context) {
var (
v = new(struct {
RepoName string `json:"repository_name"`
TagName string `json:"tag_name"`
NewRepoName string `json:"new_repository"`
NewTagName string `json:"new_tag"`
})
username string
err error
)
if username, err = getUsername(c); err != nil {
return
}
if err = c.BindWith(v, binding.JSON); err != nil {
return
}
c.JSON(svc.AddRepositoryTag(c, username, v.RepoName, v.TagName, v.NewRepoName, v.NewTagName))
}
func push(c *bm.Context) {
var (
v = new(struct {
RepoName string `json:"repository_name"`
TagName string `json:"tag_name"`
})
username string
err error
)
if username, err = getUsername(c); err != nil {
return
}
if err = c.BindWith(v, binding.JSON); err != nil {
return
}
c.JSON(svc.Push(c, username, v.RepoName, v.TagName, 0))
}
func reTag(c *bm.Context) {
var (
v = new(struct {
RepoName string `json:"repository_name"`
TagName string `json:"tag_name"`
NewRepoName string `json:"new_repository"`
NewTagName string `json:"new_tag"`
})
username string
err error
)
if username, err = getUsername(c); err != nil {
return
}
if err = c.BindWith(v, binding.JSON); err != nil {
return
}
c.JSON(svc.ReTag(c, username, v.RepoName, v.TagName, v.NewRepoName, v.NewTagName, 0))
}
func pull(c *bm.Context) {
var (
v = new(struct {
RepoName string `json:"repository_name"`
TagName string `json:"tag_name"`
})
username string
err error
)
if username, err = getUsername(c); err != nil {
return
}
if err = c.BindWith(v, binding.JSON); err != nil {
return
}
c.JSON(svc.Pull(c, username, v.RepoName, v.TagName, 0))
}
func snapshot(c *bm.Context) {
var (
v = new(struct {
MachineID int64 `form:"machine_id"`
})
username string
err error
)
if username, err = getUsername(c); err != nil {
return
}
if err = c.Bind(v); err != nil {
return
}
c.JSON(svc.CreateSnapShot(c, username, v.MachineID))
}
func querySnapshot(c *bm.Context) {
var (
v = new(struct {
MachineID int64 `form:"machine_id"`
})
err error
)
if err = c.Bind(v); err != nil {
return
}
c.JSON(svc.QuerySnapShot(c, v.MachineID))
}
func callbackSnapshot(c *bm.Context) {
var (
v = new(struct {
MachineName string `json:"name"`
ImageName string `json:"image_name"`
ResultStatus bool `json:"status"`
Message string `json:"msg"`
})
err error
)
if err = c.BindWith(v, binding.JSON); err != nil {
return
}
c.JSON(nil, svc.CallBackSnapShot(c, v.MachineName, v.ImageName, v.Message, v.ResultStatus))
}
func machine2image(c *bm.Context) {
var (
username string
err error
v = new(struct {
MachineID int64 `json:"machine_id"`
ImageName string `json:"image_name"`
NewImageName string `json:"new_image_name"`
})
)
if err = c.BindWith(v, binding.JSON); err != nil {
return
}
if username, err = getUsername(c); err != nil {
return
}
c.JSON(nil, svc.Machine2Image(c, username, v.ImageName, v.NewImageName, v.MachineID))
}
func queryMachine2ImageLog(c *bm.Context) {
var (
v = &model.QueryMachine2ImageLogRequest{}
err error
)
if err = c.Bind(v); err != nil {
return
}
c.JSON(svc.QueryMachine2ImageLog(c, v))
}
func machine2imageForceFailed(c *bm.Context) {
var (
v = new(struct {
MachineID int64 `form:"machine_id"`
})
err error
)
if err = c.Bind(v); err != nil {
return
}
c.JSON(svc.Machine2ImageForceFailed(c, v.MachineID))
}
func updateImageConf(c *bm.Context) {
var (
username string
err error
v = &model.ImageConfiguration{}
)
if err = c.BindWith(v, binding.JSON); err != nil {
return
}
if username, err = getUsername(c); err != nil {
return
}
c.JSON(svc.UpdateImageConf(c, username, v))
}
func queryImageConf(c *bm.Context) {
var (
v = new(struct {
ImageName string `form:"image_full_name"`
})
err error
)
if err = c.Bind(v); err != nil {
return
}
c.JSON(svc.QueryImageConf(c, v.ImageName))
}

View File

@@ -0,0 +1,9 @@
package http
import (
bm "go-common/library/net/http/blademaster"
)
func queryCluster(c *bm.Context) {
c.JSON(svc.QueryCluster(c))
}

View File

@@ -0,0 +1,33 @@
package http
import (
bm "go-common/library/net/http/blademaster"
)
func queryMachineLifeCycle(c *bm.Context) {
c.JSON(svc.QueryMachineLifeCycle(c))
}
func queryMachineCount(c *bm.Context) {
c.JSON(svc.QueryMachineCount(c))
}
func queryMachineTime(c *bm.Context) {
c.JSON(svc.QueryMachineCreatedAndEndTime(c))
}
func queryMachineUsage(c *bm.Context) {
c.JSON(svc.QueryMachineUsage(c))
}
func queryMobileMachineUsageCount(c *bm.Context) {
c.JSON(svc.QueryMobileMachineUsageCount(c))
}
func queryMobileMachineModeCount(c *bm.Context) {
c.JSON(svc.QueryMobileMachineModeCount(c))
}
func queryMobileMachineUsageTime(c *bm.Context) {
c.JSON(svc.QueryMobileMachineUsageTime(c))
}

View File

@@ -0,0 +1,275 @@
package http
import (
"go-common/app/admin/ep/merlin/model"
bm "go-common/library/net/http/blademaster"
"go-common/library/net/http/blademaster/binding"
)
func queryMobileDevice(c *bm.Context) {
var (
qdfr = &model.QueryMobileDeviceRequest{}
err error
)
if err = c.BindWith(qdfr, binding.JSON); err != nil {
return
}
if err = qdfr.Verify(); err != nil {
c.JSON(nil, err)
return
}
c.JSON(svc.QueryMobileDeviceList(c, qdfr))
}
func refreshMobileDeviceDetail(c *bm.Context) {
v := new(struct {
ID int64 `form:"id"`
})
if err := c.Bind(v); err != nil {
return
}
c.JSON(svc.RefreshMobileDeviceDetail(c, v.ID))
}
func bindMobileDevice(c *bm.Context) {
var (
username string
v = new(struct {
ID int64 `form:"id"`
})
err error
)
if username, err = getUsername(c); err != nil {
return
}
if err = c.Bind(v); err != nil {
return
}
c.JSON(svc.BindMobileDevice(c, v.ID, username))
}
func releaseMobileDevice(c *bm.Context) {
var (
username string
v = new(struct {
ID int64 `form:"id"`
})
err error
)
if username, err = getUsername(c); err != nil {
return
}
if err = c.Bind(v); err != nil {
return
}
c.JSON(svc.ReleaseMobileDevice(c, v.ID, username))
}
func lendOutMobileDevice(c *bm.Context) {
var (
username string
v = new(struct {
ID int64 `form:"id"`
})
err error
)
if username, err = getUsername(c); err != nil {
return
}
if err = c.Bind(v); err != nil {
return
}
c.JSON(svc.LendOutMobileDevice(c, v.ID, username))
}
func returnMobileDevice(c *bm.Context) {
var (
username string
v = new(struct {
ID int64 `form:"id"`
})
err error
)
if username, err = getUsername(c); err != nil {
return
}
if err = c.Bind(v); err != nil {
return
}
c.JSON(svc.ReturnMobileDevice(c, v.ID, username))
}
func startMobileDevice(c *bm.Context) {
var (
username string
v = new(struct {
ID int64 `form:"id"`
})
err error
)
if username, err = getUsername(c); err != nil {
return
}
if err = c.Bind(v); err != nil {
return
}
c.JSON(svc.StartMobileDevice(c, v.ID, username))
}
func shutDownMobileDevice(c *bm.Context) {
var (
username string
v = new(struct {
ID int64 `form:"id"`
})
err error
)
if username, err = getUsername(c); err != nil {
return
}
if err = c.Bind(v); err != nil {
return
}
c.JSON(svc.ShutdownMobileDevice(c, v.ID, username))
}
func syncMobileDevice(c *bm.Context) {
c.JSON(svc.SyncMobileDeviceList(c))
}
func queryCategory(c *bm.Context) {
var (
v = new(struct {
IsShowOfflineMB bool `form:"is_show_offline"`
})
err error
)
if err = c.Bind(v); err != nil {
return
}
c.JSON(svc.MobileMachineCategory(c, v.IsShowOfflineMB))
}
func reportMobileDeviceError(c *bm.Context) {
var (
v = new(struct {
SerialName string `json:"serial"`
ErrorMessage string `json:"error_message"`
ErrorCode int `json:"error_code"`
})
err error
)
if err = c.BindWith(v, binding.JSON); err != nil {
return
}
c.JSON(nil, svc.ReportMobileDeviceError(c, v.SerialName, v.ErrorMessage, v.ErrorCode))
}
func queryMobileMachineLogs(c *bm.Context) {
v := &model.QueryMobileMachineLogRequest{}
if err := c.Bind(v); err != nil {
return
}
if err := v.Verify(); err != nil {
c.JSON(nil, err)
return
}
c.JSON(svc.QueryMobileMachineLogs(c, v))
}
func queryMobileMachineLendOut(c *bm.Context) {
var (
v = &model.QueryMobileMachineLogRequest{}
err error
username string
)
if err = c.Bind(v); err != nil {
return
}
if err = v.Verify(); err != nil {
c.JSON(nil, err)
return
}
if username, err = getUsername(c); err != nil {
return
}
c.JSON(svc.QueryMobileMachineLendOutLogs(c, username, v))
}
func queryMobileMachineErrorLogs(c *bm.Context) {
v := &model.QueryMobileMachineErrorLogRequest{}
if err := c.Bind(v); err != nil {
return
}
if err := v.Verify(); err != nil {
c.JSON(nil, err)
return
}
c.JSON(svc.QueryMobileMachineErrorLogs(c, v))
}
func callbackMobileDeviceError(c *bm.Context) {
var (
v = new(struct {
SerialName string `json:"serial"`
ErrorMessage string `json:"error_message"`
ErrorCode int `json:"error_code"`
})
err error
)
if err = c.BindWith(v, binding.JSON); err != nil {
return
}
c.JSON(nil, svc.CallBackMobileDeviceError(c, v.SerialName, v.ErrorMessage, v.ErrorCode))
}
func queryDeviceFarmSuperUser(c *bm.Context) {
c.JSON(svc.DeviceFarmSuperUser(), nil)
}
func isBindByTheUser(c *bm.Context) {
var (
username string
v = new(struct {
ID int64 `form:"id"`
})
err error
)
if username, err = getUsername(c); err != nil {
return
}
if err = c.Bind(v); err != nil {
return
}
c.JSON(svc.IsBindMobileDeviceByTheUser(c, v.ID, username))
}

View File

@@ -0,0 +1,242 @@
package http
import (
"net/http"
"go-common/app/admin/ep/merlin/conf"
"go-common/app/admin/ep/merlin/service"
"go-common/library/log"
bm "go-common/library/net/http/blademaster"
"go-common/library/net/http/blademaster/middleware/permit"
)
const (
_sessIDKey = "_AJSESSIONID"
)
var (
svc *service.Service
authSvc *permit.Permit
)
// Init init
func Init(c *conf.Config, s *service.Service) {
svc = s
authSvc = permit.New(c.Auth)
engine := bm.DefaultServer(c.BM)
engine.Ping(ping)
outerRouter(engine)
if err := engine.Start(); err != nil {
log.Error("engine.Start error(%v)", err)
panic(err)
}
}
// outerRouter init outer router api path.
func outerRouter(e *bm.Engine) {
e.GET("/ep/admin/merlin/version", getVersion)
e.GET("/ep/admin/merlin/conf/version", confVersion)
base := e.Group("/ep/admin/merlin", authSvc.Permit(""))
{
v1 := base.Group("/v1")
{
cluster := v1.Group("/cluster")
{
cluster.GET("/query", queryCluster)
}
machine := v1.Group("/machine")
{
machine.POST("/gen", genMachines)
machine.GET("/del", delMachine)
machine.GET("/query/detail", queryMachineDetail)
machine.GET("/query", queryMachines)
machine.GET("/query/status", queryMachineStatus)
machine.GET("/transfer", transferMachine)
machinePackage := machine.Group("/package")
{
machinePackage.GET("/query", queryMachinePackages)
}
machineLog := machine.Group("/log")
{
machineLog.GET("/query", queryMachineLogs)
}
machineNode := machine.Group("/node")
{
machineNode.POST("update", updateNodes)
machineNode.GET("query", queryNodes)
}
}
image := v1.Group("image")
{
image.GET("/query", queryImage)
image.POST("/add", addImage)
image.POST("/update", updateImage)
image.POST("/del", delImage)
}
serviceTree := v1.Group("/tree")
{
serviceTree.GET("/query", userTree)
serviceTree.GET("/container/query", userTreeContainer)
serviceTree.GET("/auditors/query", treeAuditors)
}
audit := v1.Group("/audit")
{
auditEndTime := audit.Group("/endTime")
{
auditEndTime.GET("/delay", delayMachineEndTime) //手动延期 done ok
auditEndTime.POST("/apply", applyMachineEndTime) //申请延期 done
auditEndTime.GET("/cancel", cancelMachineEndTime) //取消延期 done
auditEndTime.POST("/audit", auditMachineEndTime) //审批 通过或驳回 done
auditEndTime.GET("/query/applyList", queryApplicationRecordsByMachineID) //done ok
auditEndTime.GET("/query/user/applyList", queryApplicationRecordsByApplicant) //done ok
auditEndTime.GET("/query/user/auditList", queryApplicationRecordsByAuditor) //done ok
}
}
user := v1.Group("/user")
{
user.GET("/query", queryUserInfo)
}
mobileDevice := v1.Group("/mobiledevice")
{
mobileDevice.POST("/query", queryMobileDevice)
mobileDevice.GET("/refresh", refreshMobileDeviceDetail)
mobileDevice.GET("/bind", bindMobileDevice)
mobileDevice.GET("/release", releaseMobileDevice)
mobileDevice.GET("/isbind", isBindByTheUser)
mobileDevice.GET("/pullout", lendOutMobileDevice)
mobileDevice.GET("/return", returnMobileDevice)
mobileDevice.GET("/start", startMobileDevice)
mobileDevice.GET("/shutdown", shutDownMobileDevice)
mobileDevice.GET("/syncall", syncMobileDevice)
mobileDevice.GET("/category/query", queryCategory)
mobileDevice.GET("/superuser/query", queryDeviceFarmSuperUser)
mobileDeviceLog := mobileDevice.Group("/log")
{
mobileDeviceLog.GET("/query", queryMobileMachineLogs)
mobileDeviceLog.GET("/lendout/query", queryMobileMachineLendOut)
}
mobileDeviceErrorLog := mobileDevice.Group("/error/log")
{
mobileDeviceErrorLog.GET("/query", queryMobileMachineErrorLogs)
mobileDeviceErrorLog.POST("/report", reportMobileDeviceError)
}
}
biliHub := v1.Group("/bilihub")
{
biliHub.GET("/auth", authHub)
biliHub.GET("/auth/access", accessAuthHub)
biliHub.GET("/projects/accesspull", accessPullProjects)
biliHub.GET("/projects", projects)
biliHub.GET("/repos", repos)
biliHub.GET("/repotags", tags)
biliHub.GET("/repos/delete", deleteRepo)
biliHub.GET("/repotags/delete", deleteRepoTag)
biliHub.GET("/snapshot", snapshot)
biliHub.GET("/snapshot/query", querySnapshot)
biliHub.POST("/machine2image", machine2image)
biliHub.GET("/machine2image/forcefailed", machine2imageForceFailed)
biliHub.GET("/machine2image/log/query", queryMachine2ImageLog)
image := biliHub.Group("/image")
{
image.GET("/all", allImage)
image.POST("/addtag", addTag)
image.POST("/push", push)
image.POST("/retag", reTag)
image.POST("/pull", pull)
conf := image.Group("/conf")
{
conf.POST("/update", updateImageConf)
conf.GET("/query", queryImageConf)
}
}
}
dashboard := v1.Group("/dashboard")
{
machine := dashboard.Group("/machine")
{
machine.GET("/lifecycle", queryMachineLifeCycle)
machine.GET("/count", queryMachineCount)
machine.GET("/time", queryMachineTime)
machine.GET("/usage", queryMachineUsage)
}
deviceFarm := dashboard.Group("/devicefarm")
{
deviceFarm.GET("/usagecount", queryMobileMachineUsageCount)
deviceFarm.GET("/modecount", queryMobileMachineModeCount)
deviceFarm.GET("/usagetime", queryMobileMachineUsageTime)
}
}
}
v2 := base.Group("/v2")
{
machine := v2.Group("/machine")
{
machine.POST("/gen", genMachinesV2)
}
}
}
callback := e.Group("/ep/admin/merlin/callback")
{
v1 := callback.Group("/v1")
{
v1.POST("/bilihub/snapshot", callbackSnapshot)
v1.POST("/mobiledevice/error", callbackMobileDeviceError)
}
}
}
func ping(c *bm.Context) {
if err := svc.Ping(c); err != nil {
log.Error("ping error(%v)", err)
c.AbortWithStatus(http.StatusServiceUnavailable)
}
}
func getVersion(c *bm.Context) {
v := new(struct {
Version string `json:"version"`
})
v.Version = "v.1.5.9.3"
c.JSON(v, nil)
}
func confVersion(c *bm.Context) {
v := new(struct {
Version string `json:"version"`
})
v.Version = svc.ConfVersion(c)
c.JSON(v, nil)
}

View File

@@ -0,0 +1,44 @@
package http
import (
"go-common/app/admin/ep/merlin/model"
bm "go-common/library/net/http/blademaster"
"go-common/library/net/http/blademaster/binding"
)
func queryImage(c *bm.Context) {
c.JSON(svc.QueryImage())
}
func addImage(c *bm.Context) {
var (
image = &model.Image{}
err error
)
if err = c.BindWith(image, binding.JSON); err != nil {
return
}
c.JSON(nil, svc.AddImage(image))
}
func updateImage(c *bm.Context) {
var (
image = &model.Image{}
err error
)
if err = c.BindWith(image, binding.JSON); err != nil {
return
}
c.JSON(nil, svc.UpdateImage(image))
}
func delImage(c *bm.Context) {
var (
image = &model.Image{}
err error
)
if err = c.BindWith(image, binding.JSON); err != nil {
return
}
c.JSON(nil, svc.DeleteImage(image.ID))
}

View File

@@ -0,0 +1,131 @@
package http
import (
"net/http"
"go-common/app/admin/ep/merlin/model"
bm "go-common/library/net/http/blademaster"
"go-common/library/net/http/blademaster/binding"
)
func genMachines(c *bm.Context) {
var (
gmr = &model.GenMachinesRequest{}
err error
username string
)
if username, err = getUsername(c); err != nil {
return
}
if err = c.BindWith(gmr, binding.JSON); err != nil {
return
}
if err = gmr.Verify(); err != nil {
c.JSON(nil, err)
return
}
c.JSON(nil, svc.GenMachines(c, gmr, username))
}
func delMachine(c *bm.Context) {
var (
username string
v = new(struct {
ID int64 `form:"id"`
})
err error
)
if username, err = getUsername(c); err != nil {
return
}
if err = c.Bind(v); err != nil {
return
}
c.JSON(svc.DelMachineWhenCanBeDel(c, v.ID, username))
}
func queryMachineDetail(c *bm.Context) {
v := new(struct {
ID int64 `form:"id"`
})
if err := c.Bind(v); err != nil {
return
}
c.JSON(svc.QueryMachineDetail(c, v.ID))
}
func queryMachinePackages(c *bm.Context) {
c.JSON(svc.QueryMachinePackages(c))
}
func queryMachines(c *bm.Context) {
var (
qmr = &model.QueryMachineRequest{}
session *http.Cookie
err error
)
if err = c.Bind(qmr); err != nil {
return
}
if err = qmr.Verify(); err != nil {
c.JSON(nil, err)
return
}
if session, err = c.Request.Cookie(_sessIDKey); err != nil {
return
}
if qmr.Requester, err = getUsername(c); err != nil {
return
}
c.JSON(svc.QueryMachines(c, session.Value, qmr))
}
func queryMachineLogs(c *bm.Context) {
v := &model.QueryMachineLogRequest{}
if err := c.Bind(v); err != nil {
return
}
if err := v.Verify(); err != nil {
c.JSON(nil, err)
return
}
c.JSON(svc.QueryMachineLogs(c, v))
}
func queryMachineStatus(c *bm.Context) {
v := new(struct {
MachineID int64 `form:"machine_id"`
})
if err := c.Bind(v); err != nil {
return
}
c.JSON(svc.QueryMachineStatus(c, v.MachineID))
}
func transferMachine(c *bm.Context) {
var (
v = new(struct {
MachineID int64 `form:"machine_id"`
Receiver string `form:"receiver"`
})
username string
err error
)
if err = c.Bind(v); err != nil {
return
}
if username, err = getUsername(c); err != nil {
return
}
c.JSON(svc.TransferMachine(c, v.MachineID, username, v.Receiver))
}

View File

@@ -0,0 +1,29 @@
package http
import (
"go-common/app/admin/ep/merlin/model"
bm "go-common/library/net/http/blademaster"
"go-common/library/net/http/blademaster/binding"
)
func genMachinesV2(c *bm.Context) {
var (
gmr = &model.GenMachinesRequest{}
err error
username string
)
if username, err = getUsername(c); err != nil {
return
}
if err = c.BindWith(gmr, binding.JSON); err != nil {
return
}
if err = gmr.Verify(); err != nil {
c.JSON(nil, err)
return
}
c.JSON(nil, svc.GenMachinesV2(c, gmr, username))
}

View File

@@ -0,0 +1,32 @@
package http
import (
"go-common/app/admin/ep/merlin/model"
bm "go-common/library/net/http/blademaster"
"go-common/library/net/http/blademaster/binding"
)
func updateNodes(c *bm.Context) {
var (
umnr = &model.UpdateMachineNodeRequest{}
err error
)
if err = c.BindWith(umnr, binding.JSON); err != nil {
return
}
if err = umnr.VerifyNodes(); err != nil {
c.JSON(nil, err)
return
}
c.JSON(nil, svc.UpdateMachineNode(c, umnr))
}
func queryNodes(c *bm.Context) {
v := new(struct {
MachineID int64 `form:"machine_id"`
})
if err := c.Bind(v); err != nil {
return
}
c.JSON(svc.QueryMachineNodes(v.MachineID))
}

View File

@@ -0,0 +1,50 @@
package http
import (
"net/http"
"go-common/app/admin/ep/merlin/model"
bm "go-common/library/net/http/blademaster"
)
func userTree(c *bm.Context) {
session, err := c.Request.Cookie(_sessIDKey)
if err != nil {
return
}
c.JSON(svc.UserTreeAsOption(c, session.Value))
}
func userTreeContainer(c *bm.Context) {
var (
tnr = &model.TreeNode{}
err error
session *http.Cookie
)
if err = c.Bind(tnr); err != nil {
return
}
if err = tnr.VerifyFieldValue(); err != nil {
return
}
if session, err = c.Request.Cookie(_sessIDKey); err != nil {
return
}
c.JSON(nil, svc.VerifyTreeContainerNode(c, session.Value, tnr))
}
func treeAuditors(c *bm.Context) {
v := new(struct {
FirstNode string `form:"first_node"`
})
if err := c.Bind(v); err != nil {
return
}
session, err := c.Request.Cookie(_sessIDKey)
if err != nil {
return
}
c.JSON(svc.TreeRoleAsAuditor(c, session.Value, v.FirstNode))
}

View File

@@ -0,0 +1,34 @@
package http
import (
"go-common/library/ecode"
bm "go-common/library/net/http/blademaster"
)
const (
_sessUnKey = "username"
)
func queryUserInfo(c *bm.Context) {
var (
username string
err error
)
if username, err = getUsername(c); err != nil {
return
}
c.JSON(svc.QueryUserInfo(c, username))
}
func getUsername(c *bm.Context) (username string, err error) {
user, exist := c.Get(_sessUnKey)
if !exist {
err = ecode.AccessKeyErr
c.JSON(nil, err)
return
}
username = user.(string)
return
}

View File

@@ -0,0 +1,43 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = [
"bilihub.go",
"constants.go",
"dashboard.go",
"device.go",
"dto.go",
"merlin.go",
"paas.go",
"tree.go",
"wechat.go",
],
importpath = "go-common/app/admin/ep/merlin/model",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//library/ecode:go_default_library",
"//library/log:go_default_library",
"//vendor/github.com/jinzhu/gorm: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"],
)

View File

@@ -0,0 +1,66 @@
package model
// SearchHubResponse Search Hub Response.
type SearchHubResponse struct {
Repository []*HubRepo `json:"repository"`
}
// HubRepo HubRepo.
type HubRepo struct {
ProjectID int `json:"project_id"`
ProjectName string `json:"project_name"`
ProjectPublic bool `json:"project_public"`
RepositoryName string `json:"repository_name"`
TagsCount int `json:"tags_count"`
}
// HubProject HubProject.
type HubProject struct {
ProjectID int `json:"project_id"`
Name string `json:"name"`
RepoCount int `json:"repo_count"`
}
// GetHubProjectDetailResponse GetHubProjectDetailResponse.
type GetHubProjectDetailResponse struct {
ProjectID int `json:"project_id"`
ProjectName string `json:"name"`
RepoCount int `json:"repo_count"`
}
// PaginateProjectRepoRecord PaginateProjectRepoRecord.
type PaginateProjectRepoRecord struct {
Total int `json:"total"`
PageNum int `json:"page_num"`
PageSize int `json:"page_size"`
ProjectRepository []*ProjectRepository `json:"project_repositories"`
}
// ProjectRepositoryRequest ProjectRepositoryRequest.
type ProjectRepositoryRequest struct {
ProjectRepository []*ProjectRepository
}
// ProjectRepository ProjectRepository.
type ProjectRepository struct {
RepositoryID int `json:"id"`
RepositoryName string `json:"name"`
TagCount int `json:"tags_count"`
CreateTime string `json:"creation_time"`
UpdateTime string `json:"update_time"`
}
// RepositoryTagResponse Repository Tag Response.
type RepositoryTagResponse struct {
Digest string `json:"digest"`
Name string `json:"name"`
OS string `json:"os"`
Size int64 `json:"size"`
Created string `json:"created"`
}
// RepositoryTag Repository Tag.
type RepositoryTag struct {
RepositoryTagResponse
ImageFullName string `json:"image_full_name"`
}

View File

@@ -0,0 +1,210 @@
package model
//merlin machine status.
const (
// ImmediatelyFailedMachineInMerlin Paas immediately returns failed, when you create some new machines.
ImmediatelyFailedMachineInMerlin = -200
// InitializedFailedMachineInMerlin Scheduled detected that paas failed to execute Initialized task.
InitializedFailedMachineInMerlin = -201
// PodScheduledFailedMachineInMerlin Scheduled detected that paas failed to execute PodScheduled task.
PodScheduledFailedMachineInMerlin = -202
// ReadyFailedMachineInMerlin Scheduled detected that paas failed to execute Ready task.
ReadyFailedMachineInMerlin = -203
// ReadyFailedMachineInMerlin Scheduled detected that paas failed to sync the node of tree service.
SynTreeFailedMachineInMerlin = -204
// CreateTagFailedMachineInMerlin detected that merlin failed to create tag.
CreateTagFailedMachineInMerlin = -205
// RemovedMachineInMerlin the user removed the machine.
RemovedMachineInMerlin = -100
// CreatingMachineInMerlin Paas is creating the machine now.
CreatingMachineInMerlin = 0
// InitializeMachineInMerlin Paas is executing Initialize task.
InitializeMachineInMerlin = 1
// InitializeMachineInMerlin Paas is executing PodScheduled task.
PodScheduledMachineInMerlin = 2
// InitializeMachineInMerlin Paas is executing Ready task.
ReadyMachineInMerlin = 3
// InitializeMachineInMerlin Paas is syncing the node of tree service.
SynTreeMachineInMerlin = 4
// BootMachineInMerlin The machine is turned on.
BootMachineInMerlin = 100
// ShutdownMachineInMerlin The machine is off state.
ShutdownMachineInMerlin = 200
)
// paas return response status.
const (
// CreateFailedMachineInPaas Paas created the machine failed
CreateFailedMachineInPaas = 0
// CreatingMachineInPass Paas is creating the machine now
CreatingMachineInPass = 1
// SuccessDeletePaasMachines success deleted paas machine
SuccessDeletePaasMachines = 1
)
// pagination.
const (
DefaultPageSize = 5
DefaultPageNum = 1
)
// snapshot status
const (
SnapshotInit = "快照初始化"
SnapshotDoing = "快照进行中"
SnapshotSuccess = "快照已完成"
SnapShotFailed = "快照失败"
)
// machine log.
const (
GenForMachineLog = "创建"
DeleteForMachineLog = "删除"
TransferForMachineLog = "转移"
OperationSuccessForMachineLog = "成功"
OperationFailedForMachineLog = "失败"
MBStartLog = "移动设备启动"
MBShutDownLog = "移动设备关闭"
MBBindLog = "移动设备绑定"
MBReleaseLog = "移动设备释放"
MBLendOutLog = "移动设备借出"
MBReturnLog = "移动设备归还"
)
// mobile machine usage.
const (
MBInUse = 1
MBFree = 2
MBNoConnect = 3
)
// mobile machine usage.
const (
MBOnline = 1 //在线
MBOffline = -1 //离线
MBHostDel = -2 //删除
)
// is Simulator or RealMachine.
const (
MBSimulator = 1 //虚拟机
MBReal = 0 //真机
)
// is real machine on site or not.
const (
MBOnSite = 0 //归还
MBLendOut = 1 //借出
)
// machine suffix.
const (
MachinePodNameSuffix = "-0"
)
// delay log.
const (
DelayMachineEndTime = "手动延期"
CancelDelayMachineEndTime = "取消延期"
AuditDelayMachineEndTime = "审批延期"
)
// delay status.
const (
DelayStatusInit = 0 //延期状态初始化
DelayStatusAuto = 1 //可自动延期
DelayStatusApply = 2 //可申请延期
DelayStatusDisable = 3 //不可申请延期
)
// apply status.
const (
ApplyDelayInit = "申请延期中"
ApplyDelayCancel = "申请延期取消"
ApplyDelayApprove = "申请延期通过"
ApplyDelayDecline = "申请延期驳回"
)
// task.
const (
DeleteMachine = "DeleteMachine" // 删除机器
)
// task status.
const (
TaskInit = 0 //未开始
TaskDone = 1 //已执行成功
TaskFailed = 2 //已执行失败
TaskDeleted = -1 //任务删除
)
// machine expired status.
const (
MailTypeMachineWillExpired = 1 //机器将要过期
MailTypeMachineDeleted = 2 //机器已被删除
MailTypeMachineTransfer = 5 //机器转移
MailTypeTaskDeleteMachineFailed = 11 //删除机器任务失败
MailTypeApplyDelayMachineEndTime = 3 //申请延长机器过期时间
MailTypeAuditDelayMachineEndTime = 4 //审核延长机器过期时间
)
// image operate type.
const (
ImageNoSnapshot = 0
ImagePullAndPush = 1
ImagePull = 2
ImageTag = 3
ImagePush = 4
ImageMachine2Image = 5
)
// image operate err
const (
ImageSuccess = 0
ImageInit = -1
ImagePullErr = -2
ImageReTagErr = -3
ImagePushErr = -4
)
// time format.
const (
TimeFormat = "2006-01-02 15:04:05"
)
// bool str.
const (
False = "False"
True = "True"
)
// tree role admin.
const (
TreeRoleAdmin = 1
)
// image status.
const (
AliveImageStatus = 1
DeletedImageStatus = 2
)
// other
const (
Success = "success"
)
// machine ratio
const (
CPURatio = 1000
MemoryRatio = 1024
)

View File

@@ -0,0 +1,86 @@
package model
import "time"
// MachineLifeCycle Machine Life Cycle.
type MachineLifeCycle struct {
Duration string `json:"duration_day"`
Count int `json:"count"`
}
// MachineCountGroupByBusiness Machine Count Group By App.
type MachineCountGroupByBusiness struct {
BusinessUnit string
Count int
}
// MachineCountGroupResponse Machine Count Group Response.
type MachineCountGroupResponse struct {
BusinessUnits []string `json:"departmentList"`
Items []*MachineCountGroupItem `json:"items"`
}
// MachineCountGroupItem Machine Count Group Item.
type MachineCountGroupItem struct {
Type string `json:"type"`
Data []int `json:"data"`
}
// MachineCreatedAndEndTime Machine Created And End Time.
type MachineCreatedAndEndTime struct {
ID int64 `json:"id"`
MachineName string `json:"machine_name"`
App string `json:"app"`
Username string `json:"username"`
CreateTime string `json:"created_time"`
EndTime string `json:"end_time"`
}
// MachineUsage Machine Usage.
type MachineUsage struct {
ID int64 `json:"id"`
MachineName string `json:"machine_name"`
App string `json:"app"`
Username string `json:"username"`
CPURequest int `json:"cpu_request"`
MemoryRequest int `json:"memory_request"`
}
// MobileMachineUserUsageCount Mobile Machine user Usage Count.
type MobileMachineUserUsageCount struct {
Username string `json:"username"`
Count int `json:"count"`
}
// MobileMachineUsageCount Mobile Machine Usage Count.
type MobileMachineUsageCount struct {
MobileMachineID string `json:"mobile_machine_id"`
MobileMachineName string `json:"mobile_machine_name"`
Count int `json:"count"`
}
// MobileMachineTypeCount Mobile Machine Type Count.
type MobileMachineTypeCount struct {
ModeName string `json:"mode_name"`
Count int `json:"count"`
}
// MobileMachineUsageTime Mobile Machine Usage Time.
type MobileMachineUsageTime struct {
//MobileMachineID int64 `json:"mobile_machine_id"`
//MobileMachineName string `json:"mobile_machine_name"`
//ModeName string `json:"mode_name"`
Username string `json:"username"`
StartTime time.Time `json:"start_time"`
EndTime time.Time `json:"end_time"`
Duration float64 `json:"duration_minutes"`
}
// MobileMachineUsageTimeResponse Mobile Machine Usage Time Response.
type MobileMachineUsageTimeResponse struct {
MobileMachineID int64 `json:"mobile_machine_id"`
MobileMachineName string `json:"mobile_machine_name"`
ModeName string `json:"mode_name"`
TotalDuration float64 `json:"total_duration_minutes"`
MobileMachinesUsageTime []*MobileMachineUsageTime `json:"machine_usage_record"`
}

View File

@@ -0,0 +1,75 @@
package model
// DeviceListResponse Device List Response.
type DeviceListResponse struct {
Status *DeviceResponseStatus `json:"status"`
Data *DeviceListData `json:"data"`
}
// DeviceListDetailResponse Device List Detail Response.
type DeviceListDetailResponse struct {
Status *DeviceResponseStatus `json:"status"`
Data *DeviceListDetailData `json:"data"`
}
// DeviceBootResponse Device Boot Response.
type DeviceBootResponse struct {
Status *DeviceResponseStatus `json:"status"`
Data *DeviceBootData `json:"data"`
}
// DeviceShutDownResponse Device Shut Down Response.
type DeviceShutDownResponse struct {
Status *DeviceResponseStatus `json:"status"`
}
// DeviceBootData Device Boot Data.
type DeviceBootData struct {
WSRUL string `json:"wsurl"`
UploadURL string `json:"uploadURL"`
}
// DeviceListDetailData Device List Detail Data.
type DeviceListDetailData struct {
Devices *Device `json:"device"`
}
// DeviceListData Device List Data.
type DeviceListData struct {
Devices []*Device `json:"devices"`
}
// DeviceResponseStatus Device Response Status.
type DeviceResponseStatus struct {
Code int `json:"code"`
Status string `json:"status"`
}
// Device Device.
type Device struct {
Serial string `json:"serial"`
Name string `json:"name"`
State string `json:"state"`
Mode string `json:"mode"`
Version string `json:"version"`
CPU string `json:"cpu"`
IsSimulator bool `json:"isSimulator"`
}
// DeviceBootRequest Device Boot Request.
type DeviceBootRequest struct {
Serial string `json:"serial"`
}
// MobileDeviceCategoryResponse MobileDeviceCategory Response.
type MobileDeviceCategoryResponse struct {
Name string `json:"name"`
Label string `json:"label"`
Values []interface{} `json:"values"`
}
// MobileMachineResponse MobileMachine Response.
type MobileMachineResponse struct {
*MobileMachine
ImageSrc string `json:"image_src"`
}

View File

@@ -0,0 +1,333 @@
package model
import (
"time"
"github.com/jinzhu/gorm"
)
// Machine Machine.
type Machine struct {
ID int64 `json:"id" gorm:"column:id"`
Name string `json:"name" gorm:"column:name"`
PodName string `json:"pod_name,omitempty" gorm:"column:pod_name"`
Status int `json:"status" gorm:"column:status"`
Username string `json:"username" gorm:"column:username"`
BusinessUnit string `json:"business_unit,omitempty" gorm:"column:business_unit"`
Project string `json:"project,omitempty" gorm:"column:project"`
App string `json:"app,omitempty" gorm:"column:app"`
ClusterID int64 `json:"cluster_id,omitempty" gorm:"column:cluster_id"`
NetworkID int64 `json:"network_id,omitempty" gorm:"column:network_id"`
Ctime time.Time `json:"ctime" gorm:"column:ctime;default:current_timestamp"`
Utime time.Time `json:"utime" gorm:"column:mtime;default:current_timestamp on update current_timestamp"`
UpdateBy string `json:"update_by" gorm:"column:update_by"`
EndTime time.Time `json:"end_time" gorm:"column:end_time"`
Comment string `json:"comment" gorm:"column:comment"`
DelayStatus int `json:"delay_status" gorm:"column:delay_status"`
}
// AfterCreate After Create.
func (m *Machine) AfterCreate(db *gorm.DB) (err error) {
if err = db.Model(m).Where("name = ?", m.Name).Find(&m).Error; err != nil {
return
}
m.EndTime = m.Ctime.AddDate(0, 3, 0)
if err = db.Model(&Machine{}).Where("id = ?", m.ID).Update("end_time", m.EndTime).Error; err != nil {
return
}
return
}
// AfterCreate After Create.
func (h *HubImageLog) AfterCreate(db *gorm.DB) (err error) {
err = db.Model(h).Where("imagetag = ?", h.ImageTag).Find(&h).Error
return
}
// IsFailed Is Failed.
func (m *Machine) IsFailed() bool {
return m.Status >= ImmediatelyFailedMachineInMerlin && m.Status < RemovedMachineInMerlin
}
// IsDeleted Is Deleted.
func (m *Machine) IsDeleted() bool {
return m.Status >= RemovedMachineInMerlin && m.Status < CreatingMachineInMerlin
}
// IsCreating Is Creating.
func (m *Machine) IsCreating() bool {
return m.Status >= CreatingMachineInMerlin && m.Status < BootMachineInMerlin
}
// IsBoot Is Boot.
func (m *Machine) IsBoot() bool {
return m.Status >= BootMachineInMerlin && m.Status < ShutdownMachineInMerlin
}
// IsShutdown Is Shutdown.
func (m *Machine) IsShutdown() bool {
return m.Status >= ShutdownMachineInMerlin && m.Status < 300
}
// ToTreeNode return Tree node.
func (m *Machine) ToTreeNode() *TreeNode {
return &TreeNode{
BusinessUnit: m.BusinessUnit,
Project: m.Project,
App: m.App,
}
}
// ToMachineLog generate a machine log struct.
func (m *Machine) ToMachineLog() (ml *MachineLog) {
ml = &MachineLog{
OperateType: GenForMachineLog,
Username: m.Username,
MachineID: m.ID,
}
if m.Status == CreatingMachineInMerlin {
ml.OperateResult = OperationSuccessForMachineLog
} else if m.Status == ImmediatelyFailedMachineInMerlin {
ml.OperateResult = OperationFailedForMachineLog
}
return
}
// MachineLog Machine Log.
type MachineLog struct {
ID int64 `json:"-" gorm:"column:id"`
Username string `json:"username" gorm:"column:username"`
MachineID int64 `json:"machine_id" gorm:"column:machine_id"`
OperateType string `json:"operate_type" gorm:"column:operation_type"`
OperateResult string `json:"operate_result" gorm:"column:operation_result"`
OperateTime time.Time `json:"operate_time" gorm:"column:ctime;default:current_timestamp"`
UTime time.Time `json:"-" gorm:"column:mtime;default:current_timestamp on update current_timestamp"`
}
// MobileMachineLog Mobile Machine Log.
type MobileMachineLog struct {
ID int64 `json:"-" gorm:"column:id"`
Username string `json:"username" gorm:"column:username"`
MachineID int64 `json:"machine_id" gorm:"column:machine_id"`
OperateType string `json:"operate_type" gorm:"column:operation_type"`
OperateResult string `json:"operate_result" gorm:"column:operation_result"`
OperateTime time.Time `json:"operate_time" gorm:"column:ctime;default:current_timestamp"`
UTime time.Time `json:"-" gorm:"column:mtime;default:current_timestamp on update current_timestamp"`
}
// MobileMachineErrorLog Mobile Machine Error Log.
type MobileMachineErrorLog struct {
ID int64 `json:"id" gorm:"column:id"`
MachineID int64 `json:"machine_id" gorm:"column:machine_id"`
SerialName string `json:"serial" gorm:"column:serial"`
ErrorMessage string `json:"error_message" gorm:"column:error_message"`
ErrorCode int `json:"error_code" gorm:"column:error_code"`
CTime time.Time `json:"create_time" gorm:"column:ctime;default:current_timestamp"`
UTime time.Time `json:"-" gorm:"column:mtime;default:current_timestamp on update current_timestamp"`
}
// Snapshot Snapshot.
type Snapshot struct {
ID int64 `gorm:"column:id"`
Name string `gorm:"column:name"`
MachineID int64 `gorm:"column:machine_id"`
UserID int64 `gorm:"column:user_id"`
Ctime time.Time `json:"ctime" gorm:"column:ctime;default:current_timestamp"`
Utime time.Time `json:"utime" gorm:"column:mtime;default:current_timestamp on update current_timestamp"`
UpdateBy int `gorm:"column:update_by"`
Comment string `gorm:"column:comment"`
}
// SnapshotLog Snapshot Log.
type SnapshotLog struct {
ID int64 `gorm:"column:id"`
UserID int64 `gorm:"column:user_id"`
SnapshotID int64 `gorm:"column:snapshot_id"`
OperateType string `gorm:"column:operation_type"`
OperateResult int `gorm:"column:operation_result"`
OperateTime time.Time `gorm:"column:operation_time;default:current_timestamp"`
}
// Task Task.
type Task struct {
ID int64 `gorm:"column:id"`
TYPE string `gorm:"column:type"`
ExecuteTime time.Time `gorm:"column:execute_time"`
MachineID int64 `gorm:"column:machine_id"`
Status int `gorm:"column:status"`
Ctime time.Time `gorm:"column:ctime;default:current_timestamp"`
UTime time.Time `gorm:"column:mtime;default:current_timestamp on update current_timestamp"`
}
// User User.
type User struct {
ID int64 `json:"id" gorm:"auto_increment;primary_key;column:id"`
Name string `json:"username" gorm:"column:name"`
EMail string `json:"email" gorm:"column:email"`
CTime time.Time `gorm:"column:ctime;default:current_timestamp"`
UTime time.Time `gorm:"column:mtime;default:current_timestamp on update current_timestamp"`
}
// Image Image.
type Image struct {
ID int64 `json:"id" gorm:"auto_increment;primary_key;column:id"`
Name string `json:"name" gorm:"varchar(100);column:name"`
Status int `json:"status" gorm:"not null;column:status"`
OS string `json:"os" gorm:"not null;column:os"`
Version string `json:"version" gorm:"not null;column:version"`
Description string `json:"description" gorm:"column:description"`
CreatedBy string `json:"created_by" gorm:"column:created_by"`
UpdatedBy string `json:"updated_by" gorm:"column:updated_by"`
Ctime time.Time `json:"ctime" gorm:"column:ctime;default:current_timestamp"`
Utime time.Time `json:"utime" gorm:"column:mtime;default:current_timestamp on update current_timestamp"`
}
// MachinePackage MachinePackage.
type MachinePackage struct {
ID int64 `json:"id" gorm:"column:id"`
Name string `json:"name" gorm:"column:name"`
CPUCore int `json:"cpu_request" gorm:"column:cpu_core"`
Memory int `json:"memory_request" gorm:"column:memory"`
StorageCapacity int `json:"disk_request" gorm:"column:storage_capacity"`
CTime time.Time `json:"ctime" gorm:"column:ctime;default:current_timestamp"`
UTime time.Time `json:"utime" gorm:"column:mtime;default:current_timestamp on update current_timestamp"`
}
// MailLog MailLog.
type MailLog struct {
ID int64 `gorm:"column:id"`
ReceiverName string `gorm:"column:receiver_name"`
MailType int `gorm:"column:mail_type"`
SendHead string `gorm:"column:send_head"`
SendContext string `gorm:"column:send_context"`
SendTime time.Time `gorm:"column:ctime;default:current_timestamp"`
UTime time.Time `gorm:"column:mtime;default:current_timestamp on update current_timestamp"`
}
// ToPaasQueryAndDelMachineRequest To Paas Query And Del Machine Request.
func (m *Machine) ToPaasQueryAndDelMachineRequest() (pqadmr *PaasQueryAndDelMachineRequest) {
pqadmr = &PaasQueryAndDelMachineRequest{}
pqadmr.Name = m.Name
pqadmr.BusinessUnit = m.BusinessUnit
pqadmr.Project = m.Project
pqadmr.App = m.App
pqadmr.ClusterID = m.ClusterID
return
}
// ApplicationRecord ApplicationRecord.
type ApplicationRecord struct {
ID int64 `json:"id" gorm:"column:id"`
Applicant string `json:"applicant" gorm:"column:applicant"`
MachineID int64 `json:"machine_id" gorm:"column:machine_id"`
ApplyEndTime time.Time `json:"apply_end_time" gorm:"column:apply_end_time"`
Status string `json:"status" gorm:"column:status"`
Auditor string `json:"auditor" gorm:"column:auditor"`
CTime time.Time `json:"ctime" gorm:"column:ctime;default:current_timestamp"`
UTime time.Time `json:"utime" gorm:"column:mtime;default:current_timestamp on update current_timestamp"`
}
// MachineNode the node is associated with machine.
type MachineNode struct {
ID int64 `json:"id" gorm:"column:id"`
MachineID int64 `json:"machine_id" gorm:"column:machine_id"`
BusinessUnit string `json:"business_unit" gorm:"column:business_unit"`
Project string `json:"project" gorm:"column:project"`
App string `json:"app" gorm:"column:app"`
TreeID int64 `json:"tree_id,omitempty" gorm:"column:tree_id"`
CTime time.Time `json:"create_time" gorm:"column:ctime;default:current_timestamp"`
}
// HubImageLog Hub Image Log
type HubImageLog struct {
ID int64 `json:"id" gorm:"column:id"`
MachineID int64 `json:"machine_id" gorm:"column:machine_id"`
UserName string `json:"username" gorm:"column:username"`
ImageSrc string `json:"image_src" gorm:"column:imagesrc"`
ImageTag string `json:"image_tag" gorm:"column:imagetag"`
Status int `json:"status" gorm:"column:status"`
OperateType int `json:"operate_type" gorm:"column:operate_type"`
CTime time.Time `json:"create_time" gorm:"column:ctime;default:current_timestamp"`
UTime time.Time `json:"update_time" gorm:"column:mtime;default:current_timestamp on update current_timestamp"`
}
// MobileMachine the node is associated with MobileMachine.
type MobileMachine struct {
ID int64 `json:"id" gorm:"column:id"`
Serial string `json:"serial" gorm:"column:serial"`
Name string `json:"name" gorm:"column:name"`
CPU string `json:"cpu" gorm:"column:cpu"`
Version string `json:"version" gorm:"column:version"`
Mode string `json:"mode" gorm:"column:mode"`
State string `json:"state" gorm:"column:state"`
Host string `json:"host" gorm:"column:host"`
CTime time.Time `json:"create_time" gorm:"column:ctime;default:current_timestamp"`
MTime time.Time `json:"update_time" gorm:"column:mtime;default:current_timestamp"`
LastBindTime time.Time `json:"last_bind_time" gorm:"column:last_bind_time;default:current_timestamp"`
OwnerName string `json:"owner_name" gorm:"column:owner_name"`
Username string `json:"username" gorm:"column:username"`
Type int `json:"type" gorm:"column:type"`
EndTime time.Time `json:"end_time" gorm:"column:end_time"`
Alias string `json:"alias" gorm:"column:alias"`
Comment string `json:"comment" gorm:"column:comment"`
WsURL string `json:"wsurl" gorm:"column:wsurl"`
UploadURL string `json:"upload_url" gorm:"column:upload_url"`
Action int `json:"action" gorm:"column:action"`
IsLendOut int `json:"is_lendout" gorm:"column:is_lendout"`
UUID string `json:"uuid" gorm:"column:uuid"`
}
// MobileImage Mobile Image.
type MobileImage struct {
ID int64 `json:"id" gorm:"column:id"`
Mode string `json:"mode" gorm:"column:mode"`
CTime time.Time `json:"ctime" gorm:"column:ctime;default:current_timestamp"`
MTime time.Time `json:"mtime" gorm:"column:mtime;default:current_timestamp"`
ImageSrc string `json:"image_src" gorm:"column:image_src"`
}
// MobileSyncLog MobileSyncLog.
type MobileSyncLog struct {
ID int64 `json:"id" gorm:"column:id"`
UUID string `json:"uuid" gorm:"column:uuid"`
AddCnt int `json:"add_count" gorm:"column:add_count"`
UpdateCnt int `json:"update_count" gorm:"column:update_count"`
DeleteCnt int `json:"delete_count" gorm:"column:delete_count"`
TotalCnt int `json:"total_count" gorm:"column:total_count"`
Status int `json:"status" gorm:"column:status"`
CTime time.Time `json:"ctime" gorm:"column:ctime;default:current_timestamp"`
MTime time.Time `json:"mtime" gorm:"column:mtime;default:current_timestamp"`
}
// MobileCategory MobileCategory.
type MobileCategory struct {
CPUs []string `json:"cpus"`
Versions []string `json:"versions"`
Modes []string `json:"modes"`
States []string `json:"states"`
Types []int `json:"types"`
Usages []int `json:"usages"`
}
// SnapshotRecord Snapshot Record
type SnapshotRecord struct {
ID int64 `json:"id" gorm:"column:id"`
MachineID int64 `json:"machine_id" gorm:"column:machine_id"`
Username string `json:"username" gorm:"column:username"`
ImageName string `json:"image_name" gorm:"column:image_name"`
Status string `json:"status" gorm:"column:status"`
Ctime time.Time `json:"ctime" gorm:"column:ctime;default:current_timestamp"`
MTime time.Time `json:"mtime" gorm:"column:mtime;default:current_timestamp on update current_timestamp"`
}
// HubImageConf Hub Image Conf
type HubImageConf struct {
ID int64 `json:"id" gorm:"column:id"`
ImageName string `json:"image_name" gorm:"column:image_name"`
UpdateBy string `json:"update_by" gorm:"column:update_by"`
Command string `json:"command" gorm:"column:command"`
Envs string `json:"environments" gorm:"column:environments"`
Hosts string `json:"hosts" gorm:"column:hosts"`
CTime time.Time `json:"create_time" gorm:"column:ctime;default:current_timestamp"`
UTime time.Time `json:"update_time" gorm:"column:mtime;default:current_timestamp on update current_timestamp"`
}

View File

@@ -0,0 +1,448 @@
package model
import (
"context"
"strconv"
"time"
"go-common/library/ecode"
)
// Env Env.
type Env struct {
ClusterID int64 `json:"cluster_id"`
NetworkID int64 `json:"network_id"`
}
// Node Node.
type Node struct {
BusinessUnit string `json:"business_unit"`
Project string `json:"project"`
App string `json:"app"`
TreeID int64 `json:"tree_id"`
}
// GenMachinesRequest Gen Machines Request.
type GenMachinesRequest struct {
Env
PaasMachine
PaasMachineSystem
Nodes []*Node `json:"nodes"`
Comment string `json:"comment"`
Amount int `json:"amount"`
}
// Verify verify GenMachinesRequest.
func (g *GenMachinesRequest) Verify() error {
if g.Amount < 1 {
return ecode.MerlinInvalidMachineAmountErr
}
l := len(g.Nodes)
if l < 1 || l > 10 {
return ecode.MerlinInvalidNodeAmountErr
}
return nil
}
// Mutator Mutator.
func (g *GenMachinesRequest) Mutator(m *Machine) {
m.BusinessUnit = g.Nodes[0].BusinessUnit
m.Project = g.Nodes[0].Project
m.App = g.Nodes[0].App
m.ClusterID = g.ClusterID
m.NetworkID = g.NetworkID
m.Comment = g.Comment
}
// ToMachineNode to machine node.
func (g *GenMachinesRequest) ToMachineNode(mID int64) (treeNodes []*MachineNode) {
for _, node := range g.Nodes {
treeNodes = append(treeNodes, &MachineNode{
MachineID: mID,
BusinessUnit: node.BusinessUnit,
Project: node.Project,
App: node.App,
TreeID: node.TreeID,
})
}
return
}
// PaasMachineSystem Paas Machine System.
type PaasMachineSystem struct {
Command string `json:"command"`
Envs []*EnvVariable `json:"envs"`
HostAlias []*Host `json:"host_alias"`
}
// Host Host.
type Host struct {
IP string `json:"ip"`
Hostnames []string `json:"hostnames"`
}
// EnvVariable EnvVariable.
type EnvVariable struct {
Name string `json:"name"`
Value string `json:"value"`
}
// GenMachine GenMachine.
type GenMachine struct {
Machine
IP string `json:"ip"`
ClusterName string `json:"cluster_name"`
NetworkName string `json:"network_name"`
}
// MachineDetail Machine Detail.
type MachineDetail struct {
Machine
PaasMachineDetail
Nodes []*MachineNode `json:"nodes"`
NetworkName string `json:"network_name"`
Name string `json:"name"`
IsSnapShot bool `json:"is_support_snapshot"`
}
// PaginateMachine Paginate Machine.
type PaginateMachine struct {
Total int64 `json:"total"`
PageNum int `json:"page_num"`
PageSize int `json:"page_size"`
Machines []GenMachine `json:"machines"`
}
// PaginateMachineLog Paginate Machine Log.
type PaginateMachineLog struct {
Total int64 `json:"total"`
PageNum int `json:"page_num"`
PageSize int `json:"page_size"`
MachineLogs []*AboundMachineLog `json:"machine_logs"`
}
// PaginateMobileMachineLog Paginate Mobile Machine Log.
type PaginateMobileMachineLog struct {
Total int64 `json:"total"`
PageNum int `json:"page_num"`
PageSize int `json:"page_size"`
MachineLogs []*AboundMobileMachineLog `json:"machine_logs"`
}
// PaginateMobileMachineLendOutLog Paginate Mobile Machine Lend Out Log.
type PaginateMobileMachineLendOutLog struct {
Total int64 `json:"total"`
PageNum int `json:"page_num"`
PageSize int `json:"page_size"`
MachineLendOutRecords []*MachineLendOutRecord `json:"machine_lendout_records"`
}
// MachineLendOutRecord Machine Lend Out Record.
type MachineLendOutRecord struct {
MachineID int64 `json:"machine_id"`
Lender string `json:"lender"`
Status int `json:"status"`
LendTime time.Time `json:"lend_time"`
EnableReturn bool `json:"enable_return"`
}
// PaginateMobileMachineErrorLog Paginate Mobile Machine Error Log.
type PaginateMobileMachineErrorLog struct {
Total int64 `json:"total"`
PageNum int `json:"page_num"`
PageSize int `json:"page_size"`
MachineLogs []*MobileMachineErrorLog `json:"machine_logs"`
}
// PaginateApplicationRecord Paginate Application Record.
type PaginateApplicationRecord struct {
Total int64 `json:"total"`
PageNum int `json:"page_num"`
PageSize int `json:"page_size"`
ApplicationRecords []*ApplicationRecord `json:"application_records"`
}
// ToPaasGenMachineRequest to Paas GenMachine Request.
func (g GenMachinesRequest) ToPaasGenMachineRequest(machineLimitRatio float32) *PaasGenMachineRequest {
var (
pms = make([]PaasMachine, g.Amount)
treeIDs []int64
fn = g.Nodes[0]
)
g.CPULimit = g.CPURequest * CPURatio
g.MemoryLimit = g.MemoryRequest * MemoryRatio
g.CPURequest = g.CPURequest * CPURatio / machineLimitRatio
g.MemoryRequest = g.MemoryRequest * MemoryRatio / machineLimitRatio
for i := 0; i < g.Amount; i++ {
pms[i] = g.PaasMachine
pms[i].Name = g.Name + "-" + strconv.Itoa(i+1)
pms[i].PaasMachineSystem = g.PaasMachineSystem
}
for _, node := range g.Nodes {
treeIDs = append(treeIDs, node.TreeID)
}
return &PaasGenMachineRequest{
BusinessUnit: fn.BusinessUnit,
Project: fn.Project,
App: fn.App,
TreeIDs: treeIDs,
Env: g.Env,
Machines: pms,
}
}
// Pagination Pagination.
type Pagination struct {
PageSize int `form:"page_size" json:"page_size"`
PageNum int `form:"page_num" json:"page_num"`
}
// Verify verify the value of pageNum and pageSize.
func (p *Pagination) Verify() error {
if p.PageNum < 0 {
return ecode.MerlinIllegalPageNumErr
} else if p.PageNum == 0 {
p.PageNum = DefaultPageNum
}
if p.PageSize < 0 {
return ecode.MerlinIllegalPageSizeErr
} else if p.PageSize == 0 {
p.PageSize = DefaultPageSize
}
return nil
}
// QueryMachineRequest Query Machine Request.
type QueryMachineRequest struct {
Pagination
TreeNode
MachineName string `form:"machine_name"`
Username string `form:"username"`
Requester string
}
// QueryMachineLogRequest Query Machine Log Request.
type QueryMachineLogRequest struct {
Pagination
MachineID int64 `form:"machine_id"`
MachineName string `form:"machine_name"`
OperateUser string `form:"operate_user"`
OperateType string `form:"operate_type"`
}
// QueryMobileMachineLogRequest Query Mobile Machine Log Request.
type QueryMobileMachineLogRequest struct {
Pagination
MachineID int64 `form:"machine_id"`
Serial string `form:"serial"`
OperateUser string `form:"operate_user"`
OperateType string `form:"operate_type"`
}
// QueryMobileMachineErrorLogRequest Query Mobile Machine Error Log Request.
type QueryMobileMachineErrorLogRequest struct {
Pagination
MachineID int64 `form:"machine_id"`
}
// AboundMachineLog Abound Machine Log.
type AboundMachineLog struct {
MachineLog
Name string `json:"machine_name"`
}
// AboundMobileMachineLog Abound mobile Machine Log.
type AboundMobileMachineLog struct {
MobileMachineLog
Serial string `json:"serial"`
}
// ApplyEndTimeRequest Apply End Time Request.
type ApplyEndTimeRequest struct {
MachineID int64 `json:"machine_id"`
ApplyEndTime string `json:"apply_end_time"`
Auditor string `json:"auditor"`
}
// AuditEndTimeRequest Audit End Time Request.
type AuditEndTimeRequest struct {
AuditID int64 `json:"audit_id"`
AuditResult bool `json:"audit_result"`
Comment string `json:"comment"`
}
// BeforeDelMachineFunc Before DelMachine Func.
type BeforeDelMachineFunc func(c context.Context, id int64, username string) error
// MachineStatusResponse Machine Status Response.
type MachineStatusResponse struct {
Initialized string `json:"initialized"`
PodScheduled string `json:"pod_scheduled"`
Ready string `json:"ready"`
SynTree string `json:"syn_tree"`
RetryCount int `json:"retry_count"`
Log string `json:"log"`
MachineEvent []string `json:"events"`
}
// CreatingMachineStatus Creating Machine Status.
func (msr *MachineStatusResponse) CreatingMachineStatus() int {
if msr.Initialized == False {
return CreatingMachineInMerlin
}
if msr.PodScheduled == False {
return InitializeMachineInMerlin
}
if msr.Ready == False {
return ReadyMachineInMerlin
}
if msr.SynTree == False {
return SynTreeMachineInMerlin
}
return BootMachineInMerlin
}
// FailedMachineStatus Failed Machine Status.
func (msr *MachineStatusResponse) FailedMachineStatus() int {
if msr.Initialized == False {
return InitializedFailedMachineInMerlin
}
if msr.PodScheduled == False {
return PodScheduledFailedMachineInMerlin
}
if msr.Ready == False {
return ReadyFailedMachineInMerlin
}
if msr.SynTree == False {
return SynTreeFailedMachineInMerlin
}
return 0
}
// InstanceMachineStatusResponse Instance Machine Status Response.
func InstanceMachineStatusResponse(machineStatus int) *MachineStatusResponse {
switch machineStatus {
case ImmediatelyFailedMachineInMerlin:
return &MachineStatusResponse{}
case InitializedFailedMachineInMerlin:
return &MachineStatusResponse{Initialized: False, PodScheduled: False, Ready: False, SynTree: False, RetryCount: 0}
case PodScheduledFailedMachineInMerlin:
return &MachineStatusResponse{Initialized: True, PodScheduled: False, Ready: False, SynTree: False, RetryCount: 0}
case ReadyFailedMachineInMerlin:
return &MachineStatusResponse{Initialized: True, PodScheduled: True, Ready: False, SynTree: False, RetryCount: 0}
case SynTreeFailedMachineInMerlin:
return &MachineStatusResponse{Initialized: True, PodScheduled: True, Ready: True, SynTree: False, RetryCount: 0}
case BootMachineInMerlin:
return &MachineStatusResponse{Initialized: True, PodScheduled: True, Ready: True, SynTree: True, RetryCount: 0}
case ShutdownMachineInMerlin:
return &MachineStatusResponse{Initialized: True, PodScheduled: True, Ready: True, SynTree: True, RetryCount: 0}
}
return nil
}
// TreeNode tree node struct for merlin.
type TreeNode struct {
BusinessUnit string `form:"business_unit"`
Project string `form:"project"`
App string `form:"app"`
}
// VerifyFieldValue verify that all fields is not empty.
func (t TreeNode) VerifyFieldValue() (err error) {
if t.BusinessUnit == "" || t.Project == "" || t.App == "" {
err = ecode.MerlinShouldTreeFullPath
}
return
}
// TreePath join all fields.
func (t *TreeNode) TreePath() string {
return t.BusinessUnit + "." + t.Project + "." + t.App
}
// TreePathWithoutEmptyField join all fields except empty.
func (t *TreeNode) TreePathWithoutEmptyField() (treePath string) {
if t.BusinessUnit == "" {
return
}
treePath = "bilibili." + t.BusinessUnit
if t.Project == "" {
return
}
treePath = treePath + "." + t.Project
if t.App == "" {
return
}
treePath = treePath + "." + t.App
return
}
// UpdateMachineNodeRequest request struct for updating machine node.
type UpdateMachineNodeRequest struct {
MachineID int64 `json:"machine_id"`
Nodes []*MachineNode `json:"nodes"`
}
// VerifyNodes verify the Nodes field of UpdateMachineNodeRequest
func (u *UpdateMachineNodeRequest) VerifyNodes() error {
l := len(u.Nodes)
if l < 1 || l > 10 {
return ecode.MerlinInvalidNodeAmountErr
}
return nil
}
// ToMachineNodes convert to machine nodes with injecting machine id.
func (u UpdateMachineNodeRequest) ToMachineNodes() []*MachineNode {
for _, n := range u.Nodes {
n.MachineID = u.MachineID
}
return u.Nodes
}
// QueryMobileDeviceRequest Query Device Farm Request.
type QueryMobileDeviceRequest struct {
Pagination
MobileID int64 `json:"mobile_id"`
Serial string `json:"serial"`
Name string `json:"name"`
Username string `json:"username"`
OwnerName string `json:"owner_name"`
CPU string `json:"cpu"`
Version string `json:"version"`
Mode string `json:"mode"`
Type int `json:"type"`
State string `json:"state"`
Usage int `json:"usage"`
Online bool `json:"online"`
}
// PaginateMobileMachines Paginate Device.
type PaginateMobileMachines struct {
Total int64 `json:"total"`
PageNum int `json:"page_num"`
PageSize int `json:"page_size"`
MobileMachines []*MobileMachineResponse `json:"mobile_devices"`
}
// QueryMachine2ImageLogRequest Query Machine to Image Log Request.
type QueryMachine2ImageLogRequest struct {
Pagination
MachineID int64 `form:"machine_id"`
}
// PaginateHubImageLog Paginate Hub Image Log.
type PaginateHubImageLog struct {
Total int64 `json:"total"`
PageNum int `json:"page_num"`
PageSize int `json:"page_size"`
HubImageLogs []*HubImageLog `json:"hub_image_logs"`
}
// ImageConfiguration Image Configuration.
type ImageConfiguration struct {
ImageFullName string `json:"image_full_name"`
PaasMachineSystem
}

View File

@@ -0,0 +1,325 @@
package model
import (
"encoding/json"
"net/http"
"go-common/library/ecode"
"go-common/library/log"
)
// PaasConf conf of paas.
type PaasConf struct {
Host string
Token string
MachineTimeout string
MachineLimitRatio float32
}
// PaasMachine machine in paas.
type PaasMachine struct {
PaasMachineSystem
Name string `json:"name"`
Image string `json:"image"`
CPURequest float32 `json:"cpu_request"`
MemoryRequest float32 `json:"memory_request"`
CPULimit float32 `json:"cpu_limit"`
MemoryLimit float32 `json:"memory_limit"`
DiskRequest int `json:"disk_request"`
VolumnMount string `json:"volumn_mount"`
Snapshot bool `json:"snapshot"`
ForcePullImage bool `json:"force_pull_image"`
}
// ToMachine convert PaasMachine to Machine.
func (pm *PaasMachine) ToMachine(u string, gmr *GenMachinesRequest) (m *Machine) {
m = &Machine{
Username: u,
Name: pm.Name,
PodName: pm.Name + MachinePodNameSuffix,
Status: CreatingMachineInMerlin,
}
gmr.Mutator(m)
return
}
// PaasGenMachineRequest create machine request in paas.
type PaasGenMachineRequest struct {
Env
BusinessUnit string `json:"business_unit"`
Project string `json:"project"`
App string `json:"app"`
TreeIDs []int64 `json:"tree_id"`
Machines []PaasMachine `json:"machines"`
}
// ExcludeDataResponse no data field response.
type ExcludeDataResponse struct {
Status int `json:"status"`
Message string `json:"message"`
}
// CheckStatus check status.
func (e *ExcludeDataResponse) CheckStatus() (err error) {
if e.Status >= http.StatusMultipleChoices || e.Status < http.StatusOK {
log.Error("The status(%d) of paas may represent a request error(%s)", e.Status, e.Message)
err = ecode.MerlinPaasRequestErr
}
return
}
// Instance instance.
type Instance struct {
InstanceName string `json:"instance_name"`
}
// CreateInstance create instance.
type CreateInstance struct {
Instance
InstanceCreateStatus int `json:"instance_create_status"`
}
// ReleaseInstance release instance.
type ReleaseInstance struct {
Instance
InstanceReleaseStatus int `json:"instance_release_status"`
}
// PaasGenMachineResponse create machine response in paas.
type PaasGenMachineResponse struct {
ExcludeDataResponse
Data []*CreateInstance `json:"data"`
}
// PaasDelMachineResponse delete machine response in paas.
type PaasDelMachineResponse struct {
ExcludeDataResponse
Data ReleaseInstance `json:"data"`
}
// PaasSnapshotMachineResponse Paas Snapshot Machine Response.
type PaasSnapshotMachineResponse struct {
ExcludeDataResponse
Data string `json:"data"`
}
// DetailCondition detail condition.
type DetailCondition struct {
Initialized string `json:"Initialized"`
PodScheduled string `json:"PodScheduled"`
Ready string `json:"Ready"`
}
// MachineStatus machine status.
type MachineStatus struct {
Condition string `json:"condition"`
Message string `json:"message"`
DetailCondition DetailCondition `json:"detail_conditions"`
InstanceIP string `json:"instance_ip"`
RestartCount int `json:"restart_count"`
Log string `json:"log"`
MachineEvent MachineEvent `json:"events"`
}
// MachineEvent MachineEvent.
type MachineEvent struct {
MachinesItems []*MachinesItem `json:"Items"`
}
// MachinesItem MachinesItem.
type MachinesItem struct {
Kind string `json:"Kind"`
Name string `json:"Name"`
Namespace string `json:"Namespace"`
Reason string `json:"Reason"`
Message string `json:"Message"`
Count int `json:"Count"`
LastTime string `json:"LastTime"`
}
// ToMachineStatusResponse convert MachineStatus to MachineStatusResponse.
func (ms *MachineStatus) ToMachineStatusResponse() (msr *MachineStatusResponse) {
var events []string
for _, item := range ms.MachineEvent.MachinesItems {
mJSON, _ := json.Marshal(item)
events = append(events, string(mJSON))
}
return &MachineStatusResponse{
Initialized: ms.DetailCondition.Initialized,
PodScheduled: ms.DetailCondition.PodScheduled,
Ready: ms.DetailCondition.Ready,
Log: ms.Log,
MachineEvent: events,
}
}
// PaasQueryMachineStatusResponse query machine status response.
type PaasQueryMachineStatusResponse struct {
ExcludeDataResponse
Data MachineStatus `json:"data"`
}
// PaasQueryClustersResponse query cluster response.
type PaasQueryClustersResponse struct {
ExcludeDataResponse
Data Clusters `json:"data"`
}
// PaasQueryClusterResponse query cluster response.
type PaasQueryClusterResponse struct {
ExcludeDataResponse
Data *Cluster `json:"data"`
}
// PaasMachineDetail machine detail.
type PaasMachineDetail struct {
Condition string `json:"condition"`
Name string `json:"name"`
Image string `json:"image"`
CPURequest float32 `json:"cpu_request"`
MemoryRequest float32 `json:"memory_request"`
CPULimit float32 `json:"cpu_limit"`
MemoryLimit float32 `json:"memory_limit"`
DiskRequest int `json:"disk_request"`
VolumnMount string `json:"volumn_mount"`
ClusterName string `json:"cluster_name"`
Env string `json:"env"`
IP string `json:"ip"`
PaasMachineSystem
}
// ConvertUnits convert units of cpu and memory.
func (p PaasMachineDetail) ConvertUnits() PaasMachineDetail {
p.CPURequest = p.CPURequest / CPURatio
p.MemoryRequest = p.MemoryRequest / MemoryRatio
p.CPULimit = p.CPULimit / CPURatio
p.MemoryLimit = p.MemoryLimit / MemoryRatio
return p
}
// PaasQueryMachineResponse query machine response.
type PaasQueryMachineResponse struct {
ExcludeDataResponse
Data PaasMachineDetail `json:"data"`
}
// Clusters clusters.
type Clusters struct {
Items []*Cluster `json:"items"`
}
// Network network.
type Network struct {
ID int64 `json:"id"`
Name string `json:"name"`
Subnet string `json:"subnet"`
Capacity float64 `json:"capacity"`
}
// Resource resource.
type Resource struct {
CPUUsage float64 `json:"cpu_usage"` //集群总体CPU使用率
MemUsage float64 `json:"mem_usage"` //集群总体内存使用率
PodTotal int `json:"pod_total"` //集群实例总数
PodCapacity int `json:"pod_capacity"` //集群实例容量
NodesNum int `json:"nodes_num"` //集群节点数量
}
// Cluster cluster.
type Cluster struct {
ID int64 `json:"id"`
Name string `json:"name"`
IsSupportSnapShot bool `json:"is_support_snapshot"`
Desc string `json:"desc"`
IDc string `json:"idc"`
Networks []Network `json:"networks"`
Resources Resource `json:"resources"`
}
// Verify verify cluster
func (c *Cluster) Verify() error {
if c.Name == "" || len(c.Networks) < 1 || c.Networks[0].Name == "" {
return ecode.MerlinInvalidClusterErr
}
return nil
}
// ToMachine convert CreateInstance to Machine.
func (i *CreateInstance) ToMachine(u string, gmr *GenMachinesRequest) (m *Machine) {
var status int
switch i.InstanceCreateStatus {
case CreateFailedMachineInPaas:
status = ImmediatelyFailedMachineInMerlin
case CreatingMachineInPass:
status = CreatingMachineInMerlin
}
m = &Machine{
Username: u,
Name: i.InstanceName,
PodName: i.InstanceName + MachinePodNameSuffix,
Status: status,
}
gmr.Mutator(m)
return
}
// PaasQueryAndDelMachineRequest query and del machines request.
type PaasQueryAndDelMachineRequest struct {
BusinessUnit string `json:"business_unit"`
Project string `json:"project"`
App string `json:"app"`
ClusterID int64 `json:"cluster_id"`
Name string `json:"name"`
}
// PaasAuthRequest auth request.
type PaasAuthRequest struct {
APIToken string `json:"api_token"`
PlatformID string `json:"platform_id"`
}
// PaasAuthResponse auth response.
type PaasAuthResponse struct {
ExcludeDataResponse
Data PaasAuthInfo `json:"data"`
}
// PaasAuthInfo auth information.
type PaasAuthInfo struct {
Token string `json:"token"`
PlatformID string `json:"platform_id"`
Username string `json:"user_name"`
Secret string `json:"secret"`
Expired int64 `json:"expired"`
Admin bool `json:"admin"`
}
// PaasUpdateMachineNodeRequest update machine request.
type PaasUpdateMachineNodeRequest struct {
PaasQueryAndDelMachineRequest
TreeID []int64 `json:"tree_id"`
}
// NewPaasUpdateMachineNodeRequest new PaasUpdateMachineNodeRequest
func NewPaasUpdateMachineNodeRequest(p *PaasQueryAndDelMachineRequest, ns []*MachineNode) *PaasUpdateMachineNodeRequest {
var treeIDs []int64
for _, n := range ns {
treeIDs = append(treeIDs, n.TreeID)
}
return &PaasUpdateMachineNodeRequest{
TreeID: treeIDs,
PaasQueryAndDelMachineRequest: *p,
}
}
// PaasUpdateMachineNodeResponse update machine response.
type PaasUpdateMachineNodeResponse struct {
ExcludeDataResponse
Data string `json:"data"`
}

View File

@@ -0,0 +1,116 @@
package model
import (
"net/url"
"reflect"
)
const _query = "query"
// TreeResponse Tree Response.
type TreeResponse struct {
Code int `json:"code"`
Data UserTree `json:"data"`
}
// UserTree User Tree.
type UserTree struct {
Bilibili map[string]interface{} `json:"bilibili"`
}
// TreeMachinesResponse Tree Machines Response.
type TreeMachinesResponse struct {
Code int `json:"code"`
Data []string `json:"data"`
}
// TreeSonResponse Tree Son Response.
type TreeSonResponse struct {
Code int `json:"code"`
Data map[string]interface{} `json:"data"`
}
// TreeRoleResponse Tree Role Response.
type TreeRoleResponse struct {
Code int `json:"code"`
Data []*TreeRole `json:"data"`
}
// TreeInstancesResponse Tree Instance Response.
type TreeInstancesResponse struct {
Code int `json:"code"`
Data map[string]*TreeInstance `json:"data"`
}
// TreeInstance Tree Instance.
type TreeInstance struct {
HostName string `json:"hostname"`
IP string `json:"ip"`
InstanceType string `json:"instance_type"`
InternalIP string `json:"internal_ip"`
ServiceIP string `json:"service_ip"`
ExtendIP string `json:"extend_ip"`
}
// TreeAppInstanceRequest Tree App Instance Request.
type TreeAppInstanceRequest struct {
Paths []string `json:"paths"`
}
// TreeAppInstanceResponse Tree App Instance Response.
type TreeAppInstanceResponse struct {
Code int `json:"code"`
Data map[string][]*TreeAppInstance `json:"data"`
}
// TreeAppInstance Tree App Instance.
type TreeAppInstance struct {
HostName string `json:"hostname"`
}
// TreePlatformTokenRequest Tree Platform Token Request.
type TreePlatformTokenRequest struct {
UserName string `json:"user_name"`
PlatformID string `json:"platform_id"`
}
// TreeRole Tree Role.
type TreeRole struct {
UserName string `json:"user_name"`
Role int `json:"role"`
OldRole int `json:"old_role"`
RdSre bool `json:"rd_sre"`
}
// TreeConf tree conf.
type TreeConf struct {
Host string
Key string
Secret string
}
// TreeInstanceRequest request for hostname.
type TreeInstanceRequest struct {
Path string `query:"path"`
PathFuzzy string `query:"path_fuzzy"`
Hostname string `query:"hostname"`
HostnameFuzzy string `query:"hostname_fuzzy"`
HostnameRegex string `query:"hostname_regex"`
}
// ToQueryURI convert field to uri.
func (tir TreeInstanceRequest) ToQueryURI() string {
var (
params = &url.Values{}
t = reflect.TypeOf(tir)
v = reflect.ValueOf(tir)
fv string
)
for i := 0; i < t.NumField(); i++ {
fv = v.Field(i).Interface().(string)
if fv != "" {
params.Set(t.Field(i).Tag.Get(_query), fv)
}
}
return params.Encode()
}

View File

@@ -0,0 +1,28 @@
package model
// MsgSendReq message send request of wechat
type MsgSendReq struct {
Chatid string `json:"chatid" form:"chatid"`
Msgtype string `json:"msgtype" form:"msgtype"`
Text MsgSendReqText `json:"text" form:"test"`
Safe int `json:"safe" form:"safe"`
}
// MsgSendReqText MegSendReq test
type MsgSendReqText struct {
Content string `json:"content"`
}
// MsgSendRes message send response
type MsgSendRes struct {
Code int `json:"code"`
Message string `json:"message"`
TTL int `json:"ttl"`
Data WechatMegSendResData `json:"data"`
}
// WechatMegSendResData message send response data of wechat
type WechatMegSendResData struct {
Errcode int `json:"errcode"`
Errmsg string `json:"errmsg"`
}

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")
})
}