57
def create_ini_file():
61
def create_ini_file(port="0"):
58
62
"""Write CouchDB ini file if not already present"""
59
# FIXME add update trigger folder
60
#update_trigger_dir = [
61
# 'lib', 'canonical', 'ubuntuone', 'cloud_server', 'update_triggers']
63
#timestamp_trigger = os.path.join(
64
# *update_trigger_dir + ['timestamp_trigger.py'])
65
#update_trigger = os.path.join(
66
# *update_trigger_dir + ['update_trigger.py'])
68
63
if os.path.exists(local_files.FILE_INI):
64
# load the username and password from the keyring
66
data = gnomekeyring.find_items_sync(gnomekeyring.ITEM_GENERIC_SECRET,
67
{'desktopcouch': 'basic'})
68
except gnomekeyring.NoMatchError:
71
username, password = data[0].secret.split(":")
72
return username, password
73
# otherwise fall through; for some reason the access details aren't
74
# in the keyring, so re-create the ini file and do it all again
76
# randomly generate tokens and usernames
77
def make_random_string(count):
79
random.SystemRandom().choice(ACCEPTABLE_USERNAME_PASSWORD_CHARS)
80
for x in range(count)])
82
admin_account_username = make_random_string(10)
83
admin_account_basic_auth_password = make_random_string(10)
84
consumer_key = make_random_string(10)
85
consumer_secret = make_random_string(10)
86
token = make_random_string(10)
87
token_secret = make_random_string(10)
88
db_dir = local_files.DIR_DB
73
'database_dir': local_files.DIR_DB,
74
'view_index_dir': local_files.DIR_DB,
92
'database_dir': db_dir,
93
'view_index_dir': db_dir,
77
96
'bind_address': '127.0.0.1',
81
100
'file': local_files.FILE_LOG,
104
## admin_account_username: admin_account_basic_auth_password
106
'oauth_consumer_secrets': {
107
consumer_key: consumer_secret
109
'oauth_token_secrets': {
112
'oauth_token_users': {
113
token: admin_account_username
115
## 'couch_httpd_auth': {
116
## 'require_valid_user': 'true'
86
120
dump_ini(local, local_files.FILE_INI)
121
# save admin account details in keyring
122
item_id = gnomekeyring.item_create_sync(
124
gnomekeyring.ITEM_GENERIC_SECRET,
125
'Desktop Couch user authentication',
126
{'desktopcouch': 'basic'},
128
admin_account_username, admin_account_basic_auth_password),
131
item_id = gnomekeyring.item_create_sync(
133
gnomekeyring.ITEM_GENERIC_SECRET,
134
'Desktop Couch user authentication',
135
{'desktopcouch': 'oauth'},
137
consumer_key, consumer_secret, token, token_secret),
139
return (admin_account_username, admin_account_basic_auth_password)
88
141
def run_couchdb():
89
142
"""Actually start the CouchDB process"""
90
143
local_exec = local_files.COUCH_EXEC_COMMAND + ['-b']
92
retcode = subprocess.call(local_exec, shell=False)
145
# subprocess is buggy. Chad patched, but that takes time to propagate.
146
proc = subprocess.Popen(local_exec)
149
retcode = proc.wait()
152
if e.errno == errno.EINTR:
94
156
print >> sys.stderr, "Child was terminated by signal", -retcode
101
163
def update_design_documents():
102
"""Check system design documents and update any that need updating"""
105
def write_bookmark_file():
164
"""Check system design documents and update any that need updating
166
A database should be created if
167
$XDG_DATA_DIRs/desktop-couch/databases/dbname/database.cfg exists
168
Design docs are defined by the existence of
169
$XDG_DATA_DIRs/desktop-couch/databases/dbname/_design/designdocname/views/viewname/map.js
170
reduce.js may also exist in the same folder.
172
for base in xdg.BaseDirectory.xdg_data_dirs:
173
db_spec = os.path.join(
174
base, "desktop-couch", "databases", "*", "database.cfg")
175
for database_path in glob.glob(db_spec):
176
database_root = os.path.split(database_path)[0]
177
database_name = os.path.split(database_root)[1]
178
# Just the presence of database.cfg is enough to create the database
179
db = CouchDatabase(database_name, create=True)
180
# look for design documents
181
dd_spec = os.path.join(
182
database_root, "_design", "*", "views", "*", "map.js")
183
for dd_path in glob.glob(dd_spec):
184
view_root = os.path.split(dd_path)[0]
185
view_name = os.path.split(view_root)[1]
186
dd_root = os.path.split(os.path.split(view_root)[0])[0]
187
dd_name = os.path.split(dd_root)[1]
189
def load_js_file(filename_no_extension):
191
view_root, "%s.js" % (filename_no_extension))
192
if not os.path.isfile(fn): return None
198
mapjs = load_js_file("map")
199
reducejs = load_js_file("reduce")
201
# XXX check whether this already exists or not, rather
202
# than inefficiently just overwriting it regardless
203
db.add_view(view_name, mapjs, reducejs, dd_name)
205
def write_bookmark_file(username, password):
106
206
"""Write out an HTML document that the user can bookmark to find their DB"""
107
207
bookmark_file = os.path.join(local_files.DIR_DB, "couchdb.html")
109
if os.path.exists(os.path.join(os.path.split(__file__)[0], "../data/couchdb.tmpl")):
110
bookmark_template = os.path.join(os.path.split(__file__)[0], "../data/couchdb.tmpl")
210
os.path.join(os.path.split(__file__)[0], "../data/couchdb.tmpl")):
211
bookmark_template = os.path.join(
212
os.path.split(__file__)[0], "../data/couchdb.tmpl")
112
214
for base in xdg.BaseDirectory.xdg_data_dirs:
113
215
template_path = os.path.join(base, "desktopcouch", "couchdb.tmpl")
114
216
if os.path.exists(template_path):
115
bookmark_template = os.path.join(os.path.split(__file__)[0], template_path)
217
bookmark_template = os.path.join(
218
os.path.split(__file__)[0], template_path)
117
220
fp = open(bookmark_template)
122
pid = desktopcouch.find_pid()
123
port = desktopcouch.find_port(pid)
225
for retry in xrange(10000, 0, -1):
226
pid = desktopcouch.find_pid(start_if_not_running=False)
228
port = desktopcouch.find_port()
125
fp = open(bookmark_file, "w")
126
fp.write(html.replace("[[COUCHDB_PORT]]", port))
128
print "Browse your desktop CouchDB at file://%s" % \
129
os.path.realpath(bookmark_file)
238
print ("We couldn't find desktop-CouchDB's network port. Bookmark "
241
os.remove(bookmark_file)
245
fp = open(bookmark_file, "w")
246
out = html.replace("[[COUCHDB_PORT]]", str(port))
247
out = out.replace("[[COUCHDB_USERNAME]]", username)
248
out = out.replace("[[COUCHDB_PASSWORD]]", password)
251
print "Browse your desktop CouchDB at file://%s" % \
252
os.path.realpath(bookmark_file)
131
254
def start_couchdb():
132
255
"""Execute each step to start a desktop CouchDB"""
256
username, password = create_ini_file()
135
update_design_documents()
136
write_bookmark_file()
258
# Note that we do not call update_design_documents here. This is because
259
# Couch won't actually have started yet, so when update_design_documents
260
# calls the Records API, that will call back into get_port and we end up
261
# starting Couch again. Instead, get_port calls update_design_documents
262
# *after* Couch startup has occurred.
263
write_bookmark_file(username, password)
138
266
if __name__ == "__main__":