Completed
Pull Request — master (#340)
by Arma
03:46
created

PostMessageAction.run()   B

Complexity

Conditions 5

Size

Total Lines 30

Duplication

Lines 0
Ratio 0 %
Metric Value
dl 0
loc 30
rs 8.0896
cc 5
1
import json
2
import httplib
3
import requests
4
import six
5
from six.moves.urllib.parse import urljoin
6
7
from st2actions.runners.pythonrunner import Action
8
9
__all__ = [
10
    'PostResultAction'
11
]
12
13
14
def _serialize(data):
15
    if isinstance(data, dict):
16
        return '\n'.join(['%s : %s' % (k, v) for k, v in six.iteritems(data)])
17
    return data
18
19
20
def format_possible_failure_result(result):
21
    '''
22
    Error result as generator by the runner container is of the form
23
    {'message': x, 'traceback': traceback}
24
25
    Try and pull out these value upfront. Some other runners that could publish
26
    these properties would get them for free.
27
    '''
28
    output = {}
29
    message = result.get('message', None)
30
    if message:
31
        output['message'] = message
32
    traceback = result.get('traceback', None)
33
    if traceback:
34
        output['traceback'] = traceback
35
    return output
36
37
38
def format_default_result(result):
39
    try:
40
        output = json.loads(result) if isinstance(result, six.string_types) else result
41
        return _serialize(output)
42
    except (ValueError, TypeError):
43
        return result
44
45
46
def format_localrunner_result(result, do_serialize=True):
47
    output = format_possible_failure_result(result)
48
    # Add in various properties if they have values
49
    stdout = result.get('stdout', None)
50
    if stdout:
51
        try:
52
            output['stdout'] = stdout.strip()
53
        except AttributeError:
54
            output['stdout'] = stdout
55
    stderr = result.get('stderr', None)
56
    if stderr:
57
        output['stderr'] = stderr.strip()
58
    return_code = result.get('return_code', 0)
59
    if return_code != 0:
60
        output['return_code'] = return_code
61
    error = result.get('error', None)
62
    if error:
63
        output['error'] = error
64
65
    return _serialize(output) if do_serialize else output
66
67
68
def format_remoterunner_result(result):
69
    output = format_possible_failure_result(result)
70
    output.update({k: format_localrunner_result(v, do_serialize=False)
71
                   for k, v in six.iteritems(result)})
72
    return _serialize(output)
73
74
75
def format_actionchain_result(result):
76
    output = format_possible_failure_result(result)
77
    return '' if not output else _serialize(output)
78
79
80
def format_mistral_result(result):
81
    return format_default_result(result)
82
83
84
def format_pythonrunner_result(result):
85
    output = format_possible_failure_result(result)
86
    # Add in various properties if they have values
87
    result_ = result.get('result', None)
88
    if result_ is not None:
89
        output['result'] = result_
90
    stdout = result.get('stdout', None)
91
    if stdout:
92
        try:
93
            output['stdout'] = stdout.strip()
94
        except AttributeError:
95
            output['stdout'] = stdout
96
    stderr = result.get('stderr', None)
97
    if stderr:
98
        output['stderr'] = stderr.strip()
99
    exit_code = result.get('exit_code', 0)
100
    if exit_code != 0:
101
        output['exit_code'] = exit_code
102
    return _serialize(output)
103
104
105
def format_httprunner_result(result):
106
    return format_default_result(result)
107
108
109
def format_windowsrunner_result(result):
110
    # same format as pythonrunner
111
    return format_pythonrunner_result(result)
112
113
114
FORMATTERS = {
115
    # localrunner
116
    'local-shell-cmd': format_localrunner_result,
117
    'run-local': format_localrunner_result,
118
    'local-shell-script': format_localrunner_result,
119
    'run-local-script': format_localrunner_result,
120
    # remoterunner
121
    'remote-shell-cmd': format_remoterunner_result,
122
    'run-remote': format_remoterunner_result,
123
    'remote-shell-script': format_remoterunner_result,
124
    'run-remote-script': format_remoterunner_result,
125
    # httprunner
126
    'http-request': format_httprunner_result,
127
    'http-runner': format_httprunner_result,
128
    # mistralrunner
129
    'mistral-v1': format_mistral_result,
130
    'mistral-v2': format_mistral_result,
131
    # actionchainrunner
132
    'action-chain': format_actionchain_result,
133
    # pythonrunner
134
    'run-python': format_pythonrunner_result,
135
    'python-script': format_pythonrunner_result,
136
    # windowsrunner
137
    'windows-cmd': format_windowsrunner_result,
138
    'windows-script': format_windowsrunner_result
139
}
140
141
142
class PostResultAction(Action):
143
    def run(self, result, channel, user=None, whisper=False):
144
        endpoint = self.config['endpoint']
145
146
        if not endpoint:
147
            raise ValueError('Missing "endpoint" config option')
148
149
        url = urljoin(endpoint, "/hubot/st2")
150
151
        headers = {}
152
        headers['Content-Type'] = 'application/json'
153
        body = {
154
            'channel': channel,
155
            'message': self._get_message(result)
156
        }
157
158
        if user:
159
            body['user'] = user
160
161
        if whisper is True:
162
            body['whisper'] = whisper
163
164
        data = json.dumps(body)
165
        self.logger.info(data)
166
        response = requests.post(url=url, headers=headers, data=data)
167
168
        if response.status_code == httplib.OK:
169
            self.logger.info('Message successfully posted')
170
        else:
171
            self.logger.exception('Failed to post message: %s' % (response.text))
172
        return True
173
174
    def _get_message(self, data):
175
        envelope = '{message}\nstatus : {status}\nexecution: {execution_id}'.format(**data)
176
        result = self._get_result(data)
177
        if result:
178
            message = '%s\n\nresult :\n--------\n%s' % (envelope, self._get_result(data))
179
        else:
180
            message = envelope
181
        return message
182
183
    def _get_result(self, data):
184
        result = data.get('data', {'result': {}}).get('result', '{}')
185
        try:
186
            result = json.loads(result)
187
        except ValueError:
188
            # if json.loads fails then very return result as-is. Should not happen.
189
            return result
190
        return FORMATTERS.get(data['runner_ref'], format_default_result)(result)
191