Update vault tests for VIP testing
Update vault tests to support an explicit client pointing at the vip. Also, switch to using a namedtuple for managing the clients to allow client metadata to be stored along with the hvac client.
This commit is contained in:
@@ -5,12 +5,16 @@ import zaza.charm_tests.vault.utils as vault_utils
|
||||
|
||||
def basic_setup():
|
||||
clients = vault_utils.get_clients()
|
||||
unseal_client = clients[0]
|
||||
vip_client = vault_utils.get_vip_client()
|
||||
if vip_client:
|
||||
unseal_client = vip_client
|
||||
else:
|
||||
unseal_client = clients[0]
|
||||
initialized = vault_utils.is_initialized(unseal_client)
|
||||
# The credentials are written to a file to allow the tests to be re-run
|
||||
# this is mainly useful for manually working on the tests.
|
||||
if initialized:
|
||||
vault_creds = vault_utils.get_credentails()
|
||||
else:
|
||||
vault_creds = vault_utils.init_vault(unseal_client[1])
|
||||
vault_creds = vault_utils.init_vault(unseal_client)
|
||||
vault_utils.store_credentails(vault_creds)
|
||||
|
||||
@@ -14,33 +14,36 @@ class VaultTest(unittest.TestCase):
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
cls.clients = vault_utils.get_clients()
|
||||
cls.vip_client = vault_utils.get_vip_client()
|
||||
if cls.vip_client:
|
||||
cls.clients.append(cls.vip_client)
|
||||
vault_creds = vault_utils.get_credentails()
|
||||
vault_utils.unseal_all(cls.clients, vault_creds['keys'][0])
|
||||
vault_utils.auth_all(cls.clients, vault_creds['root_token'])
|
||||
|
||||
def test_all_clients_authenticated(self):
|
||||
for (addr, client) in self.clients:
|
||||
for client in self.clients:
|
||||
for i in range(1, 10):
|
||||
try:
|
||||
self.assertTrue(client.is_authenticated())
|
||||
self.assertTrue(client.hvac_client.is_authenticated())
|
||||
except hvac.exceptions.InternalServerError:
|
||||
time.sleep(2)
|
||||
else:
|
||||
break
|
||||
else:
|
||||
self.assertTrue(client.is_authenticated())
|
||||
self.assertTrue(client.hvac_client.is_authenticated())
|
||||
|
||||
def check_read(self, key, value):
|
||||
for (addr, client) in self.clients:
|
||||
for client in self.clients:
|
||||
self.assertEqual(
|
||||
client.read('secret/uuids')['data']['uuid'],
|
||||
client.hvac_client.read('secret/uuids')['data']['uuid'],
|
||||
value)
|
||||
|
||||
def test_consistent_read_write(self):
|
||||
key = 'secret/uuids'
|
||||
for (addr, client) in self.clients:
|
||||
for client in self.clients:
|
||||
value = str(uuid.uuid1())
|
||||
client.write(key, uuid=value, lease='1h')
|
||||
client.hvac_client.write(key, uuid=value, lease='1h')
|
||||
# Now check all clients read the same value back
|
||||
self.check_read(key, value)
|
||||
|
||||
@@ -49,14 +52,15 @@ class VaultTest(unittest.TestCase):
|
||||
leader = []
|
||||
leader_address = []
|
||||
leader_cluster_address = []
|
||||
for (addr, client) in self.clients:
|
||||
self.assertTrue(client.ha_status['ha_enabled'])
|
||||
for client in self.clients:
|
||||
self.assertTrue(client.hvac_client.ha_status['ha_enabled'])
|
||||
leader_address.append(
|
||||
client.ha_status['leader_address'])
|
||||
client.hvac_client.ha_status['leader_address'])
|
||||
leader_cluster_address.append(
|
||||
client.ha_status['leader_cluster_address'])
|
||||
if client.ha_status['is_self']:
|
||||
leader.append(addr)
|
||||
client.hvac_client.ha_status['leader_cluster_address'])
|
||||
if (client.hvac_client.ha_status['is_self'] and not
|
||||
client.vip_client):
|
||||
leader.append(client.addr)
|
||||
# Check there is exactly one leader
|
||||
self.assertEqual(len(leader), 1)
|
||||
# Check both cluster addresses match accross the cluster
|
||||
@@ -64,9 +68,9 @@ class VaultTest(unittest.TestCase):
|
||||
self.assertEqual(len(set(leader_cluster_address)), 1)
|
||||
|
||||
def test_check_vault_status(self):
|
||||
for (addr, client) in self.clients:
|
||||
self.assertFalse(client.seal_status['sealed'])
|
||||
self.assertTrue(client.seal_status['cluster_name'])
|
||||
for client in self.clients:
|
||||
self.assertFalse(client.hvac_client.seal_status['sealed'])
|
||||
self.assertTrue(client.hvac_client.seal_status['cluster_name'])
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
@@ -7,58 +7,103 @@ import time
|
||||
import urllib3
|
||||
import yaml
|
||||
|
||||
import collections
|
||||
|
||||
import zaza.charm_lifecycle.utils as utils
|
||||
import zaza.model
|
||||
|
||||
AUTH_FILE = "vault_tests.yaml"
|
||||
CharmVaultClient = collections.namedtuple(
|
||||
'CharmVaultClient', ['addr', 'hvac_client', 'vip_client'])
|
||||
|
||||
|
||||
def get_client(vault_url):
|
||||
def get_unit_api_url(ip):
|
||||
"""Return URL for api access
|
||||
|
||||
:param unit_ip: IP address to use in vault url
|
||||
:type unit_ip: str
|
||||
:returns: URL
|
||||
:rtype: atr
|
||||
"""
|
||||
return 'http://{}:8200'.format(ip)
|
||||
|
||||
|
||||
def get_hvac_client(vault_url):
|
||||
"""Return an hvac client for the given URL
|
||||
|
||||
:param vault_url: Vault url to point client at
|
||||
:returns: hvac.Client
|
||||
:type vault_url: str
|
||||
:returns: hvac client for given url
|
||||
:rtype: hvac.Client
|
||||
"""
|
||||
return hvac.Client(url=vault_url)
|
||||
|
||||
|
||||
def get_vip_client():
|
||||
"""Return CharmVaultClient for the vip if a vip is being used
|
||||
|
||||
:returns: CharmVaultClient
|
||||
:rtype: CharmVaultClient or None
|
||||
"""
|
||||
client = None
|
||||
vault_config = zaza.model.get_application_config(
|
||||
utils.get_juju_model(), 'vault')
|
||||
vip = vault_config.get('vip', {}).get('value')
|
||||
if vip:
|
||||
client = CharmVaultClient(
|
||||
vip,
|
||||
get_hvac_client(get_unit_api_url(vip)),
|
||||
True)
|
||||
return client
|
||||
|
||||
|
||||
def init_vault(client, shares=1, threshold=1):
|
||||
"""Initialise vault
|
||||
|
||||
:param client: hvac.Client Client to use for initiliasation
|
||||
:param shares: int Number of key shares to create
|
||||
:param threshold: int Number of keys needed to unseal vault
|
||||
:returns: hvac.Client
|
||||
:param client: Client to use for initiliasation
|
||||
:type client: CharmVaultClient
|
||||
:param shares: Number of key shares to create
|
||||
:type shares: int
|
||||
:param threshold: Number of keys needed to unseal vault
|
||||
:type threshold: int
|
||||
:returns: Token and key(s) for accessing vault
|
||||
:rtype: dict
|
||||
"""
|
||||
return client.initialize(shares, threshold)
|
||||
return client.hvac_client.initialize(shares, threshold)
|
||||
|
||||
|
||||
def get_clients(units=None):
|
||||
"""Create a list of clients, one per vault server
|
||||
|
||||
:param units: [ip1, ip2, ...] List of IP addresses of vault endpoints
|
||||
:returns: [hvac.Client, ...] List of clients
|
||||
:param units: List of IP addresses of vault endpoints
|
||||
:type units: [str, str, ...]
|
||||
:returns: List of CharmVaultClients
|
||||
:rtype: [CharmVaultClient, ...]
|
||||
"""
|
||||
if not units:
|
||||
units = zaza.model.get_app_ips(utils.get_juju_model(), 'vault')
|
||||
clients = []
|
||||
for unit in units:
|
||||
vault_url = 'http://{}:8200'.format(unit)
|
||||
clients.append((unit, get_client(vault_url)))
|
||||
vault_url = get_unit_api_url(unit)
|
||||
clients.append(CharmVaultClient(
|
||||
unit,
|
||||
get_hvac_client(vault_url),
|
||||
False))
|
||||
return clients
|
||||
|
||||
|
||||
def is_initialized(client):
|
||||
"""Check if vault is initialized
|
||||
|
||||
:param client: hvac.Client Client to use to check if vault is
|
||||
initialized
|
||||
:returns: bool
|
||||
:param client: Client to use to check if vault is initialized
|
||||
:type client: CharmVaultClient
|
||||
:returns: Whether vault is initialized
|
||||
:rtype: bool
|
||||
"""
|
||||
initialized = False
|
||||
for i in range(1, 10):
|
||||
try:
|
||||
initialized = client[1].is_initialized()
|
||||
initialized = client.hvac_client.is_initialized()
|
||||
except (ConnectionRefusedError,
|
||||
urllib3.exceptions.NewConnectionError,
|
||||
urllib3.exceptions.MaxRetryError,
|
||||
@@ -72,6 +117,12 @@ def is_initialized(client):
|
||||
|
||||
|
||||
def get_credentails():
|
||||
"""Retrieve vault token and keys from unit. These are stored on a unit
|
||||
during functional tests.
|
||||
|
||||
:returns: Tokens and keys for accessing test environment
|
||||
:rtype: dict
|
||||
"""
|
||||
unit = zaza.model.get_first_unit_name(utils.get_juju_model(), 'vault')
|
||||
with tempfile.TemporaryDirectory() as tmpdirname:
|
||||
tmp_file = '{}/{}'.format(tmpdirname, AUTH_FILE)
|
||||
@@ -86,6 +137,12 @@ def get_credentails():
|
||||
|
||||
|
||||
def store_credentails(creds):
|
||||
"""Store the supplied credentials on a vault unit. ONLY USE FOR FUNCTIONAL
|
||||
TESTING.
|
||||
|
||||
:param creds: Keys and token to store
|
||||
:type creds: dict
|
||||
"""
|
||||
unit = zaza.model.get_first_unit_name(utils.get_juju_model(), 'vault')
|
||||
with tempfile.NamedTemporaryFile(mode='w') as fp:
|
||||
fp.write(yaml.dump(creds))
|
||||
@@ -100,8 +157,10 @@ def store_credentails(creds):
|
||||
def get_credentails_from_file(auth_file):
|
||||
"""Read the vault credentials from the auth_file
|
||||
|
||||
:param auth_file: str Path to file with credentials
|
||||
:returns: {} Dictionary of credentials
|
||||
:param auth_file: Path to file with credentials
|
||||
:type auth_file: str
|
||||
:returns: Token and keys
|
||||
:rtype: dict
|
||||
"""
|
||||
with open(auth_file, 'r') as stream:
|
||||
vault_creds = yaml.load(stream)
|
||||
@@ -111,7 +170,8 @@ def get_credentails_from_file(auth_file):
|
||||
def write_credentails(auth_file, vault_creds):
|
||||
"""Write the vault credentials to the auth_file
|
||||
|
||||
:param auth_file: str Path to file to write credentials
|
||||
:param auth_file: Path to file to write credentials
|
||||
:type auth_file: str
|
||||
"""
|
||||
with open(auth_file, 'w') as outfile:
|
||||
yaml.dump(vault_creds, outfile, default_flow_style=False)
|
||||
@@ -120,19 +180,23 @@ def write_credentails(auth_file, vault_creds):
|
||||
def unseal_all(clients, key):
|
||||
"""Unseal all the vaults with the given clients with the provided key
|
||||
|
||||
:param clients: [hvac.Client, ...] List of clients
|
||||
:param key: str key to unlock clients
|
||||
:param clients: List of clients
|
||||
:type clients: [CharmVaultClient, ...]
|
||||
:param key: key to unlock clients
|
||||
:type key: str
|
||||
"""
|
||||
for (addr, client) in clients:
|
||||
if client.is_sealed():
|
||||
client.unseal(key)
|
||||
for client in clients:
|
||||
if client.hvac_client.is_sealed():
|
||||
client.hvac_client.unseal(key)
|
||||
|
||||
|
||||
def auth_all(clients, token):
|
||||
"""Authenticate all the given clients with the provided token
|
||||
|
||||
:param clients: [hvac.Client, ...] List of clients
|
||||
:param token: str token to authorize clients
|
||||
:param clients: List of clients
|
||||
:type clients: [CharmVaultClient, ...]
|
||||
:param token: Token to authorize clients
|
||||
:type token: str
|
||||
"""
|
||||
for (addr, client) in clients:
|
||||
client.token = token
|
||||
for client in clients:
|
||||
client.hvac_client.token = token
|
||||
|
||||
Reference in New Issue
Block a user