Merge ~blake-rouse/maas:pools-modified-tracking into maas:master

Proposed by Blake Rouse
Status: Merged
Approved by: Blake Rouse
Approved revision: b4b742a4529f03fbd2fd87a452cdd1d583222a6b
Merge reported by: MAAS Lander
Merged at revision: not available
Proposed branch: ~blake-rouse/maas:pools-modified-tracking
Merge into: maas:master
Diff against target: 661 lines (+558/-0)
9 files modified
src/maasserver/migrations/builtin/maasserver/0179_rbacsync.py (+30/-0)
src/maasserver/models/__init__.py (+2/-0)
src/maasserver/models/rbacsync.py (+95/-0)
src/maasserver/models/tests/test_rbacsync.py (+42/-0)
src/maasserver/triggers/system.py (+152/-0)
src/maasserver/triggers/testing.py (+45/-0)
src/maasserver/triggers/tests/test_init.py (+6/-0)
src/maasserver/triggers/tests/test_system.py (+5/-0)
src/maasserver/triggers/tests/test_system_listener.py (+181/-0)
Reviewer Review Type Date Requested Status
MAAS Lander Needs Fixing
Alberto Donato (community) Approve
Review via email: mp+356106@code.launchpad.net

Commit message

Add tracking of when resource pools are created/updated/deleted. Send a notification when this occurs.

Description of the change

In a following branch the regiond will listen for the sys_rpool notification to sync the resource pools to the RBAC micro-service.

To post a comment you must log in.
Revision history for this message
Alberto Donato (ack) wrote :

The mechanism implemented here seems quite generic.
Maybe we could add a "resource_type" field so that a single table would track all type of resources that need sync.

Manager methods would then filter on that type (and the pg notification would also carry the type).

This way we don't need a different table and notification handler for each resource we cover with rbac.

review: Needs Information
Revision history for this message
Blake Rouse (blake-rouse) :
Revision history for this message
Blake Rouse (blake-rouse) wrote :

I was actually thinking the same thing that I didn't make it generic enough. Because I also need to get notifications when the settings change.

I have re-done the branch to make it generic just for syncing with RBAC in general. We can easily add on to it as we add more resources to RBAC.

Revision history for this message
Alberto Donato (ack) wrote :

+1, just minor comments inline

review: Approve
Revision history for this message
MAAS Lander (maas-lander) wrote :

UNIT TESTS
-b pools-modified-tracking lp:~blake-rouse/maas/+git/maas into -b master lp:~maas-committers/maas

STATUS: FAILED
LOG: http://maas-ci-jenkins.internal:8080/job/maas/job/branch-tester/4065/console
COMMIT: 1b10f2bc36346903128f4891f04f16aefe040a89

review: Needs Fixing
Revision history for this message
MAAS Lander (maas-lander) wrote :
Revision history for this message
MAAS Lander (maas-lander) wrote :

UNIT TESTS
-b pools-modified-tracking lp:~blake-rouse/maas/+git/maas into -b master lp:~maas-committers/maas

STATUS: FAILED
LOG: http://maas-ci-jenkins.internal:8080/job/maas/job/branch-tester/4072/console
COMMIT: aad64793fd2ef8f99aa345f42c1ff53a463b10ee

review: Needs Fixing
b4b742a... by Blake Rouse

Fix tests.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
diff --git a/src/maasserver/migrations/builtin/maasserver/0179_rbacsync.py b/src/maasserver/migrations/builtin/maasserver/0179_rbacsync.py
0new file mode 1006440new file mode 100644
index 0000000..3422aed
--- /dev/null
+++ b/src/maasserver/migrations/builtin/maasserver/0179_rbacsync.py
@@ -0,0 +1,30 @@
1# -*- coding: utf-8 -*-
2# Generated by Django 1.11.11 on 2018-10-04 14:25
3from __future__ import unicode_literals
4
5from django.db import (
6 migrations,
7 models,
8)
9
10
11class Migration(migrations.Migration):
12
13 dependencies = [
14 ('maasserver', '0178_break_apart_linked_bmcs'),
15 ]
16
17 operations = [
18 migrations.CreateModel(
19 name='RBACSync',
20 fields=[
21 ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
22 ('action', models.CharField(blank=True, choices=[('full', 'full'), ('add', 'add'), ('update', 'update'), ('remove', 'remove')], default='full', editable=False, help_text='Action that should occur on the RBAC service.', max_length=6)),
23 ('resource_type', models.CharField(blank=True, editable=False, help_text='Resource type that as been added/updated/removed.', max_length=255)),
24 ('resource_id', models.IntegerField(blank=True, editable=False, help_text='Resource ID that has been added/updated/removed.', null=True)),
25 ('resource_name', models.CharField(blank=True, editable=False, help_text='Resource name that has been added/updated/removed.', max_length=255)),
26 ('created', models.DateTimeField(auto_now_add=True)),
27 ('source', models.CharField(blank=True, editable=False, help_text='A brief explanation what changed.', max_length=255)),
28 ],
29 ),
30 ]
diff --git a/src/maasserver/models/__init__.py b/src/maasserver/models/__init__.py
index 9177321..fc75e92 100644
--- a/src/maasserver/models/__init__.py
+++ b/src/maasserver/models/__init__.py
@@ -60,6 +60,7 @@ __all__ = [
60 'PodStoragePool',60 'PodStoragePool',
61 'RackController',61 'RackController',
62 'RAID',62 'RAID',
63 'RBACSync',
63 'RDNS',64 'RDNS',
64 'RegionController',65 'RegionController',
65 'RegionControllerProcess',66 'RegionControllerProcess',
@@ -167,6 +168,7 @@ from maasserver.models.partitiontable import PartitionTable
167from maasserver.models.physicalblockdevice import PhysicalBlockDevice168from maasserver.models.physicalblockdevice import PhysicalBlockDevice
168from maasserver.models.podhints import PodHints169from maasserver.models.podhints import PodHints
169from maasserver.models.podstoragepool import PodStoragePool170from maasserver.models.podstoragepool import PodStoragePool
171from maasserver.models.rbacsync import RBACSync
170from maasserver.models.rdns import RDNS172from maasserver.models.rdns import RDNS
171from maasserver.models.regioncontrollerprocess import RegionControllerProcess173from maasserver.models.regioncontrollerprocess import RegionControllerProcess
172from maasserver.models.regioncontrollerprocessendpoint import (174from maasserver.models.regioncontrollerprocessendpoint import (
diff --git a/src/maasserver/models/rbacsync.py b/src/maasserver/models/rbacsync.py
173new file mode 100644175new file mode 100644
index 0000000..40905db
--- /dev/null
+++ b/src/maasserver/models/rbacsync.py
@@ -0,0 +1,95 @@
1# Copyright 2018 Canonical Ltd. This software is licensed under the
2# GNU Affero General Public License version 3 (see the file LICENSE).
3
4"""RBACSync objects."""
5
6__all__ = [
7 "RBACSync",
8]
9
10from django.db.models import (
11 Manager,
12 Model,
13)
14from django.db.models.fields import (
15 CharField,
16 DateTimeField,
17 IntegerField,
18)
19from maasserver import DefaultMeta
20
21
22class RBAC_ACTION:
23 #: Perform a full sync.
24 FULL = 'full'
25 #: Add a new resource.
26 ADD = 'add'
27 #: Update a resource.
28 UPDATE = 'update'
29 #: Remove a resource.
30 REMOVE = 'remove'
31
32
33RBAC_ACTION_CHOICES = [
34 (RBAC_ACTION.FULL, 'full'),
35 (RBAC_ACTION.ADD, 'add'),
36 (RBAC_ACTION.UPDATE, 'update'),
37 (RBAC_ACTION.REMOVE, 'remove'),
38]
39
40
41class RBACSyncManager(Manager):
42 """Manager for `RBACSync` records."""
43
44 def changes(self):
45 """Returns the changes that have occurred."""
46 return list(self.order_by('id'))
47
48 def clear(self):
49 """Deletes all `RBACSync`."""
50 self.all().delete()
51
52
53class RBACSync(Model):
54 """A row in this table denotes a change that requires information RBAC
55 micro-service to be updated.
56
57 Typically this will be populated by a trigger within the database. A
58 listeners in regiond will be notified and consult the un-synced records
59 in this table. This way we can consistently publish RBAC information to the
60 RBAC service in an HA environment.
61 """
62
63 class Meta(DefaultMeta):
64 """Default meta."""
65
66 objects = RBACSyncManager()
67
68 action = CharField(
69 editable=False, max_length=6, null=False, blank=True,
70 choices=RBAC_ACTION_CHOICES, default=RBAC_ACTION.FULL,
71 help_text="Action that should occur on the RBAC service.")
72
73 # An '' string is used when action is 'full'.
74 resource_type = CharField(
75 editable=False, max_length=255, null=False, blank=True,
76 help_text="Resource type that as been added/updated/removed.")
77
78 # A `None` is used when action is 'full'.
79 resource_id = IntegerField(
80 editable=False, null=True, blank=True,
81 help_text="Resource ID that has been added/updated/removed.")
82
83 # A '' string is used when action is 'full'.
84 resource_name = CharField(
85 editable=False, max_length=255, null=False, blank=True,
86 help_text="Resource name that has been added/updated/removed.")
87
88 # This field is informational.
89 created = DateTimeField(
90 editable=False, null=False, auto_now=False, auto_now_add=True)
91
92 # This field is informational.
93 source = CharField(
94 editable=False, max_length=255, null=False, blank=True,
95 help_text="A brief explanation what changed.")
diff --git a/src/maasserver/models/tests/test_rbacsync.py b/src/maasserver/models/tests/test_rbacsync.py
0new file mode 10064496new file mode 100644
index 0000000..8d1f670
--- /dev/null
+++ b/src/maasserver/models/tests/test_rbacsync.py
@@ -0,0 +1,42 @@
1# Copyright 2018 Canonical Ltd. This software is licensed under the
2# GNU Affero General Public License version 3 (see the file LICENSE).
3
4"""Tests for RBACSync models."""
5
6__all__ = []
7
8from maasserver.models.rbacsync import RBACSync
9from maasserver.testing.testcase import MAASServerTestCase
10from testtools.matchers import (
11 Equals,
12 HasLength,
13)
14
15
16class TestRBACSync(MAASServerTestCase):
17 """Test `RBACSync`."""
18
19 def setUp(self):
20 super(TestRBACSync, self).setUp()
21 # These tests expect the RBACSync table to be empty.
22 RBACSync.objects.all().delete()
23
24 def test_changes(self):
25 synced = [
26 RBACSync.objects.create()
27 for _ in range(3)
28 ]
29 self.assertThat(
30 RBACSync.objects.changes(), Equals(synced))
31
32 def test_clear_does_nothing_when_nothing(self):
33 self.assertThat(RBACSync.objects.all(), HasLength(0))
34 RBACSync.objects.clear()
35 self.assertThat(RBACSync.objects.all(), HasLength(0))
36
37 def test_clear_removes_all(self):
38 for _ in range(3):
39 RBACSync.objects.create()
40 self.assertThat(RBACSync.objects.all(), HasLength(3))
41 RBACSync.objects.clear()
42 self.assertThat(RBACSync.objects.all(), HasLength(0))
diff --git a/src/maasserver/triggers/system.py b/src/maasserver/triggers/system.py
index ef0f07e..f73d11e 100644
--- a/src/maasserver/triggers/system.py
+++ b/src/maasserver/triggers/system.py
@@ -1654,6 +1654,130 @@ PEER_PROXY_CONFIG_UPDATE = dedent("""\
1654 """)1654 """)
16551655
16561656
1657# Triggered when RBAC need to be synced. In essense this means on
1658# insert into maasserver_rbacsync.
1659RBAC_SYNC = dedent("""\
1660 CREATE OR REPLACE FUNCTION sys_rbac_sync()
1661 RETURNS trigger AS $$
1662 BEGIN
1663 PERFORM pg_notify('sys_rbac', '');
1664 RETURN NEW;
1665 END;
1666 $$ LANGUAGE plpgsql;
1667 """)
1668
1669
1670# Procedure to mark RBAC as needing a sync.
1671RBAC_SYNC_UPDATE = dedent("""\
1672 CREATE OR REPLACE FUNCTION sys_rbac_sync_update(
1673 reason text,
1674 action text DEFAULT 'full',
1675 resource_type text DEFAULT '',
1676 resource_id int DEFAULT NULL,
1677 resource_name text DEFAULT '')
1678 RETURNS void as $$
1679 BEGIN
1680 INSERT INTO maasserver_rbacsync
1681 (created, source, action, resource_type, resource_id, resource_name)
1682 VALUES (
1683 now(), substring(reason FOR 255),
1684 action, resource_type, resource_id, resource_name);
1685 END;
1686 $$ LANGUAGE plpgsql;
1687 """)
1688
1689
1690# Triggered when a new resource pool is added. Notifies that RBAC needs
1691# to be synced.
1692RBAC_RPOOL_INSERT = dedent("""\
1693 CREATE OR REPLACE FUNCTION sys_rbac_rpool_insert()
1694 RETURNS trigger as $$
1695 BEGIN
1696 PERFORM sys_rbac_sync_update(
1697 'added resource pool ' || NEW.name,
1698 'add', 'resource-pool', NEW.id, NEW.name);
1699 RETURN NEW;
1700 END;
1701 $$ LANGUAGE plpgsql;
1702 """)
1703
1704
1705# Triggered when a resource pool is updated. Notifies that RBAC needs
1706# to be synced. Only watches name.
1707RBAC_RPOOL_UPDATE = dedent("""\
1708 CREATE OR REPLACE FUNCTION sys_rbac_rpool_update()
1709 RETURNS trigger as $$
1710 DECLARE
1711 changes text[];
1712 BEGIN
1713 IF OLD.name != NEW.name THEN
1714 PERFORM sys_rbac_sync_update(
1715 'renamed resource pool ' || OLD.name || ' to ' || NEW.name,
1716 'update', 'resource-pool', OLD.id, NEW.name);
1717 END IF;
1718 RETURN NEW;
1719 END;
1720 $$ LANGUAGE plpgsql;
1721 """)
1722
1723
1724# Triggered when a resource pool is deleted. Notifies that RBAC needs
1725# to be synced.
1726RBAC_RPOOL_DELETE = dedent("""\
1727 CREATE OR REPLACE FUNCTION sys_rbac_rpool_delete()
1728 RETURNS trigger as $$
1729 BEGIN
1730 PERFORM sys_rbac_sync_update(
1731 'removed resource pool ' || OLD.name,
1732 'remove', 'resource-pool', OLD.id, OLD.name);
1733 RETURN OLD;
1734 END;
1735 $$ LANGUAGE plpgsql;
1736 """)
1737
1738
1739# Triggered when the Candid/RBAC settings are inserted. Notifies that RBAC
1740# needs to be synced.
1741RBAC_CONFIG_INSERT = dedent("""\
1742 CREATE OR REPLACE FUNCTION sys_rbac_config_insert()
1743 RETURNS trigger as $$
1744 BEGIN
1745 IF (NEW.name = 'external_auth_url' OR
1746 NEW.name = 'external_auth_user' OR
1747 NEW.name = 'external_auth_key' OR
1748 NEW.name = 'rbac_url') THEN
1749 PERFORM sys_rbac_sync_update(
1750 'configuration ' || NEW.name || ' set to ' ||
1751 COALESCE(NEW.value, 'NULL'));
1752 END IF;
1753 RETURN NEW;
1754 END;
1755 $$ LANGUAGE plpgsql;
1756 """)
1757
1758
1759# Triggered when the Candid/RBAC settings are updated. Notifies that RBAC
1760# needs to be synced. The external_auth_* keys are included that way if
1761# the agent account that MAAS uses to update RBAC is updated in MAAS, MAAS will
1762# ensure that a full sync of data is performed.
1763RBAC_CONFIG_UPDATE = dedent("""\
1764 CREATE OR REPLACE FUNCTION sys_rbac_config_update()
1765 RETURNS trigger as $$
1766 BEGIN
1767 IF (OLD.value != NEW.value AND (
1768 NEW.name = 'external_auth_url' OR
1769 NEW.name = 'external_auth_user' OR
1770 NEW.name = 'external_auth_key' OR
1771 NEW.name = 'rbac_url')) THEN
1772 PERFORM sys_rbac_sync_update(
1773 'configuration ' || NEW.name || ' changed to ' || NEW.value);
1774 END IF;
1775 RETURN NEW;
1776 END;
1777 $$ LANGUAGE plpgsql;
1778 """)
1779
1780
1657def render_sys_proxy_procedure(proc_name, on_delete=False):1781def render_sys_proxy_procedure(proc_name, on_delete=False):
1658 """Render a database procedure with name `proc_name` that notifies that a1782 """Render a database procedure with name `proc_name` that notifies that a
1659 proxy update is needed.1783 proxy update is needed.
@@ -1914,3 +2038,31 @@ def register_system_triggers():
1914 register_trigger(2038 register_trigger(
1915 "maasserver_config", "sys_proxy_config_use_peer_proxy_update",2039 "maasserver_config", "sys_proxy_config_use_peer_proxy_update",
1916 "update")2040 "update")
2041
2042 # - RBACSync
2043 register_procedure(RBAC_SYNC)
2044 register_trigger(
2045 "maasserver_rbacsync",
2046 "sys_rbac_sync", "insert")
2047 register_procedure(RBAC_SYNC_UPDATE)
2048
2049 # - ResourcePool
2050 register_procedure(RBAC_RPOOL_INSERT)
2051 register_trigger(
2052 "maasserver_resourcepool", "sys_rbac_rpool_insert", "insert")
2053 register_procedure(RBAC_RPOOL_UPDATE)
2054 register_trigger(
2055 "maasserver_resourcepool", "sys_rbac_rpool_update", "update")
2056 register_procedure(RBAC_RPOOL_DELETE)
2057 register_trigger(
2058 "maasserver_resourcepool", "sys_rbac_rpool_delete", "delete")
2059
2060 # - Config (Candid/RBAC)
2061 register_procedure(RBAC_CONFIG_INSERT)
2062 register_trigger(
2063 "maasserver_config", "sys_rbac_config_insert",
2064 "insert")
2065 register_procedure(RBAC_CONFIG_UPDATE)
2066 register_trigger(
2067 "maasserver_config", "sys_rbac_config_update",
2068 "update")
diff --git a/src/maasserver/triggers/testing.py b/src/maasserver/triggers/testing.py
index 73a6b48..0fec0ac 100644
--- a/src/maasserver/triggers/testing.py
+++ b/src/maasserver/triggers/testing.py
@@ -41,6 +41,7 @@ from maasserver.models.packagerepository import PackageRepository
41from maasserver.models.partition import Partition41from maasserver.models.partition import Partition
42from maasserver.models.partitiontable import PartitionTable42from maasserver.models.partitiontable import PartitionTable
43from maasserver.models.physicalblockdevice import PhysicalBlockDevice43from maasserver.models.physicalblockdevice import PhysicalBlockDevice
44from maasserver.models.rbacsync import RBACSync
44from maasserver.models.regioncontrollerprocess import RegionControllerProcess45from maasserver.models.regioncontrollerprocess import RegionControllerProcess
45from maasserver.models.regioncontrollerprocessendpoint import (46from maasserver.models.regioncontrollerprocessendpoint import (
46 RegionControllerProcessEndpoint,47 RegionControllerProcessEndpoint,
@@ -847,3 +848,47 @@ class DNSHelpersMixin:
847 self.assertThat(848 self.assertThat(
848 new.serial, GreaterThan(old.serial),849 new.serial, GreaterThan(old.serial),
849 "DNS has not been published again.")850 "DNS has not been published again.")
851
852
853class RBACHelpersMixin:
854 """Helper to get the latest rbac sync."""
855
856 @transactional
857 def getSynced(self):
858 try:
859 return RBACSync.objects.order_by('-id').first()
860 except RBACSync.DoesNotExist:
861 return None
862
863 @inlineCallbacks
864 def captureSynced(self):
865 """Capture the most recent `RBACSync` record."""
866 self.__synced = yield deferToDatabase(self.getSynced)
867 returnValue(self.__synced)
868
869 def getCapturedSynced(self):
870 """Return the captured sync record."""
871 try:
872 return self.__synced
873 except AttributeError:
874 self.fail(
875 "No reference modification has been captured; "
876 "use `captureSynced` before calling "
877 "`getCapturedSynced`.")
878
879 @inlineCallbacks
880 def assertSynced(self):
881 """Assert there's a newer `RBACSync` record.
882
883 Call `captureSynced` first to obtain a reference record.
884 """
885 old = self.getCapturedSynced()
886 new = yield self.captureSynced()
887 if old is None:
888 self.assertThat(
889 new, Not(Is(None)),
890 "RBAC sync tracking has not been modified at all.")
891 else:
892 self.assertThat(
893 new.id, GreaterThan(old.id),
894 "RBAC sync tracking has not been modified again.")
diff --git a/src/maasserver/triggers/tests/test_init.py b/src/maasserver/triggers/tests/test_init.py
index e5bce9e..d03a833 100644
--- a/src/maasserver/triggers/tests/test_init.py
+++ b/src/maasserver/triggers/tests/test_init.py
@@ -105,6 +105,12 @@ class TestTriggersUsed(MAASServerTestCase):
105 "subnet_sys_proxy_subnet_insert",105 "subnet_sys_proxy_subnet_insert",
106 "subnet_sys_proxy_subnet_update",106 "subnet_sys_proxy_subnet_update",
107 "vlan_sys_dhcp_vlan_update",107 "vlan_sys_dhcp_vlan_update",
108 "rbacsync_sys_rbac_sync",
109 "resourcepool_sys_rbac_rpool_insert",
110 "resourcepool_sys_rbac_rpool_update",
111 "resourcepool_sys_rbac_rpool_delete",
112 "config_sys_rbac_config_insert",
113 "config_sys_rbac_config_update",
108 }114 }
109115
110 triggers_websocket = {116 triggers_websocket = {
diff --git a/src/maasserver/triggers/tests/test_system.py b/src/maasserver/triggers/tests/test_system.py
index 1fe4615..eeccb47 100644
--- a/src/maasserver/triggers/tests/test_system.py
+++ b/src/maasserver/triggers/tests/test_system.py
@@ -61,6 +61,11 @@ class TestTriggers(MAASServerTestCase):
61 "subnet_sys_proxy_subnet_insert",61 "subnet_sys_proxy_subnet_insert",
62 "subnet_sys_proxy_subnet_update",62 "subnet_sys_proxy_subnet_update",
63 "subnet_sys_proxy_subnet_delete",63 "subnet_sys_proxy_subnet_delete",
64 "resourcepool_sys_rbac_rpool_insert",
65 "resourcepool_sys_rbac_rpool_update",
66 "resourcepool_sys_rbac_rpool_delete",
67 "config_sys_rbac_config_insert",
68 "config_sys_rbac_config_update",
64 ]69 ]
65 sql, args = psql_array(triggers, sql_type="text")70 sql, args = psql_array(triggers, sql_type="text")
66 with closing(connection.cursor()) as cursor:71 with closing(connection.cursor()) as cursor:
diff --git a/src/maasserver/triggers/tests/test_system_listener.py b/src/maasserver/triggers/tests/test_system_listener.py
index d6e2adc..c543ca3 100644
--- a/src/maasserver/triggers/tests/test_system_listener.py
+++ b/src/maasserver/triggers/tests/test_system_listener.py
@@ -36,6 +36,7 @@ from maasserver.testing.testcase import (
36from maasserver.triggers.system import register_system_triggers36from maasserver.triggers.system import register_system_triggers
37from maasserver.triggers.testing import (37from maasserver.triggers.testing import (
38 DNSHelpersMixin,38 DNSHelpersMixin,
39 RBACHelpersMixin,
39 TransactionalHelpersMixin,40 TransactionalHelpersMixin,
40)41)
41from maasserver.utils.orm import transactional42from maasserver.utils.orm import transactional
@@ -4688,3 +4689,183 @@ class TestProxyListener(
4688 yield dv.get(timeout=2)4689 yield dv.get(timeout=2)
4689 finally:4690 finally:
4690 yield listener.stopService()4691 yield listener.stopService()
4692
4693
4694class TestRBACResourcePoolListener(
4695 MAASTransactionServerTestCase, TransactionalHelpersMixin,
4696 RBACHelpersMixin):
4697 """End-to-end test for the resource pool RBAC triggers code."""
4698
4699 @wait_for_reactor
4700 @inlineCallbacks
4701 def test_sends_message_for_resource_pool_insert(self):
4702 yield deferToDatabase(register_system_triggers)
4703 yield self.captureSynced()
4704 name = factory.make_name('pool')
4705 dv = DeferredValue()
4706 listener = self.make_listener_without_delay()
4707 listener.register(
4708 "sys_rbac", lambda *args: dv.set(args))
4709 yield listener.startService()
4710 try:
4711 pool = yield deferToDatabase(
4712 self.create_resource_pool, {'name': name})
4713 yield dv.get(timeout=2)
4714 yield self.assertSynced()
4715 finally:
4716 yield listener.stopService()
4717 change = self.getCapturedSynced()
4718 self.assertThat(
4719 change.source,
4720 Equals("added resource pool %s" % name))
4721 self.assertThat(
4722 change.action, Equals("add"))
4723 self.assertThat(
4724 change.resource_type, Equals("resource-pool"))
4725 self.assertThat(
4726 change.resource_id, Equals(pool.id))
4727 self.assertThat(
4728 change.resource_name, Equals(name))
4729
4730 @wait_for_reactor
4731 @inlineCallbacks
4732 def test_sends_message_for_resource_pool_update(self):
4733 yield deferToDatabase(register_system_triggers)
4734 pool = yield deferToDatabase(
4735 self.create_resource_pool, {})
4736 pool_name = factory.make_name('pool')
4737 yield self.captureSynced()
4738 dv = DeferredValue()
4739 listener = self.make_listener_without_delay()
4740 listener.register(
4741 "sys_rbac", lambda *args: dv.set(args))
4742 yield listener.startService()
4743 try:
4744 yield deferToDatabase(self.update_resource_pool, pool.id, {
4745 "name": pool_name,
4746 })
4747 yield dv.get(timeout=2)
4748 yield self.assertSynced()
4749 finally:
4750 yield listener.stopService()
4751 change = self.getCapturedSynced()
4752 self.assertThat(
4753 change.source,
4754 Equals("renamed resource pool %s to %s" % (pool.name, pool_name)))
4755 self.assertThat(
4756 change.action, Equals("update"))
4757 self.assertThat(
4758 change.resource_type, Equals("resource-pool"))
4759 self.assertThat(
4760 change.resource_id, Equals(pool.id))
4761 self.assertThat(
4762 change.resource_name, Equals(pool_name))
4763
4764 @wait_for_reactor
4765 @inlineCallbacks
4766 def test_sends_message_for_resource_pool_delete(self):
4767 yield deferToDatabase(register_system_triggers)
4768 pool = yield deferToDatabase(self.create_resource_pool)
4769 yield self.captureSynced()
4770 dv = DeferredValue()
4771 listener = self.make_listener_without_delay()
4772 listener.register(
4773 "sys_rbac", lambda *args: dv.set(args))
4774 yield listener.startService()
4775 try:
4776 yield deferToDatabase(self.delete_resource_pool, pool.id)
4777 yield dv.get(timeout=2)
4778 yield self.assertSynced()
4779 finally:
4780 yield listener.stopService()
4781 change = self.getCapturedSynced()
4782 self.assertThat(
4783 change.source,
4784 Equals("removed resource pool %s" % pool.name))
4785 self.assertThat(
4786 change.action, Equals("remove"))
4787 self.assertThat(
4788 change.resource_type, Equals("resource-pool"))
4789 self.assertThat(
4790 change.resource_id, Equals(pool.id))
4791 self.assertThat(
4792 change.resource_name, Equals(pool.name))
4793
4794
4795class TestRBACConfigListener(
4796 MAASTransactionServerTestCase, TransactionalHelpersMixin,
4797 RBACHelpersMixin):
4798 """End-to-end test for the config RBAC triggers code."""
4799
4800 scenarios = (
4801 ('external_auth_url', {
4802 'config': 'external_auth_url',
4803 }),
4804 ('external_auth_user', {
4805 'config': 'external_auth_user',
4806 }),
4807 ('external_auth_key', {
4808 'config': 'external_auth_key',
4809 }),
4810 ('rbac_url', {
4811 'config': 'rbac_url',
4812 }),
4813 )
4814
4815 @wait_for_reactor
4816 @inlineCallbacks
4817 def test_sends_message_for_config_insert(self):
4818 new_value = factory.make_name("value")
4819 yield deferToDatabase(register_system_triggers)
4820 yield self.captureSynced()
4821 dv = DeferredValue()
4822 listener = self.make_listener_without_delay()
4823 listener.register(
4824 "sys_rbac", lambda *args: dv.set(args))
4825 yield listener.startService()
4826 try:
4827 yield deferToDatabase(
4828 Config.objects.set_config,
4829 self.config, new_value)
4830 yield dv.get(timeout=2)
4831 yield self.assertSynced()
4832 finally:
4833 yield listener.stopService()
4834 change = self.getCapturedSynced()
4835 self.assertThat(
4836 change.source, Equals(
4837 "configuration %s set to %s"
4838 % (self.config, json.dumps(new_value))))
4839 self.assertThat(
4840 change.action, Equals("full"))
4841
4842 @wait_for_reactor
4843 @inlineCallbacks
4844 def test_sends_message_for_config_update(self):
4845 old_value = factory.make_name("old")
4846 new_value = factory.make_name("new")
4847 yield deferToDatabase(register_system_triggers)
4848 yield deferToDatabase(
4849 Config.objects.set_config,
4850 self.config, old_value)
4851 yield self.captureSynced()
4852 dv = DeferredValue()
4853 listener = self.make_listener_without_delay()
4854 listener.register(
4855 "sys_rbac", lambda *args: dv.set(args))
4856 yield listener.startService()
4857 try:
4858 yield deferToDatabase(
4859 Config.objects.set_config,
4860 self.config, new_value)
4861 yield dv.get(timeout=2)
4862 yield self.assertSynced()
4863 finally:
4864 yield listener.stopService()
4865 change = self.getCapturedSynced()
4866 self.assertThat(
4867 change.source, Equals(
4868 "configuration %s changed to %s"
4869 % (self.config, json.dumps(new_value))))
4870 self.assertThat(
4871 change.action, Equals("full"))

Subscribers

People subscribed via source and target branches