def check_action_change_devel(self, request, action): advance, result = self.config_validate(action.tgt_project) if not advance: return result source_hash = package_source_hash(self.apiurl, action.tgt_project, action.tgt_package) origin_info_old = origin_find(self.apiurl, action.tgt_project, action.tgt_package, source_hash, True) with devel_project_simulate(self.apiurl, action.tgt_project, action.tgt_package, action.src_project, action.src_package): origin_info_new = origin_find(self.apiurl, action.tgt_project, action.tgt_package, source_hash) result = policy_evaluate(self.apiurl, action.tgt_project, action.tgt_package, origin_info_new, origin_info_old, source_hash, source_hash) reviews = {} # Remove all additional_reviews as there are no source changes. for key, comment in result.reviews.items(): if key in ('fallback', 'maintainer'): reviews[key] = comment if result.accept: config = config_load(self.apiurl, action.tgt_project) if request.creator == config['review-user']: # Remove all reviews since the request was generated via # origin_update() which indicates it was approved already. Acts # as workaround for to lack of set devel on a submit request. reviews = {} if len(reviews) != len(result.reviews): result = PolicyResult(result.wait, result.accept, reviews, result.comments) return self.policy_result_handle(action.tgt_project, action.tgt_package, origin_info_new, origin_info_old, result)
def check_source_submission(self, src_project, src_package, src_rev, tgt_project, tgt_package): if not self.config_validate(tgt_project): return False source_hash_new = package_source_hash(self.apiurl, src_project, src_package, src_rev) origin_info_new = origin_find(self.apiurl, tgt_project, tgt_package, source_hash_new) source_hash_old = package_source_hash(self.apiurl, tgt_project, tgt_package) origin_info_old = origin_find(self.apiurl, tgt_project, tgt_package, source_hash_old, True) result = policy_evaluate(self.apiurl, tgt_project, tgt_package, origin_info_new, origin_info_old, source_hash_new, source_hash_old) return self.policy_result_handle(tgt_project, tgt_package, origin_info_new, origin_info_old, result)
def check_action_delete_package(self, request, action): origin_info_old = origin_find(self.apiurl, action.tgt_project, action.tgt_package) reviews = {'fallback': 'Delete requests require fallback review.'} self.policy_result_reviews_add(action.tgt_project, action.tgt_package, reviews, origin_info_old, origin_info_old) return True
def check_action_delete_package(self, request, action): origin_info_old = origin_find(self.apiurl, action.tgt_project, action.tgt_package) reviews = {'fallback': 'Delete requests require fallback review.'} self.policy_result_reviews_add(action.tgt_project, action.tgt_package, reviews, origin_info_old, origin_info_old) return True
def check_action_delete_package(self, request, action): advance, result = self.config_validate(action.tgt_project) if not advance: return result origin_info_old = origin_find(self.apiurl, action.tgt_project, action.tgt_package) reviews = {'fallback': 'Delete requests require fallback review.'} self.policy_result_reviews_add(action.tgt_project, action.tgt_package, reviews, origin_info_old, origin_info_old) return True
def check_source_submission(self, src_project, src_package, src_rev, tgt_project, tgt_package): if not self.config_validate(tgt_project): return False source_hash_new = package_source_hash(self.apiurl, src_project, src_package, src_rev) origin_info_new = origin_find(self.apiurl, tgt_project, tgt_package, source_hash_new) source_hash_old = package_source_hash(self.apiurl, tgt_project, tgt_package) origin_info_old = origin_find(self.apiurl, tgt_project, tgt_package, source_hash_old, True) result = policy_evaluate(self.apiurl, tgt_project, tgt_package, origin_info_new, origin_info_old, source_hash_new, source_hash_old) return self.policy_result_handle(tgt_project, tgt_package, origin_info_new, origin_info_old, result)
def check_source_submission(self, src_project, src_package, src_rev, tgt_project, tgt_package): kind = package_kind(self.apiurl, tgt_project, tgt_package) if not (kind is None or kind == 'source'): self.review_messages['accepted'] = 'skipping {} package since not source'.format(kind) return True advance, result = self.config_validate(tgt_project) if not advance: return result if self.request_age_wait(): # Allow for parallel submission to be created. return None source_hash_new = package_source_hash(self.apiurl, src_project, src_package, src_rev) origin_info_new = origin_find(self.apiurl, tgt_project, tgt_package, source_hash_new) source_hash_old = package_source_hash(self.apiurl, tgt_project, tgt_package) origin_info_old = origin_find(self.apiurl, tgt_project, tgt_package, source_hash_old, True) # Check if simulating the devel project is appropriate. devel_project, reason = self.devel_project_simulate_check(src_project, tgt_project) if devel_project and (reason.startswith('change_devel command') or origin_info_new is None): self.logger.debug(f'reevaluate considering {devel_project} as devel since {reason}') try: with devel_project_simulate(self.apiurl, tgt_project, tgt_package, src_project, src_package): # Recurse with simulated devel project. ret = self.check_source_submission( src_project, src_package, src_rev, tgt_project, tgt_package) self.review_messages['accepted']['comment'] = reason return ret except devel_project_simulate_exception: # Invalid infinite recursion so fallback to normal behavior. pass result = policy_evaluate(self.apiurl, tgt_project, tgt_package, origin_info_new, origin_info_old, source_hash_new, source_hash_old) return self.policy_result_handle(tgt_project, tgt_package, origin_info_new, origin_info_old, result)
def osrt_origin_lookup(apiurl, project, force_refresh=False, previous=False, quiet=False): locked = project_locked(apiurl, project) if locked: force_refresh = False lookup_path = osrt_origin_lookup_file(project, previous) if not force_refresh and os.path.exists(lookup_path): if not locked and not previous: # Force refresh of lookup information if expried. if time.time() - os.stat( lookup_path).st_mtime > OSRT_ORIGIN_LOOKUP_TTL: return osrt_origin_lookup(apiurl, project, True) with open(lookup_path, 'r') as lookup_stream: lookup = yaml.safe_load(lookup_stream) if not isinstance(next(iter(lookup.values())), dict): # Convert flat format to dictionary. for package, origin in lookup.items(): lookup[package] = {'origin': origin} else: if previous: return None packages = package_list_kind_filtered(apiurl, project) lookup = {} for package in packages: origin_info = origin_find(apiurl, project, package) lookup[str(package)] = { 'origin': str(origin_info), 'revisions': origin_revision_state(apiurl, project, package, origin_info), } if os.path.exists(lookup_path): lookup_path_previous = osrt_origin_lookup_file(project, True) copyfile(lookup_path, lookup_path_previous) with open(lookup_path, 'w+') as lookup_stream: yaml.dump(lookup, lookup_stream, default_flow_style=False) if not previous and not quiet: dt = timedelta(seconds=time.time() - os.stat(lookup_path).st_mtime) print('# generated {} ago'.format(dt), file=sys.stderr) return lookup
def osrt_origin_lookup(apiurl, project, force_refresh=False, previous=False, quiet=False): locked = project_locked(apiurl, project) if locked: force_refresh = False lookup_path = osrt_origin_lookup_file(project, previous) if not force_refresh and os.path.exists(lookup_path): if not locked and not previous: # Force refresh of lookup information if expried. if time.time() - os.stat(lookup_path).st_mtime > OSRT_ORIGIN_LOOKUP_TTL: return osrt_origin_lookup(apiurl, project, True) with open(lookup_path, 'r') as lookup_stream: lookup = yaml.safe_load(lookup_stream) if not isinstance(next(iter(lookup.values())), dict): # Convert flat format to dictionary. for package, origin in lookup.items(): lookup[package] = {'origin': origin} else: if previous: return None packages = package_list_kind_filtered(apiurl, project) lookup = {} for package in packages: origin_info = origin_find(apiurl, project, package) lookup[str(package)] = { 'origin': str(origin_info), 'revisions': origin_revision_state(apiurl, project, package, origin_info), } if os.path.exists(lookup_path): lookup_path_previous = osrt_origin_lookup_file(project, True) copyfile(lookup_path, lookup_path_previous) with open(lookup_path, 'w+') as lookup_stream: yaml.dump(lookup, lookup_stream, default_flow_style=False) if not previous and not quiet: dt = timedelta(seconds=time.time() - os.stat(lookup_path).st_mtime) print('# generated {} ago'.format(dt), file=sys.stderr) return lookup
def osrt_origin_package(apiurl, opts, *packages): origin_info = origin_find(apiurl, opts.project, packages[0]) print(origin_info)
def test_split_product(self): self.remote_config_set_age_minimum() upstream1_project = self.randomString('upstream1') upstream2_project = self.randomString('upstream2') devel_project = self.randomString('devel') package = self.randomString('package') target_package = self.wf.create_package(self.target_project, package) upstream1_package = self.wf.create_package(upstream1_project, package) upstream2_package = self.wf.create_package(upstream2_project, package) devel_package = self.wf.create_package(devel_project, package) upstream1_package.create_commit() upstream2_package.create_commit() devel_package.create_commit() attribute_value_save(self.wf.apiurl, upstream1_project, 'ApprovedRequestSource', '', 'OBS') attribute_value_save(self.wf.apiurl, upstream2_project, 'ApprovedRequestSource', '', 'OBS') attribute_value_save(self.wf.apiurl, devel_project, 'ApprovedRequestSource', '', 'OBS') self.origin_config_write([ {'<devel>': {}}, {upstream1_project: {}}, {upstream2_project: { 'pending_submission_consider': True }}, {'*~': {}}, ], {'unknown_origin_wait': True}) # Simulate branch project from upstream1. copy_package(self.wf.apiurl, upstream1_project, package, self.wf.apiurl, self.target_project, package) memoize_session_reset() origin_info = origin_find(self.wf.apiurl, self.target_project, package) self.assertEqual(str(origin_info), upstream1_project) # Create request against upstream2 which considers pending submissions. request_upstream2 = self.wf.submit_package(devel_package, upstream2_project) request_target = self.wf.submit_package(devel_package, self.target_project) self.assertReviewBot(request_target.reqid, self.bot_user, 'new', 'new') comment = [ '<!-- OriginManager state=seen result=None -->', f'Waiting on acceptance of request#{request_upstream2.reqid}.', ] self.assertComment(request_target.reqid, comment) request_upstream2.change_state('accepted') self.assertReviewBot(request_target.reqid, self.bot_user, 'new', 'accepted') self.assertAnnotation(request_target.reqid, { 'origin': upstream2_project, 'origin_old': upstream1_project, }) # Accept fallback review for changing to lower priority origin. self.accept_fallback_review(request_target.reqid) request_target.change_state('accepted') memoize_session_reset() origin_info = origin_find(self.wf.apiurl, self.target_project, package) self.assertEqual(str(origin_info), upstream2_project) # Simulate upstream1 incorporating upstream2 version of package. copy_package(self.wf.apiurl, upstream2_project, package, self.wf.apiurl, upstream1_project, package) memoize_session_reset() origin_info = origin_find(self.wf.apiurl, self.target_project, package) self.assertEqual(str(origin_info), upstream1_project)
def devel_workflow(self, only_devel): self.remote_config_set_age_minimum() devel_project = self.randomString('devel') package = self.randomString('package') request = self.wf.create_submit_request(devel_project, package) attribute_value_save(self.wf.apiurl, devel_project, 'ApprovedRequestSource', '', 'OBS') if not only_devel: self.assertReviewBot(request.reqid, self.bot_user, 'new', 'new') comment = [ '<!-- OriginManager state=seen result=None -->', 'Source not found in allowed origins:', f'- {self.product_project}', f'Decision may be overridden via `@{self.bot_user} override`.', ] self.assertComment(request.reqid, comment) CommentAPI(self.wf.api.apiurl).add_comment( request_id=request.reqid, comment=f'@{self.bot_user} change_devel') comment = 'change_devel command by {}'.format('Admin') else: comment = 'only devel origin allowed' self.assertReviewBot(request.reqid, self.bot_user, 'new', 'accepted') self.assertAnnotation(request.reqid, { 'comment': comment, 'origin': devel_project, }) request.change_state('accepted') memoize_session_reset() self.osc_user(self.bot_user) request_future = origin_update(self.wf.apiurl, self.wf.project, package) self.assertNotEqual(request_future, False) if request_future: request_id_change_devel = request_future.print_and_create() # Ensure a second request is not triggered. request_future = origin_update(self.wf.apiurl, self.wf.project, package) self.assertEqual(request_future, False) self.osc_user_pop() memoize_session_reset() origin_info = origin_find(self.wf.apiurl, self.wf.project, package) self.assertEqual(origin_info, None) self.assertReviewBot(request_id_change_devel, self.bot_user, 'new', 'accepted') self.assertAnnotation(request_id_change_devel, { 'origin': devel_project, }) # Origin should change before request is accepted since it is properly # annotated and without fallback review. memoize_session_reset() origin_info = origin_find(self.wf.apiurl, self.wf.project, package) self.assertEqual(str(origin_info), devel_project) self.wf.projects[devel_project].packages[0].create_commit() self.osc_user(self.bot_user) request_future = origin_update(self.wf.apiurl, self.wf.project, package) self.assertNotEqual(request_future, False) if request_future: request_id_update = request_future.print_and_create() request_future = origin_update(self.wf.apiurl, self.wf.project, package) self.assertEqual(request_future, False) self.osc_user_pop() self.assertReviewBot(request_id_update, self.bot_user, 'new', 'accepted') self.assertAnnotation(request_id_update, { 'origin': devel_project, }) memoize_session_reset() devel_project_actual, _ = devel_project_get(self.wf.apiurl, self.wf.project, package) self.assertEqual(devel_project_actual, None) request = get_request(self.wf.apiurl, request_id_change_devel) request_state_change(self.wf.apiurl, request_id_change_devel, 'accepted') memoize_session_reset() devel_project_actual, devel_package_actual = devel_project_get( self.wf.apiurl, self.wf.project, package) self.assertEqual(devel_project_actual, devel_project) self.assertEqual(devel_package_actual, package) request = get_request(self.wf.apiurl, request_id_update) request_state_change(self.wf.apiurl, request_id_update, 'accepted') devel_project_new = self.randomString('develnew') self.wf.create_package(devel_project_new, package) attribute_value_save(self.wf.apiurl, devel_project_new, 'ApprovedRequestSource', '', 'OBS') copy_package(self.wf.apiurl, devel_project, package, self.wf.apiurl, devel_project_new, package) request_future = request_create_change_devel( self.wf.apiurl, devel_project_new, package, self.wf.project) self.assertNotEqual(request_future, False) if request_future: request_id_change_devel_new = request_future.print_and_create() self.assertReviewBot(request_id_change_devel_new, self.bot_user, 'new', 'accepted') self.assertAnnotation(request_id_change_devel_new, { 'origin': devel_project_new, 'origin_old': devel_project, }) self.accept_fallback_review(request_id_change_devel_new) request_state_change(self.wf.apiurl, request_id_change_devel_new, 'accepted') memoize_session_reset() origin_info = origin_find(self.wf.apiurl, self.wf.project, package) self.assertEqual(str(origin_info), devel_project_new)