Skip to content

Commit

Permalink
Merge branch 'release/2.12.0'
Browse files Browse the repository at this point in the history
  • Loading branch information
vmalloc committed Oct 29, 2017
2 parents a6a4d23 + 73ce700 commit b682dbd
Show file tree
Hide file tree
Showing 97 changed files with 2,283 additions and 887 deletions.
10 changes: 7 additions & 3 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ services:
before_install:

# update docker
- curl https://releases.rancher.com/install-docker/17.05.sh | sudo bash -
- curl https://releases.rancher.com/install-docker/17.09.sh | sudo bash -

# build the Backslash docker image
- python scripts/travis_version_fix.py
Expand All @@ -47,8 +47,10 @@ install:
- python manage.py bootstrap --develop
- python manage.py db upgrade

# required to work around https://github.com/dbcli/cli_helpers/issues/25
- .env/bin/pip uninstall -y cli_helpers
# (optional) install latest backlash-python
#- .env/bin/pip install -e git://github.com/getslash/backslash-python.git@develop#egg=backslash-python
- .env/bin/pip install -e git://github.com/getslash/backslash-python.git@develop#egg=backslash-python

# run docker-compose setup in testing mode
- sudo docker-compose -f docker/docker-compose.yml build
Expand All @@ -62,8 +64,10 @@ script:
# build the frontend to make sure we can serve '/' in the unit tests
- ./node_modules/.bin/ember build
- cd ..
- .env/bin/pip install -e git+https://github.com/getslash/backslash-python@develop#egg=backslash
- .env/bin/pip install -e git+https://github.com/getslash/slash@develop#egg=slash
- .env/bin/py.test tests
- .env/bin/py.test integration_tests --app-url http://127.0.0.1:8000 --driver SauceLabs --capability browserName Chrome --capability platform Linux --capability version 48.0 --capability tunnelIdentifier $TRAVIS_JOB_NUMBER
- if [ "$TRAVIS_PULL_REQUEST" == "false" ]; then .env/bin/py.test integration_tests --app-url http://127.0.0.1:8000 --driver SauceLabs --capability browserName Chrome --capability platform Linux --capability version 48.0 --capability tunnelIdentifier $TRAVIS_JOB_NUMBER; fi

after_success:
- .env/bin/python scripts/travis_docker_publish.py
12 changes: 12 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,17 @@
# Changelog

## Version 2.12.0

* Backslash now supports reporting interruption exceptions to help determine what caused the session to be interrupted. When the sessions are reported with a compatible toolchain (for example, Slash >= 1.5.0), the session and test views will show the interruption exceptions and their contexts
* Added an option to display metadata values in an accessible location for tests and sessions
* Warnings are now deduplicated within sessions and tests, preventing DB bloat
* Added a view for test cases. This is planned to evolve into a suite management feature over the next releases
* Fixed the order of display of quick-jump items, and they are now sorted by name
* Added the ability to search by product type
* Added a "status_description" field for tests, allowing setting more informative information on what the test is currently doing (via API)
* Added timing metrics API, enabling tests and sessions to display the total time distribution spent while running
* Fixed indication of search term syntax errors

## Version 2.11.1

* Fixed handling of test timespan when the test starts and before a keepalive is received
Expand Down
5 changes: 4 additions & 1 deletion _sample_suites/simple/test_1.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
import slash

import warnings


def test_1():
pass


@slash.tag('tag_without_value')
def test_2():
slash.logger.warning('This is a warning')
for i in range(5):
warnings.warn('This is a warning!')


@slash.tag('tag_with_value', 'some_value')
Expand Down
1 change: 1 addition & 0 deletions deps/develop.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ tmuxp
livereload
ipython
slash
yarl
2 changes: 1 addition & 1 deletion docker/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ RUN npm install -g ember-cli bower

ADD ./webapp/ /frontend/
RUN cd /frontend/ && yarn install && bower install --allow-root
RUN cd /frontend/ && node_modules/.bin/ember build
RUN cd /frontend/ && node_modules/.bin/ember build --environment production



Expand Down
11 changes: 11 additions & 0 deletions docs/installation.rst
Original file line number Diff line number Diff line change
Expand Up @@ -87,3 +87,14 @@ You can start Backslash by running::

Now that the server is up and running, it's time to configure your
server. You can read about it in the :ref:`configuration` section.



Upgrade
-------

The way to upgrade an existing deployment to the latest version is:
1. update the docker image:
$ docker pull getslash/backslash
2. restart the daemon:
$ sudo systemctl restart backslash-docker
18 changes: 18 additions & 0 deletions docs/server_configuration.rst
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,24 @@ page, you can specify it via the ``test_metadata_links`` variable::
The above will add a link to the test page pointing at the Jenkins
build whenever a "jenkins_url" metadata key is found for a test

Metadata Details
~~~~~~~~~~~~~~~~

Similar to metadata links, you can choose specific metadata keys to be displayed as informational values for tests and/or sessionsL::

...
test_metadata_display_items:
- key: my_metadata_key
name: My Metadata Key

Or::

...
session_metadata_display_items:
- key: my_metadata_key
name: My Metadata Key


Deployment Customization
------------------------

Expand Down
4 changes: 4 additions & 0 deletions flask_app/app.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,7 @@ display_names:
related_entity: related

test_metadata_links: []

session_metadata_display_items: []

test_metadata_display_items: []
17 changes: 13 additions & 4 deletions flask_app/blueprints/api/errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,15 @@
NoneType = type(None)


@API(version=3)
def add_error(message: str, exception_type: (str, NoneType)=None, traceback: (list, NoneType)=None, timestamp: (float, int)=None, test_id: int=None, session_id: int=None, is_failure: bool=False): # pylint: disable=bad-whitespace
@API(version=4)
def add_error(message: str, exception_type: (str, NoneType)=None, traceback: (list, NoneType)=None, timestamp: (float, int)=None, test_id: int=None, session_id: int=None, is_failure: bool=False, is_interruption: bool=False): # pylint: disable=bad-whitespace
# pylint: disable=superfluous-parens
if not ((test_id is not None) ^ (session_id is not None)):
error_abort('Either test_id or session_id required')

if is_failure and is_interruption:
error_abort('Interruptions cannot be marked as failures')

if timestamp is None:
timestamp = get_current_time()
if test_id is not None:
Expand All @@ -37,16 +40,22 @@ def add_error(message: str, exception_type: (str, NoneType)=None, traceback: (li

try:
obj = cls.query.filter(cls.id == object_id).one()
increment_field = cls.num_failures if is_failure else cls.num_errors
if is_failure:
increment_field = cls.num_failures
elif is_interruption:
increment_field = cls.num_interruptions
else:
increment_field = cls.num_errors
cls.query.filter(cls.id == object_id).update(
{increment_field: increment_field + 1})
err = Error(message=message,
exception_type=exception_type,
traceback_url=_normalize_traceback_get_url(traceback),
is_interruption=is_interruption,
is_failure=is_failure,
timestamp=timestamp)
obj.errors.append(err)
if obj.end_time is not None:
if not is_interruption and obj.end_time is not None:
if cls is Test:
if is_failure and obj.status not in (statuses.FAILURE, statuses.ERROR):
obj.status = statuses.FAILURE
Expand Down
38 changes: 11 additions & 27 deletions flask_app/blueprints/api/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import logbook

import requests
from flask import abort, current_app
from flask import abort
from flask_simple_api import error_abort
from flask_security import current_user

Expand All @@ -11,7 +11,7 @@

from .blueprint import API
from ... import metrics
from ...models import db, Session, Test, Comment, User, Role, Warning, Entity, TestVariation, TestMetadata
from ...models import db, Session, Test, Comment, User, Role, Entity, TestVariation, TestMetadata
from ...utils import get_current_time, statuses
from ...utils.api_utils import requires_role
from ...utils.subjects import get_or_create_subject_instance
Expand All @@ -29,6 +29,8 @@
from . import errors # pylint: disable=unused-import
from . import labels # pylint: disable=unused-import
from . import quick_search # pylint: disable=unused-import
from . import timing # pylint: disable=unused-import
from . import warnings # pylint: disable=unused-import
from .blueprint import blueprint # pylint: disable=unused-import


Expand Down Expand Up @@ -172,6 +174,12 @@ def report_test_distributed(
return test


@API
def update_status_description(test_id: int, description: str):
Test.query.get_or_404(test_id).status_description = description
db.session.commit()


@API
def report_test_end(id: int, duration: (float, int)=None):
test = Test.query.get(id)
Expand All @@ -195,7 +203,7 @@ def report_test_end(id: int, duration: (float, int)=None):
elif not test.interrupted and not test.skipped:
test.status = statuses.SUCCESS

db.session.add(test)
test.status_description = None
db.session.commit()


Expand Down Expand Up @@ -260,30 +268,6 @@ def _update_running_test_status(test_id, status, ignore_conflict=False, addition
abort(requests.codes.not_found)


@API
def add_warning(message: str, filename: str=None, lineno: int=None, test_id: int=None, session_id: int=None, timestamp: (int, float)=None):
# pylint: disable=superfluous-parens
if not ((test_id is not None) ^ (session_id is not None)):
error_abort('Either session_id or test_id required')
if session_id is not None:
obj = Session.query.get_or_404(session_id)
else:
obj = Test.query.get_or_404(test_id)
if timestamp is None:
timestamp = get_current_time()
if obj.num_warnings < current_app.config['MAX_WARNINGS_PER_ENTITY']:
db.session.add(
Warning(message=message, timestamp=timestamp, filename=filename, lineno=lineno, test_id=test_id, session_id=session_id))
obj.num_warnings = type(obj).num_warnings + 1
if session_id is None:
obj.session.num_test_warnings = Session.num_test_warnings + 1
db.session.add(obj.session)

db.session.add(obj)
db.session.commit()



@API(require_real_login=True)
def post_comment(comment: str, session_id: int=None, test_id: int=None):
if not (session_id is not None) ^ (test_id is not None):
Expand Down
5 changes: 4 additions & 1 deletion flask_app/blueprints/api/quick_search.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,10 @@ def quick_search(term: str):
(select email as key, CASE WHEN first_name is NULL THEN email
ELSE (first_name || ' ' || last_name || ' (' || email || ')') END as name, 'user' as type from "user")) u
where u.name ilike :term limit :num_hits""").params(
where u.name ilike :term
ORDER BY name asc
limit :num_hits
""").params(
term='%{}%'.format(term),
num_hits=num_hits,
)
Expand Down
41 changes: 41 additions & 0 deletions flask_app/blueprints/api/timing.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import flux
from ...models import Timing, db
from ...utils.db_utils import json_object_agg
from .blueprint import API

from sqlalchemy import case

NoneType = type(None)


@API
def report_timing_start(name: str, session_id: int, test_id: (int, NoneType)=None): # pylint: disable=bad-whitespace
db.session.execute(
'''
INSERT INTO timing(session_id, test_id, name, total)
VALUES (:session_id, :test_id, :name, :interval)
ON CONFLICT(id) DO UPDATE SET total = timing.total + EXCLUDED.total''',
{'session_id': session_id, 'test_id': test_id, 'name': name, 'interval': -flux.current_timeline.time()})
db.session.commit()


@API
def report_timing_end(name: str, session_id: int, test_id: (int, NoneType)=None): # pylint: disable=bad-whitespace
timing = Timing.query.filter_by(session_id=session_id, test_id=test_id, name=name).first_or_404()
timing.total = Timing.total + flux.current_timeline.time()
db.session.commit()


@API
def get_timings(session_id: (int, NoneType)=None, test_id: (int, NoneType)=None):
now = flux.current_timeline.time()
total_clause = case(
[
(Timing.total < 0, now - Timing.total)
], else_=Timing.total)
kwargs = {'test_id': test_id}
if session_id is not None:
kwargs['session_id'] = session_id
query = db.session.query(json_object_agg(Timing.name, total_clause)).\
filter_by(**kwargs)
return query.scalar() or {}
37 changes: 37 additions & 0 deletions flask_app/blueprints/api/warnings.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# pylint: disable=bad-whitespace
from flask import current_app
from flask_simple_api import error_abort
from .blueprint import API
from ...models import Session, Warning, Test, db
from ...utils import get_current_time


@API
def add_warning(message:str, filename:str=None, lineno:int=None, test_id:int=None, session_id:int=None, timestamp:(int,float)=None):
# pylint: disable=superfluous-parens
if not ((test_id is not None) ^ (session_id is not None)):
error_abort('Either session_id or test_id required')

if session_id is not None:
obj = Session.query.get_or_404(session_id)
else:
obj = Test.query.get_or_404(test_id)

if timestamp is None:
timestamp = get_current_time()

warning = Warning.query.filter_by(session_id=session_id, test_id=test_id, lineno=lineno, filename=filename, message=message).first()
if warning is None:
if obj.num_warnings < current_app.config['MAX_WARNINGS_PER_ENTITY']:
warning = Warning(message=message, timestamp=timestamp, filename=filename, lineno=lineno, test_id=test_id, session_id=session_id)
db.session.add(warning)
else:
warning.num_warnings = Warning.num_warnings + 1
warning.timestamp = timestamp

obj.num_warnings = type(obj).num_warnings + 1
if session_id is None:
obj.session.num_test_warnings = Session.num_test_warnings + 1
db.session.add(obj.session)

db.session.commit()
Loading

0 comments on commit b682dbd

Please sign in to comment.