1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30 from __future__ import print_function
31 from __future__ import unicode_literals
32 from __future__ import division
33 from __future__ import absolute_import
34
35 import fcntl
36 import urllib
37 import os
38 from bunch import Bunch
39
40 from ..constants import DEF_REMOTE_BASEDIR, DEF_BUILD_TIMEOUT, DEF_REPOS, \
41 DEF_BUILD_USER, DEF_MACROS
42 from ..exceptions import MockRemoteError, BuilderError
43
44
45
46 from ..sign import sign_rpms_in_dir, get_pubkey
47 from ..createrepo import createrepo
48
49 from .builder import Builder
50 from .callback import DefaultCallBack
54 source_basename = os.path.basename(pkg_name).replace(".src.rpm", "")
55 return os.path.normpath(os.path.join(chroot_dir, source_basename))
56
59
60
61
62
63 - def __init__(self, builder_host=None, job=None,
64 repos=None,
65 callback=None,
66 macros=None,
67 opts=None,
68 lock=None):
69
70 """
71 :param builder_host: builder hostname or ip
72
73 :param backend.job.BuildJob job: Job object with the following attributes::
74 :ivar timeout: ssh timeout
75 :ivar destdir: target directory to put built packages
76 :ivar chroot: chroot config name/base to use in the mock build
77 (e.g.: fedora20_i386 )
78 :ivar buildroot_pkgs: whitespace separated string with additional
79 packages that should present during build
80 :ivar build_id: copr build.id
81 :ivar pkg: pkg to build
82
83
84 :param repos: additional repositories for mock
85 :param backend.mockremote.callback.DefaultCallBack callback: object with hooks for notifications
86 about build progress
87
88 :param macros: { "copr_username": ...,
89 "copr_projectname": ...,
90 "vendor": ...}
91 :param multiprocessing.Lock lock: instance of Lock shared between
92 Copr backend process
93 :param DefaultCallback callback: build progress handler
94
95 :param Bunch opts: builder options, used keys::
96 :ivar build_user: user to run as/connect as on builder systems
97 :ivar do_sign: enable package signing, require configured
98 signer host and correct /etc/sign.conf
99 :ivar frontend_base_url: url to the copr frontend
100 :ivar results_baseurl: base url for the built results
101 :ivar remote_basedir: basedir on builder
102 :ivar remote_tempdir: tempdir on builder
103
104 # Removed:
105 # :param cont: if a pkg fails to build, continue to the next one--
106 # :param bool recurse: if more than one pkg and it fails to build,
107 # try to build the rest and come back to it
108 """
109 self.opts = Bunch(
110 do_sign=False,
111 frontend_base_url=None,
112 results_baseurl=u"",
113 build_user=DEF_BUILD_USER,
114 remote_basedir=DEF_REMOTE_BASEDIR,
115 remote_tempdir=None,
116 )
117 if opts:
118 self.opts.update(opts)
119
120 self.max_retry_count = 2
121
122 self.job = job
123 self.repos = repos or DEF_REPOS
124
125
126
127
128
129 self.callback = callback
130 self.macros = macros or DEF_MACROS
131 self.lock = lock
132
133 if not self.callback:
134 self.callback = DefaultCallBack()
135
136 self.callback.log("Setting up builder: {0}".format(builder_host))
137 self.builder = Builder(
138 opts=self.opts,
139 hostname=builder_host,
140 username=self.opts.build_user,
141 job=self.job,
142 chroot=self.job.chroot,
143 timeout=self.job.timeout or DEF_BUILD_TIMEOUT,
144 buildroot_pkgs=self.job.buildroot_pkgs,
145 callback=self.callback,
146 remote_basedir=self.opts.remote_basedir,
147 remote_tempdir=self.opts.remote_tempdir,
148 macros=self.macros, repos=self.repos)
149
150 self.failed = []
151 self.finished = []
152
153
154
156 """
157 Checks that MockRemote configuration and environment are correct.
158
159 :raises MockRemoteError: when configuration is wrong or
160 some expected resource is unavailable
161 """
162 if not self.job.chroot:
163 raise MockRemoteError("No chroot specified!")
164
165 self.builder.check()
166
167 @property
169 return os.path.normpath(os.path.join(self.job.destdir, self.job.chroot))
170
171 @property
174
176 s_pkg = os.path.basename(pkg)
177 pdn = s_pkg.replace(".src.rpm", "")
178 return os.path.normpath(
179 "{0}/{1}/{2}".format(self.job.destdir, self.job.chroot, pdn))
180
182 """
183 Adds pubkey.gpg with public key to ``chroot_dir``
184 using `copr_username` and `copr_projectname` from self.job.
185 """
186 self.callback.log("Retrieving pubkey ")
187
188 user = self.job.project_owner
189 project = self.job.project_name
190 pubkey_path = os.path.join(self.job.destdir, "pubkey.gpg")
191 try:
192
193
194
195
196 get_pubkey(user, project, pubkey_path)
197 self.callback.log(
198 "Added pubkey for user {} project {} into: {}".
199 format(user, project, pubkey_path))
200
201 except Exception as e:
202 self.callback.error(
203 "failed to retrieve pubkey for user {} project {} due to: \n"
204 "{}".format(user, project, e))
205
207 """
208 Sign built rpms
209 using `copr_username` and `copr_projectname` from self.job
210 by means of obs-sign. If user builds doesn't have a key pair
211 at sign service, it would be created through ``copr-keygen``
212
213 :param chroot_dir: Directory with rpms to be signed
214 :param pkg: path to the source package
215
216 """
217
218 self.callback.log("Going to sign pkgs from source: {} in chroot: {}".
219 format(self.pkg, self.chroot_dir))
220
221 try:
222 sign_rpms_in_dir(self.job.project_owner,
223 self.job.project_name,
224 get_target_dir(self.chroot_dir, self.pkg),
225 opts=self.opts,
226 callback=self.callback,)
227 except Exception as e:
228 self.callback.error(
229 "failed to sign packages "
230 "built from `{}` with error: \n"
231 "{}".format(self.pkg, e)
232 )
233 if isinstance(e, MockRemoteError):
234 raise e
235
236 self.callback.log("Sign done")
237
238 @staticmethod
240 r_log = open(filepath, 'a')
241 fcntl.flock(r_log, fcntl.LOCK_EX)
242 for to_out in to_out_list:
243 r_log.write(to_out)
244 if to_err_list:
245 r_log.write("\nstderr\n")
246 for to_err in to_err_list:
247 r_log.write(str(to_err))
248 fcntl.flock(r_log, fcntl.LOCK_UN)
249 r_log.close()
250
252 base_url = "/".join([self.opts.results_baseurl, self.job.project_owner,
253 self.job.project_name, self.job.chroot])
254 self.callback.log("Createrepo:: owner: {}; project: {}; "
255 "front url: {}; path: {}; base_url: {}"
256 .format(self.job.project_owner, self.job.project_name,
257 self.opts.frontend_base_url, self.chroot_dir, base_url))
258
259 _, _, err = createrepo(
260 path=self.chroot_dir,
261 front_url=self.opts.frontend_base_url,
262 base_url=base_url,
263 username=self.job.project_owner,
264 projectname=self.job.project_name,
265 lock=self.lock,
266 )
267 if err.strip():
268 self.callback.error(
269 "Error making local repo: {0}".format(self.chroot_dir))
270
271 self.callback.error(str(err))
272
273
274
285
287 p_path = self._get_pkg_destpath(self.pkg)
288
289 if os.path.exists(os.path.join(p_path, "fail")):
290 os.unlink(os.path.join(p_path, "fail"))
291
292
293 if not os.path.exists(p_path):
294 os.makedirs(p_path)
295
296 self.mark_dir_with_build_id()
297
337
339 """Build pkg defined in self.job
340
341
342 :return: build_details
343 """
344
345
346
347 retry_count = self.max_retry_count
348 while retry_count > 0:
349 retry_count -= 1
350 try:
351 build_details = self.build_pkg_and_process_results()
352 break
353 except MockRemoteError as exception:
354 self.callback.error(exception.msg)
355
356 else:
357
358 msg = "Build pkg {} failed {} times".format(self.pkg, self.max_retry_count)
359 self.callback.log(msg)
360 raise MockRemoteError(msg)
361
362 return build_details
363
365 """
366 Places "build.info" which contains job build_id
367 into the directory with downloaded files.
368
369 """
370 info_file_path = os.path.join(self._get_pkg_destpath(self.job.pkg),
371 "build.info")
372 self.callback.log("mark build dir with build_id, ")
373 try:
374 with open(info_file_path, 'w') as info_file:
375 info_file.writelines(["build_id={}".format(self.job.build_id),])
376
377 except Exception as error:
378 msg = "Failed to mark build {} with build_id".format(error)
379 self.callback.log(msg)
380