Introduction
Our users often ask us how to manage configuration data that changes between environments. For example: You might use a different database for development and testing than you do for your live system. The good news is that this is simple to handle with Iguana.
There are two basic scenarios:
- Iguana Channel Settings: These are a special case so you must use Iguana Local Environment Variables.
Most text fields in Channel settings can be configured using Iguana Environment Variables.
- Configuration Settings in code: For these you can use variables reserved for static data and load the initial values from Iguana Environment Variables or from External Files.
We cover the pros and cons of both methods, and allow you to choose the method you prefer. Basically if you are storing using a large number of external variables you may prefer external files.
If you have any questions please contact us at support@interfaceware.com.
Note: Configuration data that does not change between environments can be stored as readonly constant variables within iguana code modules. If the data is local to a channel then it will go in a local module, if it is shared between several channels then it will go in a shared module.
Channel Settings [top]
You can use Iguana Environment Variables to set most string value Channel Property Settings, for example: The Source Directory in a From File component. By using Environment Variables you can conveniently edit all your channel settings in one place — Settings > Environment Variables. See How to use Environment Variables in Channel Settings for more details of how to use Environment Variables to set Channel Property Settings.
For example if you created two channels that both use a From File component, and both need to store the Source Directory (different directories for each channel).
Then the setup will look something like this:
- Two channels, for example:
- Foxton Hospital
- Foxton Laboratory
- Two Environment Variables, for example:
The channel names are added to beginning of each variable name to ensure that the variable names are unique. If we used a single variable FILE_INPUT_DIRECTORY then setting the variable for one channel would overwrite the directory name for other channel. Including the channel as part of the variable prevents accidental “name collisions” and ensures the correct data is stored for each channel.
- Foxton_Hospital.FILE_INPUT_DIRECTORY
- Foxton_Laboratory.FILE_INPUT_DIRECTORY
- Channel settings for two channels:
- Foxton Hospital:
- Foxton Laboratory:
- Foxton Hospital:
Note: You could also manually edit the channel settings directly, but this would be very laborious if you are running more than a very few channels.
Tip: The Program Setting field for the Web Server > Server Name will also accept an Environment Variable — but other program setting fields do not.
Using Iguana Environment Variables in Lua [top]
In this section we will walk you through two typical scenarios using configuration data, and explain the reasons for how we implement it.
Loading Iguana Environment Variables into your Lua code is actually very simple — just use os.getenv('<environment_variable_name>')
to read the value from an Environment Variable. We recommend loading the values into an array of (psuedo) STATIC variables — we also recommend using upper case for the fields in the table to indicate that they should be treated as a STATIC values.
We will use these three examples:
- A channel with changing configuration:
This is a channel, that uses different database names, database hosts, database ports and file paths as it moves from development to test to production.
- A shared module with changing configuration:
This is very similar to the first option, except that the module is used by several channels. Each channel that uses this module uses the same database. The database changes as the module moves from development to test to production. An example of this would be a module that looks up patient demographic information for several channels for the same hospital.
- A shared database query module:
This is a module, which is used by several channels. Each channel that uses this module uses its own database, and sometimes its own database host.
Example One: A channel with changing configuration.
- Create a channel or use an existing channel with a Translator component:
In this example we used a channel called Foxton Hospital with a From Translator component. But you can use any channel and place the code in a From Translator, a Filter, or a To Translator component.
- Create the Environment Variables:
If multiple channels use the same Environment Variables in their Lua code then we get “name collisions” — and the data will be incorrect for one or more of the channels. To fix this we need to ensure the names are unique. The easiest way to do this is to add the channel name to the beginning of the Variable name. Using the channel names does get quite long but it guarantees uniqueness as channel names must be unique (within an Iguana Server instance).
- We added these four environment variables:
- Database name: Foxton_Hospital.DATABASE_SERVER
- Database name: Foxton_Hospital.DATABASE_NAME
- Database host: Foxton_Hospital.DATABASE_HOST
- Database port: Foxton_Hospital.DATABASE_PORT
- File path: Foxton_Hospital.FILE_INPUT_DIRECTORY
- This is how they look in Settings > Environment Variables:
Notice how we have a second channel Foxton Laboratory that uses the “same” variables with different values. You can see how adding the channel name to the beginning of the Variable name has prevented the accidental reuse of variables between the two channels.
- We added these four environment variables:
- Create a local code module to load the Environment Variables:
In this case we created a local config module — but you could use a different module name like: localConfig or configuration if you prefer. We loaded the Environment Variables into a Lua table config and named the fields after the environment variables. We also kept the config table local and returned it from the module rather than making it global. This differentiates it from any other global (psuedo) STATIC variables — and most importantly it greatly reduces the chance of anyone accidentally updating the configuration data variables in the code.
- The module looks like this:
And this is the code which you can modify for you own use:local Config = {} -- Load "STATIC" configuration variables into table Config['FILE_INPUT_DIRECTORY'] = os.getenv('Foxton_Hospital.FILE_INPUT_DIRECTORY') Config['DATABASE_NAME'] = os.getenv('Foxton_Hospital.DATABASE_NAME') Config['DATABASE_HOST'] = os.getenv('Foxton_Hospital.DATABASE_HOST') Config['DATABASE_PORT'] = os.getenv('Foxton_Hospital.DATABASE_PORT') Config['DATABASE_SERVER'] = os.getenv('Foxton_Hospital.DATABASE_SERVER') -- add more configuration variables here... return Config
- The code for using the config module looks like this:
This code is shown in the main module for demonstration purposes, but would usually be used in a local module.
And this is the code that you can modify for your own use:local cfg = require 'config' function main() trace(cfg) -- you can use the configuration variables like this: local DBNAME = cfg.DATABASE_SERVER..'@'.. cfg.DATABASE_HOST..':'.. cfg.DATABASE_PORT trace(DBNAME) local conn = db.connect{ api=db.MY_SQL, name= DBNAME, user='root', password='secret', use_unicode = true, live = true } conn:execute{sql='SELECT * FROM '..cfg.DATABASE_NAME..'.actor', live=true} end
- The module looks like this:
Example Two: A shared module with changing configuration.
- Create a shared module or use an existing module:
In this example we used a module called foxton_lookup. But you can modify an existing module and add similar code.
- Create the Environment Variables:
If multiple modules use the same Environment Variables then we get “name collisions” — and the data will be incorrect for one or more of the modules. To fix this we need to ensure the names are unique. The easiest way to do this is to add the module name to the beginning of the Variable name. Using the module names does get quite long but it guarantees uniqueness as module names must be unique (within an Iguana Server instance).
- We added these four environment variables:
- Database name: foxton_lookup.DATABASE_SERVER
- Database name: foxton_lookup.DATABASE_NAME
- Database host: foxton_lookup.DATABASE_HOST
- Database port: foxton_lookup.DATABASE_PORT
- This is how they look in Settings > Environment Variables:
Notice how the Foxton Laboratory channel uses the “same” variables with different values. You can see how adding the module name to the beginning of the Variable name has prevented the accidental reuse of variables between the channel and the module.
- We added these four environment variables:
- Create a shared module to load the Environment Variables:
In this case we created a shared foxton_lookup module — but you could use a different module name or an existing module if you prefer. We loaded the Environment Variables into a local Lua table Config and named the fields after the environment variables (making the Config table local prevents other code from accidentally accessing/updating it). Adding the module name (as we did with channels) to the variable differentiates it from any other global (psuedo) STATIC variables — and reduces the chance of anyone accidentally updating the configuration data variables in the code.
- The module looks like this:
The
test()
function is purely for demonstration/test purposes and should be deleted from live code.
And this is the code which you can modify for you own use:local Config = {} -- Load "STATIC" configuration variables into table Config['DATABASE_NAME'] = os.getenv('foxton_lookup.DATABASE_NAME') Config['DATABASE_HOST'] = os.getenv('foxton_lookup.DATABASE_HOST') Config['DATABASE_PORT'] = os.getenv('foxton_lookup.DATABASE_PORT') Config['DATABASE_SERVER'] = os.getenv('foxton_lookup.DATABASE_SERVER') -- add more configuration variables here... -- lookup module functions to go here -- DELETE THIS FUNCTION! function test() -- test function to demonstrate that the -- environment variables are actually loaded return Config end
- The module looks like this:
- We used the
test()
function inmain()
to demonstrate that the Environment Variables loaded correctly :The test() function is purely for demonstration/test purposes and should be deleted from live code.
And this is the test code:require 'foxton_lookup' function main(Data) test() end
Example Three: A shared database query module.
A shared database module is used by several channels and/or modules. In this case the database data is passed to functions in the module — the shared module does not store any configuration data.
Any (changing) configuration data will be stored in the calling channel (example 1) or in the calling module (example 2), and passed to the shared module.
The code to call such a module from main()
will look something like this: