Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
5afd857550
commit
cddaddb86b
|
@ -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:
|
||||
|
|
2
Gemfile
2
Gemfile
|
@ -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
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
# 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
|
|
@ -0,0 +1,5 @@
|
|||
# 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
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Improve link generation performance
|
||||
merge_request: 22426
|
||||
author:
|
||||
type: performance
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Authenticate API requests with job tokens for Rack::Attack
|
||||
merge_request: 21412
|
||||
author:
|
||||
type: fixed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Increase size of issue boards sidebar collapse button
|
||||
merge_request:
|
||||
author:
|
||||
type: fixed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Fix releases page when tag contains a slash
|
||||
merge_request: 22527
|
||||
author:
|
||||
type: fixed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Disable Prometheus metrics if initialization fails
|
||||
merge_request: 22355
|
||||
author:
|
||||
type: fixed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
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
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
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
|
||||
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
# 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.
|
||||
|
|
|
@ -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.
|
||||
Using the API, configure the asset proxy settings on your GitLab instance. For example:
|
||||
|
||||
```sh
|
||||
curl --request "PUT" "https://gitlab.example.com/api/v4/application/settings?\
|
||||
asset_proxy_enabled=true&\
|
||||
asset_proxy_url=https://proxy.gitlab.example.com&\
|
||||
asset_proxy_secret_key=<somekey>" \
|
||||
--header 'PRIVATE-TOKEN: <my_private_token>'
|
||||
```
|
||||
```sh
|
||||
curl --request "PUT" "https://gitlab.example.com/api/v4/application/settings?\
|
||||
asset_proxy_enabled=true&\
|
||||
asset_proxy_url=https://proxy.gitlab.example.com&\
|
||||
asset_proxy_secret_key=<somekey>" \
|
||||
--header 'PRIVATE-TOKEN: <my_private_token>'
|
||||
```
|
||||
|
||||
The following settings are supported:
|
||||
The following settings are supported:
|
||||
|
||||
| Attribute | Description |
|
||||
|:-------------------------|:-------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `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_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. |
|
||||
| Attribute | Description |
|
||||
|:-------------------------|:-------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `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_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. |
|
||||
|
||||
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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
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
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
# 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
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
# 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) }
|
||||
let(:project) { create(:project) }
|
||||
let(:presenter) { described_class.new(project, current_user: user) }
|
||||
|
||||
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
|
||||
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,
|
||||
|
|
|
@ -0,0 +1,54 @@
|
|||
# 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! }
|
||||
|
||||
|
|
Loading…
Reference in New Issue