Allow custom UC/standalone passwords
Fix the passwords generation precedence for containerized UC and standalone installations. This allows users to provide custom passwords/secrets in tripleo-undercloud-passwords.yaml, or undercloud-passwords.conf, when upgrading from instack. Partial-bug: #1784576 Change-Id: If7166c2419794dd9595d25ad29ee1fdafce99d72 Signed-off-by: Bogdan Dobrelya <bdobreli@redhat.com>
This commit is contained in:
parent
9ec31e0e5f
commit
16ccbb1360
|
@ -0,0 +1,10 @@
|
|||
---
|
||||
upgrade:
|
||||
- |
|
||||
Instack undercloud legacy passwords should be customized by its original
|
||||
``undercloud-passwords.conf`` location, when upgrading from instack UC.
|
||||
Those passwords will be automatically transitioned to
|
||||
``tripleo-undercloud-passwords.yaml`` during upgrade. Changes made to the
|
||||
legacy location become ignored after that. Use
|
||||
``tripleo-undercloud-passwords.yaml`` to manually update UC passwords
|
||||
further on.
|
|
@ -131,9 +131,53 @@ class TestDeployUndercloud(TestPluginV1):
|
|||
@mock.patch('subprocess.check_call', autospec=True)
|
||||
@mock.patch('tripleo_common.utils.passwords.generate_passwords')
|
||||
@mock.patch('yaml.safe_dump')
|
||||
def test_update_passwords_env_update(self, mock_dump, mock_pw, mock_cc,
|
||||
mock_exists, mock_chmod, mock_user):
|
||||
pw_dict = {"GeneratedPassword": 123}
|
||||
def test_update_passwords_env(self, mock_dump, mock_pw, mock_cc,
|
||||
mock_exists, mock_chmod, mock_user):
|
||||
pw_dict = {"GeneratedPassword": 123, "LegacyPass": "override me"}
|
||||
pw_conf_path = os.path.join(self.temp_homedir,
|
||||
'undercloud-passwords.conf')
|
||||
t_pw_conf_path = os.path.join(
|
||||
self.temp_homedir, 'tripleo-undercloud-passwords.yaml')
|
||||
|
||||
mock_pw.return_value = pw_dict
|
||||
mock_exists.return_value = True
|
||||
with open(t_pw_conf_path, 'w') as t_pw:
|
||||
t_pw.write('parameter_defaults: {ExistingKey: xyz, '
|
||||
'LegacyPass: pick-me-legacy-tht, '
|
||||
'RpcPassword: pick-me-rpc}\n')
|
||||
|
||||
with open(pw_conf_path, 'w') as t_pw:
|
||||
t_pw.write('[auth]\nundercloud_db_password = ignore-me-mysql\n'
|
||||
'undercloud_rabbit_password = ignore-me-rabbit\n'
|
||||
'undercloud_rpc_password = ignore-me-rpc\n'
|
||||
'undercloud_legacy_pass = ignore-me-legacy\n')
|
||||
|
||||
self.cmd._update_passwords_env(self.temp_homedir,
|
||||
'stack', upgrade=False,
|
||||
passwords={'ADefault': 456,
|
||||
'ExistingKey':
|
||||
'dontupdate'})
|
||||
expected_dict = {
|
||||
'parameter_defaults': {'GeneratedPassword': 123,
|
||||
'LegacyPass': 'pick-me-legacy-tht',
|
||||
'RpcPassword': 'pick-me-rpc',
|
||||
'ExistingKey': 'xyz',
|
||||
'ADefault': 456}}
|
||||
mock_dump.assert_called_once_with(expected_dict,
|
||||
mock.ANY,
|
||||
default_flow_style=False)
|
||||
|
||||
# TODO(bogdando) drop once we have proper oslo.privsep
|
||||
@mock.patch('getpass.getuser', return_value='stack')
|
||||
@mock.patch('os.chmod')
|
||||
@mock.patch('os.path.exists')
|
||||
# TODO(bogdando) drop once we have proper oslo.privsep
|
||||
@mock.patch('subprocess.check_call', autospec=True)
|
||||
@mock.patch('tripleo_common.utils.passwords.generate_passwords')
|
||||
@mock.patch('yaml.safe_dump')
|
||||
def test_update_passwords_env_upgrade(self, mock_dump, mock_pw, mock_cc,
|
||||
mock_exists, mock_chmod, mock_user):
|
||||
pw_dict = {"GeneratedPassword": 123, "LegacyPass": "override me"}
|
||||
pw_conf_path = os.path.join(self.temp_homedir,
|
||||
'undercloud-passwords.conf')
|
||||
t_pw_conf_path = os.path.join(
|
||||
|
@ -145,20 +189,29 @@ class TestDeployUndercloud(TestPluginV1):
|
|||
return not file_name.startswith('/etc/keystone')
|
||||
mock_exists.side_effect = mock_file_exists
|
||||
with open(t_pw_conf_path, 'w') as t_pw:
|
||||
t_pw.write('parameter_defaults: {ExistingKey: xyz}\n')
|
||||
t_pw.write('parameter_defaults: {ExistingKey: xyz, '
|
||||
'LegacyPass: override-me-legacy, '
|
||||
'RpcPassword: override-me-rpc}\n')
|
||||
|
||||
with open(pw_conf_path, 'w') as t_pw:
|
||||
t_pw.write('[auth]\nundercloud_db_password = abc\n')
|
||||
t_pw.write('[auth]\nundercloud_db_password = pick-me-mysql\n'
|
||||
'undercloud_rabbit_password = pick-me-rabbit\n'
|
||||
'undercloud_rpc_password = pick-me-rpc\n'
|
||||
'undercloud_legacy_pass = pick-me-legacy-instack\n')
|
||||
|
||||
self.cmd._update_passwords_env(self.temp_homedir,
|
||||
'stack',
|
||||
'stack', upgrade=True,
|
||||
passwords={'ADefault': 456,
|
||||
'ExistingKey':
|
||||
'dontupdate'})
|
||||
expected_dict = {'parameter_defaults': {'GeneratedPassword': 123,
|
||||
'ExistingKey': 'xyz',
|
||||
'MysqlRootPassword': 'abc',
|
||||
'ADefault': 456}}
|
||||
expected_dict = {
|
||||
'parameter_defaults': {'GeneratedPassword': 123,
|
||||
'ExistingKey': 'xyz',
|
||||
'MysqlRootPassword': 'pick-me-mysql',
|
||||
'RpcPassword': 'pick-me-rpc',
|
||||
'RabbitPassword': 'pick-me-rabbit',
|
||||
'LegacyPass': 'pick-me-legacy-instack',
|
||||
'ADefault': 456}}
|
||||
mock_dump.assert_called_once_with(expected_dict,
|
||||
mock.ANY,
|
||||
default_flow_style=False)
|
||||
|
|
|
@ -260,84 +260,89 @@ class Deploy(command.Command):
|
|||
constants.PUPPET_MODULES,
|
||||
constants.PUPPET_BASE)
|
||||
|
||||
def _update_passwords_env(self, output_dir, user, passwords=None):
|
||||
def _update_passwords_env(self, output_dir, user, upgrade=None,
|
||||
passwords=None):
|
||||
pw_file = os.path.join(output_dir, 'tripleo-undercloud-passwords.yaml')
|
||||
undercloud_pw_file = os.path.join(output_dir,
|
||||
'undercloud-passwords.conf')
|
||||
|
||||
# Generated passwords take the lowest precedence, allowing
|
||||
# custom overrides
|
||||
stack_env = {'parameter_defaults': {}}
|
||||
|
||||
# Getting passwords that were managed by instack-undercloud so
|
||||
# we can upgrade to a containerized undercloud and keep old passwords.
|
||||
legacy_env = {}
|
||||
if os.path.exists(undercloud_pw_file):
|
||||
config = configparser.ConfigParser()
|
||||
config.read(undercloud_pw_file)
|
||||
for k, v in config.items('auth'):
|
||||
# Manage exceptions
|
||||
if k == 'undercloud_db_password':
|
||||
k = 'MysqlRootPassword'
|
||||
elif k == 'undercloud_rabbit_username':
|
||||
k = 'RpcUserName'
|
||||
elif k == 'undercloud_rabbit_password':
|
||||
try:
|
||||
# NOTE(aschultz): Only save rabbit password to rpc
|
||||
# if it's not already defined for the upgrade case.
|
||||
# The passwords are usually different so we don't
|
||||
# want to overwrite it if it already exists because
|
||||
# we'll end up rewriting the passwords later and
|
||||
# causing problems.
|
||||
config.get('auth', 'undercloud_rpc_password')
|
||||
except Exception:
|
||||
legacy_env['RpcPassword'] = v
|
||||
k = 'RabbitPassword'
|
||||
elif k == 'undercloud_rabbit_cookie':
|
||||
k = 'RabbitCookie'
|
||||
elif k == 'undercloud_heat_encryption_key':
|
||||
k = 'HeatAuthEncryptionKey'
|
||||
elif k == 'undercloud_libvirt_tls_password':
|
||||
k = 'LibvirtTLSPassword'
|
||||
elif k == 'undercloud_ha_proxy_stats_password':
|
||||
k = 'HAProxyStatsPassword'
|
||||
else:
|
||||
k = ''.join(i.capitalize() for i in k.split('_')[1:])
|
||||
legacy_env[k] = v
|
||||
|
||||
stack_env['parameter_defaults'] = password_utils.generate_passwords(
|
||||
stack_env=stack_env)
|
||||
if os.path.exists(pw_file):
|
||||
with open(pw_file) as pf:
|
||||
stack_env = yaml.safe_load(pf.read())
|
||||
stack_env['parameter_defaults'].update(
|
||||
yaml.safe_load(pf.read())['parameter_defaults'])
|
||||
|
||||
# Get the keystone keys before upgrade
|
||||
keystone_fernet_repo = '/etc/keystone/fernet-keys/'
|
||||
keystone_credential_repo = '/etc/keystone/credential-keys/'
|
||||
self._set_data_rights('/etc/keystone', user=user)
|
||||
if upgrade:
|
||||
# Getting passwords that were managed by instack-undercloud so
|
||||
# we can upgrade to a containerized undercloud and keep old
|
||||
# passwords.
|
||||
legacy_env = {}
|
||||
if os.path.exists(undercloud_pw_file):
|
||||
config = configparser.ConfigParser()
|
||||
config.read(undercloud_pw_file)
|
||||
for k, v in config.items('auth'):
|
||||
# Manage exceptions
|
||||
if k == 'undercloud_db_password':
|
||||
k = 'MysqlRootPassword'
|
||||
elif k == 'undercloud_rabbit_username':
|
||||
k = 'RpcUserName'
|
||||
elif k == 'undercloud_rabbit_password':
|
||||
try:
|
||||
# NOTE(aschultz): Only save rabbit password to rpc
|
||||
# if it's not already defined for the upgrade case.
|
||||
# The passwords are usually different so we don't
|
||||
# want to overwrite it if it already exists because
|
||||
# we'll end up rewriting the passwords later and
|
||||
# causing problems.
|
||||
config.get('auth', 'undercloud_rpc_password')
|
||||
except Exception:
|
||||
legacy_env['RpcPassword'] = v
|
||||
k = 'RabbitPassword'
|
||||
elif k == 'undercloud_rabbit_cookie':
|
||||
k = 'RabbitCookie'
|
||||
elif k == 'undercloud_heat_encryption_key':
|
||||
k = 'HeatAuthEncryptionKey'
|
||||
elif k == 'undercloud_libvirt_tls_password':
|
||||
k = 'LibvirtTLSPassword'
|
||||
elif k == 'undercloud_ha_proxy_stats_password':
|
||||
k = 'HAProxyStatsPassword'
|
||||
else:
|
||||
k = ''.join(i.capitalize() for i in k.split('_')[1:])
|
||||
legacy_env[k] = v
|
||||
|
||||
for key_index in range(0, 2):
|
||||
file_name = keystone_credential_repo + str(key_index)
|
||||
key = 'KeystoneCredential' + str(key_index)
|
||||
if os.path.exists(file_name):
|
||||
with open(file_name, 'r') as file_content:
|
||||
content = file_content.read()
|
||||
legacy_env[key] = content
|
||||
# Get the keystone keys before upgrade
|
||||
keystone_fernet_repo = '/etc/keystone/fernet-keys/'
|
||||
keystone_credential_repo = '/etc/keystone/credential-keys/'
|
||||
self._set_data_rights('/etc/keystone', user=user)
|
||||
|
||||
fernet_keys = {}
|
||||
file_count = 0
|
||||
if os.path.exists(keystone_fernet_repo):
|
||||
file_count = len(os.listdir(keystone_fernet_repo))
|
||||
for key_index in range(0, 2):
|
||||
file_name = keystone_credential_repo + str(key_index)
|
||||
key = 'KeystoneCredential' + str(key_index)
|
||||
if os.path.exists(file_name):
|
||||
with open(file_name, 'r') as file_content:
|
||||
content = file_content.read()
|
||||
legacy_env[key] = content
|
||||
|
||||
for key_index in range(0, file_count):
|
||||
file_name = keystone_fernet_repo + str(key_index)
|
||||
if os.path.exists(file_name):
|
||||
with open(file_name, 'r') as file_content:
|
||||
content = file_content.read()
|
||||
fernet_keys[file_name] = {'content': content}
|
||||
if fernet_keys:
|
||||
legacy_env['KeystoneFernetKeys'] = fernet_keys
|
||||
fernet_keys = {}
|
||||
file_count = 0
|
||||
if os.path.exists(keystone_fernet_repo):
|
||||
file_count = len(os.listdir(keystone_fernet_repo))
|
||||
|
||||
pw = password_utils.generate_passwords(stack_env=stack_env)
|
||||
stack_env['parameter_defaults'].update(pw)
|
||||
# Override what has been generated by tripleo-common with old passwords
|
||||
# if any.
|
||||
stack_env['parameter_defaults'].update(legacy_env)
|
||||
for key_index in range(0, file_count):
|
||||
file_name = keystone_fernet_repo + str(key_index)
|
||||
if os.path.exists(file_name):
|
||||
with open(file_name, 'r') as file_content:
|
||||
content = file_content.read()
|
||||
fernet_keys[file_name] = {'content': content}
|
||||
if fernet_keys:
|
||||
legacy_env['KeystoneFernetKeys'] = fernet_keys
|
||||
|
||||
# Override with picked legacy instack-undercloud values
|
||||
stack_env['parameter_defaults'].update(legacy_env)
|
||||
|
||||
if passwords:
|
||||
# These passwords are the DefaultPasswords so we only
|
||||
|
@ -584,8 +589,12 @@ class Deploy(command.Command):
|
|||
for e in plan_env_data.get('environments', {})]
|
||||
|
||||
# this will allow the user to overwrite passwords with custom envs
|
||||
pw_file = self._update_passwords_env(self.output_dir,
|
||||
parsed_args.deployment_user)
|
||||
# or pick instack legacy passwords as is, if upgrading from instack
|
||||
pw_file = self._update_passwords_env(
|
||||
self.output_dir,
|
||||
parsed_args.deployment_user,
|
||||
parsed_args.upgrade,
|
||||
)
|
||||
environments.append(pw_file)
|
||||
|
||||
# use deployed-server because we run os-collect-config locally
|
||||
|
|
Loading…
Reference in New Issue