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
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
.ipynb_checkpoints
.venv
__pycache__
/dependencies
/lambda_function.zip
/terraform/*.tfvars
/terraform/*.tfstate*
/terraform/.terraform*
13 changes: 10 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ POST 🔒 method is guarded by JWT token in standard header "bearer"

| Method | Endpoint | Info |
|---------|-----------------------|------------------------------------------------------------------------------|
| GET | `/api` | OpenAPI 3 definition |
| GET | `/token` | forwards (HTTP303) caller to where to obtain JWT token for posting to topic |
| GET | `/topics` | lists available topics |
| GET | `/topics/{topicName}` | schema for given topic |
Expand All @@ -41,11 +42,16 @@ There are 3 configs for this solution (in conf folder)

## Terraform Deplyoment
Whole solution expects to be deployed as lambda in AWS,
there are prepared terraform scripts to make initial deplyoment, and can be found in "terraform" fodler
All that is needed is supplementing variables for
there are prepared terraform scripts to make initial deplyoment, and can be found in "terraform" folder

Resulting lambda_function zip file needs to be uploaded to aws s3 bucket (since direct upload of zip likes to fail, might be related to poor network though)

All that is needed afterwards is supplementing variables for
- aws_region
- vpc_id
- vpc_endpoint
- resource prefix - all terraform resources would be prefixed my this prefix, usefull when mixed-in with something else
- lambda_source_bucket - the bucket where "lambda_function.zip" is already uploaded
- lambda_role_arn - the role for the lambda, should be able to make HTTP calls to wherever kafka server lives
- lambda_vpc_subnet_ids

Expand All @@ -59,4 +65,5 @@ Jupyter notebook, with one cell for lambda initialization and one cell per metho
Obviously using it requires correct configs to be in place (PUBLIC key is being loaded during initilization)

### Preapare Deployment
shell script for fetching pithon requirements and ziping it together with sources and config into lambda archive, ready to be used by terraform
shell script for fetching pithon requirements and ziping it together with sources and config into lambda archive
it needs to be uploaded to s3 bucket first before running the terraform
165 changes: 165 additions & 0 deletions conf/api.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
openapi: 3.0.0
info:
title: Event Gate
version: 0.0.0
description: This API provides topic management for an event bus.

servers:
- url: https://{id}-vpce-{vpce}.execute-api.{region}.amazonaws.com/DEV
variables:
id:
default: 01234567ab
description: API Gateway ID
vpce:
default: '01234567abcdef012'
description: VPC endpoint
region:
default: 'af-south-1'
description: AWS Region

paths:
/api:
get:
summary: Open API specification
description: Open API specification
responses:
'200':
description: Open API specification
content:
application/vnd.oai.openapi:
schema:
type: string

/token:
get:
summary: Login to the service
description: Allows a user to obtain credentials (JWT token) from LoginService for the service
responses:
'303':
description: Redirect to actual address of Loing service which performs auth up to its capabilities

/topics:
get:
summary: Get a list of topics
description: Returns a list of all available topics.
responses:
'200':
description: A list of topics
content:
application/json:
schema:
type: array
items:
type: string

/topics/{topicName}:
get:
summary: Get schema for a specific topic
description: Returns the schema for a specified topic using [JSON Schema](https://json-schema.org/).
parameters:
- name: topicName
in: path
required: true
schema:
type: string
description: Name of the topic
responses:
'200':
description: Key-value pairs representing the schema for the topic
content:
application/json:
schema:
type: object
additionalProperties:
type: string
'404':
description: Topic not found
content:
application/json:
schema:
type: object
properties:
error:
type: string

put:
summary: Publish an event to a topic
description: Publishes an event to the event bus under the specified topic. User must be authenticated with a JWT token.
security:
- bearerAuth: []
parameters:
- name: topicName
in: path
required: true
schema:
type: string
description: Name of the topic
requestBody:
description: Event data to be published
required: true
content:
application/json:
schema:
type: object
additionalProperties: true
responses:
'200':
description: Event successfully published
'400':
description: Invalid event data
content:
application/json:
schema:
type: object
properties:
error:
type: string
'401':
description: Unauthorized
content:
application/json:
schema:
type: object
properties:
error:
type: string
'403':
description: Forbidden
content:
application/json:
schema:
type: object
properties:
error:
type: string
'404':
description: Topic not found
content:
application/json:
schema:
type: object
properties:
error:
type: string

/terminate:
get:
summary: Terminates lambda environment
description: Facilitates fresh start of lambda environment on next invocation (i.e. loads fresh public keys and configs)
responses:
'502':
description: Internal server error or bad gateway error
content:
application/json:
schema:
type: object
properties:
error:
type: string

components:
securitySchemes:
bearerAuth:
type: http
scheme: bearer
bearerFormat: JWT
15 changes: 14 additions & 1 deletion scripts/notebook.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,19 @@
"jwtToken = \"eyJhb...\""
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "3ae218c5-8174-41bc-be5d-9487d68260c5",
"metadata": {},
"outputs": [],
"source": [
"src.event_gate_lambda.lambda_handler({\n",
" \"httpMethod\": \"GET\",\n",
" \"resource\": \"/api\"\n",
"}, {})"
]
},
{
"cell_type": "code",
"execution_count": null,
Expand Down Expand Up @@ -128,7 +141,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.12.2"
"version": "3.12.6"
}
},
"nbformat": 4,
Expand Down
11 changes: 11 additions & 0 deletions src/event_gate_lambda.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@
logger.setLevel(logging.INFO)
logger.addHandler(logging.StreamHandler())

with open("conf/api.yaml", "r") as file:
API = file.read()

with open("conf/config.json", "r") as file:
CONFIG = json.load(file)

Expand Down Expand Up @@ -82,6 +85,12 @@ def kafkaWrite(topicName, message):
logger.info("OK")
return 202

def getApi():
return {
"statusCode": 200,
"body": API
}

def getToken():
logger.info("Handling GET Token")
return {
Expand Down Expand Up @@ -139,6 +148,8 @@ def postTopicMessage(topicName, topicMessage, tokenEncoded):

def lambda_handler(event, context):
try:
if event["resource"].lower() == "/api":
return getApi()
if event["resource"].lower() == "/token":
return getToken()
if event["resource"].lower() == "/topics":
Expand Down
23 changes: 23 additions & 0 deletions terraform/api_gateway.tf
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,28 @@ resource "aws_api_gateway_rest_api" "event_gate_api" {
})
}

resource "aws_api_gateway_resource" "event_gate_api_api" {
rest_api_id = aws_api_gateway_rest_api.event_gate_api.id
parent_id = aws_api_gateway_rest_api.event_gate_api.root_resource_id
path_part = "api"
}

resource "aws_api_gateway_method" "event_gate_api_api_get" {
rest_api_id = aws_api_gateway_rest_api.event_gate_api.id
resource_id = aws_api_gateway_resource.event_gate_api_api.id
authorization = "NONE"
http_method = "GET"
}

resource "aws_api_gateway_integration" "event_gate_api_api_get_integration" {
rest_api_id = aws_api_gateway_rest_api.event_gate_api.id
resource_id = aws_api_gateway_resource.event_gate_api_api.id
http_method = aws_api_gateway_method.event_gate_api_api_get.http_method
integration_http_method = "POST"
type = "AWS_PROXY"
uri = aws_lambda_function.event_gate_lambda.invoke_arn
}

resource "aws_api_gateway_resource" "event_gate_api_token" {
rest_api_id = aws_api_gateway_rest_api.event_gate_api.id
parent_id = aws_api_gateway_rest_api.event_gate_api.root_resource_id
Expand Down Expand Up @@ -145,6 +167,7 @@ resource "aws_api_gateway_deployment" "event_gate_api_deployment" {
rest_api_id = aws_api_gateway_rest_api.event_gate_api.id
triggers = {
redeployment = sha1(jsonencode([
aws_api_gateway_integration.event_gate_api_api_get_integration,
aws_api_gateway_integration.event_gate_api_token_get_integration,
aws_api_gateway_integration.event_gate_api_topics_get_integration,
aws_api_gateway_integration.event_gate_api_topic_name_get_integration,
Expand Down
4 changes: 2 additions & 2 deletions terraform/lambda.tf
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,11 @@ resource "aws_vpc_security_group_egress_rule" "allow_all_traffic_ipv4" {
}

resource "aws_lambda_function" "event_gate_lambda" {
filename = "../lambda_function.zip"
s3_bucket = var.lambda_source_bucket
s3_key = "lambda_function.zip"
function_name = "${var.resource_prefix}event-gate-lambda"
role = var.lambda_role_arn
handler = "event_gate_lambda.lambda_handler"
source_code_hash = filebase64sha256("../lambda_function.zip")
runtime = "python3.12"
vpc_config {
subnet_ids = var.lambda_vpc_subnet_ids
Expand Down
1 change: 1 addition & 0 deletions terraform/variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ variable "vpc_endpoint" {}
variable "resource_prefix" {}
variable "lambda_role_arn" {}
variable "lambda_vpc_subnet_ids" {}
variable "lambda_source_bucket" {}