120 lines
3.9 KiB
Go
120 lines
3.9 KiB
Go
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 解引用 *float32,nil 时返回零值。
|
||
func deref32(p *float32) float32 {
|
||
if p == nil {
|
||
return 0
|
||
}
|
||
return *p
|
||
}
|