Merge "Add support for secure.yaml file for auth info"

This commit is contained in:
Jenkins 2015-12-05 02:09:05 +00:00 committed by Gerrit Code Review
commit caede51461
5 changed files with 95 additions and 13 deletions

View File

@ -145,6 +145,34 @@ as a result of a chosen plugin need to go into the auth dict. For password
auth, this includes `auth_url`, `username` and `password` as well as anything
related to domains, projects and trusts.
Splitting Secrets
-----------------
In some scenarios, such as configuragtion managment controlled environments,
it might be eaiser to have secrets in one file and non-secrets in another.
This is fully supported via an optional file `secure.yaml` which follows all
the same location rules as `clouds.yaml`. It can contain anything you put
in `clouds.yaml` and will take precedence over anything in the `clouds.yaml`
file.
::
# clouds.yaml
clouds:
internap:
profile: internap
auth:
username: api-55f9a00fb2619
project_name: inap-17037
regions:
- ams01
- nyj01
# secure.yaml
clouds:
internap:
auth:
password: XXXXXXXXXXXXXXXXX
SSL Settings
------------

View File

@ -48,6 +48,11 @@ CONFIG_FILES = [
for d in CONFIG_SEARCH_PATH
for s in YAML_SUFFIXES + JSON_SUFFIXES
]
SECURE_FILES = [
os.path.join(d, 'secure' + s)
for d in CONFIG_SEARCH_PATH
for s in YAML_SUFFIXES + JSON_SUFFIXES
]
VENDOR_FILES = [
os.path.join(d, 'clouds-public' + s)
for d in CONFIG_SEARCH_PATH
@ -99,6 +104,20 @@ def _get_os_environ(envvar_prefix=None):
return ret
def _merge_clouds(old_dict, new_dict):
"""Like dict.update, except handling nested dicts."""
ret = old_dict.copy()
for (k, v) in new_dict.items():
if isinstance(v, dict):
if k in ret:
ret[k] = _merge_clouds(ret[k], v)
else:
ret[k] = v.copy()
else:
ret[k] = v
return ret
def _auth_update(old_dict, new_dict):
"""Like dict.update, except handling the nested dict called auth."""
for (k, v) in new_dict.items():
@ -116,20 +135,29 @@ class OpenStackConfig(object):
def __init__(self, config_files=None, vendor_files=None,
override_defaults=None, force_ipv4=None,
envvar_prefix=None):
envvar_prefix=None, secure_files=None):
self._config_files = config_files or CONFIG_FILES
self._secure_files = secure_files or SECURE_FILES
self._vendor_files = vendor_files or VENDOR_FILES
config_file_override = os.environ.pop('OS_CLIENT_CONFIG_FILE', None)
if config_file_override:
self._config_files.insert(0, config_file_override)
secure_file_override = os.environ.pop('OS_CLIENT_SECURE_FILE', None)
if secure_file_override:
self._secure_files.insert(0, secure_file_override)
self.defaults = defaults.get_defaults()
if override_defaults:
self.defaults.update(override_defaults)
# First, use a config file if it exists where expected
self.config_filename, self.cloud_config = self._load_config_file()
_, secure_config = self._load_secure_file()
if secure_config:
self.cloud_config = _merge_clouds(
self.cloud_config, secure_config)
if not self.cloud_config:
self.cloud_config = {'clouds': {}}
@ -217,6 +245,9 @@ class OpenStackConfig(object):
def _load_config_file(self):
return self._load_yaml_json_file(self._config_files)
def _load_secure_file(self):
return self._load_yaml_json_file(self._secure_files)
def _load_vendor_file(self):
return self._load_yaml_json_file(self._vendor_files)

View File

@ -64,7 +64,6 @@ USER_CONF = {
'auth': {
'auth_url': 'http://example.com/v2',
'username': 'testuser',
'password': 'testpass',
'project_name': 'testproject',
},
'region-name': 'test-region',
@ -112,6 +111,15 @@ USER_CONF = {
}
},
}
SECURE_CONF = {
'clouds': {
'_test_cloud_no_vendor': {
'auth': {
'password': 'testpass',
},
}
}
}
NO_CONF = {
'cache': {'max_age': 1},
}
@ -135,6 +143,7 @@ class TestCase(base.BaseTestCase):
tdir = self.useFixture(fixtures.TempDir())
conf['cache']['path'] = tdir.path
self.cloud_yaml = _write_yaml(conf)
self.secure_yaml = _write_yaml(SECURE_CONF)
self.vendor_yaml = _write_yaml(VENDOR_CONF)
self.no_yaml = _write_yaml(NO_CONF)
@ -155,6 +164,7 @@ class TestCase(base.BaseTestCase):
self.assertIsNone(cc.cloud)
self.assertIn('username', cc.auth)
self.assertEqual('testuser', cc.auth['username'])
self.assertEqual('testpass', cc.auth['password'])
self.assertFalse(cc.config['image_api_use_tasks'])
self.assertTrue('project_name' in cc.auth or 'project_id' in cc.auth)
if 'project_name' in cc.auth:

View File

@ -30,7 +30,8 @@ class TestConfig(base.TestCase):
def test_get_all_clouds(self):
c = config.OpenStackConfig(config_files=[self.cloud_yaml],
vendor_files=[self.vendor_yaml])
vendor_files=[self.vendor_yaml],
secure_files=[self.no_yaml])
clouds = c.get_all_clouds()
# We add one by hand because the regions cloud is going to exist
# twice since it has two regions in it
@ -74,7 +75,8 @@ class TestConfig(base.TestCase):
def test_get_one_cloud_with_config_files(self):
c = config.OpenStackConfig(config_files=[self.cloud_yaml],
vendor_files=[self.vendor_yaml])
vendor_files=[self.vendor_yaml],
secure_files=[self.secure_yaml])
self.assertIsInstance(c.cloud_config, dict)
self.assertIn('cache', c.cloud_config)
self.assertIsInstance(c.cloud_config['cache'], dict)
@ -129,7 +131,8 @@ class TestConfig(base.TestCase):
def test_fallthrough(self):
c = config.OpenStackConfig(config_files=[self.no_yaml],
vendor_files=[self.no_yaml])
vendor_files=[self.no_yaml],
secure_files=[self.no_yaml])
for k in os.environ.keys():
if k.startswith('OS_'):
self.useFixture(fixtures.EnvironmentVariable(k))
@ -137,7 +140,8 @@ class TestConfig(base.TestCase):
def test_prefer_ipv6_true(self):
c = config.OpenStackConfig(config_files=[self.no_yaml],
vendor_files=[self.no_yaml])
vendor_files=[self.no_yaml],
secure_files=[self.no_yaml])
cc = c.get_one_cloud(cloud='defaults', validate=False)
self.assertTrue(cc.prefer_ipv6)
@ -155,7 +159,8 @@ class TestConfig(base.TestCase):
def test_force_ipv4_false(self):
c = config.OpenStackConfig(config_files=[self.no_yaml],
vendor_files=[self.no_yaml])
vendor_files=[self.no_yaml],
secure_files=[self.no_yaml])
cc = c.get_one_cloud(cloud='defaults', validate=False)
self.assertFalse(cc.force_ipv4)
@ -166,7 +171,8 @@ class TestConfig(base.TestCase):
self.assertEqual('testpass', cc.auth['password'])
def test_get_cloud_names(self):
c = config.OpenStackConfig(config_files=[self.cloud_yaml])
c = config.OpenStackConfig(config_files=[self.cloud_yaml],
secure_files=[self.no_yaml])
self.assertEqual(
['_test-cloud-domain-id_',
'_test-cloud-int-project_',
@ -177,7 +183,8 @@ class TestConfig(base.TestCase):
],
sorted(c.get_cloud_names()))
c = config.OpenStackConfig(config_files=[self.no_yaml],
vendor_files=[self.no_yaml])
vendor_files=[self.no_yaml],
secure_files=[self.no_yaml])
for k in os.environ.keys():
if k.startswith('OS_'):
self.useFixture(fixtures.EnvironmentVariable(k))

View File

@ -29,6 +29,8 @@ class TestEnviron(base.TestCase):
fixtures.EnvironmentVariable('OS_AUTH_URL', 'https://example.com'))
self.useFixture(
fixtures.EnvironmentVariable('OS_USERNAME', 'testuser'))
self.useFixture(
fixtures.EnvironmentVariable('OS_PASSWORD', 'testpass'))
self.useFixture(
fixtures.EnvironmentVariable('OS_PROJECT_NAME', 'testproject'))
self.useFixture(
@ -57,13 +59,15 @@ class TestEnviron(base.TestCase):
self.useFixture(
fixtures.EnvironmentVariable('OS_PREFER_IPV6', 'false'))
c = config.OpenStackConfig(config_files=[self.cloud_yaml],
vendor_files=[self.vendor_yaml])
vendor_files=[self.vendor_yaml],
secure_files=[self.secure_yaml])
cc = c.get_one_cloud('_test-cloud_')
self.assertFalse(cc.prefer_ipv6)
def test_environ_exists(self):
c = config.OpenStackConfig(config_files=[self.cloud_yaml],
vendor_files=[self.vendor_yaml])
vendor_files=[self.vendor_yaml],
secure_files=[self.secure_yaml])
cc = c.get_one_cloud('envvars')
self._assert_cloud_details(cc)
self.assertNotIn('auth_url', cc.config)
@ -78,7 +82,8 @@ class TestEnviron(base.TestCase):
def test_environ_prefix(self):
c = config.OpenStackConfig(config_files=[self.cloud_yaml],
vendor_files=[self.vendor_yaml],
envvar_prefix='NOVA_')
envvar_prefix='NOVA_',
secure_files=[self.secure_yaml])
cc = c.get_one_cloud('envvars')
self._assert_cloud_details(cc)
self.assertNotIn('auth_url', cc.config)
@ -92,7 +97,8 @@ class TestEnviron(base.TestCase):
def test_get_one_cloud_with_config_files(self):
c = config.OpenStackConfig(config_files=[self.cloud_yaml],
vendor_files=[self.vendor_yaml])
vendor_files=[self.vendor_yaml],
secure_files=[self.secure_yaml])
self.assertIsInstance(c.cloud_config, dict)
self.assertIn('cache', c.cloud_config)
self.assertIsInstance(c.cloud_config['cache'], dict)