Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/main' into ROCKS-1453/reusable_t…
Browse files Browse the repository at this point in the history
…est_rock_workflow
  • Loading branch information
clay-lake committed Nov 26, 2024
2 parents b98d118 + a61aea3 commit 5912fd7
Show file tree
Hide file tree
Showing 5 changed files with 187 additions and 25 deletions.
3 changes: 2 additions & 1 deletion .github/workflows/Continuous-Testing.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,10 @@ jobs:
strategy:
fail-fast: false
matrix: ${{ fromJSON(needs.prepare-test-matrix.outputs.released-revisions-matrix) }}
uses: canonical/oci-factory/.github/workflows/Vulnerability-Scan.yaml@main
uses: ./.github/workflows/Vulnerability-Scan.yaml
with:
oci-image-name: "${{ matrix.source-image }}"
oci-image-path: "oci/${{ matrix.name }}"
date-last-scan: ${{ needs.prepare-test-matrix.outputs.last-scan }}
is-from-release: true
secrets: inherit
67 changes: 50 additions & 17 deletions .github/workflows/Vulnerability-Scan.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,12 @@ on:
description: "If there are new CVEs after this date, we notify"
required: false
type: string
default: "9999-12-31T23:59:59"
default: '9999-12-31T23:59:59'
create-issue:
description: 'If to create a GitHub issues for found vulnerabilities'
required: false
type: boolean
default: false

env:
VULNERABILITY_REPORT_SUFFIX: ".vulnerability-report.json" # TODO: inherit string from caller
Expand Down Expand Up @@ -155,17 +160,25 @@ jobs:
issue:
name: "issue ${{ inputs.oci-image-name != '' && format('| {0}', inputs.oci-image-name) || ' '}}"
runs-on: ubuntu-22.04
needs: [parse-results]
needs: [parse-results, test-vulnerabilities]
env:
GITHUB_TOKEN: ${{ secrets.ROCKSBOT_TOKEN }}
if: ${{ !cancelled() && github.event_name != 'pull_request' }}
steps:
- uses: actions/checkout@v4

- uses: actions/setup-python@v5
with:
python-version: '3.x'

- run: pip install pydantic==2.8.2

- id: simplify-image-name
run: |
img_name=$(echo "${{ inputs.oci-image-name }}" | sed -r 's|.*/([a-zA-Z0-9-]+:[0-9.-]+)_[0-9]+|\1|')
echo "img_name=$img_name" >> "$GITHUB_OUTPUT"
img_name_with_tag=$(echo "${{ inputs.oci-image-name }}" | sed -r 's|.*/([a-zA-Z0-9-]+:[0-9.-]+_[0-9])+|\1|')
img_revision=$(echo "${img_name_with_tag}" | cut -d '_' -f 2)
echo "img_revision=$img_revision" >> "$GITHUB_OUTPUT"
echo "img_name_with_tag=$img_name_with_tag" >> "$GITHUB_OUTPUT"
# We assume that the sources within image.yaml are the same
- name: Get image repo
Expand All @@ -175,22 +188,38 @@ jobs:
echo "img-repo=$img_repo" >> "$GITHUB_OUTPUT"
# We have to walk through the vulnerabilities since trivy does not support outputting the results as Markdown
- name: Create Markdown Content
- name: Create markdown content
id: create-markdown
run: |
set -x
title="Vulnerabilities found for ${{ steps.simplify-image-name.outputs.img_name }}"
echo "## $title" > issue.md
echo "| ID | Target | Severity | Package |" >> issue.md
echo "| -- | ----- | -------- | ------- |" >> issue.md
echo '${{ needs.parse-results.outputs.vulnerabilities }}' | jq -r '.[] | "| \(.VulnerabilityID) | /\(.Target) | \(.Severity) | \(.PkgName) |"' >> issue.md
echo -e "\nDetails: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" >> issue.md
num_vulns=$(echo '${{ needs.parse-results.outputs.vulnerabilities }}' | jq -r 'length')
echo "issue-title=$title" >> "$GITHUB_OUTPUT"
echo "issue-body-file=issue.md" >> "$GITHUB_OUTPUT"
echo "vulnerability-exists=$([[ $num_vulns -gt 0 ]] && echo 'true' || echo 'false')" >> "$GITHUB_OUTPUT"
vulnerability_exists=$([[ $num_vulns -gt 0 ]] && echo 'true' || echo 'false')
echo "vulnerability-exists=$vulnerability_exists" >> "$GITHUB_OUTPUT"
if [[ $vulnerability_exists == 'true' ]]; then
title="Vulnerabilities found for ${{ steps.simplify-image-name.outputs.img_name_with_tag }}"
echo "## $title" > issue.md
echo "| ID | Target | Severity | Package |" >> issue.md
echo "| -- | ----- | -------- | ------- |" >> issue.md
echo '${{ needs.parse-results.outputs.vulnerabilities }}' | jq -r '.[] | "| \(.VulnerabilityID) | /\(.Target) | \(.Severity) | \(.PkgName) |"' >> issue.md
if [[ ${{ inputs.create-issue }} == 'true' ]]; then
revision_to_released_tags=$(python3 -m src.shared.release_info get_revision_to_released_tags --all-releases ${{ inputs.oci-image-path }}/_releases.json)
affected_tracks=$(echo "${revision_to_released_tags}" | jq -r '."${{ steps.simplify-image-name.outputs.img_revision }}" | map("- `\(.)`") | join("\n")')
echo -e "\n### Affected tracks:" >> issue.md
echo -e "${affected_tracks}" >> issue.md
echo -e "\nDetails: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" >> issue.md
fi
echo "issue-title=$title" >> "$GITHUB_OUTPUT"
echo "issue-body-file=issue.md" >> "$GITHUB_OUTPUT"
fi
- name: Write to summary
if: ${{ !inputs.create-issue && steps.create-markdown.outputs.vulnerability-exists == 'true' }}
run: |
echo "# Vulnerabilities found for ${{ inputs.oci-image-name }}" >> $GITHUB_STEP_SUMMARY
cat ${{ steps.create-markdown.outputs.issue-body-file }} | tail -n +2 >> $GITHUB_STEP_SUMMARY
- id: issue-exists
if: ${{ inputs.create-issue}}
run: |
issue_number=$(gh issue list --repo ${{ steps.get-image-repo.outputs.img-repo }} --json "number,title" \
| jq -r '.[] | select(.title == "${{ steps.create-markdown.outputs.issue-title }}") | .number')
Expand All @@ -210,7 +239,7 @@ jobs:
# | F | F | F | nop |

- name: Notify via GitHub issue
if: ${{ steps.create-markdown.outputs.vulnerability-exists == 'true' }}
if: ${{ steps.create-markdown.outputs.vulnerability-exists == 'true' && inputs.create-issue }}
run: |
set -x
op=nop
Expand All @@ -222,11 +251,15 @@ jobs:
fi
if [[ $op != 'nop' ]]; then
gh issue $op --repo ${{ steps.get-image-repo.outputs.img-repo }} \
--title "Vulnerabilities found for ${{ steps.simplify-image-name.outputs.img_name }}" \
--title "${{ steps.create-markdown.outputs.issue-title }}" \
--body-file "${{ steps.create-markdown.outputs.issue-body-file }}"
fi
- name: Close issue
if: ${{ needs.parse-results.result == 'success' && steps.issue-exists.outputs.issue-exists == 'true' && steps.create-markdown.outputs.vulnerability-exists == 'false' }}
if: |
needs.test-vulnerabilities.result == 'success' &&
steps.issue-exists.outputs.issue-exists == 'true' &&
steps.create-markdown.outputs.vulnerability-exists == 'false' &&
inputs.create-issue
run: |
gh issue close ${{ steps.issue-exists.outputs.issue-number }} --repo ${{ steps.get-image-repo.outputs.img-repo }}
14 changes: 7 additions & 7 deletions oci/mock-rock/_releases.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,31 +35,31 @@
"1.1-22.04": {
"end-of-life": "2030-05-01T00:00:00Z",
"candidate": {
"target": "853"
"target": "885"
},
"beta": {
"target": "853"
"target": "885"
},
"edge": {
"target": "853"
"target": "885"
}
},
"1-22.04": {
"end-of-life": "2030-05-01T00:00:00Z",
"candidate": {
"target": "853"
"target": "885"
},
"beta": {
"target": "853"
"target": "885"
},
"edge": {
"target": "853"
"target": "885"
}
},
"1.2-22.04": {
"end-of-life": "2030-05-01T00:00:00Z",
"beta": {
"target": "854"
"target": "886"
},
"edge": {
"target": "1.2-22.04_beta"
Expand Down
65 changes: 65 additions & 0 deletions src/shared/release_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@
data related to _release.json and revision tags.
"""

import argparse
import json
from collections import defaultdict

from ..image.utils.schema.triggers import KNOWN_RISKS_ORDERED


Expand Down Expand Up @@ -82,3 +85,65 @@ def get_revision_to_track(all_revisions_tags: list) -> dict:

revision_track[revision] = track
return revision_track


def _find_alias_revision(tag_mapping_from_all_releases: dict, rev: str, visited: set, tag: str) -> str:
if rev in visited:
raise BadChannel(
f"Tag {tag} was caught in a circular dependency, "
"following tags that follow themselves. Cannot pin a revision."
)
visited.add(rev)
if not rev.isdigit():
return _find_alias_revision(
tag_mapping_from_all_releases, tag_mapping_from_all_releases[rev], visited, tag
)
return rev

def get_revision_to_released_tags(all_releases: dict) -> dict:
"""
Iterates over the provided dictionary with all the releases
and extracts the revision numbers and their corresponding
released tags. The resulting dictionary maps each revision
number to a list of released tags.
"""
revision_to_released_tags = defaultdict(list)
tag_mapping_from_all_releases = get_tag_mapping_from_all_releases(all_releases)
for tag, revision in tag_mapping_from_all_releases.items():
if not revision.isdigit():
visited = set()
revision = _find_alias_revision(tag_mapping_from_all_releases, revision, visited, tag)
revision = int(revision)
revision_to_released_tags[revision].append(tag)

for revision, tags in revision_to_released_tags.items():
revision_to_released_tags[revision] = sorted(tags)

return dict(revision_to_released_tags)


def main():
parser = argparse.ArgumentParser()
parser.add_argument(
"function",
help="The function to run",
choices=["get_revision_to_released_tags"],
)

parser.add_argument(
"--all-releases",
help="Path to the _releases.json file",
)

args = parser.parse_args()

if args.function == "get_revision_to_released_tags":
print(
json.dumps(
(get_revision_to_released_tags(read_json_file(args.all_releases)))
)
)


if __name__ == "__main__":
main()
63 changes: 63 additions & 0 deletions tests/unit/test_shared_release_info.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import pytest

from src.shared.release_info import *


def test_get_revision_to_release_plain():
all_releases = {
"3.8-20.04": {
"end-of-life": "2025-03-31T00:00:00Z",
"edge": {"target": "43"},
"stable": {"target": "43"},
"candidate": {"target": "43"},
"beta": {"target": "43"},
}
}
assert get_revision_to_released_tags(all_releases) == {
43: [
"3.8-20.04_beta",
"3.8-20.04_candidate",
"3.8-20.04_edge",
"3.8-20.04_stable",
]
}


def test_get_revision_to_release_circular():
all_releases = {
"1.19.0-22.04": {
"end-of-life": "2024-11-26T00:00:00Z",
"stable": {"target": "1"},
"candidate": {"target": "1.19.0-22.04_beta"},
"beta": {"target": "1.19.0-22.04_candidate"},
"edge": {"target": "5"},
}
}

with pytest.raises(BadChannel, match=r"Tag .* was caught in a circular dependency, following tags that follow themselves. Cannot pin a revision."):
get_revision_to_released_tags(all_releases)


def test_get_revision_to_release_alias():
all_releases = {
"1.19.0-22.04": {
"end-of-life": "2024-11-26T00:00:00Z",
"stable": {"target": "1"},
"candidate": {"target": "5"},
"beta": {"target": "1.19.0-22.04_candidate"},
"edge": {"target": "5"},
},
"1-22.04": {
"end-of-life": "2025-05-12T00:00:00Z",
"stable": {"target": "4"},
"candidate": {"target": "1-22.04_stable"},
"beta": {"target": "1-22.04_candidate"},
"edge": {"target": "1-22.04_beta"},
},
}

assert get_revision_to_released_tags(all_releases) == {
1: ["1.19.0-22.04_stable"],
4: ["1-22.04_beta", "1-22.04_candidate", "1-22.04_edge", "1-22.04_stable"],
5: ["1.19.0-22.04_beta", "1.19.0-22.04_candidate", "1.19.0-22.04_edge"],
}

0 comments on commit 5912fd7

Please sign in to comment.