-
Notifications
You must be signed in to change notification settings - Fork 263
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* chore: check additional deps and resolve issues * refactor: make channel_override generally usable * feat: add new zulip sink/integration * docs: add zulip sink setup * docs: update steps to add a bot * docs: update * fix: correctly export channel transformer * test: refactor channel_transformer tests * chore: rm old slack tests targeted at the channel_transformer * fix: update channel_transformer imports * chore: add missing comments * fix: dont validate channel_override on each template call, rather do it once on sinkparams creation. Also add tets.... * perf: only do work if the PREF is actually set --------- Co-authored-by: Oscar Guertler <[email protected]>
- Loading branch information
Showing
19 changed files
with
646 additions
and
323 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
Zulip | ||
###### | ||
|
||
Robusta can report issues and events in your Kubernetes cluster to Zulip. | ||
|
||
.. image:: /images/zulip_example.png | ||
:width: 1000 | ||
:align: center | ||
|
||
To configure the Zulip sink you will need a *bot email*, an *api token* and your *api/zulip url* | ||
|
||
Creating a bot account | ||
----------------------- | ||
|
||
1. Open Zulip | ||
2. Click on the gear icon in the upper right corner | ||
3. Select **Personal** or **Organization** settings | ||
4. On the left, click **Bots** | ||
5. Click **Add a new bot** | ||
6. Fill out the fields, and click **Add** | ||
4. Copy email and token | ||
|
||
Settings | ||
------------------ | ||
|
||
* ``api_url`` : The url of your Zulip instance | ||
* ``bot_email`` : The email of the bot account | ||
* ``bot_api_key`` : The api key of your bot account | ||
* ``stream_name`` : Name of the channel to send the message to | ||
* ``topic_name`` : Name of the topic of the stream to send messages to | ||
* ``topic_override`` : Dynamic topic override, same as the channel_override in the slack sink | ||
* ``log_preview_char_limit`` : [Optional - default: ``500``] The amount of log characters to append to the alert message (zulip doesnt have a builtin text file preview). If set to ``0`` a text file will be sent | ||
|
||
Configuring the Zulip sink | ||
--------------------------- | ||
|
||
.. admonition:: Add this to your generated_values.yaml | ||
|
||
.. code-block:: yaml | ||
sinksConfig: | ||
- zulip_sink: | ||
name: my_zulip_sink | ||
api_url: https://my-zulip-instance.com | ||
bot_email: [email protected] | ||
bot_api_key: very_secret_key | ||
stream_name: Monitoring | ||
topic_name: Robusta | ||
Save the file and run | ||
|
||
.. code-block:: bash | ||
:name: cb-add-zulip-sink | ||
helm upgrade robusta robusta/robusta -f generated_values.yaml | ||
You should now get alerts in Zulip! |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
from robusta.core.sinks.common.channel_transformer import ChannelTransformer |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,106 @@ | ||
from collections import defaultdict | ||
from string import Template | ||
from typing import Dict, Optional, Union | ||
|
||
import regex | ||
|
||
CLUSTER_PREF = "cluster_name" | ||
CLUSTER_PREF_PATTERN = regex.compile("(\$?\{?" + CLUSTER_PREF + "\}?)") # noqa: W605 | ||
LABELS_PREF = "labels." | ||
ESCAPED_LABEL_PREF = regex.escape(LABELS_PREF) | ||
LABEL_PREF_PATTERN = regex.compile("\$?" + ESCAPED_LABEL_PREF + "[\w.]+") # noqa: W605 | ||
ANNOTATIONS_PREF = "annotations." | ||
ESCAPED_ANNOTATIONS_PREF = regex.escape(ANNOTATIONS_PREF) | ||
ANNOTATIONS_PREF_PATTERN = regex.compile("\$?" + ESCAPED_ANNOTATIONS_PREF + "[\w.]+") # noqa: W605 | ||
BRACKETS_PATTERN = regex.compile(r"\$\{[^\}]+\}") | ||
COMPOSITE_PATTERN = r".*\$({?labels.[^$]+|{?annotations.[^$]+|{?cluster_name).*" | ||
ONLY_VALUE_PATTERN = r"^(labels.[^$]+|annotations.[^$]+|cluster_name)$" | ||
MISSING = "<missing>" | ||
|
||
|
||
class ChannelTransformer: | ||
@classmethod | ||
def validate_channel_override(cls, v: Union[str, None]): | ||
if v: | ||
if regex.match(ONLY_VALUE_PATTERN, v): | ||
return "$" + v | ||
if not regex.match(COMPOSITE_PATTERN, v): | ||
err_msg = ( | ||
f"channel_override must be '{CLUSTER_PREF}' or '{LABELS_PREF}foo' or '{ANNOTATIONS_PREF}foo' " | ||
f"or contain patters like: '${CLUSTER_PREF}'/'${LABELS_PREF}foo'/" | ||
f"'${ANNOTATIONS_PREF}foo'" | ||
) | ||
raise ValueError(err_msg) | ||
return v | ||
|
||
@classmethod | ||
def normalize_key_string(cls, s: str) -> str: | ||
return s.replace("/", "_").replace(".", "_").replace("-", "_") | ||
|
||
@classmethod | ||
def normalize_dict_keys(cls, metadata: Dict) -> Dict: | ||
result = defaultdict(lambda: MISSING) | ||
result.update({cls.normalize_key_string(k): v for k, v in metadata.items()}) | ||
return result | ||
|
||
# if prefix not present, return "" | ||
# else, if found, return replacement else return MISSING | ||
@classmethod | ||
def get_replacement(cls, prefix: str, value: str, normalized_replacements: Dict) -> str: | ||
if prefix in value: # value is in the format of "$prefix" or "prefix" | ||
value = cls.normalize_key_string(value.replace(prefix, "")) | ||
if "$" in value: | ||
return Template(value).safe_substitute(normalized_replacements) | ||
else: | ||
return normalized_replacements[value] | ||
return "" | ||
|
||
@classmethod | ||
def replace_token( | ||
cls, | ||
pattern: regex.Pattern, | ||
prefix: str, | ||
channel: str, | ||
replacements: Dict[str, str], | ||
) -> str: | ||
tokens = pattern.findall(channel) | ||
for token in tokens: | ||
clean_token = token.replace("{", "").replace("}", "") | ||
replacement = cls.get_replacement(prefix, clean_token, replacements) | ||
if replacement: | ||
channel = channel.replace(token, replacement) | ||
return channel | ||
|
||
@classmethod | ||
def template( | ||
cls, | ||
channel_override: Optional[str], | ||
default_channel: str, | ||
cluster_name: str, | ||
labels: Dict[str, str], | ||
annotations: Dict[str, str], | ||
) -> str: | ||
if not channel_override: | ||
return default_channel | ||
|
||
channel = channel_override | ||
if CLUSTER_PREF in channel: | ||
# replace "cluster_name" or "$cluster_name" or ${cluster_name} with the value of the cluster name | ||
channel = CLUSTER_PREF_PATTERN.sub(cluster_name, channel) | ||
|
||
if LABELS_PREF in channel: | ||
normalized_labels = cls.normalize_dict_keys(labels) | ||
channel = cls.replace_token(BRACKETS_PATTERN, LABELS_PREF, channel, normalized_labels) | ||
channel = cls.replace_token(LABEL_PREF_PATTERN, LABELS_PREF, channel, normalized_labels) | ||
|
||
if ANNOTATIONS_PREF in channel: | ||
normalized_annotations = cls.normalize_dict_keys(annotations) | ||
channel = cls.replace_token(BRACKETS_PATTERN, ANNOTATIONS_PREF, channel, normalized_annotations) | ||
channel = cls.replace_token( | ||
ANNOTATIONS_PREF_PATTERN, | ||
ANNOTATIONS_PREF, | ||
channel, | ||
normalized_annotations, | ||
) | ||
|
||
return channel if MISSING not in channel else default_channel |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
from robusta.core.sinks.zulip.zulip_sink import ZulipSink | ||
from robusta.core.sinks.zulip.zulip_sink_params import ZulipSinkConfigWrapper, ZulipSinkParams |
Oops, something went wrong.