Cleanup clients, add documenation, and fix 2 tests
This commit does the following: - Cleanup the service clients to not hard code resource values used in API endpoints - Add documentation to the README - Fix the following tests that were not previously working: - `test_get_action_validation` - `test_invoke_action_control` Change-Id: I8636f3b704871ad98c346b4a19c7f7f41c705e13
This commit is contained in:
parent
9f473f288a
commit
9ede7e5237
45
README.rst
45
README.rst
@ -2,13 +2,58 @@
|
|||||||
Tempest Integration of airship-tempest-plugin
|
Tempest Integration of airship-tempest-plugin
|
||||||
===============================================
|
===============================================
|
||||||
|
|
||||||
|
Purpose:
|
||||||
|
--------
|
||||||
The purpose of this plugin is to provide automated tests
|
The purpose of this plugin is to provide automated tests
|
||||||
for all OpenStack Airship components.
|
for all OpenStack Airship components.
|
||||||
|
|
||||||
DISCALIMER:
|
DISCALIMER:
|
||||||
|
-----------
|
||||||
This initial implementation is just to meet the first use case which is RBAC
|
This initial implementation is just to meet the first use case which is RBAC
|
||||||
testing. For RBAC testing, we only need to hit the API endpoint and check
|
testing. For RBAC testing, we only need to hit the API endpoint and check
|
||||||
role permission to the API being tested. Some of the REST clients will need to be
|
role permission to the API being tested. Some of the REST clients will need to be
|
||||||
rewritten if functional testing is desired. Those that need to be rewritten
|
rewritten if functional testing is desired. Those that need to be rewritten
|
||||||
are documented in each service client code.
|
are documented in each service client code.
|
||||||
|
|
||||||
|
Environment Information:
|
||||||
|
------------------------
|
||||||
|
Testing can be done in a airship-in-a-bottle environment. Please refer to [0] and [1].
|
||||||
|
Tempest and Tempest plugin installation can be done in a Python virtual environment.
|
||||||
|
|
||||||
|
FAQ:
|
||||||
|
----
|
||||||
|
- Where do the REST clients exist?
|
||||||
|
https://github.com/att-comdev/airship-tempest-plugin/tree/master/airship_tempest_plugin/services
|
||||||
|
- Where do the tests exists? [3]
|
||||||
|
https://github.com/att-comdev/airship-tempest-plugin/tree/master/airship_tempest_plugin/tests/api
|
||||||
|
- Example of where/how the REST clients are instantiated.
|
||||||
|
https://github.com/att-comdev/airship-tempest-plugin/blob/master/airship_tempest_plugin/tests/api/shipyard/base.py
|
||||||
|
- Where do we define expected results (requirements)?
|
||||||
|
https://github.com/att-comdev/airship-tempest-plugin/blob/master/airship_tempest_plugin/tests/api/common/rbac_roles.yaml
|
||||||
|
- Where do we add configuration to support another Airship component?
|
||||||
|
https://github.com/att-comdev/airship-tempest-plugin/blob/master/airship_tempest_plugin/config.py
|
||||||
|
- Where do we run the test from?
|
||||||
|
After the plugin is installed, run it from the tempest directory
|
||||||
|
- Example of how to run all the RBAC tests for Shipyard:
|
||||||
|
'tempest run --regex airship_tempest_plugin.tests.api.shipyard.rbac'
|
||||||
|
- What is Patrole?
|
||||||
|
https://github.com/openstack/patrole/blob/master/README.rst
|
||||||
|
- What is a Tempest plugin? [8]
|
||||||
|
https://docs.openstack.org/tempest/latest/plugin.html
|
||||||
|
|
||||||
|
Patrole Supporting Documentation:
|
||||||
|
---------------------------------
|
||||||
|
Patrole documentation for requirements driven approach that is used: https://github.com/openstack/patrole/blob/master/doc/source/framework/requirements_authority.rst
|
||||||
|
Patrole role-overriding: https://github.com/openstack/patrole/blob/master/doc/source/framework/rbac_utils.rst#role-overriding
|
||||||
|
Patrole under-permission exception: https://github.com/openstack/patrole/blob/master/patrole_tempest_plugin/rbac_exceptions.py#L51
|
||||||
|
Patrole over-permission exception: https://github.com/openstack/patrole/blob/master/patrole_tempest_plugin/rbac_exceptions.py#L44
|
||||||
|
|
||||||
|
Future Considerations:
|
||||||
|
---------------------
|
||||||
|
Will the airship-tempest-plugin continue to live here: https://github.com/att-comdev/airship-tempest-plugin or will it be moved under OpenStack?
|
||||||
|
Will there exist a RBAC gate for all Airship projects?
|
||||||
|
|
||||||
|
Referenced Links:
|
||||||
|
-----------------
|
||||||
|
[0] https://github.com/openstack/airship-in-a-bottle
|
||||||
|
[1] https://www.airshipit.org/
|
||||||
|
@ -24,7 +24,7 @@ from tempest.lib.common import rest_client
|
|||||||
|
|
||||||
# NOTE(rb560u): The following will need to be rewritten in the future if
|
# NOTE(rb560u): The following will need to be rewritten in the future if
|
||||||
# functional testing is desired:
|
# functional testing is desired:
|
||||||
# - 'def post_actions`
|
# - 'def create_action`
|
||||||
# This initial implementation is just to meet the first use case which is RBAC
|
# This initial implementation is just to meet the first use case which is RBAC
|
||||||
# testing. For RBAC testing, we only need to hit the API endpoint and check
|
# testing. For RBAC testing, we only need to hit the API endpoint and check
|
||||||
# role permission to that API.
|
# role permission to that API.
|
||||||
@ -40,33 +40,35 @@ class ActionsClient(rest_client.RestClient):
|
|||||||
return rest_client.ResponseBody(resp, body)
|
return rest_client.ResponseBody(resp, body)
|
||||||
|
|
||||||
def create_action(self):
|
def create_action(self):
|
||||||
url = "actions"
|
url = 'actions'
|
||||||
|
# Update post_body if functional testing is desired
|
||||||
post_body = json.dumps({})
|
post_body = json.dumps({})
|
||||||
resp, body = self.post(url, post_body)
|
resp, body = self.post(url, post_body)
|
||||||
self.expected_success(201, resp.status)
|
self.expected_success(201, resp.status)
|
||||||
body = json.loads(body)
|
body = json.loads(body)
|
||||||
return rest_client.ResponseBody(resp, body)
|
return rest_client.ResponseBody(resp, body)
|
||||||
|
|
||||||
def get_action(self):
|
def get_action(self, action_id=None):
|
||||||
resp, body = self.get('actions/1')
|
resp, body = self.get('actions/%s' % action_id)
|
||||||
self.expected_success(200, resp.status)
|
self.expected_success(200, resp.status)
|
||||||
body = json.loads(body)
|
body = json.loads(body)
|
||||||
return rest_client.ResponseBody(resp, body)
|
return rest_client.ResponseBody(resp, body)
|
||||||
|
|
||||||
def get_action_validation(self):
|
def get_action_validation(self, action_id=None, validation_id=None):
|
||||||
resp, body = self.get('actions/1/validationdetails/1')
|
resp, body = \
|
||||||
|
self.get('actions/%s/validations/%s' % (action_id, validation_id))
|
||||||
self.expected_success(200, resp.status)
|
self.expected_success(200, resp.status)
|
||||||
body = json.loads(body)
|
body = json.loads(body)
|
||||||
return rest_client.ResponseBody(resp, body)
|
return rest_client.ResponseBody(resp, body)
|
||||||
|
|
||||||
def get_action_step(self):
|
def get_action_step(self, action_id=None, step_id=None):
|
||||||
resp, body = self.get('actions/1/steps/1')
|
resp, body = self.get('actions/%s/steps/%s' % (action_id, step_id))
|
||||||
self.expected_success(200, resp.status)
|
self.expected_success(200, resp.status)
|
||||||
body = json.loads(body)
|
body = json.loads(body)
|
||||||
return rest_client.ResponseBody(resp, body)
|
return rest_client.ResponseBody(resp, body)
|
||||||
|
|
||||||
def invoke_action_control(self):
|
def invoke_action_control(self, action_id=None, control_verb=None):
|
||||||
url = "actions/1/pause"
|
url = 'actions/%s/control/%s' % (action_id, control_verb)
|
||||||
post_body = json.dumps({})
|
post_body = json.dumps({})
|
||||||
resp, body = self.post(url, post_body)
|
resp, body = self.post(url, post_body)
|
||||||
self.expected_success(202, resp.status)
|
self.expected_success(202, resp.status)
|
||||||
|
@ -32,8 +32,8 @@ class AirflowMonitoringClient(rest_client.RestClient):
|
|||||||
body = json.loads(body)
|
body = json.loads(body)
|
||||||
return rest_client.ResponseBody(resp, body)
|
return rest_client.ResponseBody(resp, body)
|
||||||
|
|
||||||
def get_workflow(self):
|
def get_workflow(self, workflow_id=None):
|
||||||
resp, body = self.get('workflows/1')
|
resp, body = self.get('workflows/%s' % workflow_id)
|
||||||
self.expected_success(200, resp.status)
|
self.expected_success(200, resp.status)
|
||||||
body = json.loads(body)
|
body = json.loads(body)
|
||||||
return rest_client.ResponseBody(resp, body)
|
return rest_client.ResponseBody(resp, body)
|
||||||
|
@ -24,9 +24,7 @@ from tempest.lib.common import rest_client
|
|||||||
|
|
||||||
# NOTE(rb560u): The following will need to be rewritten in the future if
|
# NOTE(rb560u): The following will need to be rewritten in the future if
|
||||||
# functional testing is desired:
|
# functional testing is desired:
|
||||||
# - 'def post_configdocs`
|
# - 'def create_configdocs`
|
||||||
# - `def get_configdocs_within_collection`
|
|
||||||
# - 'def post_commitconfigdocs'
|
|
||||||
# This initial implementation is just to meet the first use case which is RBAC
|
# This initial implementation is just to meet the first use case which is RBAC
|
||||||
# testing. For RBAC testing, we only need to hit the API endpoint and check
|
# testing. For RBAC testing, we only need to hit the API endpoint and check
|
||||||
# role permission to that API.
|
# role permission to that API.
|
||||||
@ -41,16 +39,17 @@ class DocumentStagingClient(rest_client.RestClient):
|
|||||||
body = json.loads(body)
|
body = json.loads(body)
|
||||||
return rest_client.ResponseBody(resp, body)
|
return rest_client.ResponseBody(resp, body)
|
||||||
|
|
||||||
def create_configdocs(self):
|
def create_configdocs(self, collection_id=None):
|
||||||
url = "configdocs/1"
|
url = "configdocs/%s" % collection_id
|
||||||
|
# Update post_body if functional testing is desired
|
||||||
post_body = json.dumps({})
|
post_body = json.dumps({})
|
||||||
resp, body = self.post(url, post_body)
|
resp, body = self.post(url, post_body)
|
||||||
self.expected_success(201, resp.status)
|
self.expected_success(201, resp.status)
|
||||||
body = json.loads(body)
|
body = json.loads(body)
|
||||||
return rest_client.ResponseBody(resp, body)
|
return rest_client.ResponseBody(resp, body)
|
||||||
|
|
||||||
def get_configdocs(self):
|
def get_configdocs(self, collection_id=None):
|
||||||
resp, body = self.get('configdocs/1')
|
resp, body = self.get('configdocs/%s' % collection_id)
|
||||||
self.expected_success(200, resp.status)
|
self.expected_success(200, resp.status)
|
||||||
body = json.loads(body)
|
body = json.loads(body)
|
||||||
return rest_client.ResponseBody(resp, body)
|
return rest_client.ResponseBody(resp, body)
|
||||||
@ -61,8 +60,8 @@ class DocumentStagingClient(rest_client.RestClient):
|
|||||||
body = json.loads(body)
|
body = json.loads(body)
|
||||||
return rest_client.ResponseBody(resp, body)
|
return rest_client.ResponseBody(resp, body)
|
||||||
|
|
||||||
def commit_configdocs(self):
|
def commit_configdocs(self, force=False, dryrun=False):
|
||||||
post_body = json.dumps({})
|
post_body = json.dumps({"force": force, "dryrun": dryrun})
|
||||||
resp, body = self.post("commitconfigdocs", post_body)
|
resp, body = self.post("commitconfigdocs", post_body)
|
||||||
self.expected_success(200, resp.status)
|
self.expected_success(200, resp.status)
|
||||||
body = json.loads(body)
|
body = json.loads(body)
|
||||||
|
@ -26,8 +26,9 @@ from tempest.lib.common import rest_client
|
|||||||
class LogRetrievalClient(rest_client.RestClient):
|
class LogRetrievalClient(rest_client.RestClient):
|
||||||
api_version = "v1.0"
|
api_version = "v1.0"
|
||||||
|
|
||||||
def get_action_step_logs(self):
|
def get_action_step_logs(self, action_id=None, step_id=None):
|
||||||
resp, body = self.get('actions/1/steps/1/logs')
|
resp, body = \
|
||||||
|
self.get('actions/%s/steps/%s/logs' % (action_id, step_id))
|
||||||
self.expected_success(200, resp.status)
|
self.expected_success(200, resp.status)
|
||||||
body = json.loads(body)
|
body = json.loads(body)
|
||||||
return rest_client.ResponseBody(resp, body)
|
return rest_client.ResponseBody(resp, body)
|
||||||
|
@ -43,7 +43,6 @@ shipyard:
|
|||||||
workflow_orchestrator:commit_configdocs:
|
workflow_orchestrator:commit_configdocs:
|
||||||
- admin
|
- admin
|
||||||
- admin_ucp
|
- admin_ucp
|
||||||
- admin_ucp_viewer
|
|
||||||
workflow_orchestrator:list_workflows:
|
workflow_orchestrator:list_workflows:
|
||||||
- admin
|
- admin
|
||||||
- admin_ucp
|
- admin_ucp
|
||||||
|
@ -41,9 +41,10 @@ class ActionsRbacTest(rbac_base.BaseShipyardRbacTest):
|
|||||||
# As this is a RBAC test, we only care about whether the role has
|
# As this is a RBAC test, we only care about whether the role has
|
||||||
# permission or not. Role permission is checked prior to validating
|
# permission or not. Role permission is checked prior to validating
|
||||||
# the post body, therefore we will ignore a BadRequest exception
|
# the post body, therefore we will ignore a BadRequest exception
|
||||||
|
# and NotFound exception
|
||||||
try:
|
try:
|
||||||
self.shipyard_actions_client.create_action()
|
self.shipyard_actions_client.create_action()
|
||||||
except exceptions.BadRequest:
|
except (exceptions.BadRequest, exceptions.NotFound):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@rbac_rule_validation.action(
|
@rbac_rule_validation.action(
|
||||||
@ -60,15 +61,19 @@ class ActionsRbacTest(rbac_base.BaseShipyardRbacTest):
|
|||||||
except exceptions.NotFound:
|
except exceptions.NotFound:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
''' NEEDS REWORK AS SHIPYARD NOT DOING POLICY ENFORCEMENT FIRST
|
|
||||||
@rbac_rule_validation.action(
|
@rbac_rule_validation.action(
|
||||||
service="shipyard",
|
service="shipyard",
|
||||||
rules=["workflow_orchestrator:get_action_validation"])
|
rules=["workflow_orchestrator:get_action_validation"])
|
||||||
@decorators.idempotent_id('a5156dcd-2674-4295-aa6a-d8db1bd4cf4b')
|
@decorators.idempotent_id('a5156dcd-2674-4295-aa6a-d8db1bd4cf4b')
|
||||||
def test_get_action_validation(self):
|
def test_get_action_validation(self):
|
||||||
with self.rbac_utils.override_role(self):
|
with self.rbac_utils.override_role(self):
|
||||||
self.shipyard_actions_client.get_action_validation()
|
# As this is a RBAC test, we only care about whether the role has
|
||||||
'''
|
# permission or not. Role permission is checked prior to validating
|
||||||
|
# the post body, therefore we will ignore a NotFound exception
|
||||||
|
try:
|
||||||
|
self.shipyard_actions_client.get_action_validation()
|
||||||
|
except exceptions.NotFound:
|
||||||
|
pass
|
||||||
|
|
||||||
@rbac_rule_validation.action(
|
@rbac_rule_validation.action(
|
||||||
service="shipyard",
|
service="shipyard",
|
||||||
@ -84,12 +89,16 @@ class ActionsRbacTest(rbac_base.BaseShipyardRbacTest):
|
|||||||
except exceptions.NotFound:
|
except exceptions.NotFound:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
''' NEEDS REWORK AS SHIPYARD NOT DOING POLICY ENFORCEMENT FIRST
|
|
||||||
@rbac_rule_validation.action(
|
@rbac_rule_validation.action(
|
||||||
service="shipyard",
|
service="shipyard",
|
||||||
rules=["workflow_orchestrator:invoke_action_control"])
|
rules=["workflow_orchestrator:invoke_action_control"])
|
||||||
@decorators.idempotent_id('4f6b6564-ff1d-463a-aee8-ed2d51e2a286')
|
@decorators.idempotent_id('4f6b6564-ff1d-463a-aee8-ed2d51e2a286')
|
||||||
def test_invoke_action_control(self):
|
def test_invoke_action_control(self):
|
||||||
with self.rbac_utils.override_role(self):
|
with self.rbac_utils.override_role(self):
|
||||||
self.shipyard_actions_client.invoke_action_control()
|
# As this is a RBAC test, we only care about whether the role has
|
||||||
'''
|
# permission or not. Role permission is checked prior to validating
|
||||||
|
# the post body, therefore we will ignore a NotFound exception
|
||||||
|
try:
|
||||||
|
self.shipyard_actions_client.invoke_action_control()
|
||||||
|
except exceptions.NotFound:
|
||||||
|
pass
|
||||||
|
Loading…
Reference in New Issue
Block a user