diff --git a/README.rst b/README.rst index 5c3dcfa..2c27153 100644 --- a/README.rst +++ b/README.rst @@ -9,6 +9,10 @@ put in a config file. It will read environment variables and config files, and it also contains some vendor specific default values so that you don't have to know extra info to use OpenStack +* If you have a config file, you will get the clouds listed in it +* If you have environment variables, you will get a cloud named 'envvars' +* If you have neither, you will get a cloud named 'defaults' with base defaults + Environment Variables --------------------- @@ -16,10 +20,10 @@ 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 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. If you don't like the name "envvars", that's -ok, you can override it by setting `OS_CLOUD_NAME`. +If you have OpenStack environment variables set, os-client-config will produce +a cloud config object named "envvars" containing your values from the +environment. If you don't like the name "envvars", that's ok, you can override +it by setting `OS_CLOUD_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 diff --git a/os_client_config/config.py b/os_client_config/config.py index e926555..45770d9 100644 --- a/os_client_config/config.py +++ b/os_client_config/config.py @@ -91,31 +91,34 @@ class OpenStackConfig(object): self.defaults = dict(defaults._defaults) - # use a config file if it exists where expected + # First, use a config file if it exists where expected self.cloud_config = self._load_config_file() - if not self.cloud_config: - self.cloud_config = dict( - clouds=dict(openstack=dict(self.defaults))) - self.envvar_key = os.environ.pop('OS_CLOUD_NAME', None) - if self.envvar_key: - if self.envvar_key in self.cloud_config['clouds']: - raise exceptions.OpenStackConfigException( - 'clouds.yaml defines a cloud named "{0}", but' - ' OS_CLOUD_NAME is also set to "{0}". Please rename' - ' either your environment based cloud, or one of your' - ' file-based clouds.'.format(self.envvar_key)) - else: - self.envvar_key = 'envvars' + if not self.cloud_config: + self.cloud_config = {'clouds': {}} + if 'clouds' not in self.cloud_config: + self.cloud_config['clouds'] = {} + + # Next, process environment variables and add them to the mix + self.envvar_key = os.environ.pop('OS_CLOUD_NAME', 'envvars') + if self.envvar_key in self.cloud_config['clouds']: + raise exceptions.OpenStackConfigException( + 'clouds.yaml defines a cloud named "{0}", but' + ' OS_CLOUD_NAME is also set to "{0}". Please rename' + ' either your environment based cloud, or one of your' + ' file-based clouds.'.format(self.envvar_key)) envvars = _get_os_environ() if envvars: - if self.envvar_key in self.cloud_config['clouds']: - raise exceptions.OpenStackConfigException( - 'clouds.yaml defines a cloud named {0}, and OS_*' - ' env vars are set') self.cloud_config['clouds'][self.envvar_key] = envvars + # Finally, fall through and make a cloud that starts with defaults + # because we need somewhere to put arguments, and there are neither + # config files or env vars + if not self.cloud_config['clouds']: + self.cloud_config = dict( + clouds=dict(defaults=dict(self.defaults))) + self._cache_max_age = 0 self._cache_path = CACHE_PATH self._cache_class = 'dogpile.cache.null' diff --git a/os_client_config/tests/base.py b/os_client_config/tests/base.py index f6e815d..1169051 100644 --- a/os_client_config/tests/base.py +++ b/os_client_config/tests/base.py @@ -59,6 +59,9 @@ USER_CONF = { }, 'cache': {'max_age': 1}, } +NO_CONF = { + 'cache': {'max_age': 1}, +} def _write_yaml(obj): @@ -80,6 +83,7 @@ class TestCase(base.BaseTestCase): conf['cache']['path'] = tdir.path self.cloud_yaml = _write_yaml(conf) self.vendor_yaml = _write_yaml(VENDOR_CONF) + self.no_yaml = _write_yaml(NO_CONF) # Isolate the test runs from the environment # Do this as two loops because you can't modify the dict in a loop diff --git a/os_client_config/tests/test_config.py b/os_client_config/tests/test_config.py index 274b188..ae573a6 100644 --- a/os_client_config/tests/test_config.py +++ b/os_client_config/tests/test_config.py @@ -12,6 +12,10 @@ # License for the specific language governing permissions and limitations # under the License. +import os + +import fixtures + from os_client_config import cloud_config from os_client_config import config from os_client_config import exceptions @@ -44,6 +48,14 @@ class TestConfig(base.TestCase): self.assertRaises( exceptions.OpenStackConfigException, c.get_one_cloud, 'envvars') + def test_fallthrough(self): + c = config.OpenStackConfig(config_files=[self.no_yaml], + vendor_files=[self.no_yaml]) + for k in os.environ.keys(): + if k.startswith('OS_'): + self.useFixture(fixtures.EnvironmentVariable(k)) + c.get_one_cloud(cloud='defaults') + def test_get_one_cloud_auth_merge(self): c = config.OpenStackConfig(config_files=[self.cloud_yaml]) cc = c.get_one_cloud(cloud='_test_cloud_', auth={'username': 'user'}) diff --git a/os_client_config/tests/test_environ.py b/os_client_config/tests/test_environ.py index 363cff1..e60c73b 100644 --- a/os_client_config/tests/test_environ.py +++ b/os_client_config/tests/test_environ.py @@ -15,6 +15,7 @@ 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 @@ -36,12 +37,18 @@ class TestEnviron(base.TestCase): vendor_files=[self.vendor_yaml]) self.assertIsInstance(c.get_one_cloud(), cloud_config.CloudConfig) - def test_envvar_name_override(self): - self.useFixture( - fixtures.EnvironmentVariable('OS_CLOUD_NAME', 'openstack')) + def test_no_fallthrough(self): c = config.OpenStackConfig(config_files=[self.cloud_yaml], vendor_files=[self.vendor_yaml]) - cc = c.get_one_cloud('openstack') + self.assertRaises( + exceptions.OpenStackConfigException, c.get_one_cloud, 'openstack') + + def test_envvar_name_override(self): + self.useFixture( + fixtures.EnvironmentVariable('OS_CLOUD_NAME', 'override')) + c = config.OpenStackConfig(config_files=[self.cloud_yaml], + vendor_files=[self.vendor_yaml]) + cc = c.get_one_cloud('override') self._assert_cloud_details(cc) def test_environ_exists(self):