Writing a new environment
In this guide, we’ll take the inline environment from the “Writing a scenario” tutorial and move it into its own file. We’ll then extend it by adding a second compose service, and use RTF environment variables to configure the docker compose stack.
Note RTF manages the environment by parameterizing
docker compose upanddocker compose downfrom thecompose_filesconfig. For details on Docker Compose files and multi-file composition, refer to the Docker Compose documentation.
Prerequisites
- Completed the “Writing a scenario” tutorial
- An
rtf-hello-worlddirectory in the state it was at the end of that guide
Your directory should look like this:
configs scripts test-plan.yaml
./configs:
scenario.yaml
./scripts:
check-status-v2.sh check-status.sh
Creating an environment file
Creating a separate environment file works exactly the same way and has the same benefits as creating a separate scenario file outlined in the “Writing a scenario” guide.
Let’s update our test plan to specify the environment in a separate file:
touch configs/environment.yaml
Make sure the environment.yaml contains a copy of the environment config currently in your
test-plan.yaml file:
name: Docker compose environment config
description: A docker compose environment config
compose_files:
- name: docker-compose.yaml
kind: inline
content: |
services:
hello-world:
image: nginx:alpine
ports:
- "8080:80"
Finally, update the test-plan.yaml file to use the new environment.yaml file:
name: Hello World
description: A test plan created as a guide for writing test plans
variables:
scenario_variable: "scenario executed with test plan variable"
scenario:
from:
kind: local
relative_path: configs/scenario.yaml
environment:
from:
kind: local
relative_path: configs/environment.yaml
Let’s verify this has made no material difference to the templated test plan:
rtf template test-plan.yaml --check
The output is similar to this:
name: Hello World
description: A test plan created as a guide for writing test plans
variables:
scenario_variable: scenario env var value from test plan
matrix:
variant_names: null
dimensions: {}
compound: {}
custom_providers: []
scenario:
...
environment:
...
You now have a reusable environment in its own file! See the framework reference docs for the full Environment config structure.
Adding additional compose files
RTF supports multiple compose files via the compose_files key — each file is passed to
docker compose up using the -f flag.
Let’s create a new compose file.
mkdir data
touch data/compose-app.yaml
We are going to create a python web server that returns Hello, World! when called. Add the
following content to the compose-app.yaml file:
services:
app:
image: python:alpine
environment:
HELLO_MESSAGE: Hello, World!
ports:
- 8000:8000
command:
- python
- -c
- |
from http.server import HTTPServer, BaseHTTPRequestHandler
import os
class H(BaseHTTPRequestHandler):
def do_GET(self):
msg = os.environ.get('HELLO_MESSAGE').encode()
self.send_response(200)
self.send_header('Content-Type', 'text/plain')
self.end_headers()
self.wfile.write(msg)
def log_message(self, *a):
pass
HTTPServer(('', 8000), H).serve_forever()
Let’s add this to our compose_files in environment.yaml:
name: Docker compose environment config
description: A docker compose environment config
compose_files:
- name: docker-compose.yaml
kind: inline
content: |
services:
hello-world:
image: nginx:alpine
ports:
- "8080:80"
# --- Add the compose-app.yaml file ---
- name: compose-app.yaml
kind: relative_path
path: ../data/compose-app.yaml
# -------------------------------------
Note The path to the
compose-app.yamlfile is relative to theenvironment.yamlfile its path is defined in.
We have not explained File Providers yet and will cover them in detail in the “Using file providers” guide.
Let’s run the test plan
rtf run test-plan.yaml
Output:
[+] up 3/3
✔ Network docker-compose-environment-config_default Created 0.0s
✔ Container docker-compose-environment-config-app-1 Healthy 0.6s
✔ Container docker-compose-environment-config-hello-world-1 Healthy 0.6s
scenario env var value from test plan
Response: <removed for brevity>
HTTP status: 200
[+] down 3/3
✔ Container docker-compose-environment-config-app-1 Removed 10.1s
✔ Container docker-compose-environment-config-hello-world-1 Removed 0.1s
✔ Network docker-compose-environment-config_default Removed 0.1s
In addition to the docker-compose-environment-config-hello-world-1 container that was running
before, we now can also see the docker-compose-environment-config-app-1 starting up. You now have
an additional service running from a new compose file! Let’s update our test to actually use it!
Using environment variables
We have a running nginx server and our new python web server. We are going to update the nginx
config so that when we call nginx, it forwards our request to our new service. We are going to do
this by updating the docker compose configuration in our environment.yaml file:
name: Docker compose environment config
description: A docker compose environment config
compose_files:
- name: docker-compose.yaml
kind: inline
content: |
services:
hello-world:
image: nginx:alpine
ports:
- "8080:80"
# -------- Add nginx config ---------
configs:
- source: nginx_conf
target: /etc/nginx/conf.d/default.conf
configs:
nginx_conf:
content: |
server {
listen 80;
location / {
proxy_pass http://app:8000;
}
}
# -----------------------------------
- name: compose-app.yaml
kind: relative_path
path: ../data/compose-app.yaml
Let’s run the test plan:
rtf run test-plan.yaml
Output:
[+] up 3/3
✔ Network docker-compose-environment-config_default Created 0.0s
✔ Container docker-compose-environment-config-app-1 Healthy 0.6s
✔ Container docker-compose-environment-config-hello-world-1 Healthy 0.6s
scenario env var value from test plan
Response: Hello, World!
HTTP status: 200
[+] down 3/3
✔ Container docker-compose-environment-config-app-1 Removed 10.1s
✔ Container docker-compose-environment-config-hello-world-1 Removed 0.1s
✔ Network docker-compose-environment-config_default Removed 0.1s
Note Inlining configuration to docker compose like this is an antipattern in RTF, as it requires you to supply a completely new docker compose YAML file if you want to update any part of its configuration. There is a better way to do this, using File Providers. We cover how to do this in the “Using file providers” guide.
Notice that we now also get Response: Hello, World!. Now, everything in our test is connecting as
expected! Let’s verify this by changing the response message using an environment variable.
First, let’s update our environment.yaml to define an environment variable for the docker compose
stack to use. We are not going to set this environment variable using RTF variables, instead we are
just going to hardcode it for ease. The “Writing a scenario” guide contains an example of
setting environment variables using RTF variables. Update environment.yaml:
name: Docker compose environment config
description: A docker compose environment config
# ------ Add an environment variable ------
env_vars:
HELLO_MESSAGE: "Goodbye, World!"
# -----------------------------------------
compose_files:
- name: docker-compose.yaml
kind: inline
content: |
services:
hello-world:
image: nginx:alpine
ports:
- "8080:80"
configs:
- source: nginx_conf
target: /etc/nginx/conf.d/default.conf
configs:
nginx_conf:
content: |
server {
listen 80;
location / {
proxy_pass http://app:8000;
}
}
- name: compose-app.yaml
kind: relative_path
path: ../data/compose-app.yaml
We also need to update compose-app.yaml to use this environment variable, instead of the hardcoded
value:
services:
app:
image: python:alpine
environment:
# ---- Replace hardcoded message with one from the env var ----
HELLO_MESSAGE: ${HELLO_MESSAGE}
# ------------------------------------------------------------
ports:
- 8000:8000
command:
- python
- -c
- |
from http.server import HTTPServer, BaseHTTPRequestHandler
import os
class H(BaseHTTPRequestHandler):
def do_GET(self):
msg = os.environ.get('HELLO_MESSAGE').encode()
self.send_response(200)
self.send_header('Content-Type', 'text/plain')
self.end_headers()
self.wfile.write(msg)
def log_message(self, *a):
pass
HTTPServer(('', 8000), H).serve_forever()
Note This is making use of docker compose variable interpolation.
Now, run the test plan:
rtf run test-plan.yaml
Output:
[+] up 3/3
✔ Network docker-compose-environment-config_default Created 0.0s
✔ Container docker-compose-environment-config-app-1 Healthy 0.6s
✔ Container docker-compose-environment-config-hello-world-1 Healthy 0.6s
scenario env var value from test plan
Response: Goodbye, World!
HTTP status: 200
[+] down 3/3
✔ Container docker-compose-environment-config-app-1 Removed 10.1s
✔ Container docker-compose-environment-config-hello-world-1 Removed 0.1s
✔ Network docker-compose-environment-config_default Removed 0.1s
Our test plan has successfully used an environment variable from RTF to set configuration in our docker compose environment!
Next steps
In this guide, we moved environment config into its own file, added an additional compose service, and used RTF environment variables to configure the docker compose stack. Next, we’ll guide you through how to use file providers.