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: ...@@ -50,7 +50,7 @@ docs lint:
- .default-retry - .default-retry
- .default-only - .default-only
- .only:changes-docs - .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 stage: test
dependencies: [] dependencies: []
script: script:
......
...@@ -327,7 +327,7 @@ group :metrics do ...@@ -327,7 +327,7 @@ group :metrics do
gem 'influxdb', '~> 0.2', require: false gem 'influxdb', '~> 0.2', require: false
# Prometheus # Prometheus
gem 'prometheus-client-mmap', '~> 0.9.10' gem 'prometheus-client-mmap', '~> 0.10.0'
gem 'raindrops', '~> 0.18' gem 'raindrops', '~> 0.18'
end end
......
...@@ -749,7 +749,7 @@ GEM ...@@ -749,7 +749,7 @@ GEM
parser parser
unparser unparser
procto (0.0.3) procto (0.0.3)
prometheus-client-mmap (0.9.10) prometheus-client-mmap (0.10.0)
pry (0.11.3) pry (0.11.3)
coderay (~> 1.1.0) coderay (~> 1.1.0)
method_source (~> 0.9.0) method_source (~> 0.9.0)
...@@ -1292,7 +1292,7 @@ DEPENDENCIES ...@@ -1292,7 +1292,7 @@ DEPENDENCIES
pg (~> 1.1) pg (~> 1.1)
png_quantizator (~> 0.2.1) png_quantizator (~> 0.2.1)
premailer-rails (~> 1.10.3) premailer-rails (~> 1.10.3)
prometheus-client-mmap (~> 0.9.10) prometheus-client-mmap (~> 0.10.0)
pry-byebug (~> 3.5.1) pry-byebug (~> 3.5.1)
pry-rails (~> 0.3.4) pry-rails (~> 0.3.4)
rack (~> 2.0.7) rack (~> 2.0.7)
......
...@@ -386,22 +386,19 @@ ...@@ -386,22 +386,19 @@
margin: 5px; margin: 5px;
} }
.issue-boards-sidebar { .right-sidebar.issue-boards-sidebar {
.gutter-toggle { .gutter-toggle {
bottom: 15px; bottom: 15px;
width: 22px; width: 22px;
color: $gray-darkest; padding-left: $gl-padding-32;
svg { svg {
position: absolute; position: absolute;
top: 50%; top: 50%;
right: 0;
margin-top: (-11px / 2); margin-top: (-11px / 2);
} height: $gl-font-size-12;
width: $gl-font-size-12;
&:hover {
path {
fill: $gray-darkest;
}
} }
} }
......
...@@ -173,6 +173,20 @@ ...@@ -173,6 +173,20 @@
margin-top: 7px; margin-top: 7px;
} }
.gutter-toggle {
margin-left: 20px;
padding-left: 10px;
&:hover {
color: $gl-text-color;
}
&:hover,
&:focus {
text-decoration: none;
}
}
.block { .block {
@include clearfix; @include clearfix;
padding: $gl-padding 0; padding: $gl-padding 0;
...@@ -195,20 +209,6 @@ ...@@ -195,20 +209,6 @@
margin-top: 0; margin-top: 0;
} }
.gutter-toggle {
margin-left: 20px;
padding-left: 10px;
&:hover {
color: $gl-text-color;
}
&:hover,
&:focus {
text-decoration: none;
}
}
&.assignee { &.assignee {
.author-link { .author-link {
display: block; display: block;
......
...@@ -23,6 +23,7 @@ class Projects::EnvironmentsController < Projects::ApplicationController ...@@ -23,6 +23,7 @@ class Projects::EnvironmentsController < Projects::ApplicationController
def index def index
@environments = project.environments @environments = project.environments
.with_state(params[:scope] || :available) .with_state(params[:scope] || :available)
@project = ProjectPresenter.new(project, current_user: current_user)
respond_to do |format| respond_to do |format|
format.html format.html
...@@ -31,6 +32,7 @@ class Projects::EnvironmentsController < Projects::ApplicationController ...@@ -31,6 +32,7 @@ class Projects::EnvironmentsController < Projects::ApplicationController
render json: { render json: {
environments: serialize_environments(request, response, params[:nested]), environments: serialize_environments(request, response, params[:nested]),
review_app: serialize_review_app,
available_count: project.environments.available.count, available_count: project.environments.available.count,
stopped_count: project.environments.stopped.count stopped_count: project.environments.stopped.count
} }
...@@ -242,6 +244,10 @@ class Projects::EnvironmentsController < Projects::ApplicationController ...@@ -242,6 +244,10 @@ class Projects::EnvironmentsController < Projects::ApplicationController
.represent(@environments) .represent(@environments)
end end
def serialize_review_app
ReviewAppSetupSerializer.new(current_user: @current_user).represent(@project)
end
def authorize_stop_environment! def authorize_stop_environment!
access_denied! unless can?(current_user, :stop_environment, environment) access_denied! unless can?(current_user, :stop_environment, environment)
end end
......
...@@ -276,8 +276,7 @@ class ProjectPresenter < Gitlab::View::Presenter::Delegated ...@@ -276,8 +276,7 @@ class ProjectPresenter < Gitlab::View::Presenter::Delegated
end end
def kubernetes_cluster_anchor_data def kubernetes_cluster_anchor_data
if current_user && can?(current_user, :create_cluster, project) if can_instantiate_cluster?
if clusters.empty? if clusters.empty?
AnchorData.new(false, AnchorData.new(false,
statistic_icon + _('Add Kubernetes cluster'), statistic_icon + _('Add Kubernetes cluster'),
...@@ -294,7 +293,7 @@ class ProjectPresenter < Gitlab::View::Presenter::Delegated ...@@ -294,7 +293,7 @@ class ProjectPresenter < Gitlab::View::Presenter::Delegated
end end
def gitlab_ci_anchor_data 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, AnchorData.new(false,
statistic_icon + _('Set up CI/CD'), statistic_icon + _('Set up CI/CD'),
add_ci_yml_path) add_ci_yml_path)
...@@ -326,8 +325,28 @@ class ProjectPresenter < Gitlab::View::Presenter::Delegated ...@@ -326,8 +325,28 @@ class ProjectPresenter < Gitlab::View::Presenter::Delegated
count_of_extra_topics_not_shown > 0 count_of_extra_topics_not_shown > 0
end 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 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) def filename_path(filename)
if blob = repository.public_send(filename) # rubocop:disable GitlabSecurity/PublicSend if blob = repository.public_send(filename) # rubocop:disable GitlabSecurity/PublicSend
project_blob_path( project_blob_path(
......
...@@ -40,7 +40,7 @@ class ReleasePresenter < Gitlab::View::Presenter::Delegated ...@@ -40,7 +40,7 @@ class ReleasePresenter < Gitlab::View::Presenter::Delegated
def evidence_file_path def evidence_file_path
return unless release.evidence.present? return unless release.evidence.present?
evidence_project_release_url(project, tag, format: :json) evidence_project_release_url(project, release.to_param, format: :json)
end end
private 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 ...@@ -5,9 +5,17 @@ module Prometheus
include ReactiveCaching include ReactiveCaching
include Gitlab::Utils::StrongMemoize 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_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_lifetime = 1.minute
self.reactive_cache_worker_finder = ->(_id, *args) { from_cache(*args) } self.reactive_cache_worker_finder = ->(_id, *args) { from_cache(*args) }
......
...@@ -8,6 +8,6 @@ module ClusterQueue ...@@ -8,6 +8,6 @@ module ClusterQueue
included do included do
queue_namespace :gcp_cluster queue_namespace :gcp_cluster
feature_category :kubernetes_configuration feature_category :kubernetes_management
end end
end end
...@@ -4,7 +4,7 @@ class GroupDestroyWorker ...@@ -4,7 +4,7 @@ class GroupDestroyWorker
include ApplicationWorker include ApplicationWorker
include ExceptionBacktrace include ExceptionBacktrace
feature_category :groups feature_category :subgroups
def perform(group_id, user_id) def perform(group_id, user_id)
begin 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 @@ ...@@ -8,10 +8,10 @@
# #
--- ---
- accessibility_testing - accessibility_testing
- account-management
- agile_portfolio_management
- analysis - analysis
- audit_management - attack_emulation
- audit_events
- audit_reports
- authentication_and_authorization - authentication_and_authorization
- auto_devops - auto_devops
- backup_restore - backup_restore
...@@ -25,25 +25,29 @@ ...@@ -25,25 +25,29 @@
- code_quality - code_quality
- code_review - code_review
- collection - collection
- compliance_controls
- compliance_frameworks
- container_network_security - container_network_security
- container_registry - container_registry
- container_scanning - container_scanning
- continuous_delivery - continuous_delivery
- continuous_integration - continuous_integration
- data_loss_prevention - data_loss_prevention
- ddos_protection
- dependency_proxy - dependency_proxy
- dependency_scanning - dependency_scanning
- design_management - design_management
- devops_score - devops_score
- disaster_recovery - disaster_recovery
- dynamic_application_security_testing - dynamic_application_security_testing
- epics
- error_tracking - error_tracking
- feature_flags - feature_flags
- fuzzing - fuzzing
- geo_replication - geo_replication
- gitaly - gitaly
- gitlab_handbook
- gitter - gitter
- groups
- helm_chart_registry - helm_chart_registry
- importers - importers
- incident_management - incident_management
...@@ -55,12 +59,13 @@ ...@@ -55,12 +59,13 @@
- internationalization - internationalization
- issue_tracking - issue_tracking
- kanban_boards - kanban_boards
- kubernetes_configuration - kubernetes_management
- language_specific - language_specific
- license_compliance - license_compliance
- live_coding - live_coding
- load_testing - load_testing
- logging - logging
- malware_scanning
- metrics - metrics
- omnibus_package - omnibus_package
- package_registry - package_registry
...@@ -69,7 +74,9 @@ ...@@ -69,7 +74,9 @@
- release_governance - release_governance
- release_orchestration - release_orchestration
- requirements_management - requirements_management
- responsible_disclosure
- review_apps - review_apps
- roadmaps
- runbooks - runbooks
- runner - runner
- runtime_application_self_protection - runtime_application_self_protection
...@@ -82,8 +89,9 @@ ...@@ -82,8 +89,9 @@
- snippets - snippets
- source_code_management - source_code_management
- static_application_security_testing - static_application_security_testing
- static_site_editor
- status_page - status_page
- storage_security - subgroups
- synthetic_monitoring - synthetic_monitoring
- system_testing - system_testing
- templates - templates
...@@ -100,4 +108,3 @@ ...@@ -100,4 +108,3 @@
- web_ide - web_ide
- web_performance - web_performance
- wiki - wiki
- workflow_policies
...@@ -43,6 +43,9 @@ if !Rails.env.test? && Gitlab::Metrics.prometheus_metrics_enabled? ...@@ -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 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 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 end
Gitlab::Cluster::LifecycleEvents.on_master_start do Gitlab::Cluster::LifecycleEvents.on_master_start do
...@@ -55,6 +58,9 @@ if !Rails.env.test? && Gitlab::Metrics.prometheus_metrics_enabled? ...@@ -55,6 +58,9 @@ if !Rails.env.test? && Gitlab::Metrics.prometheus_metrics_enabled?
end end
Gitlab::Metrics::RequestsRackMiddleware.initialize_http_request_duration_seconds Gitlab::Metrics::RequestsRackMiddleware.initialize_http_request_duration_seconds
rescue IOError => e
Gitlab::ErrorTracking.track_exception(e)
Gitlab::Metrics.error_detected!
end end
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: ...@@ -98,7 +98,7 @@ There are two ways you can configure the Registry's external domain. Either:
for that domain. for that domain.
Since the container Registry requires a TLS certificate, in the end it all boils 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 Please take this into consideration before configuring the Container Registry
for the first time. for the first time.
......
...@@ -30,22 +30,22 @@ To install a Camo server as an asset proxy: ...@@ -30,22 +30,22 @@ To install a Camo server as an asset proxy:
1. Make sure your instance of GitLab is running, and that you have created a private API token. 1. Make sure your instance of GitLab is running, and that you have created a private API token.
Using the API, configure the asset proxy settings on your GitLab instance. For example: Using the API, configure the asset proxy settings on your GitLab instance. For example:
```sh ```sh
curl --request "PUT" "https://gitlab.example.com/api/v4/application/settings?\ curl --request "PUT" "https://gitlab.example.com/api/v4/application/settings?\
asset_proxy_enabled=true&\ asset_proxy_enabled=true&\
asset_proxy_url=https://proxy.gitlab.example.com&\ asset_proxy_url=https://proxy.gitlab.example.com&\
asset_proxy_secret_key=<somekey>" \ asset_proxy_secret_key=<somekey>" \
--header 'PRIVATE-TOKEN: <my_private_token>' --header 'PRIVATE-TOKEN: <my_private_token>'
``` ```
The following settings are supported: The following settings are supported:
| Attribute | Description | | Attribute | Description |
|:-------------------------|:-------------------------------------------------------------------------------------------------------------------------------------| |:-------------------------|:-------------------------------------------------------------------------------------------------------------------------------------|
| `asset_proxy_enabled` | Enable proxying of assets. If enabled, requires: `asset_proxy_url`). | | `asset_proxy_enabled` | Enable proxying of assets. If enabled, requires: `asset_proxy_url`). |
| `asset_proxy_secret_key` | Shared secret with the asset proxy server. | | `asset_proxy_secret_key` | Shared secret with the asset proxy server. |
| `asset_proxy_url` | URL of the asset proxy server. | | `asset_proxy_url` | URL of the asset proxy server. |
| `asset_proxy_whitelist` | Assets that match these domain(s) will NOT be proxied. Wildcards allowed. Your GitLab installation URL is automatically whitelisted. | | `asset_proxy_whitelist` | Assets that match these domain(s) will NOT be proxied. Wildcards allowed. Your GitLab installation URL is automatically whitelisted. |
1. Restart the server for the changes to take effect. Each time you change any values for the asset 1. Restart the server for the changes to take effect. Each time you change any values for the asset
proxy, you need to restart the server. proxy, you need to restart the server.
......
...@@ -25,9 +25,10 @@ module Gitlab ...@@ -25,9 +25,10 @@ module Gitlab
PRIVATE_TOKEN_HEADER = 'HTTP_PRIVATE_TOKEN' PRIVATE_TOKEN_HEADER = 'HTTP_PRIVATE_TOKEN'
PRIVATE_TOKEN_PARAM = :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 JOB_TOKEN_PARAM = :job_token
RUNNER_TOKEN_PARAM = :token RUNNER_TOKEN_PARAM = :token
RUNNER_JOB_TOKEN_PARAM = :token
# Check the Rails session for valid authentication details # Check the Rails session for valid authentication details
def find_user_from_warden def find_user_from_warden
...@@ -57,11 +58,13 @@ module Gitlab ...@@ -57,11 +58,13 @@ module Gitlab
def find_user_from_job_token def find_user_from_job_token
return unless route_authentication_setting[:job_token_allowed] return unless route_authentication_setting[:job_token_allowed]
token = (params[JOB_TOKEN_PARAM] || env[JOB_TOKEN_HEADER]).to_s token = current_request.params[JOB_TOKEN_PARAM].presence ||
return unless token.present? 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) 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 @current_authenticated_job = job # rubocop:disable Gitlab/ModuleWithInstanceVariables
......
...@@ -33,7 +33,8 @@ module Gitlab ...@@ -33,7 +33,8 @@ module Gitlab
find_user_from_web_access_token(request_format) || find_user_from_web_access_token(request_format) ||
find_user_from_feed_token(request_format) || find_user_from_feed_token(request_format) ||
find_user_from_static_object_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 rescue Gitlab::Auth::AuthenticationError
nil nil
end end
...@@ -45,6 +46,14 @@ module Gitlab ...@@ -45,6 +46,14 @@ module Gitlab
rescue Gitlab::Auth::AuthenticationError rescue Gitlab::Auth::AuthenticationError
false false
end end
private
def route_authentication_setting
@route_authentication_setting ||= {
job_token_allowed: api_request?
}
end
end end
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 ...@@ -5,8 +5,14 @@ module Gitlab
include Gitlab::Metrics::InfluxDb include Gitlab::Metrics::InfluxDb
include Gitlab::Metrics::Prometheus include Gitlab::Metrics::Prometheus
@error = false
def self.enabled? def self.enabled?
influx_metrics_enabled? || prometheus_metrics_enabled? influx_metrics_enabled? || prometheus_metrics_enabled?
end end
def self.error?
@error
end
end end
end end
...@@ -61,6 +61,14 @@ module Gitlab ...@@ -61,6 +61,14 @@ module Gitlab
safe_provide_metric(:histogram, name, docstring, base_labels, buckets) safe_provide_metric(:histogram, name, docstring, base_labels, buckets)
end end
def error_detected!
clear_memoization(:prometheus_metrics_enabled)
PROVIDER_MUTEX.synchronize do
@error = true
end
end
private private
def safe_provide_metric(method, name, *args) def safe_provide_metric(method, name, *args)
...@@ -81,7 +89,7 @@ module Gitlab ...@@ -81,7 +89,7 @@ module Gitlab
end end
def prometheus_metrics_enabled_unmemoized 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 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 "" ...@@ -1350,6 +1350,9 @@ msgstr ""
msgid "AdminUsers|External" msgid "AdminUsers|External"
msgstr "" msgstr ""
msgid "AdminUsers|Is using seat"
msgstr ""
msgid "AdminUsers|It's you!" msgid "AdminUsers|It's you!"
msgstr "" msgstr ""
......
...@@ -318,6 +318,7 @@ describe 'Issue Boards', :js do ...@@ -318,6 +318,7 @@ describe 'Issue Boards', :js do
wait_for_requests wait_for_requests
click_link bug.title click_link bug.title
within('.dropdown-menu-labels') { expect(page).to have_selector('.is-active', count: 3) }
click_link regression.title click_link regression.title
wait_for_requests wait_for_requests
......
...@@ -57,4 +57,14 @@ describe 'User views releases', :js do ...@@ -57,4 +57,14 @@ describe 'User views releases', :js do
expect(page).to have_content('Upcoming Release') expect(page).to have_content('Upcoming Release')
end end
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 end
...@@ -446,6 +446,93 @@ describe Gitlab::Auth::AuthFinders do ...@@ -446,6 +446,93 @@ describe Gitlab::Auth::AuthFinders do
end end
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 describe '#find_runner_from_token' do
let(:runner) { create(:ci_runner) } let(:runner) { create(:ci_runner) }
......
...@@ -42,6 +42,8 @@ describe Gitlab::Auth::RequestAuthenticator do ...@@ -42,6 +42,8 @@ describe Gitlab::Auth::RequestAuthenticator do
describe '#find_sessionless_user' do describe '#find_sessionless_user' do
let!(:access_token_user) { build(:user) } let!(:access_token_user) { build(:user) }
let!(:feed_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 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) 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 ...@@ -56,6 +58,22 @@ describe Gitlab::Auth::RequestAuthenticator do
expect(subject.find_sessionless_user([:api])).to eq feed_token_user expect(subject.find_sessionless_user([:api])).to eq feed_token_user
end 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 it 'returns nil if no user found' do
expect(subject.find_sessionless_user([:api])).to be_blank expect(subject.find_sessionless_user([:api])).to be_blank
end end
...@@ -67,6 +85,39 @@ describe Gitlab::Auth::RequestAuthenticator do ...@@ -67,6 +85,39 @@ describe Gitlab::Auth::RequestAuthenticator do
end end
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 describe '#runner' do
let!(:runner) { build(:ci_runner) } let!(:runner) { build(:ci_runner) }
......
...@@ -17,4 +17,21 @@ describe Gitlab::Metrics::Prometheus, :prometheus do ...@@ -17,4 +17,21 @@ describe Gitlab::Metrics::Prometheus, :prometheus do
expect(all_metrics.registry.metrics.count).to eq(0) expect(all_metrics.registry.metrics.count).to eq(0)
end end
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 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' ...@@ -4,11 +4,10 @@ require 'spec_helper'
describe ProjectPresenter do describe ProjectPresenter do
let(:user) { create(:user) } let(:user) { create(:user) }
let(:project) { create(:project) }
let(:presenter) { described_class.new(project, current_user: user) }
describe '#license_short_name' do describe '#license_short_name' do
let(:project) { create(:project) }
let(:presenter) { described_class.new(project, current_user: user) }
context 'when project.repository has a license_key' do context 'when project.repository has a license_key' do
it 'returns the nickname of the license if present' do it 'returns the nickname of the license if present' do
allow(project.repository).to receive(:license_key).and_return('agpl-3.0') allow(project.repository).to receive(:license_key).and_return('agpl-3.0')
...@@ -33,8 +32,6 @@ describe ProjectPresenter do ...@@ -33,8 +32,6 @@ describe ProjectPresenter do
end end
describe '#default_view' do describe '#default_view' do
let(:presenter) { described_class.new(project, current_user: user) }
context 'user not signed in' do context 'user not signed in' do
let(:user) { nil } let(:user) { nil }
...@@ -125,7 +122,6 @@ describe ProjectPresenter do ...@@ -125,7 +122,6 @@ describe ProjectPresenter do
describe '#can_current_user_push_code?' do describe '#can_current_user_push_code?' do
let(:project) { create(:project, :repository) } let(:project) { create(:project, :repository) }
let(:presenter) { described_class.new(project, current_user: user) }
context 'empty repo' do context 'empty repo' do
let(:project) { create(:project) } let(:project) { create(:project) }
...@@ -163,7 +159,6 @@ describe ProjectPresenter do ...@@ -163,7 +159,6 @@ describe ProjectPresenter do
context 'statistics anchors (empty repo)' do context 'statistics anchors (empty repo)' do
let(:project) { create(:project, :empty_repo) } let(:project) { create(:project, :empty_repo) }
let(:presenter) { described_class.new(project, current_user: user) }
describe '#files_anchor_data' do describe '#files_anchor_data' do
it 'returns files data' do it 'returns files data' do
...@@ -200,7 +195,6 @@ describe ProjectPresenter do ...@@ -200,7 +195,6 @@ describe ProjectPresenter do
context 'statistics anchors' do context 'statistics anchors' do
let(:project) { create(:project, :repository) } let(:project) { create(:project, :repository) }
let(:presenter) { described_class.new(project, current_user: user) }
describe '#files_anchor_data' do describe '#files_anchor_data' do
it 'returns files data' do it 'returns files data' do
...@@ -416,7 +410,6 @@ describe ProjectPresenter do ...@@ -416,7 +410,6 @@ describe ProjectPresenter do
describe '#statistics_buttons' do describe '#statistics_buttons' do
let(:project) { build(:project) } let(:project) { build(:project) }
let(:presenter) { described_class.new(project, current_user: user) }
it 'orders the items correctly' do it 'orders the items correctly' do
allow(project.repository).to receive(:readme).and_return(double(name: 'readme')) allow(project.repository).to receive(:readme).and_return(double(name: 'readme'))
...@@ -435,8 +428,6 @@ describe ProjectPresenter do ...@@ -435,8 +428,6 @@ describe ProjectPresenter do
end end
describe '#repo_statistics_buttons' do describe '#repo_statistics_buttons' do
let(:presenter) { described_class.new(project, current_user: user) }
subject(:empty_repo_statistics_buttons) { presenter.empty_repo_statistics_buttons } subject(:empty_repo_statistics_buttons) { presenter.empty_repo_statistics_buttons }
before do before do
...@@ -485,4 +476,73 @@ describe ProjectPresenter do ...@@ -485,4 +476,73 @@ describe ProjectPresenter do
end end
end 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 end
...@@ -96,4 +96,28 @@ describe ReleasePresenter do ...@@ -96,4 +96,28 @@ describe ReleasePresenter do
it { is_expected.to be_nil } it { is_expected.to be_nil }
end end
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 end
...@@ -115,6 +115,16 @@ describe API::Releases do ...@@ -115,6 +115,16 @@ describe API::Releases do
end end
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 context 'when user is a guest' do
let!(:release) do let!(:release) do
create(:release, 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 ...@@ -8,6 +8,12 @@ describe Prometheus::ProxyService do
set(:project) { create(:project) } set(:project) { create(:project) }
set(:environment) { create(:environment, project: 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 describe '#initialize' do
let(:params) { ActionController::Parameters.new(query: '1').permit! } 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