Commit edeace3f authored by Clement Ho's avatar Clement Ho

Merge branch 'ce-to-ee-9-3-rc6' into '9-3-stable-ee-rc6'

CE => EE 9.3 RC6

See merge request !2195
parents 00899d8b 121e3233
Pipeline #9216556 passed with stages
in 90 minutes and 12 seconds
......@@ -399,7 +399,7 @@ Style/ParenthesesAroundCondition:
# Configuration parameters: EnforcedStyle, SupportedStyles.
# SupportedStyles: short, verbose
Style/PreferredHashMethods:
Enabled: true
Enabled: false
# Checks for an obsolete RuntimeException argument in raise/fail.
Style/RedundantException:
......
......@@ -187,7 +187,7 @@ const normalizeNewlines = function(str) {
if ($textarea.val() !== '') {
return;
}
myLastNote = $(`li.note[data-author-id='${gon.current_user_id}'][data-editable]:last`, $textarea.closest('.note, #notes'));
myLastNote = $(`li.note[data-author-id='${gon.current_user_id}'][data-editable]:last`, $textarea.closest('.note, .notes_holder, #notes'));
if (myLastNote.length) {
myLastNoteEditBtn = myLastNote.find('.js-note-edit');
return myLastNoteEditBtn.trigger('click', [true, myLastNote]);
......
......@@ -236,9 +236,6 @@
width: 35px;
background-color: $white-light;
border: none;
position: static;
right: 0;
height: 100%;
outline: none;
z-index: 1;
......
......@@ -727,33 +727,3 @@
}
}
}
.confidential-issue-warning {
background-color: $gl-gray;
border-radius: 3px;
padding: $gl-btn-padding $gl-padding;
margin-top: $gl-padding-top;
font-size: 14px;
color: $white-light;
.fa {
margin-right: 8px;
}
a {
color: $white-light;
text-decoration: underline;
}
&.affix {
position: static;
width: initial;
@media (min-width: $screen-sm-min) {
position: sticky;
position: -webkit-sticky;
top: 60px;
z-index: 200;
}
}
}
......@@ -103,6 +103,42 @@
}
}
.confidential-issue-warning {
background-color: $gray-normal;
border-radius: 3px;
padding: 3px 12px;
margin: auto;
margin-top: 0;
text-align: center;
font-size: 12px;
align-items: center;
@media (max-width: $screen-md-max) {
// On smaller devices the warning becomes the fourth item in the list,
// rather than centering, and grows to span the full width of the
// comment area.
order: 4;
margin: 6px auto;
width: 100%;
}
.fa {
margin-right: 8px;
}
}
.right-sidebar-expanded {
.confidential-issue-warning {
// When the sidebar is open the warning becomes the fourth item in the list,
// rather than centering, and grows to span the full width of the
// comment area.
order: 4;
margin: 6px auto;
width: 100%;
}
}
.discussion-form {
padding: $gl-padding-top $gl-padding $gl-padding;
background-color: $white-light;
......
......@@ -1204,4 +1204,8 @@
.text-center {
padding-top: 12px;
}
.header-action-button .btn {
margin-left: 5px;
}
}
......@@ -33,7 +33,8 @@ class EventsFinder
private
def by_current_user_access(events)
events.merge(ProjectsFinder.new(current_user: current_user).execute).references(:project)
events.merge(ProjectsFinder.new(current_user: current_user).execute).
joins(:project)
end
def by_action(events)
......
......@@ -29,35 +29,69 @@ class GroupProjectsFinder < ProjectsFinder
private
def init_collection
only_owned = options.fetch(:only_owned, false)
only_shared = options.fetch(:only_shared, false)
projects = if current_user
collection_with_user
else
collection_without_user
end
projects = []
union(projects)
end
if current_user
if group.users.include?(current_user)
projects << group.projects unless only_shared
projects << group.shared_projects unless only_owned
def collection_with_user
if group.users.include?(current_user)
if only_shared?
[shared_projects]
elsif only_owned?
[owned_projects]
else
unless only_shared
projects << group.projects.visible_to_user(current_user)
projects << group.projects.public_to_user(current_user)
end
unless only_owned
projects << group.shared_projects.visible_to_user(current_user)
projects << group.shared_projects.public_to_user(current_user)
end
[shared_projects, owned_projects]
end
else
projects << group.projects.public_only unless only_shared
projects << group.shared_projects.public_only unless only_owned
if only_shared?
[shared_projects.public_or_visible_to_user(current_user)]
elsif only_owned?
[owned_projects.public_or_visible_to_user(current_user)]
else
[
owned_projects.public_or_visible_to_user(current_user),
shared_projects.public_or_visible_to_user(current_user)
]
end
end
end
projects
def collection_without_user
if only_shared?
[shared_projects.public_only]
elsif only_owned?
[owned_projects.public_only]
else
[shared_projects.public_only, owned_projects.public_only]
end
end
def union(items)
find_union(items, Project)
if items.one?
items.first
else
find_union(items, Project)
end
end
def only_owned?
options.fetch(:only_owned, false)
end
def only_shared?
options.fetch(:only_shared, false)
end
def owned_projects
group.projects
end
def shared_projects
group.shared_projects
end
end
......@@ -28,34 +28,56 @@ class ProjectsFinder < UnionFinder
end
def execute
items = init_collection
items = items.map do |item|
item = by_ids(item)
item = by_personal(item)
item = by_starred(item)
item = by_trending(item)
item = by_visibilty_level(item)
item = by_tags(item)
item = by_search(item)
by_archived(item)
end
items = union(items)
sort(items)
collection = init_collection
collection = by_ids(collection)
collection = by_personal(collection)
collection = by_starred(collection)
collection = by_trending(collection)
collection = by_visibilty_level(collection)
collection = by_tags(collection)
collection = by_search(collection)
collection = by_archived(collection)
sort(collection)
end
private
def init_collection
projects = []
if current_user
collection_with_user
else
collection_without_user
end
end
if params[:owned].present?
projects << current_user.owned_projects if current_user
def collection_with_user
if owned_projects?
current_user.owned_projects
else
projects << current_user.authorized_projects if current_user
projects << Project.unscoped.public_to_user(current_user) unless params[:non_public].present?
if private_only?
current_user.authorized_projects
else
Project.public_or_visible_to_user(current_user)
end
end
end
# Builds a collection for an anonymous user.
def collection_without_user
if private_only? || owned_projects?
Project.none
else
Project.public_to_user
end
end
def owned_projects?
params[:owned].present?
end
projects
def private_only?
params[:non_public].present?
end
def by_ids(items)
......
......@@ -56,6 +56,27 @@ module EE
admin? || auditor?
end
def access_level
if auditor?
:auditor
else
super
end
end
def access_level=(new_level)
new_level = new_level.to_s
return unless %w(admin auditor regular).include?(new_level)
self.admin = (new_level == 'admin')
self.auditor = (new_level == 'auditor')
end
# Does the user have access to all private groups & projects?
def has_full_private_access?
admin_or_auditor?
end
def remember_me!
return if ::Gitlab::Geo.secondary?
super
......
......@@ -302,20 +302,49 @@ class Project < ActiveRecord::Base
enum auto_cancel_pending_pipelines: { disabled: 0, enabled: 1 }
# Returns a collection of projects that is either public or visible to the
# logged in user.
def self.public_or_visible_to_user(user = nil)
if user
authorized = user.
project_authorizations.
select(1).
where('project_authorizations.project_id = projects.id')
levels = Gitlab::VisibilityLevel.levels_for_user(user)
where('EXISTS (?) OR projects.visibility_level IN (?)', authorized, levels)
else
public_to_user
end
end
# project features may be "disabled", "internal" or "enabled". If "internal",
# they are only available to team members. This scope returns projects where
# the feature is either enabled, or internal with permission for the user.
#
# This method uses an optimised version of `with_feature_access_level` for
# logged in users to more efficiently get private projects with the given
# feature.
def self.with_feature_available_for_user(feature, user)
return with_feature_enabled(feature) if user.try(:admin?)
visible = [nil, ProjectFeature::ENABLED]
unconditional = with_feature_access_level(feature, [nil, ProjectFeature::ENABLED])
return unconditional if user.nil?
if user&.admin?
with_feature_enabled(feature)
elsif user
column = ProjectFeature.quoted_access_level_column(feature)
conditional = with_feature_access_level(feature, ProjectFeature::PRIVATE)
authorized = user.authorized_projects.merge(conditional.reorder(nil))
authorized = user.project_authorizations.select(1).
where('project_authorizations.project_id = projects.id')
union = Gitlab::SQL::Union.new([unconditional.select(:id), authorized.select(:id)])
where(arel_table[:id].in(Arel::Nodes::SqlLiteral.new(union.to_sql)))
with_project_feature.
where("#{column} IN (?) OR (#{column} = ? AND EXISTS (?))",
visible,
ProjectFeature::PRIVATE,
authorized)
else
with_feature_access_level(feature, visible)
end
end
scope :active, -> { joins(:issues, :notes, :merge_requests).order('issues.created_at, notes.created_at, merge_requests.created_at DESC') }
......
......@@ -27,6 +27,13 @@ class ProjectFeature < ActiveRecord::Base
"#{feature}_access_level".to_sym
end
def quoted_access_level_column(feature)
attribute = connection.quote_column_name(access_level_attribute(feature))
table = connection.quote_table_name(table_name)
"#{table}.#{attribute}"
end
end
# Default scopes force us to unscope here since a service may need to check
......
......@@ -70,7 +70,7 @@ module ChatMessage
end
def branch_link
"`[#{ref}](#{branch_url})`"
"[#{ref}](#{branch_url})"
end
def project_link
......
......@@ -61,7 +61,7 @@ module ChatMessage
end
def removed_branch_message
"#{user_name} removed #{ref_type} `#{ref}` from #{project_link}"
"#{user_name} removed #{ref_type} #{ref} from #{project_link}"
end
def push_message
......@@ -102,7 +102,7 @@ module ChatMessage
end
def branch_link
"`[#{ref}](#{branch_url})`"
"[#{ref}](#{branch_url})"
end
def project_link
......
......@@ -1003,8 +1003,6 @@ class User < ActiveRecord::Base
def access_level
if admin?
:admin
elsif auditor?
:auditor
else
:regular
end
......@@ -1012,10 +1010,14 @@ class User < ActiveRecord::Base
def access_level=(new_level)
new_level = new_level.to_s
return unless %w(admin auditor regular).include?(new_level)
return unless %w(admin regular).include?(new_level)
self.admin = (new_level == 'admin')
self.auditor = (new_level == 'auditor')
end
# Does the user have access to all private groups & projects?
def has_full_private_access?
admin?
end
def update_two_factor_requirement
......
......@@ -339,6 +339,10 @@
= f.label :prometheus_metrics_enabled do
= f.check_box :prometheus_metrics_enabled
Enable Prometheus Metrics
- unless Gitlab::Metrics.metrics_folder_present?
.help-block
%strong.cred WARNING:
Environment variable `prometheus_multiproc_dir` does not exist or is not pointing to a valid directory.
%fieldset
%legend Background Jobs
......
......@@ -9,6 +9,12 @@
%li
%a.js-md-preview-button{ href: "#md-preview-holder", tabindex: -1 }
Preview
- if defined?(@issue) && @issue.confidential?
%li.confidential-issue-warning
= icon('warning')
%span This is a confidential issue. Your comment will not be visible to the public.
%li.pull-right
.toolbar-group
= markdown_toolbar_button({ icon: "bold fw", data: { "md-tag" => "**" }, title: "Add bold text" })
......
......@@ -9,7 +9,7 @@
.dropzone
.dropzone-previews.blob-upload-dropzone-previews
%p.dz-message.light
- upload_link = link_to n_('UploadLink|click to upload'), '#', class: "markdown-selector"
- upload_link = link_to s_('UploadLink|click to upload'), '#', class: "markdown-selector"
- dropzone_text = _('Attach a file by drag &amp; drop or %{upload_link}') % { upload_link: upload_link }
#{ dropzone_text.html_safe }
......
......@@ -22,7 +22,7 @@
= label_tag 'start_branch', branch_label, class: 'control-label'
.col-sm-10
= hidden_field_tag :start_branch, @project.default_branch, id: 'start_branch'
= dropdown_tag(@project.default_branch, options: { title: n_("BranchSwitcherTitle|Switch branch"), filter: true, placeholder: n_("BranchSwitcherPlaceholder|Search branches"), toggle_class: 'js-project-refs-dropdown dynamic', dropdown_class: 'dropdown-menu-selectable', data: { field_name: "start_branch", selected: @project.default_branch, start_branch: @project.default_branch, refs_url: namespace_project_branches_path(@project.namespace, @project), submit_form_on_click: false } })
= dropdown_tag(@project.default_branch, options: { title: s_("BranchSwitcherTitle|Switch branch"), filter: true, placeholder: s_("BranchSwitcherPlaceholder|Search branches"), toggle_class: 'js-project-refs-dropdown dynamic', dropdown_class: 'dropdown-menu-selectable', data: { field_name: "start_branch", selected: @project.default_branch, start_branch: @project.default_branch, refs_url: namespace_project_branches_path(@project.namespace, @project), submit_form_on_click: false } })
- if can?(current_user, :push_code, @project)
= render 'shared/new_merge_request_checkbox'
......
......@@ -5,13 +5,6 @@
- can_update_issue = can?(current_user, :update_issue, @issue)
- can_report_spam = @issue.submittable_as_spam_by?(current_user)
- if defined?(@issue) && @issue.confidential?
.confidential-issue-warning{ data: { spy: 'affix' } }
%span.confidential-issue-text
#{confidential_icon(@issue)} This issue is confidential.
%a{ href: help_page_path('user/project/issues/confidential_issues'), target: '_blank' }
What are confidential issues?
.clearfix.detail-page-header
.issuable-header
.issuable-status-box.status-box.status-box-closed{ class: issue_button_visibility(@issue, false) }
......@@ -26,6 +19,7 @@
= icon('angle-double-left')
.issuable-meta
= confidential_icon(@issue)
= issuable_meta(@issue, @project, "Issue")
.issuable-actions
......
.dropdown.more-actions
= button_tag title: 'More actions', class: 'note-action-button more-actions-toggle has-tooltip btn btn-transparent', data: { toggle: 'dropdown', container: 'body' } do
= icon('ellipsis-v', class: 'icon')
%ul.dropdown-menu.more-actions-dropdown.dropdown-open-left
%li
= button_tag 'Edit comment', class: 'js-note-edit btn btn-transparent'
%li.divider
%li
= link_to new_abuse_report_path(user_id: note.author.id, ref_url: noteable_note_url(note)) do
Report as abuse
- if note_editable
%li
= link_to note_url(note), method: :delete, data: { confirm: 'Are you sure you want to delete this comment?' }, remote: true, class: 'js-note-delete' do
%span.text-danger Delete comment
- is_current_user = current_user == note.author
- if note_editable || !is_current_user
.dropdown.more-actions
= button_tag title: 'More actions', class: 'note-action-button more-actions-toggle has-tooltip btn btn-transparent', data: { toggle: 'dropdown', container: 'body' } do
= icon('ellipsis-v', class: 'icon')
%ul.dropdown-menu.more-actions-dropdown.dropdown-open-left
- if note_editable
%li
= button_tag 'Edit comment', class: 'js-note-edit btn btn-transparent'
%li.divider
- unless is_current_user
%li
= link_to new_abuse_report_path(user_id: note.author.id, ref_url: noteable_note_url(note)) do
Report as abuse
- if note_editable
%li
= link_to note_url(note), method: :delete, data: { confirm: 'Are you sure you want to delete this comment?' }, remote: true, class: 'js-note-delete' do
%span.text-danger Delete comment
......@@ -15,12 +15,12 @@
.form-group
.col-md-9
= f.label :cron_timezone, _('Cron Timezone'), class: 'label-light'
= dropdown_tag(_("Select a timezone"), options: { toggle_class: 'btn js-timezone-dropdown', title: _("Select a timezone"), filter: true, placeholder: _("OfSearchInADropdown|Filter"), data: { data: timezone_data } } )
= dropdown_tag(_("Select a timezone"), options: { toggle_class: 'btn js-timezone-dropdown', title: _("Select a timezone"), filter: true, placeholder: s_("OfSearchInADropdown|Filter"), data: { data: timezone_data } } )
= f.text_field :cron_timezone, value: @schedule.cron_timezone, id: 'schedule_cron_timezone', class: 'hidden', name: 'schedule[cron_timezone]', required: true
.form-group
.col-md-9
= f.label :ref, _('Target Branch'), class: 'label-light'
= dropdown_tag(_("Select target branch"), options: { toggle_class: 'btn js-target-branch-dropdown git-revision-dropdown-toggle', dropdown_class: 'git-revision-dropdown', title: _("Select target branch"), filter: true, placeholder: _("OfSearchInADropdown|Filter"), data: { data: @project.repository.branch_names, default_branch: @project.default_branch } } )
= dropdown_tag(_("Select target branch"), options: { toggle_class: 'btn js-target-branch-dropdown git-revision-dropdown-toggle', dropdown_class: 'git-revision-dropdown', title: _("Select target branch"), filter: true, placeholder: s_("OfSearchInADropdown|Filter"), data: { data: @project.repository.branch_names, default_branch: @project.default_branch } } )
= f.text_field :ref, value: @schedule.ref, id: 'schedule_ref', class: 'hidden', name: 'schedule[ref]', required: true
.form-group
.col-md-9
......
---
title: Reinstate is_admin flag in users api when authenticated user is an admin
merge_request: 12211
author: rickettm
---
title: Fix for cut & pasted images not working
merge_request:
author:
---
title: Make confidential issues more obviously confidential
merge_request:
author:
---
title: Refactor ProjectsFinder#init_collection to produce more efficient queries for
retrieving projects
merge_request:
author:
......@@ -5,6 +5,11 @@ ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__)
require 'bundler/setup' if File.exist?(ENV['BUNDLE_GEMFILE'])
# set default directory for multiproces metrics gathering
if ENV['RAILS_ENV'] == 'development' || ENV['RAILS_ENV'] == 'test'
ENV['prometheus_multiproc_dir'] ||= 'tmp/prometheus_multiproc_dir'
end
# Default Bootsnap configuration from https://github.com/Shopify/bootsnap#usage
require 'bootsnap'
Bootsnap.setup(
......
# GitLab Prometheus metrics
>**Note:**
Available since [Omnibus GitLab 9.3][29118]. Currently experimental. For installations from source
you'll have to configure it yourself.
GitLab monitors its own internal service metrics, and makes them available at the `/-/metrics` endpoint. Unlike other [Prometheus] exporters, this endpoint requires authentication as it is available on the same URL and port as user traffic.
To enable the GitLab Prometheus metrics:
1. Log into GitLab as an administrator, and go to the Admin area.
1. Click on the gear, then click on Settings.
1. Find the `Metrics - Prometheus` section, and click `Enable Prometheus Metrics`
1. [Restart GitLab][restart] for the changes to take effect
## Collecting the metrics
Since the metrics endpoint is available on the same host and port as other traffic, it requires authentication. The token and URL to access is displayed on the [Health Check][health-check] page.
Currently the embedded Prometheus server is not automatically configured to collect metrics from this endpoint. We recommend setting up another Prometheus server, because the embedded server configuration is overwritten one every reconfigure of GitLab. In the future this will not be required.
## Metrics available
In this experimental phase, only a few metrics are available:
| Metric | Type | Description |
| ------ | ---- | ----------- |
| db_ping_timeout | Gauge | Whether or not the last database ping timed out |
| db_ping_success | Gauge | Whether or not the last database ping succeeded |
| db_ping_latency | Gauge | Round trip time of the database ping |
| redis_ping_timeout | Gauge | Whether or not the last redis ping timed out |
| redis_ping_success | Gauge | Whether or not the last redis ping succeeded |
| redis_ping_latency | Gauge | Round trip time of the redis ping |
| filesystem_access_latency | gauge | Latency in accessing a specific filesystem |
| filesystem_accessible | gauge | Whether or not a specific filesystem is accessible |
| filesystem_write_latency | gauge | Write latency of a specific filesystem |
| filesystem_writable | gauge | Whether or not the filesystem is writable |
| filesystem_read_latency | gauge | Read latency of a specific filesystem |
| filesystem_readable | gauge | Whether or not the filesystem is readable |
| user_sessions_logins | Counter | Counter of how many users have logged in |
[← Back to the main Prometheus page](index.md)
[29118]: https://gitlab.com/gitlab-org/gitlab-ce/issues/29118
[Prometheus]: https://prometheus.io
[restart]: ../../restart_gitlab.md#omnibus-gitlab-restart
[health-check]: ../../../user/admin_area/monitoring/health_check.md
......@@ -4,7 +4,7 @@
Available since [Omnibus GitLab 8.17][1132]. For installations from source
you'll have to install and configure it yourself.
The [GitLab monitor exporter] allows you to measure various GitLab metrics.
The [GitLab monitor exporter] allows you to measure various GitLab metrics, pulled from Redis and the database.
To enable the GitLab monitor exporter:
......
......@@ -110,6 +110,14 @@ To disable the monitoring of Kubernetes:
1. Save the file and [reconfigure GitLab][reconfigure] for the changes to
take effect
## GitLab Prometheus metrics
> Introduced as an experimental feature in GitLab 9.3.
GitLab monitors its own internal service metrics, and makes them available at the `/-/metrics` endpoint. Unlike other exporters, this endpoint requires authentication as it is available on the same URL and port as user traffic.
[➔ Read more about the GitLab Metrics.](gitlab_metrics.md)
## Prometheus exporters
There are a number of libraries and servers which help in exporting existing
......@@ -143,7 +151,7 @@ The Postgres exporter allows you to measure various PostgreSQL metrics.
### GitLab monitor exporter
The GitLab monitor exporter allows you to measure various GitLab metrics.
The GitLab monitor exporter allows you to measure various GitLab metrics, pulled from Redis and the database.
[➔ Read more about the GitLab monitor exporter.](gitlab_monitor_exporter.md)
......
......@@ -64,6 +64,7 @@ GET /users
"avatar_url": "http://localhost:3000/uploads/user/avatar/1/index.jpg",
"web_url": "http://localhost:3000/john_smith",
"created_at": "2012-05-23T08:00:58Z",