Create a trustee user for each bay
Docker registry, k8s load balancer and volume driver have a similar need to use trust, so we need to create a trustee for each bay. Change-Id: If034e74ce2ea80a7faa886d4edf789e576c30eb5 Partially-Implements: blueprint create-trustee-user-for-each-bay
This commit is contained in:
parent
18a6cf6f97
commit
725bd5c99d
|
@ -55,6 +55,8 @@ MAGNUM_SERVICE_PORT=${MAGNUM_SERVICE_PORT:-9511}
|
||||||
MAGNUM_SERVICE_PORT_INT=${MAGNUM_SERVICE_PORT_INT:-19511}
|
MAGNUM_SERVICE_PORT_INT=${MAGNUM_SERVICE_PORT_INT:-19511}
|
||||||
MAGNUM_SERVICE_PROTOCOL=${MAGNUM_SERVICE_PROTOCOL:-$SERVICE_PROTOCOL}
|
MAGNUM_SERVICE_PROTOCOL=${MAGNUM_SERVICE_PROTOCOL:-$SERVICE_PROTOCOL}
|
||||||
|
|
||||||
|
MAGNUM_TRUSTEE_DOMAIN_ADMIN_PASSWORD=${MAGNUM_TRUSTEE_DOMAIN_ADMIN_PASSWORD:-secret}
|
||||||
|
|
||||||
# Support entry points installation of console scripts
|
# Support entry points installation of console scripts
|
||||||
if [[ -d $MAGNUM_DIR/bin ]]; then
|
if [[ -d $MAGNUM_DIR/bin ]]; then
|
||||||
MAGNUM_BIN_DIR=$MAGNUM_DIR/bin
|
MAGNUM_BIN_DIR=$MAGNUM_DIR/bin
|
||||||
|
@ -191,6 +193,16 @@ function create_magnum_conf {
|
||||||
iniset $MAGNUM_CONF certificates storage_path "$MAGNUM_LOCAL_CERT_DIR"
|
iniset $MAGNUM_CONF certificates storage_path "$MAGNUM_LOCAL_CERT_DIR"
|
||||||
iniset $MAGNUM_CONF certificates cert_manager_type "local"
|
iniset $MAGNUM_CONF certificates cert_manager_type "local"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
trustee_domain_id=$(get_or_create_domain magnum 'Owns users and projects created by magnum')
|
||||||
|
trustee_domain_admin_id=$(get_or_create_user trustee_domain_admin $MAGNUM_TRUSTEE_DOMAIN_ADMIN_PASSWORD $trustee_domain_id)
|
||||||
|
openstack --os-auth-url $KEYSTONE_SERVICE_URI_V3 \
|
||||||
|
--os-identity-api-version 3 role add \
|
||||||
|
--user $trustee_domain_admin_id --domain $trustee_domain_id \
|
||||||
|
admin
|
||||||
|
iniset $MAGNUM_CONF trust trustee_domain_id $trustee_domain_id
|
||||||
|
iniset $MAGNUM_CONF trust trustee_domain_admin_id $trustee_domain_admin_id
|
||||||
|
iniset $MAGNUM_CONF trust trustee_domain_admin_password $MAGNUM_TRUSTEE_DOMAIN_ADMIN_PASSWORD
|
||||||
}
|
}
|
||||||
|
|
||||||
function update_heat_policy {
|
function update_heat_policy {
|
||||||
|
|
|
@ -44,7 +44,8 @@ class BayPatchType(types.JsonPatchType):
|
||||||
internal_attrs = ['/api_address', '/node_addresses',
|
internal_attrs = ['/api_address', '/node_addresses',
|
||||||
'/master_addresses', '/stack_id',
|
'/master_addresses', '/stack_id',
|
||||||
'/ca_cert_ref', '/magnum_cert_ref',
|
'/ca_cert_ref', '/magnum_cert_ref',
|
||||||
'/registry_trust_id']
|
'/trust_id', '/trustee_user_name',
|
||||||
|
'/trustee_password', '/trustee_user_id']
|
||||||
return types.JsonPatchType.internal_attrs() + internal_attrs
|
return types.JsonPatchType.internal_attrs() + internal_attrs
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -547,3 +547,11 @@ class TrustCreateFailed(MagnumException):
|
||||||
|
|
||||||
class TrustDeleteFailed(MagnumException):
|
class TrustDeleteFailed(MagnumException):
|
||||||
message = _("Failed to delete trust %(trust_id)s.")
|
message = _("Failed to delete trust %(trust_id)s.")
|
||||||
|
|
||||||
|
|
||||||
|
class TrusteeCreateFailed(MagnumException):
|
||||||
|
message = _("Failed to create trustee %(username) in domain $(domain_id)")
|
||||||
|
|
||||||
|
|
||||||
|
class TrusteeDeleteFailed(MagnumException):
|
||||||
|
message = _("Failed to delete trustee %(trustee_id)")
|
||||||
|
|
|
@ -10,17 +10,37 @@
|
||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
|
from keystoneclient.auth.identity import v3
|
||||||
import keystoneclient.exceptions as kc_exception
|
import keystoneclient.exceptions as kc_exception
|
||||||
|
from keystoneclient import session
|
||||||
from keystoneclient.v3 import client as kc_v3
|
from keystoneclient.v3 import client as kc_v3
|
||||||
from oslo_config import cfg
|
from oslo_config import cfg
|
||||||
from oslo_log import log as logging
|
from oslo_log import log as logging
|
||||||
|
|
||||||
from magnum.common import exception
|
from magnum.common import exception
|
||||||
|
from magnum.common import utils
|
||||||
|
from magnum.i18n import _
|
||||||
from magnum.i18n import _LE
|
from magnum.i18n import _LE
|
||||||
|
|
||||||
|
|
||||||
CONF = cfg.CONF
|
CONF = cfg.CONF
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
trust_opts = [
|
||||||
|
cfg.StrOpt('trustee_domain_id',
|
||||||
|
help=_('Id of the domain to create trustee for bays')),
|
||||||
|
cfg.StrOpt('trustee_domain_admin_id',
|
||||||
|
help=_('Id of the admin with roles sufficient to manage users'
|
||||||
|
' in the trustee_domain')),
|
||||||
|
cfg.StrOpt('trustee_domain_admin_password',
|
||||||
|
help=_('Password of trustee_domain_admin')),
|
||||||
|
cfg.ListOpt('roles',
|
||||||
|
default=[],
|
||||||
|
help=_('The roles which are delegated to the trustee '
|
||||||
|
'by the trustor'))
|
||||||
|
]
|
||||||
|
|
||||||
|
CONF.register_opts(trust_opts, group='trust')
|
||||||
CONF.import_group('keystone_authtoken', 'keystonemiddleware.auth_token')
|
CONF.import_group('keystone_authtoken', 'keystonemiddleware.auth_token')
|
||||||
|
|
||||||
|
|
||||||
|
@ -31,6 +51,7 @@ class KeystoneClientV3(object):
|
||||||
self.context = context
|
self.context = context
|
||||||
self._client = None
|
self._client = None
|
||||||
self._admin_client = None
|
self._admin_client = None
|
||||||
|
self._domain_admin_client = None
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def auth_url(self):
|
def auth_url(self):
|
||||||
|
@ -74,6 +95,18 @@ class KeystoneClientV3(object):
|
||||||
**admin_credentials)
|
**admin_credentials)
|
||||||
return self._admin_client
|
return self._admin_client
|
||||||
|
|
||||||
|
@property
|
||||||
|
def domain_admin_client(self):
|
||||||
|
if not self._domain_admin_client:
|
||||||
|
auth = v3.Password(
|
||||||
|
auth_url=self.auth_url,
|
||||||
|
user_id=CONF.trust.trustee_domain_admin_id,
|
||||||
|
domain_id=CONF.trust.trustee_domain_id,
|
||||||
|
password=CONF.trust.trustee_domain_admin_password)
|
||||||
|
sess = session.Session(auth=auth)
|
||||||
|
self._domain_admin_client = kc_v3.Client(session=sess)
|
||||||
|
return self._domain_admin_client
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _is_v2_valid(auth_token_info):
|
def _is_v2_valid(auth_token_info):
|
||||||
return 'access' in auth_token_info
|
return 'access' in auth_token_info
|
||||||
|
@ -110,26 +143,29 @@ class KeystoneClientV3(object):
|
||||||
|
|
||||||
return kc_v3.Client(**kwargs)
|
return kc_v3.Client(**kwargs)
|
||||||
|
|
||||||
def create_trust(self, trustee_user, role_names, impersonation=True):
|
def create_trust(self, trustee_user):
|
||||||
trustor_user_id = self.client.auth_ref.user_id
|
trustor_user_id = self.client.auth_ref.user_id
|
||||||
trustor_project_id = self.client.auth_ref.project_id
|
trustor_project_id = self.client.auth_ref.project_id
|
||||||
|
|
||||||
|
# inherit the role of the trustor, unless set CONF.trust.roles
|
||||||
|
if CONF.trust.roles:
|
||||||
|
roles = CONF.trust.roles
|
||||||
|
else:
|
||||||
|
roles = self.context.roles
|
||||||
|
|
||||||
try:
|
try:
|
||||||
trust = self.client.trusts.create(
|
trust = self.client.trusts.create(
|
||||||
trustor_user=trustor_user_id,
|
trustor_user=trustor_user_id,
|
||||||
project=trustor_project_id,
|
project=trustor_project_id,
|
||||||
trustee_user=trustee_user,
|
trustee_user=trustee_user,
|
||||||
impersonation=impersonation,
|
impersonation=True,
|
||||||
role_names=role_names)
|
role_names=roles)
|
||||||
except Exception:
|
except Exception:
|
||||||
LOG.exception(_LE('Failed to create trust'))
|
LOG.exception(_LE('Failed to create trust'))
|
||||||
raise exception.TrustCreateFailed(
|
raise exception.TrustCreateFailed(
|
||||||
trustee_user_id=trustee_user)
|
trustee_user_id=trustee_user)
|
||||||
return trust
|
return trust
|
||||||
|
|
||||||
def create_trust_to_admin(self, role_names, impersonation=True):
|
|
||||||
trustee_user = self.admin_client.auth_ref.user_id
|
|
||||||
return self.create_trust(trustee_user, role_names, impersonation)
|
|
||||||
|
|
||||||
def delete_trust(self, trust_id):
|
def delete_trust(self, trust_id):
|
||||||
if trust_id is None:
|
if trust_id is None:
|
||||||
return
|
return
|
||||||
|
@ -140,3 +176,25 @@ class KeystoneClientV3(object):
|
||||||
except Exception:
|
except Exception:
|
||||||
LOG.exception(_LE('Failed to delete trust'))
|
LOG.exception(_LE('Failed to delete trust'))
|
||||||
raise exception.TrustDeleteFailed(trust_id=trust_id)
|
raise exception.TrustDeleteFailed(trust_id=trust_id)
|
||||||
|
|
||||||
|
def create_trustee(self, username, password, domain_id):
|
||||||
|
password = utils.generate_password(length=18)
|
||||||
|
try:
|
||||||
|
user = self.domain_admin_client.users.create(
|
||||||
|
name=username,
|
||||||
|
password=password,
|
||||||
|
domain=domain_id)
|
||||||
|
except Exception:
|
||||||
|
LOG.exception(_LE('Failed to create trustee'))
|
||||||
|
raise exception.TrusteeCreateFailed(username=username,
|
||||||
|
domain_id=domain_id)
|
||||||
|
return user
|
||||||
|
|
||||||
|
def delete_trustee(self, trustee_id):
|
||||||
|
try:
|
||||||
|
self.domain_admin_client.users.delete(trustee_id)
|
||||||
|
except kc_exception.NotFound:
|
||||||
|
pass
|
||||||
|
except Exception:
|
||||||
|
LOG.exception(_LE('Failed to delete trustee'))
|
||||||
|
raise exception.TrusteeDeleteFailed(trustee_id=trustee_id)
|
||||||
|
|
|
@ -41,6 +41,13 @@ from magnum.i18n import _
|
||||||
from magnum.i18n import _LE
|
from magnum.i18n import _LE
|
||||||
from magnum.i18n import _LW
|
from magnum.i18n import _LW
|
||||||
|
|
||||||
|
|
||||||
|
# Default symbols to use for passwords. Avoids visually confusing characters.
|
||||||
|
# ~6 bits per symbol
|
||||||
|
DEFAULT_PASSWORD_SYMBOLS = ['23456789', # Removed: 0,1
|
||||||
|
'ABCDEFGHJKLMNPQRSTUVWXYZ', # Removed: I, O
|
||||||
|
'abcdefghijkmnopqrstuvwxyz'] # Removed: l
|
||||||
|
|
||||||
UTILS_OPTS = [
|
UTILS_OPTS = [
|
||||||
cfg.StrOpt('rootwrap_config',
|
cfg.StrOpt('rootwrap_config',
|
||||||
default="/etc/magnum/rootwrap.conf",
|
default="/etc/magnum/rootwrap.conf",
|
||||||
|
@ -48,6 +55,9 @@ UTILS_OPTS = [
|
||||||
'running commands as root.'),
|
'running commands as root.'),
|
||||||
cfg.StrOpt('tempdir',
|
cfg.StrOpt('tempdir',
|
||||||
help='Explicitly specify the temporary working directory.'),
|
help='Explicitly specify the temporary working directory.'),
|
||||||
|
cfg.ListOpt('password_symbols',
|
||||||
|
default=DEFAULT_PASSWORD_SYMBOLS,
|
||||||
|
help='Symbols to use for passwords')
|
||||||
]
|
]
|
||||||
|
|
||||||
CONF = cfg.CONF
|
CONF = cfg.CONF
|
||||||
|
@ -565,3 +575,40 @@ def get_memory_bytes(memory):
|
||||||
return float(signed_number) * (10 ** float(suffix[1:]))
|
return float(signed_number) * (10 ** float(suffix[1:]))
|
||||||
else:
|
else:
|
||||||
raise exception.UnsupportedK8sMemoryFormat()
|
raise exception.UnsupportedK8sMemoryFormat()
|
||||||
|
|
||||||
|
|
||||||
|
def generate_password(length, symbolgroups=None):
|
||||||
|
"""Generate a random password from the supplied symbol groups.
|
||||||
|
|
||||||
|
At least one symbol from each group will be included. Unpredictable
|
||||||
|
results if length is less than the number of symbol groups.
|
||||||
|
|
||||||
|
Believed to be reasonably secure (with a reasonable password length!)
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
if symbolgroups is None:
|
||||||
|
symbolgroups = CONF.password_symbols
|
||||||
|
|
||||||
|
r = random.SystemRandom()
|
||||||
|
|
||||||
|
# NOTE(jerdfelt): Some password policies require at least one character
|
||||||
|
# from each group of symbols, so start off with one random character
|
||||||
|
# from each symbol group
|
||||||
|
password = [r.choice(s) for s in symbolgroups]
|
||||||
|
# If length < len(symbolgroups), the leading characters will only
|
||||||
|
# be from the first length groups. Try our best to not be predictable
|
||||||
|
# by shuffling and then truncating.
|
||||||
|
r.shuffle(password)
|
||||||
|
password = password[:length]
|
||||||
|
length -= len(password)
|
||||||
|
|
||||||
|
# then fill with random characters from all symbol groups
|
||||||
|
symbols = ''.join(symbolgroups)
|
||||||
|
password.extend([r.choice(symbols) for _i in range(length)])
|
||||||
|
|
||||||
|
# finally shuffle to ensure first x characters aren't from a
|
||||||
|
# predictable group
|
||||||
|
r.shuffle(password)
|
||||||
|
|
||||||
|
return ''.join(password)
|
||||||
|
|
|
@ -24,6 +24,7 @@ import six
|
||||||
from magnum.common import clients
|
from magnum.common import clients
|
||||||
from magnum.common import exception
|
from magnum.common import exception
|
||||||
from magnum.common import short_id
|
from magnum.common import short_id
|
||||||
|
from magnum.common import utils
|
||||||
from magnum.conductor.handlers.common import cert_manager
|
from magnum.conductor.handlers.common import cert_manager
|
||||||
from magnum.conductor import scale_manager
|
from magnum.conductor import scale_manager
|
||||||
from magnum.conductor.template_definition import TemplateDefinition as TDef
|
from magnum.conductor.template_definition import TemplateDefinition as TDef
|
||||||
|
@ -53,19 +54,10 @@ bay_heat_opts = [
|
||||||
'interval is in minutes. The default is no timeout.'))
|
'interval is in minutes. The default is no timeout.'))
|
||||||
]
|
]
|
||||||
|
|
||||||
docker_registry_opts = [
|
|
||||||
cfg.StrOpt('trustee_user_id',
|
|
||||||
default=None,
|
|
||||||
help='User id of the trustee'),
|
|
||||||
cfg.ListOpt('trust_roles',
|
|
||||||
default=['registry_user'],
|
|
||||||
help='The roles which are delegated to the trustee '
|
|
||||||
'by the trustor.')
|
|
||||||
]
|
|
||||||
|
|
||||||
CONF = cfg.CONF
|
CONF = cfg.CONF
|
||||||
CONF.register_opts(bay_heat_opts, group='bay_heat')
|
CONF.register_opts(bay_heat_opts, group='bay_heat')
|
||||||
CONF.register_opts(docker_registry_opts, 'docker_registry')
|
CONF.import_opt('trustee_domain_id', 'magnum.common.keystone',
|
||||||
|
group='trust')
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@ -127,6 +119,19 @@ class Handler(object):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super(Handler, self).__init__()
|
super(Handler, self).__init__()
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _create_trustee_and_trust(osc, bay):
|
||||||
|
password = utils.generate_password(length=18)
|
||||||
|
trustee = osc.keystone().create_trustee(
|
||||||
|
bay.uuid,
|
||||||
|
password,
|
||||||
|
CONF.trust.trustee_domain_id)
|
||||||
|
bay.trustee_username = trustee.name
|
||||||
|
bay.trustee_user_id = trustee.id
|
||||||
|
bay.trustee_password = password
|
||||||
|
trust = osc.keystone().create_trust(trustee.id)
|
||||||
|
bay.trust_id = trust.id
|
||||||
|
|
||||||
# Bay Operations
|
# Bay Operations
|
||||||
|
|
||||||
def bay_create(self, context, bay, bay_create_timeout):
|
def bay_create(self, context, bay, bay_create_timeout):
|
||||||
|
@ -134,17 +139,10 @@ class Handler(object):
|
||||||
|
|
||||||
osc = clients.OpenStackClients(context)
|
osc = clients.OpenStackClients(context)
|
||||||
|
|
||||||
baymodel = objects.BayModel.get_by_uuid(context,
|
bay.uuid = uuid.uuid4()
|
||||||
bay.baymodel_id)
|
self._create_trustee_and_trust(osc, bay)
|
||||||
if baymodel.registry_enabled:
|
|
||||||
trust = osc.keystone().create_trust(
|
|
||||||
CONF.docker_registry.trustee_user_id,
|
|
||||||
CONF.docker_registry.trust_roles)
|
|
||||||
bay.registry_trust_id = trust.id
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# Generate certificate and set the cert reference to bay
|
# Generate certificate and set the cert reference to bay
|
||||||
bay.uuid = uuid.uuid4()
|
|
||||||
cert_manager.generate_certificates_to_bay(bay)
|
cert_manager.generate_certificates_to_bay(bay)
|
||||||
created_stack = _create_stack(context, osc, bay,
|
created_stack = _create_stack(context, osc, bay,
|
||||||
bay_create_timeout)
|
bay_create_timeout)
|
||||||
|
@ -192,10 +190,18 @@ class Handler(object):
|
||||||
|
|
||||||
return bay
|
return bay
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _delete_trustee_and_trust(osc, bay):
|
||||||
|
osc.keystone().delete_trust(bay.trust_id)
|
||||||
|
osc.keystone().delete_trustee(bay.trustee_user_id)
|
||||||
|
|
||||||
def bay_delete(self, context, uuid):
|
def bay_delete(self, context, uuid):
|
||||||
LOG.debug('bay_heat bay_delete')
|
LOG.debug('bay_heat bay_delete')
|
||||||
osc = clients.OpenStackClients(context)
|
osc = clients.OpenStackClients(context)
|
||||||
bay = objects.Bay.get_by_uuid(context, uuid)
|
bay = objects.Bay.get_by_uuid(context, uuid)
|
||||||
|
|
||||||
|
self._delete_trustee_and_trust(osc, bay)
|
||||||
|
|
||||||
stack_id = bay.stack_id
|
stack_id = bay.stack_id
|
||||||
# NOTE(sdake): This will execute a stack_delete operation. This will
|
# NOTE(sdake): This will execute a stack_delete operation. This will
|
||||||
# Ignore HTTPNotFound exceptions (stack wasn't present). In the case
|
# Ignore HTTPNotFound exceptions (stack wasn't present). In the case
|
||||||
|
@ -218,7 +224,6 @@ class Handler(object):
|
||||||
except Exception:
|
except Exception:
|
||||||
raise
|
raise
|
||||||
|
|
||||||
osc.keystone().delete_trust(bay.registry_trust_id)
|
|
||||||
self._poll_and_check(osc, bay)
|
self._poll_and_check(osc, bay)
|
||||||
|
|
||||||
return None
|
return None
|
||||||
|
|
|
@ -0,0 +1,41 @@
|
||||||
|
# Copyright 2016 OpenStack Foundation
|
||||||
|
# All Rights Reserved.
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
"""create trustee for each bay
|
||||||
|
|
||||||
|
Revision ID: 5d4caa6e0a42
|
||||||
|
Revises: bb42b7cad130
|
||||||
|
Create Date: 2016-02-17 14:16:12.927874
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision = '5d4caa6e0a42'
|
||||||
|
down_revision = 'bb42b7cad130'
|
||||||
|
|
||||||
|
from alembic import op
|
||||||
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade():
|
||||||
|
op.alter_column('bay', 'registry_trust_id',
|
||||||
|
new_column_name='trust_id',
|
||||||
|
existing_type=sa.String(255))
|
||||||
|
op.add_column('bay', sa.Column('trustee_username',
|
||||||
|
sa.String(length=255), nullable=True))
|
||||||
|
op.add_column('bay', sa.Column('trustee_user_id',
|
||||||
|
sa.String(length=255), nullable=True))
|
||||||
|
op.add_column('bay', sa.Column('trustee_password',
|
||||||
|
sa.String(length=255), nullable=True))
|
|
@ -120,8 +120,12 @@ class Bay(Base):
|
||||||
bay_create_timeout = Column(Integer())
|
bay_create_timeout = Column(Integer())
|
||||||
discovery_url = Column(String(255))
|
discovery_url = Column(String(255))
|
||||||
master_addresses = Column(JSONEncodedList)
|
master_addresses = Column(JSONEncodedList)
|
||||||
# TODO(wanghua): encrypt registry_trust_id in db
|
# TODO(wanghua): encrypt trust_id in db
|
||||||
registry_trust_id = Column(String(255))
|
trust_id = Column(String(255))
|
||||||
|
trustee_username = Column(String(255))
|
||||||
|
trustee_user_id = Column(String(255))
|
||||||
|
# TODO(wanghua): encrypt trustee_password in db
|
||||||
|
trustee_password = Column(String(255))
|
||||||
# (yuanying) if we use barbican,
|
# (yuanying) if we use barbican,
|
||||||
# cert_ref size is determined by below format
|
# cert_ref size is determined by below format
|
||||||
# * http(s)://${DOMAIN_NAME}/v1/containers/${UUID}
|
# * http(s)://${DOMAIN_NAME}/v1/containers/${UUID}
|
||||||
|
|
|
@ -31,7 +31,10 @@ class Bay(base.MagnumPersistentObject, base.MagnumObject,
|
||||||
# Version 1.2: Add 'registry_trust_id' field
|
# Version 1.2: Add 'registry_trust_id' field
|
||||||
# Version 1.3: Added 'baymodel' field
|
# Version 1.3: Added 'baymodel' field
|
||||||
# Version 1.4: Added more types of status to bay's status field
|
# Version 1.4: Added more types of status to bay's status field
|
||||||
VERSION = '1.4'
|
# Version 1.5: Reanme 'registry_trust_id' to 'trust_id'
|
||||||
|
# Add 'trustee_user_name', 'trustee_password',
|
||||||
|
# 'trustee_user_id' field
|
||||||
|
VERSION = '1.5'
|
||||||
|
|
||||||
dbapi = dbapi.get_instance()
|
dbapi = dbapi.get_instance()
|
||||||
|
|
||||||
|
@ -54,8 +57,11 @@ class Bay(base.MagnumPersistentObject, base.MagnumObject,
|
||||||
'master_addresses': fields.ListOfStringsField(nullable=True),
|
'master_addresses': fields.ListOfStringsField(nullable=True),
|
||||||
'ca_cert_ref': fields.StringField(nullable=True),
|
'ca_cert_ref': fields.StringField(nullable=True),
|
||||||
'magnum_cert_ref': fields.StringField(nullable=True),
|
'magnum_cert_ref': fields.StringField(nullable=True),
|
||||||
'registry_trust_id': fields.StringField(nullable=True),
|
|
||||||
'baymodel': fields.ObjectField('BayModel'),
|
'baymodel': fields.ObjectField('BayModel'),
|
||||||
|
'trust_id': fields.StringField(nullable=True),
|
||||||
|
'trustee_username': fields.StringField(nullable=True),
|
||||||
|
'trustee_password': fields.StringField(nullable=True),
|
||||||
|
'trustee_user_id': fields.StringField(nullable=True)
|
||||||
}
|
}
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
|
|
@ -46,8 +46,7 @@ def list_opts():
|
||||||
('conductor', magnum.conductor.config.SERVICE_OPTS),
|
('conductor', magnum.conductor.config.SERVICE_OPTS),
|
||||||
('database', magnum.db.sql_opts),
|
('database', magnum.db.sql_opts),
|
||||||
('docker', magnum.common.docker_utils.docker_opts),
|
('docker', magnum.common.docker_utils.docker_opts),
|
||||||
('docker_registry',
|
('trust', magnum.common.keystone.trust_opts),
|
||||||
magnum.conductor.handlers.bay_conductor.docker_registry_opts),
|
|
||||||
('magnum_client', magnum.common.clients.magnum_client_opts),
|
('magnum_client', magnum.common.clients.magnum_client_opts),
|
||||||
('heat_client', magnum.common.clients.heat_client_opts),
|
('heat_client', magnum.common.clients.heat_client_opts),
|
||||||
('glance_client', magnum.common.clients.glance_client_opts),
|
('glance_client', magnum.common.clients.glance_client_opts),
|
||||||
|
|
|
@ -111,25 +111,31 @@ class KeystoneClientTest(base.BaseTestCase):
|
||||||
ks_client = keystone.KeystoneClientV3(self.ctx)
|
ks_client = keystone.KeystoneClientV3(self.ctx)
|
||||||
self.assertIsNone(ks_client.delete_trust(trust_id='atrust123'))
|
self.assertIsNone(ks_client.delete_trust(trust_id='atrust123'))
|
||||||
|
|
||||||
def test_create_trust(self, mock_ks):
|
def test_create_trust_with_all_roles(self, mock_ks):
|
||||||
mock_ks.return_value.auth_ref.user_id = '123456'
|
mock_ks.return_value.auth_ref.user_id = '123456'
|
||||||
mock_ks.return_value.auth_ref.project_id = '654321'
|
mock_ks.return_value.auth_ref.project_id = '654321'
|
||||||
|
|
||||||
|
self.ctx.roles = ['role1', 'role2']
|
||||||
ks_client = keystone.KeystoneClientV3(self.ctx)
|
ks_client = keystone.KeystoneClientV3(self.ctx)
|
||||||
ks_client.create_trust(trustee_user='888888',
|
|
||||||
role_names='xxxx')
|
ks_client.create_trust(trustee_user='888888')
|
||||||
|
|
||||||
mock_ks.return_value.trusts.create.assert_called_once_with(
|
mock_ks.return_value.trusts.create.assert_called_once_with(
|
||||||
trustor_user='123456', project='654321',
|
trustor_user='123456', project='654321',
|
||||||
trustee_user='888888', role_names='xxxx',
|
trustee_user='888888', role_names=['role1', 'role2'],
|
||||||
impersonation=True)
|
impersonation=True)
|
||||||
|
|
||||||
@mock.patch.object(keystone.KeystoneClientV3,
|
def test_create_trust_with_limit_roles(self, mock_ks):
|
||||||
'create_trust')
|
mock_ks.return_value.auth_ref.user_id = '123456'
|
||||||
def test_create_trust_to_admin(self, mock_create_trust, mock_ks):
|
mock_ks.return_value.auth_ref.project_id = '654321'
|
||||||
mock_ks.return_value.auth_ref.user_id = '777777'
|
|
||||||
|
|
||||||
|
self.ctx.roles = ['role1', 'role2']
|
||||||
ks_client = keystone.KeystoneClientV3(self.ctx)
|
ks_client = keystone.KeystoneClientV3(self.ctx)
|
||||||
ks_client.create_trust_to_admin(role_names='xxxx')
|
|
||||||
|
|
||||||
mock_create_trust.assert_called_once_with('777777', 'xxxx', True)
|
cfg.CONF.set_override('roles', ['role3'], group='trust')
|
||||||
|
ks_client.create_trust(trustee_user='888888')
|
||||||
|
|
||||||
|
mock_ks.return_value.trusts.create.assert_called_once_with(
|
||||||
|
trustor_user='123456', project='654321',
|
||||||
|
trustee_user='888888', role_names=['role3'],
|
||||||
|
impersonation=True)
|
||||||
|
|
|
@ -598,3 +598,13 @@ class Urllib2_invalid_scheme(base.TestCase):
|
||||||
|
|
||||||
def test_raise_exception_invalid_scheme_https(self):
|
def test_raise_exception_invalid_scheme_https(self):
|
||||||
utils.raise_exception_invalid_scheme(url='https://www.openstack.org')
|
utils.raise_exception_invalid_scheme(url='https://www.openstack.org')
|
||||||
|
|
||||||
|
|
||||||
|
class GeneratePasswordTestCase(base.TestCase):
|
||||||
|
def test_generate_password(self):
|
||||||
|
password = utils.generate_password(length=12)
|
||||||
|
self.assertTrue([c for c in password if c in '0123456789'])
|
||||||
|
self.assertTrue([c for c in password
|
||||||
|
if c in 'abcdefghijklmnopqrstuvwxyz'])
|
||||||
|
self.assertTrue([c for c in password
|
||||||
|
if c in 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'])
|
||||||
|
|
|
@ -44,6 +44,23 @@ class TestHandler(db_base.DbTestCase):
|
||||||
self.bay = objects.Bay(self.context, **bay_dict)
|
self.bay = objects.Bay(self.context, **bay_dict)
|
||||||
self.bay.create()
|
self.bay.create()
|
||||||
|
|
||||||
|
self.p = patch(
|
||||||
|
'magnum.conductor.handlers.bay_conductor.Handler.'
|
||||||
|
'_create_trustee_and_trust')
|
||||||
|
|
||||||
|
def create_trustee_and_trust(osc, bay):
|
||||||
|
bay.trust_id = 'trust_id'
|
||||||
|
bay.trustee_username = 'user_name'
|
||||||
|
bay.trustee_user_id = 'user_id'
|
||||||
|
bay.trustee_password = 'password'
|
||||||
|
|
||||||
|
self.p.side_effect = create_trustee_and_trust
|
||||||
|
self.p.start()
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
self.p.stop()
|
||||||
|
super(TestHandler, self).tearDown()
|
||||||
|
|
||||||
@patch('magnum.conductor.scale_manager.ScaleManager')
|
@patch('magnum.conductor.scale_manager.ScaleManager')
|
||||||
@patch('magnum.conductor.handlers.bay_conductor.Handler._poll_and_check')
|
@patch('magnum.conductor.handlers.bay_conductor.Handler._poll_and_check')
|
||||||
@patch('magnum.conductor.handlers.bay_conductor._update_stack')
|
@patch('magnum.conductor.handlers.bay_conductor._update_stack')
|
||||||
|
|
|
@ -96,8 +96,6 @@ def get_test_bay(**kw):
|
||||||
'node_count': kw.get('node_count', 3),
|
'node_count': kw.get('node_count', 3),
|
||||||
'master_count': kw.get('master_count', 3),
|
'master_count': kw.get('master_count', 3),
|
||||||
'master_addresses': kw.get('master_addresses', ['172.17.2.18']),
|
'master_addresses': kw.get('master_addresses', ['172.17.2.18']),
|
||||||
'registry_trust_id': kw.get('registry_trust_id',
|
|
||||||
'1f2281ac-e532-4e53-bbe6-3c9be24b0504'),
|
|
||||||
'created_at': kw.get('created_at'),
|
'created_at': kw.get('created_at'),
|
||||||
'updated_at': kw.get('updated_at'),
|
'updated_at': kw.get('updated_at'),
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,6 +28,10 @@ class TestBayObject(base.DbTestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(TestBayObject, self).setUp()
|
super(TestBayObject, self).setUp()
|
||||||
self.fake_bay = utils.get_test_bay()
|
self.fake_bay = utils.get_test_bay()
|
||||||
|
self.fake_bay['trust_id'] = 'trust_id'
|
||||||
|
self.fake_bay['trustee_username'] = 'trustee_user'
|
||||||
|
self.fake_bay['trustee_user_id'] = 'trustee_user_id'
|
||||||
|
self.fake_bay['trustee_password'] = 'password'
|
||||||
baymodel_id = self.fake_bay['baymodel_id']
|
baymodel_id = self.fake_bay['baymodel_id']
|
||||||
self.fake_baymodel = objects.BayModel(uuid=baymodel_id)
|
self.fake_baymodel = objects.BayModel(uuid=baymodel_id)
|
||||||
|
|
||||||
|
|
|
@ -423,7 +423,7 @@ class _TestObject(object):
|
||||||
# For more information on object version testing, read
|
# For more information on object version testing, read
|
||||||
# http://docs.openstack.org/developer/magnum/objects.html
|
# http://docs.openstack.org/developer/magnum/objects.html
|
||||||
object_data = {
|
object_data = {
|
||||||
'Bay': '1.4-ca3c05dc1b27b2e50c082bb2a1a64151',
|
'Bay': '1.5-a3b9292ef5d35175b93ca46ba3baec2d',
|
||||||
'BayModel': '1.9-d5d32553721d0cadfcc45ddc316d9c1a',
|
'BayModel': '1.9-d5d32553721d0cadfcc45ddc316d9c1a',
|
||||||
'Certificate': '1.0-2aff667971b85c1edf8d15684fd7d5e2',
|
'Certificate': '1.0-2aff667971b85c1edf8d15684fd7d5e2',
|
||||||
'Container': '1.3-e2d9d2e8a8844d421148cd9fde6c6bd6',
|
'Container': '1.3-e2d9d2e8a8844d421148cd9fde6c6bd6',
|
||||||
|
|
Loading…
Reference in New Issue