Skip to content

Commit

Permalink
Add checks for chart in index, and for tag in repo (#347)
Browse files Browse the repository at this point in the history
This commit ports the two following checks, performed at the end of the
checkpr script, to the Chart data structure:
* Check that the Chart in this version is not already present in the
  index
* Check that the release tag for this chart doesn't already exists on a
  given GitHub repository

Signed-off-by: Matthias Goerens <mgoerens@redhat.com>
  • Loading branch information
mgoerens authored Jul 8, 2024
1 parent 3c094bf commit 919146b
Show file tree
Hide file tree
Showing 2 changed files with 210 additions and 0 deletions.
76 changes: 76 additions & 0 deletions scripts/src/precheck/submission.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
import os
import re
import requests
import semver
import yaml

try:
from yaml import CLoader as Loader
except ImportError:
from yaml import Loader

from dataclasses import dataclass, field

Expand Down Expand Up @@ -36,6 +43,14 @@ class WebCatalogOnlyError(SubmissionError):
pass


class HelmIndexError(SubmissionError):
pass


class ReleaseTagError(SubmissionError):
pass


@dataclass
class Chart:
"""Represents a Helm Chart
Expand Down Expand Up @@ -73,6 +88,53 @@ def register_chart_info(self, category, organization, name, version):
def get_owners_path(self):
return f"charts/{self.category}/{self.organization}/{self.name}/OWNERS"

def get_release_tag(self):
return f"{self.organization}-{self.name}-{self.version}"

def check_index(self, index):
"""Check if the chart is present in the Helm index
Args:
index (dict): Content of the Helm repo index
Raise:
HelmIndexError if:
* The provided index is malformed
* The Chart is already present in the index
"""
try:
chart_entry = index["entries"].get(self.name, [])
except KeyError as e:
raise HelmIndexError(f"Malformed index {index}") from e

for chart in chart_entry:
if chart["version"] == self.version:
msg = f"[ERROR] Helm chart release already exists in the index.yaml: {self.version}"
raise HelmIndexError(msg)

def check_release_tag(self, repository: str):
"""Check for the existence of the chart's release tag on the provided repository.
Args:
repository (str): Name of the GitHub repository to check for existing tag.
(e.g. "openshift-helm-charts/charts")
Raise: ReleaseTagError if the tag already exists in the GitHub repo.
"""
tag_name = self.get_release_tag()
tag_api = f"https://api.github.com/repos/{repository}/git/ref/tags/{tag_name}"
headers = {
"Accept": "application/vnd.github.v3+json",
"Authorization": f'Bearer {os.environ.get("BOT_TOKEN")}',
}
print(f"[INFO] checking tag: {tag_api}")
r = requests.head(tag_api, headers=headers)
if r.status_code == 200:
msg = f"[ERROR] Helm chart release already exists in the GitHub Release/Tag: {tag_name}"
raise ReleaseTagError(msg)


@dataclass
class Report:
Expand Down Expand Up @@ -439,3 +501,17 @@ def get_file_type(file_path):
return "owners", owners_match

return "unknwown", None


def download_index_data(repository, branch="gh_pages"):
"""Download the helm repository index"""
r = requests.get(
f"https://raw.githubusercontent.com/{repository}/{branch}/index.yaml"
)

if r.status_code == 200:
data = yaml.load(r.text, Loader=Loader)
else:
data = {}

return data
134 changes: 134 additions & 0 deletions scripts/src/precheck/submission_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -618,3 +618,137 @@ def test_is_valid_web_catalog_only(test_scenario):
test_scenario.input_submission.is_valid_web_catalog_only(repo_path=temp_dir)
== test_scenario.expected_output
)


def create_new_index(charts: list[submission.Chart] = []):
"""Create the JSON representation of a Helm chart index containing the provided list of charts
The resulting index only contains the required information for the check_index to work.
"""
index = {"apiVersion": "v1", "entries": {}}

for chart in charts:
chart_entries = index["entries"].get(chart.name, [])
chart_entries.append(
{
"name": f"{chart.name}",
"version": f"{chart.version}",
}
)

index["entries"][chart.name] = chart_entries

return index


@dataclass
class CheckIndexScenario:
chart: submission.Chart = field(
default_factory=lambda: submission.Chart(
category=expected_category,
organization=expected_organization,
name=expected_name,
version=expected_version,
)
)
index: dict = field(default_factory=lambda: create_new_index())
excepted_exception: contextlib.ContextDecorator = field(
default_factory=lambda: contextlib.nullcontext()
)


scenarios_check_index = [
# Chart is not present in the index
CheckIndexScenario(
index=create_new_index([submission.Chart(name="not-awesome", version="0.42")])
),
# Chart is present but does not contain submitted version
CheckIndexScenario(
index=create_new_index([submission.Chart(name=expected_name, version="0.42")])
),
# Submitted version is present in index
CheckIndexScenario(
index=create_new_index(
[submission.Chart(name=expected_name, version=expected_version)]
),
excepted_exception=pytest.raises(
submission.HelmIndexError,
match="Helm chart release already exists in the index.yaml",
),
),
# Index is empty
CheckIndexScenario(),
# Index is an empty dict
CheckIndexScenario(
index={},
excepted_exception=pytest.raises(
submission.HelmIndexError, match="Malformed index"
),
),
]


@pytest.mark.parametrize("test_scenario", scenarios_check_index)
def test_check_index(test_scenario):
with test_scenario.excepted_exception:
test_scenario.chart.check_index(test_scenario.index)


@dataclass
class CheckReleaseTagScenario:
chart: submission.Chart = field(
default_factory=lambda: submission.Chart(
category=expected_category,
organization=expected_organization,
name=expected_name,
version=expected_version,
)
)
exising_tags: list[str] = field(default_factory=lambda: list())
excepted_exception: contextlib.ContextDecorator = field(
default_factory=lambda: contextlib.nullcontext()
)


scenarios_check_release_tag = [
# A release doesn't exist for this org
CheckReleaseTagScenario(exising_tags=["notacme-notawesome-0.42"]),
# A release exist for this org, but not for this chart
CheckReleaseTagScenario(exising_tags=[f"{expected_organization}-notawesome-0.42"]),
# A release exist for this Chart but not in this version
CheckReleaseTagScenario(
exising_tags=[f"{expected_organization}-{expected_name}-0.42"],
),
# A release exist for this Chart in this version
CheckReleaseTagScenario(
exising_tags=[f"{expected_organization}-{expected_name}-{expected_version}"],
excepted_exception=pytest.raises(
submission.ReleaseTagError,
match="Helm chart release already exists in the GitHub Release/Tag",
),
),
]


@pytest.mark.parametrize("test_scenario", scenarios_check_release_tag)
@responses.activate
def test_check_release_tag(test_scenario):
chart_release_tag = test_scenario.chart.get_release_tag()

if chart_release_tag not in test_scenario.exising_tags:
responses.head(
f"https://api.github.com/repos/my-fake-org/my-fake-repo/git/ref/tags/{chart_release_tag}",
# json=[{"filename": file} for file in test_scenario.modified_files],
status=404,
)

for tag in test_scenario.exising_tags:
# Mock GitHub API
responses.head(
f"https://api.github.com/repos/my-fake-org/my-fake-repo/git/ref/tags/{tag}",
# json=[{"filename": file} for file in test_scenario.modified_files],
)

with test_scenario.excepted_exception:
test_scenario.chart.check_release_tag(repository="my-fake-org/my-fake-repo")

0 comments on commit 919146b

Please sign in to comment.