diff options
Diffstat (limited to 'app/models/ci/runner.rb')
-rw-r--r-- | app/models/ci/runner.rb | 74 |
1 files changed, 55 insertions, 19 deletions
diff --git a/app/models/ci/runner.rb b/app/models/ci/runner.rb index d12f96f3d0b..7173f88f1c7 100644 --- a/app/models/ci/runner.rb +++ b/app/models/ci/runner.rb @@ -1,14 +1,17 @@ module Ci class Runner < ActiveRecord::Base - extend Ci::Model + extend Gitlab::Ci::Model + include Gitlab::SQL::Pattern + include RedisCacheable RUNNER_QUEUE_EXPIRY_TIME = 60.minutes - LAST_CONTACT_TIME = 1.hour.ago + ONLINE_CONTACT_TIMEOUT = 1.hour + UPDATE_DB_RUNNER_INFO_EVERY = 40.minutes AVAILABLE_SCOPES = %w[specific shared active paused online].freeze - FORM_EDITABLE = %i[description tag_list active run_untagged locked].freeze + FORM_EDITABLE = %i[description tag_list active run_untagged locked access_level].freeze has_many :builds - has_many :runner_projects, dependent: :destroy + has_many :runner_projects, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent has_many :projects, through: :runner_projects has_one :last_build, ->() { order('id DESC') }, class_name: 'Ci::Build' @@ -19,7 +22,7 @@ module Ci scope :shared, ->() { where(is_shared: true) } scope :active, ->() { where(active: true) } scope :paused, ->() { where(active: false) } - scope :online, ->() { where('contacted_at > ?', LAST_CONTACT_TIME) } + scope :online, ->() { where('contacted_at > ?', contact_time_deadline) } scope :ordered, ->() { order(id: :desc) } scope :owned_or_shared, ->(project_id) do @@ -35,11 +38,19 @@ module Ci end validate :tag_constraints + validates :access_level, presence: true acts_as_taggable after_destroy :cleanup_runner_queue + enum access_level: { + not_protected: 0, + ref_protected: 1 + } + + cached_attr_reader :version, :revision, :platform, :architecture, :contacted_at, :ip_address + # Searches for runners matching the given query. # # This method uses ILIKE on PostgreSQL and LIKE on MySQL. @@ -53,10 +64,11 @@ module Ci # # Returns an ActiveRecord::Relation. def self.search(query) - t = arel_table - pattern = "%#{query}%" + fuzzy_search(query, [:token, :description]) + end - where(t[:token].matches(pattern).or(t[:description].matches(pattern))) + def self.contact_time_deadline + ONLINE_CONTACT_TIMEOUT.ago end def set_default_values @@ -80,7 +92,7 @@ module Ci end def online? - contacted_at && contacted_at > LAST_CONTACT_TIME + contacted_at && contacted_at > self.class.contact_time_deadline end def status @@ -102,7 +114,9 @@ module Ci end def can_pick?(build) - assignable_for?(build.project) && accepting_tags?(build) + return false if self.ref_protected? && !build.protected? + + assignable_for?(build.project_id) && accepting_tags?(build) end def only_for?(project) @@ -118,11 +132,10 @@ module Ci end def predefined_variables - [ - { key: 'CI_RUNNER_ID', value: id.to_s, public: true }, - { key: 'CI_RUNNER_DESCRIPTION', value: description, public: true }, - { key: 'CI_RUNNER_TAGS', value: tag_list.to_s, public: true } - ] + Gitlab::Ci::Variables::Collection.new + .append(key: 'CI_RUNNER_ID', value: id.to_s) + .append(key: 'CI_RUNNER_DESCRIPTION', value: description) + .append(key: 'CI_RUNNER_TAGS', value: tag_list.to_s) end def tick_runner_queue @@ -138,14 +151,26 @@ module Ci expire: RUNNER_QUEUE_EXPIRY_TIME, overwrite: false) end - def is_runner_queue_value_latest?(value) + def runner_queue_value_latest?(value) ensure_runner_queue_value == value if value.present? end + def update_cached_info(values) + values = values&.slice(:version, :revision, :platform, :architecture, :ip_address) || {} + values[:contacted_at] = Time.now + + cache_attributes(values) + + if persist_cached_data? + self.assign_attributes(values) + self.save if self.changed? + end + end + private def cleanup_runner_queue - Gitlab::Redis.with do |redis| + Gitlab::Redis::Queues.with do |redis| redis.del(runner_queue_key) end end @@ -154,6 +179,17 @@ module Ci "runner:build_queue:#{self.token}" end + def persist_cached_data? + # Use a random threshold to prevent beating DB updates. + # It generates a distribution between [40m, 80m]. + + contacted_at_max_age = UPDATE_DB_RUNNER_INFO_EVERY + Random.rand(UPDATE_DB_RUNNER_INFO_EVERY) + + real_contacted_at = read_attribute(:contacted_at) + real_contacted_at.nil? || + (Time.now - real_contacted_at) >= contacted_at_max_age + end + def tag_constraints unless has_tags? || run_untagged? errors.add(:tags_list, @@ -161,8 +197,8 @@ module Ci end end - def assignable_for?(project) - !locked? || projects.exists?(id: project.id) + def assignable_for?(project_id) + is_shared? || projects.exists?(id: project_id) end def accepting_tags?(build) |