Esempio n. 1
0
    def setUp(self):
        patcher1 = patch(
            'charlesbot.slack.slack_connection.SlackConnection.api_call'
        )  # NOQA
        self.addCleanup(patcher1.stop)
        self.mock_api_call = patcher1.start()

        from charlesbot.slack.slack_connection import SlackConnection
        self.slack_connection = SlackConnection()

        from charlesbot.slack.slack_user import SlackUser
        self.su = SlackUser()
Esempio n. 2
0
    def setUp(self):
        patcher2 = patch(
            'charlesbot.slack.slack_connection.SlackConnection.api_call'
        )  # NOQA
        self.addCleanup(patcher2.stop)
        self.mock_api_call = patcher2.start()

        from charlesbot_jira.jira_helpers import send_jira_issue_response
        self.send_response = send_jira_issue_response

        from charlesbot.slack.slack_connection import SlackConnection
        self.slack_connection = SlackConnection()
Esempio n. 3
0
 def __init__(self, plugin_name):
     self.set_running(True)
     self.log = logging.getLogger(__name__)
     self.slack = SlackConnection()
     self._plugin_name = plugin_name
     self.log.info("Initializing the %s plugin" % plugin_name)
     self._q = asyncio.Queue()
     self.initialize_queue_consumer()
Esempio n. 4
0
    def setUp(self):
        patcher1 = patch('charlesbot.slack.slack_connection.SlackConnection.api_call')  # NOQA
        self.addCleanup(patcher1.stop)
        self.mock_api_call = patcher1.start()

        from charlesbot.slack.slack_connection import SlackConnection
        self.slack_connection = SlackConnection()

        from charlesbot.slack.slack_user import SlackUser
        self.su = SlackUser()
Esempio n. 5
0
class TestSendJiraIssueResponse(asynctest.TestCase):
    def setUp(self):
        patcher2 = patch(
            'charlesbot.slack.slack_connection.SlackConnection.api_call'
        )  # NOQA
        self.addCleanup(patcher2.stop)
        self.mock_api_call = patcher2.start()

        from charlesbot_jira.jira_helpers import send_jira_issue_response
        self.send_response = send_jira_issue_response

        from charlesbot.slack.slack_connection import SlackConnection
        self.slack_connection = SlackConnection()

    def tearDown(self):
        self.slack_connection._drop()

    def test_send_issue(self):
        issue = JiraIssue(key="JIRA-9",
                          summary="this is the problem",
                          description="detailed description here")
        expected_attachment = SlackAttachment(
            color="#A4ADAD",
            fallback="JIRA-9: this is the problem",
            text="detailed description here",
            mrkdwn_in=['text'],
            thumb_url=
            "https://slack.global.ssl.fastly.net/12d4/img/services/jira_48.png",  # NOQA
            title="JIRA-9: this is the problem",
            title_link="https://jira.atlassian.com/browse/JIRA-9")
        yield from self.send_response(self.slack_connection, "#work",
                                      "https://jira.atlassian.com", issue)
        expected_call = call(
            'chat.postMessage',
            channel="#work",
            attachments=expected_attachment,
            as_user=False,
            username="******",
            icon_url=
            "https://avatars.slack-edge.com/2015-07-31/8502215814_6662f69db3bed43d32e6_48.jpg"  # NOQA
        )
        self.assertEqual(self.mock_api_call.mock_calls, [expected_call])
    def setUp(self):
        self.channel_list_one = load_fixture("channel_list_one.json")
        self.channel_list_two = load_fixture("channel_list_two.json")
        self.channel_list_three = load_fixture("channel_list_three.json")
        self.channel_list_four = load_fixture("channel_list_four.json")

        from charlesbot.slack.slack_connection import SlackConnection

        self.slack = SlackConnection()
        self.slack.initialized = True
        self.slack.sc = MagicMock()
 def __init__(self, token, url, channel, jobs):
     self.log = logging.getLogger(__name__)
     self.rundeck_token = token
     self.rundeck_url = url
     self.topic_channel = channel
     self.topic_channel_id = None
     self.slack = SlackConnection()
     self.locked_by_user = ""
     self.rundeck_jobs = []
     self.rd_jobs_raw_list = jobs
     self.seed_job_list()
Esempio n. 8
0
 def start(self):  # pragma: no cover
     self.set_running(True)
     self.slack = SlackConnection()
     loop = asyncio.get_event_loop()
     self.plugin_list = self.initialize_plugins()
     self.initialize_static_plugins()
     loop.create_task(self.produce())
     loop.add_signal_handler(signal.SIGINT,
                             functools.partial(self.exit_cleanly))
     try:
         loop.run_forever()
     finally:
         loop.close()
class TestSlackConnectionApiCall(asynctest.TestCase):
    def setUp(self):
        self.channel_list_one = load_fixture("channel_list_one.json")
        self.channel_list_two = load_fixture("channel_list_two.json")
        self.channel_list_three = load_fixture("channel_list_three.json")
        self.channel_list_four = load_fixture("channel_list_four.json")

        from charlesbot.slack.slack_connection import SlackConnection

        self.slack = SlackConnection()
        self.slack.initialized = True
        self.slack.sc = MagicMock()

    def tearDown(self):
        self.slack._drop()

    def test_slack_rtm_api_call_ok(self):
        channel_list = self.channel_list_one.encode("utf8")
        self.slack.sc.api_call.return_value = channel_list
        val = yield from self.slack.api_call("fake_endpoint")
        self.assertEqual(json.loads(val), json.loads(channel_list.decode("utf-8")))

    def test_slack_rtm_api_call_not_ok(self):
        channel_list = self.channel_list_two.encode("utf8")
        self.slack.sc.api_call.return_value = channel_list
        val = yield from self.slack.api_call("fake_endpoint")
        self.assertEqual(json.loads(val), "{}")

    def test_slack_rtm_api_call_not_encoded_utf8(self):
        channel_list = self.channel_list_three
        self.slack.sc.api_call.return_value = channel_list
        with self.assertRaises(AttributeError):
            yield from self.slack.api_call("fake_endpoint")

    def test_slack_rtm_api_call_malformed_json(self):
        channel_list = self.channel_list_four.encode("utf8")
        self.slack.sc.api_call.return_value = channel_list
        with self.assertRaises(ValueError):
            yield from self.slack.api_call("fake_endpoint")
Esempio n. 10
0
class TestSlackUser(asynctest.TestCase):
    def setUp(self):
        patcher1 = patch(
            'charlesbot.slack.slack_connection.SlackConnection.api_call'
        )  # NOQA
        self.addCleanup(patcher1.stop)
        self.mock_api_call = patcher1.start()

        from charlesbot.slack.slack_connection import SlackConnection
        self.slack_connection = SlackConnection()

        from charlesbot.slack.slack_user import SlackUser
        self.su = SlackUser()

    def tearDown(self):
        self.slack_connection._drop()

    @asynctest.ignore_loop
    def test_user_equality(self):
        from charlesbot.slack.slack_user import SlackUser
        user1 = SlackUser(id="SU01", name="userone", color="red")
        user2 = SlackUser(id="SU02", name="usertwo", color="blue")
        self.assertNotEqual(user1, user2)
        user2.id = "SU01"
        self.assertNotEqual(user1, user2)
        user2.name = "userone"
        self.assertNotEqual(user1, user2)
        user2.color = "red"
        self.assertEqual(user1, user2)

    @asynctest.ignore_loop
    def test_user_return_string(self):
        self.su.id = "SU01"
        self.su.name = "User One"
        self.su.deleted = False
        self.su.is_admin = False
        self.su.has_2fa = True
        user_json = json.loads(str(self.su))
        self.assertEqual(user_json.get('id'), "SU01")
        self.assertEqual(user_json.get('name'), "User One")
        self.assertEqual(user_json.get('deleted'), False)
        self.assertEqual(user_json.get('is_admin'), False)
        self.assertEqual(user_json.get('has_2fa'), True)
        self.assertEqual(user_json.get('is_owner'), "")

    def test_empty_slack_response(self):
        self.su.name = "suser"
        self.mock_api_call.side_effect = ["{}"]
        yield from self.su.retrieve_slack_user_info(self.slack_connection,
                                                    "fake123")
        expected_call = call("users.info", user="******")
        self.assertEqual(self.mock_api_call.mock_calls, [expected_call]),
        self.assertEqual(self.su.name, "suser")
        self.assertEqual(self.su.last_name, "")
        self.assertEqual(self.su.is_bot, "")

    def test_no_profile_key(self):
        self.su.name = "suser"
        user_info = {"ok": True, "user": {"id": "U023BECGF", "name": "bobby"}}
        self.mock_api_call.side_effect = [json.dumps(user_info)]
        yield from self.su.retrieve_slack_user_info(self.slack_connection,
                                                    "fake123")
        expected_call = call("users.info", user="******")
        self.assertEqual(self.mock_api_call.mock_calls, [expected_call]),
        self.assertEqual(self.su.name, "bobby")
        self.assertEqual(self.su.id, "U023BECGF")
        self.assertEqual(self.su.last_name, "")
        self.assertEqual(self.su.is_bot, "")

    def test_with_profile_key(self):
        self.su.name = "suser"
        user_info = {
            "ok": True,
            "user": {
                "id": "U023BECGF",
                "name": "bobby",
                "profile": {
                    "real_name": "Bobby Tables",
                    "image_24": "https://www.tables.com",
                }
            }
        }
        self.mock_api_call.side_effect = [json.dumps(user_info)]
        yield from self.su.retrieve_slack_user_info(self.slack_connection,
                                                    "fake123")
        expected_call = call("users.info", user="******")
        self.assertEqual(self.mock_api_call.mock_calls, [expected_call]),
        self.assertEqual(self.su.name, "bobby")
        self.assertEqual(self.su.id, "U023BECGF")
        self.assertEqual(self.su.real_name, "Bobby Tables")
        self.assertEqual(self.su.image_24, "https://www.tables.com")
        self.assertEqual(self.su.is_bot, "")
class RundeckLock(object):

    def __init__(self, token, url, channel, jobs):
        self.log = logging.getLogger(__name__)
        self.rundeck_token = token
        self.rundeck_url = url
        self.topic_channel = channel
        self.topic_channel_id = None
        self.slack = SlackConnection()
        self.locked_by_user = ""
        self.rundeck_jobs = []
        self.rd_jobs_raw_list = jobs
        self.seed_job_list()

    @asyncio.coroutine
    def populate_slack_user_object(self, username):  # pragma: no cover
        slack_user = SlackUser()
        yield from slack_user.retrieve_slack_user_info(self.slack, username)
        return slack_user

    @asyncio.coroutine
    def toggle_rundeck_lock(self, slack_message, lock_job):
        """
        Coordinating function to toggle the Rundeck lock from open to locked,
        or visa versa. This is the user-triggered function.
        """
        slack_user = yield from self.populate_slack_user_object(slack_message.user)  # NOQA

        if not self.is_user_authorized_to_lock(slack_user):
            fail_msg = "Sorry <@%s>, you are not allowed to lock Rundeck executions." % slack_user.name  # NOQA
            self.log.warning(fail_msg)
            yield from self.slack.send_channel_message(slack_message.channel,
                                                       fail_msg)
            return
        self.locked_by_user = slack_user.name

        tasks = []
        for job in self.rundeck_jobs:
            tasks.append(self.lock_or_unlock_rundeck_job(job, lock_job))
        yield from asyncio.gather(*tasks)

        self.log.info("Rundeck jobs locked: %s" % lock_job)
        self.log.info("Job state toggled by @%s" % slack_user.name)

        full_slack_msg = self.get_execution_status_message(lock_job)
        yield from self.set_channel_topic(lock_job)
        yield from self.slack.send_channel_message(slack_message.channel,
                                                   full_slack_msg)
        yield from self.print_lock_status(slack_message)

    @asyncio.coroutine
    def get_topic_channel_id(self):
        if not self.topic_channel:
            return None

        if self.topic_channel_id:
            return self.topic_channel_id

        channel_list = yield from self.slack.api_call('channels.list',
                                                      exclude_archived=1)
        json_list = json.loads(channel_list)
        for channel in json_list['channels']:
            if channel['name'] == self.topic_channel:
                self.topic_channel_id = channel['id']
                return self.topic_channel_id
        return None

    @asyncio.coroutine
    def lock_or_unlock_rundeck_job(self, rundeck_job_obj, lock_job):
        """
        Lock the job associated with this RundeckJob object
        """
        verb = "enable"
        if lock_job:
            verb = "disable"

        url = "%s/execution/%s" % (rundeck_job_obj.href, verb)
        headers = {
            "Accept": "application/json",
            "X-Rundeck-Auth-Token": self.rundeck_token,
        }
        response = yield from http_post_request(url, headers)
        if response:
            rundeck_job_obj.execution_enabled = lock_job

    @asyncio.coroutine
    def trigger_rundeck_executions_allowed_update(self):
        tasks = []
        for job in self.rundeck_jobs:
            tasks.append(self.update_rundeck_job_execution_enabled_status(job))
        yield from asyncio.gather(*tasks)

    @asyncio.coroutine
    def update_rundeck_job_execution_enabled_status(self, rundeck_job_obj):
        """
        Update the execution_enabled flag for this RundeckJob object to reflect
        reality
        """
        url = "%s" % rundeck_job_obj.href
        headers = {
            "Accept": "application/xml",  # As of Rundeck 2.6.2, this endpoint
                                          # does not return json :(
            "X-Rundeck-Auth-Token": self.rundeck_token,
        }
        response = yield from http_get_request(url, headers, {})
        xml_root = etree.fromstring(response)
        execution_enabled = xml_root[0].find("executionEnabled").text
        rundeck_job_obj.execution_enabled = False
        if execution_enabled == "true":
            rundeck_job_obj.execution_enabled = True

    def get_execution_status_message(self, lock_job):
        """
        Return an appropriate user-facing message
        """
        if lock_job:
            return ":lock: Rundeck executions locked by <@%s> :lock:" % self.locked_by_user  # NOQA
        return ":white_check_mark: Rundeck executions unlocked! :white_check_mark:"  # NOQA

    @asyncio.coroutine
    def set_channel_topic(self, lock_job):
        topic_channel_id = yield from self.get_topic_channel_id()
        if not topic_channel_id:
            return
        topic_message = ""
        if lock_job:
            topic_message = ":lock: Rundeck executions locked by @%s :lock:" % self.locked_by_user  # NOQA
        yield from self.slack.api_call('channels.setTopic',
                                       channel=topic_channel_id,
                                       topic=topic_message)

    @asyncio.coroutine
    def print_lock_status(self, slack_message):
        """
        Print the status of the Rundeck lock (whether open or locked)
        """
        yield from self.trigger_rundeck_executions_allowed_update()
        out_message = []
        out_message.append("*Rundeck Job Lock Report*")
        out_message.append("```")
        for job in self.rundeck_jobs:
            if job.execution_enabled:
                out_message.append("%s: unlocked" % job.friendly_name)
            else:
                out_message.append("%s: locked" % job.friendly_name)
        out_message.append("```")
        yield from self.slack.send_channel_message(slack_message.channel,
                                                   "\n".join(out_message))

    def is_user_authorized_to_lock(self, slack_user_obj):
        """
        Returns True or False, depending on whether this user is authorized to
        lock rundeck executions.
        """
        return slack_user_obj.is_admin

    def seed_job_list(self):  # pragma: no cover
        loop = asyncio.get_event_loop()
        loop.create_task(self.load_rundeck_jobs())

    @asyncio.coroutine
    def load_rundeck_jobs(self):
        """
        Read the rd_jobs_raw_list array and load the information about all
        those jobs into the `rundeck_jobs` list.
        """
        for job in self.rd_jobs_raw_list:
            rd_job = RundeckJob(friendly_name=job['friendly_name'])
            job_loaded_successfully = yield from rd_job.retrieve_rundeck_job_info(  # NOQA
                self.rundeck_token,
                self.rundeck_url,
                job['project'],
                job['name']
            )

            if not job_loaded_successfully:
                self.log.warning("Could not retrieve job info for: %s" % job['friendly_name'])  # NOQA
                continue
            self.log.info("Retrieved Rundeck info for job: %s" % job['friendly_name'])  # NOQA
            self.rundeck_jobs.append(rd_job)
Esempio n. 12
0
class TestSlackUser(asynctest.TestCase):

    def setUp(self):
        patcher1 = patch('charlesbot.slack.slack_connection.SlackConnection.api_call')  # NOQA
        self.addCleanup(patcher1.stop)
        self.mock_api_call = patcher1.start()

        from charlesbot.slack.slack_connection import SlackConnection
        self.slack_connection = SlackConnection()

        from charlesbot.slack.slack_user import SlackUser
        self.su = SlackUser()

    def tearDown(self):
        self.slack_connection._drop()

    @asynctest.ignore_loop
    def test_user_equality(self):
        from charlesbot.slack.slack_user import SlackUser
        user1 = SlackUser(id="SU01",
                          name="userone",
                          color="red")
        user2 = SlackUser(id="SU02",
                          name="usertwo",
                          color="blue")
        self.assertNotEqual(user1, user2)
        user2.id = "SU01"
        self.assertNotEqual(user1, user2)
        user2.name = "userone"
        self.assertNotEqual(user1, user2)
        user2.color = "red"
        self.assertEqual(user1, user2)

    @asynctest.ignore_loop
    def test_user_return_string(self):
        self.su.id = "SU01"
        self.su.name = "User One"
        self.su.deleted = False
        self.su.is_admin = False
        self.su.has_2fa = True
        user_json = json.loads(str(self.su))
        self.assertEqual(user_json.get('id'), "SU01")
        self.assertEqual(user_json.get('name'), "User One")
        self.assertEqual(user_json.get('deleted'), False)
        self.assertEqual(user_json.get('is_admin'), False)
        self.assertEqual(user_json.get('has_2fa'), True)
        self.assertEqual(user_json.get('is_owner'), "")

    def test_empty_slack_response(self):
        self.su.name = "suser"
        self.mock_api_call.side_effect = ["{}"]
        yield from self.su.retrieve_slack_user_info(self.slack_connection,
                                                    "fake123")
        expected_call = call("users.info", user="******")
        self.assertEqual(self.mock_api_call.mock_calls,
                         [expected_call]),
        self.assertEqual(self.su.name, "suser")
        self.assertEqual(self.su.last_name, "")
        self.assertEqual(self.su.is_bot, "")

    def test_no_profile_key(self):
        self.su.name = "suser"
        user_info = {
            "ok": True,
            "user": {
                "id": "U023BECGF",
                "name": "bobby"
            }
        }
        self.mock_api_call.side_effect = [json.dumps(user_info)]
        yield from self.su.retrieve_slack_user_info(self.slack_connection,
                                                    "fake123")
        expected_call = call("users.info", user="******")
        self.assertEqual(self.mock_api_call.mock_calls,
                         [expected_call]),
        self.assertEqual(self.su.name, "bobby")
        self.assertEqual(self.su.id, "U023BECGF")
        self.assertEqual(self.su.last_name, "")
        self.assertEqual(self.su.is_bot, "")

    def test_with_profile_key(self):
        self.su.name = "suser"
        user_info = {
            "ok": True,
            "user": {
                "id": "U023BECGF",
                "name": "bobby",
                "profile": {
                    "real_name": "Bobby Tables",
                    "image_24": "https://www.tables.com",
                }
            }
        }
        self.mock_api_call.side_effect = [json.dumps(user_info)]
        yield from self.su.retrieve_slack_user_info(self.slack_connection,
                                                    "fake123")
        expected_call = call("users.info", user="******")
        self.assertEqual(self.mock_api_call.mock_calls,
                         [expected_call]),
        self.assertEqual(self.su.name, "bobby")
        self.assertEqual(self.su.id, "U023BECGF")
        self.assertEqual(self.su.real_name, "Bobby Tables")
        self.assertEqual(self.su.image_24, "https://www.tables.com")
        self.assertEqual(self.su.is_bot, "")