summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--app/services/self_monitoring/project/create_service.rb219
-rw-r--r--changelogs/unreleased/56883-migration.yml5
-rw-r--r--db/post_migrate/20190801072937_add_gitlab_instance_administration_project.rb16
-rw-r--r--db/post_migrate/20190809072552_set_self_monitoring_project_alerting_token.rb73
-rw-r--r--lib/gitlab/database_importers/self_monitoring/project/create_service.rb251
-rw-r--r--locale/gitlab.pot30
-rw-r--r--spec/lib/gitlab/database_importers/self_monitoring/project/create_service_spec.rb (renamed from spec/services/self_monitoring/project/create_service_spec.rb)123
-rw-r--r--spec/migrations/add_gitlab_instance_administration_project_spec.rb252
-rw-r--r--spec/support/helpers/stub_configuration.rb4
9 files changed, 709 insertions, 264 deletions
diff --git a/app/services/self_monitoring/project/create_service.rb b/app/services/self_monitoring/project/create_service.rb
deleted file mode 100644
index c925c6a1610..00000000000
--- a/app/services/self_monitoring/project/create_service.rb
+++ /dev/null
@@ -1,219 +0,0 @@
-# frozen_string_literal: true
-
-module SelfMonitoring
- module Project
- class CreateService < ::BaseService
- include Stepable
- include Gitlab::Utils::StrongMemoize
-
- VISIBILITY_LEVEL = Gitlab::VisibilityLevel::INTERNAL
- PROJECT_NAME = 'GitLab Instance Administration'
- PROJECT_DESCRIPTION = <<~HEREDOC
- This project is automatically generated and will be used to help monitor this GitLab instance.
- HEREDOC
-
- GROUP_NAME = 'GitLab Instance Administrators'
- GROUP_PATH = 'gitlab-instance-administrators'
-
- steps :validate_admins,
- :create_group,
- :create_project,
- :save_project_id,
- :add_group_members,
- :add_to_whitelist,
- :add_prometheus_manual_configuration
-
- def initialize
- super(nil)
- end
-
- def execute
- execute_steps
- end
-
- private
-
- def validate_admins
- unless instance_admins.any?
- log_error('No active admin user found')
- return error('No active admin user found')
- end
-
- success
- end
-
- def create_group
- if project_created?
- log_info(_('Instance administrators group already exists'))
- @group = application_settings.instance_administration_project.owner
- return success(group: @group)
- end
-
- admin_user = group_owner
- @group = ::Groups::CreateService.new(admin_user, create_group_params).execute
-
- if @group.persisted?
- success(group: @group)
- else
- error('Could not create group')
- end
- end
-
- def create_project
- if project_created?
- log_info(_('Instance administration project already exists'))
- @project = application_settings.instance_administration_project
- return success(project: project)
- end
-
- admin_user = group_owner
- @project = ::Projects::CreateService.new(admin_user, create_project_params).execute
-
- if project.persisted?
- success(project: project)
- else
- log_error(_("Could not create instance administration project. Errors: %{errors}") % { errors: project.errors.full_messages })
- error(_('Could not create project'))
- end
- end
-
- def save_project_id
- return success if project_created?
-
- result = ApplicationSettings::UpdateService.new(
- application_settings,
- group_owner,
- { instance_administration_project_id: @project.id }
- ).execute
-
- if result
- success
- else
- log_error(_("Could not save instance administration project ID, errors: %{errors}") % { errors: application_settings.errors.full_messages })
- error(_('Could not save project ID'))
- end
- end
-
- def add_group_members
- members = @group.add_users(group_maintainers, Gitlab::Access::MAINTAINER)
- errors = members.flat_map { |member| member.errors.full_messages }
-
- if errors.any?
- log_error("Could not add admins as members to self-monitoring project. Errors: #{errors}")
- error('Could not add admins as members')
- else
- success
- end
- end
-
- def add_to_whitelist
- return success unless prometheus_enabled?
- return success unless prometheus_listen_address.present?
-
- uri = parse_url(internal_prometheus_listen_address_uri)
- return error(_('Prometheus listen_address is not a valid URI')) unless uri
-
- result = ApplicationSettings::UpdateService.new(
- application_settings,
- group_owner,
- add_to_outbound_local_requests_whitelist: [uri.normalized_host]
- ).execute
-
- if result
- success
- else
- log_error(_("Could not add prometheus URL to whitelist, errors: %{errors}") % { errors: application_settings.errors.full_messages })
- error(_('Could not add prometheus URL to whitelist'))
- end
- end
-
- def add_prometheus_manual_configuration
- return success unless prometheus_enabled?
- return success unless prometheus_listen_address.present?
-
- service = project.find_or_initialize_service('prometheus')
-
- unless service.update(prometheus_service_attributes)
- log_error("Could not save prometheus manual configuration for self-monitoring project. Errors: #{service.errors.full_messages}")
- return error('Could not save prometheus manual configuration')
- end
-
- success
- end
-
- def application_settings
- strong_memoize(:application_settings) do
- Gitlab::CurrentSettings.expire_current_application_settings
- Gitlab::CurrentSettings.current_application_settings
- end
- end
-
- def project_created?
- application_settings.instance_administration_project.present?
- end
-
- def parse_url(uri_string)
- Addressable::URI.parse(uri_string)
- rescue Addressable::URI::InvalidURIError, TypeError
- end
-
- def prometheus_enabled?
- Gitlab.config.prometheus.enable
- rescue Settingslogic::MissingSetting
- false
- end
-
- def prometheus_listen_address
- Gitlab.config.prometheus.listen_address
- rescue Settingslogic::MissingSetting
- end
-
- def instance_admins
- @instance_admins ||= User.admins.active
- end
-
- def group_owner
- instance_admins.first
- end
-
- def group_maintainers
- # Exclude the first so that the group_owner is not added again as a member.
- instance_admins - [group_owner]
- end
-
- def create_group_params
- {
- name: GROUP_NAME,
- path: "#{GROUP_PATH}-#{SecureRandom.hex(4)}",
- visibility_level: VISIBILITY_LEVEL
- }
- end
-
- def create_project_params
- {
- initialize_with_readme: true,
- visibility_level: VISIBILITY_LEVEL,
- name: PROJECT_NAME,
- description: PROJECT_DESCRIPTION,
- namespace_id: @group.id
- }
- end
-
- def internal_prometheus_listen_address_uri
- if prometheus_listen_address.starts_with?('http')
- prometheus_listen_address
- else
- 'http://' + prometheus_listen_address
- end
- end
-
- def prometheus_service_attributes
- {
- api_url: internal_prometheus_listen_address_uri,
- manual_configuration: true,
- active: true
- }
- end
- end
- end
-end
diff --git a/changelogs/unreleased/56883-migration.yml b/changelogs/unreleased/56883-migration.yml
new file mode 100644
index 00000000000..d6eb49e62e1
--- /dev/null
+++ b/changelogs/unreleased/56883-migration.yml
@@ -0,0 +1,5 @@
+---
+title: Create a project for self-monitoring the GitLab instance
+merge_request: 31389
+author:
+type: added
diff --git a/db/post_migrate/20190801072937_add_gitlab_instance_administration_project.rb b/db/post_migrate/20190801072937_add_gitlab_instance_administration_project.rb
new file mode 100644
index 00000000000..8b2cf7b3d76
--- /dev/null
+++ b/db/post_migrate/20190801072937_add_gitlab_instance_administration_project.rb
@@ -0,0 +1,16 @@
+# frozen_string_literal: true
+
+class AddGitlabInstanceAdministrationProject < ActiveRecord::Migration[5.2]
+ DOWNTIME = false
+
+ def up
+ Gitlab::DatabaseImporters::SelfMonitoring::Project::CreateService.new.execute!
+ end
+
+ def down
+ ApplicationSetting.current_without_cache
+ &.instance_administration_project
+ &.owner
+ &.destroy!
+ end
+end
diff --git a/db/post_migrate/20190809072552_set_self_monitoring_project_alerting_token.rb b/db/post_migrate/20190809072552_set_self_monitoring_project_alerting_token.rb
new file mode 100644
index 00000000000..0c4faebc548
--- /dev/null
+++ b/db/post_migrate/20190809072552_set_self_monitoring_project_alerting_token.rb
@@ -0,0 +1,73 @@
+# frozen_string_literal: true
+
+class SetSelfMonitoringProjectAlertingToken < ActiveRecord::Migration[5.2]
+ DOWNTIME = false
+
+ module Migratable
+ module Alerting
+ class ProjectAlertingSetting < ApplicationRecord
+ self.table_name = 'project_alerting_settings'
+
+ belongs_to :project
+
+ validates :token, presence: true
+
+ attr_encrypted :token,
+ mode: :per_attribute_iv,
+ key: Settings.attr_encrypted_db_key_base_truncated,
+ algorithm: 'aes-256-gcm'
+
+ before_validation :ensure_token
+
+ private
+
+ def ensure_token
+ self.token ||= generate_token
+ end
+
+ def generate_token
+ SecureRandom.hex
+ end
+ end
+ end
+
+ class Project < ApplicationRecord
+ has_one :alerting_setting, inverse_of: :project, class_name: 'Alerting::ProjectAlertingSetting'
+ end
+
+ class ApplicationSetting < ApplicationRecord
+ self.table_name = 'application_settings'
+
+ belongs_to :instance_administration_project, class_name: 'Project'
+
+ def self.current_without_cache
+ last
+ end
+ end
+ end
+
+ def setup_alertmanager_token(project)
+ return unless License.feature_available?(:prometheus_alerts)
+
+ project.create_alerting_setting!
+ end
+
+ def up
+ Gitlab.ee do
+ project = Migratable::ApplicationSetting.current_without_cache&.instance_administration_project
+
+ if project
+ setup_alertmanager_token(project)
+ end
+ end
+ end
+
+ def down
+ Gitlab.ee do
+ Migratable::ApplicationSetting.current_without_cache
+ &.instance_administration_project
+ &.alerting_setting
+ &.destroy!
+ end
+ end
+end
diff --git a/lib/gitlab/database_importers/self_monitoring/project/create_service.rb b/lib/gitlab/database_importers/self_monitoring/project/create_service.rb
new file mode 100644
index 00000000000..164854e1e1a
--- /dev/null
+++ b/lib/gitlab/database_importers/self_monitoring/project/create_service.rb
@@ -0,0 +1,251 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module DatabaseImporters
+ module SelfMonitoring
+ module Project
+ include Stepable
+
+ class CreateService < ::BaseService
+ include Stepable
+
+ STEPS_ALLOWED_TO_FAIL = [
+ :validate_application_settings, :validate_project_created, :validate_admins
+ ].freeze
+
+ VISIBILITY_LEVEL = Gitlab::VisibilityLevel::INTERNAL
+ PROJECT_NAME = 'GitLab Instance Administration'
+
+ steps :validate_application_settings,
+ :validate_project_created,
+ :validate_admins,
+ :create_group,
+ :create_project,
+ :save_project_id,
+ :add_group_members,
+ :add_to_whitelist,
+ :add_prometheus_manual_configuration
+
+ def initialize
+ super(nil)
+ end
+
+ def execute!
+ result = execute_steps
+
+ if result[:status] == :success
+ result
+ elsif STEPS_ALLOWED_TO_FAIL.include?(result[:failed_step])
+ success
+ else
+ raise StandardError, result[:message]
+ end
+ end
+
+ private
+
+ def validate_application_settings
+ return success if application_settings
+
+ log_error(_('No application_settings found'))
+ error(_('No application_settings found'))
+ end
+
+ def validate_project_created
+ return success unless project_created?
+
+ log_error(_('Project already created'))
+ error(_('Project already created'))
+ end
+
+ def validate_admins
+ unless instance_admins.any?
+ log_error(_('No active admin user found'))
+ return error(_('No active admin user found'))
+ end
+
+ success
+ end
+
+ def create_group
+ if project_created?
+ log_info(_('Instance administrators group already exists'))
+ @group = application_settings.instance_administration_project.owner
+ return success(group: @group)
+ end
+
+ @group = ::Groups::CreateService.new(group_owner, create_group_params).execute
+
+ if @group.persisted?
+ success(group: @group)
+ else
+ error(_('Could not create group'))
+ end
+ end
+
+ def create_project
+ if project_created?
+ log_info(_('Instance administration project already exists'))
+ @project = application_settings.instance_administration_project
+ return success(project: project)
+ end
+
+ @project = ::Projects::CreateService.new(group_owner, create_project_params).execute
+
+ if project.persisted?
+ success(project: project)
+ else
+ log_error(_("Could not create instance administration project. Errors: %{errors}") % { errors: project.errors.full_messages })
+ error(_('Could not create project'))
+ end
+ end
+
+ def save_project_id
+ return success if project_created?
+
+ result = application_settings.update(instance_administration_project_id: @project.id)
+
+ if result
+ success
+ else
+ log_error(_("Could not save instance administration project ID, errors: %{errors}") % { errors: application_settings.errors.full_messages })
+ error(_('Could not save project ID'))
+ end
+ end
+
+ def add_group_members
+ members = @group.add_users(members_to_add, Gitlab::Access::MAINTAINER)
+ errors = members.flat_map { |member| member.errors.full_messages }
+
+ if errors.any?
+ log_error(_('Could not add admins as members to self-monitoring project. Errors: %{errors}') % { errors: errors })
+ error(_('Could not add admins as members'))
+ else
+ success
+ end
+ end
+
+ def add_to_whitelist
+ return success unless prometheus_enabled?
+ return success unless prometheus_listen_address.present?
+
+ uri = parse_url(internal_prometheus_listen_address_uri)
+ return error(_('Prometheus listen_address is not a valid URI')) unless uri
+
+ application_settings.add_to_outbound_local_requests_whitelist([uri.normalized_host])
+ result = application_settings.save
+
+ if result
+ # Expire the Gitlab::CurrentSettings cache after updating the whitelist.
+ # This happens automatically in an after_commit hook, but in migrations,
+ # the after_commit hook only runs at the end of the migration.
+ Gitlab::CurrentSettings.expire_current_application_settings
+ success
+ else
+ log_error(_("Could not add prometheus URL to whitelist, errors: %{errors}") % { errors: application_settings.errors.full_messages })
+ error(_('Could not add prometheus URL to whitelist'))
+ end
+ end
+
+ def add_prometheus_manual_configuration
+ return success unless prometheus_enabled?
+ return success unless prometheus_listen_address.present?
+
+ service = project.find_or_initialize_service('prometheus')
+
+ unless service.update(prometheus_service_attributes)
+ log_error(_('Could not save prometheus manual configuration for self-monitoring project. Errors: %{errors}') % { errors: service.errors.full_messages })
+ return error(_('Could not save prometheus manual configuration'))
+ end
+
+ success
+ end
+
+ def application_settings
+ @application_settings ||= ApplicationSetting.current_without_cache
+ end
+
+ def project_created?
+ application_settings.instance_administration_project.present?
+ end
+
+ def parse_url(uri_string)
+ Addressable::URI.parse(uri_string)
+ rescue Addressable::URI::InvalidURIError, TypeError
+ end
+
+ def prometheus_enabled?
+ Gitlab.config.prometheus.enable
+ rescue Settingslogic::MissingSetting
+ log_error(_('prometheus.enable is not present in gitlab.yml'))
+
+ false
+ end
+
+ def prometheus_listen_address
+ Gitlab.config.prometheus.listen_address
+ rescue Settingslogic::MissingSetting
+ log_error(_('prometheus.listen_address is not present in gitlab.yml'))
+
+ nil
+ end
+
+ def instance_admins
+ @instance_admins ||= User.admins.active
+ end
+
+ def group_owner
+ instance_admins.first
+ end
+
+ def members_to_add
+ # Exclude admins who are already members of group because
+ # `@group.add_users(users)` returns an error if the users parameter contains
+ # users who are already members of the group.
+ instance_admins - @group.members.collect(&:user)
+ end
+
+ def create_group_params
+ {
+ name: 'GitLab Instance Administrators',
+ path: "gitlab-instance-administrators-#{SecureRandom.hex(4)}",
+ visibility_level: VISIBILITY_LEVEL
+ }
+ end
+
+ def docs_path
+ Rails.application.routes.url_helpers.help_page_path(
+ 'administration/monitoring/gitlab_instance_administration_project/index'
+ )
+ end
+
+ def create_project_params
+ {
+ initialize_with_readme: true,
+ visibility_level: VISIBILITY_LEVEL,
+ name: PROJECT_NAME,
+ description: "This project is automatically generated and will be used to help monitor this GitLab instance. [More information](#{docs_path})",
+ namespace_id: @group.id
+ }
+ end
+
+ def internal_prometheus_listen_address_uri
+ if prometheus_listen_address.starts_with?('http')
+ prometheus_listen_address
+ else
+ 'http://' + prometheus_listen_address
+ end
+ end
+
+ def prometheus_service_attributes
+ {
+ api_url: internal_prometheus_listen_address_uri,
+ manual_configuration: true,
+ active: true
+ }
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index f8cebc22d93..61642fbbd59 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -3353,6 +3353,12 @@ msgstr ""
msgid "Copy token to clipboard"
msgstr ""
+msgid "Could not add admins as members"
+msgstr ""
+
+msgid "Could not add admins as members to self-monitoring project. Errors: %{errors}"
+msgstr ""
+
msgid "Could not add prometheus URL to whitelist"
msgstr ""
@@ -3371,6 +3377,9 @@ msgstr ""
msgid "Could not create Wiki Repository at this time. Please try again later."
msgstr ""
+msgid "Could not create group"
+msgstr ""
+
msgid "Could not create instance administration project. Errors: %{errors}"
msgstr ""
@@ -3398,6 +3407,12 @@ msgstr ""
msgid "Could not save project ID"
msgstr ""
+msgid "Could not save prometheus manual configuration"
+msgstr ""
+
+msgid "Could not save prometheus manual configuration for self-monitoring project. Errors: %{errors}"
+msgstr ""
+
msgid "Coverage"
msgstr ""
@@ -7400,9 +7415,15 @@ msgstr ""
msgid "No Tag"
msgstr ""
+msgid "No active admin user found"
+msgstr ""
+
msgid "No activities found"
msgstr ""
+msgid "No application_settings found"
+msgstr ""
+
msgid "No available namespaces to fork the project."
msgstr ""
@@ -8746,6 +8767,9 @@ msgstr ""
msgid "Project access must be granted explicitly to each user."
msgstr ""
+msgid "Project already created"
+msgstr ""
+
msgid "Project and wiki repositories"
msgstr ""
@@ -13961,6 +13985,12 @@ msgstr ""
msgid "project avatar"
msgstr ""
+msgid "prometheus.enable is not present in gitlab.yml"
+msgstr ""
+
+msgid "prometheus.listen_address is not present in gitlab.yml"
+msgstr ""
+
msgid "quick actions"
msgstr ""
diff --git a/spec/services/self_monitoring/project/create_service_spec.rb b/spec/lib/gitlab/database_importers/self_monitoring/project/create_service_spec.rb
index def20448bd9..9bd0d800086 100644
--- a/spec/services/self_monitoring/project/create_service_spec.rb
+++ b/spec/lib/gitlab/database_importers/self_monitoring/project/create_service_spec.rb
@@ -2,29 +2,48 @@
require 'spec_helper'
-describe SelfMonitoring::Project::CreateService do
+describe Gitlab::DatabaseImporters::SelfMonitoring::Project::CreateService do
describe '#execute' do
- let(:result) { subject.execute }
+ let(:result) { subject.execute! }
let(:prometheus_settings) do
- OpenStruct.new(
+ {
enable: true,
listen_address: 'localhost:9090'
- )
+ }
end
before do
- allow(Gitlab.config).to receive(:prometheus).and_return(prometheus_settings)
+ stub_config(prometheus: prometheus_settings)
+ end
+
+ context 'without application_settings' do
+ it 'does not fail' do
+ expect(subject).to receive(:log_error).and_call_original
+ expect(result).to eq(
+ status: :success
+ )
+
+ expect(Project.count).to eq(0)
+ expect(Group.count).to eq(0)
+ end
end
context 'without admin users' do
- it 'returns error' do
+ let(:application_setting) { Gitlab::CurrentSettings.current_application_settings }
+
+ before do
+ allow(ApplicationSetting).to receive(:current_without_cache) { application_setting }
+ end
+
+ it 'does not fail' do
expect(subject).to receive(:log_error).and_call_original
expect(result).to eq(
- status: :error,
- message: 'No active admin user found',
- failed_step: :validate_admins
+ status: :success
)
+
+ expect(Project.count).to eq(0)
+ expect(Group.count).to eq(0)
end
end
@@ -36,6 +55,7 @@ describe SelfMonitoring::Project::CreateService do
let!(:user) { create(:user, :admin) }
before do
+ allow(ApplicationSetting).to receive(:current_without_cache) { application_setting }
application_setting.allow_local_requests_from_web_hooks_and_services = true
end
@@ -56,8 +76,8 @@ describe SelfMonitoring::Project::CreateService do
it 'creates group' do
expect(result[:status]).to eq(:success)
expect(group).to be_persisted
- expect(group.name).to eq(described_class::GROUP_NAME)
- expect(group.path).to start_with(described_class::GROUP_PATH)
+ expect(group.name).to eq('GitLab Instance Administrators')
+ expect(group.path).to start_with('gitlab-instance-administrators')
expect(group.path.split('-').last.length).to eq(8)
expect(group.visibility_level).to eq(described_class::VISIBILITY_LEVEL)
end
@@ -77,9 +97,16 @@ describe SelfMonitoring::Project::CreateService do
end
it 'creates project with correct name and description' do
+ path = 'administration/monitoring/gitlab_instance_administration_project/index'
+ docs_path = Rails.application.routes.url_helpers.help_page_path(path)
+
expect(result[:status]).to eq(:success)
expect(project.name).to eq(described_class::PROJECT_NAME)
- expect(project.description).to eq(described_class::PROJECT_DESCRIPTION)
+ expect(project.description).to eq(
+ 'This project is automatically generated and will be used to help monitor this GitLab instance. ' \
+ "[More information](#{docs_path})"
+ )
+ expect(File).to exist("doc/#{path}.md")
end
it 'adds all admins as maintainers' do
@@ -105,19 +132,30 @@ describe SelfMonitoring::Project::CreateService do
it 'returns error when saving project ID fails' do
allow(application_setting).to receive(:update) { false }
- expect(result[:status]).to eq(:error)
- expect(result[:failed_step]).to eq(:save_project_id)
- expect(result[:message]).to eq('Could not save project ID')
+ expect { result }.to raise_error(StandardError, 'Could not save project ID')
end
- it 'does not fail when a project already exists' do
- expect(result[:status]).to eq(:success)
+ context 'when project already exists' do
+ let(:existing_group) { create(:group) }
+ let(:existing_project) { create(:project, namespace: existing_group) }
- second_result = subject.execute
+ before do
+ admin1 = create(:user, :admin)
+ admin2 = create(:user, :admin)
+
+ existing_group.add_owner(user)
+ existing_group.add_users([admin1, admin2], Gitlab::Access::MAINTAINER)
+
+ application_setting.instance_administration_project_id = existing_project.id
+ end
+
+ it 'does not fail' do
+ expect(subject).to receive(:log_error).and_call_original
+ expect(result[:status]).to eq(:success)
- expect(second_result[:status]).to eq(:success)
- expect(second_result[:project]).to eq(project)
- expect(second_result[:group]).to eq(group)
+ expect(Project.count).to eq(1)
+ expect(Group.count).to eq(1)
+ end
end
context 'when local requests from hooks and services are not allowed' do
@@ -138,8 +176,11 @@ describe SelfMonitoring::Project::CreateService do
end
context 'with non default prometheus address' do
- before do
- prometheus_settings.listen_address = 'https://localhost:9090'
+ let(:prometheus_settings) do
+ {
+ enable: true,
+ listen_address: 'https://localhost:9090'
+ }
end
it_behaves_like 'has prometheus service', 'https://localhost:9090'
@@ -157,8 +198,11 @@ describe SelfMonitoring::Project::CreateService do
end
context 'when prometheus setting is disabled in gitlab.yml' do
- before do
- prometheus_settings.enable = false
+ let(:prometheus_settings) do
+ {
+ enable: false,
+ listen_address: 'http://localhost:9090'
+ }
end
it 'does not configure prometheus' do
@@ -168,9 +212,7 @@ describe SelfMonitoring::Project::CreateService do
end
context 'when prometheus listen address is blank in gitlab.yml' do
- before do
- prometheus_settings.listen_address = ''
- end
+ let(:prometheus_settings) { { enable: true, listen_address: '' } }
it 'does not configure prometheus' do
expect(result).to include(status: :success)
@@ -192,11 +234,7 @@ describe SelfMonitoring::Project::CreateService do
it 'returns error' do
expect(subject).to receive(:log_error).and_call_original
- expect(result).to eq({
- status: :error,
- message: 'Could not create project',
- failed_step: :create_project
- })
+ expect { result }.to raise_error(StandardError, 'Could not create project')
end
end
@@ -207,26 +245,21 @@ describe SelfMonitoring::Project::CreateService do
it 'returns error' do
expect(subject).to receive(:log_error).and_call_original
- expect(result).to eq({
- status: :error,
- message: 'Could not add admins as members',
- failed_step: :add_group_members
- })
+ expect { result }.to raise_error(StandardError, 'Could not add admins as members')
end
end
context 'when prometheus manual configuration cannot be saved' do
- before do
- prometheus_settings.listen_address = 'httpinvalid://localhost:9090'
+ let(:prometheus_settings) do
+ {
+ enable: true,
+ listen_address: 'httpinvalid://localhost:9090'
+ }
end
it 'returns error' do
expect(subject).to receive(:log_error).and_call_original
- expect(result).to eq(
- status: :error,
- message: 'Could not save prometheus manual configuration',
- failed_step: :add_prometheus_manual_configuration
- )
+ expect { result }.to raise_error(StandardError, 'Could not save prometheus manual configuration')
end
end
end
diff --git a/spec/migrations/add_gitlab_instance_administration_project_spec.rb b/spec/migrations/add_gitlab_instance_administration_project_spec.rb
new file mode 100644
index 00000000000..08e20a4e8ff
--- /dev/null
+++ b/spec/migrations/add_gitlab_instance_administration_project_spec.rb
@@ -0,0 +1,252 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+require Rails.root.join('db', 'post_migrate', '20190801072937_add_gitlab_instance_administration_project.rb')
+
+describe AddGitlabInstanceAdministrationProject, :migration do
+ let(:application_settings) { table(:application_settings) }
+ let(:users) { table(:users) }
+ let(:projects) { table(:projects) }
+ let(:namespaces) { table(:namespaces) }
+ let(:members) { table(:members) }
+
+ let(:service_class) do
+ Gitlab::DatabaseImporters::SelfMonitoring::Project::CreateService
+ end
+
+ let(:prometheus_settings) do
+ {
+ enable: true,
+ listen_address: 'localhost:9090'
+ }
+ end
+
+ before do
+ stub_env('IN_MEMORY_APPLICATION_SETTINGS', 'false')
+
+ stub_config(prometheus: prometheus_settings)
+ end
+
+ describe 'down' do
+ let!(:application_setting) { application_settings.create! }
+ let!(:user) { users.create!(admin: true, email: 'admin1@example.com', projects_limit: 10, state: :active) }
+
+ it 'deletes group and project' do
+ migrate!
+
+ expect(Project.count).to eq(1)
+ expect(Group.count).to eq(1)
+
+ schema_migrate_down!
+
+ expect(Project.count).to eq(0)
+ expect(Group.count).to eq(0)
+ end
+ end
+
+ describe 'up' do
+ context 'without application_settings' do
+ it 'does not fail' do
+ migrate!
+
+ expect(Project.count).to eq(0)
+ end
+ end
+
+ context 'without admin users' do
+ let!(:application_setting) { application_settings.create! }
+
+ it 'does not fail' do
+ migrate!
+
+ expect(Project.count).to eq(0)
+ end
+ end
+
+ context 'with admin users' do
+ let(:project) { Project.last }
+ let(:group) { Group.last }
+ let!(:application_setting) { application_settings.create! }
+ let!(:user) { users.create!(admin: true, email: 'admin1@example.com', projects_limit: 10, state: :active) }
+
+ before do
+ stub_application_setting(allow_local_requests_from_web_hooks_and_services: true)
+ end
+
+ shared_examples 'has prometheus service' do |listen_address|
+ it do
+ migrate!
+
+ prometheus = project.prometheus_service
+ expect(prometheus).to be_persisted
+ expect(prometheus).not_to eq(nil)
+ expect(prometheus.api_url).to eq(listen_address)
+ expect(prometheus.active).to eq(true)
+ expect(prometheus.manual_configuration).to eq(true)
+ end
+ end
+
+ it_behaves_like 'has prometheus service', 'http://localhost:9090'
+
+ it 'creates GitLab Instance Administrator group' do
+ migrate!
+
+ expect(group).to be_persisted
+ expect(group.name).to eq('GitLab Instance Administrators')
+ expect(group.path).to start_with('gitlab-instance-administrators')
+ expect(group.path.split('-').last.length).to eq(8)
+ expect(group.visibility_level).to eq(service_class::VISIBILITY_LEVEL)
+ end
+
+ it 'creates project with internal visibility' do
+ migrate!
+
+ expect(project.visibility_level).to eq(Gitlab::VisibilityLevel::INTERNAL)
+ expect(project).to be_persisted
+ end
+
+ it 'creates project with correct name and description' do
+ migrate!
+
+ path = 'administration/monitoring/gitlab_instance_administration_project/index'
+ docs_path = Rails.application.routes.url_helpers.help_page_path(path)
+
+ expect(project.name).to eq(service_class::PROJECT_NAME)
+ expect(project.description).to eq(
+ 'This project is automatically generated and will be used to help monitor this GitLab instance. ' \
+ "[More information](#{docs_path})"
+ )
+ expect(File).to exist("doc/#{path}.md")
+ end
+
+ it 'adds all admins as maintainers' do
+ admin1 = users.create!(admin: true, email: 'admin2@example.com', projects_limit: 10, state: :active)
+ admin2 = users.create!(admin: true, email: 'admin3@example.com', projects_limit: 10, state: :active)
+ users.create!(email: 'nonadmin1@example.com', projects_limit: 10, state: :active)
+
+ migrate!
+
+ expect(project.owner).to eq(group)
+ expect(group.members.collect(&:user).collect(&:id)).to contain_exactly(user.id, admin1.id, admin2.id)
+ expect(group.members.collect(&:access_level)).to contain_exactly(
+ Gitlab::Access::OWNER,
+ Gitlab::Access::MAINTAINER,
+ Gitlab::Access::MAINTAINER
+ )
+ end
+
+ it 'saves the project id' do
+ migrate!
+
+ application_setting.reload
+ expect(application_setting.instance_administration_project_id).to eq(project.id)
+ end
+
+ it 'does not fail when a project already exists' do
+ group = namespaces.create!(
+ path: 'gitlab-instance-administrators',
+ name: 'GitLab Instance Administrators',
+ type: 'Group'
+ )
+ project = projects.create!(
+ namespace_id: group.id,
+ name: 'GitLab Instance Administration'
+ )
+
+ admin1 = users.create!(admin: true, email: 'admin4@example.com', projects_limit: 10, state: :active)
+ admin2 = users.create!(admin: true, email: 'admin5@example.com', projects_limit: 10, state: :active)
+
+ members.create!(
+ user_id: admin1.id,
+ source_id: group.id,
+ source_type: 'Namespace',
+ type: 'GroupMember',
+ access_level: GroupMember::MAINTAINER,
+ notification_level: NotificationSetting.levels[:global]
+ )
+ members.create!(
+ user_id: admin2.id,
+ source_id: group.id,
+ source_type: 'Namespace',
+ type: 'GroupMember',
+ access_level: GroupMember::MAINTAINER,
+ notification_level: NotificationSetting.levels[:global]
+ )
+
+ stub_application_setting(instance_administration_project: project)
+
+ migrate!
+
+ expect(Project.last.id).to eq(project.id)
+ expect(Group.last.id).to eq(group.id)
+ end
+
+ context 'when local requests from hooks and services are not allowed' do
+ before do
+ stub_application_setting(allow_local_requests_from_web_hooks_and_services: false)
+ end
+
+ it_behaves_like 'has prometheus service', 'http://localhost:9090'
+
+ it 'does not overwrite the existing whitelist' do
+ application_setting.update!(outbound_local_requests_whitelist: ['example.com'])
+
+ migrate!
+
+ application_setting.reload
+ expect(application_setting.outbound_local_requests_whitelist).to contain_exactly(
+ 'example.com', 'localhost'
+ )
+ end
+ end
+
+ context 'with non default prometheus address' do
+ let(:prometheus_settings) do
+ {
+ enable: true,
+ listen_address: 'https://localhost:9090'
+ }
+ end
+
+ it_behaves_like 'has prometheus service', 'https://localhost:9090'
+ end
+
+ context 'when prometheus setting is not present in gitlab.yml' do
+ before do
+ allow(Gitlab.config).to receive(:prometheus).and_raise(Settingslogic::MissingSetting)
+ end
+
+ it 'does not fail' do
+ migrate!
+
+ expect(project.prometheus_service).to be_nil
+ end
+ end
+
+ context 'when prometheus setting is disabled in gitlab.yml' do
+ let(:prometheus_settings) do
+ {
+ enable: false,
+ listen_address: 'localhost:9090'
+ }
+ end
+
+ it 'does not configure prometheus' do
+ migrate!
+
+ expect(project.prometheus_service).to be_nil
+ end
+ end
+
+ context 'when prometheus listen address is blank in gitlab.yml' do
+ let(:prometheus_settings) { { enable: true, listen_address: '' } }
+
+ it 'does not configure prometheus' do
+ migrate!
+
+ expect(project.prometheus_service).to be_nil
+ end
+ end
+ end
+ end
+end
diff --git a/spec/support/helpers/stub_configuration.rb b/spec/support/helpers/stub_configuration.rb
index c8b2bf040e6..dec7898d8d2 100644
--- a/spec/support/helpers/stub_configuration.rb
+++ b/spec/support/helpers/stub_configuration.rb
@@ -30,6 +30,10 @@ module StubConfiguration
allow(Gitlab.config.gitlab).to receive_messages(to_settings(messages))
end
+ def stub_config(messages)
+ allow(Gitlab.config).to receive_messages(to_settings(messages))
+ end
+
def stub_default_url_options(host: "localhost", protocol: "http")
url_options = { host: host, protocol: protocol }
allow(Rails.application.routes).to receive(:default_url_options).and_return(url_options)