From 01358ae97bcf27d3b05054b5c5be4fe60d13ff6d Mon Sep 17 00:00:00 2001 From: Gerardo Navarro Date: Sat, 13 Dec 2025 22:43:35 +0000 Subject: [PATCH 1/2] api: Add Elasticsearch index settings retrieval to application settings This change introduces a new method to retrieve Elasticsearch index settings within the application settings model. This is necessary to provide users with access to the configuration of Elasticsearch indices, which is essential for managing search capabilities. Technical changes: - Added `elasticsearch_index_settings` method to the ApplicationSetting model to return ordered index settings. - Created a new API entity, `IndexSetting`, to expose index attributes such as alias name, number of shards, and number of replicas. - Updated application setting API to include the new index settings in the response when the Elasticsearch feature is available. Changelog: added --- doc/api/settings.md | 8 +++++ ee/app/models/ee/application_setting.rb | 4 +++ ee/lib/api/entities/elastic/index_setting.rb | 30 ++++++++++++++++ ee/lib/ee/api/entities/application_setting.rb | 4 +++ ee/spec/models/application_setting_spec.rb | 36 +++++++++++++++++++ ee/spec/requests/api/settings_spec.rb | 36 +++++++++++++++++++ 6 files changed, 118 insertions(+) create mode 100644 ee/lib/api/entities/elastic/index_setting.rb diff --git a/doc/api/settings.md b/doc/api/settings.md index 0a755fe104d5ec..1def4f6e800a7e 100644 --- a/doc/api/settings.md +++ b/doc/api/settings.md @@ -190,6 +190,7 @@ these parameters: - `dependency_scanning_sbom_scan_api_upload_limit` - `disable_personal_access_tokens` - `duo_features_enabled` +- `elasticsearch_index_settings` - `file_template_project_id` - `geo_node_allowed_ips` - `geo_status_timeout` @@ -211,6 +212,13 @@ these parameters: "default_project_deletion_protection": false, "disable_personal_access_tokens": false, "duo_features_enabled": true, + "elasticsearch_index_settings": [ + { + "alias_name": "gitlab-production", + "number_of_shards": 5, + "number_of_replicas": 1 + } + ], "file_template_project_id": 1, "geo_node_allowed_ips": "0.0.0.0/0, ::/0", "group_owners_can_manage_default_branch_protection": true, diff --git a/ee/app/models/ee/application_setting.rb b/ee/app/models/ee/application_setting.rb index 25b113db46c2f4..1cfafbc4b48f2c 100644 --- a/ee/app/models/ee/application_setting.rb +++ b/ee/app/models/ee/application_setting.rb @@ -519,6 +519,10 @@ def elasticsearch_replicas Elastic::IndexSetting.number_of_replicas end + def elasticsearch_index_settings + Elastic::IndexSetting.order_by_name + end + def elasticsearch_indexes_project?(project) return false unless elasticsearch_indexing? return true unless elasticsearch_limit_indexing? diff --git a/ee/lib/api/entities/elastic/index_setting.rb b/ee/lib/api/entities/elastic/index_setting.rb new file mode 100644 index 00000000000000..3eddcc8be3ce0a --- /dev/null +++ b/ee/lib/api/entities/elastic/index_setting.rb @@ -0,0 +1,30 @@ +# frozen_string_literal: true + +module API + module Entities + module Elastic + class IndexSetting < Grape::Entity # rubocop:disable Search/NamespacedClass -- This is an API entity, not search code + expose :alias_name, + documentation: { + type: 'String', + desc: 'Name of the Elasticsearch index alias.', + example: 'gitlab-production' + } + + expose :number_of_shards, + documentation: { + type: 'Integer', + desc: 'Number of shards for the Elasticsearch index.', + example: 5 + } + + expose :number_of_replicas, + documentation: { + type: 'Integer', + desc: 'Number of replicas for the Elasticsearch index.', + example: 1 + } + end + end + end +end diff --git a/ee/lib/ee/api/entities/application_setting.rb b/ee/lib/ee/api/entities/application_setting.rb index f25ba921754c12..f8f68aeaba3ec2 100644 --- a/ee/lib/ee/api/entities/application_setting.rb +++ b/ee/lib/ee/api/entities/application_setting.rb @@ -59,6 +59,10 @@ module ApplicationSetting if: ->(instance, _opts) { instance.auto_duo_code_review_settings_available? } + expose :elasticsearch_index_settings, + using: ::API::Entities::Elastic::IndexSetting, + documentation: { is_array: true, desc: 'Elasticsearch index settings.' }, + if: ->(_instance, _opts) { ::License.feature_available?(:elastic_search) } end end end diff --git a/ee/spec/models/application_setting_spec.rb b/ee/spec/models/application_setting_spec.rb index 1b54705ca97210..1f8bf847b49542 100644 --- a/ee/spec/models/application_setting_spec.rb +++ b/ee/spec/models/application_setting_spec.rb @@ -1728,6 +1728,42 @@ def expect_is_es_licensed end end + describe '#elasticsearch_index_settings', feature_category: :global_search do + subject(:elasticsearch_index_settings) { setting.elasticsearch_index_settings } + + context 'when no index settings exist' do + it { is_expected.to be_empty } + end + + context 'when one index setting exists' do + let!(:index_setting) do + create(:elastic_index_setting, alias_name: 'test-index', number_of_shards: 3, number_of_replicas: 2) + end + + it 'returns the index setting with correct attributes' do + expect(elasticsearch_index_settings).to contain_exactly( + have_attributes(alias_name: 'test-index', number_of_shards: 3, number_of_replicas: 2) + ) + end + end + + context 'when multiple index settings exist' do + before do + create(:elastic_index_setting, alias_name: 'index-alpha', number_of_shards: 5, number_of_replicas: 1) + create(:elastic_index_setting, alias_name: 'index-beta', number_of_shards: 10, number_of_replicas: 3) + create(:elastic_index_setting, alias_name: 'index-gamma', number_of_shards: 2, number_of_replicas: 0) + end + + it 'returns all index settings ordered by name' do + expect(elasticsearch_index_settings).to match_array([ + have_attributes(alias_name: 'index-alpha'), + have_attributes(alias_name: 'index-beta'), + have_attributes(alias_name: 'index-gamma') + ]) + end + end + end + describe '#elasticsearch_url_with_credentials', feature_category: :global_search do let(:elasticsearch_url) { "#{host1},#{host2}" } let(:host1) { 'http://example.com' } diff --git a/ee/spec/requests/api/settings_spec.rb b/ee/spec/requests/api/settings_spec.rb index 8f590a59841f50..3e5363e2fe2383 100644 --- a/ee/spec/requests/api/settings_spec.rb +++ b/ee/spec/requests/api/settings_spec.rb @@ -292,6 +292,42 @@ expect(ElasticsearchIndexedNamespace.count).to eq(0) expect(ElasticsearchIndexedProject.count).to eq(1) end + + context 'with elasticsearch_index_settings' do + let_it_be(:index_setting) do + create(:elastic_index_setting, alias_name: 'api-test-index', number_of_shards: 5, number_of_replicas: 1) + end + + subject(:get_application_setting_response) { get api('/application/settings', admin, admin_mode: true) } + + before do + stub_licensed_features(elastic_search: true) + end + + it 'returns elasticsearch_index_settings in response' do + get_application_setting_response + + expect(response).to have_gitlab_http_status(:ok) + expect(json_response['elasticsearch_index_settings']).to include( + 'alias_name' => 'api-test-index', + 'number_of_shards' => 5, + 'number_of_replicas' => 1 + ) + end + + context 'when license feature :elastic_search is not available' do + before do + stub_licensed_features(elastic_search: false) + end + + it 'does not return elasticsearch_index_settings in response' do + get_application_setting_response + + expect(response).to have_gitlab_http_status(:ok) + expect(json_response).not_to have_key('elasticsearch_index_settings') + end + end + end end context 'secret_detection_token_revocation_enabled is true' do -- GitLab From b5620b99ae79ef8c9d1265f81f6e3e80a22337b8 Mon Sep 17 00:00:00 2001 From: Gerardo Navarro Date: Tue, 16 Dec 2025 17:25:29 +0100 Subject: [PATCH 2/2] refactor: Apply suggestion from @rkumar555 - https://gitlab.com/gitlab-org/gitlab/-/merge_requests/216396#note_2954475399 - https://gitlab.com/gitlab-org/gitlab/-/merge_requests/216396#note_2954475383 - https://gitlab.com/gitlab-org/gitlab/-/merge_requests/216396#note_2954475374 --- ee/spec/models/application_setting_spec.rb | 18 +++++------------- ee/spec/requests/api/settings_spec.rb | 2 +- 2 files changed, 6 insertions(+), 14 deletions(-) diff --git a/ee/spec/models/application_setting_spec.rb b/ee/spec/models/application_setting_spec.rb index 1f8bf847b49542..d85def19612267 100644 --- a/ee/spec/models/application_setting_spec.rb +++ b/ee/spec/models/application_setting_spec.rb @@ -1736,7 +1736,7 @@ def expect_is_es_licensed end context 'when one index setting exists' do - let!(:index_setting) do + let_it_be(:_index_setting) do create(:elastic_index_setting, alias_name: 'test-index', number_of_shards: 3, number_of_replicas: 2) end @@ -1748,19 +1748,11 @@ def expect_is_es_licensed end context 'when multiple index settings exist' do - before do - create(:elastic_index_setting, alias_name: 'index-alpha', number_of_shards: 5, number_of_replicas: 1) - create(:elastic_index_setting, alias_name: 'index-beta', number_of_shards: 10, number_of_replicas: 3) - create(:elastic_index_setting, alias_name: 'index-gamma', number_of_shards: 2, number_of_replicas: 0) - end + let_it_be(:index_alpha) { create(:elastic_index_setting, alias_name: 'index-alpha') } + let_it_be(:index_beta) { create(:elastic_index_setting, alias_name: 'index-beta') } + let_it_be(:index_gamma) { create(:elastic_index_setting, alias_name: 'index-gamma') } - it 'returns all index settings ordered by name' do - expect(elasticsearch_index_settings).to match_array([ - have_attributes(alias_name: 'index-alpha'), - have_attributes(alias_name: 'index-beta'), - have_attributes(alias_name: 'index-gamma') - ]) - end + it { is_expected.to eq [index_alpha, index_beta, index_gamma] } end end diff --git a/ee/spec/requests/api/settings_spec.rb b/ee/spec/requests/api/settings_spec.rb index 3e5363e2fe2383..3f810f7108a2d5 100644 --- a/ee/spec/requests/api/settings_spec.rb +++ b/ee/spec/requests/api/settings_spec.rb @@ -294,7 +294,7 @@ end context 'with elasticsearch_index_settings' do - let_it_be(:index_setting) do + let_it_be(:_index_setting) do create(:elastic_index_setting, alias_name: 'api-test-index', number_of_shards: 5, number_of_replicas: 1) end -- GitLab