Commit a5fceaa9 authored by Rémy Coutable's avatar Rémy Coutable

Merge branch '2847-jenkins-license-checks' into 'master'

Check license before showing Jenkins in list of integrations

Closes #2847

See merge request !2459
parents 0fa728f9 42b5554c
...@@ -18,10 +18,16 @@ module Projects ...@@ -18,10 +18,16 @@ module Projects
# Returns a list of services that should be hidden from the list # Returns a list of services that should be hidden from the list
def service_exceptions def service_exceptions
exceptions = @project.disabled_services.dup
exceptions << slack_service
end
def slack_service
if current_application_settings.slack_app_enabled if current_application_settings.slack_app_enabled
['slack_slash_commands'] 'slack_slash_commands'
else else
['gitlab_slack_application'] 'gitlab_slack_application'
end end
end end
end end
......
...@@ -454,6 +454,27 @@ module EE ...@@ -454,6 +454,27 @@ module EE
).create ).create
end end
# Override to reject disabled services
def find_or_initialize_services(exceptions: [])
available_services = super
available_services.reject do |service|
disabled_services.include?(service.to_param)
end
end
def disabled_services
return @disabled_services if defined?(@disabled_services)
@disabled_services = []
unless feature_available?(:jenkins_integration)
@disabled_services.push('jenkins', 'jenkins_deprecated')
end
@disabled_services
end
private private
def licensed_feature_available?(feature) def licensed_feature_available?(feature)
......
...@@ -17,6 +17,7 @@ class License < ActiveRecord::Base ...@@ -17,6 +17,7 @@ class License < ActiveRecord::Base
ISSUE_BOARD_FOCUS_MODE_FEATURE = 'GitLab_IssueBoardFocusMode'.freeze ISSUE_BOARD_FOCUS_MODE_FEATURE = 'GitLab_IssueBoardFocusMode'.freeze
ISSUE_BOARD_MILESTONE_FEATURE = 'GitLab_IssueBoardMilestone'.freeze ISSUE_BOARD_MILESTONE_FEATURE = 'GitLab_IssueBoardMilestone'.freeze
ISSUE_WEIGHTS_FEATURE = 'GitLab_IssueWeights'.freeze ISSUE_WEIGHTS_FEATURE = 'GitLab_IssueWeights'.freeze
JENKINS_INTEGRATION_FEATURE = 'GitLab_JenkinsIntegration'.freeze
MERGE_REQUEST_APPROVERS_FEATURE = 'GitLab_MergeRequestApprovers'.freeze MERGE_REQUEST_APPROVERS_FEATURE = 'GitLab_MergeRequestApprovers'.freeze
MERGE_REQUEST_REBASE_FEATURE = 'GitLab_MergeRequestRebase'.freeze MERGE_REQUEST_REBASE_FEATURE = 'GitLab_MergeRequestRebase'.freeze
MERGE_REQUEST_SQUASH_FEATURE = 'GitLab_MergeRequestSquash'.freeze MERGE_REQUEST_SQUASH_FEATURE = 'GitLab_MergeRequestSquash'.freeze
...@@ -54,6 +55,7 @@ class License < ActiveRecord::Base ...@@ -54,6 +55,7 @@ class License < ActiveRecord::Base
issue_board_focus_mode: ISSUE_BOARD_FOCUS_MODE_FEATURE, issue_board_focus_mode: ISSUE_BOARD_FOCUS_MODE_FEATURE,
issue_board_milestone: ISSUE_BOARD_MILESTONE_FEATURE, issue_board_milestone: ISSUE_BOARD_MILESTONE_FEATURE,
issue_weights: ISSUE_WEIGHTS_FEATURE, issue_weights: ISSUE_WEIGHTS_FEATURE,
jenkins_integration: JENKINS_INTEGRATION_FEATURE,
merge_request_approvers: MERGE_REQUEST_APPROVERS_FEATURE, merge_request_approvers: MERGE_REQUEST_APPROVERS_FEATURE,
merge_request_rebase: MERGE_REQUEST_REBASE_FEATURE, merge_request_rebase: MERGE_REQUEST_REBASE_FEATURE,
merge_request_squash: MERGE_REQUEST_SQUASH_FEATURE, merge_request_squash: MERGE_REQUEST_SQUASH_FEATURE,
...@@ -80,6 +82,7 @@ class License < ActiveRecord::Base ...@@ -80,6 +82,7 @@ class License < ActiveRecord::Base
{ ISSUE_BOARD_FOCUS_MODE_FEATURE => 1 }, { ISSUE_BOARD_FOCUS_MODE_FEATURE => 1 },
{ ISSUE_BOARD_MILESTONE_FEATURE => 1 }, { ISSUE_BOARD_MILESTONE_FEATURE => 1 },
{ ISSUE_WEIGHTS_FEATURE => 1 }, { ISSUE_WEIGHTS_FEATURE => 1 },
{ JENKINS_INTEGRATION_FEATURE => 1 },
{ MERGE_REQUEST_APPROVERS_FEATURE => 1 }, { MERGE_REQUEST_APPROVERS_FEATURE => 1 },
{ MERGE_REQUEST_REBASE_FEATURE => 1 }, { MERGE_REQUEST_REBASE_FEATURE => 1 },
{ MERGE_REQUEST_SQUASH_FEATURE => 1 }, { MERGE_REQUEST_SQUASH_FEATURE => 1 },
......
...@@ -9,8 +9,6 @@ class JenkinsDeprecatedService < CiService ...@@ -9,8 +9,6 @@ class JenkinsDeprecatedService < CiService
validates :project_url, presence: true, if: :activated? validates :project_url, presence: true, if: :activated?
delegate :execute, to: :service_hook, prefix: nil
after_save :compose_service_hook, if: :activated? after_save :compose_service_hook, if: :activated?
def compose_service_hook def compose_service_hook
...@@ -20,6 +18,12 @@ class JenkinsDeprecatedService < CiService ...@@ -20,6 +18,12 @@ class JenkinsDeprecatedService < CiService
hook.save hook.save
end end
def execute(data, hook_name = 'service_hook')
return if project.disabled_services.include?(to_param)
service_hook.execute(data, hook_name)
end
def title def title
'Jenkins CI (Deprecated)' 'Jenkins CI (Deprecated)'
end end
......
...@@ -28,6 +28,7 @@ class JenkinsService < CiService ...@@ -28,6 +28,7 @@ class JenkinsService < CiService
end end
def execute(data) def execute(data)
return if project.disabled_services.include?(to_param)
return unless supported_events.include?(data[:object_kind]) return unless supported_events.include?(data[:object_kind])
service_hook.execute(data, "#{data[:object_kind]}_hook") service_hook.execute(data, "#{data[:object_kind]}_hook")
......
require 'spec_helper' require 'spec_helper'
describe Projects::Settings::IntegrationsController do describe Projects::Settings::IntegrationsController do
let(:project) { create(:empty_project, :public) } let(:namespace) { create(:group, :private) }
let(:project) { create(:empty_project, :private, namespace: namespace) }
let(:user) { create(:user) } let(:user) { create(:user) }
before do before do
...@@ -18,14 +19,31 @@ describe Projects::Settings::IntegrationsController do ...@@ -18,14 +19,31 @@ describe Projects::Settings::IntegrationsController do
end end
end end
shared_examples 'endpoint with some disabled services' do
it 'has some disabled services' do
get :show, namespace_id: project.namespace, project_id: project
expect(active_services).not_to include(*disabled_services)
end
end
shared_examples 'endpoint without disabled services' do
it 'does not have disabled services' do
get :show, namespace_id: project.namespace, project_id: project
expect(active_services).to include(*disabled_services)
end
end
context 'Sets correct services list' do context 'Sets correct services list' do
let(:active_services) { assigns(:services).map(&:type) }
let(:disabled_services) { %w(JenkinsService JenkinsDeprecatedService) }
it 'enables SlackSlashCommandsService and disables GitlabSlackApplication' do it 'enables SlackSlashCommandsService and disables GitlabSlackApplication' do
get :show, namespace_id: project.namespace, project_id: project get :show, namespace_id: project.namespace, project_id: project
services = assigns(:services).map(&:type) expect(active_services).to include('SlackSlashCommandsService')
expect(active_services).not_to include('GitlabSlackApplicationService')
expect(services).to include('SlackSlashCommandsService')
expect(services).not_to include('GitlabSlackApplicationService')
end end
it 'enables GitlabSlackApplication and disables SlackSlashCommandsService' do it 'enables GitlabSlackApplication and disables SlackSlashCommandsService' do
...@@ -34,10 +52,42 @@ describe Projects::Settings::IntegrationsController do ...@@ -34,10 +52,42 @@ describe Projects::Settings::IntegrationsController do
get :show, namespace_id: project.namespace, project_id: project get :show, namespace_id: project.namespace, project_id: project
services = assigns(:services).map(&:type) expect(active_services).to include('GitlabSlackApplicationService')
expect(active_services).not_to include('SlackSlashCommandsService')
end
context 'without a license key' do
before do
License.destroy_all
end
it_behaves_like 'endpoint with some disabled services'
end
context 'with a license key' do
context 'when checking of namespace plan is enabled' do
before do
allow_any_instance_of(Project).to receive_message_chain(:current_application_settings, :should_check_namespace_plan?) { true }
end
context 'and namespace does not have a plan' do
it_behaves_like 'endpoint with some disabled services'
end
context 'and namespace has a plan' do
let(:namespace) { create(:group, :private, plan: Namespace::BRONZE_PLAN) }
it_behaves_like 'endpoint without disabled services'
end
end
context 'when checking of namespace plan is not enabled' do
before do
allow_any_instance_of(Project).to receive_message_chain(:current_application_settings, :should_check_namespace_plan?) { false }
end
expect(services).to include('GitlabSlackApplicationService') it_behaves_like 'endpoint without disabled services'
expect(services).not_to include('SlackSlashCommandsService') end
end end
end end
end end
...@@ -714,4 +714,56 @@ describe Project, models: true do ...@@ -714,4 +714,56 @@ describe Project, models: true do
end end
end end
end end
shared_examples 'project with disabled services' do
it 'has some disabled services' do
expect(project.disabled_services).to match_array(disabled_services)
end
end
shared_examples 'project without disabled services' do
it 'has some disabled services' do
expect(project.disabled_services).to be_empty
end
end
describe '#disabled_services' do
let(:namespace) { create(:group, :private) }
let(:project) { create(:project, :private, namespace: namespace) }
let(:disabled_services) { %w(jenkins jenkins_deprecated) }
context 'without a license key' do
before do
License.destroy_all
end
it_behaves_like 'project with disabled services'
end
context 'with a license key' do
context 'when checking of namespace plan is enabled' do
before do
stub_application_setting_on_object(project, should_check_namespace_plan: true)
end
context 'and namespace does not have a plan' do
it_behaves_like 'project with disabled services'
end
context 'and namespace has a plan' do
let(:namespace) { create(:group, :private, plan: Namespace::BRONZE_PLAN) }
it_behaves_like 'project without disabled services'
end
end
context 'when checking of namespace plan is not enabled' do
before do
stub_application_setting_on_object(project, should_check_namespace_plan: false)
end
it_behaves_like 'project without disabled services'
end
end
end
end end
...@@ -103,4 +103,65 @@ eos ...@@ -103,4 +103,65 @@ eos
end end
end end
end end
shared_examples 'a disabled jenkins deprecated service' do
it 'does not invoke the service hook' do
expect_any_instance_of(ServiceHook).not_to receive(:execute)
jenkins_service.execute(push_sample_data)
end
end
shared_examples 'an enabled jenkins deprecated service' do
it 'invokes the service hook' do
expect_any_instance_of(ServiceHook).to receive(:execute)
jenkins_service.execute(push_sample_data)
end
end
describe '#execute' do
let(:user) { create(:user, username: 'username') }
let(:namespace) { create(:group, :private) }
let(:project) { create(:project, :private, name: 'project', namespace: namespace) }
let(:push_sample_data) { Gitlab::DataBuilder::Push.build_sample(project, user) }
let(:jenkins_service) { described_class.create(active: true, project: project) }
let!(:service_hook) { create(:service_hook, service: jenkins_service) }
context 'without a license key' do
before do
License.destroy_all
end
it_behaves_like 'a disabled jenkins deprecated service'
end
context 'with a license key' do
context 'when namespace plan check is not enabled' do
before do
stub_application_setting_on_object(project, should_check_namespace_plan: false)
end
it_behaves_like 'an enabled jenkins deprecated service'
end
context 'when namespace plan check is enabled' do
before do
stub_application_setting_on_object(project, should_check_namespace_plan: true)
end
context 'when namespace does not have a plan' do
let(:namespace) { create(:group, :private) }
it_behaves_like 'a disabled jenkins deprecated service'
end
context 'when namespace has a plan' do
let(:namespace) { create(:group, :private, plan: Namespace::BRONZE_PLAN) }
it_behaves_like 'an enabled jenkins deprecated service'
end
end
end
end
end end
...@@ -136,35 +136,83 @@ describe JenkinsService do ...@@ -136,35 +136,83 @@ describe JenkinsService do
end end
end end
describe '#execute' do shared_examples 'project with disabled Jenkins service' do
it 'adds default web hook headers to the request' do it 'does not invoke the Jenkins API' do
user = create(:user, username: 'username') jenkins_service.execute(push_sample_data)
project = create(:project, name: 'project')
push_sample_data = Gitlab::DataBuilder::Push.build_sample(project, user)
jenkins_service = described_class.create(jenkins_params)
stub_request(:post, jenkins_hook_url)
expect(a_request(:any, jenkins_hook_url)).not_to have_been_made
end
end
shared_examples 'project with enabled Jenkins service' do
it 'invokes the Jenkins API' do
jenkins_service.execute(push_sample_data) jenkins_service.execute(push_sample_data)
expect( expect(a_request(:post, jenkins_hook_url)).to have_been_made.once
a_request(:post, jenkins_hook_url)
.with(headers: { 'X-Gitlab-Event' => 'Push Hook', 'Authorization' => jenkins_authorization })
).to have_been_made.once
end end
end
it 'request url contains properly serialized username and password' do describe '#execute' do
user = create(:user, username: 'username') let(:user) { create(:user, username: 'username') }
project = create(:project, name: 'project') let(:namespace) { create(:group, :private) }
push_sample_data = Gitlab::DataBuilder::Push.build_sample(project, user) let(:project) { create(:project, :private, name: 'project', namespace: namespace) }
jenkins_service = described_class.create(jenkins_params) let(:push_sample_data) { Gitlab::DataBuilder::Push.build_sample(project, user) }
let(:jenkins_service) { described_class.create(jenkins_params) }
before do
stub_request(:post, jenkins_hook_url) stub_request(:post, jenkins_hook_url)
end
jenkins_service.execute(push_sample_data) context 'without a license key' do
before do
License.destroy_all
end
it_behaves_like 'project with disabled Jenkins service'
end
context 'with a license key' do
context 'when namespace plan check is not enabled' do
before do
stub_application_setting_on_object(project, should_check_namespace_plan: false)
end
it_behaves_like 'project with enabled Jenkins service'
end
context 'when namespace plan check is enabled' do
before do
stub_application_setting_on_object(project, should_check_namespace_plan: true)
end
context 'when namespace does not have a plan' do
let(:namespace) { create(:group, :private) }
it_behaves_like 'project with disabled Jenkins service'
end
context 'when namespace has a plan' do
let(:namespace) { create(:group, :private, plan: Namespace::BRONZE_PLAN) }
expect( it 'adds default web hook headers to the request' do
a_request(:post, 'http://jenkins.example.com/project/my_project') jenkins_service.execute(push_sample_data)
.with(headers: { 'Authorization' => jenkins_authorization })
).to have_been_made.once expect(
a_request(:post, jenkins_hook_url)
.with(headers: { 'X-Gitlab-Event' => 'Push Hook', 'Authorization' => jenkins_authorization })
).to have_been_made.once
end
it 'request url contains properly serialized username and password' do
jenkins_service.execute(push_sample_data)
expect(
a_request(:post, 'http://jenkins.example.com/project/my_project')
.with(headers: { 'Authorization' => jenkins_authorization })
).to have_been_made.once
end
end
end
end end
end end
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment