Merge "Move RotateFernetKeysAction functionality to utils"
This commit is contained in:
commit
163d4b3b4b
|
@ -330,78 +330,12 @@ class RotateFernetKeysAction(GetPasswordsAction):
|
|||
|
||||
def run(self, context):
|
||||
swift = self.get_object_client(context)
|
||||
|
||||
try:
|
||||
env = plan_utils.get_env(swift, self.container)
|
||||
except swiftexceptions.ClientException as err:
|
||||
err_msg = ("Error retrieving environment for plan %s: %s" % (
|
||||
self.container, err))
|
||||
LOG.exception(err_msg)
|
||||
return actions.Result(error=err_msg)
|
||||
|
||||
parameter_defaults = env.get('parameter_defaults', {})
|
||||
passwords = self._get_overriden_passwords(env.get('passwords', {}),
|
||||
parameter_defaults)
|
||||
|
||||
next_index = self.get_next_index(passwords['KeystoneFernetKeys'])
|
||||
keys_map = self.rotate_keys(passwords['KeystoneFernetKeys'],
|
||||
next_index)
|
||||
max_keys = self.get_max_keys_value(parameter_defaults)
|
||||
keys_map = self.purge_excess_keys(max_keys, keys_map)
|
||||
|
||||
env['passwords']['KeystoneFernetKeys'] = keys_map
|
||||
|
||||
try:
|
||||
plan_utils.put_env(swift, env)
|
||||
except swiftexceptions.ClientException as err:
|
||||
err_msg = "Error uploading to container: %s" % err
|
||||
LOG.exception(err_msg)
|
||||
return actions.Result(error=err_msg)
|
||||
|
||||
plan_utils.cache_delete(swift,
|
||||
self.container,
|
||||
"tripleo.parameters.get")
|
||||
|
||||
return keys_map
|
||||
|
||||
@staticmethod
|
||||
def get_key_index_from_path(path):
|
||||
return int(path[path.rfind('/') + 1:])
|
||||
|
||||
def get_next_index(self, keys_map):
|
||||
return self.get_key_index_from_path(
|
||||
max(keys_map, key=self.get_key_index_from_path)) + 1
|
||||
|
||||
def get_key_path(self, index):
|
||||
return password_utils.KEYSTONE_FERNET_REPO + str(index)
|
||||
|
||||
def rotate_keys(self, keys_map, next_index):
|
||||
next_index_path = self.get_key_path(next_index)
|
||||
zero_index_path = self.get_key_path(0)
|
||||
|
||||
# promote staged key to be new primary
|
||||
keys_map[next_index_path] = keys_map[zero_index_path]
|
||||
# Set new staged key
|
||||
keys_map[zero_index_path] = {
|
||||
'content': password_utils.create_keystone_credential()}
|
||||
return keys_map
|
||||
|
||||
def get_max_keys_value(self, parameter_defaults):
|
||||
# The number of max keys should always be positive. The minimum amount
|
||||
# of keys is 3.
|
||||
return max(parameter_defaults.get('KeystoneFernetMaxActiveKeys', 5), 3)
|
||||
|
||||
def purge_excess_keys(self, max_keys, keys_map):
|
||||
current_repo_size = len(keys_map)
|
||||
if current_repo_size <= max_keys:
|
||||
return keys_map
|
||||
key_paths = sorted(keys_map.keys(), key=self.get_key_index_from_path)
|
||||
|
||||
keys_to_be_purged = current_repo_size - max_keys
|
||||
|
||||
for key_path in key_paths[1:keys_to_be_purged + 1]:
|
||||
del keys_map[key_path]
|
||||
return keys_map
|
||||
return plan_utils.update_plan_rotate_fernet_keys(
|
||||
swift, self.container)
|
||||
except Exception as err:
|
||||
LOG.exception(six.text_typ(err))
|
||||
return actions.Result(error=six.text_type(err))
|
||||
|
||||
|
||||
class GetNetworkConfigAction(base.TripleOAction):
|
||||
|
|
|
@ -21,7 +21,6 @@ from tripleo_common.actions import parameters
|
|||
from tripleo_common import constants
|
||||
from tripleo_common import exception
|
||||
from tripleo_common.tests import base
|
||||
from tripleo_common.utils import passwords as password_utils
|
||||
|
||||
_EXISTING_PASSWORDS = {
|
||||
'PlacementPassword': 'VFJeqBKbatYhQm9jja67hufft',
|
||||
|
@ -606,94 +605,6 @@ class GetProfileOfFlavorActionTest(base.TestCase):
|
|||
mock_get_profile_of_flavor.assert_called_once()
|
||||
|
||||
|
||||
class RotateFernetKeysActionTest(base.TestCase):
|
||||
|
||||
def test_get_next_index(self):
|
||||
action = parameters.RotateFernetKeysAction()
|
||||
keys_map = {
|
||||
password_utils.KEYSTONE_FERNET_REPO + '0': {
|
||||
'content': 'Some key'},
|
||||
password_utils.KEYSTONE_FERNET_REPO + '1': {
|
||||
'content': 'Some other key'},
|
||||
}
|
||||
next_index = action.get_next_index(keys_map)
|
||||
self.assertEqual(next_index, 2)
|
||||
|
||||
@mock.patch('tripleo_common.utils.passwords.'
|
||||
'create_keystone_credential')
|
||||
def test_rotate_keys(self, mock_keystone_creds):
|
||||
action = parameters.RotateFernetKeysAction()
|
||||
mock_keystone_creds.return_value = 'Some new key'
|
||||
|
||||
staged_key_index = password_utils.KEYSTONE_FERNET_REPO + '0'
|
||||
new_primary_key_index = password_utils.KEYSTONE_FERNET_REPO + '2'
|
||||
keys_map = {
|
||||
password_utils.KEYSTONE_FERNET_REPO + '0': {
|
||||
'content': 'Some key'},
|
||||
password_utils.KEYSTONE_FERNET_REPO + '1': {
|
||||
'content': 'Some other key'},
|
||||
}
|
||||
new_keys_map = action.rotate_keys(keys_map, 2)
|
||||
|
||||
# Staged key should be the new key
|
||||
self.assertEqual('Some new key',
|
||||
new_keys_map[staged_key_index]['content'])
|
||||
# primary key should be the previous staged key
|
||||
self.assertEqual('Some key',
|
||||
new_keys_map[new_primary_key_index]['content'])
|
||||
|
||||
def test_purge_excess_keys_should_purge(self):
|
||||
action = parameters.RotateFernetKeysAction()
|
||||
keys_map = {
|
||||
password_utils.KEYSTONE_FERNET_REPO + '0': {
|
||||
'content': 'key0'},
|
||||
password_utils.KEYSTONE_FERNET_REPO + '1': {
|
||||
'content': 'key1'},
|
||||
password_utils.KEYSTONE_FERNET_REPO + '2': {
|
||||
'content': 'key2'},
|
||||
password_utils.KEYSTONE_FERNET_REPO + '3': {
|
||||
'content': 'key3'},
|
||||
password_utils.KEYSTONE_FERNET_REPO + '4': {
|
||||
'content': 'key4'},
|
||||
}
|
||||
max_keys = 3
|
||||
keys_map = action.purge_excess_keys(max_keys, keys_map)
|
||||
self.assertEqual(max_keys, len(keys_map))
|
||||
# It should keep index 0, 3 and 4
|
||||
self.assertIn(password_utils.KEYSTONE_FERNET_REPO + '0', keys_map)
|
||||
self.assertIn(password_utils.KEYSTONE_FERNET_REPO + '3', keys_map)
|
||||
self.assertIn(password_utils.KEYSTONE_FERNET_REPO + '4', keys_map)
|
||||
# It sould have removed index 1 and 2
|
||||
self.assertNotIn(password_utils.KEYSTONE_FERNET_REPO + '1', keys_map)
|
||||
self.assertNotIn(password_utils.KEYSTONE_FERNET_REPO + '2', keys_map)
|
||||
|
||||
def test_purge_excess_keys_should_not_purge_if_equal_to_max(self):
|
||||
action = parameters.RotateFernetKeysAction()
|
||||
keys_map = {
|
||||
password_utils.KEYSTONE_FERNET_REPO + '0': {
|
||||
'content': 'key0'},
|
||||
password_utils.KEYSTONE_FERNET_REPO + '1': {
|
||||
'content': 'key1'},
|
||||
password_utils.KEYSTONE_FERNET_REPO + '2': {
|
||||
'content': 'key2'},
|
||||
}
|
||||
max_keys = 3
|
||||
keys_map = action.purge_excess_keys(max_keys, keys_map)
|
||||
self.assertEqual(max_keys, len(keys_map))
|
||||
|
||||
def test_purge_excess_keys_should_not_purge_if_less_than_max(self):
|
||||
action = parameters.RotateFernetKeysAction()
|
||||
keys_map = {
|
||||
password_utils.KEYSTONE_FERNET_REPO + '0': {
|
||||
'content': 'key0'},
|
||||
password_utils.KEYSTONE_FERNET_REPO + '1': {
|
||||
'content': 'key1'},
|
||||
}
|
||||
max_keys = 3
|
||||
keys_map = action.purge_excess_keys(max_keys, keys_map)
|
||||
self.assertEqual(2, len(keys_map))
|
||||
|
||||
|
||||
class GetNetworkConfigActionTest(base.TestCase):
|
||||
|
||||
@mock.patch('tripleo_common.utils.plan.'
|
||||
|
|
|
@ -21,6 +21,7 @@ import zlib
|
|||
from swiftclient import exceptions as swiftexceptions
|
||||
|
||||
from tripleo_common.tests import base
|
||||
from tripleo_common.utils import passwords as password_utils
|
||||
from tripleo_common.utils import plan as plan_utils
|
||||
|
||||
|
||||
|
@ -384,3 +385,83 @@ class PlanTest(base.TestCase):
|
|||
cache_container,
|
||||
cache_key
|
||||
)
|
||||
|
||||
def test_get_next_index(self):
|
||||
keys_map = {
|
||||
password_utils.KEYSTONE_FERNET_REPO + '0': {
|
||||
'content': 'Some key'},
|
||||
password_utils.KEYSTONE_FERNET_REPO + '1': {
|
||||
'content': 'Some other key'},
|
||||
}
|
||||
next_index = plan_utils.get_next_index(keys_map)
|
||||
self.assertEqual(next_index, 2)
|
||||
|
||||
@mock.patch('tripleo_common.utils.passwords.'
|
||||
'create_keystone_credential')
|
||||
def test_rotate_keys(self, mock_keystone_creds):
|
||||
mock_keystone_creds.return_value = 'Some new key'
|
||||
|
||||
staged_key_index = password_utils.KEYSTONE_FERNET_REPO + '0'
|
||||
new_primary_key_index = password_utils.KEYSTONE_FERNET_REPO + '2'
|
||||
keys_map = {
|
||||
password_utils.KEYSTONE_FERNET_REPO + '0': {
|
||||
'content': 'Some key'},
|
||||
password_utils.KEYSTONE_FERNET_REPO + '1': {
|
||||
'content': 'Some other key'},
|
||||
}
|
||||
new_keys_map = plan_utils.rotate_keys(keys_map, 2)
|
||||
|
||||
# Staged key should be the new key
|
||||
self.assertEqual('Some new key',
|
||||
new_keys_map[staged_key_index]['content'])
|
||||
# primary key should be the previous staged key
|
||||
self.assertEqual('Some key',
|
||||
new_keys_map[new_primary_key_index]['content'])
|
||||
|
||||
def test_purge_excess_keys_should_purge(self):
|
||||
keys_map = {
|
||||
password_utils.KEYSTONE_FERNET_REPO + '0': {
|
||||
'content': 'key0'},
|
||||
password_utils.KEYSTONE_FERNET_REPO + '1': {
|
||||
'content': 'key1'},
|
||||
password_utils.KEYSTONE_FERNET_REPO + '2': {
|
||||
'content': 'key2'},
|
||||
password_utils.KEYSTONE_FERNET_REPO + '3': {
|
||||
'content': 'key3'},
|
||||
password_utils.KEYSTONE_FERNET_REPO + '4': {
|
||||
'content': 'key4'},
|
||||
}
|
||||
max_keys = 3
|
||||
keys_map = plan_utils.purge_excess_keys(max_keys, keys_map)
|
||||
self.assertEqual(max_keys, len(keys_map))
|
||||
# It should keep index 0, 3 and 4
|
||||
self.assertIn(password_utils.KEYSTONE_FERNET_REPO + '0', keys_map)
|
||||
self.assertIn(password_utils.KEYSTONE_FERNET_REPO + '3', keys_map)
|
||||
self.assertIn(password_utils.KEYSTONE_FERNET_REPO + '4', keys_map)
|
||||
# It sould have removed index 1 and 2
|
||||
self.assertNotIn(password_utils.KEYSTONE_FERNET_REPO + '1', keys_map)
|
||||
self.assertNotIn(password_utils.KEYSTONE_FERNET_REPO + '2', keys_map)
|
||||
|
||||
def test_purge_excess_keys_should_not_purge_if_equal_to_max(self):
|
||||
keys_map = {
|
||||
password_utils.KEYSTONE_FERNET_REPO + '0': {
|
||||
'content': 'key0'},
|
||||
password_utils.KEYSTONE_FERNET_REPO + '1': {
|
||||
'content': 'key1'},
|
||||
password_utils.KEYSTONE_FERNET_REPO + '2': {
|
||||
'content': 'key2'},
|
||||
}
|
||||
max_keys = 3
|
||||
keys_map = plan_utils.purge_excess_keys(max_keys, keys_map)
|
||||
self.assertEqual(max_keys, len(keys_map))
|
||||
|
||||
def test_purge_excess_keys_should_not_purge_if_less_than_max(self):
|
||||
keys_map = {
|
||||
password_utils.KEYSTONE_FERNET_REPO + '0': {
|
||||
'content': 'key0'},
|
||||
password_utils.KEYSTONE_FERNET_REPO + '1': {
|
||||
'content': 'key1'},
|
||||
}
|
||||
max_keys = 3
|
||||
keys_map = plan_utils.purge_excess_keys(max_keys, keys_map)
|
||||
self.assertEqual(2, len(keys_map))
|
||||
|
|
|
@ -29,6 +29,7 @@ from swiftclient import exceptions as swiftexceptions
|
|||
|
||||
from tripleo_common import constants
|
||||
from tripleo_common.image import kolla_builder
|
||||
from tripleo_common.utils import passwords as password_utils
|
||||
from tripleo_common.utils import swift as swiftutils
|
||||
from tripleo_common.utils.validations import pattern_validator
|
||||
|
||||
|
@ -372,3 +373,87 @@ def update_plan_environment_with_image_parameters(
|
|||
LOG.exception(err_msg)
|
||||
raise RuntimeError(err_msg)
|
||||
return env
|
||||
|
||||
|
||||
def update_plan_rotate_fernet_keys(swift,
|
||||
container=constants.DEFAULT_CONTAINER_NAME):
|
||||
try:
|
||||
env = get_env(swift, container)
|
||||
except swiftexceptions.ClientException as err:
|
||||
err_msg = ("Error retrieving environment for plan %s: %s" % (
|
||||
container, err))
|
||||
LOG.exception(err_msg)
|
||||
raise RuntimeError(err_msg)
|
||||
|
||||
parameter_defaults = env.get('parameter_defaults', {})
|
||||
passwords = get_overriden_passwords(env.get(
|
||||
'passwords', {}), parameter_defaults)
|
||||
|
||||
next_index = get_next_index(passwords['KeystoneFernetKeys'])
|
||||
keys_map = rotate_keys(passwords['KeystoneFernetKeys'],
|
||||
next_index)
|
||||
max_keys = get_max_keys_value(parameter_defaults)
|
||||
keys_map = purge_excess_keys(max_keys, keys_map)
|
||||
|
||||
env['passwords']['KeystoneFernetKeys'] = keys_map
|
||||
|
||||
try:
|
||||
put_env(swift, env)
|
||||
except swiftexceptions.ClientException as err:
|
||||
err_msg = "Error uploading to container: %s" % err
|
||||
LOG.exception(err_msg)
|
||||
raise RuntimeError(err_msg)
|
||||
|
||||
cache_delete(swift, container, "tripleo.parameters.get")
|
||||
return keys_map
|
||||
|
||||
|
||||
def get_overriden_passwords(env_passwords, parameter_defaults):
|
||||
for name in constants.PASSWORD_PARAMETER_NAMES:
|
||||
if name in parameter_defaults:
|
||||
env_passwords[name] = parameter_defaults[name]
|
||||
return env_passwords
|
||||
|
||||
|
||||
def get_key_index_from_path(path):
|
||||
return int(path[path.rfind('/') + 1:])
|
||||
|
||||
|
||||
def get_next_index(keys_map):
|
||||
return get_key_index_from_path(
|
||||
max(keys_map, key=get_key_index_from_path)) + 1
|
||||
|
||||
|
||||
def get_key_path(index):
|
||||
return password_utils.KEYSTONE_FERNET_REPO + str(index)
|
||||
|
||||
|
||||
def rotate_keys(keys_map, next_index):
|
||||
next_index_path = get_key_path(next_index)
|
||||
zero_index_path = get_key_path(0)
|
||||
|
||||
# promote staged key to be new primary
|
||||
keys_map[next_index_path] = keys_map[zero_index_path]
|
||||
# Set new staged key
|
||||
keys_map[zero_index_path] = {
|
||||
'content': password_utils.create_keystone_credential()}
|
||||
return keys_map
|
||||
|
||||
|
||||
def get_max_keys_value(parameter_defaults):
|
||||
# The number of max keys should always be positive. The minimum amount
|
||||
# of keys is 3.
|
||||
return max(parameter_defaults.get('KeystoneFernetMaxActiveKeys', 5), 3)
|
||||
|
||||
|
||||
def purge_excess_keys(max_keys, keys_map):
|
||||
current_repo_size = len(keys_map)
|
||||
if current_repo_size <= max_keys:
|
||||
return keys_map
|
||||
key_paths = sorted(keys_map.keys(), key=get_key_index_from_path)
|
||||
|
||||
keys_to_be_purged = current_repo_size - max_keys
|
||||
|
||||
for key_path in key_paths[1:keys_to_be_purged + 1]:
|
||||
del keys_map[key_path]
|
||||
return keys_map
|
||||
|
|
Loading…
Reference in New Issue