Merge ~seb128/software-properties:ubuntu-pro-focal into ~robert-ancell/software-properties:ubuntu-pro-focal
- Git
- lp:~seb128/software-properties
- ubuntu-pro-focal
- Merge into ubuntu-pro-focal
Proposed by
Sebastien Bacher
Status: | Merged |
---|---|
Merged at revision: | 0dd69a30987b34691c153aa9cab2d4b6bc31901c |
Proposed branch: | ~seb128/software-properties:ubuntu-pro-focal |
Merge into: | ~robert-ancell/software-properties:ubuntu-pro-focal |
Diff against target: |
514 lines (+280/-101) 6 files modified
data/gtkbuilder/dialog-ua-attach.ui (+151/-75) data/gtkbuilder/main.ui (+1/-1) debian/changelog (+6/-0) debian/control (+1/-0) softwareproperties/SoftwareProperties.py (+2/-1) softwareproperties/gtk/DialogUaAttach.py (+119/-24) |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Robert Ancell | Pending | ||
Review via email: mp+434791@code.launchpad.net |
Commit message
Description of the change
Updated version include the work for Nathan on the magic attach workflow
Uploaded also to https:/
To post a comment you must log in.
Revision history for this message
Sebastien Bacher (seb128) wrote : | # |
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | diff --git a/data/gtkbuilder/dialog-ua-attach.ui b/data/gtkbuilder/dialog-ua-attach.ui |
2 | index 3ae3b1b..8563e86 100644 |
3 | --- a/data/gtkbuilder/dialog-ua-attach.ui |
4 | +++ b/data/gtkbuilder/dialog-ua-attach.ui |
5 | @@ -1,119 +1,195 @@ |
6 | -<?xml version="1.0"?> |
7 | +<?xml version="1.0" encoding="UTF-8"?> |
8 | <interface> |
9 | + <requires lib="gtk+" version="3.16"/> |
10 | <object class="GtkDialog" id="dialog_ua_attach"> |
11 | - <property name="border_width">18</property> |
12 | + <property name="can-focus">False</property> |
13 | + <property name="border-width">18</property> |
14 | <property name="title" translatable="yes">Enable Ubuntu Pro</property> |
15 | <property name="resizable">False</property> |
16 | <property name="modal">True</property> |
17 | - <property name="type_hint">dialog</property> |
18 | - <property name="skip_taskbar_hint">True</property> |
19 | + <property name="type-hint">dialog</property> |
20 | + <property name="skip-taskbar-hint">True</property> |
21 | + <!-- interface-requires gtk+ 3.0 --> |
22 | <child internal-child="vbox"> |
23 | <object class="GtkBox"> |
24 | + <property name="visible">True</property> |
25 | + <property name="can-focus">False</property> |
26 | + <property name="no-show-all">True</property> |
27 | <property name="orientation">vertical</property> |
28 | - <property name="spacing">18</property> |
29 | + <child> |
30 | + <object class="GtkLabel"> |
31 | + <property name="visible">True</property> |
32 | + <property name="can-focus">True</property> |
33 | + <property name="label" translatable="yes">To upgrade to Ubuntu Pro, use your existing free personal, or company Ubuntu One account, or provide a token. <a href="https://ubuntu.com/login">Register a new account</a>.</property> |
34 | + <property name="use-markup">True</property> |
35 | + <property name="wrap">True</property> |
36 | + <property name="max-width-chars">130</property> |
37 | + </object> |
38 | + </child> |
39 | + <child> |
40 | + <object class="GtkRadioButton" id="magic_radio"> |
41 | + <property name="label" translatable="yes">Log in with Ubuntu One</property> |
42 | + <property name="visible">True</property> |
43 | + <property name="can-focus">True</property> |
44 | + <property name="receives-default">False</property> |
45 | + <property name="margin-top">30</property> |
46 | + <property name="xalign">0</property> |
47 | + <property name="group">magic_radio</property> |
48 | + <signal name="toggled" handler="on_radio_toggled" swapped="no"/> |
49 | + <signal name="clicked" handler="on_magic_radio_clicked" swapped="no"/> |
50 | + </object> |
51 | + </child> |
52 | <child> |
53 | <object class="GtkBox"> |
54 | <property name="visible">True</property> |
55 | - <property name="orientation">vertical</property> |
56 | - <property name="spacing">18</property> |
57 | + <property name="can-focus">False</property> |
58 | + <property name="orientation">horizontal</property> |
59 | <child> |
60 | - <object class="GtkLabel"> |
61 | + <object class="GtkBox" id="pin_label_box"> |
62 | <property name="visible">True</property> |
63 | - <property name="label" translatable="yes">Enter your Ubuntu Pro token here. |
64 | -From <a href="https://ubuntu.com/pro">ubuntu.com/pro</a> or your system administrator</property> |
65 | - <property name="use_markup">True</property> |
66 | - <property name="xalign">0</property> |
67 | + <property name="margin-left">20</property> |
68 | + <property name="margin-top">12</property> |
69 | + <property name="margin-bottom">8</property> |
70 | + <property name="can-focus">False</property> |
71 | + <child> |
72 | + <object class="GtkLabel" id="pin_label"> |
73 | + <property name="label"> </property> |
74 | + <property name="visible">True</property> |
75 | + <property name="selectable">True</property> |
76 | + <property name="can-focus">False</property> |
77 | + <property name="halign">start</property> |
78 | + <property name="margin-left">6</property> |
79 | + <property name="margin-right">6</property> |
80 | + <property name="margin-top">4</property> |
81 | + <property name="margin-bottom">4</property> |
82 | + <attributes> |
83 | + <attribute name="scale" value="2"/> |
84 | + </attributes> |
85 | + </object> |
86 | + </child> |
87 | </object> |
88 | </child> |
89 | <child> |
90 | - <object class="GtkGrid"> |
91 | + <object class="GtkFixed"> |
92 | <property name="visible">True</property> |
93 | - <property name="row_spacing">6</property> |
94 | - <property name="column_spacing">12</property> |
95 | + <property name="valign">center</property> |
96 | + <property name="margin">3</property> |
97 | <child> |
98 | - <object class="GtkLabel"> |
99 | + <object class="GtkSpinner" id="pin_spinner"> |
100 | <property name="visible">True</property> |
101 | - <property name="label" translatable="yes">Token:</property> |
102 | + <property name="valign">center</property> |
103 | </object> |
104 | - <packing> |
105 | - <property name="left_attach">0</property> |
106 | - <property name="top_attach">0</property> |
107 | - </packing> |
108 | </child> |
109 | <child> |
110 | - <object class="GtkBox"> |
111 | - <property name="visible">True</property> |
112 | - <property name="spacing">12</property> |
113 | - <child> |
114 | - <object class="GtkEntry" id="entry_token"> |
115 | - <property name="visible">True</property> |
116 | - <property name="max-width-chars">30</property> |
117 | - <signal name="changed" handler="on_token_entry_changed"/> |
118 | - <signal name="activate" handler="on_token_entry_activate"/> |
119 | - </object> |
120 | - </child> |
121 | - <child> |
122 | - <object class="GtkSpinner" id="spinner"> |
123 | - <property name="visible">True</property> |
124 | - </object> |
125 | - </child> |
126 | + <object class="GtkImage" id="pin_status_icon"> |
127 | + <property name="visible">False</property> |
128 | + <property name="valign">center</property> |
129 | </object> |
130 | - <packing> |
131 | - <property name="left_attach">1</property> |
132 | - <property name="top_attach">0</property> |
133 | - </packing> |
134 | </child> |
135 | + </object> |
136 | + </child> |
137 | + <child> |
138 | + <object class="GtkLabel" id="pin_status"> |
139 | + <property name="margin">3</property> |
140 | + <property name="visible">True</property> |
141 | + <property name="valign">center</property> |
142 | + <property name="use-markup">True</property> |
143 | + </object> |
144 | + </child> |
145 | + </object> |
146 | + </child> |
147 | + <child> |
148 | + <object class="GtkRadioButton" id="token_radio"> |
149 | + <property name="label" translatable="yes">Or add token manually</property> |
150 | + <property name="visible">True</property> |
151 | + <property name="can-focus">True</property> |
152 | + <property name="receives-default">False</property> |
153 | + <property name="margin-top">6</property> |
154 | + <property name="xalign">0</property> |
155 | + <property name="group">magic_radio</property> |
156 | + <signal name="toggled" handler="on_radio_toggled" swapped="no"/> |
157 | + </object> |
158 | + </child> |
159 | + <child> |
160 | + <object class="GtkBox"> |
161 | + <property name="visible">True</property> |
162 | + <property name="can-focus">False</property> |
163 | + <property name="no-show-all">True</property> |
164 | + <child> |
165 | + <object class="GtkEntry" id="token_field"> |
166 | + <property name="visible">True</property> |
167 | + <property name="sensitive">False</property> |
168 | + <property name="can-focus">True</property> |
169 | + <property name="margin-left">20</property> |
170 | + <property name="margin-top">12</property> |
171 | + <property name="margin-bottom">12</property> |
172 | + <property name="margin-right">4</property> |
173 | + <property name="width-chars">35</property> |
174 | + <property name="placeholder-text" translatable="yes">Token</property> |
175 | + <property name="halign">start</property> |
176 | + <signal name="activate" handler="on_token_entry_activate" swapped="no"/> |
177 | + <signal name="changed" handler="on_token_typing" swapped="no"/> |
178 | + </object> |
179 | + </child> |
180 | + <child> |
181 | + <object class="GtkFixed"> |
182 | + <property name="visible">True</property> |
183 | + <property name="valign">center</property> |
184 | + <property name="margin">3</property> |
185 | <child> |
186 | - <object class="GtkLabel"> |
187 | + <object class="GtkSpinner" id="token_spinner"> |
188 | <property name="visible">True</property> |
189 | - <property name="xalign">0</property> |
190 | - <property name="label" translatable="yes"><a href="https://login.ubuntu.com">Don't have a token yet? Register</a></property> |
191 | - <property name="use_markup">True</property> |
192 | + <property name="valign">center</property> |
193 | </object> |
194 | - <packing> |
195 | - <property name="left_attach">1</property> |
196 | - <property name="top_attach">1</property> |
197 | - </packing> |
198 | </child> |
199 | <child> |
200 | - <object class="GtkLabel" id="label_attach_error"> |
201 | - <property name="visible">True</property> |
202 | - <property name="xalign">0</property> |
203 | - <attributes> |
204 | - <attribute name="foreground" value="red"/> |
205 | - <attribute name="scale" value="0.9"/> |
206 | - </attributes> |
207 | + <object class="GtkImage" id="token_status_icon"> |
208 | + <property name="visible">False</property> |
209 | + <property name="valign">center</property> |
210 | </object> |
211 | - <packing> |
212 | - <property name="left_attach">1</property> |
213 | - <property name="top_attach">2</property> |
214 | - </packing> |
215 | </child> |
216 | </object> |
217 | </child> |
218 | + <child> |
219 | + <object class="GtkLabel" id="token_status"> |
220 | + <property name="margin">3</property> |
221 | + <property name="visible">True</property> |
222 | + <property name="valign">center</property> |
223 | + <property name="use-markup">True</property> |
224 | + </object> |
225 | + </child> |
226 | </object> |
227 | </child> |
228 | - </object> |
229 | - </child> |
230 | - <child internal-child="action_area"> |
231 | - <object class="GtkHButtonBox"> |
232 | - <property name="visible">True</property> |
233 | - <property name="layout_style">end</property> |
234 | <child> |
235 | - <object class="GtkButton"> |
236 | + <object class="GtkLabel"> |
237 | <property name="visible">True</property> |
238 | - <property name="label" translatable="yes">Cance_l</property> |
239 | - <property name="use_underline">True</property> |
240 | - <signal name="clicked" handler="on_cancel_clicked"/> |
241 | + <property name="halign">start</property> |
242 | + <property name="margin-left">20</property> |
243 | + <property name="use-markup">True</property> |
244 | + <property name="label" translatable="yes">From your admin, or from <a href="https://ubuntu.com/pro">ubuntu.com/pro</a></property> |
245 | </object> |
246 | </child> |
247 | <child> |
248 | - <object class="GtkButton" id="button_attach"> |
249 | + <object class="GtkBox"> |
250 | <property name="visible">True</property> |
251 | - <property name="sensitive">False</property> |
252 | - <property name="label" translatable="yes">_Continue</property> |
253 | - <property name="use_underline">True</property> |
254 | - <signal name="clicked" handler="on_attach_clicked"/> |
255 | + <property name="orientation">horizontal</property> |
256 | + <property name="halign">end</property> |
257 | + <child> |
258 | + <object class="GtkButton" id="cancel"> |
259 | + <property name="visible">True</property> |
260 | + <property name="margin-right">10</property> |
261 | + <property name="label" translatable="yes">Cancel</property> |
262 | + <signal name="clicked" handler="on_cancel_clicked" swapped="no"/> |
263 | + </object> |
264 | + </child> |
265 | + <child> |
266 | + <object class="GtkButton" id="confirm"> |
267 | + <property name="visible">True</property> |
268 | + <property name="label" translatable="yes">Confirm</property> |
269 | + <property name="sensitive">False</property> |
270 | + <signal name="clicked" handler="on_confirm_clicked" swapped="no"/> |
271 | + </object> |
272 | + </child> |
273 | </object> |
274 | </child> |
275 | </object> |
276 | diff --git a/data/gtkbuilder/main.ui b/data/gtkbuilder/main.ui |
277 | index a78bfe2..1cff6cf 100644 |
278 | --- a/data/gtkbuilder/main.ui |
279 | +++ b/data/gtkbuilder/main.ui |
280 | @@ -1285,7 +1285,7 @@ |
281 | <object class="GtkLabel"> |
282 | <property name="visible">True</property> |
283 | <property name="label" translatable="yes"><b>This machine is not covered by an Ubuntu Pro subscription.</b> |
284 | -Receive security updates for over 25,000 Ubuntu packages, on up to 3 machines free for personal use. <a href="https://ubuntu.com/pro">Learn more</a>.</property> |
285 | +Receive security updates for over 25,000 Ubuntu packages, free for up to 5 machines. <a href="https://ubuntu.com/pro">Learn more</a>.</property> |
286 | <property name="use_markup">True</property> |
287 | <property name="wrap">True</property> |
288 | <property name="max-width-chars">90</property> |
289 | diff --git a/debian/changelog b/debian/changelog |
290 | index 2be2114..0a94e7e 100644 |
291 | --- a/debian/changelog |
292 | +++ b/debian/changelog |
293 | @@ -1,3 +1,9 @@ |
294 | +software-properties (0.99.9.8+ubuntupro13.1) focal; urgency=medium |
295 | + |
296 | + * Include the magic attach workflow, thanks Nathan! |
297 | + |
298 | + -- Sebastien Bacher <seb128@ubuntu.com> Fri, 16 Dec 2022 11:20:29 +0100 |
299 | + |
300 | software-properties (0.99.9.8+ubuntupro12) focal; urgency=medium |
301 | |
302 | * Show Ubuntu Pro settings. |
303 | diff --git a/debian/control b/debian/control |
304 | index 7edc838..65227f5 100644 |
305 | --- a/debian/control |
306 | +++ b/debian/control |
307 | @@ -76,6 +76,7 @@ Description: manage the repositories that you install software from (common) |
308 | Package: software-properties-gtk |
309 | Architecture: all |
310 | Depends: ubuntu-advantage-desktop-daemon, |
311 | + ubuntu-advantage-tools (>= 27.11~), |
312 | gir1.2-gtk-3.0, |
313 | libgtk3-perl, |
314 | python3, |
315 | diff --git a/softwareproperties/SoftwareProperties.py b/softwareproperties/SoftwareProperties.py |
316 | index 5f6bf64..9532bfb 100644 |
317 | --- a/softwareproperties/SoftwareProperties.py |
318 | +++ b/softwareproperties/SoftwareProperties.py |
319 | @@ -138,7 +138,8 @@ class SoftwareProperties(object): |
320 | " wait for all running threads (PPA key fetchers) to exit " |
321 | for t in threading.enumerate(): |
322 | if t.ident != threading.current_thread().ident: |
323 | - t.join() |
324 | + if not t.daemon: |
325 | + t.join() |
326 | |
327 | def backup_apt_conf(self): |
328 | """Backup all apt configuration options""" |
329 | diff --git a/softwareproperties/gtk/DialogUaAttach.py b/softwareproperties/gtk/DialogUaAttach.py |
330 | index 4649545..20bb543 100644 |
331 | --- a/softwareproperties/gtk/DialogUaAttach.py |
332 | +++ b/softwareproperties/gtk/DialogUaAttach.py |
333 | @@ -20,11 +20,12 @@ import os |
334 | from gettext import gettext as _ |
335 | import gi |
336 | gi.require_version("Gtk", "3.0") |
337 | -from gi.repository import Gtk |
338 | - |
339 | -from softwareproperties.gtk.utils import ( |
340 | - setup_ui, |
341 | -) |
342 | +from gi.repository import Gtk,Gdk,GLib |
343 | +from softwareproperties.gtk.utils import setup_ui |
344 | +from uaclient.api.u.pro.attach.magic.initiate.v1 import initiate |
345 | +from uaclient.api.u.pro.attach.magic.wait.v1 import MagicAttachWaitOptions, wait |
346 | +from uaclient.exceptions import MagicAttachTokenError |
347 | +import threading |
348 | |
349 | class DialogUaAttach: |
350 | def __init__(self, parent, datadir, ua_object): |
351 | @@ -35,50 +36,144 @@ class DialogUaAttach: |
352 | self.dialog = self.dialog_ua_attach |
353 | self.dialog.set_transient_for(parent) |
354 | |
355 | + self.contract_token = None |
356 | self.attaching = False |
357 | + self.poll = None |
358 | + self.pin_label_box.override_background_color(Gtk.StateType.NORMAL, Gdk.RGBA(0.5, 0.5, 0.5, 0.5)) |
359 | + |
360 | + self.start_magic_attach() |
361 | |
362 | def run(self): |
363 | self.dialog.run() |
364 | self.dialog.hide() |
365 | |
366 | - def update_state(self): |
367 | - have_token = self.entry_token.get_text() != '' |
368 | - self.button_attach.set_sensitive(have_token and not self.attaching) |
369 | - self.entry_token.set_sensitive(not self.attaching) |
370 | + def update_state(self, case = None): |
371 | + """ |
372 | + fail : called by the attachment callback, and it failed. |
373 | + success: called by the attachment callback, and it succeeded. |
374 | + expired: called by the token polling when the token expires. |
375 | + """ |
376 | + if self.token_radio.get_active(): |
377 | + self.pin_label.set_opacity(0) |
378 | + self.confirm.set_sensitive(self.token_field.get_text() != "" and |
379 | + not self.attaching) |
380 | + icon = self.token_status_icon |
381 | + spinner = self.token_spinner |
382 | + status = self.token_status |
383 | + else: |
384 | + self.pin_label.set_text(self.pin) |
385 | + self.pin_label.set_opacity(1) |
386 | + self.confirm.set_sensitive(self.contract_token != None and |
387 | + not self.attaching) |
388 | + icon = self.pin_status_icon |
389 | + spinner = self.pin_spinner |
390 | + status = self.pin_status |
391 | + |
392 | if self.attaching: |
393 | - self.spinner.start() |
394 | + spinner.start() |
395 | else: |
396 | - self.spinner.stop() |
397 | + spinner.stop() |
398 | + |
399 | + def lock_radio_buttons(boolean): |
400 | + self.token_radio.set_sensitive(not boolean) |
401 | + self.magic_radio.set_sensitive(not boolean) |
402 | + |
403 | + lock_radio_buttons(self.attaching) |
404 | + self.token_field.set_sensitive(not self.attaching |
405 | + and self.token_radio.get_active()) |
406 | + |
407 | + if (case == "fail"): |
408 | + status.set_markup('<span foreground="red">%s</span>' % _('Invalid token')) |
409 | + icon.set_from_icon_name('emblem-unreadable', 1) |
410 | + elif (case == "success"): |
411 | + self.finish() |
412 | + elif (case == "pin_validated"): |
413 | + status.set_markup('<span foreground="green">%s</span>' % _('Valid token')) |
414 | + icon.set_from_icon_name('emblem-default', 1) |
415 | + lock_radio_buttons(True) |
416 | + elif (case == "expired"): |
417 | + status.set_markup(_('Code expired')) |
418 | + icon.set_from_icon_name('gtk-dialog-warning', 1) |
419 | + |
420 | + #Only show icons/status if case is set |
421 | + self.token_status_icon.set_visible(False) |
422 | + self.token_status.set_visible(False) |
423 | + self.pin_status_icon.set_visible(False) |
424 | + self.pin_status.set_visible(False) |
425 | + icon.set_visible(case) |
426 | + status.set_visible(case) |
427 | |
428 | def attach(self): |
429 | if self.attaching: |
430 | return |
431 | |
432 | - token = self.entry_token.get_text() |
433 | - if token == '': |
434 | - return |
435 | + if self.token_radio.get_active(): |
436 | + token = self.token_field.get_text() |
437 | + else: |
438 | + token = self.contract_token |
439 | |
440 | self.attaching = True |
441 | - self.label_attach_error.set_text('') |
442 | def on_reply(): |
443 | - self.dialog.response(Gtk.ResponseType.OK) |
444 | + self.attaching = False |
445 | + self.update_state("success") |
446 | def on_error(error): |
447 | - # FIXME |
448 | - print(error) |
449 | - self.label_attach_error.set_text(_('Failed to attach. Please try again')) |
450 | self.attaching = False |
451 | - self.update_state() |
452 | + if self.magic_radio.get_active(): |
453 | + self.contract_token = None |
454 | + self.update_state("fail") |
455 | self.ua_object.Attach(token, reply_handler=on_reply, error_handler=on_error, dbus_interface='com.canonical.UbuntuAdvantage.Manager', timeout=600) |
456 | self.update_state() |
457 | |
458 | - def on_token_entry_changed(self, entry): |
459 | - self.update_state() |
460 | + def on_token_typing(self, entry): |
461 | + self.confirm.set_sensitive(self.token_field.get_text() != '') |
462 | |
463 | def on_token_entry_activate(self, entry): |
464 | - self.attach() |
465 | + token = self.token_field.get_text() |
466 | + if token != '': |
467 | + self.attach() |
468 | |
469 | - def on_attach_clicked(self, button): |
470 | + def on_confirm_clicked(self, button): |
471 | self.attach() |
472 | |
473 | def on_cancel_clicked(self, button): |
474 | self.dialog.response(Gtk.ResponseType.CANCEL) |
475 | + |
476 | + def poll_for_magic_token(self): |
477 | + options = MagicAttachWaitOptions(magic_token=self.req_id) |
478 | + try: |
479 | + response = wait(options) |
480 | + self.contract_token = response.contract_token |
481 | + GLib.idle_add(self.update_state, 'pin_validated') |
482 | + except MagicAttachTokenError: |
483 | + GLib.idle_add(self.update_state, 'expired') |
484 | + finally: |
485 | + self.poll = None |
486 | + |
487 | + def start_magic_attach(self): |
488 | + # Already polling, don't bother the server with a new request. |
489 | + if self.poll != None or self.contract_token != None: |
490 | + return |
491 | + |
492 | + self.contract_token = None |
493 | + |
494 | + # Request a magic attachment and parse relevants fields from response. |
495 | + # userCode: The pin the user has to type in <ubuntu.com/pro/attach>; |
496 | + # token: Identifies the request (used for polling for it). |
497 | + try: |
498 | + response = initiate() |
499 | + self.pin = response.user_code |
500 | + self.req_id = response.token |
501 | + except Exception as e: |
502 | + print(e) |
503 | + return |
504 | + self.update_state() |
505 | + threading.Thread(target=self.poll_for_magic_token, daemon=True).start() |
506 | + |
507 | + def on_radio_toggled(self, button): |
508 | + self.update_state() |
509 | + |
510 | + def on_magic_radio_clicked(self, button): |
511 | + self.start_magic_attach() |
512 | + |
513 | + def finish(self): |
514 | + self.dialog.response(Gtk.ResponseType.OK) |
and it's not working because glib in that serie doesn't have https:/ /gitlab. gnome.org/ GNOME/glib/ -/issues/ 602 so we will need to tweak...