Skip to content

Commit

Permalink
Merge pull request #123 from bcgov/dev
Browse files Browse the repository at this point in the history
include sessionCookieEnabled when syncing routes
  • Loading branch information
ikethecoder authored Oct 22, 2024
2 parents 20eca47 + 4890602 commit 16ebe40
Show file tree
Hide file tree
Showing 10 changed files with 633 additions and 486 deletions.
6 changes: 4 additions & 2 deletions .github/workflows/dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,18 +24,20 @@ jobs:
fetch-depth: 0
- uses: actions/setup-python@v5
with:
python-version: "3.12"
python-version: "3.11"
- name: Install deps
run: |
sudo apt update
sudo apt install -y pipx git
pipx ensurepath
pipx install poetry
- name: Test coverage for Gateway API
run: |
export PATH=/root/.local/bin:$PATH
cd microservices/gatewayApi
poetry install --no-root
poetry env use 3.11
poetry install --no-root --no-cache
ENV=test GITHASH=11223344 \
poetry run coverage run --branch -m pytest -s -v
poetry run coverage xml
Expand Down
2 changes: 1 addition & 1 deletion microservices/gatewayApi/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
# && go mod download \
# && CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o deck \
# -ldflags "-s -w -X github.com/kong/deck/cmd.VERSION=$TAG -X github.com/kong/deck/cmd.COMMIT=$COMMIT"
FROM python:3.9-alpine3.18
FROM python:3.11-alpine3.20

RUN mkdir /.kube

Expand Down
931 changes: 462 additions & 469 deletions microservices/gatewayApi/poetry.lock

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions microservices/gatewayApi/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ description = "Gateway API for multi-tenant configuration on Kong API Gateway"
authors = []

[tool.poetry.dependencies]
python = "^3.8"
python = "^3.11"
werkzeug = "2.2.2"
ply = "3.10"
cryptography = "38.0.4"
Expand All @@ -23,7 +23,7 @@ gevent = "22.10.2"
# greenlet = "2.0.2"
gunicorn = "20.1.0"
python-keycloak = "0.22.0"
requests = "2.31.0"
requests = "^2.32"
flask-jwt-simple = "0.0.3"

[tool.poetry.dev-dependencies]
Expand Down
2 changes: 1 addition & 1 deletion microservices/gatewayJobScheduler/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ def transform_data_by_ns(data):

logger.debug("%s - %s" % (namespace, ns_attr_dict[namespace].get('perm-data-plane', [''])))

# check if namespace has data plane attribute
# check if namespace has data plane attribute and needs to be synced
if ns_attr_dict[namespace].get('perm-data-plane', [''])[0] == os.getenv('DATA_PLANE'):
session_cookie_enabled = False
if 'aps.route.session.cookie.enabled' in route_obj['tags']:
Expand Down
34 changes: 27 additions & 7 deletions microservices/kubeApi/routers/routes.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import uuid
import base64
from fastapi import APIRouter, HTTPException, Depends, Request
from fastapi.responses import JSONResponse
from pydantic.main import BaseModel
from starlette.responses import Response
from clients.ocp_routes import get_gwa_ocp_routes, kubectl_delete, prepare_apply_routes, apply_routes, prepare_mismatched_routes, delete_routes
Expand Down Expand Up @@ -168,13 +169,14 @@ async def verify_and_create_routes(namespace: str, request: Request):
"name": route["metadata"]["name"],
"selectTag": route["metadata"]["labels"]["aps-select-tag"],
"host": route["spec"]["host"],
"dataPlane": route["spec"]["to"]["name"]
"dataPlane": route["spec"]["to"]["name"],
"sessionCookieEnabled": True if route["metadata"]["labels"].get("aps-template-version") == "v1" else False
}
)

insert_batch = [x for x in source_routes if not in_list(x, existing_routes)]
delete_batch = [y for y in existing_routes if not in_list(y, source_routes)]

delete_batch = [y for y in existing_routes if not in_list_by_name(y, source_routes)]
logger.debug("insert batch: " + str(insert_batch))

logger.debug("delete batch: " + str(delete_batch))
Expand All @@ -183,6 +185,9 @@ async def verify_and_create_routes(namespace: str, request: Request):
# this info from ns_attributes
ns_template_version = "v2"

inserted_count = 0
deleted_count = 0

try:
if len(insert_batch) > 0:
source_folder = "%s/%s-%s" % ('/tmp/sync', f'{datetime.now():%Y%m%d%H%M%S}', secrets.token_hex(5))
Expand All @@ -193,12 +198,16 @@ async def verify_and_create_routes(namespace: str, request: Request):
for route in insert_batch:
overrides = {}
if 'sessionCookieEnabled' in route and route['sessionCookieEnabled']:
overrides['aps.route.session.cookie.enabled'] = [ route['host'] ]
overrides['aps.route.session.cookie.enabled'] = [route['host']]

route_count = prepare_apply_routes(namespace, route['selectTag'], [
route['host']], source_folder, route["dataPlane"], ns_template_version, overrides)

logger.debug("[%s] - Prepared %d routes" % (namespace, route_count))
apply_routes(source_folder)
logger.debug("[%s] - Applied %d routes" % (namespace, route_count))

inserted_count += route_count
except Exception as ex:
traceback.print_exc()
logger.error("Error creating routes. %s" % (ex))
Expand All @@ -216,6 +225,7 @@ async def verify_and_create_routes(namespace: str, request: Request):
try:
kubectl_delete('route', route["name"])
logger.debug("[%s] - Deleted route %s" % (namespace, route["name"]))
deleted_count += 1
except Exception as ex:
traceback.print_exc()
logger.error("Failed deleting route %s" % route["name"])
Expand All @@ -226,8 +236,12 @@ async def verify_and_create_routes(namespace: str, request: Request):
traceback.print_exc()
logger.error("Failed deleting route %s" % route["name"])
raise HTTPException(status_code=400, detail=str(sys.exc_info()[0]))
return Response(status_code=200, content='{"message": "synced"}')

return JSONResponse(status_code=200, content={
"message": "synced",
"inserted_count": inserted_count,
"deleted_count": deleted_count
})

def get_data_plane(ns_attributes):
default_data_plane = settings.defaultDataPlane
Expand All @@ -236,12 +250,18 @@ def get_data_plane(ns_attributes):
def get_template_version(ns_attributes):
return ns_attributes.get('template-version', ["v2"])[0]

def in_list (match, list):
def in_list(match, list):
match_ref = build_ref(match)
for item in list:
if build_ref(item) == match_ref:
return True
return False

def build_ref(v):
return "%s%s%s%s" % (v['name'], v['selectTag'], v['host'], v['dataPlane'])
return "%s%s%s%s%s" % (v['name'], v['selectTag'], v['host'], v['dataPlane'], v['sessionCookieEnabled'])

def in_list_by_name(match, list):
for item in list:
if item['name'] == match['name']:
return True
return False
53 changes: 51 additions & 2 deletions microservices/kubeApi/tests/routers/test_bulk_sync.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ def test_bulk_sync(client):
"metadata": {
"name": "wild-ns-example",
"labels": {
"aps-select-tag": "ns.EXAMPLE-NS"
"aps-select-tag": "ns.EXAMPLE-NS",
"aps-template-version": "v2"
}
},
"spec": {
Expand All @@ -28,8 +29,56 @@ def test_bulk_sync(client):
"name": "wild-ns-example",
"selectTag": "ns.EXAMPLE-NS",
"dataPlane": "data-plane-1",
"host": "abc.api.gov.bc.ca"
"host": "abc.api.gov.bc.ca",
"sessionCookieEnabled": False
}]
response = client.post('/namespaces/examplens/routes/sync', json=data)
assert response.status_code == 200
assert response.json()['message'] == 'synced'
assert response.json()['inserted_count'] == 0
assert response.json()['deleted_count'] == 0

def test_bulk_sync_change_host(client):
with mock.patch("routers.routes.get_gwa_ocp_routes") as call:
call.return_value = [{
"metadata": {
"name": "wild-ns-example-xyz",
"labels": {
"aps-select-tag": "ns.EXAMPLE-NS",
"aps-template-version": "v2"
}
},
"spec": {
"host": "xyz.api.gov.bc.ca",
"to": {
"name": "data-plane-1"
}
}
}]


with mock.patch("routers.routes.prepare_apply_routes") as call_apply:
call_apply.return_value = 1

with mock.patch("routers.routes.apply_routes") as call_mismatch_routes:
call_mismatch_routes.return_value = None

with mock.patch("routers.routes.delete_route") as mock_delete_route:
mock_delete_route.return_value = None # Simulate successful deletion

with mock.patch("routers.routes.kubectl_delete") as mock_kubectl_delete:
mock_kubectl_delete.return_value = None # Simulate successful kubectl deletion

data = [{
"name": "wild-ns-example-abc",
"selectTag": "ns.EXAMPLE-NS",
"dataPlane": "data-plane-1",
"host": "abc.api.gov.bc.ca",
"sessionCookieEnabled": False
}]
response = client.post('/namespaces/examplens/routes/sync', json=data)
assert response.status_code == 200
assert response.json()['message'] == 'synced'
assert response.json()['inserted_count'] == 1
assert response.json()['deleted_count'] == 1

Original file line number Diff line number Diff line change
Expand Up @@ -72,9 +72,12 @@ def test_bulk_sync_new_route(client):
"name": "wild-ns-example",
"selectTag": "ns.EXAMPLE-NS",
"dataPlane": "data-plane-1",
"host": "abc.api.gov.bc.ca"
"host": "abc.api.gov.bc.ca",
"sessionCookieEnabled": False
}]
response = client.post('/namespaces/examplens/routes/sync', json=data)
assert response.status_code == 200
assert response.json()['message'] == 'synced'
assert response.json()['inserted_count'] == 1


Original file line number Diff line number Diff line change
Expand Up @@ -76,4 +76,4 @@ def test_bulk_sync_new_route(client):
response = client.post('/namespaces/examplens/routes/sync', json=data)
assert response.status_code == 200
assert response.json()['message'] == 'synced'

assert response.json()['inserted_count'] == 1
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
from unittest import mock

def test_bulk_sync_session_cookie(client):
with mock.patch("routers.routes.get_gwa_ocp_routes") as call:
call.return_value = [{
"metadata": {
"name": "wild-ns-example",
"labels": {
"aps-select-tag": "ns.EXAMPLE-NS",
"aps-template-version": "v1"
}
},
"spec": {
"host": "abc.api.gov.bc.ca",
"to": {
"name": "data-plane-1"
}
}
}]

with mock.patch("routers.routes.prepare_apply_routes") as call_apply:
call_apply.return_value = 0

with mock.patch("routers.routes.apply_routes") as call_mismatch_routes:
call_mismatch_routes.return_value = None

data = [{
"name": "wild-ns-example",
"selectTag": "ns.EXAMPLE-NS",
"dataPlane": "data-plane-1",
"host": "abc.api.gov.bc.ca",
"sessionCookieEnabled": True
}]
response = client.post('/namespaces/examplens/routes/sync', json=data)
assert response.status_code == 200
assert response.json()['message'] == 'synced'

def test_bulk_sync_session_cookie_change(client):
with mock.patch("routers.routes.get_gwa_ocp_routes") as mock_get_routes:
mock_get_routes.return_value = [{
"metadata": {
"name": "wild-ns-example",
"labels": {
"aps-select-tag": "ns.EXAMPLE-NS",
"aps-template-version": "v2"
}
},
"spec": {
"host": "abc.api.gov.bc.ca",
"to": {
"name": "data-plane-1"
}
}
}]

with mock.patch("routers.routes.prepare_apply_routes") as mock_prepare_apply:
mock_prepare_apply.return_value = 1

with mock.patch("routers.routes.apply_routes") as mock_apply_routes:
mock_apply_routes.return_value = None

with mock.patch("routers.routes.delete_route") as mock_delete_route:
mock_delete_route.return_value = None # Simulate successful deletion

with mock.patch("routers.routes.kubectl_delete") as mock_kubectl_delete:
mock_kubectl_delete.return_value = None # Simulate successful kubectl deletion

data = [{
"name": "wild-ns-example",
"selectTag": "ns.EXAMPLE-NS",
"dataPlane": "data-plane-1",
"host": "abc.api.gov.bc.ca",
"sessionCookieEnabled": True
}]
response = client.post('/namespaces/examplens/routes/sync', json=data)

assert response.status_code == 200
assert response.json()['message'] == 'synced'
assert response.json()['inserted_count'] == 1
assert response.json()['deleted_count'] == 0

0 comments on commit 16ebe40

Please sign in to comment.