Package coprs :: Package logic :: Module modules_logic
[hide private]
[frames] | no frames]

Source Code for Module coprs.logic.modules_logic

  1  import os 
  2  import time 
  3  import base64 
  4  import json 
  5  import requests 
  6  import modulemd 
  7  from collections import defaultdict 
  8  from sqlalchemy import and_ 
  9  from datetime import datetime 
 10  from coprs import models 
 11  from coprs import db 
 12  from coprs import exceptions 
 13  from coprs.logic import builds_logic 
 14  from wtforms import ValidationError 
15 16 17 -class ModulesLogic(object):
18 @classmethod
19 - def get(cls, module_id):
20 """ 21 Return single module identified by `module_id` 22 """ 23 return models.Module.query.filter(models.Module.id == module_id)
24 25 @classmethod
26 - def get_by_nsv(cls, copr, name, stream, version):
32 33 @classmethod
34 - def get_by_nsv_str(cls, copr, nsv):
35 name, stream, version = nsv.split("-") 36 return cls.get_by_nsv(copr, name, stream, version)
37 38 @classmethod
39 - def get_multiple(cls):
40 return models.Module.query.order_by(models.Module.id.desc())
41 42 @classmethod
43 - def get_multiple_by_copr(cls, copr):
45 46 @classmethod
47 - def yaml2modulemd(cls, yaml):
48 mmd = modulemd.ModuleMetadata() 49 mmd.loads(yaml) 50 return mmd
51 52 @classmethod
53 - def from_modulemd(cls, mmd):
54 yaml_b64 = base64.b64encode(mmd.dumps().encode("utf-8")).decode("utf-8") 55 return models.Module(name=mmd.name, stream=mmd.stream, version=mmd.version, summary=mmd.summary, 56 description=mmd.description, yaml_b64=yaml_b64)
57 58 @classmethod
59 - def validate(cls, mmd):
60 if not all([mmd.name, mmd.stream, mmd.version]): 61 raise ValidationError("Module should contain name, stream and version")
62 63 @classmethod
64 - def add(cls, user, copr, module):
65 if not user.can_build_in(copr): 66 raise exceptions.InsufficientRightsException("You don't have permissions to build in this copr.") 67 68 module.copr_id = copr.id 69 module.copr = copr 70 module.created_on = time.time() 71 72 db.session.add(module) 73 return module
74 75 @classmethod
76 - def set_defaults_for_optional_params(cls, mmd, filename=None):
77 mmd.name = mmd.name or str(os.path.splitext(filename)[0]) 78 mmd.stream = mmd.stream or "master" 79 mmd.version = mmd.version or int(datetime.now().strftime("%Y%m%d%H%M%S"))
80
81 82 -class ModuleBuildFacade(object):
83 - def __init__(self, user, copr, yaml, filename=None):
84 self.user = user 85 self.copr = copr 86 self.yaml = yaml 87 self.filename = filename 88 89 self.modulemd = ModulesLogic.yaml2modulemd(yaml) 90 ModulesLogic.set_defaults_for_optional_params(self.modulemd, filename=filename) 91 ModulesLogic.validate(self.modulemd)
92
93 - def submit_build(self):
94 module = ModulesLogic.add(self.user, self.copr, ModulesLogic.from_modulemd(self.modulemd)) 95 self.add_builds(self.modulemd.components.rpms, module) 96 return module
97 98 @classmethod
99 - def get_build_batches(cls, rpms):
100 """ 101 Determines Which component should be built in which batch. Returns an ordered list of grouped components, 102 first group of components should be built as a first batch, second as second and so on. 103 Particular components groups are represented by dicts and can by built in a random order within the batch. 104 :return: list of lists 105 """ 106 batches = defaultdict(dict) 107 for pkgname, rpm in rpms.items(): 108 batches[rpm.buildorder][pkgname] = rpm 109 return [batches[number] for number in sorted(batches.keys())]
110
111 - def add_builds(self, rpms, module):
112 for group in self.get_build_batches(rpms): 113 batch = models.Batch() 114 db.session.add(batch) 115 for pkgname, rpm in group.items(): 116 clone_url = self.get_clone_url(pkgname, rpm) 117 build = builds_logic.BuildsLogic.create_new_from_scm(self.user, self.copr, scm_type="git", 118 clone_url=clone_url, committish=rpm.ref) 119 build.batch = batch 120 build.batch_id = batch.id 121 build.module_id = module.id 122 db.session.add(build)
123
124 - def get_clone_url(self, pkgname, rpm):
125 return rpm.repository if rpm.repository else self.default_distgit.format(pkgname=pkgname)
126 127 @property
128 - def default_distgit(self):
129 # @TODO move to better place 130 return "https://src.fedoraproject.org/rpms/{pkgname}"
131
132 133 -class ModulemdGenerator(object):
134 - def __init__(self, name="", stream="", version=0, summary="", config=None):
135 self.config = config 136 self.mmd = modulemd.ModuleMetadata() 137 self.mmd.name = name 138 self.mmd.stream = stream 139 self.mmd.version = version 140 self.mmd.summary = summary
141 142 @property
143 - def nsv(self):
144 return "{}-{}-{}".format(self.mmd.name, self.mmd.stream, self.mmd.version)
145
146 - def add_api(self, packages):
147 for package in packages: 148 self.mmd.api.add_rpm(str(package))
149
150 - def add_filter(self, packages):
151 for package in packages: 152 self.mmd.filter.add_rpm(str(package))
153
154 - def add_profiles(self, profiles):
155 for i, values in profiles: 156 name, packages = values 157 self.mmd.profiles[name] = modulemd.profile.ModuleProfile() 158 for package in packages: 159 self.mmd.profiles[name].add_rpm(str(package))
160
161 - def add_components(self, packages, filter_packages, builds):
162 build_ids = sorted(list(set([int(id) for p, id in zip(packages, builds) 163 if p in filter_packages]))) 164 for package in filter_packages: 165 build_id = builds[packages.index(package)] 166 build = builds_logic.BuildsLogic.get_by_id(build_id).first() 167 build_chroot = self._build_chroot(build) 168 buildorder = build_ids.index(int(build.id)) 169 rationale = "User selected the package as a part of the module" 170 self.add_component(package, build, build_chroot, rationale, buildorder)
171
172 - def _build_chroot(self, build):
173 chroot = None 174 for chroot in build.build_chroots: 175 if chroot.name == "custom-1-x86_64": 176 break 177 return chroot
178
179 - def add_component(self, package_name, build, chroot, rationale, buildorder=1):
180 ref = str(chroot.git_hash) if chroot else "" 181 distgit_url = self.config["DIST_GIT_URL"].replace("/cgit", "/git") 182 url = os.path.join(distgit_url, build.copr.full_name, "{}.git".format(build.package.name)) 183 self.mmd.components.add_rpm(str(package_name), rationale, 184 repository=url, ref=ref, 185 buildorder=buildorder)
186
187 - def add_requires(self, module, stream):
188 self.mmd.add_requires(module, stream)
189
190 - def add_buildrequires(self, module, stream):
192
193 - def generate(self):
194 return self.mmd.dumps()
195
196 - def dump(self, handle):
197 return self.mmd.dump(handle)
198
199 200 -class MBSProxy(object):
201 - def __init__(self, mbs_url, user_name=None):
202 self.url = mbs_url 203 self.user = user_name
204
205 - def post(self, json=None, data=None, files=None):
206 request = requests.post(self.url, verify=False, 207 json=json, data=data, files=files) 208 return MBSResponse(request)
209
210 - def build_module(self, owner, project, nsv, modulemd):
211 return self.post( 212 data={"owner": self.user, "copr_owner": owner, "copr_project": project}, 213 files={"yaml": ("{}.yaml".format(nsv), modulemd)}, 214 )
215
216 217 -class MBSResponse(object):
218 - def __init__(self, response):
219 self.response = response
220 221 @property
222 - def failed(self):
223 return self.response.status_code != 201
224 225 @property
226 - def message(self):
227 if self.response.status_code in [500, 403, 404]: 228 return "Error from MBS: {} - {}".format(self.response.status_code, self.response.reason) 229 resp = json.loads(self.response.content) 230 if self.response.status_code != 201: 231 return "Error from MBS: {}".format(resp["message"]) 232 return "Created module {}-{}-{}".format(resp["name"], resp["stream"], resp["version"])
233
234 235 -class ModuleProvider(object):
236 - def __init__(self, filename, yaml):
237 self.filename = filename 238 self.yaml = yaml
239 240 @classmethod
241 - def from_input(cls, obj):
242 if hasattr(obj, "read"): 243 return cls.from_file(obj) 244 return cls.from_url(obj)
245 246 @classmethod
247 - def from_file(cls, ref):
248 return cls(ref.filename, ref.read())
249 250 @classmethod
251 - def from_url(cls, url):
252 if not url.endswith(".yaml"): 253 raise ValidationError("This URL doesn't point to a .yaml file") 254 255 request = requests.get(url) 256 if request.status_code != 200: 257 raise requests.RequestException("This URL seems to be wrong") 258 return cls(os.path.basename(url), request.text)
259