GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.

Issues (503)

st2actions/st2actions/container/base.py (1 issue)

Labels
Severity
1
# Licensed to the StackStorm, Inc ('StackStorm') under one or more
2
# contributor license agreements.  See the NOTICE file distributed with
3
# this work for additional information regarding copyright ownership.
4
# The ASF licenses this file to You under the Apache License, Version 2.0
5
# (the "License"); you may not use this file except in compliance with
6
# the License.  You may obtain a copy of the License at
7
#
8
#     http://www.apache.org/licenses/LICENSE-2.0
9
#
10
# Unless required by applicable law or agreed to in writing, software
11
# distributed under the License is distributed on an "AS IS" BASIS,
12
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
# See the License for the specific language governing permissions and
14
# limitations under the License.
15
16
import json
17
import sys
18
import traceback
19
20
from oslo_config import cfg
21
22
from st2common import log as logging
23
from st2common.util import date as date_utils
24
from st2common.constants import action as action_constants
25
from st2common.content import utils as content_utils
26
from st2common.exceptions import actionrunner
27
from st2common.exceptions.param import ParamException
28
from st2common.models.db.executionstate import ActionExecutionStateDB
29
from st2common.models.system.action import ResolvedActionParameters
30
from st2common.persistence.execution import ActionExecution
31
from st2common.persistence.executionstate import ActionExecutionState
32
from st2common.services import access, executions
33
from st2common.util.action_db import (get_action_by_ref, get_runnertype_by_name)
34
from st2common.util.action_db import (update_liveaction_status, get_liveaction_by_id)
35
from st2common.util import param as param_utils
36
from st2common.util.config_loader import ContentPackConfigLoader
37
38
from st2common.runners.base import get_runner
39
from st2common.runners.base import AsyncActionRunner
40
41
LOG = logging.getLogger(__name__)
42
43
__all__ = [
44
    'RunnerContainer',
45
    'get_runner_container'
46
]
47
48
49
class RunnerContainer(object):
50
51
    def dispatch(self, liveaction_db):
52
        action_db = get_action_by_ref(liveaction_db.action)
53
        if not action_db:
54
            raise Exception('Action %s not found in DB.' % (liveaction_db.action))
55
56
        liveaction_db.context['pack'] = action_db.pack
57
58
        runnertype_db = get_runnertype_by_name(action_db.runner_type['name'])
59
60
        extra = {'liveaction_db': liveaction_db, 'runnertype_db': runnertype_db}
61
        LOG.info('Dispatching Action to a runner', extra=extra)
62
63
        # Get runner instance.
64
        runner = self._get_runner(runnertype_db, action_db, liveaction_db)
65
66
        LOG.debug('Runner instance for RunnerType "%s" is: %s', runnertype_db.name, runner)
67
68
        # Process the request.
69
        funcs = {
70
            action_constants.LIVEACTION_STATUS_REQUESTED: self._do_run,
71
            action_constants.LIVEACTION_STATUS_SCHEDULED: self._do_run,
72
            action_constants.LIVEACTION_STATUS_RUNNING: self._do_run,
73
            action_constants.LIVEACTION_STATUS_CANCELING: self._do_cancel,
74
            action_constants.LIVEACTION_STATUS_PAUSING: self._do_pause,
75
            action_constants.LIVEACTION_STATUS_RESUMING: self._do_resume
76
        }
77
78
        if liveaction_db.status not in funcs:
79
            raise actionrunner.ActionRunnerDispatchError(
80
                'Action runner is unable to dispatch the liveaction because it is '
81
                'in an unsupported status of "%s".' % liveaction_db.status
82
            )
83
84
        liveaction_db = funcs[liveaction_db.status](
85
            runner=runner,
86
            runnertype_db=runnertype_db,
87
            action_db=action_db,
88
            liveaction_db=liveaction_db
89
        )
90
91
        return liveaction_db.result
92
93
    def _do_run(self, runner, runnertype_db, action_db, liveaction_db):
94
        # Create a temporary auth token which will be available
95
        # for the duration of the action execution.
96
        runner.auth_token = self._create_auth_token(context=runner.context, action_db=action_db,
97
                                                    liveaction_db=liveaction_db)
98
99
        try:
100
            # Finalized parameters are resolved and then rendered. This process could
101
            # fail. Handle the exception and report the error correctly.
102
            try:
103
                runner_params, action_params = param_utils.render_final_params(
104
                    runnertype_db.runner_parameters, action_db.parameters, liveaction_db.parameters,
105
                    liveaction_db.context)
106
                runner.runner_parameters = runner_params
107
            except ParamException as e:
108
                raise actionrunner.ActionRunnerException(str(e))
109
110
            LOG.debug('Performing pre-run for runner: %s', runner.runner_id)
111
            runner.pre_run()
112
113
            # Mask secret parameters in the log context
114
            resolved_action_params = ResolvedActionParameters(action_db=action_db,
115
                                                              runner_type_db=runnertype_db,
116
                                                              runner_parameters=runner_params,
117
                                                              action_parameters=action_params)
118
            extra = {'runner': runner, 'parameters': resolved_action_params}
119
            LOG.debug('Performing run for runner: %s' % (runner.runner_id), extra=extra)
0 ignored issues
show
The variable extra was used before it was assigned.
Loading history...
120
            (status, result, context) = runner.run(action_params)
121
122
            try:
123
                result = json.loads(result)
124
            except:
125
                pass
126
127
            action_completed = status in action_constants.LIVEACTION_COMPLETED_STATES
128
            if isinstance(runner, AsyncActionRunner) and not action_completed:
129
                self._setup_async_query(liveaction_db.id, runnertype_db, context)
130
        except:
131
            LOG.exception('Failed to run action.')
132
            _, ex, tb = sys.exc_info()
133
            # mark execution as failed.
134
            status = action_constants.LIVEACTION_STATUS_FAILED
135
            # include the error message and traceback to try and provide some hints.
136
            result = {'error': str(ex), 'traceback': ''.join(traceback.format_tb(tb, 20))}
137
            context = None
138
        finally:
139
            # Log action completion
140
            extra = {'result': result, 'status': status}
141
            LOG.debug('Action "%s" completed.' % (action_db.name), extra=extra)
142
143
            # Update the final status of liveaction and corresponding action execution.
144
            liveaction_db = self._update_status(liveaction_db.id, status, result, context)
145
146
            # Always clean-up the auth_token
147
            # This method should be called in the finally block to ensure post_run is not impacted.
148
            self._clean_up_auth_token(runner=runner, status=status)
149
150
        LOG.debug('Performing post_run for runner: %s', runner.runner_id)
151
        runner.post_run(status=status, result=result)
152
153
        LOG.debug('Runner do_run result', extra={'result': liveaction_db.result})
154
        LOG.audit('Liveaction completed', extra={'liveaction_db': liveaction_db})
155
156
        return liveaction_db
157
158
    def _do_cancel(self, runner, runnertype_db, action_db, liveaction_db):
159
        try:
160
            extra = {'runner': runner}
161
            LOG.debug('Performing cancel for runner: %s', (runner.runner_id), extra=extra)
162
            (status, result, context) = runner.cancel()
163
164
            # Update the final status of liveaction and corresponding action execution.
165
            # The status is updated here because we want to keep the workflow running
166
            # as is if the cancel operation failed.
167
            liveaction_db = self._update_status(liveaction_db.id, status, result, context)
168
        except:
169
            _, ex, tb = sys.exc_info()
170
            # include the error message and traceback to try and provide some hints.
171
            result = {'error': str(ex), 'traceback': ''.join(traceback.format_tb(tb, 20))}
172
            LOG.exception('Failed to cancel action %s.' % (liveaction_db.id), extra=result)
173
        finally:
174
            # Always clean-up the auth_token
175
            # This method should be called in the finally block to ensure post_run is not impacted.
176
            self._clean_up_auth_token(runner=runner, status=liveaction_db.status)
177
178
        LOG.debug('Performing post_run for runner: %s', runner.runner_id)
179
        result = {'error': 'Execution canceled by user.'}
180
        runner.post_run(status=liveaction_db.status, result=result)
181
182
        return liveaction_db
183
184
    def _do_pause(self, runner, runnertype_db, action_db, liveaction_db):
185
        try:
186
            extra = {'runner': runner}
187
            LOG.debug('Performing pause for runner: %s', (runner.runner_id), extra=extra)
188
            (status, result, context) = runner.pause()
189
        except:
190
            _, ex, tb = sys.exc_info()
191
            # include the error message and traceback to try and provide some hints.
192
            status = action_constants.LIVEACTION_STATUS_FAILED
193
            result = {'error': str(ex), 'traceback': ''.join(traceback.format_tb(tb, 20))}
194
            context = liveaction_db.context
195
            LOG.exception('Failed to pause action %s.' % (liveaction_db.id), extra=result)
196
        finally:
197
            # Update the final status of liveaction and corresponding action execution.
198
            liveaction_db = self._update_status(liveaction_db.id, status, result, context)
199
200
            # Always clean-up the auth_token
201
            self._clean_up_auth_token(runner=runner, status=liveaction_db.status)
202
203
        return liveaction_db
204
205
    def _do_resume(self, runner, runnertype_db, action_db, liveaction_db):
206
        try:
207
            extra = {'runner': runner}
208
            LOG.debug('Performing resume for runner: %s', (runner.runner_id), extra=extra)
209
210
            (status, result, context) = runner.resume()
211
212
            try:
213
                result = json.loads(result)
214
            except:
215
                pass
216
217
            action_completed = status in action_constants.LIVEACTION_COMPLETED_STATES
218
219
            if isinstance(runner, AsyncActionRunner) and not action_completed:
220
                self._setup_async_query(liveaction_db.id, runnertype_db, context)
221
        except:
222
            _, ex, tb = sys.exc_info()
223
            # include the error message and traceback to try and provide some hints.
224
            status = action_constants.LIVEACTION_STATUS_FAILED
225
            result = {'error': str(ex), 'traceback': ''.join(traceback.format_tb(tb, 20))}
226
            context = liveaction_db.context
227
            LOG.exception('Failed to resume action %s.' % (liveaction_db.id), extra=result)
228
        finally:
229
            # Update the final status of liveaction and corresponding action execution.
230
            liveaction_db = self._update_status(liveaction_db.id, status, result, context)
231
232
            # Always clean-up the auth_token
233
            # This method should be called in the finally block to ensure post_run is not impacted.
234
            self._clean_up_auth_token(runner=runner, status=liveaction_db.status)
235
236
        LOG.debug('Performing post_run for runner: %s', runner.runner_id)
237
        runner.post_run(status=status, result=result)
238
239
        LOG.debug('Runner do_run result', extra={'result': liveaction_db.result})
240
        LOG.audit('Liveaction completed', extra={'liveaction_db': liveaction_db})
241
242
        return liveaction_db
243
244
    def _clean_up_auth_token(self, runner, status):
245
        """
246
        Clean up the temporary auth token for the current action.
247
248
        Note: This method should never throw since it's called inside finally block which assumes
249
        it doesn't throw.
250
        """
251
        # Deletion of the runner generated auth token is delayed until the token expires.
252
        # Async actions such as Mistral workflows uses the auth token to launch other
253
        # actions in the workflow. If the auth token is deleted here, then the actions
254
        # in the workflow will fail with unauthorized exception.
255
        is_async_runner = isinstance(runner, AsyncActionRunner)
256
        action_completed = status in action_constants.LIVEACTION_COMPLETED_STATES
257
258
        if not is_async_runner or (is_async_runner and action_completed):
259
            try:
260
                self._delete_auth_token(runner.auth_token)
261
            except:
262
                LOG.exception('Unable to clean-up auth_token.')
263
264
            return True
265
266
        return False
267
268
    def _update_live_action_db(self, liveaction_id, status, result, context):
269
        """
270
        Update LiveActionDB object for the provided liveaction id.
271
        """
272
        liveaction_db = get_liveaction_by_id(liveaction_id)
273
274
        state_changed = (liveaction_db.status != status)
275
276
        if status in action_constants.LIVEACTION_COMPLETED_STATES:
277
            end_timestamp = date_utils.get_datetime_utc_now()
278
        else:
279
            end_timestamp = None
280
281
        liveaction_db = update_liveaction_status(status=status,
282
                                                 result=result,
283
                                                 context=context,
284
                                                 end_timestamp=end_timestamp,
285
                                                 liveaction_db=liveaction_db)
286
        return (liveaction_db, state_changed)
287
288
    def _update_status(self, liveaction_id, status, result, context):
289
        try:
290
            LOG.debug('Setting status: %s for liveaction: %s', status, liveaction_id)
291
            liveaction_db, state_changed = self._update_live_action_db(
292
                liveaction_id, status, result, context)
293
        except Exception as e:
294
            LOG.exception(
295
                'Cannot update liveaction '
296
                '(id: %s, status: %s, result: %s).' % (
297
                    liveaction_id, status, result)
298
            )
299
            raise e
300
301
        try:
302
            executions.update_execution(liveaction_db, publish=state_changed)
303
            extra = {'liveaction_db': liveaction_db}
304
            LOG.debug('Updated liveaction after run', extra=extra)
305
        except Exception as e:
306
            LOG.exception(
307
                'Cannot update action execution for liveaction '
308
                '(id: %s, status: %s, result: %s).' % (
309
                    liveaction_id, status, result)
310
            )
311
            raise e
312
313
        return liveaction_db
314
315
    def _get_entry_point_abs_path(self, pack, entry_point):
316
        return content_utils.get_entry_point_abs_path(pack=pack, entry_point=entry_point)
317
318
    def _get_action_libs_abs_path(self, pack, entry_point):
319
        return content_utils.get_action_libs_abs_path(pack=pack, entry_point=entry_point)
320
321
    def _get_rerun_reference(self, context):
322
        execution_id = context.get('re-run', {}).get('ref')
323
        return ActionExecution.get_by_id(execution_id) if execution_id else None
324
325
    def _get_runner(self, runnertype_db, action_db, liveaction_db):
326
        resolved_entry_point = self._get_entry_point_abs_path(action_db.pack,
327
                                                              action_db.entry_point)
328
        context = getattr(liveaction_db, 'context', dict())
329
        user = context.get('user', cfg.CONF.system_user.user)
330
331
        # Note: Right now configs are only supported by the Python runner actions
332
        if runnertype_db.runner_module == 'python_runner':
333
            LOG.debug('Loading config for pack')
334
335
            config_loader = ContentPackConfigLoader(pack_name=action_db.pack, user=user)
336
            config = config_loader.get_config()
337
        else:
338
            config = None
339
340
        runner = get_runner(module_name=runnertype_db.runner_module, config=config)
341
342
        # TODO: Pass those arguments to the constructor instead of late
343
        # assignment, late assignment is awful
344
        runner.runner_type_db = runnertype_db
345
        runner.action = action_db
346
        runner.action_name = action_db.name
347
        runner.liveaction = liveaction_db
348
        runner.liveaction_id = str(liveaction_db.id)
349
        runner.execution = ActionExecution.get(liveaction__id=runner.liveaction_id)
350
        runner.execution_id = str(runner.execution.id)
351
        runner.entry_point = resolved_entry_point
352
        runner.context = context
353
        runner.callback = getattr(liveaction_db, 'callback', dict())
354
        runner.libs_dir_path = self._get_action_libs_abs_path(action_db.pack,
355
                                                              action_db.entry_point)
356
357
        # For re-run, get the ActionExecutionDB in which the re-run is based on.
358
        rerun_ref_id = runner.context.get('re-run', {}).get('ref')
359
        runner.rerun_ex_ref = ActionExecution.get(id=rerun_ref_id) if rerun_ref_id else None
360
361
        return runner
362
363
    def _create_auth_token(self, context, action_db, liveaction_db):
364
        if not context:
365
            return None
366
367
        user = context.get('user', None)
368
        if not user:
369
            return None
370
371
        metadata = {
372
            'service': 'actions_container',
373
            'action_name': action_db.name,
374
            'live_action_id': str(liveaction_db.id)
375
376
        }
377
378
        ttl = cfg.CONF.auth.service_token_ttl
379
        token_db = access.create_token(username=user, ttl=ttl, metadata=metadata, service=True)
380
        return token_db
381
382
    def _delete_auth_token(self, auth_token):
383
        if auth_token:
384
            access.delete_token(auth_token.token)
385
386
    def _setup_async_query(self, liveaction_id, runnertype_db, query_context):
387
        query_module = getattr(runnertype_db, 'query_module', None)
388
        if not query_module:
389
            LOG.error('No query module specified for runner %s.', runnertype_db)
390
            return
391
        try:
392
            self._create_execution_state(liveaction_id, runnertype_db, query_context)
393
        except:
394
            LOG.exception('Unable to create action execution state db model ' +
395
                          'for liveaction_id %s', liveaction_id)
396
397
    def _create_execution_state(self, liveaction_id, runnertype_db, query_context):
398
        state_db = ActionExecutionStateDB(
399
            execution_id=liveaction_id,
400
            query_module=runnertype_db.query_module,
401
            query_context=query_context)
402
        try:
403
            return ActionExecutionState.add_or_update(state_db)
404
        except:
405
            LOG.exception('Unable to create execution state db for liveaction_id %s.'
406
                          % liveaction_id)
407
            return None
408
409
410
def get_runner_container():
411
    return RunnerContainer()
412