mirror of
https://github.com/ansible-collections/community.general.git
synced 2025-06-28 03:00:23 -07:00
slack: support slack-gov.com (#10270)
Some checks are pending
EOL CI / EOL Sanity (Ⓐ2.16) (push) Waiting to run
EOL CI / EOL Units (Ⓐ2.16+py2.7) (push) Waiting to run
EOL CI / EOL Units (Ⓐ2.16+py3.11) (push) Waiting to run
EOL CI / EOL Units (Ⓐ2.16+py3.6) (push) Waiting to run
EOL CI / EOL I (Ⓐ2.16+alpine3+py:azp/posix/1/) (push) Waiting to run
EOL CI / EOL I (Ⓐ2.16+alpine3+py:azp/posix/2/) (push) Waiting to run
EOL CI / EOL I (Ⓐ2.16+alpine3+py:azp/posix/3/) (push) Waiting to run
EOL CI / EOL I (Ⓐ2.16+fedora38+py:azp/posix/1/) (push) Waiting to run
EOL CI / EOL I (Ⓐ2.16+fedora38+py:azp/posix/2/) (push) Waiting to run
EOL CI / EOL I (Ⓐ2.16+fedora38+py:azp/posix/3/) (push) Waiting to run
EOL CI / EOL I (Ⓐ2.16+opensuse15+py:azp/posix/1/) (push) Waiting to run
EOL CI / EOL I (Ⓐ2.16+opensuse15+py:azp/posix/2/) (push) Waiting to run
EOL CI / EOL I (Ⓐ2.16+opensuse15+py:azp/posix/3/) (push) Waiting to run
nox / Run extra sanity tests (push) Waiting to run
Some checks are pending
EOL CI / EOL Sanity (Ⓐ2.16) (push) Waiting to run
EOL CI / EOL Units (Ⓐ2.16+py2.7) (push) Waiting to run
EOL CI / EOL Units (Ⓐ2.16+py3.11) (push) Waiting to run
EOL CI / EOL Units (Ⓐ2.16+py3.6) (push) Waiting to run
EOL CI / EOL I (Ⓐ2.16+alpine3+py:azp/posix/1/) (push) Waiting to run
EOL CI / EOL I (Ⓐ2.16+alpine3+py:azp/posix/2/) (push) Waiting to run
EOL CI / EOL I (Ⓐ2.16+alpine3+py:azp/posix/3/) (push) Waiting to run
EOL CI / EOL I (Ⓐ2.16+fedora38+py:azp/posix/1/) (push) Waiting to run
EOL CI / EOL I (Ⓐ2.16+fedora38+py:azp/posix/2/) (push) Waiting to run
EOL CI / EOL I (Ⓐ2.16+fedora38+py:azp/posix/3/) (push) Waiting to run
EOL CI / EOL I (Ⓐ2.16+opensuse15+py:azp/posix/1/) (push) Waiting to run
EOL CI / EOL I (Ⓐ2.16+opensuse15+py:azp/posix/2/) (push) Waiting to run
EOL CI / EOL I (Ⓐ2.16+opensuse15+py:azp/posix/3/) (push) Waiting to run
nox / Run extra sanity tests (push) Waiting to run
* slack: support slack-gov.com Allow the slack module to work with GovSlack, hosted at https://slack-gov.com/ This re-uses the existing `domain` option so that users can set it to `slack-gov.com` to use GovSlack. To maintain backwards compatibility, any setting of `domain` for WebAPI tokens that is not `slack.com` or `slack-gov.com` is ignored. * fixup * cleanup * fix pep8 * clean up docs and better function name * document default value * try to fix yaml, not sure what is wrong * Update plugins/modules/slack.py Co-authored-by: Felix Fontein <felix@fontein.de> * Update plugins/modules/slack.py Co-authored-by: Felix Fontein <felix@fontein.de> * Update plugins/modules/slack.py Co-authored-by: Felix Fontein <felix@fontein.de> --------- Co-authored-by: Felix Fontein <felix@fontein.de>
This commit is contained in:
parent
dd53a2cee0
commit
1ed0f329bc
2 changed files with 40 additions and 15 deletions
|
@ -32,8 +32,9 @@ options:
|
||||||
domain:
|
domain:
|
||||||
type: str
|
type: str
|
||||||
description:
|
description:
|
||||||
- Slack (sub)domain for your environment without protocol. (For example V(example.slack.com).) In Ansible 1.8 and beyond,
|
- "When using new format 'Webhook token' and WebAPI tokens: this can be V(slack.com) or V(slack-gov.com) and is ignored otherwise."
|
||||||
this is deprecated and may be ignored. See token documentation for information.
|
- "When using old format 'Webhook token': Slack (sub)domain for your environment without protocol. (For example V(example.slack.com).)
|
||||||
|
in Ansible 1.8 and beyond, this is deprecated and may be ignored. See token documentation for information."
|
||||||
token:
|
token:
|
||||||
type: str
|
type: str
|
||||||
description:
|
description:
|
||||||
|
@ -41,9 +42,10 @@ options:
|
||||||
depending on what method you use.
|
depending on what method you use.
|
||||||
- 'Webhook token: Prior to Ansible 1.8, a token looked like V(3Ffe373sfhRE6y42Fg3rvf4GlK). In Ansible 1.8 and above,
|
- 'Webhook token: Prior to Ansible 1.8, a token looked like V(3Ffe373sfhRE6y42Fg3rvf4GlK). In Ansible 1.8 and above,
|
||||||
Ansible adapts to the new slack API where tokens look like V(G922VJP24/D921DW937/3Ffe373sfhRE6y42Fg3rvf4GlK). If tokens
|
Ansible adapts to the new slack API where tokens look like V(G922VJP24/D921DW937/3Ffe373sfhRE6y42Fg3rvf4GlK). If tokens
|
||||||
are in the new format then slack will ignore any value of domain. If the token is in the old format the domain is
|
are in the new format then slack will ignore any value of domain except V(slack.com) or V(slack-gov.com). If the token
|
||||||
required. Ansible has no control of when slack will get rid of the old API. When slack does that the old format will
|
is in the old format the domain is required. Ansible has no control of when slack will get rid of the old API. When slack
|
||||||
stop working. ** Please keep in mind the tokens are not the API tokens but are the webhook tokens. In slack these
|
does that the old format will stop working.
|
||||||
|
** Please keep in mind the tokens are not the API tokens but are the webhook tokens. In slack these
|
||||||
are found in the webhook URL which are obtained under the apps and integrations. The incoming webhooks can be added
|
are found in the webhook URL which are obtained under the apps and integrations. The incoming webhooks can be added
|
||||||
in that area. In some cases this may be locked by your Slack admin and you must request access. It is there that the
|
in that area. In some cases this may be locked by your Slack admin and you must request access. It is there that the
|
||||||
incoming webhooks can be added. The key is on the end of the URL given to you in that section.'
|
incoming webhooks can be added. The key is on the end of the URL given to you in that section.'
|
||||||
|
@ -267,10 +269,10 @@ from ansible.module_utils.six.moves.urllib.parse import urlencode
|
||||||
from ansible.module_utils.urls import fetch_url
|
from ansible.module_utils.urls import fetch_url
|
||||||
|
|
||||||
OLD_SLACK_INCOMING_WEBHOOK = 'https://%s/services/hooks/incoming-webhook?token=%s'
|
OLD_SLACK_INCOMING_WEBHOOK = 'https://%s/services/hooks/incoming-webhook?token=%s'
|
||||||
SLACK_INCOMING_WEBHOOK = 'https://hooks.slack.com/services/%s'
|
SLACK_INCOMING_WEBHOOK = 'https://hooks.%s/services/%s'
|
||||||
SLACK_POSTMESSAGE_WEBAPI = 'https://slack.com/api/chat.postMessage'
|
SLACK_POSTMESSAGE_WEBAPI = 'https://%s/api/chat.postMessage'
|
||||||
SLACK_UPDATEMESSAGE_WEBAPI = 'https://slack.com/api/chat.update'
|
SLACK_UPDATEMESSAGE_WEBAPI = 'https://%s/api/chat.update'
|
||||||
SLACK_CONVERSATIONS_HISTORY_WEBAPI = 'https://slack.com/api/conversations.history'
|
SLACK_CONVERSATIONS_HISTORY_WEBAPI = 'https://%s/api/conversations.history'
|
||||||
|
|
||||||
# Escaping quotes and apostrophes to avoid ending string prematurely in ansible call.
|
# Escaping quotes and apostrophes to avoid ending string prematurely in ansible call.
|
||||||
# We do not escape other characters used as Slack metacharacters (e.g. &, <, >).
|
# We do not escape other characters used as Slack metacharacters (e.g. &, <, >).
|
||||||
|
@ -372,7 +374,11 @@ def build_payload_for_slack(text, channel, thread_id, username, icon_url, icon_e
|
||||||
return payload
|
return payload
|
||||||
|
|
||||||
|
|
||||||
def get_slack_message(module, token, channel, ts):
|
def validate_slack_domain(domain):
|
||||||
|
return (domain if domain in ('slack.com', 'slack-gov.com') else 'slack.com')
|
||||||
|
|
||||||
|
|
||||||
|
def get_slack_message(module, domain, token, channel, ts):
|
||||||
headers = {
|
headers = {
|
||||||
'Content-Type': 'application/json; charset=UTF-8',
|
'Content-Type': 'application/json; charset=UTF-8',
|
||||||
'Accept': 'application/json',
|
'Accept': 'application/json',
|
||||||
|
@ -384,7 +390,8 @@ def get_slack_message(module, token, channel, ts):
|
||||||
'limit': 1,
|
'limit': 1,
|
||||||
'inclusive': 'true',
|
'inclusive': 'true',
|
||||||
})
|
})
|
||||||
url = SLACK_CONVERSATIONS_HISTORY_WEBAPI + '?' + qs
|
domain = validate_slack_domain(domain)
|
||||||
|
url = (SLACK_CONVERSATIONS_HISTORY_WEBAPI % domain) + '?' + qs
|
||||||
response, info = fetch_url(module=module, url=url, headers=headers, method='GET')
|
response, info = fetch_url(module=module, url=url, headers=headers, method='GET')
|
||||||
if info['status'] != 200:
|
if info['status'] != 200:
|
||||||
module.fail_json(msg="failed to get slack message")
|
module.fail_json(msg="failed to get slack message")
|
||||||
|
@ -402,9 +409,11 @@ def do_notify_slack(module, domain, token, payload):
|
||||||
use_webapi = False
|
use_webapi = False
|
||||||
if token.count('/') >= 2:
|
if token.count('/') >= 2:
|
||||||
# New style webhook token
|
# New style webhook token
|
||||||
slack_uri = SLACK_INCOMING_WEBHOOK % token
|
domain = validate_slack_domain(domain)
|
||||||
|
slack_uri = SLACK_INCOMING_WEBHOOK % (domain, token)
|
||||||
elif re.match(r'^xox[abp]-\S+$', token):
|
elif re.match(r'^xox[abp]-\S+$', token):
|
||||||
slack_uri = SLACK_UPDATEMESSAGE_WEBAPI if 'ts' in payload else SLACK_POSTMESSAGE_WEBAPI
|
domain = validate_slack_domain(domain)
|
||||||
|
slack_uri = (SLACK_UPDATEMESSAGE_WEBAPI if 'ts' in payload else SLACK_POSTMESSAGE_WEBAPI) % domain
|
||||||
use_webapi = True
|
use_webapi = True
|
||||||
else:
|
else:
|
||||||
if not domain:
|
if not domain:
|
||||||
|
@ -426,7 +435,7 @@ def do_notify_slack(module, domain, token, payload):
|
||||||
if use_webapi:
|
if use_webapi:
|
||||||
obscured_incoming_webhook = slack_uri
|
obscured_incoming_webhook = slack_uri
|
||||||
else:
|
else:
|
||||||
obscured_incoming_webhook = SLACK_INCOMING_WEBHOOK % '[obscured]'
|
obscured_incoming_webhook = SLACK_INCOMING_WEBHOOK % (domain, '[obscured]')
|
||||||
module.fail_json(msg=" failed to send %s to %s: %s" % (data, obscured_incoming_webhook, info['msg']))
|
module.fail_json(msg=" failed to send %s to %s: %s" % (data, obscured_incoming_webhook, info['msg']))
|
||||||
|
|
||||||
# each API requires different handling
|
# each API requires different handling
|
||||||
|
@ -494,7 +503,7 @@ def main():
|
||||||
# if updating an existing message, we can check if there's anything to update
|
# if updating an existing message, we can check if there's anything to update
|
||||||
if message_id is not None:
|
if message_id is not None:
|
||||||
changed = False
|
changed = False
|
||||||
msg = get_slack_message(module, token, channel, message_id)
|
msg = get_slack_message(module, domain, token, channel, message_id)
|
||||||
for key in ('icon_url', 'icon_emoji', 'link_names', 'color', 'attachments', 'blocks'):
|
for key in ('icon_url', 'icon_emoji', 'link_names', 'color', 'attachments', 'blocks'):
|
||||||
if msg.get(key) != module.params.get(key):
|
if msg.get(key) != module.params.get(key):
|
||||||
changed = True
|
changed = True
|
||||||
|
|
|
@ -103,6 +103,22 @@ class TestSlackModule(ModuleTestCase):
|
||||||
self.assertTrue(fetch_url_mock.call_count, 1)
|
self.assertTrue(fetch_url_mock.call_count, 1)
|
||||||
self.assertEqual(fetch_url_mock.call_args[1]['url'], "https://slack.com/api/chat.postMessage")
|
self.assertEqual(fetch_url_mock.call_args[1]['url'], "https://slack.com/api/chat.postMessage")
|
||||||
|
|
||||||
|
def test_govslack_message(self):
|
||||||
|
with set_module_args({
|
||||||
|
'token': 'xoxa-123456789abcdef',
|
||||||
|
'domain': 'slack-gov.com',
|
||||||
|
'msg': 'test with ts'
|
||||||
|
}):
|
||||||
|
with patch.object(slack, "fetch_url") as fetch_url_mock:
|
||||||
|
mock_response = Mock()
|
||||||
|
mock_response.read.return_value = '{"fake":"data"}'
|
||||||
|
fetch_url_mock.return_value = (mock_response, {"status": 200})
|
||||||
|
with self.assertRaises(AnsibleExitJson):
|
||||||
|
self.module.main()
|
||||||
|
|
||||||
|
self.assertTrue(fetch_url_mock.call_count, 1)
|
||||||
|
self.assertEqual(fetch_url_mock.call_args[1]['url'], "https://slack-gov.com/api/chat.postMessage")
|
||||||
|
|
||||||
def test_edit_message(self):
|
def test_edit_message(self):
|
||||||
with set_module_args({
|
with set_module_args({
|
||||||
'token': 'xoxa-123456789abcdef',
|
'token': 'xoxa-123456789abcdef',
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue