Skip to content

Commit

Permalink
Merge pull request #203 from Onyxmoon/feature/readme
Browse files Browse the repository at this point in the history
Feature/readme
  • Loading branch information
Onyxmoon authored Jan 15, 2024
2 parents 42ca7ee + cad6217 commit dc0a535
Show file tree
Hide file tree
Showing 15 changed files with 445 additions and 29 deletions.
41 changes: 41 additions & 0 deletions .github/workflows/run-tests-load-balancer-service.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
name: Run tests (load balancer service)

on:
push:
paths:
- 'src/load-balancer-service/**'
workflow_dispatch:

jobs:
test:
name: Test web-service
runs-on: ubuntu-latest

steps:
- name: Check out code
uses: actions/checkout@v2

- name: Set up Go
uses: actions/setup-go@v2
with:
go-version: 1.21

- name: Install dependencies
working-directory: ./src/load-balancer-service
run: go mod tidy

- name: Build
working-directory: ./src/load-balancer-service
run: go build -v ./...

- name: Test Go Module
run: |
cd src/load-balancer-service
go test ./...
test_exit_code=$? # Capture the exit code of the go test command
if [ $test_exit_code -eq 0 ]; then
echo "Tests passed successfully."
else
echo "Tests failed with exit code $test_exit_code."
exit 1 # Fail the GitHub Actions workflow
fi
2 changes: 1 addition & 1 deletion README.assets/CE_Architecture_Prototype.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
50 changes: 38 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,30 @@ Key features:

![Architecture](README.assets/CE_Architecture_Prototype.svg)

## Requirements
### Development
## Setup project in Kubernetes
The complete application can be deployed within a kubernetes cluster.
1. Manifest files for deployment (including monitoring) are located at [`kubernetes/manifests`](kubernetes/manifests)
2. It is optional but highly recommended to set your own username and password for the database access in manifest files (replace `db-username` and `db-pw-changeMe!` in each service manifest).
> Some special characters are not possible because RQLite uses basic auth. Please use url-safe characters like Alphanumeric [0-9a-zA-Z], special characters $-_.+!*'(),.
3. The user service ([`price-whisper/user.yaml`](kubernetes/manifests/price-whisper/users.yaml)) uses JWT tokens for authentication while logged-in. A custom ECDSA private key can be specified as path or inline in environment variable `JWT_PRIVATE_KEY`.

Generate a key with the following command:
```shell
ssh-keygen -t ecdsa -f ./src/user-service/privateKey.pem -m pem
```
> When step is missing, container will use a random but secure generated key on each start, but users will be logged-out at restarts.
4. Apply the manifests in `kubernetes/manifests` to your kubernetes control plane.
```shell
kubectl apply -R -f ./kubernetes/manifests
```

## Setup project in Docker
### Requirements
- Docker Compose version v2.23.3
- a compatible container daemon

## Setup the project
### Development setup via docker-compose
> The development setup exposes all ports to the host machine, so you can access each service individually. Docker will build local and database is not persistent in this setup.
> The development setup exposes all ports to the host machine, so you can access each service individually. Docker will build locally and the database is not persistent in this setup.
1. Clone repository from Github `git clone https://github.com/Onyxmoon/hsfl-master-ai-cloud-engineering.git`
2. Set your own username and password for the database access in compose file (replace `db-username` and `db-pw-changeMe!`).
> Some special characters are not possible because RQLite uses basic auth. Please use url-safe characters like Alphanumeric [0-9a-zA-Z], special characters $-_.+!*'(),.
Expand Down Expand Up @@ -65,14 +82,23 @@ Key features:
```
> When step is missing, container will use a random but secure generated key on each start.
4. Run `docker compose -f docker-compose.yml up -d` to start the containers defined in the Compose file
5. Run `docker compopse down` to stop container, networks, volumes, and images created by up.

### Setup with kubernetes
1. SECRETS
1. Apply the manifests in `kubernetes/manifests`
```shell
kubectl apply -R -f ./kubernetes/manifests
```
5. Run `docker compose down` to stop container, networks, volumes, and images created by up.

## Testing
To run all tests, you can use the following command in the root directory:
```shell
go test -v hsfl.de/group6/hsfl-master-ai-cloud-engineering/...
```

### Integration tests
If you want to include integrations tests in your test flow use the following command:
```shell
go test -v hsfl.de/group6/hsfl-master-ai-cloud-engineering/... --tags=integration
```
> Please note, that a with [Testcontainers](https://testcontainers.com/) compatible container daemon is needed.
> - Docker Desktop
> - Docker Engine on Linux
> - Testcontainers Cloud
## Authors

Expand Down
53 changes: 52 additions & 1 deletion src/http-proxy-service/README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,55 @@
# The http proxy service component
[![Run tests (http proxy service)](https://github.com/Onyxmoon/hsfl-master-ai-cloud-engineering/actions/workflows/run-tests-http-proxy-service.yml/badge.svg)](https://github.com/Onyxmoon/hsfl-master-ai-cloud-engineering/actions/workflows/run-tests-http-proxy-service.yml)

The http proxy service component is the proxy component as between all services and serves as main entry point for clients.
The http proxy service component is the gateway component as between all services and serves as main entry point for clients. This service routes incoming HTTP requests to the appropriate internal services based on the predefined proxy routes.

## Configuration
The service is configured through a YAML file which details the proxy routes and additional settings. A typical configuration looks as follows:
```yaml
listenAddress: 0.0.0.0:8080
proxyRoutes:
- name: User Service (User Info)
context: /api/v1/user
target: http://users:3001/api/v1/user
- name: User Service (Login)
context: /api/v1/authentication/login
target: http://users:3001/api/v1/authentication/login
- name: User Service (Registration)
context: /api/v1/authentication/register
target: http://users:3001/api/v1/authentication/register
- name: Shoppinglist Service (Lists)
context: /api/v1/shoppinglist
target: http://shoppinglists:3002/api/v1/shoppinglist
- name: Shoppinglist Service (Entries)
context: /api/v1/shoppinglistentries
target: http://shoppinglists:3002/api/v1/shoppinglistentries
- name: Product Service (Products)
context: /api/v1/product
target: http://products:3003/api/v1/product
- name: Product Service (Prices)
context: /api/v1/price
target: http://products:3003/api/v1/price
- name: Web Service
context: /
target: http://web:3000
```
### Configuring the Path to Configuration File
You can specify the path to the configuration file using the `PROXY_CONFIG_PATH` environment variable. If this variable is set, the service will use the specified path to load the configuration file, when not it defaults to `./config/proxyConfig.yaml`.

For example:
```shell
export PROXY_CONFIG_PATH=/path/to/your/config.yaml
```

### Explanation
- `listenAddress`: This defines the network address where the proxy service will listen for incoming requests.
- `proxyRoutes`: A list of routing definitions, each containing:
- `name`: A descriptive name for the route.
- `context`: The URL path that triggers this route.
- `target`: The internal service URL to which the request should be forwarded.

## Run service
After setting up the configuration file and the `PROXY_CONFIG_PATH` environment variable, run the HTTP Proxy Service. It will start listening on the specified `listenAddress`. Incoming requests are then analyzed and routed according to the matching `context` found in the `proxyRoutes` settings.

Also: See the main [README.md](/README.md) to get information for a complete containerized setup.
28 changes: 28 additions & 0 deletions src/load-balancer-service/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Load Balancer Service

This directory contains a Load Balancer designed to orchestrate Docker containers and distribute network load. It functions as a proxy, offering selectable algorithms to optimize the distribution of requests among containers.



## Algorithms
The load balancer supports several algorithms to optimize request distribution. Configure your choice of algorithm in the `config.yml` file. Available algorithms include:

- **Round Robin** (`round_robin`): Distributes requests evenly across all containers. This is a good default choice for general use.
- **Least Connections** (`least_connections`): Directs traffic to the container with the fewest active connections. Ideal for situations where session persistence is important.
- **Least Response Time** (`least_response_time`): Routes requests to the container with the lowest average response time. This is beneficial when you need to optimize for speed and performance.


## Configuration
To configure the load balancer, edit the `config.yml` file in the root of the directory. Here's an example configuration:

```yaml
image: onyxmoon/pw-web-service:latest
network: internal
replicas: 5
algorithm: round_robin
```
In this example:
- Use the Docker image onyxmoon/pw-web-service:latest for the load balanced containers.
- Connect the containers to specified network in docker. Please make sure that the service can access the network for health checking.
- Create 5 replicas of the container for load balancing.
- The selected algorithm is Round Robin
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
image: onyxmoon/pw-web-service:latest
network: dev-network
replicas: 5
algorithm: round_robin
1 change: 1 addition & 0 deletions src/load-balancer-service/config/config.web-service.yaml
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
image: onyxmoon/pw-web-service:latest
network: internal
replicas: 5
algorithm: round_robin
1 change: 1 addition & 0 deletions src/load-balancer-service/config/config.yaml
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
image: nginxdemos/hello
network: bridge
replicas: 5
algorithm: round_robin
15 changes: 13 additions & 2 deletions src/load-balancer-service/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,20 @@ func main() {
containers := defaultOrchestrator.StartContainers(configuration.Image, configuration.Replicas, configuration.Network)

// Initialize load balancer
var algorithm scheduler.NewScheduler
switch configuration.Algorithm {
case "round_robin":
algorithm = scheduler.NewRoundRobin
case "least_connections":
algorithm = scheduler.NewLeastConnections
case "least_response_time":
algorithm = scheduler.NewLeastResponseTime
default:
log.Fatal("unknown load balancing algorithm")
}

loadBalancer := balancer.NewBalancer(
defaultOrchestrator.GetContainerEndpoints(containers, configuration.Network),
scheduler.NewRoundRobin)
defaultOrchestrator.GetContainerEndpoints(containers, configuration.Network), algorithm)
loadBalancer.SetHealthCheckFunction(health.DefaultHealthCheck, 5*time.Second)

// Start web server
Expand Down
8 changes: 5 additions & 3 deletions src/load-balancer-service/utils/configurator.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@ import (
)

type Configuration struct {
Image string `yaml:"image"` // Image which should be run.
Replicas int `yaml:"replicas"` // Replicas number to run and load balance.
Network string `yaml:"network"` // Network where replicas belong to.
Image string `yaml:"image"` // Image which should be run.
Replicas int `yaml:"replicas"` // Replicas number to run and load balance.
Network string `yaml:"network"` // Network where replicas belong to.
Algorithm string `yaml:"algorithm"` // Algorithm to use for load balancing
}

const (
Expand All @@ -21,6 +22,7 @@ func GetConfig(path string) *Configuration {
viper.SetDefault("image", "nginxdemos/hello")
viper.SetDefault("replicas", 5)
viper.SetDefault("network", "bridge")
viper.SetDefault("algorithm", "round_robin")

if len(path) != 0 {
viper.SetConfigFile(path)
Expand Down
97 changes: 96 additions & 1 deletion src/product-service/README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,99 @@
# The product service component
[![Run tests (product-service)](https://github.com/Onyxmoon/hsfl-master-ai-cloud-engineering/actions/workflows/run-tests-product-service.yml/badge.svg)](https://github.com/Onyxmoon/hsfl-master-ai-cloud-engineering/actions/workflows/run-tests-product-service.yml)

The product service component allows to manage entities (CRUD) like the prices and the products.
The product service component allows to manage entities (modified CRUD) like the products and their prices. For future extensions, this service also makes the products available internally to other services via gRPC.

This service has access restrictions and requires a connection via gRPC to the [user service](/src/user-service) in order to authorize transmitted JWT tokens.

## API Endpoints

### Product Management

#### GET `/api/v1/product/`
- **Description:** Retrieves a list of all products.
- **Usage:** Returns an array of all products in the system.
- **Access control:**: Not restricted

#### GET `/api/v1/product/:productId`
- **Description:** Retrieves details of a product by its unique ID.
- **Usage:** Include the product ID as a URL parameter. Returns the product's details.
- **Access control:**: Not restricted

#### GET `/api/v1/product/ean/:productEan`
- **Description:** Fetches a specific product using its EAN (European Article Number).
- **Usage:** Requires the product's EAN as a URL parameter. Returns details of the product associated with the given EAN.
- **Access control:**: Not restricted

#### PUT `/api/v1/product/:productId`
- **Description:** Updates details of an existing product.
- **Usage:** Requires the product ID as a URL parameter and the updated product information in the request body.
- **Access control:**: Only merchants or admins can do this.

#### POST `/api/v1/product/`
- **Description:** Adds a new product to the system.
- **Usage:** Include product details in the request body.
- **Access control:**: Only merchants or admins can do this.

#### DELETE `/api/v1/product/:productId`
- **Description:** Deletes a specific product from the system.
- **Usage:** Requires the product ID as a URL parameter.
- **Access control:**: Only admins can do this.

### Price Management

#### GET `/api/v1/price/`
- **Description:** Retrieves pricing information for all products.
- **Usage:** Returns a list of prices for all products.
- **Access control:**: Not restricted

#### GET `/api/v1/price/user/:userId`
- **Description:** Fetches prices specifically tailored for a merchant user.
- **Usage:** Include the user ID as a URL parameter. Returns user-specific pricing details.
- **Access control:**: Not restricted

#### GET `/api/v1/price/product/:productId`
- **Description:** Retrieves all prices associated with a specific product.
- **Usage:** Requires the product ID as a URL parameter.
- **Access control:**: Not restricted

#### GET `/api/v1/price/:productId/:userId`
- **Description:** Fetches the price of a product for a specific merchant user.
- **Usage:** Include both the product ID and user ID as URL parameters.
- **Access control:**: Not restricted

#### PUT `/api/v1/price/:productId/:userId`
- **Description:** Updates the price of a product for a specific merchant user.
- **Usage:** Requires the product ID and user ID as URL parameters, and the updated price details in the request body.
- **Access control:**: Merchants can only update their own prices for a product.

#### POST `/api/v1/price/:productId/:userId`
- **Description:** Adds a new price for a product and merchant user combination.
- **Usage:** Include the product ID, user ID, and price details in the request body.
- **Access control:**: Merchants can only add their own prices for a product.

#### DELETE `/api/v1/price/:productId/:userId`
- **Description:** Removes a specific price entry for a product and merchant user.
- **Usage:** Requires both the product ID and user ID as URL parameters.
- **Access control:**: Merchants can only delete their own prices for a product.


## Configuration
You can configure this service with environmental variables or an environment file (.env). relative to the main application file.

### Example configuration
```dotenv
RQLITE_HOST="database"
RQLITE_PORT=4001
RQLITE_USER="db-user"
RQLITE_PASSWORD="db-pw-changeMe!"
HTTP_SERVER_PORT=3003
GRPC_SERVER_PORT=50053
GRPC_USER_SERVICE_TARGET="users:50051"
```

## Run service
You can operate the service either as a standalone application or within a containerized environment. The database is required. It is designed to automatically generate the necessary tables should they not already exist.

Also: See the main [README.md](/README.md) to get information for a complete containerized setup.
Loading

0 comments on commit dc0a535

Please sign in to comment.