diff --git a/doc/api/settings.md b/doc/api/settings.md index 0a755fe104d5ec3d86b0041a6d19c64f16a0621a..1def4f6e800a7e4b6bd11f127f6ce2f973b1eb65 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 25b113db46c2f4ca7e0ecfe1be17a39f2508a553..1cfafbc4b48f2c43e0eb77bfc9c55f04eeb17f06 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 0000000000000000000000000000000000000000..3eddcc8be3ce0ab62a3a01c8a497aa51713c8043 --- /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 f25ba921754c127414b12c12a4de9b1ea4b68bbb..f8f68aeaba3ec28a505e64228694446e48448ffe 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 1b54705ca9721032d05c068980cd5befe0608f5d..d85def196122675701784eec45ccc7bddb3e713c 100644 --- a/ee/spec/models/application_setting_spec.rb +++ b/ee/spec/models/application_setting_spec.rb @@ -1728,6 +1728,34 @@ 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_it_be(:_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 + 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 { is_expected.to eq [index_alpha, index_beta, index_gamma] } + 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 8f590a59841f50c4d49fcc6e39afa7269488533e..3f810f7108a2d51d36ad27ba9150aba550bd2444 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