Skip to content

Commit

Permalink
[CERTTF-452] Modify submit action behaviour for reservation jobs (#400
Browse files Browse the repository at this point in the history
)

* feat: introduce action step for reservation jobs
* feat: add workflow groups
* feat: include `JOB_ID` in group messages and notice
* feat: add notice with exit status
* feat: retrieve and return IP of reserved machine
* docs: extend README to describe polling behaviour for reservations
  • Loading branch information
boukeas authored Nov 12, 2024
1 parent a77c0ee commit bb8982a
Show file tree
Hide file tree
Showing 2 changed files with 90 additions and 9 deletions.
90 changes: 81 additions & 9 deletions .github/actions/submit/action.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ outputs:
id:
description: 'The ID of the submitted job'
value: ${{ steps.submit.outputs.id }}
device-ip:
description: 'The IP of the reserved device (if applicable)'
value: ${{ steps.track-reservation.outputs.ip }}
runs:
using: composite
steps:
Expand All @@ -34,6 +37,7 @@ runs:
env:
SERVER: https://${{ inputs.server }}
run: |
echo "::group::Test connection to Testflinger server"
STATUS=$(curl --stderr error.log -Ivw "%{http_code}\n" -o /dev/null $SERVER/jobs)
ERROR=$?
if [ ! $ERROR -eq 0 ]; then
Expand All @@ -47,11 +51,13 @@ runs:
else
echo "Successful server ping at $SERVER, status: $STATUS"
fi
echo "::endgroup::"
- name: Create job file, if required
id: create-job-file
shell: bash
run: |
echo "::group::Create job file (if required)"
if [ -n "${{ inputs.job-path }}" ]; then
# the `$JOB` environment variable points to the job path provided as input
echo "JOB=${{ inputs.job-path }}" >> $GITHUB_ENV
Expand All @@ -69,14 +75,34 @@ runs:
# the `$JOB` environment variable points to the newly created job file
echo "JOB=$FILE" >> $GITHUB_ENV
fi
echo "::endgroup::"
- name: Display contents of job file (for verification)
shell: bash
run: cat "$JOB"
run: |
echo "::group::Display job file"
cat "$JOB"
echo "::endgroup::"
- name: Determine if this is a reservation job
id: check-reservation
shell: bash
run: |
echo "::group::Determine if this is a reservation job"
if grep -q "^reserve_data:" $JOB; then
RESERVATION=true
else
RESERVATION=false
fi
echo "reservation=$RESERVATION" | tee -a $GITHUB_OUTPUT
echo "::endgroup::"
- name: Install prerequisites
shell: bash
run: sudo snap install testflinger-cli jq
run: |
echo "::group::Install prerequisites"
sudo snap install testflinger-cli jq
echo "::endgroup::"
- name: Submit job to the Testflinger server
id: submit
Expand All @@ -86,26 +112,30 @@ runs:
SERVER: https://${{ inputs.server }}
RELATIVE_TO: ${{ inputs.attachments-relative-to }}
run: |
echo "::group::Submit job to the Testflinger server"
if [ -n "$RELATIVE_TO" ]; then
RELATIVE="--attachments-relative-to $RELATIVE_TO"
fi
JOB_ID=$(testflinger --server $SERVER submit --quiet "$RELATIVE" "$JOB")
echo "job id: $JOB_ID"
echo "id=$JOB_ID" >> $GITHUB_OUTPUT
echo "::endgroup::"
echo "::notice::Submitted job $JOB_ID"
- name: Track the status of the job and mirror its exit status
if: inputs.poll == 'true' && inputs.dry-run != 'true'
- name: Track the status of the job (non-reservation)
if: inputs.poll == 'true' && inputs.dry-run != 'true' && steps.check-reservation.outputs.reservation != 'true'
shell: bash
env:
SERVER: https://${{ inputs.server }}
JOB_ID: ${{ steps.submit.outputs.id }}
run: |
echo "::group::Track the status of job ${{ env.JOB_ID }}"
# poll
PYTHONUNBUFFERED=1 testflinger --server $SERVER poll $JOB_ID
# retrieve results:
echo "::endgroup::"
echo "::group::Retrieve job results, determine exit status"
# the exit status is the maximum status of the individual test phases
# (excluding the setup and cleanup phases)
STATUS=$(\
EXIT_STATUS=$(\
testflinger --server $SERVER results $JOB_ID \
| jq 'to_entries
| map(
Expand All @@ -117,8 +147,48 @@ runs:
)
| max'
)
echo "Test exit status: $STATUS"
exit $STATUS
echo "::endgroup::"
echo "::notice::Test exit status: $EXIT_STATUS"
exit $EXIT_STATUS
- name: Track the status of the job (reservation)
id: track-reservation
if: inputs.poll == 'true' && inputs.dry-run != 'true' && steps.check-reservation.outputs.reservation == 'true'
shell: bash
env:
SERVER: https://${{ inputs.server }}
JOB_ID: ${{ steps.submit.outputs.id }}
TERMINATION: "TESTFLINGER SYSTEM RESERVED"
run: |
echo "::group::Track the status of reservation job ${{ env.JOB_ID }}"
# specify file for capturing output and clear it
CAPTURED_OUTPUT=output
> "$CAPTURED_OUTPUT"
# perform one-shot polling until the reserve phase has been reached
# and the captured output contains the TERMINATION string
while true; do
PYTHONUNBUFFERED=1 testflinger --server $SERVER poll --oneshot $JOB_ID | tee -a "$CAPTURED_OUTPUT"
JOB_STATUS="$(testflinger --server $SERVER status $JOB_ID)"
if [ "$JOB_STATUS" = "reserve" ]; then
if grep -q "$TERMINATION" "$CAPTURED_OUTPUT"; then
EXIT_STATUS=0
break
fi
elif [[ "$JOB_STATUS" =~ ^(complete|cancelled)$ ]]; then
EXIT_STATUS=1
break
fi
sleep 10
done
echo "::endgroup::"
echo "::group::Retrieve IP of reserved machine"
DEVICE_IP=$(grep -m 1 "^Now try logging into the machine" "$CAPTURED_OUTPUT" | sed -n "s/.*@\([0-9.]*\).*/\1/p")
[ "$?" -eq 0 ] && DEVICE_IP_FLAG=true
[ -n "$DEVICE_IP_FLAG" ] && echo "ip=$DEVICE_IP" >> $GITHUB_OUTPUT
echo "::endgroup::"
echo "::notice::Reserve exit status: $EXIT_STATUS"
[ -n "$DEVICE_IP_FLAG" ] && echo "::notice::Device IP: $DEVICE_IP"
exit $EXIT_STATUS
- name: Cancel the job if the action is cancelled
if: ${{ cancelled() }}
Expand All @@ -127,4 +197,6 @@ runs:
SERVER: https://${{ inputs.server }}
JOB_ID: ${{ steps.submit.outputs.id }}
run: |
echo "::group::Cancel job"
testflinger --server $SERVER cancel $JOB_ID
echo "::endgroup::"
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,3 +74,12 @@ in any of the subsequent steps of the workflow:
testflinger results ${{ steps.submit-job.outputs.id }}"
```
In this example, `submit-job` is the step where the `submit` action is used.

If the submitted job is a reservation job (i.e. includes `reserve_data`) then
setting the `poll` input argument to `true` results in modified behaviour: the
job is polled only until the reservation phase is complete, instead of waiting
for the entire job to complete (which happens when the reservation timeout
expires or the job is cancelled). There will be no output to record after the
reservation so there is little point in polling and idly occupying the runner.
However, please do remember to manually cancel the job after you are done with
the reserved device.

0 comments on commit bb8982a

Please sign in to comment.