Skip to content

Commit

Permalink
Merge pull request #128 from bcgov/dev
Browse files Browse the repository at this point in the history
Feature/dataclass annotation (#127)
  • Loading branch information
rustyjux authored Nov 7, 2024
2 parents db633c3 + 67a55ba commit 89985a9
Show file tree
Hide file tree
Showing 17 changed files with 596 additions and 112 deletions.
6 changes: 4 additions & 2 deletions microservices/gatewayApi/clients/ocp_routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -263,10 +263,12 @@ def get_route_overrides(root_path, override_tag):
def eval_services(host_list, override_tag, data):
if data is not None and 'services' in data:
for service in data['services']:
service_has_tag = 'tags' in service and override_tag in service['tags']

if 'routes' in service:
for route in service['routes']:
if 'hosts' in route:
if override_tag in route['tags']:
if service_has_tag or ('tags' in route and override_tag in route['tags']):
if 'hosts' in route:
for host in route['hosts']:
if host not in host_list:
host_list.append(host)
63 changes: 61 additions & 2 deletions microservices/gatewayApi/tests/clients/test_ocp_routes.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from clients.ocp_routes import eval_services

def test_eval_services():
def test_eval_services_route_tags():

data = {
"services": [
Expand Down Expand Up @@ -35,4 +35,63 @@ def test_eval_services():
host_list = []
eval_services(host_list, 'override-tag', data)
assert len(host_list) == 1
assert host_list[0] == 'host1'
assert host_list[0] == 'host1'

def test_eval_services_service_tags():

data = {
"services": [
{},
{
"routes": [
]
},
{
"routes": [
{
"hosts": [],
"tags": []
}
]
},
{
"routes": [
{
"hosts": [
"host1"
],
"tags": [
"tag1",
"override-tag"
]
}
]
},
{
"routes": [
{
"hosts": [
"host2"
],
"tags": []
},
{
"hosts": [
"host3"
],
"tags": []
}
],
"tags": [
"tag2",
"override-tag"
]
}
]
}
host_list = []
eval_services(host_list, 'override-tag', data)
assert len(host_list) == 3
assert host_list[0] == 'host1'
assert host_list[1] == 'host2'
assert host_list[2] == 'host3'
33 changes: 31 additions & 2 deletions microservices/gatewayApi/tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,8 @@ def json():
}
return Response
elif (path == 'http://kong/certificates?tags=gwa.ns.mytest' or
path == 'http://kong/certificates?tags=gwa.ns.sescookie'):
path == 'http://kong/certificates?tags=gwa.ns.sescookie' or
path == 'http://kong/certificates?tags=gwa.ns.dclass'):
class Response:
def json():
return {
Expand Down Expand Up @@ -150,11 +151,33 @@ class Response:
'hosts': ['myapi.api.gov.bc.ca'],
'ns_attributes': {'perm-domains': ['.api.gov.bc.ca', '.cluster.local']},
'overrides': {
'aps.route.session.cookie.enabled': ['myapi.api.gov.bc.ca']
'aps.route.session.cookie.enabled': ['myapi.api.gov.bc.ca'],
"aps.route.dataclass.low": [],
"aps.route.dataclass.medium": [],
"aps.route.dataclass.high": [],
"aps.route.dataclass.public": []
},
'select_tag': 'ns.sescookie.dev'
}

assert json.dumps(kwargs['json'], sort_keys=True) == json.dumps(matched, sort_keys=True)
return Response
elif (url == 'http://kube-api/namespaces/dclass/routes'):
class Response:
status_code = 201
matched = {
'hosts': ['myapi.api.gov.bc.ca'],
'ns_attributes': {'perm-domains': ['.api.gov.bc.ca', '.cluster.local']},
'overrides': {
'aps.route.session.cookie.enabled': [],
"aps.route.dataclass.low": [],
"aps.route.dataclass.medium": [],
"aps.route.dataclass.high": ['myapi.api.gov.bc.ca'],
"aps.route.dataclass.public": []
},
'select_tag': 'ns.dclass.dev'
}

assert json.dumps(kwargs['json'], sort_keys=True) == json.dumps(matched, sort_keys=True)
return Response
elif (url == 'http://kube-api/namespaces/ns1/routes'):
Expand All @@ -179,6 +202,12 @@ class Response:
def json():
return {}
return Response
elif (url == 'http://kube-api/namespaces/dclass/local_tls'):
class Response:
status_code = 200
def json():
return {}
return Response
else:
raise Exception(url)

Expand Down
22 changes: 22 additions & 0 deletions microservices/gatewayApi/tests/routes/v2/test_gateway.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,28 @@ def test_happy_with_session_cookie_gateway_call(client):
assert response.status_code == 200
assert json.dumps(response.json) == '{"message": "Sync successful.", "results": "Deck reported no changes"}'

def test_happy_with_data_class_gateway_call(client):
configFile = '''
services:
- name: my-service
host: myupstream.local
tags: ["ns.dclass.dev", "aps.route.dataclass.high"]
routes:
- name: route-1
hosts: [ myapi.api.gov.bc.ca ]
tags: ["ns.dclass.dev"]
plugins:
- name: acl-auth
tags: ["ns.dclass.dev"]
'''

data={
"configFile": configFile,
"dryRun": False
}
response = client.put('/v2/namespaces/dclass/gateway', json=data)
assert response.status_code == 200
assert json.dumps(response.json) == '{"message": "Sync successful.", "results": "Deck reported no changes"}'

def test_success_mtls_reference(client):
configFile = '''
Expand Down
7 changes: 6 additions & 1 deletion microservices/gatewayApi/v2/routes/gateway.py
Original file line number Diff line number Diff line change
Expand Up @@ -339,7 +339,11 @@ def write_config(namespace: str) -> object:
"select_tag": selectTag,
"ns_attributes": ns_attributes.getAttrs(),
"overrides": {
"aps.route.session.cookie.enabled": get_route_overrides(tempFolder, "aps.route.session.cookie.enabled")
"aps.route.session.cookie.enabled": get_route_overrides(tempFolder, "aps.route.session.cookie.enabled"),
"aps.route.dataclass.low": get_route_overrides(tempFolder, "aps.route.dataclass.low"),
"aps.route.dataclass.medium": get_route_overrides(tempFolder, "aps.route.dataclass.medium"),
"aps.route.dataclass.high": get_route_overrides(tempFolder, "aps.route.dataclass.high"),
"aps.route.dataclass.public": get_route_overrides(tempFolder, "aps.route.dataclass.public"),
}
}
log.debug("[%s] - Initiating request to kube API %s" % (dp, route_payload))
Expand Down Expand Up @@ -622,6 +626,7 @@ def traverse_tags_transform(yaml, namespace, required_tag):
object_count = object_count + traverse_tags_transform(item, namespace, required_tag)
return object_count


def traverse_has_ns_qualifier(yaml, required_tag):
log = app.logger
traversables = ['services', 'routes', 'plugins', 'upstreams', 'consumers', 'certificates', 'ca_certificates']
Expand Down
8 changes: 8 additions & 0 deletions microservices/gatewayJobScheduler/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,21 @@ def transform_data_by_ns(data):

# 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'):
# extract override values
session_cookie_enabled = False
if 'aps.route.session.cookie.enabled' in route_obj['tags']:
session_cookie_enabled = True
data_class = None
for tag in route_obj['tags']:
if tag.startswith('aps.route.dataclass'):
data_class = tag.split(".")[-1]
break

for host in route_obj['hosts']:
name = 'wild-%s-%s' % (select_tag.replace(".", "-"), host)
ns_dict[namespace].append({"name": name, "selectTag": select_tag, "host": host,
"sessionCookieEnabled": session_cookie_enabled,
"dataClass": data_class,
"dataPlane": os.getenv('DATA_PLANE')})
return ns_dict
except Exception as err:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ def test_happy_transform_data_by_ns():
]
}
]
assert json.dumps(transform_data_by_ns(data)) == '{"ns1": [{"name": "wild-ns-ns1-host-1", "selectTag": "ns.ns1", "host": "host-1", "sessionCookieEnabled": false, "dataPlane": "test-dp"}]}'
assert json.dumps(transform_data_by_ns(data)) == '{"ns1": [{"name": "wild-ns-ns1-host-1", "selectTag": "ns.ns1", "host": "host-1", "sessionCookieEnabled": false, "dataClass": null, "dataPlane": "test-dp"}]}'

def test_happy_transform_data_by_ns_with_override():
def test_happy_transform_data_by_ns_with_override_session_cookie():
with mock.patch('clients.namespace.admin_api') as mock_admin_api:
set_mock_admin_api_response(mock_admin_api)

Expand All @@ -30,8 +30,22 @@ def test_happy_transform_data_by_ns_with_override():
]
}
]
assert json.dumps(transform_data_by_ns(data)) == '{"ns1": [{"name": "wild-ns-ns1-host-1", "selectTag": "ns.ns1", "host": "host-1", "sessionCookieEnabled": true, "dataPlane": "test-dp"}]}'
assert json.dumps(transform_data_by_ns(data)) == '{"ns1": [{"name": "wild-ns-ns1-host-1", "selectTag": "ns.ns1", "host": "host-1", "sessionCookieEnabled": true, "dataClass": null, "dataPlane": "test-dp"}]}'

def test_happy_transform_data_by_ns_with_override_data_plane():
with mock.patch('clients.namespace.admin_api') as mock_admin_api:
set_mock_admin_api_response(mock_admin_api)

data = [
{
"name": "route-1",
"tags": [ "ns.ns1", "aps.route.dataclass.high"],
"hosts": [
"host-1"
]
}
]
assert json.dumps(transform_data_by_ns(data)) == '{"ns1": [{"name": "wild-ns-ns1-host-1", "selectTag": "ns.ns1", "host": "host-1", "sessionCookieEnabled": false, "dataClass": "high", "dataPlane": "test-dp"}]}'

def set_mock_admin_api_response(dt):
class mock_admin_api:
Expand Down
24 changes: 20 additions & 4 deletions microservices/kubeApi/clients/ocp_routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,13 @@
}
}

data_class_mapping = {
'aps.route.dataclass.low': 'low',
'aps.route.dataclass.medium': 'medium',
'aps.route.dataclass.high': 'high',
'aps.route.dataclass.public': 'public'
}

def read_and_indent(full_path, indent):
pad = " "
stream = open(full_path, 'r')
Expand Down Expand Up @@ -150,12 +157,21 @@ def prepare_apply_routes(ns, select_tag, hosts, root_path, data_plane, ns_templa
with open(out_filename, 'w') as out_file:
index = 1
for host in hosts:
data_class_annotation = ''
templ_version = ns_template_version
if overrides and 'aps.route.session.cookie.enabled' in overrides and host in overrides['aps.route.session.cookie.enabled']:
templ_version = 'v1'
if overrides:
if 'aps.route.session.cookie.enabled' in overrides and host in overrides['aps.route.session.cookie.enabled']:
templ_version = 'v1'
logger.debug("[%s] %s Template version override applied %s", select_tag, host, templ_version)

for tag, value in data_class_mapping.items():
if overrides.get(tag) and host in overrides[tag]:
data_class = value
data_class_annotation = f' aviinfrasetting.ako.vmware.com/name: "dataclass-{value}"'
logger.debug("[%s] %s Dataclass override applied %s -> %s", select_tag, host, tag, data_class)
else:
logger.debug("[%s] %s No override applied %s", select_tag, hosts, str(overrides))

route_template = ROUTES[templ_version]["ROUTE"]

# If host transformation is disabled, then select the appropriate
Expand All @@ -179,7 +195,7 @@ def prepare_apply_routes(ns, select_tag, hosts, root_path, data_plane, ns_templa
(select_tag, index, select_tag.replace('.', '-'), host, resource_version))
out_file.write(route_template.substitute(name=name, ns=ns, select_tag=select_tag, resource_version=resource_version, host=host, path='/',
ssl_ref=ssl_ref, ssl_key=ssl_key, ssl_crt=ssl_crt, service_name=data_plane, timestamp=ts, fmt_time=fmt_time, data_plane=data_plane,
template_version=templ_version))
data_class_annotation=data_class_annotation, template_version=templ_version))
out_file.write('\n---\n')
index = index + 1
out_file.close()
Expand Down
10 changes: 8 additions & 2 deletions microservices/kubeApi/routers/routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ class BulkSyncRequest(BaseModel):
host: str
# Indicator of whether session cookies should be enabled by the Kube-API
sessionCookieEnabled: bool
# Data class for Emerald Cluster routes
dataClass: str


@router.put("/namespaces/{namespace}/routes", status_code=201, dependencies=[Depends(verify_credentials)])
Expand Down Expand Up @@ -170,7 +172,8 @@ async def verify_and_create_routes(namespace: str, request: Request):
"selectTag": route["metadata"]["labels"]["aps-select-tag"],
"host": route["spec"]["host"],
"dataPlane": route["spec"]["to"]["name"],
"sessionCookieEnabled": True if route["metadata"]["labels"].get("aps-template-version") == "v1" else False
"sessionCookieEnabled": True if route["metadata"]["labels"].get("aps-template-version") == "v1" else False,
"dataClass": route["metadata"]["annotations"].get("aviinfrasetting.ako.vmware.com/name").split("-")[-1] if route["metadata"]["annotations"].get("aviinfrasetting.ako.vmware.com/name") else None
}
)

Expand Down Expand Up @@ -199,6 +202,9 @@ async def verify_and_create_routes(namespace: str, request: Request):
overrides = {}
if 'sessionCookieEnabled' in route and route['sessionCookieEnabled']:
overrides['aps.route.session.cookie.enabled'] = [route['host']]

if 'dataClass' in route and route['dataClass']:
overrides[f'aps.route.dataclass.{route["dataClass"]}'] = [route['host']]

route_count = prepare_apply_routes(namespace, route['selectTag'], [
route['host']], source_folder, route["dataPlane"], ns_template_version, overrides)
Expand Down Expand Up @@ -258,7 +264,7 @@ def in_list(match, list):
return False

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

def in_list_by_name(match, list):
for item in list:
Expand Down
1 change: 1 addition & 0 deletions microservices/kubeApi/templates/v1/routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
resourceVersion: "${resource_version}"
annotations:
haproxy.router.openshift.io/timeout: 30m
${data_class_annotation}
labels:
aps-generated-by: "gwa-cli"
aps-published-on: "${fmt_time}"
Expand Down
1 change: 1 addition & 0 deletions microservices/kubeApi/templates/v2/routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
haproxy.router.openshift.io/balance: random
haproxy.router.openshift.io/disable_cookies: 'true'
haproxy.router.openshift.io/timeout: 30m
${data_class_annotation}
labels:
aps-generated-by: "gwa-cli"
aps-published-on: "${fmt_time}"
Expand Down
3 changes: 2 additions & 1 deletion microservices/kubeApi/tests/routers/test_add_route.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
haproxy.router.openshift.io/balance: random
haproxy.router.openshift.io/disable_cookies: 'true'
haproxy.router.openshift.io/timeout: 30m
labels:
aps-generated-by: "gwa-cli"
aps-published-on: "2024.05-May.08"
Expand Down Expand Up @@ -50,7 +51,7 @@ def mock_apply_routes (rootPath):
with open("%s/routes-current.yaml" % rootPath) as f:
assert routes_current_yaml == f.read()

def test_add_route_override(client):
def test_add_route(client):
with mock.patch('clients.ocp_routes.time_secs') as dt:
dt.return_value = 1715153983

Expand Down
Loading

0 comments on commit 89985a9

Please sign in to comment.