summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.rst15
-rw-r--r--os_client_config/cloud_config.py2
-rw-r--r--os_client_config/config.py31
-rw-r--r--os_client_config/tests/base.py80
-rw-r--r--os_client_config/tests/test_config.py67
-rw-r--r--os_client_config/tests/test_environ.py67
-rw-r--r--os_client_config/tests/test_os_client_config.py28
7 files changed, 181 insertions, 109 deletions
diff --git a/README.rst b/README.rst
index 4c67acf..18bf81d 100644
--- a/README.rst
+++ b/README.rst
@@ -16,14 +16,13 @@ os-client-config honors all of the normal `OS_*` variables. It does not
provide backwards compatibility to service-specific variables such as
`NOVA_USERNAME`.
-If you have environment variables and no config files, os-client-config
-will produce a cloud config object named "openstack" containing your
-values from the environment.
+If you have OpenStack environment variables seet and no config files,
+os-client-config will produce a cloud config object named "envvars" containing
+your values from the environment.
Service specific settings, like the nova service type, are set with the
default service type as a prefix. For instance, to set a special service_type
-for trove (because you're using Rackspace) set:
-::
+for trove set::
export OS_DATABASE_SERVICE_TYPE=rax:database
@@ -40,7 +39,7 @@ locations:
The first file found wins.
The keys are all of the keys you'd expect from `OS_*` - except lower case
-and without the OS prefix. So, username is set with `username`.
+and without the OS prefix. So, region name is set with `region_name`.
Service specific settings, like the nova service type, are set with the
default service type as a prefix. For instance, to set a special service_type
@@ -119,6 +118,10 @@ behaviors:
* `cache.max_age` and nothing else gets you memory cache.
* Otherwise, `cache.class` and `cache.arguments` are passed in
+`os-client-config` does not actually cache anything itself, but it collects
+and presents the cache information so that your various applications that
+are connecting to OpenStack can share a cache should you desire.
+
::
cache:
diff --git a/os_client_config/cloud_config.py b/os_client_config/cloud_config.py
index 6f501c6..c17bfc2 100644
--- a/os_client_config/cloud_config.py
+++ b/os_client_config/cloud_config.py
@@ -15,7 +15,7 @@
class CloudConfig(object):
def __init__(self, name, region, config):
- self.name = name or 'openstack'
+ self.name = name
self.region = region
self.config = config
diff --git a/os_client_config/config.py b/os_client_config/config.py
index 8494043..70326cf 100644
--- a/os_client_config/config.py
+++ b/os_client_config/config.py
@@ -54,10 +54,12 @@ def get_boolean(value):
def _get_os_environ():
ret = dict(defaults._defaults)
- for (k, v) in os.environ.items():
- if k.startswith('OS_'):
- newkey = k[3:].lower()
- ret[newkey] = v
+ environkeys = [k for k in os.environ.keys() if k.startswith('OS_')]
+ if not environkeys:
+ return None
+ for k in environkeys:
+ newkey = k[3:].lower()
+ ret[newkey] = os.environ[k]
return ret
@@ -80,7 +82,7 @@ class OpenStackConfig(object):
self._config_files = config_files or CONFIG_FILES
self._vendor_files = vendor_files or VENDOR_FILES
- self.defaults = _get_os_environ()
+ self.defaults = dict(defaults._defaults)
# use a config file if it exists where expected
self.cloud_config = self._load_config_file()
@@ -88,6 +90,14 @@ class OpenStackConfig(object):
self.cloud_config = dict(
clouds=dict(openstack=dict(self.defaults)))
+ envvars = _get_os_environ()
+ if envvars:
+ if 'envvars' in self.cloud_config['clouds']:
+ raise exceptions.OpenStackConfigException(
+ 'clouds.yaml defines a cloud named envvars, and OS_'
+ ' env vars are set')
+ self.cloud_config['clouds']['envvars'] = envvars
+
self._cache_max_age = None
self._cache_path = CACHE_PATH
self._cache_class = 'dogpile.cache.null'
@@ -109,6 +119,7 @@ class OpenStackConfig(object):
if os.path.exists(path):
with open(path, 'r') as f:
return yaml.safe_load(f)
+ return dict(clouds=dict())
def _load_vendor_file(self):
for path in self._vendor_files:
@@ -135,7 +146,7 @@ class OpenStackConfig(object):
# No region configured
return ''
- def _get_region(self, cloud):
+ def _get_region(self, cloud=None):
return self._get_regions(cloud).split(',')[0]
def _get_cloud_sections(self):
@@ -152,7 +163,7 @@ class OpenStackConfig(object):
our_cloud = self.cloud_config['clouds'].get(name, dict())
- # Get the defaults (including env vars) first
+ # Get the defaults
cloud.update(self.defaults)
# yes, I know the next line looks silly
@@ -197,7 +208,8 @@ class OpenStackConfig(object):
if key in cloud['auth']:
target = cloud['auth'][key]
del cloud['auth'][key]
- cloud['auth'][target_key] = target
+ if target:
+ cloud['auth'][target_key] = target
return cloud
def _fix_backwards_auth_plugin(self, cloud):
@@ -321,6 +333,9 @@ class OpenStackConfig(object):
:param kwargs: Additional configuration options
"""
+ if cloud is None and 'envvars' in self._get_cloud_sections():
+ cloud = 'envvars'
+
args = self._fix_args(kwargs, argparse=argparse)
if 'region_name' not in args or args['region_name'] is None:
diff --git a/os_client_config/tests/base.py b/os_client_config/tests/base.py
index 1c30cdb..f6e815d 100644
--- a/os_client_config/tests/base.py
+++ b/os_client_config/tests/base.py
@@ -15,9 +15,87 @@
# License for the specific language governing permissions and limitations
# under the License.
+
+import os
+import tempfile
+
+from os_client_config import cloud_config
+
+import extras
+import fixtures
from oslotest import base
+import yaml
-class TestCase(base.BaseTestCase):
+VENDOR_CONF = {
+ 'public-clouds': {
+ '_test_cloud_in_our_cloud': {
+ 'auth': {
+ 'username': 'testotheruser',
+ 'project_name': 'testproject',
+ },
+ },
+ }
+}
+USER_CONF = {
+ 'clouds': {
+ '_test_cloud_': {
+ 'cloud': '_test_cloud_in_our_cloud',
+ 'auth': {
+ 'username': 'testuser',
+ 'password': 'testpass',
+ },
+ 'region_name': 'test-region',
+ },
+ '_test_cloud_no_vendor': {
+ 'cloud': '_test_non_existant_cloud',
+ 'auth': {
+ 'username': 'testuser',
+ 'password': 'testpass',
+ 'project_name': 'testproject',
+ },
+ 'region_name': 'test-region',
+ },
+ },
+ 'cache': {'max_age': 1},
+}
+
+
+def _write_yaml(obj):
+ # Assume NestedTempfile so we don't have to cleanup
+ with tempfile.NamedTemporaryFile(delete=False) as obj_yaml:
+ obj_yaml.write(yaml.safe_dump(obj).encode('utf-8'))
+ return obj_yaml.name
+
+class TestCase(base.BaseTestCase):
"""Test case base class for all unit tests."""
+
+ def setUp(self):
+ super(TestCase, self).setUp()
+
+ self.useFixture(fixtures.NestedTempfile())
+ conf = dict(USER_CONF)
+ tdir = self.useFixture(fixtures.TempDir())
+ conf['cache']['path'] = tdir.path
+ self.cloud_yaml = _write_yaml(conf)
+ self.vendor_yaml = _write_yaml(VENDOR_CONF)
+
+ # Isolate the test runs from the environment
+ # Do this as two loops because you can't modify the dict in a loop
+ # over the dict in 3.4
+ keys_to_isolate = []
+ for env in os.environ.keys():
+ if env.startswith('OS_'):
+ keys_to_isolate.append(env)
+ for env in keys_to_isolate:
+ self.useFixture(fixtures.EnvironmentVariable(env))
+
+ def _assert_cloud_details(self, cc):
+ self.assertIsInstance(cc, cloud_config.CloudConfig)
+ self.assertTrue(extras.safe_hasattr(cc, 'auth'))
+ self.assertIsInstance(cc.auth, dict)
+ self.assertIsNone(cc.cloud)
+ self.assertIn('username', cc.auth)
+ self.assertEqual('testuser', cc.auth['username'])
+ self.assertEqual('testproject', cc.auth['project_name'])
diff --git a/os_client_config/tests/test_config.py b/os_client_config/tests/test_config.py
index 5ef3048..9decf3e 100644
--- a/os_client_config/tests/test_config.py
+++ b/os_client_config/tests/test_config.py
@@ -12,66 +12,12 @@
# License for the specific language governing permissions and limitations
# under the License.
-import tempfile
-
-import extras
-import fixtures
-import testtools
-import yaml
-
from os_client_config import cloud_config
from os_client_config import config
-
-VENDOR_CONF = {
- 'public-clouds': {
- '_test_cloud_in_our_cloud': {
- 'auth': {
- 'username': 'testotheruser',
- 'project_name': 'testproject',
- },
- },
- }
-}
-USER_CONF = {
- 'clouds': {
- '_test_cloud_': {
- 'cloud': '_test_cloud_in_our_cloud',
- 'auth': {
- 'username': 'testuser',
- 'password': 'testpass',
- },
- 'region_name': 'test-region',
- },
- '_test_cloud_no_vendor': {
- 'cloud': '_test_non_existant_cloud',
- 'auth': {
- 'username': 'testuser',
- 'password': 'testpass',
- 'project_name': 'testproject',
- },
- 'region_name': 'test-region',
- },
- },
- 'cache': {'max_age': 1},
-}
+from os_client_config.tests import base
-def _write_yaml(obj):
- # Assume NestedTempfile so we don't have to cleanup
- with tempfile.NamedTemporaryFile(delete=False) as obj_yaml:
- obj_yaml.write(yaml.safe_dump(obj).encode('utf-8'))
- return obj_yaml.name
-
-
-class TestConfig(testtools.TestCase):
- def setUp(self):
- super(TestConfig, self).setUp()
- self.useFixture(fixtures.NestedTempfile())
- conf = dict(USER_CONF)
- tdir = self.useFixture(fixtures.TempDir())
- conf['cache']['path'] = tdir.path
- self.cloud_yaml = _write_yaml(conf)
- self.vendor_yaml = _write_yaml(VENDOR_CONF)
+class TestConfig(base.TestCase):
def test_get_one_cloud(self):
c = config.OpenStackConfig(config_files=[self.cloud_yaml],
@@ -90,12 +36,3 @@ class TestConfig(testtools.TestCase):
self._assert_cloud_details(cc)
cc = c.get_one_cloud('_test_cloud_no_vendor')
self._assert_cloud_details(cc)
-
- def _assert_cloud_details(self, cc):
- self.assertIsInstance(cc, cloud_config.CloudConfig)
- self.assertTrue(extras.safe_hasattr(cc, 'auth'))
- self.assertIsInstance(cc.auth, dict)
- self.assertIsNone(cc.cloud)
- self.assertIn('username', cc.auth)
- self.assertEqual('testuser', cc.auth['username'])
- self.assertEqual('testproject', cc.auth['project_name'])
diff --git a/os_client_config/tests/test_environ.py b/os_client_config/tests/test_environ.py
new file mode 100644
index 0000000..473c417
--- /dev/null
+++ b/os_client_config/tests/test_environ.py
@@ -0,0 +1,67 @@
+# Copyright (c) 2014 Hewlett-Packard Development Company, L.P.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+
+from os_client_config import cloud_config
+from os_client_config import config
+from os_client_config import exceptions
+from os_client_config.tests import base
+
+import fixtures
+
+
+class TestConfig(base.TestCase):
+
+ def test_get_one_cloud(self):
+ c = config.OpenStackConfig(config_files=[self.cloud_yaml],
+ vendor_files=[self.vendor_yaml])
+ self.assertIsInstance(c.get_one_cloud(), cloud_config.CloudConfig)
+
+ def test_no_environ(self):
+ c = config.OpenStackConfig(config_files=[self.cloud_yaml],
+ vendor_files=[self.vendor_yaml])
+ self.assertRaises(
+ exceptions.OpenStackConfigException, c.get_one_cloud, 'envvars')
+
+ def test_environ_exists(self):
+ self.useFixture(
+ fixtures.EnvironmentVariable('OS_AUTH_URL', 'https://example.com'))
+ self.useFixture(
+ fixtures.EnvironmentVariable('OS_USERNAME', 'testuser'))
+ self.useFixture(
+ fixtures.EnvironmentVariable('OS_PROJECT_NAME', 'testproject'))
+ c = config.OpenStackConfig(config_files=[self.cloud_yaml],
+ vendor_files=[self.vendor_yaml])
+ cc = c.get_one_cloud('envvars')
+ self._assert_cloud_details(cc)
+ self.assertNotIn('auth_url', cc.config)
+ self.assertIn('auth_url', cc.config['auth'])
+ self.assertNotIn('auth_url', cc.config)
+ cc = c.get_one_cloud('_test_cloud_')
+ self._assert_cloud_details(cc)
+ cc = c.get_one_cloud('_test_cloud_no_vendor')
+ self._assert_cloud_details(cc)
+
+ def test_get_one_cloud_with_config_files(self):
+ c = config.OpenStackConfig(config_files=[self.cloud_yaml],
+ vendor_files=[self.vendor_yaml])
+ self.assertIsInstance(c.cloud_config, dict)
+ self.assertIn('cache', c.cloud_config)
+ self.assertIsInstance(c.cloud_config['cache'], dict)
+ self.assertIn('max_age', c.cloud_config['cache'])
+ self.assertIn('path', c.cloud_config['cache'])
+ cc = c.get_one_cloud('_test_cloud_')
+ self._assert_cloud_details(cc)
+ cc = c.get_one_cloud('_test_cloud_no_vendor')
+ self._assert_cloud_details(cc)
diff --git a/os_client_config/tests/test_os_client_config.py b/os_client_config/tests/test_os_client_config.py
deleted file mode 100644
index 7421b6f..0000000
--- a/os_client_config/tests/test_os_client_config.py
+++ /dev/null
@@ -1,28 +0,0 @@
-# -*- coding: utf-8 -*-
-
-# Licensed under the Apache License, Version 2.0 (the "License"); you may
-# not use this file except in compliance with the License. You may obtain
-# a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-"""
-test_os_client_config
-----------------------------------
-
-Tests for `os_client_config` module.
-"""
-
-from os_client_config.tests import base
-
-
-class TestOs_client_config(base.TestCase):
-
- def test_something(self):
- pass