mirror of
https://github.com/APIParkLab/APIPark.git
synced 2026-06-04 10:13:53 +08:00
368 lines
11 KiB
Go
368 lines
11 KiB
Go
package release
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
|
|
strategy_dto "github.com/APIParkLab/APIPark/module/strategy/dto"
|
|
|
|
"github.com/APIParkLab/APIPark/service/strategy"
|
|
|
|
api_doc "github.com/APIParkLab/APIPark/service/api-doc"
|
|
service_doc "github.com/APIParkLab/APIPark/service/service-doc"
|
|
|
|
"github.com/APIParkLab/APIPark/service/cluster"
|
|
"github.com/APIParkLab/APIPark/service/service"
|
|
"github.com/APIParkLab/APIPark/service/service_diff"
|
|
|
|
"github.com/APIParkLab/APIPark/module/release/dto"
|
|
serviceDiff "github.com/APIParkLab/APIPark/module/service-diff"
|
|
"github.com/APIParkLab/APIPark/service/api"
|
|
"github.com/APIParkLab/APIPark/service/publish"
|
|
"github.com/APIParkLab/APIPark/service/release"
|
|
"github.com/APIParkLab/APIPark/service/universally/commit"
|
|
"github.com/APIParkLab/APIPark/service/upstream"
|
|
"github.com/eolinker/go-common/auto"
|
|
"github.com/eolinker/go-common/store"
|
|
"github.com/eolinker/go-common/utils"
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
var (
|
|
_ IReleaseModule = (*imlReleaseModule)(nil)
|
|
projectRuleMustServer = map[string]bool{
|
|
"as_server": true,
|
|
}
|
|
)
|
|
|
|
type imlReleaseModule struct {
|
|
projectDiffModule serviceDiff.IServiceDiffModule `autowired:""`
|
|
releaseService release.IReleaseService `autowired:""`
|
|
apiService api.IAPIService `autowired:""`
|
|
apiDocService api_doc.IAPIDocService `autowired:""`
|
|
serviceDocService service_doc.IDocService `autowired:""`
|
|
upstreamService upstream.IUpstreamService `autowired:""`
|
|
publishService publish.IPublishService `autowired:""`
|
|
strategyService strategy.IStrategyService `autowired:""`
|
|
transaction store.ITransaction `autowired:""`
|
|
projectService service.IServiceService `autowired:""`
|
|
clusterService cluster.IClusterService `autowired:""`
|
|
}
|
|
|
|
func (m *imlReleaseModule) latestStrategyCommits(ctx context.Context, serviceId string) ([]*commit.Commit[strategy.Commit], error) {
|
|
list, err := m.strategyService.All(ctx, 2, serviceId)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("get latest strategy failed:%w", err)
|
|
}
|
|
for _, s := range list {
|
|
err = m.strategyService.CommitStrategy(ctx, strategy_dto.ScopeService, serviceId, s.Id, s)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
return m.strategyService.ListLatestStrategyCommit(ctx, strategy_dto.ScopeService, serviceId)
|
|
}
|
|
|
|
func (m *imlReleaseModule) Create(ctx context.Context, serviceId string, input *dto.CreateInput) (string, error) {
|
|
proInfo, err := m.projectService.Check(ctx, serviceId, projectRuleMustServer)
|
|
if err != nil {
|
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
|
return "", errors.New("project not found")
|
|
}
|
|
return "", err
|
|
}
|
|
clusters, err := m.clusterService.List(ctx)
|
|
if err != nil || len(clusters) == 0 {
|
|
return "", fmt.Errorf("cluster not set:%w", err)
|
|
}
|
|
|
|
apis, err := m.apiService.ListForService(ctx, proInfo.Id)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
if len(apis) == 0 {
|
|
return "", errors.New("api not found")
|
|
}
|
|
apiUUIDS := utils.SliceToSlice(apis, func(a *api.API) string {
|
|
return a.UUID
|
|
})
|
|
apiProxy, err := m.apiService.ListLatestCommitProxy(ctx, apiUUIDS...)
|
|
if err != nil {
|
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
|
return "", errors.New("api config or document not found")
|
|
}
|
|
return "", err
|
|
}
|
|
if len(apis) != len(apiProxy) {
|
|
return "", errors.New("api or document not found")
|
|
}
|
|
|
|
upstreams, err := m.upstreamService.ListLatestCommit(ctx, cluster.DefaultClusterID, serviceId)
|
|
if err != nil {
|
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
|
return "", errors.New("api config or document not found")
|
|
}
|
|
return "", err
|
|
}
|
|
|
|
apiProxyCommits := utils.SliceToMapO(apiProxy, func(c *commit.Commit[api.Proxy]) (string, string) {
|
|
return c.Target, c.UUID
|
|
})
|
|
|
|
upstreamCommits := utils.SliceToMapArray(upstreams, func(c *commit.Commit[upstream.Config]) string {
|
|
return c.Target
|
|
})
|
|
upstreamCommitsForUKC := utils.MapChange(upstreamCommits, func(ls []*commit.Commit[upstream.Config]) map[string]string {
|
|
return utils.SliceToMapO(ls, func(c *commit.Commit[upstream.Config]) (string, string) {
|
|
return c.Key, c.UUID
|
|
})
|
|
})
|
|
apiInfos, err := m.apiService.ListInfo(ctx, apiUUIDS...)
|
|
var newRelease *release.Release
|
|
err = m.transaction.Transaction(ctx, func(ctx context.Context) error {
|
|
for _, a := range apiInfos {
|
|
err = m.apiService.SaveRequest(ctx, a.UUID, &api.Request{
|
|
Path: a.Path,
|
|
Methods: a.Methods,
|
|
Protocols: a.Protocols,
|
|
Match: a.Match,
|
|
Disable: a.Disable,
|
|
Upstream: a.Upstream,
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
requestCommits, err := m.apiService.ListLatestCommitRequest(ctx, apiUUIDS...)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
doc, err := m.apiDocService.GetDoc(ctx, serviceId)
|
|
if err != nil {
|
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
|
return fmt.Errorf("api doc not found")
|
|
}
|
|
return err
|
|
}
|
|
err = m.apiDocService.CommitDoc(ctx, serviceId, doc)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
docCommit, err := m.apiDocService.LatestDocCommit(ctx, serviceId)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
serviceDoc, err := m.serviceDocService.Get(ctx, serviceId)
|
|
if err != nil {
|
|
if !errors.Is(err, gorm.ErrRecordNotFound) {
|
|
return err
|
|
}
|
|
serviceDoc = &service_doc.Doc{
|
|
Doc: "",
|
|
}
|
|
}
|
|
err = m.serviceDocService.CommitDoc(ctx, serviceId, serviceDoc)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
serviceDocCommit, err := m.serviceDocService.LatestDocCommit(ctx, serviceId)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
strategies, err := m.latestStrategyCommits(ctx, serviceId)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
strategyCommits := utils.SliceToMapO(strategies, func(c *commit.Commit[strategy.Commit]) (string, string) {
|
|
return c.Target, c.UUID
|
|
})
|
|
|
|
if !m.releaseService.Completeness(utils.SliceToSlice(clusters, func(s *cluster.Cluster) string {
|
|
return s.Uuid
|
|
}), apiUUIDS, requestCommits, apiProxy, upstreams) {
|
|
return errors.New("completeness check failed")
|
|
}
|
|
requestCommitMap := utils.SliceToMapO(requestCommits, func(c *commit.Commit[api.Request]) (string, string) {
|
|
return c.Target, c.UUID
|
|
})
|
|
newRelease, err = m.releaseService.CreateRelease(ctx, serviceId, input.Version, input.Remark, requestCommitMap, apiProxyCommits, docCommit.UUID, serviceDocCommit.UUID, upstreamCommitsForUKC, strategyCommits)
|
|
return err
|
|
})
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
return newRelease.UUID, err
|
|
}
|
|
|
|
func (m *imlReleaseModule) Detail(ctx context.Context, project string, id string) (*dto.Detail, error) {
|
|
r, err := m.releaseService.GetRelease(ctx, id)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if r.Service != project {
|
|
return nil, errors.New("release not found")
|
|
}
|
|
running, err := m.releaseService.GetRunning(ctx, project)
|
|
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
|
|
return nil, err
|
|
}
|
|
runningRelease := ""
|
|
if running != nil {
|
|
runningRelease = running.UUID
|
|
}
|
|
diff, err := m.projectDiffModule.Diff(ctx, project, runningRelease, r.UUID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
out, err := m.projectDiffModule.Out(ctx, diff)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &dto.Detail{
|
|
Id: r.UUID,
|
|
Version: r.Version,
|
|
Remark: r.Remark,
|
|
Service: auto.UUID(r.Service),
|
|
CreateTime: auto.TimeLabel(r.CreateAt),
|
|
Creator: auto.UUID(r.Creator),
|
|
Diffs: out,
|
|
}, nil
|
|
}
|
|
|
|
func (m *imlReleaseModule) List(ctx context.Context, project string) ([]*dto.Release, error) {
|
|
_, err := m.projectService.Check(ctx, project, projectRuleMustServer)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
list, err := m.releaseService.List(ctx, project)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
running, err := m.releaseService.GetRunning(ctx, project)
|
|
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
|
|
return nil, err
|
|
}
|
|
|
|
releaseIds := utils.SliceToSlice(list, func(s *release.Release) string {
|
|
return s.UUID
|
|
})
|
|
flows, err := m.publishService.Latest(ctx, releaseIds...)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
flowMap := utils.SliceToMap(flows, func(s *publish.Publish) string {
|
|
return s.Release
|
|
})
|
|
|
|
return utils.SliceToSlice(list, func(s *release.Release) *dto.Release {
|
|
|
|
r := &dto.Release{
|
|
Id: s.UUID,
|
|
Service: auto.UUID(s.Service),
|
|
Version: s.Version,
|
|
Remark: s.Remark,
|
|
Status: dto.StatusNone,
|
|
CanRollback: false,
|
|
CanDelete: true,
|
|
Creator: auto.UUID(s.Creator),
|
|
CreateTime: auto.TimeLabel(s.CreateAt),
|
|
}
|
|
|
|
if running != nil && running.UUID == s.UUID {
|
|
r.Status = dto.StatusRunning
|
|
r.CanRollback = true
|
|
r.CanDelete = false
|
|
}
|
|
flow, has := flowMap[s.UUID]
|
|
if has {
|
|
r.FlowId = flow.Id
|
|
|
|
if flow.Status == publish.StatusApply {
|
|
r.Status = dto.StatusApply
|
|
r.CanDelete = false
|
|
} else if flow.Status == publish.StatusAccept {
|
|
|
|
r.Status = dto.StatusAccept
|
|
r.CanDelete = false
|
|
} else if flow.Status == publish.StatusPublishError {
|
|
r.Status = dto.StatusError
|
|
r.CanDelete = false
|
|
}
|
|
}
|
|
return r
|
|
}), nil
|
|
}
|
|
|
|
func (m *imlReleaseModule) Delete(ctx context.Context, project string, id string) error {
|
|
_, err := m.projectService.Check(ctx, project, projectRuleMustServer)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return m.transaction.Transaction(ctx, func(ctx context.Context) error {
|
|
r, err := m.releaseService.GetRelease(ctx, id)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if r == nil {
|
|
return errors.New("release not found")
|
|
}
|
|
if r.Service != project {
|
|
return errors.New("project not match")
|
|
}
|
|
running, err := m.releaseService.GetRunning(ctx, project)
|
|
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
|
|
return err
|
|
}
|
|
if running != nil && running.UUID == id {
|
|
return errors.New("can not delete running release")
|
|
}
|
|
flow, err := m.publishService.GetLatest(ctx, id)
|
|
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
|
|
return err
|
|
}
|
|
if flow != nil {
|
|
if flow.Status == publish.StatusApply || flow.Status == publish.StatusAccept {
|
|
return errors.New("can not delete release in apply or approve flow")
|
|
}
|
|
}
|
|
return m.releaseService.DeleteRelease(ctx, id)
|
|
})
|
|
|
|
}
|
|
|
|
func (m *imlReleaseModule) Preview(ctx context.Context, project string) (*dto.Release, *service_diff.Diff, bool, error) {
|
|
_, err := m.projectService.Check(ctx, project, projectRuleMustServer)
|
|
if err != nil {
|
|
return nil, nil, false, err
|
|
}
|
|
running, err := m.releaseService.GetRunning(ctx, project)
|
|
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
|
|
return nil, nil, false, err
|
|
}
|
|
|
|
if running == nil {
|
|
running = new(release.Release)
|
|
}
|
|
|
|
diff, completeness, err := m.projectDiffModule.DiffForLatest(ctx, project, running.UUID)
|
|
if err != nil {
|
|
return nil, nil, false, err
|
|
}
|
|
return &dto.Release{
|
|
Id: running.UUID,
|
|
Version: running.Version,
|
|
Service: auto.UUID(project),
|
|
CreateTime: auto.TimeLabel(running.CreateAt),
|
|
Creator: auto.UUID(running.Creator),
|
|
Status: dto.StatusNone,
|
|
Remark: running.Remark,
|
|
CanDelete: false,
|
|
CanRollback: false,
|
|
}, diff, completeness, nil
|
|
|
|
}
|