prvng_platform/oci-registry/scripts/migrate-registry.nu

327 lines
8.9 KiB
Plaintext
Raw Permalink Normal View History

2025-10-07 10:59:52 +01:00
#!/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!"
}