Skip to content
Merged
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
36 changes: 36 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -477,6 +477,13 @@ variables) that are allowed to be specified in a PR command with the
`exportvariable` filters must be used (one per variable). These variables will
be exported into the build environment before running the bot/build.sh script.

The bot build script makes use of the variable `SKIP_TESTS` to determine if
ReFrame tests shall be skipped or not. Default is not to skip them. To allow the
use of the variable the setting could look like
```
allowed_exportvars = ["SKIP_TESTS=yes", "SKIP_TESTS=no"]
```


#### `[bot_control]` section

Expand Down Expand Up @@ -508,6 +515,35 @@ artefact_upload_script = PATH_TO_EESSI_BOT/scripts/eessi-upload-to-staging
```
`artefact_upload_script` provides the location for the script used for uploading built software packages to an S3 bucket.

```
signing =
{
REPO_ID: {
"script": PATH_TO_SIGN_SCRIPT,
"key": PATH_TO_KEY_FILE,
"container_runtime": PATH_TO_CONTAINER_RUNTIME
}, ...
}
```
`signing` provides a setting for signing artefacts. The value uses a JSON-like format
with `REPO_ID` being the repository ID. Repository IDs are defined in a file
`repos.cfg` (see setting `repos_cfg_dir`), `script` provides the location of the
script that is used to sign a file. If the location is a relative path, the script
must reside in the checked out pull request of the target repository (e.g.,
EESSI/software-layer). `key` points to the file of the key being used
for signing. The bot calls the script with the two arguments:
1. private key (as provided by the attribute 'key')
2. path to the file to be signed (the upload script will determine that)
NOTE (on `container_runtime`), signing requires a recent installation of OpenSSH
(8.2 or newer). If the frontend where the event handler runs does not have that
version installed, you can specify a container runtime via the `container_runtime`
attribute below. Currently, only Singularity or Apptainer are supported.
Note (on the key), make sure the file permissions are restricted to `0600` (only
readable+writable by the file owner, or the signing will likely fail.
Note (on json format), make sure no trailing commas are used after any elements
or parsing/loading the json will likely fail. Also, the whole value should start
at a new line and be indented as shown above.

```
endpoint_url = URL_TO_S3_SERVER
```
Expand Down
38 changes: 36 additions & 2 deletions app.cfg.example
Original file line number Diff line number Diff line change
Expand Up @@ -161,8 +161,17 @@ no_build_permission_comment = Label `bot:build` has been set by user `{build_lab
allow_update_submit_opts = false

# defines which name-value pairs (environment variables) are allowed to be
# exported into the build environment via `exportvariable` filters
allowed_exportvars = ["NAME1=value_1a", "NAME1=value_1b", "NAME2=value_2"]
# exported into the build environment via 'exportvariable' filters
# The bot build script makes use of the variable 'SKIP_TESTS' to determine if
# ReFrame tests shall be skipped or not. Default value is 'no'. If the value is
# 'yes' and the exportvariable filter is added to a bot build command
# ('export:SKIP_TESTS=yes'), ReFrame tests are skipped.
# NOTE, the setting is optional and commented by default. If you want to enable
# this feature ('exportvariable' filters), uncomment the line below and define
# meaningful key-value pair(s). For example, to enable the use of
# 'exportvariable:SKIP_TESTS=yes' as a filter, the key-value pair would be
# "SKIP_TESTS=yes".
# allowed_exportvars = ["NAME1=value_1a", "NAME1=value_1b", "NAME2=value_2"]


[deploycfg]
Expand All @@ -185,6 +194,31 @@ endpoint_url = URL_TO_S3_SERVER
# like: bucket_name = {"eessi-pilot-2023.06": "eessi-staging-pilot-2023.06", "eessi.io-2023.06": "software.eessi.io-2023.06"}
bucket_name = eessi-staging

# settings for signing artefacts with JSON-like format
# REPO_ID: { "script": PATH_TO_SIGN_SCRIPT, "key": PATH_TO_KEY_FILE, "container_runtime": PATH_TO_CONTAINER_RUNTIME }
# If PATH_TO_SIGN_SCRIPT is a relative path, the script must reside in the
# checked out pull request of the target repository (e.g.,
# EESSI/software-layer).
# The bot calls the script with the two arguments:
# 1. private key (as provided by the attribute 'key')
# 2. path to the file to be signed (the upload script will determine that)
# NOTE (on "container_runtime"), signing requires a recent installation of OpenSSH
# (8.2 or newer). If the frontend where the event handler runs does not have that
# version installed, you can specify a container runtime via the 'container_runtime'
# attribute below. Currently, only Singularity or Apptainer are supported.
# NOTE (on the key), make sure the file permissions are restricted to `0600` (only
# readable+writable by the file owner, or the signing will likely fail.
# Note (on json format), make sure no trailing commas are used after any elements
# or parsing/loading the json will likely fail. Also, the whole value should start
# at a new line and be indented as shown below.
signing =
{
"eessi.io-2023.06-software: {
"script": PATH_TO_SIGN_SCRIPT,
"key": PATH_TO_EESSI_BOT/config/user-site-system.key,
"container_runtime": PATH_TO_CONTAINER_RUNTIME
}
}
# upload policy: defines what policy is used for uploading built artefacts
# to an S3 bucket
# 'all' ..: upload all artefacts (mulitple uploads of the same artefact possible)
Expand Down
1 change: 1 addition & 0 deletions eessi_bot_event_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@
# config.DEPLOYCFG_SETTING_ENDPOINT_URL, # optional
config.DEPLOYCFG_SETTING_METADATA_PREFIX, # (required)
config.DEPLOYCFG_SETTING_NO_DEPLOY_PERMISSION_COMMENT, # required
# config.DEPLOYCFG_SETTING_SIGNING, # optional
config.DEPLOYCFG_SETTING_UPLOAD_POLICY], # required
config.SECTION_DOWNLOAD_PR_COMMENTS: [
config.DOWNLOAD_PR_COMMENTS_SETTING_CURL_FAILURE, # required
Expand Down
22 changes: 15 additions & 7 deletions scripts/bot-build.slurm
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@
# - the directory may contain any additional files references in job.cfg,
# for example, repos.cfg and configuration file bundles for repositories

# set default for SKIP_TESTS (don't skip ReFrame tests)
SKIP_TESTS=no

echo "Starting bot-build.slurm"
EXPORT_VARS_SCRIPT=cfg/export_vars.sh
if [ -f ${EXPORT_VARS_SCRIPT} ]; then
Expand Down Expand Up @@ -108,14 +111,19 @@ artefacts =
EOF
fi
echo "check build step finished"
TEST_SCRIPT=bot/test.sh
if [ -f ${TEST_SCRIPT} ]; then
echo "${TEST_SCRIPT} script found in '${PWD}', so running it!"
${TEST_SCRIPT}
echo "${TEST_SCRIPT} finished"
else
echo "could not find ${TEST_SCRIPT} script in '${PWD}'" >&2

# SKIP_TESTS can be defined as export variable in the bot's config and then added to bot commands (export:SKIP_TESTS=yes)
if [[ "${SKIP_TESTS}" != "yes" ]]; then
TEST_SCRIPT=bot/test.sh
if [ -f ${TEST_SCRIPT} ]; then
echo "${TEST_SCRIPT} script found in '${PWD}', so running it!"
${TEST_SCRIPT}
echo "${TEST_SCRIPT} finished"
else
echo "could not find ${TEST_SCRIPT} script in '${PWD}'" >&2
fi
fi

CHECK_TEST_SCRIPT=bot/check-test.sh
if [ -f ${CHECK_TEST_SCRIPT} ]; then
echo "${CHECK_TEST_SCRIPT} script found in '${PWD}', so running it!"
Expand Down
97 changes: 89 additions & 8 deletions scripts/eessi-upload-to-staging
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,9 @@ function display_help
echo " ingestion procedure" >&2
echo " -l | --list-variables - list variables that are available" >&2
echo " for expansion" >&2
echo " -k | --sign-key SCRIPT_KEY - specify location of the key to be" >&2
echo " used to sign artefacts and metadata" >&2
echo " files [optional; default: don't sign]" >&2
echo " -m | --metadata-prefix PREFIX - a directory to which the metadata" >&2
echo " file shall be uploaded; BASH variable" >&2
echo " expansion will be applied; arg '-l'" >&2
Expand All @@ -93,6 +96,13 @@ function display_help
echo " link the upload to a PR" >&2
echo " -r | --repository FULL_NAME - a repository name ACCOUNT/REPONAME;" >&2
echo " used to link the upload to a PR" >&2
echo " -s | --sign-script SCRIPT_PATH - path to script that is used to sign" >&2
echo " artefacts and metadata files. The" >&2
echo " script is called with two arguments:" >&2
echo " KEY file_to_sign. The KEY is the one" >&2
echo " provided via option --sign-key. The" >&2
echo " latter is determined by this script." >&2
echo " [optional; default: don't sign]" >&2
}

if [[ $# -lt 1 ]]; then
Expand Down Expand Up @@ -120,6 +130,8 @@ endpoint_url=
pr_comment_id="none"
pull_request_number="none"
github_repository="EESSI/software-layer"
sign_key=
sign_script=

# provided via options in the bot's config file app.cfg and/or command line argument
metadata_prefix=
Expand Down Expand Up @@ -155,6 +167,14 @@ while [[ $# -gt 0 ]]; do
pr_comment_id="$2"
shift 2
;;
-k|--sign-key)
sign_key=$2
if [[ ! -r "${sign_key}" ]]; then
echo "Error: SSH key '${sign_key}' to be used for signing doesn't exist or cannot be read" >&2
exit 1
fi
shift 2
;;
-m|--metadata-prefix)
metadata_prefix="$2"
shift 2
Expand All @@ -171,6 +191,14 @@ while [[ $# -gt 0 ]]; do
github_repository="$2"
shift 2
;;
-s|--sign-script)
sign_script=$2
if [[ ! -x "${sign_script}" ]]; then
echo "Error: Script '${sign_script}' to be used for signing doesn't exist or is not executable" >&2
exit 1
fi
shift 2
;;
-*|--*)
echo "Error: Unknown option: $1" >&2
exit 1
Expand All @@ -185,6 +213,21 @@ done
# restore potentially parsed filename(s) into $*
set -- "${POSITIONAL_ARGS[@]}"

# ensure that either none or both of $sign_key and $sign_script are defined
if [[ -n "${sign_key}" ]] && [[ -n "${sign_script}" ]]; then
sign=1
elif [[ -n "${sign_key}" ]]; then
sign=0
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nitpicking, can maybe be tackled in a follow-up PR: I would've used 'no' and 'yes' here, to avoid confusion.

0 is often a sign of success in bash scripts, here's it's a negative value, 'no' leaves no room for confusion

echo "Error: Signing requires a key (${sign_key}) AND a script (${sign_script}); likely the bot config is incomplete" >&2
exit 1
elif [[ -n "${sign_script}" ]]; then
sign=0
echo "Error: Signing requires a key (${sign_key}) AND a script (${sign_script}); likely the bot config is incomplete" >&2
exit 1
else
sign=0
fi

# infer bucket_base:
# if endpoint_url is not set (assume AWS S3 is used),
# bucket_base=https://${bucket_name}.s3.amazonaws.com/
Expand Down Expand Up @@ -217,6 +260,33 @@ for file in "$*"; do
aws_path=$(envsubst <<< "${artefact_prefix}")
fi
aws_file=$(basename ${file})
# 1st sign artefact, and upload signature
if [[ "${sign}" = "1" ]]; then
# sign artefact
${sign_script} sign ${sign_key} ${file}
# TODO check if signing worked (just check exit code == 0)
sig_file=${file}.sig
aws_sig_file=${aws_file}.sig

# uploading signature
echo " store artefact signature at ${aws_path}/${aws_sig_file}"
upload_to_staging_bucket \
"${sig_file}" \
"${bucket_name}" \
"${aws_path}/${aws_sig_file}" \
"${endpoint_url}"
else
echo "no signing method defined; not signing artefact"
fi

echo Uploading to "${url}"
echo " store artefact at ${aws_path}/${aws_file}"
upload_to_staging_bucket \
"${file}" \
"${bucket_name}" \
"${aws_path}/${aws_file}" \
"${endpoint_url}"

echo "Creating metadata file"
url="${bucket_base}/${aws_path}/${aws_file}"
echo "create_metadata_file file=${file} \
Expand All @@ -229,24 +299,35 @@ for file in "$*"; do
"${github_repository}" \
"${pull_request_number}" \
"${pr_comment_id}")
aws_metadata_file=${aws_file}.meta.txt
# TODO check that creating the metadata file succeeded
echo "metadata:"
cat ${metadata_file}

echo Uploading to "${url}"
echo " store artefact at ${aws_path}/${aws_file}"
upload_to_staging_bucket \
"${file}" \
"${bucket_name}" \
"${aws_path}/${aws_file}" \
"${endpoint_url}"

if [ -z ${metadata_prefix} ]; then
aws_path=${legacy_aws_path}
else
export pull_request_number
export github_repository
aws_path=$(envsubst <<< "${metadata_prefix}")
fi
# 2nd sign metadata file, and upload signature
if [[ "${sign}" = "1" ]]; then
# sign metadata file
${sign_script} sign ${sign_key} ${metadata_file}
# TODO check if signing worked (just check exit code == 0)
sig_metadata_file=${metadata_file}.sig
aws_sig_metadata_file=${aws_metadata_file}.sig

echo " store metadata signature at ${aws_path}/${aws_sig_metadata_file}"
upload_to_staging_bucket \
"${sig_metadata_file}" \
"${bucket_name}" \
"${aws_path}/${aws_sig_metadata_file}" \
"${endpoint_url}"
else
echo "no signing method defined; not signing metadata file"
fi
echo " store metadata file at ${aws_path}/${aws_file}.meta.txt"
upload_to_staging_bucket \
"${metadata_file}" \
Expand Down
Loading