def test_format_sec_truncated(self): dt1 = isotime.add_utc_tz(datetime.datetime.utcnow()) dt2 = isotime.parse(isotime.format(dt1, usec=False)) dt3 = datetime.datetime(dt1.year, dt1.month, dt1.day, dt1.hour, dt1.minute, dt1.second) self.assertLess(dt2, dt1) self.assertEqual(dt2, isotime.add_utc_tz(dt3))
def test_token_post_set_ttl(self): timestamp = isotime.add_utc_tz(datetime.datetime.utcnow()) response = self.app.post_json('/tokens', {'ttl': 60}, expect_errors=False) expected_expiry = datetime.datetime.utcnow() + datetime.timedelta(seconds=60) expected_expiry = isotime.add_utc_tz(expected_expiry) self.assertEqual(response.status_int, 201) actual_expiry = isotime.parse(response.json['expiry']) self.assertLess(timestamp, actual_expiry) self.assertLess(actual_expiry, expected_expiry)
def test_token_post_set_ttl(self): timestamp = isotime.add_utc_tz(datetime.datetime.utcnow()) response = self.app.post_json('/tokens', {'ttl': 60}, expect_errors=False) expected_expiry = datetime.datetime.utcnow() + datetime.timedelta( seconds=60) expected_expiry = isotime.add_utc_tz(expected_expiry) self.assertEqual(response.status_int, 201) actual_expiry = isotime.parse(response.json['expiry']) self.assertLess(timestamp, actual_expiry) self.assertLess(actual_expiry, expected_expiry)
def _test_token_post(self): ttl = cfg.CONF.auth.token_ttl timestamp = isotime.add_utc_tz(datetime.datetime.utcnow()) response = self.app.post_json('/tokens', {}, expect_errors=False) expected_expiry = datetime.datetime.utcnow() + datetime.timedelta(seconds=ttl) expected_expiry = isotime.add_utc_tz(expected_expiry) self.assertEqual(response.status_int, 201) self.assertIsNotNone(response.json['token']) self.assertEqual(response.json['user'], USERNAME) actual_expiry = isotime.parse(response.json['expiry']) self.assertLess(timestamp, actual_expiry) self.assertLess(actual_expiry, expected_expiry)
def _test_token_post(self): ttl = cfg.CONF.auth.token_ttl timestamp = isotime.add_utc_tz(datetime.datetime.utcnow()) response = self.app.post_json('/tokens', {}, expect_errors=False) expected_expiry = datetime.datetime.utcnow() + datetime.timedelta( seconds=ttl) expected_expiry = isotime.add_utc_tz(expected_expiry) self.assertEqual(response.status_int, 201) self.assertIsNotNone(response.json['token']) self.assertEqual(response.json['user'], USERNAME) actual_expiry = isotime.parse(response.json['expiry']) self.assertLess(timestamp, actual_expiry) self.assertLess(actual_expiry, expected_expiry)
def test_token_successfully_obtained(self): time_now = isotime.add_utc_tz(datetime.datetime.now()) registrar = InternalTriggerTypesRegistrar() self.assertTrue(registrar._auth_creds is not None) # TTL is at least 10 mins self.assertTrue((registrar._auth_creds.expiry - time_now).seconds > 10 * 60) delete_token(registrar._auth_creds.token)
def create_token(username, ttl=None, metadata=None): """ :param username: Username of the user to create the token for. If the account for this user doesn't exist yet it will be created. :type username: ``str`` :param ttl: Token TTL (in seconds). :type ttl: ``int`` :param metadata: Optional metadata to associate with the token. :type metadata: ``dict`` """ if not ttl or ttl > cfg.CONF.auth.token_ttl: ttl = cfg.CONF.auth.token_ttl if username: try: User.get_by_name(username) except: user = UserDB(name=username) User.add_or_update(user) LOG.audit('Registered new user "%s".' % username) token = uuid.uuid4().hex expiry = datetime.datetime.utcnow() + datetime.timedelta(seconds=ttl) expiry = isotime.add_utc_tz(expiry) token = TokenDB(user=username, token=token, expiry=expiry, metadata=metadata) Token.add_or_update(token) LOG.audit('Access granted to %s with the token set to expire at "%s".' % ('user "%s"' % username if username else "an anonymous user", isotime.format(expiry, offset=False))) return token
def validate_token(token_in_headers, token_in_query_params): """ Validate the provided authentication token. :param token_in_headers: Authentication token provided via headers. :type token_in_headers: ``str`` :param token_in_query_params: Authentication token provided via query params. :type token_in_query_params: ``str`` :return: TokenDB object on success. :rtype: :class:`.TokenDB` """ if not token_in_headers and not token_in_query_params: LOG.audit('Token is not found in header or query parameters.') raise exceptions.TokenNotProvidedError('Token is not provided.') if token_in_headers: LOG.audit('Token provided in headers') if token_in_query_params: LOG.audit('Token provided in query parameters') token_string = token_in_headers or token_in_query_params token = Token.get(token_string) if token.expiry <= isotime.add_utc_tz(datetime.datetime.utcnow()): # TODO: purge expired tokens LOG.audit('Token with id "%s" has expired.' % (token.id)) raise exceptions.TokenExpiredError('Token has expired.') LOG.audit('Token with id "%s" is validated.' % (token.id)) return token
def validate_token(token_in_headers, token_in_query_params): """ Validate the provided authentication token. :param token_in_headers: Authentication token provided via headers. :type token_in_headers: ``str`` :param token_in_query_params: Authentication token provided via query params. :type token_in_query_params: ``str`` :return: TokenDB object on success. :rtype: :class:`.TokenDB` """ if not token_in_headers and not token_in_query_params: LOG.audit('Token is not found in header or query parameyers.') raise exceptions.TokenNotProvidedError('Token is not provided.') if token_in_headers: LOG.audit('Token provided in headers') if token_in_query_params: LOG.audit('Token provided in query parameters') token_string = token_in_headers or token_in_query_params token = Token.get(token_string) if token.expiry <= isotime.add_utc_tz(datetime.datetime.utcnow()): # TODO: purge expired tokens LOG.audit('Token with id "%s" has expired.' % (token.id)) raise exceptions.TokenExpiredError('Token has expired.') LOG.audit('Token with id "%s" is validated.' % (token.id)) return token
def test_liveaction_crud_no_notify(self): created = LiveActionDB() created.action = 'core.local' created.description = '' created.status = 'running' created.parameters = {} saved = LiveActionModelTest._save_liveaction(created) retrieved = LiveAction.get_by_id(saved.id) self.assertEqual(saved.action, retrieved.action, 'Same triggertype was not returned.') self.assertEqual(retrieved.notify, None) # Test update self.assertTrue(retrieved.end_timestamp is None) retrieved.end_timestamp = isotime.add_utc_tz( datetime.datetime.utcnow()) updated = LiveAction.add_or_update(retrieved) self.assertTrue(updated.end_timestamp == retrieved.end_timestamp) # Test delete LiveActionModelTest._delete([retrieved]) try: retrieved = LiveAction.get_by_id(saved.id) except ValueError: retrieved = None self.assertIsNone(retrieved, 'managed to retrieve after failure.')
def test_token_model_null_user(self): dt = isotime.add_utc_tz(datetime.datetime.utcnow()) tk = TokenAPI(user=None, token=uuid.uuid4().hex, expiry=isotime.format(dt)) self.assertRaises(ValueError, Token.add_or_update, TokenAPI.to_model(tk))
def setUpClass(cls): super(TestActionExecutionHistory, cls).setUpClass() cls.dt_base = isotime.add_utc_tz( datetime.datetime(2014, 12, 25, 0, 0, 0)) cls.num_records = 100 cls.refs = {} cls.fake_types = [{ 'trigger': copy.deepcopy(fixture.ARTIFACTS['trigger']), 'trigger_type': copy.deepcopy(fixture.ARTIFACTS['trigger_type']), 'trigger_instance': copy.deepcopy(fixture.ARTIFACTS['trigger_instance']), 'rule': copy.deepcopy(fixture.ARTIFACTS['rule']), 'action': copy.deepcopy(fixture.ARTIFACTS['actions']['chain']), 'runner': copy.deepcopy(fixture.ARTIFACTS['runners']['action-chain']), 'execution': copy.deepcopy(fixture.ARTIFACTS['executions']['workflow']), 'children': [] }, { 'action': copy.deepcopy(fixture.ARTIFACTS['actions']['local']), 'runner': copy.deepcopy(fixture.ARTIFACTS['runners']['run-local']), 'execution': copy.deepcopy(fixture.ARTIFACTS['executions']['task1']) }] def assign_parent(child): candidates = [ v for k, v in cls.refs.iteritems() if v.action['name'] == 'chain' ] if candidates: parent = random.choice(candidates) child['parent'] = str(parent.id) parent.children.append(child['id']) cls.refs[str( parent.id)] = ActionExecutionHistory.add_or_update(parent) for i in range(cls.num_records): obj_id = str(bson.ObjectId()) timestamp = cls.dt_base + datetime.timedelta(seconds=i) fake_type = random.choice(cls.fake_types) data = copy.deepcopy(fake_type) data['id'] = obj_id data['execution']['start_timestamp'] = isotime.format(timestamp, offset=False) if fake_type['action']['name'] == 'local' and random.choice( [True, False]): assign_parent(data) wb_obj = ActionExecutionHistoryAPI(**data) db_obj = ActionExecutionHistoryAPI.to_model(wb_obj) cls.refs[obj_id] = ActionExecutionHistory.add_or_update(db_obj)
def test_token_successfully_obtained(self): time_now = isotime.add_utc_tz(datetime.datetime.now()) registrar = InternalTriggerTypesRegistrar() self.assertTrue(registrar._auth_creds is not None) # TTL is at least 10 mins self.assertTrue( (registrar._auth_creds.expiry - time_now).seconds > 10 * 60) delete_token(registrar._auth_creds.token)
def test_create_token_ttl_capped(self): ttl = cfg.CONF.auth.token_ttl + 10 expected_expiry = datetime.datetime.utcnow() + datetime.timedelta(seconds=ttl) expected_expiry = isotime.add_utc_tz(expected_expiry) token = access.create_token('manas', 10) self.assertTrue(token is not None) self.assertTrue(token.token is not None) self.assertEqual(token.user, 'manas') self.assertLess(isotime.parse(token.expiry), expected_expiry)
def test_create_token_ttl_ok(self): ttl = 10 token = access.create_token(USERNAME, 10) self.assertTrue(token is not None) self.assertTrue(token.token is not None) self.assertEqual(token.user, USERNAME) expected_expiry = datetime.datetime.utcnow() + datetime.timedelta(seconds=ttl) expected_expiry = isotime.add_utc_tz(expected_expiry) self.assertLess(isotime.parse(token.expiry), expected_expiry)
def delete(self, exec_id): """ Stops a single execution. Handles requests: DELETE /actionexecutions/<id> """ execution_api = self._get_one(id=exec_id) if not execution_api: abort(http_client.NOT_FOUND, 'Execution with id %s not found.' % exec_id) return liveaction_id = execution_api.liveaction['id'] if not liveaction_id: abort( http_client.INTERNAL_SERVER_ERROR, 'Execution object missing link to liveaction %s.' % liveaction_id) try: liveaction_db = LiveAction.get_by_id(liveaction_id) except: abort( http_client.INTERNAL_SERVER_ERROR, 'Execution object missing link to liveaction %s.' % liveaction_id) return if liveaction_db.status == LIVEACTION_STATUS_CANCELED: abort(http_client.OK, 'Action is already in "canceled" state.') if liveaction_db.status not in CANCELABLE_STATES: abort( http_client.OK, 'Action cannot be canceled. State = %s.' % liveaction_db.status) return liveaction_db.status = 'canceled' liveaction_db.end_timestamp = isotime.add_utc_tz( datetime.datetime.utcnow()) liveaction_db.result = {'message': 'Action canceled by user.'} try: LiveAction.add_or_update(liveaction_db) except: LOG.exception( 'Failed updating status to canceled for liveaction %s.', liveaction_db.id) abort(http_client.INTERNAL_SERVER_ERROR, 'Failed canceling execution.') return execution_db = execution_service.update_execution(liveaction_db) return ActionExecutionAPI.from_model(execution_db)
def test_create_token_ttl_capped(self): ttl = cfg.CONF.auth.token_ttl + 10 expected_expiry = datetime.datetime.utcnow() + datetime.timedelta( seconds=ttl) expected_expiry = isotime.add_utc_tz(expected_expiry) token = access.create_token('manas', 10) self.assertTrue(token is not None) self.assertTrue(token.token is not None) self.assertEqual(token.user, 'manas') self.assertLess(isotime.parse(token.expiry), expected_expiry)
def _validate_token(self, env): """Validate token""" if 'HTTP_X_AUTH_TOKEN' not in env: LOG.audit('Token is not found in header.') raise exceptions.TokenNotProvidedError('Token is not provided.') token = Token.get(env['HTTP_X_AUTH_TOKEN']) if token.expiry <= isotime.add_utc_tz(datetime.datetime.utcnow()): LOG.audit('Token "%s" has expired.' % env['HTTP_X_AUTH_TOKEN']) raise exceptions.TokenExpiredError('Token has expired.') LOG.audit('Token "%s" is validated.' % env['HTTP_X_AUTH_TOKEN']) return token
def schedule(liveaction): """ Schedule an action to be run. :return: (liveaction, execution) :rtype: tuple """ # Use the user context from the parent action execution. Subtasks in a workflow # action can be invoked by a system user and so we want to use the user context # from the original workflow action. if getattr(liveaction, 'context', None) and 'parent' in liveaction.context: parent = LiveAction.get_by_id(liveaction.context['parent']) liveaction.context['user'] = getattr(parent, 'context', dict()).get('user') # Validate action. action_db = action_utils.get_action_by_ref(liveaction.action) if not action_db: raise ValueError('Action "%s" cannot be found.' % liveaction.action) if not action_db.enabled: raise ValueError('Unable to execute. Action "%s" is disabled.' % liveaction.action) runnertype_db = action_utils.get_runnertype_by_name(action_db.runner_type['name']) if not hasattr(liveaction, 'parameters'): liveaction.parameters = dict() # Validate action parameters. schema = util_schema.get_parameter_schema(action_db) validator = util_schema.get_validator() jsonschema.validate(liveaction.parameters, schema, validator) # validate that no immutable params are being overriden. Although possible to # ignore the override it is safer to inform the user to avoid surprises. immutables = _get_immutable_params(action_db.parameters) immutables.extend(_get_immutable_params(runnertype_db.runner_parameters)) overridden_immutables = [p for p in six.iterkeys(liveaction.parameters) if p in immutables] if len(overridden_immutables) > 0: raise ValueError('Override of immutable parameter(s) %s is unsupported.' % str(overridden_immutables)) # Write to database and send to message queue. liveaction.status = LIVEACTION_STATUS_SCHEDULED liveaction.start_timestamp = isotime.add_utc_tz(datetime.datetime.utcnow()) # Publish creation after both liveaction and actionexecution are created. liveaction = LiveAction.add_or_update(liveaction, publish=False) execution = executions.create_execution_object(liveaction, publish=False) # assume that this is a creation. LiveAction.publish_create(liveaction) ActionExecution.publish_create(execution) LOG.audit('Action execution scheduled. LiveAction=%s. ActionExecution=%s', liveaction, execution) return liveaction, execution
def test_format(self): dt = isotime.add_utc_tz(datetime.datetime(2000, 1, 1, 12)) dt_str_usec_offset = '2000-01-01T12:00:00.000000+00:00' dt_str_usec = '2000-01-01T12:00:00.000000Z' dt_str_offset = '2000-01-01T12:00:00+00:00' dt_str = '2000-01-01T12:00:00Z' dt_unicode = u'2000-01-01T12:00:00Z' self.assertEqual(isotime.format(dt, usec=True, offset=True), dt_str_usec_offset) self.assertEqual(isotime.format(dt, usec=True, offset=False), dt_str_usec) self.assertEqual(isotime.format(dt, usec=False, offset=True), dt_str_offset) self.assertEqual(isotime.format(dt, usec=False, offset=False), dt_str) self.assertEqual(isotime.format(dt_str, usec=False, offset=False), dt_str) self.assertEqual(isotime.format(dt_unicode, usec=False, offset=False), dt_unicode)
def _update_live_action_db(self, liveaction_id, status, result, context): liveaction_db = get_liveaction_by_id(liveaction_id) if status in DONE_STATES: end_timestamp = isotime.add_utc_tz(datetime.datetime.utcnow()) else: end_timestamp = None liveaction_db = update_liveaction_status(status=status, result=result, context=context, end_timestamp=end_timestamp, liveaction_db=liveaction_db) return liveaction_db
def _update_live_action_db(self, liveaction_id, status, result, context): liveaction_db = get_liveaction_by_id(liveaction_id) if status in action_constants.COMPLETED_STATES: end_timestamp = isotime.add_utc_tz(datetime.datetime.utcnow()) else: end_timestamp = None liveaction_db = update_liveaction_status(status=status, result=result, context=context, end_timestamp=end_timestamp, liveaction_db=liveaction_db) return liveaction_db
def test_parse(self): dt = isotime.add_utc_tz(datetime.datetime(2000, 1, 1, 12)) self.assertEqual(isotime.parse('2000-01-01 12:00:00Z'), dt) self.assertEqual(isotime.parse('2000-01-01 12:00:00+00'), dt) self.assertEqual(isotime.parse('2000-01-01 12:00:00+0000'), dt) self.assertEqual(isotime.parse('2000-01-01 12:00:00+00:00'), dt) self.assertEqual(isotime.parse('2000-01-01 12:00:00.000000Z'), dt) self.assertEqual(isotime.parse('2000-01-01 12:00:00.000000+00'), dt) self.assertEqual(isotime.parse('2000-01-01 12:00:00.000000+0000'), dt) self.assertEqual(isotime.parse('2000-01-01 12:00:00.000000+00:00'), dt) self.assertEqual(isotime.parse('2000-01-01T12:00:00Z'), dt) self.assertEqual(isotime.parse('2000-01-01T12:00:00+00:00'), dt) self.assertEqual(isotime.parse('2000-01-01T12:00:00.000000Z'), dt) self.assertEqual(isotime.parse('2000-01-01T12:00:00.000000+00:00'), dt)
def _update_action_execution_db(self, actionexec_id, status, result): actionexec_db = get_actionexec_by_id(actionexec_id) if status in DONE_STATES: end_timestamp = isotime.add_utc_tz(datetime.datetime.utcnow()) else: end_timestamp = None # Push result data and updated status to ActionExecution DB actionexec_db = update_actionexecution_status( status=status, result=result, end_timestamp=end_timestamp, actionexec_db=actionexec_db) return actionexec_db
def test_parse(self): dt = isotime.add_utc_tz(datetime.datetime(2000, 1, 1, 12)) self.assertEqual(isotime.parse('2000-01-01 12:00:00Z'), dt) self.assertEqual(isotime.parse('2000-01-01 12:00:00+00'), dt) self.assertEqual(isotime.parse('2000-01-01 12:00:00+0000'), dt) self.assertEqual(isotime.parse('2000-01-01 12:00:00+00:00'), dt) self.assertEqual(isotime.parse('2000-01-01 12:00:00.000000Z'), dt) self.assertEqual(isotime.parse('2000-01-01 12:00:00.000000+00'), dt) self.assertEqual(isotime.parse('2000-01-01 12:00:00.000000+0000'), dt) self.assertEqual(isotime.parse('2000-01-01 12:00:00.000000+00:00'), dt) self.assertEqual(isotime.parse('2000-01-01T12:00:00Z'), dt) self.assertEqual(isotime.parse('2000-01-01T12:00:00+00:00'), dt) self.assertEqual(isotime.parse('2000-01-01T12:00:00.000000Z'), dt) self.assertEqual(isotime.parse('2000-01-01T12:00:00.000000+00:00'), dt) self.assertEqual(isotime.parse('2000-01-01T12:00:00.000Z'), dt)
def delete(self, exec_id): """ Stops a single execution. Handles requests: DELETE /actionexecutions/<id> """ execution_api = self._get_one(id=exec_id) if not execution_api: abort(http_client.NOT_FOUND, 'Execution with id %s not found.' % exec_id) return liveaction_id = execution_api.liveaction['id'] if not liveaction_id: abort(http_client.INTERNAL_SERVER_ERROR, 'Execution object missing link to liveaction %s.' % liveaction_id) try: liveaction_db = LiveAction.get_by_id(liveaction_id) except: abort(http_client.INTERNAL_SERVER_ERROR, 'Execution object missing link to liveaction %s.' % liveaction_id) return if liveaction_db.status == LIVEACTION_STATUS_CANCELED: abort(http_client.OK, 'Action is already in "canceled" state.') if liveaction_db.status not in CANCELABLE_STATES: abort(http_client.OK, 'Action cannot be canceled. State = %s.' % liveaction_db.status) return liveaction_db.status = 'canceled' liveaction_db.end_timestamp = isotime.add_utc_tz(datetime.datetime.utcnow()) liveaction_db.result = {'message': 'Action canceled by user.'} try: LiveAction.add_or_update(liveaction_db) except: LOG.exception('Failed updating status to canceled for liveaction %s.', liveaction_db.id) abort(http_client.INTERNAL_SERVER_ERROR, 'Failed canceling execution.') return execution_db = execution_service.update_execution(liveaction_db) return ActionExecutionAPI.from_model(execution_db)
def test_datetime_range(self): base = isotime.add_utc_tz(datetime.datetime(2014, 12, 25, 0, 0, 0)) for i in range(60): timestamp = base + datetime.timedelta(seconds=i) obj = FakeModelDB(name=uuid.uuid4().hex, timestamp=timestamp) self.access.add_or_update(obj) dt_range = '2014-12-25T00:00:10Z..2014-12-25T00:00:19Z' objs = self.access.query(timestamp=dt_range) self.assertEqual(len(objs), 10) self.assertLess(objs[0].timestamp, objs[9].timestamp) dt_range = '2014-12-25T00:00:19Z..2014-12-25T00:00:10Z' objs = self.access.query(timestamp=dt_range) self.assertEqual(len(objs), 10) self.assertLess(objs[9].timestamp, objs[0].timestamp)
def test_delayed_executions_recovery_before_timeout(self): # Create a live action that's delayed but has not passed the timeout. liveaction = LiveActionDB(action='wolfpack.action-1', parameters={'actionstr': 'foo'}, start_timestamp=isotime.add_utc_tz(datetime.datetime.utcnow()), status=action_constants.LIVEACTION_STATUS_DELAYED) liveaction = LiveAction.add_or_update(liveaction, publish=False) executions.create_execution_object(liveaction, publish=False) # Run the rescheduling routine. scheduler.recover_delayed_executions() # The live action is expected to stay "delayed". liveaction = LiveAction.get_by_id(str(liveaction.id)) self.assertEqual(liveaction.status, action_constants.LIVEACTION_STATUS_DELAYED)
def test_datetime_range(self): base = isotime.add_utc_tz(datetime.datetime(2014, 12, 25, 0, 0, 0)) for i in range(60): timestamp = base + datetime.timedelta(seconds=i) doc = copy.deepcopy(self.fake_history_subtasks[0]) doc['id'] = str(bson.ObjectId()) doc['start_timestamp'] = isotime.format(timestamp) obj = ActionExecutionAPI(**doc) ActionExecution.add_or_update(ActionExecutionAPI.to_model(obj)) dt_range = '2014-12-25T00:00:10Z..2014-12-25T00:00:19Z' objs = ActionExecution.query(start_timestamp=dt_range) self.assertEqual(len(objs), 10) dt_range = '2014-12-25T00:00:19Z..2014-12-25T00:00:10Z' objs = ActionExecution.query(start_timestamp=dt_range) self.assertEqual(len(objs), 10)
def test_datetime_range(self): base = isotime.add_utc_tz(datetime.datetime(2014, 12, 25, 0, 0, 0)) for i in range(60): timestamp = base + datetime.timedelta(seconds=i) doc = copy.deepcopy(self.fake_history_subtasks[0]) doc['id'] = str(bson.ObjectId()) doc['execution']['start_timestamp'] = isotime.format(timestamp) obj = ActionExecutionHistoryAPI(**doc) ActionExecutionHistory.add_or_update(ActionExecutionHistoryAPI.to_model(obj)) dt_range = '2014-12-25T00:00:10Z..2014-12-25T00:00:19Z' objs = ActionExecutionHistory.query(execution__start_timestamp=dt_range) self.assertEqual(len(objs), 10) dt_range = '2014-12-25T00:00:19Z..2014-12-25T00:00:10Z' objs = ActionExecutionHistory.query(execution__start_timestamp=dt_range) self.assertEqual(len(objs), 10)
def test_sort_multiple(self): count = 60 base = isotime.add_utc_tz(datetime.datetime(2014, 12, 25, 0, 0, 0)) for i in range(count): category = 'type1' if i % 2 else 'type2' timestamp = base + datetime.timedelta(seconds=i) obj = FakeModelDB(name=uuid.uuid4().hex, timestamp=timestamp, category=category) self.access.add_or_update(obj) objs = self.access.query(order_by=['category', 'timestamp']) self.assertEqual(len(objs), count) for i in range(count): category = 'type1' if i < count / 2 else 'type2' self.assertEqual(objs[i].category, category) self.assertLess(objs[0].timestamp, objs[(count / 2) - 1].timestamp) self.assertLess(objs[count / 2].timestamp, objs[(count / 2) - 1].timestamp) self.assertLess(objs[count / 2].timestamp, objs[count - 1].timestamp)
def create_token(username, ttl=None, metadata=None): """ :param username: Username of the user to create the token for. If the account for this user doesn't exist yet it will be created. :type username: ``str`` :param ttl: Token TTL (in seconds). :type ttl: ``int`` :param metadata: Optional metadata to associate with the token. :type metadata: ``dict`` """ if ttl: if ttl > cfg.CONF.auth.token_ttl: msg = 'TTL specified %s is greater than max allowed %s.' % ( ttl, cfg.CONF.auth.token_ttl ) raise TTLTooLargeException(msg) else: ttl = cfg.CONF.auth.token_ttl if username: try: User.get_by_name(username) except: user = UserDB(name=username) User.add_or_update(user) extra = {'username': username, 'user': user} LOG.audit('Registered new user "%s".' % (username), extra=extra) token = uuid.uuid4().hex expiry = datetime.datetime.utcnow() + datetime.timedelta(seconds=ttl) expiry = isotime.add_utc_tz(expiry) token = TokenDB(user=username, token=token, expiry=expiry, metadata=metadata) Token.add_or_update(token) username_string = username if username else 'an anonymous user' token_expire_string = isotime.format(expiry, offset=False) extra = {'username': username, 'token_expiration': token_expire_string} LOG.audit('Access granted to "%s" with the token set to expire at "%s".' % (username_string, token_expire_string), extra=extra) return token
def test_token_model(self): dt = isotime.add_utc_tz(datetime.datetime.utcnow()) tk1 = TokenAPI(user='******', token=uuid.uuid4().hex, expiry=isotime.format(dt, offset=False)) tkdb1 = TokenAPI.to_model(tk1) self.assertIsNotNone(tkdb1) self.assertIsInstance(tkdb1, TokenDB) self.assertEqual(tkdb1.user, tk1.user) self.assertEqual(tkdb1.token, tk1.token) self.assertEqual(tkdb1.expiry, isotime.parse(tk1.expiry)) tkdb2 = Token.add_or_update(tkdb1) self.assertEqual(tkdb1, tkdb2) self.assertIsNotNone(tkdb2.id) tk2 = TokenAPI.from_model(tkdb2) self.assertEqual(tk2.user, tk1.user) self.assertEqual(tk2.token, tk1.token) self.assertEqual(tk2.expiry, tk1.expiry)
def setUpClass(cls): super(TestActionExecutionHistory, cls).setUpClass() cls.dt_base = isotime.add_utc_tz(datetime.datetime(2014, 12, 25, 0, 0, 0)) cls.num_records = 100 cls.refs = {} cls.fake_types = [ { 'trigger': copy.deepcopy(fixture.ARTIFACTS['trigger']), 'trigger_type': copy.deepcopy(fixture.ARTIFACTS['trigger_type']), 'trigger_instance': copy.deepcopy(fixture.ARTIFACTS['trigger_instance']), 'rule': copy.deepcopy(fixture.ARTIFACTS['rule']), 'action': copy.deepcopy(fixture.ARTIFACTS['actions']['chain']), 'runner': copy.deepcopy(fixture.ARTIFACTS['runners']['action-chain']), 'execution': copy.deepcopy(fixture.ARTIFACTS['executions']['workflow']), 'children': [] }, { 'action': copy.deepcopy(fixture.ARTIFACTS['actions']['local']), 'runner': copy.deepcopy(fixture.ARTIFACTS['runners']['run-local']), 'execution': copy.deepcopy(fixture.ARTIFACTS['executions']['task1']) } ] def assign_parent(child): candidates = [v for k, v in cls.refs.iteritems() if v.action['name'] == 'chain'] if candidates: parent = random.choice(candidates) child['parent'] = str(parent.id) parent.children.append(child['id']) cls.refs[str(parent.id)] = ActionExecutionHistory.add_or_update(parent) for i in range(cls.num_records): obj_id = str(bson.ObjectId()) timestamp = cls.dt_base + datetime.timedelta(seconds=i) fake_type = random.choice(cls.fake_types) data = copy.deepcopy(fake_type) data['id'] = obj_id data['execution']['start_timestamp'] = isotime.format(timestamp, offset=False) if fake_type['action']['name'] == 'local' and random.choice([True, False]): assign_parent(data) wb_obj = ActionExecutionHistoryAPI(**data) db_obj = ActionExecutionHistoryAPI.to_model(wb_obj) cls.refs[obj_id] = ActionExecutionHistory.add_or_update(db_obj)
def test_sort_by_start_timestamp(self): base = isotime.add_utc_tz(datetime.datetime(2014, 12, 25, 0, 0, 0)) for i in range(60): timestamp = base + datetime.timedelta(seconds=i) doc = copy.deepcopy(self.fake_history_subtasks[0]) doc['id'] = str(bson.ObjectId()) doc['execution']['start_timestamp'] = isotime.format(timestamp) obj = ActionExecutionHistoryAPI(**doc) ActionExecutionHistory.add_or_update(ActionExecutionHistoryAPI.to_model(obj)) dt_range = '2014-12-25T00:00:10Z..2014-12-25T00:00:19Z' objs = ActionExecutionHistory.query(execution__start_timestamp=dt_range, order_by=['execution__start_timestamp']) self.assertLess(objs[0].execution['start_timestamp'], objs[9].execution['start_timestamp']) dt_range = '2014-12-25T00:00:19Z..2014-12-25T00:00:10Z' objs = ActionExecutionHistory.query(execution__start_timestamp=dt_range, order_by=['-execution__start_timestamp']) self.assertLess(objs[9].execution['start_timestamp'], objs[0].execution['start_timestamp'])
def test_delayed_executions_recovery(self): # Create a live action that's already delayed pass the allowed timeout. dt_now = isotime.add_utc_tz(datetime.datetime.utcnow()) dt_delta = datetime.timedelta(seconds=cfg.CONF.scheduler.delayed_execution_recovery) dt_timeout = dt_now - dt_delta liveaction = LiveActionDB(action='wolfpack.action-1', parameters={'actionstr': 'foo'}, start_timestamp=dt_timeout, status=action_constants.LIVEACTION_STATUS_DELAYED) liveaction = LiveAction.add_or_update(liveaction, publish=False) executions.create_execution_object(liveaction, publish=False) # Run the rescheduling routine. scheduler.recover_delayed_executions() # The live action is expected to complete. liveaction = LiveAction.get_by_id(str(liveaction.id)) self.assertEqual(liveaction.status, action_constants.LIVEACTION_STATUS_SUCCEEDED)
def schedule(execution): # Use the user context from the parent action execution. Subtasks in a workflow # action can be invoked by a system user and so we want to use the user context # from the original workflow action. if getattr(execution, 'context', None) and 'parent' in execution.context: parent = ActionExecution.get_by_id(execution.context['parent']) execution.context['user'] = getattr(parent, 'context', dict()).get('user') # Validate action. action_db = action_utils.get_action_by_ref(execution.action) if not action_db: raise ValueError('Action "%s" cannot be found.' % execution.action) if not action_db.enabled: raise ValueError('Unable to execute. Action "%s" is disabled.' % execution.action) runnertype_db = action_utils.get_runnertype_by_name(action_db.runner_type['name']) if not hasattr(execution, 'parameters'): execution.parameters = dict() # Validate action parameters. schema = util_schema.get_parameter_schema(action_db) validator = util_schema.get_validator() jsonschema.validate(execution.parameters, schema, validator) # validate that no immutable params are being overriden. Although possible to # ignore the override it is safer to inform the user to avoid surprises. immutables = _get_immutable_params(action_db.parameters) immutables.extend(_get_immutable_params(runnertype_db.runner_parameters)) overridden_immutables = [p for p in six.iterkeys(execution.parameters) if p in immutables] if len(overridden_immutables) > 0: raise ValueError('Override of immutable parameter(s) %s is unsupported.' % str(overridden_immutables)) # Write to database and send to message queue. execution.status = ACTIONEXEC_STATUS_SCHEDULED execution.start_timestamp = isotime.add_utc_tz(datetime.datetime.utcnow()) execution = ActionExecution.add_or_update(execution) LOG.audit('Action execution scheduled. ActionExecution=%s.', execution) return execution
def test_sort_by_start_timestamp(self): base = isotime.add_utc_tz(datetime.datetime(2014, 12, 25, 0, 0, 0)) for i in range(60): timestamp = base + datetime.timedelta(seconds=i) doc = copy.deepcopy(self.fake_history_subtasks[0]) doc['id'] = str(bson.ObjectId()) doc['start_timestamp'] = isotime.format(timestamp) obj = ActionExecutionAPI(**doc) ActionExecution.add_or_update(ActionExecutionAPI.to_model(obj)) dt_range = '2014-12-25T00:00:10Z..2014-12-25T00:00:19Z' objs = ActionExecution.query(start_timestamp=dt_range, order_by=['start_timestamp']) self.assertLess(objs[0]['start_timestamp'], objs[9]['start_timestamp']) dt_range = '2014-12-25T00:00:19Z..2014-12-25T00:00:10Z' objs = ActionExecution.query(start_timestamp=dt_range, order_by=['-start_timestamp']) self.assertLess(objs[9]['start_timestamp'], objs[0]['start_timestamp'])
def create_token(username, ttl=None): if not ttl or ttl > cfg.CONF.auth.token_ttl: ttl = cfg.CONF.auth.token_ttl if username: try: User.get_by_name(username) except: user = UserDB(name=username) User.add_or_update(user) LOG.audit('Registered new user "%s".' % username) LOG.audit('Access granted to user "%s".' % username) token = uuid.uuid4().hex expiry = datetime.datetime.utcnow() + datetime.timedelta(seconds=ttl) expiry = isotime.add_utc_tz(expiry) token = TokenDB(user=username, token=token, expiry=expiry) Token.add_or_update(token) LOG.audit('Access granted to %s with the token set to expire at "%s".' % ('user "%s"' % username if username else "an anonymous user", isotime.format(expiry, offset=False))) return token
def test_liveaction_crud_no_notify(self): created = LiveActionDB() created.action = 'core.local' created.description = '' created.status = 'running' created.parameters = {} saved = LiveActionModelTest._save_liveaction(created) retrieved = LiveAction.get_by_id(saved.id) self.assertEqual(saved.action, retrieved.action, 'Same triggertype was not returned.') self.assertEqual(retrieved.notify, None) # Test update self.assertTrue(retrieved.end_timestamp is None) retrieved.end_timestamp = isotime.add_utc_tz(datetime.datetime.utcnow()) updated = LiveAction.add_or_update(retrieved) self.assertTrue(updated.end_timestamp == retrieved.end_timestamp) # Test delete LiveActionModelTest._delete([retrieved]) try: retrieved = LiveAction.get_by_id(saved.id) except ValueError: retrieved = None self.assertIsNone(retrieved, 'managed to retrieve after failure.')
def test_add_utc_tz_info(self): dt = datetime.datetime.utcnow() self.assertIsNone(dt.tzinfo) dt = isotime.add_utc_tz(dt) self.assertIsNotNone(dt.tzinfo) self.assertEqual(dt.tzinfo.tzname(None), 'UTC')
WF2_YAML_FILE_PATH = LOADER.get_fixture_file_path_abs(PACK, 'workflows', WF2_YAML_FILE_NAME) WF2_SPEC = FIXTURES['workflows'][WF2_YAML_FILE_NAME] WF2_YAML = yaml.safe_dump(WF2_SPEC, default_flow_style=False) WF2_NAME = '%s.%s' % (PACK, WF2_YAML_FILE_NAME.replace('.yaml', '')) WF2 = workflows.Workflow(None, {'name': WF2_NAME, 'definition': WF2_YAML}) WF2_EXEC = copy.deepcopy(MISTRAL_EXECUTION) WF2_EXEC['workflow_name'] = WF2_NAME # Action executions requirements ACTION_CONTEXT = {'user': '******'} ACTION_PARAMS = {'friend': 'Rocky'} # Token for auth test cases TOKEN_API = TokenAPI( user=ACTION_CONTEXT['user'], token=uuid.uuid4().hex, expiry=isotime.format(isotime.add_utc_tz(datetime.datetime.utcnow()), offset=False)) TOKEN_DB = TokenAPI.to_model(TOKEN_API) NON_EMPTY_RESULT = 'non-empty' @mock.patch.object(LocalShellRunner, 'run', mock. MagicMock(return_value=(action_constants.LIVEACTION_STATUS_SUCCEEDED, NON_EMPTY_RESULT, None))) @mock.patch.object(CUDPublisher, 'publish_update', mock.MagicMock(return_value=None)) @mock.patch.object(CUDPublisher, 'publish_create', mock.MagicMock(side_effect=MockLiveActionPublisher.publish_create)) @mock.patch.object(LiveActionPublisher, 'publish_state', mock.MagicMock(side_effect=MockLiveActionPublisher.publish_state)) class TestMistralRunner(DbTestCase):
def test_format_tz_aware(self): dt1 = isotime.add_utc_tz(datetime.datetime.utcnow()) dt2 = isotime.parse(isotime.format(dt1, usec=True)) self.assertEqual(dt2, dt1)
resp.json['faultstring']) @staticmethod def _get_actionexecution_id(resp): return resp.json['id'] def _do_get_one(self, actionexecution_id, *args, **kwargs): return self.app.get('/v1/actionexecutions/%s' % actionexecution_id, *args, **kwargs) def _do_post(self, liveaction, *args, **kwargs): return self.app.post_json('/v1/actionexecutions', liveaction, *args, **kwargs) NOW = isotime.add_utc_tz(datetime.datetime.utcnow()) EXPIRY = NOW + datetime.timedelta(seconds=300) SYS_TOKEN = TokenDB(id=bson.ObjectId(), user='******', token=uuid.uuid4().hex, expiry=EXPIRY) USR_TOKEN = TokenDB(id=bson.ObjectId(), user='******', token=uuid.uuid4().hex, expiry=EXPIRY) def mock_get_token(*args, **kwargs): if args[0] == SYS_TOKEN.token: return SYS_TOKEN return USR_TOKEN
def request(liveaction): """ Request an action execution. :return: (liveaction, execution) :rtype: tuple """ # Use the user context from the parent action execution. Subtasks in a workflow # action can be invoked by a system user and so we want to use the user context # from the original workflow action. if getattr(liveaction, 'context', None) and 'parent' in liveaction.context: parent = LiveAction.get_by_id(liveaction.context['parent']) liveaction.context['user'] = getattr(parent, 'context', dict()).get('user') # Validate action. action_db = action_utils.get_action_by_ref(liveaction.action) if not action_db: raise ValueError('Action "%s" cannot be found.' % liveaction.action) if not action_db.enabled: raise ValueError('Unable to execute. Action "%s" is disabled.' % liveaction.action) runnertype_db = action_utils.get_runnertype_by_name(action_db.runner_type['name']) if not hasattr(liveaction, 'parameters'): liveaction.parameters = dict() # Validate action parameters. schema = util_schema.get_parameter_schema(action_db) validator = util_schema.get_validator() util_schema.validate(liveaction.parameters, schema, validator, use_default=True) # validate that no immutable params are being overriden. Although possible to # ignore the override it is safer to inform the user to avoid surprises. immutables = _get_immutable_params(action_db.parameters) immutables.extend(_get_immutable_params(runnertype_db.runner_parameters)) overridden_immutables = [p for p in six.iterkeys(liveaction.parameters) if p in immutables] if len(overridden_immutables) > 0: raise ValueError('Override of immutable parameter(s) %s is unsupported.' % str(overridden_immutables)) # Set notification settings for action. # XXX: There are cases when we don't want notifications to be sent for a particular # execution. So we should look at liveaction.parameters['notify'] # and not set liveaction.notify. if action_db.notify: liveaction.notify = action_db.notify # Write to database and send to message queue. liveaction.status = action_constants.LIVEACTION_STATUS_REQUESTED liveaction.start_timestamp = isotime.add_utc_tz(datetime.datetime.utcnow()) # Publish creation after both liveaction and actionexecution are created. liveaction = LiveAction.add_or_update(liveaction, publish=False) execution = executions.create_execution_object(liveaction, publish=False) # Assume that this is a creation. LiveAction.publish_create(liveaction) LiveAction.publish_status(liveaction) ActionExecution.publish_create(execution) extra = {'liveaction_db': liveaction, 'execution_db': execution} LOG.audit('Action execution requested. LiveAction.id=%s, ActionExecution.id=%s' % (liveaction.id, execution.id), extra=extra) return liveaction, execution
import datetime import bson import mock from tests import AuthMiddlewareTest from st2common.util import isotime from st2common.models.db.auth import TokenDB from st2common.persistence.auth import Token from st2common.exceptions.auth import TokenNotFoundError OBJ_ID = bson.ObjectId() USER = '******' TOKEN = uuid.uuid4().hex NOW = isotime.add_utc_tz(datetime.datetime.utcnow()) FUTURE = NOW + datetime.timedelta(seconds=300) PAST = NOW + datetime.timedelta(seconds=-300) class TestTokenValidation(AuthMiddlewareTest): @mock.patch.object( Token, 'get', mock.Mock(return_value=TokenDB(id=OBJ_ID, user=USER, token=TOKEN, expiry=FUTURE))) def test_token_validation_token_in_headers(self): response = self.app.get('/v1/actions', headers={'X-Auth-Token': TOKEN}, expect_errors=False) self.assertEqual(response.status_int, 200) @mock.patch.object(