Example #1
0
    def __init__(self, cfgfile):
        with open(cfgfile, "r") as fd:
            self.__lconfig = json.load(fd)
        self.__name = self.__lconfig['name']
        self.__tmp_dir = self.__lconfig['global_tmp_dir']
        self.__artifacts_dir = os.path.join(self.__tmp_dir, "artifacts")
        os.makedirs(self.__artifacts_dir, exist_ok=True)
        self.__working_dir = os.path.join(self.__tmp_dir, ".mincid")
        with open(
                os.path.join(self.__working_dir,
                             'project_config.json'), "r") as fd:
            self.__global_config = json.load(fd)
        with open(os.path.join(self.__working_dir,
                               "mincid_master.json"), "r") as fd:
            self.__master_config = json.load(fd)

        self.__config = Config(self.__working_dir,
                               self.__lconfig['branch_name'])
            
        self.__name = self.__lconfig['name']
        self.__variant_dir = self.__lconfig['directory']
        self.__logger = MLogger("Variant", self.__name, self.__variant_dir)
        self.__logger.info("Init variant [%s]" % self.__lconfig['name'])

        self.__cmds = []
        self.__build_pre_cmds = [r'true']
        self.__cmds_post = [r'true']
Example #2
0
    def __init__(self, branch_name, tmp_dir):
        self.__name = branch_name
        self.__tmp_dir = tmp_dir
        self.__working_dir = os.path.join(self.__tmp_dir, ".mincid")
        
        with open(os.path.join(self.__working_dir, "mincid_master.json"), "r") as fd:
            self.__master_config = json.load(fd)

        self.__jobids = {}
        self.__config = Config(self.__working_dir, branch_name)
        self.__branch_dir = os.path.join(self.__working_dir,
                                         branch_name.replace("/", "_"))
        self.__logger = MLogger("Branch", "build_branch", self.__branch_dir)
        self.__logger.info("Init branch [%s]" % (self.__name))
        self.__variants_base_dir = os.path.join(self.__branch_dir, "variants")
        os.makedirs(self.__variants_base_dir)
Example #3
0
class Branch(object):

    def __init__(self, branch_name, tmp_dir):
        self.__name = branch_name
        self.__tmp_dir = tmp_dir
        self.__working_dir = os.path.join(self.__tmp_dir, ".mincid")
        
        with open(os.path.join(self.__working_dir, "mincid_master.json"), "r") as fd:
            self.__master_config = json.load(fd)

        self.__jobids = {}
        self.__config = Config(self.__working_dir, branch_name)
        self.__branch_dir = os.path.join(self.__working_dir,
                                         branch_name.replace("/", "_"))
        self.__logger = MLogger("Branch", "build_branch", self.__branch_dir)
        self.__logger.info("Init branch [%s]" % (self.__name))
        self.__variants_base_dir = os.path.join(self.__branch_dir, "variants")
        os.makedirs(self.__variants_base_dir)

    def __sort_deps(self):
        """Sorts the branch jobs using the dependencies"""
        jc = self.__config.branch_jobs()
        result = []
        todo = set(jc.keys())
        while len(todo) > 0:
            for k in todo:
                if not 'depends_on' in jc[k]:
                    # No dependecies: can start right away
                    result.append(k)
                    todo.remove(k)
                    break
                else:
                    don = jc[k]['depends_on']
                    if set(don).issubset(set(result)):
                        # Yep: all dependencies are already handled!
                        result.append(k)
                        todo.remove(k)
                        break
        return result

    
    def __start_variant(self, sname, image, variant_list):
        base = self.__config.expand(image['base'])
        self.__logger.info("Start variant [%s] [%s] [%s]"
                           % (sname, base, variant_list))
        stdouterr_filename = os.path.join(self.__branch_dir,
                                          "start_variants.stdouterr")

        dep_jobids = []
        # Collect jobids of all dependend stages
        if 'depends_on' in self.__config.branch_jobs()[sname]:
            for dep_stage in self.__config.branch_jobs()[sname]['depends_on']:
                dep_jobids.extend(self.__jobids[dep_stage])
            self.__logger.info("Dependent jobids [%s]" % dep_jobids)
        
        with open(stdouterr_filename, "w") as fd_stdouterr:
            # Create own temp dir
            rfsname = RFSString(self.__name)
            rfsbase = RFSString(base)
            variant_name = "%s+%s+%s+%s+%s" % \
                           (self.__config.project_cfg('name'),
                            rfsname.fs(),
                            sname, rfsbase.fs(), "_".join(variant_list))
            variant_tmp_dir = os.path.join(self.__variants_base_dir, variant_name)
            os.makedirs(variant_tmp_dir, exist_ok=True)
            variant_desc = {
                'name': variant_name,
                'base': base,
                'branch_name': self.__name,
                'global_tmp_dir': self.__tmp_dir,
                'directory': variant_tmp_dir
            }

            if len(variant_list)>0:
                variant_desc['variant_list'] = variant_list

            for pword in ('install', 'run'):
                if pword in self.__config.branch_jobs()[sname]:
                    variant_desc[
                        pword] = self.__config.branch_jobs()[sname][pword]

            if 'prepare' in image:
                variant_desc['prepare'] = image['prepare']
                
            if 'build_prepare' in image:
                variant_desc['build_prepare'] = image['build_prepare']

            variant_cfg_file_name = os.path.join(
                variant_tmp_dir, "variant.json")
            with open(variant_cfg_file_name, "w") as fd:
                json.dump(variant_desc, fd)

            subproc_slist = [
                "sbatch", "--job-name=%s" % variant_name,
                "--output=%s" % os.path.join(self.__working_dir,
                                             "slurm_build_branch_%j.out"),
                "--extra-node-info=1:2:1",
                 "--export=PYTHONPATH"]
            if len(dep_jobids)>0:
                dep_str = ":".join(str(x) for x in dep_jobids)
                self.__logger.info("New batch is dependent on [%s]" %
                                   dep_str)
                subproc_slist.append("--dependency=afterok:%s" % dep_str)
            subproc_slist.extend(
                [os.path.join(self.__master_config['mincid_install_dir'],
                              "build_variant.py"), variant_cfg_file_name])
                
            p = subprocess.Popen(subproc_slist,
                stdout=subprocess.PIPE, stderr=fd_stdouterr)

        res = p.stdout.read()
        p.wait()
        
        self.__logger.info("sbatch process return value [%d]" % p.returncode)

        decoded = res.decode("UTF-8")
        jobnr = int(decoded[20:-1])
        self.__jobids[sname].append(jobnr)
        
        self.__logger.info("sbatch process id [%d]" % jobnr)
        self.__logger.info("Finished variant [%s] [%s] [%s]"
                           % (sname, base, variant_list))
    
    def __start_variants(self, sname, image):
        self.__logger.info("Start variants [%s] [%s]" % (sname, image))
        variants = list(itertools.product(*image['variants']))
        for variant_list in variants:
            self.__start_variant(sname, image, variant_list)
        self.__logger.info("Finished variants [%s] [%s]" % (sname, image))

    def __start_image(self, sname, image):
        jc = self.__config.branch_jobs()
        base = self.__config.expand(image['base'])
        if 'ignore' in image and image['ignore']=="true":
            self.__logger.info("Ignoring image [%s] [%s]" % (sname, base))
            return
        self.__logger.info("Start image [%s] [%s]" % (sname, base))
        if 'variants' in image:
            self.__start_variants(sname, image)
        else:
            self.__start_variant(sname, image, [])
            
        self.__logger.info("Finished image [%s] [%s]" % (sname, base))

    def __start_stage(self, sname):
        self.__logger.info("Start stage [%s]" % sname)

        for image in self.__config.branch_jobs()[sname]['images']:
            self.__start_image(sname, image)
        self.__logger.info("Finished stage [%s]" % sname)

    def __start_stages(self, nlist):
        self.__logger.info("Start all stages [%s]" % (nlist))
        # Store all jobids for the appropriate (high level) job
        for n in nlist:
            self.__jobids[n] = []

        for n in nlist:
            self.__start_stage(n)

        self.__logger.info("Finished stating all stages [%s]" % (nlist))

    def process(self):
        self.__logger.info("Process branch [%s]" % (self.__name))
        nlist = self.__sort_deps()
        self.__logger.info("Node list [%s]" % (nlist))
        self.__start_stages(nlist)
Example #4
0
class Variant(object):

    def __init__(self, cfgfile):
        with open(cfgfile, "r") as fd:
            self.__lconfig = json.load(fd)
        self.__name = self.__lconfig['name']
        self.__tmp_dir = self.__lconfig['global_tmp_dir']
        self.__artifacts_dir = os.path.join(self.__tmp_dir, "artifacts")
        os.makedirs(self.__artifacts_dir, exist_ok=True)
        self.__working_dir = os.path.join(self.__tmp_dir, ".mincid")
        with open(
                os.path.join(self.__working_dir,
                             'project_config.json'), "r") as fd:
            self.__global_config = json.load(fd)
        with open(os.path.join(self.__working_dir,
                               "mincid_master.json"), "r") as fd:
            self.__master_config = json.load(fd)

        self.__config = Config(self.__working_dir,
                               self.__lconfig['branch_name'])
            
        self.__name = self.__lconfig['name']
        self.__variant_dir = self.__lconfig['directory']
        self.__logger = MLogger("Variant", self.__name, self.__variant_dir)
        self.__logger.info("Init variant [%s]" % self.__lconfig['name'])

        self.__cmds = []
        self.__build_pre_cmds = [r'true']
        self.__cmds_post = [r'true']

    def __variant_cmds(self, variant_flat):
        # Check if re matches
        for vcfg in self.__master_config['imagedef'][self.__lconfig['base']]['variant_cmds']:
            vre = vcfg[0]
            vcmd = vcfg[1]
            self.__logger.debug("RE search [%s] [%s]" % (vre, variant_flat))
            result = re.search(vre, variant_flat)
            if not result:
                continue
            for icmd in vcmd:
                self.__logger.info("Calling Sub [%s] [%s] [%s]" %
                                   (vre, icmd, variant_flat))
                nsub = re.sub(vre, icmd, variant_flat)
                self.__logger.info("Add cmd [%s]" % nsub)
                self.__cmds.append(nsub)

            if len(vcfg)>2:
                vpreb = vcfg[2]
                for ipreb in vpreb:
                    self.__logger.info("Calling Sub [%s] [%s] [%s]"
                                       % (vre, ipreb, variant_flat))
                    nsub = re.sub(vre, ipreb, variant_flat)
                    self.__logger.info("Add pre cmd [%s]" % nsub)
                    self.__build_pre_cmds.append(nsub)

    def __variant_install_pkgs(self, install_dict):
        # Add other packages...
        pkgs=[]
        if not 'pkgs' in install_dict:
            return

        rpkgs = self.__config.expand(install_dict['pkgs'])

        install_pkg_names = self.__master_config['imagedef'][self.__lconfig['base']]['pkg_names']
        for ipkg in rpkgs:
            if not ipkg in install_pkg_names:
                self.__logger.warning(
                    "Package with symolic name [%s] " % ipkg
                    + "has no name mapping")
                pkgs.append(ipkg)
            else:
                pkgs.extend(install_pkg_names[ipkg])
        if len(pkgs)>0:
            self.__cmds.append(
                "%s %s" % (
                    self.__master_config['imagedef'][self.__lconfig['base']]['install_pkg_cmd'],
                    (" ".join(pkgs))))

    def __variant_cmds_post(self, install_dict):
        # Add packages from the control file
        # Please note that this can happen AFTER the checkout!
        if not 'control_files' in install_dict:
            self.__logger.info("No control files given")
            return
            
        for cf in install_dict['control_files']:
            self.__logger.info("Control file [%s]" % cf)
            self.__cmds_post.append(
                "cd ~builder && mk-build-deps "
                + "--tool='apt-get -y --no-install-recommends' "
                + "--install %s" % cf)

    def __image_prepare(self):
        if not 'prepare' in self.__lconfig:
            self.__logger.info("No prepare given")
            return
        self.__cmds.extend(self.__lconfig['prepare'])

    def __image_build_prepare(self):
        if not 'build_prepare' in self.__lconfig:
            self.__logger.info("No build prepare given")
            return
        self.__cmds_post.extend(self.__lconfig['build_prepare'])
            
    def __handle_variant(self):
        variant_flat = ":" + ":".join(self.__lconfig['variant_list']) + ":"
        self.__logger.info("Flat variant [%s]" % variant_flat)

        self.__variant_cmds(variant_flat)

    def __global_build_pre_cmds(self):
        if "build_preparation_cmds" in self.__master_config:
            self.__build_pre_cmds.extend(self.__master_config["build_preparation_cmds"])
        
    def process(self):
        self.__logger.info("Start variant [%s]" % self.__name)

        self.__image_prepare()
        self.__image_build_prepare()
        self.__global_build_pre_cmds()
        if 'variant_list' in self.__lconfig:
            self.__handle_variant()
        if 'install' in self.__lconfig:
            self.__variant_install_pkgs(self.__lconfig['install'])
            self.__variant_cmds_post(self.__lconfig['install'])
                
        stdouterr_filename = os.path.join(
            self.__tmp_dir, self.__name + ".log")
        rm_docker_image = "true"
        if "remove_docker_image" in self.__master_config['imagedef'][self.__lconfig['base']]:
            rm_docker_image =  self.__master_config['imagedef'][self.__lconfig['base']]['remove_docker_image']
        setup_minimalistic=""
        if 'setup_image_minimalistic' in self.__master_config['imagedef'][self.__lconfig['base']]:
            setup_minimalistic="\n".join(self.__master_config['imagedef'][self.__lconfig['base']]['setup_image_minimalistic'])

        # Log all commands that are executed
        complete_docker_cmd = ["docker", "run", "--rm=%s" % rm_docker_image, "-i",
                               "-v", "%s:/working:rw" % self.__working_dir,
                               "-v", "%s:/artifacts:rw" % self.__artifacts_dir,
                               "-v", "%s:/resources:ro" % self.__master_config["resource_dir"],
                               self.__lconfig['base'],
                               "/bin/bash", "-x", "-e"]
        complete_build_cmds = """%s
%s
%s
su - builder --command "%s"
su - builder --command 'git clone %s --branch %s --single-branch %s'
%s
su - builder --command '%s && %s'
""" % \
            (setup_minimalistic,
             "\n".join(self.__master_config['imagedef'][self.__lconfig['base']]['setup_image']),
             "\n".join(self.__cmds),
             self.__global_config['vcs']['authcmd'],

             self.__global_config['vcs']['url'],
             self.__lconfig['branch_name'],
             self.__global_config['dest'],

             " && ".join(self.__cmds_post),
             "\n".join(self.__build_pre_cmds),
             self.__lconfig['run'] if 'run' in self.__lconfig else "")
        with open(os.path.join(self.__tmp_dir, self.__name + ".sh"), "w") as shlog:
            shlog.write("# %s\n" % " ".join(complete_docker_cmd))
            shlog.write(complete_build_cmds)
            
        with open(stdouterr_filename, "w") as fd_stdouterr:
            p = subprocess.Popen(complete_docker_cmd,
                                 stdin=subprocess.PIPE,
                                 stdout=fd_stdouterr, stderr=fd_stdouterr)
        p.stdin.write(bytes(complete_build_cmds, 'UTF-8'))
        
        p.stdin.close()
        p.wait()
        self.__logger.info("Docker process return value [%d]" % p.returncode)
        self.__logger.info("Finished variant [%s]" % (self.__name))

        with open(os.path.join(self.__tmp_dir, "results.txt"), "a") as fd:
            fd.write("%s: %s\n" % (self.__name, "success" if p.returncode == 0 else "failure"))
        
        # Exit with the result of the docker (script)
        sys.exit(p.returncode)