Skip to content

Commit

Permalink
Merge pull request #18 from Flagsmith/Release/v2.1.0
Browse files Browse the repository at this point in the history
Release v2.1.0
  • Loading branch information
gagantrivedi authored Aug 22, 2022
2 parents 6754534 + ecc833c commit eac0b64
Show file tree
Hide file tree
Showing 14 changed files with 422 additions and 12 deletions.
75 changes: 75 additions & 0 deletions .github/actions/sse-deploy-ecs/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
name: Deploy SSE to ECS
description: "Deploy the SSE service to ECS"

inputs:
aws_access_key_id:
description: "The AWS access key ID to use for deploying to ECS."
required: true
aws_secret_access_key:
description: "The AWS secret access key to use for deploying to ECS."
required: true
aws_ecs_cluster_name:
description: "The name of the ECS cluster to deploy to."
required: true
aws_ecs_service_name:
description: "The name of the ECS service to deploy to."
required: true
aws_ecr_repository_arn:
description: "The ARN of the ECR repository to deploy docker image to."
required: true
aws_task_definitions_directory_path:
description: "The local path in the repository to the json file containing the task definition template"
required: true

runs:
using: composite

steps:
- name: Set ECR tag
id: ecr-tag-variable
run: echo ::set-output name=tag::${GITHUB_REF#refs/*/}
shell: bash

- name: Configure AWS Credentials
uses: aws-actions/configure-aws-credentials@v1
with:
aws-access-key-id: ${{ inputs.aws_access_key_id }}
aws-secret-access-key: ${{ inputs.aws_secret_access_key }}
aws-region: eu-west-2

- name: Login to Amazon ECR
id: login-ecr
uses: aws-actions/amazon-ecr-login@v1

- name: Build, tag, and push image to Amazon ECR
id: build-image
env:
IMAGE_TAG: ${{ steps.ecr-tag-variable.outputs.tag }}
ECR_REPOSITORY: ${{ inputs.aws_ecr_repository_arn }}
DOCKER_BUILDKIT: "1"
run: |
echo "Building docker image with URL: "
echo $ECR_REPOSITORY:$IMAGE_TAG
docker build -t $ECR_REPOSITORY:$IMAGE_TAG -f Dockerfile .
docker push $ECR_REPOSITORY:$IMAGE_TAG
echo "::set-output name=image::$ECR_REPOSITORY:$IMAGE_TAG"
shell: bash

- name: Fill in the new image ID in the Amazon SSE task definition
id: task-def-api
uses: aws-actions/amazon-ecs-render-task-definition@v1
with:
task-definition: ${{ inputs.aws_task_definitions_directory_path }}/ecs-task-definition-sse.json
container-name: flagsmith-sse
image: ${{ steps.build-image.outputs.image }}

- name: Deploy Amazon ECS SSE task definition
uses: aws-actions/amazon-ecs-deploy-task-definition@v1
with:
cluster: ${{ inputs.aws_ecs_cluster_name }}
service: ${{ inputs.aws_ecs_service_name }}
task-definition: ${{ steps.task-def-api.outputs.task-definition }}

- name: Wait for service to be stable
run: aws ecs wait services-stable --cluster ${{ inputs.aws_ecs_cluster_name }} --services ${{ inputs.aws_ecs_service_name }}
shell: bash
33 changes: 33 additions & 0 deletions .github/workflows/sse-deploy-staging-ecs.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
name: Deploy SSE to Staging ECS

on:
push:
branches:
- main
paths:
- "api/**"
- ".github/**"
- 'infrastructure/aws/staging/**'

jobs:
deploy-staging-ecs:
runs-on: ubuntu-latest
name: Deploy SSE to Staging ECS
environment: staging

steps:
- name: Cloning repo
uses: actions/checkout@v2
with:
fetch-depth: 0

- name: Deploy SSE to Staging
uses: ./.github/actions/sse-deploy-ecs
with:
aws_access_key_id: AKIAUM26IRCPH7BHBLVE
aws_secret_access_key: ${{ secrets.STAGING_AWS_SECRET_ACCESS_KEY }}
aws_ecs_cluster_name: flagsmith-api-cluster-eu-west-2-f241261
aws_ecs_service_name: flagsmith-sse-svc-eu-west-2-43f7af2
aws_ecr_repository_arn: 302456015006.dkr.ecr.eu-west-2.amazonaws.com/flagsmith-sse-ecr-fa16ac8
aws_task_definitions_directory_path: infrastructure/aws/staging

44 changes: 44 additions & 0 deletions infrastructure/aws/staging/ecs-task-definition-sse.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
{
"family": "flagsmith-sse",
"networkMode": "awsvpc",
"executionRoleArn": "arn:aws:iam::302456015006:role/task-sse-role-8474d2a",
"taskRoleArn": "arn:aws:iam::302456015006:role/task-sse-role-8474d2a",
"containerDefinitions": [
{
"name": "flagsmith-sse",
"command": [
"uvicorn",
"src.main:app",
"--host",
"0.0.0.0",
"--port",
"8000",
"--no-access-log"
],
"cpu": 0,
"portMappings": [
{
"containerPort": 8000,
"hostPort": 8000,
"protocol": "tcp"
}
],
"essential": true,
"environment": [],
"secrets": [],
"logConfiguration": {
"logDriver": "awslogs",
"options": {
"awslogs-group": "flagsmith-sse-eu-west-2-4a10ba2",
"awslogs-region": "eu-west-2",
"awslogs-stream-prefix": "awslogs-flagsmith-sse"
}
}
}
],
"requiresCompatibilities": [
"FARGATE"
],
"cpu": "1024",
"memory": "2048"
}
10 changes: 9 additions & 1 deletion requirements-dev.in
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,12 @@ flake8
pytest
pytest-asyncio
pytest-mock
reorder-python-imports
reorder-python-imports
httpx
# lock the version because `starlette`(from requirements.in) explicitly depends on it
# but httpx tries to fetch the latest version causing conflict between
# requirements.txt and requirements-dev.txt
anyio==3.4.0

# conflict between `requests` and httpx
certifi==2021.10.8
28 changes: 27 additions & 1 deletion requirements-dev.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@
#
# pip-compile requirements-dev.in
#
anyio==3.4.0
# via
# -r requirements-dev.in
# httpcore
aspy-refactor-imports==3.0.1
# via reorder-python-imports
astroid==2.9.0
Expand All @@ -16,6 +20,11 @@ backports-entry-points-selectable==1.1.1
# via virtualenv
black==22.6.0
# via -r requirements-dev.in
certifi==2021.10.8
# via
# -r requirements-dev.in
# httpcore
# httpx
cfgv==3.3.1
# via pre-commit
click==8.0.3
Expand All @@ -26,8 +35,18 @@ filelock==3.4.0
# via virtualenv
flake8==4.0.1
# via -r requirements-dev.in
h11==0.12.0
# via httpcore
httpcore==0.15.0
# via httpx
httpx==0.23.0
# via -r requirements-dev.in
identify==2.4.0
# via pre-commit
idna==3.3
# via
# anyio
# rfc3986
iniconfig==1.1.1
# via pytest
isort==5.10.1
Expand Down Expand Up @@ -74,16 +93,23 @@ pytest==6.2.5
# -r requirements-dev.in
# pytest-asyncio
# pytest-mock
pytest-asyncio==0.16.0
pytest-asyncio==0.19.0
# via -r requirements-dev.in
pytest-mock==3.6.1
# via -r requirements-dev.in
pyyaml==6.0
# via pre-commit
reorder-python-imports==3.0.1
# via -r requirements-dev.in
rfc3986[idna2008]==1.5.0
# via httpx
six==1.16.0
# via virtualenv
sniffio==1.2.0
# via
# anyio
# httpcore
# httpx
toml==0.10.2
# via
# autopep8
Expand Down
5 changes: 5 additions & 0 deletions requirements.in
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,8 @@ flagsmith-flag-engine
python-decouple
python-dotenv
pydantic
# sse-stuff
sse-starlette
asyncio
SQLAlchemy
aiosqlite
26 changes: 20 additions & 6 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,26 @@
# This file is autogenerated by pip-compile with python 3.10
# To update, run:
#
# pip-compile
# pip-compile requirements.in
#
aiosqlite==0.17.0
# via -r requirements.in
anyio==3.4.0
# via starlette
asyncio==3.4.3
# via -r requirements.in
certifi==2021.10.8
# via requests
charset-normalizer==2.0.9
# via requests
click==8.0.3
# via uvicorn
fastapi==0.70.0
fastapi==0.79.0
# via -r requirements.in
flagsmith-flag-engine==1.5.0
# via -r requirements.in
greenlet==1.1.2
# via sqlalchemy
h11==0.12.0
# via uvicorn
idna==3.3
Expand All @@ -34,14 +40,22 @@ python-decouple==3.5
# via -r requirements.in
python-dotenv==0.19.2
# via -r requirements.in
requests==2.26.0
requests==2.28.1
# via -r requirements.in
sniffio==1.2.0
# via anyio
starlette==0.16.0
# via fastapi
sqlalchemy==1.4.39
# via -r requirements.in
sse-starlette==1.1.1
# via -r requirements.in
starlette==0.19.1
# via
# fastapi
# sse-starlette
typing-extensions==4.0.1
# via pydantic
# via
# aiosqlite
# pydantic
urllib3==1.26.7
# via requests
uvicorn==0.18.2
Expand Down
23 changes: 19 additions & 4 deletions src/main.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from fastapi import FastAPI
from fastapi import Header
from fastapi.responses import JSONResponse
from fastapi.middleware.cors import CORSMiddleware
from flag_engine.engine import get_environment_feature_state
from flag_engine.engine import get_environment_feature_states
from flag_engine.engine import get_identity_feature_states
Expand All @@ -12,17 +12,22 @@
from .schemas import APIFeatureStateSchema
from .schemas import APITraitSchema
from .settings import Settings
from .sse import router as sse_router
from fastapi_utils.tasks import repeat_every

app = FastAPI()
settings = Settings()
cache_service = CacheService(settings)


fs_schema = APIFeatureStateSchema()
trait_schema = APITraitSchema()


@app.get("/health")
def health_check():
return {"status": "ok"}


@app.get("/api/v1/flags/")
def flags(feature: str = None, x_environment_key: str = Header(None)):
environment_document = cache_service.get_environment(x_environment_key)
Expand All @@ -35,7 +40,7 @@ def flags(feature: str = None, x_environment_key: str = Header(None)):
feature_states = get_environment_feature_states(environment)
data = fs_schema.dump(feature_states, many=True)

return JSONResponse(content=data)
return data


def _get_fs_schema(identity_model: IdentityModel):
Expand Down Expand Up @@ -63,10 +68,20 @@ def identity(
"traits": trait_schema.dump(trait_models, many=True),
"flags": fs_schema.dump(flags, many=True),
}
return JSONResponse(content=data)
return data


@app.on_event("startup")
@repeat_every(seconds=settings.api_poll_frequency, raise_exceptions=True)
def refresh_cache():
cache_service.refresh()


app.add_middleware(
CORSMiddleware,
allow_origins=settings.allow_origins,
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
app.include_router(sse_router)
6 changes: 6 additions & 0 deletions src/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,12 @@ class Settings(BaseSettings):
api_url: HttpUrl = "https://edge.api.flagsmith.com/api/v1"
api_poll_frequency: int = 10

# sse settings
stream_delay: int = 1 # seconds
retry_timeout: int = 15000 # milliseconds
max_stream_age: int = 30 # seconds
allow_origins: List[str] = ["*"]

class Config:
env_file = "config.json"
env_file_encoding = "utf-8"
Expand Down
Loading

0 comments on commit eac0b64

Please sign in to comment.