Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2021-04-13 00:11:29 +00:00
parent beab869416
commit 248057a54a
33 changed files with 540 additions and 215 deletions

View File

@ -1,3 +1,4 @@
import TagSortDropdown from '~/tags';
import { initRemoveTag } from '../remove_tag';
initRemoveTag({
@ -5,3 +6,4 @@ initRemoveTag({
document.querySelector(`[data-path="${path}"]`).closest('.js-tag-list').remove();
},
});
TagSortDropdown();

View File

@ -0,0 +1,77 @@
<script>
import { GlDropdown, GlDropdownItem, GlSearchBoxByClick } from '@gitlab/ui';
import { mergeUrlParams, visitUrl, getParameterValues } from '~/lib/utils/url_utility';
import { s__ } from '~/locale';
export default {
i18n: {
searchPlaceholder: s__('TagsPage|Filter by tag name'),
},
components: {
GlDropdown,
GlDropdownItem,
GlSearchBoxByClick,
},
inject: ['sortOptions', 'filterTagsPath'],
data() {
return {
selectedKey: 'updated_desc',
searchTerm: '',
};
},
computed: {
selectedSortMethod() {
return this.sortOptions[this.selectedKey];
},
},
created() {
const sortValue = getParameterValues('sort');
const searchValue = getParameterValues('search');
if (sortValue.length > 0) {
[this.selectedKey] = sortValue;
}
if (searchValue.length > 0) {
[this.searchTerm] = searchValue;
}
},
methods: {
isSortMethodSelected(sortKey) {
return sortKey === this.selectedKey;
},
visitUrlFromOption(sortKey) {
this.selectedKey = sortKey;
const urlParams = {};
urlParams.search = this.searchTerm.length > 0 ? this.searchTerm : null;
urlParams.sort = sortKey;
const newUrl = mergeUrlParams(urlParams, this.filterTagsPath);
visitUrl(newUrl);
},
},
};
</script>
<template>
<div class="gl-display-flex gl-pr-3">
<gl-search-box-by-click
v-model="searchTerm"
:placeholder="$options.i18n.searchPlaceholder"
class="gl-pr-3"
data-testid="tag-search"
@submit="visitUrlFromOption(selectedKey)"
/>
<gl-dropdown :text="selectedSortMethod" right data-testid="tags-dropdown">
<gl-dropdown-item
v-for="(value, key) in sortOptions"
:key="key"
:is-checked="isSortMethodSelected(key)"
is-check-item
@click="visitUrlFromOption(key)"
>
{{ value }}
</gl-dropdown-item>
</gl-dropdown>
</div>
</template>

View File

@ -0,0 +1,24 @@
import Vue from 'vue';
import SortDropdown from './components/sort_dropdown.vue';
const mountDropdownApp = (el) => {
const { sortOptions, filterTagsPath } = el.dataset;
return new Vue({
el,
name: 'SortTagsDropdownApp',
components: {
SortDropdown,
},
provide: {
sortOptions: JSON.parse(sortOptions),
filterTagsPath,
},
render: (createElement) => createElement(SortDropdown),
});
};
export default () => {
const el = document.getElementById('js-tags-sort-dropdown');
return el ? mountDropdownApp(el) : null;
};

View File

@ -9,6 +9,9 @@ class Projects::TagsController < Projects::ApplicationController
before_action :require_non_empty_project
before_action :authorize_download_code!
before_action :authorize_admin_tag!, only: [:new, :create, :destroy]
before_action do
push_frontend_feature_flag(:gldropdown_tags, default_enabled: :yaml)
end
feature_category :source_code_management, [:index, :show, :new, :destroy]
feature_category :release_evidence, [:create]

View File

@ -3,7 +3,7 @@
module Clusters
module Applications
class Prometheus < ApplicationRecord
include PrometheusAdapter
include ::Clusters::Concerns::PrometheusClient
VERSION = '10.4.1'
@ -58,14 +58,6 @@ module Clusters
'https://gitlab-org.gitlab.io/cluster-integration/helm-stable-archive'
end
def service_name
'prometheus-prometheus-server'
end
def service_port
80
end
def install_command
helm_command_module::InstallCommand.new(
name: name,
@ -106,29 +98,6 @@ module Clusters
files.merge('values.yaml': replaced_values)
end
def prometheus_client
return unless kube_client
proxy_url = kube_client.proxy_url('service', service_name, service_port, Gitlab::Kubernetes::Helm::NAMESPACE)
# ensures headers containing auth data are appended to original k8s client options
options = kube_client.rest_client.options
.merge(prometheus_client_default_options)
.merge(headers: kube_client.headers)
Gitlab::PrometheusClient.new(proxy_url, options)
rescue Kubeclient::HttpError, Errno::ECONNRESET, Errno::ECONNREFUSED, Errno::ENETUNREACH
# If users have mistakenly set parameters or removed the depended clusters,
# `proxy_url` could raise an exception because gitlab can not communicate with the cluster.
# Since `PrometheusAdapter#can_query?` is eargely loaded on environement pages in gitlab,
# we need to silence the exceptions
end
def configured?
kube_client.present? && available?
rescue Gitlab::UrlBlocker::BlockedUrlError
false
end
def generate_alert_manager_token!
unless alert_manager_token.present?
update!(alert_manager_token: generate_token)
@ -146,10 +115,6 @@ module Clusters
.perform_async(cluster_id, ::PrometheusService.to_param) # rubocop:disable CodeReuse/ServiceClass
end
def kube_client
cluster&.kubeclient&.core_client
end
def install_knative_metrics
return [] unless cluster.application_knative_available?

View File

@ -0,0 +1,50 @@
# frozen_string_literal: true
module Clusters
module Concerns
module PrometheusClient
extend ActiveSupport::Concern
included do
include PrometheusAdapter
def service_name
'prometheus-prometheus-server'
end
def service_port
80
end
def prometheus_client
return unless kube_client
proxy_url = kube_client.proxy_url('service', service_name, service_port, Gitlab::Kubernetes::Helm::NAMESPACE)
# ensures headers containing auth data are appended to original k8s client options
options = kube_client.rest_client.options
.merge(prometheus_client_default_options)
.merge(headers: kube_client.headers)
Gitlab::PrometheusClient.new(proxy_url, options)
rescue Kubeclient::HttpError, Errno::ECONNRESET, Errno::ECONNREFUSED, Errno::ENETUNREACH
# If users have mistakenly set parameters or removed the depended clusters,
# `proxy_url` could raise an exception because gitlab can not communicate with the cluster.
# Since `PrometheusAdapter#can_query?` is eargely loaded on environement pages in gitlab,
# we need to silence the exceptions
end
def configured?
kube_client.present? && available?
rescue Gitlab::UrlBlocker::BlockedUrlError
false
end
private
def kube_client
cluster&.kubeclient&.core_client
end
end
end
end
end

View File

@ -127,7 +127,7 @@ class User < ApplicationRecord
# Groups
has_many :members
has_many :group_members, -> { where(requested_at: nil).where("access_level >= ?", Gitlab::Access::GUEST) }, source: 'GroupMember'
has_many :group_members, -> { where(requested_at: nil).where("access_level >= ?", Gitlab::Access::GUEST) }, class_name: 'GroupMember'
has_many :groups, through: :group_members
has_many :owned_groups, -> { where(members: { access_level: Gitlab::Access::OWNER }) }, through: :group_members, source: :group
has_many :maintainers_groups, -> { where(members: { access_level: Gitlab::Access::MAINTAINER }) }, through: :group_members, source: :group
@ -141,7 +141,7 @@ class User < ApplicationRecord
-> { where(members: { access_level: [Gitlab::Access::REPORTER, Gitlab::Access::DEVELOPER, Gitlab::Access::MAINTAINER, Gitlab::Access::OWNER] }) },
through: :group_members,
source: :group
has_many :minimal_access_group_members, -> { where(access_level: [Gitlab::Access::MINIMAL_ACCESS]) }, source: 'GroupMember', class_name: 'GroupMember'
has_many :minimal_access_group_members, -> { where(access_level: [Gitlab::Access::MINIMAL_ACCESS]) }, class_name: 'GroupMember'
has_many :minimal_access_groups, through: :minimal_access_group_members, source: :group
# Projects

View File

@ -38,7 +38,7 @@ module Issues
user_agent_detail_service.create
resolve_discussions_with_issue(issue)
if Feature.disabled?(:issue_perform_after_creation_tasks_async, issue.project)
if Feature.disabled?(:issue_perform_after_creation_tasks_async, issue.project, default_enabled: :yaml)
Issues::AfterCreateService
.new(issue.project, current_user)
.execute(issue)

View File

@ -1,18 +1,18 @@
.row.gl-mt-3.gl-mb-3
.col-lg-3
%h4.gl-mt-0
Recent Deliveries
%p When an event in GitLab triggers a webhook, you can use the request details to figure out if something went wrong.
= _('Recent Deliveries')
%p= _('When an event in GitLab triggers a webhook, you can use the request details to figure out if something went wrong.')
.col-lg-9
- if hook_logs.any?
%table.table
%thead
%tr
%th Status
%th Trigger
%th URL
%th Elapsed time
%th Request time
%th= _('Status')
%th= _('Trigger')
%th= _('URL')
%th= _('Elapsed time')
%th= _('Request time')
%th
- hook_logs.each do |hook_log|
%tr
@ -28,10 +28,10 @@
%td.light
= time_ago_with_tooltip(hook_log.created_at)
%td
= link_to 'View details', admin_hook_hook_log_path(hook, hook_log)
= link_to _('View details'), admin_hook_hook_log_path(hook, hook_log)
= paginate hook_logs, theme: 'gitlab'
- else
.settings-message.text-center
You don't have any webhooks deliveries
= _("You don't have any webhooks deliveries")

View File

@ -4,7 +4,7 @@
- @projects.each_with_index do |project|
%li.project-row{ class: ('no-description' if project.description.blank?) }
.controls
= link_to 'Edit', edit_project_path(project), id: "edit_#{dom_id(project)}", class: "btn gl-button btn-default"
= link_to _('Edit'), edit_project_path(project), id: "edit_#{dom_id(project)}", class: "btn gl-button btn-default"
%button.delete-project-button.gl-button.btn.btn-danger{ data: { delete_project_url: admin_project_path(project), project_name: project.name } }
= s_('AdminProjects|Delete')
@ -31,6 +31,6 @@
= paginate @projects, theme: 'gitlab'
- else
.nothing-here-block No projects found
.nothing-here-block= _('No projects found')
#delete-project-modal

View File

@ -3,15 +3,15 @@
.table-mobile-header{ role: 'rowheader' }= _('Type')
.table-mobile-content
- if runner.instance_type?
%span.badge.badge-pill.gl-badge.sm.badge-success shared
%span.badge.badge-pill.gl-badge.sm.badge-success= _("shared")
- elsif runner.group_type?
%span.badge.badge-pill.gl-badge.sm.badge-success group
%span.badge.badge-pill.gl-badge.sm.badge-success= _("group")
- else
%span.badge.badge-pill.gl-badge.sm.badge-info specific
%span.badge.badge-pill.gl-badge.sm.badge-info= _("specific")
- if runner.locked?
%span.badge.badge-pill.gl-badge.sm.badge-warning locked
%span.badge.badge-pill.gl-badge.sm.badge-warning= _("locked")
- unless runner.active?
%span.badge.badge-pill.gl-badge.sm.badge-danger paused
%span.badge.badge-pill.gl-badge.sm.badge-danger= _("paused")
.table-section.section-10
.table-mobile-header{ role: 'rowheader' }= _('Runner token')

View File

@ -9,20 +9,23 @@
= s_('TagsPage|Tags give the ability to mark specific points in history as being important')
.nav-controls
= form_tag(filter_tags_path, method: :get) do
= search_field_tag :search, params[:search], { placeholder: s_('TagsPage|Filter by tag name'), id: 'tag-search', class: 'form-control search-text-input input-short', spellcheck: false }
- unless Gitlab::Ci::Features.gldropdown_tags_enabled?
= form_tag(filter_tags_path, method: :get) do
= search_field_tag :search, params[:search], { placeholder: s_('TagsPage|Filter by tag name'), id: 'tag-search', class: 'form-control search-text-input input-short', spellcheck: false }
.dropdown
%button.dropdown-menu-toggle{ type: 'button', data: { toggle: 'dropdown'} }
%span.light
= tags_sort_options_hash[@sort]
= sprite_icon('chevron-down', css_class: 'dropdown-menu-toggle-icon gl-top-3')
%ul.dropdown-menu.dropdown-menu-right.dropdown-menu-selectable
%li.dropdown-header
= s_('TagsPage|Sort by')
- tags_sort_options_hash.each do |value, title|
%li
= link_to title, filter_tags_path(sort: value), class: ("is-active" if @sort == value)
.dropdown
%button.dropdown-menu-toggle{ type: 'button', data: { toggle: 'dropdown'} }
%span.light
= tags_sort_options_hash[@sort]
= sprite_icon('chevron-down', css_class: 'dropdown-menu-toggle-icon gl-top-3')
%ul.dropdown-menu.dropdown-menu-right.dropdown-menu-selectable
%li.dropdown-header
= s_('TagsPage|Sort by')
- tags_sort_options_hash.each do |value, title|
%li
= link_to title, filter_tags_path(sort: value), class: ("is-active" if @sort == value)
- else
#js-tags-sort-dropdown{ data: { filter_tags_path: filter_tags_path, sort_options: tags_sort_options_hash.to_json } }
- if can?(current_user, :admin_tag, @project)
= link_to new_project_tag_path(@project), class: 'btn gl-button btn-confirm', data: { qa_selector: "new_tag_button" } do
= s_('TagsPage|New tag')

View File

@ -17,7 +17,7 @@ class NewIssueWorker # rubocop:disable Scalability/IdempotentWorker
issuable.create_cross_references!(user)
if Feature.enabled?(:issue_perform_after_creation_tasks_async, issuable.project)
if Feature.enabled?(:issue_perform_after_creation_tasks_async, issuable.project, default_enabled: :yaml)
Issues::AfterCreateService
.new(issuable.project, user)
.execute(issuable)

View File

@ -0,0 +1,5 @@
---
title: Externalize strings in hook_logs/_index.html.haml
merge_request: 58155
author: nuwe1
type: other

View File

@ -0,0 +1,5 @@
---
title: Externalize strings in projects/_projects.html.haml
merge_request: 58158
author: nuwe1
type: other

View File

@ -0,0 +1,5 @@
---
title: Externalise strings in runners/_runner.html.haml
merge_request: 58168
author: nuwe1
type: other

View File

@ -0,0 +1,8 @@
---
name: gldropdown_tags
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/58589
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/327055
milestone: '13.11'
type: development
group: group::continuous integration
default_enabled: false

View File

@ -5,4 +5,4 @@ rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/21140
milestone: '13.11'
type: development
group: group::geo
default_enabled: false
default_enabled: true

View File

@ -1,16 +0,0 @@
---
key_path: counts.confidential_epics
description:
product_section: dev
product_stage: plan
product_group: group::portfolio management
product_category:
value_type: number
status: data_available
time_frame: all
data_source: database
distribution:
- ce
tier:
- free
skip_validation: true

View File

@ -1,16 +0,0 @@
---
key_path: counts.epics
description:
product_section: dev
product_stage: plan
product_group: group::portfolio management
product_category:
value_type: number
status: data_available
time_frame: all
data_source: database
distribution:
- ce
tier:
- free
skip_validation: true

View File

@ -1,16 +0,0 @@
---
key_path: counts.issues_with_health_status
description:
product_section: dev
product_stage: plan
product_group: group::portfolio management
product_category:
value_type: number
status: data_available
time_frame: all
data_source: database
distribution:
- ce
tier:
- free
skip_validation: true

View File

@ -921,6 +921,7 @@ cluster.
> - [Made generally available and enabled by default](https://gitlab.com/gitlab-org/gitaly/-/issues/2951) in GitLab 13.3.
> - [Disabled by default](https://gitlab.com/gitlab-org/gitaly/-/issues/3178) in GitLab 13.5.
> - [Enabled by default](https://gitlab.com/gitlab-org/gitaly/-/issues/3334) in GitLab 13.8.
> - [Feature flag removed](https://gitlab.com/gitlab-org/gitaly/-/issues/3383) in GitLab 13.11.
Praefect supports distribution of read operations across Gitaly nodes that are
configured for the virtual node.

View File

@ -790,15 +790,15 @@ Tiers: `free`
### `counts.confidential_epics`
Missing description
Count of confidential epics
[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/config/metrics/counts_all/20210216181205_confidential_epics.yml)
[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/ee/config/metrics/counts_all/20210216181205_confidential_epics.yml)
Group: `group::portfolio management`
Group: `group::product planning`
Status: `data_available`
Tiers: `free`
Tiers: `premium`, `ultimate`
### `counts.container_scanning_jobs`
@ -962,7 +962,7 @@ Count of issues that are assigned to an epic
[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/ee/config/metrics/counts_all/20210216181208_epic_issues.yml)
Group: `group::portfolio management`
Group: `group::product planning`
Status: `data_available`
@ -970,27 +970,27 @@ Tiers: `premium`, `ultimate`
### `counts.epics`
Missing description
Count of all epics
[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/config/metrics/counts_all/20210216181206_epics.yml)
[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/ee/config/metrics/counts_all/20210216181206_epics.yml)
Group: `group::portfolio management`
Group: `group::product planning`
Status: `data_available`
Tiers: `free`
Tiers: `premium`, `ultimate`
### `counts.epics_deepest_relationship_level`
Missing description
Count of the deepest relationship level for epics
[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/ee/config/metrics/counts_all/20210216181212_epics_deepest_relationship_level.yml)
Group: `group::portfolio management`
Group: `group::product planning`
Status: `data_available`
Tiers:
Tiers: `ultimate`
### `counts.failed_deployments`
@ -2698,15 +2698,15 @@ Tiers: `free`, `premium`, `ultimate`
### `counts.issues_with_health_status`
Missing description
Count of issues with health status
[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/config/metrics/counts_all/20210216181210_issues_with_health_status.yml)
[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/ee/config/metrics/counts_all/20210216181210_issues_with_health_status.yml)
Group: `group::portfolio management`
Group: `group::product planning`
Status: `data_available`
Tiers: `free`
Tiers: `ultimate`
### `counts.jira_imports_projects_count`

View File

@ -571,9 +571,8 @@ Instructions are available in the [legacy template project](https://gitlab.com/g
This is the current default behavior, because the job's status indicates success or failure of the analyzer itself.
Analyzer results are displayed in the [job logs](../../ci/jobs/index.md#expand-and-collapse-job-log-sections),
[Merge Request widget](sast/index.md)
[Merge Request widget](#view-security-scan-information-in-merge-requests)
or [Security Dashboard](security_dashboard/index.md).
There is [an open issue](https://gitlab.com/gitlab-org/gitlab/-/issues/235772) in which changes to this behavior are being discussed.
### Error: job `is used for configuration only, and its script should not be executed`

View File

@ -262,6 +262,9 @@ To view the activity feed in Atom format, select the
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/18328) in GitLab 12.7.
NOTE:
In GitLab 13.11, you can [replace this form with a modal window](#share-a-group-modal-window).
Similar to how you [share a project with a group](../project/members/share_project_with_groups.md),
you can share a group with another group. Members get direct access
to the shared group. This is not valid for inherited members.
@ -278,6 +281,27 @@ To share a given group, for example, `Frontend` with another group, for example,
All the members of the `Engineering` group are added to the `Frontend` group.
### Share a group modal window
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/247208) in GitLab 13.11.
> - [Deployed behind a feature flag](../feature_flags.md), disabled by default.
> - Enabled on GitLab.com.
> - Recommended for production use.
> - Replaces the existing form with buttons to open a modal window.
> - To use in GitLab self-managed instances, ask a GitLab administrator to [enable it](../project/members/index.md#enable-or-disable-modal-window). **(FREE SELF)**
WARNING:
This feature might not be available to you. Check the **version history** note above for details.
In GitLab 13.11, you can optionally replace the sharing form with a modal window.
To share a group after enabling this feature:
1. Go to your group's page.
1. In the left sidebar, go to **Members**, and then select **Invite a group**.
1. Select a group, and select a **Max access level**.
1. (Optional) Select an **Access expiration date**.
1. Select **Invite**.
## Manage group memberships via LDAP **(PREMIUM SELF)**
Group syncing allows LDAP groups to be mapped to GitLab groups. This provides more control over per-group user management. To configure group syncing, edit the `group_base` **DN** (`'OU=Global Groups,OU=GitLab INT,DC=GitLab,DC=org'`). This **OU** contains all groups that will be associated with GitLab groups.

View File

@ -109,6 +109,9 @@ had on the project you imported from are retained.
## Invite people using their e-mail address
NOTE:
In GitLab 13.11, you can [replace this form with a modal window](#add-a-member-modal-window).
If a user you want to give access to doesn't have an account on your GitLab
instance, you can invite them just by typing their e-mail address in the
user search field.
@ -135,6 +138,46 @@ GitLab account using the same e-mail address the invitation was sent to.
NOTE:
Unaccepted invites are automatically deleted after 90 days.
### Add a member modal window
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/247208) in GitLab 13.11.
> - [Deployed behind a feature flag](../../feature_flags.md), disabled by default.
> - Enabled on GitLab.com.
> - Recommended for production use.
> - Replaces the existing form with buttons to open a modal window.
> - To use in GitLab self-managed instances, ask a GitLab administrator to [enable it](#enable-or-disable-modal-window). **(FREE SELF)**
WARNING:
This feature might not be available to you. Check the **version history** note above for details.
In GitLab 13.11, you can optionally replace the form to add a member with a modal window.
To add a member after enabling this feature:
1. Go to your project's page.
1. In the left sidebar, go to **Members**, and then select **Invite members**.
1. Enter an email address, and select a role permission for this user.
1. (Optional) Select an **Access expiration date**.
1. Select **Invite**.
### Enable or disable modal window **(FREE SELF)**
The modal window for adding a member is under development and is ready for production use. It is
deployed behind a feature flag that is **disabled by default**.
[GitLab administrators with access to the GitLab Rails console](../../../administration/feature_flags.md)
can enable it.
To enable it:
```ruby
Feature.enable(:invite_members_group_modal)
```
To disable it:
```ruby
Feature.disable(:invite_members_group_modal)
```
## Project membership and requesting access
Project owners can :

View File

@ -17,6 +17,9 @@ members.
## Sharing a project with a group of users
NOTE:
In GitLab 13.11, you can [replace this form with a modal window](#share-a-project-modal-window).
The primary mechanism to give a group of users, say 'Engineering', access to a project,
say 'Project Acme', in GitLab is to make the 'Engineering' group the owner of 'Project
Acme'. But what if 'Project Acme' already belongs to another group, say 'Open Source'?
@ -48,6 +51,46 @@ Note that you can only share a project with:
Administrators are able to share projects with any group in the system.
### Share a project modal window
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/247208) in GitLab 13.11.
> - [Deployed behind a feature flag](../../feature_flags.md), disabled by default.
> - Enabled on GitLab.com.
> - Recommended for production use.
> - Replaces the existing form with buttons to open a modal window.
> - To use in GitLab self-managed instances, ask a GitLab administrator to [enable it](#enable-or-disable-modal-window). **(FREE SELF)**
WARNING:
This feature might not be available to you. Check the **version history** note above for details.
In GitLab 13.11, you can optionally replace the sharing form with a modal window.
To share a project after enabling this feature:
1. Go to your project's page.
1. In the left sidebar, go to **Members**, and then select **Invite a group**.
1. Select a group, and select a **Max access level**.
1. (Optional) Select an **Access expiration date**.
1. Select **Invite**.
### Enable or disable modal window **(FREE SELF)**
The modal window for sharing a project is under development and is ready for production use. It is
deployed behind a feature flag that is **disabled by default**.
[GitLab administrators with access to the GitLab Rails console](../../../administration/feature_flags.md)
can enable it.
To enable it:
```ruby
Feature.enable(:invite_members_group_modal)
```
To disable it:
```ruby
Feature.disable(:invite_members_group_modal)
```
## Maximum access level
In the example above, the maximum access level of 'Developer' for members from 'Engineering' means that users with higher access levels in 'Engineering' ('Maintainer' or 'Owner') only have 'Developer' access to 'Project Acme'.

View File

@ -63,6 +63,10 @@ module Gitlab
def self.multiple_cache_per_job?
::Feature.enabled?(:multiple_cache_per_job, default_enabled: :yaml)
end
def self.gldropdown_tags_enabled?
::Feature.enabled?(:gldropdown_tags, default_enabled: :yaml)
end
end
end
end

View File

@ -11547,6 +11547,9 @@ msgstr ""
msgid "Editor Lite instance is required to set up an extension."
msgstr ""
msgid "Elapsed time"
msgstr ""
msgid "Elasticsearch AWS IAM credentials"
msgstr ""
@ -21356,6 +21359,9 @@ msgstr ""
msgid "No prioritized labels with such name or description"
msgstr ""
msgid "No projects found"
msgstr ""
msgid "No public groups"
msgstr ""
@ -25767,6 +25773,9 @@ msgstr ""
msgid "Recent Activity"
msgstr ""
msgid "Recent Deliveries"
msgstr ""
msgid "Recent Project Activity"
msgstr ""
@ -26580,6 +26589,9 @@ msgstr ""
msgid "Request review from"
msgstr ""
msgid "Request time"
msgstr ""
msgid "Request to link SAML account must be authorized"
msgstr ""
@ -35018,6 +35030,9 @@ msgstr ""
msgid "When a runner is locked, it cannot be assigned to other projects"
msgstr ""
msgid "When an event in GitLab triggers a webhook, you can use the request details to figure out if something went wrong."
msgstr ""
msgid "When enabled, any user visiting %{host} and creating an account will have to be explicitly approved by an admin before they can sign in. This setting is effective only if sign-ups are enabled."
msgstr ""
@ -35647,6 +35662,9 @@ msgstr ""
msgid "You don't have any recent searches"
msgstr ""
msgid "You don't have any webhooks deliveries"
msgstr ""
msgid "You don't have sufficient permission to perform this action."
msgstr ""
@ -37556,6 +37574,9 @@ msgstr ""
msgid "severity|Unknown"
msgstr ""
msgid "shared"
msgstr ""
msgid "should be an array of %{object_name} objects"
msgstr ""

View File

@ -0,0 +1,81 @@
import { GlDropdownItem, GlSearchBoxByClick } from '@gitlab/ui';
import { mount } from '@vue/test-utils';
import { extendedWrapper } from 'helpers/vue_test_utils_helper';
import * as urlUtils from '~/lib/utils/url_utility';
import SortDropdown from '~/tags/components/sort_dropdown.vue';
describe('Tags sort dropdown', () => {
let wrapper;
const createWrapper = (props = {}) => {
return extendedWrapper(
mount(SortDropdown, {
provide: {
filterTagsPath: '/root/ci-cd-project-demo/-/tags',
sortOptions: {
name_asc: 'Name',
updated_asc: 'Oldest updated',
updated_desc: 'Last updated',
},
...props,
},
}),
);
};
const findSearchBox = () => wrapper.findComponent(GlSearchBoxByClick);
const findTagsDropdown = () => wrapper.findByTestId('tags-dropdown');
afterEach(() => {
if (wrapper) {
wrapper.destroy();
}
});
describe('default state', () => {
beforeEach(() => {
wrapper = createWrapper();
});
it('should have a search box with a placeholder', () => {
const searchBox = findSearchBox();
expect(searchBox.exists()).toBe(true);
expect(searchBox.find('input').attributes('placeholder')).toBe('Filter by tag name');
});
it('should have a sort order dropdown', () => {
const branchesDropdown = findTagsDropdown();
expect(branchesDropdown.exists()).toBe(true);
});
});
describe('when submitting a search term', () => {
beforeEach(() => {
urlUtils.visitUrl = jest.fn();
wrapper = createWrapper();
});
it('should call visitUrl', () => {
const searchBox = findSearchBox();
searchBox.vm.$emit('submit');
expect(urlUtils.visitUrl).toHaveBeenCalledWith(
'/root/ci-cd-project-demo/-/tags?sort=updated_desc',
);
});
it('should send a sort parameter', () => {
const sortDropdownItems = findTagsDropdown().findAllComponents(GlDropdownItem).at(0);
sortDropdownItems.vm.$emit('click');
expect(urlUtils.visitUrl).toHaveBeenCalledWith(
'/root/ci-cd-project-demo/-/tags?sort=name_asc',
);
});
});
});

View File

@ -74,85 +74,8 @@ RSpec.describe Clusters::Applications::Prometheus do
end
describe '#prometheus_client' do
shared_examples 'exception caught for prometheus client' do
before do
allow(kube_client).to receive(:proxy_url).and_raise(exception)
end
it 'returns nil' do
expect(subject.prometheus_client).to be_nil
end
end
context 'cluster is nil' do
it 'returns nil' do
expect(subject.cluster).to be_nil
expect(subject.prometheus_client).to be_nil
end
end
context "cluster doesn't have kubeclient" do
let(:cluster) { create(:cluster) }
subject { create(:clusters_applications_prometheus, cluster: cluster) }
it 'returns nil' do
expect(subject.prometheus_client).to be_nil
end
end
context 'cluster has kubeclient' do
let(:cluster) { create(:cluster, :project, :provided_by_gcp) }
let(:kubernetes_url) { subject.cluster.platform_kubernetes.api_url }
let(:kube_client) { subject.cluster.kubeclient.core_client }
subject { create(:clusters_applications_prometheus, cluster: cluster) }
before do
subject.cluster.platform_kubernetes.namespace = 'a-namespace'
stub_kubeclient_discover(cluster.platform_kubernetes.api_url)
create(:cluster_kubernetes_namespace,
cluster: cluster,
cluster_project: cluster.cluster_project,
project: cluster.cluster_project.project)
end
it 'creates proxy prometheus_client' do
expect(subject.prometheus_client).to be_instance_of(Gitlab::PrometheusClient)
end
it 'merges proxy_url, options and headers from kube client with prometheus_client options' do
expect(Gitlab::PrometheusClient)
.to(receive(:new))
.with(a_valid_url, kube_client.rest_client.options.merge({
headers: kube_client.headers,
timeout: PrometheusAdapter::DEFAULT_PROMETHEUS_REQUEST_TIMEOUT_SEC
}))
subject.prometheus_client
end
context 'when cluster is not reachable' do
it_behaves_like 'exception caught for prometheus client' do
let(:exception) { Kubeclient::HttpError.new(401, 'Unauthorized', nil) }
end
end
context 'when there is a socket error while contacting cluster' do
it_behaves_like 'exception caught for prometheus client' do
let(:exception) { Errno::ECONNREFUSED }
end
it_behaves_like 'exception caught for prometheus client' do
let(:exception) { Errno::ECONNRESET }
end
end
context 'when the network is unreachable' do
it_behaves_like 'exception caught for prometheus client' do
let(:exception) { Errno::ENETUNREACH }
end
end
include_examples '#prometheus_client shared' do
let(:factory) { :clusters_applications_prometheus }
end
end

View File

@ -0,0 +1,86 @@
# frozen_string_literal: true
# Input
# - factory: [:clusters_applications_prometheus, :clusters_integrations_prometheus]
RSpec.shared_examples '#prometheus_client shared' do
shared_examples 'exception caught for prometheus client' do
before do
allow(kube_client).to receive(:proxy_url).and_raise(exception)
end
it 'returns nil' do
expect(subject.prometheus_client).to be_nil
end
end
context 'cluster is nil' do
it 'returns nil' do
expect(subject.cluster).to be_nil
expect(subject.prometheus_client).to be_nil
end
end
context "cluster doesn't have kubeclient" do
let(:cluster) { create(:cluster) }
subject { create(factory, cluster: cluster) }
it 'returns nil' do
expect(subject.prometheus_client).to be_nil
end
end
context 'cluster has kubeclient' do
let(:cluster) { create(:cluster, :project, :provided_by_gcp) }
let(:kubernetes_url) { subject.cluster.platform_kubernetes.api_url }
let(:kube_client) { subject.cluster.kubeclient.core_client }
subject { create(factory, cluster: cluster) }
before do
subject.cluster.platform_kubernetes.namespace = 'a-namespace'
stub_kubeclient_discover(cluster.platform_kubernetes.api_url)
create(:cluster_kubernetes_namespace,
cluster: cluster,
cluster_project: cluster.cluster_project,
project: cluster.cluster_project.project)
end
it 'creates proxy prometheus_client' do
expect(subject.prometheus_client).to be_instance_of(Gitlab::PrometheusClient)
end
it 'merges proxy_url, options and headers from kube client with prometheus_client options' do
expect(Gitlab::PrometheusClient)
.to(receive(:new))
.with(a_valid_url, kube_client.rest_client.options.merge({
headers: kube_client.headers,
timeout: PrometheusAdapter::DEFAULT_PROMETHEUS_REQUEST_TIMEOUT_SEC
}))
subject.prometheus_client
end
context 'when cluster is not reachable' do
it_behaves_like 'exception caught for prometheus client' do
let(:exception) { Kubeclient::HttpError.new(401, 'Unauthorized', nil) }
end
end
context 'when there is a socket error while contacting cluster' do
it_behaves_like 'exception caught for prometheus client' do
let(:exception) { Errno::ECONNREFUSED }
end
it_behaves_like 'exception caught for prometheus client' do
let(:exception) { Errno::ECONNRESET }
end
end
context 'when the network is unreachable' do
it_behaves_like 'exception caught for prometheus client' do
let(:exception) { Errno::ENETUNREACH }
end
end
end
end

View File

@ -21,6 +21,7 @@ RSpec.describe 'projects/tags/index.html.haml' do
end
it 'defaults sort dropdown toggle to last updated' do
stub_feature_flags(gldropdown_tags: false)
render
expect(rendered).to have_button('Last updated')
end