Skip to content

Commit

Permalink
Merge pull request #52 from SEKOIA-IO/feat/sync_library_registry
Browse files Browse the repository at this point in the history
feat: Add registry info to docker images
  • Loading branch information
Darkheir authored Jun 9, 2023
2 parents fde1978 + dc14cdb commit 6f7cb72
Show file tree
Hide file tree
Showing 4 changed files with 71 additions and 133 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Changed

- In `synchronize-lib` script:
- Specify the registry in the image name
- Improve checking the existence of the image in the registry

## [1.3.1] - 2023-06-06

### Changed
Expand Down
26 changes: 15 additions & 11 deletions sekoia_automation/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -150,24 +150,28 @@ def sync_library(
check_image_on_registry: bool = typer.Option(
False, help="Whether to check registry for existing image"
),
registry_pat: str = typer.Option(
"", envvar="REGISTRY_PAT", help="Docker registry personal access token"
registry: OptionalStr = typer.Option(
None, envvar="REGISTRY", help="Docker registry"
),
registry_user: str = typer.Option(
"", envvar="REGISTRY_USER", help="Docker registry username"
namespace: OptionalStr = typer.Option(
None, envvar="NAMESPACE", help="Docker namespace use by the images"
),
registry_pat: OptionalStr = typer.Option(
None, envvar="REGISTRY_PAT", help="Docker registry personal access token"
),
):
"""
Synchronize the module library to Sekoia.io
"""
SyncLibrary(
playbook_url,
api_key,
modules_path,
registry_pat,
registry_user,
module,
check_image_on_registry,
playbook_url=playbook_url,
api_key=api_key,
modules_path=modules_path,
module=module,
registry_check=check_image_on_registry,
registry=registry,
namespace=namespace,
registry_pat=registry_pat,
).execute()


Expand Down
67 changes: 22 additions & 45 deletions sekoia_automation/scripts/sync_library.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
"""
Load module library into database via the API
"""
import base64
import json
import re
from base64 import b64encode
Expand All @@ -12,25 +13,25 @@

import requests
import typer
from requests.auth import HTTPBasicAuth
from rich import print


class SyncLibrary:
DOCKER_PREFIX = "automation-module-"
DOCKER_REGISTRY = "ghcr.io"
DOCKER_NAMESPACE = "sekoia-io"
DOCKER_PREFIX = "automation-module"

def __init__(
self,
playbook_url: str,
api_key: str,
modules_path: Path,
registry_pat: str | None = None,
registry_user: str | None = None,
module: str = "",
registry_check: bool = False,
registry: str | None = None,
namespace: str | None = None,
registry_pat: str | None = None,
):
self.registry_pat = registry_pat
self.registry_user = registry_user
self.playbook_url = playbook_url
self.api_key = api_key
self.headers = {
Expand All @@ -39,7 +40,12 @@ def __init__(
}
self.modules_path = modules_path
self.module = module

self.registry_check = registry_check
self.registry = registry or self.DOCKER_REGISTRY
self.namespace = namespace or self.DOCKER_NAMESPACE
# In case of a public image any base64 encoded token works
self.registry_pat = registry_pat or "none"

def pprint(
self, created: list, updated: list, up_to_date: list, errors: list, nb_tabs: int
Expand Down Expand Up @@ -263,60 +269,37 @@ def get_module_logo(self, module_path: Path) -> str | None:
with path_to_use.open("rb") as f:
return f"{prefix}{b64encode(f.read()).decode('utf-8')}"

def check_image_on_registry(
self, docker_image: str, docker_image_version: str
) -> bool:
def check_image_on_registry(self, docker_image: str, version: str) -> bool:
"""Checks if a Docker image exists on a registry
If no registry is specified in the Module's manifest, we use a default value
If the docker image name in the Module's manifest begins with a registry,
this custom registry is used for the verification instead
An image is considered to contain the registry path if it contains at least 2 /
They delimit the path and the pathinfo fields
e.g. my_registry.com/v2/my_docker_image
e.g. my_registry.com/sekoia-io/my_docker_image
Args:
docker_image (str): Docker image name as specified in the manifest
docker_image_version (str): Docker image version
version (str): Docker image version
Returns:
bool: True if the image exists on the registry or if we don't have access
to a registry
False otherwise
"""
assert self.registry_user and self.registry_pat
auth = HTTPBasicAuth(self.registry_user, self.registry_pat)

if match := re.match(r"(.*?)/(.*)/(.*)", docker_image):
registry_path = match[1]
registry_pathinfo = match[2]
registry = match[1]
namespace = match[2]
image_name = match[3]
else:
registry_path = "ghcr.io"
registry_pathinfo = "v2/sekoialab"
registry = self.registry
namespace = self.namespace
image_name = docker_image

token = base64.b64encode(self.registry_pat.encode()).decode()
response = requests.get(
f"https://{registry_path}/token",
params={
"service": registry_path,
"scope": "repository:<repo>:pull",
"client_id": "symphony-docker-image",
},
auth=auth,
)
if not response.ok:
print(
f"[bold red][!] Authentication against the docker registry "
f"failed with status {response.status_code}"
)
raise typer.Exit(code=1)

token = response.json()["token"]

response = requests.get(
f"https://{registry_path}/{registry_pathinfo}/{image_name}/manifests/\
{docker_image_version}",
f"https://{registry}/v2/{namespace}/{image_name}/manifests/{version}",
headers={
"Authorization": f"Bearer {token}",
"Accept": "application/vnd.docker.distribution.manifest.v2+json",
Expand Down Expand Up @@ -404,12 +387,6 @@ def execute(self):
Otherwise, it will attempt to load all modules present in the library path
specified
"""
if self.registry_check and not (self.registry_pat and self.registry_user):
print(
"[bold red][!] Credentials must be provided to check image in registry"
)
raise typer.Exit(code=1)

if not self.module:
library_path = self.modules_path.absolute()
print("Library path: ", library_path)
Expand All @@ -425,5 +402,5 @@ def _get_module_docker_name(self, manifest: dict) -> str:
if docker := manifest.get("docker"):
return docker
if slug := manifest.get("slug"):
return f"{self.DOCKER_PREFIX}{slug}"
return f"{self.registry}/{self.namespace}/{self.DOCKER_PREFIX}-{slug}"
raise ValueError("Impossible to generate image name")
Loading

0 comments on commit 6f7cb72

Please sign in to comment.