diff options
Diffstat (limited to 'lib/gitlab/ci/runner_releases.rb')
-rw-r--r-- | lib/gitlab/ci/runner_releases.rb | 65 |
1 files changed, 65 insertions, 0 deletions
diff --git a/lib/gitlab/ci/runner_releases.rb b/lib/gitlab/ci/runner_releases.rb new file mode 100644 index 00000000000..944c24ca128 --- /dev/null +++ b/lib/gitlab/ci/runner_releases.rb @@ -0,0 +1,65 @@ +# 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 |