Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
da07b341fd
commit
962af1ccc6
25 changed files with 397 additions and 70 deletions
|
@ -808,10 +808,6 @@ RSpec/EmptyLineAfterFinalLetItBe:
|
|||
- ee/spec/workers/set_user_status_based_on_user_cap_setting_worker_spec.rb
|
||||
- spec/controllers/confirmations_controller_spec.rb
|
||||
- spec/controllers/dashboard/projects_controller_spec.rb
|
||||
- spec/controllers/groups/dependency_proxy_auth_controller_spec.rb
|
||||
- spec/controllers/groups/dependency_proxy_for_containers_controller_spec.rb
|
||||
- spec/controllers/groups/registry/repositories_controller_spec.rb
|
||||
- spec/controllers/groups/variables_controller_spec.rb
|
||||
- spec/controllers/invites_controller_spec.rb
|
||||
- spec/controllers/profiles/emails_controller_spec.rb
|
||||
- spec/controllers/projects/alerting/notifications_controller_spec.rb
|
||||
|
|
|
@ -22,16 +22,13 @@ export default {
|
|||
GlAlert,
|
||||
},
|
||||
mixins: [glFeatureFlagMixin()],
|
||||
inject: ['canAdminList'],
|
||||
props: {
|
||||
lists: {
|
||||
type: Array,
|
||||
required: false,
|
||||
default: () => [],
|
||||
},
|
||||
canAdminList: {
|
||||
type: Boolean,
|
||||
required: true,
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
required: true,
|
||||
|
@ -99,7 +96,7 @@ export default {
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<div v-cloak data-qa-selector="boards_list">
|
||||
<gl-alert v-if="error" variant="danger" :dismissible="true" @dismiss="unsetError">
|
||||
{{ error }}
|
||||
</gl-alert>
|
||||
|
|
|
@ -22,13 +22,7 @@ export default {
|
|||
import('ee_component/boards/components/board_settings_list_types.vue'),
|
||||
},
|
||||
mixins: [glFeatureFlagMixin()],
|
||||
props: {
|
||||
canAdminList: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
inject: ['canAdminList'],
|
||||
data() {
|
||||
return {
|
||||
ListType,
|
||||
|
|
|
@ -81,6 +81,7 @@ export default () => {
|
|||
rootPath: $boardApp.dataset.rootPath,
|
||||
currentUserId: gon.current_user_id || null,
|
||||
canUpdate: parseBoolean($boardApp.dataset.canUpdate),
|
||||
canAdminList: parseBoolean($boardApp.dataset.canAdminList),
|
||||
labelsFetchPath: $boardApp.dataset.labelsFetchPath,
|
||||
labelsManagePath: $boardApp.dataset.labelsManagePath,
|
||||
labelsFilterBasePath: $boardApp.dataset.labelsFilterBasePath,
|
||||
|
|
|
@ -228,7 +228,7 @@
|
|||
padding: 0 2px;
|
||||
|
||||
svg {
|
||||
margin-bottom: 1px;
|
||||
margin-bottom: 3px;
|
||||
height: $default-icon-size;
|
||||
width: $default-icon-size;
|
||||
border-radius: 50%;
|
||||
|
|
|
@ -14,7 +14,8 @@ module BoardsHelper
|
|||
root_path: root_path,
|
||||
full_path: full_path,
|
||||
bulk_update_path: @bulk_issues_path,
|
||||
can_update: (!!can?(current_user, :admin_issue, board)).to_s,
|
||||
can_update: can_update?.to_s,
|
||||
can_admin_list: can_admin_list?.to_s,
|
||||
time_tracking_limit_to_hours: Gitlab::CurrentSettings.time_tracking_limit_to_hours.to_s,
|
||||
recent_boards_endpoint: recent_boards_path,
|
||||
parent: current_board_parent.model_name.param_key,
|
||||
|
@ -88,6 +89,14 @@ module BoardsHelper
|
|||
@current_board_parent ||= @group || @project
|
||||
end
|
||||
|
||||
def can_update?
|
||||
can?(current_user, :admin_issue, board)
|
||||
end
|
||||
|
||||
def can_admin_list?
|
||||
can?(current_user, :admin_issue_board_list, current_board_parent)
|
||||
end
|
||||
|
||||
def can_admin_issue?
|
||||
can?(current_user, :admin_issue, current_board_parent)
|
||||
end
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
= f.label :create_chat_team do
|
||||
%span.gl-display-flex
|
||||
= custom_icon('icon_mattermost')
|
||||
%span.gl-ml-2 Mattermost
|
||||
%span.gl-ml-2= _('Mattermost')
|
||||
.col-sm-12
|
||||
.form-check.js-toggle-container
|
||||
.js-toggle-button.form-check-input= f.check_box(:create_chat_team, { checked: false }, true, false)
|
||||
|
@ -11,7 +11,7 @@
|
|||
= _('Create a Mattermost team for this group')
|
||||
%br
|
||||
%small.light.js-toggle-content
|
||||
Mattermost URL:
|
||||
= _('Mattermost URL:')
|
||||
= Settings.mattermost.host
|
||||
%span> /
|
||||
%span{ "data-bind-out" => "create_chat_team" }
|
||||
|
|
|
@ -1,15 +1,15 @@
|
|||
.form-group.row
|
||||
.col-sm-2.col-form-label.pt-0
|
||||
= f.label :lfs_enabled, 'Large File Storage'
|
||||
= f.label :lfs_enabled, _('Large File Storage')
|
||||
.col-sm-10
|
||||
.form-check
|
||||
= f.check_box :lfs_enabled, checked: @group.lfs_enabled?, class: 'form-check-input'
|
||||
= f.label :lfs_enabled, class: 'form-check-label' do
|
||||
%strong
|
||||
Allow projects within this group to use Git LFS
|
||||
= _('Allow projects within this group to use Git LFS')
|
||||
= link_to sprite_icon('question-o'), help_page_path('topics/git/lfs/index')
|
||||
%br/
|
||||
%span This setting can be overridden in each project.
|
||||
%span= _('This setting can be overridden in each project.')
|
||||
.form-group.row
|
||||
.col-sm-2.col-form-label
|
||||
= f.label s_('ProjectCreationLevel|Allowed to create projects')
|
||||
|
@ -24,16 +24,16 @@
|
|||
|
||||
.form-group.row
|
||||
.col-sm-2.col-form-label.pt-0
|
||||
= f.label :require_two_factor_authentication, 'Two-factor authentication'
|
||||
= f.label :require_two_factor_authentication, _('Two-factor authentication')
|
||||
.col-sm-10
|
||||
.form-check
|
||||
= f.check_box :require_two_factor_authentication, class: 'form-check-input'
|
||||
= f.label :require_two_factor_authentication, class: 'form-check-label' do
|
||||
%strong
|
||||
Require all users in this group to set up Two-factor authentication
|
||||
= _("Require all users in this group to setup Two-factor authentication")
|
||||
= link_to sprite_icon('question-o'), help_page_path('security/two_factor_authentication', anchor: 'enforcing-2fa-for-all-users-in-a-group')
|
||||
.form-group.row
|
||||
.offset-sm-2.col-sm-10
|
||||
.form-check
|
||||
= f.text_field :two_factor_grace_period, class: 'form-control'
|
||||
.form-text.text-muted Amount of time (in hours) that users are allowed to skip forced configuration of two-factor authentication
|
||||
.form-text.text-muted= _("Amount of time (in hours) that users are allowed to skip forced configuration of two-factor authentication")
|
||||
|
|
|
@ -1,8 +1,5 @@
|
|||
- board = local_assigns.fetch(:board, nil)
|
||||
- group = local_assigns.fetch(:group, false)
|
||||
-# TODO: Move group_id and can_admin_list to the board store
|
||||
See: https://gitlab.com/gitlab-org/gitlab/-/issues/213082
|
||||
- can_admin_list = can?(current_user, :admin_issue_board_list, current_board_parent) == true
|
||||
- @no_breadcrumb_container = true
|
||||
- @no_container = true
|
||||
- @content_class = "issue-boards-content js-focus-mode-board"
|
||||
|
@ -15,11 +12,6 @@
|
|||
|
||||
= render 'shared/issuable/search_bar', type: :boards, board: board
|
||||
#board-app.boards-app.position-relative{ "v-cloak" => "true", data: board_data, ":class" => "{ 'is-compact': detailIssueVisible }" }
|
||||
%board-content{ "v-cloak" => "true",
|
||||
"ref" => "board_content",
|
||||
":lists" => "state.lists",
|
||||
":can-admin-list" => can_admin_list,
|
||||
":disabled" => "disabled",
|
||||
data: { qa_selector: "boards_list" } }
|
||||
%board-content{ ":lists" => "state.lists", ":disabled" => "disabled" }
|
||||
= render "shared/boards/components/sidebar", group: group
|
||||
%board-settings-sidebar{ ":can-admin-list" => can_admin_list }
|
||||
%board-settings-sidebar
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Externalise strings in groups/_group_admin_settings.html.haml
|
||||
merge_request: 58331
|
||||
author: nuwe1
|
||||
type: other
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Externalize strings in groups/_create_chat_team.html.haml
|
||||
merge_request: 58328
|
||||
author: nuwe1
|
||||
type: other
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Fix EmptyLineAfterFinalLetItBe Rubocop offenses for groups controller
|
||||
merge_request: 58174
|
||||
author: Huzaifa Iftikhar @huzaifaiftikhar
|
||||
type: fixed
|
|
@ -941,3 +941,226 @@ result as you did at the start. For example:
|
|||
```
|
||||
|
||||
Note that `enforced="true"` means that authentication is being enforced.
|
||||
|
||||
## Pack-objects cache **(FREE SELF)**
|
||||
|
||||
> - [Introduced](https://gitlab.com/groups/gitlab-com/gl-infra/-/epics/372) in GitLab 13.11.
|
||||
> - It's enabled on GitLab.com.
|
||||
> - It's recommended for production use.
|
||||
|
||||
[Gitaly](index.md), the service that provides storage for Git
|
||||
repositories, can be configured to cache a short rolling window of Git
|
||||
fetch responses. This can reduce server load when your server receives
|
||||
lots of CI fetch traffic.
|
||||
|
||||
### Overview
|
||||
|
||||
The pack-objects cache wraps `git pack-objects`, an internal part of
|
||||
Git that gets invoked indirectly via the PostUploadPack and
|
||||
SSHUploadPack Gitaly RPCs. These are the RPCs that Gitaly runs when a
|
||||
user does a Git fetch via HTTP or SSH, respectively. When the cache is
|
||||
enabled, anything that uses PostUploadPack or SSHUploadPack can
|
||||
benefit from it. It is orthogonal to:
|
||||
|
||||
- The transport (HTTP or SSH).
|
||||
- Git protocol version (v0 or v2).
|
||||
- The type of fetch (full clones, incremental fetches, shallow clones,
|
||||
partial clones, and so on).
|
||||
|
||||
The strength of this cache is its ability to deduplicate concurrent
|
||||
identical fetches. It:
|
||||
|
||||
- Can benefit GitLab instances where your users run CI/CD pipelines with many concurrent jobs.
|
||||
There should be a noticeable reduction in server CPU utilization.
|
||||
- Does not benefit unique fetches at all. For example, if you run a spot check by cloning a
|
||||
repository to your local computer, you are unlikely to see a benefit from this cache because
|
||||
your fetch is probably unique.
|
||||
|
||||
The pack-objects cache is a local cache. It:
|
||||
|
||||
- Stores its metadata in the memory of the Gitaly process it is enabled in.
|
||||
- Stores the actual Git data it is caching in files on local storage.
|
||||
|
||||
Using local files has the benefit that the operating system may
|
||||
automatically keep parts of the pack-objects cache files in RAM,
|
||||
making it faster.
|
||||
|
||||
Because the pack-objects cache can lead to a significant increase in
|
||||
disk write IO, it is off by default.
|
||||
|
||||
### Configure the cache
|
||||
|
||||
These are the configuration settings for the pack-objects cache. Each
|
||||
setting is discussed in greater detail below.
|
||||
|
||||
|Setting|Default|Description|
|
||||
|:---|:---|:---|
|
||||
|`enabled`|`false`|Turns on the cache. When off, Gitaly runs a dedicated `git pack-objects` process for each request. |
|
||||
|`dir`|`<PATH TO FIRST STORAGE>/+gitaly/PackObjectsCache`|Local directory where cache files get stored.|
|
||||
|`max_age`|`5m` (5 minutes)|Cache entries older than this get evicted and removed from disk.|
|
||||
|
||||
In `/etc/gitlab/gitlab.rb`, set:
|
||||
|
||||
```ruby
|
||||
gitaly['pack_objects_cache_enabled'] = true
|
||||
## gitaly['pack_objects_cache_dir'] = '/var/opt/gitlab/git-data/repositories/+gitaly/PackObjectsCache'
|
||||
## gitaly['pack_objects_cache_max_age'] = '5m'
|
||||
```
|
||||
|
||||
#### `enabled` defaults to `false`
|
||||
|
||||
The cache is disabled by default. This is because in some cases, it
|
||||
can create an [extreme
|
||||
increase](https://gitlab.com/gitlab-com/gl-infra/production/-/issues/4010#note_534564684)
|
||||
in the number of bytes written to disk. On GitLab.com, we have verified
|
||||
that our repository storage disks can handle this extra workload, but
|
||||
we felt we cannot assume this is true everywhere.
|
||||
|
||||
#### Cache storage directory `dir`
|
||||
|
||||
The cache needs a directory to store its files in. This directory
|
||||
should be:
|
||||
|
||||
- In a filesystem with enough space. If the cache filesystem runs out of space, all
|
||||
fetches start failing.
|
||||
- On a disk with enough IO bandwidth. If the cache disk runs out of IO bandwidth, all
|
||||
fetches, and probably the entire server, slows down.
|
||||
|
||||
By default, the cache storage directory is set to a subdirectory of the first Gitaly storage
|
||||
defined in the configuration file.
|
||||
|
||||
Multiple Gitaly processes can use the same directory for cache storage. Each Gitaly process
|
||||
uses a unique random string as part of the cache filenames it creates. This means:
|
||||
|
||||
- They do not collide.
|
||||
- They do not reuse another process's files.
|
||||
|
||||
While the default directory puts the cache files in the same
|
||||
filesystem as your repository data, this is not requirement. You can
|
||||
put the cache files on a different filesystem if that works better for
|
||||
your infrastructure.
|
||||
|
||||
The amount of IO bandwidth required from the disk depends on:
|
||||
|
||||
- The size and shape of the repositories on your Gitaly server.
|
||||
- The kind of traffic your users generate.
|
||||
|
||||
You can use the `gitaly_pack_objects_generated_bytes_total` metric as a pessimistic estimate,
|
||||
pretending your cache hit ratio is 0%.
|
||||
|
||||
The amount of space required depends on:
|
||||
|
||||
- The bytes per second that your users pull from the cache.
|
||||
- The size of the `max_age` cache eviction window.
|
||||
|
||||
If your users pull 100 MB/s and you use a 5 minute window, then on average you have
|
||||
`5*60*100MB = 30GB` of data in your cache directory. This is an expected average, not
|
||||
a guarantee. Peak size may exceed this average.
|
||||
|
||||
#### Cache eviction window `max_age`
|
||||
|
||||
The `max_age` configuration setting lets you control the chance of a
|
||||
cache hit and the average amount of storage used by cache files.
|
||||
Entries older than `max_age` get evicted from the in-memory metadata
|
||||
store, and deleted from disk.
|
||||
|
||||
Note that eviction does not interfere with ongoing requests, so it is OK
|
||||
for `max_age` to be less than the time it takes to do a fetch over a
|
||||
slow connection. This is because Unix filesystems do not truly delete
|
||||
a file until all processes that are reading the deleted file have
|
||||
closed it.
|
||||
|
||||
### Observe the cache
|
||||
|
||||
The cache can be observed in logs and using metrics.
|
||||
|
||||
#### Logs
|
||||
|
||||
|Message|Fields|Description|
|
||||
|:---|:---|:---|
|
||||
|`generated bytes`|`bytes`, `cache_key`|Logged when an entry was added to the cache|
|
||||
|`served bytes`|`bytes`, `cache_key`|Logged when an entry was read from the cache|
|
||||
|
||||
In the case of a:
|
||||
|
||||
- Cache miss, Gitaly logs both a `generated bytes` and a `served bytes` message.
|
||||
- Cache hit, Gitaly logs only a `served bytes` message.
|
||||
|
||||
Example:
|
||||
|
||||
```json
|
||||
{
|
||||
"bytes":26186490,
|
||||
"cache_key":"1b586a2698ca93c2529962e85cda5eea8f0f2b0036592615718898368b462e19",
|
||||
"correlation_id":"01F1MY8JXC3FZN14JBG1H42G9F",
|
||||
"grpc.meta.deadline_type":"none",
|
||||
"grpc.method":"PackObjectsHook",
|
||||
"grpc.request.fullMethod":"/gitaly.HookService/PackObjectsHook",
|
||||
"grpc.request.glProjectPath":"root/gitlab-workhorse",
|
||||
"grpc.request.glRepository":"project-2",
|
||||
"grpc.request.repoPath":"@hashed/d4/73/d4735e3a265e16eee03f59718b9b5d03019c07d8b6c51f90da3a666eec13ab35.git",
|
||||
"grpc.request.repoStorage":"default",
|
||||
"grpc.request.topLevelGroup":"@hashed",
|
||||
"grpc.service":"gitaly.HookService",
|
||||
"grpc.start_time":"2021-03-25T14:57:52.747Z",
|
||||
"level":"info",
|
||||
"msg":"generated bytes",
|
||||
"peer.address":"@",
|
||||
"pid":20961,
|
||||
"span.kind":"server",
|
||||
"system":"grpc",
|
||||
"time":"2021-03-25T14:57:53.543Z"
|
||||
}
|
||||
{
|
||||
"bytes":26186490,
|
||||
"cache_key":"1b586a2698ca93c2529962e85cda5eea8f0f2b0036592615718898368b462e19",
|
||||
"correlation_id":"01F1MY8JXC3FZN14JBG1H42G9F",
|
||||
"grpc.meta.deadline_type":"none",
|
||||
"grpc.method":"PackObjectsHook",
|
||||
"grpc.request.fullMethod":"/gitaly.HookService/PackObjectsHook",
|
||||
"grpc.request.glProjectPath":"root/gitlab-workhorse",
|
||||
"grpc.request.glRepository":"project-2",
|
||||
"grpc.request.repoPath":"@hashed/d4/73/d4735e3a265e16eee03f59718b9b5d03019c07d8b6c51f90da3a666eec13ab35.git",
|
||||
"grpc.request.repoStorage":"default",
|
||||
"grpc.request.topLevelGroup":"@hashed",
|
||||
"grpc.service":"gitaly.HookService",
|
||||
"grpc.start_time":"2021-03-25T14:57:52.747Z",
|
||||
"level":"info",
|
||||
"msg":"served bytes",
|
||||
"peer.address":"@",
|
||||
"pid":20961,
|
||||
"span.kind":"server",
|
||||
"system":"grpc",
|
||||
"time":"2021-03-25T14:57:53.543Z"
|
||||
}
|
||||
```
|
||||
|
||||
#### Metrics
|
||||
|
||||
The following cache metrics are available.
|
||||
|
||||
|Metric|Type|Labels|Description|
|
||||
|:---|:---|:---|:---|
|
||||
|`gitaly_pack_objects_cache_enabled`|gauge|`dir`,`max_age`|Set to `1` when the cache is enabled via the Gitaly config file|
|
||||
|`gitaly_pack_objects_cache_lookups_total`|counter|`result`|Hit/miss counter for cache lookups|
|
||||
|`gitaly_pack_objects_generated_bytes_total`|counter||Number of bytes written into the cache|
|
||||
|`gitaly_pack_objects_served_bytes_total`|counter||Number of bytes read from the cache|
|
||||
|`gitaly_streamcache_filestore_disk_usage_bytes`|gauge|`dir`|Total size of cache files|
|
||||
|`gitaly_streamcache_index_entries`|gauge|`dir`|Number of entries in the cache|
|
||||
|
||||
Some of these metrics start with `gitaly_streamcache`
|
||||
because they are generated by the "streamcache" internal library
|
||||
package in Gitaly.
|
||||
|
||||
Example:
|
||||
|
||||
```plaintext
|
||||
gitaly_pack_objects_cache_enabled{dir="/var/opt/gitlab/git-data/repositories/+gitaly/PackObjectsCache",max_age="300"} 1
|
||||
gitaly_pack_objects_cache_lookups_total{result="hit"} 2
|
||||
gitaly_pack_objects_cache_lookups_total{result="miss"} 1
|
||||
gitaly_pack_objects_generated_bytes_total 2.618649e+07
|
||||
gitaly_pack_objects_served_bytes_total 7.855947e+07
|
||||
gitaly_streamcache_filestore_disk_usage_bytes{dir="/var/opt/gitlab/git-data/repositories/+gitaly/PackObjectsCache"} 2.6200152e+07
|
||||
gitaly_streamcache_filestore_removed_total{dir="/var/opt/gitlab/git-data/repositories/+gitaly/PackObjectsCache"} 1
|
||||
gitaly_streamcache_index_entries{dir="/var/opt/gitlab/git-data/repositories/+gitaly/PackObjectsCache"} 1
|
||||
```
|
||||
|
|
|
@ -91,6 +91,9 @@ The following table describes details of your subscription:
|
|||
> - [Updated](https://gitlab.com/gitlab-org/gitlab/-/issues/292086) in GitLab 13.8 to include public
|
||||
email address.
|
||||
|
||||
To view a list of seats being used, go to **Settings > Billing**.
|
||||
Under **Seats currently in use**, select **See usage**.
|
||||
|
||||
The **Seat usage** page lists all users occupying seats. Details for each user include:
|
||||
|
||||
- Full name
|
||||
|
@ -117,6 +120,14 @@ For example:
|
|||
| Amir | `ami` | Yes |
|
||||
| Amir | `amr` | No |
|
||||
|
||||
## Subscription expiry
|
||||
|
||||
When your subscription expires, you can continue to use paid features of GitLab for 14 days.
|
||||
On the 15th day, paid features are no longer available. You can
|
||||
continue to use free features.
|
||||
|
||||
To resume paid feature functionality, purchase a new subscription.
|
||||
|
||||
## Renew your GitLab SaaS subscription
|
||||
|
||||
To renew your subscription:
|
||||
|
@ -145,23 +156,42 @@ It's important to regularly review your user accounts, because:
|
|||
attempt to renew a subscription for too few users.
|
||||
- Stale user accounts can be a security risk. A regular review helps reduce this risk.
|
||||
|
||||
#### Users over License
|
||||
#### Seats owed
|
||||
|
||||
A GitLab subscription is valid for a specific number of users. For details, see
|
||||
[Choose the number of users](#choose-the-number-of-users).
|
||||
|
||||
If the number of [billable users](#view-your-gitlab-saas-subscription) exceeds the number included in the subscription, known
|
||||
as the number of _users over license_, you must pay for the excess number of
|
||||
users either before renewal, or at the time of renewal. This is also known the
|
||||
_true up_ process.
|
||||
as the number of _seats owed_, you must pay for the excess number of users before renewal.
|
||||
|
||||
##### Seats owed example
|
||||
|
||||
You purchase a subscription for 10 users.
|
||||
|
||||
| Event | Billable members | Maximum users |
|
||||
|:---------------------------------------------------|:-----------------|:--------------|
|
||||
| Ten users occupy all 10 seats. | 10 | 10 |
|
||||
| Two new users join. | 12 | 12 |
|
||||
| Three users leave and their accounts are removed. | 9 | 12 |
|
||||
|
||||
Seats owed = 12 - 10 (Maximum users - users in subscription)
|
||||
|
||||
### Renew or change a GitLab SaaS subscription
|
||||
|
||||
You can adjust the number of users before renewing your GitLab SaaS subscription.
|
||||
Starting 30 days before a subscription expires, GitLab notifies group owners
|
||||
of the date of expiry with a banner in the GitLab user interface.
|
||||
|
||||
- To renew for more users than are currently included in your GitLab SaaS plan, [add users to your subscription](#add-users-to-your-subscription).
|
||||
- To renew for fewer users than are currently included in your GitLab SaaS plan,
|
||||
[remove](#remove-billable-user) the user accounts you no longer need.
|
||||
We recommend following these steps during renewal:
|
||||
|
||||
1. Prune any [inactive or unwanted users](#remove-billable-user).
|
||||
1. Determine if you have a need for user growth in the upcoming subscription.
|
||||
1. Log in to the [Customers Portal](https://customers.gitlab.com/customers/sign_in) and beneath your existing subscription, select the **Renew** button.
|
||||
1. Review your renewal details and complete the payment process.
|
||||
1. Select **Confirm purchase**.
|
||||
|
||||
Your updated subscription is applied to your namespace on the renewal period start date.
|
||||
|
||||
An invoice is generated for the renewal and available for viewing or download on the [View invoices](https://customers.gitlab.com/receipts) page. If you have difficulty during the renewal process, contact our [support team](https://support.gitlab.com/hc/en-us/requests/new?ticket_form_id=360000071293) for assistance.
|
||||
|
||||
For details on upgrading your subscription tier, see
|
||||
[Upgrade your GitLab SaaS subscription tier](#upgrade-your-gitlab-saas-subscription-tier).
|
||||
|
@ -235,14 +265,6 @@ To remove a billable user:
|
|||
1. In the row for the user you want to remove, on the right side, select the ellipsis and **Remove user**.
|
||||
1. Re-type the username and select **Remove user**.
|
||||
|
||||
## Subscription expiry
|
||||
|
||||
When your subscription expires, you can continue to use GitLab for 14 days.
|
||||
On the 15th day, paid features are no longer available. You can
|
||||
continue to use free features.
|
||||
|
||||
To resume paid feature functionality, purchase a new subscription.
|
||||
|
||||
## CI pipeline minutes
|
||||
|
||||
CI pipeline minutes are the execution time for your
|
||||
|
|
|
@ -150,9 +150,9 @@ You purchase a license for 10 users.
|
|||
|
||||
| Event | Billable members | Maximum users |
|
||||
|:---------------------------------------------------|:-----------------|:--------------|
|
||||
| Ten people (users) occupy all 10 seats. | 10 | 10 |
|
||||
| Two new people join. | 12 | 12 |
|
||||
| Three people leave and their accounts are removed. | 9 | 12 |
|
||||
| Ten users occupy all 10 seats. | 10 | 10 |
|
||||
| Two new users join. | 12 | 12 |
|
||||
| Three users leave and their accounts are removed. | 9 | 12 |
|
||||
|
||||
Users over license = 12 - 10 (Maximum users - users in license)
|
||||
|
||||
|
|
|
@ -109,7 +109,7 @@ There are two types of jobs:
|
|||
Here's our current suggestion for configuring your fuzz target's timeout:
|
||||
|
||||
- Set `COVFUZZ_BRANCH` to the branch where you want to run long-running (asynchronous) fuzzing
|
||||
jobs. This is `master` by default.
|
||||
jobs. This is normally the default branch.
|
||||
- Use regression or short-running fuzzing jobs for other branches or merge requests.
|
||||
|
||||
This suggestion helps find new bugs on the development branch and catch old bugs in merge requests
|
||||
|
@ -121,10 +121,10 @@ any option available in the underlying fuzzing engine.
|
|||
|
||||
### Available CI/CD variables
|
||||
|
||||
| CI/CD variable | Description |
|
||||
|-----------------------|--------------------------------------------------------------------|
|
||||
| `COVFUZZ_BRANCH` | The branch for long-running fuzzing jobs. The default is `master`. |
|
||||
| `COVFUZZ_SEED_CORPUS` | Path to a seed corpus directory. The default is empty. |
|
||||
| CI/CD variable | Description |
|
||||
|-----------------------|--------------------------------------------------------------------------------|
|
||||
| `COVFUZZ_BRANCH` | The branch for long-running fuzzing jobs. This is normally the default branch. |
|
||||
| `COVFUZZ_SEED_CORPUS` | Path to a seed corpus directory. The default is empty. |
|
||||
| `COVFUZZ_URL_PREFIX` | Path to the `gitlab-cov-fuzz` repository cloned for use with an offline environment. You should only change this when using an offline environment. The default value is `https://gitlab.com/gitlab-org/security-products/analyzers/gitlab-cov-fuzz/-/raw`. |
|
||||
|
||||
The files in the seed corpus (`COVFUZZ_SEED_CORPUS`), if provided, aren't updated unless you commit new
|
||||
|
|
|
@ -18969,6 +18969,12 @@ msgstr ""
|
|||
msgid "Match not found; try refining your search query."
|
||||
msgstr ""
|
||||
|
||||
msgid "Mattermost"
|
||||
msgstr ""
|
||||
|
||||
msgid "Mattermost URL:"
|
||||
msgstr ""
|
||||
|
||||
msgid "MattermostService|Add to Mattermost"
|
||||
msgstr ""
|
||||
|
||||
|
@ -26246,6 +26252,9 @@ msgstr ""
|
|||
msgid "Require admin approval for new sign-ups"
|
||||
msgstr ""
|
||||
|
||||
msgid "Require all users in this group to setup Two-factor authentication"
|
||||
msgstr ""
|
||||
|
||||
msgid "Require all users in this group to setup two-factor authentication"
|
||||
msgstr ""
|
||||
|
||||
|
|
|
@ -32,7 +32,7 @@ module QA
|
|||
element :labels_edit_button
|
||||
end
|
||||
|
||||
view 'app/views/shared/boards/_show.html.haml' do
|
||||
view 'app/assets/javascripts/boards/components/board_content.vue' do
|
||||
element :boards_list
|
||||
end
|
||||
|
||||
|
|
|
@ -31,6 +31,7 @@ RSpec.describe Groups::DependencyProxyAuthController do
|
|||
|
||||
context 'with valid JWT' do
|
||||
let_it_be(:user) { create(:user) }
|
||||
|
||||
let(:jwt) { build_jwt(user) }
|
||||
let(:token_header) { "Bearer #{jwt.encoded}" }
|
||||
|
||||
|
@ -65,6 +66,7 @@ RSpec.describe Groups::DependencyProxyAuthController do
|
|||
|
||||
context 'expired token' do
|
||||
let_it_be(:user) { create(:user) }
|
||||
|
||||
let(:jwt) { build_jwt(user, expire_time: Time.zone.now - 1.hour) }
|
||||
let(:token_header) { "Bearer #{jwt.encoded}" }
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@ RSpec.describe Groups::DependencyProxyForContainersController do
|
|||
include DependencyProxyHelpers
|
||||
|
||||
let_it_be(:user) { create(:user) }
|
||||
|
||||
let(:group) { create(:group) }
|
||||
let(:token_response) { { status: :success, token: 'abcd1234' } }
|
||||
let(:jwt) { build_jwt(user) }
|
||||
|
@ -102,6 +103,7 @@ RSpec.describe Groups::DependencyProxyForContainersController do
|
|||
|
||||
describe 'GET #manifest' do
|
||||
let_it_be(:manifest) { create(:dependency_proxy_manifest) }
|
||||
|
||||
let(:pull_response) { { status: :success, manifest: manifest } }
|
||||
|
||||
before do
|
||||
|
@ -182,6 +184,7 @@ RSpec.describe Groups::DependencyProxyForContainersController do
|
|||
|
||||
describe 'GET #blob' do
|
||||
let_it_be(:blob) { create(:dependency_proxy_blob) }
|
||||
|
||||
let(:blob_sha) { blob.file_name.sub('.gz', '') }
|
||||
let(:blob_response) { { status: :success, blob: blob } }
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@ RSpec.describe Groups::Registry::RepositoriesController do
|
|||
let_it_be(:user) { create(:user) }
|
||||
let_it_be(:guest) { create(:user) }
|
||||
let_it_be(:group, reload: true) { create(:group) }
|
||||
|
||||
let(:additional_parameters) { {} }
|
||||
|
||||
subject do
|
||||
|
|
|
@ -8,6 +8,7 @@ RSpec.describe Groups::VariablesController do
|
|||
let_it_be(:group) { create(:group) }
|
||||
let_it_be(:user) { create(:user) }
|
||||
let_it_be(:variable) { create(:ci_group_variable, group: group) }
|
||||
|
||||
let(:access_level) { :owner }
|
||||
|
||||
before do
|
||||
|
|
|
@ -33,7 +33,12 @@ describe('BoardContent', () => {
|
|||
});
|
||||
};
|
||||
|
||||
const createComponent = ({ state, props = {}, graphqlBoardListsEnabled = false } = {}) => {
|
||||
const createComponent = ({
|
||||
state,
|
||||
props = {},
|
||||
graphqlBoardListsEnabled = false,
|
||||
canAdminList = true,
|
||||
} = {}) => {
|
||||
const store = createStore({
|
||||
...defaultState,
|
||||
...state,
|
||||
|
@ -42,11 +47,11 @@ describe('BoardContent', () => {
|
|||
localVue,
|
||||
propsData: {
|
||||
lists: mockListsWithModel,
|
||||
canAdminList: true,
|
||||
disabled: false,
|
||||
...props,
|
||||
},
|
||||
provide: {
|
||||
canAdminList,
|
||||
glFeatures: { graphqlBoardLists: graphqlBoardListsEnabled },
|
||||
},
|
||||
store,
|
||||
|
@ -82,7 +87,7 @@ describe('BoardContent', () => {
|
|||
|
||||
describe('can admin list', () => {
|
||||
beforeEach(() => {
|
||||
createComponent({ graphqlBoardListsEnabled: true, props: { canAdminList: true } });
|
||||
createComponent({ graphqlBoardListsEnabled: true, canAdminList: true });
|
||||
});
|
||||
|
||||
it('renders draggable component', () => {
|
||||
|
@ -92,7 +97,7 @@ describe('BoardContent', () => {
|
|||
|
||||
describe('can not admin list', () => {
|
||||
beforeEach(() => {
|
||||
createComponent({ graphqlBoardListsEnabled: true, props: { canAdminList: false } });
|
||||
createComponent({ graphqlBoardListsEnabled: true, canAdminList: false });
|
||||
});
|
||||
|
||||
it('does not render draggable component', () => {
|
||||
|
|
|
@ -4,6 +4,7 @@ import { shallowMount, createLocalVue } from '@vue/test-utils';
|
|||
import axios from 'axios';
|
||||
import MockAdapter from 'axios-mock-adapter';
|
||||
import Vuex from 'vuex';
|
||||
import { extendedWrapper } from 'helpers/vue_test_utils_helper';
|
||||
import BoardSettingsSidebar from '~/boards/components/board_settings_sidebar.vue';
|
||||
import { inactiveId, LIST } from '~/boards/constants';
|
||||
import { createStore } from '~/boards/stores';
|
||||
|
@ -22,11 +23,18 @@ describe('BoardSettingsSidebar', () => {
|
|||
const labelColor = '#FFFF';
|
||||
const listId = 1;
|
||||
|
||||
const createComponent = () => {
|
||||
wrapper = shallowMount(BoardSettingsSidebar, {
|
||||
store,
|
||||
localVue,
|
||||
});
|
||||
const findRemoveButton = () => wrapper.findByTestId('remove-list');
|
||||
|
||||
const createComponent = ({ canAdminList = false } = {}) => {
|
||||
wrapper = extendedWrapper(
|
||||
shallowMount(BoardSettingsSidebar, {
|
||||
store,
|
||||
localVue,
|
||||
provide: {
|
||||
canAdminList,
|
||||
},
|
||||
}),
|
||||
);
|
||||
};
|
||||
const findLabel = () => wrapper.find(GlLabel);
|
||||
const findDrawer = () => wrapper.find(GlDrawer);
|
||||
|
@ -164,4 +172,29 @@ describe('BoardSettingsSidebar', () => {
|
|||
expect(findDrawer().exists()).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
it('does not render "Remove list" when user cannot admin the boards list', () => {
|
||||
createComponent();
|
||||
|
||||
expect(findRemoveButton().exists()).toBe(false);
|
||||
});
|
||||
|
||||
describe('when user can admin the boards list', () => {
|
||||
beforeEach(() => {
|
||||
store.state.activeId = listId;
|
||||
store.state.sidebarType = LIST;
|
||||
|
||||
boardsStore.addList({
|
||||
id: listId,
|
||||
label: { title: labelTitle, color: labelColor },
|
||||
list_type: 'label',
|
||||
});
|
||||
|
||||
createComponent({ canAdminList: true });
|
||||
});
|
||||
|
||||
it('renders "Remove list" button', () => {
|
||||
expect(findRemoveButton().exists()).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -64,6 +64,7 @@ RSpec.describe BoardsHelper do
|
|||
allow(helper).to receive(:current_user) { user }
|
||||
allow(helper).to receive(:can?).with(user, :create_non_backlog_issues, project_board).and_return(true)
|
||||
allow(helper).to receive(:can?).with(user, :admin_issue, project_board).and_return(true)
|
||||
allow(helper).to receive(:can?).with(user, :admin_issue_board_list, project).and_return(false)
|
||||
end
|
||||
|
||||
it 'returns a board_lists_path as lists_endpoint' do
|
||||
|
@ -86,6 +87,17 @@ RSpec.describe BoardsHelper do
|
|||
it 'returns the group id of a project' do
|
||||
expect(helper.board_data[:group_id]).to eq(project.group.id)
|
||||
end
|
||||
|
||||
context 'can_admin_list' do
|
||||
it 'returns can_admin_list as false by default' do
|
||||
expect(helper.board_data[:can_admin_list]).to eq('false')
|
||||
end
|
||||
it 'returns can_admin_list as true when user can admin the board' do
|
||||
allow(helper).to receive(:can?).with(user, :admin_issue_board_list, project).and_return(true)
|
||||
|
||||
expect(helper.board_data[:can_admin_list]).to eq('true')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'group board' do
|
||||
|
@ -96,6 +108,7 @@ RSpec.describe BoardsHelper do
|
|||
allow(helper).to receive(:current_user) { user }
|
||||
allow(helper).to receive(:can?).with(user, :create_non_backlog_issues, group_board).and_return(true)
|
||||
allow(helper).to receive(:can?).with(user, :admin_issue, group_board).and_return(true)
|
||||
allow(helper).to receive(:can?).with(user, :admin_issue_board_list, base_group).and_return(false)
|
||||
end
|
||||
|
||||
it 'returns correct path for base group' do
|
||||
|
@ -110,6 +123,17 @@ RSpec.describe BoardsHelper do
|
|||
it 'returns the group id' do
|
||||
expect(helper.board_data[:group_id]).to eq(base_group.id)
|
||||
end
|
||||
|
||||
context 'can_admin_list' do
|
||||
it 'returns can_admin_list as false by default' do
|
||||
expect(helper.board_data[:can_admin_list]).to eq('false')
|
||||
end
|
||||
it 'returns can_admin_list as true when user can admin the board' do
|
||||
allow(helper).to receive(:can?).with(user, :admin_issue_board_list, base_group).and_return(true)
|
||||
|
||||
expect(helper.board_data[:can_admin_list]).to eq('true')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
Loading…
Reference in a new issue