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
# Returns a list of services that should be hidden from the list
def service_exceptions
exceptions = @project.disabled_services.dup
exceptions << slack_service
end
def slack_service
if current_application_settings.slack_app_enabled
['slack_slash_commands']
'slack_slash_commands'
else
['gitlab_slack_application']
'gitlab_slack_application'
end
end
end
......
......@@ -454,6 +454,27 @@ module EE
).create
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
def licensed_feature_available?(feature)
......
......@@ -17,6 +17,7 @@ class License < ActiveRecord::Base
ISSUE_BOARD_FOCUS_MODE_FEATURE = 'GitLab_IssueBoardFocusMode'.freeze
ISSUE_BOARD_MILESTONE_FEATURE = 'GitLab_IssueBoardMilestone'.freeze
ISSUE_WEIGHTS_FEATURE = 'GitLab_IssueWeights'.freeze
JENKINS_INTEGRATION_FEATURE = 'GitLab_JenkinsIntegration'.freeze
MERGE_REQUEST_APPROVERS_FEATURE = 'GitLab_MergeRequestApprovers'.freeze
MERGE_REQUEST_REBASE_FEATURE = 'GitLab_MergeRequestRebase'.freeze
MERGE_REQUEST_SQUASH_FEATURE = 'GitLab_MergeRequestSquash'.freeze
......@@ -54,6 +55,7 @@ class License < ActiveRecord::Base
issue_board_focus_mode: ISSUE_BOARD_FOCUS_MODE_FEATURE,
issue_board_milestone: ISSUE_BOARD_MILESTONE_FEATURE,
issue_weights: ISSUE_WEIGHTS_FEATURE,
jenkins_integration: JENKINS_INTEGRATION_FEATURE,
merge_request_approvers: MERGE_REQUEST_APPROVERS_FEATURE,
merge_request_rebase: MERGE_REQUEST_REBASE_FEATURE,
merge_request_squash: MERGE_REQUEST_SQUASH_FEATURE,
......@@ -80,6 +82,7 @@ class License < ActiveRecord::Base
{ ISSUE_BOARD_FOCUS_MODE_FEATURE => 1 },
{ ISSUE_BOARD_MILESTONE_FEATURE => 1 },
{ ISSUE_WEIGHTS_FEATURE => 1 },
{ JENKINS_INTEGRATION_FEATURE => 1 },
{ MERGE_REQUEST_APPROVERS_FEATURE => 1 },
{ MERGE_REQUEST_REBASE_FEATURE => 1 },
{ MERGE_REQUEST_SQUASH_FEATURE => 1 },
......
......@@ -9,8 +9,6 @@ class JenkinsDeprecatedService < CiService
validates :project_url, presence: true, if: :activated?
delegate :execute, to: :service_hook, prefix: nil
after_save :compose_service_hook, if: :activated?
def compose_service_hook
......@@ -20,6 +18,12 @@ class JenkinsDeprecatedService < CiService
hook.save
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
'Jenkins CI (Deprecated)'
end
......
......@@ -28,6 +28,7 @@ class JenkinsService < CiService
end
def execute(data)
return if project.disabled_services.include?(to_param)
return unless supported_events.include?(data[:object_kind])
service_hook.execute(data, "#{data[:object_kind]}_hook")
......
require 'spec_helper'
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) }
before do
......@@ -18,14 +19,31 @@ describe Projects::Settings::IntegrationsController do
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
let(:active_services) { assigns(:services).map(&:type) }
let(:disabled_services) { %w(JenkinsService JenkinsDeprecatedService) }
it 'enables SlackSlashCommandsService and disables GitlabSlackApplication' do
get :show, namespace_id: project.namespace, project_id: project
services = assigns(:services).map(&:type)
expect(services).to include('SlackSlashCommandsService')
expect(services).not_to include('GitlabSlackApplicationService')
expect(active_services).to include('SlackSlashCommandsService')
expect(active_services).not_to include('GitlabSlackApplicationService')
end
it 'enables GitlabSlackApplication and disables SlackSlashCommandsService' do
......@@ -34,10 +52,42 @@ describe Projects::Settings::IntegrationsController do
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')
expect(services).not_to include('SlackSlashCommandsService')
it_behaves_like 'endpoint without disabled services'
end
end
end
end
......@@ -714,4 +714,56 @@ describe Project, models: true do
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
......@@ -103,4 +103,65 @@ eos
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
......@@ -136,35 +136,83 @@ describe JenkinsService do
end
end
describe '#execute' do
it 'adds default web hook headers to the request' do
user = create(:user, username: 'username')
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)
shared_examples 'project with disabled Jenkins service' do
it 'does not invoke the Jenkins API' do
jenkins_service.execute(push_sample_data)
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)
expect(
a_request(:post, jenkins_hook_url)
.with(headers: { 'X-Gitlab-Event' => 'Push Hook', 'Authorization' => jenkins_authorization })
).to have_been_made.once
expect(a_request(:post, jenkins_hook_url)).to have_been_made.once
end
end
it 'request url contains properly serialized username and password' do
user = create(:user, username: 'username')
project = create(:project, name: 'project')
push_sample_data = Gitlab::DataBuilder::Push.build_sample(project, user)
jenkins_service = described_class.create(jenkins_params)
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(jenkins_params) }
before do
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(
a_request(:post, 'http://jenkins.example.com/project/my_project')
.with(headers: { 'Authorization' => jenkins_authorization })
).to have_been_made.once
it 'adds default web hook headers to the request' do
jenkins_service.execute(push_sample_data)
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
......
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