prvng_platform/oci-registry/scripts/migrate-registry.nu
2025-10-07 10:59:52 +01:00

327 lines
8.9 KiB
Plaintext
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env nu
# Migrate images between registries
export def main [
--source-registry: string = "localhost:5000"
--source-type: string = "zot"
--dest-registry: string = "localhost:5001"
--dest-type: string = "harbor"
--namespaces: list<string> = []
--dry-run
--use-skopeo
] {
print "🔄 Migrating registry images..."
print $" Source: ($source_registry) \(($source_type)\)"
print $" Destination: ($dest_registry) \(($dest_type)\)"
if $dry_run {
print " Mode: DRY RUN (no actual migration)"
}
print ""
# Get source catalog
print "📋 Fetching source catalog..."
let source_repos = get-registry-repos $source_registry
if ($source_repos | is-empty) {
print "❌ No repositories found in source registry"
return
}
print $" Found ($source_repos | length) repositories"
# Filter by namespaces if specified
let repos_to_migrate = if ($namespaces | is-empty) {
$source_repos
} else {
$source_repos | where { |repo|
let ns = ($repo | split row "/" | first)
$ns in $namespaces
}
}
print $" Will migrate ($repos_to_migrate | length) repositories\n"
# Migrate each repository
mut success = 0
mut failed = 0
for repo in $repos_to_migrate {
print $"Migrating: ($repo)"
# Get tags for repository
let tags = get-repo-tags $source_registry $repo
if ($tags | is-empty) {
print " ⚠️ No tags found, skipping"
continue
}
print $" Tags: ($tags | str join ', ')"
# Migrate each tag
for tag in $tags {
let source_image = $"($source_registry)/($repo):($tag)"
let dest_image = $"($dest_registry)/($repo):($tag)"
if $dry_run {
print $" [DRY RUN] Would migrate: ($source_image) → ($dest_image)"
$success = ($success + 1)
} else {
if $use_skopeo {
# Use skopeo for migration
if (migrate-with-skopeo $source_image $dest_image) {
print $" ✓ Migrated: ($tag)"
$success = ($success + 1)
} else {
print $" ✗ Failed: ($tag)"
$failed = ($failed + 1)
}
} else {
# Use Docker for migration
if (migrate-with-docker $source_image $dest_image) {
print $" ✓ Migrated: ($tag)"
$success = ($success + 1)
} else {
print $" ✗ Failed: ($tag)"
$failed = ($failed + 1)
}
}
}
}
print ""
}
# Summary
print "=" * 60
print $"Migration Summary:"
print $" Successfully migrated: ($success) images"
print $" Failed: ($failed) images"
if $dry_run {
print "\n This was a dry run. Use without --dry-run to actually migrate."
}
if $failed > 0 {
print "\n⚠ Some migrations failed. Check logs above."
} else {
print "\n✅ All migrations completed successfully!"
}
}
def get-registry-repos [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
}
def get-repo-tags [registry_url: string, repo: string] -> list {
let result = (do {
http get $"http://($registry_url)/v2/($repo)/tags/list"
} | complete)
if $result.exit_code != 0 {
return []
}
let tags_data = ($result.stdout | from json)
$tags_data.tags | default []
}
def migrate-with-docker [source: string, dest: string] -> bool {
# Pull from source
let pull_result = (^docker pull $source | complete)
if $pull_result.exit_code != 0 {
print $" Error pulling: ($pull_result.stderr)"
return false
}
# Tag for destination
let tag_result = (^docker tag $source $dest | complete)
if $tag_result.exit_code != 0 {
print $" Error tagging: ($tag_result.stderr)"
return false
}
# Push to destination
let push_result = (^docker push $dest | complete)
if $push_result.exit_code != 0 {
print $" Error pushing: ($push_result.stderr)"
return false
}
# Cleanup local image
^docker rmi $source | ignore
^docker rmi $dest | ignore
return true
}
def migrate-with-skopeo [source: string, dest: string] -> bool {
# Check if skopeo is available
let skopeo_check = (^skopeo --version | complete)
if $skopeo_check.exit_code != 0 {
print " Error: skopeo not found"
return false
}
# Use skopeo copy
let copy_result = (^skopeo copy \
$"docker://($source)" \
$"docker://($dest)" \
--insecure-policy | complete)
if $copy_result.exit_code != 0 {
print $" Error: ($copy_result.stderr)"
return false
}
return true
}
# Sync specific namespace
export def "sync namespace" [
namespace: string
--source-registry: string = "localhost:5000"
--dest-registry: string = "localhost:5001"
--dry-run
] {
main --source-registry $source_registry \
--dest-registry $dest_registry \
--namespaces [$namespace] \
--dry-run=$dry_run
}
# Sync all namespaces
export def "sync all" [
--source-registry: string = "localhost:5000"
--dest-registry: string = "localhost:5001"
--dry-run
] {
let namespaces = [
"provisioning-extensions"
"provisioning-kcl"
"provisioning-platform"
"provisioning-test"
]
for ns in $namespaces {
print $"\n🔄 Syncing namespace: ($ns)"
sync namespace $ns --source-registry $source_registry \
--dest-registry $dest_registry \
--dry-run=$dry_run
}
}
# Backup registry to tar files
export def "backup to-tar" [
--registry-url: string = "localhost:5000"
--output-dir: string = "./registry-backup"
--namespaces: list<string> = []
] {
print "💾 Backing up registry to tar files..."
print $" Registry: ($registry_url)"
print $" Output: ($output_dir)\n"
# Create output directory
mkdir $output_dir
# Get repositories
let repos = get-registry-repos $registry_url
let repos_to_backup = if ($namespaces | is-empty) {
$repos
} else {
$repos | where { |repo|
let ns = ($repo | split row "/" | first)
$ns in $namespaces
}
}
print $"Backing up ($repos_to_backup | length) repositories...\n"
for repo in $repos_to_backup {
print $"Backing up: ($repo)"
let tags = get-repo-tags $registry_url $repo
for tag in $tags {
let image = $"($registry_url)/($repo):($tag)"
let filename = $"($output_dir)/($repo | str replace '/' '_')_($tag).tar"
# Pull image
^docker pull $image | ignore
# Save to tar
let save_result = (^docker save -o $filename $image | complete)
if $save_result.exit_code == 0 {
print $" ✓ Saved: ($filename)"
} else {
print $" ✗ Failed: ($tag)"
}
}
}
print "\n✅ Backup complete!"
}
# Restore registry from tar files
export def "restore from-tar" [
--input-dir: string = "./registry-backup"
--registry-url: string = "localhost:5000"
] {
print "📥 Restoring registry from tar files..."
print $" Input: ($input_dir)"
print $" Registry: ($registry_url)\n"
# Find all tar files
let tar_files = (ls $"($input_dir)/*.tar" | get name)
print $"Found ($tar_files | length) tar files\n"
for tar_file in $tar_files {
print $"Restoring: ($tar_file)"
# Load from tar
let load_result = (^docker load -i $tar_file | complete)
if $load_result.exit_code != 0 {
print $" ✗ Failed to load"
continue
}
# Extract image name from docker load output
# Format: "Loaded image: localhost:5000/repo:tag"
let image = ($load_result.stdout | str trim | split row ": " | last)
# Retag if needed and push
let target_image = $"($registry_url)/($image | split row '/' | skip 1 | str join '/')"
^docker tag $image $target_image | ignore
let push_result = (^docker push $target_image | complete)
if $push_result.exit_code == 0 {
print $" ✓ Restored and pushed"
} else {
print $" ✗ Failed to push"
}
# Cleanup
^docker rmi $image | ignore
^docker rmi $target_image | ignore
}
print "\n✅ Restore complete!"
}