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
33 changes: 33 additions & 0 deletions .github/workflows/lambda-do-release-runners.yml
Original file line number Diff line number Diff line change
Expand Up @@ -179,12 +179,45 @@ jobs:
tag: ${{ inputs.tag }}
updateOnlyUnreleased: true

release-benchmark-results-uploader:
name: Upload Release for benchmark-results-uploader lambda
runs-on: ubuntu-latest
permissions:
contents: write
env:
REF: ${{ inputs.tag }}
steps:
- name: Checkout code
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
ref: ${{ inputs.tag }}

- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
with:
python-version: '3.10'

- name: Build deployment.zip
working-directory: aws/lambda/benchmark-results-uploader
run: make deployment.zip

- name: Copy deployment.zip to root
run: cp aws/lambda/benchmark-results-uploader/deployment.zip benchmark-results-uploader.zip

- uses: ncipollo/release-action@v1
with:
artifacts: "benchmark-results-uploader.zip"
allowUpdates: true
draft: true
name: ${{ inputs.tag }}
tag: ${{ inputs.tag }}
updateOnlyUnreleased: true

finish-release:
needs:
- release-lambdas
- release-ci-queue-pct
- release-oss-ci-job-queue-time
- release-benchmark-results-uploader
name: Mark the release as final and publish it
runs-on: ubuntu-latest
permissions:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ jobs:

- name: Install Lintrunner
run: |
pip install lintrunner==0.12.5
pip install lintrunner==0.12.5 boto3-stubs==1.34.51
lintrunner init

- name: Run lintrunner on all files - Linux
Expand Down
19 changes: 19 additions & 0 deletions aws/lambda/benchmark-results-uploader/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
all: run-local

clean:
rm -rf deployment
rm -rf venv
rm -rf deployment.zip

venv/bin/python:
virtualenv venv
venv/bin/pip install -r requirements.txt

deployment.zip:
mkdir -p deployment
cp lambda_function.py ./deployment/.
pip3.10 install -r requirements.txt -t ./deployment/. --platform manylinux2014_x86_64 --only-binary=:all: --implementation cp --python-version 3.10 --upgrade
cd ./deployment && zip -q -r ../deployment.zip .

.PHONY: create-deployment-package
create-deployment-package: deployment.zip
56 changes: 56 additions & 0 deletions aws/lambda/benchmark-results-uploader/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# Benchmark Results Uploader

This AWS Lambda function uploads benchmark result files to S3 buckets with authentication.

## Functionality

This Lambda:

1. Accepts S3 bucket name, path, and content of a file
2. Authenticates the request using username/password from environment variables
3. Checks if the specified path already exists in the S3 bucket
4. If the path doesn't exist, uploads the content to that path
5. Returns appropriate HTTP status codes and messages

## Input Parameters

The Lambda expects the following input parameters in the event object:

- `username`: Username for authentication
- `password`: Password for authentication
- `bucket_name`: Name of the S3 bucket
- `path`: Path within the bucket where content will be stored
- `content`: The content to upload

## Environment Variables

The Lambda requires two environment variables:

- `AUTH_USERNAME`: Username for authentication
- `AUTH_PASSWORD`: Password for authentication

## Deployment

To deploy the Lambda function:

```bash
make deploy
```

This will:
1. Install dependencies
2. Package the Lambda function
3. Deploy to AWS

## Testing

To test the Lambda function locally:

```bash
# Setup environment variables
export AUTH_USERNAME=your_username
export AUTH_PASSWORD=your_password

# Run test
python test_lambda_function.py
```
151 changes: 151 additions & 0 deletions aws/lambda/benchmark-results-uploader/lambda_function.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
import os
import json
import boto3
from botocore.exceptions import ClientError
from typing import Dict, Any
from json.decoder import JSONDecodeError

# Configure AWS S3 client
S3_CLIENT = boto3.client("s3")
OSSCI_BENCHMARKS_BUCKET = "ossci-benchmarks"


def authenticate(username: str, password: str) -> bool:
"""
Authenticate request using environment variable credentials.

Args:
username (str): Username provided in the request
password (str): Password provided in the request

Returns:
bool: True if authentication is successful, False otherwise
"""
return username == os.environ.get(
"UPLOADER_USERNAME"
) and password == os.environ.get("UPLOADER_PASSWORD")


def check_path_exists(path: str) -> bool:
"""
Check if a specific path exists in the S3 bucket.

Args:
path (str): The path to check within the bucket

Returns:
bool: True if the path exists, False otherwise
"""
try:
S3_CLIENT.head_object(Bucket=OSSCI_BENCHMARKS_BUCKET, Key=path)
return True
except ClientError as e:
# If the error code is 404, the path doesn't exist
if e.response["Error"]["Code"] == "404":
return False
# For other errors, raise the exception
raise


def upload_to_s3(path: str, content: str) -> Dict[str, Any]:
"""
Upload content to a specific path in the S3 bucket.

Args:
path (str): The path within the bucket where content will be stored
content (str): The content to upload

Returns:
Dict[str, Any]: Response from S3 upload
"""
try:
response = S3_CLIENT.put_object(
Bucket=OSSCI_BENCHMARKS_BUCKET,
Key=path,
Body=content,
ContentType="application/json",
)
return {
"statusCode": 200,
"body": json.dumps(
{
"message": f"File uploaded successfully to {OSSCI_BENCHMARKS_BUCKET}/{path}",
"etag": response.get("ETag", ""),
}
),
}
except Exception as e:
return {
"statusCode": 500,
"body": json.dumps({"message": f"Error uploading file: {str(e)}"}),
}


def lambda_handler(event: Dict[str, Any], context: Any) -> Dict[str, Any]:
"""
Main Lambda handler function.

Args:
event (Dict[str, Any]): Contains input data for the Lambda function
Required fields:
- s3_path: The path within the bucket where content will be stored
- content: The content to upload
- username: Username for authentication
- password: Password for authentication
context (Any): Provides runtime information about the Lambda function

Returns:
Dict[str, Any]: Response containing status and result information
"""
body = event["body"]
if not body:
return {
"statusCode": 400,
"body": json.dumps({"message": "Missing json request body"}),
}

try:
parsed_body = json.loads(body)
except JSONDecodeError as e:
return {
"statusCode": 400,
"body": json.dumps(
{"message": f"Cannot parse json request body: {str(e)}"}
),
}

try:
username = parsed_body["username"]
password = parsed_body["password"]
except KeyError:
return {
"statusCode": 401,
"body": json.dumps({"message": "Authentication credentials are required"}),
}

if not authenticate(username, password):
return {
"statusCode": 403,
"body": json.dumps({"message": "Invalid authentication credentials"}),
}

try:
s3_path = parsed_body["s3_path"]
content = parsed_body["content"]
except KeyError as e:
return {
"statusCode": 400,
"body": json.dumps({"message": f"Missing required parameter: {str(e)}"}),
}

if check_path_exists(s3_path):
return {
"statusCode": 409, # Conflict status code
"body": json.dumps(
{
"message": f"Path {s3_path} already exists in bucket {OSSCI_BENCHMARKS_BUCKET}"
}
),
}

return upload_to_s3(s3_path, content)
1 change: 1 addition & 0 deletions aws/lambda/benchmark-results-uploader/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
boto3==1.36.21
Loading