]> git.ipfire.org Git - pbs.git/commitdiff
builders: Add controls to manually start/stop builders
authorMichael Tremer <michael.tremer@ipfire.org>
Fri, 2 Jun 2023 11:35:49 +0000 (11:35 +0000)
committerMichael Tremer <michael.tremer@ipfire.org>
Fri, 2 Jun 2023 11:35:49 +0000 (11:35 +0000)
Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
Makefile.am
src/buildservice/builders.py
src/templates/builders/show.html
src/templates/builders/start.html [new file with mode: 0644]
src/templates/builders/stop.html [new file with mode: 0644]
src/web/__init__.py
src/web/builders.py

index 05e2236bb41d8129789f72ac4cd64102cb637b5a..9711e5732a012834815f59ce742c538529414a09 100644 (file)
@@ -169,7 +169,9 @@ dist_templates_builders_DATA = \
        src/templates/builders/delete.html \
        src/templates/builders/edit.html \
        src/templates/builders/index.html \
-       src/templates/builders/show.html
+       src/templates/builders/show.html \
+       src/templates/builders/start.html \
+       src/templates/builders/stop.html
 
 templates_buildersdir = $(templatesdir)/builders
 
index 52944ba74f07b84e46a4a161563aad1a5db3d23f..ad7b987022f45295f8c732894e5007cda2f38e68 100644 (file)
@@ -174,13 +174,13 @@ class Builders(base.Object):
                        # Start all builders that have been allocated at least one job
                        for builder in builders_to_be_launched:
                                tasks.create_task(
-                                       builder.start(wait=wait),
+                                       builder.start(auto=True, wait=wait),
                                )
 
                        # Shutdown the rest
                        for builder in builders_to_be_shut_down:
                                tasks.create_task(
-                                       builder.stop(wait=wait),
+                                       builder.stop(auto=True, wait=wait),
                                )
 
        # Stats
@@ -616,7 +616,7 @@ class Builder(base.DataObject):
                if self.instance:
                        return self.instance.state.get("Name")
 
-       async def start(self, wait=True):
+       async def start(self, auto=False, wait=True):
                """
                        Starts the instance on AWS
                """
@@ -624,8 +624,8 @@ class Builder(base.DataObject):
                if await self.is_running():
                        return
 
-               # Don't start when in maintenance mode
-               if self.maintenance:
+               # Don't start automatically when in maintenance mode
+               if auto and self.maintenance:
                        log.warning("Won't start %s in maintenance mode" % self)
                        return
 
@@ -681,7 +681,7 @@ class Builder(base.DataObject):
                except botocore.exceptions.ClientError as e:
                        log.warning("Could not change instance type of %s: %s" % (self, e))
 
-       async def stop(self, wait=True):
+       async def stop(self, auto=False, wait=True):
                """
                        Stops this instance on AWS
                """
@@ -689,8 +689,8 @@ class Builder(base.DataObject):
                if await self.is_shutting_down() or await self.is_shut_down():
                        return
 
-               # Don't stop when in maintenance mode
-               if self.maintenance:
+               # Don't stop automatically when in maintenance mode
+               if auto and self.maintenance:
                        log.warning("Won't stop %s in maintenance mode" % self)
                        return
 
index b777598fd8c3c7d6a46bf5a147603e7739aeac2a..7365ec8f7e9e07f5cc66b9183a5bad2421d32dc4 100644 (file)
                                <a class="button is-danger" href="/builders/{{ builder.hostname }}/delete">
                                        {{ _("Delete") }}
                                </a>
+
+                               {# Maintenance Mode Actions #}
+                               {% if builder.maintenance %}
+                                       {% if is_running %}
+                                               <a class="button is-danger" href="/builders/{{ builder.hostname }}/stop">
+                                                       {{ _("Stop") }}
+                                               </a>
+                                       {% elif is_shut_down %}
+                                               <a class="button is-success" href="/builders/{{ builder.hostname }}/start">
+                                                       {{ _("Start") }}
+                                               </a>
+                                       {% end %}
+                               {% end %}
                        </div>
                </section>
        {% end %}
diff --git a/src/templates/builders/start.html b/src/templates/builders/start.html
new file mode 100644 (file)
index 0000000..d76180b
--- /dev/null
@@ -0,0 +1,43 @@
+{% extends "../modal.html" %}
+
+{% block title %}{{ _("Start Builder") }} - {{ builder }}{% end block %}
+
+{% block breadcrumbs %}
+       <nav class="breadcrumb" aria-label="breadcrumbs">
+               <ul>
+                       <li>
+                               <a href="/builders">{{ _("Builders") }}</a>
+                       </li>
+                       <li>
+                               <a href="/builders/{{ builder.hostname }}">{{ builder }}</a>
+                       </li>
+                       <li class="is-active">
+                               <a href="#" aria-current="page">{{ _("Start") }}</a>
+                       </li>
+               </ul>
+       </nav>
+{% end block %}
+
+{% block modal_title %}
+       <h4 class="title is-4">{{ _("Start Builder") }}</h4>
+       <h6 class="subtitle is-6">{{ builder }}</h6>
+{% end block %}
+
+{% block modal %}
+       <form method="POST" action="">
+               {% raw xsrf_form_html() %}
+
+               <div class="content">
+                       <p>
+                               {{ _("Do you want to manually start %s?") % builder }}
+                       </p>
+               </div>
+
+               {# Submit! #}
+               <div class="field">
+                       <button type="submit" class="button is-success is-fullwidth">
+                               {{ _("Start") }}
+                       </button>
+               </div>
+       </form>
+{% end block %}
diff --git a/src/templates/builders/stop.html b/src/templates/builders/stop.html
new file mode 100644 (file)
index 0000000..03569bb
--- /dev/null
@@ -0,0 +1,43 @@
+{% extends "../modal.html" %}
+
+{% block title %}{{ _("Stop Builder") }} - {{ builder }}{% end block %}
+
+{% block breadcrumbs %}
+       <nav class="breadcrumb" aria-label="breadcrumbs">
+               <ul>
+                       <li>
+                               <a href="/builders">{{ _("Builders") }}</a>
+                       </li>
+                       <li>
+                               <a href="/builders/{{ builder.hostname }}">{{ builder }}</a>
+                       </li>
+                       <li class="is-active">
+                               <a href="#" aria-current="page">{{ _("Stop") }}</a>
+                       </li>
+               </ul>
+       </nav>
+{% end block %}
+
+{% block modal_title %}
+       <h4 class="title is-4">{{ _("Stop Builder") }}</h4>
+       <h6 class="subtitle is-6">{{ builder }}</h6>
+{% end block %}
+
+{% block modal %}
+       <form method="POST" action="">
+               {% raw xsrf_form_html() %}
+
+               <div class="content">
+                       <p>
+                               {{ _("Do you want to manually stop %s?") % builder }}
+                       </p>
+               </div>
+
+               {# Submit! #}
+               <div class="field">
+                       <button type="submit" class="button is-danger is-fullwidth">
+                               {{ _("Stop") }}
+                       </button>
+               </div>
+       </form>
+{% end block %}
index 6be6c04e150470e40c598c864ea7b0e3e7cb339e..5d5ce96e9bf85b3947672f3ddd869c7256fa978c 100644 (file)
@@ -183,7 +183,9 @@ class Application(tornado.web.Application):
                        (r"/builders/([A-Za-z0-9\-\.]+)", builders.ShowHandler),
                        (r"/builders/([A-Za-z0-9\-\.]+)/delete", builders.DeleteHandler),
                        (r"/builders/([A-Za-z0-9\-\.]+)/edit", builders.BuilderEditHandler),
+                       (r"/builders/([A-Za-z0-9\-\.]+)/start", builders.StartHandler),
                        (r"/builders/([A-Za-z0-9\-\.]+)/stats", builders.StatsHandler),
+                       (r"/builders/([A-Za-z0-9\-\.]+)/stop", builders.StopHandler),
                        (r"/api/v1/builders/control", builders.APIv1ControlHandler),
 
                        # Distributions
index 89336b4ddd544dba6875fcec9ebeaf1053ab5372..6a256891ee8085f63857dc540dac57a6d5649bdf 100644 (file)
@@ -80,12 +80,18 @@ class IndexHandler(base.BaseHandler):
 
 
 class ShowHandler(base.BaseHandler):
-       def get(self, hostname):
+       async def get(self, hostname):
                builder = self.backend.builders.get_by_name(hostname)
                if not builder:
                        raise tornado.web.HTTPError(404, "Could not find builder %s" % hostname)
 
-               self.render("builders/show.html", builder=builder)
+               # Fetch status
+               args = {
+                       "is_running"   : await builder.is_running(),
+                       "is_shut_down" : await builder.is_shut_down(),
+               }
+
+               self.render("builders/show.html", builder=builder, **args)
 
 
 class CreateHandler(base.BaseHandler):
@@ -176,6 +182,90 @@ class DeleteHandler(base.BaseHandler):
                self.redirect("/builders")
 
 
+class StartHandler(base.BaseHandler):
+       @tornado.web.authenticated
+       def get(self, name):
+               builder = self.backend.builders.get_by_name(name)
+               if not builder:
+                       raise tornado.web.HTTPError(404, "Builder not found: %s" % name)
+
+               # Check permissions
+               if not builder.has_perm(self.current_user):
+                       raise tornado.web.HTTPError(403)
+
+               # Builders must be in maintenance mode
+               if not builder.maintenance:
+                       raise tornado.web.HTTPError(400, "%s is not in maintenance mode" % builder)
+
+               self.render("builders/start.html", builder=builder)
+
+       @tornado.web.authenticated
+       async def post(self, name):
+               builder = self.backend.builders.get_by_name(name)
+               if not builder:
+                       raise tornado.web.HTTPError(404, "Builder not found: %s" % name)
+
+               # Check permissions
+               if not builder.has_perm(self.current_user):
+                       raise tornado.web.HTTPError(403)
+
+               # Builders must be in maintenance mode
+               if not builder.maintenance:
+                       raise tornado.web.HTTPError(400, "%s is not in maintenance mode" % builder)
+
+               # Start the builder
+               try:
+                       await builder.start(wait=False)
+
+               # XXX what do we do when this fails?
+               except:
+                       raise
+
+               self.redirect("/builders/%s" % builder.hostname)
+
+
+class StopHandler(base.BaseHandler):
+       @tornado.web.authenticated
+       def get(self, name):
+               builder = self.backend.builders.get_by_name(name)
+               if not builder:
+                       raise tornado.web.HTTPError(404, "Builder not found: %s" % name)
+
+               # Check permissions
+               if not builder.has_perm(self.current_user):
+                       raise tornado.web.HTTPError(403)
+
+               # Builders must be in maintenance mode
+               if not builder.maintenance:
+                       raise tornado.web.HTTPError(400, "%s is not in maintenance mode" % builder)
+
+               self.render("builders/stop.html", builder=builder)
+
+       @tornado.web.authenticated
+       async def post(self, name):
+               builder = self.backend.builders.get_by_name(name)
+               if not builder:
+                       raise tornado.web.HTTPError(404, "Builder not found: %s" % name)
+
+               # Check permissions
+               if not builder.has_perm(self.current_user):
+                       raise tornado.web.HTTPError(403)
+
+               # Builders must be in maintenance mode
+               if not builder.maintenance:
+                       raise tornado.web.HTTPError(400, "%s is not in maintenance mode" % builder)
+
+               # Stop the builder
+               try:
+                       await builder.stop(wait=False)
+
+               # XXX what do we do when this fails?
+               except:
+                       raise
+
+               self.redirect("/builders/%s" % builder.hostname)
+
+
 class StatsModule(ui_modules.UIModule):
        def render(self, builder):
                return self.render_string("builders/modules/stats.html", builder=builder)