A “Bootstrapable” Koji : A “Feature” Bypassed

# ko build builds /tmp/nled-2.52-7.fc15.src.rpm
282 build (builds, nled-2.52-7.fc15.src.rpm) completed successfully

# ko build builds /tmp/nled-2.52-7.fc15.src.rpm
290 build (builds, nled-2.52-7.fc15.src.rpm) completed successfully

# ko build builds /tmp/nled-2.52-7.fc15.src.rpm 
298 build (builds, nled-2.52-7.fc15.src.rpm) completed successfully

# ko list-tagged builds nled
Build                                     Tag                   Built by
----------------------------------------  --------------------  ----------------
nled-2.52-7.267919.111107.fc15            builds                admin
nled-2.52-7.496d81.111107.fc15            builds                admin
nled-2.52-7.fc15                          builds                admin

# ls -l /mnt/koji/packages/nled/2.52
total 12
drwxr-xr-x 6 apache apache 4096 Nov  7 19:43 7.267919.111107.fc15
drwxr-xr-x 6 apache apache 4096 Nov  7 19:52 7.496d81.111107.fc15
drwxr-xr-x 6 apache apache 4096 Nov  7 20:01 7.fc15

...

# ko list-tagged builds --latest nled
Build                                     Tag                   Built by
----------------------------------------  --------------------  ----------------
nled-2.52-7.fc15                          builds                admin

# yum clean all ; yum --disablerepo=* --enablerepo=custom-test list nled
nled.x86_64  2.52-7.fc15  custom-test

Code change: ( Don’t forget: import hashlib )

> ./hub/kojihub.py

def new_build(data):
    """insert a new build entry"""
    tdic = data.copy()
    data = data.copy()
    if not data.has_key('pkg_id'):
        #see if there's a package name
        name = data.get('name')
        if not name:
            raise koji.GenericError, "No name or package id provided for build"
        data['pkg_id'] = new_package(name,strict=False)
    for f in ('version','release','epoch'):
        if not data.has_key(f):
            raise koji.GenericError, "No %s value for build" % f
    #provide a few default values
    data.setdefault('state',koji.BUILD_STATES['COMPLETE'])
    data.setdefault('completion_time', 'NOW')
    data.setdefault('owner',context.session.user_id)
    data.setdefault('task_id',None)
    #check for existing build
    # TODO - table lock?
    q="""SELECT id,state,task_id FROM build
    WHERE pkg_id=%(pkg_id)d AND version=%(version)s AND release=%(release)s
    FOR UPDATE"""
    row = _fetchSingle(q, data)
    if row:
        id, state, task_id = row
        data['id'] = id
        st_desc = koji.BUILD_STATES[state]
        if st_desc == 'BUILDING':
            koji.plugin.run_callbacks('preBuildStateChange', attribute='state', old=state, new=data['state'], info=data)
            # check to see if this is the controlling task
            if data['state'] == state and data.get('task_id','') == task_id:
                #the controlling task must have restarted (and called initBuild again)
                return id
            raise koji.GenericError, "Build already in progress (task %d)" % task_id
            # TODO? - reclaim 'stale' builds (state=BUILDING and task_id inactive)
        elif st_desc in ('FAILED','CANCELED'):
            koji.plugin.run_callbacks('preBuildStateChange', attribute='state', old=state, new=data['state'], info=data)
            #should be ok to replace
            update = """UPDATE build SET state=%(state)i,task_id=%(task_id)s,
            owner=%(owner)s,completion_time=%(completion_time)s,create_event=get_event()
            WHERE id = %(id)i"""
            _dml(update, data)
            koji.plugin.run_callbacks('postBuildStateChange', attribute='state', old=state, new=data['state'], info=data)
            return id
        else:
            # BEG METHOD HIJACK
            # open the current file
            frot = "/mnt/koji/packages"
            fpth = ("%s/%s/%s" % (str(data["name"]), str(data["version"]), str(data["release"])))
            fnme = ("%s-%s-%s" % (str(data["name"]), str(data["version"]), str(data["release"])))
            fsrc = ("%s/%s/src/%s.src.rpm" % (frot, fpth, fnme))
            try:
                fobj = open(fsrc, "r")
            except:
                raise koji.GenericError, "Could not open file [%s]" % (fsrc)
            # hash the current file
            hobj = hashlib.md5()
            while (1):
                fdta = fobj.read(2**10)
                if (not fdta):
                    break
                hobj.update(fdta)
            fobj.close()
            hash = hobj.hexdigest()
            tida = time.strftime("%y%m%d")
            # set the new release name
            while (1):
                rell = str(data["release"]).rsplit(".", 1)
                rell.insert(1, hash[0:6] + "." + tida)
                reln = ".".join(rell)
                foln = ("%s/%s/%s/%s" % (frot, str(data["name"]), str(data["version"]), reln))
                if (not os.path.exists(foln)):
                    break
                hobj = hashlib.md5()
                hobj.update(hash)
                hash = hobj.hexdigest()
            data["reln"] = reln
            # get a list of old files/folders
            fill = []
            dirl = ["%s/%s" % (frot, fpth)]
            while (len(dirl) > 0):
                try:
                    tmpl = os.listdir(dirl[0])
                except:
                    tmpl = []
                parn = dirl[0]
                dirl.pop(0)
                for tmpi in tmpl:
                    tpth = ("%s/%s" % (parn, tmpi))
                    if (os.path.isdir(tpth)):
                        dirl.append(tpth)
                    else:
                        name = re.sub("[^0-9A-Za-z]", ".", str(data["name"]))
                        vers = re.sub("[^0-9A-Za-z]", ".", str(data["version"]))
                        rels = re.sub("[^0-9A-Za-z]", ".", str(data["release"]))
                        robj = re.match("^(.*)%s(.*)%s(.*)%s(.*)$" % (name, vers, rels), tmpi)
                        if (robj):
                            newn = (parn + "/" + robj.group(1) + name + robj.group(2) + vers + robj.group(3) + reln + robj.group(4))
                            fill.append([tpth, newn])
            fill.append([frot + "/" + fpth, foln])
            # update the sql database
            c = context.cnx.cursor()
            buil = "UPDATE build SET release=%(reln)s WHERE id = %(id)i"
            #_dml(buil, data)
            c.execute(buil, data)
            rpmi = "UPDATE rpminfo SET release=%(reln)s WHERE build_id = %(id)i"
            #_dml(rpmi, data)
            c.execute(rpmi, data)
            context.cnx.commit()
            c.close()
            # rename the files/folders now
            for item in fill:
                os.rename(item[0], item[1])
            # carry on the same now
            return new_build(tdic)
            # END METHOD HIJACK
    else:
        koji.plugin.run_callbacks('preBuildStateChange', attribute='state', old=None, new=data['state'], info=data)
    #insert the new data
    data['id'] = _singleValue("SELECT nextval('build_id_seq')")
    q="""
    INSERT INTO build (id,pkg_id,version,release,epoch,state,
            task_id,owner,completion_time)
    VALUES (%(id)i,%(pkg_id)i,%(version)s,%(release)s,%(epoch)s,
            %(state)s,%(task_id)s,%(owner)s,%(completion_time)s)
    """
    _dml(q, data)
    koji.plugin.run_callbacks('postBuildStateChange', attribute='state', old=None, new=data['state'], info=data)
    #return build_id
    return data['id']

One thought on “A “Bootstrapable” Koji : A “Feature” Bypassed

Leave a reply to Mini-Koji Build System (Moji) « Jon's FOSS Blog Cancel reply