Files
cluster/internal/model/registry.go
2025-11-09 15:19:11 +08:00

163 lines
5.6 KiB
Go

package model
import (
"crypto/sha256"
"encoding/hex"
"fmt"
"io"
"path/filepath"
"strings"
"time"
"gorm.io/gorm"
)
// Hash represents a content hash
type Hash string
// NewHash creates a new Hash from a string
func NewHash(s string) (Hash, error) {
if !strings.HasPrefix(s, "sha256:") {
return "", fmt.Errorf("invalid hash format: %s", s)
}
return Hash(s), nil
}
// String returns the string representation of the hash
func (h Hash) String() string {
return string(h)
}
// Hex returns the hex part of the hash
func (h Hash) Hex() (string, error) {
if !strings.HasPrefix(string(h), "sha256:") {
return "", fmt.Errorf("invalid hash format: %s", h)
}
return strings.TrimPrefix(string(h), "sha256:"), nil
}
// SHA256 computes the SHA256 hash of the given reader
func SHA256(r io.Reader) (Hash, int64, error) {
hasher := sha256.New()
n, err := io.Copy(hasher, r)
if err != nil {
return "", 0, err
}
return Hash("sha256:" + hex.EncodeToString(hasher.Sum(nil))), n, nil
}
// RegistryRepository represents a repository in the registry
type RegistryRepository struct {
ID uint `gorm:"primaryKey" json:"id"`
Name string `gorm:"uniqueIndex;not null" json:"name"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
DeletedAt gorm.DeletedAt `gorm:"index" json:"-"`
}
// RegistryManifest represents a manifest in the registry
type RegistryManifest struct {
ID uint `gorm:"primaryKey" json:"id"`
RepositoryID uint `gorm:"not null;index;uniqueIndex:idx_repo_digest" json:"repository_id"`
Repository RegistryRepository `gorm:"foreignKey:RepositoryID" json:"repository"`
Digest string `gorm:"not null;uniqueIndex:idx_repo_digest" json:"digest"`
Tag string `gorm:"index" json:"tag"`
ContentType string `gorm:"not null" json:"content_type"`
Size int64 `gorm:"not null" json:"size"`
Blob []byte `gorm:"type:blob" json:"blob"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
DeletedAt gorm.DeletedAt `gorm:"index" json:"-"`
}
// RegistryBlob represents a blob in the registry
type RegistryBlob struct {
ID uint `gorm:"primaryKey" json:"id"`
Digest string `gorm:"uniqueIndex;not null" json:"digest"`
Size int64 `gorm:"not null" json:"size"`
Path string `gorm:"not null" json:"path"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
DeletedAt gorm.DeletedAt `gorm:"index" json:"-"`
}
// RegistryTag represents a tag in the registry
type RegistryTag struct {
ID uint `gorm:"primaryKey" json:"id"`
RepositoryID uint `gorm:"not null;index" json:"repository_id"`
Repository RegistryRepository `gorm:"foreignKey:RepositoryID" json:"repository"`
Name string `gorm:"not null;index" json:"name"`
ManifestID uint `gorm:"not null;index" json:"manifest_id"`
Manifest RegistryManifest `gorm:"foreignKey:ManifestID" json:"manifest"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
DeletedAt gorm.DeletedAt `gorm:"index" json:"-"`
}
// RegistryUploadSession represents an upload session
type RegistryUploadSession struct {
ID uint `gorm:"primaryKey" json:"id"`
RepositoryID uint `gorm:"not null;index" json:"repository_id"`
Repository RegistryRepository `gorm:"foreignKey:RepositoryID" json:"repository"`
SessionID string `gorm:"uniqueIndex;not null" json:"session_id"`
Path string `gorm:"not null" json:"path"`
Size int64 `gorm:"not null" json:"size"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
DeletedAt gorm.DeletedAt `gorm:"index" json:"-"`
}
// Tag represents a tag list response
type Tag struct {
Name string `json:"name"`
Tags []string `json:"tags"`
}
// Catalog represents a catalog response
type Catalog struct {
Repositories []string `json:"repositories"`
}
// RepoSimpleManifest represents a simple manifest
type RepoSimpleManifest struct {
Blob []byte `json:"blob"`
ContentType string `json:"content_type"`
}
// IndexManifest represents an index manifest
type IndexManifest struct {
SchemaVersion int `json:"schemaVersion"`
MediaType string `json:"mediaType"`
Manifests []IndexManifestEntry `json:"manifests"`
}
// IndexManifestEntry represents an entry in an index manifest
type IndexManifestEntry struct {
MediaType string `json:"mediaType"`
Size int64 `json:"size"`
Digest string `json:"digest"`
Platform *Platform `json:"platform,omitempty"`
Annotations map[string]string `json:"annotations,omitempty"`
}
// Platform represents a platform specification
type Platform struct {
Architecture string `json:"architecture"`
OS string `json:"os"`
Variant string `json:"variant,omitempty"`
}
// GetBlobPath returns the file path for a blob
func GetBlobPath(baseDir, digest string) string {
hash := strings.TrimPrefix(digest, "sha256:")
if len(hash) < 4 {
return filepath.Join(baseDir, "registry", "blobs", "sha256", hash)
}
return filepath.Join(baseDir, "registry", "blobs", "sha256", hash[:2], hash[2:4], hash)
}
// GetUploadPath returns the file path for an upload session
func GetUploadPath(baseDir, sessionID string) string {
return filepath.Join(baseDir, "registry", "uploads", sessionID)
}