Beispiel #1
0
 def test_get_resources(self):
     resources = [
         {
             'identifier': '1',
             'name': 'pool-1',
         },
         {
             'identifier': '2',
             'name': 'pool-2',
         },
     ]
     response = mock.MagicMock(status_code=200)
     response.json.return_value = resources
     self.mock_request.return_value = response
     self.assertCountEqual(self.client.get_resources('resource-pool'), [
         Resource(identifier='1', name='pool-1'),
         Resource(identifier='2', name='pool-2'),
     ])
     self.assertThat(
         self.mock_request,
         MockCalledOnceWith('GET', 'https://rbac.example.com/api/'
                            'service/1.0/resources/resource-pool',
                            auth=mock.ANY,
                            cookies=mock.ANY,
                            json=None))
Beispiel #2
0
 def test__rbacDifference(self):
     service = self.make_service(sentinel.listener)
     changes = [
         RBACSync(action=RBAC_ACTION.UPDATE,
                  resource_id=1,
                  resource_name='r-1'),
         RBACSync(action=RBAC_ACTION.ADD,
                  resource_id=2,
                  resource_name='r-2'),
         RBACSync(action=RBAC_ACTION.UPDATE,
                  resource_id=3,
                  resource_name='r-3'),
         RBACSync(action=RBAC_ACTION.REMOVE,
                  resource_id=1,
                  resource_name='r-1'),
         RBACSync(action=RBAC_ACTION.UPDATE,
                  resource_id=3,
                  resource_name='r-3-updated'),
         RBACSync(action=RBAC_ACTION.ADD,
                  resource_id=4,
                  resource_name='r-4'),
         RBACSync(action=RBAC_ACTION.REMOVE,
                  resource_id=4,
                  resource_name='r-4'),
     ]
     self.assertEquals(([
         Resource(identifier=2, name='r-2'),
         Resource(identifier=3, name='r-3-updated'),
     ], {
         1,
     }), service._rbacDifference(changes))
Beispiel #3
0
 def test_get_resources(self):
     resources = [
         {
             "identifier": "1",
             "name": "pool-1"
         },
         {
             "identifier": "2",
             "name": "pool-2"
         },
     ]
     response = mock.MagicMock(status_code=200)
     response.json.return_value = resources
     self.mock_request.return_value = response
     self.assertCountEqual(
         self.client.get_resources("resource-pool"),
         [
             Resource(identifier="1", name="pool-1"),
             Resource(identifier="2", name="pool-2"),
         ],
     )
     self.assertThat(
         self.mock_request,
         MockCalledOnceWith(
             "GET",
             "https://rbac.example.com/api/"
             "service/v1/resources/resource-pool",
             auth=mock.ANY,
             cookies=mock.ANY,
             json=None,
         ),
     )
Beispiel #4
0
 def test_update_resources(self):
     updates = [
         Resource(identifier='1', name='pool-1'),
         Resource(identifier='2', name='pool-2'),
     ]
     removals = [11, 22, 33]
     json = {
         'last-sync-id': 'a-b-c',
         'updates': [
             {
                 'identifier': '1',
                 'name': 'pool-1',
             },
             {
                 'identifier': '2',
                 'name': 'pool-2',
             },
         ],
         'removals': ['11', '22', '33']
     }
     response = mock.MagicMock(status_code=200)
     response.json.return_value = {'sync-id': 'x-y-z'}
     self.mock_request.return_value = response
     sync_id = self.client.update_resources(
         'resource-pool', updates=updates, removals=removals,
         last_sync_id='a-b-c')
     self.assertEqual(sync_id, 'x-y-z')
     self.assertThat(
         self.mock_request,
         MockCalledOnceWith(
             'POST',
             'https://rbac.example.com/api/'
             'service/1.0/resources/resource-pool',
             auth=mock.ANY, cookies=mock.ANY, json=json))
Beispiel #5
0
 def test_update_resources_no_sync_id(self):
     updates = [
         Resource(identifier="1", name="pool-1"),
         Resource(identifier="2", name="pool-2"),
     ]
     removals = [11, 22, 33]
     # removals are ignored
     json = {
         "last-sync-id": None,
         "updates": [
             {"identifier": "1", "name": "pool-1"},
             {"identifier": "2", "name": "pool-2"},
         ],
         "removals": [],
     }
     response = mock.MagicMock(status_code=200)
     response.json.return_value = {"sync-id": "x-y-z"}
     self.mock_request.return_value = response
     sync_id = self.client.update_resources(
         "resource-pool", updates=updates, removals=removals
     )
     self.assertEqual(sync_id, "x-y-z")
     self.assertThat(
         self.mock_request,
         MockCalledOnceWith(
             "POST",
             "https://rbac.example.com/api/"
             "service/v1/resources/resource-pool",
             auth=mock.ANY,
             cookies=mock.ANY,
             json=json,
         ),
     )
Beispiel #6
0
 def test_update_resources_sync_conflict(self):
     updates = [
         Resource(identifier='1', name='pool-1'),
         Resource(identifier='2', name='pool-2'),
     ]
     removals = [11, 22, 33]
     response = mock.MagicMock(status_code=int(http.client.CONFLICT))
     response.json.return_value = {'sync-id': 'x-y-z'}
     self.mock_request.return_value = response
     self.assertRaises(SyncConflictError,
                       self.client.update_resources,
                       'resource-pool',
                       updates=updates,
                       removals=removals,
                       last_sync_id='a-b-c')
Beispiel #7
0
 def _rbacDifference(self, changes):
     """Return the only the changes that need to be pushed to RBAC."""
     # Removals are calculated first. A removal is never followed by an
     # update and `resource_id` is never re-used.
     removals = set(change.resource_id for change in changes
                    if change.action == RBAC_ACTION.REMOVE)
     # Changes are ordered from oldest to lates. The latest change will
     # be the last item of that `resource_id` in the dictionary.
     updates = {
         change.resource_id: change.resource_name
         for change in changes if (change.resource_id not in removals
                                   and change.action != RBAC_ACTION.REMOVE)
     }
     # Any additions with also a removal is not sync to RBAC.
     for change in changes:
         if change.action == RBAC_ACTION.ADD:
             if change.resource_id in removals:
                 removals.remove(change.resource_id)
     return (
         sorted(
             [
                 Resource(identifier=res_id, name=res_name)
                 for res_id, res_name in updates.items()
             ],
             key=attrgetter("identifier"),
         ),
         removals,
     )
Beispiel #8
0
 def make_resource_pools(self):
     rpools = [factory.make_ResourcePool() for _ in range(3)]
     return rpools, sorted([
         Resource(identifier=rpool.id, name=rpool.name)
         for rpool in ResourcePool.objects.all()
     ],
                           key=attrgetter('identifier'))
Beispiel #9
0
 def test_update_resources_sync_conflict(self):
     updates = [
         Resource(identifier="1", name="pool-1"),
         Resource(identifier="2", name="pool-2"),
     ]
     removals = [11, 22, 33]
     response = mock.MagicMock(status_code=int(http.client.CONFLICT))
     response.json.return_value = {"sync-id": "x-y-z"}
     self.mock_request.return_value = response
     self.assertRaises(
         SyncConflictError,
         self.client.update_resources,
         "resource-pool",
         updates=updates,
         removals=removals,
         last_sync_id="a-b-c",
     )
Beispiel #10
0
    def _rbacSync(self):
        """Sync the RBAC information."""
        changes = RBACSync.objects.changes()
        if not changes and self.rbacInit:
            # Nothing has changed, meaning another region already took care
            # of performing the update.
            return None

        client = self._getRBACClient()
        if client is None:
            # RBAC is disabled, do nothing.
            RBACSync.objects.clear()  # Changes not needed, RBAC disabled.
            return None

        # XXX blake_r 2018-10-04 - This can be smarter by looking at the action
        # and resource_type information in the changes and include the
        # last_sync_id in the request, to only update what changed.
        resources = [
            Resource(identifier=rpool.id, name=rpool.name)
            for rpool in ResourcePool.objects.all()
        ]
        new_sync_id = client.update_resources('resource-pool',
                                              updates=resources)
        RBACLastSync.objects.update_or_create(
            resource_type='resource-pool', defaults={'sync_id': new_sync_id})

        if not self.rbacInit:
            # This was initial sync on start-up.
            RBACSync.objects.clear()
            return []

        # Return the changes and clear the table, so new changes will be
        # tracked. Being inside a transaction allows us not to worry about
        # a new change already existing with the clear.
        changes = [change.source for change in changes]
        RBACSync.objects.clear()
        return changes
Beispiel #11
0
    def _rbacSync(self):
        """Sync the RBAC information."""
        # Currently this whole method is scoped to dealing with
        # 'resource-pool'. As more items are synced to RBAC this
        # will need to be adjusted to handle multiple.
        changes = RBACSync.objects.changes('resource-pool')
        if not changes and self.rbacInit:
            # Nothing has changed, meaning another region already took care
            # of performing the update.
            return None

        client = self._getRBACClient()
        if client is None:
            # RBAC is disabled, do nothing.
            RBACSync.objects.clear('resource-pool')  # Changes not needed.
            return None

        # Push the resource information based on the last sync.
        new_sync_id = None
        try:
            last_sync = RBACLastSync.objects.get(resource_type='resource-pool')
        except RBACLastSync.DoesNotExist:
            last_sync = None
        if last_sync is None or self._rbacNeedsFull(changes):
            # First sync or requires a full sync.
            resources = [
                Resource(identifier=rpool.id, name=rpool.name)
                for rpool in ResourcePool.objects.order_by('id')
            ]
            new_sync_id = client.update_resources('resource-pool',
                                                  updates=resources)
        else:
            # Send only the difference of what has been changed.
            updates, removals = self._rbacDifference(changes)
            if updates or removals:
                try:
                    new_sync_id = client.update_resources(
                        'resource-pool',
                        updates=updates,
                        removals=removals,
                        last_sync_id=last_sync.sync_id)
                except SyncConflictError:
                    # Issue occurred syncing, push all information.
                    resources = [
                        Resource(identifier=rpool.id, name=rpool.name)
                        for rpool in ResourcePool.objects.order_by('id')
                    ]
                    new_sync_id = client.update_resources('resource-pool',
                                                          updates=resources)
        if new_sync_id:
            RBACLastSync.objects.update_or_create(
                resource_type='resource-pool',
                defaults={'sync_id': new_sync_id})

        if not self.rbacInit:
            # This was initial sync on start-up.
            RBACSync.objects.clear('resource-pool')
            return []

        # Return the changes and clear the table, so new changes will be
        # tracked. Being inside a transaction allows us not to worry about
        # a new change already existing with the clear.
        changes = [change.source for change in changes]
        RBACSync.objects.clear('resource-pool')
        return changes
Beispiel #12
0
 def make_resource_pools(self):
     rpools = [factory.make_ResourcePool() for _ in range(3)]
     return rpools, [
         Resource(identifier=rpool.id, name=rpool.name)
         for rpool in ResourcePool.objects.all()
     ]