robusta-api 0.47.0__tar.gz → 0.48.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.
- {robusta_api-0.47.0 → robusta_api-0.48.0}/PKG-INFO +6 -5
- {robusta_api-0.47.0 → robusta_api-0.48.0}/pyproject.toml +16 -7
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/_version.py +1 -1
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/discovery/discovery.py +3 -2
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/model/env_vars.py +8 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/reporting/utils.py +4 -4
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/sinks/mail/mail_sink.py +4 -4
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/sinks/robusta/dal/supabase_dal.py +106 -5
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/sinks/transformer.py +96 -59
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/integrations/mail/sender.py +4 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/integrations/msteams/msteams_adaptive_card_files_image.py +5 -4
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/runner/config_loader.py +2 -1
- robusta_api-0.48.0/src/robusta/utils/archive.py +54 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/LICENSE +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/__init__.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/api/__init__.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/clients/robusta_client.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/__init__.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/discovery/__init__.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/discovery/resource_names.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/discovery/top_service_resolver.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/discovery/utils.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/exceptions.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/model/__init__.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/model/base_params.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/model/cluster_status.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/model/events.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/model/helm_release.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/model/jobs.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/model/k8s_operation_type.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/model/namespaces.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/model/nodes.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/model/openshift_group.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/model/pods.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/model/runner_config.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/model/services.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/persistency/__init__.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/persistency/in_memory.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/persistency/scheduled_jobs_states_dal.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/playbooks/__init__.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/playbooks/actions_registry.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/playbooks/base_trigger.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/playbooks/common.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/playbooks/container_playbook_utils.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/playbooks/crash_reporter.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/playbooks/generation.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/playbooks/internal/CRD_API_DOCUMENTATION.md +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/playbooks/internal/__init__.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/playbooks/internal/ai_integration.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/playbooks/internal/crds.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/playbooks/internal/discovery_events.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/playbooks/job_utils.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/playbooks/node_enrichment_utils.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/playbooks/node_playbook_utils.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/playbooks/oom_killer_utils.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/playbooks/playbook_utils.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/playbooks/playbooks_event_handler.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/playbooks/playbooks_event_handler_impl.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/playbooks/pod_utils/crashloop_utils.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/playbooks/pod_utils/imagepull_utils.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/playbooks/pod_utils/pending_pod_utils.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/playbooks/prometheus_enrichment_utils.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/playbooks/trigger.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/pubsub/__init__.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/pubsub/event_emitter.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/pubsub/event_subscriber.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/pubsub/events_pubsub.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/reporting/__init__.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/reporting/action_requests.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/reporting/base.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/reporting/blocks.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/reporting/callbacks.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/reporting/consts.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/reporting/custom_rendering.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/reporting/finding_subjects.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/reporting/holmes.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/reporting/url_helpers.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/schedule/__init__.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/schedule/model.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/schedule/scheduler.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/sinks/__init__.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/sinks/common/__init__.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/sinks/common/channel_transformer.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/sinks/common/html_tools.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/sinks/datadog/__init__.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/sinks/datadog/datadog_sink.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/sinks/datadog/datadog_sink_params.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/sinks/discord/__init__.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/sinks/discord/discord_sink.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/sinks/discord/discord_sink_params.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/sinks/file/__init__.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/sinks/file/file_sink.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/sinks/file/file_sink_params.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/sinks/file/object_traverser.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/sinks/google_chat/__init__.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/sinks/google_chat/google_chat.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/sinks/google_chat/google_chat_params.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/sinks/incidentio/__init__.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/sinks/incidentio/incidentio_api.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/sinks/incidentio/incidentio_client.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/sinks/incidentio/incidentio_sink.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/sinks/incidentio/incidentio_sink_params.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/sinks/jira/__init__.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/sinks/jira/jira_sink.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/sinks/jira/jira_sink_params.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/sinks/kafka/__init__.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/sinks/kafka/kafka_sink.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/sinks/kafka/kafka_sink_params.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/sinks/mail/__init__.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/sinks/mail/mail_sink_params.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/sinks/mattermost/__init__.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/sinks/mattermost/mattermost_sink.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/sinks/mattermost/mattermost_sink_params.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/sinks/msteams/__init__.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/sinks/msteams/msteams_sink.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/sinks/msteams/msteams_sink_params.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/sinks/msteams/msteams_webhook_tranformer.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/sinks/opsgenie/__init__.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/sinks/opsgenie/opsgenie_sink.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/sinks/opsgenie/opsgenie_sink_params.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/sinks/pagerduty/__init__.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/sinks/pagerduty/pagerduty_sink.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/sinks/pagerduty/pagerduty_sink_params.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/sinks/pushover/__init__.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/sinks/pushover/pushover_client.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/sinks/pushover/pushover_sink.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/sinks/pushover/pushover_sink_params.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/sinks/robusta/__init__.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/sinks/robusta/dal/__init__.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/sinks/robusta/dal/model_conversion.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/sinks/robusta/discovery_metrics.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/sinks/robusta/prometheus_discovery_utils.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/sinks/robusta/robusta_sink.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/sinks/robusta/robusta_sink_params.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/sinks/robusta/rrm/__init__.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/sinks/robusta/rrm/account_resource_fetcher.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/sinks/robusta/rrm/base_resource_manager.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/sinks/robusta/rrm/prometheus_alert_resource_manager.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/sinks/robusta/rrm/rrm.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/sinks/robusta/rrm/types.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/sinks/rocketchat/__init__.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/sinks/rocketchat/rocketchat_sink.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/sinks/rocketchat/rocketchat_sink_params.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/sinks/servicenow/__init__.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/sinks/servicenow/servicenow_sink.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/sinks/servicenow/servicenow_sink_params.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/sinks/sink_base.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/sinks/sink_base_params.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/sinks/sink_config.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/sinks/sink_factory.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/sinks/slack/__init__.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/sinks/slack/preview/slack_sink_preview.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/sinks/slack/preview/slack_sink_preview_params.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/sinks/slack/slack_sink.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/sinks/slack/slack_sink_params.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/sinks/slack/templates/header.j2 +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/sinks/slack/templates/template_loader.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/sinks/telegram/__init__.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/sinks/telegram/telegram_client.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/sinks/telegram/telegram_sink.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/sinks/telegram/telegram_sink_params.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/sinks/timing.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/sinks/victorops/__init__.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/sinks/victorops/victorops_sink.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/sinks/victorops/victorops_sink_params.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/sinks/webex/__init__.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/sinks/webex/webex_sink.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/sinks/webex/webex_sink_params.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/sinks/webhook/__init__.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/sinks/webhook/webhook_sink.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/sinks/webhook/webhook_sink_params.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/sinks/yamessenger/__init__.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/sinks/yamessenger/yamessenger_client.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/sinks/yamessenger/yamessenger_sink.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/sinks/yamessenger/yamessenger_sink_params.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/sinks/zulip/__init__.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/sinks/zulip/zulip_sink.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/sinks/zulip/zulip_sink_params.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/stream/utils.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/triggers/container_oom_killed_trigger.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/triggers/custom_triggers.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/triggers/error_event_trigger.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/triggers/helm_releases_triggers.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/triggers/job_failed_trigger.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/triggers/multi_resources_trigger.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/triggers/oom_killed_trigger_base.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/triggers/pod_crash_loop_trigger.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/triggers/pod_evicted_trigger.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/triggers/pod_image_pull_backoff.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/triggers/pod_oom_killed_trigger.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/integrations/__init__.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/integrations/argocd/argocd_client.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/integrations/common/__init__.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/integrations/common/requests.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/integrations/discord/__init__.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/integrations/discord/sender.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/integrations/git/__init__.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/integrations/git/git_repo.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/integrations/git/well_known_hosts.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/integrations/google_chat/__init__.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/integrations/google_chat/sender.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/integrations/grafana.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/integrations/helper.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/integrations/jira/__init__.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/integrations/jira/client.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/integrations/jira/sender.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/integrations/kubernetes/__init__.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/integrations/kubernetes/api_client_utils.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/integrations/kubernetes/autogenerated/__init__.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/integrations/kubernetes/autogenerated/events.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/integrations/kubernetes/autogenerated/models.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/integrations/kubernetes/autogenerated/triggers.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/integrations/kubernetes/autogenerated/v1/__init__.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/integrations/kubernetes/autogenerated/v1/models.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/integrations/kubernetes/base_event.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/integrations/kubernetes/base_triggers.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/integrations/kubernetes/custom_crds.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/integrations/kubernetes/custom_models.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/integrations/kubernetes/model_not_found_exception.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/integrations/kubernetes/process_utils.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/integrations/kubernetes/templates.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/integrations/mail/__init__.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/integrations/mattermost/__init__.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/integrations/mattermost/client.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/integrations/mattermost/sender.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/integrations/msteams/__init__.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/integrations/msteams/msteams_adaptive_card_files.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/integrations/msteams/msteams_adaptive_card_files_text.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/integrations/msteams/msteams_elements/__init__.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/integrations/msteams/msteams_elements/msteams_action.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/integrations/msteams/msteams_elements/msteams_base.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/integrations/msteams/msteams_elements/msteams_card.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/integrations/msteams/msteams_elements/msteams_column.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/integrations/msteams/msteams_elements/msteams_container.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/integrations/msteams/msteams_elements/msteams_images.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/integrations/msteams/msteams_elements/msteams_table.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/integrations/msteams/msteams_elements/msteams_text_block.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/integrations/msteams/msteams_mark_down_fix_url.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/integrations/msteams/msteams_msg.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/integrations/msteams/sender.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/integrations/openshift/__init__.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/integrations/openshift/token.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/integrations/prometheus/__init__.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/integrations/prometheus/models.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/integrations/prometheus/trigger.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/integrations/prometheus/utils.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/integrations/receiver.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/integrations/resource_analysis/__init__.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/integrations/resource_analysis/cpu_analyzer.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/integrations/resource_analysis/memory_analyzer.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/integrations/resource_analysis/node_cpu_analyzer.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/integrations/resource_analysis/prometheus_analyzer.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/integrations/rocketchat/__init__.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/integrations/rocketchat/sender.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/integrations/scheduled/__init__.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/integrations/scheduled/event.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/integrations/scheduled/models.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/integrations/scheduled/playbook_scheduler.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/integrations/scheduled/playbook_scheduler_manager.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/integrations/scheduled/playbook_scheduler_manager_impl.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/integrations/scheduled/trigger.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/integrations/servicenow/__init__.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/integrations/servicenow/sender.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/integrations/slack/__init__.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/integrations/slack/sender.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/integrations/webex/__init__.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/integrations/webex/sender.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/integrations/zulip/__init__.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/integrations/zulip/sender.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/model/alert_relabel_config.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/model/config.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/model/playbook_action.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/model/playbook_definition.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/patch/patch.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/runner/__init__.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/runner/log_init.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/runner/main.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/runner/not_found_exception.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/runner/object_updater.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/runner/process_setup.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/runner/ssl_utils.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/runner/telemetry.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/runner/telemetry_service.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/runner/web.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/runner/web_api.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/utils/__init__.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/utils/auth_provider.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/utils/base64_utils.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/utils/cluster_provider_discovery.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/utils/common.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/utils/decorators.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/utils/docs.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/utils/documented_pydantic.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/utils/error_codes.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/utils/file_system_watcher.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/utils/function_hashes.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/utils/json_schema.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/utils/parsing.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/utils/rate_limiter.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/utils/scope.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/utils/server_start.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/utils/service_discovery.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/utils/silence_utils.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/utils/stack_tracer.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/utils/task_queue.py +0 -0
- {robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/utils/time_utils.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: robusta-api
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.48.0
|
|
4
4
|
Summary:
|
|
5
5
|
Author: Arik Alon
|
|
6
6
|
Author-email: arikalon1@users.noreply.github.com
|
|
@@ -9,8 +9,7 @@ Classifier: Programming Language :: Python :: 3
|
|
|
9
9
|
Classifier: Programming Language :: Python :: 3.10
|
|
10
10
|
Classifier: Programming Language :: Python :: 3.11
|
|
11
11
|
Provides-Extra: all
|
|
12
|
-
Requires-Dist:
|
|
13
|
-
Requires-Dist: Flask (>=3.0.0,<4.0.0) ; extra == "all"
|
|
12
|
+
Requires-Dist: Flask (>=3.1.3,<4.0.0) ; extra == "all"
|
|
14
13
|
Requires-Dist: apprise (==1.6.0)
|
|
15
14
|
Requires-Dist: attrs (>=23.1.0,<24.0.0)
|
|
16
15
|
Requires-Dist: better-exceptions (>=0.3.3,<0.4.0) ; extra == "all"
|
|
@@ -21,12 +20,11 @@ Requires-Dist: certifi (>=2026.4.22)
|
|
|
21
20
|
Requires-Dist: click (>=8.3.3)
|
|
22
21
|
Requires-Dist: colorlog (>=5.0.1,<6.0.0)
|
|
23
22
|
Requires-Dist: croniter (>=1.3.15,<2.0.0)
|
|
24
|
-
Requires-Dist: cryptography (>=
|
|
23
|
+
Requires-Dist: cryptography (>=50.0.0,<51.0.0)
|
|
25
24
|
Requires-Dist: datadog-api-client (>=1.2.0,<2.0.0) ; extra == "all"
|
|
26
25
|
Requires-Dist: docutils (==0.19)
|
|
27
26
|
Requires-Dist: dpath (>=2.0.5,<3.0.0)
|
|
28
27
|
Requires-Dist: filelock (>=3.20.3)
|
|
29
|
-
Requires-Dist: fpdf2 (>=2.7.1,<3.0.0)
|
|
30
28
|
Requires-Dist: grafana-api (>=1.0.3,<2.0.0) ; extra == "all"
|
|
31
29
|
Requires-Dist: h2 (>=4.3.0,<5.0.0)
|
|
32
30
|
Requires-Dist: hikaru-model-26 (>=1.1.1,<2.0.0)
|
|
@@ -54,13 +52,16 @@ Requires-Dist: python-json-logger (>=3.0.0,<4.0.0)
|
|
|
54
52
|
Requires-Dist: pytz (>=2021.3,<2022.0)
|
|
55
53
|
Requires-Dist: pyyaml (>=6.0,<7.0)
|
|
56
54
|
Requires-Dist: regex (==2024.5.15)
|
|
55
|
+
Requires-Dist: reportlab (>=5.0.0,<6.0.0)
|
|
57
56
|
Requires-Dist: requests (>=2.33.0,<3.0.0)
|
|
57
|
+
Requires-Dist: resvg-py (==0.3.4)
|
|
58
58
|
Requires-Dist: rocketchat-api (>=1.30.0,<2.0.0)
|
|
59
59
|
Requires-Dist: sentry-sdk[flask] (>=2.13.0,<3.0.0) ; extra == "all"
|
|
60
60
|
Requires-Dist: setuptools (>=80.9.0,<81.0.0)
|
|
61
61
|
Requires-Dist: slack-sdk (>=3.23.0,<4.0.0)
|
|
62
62
|
Requires-Dist: supabase (==2.5.1) ; extra == "all"
|
|
63
63
|
Requires-Dist: tabulate (>=0.8.10,<0.9.0) ; extra == "all"
|
|
64
|
+
Requires-Dist: tenacity (>=9.1.2,<10.0.0)
|
|
64
65
|
Requires-Dist: toml (>=0.10.2,<0.11.0)
|
|
65
66
|
Requires-Dist: urllib3 (>=2.7.0,<3.0.0)
|
|
66
67
|
Requires-Dist: virtualenv (>=20.36.1,<21.0.0)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "robusta-api"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.48.0"
|
|
4
4
|
description = ""
|
|
5
5
|
authors = ["Arik Alon <arikalon1@users.noreply.github.com>"]
|
|
6
6
|
packages = [
|
|
@@ -30,13 +30,17 @@ idna= { version = ">=3.15", optional = true }
|
|
|
30
30
|
supabase = "2.5.1"
|
|
31
31
|
tabulate = { version = "^0.8.10" }
|
|
32
32
|
slack-sdk = { version = "^3.23.0" }
|
|
33
|
-
Flask = { version = "^3.
|
|
33
|
+
Flask = { version = "^3.1.3", optional = true }
|
|
34
34
|
werkzeug = { version = ">=3.1.6", optional = true }
|
|
35
35
|
jinja2 = { version = ">=3.1.5", optional = true }
|
|
36
36
|
grafana-api = { version = "^1.0.3", optional = true }
|
|
37
37
|
watchdog = { version = "^2.1.0", optional = true }
|
|
38
38
|
better-exceptions = { version = "^0.3.3", optional = true }
|
|
39
|
-
|
|
39
|
+
# SVG -> PNG rasterizer for charts sent to chat sinks (resvg is Apache-2.0 OR MIT,
|
|
40
|
+
# bindings are MIT; replaced LGPL-licensed CairoSVG)
|
|
41
|
+
# Pinned to an exact version for supply-chain safety since the binding package
|
|
42
|
+
# has a small maintainer base (see PR #2136 review)
|
|
43
|
+
resvg-py = "0.3.4"
|
|
40
44
|
# Bump to fix CVE-2026-10142, CVE-2026-10143 (High): kafka-python protocol-parser DoS
|
|
41
45
|
kafka-python = { version = "^2.3.2", optional = true }
|
|
42
46
|
datadog-api-client = { version = "^1.2.0", optional = true }
|
|
@@ -60,10 +64,12 @@ humanize = "^3.13.1"
|
|
|
60
64
|
# we're freezing a specific version here because the latest version doesn't have prebuilt wheels on pypi
|
|
61
65
|
# and therefore requires gcc to install which we'd like to avoid
|
|
62
66
|
# this is similar to the issue at https://github.com/dulwich/dulwich/issues/839
|
|
67
|
+
# Pin to fix CVE-2026-69247 (High): cryptography Bleichenbacher oracle in PKCS#7 decryption
|
|
63
68
|
# Pin to fix CVE-2026-39892 (Critical): cryptography buffer overflow with non-contiguous buffers
|
|
64
69
|
# Pin to fix GHSA-537c-gmf6-5ccf (High): cryptography
|
|
65
|
-
cryptography = "^
|
|
66
|
-
|
|
70
|
+
cryptography = "^50.0.0"
|
|
71
|
+
# PDF renderer for KRR/Popeye scan reports (BSD-3-Clause; replaced LGPL-licensed fpdf2)
|
|
72
|
+
reportlab = "^5.0.0"
|
|
67
73
|
attrs = "^23.1.0"
|
|
68
74
|
prometrix = "0.2.11"
|
|
69
75
|
hikaru-model-26 = "^1.1.1"
|
|
@@ -92,13 +98,16 @@ pyasn1 = ">=0.6.4"
|
|
|
92
98
|
filelock = ">=3.20.3"
|
|
93
99
|
# Pin to fix CVE-2026-22702 (transitive via pre-commit)
|
|
94
100
|
virtualenv = "^20.36.1"
|
|
95
|
-
# Pillow is
|
|
101
|
+
# Pillow is used directly (MS Teams PNG->JPEG image conversion) and by reportlab.
|
|
102
|
+
# Pin to fix CVE-2026-54058,
|
|
96
103
|
# CVE-2026-54059, CVE-2026-54060, CVE-2026-55379, CVE-2026-55380, CVE-2026-59197,
|
|
97
104
|
# CVE-2026-59199, CVE-2026-59200, CVE-2026-59204, CVE-2026-59205 (High)
|
|
98
105
|
pillow = "^12.3.0"
|
|
99
106
|
# click is pulled in transitively (flask, typer). Pin to fix CVE-2026-7246 (High):
|
|
100
107
|
# command injection via unsanitized filename in click.edit()
|
|
101
108
|
click = ">=8.3.3"
|
|
109
|
+
# Used directly for the Supabase login retry (ROB-1228); was only transitive via opsgenie-sdk
|
|
110
|
+
tenacity = "^9.1.2"
|
|
102
111
|
|
|
103
112
|
[tool.poetry.dev-dependencies]
|
|
104
113
|
pre-commit = "^2.13.0"
|
|
@@ -138,7 +147,7 @@ types-toml = "^0.10.2"
|
|
|
138
147
|
types-tabulate = "^0.8.10"
|
|
139
148
|
|
|
140
149
|
[tool.poetry.extras]
|
|
141
|
-
all = ["Flask", "grafana-api", "watchdog", "better-exceptions", "
|
|
150
|
+
all = ["Flask", "grafana-api", "watchdog", "better-exceptions", "tabulate", "kafka-python", "prometheus-api-client", "supabase", "datadog-api-client", "sentry-sdk", "poetry-core"]
|
|
142
151
|
|
|
143
152
|
[tool.poetry.group.dev.dependencies]
|
|
144
153
|
sphinx-jinja = { git = "https://github.com/robusta-dev/sphinx-jinja.git" }
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
# this is updated by .github/workflows/release.yaml
|
|
2
|
-
__version__ = "0.
|
|
2
|
+
__version__ = "0.48.0"
|
|
@@ -897,12 +897,13 @@ def extract_containers_k8(resource) -> List[Container]:
|
|
|
897
897
|
|
|
898
898
|
|
|
899
899
|
def is_pod_ready(pod) -> bool:
|
|
900
|
+
# Unset until kubelet reports on the pod.
|
|
900
901
|
conditions = []
|
|
901
902
|
if isinstance(pod, V1Pod):
|
|
902
|
-
conditions = pod.status
|
|
903
|
+
conditions = getattr(pod.status, "conditions", None) or []
|
|
903
904
|
|
|
904
905
|
if isinstance(pod, Pod):
|
|
905
|
-
conditions = pod.status
|
|
906
|
+
conditions = getattr(pod.status, "conditions", None) or []
|
|
906
907
|
|
|
907
908
|
for condition in conditions:
|
|
908
909
|
if condition.type == "Ready":
|
|
@@ -28,6 +28,14 @@ DISCOVERY_CHECK_THRESHOLD_SEC = int(os.environ.get("DISCOVERY_CHECK_THRESHOLD_SE
|
|
|
28
28
|
DISCOVERY_PROCESS_TIMEOUT_SEC = int(os.environ.get("DISCOVERY_PROCESS_TIMEOUT_SEC", 60 * 120)) # 120 min
|
|
29
29
|
DISCOVERY_WATCHDOG_CHECK_SEC = int(os.environ.get("DISCOVERY_WATCHDOG_CHECK_SEC", 15 * 120)) # 15 min
|
|
30
30
|
SUPABASE_TIMEOUT_SECONDS = int(os.environ.get("SUPABASE_TIMEOUT_SECONDS", 60))
|
|
31
|
+
# Transient network failures on the Supabase login used to kill the runner (ROB-1228).
|
|
32
|
+
SUPABASE_LOGIN_RETRIES = int(os.environ.get("SUPABASE_LOGIN_RETRIES", 4))
|
|
33
|
+
SUPABASE_LOGIN_RETRY_BACKOFF_SEC = float(os.environ.get("SUPABASE_LOGIN_RETRY_BACKOFF_SEC", 2))
|
|
34
|
+
SUPABASE_LOGIN_RETRY_MAX_BACKOFF_SEC = float(os.environ.get("SUPABASE_LOGIN_RETRY_MAX_BACKOFF_SEC", 30))
|
|
35
|
+
# A TCP handshake that has not completed in this many seconds is not going to. Capped
|
|
36
|
+
# separately from SUPABASE_TIMEOUT_SECONDS so an unreachable platform fails (and retries)
|
|
37
|
+
# promptly instead of parking every attempt on the full read budget.
|
|
38
|
+
SUPABASE_CONNECT_TIMEOUT_SECONDS = float(os.environ.get("SUPABASE_CONNECT_TIMEOUT_SECONDS", 10))
|
|
31
39
|
GRAFANA_RENDERER_URL = os.environ.get("GRAFANA_RENDERER_URL", "http://127.0.0.1:8281/render")
|
|
32
40
|
RESOURCE_UPDATES_CACHE_TTL_SEC = os.environ.get("RESOURCE_UPDATES_CACHE_TTL_SEC", 120)
|
|
33
41
|
INTERNAL_PLAYBOOKS_ROOT = os.environ.get("INTERNAL_PLAYBOOKS_ROOT", "/app/src/robusta/core/playbooks/internal")
|
|
@@ -21,12 +21,12 @@ def is_image(file_name: str):
|
|
|
21
21
|
|
|
22
22
|
|
|
23
23
|
def convert_svg_to_png(svg: bytes) -> Optional[bytes]:
|
|
24
|
-
# we import
|
|
25
|
-
#
|
|
26
|
-
import
|
|
24
|
+
# we import resvg_py here and not globally so the rasterizer is only loaded on
|
|
25
|
+
# the SVG-conversion path, and environments without it can still import this module
|
|
26
|
+
import resvg_py
|
|
27
27
|
|
|
28
28
|
try:
|
|
29
|
-
return
|
|
29
|
+
return resvg_py.svg_to_bytes(svg_string=svg.decode("utf-8"))
|
|
30
30
|
except Exception:
|
|
31
31
|
logging.error(f"error converting svg to png; svg={svg}")
|
|
32
32
|
return None
|
|
@@ -10,10 +10,10 @@ class MailSink(SinkBase):
|
|
|
10
10
|
params = sink_config.mail_sink
|
|
11
11
|
|
|
12
12
|
self.sender = MailSender(
|
|
13
|
-
params.mailto,
|
|
14
|
-
self.
|
|
15
|
-
self.
|
|
16
|
-
self.
|
|
13
|
+
mailto=params.mailto,
|
|
14
|
+
account_id=self.account_id,
|
|
15
|
+
cluster_name=self.cluster_name,
|
|
16
|
+
signing_key=self.signing_key,
|
|
17
17
|
use_ses=params.use_ses,
|
|
18
18
|
aws_region=params.aws_region,
|
|
19
19
|
from_email=params.from_email,
|
{robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/sinks/robusta/dal/supabase_dal.py
RENAMED
|
@@ -7,7 +7,9 @@ import threading
|
|
|
7
7
|
from typing import Any, Dict, List, Optional, Tuple
|
|
8
8
|
from uuid import uuid4
|
|
9
9
|
from cachetools import TTLCache
|
|
10
|
+
import httpx
|
|
10
11
|
import requests
|
|
12
|
+
from gotrue.errors import AuthRetryableError
|
|
11
13
|
from postgrest._sync.request_builder import SyncQueryRequestBuilder
|
|
12
14
|
from postgrest.base_request_builder import BaseFilterRequestBuilder, QueryArgs
|
|
13
15
|
from postgrest.exceptions import APIError as PostgrestAPIError
|
|
@@ -15,10 +17,17 @@ from postgrest.types import ReturnMethod
|
|
|
15
17
|
from postgrest.utils import sanitize_param
|
|
16
18
|
from supabase import create_client
|
|
17
19
|
from supabase.lib.client_options import ClientOptions
|
|
20
|
+
from tenacity import RetryCallState, Retrying, retry_if_exception_type, stop_after_attempt, wait_exponential
|
|
18
21
|
|
|
19
22
|
from robusta.core.model.cluster_status import ClusterStatus
|
|
20
23
|
from robusta.core.exceptions import SupabaseDnsException
|
|
21
|
-
from robusta.core.model.env_vars import
|
|
24
|
+
from robusta.core.model.env_vars import (
|
|
25
|
+
SUPABASE_CONNECT_TIMEOUT_SECONDS,
|
|
26
|
+
SUPABASE_LOGIN_RETRIES,
|
|
27
|
+
SUPABASE_LOGIN_RETRY_BACKOFF_SEC,
|
|
28
|
+
SUPABASE_LOGIN_RETRY_MAX_BACKOFF_SEC,
|
|
29
|
+
SUPABASE_TIMEOUT_SECONDS,
|
|
30
|
+
)
|
|
22
31
|
from robusta.core.model.helm_release import HelmRelease
|
|
23
32
|
from robusta.core.model.jobs import JobInfo
|
|
24
33
|
from robusta.core.model.namespaces import NamespaceInfo
|
|
@@ -69,6 +78,17 @@ def pre_select_patched(*args, **kwargs) -> QueryArgs:
|
|
|
69
78
|
supabase_request_builder.pre_select = pre_select_patched
|
|
70
79
|
|
|
71
80
|
|
|
81
|
+
class _AuthHttpClient(httpx.Client):
|
|
82
|
+
"""The httpx client we hand to Supabase's auth (GoTrue) sub-client.
|
|
83
|
+
|
|
84
|
+
Mirrors ``gotrue.http_clients.SyncClient``, whose only addition over ``httpx.Client``
|
|
85
|
+
is the ``aclose()`` alias that GoTrue's ``close()`` calls.
|
|
86
|
+
"""
|
|
87
|
+
|
|
88
|
+
def aclose(self) -> None:
|
|
89
|
+
self.close()
|
|
90
|
+
|
|
91
|
+
|
|
72
92
|
class SupabaseDal(AccountResourceFetcher):
|
|
73
93
|
def __init__(
|
|
74
94
|
self,
|
|
@@ -92,6 +112,7 @@ class SupabaseDal(AccountResourceFetcher):
|
|
|
92
112
|
self.cluster = cluster_name
|
|
93
113
|
options = ClientOptions(postgrest_client_timeout=SUPABASE_TIMEOUT_SECONDS, auto_refresh_token=True)
|
|
94
114
|
self.client = create_client(url, key, options)
|
|
115
|
+
self.__apply_auth_client_timeout()
|
|
95
116
|
self.patch_postgrest_execute()
|
|
96
117
|
self.email = email
|
|
97
118
|
self.password = password
|
|
@@ -104,6 +125,53 @@ class SupabaseDal(AccountResourceFetcher):
|
|
|
104
125
|
self.token_cache = TTLCache(maxsize=1, ttl=ttl)
|
|
105
126
|
self.lock = threading.Lock()
|
|
106
127
|
|
|
128
|
+
def __apply_auth_client_timeout(self) -> None:
|
|
129
|
+
"""Give the auth (GoTrue) client the configured timeout, over HTTP/1.1.
|
|
130
|
+
|
|
131
|
+
``ClientOptions.postgrest_client_timeout`` only reaches postgrest. supabase 2.5.1
|
|
132
|
+
builds the auth client with neither a timeout nor an http client of its own, so
|
|
133
|
+
logins ran on httpx's 5s default - and because ``socket.create_connection`` applies
|
|
134
|
+
the connect timeout once per resolved address, a dual-stack host gave up after ~10s
|
|
135
|
+
no matter what SUPABASE_TIMEOUT_SECONDS said (ROB-1228). supabase only grew a
|
|
136
|
+
supported hook for this in 2.28 (``ClientOptions(httpx_client=...)``), so until we
|
|
137
|
+
bump, replace the client on the instance.
|
|
138
|
+
|
|
139
|
+
Connect is capped below the read budget (SUPABASE_CONNECT_TIMEOUT_SECONDS): with a
|
|
140
|
+
60s flat timeout, every attempt against a firewalled platform parks for a full
|
|
141
|
+
minute before the retry, so the actionable error takes minutes to appear.
|
|
142
|
+
|
|
143
|
+
HTTP/2 is dropped at the same time: httpcore's sync HTTP/2 connection is not thread
|
|
144
|
+
safe, and this DAL is shared across threads (the same problem as ROB-228). No
|
|
145
|
+
transport is passed, so environment proxies keep being honored as before.
|
|
146
|
+
"""
|
|
147
|
+
auth_client = self.client.auth
|
|
148
|
+
http_client = _AuthHttpClient(
|
|
149
|
+
timeout=httpx.Timeout(
|
|
150
|
+
SUPABASE_TIMEOUT_SECONDS,
|
|
151
|
+
connect=min(SUPABASE_CONNECT_TIMEOUT_SECONDS, SUPABASE_TIMEOUT_SECONDS),
|
|
152
|
+
),
|
|
153
|
+
follow_redirects=True,
|
|
154
|
+
http2=False,
|
|
155
|
+
)
|
|
156
|
+
try:
|
|
157
|
+
previous = auth_client._http_client
|
|
158
|
+
# the admin sub-API was handed the original client at construction time
|
|
159
|
+
admin = auth_client.admin
|
|
160
|
+
except AttributeError:
|
|
161
|
+
# A supabase/gotrue bump moved the internals. Keep their default client rather
|
|
162
|
+
# than failing startup over it, but say so: auth calls stay on httpx defaults.
|
|
163
|
+
http_client.close()
|
|
164
|
+
logging.warning(
|
|
165
|
+
"Could not apply SUPABASE_TIMEOUT_SECONDS to the Supabase auth client - its internals changed. "
|
|
166
|
+
"Auth calls will use the httpx default timeout.",
|
|
167
|
+
exc_info=True,
|
|
168
|
+
)
|
|
169
|
+
return
|
|
170
|
+
|
|
171
|
+
auth_client._http_client = http_client
|
|
172
|
+
admin._http_client = http_client
|
|
173
|
+
previous.close()
|
|
174
|
+
|
|
107
175
|
def patch_postgrest_execute(self):
|
|
108
176
|
# This is somewhat hacky.
|
|
109
177
|
def execute_with_retry(_self):
|
|
@@ -553,13 +621,46 @@ class SupabaseDal(AccountResourceFetcher):
|
|
|
553
621
|
logging.error(f"Failed to persist helm_releases {helm_releases} error: {e}")
|
|
554
622
|
raise
|
|
555
623
|
|
|
624
|
+
def __sign_in_once(self) -> str:
|
|
625
|
+
res = self.client.auth.sign_in_with_password({"email": self.email, "password": self.password})
|
|
626
|
+
self.client.auth.set_session(res.session.access_token, res.session.refresh_token)
|
|
627
|
+
self.client.postgrest.auth(res.session.access_token)
|
|
628
|
+
return res.user.id
|
|
629
|
+
|
|
630
|
+
@staticmethod
|
|
631
|
+
def __log_sign_in_retry(retry_state: RetryCallState) -> None:
|
|
632
|
+
logging.warning(
|
|
633
|
+
"Supabase login attempt %s/%s failed: %s. Retrying in %.1fs",
|
|
634
|
+
retry_state.attempt_number,
|
|
635
|
+
SUPABASE_LOGIN_RETRIES,
|
|
636
|
+
retry_state.outcome.exception() if retry_state.outcome else "unknown error",
|
|
637
|
+
retry_state.next_action.sleep if retry_state.next_action else 0,
|
|
638
|
+
)
|
|
639
|
+
|
|
556
640
|
def sign_in(self) -> str:
|
|
641
|
+
"""Log in to Supabase, retrying transient network failures.
|
|
642
|
+
|
|
643
|
+
A single failure here used to be fatal: it propagates out of ``__init__`` through
|
|
644
|
+
Robusta sink construction up to ConfigLoader, which kills the whole runner process
|
|
645
|
+
group - so one connect timeout at pod start put the runner into CrashLoopBackOff,
|
|
646
|
+
with no in-process recovery path (ROB-1228). GoTrue labels these errors "retryable"
|
|
647
|
+
(``AuthRetryableError``); honor that instead of dying. DNS failures are retried too:
|
|
648
|
+
cluster DNS is often not ready yet at the moment the runner starts. A wrong
|
|
649
|
+
password or any other 4xx is an ``AuthApiError``, so it still fails immediately.
|
|
650
|
+
"""
|
|
557
651
|
logging.info("Supabase dal login")
|
|
558
652
|
try:
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
653
|
+
login = Retrying(
|
|
654
|
+
stop=stop_after_attempt(max(1, SUPABASE_LOGIN_RETRIES)),
|
|
655
|
+
wait=wait_exponential(
|
|
656
|
+
multiplier=SUPABASE_LOGIN_RETRY_BACKOFF_SEC,
|
|
657
|
+
max=SUPABASE_LOGIN_RETRY_MAX_BACKOFF_SEC,
|
|
658
|
+
),
|
|
659
|
+
retry=retry_if_exception_type((AuthRetryableError, httpx.TransportError)),
|
|
660
|
+
before_sleep=self.__log_sign_in_retry,
|
|
661
|
+
reraise=True,
|
|
662
|
+
)
|
|
663
|
+
return login(self.__sign_in_once)
|
|
563
664
|
except Exception as e:
|
|
564
665
|
# Check if this is a DNS-related error
|
|
565
666
|
error_msg = str(e).lower()
|
|
@@ -4,8 +4,6 @@ import urllib.parse
|
|
|
4
4
|
from typing import List, Optional, Union
|
|
5
5
|
|
|
6
6
|
import markdown2
|
|
7
|
-
from fpdf import FPDF
|
|
8
|
-
from fpdf.fonts import FontFace
|
|
9
7
|
|
|
10
8
|
try:
|
|
11
9
|
from tabulate import tabulate
|
|
@@ -233,67 +231,106 @@ class Transformer:
|
|
|
233
231
|
if not isinstance(block, ScanReportBlock):
|
|
234
232
|
return block
|
|
235
233
|
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
def
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
def write_table(pdf: FPDF, rows: List[list[str]]):
|
|
269
|
-
pdf.set_draw_color(table_color)
|
|
270
|
-
set_normal_test_style(pdf)
|
|
271
|
-
with pdf.table(
|
|
272
|
-
borders_layout="INTERNAL",
|
|
273
|
-
rows=rows,
|
|
274
|
-
headings_style=FontFace(color=(headers_color)),
|
|
275
|
-
col_widths=scan.table_widths,
|
|
276
|
-
markdown=True,
|
|
277
|
-
line_height=1.5 * pdf.font_size,
|
|
278
|
-
):
|
|
279
|
-
pass
|
|
234
|
+
# reportlab is imported lazily so the PDF toolchain is only required on the
|
|
235
|
+
# scan-report path, not for merely importing the sink layer
|
|
236
|
+
from io import BytesIO
|
|
237
|
+
|
|
238
|
+
from reportlab.lib import colors
|
|
239
|
+
from reportlab.lib.pagesizes import A4, landscape
|
|
240
|
+
from reportlab.lib.styles import ParagraphStyle
|
|
241
|
+
from reportlab.platypus import Paragraph, SimpleDocTemplate, Spacer, Table, TableStyle
|
|
242
|
+
|
|
243
|
+
accent_color = colors.Color(140 / 255, 249 / 255, 209 / 255)
|
|
244
|
+
headers_color = colors.Color(63 / 255, 63 / 255, 63 / 255)
|
|
245
|
+
table_color = colors.Color(207 / 255, 215 / 255, 216 / 255)
|
|
246
|
+
|
|
247
|
+
title_style = ParagraphStyle("scan-title", fontName="Courier", fontSize=18, leading=22)
|
|
248
|
+
config_label_style = ParagraphStyle(
|
|
249
|
+
"scan-config-label", fontName="Courier", fontSize=18, leading=22, textColor=accent_color
|
|
250
|
+
)
|
|
251
|
+
section_style = ParagraphStyle(
|
|
252
|
+
"scan-section", fontName="Courier-Bold", fontSize=12, leading=14, textColor=headers_color
|
|
253
|
+
)
|
|
254
|
+
normal_style = ParagraphStyle("scan-normal", fontName="Courier", fontSize=8, leading=12)
|
|
255
|
+
heading_cell_style = ParagraphStyle(
|
|
256
|
+
"scan-heading-cell", fontName="Courier", fontSize=8, leading=12, textColor=headers_color
|
|
257
|
+
)
|
|
258
|
+
|
|
259
|
+
def cell_markup(text) -> str:
|
|
260
|
+
"""Escape a table cell and translate the markdown supported in scan cells
|
|
261
|
+
(**bold** and newlines) to paragraph markup."""
|
|
262
|
+
escaped = str(text).replace("&", "&").replace("<", "<").replace(">", ">")
|
|
263
|
+
bolded = re.sub(r"\*\*(.+?)\*\*", r"<b>\1</b>", escaped, flags=re.DOTALL)
|
|
264
|
+
return bolded.replace("\n", "<br/>")
|
|
280
265
|
|
|
281
266
|
scan: ScanReportBlock = block
|
|
282
267
|
scan_headers = scan.table_headers
|
|
283
268
|
scan_data = scan.table_data
|
|
284
|
-
pdf = FPDF(orientation="landscape", format="A4")
|
|
285
|
-
pdf.add_page()
|
|
286
|
-
pdf.set_font("courier", "", 18)
|
|
287
|
-
pdf.set_line_width(0.1)
|
|
288
|
-
pdf.c_margin = 2 # create default cell margin to add table "padding"
|
|
289
|
-
|
|
290
269
|
title = f"{scan.type.capitalize()} report"
|
|
291
|
-
write_report_header(title, scan.end_time, scan.score, scan.grade)
|
|
292
|
-
write_config(pdf, scan.config)
|
|
293
270
|
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
271
|
+
story = []
|
|
272
|
+
|
|
273
|
+
header_cells = [Paragraph(f"<b>{cell_markup(title)}</b> {scan.end_time.strftime('%b %d, %y %X')}", title_style)]
|
|
274
|
+
header_widths = [0.7]
|
|
275
|
+
try:
|
|
276
|
+
# scan.grade also parses the score, so it must stay inside this guard
|
|
277
|
+
if int(scan.score) >= 0:
|
|
278
|
+
header_cells.append(Paragraph(f"<b>{scan.grade}</b> {scan.score}", title_style))
|
|
279
|
+
header_widths.append(0.3)
|
|
280
|
+
except (TypeError, ValueError):
|
|
281
|
+
logging.warning(f"scan report has a non-integer score {scan.score!r}; skipping the score badge")
|
|
282
|
+
|
|
283
|
+
page_width, _ = landscape(A4)
|
|
284
|
+
margin = 28 # ~10mm, matching the previous layout
|
|
285
|
+
content_width = page_width - 2 * margin
|
|
286
|
+
story.append(Table([header_cells], colWidths=[w * content_width for w in header_widths]))
|
|
287
|
+
story.append(Spacer(1, 20))
|
|
288
|
+
|
|
289
|
+
story.append(Paragraph("config", config_label_style))
|
|
290
|
+
story.append(Spacer(1, 6))
|
|
291
|
+
story.append(Paragraph(cell_markup(scan.config), normal_style))
|
|
298
292
|
|
|
299
|
-
|
|
293
|
+
for section_name, section_data in scan_data.items():
|
|
294
|
+
story.append(Spacer(1, 12))
|
|
295
|
+
story.append(Paragraph(cell_markup(section_name), section_style))
|
|
296
|
+
story.append(Spacer(1, 6))
|
|
297
|
+
|
|
298
|
+
heading_row = [Paragraph(cell_markup(header), heading_cell_style) for header in scan_headers]
|
|
299
|
+
body_rows = [
|
|
300
|
+
[Paragraph(cell_markup(cell), normal_style) for cell in row] for row in section_data
|
|
301
|
+
]
|
|
302
|
+
width_total = sum(scan.table_widths)
|
|
303
|
+
column_widths = [w / width_total * content_width for w in scan.table_widths]
|
|
304
|
+
# splitInRow lets a single row taller than the page (e.g. a Popeye resource
|
|
305
|
+
# with very many issues in one cell) split across pages instead of raising
|
|
306
|
+
table = Table(
|
|
307
|
+
[heading_row, *body_rows],
|
|
308
|
+
colWidths=column_widths,
|
|
309
|
+
repeatRows=1,
|
|
310
|
+
splitByRow=1,
|
|
311
|
+
splitInRow=1,
|
|
312
|
+
)
|
|
313
|
+
table.setStyle(
|
|
314
|
+
TableStyle(
|
|
315
|
+
[
|
|
316
|
+
("INNERGRID", (0, 0), (-1, -1), 0.3, table_color),
|
|
317
|
+
("VALIGN", (0, 0), (-1, -1), "TOP"),
|
|
318
|
+
("LEFTPADDING", (0, 0), (-1, -1), 6),
|
|
319
|
+
("RIGHTPADDING", (0, 0), (-1, -1), 6),
|
|
320
|
+
]
|
|
321
|
+
)
|
|
322
|
+
)
|
|
323
|
+
story.append(table)
|
|
324
|
+
|
|
325
|
+
buffer = BytesIO()
|
|
326
|
+
doc = SimpleDocTemplate(
|
|
327
|
+
buffer,
|
|
328
|
+
pagesize=landscape(A4),
|
|
329
|
+
leftMargin=margin,
|
|
330
|
+
rightMargin=margin,
|
|
331
|
+
topMargin=margin,
|
|
332
|
+
bottomMargin=margin,
|
|
333
|
+
title=title,
|
|
334
|
+
)
|
|
335
|
+
doc.build(story)
|
|
336
|
+
return FileBlock(f"{title}.pdf", buffer.getvalue())
|
|
@@ -23,8 +23,12 @@ from robusta.core.sinks.transformer import Transformer
|
|
|
23
23
|
|
|
24
24
|
|
|
25
25
|
class MailSender(HTMLBaseSender):
|
|
26
|
+
# Keyword-only: account_id, cluster_name and signing_key are all plain strings, so a
|
|
27
|
+
# positional call that gets their order wrong silently puts the signing key into the
|
|
28
|
+
# `account` query parameter of the platform links we email out.
|
|
26
29
|
def __init__(
|
|
27
30
|
self,
|
|
31
|
+
*,
|
|
28
32
|
mailto: str,
|
|
29
33
|
account_id: str,
|
|
30
34
|
cluster_name: str,
|
|
@@ -5,7 +5,7 @@ import uuid
|
|
|
5
5
|
from typing import List
|
|
6
6
|
|
|
7
7
|
from robusta.core.reporting.blocks import FileBlock
|
|
8
|
-
from robusta.core.reporting.utils import JPG_SUFFIX, PNG_SUFFIX, file_suffix_match, is_image
|
|
8
|
+
from robusta.core.reporting.utils import JPG_SUFFIX, PNG_SUFFIX, convert_svg_to_png, file_suffix_match, is_image
|
|
9
9
|
from robusta.integrations.msteams.msteams_elements.msteams_images import MsTeamsImages
|
|
10
10
|
|
|
11
11
|
|
|
@@ -64,6 +64,7 @@ class MsTeamsAdaptiveCardFilesImage:
|
|
|
64
64
|
# msteams cant read parsing of url to svg image
|
|
65
65
|
@classmethod
|
|
66
66
|
def __svg_convert_bytes_to_jpg(cls, svg_bytes: bytes):
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
67
|
+
png_bytes = convert_svg_to_png(svg_bytes)
|
|
68
|
+
if png_bytes is None:
|
|
69
|
+
raise ValueError("failed to convert svg to png for msteams image")
|
|
70
|
+
return cls.__png_convert_bytes_to_base_64_url(png_bytes)
|
|
@@ -41,6 +41,7 @@ from robusta.integrations.scheduled.playbook_scheduler_manager_impl import Playb
|
|
|
41
41
|
from robusta.integrations.scheduled.trigger import ScheduledTriggerEvent
|
|
42
42
|
from robusta.model.config import PlaybooksRegistry, PlaybooksRegistryImpl, Registry, SinksRegistry
|
|
43
43
|
from robusta.model.playbook_definition import PlaybookDefinition
|
|
44
|
+
from robusta.utils.archive import safe_extract_tar
|
|
44
45
|
from robusta.utils.cluster_provider_discovery import cluster_provider
|
|
45
46
|
from robusta.utils.file_system_watcher import FileSystemWatcher
|
|
46
47
|
from robusta.core.exceptions import SupabaseDnsException
|
|
@@ -354,7 +355,7 @@ class ConfigLoader:
|
|
|
354
355
|
f.flush()
|
|
355
356
|
|
|
356
357
|
with tarfile.open(f.name, "r:gz") as tar, tempfile.TemporaryDirectory() as temp_dir:
|
|
357
|
-
tar
|
|
358
|
+
safe_extract_tar(tar, temp_dir)
|
|
358
359
|
extracted_items = os.listdir(temp_dir)
|
|
359
360
|
|
|
360
361
|
pkg_path = temp_dir
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import tarfile
|
|
3
|
+
from typing import List
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class UnsafeArchiveError(Exception):
|
|
7
|
+
"""Raised when a tar archive contains a member that would be extracted outside the target directory."""
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def _is_within(directory: str, target: str) -> bool:
|
|
11
|
+
directory = os.path.realpath(directory)
|
|
12
|
+
target = os.path.realpath(target)
|
|
13
|
+
return target == directory or target.startswith(directory + os.sep)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def _validate_members(tar: tarfile.TarFile, path: str) -> List[tarfile.TarInfo]:
|
|
17
|
+
members = tar.getmembers()
|
|
18
|
+
for member in members:
|
|
19
|
+
if member.isdev():
|
|
20
|
+
raise UnsafeArchiveError(f"Archive contains a device/fifo member: {member.name}")
|
|
21
|
+
|
|
22
|
+
if os.path.isabs(member.name) or member.name.startswith("/"):
|
|
23
|
+
raise UnsafeArchiveError(f"Archive contains a member with an absolute path: {member.name}")
|
|
24
|
+
|
|
25
|
+
if not _is_within(path, os.path.join(path, member.name)):
|
|
26
|
+
raise UnsafeArchiveError(f"Archive contains a member escaping the target directory: {member.name}")
|
|
27
|
+
|
|
28
|
+
if member.issym() or member.islnk():
|
|
29
|
+
# link targets are resolved relative to the directory holding the link
|
|
30
|
+
link_base = os.path.join(path, os.path.dirname(member.name))
|
|
31
|
+
if os.path.isabs(member.linkname) or not _is_within(path, os.path.join(link_base, member.linkname)):
|
|
32
|
+
raise UnsafeArchiveError(
|
|
33
|
+
f"Archive contains a link escaping the target directory: {member.name} -> {member.linkname}"
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
return members
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def safe_extract_tar(tar: tarfile.TarFile, path: str) -> None:
|
|
40
|
+
"""
|
|
41
|
+
Extract a tar archive into ``path``, refusing archives whose members would be written outside of it.
|
|
42
|
+
|
|
43
|
+
Guards against path traversal ("zip slip"), absolute member paths, escaping symlinks/hardlinks and
|
|
44
|
+
device nodes. Raises ``UnsafeArchiveError`` without extracting anything if the archive is unsafe.
|
|
45
|
+
"""
|
|
46
|
+
if hasattr(tarfile, "data_filter"): # python 3.12+, and 3.10.12+/3.11.4+ backports
|
|
47
|
+
# pre-validate so that nothing is written before an unsafe member is detected
|
|
48
|
+
_validate_members(tar, path)
|
|
49
|
+
try:
|
|
50
|
+
tar.extractall(path=path, filter="data")
|
|
51
|
+
except tarfile.FilterError as e:
|
|
52
|
+
raise UnsafeArchiveError(f"Refusing to extract unsafe archive: {e}") from e
|
|
53
|
+
else:
|
|
54
|
+
tar.extractall(path=path, members=_validate_members(tar, path))
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{robusta_api-0.47.0 → robusta_api-0.48.0}/src/robusta/core/discovery/top_service_resolver.py
RENAMED
|
File without changes
|