Merge lp:~cmiller/desktopcouch/replication-to-u1 into lp:desktopcouch
- replication-to-u1
- Merge into trunk
Proposed by
Chad Miller
Status: | Merged |
---|---|
Approved by: | Chad Miller |
Approved revision: | 79 |
Merged at revision: | not available |
Proposed branch: | lp:~cmiller/desktopcouch/replication-to-u1 |
Merge into: | lp:desktopcouch |
Diff against target: |
253 lines 8 files modified
bin/desktopcouch-service (+1/-1) desktopcouch/pair/couchdb_pairing/couchdb_io.py (+23/-14) desktopcouch/records/server.py (+5/-4) desktopcouch/records/server_base.py (+5/-2) desktopcouch/replication.py (+1/-4) desktopcouch/replication_services/example.py (+1/-7) desktopcouch/replication_services/ubuntuone.py (+8/-11) setup.py (+1/-1) |
To merge this branch: | bzr merge lp:~cmiller/desktopcouch/replication-to-u1 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Ubuntu One hackers | Pending | ||
Review via email: mp+12527@code.launchpad.net |
Commit message
Use U1 credentials when connecting to U1 for creating databases to replicate into. Remove extraneous function for couchdb service locations.
Description of the change
To post a comment you must log in.
- 75. By Chad Miller
-
Switch back to HMAC, from debugging PLAINTEXT, for OAuth.
- 76. By Chad Miller
-
Log to correct place.
- 77. By Chad Miller
-
Always try to replicate. Destroy the semantic of not having a self-identity
record meaning no replication; now, always create a self-identity record in
the service. - 78. By Chad Miller
-
Restructure replicate function so we try replicate even if we can't verify that
the database exists.Remove bogus put of test record!
- 79. By Chad Miller
-
Use OAuthCapableServer to connect to local, for replication action unsupported
in python couchdb module.
Revision history for this message
Chad Miller (cmiller) wrote : | # |
- 80. By Chad Miller
-
Bumped version number for release.
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'bin/desktopcouch-pair' | |||
2 | === modified file 'bin/desktopcouch-service' | |||
3 | --- bin/desktopcouch-service 2009-09-11 03:18:00 +0000 | |||
4 | +++ bin/desktopcouch-service 2009-09-28 18:39:12 +0000 | |||
5 | @@ -72,7 +72,7 @@ | |||
6 | 72 | import xdg.BaseDirectory | 72 | import xdg.BaseDirectory |
7 | 73 | 73 | ||
8 | 74 | log_directory = os.path.join(xdg.BaseDirectory.xdg_cache_home, | 74 | log_directory = os.path.join(xdg.BaseDirectory.xdg_cache_home, |
10 | 75 | "ubuntuone/log") | 75 | "desktop-couch/log") |
11 | 76 | try: | 76 | try: |
12 | 77 | os.makedirs(log_directory) | 77 | os.makedirs(log_directory) |
13 | 78 | except: | 78 | except: |
14 | 79 | 79 | ||
15 | === modified file 'desktopcouch/pair/couchdb_pairing/couchdb_io.py' | |||
16 | --- desktopcouch/pair/couchdb_pairing/couchdb_io.py 2009-09-26 11:49:49 +0000 | |||
17 | +++ desktopcouch/pair/couchdb_pairing/couchdb_io.py 2009-09-28 18:39:12 +0000 | |||
18 | @@ -180,10 +180,12 @@ | |||
19 | 180 | logging.debug("found %d %s records", len(values), key) | 180 | logging.debug("found %d %s records", len(values), key) |
20 | 181 | return values | 181 | return values |
21 | 182 | 182 | ||
23 | 183 | def create_database(dst_host, dst_port, dst_name): | 183 | def create_database(dst_host, dst_port, dst_name, use_ssl=False, |
24 | 184 | oauth_tokens=None): | ||
25 | 184 | """Given parts, create a database.""" | 185 | """Given parts, create a database.""" |
28 | 185 | dst_url = mkuri(dst_host, dst_port) | 186 | dst_url = mkuri(dst_host, dst_port, use_ssl) |
29 | 186 | return server.CouchDatabase(dst_name, dst_url, create=True) | 187 | return server.CouchDatabase(dst_name, dst_url, create=True, |
30 | 188 | oauth_tokens=oauth_tokens) | ||
31 | 187 | 189 | ||
32 | 188 | def replicate(source_database, target_database, target_host=None, | 190 | def replicate(source_database, target_database, target_host=None, |
33 | 189 | target_port=None, source_host=None, source_port=None, | 191 | target_port=None, source_host=None, source_port=None, |
34 | @@ -211,27 +213,34 @@ | |||
35 | 211 | 213 | ||
36 | 212 | record = dict(source=source, target=target) | 214 | record = dict(source=source, target=target) |
37 | 213 | try: | 215 | try: |
38 | 214 | # regardless of source and target, we talk to our local couchdb :( | ||
39 | 215 | url = None # so logging works in exception handler | ||
40 | 216 | port = int(desktopcouch_find_port()) | ||
41 | 217 | # TODO: Get admin username and password from keyring. Populate URL. | ||
42 | 218 | url = mkuri("localhost", port,) | ||
43 | 219 | 216 | ||
44 | 220 | if target_host: | 217 | if target_host: |
45 | 221 | # Target databases must exist before replicating to them. | 218 | # Target databases must exist before replicating to them. |
46 | 222 | logging.debug("creating %r %s:%d", target_database, target_host, | 219 | logging.debug("creating %r %s:%d", target_database, target_host, |
47 | 223 | target_port) | 220 | target_port) |
49 | 224 | create_database(target_host, target_port, target_database) | 221 | create_database(target_host, target_port, target_database, |
50 | 222 | target_ssl, target_oauth) | ||
51 | 223 | except: | ||
52 | 224 | logging.exception("can't talk to couchdb. %r %s:%d oauth=%s", | ||
53 | 225 | target_database, target_host, target_port, target_oauth) | ||
54 | 226 | |||
55 | 227 | logging.debug("db exists, and we're ready to replicate") | ||
56 | 228 | try: | ||
57 | 229 | # regardless of source and target, we talk to our local couchdb :( | ||
58 | 230 | port = int(desktopcouch_find_port()) | ||
59 | 231 | url = mkuri("localhost", port,) | ||
60 | 232 | |||
61 | 233 | logging.debug("asking %r to send %s to %s", url, source, target) | ||
62 | 225 | 234 | ||
63 | 226 | ### All until python-couchdb gets a Server.replicate() function | 235 | ### All until python-couchdb gets a Server.replicate() function |
68 | 227 | import couchdb | 236 | local_server = server.OAuthCapableServer(url) |
69 | 228 | logging.debug("asking %r to send %s to %s", url, source, target) | 237 | resp, data = local_server.resource.post(path='/_replicate', content=record) |
70 | 229 | server = couchdb.client.Server(url) | 238 | |
67 | 230 | resp, data = server.resource.post(path='/_replicate', content=record) | ||
71 | 231 | logging.debug("replicate result: %r %r", resp, data) | 239 | logging.debug("replicate result: %r %r", resp, data) |
72 | 232 | ### | 240 | ### |
73 | 233 | except: | 241 | except: |
75 | 234 | logging.exception("can't talk to couchdb. %r <== %r", url, record) | 242 | logging.error("can't talk to couchdb. %r <== %r", url, record) |
76 | 243 | raise | ||
77 | 235 | 244 | ||
78 | 236 | def get_pairings(uri=None): | 245 | def get_pairings(uri=None): |
79 | 237 | """Get a list of paired servers.""" | 246 | """Get a list of paired servers.""" |
80 | 238 | 247 | ||
81 | === modified file 'desktopcouch/records/server.py' | |||
82 | --- desktopcouch/records/server.py 2009-09-14 20:18:53 +0000 | |||
83 | +++ desktopcouch/records/server.py 2009-09-28 18:39:12 +0000 | |||
84 | @@ -26,12 +26,13 @@ | |||
85 | 26 | from desktopcouch.records import server_base | 26 | from desktopcouch.records import server_base |
86 | 27 | 27 | ||
87 | 28 | class OAuthCapableServer(Server): | 28 | class OAuthCapableServer(Server): |
89 | 29 | def __init__(self, uri): | 29 | def __init__(self, uri, oauth_tokens=None): |
90 | 30 | """Subclass of couchdb.client.Server which creates a custom | 30 | """Subclass of couchdb.client.Server which creates a custom |
91 | 31 | httplib2.Http subclass which understands OAuth""" | 31 | httplib2.Http subclass which understands OAuth""" |
92 | 32 | http = server_base.OAuthCapableHttp() | 32 | http = server_base.OAuthCapableHttp() |
93 | 33 | http.force_exception_to_status_code = False | 33 | http.force_exception_to_status_code = False |
95 | 34 | oauth_tokens = desktopcouch.local_files.get_oauth_tokens() | 34 | if oauth_tokens is None: |
96 | 35 | oauth_tokens = desktopcouch.local_files.get_oauth_tokens() | ||
97 | 35 | (consumer_key, consumer_secret, token, token_secret) = ( | 36 | (consumer_key, consumer_secret, token, token_secret) = ( |
98 | 36 | oauth_tokens["consumer_key"], oauth_tokens["consumer_secret"], | 37 | oauth_tokens["consumer_key"], oauth_tokens["consumer_secret"], |
99 | 37 | oauth_tokens["token"], oauth_tokens["token_secret"]) | 38 | oauth_tokens["token"], oauth_tokens["token_secret"]) |
100 | @@ -42,11 +43,11 @@ | |||
101 | 42 | """An small records specific abstraction over a couch db database.""" | 43 | """An small records specific abstraction over a couch db database.""" |
102 | 43 | 44 | ||
103 | 44 | def __init__(self, database, uri=None, record_factory=None, create=False, | 45 | def __init__(self, database, uri=None, record_factory=None, create=False, |
105 | 45 | server_class=OAuthCapableServer): | 46 | server_class=OAuthCapableServer, oauth_tokens=None): |
106 | 46 | if not uri: | 47 | if not uri: |
107 | 47 | desktopcouch.find_pid() | 48 | desktopcouch.find_pid() |
108 | 48 | port = desktopcouch.find_port() | 49 | port = desktopcouch.find_port() |
109 | 49 | uri = "http://localhost:%s" % port | 50 | uri = "http://localhost:%s" % port |
110 | 50 | super(CouchDatabase, self).__init__( | 51 | super(CouchDatabase, self).__init__( |
111 | 51 | database, uri, record_factory=record_factory, create=create, | 52 | database, uri, record_factory=record_factory, create=create, |
113 | 52 | server_class=server_class) | 53 | server_class=server_class, oauth_tokens=oauth_tokens) |
114 | 53 | 54 | ||
115 | === modified file 'desktopcouch/records/server_base.py' | |||
116 | --- desktopcouch/records/server_base.py 2009-09-14 20:22:03 +0000 | |||
117 | +++ desktopcouch/records/server_base.py 2009-09-28 18:39:12 +0000 | |||
118 | @@ -29,6 +29,7 @@ | |||
119 | 29 | from oauth import oauth | 29 | from oauth import oauth |
120 | 30 | import urlparse | 30 | import urlparse |
121 | 31 | import cgi | 31 | import cgi |
122 | 32 | import logging | ||
123 | 32 | 33 | ||
124 | 33 | #DEFAULT_DESIGN_DOCUMENT = "design" | 34 | #DEFAULT_DESIGN_DOCUMENT = "design" |
125 | 34 | DEFAULT_DESIGN_DOCUMENT = None # each view in its own eponymous design doc. | 35 | DEFAULT_DESIGN_DOCUMENT = None # each view in its own eponymous design doc. |
126 | @@ -72,6 +73,8 @@ | |||
127 | 72 | ) | 73 | ) |
128 | 73 | req.sign_request(oauth.OAuthSignatureMethod_HMAC_SHA1(), consumer, access_token) | 74 | req.sign_request(oauth.OAuthSignatureMethod_HMAC_SHA1(), consumer, access_token) |
129 | 74 | headers.update(httplib2._normalize_headers(req.to_header())) | 75 | headers.update(httplib2._normalize_headers(req.to_header())) |
130 | 76 | for header in headers.iteritems(): | ||
131 | 77 | logging.debug("header %s", header) | ||
132 | 75 | 78 | ||
133 | 76 | class OAuthCapableHttp(httplib2.Http): | 79 | class OAuthCapableHttp(httplib2.Http): |
134 | 77 | """Subclass of httplib2.Http which specifically uses our OAuth | 80 | """Subclass of httplib2.Http which specifically uses our OAuth |
135 | @@ -107,9 +110,9 @@ | |||
136 | 107 | """An small records specific abstraction over a couch db database.""" | 110 | """An small records specific abstraction over a couch db database.""" |
137 | 108 | 111 | ||
138 | 109 | def __init__(self, database, uri, record_factory=None, create=False, | 112 | def __init__(self, database, uri, record_factory=None, create=False, |
140 | 110 | server_class=Server): | 113 | server_class=Server, **server_class_extras): |
141 | 111 | self.server_uri = uri | 114 | self.server_uri = uri |
143 | 112 | self._server = server_class(self.server_uri) | 115 | self._server = server_class(self.server_uri, **server_class_extras) |
144 | 113 | if database not in self._server: | 116 | if database not in self._server: |
145 | 114 | if create: | 117 | if create: |
146 | 115 | self._server.create(database) | 118 | self._server.create(database) |
147 | 116 | 119 | ||
148 | === modified file 'desktopcouch/replication.py' | |||
149 | --- desktopcouch/replication.py 2009-09-16 17:03:02 +0000 | |||
150 | +++ desktopcouch/replication.py 2009-09-28 18:39:12 +0000 | |||
151 | @@ -205,10 +205,7 @@ | |||
152 | 205 | def set_up(port_getter): | 205 | def set_up(port_getter): |
153 | 206 | port = port_getter() | 206 | port = port_getter() |
154 | 207 | unique_identifiers = couchdb_io.get_my_host_unique_id( | 207 | unique_identifiers = couchdb_io.get_my_host_unique_id( |
159 | 208 | couchdb_io.mkuri("localhost", int(port)), create=False) | 208 | couchdb_io.mkuri("localhost", int(port)), create=True) |
156 | 209 | if unique_identifiers is None: | ||
157 | 210 | log.warn("No unique hostaccount id is set, so pairing not enabled.") | ||
158 | 211 | return None | ||
160 | 212 | 209 | ||
161 | 213 | beacons = [dbus_io.LocationAdvertisement(port, "desktopcouch " + i) | 210 | beacons = [dbus_io.LocationAdvertisement(port, "desktopcouch " + i) |
162 | 214 | for i in unique_identifiers] | 211 | for i in unique_identifiers] |
163 | 215 | 212 | ||
164 | === modified file 'desktopcouch/replication_services/example.py' | |||
165 | --- desktopcouch/replication_services/example.py 2009-09-04 22:25:44 +0000 | |||
166 | +++ desktopcouch/replication_services/example.py 2009-09-28 18:39:12 +0000 | |||
167 | @@ -19,14 +19,8 @@ | |||
168 | 19 | # or to symbolize failure | 19 | # or to symbolize failure |
169 | 20 | return None | 20 | return None |
170 | 21 | 21 | ||
171 | 22 | # Required | ||
172 | 23 | def couchdb_location(): | ||
173 | 24 | """Give a tuple of hostname and port number.""" | ||
174 | 25 | |||
175 | 26 | return "couchdb.example.com", 5984 | ||
176 | 27 | |||
177 | 28 | # Access to this as a string fires off functions. | 22 | # Access to this as a string fires off functions. |
178 | 29 | # Required | 23 | # Required |
180 | 30 | db_name_prefix = "foo" | 24 | db_name_prefix = "http://host.required.example.com/a_prefix_if_necessary" |
181 | 31 | # You can be sure that access to this will always, always be through its | 25 | # You can be sure that access to this will always, always be through its |
182 | 32 | # __str__ method. | 26 | # __str__ method. |
183 | 33 | 27 | ||
184 | === modified file 'desktopcouch/replication_services/ubuntuone.py' | |||
185 | --- desktopcouch/replication_services/ubuntuone.py 2009-09-17 09:58:35 +0000 | |||
186 | +++ desktopcouch/replication_services/ubuntuone.py 2009-09-28 18:39:12 +0000 | |||
187 | @@ -8,6 +8,9 @@ | |||
188 | 8 | name = "Ubuntu One" | 8 | name = "Ubuntu One" |
189 | 9 | description = "The Ubuntu One cloud service" | 9 | description = "The Ubuntu One cloud service" |
190 | 10 | 10 | ||
191 | 11 | oauth_consumer_key = "ubuntuone" | ||
192 | 12 | oauth_consumer_secret = "hammertime" | ||
193 | 13 | |||
194 | 11 | def is_active(): | 14 | def is_active(): |
195 | 12 | """Can we deliver information?""" | 15 | """Can we deliver information?""" |
196 | 13 | return get_oauth_data() is not None | 16 | return get_oauth_data() is not None |
197 | @@ -24,7 +27,7 @@ | |||
198 | 24 | matches = gnomekeyring.find_items_sync( | 27 | matches = gnomekeyring.find_items_sync( |
199 | 25 | gnomekeyring.ITEM_GENERIC_SECRET, | 28 | gnomekeyring.ITEM_GENERIC_SECRET, |
200 | 26 | {'ubuntuone-realm': "https://ubuntuone.com", | 29 | {'ubuntuone-realm': "https://ubuntuone.com", |
202 | 27 | 'oauth-consumer-key': "ubuntuone"}) | 30 | 'oauth-consumer-key': oauth_consumer_key}) |
203 | 28 | if matches: | 31 | if matches: |
204 | 29 | # parse "a=b&c=d" to {"a":"b","c":"d"} | 32 | # parse "a=b&c=d" to {"a":"b","c":"d"} |
205 | 30 | kv_list = [x.split("=", 1) for x in matches[0].secret.split("&")] | 33 | kv_list = [x.split("=", 1) for x in matches[0].secret.split("&")] |
206 | @@ -32,8 +35,8 @@ | |||
207 | 32 | keys = [k.replace("oauth_", "") for k in keys] | 35 | keys = [k.replace("oauth_", "") for k in keys] |
208 | 33 | oauth_data = dict(zip(keys, values)) | 36 | oauth_data = dict(zip(keys, values)) |
209 | 34 | oauth_data.update({ | 37 | oauth_data.update({ |
212 | 35 | "consumer_key": "ubuntuone", | 38 | "consumer_key": oauth_consumer_key, |
213 | 36 | "consumer_secret": "", | 39 | "consumer_secret": oauth_consumer_secret, |
214 | 37 | }) | 40 | }) |
215 | 38 | return oauth_data | 41 | return oauth_data |
216 | 39 | except ImportError, e: | 42 | except ImportError, e: |
217 | @@ -47,13 +50,6 @@ | |||
218 | 47 | logging.error("No keyring daemon found in this session, so we have " | 50 | logging.error("No keyring daemon found in this session, so we have " |
219 | 48 | "no access to Ubuntu One data.") | 51 | "no access to Ubuntu One data.") |
220 | 49 | 52 | ||
221 | 50 | def couchdb_location(): | ||
222 | 51 | """This can vary more often than the OAuth information. Support SRV | ||
223 | 52 | records and whatnot.""" | ||
224 | 53 | |||
225 | 54 | # ...eventually. For now, hard-coded. Maybe YAGNI. | ||
226 | 55 | return "couchdb.one.ubuntu.com", 5984 | ||
227 | 56 | |||
228 | 57 | def get_oauth_token(consumer): | 53 | def get_oauth_token(consumer): |
229 | 58 | """Get the token from the keyring""" | 54 | """Get the token from the keyring""" |
230 | 59 | import gobject | 55 | import gobject |
231 | @@ -96,7 +92,8 @@ | |||
232 | 96 | 92 | ||
233 | 97 | url = "https://one.ubuntu.com/api/account/" | 93 | url = "https://one.ubuntu.com/api/account/" |
234 | 98 | if self.oauth_header is None: | 94 | if self.oauth_header is None: |
236 | 99 | consumer = oauth.OAuthConsumer("ubuntuone", "hammertime") | 95 | consumer = oauth.OAuthConsumer(oauth_consumer_key, |
237 | 96 | oauth_consumer_secret) | ||
238 | 100 | try: | 97 | try: |
239 | 101 | access_token = get_oauth_token(consumer) | 98 | access_token = get_oauth_token(consumer) |
240 | 102 | except gnomekeyring.NoKeyringDaemonError: | 99 | except gnomekeyring.NoKeyringDaemonError: |
241 | 103 | 100 | ||
242 | === modified file 'setup.py' | |||
243 | --- setup.py 2009-09-14 21:48:36 +0000 | |||
244 | +++ setup.py 2009-09-28 18:39:12 +0000 | |||
245 | @@ -22,7 +22,7 @@ | |||
246 | 22 | 22 | ||
247 | 23 | setup( | 23 | setup( |
248 | 24 | name='desktopcouch', | 24 | name='desktopcouch', |
250 | 25 | version='0.4', | 25 | version='0.4.2', |
251 | 26 | description='A Desktop CouchDB instance.', | 26 | description='A Desktop CouchDB instance.', |
252 | 27 | url='https://launchpad.net/desktopcouch', | 27 | url='https://launchpad.net/desktopcouch', |
253 | 28 | license='LGPL-3', | 28 | license='LGPL-3', |
Reviewed by Stuart and me on phone.