Skip to contentSkip to conversation

toaster: Delete project fixes and clean ups

PATCH New bavery 1 participant 7 messages Oct 6, 2016

lore
Share & tools
lore

bavery

author
#0 lore raw

cover toaster: Delete project fixes and clean ups

This addresses several toaster bugs:

9936 - import layer dependencies button toggle 9097 - startup message cleanup
10289 - stop in progress build before deleting project

Also has some buildinfohelper cleanup as well as some test fixes for the ui changes.

-bavery

The following changes since commit 4189a1977e65f71ddb8fc0498bcef91754c673d8:

bitbake: toaster: add Font Awesome license (2016-10-05 10:28:53 +0100)

are available in the git repository at:

git://git.yoctoproject.org/poky-contrib bavery/submit/michaelw/toaster/pending_for_upstream_5_10_16
http://git.yoctoproject.org/cgit.cgi/poky-contrib/log/?h=bavery/submit/michaelw/toaster/pending_for_upstream_5_10_16

Michael Wood (6):
toaster: api / project Cancel any in progress builds before project
delete
toaster: buildinfohelper: Use correct way to get message from
LogMessage
toaster: checksettings Remove confusing startup messages
toaster: importlayer Fix layer dependencies button state toggle
toaster: Delete notification update front end implementation to design
toaster: Update tests to reflect front end changes

 lib/bb/ui/buildinfohelper.py                       |  9 ++--
 .../management/commands/checksettings.py           |  2 -
 .../tests/browser/test_layerdetails_page.py        |  4 ++
 lib/toaster/tests/browser/test_project_page.py     |  2 +-
 lib/toaster/toastergui/api.py                      | 57 +++++++++++++---------
 lib/toaster/toastergui/static/js/importlayer.js    | 30 ++----------
 lib/toaster/toastergui/static/js/layerdetails.js   |  2 +
 lib/toaster/toastergui/static/js/libtoaster.js     |  2 +-
 lib/toaster/toastergui/templates/base.html         |  2 +-
 .../toastergui/templates/basebuildpage.html        |  6 ++-
 lib/toaster/toastergui/templates/project.html      | 12 +++--
 lib/toaster/toastergui/tests.py                    |  8 ++-
 12 files changed, 71 insertions(+), 65 deletions(-)

--
1.9.1

author
#1 lore raw

PATCH 1/6 toaster: api / project Cancel any in progress builds before project delete

From: Michael Wood <[email protected]>

Before we finally delete any project make sure we send the cancel command to any in-progress builds. This ensures that an inaccessible build doesn't block up the system and that we don't get errors after deletion.

[YOCTO #10289]

---
 lib/toaster/toastergui/api.py                 | 55 +++++++++++++++++----------
 lib/toaster/toastergui/templates/project.html | 12 +++---
 2 files changed, 41 insertions(+), 26 deletions(-)
lib/toaster/toastergui/api.py +34 -21
index 3a05d66..b01d4ba 100644
@@ -49,6 +49,28 @@ class XhrBuildRequest(View):
     def get(self, request, *args, **kwargs):
         return HttpResponse()
 
+    @staticmethod
+    def cancel_build(br):
+        """Cancel a build request"""
+        try:
+            bbctrl = bbcontroller.BitbakeController(br.environment)
+            bbctrl.forceShutDown()
+        except:
+            # We catch a bunch of exceptions here because
+            # this is where the server has not had time to start up
+            # and the build request or build is in transit between
+            # processes.
+            # We can safely just set the build as cancelled
+            # already as it never got started
+            build = br.build
+            build.outcome = Build.CANCELLED
+            build.save()
+
+        # We now hand over to the buildinfohelper to update the
+        # build state once we've finished cancelling
+        br.state = BuildRequest.REQ_CANCELLING
+        br.save()
+
     def post(self, request, *args, **kwargs):
         """
           Build control
@@ -74,26 +96,7 @@ class XhrBuildRequest(View):
             for i in request.POST['buildCancel'].strip().split(" "):
                 try:
                     br = BuildRequest.objects.get(project=project, pk=i)
-
-                    try:
-                        bbctrl = bbcontroller.BitbakeController(br.environment)
-                        bbctrl.forceShutDown()
-                    except:
-                        # We catch a bunch of exceptions here because
-                        # this is where the server has not had time to start up
-                        # and the build request or build is in transit between
-                        # processes.
-                        # We can safely just set the build as cancelled
-                        # already as it never got started
-                        build = br.build
-                        build.outcome = Build.CANCELLED
-                        build.save()
-
-                    # We now hand over to the buildinfohelper to update the
-                    # build state once we've finished cancelling
-                    br.state = BuildRequest.REQ_CANCELLING
-                    br.save()
-
+                    self.cancel_build(br)
                 except BuildRequest.DoesNotExist:
                     return error_response('No such build request id %s' % i)
 
@@ -823,11 +826,21 @@ class XhrProject(View):
         return HttpResponse()
 
     def delete(self, request, *args, **kwargs):
+        """Delete a project. Cancels any builds in progress"""
         try:
-            Project.objects.get(pk=kwargs['project_id']).delete()
+            project = Project.objects.get(pk=kwargs['project_id'])
+            # Cancel any builds in progress
+            for br in BuildRequest.objects.filter(
+                    project=project,
+                    state=BuildRequest.REQ_INPROGRESS):
+                XhrBuildRequest.cancel_build(br)
+
+            project.delete()
+
         except Project.DoesNotExist:
             return error_response("Project %s does not exist" %
                                   kwargs['project_id'])
+
         return JsonResponse({
             "error": "ok",
             "gotoUrl": reverse("all-projects", args=[])
lib/toaster/toastergui/templates/project.html +7 -5
index 7644dad..5abe241 100644
@@ -31,12 +31,14 @@
         <h4>Are you sure you want to delete this project?</h4>
       </div>
       <div class="modal-body">
-        <p>Deleting the <strong class="project-name"></strong> project will remove forever:</p>
+        <p>Deleting the <strong class="project-name"></strong> project
+        will:</p>
         <ul>
-          <li>Its configuration information</li>
-          <li>Its imported layers</li>
-          <li>Its custom images</li>
-          <li>All its build information</li>
+          <li>Cancel its builds currently in progress</li>
+          <li>Remove its configuration information</li>
+          <li>Remove its imported layers</li>
+          <li>Remove its custom images</li>
+          <li>Remove all its build information</li>
         </ul>
       </div>
       <div class="modal-footer">
Signature · 1 line
1.9.1
author
#2 lore raw

PATCH 2/6 toaster: buildinfohelper: Use correct way to get message from LogMessage

From: Michael Wood <[email protected]>

Use the correct method to get a message value from the LogMessage object rather than constructing it ourselves which is not recommended. This causes an exception when the msg contains a '%' such as when there are wildcards in file names (something2.%.bbappends)

---
 lib/bb/ui/buildinfohelper.py | 9 +++------
 1 file changed, 3 insertions(+), 6 deletions(-)
lib/bb/ui/buildinfohelper.py +3 -6
index 970a941..5b69660 100644
@@ -1565,16 +1565,12 @@ class BuildInfoHelper(object):
         mockevent.lineno = -1
         self.store_log_event(mockevent)
 
-
     def store_log_event(self, event):
         self._ensure_build()
 
         if event.levelno < formatter.WARNING:
             return
 
-        if 'args' in vars(event):
-            event.msg = event.msg % event.args
-
         # early return for CLI builds
         if self.brbe is None:
             if not 'backlog' in self.internal_state:
@@ -1586,7 +1582,8 @@ class BuildInfoHelper(object):
             # if we have a backlog of events, do our best to save them here
             if len(self.internal_state['backlog']):
                 tempevent = self.internal_state['backlog'].pop()
-                logger.debug(1, "buildinfohelper: Saving stored event %s " % tempevent)
+                logger.debug(1, "buildinfohelper: Saving stored event %s "
+                             % tempevent)
                 self.store_log_event(tempevent)
             else:
                 logger.info("buildinfohelper: All events saved")
@@ -1605,7 +1602,7 @@ class BuildInfoHelper(object):
         else:
             log_information['level'] = LogMessage.INFO
 
-        log_information['message'] = event.msg
+        log_information['message'] = event.getMessage()
         log_information['pathname'] = event.pathname
         log_information['lineno'] = event.lineno
         logger.info("Logging error 2: %s", log_information)
Signature · 1 line
1.9.1
author
#3 lore raw

PATCH 3/6 toaster: checksettings Remove confusing startup messages

In reply to bavery

From: Michael Wood <[email protected]>

These "validation" messages are shown regardless as to whether the settings are being correctly set or not.
For the time being remove them.

[YOCTO #9097]

---
 lib/toaster/bldcontrol/management/commands/checksettings.py | 2 --
 1 file changed, 2 deletions(-)
lib/toaster/bldcontrol/management/commands/checksettings.py +0 -2
index 28b9945..2ed994f 100644
@@ -42,7 +42,6 @@ class Command(NoArgsCommand):
                     return True
 
                 if len(be.sourcedir) == 0:
-                    print("\n -- Validation: The layers checkout directory must be set.")
                     is_changed = _update_sourcedir()
 
                 if not be.sourcedir.startswith("/"):
@@ -59,7 +58,6 @@ class Command(NoArgsCommand):
                     return True
 
                 if len(be.builddir) == 0:
-                    print("\n -- Validation: The build directory must be set.")
                     is_changed = _update_builddir()
 
                 if not be.builddir.startswith("/"):
Signature · 1 line
1.9.1
author
#4 lore raw

PATCH 4/6 toaster: importlayer Fix layer dependencies button state toggle

In reply to bavery

From: Michael Wood <[email protected]>

Fix regression introduced by switching typeahead library. Make sure we enable and disable the add button based on whether the selection event has fired or not.

[YOCTO #9936]

---
 lib/toaster/toastergui/static/js/importlayer.js | 30 +++++--------------------
 1 file changed, 5 insertions(+), 25 deletions(-)
lib/toaster/toastergui/static/js/importlayer.js +5 -25
index 570ca33..30dc282 100644
@@ -21,21 +21,11 @@ function importLayerPageInit (ctx) {
                            libtoaster.ctx.layersTypeAheadUrl,
                            { include_added: "true" }, function(item){
     currentLayerDepSelection = item;
+    layerDepBtn.removeAttr("disabled");
   });
 
-  // choices available in the typeahead
-  var layerDepsChoices = {};
-
-  // when the typeahead choices change, store an array of the available layer
-  // choices locally, to use for enabling/disabling the "Add layer" button
-  layerDepInput.on("typeahead-choices-change", function (event, data) {
-    layerDepsChoices = {};
-
-    if (data.choices) {
-      data.choices.forEach(function (item) {
-        layerDepsChoices[item.name] = item;
-      });
-    }
+  layerDepInput.on("typeahead:select", function(event, data){
+    currentLayerDepSelection = data;
   });
 
   // Disable local dir repo when page is loaded.
@@ -43,18 +33,8 @@ function importLayerPageInit (ctx) {
 
   // disable the "Add layer" button when the layer input typeahead is empty
   // or not in the typeahead choices
-  layerDepInput.on("input change", function () {
-    // get the choices from the typeahead
-    var choice = layerDepsChoices[$(this).val()];
-
-    if (choice) {
-      layerDepBtn.removeAttr("disabled");
-      currentLayerDepSelection = choice;
-    }
-    else {
-      layerDepBtn.attr("disabled","disabled");
-      currentLayerDepSelection = undefined;
-    }
+  layerDepInput.on("input change", function(){
+    layerDepBtn.attr("disabled","disabled");
   });
 
   /* We automatically add "openembedded-core" layer for convenience as a
Signature · 1 line
1.9.1
author
#5 lore raw

PATCH 5/6 toaster: Delete notification update front end implementation to design

In reply to bavery

From: Michael Wood <[email protected]>

Update the delete notifications to reflect feedback from design review comments.

---
 lib/toaster/toastergui/api.py                       | 2 +-
 lib/toaster/toastergui/static/js/layerdetails.js    | 2 ++
 lib/toaster/toastergui/static/js/libtoaster.js      | 2 +-
 lib/toaster/toastergui/templates/base.html          | 2 +-
 lib/toaster/toastergui/templates/basebuildpage.html | 6 +++++-
 5 files changed, 10 insertions(+), 4 deletions(-)
lib/toaster/toastergui/api.py +1 -1
index b01d4ba..ae1f150 100644
@@ -224,7 +224,7 @@ class XhrLayer(View):
 
         return JsonResponse({
             "error": "ok",
-            "gotoUrl": reverse('project', args=(kwargs['pid'],))
+            "gotoUrl": reverse('projectlayers', args=(kwargs['pid'],))
         })
 
 
lib/toaster/toastergui/static/js/layerdetails.js +2 -0
index 4c0d042..9ead393 100644
@@ -392,6 +392,8 @@ function layerDetailsPageInit (ctx) {
 
   $("#layer-delete-confirmed").click(function(){
 
+    $("#delete-layer-modal button[data-dismiss='modal']").hide();
+
     var message = $('<span>You have deleted <strong>1</strong> layer from your project: <strong id="deleted-layer-name"></strong>');
     message.find("#deleted-layer-name").text(ctx.layerVersion.name);
 
lib/toaster/toastergui/static/js/libtoaster.js +1 -1
index 0832ba4..86662b7 100644
@@ -342,7 +342,7 @@ var libtoaster = (function () {
   }
 
   function _showChangeNotification(message){
-    $(".alert").fadeOut().promise().done(function(){
+    $(".alert-dismissible").fadeOut().promise().done(function(){
       var alertMsg = $("#change-notification-msg");
 
       alertMsg.html(message);
lib/toaster/toastergui/templates/base.html +1 -1
index c1b1417..496dd6e 100644
@@ -74,7 +74,7 @@
     </div>
 
     <div id="change-notification" class="alert alert-info alert-dismissible change-notification" style="display:none">
-      <button type="button" class="close" id="hide-alert" data-dismiss="alert">&times;</button>
+      <button type="button" class="close" id="hide-alert" data-toggle="alert">&times;</button>
       <span id="change-notification-msg"></span>
     </div>
 
lib/toaster/toastergui/templates/basebuildpage.html +5 -1
index 0b6ef56..f5eee96 100644
@@ -28,6 +28,8 @@
             if (data.error !== "ok") {
               console.warn(data.error);
             } else {
+              libtoaster.setNotification("build-deleted",
+                $("#deleted-build-message").html());
              window.location.replace(data.gotoUrl);
             }
           },
@@ -61,7 +63,9 @@
   });
  </script>
 
-
+<span style="display:none" id="deleted-build-message">
+  You have deleted 1 build: <strong>{{build.get_sorted_target_list|field_values:"target"|join:", "}} {{build.machine}}</strong> completed on <strong>{{build.completed_on|date:"d/m/y H:i"}}</strong>
+</span>
 
 <div class="modal fade" tabindex="-1" role="dialog" id="delete-build-modal" style="display: none;" data-backdrop="static" data-keyboard="false">
   <div class="modal-dialog">
Signature · 1 line
1.9.1
author
#6 lore raw

PATCH 6/6 toaster: Update tests to reflect front end changes

In reply to bavery

From: Michael Wood <[email protected]>

- Browser test we changed the project heading access to use the class name
- Update toastergui unit test for additional gotoUrl property
- On faster browsers we had a race for layer details inputs being visible

---
 lib/toaster/tests/browser/test_layerdetails_page.py | 4 ++++
 lib/toaster/tests/browser/test_project_page.py      | 2 +-
 lib/toaster/toastergui/tests.py                     | 8 +++++++-
 3 files changed, 12 insertions(+), 2 deletions(-)
lib/toaster/tests/browser/test_layerdetails_page.py +4 -0
index 72b6f60..6392d1e 100644
@@ -91,6 +91,10 @@ class TestLayerDetailsPage(SeleniumTestCase):
         for btn in self.find_all("dd .glyphicon-edit"):
             btn.click()
 
+        # Wait for the inputs to become visible
+        self.wait_until_visible("#layer-git input[type=text]")
+        self.wait_until_visible("dd textarea")
+
         # Edit each value
         for inputs in self.find_all("#layer-git input[type=text]") + \
                 self.find_all("dd textarea"):
lib/toaster/tests/browser/test_project_page.py +1 -1
index 786bef1..0186463 100644
@@ -55,5 +55,5 @@ class TestProjectPage(SeleniumTestCase):
         self.get(url)
 
         # check that we get a project page with the correct heading
-        project_name = self.find('#project-name').text.strip()
+        project_name = self.find('.project-name').text.strip()
         self.assertEqual(project_name, self.CLI_BUILDS_PROJECT_NAME)
lib/toaster/toastergui/tests.py +7 -1
index 0508611..2b5894f 100644
@@ -248,7 +248,13 @@ class ViewTests(TestCase):
         url = reverse('xhr_customrecipe_id', args=(recipe.id,))
         response = self.client.delete(url)
         self.assertEqual(response.status_code, 200)
-        self.assertEqual(json.loads(response.content.decode('utf-8')), {"error": "ok"})
+
+        gotoUrl = reverse('projectcustomimages', args=(self.project.pk,))
+
+        self.assertEqual(json.loads(response.content.decode('utf-8')),
+                         {"error": "ok",
+                          "gotoUrl": gotoUrl})
+
         # try to delete not-existent recipe
         url = reverse('xhr_customrecipe_id', args=(recipe.id,))
         response = self.client.delete(url)
Signature · 1 line
1.9.1