Rename application credential restriction column

In the application credential spec[1] we decided to add on a parameter
that would control whether an application credential could be used to
create other application credentials. This parameter is also used to
control whether it can be used to delete other application credentials
and whether it can create and delete trusts. Therefore the name
`allow_application_credential_creation` is misleading. Moreover, giving
a property of the resource a name that is an imperative verb is not
great. It makes more sense for a property to be a noun, an adjective, or
a passive verb.

This change renames the `allow_application_credential_creation`` column
to ``unrestricted``. This maintains the same boolean context, i.e. a
"true" value for the old name maintains the same meaning as a "true"
value for the new name.

At this point, the application credential API has not yet been exposed,
so there should be no data in this table and no need for complicated
migration triggers. In general, we only need to do a column alter to
rename it. Sqlite is special because it does not support column alters,
so in order to accomodate our tests the migration involves copying the
whole table, minus the old column, and recreating it with the new
column.

Change-Id: Id26a2790acae25f80bd28a8cb121c80cb5064645
This commit is contained in:
Colleen Murphy 2018-01-22 12:59:30 +01:00
parent d94d9c566f
commit 5fe9e3761d
6 changed files with 137 additions and 4 deletions

View File

@ -27,7 +27,7 @@ class ApplicationCredentialModel(sql.ModelBase, sql.ModelDictMixin):
__tablename__ = 'application_credential'
attributes = ['internal_id', 'id', 'name', 'secret_hash', 'description',
'user_id', 'project_id', 'system', 'expires_at',
'allow_application_credential_creation']
'unrestricted']
internal_id = sql.Column(sql.Integer, primary_key=True, nullable=False)
id = sql.Column(sql.String(64), nullable=False)
name = sql.Column(sql.String(255), nullable=False)
@ -37,7 +37,7 @@ class ApplicationCredentialModel(sql.ModelBase, sql.ModelDictMixin):
project_id = sql.Column(sql.String(64), nullable=True)
system = sql.Column(sql.String(64), nullable=True)
expires_at = sql.Column(sql.DateTimeInt())
allow_application_credential_creation = sql.Column(sql.Boolean)
unrestricted = sql.Column(sql.Boolean)
__table_args__ = (sql.UniqueConstraint('name', 'user_id',
name='duplicate_app_cred_constraint'),)

View File

@ -0,0 +1,32 @@
# 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 sqlalchemy as sql
def upgrade(migrate_engine):
meta = sql.MetaData()
meta.bind = migrate_engine
application_credential_table = sql.Table(
'application_credential', meta, autoload=True
)
if migrate_engine.name == 'sqlite':
old_table = sql.Table('application_credential', meta, autoload=True)
new_table = sql.Table('application_credential_temp', meta,
autoload=True)
old_table.drop()
new_table.rename('application_credential')
else:
table = application_credential_table
column = table.c.allow_application_credential_creation
column.alter(name='unrestricted')

View File

@ -0,0 +1,15 @@
# 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.
def upgrade(migrate_engine):
pass

View File

@ -0,0 +1,40 @@
# 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 sqlalchemy as sql
def upgrade(migrate_engine):
meta = sql.MetaData()
meta.bind = migrate_engine
# MySQL and PostgreSQL can handle a column rename.
# Only Sqlite is special. Since Sqlite can't support an online upgrade
# anyway, just brute-force the migration by copying the table.
if migrate_engine.name == 'sqlite':
old_table = sql.Table(
'application_credential', meta, autoload=True
)
args = []
for column in old_table.columns:
if column.name != 'allow_application_credential_creation':
args.append(column.copy())
unrestricted = sql.Column('unrestricted', sql.Boolean)
args.append(unrestricted)
constraint = sql.UniqueConstraint('user_id', 'name',
name='duplicate_app_cred_constraint')
args.append(constraint)
new_table = sql.Table('application_credential_temp',
old_table.metadata, *args)
new_table.create(migrate_engine, checkfirst=True)

View File

@ -47,7 +47,7 @@ class ApplicationCredentialTests(object):
{'id': self.role__member_['id']},
],
'secret': uuid.uuid4().hex,
'allow_application_credential_creation': False
'unrestricted': False
}
return app_cred_data
@ -86,7 +86,7 @@ class ApplicationCredentialTests(object):
def test_application_credential_allow_recursion(self):
app_cred = self._new_app_cred_data(self.user_foo['id'],
project_id=self.tenant_bar['id'])
app_cred['allow_application_credential_creation'] = True
app_cred['unrestricted'] = True
resp = self.app_cred_api.create_application_credential(app_cred)
resp.pop('roles')
app_cred.pop('roles')

View File

@ -2866,6 +2866,52 @@ class FullMigration(SqlMigrateBase, unit.TestCase):
}
application_credential_table.insert().values(app_cred).execute()
def test_migration_036_rename_application_credentials_column(self):
self.expand(35)
self.migrate(35)
self.contract(35)
application_credential_table_name = 'application_credential'
application_credential_role_table_name = 'application_credential_role'
self.expand(36)
self.migrate(36)
self.contract(36)
self.assertTableColumns(
application_credential_table_name,
['internal_id', 'id', 'name', 'secret_hash',
'description', 'user_id', 'project_id', 'system', 'expires_at',
'unrestricted']
)
application_credential_table = sqlalchemy.Table(
application_credential_table_name, self.metadata, autoload=True
)
app_cred_role_table = sqlalchemy.Table(
application_credential_role_table_name,
self.metadata, autoload=True
)
# Test that the new column works
app_cred = {
'internal_id': 1,
'id': uuid.uuid4().hex,
'name': uuid.uuid4().hex,
'secret_hash': uuid.uuid4().hex,
'description': uuid.uuid4().hex,
'user_id': uuid.uuid4().hex,
'system': uuid.uuid4().hex,
'expires_at': None,
'unrestricted': False
}
application_credential_table.insert().values(app_cred).execute()
role_rel = {
'application_credential_id': app_cred['internal_id'],
'role_id': uuid.uuid4().hex
}
app_cred_role_table.insert().values(role_rel).execute()
class MySQLOpportunisticFullMigration(FullMigration):
FIXTURE = test_base.MySQLOpportunisticFixture