Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changes/6464.feature.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Support OCP (OpenShift Container Platform) Registry
4 changes: 2 additions & 2 deletions docs/manager/graphql-reference/schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -2451,9 +2451,9 @@
ssl_verify: Boolean

"""
Added in 24.09.0. Registry type. One of ('docker', 'harbor', 'harbor2', 'github', 'gitlab', 'ecr', 'ecr-public', 'local').
Added in 24.09.0. Registry type. One of ('docker', 'harbor', 'harbor2', 'github', 'gitlab', 'ecr', 'ecr-public', 'local', 'ocp').
"""
type: ContainerRegistryTypeField!

Check warning on line 2456 in docs/manager/graphql-reference/schema.graphql

View workflow job for this annotation

GitHub Actions / GraphQL Inspector

Description for argument 'type' on field 'Mutation.create_container_registry_node' changed from 'Added in 24.09.0. Registry type. One of ('docker', 'harbor', 'harbor2', 'github', 'gitlab', 'ecr', 'ecr-public', 'local').' to 'Added in 24.09.0. Registry type. One of ('docker', 'harbor', 'harbor2', 'github', 'gitlab', 'ecr', 'ecr-public', 'local', 'ocp').'

Description for argument 'type' on field 'Mutation.create_container_registry_node' changed from 'Added in 24.09.0. Registry type. One of ('docker', 'harbor', 'harbor2', 'github', 'gitlab', 'ecr', 'ecr-public', 'local').' to 'Added in 24.09.0. Registry type. One of ('docker', 'harbor', 'harbor2', 'github', 'gitlab', 'ecr', 'ecr-public', 'local', 'ocp').'

"""Added in 24.09.0."""
url: String!
Expand Down Expand Up @@ -2486,9 +2486,9 @@
ssl_verify: Boolean

"""
Registry type. One of ('docker', 'harbor', 'harbor2', 'github', 'gitlab', 'ecr', 'ecr-public', 'local'). Added in 24.09.0.
Registry type. One of ('docker', 'harbor', 'harbor2', 'github', 'gitlab', 'ecr', 'ecr-public', 'local', 'ocp'). Added in 24.09.0.
"""
type: ContainerRegistryTypeField

Check warning on line 2491 in docs/manager/graphql-reference/schema.graphql

View workflow job for this annotation

GitHub Actions / GraphQL Inspector

Description for argument 'type' on field 'Mutation.modify_container_registry_node' changed from 'Registry type. One of ('docker', 'harbor', 'harbor2', 'github', 'gitlab', 'ecr', 'ecr-public', 'local'). Added in 24.09.0.' to 'Registry type. One of ('docker', 'harbor', 'harbor2', 'github', 'gitlab', 'ecr', 'ecr-public', 'local', 'ocp'). Added in 24.09.0.'

Description for argument 'type' on field 'Mutation.modify_container_registry_node' changed from 'Registry type. One of ('docker', 'harbor', 'harbor2', 'github', 'gitlab', 'ecr', 'ecr-public', 'local'). Added in 24.09.0.' to 'Registry type. One of ('docker', 'harbor', 'harbor2', 'github', 'gitlab', 'ecr', 'ecr-public', 'local', 'ocp'). Added in 24.09.0.'

"""Added in 24.09.0."""
url: String
Expand Down
4 changes: 2 additions & 2 deletions docs/manager/graphql-reference/supergraph.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -4420,7 +4420,7 @@ type Mutation
ssl_verify: Boolean

"""
Added in 24.09.0. Registry type. One of ('docker', 'harbor', 'harbor2', 'github', 'gitlab', 'ecr', 'ecr-public', 'local').
Added in 24.09.0. Registry type. One of ('docker', 'harbor', 'harbor2', 'github', 'gitlab', 'ecr', 'ecr-public', 'local', 'ocp').
"""
type: ContainerRegistryTypeField!

Expand Down Expand Up @@ -4455,7 +4455,7 @@ type Mutation
ssl_verify: Boolean

"""
Registry type. One of ('docker', 'harbor', 'harbor2', 'github', 'gitlab', 'ecr', 'ecr-public', 'local'). Added in 24.09.0.
Registry type. One of ('docker', 'harbor', 'harbor2', 'github', 'gitlab', 'ecr', 'ecr-public', 'local', 'ocp'). Added in 24.09.0.
"""
type: ContainerRegistryTypeField

Expand Down
3 changes: 2 additions & 1 deletion docs/manager/rest-reference/openapi.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,8 @@
"gitlab",
"ecr",
"ecr-public",
"local"
"local",
"ocp"
],
"title": "ContainerRegistryType",
"type": "string"
Expand Down
13 changes: 13 additions & 0 deletions fixtures/manager/example-container-registries-ocp.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"container_registries": [
{
"id": "7a92d3c1-5f4e-4829-b8e6-2d9c1a3f7e56",
"registry_name": "default-route-openshift-image-registry.apps-crc.testing",
"url": "https://default-route-openshift-image-registry.apps-crc.testing",
"type": "ocp",
"project": "openshift",
"username": "kubeadmin",
"password": "<ocp_token>"
}
]
}
1 change: 1 addition & 0 deletions src/ai/backend/common/container_registry.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ class ContainerRegistryType(enum.StrEnum):
ECR = "ecr"
ECR_PUB = "ecr-public"
LOCAL = "local"
OCP = "ocp"


class AllowedGroupsModel(BaseFieldModel):
Expand Down
4 changes: 4 additions & 0 deletions src/ai/backend/manager/container_registry/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,10 @@ def get_container_registry_cls(registry_info: ContainerRegistryRow) -> Type[Base
from .local import LocalRegistry

cr_cls = LocalRegistry
elif registry_type == ContainerRegistryType.OCP:
from .ocp import OpenShiftPlatformContainerRegistry

cr_cls = OpenShiftPlatformContainerRegistry
else:
raise RuntimeError(f"Unsupported registry type: {registry_type}")
return cr_cls
21 changes: 10 additions & 11 deletions src/ai/backend/manager/container_registry/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -479,18 +479,17 @@ async def _process_oci_manifest(
if (reporter := progress_reporter.get()) is not None:
reporter.total_progress += 1

async with concurrency_sema.get():
config_digest = image_info["config"]["digest"]
size_bytes = (
sum(layer["size"] for layer in image_info["layers"]) + image_info["config"]["size"]
)
config_digest = image_info["config"]["digest"]
size_bytes = (
sum(layer["size"] for layer in image_info["layers"]) + image_info["config"]["size"]
)

async with sess.get(
self.registry_url / f"v2/{image}/blobs/{config_digest}",
**rqst_args,
) as resp:
resp.raise_for_status()
config_data = await read_json(resp)
async with sess.get(
self.registry_url / f"v2/{image}/blobs/{config_digest}",
**rqst_args,
) as resp:
resp.raise_for_status()
config_data = await read_json(resp)

labels = {}
if _config_labels := (config_data.get("config") or {}).get("Labels"):
Expand Down
61 changes: 61 additions & 0 deletions src/ai/backend/manager/container_registry/ocp.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import logging
from http import HTTPStatus
from typing import AsyncIterator, Optional, cast, override

import aiohttp
import yarl

from ai.backend.common.docker import login as registry_login
from ai.backend.common.json import read_json
from ai.backend.logging import BraceStyleAdapter
from ai.backend.manager.exceptions import ContainerRegistryProjectEmpty

from .base import BaseContainerRegistry

log = BraceStyleAdapter(logging.getLogger(__spec__.name)) # type: ignore[name-defined]


class OpenShiftPlatformContainerRegistry(BaseContainerRegistry):
@override
async def fetch_repositories(
self,
sess: aiohttp.ClientSession,
) -> AsyncIterator[str]:
if not self.registry_info.project:
raise ContainerRegistryProjectEmpty(self.registry_info.type, self.registry_info.project)

# OpenShift Container Registry uses Docker Registry API v2
# The credential should have the catalog search privilege.
rqst_args = await registry_login(
sess,
self.registry_url,
self.credentials,
"registry:catalog:*",
)
catalog_url: Optional[yarl.URL]
catalog_url = (self.registry_url / "v2/_catalog").with_query(
{"n": "30"},
)
while catalog_url is not None:
async with sess.get(catalog_url, **rqst_args) as resp:
if resp.status == HTTPStatus.OK:
data = await read_json(resp)

for item in data["repositories"]:
if item.startswith(self.registry_info.project):
yield item
log.debug("found {} repositories", len(data["repositories"]))
else:
log.warning(
"OpenShift Container Registry {0} does not allow/support catalog search. (status={1})",
self.registry_url,
resp.status,
)
break
catalog_url = None
next_page_link = resp.links.get("next")
if next_page_link:
next_page_url = cast(yarl.URL, next_page_link["url"])
catalog_url = self.registry_url.with_path(next_page_url.path).with_query(
next_page_url.query
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
"""Remove container_registries.registry_name length restriction

Revision ID: b0fb0eb6b6bc
Revises: d811b103dbfc
Create Date: 2025-10-29 10:38:05.866930

"""

import sqlalchemy as sa
from alembic import op

# revision identifiers, used by Alembic.
revision = "b0fb0eb6b6bc"
down_revision = "d811b103dbfc"
branch_labels = None
depends_on = None


def upgrade() -> None:
op.alter_column(
"container_registries",
"registry_name",
existing_type=sa.String(length=50),
type_=sa.String(),
existing_nullable=False,
existing_server_default=None,
)


def downgrade() -> None:
op.alter_column(
"container_registries",
"registry_name",
existing_type=sa.String(),
type_=sa.String(length=50),
existing_nullable=False,
existing_server_default=None,
)
2 changes: 1 addition & 1 deletion src/ai/backend/manager/models/container_registry.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ class ContainerRegistryRow(Base):
__tablename__ = "container_registries"
id = IDColumn()
url = sa.Column("url", sa.String(length=512), index=True, nullable=False)
registry_name = sa.Column("registry_name", sa.String(length=50), index=True, nullable=False)
registry_name = sa.Column("registry_name", sa.String(), index=True, nullable=False)
type = sa.Column(
"type",
StrEnumType(ContainerRegistryType),
Expand Down
Loading