413 lines
14 KiB
Python
413 lines
14 KiB
Python
# Copyright 2017 AT&T Intellectual Property. All other rights reserved.
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
# you may not use this file except in compliance with the License.
|
|
# You may obtain a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
# See the License for the specific language governing permissions and
|
|
# limitations under the License.
|
|
from unittest import mock
|
|
|
|
import responses
|
|
|
|
from shipyard_client.api_client.base_client import BaseClient
|
|
from shipyard_client.cli.describe.actions import DescribeAction
|
|
from shipyard_client.cli.describe.actions import DescribeStep
|
|
from shipyard_client.cli.describe.actions import DescribeValidation
|
|
from shipyard_client.cli.describe.actions import DescribeWorkflow
|
|
from tests.unit.cli import stubs
|
|
|
|
|
|
GET_ACTION_API_RESP = """
|
|
{
|
|
"name": "deploy_site",
|
|
"dag_execution_date": "2017-09-24T19:05:49",
|
|
"validations": [],
|
|
"id": "01BTTMFVDKZFRJM80FGD7J1AKN",
|
|
"dag_id": "deploy_site",
|
|
"command_audit": [
|
|
{
|
|
"id": "01BTTMG16R9H3Z4JVQNBMRV1MZ",
|
|
"action_id": "01BTTMFVDKZFRJM80FGD7J1AKN",
|
|
"datetime": "2017-09-24 19:05:49.530223+00:00",
|
|
"user": "shipyard",
|
|
"command": "invoke"
|
|
}
|
|
],
|
|
"user": "shipyard",
|
|
"context_marker": "629f2ea2-c59d-46b9-8641-7367a91a7016",
|
|
"datetime": "2017-09-24 19:05:43.603591+00:00",
|
|
"dag_status": "failed",
|
|
"parameters": {},
|
|
"steps": [
|
|
{
|
|
"id": "action_xcom",
|
|
"url": "/actions/01BTTMFVDKZFRJM80FGD7J1AKN/steps/action_xcom",
|
|
"index": 1,
|
|
"state": "success",
|
|
"notes": [
|
|
{
|
|
"assoc_id": "step/01BTTMFVDKZFRJM80FGD7J1AKN/action_xcom",
|
|
"subject": "action_xcom",
|
|
"sub_type": "step metadata",
|
|
"note_val": "This is a note for the action_xcom",
|
|
"verbosity": 1,
|
|
"note_id": "ABCDEFGHIJKLMNOPQRSTUVWXY0",
|
|
"note_timestamp": "2018-10-08 14:23:53.346534",
|
|
"resolved_url_value": null
|
|
},
|
|
{
|
|
"assoc_id": "step/01BTTMFVDKZFRJM80FGD7J1AKN/action_xcom",
|
|
"subject": "action_xcom",
|
|
"sub_type": "step metadata",
|
|
"note_val": "action_xcom really worked",
|
|
"verbosity": 1,
|
|
"note_id": "ABCDEFGHIJKLMNOPQRSTUVWXY1",
|
|
"note_timestamp": "2018-10-08 14:23:53.346534",
|
|
"resolved_url_value": null
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"id": "part2",
|
|
"url": "/actions/01BTTMFVDKZFRJM80FGD7J1AKN/steps/part2",
|
|
"index": 2,
|
|
"state": "success",
|
|
"notes": []
|
|
},
|
|
{
|
|
"id": "part3",
|
|
"url": "/actions/01BTTMFVDKZFRJM80FGD7J1AKN/steps/part3",
|
|
"index": 3,
|
|
"state": "success",
|
|
"notes": [
|
|
{
|
|
"assoc_id": "step/01BTTMFVDKZFRJM80FGD7J1AKN/part3",
|
|
"subject": "part3",
|
|
"sub_type": "step metadata",
|
|
"note_val": "This is a note for the part3",
|
|
"verbosity": 1,
|
|
"note_id": "ABCDEFGHIJKLMNOPQRSTUVWXY2",
|
|
"note_timestamp": "2018-10-08 14:23:53.346534",
|
|
"resolved_url_value": null
|
|
}
|
|
]
|
|
}
|
|
],
|
|
"action_lifecycle": "Failed",
|
|
"notes": [
|
|
{
|
|
"assoc_id": "action/01BTTMFVDKZFRJM80FGD7J1AKN",
|
|
"subject": "01BTTMFVDKZFRJM80FGD7J1AKN",
|
|
"sub_type": "action metadata",
|
|
"note_val": "This is a note for some action",
|
|
"verbosity": 1,
|
|
"note_id": "ABCDEFGHIJKLMNOPQRSTUVWXYA",
|
|
"note_timestamp": "2018-10-08 14:23:53.346534",
|
|
"resolved_url_value": "Your lucky numbers are 1, 3, 5, and Q"
|
|
}
|
|
]
|
|
}
|
|
"""
|
|
|
|
|
|
@responses.activate
|
|
@mock.patch.object(BaseClient, 'get_endpoint', lambda x: 'http://shiptest')
|
|
@mock.patch.object(BaseClient, 'get_token', lambda x: 'abc')
|
|
def test_describe_action(*args):
|
|
responses.add(responses.GET,
|
|
'http://shiptest/actions/01BTTMFVDKZFRJM80FGD7J1AKN',
|
|
body=GET_ACTION_API_RESP,
|
|
status=200)
|
|
|
|
response = DescribeAction(
|
|
stubs.StubCliContext(),
|
|
'01BTTMFVDKZFRJM80FGD7J1AKN'
|
|
).invoke_and_return_resp()
|
|
assert 'action/01BTTMFVDKZFRJM80FGD7J1AKN' in response
|
|
assert 'step/01BTTMFVDKZFRJM80FGD7J1AKN/action_xcom' in response
|
|
assert 'Steps' in response
|
|
assert 'Commands' in response
|
|
assert 'Validations:' in response
|
|
assert 'This is a note for the part3' in response
|
|
assert '>>> Your lucky numbers are 1, 3, 5, and Q'
|
|
|
|
|
|
@responses.activate
|
|
@mock.patch.object(BaseClient, 'get_endpoint', lambda x: 'http://shiptest')
|
|
@mock.patch.object(BaseClient, 'get_token', lambda x: 'abc')
|
|
def test_describe_action_not_found(*args):
|
|
api_resp = stubs.gen_err_resp(message='Not Found',
|
|
sub_error_count=0,
|
|
sub_info_count=0,
|
|
reason='It does not exist',
|
|
code=404)
|
|
responses.add(responses.GET,
|
|
'http://shiptest/actions/01BTTMFVDKZFRJM80FGD7J1AKN',
|
|
body=api_resp,
|
|
status=404)
|
|
|
|
response = DescribeAction(
|
|
stubs.StubCliContext(),
|
|
'01BTTMFVDKZFRJM80FGD7J1AKN'
|
|
).invoke_and_return_resp()
|
|
assert 'Error: Not Found' in response
|
|
assert 'Reason: It does not exist' in response
|
|
|
|
|
|
GET_STEP_API_RESP = """
|
|
{
|
|
"end_date": "2017-09-24 19:05:59.446213",
|
|
"duration": 0.165181,
|
|
"queued_dttm": "2017-09-24 19:05:52.993983",
|
|
"operator": "PythonOperator",
|
|
"try_number": 1,
|
|
"task_id": "preflight",
|
|
"state": "success",
|
|
"execution_date": "2017-09-24 19:05:49",
|
|
"dag_id": "deploy_site",
|
|
"index": 1,
|
|
"start_date": "2017-09-24 19:05:59.281032",
|
|
"notes": [
|
|
{
|
|
"assoc_id": "step/01BTTMFVDKZFRJM80FGD7J1AKN/preflight",
|
|
"subject": "preflight",
|
|
"sub_type": "step metadata",
|
|
"note_val": "This is a note for the preflight",
|
|
"verbosity": 1,
|
|
"note_id": "ABCDEFGHIJKLMNOPQRSTUVWXY3",
|
|
"note_timestamp": "2018-10-08 14:23:53.346534",
|
|
"resolved_url_value": null
|
|
},
|
|
{
|
|
"assoc_id": "step/01BTTMFVDKZFRJM80FGD7J1AKN/preflight",
|
|
"subject": "preflight",
|
|
"sub_type": "step metadata",
|
|
"note_val": "preflight really worked",
|
|
"verbosity": 1,
|
|
"note_id": "ABCDEFGHIJKLMNOPQRSTUVWXY4",
|
|
"note_timestamp": "2018-10-08 14:23:53.346534",
|
|
"resolved_url_value": null
|
|
}
|
|
]
|
|
}
|
|
"""
|
|
|
|
|
|
@responses.activate
|
|
@mock.patch.object(BaseClient, 'get_endpoint', lambda x: 'http://shiptest')
|
|
@mock.patch.object(BaseClient, 'get_token', lambda x: 'abc')
|
|
def test_describe_step(*args):
|
|
responses.add(
|
|
responses.GET,
|
|
'http://shiptest/actions/01BTTMFVDKZFRJM80FGD7J1AKN/steps/preflight',
|
|
body=GET_STEP_API_RESP,
|
|
status=200)
|
|
|
|
response = DescribeStep(stubs.StubCliContext(),
|
|
'01BTTMFVDKZFRJM80FGD7J1AKN',
|
|
'preflight').invoke_and_return_resp()
|
|
assert 'step/01BTTMFVDKZFRJM80FGD7J1AKN/preflight' in response
|
|
assert 'preflight really worked' in response
|
|
assert 'This is a note for the preflight' in response
|
|
|
|
|
|
@responses.activate
|
|
@mock.patch.object(BaseClient, 'get_endpoint', lambda x: 'http://shiptest')
|
|
@mock.patch.object(BaseClient, 'get_token', lambda x: 'abc')
|
|
def test_describe_step_not_found(*args):
|
|
api_resp = stubs.gen_err_resp(message='Not Found',
|
|
sub_error_count=0,
|
|
sub_info_count=0,
|
|
reason='It does not exist',
|
|
code=404)
|
|
responses.add(
|
|
responses.GET,
|
|
'http://shiptest/actions/01BTTMFVDKZFRJM80FGD7J1AKN/steps/preflight',
|
|
body=api_resp,
|
|
status=404)
|
|
|
|
response = DescribeStep(stubs.StubCliContext(),
|
|
'01BTTMFVDKZFRJM80FGD7J1AKN',
|
|
'preflight').invoke_and_return_resp()
|
|
assert 'Error: Not Found' in response
|
|
assert 'Reason: It does not exist' in response
|
|
|
|
|
|
GET_VALIDATION_API_RESP = """
|
|
{
|
|
"validation_name": "validation_1",
|
|
"action_id": "01BTTMFVDKZFRJM80FGD7J1AKN",
|
|
"id": "02AURNEWAAAESKN99EBF8J2BHD",
|
|
"details": "Validations failed for field 'abc'"
|
|
}
|
|
"""
|
|
|
|
|
|
@responses.activate
|
|
@mock.patch.object(BaseClient, 'get_endpoint', lambda x: 'http://shiptest')
|
|
@mock.patch.object(BaseClient, 'get_token', lambda x: 'abc')
|
|
def test_describe_validation(*args):
|
|
responses.add(
|
|
responses.GET,
|
|
'http://shiptest/actions/01BTTMFVDKZFRJM80FGD7J1AKN/'
|
|
'validations/02AURNEWAAAESKN99EBF8J2BHD',
|
|
body=GET_VALIDATION_API_RESP,
|
|
status=200)
|
|
|
|
response = DescribeValidation(
|
|
stubs.StubCliContext(),
|
|
action_id='01BTTMFVDKZFRJM80FGD7J1AKN',
|
|
validation_id='02AURNEWAAAESKN99EBF8J2BHD').invoke_and_return_resp()
|
|
|
|
v_str = "validation/01BTTMFVDKZFRJM80FGD7J1AKN/02AURNEWAAAESKN99EBF8J2BHD"
|
|
assert v_str in response
|
|
assert "Validations failed for field 'abc'" in response
|
|
|
|
|
|
@responses.activate
|
|
@mock.patch.object(BaseClient, 'get_endpoint', lambda x: 'http://shiptest')
|
|
@mock.patch.object(BaseClient, 'get_token', lambda x: 'abc')
|
|
def test_describe_validation_not_found(*args):
|
|
api_resp = stubs.gen_err_resp(message='Not Found',
|
|
sub_error_count=0,
|
|
sub_info_count=0,
|
|
reason='It does not exist',
|
|
code=404)
|
|
responses.add(
|
|
responses.GET,
|
|
'http://shiptest/actions/01BTTMFVDKZFRJM80FGD7J1AKN/'
|
|
'validations/02AURNEWAAAESKN99EBF8J2BHD',
|
|
body=api_resp,
|
|
status=404)
|
|
|
|
response = DescribeValidation(
|
|
stubs.StubCliContext(),
|
|
action_id='01BTTMFVDKZFRJM80FGD7J1AKN',
|
|
validation_id='02AURNEWAAAESKN99EBF8J2BHD').invoke_and_return_resp()
|
|
assert 'Error: Not Found' in response
|
|
assert 'Reason: It does not exist' in response
|
|
|
|
|
|
WF_API_RESP = """
|
|
{
|
|
"execution_date": "2017-10-09 21:19:03",
|
|
"end_date": null,
|
|
"workflow_id": "deploy_site__2017-10-09T21:19:03.000000",
|
|
"start_date": "2017-10-09 21:19:03.361522",
|
|
"external_trigger": true,
|
|
"steps": [
|
|
{
|
|
"end_date": "2017-10-09 21:19:14.916220",
|
|
"task_id": "action_xcom",
|
|
"start_date": "2017-10-09 21:19:14.798053",
|
|
"duration": 0.118167,
|
|
"queued_dttm": "2017-10-09 21:19:08.432582",
|
|
"try_number": 1,
|
|
"state": "success",
|
|
"operator": "PythonOperator",
|
|
"dag_id": "deploy_site",
|
|
"execution_date": "2017-10-09 21:19:03"
|
|
},
|
|
{
|
|
"end_date": "2017-10-09 21:19:25.283785",
|
|
"task_id": "dag_concurrency_check",
|
|
"start_date": "2017-10-09 21:19:25.181492",
|
|
"duration": 0.102293,
|
|
"queued_dttm": "2017-10-09 21:19:19.283132",
|
|
"try_number": 1,
|
|
"state": "success",
|
|
"operator": "ConcurrencyCheckOperator",
|
|
"dag_id": "deploy_site",
|
|
"execution_date": "2017-10-09 21:19:03"
|
|
},
|
|
{
|
|
"end_date": "2017-10-09 21:20:05.394677",
|
|
"task_id": "preflight",
|
|
"start_date": "2017-10-09 21:19:34.994775",
|
|
"duration": 30.399902,
|
|
"queued_dttm": "2017-10-09 21:19:28.449848",
|
|
"try_number": 1,
|
|
"state": "failed",
|
|
"operator": "SubDagOperator",
|
|
"dag_id": "deploy_site",
|
|
"execution_date": "2017-10-09 21:19:03"
|
|
}
|
|
],
|
|
"dag_id": "deploy_site",
|
|
"state": "failed",
|
|
"run_id": "manual__2017-10-09T21:19:03",
|
|
"sub_dags": [
|
|
{
|
|
"execution_date": "2017-10-09 21:19:03",
|
|
"end_date": null,
|
|
"workflow_id": "deploy_site.preflight__2017-10-09T21:19:03.000000",
|
|
"start_date": "2017-10-09 21:19:35.082479",
|
|
"external_trigger": false,
|
|
"dag_id": "deploy_site.preflight",
|
|
"state": "failed",
|
|
"run_id": "backfill_2017-10-09T21:19:03"
|
|
},
|
|
{
|
|
"execution_date": "2017-10-09 21:19:03",
|
|
"end_date": null,
|
|
"workflow_id": "deploy_site.postflight__2017-10-09T21:19:03.000000",
|
|
"start_date": "2017-10-09 21:19:35.082479",
|
|
"external_trigger": false,
|
|
"dag_id": "deploy_site.postflight",
|
|
"state": "failed",
|
|
"run_id": "backfill_2017-10-09T21:19:03"
|
|
}
|
|
]
|
|
}
|
|
"""
|
|
|
|
|
|
@responses.activate
|
|
@mock.patch.object(BaseClient, 'get_endpoint', lambda x: 'http://shiptest')
|
|
@mock.patch.object(BaseClient, 'get_token', lambda x: 'abc')
|
|
def test_describe_workflow(*args):
|
|
responses.add(
|
|
responses.GET,
|
|
'http://shiptest/workflows/deploy_site__2017-10-09T21:19:03.000000',
|
|
body=WF_API_RESP,
|
|
status=200)
|
|
|
|
response = DescribeWorkflow(
|
|
stubs.StubCliContext(),
|
|
'deploy_site__2017-10-09T21:19:03.000000'
|
|
).invoke_and_return_resp()
|
|
assert 'deploy_site__2017-10-09T21:19:03.000000' in response
|
|
assert 'deploy_site.preflight__2017-10-09T21:19:03.000000' in response
|
|
assert 'deploy_site.postflight__2017-10-09T21:19:03.000000' in response
|
|
assert 'dag_concurrency_check' in response
|
|
assert 'Subworkflows:' in response
|
|
|
|
|
|
@responses.activate
|
|
@mock.patch.object(BaseClient, 'get_endpoint', lambda x: 'http://shiptest')
|
|
@mock.patch.object(BaseClient, 'get_token', lambda x: 'abc')
|
|
def test_describe_workflow_not_found(*args):
|
|
api_resp = stubs.gen_err_resp(message='Not Found',
|
|
sub_error_count=0,
|
|
sub_info_count=0,
|
|
reason='It does not exist',
|
|
code=404)
|
|
responses.add(
|
|
responses.GET,
|
|
'http://shiptest/workflows/deploy_site__2017-10-09T21:19:03.000000',
|
|
body=api_resp,
|
|
status=404)
|
|
|
|
response = DescribeWorkflow(
|
|
stubs.StubCliContext(),
|
|
'deploy_site__2017-10-09T21:19:03.000000'
|
|
).invoke_and_return_resp()
|
|
assert 'Error: Not Found' in response
|
|
assert 'Reason: It does not exist' in response
|