Get Started - Explore Workspaces - Create a Shared Service Across Workspaces
Introduction
Kong Enterprise ships with a novel Workspaces implementation, allowing Kong Admins to segment Admin API configuration & traffic domains. Workspaces are useful in a wide range of scenarios; for instance, in multi-team setups, where multiple teams share the same Kong cluster.
In previous Kong Enterprise versions, these teams would be able to access each other’s Admin API entities, which might be undesirable for many reasons; in Kong Enterprise 0.33 it’s possible for teams to have their own private, segmented spaces, while still sharing the same Kong cluster.
Workspaces
Kong Enterprise includes Workspaces (via the Kong Manager or Admin API), where you can organize your Kong Enterprise implementation for scale across teams. Key features include:
- Simplify team management by creating default roles for each workspace, and custom roles as needed
- Easily create new users, assign them to workspaces, and even brand with colors/images if needed
- Organize your services, plugins, consumer management, and more according to your exact specification
- Use Enhanced RBAC to restrict your teams’ access to see only see what they need to, and only what they should.
- Appoint workspace-specific admins to maintain governance while avoiding becoming bottle-necked by a single administrator.
Learning Lab
With workspaces, you can organize your Kong Enterprise implementation for scale across teams. In this learning lab, you will:
- Create 3 workspaces with 1 shared service
- Create 3 different routes types for the shared service
- Verify access to each route to the shared service
High Level Tasks
- Step 0: Review: Setup the environment
- Step 1: List workspaces and its entities
- Step 2: Create 3 different workspaces
- Step 3: Create 1 shared service and add it to these workspaces
- Step 4: Add a routes for shared service to each workspace
- Step 5: Verify the service is shared between workspaces
The Topics and Tasks are as follows.
Step 0: Review: Setup the environment
Let’s setup the environment that will load the following
- Creates a Docker Network called kong-net
- Installs and starts the database
- Prepares your database (Kong migration)
- Installs and starts Kong with environment variables and configurations
Launch Setup Script
Run this script in the terminal to setup your environment (~30 seconds).
launch.sh
Step 1: List workspaces and its entities
List workspaces and its entities
http get :8001/workspaces
Response
HTTP/1.1 200 OK
{
"data": [
{
"config": {
"portal": true
},
"created_at": 1546437794000,
"id": "6d9e2ec6-4a99-4ba6-9fe0-a8b0c9f0b617",
"meta": {},
"name": "**default**"
}
],
"total": 1
}
List entities in the default workspace
http GET :8001/workspaces/default/entities
Response
HTTP/1.1 200 OK
{
"data": [
{
"entity_id": "b8c5a2a3-7308-49cf-a481-f3f716f74501",
"entity_type": "rbac_roles",
"unique_field_name": "name",
"unique_field_value": "super-admin",
"workspace_id": "6d9e2ec6-4a99-4ba6-9fe0-a8b0c9f0b617",
"workspace_name": "default"
},
{
"entity_id": "b8c5a2a3-7308-49cf-a481-f3f716f74501",
"entity_type": "rbac_roles",
"unique_field_name": "id",
"unique_field_value": "b8c5a2a3-7308-49cf-a481-f3f716f74501",
"workspace_id": "6d9e2ec6-4a99-4ba6-9fe0-a8b0c9f0b617",
"workspace_name": "default"
},
{
"entity_id": "354f3cd0-3302-4007-94b2-aca1ce4dd721",
"entity_type": "rbac_roles",
"unique_field_name": "name",
"unique_field_value": "admin",
"workspace_id": "6d9e2ec6-4a99-4ba6-9fe0-a8b0c9f0b617",
"workspace_name": "default"
}
Step 2: Create 3 different workspaces
http POST :8001/workspaces \
name=teamA
http POST :8001/workspaces \
name=teamB
http POST :8001/workspaces \
name=teamC
List workspaces.
How many workspaces are there? Why is there an extra one?
http get :8001/workspaces
Response
HTTP/1.1 200 OK
...
{
"data": [
{
"config": {
"portal": false
},
"created_at": 1547034980000,
"id": "a2f991c2-98c8-43b3-b626-1a6268925f08",
"meta": {},
"name": "**teamC**"
},
{
"config": {
"portal": false
},
"created_at": 1547034977000,
"id": "34b5fb87-5f9b-470f-a591-b0b25e8a5c32",
"meta": {},
"name": "**teamB**"
},
{
"config": {
"portal": true
},
"created_at": 1547034938000,
"id": "27ead2dc-31b7-48dd-9ad8-5249bef6642f",
"meta": {},
"name": "**default**"
},
{
"config": {
"portal": false
},
"created_at": 1547034972000,
"id": "e22ac4e2-e023-4efb-be7e-68beda0c7c5a",
"meta": {},
"name": "**teamA**"
}
],
"total": 4
}
Step 3: Create 1 shared service and add it to these workspaces
Add a shared entity to each of the workspaces
Having teamA, teamB, and teamC workspaces set up, add some entities to each of them.
Let’s add a shared service- represented by the Service Kong entity and then add different routes associated with this upstream service.
Create the shared service
Add a service to Kong named shared-service that will proxy request going to the URL httpbin.org/ Run this command to add the service:
http post :8001/services \
url=http://httpbin.org/ \
name=shared-service
Response
HTTP/1.1 201 Created
{
"connect_timeout": 60000,
"created_at": 1546439401,
"host": "httpbin.org",
"id": "365b8798-96a6-46de-96b8-0c38a70526b9",
"name": "shared-service",
"path": "/",
"port": 80,
"protocol": "http",
"read_timeout": 60000,
"retries": 5,
"updated_at": 1546439401,
"write_timeout": 60000
}
Verify entity you just created
Notice the endpoint /services does not include a workspace prefix—which is how one specifies the workspace under which a given API call applies to. In such cases, the call applies to the default workspace. Confirm that by listing all entities under the default workspace:
http get :8001/workspaces/default/entities
Response
The response should look similar to this. Entities not relevant for this example are redacted. Notice that our shared-service is on the list.
**HTTP/1.1 200 OK**
...
{
"entity_id": "ffa49a62-d471-426e-b24a-648641b38d64",
"entity_type": "basicauth_credentials",
"unique_field_name": "id",
"unique_field_value": "ffa49a62-d471-426e-b24a-648641b38d64",
"workspace_id": "27ead2dc-31b7-48dd-9ad8-5249bef6642f",
"workspace_name": "default"
},
{
"entity_id": "4a9b8e08-1bae-4aab-ba03-441662f5733a",
"entity_type": "services",
"unique_field_name": "name",
"unique_field_value": "**shared-service**",
"workspace_id": "27ead2dc-31b7-48dd-9ad8-5249bef6642f",
"workspace_name": "**default**"
},
{
"entity_id": "4a9b8e08-1bae-4aab-ba03-441662f5733a",
"entity_type": "services",
"unique_field_name": "id",
"unique_field_value": "4a9b8e08-1bae-4aab-ba03-441662f5733a",
"workspace_id": "27ead2dc-31b7-48dd-9ad8-5249bef6642f",
"workspace_name": "default"
}
],
"total": 113
Set Variable
Set variable for the $shared_service for later use
shared_service variable
shared_service=$(http get :8001/services/shared-service | python -c "import sys, json; print json.load(sys.stdin)['id']")
Note: If you do not set this variable, run the following command to get the id string for the shared-service. Replace any $share_service variable below with this id string
http get :8001/services/shared-service
Add shared service to workspace teams
The next step is to add the shared service to the team workspaces. This can be done as follows.
http post :8001/workspaces/teamA/entities \
entities=$shared_service
Response
**HTTP/1.1 201 Created**
...
[
{
"connect_timeout": 60000,
"created_at": 1547035900,
"host": "httpbin.org",
"id": "4a9b8e08-1bae-4aab-ba03-441662f5733a",
"name": "shared-service",
"path": "/",
"port": 80,
"protocol": "http",
"read_timeout": 60000,
"retries": 5,
"updated_at": 1547035900,
"write_timeout": 60000
}
]
http post :8001/workspaces/teamB/entities \
entities=$shared_service
http post :8001/workspaces/teamC/entities \
entities=$shared_service
Verify shared service is added to for each workspace
Run the following command to verify the service for teamA.
Repeat this command for teamB and teamC
http :8001/teamA/services
Response
The response should look similar to this. Notice the id. This will be the same for teamB and teamC.
**HTTP/1.1 200 OK**
...
{
"data": [
{
"connect_timeout": 60000,
"created_at": 1547035900,
"host": "httpbin.org",
"id": "**4a9b8e08-1bae-4aab-ba03-441662f5733a**",
"name": "shared-service",
"path": "/",
"port": 80,
"protocol": "http",
"read_timeout": 60000,
"retries": 5,
"updated_at": 1547035900,
"write_timeout": 60000
}
],
"next": null
}
Step 4: Add a routes for the shared service
Add route to shared service
The next step is to set up each team’s routes to the shared service. For example, teamA, teamB, and teamC have routes /headers, /ip, and /user-agent, respectively.
Note: the service.id is set to the variable you created previously. If this variable was not set make sure you use the service.id which is a string that looks like this 4a9b8e08-1bae-4aab-ba03-441662f5733a
Team A
http POST :8001/teamA/routes \
paths[]=/headers \
service.id=$shared_service \
strip_path=false -f
Response
HTTP/1.1 201 Created
...
{
"created_at": 1547037399,
"hosts": null,
"id": "14af8345-31ff-4a5e-90a0-f3df4026a440",
"methods": null,
"paths": [
"**/headers**"
],
"preserve_host": false,
"protocols": [
"http",
"https"
],
"regex_priority": 0,
"service": {
"id": "4a9b8e08-1bae-4aab-ba03-441662f5733a"
},
"strip_path": false,
"updated_at": 1547037399
}
Team B
http POST :8001/teamB/routes \
paths[]=/ip \
service.id=$shared_service \
strip_path=false -f
Response
HTTP/1.1 201 Created
...
{
"created_at": 1547037557,
"hosts": null,
"id": "0dca33f5-91b7-4f28-85c9-2c877d5404dd",
"methods": null,
"paths": [
"**/ip**"
],
"preserve_host": false,
"protocols": [
"http",
"https"
],
"regex_priority": 0,
"service": {
"id": "4a9b8e08-1bae-4aab-ba03-441662f5733a"
},
"strip_path": false,
"updated_at": 1547037557
}
Team C
http POST :8001/teamC/routes \
paths[]=/user-agent \
service.id=$shared_service \
strip_path=false -f
Response
**HTTP/1.1 201 Created**
...
{
"created_at": 1547037636,
"hosts": null,
"id": "d58cf4e7-9f88-4a31-b526-1fc206cac616",
"methods": null,
"paths": [
"**/user-agent****"**
],
"preserve_host": false,
"protocols": [
"http",
"https"
],
"regex_priority": 0,
"service": {
"id": "4a9b8e08-1bae-4aab-ba03-441662f5733a"
},
"strip_path": false,
"updated_at": 1547037636
}
Step 5: Verify the service is shared between workspaces
Now you have all teams with their routes sharing the same service.
To make sure it is set up correctly, list the routes in each workspace.
Team A
Verify teamA has a /headers route pointing to the shared service.
http :8001/teamA/routes
Response
HTTP/1.1 200 OK
...
{
"data": [
{
"created_at": 1547037399,
"hosts": null,
"id": "14af8345-31ff-4a5e-90a0-f3df4026a440",
"methods": null,
"paths": [
"/headers"
],
"preserve_host": false,
"protocols": [
"http",
"https"
],
"regex_priority": 0,
"service": {
"id": "4a9b8e08-1bae-4aab-ba03-441662f5733a"
},
"strip_path": false,
"updated_at": 1547037399
}
],
"next": null
}
Team B
Verify teamB has a /ip route pointing to the shared service.
http :8001/teamB/routes
Response
HTTP/1.1 200 OK
...
{
"data": [
{
"created_at": 1547037399,
"hosts": null,
"id": "14af8345-31ff-4a5e-90a0-f3df4026a440",
"methods": null,
"paths": [
"/ip"
],
"preserve_host": false,
"protocols": [
"http",
"https"
],
"regex_priority": 0,
"service": {
"id": "4a9b8e08-1bae-4aab-ba03-441662f5733a"
},
"strip_path": false,
"updated_at": 1547037399
}
],
"next": null
}
Team C
Verify teamC has a /user-agent route pointing to the shared service.
http :8001/teamC/routes
Response
HTTP/1.1 200 OK
...
{
"data": [
{
"created_at": 1547037399,
"hosts": null,
"id": "14af8345-31ff-4a5e-90a0-f3df4026a440",
"methods": null,
"paths": [
"/user-agent"
],
"preserve_host": false,
"protocols": [
"http",
"https"
],
"regex_priority": 0,
"service": {
"id": "4a9b8e08-1bae-4aab-ba03-441662f5733a"
},
"strip_path": false,
"updated_at": 1547037399
}
],
"next": null
}
GREAT!
With this setup, Teams A, B, and C only have access to their own Routes entities through the Admin API.
In subsequent learning labs, you’ll configure RBAC for additional control such as granular read/write/delete/update rights assigned to workspaces. This allows for flexible intra and inter-team permission schemes.
What Next? Learning Challenge
Create an entity with the same name in different workspaces
Given the following High Level Tasks, try to complete the learning challenge.
Introduction
Entities in different workspaces can have the same name! Different teams—belonging to different workspaces—are allowed to give any name to their entities. To provide an example of that, say that Teams A, B, and C want a particular consumer named guest—a different consumer for each team, sharing the same username.
High Level Tasks
- Create consumer with username=guest for each of the workspaces (provide different password for each user guest)
- Add this user to basic-authentication plugin
- Verify guest user has access to its respective workspace.
With this, Teams A, Team B, and Team C will have the freedom to operate their guest consumer independently, choosing authentication plugins or doing any other operation that is allowed in the non-workspaced Kong world.