#57 Make the `copr-builder` the only option
Merged 6 years ago by clime. Opened 7 years ago by praiskup.
Unknown source standalone-builder-only  into  master

@@ -4,7 +4,6 @@

  # rsync path

  rsync = "/usr/bin/rsync"

  

- DEF_REMOTE_BASEDIR = "/var/tmp"

  DEF_BUILD_TIMEOUT = 3600 * 6

  DEF_REPOS = []

  DEF_CHROOT = None

@@ -246,9 +246,9 @@

  

              self.log.info(

                  "Finished build: id={} builder={} timeout={} destdir={}"

-                 " chroot={} repos={}"

+                 " chroot={}"

                  .format(job.build_id, self.vm.vm_ip, job.timeout, job.destdir,

-                         job.chroot, str(job.repos)))

+                         job.chroot))

  

              self.copy_mock_logs(job)

  

@@ -5,16 +5,12 @@

      pass

  

  class RemoteCmdError(BuilderError):

-     def __init__(self, msg, cmd, as_root, rc, stderr, stdout):

-         self.msg = "{}\nCMD:{}\nRC:{}\nAS_ROOT:{}\nSTDERR:{}\nSTDOUT:{}".format(

-             msg, cmd, as_root, rc, stderr, stdout

+     def __init__(self, msg, cmd, rc, stderr, stdout):

+         self.msg = "{}\nCMD:{}\nRC:{}\nSTDERR:{}\nSTDOUT:{}".format(

+             msg, cmd, rc, stderr, stdout

          )

          super(RemoteCmdError, self).__init__(self.msg)

  

- class BuilderTimeOutError(BuilderError):

-     pass

- 

- 

  class CoprSignError(MockRemoteError):

      """

      Related to invocation of /bin/sign

file modified
-10
@@ -11,7 +11,6 @@

                  (based frontend.models.Build)::

  

                  - pkgs: list of space separated urls of packages to build

-                 - repos: list of space separated additional repos

                  - timeout: maximum allowed time of build, build will fail if exceeded # unused

                  - project_owner:

                  - project_name:
@@ -65,8 +64,6 @@

  

          self.arch = self.chroot.split("-")[2]

  

-         self.repos = [r for r in task_data["repos"].split(" ") if r.strip()]

- 

          self.destdir = os.path.normpath(os.path.join(

              worker_opts.destdir,

              task_data["project_owner"],
@@ -83,13 +80,6 @@

          self.built_packages = ""

  

      @property

-     def chroot_repos_extended(self):

-         repos = list(self.repos)

-         repos.append("{}/{}".format(self.results_repo_url, self.chroot))

-         repos.append("{}/{}/devel".format(self.results_repo_url, self.chroot))

-         return repos

- 

-     @property

      def chroot_dir(self):

          return os.path.normpath("{}/{}".format(self.destdir, self.chroot))

  

@@ -39,7 +39,7 @@

  from munch import Munch

  import time

  

- from ..constants import DEF_REMOTE_BASEDIR, DEF_BUILD_TIMEOUT, DEF_REPOS, \

+ from ..constants import DEF_BUILD_TIMEOUT, DEF_REPOS, \

      DEF_BUILD_USER, DEF_MACROS

  from ..exceptions import MockRemoteError, BuilderError, CreateRepoError

  
@@ -80,7 +80,7 @@

      #   idea: send events according to the build progress to handler

  

      def __init__(self, builder_host, job, logger,

-                  repos=None, opts=None):

+                  opts=None):

  

          """

          :param builder_host: builder hostname or ip
@@ -90,14 +90,10 @@

              :ivar destdir: target directory to put built packages

              :ivar chroot: chroot config name/base to use in the mock build

                             (e.g.: fedora20_i386 )

-             :ivar buildroot_pkgs: whitespace separated string with additional

-                                packages that should present during build

              :ivar build_id: copr build.id

              :ivar pkg: pkg to build

  

  

-         :param repos: additional repositories for mock

- 

          :param macros: {    "copr_username": ...,

                              "copr_projectname": ...,

                              "vendor": ...}
@@ -108,8 +104,6 @@

                  signer host and correct /etc/sign.conf

              :ivar frontend_base_url: url to the copr frontend

              :ivar results_baseurl: base url for the built results

-             :ivar remote_basedir: basedir on builder

-             :ivar remote_tempdir: tempdir on builder

  

          # Removed:

          # :param cont: if a pkg fails to build, continue to the next one--
@@ -121,8 +115,6 @@

              frontend_base_url=None,

              results_baseurl=u"",

              build_user=DEF_BUILD_USER,

-             remote_basedir=DEF_REMOTE_BASEDIR,

-             remote_tempdir=None,

              timeout=DEF_BUILD_TIMEOUT,

          )

          if opts:

@@ -9,9 +9,9 @@

  from backend.vm_manage import PUBSUB_INTERRUPT_BUILDER

  from ..helpers import get_redis_connection, ensure_dir_exists

  

- from ..exceptions import BuilderError, BuilderTimeOutError, RemoteCmdError, VmError

+ from ..exceptions import BuilderError, RemoteCmdError, VmError

  

- from ..constants import mockchain, rsync, DEF_BUILD_TIMEOUT

+ from ..constants import rsync

  from ..sshcmd import SSHConnectionError, SSHConnection

  

  import modulemd
@@ -25,16 +25,19 @@

          self.hostname = hostname

          self.job = job

          self.timeout = self.job.timeout or self.opts.timeout

-         self.repos =  []

          self.log = logger

  

-         self.buildroot_pkgs = self.job.buildroot_pkgs or ""

-         self._remote_tempdir = self.opts.remote_tempdir

-         self._remote_basedir = self.opts.remote_basedir

-         self._remote_pkg_path = None

+         # BACKEND/BUILDER API

+         self.builddir = "/var/lib/copr-builder"

+         self.livelog_name = os.path.join(self.builddir, 'live-log')

+         self.resultdir = os.path.join(self.builddir, 'results')

+         self.pidfile = os.path.join(self.builddir, 'pid')

  

-         self.root_conn = SSHConnection(host=self.hostname, config_file=self.opts.ssh.builder_config)

-         self.conn = SSHConnection(user=self.opts.build_user, host=self.hostname, config_file=self.opts.ssh.builder_config)

+         self.conn = SSHConnection(

+             user=self.opts.build_user,

+             host=self.hostname,

+             config_file=self.opts.ssh.builder_config

+         )

  

          self.module_dist_tag = self._load_module_dist_tag()

          self._build_pid = None
@@ -54,235 +57,43 @@

              self.log.info("Loaded {}, dist_tag {}".format(module_md_filepath, dist_tag))

          return dist_tag

  

-     def get_chroot_config_path(self, chroot):

-         return "{tempdir}/{chroot}.cfg".format(tempdir=self.tempdir, chroot=chroot)

- 

-     @property

-     def remote_build_dir(self):

-         return self.tempdir + "/build/"

- 

-     @property

-     def tempdir(self):

-         if self._remote_tempdir:

-             return self._remote_tempdir

- 

-         if self.opts.standalone_builder:

-             tempdir_path = "/var/lib/copr-builder"

-         else:

-             tempdir_path = "{0}/{1}-{2}".format(

-                 self._remote_basedir, "mockremote", self.job.task_id)

-             self._run_ssh_cmd("/bin/mkdir -m 755 -p {0}".format(tempdir_path))

- 

-         self._remote_tempdir = tempdir_path

-         return self._remote_tempdir

- 

-     @tempdir.setter

-     def tempdir(self, value):

-         self._remote_tempdir = value

- 

-     def _run_ssh_cmd(self, cmd, as_root=False):

+     def _run_ssh_cmd(self, cmd):

          """

          Executes single shell command remotely

  

          :param str cmd: shell command

-         :param bool as_root:

          :return: stdout, stderr as strings

          """

-         if as_root:

-             conn = self.root_conn

-         else:

-             conn = self.conn

- 

          self.log.info("BUILDER CMD: "+cmd)

- 

-         rc, out, err = conn.run_expensive(cmd)

+         rc, out, err = self.conn.run_expensive(cmd)

          if rc != 0:

              raise RemoteCmdError("Error running remote ssh command.",

-                                  cmd, rc, as_root, err, out)

+                                  cmd, rc, err, out)

          return out, err

  

-     def _get_remote_results_dir(self):

-         if self.opts.standalone_builder:

-             return os.path.join(self.tempdir, 'results')

- 

-         if any(x is None for x in [self.remote_build_dir,

-                                    self.remote_pkg_name,

-                                    self.job.chroot]):

-             return None

-         # the pkg will build into a dir by mockchain named:

-         # $tempdir/build/results/$chroot/$packagename

-         return os.path.normpath(os.path.join(

-             self.remote_build_dir, "results", self.job.chroot, self.remote_pkg_name))

- 

-     def _get_remote_config_dir(self):

-         return os.path.normpath(os.path.join(self.remote_build_dir, "configs", self.job.chroot))

- 

-     def setup_mock_chroot_config(self):

-         """

-         Setup mock config for current chroot.

- 

-         Packages in buildroot_pkgs are added to minimal buildroot.

-         """

-         cfg_path = self.get_chroot_config_path(self.job.chroot)

-         copy_cmd = "cp /etc/mock/{chroot}.cfg {dest}".format(chroot=self.job.chroot, dest=cfg_path)

-         self._run_ssh_cmd(copy_cmd)

- 

-         if ("'{0} '".format(self.buildroot_pkgs) !=

-                 pipes.quote(str(self.buildroot_pkgs) + ' ')):

- 

-             # just different test if it contains only alphanumeric characters

-             # allowed in packages name

-             raise BuilderError("Do not try this kind of attack on me")

- 

-         set_networking_cmd = "echo \"config_opts['use_host_resolv'] = {net_enabled}\" >> {path}".format(

-             net_enabled=("True" if self.job.enable_net else "False"), path=cfg_path

-         )

-         self._run_ssh_cmd(set_networking_cmd)

- 

-         if self.buildroot_pkgs:

-             if 'custom' in self.job.chroot:

-                 pattern = "^config_opts\['chroot_setup_cmd'\] = ''$"

-                 replace_by = "config_opts['chroot_setup_cmd'] = 'install {pkgs}'".format(pkgs=self.buildroot_pkgs)

-                 buildroot_custom_cmd = "sed -i \"s+{pattern}+{replace_by}+\" {path}".format(

-                     pattern=pattern, replace_by=replace_by, path=cfg_path

-                 )

-                 self._run_ssh_cmd(buildroot_custom_cmd)

-             else:

-                 pattern = "^.*chroot_setup_cmd.*\(@buildsys-build\|@build\|buildsys-build buildsys-macros\).*$"

-                 replace_by = "config_opts['chroot_setup_cmd'] = 'install \\1 {pkgs}'".format(pkgs=self.buildroot_pkgs)

-                 buildroot_cmd = "sed -i \"s+{pattern}+{replace_by}+\" {path}".format(

-                     pattern=pattern, replace_by=replace_by, path=cfg_path

-                 )

-                 self._run_ssh_cmd(buildroot_cmd)

- 

-         if self.module_dist_tag:

-             dist_tag_cmd = "echo \"config_opts['macros']['%dist'] = '{dist_tag}'\" >> {path}".format(

-                 dist_tag=self.module_dist_tag, path=cfg_path

-             )

-             self._run_ssh_cmd(dist_tag_cmd)

- 

      def collect_built_packages(self):

+         # TODO: cold we delegate this to copr-builder?

          self.log.info("Listing built binary packages")

          built_packages = self._run_ssh_cmd(

              "cd {0} && "

              "for f in `ls *.rpm |grep -v \"src.rpm$\"`; do"

              "   rpm -qp --qf \"%{{NAME}} %{{VERSION}}\n\" $f; "

-             "done".format(pipes.quote(self._get_remote_results_dir()))

+             "done".format(pipes.quote(self.resultdir))

          )[0].strip()

          self.log.info("Built packages:\n{}".format(built_packages))

          return built_packages

  

      def check_build_success(self):

-         successfile = os.path.join(self._get_remote_results_dir(), "success")

+         successfile = os.path.join(self.resultdir, "success")

          self._run_ssh_cmd("/usr/bin/test -f {0}".format(successfile))

  

-     def download_job_pkg_to_builder(self):

-         repo_url = "{}/{}.git".format(self.opts.dist_git_url, self.job.git_repo)

-         self.log.info("Cloning Dist Git repo {}, branch {}, hash {}".format(

-             self.job.git_repo, self.job.git_branch, self.job.git_hash))

-         stdout, stderr = self._run_ssh_cmd(

-             "rm -rf /tmp/build_package_repo && "

-             "mkdir /tmp/build_package_repo && "

-             "cd /tmp/build_package_repo && "

-             "git clone {repo_url} && "

-             "cd {pkg_name} && "

-             "git checkout {git_hash} && "

-             "fedpkg-copr --dist {branch} srpm"

-             .format(repo_url=repo_url,

-                     pkg_name=self.job.package_name,

-                     git_hash=self.job.git_hash,

-                     branch=self.job.git_branch))

- 

-     @property

-     def remote_pkg_path(self):

-         if self._remote_pkg_path:

-             return self._remote_pkg_path

- 

-         try:

-             remote_path_pattern = "/tmp/build_package_repo/{0}/*.src.rpm".format(self.job.package_name)

-             stdout, stderr = self._run_ssh_cmd("/usr/bin/ls {0}".format(remote_path_pattern))

-         except RemoteCmdError:

-             return None

- 

-         self._remote_pkg_path = stdout.strip()

-         return self._remote_pkg_path

- 

-     @property

-     def remote_pkg_name(self):

-         try:

-             return os.path.basename(self.remote_pkg_path).replace(".src.rpm", "")

-         except AttributeError:

-             return None

- 

-     def pre_process_repo_url(self, repo_url):

-         """

-             Expands variables and sanitize repo url to be used for mock config

-         """

-         try:

-             parsed_url = urlparse(repo_url)

-             if parsed_url.scheme == "copr":

-                 user = parsed_url.netloc

-                 prj = parsed_url.path.split("/")[1]

-                 repo_url = "/".join([self.opts.results_baseurl, user, prj, self.job.chroot])

- 

-             else:

-                 if "rawhide" in self.job.chroot:

-                     repo_url = repo_url.replace("$releasever", "rawhide")

-                 # custom expand variables

-                 repo_url = repo_url.replace("$chroot", self.job.chroot)

-                 repo_url = repo_url.replace("$distname", self.job.chroot.split("-")[0])

- 

-             return pipes.quote(repo_url)

-         except Exception as err:

-             self.log.exception("Failed to pre-process repo url: {}".format(err))

-             return None

- 

-     @property

-     def livelog_name(self):

-         if self.opts.standalone_builder:

-             return "/var/lib/copr-builder/live-log"

-         return pipes.quote('/tmp/{}.log'.format(self.job.task_id))

- 

      def run_async_build(self):

-         if self.opts.standalone_builder:

-             cmd = self._copr_builder_cmd()

-             pid, _ = self._run_ssh_cmd(cmd)

-             self._build_pid =int(pid.strip())

-             return

- 

- 

-         buildcmd = "timeout {} {} -r {} -l {} ".format(

-             self.timeout, mockchain, pipes.quote(self.get_chroot_config_path(self.job.chroot)),

-             pipes.quote(self.remote_build_dir))

- 

-         for repo in self.job.chroot_repos_extended:

-             repo = self.pre_process_repo_url(repo)

-             if repo is not None:

-                 buildcmd += "-a {0} ".format(repo)

- 

-         for k, v in self.job.mockchain_macros.items():

-             mock_opt = "--define={} {}".format(k, v)

-             buildcmd += "-m {} ".format(pipes.quote(mock_opt))

- 

-         buildcmd += self.remote_pkg_path

- 

-         # To run something on background, we need to:

-         # - ignore SIGHUP, 'nohup' is racy -> sighup'ed by ssh before signal

-         #   handler is actually set by nohup

-         # - make sure to not have attached std{out,err} descriptors to the pty

-         #   provided by 'ssh -t' (redirect or close them!); otherwise 'ssh -t'

-         #   hangs here till the command finishes, OTOH ...

-         # - doing &>{livelog} means that the mockchain command has no terminal

-         #   on std{out,err} which means that it's output are not line-buffered

-         #   (which wouldn't be very useful live-log), so let's use `unbuffer`

-         #   from expect.rpm to allocate _persistent_ server-side pseudo-terminal

-         buildcmd_async = 'trap "" SIGHUP; unbuffer {buildcmd} &>{livelog} & echo $!'.format(

-             livelog=self.livelog_name, buildcmd=buildcmd)

-         pid, _ = self._run_ssh_cmd(buildcmd_async)

-         self._build_pid =int(pid.strip())

+         cmd = self._copr_builder_cmd()

+         pid, _ = self._run_ssh_cmd(cmd)

+         self._build_pid = int(pid.strip())

  

      def setup_pubsub_handler(self):

- 

+         # TODO: is this used?

          self.rc = get_redis_connection(self.opts)

          self.ps = self.rc.pubsub(ignore_subscribe_messages=True)

          channel_name = PUBSUB_INTERRUPT_BUILDER.format(self.hostname)
@@ -296,41 +107,17 @@

          if msg is not None and msg.get("type") == "message":

              raise VmError("Build interrupted by msg: {}".format(msg["data"]))

  

-     # def start_build(self, pkg):

-     #     # build the pkg passed in

-     #     # add pkg to various lists

-     #     # check for success/failure of build

-     #

-     #     # build_details = {}

-     #     self.setup_mock_chroot_config()

-     #

-     #     # check if pkg is local or http

-     #     dest = self.check_if_pkg_local_or_http(pkg)

-     #

-     #     # srpm version

-     #     self.update_job_pkg_version(pkg)

-     #

-     #     # construct the mockchain command

-     #     buildcmd = self.gen_mockchain_command(dest)

-     #

- 

      @property

      def build_pid(self):

-         if self._build_pid:

-             return self._build_pid

-         try:

-             pidof_cmd = "/usr/bin/pgrep -o -u {user} {command}".format(

-                 user=self.opts.build_user, command="mockchain")

- 

-             if self.opts.standalone_builder:

-                 pidof_cmd = "cat /var/lib/copr-builder/pid"

- 

-             out, _ = self._run_ssh_cmd(pidof_cmd)

-         except:

-             return None

- 

-         return int(out.strip())

+         if not self._build_pid:

+             try:

+                 pidof_cmd = "cat {0}".format(self.pidfile)

+                 out, _ = self._run_ssh_cmd(pidof_cmd)

+                 self._build_pid = int(out.strip())

+             except:

+                 return None

  

+         return self._build_pid

  

      def _copr_builder_cmd(self):

          template = 'copr-builder --config {config} --copr {copr} ' \
@@ -338,6 +125,10 @@

                   + '--host-resolv {net} --timeout {timeout} ' \

                   + '--chroot {chroot} --detached '

  

+         if self.module_dist_tag:

+             template += '--define {0} '.format(

+                     pipes.quote('dist ' + self.module_dist_tag))

+ 

          # Repo name like <user/group>/<copr>/<package>

          git_repo_path = self.job.git_repo.split('/')

  
@@ -362,7 +153,7 @@

          ensure_dir_exists(self.job.results_dir, self.log)

          live_log = os.path.join(self.job.results_dir, 'mockchain-live.log')

  

-         live_cmd = '/usr/bin/tail -n +0 -f --pid={pid} {log}'.format(

+         live_cmd = '/usr/bin/tail -F -n +0 --pid={pid} {log}'.format(

              pid=self.build_pid, log=self.livelog_name)

  

          self.log.info("Attaching to live build log: " + live_cmd)
@@ -372,13 +163,6 @@

  

  

      def build(self):

-         if not self.opts.standalone_builder:

-             # make mock config

-             self.setup_mock_chroot_config()

- 

-             # download the package to the builder

-             self.download_job_pkg_to_builder()

- 

          # run the build

          self.run_async_build()

  
@@ -389,6 +173,7 @@

          self.attach_to_build()

  

      def rsync_call(self, source_path, target_path):

+         # TODO: sshcmd.py uses pre-allocated socket, use it here, too

          ensure_dir_exists(target_path, self.log)

  

          # make spaces work w/our rsync command below :(
@@ -421,38 +206,16 @@

              raise BuilderError(err_msg)

  

      def download_results(self, target_path):

-         if self._get_remote_results_dir():

-             self.rsync_call(self._get_remote_results_dir(), target_path)

- 

-     def download_configs(self, target_path):

-         self.rsync_call(self._get_remote_config_dir(), target_path)

+         self.rsync_call(self.resultdir, target_path)

  

      def check(self):

-         if self.opts.standalone_builder:

-             # We simply expect that 'copr-builder' package is installed on the

-             # builder machine.

-             return

- 

-         # do check of host

-         try:

-             # requires name resolve facility

-             socket.gethostbyname(self.hostname)

-         except IOError:

-             raise BuilderError("{0} could not be resolved".format(self.hostname))

- 

          try:

-             self._run_ssh_cmd("/bin/rpm -q mock rsync")

+             self._run_ssh_cmd("/bin/rpm -q copr-builder")

          except RemoteCmdError:

              raise BuilderError(msg="Build host `{0}` does not have mock or rsync installed"

                                 .format(self.hostname))

  

-         # test for path existence for mockchain and chroot config for this chroot

-         try:

-             self._run_ssh_cmd("/usr/bin/test -f {0}".format(mockchain))

-         except RemoteCmdError:

-             raise BuilderError(msg="Build host `{}` missing mockchain binary `{}`"

-                                .format(self.hostname, mockchain))

- 

+         # test for path existence for chroot config

          try:

              self._run_ssh_cmd("/usr/bin/test -f /etc/mock/{}.cfg"

                                .format(self.job.chroot))

no initial comment

rebased

7 years ago

Could you, please, resolve the conflicts that occurred?

rebased

6 years ago

Thank you very much.

Pull-Request has been merged by clime

6 years ago

Thanks, I haven't finished the testing yet ... but hopefully it will be OK.