Files
2026-07-17 16:52:49 +08:00

120 lines
3.9 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package oci
import (
"context"
"fmt"
"time"
"github.com/oracle/oci-go-sdk/v65/common"
"github.com/oracle/oci-go-sdk/v65/core"
)
// shapeCacheTTL 是 shape 清单缓存时长:区域提供的 shape 极少变化。
const shapeCacheTTL = 6 * time.Hour
// ComputeShape 是租户在目标区域实际可用的一种实例规格。
// Flex 规格带 Ocpus/Memory 的可选范围;固定规格带其固定值。
type ComputeShape struct {
Name string `json:"name"`
BillingType string `json:"billingType"` // ALWAYS_FREE / LIMITED_FREE / PAID
IsFlexible bool `json:"isFlexible"`
Ocpus float32 `json:"ocpus,omitempty"`
MemoryInGBs float32 `json:"memoryInGBs,omitempty"`
OcpusMin float32 `json:"ocpusMin,omitempty"`
OcpusMax float32 `json:"ocpusMax,omitempty"`
MemoryMinGBs float32 `json:"memoryMinGBs,omitempty"`
MemoryMaxGBs float32 `json:"memoryMaxGBs,omitempty"`
Gpus int `json:"gpus,omitempty"`
ProcessorDescription string `json:"processorDescription,omitempty"`
// QuotaNames 是该 shape 对应的配额名(与 Limits 服务 compute limit name 同名),
// 前端据此结合 limits 行判定配额与 AD 可用性。
QuotaNames []string `json:"quotaNames,omitempty"`
}
// shapeCacheEntry 是一份 shape 清单的缓存条目。
type shapeCacheEntry struct {
expires time.Time
shapes []ComputeShape
}
// ListShapes 实现 Client:列出租户在目标区域可用的实例规格(带 TTL 缓存)。
// availabilityDomain 为空时返回区域级去重清单。
func (c *RealClient) ListShapes(ctx context.Context, cred Credentials, region, availabilityDomain string) ([]ComputeShape, error) {
key := cred.TenancyOCID + "|" + region + "|" + availabilityDomain
if v, ok := c.shapes.Load(key); ok {
if e := v.(shapeCacheEntry); time.Now().Before(e.expires) {
return e.shapes, nil
}
}
shapes, err := c.fetchShapes(ctx, cred, region, availabilityDomain)
if err != nil {
return nil, err
}
c.shapes.Store(key, shapeCacheEntry{expires: time.Now().Add(shapeCacheTTL), shapes: shapes})
return shapes, nil
}
// fetchShapes 分页拉取 shape 清单并按名称去重(不带 AD 时 OCI 每个可用域各返回一份)。
func (c *RealClient) fetchShapes(ctx context.Context, cred Credentials, region, availabilityDomain string) ([]ComputeShape, error) {
cc, err := c.computeClient(cred, region)
if err != nil {
return nil, err
}
shapes := []ComputeShape{}
seen := map[string]bool{}
var page *string
for {
req := core.ListShapesRequest{
CompartmentId: cred.EffectiveCompartment(),
Limit: common.Int(500),
Page: page,
}
if availabilityDomain != "" {
req.AvailabilityDomain = &availabilityDomain
}
resp, err := cc.ListShapes(ctx, req)
if err != nil {
return nil, fmt.Errorf("list shapes: %w", err)
}
for _, item := range resp.Items {
if name := deref(item.Shape); !seen[name] {
seen[name] = true
shapes = append(shapes, toComputeShape(item))
}
}
if resp.OpcNextPage == nil {
return shapes, nil
}
page = resp.OpcNextPage
}
}
func toComputeShape(s core.Shape) ComputeShape {
out := ComputeShape{
Name: deref(s.Shape),
BillingType: string(s.BillingType),
IsFlexible: s.OcpuOptions != nil,
ProcessorDescription: deref(s.ProcessorDescription),
QuotaNames: s.QuotaNames,
}
out.Ocpus, out.MemoryInGBs = deref32(s.Ocpus), deref32(s.MemoryInGBs)
if s.Gpus != nil {
out.Gpus = *s.Gpus
}
if o := s.OcpuOptions; o != nil {
out.OcpusMin, out.OcpusMax = deref32(o.Min), deref32(o.Max)
}
if m := s.MemoryOptions; m != nil {
out.MemoryMinGBs, out.MemoryMaxGBs = deref32(m.MinInGBs), deref32(m.MaxInGBs)
}
return out
}
// deref32 解引用 *float32nil 时返回零值。
func deref32(p *float32) float32 {
if p == nil {
return 0
}
return *p
}