~jcsackett/charmworld/bac-tag-constraints

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
185
186
187
188
# Copyright 2012, 2013 Marco Ceppi, Canonical Ltd.  This software is
# licensed under the GNU Affero General Public License version 3 (see
# the file LICENSE).

import argparse
from datetime import (
    datetime,
)
import logging
import pymongo

from charmworld.charmstore import (
    CharmStore,
    get_store_charm_info,
)
from charmworld.jobs.ingest import update_from_store
from charmworld.lp import (
    BASKET_SERIES,
    get_branch_tips,
)
from charmworld.models import (
    getconnection,
    getdb,
)
from charmworld.utils import (
    configure_logging,
    get_ini,
)
from config import (
    BASKET_QUEUE,
    CHARM_IMPORT_FILTER,
    CHARM_QUEUE,
)
from utils import get_queue
from utils import lock
from utils import LockHeld
from utils import parse_branch


default = object()


def all_branch_data(charm_data=None):
    # Return data needed for the ingest job for charm branches on Launchpad.
    log = logging.getLogger("charm.launchpad")
    charms = []
    baskets = []
    if not charm_data:
        charm_data = get_branch_tips()

    for repo, commit, series in charm_data:
        _, branch_name = repo.rsplit("/", 1)

        data = parse_branch(repo, series, commit)

        data['promulgated'] = data['series'] in data['distro_series']

        if data['series'] == BASKET_SERIES:
            if data['bname'] == 'bundle':
                baskets.append(data)
            continue

        if data["bname"] != "trunk":
            log.debug("Skipped branch %s", repo)
            continue

        data['branch_deleted'] = False
        charms.append(data)
    return charms, baskets


def db_charms(db):
    # Return data needed for the ingest job for charms stored in
    # charmworld's database.
    for charm in db.charms.find(
            fields=('owner', 'series', 'name', 'bname', 'branch_spec'),
            sort=[('branch_spec', pymongo.ASCENDING)]):
        # Override the promulgation related settings: Data from this
        # iterator is used by all_charms() only when the charm has no
        # longer a related Launchpad branch. A charm is promulgated if
        # its LP branch is linked to a sourcepackage, hence a charm
        # without a branch cannot be promulgated.
        charm['promulgated'] = False
        charm['distro_series'] = []
        charm['branch_deleted'] = True
        yield charm


def all_charms(db, available, limit=None, import_filter=default):
    # Return a sequence of charms that have a Launchpad branch or that are
    # already known in charmworld's database.
    log = logging.getLogger("charm.launchpad")
    if import_filter is default:
        import_filter = CHARM_IMPORT_FILTER
    all = dict((charm['branch_spec'], charm) for charm in db_charms(db))
    all.update((charm['branch_spec'], charm) for charm in available)
    all = sorted(all.values(), key=lambda charm: charm['branch_spec'])

    count = 0
    for charm in all:
        if import_filter and not charm['branch_spec'].startswith(
                import_filter):
            continue
        log.info("Queueing %s", charm['branch_spec'])
        yield charm
        count += 1
        if limit and count >= limit:
            log.info("Import limit reached (%d)... stopping", limit)
            break


def queue_from_branches(db, store, charm_queue, basket_queue, limit=None,
                        import_filter=default, check_time=None):
    log = logging.getLogger("charm.launchpad")
    charm_data, baskets = all_branch_data()
    charms = list(all_charms(db, charm_data, limit=limit,
                             import_filter=import_filter))
    charms_info = get_store_charm_info(store, charms)
    if check_time is None:
        check_time = datetime.now().ctime()
    for charm, (address, store_data) in zip(charms, charms_info):
        update_from_store(charm, address, store_data, check_time, log)
        added = charm_queue.put(charm)
        if added and 0:
            log.info("Queued %s", charm)
    for basket in baskets:
        basket_queue.put(basket)


def dequeue():
    """Dequeue all the charm data in the CHARM_QUEUE.

    This is a script entry point to dequeue all charms. This might be done when
    when a running instance will get a code update that is incompatible with
    charm data in the queue.
    """
    configure_logging()
    log = logging.getLogger("charm.launchpad")
    settings = get_ini()
    connection = getconnection(settings)
    db = getdb(connection, settings.get('mongo.database'))
    try:
        lease_time = int(settings['script_lease_time']) * 60
        with lock('ingest-queue', lease_time, db, log):
            queue = get_queue(db, CHARM_QUEUE)
            queue_size = queue.size()
            queue.clear()
            log.info("Dequeued %s charms", queue_size)
    except LockHeld, error:
        log.warn(str(error))


# XXX bug=1130732: This function does not appear to be tested.
def main():
    configure_logging()
    parser = argparse.ArgumentParser()
    parser.add_argument(
        "--limit",
        help="Maximum number of charms to queue.",
        type=int)
    parser.add_argument(
        "--prefix",
        help="Only charms matching this prefix are queued.",
        type=str, default=default)
    args = parser.parse_args()
    log = logging.getLogger("charm.launchpad")
    settings = get_ini()
    if args.limit is None:
        charm_import_limit = settings.get('charm_import_limit')
        charm_import_limit = (
            int(charm_import_limit) if charm_import_limit else None)
    else:
        charm_import_limit = args.limit
    connection = getconnection(settings)
    db = getdb(connection, settings.get('mongo.database'))
    try:
        with lock('ingest-queue', int(settings['script_lease_time']) * 60,
                  db, log):
            charm_queue = get_queue(db, CHARM_QUEUE)
            basket_queue = get_queue(db, BASKET_QUEUE)
            queue_from_branches(db, CharmStore(), charm_queue, basket_queue,
                                charm_import_limit, args.prefix)
    except LockHeld, error:
        log.warn(str(error))


if __name__ == '__main__':
    main()