-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathconsul_options.py
184 lines (151 loc) · 5.64 KB
/
consul_options.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
# encoding: utf8
from __future__ import absolute_import
try:
from urlparse import urlparse
except ImportError:
from urllib.parse import urlparse
import consul.base
from consul.std import HTTPClient
from six import with_metaclass
class LazyHTTPClient(HTTPClient):
def setup(self, url=None, host=None, port=None, scheme=None):
if url:
o = urlparse(url.strip('/'))
self.host = o.hostname
self.port = o.port
self.scheme = o.scheme
self.base_uri = url
else:
self.host = host
self.port = port
self.scheme = scheme
self.base_uri = '%s://%s:%s' % (scheme, host, port)
class LazyConsul(consul.base.Consul):
def connect(self, host, port, scheme, verify=True, cert=None):
return LazyHTTPClient(host, port, scheme, verify, cert)
class KeyValue(object):
__root__ = False
def __getter__(self, key):
raise NotImplementedError
def __setter__(self, key, value):
raise NotImplementedError
def __getattribute__(self, item):
if item in ('__getter__', '__setter__', '__path__') +\
tuple(object.__getattribute__(self, '__dict__').keys()):
return object.__getattribute__(self, item)
value = self.__getter__(item)
if value is not None:
try:
default = object.__getattribute__(self, item)
value = str(value.decode('utf8')) # for compatibility
return adapt_value(value, type(default))
except AttributeError:
return value
else:
try:
value = object.__getattribute__(self, item)
except AttributeError:
path = self.__path__
full_path = '%s/%s' % (path, item) if path else item
msg = "There is no such key '%s'" % full_path
raise AttributeError(msg)
self.__setter__(item, value)
return value
@property
def __path__(self):
mro = object.__getattribute__(self, '__class__').mro()
path = []
for base in mro:
if getattr(base, '__root__'):
break
if base in (ConsulKV, CachedConsulKV):
break
else:
try:
name = object.__getattribute__(base, '__key__')
except AttributeError:
name = base.__name__.lower()
path.append(name)
return '/'.join(reversed(path))
class ConsulOptionsMeta(type):
def __init__(cls, name, bases, clsdict):
if name not in ('ConsulKV', 'CachedConsulKV'):
path = []
for c in cls.mro():
if c in (ConsulKV, CachedConsulKV):
break
else:
path.append(c)
po = ConsulOptions.__global__
is_root = getattr(cls, '__root__')
if is_root:
o = cls(po.__consul__)
po.__root_options__.append(o)
else:
for c in reversed(path):
try:
key = object.__getattribute__(c, '__key__')
except AttributeError:
key = object.__getattribute__(c, '__name__').lower()
try:
o = object.__getattribute__(po, key)
except AttributeError:
o = c(ConsulOptions.__global__.__consul__)
object.__setattr__(po, key, o)
object.__setattr__(o, '__key__', key)
po = o
super(ConsulOptionsMeta, cls).__init__(name, bases, clsdict)
class ConsulKV(with_metaclass(ConsulOptionsMeta, KeyValue)):
def __init__(self, server):
self.__consul__ = server
def __getter__(self, key):
path = self.__path__
path = path + '/' + key if path else key
index, data = self.__consul__.kv.get(path)
# index, data = None, None
value = data['Value'] if data else None
return value
def __setter__(self, key, value):
path = self.__path__
path = path + '/' + key if path else key
self.__consul__.kv.put(path, str(value))
class CachedConsulKV(with_metaclass(ConsulOptionsMeta, ConsulKV)):
def __init__(self, *args, **kwargs):
super(CachedConsulKV, self).__init__(*args, **kwargs)
self.__cache__ = {}
def __getter__(self, key):
value = self.__cache__.get(key)
if not value:
value = super(CachedConsulKV, self).__getter__(key)
self.__cache__[key] = value
return value
class ConsulOptions(object):
def __init__(self):
self.__consul__ = LazyConsul()
self.__root_options__ = []
def __getattr__(self, key):
for root_kv in self.__root_options__:
try:
value = getattr(root_kv, key)
return value
except AttributeError:
pass
else:
msg = "There is no such key '%s'" % key
raise AttributeError(msg)
def adapt_value(value, which_type):
if which_type == bool:
value = str(value).lower()
if value == 'true':
return True
elif value == 'false':
return False
else:
raise ValueError(value)
else:
return which_type(value)
def setup_consul(url=None, host=None, port=None, scheme=None):
options.__consul__.http.setup(url, host, port, scheme)
options = ConsulOptions()
ConsulOptions.__global__ = options
__all__ = ['options', 'setup_consul', 'ConsulKV', 'CachedConsulKV']