Merge lp:~mterry/duplicity/webdav-fixes into lp:~duplicity-team/duplicity/0.7-series

Proposed by Michael Terry
Status: Merged
Merged at revision: 992
Proposed branch: lp:~mterry/duplicity/webdav-fixes
Merge into: lp:~duplicity-team/duplicity/0.7-series
Diff against target: 78 lines (+6/-6)
3 files modified
duplicity/backend.py (+1/-1)
duplicity/backends/webdavbackend.py (+4/-5)
testing/manual/backendtest (+1/-0)
To merge this branch: bzr merge lp:~mterry/duplicity/webdav-fixes
Reviewer Review Type Date Requested Status
duplicity-team Pending
Review via email: mp+223183@code.launchpad.net

Description of the change

This branch fixes two issues I saw when testing the webdav backend:

1) Errors like the following: "Attempt 1 failed. BackendException: File /tmp/duplicity-LQ1a0i-tempdir/mktemp-u2aiyX-2 not found locally after get from backend"

These were caused by the _get() method not calling setdata() on the local path object, so the rest of the code thought it didn't exist.

2) Some odd issues from stale responses/data. We have a couple places in webdavbackend.py where we close the connection before making a request because of this problem. But I've changed it to do it every time, more reliably, by putting a _close() call inside the request() method.

With this, the webdav backend seems fine to me.

To post a comment you must log in.
Revision history for this message
edso (ed.so) wrote :

On 15.06.2014 22:26, Michael Terry wrote:
> 2) Some odd issues from stale responses/data. We have a couple places in webdavbackend.py where we close the connection before making a request because of this problem. But I've changed it to do it every time, more reliably, by putting a _close() call inside the request() method.
>
> With this, the webdav backend seems fine to me.

sure you can do that. but it seems to me that the original author had a web background and didn't do it on purpose to reuse the already setup connection for performance reasons.

i would bet that now every request needs to be re-authenticated now. not sure that's a good idea.

..ede

PS: i wonder why the backend suddenly needs this other way to write the request data. any clue?

Revision history for this message
Michael Terry (mterry) wrote :

> i would bet that now every request needs to be re-authenticated now. not sure that's a good idea.

Well... We already did the connection close in many cases. I just made it consistent.

I feel like the connection close to clear out the bad data/status was a bit of hack in the first place -- seems like really odd behavior of the library, but the workaround worked well. I'm just extending the hack.

If we could come up with a better way that doesn't mean we close the connection, I'm all ears. But I'm not very familiar with http python libraries.

> PS: i wonder why the backend suddenly needs this other way to write the request data. any clue?

It was an extra check added during the backends re-organization to enforce correctness. When writing to a Path object, you ideally call setdata() on it after.

Revision history for this message
edso (ed.so) wrote :

On 15.06.2014 22:57, Michael Terry wrote:
>> i would bet that now every request needs to be re-authenticated now. not sure that's a good idea.
>
> Well... We already did the connection close in many cases. I just made it consistent.

nope, just when failure occured, of which the error cause might have been the connection, hence the reset.

> I feel like the connection close to clear out the bad data/status was a bit of hack in the first place -- seems like really odd behavior of the library, but the workaround worked well. I'm just extending the hack.

actually the workaround was to read the response, which seemed to be managed by the connection and not properly cleaned on response.close()

> If we could come up with a better way that doesn't mean we close the connection, I'm all ears.

yeah.. just read out the response every time and close/reset only on errors.

>But I'm not very familiar with http python libraries.

pfffh.. don't ask me. i did this stuff in php & perl once upon a time, but i assume the mechanics shouldn't be to different.

>> PS: i wonder why the backend suddenly needs this other way to write the request data. any clue?
>
> It was an extra check added during the backends re-organization to enforce correctness. When writing to a Path object, you ideally call setdata() on it after.
>

ohh, i see, so your added check was that actually failed. not the old code?

..ede

lp:~mterry/duplicity/webdav-fixes updated
992. By Michael Terry

Fix setdata() not being called a better way

Revision history for this message
Michael Terry (mterry) wrote :

> nope, just when failure occured, of which the error cause might have been the connection, hence the reset.

That's not quite true. We did it in a normal case and an error case. But I'm willing to believe there's a better fix. Let me try forcing a read...

> ohh, i see, so your added check was that actually failed. not the old code?

Yeah, I guess. Your comment made me rethink the fix. I forgot the mantra of the backend rewrite, which is "let backends be dumb". We already call setdata() for the backend, but we were doing it before the check I added.

Better to just move the setdata() call before the check. I've done that already in this branch.

Revision history for this message
Michael Terry (mterry) wrote :

OK, playing around with it, I couldn't make anything besides the close-every-request thing work. Try the following command:

./testing/manual/backendtest 'webdavs://demo:<email address hidden>/demo/duplicity-testing'

This fails in a few places for me without the close behavior in this tree. I'm sure there's a more elegant fix, but I haven't found it yet. Any ideas? Do you see the same bugs I do with the command?

Revision history for this message
edso (ed.so) wrote :

On 18.06.2014 00:03, Michael Terry wrote:
> Better to just move the setdata() call before the check. I've done that already in this branch.

what does setdata() do anyway? ..ede

Revision history for this message
edso (ed.so) wrote :

On 18.06.2014 02:31, Michael Terry wrote:
> OK, playing around with it, I couldn't make anything besides the close-every-request thing work. Try the following command:
>
> ./testing/manual/backendtest 'webdavs://demo:<email address hidden>/demo/duplicity-testing'
>
> This fails in a few places for me without the close behavior in this tree. I'm sure there's a more elegant fix, but I haven't found it yet. Any ideas? Do you see the same bugs I do with the command?
>

give me some time to look at it.. ede

Revision history for this message
Michael Terry (mterry) wrote :

> what does setdata() do anyway? ..ede

Basically just a stat() on the backing file for the Path object. Fills out the fields that tell if the file exists, is writable, etc.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'duplicity/backend.py'
2--- duplicity/backend.py 2014-05-11 05:43:32 +0000
3+++ duplicity/backend.py 2014-06-17 21:56:30 +0000
4@@ -538,10 +538,10 @@
5 """Retrieve remote_filename and place in local_path"""
6 if hasattr(self.backend, '_get'):
7 self.backend._get(remote_filename, local_path)
8+ local_path.setdata()
9 if not local_path.exists():
10 raise BackendException(_("File %s not found locally after get "
11 "from backend") % util.ufn(local_path.name))
12- local_path.setdata()
13 else:
14 raise NotImplementedError()
15
16
17=== modified file 'duplicity/backends/webdavbackend.py'
18--- duplicity/backends/webdavbackend.py 2014-05-07 21:41:31 +0000
19+++ duplicity/backends/webdavbackend.py 2014-06-17 21:56:30 +0000
20@@ -161,7 +161,7 @@
21 and self.conn.host == self.parsed_url.hostname: return
22
23 log.Info("WebDAV create connection on '%s'" % (self.parsed_url.hostname))
24- if self.conn: self.conn.close()
25+ self._close()
26 # http schemes needed for redirect urls from servers
27 if self.parsed_url.scheme in ['webdav','http']:
28 self.conn = httplib.HTTPConnection(self.parsed_url.hostname, self.parsed_url.port)
29@@ -174,13 +174,15 @@
30 raise FatalBackendException("WebDAV Unknown URI scheme: %s" % (self.parsed_url.scheme))
31
32 def _close(self):
33- self.conn.close()
34+ if self.conn:
35+ self.conn.close()
36
37 def request(self, method, path, data=None, redirected=0):
38 """
39 Wraps the connection.request method to retry once if authentication is
40 required
41 """
42+ self._close() # or we get previous request's data or exception
43 self.connect()
44
45 quoted_path = urllib.quote(path,"/:~")
46@@ -309,7 +311,6 @@
47 for i in range(1,len(dirs)):
48 d="/".join(dirs[0:i+1])+"/"
49
50- self._close() # or we get previous request's data or exception
51 self.headers['Depth'] = "1"
52 response = self.request("PROPFIND", d)
53 del self.headers['Depth']
54@@ -318,12 +319,10 @@
55
56 if response.status == 404:
57 log.Info("Creating missing directory %s" % d)
58- self._close() # or we get previous request's data or exception
59
60 res = self.request("MKCOL", d)
61 if res.status != 201:
62 raise BackendException("WebDAV MKCOL %s failed: %s %s" % (d,res.status,res.reason))
63- self._close()
64
65 def taste_href(self, href):
66 """
67
68=== modified file 'testing/manual/backendtest'
69--- testing/manual/backendtest 2014-05-11 11:50:12 +0000
70+++ testing/manual/backendtest 2014-06-17 21:56:30 +0000
71@@ -52,6 +52,7 @@
72 def setUp(self):
73 super(ManualBackendBase, self).setUp()
74 self.set_global('num_retries', 1)
75+ self.set_global('ssl_no_check_certificate', True)
76 self.setBackendInfo()
77 if self.password is not None:
78 self.set_environ("FTP_PASSWORD", self.password)

Subscribers

People subscribed via source and target branches