From d36922064b517ae51020ef6fd902886d2f93a547 Mon Sep 17 00:00:00 2001
From: Abhijeet Kasurde <akasurde@redhat.com>
Date: Wed, 12 Dec 2018 14:11:49 +0530
Subject: [PATCH] Add support for hex color in slack module (#49804)

Fixes: #11935

Signed-off-by: Abhijeet Kasurde <akasurde@redhat.com>
---
 .../11935-slack-add_hex_color_values.yaml     |  2 +
 lib/ansible/modules/notification/slack.py     | 31 ++++++++++++----
 test/units/modules/notification/test_slack.py | 37 ++++++++++++++++++-
 3 files changed, 62 insertions(+), 8 deletions(-)
 create mode 100644 changelogs/fragments/11935-slack-add_hex_color_values.yaml

diff --git a/changelogs/fragments/11935-slack-add_hex_color_values.yaml b/changelogs/fragments/11935-slack-add_hex_color_values.yaml
new file mode 100644
index 00000000000..859c8181488
--- /dev/null
+++ b/changelogs/fragments/11935-slack-add_hex_color_values.yaml
@@ -0,0 +1,2 @@
+minor_changes:
+    - Add support for hex color values in Slack module.
diff --git a/lib/ansible/modules/notification/slack.py b/lib/ansible/modules/notification/slack.py
index 7bf4026540e..bc69c3b35a0 100644
--- a/lib/ansible/modules/notification/slack.py
+++ b/lib/ansible/modules/notification/slack.py
@@ -92,13 +92,10 @@ options:
   color:
     version_added: "2.0"
     description:
-      - Allow text to use default colors - use the default of 'normal' to not send a custom color bar at the start of the message
+      - Allow text to use default colors - use the default of 'normal' to not send a custom color bar at the start of the message.
+      - Allowed values for color can be one of 'normal', 'good', 'warning', 'danger', any valid 3 digit or 6 digit hex color value.
+      - Specifying value in hex is supported from version 2.8.
     default: 'normal'
-    choices:
-      - 'normal'
-      - 'good'
-      - 'warning'
-      - 'danger'
   attachments:
     description:
       - Define a list of attachments. This list mirrors the Slack JSON API.
@@ -132,6 +129,14 @@ EXAMPLES = """
     username: ''
     icon_url: ''
 
+- name: insert a color bar in front of the message with valid hex color value
+  slack:
+    token: thetoken/generatedby/slack
+    msg: 'This message uses color in hex value'
+    color: '#00aacc'
+    username: ''
+    icon_url: ''
+
 - name: Use the attachments API
   slack:
     token: thetoken/generatedby/slack
@@ -158,6 +163,7 @@ EXAMPLES = """
     msg: This message has &lt;brackets&gt; &amp; ampersands in plain text.
 """
 
+import re
 from ansible.module_utils.basic import AnsibleModule
 from ansible.module_utils.urls import fetch_url
 
@@ -173,6 +179,12 @@ escape_table = {
 }
 
 
+def is_valid_hex_color(color_choice):
+    if re.match(r'^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$', color_choice):
+        return True
+    return False
+
+
 def escape_quotes(text):
     '''Backslash any quotes within text.'''
     return "".join(escape_table.get(c, c) for c in text)
@@ -265,7 +277,7 @@ def main():
             link_names=dict(type='int', default=1, choices=[0, 1]),
             parse=dict(type='str', default=None, choices=['none', 'full']),
             validate_certs=dict(default='yes', type='bool'),
-            color=dict(type='str', default='normal', choices=['normal', 'good', 'warning', 'danger']),
+            color=dict(type='str', default='normal'),
             attachments=dict(type='list', required=False, default=None)
         )
     )
@@ -283,6 +295,11 @@ def main():
     color = module.params['color']
     attachments = module.params['attachments']
 
+    color_choices = ['normal', 'good', 'warning', 'danger']
+    if color not in color_choices and not is_valid_hex_color(color):
+        module.fail_json(msg="Color value specified should be either one of %r "
+                             "or any valid hex value with length 3 or 6." % color_choices)
+
     payload = build_payload_for_slack(module, text, channel, thread_id, username, icon_url, icon_emoji, link_names,
                                       parse, color, attachments)
     do_notify_slack(module, domain, token, payload)
diff --git a/test/units/modules/notification/test_slack.py b/test/units/modules/notification/test_slack.py
index 5b325cbeb59..dc2e3eea024 100644
--- a/test/units/modules/notification/test_slack.py
+++ b/test/units/modules/notification/test_slack.py
@@ -3,7 +3,6 @@ import pytest
 from units.compat.mock import patch
 from ansible.modules.notification import slack
 from units.modules.utils import AnsibleExitJson, AnsibleFailJson, ModuleTestCase, set_module_args
-from ansible import module_utils
 
 
 class TestSlackModule(ModuleTestCase):
@@ -83,3 +82,39 @@ class TestSlackModule(ModuleTestCase):
             assert call_data['text'] == "test"
             assert call_data['thread_ts'] == 100.00
             assert fetch_url_mock.call_args[1]['url'] == "https://hooks.slack.com/services/XXXX/YYYY/ZZZZ"
+
+    def test_message_with_invalid_color(self):
+        """tests sending invalid color value to module"""
+        set_module_args({
+            'token': 'XXXX/YYYY/ZZZZ',
+            'msg': 'test',
+            'color': 'aa',
+        })
+        with self.assertRaises(AnsibleFailJson) as exec_info:
+            self.module.main()
+
+        msg = "Color value specified should be either one of" \
+              " ['normal', 'good', 'warning', 'danger'] or any valid" \
+              " hex value with length 3 or 6."
+        assert exec_info.exception.args[0]['msg'] == msg
+
+
+color_test = [
+    ('#111111', True),
+    ('#00aabb', True),
+    ('#abc', True),
+    ('#gghhjj', False),
+    ('#ghj', False),
+    ('#a', False),
+    ('#aaaaaaaa', False),
+    ('', False),
+    ('aaaa', False),
+    ('$00aabb', False),
+    ('$00a', False),
+]
+
+
+@pytest.mark.parametrize("color_value, ret_status", color_test)
+def test_is_valid_hex_color(color_value, ret_status):
+    generated_value = slack.is_valid_hex_color(color_value)
+    assert generated_value == ret_status