219 lines
7.0 KiB
Go
219 lines
7.0 KiB
Go
package service
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
|
|
"gorm.io/gorm"
|
|
|
|
"oci-portal/internal/model"
|
|
"oci-portal/internal/oci"
|
|
)
|
|
|
|
// syncScopeCaches 按配置开关同步区域 / 区间缓存:开启则整组刷新,关闭则清空。
|
|
// 随测活(导入 / 修改 / 测活按钮 / 定时任务)触发;OCI 调用失败不影响测活主流程。
|
|
func (s *OciConfigService) syncScopeCaches(ctx context.Context, cfg *model.OciConfig, cred oci.Credentials) {
|
|
if cfg.MultiRegion {
|
|
if subs, err := s.client.ListRegionSubscriptions(ctx, cred); err == nil {
|
|
_ = s.saveRegionCache(ctx, cfg.ID, subs)
|
|
}
|
|
} else {
|
|
s.db.WithContext(ctx).Where("oci_config_id = ?", cfg.ID).Delete(&model.RegionCache{})
|
|
}
|
|
if cfg.MultiCompartment {
|
|
if comps, err := s.client.ListCompartments(ctx, cred); err == nil {
|
|
_ = s.saveCompartmentCache(ctx, cfg.ID, comps)
|
|
}
|
|
} else {
|
|
s.db.WithContext(ctx).Where("oci_config_id = ?", cfg.ID).Delete(&model.CompartmentCache{})
|
|
}
|
|
}
|
|
|
|
// saveRegionCache 整组覆盖写入订阅区域缓存。
|
|
func (s *OciConfigService) saveRegionCache(ctx context.Context, cfgID uint, subs []oci.RegionSubscription) error {
|
|
rows := make([]model.RegionCache, 0, len(subs))
|
|
for _, sub := range subs {
|
|
rows = append(rows, model.RegionCache{
|
|
OciConfigID: cfgID, Key: sub.Key, Name: sub.Name,
|
|
Status: sub.Status, IsHomeRegion: sub.IsHomeRegion,
|
|
})
|
|
}
|
|
return s.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
|
|
if err := lockOciConfig(tx, cfgID); err != nil {
|
|
return err
|
|
}
|
|
if err := tx.Where("oci_config_id = ?", cfgID).Delete(&model.RegionCache{}).Error; err != nil {
|
|
return err
|
|
}
|
|
if len(rows) == 0 {
|
|
return nil
|
|
}
|
|
return tx.Create(&rows).Error
|
|
})
|
|
}
|
|
|
|
// saveCompartmentCache 整组覆盖写入 compartment 缓存。
|
|
func (s *OciConfigService) saveCompartmentCache(ctx context.Context, cfgID uint, comps []oci.Compartment) error {
|
|
rows := make([]model.CompartmentCache, 0, len(comps))
|
|
for _, c := range comps {
|
|
rows = append(rows, model.CompartmentCache{
|
|
OciConfigID: cfgID, OCID: c.ID, Name: c.Name, State: c.LifecycleState,
|
|
})
|
|
}
|
|
return s.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
|
|
if err := lockOciConfig(tx, cfgID); err != nil {
|
|
return err
|
|
}
|
|
if err := tx.Where("oci_config_id = ?", cfgID).Delete(&model.CompartmentCache{}).Error; err != nil {
|
|
return err
|
|
}
|
|
if len(rows) == 0 {
|
|
return nil
|
|
}
|
|
return tx.Create(&rows).Error
|
|
})
|
|
}
|
|
|
|
// CachedRegions 返回筛选器用的区域列表:未开启多区域支持时锁定为配置默认区域;
|
|
// 缓存缺失或存在非 READY 状态(订阅进行中)时实时刷新,直到全部 READY。
|
|
func (s *OciConfigService) CachedRegions(ctx context.Context, id uint) ([]RegionSubscriptionView, error) {
|
|
cfg, err := s.Get(ctx, id)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if !cfg.MultiRegion {
|
|
return defaultOnlyRegion(cfg), nil
|
|
}
|
|
var rows []model.RegionCache
|
|
if err := s.db.WithContext(ctx).Where("oci_config_id = ?", id).Find(&rows).Error; err != nil {
|
|
return nil, fmt.Errorf("load region cache: %w", err)
|
|
}
|
|
if len(rows) == 0 || hasPendingRegion(rows) {
|
|
if fresh, err := s.refreshRegionCache(ctx, cfg); err == nil {
|
|
return fresh, nil
|
|
}
|
|
// 实时刷新失败时退回现有缓存,避免筛选器整体不可用
|
|
}
|
|
return regionCacheViews(rows), nil
|
|
}
|
|
|
|
// refreshRegionCache 实时拉取订阅区域并覆盖缓存。
|
|
func (s *OciConfigService) refreshRegionCache(ctx context.Context, cfg *model.OciConfig) ([]RegionSubscriptionView, error) {
|
|
cred, err := s.credentialsOf(cfg)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
subs, err := s.client.ListRegionSubscriptions(ctx, cred)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if err := s.saveRegionCache(ctx, cfg.ID, subs); err != nil {
|
|
return nil, err
|
|
}
|
|
return enrichRegionAlias(subs), nil
|
|
}
|
|
|
|
// reconcileRegionCache 把 SDK 实时数据与缓存比较,有差异时覆盖缓存。
|
|
func (s *OciConfigService) reconcileRegionCache(ctx context.Context, cfgID uint, subs []oci.RegionSubscription) {
|
|
var rows []model.RegionCache
|
|
if err := s.db.WithContext(ctx).Where("oci_config_id = ?", cfgID).Find(&rows).Error; err != nil {
|
|
return
|
|
}
|
|
if regionCacheDiffers(rows, subs) {
|
|
_ = s.saveRegionCache(ctx, cfgID, subs)
|
|
}
|
|
}
|
|
|
|
func regionCacheDiffers(rows []model.RegionCache, subs []oci.RegionSubscription) bool {
|
|
if len(rows) != len(subs) {
|
|
return true
|
|
}
|
|
byKey := make(map[string]model.RegionCache, len(rows))
|
|
for _, r := range rows {
|
|
byKey[r.Key] = r
|
|
}
|
|
for _, sub := range subs {
|
|
r, ok := byKey[sub.Key]
|
|
if !ok || r.Status != sub.Status || r.Name != sub.Name || r.IsHomeRegion != sub.IsHomeRegion {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func hasPendingRegion(rows []model.RegionCache) bool {
|
|
for _, r := range rows {
|
|
if r.Status != regionStatusReady {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
// regionStatusReady 是 OCI 区域订阅完成态。
|
|
const regionStatusReady = "READY"
|
|
|
|
// defaultOnlyRegion 为未开启多区域支持的配置合成单元素列表(配置默认区域)。
|
|
func defaultOnlyRegion(cfg *model.OciConfig) []RegionSubscriptionView {
|
|
sub := oci.RegionSubscription{
|
|
Key: cfg.HomeRegionKey, Name: cfg.Region, Status: regionStatusReady,
|
|
}
|
|
if info, ok := oci.RegionByKey(cfg.HomeRegionKey); ok {
|
|
sub.IsHomeRegion = info.Name == cfg.Region
|
|
}
|
|
return enrichRegionAlias([]oci.RegionSubscription{sub})
|
|
}
|
|
|
|
func regionCacheViews(rows []model.RegionCache) []RegionSubscriptionView {
|
|
subs := make([]oci.RegionSubscription, 0, len(rows))
|
|
for _, r := range rows {
|
|
subs = append(subs, oci.RegionSubscription{
|
|
Key: r.Key, Name: r.Name, Status: r.Status, IsHomeRegion: r.IsHomeRegion,
|
|
})
|
|
}
|
|
// 与实时接口一致:主区域置顶,其余按 alias 字典序
|
|
oci.SortRegionSubscriptions(subs)
|
|
return enrichRegionAlias(subs)
|
|
}
|
|
|
|
// CachedCompartments 返回筛选器用的区间列表:未开启多区间支持返回空(前端锁定根);
|
|
// 已开启但缓存为空时实时拉取一次入库。
|
|
func (s *OciConfigService) CachedCompartments(ctx context.Context, id uint) ([]oci.Compartment, error) {
|
|
cfg, err := s.Get(ctx, id)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if !cfg.MultiCompartment {
|
|
return []oci.Compartment{}, nil
|
|
}
|
|
var rows []model.CompartmentCache
|
|
if err := s.db.WithContext(ctx).Where("oci_config_id = ?", id).
|
|
Order("name").Find(&rows).Error; err != nil {
|
|
return nil, fmt.Errorf("load compartment cache: %w", err)
|
|
}
|
|
if len(rows) == 0 {
|
|
return s.refreshCompartmentCache(ctx, cfg)
|
|
}
|
|
comps := make([]oci.Compartment, 0, len(rows))
|
|
for _, r := range rows {
|
|
comps = append(comps, oci.Compartment{ID: r.OCID, Name: r.Name, LifecycleState: r.State})
|
|
}
|
|
return comps, nil
|
|
}
|
|
|
|
// refreshCompartmentCache 实时拉取 compartment 并覆盖缓存。
|
|
func (s *OciConfigService) refreshCompartmentCache(ctx context.Context, cfg *model.OciConfig) ([]oci.Compartment, error) {
|
|
cred, err := s.credentialsOf(cfg)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
comps, err := s.client.ListCompartments(ctx, cred)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if err := s.saveCompartmentCache(ctx, cfg.ID, comps); err != nil {
|
|
return nil, err
|
|
}
|
|
return comps, nil
|
|
}
|