Add auto-unlock option
The auto-unlock option intialises vault and stores the keys and root token in the leadership database. This option should only be used in testing as it is probably undesirable to store the vault keys in the leader db from a security pov. Change-Id: I10ec2f009920acf47f5353f6f947520514350bc0
This commit is contained in:
parent
5d16f8011d
commit
cb5c24366c
|
@ -58,3 +58,9 @@ options:
|
|||
description: |
|
||||
DNS record to use for DNS HA with MAAS. Do not use vip setting
|
||||
if this is set.
|
||||
auto-unlock:
|
||||
type: boolean
|
||||
default: false
|
||||
description: >-
|
||||
FOR TESTING ONLY. Initialise vault after deployment and store the keys
|
||||
locally.
|
||||
|
|
|
@ -1,7 +1,12 @@
|
|||
import functools
|
||||
import json
|
||||
import requests
|
||||
|
||||
import hvac
|
||||
import tenacity
|
||||
|
||||
import charmhelpers.core.hookenv as hookenv
|
||||
import charmhelpers.core.host as host
|
||||
import charms.reactive
|
||||
|
||||
CHARM_ACCESS_ROLE = 'local-charm-access'
|
||||
|
@ -44,6 +49,8 @@ path "sys/mounts/" {
|
|||
capabilities = ["list"]
|
||||
}"""
|
||||
|
||||
VAULT_HEALTH_URL = '{vault_addr}/v1/sys/health'
|
||||
|
||||
|
||||
def binding_address(binding):
|
||||
try:
|
||||
|
@ -94,13 +101,15 @@ def create_local_charm_access_role(client, policies):
|
|||
return client.get_role_id(CHARM_ACCESS_ROLE)
|
||||
|
||||
|
||||
def setup_charm_vault_access(token):
|
||||
def setup_charm_vault_access(token=None):
|
||||
"""Create policies and role. Grant role to charm.
|
||||
|
||||
:param token: Token to use to authenticate with vault
|
||||
:type token: str
|
||||
:returns: Id of created role
|
||||
:rtype: str"""
|
||||
if not token:
|
||||
token = hookenv.leader_get('token')
|
||||
vault_url = get_api_url()
|
||||
client = hvac.Client(
|
||||
url=vault_url,
|
||||
|
@ -118,3 +127,104 @@ def get_local_charm_access_role_id():
|
|||
:rtype: str
|
||||
"""
|
||||
return hookenv.leader_get(CHARM_ACCESS_ROLE_ID)
|
||||
|
||||
|
||||
def get_client():
|
||||
"""Provide a client for talking to the vault api
|
||||
|
||||
:returns: vault client
|
||||
:rtype: hvac.Client
|
||||
"""
|
||||
return hvac.Client(url=get_api_url())
|
||||
|
||||
|
||||
@tenacity.retry(wait=tenacity.wait_exponential(multiplier=1, max=10),
|
||||
stop=tenacity.stop_after_attempt(10),
|
||||
reraise=True)
|
||||
def get_vault_health():
|
||||
"""Query vault to retrieve health
|
||||
|
||||
:returns: Vault health
|
||||
:rtype: dict
|
||||
"""
|
||||
response = requests.get(
|
||||
VAULT_HEALTH_URL.format(vault_addr=get_api_url()))
|
||||
return response.json()
|
||||
|
||||
|
||||
def opportunistic_restart():
|
||||
"""Restart vault if possible"""
|
||||
if can_restart():
|
||||
hookenv.log("Restarting vault", level=hookenv.DEBUG)
|
||||
host.service_restart('vault')
|
||||
else:
|
||||
hookenv.log("Starting vault", level=hookenv.DEBUG)
|
||||
host.service_start('vault')
|
||||
|
||||
|
||||
def prepare_vault():
|
||||
"""Setup vault as much as possible
|
||||
|
||||
Attempt to prepare vault for operation. Where possible, initialise, unseal
|
||||
and create role for local charm access to vault.
|
||||
"""
|
||||
if not host.service_running('vault'):
|
||||
hookenv.log("Defering unlock vault not running ", level=hookenv.DEBUG)
|
||||
return
|
||||
vault_health = get_vault_health()
|
||||
if not vault_health['initialized'] and hookenv.is_leader():
|
||||
initialize_vault()
|
||||
if vault_health['sealed']:
|
||||
unseal_vault()
|
||||
if hookenv.is_leader():
|
||||
setup_charm_vault_access()
|
||||
|
||||
|
||||
def initialize_vault(shares=1, threshold=1):
|
||||
"""Initialise vault
|
||||
|
||||
Initialise vault and store the resulting key(s) and token in the leader db.
|
||||
:param shares: Number of shares to create
|
||||
:type shares: int
|
||||
:param threshold: Minimum number of shares needed to unlock
|
||||
:type threshold: int
|
||||
"""
|
||||
client = get_client()
|
||||
result = client.initialize(shares, threshold)
|
||||
client.token = result['root_token']
|
||||
hookenv.leader_set(
|
||||
root_token=result['root_token'],
|
||||
keys=json.dumps(result['keys']))
|
||||
|
||||
|
||||
def unseal_vault(keys=None):
|
||||
"""Unseal vault with provided keys. If no keys are provided retrieve from
|
||||
leader db"""
|
||||
client = get_client()
|
||||
if not keys:
|
||||
keys = json.loads(hookenv.leader_get()['keys'])
|
||||
for key in keys:
|
||||
client.unseal(key)
|
||||
|
||||
|
||||
def can_restart():
|
||||
"""Check if vault can be restarted
|
||||
|
||||
:returns: Can vault be restarted
|
||||
:rtype: bool
|
||||
"""
|
||||
safe_restart = False
|
||||
if not host.service_running('vault'):
|
||||
safe_restart = True
|
||||
elif hookenv.config('auto-unlock'):
|
||||
safe_restart = True
|
||||
else:
|
||||
client = get_client()
|
||||
if not client.is_initialized():
|
||||
safe_restart = True
|
||||
elif client.is_sealed():
|
||||
safe_restart = True
|
||||
hookenv.log(
|
||||
"Safe to restart: {}".format(safe_restart),
|
||||
level=hookenv.DEBUG)
|
||||
return safe_restart
|
||||
|
|
|
@ -1,9 +1,6 @@
|
|||
import base64
|
||||
import hvac
|
||||
import psycopg2
|
||||
import requests
|
||||
import subprocess
|
||||
import tenacity
|
||||
|
||||
|
||||
from charmhelpers.contrib.charmsupport.nrpe import (
|
||||
|
@ -28,9 +25,9 @@ from charmhelpers.core.hookenv import (
|
|||
)
|
||||
|
||||
from charmhelpers.core.host import (
|
||||
service,
|
||||
service_restart,
|
||||
service_running,
|
||||
service_start,
|
||||
write_file,
|
||||
)
|
||||
|
||||
|
@ -44,6 +41,7 @@ from charms.reactive import (
|
|||
remove_state,
|
||||
set_state,
|
||||
when,
|
||||
when_file_changed,
|
||||
when_not,
|
||||
)
|
||||
|
||||
|
@ -76,7 +74,6 @@ VAULT_INDEX_DDL = """
|
|||
CREATE INDEX IF NOT EXISTS parent_path_idx ON vault_kv_store (parent_path);
|
||||
"""
|
||||
|
||||
VAULT_HEALTH_URL = '{vault_addr}/v1/sys/health'
|
||||
|
||||
OPTIONAL_INTERFACES = [
|
||||
['etcd'],
|
||||
|
@ -85,32 +82,8 @@ REQUIRED_INTERFACES = [
|
|||
['shared-db', 'db.master']
|
||||
]
|
||||
|
||||
|
||||
def get_client():
|
||||
return hvac.Client(url=vault.get_api_url())
|
||||
|
||||
|
||||
@tenacity.retry(wait=tenacity.wait_exponential(multiplier=1, max=10),
|
||||
stop=tenacity.stop_after_attempt(10),
|
||||
reraise=True)
|
||||
def get_vault_health():
|
||||
response = requests.get(
|
||||
VAULT_HEALTH_URL.format(vault_addr=vault.get_api_url()))
|
||||
return response.json()
|
||||
|
||||
|
||||
def can_restart():
|
||||
safe_restart = False
|
||||
if not service_running('vault'):
|
||||
safe_restart = True
|
||||
else:
|
||||
client = get_client()
|
||||
if not client.is_initialized():
|
||||
safe_restart = True
|
||||
elif client.is_sealed():
|
||||
safe_restart = True
|
||||
log("Safe to restart: {}".format(safe_restart), level=DEBUG)
|
||||
return safe_restart
|
||||
VAULT_CONFIG = '/var/snap/vault/common/vault.hcl'
|
||||
VAULT_SYSTEMD_CONFIG = '/etc/systemd/system/vault.service'
|
||||
|
||||
|
||||
def ssl_available(config):
|
||||
|
@ -158,9 +131,11 @@ def snap_refresh():
|
|||
if validate_snap_channel(channel):
|
||||
clear_flag('snap.channel.invalid')
|
||||
snap.refresh('vault', channel=channel)
|
||||
if can_restart():
|
||||
if vault.can_restart():
|
||||
log("Restarting vault", level=DEBUG)
|
||||
service_restart('vault')
|
||||
if config('auto-unlock'):
|
||||
vault.prepare_vault()
|
||||
else:
|
||||
set_flag('snap.channel.invalid')
|
||||
|
||||
|
@ -192,20 +167,16 @@ def configure_vault(context):
|
|||
log("Rendering vault.hcl.j2", level=DEBUG)
|
||||
render(
|
||||
'vault.hcl.j2',
|
||||
'/var/snap/vault/common/vault.hcl',
|
||||
VAULT_CONFIG,
|
||||
context,
|
||||
perms=0o600)
|
||||
log("Rendering vault systemd configuation", level=DEBUG)
|
||||
render(
|
||||
'vault.service.j2',
|
||||
'/etc/systemd/system/vault.service',
|
||||
VAULT_SYSTEMD_CONFIG,
|
||||
{},
|
||||
perms=0o644)
|
||||
if can_restart():
|
||||
log("Restarting vault", level=DEBUG)
|
||||
service_restart('vault')
|
||||
else:
|
||||
service_start('vault') # restart seals the vault
|
||||
service('enable', 'vault')
|
||||
log("Opening vault port", level=DEBUG)
|
||||
open_port(8200)
|
||||
|
||||
|
@ -409,6 +380,14 @@ def cluster_connected(hacluster):
|
|||
hacluster.bind_resources()
|
||||
|
||||
|
||||
@when_file_changed(VAULT_CONFIG, VAULT_SYSTEMD_CONFIG)
|
||||
def file_change_auto_unlock_mode():
|
||||
log("Calling opportunistic_restart", level=DEBUG)
|
||||
vault.opportunistic_restart()
|
||||
if config('auto-unlock'):
|
||||
vault.prepare_vault()
|
||||
|
||||
|
||||
@when('snap.installed.vault')
|
||||
def prime_assess_status():
|
||||
atexit(_assess_status)
|
||||
|
@ -511,7 +490,7 @@ def _assess_status():
|
|||
|
||||
health = None
|
||||
if service_running('vault'):
|
||||
health = get_vault_health()
|
||||
health = vault.get_vault_health()
|
||||
application_version_set(health.get('version'))
|
||||
|
||||
_missing_interfaces = []
|
||||
|
|
|
@ -12,3 +12,13 @@ smoke_bundles:
|
|||
- xenial-mysql
|
||||
dev_bundles:
|
||||
- bionic
|
||||
target_deploy_status:
|
||||
vault:
|
||||
workload-status: blocked
|
||||
workload-status-message: Vault needs to be initialized
|
||||
easyrsa:
|
||||
workload-status-message: Certificate Authority connected.
|
||||
etcd:
|
||||
workload-status-message: Healthy
|
||||
postgresql:
|
||||
workload-status-message: Live
|
||||
|
|
|
@ -7,6 +7,16 @@ import unit_tests.test_utils
|
|||
|
||||
class TestLibCharmVault(unit_tests.test_utils.CharmTestCase):
|
||||
|
||||
_health_response = {
|
||||
"initialized": True,
|
||||
"sealed": False,
|
||||
"standby": False,
|
||||
"server_time_utc": 1523952750,
|
||||
"version": "0.9.0",
|
||||
"cluster_name": "vault-cluster-9dd8dd12",
|
||||
"cluster_id": "1ea3d74c-3819-fbaf-f780-bae0babc998f"
|
||||
}
|
||||
|
||||
def setUp(self):
|
||||
super(TestLibCharmVault, self).setUp()
|
||||
self.obj = vault
|
||||
|
@ -97,3 +107,211 @@ class TestLibCharmVault(unit_tests.test_utils.CharmTestCase):
|
|||
network_get_primary_address.return_value = '1.2.3.4'
|
||||
self.assertEqual(vault.get_cluster_url(), 'http://1.2.3.4:8201')
|
||||
network_get_primary_address.assert_called_with('cluster')
|
||||
|
||||
@patch.object(vault.hvac, 'Client')
|
||||
@patch.object(vault, 'get_api_url')
|
||||
def test_get_client(self, get_api_url, hvac_Client):
|
||||
get_api_url.return_value = 'http://this-unit'
|
||||
vault.get_client()
|
||||
hvac_Client.assert_called_once_with(url='http://this-unit')
|
||||
|
||||
@patch.object(vault.host, 'service_running')
|
||||
def test_can_restart_vault_down(self, service_running):
|
||||
service_running.return_value = False
|
||||
self.assertTrue(vault.can_restart())
|
||||
|
||||
@patch.object(vault.host, 'service_running')
|
||||
@patch.object(vault.hookenv, 'config')
|
||||
@patch.object(vault, 'get_client')
|
||||
def test_can_restart_not_initialized(self, get_client, config,
|
||||
service_running):
|
||||
config.return_value = False
|
||||
service_running.return_value = True
|
||||
hvac_mock = mock.MagicMock()
|
||||
hvac_mock.is_initialized.return_value = False
|
||||
get_client.return_value = hvac_mock
|
||||
self.assertTrue(vault.can_restart())
|
||||
hvac_mock.is_initialized.assert_called_once_with()
|
||||
|
||||
@patch.object(vault.host, 'service_running')
|
||||
@patch.object(vault.hookenv, 'config')
|
||||
@patch.object(vault, 'get_client')
|
||||
def test_can_restart_sealed(self, get_client, config, service_running):
|
||||
config.return_value = False
|
||||
service_running.return_value = True
|
||||
hvac_mock = mock.MagicMock()
|
||||
hvac_mock.is_initialized.return_value = True
|
||||
hvac_mock.is_sealed.return_value = True
|
||||
get_client.return_value = hvac_mock
|
||||
self.assertTrue(vault.can_restart())
|
||||
hvac_mock.is_initialized.assert_called_once_with()
|
||||
hvac_mock.is_sealed.assert_called_once_with()
|
||||
|
||||
@patch.object(vault.host, 'service_running')
|
||||
@patch.object(vault.hookenv, 'config')
|
||||
@patch.object(vault, 'get_client')
|
||||
def test_can_restart_unsealed(self, get_client, config, service_running):
|
||||
config.return_value = False
|
||||
service_running.return_value = True
|
||||
hvac_mock = mock.MagicMock()
|
||||
hvac_mock.is_initialized.return_value = True
|
||||
hvac_mock.is_sealed.return_value = False
|
||||
get_client.return_value = hvac_mock
|
||||
self.assertFalse(vault.can_restart())
|
||||
|
||||
@patch.object(vault.host, 'service_running')
|
||||
@patch.object(vault.hookenv, 'config')
|
||||
def test_can_restart_auto_unlock(self, config, service_running):
|
||||
config.return_value = True
|
||||
service_running.return_value = True
|
||||
self.assertTrue(vault.can_restart())
|
||||
|
||||
@patch.object(vault, 'get_api_url')
|
||||
@patch.object(vault, 'requests')
|
||||
def test_get_vault_health(self, requests, get_api_url):
|
||||
get_api_url.return_value = "https://vault.demo.com:8200"
|
||||
mock_response = mock.MagicMock()
|
||||
mock_response.json.return_value = self._health_response
|
||||
requests.get.return_value = mock_response
|
||||
self.assertEqual(vault.get_vault_health(),
|
||||
self._health_response)
|
||||
requests.get.assert_called_with(
|
||||
"https://vault.demo.com:8200/v1/sys/health")
|
||||
mock_response.json.assert_called_once()
|
||||
|
||||
@patch.object(vault, 'setup_charm_vault_access')
|
||||
@patch.object(vault.hookenv, 'is_leader')
|
||||
@patch.object(vault, 'unseal_vault')
|
||||
@patch.object(vault, 'initialize_vault')
|
||||
@patch.object(vault, 'get_vault_health')
|
||||
@patch.object(vault.hookenv, 'log')
|
||||
@patch.object(vault.host, 'service_running')
|
||||
def test_prepare_vault(self, service_running, log, get_vault_health,
|
||||
initialize_vault, unseal_vault, is_leader,
|
||||
setup_charm_vault_access):
|
||||
is_leader.return_value = True
|
||||
service_running.return_value = True
|
||||
get_vault_health.return_value = {
|
||||
'initialized': False,
|
||||
'sealed': True}
|
||||
vault.prepare_vault()
|
||||
initialize_vault.assert_called_once_with()
|
||||
setup_charm_vault_access.assert_called_once_with()
|
||||
unseal_vault.assert_called_once_with()
|
||||
setup_charm_vault_access.assert_called_once_with()
|
||||
|
||||
@patch.object(vault.hookenv, 'is_leader')
|
||||
@patch.object(vault, 'unseal_vault')
|
||||
@patch.object(vault, 'initialize_vault')
|
||||
@patch.object(vault, 'get_vault_health')
|
||||
@patch.object(vault.hookenv, 'log')
|
||||
@patch.object(vault.host, 'service_running')
|
||||
def test_prepare_vault_non_leader(self, service_running, log,
|
||||
get_vault_health, initialize_vault,
|
||||
unseal_vault, is_leader):
|
||||
is_leader.return_value = False
|
||||
service_running.return_value = True
|
||||
get_vault_health.return_value = {
|
||||
'initialized': False,
|
||||
'sealed': True}
|
||||
vault.prepare_vault()
|
||||
self.assertFalse(initialize_vault.called)
|
||||
unseal_vault.assert_called_once_with()
|
||||
|
||||
@patch.object(vault, 'unseal_vault')
|
||||
@patch.object(vault, 'initialize_vault')
|
||||
@patch.object(vault.hookenv, 'log')
|
||||
@patch.object(vault.host, 'service_running')
|
||||
def test_prepare_vault_svc_down(self, service_running, log,
|
||||
initialize_vault, unseal_vault):
|
||||
service_running.return_value = False
|
||||
vault.prepare_vault()
|
||||
self.assertFalse(initialize_vault.called)
|
||||
self.assertFalse(unseal_vault.called)
|
||||
|
||||
@patch.object(vault, 'setup_charm_vault_access')
|
||||
@patch.object(vault.hookenv, 'is_leader')
|
||||
@patch.object(vault, 'unseal_vault')
|
||||
@patch.object(vault, 'initialize_vault')
|
||||
@patch.object(vault, 'get_vault_health')
|
||||
@patch.object(vault.hookenv, 'log')
|
||||
@patch.object(vault.host, 'service_running')
|
||||
def test_prepare_vault_initialised(self, service_running, log,
|
||||
get_vault_health, initialize_vault,
|
||||
unseal_vault, is_leader,
|
||||
setup_charm_vault_access):
|
||||
is_leader.return_Value = False
|
||||
service_running.return_value = True
|
||||
get_vault_health.return_value = {
|
||||
'initialized': True,
|
||||
'sealed': True}
|
||||
vault.prepare_vault()
|
||||
self.assertFalse(initialize_vault.called)
|
||||
unseal_vault.assert_called_once_with()
|
||||
|
||||
@patch.object(vault, 'setup_charm_vault_access')
|
||||
@patch.object(vault.hookenv, 'is_leader')
|
||||
@patch.object(vault, 'unseal_vault')
|
||||
@patch.object(vault, 'initialize_vault')
|
||||
@patch.object(vault, 'get_vault_health')
|
||||
@patch.object(vault.hookenv, 'log')
|
||||
@patch.object(vault.host, 'service_running')
|
||||
def test_prepare_vault_unsealed(self, service_running, log,
|
||||
get_vault_health, initialize_vault,
|
||||
unseal_vault, is_leader,
|
||||
setup_charm_vault_access):
|
||||
is_leader.return_Value = False
|
||||
service_running.return_value = True
|
||||
get_vault_health.return_value = {
|
||||
'initialized': True,
|
||||
'sealed': False}
|
||||
vault.prepare_vault()
|
||||
self.assertFalse(initialize_vault.called)
|
||||
self.assertFalse(unseal_vault.called)
|
||||
|
||||
@patch.object(vault.hookenv, 'leader_set')
|
||||
@patch.object(vault, 'get_client')
|
||||
def test_initialize_vault(self, get_client, leader_set):
|
||||
hvac_mock = mock.MagicMock()
|
||||
hvac_mock.is_initialized.return_value = True
|
||||
hvac_mock.initialize.return_value = {
|
||||
'keys': ['c579a143d55423483b9076ea7bba49b63ae432bf74729f77afb4e'],
|
||||
'keys_base64': ['xX35oUPVVCNIO5B26nu6SbY65DK/dHKfd6+05y1Afcw='],
|
||||
'root_token': 'dee94df7-23a3-9bf2-cb96-e943537c2b76'
|
||||
}
|
||||
get_client.return_value = hvac_mock
|
||||
vault.initialize_vault()
|
||||
hvac_mock.initialize.assert_called_once_with(1, 1)
|
||||
leader_set.assert_called_once_with(
|
||||
keys='["c579a143d55423483b9076ea7bba49b63ae432bf74729f77afb4e"]',
|
||||
root_token='dee94df7-23a3-9bf2-cb96-e943537c2b76')
|
||||
|
||||
@patch.object(vault.hookenv, 'leader_get')
|
||||
@patch.object(vault, 'get_client')
|
||||
def test_unseal_vault(self, get_client, leader_get):
|
||||
hvac_mock = mock.MagicMock()
|
||||
get_client.return_value = hvac_mock
|
||||
leader_get.return_value = {
|
||||
'root_token': 'dee94df7-23a3-9bf2-cb96-e943537c2b76',
|
||||
'keys': '["c579a143d55423483b9076ea7bba49b63ae432bf74729f77afb4e"]'
|
||||
}
|
||||
vault.unseal_vault()
|
||||
hvac_mock.unseal.assert_called_once_with(
|
||||
'c579a143d55423483b9076ea7bba49b63ae432bf74729f77afb4e')
|
||||
|
||||
@patch.object(vault.hookenv, 'log')
|
||||
@patch.object(vault.host, 'service_restart')
|
||||
@patch.object(vault, 'can_restart')
|
||||
def test_opportunistic_restart(self, can_restart, service_restart, log):
|
||||
can_restart.return_value = True
|
||||
vault.opportunistic_restart()
|
||||
service_restart.assert_called_once_with('vault')
|
||||
|
||||
@patch.object(vault.hookenv, 'log')
|
||||
@patch.object(vault.host, 'service_start')
|
||||
@patch.object(vault, 'can_restart')
|
||||
def test_opportunistic_restart_no_restart(self, can_restart, service_start,
|
||||
log):
|
||||
can_restart.return_value = False
|
||||
vault.opportunistic_restart()
|
||||
service_start.assert_called_once_with('vault')
|
||||
|
|
|
@ -58,7 +58,6 @@ class TestHandlers(unit_tests.test_utils.CharmTestCase):
|
|||
'open_port',
|
||||
'service_restart',
|
||||
'service_running',
|
||||
'service_start',
|
||||
'set_state',
|
||||
'status_set',
|
||||
'remove_state',
|
||||
|
@ -86,7 +85,7 @@ class TestHandlers(unit_tests.test_utils.CharmTestCase):
|
|||
'ssl-cert': 'acert',
|
||||
'ssl-key': 'akey'}))
|
||||
|
||||
@patch.object(handlers, 'can_restart')
|
||||
@patch.object(handlers.vault, 'can_restart')
|
||||
def test_configure_vault(self, can_restart):
|
||||
can_restart.return_value = True
|
||||
self.config.return_value = {'disable-mlock': False}
|
||||
|
@ -215,7 +214,7 @@ class TestHandlers(unit_tests.test_utils.CharmTestCase):
|
|||
|
||||
@patch.object(handlers, 'save_etcd_client_credentials')
|
||||
@patch.object(handlers.vault, 'get_cluster_url')
|
||||
@patch.object(handlers, 'can_restart')
|
||||
@patch.object(handlers.vault, 'can_restart')
|
||||
@patch.object(handlers.vault, 'get_api_url')
|
||||
def test_configure_vault_etcd(self, get_api_url, can_restart,
|
||||
get_cluster_url,
|
||||
|
@ -260,55 +259,8 @@ class TestHandlers(unit_tests.test_utils.CharmTestCase):
|
|||
)
|
||||
self.is_flag_set.assert_called_with('etcd.tls.available')
|
||||
|
||||
@patch.object(handlers.hvac, 'Client')
|
||||
@patch.object(handlers.vault, 'get_api_url')
|
||||
def test_get_client(self, get_api_url, hvac_Client):
|
||||
get_api_url.return_value = 'http://this-unit'
|
||||
handlers.get_client()
|
||||
hvac_Client.assert_called_once_with(url='http://this-unit')
|
||||
|
||||
def test_can_restart_vault_down(self):
|
||||
self.service_running.return_value = False
|
||||
self.assertTrue(handlers.can_restart())
|
||||
|
||||
@patch.object(handlers, 'get_client')
|
||||
def test_can_restart_not_initialized(self, get_client):
|
||||
hvac_mock = mock.MagicMock()
|
||||
hvac_mock.is_initialized.return_value = False
|
||||
get_client.return_value = hvac_mock
|
||||
self.assertTrue(handlers.can_restart())
|
||||
|
||||
@patch.object(handlers, 'get_client')
|
||||
def test_can_restart_sealed(self, get_client):
|
||||
hvac_mock = mock.MagicMock()
|
||||
hvac_mock.is_initialized.return_value = True
|
||||
hvac_mock.is_sealed.return_value = True
|
||||
get_client.return_value = hvac_mock
|
||||
self.assertTrue(handlers.can_restart())
|
||||
|
||||
@patch.object(handlers, 'get_client')
|
||||
def test_can_restart_unsealed(self, get_client):
|
||||
hvac_mock = mock.MagicMock()
|
||||
hvac_mock.is_initialized.return_value = True
|
||||
hvac_mock.is_sealed.return_value = False
|
||||
get_client.return_value = hvac_mock
|
||||
self.assertFalse(handlers.can_restart())
|
||||
|
||||
@patch.object(handlers.vault, 'get_api_url')
|
||||
@patch.object(handlers, 'requests')
|
||||
def test_get_vault_health(self, requests, get_api_url):
|
||||
get_api_url.return_value = "https://vault.demo.com:8200"
|
||||
mock_response = mock.MagicMock()
|
||||
mock_response.json.return_value = self._health_response
|
||||
requests.get.return_value = mock_response
|
||||
self.assertEqual(handlers.get_vault_health(),
|
||||
self._health_response)
|
||||
requests.get.assert_called_with(
|
||||
"https://vault.demo.com:8200/v1/sys/health")
|
||||
mock_response.json.assert_called_once()
|
||||
|
||||
@patch.object(handlers, '_assess_interface_groups')
|
||||
@patch.object(handlers, 'get_vault_health')
|
||||
@patch.object(handlers.vault, 'get_vault_health')
|
||||
def test_assess_status(self, get_vault_health,
|
||||
_assess_interface_groups):
|
||||
self.is_flag_set.return_value = False
|
||||
|
@ -356,7 +308,7 @@ class TestHandlers(unit_tests.test_utils.CharmTestCase):
|
|||
self.is_flag_set.assert_called_with('config.dns_vip.invalid')
|
||||
|
||||
@patch.object(handlers, '_assess_interface_groups')
|
||||
@patch.object(handlers, 'get_vault_health')
|
||||
@patch.object(handlers.vault, 'get_vault_health')
|
||||
def test_assess_status_not_running(self, get_vault_health,
|
||||
_assess_interface_groups):
|
||||
self.is_flag_set.return_value = False
|
||||
|
@ -368,7 +320,7 @@ class TestHandlers(unit_tests.test_utils.CharmTestCase):
|
|||
'blocked', 'Vault service not running')
|
||||
|
||||
@patch.object(handlers, '_assess_interface_groups')
|
||||
@patch.object(handlers, 'get_vault_health')
|
||||
@patch.object(handlers.vault, 'get_vault_health')
|
||||
def test_assess_status_vault_init(self, get_vault_health,
|
||||
_assess_interface_groups):
|
||||
self.is_flag_set.return_value = False
|
||||
|
@ -380,7 +332,7 @@ class TestHandlers(unit_tests.test_utils.CharmTestCase):
|
|||
'blocked', 'Vault needs to be initialized')
|
||||
|
||||
@patch.object(handlers, '_assess_interface_groups')
|
||||
@patch.object(handlers, 'get_vault_health')
|
||||
@patch.object(handlers.vault, 'get_vault_health')
|
||||
def test_assess_status_vault_sealed(self, get_vault_health,
|
||||
_assess_interface_groups):
|
||||
self.is_flag_set.return_value = False
|
||||
|
@ -438,17 +390,23 @@ class TestHandlers(unit_tests.test_utils.CharmTestCase):
|
|||
self.config.assert_called_with('channel')
|
||||
self.set_flag.assert_called_with('snap.channel.invalid')
|
||||
|
||||
@patch.object(handlers, 'can_restart')
|
||||
@patch.object(handlers.vault, 'can_restart')
|
||||
def test_snap_refresh_restartable(self, can_restart):
|
||||
self.config.return_value = 'edge'
|
||||
conf = {
|
||||
'channel': 'edge',
|
||||
'auto-unlock': False}
|
||||
self.config.side_effect = lambda x: conf[x]
|
||||
can_restart.return_value = True
|
||||
handlers.snap_refresh()
|
||||
self.snap.refresh.assert_called_with('vault', channel='edge')
|
||||
self.config.assert_called_with('channel')
|
||||
self.service_restart.assert_called_with('vault')
|
||||
self.clear_flag.assert_called_with('snap.channel.invalid')
|
||||
config_calls = [
|
||||
mock.call('channel'),
|
||||
mock.call('auto-unlock')]
|
||||
self.config.assert_has_calls(config_calls)
|
||||
|
||||
@patch.object(handlers, 'can_restart')
|
||||
@patch.object(handlers.vault, 'can_restart')
|
||||
def test_snap_refresh_not_restartable(self, can_restart):
|
||||
self.config.return_value = 'edge'
|
||||
can_restart.return_value = False
|
||||
|
|
Loading…
Reference in New Issue