You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
533 lines
14 KiB
533 lines
14 KiB
package service |
|
|
|
import ( |
|
"bytes" |
|
"context" |
|
"crypto/md5" |
|
"encoding/hex" |
|
"encoding/json" |
|
"strconv" |
|
"strings" |
|
"time" |
|
|
|
"go-common/app/admin/main/config/model" |
|
"go-common/library/database/sql" |
|
"go-common/library/ecode" |
|
"go-common/library/log" |
|
|
|
"github.com/jinzhu/gorm" |
|
"github.com/satori/go.uuid" |
|
) |
|
|
|
var ( |
|
rdsEnvs = []*model.Env{ |
|
{Name: "dev", NikeName: "开发环境"}, |
|
{Name: "fat1", NikeName: "功能环境1"}, |
|
{Name: "uat", NikeName: "集成环境"}, |
|
{Name: "pre", NikeName: "预发环境"}, |
|
{Name: "prod", NikeName: "线上环境"}, |
|
} |
|
opsEnvs = []*model.Env{ |
|
{Name: "dev", NikeName: "开发环境"}, |
|
{Name: "fat1", NikeName: "功能环境1"}, |
|
{Name: "uat", NikeName: "集成环境"}, |
|
} |
|
) |
|
|
|
// CreateApp create App. |
|
func (s *Service) CreateApp(name, env, zone string, treeID int64) error { |
|
bytes := [16]byte(uuid.NewV1()) |
|
token := md5.Sum([]byte(hex.EncodeToString(bytes[:]))) |
|
app := &model.App{Name: name, Env: env, Zone: zone, Token: hex.EncodeToString(token[:]), TreeID: treeID, Status: model.StatusShow} |
|
return s.dao.DB.Create(app).Error |
|
} |
|
|
|
// UpdateToken update token. |
|
func (s *Service) UpdateToken(c context.Context, env, zone string, treeID int64) (err error) { |
|
bytes := [16]byte(uuid.NewV1()) |
|
token := hex.EncodeToString(bytes[:]) |
|
if err = s.dao.DB.Model(&model.App{}).Where("tree_id =? AND env=? AND zone=?", treeID, env, zone).Update("token", token).Error; err != nil { |
|
return |
|
} |
|
err = s.SetToken(c, treeID, env, zone, token) |
|
return |
|
} |
|
|
|
// AppByTree get token by Name. |
|
func (s *Service) AppByTree(treeID int64, env, zone string) (app *model.App, err error) { |
|
app = &model.App{} |
|
row := s.dao.DB.Select("id,token").Where("tree_id=? AND env=? AND zone=?", treeID, env, zone).Model(&model.App{}).Row() |
|
if err = row.Scan(&app.ID, &app.Token); err != nil { |
|
log.Error("AppByTree(%v) err(%v)", treeID, err) |
|
if err == sql.ErrNoRows { |
|
err = ecode.NothingFound |
|
} |
|
} |
|
return |
|
} |
|
|
|
// AppsByTreeZone get token by Name and zone. |
|
func (s *Service) AppsByTreeZone(treeID int64, zone string) (apps []*model.App, err error) { |
|
if err = s.dao.DB.Select("id,env,token").Where("tree_id=? AND zone=?", treeID, zone).Find(&apps).Error; err != nil { |
|
log.Error("AppsByTreezone(%d) error(%v)", treeID, err) |
|
if err == sql.ErrNoRows { |
|
err = ecode.NothingFound |
|
} |
|
} |
|
return |
|
} |
|
|
|
// AppList get token by Name. |
|
func (s *Service) AppList(ctx context.Context, bu, team, name, env, zone string, ps, pn int64, nodes *model.CacheData, status int8) (pager *model.AppPager, err error) { |
|
var ( |
|
like string |
|
apps []*model.App |
|
total int64 |
|
ids []int64 |
|
statusIn []int8 |
|
) |
|
like = "%" |
|
if len(bu) != 0 { |
|
like = bu + ".%" |
|
} |
|
if len(team) != 0 { |
|
like = team + ".%" |
|
} |
|
if len(name) != 0 { |
|
like = "%.%.%" + name + "%" |
|
} |
|
if len(name) != 0 && len(team) != 0 { |
|
like = team + ".%" + name + "%" |
|
} else if len(name) != 0 && len(bu) != 0 { |
|
like = bu + ".%.%" + name + "%" |
|
} |
|
if status > 0 { |
|
statusIn = append(statusIn, status) |
|
} else { |
|
statusIn = []int8{model.StatusShow, model.StatusHidden} |
|
} |
|
for _, node := range nodes.Data { |
|
ids = append(ids, node.ID) |
|
} |
|
if err = s.dao.DB.Where("env=? AND zone=? AND name like ? AND tree_id in (?) And status in (?)", env, zone, like, ids, statusIn). |
|
Offset((pn - 1) * ps).Limit(ps).Find(&apps).Error; err != nil { |
|
log.Error("AppList() find page apps() error(%v)", err) |
|
return |
|
} |
|
if err = s.dao.DB.Model(&model.App{}).Where("env=? AND zone=? AND name like ? AND tree_id in (?) And status in (?)", env, zone, like, ids, statusIn). |
|
Count(&total).Error; err != nil { |
|
log.Error("AppList() count page apps() error(%v)", err) |
|
return |
|
} |
|
pager = &model.AppPager{Total: total, Pn: pn, Ps: ps, Items: apps} |
|
return |
|
} |
|
|
|
// Tree get service tree. |
|
func (s *Service) Tree(ctx context.Context, user string) (data interface{}, err error) { |
|
var ( |
|
parme []byte |
|
msg map[string]interface{} |
|
tmp interface{} |
|
token string |
|
ok bool |
|
) |
|
if parme, err = json.Marshal(map[string]string{"user_name": user, "platform_id": s.c.Tree.Platform}); err != nil { |
|
log.Error("json.Marshal() error(%v)", err) |
|
return |
|
} |
|
if msg, err = s.dao.Token(ctx, string(parme)); err != nil { |
|
return |
|
} |
|
if tmp, ok = msg["token"]; !ok { |
|
err = ecode.NothingFound |
|
return |
|
} |
|
if token, ok = tmp.(string); !ok { |
|
err = ecode.NothingFound |
|
return |
|
} |
|
return s.dao.Tree(ctx, token) |
|
} |
|
|
|
// Node node. |
|
func (s *Service) Node(ctx context.Context, user, node, team, cookie string, nodes *model.CacheData) (res []*model.Node, err error) { |
|
var nNodes *model.CacheData |
|
//bu list. |
|
if node == "" && team == "" { |
|
if nNodes, err = s.SyncTree(ctx, user, cookie); err == nil { |
|
nodes = nNodes |
|
} |
|
tmp := make(map[string]struct{}) |
|
for _, app := range nodes.Data { |
|
idx := strings.Index(app.Path, ".") |
|
bu := string([]byte(app.Path)[:idx]) |
|
if _, ok := tmp[bu]; ok { |
|
continue |
|
} |
|
n := new(model.Node) |
|
n.Name = bu |
|
n.Path = bu |
|
res = append(res, n) |
|
tmp[bu] = struct{}{} |
|
} |
|
return |
|
} |
|
//team list. |
|
if node != "" && team == "" { |
|
tmp := make(map[string]struct{}) |
|
for _, app := range nodes.Data { |
|
s := []byte(app.Path) |
|
sep := []byte(".") |
|
fidx := bytes.Index(s, sep) |
|
lidx := bytes.LastIndex(s, sep) |
|
team = string(s[:lidx]) |
|
if node == string(s[:fidx]) { |
|
if _, ok := tmp[team]; ok { |
|
continue |
|
} |
|
n := new(model.Node) |
|
n.Name = string([]byte(app.Path)[fidx+1 : lidx]) |
|
n.Path = team |
|
tmp[team] = struct{}{} |
|
res = append(res, n) |
|
} |
|
} |
|
return |
|
} |
|
//app list. |
|
if team == "" { |
|
return |
|
} |
|
for _, app := range nodes.Data { |
|
s := []byte(app.Path) |
|
sep := []byte(".") |
|
lidx := bytes.LastIndex(s, sep) |
|
t := string(s[:lidx]) |
|
if team != t { |
|
continue |
|
} |
|
n := new(model.Node) |
|
n.Name = string(s[lidx+1:]) |
|
n.Path = app.Path |
|
n.TreeID = app.ID |
|
res = append(res, n) |
|
} |
|
return |
|
} |
|
|
|
//Envs envs. |
|
func (s *Service) Envs(ctx context.Context, user, appName, zone string, treeID int64, nodes *model.CacheData) (envs []*model.Env, err error) { |
|
var ( |
|
ok bool |
|
node *model.RoleNode |
|
apps []*model.App |
|
) |
|
envs = rdsEnvs |
|
if node, ok = nodes.Data[treeID]; !ok { |
|
return |
|
} |
|
if node.Role == model.Ops { |
|
envs = opsEnvs |
|
} |
|
apps, err = s.AppsByTreeZone(treeID, zone) |
|
for _, env := range envs { |
|
env.Token = "" |
|
for _, app := range apps { |
|
if app.Env == env.Name { |
|
env.Token = app.Token |
|
break |
|
} |
|
} |
|
} |
|
return |
|
} |
|
|
|
//EnvsByTeam envs. |
|
func (s *Service) EnvsByTeam(ctx context.Context, appName, zone string, nodes *model.CacheData) (envs []*model.Env, err error) { |
|
envs = rdsEnvs |
|
return |
|
} |
|
|
|
//SyncTree syncTree. |
|
func (s *Service) SyncTree(ctx context.Context, user string, cookie string) (nodes *model.CacheData, err error) { |
|
var ( |
|
msg map[string]interface{} |
|
tmp interface{} |
|
token string |
|
ok bool |
|
) |
|
|
|
if msg, err = s.dao.Auth(ctx, cookie); err != nil { |
|
return |
|
} |
|
if tmp, ok = msg["token"]; !ok { |
|
err = ecode.NothingFound |
|
return |
|
} |
|
if token, ok = tmp.(string); !ok { |
|
err = ecode.NothingFound |
|
return |
|
} |
|
if nodes, err = s.dao.Role(ctx, user, token); err != nil { |
|
return |
|
} |
|
s.cLock.Lock() |
|
s.cache[user] = nodes |
|
s.cLock.Unlock() |
|
return |
|
} |
|
|
|
//AuthApps authApps. |
|
func (s *Service) AuthApps(ctx context.Context, user, cookie string) (nodes *model.CacheData, err error) { |
|
if len(user) == 0 { |
|
err = ecode.NothingFound |
|
return |
|
} |
|
var ok bool |
|
s.cLock.RLock() |
|
nodes, ok = s.cache[user] |
|
s.cLock.RUnlock() |
|
if !ok || (time.Since(nodes.CTime) > 60*time.Second) { |
|
s.SyncTree(ctx, user, cookie) |
|
s.cLock.RLock() |
|
nodes, ok = s.cache[user] |
|
s.cLock.RUnlock() |
|
if !ok { |
|
err = ecode.NothingFound |
|
} |
|
} |
|
return |
|
} |
|
|
|
//AuthApp authApp. |
|
func (s *Service) AuthApp(ctx context.Context, user, cookie string, treeID int64) (rule int8, err error) { |
|
var ( |
|
ok bool |
|
node *model.RoleNode |
|
nodes *model.CacheData |
|
) |
|
if nodes, err = s.AuthApps(ctx, user, cookie); err != nil { |
|
return |
|
} |
|
if node, ok = nodes.Data[treeID]; !ok { |
|
err = ecode.AccessDenied |
|
return |
|
} |
|
return node.Role, nil |
|
} |
|
|
|
//ConfigGetTreeID ... |
|
func (s *Service) ConfigGetTreeID(configID int64) (TreeID int64, err error) { |
|
conf := new(model.Config) |
|
if err = s.dao.DB.First(&conf, configID).Error; err != nil { |
|
log.Error("ConfigGetTreeID(%v) error(%v)", configID, err) |
|
return |
|
} |
|
TreeID, err = s.AppIDGetTreeID(conf.AppID) |
|
return |
|
} |
|
|
|
//AppIDGetTreeID ... |
|
func (s *Service) AppIDGetTreeID(appID int64) (TreeID int64, err error) { |
|
app := new(model.App) |
|
if err = s.dao.DB.First(&app, appID).Error; err != nil { |
|
log.Error("AppIDGetTreeID(%v) error(%v)", appID, err) |
|
return |
|
} |
|
TreeID = app.TreeID |
|
return |
|
} |
|
|
|
//BuildGetTreeID ... |
|
func (s *Service) BuildGetTreeID(buildID int64) (TreeID int64, err error) { |
|
build := new(model.Build) |
|
if err = s.dao.DB.First(&build, buildID).Error; err != nil { |
|
log.Error("BuildGetTreeID(%v) error(%v)", buildID, err) |
|
return |
|
} |
|
TreeID, err = s.AppIDGetTreeID(build.AppID) |
|
return |
|
} |
|
|
|
//TagGetTreeID ... |
|
func (s *Service) TagGetTreeID(tagID int64) (TreeID int64, err error) { |
|
tag := new(model.Tag) |
|
if err = s.dao.DB.First(&tag, tagID).Error; err != nil { |
|
log.Error("TagGetTreeID(%v) error(%v)", tagID, err) |
|
return |
|
} |
|
TreeID, err = s.AppIDGetTreeID(tag.AppID) |
|
return |
|
} |
|
|
|
//ZoneCopy ... |
|
func (s *Service) ZoneCopy(ctx context.Context, AppName, From, To string, TreeID int64) (err error) { |
|
apps := []*model.App{} |
|
if err = s.dao.DB.Where("name = ? and tree_id = ? and zone = ?", AppName, TreeID, From).Find(&apps).Error; err != nil { |
|
log.Error("ZoneCopy from apps error(%v)", err) |
|
return |
|
} |
|
tx := s.dao.DB.Begin() |
|
for _, v := range apps { |
|
app := &model.App{} |
|
if err = s.dao.DB.Where("name = ? and tree_id = ? and zone = ? and env = ?", AppName, TreeID, To, v.Env).First(app).Error; err != nil { |
|
if err != gorm.ErrRecordNotFound { |
|
log.Error("ZoneCopy to app error(%v)", err) |
|
return |
|
} |
|
//add app |
|
if err = s.CreateApp(AppName, v.Env, To, TreeID); err != nil { |
|
log.Error("ZoneCopy add app error(%v)", err) |
|
return |
|
} |
|
if err = s.dao.DB.Where("name = ? and tree_id = ? and zone = ? and env = ?", AppName, TreeID, To, v.Env).First(app).Error; err != nil { |
|
log.Error("ZoneCopy first app error(%v)", err) |
|
return |
|
} |
|
} |
|
// |
|
configs := []*model.Config{} |
|
if err = tx.Where("app_id = ?", app.ID).Find(&configs).Error; err != nil { |
|
log.Error("ZoneCopy find configs error(%v)", err) |
|
tx.Rollback() |
|
return |
|
} |
|
|
|
builds := []*model.Build{} |
|
if err = tx.Where("app_id = ?", v.ID).Find(&builds).Error; err != nil { |
|
log.Error("ZoneCopy find builds error(%v)", err) |
|
tx.Rollback() |
|
return |
|
} |
|
for _, val := range builds { |
|
tag := &model.Tag{} |
|
if err = tx.First(tag, val.TagID).Error; err != nil { |
|
log.Error("ZoneCopy find tag error(%v)", err) |
|
tx.Rollback() |
|
return |
|
} |
|
configs = []*model.Config{} |
|
in := strings.Split(tag.ConfigIDs, ",") |
|
if err = tx.Where("id in (?)", in).Find(&configs).Error; err != nil { |
|
log.Error("ZoneCopy find build configs error(%v)", err) |
|
tx.Rollback() |
|
return |
|
} |
|
config := &model.Config{} |
|
var configIDS string |
|
for _, vvv := range configs { |
|
config = &model.Config{} |
|
config.Operator = vvv.Operator |
|
config.Name = vvv.Name |
|
config.Mark = vvv.Mark |
|
config.Comment = vvv.Comment |
|
config.State = vvv.State |
|
config.From = 0 //公共文件变私人文件 |
|
if err = s.CreateConf(config, TreeID, v.Env, To, true); err != nil { |
|
log.Error("ZoneCopy config create error(%v)", err) |
|
tx.Rollback() |
|
return |
|
} |
|
if len(configIDS) > 0 { |
|
configIDS += "," |
|
} |
|
configIDS += strconv.FormatInt(config.ID, 10) |
|
} |
|
newTag := &model.Tag{} |
|
newTag.Operator = tag.Operator |
|
newTag.Mark = tag.Mark |
|
newTag.ConfigIDs = configIDS |
|
s.UpdateTag(ctx, TreeID, v.Env, To, val.Name, newTag) |
|
} |
|
} |
|
tx.Commit() |
|
return |
|
} |
|
|
|
// CanalCheckToken ... |
|
func (s *Service) CanalCheckToken(AppName, Env, Zone, Token string) (err error) { |
|
app := &model.App{} |
|
if err = s.dao.DB.Where("name = ? and env = ? and zone = ? and tree_id = ? and token = ?", AppName, Env, Zone, 3766, Token).First(app).Error; err != nil { |
|
log.Error("canalCheckToken error(%v)", err) |
|
} |
|
return |
|
} |
|
|
|
// CasterEnvs ... |
|
func (s *Service) CasterEnvs(zone string, treeID int64) (envs []*model.Env, err error) { |
|
var ( |
|
apps []*model.App |
|
) |
|
envs = rdsEnvs |
|
apps, err = s.AppsByTreeZone(treeID, zone) |
|
for _, env := range envs { |
|
env.Token = "" |
|
for _, app := range apps { |
|
if app.Env == env.Name { |
|
env.Token = app.Token |
|
break |
|
} |
|
} |
|
} |
|
return |
|
} |
|
|
|
// AppRename ... |
|
func (s *Service) AppRename(treeID int64, user, cookie string) (err error) { |
|
var ( |
|
ok bool |
|
node *model.RoleNode |
|
nodes *model.CacheData |
|
) |
|
s.cLock.RLock() |
|
nodes, ok = s.cache[user] |
|
s.cLock.RUnlock() |
|
if !ok { |
|
err = ecode.NothingFound |
|
return |
|
} |
|
if node, ok = nodes.Data[treeID]; !ok { |
|
err = ecode.AccessDenied |
|
return |
|
} |
|
if len(node.Path) == 0 { |
|
err = ecode.NothingFound |
|
return |
|
} |
|
if err = s.dao.DB.Model(&model.App{}).Where("tree_id =?", treeID).Update("name", node.Path).Error; err != nil { |
|
log.Error("AppRename update error(%v)", err) |
|
return |
|
} |
|
return |
|
} |
|
|
|
// GetApps ... |
|
func (s *Service) GetApps(env string) (apps []*model.App, err error) { |
|
if err = s.dao.DB.Where("env = ?", env).Find(&apps).Error; err != nil { |
|
log.Error("GetApps error(%v)", err) |
|
} |
|
return |
|
} |
|
|
|
// IdsGetApps ... |
|
func (s *Service) IdsGetApps(ids []int64) (apps []*model.App, err error) { |
|
if err = s.dao.DB.Where("id in (?)", ids).Find(&apps).Error; err != nil { |
|
log.Error("IdsGetApps error(%v)", err) |
|
} |
|
return |
|
} |
|
|
|
// UpAppStatus edit status. |
|
func (s *Service) UpAppStatus(ctx context.Context, status int8, treeID int64) (err error) { |
|
var ( |
|
apps []*model.App |
|
) |
|
ups := map[string]interface{}{ |
|
"status": status, |
|
} |
|
if err = s.dao.DB.Model(apps).Where("tree_id = ?", treeID).Updates(ups).Error; err != nil { |
|
log.Error("AppStatus error(%v) status(%v)", err, status) |
|
} |
|
return |
|
}
|
|
|