Merge lp:~kissiel/checkbox/fix-1397109 into lp:checkbox

Proposed by Maciej Kisielewski
Status: Rejected
Rejected by: Maciej Kisielewski
Proposed branch: lp:~kissiel/checkbox/fix-1397109
Merge into: lp:checkbox
Diff against target: 202 lines (+122/-4)
3 files modified
checkbox-touch/components/CheckboxTouchApplication.qml (+14/-1)
checkbox-touch/main.qml (+54/-2)
checkbox-touch/py/checkbox_touch.py (+54/-1)
To merge this branch: bzr merge lp:~kissiel/checkbox/fix-1397109
Reviewer Review Type Date Requested Status
Zygmunt Krynicki (community) Needs Information
Review via email: mp+243503@code.launchpad.net

Description of the change

This MR brings garbage collecting of old session storage.

57d9d66 checkbox-touch: make exporting of results mark session as 'submitted'
1e65a00 checkbox-touch: add garbage collection of old session storage on start-up
10c39de checkbox-touch: add removal of 'bootstrapping' flag

To post a comment you must log in.
Revision history for this message
Zygmunt Krynicki (zyga) wrote :

10:24 < kissiel> zyga, https://code.launchpad.net/~kissiel/checkbox/fix-1397109/+merge/243503 :)
10:25 <@zyga> kissiel: hmm, interesting
10:26 <@zyga> kissiel: I think it might be useful to keep those around indefinitely, I would certainly not remove them without first informing the
              user or let them choose
10:26 <@zyga> kissiel: the code itself looks okay
10:26 <@zyga> kissiel: load_checkpoint can raise exceptions
10:26 <@zyga> kissiel: IIRC
10:27 <@zyga> kissiel: I'd move the try/catch so that it covers only the .peek() call
10:27 <@zyga> kissiel: and move the garbage_flags set outside the loop
10:27 <@zyga> kissiel: intersection can be *probably* spelled as & for sets
10:27 <@zyga> kissiel: you may want to remove stuff you cannot peek either
10:28 < kissiel> zyga, I thought of that
10:28 < kissiel> zyga, but this would mean, that broken sessions are lost
10:28 < kissiel> zyga, and we/some1 might want to recover those
10:29 <@zyga> kissiel: yes, that's a good point, though some of those sessions are not broken (incompatible job error)
10:29 <@zyga> kissiel: just no longer resumable
10:29 <@zyga> ...
10:29 <@zyga> though
10:29 <@zyga> I'm wrong
10:29 <@zyga> peek doens't have that
10:29 <@zyga> only stuff like broken json/zip can kill peek
10:30 <@zyga> kissiel: oh, and missing i18n :D
10:30 <@zyga> kissiel: let's iterate and we'll se
10:30 <@zyga> see
10:30 < kissiel> zyga, cool, tahnks

Revision history for this message
Sylvain Pineau (sylvain-pineau) wrote :

kissiel: spineau, there's also https://code.launchpad.net/~kissiel/checkbox/fix-1397109/+merge/243503 laying around for a while
spineau: kissiel: indeed, one question/suggestion for this one. Suppose that the tester has to resume x times cbt to complete a single testrun, I think I gonna be annoying to see the popup when resuming and decline the removal.
spineau: kissiel: maybe we should make this a Header action available in the first screen(s)
kissiel: spineau, hmm, garbage collection is for sessions that cannot be removed
kissiel: spineau, so if they are only resuming they shouldn't see the popup
kissiel: spineau, if I understood your scenario correctly
spineau: kissiel: ok, so we'll only popup when the last session is completed
kissiel: spineau, yep
spineau: kissiel: could you help me a bit to understand what types of sessions we could find in the session storage folder?
spineau: kissiel: bootstrapped/uncompleted/?
kissiel: spineau, sure, 1) bootstrapping, session storage created, but test haven't been selected yet
spineau: kissiel: and which ones will be removed by the new function?
kissiel: spineau, so there's nothing to resume, cause we don't have list of test to run
kissiel: spineau, so it's 'garbage'
spineau: ok
kissiel: spineau, 2) uncompleted sessions are the one that have list of test associated with them and are potential candidates for resume
kissiel: spineau, we do not want to remove those when doing GC
spineau: kissiel: and we can have many of them potentially
kissiel: spineau, and finally 3) submitted - there's nothing to resume, cause all test were run and results were saved/submitted, ergo 'garbage'
kissiel: spineau, yep, there might be multiple uncompleted sessions
kissiel: spineau, as we have in cli
spineau: kissiel: but cbt is a single session client so far, so only the last one is resumable right. So keeping uncompleted sessions is not really used today
kissiel: spineau, they are stacking, so if you abandon session (ID: a) start a new one (ID:B), stop, and then rerun you will have an option of resuming B, after this is done, you'll still be able to run A (AFAIK)
spineau: kissiel: but no methods are there to remove session A (uncompleted)
kissiel: spineau, indeed. I think we should have some explicit flag to mark those sessions and garbage collect them... Maybe ABANDONED flag?
spineau: kissiel: maybe. Actually I would automatically GC the "bootstrapping" sessions without user confirmation and offer a menu action for uncompleted/saved
kissiel: spineau, cool, that's easily doable
spineau: kissiel: even two distinct actions, one for resumable sessions and one another for saved sessions as the tester may wnat to keep the io logs on the device even for saved sessions

Revision history for this message
Zygmunt Krynicki (zyga) wrote :

This is mostly okay, I have one question below.

Oh, and perhaps, can we have unit tests for the python part (just have a look, maybe it's easy, maybe not)?

review: Needs Information
Revision history for this message
Maciej Kisielewski (kissiel) wrote :

Unmerged revisions

3521. By Maciej Kisielewski

checkbox-touch: fix re-removal of session when doing GC

This patch makes checkbox-touch remove garbage collected sessions only once.
The problem with re-removal manifested when session had more than one flag set
(and more than one flag was matching to-be-removed flags)

Signed-off-by: Maciej Kisielewski <email address hidden>

3520. By Maciej Kisielewski

checkbox-touch: file header date bumps

Signed-off-by: Maciej Kisielewski <email address hidden>

3519. By Maciej Kisielewski

checkbox-touch: qml changes for new session garbage collection

This patch changes how session garbage collection is run in checkbox-touch.
This patch includes changes to qml side of things.

Signed-off-by: Maciej Kisielewski <email address hidden>

3518. By Maciej Kisielewski

checkbox-touch: new way of doing session garbage collection

This patch changes how session garbage collection is run in checkbox-touch.
This patch includes changes to python side of things.

Signed-off-by: Maciej Kisielewski <email address hidden>

3517. By Maciej Kisielewski

checkbox-touch: add flags parameter to python interface

Signed-off-by: Maciej Kisielewski <email address hidden>

3516. By Maciej Kisielewski

checkbox-touch: ask user if they want to run session storage clearing

This patch makes CBT show dialog when stale sesssions are found in the
filesystem.

Signed-off-by: Maciej Kisielewski <email address hidden>

3515. By Maciej Kisielewski

checkbox-touch: fixes after Zygmunt's review

Signed-off-by: Maciej Kisielewski <email address hidden>

3514. By Maciej Kisielewski

checkbox-touch: make exporting of results mark session as 'submitted'

Signed-off-by: Maciej Kisielewski <email address hidden>

3513. By Maciej Kisielewski

checkbox-touch: add garbage collection of old session storage on start-up

This patch makes Checkbox Touch remove old session storage when launching the
App.

Fixes: https://bugs.launchpad.net/checkbox-touch/+bug/1397109
Signed-off-by: Maciej Kisielewski <email address hidden>

3512. By Maciej Kisielewski

checkbox-touch: add removal of 'bootstrapping' flag

This patch makes Checkbox Touch remove 'bootstrapping' flag once user selects
test to run.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'checkbox-touch/components/CheckboxTouchApplication.qml'
--- checkbox-touch/components/CheckboxTouchApplication.qml 2014-11-27 22:31:39 +0000
+++ checkbox-touch/components/CheckboxTouchApplication.qml 2015-01-15 16:33:58 +0000
@@ -1,10 +1,11 @@
1/*1/*
2 * This file is part of Checkbox2 * This file is part of Checkbox
3 *3 *
4 * Copyright 2014 Canonical Ltd.4 * Copyright 2014-2015 Canonical Ltd.
5 *5 *
6 * Authors:6 * Authors:
7 * - Zygmunt Krynicki <zygmunt.krynicki@canonical.com>7 * - Zygmunt Krynicki <zygmunt.krynicki@canonical.com>
8 * - Maciej Kisielewski <maciej.kisielewski@canonical.com>
8 *9 *
9 * This program is free software; you can redistribute it and/or modify10 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by11 * it under the terms of the GNU General Public License as published by
@@ -81,6 +82,18 @@
81 });82 });
82 }83 }
8384
85 function getGarbageCollectionCandidates(continuation) {
86 request("get_garbage_collection_candidates", [], continuation, function(error) {
87 console.error("Unable to get garbage collection candidates");
88 });
89 }
90
91 function sessionGarbageCollect(flags, continuation) {
92 request("session_garbage_collect", [flags], continuation, function(error) {
93 console.error("Unable to run session garbage collection");
94 });
95 }
96
84 function getTestplans(continuation) {97 function getTestplans(continuation) {
85 request("get_testplans", [], continuation, function(error) {98 request("get_testplans", [], continuation, function(error) {
86 console.error("Unable to get testplans");99 console.error("Unable to get testplans");
87100
=== modified file 'checkbox-touch/main.qml'
--- checkbox-touch/main.qml 2014-12-11 22:27:53 +0000
+++ checkbox-touch/main.qml 2015-01-15 16:33:58 +0000
@@ -1,7 +1,7 @@
1/*1/*
2 * This file is part of Checkbox2 * This file is part of Checkbox
3 *3 *
4 * Copyright 2014 Canonical Ltd.4 * Copyright 2014-2015 Canonical Ltd.
5 *5 *
6 * Authors:6 * Authors:
7 * - Zygmunt Krynicki <zygmunt.krynicki@canonical.com>7 * - Zygmunt Krynicki <zygmunt.krynicki@canonical.com>
@@ -115,7 +115,59 @@
115 "checkbox_touch" : applicationVersion,115 "checkbox_touch" : applicationVersion,
116 "plainbox" : plainboxVersion116 "plainbox" : plainboxVersion
117 };117 };
118 resumeOrStartSession();118 getGarbageCollectionCandidates(function(oldStorages) {
119 var buttons = [];
120 var flagToButtonText = {
121 "incomplete": i18n.tr("Remove incomplete sessions"),
122 "submitted": i18n.tr("Remove submitted sessions"),
123 }
124 var candidateFlags = [];
125 var totalCandidatesCount = 0;
126 for (var flag in flagToButtonText) {
127 if (oldStorages[flag] && oldStorages[flag].length > 0) {
128 candidateFlags.push(flag);
129 // The next bit is somewhat convoluted because we need to capture flag variable
130 // see http://stackoverflow.com/questions/12930272/javascript-closures-vs-anonymous-functions
131 // for full explanation of closures in JS
132 var newButton = {
133 "text": flagToButtonText[flag],
134 "color": UbuntuColors.red,
135 "onClicked": (function(flag) {
136 return function() {
137 console.log("Removing sessions marked with '"+ flag + "' flag");
138 sessionGarbageCollect([flag], resumeOrStartSession);
139 }
140
141 })(flag)};
142 totalCandidatesCount += oldStorages[flag].length;
143 buttons.push(newButton);
144 }
145 }
146 if (buttons.length > 1) {
147 //there is more than one button, so append 'remove all' button
148 buttons.push({
149 "text": i18n.tr("Remove all old sessions"),
150 "color": UbuntuColors.red,
151 "onClicked": function() {
152 sessionGarbageCollect(candidateFlags, resumeOrStartSession);
153 }
154 });
155 }
156 if (buttons.length > 0) {
157 buttons.push({
158 "text": i18n.tr("Do not remove sessions"),
159 "color": UbuntuColors.green,
160 "onClicked": function() {
161 sessionGarbageCollect([], resumeOrStartSession);
162 }
163 });
164 CbtDialogLogic.showDialog(mainView, i18n.tr("Do you want to clear old sessions storage?\n"
165 + " Number of sessions qualifying for removal: ") +
166 totalCandidatesCount, buttons);
167 } else {
168 sessionGarbageCollect([], resumeOrStartSession);
169 }
170 });
119 }171 }
120 onSessionReady: {172 onSessionReady: {
121 welcomePage.enableButton()173 welcomePage.enableButton()
122174
=== modified file 'checkbox-touch/py/checkbox_touch.py'
--- checkbox-touch/py/checkbox_touch.py 2015-01-07 14:52:42 +0000
+++ checkbox-touch/py/checkbox_touch.py 2015-01-15 16:33:58 +0000
@@ -1,6 +1,6 @@
1# This file is part of Checkbox.1# This file is part of Checkbox.
2#2#
3# Copyright 2014 Canonical Ltd.3# Copyright 2014-2015 Canonical Ltd.
4# Written by:4# Written by:
5# Zygmunt Krynicki <zygmunt.krynicki@canonical.com>5# Zygmunt Krynicki <zygmunt.krynicki@canonical.com>
6# Maciej Kisielewski <maciej.kisielewski@canonical.com>6# Maciej Kisielewski <maciej.kisielewski@canonical.com>
@@ -333,6 +333,7 @@
333 self.desired_test_ids = frozenset()333 self.desired_test_ids = frozenset()
334 self.test_plan_id = ""334 self.test_plan_id = ""
335 self.resume_candidate_storage = None335 self.resume_candidate_storage = None
336 self._garbage_collection_candidates = None
336 self.session_storage_repo = None337 self.session_storage_repo = None
337 self.timestamp = datetime.datetime.utcnow().isoformat()338 self.timestamp = datetime.datetime.utcnow().isoformat()
338339
@@ -465,6 +466,55 @@
465 }466 }
466467
467 @view468 @view
469 def get_garbage_collection_candidates(self):
470 """
471 Returns a dict keyed by flag type with value being list of ids of all
472 sessions available in the storage.
473 """
474 # every session *should* have at least one flag set (altough logically
475 # those flags are mutally exclusive
476 self._init_session_storage_repo()
477 self._garbage_collection_candidates = collections.defaultdict(list)
478 for storage in self.session_storage_repo.get_storage_list():
479 data = storage.load_checkpoint()
480 if len(data) == 0:
481 continue
482 try:
483 metadata = SessionPeekHelper().peek(data)
484 if (metadata.app_id == 'checkbox-touch'):
485 _logger.debug(_("Found an existing session storage: %s"),
486 storage.id)
487 # some sessions might be appended to more than one list
488 for flag in metadata.flags:
489 self._garbage_collection_candidates[flag].append(
490 storage)
491 except SessionResumeError as exc:
492 _logger.info(_("Exception raised when trying to gather garbage"
493 " collection candidates: %s"), str(exc))
494 return { flag: [storage.id for storage in storages] for
495 flag, storages in self._garbage_collection_candidates.items()}
496 @view
497 def session_garbage_collect(self, flags):
498 """
499 Removes all session with any flag matching ``flags`` or having
500 'bootstrapping' flag set.
501 """
502 if self._garbage_collection_candidates is None:
503 _logger.warning(_("session_garbage_collect called before "
504 "get_garbage_collection_candidates"))
505 return
506 # if there are candidates with 'bootstrapping', remove them
507 if 'bootstrapping' in self._garbage_collection_candidates.keys():
508 flags.append('bootstrapping')
509 # we build a set of sessions marked for removal so we don't try to
510 # re-remove any of them
511 to_remove = set()
512 for flag in flags:
513 to_remove |= set(self._garbage_collection_candidates[flag])
514 while to_remove:
515 to_remove.pop().remove()
516
517 @view
468 def get_testplans(self):518 def get_testplans(self):
469 all_units = self.manager.default_device_context.unit_list519 all_units = self.manager.default_device_context.unit_list
470 return {520 return {
@@ -576,6 +626,7 @@
576 self.context.state.update_desired_job_list(desired_job_list)626 self.context.state.update_desired_job_list(desired_job_list)
577 _logger.info("Run job list: %s", self.context.state.run_list)627 _logger.info("Run job list: %s", self.context.state.run_list)
578 self.context.state.metadata.flags.add('incomplete')628 self.context.state.metadata.flags.add('incomplete')
629 self.context.state.metadata.flags.remove('bootstrapping')
579 self._checkpoint()630 self._checkpoint()
580631
581 @view632 @view
@@ -680,6 +731,8 @@
680 output_file = os.path.join(dirname, filename)731 output_file = os.path.join(dirname, filename)
681 with open(output_file, 'wb') as stream:732 with open(output_file, 'wb') as stream:
682 self._export_session_to_stream(output_format, option_list, stream)733 self._export_session_to_stream(output_format, option_list, stream)
734 self.context.state.metadata.flags.add(SessionMetaData.FLAG_SUBMITTED)
735 self._checkpoint()
683 return output_file736 return output_file
684737
685 def _get_user_directory_documents(self):738 def _get_user_directory_documents(self):

Subscribers

People subscribed via source and target branches