diff --git a/.rubocop_todo/gitlab/bounded_contexts.yml b/.rubocop_todo/gitlab/bounded_contexts.yml index 008a7bdf603af8506aba47c5f1703a0e9a826c2f..967fee319437fdea09e8ce27360f9972c6401991 100644 --- a/.rubocop_todo/gitlab/bounded_contexts.yml +++ b/.rubocop_todo/gitlab/bounded_contexts.yml @@ -4117,6 +4117,5 @@ Gitlab/BoundedContexts: - 'lib/unnested_in_filters/dsl.rb' - 'lib/unnested_in_filters/rewriter.rb' - 'lib/uploaded_file.rb' - - 'lib/version_check.rb' - 'lib/vite_gdk.rb' - 'lib/vs_code/settings.rb' diff --git a/.rubocop_todo/gitlab/namespaced_class.yml b/.rubocop_todo/gitlab/namespaced_class.yml index 6c5874ab069afc8b6770dc0b7fcb56dcb71323db..d1768b1c6debacf866d63b7325b5e6193430e091 100644 --- a/.rubocop_todo/gitlab/namespaced_class.yml +++ b/.rubocop_todo/gitlab/namespaced_class.yml @@ -1197,7 +1197,6 @@ Gitlab/NamespacedClass: - 'lib/tasks/gitlab/seed/group_seed.rake' - 'lib/tasks/tokens.rake' - 'lib/uploaded_file.rb' - - 'lib/version_check.rb' - 'spec/controllers/concerns/page_limiter_spec.rb' - 'spec/lib/bitbucket/collection_spec.rb' - 'spec/lib/gitlab/multi_destination_logger_spec.rb' diff --git a/.rubocop_todo/lint/unused_block_argument.yml b/.rubocop_todo/lint/unused_block_argument.yml index a6fec0306b67927f15aa904322e26b6bb3ea2393..373522011cfc1e5ea2bb11045306816b38b24c2e 100644 --- a/.rubocop_todo/lint/unused_block_argument.yml +++ b/.rubocop_todo/lint/unused_block_argument.yml @@ -228,7 +228,6 @@ Lint/UnusedBlockArgument: - 'lib/tasks/gitlab/uploads/sanitize.rake' - 'lib/tasks/gitlab/user_management.rake' - 'lib/tasks/gitlab/workhorse.rake' - - 'lib/version_check.rb' - 'qa/qa/service/praefect_manager.rb' - 'qa/qa/specs/features/api/12_systems/gitaly/distributed_reads_spec.rb' - 'qa/qa/support/matchers/eventually_matcher.rb' diff --git a/.rubocop_todo/rspec/context_wording.yml b/.rubocop_todo/rspec/context_wording.yml index 8b9cbddb6692681bad4ab8bcaeb55732ab8c479d..9f2d6559e8185dee5c66701cf87e76f1779ca040 100644 --- a/.rubocop_todo/rspec/context_wording.yml +++ b/.rubocop_todo/rspec/context_wording.yml @@ -1862,7 +1862,6 @@ RSpec/ContextWording: - 'spec/lib/system_check/base_check_spec.rb' - 'spec/lib/system_check/incoming_email/imap_authentication_check_spec.rb' - 'spec/lib/uploaded_file_spec.rb' - - 'spec/lib/version_check_spec.rb' - 'spec/mailers/devise_mailer_spec.rb' - 'spec/mailers/emails/profile_spec.rb' - 'spec/mailers/emails/projects_spec.rb' diff --git a/.rubocop_todo/rspec/feature_category.yml b/.rubocop_todo/rspec/feature_category.yml index c6dc02a81510fcf6d6172c3668bea57f2d9943f2..cb898b6ed3d6ed89b071a8e3177fc74de263c5fd 100644 --- a/.rubocop_todo/rspec/feature_category.yml +++ b/.rubocop_todo/rspec/feature_category.yml @@ -3099,7 +3099,6 @@ RSpec/FeatureCategory: - 'spec/lib/system_check/simple_executor_spec.rb' - 'spec/lib/system_check_spec.rb' - 'spec/lib/unnested_in_filters/dsl_spec.rb' - - 'spec/lib/version_check_spec.rb' - 'spec/mailers/abuse_report_mailer_spec.rb' - 'spec/mailers/email_rejection_mailer_spec.rb' - 'spec/mailers/emails/admin_notification_spec.rb' diff --git a/app/controllers/admin/version_check_controller.rb b/app/controllers/admin/version_check_controller.rb index f5c70dc9e1b9de7d32a1965074db5b774f190de1..33cedce7ee536fdbb208b6469297ea468a3b65ee 100644 --- a/app/controllers/admin/version_check_controller.rb +++ b/app/controllers/admin/version_check_controller.rb @@ -3,8 +3,10 @@ class Admin::VersionCheckController < Admin::ApplicationController feature_category :not_owned # rubocop:todo Gitlab/AvoidFeatureCategoryNotOwned + include VersionCheckHelper + def version_check - response = VersionCheck.new.response + response = gitlab_version_check expires_in 1.minute if response render json: response diff --git a/app/helpers/version_check_helper.rb b/app/helpers/version_check_helper.rb index 76404862b960d0d2b33a9c4819218eb364efce12..ac6b20de8a5f7acf8bc5d2bb01ee1d765f1de0c5 100644 --- a/app/helpers/version_check_helper.rb +++ b/app/helpers/version_check_helper.rb @@ -12,7 +12,10 @@ def show_version_check? def gitlab_version_check return unless show_version_check? - VersionCheck.new.response + version = Rails.cache.fetch('version_check') + Gitlab::Version::VersionCheckCronWorker.perform_async if version.nil? + + version end strong_memoize_attr :gitlab_version_check diff --git a/app/workers/all_queues.yml b/app/workers/all_queues.yml index f98e31efe9b2f4b8c2a3a48cb37fcb160c8c256a..0cdec4ed74cd5a52c5c95b0a2e605de67991abbc 100644 --- a/app/workers/all_queues.yml +++ b/app/workers/all_queues.yml @@ -1166,6 +1166,16 @@ :idempotent: true :tags: [] :queue_namespace: :cronjob +- :name: cronjob:version_version_check_cron + :worker_name: Gitlab::Version::VersionCheckCronWorker + :feature_category: :service_ping + :has_external_dependencies: false + :urgency: :low + :resource_boundary: :unknown + :weight: 1 + :idempotent: true + :tags: [] + :queue_namespace: :cronjob - :name: cronjob:x509_issuer_crl_check :worker_name: X509IssuerCrlCheckWorker :feature_category: :source_code_management diff --git a/app/workers/gitlab/version/version_check_cron_worker.rb b/app/workers/gitlab/version/version_check_cron_worker.rb new file mode 100644 index 0000000000000000000000000000000000000000..cf020775433f848451c4522214b6b6850d681f7b --- /dev/null +++ b/app/workers/gitlab/version/version_check_cron_worker.rb @@ -0,0 +1,55 @@ +# frozen_string_literal: true + +require "base64" + +module Gitlab + module Version + class VersionCheckCronWorker + include ApplicationWorker + include CronjobQueue # rubocop: disable Scalability/CronWorkerContext -- no relevant metadata + + deduplicate :until_executed + idempotent! + + data_consistency :sticky + + sidekiq_options retry: 3 + + feature_category :service_ping + urgency :low + + def perform + response = Gitlab::HTTP.try_get(url) + + if response.present? && response.code == 200 + result = Gitlab::Json.parse(response.body) + Gitlab::AppLogger.info(message: 'Version check succeeded', result: result) + + Rails.cache.write("version_check", result) + else + Gitlab::AppLogger.error(message: 'Version check failed', + error: { code: response&.code, message: response&.body }) + end + rescue JSON::ParserError => e + Gitlab::AppLogger.error(message: 'Parsing version check response failed', + error: { message: e.message, code: response&.code }) + end + + private + + def data + { version: Gitlab::VERSION } + end + + def url + encoded_data = Base64.urlsafe_encode64(data.to_json) + + "#{host}/check.json?gitlab_info=#{encoded_data}" + end + + def host + 'https://version.gitlab.com' + end + end + end +end diff --git a/config/initializers/1_settings.rb b/config/initializers/1_settings.rb index 8a02b72906abd3a7fcd7df58de5ca53558e1634f..51f80a6fc4d42778c1b8051c8b9e5effbbcb5769 100644 --- a/config/initializers/1_settings.rb +++ b/config/initializers/1_settings.rb @@ -741,6 +741,9 @@ Settings.cron_jobs['ci_schedule_old_pipelines_removal_cron_worker'] ||= {} Settings.cron_jobs['ci_schedule_old_pipelines_removal_cron_worker']['cron'] ||= '*/11 * * * *' Settings.cron_jobs['ci_schedule_old_pipelines_removal_cron_worker']['job_class'] = 'Ci::ScheduleOldPipelinesRemovalCronWorker' +Settings.cron_jobs['version_version_check_cron'] ||= {} +Settings.cron_jobs['version_version_check_cron']['cron'] ||= "#{rand(60)} #{rand(24)} * * *" +Settings.cron_jobs['version_version_check_cron']['job_class'] = 'Gitlab::Version::VersionCheckCronWorker' Gitlab.ee do Settings.cron_jobs['analytics_devops_adoption_create_all_snapshots_worker'] ||= {} diff --git a/ee/spec/features/admin/admin_dashboard_spec.rb b/ee/spec/features/admin/admin_dashboard_spec.rb index 4afc70da1ea42378fcb474c9d264bead94537509..8b9d54faee73da7d18a9286186186f60ff338966 100644 --- a/ee/spec/features/admin/admin_dashboard_spec.rb +++ b/ee/spec/features/admin/admin_dashboard_spec.rb @@ -3,6 +3,8 @@ require 'spec_helper' RSpec.describe 'Admin Dashboard', feature_category: :shared do + include VersionCheckHelpers + before do admin = create(:admin) sign_in(admin) @@ -101,6 +103,10 @@ end describe 'Version check', :js do + before do + stub_version_check({ "severity" => "success" }) + end + it 'shows badge on EE' do visit admin_root_path diff --git a/lib/version_check.rb b/lib/version_check.rb deleted file mode 100644 index 1447a44f6e36d5883094a0500d0775caa6f661be..0000000000000000000000000000000000000000 --- a/lib/version_check.rb +++ /dev/null @@ -1,88 +0,0 @@ -# frozen_string_literal: true - -require "base64" - -class VersionCheck - include ReactiveCaching - - # Increment when format of cache value is changed - CACHE_VERSION = 1 - - ## Version Check Reactive Caching - ## This cache stores the external API response from https://version.gitlab.com - ## - ## Example API Response - ## { - ## "latest_version": "15.2.2", - ## "severity": "success" - ## } - ## - ## This response from this endpoint only changes in 2 scenarios: - ## 1. Customer upgrades their GitLab Instance - ## 2. GitLab releases a new version - ## - ## We use GitLab::VERSION as the identifier for the cached information. - ## This means if the user upgrades their version we will create a new cache record. - ## The old one will be invalidated and cleaned up at the end of the self.reactive_cache_lifetime. - ## - ## - self.reactive_cache_refresh_interval = 12.hours - ## We want to prevent as many external API calls as possible to save on resources. - ## Since an EXISTING cache record will only become "invalid" if GitLab releases a new version we - ## determined that 12 hour intervals is enough of a window to capture an available upgrade. - ## - ## - self.reactive_cache_lifetime = 7.days - ## We don't want the data to be missing every time a user revisits a page using this info. - ## Thus 7 days seems like a fair amount of time before we erase the cache. - ## This also will handle cleaning up old cache records as they will no longer be accessed after an upgrade. - ## - - self.reactive_cache_refresh_interval = 12.hours - self.reactive_cache_lifetime = 7.days - self.reactive_cache_work_type = :external_dependency - self.reactive_cache_worker_finder = ->(_id, *args) { from_cache } - - def self.data - { version: Gitlab::VERSION } - end - - def self.url - encoded_data = Base64.urlsafe_encode64(data.to_json) - - "#{host}/check.json?gitlab_info=#{encoded_data}" - end - - def self.host - 'https://version.gitlab.com' - end - - def self.from_cache(*) - new - end - - def id - [Gitlab::VERSION, Gitlab.revision, CACHE_VERSION].join('-') - end - - def calculate_reactive_cache(*) - response = Gitlab::HTTP.try_get(self.class.url) - - case response&.code - when 200 - Gitlab::Json.parse(response.body) - else - { error: 'version check failed', status: response&.code } - end - rescue JSON::ParserError - { error: 'parsing version check response failed', status: response&.code } - end - - def response - with_reactive_cache do |data| - raise InvalidateReactiveCache if !data.is_a?(Hash) || data[:error] - - data - end - end -end - -VersionCheck.prepend_mod diff --git a/spec/features/admin/dashboard_spec.rb b/spec/features/admin/dashboard_spec.rb index 67e7cd6dc94c3c92fb247a3cc320fc159bd75fdf..da2e50651a9a9922a387257d0e8b2b50adaf0e8d 100644 --- a/spec/features/admin/dashboard_spec.rb +++ b/spec/features/admin/dashboard_spec.rb @@ -4,6 +4,7 @@ RSpec.describe 'admin visits dashboard' do include ProjectForksHelper + include VersionCheckHelpers before do admin = create(:admin) @@ -55,6 +56,10 @@ end describe 'Version check', :js, feature_category: :deployment_management do + before do + stub_version_check({ "severity" => "success" }) + end + it 'shows badge on CE' do visit admin_root_path diff --git a/spec/features/help_dropdown_spec.rb b/spec/features/help_dropdown_spec.rb index e9ef67a70c34a7f0982f978715646bad6dc6fa64..95b879a808cef8eea8521680ab2b9eebd9cb7d33 100644 --- a/spec/features/help_dropdown_spec.rb +++ b/spec/features/help_dropdown_spec.rb @@ -3,6 +3,8 @@ require 'spec_helper' RSpec.describe "Help Dropdown", :js, feature_category: :shared do + include VersionCheckHelpers + let_it_be(:user) { create(:user) } let_it_be(:admin) { create(:admin) } @@ -29,9 +31,7 @@ sign_in(admin) enable_admin_mode!(admin) - allow_next_instance_of(VersionCheck) do |instance| - allow(instance).to receive(:response).and_return({ "severity" => severity }) - end + stub_version_check({ "severity" => severity }) visit root_path end diff --git a/spec/helpers/version_check_helper_spec.rb b/spec/helpers/version_check_helper_spec.rb index 9af903065b3bf30ee8e96fb4dbb06d04f5af29af..3bf332927d1d8bbf99e2fbd06380ea5b59faa94c 100644 --- a/spec/helpers/version_check_helper_spec.rb +++ b/spec/helpers/version_check_helper_spec.rb @@ -51,14 +51,32 @@ context 'when show_version_check? is true' do let(:show_version_check) { true } - before do - allow_next_instance_of(VersionCheck) do |instance| - allow(instance).to receive(:response).and_return({ "severity" => "success" }) + context 'when it has no cached version_check response' do + before do + allow(Rails.cache).to receive(:fetch).with('version_check').and_return(nil) + end + + it 'schedules a version check worker' do + expect(Gitlab::Version::VersionCheckCronWorker).to receive(:perform_async) + + helper.gitlab_version_check + end + + it 'returns nil' do + expect(helper.gitlab_version_check).to be nil end end - it 'returns an instance of the VersionCheck class if the user has access' do - expect(helper.gitlab_version_check).to eq({ "severity" => "success" }) + context 'when it has a cached version_check response' do + let(:version_check) { { "severity" => "success" } } + + before do + allow(Rails.cache).to receive(:fetch).with('version_check').and_return(version_check) + end + + it 'returns the cached version check response' do + expect(helper.gitlab_version_check).to eq(version_check) + end end end end diff --git a/spec/lib/version_check_spec.rb b/spec/lib/version_check_spec.rb deleted file mode 100644 index 4aa8975b7cf81ba20fe8ce6483804be1a241fdcb..0000000000000000000000000000000000000000 --- a/spec/lib/version_check_spec.rb +++ /dev/null @@ -1,114 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -RSpec.describe VersionCheck, :use_clean_rails_memory_store_caching do - include ReactiveCachingHelpers - - describe '.url' do - it 'returns the correct URL' do - expect(described_class.url).to match(%r{\A#{Regexp.escape(described_class.host)}/check\.json\?gitlab_info=\w+}) - end - end - - context 'reactive cache properties' do - describe '.reactive_cache_refresh_interval' do - it 'returns 12.hours' do - expect(described_class.reactive_cache_refresh_interval).to eq(12.hours) - end - end - - describe '.reactive_cache_lifetime' do - it 'returns 7.days' do - expect(described_class.reactive_cache_lifetime).to eq(7.days) - end - end - end - - describe '#calculate_reactive_cache' do - context 'response code is 200 with valid body' do - before do - stub_request(:get, described_class.url).to_return(status: 200, body: '{ "status": "success" }', headers: {}) - end - - it 'returns the response object' do - expect(described_class.new.calculate_reactive_cache).to eq({ "status" => "success" }) - end - end - - context 'response code is 200 with invalid body' do - before do - stub_request(:get, described_class.url).to_return(status: 200, body: '{ "invalid: json" }', headers: {}) - end - - it 'returns an error hash' do - expect(described_class.new.calculate_reactive_cache).to eq( - { error: 'parsing version check response failed', status: 200 } - ) - end - end - - context 'response code is not 200' do - before do - stub_request(:get, described_class.url).to_return(status: 500, body: nil, headers: {}) - end - - it 'returns an error hash' do - expect(described_class.new.calculate_reactive_cache).to eq({ error: 'version check failed', status: 500 }) - end - end - end - - describe '#response' do - # see https://gitlab.com/gitlab-org/gitlab/-/merge_requests/106254 - context "with old string value in cache" do - before do - old_version_check = described_class.new - allow(old_version_check).to receive(:id).and_return(Gitlab::VERSION) - write_reactive_cache(old_version_check, - "{\"latest_stable_versions\":[],\"latest_version\":\"15.6.2\",\"severity\":\"success\",\"details\":\"\"}" - ) - end - - it 'returns nil' do - version_check = described_class.new - expect(version_check.response).to be_nil - end - end - - # see https://gitlab.com/gitlab-org/gitlab/-/merge_requests/106254 - context "with non-hash value in cache" do - it 'returns nil and invalidates the reactive cache' do - version_check = described_class.new - stub_reactive_cache(version_check, - "{\"latest_stable_versions\":[],\"latest_version\":\"15.6.2\",\"severity\":\"success\",\"details\":\"\"}" - ) - - expect(version_check).to receive(:refresh_reactive_cache!).and_call_original - expect(version_check.response).to be_nil - expect(read_reactive_cache(version_check)).to be_nil - end - end - - context 'cache returns value' do - it 'returns the response object' do - version_check = described_class.new - data = { status: 'success' } - stub_reactive_cache(version_check, data) - - expect(version_check.response).to eq(data) - end - end - - context 'cache returns error' do - it 'returns nil and invalidates the reactive cache' do - version_check = described_class.new - stub_reactive_cache(version_check, error: 'version check failed') - - expect(version_check).to receive(:refresh_reactive_cache!).and_call_original - expect(version_check.response).to be_nil - expect(read_reactive_cache(version_check)).to be_nil - end - end - end -end diff --git a/spec/requests/admin/version_check_controller_spec.rb b/spec/requests/admin/version_check_controller_spec.rb index a998c2f426b59c76b857cd0e3164c707b5b4424f..291b17284df6633b584441aaaffe9dee0ab33b6d 100644 --- a/spec/requests/admin/version_check_controller_spec.rb +++ b/spec/requests/admin/version_check_controller_spec.rb @@ -10,40 +10,38 @@ end describe 'GET #version_check' do - context 'when VersionCheck.response is nil' do + let(:version_check_response) { { 'success' => true, 'version' => '16.0.0' } } + + context 'when version check is successful' do before do - allow_next_instance_of(VersionCheck) do |instance| - allow(instance).to receive(:response).and_return(nil) - end - get admin_version_check_path + allow(Rails.cache).to receive(:fetch).with("version_check").and_return(version_check_response) end - it 'returns nil' do + it 'returns version check data' do + get admin_version_check_path + expect(response).to have_gitlab_http_status(:ok) - expect(json_response).to be_nil + expect(json_response).to eq(version_check_response) end - it 'sets no-cache headers' do - expect(response.headers['Cache-Control']).to eq('max-age=0, private, must-revalidate') + it 'sets cache expiration to 1 minute' do + get admin_version_check_path + + expect(response.headers['Cache-Control']).to include('max-age=60') end end - context 'when VersionCheck.response is valid' do + context 'when version check fails' do before do - allow_next_instance_of(VersionCheck) do |instance| - allow(instance).to receive(:response).and_return({ "severity" => "success" }) - end + allow(VersionCheckHelper).to receive(:gitlab_version_check).and_return(nil) + end + it 'returns nil without cache headers' do get admin_version_check_path - end - it 'returns the valid data' do expect(response).to have_gitlab_http_status(:ok) - expect(json_response).to eq({ "severity" => "success" }) - end - - it 'sets proper cache headers' do - expect(response.headers['Cache-Control']).to eq('max-age=60, private') + expect(json_response).to be_nil + expect(response.headers['Cache-Control']).not_to include('max-age=60') end end end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 4aa26e423f7c8148095aa452953ceae2ee66338c..acb698e37593ee96b00a4826f112ca33da3d0d53 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -224,6 +224,7 @@ include StubFeatureFlags include StubSnowplow include StubMember + include VersionCheckHelpers if ENV['CI'] || ENV['RETRIES'] # Gradually stop using rspec-retry @@ -497,7 +498,7 @@ # Ensures that any Javascript script that tries to make the external VersionCheck API call skips it and returns a response config.before(:each, :js) do - allow_any_instance_of(VersionCheck).to receive(:response).and_return({ "severity" => "success" }) + stub_version_check({ "severity" => "success" }) end [:migration, :delete].each do |spec_type| diff --git a/spec/support/helpers/version_check_helpers.rb b/spec/support/helpers/version_check_helpers.rb new file mode 100644 index 0000000000000000000000000000000000000000..caf8c15036798b516323d9b24fa3f64f51c657a9 --- /dev/null +++ b/spec/support/helpers/version_check_helpers.rb @@ -0,0 +1,8 @@ +# frozen_string_literal: true + +module VersionCheckHelpers + def stub_version_check(response) + allow(Rails.cache).to receive(:fetch).and_call_original + allow(Rails.cache).to receive(:fetch).with('version_check').and_return(response) + end +end diff --git a/spec/support/rspec_order_todo.yml b/spec/support/rspec_order_todo.yml index f363f8fee77b34ecb7356ef8ecdfed84dc80bd28..9d11e8be59e5e5f104e3d9cd49d202ac00ff7151 100644 --- a/spec/support/rspec_order_todo.yml +++ b/spec/support/rspec_order_todo.yml @@ -5745,7 +5745,6 @@ - './spec/lib/unnested_in_filters/dsl_spec.rb' - './spec/lib/unnested_in_filters/rewriter_spec.rb' - './spec/lib/uploaded_file_spec.rb' -- './spec/lib/version_check_spec.rb' - './spec/mailers/abuse_report_mailer_spec.rb' - './spec/mailers/devise_mailer_spec.rb' - './spec/mailers/email_rejection_mailer_spec.rb' diff --git a/spec/views/shared/gitlab_version/_security_patch_upgrade_alert.html.haml_spec.rb b/spec/views/shared/gitlab_version/_security_patch_upgrade_alert.html.haml_spec.rb index a0e2e5507550d195016595a01a16d36cb5cadb0a..82b180a36199893e1be73e6ffadade042c504b07 100644 --- a/spec/views/shared/gitlab_version/_security_patch_upgrade_alert.html.haml_spec.rb +++ b/spec/views/shared/gitlab_version/_security_patch_upgrade_alert.html.haml_spec.rb @@ -3,15 +3,13 @@ require 'spec_helper' RSpec.describe 'shared/gitlab_version/_security_patch_upgrade_alert' do + include VersionCheckHelper + let_it_be(:user) { build_stubbed(:user) } - let(:version_check_response) { { 'critical_vulnerability' => 'true' } } before do stub_application_setting(version_check_enabled: true) - - allow_next_instance_of(VersionCheck) do |service| - allow(service).to receive(:response).and_return(version_check_response) - end + stub_version_check({ 'critical_vulnerability' => 'true' }) end describe 'when version check is enabled and is admin' do diff --git a/spec/workers/every_sidekiq_worker_spec.rb b/spec/workers/every_sidekiq_worker_spec.rb index 1b9a0db11031ee8a65223969ee60d52a370f169c..794675b81224489c7844e0f6946ac0144c3809cc 100644 --- a/spec/workers/every_sidekiq_worker_spec.rb +++ b/spec/workers/every_sidekiq_worker_spec.rb @@ -311,6 +311,7 @@ 'Gitlab::JiraImport::Stage::ImportLabelsWorker' => 6, 'Gitlab::JiraImport::Stage::ImportNotesWorker' => 6, 'Gitlab::JiraImport::Stage::StartImportWorker' => 6, + 'Gitlab::Version::VersionCheckCronWorker' => 3, 'GitlabPerformanceBarStatsWorker' => 3, 'GitlabSubscriptions::RefreshSeatsWorker' => 0, 'GitlabSubscriptions::AddOnPurchases::BulkRefreshUserAssignmentsWorker' => 0, diff --git a/spec/workers/gitlab/version/version_check_cron_worker_spec.rb b/spec/workers/gitlab/version/version_check_cron_worker_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..d07fd89433d38f2c60e8f6fab159dd120d4e9fca --- /dev/null +++ b/spec/workers/gitlab/version/version_check_cron_worker_spec.rb @@ -0,0 +1,82 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Gitlab::Version::VersionCheckCronWorker, feature_category: :service_ping do + let(:worker) { described_class.new } + let(:response) { double } + let(:version_info) { { 'version' => '1.0.0' } } + let(:encoded_data) { Base64.urlsafe_encode64({ version: Gitlab::VERSION }.to_json) } + let(:version_url) { "https://version.gitlab.com/check.json?gitlab_info=#{encoded_data}" } + + before do + allow(Gitlab::HTTP).to receive(:try_get).with(version_url).and_return(response) + end + + describe '#perform' do + context 'when request is successful' do + before do + allow(response).to receive_messages(body: version_info.to_json, code: 200) + end + + it 'caches the version information' do + expect(Rails.cache).to receive(:write).with('version_check', version_info) + + worker.perform + end + + it 'logs the response' do + expect(Gitlab::AppLogger).to receive(:info).with( + message: 'Version check succeeded', + result: version_info) + + worker.perform + end + end + + context 'when request fails' do + before do + allow(response).to receive_messages(body: 'error', code: 500) + end + + it 'logs an error' do + expect(Gitlab::AppLogger).to receive(:error).with( + message: 'Version check failed', + error: { code: 500, message: 'error' } + ) + + worker.perform + end + end + + context 'when response is not present' do + before do + allow(Gitlab::HTTP).to receive(:try_get).with(version_url).and_return(nil) + end + + it 'logs an error' do + expect(Gitlab::AppLogger).to receive(:error).with( + message: 'Version check failed', + error: { code: nil, message: nil } + ) + + worker.perform + end + end + + context 'when JSON parsing fails' do + before do + allow(response).to receive_messages(body: 'invalid json', code: 200) + end + + it 'logs a parsing error' do + expect(Gitlab::AppLogger).to receive(:error).with( + message: 'Parsing version check response failed', + error: { message: kind_of(String), code: 200 } + ) + + worker.perform + end + end + end +end