Commit cddaddb8 authored by GitLab Bot's avatar GitLab Bot

Add latest changes from gitlab-org/gitlab@master

parent 5afd8575
......@@ -50,7 +50,7 @@ docs lint:
- .default-retry
- .default-only
- .only:changes-docs
image: "registry.gitlab.com/gitlab-org/gitlab-build-images:gitlab-docs-lint"
image: "registry.gitlab.com/gitlab-org/gitlab-docs:docs-lint"
stage: test
dependencies: []
script:
......
......@@ -327,7 +327,7 @@ group :metrics do
gem 'influxdb', '~> 0.2', require: false
# Prometheus
gem 'prometheus-client-mmap', '~> 0.9.10'
gem 'prometheus-client-mmap', '~> 0.10.0'
gem 'raindrops', '~> 0.18'
end
......
......@@ -749,7 +749,7 @@ GEM
parser
unparser
procto (0.0.3)
prometheus-client-mmap (0.9.10)
prometheus-client-mmap (0.10.0)
pry (0.11.3)
coderay (~> 1.1.0)
method_source (~> 0.9.0)
......@@ -1292,7 +1292,7 @@ DEPENDENCIES
pg (~> 1.1)
png_quantizator (~> 0.2.1)
premailer-rails (~> 1.10.3)
prometheus-client-mmap (~> 0.9.10)
prometheus-client-mmap (~> 0.10.0)
pry-byebug (~> 3.5.1)
pry-rails (~> 0.3.4)
rack (~> 2.0.7)
......
......@@ -386,22 +386,19 @@
margin: 5px;
}
.issue-boards-sidebar {
.right-sidebar.issue-boards-sidebar {
.gutter-toggle {
bottom: 15px;
width: 22px;
color: $gray-darkest;
padding-left: $gl-padding-32;
svg {
position: absolute;
top: 50%;
right: 0;
margin-top: (-11px / 2);
}
&:hover {
path {
fill: $gray-darkest;
}
height: $gl-font-size-12;
width: $gl-font-size-12;
}
}
......
......@@ -173,6 +173,20 @@
margin-top: 7px;
}
.gutter-toggle {
margin-left: 20px;
padding-left: 10px;
&:hover {
color: $gl-text-color;
}
&:hover,
&:focus {
text-decoration: none;
}
}
.block {
@include clearfix;
padding: $gl-padding 0;
......@@ -195,20 +209,6 @@
margin-top: 0;
}
.gutter-toggle {
margin-left: 20px;
padding-left: 10px;
&:hover {
color: $gl-text-color;
}
&:hover,
&:focus {
text-decoration: none;
}
}
&.assignee {
.author-link {
display: block;
......
......@@ -23,6 +23,7 @@ class Projects::EnvironmentsController < Projects::ApplicationController
def index
@environments = project.environments
.with_state(params[:scope] || :available)
@project = ProjectPresenter.new(project, current_user: current_user)
respond_to do |format|
format.html
......@@ -31,6 +32,7 @@ class Projects::EnvironmentsController < Projects::ApplicationController
render json: {
environments: serialize_environments(request, response, params[:nested]),
review_app: serialize_review_app,
available_count: project.environments.available.count,
stopped_count: project.environments.stopped.count
}
......@@ -242,6 +244,10 @@ class Projects::EnvironmentsController < Projects::ApplicationController
.represent(@environments)
end
def serialize_review_app
ReviewAppSetupSerializer.new(current_user: @current_user).represent(@project)
end
def authorize_stop_environment!
access_denied! unless can?(current_user, :stop_environment, environment)
end
......
......@@ -276,8 +276,7 @@ class ProjectPresenter < Gitlab::View::Presenter::Delegated
end
def kubernetes_cluster_anchor_data
if current_user && can?(current_user, :create_cluster, project)
if can_instantiate_cluster?
if clusters.empty?
AnchorData.new(false,
statistic_icon + _('Add Kubernetes cluster'),
......@@ -294,7 +293,7 @@ class ProjectPresenter < Gitlab::View::Presenter::Delegated
end
def gitlab_ci_anchor_data
if current_user && can_current_user_push_code? && repository.gitlab_ci_yml.blank? && !auto_devops_enabled?
if cicd_missing?
AnchorData.new(false,
statistic_icon + _('Set up CI/CD'),
add_ci_yml_path)
......@@ -326,8 +325,28 @@ class ProjectPresenter < Gitlab::View::Presenter::Delegated
count_of_extra_topics_not_shown > 0
end
def can_setup_review_app?
strong_memoize(:can_setup_review_app) do
(can_instantiate_cluster? && all_clusters_empty?) || cicd_missing?
end
end
def all_clusters_empty?
strong_memoize(:all_clusters_empty) do
project.all_clusters.empty?
end
end
private
def cicd_missing?
current_user && can_current_user_push_code? && repository.gitlab_ci_yml.blank? && !auto_devops_enabled?
end
def can_instantiate_cluster?
current_user && can?(current_user, :create_cluster, project)
end
def filename_path(filename)
if blob = repository.public_send(filename) # rubocop:disable GitlabSecurity/PublicSend
project_blob_path(
......
......@@ -40,7 +40,7 @@ class ReleasePresenter < Gitlab::View::Presenter::Delegated
def evidence_file_path
return unless release.evidence.present?
evidence_project_release_url(project, tag, format: :json)
evidence_project_release_url(project, release.to_param, format: :json)
end
private
......
# frozen_string_literal: true
class ReviewAppSetupEntity < Grape::Entity
include RequestAwareEntity
expose :can_setup_review_app?, as: :can_setup_review_app
expose :all_clusters_empty?, as: :all_clusters_empty, if: -> (_, _) { project.can_setup_review_app? } do |project|
project.all_clusters_empty?
end
expose :review_snippet, if: -> (_, _) { project.can_setup_review_app? } do |_|
YAML.safe_load(File.read(Rails.root.join('lib', 'gitlab', 'ci', 'snippets', 'review_app_default.yml'))).to_s
end
private
def current_user
request.current_user
end
def project
object
end
end
# frozen_string_literal: true
class ReviewAppSetupSerializer < BaseSerializer
entity ReviewAppSetupEntity
end
......@@ -5,9 +5,17 @@ module Prometheus
include ReactiveCaching
include Gitlab::Utils::StrongMemoize
self.reactive_cache_key = ->(service) { service.cache_key }
self.reactive_cache_key = ->(service) { [] }
self.reactive_cache_lease_timeout = 30.seconds
self.reactive_cache_refresh_interval = 30.seconds
# reactive_cache_refresh_interval should be set to a value higher than
# reactive_cache_lifetime. If the refresh_interval is less than lifetime
# then the ReactiveCachingWorker will re-query prometheus for this
# PromQL query even though it's (probably) already been picked up by
# the frontend
# refresh_interval should be set less than lifetime only if this data
# is expected to change *and* be fetched again by the frontend
self.reactive_cache_refresh_interval = 90.seconds
self.reactive_cache_lifetime = 1.minute
self.reactive_cache_worker_finder = ->(_id, *args) { from_cache(*args) }
......
......@@ -8,6 +8,6 @@ module ClusterQueue
included do
queue_namespace :gcp_cluster
feature_category :kubernetes_configuration
feature_category :kubernetes_management
end
end
......@@ -4,7 +4,7 @@ class GroupDestroyWorker
include ApplicationWorker
include ExceptionBacktrace
feature_category :groups
feature_category :subgroups
def perform(group_id, user_id)
begin
......
---
title: Improve link generation performance
merge_request: 22426
author:
type: performance
---
title: Authenticate API requests with job tokens for Rack::Attack
merge_request: 21412
author:
type: fixed
---
title: Increase size of issue boards sidebar collapse button
merge_request:
author:
type: fixed
---
title: Fix releases page when tag contains a slash
merge_request: 22527
author:
type: fixed
---
title: Disable Prometheus metrics if initialization fails
merge_request: 22355
author:
type: fixed
---
title: When sidekiq-cluster is asked to shutdown, actively terminate any sidekiq processes that don't finish cleanly in short order
merge_request: 21796
author:
type: fixed
---
title: Reduce redis key size for the Prometheus proxy and the amount of queries by half
merge_request: 20006
author:
type: performance
......@@ -8,10 +8,10 @@
#
---
- accessibility_testing
- account-management
- agile_portfolio_management
- analysis
- audit_management
- attack_emulation
- audit_events
- audit_reports
- authentication_and_authorization
- auto_devops
- backup_restore
......@@ -25,25 +25,29 @@
- code_quality
- code_review
- collection
- compliance_controls
- compliance_frameworks
- container_network_security
- container_registry
- container_scanning
- continuous_delivery
- continuous_integration
- data_loss_prevention
- ddos_protection
- dependency_proxy
- dependency_scanning
- design_management
- devops_score
- disaster_recovery
- dynamic_application_security_testing
- epics
- error_tracking
- feature_flags
- fuzzing
- geo_replication
- gitaly
- gitlab_handbook
- gitter
- groups
- helm_chart_registry
- importers
- incident_management
......@@ -55,12 +59,13 @@
- internationalization
- issue_tracking
- kanban_boards
- kubernetes_configuration
- kubernetes_management
- language_specific
- license_compliance
- live_coding
- load_testing
- logging
- malware_scanning
- metrics
- omnibus_package
- package_registry
......@@ -69,7 +74,9 @@
- release_governance
- release_orchestration
- requirements_management
- responsible_disclosure
- review_apps
- roadmaps
- runbooks
- runner
- runtime_application_self_protection
......@@ -82,8 +89,9 @@
- snippets
- source_code_management
- static_application_security_testing
- static_site_editor
- status_page
- storage_security
- subgroups
- synthetic_monitoring
- system_testing
- templates
......@@ -100,4 +108,3 @@
- web_ide
- web_performance
- wiki
- workflow_policies
......@@ -43,6 +43,9 @@ if !Rails.env.test? && Gitlab::Metrics.prometheus_metrics_enabled?
defined?(::Prometheus::Client.reinitialize_on_pid_change) && Prometheus::Client.reinitialize_on_pid_change
Gitlab::Metrics::Samplers::RubySampler.initialize_instance(Settings.monitoring.ruby_sampler_interval).start
rescue IOError => e
Gitlab::ErrorTracking.track_exception(e)
Gitlab::Metrics.error_detected!
end
Gitlab::Cluster::LifecycleEvents.on_master_start do
......@@ -55,6 +58,9 @@ if !Rails.env.test? && Gitlab::Metrics.prometheus_metrics_enabled?
end
Gitlab::Metrics::RequestsRackMiddleware.initialize_http_request_duration_seconds
rescue IOError => e
Gitlab::ErrorTracking.track_exception(e)
Gitlab::Metrics.error_detected!
end
end
......
# frozen_string_literal: true
# TODO: Eliminate this file when https://github.com/rails/rails/pull/38184 is released.
# Cleanup issue: https://gitlab.com/gitlab-org/gitlab/issues/195841
ActionDispatch::Journey::Formatter.prepend(Gitlab::Patch::ActionDispatchJourneyFormatter)
module ActionDispatch
module Journey
module Path
class Pattern
def requirements_for_missing_keys_check
@requirements_for_missing_keys_check ||= requirements.each_with_object({}) do |(key, regex), hash|
hash[key] = /\A#{regex}\Z/
end
end
end
end
end
end
......@@ -98,7 +98,7 @@ There are two ways you can configure the Registry's external domain. Either:
for that domain.
Since the container Registry requires a TLS certificate, in the end it all boils
down to how easy or pricey is to get a new one.
down to how easy or pricey it is to get a new one.
Please take this into consideration before configuring the Container Registry
for the first time.
......
......@@ -25,9 +25,10 @@ module Gitlab
PRIVATE_TOKEN_HEADER = 'HTTP_PRIVATE_TOKEN'
PRIVATE_TOKEN_PARAM = :private_token
JOB_TOKEN_HEADER = "HTTP_JOB_TOKEN".freeze
JOB_TOKEN_HEADER = 'HTTP_JOB_TOKEN'.freeze
JOB_TOKEN_PARAM = :job_token
RUNNER_TOKEN_PARAM = :token
RUNNER_JOB_TOKEN_PARAM = :token
# Check the Rails session for valid authentication details
def find_user_from_warden
......@@ -57,11 +58,13 @@ module Gitlab
def find_user_from_job_token
return unless route_authentication_setting[:job_token_allowed]
token = (params[JOB_TOKEN_PARAM] || env[JOB_TOKEN_HEADER]).to_s
return unless token.present?
token = current_request.params[JOB_TOKEN_PARAM].presence ||
current_request.params[RUNNER_JOB_TOKEN_PARAM].presence ||
current_request.env[JOB_TOKEN_HEADER].presence
return unless token
job = ::Ci::Build.find_by_token(token)
raise ::Gitlab::Auth::UnauthorizedError unless job
raise UnauthorizedError unless job
@current_authenticated_job = job # rubocop:disable Gitlab/ModuleWithInstanceVariables
......
......@@ -33,7 +33,8 @@ module Gitlab
find_user_from_web_access_token(request_format) ||
find_user_from_feed_token(request_format) ||
find_user_from_static_object_token(request_format) ||
find_user_from_basic_auth_job
find_user_from_basic_auth_job ||
find_user_from_job_token
rescue Gitlab::Auth::AuthenticationError
nil
end
......@@ -45,6 +46,14 @@ module Gitlab
rescue Gitlab::Auth::AuthenticationError
false
end
private
def route_authentication_setting
@route_authentication_setting ||= {
job_token_allowed: api_request?
}
end
end
end
end
deploy_review:
stage: deploy
script:
- echo "Deploy a review app"
environment:
name: review/$CI_COMMIT_REF_NAME
url: https://$CI_ENVIRONMENT_SLUG.example.com
only:
- branches
......@@ -5,8 +5,14 @@ module Gitlab
include Gitlab::Metrics::InfluxDb
include Gitlab::Metrics::Prometheus
@error = false
def self.enabled?
influx_metrics_enabled? || prometheus_metrics_enabled?
end
def self.error?
@error
end
end
end
......@@ -61,6 +61,14 @@ module Gitlab
safe_provide_metric(:histogram, name, docstring, base_labels, buckets)
end
def error_detected!
clear_memoization(:prometheus_metrics_enabled)
PROVIDER_MUTEX.synchronize do
@error = true
end
end
private
def safe_provide_metric(method, name, *args)
......@@ -81,7 +89,7 @@ module Gitlab
end
def prometheus_metrics_enabled_unmemoized
metrics_folder_present? && Gitlab::CurrentSettings.prometheus_metrics_enabled || false
!error? && metrics_folder_present? && Gitlab::CurrentSettings.prometheus_metrics_enabled || false
end
end
end
......
# frozen_string_literal: true
module Gitlab
module Patch
module ActionDispatchJourneyFormatter
def self.prepended(mod)
mod.alias_method(:old_missing_keys, :missing_keys)
mod.remove_method(:missing_keys)
end
private
def missing_keys(route, parts)
missing_keys = nil
tests = route.path.requirements_for_missing_keys_check
route.required_parts.each do |key|
case tests[key]
when nil
unless parts[key]
missing_keys ||= []
missing_keys << key
end
else
unless tests[key].match?(parts[key])
missing_keys ||= []
missing_keys << key
end
end
end
missing_keys
end
end
end
end
......@@ -1350,6 +1350,9 @@ msgstr ""
msgid "AdminUsers|External"
msgstr ""
msgid "AdminUsers|Is using seat"
msgstr ""
msgid "AdminUsers|It's you!"
msgstr ""
......
......@@ -318,6 +318,7 @@ describe 'Issue Boards', :js do
wait_for_requests
click_link bug.title
within('.dropdown-menu-labels') { expect(page).to have_selector('.is-active', count: 3) }
click_link regression.title
wait_for_requests
......
......@@ -57,4 +57,14 @@ describe 'User views releases', :js do
expect(page).to have_content('Upcoming Release')
end
end
context 'with a tag containing a slash' do
it 'sees the release' do
release = create :release, :with_evidence, project: project, tag: 'debian/2.4.0-1'
visit project_releases_path(project)
expect(page).to have_content(release.name)
expect(page).to have_content(release.tag)
end
end
end
......@@ -446,6 +446,93 @@ describe Gitlab::Auth::AuthFinders do
end
end
describe '#find_user_from_job_token' do
let(:job) { create(:ci_build, user: user) }
let(:route_authentication_setting) { { job_token_allowed: true } }
subject { find_user_from_job_token }
context 'when the job token is in the headers' do
it 'returns the user if valid job token' do
env[described_class::JOB_TOKEN_HEADER] = job.token
is_expected.to eq(user)
expect(@current_authenticated_job).to eq(job)
end
it 'returns nil without job token' do
env[described_class::JOB_TOKEN_HEADER] = ''
is_expected.to be_nil
end
it 'returns exception if invalid job token' do
env[described_class::JOB_TOKEN_HEADER] = 'invalid token'
expect { subject }.to raise_error(Gitlab::Auth::UnauthorizedError)
end
context 'when route is not allowed to be authenticated' do
let(:route_authentication_setting) { { job_token_allowed: false } }
it 'sets current_user to nil' do
env[described_class::JOB_TOKEN_HEADER] = job.token
allow_any_instance_of(Gitlab::UserAccess).to receive(:allowed?).and_return(true)
is_expected.to be_nil
end
end
end
context 'when the job token is in the params' do
shared_examples 'job token params' do |token_key_name|
before do
set_param(token_key_name, token)
end
context 'with valid job token' do
let(:token) { job.token }
it 'returns the user' do
is_expected.to eq(user)
expect(@current_authenticated_job).to eq(job)
end
end
context 'with empty job token' do
let(:token) { '' }
it 'returns nil' do
is_expected.to be_nil
end
end
context 'with invalid job token' do
let(:token) { 'invalid token' }
it 'returns exception' do
expect { subject }.to raise_error(Gitlab::Auth::UnauthorizedError)
end
end
context 'when route is not allowed to be authenticated' do
let(:route_authentication_setting) { { job_token_allowed: false } }
let(:token) { job.token }
it 'sets current_user to nil' do
allow_any_instance_of(Gitlab::UserAccess).to receive(:allowed?).and_return(true)
is_expected.to be_nil
end
end
end
it_behaves_like 'job token params', described_class::JOB_TOKEN_PARAM
it_behaves_like 'job token params', described_class::RUNNER_JOB_TOKEN_PARAM
end
end
describe '#find_runner_from_token' do
let(:runner) { create(:ci_runner) }
......
......@@ -42,6 +42,8 @@ describe Gitlab::Auth::RequestAuthenticator do
describe '#find_sessionless_user' do
let!(:access_token_user) { build(:user) }
let!(:feed_token_user) { build(:user) }
let!(:static_object_token_user) { build(:user) }
let!(:job_token_user) { build(:user) }
it 'returns access_token user first' do
allow_any_instance_of(described_class).to receive(:find_user_from_web_access_token).and_return(access_token_user)
......@@ -56,6 +58,22 @@ describe Gitlab::Auth::RequestAuthenticator do
expect(subject.find_sessionless_user([:api])).to eq feed_token_user
end
it 'returns static_object_token user if no feed_token user found' do
allow_any_instance_of(described_class)
.to receive(:find_user_from_static_object_token)
.and_return(static_object_token_user)
expect(subject.find_sessionless_user([:api])).to eq static_object_token_user
end
it 'returns job_token user if no static_object_token user found' do
allow_any_instance_of(described_class)
.to receive(:find_user_from_job_token)
.and_return(job_token_user)
expect(subject.find_sessionless_user([:api])).to eq job_token_user
end
it 'returns nil if no user found' do
expect(subject.find_sessionless_user([:api])).to be_blank
end
......@@ -67,6 +85,39 @@ describe Gitlab::Auth::RequestAuthenticator do
end
end
describe '#find_user_from_job_token' do
let!(:user) { build(:user) }
let!(:job) { build(:ci_build, user: user) }
before do
env[Gitlab::Auth::AuthFinders::JOB_TOKEN_HEADER] = 'token'
end
context 'with API requests' do
before do
env['SCRIPT_NAME'] = '/api/endpoint'
end
it 'tries to find the user' do
expect(::Ci::Build).to receive(:find_by_token).and_return(job)
expect(subject.find_sessionless_user([:api])).to eq user
end
end
context 'without API requests' do
before do
env['SCRIPT_NAME'] = '/web/endpoint'
end
it 'does not search for job users' do
expect(::Ci::Build).not_to receive(:find_by_token)
expect(subject.find_sessionless_user([:api])).to be_nil
end
end
end
describe '#runner' do
let!(:runner) { build(:ci_runner) }
......
......@@ -17,4 +17,21 @@ describe Gitlab::Metrics::Prometheus, :prometheus do
expect(all_metrics.registry.metrics.count).to eq(0)
end
end
describe '#error_detected!' do
before do
allow(all_metrics).to receive(:metrics_folder_present?).and_return(true)
stub_application_setting(prometheus_metrics_enabled: true)
end
it 'disables Prometheus metrics' do
expect(all_metrics.error?).to be_falsey
expect(all_metrics.prometheus_metrics_enabled?).to be_truthy
all_metrics.error_detected!
expect(all_metrics.prometheus_metrics_enabled?).to be_falsey
expect(all_metrics.error?).to be_truthy
end
end
end
# frozen_string_literal: true
require 'spec_helper'
describe Gitlab::Patch::ActionDispatchJourneyFormatter do
let(:group) { create(:group) }
let(:project) { create(:project, namespace: group) }
let(:pipeline) { create(:ci_empty_pipeline, project: project) }
let(:url) { Gitlab::Routing.url_helpers.project_pipeline_url(project, pipeline) }
let(:expected_path) { "#{project.full_path}/pipelines/#{pipeline.id}" }
context 'custom implementation of #missing_keys' do
before do
expect_any_instance_of(Gitlab::Patch::ActionDispatchJourneyFormatter).to receive(:missing_keys)
end
it 'generates correct url' do
expect(url).to end_with(expected_path)
end
end
context 'original implementation of #missing_keys' do
before do
allow_any_instance_of(Gitlab::Patch::ActionDispatchJourneyFormatter).to receive(:missing_keys) do |instance, route, parts|
instance.send(:old_missing_keys, route, parts) # test the old implementation
end
end
it 'generates correct url' do
expect(url).to end_with(expected_path)
end
end
end
......@@ -4,11 +4,10 @@ require 'spec_helper'
describe ProjectPresenter do
let(:user) { create(:user) }
describe '#license_short_name' do
let(:project) { create(:project) }
let(:presenter) { described_class.new(project, current_user: user) }
describe '#license_short_name' do
context 'when project.repository has a license_key' do
it 'returns the nickname of the license if present' do
allow(project.repository).to receive(:license_key).and_return('agpl-3.0')
......@@ -33,8 +32,6 @@ describe ProjectPresenter do
end
describe '#default_view' do
let(:presenter) { described_class.new(project, current_user: user) }
context 'user not signed in' do
let(:user) { nil }
......@@ -125,7 +122,6 @@ describe ProjectPresenter do
describe '#can_current_user_push_code?' do
let(:project) { create(:project, :repository) }
let(:presenter) { described_class.new(project, current_user: user) }
context 'empty repo' do
let(:project) { create(:project) }
......@@ -163,7 +159,6 @@ describe ProjectPresenter do
context 'statistics anchors (empty repo)' do
let(:project) { create(:project, :empty_repo) }
let(:presenter) { described_class.new(project, current_user: user) }
describe '#files_anchor_data' do
it 'returns files data' do
......@@ -200,7 +195,6 @@ describe ProjectPresenter do
context 'statistics anchors' do
let(:project) { create(:project, :repository) }
let(:presenter) { described_class.new(project, current_user: user) }
describe '#files_anchor_data' do
it 'returns files data' do
......@@ -416,7 +410,6 @@ describe ProjectPresenter do
describe '#statistics_buttons' do
let(:project) { build(:project) }
let(:presenter) { described_class.new(project, current_user: user) }
it 'orders the items correctly' do
allow(project.repository).to receive(:readme).and_return(double(name: 'readme'))
......@@ -435,8 +428,6 @@ describe ProjectPresenter do
end
describe '#repo_statistics_buttons' do
let(:presenter) { described_class.new(project, current_user: user) }
subject(:empty_repo_statistics_buttons) { presenter.empty_repo_statistics_buttons }
before do
......@@ -485,4 +476,73 @@ describe ProjectPresenter do
end
end
end
describe '#can_setup_review_app?' do
subject { presenter.can_setup_review_app? }
context 'when the ci/cd file is missing' do
before do
allow(presenter).to receive(:cicd_missing?).and_return(true)
end
it { is_expected.to be_truthy }
end
context 'when the ci/cd file is not missing' do
before do
allow(presenter).to receive(:cicd_missing?).and_return(false)
end
context 'and the user can create a cluster' do
before do
allow(Ability).to receive(:allowed?).and_call_original
allow(Ability).to receive(:allowed?).with(user, :create_cluster, project).and_return(true)
end
context 'and there is no cluster associated to this project' do
let(:project) { create(:project, clusters: []) }
it { is_expected.to be_truthy }
end
context 'and there is already a cluster associated to this project' do
let(:project) { create(:project, clusters: [build(:cluster, :providing_by_gcp)]) }
it { is_expected.to be_falsey }
end
context 'when a group cluster is instantiated' do
let_it_be(:cluster) { create(:cluster, :group) }
let_it_be(:group) { cluster.group }
context 'and the project belongs to this group' do
let!(:project) { create(:project, group: group) }
it { is_expected.to be_falsey }
end
context 'and the project does not belong to this group' do
it { is_expected.to be_truthy }
end
end
context 'and there is already an instance cluster' do
it 'is false' do
create(:cluster, :instance)
is_expected.to be_falsey
end
end
end
context 'and the user cannot create a cluster' do
before do
allow(Ability).to receive(:allowed?).and_call_original
allow(Ability).to receive(:allowed?).with(user, :create_cluster, project).and_return(false)
end
it { is_expected.to be_falsey }
end
end
end
end
......@@ -96,4 +96,28 @@ describe ReleasePresenter do
it { is_expected.to be_nil }
end
end
describe '#evidence_file_path' do
subject { presenter.evidence_file_path }
context 'without evidence' do
it { is_expected.to be_falsy }
end
context 'with evidence' do
let(:release) { create :release, :with_evidence, project: project }
specify do
is_expected.to match /#{evidence_project_release_url(project, release.tag, format: :json)}/
end
end
context 'when a tag contains a slash' do
let(:release) { create :release, :with_evidence, project: project, tag: 'debian/2.4.0-1' }
specify do
is_expected.to match /#{evidence_project_release_url(project, CGI.escape(release.tag), format: :json)}/
end
end
end
end
......@@ -115,6 +115,16 @@ describe API::Releases do
end
end
context 'when tag contains a slash' do
let!(:release) { create(:release, project: project, tag: 'debian/2.4.0-1', description: "debian/2.4.0-1") }
it 'returns 200 HTTP status' do
get api("/projects/#{project.id}/releases", maintainer)
expect(response).to have_gitlab_http_status(:ok)
end
end
context 'when user is a guest' do
let!(:release) do
create(:release,
......
# frozen_string_literal: true
require 'spec_helper'
describe ReviewAppSetupEntity do
let_it_be(:user) { create(:admin) }
let(:project) { create(:project) }
let(:presenter) { ProjectPresenter.new(project, current_user: user) }
let(:entity) { described_class.new(presenter) }
let(:request) { double('request') }
before do
allow(request).to receive(:current_user).and_return(user)
allow(request).to receive(:project).and_return(project)
end
subject { entity.as_json }
describe '#as_json' do
it 'contains can_setup_review_app' do
expect(subject).to include(:can_setup_review_app)
end
context 'when the user can setup a review app' do
before do
allow(presenter).to receive(:can_setup_review_app?).and_return(true)
end
it 'contains relevant fields' do
expect(subject.keys).to include(:all_clusters_empty, :review_snippet)
end
it 'exposes the relevant review snippet' do
review_app_snippet = YAML.safe_load(File.read(Rails.root.join('lib', 'gitlab', 'ci', 'snippets', 'review_app_default.yml'))).to_s
expect(subject[:review_snippet]).to eq(review_app_snippet)
end
it 'exposes whether the project has associated clusters' do
expect(subject[:all_clusters_empty]).to be_truthy
end
end
context 'when the user cannot setup a review app' do
before do
allow(presenter).to receive(:can_setup_review_app?).and_return(false)
end
it 'does not expose certain fields' do
expect(subject.keys).not_to include(:all_clusters_empty, :review_snippet)
end
end
end
end
......@@ -8,6 +8,12 @@ describe Prometheus::ProxyService do
set(:project) { create(:project) }
set(:environment) { create(:environment, project: project) }
describe 'configuration' do
it 'ReactiveCaching refresh is not needed' do
expect(described_class.reactive_cache_refresh_interval).to be > described_class.reactive_cache_lifetime
end
end
describe '#initialize' do
let(:params) { ActionController::Parameters.new(query: '1').permit! }
......
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