66 lines
1.6 KiB
Ruby
66 lines
1.6 KiB
Ruby
|
# frozen_string_literal: true
|
||
|
|
||
|
module Gitlab
|
||
|
module Ci
|
||
|
class RunnerReleases
|
||
|
include Singleton
|
||
|
|
||
|
RELEASES_VALIDITY_PERIOD = 1.day
|
||
|
RELEASES_VALIDITY_AFTER_ERROR_PERIOD = 5.seconds
|
||
|
|
||
|
INITIAL_BACKOFF = 5.seconds
|
||
|
MAX_BACKOFF = 1.hour
|
||
|
BACKOFF_GROWTH_FACTOR = 2.0
|
||
|
|
||
|
def initialize
|
||
|
reset!
|
||
|
end
|
||
|
|
||
|
# Returns a sorted list of the publicly available GitLab Runner releases
|
||
|
#
|
||
|
def releases
|
||
|
return @releases unless Time.now.utc >= @expire_time
|
||
|
|
||
|
@releases = fetch_new_releases
|
||
|
end
|
||
|
|
||
|
def reset!
|
||
|
@expire_time = Time.now.utc
|
||
|
@releases = nil
|
||
|
@backoff_count = 0
|
||
|
end
|
||
|
|
||
|
public_class_method :instance
|
||
|
|
||
|
private
|
||
|
|
||
|
def fetch_new_releases
|
||
|
response = Gitlab::HTTP.try_get(::Gitlab::CurrentSettings.current_application_settings.public_runner_releases_url)
|
||
|
|
||
|
releases = response.success? ? extract_releases(response) : nil
|
||
|
ensure
|
||
|
@expire_time = (releases ? RELEASES_VALIDITY_PERIOD : next_backoff).from_now
|
||
|
end
|
||
|
|
||
|
def extract_releases(response)
|
||
|
response.parsed_response.map { |release| parse_runner_release(release) }.sort!
|
||
|
end
|
||
|
|
||
|
def parse_runner_release(release)
|
||
|
::Gitlab::VersionInfo.parse(release['name'].delete_prefix('v'))
|
||
|
end
|
||
|
|
||
|
def next_backoff
|
||
|
return MAX_BACKOFF if @backoff_count >= 11 # optimization to prevent expensive exponentiation and possible overflows
|
||
|
|
||
|
backoff = (INITIAL_BACKOFF * (BACKOFF_GROWTH_FACTOR**@backoff_count))
|
||
|
.clamp(INITIAL_BACKOFF, MAX_BACKOFF)
|
||
|
.seconds
|
||
|
@backoff_count += 1
|
||
|
|
||
|
backoff
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
end
|