293 lines
9.1 KiB
Plaintext
293 lines
9.1 KiB
Plaintext
#!/usr/bin/env nu
|
|
|
|
# Initialize OCI registry with required namespaces and policies
|
|
|
|
export def main [
|
|
--registry-type: string = "zot" # zot, harbor, distribution
|
|
--registry-url: string = "localhost:5000"
|
|
--admin-password: string = "admin"
|
|
--skip-test
|
|
] {
|
|
print "🚀 Initializing OCI registry..."
|
|
print $" Type: ($registry_type)"
|
|
print $" URL: ($registry_url)"
|
|
|
|
# Wait for registry to be healthy
|
|
print "\n⏳ Waiting for registry to be ready..."
|
|
wait-for-registry-health $registry_url
|
|
|
|
# Create namespaces
|
|
print "\n📦 Creating namespaces..."
|
|
create-default-namespaces $registry_url $registry_type $admin_password
|
|
|
|
# Setup policies
|
|
print "\n🔒 Configuring access policies..."
|
|
setup-access-policies $registry_url $registry_type $admin_password
|
|
|
|
# Push test image (unless skipped)
|
|
if not $skip_test {
|
|
print "\n🧪 Pushing test image..."
|
|
push-test-image $registry_url $registry_type
|
|
}
|
|
|
|
print "\n✅ OCI registry initialized successfully!"
|
|
print "\n📊 Registry Information:"
|
|
get-registry-info $registry_url $registry_type
|
|
}
|
|
|
|
def wait-for-registry-health [registry_url: string] {
|
|
mut retries = 30
|
|
mut attempt = 1
|
|
|
|
while $retries > 0 {
|
|
print $" Attempt ($attempt)/30..."
|
|
|
|
let result = (do {
|
|
http get $"http://($registry_url)/v2/"
|
|
} | complete)
|
|
|
|
if $result.exit_code == 0 {
|
|
print " ✓ Registry is healthy"
|
|
return
|
|
}
|
|
|
|
$retries = ($retries - 1)
|
|
$attempt = ($attempt + 1)
|
|
sleep 2sec
|
|
}
|
|
|
|
error make {msg: "Registry failed to become healthy after 60 seconds"}
|
|
}
|
|
|
|
def create-default-namespaces [
|
|
registry_url: string
|
|
registry_type: string
|
|
admin_password: string
|
|
] {
|
|
let namespaces = [
|
|
{
|
|
name: "provisioning-extensions"
|
|
description: "Extension packages (providers, taskservs, clusters)"
|
|
public: false
|
|
}
|
|
{
|
|
name: "provisioning-kcl"
|
|
description: "KCL schema packages"
|
|
public: false
|
|
}
|
|
{
|
|
name: "provisioning-platform"
|
|
description: "Platform service images (orchestrator, control-center, etc.)"
|
|
public: false
|
|
}
|
|
{
|
|
name: "provisioning-test"
|
|
description: "Test images and artifacts"
|
|
public: true
|
|
}
|
|
]
|
|
|
|
for namespace in $namespaces {
|
|
print $" Creating ($namespace.name)..."
|
|
print $" Description: ($namespace.description)"
|
|
print $" Public: ($namespace.public)"
|
|
|
|
# Create namespace (method depends on registry type)
|
|
match $registry_type {
|
|
"zot" => {
|
|
# Zot creates namespaces implicitly on first push
|
|
print " ✓ Will be created on first push"
|
|
}
|
|
"harbor" => {
|
|
# Harbor requires API call to create project
|
|
create-harbor-project $registry_url $namespace.name $namespace.description $namespace.public $admin_password
|
|
}
|
|
"distribution" => {
|
|
# Distribution creates namespaces implicitly
|
|
print " ✓ Will be created on first push"
|
|
}
|
|
_ => {
|
|
print $" ⚠ Unknown registry type: ($registry_type)"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
def setup-access-policies [
|
|
registry_url: string
|
|
registry_type: string
|
|
admin_password: string
|
|
] {
|
|
# Configure access policies based on registry type
|
|
match $registry_type {
|
|
"zot" => {
|
|
# Zot policies in config.json
|
|
print " ✓ Policies configured via config.json"
|
|
print " - provisioning-extensions: authenticated users"
|
|
print " - provisioning-kcl: authenticated users"
|
|
print " - provisioning-platform: private"
|
|
print " - provisioning-test: public read"
|
|
}
|
|
"harbor" => {
|
|
# Harbor policies via API
|
|
print " Configuring Harbor policies..."
|
|
setup-harbor-policies $registry_url $admin_password
|
|
}
|
|
"distribution" => {
|
|
# Distribution uses htpasswd
|
|
print " ✓ Policies configured via htpasswd"
|
|
print " - All authenticated users: push/pull"
|
|
print " - Anonymous: pull only public repos"
|
|
}
|
|
_ => {
|
|
print $" ⚠ Unknown registry type: ($registry_type)"
|
|
}
|
|
}
|
|
}
|
|
|
|
def push-test-image [registry_url: string, registry_type: string] {
|
|
print $" Pushing test artifact to ($registry_url)/provisioning-test/hello:latest"
|
|
|
|
# Create minimal OCI artifact using oras or docker
|
|
let result = (do {
|
|
# Try to push using docker if available
|
|
docker pull hello-world:latest | complete
|
|
} | complete)
|
|
|
|
if $result.exit_code == 0 {
|
|
let tag_result = (do {
|
|
docker tag hello-world:latest $"($registry_url)/provisioning-test/hello:latest"
|
|
} | complete)
|
|
|
|
if $tag_result.exit_code == 0 {
|
|
let push_result = (do {
|
|
docker push $"($registry_url)/provisioning-test/hello:latest"
|
|
} | complete)
|
|
|
|
if $push_result.exit_code == 0 {
|
|
print " ✓ Test image pushed successfully"
|
|
return
|
|
}
|
|
}
|
|
}
|
|
|
|
print " ⚠ Could not push test image (docker may not be available)"
|
|
print " This is optional and can be done manually later"
|
|
}
|
|
|
|
def create-harbor-project [
|
|
registry_url: string
|
|
project_name: string
|
|
description: string
|
|
public: bool
|
|
admin_password: string
|
|
] {
|
|
# API call to create Harbor project
|
|
let body = {
|
|
project_name: $project_name
|
|
metadata: {
|
|
public: ($public | into string)
|
|
}
|
|
storage_limit: -1
|
|
}
|
|
|
|
let auth = $"admin:($admin_password)" | encode base64
|
|
|
|
let result = (do {
|
|
http post $"http://($registry_url)/api/v2.0/projects" $body --headers {
|
|
"Authorization": $"Basic ($auth)"
|
|
"Content-Type": "application/json"
|
|
}
|
|
} | complete)
|
|
|
|
if $result.exit_code == 0 {
|
|
print $" ✓ Project created"
|
|
} else {
|
|
print $" ⚠ Could not create project (may already exist)"
|
|
}
|
|
}
|
|
|
|
def setup-harbor-policies [registry_url: string, admin_password: string] {
|
|
# Setup Harbor policies via API
|
|
let projects = ["provisioning-extensions", "provisioning-kcl", "provisioning-platform", "provisioning-test"]
|
|
|
|
for project in $projects {
|
|
print $" Configuring policies for ($project)..."
|
|
|
|
# Set retention policy
|
|
let retention = {
|
|
rules: [
|
|
{
|
|
disabled: false
|
|
action: "retain"
|
|
params: {
|
|
latestPushedK: 10
|
|
}
|
|
tag_selectors: [
|
|
{
|
|
kind: "doublestar"
|
|
decoration: "matches"
|
|
pattern: "**"
|
|
}
|
|
]
|
|
scope_selectors: {
|
|
repository: [
|
|
{
|
|
kind: "doublestar"
|
|
decoration: "matches"
|
|
pattern: "**"
|
|
}
|
|
]
|
|
}
|
|
}
|
|
]
|
|
algorithm: "or"
|
|
}
|
|
|
|
# Additional Harbor-specific configurations
|
|
print $" ✓ Policies configured"
|
|
}
|
|
}
|
|
|
|
def get-registry-info [registry_url: string, registry_type: string] -> table {
|
|
let info = match $registry_type {
|
|
"zot" => {
|
|
[
|
|
{key: "Type", value: "Zot (lightweight)"}
|
|
{key: "Version", value: "2.0.1"}
|
|
{key: "API", value: $"http://($registry_url)/v2/"}
|
|
{key: "UI", value: $"http://($registry_url)/"}
|
|
{key: "Metrics", value: $"http://($registry_url)/metrics"}
|
|
{key: "Features", value: "Search, UI, Metrics, Scrub, GC"}
|
|
]
|
|
}
|
|
"harbor" => {
|
|
[
|
|
{key: "Type", value: "Harbor (full-featured)"}
|
|
{key: "Version", value: "2.9.0"}
|
|
{key: "API", value: $"http://($registry_url)/api/v2.0/"}
|
|
{key: "UI", value: $"http://($registry_url)/"}
|
|
{key: "Features", value: "Replication, Scanning, RBAC, Webhooks"}
|
|
]
|
|
}
|
|
"distribution" => {
|
|
[
|
|
{key: "Type", value: "Distribution (OCI standard)"}
|
|
{key: "Version", value: "2.8"}
|
|
{key: "API", value: $"http://($registry_url)/v2/"}
|
|
{key: "UI", value: $"http://($registry_url):8080/"}
|
|
{key: "Metrics", value: $"http://($registry_url):5001/metrics"}
|
|
{key: "Features", value: "OCI-compliant, Lightweight, Fast"}
|
|
]
|
|
}
|
|
_ => {
|
|
[
|
|
{key: "Type", value: "Unknown"}
|
|
{key: "URL", value: $registry_url}
|
|
]
|
|
}
|
|
}
|
|
|
|
$info | table
|
|
}
|