blob: ac502103d9fe5537b6a4a0589dca73b39a55f9ab [file] [log] [blame]
Felipe Monteiro26b7e092018-07-27 22:15:27 +01001Patrole Test Writing Overview
2=============================
3
4Introduction
5------------
6
7Patrole 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 Monteiro0170c992018-07-31 20:10:05 -040013See the :ref:`framework overview documentation <validation-workflow-overview>`
14for a high-level explanation of the entire testing work flow and framework
Felipe Monteiro26b7e092018-07-27 22:15:27 +010015implementation. The guide that follows is concerned with helping developers
16know how to write Patrole tests.
17
18.. _role-overriding:
19
20Role Overriding
21---------------
22
23Role overriding is the way Patrole is able to create resources and delete
24resources -- including those that require admin credentials -- while still
25being able to exercise the same set of Tempest credentials to perform the API
Mykola Yakovlieve0f35502018-09-26 18:26:57 -050026action that authorizes the policy under test, by manipulating roles of the
27Tempest credentials.
Felipe Monteiro26b7e092018-07-27 22:15:27 +010028
29Patrole implicitly splits up each test into 3 stages: set up, test execution,
30and teardown.
31
32The role workflow is as follows:
33
34#. Setup: Admin role is used automatically. The primary credentials are
35 overridden with the admin role.
Mykola Yakovlieve0f35502018-09-26 18:26:57 -050036#. Test execution: ``[patrole] rbac_test_roles`` is used manually via the
Sergey Vilgelm78e7f572019-02-03 10:35:01 -060037 call to ``with self.override_role()``. Everything that
Felipe Monteiro26b7e092018-07-27 22:15:27 +010038 is executed within this contextmanager uses the primary
Mykola Yakovlieve0f35502018-09-26 18:26:57 -050039 credentials overridden with the ``[patrole] rbac_test_roles``.
Felipe Monteiro26b7e092018-07-27 22:15:27 +010040#. Teardown: Admin role is used automatically. The primary credentials have
41 been overridden with the admin role.
42
43.. _rbac-test-setup:
44
45Test Setup
46----------
47
48Automatic role override in background.
49
50Resources can be set up inside the ``resource_setup`` class method that Tempest
51provides. These resources are typically reserved for "expensive" resources
52in terms of memory or storage requirements, like volumes and VMs. These
53resources are **always** created via the admin role; Patrole automatically
54handles this.
55
56Like Tempest, however, Patrole must also create resources inside tests
57themselves. At the beginning of each test, the primary credentials have already
58been overridden with the admin role. One can create whatever test-level
59resources one needs, without having to worry about permissions.
60
61.. _rbac-test-execution:
62
63Test Execution
64--------------
65
66Manual role override required.
67
68"Test execution" here means calling the API endpoint that enforces the policy
69action expected by the ``rbac_rule_validation`` decorator. Test execution
70should be performed *only after* calling
Sergey Vilgelm78e7f572019-02-03 10:35:01 -060071``with self.override_role()``.
Felipe Monteiro26b7e092018-07-27 22:15:27 +010072
73Immediately after that call, the API endpoint that enforces the policy should
74be called.
75
76Examples
77^^^^^^^^
78
79Always use the contextmanager before calling the API that enforces the
80expected policy action.
81
82Example::
83
84 @rbac_rule_validation.action(
85 service="nova",
Felipe Monteiro59f538f2018-08-22 23:34:40 -040086 rules=["os_compute_api:os-aggregates:show"])
Felipe Monteiro26b7e092018-07-27 22:15:27 +010087 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 Vilgelm78e7f572019-02-03 10:35:01 -060092 with self.override_role():
Felipe Monteiro26b7e092018-07-27 22:15:27 +010093 self.aggregates_client.show_aggregate(aggregate_id)
94
95When using a waiter, do the wait outside the contextmanager. "Waiting" always
96entails executing a ``GET`` request to the server, until the state of the
97returned resource matches a desired state. These ``GET`` requests enforce
98a different policy than the one expected. This is undesirable because
99Patrole should only test policies in isolation from one another.
100
101Otherwise, the test result will be tainted, because instead of only the
102expected policy getting enforced with the ``os_primary`` role, at least
103two policies get enforced.
104
105Example using waiter::
106
107 @rbac_rule_validation.action(
108 service="nova",
Felipe Monteiro59f538f2018-08-22 23:34:40 -0400109 rules=["os_compute_api:os-admin-password"])
Felipe Monteiro26b7e092018-07-27 22:15:27 +0100110 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 Vilgelm78e7f572019-02-03 10:35:01 -0600116 with self.override_role():
Felipe Monteiro26b7e092018-07-27 22:15:27 +0100117 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
124Below is an example of a method that enforces multiple policies getting
125called inside the contextmanager. The ``_complex_setup_method`` below
126performs the correct API that enforces the expected policy -- in this
127case ``self.resources_client.create_resource`` -- but then proceeds to
128use a waiter.
129
130Incorrect::
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 Monteiro59f538f2018-08-22 23:34:40 -0400143 rules=["example-rule"])
Felipe Monteiro26b7e092018-07-27 22:15:27 +0100144 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 Vilgelm78e7f572019-02-03 10:35:01 -0600148 with self.override_role():
Felipe Monteiro26b7e092018-07-27 22:15:27 +0100149 self._complex_setup_method()
150
151To fix this test, see the "Example using waiter" section above. It is
152recommended to re-implement the logic in a helper method inside a test such
153that only the relevant API is called inside the contextmanager, with
154everything extraneous outside.
155
156.. _rbac-test-cleanup:
157
158Test Cleanup
159------------
160
161Automatic role override in background.
162
163After the test -- no matter whether it ended successfully or in failure --
164the credentials are overridden with the admin role by the Patrole framework,
165*before* ``tearDown`` or ``tearDownClass`` are called. This means that
166resources are always cleaned up using the admin role.