robusta-cli 0.11.0__tar.gz → 0.12.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (297) hide show
  1. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/PKG-INFO +4 -1
  2. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/pyproject.toml +6 -2
  3. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/_version.py +1 -1
  4. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/core/model/env_vars.py +5 -0
  5. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/core/model/runner_config.py +2 -0
  6. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/core/playbooks/pod_utils/imagepull_utils.py +1 -1
  7. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/core/reporting/base.py +11 -2
  8. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/core/reporting/blocks.py +2 -2
  9. robusta_cli-0.12.0/src/robusta/core/sinks/common/__init__.py +1 -0
  10. robusta_cli-0.12.0/src/robusta/core/sinks/common/channel_transformer.py +106 -0
  11. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/core/sinks/datadog/datadog_sink_params.py +4 -0
  12. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/core/sinks/discord/discord_sink_params.py +4 -0
  13. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/core/sinks/file/file_sink_params.py +4 -0
  14. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/core/sinks/google_chat/google_chat_params.py +4 -0
  15. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/core/sinks/jira/jira_sink_params.py +4 -0
  16. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/core/sinks/kafka/kafka_sink_params.py +4 -0
  17. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/core/sinks/mail/mail_sink_params.py +4 -0
  18. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/core/sinks/mattermost/mattermost_sink_params.py +4 -0
  19. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/core/sinks/msteams/msteams_sink_params.py +4 -0
  20. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/core/sinks/opsgenie/opsgenie_sink_params.py +4 -0
  21. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/core/sinks/pagerduty/pagerduty_sink_params.py +4 -0
  22. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/core/sinks/pushover/pushover_sink_params.py +5 -0
  23. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/core/sinks/robusta/robusta_sink_params.py +4 -0
  24. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/core/sinks/rocketchat/rocketchat_sink_params.py +4 -0
  25. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/core/sinks/servicenow/servicenow_sink_params.py +4 -0
  26. robusta_cli-0.12.0/src/robusta/core/sinks/sink_base.py +185 -0
  27. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/core/sinks/sink_base_params.py +62 -1
  28. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/core/sinks/sink_factory.py +3 -1
  29. robusta_cli-0.12.0/src/robusta/core/sinks/slack/slack_sink.py +80 -0
  30. robusta_cli-0.12.0/src/robusta/core/sinks/slack/slack_sink_params.py +32 -0
  31. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/core/sinks/telegram/telegram_sink_params.py +4 -0
  32. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/core/sinks/victorops/victorops_sink_params.py +4 -0
  33. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/core/sinks/webex/webex_sink_params.py +4 -0
  34. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/core/sinks/webhook/webhook_sink_params.py +4 -0
  35. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/core/sinks/yamessenger/yamessenger_sink_params.py +4 -0
  36. robusta_cli-0.12.0/src/robusta/core/sinks/zulip/__init__.py +2 -0
  37. robusta_cli-0.12.0/src/robusta/core/sinks/zulip/zulip_sink.py +32 -0
  38. robusta_cli-0.12.0/src/robusta/core/sinks/zulip/zulip_sink_params.py +28 -0
  39. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/core/triggers/container_oom_killed_trigger.py +4 -1
  40. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/core/triggers/error_event_trigger.py +9 -0
  41. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/core/triggers/job_failed_trigger.py +4 -1
  42. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/core/triggers/multi_resources_trigger.py +2 -5
  43. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/core/triggers/oom_killed_trigger_base.py +3 -0
  44. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/core/triggers/pod_crash_loop_trigger.py +4 -1
  45. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/core/triggers/pod_image_pull_backoff.py +4 -1
  46. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/core/triggers/pod_oom_killed_trigger.py +4 -1
  47. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/integrations/kubernetes/api_client_utils.py +23 -1
  48. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/integrations/kubernetes/autogenerated/triggers.py +32 -9
  49. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/integrations/kubernetes/base_triggers.py +4 -8
  50. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/integrations/kubernetes/custom_models.py +75 -30
  51. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/integrations/slack/sender.py +153 -12
  52. robusta_cli-0.12.0/src/robusta/integrations/zulip/__init__.py +1 -0
  53. robusta_cli-0.12.0/src/robusta/integrations/zulip/sender.py +171 -0
  54. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/runner/web.py +30 -2
  55. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/utils/scope.py +11 -8
  56. robusta_cli-0.11.0/src/robusta/core/sinks/sink_base.py +0 -63
  57. robusta_cli-0.11.0/src/robusta/core/sinks/slack/slack_sink.py +0 -15
  58. robusta_cli-0.11.0/src/robusta/core/sinks/slack/slack_sink_params.py +0 -116
  59. robusta_cli-0.11.0/src/robusta/utils/__init__.py +0 -0
  60. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/LICENSE +0 -0
  61. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/__init__.py +0 -0
  62. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/api/__init__.py +0 -0
  63. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/cli/__init__.py +0 -0
  64. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/cli/auth.py +0 -0
  65. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/cli/backend_profile.py +0 -0
  66. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/cli/eula.py +0 -0
  67. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/cli/integrations_cmd.py +0 -0
  68. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/cli/main.py +0 -0
  69. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/cli/playbooks_cmd.py +0 -0
  70. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/cli/self_host.py +0 -0
  71. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/cli/slack_feedback_message.py +0 -0
  72. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/cli/slack_verification.py +0 -0
  73. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/cli/utils.py +0 -0
  74. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/core/__init__.py +0 -0
  75. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/core/discovery/__init__.py +0 -0
  76. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/core/discovery/discovery.py +0 -0
  77. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/core/discovery/resource_names.py +0 -0
  78. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/core/discovery/top_service_resolver.py +0 -0
  79. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/core/discovery/utils.py +0 -0
  80. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/core/exceptions.py +0 -0
  81. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/core/model/__init__.py +0 -0
  82. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/core/model/base_params.py +0 -0
  83. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/core/model/cluster_status.py +0 -0
  84. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/core/model/events.py +0 -0
  85. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/core/model/helm_release.py +0 -0
  86. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/core/model/jobs.py +0 -0
  87. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/core/model/k8s_operation_type.py +0 -0
  88. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/core/model/namespaces.py +0 -0
  89. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/core/model/nodes.py +0 -0
  90. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/core/model/pods.py +0 -0
  91. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/core/model/services.py +0 -0
  92. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/core/persistency/__init__.py +0 -0
  93. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/core/persistency/in_memory.py +0 -0
  94. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/core/persistency/scheduled_jobs_states_dal.py +0 -0
  95. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/core/playbooks/__init__.py +0 -0
  96. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/core/playbooks/actions_registry.py +0 -0
  97. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/core/playbooks/base_trigger.py +0 -0
  98. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/core/playbooks/common.py +0 -0
  99. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/core/playbooks/container_playbook_utils.py +0 -0
  100. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/core/playbooks/crash_reporter.py +0 -0
  101. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/core/playbooks/generation.py +0 -0
  102. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/core/playbooks/internal/discovery_events.py +0 -0
  103. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/core/playbooks/job_utils.py +0 -0
  104. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/core/playbooks/node_playbook_utils.py +0 -0
  105. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/core/playbooks/oom_killer_utils.py +0 -0
  106. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/core/playbooks/playbook_utils.py +0 -0
  107. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/core/playbooks/playbooks_event_handler.py +0 -0
  108. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/core/playbooks/playbooks_event_handler_impl.py +0 -0
  109. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/core/playbooks/pod_utils/crashloop_utils.py +0 -0
  110. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/core/playbooks/pod_utils/pending_pod_utils.py +0 -0
  111. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/core/playbooks/prometheus_enrichment_utils.py +0 -0
  112. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/core/playbooks/trigger.py +0 -0
  113. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/core/pubsub/__init__.py +0 -0
  114. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/core/pubsub/event_emitter.py +0 -0
  115. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/core/pubsub/event_subscriber.py +0 -0
  116. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/core/pubsub/events_pubsub.py +0 -0
  117. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/core/reporting/__init__.py +0 -0
  118. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/core/reporting/action_requests.py +0 -0
  119. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/core/reporting/callbacks.py +0 -0
  120. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/core/reporting/consts.py +0 -0
  121. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/core/reporting/custom_rendering.py +0 -0
  122. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/core/reporting/finding_subjects.py +0 -0
  123. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/core/reporting/utils.py +0 -0
  124. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/core/schedule/__init__.py +0 -0
  125. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/core/schedule/model.py +0 -0
  126. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/core/schedule/scheduler.py +0 -0
  127. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/core/sinks/__init__.py +0 -0
  128. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/core/sinks/common/html_tools.py +0 -0
  129. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/core/sinks/datadog/__init__.py +0 -0
  130. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/core/sinks/datadog/datadog_sink.py +0 -0
  131. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/core/sinks/discord/__init__.py +0 -0
  132. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/core/sinks/discord/discord_sink.py +0 -0
  133. {robusta_cli-0.11.0/src/robusta/core/sinks/common → robusta_cli-0.12.0/src/robusta/core/sinks/file}/__init__.py +0 -0
  134. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/core/sinks/file/file_sink.py +0 -0
  135. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/core/sinks/file/object_traverser.py +0 -0
  136. {robusta_cli-0.11.0/src/robusta/core/sinks/file → robusta_cli-0.12.0/src/robusta/core/sinks/google_chat}/__init__.py +0 -0
  137. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/core/sinks/google_chat/google_chat.py +0 -0
  138. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/core/sinks/jira/__init__.py +0 -0
  139. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/core/sinks/jira/jira_sink.py +0 -0
  140. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/core/sinks/kafka/__init__.py +0 -0
  141. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/core/sinks/kafka/kafka_sink.py +0 -0
  142. {robusta_cli-0.11.0/src/robusta/core/sinks/google_chat → robusta_cli-0.12.0/src/robusta/core/sinks/mail}/__init__.py +0 -0
  143. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/core/sinks/mail/mail_sink.py +0 -0
  144. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/core/sinks/mattermost/__init__.py +0 -0
  145. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/core/sinks/mattermost/mattermost_sink.py +0 -0
  146. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/core/sinks/msteams/__init__.py +0 -0
  147. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/core/sinks/msteams/msteams_sink.py +0 -0
  148. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/core/sinks/opsgenie/__init__.py +0 -0
  149. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/core/sinks/opsgenie/opsgenie_sink.py +0 -0
  150. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/core/sinks/pagerduty/__init__.py +0 -0
  151. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/core/sinks/pagerduty/pagerduty_sink.py +0 -0
  152. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/core/sinks/pushover/__init__.py +0 -0
  153. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/core/sinks/pushover/pushover_client.py +0 -0
  154. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/core/sinks/pushover/pushover_sink.py +0 -0
  155. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/core/sinks/robusta/__init__.py +0 -0
  156. {robusta_cli-0.11.0/src/robusta/core/sinks/mail → robusta_cli-0.12.0/src/robusta/core/sinks/robusta/dal}/__init__.py +0 -0
  157. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/core/sinks/robusta/dal/model_conversion.py +0 -0
  158. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/core/sinks/robusta/dal/supabase_dal.py +0 -0
  159. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/core/sinks/robusta/discovery_metrics.py +0 -0
  160. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/core/sinks/robusta/prometheus_health_checker.py +0 -0
  161. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/core/sinks/robusta/robusta_sink.py +0 -0
  162. {robusta_cli-0.11.0/src/robusta/core/sinks/robusta/dal → robusta_cli-0.12.0/src/robusta/core/sinks/robusta/rrm}/__init__.py +0 -0
  163. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/core/sinks/robusta/rrm/account_resource_fetcher.py +0 -0
  164. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/core/sinks/robusta/rrm/base_resource_manager.py +0 -0
  165. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/core/sinks/robusta/rrm/prometheus_alert_resource_manager.py +0 -0
  166. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/core/sinks/robusta/rrm/rrm.py +0 -0
  167. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/core/sinks/robusta/rrm/types.py +0 -0
  168. {robusta_cli-0.11.0/src/robusta/core/sinks/robusta/rrm → robusta_cli-0.12.0/src/robusta/core/sinks/rocketchat}/__init__.py +0 -0
  169. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/core/sinks/rocketchat/rocketchat_sink.py +0 -0
  170. {robusta_cli-0.11.0/src/robusta/core/sinks/rocketchat → robusta_cli-0.12.0/src/robusta/core/sinks/servicenow}/__init__.py +0 -0
  171. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/core/sinks/servicenow/servicenow_sink.py +0 -0
  172. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/core/sinks/sink_config.py +0 -0
  173. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/core/sinks/slack/__init__.py +0 -0
  174. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/core/sinks/telegram/__init__.py +0 -0
  175. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/core/sinks/telegram/telegram_client.py +0 -0
  176. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/core/sinks/telegram/telegram_sink.py +0 -0
  177. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/core/sinks/timing.py +0 -0
  178. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/core/sinks/transformer.py +0 -0
  179. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/core/sinks/victorops/__init__.py +0 -0
  180. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/core/sinks/victorops/victorops_sink.py +0 -0
  181. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/core/sinks/webex/__init__.py +0 -0
  182. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/core/sinks/webex/webex_sink.py +0 -0
  183. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/core/sinks/webhook/__init__.py +0 -0
  184. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/core/sinks/webhook/webhook_sink.py +0 -0
  185. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/core/sinks/yamessenger/__init__.py +0 -0
  186. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/core/sinks/yamessenger/yamessenger_client.py +0 -0
  187. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/core/sinks/yamessenger/yamessenger_sink.py +0 -0
  188. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/core/triggers/custom_triggers.py +0 -0
  189. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/core/triggers/helm_releases_triggers.py +0 -0
  190. {robusta_cli-0.11.0/src/robusta/core/sinks/servicenow → robusta_cli-0.12.0/src/robusta/integrations}/__init__.py +0 -0
  191. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/integrations/argocd/argocd_client.py +0 -0
  192. {robusta_cli-0.11.0/src/robusta/integrations → robusta_cli-0.12.0/src/robusta/integrations/common}/__init__.py +0 -0
  193. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/integrations/common/requests.py +0 -0
  194. {robusta_cli-0.11.0/src/robusta/integrations/common → robusta_cli-0.12.0/src/robusta/integrations/discord}/__init__.py +0 -0
  195. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/integrations/discord/sender.py +0 -0
  196. {robusta_cli-0.11.0/src/robusta/integrations/discord → robusta_cli-0.12.0/src/robusta/integrations/git}/__init__.py +0 -0
  197. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/integrations/git/git_repo.py +0 -0
  198. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/integrations/git/well_known_hosts.py +0 -0
  199. {robusta_cli-0.11.0/src/robusta/integrations/git → robusta_cli-0.12.0/src/robusta/integrations/google_chat}/__init__.py +0 -0
  200. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/integrations/google_chat/sender.py +0 -0
  201. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/integrations/grafana.py +0 -0
  202. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/integrations/helper.py +0 -0
  203. {robusta_cli-0.11.0/src/robusta/integrations/google_chat → robusta_cli-0.12.0/src/robusta/integrations/jira}/__init__.py +0 -0
  204. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/integrations/jira/client.py +0 -0
  205. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/integrations/jira/sender.py +0 -0
  206. {robusta_cli-0.11.0/src/robusta/integrations/jira → robusta_cli-0.12.0/src/robusta/integrations/kubernetes}/__init__.py +0 -0
  207. {robusta_cli-0.11.0/src/robusta/integrations/kubernetes → robusta_cli-0.12.0/src/robusta/integrations/kubernetes/autogenerated}/__init__.py +0 -0
  208. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/integrations/kubernetes/autogenerated/events.py +0 -0
  209. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/integrations/kubernetes/autogenerated/models.py +0 -0
  210. {robusta_cli-0.11.0/src/robusta/integrations/kubernetes/autogenerated → robusta_cli-0.12.0/src/robusta/integrations/kubernetes/autogenerated/v1}/__init__.py +0 -0
  211. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/integrations/kubernetes/autogenerated/v1/models.py +0 -0
  212. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/integrations/kubernetes/base_event.py +0 -0
  213. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/integrations/kubernetes/model_not_found_exception.py +0 -0
  214. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/integrations/kubernetes/process_utils.py +0 -0
  215. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/integrations/kubernetes/templates.py +0 -0
  216. {robusta_cli-0.11.0/src/robusta/integrations/kubernetes/autogenerated/v1 → robusta_cli-0.12.0/src/robusta/integrations/mail}/__init__.py +0 -0
  217. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/integrations/mail/sender.py +0 -0
  218. {robusta_cli-0.11.0/src/robusta/integrations/mail → robusta_cli-0.12.0/src/robusta/integrations/mattermost}/__init__.py +0 -0
  219. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/integrations/mattermost/client.py +0 -0
  220. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/integrations/mattermost/sender.py +0 -0
  221. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/integrations/msteams/__init__.py +0 -0
  222. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/integrations/msteams/msteams_adaptive_card_files.py +0 -0
  223. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/integrations/msteams/msteams_adaptive_card_files_image.py +0 -0
  224. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/integrations/msteams/msteams_adaptive_card_files_text.py +0 -0
  225. {robusta_cli-0.11.0/src/robusta/integrations/mattermost → robusta_cli-0.12.0/src/robusta/integrations/msteams/msteams_elements}/__init__.py +0 -0
  226. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/integrations/msteams/msteams_elements/msteams_action.py +0 -0
  227. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/integrations/msteams/msteams_elements/msteams_base.py +0 -0
  228. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/integrations/msteams/msteams_elements/msteams_card.py +0 -0
  229. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/integrations/msteams/msteams_elements/msteams_column.py +0 -0
  230. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/integrations/msteams/msteams_elements/msteams_container.py +0 -0
  231. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/integrations/msteams/msteams_elements/msteams_images.py +0 -0
  232. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/integrations/msteams/msteams_elements/msteams_table.py +0 -0
  233. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/integrations/msteams/msteams_elements/msteams_text_block.py +0 -0
  234. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/integrations/msteams/msteams_mark_down_fix_url.py +0 -0
  235. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/integrations/msteams/msteams_msg.py +0 -0
  236. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/integrations/msteams/sender.py +0 -0
  237. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/integrations/openshift/__init__.py +0 -0
  238. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/integrations/openshift/token.py +0 -0
  239. {robusta_cli-0.11.0/src/robusta/integrations/msteams/msteams_elements → robusta_cli-0.12.0/src/robusta/integrations/prometheus}/__init__.py +0 -0
  240. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/integrations/prometheus/models.py +0 -0
  241. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/integrations/prometheus/trigger.py +0 -0
  242. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/integrations/prometheus/utils.py +0 -0
  243. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/integrations/receiver.py +0 -0
  244. {robusta_cli-0.11.0/src/robusta/integrations/prometheus → robusta_cli-0.12.0/src/robusta/integrations/resource_analysis}/__init__.py +0 -0
  245. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/integrations/resource_analysis/cpu_analyzer.py +0 -0
  246. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/integrations/resource_analysis/memory_analyzer.py +0 -0
  247. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/integrations/resource_analysis/node_cpu_analyzer.py +0 -0
  248. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/integrations/resource_analysis/prometheus_analyzer.py +0 -0
  249. {robusta_cli-0.11.0/src/robusta/integrations/resource_analysis → robusta_cli-0.12.0/src/robusta/integrations/rocketchat}/__init__.py +0 -0
  250. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/integrations/rocketchat/sender.py +0 -0
  251. {robusta_cli-0.11.0/src/robusta/integrations/rocketchat → robusta_cli-0.12.0/src/robusta/integrations/scheduled}/__init__.py +0 -0
  252. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/integrations/scheduled/event.py +0 -0
  253. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/integrations/scheduled/models.py +0 -0
  254. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/integrations/scheduled/playbook_scheduler.py +0 -0
  255. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/integrations/scheduled/playbook_scheduler_manager.py +0 -0
  256. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/integrations/scheduled/playbook_scheduler_manager_impl.py +0 -0
  257. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/integrations/scheduled/trigger.py +0 -0
  258. {robusta_cli-0.11.0/src/robusta/integrations/scheduled → robusta_cli-0.12.0/src/robusta/integrations/servicenow}/__init__.py +0 -0
  259. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/integrations/servicenow/sender.py +0 -0
  260. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/integrations/slack/__init__.py +0 -0
  261. {robusta_cli-0.11.0/src/robusta/integrations/servicenow → robusta_cli-0.12.0/src/robusta/integrations/webex}/__init__.py +0 -0
  262. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/integrations/webex/sender.py +0 -0
  263. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/model/alert_relabel_config.py +0 -0
  264. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/model/config.py +0 -0
  265. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/model/playbook_action.py +0 -0
  266. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/model/playbook_definition.py +0 -0
  267. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/patch/patch.py +0 -0
  268. {robusta_cli-0.11.0/src/robusta/integrations/webex → robusta_cli-0.12.0/src/robusta/runner}/__init__.py +0 -0
  269. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/runner/config_loader.py +0 -0
  270. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/runner/log_init.py +0 -0
  271. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/runner/main.py +0 -0
  272. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/runner/not_found_exception.py +0 -0
  273. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/runner/object_updater.py +0 -0
  274. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/runner/process_setup.py +0 -0
  275. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/runner/ssl_utils.py +0 -0
  276. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/runner/telemetry.py +0 -0
  277. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/runner/telemetry_service.py +0 -0
  278. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/runner/web_api.py +0 -0
  279. {robusta_cli-0.11.0/src/robusta/runner → robusta_cli-0.12.0/src/robusta/utils}/__init__.py +0 -0
  280. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/utils/auth_provider.py +0 -0
  281. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/utils/base64_utils.py +0 -0
  282. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/utils/cluster_provider_discovery.py +0 -0
  283. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/utils/common.py +0 -0
  284. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/utils/decorators.py +0 -0
  285. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/utils/docs.py +0 -0
  286. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/utils/documented_pydantic.py +0 -0
  287. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/utils/error_codes.py +0 -0
  288. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/utils/file_system_watcher.py +0 -0
  289. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/utils/function_hashes.py +0 -0
  290. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/utils/json_schema.py +0 -0
  291. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/utils/parsing.py +0 -0
  292. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/utils/rate_limiter.py +0 -0
  293. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/utils/server_start.py +0 -0
  294. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/utils/service_discovery.py +0 -0
  295. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/utils/silence_utils.py +0 -0
  296. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/utils/stack_tracer.py +0 -0
  297. {robusta_cli-0.11.0 → robusta_cli-0.12.0}/src/robusta/utils/task_queue.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: robusta-cli
3
- Version: 0.11.0
3
+ Version: 0.12.0
4
4
  Summary:
5
5
  Author: Natan Yellin
6
6
  Author-email: aantn@users.noreply.github.com
@@ -17,6 +17,8 @@ Requires-Dist: apprise (>=1.5.0,<2.0.0)
17
17
  Requires-Dist: attrs (>=23.1.0,<24.0.0)
18
18
  Requires-Dist: better-exceptions (>=0.3.3,<0.4.0) ; extra == "all"
19
19
  Requires-Dist: bitmath (>=1.3.3.1,<2.0.0.0)
20
+ Requires-Dist: boto3 (==1.28.72)
21
+ Requires-Dist: botocore (==1.31.72)
20
22
  Requires-Dist: click-spinner (>=0.1.10,<0.2.0)
21
23
  Requires-Dist: colorlog (>=5.0.1,<6.0.0)
22
24
  Requires-Dist: croniter (>=1.3.15,<2.0.0)
@@ -28,6 +30,7 @@ Requires-Dist: dulwich (==0.20.28) ; extra == "all"
28
30
  Requires-Dist: fpdf2 (>=2.7.1,<3.0.0)
29
31
  Requires-Dist: grafana-api (>=1.0.3,<2.0.0) ; extra == "all"
30
32
  Requires-Dist: hikaru-model-26 (>=1.1.1,<2.0.0)
33
+ Requires-Dist: humanize (>=3.13.1,<4.0.0)
31
34
  Requires-Dist: jinja2 (==3.1.3)
32
35
  Requires-Dist: kafka-python (>=2.0.2,<3.0.0) ; extra == "all"
33
36
  Requires-Dist: kubernetes (>=26.1.0,<27.0.0)
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "robusta-cli"
3
- version = "0.11.0"
3
+ version = "0.12.0"
4
4
  description = ""
5
5
  authors = ["Natan Yellin <aantn@users.noreply.github.com>"]
6
6
  packages = [
@@ -58,6 +58,11 @@ watchgod = "^0.7"
58
58
  webexteamssdk = "^1.6.1"
59
59
  bitmath = "^1.3.3.1"
60
60
  croniter = "^1.3.15"
61
+ humanize = "^3.13.1"
62
+
63
+ # The following are added to speed up poetry dependency resolution
64
+ botocore = "1.31.72"
65
+ boto3 = "1.28.72"
61
66
 
62
67
  # we're freezing a specific version here because the latest version doesn't have prebuilt wheels on pypi
63
68
  # and therefore requires gcc to install which we'd like to avoid
@@ -88,7 +93,6 @@ sphinxcontrib-images = "^0.9.4"
88
93
  jsonref = "^0.2"
89
94
  Pillow = "^10.3.0"
90
95
  sphinxcontrib-mermaid = "^0.7.1"
91
- humanize = "^3.13.1"
92
96
  cssselect = "^1.1.0"
93
97
  pygal = "^3.0.0"
94
98
  tinycss = "^0.4"
@@ -1,2 +1,2 @@
1
1
  # this is updated by .github/workflows/release.yaml
2
- __version__ = "0.11.0"
2
+ __version__ = "0.12.0"
@@ -113,3 +113,8 @@ RUN_AS_SUBPROCESS = load_bool("RUN_AS_SUBPROCESS", True)
113
113
  ARGO_ROLLOUTS = load_bool("ARGO_ROLLOUTS", False)
114
114
  # lowered case k8s kinds in a json array string. "[\"configmap\", \"secret\"]"
115
115
  RESOURCE_YAML_BLOCK_LIST = json.loads(os.environ.get("RESOURCE_YAML_BLOCK_LIST", "[]"))
116
+
117
+ NAMESPACE_DATA_TTL = int(os.environ.get("NAMESPACE_DATA_TTL", 30*60)) # in seconds
118
+
119
+ PROCESSED_ALERTS_CACHE_TTL = int(os.environ.get("PROCESSED_ALERT_CACHE_TTL", 2*3600))
120
+ PROCESSED_ALERTS_CACHE_MAX_SIZE = int(os.environ.get("PROCESSED_ALERTS_CACHE_MAX_SIZE", 100_000))
@@ -25,6 +25,7 @@ from robusta.core.sinks.webex.webex_sink_params import WebexSinkConfigWrapper
25
25
  from robusta.core.sinks.webhook.webhook_sink_params import WebhookSinkConfigWrapper
26
26
  from robusta.core.sinks.yamessenger.yamessenger_sink_params import YaMessengerSinkConfigWrapper
27
27
  from robusta.core.sinks.pushover.pushover_sink_params import PushoverSinkConfigWrapper
28
+ from robusta.core.sinks.zulip.zulip_sink_params import ZulipSinkConfigWrapper
28
29
  from robusta.model.alert_relabel_config import AlertRelabel
29
30
  from robusta.model.playbook_definition import PlaybookDefinition
30
31
  from robusta.utils.base64_utils import is_base64_encoded
@@ -63,6 +64,7 @@ class RunnerConfig(BaseModel):
63
64
  PushoverSinkConfigWrapper,
64
65
  GoogleChatSinkConfigWrapper,
65
66
  ServiceNowSinkConfigWrapper,
67
+ ZulipSinkConfigWrapper
66
68
  ]
67
69
  ]
68
70
  ]
@@ -101,7 +101,7 @@ def get_image_pull_backoff_enrichment(pod: Pod) -> Enrichment:
101
101
  return Enrichment(
102
102
  enrichment_type=EnrichmentType.image_pull_backoff_info,
103
103
  blocks=image_pull_table_blocks,
104
- title="Container Image-Pull-Backoff Information")
104
+ title="Container ImagePullBackoff Information")
105
105
 
106
106
 
107
107
  def __imagepull_backoff_reason_to_fix(reason: ImagePullBackoffReason) -> Optional[Tuple[str, str]]:
@@ -14,6 +14,7 @@ from pydantic.main import BaseModel
14
14
  from robusta.core.discovery.top_service_resolver import TopServiceResolver
15
15
  from robusta.core.model.env_vars import ROBUSTA_UI_DOMAIN
16
16
  from robusta.core.reporting.consts import FindingSource, FindingSubjectType, FindingType
17
+ from robusta.integrations.kubernetes.api_client_utils import get_namespace_labels
17
18
  from robusta.utils.scope import BaseScopeMatcher
18
19
 
19
20
 
@@ -106,7 +107,7 @@ class EnrichmentType(Enum):
106
107
 
107
108
 
108
109
  class Enrichment:
109
- # These is the actual enrichment data
110
+ # This is the actual enrichment data
110
111
  blocks: List[BaseBlock] = []
111
112
  # General purpose rendering flags, that can be used by specific sinks
112
113
  annotations: Dict[str, str] = {}
@@ -138,6 +139,9 @@ class FilterableScopeMatcher(BaseScopeMatcher):
138
139
  def get_data(self) -> Dict:
139
140
  return self.data
140
141
 
142
+ def scope_match_namespace_labels(self, attr_matcher, attr_value):
143
+ return self.match_labels_annotations(attr_matcher, attr_value)
144
+
141
145
 
142
146
  class Filterable(ABC):
143
147
  @property
@@ -169,7 +173,12 @@ class Filterable(ABC):
169
173
  # 1. "scope" check
170
174
  accept = True
171
175
  if scope_requirements is not None:
172
- matcher = FilterableScopeMatcher(self.attribute_map)
176
+ data = self.attribute_map
177
+ try:
178
+ data["namespace_labels"] = get_namespace_labels(data["namespace"])
179
+ except KeyError:
180
+ data["namespace_labels"] = {}
181
+ matcher = FilterableScopeMatcher(data)
173
182
  if scope_requirements.exclude:
174
183
  if matcher.scope_inc_exc_matches(scope_requirements.exclude):
175
184
  return False
@@ -427,13 +427,13 @@ class TableBlock(BaseBlock):
427
427
 
428
428
  return MarkdownBlock(f"{prefix}{table_contents}{suffix}")
429
429
 
430
- def to_table_string(self, table_max_width: int = PRINTED_TABLE_MAX_WIDTH) -> str:
430
+ def to_table_string(self, table_max_width: int = PRINTED_TABLE_MAX_WIDTH, table_fmt: str = "presto") -> str:
431
431
  rendered_rows = self.__to_strings_rows(self.render_rows())
432
432
  col_max_width = self.__calc_max_width(self.headers, rendered_rows, table_max_width)
433
433
  return tabulate(
434
434
  rendered_rows,
435
435
  headers=self.headers,
436
- tablefmt="presto",
436
+ tablefmt=table_fmt,
437
437
  maxcolwidths=col_max_width,
438
438
  )
439
439
 
@@ -0,0 +1 @@
1
+ from robusta.core.sinks.common.channel_transformer import ChannelTransformer
@@ -0,0 +1,106 @@
1
+ from collections import defaultdict
2
+ from string import Template
3
+ from typing import Dict, Optional, Union
4
+
5
+ import regex
6
+
7
+ CLUSTER_PREF = "cluster_name"
8
+ CLUSTER_PREF_PATTERN = regex.compile("(\$?\{?" + CLUSTER_PREF + "\}?)") # noqa: W605
9
+ LABELS_PREF = "labels."
10
+ ESCAPED_LABEL_PREF = regex.escape(LABELS_PREF)
11
+ LABEL_PREF_PATTERN = regex.compile("\$?" + ESCAPED_LABEL_PREF + "[\w.]+") # noqa: W605
12
+ ANNOTATIONS_PREF = "annotations."
13
+ ESCAPED_ANNOTATIONS_PREF = regex.escape(ANNOTATIONS_PREF)
14
+ ANNOTATIONS_PREF_PATTERN = regex.compile("\$?" + ESCAPED_ANNOTATIONS_PREF + "[\w.]+") # noqa: W605
15
+ BRACKETS_PATTERN = regex.compile(r"\$\{[^\}]+\}")
16
+ COMPOSITE_PATTERN = r".*\$({?labels.[^$]+|{?annotations.[^$]+|{?cluster_name).*"
17
+ ONLY_VALUE_PATTERN = r"^(labels.[^$]+|annotations.[^$]+|cluster_name)$"
18
+ MISSING = "<missing>"
19
+
20
+
21
+ class ChannelTransformer:
22
+ @classmethod
23
+ def validate_channel_override(cls, v: Union[str, None]):
24
+ if v:
25
+ if regex.match(ONLY_VALUE_PATTERN, v):
26
+ return "$" + v
27
+ if not regex.match(COMPOSITE_PATTERN, v):
28
+ err_msg = (
29
+ f"channel_override must be '{CLUSTER_PREF}' or '{LABELS_PREF}foo' or '{ANNOTATIONS_PREF}foo' "
30
+ f"or contain patters like: '${CLUSTER_PREF}'/'${LABELS_PREF}foo'/"
31
+ f"'${ANNOTATIONS_PREF}foo'"
32
+ )
33
+ raise ValueError(err_msg)
34
+ return v
35
+
36
+ @classmethod
37
+ def normalize_key_string(cls, s: str) -> str:
38
+ return s.replace("/", "_").replace(".", "_").replace("-", "_")
39
+
40
+ @classmethod
41
+ def normalize_dict_keys(cls, metadata: Dict) -> Dict:
42
+ result = defaultdict(lambda: MISSING)
43
+ result.update({cls.normalize_key_string(k): v for k, v in metadata.items()})
44
+ return result
45
+
46
+ # if prefix not present, return ""
47
+ # else, if found, return replacement else return MISSING
48
+ @classmethod
49
+ def get_replacement(cls, prefix: str, value: str, normalized_replacements: Dict) -> str:
50
+ if prefix in value: # value is in the format of "$prefix" or "prefix"
51
+ value = cls.normalize_key_string(value.replace(prefix, ""))
52
+ if "$" in value:
53
+ return Template(value).safe_substitute(normalized_replacements)
54
+ else:
55
+ return normalized_replacements[value]
56
+ return ""
57
+
58
+ @classmethod
59
+ def replace_token(
60
+ cls,
61
+ pattern: regex.Pattern,
62
+ prefix: str,
63
+ channel: str,
64
+ replacements: Dict[str, str],
65
+ ) -> str:
66
+ tokens = pattern.findall(channel)
67
+ for token in tokens:
68
+ clean_token = token.replace("{", "").replace("}", "")
69
+ replacement = cls.get_replacement(prefix, clean_token, replacements)
70
+ if replacement:
71
+ channel = channel.replace(token, replacement)
72
+ return channel
73
+
74
+ @classmethod
75
+ def template(
76
+ cls,
77
+ channel_override: Optional[str],
78
+ default_channel: str,
79
+ cluster_name: str,
80
+ labels: Dict[str, str],
81
+ annotations: Dict[str, str],
82
+ ) -> str:
83
+ if not channel_override:
84
+ return default_channel
85
+
86
+ channel = channel_override
87
+ if CLUSTER_PREF in channel:
88
+ # replace "cluster_name" or "$cluster_name" or ${cluster_name} with the value of the cluster name
89
+ channel = CLUSTER_PREF_PATTERN.sub(cluster_name, channel)
90
+
91
+ if LABELS_PREF in channel:
92
+ normalized_labels = cls.normalize_dict_keys(labels)
93
+ channel = cls.replace_token(BRACKETS_PATTERN, LABELS_PREF, channel, normalized_labels)
94
+ channel = cls.replace_token(LABEL_PREF_PATTERN, LABELS_PREF, channel, normalized_labels)
95
+
96
+ if ANNOTATIONS_PREF in channel:
97
+ normalized_annotations = cls.normalize_dict_keys(annotations)
98
+ channel = cls.replace_token(BRACKETS_PATTERN, ANNOTATIONS_PREF, channel, normalized_annotations)
99
+ channel = cls.replace_token(
100
+ ANNOTATIONS_PREF_PATTERN,
101
+ ANNOTATIONS_PREF,
102
+ channel,
103
+ normalized_annotations,
104
+ )
105
+
106
+ return channel if MISSING not in channel else default_channel
@@ -5,6 +5,10 @@ from robusta.core.sinks.sink_config import SinkConfigBase
5
5
  class DataDogSinkParams(SinkBaseParams):
6
6
  api_key: str
7
7
 
8
+ @classmethod
9
+ def _get_sink_type(cls):
10
+ return "datadog"
11
+
8
12
 
9
13
  class DataDogSinkConfigWrapper(SinkConfigBase):
10
14
  datadog_sink: DataDogSinkParams
@@ -5,6 +5,10 @@ from robusta.core.sinks.sink_config import SinkConfigBase
5
5
  class DiscordSinkParams(SinkBaseParams):
6
6
  url: str
7
7
 
8
+ @classmethod
9
+ def _get_sink_type(cls):
10
+ return "discord"
11
+
8
12
 
9
13
  class DiscordSinkConfigWrapper(SinkConfigBase):
10
14
  discord_sink: DiscordSinkParams
@@ -7,6 +7,10 @@ from robusta.core.sinks.sink_config import SinkConfigBase
7
7
  class FileSinkParms(SinkBaseParams):
8
8
  file_name: str = None
9
9
 
10
+ @classmethod
11
+ def _get_sink_type(cls):
12
+ return "file"
13
+
10
14
 
11
15
  class FileSinkConfigWrapper(SinkConfigBase):
12
16
  file_sink: FileSinkParms
@@ -7,6 +7,10 @@ from robusta.core.sinks.sink_config import SinkConfigBase
7
7
  class GoogleChatSinkParams(SinkBaseParams):
8
8
  webhook_url: SecretStr
9
9
 
10
+ @classmethod
11
+ def _get_sink_type(cls):
12
+ return "google_chat"
13
+
10
14
 
11
15
  class GoogleChatSinkConfigWrapper(SinkConfigBase):
12
16
  google_chat_sink: GoogleChatSinkParams
@@ -19,6 +19,10 @@ class JiraSinkParams(SinkBaseParams):
19
19
  reopenStatusName: Optional[str] = "To Do"
20
20
  noReopenResolution: Optional[str] = ""
21
21
 
22
+ @classmethod
23
+ def _get_sink_type(cls):
24
+ return "jira"
25
+
22
26
 
23
27
  class JiraSinkConfigWrapper(SinkConfigBase):
24
28
  jira_sink: JiraSinkParams
@@ -7,6 +7,10 @@ class KafkaSinkParams(SinkBaseParams):
7
7
  topic: str
8
8
  auth: dict = {}
9
9
 
10
+ @classmethod
11
+ def _get_sink_type(cls):
12
+ return "kafka"
13
+
10
14
 
11
15
  class KafkaSinkConfigWrapper(SinkConfigBase):
12
16
  kafka_sink: KafkaSinkParams
@@ -7,6 +7,10 @@ from robusta.core.sinks.sink_config import SinkConfigBase
7
7
  class MailSinkParams(SinkBaseParams):
8
8
  mailto: str
9
9
 
10
+ @classmethod
11
+ def _get_sink_type(cls):
12
+ return "mail"
13
+
10
14
  @validator("mailto")
11
15
  def validate_mailto(cls, mailto):
12
16
  # Make sure we only handle emails and exclude other schemes provided by apprise
@@ -15,6 +15,10 @@ class MattermostSinkParams(SinkBaseParams):
15
15
  team: Optional[str]
16
16
  team_id: Optional[str]
17
17
 
18
+ @classmethod
19
+ def _get_sink_type(cls):
20
+ return "mattermost"
21
+
18
22
  @validator("url")
19
23
  def set_http_schema(cls, url):
20
24
  parsed_url = urlparse(url)
@@ -5,6 +5,10 @@ from robusta.core.sinks.sink_config import SinkConfigBase
5
5
  class MsTeamsSinkParams(SinkBaseParams):
6
6
  webhook_url: str
7
7
 
8
+ @classmethod
9
+ def _get_sink_type(cls):
10
+ return "msteams"
11
+
8
12
 
9
13
  class MsTeamsSinkConfigWrapper(SinkConfigBase):
10
14
  ms_teams_sink: MsTeamsSinkParams
@@ -10,6 +10,10 @@ class OpsGenieSinkParams(SinkBaseParams):
10
10
  tags: List[str] = []
11
11
  host: Optional[str] = None # NOTE: If None, the default value will be used from opsgenie_sdk
12
12
 
13
+ @classmethod
14
+ def _get_sink_type(cls):
15
+ return "opsgenie"
16
+
13
17
 
14
18
  class OpsGenieSinkConfigWrapper(SinkConfigBase):
15
19
  opsgenie_sink: OpsGenieSinkParams
@@ -5,6 +5,10 @@ from robusta.core.sinks.sink_config import SinkConfigBase
5
5
  class PagerdutySinkParams(SinkBaseParams):
6
6
  api_key: str
7
7
 
8
+ @classmethod
9
+ def _get_sink_type(cls):
10
+ return "pagerduty"
11
+
8
12
 
9
13
  class PagerdutyConfigWrapper(SinkConfigBase):
10
14
  pagerduty_sink: PagerdutySinkParams
@@ -10,6 +10,11 @@ class PushoverSinkParams(SinkBaseParams):
10
10
  device: str = None
11
11
  pushover_url: str = "https://api.pushover.net/1/messages.json"
12
12
 
13
+ @classmethod
14
+ def _get_sink_type(cls):
15
+ return "pushover"
16
+
17
+
13
18
  class PushoverSinkConfigWrapper(SinkConfigBase):
14
19
  pushover_sink: PushoverSinkParams
15
20
 
@@ -17,6 +17,10 @@ class RobustaSinkParams(SinkBaseParams):
17
17
  ttl_hours: int = 4380 # Time before unactive cluster data is deleted. 6 Months default.
18
18
  persist_events: bool = False
19
19
 
20
+ @classmethod
21
+ def _get_sink_type(cls):
22
+ return "robusta"
23
+
20
24
 
21
25
  class RobustaSinkConfigWrapper(SinkConfigBase):
22
26
  robusta_sink: RobustaSinkParams
@@ -12,6 +12,10 @@ class RocketchatSinkParams(SinkBaseParams):
12
12
  user_id: str
13
13
  server_url: str
14
14
 
15
+ @classmethod
16
+ def _get_sink_type(cls):
17
+ return "rocketchat"
18
+
15
19
  def get_rocketchat_channel(self) -> str:
16
20
  return self.channel
17
21
 
@@ -12,6 +12,10 @@ class ServiceNowSinkParams(SinkBaseParams):
12
12
  password: SecretStr
13
13
  caller_id: Optional[str]
14
14
 
15
+ @classmethod
16
+ def _get_sink_type(cls):
17
+ return "servicenow"
18
+
15
19
 
16
20
  class ServiceNowSinkConfigWrapper(SinkConfigBase):
17
21
  service_now_sink: ServiceNowSinkParams
@@ -0,0 +1,185 @@
1
+ import threading
2
+ import time
3
+ from abc import abstractmethod, ABC
4
+ from collections import defaultdict
5
+ from typing import Any, List, Dict, Tuple, DefaultDict, Optional
6
+
7
+ from pydantic import BaseModel, Field
8
+
9
+ from robusta.core.model.k8s_operation_type import K8sOperationType
10
+ from robusta.core.reporting.base import Finding
11
+ from robusta.core.sinks.sink_base_params import ActivityInterval, ActivityParams, SinkBaseParams
12
+ from robusta.core.sinks.timing import TimeSlice, TimeSliceAlways
13
+
14
+
15
+ KeyT = Tuple[str, ...]
16
+
17
+
18
+ class NotificationGroup(BaseModel):
19
+ timestamps: List[float] = []
20
+
21
+ def register_notification(self, interval: int, threshold: int) -> bool:
22
+ """Record information about an incoming notification. Returns True if this
23
+ notification should be emitted by a sink, False otherwise."""
24
+ now_ts = time.time()
25
+
26
+ # Prune any events older than the interval
27
+ while self.timestamps and now_ts - self.timestamps[0] > interval:
28
+ self.timestamps.pop(0)
29
+
30
+ self.timestamps.append(now_ts)
31
+ return len(self.timestamps) >= threshold
32
+
33
+
34
+ class NotificationSummary(BaseModel):
35
+ message_id: Optional[str] = None # identifier of the summary message
36
+ start_ts: float = Field(default_factory=lambda: time.time()) # Timestamp of the first notification
37
+ # Keys for the table are determined by grouping.notification_mode.summary.by
38
+ summary_table: DefaultDict[KeyT, List[int]] = None
39
+
40
+ def register_notification(self, summary_key: KeyT, resolved: bool, interval: int):
41
+ now_ts = time.time()
42
+ idx = 1 if resolved else 0
43
+ if now_ts - self.start_ts > interval or not self.summary_table:
44
+ # Expired or the first summary ever for this group_key, reset the data
45
+ self.summary_table = defaultdict(lambda: [0, 0])
46
+ self.start_ts = now_ts
47
+ self.message_id = None
48
+ self.summary_table[summary_key][idx] += 1
49
+
50
+
51
+ class SinkBase(ABC):
52
+ grouping_enabled: bool
53
+ grouping_summary_mode: bool
54
+
55
+ # Keys for groups and summaries are determined by grouping.group_by
56
+ groups: DefaultDict[KeyT, NotificationGroup]
57
+ summaries: DefaultDict[KeyT, NotificationSummary]
58
+
59
+ # Notification summaries
60
+ summary_header: List[str] # descriptive header for the summary table
61
+
62
+ def __init__(self, sink_params: SinkBaseParams, registry):
63
+ self.sink_name = sink_params.name
64
+ self.params = sink_params
65
+ self.default = sink_params.default
66
+ self.registry = registry
67
+ global_config = self.registry.get_global_config()
68
+
69
+ self.account_id: str = global_config.get("account_id", "")
70
+ self.cluster_name: str = global_config.get("cluster_name", "")
71
+ self.signing_key = global_config.get("signing_key", "")
72
+
73
+ self.time_slices = self._build_time_slices_from_params(self.params.activity)
74
+
75
+ self.grouping_summary_mode = False
76
+ self.grouping_enabled = False
77
+
78
+ if sink_params.grouping:
79
+ self.finding_group_lock = threading.RLock()
80
+ self.grouping_enabled = True
81
+ if sink_params.grouping.notification_mode.summary:
82
+ self.grouping_summary_mode = True
83
+ self.summaries = defaultdict(lambda: NotificationSummary())
84
+ self.summary_header = self.create_summary_header()
85
+ else:
86
+ self.grouping_summary_mode = False
87
+ self.groups = defaultdict(lambda: NotificationGroup())
88
+
89
+ def create_summary_header(self):
90
+ summary_header = []
91
+ for attr in self.params.grouping.notification_mode.summary.by:
92
+ if isinstance(attr, str):
93
+ summary_header.append("notification" if attr == "identifier" else attr)
94
+ elif isinstance(attr, dict):
95
+ keys = list(attr.keys())
96
+ if len(keys) > 1:
97
+ raise ValueError(
98
+ "Invalid sink configuration: multiple values for one of the elements in"
99
+ "grouping.notification_mode.summary.by"
100
+ )
101
+ key = keys[0]
102
+ if key not in ["labels", "annotations"]:
103
+ raise ValueError(
104
+ f"Sink configuration: grouping.notification_mode.summary.by.{key} is invalid "
105
+ "(only labels/annotations allowed)"
106
+ )
107
+ for label_or_attr_name in attr[key]:
108
+ summary_header.append(f"{key[:-1]}:{label_or_attr_name}")
109
+ return summary_header
110
+
111
+ def get_group_key_and_header(self, finding_data: Dict, attributes: List) -> Tuple[KeyT, List[str]]:
112
+ """Generates group key and descriptive header from finding data.
113
+
114
+ For example, for finding_data = {"a":1, "b":2, "x": 3, "y": None} and attributes = ["x", "y"]
115
+ this method will return ((3, "(undefined)"), [])"""
116
+ values = ()
117
+ descriptions = []
118
+ for attr in attributes:
119
+ if isinstance(attr, str):
120
+ if attr not in finding_data:
121
+ continue
122
+ value = finding_data[attr]
123
+ values += (value, )
124
+ descriptions.append(
125
+ f"{'notification' if attr=='identifier' else attr}: {self.display_value(value)}"
126
+ )
127
+ elif isinstance(attr, dict):
128
+ # This is typically for labels and annotations
129
+ top_level_attr_name = list(attr.keys())[0]
130
+ values += tuple(
131
+ finding_data.get(top_level_attr_name, {}).get(element_name)
132
+ for element_name in sorted(attr[top_level_attr_name])
133
+ )
134
+ subvalues = []
135
+ for subattr_name in sorted(attr[top_level_attr_name]):
136
+ subvalues.append((subattr_name, finding_data.get(top_level_attr_name, {}).get(subattr_name)))
137
+ subvalues_str = ", ".join(f"{key}={self.display_value(value)}" for key, value in sorted(subvalues))
138
+ descriptions.append(f"{top_level_attr_name}: {subvalues_str}")
139
+ return values, descriptions
140
+
141
+ def display_value(self, value: Optional[str]) -> str:
142
+ return value if value is not None else "(undefined)"
143
+
144
+ def _build_time_slices_from_params(self, params: ActivityParams):
145
+ if params is None:
146
+ return [TimeSliceAlways()]
147
+ else:
148
+ timezone = params.timezone
149
+ return [self._interval_to_time_slice(timezone, interval) for interval in params.intervals]
150
+
151
+ def _interval_to_time_slice(self, timezone: str, interval: ActivityInterval):
152
+ return TimeSlice(interval.days, [(time.start, time.end) for time in interval.hours], timezone)
153
+
154
+ def is_global_config_changed(self):
155
+ # registry global config can be updated without these stored values being changed
156
+ global_config = self.registry.get_global_config()
157
+ account_id = global_config.get("account_id", "")
158
+ cluster_name = global_config.get("cluster_name", "")
159
+ signing_key = global_config.get("signing_key", "")
160
+ return self.account_id != account_id or self.cluster_name != cluster_name or self.signing_key != signing_key
161
+
162
+ def stop(self):
163
+ pass
164
+
165
+ def accepts(self, finding: Finding) -> bool:
166
+ return (
167
+ finding.matches(self.params.match, self.params.scope)
168
+ and any(time_slice.is_active_now for time_slice in self.time_slices)
169
+ )
170
+
171
+ @abstractmethod
172
+ def write_finding(self, finding: Finding, platform_enabled: bool):
173
+ raise NotImplementedError(f"write_finding not implemented for sink {self.sink_name}")
174
+
175
+ def is_healthy(self) -> bool:
176
+ """
177
+ Sink health check. Concrete sinks can implement real health checks
178
+ """
179
+ return True
180
+
181
+ def handle_service_diff(self, new_obj: Any, operation: K8sOperationType):
182
+ pass
183
+
184
+ def set_cluster_active(self, active: bool):
185
+ pass