コード例 #1
0
def main():

    settings = get_settings()

    conn = psycopg2.connect(**parse_pgurl(settings.db_url))
    conn.set_isolation_level(psycopg2.extensions.ISOLATION_LEVEL_AUTOCOMMIT)

    rabbit_conn = pika.BlockingConnection(
        pika.URLParameters(settings.rabbit_url))
    rabbit = rabbit_conn.channel()
    exchange = settings['state_exchange']
    rabbit.exchange_declare(exchange=exchange, type='topic', durable=True)

    pubsub = pgpubsub.PubSub(conn)
    pubsub.listen(PG_CHANNEL)

    logger.info('Listening on Postgres channel "%s"' % PG_CHANNEL)

    for event in pubsub.events():
        payload = json.loads(event.payload)
        table = payload['tablename']

        with conn.cursor() as curs:
            # Handle payloads too big for a PG NOTIFY.
            if payload.get('error') == 'too long':
                payload = get_record_as_json(curs, table, payload['id'])

            # add service_name and pipeline_name where applicable, doing
            # another DB lookup if necessary.
            extra = extra_data(curs, table, payload['id'])
        payload.update(extra)
        publish_event(rabbit, exchange, payload)
コード例 #2
0
def send_email(to, subj, body):
    """
    Send an email using the server specified by the 'smtp_url' setting.

    'to' should be a list of addresses.

    'from' should be a single address.

    Any address may be provided as a plain string like '*****@*****.**', or
    as a tuple like ('Joe Somebody', '*****@*****.**').
    """
    settings = get_settings()

    msg = MIMEText(body)
    msg['From'] = format_email_address(settings.smtp_sender)
    msg['Subject'] = subj
    for r in to:
        addr = format_email_address(r)
        msg.add_header('To', addr)

    logger.info('Sending email "{subject}" to {recipients}.'.format(
        subject=subj,
        recipients=', '.join([str(x) for x in to]),
    ))

    if settings.smtp_url is not None:
        with SMTPServer(settings.smtp_url) as server:
            server.sendmail(just_email_address(settings.smtp_sender),
                            msg.get_all('To'), msg.as_string())
    else:
        logger.warning('settings.smtp_url is None.  Email not sent!')
コード例 #3
0
def main():
    settings = get_settings()

    rabbit_conn = pika.BlockingConnection(
        pika.URLParameters(settings.rabbit_url))
    rabbit = rabbit_conn.channel()
    mp.declare_exchanges(rabbit)
    queue_name = 'mettle_job_logs'
    rabbit.queue_declare(queue=queue_name, exclusive=False, durable=True)
    rabbit.queue_bind(exchange=mp.JOB_LOGS_EXCHANGE,
                      queue=queue_name,
                      routing_key='#')
    logger.info('Bound exchange %s to queue %s' %
                (mp.JOB_LOGS_EXCHANGE, queue_name))

    Session = make_session_cls(settings.db_url)
    for method, properties, body in rabbit.consume(queue=queue_name):
        db = Session()
        data = json.loads(body)
        job_id = data['job_id']
        line_num = data['line_num']
        message = data['msg']
        try:
            db.add(
                JobLogLine(
                    job_id=job_id,
                    line_num=line_num,
                    message=message,
                ))
            db.commit()
            logger.info(message)
        except IntegrityError:
            # We probably got a duplicate log line, which can happen given
            # Rabbit retries.  Query DB for log line matching job_id and
            # line_num.  If we have one, and it is the same message, then just
            # carry on.  If the message is different, then log an error.
            db.rollback()
            existing_line = db.query(JobLogLine).filter_by(
                job_id=job_id, line_num=line_num).one()
            if existing_line.message != message:
                err = """Job {job_id}, log line {num} is stored as
                this:\n{old}\n\n but the queue has just produced a new message
                for the same line, with this value:\n{new}"""

                logger.error(
                    textwrap.dedent(err).format(
                        job_id=job_id,
                        num=line_num,
                        old=existing_line.message,
                        new=message,
                    ))

        rabbit.basic_ack(method.delivery_tag)
コード例 #4
0
ファイル: dispatcher.py プロジェクト: thinkwelltwd/mettle
def main():
    settings = get_settings()

    rabbit_conn = pika.BlockingConnection(
        pika.URLParameters(settings.rabbit_url))
    rabbit = rabbit_conn.channel()
    mp.declare_exchanges(rabbit)
    queue_name = 'mettle_dispatcher'

    rabbit.queue_declare(queue=queue_name, exclusive=False, durable=True)
    rabbit.queue_bind(exchange=mp.ANNOUNCE_SERVICE_EXCHANGE,
                      queue=queue_name,
                      routing_key='#')
    rabbit.queue_bind(exchange=mp.ACK_PIPELINE_RUN_EXCHANGE,
                      queue=queue_name,
                      routing_key='#')
    rabbit.queue_bind(exchange=mp.NACK_PIPELINE_RUN_EXCHANGE,
                      queue=queue_name,
                      routing_key='#')
    rabbit.queue_bind(exchange=mp.CLAIM_JOB_EXCHANGE,
                      queue=queue_name,
                      routing_key='#')
    rabbit.queue_bind(exchange=mp.END_JOB_EXCHANGE,
                      queue=queue_name,
                      routing_key='#')
    rabbit.queue_bind(exchange=settings.dispatcher_ping_exchange,
                      queue=queue_name,
                      routing_key='timer')

    Session = make_session_cls(settings.db_url)

    for method, properties, body in rabbit.consume(queue=queue_name):
        db = Session()
        if method.exchange == mp.ANNOUNCE_SERVICE_EXCHANGE:
            on_announce_service(settings, db, json.loads(body))
        elif method.exchange == mp.ACK_PIPELINE_RUN_EXCHANGE:
            on_pipeline_run_ack(settings, rabbit, db, json.loads(body))
        elif method.exchange == mp.NACK_PIPELINE_RUN_EXCHANGE:
            on_pipeline_run_nack(settings, rabbit, db, json.loads(body))
        elif method.exchange == mp.CLAIM_JOB_EXCHANGE:
            on_job_claim(settings, rabbit, db, json.loads(body),
                         properties.correlation_id)
        elif method.exchange == mp.END_JOB_EXCHANGE:
            on_job_end(settings, rabbit, db, json.loads(body))
        # get messages from process timer restart queue
        elif method.exchange == settings.dispatcher_ping_exchange:
            db.merge(Checkin(proc_name='dispatcher', time=utc.now()))
        db.commit()
        rabbit.basic_ack(method.delivery_tag)
コード例 #5
0
ファイル: test_publisher.py プロジェクト: thinkwelltwd/mettle
def test_long_routing_key():
    settings = get_settings()
    conn = pika.BlockingConnection(pika.URLParameters(settings.rabbit_url))
    chan = conn.channel()
    exchange = settings['state_exchange']
    chan.exchange_declare(exchange=exchange, type='topic', durable=True)

    with pytest.raises(ValueError):
        publish_event(
            chan, exchange,
            dict(
                description=None,
                tablename='a' * 8000,
                name="foo",
                pipeline_names=None,
                id=15,
                updated_by='vagrant',
            ))
コード例 #6
0
def run():
    settings = get_settings()
    conn = psycopg2.connect(**parse_pgurl(settings.db_url))
    conn.autocommit = True
    cur = conn.cursor()
    migrations_folder = resource_filename('mettle', 'migrations')
    migration_names = [
        i for i in os.listdir(migrations_folder)
        if os.path.isdir(os.path.join(migrations_folder, i))
    ]

    logger.info('Found migrations: ' + ', '.join(migration_names))

    try:
        cur.execute('SELECT name FROM migration_history ORDER BY name;')
        completed_migrations = [m[0] for m in cur]
    except psycopg2.ProgrammingError:
        # The first migration creates the migration_history table.  So the query
        # on that table will fail if we have never run migrations.
        completed_migrations = []

    logger.info('Already run: ' + ', '.join(completed_migrations))

    to_run = sorted(list(
        set(migration_names).difference(completed_migrations)))

    if not len(to_run):
        logger.info('No migrations need running.')
        return
    logger.info('Migrations to run: ' + ', '.join(to_run))

    for m in to_run:
        logger.info('Running %s.' % m)
        script = os.path.join(migrations_folder, m, 'forward.sql')
        sql = open(script).read()
        cur.execute(sql)
コード例 #7
0
def main():
    settings = get_settings()
    session = make_session_cls(settings.db_url)()

    services = {}
    for service_name, service_data in settings.stub_services.items():
        service = session.query(Service).filter_by(name=service_name).first()
        if not service:
            logger.info('Making service %s' % service_name)
            service = Service(
                name=service_name,
                updated_by='datastub',
                pipeline_names=service_data['pipeline_names'],
            )
            session.add(service)
        services[service.name] = service

    nl_lists = {}

    for nl_data in settings.stub_notification_lists:
        nl = session.query(NotificationList).filter_by(
            name=nl_data['name'],
        ).first()

        if not nl:
            logger.info('Making notification list %s' % nl_data['name'])
            nl = NotificationList(
                name=nl_data['name'],
                updated_by='datastub',
            )
            session.add(nl)
        nl.recipients = nl_data['recipients']
        nl_lists[nl.name] = nl

    pipelines = {}
    for pl_data in settings.stub_pipelines:
        pipeline = session.query(Pipeline).filter_by(
            name=pl_data['name'],
            service=services[pl_data['service']],
        ).first()

        if not pipeline:
            logger.info('Making pipeline %s' % pl_data['name'])
            pipeline = Pipeline(
                name=pl_data['name'],
                service=services[pl_data['service']],
                updated_by='datastub',
            )
        if 'crontab' in pl_data:
            pipeline.crontab = pl_data['crontab']
        elif 'chained_from' in pl_data:
            pipeline.chained_from=pipelines[pl_data['chained_from']]
        session.add(pipeline)
        pipeline.notification_list = nl_lists[pl_data['notification_list']]
        pipelines[pl_data['name']] = pipeline

    # query for all pipelines.  any not in the data stub should be made
    # inactive.
    for pipeline in session.query(Pipeline):
        if pipeline.name not in pipelines:
            logger.info('Deactivating unstubbed pipeline "%s".' % pipeline.name)
            pipeline.active = False

    session.commit()

    logger.info('Sleeping')
    sys.stdin.read()
コード例 #8
0
def main():
    settings = get_settings()
    while True:
        do_scheduled_tasks(settings)
        logger.info("Sleeping for %s seconds" % settings.timer_sleep_secs)
        time.sleep(settings.timer_sleep_secs)
コード例 #9
0
ファイル: test_api.py プロジェクト: thinkwelltwd/mettle
def get_db():
    settings = get_settings()
    return make_session_cls(settings.db_url)