Write a script to send emails before koji cert expirations #146

ralphbean opened this Issue Jun 12, 2013 · 9 comments


None yet
3 participants

ralphbean commented Jun 12, 2013

This can query datanommer for "who got a new cert 6months - 1 week ago".

This depends on #145 being in place first.


tyll commented Sep 24, 2013

you need to validate that only the last request is used per user, otherwise there would be unnecessary noise if a user requests two certs within 6 months.


ralphbean commented Apr 9, 2014

How about something like this that we can keep in puppet/ansible and run as a weekly cronjob.

""" Send emails to Fedora users whose koji certs are about to expire.

We first get a list of Fedora users in the cla_done group.  Then we query
datagrepper for the history of when each user last changed their cert.  If that
event occurred inside a window (between 5.75 months ago and 6 months ago), then
send them an email letting them know their cert is about to expire.

Requires:   python-arrow python-fedora python-requests fedmsg
License:    LGPLv2+
Authors:    Ralph Bean <rbean@redhat.com>

import arrow
import datetime
import email
import fedmsg
import fedora.client.fas2
import getpass
import smtplib
import requests

# This is a flag used to turn of email to the actual users

datagrepper_url = 'https://apps.fedoraproject.org/datagrepper/raw'

mail_server = 'bastion.fedoraproject.org'
message_template = """{human_name}/{username}:

This is an automated email sent to inform you that your Fedora Project Koji
certificate is about to expire.  Koji certificates are valid for 6 months and
our records indicate that you last recreated yours about {change_human}
on {change_date}.

Please run the following command to regenerate your certificate:

    $ /usr/bin/fedora-cert -n

For more information, see the following wiki page:

# We want to alert users if their cert is going to expire this week.
now = arrow.utcnow()
six_months = 1.57785e7
one_week = 604800

window_delta = one_week
window_max = six_months
window_min = window_max - window_delta

def cert_changes(user):
    """ Generator that returns all the koji cert changes for a user.

    >>> user = 'ralph'
    >>> for change in cert_changes(user):
    ...     print change.humanize(), "on", change.format('YYYY-MM-DD')
    21 hours ago on 2014-04-08
    2 months ago on 2014-02-09
    8 months ago on 2013-08-12


    def get_page(page):
        params = dict(
        return requests.get(datagrepper_url, params=params).json()

    data = get_page(1)
    pages = data['pages']

    for page in range(1, pages + 1):
        data = get_page(page)
        for message in data['raw_messages']:
            if 'certificate' in message['msg']['fields']:
                yield arrow.get(message['timestamp'])

def test_cert_changes():
    """ Just messing around... """
    for user in ['kevin', 'ralph', 'lmacken', 'pingou']:
        for change in cert_changes(user):
            print user, change.humanize(), "on", change.format('YYYY-MM-DD')

def fedora_users(credentials):
    return fedora.client.fas2.AccountSystem(

def total_seconds(td):
    """ Take a datetime.timedelta object and return the total seconds.

    td.total_seconds() exists in the python 2.7 stdlib, but not in python 2.6.
    return td.days * 24 * 60 * 60 + td.seconds + td.microseconds / 1000000.0

def to_address(user):
        return 'ralph@fedoraproject.org'
        return user['email']

def send_email(user, last_change):
    print "send an email to %r since they last changed on %r" % (
        user, last_change.format('YYYY-MM-DD'))

    message = email.Message.Message()
    message.add_header('To', to_address(user))
    message.add_header('From', 'admin@fedoraproject.org')
    subject = 'Your Koji certificate expires within a week'
    message.add_header('Subject', subject)

    content = message_template.format(

    server = smtplib.SMTP(mail_server)

def main(credentials):
    print "* Querying FAS for a list of users"
    users = fedora_users(credentials)
    print "* Found %r people" % len(users)
    for user in users:
        #print "* Querying datagrepper for %r." % user['username'],
        changes = cert_changes(user['username'])

            latest = changes.next()
        except StopIteration:
            # Then the user has no changes in the fedmsg history.
            #print "No record of %r changing a cert." % user['username']

        print user['username'], "changed", latest.humanize(),
        print "on", latest.format('YYYY-MM-DD')

        delta = total_seconds(now - latest)
        if delta >= window_min and delta <= window_max:
            send_email(user, latest)

if __name__ == '__main__':
    # Load credentials from /etc/fedmsg.d/
    config = fedmsg.config.load_config()

    if 'fas_credentials' not in config:
        print "No 'fas_credentials' found in `fedmsg-config`..."
        username = raw_input("Enter your fas username: ")
        password = getpass.getpass("Enter your fas password: ")
        config['fas_credentials'] = dict(username=username, password=password)


tyll commented Apr 9, 2014

There seems to be an
import arrow
I would change the Subject to be "Your Koji certificate expires within a week". Also I am not sure whether just encoding to utf-8 is correct for the sending the mail. Other than that it looks good at first sight.


ralphbean commented Apr 9, 2014

Thanks! I fixed the import arrow and the subject.

I'm pretty sure the utf-8 encoding works. I took the code from some code I was working on this winter over here.


pypingou commented Apr 10, 2014

/usr/bin/fedora-packager-setup vs fedora-cert -n?


ralphbean commented Apr 10, 2014

Is fedora-cert -n documented on the wiki? If so, where?


pypingou commented Apr 10, 2014

Good point, it doesn't seem to be very well documented, the only place I found it is at: http://fedoraproject.org/wiki/Package_update_HOWTO#Build_a_package_for_Rawhide

ralphbean closed this Apr 28, 2014

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment