class TestToggleRundeckLock(asynctest.TestCase): def setUp(self): patcher1 = patch('charlesbot_rundeck.rundeck_lock.RundeckLock.seed_job_list') # NOQA self.addCleanup(patcher1.stop) self.mock_seed_job_list = patcher1.start() from charlesbot_rundeck.rundeck_lock import RundeckLock self.rd_lock = RundeckLock("token", "url", "channel", []) self.rd_lock.rundeck_jobs = [RundeckJob(friendly_name="job1")] self.rd_lock.populate_slack_user_object = CoroutineMock() self.rd_lock.slack.send_channel_message = CoroutineMock() self.rd_lock.lock_or_unlock_rundeck_job = CoroutineMock() self.rd_lock.set_channel_topic = CoroutineMock() self.rd_lock.print_lock_status = CoroutineMock() self.sm = SlackMessage(user="******", channel="C001") self.su = SlackUser(name="suser", is_admin=True) def test_unauthorized_user(self): self.su.is_admin = False self.rd_lock.populate_slack_user_object.side_effect = [self.su] yield from self.rd_lock.toggle_rundeck_lock(self.sm, True) calls = [ call("C001", "Sorry <@suser>, you are not allowed to lock Rundeck executions.") # NOQA ] self.assertEqual(self.rd_lock.slack.send_channel_message.mock_calls, calls) def test_authorized_user_unlock(self): self.rd_lock.populate_slack_user_object.side_effect = [self.su] yield from self.rd_lock.toggle_rundeck_lock(self.sm, False) self.assertEqual(self.rd_lock.set_channel_topic.mock_calls, [call(False)]) send_channel_msg_calls = [ call("C001", ":white_check_mark: Rundeck executions unlocked! :white_check_mark:") # NOQA ] self.assertEqual(self.rd_lock.slack.send_channel_message.mock_calls, send_channel_msg_calls) self.assertEqual(self.rd_lock.print_lock_status.mock_calls, [call(self.sm)]) def test_authorized_user_lock(self): self.rd_lock.populate_slack_user_object.side_effect = [self.su] yield from self.rd_lock.toggle_rundeck_lock(self.sm, True) self.assertEqual(self.rd_lock.set_channel_topic.mock_calls, [call(True)]) send_channel_msg_calls = [ call("C001", ":lock: Rundeck executions locked by <@suser> :lock:") ] self.assertEqual(self.rd_lock.slack.send_channel_message.mock_calls, send_channel_msg_calls) self.assertEqual(self.rd_lock.print_lock_status.mock_calls, [call(self.sm)])
class TestLockUnlockRundeckJob(asynctest.TestCase): def setUp(self): patcher1 = patch('charlesbot_rundeck.rundeck_lock.RundeckLock.seed_job_list') # NOQA self.addCleanup(patcher1.stop) self.mock_seed_job_list = patcher1.start() patcher2 = patch('charlesbot_rundeck.rundeck_lock.http_post_request') self.addCleanup(patcher2.stop) self.mock_http_post_request = patcher2.start() from charlesbot_rundeck.rundeck_lock import RundeckLock self.rd_lock = RundeckLock("token", "url", "channel", []) self.rd_job = RundeckJob(execution_enabled=False, href="job.com") self.headers = { "Accept": "application/json", "X-Rundeck-Auth-Token": "token", } def test_lock_invalid_response(self): self.mock_http_post_request.side_effect = [""] yield from self.rd_lock.lock_or_unlock_rundeck_job(self.rd_job, True) calls = [ call("job.com/execution/disable", self.headers) ] self.assertFalse(self.rd_job.execution_enabled) self.assertEqual(self.mock_http_post_request.mock_calls, calls) def test_lock_valid_response(self): self.mock_http_post_request.side_effect = ["200"] yield from self.rd_lock.lock_or_unlock_rundeck_job(self.rd_job, True) calls = [ call("job.com/execution/disable", self.headers) ] self.assertTrue(self.rd_job.execution_enabled) self.assertEqual(self.mock_http_post_request.mock_calls, calls) def test_unlock_valid_response(self): self.rd_job.execution_enabled = True self.mock_http_post_request.side_effect = ["200"] yield from self.rd_lock.lock_or_unlock_rundeck_job(self.rd_job, False) calls = [ call("job.com/execution/enable", self.headers) ] self.assertFalse(self.rd_job.execution_enabled) self.assertEqual(self.mock_http_post_request.mock_calls, calls)
class Rundeck(BasePlugin): def __init__(self): super().__init__("Rundeck") self.load_config() def load_config(self): # pragma: no cover config_dict = configuration.get() self.rundeck_token = config_dict['rundeck']['token'] self.rundeck_url = config_dict['rundeck']['url'] try: # It's okay if this key isn't set self.topic_channel = config_dict['rundeck']['deployment_status_channel'] # NOQA except KeyError: self.topic_channel = None self.rd_jobs_raw_list = config_dict['rundeck']['lock_jobs'] self.rundeck_lock = RundeckLock(self.rundeck_token, self.rundeck_url, self.topic_channel, self.rd_jobs_raw_list) def get_help_message(self): help_msg = [] help_msg.append("!lock status - Prints the status of the Rundeck deployment lock") # NOQA help_msg.append("!lock acquire - Acquires the Rundeck deployment lock (only available to Slack admins)") # NOQA help_msg.append("!lock release - Releases the Rundeck deployment lock (only available to Slack admins)") # NOQA return "\n".join(help_msg) @asyncio.coroutine def process_message(self, message): """ Main method that handles all messages sent to this plugin """ if not type(message) is SlackMessage: return parsed_message = parse_msg_with_prefix("!lock", message.text) if not parsed_message: return if does_msg_contain_prefix("acquire", parsed_message): yield from self.rundeck_lock.toggle_rundeck_lock(message, lock_job=True) elif does_msg_contain_prefix("release", parsed_message): yield from self.rundeck_lock.toggle_rundeck_lock(message, lock_job=False) elif does_msg_contain_prefix("status", parsed_message): yield from self.rundeck_lock.print_lock_status(message)
def setUp(self): patcher1 = patch('charlesbot_rundeck.rundeck_lock.RundeckLock.seed_job_list') # NOQA self.addCleanup(patcher1.stop) self.mock_seed_job_list = patcher1.start() from charlesbot_rundeck.rundeck_lock import RundeckLock self.rd_lock = RundeckLock("token", "url", "channel", []) self.rd_lock.slack.send_channel_message = CoroutineMock() self.rd_lock.trigger_rundeck_executions_allowed_update = CoroutineMock() # NOQA self.sm = SlackMessage(channel="sixchan")
def setUp(self): patcher1 = patch('charlesbot_rundeck.rundeck_lock.RundeckLock.seed_job_list') # NOQA self.addCleanup(patcher1.stop) self.mock_seed_job_list = patcher1.start() patcher2 = patch('charlesbot_rundeck.rundeck_lock.http_get_request') self.addCleanup(patcher2.stop) self.mock_http_get_request = patcher2.start() from charlesbot_rundeck.rundeck_lock import RundeckLock self.rd_lock = RundeckLock("token", "url", "channel", [])
def load_config(self): # pragma: no cover config_dict = configuration.get() self.rundeck_token = config_dict['rundeck']['token'] self.rundeck_url = config_dict['rundeck']['url'] try: # It's okay if this key isn't set self.topic_channel = config_dict['rundeck']['deployment_status_channel'] # NOQA except KeyError: self.topic_channel = None self.rd_jobs_raw_list = config_dict['rundeck']['lock_jobs'] self.rundeck_lock = RundeckLock(self.rundeck_token, self.rundeck_url, self.topic_channel, self.rd_jobs_raw_list)
def setUp(self): patcher1 = patch('charlesbot_rundeck.rundeck_lock.RundeckLock.seed_job_list') # NOQA self.addCleanup(patcher1.stop) self.mock_seed_job_list = patcher1.start() patcher2 = patch('charlesbot.slack.slack_connection.SlackConnection.api_call') # NOQA self.addCleanup(patcher2.stop) self.mock_api_call = patcher2.start() from charlesbot_rundeck.rundeck_lock import RundeckLock self.rd_lock = RundeckLock("token", "url", "channel", [])
def setUp(self): patcher1 = patch('charlesbot_rundeck.rundeck_lock.RundeckLock.seed_job_list') # NOQA self.addCleanup(patcher1.stop) self.mock_seed_job_list = patcher1.start() from charlesbot_rundeck.rundeck_lock import RundeckLock self.rd_lock = RundeckLock("token", "url", "channel", []) self.rd_lock.rundeck_jobs = [RundeckJob(friendly_name="job1")] self.rd_lock.populate_slack_user_object = CoroutineMock() self.rd_lock.slack.send_channel_message = CoroutineMock() self.rd_lock.lock_or_unlock_rundeck_job = CoroutineMock() self.rd_lock.set_channel_topic = CoroutineMock() self.rd_lock.print_lock_status = CoroutineMock() self.sm = SlackMessage(user="******", channel="C001") self.su = SlackUser(name="suser", is_admin=True)
def setUp(self): patcher1 = patch('charlesbot_rundeck.rundeck_lock.RundeckLock.seed_job_list') # NOQA self.addCleanup(patcher1.stop) self.mock_seed_job_list = patcher1.start() patcher2 = patch('charlesbot_rundeck.rundeck_lock.http_post_request') self.addCleanup(patcher2.stop) self.mock_http_post_request = patcher2.start() from charlesbot_rundeck.rundeck_lock import RundeckLock self.rd_lock = RundeckLock("token", "url", "channel", []) self.rd_job = RundeckJob(execution_enabled=False, href="job.com") self.headers = { "Accept": "application/json", "X-Rundeck-Auth-Token": "token", }
class TestUpdateExecutionEnabledStatus(asynctest.TestCase): def setUp(self): patcher1 = patch('charlesbot_rundeck.rundeck_lock.RundeckLock.seed_job_list') # NOQA self.addCleanup(patcher1.stop) self.mock_seed_job_list = patcher1.start() patcher2 = patch('charlesbot_rundeck.rundeck_lock.http_get_request') self.addCleanup(patcher2.stop) self.mock_http_get_request = patcher2.start() from charlesbot_rundeck.rundeck_lock import RundeckLock self.rd_lock = RundeckLock("token", "url", "channel", []) def test_empty_task_list(self): self.rd_lock.rundeck_jobs = [] yield from self.rd_lock.trigger_rundeck_executions_allowed_update() self.assertEqual(self.mock_http_get_request.mock_calls, []) def test_multiple_tasks_execution_disabled(self): rd_job1 = RundeckJob(friendly_name="job1") rd_job2 = RundeckJob(friendly_name="job2") self.rd_lock.rundeck_jobs = [ rd_job1, rd_job2 ] side_effect = [ "<joblist><job><description></description><executionEnabled>false</executionEnabled></job></joblist>", # NOQA "<joblist><job><description></description><executionEnabled>false</executionEnabled></job></joblist>", # NOQA ] self.mock_http_get_request.side_effect = side_effect yield from self.rd_lock.trigger_rundeck_executions_allowed_update() self.assertEqual({False, False}, {rd_job1.execution_enabled, rd_job2.execution_enabled}) # NOQA def test_multiple_tasks_execution_enabled(self): rd_job1 = RundeckJob(friendly_name="job1") rd_job2 = RundeckJob(friendly_name="job2") self.rd_lock.rundeck_jobs = [ rd_job1, rd_job2 ] side_effect = [ "<joblist><job><description></description><executionEnabled>true</executionEnabled></job></joblist>", # NOQA "<joblist><job><description></description><executionEnabled>true</executionEnabled></job></joblist>", # NOQA ] self.mock_http_get_request.side_effect = side_effect yield from self.rd_lock.trigger_rundeck_executions_allowed_update() self.assertEqual({True, True}, {rd_job1.execution_enabled, rd_job2.execution_enabled}) # NOQA def test_multiple_tasks_mixed_execution_states(self): rd_job1 = RundeckJob(friendly_name="job1") rd_job2 = RundeckJob(friendly_name="job2") self.rd_lock.rundeck_jobs = [ rd_job1, rd_job2, ] side_effect = [ "<joblist><job><description></description><executionEnabled>true</executionEnabled></job></joblist>", # NOQA "<joblist><job><description></description><executionEnabled>false</executionEnabled></job></joblist>", # NOQA ] self.mock_http_get_request.side_effect = side_effect yield from self.rd_lock.trigger_rundeck_executions_allowed_update() self.assertEqual({True, False}, {rd_job1.execution_enabled, rd_job2.execution_enabled}) # NOQA
class TestPrintLockStatus(asynctest.TestCase): def setUp(self): patcher1 = patch('charlesbot_rundeck.rundeck_lock.RundeckLock.seed_job_list') # NOQA self.addCleanup(patcher1.stop) self.mock_seed_job_list = patcher1.start() from charlesbot_rundeck.rundeck_lock import RundeckLock self.rd_lock = RundeckLock("token", "url", "channel", []) self.rd_lock.slack.send_channel_message = CoroutineMock() self.rd_lock.trigger_rundeck_executions_allowed_update = CoroutineMock() # NOQA self.sm = SlackMessage(channel="sixchan") def test_empty_job_list(self): self.rd_lock.rundeck_jobs = [] expected_slack_msg_raw = [] expected_slack_msg_raw.append("*Rundeck Job Lock Report*") expected_slack_msg_raw.append("```") expected_slack_msg_raw.append("```") expected_slack_msg = "\n".join(expected_slack_msg_raw) expected_call = call("sixchan", expected_slack_msg) yield from self.rd_lock.print_lock_status(self.sm) self.assertEqual(self.rd_lock.slack.send_channel_message.mock_calls, [expected_call]) def test_single_enabled_job(self): self.rd_lock.rundeck_jobs = [ RundeckJob(friendly_name="job1", execution_enabled=True) ] expected_slack_msg_raw = [] expected_slack_msg_raw.append("*Rundeck Job Lock Report*") expected_slack_msg_raw.append("```") expected_slack_msg_raw.append("job1: unlocked") expected_slack_msg_raw.append("```") expected_slack_msg = "\n".join(expected_slack_msg_raw) expected_call = call("sixchan", expected_slack_msg) yield from self.rd_lock.print_lock_status(self.sm) self.assertEqual(self.rd_lock.slack.send_channel_message.mock_calls, [expected_call]) def test_single_disabled_job(self): self.rd_lock.rundeck_jobs = [ RundeckJob(friendly_name="job1", execution_enabled=False) ] expected_slack_msg_raw = [] expected_slack_msg_raw.append("*Rundeck Job Lock Report*") expected_slack_msg_raw.append("```") expected_slack_msg_raw.append("job1: locked") expected_slack_msg_raw.append("```") expected_slack_msg = "\n".join(expected_slack_msg_raw) expected_call = call("sixchan", expected_slack_msg) yield from self.rd_lock.print_lock_status(self.sm) self.assertEqual(self.rd_lock.slack.send_channel_message.mock_calls, [expected_call]) def test_multiple_mixed_jobs(self): self.rd_lock.rundeck_jobs = [ RundeckJob(friendly_name="job1", execution_enabled=False), RundeckJob(friendly_name="job3", execution_enabled=True), RundeckJob(friendly_name="job2", execution_enabled=False) ] expected_slack_msg_raw = [] expected_slack_msg_raw.append("*Rundeck Job Lock Report*") expected_slack_msg_raw.append("```") expected_slack_msg_raw.append("job1: locked") expected_slack_msg_raw.append("job3: unlocked") expected_slack_msg_raw.append("job2: locked") expected_slack_msg_raw.append("```") expected_slack_msg = "\n".join(expected_slack_msg_raw) expected_call = call("sixchan", expected_slack_msg) yield from self.rd_lock.print_lock_status(self.sm) self.assertEqual(self.rd_lock.slack.send_channel_message.mock_calls, [expected_call])
class TestLoadRundeckJobs(asynctest.TestCase): def setUp(self): patcher1 = patch('charlesbot_rundeck.rundeck_lock.RundeckLock.seed_job_list') # NOQA self.addCleanup(patcher1.stop) self.mock_seed_job_list = patcher1.start() patcher2 = patch('charlesbot_rundeck.rundeck_lock.RundeckJob.retrieve_rundeck_job_info') # NOQA self.addCleanup(patcher2.stop) self.mock_rundeck_job = patcher2.start() from charlesbot_rundeck.rundeck_lock import RundeckLock self.rd_lock = RundeckLock("token", "url", "channel", []) def test_empty_raw_job_list(self): self.rd_lock.rd_jobs_raw_list = [] yield from self.rd_lock.load_rundeck_jobs() self.assertEqual(self.rd_lock.rundeck_jobs, []) def test_friendly_name_key_not_present(self): self.rd_lock.rd_jobs_raw_list = [ { "key1": "val1", } ] with self.assertRaises(KeyError): yield from self.rd_lock.load_rundeck_jobs() self.assertEqual(self.rd_lock.rundeck_jobs, []) def test_single_unsuccessful_job(self): self.rd_lock.rd_jobs_raw_list = [ { "friendly_name": "fname1", "project": "project1", "name": "name1", } ] self.mock_rundeck_job.side_effect = [False] yield from self.rd_lock.load_rundeck_jobs() self.assertEqual(self.rd_lock.rundeck_jobs, []) def test_multiple_mixed_success_job_1(self): self.rd_lock.rd_jobs_raw_list = [ { "friendly_name": "fname1", "project": "project1", "name": "name1", }, { "friendly_name": "fname2", "project": "project2", "name": "name2", }, { "friendly_name": "fname3", "project": "project3", "name": "name3", }, ] self.mock_rundeck_job.side_effect = [True, False, True] yield from self.rd_lock.load_rundeck_jobs() self.assertEqual(len(self.rd_lock.rundeck_jobs), 2) def test_multiple_mixed_success_job_2(self): self.rd_lock.rd_jobs_raw_list = [ { "friendly_name": "fname1", "project": "project1", "name": "name1", }, { "friendly_name": "fname2", "project": "project2", "name": "name2", }, { "friendly_name": "fname3", "project": "project3", "name": "name3", }, ] self.mock_rundeck_job.side_effect = [False, True, True] yield from self.rd_lock.load_rundeck_jobs() self.assertEqual(len(self.rd_lock.rundeck_jobs), 2) def test_multiple_mixed_success_job_3(self): self.rd_lock.rd_jobs_raw_list = [ { "friendly_name": "fname1", "project": "project1", "name": "name1", }, { "friendly_name": "fname2", "project": "project2", "name": "name2", }, { "friendly_name": "fname3", "project": "project3", "name": "name3", }, ] self.mock_rundeck_job.side_effect = [True, True, False] yield from self.rd_lock.load_rundeck_jobs() self.assertEqual(len(self.rd_lock.rundeck_jobs), 2) def test_multiple_successful_jobs(self): self.rd_lock.rd_jobs_raw_list = [ { "friendly_name": "fname1", "project": "project1", "name": "name1", }, { "friendly_name": "fname2", "project": "project2", "name": "name2", }, { "friendly_name": "fname3", "project": "project3", "name": "name3", }, ] self.mock_rundeck_job.side_effect = [True, True, True] yield from self.rd_lock.load_rundeck_jobs() self.assertEqual(len(self.rd_lock.rundeck_jobs), 3)
class TestSetChannelTopic(asynctest.TestCase): def setUp(self): patcher1 = patch('charlesbot_rundeck.rundeck_lock.RundeckLock.seed_job_list') # NOQA self.addCleanup(patcher1.stop) self.mock_seed_job_list = patcher1.start() patcher2 = patch('charlesbot.slack.slack_connection.SlackConnection.api_call') # NOQA self.addCleanup(patcher2.stop) self.mock_api_call = patcher2.start() from charlesbot_rundeck.rundeck_lock import RundeckLock self.rd_lock = RundeckLock("token", "url", "channel", []) def test_topic_channel_not_set(self): self.rd_lock.topic_channel = None yield from self.rd_lock.set_channel_topic(True) self.assertEqual(self.mock_api_call.mock_calls, []) def test_topic_channel_id_already_set(self): self.rd_lock.locked_by_user = "******" self.rd_lock.topic_channel = "chan1" self.rd_lock.topic_channel_id = "C1234" yield from self.rd_lock.set_channel_topic(True) calls = [ call("channels.setTopic", channel="C1234", topic=":lock: Rundeck executions locked by @bob :lock:") ] self.assertEqual(self.mock_api_call.mock_calls, calls) def test_topic_channel_not_found(self): channels = { "ok": True, "channels": [ { "name": "chan1", }, { "name": "chan2", }, { "name": "chan3", }, ] } self.rd_lock.locked_by_user = "******" self.rd_lock.topic_channel = "chan4" self.mock_api_call.side_effect = [json.dumps(channels), None] yield from self.rd_lock.set_channel_topic(True) calls = [ call("channels.list", exclude_archived=1), ] self.assertEqual(self.mock_api_call.mock_calls, calls) def test_topic_channel_found(self): channels = { "ok": True, "channels": [ { "name": "chan1", "id": "C1", }, { "name": "chan2", "id": "C2", }, { "name": "chan3", "id": "C3", }, ] } self.rd_lock.locked_by_user = "******" self.rd_lock.topic_channel = "chan2" self.mock_api_call.side_effect = [json.dumps(channels), None] yield from self.rd_lock.set_channel_topic(False) calls = [ call("channels.list", exclude_archived=1), call("channels.setTopic", channel="C2", topic="") ] self.assertEqual(self.mock_api_call.mock_calls, calls)