370 lines
9.2 KiB
Text
370 lines
9.2 KiB
Text
# OCI Registry Management Commands
|
||
|
||
use ../config/loader.nu get-config
|
||
|
||
# Start OCI registry
|
||
export def "oci-registry start" [
|
||
--type: string = "zot" # zot, harbor, distribution
|
||
--config: string
|
||
--detach (-d)
|
||
] {
|
||
print $"🚀 Starting OCI registry \(($type)\)..."
|
||
|
||
let registry_path = get-registry-path $type
|
||
|
||
if not ($registry_path | path exists) {
|
||
error make {
|
||
msg: $"Registry path not found: ($registry_path)"
|
||
}
|
||
}
|
||
|
||
cd $registry_path
|
||
|
||
let compose_file = if ($config | is-empty) {
|
||
"docker-compose.yml"
|
||
} else {
|
||
$config
|
||
}
|
||
|
||
let flags = if $detach { ["-d"] } else { [] }
|
||
|
||
^docker-compose -f $compose_file up ...$flags
|
||
|
||
if $detach {
|
||
print "✅ Registry started in background"
|
||
print $" Use 'oci-registry logs --type ($type)' to view logs"
|
||
print $" Use 'oci-registry status --type ($type)' to check status"
|
||
}
|
||
}
|
||
|
||
# Stop OCI registry
|
||
export def "oci-registry stop" [
|
||
--type: string = "zot"
|
||
--remove-volumes (-v)
|
||
] {
|
||
print $"🛑 Stopping OCI registry \(($type)\)..."
|
||
|
||
let registry_path = get-registry-path $type
|
||
|
||
cd $registry_path
|
||
|
||
let flags = if $remove_volumes { ["-v"] } else { [] }
|
||
|
||
^docker-compose down ...$flags
|
||
|
||
print "✅ Registry stopped"
|
||
}
|
||
|
||
# Get registry status
|
||
export def "oci-registry status" [
|
||
--type: string = "zot"
|
||
--format: string = "table" # table, json
|
||
] -> any {
|
||
let registry_path = get-registry-path $type
|
||
|
||
cd $registry_path
|
||
|
||
let containers = (^docker-compose ps --format json | from json)
|
||
|
||
match $format {
|
||
"json" => {
|
||
$containers
|
||
}
|
||
"table" => {
|
||
$containers | select Name State Status
|
||
}
|
||
_ => {
|
||
$containers | select Name State Status
|
||
}
|
||
}
|
||
}
|
||
|
||
# Initialize registry (create namespaces, policies)
|
||
export def "oci-registry init" [
|
||
--type: string = "zot"
|
||
--registry-url: string = "localhost:5000"
|
||
--admin-password: string = "admin"
|
||
--skip-test
|
||
] {
|
||
let script_path = $"(get-oci-registry-root)/scripts/init-registry.nu"
|
||
|
||
let args = [
|
||
"--registry-type" $type
|
||
"--registry-url" $registry_url
|
||
"--admin-password" $admin_password
|
||
]
|
||
|
||
let args = if $skip_test {
|
||
$args | append ["--skip-test"]
|
||
} else {
|
||
$args
|
||
}
|
||
|
||
nu $script_path ...$args
|
||
}
|
||
|
||
# Configure registry setting
|
||
export def "oci-registry configure" [
|
||
--type: string = "zot"
|
||
setting: string
|
||
value: string
|
||
] {
|
||
print $"⚙️ Configuring ($setting) = ($value)..."
|
||
|
||
match $type {
|
||
"zot" => {
|
||
configure-zot-setting $setting $value
|
||
}
|
||
"harbor" => {
|
||
configure-harbor-setting $setting $value
|
||
}
|
||
"distribution" => {
|
||
configure-distribution-setting $setting $value
|
||
}
|
||
_ => {
|
||
error make {
|
||
msg: $"Unknown registry type: ($type)"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
# Get registry logs
|
||
export def "oci-registry logs" [
|
||
--type: string = "zot"
|
||
--follow (-f)
|
||
--tail: int = 100
|
||
] {
|
||
let registry_path = get-registry-path $type
|
||
|
||
cd $registry_path
|
||
|
||
let flags = if $follow {
|
||
["--follow", "--tail", ($tail | into string)]
|
||
} else {
|
||
["--tail", ($tail | into string)]
|
||
}
|
||
|
||
^docker-compose logs ...$flags
|
||
}
|
||
|
||
# Health check
|
||
export def "oci-registry health" [
|
||
--type: string = "zot"
|
||
--registry-url: string = "localhost:5000"
|
||
] -> record {
|
||
print "🏥 Checking registry health..."
|
||
|
||
let api_check = check-api-health $registry_url
|
||
let catalog_check = check-catalog-health $registry_url
|
||
let metrics_check = check-metrics-health $registry_url $type
|
||
|
||
let overall = $api_check.healthy and $catalog_check.healthy
|
||
|
||
{
|
||
healthy: $overall
|
||
api: $api_check
|
||
catalog: $catalog_check
|
||
metrics: $metrics_check
|
||
timestamp: (date now | format date "%Y-%m-%d %H:%M:%S")
|
||
}
|
||
}
|
||
|
||
# Push test image
|
||
export def "oci-registry test-push" [
|
||
--registry-url: string = "localhost:5000"
|
||
] {
|
||
print "🧪 Pushing test image..."
|
||
|
||
# Pull hello-world
|
||
^docker pull hello-world:latest
|
||
|
||
# Tag for our registry
|
||
^docker tag hello-world:latest $"($registry_url)/provisioning-test/hello:latest"
|
||
|
||
# Push to registry
|
||
^docker push $"($registry_url)/provisioning-test/hello:latest"
|
||
|
||
print "✅ Test image pushed successfully"
|
||
}
|
||
|
||
# Pull test image
|
||
export def "oci-registry test-pull" [
|
||
--registry-url: string = "localhost:5000"
|
||
] {
|
||
print "🧪 Pulling test image..."
|
||
|
||
^docker pull $"($registry_url)/provisioning-test/hello:latest"
|
||
|
||
print "✅ Test image pulled successfully"
|
||
}
|
||
|
||
# List namespaces
|
||
export def "oci-registry namespaces" [
|
||
--registry-url: string = "localhost:5000"
|
||
--type: string = "zot"
|
||
] -> table {
|
||
let result = (do {
|
||
http get $"http://($registry_url)/v2/_catalog"
|
||
} | complete)
|
||
|
||
if $result.exit_code != 0 {
|
||
error make {
|
||
msg: $"Failed to fetch catalog: ($result.stderr)"
|
||
}
|
||
}
|
||
|
||
let repos = ($result.stdout | from json | get repositories)
|
||
let namespaces = ($repos | each { |repo|
|
||
$repo | split row "/" | first
|
||
} | uniq)
|
||
|
||
$namespaces | each { |ns|
|
||
let repo_count = ($repos | where { |r| $r | str starts-with $ns } | length)
|
||
{
|
||
namespace: $ns
|
||
repositories: $repo_count
|
||
}
|
||
}
|
||
}
|
||
|
||
# Create namespace
|
||
export def "oci-registry namespace create" [
|
||
name: string
|
||
--registry-url: string = "localhost:5000"
|
||
--type: string = "zot"
|
||
] {
|
||
print $"📦 Creating namespace: ($name)..."
|
||
|
||
match $type {
|
||
"zot" => {
|
||
print " ℹ Namespace will be created on first push"
|
||
}
|
||
"harbor" => {
|
||
create-harbor-namespace $registry_url $name
|
||
}
|
||
"distribution" => {
|
||
print " ℹ Namespace will be created on first push"
|
||
}
|
||
_ => {
|
||
error make {
|
||
msg: $"Unknown registry type: ($type)"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
# Delete namespace
|
||
export def "oci-registry namespace delete" [
|
||
name: string
|
||
--registry-url: string = "localhost:5000"
|
||
--type: string = "zot"
|
||
--force
|
||
] {
|
||
if not $force {
|
||
let confirm = (input $"⚠️ Delete namespace '($name)'? This will remove all images. \(y/N\): ")
|
||
if $confirm != "y" {
|
||
print "Cancelled"
|
||
return
|
||
}
|
||
}
|
||
|
||
print $"🗑️ Deleting namespace: ($name)..."
|
||
|
||
match $type {
|
||
"harbor" => {
|
||
delete-harbor-namespace $registry_url $name
|
||
}
|
||
_ => {
|
||
print $" ⚠ Namespace deletion not supported for ($type)"
|
||
print " Delete all images manually and run garbage collection"
|
||
}
|
||
}
|
||
}
|
||
|
||
# Helper functions
|
||
|
||
def get-registry-path [type: string] -> string {
|
||
let root = get-oci-registry-root
|
||
$"($root)/($type)"
|
||
}
|
||
|
||
def get-oci-registry-root [] -> string {
|
||
let config = (get-config)
|
||
let base_path = ($config | get paths.base)
|
||
$"($base_path)/provisioning/platform/oci-registry"
|
||
}
|
||
|
||
def check-api-health [registry_url: string] -> record {
|
||
let result = (do {
|
||
http get $"http://($registry_url)/v2/" --timeout 5sec
|
||
} | complete)
|
||
|
||
{
|
||
healthy: ($result.exit_code == 0)
|
||
message: (if $result.exit_code == 0 { "OK" } else { $result.stderr })
|
||
}
|
||
}
|
||
|
||
def check-catalog-health [registry_url: string] -> record {
|
||
let result = (do {
|
||
http get $"http://($registry_url)/v2/_catalog" --timeout 5sec
|
||
} | complete)
|
||
|
||
{
|
||
healthy: ($result.exit_code == 0)
|
||
message: (if $result.exit_code == 0 { "OK" } else { $result.stderr })
|
||
}
|
||
}
|
||
|
||
def check-metrics-health [registry_url: string, type: string] -> record {
|
||
let metrics_url = match $type {
|
||
"zot" => $"http://($registry_url)/metrics"
|
||
"distribution" => $"http://($registry_url):5001/metrics"
|
||
_ => null
|
||
}
|
||
|
||
if ($metrics_url | is-empty) {
|
||
return {
|
||
healthy: false
|
||
message: "Metrics not supported"
|
||
}
|
||
}
|
||
|
||
let result = (do {
|
||
http get $metrics_url --timeout 5sec
|
||
} | complete)
|
||
|
||
{
|
||
healthy: ($result.exit_code == 0)
|
||
message: (if $result.exit_code == 0 { "OK" } else { "Not available" })
|
||
}
|
||
}
|
||
|
||
def configure-zot-setting [setting: string, value: string] {
|
||
print " ℹ Zot configuration requires editing config.json and restarting"
|
||
print $" Setting: ($setting)"
|
||
print $" Value: ($value)"
|
||
}
|
||
|
||
def configure-harbor-setting [setting: string, value: string] {
|
||
print " ℹ Harbor configuration via API not fully implemented"
|
||
print $" Setting: ($setting)"
|
||
print $" Value: ($value)"
|
||
}
|
||
|
||
def configure-distribution-setting [setting: string, value: string] {
|
||
print " ℹ Distribution configuration requires editing config.yml and restarting"
|
||
print $" Setting: ($setting)"
|
||
print $" Value: ($value)"
|
||
}
|
||
|
||
def create-harbor-namespace [registry_url: string, name: string] {
|
||
print " ℹ Harbor project creation requires authentication"
|
||
print $" Run: oci-registry init --type harbor"
|
||
}
|
||
|
||
def delete-harbor-namespace [registry_url: string, name: string] {
|
||
print " ℹ Harbor project deletion requires authentication"
|
||
print $" Use Harbor UI or API with admin credentials"
|
||
}
|