Felipe Monteiro | 26b7e09 | 2018-07-27 22:15:27 +0100 | [diff] [blame] | 1 | Patrole Test Writing Overview |
| 2 | ============================= |
| 3 | |
| 4 | Introduction |
| 5 | ------------ |
| 6 | |
| 7 | Patrole tests are broken up into 3 stages: |
| 8 | |
| 9 | #. :ref:`rbac-test-setup` |
| 10 | #. :ref:`rbac-test-execution` |
| 11 | #. :ref:`rbac-test-cleanup` |
| 12 | |
Felipe Monteiro | 0170c99 | 2018-07-31 20:10:05 -0400 | [diff] [blame] | 13 | See the :ref:`framework overview documentation <validation-workflow-overview>` |
| 14 | for a high-level explanation of the entire testing work flow and framework |
Felipe Monteiro | 26b7e09 | 2018-07-27 22:15:27 +0100 | [diff] [blame] | 15 | implementation. The guide that follows is concerned with helping developers |
| 16 | know how to write Patrole tests. |
| 17 | |
| 18 | .. _role-overriding: |
| 19 | |
| 20 | Role Overriding |
| 21 | --------------- |
| 22 | |
| 23 | Role overriding is the way Patrole is able to create resources and delete |
| 24 | resources -- including those that require admin credentials -- while still |
| 25 | being able to exercise the same set of Tempest credentials to perform the API |
Mykola Yakovliev | e0f3550 | 2018-09-26 18:26:57 -0500 | [diff] [blame] | 26 | action that authorizes the policy under test, by manipulating roles of the |
| 27 | Tempest credentials. |
Felipe Monteiro | 26b7e09 | 2018-07-27 22:15:27 +0100 | [diff] [blame] | 28 | |
| 29 | Patrole implicitly splits up each test into 3 stages: set up, test execution, |
| 30 | and teardown. |
| 31 | |
| 32 | The role workflow is as follows: |
| 33 | |
| 34 | #. Setup: Admin role is used automatically. The primary credentials are |
| 35 | overridden with the admin role. |
Mykola Yakovliev | e0f3550 | 2018-09-26 18:26:57 -0500 | [diff] [blame] | 36 | #. Test execution: ``[patrole] rbac_test_roles`` is used manually via the |
Sergey Vilgelm | 78e7f57 | 2019-02-03 10:35:01 -0600 | [diff] [blame] | 37 | call to ``with self.override_role()``. Everything that |
Felipe Monteiro | 26b7e09 | 2018-07-27 22:15:27 +0100 | [diff] [blame] | 38 | is executed within this contextmanager uses the primary |
Mykola Yakovliev | e0f3550 | 2018-09-26 18:26:57 -0500 | [diff] [blame] | 39 | credentials overridden with the ``[patrole] rbac_test_roles``. |
Felipe Monteiro | 26b7e09 | 2018-07-27 22:15:27 +0100 | [diff] [blame] | 40 | #. Teardown: Admin role is used automatically. The primary credentials have |
| 41 | been overridden with the admin role. |
| 42 | |
| 43 | .. _rbac-test-setup: |
| 44 | |
| 45 | Test Setup |
| 46 | ---------- |
| 47 | |
| 48 | Automatic role override in background. |
| 49 | |
| 50 | Resources can be set up inside the ``resource_setup`` class method that Tempest |
| 51 | provides. These resources are typically reserved for "expensive" resources |
| 52 | in terms of memory or storage requirements, like volumes and VMs. These |
| 53 | resources are **always** created via the admin role; Patrole automatically |
| 54 | handles this. |
| 55 | |
| 56 | Like Tempest, however, Patrole must also create resources inside tests |
| 57 | themselves. At the beginning of each test, the primary credentials have already |
| 58 | been overridden with the admin role. One can create whatever test-level |
| 59 | resources one needs, without having to worry about permissions. |
| 60 | |
| 61 | .. _rbac-test-execution: |
| 62 | |
| 63 | Test Execution |
| 64 | -------------- |
| 65 | |
| 66 | Manual role override required. |
| 67 | |
| 68 | "Test execution" here means calling the API endpoint that enforces the policy |
| 69 | action expected by the ``rbac_rule_validation`` decorator. Test execution |
| 70 | should be performed *only after* calling |
Sergey Vilgelm | 78e7f57 | 2019-02-03 10:35:01 -0600 | [diff] [blame] | 71 | ``with self.override_role()``. |
Felipe Monteiro | 26b7e09 | 2018-07-27 22:15:27 +0100 | [diff] [blame] | 72 | |
| 73 | Immediately after that call, the API endpoint that enforces the policy should |
| 74 | be called. |
| 75 | |
| 76 | Examples |
| 77 | ^^^^^^^^ |
| 78 | |
| 79 | Always use the contextmanager before calling the API that enforces the |
| 80 | expected policy action. |
| 81 | |
| 82 | Example:: |
| 83 | |
| 84 | @rbac_rule_validation.action( |
| 85 | service="nova", |
Felipe Monteiro | 59f538f | 2018-08-22 23:34:40 -0400 | [diff] [blame] | 86 | rules=["os_compute_api:os-aggregates:show"]) |
Felipe Monteiro | 26b7e09 | 2018-07-27 22:15:27 +0100 | [diff] [blame] | 87 | def test_show_aggregate_rbac(self): |
| 88 | # Do test setup before the ``override_role`` call. |
| 89 | aggregate_id = self._create_aggregate() |
| 90 | # Call the ``override_role`` method so that the primary credentials |
| 91 | # have the test role needed for test execution. |
Sergey Vilgelm | 78e7f57 | 2019-02-03 10:35:01 -0600 | [diff] [blame] | 92 | with self.override_role(): |
Felipe Monteiro | 26b7e09 | 2018-07-27 22:15:27 +0100 | [diff] [blame] | 93 | self.aggregates_client.show_aggregate(aggregate_id) |
| 94 | |
| 95 | When using a waiter, do the wait outside the contextmanager. "Waiting" always |
| 96 | entails executing a ``GET`` request to the server, until the state of the |
| 97 | returned resource matches a desired state. These ``GET`` requests enforce |
| 98 | a different policy than the one expected. This is undesirable because |
| 99 | Patrole should only test policies in isolation from one another. |
| 100 | |
| 101 | Otherwise, the test result will be tainted, because instead of only the |
| 102 | expected policy getting enforced with the ``os_primary`` role, at least |
| 103 | two policies get enforced. |
| 104 | |
| 105 | Example using waiter:: |
| 106 | |
| 107 | @rbac_rule_validation.action( |
| 108 | service="nova", |
Felipe Monteiro | 59f538f | 2018-08-22 23:34:40 -0400 | [diff] [blame] | 109 | rules=["os_compute_api:os-admin-password"]) |
Felipe Monteiro | 26b7e09 | 2018-07-27 22:15:27 +0100 | [diff] [blame] | 110 | def test_change_server_password(self): |
| 111 | original_password = self.servers_client.show_password( |
| 112 | self.server['id']) |
| 113 | self.addCleanup(self.servers_client.change_password, self.server['id'], |
| 114 | adminPass=original_password) |
| 115 | |
Sergey Vilgelm | 78e7f57 | 2019-02-03 10:35:01 -0600 | [diff] [blame] | 116 | with self.override_role(): |
Felipe Monteiro | 26b7e09 | 2018-07-27 22:15:27 +0100 | [diff] [blame] | 117 | self.servers_client.change_password( |
| 118 | self.server['id'], adminPass=data_utils.rand_password()) |
| 119 | # Call the waiter outside the ``override_role`` contextmanager, so that |
| 120 | # it is executed with admin role. |
| 121 | waiters.wait_for_server_status( |
| 122 | self.servers_client, self.server['id'], 'ACTIVE') |
| 123 | |
| 124 | Below is an example of a method that enforces multiple policies getting |
| 125 | called inside the contextmanager. The ``_complex_setup_method`` below |
| 126 | performs the correct API that enforces the expected policy -- in this |
| 127 | case ``self.resources_client.create_resource`` -- but then proceeds to |
| 128 | use a waiter. |
| 129 | |
| 130 | Incorrect:: |
| 131 | |
| 132 | def _complex_setup_method(self): |
| 133 | resource = self.resources_client.create_resource( |
| 134 | **kwargs)['resource'] |
| 135 | self.addCleanup(test_utils.call_and_ignore_notfound_exc, |
| 136 | self._delete_resource, resource) |
| 137 | waiters.wait_for_resource_status( |
| 138 | self.resources_client, resource['id'], 'available') |
| 139 | return resource |
| 140 | |
| 141 | @rbac_rule_validation.action( |
| 142 | service="example-service", |
Felipe Monteiro | 59f538f | 2018-08-22 23:34:40 -0400 | [diff] [blame] | 143 | rules=["example-rule"]) |
Felipe Monteiro | 26b7e09 | 2018-07-27 22:15:27 +0100 | [diff] [blame] | 144 | def test_change_server_password(self): |
| 145 | # Never call a helper function inside the contextmanager that calls a |
| 146 | # bunch of APIs. Only call the API that enforces the policy action |
| 147 | # contained in the decorator above. |
Sergey Vilgelm | 78e7f57 | 2019-02-03 10:35:01 -0600 | [diff] [blame] | 148 | with self.override_role(): |
Felipe Monteiro | 26b7e09 | 2018-07-27 22:15:27 +0100 | [diff] [blame] | 149 | self._complex_setup_method() |
| 150 | |
| 151 | To fix this test, see the "Example using waiter" section above. It is |
| 152 | recommended to re-implement the logic in a helper method inside a test such |
| 153 | that only the relevant API is called inside the contextmanager, with |
| 154 | everything extraneous outside. |
| 155 | |
| 156 | .. _rbac-test-cleanup: |
| 157 | |
| 158 | Test Cleanup |
| 159 | ------------ |
| 160 | |
| 161 | Automatic role override in background. |
| 162 | |
| 163 | After the test -- no matter whether it ended successfully or in failure -- |
| 164 | the credentials are overridden with the admin role by the Patrole framework, |
| 165 | *before* ``tearDown`` or ``tearDownClass`` are called. This means that |
| 166 | resources are always cleaned up using the admin role. |