Repository: opsdroid/opsdroid

Issue:
Allow usage of env vars in config
The configuration should be parsed for environment variables when loaded. This would allow for secrets like api keys to be kept outside of the opsdroid configuration.

#### Example

```yaml
connectors:
  - name: slack
    default-room: '#general'
    bot-name: "opsdroid"
    icon-emoji: ":robot:"
    api-token: "$SLACK_API_KEY"
```
In this example `$SLACK_API_KEY` would be replaced with the contents of the environment variable of the same name.

Discussion:
coveralls: 
[![Coverage Status](https://coveralls.io/builds/13259107/badge)](https://coveralls.io/builds/13259107)

Coverage decreased (-0.4%) to 95.253% when pulling **5231408d02b3e69aa963d1702af1a18cfdbfbe24 on jacobtomlinson:env-var-config** into **b67d32310da842dddddb9b907f731a03d0562a2f on opsdroid:master**.

coveralls: 
[![Coverage Status](https://coveralls.io/builds/13259374/badge)](https://coveralls.io/builds/13259374)

Coverage decreased (-0.4%) to 95.253% when pulling **f1c3e2f87b7f8c3cb565f8d4054662747b861f47 on jacobtomlinson:env-var-config** into **b67d32310da842dddddb9b907f731a03d0562a2f on opsdroid:master**.

coveralls: 
[![Coverage Status](https://coveralls.io/builds/13259976/badge)](https://coveralls.io/builds/13259976)

Coverage increased (+0.05%) to 95.712% when pulling **4d1c90850d0a42784b4c11c25a97cd7792510ec2 on jacobtomlinson:env-var-config** into **b67d32310da842dddddb9b907f731a03d0562a2f on opsdroid:master**.


Patch:
diff --git a/docs/configuration-reference.md b/docs/configuration-reference.md
index 2370bf9..cefea9c 100644
--- a/docs/configuration-reference.md
+++ b/docs/configuration-reference.md
@@ -176,3 +176,15 @@ databases:
     repo: https://github.com/username/mymongofork.git
     no-cache: true
 ```
+
+## Environment variables
+
+You can use environment variables in your config. You need to specify the variable in the place of a value.
+
+```yaml
+skills:
+  - name: myawesomeskill
+    somekey: $ENVIRONMENT_VARIABLE
+```
+
+_Note: Your environment variable names must consist of uppercase characters and underscores only. The value must also be just the environment variable, you cannot currently mix env vars inside strings._
\ No newline at end of file
diff --git a/opsdroid/loader.py b/opsdroid/loader.py
index 055b499..73ec3c5 100644
--- a/opsdroid/loader.py
+++ b/opsdroid/loader.py
@@ -6,6 +6,7 @@ import sys
 import shutil
 import subprocess
 import importlib
+import re
 import yaml
 from opsdroid.const import (
     DEFAULT_GIT_URL, MODULES_DIRECTORY, DEFAULT_MODULES_PATH,
@@ -135,6 +136,17 @@ class Loader:
             _LOGGER.info("No configuration files found.")
             config_path = self.create_default_config(DEFAULT_CONFIG_PATH)
 
+        env_var_pattern = re.compile(r'^\$([A-Z_]*)$')
+        yaml.add_implicit_resolver("!envvar", env_var_pattern)
+
+        def envvar_constructor(loader, node):
+            """Yaml parser for env vars."""
+            value = loader.construct_scalar(node)
+            [env_var] = env_var_pattern.match(value).groups()
+            return os.environ[env_var]
+
+        yaml.add_constructor('!envvar', envvar_constructor)
+
         try:
             with open(config_path, 'r') as stream:
                 _LOGGER.info("Loaded config from %s", config_path)


Test patch:
diff --git a/tests/configs/minimal_with_envs.yaml b/tests/configs/minimal_with_envs.yaml
new file mode 100644
index 0000000..ce97849
--- /dev/null
+++ b/tests/configs/minimal_with_envs.yaml
@@ -0,0 +1,1 @@
+test: $ENVVAR
\ No newline at end of file
diff --git a/tests/test_loader.py b/tests/test_loader.py
index ae78a18..fcb2fbc 100644
--- a/tests/test_loader.py
+++ b/tests/test_loader.py
@@ -29,6 +29,13 @@ class TestLoader(unittest.TestCase):
         config = loader.load_config_file(["tests/configs/minimal.yaml"])
         self.assertIsNotNone(config)
 
+    def test_load_config_file_with_env_vars(self):
+        opsdroid, loader = self.setup()
+        os.environ["ENVVAR"] = 'test'
+        config = loader.load_config_file(
+            ["tests/configs/minimal_with_envs.yaml"])
+        self.assertEqual(config["test"], os.environ["ENVVAR"])
+
     def test_create_default_config(self):
         test_config_path = "/tmp/test_config_path/configuration.yaml"
         opsdroid, loader = self.setup()