def setUp(self): self.host = 'http://localhost' self.port = '9000' self.user = '******' self.password = '******' self.test_group = str(uuid.uuid1()) username = str(uuid.uuid1()) self.sonar = SonarAPIHandler(user=self.user, password=self.password) self.test_user = self.sonar.create_user( username, 'qwerty', username, "{}@example.com".format(username)).json().get('user')
def main(): """ Manage a SonarQube's groups, using a SonarAPIHandler connected to the given host. """ options = parser.parse_args() h = SonarAPIHandler(host=options.host, port=options.port, user=options.user, password=options.password, token=options.authtoken, base_path=options.basepath) if options.command == 'list': groups = h.get_groups(options.fields, options.query).json() table = PrettyTable( ['ID', 'Name', 'Description', 'Members', 'Default']) for group in groups['groups']: table.add_row([ group.get('id'), group.get('name'), group.get('description'), group.get('membersCount'), group.get('default') ]) print(table) elif options.command == 'create': res = h.create_group(options.name, options.description).json() print(res['group']) elif options.command == 'update': res = h.update_group(options.gid, options.name, options.description).json() print(res['group']) elif options.command == 'delete': res = h.delete_group(options.gid, options.name) if res.status_code == 204: print("Group was successfully deleted") else: print("Error[%s] %s" % (res.status_code, res.reason)) elif options.command == 'add-user': res = h.add_user_group(options.login, options.gid, options.name) if res.status_code == 204: print("User was successfully added") else: print("Error[%s] %s" % (res.status_code, res.reason)) elif options.command == 'remove-user': res = h.remove_user_group(options.login, options.gid, options.name) if res.status_code == 204: print("User was successfully removed") else: print("Error[%s] %s" % (res.status_code, res.reason)) elif options.command == 'list-users': users = h.get_group_users(options.gid, options.name, options.query).json() table = PrettyTable(['Login', 'Name']) for user in users['users']: table.add_row([user['login'], user['name']]) print(table)
def main(): """ Activate rules in a profile using a SonarAPIHandler instance. """ options = parser.parse_args() h = SonarAPIHandler( host=options.host, port=options.port, user=options.user, password=options.password, token=options.authtoken, base_path=options.basepath, ) # Counters (total, created, skipped and failed) a, f = 0, 0 # Read file and import try: with open(options.filename, "r") as import_file: # Init reader and check headers reader = csv.DictReader(import_file) # Iterate rules and try to import them for rule_def in reader: key = rule_def.pop("key", None) try: # Pop key, clean data and attempt activation rule_def["reset"] = rule_def.get("reset", "").lower() in ("y", "yes", "true") rule_def = {k: v for k, v in rule_def.items() if v} h.activate_rule(key, options.profile_key, **rule_def) a += 1 except ValidationError as e: # Invalid data, print error sys.stderr.write("Failed to activate rule {}: " "{}\n".format(key, e)) f += 1 except Exception as e: # Other errors, stop execution immediately sys.stderr.write("Error: {}\n".format(e)) status = "Incomplete" else: # No errors, write result status = "Complete" # Finally, write results sys.stdout.write("{} rules activation: {} activated and " "{} failed.\n".format(status, a, f))
def main(): """ Activate rules in a profile using a SonarAPIHandler instance. """ options = parser.parse_args() h = SonarAPIHandler(host=options.host, port=options.port, user=options.user, password=options.password, token=options.authtoken, base_path=options.basepath) # Counters (total, created, skipped and failed) a, f = 0, 0 # Read file and import try: with open(options.filename, 'r') as import_file: # Init reader and check headers reader = csv.DictReader(import_file) # Iterate rules and try to import them for rule_def in reader: key = rule_def.pop('key', None) try: # Pop key, clean data and attempt activation rule_def['reset'] = rule_def.get('reset', '').lower() in ('y', 'yes', 'true') rule_def = {k: v for k, v in rule_def.items() if v} h.activate_rule(key, options.profile_key, **rule_def) a += 1 except ValidationError as e: # Invalid data, print error sys.stderr.write("Failed to activate rule {}: " "{}\n".format(key, e)) f += 1 except Exception as e: # Other errors, stop execution immediately sys.stderr.write("Error: {}\n".format(e)) status = 'Incomplete' else: # No errors, write result status = 'Complete' # Finally, write results sys.stdout.write("{} rules activation: {} activated and " "{} failed.\n".format(status, a, f))
def main(): """ Activate rules in a profile using a SonarAPIHandler instance. """ options = parser.parse_args() h = SonarAPIHandler(options.host, options.port, options.user, options.password) # Counters (total, created, skipped and failed) a, f = 0, 0 # Read file and import try: with open(options.filename, 'r') as import_file: # Init reader and check headers reader = csv.DictReader(import_file) # Iterate rules and try to import them for rule_def in reader: key = rule_def.pop('key', None) try: # Pop key, clean data and attempt activation rule_def['reset'] = rule_def.get('reset', '').lower() in ('y', 'yes', 'true') rule_def = {k: v for k, v in rule_def.items() if v} h.activate_rule(key, options.profile_key, **rule_def) a += 1 except ValidationError as e: # Invalid data, print error sys.stderr.write("Failed to activate rule {}: " "{}\n".format(key, e)) f += 1 except Exception as e: # Other errors, stop execution immediately sys.stderr.write("Error: {}\n".format(e)) status = 'Incomplete' else: # No errors, write result status = 'Complete' # Finally, write results sys.stdout.write("{} rules activation: {} activated and " "{} failed.\n".format(status, a, f))
def main(): """ Manage a SonarQube's users, using a SonarAPIHandler connected to the given host. """ options = parser.parse_args() h = SonarAPIHandler(host=options.host, port=options.port, user=options.user, password=options.password, token=options.authtoken, base_path=options.basepath) if options.command == 'list': users = h.get_users(options.logins, options.deactivated).json() table = PrettyTable(['Login', 'Name', 'Email', 'Groups', 'Active']) for user in users['users']: table.add_row([ user.get('login'), user.get('name'), user.get('email'), user.get('groups'), user.get('active') ]) print(table) elif options.command == 'create': res = h.create_user(options.login, options.user_pass, options.name, options.email).json() print(res['user']) elif options.command == 'update': res = h.update_user(options.login, options.name, options.email).json() print(res['user']) elif options.command == 'deactivate': res = h.deactivate_user(options.login).json() print(res['user'])
def test_main(self, post_mock, parse_mock, stderr_mock, stdout_mock, open_mock): # Set call arguments parse_mock.return_value = mock.MagicMock(host='localhost', port='9000', user='******', password='******', profile_key='py-234345', filename='active-rules.csv') # Mock file handlers csv_file = StringIO( # Headers u'key,reset,severity,xpathQuery,message,format\n' # Standard rules: only reset first three 'pylint:123,yes,,,,\n' 'pylint:234,TRUE,,,,\n' 'pylint:345,Y,,,,\n' 'pylint:346,,,,,\n' # Customized rule: set severity and format 'S123,,major,,,^foo|bar$\n' # Custom rule: set severity, xpath and message 'X123,no,BLOCKER,\lala,Do not use lala,\n' # Error: incorrect severity 'X123,no,so-so,\lala,Do not use lala,\n') open_mock.return_value = csv_file # Set data to receive from server post_mock.side_effect = [ # First fix rules OK mock.MagicMock(status_code=200), mock.MagicMock(status_code=200), mock.MagicMock(status_code=200), mock.MagicMock(status_code=200), mock.MagicMock(status_code=200), mock.MagicMock(status_code=200), # Sixth rule wrong: bad severity mock.MagicMock( status_code=400, json=mock.MagicMock( return_value={ 'errors': [{ 'msg': "Value of parameter 'severity' (SO-SO) " "must be one of: [INFO, MINOR, MAJOR, CRITICAL, BLOCKER]." }] })), ] # Execute command activate_rules.main() # Check post calls # Note: check by one to ease debugging h = SonarAPIHandler(host='localhost', port='9000', user='******', password='******') url = h._get_url(h.RULES_ACTIVATION_ENDPOINT) self.assertEqual( post_mock.mock_calls[0], mock.call(url, data={ 'profile_key': 'py-234345', 'rule_key': 'pylint:123', 'reset': 'true' })) self.assertEqual( post_mock.mock_calls[1], mock.call(url, data={ 'profile_key': 'py-234345', 'rule_key': 'pylint:234', 'reset': 'true' })) self.assertEqual( post_mock.mock_calls[2], mock.call(url, data={ 'profile_key': 'py-234345', 'rule_key': 'pylint:345', 'reset': 'true' })) self.assertEqual( post_mock.mock_calls[3], mock.call(url, data={ 'profile_key': 'py-234345', 'rule_key': 'pylint:346', 'reset': 'false' })) self.assertEqual( post_mock.mock_calls[4], mock.call(url, data={ 'profile_key': 'py-234345', 'rule_key': 'S123', 'reset': 'false', 'severity': 'MAJOR', 'params': 'format=^foo|bar$' })) self.assertEqual( post_mock.mock_calls[5], mock.call(url, data={ 'profile_key': 'py-234345', 'rule_key': 'X123', 'reset': 'false', 'severity': 'BLOCKER', 'params': 'message=Do not use lala;xpathQuery=\\lala' })) self.assertEqual( post_mock.mock_calls[6], mock.call(url, data={ 'profile_key': 'py-234345', 'rule_key': 'X123', 'reset': 'false', 'severity': 'SO-SO', 'params': 'message=Do not use lala;xpathQuery=\\lala' })) # Check error calls stderr_mock.write.assert_called_once_with( "Failed to activate rule X123: Value of parameter 'severity' " "(SO-SO) must be one of: [INFO, MINOR, MAJOR, CRITICAL, BLOCKER].\n" ) # Check stdout write: 3 exported and 1 failed stdout_mock.write.assert_called_once_with( 'Complete rules activation: 6 activated and 1 failed.\n')
def main(): """ Migrate custom rules from one server to another one using two SonarAPIHandler instances. """ options = parser.parse_args() sh = SonarAPIHandler(host=options.source_host, port=options.source_port, user=options.source_user, password=options.source_password, token=options.source_authtoken, base_path=options.source_basepath) th = SonarAPIHandler(host=options.target_host, port=options.target_port, user=options.target_user, password=options.target_password, token=options.target_authtoken, base_path=options.target_basepath) # Get the generator of source rules rules = sh.get_rules(active_only=True, custom_only=True) # Counters (total, created, skipped and failed) c, s, f = 0, 0, 0 # Now import and keep count try: for rule in rules: # Ensure we have params (only custom rules have them) params = rule.get('params') if params: # Ok, let's try to create it try: # Get key, message, and xpath params key = rule['key'].split(':')[-1] message = None xpath = None for p in params: if p['key'] == 'message': message = p['defaultValue'] elif p['key'] == 'xpathQuery': xpath = p['defaultValue'] # Now create it and increase counter th.create_rule(key, rule['name'], rule['mdDesc'], message, xpath, rule['severity'], rule['status'], rule['templateKey']) c += 1 except ValidationError as e: # Validation error, should continue execution afterwards if 'already exists' in str(e): # Rule already exists, skip s += 1 else: # Invalid data for rule creation, fail f += 1 sys.stderr.write("Failed to create rule {}: " "{}\n".format(rule['key'], e)) except Exception as e: # Other errors, stop execution immediately sys.stderr.write("Error: {}\n".format(e)) status = 'Incomplete' else: # No errors, write result status = 'Complete' # Finally, write results sys.stdout.write("{} rules migration: {} created, {} skipped (already " "existing) and {} failed.\n".format(status, c, s, f))
class GroupsTest(TestCase): def setUp(self): self.host = 'http://localhost' self.port = '9000' self.user = '******' self.password = '******' self.test_group = str(uuid.uuid1()) username = str(uuid.uuid1()) self.sonar = SonarAPIHandler(user=self.user, password=self.password) self.test_user = self.sonar.create_user( username, 'qwerty', username, "{}@example.com".format(username)).json().get('user') # List @mock.patch('sonarqube_api.cmd.users.argparse.ArgumentParser.parse_args') def test_cmd_list_groups(self, parse_mock): parse_mock.return_value = argparse.Namespace(host=self.host, port=self.port, user=self.user, password=self.password, authtoken=None, basepath=None, command='list', fields=None, query=None) groups.main() # Create @mock.patch('sonarqube_api.cmd.users.argparse.ArgumentParser.parse_args') def test_cmd_create_group(self, parse_mock): parse_mock.return_value = argparse.Namespace(host=self.host, port=self.port, user=self.user, password=self.password, authtoken=None, basepath=None, command='create', name=self.test_group, description=None) groups.main() # Update and delete @mock.patch('sonarqube_api.cmd.users.argparse.ArgumentParser.parse_args') def test_cmd_update_delete_group(self, parse_mock): res = self.sonar.create_group(str(uuid.uuid1())).json() parse_mock.return_value = argparse.Namespace( host=self.host, port=self.port, user=self.user, password=self.password, authtoken=None, basepath=None, command='update', gid=res['group']['id'], name=None, description='Awesome group') groups.main() parse_mock.return_value = argparse.Namespace(host=self.host, port=self.port, user=self.user, password=self.password, authtoken=None, basepath=None, command='delete', gid=res['group']['id'], name=None) groups.main() # Add and remove user @mock.patch('sonarqube_api.cmd.users.argparse.ArgumentParser.parse_args') def test_cmd_add_remove_user_group(self, parse_mock): res = self.sonar.create_group(str(uuid.uuid1())).json() parse_mock.return_value = argparse.Namespace( host=self.host, port=self.port, user=self.user, password=self.password, authtoken=None, basepath=None, command='add-user', login=self.test_user['login'], name=res['group']['name'], gid=None) groups.main() parse_mock.return_value = argparse.Namespace( host=self.host, port=self.port, user=self.user, password=self.password, authtoken=None, basepath=None, command='remove-user', login=self.test_user['login'], name=res['group']['name'], gid=None) groups.main() self.sonar.delete_group(name=res['group']['name']) # List users @mock.patch('sonarqube_api.cmd.users.argparse.ArgumentParser.parse_args') def test_cmd_list_users_group(self, parse_mock): res = self.sonar.create_group(str(uuid.uuid1())).json() self.sonar.add_user_group(self.test_user['login'], name=res['group']['name']) parse_mock.return_value = argparse.Namespace(host=self.host, port=self.port, user=self.user, password=self.password, authtoken=None, basepath=None, command='list-users', name=res['group']['name'], gid=None, query=None) groups.main() self.sonar.delete_group(name=res['group']['name'])
def main(): """ Export a SonarQube's rules to a CSV and an HTML file, using a SonarAPIHandler connected to the given host. """ options = parser.parse_args() h = SonarAPIHandler(options.host, options.port, options.user, options.password) # Determine output csv and html file names csv_fn = os.path.expanduser(os.path.join(options.output, 'rules.csv')) html_fn = os.path.expanduser(os.path.join(options.output, 'rules.html')) # Open csv and html files with open(csv_fn, 'w') as csv_f, open(html_fn, 'w') as html_f: # Init csv writer and write header csv_w = csv.writer(csv_f) csv_w.writerow(['language', 'key', 'name', 'debt', 'severity']) # Start html file html_f.write(u'<html><body>') # Get the rules generator rules = h.get_rules(options.active, options.profile, options.languages) # Counters (total, exported and failed) s, f = 0, 0 # Now import and keep count try: for rule in rules: try: # Write CSV row csv_w.writerow([ rule['langName'], rule['key'], rule['name'], # Note: debt can be in diff. fields depending on type rule.get('debtRemFnOffset', rule.get('debtRemFnCoeff', u'-')), rule['severity'] ]) # Render parameters sublist params_htmls = [] if rule['params']: for param in rule['params']: params_htmls.append(u'<li>{}: {}</li>'.format( param.get('key', u'-'), param.get('defaultValue', u'-') )) else: params_htmls.append(u'-') # Build values to write in html values = ( rule['key'], rule['name'], rule['langName'], rule['key'], rule['severity'], rule.get('debtRemFnOffset', rule.get('debtRemFnCoeff', u'-')), u''.join(params_htmls), rule.get('htmlDesc', u'-') ) # Render html and write to file html = utf_encode(HTML_RULE_TEMPLATE.format(*values)) html_f.write(html) s += 1 except KeyError as exc: # Key error, should continue execution afterwards sys.stderr.write("Error: missing values for {}\n".format(','.join(exc.args))) f += 1 # Done with rules, close html body and document html_f.write(u'</body></html>') except Exception as exc: # Other errors, stop execution immediately sys.stderr.write("Error: {}\n".format(exc)) status = 'Incomplete' else: # No errors, complete status = 'Complete' # Finally, write results sys.stdout.write("{} rules export: {} exported and " "{} failed.\n".format(status, s, f))
def test_main(self, post_mock, parse_mock, stderr_mock, stdout_mock, open_mock): # Set call arguments parse_mock.return_value = mock.MagicMock( host='localhost', port='9000', user='******', password='******', profile_key='py-234345', filename='active-rules.csv', basepath=None ) # Mock file handlers csv_file = StringIO( # Headers u'key,reset,severity,xpathQuery,message,format\n' # Standard rules: only reset first three 'pylint:123,yes,,,,\n' 'pylint:234,TRUE,,,,\n' 'pylint:345,Y,,,,\n' 'pylint:346,,,,,\n' # Customized rule: set severity and format 'S123,,major,,,^foo|bar$\n' # Custom rule: set severity, xpath and message 'X123,no,BLOCKER,\lala,Do not use lala,\n' # Error: incorrect severity 'X123,no,so-so,\lala,Do not use lala,\n' ) open_mock.return_value = csv_file # Set data to receive from server post_mock.side_effect = [ # First fix rules OK mock.MagicMock(status_code=200), mock.MagicMock(status_code=200), mock.MagicMock(status_code=200), mock.MagicMock(status_code=200), mock.MagicMock(status_code=200), mock.MagicMock(status_code=200), # Sixth rule wrong: bad severity mock.MagicMock(status_code=400, json=mock.MagicMock(return_value={'errors': [{ 'msg': "Value of parameter 'severity' (SO-SO) " "must be one of: [INFO, MINOR, MAJOR, CRITICAL, BLOCKER]." }]})), ] # Execute command activate_rules.main() # Check post calls # Note: check by one to ease debugging h = SonarAPIHandler(host='localhost', port='9000', user='******', password='******') url = h._get_url(h.RULES_ACTIVATION_ENDPOINT) self.assertEqual(post_mock.mock_calls[0], mock.call( url, data={'profile_key': 'py-234345', 'rule_key': 'pylint:123', 'reset': 'true'} )) self.assertEqual(post_mock.mock_calls[1], mock.call( url, data={'profile_key': 'py-234345', 'rule_key': 'pylint:234', 'reset': 'true'} )) self.assertEqual(post_mock.mock_calls[2], mock.call( url, data={'profile_key': 'py-234345', 'rule_key': 'pylint:345', 'reset': 'true'} )) self.assertEqual(post_mock.mock_calls[3], mock.call( url, data={'profile_key': 'py-234345', 'rule_key': 'pylint:346', 'reset': 'false'} )) self.assertEqual(post_mock.mock_calls[4], mock.call( url, data={'profile_key': 'py-234345', 'rule_key': 'S123', 'reset': 'false', 'severity': 'MAJOR', 'params': 'format=^foo|bar$'} )) self.assertEqual(post_mock.mock_calls[5], mock.call( url, data={'profile_key': 'py-234345', 'rule_key': 'X123', 'reset': 'false', 'severity': 'BLOCKER', 'params': 'message=Do not use lala;xpathQuery=\\lala'} )) self.assertEqual(post_mock.mock_calls[6], mock.call( url, data={'profile_key': 'py-234345', 'rule_key': 'X123', 'reset': 'false', 'severity': 'SO-SO', 'params': 'message=Do not use lala;xpathQuery=\\lala'} )) # Check error calls stderr_mock.write.assert_called_once_with( "Failed to activate rule X123: Value of parameter 'severity' " "(SO-SO) must be one of: [INFO, MINOR, MAJOR, CRITICAL, BLOCKER].\n" ) # Check stdout write: 3 exported and 1 failed stdout_mock.write.assert_called_once_with('Complete rules activation: 6 activated and 1 failed.\n')
def main(): """ Export a SonarQube's rules to a CSV and an HTML file, using a SonarAPIHandler connected to the given host. """ options = parser.parse_args() h = SonarAPIHandler(options.host, options.port, options.user, options.password) # Determine output csv and html file names csv_fn = os.path.expanduser(os.path.join(options.output, 'rules.csv')) html_fn = os.path.expanduser(os.path.join(options.output, 'rules.html')) # Open csv and html files with open(csv_fn, 'w') as csv_f, open(html_fn, 'w') as html_f: # Init csv writer and write header csv_w = csv.writer(csv_f) csv_w.writerow(['language', 'key', 'name', 'debt', 'severity']) # Start html file html_f.write(u'<html><body>') # Get the rules generator rules = h.get_rules(options.active, options.profile, options.languages) # Counters (total, exported and failed) s, f = 0, 0 # Now import and keep count try: for rule in rules: try: # Write CSV row csv_w.writerow([ rule['langName'], rule['key'], rule['name'], # Note: debt can be in diff. fields depending on type rule.get('debtRemFnOffset', rule.get('debtRemFnCoeff', u'-')), rule['severity'] ]) # Render parameters sublist params_htmls = [] if rule['params']: for param in rule['params']: params_htmls.append(u'<li>{}: {}</li>'.format( param.get('key', u'-'), param.get('defaultValue', u'-'))) else: params_htmls.append(u'-') # Build values to write in html values = (rule['key'], rule['name'], rule['langName'], rule['key'], rule['severity'], rule.get('debtRemFnOffset', rule.get('debtRemFnCoeff', u'-')), u''.join(params_htmls), rule.get('htmlDesc', u'-')) # Render html and write to file html = utf_encode(HTML_RULE_TEMPLATE.format(*values)) html_f.write(html) s += 1 except KeyError as exc: # Key error, should continue execution afterwards sys.stderr.write("Error: missing values for {}\n".format( ','.join(exc.args))) f += 1 # Done with rules, close html body and document html_f.write(u'</body></html>') except Exception as exc: # Other errors, stop execution immediately sys.stderr.write("Error: {}\n".format(exc)) status = 'Incomplete' else: # No errors, complete status = 'Complete' # Finally, write results sys.stdout.write("{} rules export: {} exported and " "{} failed.\n".format(status, s, f))