This file serves as guidance to those wishing to contribute/extend this repo.
You can use the Tyk deployment deployments/tyk
as a reference implementation.
Deployments are the discrete elements of this repo which enable users to decide what to deploy.
Deployments should be contained within their own directory within the deployments
directory. The deployment directory name should be refer to the purpose of the deployment i.e. the functionality that can be demonstrated by the deployment.
Deployments must contain the following files:
docker-compose.yml
: A Docker Compose file which specifies the services to deploybootstrap.sh
: A bootstrap script which prepares the deployment so that it's ready to usereadme.md
: A readme file which describes how the deployment can be used
Optionally, a Postman collection can be provided, containing deployment-specific requests. This file should be named tyk_demo_<DEPLOYMENT_NAME>.postman_collection.json
e.g. for the mdcb
deployment, the file is called tyk_demo_mdcb.postman_collection.json
.
Deployments can also contain directories, if needed:
volumes
: Contains data for use as volumes referenced by thedocker-compose.yml
filedata
: Contains data for general use e.g. during bootstrappingscripts
: Contains scripts related to bootstrapping or using the deployment
Both the volumes
and data
directories should contain sub-directories named after services, using the service name from the docker-compose.yml
file.
For example, in the tyk
deployment, the Tyk Gateway's (tyk-gateway
) configuration file (tyk.conf
) is mapped as a volume, so has the path volumes/tyk-gateway/tyk.conf
.
Numbered directories within the /data/tyk-dashboard
directory allow the data files to be grouped and processed in a segmented manner. This approach simplifies bootstrapping of separate organisations and recording resulting data.
The bootstrap and export scripts are written to iterate through these directories, processing them in numerical order.
The deployment's Docker Compose file must be named docker-compose.yml
, and contain the services required by the deployment.
This file will be used as a parameter, alongside the base deployment's deployments/tyk/docker-compose.yml
. This means that it only needs to contain services beyond those in the base deployment i.e. you do not need to specify a Dashboard, Gateway pump etc, as these are already in the base deployment.
If the deployment makes use of Docker environment variables which would have adverse effects on the system if set incorrectly, use the up.sh
script to verify the values and correct them as needed.
The scripts/common.sh
provides the set_docker_environment_value
function to set Docker environment variables to desired values. Check the up.sh
script for implementations for the tracing
and instrumentation
deployments.
The bootstrapping process prepares the deployment for use, so that they can immediately be demonstrated.
When creating the bootstrap for a deployment, follow these conventions:
- At the start of the bootstrap script:
- Reference the
scripts/common.sh
script, as this contains useful bootstrap functions e.g.source scripts/common.sh
- Set the
deployment
variable, as this is used in thecommon.sh
bootstrap functions e.g.deployment="My Deployment Name"
- Call the
log_start_deployment
function, to log the start of the deployment
- Reference the
- At the start of each step:
- Call the
log_message
function to log what is happening e.g.log_message "Updating configuration"
- Call the
- During each step:
- Call the
log_message
function to log any useful information, prefixing messages with two spaces to indent them e.g.log_message " Useful indented message"
- Call the
bootstrap_progress
function to provide progress feedback during long-running steps
- Call the
- At the end of each step:
- Call the
bootstrap_progress
function to provide progress feedback - Call either the
log_ok
,log_json_result
orlog_http_result
function, as appropriate, to log the end of the step
- Call the
- At the end of the bootstrap script:
- Call the
log_end_deployment
function, to log the end of the deployment - Echo relevant information about the deployment (see Displaying Information, below)
- Call the
At the end of the bootstrap process, display relevant information about the deployment that the user will find useful. This may be URLs of services, usernames, passwords etc.
Follow these rules when displaying the deployment output:
- Start with the echo command
echo -e "\033[2K
- Put the deployment name prefixed with
▼
e.g.▼ Deployment name
- For each service you want to display:
- In column 3, put the service name, prefixed with
▽
e.g.▽ Service name
- For each piece of information you want to display:
- Display the information as a colon separated label and value, aligned so that the colon is on column 25 e.g.
Useful info : $variable_data
- Display the information as a colon separated label and value, aligned so that the colon is on column 25 e.g.
- If you need to embed additional information you can use the small triangle
▾
and▿
characters
- In column 3, put the service name, prefixed with
- Remember to end the last line with a string terminator e.g.
"
Here is an example:
echo -e "\033[2K
▼ Deployment name
▽ Service name
Useful info : $variable_data
Other info : hardcoded data
▾ Embedded object
With some info : $variable_data_2
▽ Another service
More data : $another_variable"
Following these rules will allow the displayed data to be aligned uniformly with other bootstrap output.
For more examples, check the bootstrap.sh
files in other deployments.
The .context-data
directory is used to store data generated during bootstrap scripts so that other scripts can access and use that data. This is particularly important for dynamic data, such as the ids of data added via the Dashboard API. The scripts/common.sh
script contains functions to read (get_context_data
) and write (set_context_data
) this data.
For example, the base Tyk deployment bootstrap script (deployments/tyk/bootstrap.sh
) writes the Dashboard API credentials to .context-data/dashboard-user-api-credentials
, which can then be read by other scripts. When the SSO deployment is used, its bootstrap script (deployments/sso/bootstrap.sh
) reads the content of the file so that it can access the Dashboard API.
The deployment's readme.md
should contain:
- A description of the deployment
- Example command to deploy the deployment
- Useful information on the usage of the deployment
The up.sh
and down.sh
scripts bring the deployments up and down.
Both scripts accept arguments for the deployments to include. The base Tyk deployment is hard-coded into the scripts, so there is no need to pass tyk
as an argument.
The up script has three main purposes:
- Ensure the Docker environment variables are set correctly based on the deployment arguments
- Run a Docker Compose command referencing the base Tyk deployment (
deployments/tyk/docker-compose.yml
) and any deployments provided as arguments - Run the bootstrap script for the base Tyk deployment (
deployments/tyk/bootstrap.sh
) and any deployments provided as arguments
The down script's only purpose is to run a docker-compose command to bring the deployments down. The docker-compose command include the -v
switch, which removes the volumes, ensuring that no data is persisted.
These utility scripts are available in the scripts
directory:
add-gateway.sh
: Creates a new Tyk Gateway container, using the same configuration as the base Tyk deployment Gatewaycommon.sh
: Contains functions useful for bootstrap scriptsexport.sh
: Uses the Dashboard API to export API and Policy definitions, overwriting data used to bootstrap the base Tyk deploymenttest.sh
: Uses a Newman container to run the Postman collection tests for all deployment that are currently bootstrappedtest-all.sh
: Astest.sh
, but runs for all deploymentsupdate-hosts.sh
: Adds the necessary hosts to the/etc/hosts
file
Note that there is no import script, as the up.sh
script essentially imports the data when bootstrapping the deployment.
There are two scenarios for working with this data:
- You have made changes and want to commit them so that others can get them
- You want to get the changes other people have made
In this scenario you have made updates to the repo which you want to commit back to master.
Before making a pull request, if your changes are to an existing deployment, please check that all tests are working correctly by running the ./scripts/test-all.sh
script. If any tests fail then please resolve.
If you have made changes to APIs or Policies, you can run the export script to update the version controlled data files:
./scripts/export.sh
This will update the apis.json
and policies.json
files in the deployments/tyk/data/tyk-dashboard
path. Other types of data will need to be exported manually, or update the export.sh
script to include your data. Please ensure that any necessary data is automatically added to the deployment when the up.sh
script is run.
When adding functionality to this repo, please also add requests to the Postman collection to demonstrate the functionality. Include a description and enough tests to validate the response. Tests are especially important to avoid regressions, so please add them. Export the collection and overwrite the tyk_demo.postman_collection.json
file in the deployment directory.
The simplest and best-practice approach is to simply bring the environment down, pull the repo then bring it back up again. The up.sh
script includes commands to import APIs, Policies and other data, so all latest data will be imported:
./down.sh
git pull
./up.sh
The Tyk Postman Library provides functions for simplified access to the Tyk Gateway, Dashboard and Dashboard Admin APIs.
The library simplifies Tyk API access by wrapping the Postman HTTP request method (pm.sendRequest
) and automatically setting the necessary host, method, path, headers and body. All the developer must do is provide the necessary parameter values, callback function (if needed) and Postman context:
- The parameters vary depending on the function e.g. the id of an object to retrieve, or the body data to create an object.
- The callback function can be used to perform further operations once the request is completed. The function is passed straight to Postman's
pm.sendRequest
function. - The Postman context is needed, as it is not accessible by the script directly, so must be passed into the function at runtime.
The library is stored in the root of the Postman collection as a pre-request script. To view it, click the Tyk Demo tree root element, then click on Pre-request Script. Storing the script here makes it available to all requests within the collection.
Functions are namespaced, by API and object e.g. tyk.dashboardAdminApi.organisations
. Within the namespaces are the functions which represent the different endpoints for that API and object. For example, the create
function within tyk.dashboardAdminApi.organisations
represents a POST
request to the /admin/organisations
endpoint:
tyk = {
dashboardAdminApi: {
organisations: {
create: function (organisation, callback, pm) {
pm.sendRequest(
{
url: "http://" + pm.variables.get("tyk-dashboard.host") + "/admin/organisations",
method: "POST",
header: "admin-auth: " + pm.variables.get("tyk-dashboard.admin-api-key"),
body: organisation
},
callback
);
}
}
}
}
Postman variables are used to retrieve some data, where it's possible and appropriate to do so, such as hostnames and API keys.
Some requests benefit from or require access to Tyk data. The Tyk Postman Library can be used to perform the necessary interactions with the Tyk API.
The Tyk Demo > General Tests > Dashboard API > Users > Get a User request creates a temporary user in the Pre-request Script so that the request can retrieve it:
tyk.dashboardAdminApi.users.create(
JSON.stringify({
first_name: pm.variables.get("user-first-name"),
last_name: pm.variables.get("user-last-name"),
email_address: pm.variables.get("user-email-address"),
org_id: "5e9d9544a1dcd60001d0ed20",
active: true,
password: "3LEsHO1jv1dt9Xgf",
user_permissions: {
IsAdmin: "admin",
ResetPassword: "admin"
}
}),
(error, response) => {
pm.expect(response.code).to.eql(200);
// user id needed for request, and to delete after tests
pm.variables.set("user-id", response.json().Meta.id);
pm.variables.set("user-api-key", response.json().Meta.access_key);
},
pm
);
Notice that the Postman variables user-id
and user-api-key
are used to temporarily store the data. This is so it can be used later on, in the request and tests.
Any temporary data created should be deleted once it's no longer required. This prevents the Tyk deployment from filling up with temporary data when running requests in the Postman collection. Use the Tyk Postman Library's delete
functions to do this. In this example, the user is deleted at the end of the Tests script:
tyk.dashboardApi.users.delete(
pm.variables.get("user-id"),
(error, response) => {
pm.expect(response.code).to.eq(200);
tyk.dashboardApi.tools.apiKey.delete(pm);
},
pm
);
The Tyk Demo > General Tests > Dashboard Admin API > Organisations > Create an Organisation request uses the Meta
JSON value returned in the response to retreive the organisation and then validate the value of its owner_name
property:
pm.test("Organisation is created", function () {
tyk.dashboardAdminApi.organisations.get(
pm.response.json().Meta,
(error, response) => {
pm.expect(response.code).to.eql(200);
pm.expect(response.json().owner_name).to.eql("Create an Organisation Test");
},
pm
);
});
The Dashboard API requires authentication using a Dashboard User API key. These keys are randomly generated when a user is created, so cannot be defined in advance like the Dashboard Admin API key.
To provide the Dashboard API requests with a key, there are two functions which will generate and delete a key (and the related user). When the key is generated it is automatically stored in the tyk-dashboard.api-key
Postman variable so that it can be used in the requests.
To facilitate the generation of Dashboard API Keys for all Dashboard API requests, the Tyk Demo > General Tests > Dashboard API tree element has a pre-request script which generates a Dashboard API key:
tyk.dashboardApi.tools.apiKey.create(pm);
This key can then be used by all the scripts within the Dashboard API branch, such as Users > Get a User, by referencing the Postman variable {{tyk-dashboard.api-key}}
for the value of the Authorization
header.
Once the tests are finished, the delete
function can be called to remove the key from the database:
tyk.dashboardApi.tools.apiKey.delete(pm);
Deployments are included in test scripts (scripts/test.sh
and scripts/test-all.sh
) if all of the following criteria are met:
- A Postman collection is found in the deployment
- The collection does not contain a variable
test-runner-ignore
with the valuetrue
- The collection contains tests
Some test scenarios may rely on data that is non-deterministic e.g. an API key that's randomly generated during the bootstrap process. To support these scenarios, the test scripts check the deployment directory for a dynamic-test-vars.env
file. If it exists, the values within are added as environment variables when running test containers.
For example, the portal
deployment generates an API key that's required by the tests. To make the key available to the tests, the deployment bootstrap scripts adds the key to the dynamic-test-vars.env
file:
echo "jwt=$portal_admin_api_token" > deployments/portal/dynamic-test-vars.env
Entries added to the env file must be key/value pairs, with the format key=value
. The values must also be on separate lines. For example:
my-key=my-value
hello=world
The Tyk Demo Postman collection contains many requests, each of which demonstrate a particular piece of functionality. Testing the responses generated by these requests provides validation that the desired result was achieved.
There are many ways in which the response can be validated, here are some examples.
The HTTP Status Code returned by the response e.g. 200:
pm.test("Status code is 200", function () {
pm.response.to.have.status(200);
});
The body data returned by the response, depending if it's relevant e.g. "status" is "ok":
pm.test("Status is ok", function () {
var jsonData = pm.response.json();
pm.expect(jsonData.status).to.eql("ok");
});
The header data returned by the response, depending if it's relevant e.g. "New-Header" is present:
pm.test("'New-Header' header is present", function () {
var jsonData = pm.response.json();
pm.expect(jsonData.headers['New-Header']).to.eql("new-header-value");
});
Making additional requests can help validate a feature e.g. rate limiting:
pm.test("Status code is 429", function () {
var rateLimitRequest = {
url: 'http://' + tykGatewayHost + '/basic-protected-api/get',
method: 'GET',
header: 'Authorization:' + keyId
};
pm.sendRequest(rateLimitRequest, function (err, response) {
pm.expect(response.code).to.eql(200);
pm.sendRequest(rateLimitRequest, function (err, response) {
pm.expect(response.code).to.eql(200);
pm.sendRequest(rateLimitRequest, function (err, response) {
pm.expect(response.code).to.eql(429);
});
});
});
});
Postman's dynamic variables produces random values such as names e.g. "{{$randomFirstName}}"
. More information can be found on the Postman dynamic variables documentation.
These can be found throughout the collection, where general random values are helpful. For example, the Tyk Demo > General Tests > Dashboard Admin API > Organisations > Get an Organisation request uses {{$randomCompanyName}}
to generate a random company name:
var organisationName = pm.variables.replaceIn("{{$randomCompanyName}}");
tyk.dashboardAdminApi.organisations.create(
JSON.stringify({
owner_name: organisationName
}),
(error, response) => {
pm.expect(response.code).to.eql(200);
pm.variables.set("organisation-id", response.json().Meta);
pm.variables.set("organisation-name", organisationName);
},
pm
);