feat: complete OCI registry implementation with docker push/pull support
A lightweight OCI (Open Container Initiative) registry implementation written in Go.
This commit is contained in:
115
internal/module/registry/registry.go
Normal file
115
internal/module/registry/registry.go
Normal file
@@ -0,0 +1,115 @@
|
||||
package registry
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log"
|
||||
"strings"
|
||||
|
||||
"gitea.loveuer.com/loveuer/cluster/internal/model"
|
||||
"gitea.loveuer.com/loveuer/cluster/pkg/resp"
|
||||
"gitea.loveuer.com/loveuer/cluster/pkg/store"
|
||||
"github.com/gofiber/fiber/v3"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
func Registry(ctx context.Context, db *gorm.DB, store store.Store) fiber.Handler {
|
||||
// ???????
|
||||
if err := db.AutoMigrate(
|
||||
&model.Repository{},
|
||||
&model.Blob{},
|
||||
&model.Manifest{},
|
||||
&model.Tag{},
|
||||
&model.BlobUpload{},
|
||||
); err != nil {
|
||||
log.Fatalf("failed to migrate database: %v", err)
|
||||
}
|
||||
|
||||
if err := store.CreatePartition(ctx, "registry"); err != nil {
|
||||
log.Fatalf("failed to create registry partition: %v", err)
|
||||
}
|
||||
|
||||
return func(c fiber.Ctx) error {
|
||||
if isBlob(c) {
|
||||
return HandleBlobs(c, db, store)
|
||||
}
|
||||
|
||||
if isManifest(c) {
|
||||
return HandleManifest(c, db, store)
|
||||
}
|
||||
|
||||
if isTags(c) {
|
||||
return HandleTags(c, db, store)
|
||||
}
|
||||
|
||||
if isCatalog(c) {
|
||||
return HandleCatalog(c, db, store)
|
||||
}
|
||||
|
||||
if isReferrers(c) {
|
||||
return HandleReferrers(c, db, store)
|
||||
}
|
||||
|
||||
// Handle root v2 endpoint
|
||||
if c.Path() == "/v2/" {
|
||||
c.Set("Docker-Distribution-API-Version", "registry/2.0")
|
||||
return c.SendStatus(200)
|
||||
}
|
||||
|
||||
c.Set("Docker-Distribution-API-Version", "registry/2.0")
|
||||
|
||||
log.Printf("[Warn] Registry: unknown endpoint - path = %s, method = %s, headers = %v", c.Path(), c.Method(), &c.Request().Header)
|
||||
|
||||
return resp.R404(c, "UNKNOWN_ENDPOINT", nil, "endpoint not found")
|
||||
}
|
||||
}
|
||||
|
||||
func isBlob(c fiber.Ctx) bool {
|
||||
elem := strings.Split(c.Path(), "/")
|
||||
elem = elem[1:]
|
||||
if elem[len(elem)-1] == "" {
|
||||
elem = elem[:len(elem)-1]
|
||||
}
|
||||
if len(elem) < 3 {
|
||||
return false
|
||||
}
|
||||
return elem[len(elem)-2] == "blobs" || (elem[len(elem)-3] == "blobs" &&
|
||||
elem[len(elem)-2] == "uploads")
|
||||
}
|
||||
|
||||
func isManifest(c fiber.Ctx) bool {
|
||||
elems := strings.Split(c.Path(), "/")
|
||||
elems = elems[1:]
|
||||
if len(elems) < 4 {
|
||||
return false
|
||||
}
|
||||
return elems[len(elems)-2] == "manifests"
|
||||
}
|
||||
|
||||
func isTags(c fiber.Ctx) bool {
|
||||
elems := strings.Split(c.Path(), "/")
|
||||
elems = elems[1:]
|
||||
if len(elems) < 4 {
|
||||
return false
|
||||
}
|
||||
return elems[len(elems)-2] == "tags"
|
||||
}
|
||||
|
||||
func isCatalog(c fiber.Ctx) bool {
|
||||
elems := strings.Split(c.Path(), "/")
|
||||
elems = elems[1:]
|
||||
if len(elems) < 2 {
|
||||
return false
|
||||
}
|
||||
|
||||
return elems[len(elems)-1] == "_catalog"
|
||||
}
|
||||
|
||||
func isReferrers(c fiber.Ctx) bool {
|
||||
elems := strings.Split(c.Path(), "/")
|
||||
elems = elems[1:]
|
||||
if len(elems) < 4 {
|
||||
return false
|
||||
}
|
||||
|
||||
return elems[len(elems)-2] == "referrers"
|
||||
}
|
||||
Reference in New Issue
Block a user