Commit 4e9acbfb authored by GitLab Bot's avatar GitLab Bot

Add latest changes from gitlab-org/gitlab@master

parent 506d6dcd
......@@ -22,15 +22,18 @@ export default class MirrorRepos {
}
initMirrorPush() {
this.$keepDivergentRefsInput = $('.js-mirror-keep-divergent-refs', this.$form);
this.$passwordGroup = $('.js-password-group', this.$container);
this.$password = $('.js-password', this.$passwordGroup);
this.$authMethod = $('.js-auth-method', this.$form);
this.$keepDivergentRefsInput.on('change', () => this.updateKeepDivergentRefs());
this.$authMethod.on('change', () => this.togglePassword());
this.$password.on('input.updateUrl', () => this.debouncedUpdateUrl());
this.initMirrorSSH();
this.updateProtectedBranches();
this.updateKeepDivergentRefs();
}
initMirrorSSH() {
......@@ -61,6 +64,16 @@ export default class MirrorRepos {
$('.js-mirror-protected-hidden', this.$form).val(val);
}
updateKeepDivergentRefs() {
const field = this.$keepDivergentRefsInput.get(0);
// This field only exists after the form is switched to 'Push' mode
if (field) {
const val = field.checked ? this.$keepDivergentRefsInput.val() : '0';
$('.js-mirror-keep-divergent-refs-hidden', this.$form).val(val);
}
}
registerUpdateListeners() {
this.debouncedUpdateUrl = debounce(() => this.updateUrl(), 200);
this.$urlInput.on('input', () => this.debouncedUpdateUrl());
......
......@@ -77,6 +77,7 @@ class Projects::MirrorsController < Projects::ApplicationController
id
enabled
only_protected_branches
keep_divergent_refs
auth_method
password
ssh_known_hosts
......
......@@ -30,26 +30,12 @@ class AuditEvent < ApplicationRecord
end
def author_name
lazy_author.name
self.user.name
end
def formatted_details
details.merge(details.slice(:from, :to).transform_values(&:to_s))
end
def lazy_author
BatchLoader.for(author_id).batch(default_value: default_author_value) do |author_ids, loader|
User.where(id: author_ids).find_each do |user|
loader.call(user.id, user)
end
end
end
private
def default_author_value
::Gitlab::Audit::NullAuthor.for(author_id, details[:author_name])
end
end
AuditEvent.prepend_if_ee('EE::AuditEvent')
......@@ -1825,7 +1825,7 @@ class User < ApplicationRecord
return if restrictions.blank?
if Gitlab::UntrustedRegexp.new(restrictions).match?(email)
errors.add(:email, _('is not allowed for sign-up'))
errors.add(:email, _('is not allowed. Try again with a different email address, or contact your GitLab admin.'))
end
end
......
# frozen_string_literal: true
class PagesDomainPresenter < Gitlab::View::Presenter::Delegated
presents :pages_domain
def needs_verification?
Gitlab::CurrentSettings.pages_domain_verification_enabled? && unverified?
end
def show_auto_ssl_failed_warning?
return false unless Feature.enabled?(:pages_letsencrypt_errors, pages_domain.project)
# validations prevents auto ssl from working, so there is no need to show that warning until
return false if needs_verification?
::Gitlab::LetsEncrypt.enabled? && auto_ssl_failed
end
end
......@@ -13,7 +13,7 @@ class AuditEventService
#
# @return [AuditEventService]
def initialize(author, entity, details = {})
@author = build_author(author)
@author = author
@entity = entity
@details = details
end
......@@ -49,14 +49,6 @@ class AuditEventService
private
def build_author(author)
if author.is_a?(User)
author
else
Gitlab::Audit::UnauthenticatedAuthor.new(name: author)
end
end
def base_payload
{
author_id: @author.id,
......
......@@ -5,9 +5,18 @@
module Metrics
module Dashboard
class CloneDashboardService < ::BaseService
include Stepable
ALLOWED_FILE_TYPE = '.yml'
USER_DASHBOARDS_DIR = ::Metrics::Dashboard::CustomDashboardService::DASHBOARD_ROOT
steps :check_push_authorized,
:check_branch_name,
:check_file_type,
:check_dashboard_template,
:create_file,
:refresh_repository_method_caches
class << self
def allowed_dashboard_templates
@allowed_dashboard_templates ||= Set[::Metrics::Dashboard::SystemDashboardService::DASHBOARD_PATH].freeze
......@@ -22,21 +31,52 @@ module Metrics
end
end
# rubocop:disable Cop/BanCatchThrow
def execute
catch(:error) do
throw(:error, error(_(%q(You are not allowed to push into this branch. Create another branch or open a merge request.)), :forbidden)) unless push_authorized?
execute_steps
end
private
def check_push_authorized(result)
return error(_('You are not allowed to push into this branch. Create another branch or open a merge request.'), :forbidden) unless push_authorized?
success(result)
end
def check_branch_name(result)
return error(_('There was an error creating the dashboard, branch name is invalid.'), :bad_request) unless valid_branch_name?
return error(_('There was an error creating the dashboard, branch named: %{branch} already exists.') % { branch: params[:branch] }, :bad_request) unless new_or_default_branch?
success(result)
end
result = ::Files::CreateService.new(project, current_user, dashboard_attrs).execute
throw(:error, wrap_error(result)) unless result[:status] == :success
def check_file_type(result)
return error(_('The file name should have a .yml extension'), :bad_request) unless target_file_type_valid?
repository.refresh_method_caches([:metrics_dashboard])
success(result.merge(http_status: :created, dashboard: dashboard_details))
success(result)
end
def check_dashboard_template(result)
return error(_('Not found.'), :not_found) unless self.class.allowed_dashboard_templates.include?(params[:dashboard])
success(result)
end
def create_file(result)
create_file_response = ::Files::CreateService.new(project, current_user, dashboard_attrs).execute
if create_file_response[:status] == :success
success(result.merge(create_file_response))
else
wrap_error(create_file_response)
end
end
# rubocop:enable Cop/BanCatchThrow
private
def refresh_repository_method_caches(result)
repository.refresh_method_caches([:metrics_dashboard])
success(result.merge(http_status: :created, dashboard: dashboard_details))
end
def dashboard_attrs
{
......@@ -62,26 +102,13 @@ module Metrics
Gitlab::UserAccess.new(current_user, project: project).can_push_to_branch?(branch)
end
# rubocop:disable Cop/BanCatchThrow
def dashboard_template
@dashboard_template ||= begin
throw(:error, error(_('Not found.'), :not_found)) unless self.class.allowed_dashboard_templates.include?(params[:dashboard])
params[:dashboard]
end
@dashboard_template ||= params[:dashboard]
end
# rubocop:enable Cop/BanCatchThrow
# rubocop:disable Cop/BanCatchThrow
def branch
@branch ||= begin
throw(:error, error(_('There was an error creating the dashboard, branch name is invalid.'), :bad_request)) unless valid_branch_name?
throw(:error, error(_('There was an error creating the dashboard, branch named: %{branch} already exists.') % { branch: params[:branch] }, :bad_request)) unless new_or_default_branch? # temporary validation for first UI iteration
params[:branch]
end
@branch ||= params[:branch]
end
# rubocop:enable Cop/BanCatchThrow
def new_or_default_branch?
!repository.branch_exists?(params[:branch]) || project.default_branch == params[:branch]
......@@ -95,20 +122,22 @@ module Metrics
@new_dashboard_path ||= File.join(USER_DASHBOARDS_DIR, file_name)
end
# rubocop:disable Cop/BanCatchThrow
def file_name
@file_name ||= begin
throw(:error, error(_('The file name should have a .yml extension'), :bad_request)) unless target_file_type_valid?
File.basename(params[:file_name])
end
@file_name ||= File.basename(params[:file_name])
end
# rubocop:enable Cop/BanCatchThrow
def target_file_type_valid?
File.extname(params[:file_name]) == ALLOWED_FILE_TYPE
end
def wrap_error(result)
if result[:message] == 'A file with this name already exists'
error(_("A file with '%{file_name}' already exists in %{branch} branch") % { file_name: file_name, branch: branch }, :bad_request)
else
result
end
end
def new_dashboard_content
::Gitlab::Metrics::Dashboard::Processor
.new(project, raw_dashboard, sequence, {})
......@@ -119,14 +148,6 @@ module Metrics
@repository ||= project.repository
end
def wrap_error(result)
if result[:message] == 'A file with this name already exists'
error(_("A file with '%{file_name}' already exists in %{branch} branch") % { file_name: file_name, branch: branch }, :bad_request)
else
result
end
end
def raw_dashboard
YAML.safe_load(File.read(Rails.root.join(dashboard_template)))
end
......
- protocols = Gitlab::UrlSanitizer::ALLOWED_SCHEMES.join('|')
- keep_divergent_refs = Feature.enabled?(:keep_divergent_refs, @project)
= f.fields_for :remote_mirrors, @project.remote_mirrors.build do |rm_f|
= rm_f.hidden_field :enabled, value: '1'
= rm_f.hidden_field :url, class: 'js-mirror-url-hidden', required: true, pattern: "(#{protocols}):\/\/.+"
= rm_f.hidden_field :only_protected_branches, class: 'js-mirror-protected-hidden'
- if keep_divergent_refs
= rm_f.hidden_field :keep_divergent_refs, class: 'js-mirror-keep-divergent-refs-hidden'
= render partial: 'projects/mirrors/ssh_host_keys', locals: { f: rm_f }
= render partial: 'projects/mirrors/authentication_method', locals: { f: rm_f }
- if keep_divergent_refs
.form-check.append-bottom-10
= check_box_tag :keep_divergent_refs, '1', false, class: 'js-mirror-keep-divergent-refs form-check-input'
= label_tag :keep_divergent_refs, 'Keep divergent refs', class: 'form-check-label'
......@@ -6,6 +6,7 @@
Domains (#{@domains.count})
%ul.list-group.list-group-flush.pages-domain-list{ class: ("has-verification-status" if verification_enabled) }
- @domains.each do |domain|
- domain = Gitlab::View::Presenter::Factory.new(domain, current_user: current_user).fabricate!
%li.pages-domain-list-item.list-group-item.d-flex.justify-content-between
- if verification_enabled
- tooltip, status = domain.unverified? ? [s_('GitLabPages|Unverified'), 'failed'] : [s_('GitLabPages|Verified'), 'success']
......@@ -13,20 +14,27 @@
= sprite_icon("status_#{status}", size: 16 )
.domain-name
= external_link(domain.url, domain.url)
- if domain.subject
- if domain.certificate
%div
%span.badge.badge-gray
= s_('GitLabPages|Certificate: %{subject}') % { subject: domain.subject }
= s_('GitLabPages|Certificate: %{subject}') % { subject: domain.pages_domain.subject }
- if domain.expired?
%span.badge.badge-danger
= s_('GitLabPages|Expired')
%div
= link_to s_('GitLabPages|Edit'), project_pages_domain_path(@project, domain), class: "btn btn-sm btn-grouped btn-success btn-inverted"
= link_to s_('GitLabPages|Remove'), project_pages_domain_path(@project, domain), data: { confirm: s_('GitLabPages|Are you sure?')}, method: :delete, class: "btn btn-remove btn-sm btn-grouped"
- if verification_enabled && domain.unverified?
- if domain.needs_verification?
%li.list-group-item.bs-callout-warning
- details_link_start = "<a href='#{project_pages_domain_path(@project, domain)}'>".html_safe
- details_link_end = '</a>'.html_safe
= s_('GitLabPages|%{domain} is not verified. To learn how to verify ownership, visit your %{link_start}domain details%{link_end}.').html_safe % { domain: domain.domain,
link_start: details_link_start,
link_end: details_link_end }
- if domain.show_auto_ssl_failed_warning?
%li.list-group-item.bs-callout-warning
- details_link_start = "<a href='#{project_pages_domain_path(@project, domain)}'>".html_safe
- details_link_end = '</a>'.html_safe
= s_("GitLabPages|Something went wrong while obtaining Let's Encrypt certificate for %{domain}. To retry visit your %{link_start}domain details%{link_end}.").html_safe % { domain: domain.domain,
link_start: details_link_start,
link_end: details_link_end }
---
title: Move some global routes to - scope
merge_request: 27106
author:
type: changed
......@@ -71,6 +71,8 @@ Rails.application.routes.draw do
# Health check
get 'health_check(/:checks)' => 'health_check#index', as: :health_check
# Begin of the /-/ scope.
# Use this scope for all new global routes.
scope path: '-' do
# '/-/health' implemented by BasicHealthCheck middleware
get 'liveness' => 'health#liveness'
......@@ -122,6 +124,10 @@ Rails.application.routes.draw do
draw :country_state
draw :subscription
draw :analytics
scope '/push_from_secondary/:geo_node_id' do
draw :git_http
end
end
if ENV['GITLAB_CHAOS_SECRET'] || Rails.env.development? || Rails.env.test?
......@@ -136,7 +142,24 @@ Rails.application.routes.draw do
# Notification settings
resources :notification_settings, only: [:create, :update]
resources :invites, only: [:show], constraints: { id: /[A-Za-z0-9_-]+/ } do
member do
post :accept
match :decline, via: [:get, :post]
end
end
resources :sent_notifications, only: [], constraints: { id: /\h{32}/ } do
member do
get :unsubscribe
end
end
# Spam reports
resources :abuse_reports, only: [:new, :create]
end
# End of the /-/ scope.
concern :clusterable do
resources :clusters, only: [:index, :new, :show, :update, :destroy] do
......@@ -167,22 +190,24 @@ Rails.application.routes.draw do
end
end
# Invites
resources :invites, only: [:show], constraints: { id: /[A-Za-z0-9_-]+/ } do
member do
post :accept
match :decline, via: [:get, :post]
# Deprecated routes.
# Will be removed as part of https://gitlab.com/gitlab-org/gitlab/-/issues/210024
scope as: :deprecated do
resources :invites, only: [:show], constraints: { id: /[A-Za-z0-9_-]+/ } do
member do
post :accept
match :decline, via: [:get, :post]
end
end
end
resources :sent_notifications, only: [], constraints: { id: /\h{32}/ } do
member do
get :unsubscribe
resources :sent_notifications, only: [], constraints: { id: /\h{32}/ } do
member do
get :unsubscribe
end
end
end
# Spam reports
resources :abuse_reports, only: [:new, :create]
resources :abuse_reports, only: [:new, :create]
end
resources :groups, only: [:index, :new, :create] do
post :preview_markdown
......@@ -192,12 +217,6 @@ Rails.application.routes.draw do
get '/projects/:id' => 'projects#resolve'
Gitlab.ee do
scope '/-/push_from_secondary/:geo_node_id' do
draw :git_http
end
end
draw :git_http
draw :api
draw :sidekiq
......
......@@ -4,6 +4,6 @@ class AddPagesHttpsOnlyToProjects < ActiveRecord::Migration[4.2]
DOWNTIME = false
def change
add_column :projects, :pages_https_only, :boolean
add_column :projects, :pages_https_only, :boolean # rubocop:disable Migration/AddColumnsToWideTables
end
end
......@@ -2,6 +2,6 @@ class AddIncludePrivateContributionsToUsers < ActiveRecord::Migration[4.2]
DOWNTIME = false
def change
add_column :users, :include_private_contributions, :boolean
add_column :users, :include_private_contributions, :boolean # rubocop:disable Migration/AddColumnsToWideTables
end
end
......@@ -6,7 +6,7 @@ class AddRemoteMirrorAvailableOverriddenToProjects < ActiveRecord::Migration[4.2
disable_ddl_transaction!
def up
add_column(:projects, :remote_mirror_available_overridden, :boolean) unless column_exists?(:projects, :remote_mirror_available_overridden)
add_column(:projects, :remote_mirror_available_overridden, :boolean) unless column_exists?(:projects, :remote_mirror_available_overridden) # rubocop:disable Migration/AddColumnsToWideTables
end
def down
......
......@@ -5,6 +5,6 @@ class AddPrivateProfileToUsers < ActiveRecord::Migration[4.2]
DOWNTIME = false
def change
add_column :users, :private_profile, :boolean
add_column :users, :private_profile, :boolean # rubocop:disable Migration/AddColumnsToWideTables
end
end
......@@ -27,7 +27,11 @@ class AddCommitEmailToUsers < ActiveRecord::Migration[4.2]
# comments:
# disable_ddl_transaction!
# rubocop:disable Migration/AddLimitToStringColumns
# rubocop:disable Migration/AddColumnsToWideTables
def change
add_column :users, :commit_email, :string # rubocop:disable Migration/AddLimitToStringColumns
add_column :users, :commit_email, :string
end
# rubocop:enable Migration/AddLimitToStringColumns
# rubocop:enable Migration/AddColumnsToWideTables
end
......@@ -4,6 +4,6 @@ class AddScheduledAtToCiBuilds < ActiveRecord::Migration[4.2]
DOWNTIME = false
def change
add_column :ci_builds, :scheduled_at, :datetime_with_timezone
add_column :ci_builds, :scheduled_at, :datetime_with_timezone # rubocop:disable Migration/AddColumnsToWideTables
end
end
......@@ -9,7 +9,7 @@ class AddRepositoriesTable < ActiveRecord::Migration[4.2]
t.string :disk_path, null: false, index: { unique: true } # rubocop:disable Migration/AddLimitToStringColumns
end
add_column :projects, :pool_repository_id, :bigint
add_column :projects, :pool_repository_id, :bigint # rubocop:disable Migration/AddColumnsToWideTables
add_index :projects, :pool_repository_id, where: 'pool_repository_id IS NOT NULL'
end
end
......@@ -5,7 +5,11 @@ class AddEncryptedRunnersTokenToProjects < ActiveRecord::Migration[4.2]
DOWNTIME = false
# rubocop:disable Migration/AddColumnsToWideTables
# rubocop:disable Migration/AddLimitToStringColumns
def change
add_column :projects, :runners_token_encrypted, :string # rubocop:disable Migration/AddLimitToStringColumns
add_column :projects, :runners_token_encrypted, :string
end
# rubocop:enable Migration/AddColumnsToWideTables
# rubocop:enable Migration/AddLimitToStringColumns
end
......@@ -5,7 +5,11 @@ class AddTokenEncryptedToCiBuilds < ActiveRecord::Migration[5.0]
DOWNTIME = false
# rubocop:disable Migration/AddColumnsToWideTables
# rubocop:disable Migration/AddLimitToStringColumns
def change
add_column :ci_builds, :token_encrypted, :string # rubocop:disable Migration/AddLimitToStringColumns
add_column :ci_builds, :token_encrypted, :string
end
# rubocop:enable Migration/AddColumnsToWideTables
# rubocop:enable Migration/AddLimitToStringColumns
end
......@@ -3,7 +3,11 @@
class AddProjectBfgObjectMapColumn < ActiveRecord::Migration[5.0]
DOWNTIME = false
# rubocop:disable Migration/AddColumnsToWideTables
# rubocop:disable Migration/AddLimitToStringColumns
def change
add_column :projects, :bfg_object_map, :string # rubocop:disable Migration/AddLimitToStringColumns
add_column :projects, :bfg_object_map, :string
end
# rubocop:enable Migration/AddColumnsToWideTables
# rubocop:enable Migration/AddLimitToStringColumns
end
......@@ -7,6 +7,6 @@ class AddDetectedRepositoryLanguagesToProjects < ActiveRecord::Migration[5.0]
DOWNTIME = false
def change
add_column :projects, :detected_repository_languages, :boolean
add_column :projects, :detected_repository_languages, :boolean # rubocop:disable Migration/AddColumnsToWideTables
end
end
......@@ -10,6 +10,6 @@ class AddBridgedPipelineIdToBridges < ActiveRecord::Migration[5.0]
DOWNTIME = false
def change
add_column :ci_builds, :upstream_pipeline_id, :integer
add_column :ci_builds, :upstream_pipeline_id, :integer # rubocop:disable Migration/AddColumnsToWideTables
end
end
......@@ -4,6 +4,6 @@ class AddProjectEmailsDisabled < ActiveRecord::Migration[5.2]
DOWNTIME = false
def change
add_column :projects, :emails_disabled, :boolean
add_column :projects, :emails_disabled, :boolean # rubocop:disable Migration/AddColumnsToWideTables
end
end
......@@ -9,7 +9,7 @@ class AddStaticObjectTokenToUsers < ActiveRecord::Migration[5.2]
disable_ddl_transaction!
def up
add_column :users, :static_object_token, :string, limit: 255
add_column :users, :static_object_token, :string, limit: 255 # rubocop:disable Migration/AddColumnsToWideTables
end
def down
......
......@@ -7,8 +7,10 @@ class AddFirstLastNameToUser < ActiveRecord::Migration[5.2]
# Set this constant to true if this migration requires downtime.
DOWNTIME = false
# rubocop:disable Migration/AddColumnsToWideTables
def change
add_column(:users, :first_name, :string, null: true, limit: 255)
add_column(:users, :last_name, :string, null: true, limit: 255)
end
# rubocop:enable Migration/AddColumnsToWideTables
end
......@@ -4,6 +4,6 @@ class AddProjectsMaxPagesSize < ActiveRecord::Migration[5.2]
DOWNTIME = false
def change
add_column :projects, :max_pages_size, :integer
add_column :projects, :max_pages_size, :integer # rubocop:disable Migration/AddColumnsToWideTables
end
end
......@@ -4,6 +4,6 @@ class AddProjectsMaxArtifactsSize < ActiveRecord::Migration[5.2]
DOWNTIME = false
def change
add_column :projects, :max_artifacts_size, :integer
add_column :projects, :max_artifacts_size, :integer # rubocop:disable Migration/AddColumnsToWideTables
end
end
......@@ -7,6 +7,6 @@ class AddRoleToUsers < ActiveRecord::Migration[5.2]
DOWNTIME = false
def change
add_column :users, :role, :smallint
add_column :users, :role, :smallint # rubocop:disable Migration/AddColumnsToWideTables
end
end
......@@ -4,6 +4,6 @@ class AddPullMirrorBranchPrefixToProjects < ActiveRecord::Migration[5.2]
DOWNTIME = false
def change
add_column :projects, :pull_mirror_branch_prefix, :string, limit: 50
add_column :projects, :pull_mirror_branch_prefix, :string, limit: 50 # rubocop:disable Migration/AddColumnsToWideTables
end
end
......@@ -4,8 +4,10 @@ class AddMarkForDeletionToProjects < ActiveRecord::Migration[5.2]
# Set this constant to true if this migration requires downtime.
DOWNTIME = false
# rubocop:disable Migration/AddColumnsToWideTables
def change
add_column :projects, :marked_for_deletion_at, :date
add_column :projects, :marked_for_deletion_by_user_id, :integer
end
# rubocop:enable Migration/AddColumnsToWideTables
end
......@@ -8,7 +8,7 @@ class AddRemoveSourceBranchAfterMergeToProjects < ActiveRecord::Migration[5.1]
DOWNTIME = false
def up
add_column :projects, :remove_source_branch_after_merge, :boolean
add_column :projects, :remove_source_branch_after_merge, :boolean # rubocop:disable Migration/AddColumnsToWideTables
end
def down
......
......@@ -4,6 +4,6 @@ class AddProcessedToCiBuilds < ActiveRecord::Migration[5.2]
DOWNTIME = false
def change
add_column :ci_builds, :processed, :boolean
add_column :ci_builds, :processed, :boolean # rubocop:disable Migration/AddColumnsToWideTables
end
end
......@@ -3,6 +3,7 @@
class AddResourceGroupIdToCiBuilds < ActiveRecord::Migration[5.2]
DOWNTIME = false
# rubocop:disable Migration/AddColumnsToWideTables
def up
unless column_exists?(:ci_builds, :resource_group_id)
add_column :ci_builds, :resource_group_id, :bigint
......@@ -12,6 +13,7 @@ class AddResourceGroupIdToCiBuilds < ActiveRecord::Migration[5.2]
add_column :ci_builds, :waiting_for_resource_at, :datetime_with_timezone
end
end
# rubocop:enable Migration/AddColumnsToWideTables
def down
if column_exists?(:ci_builds, :resource_group_id)
......
......@@ -4,6 +4,6 @@ class AddSuggestionCommitMessageToProjects < ActiveRecord::Migration[5.2]
DOWNTIME = false
def change
add_column :projects, :suggestion_commit_message, :string, limit: 255
add_column :projects, :suggestion_commit_message, :string, limit: 255 # rubocop:disable Migration/AddColumnsToWideTables
end
end
......@@ -6,6 +6,6 @@ class AddSchedulingTypeToCiBuilds < ActiveRecord::Migration[5.2]
DOWNTIME = false
def change
add_column :ci_builds, :scheduling_type, :integer, limit: 2
add_column :ci_builds, :scheduling_type, :integer, limit: 2 # rubocop:disable Migration/AddColumnsToWideTables
end
end
......@@ -4,6 +4,6 @@ class AddAutocloseReferencedIssuesToProjects < ActiveRecord::Migration[5.2]
DOWNTIME = false
def change
add_column :projects, :autoclose_referenced_issues, :boolean
add_column :projects, :autoclose_referenced_issues, :boolean # rubocop:disable Migration/AddColumnsToWideTables
end
end
......@@ -7,7 +7,7 @@ class AddUserType < ActiveRecord::Migration[6.0]
def up
with_lock_retries do
add_column :users, :user_type, :integer, limit: 2
add_column :users, :user_type, :integer, limit: 2 # rubocop:disable Migration/AddColumnsToWideTables
end
end
......
......@@ -23,7 +23,7 @@ class DropProjectsCiId < ActiveRecord::Migration[5.1]
def down
unless column_exists?(:projects, :ci_id)
add_column :projects, :ci_id, :integer
add_column :projects, :ci_id, :integer # rubocop:disable Migration/AddColumnsToWideTables
end
unless index_exists?(:projects, :ci_id)
......
......@@ -17,7 +17,7 @@ class RemoveUsersSupportType < ActiveRecord::Migration[5.1]
end
def down
add_column :users, :support_bot, :boolean
add_column :users, :support_bot, :boolean # rubocop:disable Migration/AddColumnsToWideTables
add_concurrent_index :users, :support_bot
add_concurrent_index :users, :state,
......
......@@ -15,7 +15,7 @@ class DropMergeRequestsRequireCodeOwnerApprovalFromProjects < ActiveRecord::Migr
end
def down
add_column :projects, :merge_requests_require_code_owner_approval, :boolean
add_column :projects, :merge_requests_require_code_owner_approval, :boolean # rubocop:disable Migration/AddColumnsToWideTables
add_concurrent_index(
:projects,
......
# Parsing GitLab logs with `jq`
We recommend using log aggregation and search tools like Kibana and Splunk whenever possible,
but if they are not available you can still quickly parse
[GitLab logs](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/26311) in JSON format
(the default since GitLab 12.0) using [`jq`](https://stedolan.github.io/jq/).
## What is JQ?
As noted in its [manual](https://stedolan.github.io/jq/manual), jq is a command-line JSON processor. The following examples
include use cases targeted for parsing GitLab log files.
## Parsing Logs
### General Commands
#### Pipe colorized `jq` output into `less`
```sh
jq . <FILE> -C | less -R
```
#### Search for a term and pretty-print all matching lines
```sh
grep <TERM> <FILE> | jq .
```
#### Skip invalid lines of JSON
```sh
jq -cR 'fromjson?' file.json | jq <COMMAND>
```
By default `jq` will error out when it encounters a line that is not valid JSON.
This skips over all invalid lines and parses the rest.
### Parsing `production_json.log` and `api_json.log`
#### Find all requests with a 5XX status code
```sh
jq 'select(status >= 500)' <FILE>
```
#### Top 10 slowest requests
```sh
jq -s 'sort_by(-.duration) | limit(10; .[])' <FILE>
```
#### Find and pretty print all requests related to a project
```sh
grep <PROJECT_NAME> <FILE> | jq .
```
#### Find all requests with a total duration > 5 seconds
```sh
jq 'select(.duration > 5000)' <FILE>
```
#### Find all project requests with more than 5 rugged calls
```sh
grep <PROJECT_NAME> <FILE> | jq 'select(.rugged_calls > 5)'
```
#### Find all requests with a Gitaly duration > 10 seconds
```sh
jq 'select(.gitaly_duration > 10000)' <FILE>
```
#### Find all requests with a queue duration > 10 seconds
```sh
jq 'select(.queue_duration > 10000)' <FILE>
```
#### Top 10 requests by # of Gitaly calls
```sh
jq -s 'map(select(.gitaly_calls != null)) | sort_by(-.gitaly_calls) | limit(10; .[])' <FILE>
```
### Parsing `production_json.log`
#### Print the top three controller methods by request volume and their three longest durations
```sh
jq -s -r 'group_by(.controller+.action) | sort_by(-length) | limit(3; .[]) | sort_by(-.duration) | "CT: \(length)\tMETHOD: \(.[0].controller)#\(.[0].action)\tDURS: \(.[0].duration), \(.[1].duration), \(.[2].duration)"' production_json.log
```
**Example output**
```plaintext
CT: 2721 METHOD: SessionsController#new DURS: 844.06, 713.81, 704.66
CT: 2435 METHOD: MetricsController#index DURS: 299.29, 284.01, 158.57
CT: 1328 METHOD: Projects::NotesController#index DURS: 403.99, 386.29, 384.39
```
### Parsing `api_json.log`
#### Print top three routes with request count and their three longest durations
```sh
jq -s -r 'group_by(.route) | sort_by(-length) | limit(3; .[]) | sort_by(-.duration) | "CT: \(length)\tROUTE: \(.[0].route)\tDURS: \(.[0].duration), \(.[1].duration), \(.[2].duration)"' api_json.log
```
**Example output**
```plaintext
CT: 2472 ROUTE: /api/:version/internal/allowed DURS: 56402.65, 38411.43, 19500.41
CT: 297 ROUTE: /api/:version/projects/:id/repository/tags DURS: 731.39, 685.57, 480.86
CT: 190 ROUTE: /api/:version/projects/:id/repository/commits DURS: 1079.02, 979.68, 958.21
```
### Parsing `gitaly/current`
#### Find all Gitaly requests sent from web UI
```sh
jq 'select(."grpc.meta.client_name" == "gitlab-web")' current
```
#### Find all failed Gitaly requests
```sh
jq 'select(."grpc.code" != null and ."grpc.code" != "OK")' current
```
#### Find all requests that took longer than 30 seconds
```sh
jq 'select(."grpc.time_ms" > 30000)' current
```
#### Print top three projects by request volume and their three longest durations
```sh
jq -s -r 'map(select(."grpc.request.glProjectPath" != null and ."grpc.request.glProjectPath" != "" and ."grpc.time_ms" != null)) | group_by(."grpc.request.glProjectPath") | sort_by(-length) | limit(3; .[]) | sort_by(-."grpc.time_ms") | "CT: \(length)\tPROJECT: \(.[0]."grpc.request.glProjectPath")\tDURS: \(.[0]."grpc.time_ms"), \(.[1]."grpc.time_ms"), \(.[2]."grpc.time_ms")"' current
```
**Example output**
```plaintext
CT: 635 PROJECT: groupA/project1 DURS: 4292.269, 4228.853, 2885.548
CT: 462 PROJECT: groupB/project5 DURS: 4368.981, 3623.553, 361.399
CT: 455 PROJECT: groupC/project7 DURS: 387.295, 381.874, 373.988
```
......@@ -115,12 +115,12 @@ RDS instances as well:
1. Follow the same steps to create all subnets:
| Name tag | Type | Availability Zone | CIDR block |
| ------------------------- | ------- | ----------------- | ---------- |
| `gitlab-public-10.0.0.0` | public | `us-west-2a` | `10.0.0.0` |
| `gitlab-private-10.0.1.0` | private | `us-west-2a` | `10.0.1.0` |
| `gitlab-public-10.0.2.0` | public | `us-west-2b` | `10.0.2.0` |
| `gitlab-private-10.0.3.0` | private | `us-west-2b` | `10.0.3.0` |
| Name tag | Type | Availability Zone | CIDR block |
| ------------------------- | ------- | ----------------- | ------------- |
| `gitlab-public-10.0.0.0` | public | `us-west-2a` | `10.0.0.0/24` |
| `gitlab-private-10.0.1.0` | private | `us-west-2a` | `10.0.1.0/24` |
| `gitlab-public-10.0.2.0` | public | `us-west-2b` | `10.0.2.0/24` |
| `gitlab-private-10.0.3.0` | private | `us-west-2b` | `10.0.3.0/24` |
### Create NAT Gateways
......
......@@ -434,6 +434,35 @@ Note the following properties:
![anomaly panel type](img/prometheus_dashboard_anomaly_panel_type.png)
##### Bar chart
To add a bar chart to a dashboard, look at the following sample dashboard file:
```yaml
dashboard: 'Dashboard Title'
panel_groups:
- group: 'Group title'
panels:
- type: bar
title: "Http Handlers"
x_label: 'Response Size'
y_axis:
name: "Handlers"
metrics:
- id: prometheus_http_response_size_bytes_bucket
query_range: "sum(increase(prometheus_http_response_size_bytes_bucket[1d])) by (handler)"
unit: 'Bytes'
```
Note the following properties:
| Property | Type | Required | Description |
| ------ | ------ | ------ | ------ |
| `type` | string | yes | Type of panel to be rendered. For bar chart types, set to `bar` |
| `query_range` | yes | yes | For bar chart, you must use a [range query]
![bar chart panel type](img/prometheus_dashboard_bar_chart_panel_type_v12.10.png)
##### Column chart
To add a column panel type to a dashboard, look at the following sample dashboard file:
......
......@@ -9723,6 +9723,9 @@ msgstr ""
msgid "GitLabPages|Save"
msgstr ""
msgid "GitLabPages|Something went wrong while obtaining Let's Encrypt certificate for %{domain}. To retry visit your %{link_start}domain details%{link_end}."
msgstr ""
msgid "GitLabPages|Support for domains and certificates is disabled. Ask your system's administrator to enable it."
msgstr ""
......@@ -24067,7 +24070,7 @@ msgstr ""
msgid "is not a valid X509 certificate."
msgstr ""
msgid "is not allowed for sign-up"
msgid "is not allowed. Try again with a different email address, or contact your GitLab admin."
msgstr ""
msgid "is not an email you own"
......
# frozen_string_literal: true
require_relative '../../migration_helpers'
module RuboCop
module Cop
module Migration
# Cop that prevents adding columns to wide tables.
class AddColumnsToWideTables < RuboCop::Cop::Cop
include MigrationHelpers
MSG = '`%s` is a wide table with several columns, addig more should be avoided unless absolutely necessary.' \
' Consider storing the column in a different table or creating a new one.'.freeze
BLACKLISTED_METHODS = %i[
add_column
add_column_with_default
add_reference
add_timestamps_with_timezone
].freeze
def on_send(node)
return unless in_migration?(node)
method_name = node.children[1]
table_name = node.children[2]
return unless offense?(method_name, table_name)
add_offense(node, location: :selector, message: format(MSG, table_name.value))
end
private
def offense?(method_name, table_name)
wide_table?(table_name) &&
BLACKLISTED_METHODS.include?(method_name)
end
def wide_table?(table_name)
table_name && table_name.type == :sym &&
WIDE_TABLES.include?(table_name.value)
end
end
end
end
end
......@@ -6,7 +6,7 @@ module RuboCop
plan_limits
].freeze
# Blacklisted table due to:
# Blacklisted tables due to:
# - size in GB (>= 10 GB on GitLab.com as of 02/2020)
# - number of records
BLACKLISTED_TABLES = %i[
......@@ -44,6 +44,15 @@ module RuboCop
web_hook_logs
].freeze
# Blacklisted tables due to:
# - number of columns (> 50 on GitLab.com as of 03/2020)
# - number of records
WIDE_TABLES = %i[
users
projects
ci_builds
].freeze
# Returns true if the given node originated from the db/migrate directory.
def in_migration?(node)
dirname(node).end_with?('db/migrate', 'db/geo/migrate') || in_post_deployment_migration?(node)
......
......@@ -72,6 +72,21 @@ describe 'Projects > Settings > Repository settings' do
expect(project.remote_mirrors.first.only_protected_branches).to eq(true)
end
it 'creates a push mirror that keeps divergent refs', :js do
select_direction
fill_in 'url', with: 'ssh://user@localhost/project.git'
fill_in 'Password', with: 'password'
check 'Keep divergent refs'
Sidekiq::Testing.fake! do
click_button 'Mirror repository'
end
expect(page).to have_content('Mirroring settings were successfully updated')
expect(project.reload.remote_mirrors.first.keep_divergent_refs).to eq(true)
end
it 'generates an SSH public key on submission', :js do
fill_in 'url', with: 'ssh://user@localhost/project.git'
select 'SSH public key', from: 'Authentication method'
......@@ -110,6 +125,20 @@ describe 'Projects > Settings > Repository settings' do
end
end
# Removal: https://gitlab.com/gitlab-org/gitlab/-/issues/208828
context 'with the `keep_divergent_refs` feature flag disabled' do
before do
stub_feature_flags(keep_divergent_refs: { enabled: false, thing: project })
end
it 'hides the "Keep divergent refs" option' do
visit project_settings_repository_path(project)
expect(page).not_to have_selector('#keep_divergent_refs')
expect(page).not_to have_text('Keep divergent refs')
end
end
context 'repository cleanup settings' do
let(:object_map_file) { Rails.root.join('spec', 'fixtures', 'bfg_object_map.txt') }
......
......@@ -529,7 +529,7 @@ describe User, :do_not_mock_admin_mode do
user = build(:user, email: 'info@gitlab.com')
expect(user).not_to be_valid
expect(user.errors.messages[:email].first).to eq(_('is not allowed for sign-up'))
expect(user.errors.messages[:email].first).to eq(_('is not allowed. Try again with a different email address, or contact your GitLab admin.'))
end
it 'does accept a valid email address' do
......
# frozen_string_literal: true
require 'spec_helper'
describe PagesDomainPresenter do
using RSpec::Parameterized::TableSyntax
include LetsEncryptHelpers
let(:presenter) { Gitlab::View::Presenter::Factory.new(domain).fabricate! }
describe 'needs_validation?' do
where(:pages_verification_enabled, :traits, :expected) do
false | :unverified | false
false | [] | false
true | :unverified | true
true | [] | false
end
with_them do
before do
stub_application_setting(pages_domain_verification_enabled: pages_verification_enabled)
end
let(:domain) { create(:pages_domain, *traits) }
it { expect(presenter.needs_verification?).to eq(expected) }
end
end
describe 'show_auto_ssl_failed_warning?' do
subject { presenter.show_auto_ssl_failed_warning? }
let(:domain) { create(:pages_domain) }
before do
stub_lets_encrypt_settings
end
it { is_expected.to eq(false) }
context "when we failed to obtain Let's Encrypt's certificate" do
before do
domain.update!(auto_ssl_failed: true)
end
it { is_expected.to eq(true) }
context 'when lets_encrypt_error feature flag is disabled' do
before do
stub_feature_flags(pages_letsencrypt_errors: false)
end
it { is_expected.to eq(false) }
end
context "when Let's Encrypt integration is disabled" do
before do
allow(::Gitlab::LetsEncrypt).to receive(:enabled?).and_return false
end
it { is_expected.to eq(false) }
end
context "when domain is unverified" do
before do
domain.update!(verified_at: nil)
end
it { is_expected.to eq(false) }
end
end
end
end
......@@ -313,3 +313,34 @@ describe HealthCheckController, 'routing' do
expect(get('/health_check/email')).to route_to('health_check#index', checks: 'email')
end
end
describe InvitesController, 'routing' do
let_it_be(:member) { create(:project_member, :invited) }
it 'to #show' do
expect(get("/-/invites/#{member.invite_token}")).to route_to('invites#show', id: member.invite_token)
end
it 'to legacy route' do
expect(get("/invites/#{member.invite_token}")).to route_to('invites#show', id: member.invite_token)
end
end
describe AbuseReportsController, 'routing' do
let_it_be(:user) { create(:user) }
it 'to #new' do
expect(get("/-/abuse_reports/new?user_id=#{user.id}")).to route_to('abuse_reports#new', user_id: user.id.to_s)
end
it 'to legacy route' do
expect(get("/abuse_reports/new?user_id=#{user.id}")).to route_to('abuse_reports#new', user_id: user.id.to_s)
end
end
describe SentNotificationsController, 'routing' do
it 'to #unsubscribe' do
expect(get("/-/sent_notifications/4bee17d4a63ed60cf5db53417e9aeb4c/unsubscribe"))
.to route_to('sent_notifications#unsubscribe', id: '4bee17d4a63ed60cf5db53417e9aeb4c')
end
end
# frozen_string_literal: true
require 'spec_helper'
require 'rubocop'
require_relative '../../../../rubocop/cop/migration/add_columns_to_wide_tables'
describe RuboCop::Cop::Migration::AddColumnsToWideTables do
include CopHelper
let(:cop) { described_class.new }
context 'outside of a migration' do
it 'does not register any offenses' do
expect_no_offenses(<<~RUBY)
def up
add_column(:users, :another_column, :string)
end
RUBY
end
end
context 'in a migration' do
before do
allow(cop).to receive(:in_migration?).and_return(true)
end
context 'with wide tables' do
it 'registers an offense when adding a column to a wide table' do
offense = '`projects` is a wide table with several columns, addig more should be avoided unless absolutely necessary. Consider storing the column in a different table or creating a new one.'
expect_offense(<<~RUBY)
def up
add_column(:projects, :another_column, :integer)
^^^^^^^^^^ #{offense}
end
RUBY
end
it 'registers an offense when adding a column with default to a wide table' do
offense = '`users` is a wide table with several columns, addig more should be avoided unless absolutely necessary. Consider storing the column in a different table or creating a new one.'
expect_offense(<<~RUBY)
def up
add_column_with_default(:users, :another_column, :boolean, default: false)
^^^^^^^^^^^^^^^^^^^^^^^ #{offense}
end
RUBY
end
it 'registers an offense when adding a reference' do
offense = '`ci_builds` is a wide table with several columns, addig more should be avoided unless absolutely necessary. Consider storing the column in a different table or creating a new one.'
expect_offense(<<~RUBY)
def up
add_reference(:ci_builds, :issue, :boolean, index: true)
^^^^^^^^^^^^^ #{offense}
end
RUBY
end
it 'registers an offense when adding timestamps' do
offense = '`projects` is a wide table with several columns, addig more should be avoided unless absolutely necessary. Consider storing the column in a different table or creating a new one.'
expect_offense(<<~RUBY)
def up
add_timestamps_with_timezone(:projects, null: false)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^ #{offense}
end
RUBY
end
it 'register no offense when using other method' do
expect_no_offenses(<<~RUBY)
def up
add_concurrent_index(:projects, :new_index)
end
RUBY
end
end
context 'with a regular table' do
it 'registers no offense for notes' do
expect_no_offenses(<<~RUBY)
def up
add_column(:notes, :another_column, :boolean)
end
RUBY
end
end
end
end
......@@ -29,7 +29,7 @@ describe Metrics::Dashboard::CloneDashboardService, :use_clean_rails_memory_stor
end
context 'user does not have push right to repository' do
it_behaves_like 'misconfigured dashboard service response', :forbidden, %q(You are not allowed to push into this branch. Create another branch or open a merge request.)
it_behaves_like 'misconfigured dashboard service response with stepable', :forbidden, 'You are not allowed to push into this branch. Create another branch or open a merge request.'
end
context 'with rights to push to the repository' do
......@@ -40,19 +40,19 @@ describe Metrics::Dashboard::CloneDashboardService, :use_clean_rails_memory_stor
context 'wrong target file extension' do
let(:file_name) { 'custom_dashboard.txt' }
it_behaves_like 'misconfigured dashboard service response', :bad_request, 'The file name should have a .yml extension'
it_behaves_like 'misconfigured dashboard service response with stepable', :bad_request, 'The file name should have a .yml extension'
end
context 'wrong source dashboard file' do
let(:dashboard) { 'config/prometheus/common_metrics_123.yml' }
it_behaves_like 'misconfigured dashboard service response', :not_found, 'Not found.'
it_behaves_like 'misconfigured dashboard service response with stepable', :not_found, 'Not found.'
end
context 'path traversal attack attempt' do
let(:dashboard) { 'config/prometheus/../database.yml' }
it_behaves_like 'misconfigured dashboard service response', :not_found, 'Not found.'
it_behaves_like 'misconfigured dashboard service response with stepable', :not_found, 'Not found.'
end
context 'path traversal attack attempt on target file' do
......@@ -92,7 +92,7 @@ describe Metrics::Dashboard::CloneDashboardService, :use_clean_rails_memory_stor
project.repository.add_branch(user, branch, 'master')
end
it_behaves_like 'misconfigured dashboard service response', :bad_request, "There was an error creating the dashboard, branch named: existing_branch already exists."
it_behaves_like 'misconfigured dashboard service response with stepable', :bad_request, 'There was an error creating the dashboard, branch named: existing_branch already exists.'
# temporary not available function for first iteration
# follow up issue https://gitlab.com/gitlab-org/gitlab/issues/196237 which
......@@ -111,7 +111,7 @@ describe Metrics::Dashboard::CloneDashboardService, :use_clean_rails_memory_stor
context 'blank branch name' do
let(:branch) { '' }
it_behaves_like 'misconfigured dashboard service response', :bad_request, 'There was an error creating the dashboard, branch name is invalid.'
it_behaves_like 'misconfigured dashboard service response with stepable', :bad_request, 'There was an error creating the dashboard, branch name is invalid.'
end
context 'dashboard file already exists' do
......@@ -129,7 +129,7 @@ describe Metrics::Dashboard::CloneDashboardService, :use_clean_rails_memory_stor
).execute
end
it_behaves_like 'misconfigured dashboard service response', :bad_request, "A file with 'custom_dashboard.yml' already exists in custom_dashboard branch"
it_behaves_like 'misconfigured dashboard service response with stepable', :bad_request, "A file with 'custom_dashboard.yml' already exists in custom_dashboard branch"
end
it 'extends dashboard template path to absolute url' do
......
......@@ -107,3 +107,14 @@ RSpec.shared_examples 'valid dashboard update process' do
service_call
end
end
RSpec.shared_examples 'misconfigured dashboard service response with stepable' do |status_code, message = nil|
it 'returns an appropriate message and status code', :aggregate_failures do
result = service_call
expect(result.keys).to contain_exactly(:message, :http_status, :status, :last_step)
expect(result[:status]).to eq(:error)
expect(result[:http_status]).to eq(status_code)
expect(result[:message]).to eq(message) if message
end
end
# frozen_string_literal: true
require 'spec_helper'
describe 'projects/pages/show' do
include LetsEncryptHelpers
let(:project) { create(:project, :repository) }
let(:user) { create(:user) }
let(:domain) { create(:pages_domain, project: project) }
before do
allow(project).to receive(:pages_deployed?).and_return(true)
stub_pages_setting(external_https: true)
stub_lets_encrypt_settings
project.add_maintainer(user)
assign(:project, project)
allow(view).to receive(:current_user).and_return(user)
assign(:domains, [domain])
end
describe 'validation warning' do
let(:warning_message) do
"#{domain.domain} is not verified. To learn how to verify ownership, "\
"visit your domain details."
end
it "doesn't show auto ssl error warning" do
render
expect(rendered).not_to have_content(warning_message)
end
context "when domain is not verified" do
before do
domain.update!(verified_at: nil)
end
it 'shows auto ssl error warning' do
render
expect(rendered).to have_content(warning_message)
end
end
end
describe "warning about failed Let's Encrypt" do
let(:error_message) do
"Something went wrong while obtaining Let's Encrypt certificate for #{domain.domain}. "\
"To retry visit your domain details."
end
it "doesn't show auto ssl error warning" do
render
expect(rendered).not_to have_content(error_message)
end
context "when we failed to obtain Let's Encrypt's certificate" do
before do
domain.update!(auto_ssl_failed: true)
end
it 'shows auto ssl error warning' do
render
expect(rendered).to have_content(error_message)
end
end
end
end
......@@ -1036,10 +1036,10 @@
"@sentry/types" "5.10.0"
tslib "^1.9.3"
"@sourcegraph/code-host-integration@0.0.33":
version "0.0.33"
resolved "https://registry.yarnpkg.com/@sourcegraph/code-host-integration/-/code-host-integration-0.0.33.tgz#133f11535be0cc937fbadc6a6951ee9fd21fbe1d"
integrity sha512-J49ljHsSIe8KD5+ke9C3ugGO9T6R2M96miiGEZFRHJQ7FXyKi/zP5N4BFVweHjE+b15BLSicoaAnnyg4nhIBIw==
"@sourcegraph/code-host-integration@0.0.34":
version "0.0.34"
resolved "https://registry.yarnpkg.com/@sourcegraph/code-host-integration/-/code-host-integration-0.0.34.tgz#c8f94854d64fe035926bbda7bed3a538a7259d03"
integrity sha512-TAa5kU/zPb9PfB4HIhaEDhKKdW5Fx9YVx9WWMOwz9elD0y9FZoAXDO1o4Pz1cm1IP/VZwd8csypAWgfxsAmfzw==
"@types/anymatch@*":
version "1.3.0"
......
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