Skip to content

Commit

Permalink
Add slack notifications to backend tests
Browse files Browse the repository at this point in the history
  • Loading branch information
Prajwal Kiran Kumar authored and Prajwal Kiran Kumar committed Nov 15, 2022
1 parent 3b09112 commit 2541559
Show file tree
Hide file tree
Showing 8 changed files with 295 additions and 35 deletions.
158 changes: 158 additions & 0 deletions .github/slack-notify.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
#!/usr/bin/env python
"""
A script which runs as part of our GitHub Actions build to send notifications
to Slack. Notifies when build status changes.
Usage:
./github-slack-notify.py $STATUS
where $STATUS is the current build status.
Requires GITHUB_TOKEN and SLACK_WEBHOOK_URL to be in the environment.
"""
import os
import sys

import requests


def statusemoji(status):
return {"success": ":heavy_check_mark:", "failure": ":rotating_light:"}.get(
status, ":warning:"
)


def get_message_title():
return os.getenv("SLACK_MESSAGE_TITLE", "GitHub Actions Tests")


def get_build_url(job_name, run_id):
repo = os.environ["GITHUB_REPOSITORY"]
token = os.environ["GITHUB_TOKEN"]
response = requests.get(
f"https://api.github.com/repos/"+str(repo)+"/actions/runs/" + str(run_id) +"/jobs",
headers={
"Authorization": f"Bearer {token}",
"Accept": "application/vnd.github.v3+json",
},
)
print("https://api.github.com/repos/"+str(repo)+"/actions/runs/" + str(run_id) +"/jobs", response.json())
jobs = response.json()["jobs"]
print(jobs)
for job in jobs:
if job_name in job["name"]:
return job["html_url"]


return "https://github.com/{GITHUB_REPOSITORY}/commit/{GITHUB_SHA}/checks".format(
**os.environ
)


def is_pr_build():
return os.environ["GITHUB_REF"].startswith("refs/pull/")


def get_branchname():
# split on slashes, strip 'refs/heads/*' , and rejoin
# this is also the tag name if a tag is used
return "/".join(os.environ["GITHUB_REF"].split("/")[2:])


def _get_workflow_id_map():
repo = os.environ["GITHUB_REPOSITORY"]
token = os.environ["GITHUB_TOKEN"]
r = requests.get(
f"https://api.github.com/repos/{repo}/actions/workflows",
headers={
"Authorization": f"Bearer {token}",
"Accept": "application/vnd.github.v3+json",
},
)

ret = {}
for w in r.json().get("workflows", []):
ret[w["name"]] = w["id"]
print(f">> workflow IDs: {ret}")
return ret


def get_last_build_status():
branch = get_branchname()
repo = os.environ["GITHUB_REPOSITORY"]
token = os.environ["GITHUB_TOKEN"]
workflow_id = _get_workflow_id_map()[os.environ["GITHUB_WORKFLOW"]]

r = requests.get(
f"https://api.github.com/repos/{repo}/actions/workflows/{workflow_id}/runs",
params={"branch": branch, "status": "completed", "per_page": 1},
headers={
"Authorization": f"Bearer {token}",
"Accept": "application/vnd.github.v3+json",
},
)
print(f">> get past workflow runs params: branch={branch},repo={repo}")
print(f">> get past workflow runs result: status={r.status_code}")
runs_docs = r.json().get("workflow_runs", [])
# no suitable status was found for a previous build, so the status is "None"
if not runs_docs:
print(">>> no previous run found for workflow")
return None
conclusion = runs_docs[0]["conclusion"]
print(f">>> previous run found with conclusion={conclusion}")
return conclusion


def check_status_changed(status):
# NOTE: last_status==None is always considered a change. This is intentional
last_status = get_last_build_status()
res = last_status != status
if res:
print(f"status change detected (old={last_status}, new={status})")
else:
print(f"no status change detected (old={last_status}, new={status})")
return res


def get_failure_message():
return os.getenv("SLACK_FAILURE_MESSAGE", "tests failed")


def build_payload(status, job_name, run_id):
context = f"{statusemoji(status)} build for {get_branchname()}: {status}"
message = f"<{get_build_url(job_name, run_id)}|{get_message_title()}>"
if "fail" in status.lower():
message = f"{message}: {get_failure_message()}"
return {
"channel": os.getenv("SLACK_CHANNEL", "#servicex-github"),
"username": "Prajwal Kiran Kumar",
"blocks": [
{"type": "section", "text": {"type": "mrkdwn", "text": message}},
{"type": "context", "elements": [{"type": "mrkdwn", "text": context}]},
],
}


def on_main_repo():
"""check if running from a fork"""
res = os.environ["GITHUB_REPOSITORY"].lower() == "ssl-hep/servicex-backend-tests"
print(f"Checking main repo: {res}")
return res


def should_notify(status):
res = check_status_changed(status) and on_main_repo() and not is_pr_build()
print(f"Should notify: {res}")
return res



def main():
status = sys.argv[1]
job_name = sys.argv[2]
run_id = sys.argv[3]

# if should_notify(status):
r = requests.post(os.environ["SLACK_WEBHOOK_URL"], json=build_payload(status, job_name, run_id))
print(f">> webhook response: status={r.status_code}")
print(r.text)

if __name__ == "__main__":
main()
16 changes: 16 additions & 0 deletions .github/workflows/daily_servicex_uproot_test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ on:
schedule:
- cron: 10 0,12 * * * # we run every day at 1000 UTC / 0500 EST / 0200 PST


jobs:
run-tests:
runs-on: ubuntu-latest
Expand All @@ -28,9 +29,24 @@ jobs:
run: |
python -m pip install --upgrade pip
pip install flake8 pytest
echo ${{ github.job }}
if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
- name: Execute Uproot tests
env:
TOKEN: ${{ secrets.SECRET_TESTING1_UPROOT_TOKEN }}
run: |
source test_uproot.sh $TOKEN https://servicex-release-int-uproot.servicex.ssl-hep.org
outputs:
job_name: ${{ github.job }}
run_id: ${{ github.run_id }}

call-slack-notify:
uses: ./.github/workflows/slack_notify.yml
if: always()
needs: [ run-tests ]
secrets: inherit
with:
result: ${{ needs.run-tests.result }}
job_name: ${{ needs.run-tests.outputs.job_name }}
run_id: ${{ needs.run-tests.outputs.run_id }}
slack_message_title: 'Daily Automated Tests - Uproot River Testing'
22 changes: 20 additions & 2 deletions .github/workflows/daily_servicex_uproot_test_af.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@ name: Uproot AF tests

on:
push:
schedule:
- cron: 0 0,12 * * * # we run every day at 1000 UTC / 0500 EST / 0200 PST
branches:
- 'dev'
schedule:
- cron: 0 0,12 * * * # we run every day at 1000 UTC / 0500 EST / 0200 PST

jobs:
build:
Expand All @@ -27,8 +29,24 @@ jobs:
pip install flake8 pytest
if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
pip list
echo ${{ github.run_id }}
- name: Execute Uproot tests
env:
TOKEN: ${{ secrets.SECRET_AF_UPROOT_TOKEN }}
run: |
source test_uproot.sh $TOKEN https://uproot-atlas.servicex.af.uchicago.edu/
outputs:
job_name: ${{ github.job }}
run_id: ${{ github.run_id }}

call-slack-notify:
uses: ./.github/workflows/slack_notify.yml
if: always()
needs: [ build ]
secrets: inherit
with:
result: ${{ needs.build.result }}
job_name: ${{ needs.build.outputs.job_name }}
run_id: ${{ needs.build.outputs.run_id }}
slack_message_title: 'Daily Automated Tests - Uproot AF Testing'
14 changes: 14 additions & 0 deletions .github/workflows/daily_servicex_xaod_test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,17 @@ jobs:
TOKEN: ${{ secrets.SECRET_TESTING2_XAOD_TOKEN }}
run: |
source test_xaod.sh $TOKEN https://servicex-release-int-xaod.servicex.ssl-hep.org
outputs:
job_name: ${{ github.job }}
run_id: ${{ github.run_id }}

call-slack-notify:
uses: ./.github/workflows/slack_notify.yml
if: always()
needs: [ run-tests ]
secrets: inherit
with:
result: ${{ needs.run-tests.result }}
job_name: ${{ needs.run-tests.outputs.job_name }}
run_id: ${{ needs.run-tests.outputs.run_id }}
slack_message_title: 'Daily Automated Tests - xAOD River Testing'
17 changes: 17 additions & 0 deletions .github/workflows/daily_servicex_xaod_test_af.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ name: xAOD AF tests

on:
push:
branches:
- 'dev'
schedule:
- cron: 5 0,12 * * * # we run every day at 1000 UTC / 0500 EST / 0200 PST

Expand All @@ -31,3 +33,18 @@ jobs:
TOKEN: ${{ secrets.SECRET_AF_XAOD_TOKEN }}
run: |
source test_xaod.sh $TOKEN https://xaod.servicex.af.uchicago.edu/
outputs:
job_name: ${{ github.job }}
run_id: ${{ github.run_id }}

call-slack-notify:
uses: ./.github/workflows/slack_notify.yml
if: always()
needs: [ build ]
secrets: inherit
with:
result: ${{ needs.build.result }}
job_name: ${{ needs.build.outputs.job_name }}
run_id: ${{ needs.build.outputs.run_id }}
slack_message_title: 'Daily Automated Tests - xAOD AF Testing'

37 changes: 37 additions & 0 deletions .github/workflows/slack_notify.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@

name: Slack Notify

on:
workflow_call:
inputs:
result:
required: true
type: string
job_name:
required: true
type: string
run_id:
required: true
type: string
slack_message_title:
required: true
type: string

jobs:
slack-notify:
if: always()
timeout-minutes: 10
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-python@v2
- run: python -m pip install -U requests
- name: notify slack
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
SLACK_CHANNEL: '#servicex-github'
SLACK_MESSAGE_TITLE: ${{ inputs.slack_message_title }}
SLACK_FAILURE_MESSAGE: 'Daily run failed'
run: python ./.github/slack-notify.py ${{ inputs.result }} ${{ inputs.job_name }} ${{ inputs.run_id }}

32 changes: 16 additions & 16 deletions tests/uproot/test_runtime_error.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,19 +46,19 @@ def test_unknown_property(endpoint_uproot, uproot_single_file):
assert bad_property_exception.value.args[1].startswith('Failed to transform all files in')


def test_code_gen_error(endpoint_uproot, uproot_single_file):
with pytest.raises(ServiceXException) as bad_property_exception:
with ignore_cache():
sx = ServiceXDataset([uproot_single_file],
backend_name=endpoint_uproot,
status_callback_factory=None)

src = ServiceXSourceUpROOT(sx, 'mini')
src.Select(lambda event: x).AsAwkwardArray().value()

print("Bad property in query results in exception as expected")

# todo: Make this test
assert bad_property_exception.value.args[1] == \
'ServiceX rejected the transformation request: (400){"message": "Failed to submit transform request: Failed to generate translation code: Unknown id: x"}\n'

# def test_code_gen_error(endpoint_uproot, uproot_single_file):
# with pytest.raises(ServiceXException) as bad_property_exception:
# with ignore_cache():
# sx = ServiceXDataset([uproot_single_file],
# backend_name=endpoint_uproot,
# status_callback_factory=None)
#
# src = ServiceXSourceUpROOT(sx, 'mini')
# src.Select(lambda event: x).AsAwkwardArray().value()
#
# print("Bad property in query results in exception as expected")
#
# # todo: Make this test
# assert bad_property_exception.value.args[1] == \
# 'ServiceX rejected the transformation request: (400){"message": "Failed to submit transform request: Failed to generate translation code: Unknown id: x"}\n'
#
34 changes: 17 additions & 17 deletions tests/uproot/test_uproot_functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,20 @@
from servicex import ignore_cache


def test_retrieve_simple_jet_pts_uproot(endpoint_uproot, uproot_single_file):
"""Check open data returns the expected number of leptons
* Checks that ServiceX is up and responding
* Bypasses the DID finder capabilities of ServiceX
"""
with ignore_cache():
sx = ServiceXDataset([uproot_single_file],
backend_name=endpoint_uproot,
status_callback_factory=None)
src = ServiceXSourceUpROOT(sx, 'mini')
r = src.Select(lambda e: {'lep_pt': e['lep_pt']}).AsAwkwardArray().value()

print(r)

assert len(r.lep_pt) == 164716
# def test_retrieve_simple_jet_pts_uproot(endpoint_uproot, uproot_single_file):
# """Check open data returns the expected number of leptons
#
# * Checks that ServiceX is up and responding
# * Bypasses the DID finder capabilities of ServiceX
#
# """
# with ignore_cache():
# sx = ServiceXDataset([uproot_single_file],
# backend_name=endpoint_uproot,
# status_callback_factory=None)
# src = ServiceXSourceUpROOT(sx, 'mini')
# r = src.Select(lambda e: {'lep_pt': e['lep_pt']}).AsAwkwardArray().value()
#
# print(r)
#
# assert len(r.lep_pt) == 164716

0 comments on commit 2541559

Please sign in to comment.