Files
any-hub/internal/hubmodule/docker/hooks.go

132 lines
3.3 KiB
Go

package docker
import (
"net"
"strings"
"github.com/any-hub/any-hub/internal/proxy/hooks"
)
func init() {
hooks.MustRegister("docker", hooks.Hooks{
NormalizePath: normalizePath,
CachePolicy: cachePolicy,
ContentType: contentType,
})
}
func normalizePath(ctx *hooks.RequestContext, clean string, rawQuery []byte) (string, []byte) {
if ctx == nil || !isDockerHubHost(ctx.UpstreamHost) {
return clean, rawQuery
}
repo, rest, ok := splitDockerRepoPath(clean)
if !ok || repo == "" || strings.Contains(repo, "/") || repo == "library" {
return clean, rawQuery
}
return "/v2/library/" + repo + rest, rawQuery
}
func cachePolicy(_ *hooks.RequestContext, locatorPath string, current hooks.CachePolicy) hooks.CachePolicy {
clean := locatorPath
if clean == "/v2" || clean == "v2" || clean == "/v2/" {
return hooks.CachePolicy{}
}
if strings.Contains(clean, "/_catalog") {
return hooks.CachePolicy{}
}
if isDockerImmutablePath(clean) {
current.AllowCache = true
current.AllowStore = true
current.RequireRevalidate = false
return current
}
current.AllowCache = true
current.AllowStore = true
current.RequireRevalidate = true
return current
}
func contentType(_ *hooks.RequestContext, locatorPath string) string {
switch {
case strings.Contains(locatorPath, "/tags/list"):
return "application/json"
case strings.Contains(locatorPath, "/blobs/"):
return "application/octet-stream"
default:
return ""
}
}
func isDockerHubHost(host string) bool {
if parsedHost, _, err := net.SplitHostPort(host); err == nil {
host = parsedHost
}
switch strings.ToLower(host) {
case "registry-1.docker.io", "docker.io", "index.docker.io":
return true
default:
return false
}
}
func isRegistryK8sHost(host string) bool {
if parsedHost, _, err := net.SplitHostPort(host); err == nil {
host = parsedHost
}
return strings.EqualFold(host, "registry.k8s.io")
}
func manifestFallbackPath(ctx *hooks.RequestContext, clean string) (string, bool) {
if ctx == nil || !isRegistryK8sHost(ctx.UpstreamHost) {
return "", false
}
repo, rest, ok := splitDockerRepoPath(clean)
if !ok || strings.Contains(repo, "/") || !strings.HasPrefix(rest, "/manifests/") {
return "", false
}
return "/v2/" + repo + "/" + repo + rest, true
}
func RegistryK8sManifestFallbackPath(ctx *hooks.RequestContext, clean string) (string, bool) {
return manifestFallbackPath(ctx, clean)
}
func splitDockerRepoPath(path string) (string, string, bool) {
if !strings.HasPrefix(path, "/v2/") {
return "", "", false
}
suffix := strings.TrimPrefix(path, "/v2/")
if suffix == "" || suffix == "/" {
return "", "", false
}
segments := strings.Split(suffix, "/")
var repoSegments []string
for i, seg := range segments {
if seg == "" {
return "", "", false
}
switch seg {
case "manifests", "blobs", "tags", "referrers":
if len(repoSegments) == 0 {
return "", "", false
}
rest := "/" + strings.Join(segments[i:], "/")
return strings.Join(repoSegments, "/"), rest, true
case "_catalog":
return "", "", false
}
repoSegments = append(repoSegments, seg)
}
return "", "", false
}
func isDockerImmutablePath(path string) bool {
if strings.Contains(path, "/blobs/sha256:") {
return true
}
if strings.Contains(path, "/manifests/sha256:") {
return true
}
return false
}