Skip to content

Solomon/code execute #62

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 18 commits into from
Nov 5, 2024
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
23 changes: 21 additions & 2 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,12 @@ jobs:
env:
QUESTION_FIREBASE_CREDENTIAL_PATH: ${{ vars.QUESTION_SERVICE_FIREBASE_CREDENTIAL_PATH }}
JWT_SECRET: ${{ secrets.JWT_SECRET }}
EXECUTION_SERVICE_URL: ${{ vars.EXECUTION_SERVICE_URL }}
run: |
cd ./apps/question-service

echo "FIREBASE_CREDENTIAL_PATH=$QUESTION_FIREBASE_CREDENTIAL_PATH" >> .env
echo "JWT_SECRET=$JWT_SECRET" >> .env
echo "EXECUTION_SERVICE_URL=$EXECUTION_SERVICE_URL" >> .env

- name: Set up credentials
env:
Expand All @@ -52,7 +53,7 @@ jobs:
run: curl -sL firebase.tools | bash

- name: Run Go tests with Firebase emulator
run: firebase emulators:exec --only firestore 'cd ./apps/question-service; go test ./...'
run: firebase emulators:exec --only firestore 'cd ./apps/question-service; go test -v ./tests'

frontend-unit-tests:
runs-on: ubuntu-latest
Expand Down Expand Up @@ -103,14 +104,17 @@ jobs:
MATCHING_SERVICE_URL: ${{ vars.MATCHING_SERVICE_URL }}
HISTORY_SERVICE_URL: ${{ vars.HISTORY_SERVICE_URL }}
SIGNALLING_SERVICE_URL: ${{ vars.SIGNALLING_SERVICE_URL }}
EXECUTION_SERVICE_URL: ${{ vars.EXECUTION_SERVICE_URL }}
JWT_SECRET: ${{ secrets.JWT_SECRET }}
QUESTION_FIREBASE_CREDENTIAL_PATH: ${{ vars.QUESTION_SERVICE_FIREBASE_CREDENTIAL_PATH }}
HISTORY_FIREBASE_CREDENTIAL_PATH: ${{ vars.HISTORY_SERVICE_FIREBASE_CREDENTIAL_PATH }}
EXECUTION_FIREBASE_CREDENTIAL_PATH: ${{ vars.EXECUTION_SERVICE_FIREBASE_CREDENTIAL_PATH }}
DB_CLOUD_URI: ${{ secrets.USER_SERVICE_DB_CLOUD_URI }}
USER_SERVICE_PORT: ${{ vars.USER_SERVICE_PORT }}
MATCHING_SERVICE_PORT: ${{ vars.MATCHING_SERVICE_PORT }}
HISTORY_SERVICE_PORT: ${{ vars.HISTORY_SERVICE_PORT }}
SIGNALLING_SERVICE_PORT: ${{ vars.SIGNALLING_SERVICE_PORT }}
EXECUTION_SERVICE_PORT: ${{ vars.EXECUTION_SERVICE_PORT }}
MATCHING_SERVICE_TIMEOUT: ${{ vars.MATCHING_SERVICE_TIMEOUT }}
REDIS_URL: ${{ vars.REDIS_URL }}
QUESTION_SERVICE_GRPC_URL: ${{ vars.QUESTION_SERVICE_GPRC_URL }}
Expand All @@ -121,10 +125,12 @@ jobs:
echo "NEXT_PUBLIC_MATCHING_SERVICE_URL=$MATCHING_SERVICE_URL" >> .env
echo "NEXT_PUBLIC_HISTORY_SERVICE_URL=$HISTORY_SERVICE_URL" >> .env
echo "NEXT_PUBLIC_SIGNALLING_SERVICE_URL=$SIGNALLING_SERVICE_URL" >> .env
echo "NEXT_PUBLIC_EXECUTION_SERVICE_URL=EXECUTION_SERVICE_URL" >> .env

cd ../question-service
echo "FIREBASE_CREDENTIAL_PATH=$QUESTION_FIREBASE_CREDENTIAL_PATH" >> .env
echo "JWT_SECRET=$JWT_SECRET" >> .env
echo "EXECUTION_SERVICE_URL=$EXECUTION_SERVICE_URL" >> .env

cd ../user-service
echo "DB_CLOUD_URI=$DB_CLOUD_URI" >> .env
Expand All @@ -141,6 +147,11 @@ jobs:
cd ../history-service
echo "FIREBASE_CREDENTIAL_PATH=$HISTORY_FIREBASE_CREDENTIAL_PATH" >> .env
echo "PORT=$HISTORY_SERVICE_PORT" >> .env

cd ../execution-service
echo "FIREBASE_CREDENTIAL_PATH=$EXECUTION_FIREBASE_CREDENTIAL_PATH" >> .env
echo "PORT=$EXECUTION_SERVICE_PORT" >> .env
echo "HISTORY_SERVICE_URL=$HISTORY_SERVICE_URL" >> .env

cd ../signalling-service
echo "PORT=$SIGNALLING_SERVICE_PORT" >> .env
Expand All @@ -151,12 +162,17 @@ jobs:
QUESTION_FIREBASE_CREDENTIAL_PATH: ${{ vars.QUESTION_SERVICE_FIREBASE_CREDENTIAL_PATH }}
HISTORY_FIREBASE_JSON: ${{ secrets.HISTORY_SERVICE_FIREBASE_CREDENTIAL }}
HISTORY_FIREBASE_CREDENTIAL_PATH: ${{ vars.HISTORY_SERVICE_FIREBASE_CREDENTIAL_PATH }}
EXECUTION_FIREBASE_JSON: ${{ secrets.EXECUTION_SERVICE_FIREBASE_CREDENTIAL }}
EXECUTION_FIREBASE_CREDENTIAL_PATH: ${{ vars.EXECUTION_SERVICE_FIREBASE_CREDENTIAL_PATH }}
run: |
cd ./apps/question-service
echo "$QUESTION_FIREBASE_JSON" > "./$QUESTION_FIREBASE_CREDENTIAL_PATH"

cd ../history-service
echo "$HISTORY_FIREBASE_JSON" > "./$HISTORY_FIREBASE_CREDENTIAL_PATH"

cd ../execution-service
echo "$EXECUTION_FIREBASE_JSON" > "./$EXECUTION_FIREBASE_CREDENTIAL_PATH"

- name: Build and Run Services
run: |
Expand All @@ -180,6 +196,7 @@ jobs:
MATCHING_SERVICE_URL: ${{ vars.MATCHING_SERVICE_URL }}
HISTORY_SERVICE_URL: ${{ vars.HISTORY_SERVICE_URL }}
SIGNALLING_SERVICE_URL: ${{ vars.SIGNALLING_SERVICE_URL }}
EXECUTION_SERVICE_URL: ${{ vars.EXECUTION_SERVICE_URL }}
run: |
echo "Testing Question Service..."
curl -sSL -o /dev/null $QUESTION_SERVICE_URL && echo "Question Service is up"
Expand All @@ -189,6 +206,8 @@ jobs:
curl -fsSL -o /dev/null $FRONTEND_URL && echo "Frontend is up"
echo "Testing History Service..."
curl -fsSL -o /dev/null $HISTORY_SERVICE_URL && echo "History Service is up"
echo "Testing Execution Service..."
curl -fsSL -o /dev/null $EXECUTION_SERVICE_URL && echo "Execution Service is up"
echo "Testing Matching Service..."
if ! (echo "Hello" | websocat $MATCHING_SERVICE_URL); then
echo "WebSocket for Matching Service is not live"
Expand Down
22 changes: 22 additions & 0 deletions apps/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,20 @@ services:
volumes:
- ./signalling-service:/signalling-service

execution-service:
build:
context: ./execution-service
dockerfile: Dockerfile
ports:
- 8083:8083
env_file:
- ./execution-service/.env
networks:
- apps_network
volumes:
- ./execution-service:/execution-service
- /var/run/docker.sock:/var/run/docker.sock

redis:
image: redis:latest
networks:
Expand All @@ -90,5 +104,13 @@ services:
- 6379:6379
container_name: redis-container

python-sandbox:
build:
context: ./execution-service/execution/python
dockerfile: Dockerfile
networks:
- apps_network
container_name: python-sandbox

networks:
apps_network:
9 changes: 9 additions & 0 deletions apps/execution-service/.dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
.env.example

.git
.gitignore

.dockerignore
Dockerfile

README.md
8 changes: 8 additions & 0 deletions apps/execution-service/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
FIREBASE_CREDENTIAL_PATH=cs3219-staging-codeexecution-firebase-adminsdk-ce48j-00ab09514c.json
PORT=8083

# If you are NOT USING docker, use the below variables
# HISTORY_SERVICE_URL=http://localhost:8082/

# If you are USING docker, use the below variables
HISTORY_SERVICE_URL=http://history-service:8082/
2 changes: 2 additions & 0 deletions apps/execution-service/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
.env
cs3219-staging-codeexecution-firebase-adminsdk-ce48j-00ab09514c.json
22 changes: 22 additions & 0 deletions apps/execution-service/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
FROM golang:1.23

WORKDIR /usr/src/app

# Install Docker CLI
RUN apt-get update && apt-get install -y \
docker.io \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*

# pre-copy/cache go.mod for pre-downloading dependencies and only redownloading them in subsequent builds if they change
COPY go.mod go.sum ./

RUN go mod tidy && go mod download && go mod verify

COPY . .

RUN go build -v -o /usr/local/bin/app ./main.go

EXPOSE 8083

CMD ["app"]
217 changes: 217 additions & 0 deletions apps/execution-service/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,217 @@
# Execution Service

### Installation

1. Install dependencies:

```bash
go mod tidy
```

2. Create the `.env` file from copying the `.env.example`, and copy the firebase JSON file into execution-service/ fill in the `FIREBASE_CREDENTIAL_PATH` with the path of the firebase credential JSON file.

### Running the Application

To start the server, run the following command:

```bash
go run main.go
```

The server will be available at http://localhost:8083.

## Running the Application via Docker

To run the application via Docker, run the following command:

```bash
docker build -t execution-service .
```

```bash
docker run -p 8083:8083 --env-file .env -d execution-service
```

The server will be available at http://localhost:8083.

## API Endpoints

- `POST /tests/populate`
- `GET /tests/{questionDocRefId}/`
- `POST /tests/{questionDocRefId}/execute`
- `POST /tests/{questionDocRefId}/submit`

## Managing Firebase

To reset and repopulate the database, run the following command:

```bash
go run main.go
```

## Repopulate test cases

To repopulate test cases, you need to repopulate the questions in the question-service, which will automatically call the execution-service to populate the test cases.

In question-service, run the following command:

```bash
go run main.go -populate
```

## API Documentation

`GET /tests/{questionDocRefId}/`

To read visible test cases via a question ID, run the following command:

```bash
curl -X GET http://localhost:8083/tests/{questioinDocRefId}/ \
-H "Content-Type: application/json"
```

The following json format will be returned:

```json
[
{
"input":"hello",
"expected":"olleh"
}
]
```

`POST /tests/{questionDocRefId}/execute`

To execute test cases via a question ID without custom test cases, run the following command, with custom code and language:

```bash
curl -X POST http://localhost:8083/tests/{questioinDocRefId}/execute \
-H "Content-Type: application/json" \
-d '{
"code": "name = input()\nprint(name[::-1])",
"language": "Python"
}'
```

The following json format will be returned:

```json
{
"visibleTestResults":[
{
"input":"hello",
"expected":"olleh",
"actual":"olleh",
"passed":true,
"error":""
}
],
"customTestResults":null
}
```

To execute visible and custom test cases via a question ID with custom test cases, run the following command, with custom code, language and custom test cases:

```bash
curl -X POST http://localhost:8083/tests/{questioinDocRefId}/execute \
-H "Content-Type: application/json" \
-d '{
"code": "name = input()\nprint(name[::-1])",
"language": "Python",
"customTestCases": "2\nHannah\nhannaH\nabcdefg\ngfedcba\n"
}'
```

The following json format will be returned:

```json
{
"visibleTestResults":[
{
"input":"hello",
"expected":"olleh",
"actual":"olleh",
"passed":true,
"error":""
}
],
"customTestResults":[
{
"input":"Hannah",
"expected":"hannaH",
"actual":"hannaH",
"passed":true,
"error":""
},
{
"input":"abcdefg",
"expected":"gfedcba",
"actual":"gfedcba",
"passed":true,
"error":""
}
]
}
```

`POST /tests/{questionDocRefId}/submit`

To submit a solution and execute visible and hidden test cases via a question ID, run the following command, with custom code and language:

```bash
curl -X POST http://localhost:8083/tests/{questioinDocRefId}/submit \
-H "Content-Type: application/json" \
-d '{
"title": "Example Title",
"code": "name = input()\nprint(name[::-1])",
"language": "Python",
"user": "user123",
"matchedUser": "user456",
"matchedTopics": ["topic1", "topic2"],
"questionDifficulty": "Medium",
"questionTopics": ["Loops", "Strings"]
}'
```

The following json format will be returned:

```json
{
"visibleTestResults":[
{
"input":"hello",
"expected":"olleh",
"actual":"olleh",
"passed":true,
"error":""
}
],
"hiddenTestResults":{
"passed":2,
"total":2
},
"status":"Accepted"
}
```

If compilation error exists or any of the tests (visible and hidden) fails, status "Attempted" will be returned:

```json
{
"visibleTestResults":[
{
"input":"hello",
"expected":"olleh",
"actual":"",
"passed":false,
"error":"Command execution failed: Traceback (most recent call last):\n File \"/tmp/4149249165.py\", line 2, in \u003cmodule\u003e\n prit(name[::-1])\n ^^^^\nNameError: name 'prit' is not defined. Did you mean: 'print'?\n: %!w(*exec.ExitError=\u0026{0x4000364678 []})"
}
],
"hiddenTestResults":{
"passed":0,
"total":2
},
"status":"Attempted"
}
```
Loading
Loading