[LNT] r306901 - Add a delete method with auth to the runs API
Chris Matthews via llvm-commits
llvm-commits at lists.llvm.org
Fri Jun 30 15:39:22 PDT 2017
Author: cmatthews
Date: Fri Jun 30 15:39:22 2017
New Revision: 306901
URL: http://llvm.org/viewvc/llvm-project?rev=306901&view=rev
Log:
Add a delete method with auth to the runs API
Add a token based auth system to the API. Add a delete method to the
runs endpoint for authorized users. This will make it easier for non
admins to delete their data. Hopefully we can get a better auth system
in place, but for now it is single key per instance.
Added:
lnt/trunk/tests/server/ui/test_api_deletes.py
Modified:
lnt/trunk/lnt/server/config.py
lnt/trunk/lnt/server/db/testsuitedb.py
lnt/trunk/lnt/server/ui/api.py
lnt/trunk/tests/SharedInputs/SmallInstance/lnt.cfg
lnt/trunk/tests/server/ui/change_processing.py
Modified: lnt/trunk/lnt/server/config.py
URL: http://llvm.org/viewvc/llvm-project/lnt/trunk/lnt/server/config.py?rev=306901&r1=306900&r2=306901&view=diff
==============================================================================
--- lnt/trunk/lnt/server/config.py (original)
+++ lnt/trunk/lnt/server/config.py Fri Jun 30 15:39:22 2017
@@ -110,6 +110,7 @@ class Config:
# FIXME: Remove this default.
tempDir = data.get('tmp_dir', 'viewer/resources/graphs')
blacklist = data.get('blacklist', None)
+ api_auth_token = data.get('api_auth_token', None)
if blacklist and baseDir:
blacklist = os.path.join(baseDir, blacklist)
else:
@@ -123,7 +124,7 @@ class Config:
default_email_config,
0))
for k, v in data['databases'].items()]),
- blacklist)
+ blacklist, api_auth_token)
@staticmethod
def dummy_instance():
@@ -142,9 +143,10 @@ class Config:
profileDirPath,
secretKey,
dbInfo,
- blacklist)
+ blacklist,
+ "test_key")
- def __init__(self, name, zorgURL, dbDir, tempDir, profileDir, secretKey, databases, blacklist):
+ def __init__(self, name, zorgURL, dbDir, tempDir, profileDir, secretKey, databases, blacklist, api_auth_token=None):
self.name = name
self.zorgURL = zorgURL
self.dbDir = dbDir
@@ -157,6 +159,7 @@ class Config:
self.databases = databases
for db in self.databases.values():
db.config = self
+ self.api_auth_token = api_auth_token
def get_database(self, name, echo=False):
"""
Modified: lnt/trunk/lnt/server/db/testsuitedb.py
URL: http://llvm.org/viewvc/llvm-project/lnt/trunk/lnt/server/db/testsuitedb.py?rev=306901&r1=306900&r2=306901&view=diff
==============================================================================
--- lnt/trunk/lnt/server/db/testsuitedb.py (original)
+++ lnt/trunk/lnt/server/db/testsuitedb.py Fri Jun 30 15:39:22 2017
@@ -12,6 +12,7 @@ import os
import sqlalchemy
from flask import session
from sqlalchemy import *
+from typing import List
import testsuite
import lnt.testing.profile.profile as profile
@@ -1003,5 +1004,37 @@ supplied run is missing required run par
def get_next_runs_on_machine(self, run, N):
return self.get_adjacent_runs_on_machine(run, N, direction = 1)
+ def delete_runs(self, run_ids, commit=False):
+ # type: (object, List[int], bool) -> None
+ """Delete the following Runs, their Samples, Field Changes and Regression Indicators.
+
+ :param run_ids: list of the run ids to delete.
+ :param commit: commit now?
+ """
+
+ # Delete all samples associated with those runs.
+ self.query(self.Sample). \
+ filter(self.Sample.run_id.in_(run_ids)). \
+ delete(synchronize_session=False)
+
+ # Delete all FieldChanges and RegressionIndicators
+ for r in run_ids:
+ fcs = self.query(self.FieldChange). \
+ filter(self.FieldChange.run_id == r).all()
+ for f in fcs:
+ ris = self.query(self.RegressionIndicator). \
+ filter(self.RegressionIndicator.field_change_id == f.id).all()
+ for ri in ris:
+ self.delete(ri)
+ self.delete(f)
+
+ # Delete all those runs.
+ self.query(self.Run). \
+ filter(self.Run.id.in_(run_ids)). \
+ delete(synchronize_session=False)
+
+ if commit:
+ self.commit()
+
def __repr__(self):
return "{} (on {})".format(self.name, self.v4db.path)
Modified: lnt/trunk/lnt/server/ui/api.py
URL: http://llvm.org/viewvc/llvm-project/lnt/trunk/lnt/server/ui/api.py?rev=306901&r1=306900&r2=306901&view=diff
==============================================================================
--- lnt/trunk/lnt/server/ui/api.py (original)
+++ lnt/trunk/lnt/server/ui/api.py Fri Jun 30 15:39:22 2017
@@ -8,6 +8,7 @@ from sqlalchemy.orm.exc import NoResultF
from lnt.server.ui.util import convert_revision
from lnt.testing import PASS
+from functools import wraps
def in_db(func):
@@ -32,6 +33,16 @@ def in_db(func):
return wrap
+def requires_auth_token(f):
+ @wraps(f)
+ def decorated(*args, **kwargs):
+ token = request.headers.get("AuthToken", None)
+ if not current_app.old_config.api_auth_token or token != current_app.old_config.api_auth_token:
+ return abort(401, msg="Auth Token must be passed in AuthToken header, and included in LNT config.")
+ return f(*args, **kwargs)
+ return decorated
+
+
def with_ts(obj):
"""For Url type fields to work, the objects we return must have a test-suite
and database attribute set, the function attempts to set them."""
@@ -169,6 +180,23 @@ class Runs(Resource):
full_run['samples'] = ret
return jsonify(full_run)
+ @staticmethod
+ @requires_auth_token
+ def delete(run_id):
+ ts = request.get_testsuite()
+
+ try:
+ run = ts.query(ts.Run) \
+ .join(ts.Machine) \
+ .join(ts.Order) \
+ .filter(ts.Run.id == run_id) \
+ .options(joinedload('order')) \
+ .one()
+ except sqlalchemy.orm.exc.NoResultFound:
+ return abort(404, msg="Did not find run " + str(run_id))
+ ts.delete_runs([run_id], commit=True)
+ return
+
class Order(Resource):
method_decorators = [in_db]
Modified: lnt/trunk/tests/SharedInputs/SmallInstance/lnt.cfg
URL: http://llvm.org/viewvc/llvm-project/lnt/trunk/tests/SharedInputs/SmallInstance/lnt.cfg?rev=306901&r1=306900&r2=306901&view=diff
==============================================================================
--- lnt/trunk/tests/SharedInputs/SmallInstance/lnt.cfg (original)
+++ lnt/trunk/tests/SharedInputs/SmallInstance/lnt.cfg Fri Jun 30 15:39:22 2017
@@ -24,6 +24,9 @@ db_dir = 'data'
# Secret key for this server instance.
secret_key = '540de6a499d57deecc880ce399a95de8b7bf43fb'
+# API Auth Token
+api_auth_token = "test_token"
+
# The list of available databases, and their properties. At a minimum, there
# should be a 'default' entry for the default database.
databases = {
Modified: lnt/trunk/tests/server/ui/change_processing.py
URL: http://llvm.org/viewvc/llvm-project/lnt/trunk/tests/server/ui/change_processing.py?rev=306901&r1=306900&r2=306901&view=diff
==============================================================================
--- lnt/trunk/tests/server/ui/change_processing.py (original)
+++ lnt/trunk/tests/server/ui/change_processing.py Fri Jun 30 15:39:22 2017
@@ -72,6 +72,7 @@ class ChangeProcessingTests(unittest.Tes
machine,
test,
a_field)
+ field_change.run = run
ts_db.add(field_change)
fc_mach2 = ts_db.FieldChange(order1234,
@@ -79,12 +80,15 @@ class ChangeProcessingTests(unittest.Tes
machine2,
test,
a_field)
+ fc_mach2.run = run2
ts_db.add(fc_mach2)
field_change2 = self.field_change2 = ts_db.FieldChange(order1235, order1236, machine,
test,
a_field)
+
+ field_change2.run = run
ts_db.add(field_change2)
field_change3 = self.field_change3 = ts_db.FieldChange(order1237, order1238, machine,
@@ -192,6 +196,30 @@ class ChangeProcessingTests(unittest.Tes
delete_fieldchange(self.ts_db, self.field_change2)
delete_fieldchange(self.ts_db, self.field_change3)
+ def test_run_deletion(self):
+ """Do the FC and RIs get cleaned up when runs are deleted?"""
+ ts_db = self.ts_db
+ run_ids = ts_db.query(ts_db.Run.id).all()
+ fc_ids = ts_db.query(ts_db.FieldChange.id).all()
+ ri_ids = ts_db.query(ts_db.RegressionIndicator.id).all()
+
+ ts_db.delete_runs([r[0] for r in run_ids])
+ run_ids_new = ts_db.query(ts_db.Run.id).all()
+ fc_ids_new = ts_db.query(ts_db.FieldChange.id).all()
+ ri_ids_new = ts_db.query(ts_db.RegressionIndicator.id).all()
+ # Make sure there was some runs.
+ self.assertNotEqual(len(run_ids), 0)
+ self.assertNotEqual(len(fc_ids), 0)
+ self.assertNotEqual(len(ri_ids), 0)
+
+ # Now make sure there were all deleted.
+ self.assertEqual(len(run_ids_new), 0)
+
+ # Not all the FCs are covered by the runs.
+ self.assertEqual(len(fc_ids_new), 1)
+
+ self.assertEqual(len(ri_ids_new), 0)
+
if __name__ == '__main__':
unittest.main(argv=[sys.argv[0], ])
Added: lnt/trunk/tests/server/ui/test_api_deletes.py
URL: http://llvm.org/viewvc/llvm-project/lnt/trunk/tests/server/ui/test_api_deletes.py?rev=306901&view=auto
==============================================================================
--- lnt/trunk/tests/server/ui/test_api_deletes.py (added)
+++ lnt/trunk/tests/server/ui/test_api_deletes.py Fri Jun 30 15:39:22 2017
@@ -0,0 +1,60 @@
+# Check that the LNT REST JSON API is working.
+# create temporary instance
+# RUN: rm -rf %t.instance
+# RUN: python %{shared_inputs}/create_temp_instance.py \
+# RUN: %s %{shared_inputs}/SmallInstance \
+# RUN: %t.instance %S/Inputs/V4Pages_extra_records.sql
+#
+# RUN: python %s %t.instance
+
+import logging
+import sys
+import unittest
+
+import lnt.server.db.migrate
+import lnt.server.ui.app
+from V4Pages import check_json
+
+logging.basicConfig(level=logging.DEBUG)
+
+
+class JSONAPIDeleteTester(unittest.TestCase):
+ """Test the REST api."""
+
+ def setUp(self):
+ """Bind to the LNT test instance."""
+ _, instance_path = sys.argv
+ app = lnt.server.ui.app.App.create_standalone(instance_path)
+ app.testing = True
+ self.client = app.test_client()
+
+ def test_run_api(self):
+ """Check /runs/n can be deleted."""
+ client = self.client
+
+ j = check_json(client, 'api/db_default/v4/nts/runs/1')
+ sample_ids = [s['id'] for s in j['samples']]
+ self.assertNotEqual(len(sample_ids), 0)
+ for sid in sample_ids:
+ resp = client.get('api/db_default/v4/nts/samples/{}'.format(sid))
+ self.assertEqual(resp.status_code, 200)
+
+ resp = client.delete('api/db_default/v4/nts/runs/1')
+ self.assertEqual(resp.status_code, 401)
+
+ resp = client.delete('api/db_default/v4/nts/runs/1', headers={'AuthToken': 'wrong token'})
+ self.assertEqual(resp.status_code, 401)
+
+ resp = client.delete('api/db_default/v4/nts/runs/1', headers={'AuthToken': 'test_token'})
+ self.assertEqual(resp.status_code, 200)
+
+ resp = client.get('api/db_default/v4/nts/runs/1')
+ self.assertEqual(resp.status_code, 404)
+
+ for sid in sample_ids:
+ resp = client.get('api/db_default/v4/nts/samples/{}'.format(sid))
+ self.assertEqual(resp.status_code, 404)
+
+
+if __name__ == '__main__':
+ unittest.main(argv=[sys.argv[0], ])
More information about the llvm-commits
mailing list