Merge "Remove support for PKI tokens and legacy charm managed certificates"
This commit is contained in:
commit
c256592460
|
@ -12,18 +12,9 @@
|
|||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import hashlib
|
||||
import os
|
||||
import json
|
||||
|
||||
from base64 import b64decode
|
||||
|
||||
from charmhelpers.core.host import (
|
||||
mkdir,
|
||||
write_file,
|
||||
service_restart,
|
||||
)
|
||||
|
||||
from charmhelpers.contrib.openstack import context
|
||||
|
||||
from charmhelpers.contrib.hahelpers.cluster import (
|
||||
|
@ -38,116 +29,13 @@ from charmhelpers.core.hookenv import (
|
|||
config,
|
||||
log,
|
||||
leader_get,
|
||||
DEBUG,
|
||||
INFO,
|
||||
related_units,
|
||||
relation_ids,
|
||||
relation_get,
|
||||
)
|
||||
|
||||
from charmhelpers.core.strutils import (
|
||||
bool_from_string,
|
||||
)
|
||||
|
||||
from charmhelpers.contrib.hahelpers.apache import install_ca_cert
|
||||
|
||||
CA_CERT_PATH = '/usr/local/share/ca-certificates/keystone_juju_ca_cert.crt'
|
||||
|
||||
|
||||
def is_cert_provided_in_config():
|
||||
cert = config('ssl_cert')
|
||||
key = config('ssl_key')
|
||||
return bool(cert and key)
|
||||
|
||||
|
||||
class SSLContext(context.ApacheSSLContext):
|
||||
|
||||
def configure_cert(self, cn):
|
||||
from keystone_utils import (
|
||||
SSH_USER,
|
||||
get_ca,
|
||||
ensure_permissions,
|
||||
is_ssl_cert_master,
|
||||
KEYSTONE_USER,
|
||||
)
|
||||
|
||||
# Ensure ssl dir exists whether master or not
|
||||
perms = 0o775
|
||||
mkdir(path=self.ssl_dir, owner=SSH_USER, group=KEYSTONE_USER,
|
||||
perms=perms)
|
||||
# Ensure accessible by keystone ssh user and group (for sync)
|
||||
ensure_permissions(self.ssl_dir, user=SSH_USER, group=KEYSTONE_USER,
|
||||
perms=perms)
|
||||
|
||||
if not is_cert_provided_in_config() and not is_ssl_cert_master():
|
||||
log("Not ssl-cert-master - skipping apache cert config until "
|
||||
"master is elected", level=INFO)
|
||||
return
|
||||
|
||||
log("Creating apache ssl certs in %s" % (self.ssl_dir), level=INFO)
|
||||
|
||||
cert = config('ssl_cert')
|
||||
key = config('ssl_key')
|
||||
|
||||
if not (cert and key):
|
||||
ca = get_ca(user=SSH_USER)
|
||||
cert, key = ca.get_cert_and_key(common_name=cn)
|
||||
else:
|
||||
cert = b64decode(cert)
|
||||
key = b64decode(key)
|
||||
|
||||
write_file(path=os.path.join(self.ssl_dir, 'cert_{}'.format(cn)),
|
||||
content=cert, owner=SSH_USER, group=KEYSTONE_USER,
|
||||
perms=0o640)
|
||||
write_file(path=os.path.join(self.ssl_dir, 'key_{}'.format(cn)),
|
||||
content=key, owner=SSH_USER, group=KEYSTONE_USER,
|
||||
perms=0o640)
|
||||
|
||||
def configure_ca(self):
|
||||
from keystone_utils import (
|
||||
SSH_USER,
|
||||
get_ca,
|
||||
ensure_permissions,
|
||||
is_ssl_cert_master,
|
||||
KEYSTONE_USER,
|
||||
)
|
||||
|
||||
if not is_cert_provided_in_config() and not is_ssl_cert_master():
|
||||
log("Not ssl-cert-master - skipping apache ca config until "
|
||||
"master is elected", level=INFO)
|
||||
return
|
||||
|
||||
cert = config('ssl_cert')
|
||||
key = config('ssl_key')
|
||||
|
||||
ca_cert = config('ssl_ca')
|
||||
if ca_cert:
|
||||
ca_cert = b64decode(ca_cert)
|
||||
elif not (cert and key):
|
||||
# NOTE(hopem): if a cert and key are provided as config we don't
|
||||
# mandate that a CA is also provided since it isn't necessarily
|
||||
# needed. As a result we only generate a custom CA if we are also
|
||||
# generating cert and key.
|
||||
ca = get_ca(user=SSH_USER)
|
||||
ca_cert = ca.get_ca_bundle()
|
||||
|
||||
if ca_cert:
|
||||
# Ensure accessible by keystone ssh user and group (unison)
|
||||
install_ca_cert(ca_cert)
|
||||
ensure_permissions(CA_CERT_PATH, user=SSH_USER,
|
||||
group=KEYSTONE_USER, perms=0o0644)
|
||||
|
||||
def canonical_names(self):
|
||||
addresses = self.get_network_addresses()
|
||||
addrs = []
|
||||
for address, endpoint in addresses:
|
||||
addrs.append(endpoint)
|
||||
|
||||
return list(set(addrs))
|
||||
|
||||
|
||||
class ApacheSSLContext(SSLContext):
|
||||
|
||||
class ApacheSSLContext(context.ApacheSSLContext):
|
||||
interfaces = ['https']
|
||||
external_ports = []
|
||||
service_namespace = 'keystone'
|
||||
|
@ -157,31 +45,13 @@ class ApacheSSLContext(SSLContext):
|
|||
# late import to work around circular dependency
|
||||
from keystone_utils import (
|
||||
determine_ports,
|
||||
update_hash_from_path,
|
||||
)
|
||||
|
||||
ssl_paths = [CA_CERT_PATH, self.ssl_dir]
|
||||
|
||||
self.external_ports = determine_ports()
|
||||
before = hashlib.sha256()
|
||||
for path in ssl_paths:
|
||||
update_hash_from_path(before, path)
|
||||
|
||||
ret = super(ApacheSSLContext, self).__call__()
|
||||
|
||||
after = hashlib.sha256()
|
||||
for path in ssl_paths:
|
||||
update_hash_from_path(after, path)
|
||||
|
||||
# Ensure that apache2 is restarted if these change
|
||||
if before.hexdigest() != after.hexdigest():
|
||||
service_restart('apache2')
|
||||
|
||||
return ret
|
||||
return super(ApacheSSLContext, self).__call__()
|
||||
|
||||
|
||||
class NginxSSLContext(SSLContext):
|
||||
|
||||
class NginxSSLContext(context.ApacheSSLContext):
|
||||
interfaces = ['https']
|
||||
external_ports = []
|
||||
service_namespace = 'keystone'
|
||||
|
@ -192,30 +62,14 @@ class NginxSSLContext(SSLContext):
|
|||
# late import to work around circular dependency
|
||||
from keystone_utils import (
|
||||
determine_ports,
|
||||
update_hash_from_path,
|
||||
APACHE_SSL_DIR
|
||||
)
|
||||
|
||||
ssl_paths = [CA_CERT_PATH, APACHE_SSL_DIR]
|
||||
|
||||
self.external_ports = determine_ports()
|
||||
before = hashlib.sha256()
|
||||
for path in ssl_paths:
|
||||
update_hash_from_path(before, path)
|
||||
|
||||
ret = super(NginxSSLContext, self).__call__()
|
||||
if not ret:
|
||||
log("SSL not used", level='DEBUG')
|
||||
return {}
|
||||
|
||||
after = hashlib.sha256()
|
||||
for path in ssl_paths:
|
||||
update_hash_from_path(after, path)
|
||||
|
||||
# Ensure that Nginx is restarted if these change
|
||||
if before.hexdigest() != after.hexdigest():
|
||||
service_restart('snap.keystone.nginx')
|
||||
|
||||
# Transform for use by Nginx
|
||||
"""
|
||||
{'endpoints': [(u'10.5.0.30', u'10.5.0.30', 4990, 4980),
|
||||
|
@ -294,7 +148,7 @@ class KeystoneContext(context.OSContextGenerator):
|
|||
def __call__(self):
|
||||
from keystone_utils import (
|
||||
api_port, set_admin_token, endpoint_url, resolve_address,
|
||||
PUBLIC, ADMIN, PKI_CERTS_DIR, ensure_pki_cert_paths, ADMIN_DOMAIN,
|
||||
PUBLIC, ADMIN, ADMIN_DOMAIN,
|
||||
snap_install_requested, get_api_version,
|
||||
)
|
||||
ctxt = {}
|
||||
|
@ -331,25 +185,6 @@ class KeystoneContext(context.OSContextGenerator):
|
|||
flags = context.config_flags_parser(ldap_flags)
|
||||
ctxt['ldap_config_flags'] = flags
|
||||
|
||||
enable_pki = config('enable-pki')
|
||||
if enable_pki and bool_from_string(enable_pki):
|
||||
log("Enabling PKI", level=DEBUG)
|
||||
ctxt['token_provider'] = 'pki'
|
||||
|
||||
# NOTE(jamespage): Only check PKI configuration if the PKI
|
||||
# token format is in use, which has been
|
||||
# removed as of OpenStack Ocata.
|
||||
ensure_pki_cert_paths()
|
||||
certs = os.path.join(PKI_CERTS_DIR, 'certs')
|
||||
privates = os.path.join(PKI_CERTS_DIR, 'privates')
|
||||
ctxt['enable_signing'] = True
|
||||
ctxt.update({'certfile': os.path.join(certs, 'signing_cert.pem'),
|
||||
'keyfile': os.path.join(privates, 'signing_key.pem'),
|
||||
'ca_certs': os.path.join(certs, 'ca.pem'),
|
||||
'ca_key': os.path.join(certs, 'ca_key.pem')})
|
||||
else:
|
||||
ctxt['enable_signing'] = False
|
||||
|
||||
# Base endpoint URL's which are used in keystone responses
|
||||
# to unauthenticated requests to redirect clients to the
|
||||
# correct auth URL.
|
||||
|
|
|
@ -16,12 +16,10 @@
|
|||
|
||||
import hashlib
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
|
||||
from subprocess import check_call
|
||||
|
||||
from charmhelpers.contrib import unison
|
||||
from charmhelpers.core import unitdata
|
||||
|
||||
from charmhelpers.core.hookenv import (
|
||||
|
@ -29,7 +27,6 @@ from charmhelpers.core.hookenv import (
|
|||
UnregisteredHookError,
|
||||
config,
|
||||
log,
|
||||
local_unit,
|
||||
DEBUG,
|
||||
INFO,
|
||||
WARNING,
|
||||
|
@ -44,17 +41,12 @@ from charmhelpers.core.hookenv import (
|
|||
)
|
||||
|
||||
from charmhelpers.core.host import (
|
||||
mkdir,
|
||||
service_pause,
|
||||
service_stop,
|
||||
service_start,
|
||||
service_restart,
|
||||
)
|
||||
|
||||
from charmhelpers.core.strutils import (
|
||||
bool_from_string,
|
||||
)
|
||||
|
||||
from charmhelpers.fetch import (
|
||||
apt_install, apt_update,
|
||||
filter_installed_packages
|
||||
|
@ -85,32 +77,18 @@ from keystone_utils import (
|
|||
migrate_database,
|
||||
save_script_rc,
|
||||
post_snap_install,
|
||||
synchronize_ca_if_changed,
|
||||
register_configs,
|
||||
restart_map,
|
||||
services,
|
||||
CLUSTER_RES,
|
||||
KEYSTONE_CONF,
|
||||
KEYSTONE_USER,
|
||||
POLICY_JSON,
|
||||
TOKEN_FLUSH_CRON_FILE,
|
||||
SSH_USER,
|
||||
setup_ipv6,
|
||||
send_notifications,
|
||||
check_peer_actions,
|
||||
get_ssl_sync_request_units,
|
||||
is_ssl_cert_master,
|
||||
is_db_ready,
|
||||
clear_ssl_synced_units,
|
||||
is_db_initialised,
|
||||
update_certs_if_available,
|
||||
ensure_ssl_dir,
|
||||
ensure_pki_dir_permissions,
|
||||
ensure_permissions,
|
||||
force_ssl_sync,
|
||||
filter_null,
|
||||
ensure_ssl_dirs,
|
||||
ensure_pki_cert_paths,
|
||||
is_service_present,
|
||||
delete_service_entry,
|
||||
assess_status,
|
||||
|
@ -128,7 +106,6 @@ from keystone_utils import (
|
|||
from charmhelpers.contrib.hahelpers.cluster import (
|
||||
is_elected_leader,
|
||||
get_hacluster_config,
|
||||
peer_units,
|
||||
https,
|
||||
is_clustered,
|
||||
)
|
||||
|
@ -142,7 +119,6 @@ from charmhelpers.payload.execd import execd_preinstall
|
|||
from charmhelpers.contrib.peerstorage import (
|
||||
peer_retrieve_by_prefix,
|
||||
peer_echo,
|
||||
relation_get as relation_get_and_migrate,
|
||||
)
|
||||
from charmhelpers.contrib.openstack.ip import (
|
||||
ADMIN,
|
||||
|
@ -199,13 +175,9 @@ def install():
|
|||
disable_unused_apache_sites()
|
||||
service_pause('keystone')
|
||||
|
||||
unison.ensure_user(user=SSH_USER, group=SSH_USER)
|
||||
unison.ensure_user(user=SSH_USER, group=KEYSTONE_USER)
|
||||
|
||||
|
||||
@hooks.hook('config-changed')
|
||||
@restart_on_change(restart_map(), restart_functions=restart_function_map())
|
||||
@synchronize_ca_if_changed(fatal=True)
|
||||
@harden()
|
||||
def config_changed():
|
||||
if config('prefer-ipv6'):
|
||||
|
@ -214,36 +186,21 @@ def config_changed():
|
|||
sync_db_with_multi_ipv6_addresses(config('database'),
|
||||
config('database-user'))
|
||||
|
||||
unison.ensure_user(user=SSH_USER, group=SSH_USER)
|
||||
unison.ensure_user(user=SSH_USER, group=KEYSTONE_USER)
|
||||
homedir = unison.get_homedir(SSH_USER)
|
||||
if not os.path.isdir(homedir):
|
||||
mkdir(homedir, SSH_USER, SSH_USER, 0o775)
|
||||
|
||||
if not config('action-managed-upgrade'):
|
||||
if openstack_upgrade_available('keystone'):
|
||||
status_set('maintenance', 'Running openstack upgrade')
|
||||
do_openstack_upgrade_reexec(configs=CONFIGS)
|
||||
|
||||
for r_id in relation_ids('cluster'):
|
||||
cluster_joined(rid=r_id, ssl_sync_request=False)
|
||||
cluster_joined(rid=r_id)
|
||||
|
||||
config_changed_postupgrade()
|
||||
|
||||
|
||||
@hooks.hook('config-changed-postupgrade')
|
||||
@restart_on_change(restart_map(), restart_functions=restart_function_map())
|
||||
@synchronize_ca_if_changed(fatal=True)
|
||||
@harden()
|
||||
def config_changed_postupgrade():
|
||||
# Ensure ssl dir exists and is unison-accessible
|
||||
ensure_ssl_dir()
|
||||
|
||||
if not snap_install_requested():
|
||||
check_call(['chmod', '-R', 'g+wrx', '/var/lib/keystone/'])
|
||||
|
||||
ensure_ssl_dirs()
|
||||
|
||||
save_script_rc()
|
||||
release = os_release('keystone')
|
||||
if run_in_apache(release=release):
|
||||
|
@ -275,57 +232,14 @@ def config_changed_postupgrade():
|
|||
if snap_install_requested() and not is_unit_paused_set():
|
||||
service_restart('snap.keystone.*')
|
||||
|
||||
initialise_pki()
|
||||
|
||||
update_all_identity_relation_units()
|
||||
update_all_domain_backends()
|
||||
update_all_fid_backends()
|
||||
|
||||
# Ensure sync request is sent out (needed for any/all ssl change)
|
||||
send_ssl_sync_request()
|
||||
|
||||
for r_id in relation_ids('ha'):
|
||||
ha_joined(relation_id=r_id)
|
||||
|
||||
|
||||
@synchronize_ca_if_changed(fatal=True)
|
||||
def initialise_pki():
|
||||
"""Create certs and keys required for token signing.
|
||||
|
||||
Used for PKI and signing token revocation list.
|
||||
|
||||
NOTE: keystone.conf [signing] section must be up-to-date prior to
|
||||
executing this.
|
||||
"""
|
||||
if CompareOpenStackReleases(os_release('keystone-common')) >= 'pike':
|
||||
# pike dropped support for PKI token; skip function
|
||||
return
|
||||
ensure_pki_cert_paths()
|
||||
if not peer_units() or is_ssl_cert_master():
|
||||
log("Ensuring PKI token certs created", level=DEBUG)
|
||||
if snap_install_requested():
|
||||
cmd = ['/snap/bin/keystone-manage', 'pki_setup',
|
||||
'--keystone-user', KEYSTONE_USER,
|
||||
'--keystone-group', KEYSTONE_USER]
|
||||
_log_dir = '/var/snap/keystone/common/log'
|
||||
else:
|
||||
cmd = ['keystone-manage', 'pki_setup',
|
||||
'--keystone-user', KEYSTONE_USER,
|
||||
'--keystone-group', KEYSTONE_USER]
|
||||
_log_dir = '/var/log/keystone'
|
||||
check_call(cmd)
|
||||
|
||||
# Ensure logfile has keystone perms since we may have just created it
|
||||
# with root.
|
||||
ensure_permissions(_log_dir, user=KEYSTONE_USER,
|
||||
group=KEYSTONE_USER, perms=0o744)
|
||||
ensure_permissions('{}/keystone.log'.format(_log_dir),
|
||||
user=KEYSTONE_USER, group=KEYSTONE_USER,
|
||||
perms=0o644)
|
||||
|
||||
ensure_pki_dir_permissions()
|
||||
|
||||
|
||||
@hooks.hook('shared-db-relation-joined')
|
||||
def db_joined():
|
||||
if config('prefer-ipv6'):
|
||||
|
@ -377,11 +291,6 @@ def update_all_identity_relation_units(check_db_ready=True):
|
|||
identity_credentials_changed(relation_id=rid, remote_unit=unit)
|
||||
|
||||
|
||||
@synchronize_ca_if_changed(force=True)
|
||||
def update_all_identity_relation_units_force_sync():
|
||||
update_all_identity_relation_units()
|
||||
|
||||
|
||||
def update_all_domain_backends():
|
||||
"""Re-trigger hooks for all domain-backend relations/units"""
|
||||
for rid in relation_ids('domain-backend'):
|
||||
|
@ -431,7 +340,6 @@ def leader_init_db_if_ready(use_current_context=False):
|
|||
|
||||
@hooks.hook('shared-db-relation-changed')
|
||||
@restart_on_change(restart_map(), restart_functions=restart_function_map())
|
||||
@synchronize_ca_if_changed()
|
||||
def db_changed():
|
||||
if 'shared-db' not in CONFIGS.complete_contexts():
|
||||
log('shared-db relation incomplete. Peer not ready?')
|
||||
|
@ -446,7 +354,6 @@ def db_changed():
|
|||
|
||||
@hooks.hook('identity-service-relation-changed')
|
||||
@restart_on_change(restart_map(), restart_functions=restart_function_map())
|
||||
@synchronize_ca_if_changed()
|
||||
def identity_changed(relation_id=None, remote_unit=None):
|
||||
CONFIGS.write_all()
|
||||
|
||||
|
@ -528,59 +435,8 @@ def identity_credentials_changed(relation_id=None, remote_unit=None):
|
|||
log('Deferring identity_credentials_changed() to service leader.')
|
||||
|
||||
|
||||
def send_ssl_sync_request():
|
||||
"""Set sync request on cluster relation.
|
||||
|
||||
Value set equals number of ssl configs currently enabled so that if they
|
||||
change, we ensure that certs are synced. This setting is consumed by
|
||||
cluster-relation-changed ssl master. We also clear the 'synced' set to
|
||||
guarantee that a sync will occur.
|
||||
|
||||
Note the we do nothing if the setting is already applied.
|
||||
"""
|
||||
unit = local_unit().replace('/', '-')
|
||||
# Start with core config (e.g. used for signing revoked token list)
|
||||
ssl_config = 0b1
|
||||
|
||||
use_https = config('use-https')
|
||||
if use_https and bool_from_string(use_https):
|
||||
ssl_config ^= 0b10
|
||||
|
||||
https_service_endpoints = config('https-service-endpoints')
|
||||
if (https_service_endpoints and
|
||||
bool_from_string(https_service_endpoints)):
|
||||
ssl_config ^= 0b100
|
||||
|
||||
enable_pki = config('enable-pki')
|
||||
if enable_pki and bool_from_string(enable_pki):
|
||||
ssl_config ^= 0b1000
|
||||
|
||||
key = 'ssl-sync-required-%s' % (unit)
|
||||
settings = {key: ssl_config}
|
||||
|
||||
prev = 0b0
|
||||
rid = None
|
||||
for rid in relation_ids('cluster'):
|
||||
for unit in related_units(rid):
|
||||
_prev = relation_get(rid=rid, unit=unit, attribute=key) or 0b0
|
||||
if _prev and _prev > prev:
|
||||
prev = bin(_prev)
|
||||
|
||||
if rid and prev ^ ssl_config:
|
||||
if is_leader():
|
||||
clear_ssl_synced_units()
|
||||
|
||||
log("Setting %s=%s" % (key, bin(ssl_config)), level=DEBUG)
|
||||
relation_set(relation_id=rid, relation_settings=settings)
|
||||
|
||||
|
||||
@hooks.hook('cluster-relation-joined')
|
||||
def cluster_joined(rid=None, ssl_sync_request=True):
|
||||
unison.ssh_authorized_peers(user=SSH_USER,
|
||||
group=SSH_USER,
|
||||
peer_interface='cluster',
|
||||
ensure_local_user=True)
|
||||
|
||||
def cluster_joined(rid=None):
|
||||
settings = {}
|
||||
|
||||
for addr_type in ADDRESS_TYPES:
|
||||
|
@ -594,57 +450,19 @@ def cluster_joined(rid=None, ssl_sync_request=True):
|
|||
|
||||
relation_set(relation_id=rid, relation_settings=settings)
|
||||
|
||||
if ssl_sync_request:
|
||||
send_ssl_sync_request()
|
||||
|
||||
|
||||
@hooks.hook('cluster-relation-changed')
|
||||
@restart_on_change(restart_map(), stopstart=True)
|
||||
@update_certs_if_available
|
||||
def cluster_changed():
|
||||
unison.ssh_authorized_peers(user=SSH_USER,
|
||||
group=SSH_USER,
|
||||
peer_interface='cluster',
|
||||
ensure_local_user=True)
|
||||
# NOTE(jamespage) re-echo passwords for peer storage
|
||||
echo_whitelist = ['_passwd', 'identity-service:',
|
||||
'db-initialised', 'ssl-cert-available-updates']
|
||||
# Don't echo if leader since a re-election may be in progress.
|
||||
if not is_leader():
|
||||
echo_whitelist.append('ssl-cert-master')
|
||||
echo_whitelist = ['_passwd', 'identity-service:', 'db-initialised']
|
||||
|
||||
log("Peer echo whitelist: %s" % (echo_whitelist), level=DEBUG)
|
||||
peer_echo(includes=echo_whitelist, force=True)
|
||||
|
||||
check_peer_actions()
|
||||
update_all_identity_relation_units()
|
||||
|
||||
initialise_pki()
|
||||
|
||||
if is_leader():
|
||||
# Figure out if we need to mandate a sync
|
||||
units = get_ssl_sync_request_units()
|
||||
synced_units = relation_get_and_migrate(attribute='ssl-synced-units',
|
||||
unit=local_unit())
|
||||
diff = None
|
||||
if synced_units:
|
||||
synced_units = json.loads(synced_units)
|
||||
diff = set(units).symmetric_difference(set(synced_units))
|
||||
else:
|
||||
units = None
|
||||
|
||||
if units and (not synced_units or diff):
|
||||
log("New peers joined and need syncing - %s" %
|
||||
(', '.join(units)), level=DEBUG)
|
||||
update_all_identity_relation_units_force_sync()
|
||||
else:
|
||||
update_all_identity_relation_units()
|
||||
|
||||
if not is_leader() and is_ssl_cert_master():
|
||||
# Force and sync and trigger a sync master re-election since we are not
|
||||
# leader anymore.
|
||||
force_ssl_sync()
|
||||
else:
|
||||
CONFIGS.write_all()
|
||||
CONFIGS.write_all()
|
||||
|
||||
|
||||
@hooks.hook('leader-elected')
|
||||
|
@ -739,7 +557,6 @@ def ha_joined(relation_id=None):
|
|||
|
||||
@hooks.hook('ha-relation-changed')
|
||||
@restart_on_change(restart_map(), restart_functions=restart_function_map())
|
||||
@synchronize_ca_if_changed()
|
||||
def ha_changed():
|
||||
CONFIGS.write_all()
|
||||
|
||||
|
@ -747,10 +564,7 @@ def ha_changed():
|
|||
if clustered:
|
||||
log('Cluster configured, notifying other services and updating '
|
||||
'keystone endpoint configuration')
|
||||
if is_ssl_cert_master():
|
||||
update_all_identity_relation_units_force_sync()
|
||||
else:
|
||||
update_all_identity_relation_units()
|
||||
update_all_identity_relation_units()
|
||||
|
||||
|
||||
@hooks.hook('identity-admin-relation-changed')
|
||||
|
@ -808,7 +622,6 @@ def domain_backend_changed(relation_id=None, unit=None):
|
|||
db.flush()
|
||||
|
||||
|
||||
@synchronize_ca_if_changed(fatal=True)
|
||||
def configure_https():
|
||||
'''
|
||||
Enables SSL API Apache config if appropriate and kicks identity-service
|
||||
|
@ -831,17 +644,10 @@ def configure_https():
|
|||
|
||||
@hooks.hook('upgrade-charm')
|
||||
@restart_on_change(restart_map(), stopstart=True)
|
||||
@synchronize_ca_if_changed()
|
||||
@harden()
|
||||
def upgrade_charm():
|
||||
status_set('maintenance', 'Installing apt packages')
|
||||
apt_install(filter_installed_packages(determine_packages()))
|
||||
unison.ssh_authorized_peers(user=SSH_USER,
|
||||
group=SSH_USER,
|
||||
peer_interface='cluster',
|
||||
ensure_local_user=True)
|
||||
|
||||
ensure_ssl_dirs()
|
||||
|
||||
if run_in_apache():
|
||||
disable_unused_apache_sites()
|
||||
|
|
|
@ -1,358 +0,0 @@
|
|||
#!/usr/bin/python
|
||||
#
|
||||
# Copyright 2016 Canonical Ltd
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import os
|
||||
import shutil
|
||||
import subprocess
|
||||
import tarfile
|
||||
import tempfile
|
||||
|
||||
from charmhelpers.core.hookenv import (
|
||||
log,
|
||||
DEBUG,
|
||||
)
|
||||
|
||||
CA_EXPIRY = '365'
|
||||
ORG_NAME = 'Ubuntu'
|
||||
ORG_UNIT = 'Ubuntu Cloud'
|
||||
CA_BUNDLE = '/usr/local/share/ca-certificates/juju_ca_cert.crt'
|
||||
|
||||
CA_CONFIG = """
|
||||
[ ca ]
|
||||
default_ca = CA_default
|
||||
|
||||
[ CA_default ]
|
||||
dir = %(ca_dir)s
|
||||
policy = policy_match
|
||||
database = $dir/index.txt
|
||||
serial = $dir/serial
|
||||
certs = $dir/certs
|
||||
crl_dir = $dir/crl
|
||||
new_certs_dir = $dir/newcerts
|
||||
certificate = $dir/cacert.pem
|
||||
private_key = $dir/private/cacert.key
|
||||
RANDFILE = $dir/private/.rand
|
||||
default_md = default
|
||||
|
||||
[ req ]
|
||||
default_bits = 1024
|
||||
default_md = sha1
|
||||
|
||||
prompt = no
|
||||
distinguished_name = ca_distinguished_name
|
||||
|
||||
x509_extensions = ca_extensions
|
||||
|
||||
[ ca_distinguished_name ]
|
||||
organizationName = %(org_name)s
|
||||
organizationalUnitName = %(org_unit_name)s Certificate Authority
|
||||
commonName = %(common_name)s
|
||||
|
||||
[ policy_match ]
|
||||
countryName = optional
|
||||
stateOrProvinceName = optional
|
||||
organizationName = match
|
||||
organizationalUnitName = optional
|
||||
commonName = supplied
|
||||
|
||||
[ ca_extensions ]
|
||||
basicConstraints = critical,CA:true
|
||||
subjectKeyIdentifier = hash
|
||||
authorityKeyIdentifier = keyid:always, issuer
|
||||
keyUsage = cRLSign, keyCertSign
|
||||
"""
|
||||
|
||||
SIGNING_CONFIG = """
|
||||
[ ca ]
|
||||
default_ca = CA_default
|
||||
|
||||
[ CA_default ]
|
||||
dir = %(ca_dir)s
|
||||
policy = policy_match
|
||||
database = $dir/index.txt
|
||||
serial = $dir/serial
|
||||
certs = $dir/certs
|
||||
crl_dir = $dir/crl
|
||||
new_certs_dir = $dir/newcerts
|
||||
certificate = $dir/cacert.pem
|
||||
private_key = $dir/private/cacert.key
|
||||
RANDFILE = $dir/private/.rand
|
||||
default_md = default
|
||||
|
||||
[ req ]
|
||||
default_bits = 1024
|
||||
default_md = sha1
|
||||
|
||||
prompt = no
|
||||
distinguished_name = req_distinguished_name
|
||||
|
||||
x509_extensions = req_extensions
|
||||
|
||||
[ req_distinguished_name ]
|
||||
organizationName = %(org_name)s
|
||||
organizationalUnitName = %(org_unit_name)s Server Farm
|
||||
|
||||
[ policy_match ]
|
||||
countryName = optional
|
||||
stateOrProvinceName = optional
|
||||
organizationName = match
|
||||
organizationalUnitName = optional
|
||||
commonName = supplied
|
||||
|
||||
[ req_extensions ]
|
||||
basicConstraints = CA:false
|
||||
subjectKeyIdentifier = hash
|
||||
authorityKeyIdentifier = keyid:always, issuer
|
||||
keyUsage = digitalSignature, keyEncipherment, keyAgreement
|
||||
extendedKeyUsage = serverAuth, clientAuth
|
||||
"""
|
||||
|
||||
# Instance can be appended to this list to represent a singleton
|
||||
CA_SINGLETON = []
|
||||
|
||||
|
||||
def init_ca(ca_dir, common_name, org_name=ORG_NAME, org_unit_name=ORG_UNIT):
|
||||
log('Ensuring certificate authority exists at %s.' % ca_dir, level=DEBUG)
|
||||
if not os.path.exists(ca_dir):
|
||||
log('Initializing new certificate authority at %s' % ca_dir,
|
||||
level=DEBUG)
|
||||
os.mkdir(ca_dir)
|
||||
|
||||
for i in ['certs', 'crl', 'newcerts', 'private']:
|
||||
d = os.path.join(ca_dir, i)
|
||||
if not os.path.exists(d):
|
||||
log('Creating %s.' % d, level=DEBUG)
|
||||
os.mkdir(d)
|
||||
os.chmod(os.path.join(ca_dir, 'private'), 0o710)
|
||||
|
||||
if not os.path.isfile(os.path.join(ca_dir, 'serial')):
|
||||
with open(os.path.join(ca_dir, 'serial'), 'wb') as out:
|
||||
out.write('01\n')
|
||||
|
||||
if not os.path.isfile(os.path.join(ca_dir, 'index.txt')):
|
||||
with open(os.path.join(ca_dir, 'index.txt'), 'wb') as out:
|
||||
out.write('')
|
||||
|
||||
conf = os.path.join(ca_dir, 'ca.cnf')
|
||||
if not os.path.isfile(conf):
|
||||
log('Creating new CA config in %s' % ca_dir, level=DEBUG)
|
||||
with open(conf, 'wb') as out:
|
||||
out.write(CA_CONFIG % locals())
|
||||
|
||||
|
||||
def root_ca_crt_key(ca_dir):
|
||||
init = False
|
||||
crt = os.path.join(ca_dir, 'cacert.pem')
|
||||
key = os.path.join(ca_dir, 'private', 'cacert.key')
|
||||
for f in [crt, key]:
|
||||
if not os.path.isfile(f):
|
||||
log('Missing %s, will re-initialize cert+key.' % f, level=DEBUG)
|
||||
init = True
|
||||
else:
|
||||
log('Found %s.' % f, level=DEBUG)
|
||||
|
||||
if init:
|
||||
conf = os.path.join(ca_dir, 'ca.cnf')
|
||||
cmd = ['openssl', 'req', '-config', conf,
|
||||
'-x509', '-nodes', '-newkey', 'rsa', '-days', '21360',
|
||||
'-keyout', key, '-out', crt, '-outform', 'PEM']
|
||||
subprocess.check_call(cmd)
|
||||
|
||||
return crt, key
|
||||
|
||||
|
||||
def intermediate_ca_csr_key(ca_dir):
|
||||
log('Creating new intermediate CSR.', level=DEBUG)
|
||||
key = os.path.join(ca_dir, 'private', 'cacert.key')
|
||||
csr = os.path.join(ca_dir, 'cacert.csr')
|
||||
conf = os.path.join(ca_dir, 'ca.cnf')
|
||||
cmd = ['openssl', 'req', '-config', conf, '-sha1', '-newkey', 'rsa',
|
||||
'-nodes', '-keyout', key, '-out', csr, '-outform', 'PEM']
|
||||
subprocess.check_call(cmd)
|
||||
return csr, key
|
||||
|
||||
|
||||
def sign_int_csr(ca_dir, csr, common_name):
|
||||
log('Signing certificate request %s.' % csr, level=DEBUG)
|
||||
crt_name = os.path.basename(csr).split('.')[0]
|
||||
crt = os.path.join(ca_dir, 'certs', '%s.crt' % crt_name)
|
||||
subj = '/O=%s/OU=%s/CN=%s' % (ORG_NAME, ORG_UNIT, common_name)
|
||||
conf = os.path.join(ca_dir, 'ca.cnf')
|
||||
cmd = ['openssl', 'ca', '-batch', '-config', conf, '-extensions',
|
||||
'ca_extensions', '-days', CA_EXPIRY, '-notext', '-in', csr, '-out',
|
||||
crt, '-subj', subj, '-batch']
|
||||
log("Executing: %s" % ' '.join(cmd), level=DEBUG)
|
||||
subprocess.check_call(cmd)
|
||||
return crt
|
||||
|
||||
|
||||
def init_root_ca(ca_dir, common_name):
|
||||
init_ca(ca_dir, common_name)
|
||||
return root_ca_crt_key(ca_dir)
|
||||
|
||||
|
||||
def init_intermediate_ca(ca_dir, common_name, root_ca_dir, org_name=ORG_NAME,
|
||||
org_unit_name=ORG_UNIT):
|
||||
init_ca(ca_dir, common_name)
|
||||
if not os.path.isfile(os.path.join(ca_dir, 'cacert.pem')):
|
||||
csr, key = intermediate_ca_csr_key(ca_dir)
|
||||
crt = sign_int_csr(root_ca_dir, csr, common_name)
|
||||
shutil.copy(crt, os.path.join(ca_dir, 'cacert.pem'))
|
||||
else:
|
||||
log('Intermediate CA certificate already exists.', level=DEBUG)
|
||||
|
||||
conf = os.path.join(ca_dir, 'signing.cnf')
|
||||
if not os.path.isfile(conf):
|
||||
log('Creating new signing config in %s' % ca_dir, level=DEBUG)
|
||||
with open(conf, 'wb') as out:
|
||||
out.write(SIGNING_CONFIG % locals())
|
||||
|
||||
|
||||
def create_certificate(ca_dir, service):
|
||||
common_name = service
|
||||
subj = '/O=%s/OU=%s/CN=%s' % (ORG_NAME, ORG_UNIT, common_name)
|
||||
csr = os.path.join(ca_dir, 'certs', '%s.csr' % service)
|
||||
key = os.path.join(ca_dir, 'certs', '%s.key' % service)
|
||||
cmd = ['openssl', 'req', '-sha1', '-newkey', 'rsa', '-nodes', '-keyout',
|
||||
key, '-out', csr, '-subj', subj]
|
||||
subprocess.check_call(cmd)
|
||||
crt = sign_int_csr(ca_dir, csr, common_name)
|
||||
log('Signed new CSR, crt @ %s' % crt, level=DEBUG)
|
||||
return
|
||||
|
||||
|
||||
def update_bundle(bundle_file, new_bundle):
|
||||
return
|
||||
if os.path.isfile(bundle_file):
|
||||
with open(bundle_file, 'r') as f:
|
||||
current = f.read().strip()
|
||||
if new_bundle == current:
|
||||
log('CA Bundle @ %s is up to date.' % bundle_file, level=DEBUG)
|
||||
return
|
||||
|
||||
log('Updating CA bundle @ %s.' % bundle_file, level=DEBUG)
|
||||
|
||||
with open(bundle_file, 'wb') as out:
|
||||
out.write(new_bundle)
|
||||
|
||||
subprocess.check_call(['update-ca-certificates'])
|
||||
|
||||
|
||||
def tar_directory(path):
|
||||
cwd = os.getcwd()
|
||||
parent = os.path.dirname(path)
|
||||
directory = os.path.basename(path)
|
||||
tmp = tempfile.TemporaryFile()
|
||||
os.chdir(parent)
|
||||
tarball = tarfile.TarFile(fileobj=tmp, mode='w')
|
||||
tarball.add(directory)
|
||||
tarball.close()
|
||||
tmp.seek(0)
|
||||
out = tmp.read()
|
||||
tmp.close()
|
||||
os.chdir(cwd)
|
||||
return out
|
||||
|
||||
|
||||
class JujuCA(object):
|
||||
|
||||
def __init__(self, name, ca_dir, root_ca_dir, user, group):
|
||||
# Root CA
|
||||
cn = '%s Certificate Authority' % name
|
||||
root_crt, root_key = init_root_ca(root_ca_dir, cn)
|
||||
# Intermediate CA
|
||||
cn = '%s Intermediate Certificate Authority' % name
|
||||
init_intermediate_ca(ca_dir, cn, root_ca_dir)
|
||||
|
||||
# Create dirs
|
||||
cmd = ['chown', '-R', '%s.%s' % (user, group), ca_dir]
|
||||
subprocess.check_call(cmd)
|
||||
cmd = ['chown', '-R', '%s.%s' % (user, group), root_ca_dir]
|
||||
subprocess.check_call(cmd)
|
||||
|
||||
self.ca_dir = ca_dir
|
||||
self.root_ca_dir = root_ca_dir
|
||||
self.user = user
|
||||
self.group = group
|
||||
update_bundle(CA_BUNDLE, self.get_ca_bundle())
|
||||
|
||||
def _sign_csr(self, csr, service, common_name):
|
||||
subj = '/O=%s/OU=%s/CN=%s' % (ORG_NAME, ORG_UNIT, common_name)
|
||||
crt = os.path.join(self.ca_dir, 'certs', '%s.crt' % common_name)
|
||||
conf = os.path.join(self.ca_dir, 'signing.cnf')
|
||||
cmd = ['openssl', 'ca', '-config', conf, '-extensions',
|
||||
'req_extensions', '-days', '365', '-notext', '-in', csr,
|
||||
'-out', crt, '-batch', '-subj', subj]
|
||||
subprocess.check_call(cmd)
|
||||
return crt
|
||||
|
||||
def _create_certificate(self, service, common_name):
|
||||
subj = '/O=%s/OU=%s/CN=%s' % (ORG_NAME, ORG_UNIT, common_name)
|
||||
csr = os.path.join(self.ca_dir, 'certs', '%s.csr' % service)
|
||||
key = os.path.join(self.ca_dir, 'certs', '%s.key' % service)
|
||||
cmd = ['openssl', 'req', '-sha1', '-newkey', 'rsa', '-nodes',
|
||||
'-keyout', key, '-out', csr, '-subj', subj]
|
||||
subprocess.check_call(cmd)
|
||||
crt = self._sign_csr(csr, service, common_name)
|
||||
cmd = ['chown', '-R', '%s.%s' % (self.user, self.group), self.ca_dir]
|
||||
subprocess.check_call(cmd)
|
||||
log('Signed new CSR, crt @ %s' % crt, level=DEBUG)
|
||||
return crt, key
|
||||
|
||||
def get_key_path(self, cn):
|
||||
return os.path.join(self.ca_dir, 'certs', '%s.key' % cn)
|
||||
|
||||
def get_cert_path(self, cn):
|
||||
return os.path.join(self.ca_dir, 'certs', '%s.crt' % cn)
|
||||
|
||||
def get_cert_and_key(self, common_name):
|
||||
keypath = self.get_key_path(common_name)
|
||||
crtpath = self.get_cert_path(common_name)
|
||||
if not os.path.isfile(crtpath):
|
||||
log("Creating certificate and key for {}.".format(common_name),
|
||||
level=DEBUG)
|
||||
crtpath, keypath = self._create_certificate(common_name,
|
||||
common_name)
|
||||
|
||||
with open(crtpath, 'r') as f:
|
||||
crt = f.read()
|
||||
with open(keypath, 'r') as f:
|
||||
key = f.read()
|
||||
return crt, key
|
||||
|
||||
@property
|
||||
def ca_cert_path(self):
|
||||
return os.path.join(self.ca_dir, 'cacert.pem')
|
||||
|
||||
@property
|
||||
def ca_key_path(self):
|
||||
return os.path.join(self.ca_dir, 'private', 'cacert.key')
|
||||
|
||||
@property
|
||||
def root_ca_cert_path(self):
|
||||
return os.path.join(self.root_ca_dir, 'cacert.pem')
|
||||
|
||||
@property
|
||||
def root_ca_key_path(self):
|
||||
return os.path.join(self.root_ca_dir, 'private', 'cacert.key')
|
||||
|
||||
def get_ca_bundle(self):
|
||||
with open(self.ca_cert_path) as f:
|
||||
int_cert = f.read()
|
||||
with open(self.root_ca_cert_path) as f:
|
||||
root_cert = f.read()
|
||||
# NOTE: ordering of certs in bundle matters!
|
||||
return int_cert + root_cert
|
|
@ -14,24 +14,15 @@
|
|||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import glob
|
||||
import grp
|
||||
import hashlib
|
||||
import json
|
||||
import os
|
||||
import pwd
|
||||
import re
|
||||
import shutil
|
||||
import subprocess
|
||||
import tarfile
|
||||
import threading
|
||||
import time
|
||||
import urlparse
|
||||
import uuid
|
||||
import sys
|
||||
|
||||
from itertools import chain
|
||||
from base64 import b64encode
|
||||
from collections import OrderedDict
|
||||
from copy import deepcopy
|
||||
|
||||
|
@ -39,7 +30,6 @@ from charmhelpers.contrib.hahelpers.cluster import (
|
|||
is_elected_leader,
|
||||
determine_api_port,
|
||||
https,
|
||||
peer_units,
|
||||
get_hacluster_config,
|
||||
)
|
||||
|
||||
|
@ -79,8 +69,6 @@ from charmhelpers.core.strutils import (
|
|||
bool_from_string,
|
||||
)
|
||||
|
||||
import charmhelpers.contrib.unison as unison
|
||||
|
||||
from charmhelpers.core.decorators import (
|
||||
retry_on_exception,
|
||||
)
|
||||
|
@ -98,9 +86,6 @@ from charmhelpers.core.hookenv import (
|
|||
related_units,
|
||||
DEBUG,
|
||||
INFO,
|
||||
WARNING,
|
||||
ERROR,
|
||||
is_leader,
|
||||
)
|
||||
|
||||
from charmhelpers.fetch import (
|
||||
|
@ -111,13 +96,11 @@ from charmhelpers.fetch import (
|
|||
)
|
||||
|
||||
from charmhelpers.core.host import (
|
||||
mkdir,
|
||||
service_restart,
|
||||
service_stop,
|
||||
service_start,
|
||||
service_restart,
|
||||
pwgen,
|
||||
lsb_release,
|
||||
write_file,
|
||||
CompareHostReleases,
|
||||
)
|
||||
|
||||
|
@ -125,11 +108,9 @@ from charmhelpers.contrib.peerstorage import (
|
|||
peer_store_and_set,
|
||||
peer_store,
|
||||
peer_retrieve,
|
||||
relation_set as relation_set_and_migrate_to_leader,
|
||||
)
|
||||
|
||||
import keystone_context
|
||||
import keystone_ssl as ssl
|
||||
|
||||
|
||||
TEMPLATES = 'templates/'
|
||||
|
@ -145,7 +126,6 @@ BASE_PACKAGES = [
|
|||
'python-psycopg2',
|
||||
'python-six',
|
||||
'pwgen',
|
||||
'unison',
|
||||
'uuid',
|
||||
]
|
||||
|
||||
|
@ -154,13 +134,11 @@ BASE_PACKAGES_SNAP = [
|
|||
'openssl',
|
||||
'python-six',
|
||||
'pwgen',
|
||||
'unison',
|
||||
'uuid',
|
||||
]
|
||||
|
||||
VERSION_PACKAGE = 'keystone'
|
||||
|
||||
SSH_USER = 'juju_keystone'
|
||||
if snap_install_requested():
|
||||
SNAP_BASE_DIR = "/snap/keystone/current"
|
||||
SNAP_COMMON_DIR = "/var/snap/keystone/common"
|
||||
|
@ -182,17 +160,9 @@ if snap_install_requested():
|
|||
STORED_DEFAULT_DOMAIN_ID = ("{}/keystone.default_domain_id"
|
||||
"".format(SNAP_LIB_DIR))
|
||||
SERVICE_PASSWD_PATH = '{}/services.passwd'.format(SNAP_LIB_DIR)
|
||||
|
||||
SSH_USER_HOME = '/home/{}'.format(SSH_USER)
|
||||
SYNC_FLAGS_DIR = '{}/juju_sync_flags/'.format(SSH_USER_HOME)
|
||||
SYNC_DIR = '{}/juju_sync/'.format(SSH_USER_HOME)
|
||||
SSL_SYNC_ARCHIVE = os.path.join(SYNC_DIR, 'juju-ssl-sync.tar')
|
||||
SSL_DIR = '{}/juju_ssl/'.format(SNAP_LIB_DIR)
|
||||
PKI_CERTS_DIR = os.path.join(SSL_DIR, 'pki')
|
||||
POLICY_JSON = ('{}/keystone.conf.d/policy.json'
|
||||
''.format(SNAP_COMMON_KEYSTONE_DIR))
|
||||
BASE_SERVICES = ['snap.keystone.uwsgi', 'snap.keystone.nginx']
|
||||
APACHE_SSL_DIR = '{}/keystone'.format(SSL_DIR)
|
||||
else:
|
||||
APACHE_SSL_DIR = '/etc/apache2/ssl/keystone'
|
||||
KEYSTONE_USER = 'keystone'
|
||||
|
@ -206,12 +176,6 @@ else:
|
|||
STORED_ADMIN_DOMAIN_ID = "/var/lib/keystone/keystone.admin_domain_id"
|
||||
STORED_DEFAULT_DOMAIN_ID = "/var/lib/keystone/keystone.default_domain_id"
|
||||
SERVICE_PASSWD_PATH = '/var/lib/keystone/services.passwd'
|
||||
|
||||
SYNC_FLAGS_DIR = '/var/lib/keystone/juju_sync_flags/'
|
||||
SYNC_DIR = '/var/lib/keystone/juju_sync/'
|
||||
SSL_SYNC_ARCHIVE = os.path.join(SYNC_DIR, 'juju-ssl-sync.tar')
|
||||
SSL_DIR = '/var/lib/keystone/juju_ssl/'
|
||||
PKI_CERTS_DIR = os.path.join(SSL_DIR, 'pki')
|
||||
POLICY_JSON = '/etc/keystone/policy.json'
|
||||
BASE_SERVICES = [
|
||||
'keystone',
|
||||
|
@ -223,11 +187,7 @@ APACHE_CONF = '/etc/apache2/sites-available/openstack_https_frontend'
|
|||
APACHE_24_CONF = '/etc/apache2/sites-available/openstack_https_frontend.conf'
|
||||
MEMCACHED_CONF = '/etc/memcached.conf'
|
||||
|
||||
SSL_CA_NAME = 'Ubuntu Cloud'
|
||||
CLUSTER_RES = 'grp_ks_vips'
|
||||
CA_CERT_PATH = '/usr/local/share/ca-certificates/keystone_juju_ca_cert.crt'
|
||||
SSL_SYNC_SEMAPHORE = threading.Semaphore()
|
||||
SSL_DIRS = [SSL_DIR, APACHE_SSL_DIR, CA_CERT_PATH]
|
||||
ADMIN_DOMAIN = 'admin_domain'
|
||||
ADMIN_PROJECT = 'admin'
|
||||
DEFAULT_DOMAIN = 'default'
|
||||
|
@ -1311,594 +1271,6 @@ def is_password_changed(username, passwd):
|
|||
return (_passwd is None or passwd != _passwd)
|
||||
|
||||
|
||||
def ensure_ssl_dirs():
|
||||
"""Ensure unison has access to these dirs."""
|
||||
for path in [SYNC_FLAGS_DIR, SYNC_DIR]:
|
||||
if not os.path.isdir(path):
|
||||
mkdir(path, SSH_USER, KEYSTONE_USER, 0o775)
|
||||
else:
|
||||
ensure_permissions(path, user=SSH_USER, group=KEYSTONE_USER,
|
||||
perms=0o775)
|
||||
|
||||
|
||||
def ensure_permissions(path, user=None, group=None, perms=None, recurse=False,
|
||||
maxdepth=50):
|
||||
"""Set chownand chmod for path
|
||||
|
||||
Note that -1 for uid or gid result in no change.
|
||||
"""
|
||||
if user:
|
||||
uid = pwd.getpwnam(user).pw_uid
|
||||
else:
|
||||
uid = -1
|
||||
|
||||
if group:
|
||||
gid = grp.getgrnam(group).gr_gid
|
||||
else:
|
||||
gid = -1
|
||||
|
||||
os.chown(path, uid, gid)
|
||||
|
||||
if perms:
|
||||
os.chmod(path, perms)
|
||||
|
||||
if recurse:
|
||||
if not maxdepth:
|
||||
log("Max recursion depth reached - skipping further recursion")
|
||||
return
|
||||
|
||||
paths = glob.glob("%s/*" % (path))
|
||||
for path in paths:
|
||||
ensure_permissions(path, user=user, group=group, perms=perms,
|
||||
recurse=recurse, maxdepth=maxdepth - 1)
|
||||
|
||||
|
||||
def check_peer_actions():
|
||||
"""Honour service action requests from sync master.
|
||||
|
||||
Check for service action request flags, perform the action then delete the
|
||||
flag.
|
||||
"""
|
||||
restart = relation_get(attribute='restart-services-trigger')
|
||||
if restart and os.path.isdir(SYNC_FLAGS_DIR):
|
||||
for flagfile in glob.glob(os.path.join(SYNC_FLAGS_DIR, '*')):
|
||||
flag = os.path.basename(flagfile)
|
||||
key = re.compile("^(.+)?\.(.+)?\.(.+)")
|
||||
res = re.search(key, flag)
|
||||
if res:
|
||||
source = res.group(1)
|
||||
service = res.group(2)
|
||||
action = res.group(3)
|
||||
else:
|
||||
key = re.compile("^(.+)?\.(.+)?")
|
||||
res = re.search(key, flag)
|
||||
source = res.group(1)
|
||||
action = res.group(2)
|
||||
|
||||
# Don't execute actions requested by this unit.
|
||||
if local_unit().replace('.', '-') != source:
|
||||
if action == 'restart':
|
||||
log("Running action='%s' on service '%s'" %
|
||||
(action, service), level=DEBUG)
|
||||
service_restart(service)
|
||||
elif action == 'start':
|
||||
log("Running action='%s' on service '%s'" %
|
||||
(action, service), level=DEBUG)
|
||||
service_start(service)
|
||||
elif action == 'stop':
|
||||
log("Running action='%s' on service '%s'" %
|
||||
(action, service), level=DEBUG)
|
||||
service_stop(service)
|
||||
elif action == 'update-ca-certificates':
|
||||
log("Running %s" % (action), level=DEBUG)
|
||||
subprocess.check_call(['update-ca-certificates'])
|
||||
elif action == 'ensure-pki-permissions':
|
||||
log("Running %s" % (action), level=DEBUG)
|
||||
ensure_pki_dir_permissions()
|
||||
else:
|
||||
log("Unknown action flag=%s" % (flag), level=WARNING)
|
||||
|
||||
try:
|
||||
os.remove(flagfile)
|
||||
except:
|
||||
pass
|
||||
|
||||
|
||||
def create_peer_service_actions(action, services):
|
||||
"""Mark remote services for action.
|
||||
|
||||
Default action is restart. These action will be picked up by peer units
|
||||
e.g. we may need to restart services on peer units after certs have been
|
||||
synced.
|
||||
"""
|
||||
for service in services:
|
||||
flagfile = os.path.join(SYNC_FLAGS_DIR, '%s.%s.%s' %
|
||||
(local_unit().replace('/', '-'),
|
||||
service.strip(), action))
|
||||
log("Creating action %s" % (flagfile), level=DEBUG)
|
||||
write_file(flagfile, content='', owner=SSH_USER, group=KEYSTONE_USER,
|
||||
perms=0o744)
|
||||
|
||||
|
||||
def create_peer_actions(actions):
|
||||
for action in actions:
|
||||
action = "%s.%s" % (local_unit().replace('/', '-'), action)
|
||||
flagfile = os.path.join(SYNC_FLAGS_DIR, action)
|
||||
log("Creating action %s" % (flagfile), level=DEBUG)
|
||||
write_file(flagfile, content='', owner=SSH_USER, group=KEYSTONE_USER,
|
||||
perms=0o744)
|
||||
|
||||
|
||||
@retry_on_exception(3, base_delay=2, exc_type=subprocess.CalledProcessError)
|
||||
def unison_sync(paths_to_sync):
|
||||
"""Do unison sync and retry a few times if it fails since peers may not be
|
||||
ready for sync.
|
||||
|
||||
Returns list of synced units or None if one or more peers was not synced.
|
||||
"""
|
||||
log('Synchronizing CA (%s) to all peers.' % (', '.join(paths_to_sync)),
|
||||
level=INFO)
|
||||
keystone_gid = grp.getgrnam(KEYSTONE_USER).gr_gid
|
||||
|
||||
# NOTE(dosaboy): This will sync to all peers who have already provided
|
||||
# their ssh keys. If any existing peers have not provided their keys yet,
|
||||
# they will be silently ignored.
|
||||
unison.sync_to_peers(peer_interface='cluster', paths=paths_to_sync,
|
||||
user=SSH_USER, verbose=True, gid=keystone_gid,
|
||||
fatal=True)
|
||||
|
||||
synced_units = peer_units()
|
||||
if len(unison.collect_authed_hosts('cluster')) != len(synced_units):
|
||||
log("Not all peer units synced due to missing public keys", level=INFO)
|
||||
return None
|
||||
else:
|
||||
return synced_units
|
||||
|
||||
|
||||
def get_ssl_sync_request_units():
|
||||
"""Get list of units that have requested to be synced.
|
||||
|
||||
NOTE: this must be called from cluster relation context.
|
||||
"""
|
||||
units = []
|
||||
for unit in related_units():
|
||||
settings = relation_get(unit=unit) or {}
|
||||
rkeys = settings.keys()
|
||||
key = re.compile("^ssl-sync-required-(.+)")
|
||||
for rkey in rkeys:
|
||||
res = re.search(key, rkey)
|
||||
if res:
|
||||
units.append(res.group(1))
|
||||
|
||||
return units
|
||||
|
||||
|
||||
def is_ssl_cert_master(votes=None):
|
||||
"""Return True if this unit is ssl cert master."""
|
||||
|
||||
votes = votes or get_ssl_cert_master_votes()
|
||||
set_votes = set(votes)
|
||||
# Discard unknown votes
|
||||
if 'unknown' in set_votes:
|
||||
set_votes.remove('unknown')
|
||||
|
||||
# This is the elected ssl-cert-master leader
|
||||
if len(set_votes) == 1 and set_votes == set([local_unit()]):
|
||||
log("This unit is the elected ssl-cert-master "
|
||||
"{}".format(votes), level=DEBUG)
|
||||
return True
|
||||
|
||||
# Contested election
|
||||
if len(set_votes) > 1:
|
||||
log("Did not get consensus from peers on who is ssl-cert-master "
|
||||
"{}".format(votes), level=DEBUG)
|
||||
return False
|
||||
|
||||
# Neither the elected ssl-cert-master leader nor the juju leader
|
||||
if not is_leader():
|
||||
return False
|
||||
# Only the juju elected leader continues
|
||||
|
||||
# Singleton
|
||||
if not peer_units():
|
||||
log("This unit is a singleton and thefore ssl-cert-master",
|
||||
level=DEBUG)
|
||||
return True
|
||||
|
||||
# Early in the process and juju leader
|
||||
if not set_votes:
|
||||
log("This unit is the juju leader and there are no votes yet, "
|
||||
"becoming the ssl-cert-master",
|
||||
level=DEBUG)
|
||||
return True
|
||||
elif (len(set_votes) == 1 and set_votes != set([local_unit()]) and
|
||||
is_leader()):
|
||||
log("This unit is the juju leader but not yet ssl-cert-master "
|
||||
"(current votes = {})".format(set_votes), level=DEBUG)
|
||||
return False
|
||||
|
||||
# Should never reach here
|
||||
log("Could not determine the ssl-cert-master. Missing edge case. "
|
||||
"(current votes = {})".format(set_votes),
|
||||
level=ERROR)
|
||||
return False
|
||||
|
||||
|
||||
def get_ssl_cert_master_votes():
|
||||
"""Returns a list of unique votes."""
|
||||
votes = []
|
||||
# Gather election results from peers. These will need to be consistent.
|
||||
for rid in relation_ids('cluster'):
|
||||
for unit in related_units(rid):
|
||||
m = relation_get(rid=rid, unit=unit,
|
||||
attribute='ssl-cert-master')
|
||||
if m is not None:
|
||||
votes.append(m)
|
||||
|
||||
return list(set(votes))
|
||||
|
||||
|
||||
def ensure_ssl_cert_master():
|
||||
"""Ensure that an ssl cert master has been elected.
|
||||
|
||||
Normally the cluster leader will take control but we allow for this to be
|
||||
ignored since this could be called before the cluster is ready.
|
||||
"""
|
||||
master_override = False
|
||||
elect = is_elected_leader(CLUSTER_RES)
|
||||
|
||||
# If no peers we allow this unit to elect itsef as master and do
|
||||
# sync immediately.
|
||||
if not peer_units():
|
||||
elect = True
|
||||
master_override = True
|
||||
|
||||
if elect:
|
||||
votes = get_ssl_cert_master_votes()
|
||||
# We expect all peers to echo this setting
|
||||
if not votes or 'unknown' in votes:
|
||||
log("Notifying peers this unit is ssl-cert-master", level=INFO)
|
||||
for rid in relation_ids('cluster'):
|
||||
settings = {'ssl-cert-master': local_unit()}
|
||||
relation_set(relation_id=rid, relation_settings=settings)
|
||||
|
||||
# Return now and wait for cluster-relation-changed (peer_echo) for
|
||||
# sync.
|
||||
return master_override
|
||||
elif not is_ssl_cert_master(votes):
|
||||
if not master_override:
|
||||
log("Conscensus not reached - current master will need to "
|
||||
"release", level=INFO)
|
||||
|
||||
return master_override
|
||||
|
||||
if not is_ssl_cert_master():
|
||||
log("Not ssl cert master - skipping sync", level=INFO)
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def stage_paths_for_sync(paths):
|
||||
shutil.rmtree(SYNC_DIR)
|
||||
ensure_ssl_dirs()
|
||||
with tarfile.open(SSL_SYNC_ARCHIVE, 'w') as fd:
|
||||
for path in paths:
|
||||
if os.path.exists(path):
|
||||
log("Adding path '%s' sync tarball" % (path), level=DEBUG)
|
||||
fd.add(path)
|
||||
else:
|
||||
log("Path '%s' does not exist - not adding to sync "
|
||||
"tarball" % (path), level=INFO)
|
||||
|
||||
ensure_permissions(SYNC_DIR, user=SSH_USER, group=KEYSTONE_USER,
|
||||
perms=0o775, recurse=True)
|
||||
|
||||
|
||||
def is_pki_enabled():
|
||||
enable_pki = config('enable-pki')
|
||||
if enable_pki and bool_from_string(enable_pki):
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def ensure_pki_cert_paths():
|
||||
certs = os.path.join(PKI_CERTS_DIR, 'certs')
|
||||
privates = os.path.join(PKI_CERTS_DIR, 'privates')
|
||||
not_exists = [p for p in [PKI_CERTS_DIR, certs, privates]
|
||||
if not os.path.exists(p)]
|
||||
if not_exists:
|
||||
log("Configuring token signing cert paths", level=DEBUG)
|
||||
perms = 0o775
|
||||
for path in not_exists:
|
||||
if not os.path.isdir(path):
|
||||
mkdir(path=path, owner=SSH_USER, group=KEYSTONE_USER,
|
||||
perms=perms)
|
||||
else:
|
||||
# Ensure accessible by ssh user and group (for sync).
|
||||
ensure_permissions(path, user=SSH_USER, group=KEYSTONE_USER,
|
||||
perms=perms)
|
||||
|
||||
|
||||
def ensure_pki_dir_permissions():
|
||||
# Ensure accessible by unison user and group (for sync).
|
||||
ensure_permissions(PKI_CERTS_DIR, user=SSH_USER, group=KEYSTONE_USER,
|
||||
perms=0o775, recurse=True)
|
||||
|
||||
|
||||
def update_certs_if_available(f):
|
||||
def _inner_update_certs_if_available(*args, **kwargs):
|
||||
path = None
|
||||
for rid in relation_ids('cluster'):
|
||||
path = relation_get(attribute='ssl-cert-available-updates',
|
||||
rid=rid, unit=local_unit())
|
||||
|
||||
if path and os.path.exists(path):
|
||||
log("Updating certs from '%s'" % (path), level=DEBUG)
|
||||
with tarfile.open(path) as fd:
|
||||
files = ["/%s" % m.name for m in fd.getmembers()]
|
||||
fd.extractall(path='/')
|
||||
|
||||
for syncfile in files:
|
||||
ensure_permissions(syncfile, user=KEYSTONE_USER,
|
||||
group=KEYSTONE_USER,
|
||||
perms=0o744, recurse=True)
|
||||
|
||||
# Mark as complete
|
||||
os.rename(path, "%s.complete" % (path))
|
||||
else:
|
||||
log("No cert updates available", level=DEBUG)
|
||||
|
||||
return f(*args, **kwargs)
|
||||
|
||||
return _inner_update_certs_if_available
|
||||
|
||||
|
||||
def synchronize_ca(fatal=False):
|
||||
"""Broadcast service credentials to peers.
|
||||
|
||||
By default a failure to sync is fatal and will result in a raised
|
||||
exception.
|
||||
|
||||
This function uses a relation setting 'ssl-cert-master' to get some
|
||||
leader stickiness while synchronisation is being carried out. This ensures
|
||||
that the last host to create and broadcast cetificates has the option to
|
||||
complete actions before electing the new leader as sync master.
|
||||
|
||||
Returns a dictionary of settings to be set on the cluster relation.
|
||||
"""
|
||||
paths_to_sync = []
|
||||
peer_service_actions = {'restart': []}
|
||||
peer_actions = []
|
||||
|
||||
if bool_from_string(config('https-service-endpoints')):
|
||||
log("Syncing all endpoint certs since https-service-endpoints=True",
|
||||
level=DEBUG)
|
||||
paths_to_sync.append(SSL_DIR)
|
||||
paths_to_sync.append(CA_CERT_PATH)
|
||||
# We need to restart peer apache services to ensure they have picked up
|
||||
# new ssl keys.
|
||||
peer_service_actions['restart'].append('apache2')
|
||||
peer_actions.append('update-ca-certificates')
|
||||
|
||||
if bool_from_string(config('use-https')):
|
||||
log("Syncing keystone-endpoint certs since use-https=True",
|
||||
level=DEBUG)
|
||||
paths_to_sync.append(SSL_DIR)
|
||||
paths_to_sync.append(APACHE_SSL_DIR)
|
||||
paths_to_sync.append(CA_CERT_PATH)
|
||||
# We need to restart peer apache services to ensure they have picked up
|
||||
# new ssl keys.
|
||||
peer_service_actions['restart'].append('apache2')
|
||||
peer_actions.append('update-ca-certificates')
|
||||
|
||||
# NOTE: certs needed for token signing e.g. pki and revocation list query.
|
||||
log("Syncing token certs", level=DEBUG)
|
||||
# pike dropped support for PKI token; only run on releases <= pike
|
||||
if CompareOpenStackReleases(os_release('keystone-common')) <= 'pike':
|
||||
paths_to_sync.append(PKI_CERTS_DIR)
|
||||
peer_actions.append('ensure-pki-permissions')
|
||||
|
||||
if not paths_to_sync:
|
||||
log("Nothing to sync - skipping", level=DEBUG)
|
||||
return {}
|
||||
|
||||
if not os.path.isdir(SYNC_FLAGS_DIR):
|
||||
mkdir(SYNC_FLAGS_DIR, SSH_USER, KEYSTONE_USER, 0o775)
|
||||
|
||||
restart_trigger = None
|
||||
for action, services in peer_service_actions.iteritems():
|
||||
services = set(services)
|
||||
if services:
|
||||
restart_trigger = str(uuid.uuid4())
|
||||
create_peer_service_actions(action, services)
|
||||
|
||||
create_peer_actions(peer_actions)
|
||||
|
||||
paths_to_sync = list(set(paths_to_sync))
|
||||
stage_paths_for_sync(paths_to_sync)
|
||||
|
||||
hash1 = hashlib.sha256()
|
||||
for path in paths_to_sync:
|
||||
update_hash_from_path(hash1, path)
|
||||
|
||||
cluster_rel_settings = {'ssl-cert-available-updates': SSL_SYNC_ARCHIVE,
|
||||
'sync-hash': hash1.hexdigest()}
|
||||
|
||||
synced_units = unison_sync([SSL_SYNC_ARCHIVE, SYNC_FLAGS_DIR])
|
||||
if synced_units:
|
||||
# Format here needs to match that used when peers request sync
|
||||
synced_units = [u.replace('/', '-') for u in synced_units]
|
||||
ssl_synced_units = \
|
||||
json.dumps(synced_units)
|
||||
# NOTE(hopem): we pull this onto the leader settings to avoid
|
||||
# unnecessary cluster relation noise. This is possible because the
|
||||
# setting is only needed by the cert master.
|
||||
if 'ssl-synced-units' not in leader_get():
|
||||
rid = relation_ids('cluster')[0]
|
||||
relation_set_and_migrate_to_leader(relation_id=rid,
|
||||
**{'ssl-synced-units':
|
||||
ssl_synced_units})
|
||||
else:
|
||||
leader_set({'ssl-synced-units': ssl_synced_units})
|
||||
|
||||
if restart_trigger:
|
||||
log("Sending restart-services-trigger=%s to all peers" %
|
||||
(restart_trigger), level=DEBUG)
|
||||
cluster_rel_settings['restart-services-trigger'] = restart_trigger
|
||||
|
||||
log("Sync complete", level=DEBUG)
|
||||
return cluster_rel_settings
|
||||
|
||||
|
||||
def clear_ssl_synced_units():
|
||||
"""Clear the 'synced' units record on the cluster relation.
|
||||
|
||||
If new unit sync reauests are set this will ensure that a sync occurs when
|
||||
the sync master receives the requests.
|
||||
"""
|
||||
log("Clearing ssl sync units", level=DEBUG)
|
||||
for rid in relation_ids('cluster'):
|
||||
if 'ssl-synced-units' not in leader_get():
|
||||
relation_set_and_migrate_to_leader(relation_id=rid,
|
||||
**{'ssl-synced-units': None})
|
||||
else:
|
||||
leader_set({'ssl-synced-units': None})
|
||||
|
||||
|
||||
def update_hash_from_path(hash, path, recurse_depth=10):
|
||||
"""Recurse through path and update the provided hash for every file found.
|
||||
"""
|
||||
if not recurse_depth:
|
||||
log("Max recursion depth (%s) reached for update_hash_from_path() at "
|
||||
"path='%s' - not going any deeper" % (recurse_depth, path),
|
||||
level=WARNING)
|
||||
return
|
||||
|
||||
for p in glob.glob("%s/*" % path):
|
||||
if os.path.isdir(p):
|
||||
update_hash_from_path(hash, p, recurse_depth=recurse_depth - 1)
|
||||
else:
|
||||
with open(p, 'r') as fd:
|
||||
hash.update(fd.read())
|
||||
|
||||
|
||||
def synchronize_ca_if_changed(force=False, fatal=False):
|
||||
"""Decorator to perform ssl cert sync if decorated function modifies them
|
||||
in any way.
|
||||
|
||||
If force is True a sync is done regardless.
|
||||
"""
|
||||
def inner_synchronize_ca_if_changed1(f):
|
||||
def inner_synchronize_ca_if_changed2(*args, **kwargs):
|
||||
# Only sync master can do sync. Ensure (a) we are not nested and
|
||||
# (b) a master is elected and we are it.
|
||||
acquired = SSL_SYNC_SEMAPHORE.acquire(blocking=0)
|
||||
try:
|
||||
if not acquired:
|
||||
log("Nested sync - ignoring", level=DEBUG)
|
||||
return f(*args, **kwargs)
|
||||
|
||||
if not ensure_ssl_cert_master():
|
||||
log("Not ssl-cert-master - ignoring sync", level=DEBUG)
|
||||
return f(*args, **kwargs)
|
||||
|
||||
peer_settings = {}
|
||||
if not force:
|
||||
hash1 = hashlib.sha256()
|
||||
for path in SSL_DIRS:
|
||||
update_hash_from_path(hash1, path)
|
||||
|
||||
ret = f(*args, **kwargs)
|
||||
|
||||
hash2 = hashlib.sha256()
|
||||
for path in SSL_DIRS:
|
||||
update_hash_from_path(hash2, path)
|
||||
|
||||
if hash1.hexdigest() != hash2.hexdigest():
|
||||
log("SSL certs have changed - syncing peers",
|
||||
level=DEBUG)
|
||||
peer_settings = synchronize_ca(fatal=fatal)
|
||||
else:
|
||||
log("SSL certs have not changed - skipping sync",
|
||||
level=DEBUG)
|
||||
else:
|
||||
ret = f(*args, **kwargs)
|
||||
log("Doing forced ssl cert sync", level=DEBUG)
|
||||
peer_settings = synchronize_ca(fatal=fatal)
|
||||
|
||||
# If we are the sync master but not leader, ensure we have
|
||||
# relinquished master status.
|
||||
cluster_rids = relation_ids('cluster')
|
||||
if cluster_rids:
|
||||
master = relation_get('ssl-cert-master',
|
||||
rid=cluster_rids[0],
|
||||
unit=local_unit())
|
||||
if not is_leader() and master == local_unit():
|
||||
log("Re-electing ssl cert master.", level=INFO)
|
||||
peer_settings['ssl-cert-master'] = 'unknown'
|
||||
|
||||
if peer_settings:
|
||||
relation_set(relation_id=cluster_rids[0],
|
||||
relation_settings=peer_settings)
|
||||
|
||||
return ret
|
||||
finally:
|
||||
SSL_SYNC_SEMAPHORE.release()
|
||||
|
||||
return inner_synchronize_ca_if_changed2
|
||||
|
||||
return inner_synchronize_ca_if_changed1
|
||||
|
||||
|
||||
@synchronize_ca_if_changed(force=True, fatal=True)
|
||||
def force_ssl_sync():
|
||||
"""Force SSL sync to all peers.
|
||||
|
||||
This is useful if we need to relinquish ssl-cert-master status while
|
||||
making sure that the new master has up-to-date certs.
|
||||
"""
|
||||
return
|
||||
|
||||
|
||||
def ensure_ssl_dir():
|
||||
"""Ensure juju ssl dir exists and is unsion read/writable."""
|
||||
# NOTE(thedac) Snap service restarts will override permissions
|
||||
# in SNAP_LIB_DIR including SSL_DIR
|
||||
perms = 0o775
|
||||
if not os.path.isdir(SSL_DIR):
|
||||
mkdir(SSL_DIR, SSH_USER, KEYSTONE_USER, perms)
|
||||
else:
|
||||
ensure_permissions(SSL_DIR, user=SSH_USER, group=KEYSTONE_USER,
|
||||
perms=perms)
|
||||
|
||||
|
||||
def get_ca(user=KEYSTONE_USER, group=KEYSTONE_USER):
|
||||
"""Initialize a new CA object if one hasn't already been loaded.
|
||||
|
||||
This will create a new CA or load an existing one.
|
||||
"""
|
||||
if not ssl.CA_SINGLETON:
|
||||
ensure_ssl_dir()
|
||||
d_name = '_'.join(SSL_CA_NAME.lower().split(' '))
|
||||
ca = ssl.JujuCA(name=SSL_CA_NAME, user=user, group=group,
|
||||
ca_dir=os.path.join(SSL_DIR,
|
||||
'%s_intermediate_ca' % d_name),
|
||||
root_ca_dir=os.path.join(SSL_DIR,
|
||||
'%s_root_ca' % d_name))
|
||||
|
||||
# Ensure a master is elected. This should cover the following cases:
|
||||
# * single unit == 'oldest' unit is elected as master
|
||||
# * multi unit + not clustered == 'oldest' unit is elcted as master
|
||||
# * multi unit + clustered == cluster leader is elected as master
|
||||
ensure_ssl_cert_master()
|
||||
|
||||
ssl.CA_SINGLETON.append(ca)
|
||||
|
||||
return ssl.CA_SINGLETON[0]
|
||||
|
||||
|
||||
def relation_list(rid):
|
||||
cmd = [
|
||||
'relation-list',
|
||||
|
@ -2017,8 +1389,6 @@ def add_service_to_keystone(relation_id=None, remote_unit=None):
|
|||
relation_data["api_version"] = get_api_version()
|
||||
relation_data["admin_domain_id"] = leader_get(
|
||||
attribute='admin_domain_id')
|
||||
# Get and pass CA bundle settings
|
||||
relation_data.update(get_ssl_ca_settings())
|
||||
|
||||
# Allow the remote service to request creation of any additional
|
||||
# roles. Currently used by Horizon
|
||||
|
@ -2075,7 +1445,6 @@ def add_service_to_keystone(relation_id=None, remote_unit=None):
|
|||
endpoints[ep][x] = v
|
||||
|
||||
services = []
|
||||
https_cn = None
|
||||
for ep in endpoints:
|
||||
# weed out any unrelated relation stuff Juju might have added
|
||||
# by ensuring each possible endpiont has appropriate fields
|
||||
|
@ -2145,25 +1514,6 @@ def add_service_to_keystone(relation_id=None, remote_unit=None):
|
|||
"admin_domain_id": leader_get(attribute='admin_domain_id'),
|
||||
}
|
||||
|
||||
# generate or get a new cert/key for service if set to manage certs.
|
||||
https_service_endpoints = config('https-service-endpoints')
|
||||
if https_service_endpoints and bool_from_string(https_service_endpoints):
|
||||
ca = get_ca(user=SSH_USER)
|
||||
# NOTE(jamespage) may have multiple cns to deal with to iterate
|
||||
https_cns = set(https_cns)
|
||||
for https_cn in https_cns:
|
||||
cert, key = ca.get_cert_and_key(common_name=https_cn)
|
||||
relation_data['ssl_cert_{}'.format(https_cn)] = b64encode(cert)
|
||||
relation_data['ssl_key_{}'.format(https_cn)] = b64encode(key)
|
||||
|
||||
# NOTE(jamespage) for backwards compatibility
|
||||
cert, key = ca.get_cert_and_key(common_name=internal_cn)
|
||||
relation_data['ssl_cert'] = b64encode(cert)
|
||||
relation_data['ssl_key'] = b64encode(key)
|
||||
|
||||
# Get and pass CA bundle settings
|
||||
relation_data.update(get_ssl_ca_settings())
|
||||
|
||||
peer_store_and_set(relation_id=relation_id, **relation_data)
|
||||
# NOTE(dosaboy): '__null__' settings are for peer relation only so that
|
||||
# settings can flushed so we filter them out for non-peer relation.
|
||||
|
@ -2229,30 +1579,10 @@ def add_credentials_to_keystone(relation_id=None, remote_unit=None):
|
|||
}
|
||||
if domain:
|
||||
relation_data['domain'] = domain
|
||||
# Get and pass CA bundle settings
|
||||
relation_data.update(get_ssl_ca_settings())
|
||||
|
||||
peer_store_and_set(relation_id=relation_id, **relation_data)
|
||||
|
||||
|
||||
def get_ssl_ca_settings():
|
||||
""" Get the Certificate Authority settings required to use the CA
|
||||
|
||||
:returns: Dictionary with https_keystone and ca_cert set
|
||||
"""
|
||||
ca_data = {}
|
||||
https_service_endpoints = config('https-service-endpoints')
|
||||
if (https_service_endpoints and
|
||||
bool_from_string(https_service_endpoints)):
|
||||
# Pass CA cert as client will need it to
|
||||
# verify https connections
|
||||
ca = get_ca(user=SSH_USER)
|
||||
ca_bundle = ca.get_ca_bundle()
|
||||
ca_data['https_keystone'] = 'True'
|
||||
ca_data['ca_cert'] = b64encode(ca_bundle)
|
||||
return ca_data
|
||||
|
||||
|
||||
def get_protocol():
|
||||
"""Determine the http protocol
|
||||
|
||||
|
|
|
@ -37,68 +37,11 @@ class TestKeystoneContexts(CharmTestCase):
|
|||
def setUp(self):
|
||||
super(TestKeystoneContexts, self).setUp(context, TO_PATCH)
|
||||
|
||||
def test_is_cert_provided_in_config(self):
|
||||
config = {'ssl_cert': 'somecert', 'ssl_key': 'greatkey'}
|
||||
|
||||
def fake_config(key):
|
||||
return config.get(key)
|
||||
|
||||
self.config.side_effect = fake_config
|
||||
self.assertTrue(context.is_cert_provided_in_config())
|
||||
|
||||
del config['ssl_cert']
|
||||
self.assertFalse(context.is_cert_provided_in_config())
|
||||
|
||||
@patch.object(context, 'mkdir')
|
||||
@patch('keystone_utils.get_ca')
|
||||
@patch('keystone_utils.ensure_permissions')
|
||||
@patch('keystone_utils.determine_ports', lambda: None)
|
||||
@patch('keystone_utils.is_ssl_cert_master', lambda: False)
|
||||
@patch.object(context, 'is_cert_provided_in_config', lambda: False)
|
||||
@patch.object(context, 'log', lambda *args, **kwargs: None)
|
||||
def test_apache_ssl_context_ssl_not_master(self, mock_ensure_permissions,
|
||||
mock_get_ca, mock_mkdir):
|
||||
context.ApacheSSLContext().configure_cert('foo')
|
||||
context.ApacheSSLContext().configure_ca()
|
||||
self.assertTrue(mock_mkdir.called)
|
||||
self.assertTrue(mock_ensure_permissions.called)
|
||||
self.assertFalse(mock_get_ca.called)
|
||||
|
||||
@patch('keystone_utils.ensure_permissions')
|
||||
@patch.object(context, 'install_ca_cert')
|
||||
@patch.object(context, 'b64decode')
|
||||
@patch.object(context, 'mkdir', lambda *args: None)
|
||||
@patch('keystone_utils.get_ca', lambda: None)
|
||||
@patch('keystone_utils.determine_ports', lambda: None)
|
||||
@patch('keystone_utils.is_ssl_cert_master', lambda: True)
|
||||
@patch.object(context, 'log', lambda *args, **kwargs: None)
|
||||
def test_apache_ssl_context_ssl_configure_ca(self, mock_b64decode,
|
||||
mock_install_ca_cert,
|
||||
mock_ensure_permissions):
|
||||
config = {'ssl_cert': 'somecert', 'ssl_key': 'greatkey'}
|
||||
|
||||
def fake_config(key):
|
||||
return config.get(key)
|
||||
|
||||
self.config.side_effect = fake_config
|
||||
|
||||
context.ApacheSSLContext().configure_ca()
|
||||
self.assertFalse(mock_b64decode.called)
|
||||
self.assertFalse(mock_install_ca_cert.called)
|
||||
self.assertFalse(mock_ensure_permissions.called)
|
||||
|
||||
config['ssl_ca'] = 'foofoofalalala'
|
||||
context.ApacheSSLContext().configure_ca()
|
||||
self.assertTrue(mock_b64decode.called)
|
||||
self.assertTrue(mock_install_ca_cert.called)
|
||||
self.assertTrue(mock_ensure_permissions.called)
|
||||
|
||||
@patch('charmhelpers.contrib.hahelpers.cluster.relation_ids')
|
||||
@patch('charmhelpers.contrib.openstack.ip.unit_get')
|
||||
@patch('charmhelpers.contrib.openstack.ip.service_name')
|
||||
@patch('charmhelpers.contrib.openstack.ip.config')
|
||||
@patch('keystone_utils.determine_ports')
|
||||
@patch('keystone_utils.is_ssl_cert_master')
|
||||
@patch('charmhelpers.contrib.openstack.context.config')
|
||||
@patch('charmhelpers.contrib.openstack.context.is_clustered')
|
||||
@patch('charmhelpers.contrib.openstack.context.determine_apache_port')
|
||||
|
@ -113,15 +56,12 @@ class TestKeystoneContexts(CharmTestCase):
|
|||
mock_determine_apache_port,
|
||||
mock_is_clustered,
|
||||
mock_config,
|
||||
mock_is_ssl_cert_master,
|
||||
mock_determine_ports,
|
||||
mock_ip_config,
|
||||
mock_service_name,
|
||||
mock_ip_unit_get,
|
||||
mock_rel_ids,
|
||||
):
|
||||
mock_relation_ids.return_value = []
|
||||
mock_is_ssl_cert_master.return_value = True
|
||||
mock_https.return_value = True
|
||||
mock_unit_get.return_value = '1.2.3.4'
|
||||
mock_ip_unit_get.return_value = '1.2.3.4'
|
||||
|
|
|
@ -13,7 +13,6 @@
|
|||
# limitations under the License.
|
||||
|
||||
import os
|
||||
import uuid
|
||||
import sys
|
||||
|
||||
from mock import call, patch, MagicMock
|
||||
|
@ -43,8 +42,6 @@ with patch('charmhelpers.contrib.hardening.harden.harden') as mock_dec:
|
|||
with patch('keystone_utils.run_in_apache') as mock_run_in_apache:
|
||||
import keystone_hooks as hooks
|
||||
|
||||
from charmhelpers.contrib import unison
|
||||
|
||||
utils.register_configs = _reg
|
||||
utils.restart_map = _map
|
||||
|
||||
|
@ -53,7 +50,6 @@ TO_PATCH = [
|
|||
'Hooks',
|
||||
'config',
|
||||
'log',
|
||||
'local_unit',
|
||||
'filter_installed_packages',
|
||||
'relation_ids',
|
||||
'relation_set',
|
||||
|
@ -89,17 +85,13 @@ TO_PATCH = [
|
|||
'migrate_database',
|
||||
'ensure_initial_admin',
|
||||
'add_service_to_keystone',
|
||||
'synchronize_ca_if_changed',
|
||||
'update_nrpe_config',
|
||||
'ensure_ssl_dirs',
|
||||
'is_db_ready',
|
||||
'create_or_show_domain',
|
||||
'get_api_version',
|
||||
# other
|
||||
'check_call',
|
||||
'execd_preinstall',
|
||||
'mkdir',
|
||||
'os',
|
||||
# ip
|
||||
'get_iface_for_address',
|
||||
'get_netmask_for_address',
|
||||
|
@ -123,10 +115,9 @@ class KeystoneRelationTests(CharmTestCase):
|
|||
self.snap_install_requested.return_value = False
|
||||
|
||||
@patch.object(utils, 'os_release')
|
||||
@patch.object(unison, 'ensure_user')
|
||||
@patch.object(hooks, 'service_stop', lambda *args: None)
|
||||
@patch.object(hooks, 'service_start', lambda *args: None)
|
||||
def test_install_hook(self, ensure_user, os_release):
|
||||
def test_install_hook(self, os_release):
|
||||
os_release.return_value = 'havana'
|
||||
self.run_in_apache.return_value = False
|
||||
repo = 'cloud:precise-grizzly'
|
||||
|
@ -134,33 +125,29 @@ class KeystoneRelationTests(CharmTestCase):
|
|||
hooks.install()
|
||||
self.assertTrue(self.execd_preinstall.called)
|
||||
self.configure_installation_source.assert_called_with(repo)
|
||||
ensure_user.assert_called_with(user=self.ssh_user, group='keystone')
|
||||
self.assertTrue(self.apt_update.called)
|
||||
self.apt_install.assert_called_with(
|
||||
['apache2', 'haproxy', 'keystone', 'openssl', 'pwgen',
|
||||
'python-keystoneclient', 'python-mysqldb', 'python-psycopg2',
|
||||
'python-six', 'unison', 'uuid'], fatal=True)
|
||||
'python-six', 'uuid'], fatal=True)
|
||||
self.disable_unused_apache_sites.assert_not_called()
|
||||
|
||||
@patch.object(utils, 'os_release')
|
||||
@patch.object(unison, 'ensure_user')
|
||||
@patch.object(hooks, 'service_stop', lambda *args: None)
|
||||
@patch.object(hooks, 'service_start', lambda *args: None)
|
||||
def test_install_hook_apache2(self, ensure_user, os_release):
|
||||
def test_install_hook_apache2(self, os_release):
|
||||
os_release.return_value = 'havana'
|
||||
self.run_in_apache.return_value = True
|
||||
repo = 'cloud:xenial-newton'
|
||||
self.test_config.set('openstack-origin', repo)
|
||||
self.os.path.exists.return_value = True
|
||||
hooks.install()
|
||||
self.assertTrue(self.execd_preinstall.called)
|
||||
self.configure_installation_source.assert_called_with(repo)
|
||||
ensure_user.assert_called_with(user=self.ssh_user, group='keystone')
|
||||
self.assertTrue(self.apt_update.called)
|
||||
self.apt_install.assert_called_with(
|
||||
['apache2', 'haproxy', 'keystone', 'openssl', 'pwgen',
|
||||
'python-keystoneclient', 'python-mysqldb', 'python-psycopg2',
|
||||
'python-six', 'unison', 'uuid'], fatal=True)
|
||||
'python-six', 'uuid'], fatal=True)
|
||||
self.disable_unused_apache_sites.assert_called_with()
|
||||
|
||||
mod_ch_openstack_utils = 'charmhelpers.contrib.openstack.utils'
|
||||
|
@ -196,12 +183,9 @@ class KeystoneRelationTests(CharmTestCase):
|
|||
hostname='192.168.20.1')
|
||||
|
||||
@patch('keystone_utils.log')
|
||||
@patch('keystone_utils.ensure_ssl_cert_master')
|
||||
@patch.object(hooks, 'CONFIGS')
|
||||
def test_db_changed_missing_relation_data(self, configs,
|
||||
mock_ensure_ssl_cert_master,
|
||||
mock_log):
|
||||
mock_ensure_ssl_cert_master.return_value = False
|
||||
configs.complete_contexts = MagicMock()
|
||||
configs.complete_contexts.return_value = []
|
||||
hooks.db_changed()
|
||||
|
@ -212,20 +196,15 @@ class KeystoneRelationTests(CharmTestCase):
|
|||
@patch.object(hooks, 'update_all_identity_relation_units')
|
||||
def _shared_db_test(self, configs, unit_name, mock_update_all):
|
||||
self.relation_get.return_value = 'keystone/0 keystone/3'
|
||||
self.local_unit.return_value = unit_name
|
||||
configs.complete_contexts = MagicMock()
|
||||
configs.complete_contexts.return_value = ['shared-db']
|
||||
configs.write = MagicMock()
|
||||
hooks.db_changed()
|
||||
|
||||
@patch.object(hooks, 'leader_init_db_if_ready')
|
||||
@patch('keystone_utils.ensure_ssl_cert_master')
|
||||
@patch.object(hooks, 'CONFIGS')
|
||||
def test_db_changed(self, configs,
|
||||
mock_ensure_ssl_cert_master,
|
||||
leader_init):
|
||||
def test_db_changed(self, configs, leader_init):
|
||||
self.os_release.return_value = 'havana'
|
||||
mock_ensure_ssl_cert_master.return_value = False
|
||||
self._shared_db_test(configs, 'keystone/3')
|
||||
self.assertEqual([call('/etc/keystone/keystone.conf')],
|
||||
configs.write.call_args_list)
|
||||
|
@ -236,37 +215,16 @@ class KeystoneRelationTests(CharmTestCase):
|
|||
@patch.object(hooks, 'run_in_apache')
|
||||
@patch.object(hooks, 'is_db_initialised')
|
||||
@patch('keystone_utils.log')
|
||||
@patch('keystone_utils.ensure_ssl_cert_master')
|
||||
@patch('keystone_utils.ensure_ssl_dirs')
|
||||
@patch.object(hooks, 'ensure_permissions')
|
||||
@patch.object(hooks, 'ensure_pki_cert_paths')
|
||||
@patch.object(hooks, 'ensure_pki_dir_permissions')
|
||||
@patch.object(hooks, 'ensure_ssl_dir')
|
||||
@patch.object(hooks, 'is_ssl_cert_master')
|
||||
@patch.object(hooks, 'send_ssl_sync_request')
|
||||
@patch.object(hooks, 'peer_units')
|
||||
@patch.object(hooks, 'admin_relation_changed')
|
||||
@patch.object(hooks, 'cluster_joined')
|
||||
@patch.object(unison, 'ensure_user')
|
||||
@patch.object(unison, 'get_homedir')
|
||||
@patch.object(hooks, 'CONFIGS')
|
||||
@patch.object(hooks, 'identity_changed')
|
||||
@patch.object(hooks, 'configure_https')
|
||||
def test_config_changed_no_upgrade_leader(self, configure_https,
|
||||
identity_changed,
|
||||
configs, get_homedir,
|
||||
ensure_user,
|
||||
configs,
|
||||
mock_cluster_joined,
|
||||
admin_relation_changed,
|
||||
mock_peer_units,
|
||||
mock_send_ssl_sync_request,
|
||||
mock_is_ssl_cert_master,
|
||||
mock_ensure_ssl_dir,
|
||||
mock_ensure_pki_cert_paths,
|
||||
mock_ensure_permissions,
|
||||
mock_ensure_pki_dir_permissions,
|
||||
mock_ensure_ssl_dirs,
|
||||
mock_ensure_ssl_cert_master,
|
||||
mock_log,
|
||||
mock_is_db_initialised,
|
||||
mock_run_in_apache,
|
||||
|
@ -282,19 +240,12 @@ class KeystoneRelationTests(CharmTestCase):
|
|||
self.relation_ids.side_effect = fake_relation_ids
|
||||
|
||||
mock_run_in_apache.return_value = False
|
||||
mock_is_ssl_cert_master.return_value = True
|
||||
mock_is_db_initialised.return_value = True
|
||||
self.is_db_ready.return_value = True
|
||||
self.openstack_upgrade_available.return_value = False
|
||||
self.is_elected_leader.return_value = True
|
||||
# avoid having to mock syncer
|
||||
mock_ensure_ssl_cert_master.return_value = False
|
||||
mock_peer_units.return_value = []
|
||||
self.related_units.return_value = ['unit/0']
|
||||
|
||||
hooks.config_changed()
|
||||
ensure_user.assert_called_with(user=self.ssh_user, group='keystone')
|
||||
get_homedir.assert_called_with(self.ssh_user)
|
||||
|
||||
self.save_script_rc.assert_called_with()
|
||||
configure_https.assert_called_with()
|
||||
|
@ -309,33 +260,14 @@ class KeystoneRelationTests(CharmTestCase):
|
|||
@patch.object(hooks, 'update_all_identity_relation_units')
|
||||
@patch.object(hooks, 'run_in_apache')
|
||||
@patch('keystone_utils.log')
|
||||
@patch('keystone_utils.ensure_ssl_cert_master')
|
||||
@patch('keystone_utils.ensure_ssl_dirs')
|
||||
@patch.object(hooks, 'ensure_permissions')
|
||||
@patch.object(hooks, 'ensure_pki_cert_paths')
|
||||
@patch.object(hooks, 'ensure_pki_dir_permissions')
|
||||
@patch.object(hooks, 'ensure_ssl_dir')
|
||||
@patch.object(hooks, 'peer_units')
|
||||
@patch.object(hooks, 'is_ssl_cert_master')
|
||||
@patch.object(hooks, 'cluster_joined')
|
||||
@patch.object(unison, 'ensure_user')
|
||||
@patch.object(unison, 'get_homedir')
|
||||
@patch.object(hooks, 'CONFIGS')
|
||||
@patch.object(hooks, 'identity_changed')
|
||||
@patch.object(hooks, 'configure_https')
|
||||
def test_config_changed_no_upgrade_not_leader(self, configure_https,
|
||||
identity_changed,
|
||||
configs, get_homedir,
|
||||
ensure_user,
|
||||
configs,
|
||||
mock_cluster_joined,
|
||||
mock_is_ssl_cert_master,
|
||||
mock_peer_units,
|
||||
mock_ensure_ssl_dir,
|
||||
mock_ensure_permissions,
|
||||
mock_ensure_pki_cert_paths,
|
||||
mock_ensure_pki_permissions,
|
||||
ensure_ssl_dirs,
|
||||
mock_ensure_ssl_cert_master,
|
||||
mock_log,
|
||||
mock_run_in_apache, update,
|
||||
mock_update_domains):
|
||||
|
@ -349,15 +281,9 @@ class KeystoneRelationTests(CharmTestCase):
|
|||
self.relation_ids.side_effect = fake_relation_ids
|
||||
|
||||
mock_run_in_apache.return_value = False
|
||||
mock_is_ssl_cert_master.return_value = True
|
||||
mock_peer_units.return_value = []
|
||||
self.openstack_upgrade_available.return_value = False
|
||||
self.is_elected_leader.return_value = False
|
||||
mock_ensure_ssl_cert_master.return_value = False
|
||||
|
||||
hooks.config_changed()
|
||||
ensure_user.assert_called_with(user=self.ssh_user, group='keystone')
|
||||
get_homedir.assert_called_with(self.ssh_user)
|
||||
|
||||
self.assertFalse(mock_cluster_joined.called)
|
||||
self.save_script_rc.assert_called_with()
|
||||
|
@ -373,36 +299,16 @@ class KeystoneRelationTests(CharmTestCase):
|
|||
@patch.object(hooks, 'run_in_apache')
|
||||
@patch.object(hooks, 'is_db_initialised')
|
||||
@patch('keystone_utils.log')
|
||||
@patch('keystone_utils.ensure_ssl_cert_master')
|
||||
@patch('keystone_utils.ensure_ssl_dirs')
|
||||
@patch.object(hooks, 'ensure_permissions')
|
||||
@patch.object(hooks, 'ensure_pki_cert_paths')
|
||||
@patch.object(hooks, 'ensure_pki_dir_permissions')
|
||||
@patch.object(hooks, 'ensure_ssl_dir')
|
||||
@patch.object(hooks, 'is_ssl_cert_master')
|
||||
@patch.object(hooks, 'send_ssl_sync_request')
|
||||
@patch.object(hooks, 'peer_units')
|
||||
@patch.object(hooks, 'admin_relation_changed')
|
||||
@patch.object(hooks, 'cluster_joined')
|
||||
@patch.object(unison, 'ensure_user')
|
||||
@patch.object(unison, 'get_homedir')
|
||||
@patch.object(hooks, 'CONFIGS')
|
||||
@patch.object(hooks, 'identity_changed')
|
||||
@patch.object(hooks, 'configure_https')
|
||||
def test_config_changed_with_openstack_upgrade(self, configure_https,
|
||||
identity_changed,
|
||||
configs, get_homedir,
|
||||
ensure_user, cluster_joined,
|
||||
configs,
|
||||
cluster_joined,
|
||||
admin_relation_changed,
|
||||
mock_peer_units,
|
||||
mock_send_ssl_sync_request,
|
||||
mock_is_ssl_cert_master,
|
||||
mock_ensure_ssl_dir,
|
||||
mock_ensure_permissions,
|
||||
mock_ensure_pki_cert_paths,
|
||||
mock_ensure_pki_permissions,
|
||||
mock_ensure_ssl_dirs,
|
||||
mock_ensure_ssl_cert_master,
|
||||
mock_log,
|
||||
mock_is_db_initialised,
|
||||
mock_run_in_apache,
|
||||
|
@ -417,19 +323,12 @@ class KeystoneRelationTests(CharmTestCase):
|
|||
self.relation_ids.side_effect = fake_relation_ids
|
||||
|
||||
mock_run_in_apache.return_value = False
|
||||
mock_is_ssl_cert_master.return_value = True
|
||||
self.is_db_ready.return_value = True
|
||||
mock_is_db_initialised.return_value = True
|
||||
self.openstack_upgrade_available.return_value = True
|
||||
self.is_elected_leader.return_value = True
|
||||
# avoid having to mock syncer
|
||||
mock_ensure_ssl_cert_master.return_value = False
|
||||
mock_peer_units.return_value = []
|
||||
self.related_units.return_value = ['unit/0']
|
||||
|
||||
hooks.config_changed()
|
||||
ensure_user.assert_called_with(user=self.ssh_user, group='keystone')
|
||||
get_homedir.assert_called_with(self.ssh_user)
|
||||
|
||||
self.assertTrue(self.do_openstack_upgrade_reexec.called)
|
||||
|
||||
|
@ -442,31 +341,16 @@ class KeystoneRelationTests(CharmTestCase):
|
|||
|
||||
@patch.object(hooks, 'os_release')
|
||||
@patch.object(hooks, 'run_in_apache')
|
||||
@patch.object(hooks, 'initialise_pki')
|
||||
@patch.object(hooks, 'is_db_initialised')
|
||||
@patch.object(hooks, 'ensure_ssl_dir')
|
||||
@patch.object(hooks, 'configure_https')
|
||||
@patch.object(hooks, 'is_ssl_cert_master')
|
||||
@patch.object(hooks, 'peer_units')
|
||||
@patch.object(unison, 'get_homedir')
|
||||
@patch.object(unison, 'ensure_user')
|
||||
@patch('keystone_utils.ensure_ssl_cert_master')
|
||||
def test_config_changed_with_openstack_upgrade_action(self,
|
||||
ensure_ssl_cert,
|
||||
ensure_user,
|
||||
get_home,
|
||||
peer_units, is_ssl,
|
||||
config_https,
|
||||
ensure_ssl_dir,
|
||||
mock_db_init,
|
||||
mock_initialise_pki,
|
||||
mock_run_in_apache,
|
||||
os_release):
|
||||
os_release.return_value = 'ocata'
|
||||
self.enable_memcache.return_value = False
|
||||
mock_run_in_apache.return_value = False
|
||||
ensure_ssl_cert.return_value = False
|
||||
peer_units.return_value = []
|
||||
|
||||
self.openstack_upgrade_available.return_value = True
|
||||
self.test_config.set('action-managed-upgrade', True)
|
||||
|
@ -477,17 +361,18 @@ class KeystoneRelationTests(CharmTestCase):
|
|||
|
||||
@patch.object(hooks, 'is_db_initialised')
|
||||
@patch('keystone_utils.log')
|
||||
@patch('keystone_utils.ensure_ssl_cert_master')
|
||||
@patch.object(hooks, 'hashlib')
|
||||
@patch.object(hooks, 'send_notifications')
|
||||
def test_identity_changed_leader(self, mock_send_notifications,
|
||||
mock_hashlib, mock_ensure_ssl_cert_master,
|
||||
mock_log, mock_is_db_initialised):
|
||||
self.expect_ha.return_value = False
|
||||
mock_is_db_initialised.return_value = True
|
||||
self.is_db_ready.return_value = True
|
||||
self.is_service_present.return_value = True
|
||||
mock_ensure_ssl_cert_master.return_value = False
|
||||
self.relation_get.return_value = {
|
||||
'public_url': 'http://dummy.local',
|
||||
'admin_url': 'http://dummy.local',
|
||||
'internal_url': 'http://dummy.local',
|
||||
}
|
||||
hooks.identity_changed(
|
||||
relation_id='identity-service:0',
|
||||
remote_unit='unit/0')
|
||||
|
@ -500,31 +385,26 @@ class KeystoneRelationTests(CharmTestCase):
|
|||
|
||||
@patch.object(hooks, 'is_db_initialised')
|
||||
@patch('keystone_utils.log')
|
||||
@patch('keystone_utils.ensure_ssl_cert_master')
|
||||
@patch.object(hooks, 'hashlib')
|
||||
@patch.object(hooks, 'send_notifications')
|
||||
def test_identity_changed_leader_no_neutron(self, mock_send_notifications,
|
||||
mock_hashlib,
|
||||
mock_ensure_ssl_cert_master,
|
||||
mock_log,
|
||||
mock_is_db_initialised):
|
||||
self.expect_ha.return_value = False
|
||||
mock_is_db_initialised.return_value = True
|
||||
self.is_db_ready.return_value = True
|
||||
self.is_service_present.return_value = False
|
||||
mock_ensure_ssl_cert_master.return_value = False
|
||||
self.relation_get.return_value = {
|
||||
'public_url': 'http://dummy.local',
|
||||
'admin_url': 'http://dummy.local',
|
||||
'internal_url': 'http://dummy.local',
|
||||
}
|
||||
hooks.identity_changed(
|
||||
relation_id='identity-service:0',
|
||||
remote_unit='unit/0')
|
||||
self.assertFalse(self.delete_service_entry.called)
|
||||
|
||||
@patch.object(hooks, 'local_unit')
|
||||
@patch('keystone_utils.log')
|
||||
@patch('keystone_utils.ensure_ssl_cert_master')
|
||||
def test_identity_changed_no_leader(self, mock_ensure_ssl_cert_master,
|
||||
mock_log, mock_local_unit):
|
||||
mock_ensure_ssl_cert_master.return_value = False
|
||||
mock_local_unit.return_value = 'unit/0'
|
||||
def test_identity_changed_no_leader(self, mock_log):
|
||||
self.is_elected_leader.return_value = False
|
||||
hooks.identity_changed(
|
||||
relation_id='identity-service:0',
|
||||
|
@ -533,60 +413,18 @@ class KeystoneRelationTests(CharmTestCase):
|
|||
self.log.assert_called_with(
|
||||
'Deferring identity_changed() to service leader.')
|
||||
|
||||
@patch.object(hooks, 'send_ssl_sync_request')
|
||||
@patch.object(hooks, 'local_unit')
|
||||
@patch.object(hooks, 'peer_units')
|
||||
@patch.object(unison, 'ssh_authorized_peers')
|
||||
def test_cluster_joined(self, ssh_authorized_peers, mock_peer_units,
|
||||
mock_local_unit, mock_send_ssl_sync_request):
|
||||
mock_local_unit.return_value = 'unit/0'
|
||||
mock_peer_units.return_value = ['unit/0']
|
||||
hooks.cluster_joined()
|
||||
ssh_authorized_peers.assert_called_with(
|
||||
user=self.ssh_user, group='juju_keystone',
|
||||
peer_interface='cluster', ensure_local_user=True)
|
||||
self.assertTrue(mock_send_ssl_sync_request.called)
|
||||
|
||||
mock_send_ssl_sync_request.reset_mock()
|
||||
hooks.cluster_joined(rid='foo:1', ssl_sync_request=True)
|
||||
self.assertTrue(mock_send_ssl_sync_request.called)
|
||||
|
||||
mock_send_ssl_sync_request.reset_mock()
|
||||
hooks.cluster_joined(rid='foo:1', ssl_sync_request=False)
|
||||
self.assertFalse(mock_send_ssl_sync_request.called)
|
||||
|
||||
@patch.object(hooks, 'relation_get_and_migrate')
|
||||
@patch.object(hooks, 'initialise_pki')
|
||||
@patch.object(hooks, 'update_all_identity_relation_units')
|
||||
@patch.object(hooks, 'get_ssl_sync_request_units')
|
||||
@patch.object(hooks, 'is_ssl_cert_master')
|
||||
@patch.object(hooks, 'peer_units')
|
||||
@patch('keystone_utils.relation_ids')
|
||||
@patch('keystone_utils.config')
|
||||
@patch('keystone_utils.log')
|
||||
@patch('keystone_utils.ensure_ssl_cert_master')
|
||||
@patch('keystone_utils.synchronize_ca')
|
||||
@patch.object(hooks, 'check_peer_actions')
|
||||
@patch.object(unison, 'ssh_authorized_peers')
|
||||
@patch.object(hooks, 'CONFIGS')
|
||||
def test_cluster_changed(self, configs, ssh_authorized_peers,
|
||||
check_peer_actions, mock_synchronize_ca,
|
||||
mock_ensure_ssl_cert_master,
|
||||
def test_cluster_changed(self, configs,
|
||||
mock_log, mock_config, mock_relation_ids,
|
||||
mock_peer_units,
|
||||
mock_is_ssl_cert_master,
|
||||
mock_get_ssl_sync_request_units,
|
||||
mock_update_all_identity_relation_units,
|
||||
mock_initialise_pki,
|
||||
mock_relation_get_and_migrate):
|
||||
mock_update_all_identity_relation_units):
|
||||
|
||||
relation_settings = {'foo_passwd': '123',
|
||||
'identity-service:16_foo': 'bar'}
|
||||
|
||||
mock_relation_get_and_migrate.return_value = None
|
||||
mock_is_ssl_cert_master.return_value = False
|
||||
mock_peer_units.return_value = ['unit/0']
|
||||
mock_ensure_ssl_cert_master.return_value = False
|
||||
mock_relation_ids.return_value = []
|
||||
self.is_leader.return_value = False
|
||||
|
||||
|
@ -601,13 +439,8 @@ class KeystoneRelationTests(CharmTestCase):
|
|||
mock_config.return_value = None
|
||||
|
||||
hooks.cluster_changed()
|
||||
whitelist = ['_passwd', 'identity-service:', 'db-initialised',
|
||||
'ssl-cert-available-updates', 'ssl-cert-master']
|
||||
whitelist = ['_passwd', 'identity-service:', 'db-initialised']
|
||||
self.peer_echo.assert_called_with(force=True, includes=whitelist)
|
||||
ssh_authorized_peers.assert_called_with(
|
||||
user=self.ssh_user, group='juju_keystone',
|
||||
peer_interface='cluster', ensure_local_user=True)
|
||||
self.assertFalse(mock_synchronize_ca.called)
|
||||
self.assertTrue(configs.write_all.called)
|
||||
|
||||
@patch.object(hooks, 'update_all_identity_relation_units')
|
||||
|
@ -786,54 +619,40 @@ class KeystoneRelationTests(CharmTestCase):
|
|||
self.assertTrue(self.update_dns_ha_resource_params.called)
|
||||
self.relation_set.assert_called_with(**args)
|
||||
|
||||
@patch.object(utils, 'peer_retrieve')
|
||||
@patch('keystone_utils.log')
|
||||
@patch('keystone_utils.ensure_ssl_cert_master')
|
||||
@patch('keystone_utils.synchronize_ca')
|
||||
@patch.object(hooks, 'CONFIGS')
|
||||
def test_ha_relation_changed_not_clustered_not_leader(self, configs,
|
||||
mock_synchronize_ca,
|
||||
mock_is_master,
|
||||
mock_log):
|
||||
mock_is_master.return_value = False
|
||||
mock_log,
|
||||
mock_peer_retrieve):
|
||||
self.relation_get.return_value = False
|
||||
self.is_elected_leader.return_value = False
|
||||
|
||||
hooks.ha_changed()
|
||||
self.assertTrue(configs.write_all.called)
|
||||
self.assertFalse(mock_synchronize_ca.called)
|
||||
|
||||
@patch.object(hooks, 'is_ssl_cert_master')
|
||||
@patch.object(hooks, 'update_all_identity_relation_units_force_sync')
|
||||
@patch.object(hooks, 'update_all_identity_relation_units')
|
||||
@patch.object(hooks, 'is_db_initialised')
|
||||
@patch('keystone_utils.log')
|
||||
@patch('keystone_utils.ensure_ssl_cert_master')
|
||||
@patch.object(hooks, 'identity_changed')
|
||||
@patch.object(hooks, 'CONFIGS')
|
||||
def test_ha_relation_changed_clustered_leader(self, configs,
|
||||
identity_changed,
|
||||
mock_ensure_ssl_cert_master,
|
||||
mock_log,
|
||||
mock_is_db_initialised,
|
||||
update, cert_master):
|
||||
update):
|
||||
mock_is_db_initialised.return_value = True
|
||||
self.is_db_ready.return_value = True
|
||||
mock_ensure_ssl_cert_master.return_value = False
|
||||
self.relation_get.return_value = True
|
||||
self.is_elected_leader.return_value = True
|
||||
self.relation_ids.return_value = ['identity-service:0']
|
||||
self.related_units.return_value = ['unit/0']
|
||||
cert_master.return_value = True
|
||||
|
||||
hooks.ha_changed()
|
||||
self.assertTrue(configs.write_all.called)
|
||||
self.assertTrue(update.called)
|
||||
|
||||
@patch('keystone_utils.log')
|
||||
@patch('keystone_utils.ensure_ssl_cert_master')
|
||||
@patch.object(hooks, 'CONFIGS')
|
||||
def test_configure_https_enable(self, configs, mock_ensure_ssl_cert_master,
|
||||
mock_log):
|
||||
mock_ensure_ssl_cert_master.return_value = False
|
||||
def test_configure_https_enable(self, configs, mock_log):
|
||||
configs.complete_contexts = MagicMock()
|
||||
configs.complete_contexts.return_value = ['https']
|
||||
configs.write = MagicMock()
|
||||
|
@ -844,12 +663,8 @@ class KeystoneRelationTests(CharmTestCase):
|
|||
self.check_call.assert_called_with(cmd)
|
||||
|
||||
@patch('keystone_utils.log')
|
||||
@patch('keystone_utils.ensure_ssl_cert_master')
|
||||
@patch.object(hooks, 'CONFIGS')
|
||||
def test_configure_https_disable(self, configs,
|
||||
mock_ensure_ssl_cert_master,
|
||||
mock_log):
|
||||
mock_ensure_ssl_cert_master.return_value = False
|
||||
def test_configure_https_disable(self, configs, mock_log):
|
||||
configs.complete_contexts = MagicMock()
|
||||
configs.complete_contexts.return_value = ['']
|
||||
configs.write = MagicMock()
|
||||
|
@ -865,16 +680,7 @@ class KeystoneRelationTests(CharmTestCase):
|
|||
@patch.object(hooks, 'is_db_initialised')
|
||||
@patch('keystone_utils.log')
|
||||
@patch('keystone_utils.relation_ids')
|
||||
@patch('keystone_utils.is_elected_leader')
|
||||
@patch('keystone_utils.ensure_ssl_cert_master')
|
||||
@patch('keystone_utils.update_hash_from_path')
|
||||
@patch('keystone_utils.synchronize_ca')
|
||||
@patch.object(unison, 'ssh_authorized_peers')
|
||||
def test_upgrade_charm_leader(self, ssh_authorized_peers,
|
||||
mock_synchronize_ca,
|
||||
mock_update_hash_from_path,
|
||||
mock_ensure_ssl_cert_master,
|
||||
mock_is_elected_leader,
|
||||
def test_upgrade_charm_leader(self,
|
||||
mock_relation_ids,
|
||||
mock_log,
|
||||
mock_is_db_initialised,
|
||||
|
@ -884,21 +690,11 @@ class KeystoneRelationTests(CharmTestCase):
|
|||
os_release.return_value = 'havana'
|
||||
mock_is_db_initialised.return_value = True
|
||||
mock_is_db_ready.return_value = True
|
||||
mock_is_elected_leader.return_value = False
|
||||
mock_relation_ids.return_value = []
|
||||
mock_ensure_ssl_cert_master.return_value = True
|
||||
# Ensure always returns diff
|
||||
mock_update_hash_from_path.side_effect = \
|
||||
lambda hash, *args, **kwargs: hash.update(str(uuid.uuid4()))
|
||||
|
||||
self.is_elected_leader.return_value = True
|
||||
self.filter_installed_packages.return_value = []
|
||||
hooks.upgrade_charm()
|
||||
self.assertTrue(self.apt_install.called)
|
||||
ssh_authorized_peers.assert_called_with(
|
||||
user=self.ssh_user, group='juju_keystone',
|
||||
peer_interface='cluster', ensure_local_user=True)
|
||||
self.assertTrue(mock_synchronize_ca.called)
|
||||
self.assertTrue(update.called)
|
||||
|
||||
@patch.object(hooks, 'update_all_identity_relation_units')
|
||||
|
@ -1039,33 +835,22 @@ class KeystoneRelationTests(CharmTestCase):
|
|||
# Still updates relations
|
||||
self.assertTrue(self.relation_ids.called)
|
||||
|
||||
@patch.object(utils, 'peer_retrieve')
|
||||
@patch.object(hooks, 'update_all_identity_relation_units')
|
||||
@patch.object(utils, 'os_release')
|
||||
@patch('keystone_utils.log')
|
||||
@patch('keystone_utils.relation_ids')
|
||||
@patch('keystone_utils.ensure_ssl_cert_master')
|
||||
@patch('keystone_utils.update_hash_from_path')
|
||||
@patch.object(unison, 'ssh_authorized_peers')
|
||||
def test_upgrade_charm_not_leader(self, ssh_authorized_peers,
|
||||
mock_update_hash_from_path,
|
||||
mock_ensure_ssl_cert_master,
|
||||
def test_upgrade_charm_not_leader(self,
|
||||
mock_relation_ids,
|
||||
mock_log,
|
||||
os_release, update):
|
||||
os_release, update, mock_peer_retrieve):
|
||||
os_release.return_value = 'havana'
|
||||
mock_relation_ids.return_value = []
|
||||
mock_ensure_ssl_cert_master.return_value = False
|
||||
# Ensure always returns diff
|
||||
mock_update_hash_from_path.side_effect = \
|
||||
lambda hash, *args, **kwargs: hash.update(str(uuid.uuid4()))
|
||||
|
||||
self.is_elected_leader.return_value = False
|
||||
self.filter_installed_packages.return_value = []
|
||||
mock_peer_retrieve.return_value = 'true'
|
||||
self.is_elected_leader.return_value = False
|
||||
hooks.upgrade_charm()
|
||||
self.assertTrue(self.apt_install.called)
|
||||
ssh_authorized_peers.assert_called_with(
|
||||
user=self.ssh_user, group='juju_keystone',
|
||||
peer_interface='cluster', ensure_local_user=True)
|
||||
self.assertTrue(self.log.called)
|
||||
self.assertFalse(update.called)
|
||||
|
||||
|
|
|
@ -15,7 +15,6 @@
|
|||
from mock import patch, call, MagicMock
|
||||
from test_utils import CharmTestCase
|
||||
import os
|
||||
from base64 import b64encode
|
||||
import subprocess
|
||||
|
||||
os.environ['JUJU_UNIT_NAME'] = 'keystone'
|
||||
|
@ -30,7 +29,6 @@ TO_PATCH = [
|
|||
'config',
|
||||
'os_release',
|
||||
'log',
|
||||
'get_ca',
|
||||
'create_role',
|
||||
'create_service_entry',
|
||||
'create_endpoint_template',
|
||||
|
@ -41,12 +39,9 @@ TO_PATCH = [
|
|||
'get_os_codename_install_source',
|
||||
'grant_role',
|
||||
'configure_installation_source',
|
||||
'is_elected_leader',
|
||||
'is_ssl_cert_master',
|
||||
'https',
|
||||
'lsb_release',
|
||||
'peer_store_and_set',
|
||||
'service_restart',
|
||||
'service_stop',
|
||||
'service_start',
|
||||
'snap_install_requested',
|
||||
|
@ -66,7 +61,6 @@ TO_PATCH = [
|
|||
'time',
|
||||
'pwgen',
|
||||
'os_application_version_set',
|
||||
'is_leader',
|
||||
'reset_os_release',
|
||||
]
|
||||
|
||||
|
@ -174,6 +168,7 @@ class TestKeystoneUtils(CharmTestCase):
|
|||
ex = utils.BASE_PACKAGES_SNAP + ['memcached']
|
||||
self.assertEqual(set(ex), set(result))
|
||||
|
||||
@patch.object(utils, 'is_elected_leader')
|
||||
@patch.object(utils, 'disable_unused_apache_sites')
|
||||
@patch('os.path.exists')
|
||||
@patch.object(utils, 'run_in_apache')
|
||||
|
@ -181,11 +176,11 @@ class TestKeystoneUtils(CharmTestCase):
|
|||
@patch.object(utils, 'migrate_database')
|
||||
def test_openstack_upgrade_leader(
|
||||
self, migrate_database, determine_packages,
|
||||
run_in_apache, os_path_exists, disable_unused_apache_sites):
|
||||
run_in_apache, os_path_exists, disable_unused_apache_sites,
|
||||
mock_is_elected_leader):
|
||||
configs = MagicMock()
|
||||
self.test_config.set('openstack-origin', 'cloud:xenial-newton')
|
||||
determine_packages.return_value = []
|
||||
self.is_elected_leader.return_value = True
|
||||
os_path_exists.return_value = True
|
||||
run_in_apache.return_value = True
|
||||
|
||||
|
@ -231,9 +226,8 @@ class TestKeystoneUtils(CharmTestCase):
|
|||
@patch.object(utils, 'get_api_version')
|
||||
@patch.object(utils, 'get_manager')
|
||||
@patch.object(utils, 'resolve_address')
|
||||
@patch.object(utils, 'b64encode')
|
||||
def test_add_service_to_keystone_clustered_https_none_values(
|
||||
self, b64encode, _resolve_address, _get_manager,
|
||||
self, _resolve_address, _get_manager,
|
||||
_get_api_version, _leader_get):
|
||||
_get_api_version.return_value = 2
|
||||
_leader_get.return_value = None
|
||||
|
@ -241,11 +235,9 @@ class TestKeystoneUtils(CharmTestCase):
|
|||
remote_unit = 'unit/0'
|
||||
_resolve_address.return_value = '10.10.10.10'
|
||||
self.https.return_value = True
|
||||
self.test_config.set('https-service-endpoints', 'True')
|
||||
self.test_config.set('vip', '10.10.10.10')
|
||||
self.test_config.set('admin-port', 80)
|
||||
self.test_config.set('service-port', 81)
|
||||
b64encode.return_value = 'certificate'
|
||||
self.get_requested_roles.return_value = ['role1', ]
|
||||
|
||||
self.relation_get.return_value = {'service': 'keystone',
|
||||
|
@ -262,12 +254,10 @@ class TestKeystoneUtils(CharmTestCase):
|
|||
|
||||
relation_data = {'auth_host': '10.10.10.10',
|
||||
'service_host': '10.10.10.10',
|
||||
'auth_protocol': 'https',
|
||||
'service_protocol': 'https',
|
||||
'auth_port': 80,
|
||||
'auth_protocol': 'https',
|
||||
'service_port': 81,
|
||||
'https_keystone': 'True',
|
||||
'ca_cert': 'certificate',
|
||||
'region': 'RegionOne',
|
||||
'api_version': 2,
|
||||
'admin_domain_id': None}
|
||||
|
@ -649,12 +639,15 @@ class TestKeystoneUtils(CharmTestCase):
|
|||
mock_relation_set.assert_called_once_with(relation_id=relation_id,
|
||||
relation_settings=settings)
|
||||
|
||||
@patch.object(utils, 'is_elected_leader')
|
||||
@patch.object(utils, 'peer_retrieve')
|
||||
@patch.object(utils, 'peer_store')
|
||||
def test_get_admin_passwd_pwd_set(self, mock_peer_store,
|
||||
mock_peer_retrieve):
|
||||
mock_peer_retrieve,
|
||||
mock_is_elected_leader):
|
||||
mock_peer_retrieve.return_value = None
|
||||
self.test_config.set('admin-password', 'supersecret')
|
||||
mock_is_elected_leader.return_value = True
|
||||
self.assertEqual(utils.get_admin_passwd(), 'supersecret')
|
||||
mock_peer_store.assert_called_once_with('admin_passwd', 'supersecret')
|
||||
|
||||
|
@ -702,96 +695,6 @@ class TestKeystoneUtils(CharmTestCase):
|
|||
self.related_units.return_value = []
|
||||
self.assertTrue(utils.is_db_ready())
|
||||
|
||||
@patch.object(utils, 'peer_units')
|
||||
def test_ensure_ssl_cert_master_ssl_no_peers(self, mock_peer_units):
|
||||
def mock_rel_get(unit=None, **kwargs):
|
||||
return None
|
||||
|
||||
self.relation_get.side_effect = mock_rel_get
|
||||
self.relation_ids.return_value = ['cluster:0']
|
||||
self.local_unit.return_value = 'unit/0'
|
||||
self.related_units.return_value = []
|
||||
mock_peer_units.return_value = []
|
||||
# This should get ignored since we are overriding
|
||||
self.is_ssl_cert_master.return_value = False
|
||||
self.is_elected_leader.return_value = False
|
||||
self.assertTrue(utils.ensure_ssl_cert_master())
|
||||
settings = {'ssl-cert-master': 'unit/0'}
|
||||
self.relation_set.assert_called_with(relation_id='cluster:0',
|
||||
relation_settings=settings)
|
||||
|
||||
@patch.object(utils, 'peer_units')
|
||||
def test_ensure_ssl_cert_master_ssl_master_no_peers(self,
|
||||
mock_peer_units):
|
||||
def mock_rel_get(unit=None, **kwargs):
|
||||
if unit == 'unit/0':
|
||||
return 'unit/0'
|
||||
|
||||
return None
|
||||
|
||||
self.relation_get.side_effect = mock_rel_get
|
||||
self.relation_ids.return_value = ['cluster:0']
|
||||
self.local_unit.return_value = 'unit/0'
|
||||
self.related_units.return_value = []
|
||||
mock_peer_units.return_value = []
|
||||
# This should get ignored since we are overriding
|
||||
self.is_ssl_cert_master.return_value = False
|
||||
self.is_elected_leader.return_value = False
|
||||
self.assertTrue(utils.ensure_ssl_cert_master())
|
||||
settings = {'ssl-cert-master': 'unit/0'}
|
||||
self.relation_set.assert_called_with(relation_id='cluster:0',
|
||||
relation_settings=settings)
|
||||
|
||||
@patch.object(utils, 'peer_units')
|
||||
def test_ensure_ssl_cert_master_ssl_not_leader(self, mock_peer_units):
|
||||
self.relation_ids.return_value = ['cluster:0']
|
||||
self.local_unit.return_value = 'unit/0'
|
||||
mock_peer_units.return_value = ['unit/1']
|
||||
self.is_ssl_cert_master.return_value = False
|
||||
self.is_elected_leader.return_value = False
|
||||
self.assertFalse(utils.ensure_ssl_cert_master())
|
||||
self.assertFalse(self.relation_set.called)
|
||||
|
||||
@patch.object(utils, 'peer_units')
|
||||
def test_ensure_ssl_cert_master_is_leader_new_peer(self,
|
||||
mock_peer_units):
|
||||
def mock_rel_get(unit=None, **kwargs):
|
||||
if unit == 'unit/0':
|
||||
return 'unit/0'
|
||||
|
||||
return 'unknown'
|
||||
|
||||
self.relation_get.side_effect = mock_rel_get
|
||||
self.relation_ids.return_value = ['cluster:0']
|
||||
self.local_unit.return_value = 'unit/0'
|
||||
mock_peer_units.return_value = ['unit/1']
|
||||
self.related_units.return_value = ['unit/1']
|
||||
self.is_ssl_cert_master.return_value = False
|
||||
self.is_elected_leader.return_value = True
|
||||
self.assertFalse(utils.ensure_ssl_cert_master())
|
||||
settings = {'ssl-cert-master': 'unit/0'}
|
||||
self.relation_set.assert_called_with(relation_id='cluster:0',
|
||||
relation_settings=settings)
|
||||
|
||||
@patch.object(utils, 'peer_units')
|
||||
def test_ensure_ssl_cert_master_is_leader_no_new_peer(self,
|
||||
mock_peer_units):
|
||||
def mock_rel_get(unit=None, **kwargs):
|
||||
if unit == 'unit/0':
|
||||
return 'unit/0'
|
||||
|
||||
return 'unit/0'
|
||||
|
||||
self.relation_get.side_effect = mock_rel_get
|
||||
self.relation_ids.return_value = ['cluster:0']
|
||||
self.local_unit.return_value = 'unit/0'
|
||||
mock_peer_units.return_value = ['unit/1']
|
||||
self.related_units.return_value = ['unit/1']
|
||||
self.is_ssl_cert_master.return_value = False
|
||||
self.is_elected_leader.return_value = True
|
||||
self.assertFalse(utils.ensure_ssl_cert_master())
|
||||
self.assertFalse(self.relation_set.called)
|
||||
|
||||
@patch.object(utils, 'leader_set')
|
||||
@patch.object(utils, 'leader_get')
|
||||
@patch('charmhelpers.contrib.openstack.ip.unit_get')
|
||||
|
@ -826,30 +729,6 @@ class TestKeystoneUtils(CharmTestCase):
|
|||
region='RegionOne',
|
||||
)
|
||||
|
||||
@patch.object(utils, 'peer_units')
|
||||
def test_ensure_ssl_cert_master_is_leader_bad_votes(self,
|
||||
mock_peer_units):
|
||||
counter = {0: 0}
|
||||
|
||||
def mock_rel_get(unit=None, **kwargs):
|
||||
"""Returns a mix of votes."""
|
||||
if unit == 'unit/0':
|
||||
return 'unit/0'
|
||||
|
||||
ret = 'unit/%d' % (counter[0])
|
||||
counter[0] += 1
|
||||
return ret
|
||||
|
||||
self.relation_get.side_effect = mock_rel_get
|
||||
self.relation_ids.return_value = ['cluster:0']
|
||||
self.local_unit.return_value = 'unit/0'
|
||||
mock_peer_units.return_value = ['unit/1']
|
||||
self.related_units.return_value = ['unit/1']
|
||||
self.is_ssl_cert_master.return_value = False
|
||||
self.is_elected_leader.return_value = True
|
||||
self.assertFalse(utils.ensure_ssl_cert_master())
|
||||
self.assertFalse(self.relation_set.called)
|
||||
|
||||
@patch.object(utils, 'get_manager')
|
||||
def test_is_service_present(self, KeystoneManager):
|
||||
mock_keystone = MagicMock()
|
||||
|
@ -1006,16 +885,6 @@ class TestKeystoneUtils(CharmTestCase):
|
|||
protocol = utils.get_protocol()
|
||||
self.assertEqual(protocol, 'https')
|
||||
|
||||
def test_get_ssl_ca_settings(self):
|
||||
CA = MagicMock()
|
||||
CA.get_ca_bundle.return_value = 'certstring'
|
||||
self.test_config.set('https-service-endpoints', 'True')
|
||||
self.get_ca.return_value = CA
|
||||
expected_settings = {'https_keystone': 'True',
|
||||
'ca_cert': b64encode('certstring')}
|
||||
settings = utils.get_ssl_ca_settings()
|
||||
self.assertEqual(settings, expected_settings)
|
||||
|
||||
@patch.object(utils, 'get_manager')
|
||||
def test_add_credentials_keystone_not_ready(self, get_manager):
|
||||
""" Verify add_credentials_to_keystone when the relation
|
||||
|
@ -1201,67 +1070,6 @@ class TestKeystoneUtils(CharmTestCase):
|
|||
self.peer_store_and_set.assert_called_with(relation_id=relation_id,
|
||||
**relation_data)
|
||||
|
||||
@patch.object(utils, 'set_service_password')
|
||||
@patch.object(utils, 'get_service_password')
|
||||
@patch.object(utils, 'get_ssl_ca_settings')
|
||||
@patch.object(utils, 'create_user_credentials')
|
||||
@patch.object(utils, 'get_protocol')
|
||||
@patch.object(utils, 'resolve_address')
|
||||
@patch.object(utils, 'get_api_version')
|
||||
@patch.object(utils, 'get_manager')
|
||||
def test_add_credentials_keystone_ssl(self, get_manager,
|
||||
get_api_version,
|
||||
resolve_address,
|
||||
get_protocol,
|
||||
create_user_credentials,
|
||||
get_ssl_ca_settings,
|
||||
get_callback, set_callback):
|
||||
""" Verify add_credentials with SSL """
|
||||
manager = MagicMock()
|
||||
manager.resolve_tenant_id.return_value = 'abcdef0123456789'
|
||||
get_manager.return_value = manager
|
||||
remote_unit = 'unit/0'
|
||||
relation_id = 'identity-credentials:0'
|
||||
get_api_version.return_value = 2
|
||||
get_protocol.return_value = 'https'
|
||||
resolve_address.return_value = '10.10.10.10'
|
||||
create_user_credentials.return_value = 'password'
|
||||
get_ssl_ca_settings.return_value = {'https_keystone': 'True',
|
||||
'ca_cert': 'base64certstring'}
|
||||
self.relation_get.return_value = {'username': 'requester'}
|
||||
self.get_service_password.return_value = 'password'
|
||||
self.get_requested_roles.return_value = []
|
||||
self.test_config.set('admin-port', 80)
|
||||
self.test_config.set('service-port', 81)
|
||||
self.test_config.set('https-service-endpoints', 'True')
|
||||
relation_data = {'auth_host': '10.10.10.10',
|
||||
'credentials_host': '10.10.10.10',
|
||||
'credentials_port': 81,
|
||||
'auth_port': 80,
|
||||
'auth_protocol': 'https',
|
||||
'credentials_username': 'requester',
|
||||
'credentials_protocol': 'https',
|
||||
'credentials_password': 'password',
|
||||
'credentials_project': 'services',
|
||||
'credentials_project_id': 'abcdef0123456789',
|
||||
'region': 'RegionOne',
|
||||
'api_version': 2,
|
||||
'https_keystone': 'True',
|
||||
'ca_cert': 'base64certstring'}
|
||||
|
||||
utils.add_credentials_to_keystone(
|
||||
relation_id=relation_id,
|
||||
remote_unit=remote_unit)
|
||||
create_user_credentials.assert_called_with('requester',
|
||||
get_callback,
|
||||
set_callback,
|
||||
domain=None,
|
||||
new_roles=[],
|
||||
grants=['Admin'],
|
||||
tenant='services')
|
||||
self.peer_store_and_set.assert_called_with(relation_id=relation_id,
|
||||
**relation_data)
|
||||
|
||||
@patch.object(utils.os, 'remove')
|
||||
@patch.object(utils.os.path, 'exists')
|
||||
def test_disable_unused_apache_sites(self, os_path_exists, os_remove):
|
||||
|
|
Loading…
Reference in New Issue