From: Michael Tremer Date: Tue, 23 May 2023 15:14:35 +0000 (+0000) Subject: monitorings: Implement creating/editing/deleting monitorings X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=91d9818d0ffb2dc6e24f9aded54896c8bc17b826;p=pbs.git monitorings: Implement creating/editing/deleting monitorings Signed-off-by: Michael Tremer --- diff --git a/Makefile.am b/Makefile.am index 3ccfb95d..9974a933 100644 --- a/Makefile.am +++ b/Makefile.am @@ -233,6 +233,8 @@ dist_templates_distros_modules_DATA = \ templates_distros_modulesdir = $(templates_distrosdir)/modules dist_templates_monitorings_DATA = \ + src/templates/monitorings/delete.html \ + src/templates/monitorings/edit.html \ src/templates/monitorings/show.html templates_monitoringsdir = $(templatesdir)/monitorings diff --git a/src/buildservice/releasemonitoring.py b/src/buildservice/releasemonitoring.py index 84a5bc1b..05e9fedc 100644 --- a/src/buildservice/releasemonitoring.py +++ b/src/buildservice/releasemonitoring.py @@ -100,17 +100,11 @@ class Monitorings(base.Object): return body - def _get_monitoring(self, query, *args): - res = self.db.get(query, *args) + def _get_monitoring(self, query, *args, **kwargs): + return self.db.fetch_one(Monitoring, query, *args, **kwargs) - if res: - return Monitoring(self.backend, res.id, res) - - def _get_monitorings(self, query, *args): - res = self.db.query(query, *args) - - for row in res: - yield Monitoring(self.backend, row.id, row) + def _get_monitorings(self, query, *args, **kwargs): + return self.db.fetch_many(Monitoring, query, *args, **kwargs) def get_by_id(self, id): return self._get_monitoring(""" @@ -135,11 +129,11 @@ class Monitorings(base.Object): distro_id = %s AND name = %s - """, distro, name, + """, distro, name, distro=distro, ) async def create(self, distro, name, created_by, project_id, - follow="mainline", create_builds=True): + follow="mainline", create_builds=True, check=True): monitoring = self._get_monitoring(""" INSERT INTO release_monitorings @@ -156,11 +150,13 @@ class Monitorings(base.Object): ) RETURNING * - """, distro, name, created_by, project_id, follow, + """, distro, name, created_by, project_id, follow, create_builds, + distro=distro, ) - # Schedule the first check in the background - self.backend.run_task(monitoring.check) + # Perform the first check immediately + if check: + await monitoring.check() return monitoring @@ -248,10 +244,16 @@ class Monitoring(base.DataObject): def follow(self): return self.data.follow - @property - def create_builds(self): + # Create Builds + + def get_create_builds(self): return self.data.create_builds + def set_create_builds(self, value): + self._set_attribute("create_builds", value) + + create_builds = property(get_create_builds, set_create_builds) + # Permissions def has_perm(self, user=None): @@ -262,6 +264,19 @@ class Monitoring(base.DataObject): # Users must be admins return user.is_admin() + # Delete + + async def delete(self, user=None): + # Mark as deleted + self._set_attribute_now("deleted_at") + if user: + self._set_attribute("deleted_by", user) + + # Delete all releases + async with asyncio.TaskGroup() as tasks: + for release in self.releases: + tasks.create_task(release.delete()) + # Check async def check(self): @@ -405,8 +420,10 @@ class Monitoring(base.DataObject): Returns True if a build with this version already exists """ # XXX needs to check if we already have a newer version + # XXX packages don't have version separately + # XXX How do we get this from EVR? - return version in [build.package.version for build in self.builds] + #return version in [build.pkg.version for build in self.builds] @property def builds(self): @@ -444,6 +461,25 @@ class Release(base.DataObject): def created_at(self): return self.data.created_at + # Delete + + async def delete(self, user=None): + """ + Deletes this release + """ + async with asyncio.TaskGroup() as tasks: + # Delete the build + if self.build: + tasks.create_task(self.build.delete(user=user)) + + # Close the bug + tasks.create_task( + self._close_bug( + resolution="WONTFIX", + comment="Release Monitoring for this package has been terminated", + ), + ) + # Bug async def _create_bug(self): @@ -492,10 +528,24 @@ class Release(base.DataObject): return bug + async def _close_bug(self, *args, **kwargs): + # Fetch the bug + bug = await self.get_bug() + + if bug and not bug.is_closed(): + await bug.close(*args, **kwargs) + @property def bug_id(self): return self.data.bug_id + async def get_bug(self): + """ + Fetches the bug from Bugzilla + """ + if self.bug_id: + return await self.backend.bugzilla.get_bug(self.bug_id) + # Build def get_build(self): diff --git a/src/templates/monitorings/delete.html b/src/templates/monitorings/delete.html new file mode 100644 index 00000000..d8b367fe --- /dev/null +++ b/src/templates/monitorings/delete.html @@ -0,0 +1,47 @@ +{% extends "../modal.html" %} + +{% block title %}{{ _("Delete Release Monitoring") }} - {{ monitoring }}{% end block %} + +{% block breadcrumbs %} + +{% end block %} + +{% block modal_title %} +

{{ _("Delete Release Monitoring") }}

+
{{ monitoring }}
+{% end block %} + +{% block modal %} +
+ {% raw xsrf_form_html() %} + +
+

+ {{ _("Are you sure you want to delete %s?") % monitoring }} +

+
+ + {# Submit! #} +
+ +
+
+{% end block %} diff --git a/src/templates/monitorings/edit.html b/src/templates/monitorings/edit.html new file mode 100644 index 00000000..251687de --- /dev/null +++ b/src/templates/monitorings/edit.html @@ -0,0 +1,126 @@ +{% extends "../modal.html" %} + +{% block title %} + {% if monitoring %} + {{ _("Edit Release Monitoring") }} - {{ monitoring }} + {% else %} + {{ _("Create Release Monitoring") }} + {% end %} +{% end block %} + +{% block breadcrumbs %} + +{% end block %} + +{% block modal_title %} + {% if monitoring %} +

{{ _("Edit Release Monitoring") }}

+
{{ monitoring }}
+ {% else %} +

{{ _("Create A New Release Monitoring") }}

+ {% end %} +{% end block %} + +{% block modal %} +
+ {% raw xsrf_form_html() %} + + {# Project & Follow can only be set once #} + {% if not monitoring %} + {# Project #} +
+ +
+
+ +
+
+

+ {{ _("Select an upstream project that matches this package.") }} +

+
+ + {% set follows = { + "mainline" : _("Mainline - Follow the latest releases"), + } %} + + {# Follow #} +
+ +
+
+ +
+
+

+ {{ _("Select which releases to follow.") }} +

+
+ {% end %} + + {# Create Builds? #} +
+ +
+ +
+
+ + {# Submit! #} +
+ {% if monitoring %} + + {% else %} + + {% end %} +
+
+{% end block %} diff --git a/src/templates/monitorings/show.html b/src/templates/monitorings/show.html index ffc292d0..100a1c80 100644 --- a/src/templates/monitorings/show.html +++ b/src/templates/monitorings/show.html @@ -55,7 +55,11 @@

{{ _("Last Check") }}

- {{ locale.format_date(monitoring.last_check_at, shorter=True) }} + {% if monitoring.last_check_at %} + {{ locale.format_date(monitoring.last_check_at, shorter=True) }} + {% else %} + {{ _("N/A") }} + {% end %}

diff --git a/src/templates/packages/name.html b/src/templates/packages/name.html index 4cb594ef..b1464468 100644 --- a/src/templates/packages/name.html +++ b/src/templates/packages/name.html @@ -65,7 +65,7 @@ {% else %} - + {{ _("Enable Release Monitoring") }} {% end %} diff --git a/src/web/__init__.py b/src/web/__init__.py index 1dba2b1a..e82dfb82 100644 --- a/src/web/__init__.py +++ b/src/web/__init__.py @@ -205,6 +205,9 @@ class Application(tornado.web.Application): # Distro Monitorings (r"/distros/([A-Za-z0-9\-\.]+)/monitorings/([\w\-_]+)", monitorings.ShowHandler), (r"/distros/([A-Za-z0-9\-\.]+)/monitorings/([\w\-_]+)/check", monitorings.CheckHandler), + (r"/distros/([A-Za-z0-9\-\.]+)/monitorings/([\w\-_]+)/create", monitorings.CreateHandler), + (r"/distros/([A-Za-z0-9\-\.]+)/monitorings/([\w\-_]+)/delete", monitorings.DeleteHandler), + (r"/distros/([A-Za-z0-9\-\.]+)/monitorings/([\w\-_]+)/edit", monitorings.EditHandler), # Mirrors (r"/mirrors", mirrors.IndexHandler), diff --git a/src/web/monitorings.py b/src/web/monitorings.py index c7b3ac5c..ed9ff761 100644 --- a/src/web/monitorings.py +++ b/src/web/monitorings.py @@ -38,7 +38,128 @@ class ShowHandler(base.BaseHandler): self.render("monitorings/show.html", monitoring=monitoring) -class CheckHandler(base.BaseHandler): +class CreateHandler(base.BaseHandler): + @tornado.web.authenticated + async def get(self, slug, name): + # Fetch the distribution + distro = self.backend.distros.get_by_slug(slug) + if not distro: + raise tornado.web.HTTPError(404, "Could not find distro %s" % slug) + + # Fetch the monitoring + monitoring = self.backend.monitorings.get_by_distro_and_name(distro, name) + if monitoring: + raise tornado.web.HTTPError(400, "Monitoring for %s in %s already exists" % (name, distro)) + + # Check permissions + if not self.current_user.is_admin(): + raise tornado.web.HTTPError(403) + + # Fetch the search query + q = self.get_argument("q", name) + + # Search for projects + projects = await self.backend.monitorings.search(q) + + self.render("monitorings/edit.html", monitoring=None, distro=distro, name=name, + projects=projects) + + @tornado.web.authenticated + async def post(self, slug, name): + # Fetch the distribution + distro = self.backend.distros.get_by_slug(slug) + if not distro: + raise tornado.web.HTTPError(404, "Could not find distro %s" % slug) + + # Fetch the monitoring + monitoring = self.backend.monitorings.get_by_distro_and_name(distro, name) + if monitoring: + raise tornado.web.HTTPError(400, "Monitoring for %s in %s already exists" % (name, distro)) + + # Check permissions + if not self.current_user.is_admin(): + raise tornado.web.HTTPError(403) + + # Collect parameters + project_id = self.get_argument_int("project_id") + follow = self.get_argument("follow") + create_builds = self.get_argument_bool("create_builds") + + with self.db.transaction(): + try: + # Create a new monitoring + monitoring = await self.backend.monitorings.create(distro, name, + created_by=self.current_user, project_id=project_id, + follow=follow, create_builds=create_builds, + ) + + except ValueError as e: + raise tornado.web.HTTPError(400, "%s" % e) from e + + # Redirect to the newly created monitoring + self.redirect(monitoring.url) + + +class EditHandler(base.BaseHandler): + @tornado.web.authenticated + def get(self, slug, name): + # Fetch the distribution + distro = self.backend.distros.get_by_slug(slug) + if not distro: + raise tornado.web.HTTPError(404, "Could not find distro %s" % slug) + + # Fetch the monitoring + monitoring = self.backend.monitorings.get_by_distro_and_name(distro, name) + if not monitoring: + raise tornado.web.HTTPError(404, "Could not find monitoring for %s in %s" % (name, distro)) + + # Check permissions + if not monitoring.has_perm(self.current_user): + raise tornado.web.HTTPError(403) + + self.render("monitorings/edit.html", monitoring=monitoring, distro=distro, name=name) + + @tornado.web.authenticated + def post(self, slug, name): + # Fetch the distribution + distro = self.backend.distros.get_by_slug(slug) + if not distro: + raise tornado.web.HTTPError(404, "Could not find distro %s" % slug) + + # Fetch the monitoring + monitoring = self.backend.monitorings.get_by_distro_and_name(distro, name) + if not monitoring: + raise tornado.web.HTTPError(404, "Could not find monitoring for %s in %s" % (name, distro)) + + # Check permissions + if not monitoring.has_perm(self.current_user): + raise tornado.web.HTTPError(403) + + with self.db.transaction(): + monitoring.create_builds = self.get_argument_bool("create_builds") + + self.redirect(monitoring.url) + + +class DeleteHandler(base.BaseHandler): + @tornado.web.authenticated + def get(self, slug, name): + # Fetch the distribution + distro = self.backend.distros.get_by_slug(slug) + if not distro: + raise tornado.web.HTTPError(404, "Could not find distro %s" % slug) + + # Fetch the monitoring + monitoring = self.backend.monitorings.get_by_distro_and_name(distro, name) + if not monitoring: + raise tornado.web.HTTPError(404, "Could not find monitoring for %s in %s" % (name, distro)) + + # Check permissions + if not monitoring.has_perm(self.current_user): + raise tornado.web.HTTPError(403) + + self.render("monitorings/delete.html", monitoring=monitoring) + @tornado.web.authenticated async def post(self, slug, name): # Fetch the distribution @@ -55,9 +176,31 @@ class CheckHandler(base.BaseHandler): if not monitoring.has_perm(self.current_user): raise tornado.web.HTTPError(403) - # Perform check with self.db.transaction(): - await monitoring.check() + await monitoring.delete(self.current_user) + + self.redirect("/packages/%s" % monitoring.name) + + +class CheckHandler(base.BaseHandler): + @tornado.web.authenticated + async def post(self, slug, name): + # Fetch the distribution + distro = self.backend.distros.get_by_slug(slug) + if not distro: + raise tornado.web.HTTPError(404, "Could not find distro %s" % slug) + + # Fetch the monitoring + monitoring = self.backend.monitorings.get_by_distro_and_name(distro, name) + if not monitoring: + raise tornado.web.HTTPError(404, "Could not find monitoring for %s in %s" % (name, distro)) + + # Check permissions + if not monitoring.has_perm(self.current_user): + raise tornado.web.HTTPError(403) + + # Perform check (it is already starting its own transaction) + await monitoring.check() # Redirect back self.redirect(monitoring.url)