Files
APIPark/service/release/iml.go
T

555 lines
15 KiB
Go

package release
import (
"context"
"errors"
"time"
"github.com/APIParkLab/APIPark/service/api"
"github.com/APIParkLab/APIPark/service/universally/commit"
"github.com/APIParkLab/APIPark/service/upstream"
"github.com/APIParkLab/APIPark/stores/release"
"github.com/eolinker/go-common/auto"
"github.com/eolinker/go-common/autowire"
"github.com/eolinker/go-common/utils"
"github.com/google/uuid"
"gorm.io/gorm"
)
var (
_ IReleaseService = (*imlReleaseService)(nil)
_ auto.CompleteService = (*imlReleaseService)(nil)
_ autowire.Complete = (*imlReleaseService)(nil)
)
type imlReleaseService struct {
releaseStore release.IReleaseStore `autowired:""`
commitStore release.IReleaseCommitStore `autowired:""`
releaseRuntime release.IReleaseRuntime `autowired:""`
}
func (s *imlReleaseService) UpdateRelease(ctx context.Context, id string, update *Update) error {
info, err := s.releaseStore.GetByUUID(ctx, id)
if err != nil {
return err
}
if update.Version != nil {
info.Name = *update.Version
}
if update.Remark != nil {
info.Remark = *update.Remark
}
_, err = s.releaseStore.Update(ctx, info)
return err
}
func (s *imlReleaseService) GetRunningList(ctx context.Context, serviceId ...string) ([]*Release, error) {
w := make(map[string]interface{})
if len(serviceId) > 0 {
w["service"] = serviceId
}
list, err := s.releaseRuntime.List(ctx, w)
if err != nil {
return nil, err
}
if len(list) == 0 {
return nil, nil
}
commitIds := utils.SliceToSlice(list, func(o *release.Runtime) string {
return o.Release
})
commits, err := s.releaseStore.List(ctx, map[string]interface{}{
"uuid": commitIds,
})
return utils.SliceToSlice(commits, FromEntity), err
}
func (s *imlReleaseService) GetRunningApiDocCommits(ctx context.Context, serviceIds ...string) ([]string, error) {
w := make(map[string]interface{})
if len(serviceIds) > 0 {
w["service"] = serviceIds
}
runnings, err := s.releaseRuntime.List(ctx, w)
if err != nil {
return nil, err
}
apiDocCommits := make([]string, 0, len(runnings))
for _, running := range runnings {
commits, err := s.getCommitByType(ctx, running.Release, CommitApiDocument, running.Service)
if err != nil {
return nil, err
}
if len(commits) == 0 {
continue
}
apiDocCommits = append(apiDocCommits, commits[0].Commit)
}
return apiDocCommits, nil
}
func (s *imlReleaseService) Completeness(partitions []string, apis []string, apiRequestCommits []*commit.Commit[api.Request], proxyCommits []*commit.Commit[api.Proxy], upstreamCommits []*commit.Commit[upstream.Config]) bool {
requests := utils.SliceToMap(apiRequestCommits, func(o *commit.Commit[api.Request]) string {
return o.Target
})
proxys := utils.SliceToMap(proxyCommits, func(o *commit.Commit[api.Proxy]) string {
return o.Target
})
for _, aid := range apis {
_, has := requests[aid]
if !has {
return false
}
_, has = proxys[aid]
if !has {
return false
}
}
upstreamMap := make(map[string]map[string]struct{})
for _, upstreamCommit := range upstreamCommits {
if _, has := upstreamMap[upstreamCommit.Target]; !has {
upstreamMap[upstreamCommit.Target] = make(map[string]struct{})
}
upstreamMap[upstreamCommit.Target][upstreamCommit.Key] = struct{}{}
}
for _, partition := range partitions {
for _, u := range upstreamMap {
if _, has := u[partition]; !has {
return false
}
}
}
return true
}
func (s *imlReleaseService) GetCommits(ctx context.Context, id string) ([]*ProjectCommits, error) {
list, err := s.commitStore.List(ctx, map[string]interface{}{
"release": id,
})
if err != nil {
return nil, err
}
return utils.SliceToSlice(list, func(o *release.Commit) *ProjectCommits {
return &ProjectCommits{
Release: o.Release,
Target: o.Target,
Key: o.Key,
Type: o.Type,
Commit: o.Commit,
}
}), nil
}
func (s *imlReleaseService) OnComplete() {
auto.RegisterService("release", s)
}
func (s *imlReleaseService) GetLabels(ctx context.Context, ids ...string) map[string]string {
if len(ids) == 0 {
return nil
}
if len(ids) == 1 {
o, err := s.releaseStore.GetByUUID(ctx, ids[0])
if err != nil || o == nil {
return nil
}
return map[string]string{
o.UUID: o.Name,
}
}
list, err := s.releaseStore.ListQuery(ctx, "`uuid` in ?", []interface{}{ids}, "id")
if err != nil {
return nil
}
return utils.SliceToMapO(list, func(o *release.Release) (string, string) { return o.UUID, o.Name })
}
func (s *imlReleaseService) GetApiProxyCommit(ctx context.Context, id string, apiUUID string) (string, error) {
commits, err := s.getCommitByType(ctx, id, CommitApiProxy, apiUUID)
if err != nil {
return "", err
}
if len(commits) == 0 {
return "", errors.New("not found")
}
return commits[0].Commit, nil
}
func (s *imlReleaseService) getCommitByType(ctx context.Context, releaseId, t CommitType, target string) ([]*release.Commit, error) {
where := "`release` = ? and `type` = ? and `target` = ?"
args := []interface{}{releaseId, t, target}
return s.commitStore.ListQuery(ctx, where, args, "")
}
//func (s *imlReleaseService) GetApiDocCommit(ctx context.Context, id string, apiUUID string) (string, error) {
// commits, err := s.getCommitByType(ctx, id, CommitApiDocument, apiUUID, CommitApiDocument)
// if err != nil {
// return "", err
// }
// if len(commits) == 0 {
// return "", errors.New("not found")
// }
//
// return commits[0].Commit, nil
//}
//func (s *imlReleaseService) GetRunningApiDocCommit(ctx context.Context, service string, apiUUID string) (string, error) {
// running, err := s.releaseRuntime.First(ctx, map[string]interface{}{
// "service": service,
// })
// if err != nil {
// return "", err
// }
// return s.GetApiDocCommit(ctx, running.Release, apiUUID)
//
//}
func (s *imlReleaseService) GetRunningApiProxyCommit(ctx context.Context, service string, apiUUID string) (string, error) {
running, err := s.releaseRuntime.First(ctx, map[string]interface{}{
"service": service,
})
if err != nil {
return "", err
}
return s.GetApiProxyCommit(ctx, running.Release, apiUUID)
}
//
//func (s *imlReleaseService) DiffApis(ctx context.Context, baseApis []*Api, targetApis []*Api) []*APiDiff {
// result := make([]*APiDiff, 0, len(targetApis)+len(baseApis))
// baseApiMap := utils.SliceToMap(baseApis, func(v *Api) string {
// return v.Api
// })
// for _, targetApi := range targetApis {
// if baseApi, ok := baseApiMap[targetApi.Api]; ok {
// if baseApi.ProxyCommit != targetApi.ProxyCommit || baseApi.DocCommit != targetApi.DocCommit {
// result = append(result, &APiDiff{
// Api: targetApi.Api,
// Change: project_diff.ChangeTypeUpdate,
// })
// } else {
// result = append(result, &APiDiff{
// Api: targetApi.Api,
// Change: project_diff.ChangeTypeNone,
// })
// }
// delete(baseApiMap, targetApi.Api)
// } else {
// result = append(result, &APiDiff{
// Api: targetApi.Api,
// Change: project_diff.ChangeTypeNew,
// })
// }
// }
// for _, baseApi := range baseApiMap {
// result = append(result, &APiDiff{
// Api: baseApi.Api,
// Change: project_diff.ChangeTypeDelete,
// })
// }
// return result
//}
//
//func (s *imlReleaseService) DiffUpstreams(ctx context.Context, baseUpstreams []*UpstreamCommit, targetUpstreams []*UpstreamCommit) []*UpstreamDiff {
// Upstreams := make([]*UpstreamDiff, 0, len(targetUpstreams)+len(baseUpstreams))
// baseUpstreamMap := utils.SliceToMap(baseUpstreams, func(v *UpstreamCommit) string {
// return fmt.Sprintf("%s-%s", v.UpstreamCommit, v.Cluster)
// })
// for _, targetUpstream := range targetUpstreams {
// key := fmt.Sprintf("%s-%s", targetUpstream.UpstreamCommit, targetUpstream.Cluster)
// if baseUpstream, ok := baseUpstreamMap[key]; ok {
// if baseUpstream.Commit != targetUpstream.Commit {
// Upstreams = append(Upstreams, &UpstreamDiff{
// UpstreamCommit: targetUpstream.UpstreamCommit,
// Cluster: targetUpstream.Cluster,
// Commit: targetUpstream.Commit,
// Change: project_diff.ChangeTypeUpdate,
// })
// } else {
// Upstreams = append(Upstreams, &UpstreamDiff{
// UpstreamCommit: targetUpstream.UpstreamCommit,
// Cluster: targetUpstream.Cluster,
// Commit: targetUpstream.Commit,
// Change: project_diff.ChangeTypeNone,
// })
// }
// delete(baseUpstreamMap, targetUpstream.UpstreamCommit)
// } else {
// Upstreams = append(Upstreams, &UpstreamDiff{
// UpstreamCommit: targetUpstream.UpstreamCommit,
// Cluster: targetUpstream.Cluster,
// Commit: targetUpstream.Commit,
// Change: project_diff.ChangeTypeNew,
// })
// }
// }
// for _, baseUpstream := range baseUpstreamMap {
// Upstreams = append(Upstreams, &UpstreamDiff{
// UpstreamCommit: baseUpstream.UpstreamCommit,
// Cluster: baseUpstream.Cluster,
// Commit: baseUpstream.Commit,
// Change: project_diff.ChangeTypeDelete,
// })
// }
// return Upstreams
//}
func (s *imlReleaseService) SetRunning(ctx context.Context, service string, id string) error {
_, err := s.releaseRuntime.DeleteWhere(ctx, map[string]interface{}{"service": service})
if err != nil {
return err
}
operator := utils.UserId(ctx)
return s.releaseRuntime.Save(ctx, &release.Runtime{
Id: 0,
Service: service,
Release: id,
UpdateTime: time.Now(),
Operator: operator,
})
}
func (s *imlReleaseService) CreateRelease(ctx context.Context, service, version, remark string, apiRequestCommit, apisProxyCommits map[string]string, apiDocCommits, serviceDocCommits string, upstreams map[string]map[string]string, strategies map[string]string) (*Release, error) {
operator := utils.UserId(ctx)
releaseId := uuid.NewString()
commits := make([]*release.Commit, 0, len(apisProxyCommits)+len(upstreams)+2)
for apiId, commitUUID := range apiRequestCommit {
commits = append(commits, &release.Commit{
Type: CommitApiRequest,
Target: apiId,
Release: releaseId,
Key: CommitApiRequest,
Commit: commitUUID,
})
}
for aid, commitUUID := range apisProxyCommits {
commits = append(commits, &release.Commit{
Type: CommitApiProxy,
Target: aid,
Release: releaseId,
Key: CommitApiProxy,
Commit: commitUUID,
})
}
for upId, upstreamsByPartition := range upstreams {
for partition, commitUUID := range upstreamsByPartition {
commits = append(commits, &release.Commit{
Type: CommitUpstream,
Target: upId,
Release: releaseId,
Key: partition,
Commit: commitUUID,
})
}
}
if apiDocCommits != "" {
commits = append(commits, &release.Commit{
Type: CommitApiDocument,
Target: service,
Release: releaseId,
Key: CommitApiDocument,
Commit: apiDocCommits,
})
}
if serviceDocCommits != "" {
commits = append(commits, &release.Commit{
Type: CommitServiceDoc,
Target: service,
Release: releaseId,
Key: CommitServiceDoc,
Commit: serviceDocCommits,
})
}
for key, value := range strategies {
commits = append(commits, &release.Commit{
Type: CommitStrategy,
Target: key,
Release: releaseId,
Key: CommitStrategy,
Commit: value,
})
}
ev := &release.Release{
Id: 0,
UUID: releaseId,
Name: version,
Service: service,
Remark: remark,
Creator: operator,
CreateAt: time.Now(),
}
err := s.releaseStore.Transaction(ctx, func(ctx context.Context) error {
ok, e := s.CheckNewVersion(ctx, service, version)
if e != nil {
return e
}
if !ok {
return errors.New("version already exists")
}
err := s.releaseStore.Insert(ctx, ev)
if err != nil {
return err
}
return s.commitStore.Insert(ctx, commits...)
})
if err != nil {
return nil, err
}
return FromEntity(ev), nil
}
func (s *imlReleaseService) CheckNewVersion(ctx context.Context, service string, version string) (bool, error) {
v, err := s.releaseStore.First(ctx, map[string]interface{}{
"service": service,
"name": version,
})
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
return false, err
}
if errors.Is(err, gorm.ErrRecordNotFound) || v == nil {
return true, nil
}
return false, nil
}
func (s *imlReleaseService) GetRelease(ctx context.Context, id string) (*Release, error) {
r, err := s.releaseStore.GetByUUID(ctx, id)
if err != nil {
return nil, err
}
return FromEntity(r), nil
}
//
//func (s *imlReleaseService) Diff(ctx context.Context, baseReleaseId string, targetReleaseId string) (*Diff, error) {
// if baseReleaseId != "" || targetReleaseId != "" {
// return nil, errors.New("not support")
// }
// baseApis, baseUpstreams, err := s.GetReleaseInfos(ctx, baseReleaseId)
// if err != nil {
// return nil, err
// }
// targetApis, targetUpstreams, err := s.GetReleaseInfos(ctx, targetReleaseId)
// if err != nil {
// return nil, err
// }
//
// df := new(Diff)
// df.Routers = s.DiffApis(ctx, baseApis, targetApis)
// df.Upstreams = s.DiffUpstreams(ctx, baseUpstreams, targetUpstreams)
// return df, nil
//}
func (s *imlReleaseService) DeleteRelease(ctx context.Context, id string) error {
//todo 判断版本是否有使用中的未完结流程
return s.releaseStore.Transaction(ctx, func(ctx context.Context) error {
first, err := s.releaseRuntime.First(ctx, map[string]interface{}{
"release": id,
})
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
return err
}
if err == nil && first != nil {
return errors.New("release is in use")
}
err = s.releaseStore.DeleteUUID(ctx, id)
if err != nil {
return err
}
_, err = s.commitStore.DeleteWhere(ctx, map[string]interface{}{
"release": id,
})
if err != nil {
return err
}
return nil
})
}
func (s *imlReleaseService) List(ctx context.Context, service string) ([]*Release, error) {
list, err := s.releaseStore.List(ctx, map[string]interface{}{"service": service}, "create_at desc")
if err != nil {
return nil, err
}
return utils.SliceToSlice(list, FromEntity), nil
}
func (s *imlReleaseService) GetReleaseInfos(ctx context.Context, id string) ([]*APICommit, []*APICommit, *APICommit, []*UpstreamCommit, *ServiceCommit, error) {
commits, err := s.commitStore.List(ctx, map[string]interface{}{
"release": id,
})
if err != nil {
return nil, nil, nil, nil, nil, err
}
apiRequestCommits := make([]*APICommit, 0, len(commits))
apiProxyCommits := make([]*APICommit, 0, len(commits))
var apiDocCommit *APICommit
upstreamCommits := make([]*UpstreamCommit, 0, len(commits))
var serviceDocCommit *ServiceCommit
for _, v := range commits {
switch v.Type {
case CommitApiRequest:
apiRequestCommits = append(apiRequestCommits, &APICommit{
Release: v.Release,
API: v.Target,
Commit: v.Commit,
})
case CommitApiProxy:
apiProxyCommits = append(apiProxyCommits, &APICommit{
Release: v.Release,
API: v.Target,
Commit: v.Commit,
})
case CommitApiDocument:
apiDocCommit = &APICommit{
Release: v.Release,
API: v.Target,
Commit: v.Commit,
}
case CommitUpstream:
upstreamCommits = append(upstreamCommits, &UpstreamCommit{
Release: v.Release,
Upstream: v.Target,
Partition: v.Key,
Commit: v.Commit,
})
case CommitServiceDoc:
serviceDocCommit = &ServiceCommit{
Release: v.Release,
Service: v.Target,
Commit: v.Commit,
}
}
}
return apiRequestCommits, apiProxyCommits, apiDocCommit, upstreamCommits, serviceDocCommit, nil
}
func (s *imlReleaseService) GetRunning(ctx context.Context, service string) (*Release, error) {
running, err := s.releaseRuntime.First(ctx, map[string]interface{}{
"service": service,
})
if err != nil {
return nil, err
}
return s.GetRelease(ctx, running.Release)
}