Skip to content

Commit 4890602

Browse files
authored
Merge pull request #121 from bcgov/feature/sync-route-template-v
include sessionCookieEnabled when syncing routes
2 parents eaa8ea6 + e8ac803 commit 4890602

File tree

10 files changed

+633
-486
lines changed

10 files changed

+633
-486
lines changed

.github/workflows/dev.yml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,18 +24,20 @@ jobs:
2424
fetch-depth: 0
2525
- uses: actions/setup-python@v5
2626
with:
27-
python-version: "3.12"
27+
python-version: "3.11"
2828
- name: Install deps
2929
run: |
3030
sudo apt update
3131
sudo apt install -y pipx git
3232
pipx ensurepath
3333
pipx install poetry
34+
3435
- name: Test coverage for Gateway API
3536
run: |
3637
export PATH=/root/.local/bin:$PATH
3738
cd microservices/gatewayApi
38-
poetry install --no-root
39+
poetry env use 3.11
40+
poetry install --no-root --no-cache
3941
ENV=test GITHASH=11223344 \
4042
poetry run coverage run --branch -m pytest -s -v
4143
poetry run coverage xml

microservices/gatewayApi/Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
# && go mod download \
66
# && CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o deck \
77
# -ldflags "-s -w -X github.com/kong/deck/cmd.VERSION=$TAG -X github.com/kong/deck/cmd.COMMIT=$COMMIT"
8-
FROM python:3.9-alpine3.18
8+
FROM python:3.11-alpine3.20
99

1010
RUN mkdir /.kube
1111

microservices/gatewayApi/poetry.lock

Lines changed: 462 additions & 469 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

microservices/gatewayApi/pyproject.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ description = "Gateway API for multi-tenant configuration on Kong API Gateway"
66
authors = []
77

88
[tool.poetry.dependencies]
9-
python = "^3.8"
9+
python = "^3.11"
1010
werkzeug = "2.2.2"
1111
ply = "3.10"
1212
cryptography = "38.0.4"
@@ -23,7 +23,7 @@ gevent = "22.10.2"
2323
# greenlet = "2.0.2"
2424
gunicorn = "20.1.0"
2525
python-keycloak = "0.22.0"
26-
requests = "2.31.0"
26+
requests = "^2.32"
2727
flask-jwt-simple = "0.0.3"
2828

2929
[tool.poetry.dev-dependencies]

microservices/gatewayJobScheduler/app.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ def transform_data_by_ns(data):
2121

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

24-
# check if namespace has data plane attribute
24+
# check if namespace has data plane attribute and needs to be synced
2525
if ns_attr_dict[namespace].get('perm-data-plane', [''])[0] == os.getenv('DATA_PLANE'):
2626
session_cookie_enabled = False
2727
if 'aps.route.session.cookie.enabled' in route_obj['tags']:

microservices/kubeApi/routers/routes.py

Lines changed: 27 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import uuid
22
import base64
33
from fastapi import APIRouter, HTTPException, Depends, Request
4+
from fastapi.responses import JSONResponse
45
from pydantic.main import BaseModel
56
from starlette.responses import Response
67
from clients.ocp_routes import get_gwa_ocp_routes, kubectl_delete, prepare_apply_routes, apply_routes, prepare_mismatched_routes, delete_routes
@@ -168,13 +169,14 @@ async def verify_and_create_routes(namespace: str, request: Request):
168169
"name": route["metadata"]["name"],
169170
"selectTag": route["metadata"]["labels"]["aps-select-tag"],
170171
"host": route["spec"]["host"],
171-
"dataPlane": route["spec"]["to"]["name"]
172+
"dataPlane": route["spec"]["to"]["name"],
173+
"sessionCookieEnabled": True if route["metadata"]["labels"].get("aps-template-version") == "v1" else False
172174
}
173175
)
174176

175177
insert_batch = [x for x in source_routes if not in_list(x, existing_routes)]
176-
delete_batch = [y for y in existing_routes if not in_list(y, source_routes)]
177-
178+
delete_batch = [y for y in existing_routes if not in_list_by_name(y, source_routes)]
179+
178180
logger.debug("insert batch: " + str(insert_batch))
179181

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

188+
inserted_count = 0
189+
deleted_count = 0
190+
186191
try:
187192
if len(insert_batch) > 0:
188193
source_folder = "%s/%s-%s" % ('/tmp/sync', f'{datetime.now():%Y%m%d%H%M%S}', secrets.token_hex(5))
@@ -193,12 +198,16 @@ async def verify_and_create_routes(namespace: str, request: Request):
193198
for route in insert_batch:
194199
overrides = {}
195200
if 'sessionCookieEnabled' in route and route['sessionCookieEnabled']:
196-
overrides['aps.route.session.cookie.enabled'] = [ route['host'] ]
201+
overrides['aps.route.session.cookie.enabled'] = [route['host']]
202+
197203
route_count = prepare_apply_routes(namespace, route['selectTag'], [
198204
route['host']], source_folder, route["dataPlane"], ns_template_version, overrides)
205+
199206
logger.debug("[%s] - Prepared %d routes" % (namespace, route_count))
200207
apply_routes(source_folder)
201208
logger.debug("[%s] - Applied %d routes" % (namespace, route_count))
209+
210+
inserted_count += route_count
202211
except Exception as ex:
203212
traceback.print_exc()
204213
logger.error("Error creating routes. %s" % (ex))
@@ -216,6 +225,7 @@ async def verify_and_create_routes(namespace: str, request: Request):
216225
try:
217226
kubectl_delete('route', route["name"])
218227
logger.debug("[%s] - Deleted route %s" % (namespace, route["name"]))
228+
deleted_count += 1
219229
except Exception as ex:
220230
traceback.print_exc()
221231
logger.error("Failed deleting route %s" % route["name"])
@@ -226,8 +236,12 @@ async def verify_and_create_routes(namespace: str, request: Request):
226236
traceback.print_exc()
227237
logger.error("Failed deleting route %s" % route["name"])
228238
raise HTTPException(status_code=400, detail=str(sys.exc_info()[0]))
229-
return Response(status_code=200, content='{"message": "synced"}')
230239

240+
return JSONResponse(status_code=200, content={
241+
"message": "synced",
242+
"inserted_count": inserted_count,
243+
"deleted_count": deleted_count
244+
})
231245

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

239-
def in_list (match, list):
253+
def in_list(match, list):
240254
match_ref = build_ref(match)
241255
for item in list:
242256
if build_ref(item) == match_ref:
243257
return True
244258
return False
245259

246260
def build_ref(v):
247-
return "%s%s%s%s" % (v['name'], v['selectTag'], v['host'], v['dataPlane'])
261+
return "%s%s%s%s%s" % (v['name'], v['selectTag'], v['host'], v['dataPlane'], v['sessionCookieEnabled'])
262+
263+
def in_list_by_name(match, list):
264+
for item in list:
265+
if item['name'] == match['name']:
266+
return True
267+
return False

microservices/kubeApi/tests/routers/test_bulk_sync.py

Lines changed: 51 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@ def test_bulk_sync(client):
66
"metadata": {
77
"name": "wild-ns-example",
88
"labels": {
9-
"aps-select-tag": "ns.EXAMPLE-NS"
9+
"aps-select-tag": "ns.EXAMPLE-NS",
10+
"aps-template-version": "v2"
1011
}
1112
},
1213
"spec": {
@@ -28,8 +29,56 @@ def test_bulk_sync(client):
2829
"name": "wild-ns-example",
2930
"selectTag": "ns.EXAMPLE-NS",
3031
"dataPlane": "data-plane-1",
31-
"host": "abc.api.gov.bc.ca"
32+
"host": "abc.api.gov.bc.ca",
33+
"sessionCookieEnabled": False
3234
}]
3335
response = client.post('/namespaces/examplens/routes/sync', json=data)
3436
assert response.status_code == 200
3537
assert response.json()['message'] == 'synced'
38+
assert response.json()['inserted_count'] == 0
39+
assert response.json()['deleted_count'] == 0
40+
41+
def test_bulk_sync_change_host(client):
42+
with mock.patch("routers.routes.get_gwa_ocp_routes") as call:
43+
call.return_value = [{
44+
"metadata": {
45+
"name": "wild-ns-example-xyz",
46+
"labels": {
47+
"aps-select-tag": "ns.EXAMPLE-NS",
48+
"aps-template-version": "v2"
49+
}
50+
},
51+
"spec": {
52+
"host": "xyz.api.gov.bc.ca",
53+
"to": {
54+
"name": "data-plane-1"
55+
}
56+
}
57+
}]
58+
59+
60+
with mock.patch("routers.routes.prepare_apply_routes") as call_apply:
61+
call_apply.return_value = 1
62+
63+
with mock.patch("routers.routes.apply_routes") as call_mismatch_routes:
64+
call_mismatch_routes.return_value = None
65+
66+
with mock.patch("routers.routes.delete_route") as mock_delete_route:
67+
mock_delete_route.return_value = None # Simulate successful deletion
68+
69+
with mock.patch("routers.routes.kubectl_delete") as mock_kubectl_delete:
70+
mock_kubectl_delete.return_value = None # Simulate successful kubectl deletion
71+
72+
data = [{
73+
"name": "wild-ns-example-abc",
74+
"selectTag": "ns.EXAMPLE-NS",
75+
"dataPlane": "data-plane-1",
76+
"host": "abc.api.gov.bc.ca",
77+
"sessionCookieEnabled": False
78+
}]
79+
response = client.post('/namespaces/examplens/routes/sync', json=data)
80+
assert response.status_code == 200
81+
assert response.json()['message'] == 'synced'
82+
assert response.json()['inserted_count'] == 1
83+
assert response.json()['deleted_count'] == 1
84+

microservices/kubeApi/tests/routers/test_bulk_sync_new_route.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,9 +72,12 @@ def test_bulk_sync_new_route(client):
7272
"name": "wild-ns-example",
7373
"selectTag": "ns.EXAMPLE-NS",
7474
"dataPlane": "data-plane-1",
75-
"host": "abc.api.gov.bc.ca"
75+
"host": "abc.api.gov.bc.ca",
76+
"sessionCookieEnabled": False
7677
}]
7778
response = client.post('/namespaces/examplens/routes/sync', json=data)
7879
assert response.status_code == 200
7980
assert response.json()['message'] == 'synced'
81+
assert response.json()['inserted_count'] == 1
82+
8083

microservices/kubeApi/tests/routers/test_bulk_sync_override.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,4 +76,4 @@ def test_bulk_sync_new_route(client):
7676
response = client.post('/namespaces/examplens/routes/sync', json=data)
7777
assert response.status_code == 200
7878
assert response.json()['message'] == 'synced'
79-
79+
assert response.json()['inserted_count'] == 1
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
from unittest import mock
2+
3+
def test_bulk_sync_session_cookie(client):
4+
with mock.patch("routers.routes.get_gwa_ocp_routes") as call:
5+
call.return_value = [{
6+
"metadata": {
7+
"name": "wild-ns-example",
8+
"labels": {
9+
"aps-select-tag": "ns.EXAMPLE-NS",
10+
"aps-template-version": "v1"
11+
}
12+
},
13+
"spec": {
14+
"host": "abc.api.gov.bc.ca",
15+
"to": {
16+
"name": "data-plane-1"
17+
}
18+
}
19+
}]
20+
21+
with mock.patch("routers.routes.prepare_apply_routes") as call_apply:
22+
call_apply.return_value = 0
23+
24+
with mock.patch("routers.routes.apply_routes") as call_mismatch_routes:
25+
call_mismatch_routes.return_value = None
26+
27+
data = [{
28+
"name": "wild-ns-example",
29+
"selectTag": "ns.EXAMPLE-NS",
30+
"dataPlane": "data-plane-1",
31+
"host": "abc.api.gov.bc.ca",
32+
"sessionCookieEnabled": True
33+
}]
34+
response = client.post('/namespaces/examplens/routes/sync', json=data)
35+
assert response.status_code == 200
36+
assert response.json()['message'] == 'synced'
37+
38+
def test_bulk_sync_session_cookie_change(client):
39+
with mock.patch("routers.routes.get_gwa_ocp_routes") as mock_get_routes:
40+
mock_get_routes.return_value = [{
41+
"metadata": {
42+
"name": "wild-ns-example",
43+
"labels": {
44+
"aps-select-tag": "ns.EXAMPLE-NS",
45+
"aps-template-version": "v2"
46+
}
47+
},
48+
"spec": {
49+
"host": "abc.api.gov.bc.ca",
50+
"to": {
51+
"name": "data-plane-1"
52+
}
53+
}
54+
}]
55+
56+
with mock.patch("routers.routes.prepare_apply_routes") as mock_prepare_apply:
57+
mock_prepare_apply.return_value = 1
58+
59+
with mock.patch("routers.routes.apply_routes") as mock_apply_routes:
60+
mock_apply_routes.return_value = None
61+
62+
with mock.patch("routers.routes.delete_route") as mock_delete_route:
63+
mock_delete_route.return_value = None # Simulate successful deletion
64+
65+
with mock.patch("routers.routes.kubectl_delete") as mock_kubectl_delete:
66+
mock_kubectl_delete.return_value = None # Simulate successful kubectl deletion
67+
68+
data = [{
69+
"name": "wild-ns-example",
70+
"selectTag": "ns.EXAMPLE-NS",
71+
"dataPlane": "data-plane-1",
72+
"host": "abc.api.gov.bc.ca",
73+
"sessionCookieEnabled": True
74+
}]
75+
response = client.post('/namespaces/examplens/routes/sync', json=data)
76+
77+
assert response.status_code == 200
78+
assert response.json()['message'] == 'synced'
79+
assert response.json()['inserted_count'] == 1
80+
assert response.json()['deleted_count'] == 0

0 commit comments

Comments
 (0)