172 lines
5.3 KiB
Go
172 lines
5.3 KiB
Go
package entity
|
||
|
||
import (
|
||
"strings"
|
||
"time"
|
||
)
|
||
|
||
// ArtifactType Artifact 类型
|
||
type ArtifactType string
|
||
|
||
const (
|
||
ArtifactTypeChart ArtifactType = "chart" // Helm Chart
|
||
ArtifactTypeImage ArtifactType = "image" // Docker/OCI Image
|
||
ArtifactTypeOther ArtifactType = "other" // Other types
|
||
)
|
||
|
||
// Artifact OCI Artifact 领域实体
|
||
type Artifact struct {
|
||
RegistryID string
|
||
Repository string
|
||
Tag string
|
||
Digest string
|
||
Type ArtifactType
|
||
Size int64
|
||
MediaType string
|
||
ConfigType string // Config layer 的 mediaType (用于更准确的类型判断)
|
||
Annotations map[string]string
|
||
CreatedAt time.Time
|
||
}
|
||
|
||
// Repository 仓库信息
|
||
type Repository struct {
|
||
RegistryID string
|
||
Name string
|
||
TagCount int
|
||
}
|
||
|
||
// NewArtifact 创建新 Artifact
|
||
func NewArtifact(registryID, repository, tag, digest string) *Artifact {
|
||
return &Artifact{
|
||
RegistryID: registryID,
|
||
Repository: repository,
|
||
Tag: tag,
|
||
Digest: digest,
|
||
Annotations: make(map[string]string),
|
||
CreatedAt: time.Now(),
|
||
}
|
||
}
|
||
|
||
// SetType 设置 Artifact 类型(根据 mediaType 识别为 chart | image | other)
|
||
// 已废弃:请使用 DetermineType() 方法,它提供更准确的类型判断
|
||
func (a *Artifact) SetType(mediaType string) {
|
||
lowerMediaType := strings.ToLower(strings.TrimSpace(mediaType))
|
||
|
||
containsAny := func(target string, keywords ...string) bool {
|
||
for _, keyword := range keywords {
|
||
if keyword != "" && strings.Contains(target, keyword) {
|
||
return true
|
||
}
|
||
}
|
||
return false
|
||
}
|
||
|
||
switch {
|
||
case lowerMediaType == "":
|
||
a.Type = ArtifactTypeOther
|
||
case containsAny(lowerMediaType,
|
||
"helm", "cncf.helm", "helm.chart", "helm+", "chart+json", "chart.v1", "helm-package", "helm.config",
|
||
):
|
||
a.Type = ArtifactTypeChart
|
||
case containsAny(lowerMediaType,
|
||
"docker", "vnd.docker", "docker.distribution", "docker.container.image",
|
||
"vnd.oci", "oci.image", "opencontainers", "container.image",
|
||
):
|
||
a.Type = ArtifactTypeImage
|
||
case strings.Contains(lowerMediaType, "image") || strings.Contains(lowerMediaType, "manifest") || strings.Contains(lowerMediaType, "container"):
|
||
a.Type = ArtifactTypeImage
|
||
default:
|
||
a.Type = ArtifactTypeOther
|
||
}
|
||
}
|
||
|
||
// DetermineType 智能判断 Artifact 类型(综合多种信息)
|
||
// 优先级:
|
||
// 1. ConfigType (config.mediaType) - 最准确
|
||
// 2. Annotations - 可能包含类型标注
|
||
// 3. Repository 名称 - charts/ 前缀暗示
|
||
// 4. MediaType - 兜底判断
|
||
func (a *Artifact) DetermineType() {
|
||
containsAny := func(target string, keywords ...string) bool {
|
||
for _, keyword := range keywords {
|
||
if keyword != "" && strings.Contains(target, keyword) {
|
||
return true
|
||
}
|
||
}
|
||
return false
|
||
}
|
||
|
||
// 1. 优先检查 ConfigType(最准确的判断方式)
|
||
if a.ConfigType != "" {
|
||
lowerConfigType := strings.ToLower(strings.TrimSpace(a.ConfigType))
|
||
|
||
// Helm Chart 的 config.mediaType
|
||
if containsAny(lowerConfigType,
|
||
"helm.config", "cncf.helm", "helm.chart", "chart.content",
|
||
) {
|
||
a.Type = ArtifactTypeChart
|
||
return
|
||
}
|
||
|
||
// Docker/OCI Image 的 config.mediaType
|
||
if containsAny(lowerConfigType,
|
||
"docker.container.image", "oci.image.config",
|
||
) {
|
||
a.Type = ArtifactTypeImage
|
||
return
|
||
}
|
||
}
|
||
|
||
// 2. 检查 Annotations
|
||
for key, value := range a.Annotations {
|
||
lowerKey := strings.ToLower(key)
|
||
lowerValue := strings.ToLower(value)
|
||
|
||
if containsAny(lowerKey, "helm", "chart") ||
|
||
containsAny(lowerValue, "helm", "chart") {
|
||
a.Type = ArtifactTypeChart
|
||
return
|
||
}
|
||
}
|
||
|
||
// 3. 检查 Repository 名称(辅助判断)
|
||
if strings.HasPrefix(strings.ToLower(a.Repository), "charts/") {
|
||
// charts/ 开头的仓库很可能是 Helm Chart
|
||
// 但需要结合 MediaType 进一步确认
|
||
lowerMediaType := strings.ToLower(strings.TrimSpace(a.MediaType))
|
||
|
||
// 如果是 OCI manifest 格式,很可能是以 OCI 格式存储的 Helm Chart
|
||
if strings.Contains(lowerMediaType, "oci.image.manifest") ||
|
||
strings.Contains(lowerMediaType, "vnd.oci") {
|
||
a.Type = ArtifactTypeChart
|
||
return
|
||
}
|
||
}
|
||
|
||
// 4. 回退到基于 MediaType 的判断(兜底逻辑)
|
||
lowerMediaType := strings.ToLower(strings.TrimSpace(a.MediaType))
|
||
|
||
switch {
|
||
case lowerMediaType == "":
|
||
a.Type = ArtifactTypeOther
|
||
case containsAny(lowerMediaType,
|
||
"helm", "cncf.helm", "helm.chart", "helm+", "chart+json", "chart.v1", "helm-package", "helm.config",
|
||
):
|
||
a.Type = ArtifactTypeChart
|
||
case containsAny(lowerMediaType,
|
||
"docker", "vnd.docker", "docker.distribution", "docker.container.image",
|
||
):
|
||
a.Type = ArtifactTypeImage
|
||
case strings.Contains(lowerMediaType, "image") || strings.Contains(lowerMediaType, "manifest"):
|
||
a.Type = ArtifactTypeImage
|
||
default:
|
||
a.Type = ArtifactTypeOther
|
||
}
|
||
}
|
||
|
||
// IsChart 判断是否为 Helm Chart
|
||
func (a *Artifact) IsChart() bool {
|
||
return a.Type == ArtifactTypeChart
|
||
}
|
||
|