diff options
Diffstat (limited to 'app/models/hooks')
-rw-r--r-- | app/models/hooks/project_hook.rb | 15 | ||||
-rw-r--r-- | app/models/hooks/system_hook.rb | 2 | ||||
-rw-r--r-- | app/models/hooks/web_hook.rb | 39 |
3 files changed, 55 insertions, 1 deletions
diff --git a/app/models/hooks/project_hook.rb b/app/models/hooks/project_hook.rb index b7ace34141e..bcbf43ee38b 100644 --- a/app/models/hooks/project_hook.rb +++ b/app/models/hooks/project_hook.rb @@ -27,6 +27,8 @@ class ProjectHook < WebHook belongs_to :project validates :project, presence: true + scope :for_projects, ->(project) { where(project: project) } + def pluralized_name _('Webhooks') end @@ -41,6 +43,19 @@ class ProjectHook < WebHook project end + override :update_last_failure + def update_last_failure + return if executable? + + key = "web_hooks:last_failure:project-#{project_id}" + time = Time.current.utc.iso8601 + + Gitlab::Redis::SharedState.with do |redis| + prev = redis.get(key) + redis.set(key, time) if !prev || prev < time + end + end + private override :web_hooks_disable_failed? diff --git a/app/models/hooks/system_hook.rb b/app/models/hooks/system_hook.rb index c8a0cc05912..c0073f9a9b8 100644 --- a/app/models/hooks/system_hook.rb +++ b/app/models/hooks/system_hook.rb @@ -26,6 +26,6 @@ class SystemHook < WebHook end def help_path - 'system_hooks/system_hooks' + 'administration/system_hooks' end end diff --git a/app/models/hooks/web_hook.rb b/app/models/hooks/web_hook.rb index 37fd612e652..f428d07cd7f 100644 --- a/app/models/hooks/web_hook.rb +++ b/app/models/hooks/web_hook.rb @@ -3,6 +3,8 @@ class WebHook < ApplicationRecord include Sortable + InterpolationError = Class.new(StandardError) + MAX_FAILURES = 100 FAILURE_THRESHOLD = 3 # three strikes INITIAL_BACKOFF = 10.minutes @@ -36,6 +38,7 @@ class WebHook < ApplicationRecord validates :token, format: { without: /\n/ } validates :push_events_branch_filter, branch_filter: true validates :url_variables, json_schema: { filename: 'web_hooks_url_variables' } + validate :no_missing_url_variables after_initialize :initialize_url_variables @@ -45,6 +48,11 @@ class WebHook < ApplicationRecord where('recent_failures <= ? AND (disabled_until IS NULL OR disabled_until < ?)', FAILURE_THRESHOLD, Time.current) end + # Inverse of executable + scope :disabled, -> do + where('recent_failures > ? OR disabled_until >= ?', FAILURE_THRESHOLD, Time.current) + end + def executable? !temporarily_disabled? && !permanently_disabled? end @@ -164,6 +172,24 @@ class WebHook < ApplicationRecord super(options) end + # See app/validators/json_schemas/web_hooks_url_variables.json + VARIABLE_REFERENCE_RE = /\{([A-Za-z_][A-Za-z0-9_]+)\}/.freeze + + def interpolated_url + return url unless url.include?('{') + + vars = url_variables + url.gsub(VARIABLE_REFERENCE_RE) do + vars.fetch(_1.delete_prefix('{').delete_suffix('}')) + end + rescue KeyError => e + raise InterpolationError, "Invalid URL template. Missing key #{e.key}" + end + + def update_last_failure + # Overridden in child classes. + end + private def web_hooks_disable_failed? @@ -177,4 +203,17 @@ class WebHook < ApplicationRecord def rate_limiter @rate_limiter ||= Gitlab::WebHooks::RateLimiter.new(self) end + + def no_missing_url_variables + return if url.nil? + + variable_names = url_variables.keys + used_variables = url.scan(VARIABLE_REFERENCE_RE).map(&:first) + + missing = used_variables - variable_names + + return if missing.empty? + + errors.add(:url, "Invalid URL template. Missing keys: #{missing}") + end end |