Merge ~ddstreet/software-properties:simple_http_unix_connection into software-properties:ubuntu/master
- Git
- lp:~ddstreet/software-properties
- simple_http_unix_connection
- Merge into ubuntu/master
Status: | Merged |
---|---|
Merged at revision: | fe446246f23a119d305b5cb924eeba31ca2b366d |
Proposed branch: | ~ddstreet/software-properties:simple_http_unix_connection |
Merge into: | software-properties:ubuntu/master |
Diff against target: |
163 lines (+41/-60) 2 files modified
dev/null (+0/-43) softwareproperties/LivepatchService.py (+41/-17) |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Shivaram Lingamneni | Pending | ||
Review via email: mp+396926@code.launchpad.net |
Commit message
Description of the change
Shivaram Lingamneni (slingamn) wrote : | # |
Shivaram Lingamneni (slingamn) wrote : | # |
Found this in strace: "WARNING:root:name 'socket' is not defined, Retrying in 12.8 seconds..."
I added the missing import of `socket`, but then I got this from strace:
connect(9, {sa_family=AF_UNIX, sun_path=
so it's not correctly decoding the socket path from the URL.
Dan Streetman (ddstreet) wrote : | # |
thanks - added the import socket and fixed the socket paths; can you try again?
Shivaram Lingamneni (slingamn) wrote : | # |
I believe I tested this successfully in 20.04, with minor backporting. The following changes are necessary on top of ecf462b:
1. `sock.sock = sock` should be `self.sock = sock`
2. A reference to `self.LIVEPATCH
Dan Streetman (ddstreet) wrote : | # |
thanks, think it should be all corrected now
Shivaram Lingamneni (slingamn) wrote : | # |
Looks good to me --- fe44624 is identical to the version I successfully tested, except for the 20.04 backporting changes.
Thanks!
Dan Streetman (ddstreet) wrote : | # |
thanks, merged!
Preview Diff
1 | diff --git a/softwareproperties/LivepatchService.py b/softwareproperties/LivepatchService.py | |||
2 | index 7dcd889..163f729 100644 | |||
3 | --- a/softwareproperties/LivepatchService.py | |||
4 | +++ b/softwareproperties/LivepatchService.py | |||
5 | @@ -20,8 +20,12 @@ | |||
6 | 20 | # USA | 20 | # USA |
7 | 21 | 21 | ||
8 | 22 | from gettext import gettext as _ | 22 | from gettext import gettext as _ |
9 | 23 | from contextlib import closing | ||
10 | 24 | from urllib.parse import urlencode | ||
11 | 25 | import http.client | ||
12 | 23 | import logging | 26 | import logging |
13 | 24 | import json | 27 | import json |
14 | 28 | import socket | ||
15 | 25 | 29 | ||
16 | 26 | import gi | 30 | import gi |
17 | 27 | from gi.repository import Gio, GLib, GObject | 31 | from gi.repository import Gio, GLib, GObject |
18 | @@ -43,8 +47,6 @@ from softwareproperties.gtk.utils import ( | |||
19 | 43 | 47 | ||
20 | 44 | from softwareproperties.LivepatchSnap import LivepatchSnap | 48 | from softwareproperties.LivepatchSnap import LivepatchSnap |
21 | 45 | 49 | ||
22 | 46 | from .http_unixdomain import unix_http_request | ||
23 | 47 | |||
24 | 48 | def datetime_parser(json_dict): | 50 | def datetime_parser(json_dict): |
25 | 49 | for (key, value) in json_dict.items(): | 51 | for (key, value) in json_dict.items(): |
26 | 50 | try: | 52 | try: |
27 | @@ -60,13 +62,27 @@ class LivepatchAvailability: | |||
28 | 60 | CHECKING = 2 | 62 | CHECKING = 2 |
29 | 61 | 63 | ||
30 | 62 | 64 | ||
32 | 63 | class LivepatchService(GObject.GObject): | 65 | # copied from update-manager package UpdateManager/Core/LivePatchSocket.py |
33 | 66 | class UHTTPConnection(http.client.HTTPConnection): | ||
34 | 67 | |||
35 | 68 | def __init__(self, path): | ||
36 | 69 | http.client.HTTPConnection.__init__(self, 'localhost') | ||
37 | 70 | self.path = path | ||
38 | 71 | |||
39 | 72 | def connect(self): | ||
40 | 73 | sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) | ||
41 | 74 | sock.connect(self.path) | ||
42 | 75 | self.sock = sock | ||
43 | 64 | 76 | ||
49 | 65 | # Constants | 77 | |
50 | 66 | STATUS_ENDPOINT = 'http+unix://%2Fvar%2Fsnap%2Fcanonical-livepatch%2Fcurrent%2Flivepatchd.sock/status' | 78 | # Constants |
51 | 67 | ENABLE_ENDPOINT = 'http+unix://%2Fvar%2Fsnap%2Fcanonical-livepatch%2Fcurrent%2Flivepatchd-priv.sock/enable' | 79 | LIVEPATCH_SNAP_CURRENT_DIR = '/var/snap/canonical-livepatch/current' |
52 | 68 | DISABLE_ENDPOINT = 'http+unix://%2Fvar%2Fsnap%2Fcanonical-livepatch%2Fcurrent%2Flivepatchd-priv.sock/disable' | 80 | LIVEPATCH_SOCKET = f'{LIVEPATCH_SNAP_CURRENT_DIR}/livepatchd.sock' |
53 | 69 | LIVEPATCH_RUNNING_FILE = '/var/snap/canonical-livepatch/common/machine-token' | 81 | LIVEPATCH_PRIV_SOCKET = f'{LIVEPATCH_SNAP_CURRENT_DIR}/livepatchd-priv.sock' |
54 | 82 | LIVEPATCH_RUNNING_FILE = '/var/snap/canonical-livepatch/common/machine-token' | ||
55 | 83 | |||
56 | 84 | |||
57 | 85 | class LivepatchService(GObject.GObject): | ||
58 | 70 | 86 | ||
59 | 71 | ENABLE_ERROR_MSG = _('Failed to enable Livepatch: {}') | 87 | ENABLE_ERROR_MSG = _('Failed to enable Livepatch: {}') |
60 | 72 | DISABLE_ERROR_MSG = _('Failed to disable Livepatch: {}') | 88 | DISABLE_ERROR_MSG = _('Failed to disable Livepatch: {}') |
61 | @@ -95,7 +111,7 @@ class LivepatchService(GObject.GObject): | |||
62 | 95 | # Init Properties | 111 | # Init Properties |
63 | 96 | self._availability = LivepatchAvailability.FALSE | 112 | self._availability = LivepatchAvailability.FALSE |
64 | 97 | self._availability_message = None | 113 | self._availability_message = None |
66 | 98 | lp_file = Gio.File.new_for_path(path=self.LIVEPATCH_RUNNING_FILE) | 114 | lp_file = Gio.File.new_for_path(path=LIVEPATCH_RUNNING_FILE) |
67 | 99 | self._enabled = lp_file.query_exists() | 115 | self._enabled = lp_file.query_exists() |
68 | 100 | 116 | ||
69 | 101 | # Monitor connectivity status | 117 | # Monitor connectivity status |
70 | @@ -196,8 +212,11 @@ class LivepatchService(GObject.GObject): | |||
71 | 196 | """ | 212 | """ |
72 | 197 | try: | 213 | try: |
73 | 198 | params = {'verbosity': 3, 'format': 'json'} | 214 | params = {'verbosity': 3, 'format': 'json'} |
76 | 199 | _, response = unix_http_request('GET', self.STATUS_ENDPOINT, params=params) | 215 | with closing(UHTTPConnection(LIVEPATCH_SOCKET)) as c: |
77 | 200 | return json.loads(response, object_hook=datetime_parser) | 216 | url = f'/status?{urlencode(params)}' |
78 | 217 | c.request('GET', url) | ||
79 | 218 | response = c.getresponse() | ||
80 | 219 | return json.loads(response.read(), object_hook=datetime_parser) | ||
81 | 201 | except Exception as e: | 220 | except Exception as e: |
82 | 202 | logging.debug('Failed to get Livepatch status: {}'.format(str(e))) | 221 | logging.debug('Failed to get Livepatch status: {}'.format(str(e))) |
83 | 203 | return None | 222 | return None |
84 | @@ -222,9 +241,12 @@ class LivepatchService(GObject.GObject): | |||
85 | 222 | @retry(Exception) | 241 | @retry(Exception) |
86 | 223 | def _enable_service_with_retry(self, token): | 242 | def _enable_service_with_retry(self, token): |
87 | 224 | params = {'auth-token': token} | 243 | params = {'auth-token': token} |
91 | 225 | code, response = unix_http_request('PUT', self.ENABLE_ENDPOINT, params=params) | 244 | with closing(UHTTPConnection(LIVEPATCH_PRIV_SOCKET)) as c: |
92 | 226 | if 400 <= code < 600: | 245 | url = f'/enable?{urlencode(params)}' |
93 | 227 | return True, self.ENABLE_ERROR_MSG.format(response.decode('utf-8')) | 246 | c.request('PUT', url) |
94 | 247 | response = c.getresponse() | ||
95 | 248 | if 400 <= response.status < 600: | ||
96 | 249 | return True, self.ENABLE_ERROR_MSG.format(response.read().decode('utf-8')) | ||
97 | 228 | return False, '' | 250 | return False, '' |
98 | 229 | 251 | ||
99 | 230 | def _disable_service(self): | 252 | def _disable_service(self): |
100 | @@ -242,9 +264,11 @@ class LivepatchService(GObject.GObject): | |||
101 | 242 | 264 | ||
102 | 243 | @retry(Exception) | 265 | @retry(Exception) |
103 | 244 | def _disable_service_with_retry(self): | 266 | def _disable_service_with_retry(self): |
107 | 245 | code, response = unix_http_request('PUT', self.DISABLE_ENDPOINT) | 267 | with closing(UHTTPConnection(LIVEPATCH_PRIV_SOCKET)) as c: |
108 | 246 | if 400 <= code < 600: | 268 | c.request('PUT', '/disable') |
109 | 247 | return True, self.DISABLE_ERROR_MSG.format(response.decode('utf-8')) | 269 | response = c.getresponse() |
110 | 270 | if 400 <= response.status < 600: | ||
111 | 271 | return True, self.DISABLE_ERROR_MSG.format(response.read().decode('utf-8')) | ||
112 | 248 | return False, '' | 272 | return False, '' |
113 | 249 | 273 | ||
114 | 250 | # Signals handlers | 274 | # Signals handlers |
115 | diff --git a/softwareproperties/http_unixdomain.py b/softwareproperties/http_unixdomain.py | |||
116 | 251 | deleted file mode 100644 | 275 | deleted file mode 100644 |
117 | index 767a9f4..0000000 | |||
118 | --- a/softwareproperties/http_unixdomain.py | |||
119 | +++ /dev/null | |||
120 | @@ -1,43 +0,0 @@ | |||
121 | 1 | import socket | ||
122 | 2 | import http.client | ||
123 | 3 | from urllib.parse import unquote, urlencode, urlparse | ||
124 | 4 | |||
125 | 5 | # UnixHTTPConnection is from https://github.com/msabramo/requests-unixsocket | ||
126 | 6 | # which is released under the Apache License 2.0 | ||
127 | 7 | class UnixHTTPConnection(http.client.HTTPConnection, object): | ||
128 | 8 | |||
129 | 9 | def __init__(self, unix_socket_url, timeout=60): | ||
130 | 10 | """Create an HTTP connection to a unix domain socket | ||
131 | 11 | |||
132 | 12 | :param unix_socket_url: A URL with a scheme of 'http+unix' and the | ||
133 | 13 | netloc is a percent-encoded path to a unix domain socket. E.g.: | ||
134 | 14 | 'http+unix://%2Ftmp%2Fprofilesvc.sock/status/pid' | ||
135 | 15 | """ | ||
136 | 16 | super(UnixHTTPConnection, self).__init__('localhost', timeout=timeout) | ||
137 | 17 | self.unix_socket_url = unix_socket_url | ||
138 | 18 | self.timeout = timeout | ||
139 | 19 | self.sock = None | ||
140 | 20 | |||
141 | 21 | def __del__(self): # base class does not have d'tor | ||
142 | 22 | if self.sock: | ||
143 | 23 | self.sock.close() | ||
144 | 24 | |||
145 | 25 | def connect(self): | ||
146 | 26 | sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) | ||
147 | 27 | sock.settimeout(self.timeout) | ||
148 | 28 | socket_path = unquote(urlparse(self.unix_socket_url).netloc) | ||
149 | 29 | sock.connect(socket_path) | ||
150 | 30 | self.sock = sock | ||
151 | 31 | |||
152 | 32 | def unix_http_request(verb, url, params=None): | ||
153 | 33 | parsed_url = urlparse(url) | ||
154 | 34 | conn = UnixHTTPConnection(url) | ||
155 | 35 | try: | ||
156 | 36 | path = parsed_url.path | ||
157 | 37 | if params is not None: | ||
158 | 38 | path = '%s?%s' % (parsed_url.path, urlencode(params)) | ||
159 | 39 | conn.request(verb.upper(), path) | ||
160 | 40 | response = conn.getresponse() | ||
161 | 41 | return response.status, response.read() | ||
162 | 42 | finally: | ||
163 | 43 | conn.close() |
This crashed for me on 20.04:
Jan 26 12:17:00 guivm gnome-control- center. desktop[ 2058]: Traceback (most recent call last): center. desktop[ 2058]: File "/usr/bin/ software- properties- gtk", line 37, in <module> center. desktop[ 2058]: from softwarepropert ies.gtk. SoftwarePropert iesGtk import SoftwarePropert iesGtk center. desktop[ 2058]: File "/usr/lib/ python3/ dist-packages/ softwarepropert ies/gtk/ SoftwarePropert iesGtk. py", line 56, in <module> center. desktop[ 2058]: from .LivepatchPage import LivepatchPage center. desktop[ 2058]: File "/usr/lib/ python3/ dist-packages/ softwarepropert ies/gtk/ LivepatchPage. py", line 31, in <module> center. desktop[ 2058]: from softwarepropert ies.LivepatchSe rvice import ( center. desktop[ 2058]: File "/usr/lib/ python3/ dist-packages/ softwarepropert ies/LivepatchSe rvice.py" , line 24, in <module> center. desktop[ 2058]: from urllib import urlparse, urlencode center. desktop[ 2058]: ImportError: cannot import name 'urlparse' from 'urllib' (/usr/lib/ python3. 8/urllib/ __init_ _.py)
Jan 26 12:17:00 guivm gnome-control-
Jan 26 12:17:00 guivm gnome-control-
Jan 26 12:17:00 guivm gnome-control-
Jan 26 12:17:00 guivm gnome-control-
Jan 26 12:17:00 guivm gnome-control-
Jan 26 12:17:00 guivm gnome-control-
Jan 26 12:17:00 guivm gnome-control-
Jan 26 12:17:00 guivm gnome-control-
Jan 26 12:17:00 guivm gnome-control-
With the import changed to `from urllib.parse import urlparse, urlencode`, it doesn't crash, but the UI displays "Failed to retrieve Livepatch status.". I think it might be hitting an exception somewhere?