This plugin provides production-ready integration between Waldur Mastermind and Harbor container registry, enabling automated management of Harbor projects, storage quotas, and OIDC-based access control.
graph TB
subgraph "Waldur Mastermind"
WC[Waldur Customer<br/>customer-slug]
WP[Waldur Project<br/>project-slug]
WR1[Waldur Resource 1<br/>resource-slug-1]
WR2[Waldur Resource 2<br/>resource-slug-2]
WU1[Waldur User 1]
WU2[Waldur User 2]
WU3[Waldur User 3]
end
subgraph "Harbor Registry"
HG[OIDC Group<br/>waldur-project-slug]
HP1[Harbor Project 1<br/>waldur-resource-slug-1]
HP2[Harbor Project 2<br/>waldur-resource-slug-2]
HQ1[Storage Quota 1<br/>e.g., 10GB]
HQ2[Storage Quota 2<br/>e.g., 20GB]
HR1[Container Repos 1]
HR2[Container Repos 2]
end
subgraph "OIDC Provider"
OG[OIDC Group<br/>waldur-project-slug]
OU1[OIDC User 1]
OU2[OIDC User 2]
OU3[OIDC User 3]
end
%% Relationships
WC --> WP
WP --> WR1
WP --> WR2
WP --> WU1
WP --> WU2
WP --> WU3
%% Waldur to Harbor mapping
WR1 -.->|"1:1 mapping"| HP1
WR2 -.->|"1:1 mapping"| HP2
WP -.->|"1:1 mapping"| HG
%% Harbor internal relationships
HG -->|"Developer role"| HP1
HG -->|"Developer role"| HP2
HP1 --> HQ1
HP2 --> HQ2
HP1 --> HR1
HP2 --> HR2
%% OIDC relationships
WU1 -.->|"SSO identity"| OU1
WU2 -.->|"SSO identity"| OU2
WU3 -.->|"SSO identity"| OU3
OU1 --> OG
OU2 --> OG
OU3 --> OG
HG -.->|"Same group"| OG
%% Styling
classDef waldur fill:#e1f5fe
classDef harbor fill:#fff3e0
classDef oidc fill:#f3e5f5
class WC,WP,WR1,WR2,WU1,WU2,WU3 waldur
class HG,HP1,HP2,HQ1,HQ2,HR1,HR2 harbor
class OG,OU1,OU2,OU3 oidc
{allocation_prefix}{resource_slug}{oidc_group_prefix}{project_slug}# From the workspace root
uv sync --all-packages
Add the Harbor backend configuration to your waldur-site-agent-config.yaml:
offerings:
harbor-registry:
backend_type: harbor
backend_settings:
# Harbor instance URL
harbor_url: "https://harbor.example.com"
# Robot account credentials (ensure robot has sufficient permissions)
robot_username: "robot$waldur-agent"
robot_password: "your-robot-password-here"
# Default storage quota in GB for new projects
default_storage_quota_gb: 10
# Naming prefixes
oidc_group_prefix: "waldur-" # OIDC groups: waldur-{project_slug}
allocation_prefix: "waldur-" # Harbor projects: waldur-{resource_slug}
# Harbor project role for OIDC groups
# 1=Admin, 2=Developer (recommended), 3=Guest, 4=Maintainer
project_role_id: 2
backend_components:
storage:
measured_unit: "GB"
accounting_type: "limit"
label: "Container Storage"
unit_factor: 1
# Waldur API settings
api_url: "https://waldur.example.com/api/"
api_token: "your-waldur-api-token"
# Offering UUID in Waldur
offering_uuid: "harbor-offering-uuid"
Critical: The Harbor robot account must have the following permissions:
POST /api/v2.0/projects)DELETE /api/v2.0/projects/{id}) - REQUIRED for proper resource lifecycleGET/PUT /api/v2.0/quotas)GET/POST /api/v2.0/usergroups)GET/POST/DELETE /api/v2.0/projects/{id}/members)✅ Verified: All operations including project deletion are working with proper system-level robot account permissions.
Note: The robot account needs system-level permissions to delete projects. Project-level robot accounts cannot delete their own projects.
openid,email,profile,groupsgroups (or your IdP’s group claim)# Process orders (create/delete Harbor projects)
uv run waldur_site_agent -m order_process -c config.yaml
# Report usage back to Waldur
uv run waldur_site_agent -m report -c config.yaml
# Synchronize memberships (OIDC group management)
uv run waldur_site_agent -m membership_sync -c config.yaml
Create a systemd service for automated operation:
[Unit]
Description=Waldur Harbor Agent - Order Processing
After=network.target
[Service]
Type=simple
User=waldur
ExecStart=/usr/local/bin/waldur_site_agent -m order_process -c /etc/waldur/harbor-config.yaml
Restart=on-failure
RestartSec=60
[Install]
WantedBy=multi-user.target
The plugin implements the following Harbor API operations:
Run the test suite:
# Run all Harbor plugin tests
uv run pytest plugins/harbor/tests/ -v
# Run with coverage
uv run pytest plugins/harbor/tests/ --cov=waldur_site_agent_harbor
Symptom: 403 Forbidden - CSRF token not found in request
Root Cause: Harbor’s session-based authentication requires CSRF tokens for persistent sessions.
✅ Solution: The plugin now uses direct HTTP requests with authentication tuples instead of persistent sessions, which bypasses CSRF requirements entirely.
Technical Details:
# OLD (caused CSRF issues)
session = requests.Session()
session.headers.update({"Authorization": "Basic ..."})
response = session.post(url, json=data)
# NEW (works perfectly)
auth = (username, password)
response = requests.post(url, auth=auth, json=data)
Symptoms:
✅ Solution: Ensure robot account has system-level permissions:
Critical: Without project deletion permissions, Harbor projects will accumulate when Waldur resources are terminated, leading to storage waste and potential quota issues.
curl -u "robot\$user:pass" https://harbor.example.com/api/v2.0/healthgroups is common)curl -u "robot\$user:pass" https://harbor.example.com/api/v2.0/quotas# In waldur-site-agent config
logging:
level: DEBUG
format: "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
from waldur_site_agent_harbor.client import HarborClient
client = HarborClient("https://harbor.example.com", "robot$user", "password")
# Test connectivity
print("Ping:", client.ping())
# List projects
projects = client.list_resources()
print("Projects:", [p.name for p in projects])
# Test permissions
try:
# This should work if permissions are correct
group_id = client.create_user_group("test-group")
print("Group created:", group_id)
except Exception as e:
print("Permission issue:", e)
# For systemd deployments
journalctl -u waldur-harbor-agent -f --since "1 hour ago"
# For direct execution
tail -f /var/log/waldur-site-agent.log
Test robot account permissions manually:
# Test authentication
curl -u "robot\$username:password" https://harbor.example.com/api/v2.0/health
# Test project listing
curl -u "robot\$username:password" https://harbor.example.com/api/v2.0/projects
# Test quota access
curl -u "robot\$username:password" https://harbor.example.com/api/v2.0/quotas
# Test group management
curl -u "robot\$username:password" https://harbor.example.com/api/v2.0/usergroups
# Test project deletion permissions (CRITICAL)
# First create a test project
curl -X POST -H "Content-Type: application/json" \
-u "robot\$username:password" \
-d '{"project_name":"deletion-test","metadata":{"public":"false"}}' \
https://harbor.example.com/api/v2.0/projects
# Then try to delete it (should return 200/204, not 403)
# Get project ID first, then delete
curl -X DELETE -u "robot\$username:password" \
https://harbor.example.com/api/v2.0/projects/{project_id}
plugins/harbor/
├── waldur_site_agent_harbor/
│ ├── __init__.py
│ ├── backend.py # HarborBackend implementation
│ ├── client.py # Harbor API client
│ └── exceptions.py # Custom exceptions
├── tests/
│ ├── test_harbor_backend.py
│ └── test_harbor_client.py
├── pyproject.toml
└── README.md
HarborClient class for new API operationsHarborBackend to utilize new client methodsThis plugin is part of the Waldur Site Agent project and follows the same licensing terms.
For issues and questions: