-
Story
-
Resolution: Unresolved
-
Undefined
-
None
-
None
-
None
[API] Extend Mirror API with Architecture Configuration
Summary
Extend the mirror configuration API to accept and return architecture filter settings. This allows users and automation to configure which architectures should be mirrored from multi-architecture images via the REST API.
Acceptance Criteria
- [ ] Mirror API accepts architecture_filter in create/update requests
- [ ] Mirror API returns architecture_filter in get responses
- [ ] Architecture values are validated against supported list
- [ ] Empty array or null means "mirror all architectures"
- [ ] Invalid architecture values return appropriate error response
- [ ] Changes to architecture filter are logged in audit log
- [ ] Existing API clients continue to work (backwards compatible)
Technical Requirements
API Schema Updates
File: endpoints/api/mirror.py
Add architecture_filter to the common properties schema (around line 28):
common_properties = {
# ... existing properties ...
"architecture_filter": {
"type": ["array", "null"],
"description": "List of architectures to mirror. Empty or null mirrors all architectures.",
"items": {
"type": "string",
"enum": ["amd64", "arm64", "ppc64le", "s390x", "386", "riscv64"]
},
"uniqueItems": True,
"example": ["amd64", "arm64"]
},
}
GET Response
File: endpoints/api/mirror.py
Update RepoMirrorResource.get() to include architecture filter:
@require_repo_read(allow_for_superuser=True) @define_json_response("RepoMirrorConfig") def get(self, namespace_name, repository_name): """Get repository mirror configuration.""" mirror = model.repo_mirror.get_mirror(repository) if mirror is None: raise NotFound() return { # ... existing fields ... "architecture_filter": mirror.architecture_filter or [], }
PUT/POST Request Handling
File: endpoints/api/mirror.py
Update RepoMirrorResource.put() to handle architecture filter:
@require_repo_admin(allow_for_superuser=True) @validate_json_request("RepoMirrorConfig") def put(self, namespace_name, repository_name): """Update repository mirror configuration.""" values = request.get_json() # ... existing validation ... # Handle architecture filter if "architecture_filter" in values: arch_filter = values["architecture_filter"] if arch_filter: validate_architecture_filter(arch_filter) model.repo_mirror.set_architecture_filter( repository, arch_filter or [] ) # Audit log the change track_and_log( "repo_mirror_config_changed", repository, { "field": "architecture_filter", "value": arch_filter, } ) # ... rest of update logic ...
Validation Function
File: endpoints/api/mirror.py
Add validation for architecture values:
VALID_ARCHITECTURES = {"amd64", "arm64", "ppc64le", "s390x", "386", "riscv64"}
def validate_architecture_filter(arch_filter):
"""
Validate architecture filter values.
Raises:
InvalidRequest: If any architecture value is invalid
"""
if not isinstance(arch_filter, list):
raise InvalidRequest("architecture_filter must be an array")
invalid_archs = set(arch_filter) - VALID_ARCHITECTURES
if invalid_archs:
raise InvalidRequest(
f"Invalid architecture values: {', '.join(sorted(invalid_archs))}. "
f"Valid values: {', '.join(sorted(VALID_ARCHITECTURES))}"
)
if len(arch_filter) != len(set(arch_filter)):
raise InvalidRequest("Duplicate architecture values not allowed")
OpenAPI Schema
File: endpoints/api/mirror.py
Update the response schema to include architecture filter:
schemas = {
"RepoMirrorConfig": {
"type": "object",
"properties": {
# ... existing properties ...
"architecture_filter": {
"type": "array",
"items": {"type": "string"},
"description": "List of architectures to mirror",
},
},
},
}
Implementation Notes
Existing Patterns to Follow
- Validation: See validate_json_request() usage in mirror.py
- Audit logging: See track_and_log() usage for other mirror config changes
- Error responses: See InvalidRequest usage pattern
Request/Response Examples
GET Response:
{
"is_enabled": true,
"external_reference": "docker.io/library/nginx",
"sync_interval": 86400,
"robot_username": "org+robot",
"root_rule": {
"rule_kind": "tag_glob_csv",
"rule_value": ["latest", "1.*"]
},
"architecture_filter": ["amd64", "arm64"]
}
PUT Request:
{
"architecture_filter": ["amd64"]
}
Error Response:
{
"status": 400,
"error_message": "Invalid architecture values: x86. Valid values: 386, amd64, arm64, ppc64le, riscv64, s390x",
"error_type": "invalid_request"
}
Backwards Compatibility
- Existing API clients that don't send architecture_filter continue to work
- Mirrors without architecture filter configured return [] (empty array)
- Empty array or null in request means "mirror all architectures"
Audit Logging
Add new log entry kind if needed:
# In migration or seeding
LogEntryKind.create(name="repo_mirror_architecture_changed")
Log format:
track_and_log(
"repo_mirror_config_changed",
repository,
{
"field": "architecture_filter",
"old_value": old_arch_filter,
"new_value": new_arch_filter,
}
)
Dependencies
- Story 01: Database schema for architecture_filter field
Testing Requirements
Unit Tests
File: endpoints/api/test/test_mirror.py (extend)
def test_get_mirror_includes_architecture_filter():
"""Test GET response includes architecture_filter field."""
def test_get_mirror_empty_architecture_filter():
"""Test GET returns empty array when no filter configured."""
def test_put_mirror_architecture_filter():
"""Test updating mirror with architecture filter."""
def test_put_mirror_architecture_filter_validation():
"""Test invalid architecture values are rejected."""
def test_put_mirror_architecture_filter_empty():
"""Test setting empty array clears filter."""
def test_put_mirror_architecture_filter_null():
"""Test setting null clears filter."""
def test_put_mirror_architecture_filter_duplicate():
"""Test duplicate architecture values are rejected."""
def test_put_mirror_backwards_compatible():
"""Test existing clients without architecture_filter still work."""
def test_architecture_filter_audit_logged():
"""Test changes to architecture filter are logged."""
API Integration Tests
Test with actual HTTP requests:
1. Create mirror with architecture filter
2. Get mirror and verify filter is returned
3. Update filter and verify changes
4. Test validation error responses
Definition of Done
- [ ] Code implemented and follows project conventions
- [ ] All acceptance criteria met
- [ ] Unit tests written and passing
- [ ] API integration tests written and passing
- [ ] API documentation updated
- [ ] Backwards compatibility verified
- [ ] Code reviewed and approved