Skip to content

Commit

Permalink
Add kentik_webhook.py
Browse files Browse the repository at this point in the history
  • Loading branch information
jryburn committed Feb 7, 2024
1 parent e1a2cf2 commit e1df206
Show file tree
Hide file tree
Showing 9 changed files with 184 additions and 58 deletions.
3 changes: 3 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"ansible.python.interpreterPath": "/usr/local/bin/python3"
}
Empty file removed MAINTAINERS
Empty file.
3 changes: 0 additions & 3 deletions REVIEW_CHECKLIST.md

This file was deleted.

35 changes: 17 additions & 18 deletions docs/docsite/links.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@
# Remove this section if the collection repository is not on GitHub, or if you do not want this
# functionality for your collection.
edit_on_github:
repository: ansible-collections/community.REPO_NAME
repository: kentik/ansible_eda
branch: main
# If your collection root (the directory containing galaxy.yml) does not coincide with your
# repository's root, you have to specify the path to the collection root here. For example,
# if the collection root is in a subdirectory ansible_collections/community/REPO_NAME
# in your repository, you have to set path_prefix to 'ansible_collections/community/REPO_NAME'.
path_prefix: ''
path_prefix: ""

# Here you can add arbitrary extra links. Please keep the number of links down to a
# minimum! Also please keep the description short, since this will be the text put on
Expand All @@ -25,21 +25,20 @@ edit_on_github:

extra_links:
- description: Report an issue
url: https://github.com/ansible-collections/community.REPO_NAME/issues/new/choose

url: https://github.com/kentik/ansible_eda/issues/new/choose
# Specify communication channels for your collection. We suggest to not specify more
# than one place for communication per communication tool to avoid confusion.
communication:
matrix_rooms:
- topic: General usage and support questions
room: '#users:ansible.im'
irc_channels:
- topic: General usage and support questions
network: Libera
channel: '#ansible'
mailing_lists:
- topic: Ansible Project List
url: https://groups.google.com/g/ansible-project
# You can also add a `subscribe` field with an URI that allows to subscribe
# to the mailing list. For lists on https://groups.google.com/ a subscribe link is
# automatically generated.
# communication:
# matrix_rooms:
# - topic: General usage and support questions
# room: '#users:ansible.im'
# irc_channels:
# - topic: General usage and support questions
# network: Libera
# channel: '#ansible'
# mailing_lists:
# - topic: Ansible Project List
# url: https://groups.google.com/g/ansible-project
# You can also add a `subscribe` field with an URI that allows to subscribe
# to the mailing list. For lists on https://groups.google.com/ a subscribe link is
# automatically generated.
10 changes: 6 additions & 4 deletions galaxy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,12 @@ name: ansible_eda
version: 1.0.0
readme: README.md
authors:
- Justin Ryburn <[email protected]>
- Ian Pye <[email protected]>
- Kentik <[email protected]>
description: Ansible Collection for collect Kentik alering notification JSON webhooks for Event Driven Ansible (EDA)
license:
- GPL-2.0-or-later
license_file: "LICENSE"
tags: [networking]
tags: [kentik, eda, networking]

# Collections that this collection requires to be installed for it to be usable. The key of the dict is the
# collection label 'namespace.name'. The value is a version range
Expand All @@ -22,11 +21,14 @@ dependencies: {}
repository: https://github.com/kentik/ansible_eda

# The URL to any online docs
documentation: http://kb.kentik.com
documentation: https://github.com/kentik/ansible_eda/blob/main/README.md

# The URL to the homepage of the collection/project
homepage: https://kentik.com

# The URL to the collection issue tracker
issues: https://github.com/kentik/ansible_eda/issues

# A list of file glob-like patterns used to filter any files or directories that should not be included in the build
# artifact. A pattern is matched from the relative path of the file or directory of the collection directory. This
# uses 'fnmatch' to match the files or directories. Some directories and files like 'galaxy.yml', '*.pyc', '*.retry',
Expand Down
6 changes: 6 additions & 0 deletions meta/ee-requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,9 @@
# See https://ansible.readthedocs.io/projects/builder/en/latest/collection_metadata/#how-to-verify-collection-level-metadata
# to learn how to test your configuration.
# Do not delete this file even if the collection has no dependencies!
asyncio
aiohttp
collections
json
logging
typing
3 changes: 1 addition & 2 deletions meta/runtime.yml
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
---
# Collections must specify a minimum required ansible version to upload
# to galaxy
# requires_ansible: '>=2.9.10'

requires_ansible: ">=2.9.10"
# Content that Ansible needs to load from another location or that has
# been deprecated/removed
# plugin_routing:
Expand Down
31 changes: 0 additions & 31 deletions plugins/README.md

This file was deleted.

151 changes: 151 additions & 0 deletions plugins/event_source/kentik_webhook.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
"""kentik_webhook.py.
Description:
This is an event source plugin for receiving events via a webhook
from the webhook notifications action of Kentik's Alerting engine.
The message must be a valid JSON object.
Arguments:
---------
host: The hostname to listen to. Defaults to 0.0.0.0 (all interfaces)
port: The TCP port to listen to. Defaults to 5000
"""
import asyncio
import json
import logging
from collections.abc import Callable
from typing import Any

from aiohttp import web

logger = logging.getLogger(__name__)
routes = web.RouteTableDef()

# Initialize the loggger configuration
def _initialize_logger_config() -> None:
logging.basicConfig(
format="[%(asctime)s] - %(pathname)s: %(message)s",
level=logging.INFO,
datefmt="%Y-%m-%d %I:%M:%S",
)

# Process incoming webhook alert notifications from Kentik
@routes.post("/alert")
async def webhook(request: web.Request) -> web.Response:
"""Process incoming webhook alert notifications from Kentik
Parameters
----------
request : web.Request
Received request
Returns
-------
Response with empty JSON object
Raises
------
HTTPBadRequest
If the payload can't be parsed as JSON
"""
logger.info("Received alert")
try:
payload = await request.json()
except json.JSONDecodeError as exc:
logger.warning("Wrong body request: failed to decode JSON payload: %s", exc)
raise web.HTTPBadRequest(text="Invalid JSON payload") from None
headers = dict(request.headers)
headers.pop("Authorization", None)
data = {
"payload": payload,
"meta": {"headers": headers},
}
logger.info("Put alert on queue")
await request.app["queue"].put(data)
return web.json_response({})

# Set app_attr settings in a dictionary
def set_app_attr(args: dict[str, Any]) -> dict[str, Any]:
"""Set app_attr settings in a dictionary
Parameters
----------
args : Dict[str,Any]
Empty dictionary of arguments
Returns
-------
args : Dict[str,Any]
Args containing the host and port
"""
if "host" not in args:
host="0.0.0.0"
if "port" not in args:
port=5000
app_attrs = {}
app_attrs["host"] = args.get("host")
app_attrs["port"] = args.get("port")

return app_attrs

# Entrypoint from ansible-rulebook
async def main(queue: asyncio.Queue, args: dict[str, Any]) -> None:
"""Receive events via webhook.
Parameters
----------
queue : asyncio.Queue
Problem queue
args : Dict[str,Any])
Args containing the host and port
"""
_initialize_logger_config()
logging.info("Starting kentik_webhook...")

app_attrs = set_app_attr(args)
app = web.Application()
app.add_routes(routes)

# Store queue to access it in the event handler function
app["queue"] = queue

runner = web.AppRunner(app)
await runner.setup()
site = web.TCPSite(
runner,
app_attrs["host"],
app_attrs["port"],
)
await site.start()
logger.info("kentik_webhook is running and waiting for alerts")

try:
await asyncio.Future()
except asyncio.CancelledError:
logger.info("Webhook Plugin Task Cancelled")
finally:
await runner.cleanup()

if __name__ == "__main__":
"""MockQueue if running directly."""

class MockQueue:
"""A fake queue."""

async def put(self: "MockQueue", event: dict) -> None:
"""Print the event."""
print(event)

asyncio.run(
main(
MockQueue(),
{
"host": "localhost",
"port": 80,
},
),
)

0 comments on commit e1df206

Please sign in to comment.