371 lines
9.2 KiB
Text
371 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"
|
|||
|
|
}
|