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
1=== modified file 'checkbox-touch/components/CheckboxTouchApplication.qml'
2--- checkbox-touch/components/CheckboxTouchApplication.qml 2014-11-27 22:31:39 +0000
3+++ checkbox-touch/components/CheckboxTouchApplication.qml 2015-01-15 16:33:58 +0000
4@@ -1,10 +1,11 @@
5 /*
6 * This file is part of Checkbox
7 *
8- * Copyright 2014 Canonical Ltd.
9+ * Copyright 2014-2015 Canonical Ltd.
10 *
11 * Authors:
12 * - Zygmunt Krynicki <zygmunt.krynicki@canonical.com>
13+ * - Maciej Kisielewski <maciej.kisielewski@canonical.com>
14 *
15 * This program is free software; you can redistribute it and/or modify
16 * it under the terms of the GNU General Public License as published by
17@@ -81,6 +82,18 @@
18 });
19 }
20
21+ function getGarbageCollectionCandidates(continuation) {
22+ request("get_garbage_collection_candidates", [], continuation, function(error) {
23+ console.error("Unable to get garbage collection candidates");
24+ });
25+ }
26+
27+ function sessionGarbageCollect(flags, continuation) {
28+ request("session_garbage_collect", [flags], continuation, function(error) {
29+ console.error("Unable to run session garbage collection");
30+ });
31+ }
32+
33 function getTestplans(continuation) {
34 request("get_testplans", [], continuation, function(error) {
35 console.error("Unable to get testplans");
36
37=== modified file 'checkbox-touch/main.qml'
38--- checkbox-touch/main.qml 2014-12-11 22:27:53 +0000
39+++ checkbox-touch/main.qml 2015-01-15 16:33:58 +0000
40@@ -1,7 +1,7 @@
41 /*
42 * This file is part of Checkbox
43 *
44- * Copyright 2014 Canonical Ltd.
45+ * Copyright 2014-2015 Canonical Ltd.
46 *
47 * Authors:
48 * - Zygmunt Krynicki <zygmunt.krynicki@canonical.com>
49@@ -115,7 +115,59 @@
50 "checkbox_touch" : applicationVersion,
51 "plainbox" : plainboxVersion
52 };
53- resumeOrStartSession();
54+ getGarbageCollectionCandidates(function(oldStorages) {
55+ var buttons = [];
56+ var flagToButtonText = {
57+ "incomplete": i18n.tr("Remove incomplete sessions"),
58+ "submitted": i18n.tr("Remove submitted sessions"),
59+ }
60+ var candidateFlags = [];
61+ var totalCandidatesCount = 0;
62+ for (var flag in flagToButtonText) {
63+ if (oldStorages[flag] && oldStorages[flag].length > 0) {
64+ candidateFlags.push(flag);
65+ // The next bit is somewhat convoluted because we need to capture flag variable
66+ // see http://stackoverflow.com/questions/12930272/javascript-closures-vs-anonymous-functions
67+ // for full explanation of closures in JS
68+ var newButton = {
69+ "text": flagToButtonText[flag],
70+ "color": UbuntuColors.red,
71+ "onClicked": (function(flag) {
72+ return function() {
73+ console.log("Removing sessions marked with '"+ flag + "' flag");
74+ sessionGarbageCollect([flag], resumeOrStartSession);
75+ }
76+
77+ })(flag)};
78+ totalCandidatesCount += oldStorages[flag].length;
79+ buttons.push(newButton);
80+ }
81+ }
82+ if (buttons.length > 1) {
83+ //there is more than one button, so append 'remove all' button
84+ buttons.push({
85+ "text": i18n.tr("Remove all old sessions"),
86+ "color": UbuntuColors.red,
87+ "onClicked": function() {
88+ sessionGarbageCollect(candidateFlags, resumeOrStartSession);
89+ }
90+ });
91+ }
92+ if (buttons.length > 0) {
93+ buttons.push({
94+ "text": i18n.tr("Do not remove sessions"),
95+ "color": UbuntuColors.green,
96+ "onClicked": function() {
97+ sessionGarbageCollect([], resumeOrStartSession);
98+ }
99+ });
100+ CbtDialogLogic.showDialog(mainView, i18n.tr("Do you want to clear old sessions storage?\n"
101+ + " Number of sessions qualifying for removal: ") +
102+ totalCandidatesCount, buttons);
103+ } else {
104+ sessionGarbageCollect([], resumeOrStartSession);
105+ }
106+ });
107 }
108 onSessionReady: {
109 welcomePage.enableButton()
110
111=== modified file 'checkbox-touch/py/checkbox_touch.py'
112--- checkbox-touch/py/checkbox_touch.py 2015-01-07 14:52:42 +0000
113+++ checkbox-touch/py/checkbox_touch.py 2015-01-15 16:33:58 +0000
114@@ -1,6 +1,6 @@
115 # This file is part of Checkbox.
116 #
117-# Copyright 2014 Canonical Ltd.
118+# Copyright 2014-2015 Canonical Ltd.
119 # Written by:
120 # Zygmunt Krynicki <zygmunt.krynicki@canonical.com>
121 # Maciej Kisielewski <maciej.kisielewski@canonical.com>
122@@ -333,6 +333,7 @@
123 self.desired_test_ids = frozenset()
124 self.test_plan_id = ""
125 self.resume_candidate_storage = None
126+ self._garbage_collection_candidates = None
127 self.session_storage_repo = None
128 self.timestamp = datetime.datetime.utcnow().isoformat()
129
130@@ -465,6 +466,55 @@
131 }
132
133 @view
134+ def get_garbage_collection_candidates(self):
135+ """
136+ Returns a dict keyed by flag type with value being list of ids of all
137+ sessions available in the storage.
138+ """
139+ # every session *should* have at least one flag set (altough logically
140+ # those flags are mutally exclusive
141+ self._init_session_storage_repo()
142+ self._garbage_collection_candidates = collections.defaultdict(list)
143+ for storage in self.session_storage_repo.get_storage_list():
144+ data = storage.load_checkpoint()
145+ if len(data) == 0:
146+ continue
147+ try:
148+ metadata = SessionPeekHelper().peek(data)
149+ if (metadata.app_id == 'checkbox-touch'):
150+ _logger.debug(_("Found an existing session storage: %s"),
151+ storage.id)
152+ # some sessions might be appended to more than one list
153+ for flag in metadata.flags:
154+ self._garbage_collection_candidates[flag].append(
155+ storage)
156+ except SessionResumeError as exc:
157+ _logger.info(_("Exception raised when trying to gather garbage"
158+ " collection candidates: %s"), str(exc))
159+ return { flag: [storage.id for storage in storages] for
160+ flag, storages in self._garbage_collection_candidates.items()}
161+ @view
162+ def session_garbage_collect(self, flags):
163+ """
164+ Removes all session with any flag matching ``flags`` or having
165+ 'bootstrapping' flag set.
166+ """
167+ if self._garbage_collection_candidates is None:
168+ _logger.warning(_("session_garbage_collect called before "
169+ "get_garbage_collection_candidates"))
170+ return
171+ # if there are candidates with 'bootstrapping', remove them
172+ if 'bootstrapping' in self._garbage_collection_candidates.keys():
173+ flags.append('bootstrapping')
174+ # we build a set of sessions marked for removal so we don't try to
175+ # re-remove any of them
176+ to_remove = set()
177+ for flag in flags:
178+ to_remove |= set(self._garbage_collection_candidates[flag])
179+ while to_remove:
180+ to_remove.pop().remove()
181+
182+ @view
183 def get_testplans(self):
184 all_units = self.manager.default_device_context.unit_list
185 return {
186@@ -576,6 +626,7 @@
187 self.context.state.update_desired_job_list(desired_job_list)
188 _logger.info("Run job list: %s", self.context.state.run_list)
189 self.context.state.metadata.flags.add('incomplete')
190+ self.context.state.metadata.flags.remove('bootstrapping')
191 self._checkpoint()
192
193 @view
194@@ -680,6 +731,8 @@
195 output_file = os.path.join(dirname, filename)
196 with open(output_file, 'wb') as stream:
197 self._export_session_to_stream(output_format, option_list, stream)
198+ self.context.state.metadata.flags.add(SessionMetaData.FLAG_SUBMITTED)
199+ self._checkpoint()
200 return output_file
201
202 def _get_user_directory_documents(self):

Subscribers

People subscribed via source and target branches