Skip to content

Commit 4803361

Browse files
author
ikethecoder
authored
Various changes (#9)
1 parent f883bee commit 4803361

File tree

9 files changed

+255
-32
lines changed

9 files changed

+255
-32
lines changed

README.md

Lines changed: 38 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -38,32 +38,30 @@ All APIs are protected by an OIDC JWT Token with the following claims:
3838

3939
## 1. Register a new namespace
4040

41-
A `namespace` represents a collections of Kong Services and Routes that are managed independently.
41+
A `namespace` represents a collection of Kong Services and Routes that are managed independently.
4242

4343
To create a new namespace, go to the <a href="https://gwa-qwzrwc-test.pathfinder.gov.bc.ca/int" target="_blank">API Services Portal</a>.
4444

45-
After login and selection of an existing namespace, go to the `New Namespace` tab and click the `Create Namespace` button.
45+
After login (and selection of an existing namespace if you have one already assigned), go to the `New Namespace` tab and click the `Create Namespace` button.
4646

47-
The namespace must be an alphanumeric string between 5 and 10 characters.
47+
The namespace must be an alphanumeric string between 5 and 15 characters (RegExp reference: `^[a-z][a-z0-9]{4,14}$`).
4848

4949
Logout by clicking your username at the top right of the page. When you login again, you should be able to select the new namespace from the `API Programme Services` project selector.
5050

5151
## 2. Generate a Service Account
5252

5353
Go to the `Service Accounts` tab and click the `Create Service Account`. A new credential will be created - make a note of the `ID` and `Secret`.
5454

55-
With access:
55+
The credential has the following access:
5656
* `admin:gateway` : Permission to publish gateway configuration to Kong
5757
* `admin:acl` : Permission to update the Access Control List for controlling access to viewing metrics, service configuration and service account management
5858
* `admin:catalog` : Permission to update BC Data Catalog datasets for describing APIs available for consumption
5959

6060
## 3. Prepare configuration
6161

62-
The gateway configuration can be hand-crafted or you can use the `gwa` `new` command to walk you through the creation of the config.
62+
The gateway configuration can be hand-crafted or you can use a command line interface that we developed called `gwa` to convert your Openapi v3 spec to a Kong configuration.
6363

64-
To view a list of available plugins, you can run: `gwa plugins`.
65-
66-
To view examples go [here](/docs/samples/service-plugins).
64+
### Hand-crafted (recommended if you don't have an Openapi spec)
6765

6866
**Simple Example**
6967

@@ -91,9 +89,11 @@ services:
9189
" > sample.yaml
9290
```
9391

92+
To view optional plugin examples go [here](/docs/samples/service-plugins).
93+
9494
> NOTE: If you have separate pipelines for your environments (i.e./ dev, test and prod), you can split your configuration and update the `tags`. So for example, you can use a tag `ns.$NS.dev` to sync Kong configuration for `dev` Service and Routes only.
9595
96-
**gwa CLI Example**
96+
### gwa Command Line
9797

9898
Run: `gwa new` and follow the prompts.
9999

@@ -115,23 +115,27 @@ gwa new -o sample.yaml https://bcgov.github.io/gwa-api/openapi/simple.yaml
115115

116116
The Swagger console for the `gwa-api` can be used to publish Kong Gateway configuration, or the `gwa Command Line` can be used.
117117

118-
### Swagger Console
118+
```
119+
gwa new -o sample.yaml https://bcgov.github.io/gwa-api/openapi/simple.yaml
120+
```
119121

120-
Go to <a href="https://gwa-api-qwzrwc-test.pathfinder.gov.bc.ca/api/doc" target="_blank">gwa-api Swagger Console</a>.
122+
> See below for the `gwa` CLI install instructions.
121123
122-
Select the `PUT` `/namespaces/{namespace}/gateway` API.
124+
> The current beta version of `gwa new` results in Kong configuration that needs to be edited before it is ready to be applied.
123125
124-
The Service Account uses the OAuth2 Client Credentials Grant Flow. Click the `lock` link on the right and enter in the Service Account credentials that were generated in step #2.
126+
> Make the following edits:
127+
> * Add a `hosts` list under each `route` with the external URL of your service on the gateway (i.e./ a value that is: `$NAME.api.gov.bc.ca`)
128+
> * The `service` `url` might need to be edited to equal your upstream URL
129+
> * Optionally: Add a qualifier to the namespace tags if you are separating your configuration into different pipelines
125130
126-
For the `Parameter namespace`, enter the namespace that you created in step #1.
127131

128-
Select `dryRun` to `true`.
132+
## 4. Apply gateway configuration
129133

130-
Select a `configFile` file.
134+
The Swagger console for the `gwa-api` can be used to publish Kong Gateway configuration, or the `gwa Command Line` can be used.
131135

136+
### gwa Command Line (recommended)
132137
Send the request.
133138

134-
### gwa Command Line
135139

136140
**Install**
137141

@@ -173,6 +177,23 @@ If you want to see the expected changes but not actually apply them, you can run
173177
gwa pg --dry-run sample.yaml
174178
```
175179

180+
### Swagger Console
181+
182+
Go to <a href="https://gwa-api-qwzrwc-test.pathfinder.gov.bc.ca/api/doc" target="_blank">gwa-api Swagger Console</a>.
183+
184+
Select the `PUT` `/namespaces/{namespace}/gateway` API.
185+
186+
The Service Account uses the OAuth2 Client Credentials Grant Flow. Click the `lock` link on the right and enter in the Service Account credentials that were generated in step #2.
187+
188+
For the `Parameter namespace`, enter the namespace that you created in step #1.
189+
190+
Select `dryRun` to `true`.
191+
192+
Select a `configFile` file.
193+
194+
Send the request.
195+
196+
176197
## 5. Verify routes
177198

178199
In our test environment, the hosts that you defined in the routes get altered; to see the actual hosts, log into the <a href="https://gwa-qwzrwc-test.pathfinder.gov.bc.ca/int" target="_blank">API Services Portal</a> and view the hosts under `Services`.

docs/samples/service-plugins/service-rate-limit.yaml

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,18 +11,12 @@ services:
1111
hide_client_headers: false
1212
limit_by: consumer
1313
minute: 10
14-
policy: cluster
1514
header_name: null
1615
second: null
1716
hour: null
1817
day: null
1918
month: null
2019
year: null
21-
redis_database: 0
22-
redis_host: null
23-
redis_password: null
24-
redis_port: 6379
25-
redis_timeout: 2000
2620
enabled: true
2721
protocols:
2822
- http
Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,23 @@
1+
from flask import current_app as app
2+
import requests
3+
import urllib.parse
14

25
# Access the Kong Admin API for details about the Kong configuration
36
#
4-
# Use the Route Hosts found in Kong to ensure there are no conflicts
7+
# Use the Route Hosts found in Kong to ensure there are no conflicts
8+
def get_routes ():
9+
return recurse_get_routes ([], "/routes")
10+
11+
def recurse_get_routes (result, url):
12+
log = app.logger
13+
admin_url = app.config['kongAdminUrl']
14+
15+
log.debug("%s%s" % (admin_url, url))
16+
r = requests.get("%s%s" % (admin_url, url))
17+
json = r.json()
18+
data = json['data']
19+
result.extend(data)
20+
21+
if json['next'] is not None:
22+
recurse_get_routes (result, json['next'])
23+
return result
Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
import time
2+
import os
3+
import yaml
4+
from datetime import datetime
5+
from flask import current_app as app
6+
from string import Template
7+
from subprocess import Popen, PIPE, STDOUT
8+
9+
from clients.openshift import kubectl_apply, kubectl_delete
10+
11+
def check_nsp (ns, ocp_ns):
12+
log = app.logger
13+
b = check_exists ("nsp", ns, ocp_ns)
14+
log.debug("[%s] Check? aps-upstream-%s-%s : %s" % (ns, ns, ocp_ns, b))
15+
return b
16+
17+
18+
def check_exists (_type, ns, ocp_ns):
19+
log = app.logger
20+
args = [
21+
"kubectl", "get", _type, "aps-upstream-%s-%s" % (ns, ocp_ns)
22+
]
23+
run = Popen(args, stdout=PIPE, stderr=STDOUT)
24+
out, err = run.communicate()
25+
if run.returncode != 0:
26+
return False
27+
else:
28+
return True
29+
30+
def apply_nsp (ns, ocp_ns, rootPath):
31+
log = app.logger
32+
33+
template = Template("""
34+
kind: NetworkSecurityPolicy
35+
apiVersion: security.devops.gov.bc.ca/v1alpha1
36+
metadata:
37+
name: aps-upstream-${ns}-${ocp_ns}
38+
labels:
39+
aps-generated-by: "gwa-cli"
40+
aps-published-on: "${fmt_time}"
41+
aps-namespace: "${ns}"
42+
aps-published-ts: "${timestamp}"
43+
spec:
44+
description: |
45+
allow namespace to access the internet
46+
source:
47+
- - app.kubernetes.io/instance=kong
48+
destination:
49+
- - $$namespace=${ocp_ns}
50+
""")
51+
52+
ts = int(time.time())
53+
fmt_time = datetime.now().strftime("%Y.%m-%b.%d")
54+
55+
out_filename = "%s/nsp.yaml" % rootPath
56+
57+
with open(out_filename, 'w') as out_file:
58+
index = 1
59+
log.debug("[%s] NSP aps-upstream-%s-%s" % (ns, ns, ocp_ns))
60+
out_file.write(template.substitute(ns=ns, ocp_ns=ocp_ns, timestamp=ts, fmt_time=fmt_time))
61+
out_file.write('\n---\n')
62+
index = index + 1
63+
64+
kubectl_apply (out_filename)
65+
66+
def delete_nsp (ns, ocp_ns):
67+
log = app.logger
68+
69+
name = "aps-upstream-%s-%s" % (ns, ocp_ns)
70+
71+
kubectl_delete ("nsp", name)
72+
73+
def get_ocp_service_namespaces(rootPath):
74+
files_to_ignore = ["deck.yaml", "routes-current.yaml", "routes-deletions.yaml"]
75+
76+
service_ns_list = []
77+
78+
for x in os.walk(rootPath):
79+
for file in x[2]:
80+
if file not in files_to_ignore:
81+
full_path = "%s/%s" % (x[0],file)
82+
83+
stream = open(full_path, 'r')
84+
data = yaml.load(stream, Loader=yaml.SafeLoader)
85+
86+
if 'services' in data:
87+
for service in data['services']:
88+
if 'host' in service and service['host'].endswith('.svc'):
89+
h = service['host']
90+
parts = h.split('.')
91+
ns = parts[len(parts) - 2]
92+
service_ns_list.append(ns)
93+
94+
service_ns_list = list(set(service_ns_list))
95+
return service_ns_list
96+
97+
# def prepare_deletions (ns, ocp_ns, rootPath):
98+
# log = app.logger
99+
100+
# args = [
101+
# "kubectl", "get", "nsp", "-l", "aps-namespace=%s" % select_tag, "-o", "json"
102+
# ]
103+
# run = Popen(args, stdout=PIPE, stderr=PIPE)
104+
# out, err = run.communicate()
105+
# if run.returncode != 0:
106+
# log.error("Failed to get existing routes", out, err)
107+
# raise Exception("Failed to get existing routes")
108+
109+
# current_routes = []
110+
111+
# existing = json.loads(out)
112+
# for route in existing['items']:
113+
# current_routes.append(route['metadata']['name'])
114+
115+
# host_list = get_host_list(rootPath)
116+
117+
# delete_list = []
118+
# for route_name in current_routes:
119+
# match = False
120+
# for host in host_list:
121+
# if route_name == "wild-%s-%s" % (select_tag.replace('.','-'), host):
122+
# match = True
123+
# if match == False:
124+
# delete_list.append(route_name)
125+
126+
# template = Template("""
127+
# apiVersion: route.openshift.io/v1
128+
# kind: Route
129+
# metadata:
130+
# name: ${name}
131+
132+
# """)
133+
134+
# ts = int(time.time())
135+
# fmt_time = datetime.now().strftime("%Y.%m-%b.%d")
136+
137+
# out_filename = "%s/routes-deletions.yaml" % rootPath
138+
139+
# with open(out_filename, 'w') as out_file:
140+
# index = 1
141+
# for route_name in delete_list:
142+
# log.debug("[%s] Route D %03d %s" % (select_tag, index, route_name))
143+
# out_file.write(template.substitute(name=route_name))
144+
# out_file.write('\n---\n')
145+
# index = index + 1
146+
147+
# if len(delete_list) == 0:
148+
# log.debug("[%s] Route D No Deletions Needed" % select_tag)
149+
150+
# return len(delete_list)

microservices/gatewayApi/clients/openshift.py

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,28 @@ def read_and_indent(full_path, indent):
1919
return result
2020

2121
def apply_routes (rootPath):
22+
kubectl_apply ("%s/routes-current.yaml" % rootPath)
23+
24+
def kubectl_apply (fileName):
25+
log = app.logger
26+
args = [
27+
"kubectl", "apply", "-f", fileName
28+
]
29+
run = Popen(args, stdout=PIPE, stderr=STDOUT)
30+
out, err = run.communicate()
31+
if run.returncode != 0:
32+
log.error("Failed to apply", out, err)
33+
raise Exception("Failed to apply routes")
34+
35+
def kubectl_delete (type, name):
2236
log = app.logger
2337
args = [
24-
"kubectl", "apply", "-f", "%s/routes-current.yaml" % rootPath
38+
"kubectl", "delete", type, name
2539
]
2640
run = Popen(args, stdout=PIPE, stderr=STDOUT)
2741
out, err = run.communicate()
2842
if run.returncode != 0:
29-
log.error("Failed to apply routes", out, err)
43+
log.error("Failed to delete", out, err)
3044
raise Exception("Failed to apply routes")
3145

3246
def delete_routes (rootPath):

microservices/gatewayApi/utils/validators.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11

22
import re
33

4-
namespace_validation_rule='^[a-z][a-z0-9]{4,14}$'
4+
namespace_validation_rule='^[a-z][a-z0-9-]{4,14}$'
55

66
host_validation_rule='[a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*'
77

0 commit comments

Comments
 (0)