110 lines
4.3 KiB
Go
110 lines
4.3 KiB
Go
package oci
|
||
|
||
import (
|
||
"errors"
|
||
"regexp"
|
||
"strings"
|
||
|
||
"github.com/oracle/oci-go-sdk/v65/common"
|
||
)
|
||
|
||
// ociErrorHints 把常见 OCI 错误码翻译成给用户看的中文提示。
|
||
var ociErrorHints = map[string]string{
|
||
"IncorrectState": "资源当前状态不允许该操作,通常是仍被其他资源占用或正在变更中",
|
||
"NotAuthorizedOrNotFound": "无权限或资源不存在(可能已被删除)",
|
||
"LimitExceeded": "已达到服务配额上限",
|
||
"QuotaExceeded": "compartment 配额不足",
|
||
"OutOfHostCapacity": "该可用域容量不足,可稍后重试或改用抢机任务",
|
||
"TooManyRequests": "请求过于频繁,请稍后再试",
|
||
"InvalidParameter": "请求参数无效",
|
||
"InternalError": "OCI 服务内部错误,请稍后重试",
|
||
"Conflict": "资源正在变更中,请稍后重试",
|
||
}
|
||
|
||
// ocidRe 匹配消息中的完整 OCID(unique 段 20 位以上才压缩,避免误伤短标识)。
|
||
var ocidRe = regexp.MustCompile(`ocid1\.([a-z0-9]+)\.[a-z0-9]*\.[a-z0-9-]*\.{1,2}[a-z0-9]{20,}`)
|
||
|
||
// shortenOcids 把消息里的长 OCID 压缩为「ocid1.<类型>…<尾6位>」,
|
||
// 保留资源类型与可比对的尾部,避免整条错误被 OCID 撑爆。
|
||
func shortenOcids(s string) string {
|
||
return ocidRe.ReplaceAllStringFunc(s, func(m string) string {
|
||
i := strings.Index(m, ".")
|
||
typ := m[i+1:]
|
||
if j := strings.Index(typ, "."); j > 0 {
|
||
typ = typ[:j]
|
||
}
|
||
return "ocid1." + typ + "…" + m[len(m)-6:]
|
||
})
|
||
}
|
||
|
||
// CompactError 把含 OCI ServiceError 的错误链提炼成短消息:
|
||
// 操作前缀 + 服务端消息,剥掉 SDK 附加的错误码、排障链接、
|
||
// 时间戳、SDK 版本等长文(错误码另经 API 的 ociCode 字段透出);
|
||
// 消息中的长 OCID 压缩为类型加尾位的短形式。非 OCI 错误原样返回。
|
||
func CompactError(err error) string {
|
||
var svcErr common.ServiceError
|
||
if !errors.As(err, &svcErr) {
|
||
return shortenOcids(err.Error())
|
||
}
|
||
prefix := err.Error()
|
||
if i := strings.Index(prefix, ": Error returned by"); i > 0 {
|
||
prefix = prefix[:i] + ": "
|
||
} else {
|
||
prefix = ""
|
||
}
|
||
return shortenOcids(prefix + svcErr.GetMessage())
|
||
}
|
||
|
||
// ErrorHint 返回常见 OCI 错误的中文提示;未识别返回空串。
|
||
// 容量不足常以 code=InternalError 返回,按消息内容归到容量提示。
|
||
func ErrorHint(err error) string {
|
||
var svcErr common.ServiceError
|
||
if !errors.As(err, &svcErr) {
|
||
return ""
|
||
}
|
||
if strings.Contains(svcErr.GetMessage(), "Out of host capacity") {
|
||
return ociErrorHints["OutOfHostCapacity"]
|
||
}
|
||
return ociErrorHints[svcErr.GetCode()]
|
||
}
|
||
|
||
// ServiceStatus 返回错误链中 OCI ServiceError 的 HTTP 状态码;非服务端错误 ok=false。
|
||
func ServiceStatus(err error) (int, bool) {
|
||
var svcErr common.ServiceError
|
||
if errors.As(err, &svcErr) {
|
||
return svcErr.GetHTTPStatusCode(), true
|
||
}
|
||
return 0, false
|
||
}
|
||
|
||
// IsOnDemandUnsupported 识别「模型在该区域仅为微调基座、不支持按需调用」的 400:
|
||
// OCI 消息形如 "Not allowed to call finetune base model …, use Endpoint: false"。
|
||
// ListModels 无字段可事先区分,只能在调用报错时识别并换渠道。
|
||
func IsOnDemandUnsupported(err error) bool {
|
||
var svcErr common.ServiceError
|
||
if !errors.As(err, &svcErr) {
|
||
return false
|
||
}
|
||
return svcErr.GetHTTPStatusCode() == 400 &&
|
||
strings.Contains(strings.ToLower(svcErr.GetMessage()), "finetune base model")
|
||
}
|
||
|
||
// IsEntityNotFound 识别「实体不存在」404(消息 "Entity with key … not found"):
|
||
// GenAI 对区域内无按需供给的模型 OCID 返回此类 404,属模型级错误;
|
||
// 鉴权失败的 404 是 NotAuthorizedOrNotFound(消息为 Authorization failed…),不在此列。
|
||
func IsEntityNotFound(err error) bool {
|
||
var svcErr common.ServiceError
|
||
if !errors.As(err, &svcErr) {
|
||
return false
|
||
}
|
||
msg := strings.ToLower(svcErr.GetMessage())
|
||
return svcErr.GetHTTPStatusCode() == 404 &&
|
||
strings.Contains(msg, "entity with key") && strings.Contains(msg, "not found")
|
||
}
|
||
|
||
// IsModelUnavailable 归并「模型×区域不可按需调用」两类报错:微调基座 400 与实体不存在 404;
|
||
// 命中即应把该 (渠道, 模型) 从池中剔除并换候选/换渠道,而非定论租户配额问题。
|
||
func IsModelUnavailable(err error) bool {
|
||
return IsOnDemandUnsupported(err) || IsEntityNotFound(err)
|
||
}
|