333 lines
8.4 KiB
Text
333 lines
8.4 KiB
Text
|
|
# OCI Registry Service Integration
|
|||
|
|
|
|||
|
|
use ../config/loader.nu get-config
|
|||
|
|
|
|||
|
|
# Start registry service
|
|||
|
|
export def start-oci-registry [
|
|||
|
|
registry_type: string = "zot"
|
|||
|
|
config_path?: string
|
|||
|
|
] -> bool {
|
|||
|
|
print $"Starting OCI registry service \(($registry_type)\)..."
|
|||
|
|
|
|||
|
|
let registry_dir = get-registry-directory $registry_type
|
|||
|
|
|
|||
|
|
if not ($registry_dir | path exists) {
|
|||
|
|
print $"ERROR: Registry directory not found: ($registry_dir)"
|
|||
|
|
return false
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
cd $registry_dir
|
|||
|
|
|
|||
|
|
let compose_file = if ($config_path | is-empty) {
|
|||
|
|
"docker-compose.yml"
|
|||
|
|
} else {
|
|||
|
|
$config_path
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
let result = (^docker-compose -f $compose_file up -d | complete)
|
|||
|
|
|
|||
|
|
if $result.exit_code == 0 {
|
|||
|
|
print "✅ Registry service started"
|
|||
|
|
return true
|
|||
|
|
} else {
|
|||
|
|
print $"ERROR: Failed to start registry: ($result.stderr)"
|
|||
|
|
return false
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
# Stop registry service
|
|||
|
|
export def stop-oci-registry [
|
|||
|
|
registry_type: string = "zot"
|
|||
|
|
] -> bool {
|
|||
|
|
print $"Stopping OCI registry service \(($registry_type)\)..."
|
|||
|
|
|
|||
|
|
let registry_dir = get-registry-directory $registry_type
|
|||
|
|
|
|||
|
|
if not ($registry_dir | path exists) {
|
|||
|
|
print $"ERROR: Registry directory not found: ($registry_dir)"
|
|||
|
|
return false
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
cd $registry_dir
|
|||
|
|
|
|||
|
|
let result = (^docker-compose down | complete)
|
|||
|
|
|
|||
|
|
if $result.exit_code == 0 {
|
|||
|
|
print "✅ Registry service stopped"
|
|||
|
|
return true
|
|||
|
|
} else {
|
|||
|
|
print $"ERROR: Failed to stop registry: ($result.stderr)"
|
|||
|
|
return false
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
# Get registry status
|
|||
|
|
export def get-oci-registry-status [
|
|||
|
|
registry_type: string = "zot"
|
|||
|
|
] -> record {
|
|||
|
|
let registry_dir = get-registry-directory $registry_type
|
|||
|
|
|
|||
|
|
if not ($registry_dir | path exists) {
|
|||
|
|
return {
|
|||
|
|
running: false
|
|||
|
|
error: "Registry directory not found"
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
cd $registry_dir
|
|||
|
|
|
|||
|
|
let result = (^docker-compose ps --format json | complete)
|
|||
|
|
|
|||
|
|
if $result.exit_code != 0 {
|
|||
|
|
return {
|
|||
|
|
running: false
|
|||
|
|
error: $result.stderr
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
let containers = ($result.stdout | from json)
|
|||
|
|
let running = ($containers | all { |c| $c.State == "running" })
|
|||
|
|
|
|||
|
|
{
|
|||
|
|
running: $running
|
|||
|
|
containers: $containers
|
|||
|
|
type: $registry_type
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
# Check registry health
|
|||
|
|
export def check-oci-registry-health [
|
|||
|
|
registry_url: string
|
|||
|
|
] -> bool {
|
|||
|
|
let result = (do {
|
|||
|
|
http get $"http://($registry_url)/v2/" --timeout 5sec
|
|||
|
|
} | complete)
|
|||
|
|
|
|||
|
|
$result.exit_code == 0
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
# Get registry info
|
|||
|
|
export def get-oci-registry-info [
|
|||
|
|
registry_url: string
|
|||
|
|
] -> record {
|
|||
|
|
# Try to fetch catalog
|
|||
|
|
let catalog_result = (do {
|
|||
|
|
http get $"http://($registry_url)/v2/_catalog"
|
|||
|
|
} | complete)
|
|||
|
|
|
|||
|
|
let catalog = if $catalog_result.exit_code == 0 {
|
|||
|
|
$catalog_result.stdout | from json
|
|||
|
|
} else {
|
|||
|
|
{repositories: []}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
# Count namespaces
|
|||
|
|
let namespaces = ($catalog.repositories
|
|||
|
|
| each { |r| $r | split row "/" | first }
|
|||
|
|
| uniq
|
|||
|
|
| length)
|
|||
|
|
|
|||
|
|
{
|
|||
|
|
url: $registry_url
|
|||
|
|
healthy: ($catalog_result.exit_code == 0)
|
|||
|
|
repositories: ($catalog.repositories | length)
|
|||
|
|
namespaces: $namespaces
|
|||
|
|
timestamp: (date now | format date "%Y-%m-%d %H:%M:%S")
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
# Get repository list
|
|||
|
|
export def get-oci-registry-repositories [
|
|||
|
|
registry_url: string
|
|||
|
|
] -> list {
|
|||
|
|
let result = (do {
|
|||
|
|
http get $"http://($registry_url)/v2/_catalog"
|
|||
|
|
} | complete)
|
|||
|
|
|
|||
|
|
if $result.exit_code != 0 {
|
|||
|
|
return []
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
let catalog = ($result.stdout | from json)
|
|||
|
|
$catalog.repositories
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
# Get repository tags
|
|||
|
|
export def get-oci-repository-tags [
|
|||
|
|
registry_url: string
|
|||
|
|
repository: string
|
|||
|
|
] -> list {
|
|||
|
|
let result = (do {
|
|||
|
|
http get $"http://($registry_url)/v2/($repository)/tags/list"
|
|||
|
|
} | complete)
|
|||
|
|
|
|||
|
|
if $result.exit_code != 0 {
|
|||
|
|
return []
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
let tags = ($result.stdout | from json)
|
|||
|
|
$tags.tags | default []
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
# Get manifest for image
|
|||
|
|
export def get-oci-image-manifest [
|
|||
|
|
registry_url: string
|
|||
|
|
repository: string
|
|||
|
|
tag: string
|
|||
|
|
] -> record {
|
|||
|
|
let result = (do {
|
|||
|
|
http get $"http://($registry_url)/v2/($repository)/manifests/($tag)" --headers {
|
|||
|
|
"Accept": "application/vnd.oci.image.manifest.v1+json"
|
|||
|
|
}
|
|||
|
|
} | complete)
|
|||
|
|
|
|||
|
|
if $result.exit_code != 0 {
|
|||
|
|
return {}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
$result.stdout | from json
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
# Delete image tag
|
|||
|
|
export def delete-oci-image-tag [
|
|||
|
|
registry_url: string
|
|||
|
|
repository: string
|
|||
|
|
tag: string
|
|||
|
|
] -> bool {
|
|||
|
|
# First get the manifest digest
|
|||
|
|
let manifest_result = (do {
|
|||
|
|
http get $"http://($registry_url)/v2/($repository)/manifests/($tag)" --headers {
|
|||
|
|
"Accept": "application/vnd.oci.image.manifest.v1+json"
|
|||
|
|
} --full
|
|||
|
|
} | complete)
|
|||
|
|
|
|||
|
|
if $manifest_result.exit_code != 0 {
|
|||
|
|
print $"ERROR: Failed to get manifest: ($manifest_result.stderr)"
|
|||
|
|
return false
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
let manifest = ($manifest_result.stdout | from json)
|
|||
|
|
let digest = ($manifest.headers | where name == "Docker-Content-Digest" | first | get value)
|
|||
|
|
|
|||
|
|
# Delete using digest
|
|||
|
|
let delete_result = (do {
|
|||
|
|
http delete $"http://($registry_url)/v2/($repository)/manifests/($digest)"
|
|||
|
|
} | complete)
|
|||
|
|
|
|||
|
|
if $delete_result.exit_code == 0 {
|
|||
|
|
print $"✅ Image ($repository):($tag) deleted"
|
|||
|
|
return true
|
|||
|
|
} else {
|
|||
|
|
print $"ERROR: Failed to delete image: ($delete_result.stderr)"
|
|||
|
|
return false
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
# Garbage collection (Zot)
|
|||
|
|
export def run-oci-registry-gc [
|
|||
|
|
registry_type: string = "zot"
|
|||
|
|
] -> bool {
|
|||
|
|
match $registry_type {
|
|||
|
|
"zot" => {
|
|||
|
|
# Zot runs GC automatically based on config
|
|||
|
|
print "ℹ Zot runs garbage collection automatically"
|
|||
|
|
print " Check config.json for GC settings"
|
|||
|
|
return true
|
|||
|
|
}
|
|||
|
|
"distribution" => {
|
|||
|
|
# Distribution requires manual GC
|
|||
|
|
print "Running garbage collection..."
|
|||
|
|
|
|||
|
|
let registry_dir = get-registry-directory $registry_type
|
|||
|
|
|
|||
|
|
let result = (^docker-compose exec registry \
|
|||
|
|
registry garbage-collect /etc/docker/registry/config.yml | complete)
|
|||
|
|
|
|||
|
|
if $result.exit_code == 0 {
|
|||
|
|
print "✅ Garbage collection completed"
|
|||
|
|
return true
|
|||
|
|
} else {
|
|||
|
|
print $"ERROR: GC failed: ($result.stderr)"
|
|||
|
|
return false
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
"harbor" => {
|
|||
|
|
print "ℹ Harbor runs GC via UI or API"
|
|||
|
|
print " Use Harbor admin interface for GC"
|
|||
|
|
return true
|
|||
|
|
}
|
|||
|
|
_ => {
|
|||
|
|
print $"ERROR: Unknown registry type: ($registry_type)"
|
|||
|
|
return false
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
# Get registry metrics (if available)
|
|||
|
|
export def get-oci-registry-metrics [
|
|||
|
|
registry_url: string
|
|||
|
|
registry_type: string = "zot"
|
|||
|
|
] -> string {
|
|||
|
|
let metrics_url = match $registry_type {
|
|||
|
|
"zot" => $"http://($registry_url)/metrics"
|
|||
|
|
"distribution" => $"http://($registry_url):5001/metrics"
|
|||
|
|
_ => null
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if ($metrics_url | is-empty) {
|
|||
|
|
return "Metrics not available for this registry type"
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
let result = (do {
|
|||
|
|
http get $metrics_url
|
|||
|
|
} | complete)
|
|||
|
|
|
|||
|
|
if $result.exit_code == 0 {
|
|||
|
|
$result.stdout
|
|||
|
|
} else {
|
|||
|
|
$"ERROR: Failed to fetch metrics: ($result.stderr)"
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
# Helper: Get registry directory
|
|||
|
|
def get-registry-directory [registry_type: string] -> string {
|
|||
|
|
let config = (get-config)
|
|||
|
|
let base_path = ($config | get paths.base)
|
|||
|
|
$"($base_path)/provisioning/platform/oci-registry/($registry_type)"
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
# Push artifact to registry
|
|||
|
|
export def push-oci-artifact [
|
|||
|
|
registry_url: string
|
|||
|
|
repository: string
|
|||
|
|
tag: string
|
|||
|
|
artifact_path: string
|
|||
|
|
] -> bool {
|
|||
|
|
if not ($artifact_path | path exists) {
|
|||
|
|
print $"ERROR: Artifact not found: ($artifact_path)"
|
|||
|
|
return false
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
# This would require oras or docker buildx
|
|||
|
|
print "ℹ Use 'oras push' or 'docker push' to upload artifacts"
|
|||
|
|
print $" Target: ($registry_url)/($repository):($tag)"
|
|||
|
|
print $" Artifact: ($artifact_path)"
|
|||
|
|
|
|||
|
|
false
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
# Pull artifact from registry
|
|||
|
|
export def pull-oci-artifact [
|
|||
|
|
registry_url: string
|
|||
|
|
repository: string
|
|||
|
|
tag: string
|
|||
|
|
output_path: string
|
|||
|
|
] -> bool {
|
|||
|
|
# This would require oras or docker pull
|
|||
|
|
print "ℹ Use 'oras pull' or 'docker pull' to download artifacts"
|
|||
|
|
print $" Source: ($registry_url)/($repository):($tag)"
|
|||
|
|
print $" Output: ($output_path)"
|
|||
|
|
|
|||
|
|
false
|
|||
|
|
}
|