Add barbican-hsm-plugin interface support

The barbican-hsm-plugin interface provides a mechanism for the Barbican
charm to communicate with an HSM plugin.  The plugin (from the Barbican
perspective) is provided as a PKCS#11 compliant library (.so) and so is
local to the Barbican installation.  Thus, the hsm-plugin charms are
subordinate to the Barbican charm and run on the same unit.

This change also provides two actions (generate-mkek and generate-hmac)
which are 'one-off' operations to initialise the HSM with the global
master keys.

Add a note to the README that the generate-mkek and generate-hmac
actions may only be done once as the HSM may reject overwriting the key.

Add Apache2.0 LICENSE and license headers to files
Removed redundant copyright file

Change the reference for the internal port to 9311 The barbican project
changed the INTERNAL port to the same as the PUBLIC port.

Add in seed_file and seed_length to template. These are needed for a
change in Barbican to support seeding the RNG in the HSM if required.
They are set to /dev/random and 32.

Fetch the barbican sources from a PPA (for bug: 1599550)

Remove the trusty support for Py3 from install hook
This commit is contained in:
Alex Kavanagh 2016-06-23 13:25:00 +00:00
parent 9c7c6bbdca
commit 6d0248e9d9
26 changed files with 990 additions and 158 deletions

202
LICENSE Normal file
View File

@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
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.

View File

@ -12,4 +12,4 @@ lint:
test:
@echo Starting unit tests...
@tox -e py27,py34,py35
@tox -e py34,py35

87
README.md Normal file
View File

@ -0,0 +1,87 @@
# Barbican Source Charm
This repository is for the reactive, layered,
[Barbican](https://wiki.openstack.org/wiki/Barbican) _source_ charm. From the
[wiki](https://wiki.openstack.org/wiki/Barbican) 'Barbican is a REST API
designed for the secure storage, provisioning and management of secrets such as
passwords, encryption keys and X.509 Certificates. It is aimed at being useful
for all environments, including large ephemeral Clouds.'
# Plugins
The Barbican charm currently supports the following plugins:
- [charm-barbican-softhsm-plugin](https://github.com/openstack/charm-barbican-softhsm-plugin)
# Creating the primary MKEK and primary HMAC
Barbican (can use|uses) a Master Key Encryption Key (MKEK) scheme to wrap other
keys so that in the course of issuing new encryption keys, it doesn't exhaust
the storage capacity of an HSM.
See [KMIP MKEK Model
Plugin](https://specs.openstack.org/openstack/barbican-specs/specs/kilo/barbican-mkek-model.html)
for more details.
Barbican itself can generate the MKEK and HMAC keys and store them in the
associated HSM through the use of two actions 'generate-mkek' and
'generate-hmac'.
The names of the keys are stored in the configuration for the service as
'mkek-label' and 'hmac-label'. These default to 'primarymkek' and
'primaryhmac' respectively.
Note that these keys are not recoverable _from_ the HSM. If the HSM has
already been configured with these keys then these actions would overwrite the
existing key. So only use them for the initial implementation or to change the
MKEK and HMAC keys in the HSM.
## Use of actions
For juju 1.x:
```bash
juju action do generate-mkek
```
For juju 2.x:
```bash
juju run-action generate-mkek
```
Note that, depending on the HSM, it may only be possible to do this ONCE as the
HSM may reject setting up the keys more than once.
# Developer Notes
The Barbican charm has to be able to set `[crypto]` and `[xxx_plugin]` sections
in the `barbican-api.conf` file. This data comes via the `barbican-hsm-plugin`
interface from a charm (probably a subordinate) that provides the interface.
On the `barbican-hsm-interface` the data is provided in the `plugin_data()`
method of the interface (or if it is adapted) in the `plugin_data` property.
The theory of operation for the crypto plugin is that a local library that
supports the PKCS#11 interface that Barbican can talk to locally.
Note(AJK): it is not clear yet how a clustered Barbican can be created with
a single HSM backend. It's likely to be a separate piece of hardward with
a local library that talks to it.
In order for Barbican to be configured for the example softhsm2 library, the
configuration file needs to include the entries:
```ini
[crypto]
enabled_crypto_plugins = p11_crypto
[p11_crypto_plugin]
library_path = '/usr/lib/libCryptoki2_64.so'
login = 'catt'
mkek_label = 'primarymkek'
mkek_length = 32
hmac_label = 'primaryhmac' slot_id = <slot_id>
```
Note that the /var/lib/softhsm/tokens directory HAS to exist as otherwise the
softhsm2-util command won't work.

View File

@ -1,13 +0,0 @@
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.

8
src/actions.yaml Normal file
View File

@ -0,0 +1,8 @@
generate-mkek:
description: |
Generate an MKEK in the associated HSM (via the barbican-hsm-plugin
interface).
generate-hmac:
description: |
Generate an HMAC in the associated HSM (via the barbican-hsm-plugin
interface).

82
src/actions/actions.py Executable file
View File

@ -0,0 +1,82 @@
#!/usr/bin/env python3
# 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 sys
# Load modules from $CHARM_DIR/lib
sys.path.append('lib')
from charms.layer import basic
basic.bootstrap_charm_deps()
basic.init_config_states()
import charms.reactive as reactive
import charmhelpers.core.hookenv as hookenv
import charm.openstack.barbican as barbican
def generate_mkek_action(*args):
"""Generate an MKEK in the backend HSM"""
# try and get the reactive relation instance for hsm:
# We only do this because there's no @action(<name>) yet that we could
# access from the reactive file.
hsm = reactive.RelationBase.from_state('hsm.available')
if hsm is None:
hookenv.action_fail(
"Can't generate an MKEK in associated HSM because HSM is not "
"available.")
return
barbican.generate_mkek(hsm)
def generate_hmac_action(*args):
"""Generate an HMAC in the backend HSM"""
# try and get the reactive relation instance for hsm:
# We only do this because there's no @action(<name>) yet that we could
# access from the reactive file.
hsm = reactive.RelationBase.from_state('hsm.available')
if hsm is None:
hookenv.action_fail(
"Can't generate an HMAC in associated HSM because HSM is not "
"available.")
barbican.generate_hmac(hsm)
# Actions to function mapping, to allow for illegal python action names that
# can map to a python function.
ACTIONS = {
"generate-mkek": generate_mkek_action,
"generate-hmac": generate_hmac_action,
}
def main(args):
action_name = os.path.basename(args[0])
try:
action = ACTIONS[action_name]
except KeyError:
return "Action %s undefined" % action_name
else:
try:
action(args)
except Exception as e:
hookenv.action_fail(str(e))
if __name__ == "__main__":
sys.exit(main(sys.argv))

1
src/actions/generate-hmac Symbolic link
View File

@ -0,0 +1 @@
actions.py

1
src/actions/generate-mkek Symbolic link
View File

@ -0,0 +1 @@
actions.py

View File

@ -46,3 +46,38 @@ options:
default: "3"
type: string
description: none, 2 or 3
require-hsm-plugin:
default: True
type: boolean
description: |
If True (the default) then the barbcian-worker process won't be fully
functional until an HSM is associated with the charm. The charm will
remain in the blocked state until an HSM is available.
label-mkek:
default: primarymkek
type: string
description: |
This is the label for the primary MKEK (Master Key Encryption Key) stored
in the HSM that is used by Barbican to wrap other encryption keys that
are provided to projects.
Note the assocated action 'generate-mkek' is used to create an MKEK when
initialising a system.
mkek-key-length:
default: 32
type: int
description: The length for generating an MKEK
label-hmac:
default: primaryhmac
type: string
description: |
This is the label for the primary HMAC (keyed-hash message authentication
code) stored in the HSM that is used by Barbican to wrap other HMACs that
are provided to projects.
Note the assocated action 'generate-hmac' is used to create an HMAC when
initialising a system.
hmac-key-length:
default: 32
type: int
description: The length for generating an HMAC

View File

@ -10,19 +10,8 @@ check_and_install() {
}
status-set maintenance "Install hook running"
if [[ $(lsb_release -sc) -eq "trusty" ]]; then
juju-log "Enabling cloud archive to work around old trusty tools"
# Add a random cloud archive for the Openstack python3 clients
add-apt-repository --yes ppa:ubuntu-cloud-archive/mitaka-staging
apt-get update
check_and_install 'python3-pip'
# The trusty version of tox is too low (tox version is 1.6, required is at least 2.3.1)
# pip install tox to get around this and die a little inside
pip3 install tox
else
juju-log "Installing tox"
check_and_install 'tox'
fi
juju-log "Installing tox"
check_and_install 'tox'
declare -a DEPS=('libssl-dev' 'libffi-dev' 'apt' 'python3-netaddr' 'python3-netifaces' 'python3-pip' 'python3-yaml' 'python-cinderclient' 'python-glanceclient' 'python-heatclient' 'python-keystoneclient' 'python-neutronclient' 'python-novaclient' 'python-swiftclient' 'python-ceilometerclient' 'openvswitch-test' 'python3-cinderclient' 'python3-glanceclient' 'python3-heatclient' 'python3-keystoneclient' 'python3-neutronclient' 'python3-novaclient' 'python3-swiftclient' 'python3-ceilometerclient')

View File

@ -1 +1,7 @@
includes: ['layer:openstack', 'interface:mysql-shared', 'interface:rabbitmq', 'interface:keystone']
includes:
- layer:openstack
- interface:mysql-shared
- interface:rabbitmq
- interface:keystone
- interface:barbican-hsm-plugin
repo: git@github.com:openstack-charmers/charm-barbican.git

View File

@ -0,0 +1,13 @@
# 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.

View File

@ -0,0 +1,13 @@
# 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.

View File

@ -0,0 +1,13 @@
# 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.

View File

@ -1,11 +1,28 @@
# 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.
# The barbican handlers class
# bare functions are provided to the reactive handlers to perform the functions
# needed on the class.
from __future__ import absolute_import
import charmhelpers.fetch
import subprocess
import charmhelpers.contrib.openstack.utils as ch_utils
import charmhelpers.core.hookenv as hookenv
import charmhelpers.core.unitdata as unitdata
import charmhelpers.fetch
import charms_openstack.charm
import charms_openstack.adapters
@ -13,10 +30,11 @@ import charms_openstack.ip as os_ip
PACKAGES = ['barbican-common', 'barbican-api', 'barbican-worker',
'python-mysqldb']
BARBICAN_DIR = '/etc/barbican'
BARBICAN_ADMIN_PASTE_CONF = "barbican-admin-paste.ini"
BARBICAN_API_CONF = "barbican-api.conf"
BARBICAN_API_PASTE_CONF = "barbican-api-paste.ini"
BARBICAN_DIR = '/etc/barbican/'
BARBICAN_CONF = BARBICAN_DIR + "barbican.conf"
BARBICAN_API_PASTE_CONF = BARBICAN_DIR + "barbican-api-paste.ini"
OPENSTACK_RELEASE_KEY = 'barbican-charm.openstack-release-version'
###
@ -26,12 +44,15 @@ def install():
"""Use the singleton from the BarbicanCharm to install the packages on the
unit
"""
unitdata.kv().unset(OPENSTACK_RELEASE_KEY)
BarbicanCharm.singleton.install()
def setup_endpoint(keystone):
"""When the keystone interface connects, register this unit in the keystone
catalogue.
:param keystone: instance of KeystoneRequires() class from i/f
"""
charm = BarbicanCharm.singleton
keystone.register_endpoints(charm.service_type,
@ -44,10 +65,26 @@ def setup_endpoint(keystone):
def render_configs(interfaces_list):
"""Using a list of interfaces, render the configs and, if they have
changes, restart the services on the unit.
:param interfaces_list: [RelationBase] interfaces from reactive
"""
BarbicanCharm.singleton.render_with_interfaces(interfaces_list)
def generate_mkek(hsm):
"""Ask barbican to generate an MKEK in the backend store using the HSM.
This assumes that an HSM is available, and configured. Uses the charm.
"""
BarbicanCharm.singleton.action_generate_mkek(hsm)
def generate_hmac(hsm):
"""Ask barbican to generate an HMAC in the backend store using the HSM.
This assumes that an HSM is available, and configured. Uses the charm.
"""
BarbicanCharm.singleton.action_generate_hmac(hsm)
def assess_status():
"""Just call the BarbicanCharm.singleton.assess_status() command to update
status on the unit.
@ -71,18 +108,59 @@ class BarbicanConfigurationAdapter(
@property
def barbican_api_keystone_pipeline(self):
if self.keystone_api_version == "2":
return 'keystone_authtoken context apiapp'
return 'cors keystone_authtoken context apiapp'
else:
return 'keystone_v3_authtoken context apiapp'
return 'cors keystone_v3_authtoken context apiapp'
@property
def barbican_api_pipeline(self):
return {
"2": "keystone_authtoken context apiapp",
"3": "keystone_v3_authtoken context apiapp",
"none": "unauthenticated-context apiapp"
"2": "cors keystone_authtoken context apiapp",
"3": "cors keystone_v3_authtoken context apiapp",
"none": "cors unauthenticated-context apiapp"
}[self.keystone_api_version]
@property
def barbican_api_keystone_audit_pipeline(self):
if self.keystone_api_version == "2":
return 'keystone_authtoken context audit apiapp'
else:
return 'keystone_v3_authtoken context audit apiapp'
class HSMAdapter(charms_openstack.adapters.OpenStackRelationAdapter):
"""Adapt the barbican-hsm-plugin relation for use in rendering the config
for Barbican. Note that the HSM relation is optional, so we have a class
variable 'exists' that we can test in the template to see if we should
render HSM parameters into the template.
"""
interface_type = 'hsm'
@property
def library_path(self):
"""Provide a library_path property to the template if it exists"""
try:
return self.relation.plugin_data['library_path']
except:
return ''
@property
def login(self):
"""Provide a login property to the template if it exists"""
try:
return self.relation.plugin_data['login']
except:
return ''
@property
def slot_id(self):
"""Provide a slot_id property to the template if it exists"""
try:
return self.relation.plugin_data['slot_id']
except:
return ''
class BarbicanAdapters(charms_openstack.adapters.OpenStackRelationAdapters):
"""
@ -91,9 +169,14 @@ class BarbicanAdapters(charms_openstack.adapters.OpenStackRelationAdapters):
This plumbs in the BarbicanConfigurationAdapter as the ConfigurationAdapter
to provide additional properties.
"""
relation_adapters = {
'hsm': HSMAdapter,
}
def __init__(self, relations):
super(BarbicanAdapters, self).__init__(
relations, options=BarbicanConfigurationAdapter)
relations, options_instance=BarbicanConfigurationAdapter())
class BarbicanCharm(charms_openstack.charm.OpenStackCharm):
@ -101,58 +184,126 @@ class BarbicanCharm(charms_openstack.charm.OpenStackCharm):
functionality to manage a barbican unit.
"""
release = 'liberty'
release = 'mitaka'
name = 'barbican'
packages = PACKAGES
api_ports = {
'barbican-api': {
'barbican-worker': {
os_ip.PUBLIC: 9311,
os_ip.ADMIN: 9312,
os_ip.INTERNAL: 9313,
os_ip.INTERNAL: 9311,
}
}
service_type = 'barbican'
default_service = 'barbican-api'
services = ['barbican-api', 'barbican-worker']
default_service = 'barbican-worker'
services = ['apache2', 'barbican-worker']
# Note that the hsm interface is optional - defined in config.yaml
required_relations = ['shared-db', 'amqp', 'identity-service']
restart_map = {
"{}/{}".format(BARBICAN_DIR, BARBICAN_API_CONF): services,
"{}/{}".format(BARBICAN_DIR, BARBICAN_ADMIN_PASTE_CONF): services,
"{}/{}".format(BARBICAN_DIR, BARBICAN_API_PASTE_CONF): services,
BARBICAN_CONF: services,
BARBICAN_API_PASTE_CONF: services,
}
adapters_class = BarbicanAdapters
def __init__(self, release=None, **kwargs):
"""Custom initialiser for class
If no release is passed, then the charm determines the release from the
ch_utils.os_release() function.
Note that the release_selector can be used to control which class is
instantiated.
"""
if release is None:
# release = ch_utils.os_release('barbican-common')
release = ch_utils.os_release('python-keystonemiddleware')
super(BarbicanCharm, self).__init__(release=release, **kwargs)
def install(self):
"""Customise the installation, configure the source and then call the
parent install() method to install the packages
"""
charmhelpers.fetch.add_source("ppa:gnuoy/barbican-alt")
# DEBUG - until seed random change lands into xenial cloud archive
# BUG #1599550 - barbican + softhsm2 + libssl1.0.0:
# pkcs11:_generate_random() fails
charmhelpers.fetch.add_source("ppa:ajkavanagh/barbican")
self.configure_source()
# and do the actual install
super(BarbicanCharm, self).install()
def action_generate_mkek(self, hsm):
"""Generate an MKEK on a connected HSM. Requires that an HSM is
avaiable via the barbican-hsm-plugin interface, generically known as
'hsm'.
Uses the barbican-manage command.
:param hsm: instance of BarbicanRequires() class from the
barbican-hsm-plugin interface
"""
plugin_data = hsm.plugin_data
cmd = [
'barbican-manage', 'hsm', 'gen_mkek',
'--library-path', plugin_data['library_path'],
'--passphrase', plugin_data['login'],
'--slot-id', plugin_data['slot_id'],
'--length', str(hookenv.config('mkek-key-length')),
'--label', hookenv.config('label-mkek'),
]
try:
subprocess.check_call(cmd)
hookenv.log("barbican-mangage hsm gen_mkek succeeded")
except subprocess.CalledProcessError:
str_err = "barbican-manage hsm gen_mkek failed."
hookenv.log(str_err)
raise Exception(str_err)
def action_generate_hmac(self, hsm):
"""Generate an HMAC on a connected HSM. Requires that an HSM is
avaiable via the barbican-hsm-plugin interface, generically known as
'hsm'.
Uses the barbican-manage command.
:param hsm: instance of BarbicanRequires() class from the
barbican-hsm-plugin interface
"""
plugin_data = hsm.plugin_data
cmd = [
'barbican-manage', 'hsm', 'gen_hmac',
'--library-path', plugin_data['library_path'],
'--passphrase', plugin_data['login'],
'--slot-id', plugin_data['slot_id'],
'--length', str(hookenv.config('hmac-key-length')),
'--label', hookenv.config('label-hmac'),
]
try:
subprocess.check_call(cmd)
hookenv.log("barbican-mangage hsm gen_hmac succeeded")
except subprocess.CalledProcessError:
str_err = "barbican-manage hsm gen_hmac failed."
hookenv.log(str_err)
raise Exception(str_err)
def states_to_check(self, required_relations=None):
"""Override the default states_to_check() for the assess_status
functionality so that, if we have to have an HSM relation, then enforce
it on the assess_status() call.
If param required_relations is not None then it overrides the
instance/class variable self.required_relations.
:param required_relations: [list of state names]
:returns: [states{} as per parent method]
"""
if required_relations is None:
required_relations = self.required_relations
if hookenv.config('require-hsm-plugin'):
required_relations.append('hsm')
return super(BarbicanCharm, self).states_to_check(
required_relations=required_relations)
# Determine the charm class by the supported release
@charms_openstack.charm.register_os_release_selector
def select_release():
"""Determine the release based on the python-keystonemiddleware that is
installed.
Note that this function caches the release after the first install so that
it doesn't need to keep going and getting it from the package information.
"""
return ch_utils.os_release('python-keystonemiddleware')
release_version = unitdata.kv().get(OPENSTACK_RELEASE_KEY, None)
if release_version is None:
release_version = ch_utils.os_release('python-keystonemiddleware')
unitdata.kv().set(OPENSTACK_RELEASE_KEY, release_version)
return release_version

View File

@ -18,3 +18,6 @@ requires:
interface: rabbitmq
identity-service:
interface: keystone
hsm:
interface: barbican-hsm-plugin
optional: true

View File

@ -0,0 +1,13 @@
# 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.

View File

@ -1,3 +1,17 @@
# 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.
# this is just for the reactive handlers and calls into the charm.
from __future__ import absolute_import
@ -48,6 +62,16 @@ def setup_endpoint(keystone):
@reactive.when('identity-service.available')
@reactive.when('amqp.available')
def render_stuff(*args):
"""Render the configuration for Barbican when all the interfaces are
available.
Note that the HSM interface is optional (hence the @when_any) and thus is
only used if it is available.
"""
# Get the optional hsm relation, if it is available for rendering.
hsm = reactive.RelationBase.from_state('hsm.available')
if hsm is not None:
args = args + (hsm, )
barbican.render_configs(args)
barbican.assess_status()

View File

@ -1,8 +0,0 @@
[pipeline:main]
pipeline = unauthenticated-context admin
[app:admin]
paste.app_factory = barbican.api.app:create_admin_app
[filter:unauthenticated-context]
paste.filter_factory = barbican.api.middleware.context:UnauthenticatedContextMiddleware.factory

View File

@ -5,24 +5,28 @@ use = egg:Paste#urlmap
# Use this pipeline for Barbican API - versions no authentication
[pipeline:barbican_version]
pipeline = versionapp
pipeline = cors versionapp
# Use this pipeline for Barbican API - DEFAULT no authentication
[pipeline:barbican_api]
####pipeline = simple apiapp
#pipeline = keystone_authtoken context apiapp
# pipeline = cors unauthenticated-context apiapp
pipeline = {{ options.barbican_api_pipeline }}
#Use this pipeline to activate a repoze.profile middleware and HTTP port,
# to provide profiling information for the REST API processing.
[pipeline:barbican-profile]
pipeline = unauthenticated-context egg:Paste#cgitb egg:Paste#httpexceptions profile apiapp
pipeline = cors unauthenticated-context egg:Paste#cgitb egg:Paste#httpexceptions profile apiapp
#Use this pipeline for keystone auth
[pipeline:barbican-api-keystone]
#pipeline = keystone_authtoken context apiapp
# pipeline = cors keystone_authtoken context apiapp
pipeline = {{ options.barbican_api_keystone_pipeline }}
#Use this pipeline for keystone auth with audit feature
[pipeline:barbican-api-keystone-audit]
# pipeline = keystone_authtoken context audit apiapp
pipeline = {{ options.barbican_api_keystone_audit_pipeline }}
[app:apiapp]
paste.app_factory = barbican.api.app:create_main_app
@ -38,6 +42,10 @@ paste.filter_factory = barbican.api.middleware.context:UnauthenticatedContextMid
[filter:context]
paste.filter_factory = barbican.api.middleware.context:ContextMiddleware.factory
[filter:audit]
paste.filter_factory = keystonemiddleware.audit:filter_factory
audit_map_file = /etc/barbican/api_audit_map.conf
[filter:keystone_authtoken]
paste.filter_factory = keystonemiddleware.auth_token:filter_factory
signing_dir = /var/lib/barbican/keystone-signing
@ -53,7 +61,7 @@ auth_version = v2.0
#delay_auth_decision = true
[filter:keystone_v3_authtoken]
paste.filter_factory = keystoneclient.middleware.auth_token:filter_factory
paste.filter_factory = keystonemiddleware.auth_token:filter_factory
signing_dir = /var/lib/barbican/keystone-signing
auth_host = {{ identity_service.auth_host }}
#need ability to re-auth a token, thus admin url
@ -74,3 +82,7 @@ discard_first_request = true
path = /__profile__
flush_at_shutdown = true
unwind = false
[filter:cors]
paste.filter_factory = oslo_middleware.cors:filter_factory
oslo_config_project = barbican

View File

@ -11,7 +11,7 @@ bind_host = 0.0.0.0
# Port to bind the API server to
bind_port = 9311
# Host name, for use in HATEOS-style references
# Host name, for use in HATEOAS-style references
# Note: Typically this would be the load balanced endpoint that clients would use
# communicate back with this service.
host_href = http://localhost:9311
@ -38,6 +38,10 @@ max_allowed_request_size_in_bytes = 1000000
#sql_connection = sqlite:///barbican.sqlite
# Note: For absolute addresses, use '////' slashes after 'sqlite:'
# Uncomment for a more global development environment
#sql_connection = sqlite:////var/lib/barbican/barbican.sqlite
# This is shared_db.uri from the shared_db relationship.
{% include "parts/database" %}
# Period in seconds after which SQLAlchemy should reestablish its connection
# to the database.
@ -48,19 +52,39 @@ max_allowed_request_size_in_bytes = 1000000
# before MySQL can drop the connection.
sql_idle_timeout = 3600
# Accepts a class imported from the sqlalchemy.pool module, and handles the
# details of building the pool for you. If commented out, SQLAlchemy
# will select based on the database dialect. Other options are QueuePool
# (for SQLAlchemy-managed connections) and NullPool (to disabled SQLAlchemy
# management of connections).
# See http://docs.sqlalchemy.org/en/latest/core/pooling.html for more details.
#sql_pool_class = QueuePool
# Show SQLAlchemy pool-related debugging output in logs (sets DEBUG log level
# output) if specified.
#sql_pool_logging = True
# Size of pool used by SQLAlchemy. This is the largest number of connections
# that will be kept persistently in the pool. Can be set to 0 to indicate no
# size limit. To disable pooling, use a NullPool with sql_pool_class instead.
# Comment out to allow SQLAlchemy to select the default.
#sql_pool_size = 5
# The maximum overflow size of the pool used by SQLAlchemy. When the number of
# checked-out connections reaches the size set in sql_pool_size, additional
# connections will be returned up to this limit. It follows then that the
# total number of simultaneous connections the pool will allow is
# sql_pool_size + sql_pool_max_overflow. Can be set to -1 to indicate no
# overflow limit, so no limit will be placed on the total number of concurrent
# connections. Comment out to allow SQLAlchemy to select the default.
#sql_pool_max_overflow = 10
# Default page size for the 'limit' paging URL parameter.
default_limit_paging = 10
# Maximum page size for the 'limit' paging URL parameter.
max_limit_paging = 100
# Number of Barbican API worker processes to start.
# On machines with more than one CPU increasing this value
# may improve performance (especially if using SSL with
# compression turned on). It is typically recommended to set
# this value to the number of CPUs present on your machine.
workers = 1
# Role used to identify an authenticated user as administrator
#admin_role = admin
@ -92,17 +116,27 @@ workers = 1
# Should be set to a random string of length 16, 24 or 32 bytes
#metadata_encryption_key = <16, 24 or 32 char registry metadata key>
# ============ Delayed Delete Options =============================
# ================= Queue Options - oslo.messaging ==========================
# Turn on/off delayed delete
delayed_delete = False
# rabbitmq-olso section from the rabbit-mq and releated relations.
{% include "parts/section-rabbitmq-oslo" %}
# Delayed delete time in seconds
scrub_time = 43200
# Directory that the scrubber will use to remind itself of what to delete
# Make sure this is also set in glance-scrubber.conf
scrubber_datadir = /var/lib/barbican/scrubber
# For HA, specify queue nodes in cluster as 'user@host:5672', comma delimited, ending with '/offset':
# For example: transport_url = rabbit://guest@192.168.50.8:5672,guest@192.168.50.9:5672/
# DO NOT USE THIS, due to '# FIXME(markmc): support multiple hosts' in oslo/messaging/_drivers/amqpdriver.py
# transport_url = rabbit://guest@localhost:5672/
# oslo notification driver for sending audit events via audit middleware.
# Meaningful only when middleware is enabled in barbican paste ini file.
# This is oslo config MultiStrOpt so can be defined multiple times in case
# there is need to route audit event to messaging as well as log.
# notification_driver = messagingv2
# notification_driver = log
# ======== OpenStack policy - oslo_policy ===============
[oslo_policy]
# ======== OpenStack policy integration
# JSON file representing policy (string value)
@ -112,17 +146,6 @@ policy_file=/etc/barbican/policy.json
policy_default_rule=default
{% include "parts/database" %}
# ================= Queue Options - oslo.messaging ==========================
{% include "parts/section-rabbitmq-oslo" %}
# For HA, specify queue nodes in cluster as 'user@host:5672', comma delimited, ending with '/offset':
# For example: transport_url = rabbit://guest@192.168.50.8:5672,guest@192.168.50.9:5672/
# DO NOT USE THIS, due to '# FIXME(markmc): support multiple hosts' in oslo/messaging/_drivers/amqpdriver.py
# transport_url = rabbit://guest@localhost:5672/
# ================= Queue Options - Application ==========================
[queue]
@ -142,6 +165,44 @@ version = '1.1'
# Server name for RPC service
server_name = 'barbican.queue'
# Number of asynchronous worker processes.
# When greater than 1, then that many additional worker processes are
# created for asynchronous worker functionality.
asynchronous_workers = 1
# ================= Retry/Scheduler Options ==========================
[retry_scheduler]
# Seconds (float) to wait between starting retry scheduler
initial_delay_seconds = 10.0
# Seconds (float) to wait between starting retry scheduler
periodic_interval_max_seconds = 10.0
# ====================== Quota Options ===============================
[quotas]
# For each resource, the default maximum number that can be used for
# a project is set below. This value can be overridden for each
# project through the API. A negative value means no limit. A zero
# value effectively disables the resource.
# default number of secrets allowed per project
quota_secrets = -1
# default number of orders allowed per project
quota_orders = -1
# default number of containers allowed per project
quota_containers = -1
# default number of consumers allowed per project
quota_consumers = -1
# default number of CAs allowed per project
quota_cas = -1
# ================= Keystone Notification Options - Application ===============
[keystone_notifications]
@ -187,7 +248,11 @@ enabled_secretstore_plugins = store_crypto
# ================= Crypto plugin ===================
[crypto]
namespace = barbican.crypto.plugin
{% if hsm -%}
enabled_crypto_plugins = p11_crypto
{% else -%}
enabled_crypto_plugins = simple_crypto
{%- endif %}
[simple_crypto_plugin]
# the kek should be a 32-byte value which is base64 encoded
@ -198,19 +263,40 @@ pem_path = '/etc/barbican/kra_admin_cert.pem'
dogtag_host = localhost
dogtag_port = 8443
nss_db_path = '/etc/barbican/alias'
nss_db_path_ca = '/etc/barbican/alias-ca'
nss_password = 'password123'
simple_cmc_profile = 'caOtherCert'
ca_expiration_time = 1
plugin_working_dir = '/etc/barbican/dogtag'
{% if hsm -%}
[p11_crypto_plugin]
# Path to vendor PKCS11 library
library_path = '/usr/lib/libCryptoki2_64.so'
library_path = {{ hsm.library_path }}
# Password to login to PKCS11 session
login = 'mypassword'
login = {{ hsm.login }}
# Label to identify master KEK in the HSM (must not be the same as HMAC label)
mkek_label = 'an_mkek'
mkek_label = {{ options.label_mkek }}
# Length in bytes of master KEK
mkek_length = 32
mkek_length = {{ options.mkek_key_length }}
# Label to identify HMAC key in the HSM (must not be the same as MKEK label)
hmac_label = 'my_hmac_label'
hmac_label = {{ options.label_hmac }}
# HSM Slot id (Should correspond to a configured PKCS11 slot). Default: 1
slot_id = {{ hsm.slot_id }}
# Enable Read/Write session with the HSM?
# rw_session = True
# Length of Project KEKs to create
# pkek_length = 32
# How long to cache unwrapped Project KEKs
# pkek_cache_ttl = 900
# Max number of items in pkek cache
# pkek_cache_limit = 100
# Seedfile to generate random data from.
seed_file = /dev/random
# Seed length to read the random data for seeding the RNG
seed_length = 32
{%- endif %}
# ================== KMIP plugin =====================
@ -218,13 +304,84 @@ hmac_label = 'my_hmac_label'
username = 'admin'
password = 'password'
host = localhost
port = 9090
port = 5696
keyfile = '/path/to/certs/cert.key'
certfile = '/path/to/certs/cert.crt'
ca_certs = '/path/to/certs/LocalCA.crt'
# ================= Certificate plugin ===================
[certificate]
namespace = barbican.certificate.plugin
enabled_certificate_plugins = simple_certificate
enabled_certificate_plugins = snakeoil_ca
[certificate_event]
namespace = barbican.certificate.event.plugin
enabled_certificate_event_plugins = simple_certificate
enabled_certificate_event_plugins = simple_certificate_event
[snakeoil_ca_plugin]
ca_cert_path = /etc/barbican/snakeoil-ca.crt
ca_cert_key_path = /etc/barbican/snakeoil-ca.key
ca_cert_chain_path = /etc/barbican/snakeoil-ca.chain
ca_cert_pkcs7_path = /etc/barbican/snakeoil-ca.p7b
subca_cert_key_directory=/etc/barbican/snakeoil-cas
[cors]
#
# From oslo.middleware.cors
#
# Indicate whether this resource may be shared with the domain
# received in the requests "origin" header. (list value)
#allowed_origin = <None>
# Indicate that the actual request can include user credentials
# (boolean value)
#allow_credentials = true
# Indicate which headers are safe to expose to the API. Defaults to
# HTTP Simple Headers. (list value)
#expose_headers = X-Auth-Token, X-Openstack-Request-Id, X-Project-Id, X-Identity-Status, X-User-Id, X-Storage-Token, X-Domain-Id, X-User-Domain-Id, X-Project-Domain-Id, X-Roles
# Maximum cache age of CORS preflight requests. (integer value)
#max_age = 3600
# Indicate which methods can be used during the actual request. (list
# value)
#allow_methods = GET,PUT,POST,DELETE,PATCH
# Indicate which header field names may be used during the actual
# request. (list value)
#allow_headers = X-Auth-Token, X-Openstack-Request-Id, X-Project-Id, X-Identity-Status, X-User-Id, X-Storage-Token, X-Domain-Id, X-User-Domain-Id, X-Project-Domain-Id, X-Roles
[cors.subdomain]
#
# From oslo.middleware.cors
#
# Indicate whether this resource may be shared with the domain
# received in the requests "origin" header. (list value)
#allowed_origin = <None>
# Indicate that the actual request can include user credentials
# (boolean value)
#allow_credentials = true
# Indicate which headers are safe to expose to the API. Defaults to
# HTTP Simple Headers. (list value)
#expose_headers = X-Auth-Token, X-Openstack-Request-Id, X-Project-Id, X-Identity-Status, X-User-Id, X-Storage-Token, X-Domain-Id, X-User-Domain-Id, X-Project-Domain-Id, X-Roles
# Maximum cache age of CORS preflight requests. (integer value)
#max_age = 3600
# Indicate which methods can be used during the actual request. (list
# value)
#allow_methods = GET,PUT,POST,DELETE,PATCH
# Indicate which header field names may be used during the actual
# request. (list value)
#allow_headers = X-Auth-Token, X-Openstack-Request-Id, X-Project-Id, X-Identity-Status, X-User-Id, X-Storage-Token, X-Domain-Id, X-User-Domain-Id, X-Project-Domain-Id, X-Roles

View File

@ -3,4 +3,4 @@ os-testr>=0.4.1
charms.reactive
mock>=1.2
coverage>=3.6
git+https://github.com/ajkavanagh/charm.openstack#egg=charms-openstack
git+https://github.com/ajkavanagh/charms.openstack.git#egg=charms-openstack

View File

@ -9,6 +9,7 @@ setenv = VIRTUAL_ENV={envdir}
TERM=linux
INTERFACE_PATH={toxinidir}/interfaces
LAYER_PATH={toxinidir}/layers
INTERFACE_PATH={toxinidir}/interfaces
JUJU_REPOSITORY={toxinidir}/build
passenv = http_proxy https_proxy
install_command =
@ -19,12 +20,7 @@ deps =
[testenv:build]
basepython = python2.7
commands =
charm build --log-level DEBUG -o {toxinidir}/build src
[testenv:py27]
basepython = python2.7
deps = -r{toxinidir}/test-requirements.txt
commands = ostestr {posargs}
charm-build --log-level DEBUG -o {toxinidir}/build src {posargs}
[testenv:py34]
basepython = python3.4

View File

@ -1,3 +1,17 @@
# 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 sys
import mock
@ -11,6 +25,7 @@ sys.modules['charmhelpers'] = charmhelpers
sys.modules['charmhelpers.core'] = charmhelpers.core
sys.modules['charmhelpers.core.hookenv'] = charmhelpers.core.hookenv
sys.modules['charmhelpers.core.host'] = charmhelpers.core.host
sys.modules['charmhelpers.core.unitdata'] = charmhelpers.core.unitdata
sys.modules['charmhelpers.core.templating'] = charmhelpers.core.templating
sys.modules['charmhelpers.contrib'] = charmhelpers.contrib
sys.modules['charmhelpers.contrib.openstack'] = charmhelpers.contrib.openstack

View File

@ -1,3 +1,17 @@
# 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.
from __future__ import absolute_import
from __future__ import print_function
@ -93,6 +107,7 @@ class TestBarbicanHandlers(unittest.TestCase):
'render_stuff': ('shared-db.available',
'identity-service.available',
'amqp.available',),
'config_changed': ('config.changed', ),
}
when_not_patterns = {
'install_packages': ('charm.installed', ),
@ -102,6 +117,7 @@ class TestBarbicanHandlers(unittest.TestCase):
(_when_not_args, when_not_patterns)]:
for f, args in t.items():
# check that function is in patterns
# print("f: {}, args: {}".format(f, args))
self.assertTrue(f in p.keys())
# check that the lists are equal
l = [a['args'][0] for a in args]
@ -122,9 +138,11 @@ class TestBarbicanHandlers(unittest.TestCase):
'rabbit-vhost': 'vhost1',
}
self.config.side_effect = lambda x: reply[x]
self.patch(handlers.barbican, 'assess_status')
handlers.setup_amqp_req(amqp)
amqp.request_access.assert_called_once_with(
username='user1', vhost='vhost1')
self.assess_status.assert_called_once_with()
def test_database(self):
database = mock.MagicMock()
@ -135,16 +153,25 @@ class TestBarbicanHandlers(unittest.TestCase):
}
self.config.side_effect = lambda x: reply[x]
self.patch(handlers.hookenv, 'unit_private_ip', 'private_ip')
self.patch(handlers.barbican, 'assess_status')
handlers.setup_database(database)
database.configure.assert_called_once_with(
'db1', 'dbuser1', 'private_ip')
self.assess_status.assert_called_once_with()
def test_setup_endpoint(self):
self.patch(handlers.barbican, 'setup_endpoint')
self.patch(handlers.barbican, 'assess_status')
handlers.setup_endpoint('endpoint_object')
self.setup_endpoint.assert_called_once_with('endpoint_object')
self.assess_status.assert_called_once_with()
def test_render_stuff(self):
self.patch(handlers.barbican, 'render_configs')
self.patch(handlers.barbican, 'assess_status')
self.patch(handlers.reactive.RelationBase, 'from_state',
return_value='hsm')
handlers.render_stuff('arg1', 'arg2')
self.render_configs.assert_called_once_with(('arg1', 'arg2', ))
self.render_configs.assert_called_once_with(('arg1', 'arg2', 'hsm'))
self.assess_status.assert_called_once_with()
self.from_state.assert_called_once_with('hsm.available')

View File

@ -1,3 +1,17 @@
# 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.
from __future__ import absolute_import
from __future__ import print_function
@ -13,6 +27,11 @@ class Helper(unittest.TestCase):
def setUp(self):
self._patches = {}
self._patches_start = {}
# patch out the select_release to always return 'mitaka'
self.patch(barbican.unitdata, 'kv')
_getter = mock.MagicMock()
_getter.get.return_value = barbican.BarbicanCharm.release
self.kv.return_value = _getter
def tearDown(self):
for k, v in self._patches.items():
@ -76,23 +95,23 @@ class TestBarbicanConfigurationAdapter(Helper):
# Make one with no errors, api version 2
a = barbican.BarbicanConfigurationAdapter()
self.assertEqual(a.barbican_api_keystone_pipeline,
'keystone_authtoken context apiapp')
'cors keystone_authtoken context apiapp')
self.assertEqual(a.barbican_api_pipeline,
'keystone_authtoken context apiapp')
'cors keystone_authtoken context apiapp')
# Now test it with api version 3
reply['keystone-api-version'] = '3'
a = barbican.BarbicanConfigurationAdapter()
self.assertEqual(a.barbican_api_keystone_pipeline,
'keystone_v3_authtoken context apiapp')
'cors keystone_v3_authtoken context apiapp')
self.assertEqual(a.barbican_api_pipeline,
'keystone_v3_authtoken context apiapp')
'cors keystone_v3_authtoken context apiapp')
# and a 'none' version
reply['keystone-api-version'] = 'none'
a = barbican.BarbicanConfigurationAdapter()
self.assertEqual(a.barbican_api_keystone_pipeline,
'keystone_v3_authtoken context apiapp')
'cors keystone_v3_authtoken context apiapp')
self.assertEqual(a.barbican_api_pipeline,
'unauthenticated-context apiapp')
'cors unauthenticated-context apiapp')
# finally, try to create an invalid one.
reply['keystone-api-version'] = None
with self.assertRaises(ValueError):
@ -129,19 +148,5 @@ class TestBarbicanAdapters(Helper):
class TestBarbicanCharm(Helper):
def test__init__(self):
self.patch(barbican.ch_utils, 'os_release')
barbican.BarbicanCharm()
self.os_release.assert_called_once_with('python-keystonemiddleware')
def test_install(self):
self.patch(barbican.charmhelpers.fetch, 'add_source')
b = barbican.BarbicanCharm()
self.patch(barbican.charms_openstack.charm.OpenStackCharm,
'configure_source')
self.patch(barbican.charms_openstack.charm.OpenStackCharm,
'install')
b.install()
self.add_source.assert_called_once_with('ppa:gnuoy/barbican-alt')
self.configure_source.assert_called_once_with()
self.install.assert_called_once_with()
# tests to be added
pass