mirror of
https://github.com/APIParkLab/APIPark.git
synced 2026-06-04 10:13:53 +08:00
524 lines
14 KiB
Go
524 lines
14 KiB
Go
package dynamic_module
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/eolinker/eosc/log"
|
|
|
|
"github.com/APIParkLab/APIPark/gateway"
|
|
|
|
"github.com/eolinker/go-common/store"
|
|
|
|
"github.com/eolinker/ap-account/service/user"
|
|
|
|
"github.com/APIParkLab/APIPark/service/cluster"
|
|
|
|
"github.com/eolinker/go-common/utils"
|
|
|
|
"github.com/google/uuid"
|
|
|
|
"github.com/APIParkLab/APIPark/module/dynamic-module/driver"
|
|
|
|
dynamic_module_dto "github.com/APIParkLab/APIPark/module/dynamic-module/dto"
|
|
dynamic_module "github.com/APIParkLab/APIPark/service/dynamic-module"
|
|
)
|
|
|
|
var _ IDynamicModuleModule = (*imlDynamicModule)(nil)
|
|
|
|
type imlDynamicModule struct {
|
|
clusterService cluster.IClusterService `autowired:""`
|
|
dynamicModuleService dynamic_module.IDynamicModuleService `autowired:""`
|
|
dynamicModulePublishService dynamic_module.IDynamicModulePublishService `autowired:""`
|
|
userService user.IUserService `autowired:""`
|
|
transaction store.ITransaction `autowired:""`
|
|
}
|
|
|
|
func (i *imlDynamicModule) initGateway(ctx context.Context, clusterId string, clientDriver gateway.IClientDriver) error {
|
|
// TODO: 初始化集群操作
|
|
return nil
|
|
}
|
|
|
|
func (i *imlDynamicModule) Online(ctx context.Context, module string, id string) error {
|
|
_, has := driver.Get(module)
|
|
if !has {
|
|
return fmt.Errorf("模块【%s】不存在", module)
|
|
}
|
|
//if len(clusterInput.Clusters) == 0 {
|
|
// return fmt.Errorf("上线分区失败,分区为空")
|
|
//}
|
|
|
|
id = strings.ToLower(fmt.Sprintf("%s_%s", id, module))
|
|
info, err := i.dynamicModuleService.Get(ctx, id)
|
|
if err != nil {
|
|
return fmt.Errorf("上线失败,配置不存在")
|
|
}
|
|
clusters, err := i.clusterService.List(ctx)
|
|
if err != nil || len(clusters) == 0 {
|
|
return fmt.Errorf("上线失败,集群不存在")
|
|
}
|
|
|
|
return i.transaction.Transaction(ctx, func(ctx context.Context) error {
|
|
for _, c := range clusters {
|
|
|
|
// 插入发布历史
|
|
err = i.dynamicModulePublishService.Create(ctx, &dynamic_module.CreateDynamicModulePublish{
|
|
ID: uuid.New().String(),
|
|
DynamicModule: id,
|
|
Module: module,
|
|
Cluster: c.Uuid,
|
|
Version: info.Version,
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
err = i.dynamicClient(ctx, c.Uuid, module, func(dynamicClient gateway.IDynamicClient) error {
|
|
cfg := &gateway.DynamicRelease{}
|
|
err = json.Unmarshal([]byte(info.Config), &cfg)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
cfg.ID = id
|
|
cfg.Version = info.Version
|
|
cfg.MatchLabels = map[string]string{
|
|
"module": module,
|
|
}
|
|
err = dynamicClient.Online(ctx, cfg)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
})
|
|
}
|
|
|
|
func (i *imlDynamicModule) Offline(ctx context.Context, module string, id string) error {
|
|
_, has := driver.Get(module)
|
|
if !has {
|
|
return fmt.Errorf("模块【%s】不存在", module)
|
|
}
|
|
return i.transaction.Transaction(ctx, func(ctx context.Context) error {
|
|
id = strings.ToLower(fmt.Sprintf("%s_%s", id, module))
|
|
clusters, err := i.clusterService.List(ctx)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
clusterIds := utils.SliceToSlice(clusters, func(s *cluster.Cluster) string {
|
|
return s.Uuid
|
|
})
|
|
for _, clusterId := range clusterIds {
|
|
err := i.dynamicClient(ctx, clusterId, module, func(dynamicClient gateway.IDynamicClient) error {
|
|
return dynamicClient.Offline(ctx, &gateway.DynamicRelease{
|
|
BasicItem: &gateway.BasicItem{
|
|
ID: id,
|
|
},
|
|
})
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
}
|
|
return nil
|
|
})
|
|
}
|
|
|
|
//
|
|
//func (i *imlDynamicModule) PartitionStatuses(ctx context.Context, module string, keyword string, page int, pageSize int) (map[string]map[string]string, error) {
|
|
// _, has := driver.Get(module)
|
|
// if !has {
|
|
// return nil, fmt.Errorf("模块【%s】不存在", module)
|
|
// }
|
|
// list, _, err := i.dynamicModuleService.SearchByPage(ctx, keyword, map[string]interface{}{
|
|
// "module": module,
|
|
// }, page, pageSize, "update_at desc")
|
|
// if err != nil {
|
|
// return nil, err
|
|
// }
|
|
// partitions, err := i.partitionService.List(ctx)
|
|
// if err != nil {
|
|
// return nil, err
|
|
// }
|
|
// out := make(map[string]map[string]string)
|
|
// for _, c := range partitions {
|
|
// err := i.dynamicClient(ctx, c.Cluster, module, func(dynamicClient gateway.IDynamicClient) error {
|
|
// versions, err := dynamicClient.Versions(ctx, map[string]string{
|
|
// "module": module,
|
|
// })
|
|
// if err != nil {
|
|
// return err
|
|
// }
|
|
// for _, l := range list {
|
|
// id := strings.TrimSuffix(l.ID, fmt.Sprintf("_%s", module))
|
|
// if _, ok := out[id]; !ok {
|
|
// out[id] = make(map[string]string)
|
|
// }
|
|
//
|
|
// out[id][c.UUID] = "未发布"
|
|
// if v, ok := versions[strings.ToLower(l.ID)]; ok {
|
|
// if v == l.Version {
|
|
// out[id][c.UUID] = "已发布"
|
|
// } else {
|
|
// out[id][c.UUID] = "待发布"
|
|
// }
|
|
// }
|
|
// }
|
|
// return nil
|
|
// })
|
|
// if err != nil {
|
|
// return nil, err
|
|
// }
|
|
//
|
|
// }
|
|
// return out, nil
|
|
//}
|
|
|
|
func (i *imlDynamicModule) dynamicClient(ctx context.Context, clusterId string, resource string, h func(gateway.IDynamicClient) error) error {
|
|
client, err := i.clusterService.GatewayClient(ctx, clusterId)
|
|
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer func() {
|
|
err := client.Close(ctx)
|
|
if err != nil {
|
|
log.Warn("close apinto client:", err)
|
|
}
|
|
}()
|
|
dynamic, err := client.Dynamic(resource)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return h(dynamic)
|
|
}
|
|
|
|
//
|
|
//func (i *imlDynamicModule) PartitionStatus(ctx context.Context, module string, id string) (*dynamic_module_dto.OnlineInfo, error) {
|
|
// _, has := driver.Get(module)
|
|
//
|
|
// if !has {
|
|
// return nil, fmt.Errorf("模块【%s】不存在", module)
|
|
// }
|
|
// partitions, err := i.partitionService.List(ctx)
|
|
// if err != nil {
|
|
// return nil, err
|
|
// }
|
|
// partitionIds := utils.SliceToSlice(partitions, func(s *partition.Cluster) string {
|
|
// return s.UUID
|
|
// })
|
|
// suffix := fmt.Sprintf("_%s", module)
|
|
// id = id + suffix
|
|
// info, err := i.dynamicModuleService.Get(ctx, id)
|
|
// if err != nil {
|
|
// return nil, err
|
|
// }
|
|
// publishMap, err := i.dynamicModulePublishService.Latest(ctx, id, partitionIds)
|
|
// if err != nil {
|
|
// return nil, err
|
|
// }
|
|
//
|
|
// partitionInfos := make([]*dynamic_module_dto.PartitionInfo, 0, len(partitionIds))
|
|
// for _, c := range partitions {
|
|
// err := i.dynamicClient(ctx, c.Cluster, module, func(dynamicClient gateway.IDynamicClient) error {
|
|
// version, err := dynamicClient.Version(ctx, id)
|
|
// if err != nil {
|
|
// return err
|
|
// }
|
|
// updater := ""
|
|
// updateTime := time.Time{}
|
|
// publishInfo, ok := publishMap[c.UUID]
|
|
// if ok {
|
|
// updater = publishInfo.Creator
|
|
// updateTime = publishInfo.CreateAt
|
|
// }
|
|
// cInfo := &dynamic_module_dto.PartitionInfo{
|
|
// Name: c.UUID,
|
|
// Title: c.Name,
|
|
// Status: "未发布",
|
|
// Updater: auto.UUID(updater),
|
|
// UpdateTime: auto.TimeLabel(updateTime),
|
|
// }
|
|
// if version == info.Version {
|
|
// cInfo.Status = "已发布"
|
|
// } else if version != "" {
|
|
// cInfo.Status = "待发布"
|
|
// }
|
|
// partitionInfos = append(partitionInfos, cInfo)
|
|
// return nil
|
|
// })
|
|
// if err != nil {
|
|
// return nil, err
|
|
// }
|
|
//
|
|
// }
|
|
// return &dynamic_module_dto.OnlineInfo{
|
|
// Id: strings.TrimSuffix(info.ID, suffix),
|
|
// Name: strings.TrimSuffix(info.ID, suffix),
|
|
// Title: info.Name,
|
|
// Description: info.Description,
|
|
// Clusters: partitionInfos,
|
|
// }, nil
|
|
//}
|
|
|
|
func (i *imlDynamicModule) ModuleDrivers(ctx context.Context, group string) ([]*dynamic_module_dto.ModuleDriver, error) {
|
|
ds := driver.List(group)
|
|
return utils.SliceToSlice(ds, func(s driver.IDriver) *dynamic_module_dto.ModuleDriver {
|
|
return &dynamic_module_dto.ModuleDriver{
|
|
Name: s.Name(),
|
|
Title: s.Title(),
|
|
Path: s.Front(),
|
|
}
|
|
|
|
}), nil
|
|
}
|
|
|
|
func (i *imlDynamicModule) Render(ctx context.Context, module string) (map[string]interface{}, error) {
|
|
d, has := driver.Get(module)
|
|
if !has {
|
|
return nil, fmt.Errorf("module %s not found", module)
|
|
}
|
|
return d.Define().Render(), nil
|
|
}
|
|
|
|
func (i *imlDynamicModule) PluginInfo(ctx context.Context, module string, clusterIds ...string) (*dynamic_module_dto.PluginInfo, error) {
|
|
d, has := driver.Get(module)
|
|
if !has {
|
|
return nil, fmt.Errorf("module %s not found", module)
|
|
}
|
|
|
|
fields := make([]*driver.Field, 0, 1)
|
|
|
|
fields = append(fields, &driver.Field{
|
|
Name: "status",
|
|
Title: fmt.Sprintf("状态"),
|
|
Attr: "status",
|
|
Enum: []string{
|
|
"已发布",
|
|
"待发布",
|
|
"未发布",
|
|
},
|
|
})
|
|
return &dynamic_module_dto.PluginInfo{
|
|
PluginBasic: &dynamic_module_dto.PluginBasic{
|
|
Id: d.ID(),
|
|
Name: d.Name(),
|
|
Title: d.Title(),
|
|
},
|
|
Drivers: utils.SliceToSlice(d.Define().Drivers(), func(s *driver.Field) *dynamic_module_dto.Field {
|
|
return &dynamic_module_dto.Field{
|
|
Name: s.Name,
|
|
Title: s.Title,
|
|
}
|
|
}),
|
|
Fields: utils.SliceToSlice(d.Define().Fields(fields...), func(s *driver.Field) *dynamic_module_dto.Field {
|
|
return &dynamic_module_dto.Field{
|
|
Name: s.Name,
|
|
Title: s.Title,
|
|
Attr: s.Attr,
|
|
Enum: s.Enum,
|
|
}
|
|
}),
|
|
}, nil
|
|
}
|
|
|
|
func (i *imlDynamicModule) Create(ctx context.Context, module string, input *dynamic_module_dto.CreateDynamicModule) (*dynamic_module_dto.DynamicModule, error) {
|
|
d, has := driver.Get(module)
|
|
if !has {
|
|
return nil, fmt.Errorf("module %s not found", module)
|
|
}
|
|
|
|
id := strings.ToLower(fmt.Sprintf("%s_%s", input.Id, module))
|
|
err := i.transaction.Transaction(ctx, func(ctx context.Context) error {
|
|
cfg, err := json.Marshal(input.Config)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return i.dynamicModuleService.Create(ctx, &dynamic_module.CreateDynamicModule{
|
|
Id: id,
|
|
Name: input.Name,
|
|
Driver: input.Driver,
|
|
Description: input.Description,
|
|
Config: string(cfg),
|
|
Module: module,
|
|
Profession: d.Define().Profession(),
|
|
Skill: d.Define().Skill(),
|
|
Version: time.Now().Format("20060102150405"),
|
|
})
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return i.Get(ctx, module, input.Id)
|
|
}
|
|
|
|
func (i *imlDynamicModule) Edit(ctx context.Context, module string, id string, input *dynamic_module_dto.EditDynamicModule) (*dynamic_module_dto.DynamicModule, error) {
|
|
id = strings.ToLower(fmt.Sprintf("%s_%s", id, module))
|
|
_, err := i.get(ctx, module, id)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
var cfg *string
|
|
var version *string
|
|
if input.Config != nil {
|
|
tmp, _ := json.Marshal(input.Config)
|
|
t := string(tmp)
|
|
cfg = &t
|
|
v := time.Now().Format("20060102150405")
|
|
version = &v
|
|
}
|
|
err = i.dynamicModuleService.Save(ctx, id, &dynamic_module.EditDynamicModule{
|
|
Name: input.Name,
|
|
Description: input.Description,
|
|
Config: cfg,
|
|
Version: version,
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return i.Get(ctx, module, id)
|
|
}
|
|
|
|
func (i *imlDynamicModule) Delete(ctx context.Context, module string, ids []string) error {
|
|
return i.transaction.Transaction(ctx, func(ctx context.Context) error {
|
|
for _, id := range ids {
|
|
id = strings.ToLower(fmt.Sprintf("%s_%s", id, module))
|
|
_, err := i.get(ctx, module, id)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
err = i.dynamicModuleService.Delete(ctx, id)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return nil
|
|
})
|
|
|
|
}
|
|
|
|
func (i *imlDynamicModule) Get(ctx context.Context, module string, id string) (*dynamic_module_dto.DynamicModule, error) {
|
|
suffix := fmt.Sprintf("_%s", module)
|
|
if !strings.HasSuffix(id, suffix) {
|
|
id = strings.ToLower(fmt.Sprintf("%s_%s", id, module))
|
|
}
|
|
|
|
info, err := i.get(ctx, module, id)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
cfg := make(map[string]interface{})
|
|
err = json.Unmarshal([]byte(info.Config), &cfg)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &dynamic_module_dto.DynamicModule{
|
|
Id: strings.TrimSuffix(info.ID, suffix),
|
|
Name: info.Name,
|
|
Driver: info.Driver,
|
|
Description: info.Description,
|
|
Config: cfg,
|
|
}, nil
|
|
}
|
|
|
|
func (i *imlDynamicModule) get(ctx context.Context, module string, id string) (*dynamic_module.DynamicModule, error) {
|
|
info, err := i.dynamicModuleService.Get(ctx, id)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if info.Module != module {
|
|
return nil, fmt.Errorf("module not match")
|
|
}
|
|
return info, nil
|
|
}
|
|
|
|
func (i *imlDynamicModule) List(ctx context.Context, module string, keyword string, page int, pageSize int) ([]map[string]interface{}, int64, error) {
|
|
d, has := driver.Get(module)
|
|
if !has {
|
|
return nil, 0, fmt.Errorf("module %s not found", module)
|
|
}
|
|
list, total, err := i.dynamicModuleService.SearchByPage(ctx, keyword, map[string]interface{}{
|
|
"module": module,
|
|
}, page, pageSize, "update_at desc")
|
|
if err != nil {
|
|
return nil, 0, err
|
|
}
|
|
|
|
userIDs := utils.SliceToSlice(list, func(s *dynamic_module.DynamicModule) string {
|
|
return s.Updater
|
|
})
|
|
clusters, err := i.clusterService.List(ctx)
|
|
if err != nil {
|
|
return nil, 0, err
|
|
}
|
|
|
|
userMap := i.userService.GetLabels(ctx, userIDs...)
|
|
items := make([]map[string]interface{}, 0, len(list))
|
|
suffix := fmt.Sprintf("_%s", module)
|
|
for _, c := range clusters {
|
|
err = i.dynamicClient(ctx, c.Uuid, module, func(dynamicClient gateway.IDynamicClient) error {
|
|
versions, err := dynamicClient.Versions(ctx, map[string]string{
|
|
"module": module,
|
|
})
|
|
if err != nil {
|
|
log.Error("get versions error", err)
|
|
}
|
|
for _, l := range list {
|
|
status := "未发布"
|
|
id := strings.TrimSuffix(l.ID, suffix)
|
|
|
|
item := map[string]interface{}{
|
|
"id": id,
|
|
"title": l.Name,
|
|
"driver": l.Driver,
|
|
"description": l.Description,
|
|
"updater": userMap[l.Updater],
|
|
"update_time": l.UpdateAt.Format("2006-01-02 15:04:05"),
|
|
}
|
|
|
|
tmp := make(map[string]interface{})
|
|
err = json.Unmarshal([]byte(l.Config), &tmp)
|
|
if err == nil {
|
|
for _, column := range d.Define().Columns() {
|
|
if _, ok := item[column]; ok {
|
|
continue
|
|
}
|
|
item[column] = tmp[column]
|
|
|
|
}
|
|
}
|
|
if versions != nil {
|
|
if v, ok := versions[strings.ToLower(l.ID)]; ok {
|
|
if v == l.Version {
|
|
status = "已发布"
|
|
} else {
|
|
status = "待发布"
|
|
}
|
|
}
|
|
}
|
|
item["status"] = status
|
|
items = append(items, item)
|
|
}
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
return nil, 0, err
|
|
}
|
|
|
|
}
|
|
|
|
return items, total, nil
|
|
}
|