~evarlast/cloud-init/cloud-init

« back to all changes in this revision

Viewing changes to debian/patches/lp-978127-maas-oauth-fix-bad-clock.patch

  • Committer: Package Import Robot
  • Author(s): Scott Moser
  • Date: 2012-11-12 17:01:54 UTC
  • Revision ID: package-import@ubuntu.com-20121112170154-37170939vlg68nst
Tags: 0.6.3-0ubuntu1.2
* debian/patches/lp-978127-maas-oauth-fix-bad-clock.patch: fix usage of
  oauth in maas data source if local system has a bad clock (LP: #978127)
* debian/cloud-init.preinst: fix bug where user data scripts re-ran on
  upgrade from 10.04 versions (LP: #1049146)
* debian/patches/lp-974509-detect-dns-server-redirection.patch: detect dns
  server redirection and disable searching dns for a mirror named
  'ubuntu-mirror' (LP: #974509)
* debian/patches/lp-1018554-shutdown-message-to-console.patch: write a
  message to the console on system shutdown. (LP: #1018554)
* debian/patches/lp-1066115-landscape-install-fix-perms.patch: install
  landscape package if needed which will ensure proper permissions on config
  file (LP: #1066115).
* debian/patches/lp-1070345-landscape-restart-after-change.patch: restart
  landscape after modifying config (LP: #1070345)
* debian/patches/lp-1073077-zsh-workaround-for-locale_warn.patch: avoid
  warning when user's shell is zsh (LP: #1073077)
* debian/patches/rework-mirror-selection.patch: improve mirror selection by:
  * allowing region/availability-zone to be part of mirror (LP: #1037727)
  * making mirror selection arch aware (LP: #1028501)
  * allow specification of a security mirror (LP: #1006963)

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
Author: Scott Moser <smoser@ubuntu.com>
 
2
Bug: https://launchpad.net/bugs/978127
 
3
Applied-Upstream: revno 666 and 678
 
4
Description:  DataSourceMAAS: adjust oauth request timestamps on 401 or 403
 
5
 In the event of a 401 or 403 (Unauthorized) in oauth, try set a
 
6
 'oauth_clockskew' variable.  In future headers, use a time created by
 
7
 'time.time() + self.oauth_clockskew'.  The idea here is that if the local time
 
8
 is bad (or even if the server time is bad) we will essentially use something
 
9
 that should be similar to the remote clock.
 
10
--- a/cloudinit/DataSourceMAAS.py
 
11
+++ b/cloudinit/DataSourceMAAS.py
 
12
@@ -20,6 +20,7 @@ import cloudinit.DataSource as DataSourc
 
13
 
 
14
 from cloudinit import seeddir as base_seeddir
 
15
 from cloudinit import log
 
16
+from email.utils import parsedate
 
17
 import cloudinit.util as util
 
18
 import errno
 
19
 import oauth.oauth as oauth
 
20
@@ -29,6 +30,7 @@ import time
 
21
 
 
22
 
 
23
 MD_VERSION = "2012-03-01"
 
24
+LOG = log
 
25
 
 
26
 
 
27
 class DataSourceMAAS(DataSource.DataSource):
 
28
@@ -42,6 +44,7 @@ class DataSourceMAAS(DataSource.DataSour
 
29
     """
 
30
     seeddir = base_seeddir + '/maas'
 
31
     baseurl = None
 
32
+    oauth_clockskew = None
 
33
 
 
34
     def __str__(self):
 
35
         return("DataSourceMAAS[%s]" % self.baseurl)
 
36
@@ -92,9 +95,14 @@ class DataSourceMAAS(DataSource.DataSour
 
37
 
 
38
         consumer_secret = mcfg.get('consumer_secret', "")
 
39
 
 
40
+        timestamp = None
 
41
+        if self.oauth_clockskew:
 
42
+            timestamp = int(time.time()) + self.oauth_clockskew
 
43
+
 
44
         return(oauth_headers(url=url, consumer_key=mcfg['consumer_key'],
 
45
             token_key=mcfg['token_key'], token_secret=mcfg['token_secret'],
 
46
-            consumer_secret=consumer_secret))
 
47
+            consumer_secret=consumer_secret,
 
48
+            timestamp=timestamp))
 
49
 
 
50
     def wait_for_metadata_service(self, url):
 
51
         mcfg = self.ds_cfg
 
52
@@ -119,7 +127,8 @@ class DataSourceMAAS(DataSource.DataSour
 
53
         starttime = time.time()
 
54
         check_url = "%s/%s/meta-data/instance-id" % (url, MD_VERSION)
 
55
         url = util.wait_for_url(urls=[check_url], max_wait=max_wait,
 
56
-            timeout=timeout, status_cb=log.warn,
 
57
+            timeout=timeout, status_cb=LOG.warn,
 
58
+            exception_cb=self._except_cb,
 
59
             headers_cb=self.md_headers)
 
60
 
 
61
         if url:
 
62
@@ -130,6 +139,26 @@ class DataSourceMAAS(DataSource.DataSour
 
63
 
 
64
         return (bool(url))
 
65
 
 
66
+    def _except_cb(self, msg, exception):
 
67
+        if not (isinstance(exception, urllib2.HTTPError) and
 
68
+                (exception.code == 403 or exception.code == 401)):
 
69
+            return
 
70
+        if 'date' not in exception.headers:
 
71
+            LOG.warn("date field not in %d headers" % exception.code)
 
72
+            return
 
73
+
 
74
+        date = exception.headers['date']
 
75
+
 
76
+        try:
 
77
+            ret_time = time.mktime(parsedate(date))
 
78
+        except:
 
79
+            LOG.warn("failed to convert datetime '%s'")
 
80
+            return
 
81
+
 
82
+        self.oauth_clockskew = int(ret_time - time.time())
 
83
+        LOG.warn("set oauth clockskew to %d" % self.oauth_clockskew)
 
84
+        return
 
85
+
 
86
 
 
87
 def read_maas_seed_dir(seed_d):
 
88
     """
 
89
@@ -220,13 +249,20 @@ def check_seed_contents(content, seed):
 
90
     return(userdata, md)
 
91
 
 
92
 
 
93
-def oauth_headers(url, consumer_key, token_key, token_secret, consumer_secret):
 
94
+def oauth_headers(url, consumer_key, token_key, token_secret, consumer_secret,
 
95
+                  timestamp=None):
 
96
     consumer = oauth.OAuthConsumer(consumer_key, consumer_secret)
 
97
     token = oauth.OAuthToken(token_key, token_secret)
 
98
+
 
99
+    if timestamp is None:
 
100
+        ts = int(time.time())
 
101
+    else:
 
102
+        ts = timestamp
 
103
+
 
104
     params = {
 
105
         'oauth_version': "1.0",
 
106
         'oauth_nonce': oauth.generate_nonce(),
 
107
-        'oauth_timestamp': int(time.time()),
 
108
+        'oauth_timestamp': ts,
 
109
         'oauth_token': token.key,
 
110
         'oauth_consumer_key': consumer.key,
 
111
     }
 
112
--- a/cloudinit/util.py
 
113
+++ b/cloudinit/util.py
 
114
@@ -756,7 +756,7 @@ def mount_callback_umount(device, callba
 
115
 
 
116
 
 
117
 def wait_for_url(urls, max_wait=None, timeout=None,
 
118
-                 status_cb=None, headers_cb=None):
 
119
+                 status_cb=None, headers_cb=None, exception_cb=None):
 
120
     """
 
121
     urls:      a list of urls to try
 
122
     max_wait:  roughly the maximum time to wait before giving up
 
123
@@ -766,6 +766,8 @@ def wait_for_url(urls, max_wait=None, ti
 
124
     status_cb: call method with string message when a url is not available
 
125
     headers_cb: call method with single argument of url to get headers
 
126
                 for request.
 
127
+    exception_cb: call method with 2 arguments 'msg' (per status_cb) and
 
128
+                  'exception', the exception that occurred.
 
129
 
 
130
     the idea of this routine is to wait for the EC2 metdata service to
 
131
     come up.  On both Eucalyptus and EC2 we have seen the case where
 
132
@@ -817,9 +819,15 @@ def wait_for_url(urls, max_wait=None, ti
 
133
 
 
134
                 req = urllib2.Request(url, data=None, headers=headers)
 
135
                 resp = urllib2.urlopen(req, timeout=timeout)
 
136
-                if resp.read() != "":
 
137
+                contents = resp.read()
 
138
+                if not contents:
 
139
+                    reason = "empty data [%s]" % (resp.code)
 
140
+                    e = ValueError(reason)
 
141
+                elif not (resp.code >= 200 and resp.code < 400):
 
142
+                    reason = "bad status code [%s]" % (resp.code)
 
143
+                    e = ValueError(reason)
 
144
+                else:
 
145
                     return url
 
146
-                reason = "empty data [%s]" % resp.getcode()
 
147
             except urllib2.HTTPError as e:
 
148
                 reason = "http error [%s]" % e.code
 
149
             except urllib2.URLError as e:
 
150
@@ -829,9 +837,12 @@ def wait_for_url(urls, max_wait=None, ti
 
151
             except Exception as e:
 
152
                 reason = "unexpected error [%s]" % e
 
153
 
 
154
-            status_cb("'%s' failed [%s/%ss]: %s" %
 
155
-                      (url, int(time.time() - starttime), max_wait,
 
156
-                       reason))
 
157
+            status_msg = ("'%s' failed [%s/%ss]: %s" %
 
158
+                          (url, int(time.time() - starttime), max_wait,
 
159
+                           reason))
 
160
+            status_cb(status_msg)
 
161
+            if exception_cb:
 
162
+                exception_cb(msg=status_msg, exception=e)
 
163
 
 
164
         if timeup(max_wait, starttime):
 
165
             break