summaryrefslogtreecommitdiff
path: root/scripts/update_design_docs.py
blob: e7b5a29cff73e284fe037d1ae7fa834f10caad0d (plain)
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
#!/usr/bin/python

# This script updates Soledad's design documents in the session database and
# all user databases with contents from the installed leap.soledad.common
# package.

import json
import logging
import argparse
import re
import threading
import binascii


from getpass import getpass
from ConfigParser import ConfigParser
from couchdb.client import Server
from couchdb.http import Resource, Session
from datetime import datetime
from urlparse import urlparse


from leap.soledad.common import ddocs


# parse command line for the log file name
logger_fname = "/tmp/update-design-docs_%s.log" % \
               str(datetime.now()).replace(' ', '_')
parser = argparse.ArgumentParser()
parser.add_argument('--log', action='store', default=logger_fname, type=str,
                    required=False, help='the name of the log file', nargs=1)
args = parser.parse_args()


# configure the logger
logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)
print "Logging to %s." % args.log
logging.basicConfig(
    filename=args.log,
    format="%(asctime)-15s %(message)s")


# configure threads
max_threads = 20
semaphore_pool = threading.BoundedSemaphore(value=max_threads)
threads = []

# get couch url
cp = ConfigParser()
cp.read('/etc/leap/soledad-server.conf')
url = urlparse(cp.get('soledad-server', 'couch_url'))

# get admin password
netloc = re.sub('^.*@', '', url.netloc)
url = url._replace(netloc=netloc)
password = getpass("Admin password for %s: " % url.geturl())
url = url._replace(netloc='admin:%s@%s' % (password, netloc))

resource = Resource(url.geturl(), Session(retry_delays=[1,2,4,8], timeout=10))
server = Server(url=resource)

hidden_url = re.sub(
    'http://(.*):.*@',
    'http://\\1:xxxxx@',
    url.geturl())

print """
==========
ATTENTION!
==========

This script will modify Soledad's shared and user databases in:

  %s

This script does not make a backup of the couch db data, so make sure you
have a copy or you may loose data.
""" % hidden_url
confirm = raw_input("Proceed (type uppercase YES)? ")

if confirm != "YES":
    exit(1)

# convert design doc content

design_docs = {
    '_design/docs': json.loads(binascii.a2b_base64(ddocs.docs)),
    '_design/syncs': json.loads(binascii.a2b_base64(ddocs.syncs)),
    '_design/transactions': json.loads(binascii.a2b_base64(ddocs.transactions)),
}

#
# Thread
#

class DBWorkerThread(threading.Thread):

    def __init__(self, server, dbname, db_idx, db_len, release_fun):
        threading.Thread.__init__(self)
        self._dbname = dbname
        self._cdb = server[self._dbname]
        self._db_idx = db_idx
        self._db_len = db_len
        self._release_fun = release_fun

    def run(self):

        logger.info("(%d/%d) Updating db %s." % (self._db_idx, self._db_len,
                    self._dbname))

        for doc_id in design_docs:
            doc = self._cdb[doc_id]
            for key in ['lists', 'views', 'updates']:
                if key in design_docs[doc_id]:
                    doc[key] = design_docs[doc_id][key]
            self._cdb.save(doc)

        # release the semaphore
        self._release_fun()


db_idx = 0
db_len = len(server)
for dbname in server:

    db_idx += 1

    if not (dbname.startswith('user-') or dbname == 'shared') \
            or dbname == 'user-test-db':
        logger.info("(%d/%d) Skipping db %s." % (db_idx, db_len, dbname))
        continue


    # get access to couch db
    cdb = Server(url.geturl())[dbname]

    #---------------------------------------------------------------------
    # Start DB worker thread
    #---------------------------------------------------------------------
    semaphore_pool.acquire()
    thread = DBWorkerThread(server, dbname, db_idx, db_len, semaphore_pool.release)
    thread.daemon = True
    thread.start()
    threads.append(thread)

map(lambda thread: thread.join(), threads)