Skip to content

Commit

Permalink
Image sorting
Browse files Browse the repository at this point in the history
  • Loading branch information
athornton committed Jan 2, 2025
1 parent 689631f commit 86037cd
Show file tree
Hide file tree
Showing 19 changed files with 21,008 additions and 330 deletions.
20 changes: 15 additions & 5 deletions harnesses/scan/dockerhub.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,23 @@

import os

from rsp_reaper.config import RegistryAuth, RegistryConfig, KeepPolicy
from rsp_reaper.storage.dockerhub import DockerHubClient

# We want to explode if the auth isn't set.
username = os.environ["DOCKERHUB_USER"]
password = os.environ["DOCKERHUB_PASSWORD"]
c = DockerHubClient(
namespace="lsstsqre", repository="sciplat-lab", dry_run=True
auth = RegistryAuth(
username=os.environ["DOCKERHUB_USER"],
password=os.environ["DOCKERHUB_PASSWORD"],
)
c.authenticate(username=username, password=password)
cfg = RegistryConfig(
category="hub.docker.com",
registry="https://docker.io",
owner="lsstsqre",
repository="sciplat-lab",
keep=KeepPolicy(),
debug=True,
dry_run=True
)
c = DockerHubClient(cfg=cfg)
c.authenticate(auth)
c.scan_repo()
16 changes: 11 additions & 5 deletions harnesses/scan/gar.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,18 @@
"""Interactive harness for Google Artifact Registry."""

from rsp_reaper.config import KeepPolicy, RegistryConfig
from rsp_reaper.storage.gar import GARClient

c = GARClient(
location="us-central1",
project_id="rubin-shared-services-71ec",
repository="sciplat",
image="sciplat-lab",
cfg = RegistryConfig(
category="pkg.dev",
registry="https://us-central1-docker.pkg.dev",
owner="rubin-shared-services-71ec",
namespace="sciplat",
repository="sciplat-lab",
keep = KeepPolicy(),
dry_run=True,
debug=True,
)
c = GARClient(cfg=cfg)
# No authentication; set up application default credentials in the environment
c.scan_repo()
15 changes: 12 additions & 3 deletions harnesses/scan/ghcr.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,19 @@

import os

from rsp_reaper.config import RegistryAuth, RegistryConfig, KeepPolicy
from rsp_reaper.storage.ghcr import GhcrClient

# We want to explode if the token isn't set.
token = os.environ["GHCR_TOKEN"]
c = GhcrClient(namespace="lsst-sqre", repository="sciplat-lab", dry_run=True)
c.authenticate(token=token)
auth = RegistryAuth(password=os.environ["GHCR_TOKEN"])
cfg=RegistryConfig(
category="ghcr.io",
registry="https://ghcr.io",
owner="lsst-sqre",
repository="sciplat-lab",
keep=KeepPolicy(),
dry_run=True
)
c = GhcrClient(cfg=cfg)
c.authenticate(auth)
c.scan_repo()
26 changes: 26 additions & 0 deletions harnesses/sort/gar.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
"""Interactive harness for Google Artifact Registry."""
from pathlib import Path
from rsp_reaper.config import KeepPolicy, RegistryConfig
from rsp_reaper.models.image import ImageVersionClass
from rsp_reaper.storage.gar import GARClient

input_file = ( Path(__file__).parent.parent.parent / "tests" / "support" /
"gar.contents.json")

cfg = RegistryConfig(
category="pkg.dev",
registry="https://us-central1-docker.pkg.dev",
owner="rubin-shared-services-71ec",
namespace="sciplat",
repository="sciplat-lab",
dry_run=True,
debug=True,
input_file=input_file,
keep = KeepPolicy()
)

c = GARClient(cfg=cfg)
c.categorize(ImageVersionClass.RSP)



10 changes: 8 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -118,8 +118,14 @@ python_files = [
extend = "ruff-shared.toml"

[tool.ruff.lint.per-file-ignores]
"src/jupyterlabcontroller/services/**" = [
"S108", # constructing /tmp paths for Kubernetes Pods
"src/rsp_reaper/models/rsptag.py" = [
"C901", # yes, compare() does have a lot of cases
]

"tests/**.py" = [
"SLF001", # Tests are allowed to access private members
"T201", # Tests are allowed to print
"S101", # Tests can use assert
]

[tool.ruff.lint.isort]
Expand Down
210 changes: 108 additions & 102 deletions requirements/dev.txt

Large diffs are not rendered by default.

100 changes: 86 additions & 14 deletions src/rsp_reaper/config.py
Original file line number Diff line number Diff line change
@@ -1,26 +1,98 @@
"""Configuration for a reaper for a particular container registry."""

from dataclasses import dataclass

from .models.registry_category import RegistryCategory
from dataclasses import dataclass, field
from pathlib import Path


@dataclass
class RegistryAuth:
"""Generic authentication item for a container registry."""

realm: str
username: str
password: str
realm: str|None = None
username: str|None = None
password: str|None = None


class LatestSemverKeepers:
"""Number of items to keep for latest major semver release. Default is
"do not purge".
"""

minor: int | None = None
patch: int | None = None
build: int | None = None


class OlderSemverKeepers:
"""Number of items to keep for previous major semver releases. Default is
"do not purge".
"""

major: int | None = None
minor: int | None = None
patch: int | None = None
build: int | None = None


@dataclass
class SemverKeepers:
"""Within each of latest_major and older, how many minor versions,
how many patch versions within each of those, and how many builds of each
of those, to keep. Older also has a major version number. For instance,
older.major might be 3, and then when version 5.0 came out, you would
keep some images for the 2.x.y, 3.x.y, and 4.x.y series, but no 1.x images.
"""

latest_major: LatestSemverKeepers = field(
default_factory=LatestSemverKeepers
)
older: OlderSemverKeepers = field(default_factory=OlderSemverKeepers)


@dataclass
class RSPKeepers:
"""Aliases are never purged. Default for everything else is "do not
purge".
"""

release: int | None = None
weekly: int | None = None
daily: int | None = None
release_candidate: int | None = None
experimental: int | None = None
unknown: int | None = None


@dataclass
class KeepPolicy:
"""How many of each image category to keep. `-1` or `None` means
"don't reap that category at all". `0` means "purge them all".
The default is to purge nothing.
"""

untagged: int | None = None
semver: SemverKeepers | None = field(default_factory=SemverKeepers)
rsp: RSPKeepers | None = field(default_factory=RSPKeepers)


@dataclass
class ContainerRegistryConfig:
"""Configuration for a particular container registry."""

namespace: str
repository: str
registry: str
category: RegistryCategory
project: str | None = None
class RegistryConfig:
"""Configuration to talk to a particular container registry."""

registry: str # URL of registry host
owner: str # Usually repo owner; at GAR, project ID
repository: str # Repository name, e.g. "sciplat-lab"
category: str
keep: KeepPolicy
namespace: str | None = None # Intermediate layer if any; GAR: "sciplat"
auth: RegistryAuth | None = None
dry_run: bool = True
debug: bool = True
input_file: Path | None = None


@dataclass
class Config:
"""Configuration for multiple registries."""

registries: list[RegistryConfig]
10 changes: 4 additions & 6 deletions src/rsp_reaper/factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,15 @@
import structlog
from structlog.stdlib import BoundLogger

from .config import ContainerRegistryConfig
from .config import RegistryConfig


class Factory:
"""Build reaper components.
Parameters
----------
config: ContainerRegistryConfig
config: RegistryConfig
Reaper configuration.
logger
Expand All @@ -26,17 +26,15 @@ class Factory:

@classmethod
@asynccontextmanager
async def standalone(
cls, config: ContainerRegistryConfig
) -> AsyncIterator[Self]:
async def standalone(cls, config: RegistryConfig) -> AsyncIterator[Self]:
"""Async context manager for reaper components.
Intended for the test suite.
Parameters
----------
config
Container Registry configuration
Registry configuration
Yields
------
Expand Down
Loading

0 comments on commit 86037cd

Please sign in to comment.