Skip to content
Open
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
6 changes: 3 additions & 3 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ jobs:

- name: Remove 'content-ok' label
uses: actions/github-script@v3
if: ${{ steps.check_pr_content.outcome == 'failure'}}
if: ${{ steps.check_pr_content.outcome == 'failure' && contains( github.event.pull_request.labels.*.name, 'content-ok') }}
continue-on-error: true
with:
github-token: ${{secrets.GITHUB_TOKEN}}
Expand All @@ -138,7 +138,7 @@ jobs:

- name: Remove 'authorized-request' label from PR
uses: actions/github-script@v3
if: ${{ steps.check_build_required.outputs.run-build == 'true' }}
if: ${{ steps.check_build_required.outputs.run-build == 'true' && contains( github.event.pull_request.labels.*.name, 'authorized-request') }}
continue-on-error: true
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
Expand Down Expand Up @@ -225,7 +225,7 @@ jobs:
KUBECONFIG: /tmp/ci-kubeconfig
run: |
API_SERVER=$( echo -n ${{ secrets.API_SERVER }} | base64 -d)
oc login --token=${{ secrets.CLUSTER_TOKEN }} --server=${API_SERVER}
oc login --token=${{ secrets.CLUSTER_TOKEN }} --server=${API_SERVER} --insecure-skip-tls-verify=${{ steps.set-env.outputs.insecure_skip_tls_verify }}
ve1/bin/sa-for-chart-testing --delete charts-${{ github.event.number }}

- name: Save PR artifact
Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -105,10 +105,10 @@ jobs:
echo "Full test in pr : {{steps.check_request.outputs.full_tests_in_pr }}"
if ${{steps.check_if_release_pr.outputs.charts_release_branch == 'true' || steps.check_request.outputs.full_tests_in_pr == 'true' }} ; then
echo "Release PR from dev to charts, oer PR with new full test, so running full tests"
ve1/bin/pytest tests/ --log-cli-level=WARNING --ignore=tests/functional/step_defs/test_smoke_scenarios.py --ignore=tests/functional/step_defs/test_submitted_charts.py --tb=short
ve1/bin/behave tests/functional/behave_features/ --tags=full --logging-level=WARNING --no-capture --no-color
else
echo "Not a release PR from dev to charts, so running only smoke tests"
ve1/bin/pytest tests/functional/step_defs/test_smoke_scenarios.py --log-cli-level=WARNING --tb=short
ve1/bin/behave tests/functional/behave_features/ --tags=smoke --logging-level=WARNING --no-capture --no-color
fi

- name: (Manual) Test CI Workflow
Expand All @@ -130,7 +130,7 @@ jobs:
echo "[INFO] Notify ID '${{ env.NOTIFY_ID }}'"
echo "[INFO] Software Name '${{ env.SOFTWARE_NAME }}'"
echo "[INFO] Software Version '${{ env.SOFTWARE_VERSION }}'"
ve1/bin/pytest tests/functional/step_defs/test_submitted_charts.py --log-cli-level=WARNING --tb=short
ve1/bin/behave tests/functional/behave_features/ --tags=version-change --logging-level=WARNING --no-capture --no-color

- name: Approve PR
id: approve_pr
Expand Down
168 changes: 112 additions & 56 deletions .github/workflows/version_check.yml

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions scripts/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,4 @@ toml==0.10.2
urllib3==1.26.5
websocket-client==1.2.1
analytics-python==1.4.0
behave==1.2.6
41 changes: 25 additions & 16 deletions scripts/src/chartrepomanager/indexannotations.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,22 @@
import sys
import semantic_version
import requests
import yaml

sys.path.append('../')
from report import report_info

kubeOpenShiftVersionMap = {"1.13": "4.1",
"1.14": "4.2",
"1.16": "4.3",
"1.17": "4.4",
"1.18": "4.5",
"1.19": "4.6",
"1.20": "4.7",
"1.21": "4.8",
"1.22": "4.9"}
kubeOpenShiftVersionMap = {}

def getKubVersionMap():

if not kubeOpenShiftVersionMap:
content = requests.get("https://github.com/redhat-certification/chart-verifier/blob/main/internal/tool/kubeOpenShiftVersionMap.yaml?raw=true")
version_data = yaml.safe_load(content.text)
for kubeVersion in version_data["versions"]:
kubeOpenShiftVersionMap[kubeVersion["kube-version"]] = kubeVersion["ocp-version"]

return kubeOpenShiftVersionMap


def getOCPVersions(kubeVersion):
Expand Down Expand Up @@ -56,21 +60,26 @@ def getOCPVersions(kubeVersion):

minOCP = ""
maxOCP = ""
getKubVersionMap()
for kubeVersionKey in kubeOpenShiftVersionMap :
#print(f"\n Map entry : {kubeVersionKey}: {kubeOpenShiftVersionMap[kubeVersionKey]}")
#print(f" MinOCP : {minOCP}, maxOCP: {maxOCP}")
coercedKubeVersionKey = semantic_version.Version.coerce(kubeVersionKey)
if minOCP == "" and coercedKubeVersionKey in semantic_version.NpmSpec(checkKubeVersion):
minOCP = kubeOpenShiftVersionMap[kubeVersionKey]
print(f" Found min : {kubeVersion}: {minOCP}")
elif coercedKubeVersionKey in semantic_version.NpmSpec(checkKubeVersion):
maxOCP = kubeOpenShiftVersionMap[kubeVersionKey]
print(f" Found new Max : {kubeVersion}: {maxOCP}")
if coercedKubeVersionKey in semantic_version.NpmSpec(checkKubeVersion):
coercedOCPVersionValue = semantic_version.Version.coerce(kubeOpenShiftVersionMap[kubeVersionKey])
if minOCP == "" or semantic_version.Version.coerce(minOCP) > coercedOCPVersionValue:
minOCP = kubeOpenShiftVersionMap[kubeVersionKey]
#print(f" Found new min : {checkKubeVersion}: {minOCP}")
if maxOCP == "" or semantic_version.Version.coerce(maxOCP) < coercedOCPVersionValue:
maxOCP = kubeOpenShiftVersionMap[kubeVersionKey]
#print(f" Found new Max : {checkKubeVersion}: {maxOCP}")

# check if minOCP is open ended
if minOCP != "" and semantic_version.Version("1.999.999") in semantic_version.NpmSpec(checkKubeVersion):
ocp_versions = f">={minOCP}"
elif minOCP == "":
ocp_versions = "N/A"
elif maxOCP == "":
elif maxOCP == "" or maxOCP == minOCP:
ocp_versions = minOCP
else:
ocp_versions = f"{minOCP} - {maxOCP}"
Expand Down
24 changes: 21 additions & 3 deletions scripts/src/checkprcontent/checkpr.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,24 @@ def check_provider_delivery(report_in_pr,num_files_in_pr,report_file_match):
print(f"::set-output name=providerDelivery::False")
print(f"[INFO] providerDelivery is a no-go")

def get_file_match_compiled_patterns():
"""Return a tuple of patterns, where the first can be used to match any file in a chart PR
and the second can be used to match a valid report file within a chart PR. The patterns
match based on the relative path of a file to the base repository

Both patterns capture chart type, chart vendor, chart name and chart version from the file path..

Examples of valid file paths are:

charts/partners/hashicorp/vault/0.20.0/<file>
charts/partners/hashicorp/vault/0.20.0//report.yaml
"""

pattern = re.compile(r"charts/"+TYPE_MATCH_EXPRESSION+"/([\w-]+)/([\w-]+)/([\w\.-]+)/.*")
reportpattern = re.compile(r"charts/"+TYPE_MATCH_EXPRESSION+"/([\w-]+)/([\w-]+)/([\w\.-]+)/report.yaml")

return pattern,reportpattern


def ensure_only_chart_is_modified(api_url, repository, branch):
# api_url https://api.github.com/repos/<organization-name>/<repository-name>/pulls/1
Expand All @@ -95,8 +113,7 @@ def ensure_only_chart_is_modified(api_url, repository, branch):
files_api_url = f'{api_url}/files'
headers = {'Accept': 'application/vnd.github.v3+json'}
r = requests.get(files_api_url, headers=headers)
pattern = re.compile(r"charts/"+TYPE_MATCH_EXPRESSION+"/([\w-]+)/([\w-]+)/([\w\.-]+)/.*")
reportpattern = re.compile(r"charts/"+TYPE_MATCH_EXPRESSION+"/([\w-]+)/([\w-]+)/([\w\.-]+)/report.yaml")
pattern,reportpattern = get_file_match_compiled_patterns()
page_number = 1
max_page_size,page_size = 100,100
matches_found = 0
Expand Down Expand Up @@ -129,9 +146,10 @@ def ensure_only_chart_is_modified(api_url, repository, branch):
if matches_found == 1:
pattern_match = match
elif pattern_match.groups() != match.groups():
msg = f"[ERROR] PR must only include one chart"
msg = "[ERROR] A PR must contain only one chart. Current PR includes files for multiple charts."
print(msg)
print(f"::set-output name=pr-content-error-message::{msg}")
exit(1)

if none_chart_files:
if file_count > 1 or "OWNERS" not in none_chart_files: #OWNERS not present or preset but not the only file
Expand Down
118 changes: 105 additions & 13 deletions scripts/src/indexfile/index.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,30 +2,30 @@
import json
import requests
import yaml
import semantic_version
import sys

def _make_http_request(method, url, body=None, params={}, headers={}, verbose=False):
method_map = {"get": requests.get,
"post": requests.post,
"put": requests.put,
"delete": requests.delete,
"patch": requests.patch}
request_method = method_map[method]
response = request_method(url, params=params, headers=headers, json=body)
sys.path.append('../')
from chartrepomanager import indexannotations

INDEX_FILE = "https://charts.openshift.io/index.yaml"

def _make_http_request(url, body=None, params={}, headers={}, verbose=False):
response = requests.get(url, params=params, headers=headers, json=body)
if verbose:
print(json.dumps(headers, indent=4, sort_keys=True))
print(json.dumps(body, indent=4, sort_keys=True))
print(json.dumps(params, indent=4, sort_keys=True))
print(response.text)
return response.text

def _load_index_yaml(url):

yaml_text = _make_http_request('get', url)
def _load_index_yaml():
yaml_text = _make_http_request(INDEX_FILE)
dct = yaml.safe_load(yaml_text)
return dct

def get_chart_info(tar_name):
index_dct = _load_index_yaml("https://charts.openshift.io/index.yaml")
index_dct = _load_index_yaml()
for entry, charts in index_dct["entries"].items():
if tar_name.startswith(entry):
for chart in charts:
Expand All @@ -38,5 +38,97 @@ def get_chart_info(tar_name):
print(f"[INFO] match not found: {tar_name}")
return "","","",""

def get_charts_info():
chart_info_list = []

index_dct = _load_index_yaml()
for entry, charts in index_dct["entries"].items():
for chart in charts:
chart_info = {}
chart_info["name"] = chart['name']
chart_info["version"] = chart["version"]
chart_info["providerType"] = chart["annotations"]["charts.openshift.io/providerType"]
chart_info["provider"] = entry.removesuffix(f'-{chart["name"]}')
#print(f'[INFO] found chart : {chart_info["provider"]} {chart["name"]} {chart["version"]} ')
if 'charts.openshift.io/supportedOpenShiftVersions' in chart["annotations"]:
chart_info["supportedOCP"] = chart["annotations"]["charts.openshift.io/supportedOpenShiftVersions"]
else:
chart_info["supportedOCP"] = ""
if "kubeVersion" in chart:
chart_info["kubeVersion"] = chart["kubeVersion"]
else:
chart_info["kubeVersion"] =""
chart_info_list.append(chart_info)

return chart_info_list

def get_latest_charts():
chart_list = get_charts_info()

print(f"{len(chart_list)} charts found in Index file")

chart_in_process = {"name" : ""}
chart_latest_version = ""
latest_charts = []

for index,chart in enumerate(chart_list):
chart_name = chart["name"]
#print(f'[INFO] look for latest chart : {chart_name} {chart["version"]}')
if chart_name == chart_in_process["name"]:
new_version = semantic_version.Version.coerce(chart["version"])
#print(f' [INFO] compare chart versions : {new_version}({chart["version"]}) : {chart_latest_version}')
if new_version > chart_latest_version:
#print(f' [INFO] a new latest chart version : {new_version}')
chart_latest_version = new_version
chart_in_process = chart
else:
if chart_in_process["name"] != "":
#print(f' [INFO] chart completed : {chart_in_process["name"]} {chart_in_process["version"]}')
latest_charts.append(chart_in_process)

#print(f'[INFO] new chart found : {chart_name} {chart["version"]}')
chart_in_process = chart
chart_version = chart["version"]
if chart_version.startswith("v"):
chart_version = chart_version[1:]
chart_latest_version = semantic_version.Version.coerce(chart_version)
else:
chart_in_process = chart

if index+1 == len(chart_list):
#print(f' [INFO] last chart completed : {chart_in_process["name"]} {chart_in_process["version"]}')
latest_charts.append(chart_in_process)

return latest_charts


if __name__ == "__main__":
get_chart_info("redhat-dotnet-0.0.1")
get_chart_info("redhat-dotnet-0.0.1")

chart_list = get_latest_charts()

for chart in chart_list:
print(f'[INFO] found latest chart : {chart["name"]} {chart["version"]}')


OCP_VERSION = semantic_version.Version.coerce("4.11")

for chart in chart_list:
if "supportedOCP" in chart and chart["supportedOCP"] != "N/A" and chart["supportedOCP"] != "":
if OCP_VERSION in semantic_version.NpmSpec(chart["supportedOCP"]):
print(f'PASS: Chart supported OCP version {chart["supportedOCP"]} includes: {OCP_VERSION}')
else:
print(f' ERROR: Chart supported OCP version {chart["supportedOCP"]} does not include {OCP_VERSION}')
elif "kubeVersion" in chart and chart["kubeVersion"] != "":
supportedOCPVersion = indexannotations.getOCPVersions(chart["kubeVersion"])
if OCP_VERSION in semantic_version.NpmSpec(supportedOCPVersion):
print(f'PASS: Chart kubeVersion {chart["kubeVersion"]} (OCP: {supportedOCPVersion}) includes OCP version: {OCP_VERSION}')
else:
print(f' ERROR: Chart kubeVersion {chart["kubeVersion"]} (OCP: {supportedOCPVersion}) does not include {OCP_VERSION}')







8 changes: 6 additions & 2 deletions scripts/src/pullrequest/prartifact.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,15 @@

import requests

sys.path.append('../')
from checkprcontent import checkpr

# TODO(baijum): Move this code under chartsubmission.chart module
def get_modified_charts(api_url):
files_api_url = f'{api_url}/files'
headers = {'Accept': 'application/vnd.github.v3+json'}
r = requests.get(files_api_url, headers=headers)
pattern = re.compile(r"charts/(\w+)/([\w-]+)/([\w-]+)/([\w\.]+)/.*")
count = 0
pattern,_ = checkpr.get_file_match_compiled_patterns()
for f in r.json():
m = pattern.match(f["filename"])
if m:
Expand All @@ -25,9 +27,11 @@ def get_modified_charts(api_url):

def save_metadata(directory, vendor_label, chart, number):
with open(os.path.join(directory, "vendor"), "w") as fd:
print(f"add {directory}/vendor as {vendor_label}")
fd.write(vendor_label)

with open(os.path.join(directory, "chart"), "w") as fd:
print(f"add {directory}/chart as {chart}")
fd.write(chart)

with open(os.path.join(directory, "NR"), "w") as fd:
Expand Down
26 changes: 21 additions & 5 deletions scripts/src/saforcharttesting/saforcharttesting.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import argparse
import subprocess
import tempfile
import re
from string import Template

namespace_template = """\
Expand Down Expand Up @@ -211,7 +212,8 @@ def delete_clusterrolebinding(name):
sys.exit(1)

def write_sa_token(namespace, token):
sa_found = False
secret_found = False
secrets = []
for i in range(7):
out = subprocess.run(["oc", "get", "serviceaccount", namespace, "-n", namespace, "-o", "json"], capture_output=True)
stdout = out.stdout.decode("utf-8")
Expand All @@ -223,15 +225,29 @@ def write_sa_token(namespace, token):
else:
sa = json.loads(stdout)
if len(sa["secrets"]) >= 2:
sa_found = True
secrets = sa["secrets"]
secret_found = True
break
time.sleep(10)
else:
pattern = r'Tokens:\s+([A-Za-z0-9-]+)'
dout = subprocess.run(["oc", "describe", "serviceaccount", namespace, "-n", namespace], capture_output=True)
dstdout = dout.stdout.decode("utf-8")
match = re.search(pattern, dstdout)
if match:
token_name = match.group(1)
else:
print("[ERROR] Token not found, Exiting")
sys.exit(1)
secrets.append({"name": token_name})
secret_found = True
break
time.sleep(10)

if not sa_found:
if not secret_found:
print("[ERROR] retrieving ServiceAccount:", namespace, stderr)
sys.exit(1)

for secret in sa["secrets"]:
for secret in secrets:
out = subprocess.run(["oc", "get", "secret", secret["name"], "-n", namespace, "-o", "json"], capture_output=True)
stdout = out.stdout.decode("utf-8")
if out.returncode != 0:
Expand Down
Loading