Skip to content

Commit

Permalink
Add helper classes and CLI tool to work with catalog image
Browse files Browse the repository at this point in the history
The set of classes is introduced that helps with catalog image parsing.
The classes automatically parse the catalog using 'opm render' command
and splits it into logical sub-classes that represents Catalog, Bundle,
Operator package, Channel.

The commit also introduces a CLI tool that simplifies working with
Catalog images locally and allows user to browse through catalog content
in terminal using human readable console output.

JIRA: ISV-5279

Signed-off-by: Ales Raszka <[email protected]>
  • Loading branch information
Allda committed Jan 3, 2025
1 parent c7b6080 commit d5a0233
Show file tree
Hide file tree
Showing 17 changed files with 1,719 additions and 3 deletions.
120 changes: 120 additions & 0 deletions docs/catalog_image_browser.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
# Catalog image browser
Working with catalog images locally might be a tricky task since it requires a lot of manual work.
To make it easier for maintainers and contributors, we have created a simple catalog image
browser CLI tool that allows you to browse and search for images in the catalog locally
in your terminal.

## Installation
To install the catalog browser, you will need a Python 3.10+ environment.

Then, you can install the tool using pip:

```bash
$ pip install git+https://github.com/redhat-openshift-ecosystem/operator-pipelines.git
```

Once install you can run the tool using the following command:

```bash
$ catalog-browser --help

usage: catalog-browser [-h] [--image IMAGE] [--rendered RENDERED] {list,show} ...

Browse and query index image content.

positional arguments:
{list,show} Commands
list List content in the index image.
show Show details of specific content.

options:
-h, --help show this help message and exit
--image IMAGE Path to the index image.
--rendered RENDERED Path to the rendered index image content.

```

## Usage
The browser requires one of 2 argument inputs: `--image` or `--rendered`. Using `--image` argument
the tool will pull the image and extract the content to a temporary directory. Based on catalog image
size the extraction might take a while. To avoid the rendering details you can render a catalog
in advance using `opm render` and then using `--rendered` argument to browse the content.

```bash
$ catalog-browser --image registry.redhat.io/redhat/community-operator-index:v4.16 list packages

# or
$ opm render -o yaml registry.redhat.io/redhat/community-operator-index:v4.16 > /tmp/v.4.16.yaml

$ catalog-browser--rendered /tmp/v.4.16.yaml list bundles
```

The browser supports 2 commands: `list` and `show`.

### List
The `list` command will list all packages, bundles or channels
```bash
$ catalog-browser --image registry.redhat.io/redhat/community-operator-index:v4.16 list packages 
3scale-community-operator
ack-acm-controller
ack-acmpca-controller
ack-apigateway-controller
ack-apigatewayv2-controller
ack-applicationautoscaling-controller
ack-athena-controller
...
```

```bash
$ catalog-browser --rendered /tmp/v.4.16.yaml list bundles  ✔ │
3scale-community-operator.v0.10.1
3scale-community-operator.v0.8.2
3scale-community-operator.v0.9.0
ack-acm-controller.v0.0.1
ack-acm-controller.v0.0.10
ack-acm-controller.v0.0.12
ack-acm-controller.v0.0.16
ack-acm-controller.v0.0.17
ack-acm-controller.v0.0.18
...
```

### Show
The `show` command will show details of a specific package, bundle or channel in
human readable format.

To show details of a package:
```bash
$ catalog-browser --rendered /tmp/v.4.16.yaml show package tempo-operator
Package: tempo-operator
Channels:
- tempo-operator/alpha
Bundles:
- tempo-operator.v0.1.0
- tempo-operator.v0.10.0
- tempo-operator.v0.11.0
- tempo-operator.v0.11.1
- tempo-operator.v0.12.0
- tempo-operator.v0.13.0
- tempo-operator.v0.14.0
- tempo-operator.v0.14.1
- tempo-operator.v0.14.2
- tempo-operator.v0.2.0
- tempo-operator.v0.3.0
- tempo-operator.v0.4.0
- tempo-operator.v0.5.0
- tempo-operator.v0.6.0
- tempo-operator.v0.7.0
- tempo-operator.v0.8.0
- tempo-operator.v0.9.0
```

To show details of a bundle:
```bash
$ catalog-browser --rendered /tmp/v.4.16.yaml show bundle snyk-operator.v1.90.2
Bundle: snyk-operator.v1.90.2
Package: snyk-operator
Image: quay.io/openshift-community-operators/snyk-operator@sha256:daf143ff1e9fbcf9bbbb350f8aab8593a1a35f693b0118c06a6b84c89a474397
Channels:
- snyk-operator/stable
```
1 change: 1 addition & 0 deletions mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ nav:
- "OKD/OpenShift Catalogs criteria and options": "users/packaging-required-criteria-ocp.md"
- "Admin":
- "Admin guide": "pipeline-admin-guide.md"
- "Catalog image browser": "catalog_image_browser.md"
theme:
name: material
features:
Expand Down
3 changes: 3 additions & 0 deletions operator-pipeline-images/operatorcert/catalog/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
"""
This package contains the code for the operator-catalog image.
"""
86 changes: 86 additions & 0 deletions operator-pipeline-images/operatorcert/catalog/bundle.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
"""Module representing a bundle in the catalog."""

from typing import Any


class CatalogBundle:
"""
A class representing a bundle in the catalog.
"""

def __init__(self, definition: dict[str, Any], catalog: Any):
self.definition = definition
self.catalog = catalog

@property
def name(self) -> str:
"""
A bundle name property.
Returns:
str: A bundle name.
"""
return self.definition.get("name", "") or ""

@property
def package_name(self) -> str:
"""
A package name property.
Returns:
str: A name of a bundle package.
"""
return self.definition.get("package", "") or ""

@property
def image(self) -> str:
"""
A bundle image property.
Returns:
str: A bundle image pull spec as defined in the catalog.
"""
return self.definition.get("image", "") or ""

def __str__(self) -> str:
"""
String representation of a bundle.
Returns:
str: A name of a bundle.
"""
return self.name

def __repr__(self) -> str:
"""
A representation of a bundle.
Returns:
str: A representation of a bundle class.
"""
return f"CatalogBundle({self.name})"

def get_channels(self) -> list[Any]:
"""
Get all channels for a bundle.
Returns:
list: A list of channels where the bundle is available.
"""
channels = []
for channel in self.catalog.get_all_channels():
if channel.package_name == self.package_name:
channels.append(channel)
return channels

def print(self) -> None:
"""
Print details of a bundle in a formatted way.
"""
print(f"Bundle: {self}")
print(f"Package: {self.catalog.get_package(self.package_name)}")
print(f"Image: {self.image}")

print("Channels:")
for channel in self.get_channels():
print(f" - {channel}")
Loading

0 comments on commit d5a0233

Please sign in to comment.