Include Tower configurations into activity stream

Relates #7386 of ansible-tower.

Due to the uniqueness of Tower configuration datastore model, it is not
fully compatible with activity stream workflow. This PR introduced
setting field for activitystream model along with other changes to make
Tower configuration a special case for activity streams.

Signed-off-by: Aaron Tan <jangsutsr@gmail.com>
This commit is contained in:
Aaron Tan 2017-09-27 15:11:38 -04:00 committed by Matthew Jones
parent 4fd190e4c8
commit a11e33458f
7 changed files with 75 additions and 3 deletions

View File

@ -3797,6 +3797,11 @@ class ActivityStreamSerializer(BaseSerializer):
if fk == 'schedule':
rel['unified_job_template'] = thisItem.unified_job_template.get_absolute_url(self.context.get('request'))
if obj.setting and obj.setting.get('category', None):
rel['setting'] = self.reverse(
'api:setting_singleton_detail',
kwargs={'category_slug': obj.setting['category']}
)
return rel
def _get_rel(self, obj, fk):
@ -3848,6 +3853,8 @@ class ActivityStreamSerializer(BaseSerializer):
username = obj.actor.username,
first_name = obj.actor.first_name,
last_name = obj.actor.last_name)
if obj.setting:
summary_fields['setting'] = [obj.setting]
return summary_fields

View File

@ -120,6 +120,9 @@ class SettingsRegistry(object):
def is_setting_read_only(self, setting):
return bool(self._registry.get(setting, {}).get('read_only', False))
def get_setting_category(self, setting):
return self._registry.get(setting, {}).get('category_slug', None)
def get_setting_field(self, setting, mixin_class=None, for_user=False, **kwargs):
from rest_framework.fields import empty
field_kwargs = {}

View File

@ -9,7 +9,10 @@ import shutil
# RedBaron
from redbaron import RedBaron, indent
__all__ = ['comment_assignments']
# AWX
from awx.conf.registry import settings_registry
__all__ = ['comment_assignments', 'conf_to_dict']
def comment_assignments(patterns, assignment_names, dry_run=True, backup_suffix='.old'):
@ -103,6 +106,13 @@ def comment_assignments_in_file(filename, assignment_names, dry_run=True, backup
return '\n'.join(diff_lines)
def conf_to_dict(obj):
return {
'category': settings_registry.get_setting_category(obj.key),
'name': obj.key,
}
if __name__ == '__main__':
pattern = os.path.join(os.path.dirname(__file__), '..', 'settings', 'local_*.py')
diffs = comment_assignments(pattern, ['AUTH_LDAP_ORGANIZATION_MAP'])

View File

@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations
import awx.main.fields
class Migration(migrations.Migration):
dependencies = [
('main', '0009_v322_add_support_for_ovirt4_inventory'),
]
operations = [
migrations.AddField(
model_name='activitystream',
name='setting',
field=awx.main.fields.JSONField(default=dict, blank=True),
),
]

View File

@ -3,6 +3,7 @@
# Tower
from awx.api.versioning import reverse
from awx.main.fields import JSONField
# Django
from django.db import models
@ -66,6 +67,8 @@ class ActivityStream(models.Model):
role = models.ManyToManyField("Role", blank=True)
instance_group = models.ManyToManyField("InstanceGroup", blank=True)
setting = JSONField(blank=True)
def get_absolute_url(self, request=None):
return reverse('api:activity_stream_detail', kwargs={'pk': self.pk}, request=request)

View File

@ -26,6 +26,8 @@ from awx.main.fields import is_implicit_parent
from awx.main.consumers import emit_channel_notification
from awx.conf.utils import conf_to_dict
__all__ = []
logger = logging.getLogger('awx.main.signals')
@ -402,12 +404,15 @@ def activity_stream_create(sender, instance, created, **kwargs):
object1=object1,
changes=json.dumps(changes),
actor=get_current_user_or_none())
activity_entry.save()
#TODO: Weird situation where cascade SETNULL doesn't work
# it might actually be a good idea to remove all of these FK references since
# we don't really use them anyway.
if instance._meta.model_name != 'setting': # Is not conf.Setting instance
activity_entry.save()
getattr(activity_entry, object1).add(instance)
else:
activity_entry.setting = conf_to_dict(instance)
activity_entry.save()
def activity_stream_update(sender, instance, **kwargs):
@ -433,9 +438,12 @@ def activity_stream_update(sender, instance, **kwargs):
object1=object1,
changes=json.dumps(changes),
actor=get_current_user_or_none())
activity_entry.save()
if instance._meta.model_name != 'setting': # Is not conf.Setting instance
activity_entry.save()
getattr(activity_entry, object1).add(instance)
else:
activity_entry.setting = conf_to_dict(instance)
activity_entry.save()
def activity_stream_delete(sender, instance, **kwargs):

View File

@ -5,6 +5,7 @@ from awx.api.versioning import reverse
from awx.main.middleware import ActivityStreamMiddleware
from awx.main.models.activity_stream import ActivityStream
from awx.main.access import ActivityStreamAccess
from awx.conf.models import Setting
def mock_feature_enabled(feature):
@ -47,6 +48,26 @@ def test_basic_fields(monkeypatch, organization, get, user, settings):
assert response.data['summary_fields']['organization'][0]['name'] == 'test-org'
@mock.patch('awx.api.views.feature_enabled', new=mock_feature_enabled)
@pytest.mark.django_db
def test_ctint_activity_stream(monkeypatch, get, user, settings):
Setting.objects.create(key="FOO", value="bar")
settings.ACTIVITY_STREAM_ENABLED = True
u = user('admin', True)
activity_stream = ActivityStream.objects.filter(setting={'name': 'FOO', 'category': None}).latest('pk')
activity_stream.actor = u
activity_stream.save()
aspk = activity_stream.pk
url = reverse('api:activity_stream_detail', kwargs={'pk': aspk})
response = get(url, user('admin', True))
assert response.status_code == 200
assert 'summary_fields' in response.data
assert 'setting' in response.data['summary_fields']
assert response.data['summary_fields']['setting'][0]['name'] == 'FOO'
@mock.patch('awx.api.views.feature_enabled', new=mock_feature_enabled)
@pytest.mark.django_db
def test_middleware_actor_added(monkeypatch, post, get, user, settings):