mirror of
https://github.com/ansible/awx.git
synced 2024-11-01 08:21:15 +03:00
Eliminate multiple-organization projects
Projects are duplicated with this migration to provide a nearly equivalent functionality. Satisifies #1164
This commit is contained in:
parent
10646a448a
commit
ba6752fb23
@ -184,4 +184,19 @@ class Migration(migrations.Migration):
|
||||
name='rolepermission',
|
||||
index_together=set([('content_type', 'object_id')]),
|
||||
),
|
||||
migrations.RenameField(
|
||||
model_name='organization',
|
||||
old_name='projects',
|
||||
new_name='deprecated_projects',
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='organization',
|
||||
name='deprecated_projects',
|
||||
field=models.ManyToManyField(related_name='deprecated_organizations', to='main.Project', blank=True),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='project',
|
||||
name='organization',
|
||||
field=models.ForeignKey(related_name='projects', to='main.Organization', blank=True, null=True),
|
||||
),
|
||||
]
|
||||
|
@ -1,3 +1,5 @@
|
||||
from django.db import connection, transaction, reset_queries
|
||||
from django.db.transaction import TransactionManagementError
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
|
||||
from collections import defaultdict
|
||||
@ -131,31 +133,72 @@ def migrate_projects(apps, schema_editor):
|
||||
|
||||
Project = apps.get_model('main', 'Project')
|
||||
Permission = apps.get_model('main', 'Permission')
|
||||
JobTemplate = apps.get_model('main', 'JobTemplate')
|
||||
|
||||
for project in Project.objects.all():
|
||||
if project.organizations.count() == 0 and project.created_by is not None:
|
||||
# Migrate projects to single organizations, duplicating as necessary
|
||||
for project in [p for p in Project.objects.all()]:
|
||||
original_project_name = project.name
|
||||
project_orgs = project.deprecated_organizations.distinct().all()
|
||||
|
||||
if project_orgs.count() > 1:
|
||||
first_org = None
|
||||
for org in project_orgs:
|
||||
if first_org is None:
|
||||
# For the first org, re-use our existing Project object, so don't do the below duplication effort
|
||||
first_org = org
|
||||
project.name = first_org.name + ' - ' + original_project_name
|
||||
project.organization = first_org
|
||||
project.save()
|
||||
else:
|
||||
print('Fork to %s ' % (org.name + ' - ' + original_project_name))
|
||||
new_prj = Project.objects.create(
|
||||
created = project.created,
|
||||
description = project.description,
|
||||
name = org.name + ' - ' + original_project_name,
|
||||
old_pk = project.old_pk,
|
||||
created_by_id = project.created_by_id,
|
||||
scm_type = project.scm_type,
|
||||
scm_url = project.scm_url,
|
||||
scm_branch = project.scm_branch,
|
||||
scm_clean = project.scm_clean,
|
||||
scm_delete_on_update = project.scm_delete_on_update,
|
||||
scm_delete_on_next_update = project.scm_delete_on_next_update,
|
||||
scm_update_on_launch = project.scm_update_on_launch,
|
||||
scm_update_cache_timeout = project.scm_update_cache_timeout,
|
||||
credential = project.credential,
|
||||
organization = org
|
||||
)
|
||||
migrations[original_project_name]['projects'].add(new_prj)
|
||||
job_templates = JobTemplate.objects.filter(inventory__organization=org).all()
|
||||
for jt in job_templates:
|
||||
jt.project = new_prj
|
||||
print('Updating jt to point to %s' % repr(new_prj))
|
||||
jt.save()
|
||||
|
||||
# Migrate permissions
|
||||
for project in [p for p in Project.objects.all()]:
|
||||
if project.organization is not None and project.created_by is not None:
|
||||
project.admin_role.members.add(project.created_by)
|
||||
migrations[project.name]['users'].add(project.created_by)
|
||||
migrations[original_project_name]['users'].add(project.created_by)
|
||||
|
||||
for team in project.teams.all():
|
||||
team.member_role.children.add(project.member_role)
|
||||
migrations[project.name]['teams'].add(team)
|
||||
migrations[original_project_name]['teams'].add(team)
|
||||
|
||||
if project.organizations.count() > 0:
|
||||
for org in project.organizations.all():
|
||||
for user in org.users.all():
|
||||
project.member_role.members.add(user)
|
||||
migrations[project.name]['users'].add(user)
|
||||
if project.organization is not None:
|
||||
for user in project.organization.member_role.members.all():
|
||||
project.member_role.members.add(user)
|
||||
migrations[original_project_name]['users'].add(user)
|
||||
|
||||
for perm in Permission.objects.filter(project=project, active=True):
|
||||
# All perms at this level just imply a user or team can read
|
||||
if perm.team:
|
||||
perm.team.member_role.children.add(project.member_role)
|
||||
migrations[project.name]['teams'].add(perm.team)
|
||||
migrations[original_project_name]['teams'].add(perm.team)
|
||||
|
||||
if perm.user:
|
||||
project.member_role.members.add(perm.user)
|
||||
migrations[project.name]['users'].add(perm.user)
|
||||
migrations[original_project_name]['users'].add(perm.user)
|
||||
|
||||
return migrations
|
||||
|
||||
|
@ -38,10 +38,10 @@ class Organization(CommonModel, NotificationFieldsModel, ResourceMixin):
|
||||
app_label = 'main'
|
||||
ordering = ('name',)
|
||||
|
||||
projects = models.ManyToManyField(
|
||||
deprecated_projects = models.ManyToManyField(
|
||||
'Project',
|
||||
blank=True,
|
||||
related_name='organizations',
|
||||
related_name='deprecated_organizations',
|
||||
)
|
||||
admin_role = ImplicitRoleField(
|
||||
role_name='Organization Administrator',
|
||||
|
@ -198,6 +198,13 @@ class Project(UnifiedJobTemplate, ProjectOptions, ResourceMixin):
|
||||
app_label = 'main'
|
||||
ordering = ('id',)
|
||||
|
||||
organization = models.ForeignKey(
|
||||
'Organization',
|
||||
blank=True,
|
||||
null=True,
|
||||
on_delete=models.CASCADE,
|
||||
related_name='projects',
|
||||
)
|
||||
scm_delete_on_next_update = models.BooleanField(
|
||||
default=False,
|
||||
editable=False,
|
||||
@ -212,13 +219,13 @@ class Project(UnifiedJobTemplate, ProjectOptions, ResourceMixin):
|
||||
admin_role = ImplicitRoleField(
|
||||
role_name='Project Administrator',
|
||||
role_description='May manage this project',
|
||||
parent_role='organizations.admin_role',
|
||||
parent_role='organization.admin_role',
|
||||
permissions = {'all': True}
|
||||
)
|
||||
auditor_role = ImplicitRoleField(
|
||||
role_name='Project Auditor',
|
||||
role_description='May read all settings associated with this project',
|
||||
parent_role='organizations.auditor_role',
|
||||
parent_role='organization.auditor_role',
|
||||
permissions = {'read': True}
|
||||
)
|
||||
member_role = ImplicitRoleField(
|
||||
|
@ -97,8 +97,9 @@ def project(instance, organization):
|
||||
prj = Project.objects.create(name="test-proj",
|
||||
description="test-proj-desc",
|
||||
scm_type="git",
|
||||
scm_url="https://github.com/jlaska/ansible-playbooks")
|
||||
prj.organizations.add(organization)
|
||||
scm_url="https://github.com/jlaska/ansible-playbooks",
|
||||
organization=organization
|
||||
)
|
||||
return prj
|
||||
|
||||
@pytest.fixture
|
||||
|
@ -86,7 +86,6 @@ def test_inherited_notifiers(get, post, user, organization, project):
|
||||
u)
|
||||
assert response.status_code == 201
|
||||
notifiers.append(response.data['id'])
|
||||
organization.projects.add(project)
|
||||
i = Inventory.objects.create(name='test', organization=organization)
|
||||
i.save()
|
||||
g = Group.objects.create(name='test', inventory=i)
|
||||
@ -109,7 +108,6 @@ def test_inherited_notifiers(get, post, user, organization, project):
|
||||
@pytest.mark.django_db
|
||||
def test_notifier_merging(get, post, user, organization, project, notifier):
|
||||
user('admin-poster', True)
|
||||
organization.projects.add(project)
|
||||
organization.notifiers_any.add(notifier)
|
||||
project.notifiers_any.add(notifier)
|
||||
assert len(project.notifiers['any']) == 1
|
||||
|
@ -1,11 +1,96 @@
|
||||
import pytest
|
||||
|
||||
from awx.main.migrations import _rbac as rbac
|
||||
from awx.main.models import Role
|
||||
from awx.main.models import Role, Permission, Project, Organization, Credential, JobTemplate, Inventory
|
||||
from django.apps import apps
|
||||
from awx.main.migrations import _old_access as old_access
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_project_migration():
|
||||
'''
|
||||
|
||||
o1 o2 o3 with o1 -- i1 o2 -- i2
|
||||
\ | /
|
||||
\ | /
|
||||
c1 ---- p1
|
||||
/ | \
|
||||
/ | \
|
||||
jt1 jt2 jt3
|
||||
| | |
|
||||
i1 i2 i1
|
||||
|
||||
|
||||
goes to
|
||||
|
||||
|
||||
o1
|
||||
|
|
||||
|
|
||||
c1 ---- p1
|
||||
/ |
|
||||
/ |
|
||||
jt1 jt3
|
||||
| |
|
||||
i1 i1
|
||||
|
||||
|
||||
o2
|
||||
|
|
||||
|
|
||||
c1 ---- p2
|
||||
|
|
||||
|
|
||||
jt2
|
||||
|
|
||||
i2
|
||||
|
||||
o3
|
||||
|
|
||||
|
|
||||
c1 ---- p3
|
||||
|
||||
|
||||
'''
|
||||
|
||||
|
||||
o1 = Organization.objects.create(name='o1')
|
||||
o2 = Organization.objects.create(name='o2')
|
||||
o3 = Organization.objects.create(name='o3')
|
||||
|
||||
c1 = Credential.objects.create(name='c1')
|
||||
|
||||
p1 = Project.objects.create(name='p1', credential=c1)
|
||||
p1.deprecated_organizations.add(o1, o2, o3)
|
||||
|
||||
i1 = Inventory.objects.create(name='i1', organization=o1)
|
||||
i2 = Inventory.objects.create(name='i2', organization=o2)
|
||||
|
||||
jt1 = JobTemplate.objects.create(name='jt1', project=p1, inventory=i1)
|
||||
jt2 = JobTemplate.objects.create(name='jt2', project=p1, inventory=i2)
|
||||
jt3 = JobTemplate.objects.create(name='jt3', project=p1, inventory=i1)
|
||||
|
||||
assert o1.projects.count() == 0
|
||||
assert o2.projects.count() == 0
|
||||
assert o3.projects.count() == 0
|
||||
|
||||
rbac.migrate_projects(apps, None)
|
||||
|
||||
jt1 = JobTemplate.objects.get(pk=jt1.pk)
|
||||
jt2 = JobTemplate.objects.get(pk=jt2.pk)
|
||||
jt3 = JobTemplate.objects.get(pk=jt3.pk)
|
||||
|
||||
assert jt1.project == jt3.project
|
||||
assert jt1.project != jt2.project
|
||||
|
||||
assert o1.projects.count() == 1
|
||||
assert o2.projects.count() == 1
|
||||
assert o3.projects.count() == 1
|
||||
assert o1.projects.all()[0].jobtemplates.count() == 2
|
||||
assert o2.projects.all()[0].jobtemplates.count() == 1
|
||||
assert o3.projects.all()[0].jobtemplates.count() == 0
|
||||
|
||||
|
||||
#@pytest.mark.django_db
|
||||
#def test_project_user_project(user_project, project, user):
|
||||
# u = user('owner')
|
||||
@ -56,13 +141,14 @@ from awx.main.migrations import _old_access as old_access
|
||||
# assert len(migrations[project.name]['teams']) == 0
|
||||
# assert project.accessible_by(admin, {'read': True, 'write': True}) is True
|
||||
# assert project.accessible_by(member, {'read': True}) is False
|
||||
|
||||
#
|
||||
#@pytest.mark.django_db
|
||||
#def test_project_team(user, team, project):
|
||||
# nonmember = user('nonmember')
|
||||
# member = user('member')
|
||||
#
|
||||
# team.users.add(member)
|
||||
# #team.users.add(member)
|
||||
# team.member_role.members.add(member)
|
||||
# project.teams.add(team)
|
||||
#
|
||||
# assert project.accessible_by(nonmember, {'read': True}) is False
|
||||
@ -76,14 +162,14 @@ from awx.main.migrations import _old_access as old_access
|
||||
# assert len(migrations[project.name]['teams']) == 1
|
||||
# assert project.accessible_by(member, {'read': True}) is True
|
||||
# assert project.accessible_by(nonmember, {'read': True}) is False
|
||||
|
||||
#
|
||||
#@pytest.mark.django_db
|
||||
#def test_project_explicit_permission(user, team, project, organization):
|
||||
# u = user('prjuser')
|
||||
#
|
||||
# assert old_access.check_user_access(u, project.__class__, 'read', project) is False
|
||||
#
|
||||
# organization.users.add(u)
|
||||
# organization.member_role.members.add(u)
|
||||
# p = Permission(user=u, project=project, permission_type='create', name='Perm name')
|
||||
# p.save()
|
||||
#
|
||||
|
Loading…
Reference in New Issue
Block a user