Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2022-02-16 15:14:05 +00:00
parent abed3501da
commit 5382b5cdc4
40 changed files with 495 additions and 139 deletions

View File

@ -1 +1 @@
ac32a7f07a26a15fb59ea897ee7150f98910903d
5d557a52c40e2641d6ea1b44b60a897d0e9401e7

View File

@ -140,8 +140,8 @@ export default {
return {
id: 'signup-settings-modal',
text: n__(
'ApplicationSettings|By making this change, you will automatically approve %d user with the pending approval status.',
'ApplicationSettings|By making this change, you will automatically approve %d users with the pending approval status.',
'ApplicationSettings|By making this change, you will automatically approve %d user who is pending approval.',
'ApplicationSettings|By making this change, you will automatically approve %d users who are pending approval.',
pendingUserCount,
),
actionPrimary: {
@ -157,7 +157,7 @@ export default {
actionCancel: {
text: __('Cancel'),
},
title: s__('ApplicationSettings|Approve users in the pending approval status?'),
title: s__('ApplicationSettings|Approve users who are pending approval?'),
};
},
},

View File

@ -425,7 +425,10 @@ module ApplicationSettingsHelper
:suggest_pipeline_enabled,
:user_email_lookup_limit,
:users_get_by_id_limit,
:users_get_by_id_limit_allowlist_raw
:users_get_by_id_limit_allowlist_raw,
:runner_token_expiration_interval,
:group_runner_token_expiration_interval,
:project_runner_token_expiration_interval
].tap do |settings|
settings << :deactivate_dormant_users unless Gitlab.com?
end

View File

@ -75,8 +75,6 @@
- if can?(current_user, :"destroy_#{issuable.to_ability_name}", @project)
= link_to 'Delete', polymorphic_path([@project, issuable], params: { destroy_confirm: true }), data: { confirm: _('%{issuableType} will be removed! Are you sure?') % { issuableType: issuable.human_class_name } }, method: :delete, class: 'btn gl-button btn-danger btn-danger-secondary gl-float-right'
= render_if_exists 'shared/issuable/remove_approver'
- if issuable.respond_to?(:issue_type)
= form.hidden_field :issue_type

View File

@ -1,8 +1,8 @@
---
name: api_caching_merge_requests
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/61067
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/330342
milestone: '13.12'
name: force_no_sharing_primary_model
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/76188
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/347286
milestone: '14.8'
type: development
group: group::source code
group: group::sharding
default_enabled: false

View File

@ -1,13 +1,13 @@
- name: "REST and GraphQL API Runner usage of `active` replaced by `paused`"
announcement_milestone: "14.8"
announcement_date: "2022-02-22"
removal_milestone: "15.0"
removal_date: "2022-05-22"
removal_milestone: "16.0"
removal_date: "2023-04-22"
breaking_change: true
reporter: pedropombeiro
body: |
Occurrences of the `active` identifier in the GitLab Runner REST and GraphQL API endpoints will be
renamed to `paused` in GitLab 15.0, namely:
renamed to `paused` in GitLab 16.0, namely:
- GraphQL API:
- the `CiRunner` property;
@ -22,8 +22,8 @@
- `GET /projects/:id/runners` / `POST /projects/:id/runners`
- `GET /groups/:id/runners`
The 15.0 release of the GitLab Runner will start using the `paused` property when registering runners, and therefore
will only be compatible with GitLab 15.0 and later. Until 15.0, GitLab will accept the deprecated `active` flag from
The 16.0 release of the GitLab Runner will start using the `paused` property when registering runners, and therefore
will only be compatible with GitLab 16.0 and later. Until 16.0, GitLab will accept the deprecated `active` flag from
existing runners.
stage: Verify
tiers: [Core, Premium, Ultimate]

View File

@ -1,12 +1,12 @@
- name: "REST API endpoint to list group runners no longer accepts `project_type` value for `type` argument"
announcement_milestone: "14.8"
announcement_date: "2022-02-22"
removal_milestone: "15.0"
removal_date: "2022-05-22"
removal_milestone: "16.0"
removal_date: "2023-04-22"
breaking_change: true
reporter: pedropombeiro
body: |
The `GET /groups/:id/runners?type=project_type` endpoint will be removed in GitLab 15.0. The endpoint always returned an empty collection.
The `GET /groups/:id/runners?type=project_type` endpoint will be removed in GitLab 16.0. The endpoint always returned an empty collection.
stage: Verify
tiers: [Core, Premium, Ultimate]
issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/351466

View File

@ -1,11 +1,11 @@
- name: "REST API Runner will not accept `status` filter values of `active` or `paused`"
announcement_milestone: "14.8" # The milestone when this feature was first announced as deprecated.
announcement_date: "2022-02-22"
removal_milestone: "15.0" # the milestone when this feature is planned to be removed
removal_date: "2022-05-22" # the date of the milestone release when this feature is planned to be removed
removal_milestone: "16.0"
removal_date: "2023-04-22"
breaking_change: true
body: | # Do not modify this line, instead modify the lines below.
The GitLab Runner REST endpoints will stop accepting `paused` or `active` as a status value in GitLab 15.0.
The GitLab Runner REST endpoints will stop accepting `paused` or `active` as a status value in GitLab 16.0.
A runner's status will only relate to runner contact status, such as: `online`, `offline`.
Status values `paused` or `active` will no longer be accepted and will be replaced by the `paused` query parameter.

View File

@ -0,0 +1,20 @@
# frozen_string_literal: true
class ScheduleFixIncorrectMaxSeatsUsed2 < Gitlab::Database::Migration[1.0]
MIGRATION = 'FixIncorrectMaxSeatsUsed'
TMP_IDX_NAME = 'tmp_gitlab_subscriptions_max_seats_used_migration_2'
disable_ddl_transaction!
def up
add_concurrent_index :gitlab_subscriptions, :id, where: "start_date < '2021-08-02' AND max_seats_used != 0 AND max_seats_used > seats_in_use AND max_seats_used > seats", name: TMP_IDX_NAME
return unless Gitlab.com?
migrate_in(1.hour, MIGRATION, ['batch_2_for_start_date_before_02_aug_2021'])
end
def down
remove_concurrent_index_by_name :gitlab_subscriptions, TMP_IDX_NAME
end
end

View File

@ -0,0 +1 @@
c075ee9d6efeae4b7a9b6e310f0c3d0bdd0ac6a58dc214427d4de9ae579db50d

View File

@ -28355,6 +28355,8 @@ CREATE UNIQUE INDEX term_agreements_unique_index ON term_agreements USING btree
CREATE INDEX tmp_gitlab_subscriptions_max_seats_used_migration ON gitlab_subscriptions USING btree (id) WHERE ((start_date >= '2021-08-02'::date) AND (start_date <= '2021-11-20'::date) AND (max_seats_used <> 0) AND (max_seats_used > seats_in_use) AND (max_seats_used > seats));
CREATE INDEX tmp_gitlab_subscriptions_max_seats_used_migration_2 ON gitlab_subscriptions USING btree (id) WHERE ((start_date < '2021-08-02'::date) AND (max_seats_used <> 0) AND (max_seats_used > seats_in_use) AND (max_seats_used > seats));
CREATE INDEX tmp_idx_vulnerability_occurrences_on_id_where_report_type_7_99 ON vulnerability_occurrences USING btree (id) WHERE (report_type = ANY (ARRAY[7, 99]));
CREATE INDEX tmp_index_container_repositories_on_id_migration_state ON container_repositories USING btree (id, migration_state);

View File

@ -98,6 +98,15 @@ This section is for links to information elsewhere in the GitLab documentation.
- [Common Geo errors](../geo/replication/troubleshooting.md#fixing-common-errors).
- Mismatch in `pg_dump` and `psql` versions:
```plaintext
Dumping PostgreSQL database gitlabhq_production ... pg_dump: error: server version: 13.3; pg_dump version: 14.2
pg_dump: error: aborting because of server version mismatch
```
To fix this, see [Backup and restore a non-packaged PostgreSQL database](https://docs.gitlab.com/omnibus/settings/database.html#backup-and-restore-a-non-packaged-postgresql-database).
## Support topics
### Database deadlocks

View File

@ -679,7 +679,8 @@ Example response:
```json
{
"id": 12345,
"token": "6337ff461c94fd3fa32ba3b1ff4125"
"token": "6337ff461c94fd3fa32ba3b1ff4125",
"token_expires_at": "2021-09-27T21:05:03.203Z"
}
```
@ -819,6 +820,7 @@ Example response:
```json
{
"token": "6337ff461c94fd3fa32ba3b1ff4125"
"token": "6337ff461c94fd3fa32ba3b1ff4125",
"token_expires_at": "2021-09-27T21:05:03.203Z"
}
```

View File

@ -142,8 +142,14 @@ parent project when the pipeline runs, even before merge. As a reviewer, careful
check the changes in the merge request before triggering the pipeline. GitLab shows
a warning that you must accept before you can trigger the pipeline.
Parent project members with at least the Developer role
can create pipelines in the parent project for merge requests from a forked project:
Prerequisites:
- You must be a member of the parent project and have at least the [Developer role](../../user/permissions.md).
- The fork project must be [visible](../../public_access/public_access.md) to the
user running the pipeline. Otherwise, the **Pipelines** tab does not display
in the merge request.
To run a pipeline in the parent project for a merge request from a fork project:
1. In the merge request, go to the **Pipelines** tab.
1. Select **Run pipeline**. You must accept the warning, or the pipeline does not run.

View File

@ -107,9 +107,9 @@ Default client accepts two parameters: `resolvers` and `config`.
### Multiple client queries for the same object
If you are make multiple queries to the same Apollo client object you might encounter the following error: "Store error: the application attempted to write an object with no provided ID but the store already contains an ID of SomeEntity". [This error only should occur when you have made a query with an ID field for a portion, then made another that returns what would be the same object, but is missing the ID field.](https://github.com/apollographql/apollo-client/issues/2510#issue-271829009)
If you are making multiple queries to the same Apollo client object you might encounter the following error: `Cache data may be lost when replacing the someProperty field of a Query object. To address this problem, either ensure all objects of SomeEntityhave an id or a custom merge function`. We are already checking `ID` presence for every GraphQL type that has an `ID`, so this shouldn't be the case. Most likely, the `SomeEntity` type doesn't have an `ID` property, and to fix this warning we need to define a custom merge function.
This is being tracked in [this issue](https://gitlab.com/gitlab-org/gitlab/-/issues/326101) and the documentation will be updated when this issue is resolved.
We have some client-wide types with `merge: true` defined in the default client as [typePolicies](https://gitlab.com/gitlab-org/gitlab/-/blob/master/app/assets/javascripts/lib/graphql.js) (this means that Apollo will merge existing and incoming responses in the case of subsequent queries). Please consider adding `SomeEntity` there or defining a custom merge function for it.
## GraphQL Queries
@ -667,9 +667,7 @@ apollo: {
```
When we want to move to the next page, we use an Apollo `fetchMore` method, passing a
new cursor (and, optionally, new variables) there. In the `updateQuery` hook, we have
to return a result we want to see in the Apollo cache after fetching the next page.
[`Immer`s `produce`](#immutability-and-cache-updates)-function can help us with the immutability here:
new cursor (and, optionally, new variables) there.
```javascript
fetchNextPage(endCursor) {
@ -679,24 +677,114 @@ fetchNextPage(endCursor) {
first: 10,
after: endCursor,
},
updateQuery(previousResult, { fetchMoreResult }) {
// Here we can implement the logic of adding new designs to existing ones
// (for example, if we use infinite scroll) or replacing old result
// with the new one if we use numbered pages
const { designs: previousDesigns } = previousResult.project.issue.designCollection;
const { designs: newDesigns } = fetchMoreResult.project.issue.designCollection
return produce(previousResult, draftData => {
// `produce` gives us a working copy, `draftData`, that we can modify
// as we please and from it will produce the next immutable result for us
draftData.project.issue.designCollection.designs = [...previousDesigns, ...newDesigns];
});
},
});
}
```
##### Defining field merge policy
We would also need to define a field policy to specify how do we want to merge the existing results with the incoming results. For example, if we have `Previous/Next` buttons, it makes sense to replace the existing result with the incoming one:
```javascript
const apolloProvider = new VueApollo({
defaultClient: createDefaultClient(
{},
{
cacheConfig: {
typePolicies: {
DesignCollection: {
fields: {
designs: {
merge(existing, incoming) {
if (!incoming) return existing;
if (!existing) return incoming;
// We want to save only incoming nodes and replace existing ones
return incoming
}
}
}
}
}
},
},
),
});
```
When we have an infinite scroll, it would make sense to add the incoming `designs` nodes to existing ones instead of replacing. In this case, merge function would be slightly different:
```javascript
const apolloProvider = new VueApollo({
defaultClient: createDefaultClient(
{},
{
cacheConfig: {
typePolicies: {
DesignCollection: {
fields: {
designs: {
merge(existing, incoming) {
if (!incoming) return existing;
if (!existing) return incoming;
const { nodes, ...rest } = incoming;
// We only need to merge the nodes array.
// The rest of the fields (pagination) should always be overwritten by incoming
let result = rest;
result.nodes = [...existing.nodes, ...nodes];
return result;
}
}
}
}
}
},
},
),
});
```
`apollo-client` [provides](https://github.com/apollographql/apollo-client/blob/212b1e686359a3489b48d7e5d38a256312f81fde/src/utilities/policies/pagination.ts)
a few field policies to be used with paginated queries. Here's another way to achieve infinite
scroll pagination with the `concatPagination` policy:
```javascript
import { concatPagination } from '@apollo/client/utilities';
import Vue from 'vue';
import VueApollo from 'vue-apollo';
import createDefaultClient from '~/lib/graphql';
Vue.use(VueApollo);
export default new VueApollo({
defaultClient: createDefaultClient(
{},
{
cacheConfig: {
typePolicies: {
Project: {
fields: {
dastSiteProfiles: {
keyArgs: ['fullPath'], // You might need to set the keyArgs option to enforce the cache's integrity
},
},
},
DastSiteProfileConnection: {
fields: {
nodes: concatPagination(),
},
},
},
},
},
),
});
```
This is similar to the `DesignCollection` example above as new page results are appended to the
previous ones.
#### Using a recursive query in components
When it is necessary to fetch all paginated data initially an Apollo query can do the trick for us.
@ -816,7 +904,7 @@ const data = store.readQuery({
});
```
Read more about the `@connection` directive in [Apollo's documentation](https://www.apollographql.com/docs/react/v2/caching/cache-interaction/#the-connection-directive).
Read more about the `@connection` directive in [Apollo's documentation](https://www.apollographql.com/docs/react/caching/advanced-topics/#the-connection-directive).
### Managing performance
@ -1017,22 +1105,13 @@ apollo: {
issuableId: convertToGraphQLId(this.issuableClass, this.issuableId),
};
},
// Describe how subscription should update the query
updateQuery(prev, { subscriptionData }) {
if (prev && subscriptionData?.data?.issuableAssigneesUpdated) {
const data = produce(prev, (draftData) => {
draftData.workspace.issuable.assignees.nodes =
subscriptionData.data.issuableAssigneesUpdated.assignees.nodes;
});
return data;
}
return prev;
},
},
},
},
```
We would need also to define a field policy similarly like we do it for the [paginated queries](#defining-field-merge-policy)
### Best Practices
#### When to use (and not use) `update` hook in mutations
@ -1808,6 +1887,16 @@ relative to `app/graphql/queries` folder: for example, if we need a
`app/graphql/queries/repository/files.query.graphql` query, the path is
`repository/files`.
## Troubleshooting
### Mocked client returns empty objects instead of mock response
If your unit test is failing because response contains empty objects instead of mock data, you would need to add `__typename` field to the mocked response. This happens because mocked client (unlike the real one) does not populate the response with typenames and in some cases we need to do it manually so the client is able to recognize a GraphQL type.
### Warning about losing cache data
Sometimes you can see a warning in the console: `Cache data may be lost when replacing the someProperty field of a Query object. To address this problem, either ensure all objects of SomeEntityhave an id or a custom merge function`. Please check section about [multiple queries](#multiple-client-queries-for-the-same-object) to resolve an issue.
```yaml
- current_route_path = request.fullpath.match(/-\/tree\/[^\/]+\/(.+$)/).to_a[1]
- add_page_startup_graphql_call('repository/path_last_commit', { projectPath: @project.full_path, ref: current_ref, path: current_route_path || "" })

View File

@ -1024,12 +1024,12 @@ The `instanceStatisticsMeasurements` GraphQL node has been renamed to `usageTren
### REST API Runner will not accept `status` filter values of `active` or `paused`
WARNING:
This feature will be changed or removed in 15.0
This feature will be changed or removed in 16.0
as a [breaking change](https://docs.gitlab.com/ee/development/contributing/#breaking-changes).
Before updating GitLab, review the details carefully to determine if you need to make any
changes to your code, settings, or workflow.
The GitLab Runner REST endpoints will stop accepting `paused` or `active` as a status value in GitLab 15.0.
The GitLab Runner REST endpoints will stop accepting `paused` or `active` as a status value in GitLab 16.0.
A runner's status will only relate to runner contact status, such as: `online`, `offline`.
Status values `paused` or `active` will no longer be accepted and will be replaced by the `paused` query parameter.
@ -1037,30 +1037,30 @@ Status values `paused` or `active` will no longer be accepted and will be replac
When checking for paused runners, API users are advised to specify `paused=true` as the query parameter.
When checking for active runners, specify `paused=false`.
**Planned removal milestone: 15.0 (2022-05-22)**
**Planned removal milestone: 16.0 (2023-04-22)**
### REST API endpoint to list group runners no longer accepts `project_type` value for `type` argument
WARNING:
This feature will be changed or removed in 15.0
This feature will be changed or removed in 16.0
as a [breaking change](https://docs.gitlab.com/ee/development/contributing/#breaking-changes).
Before updating GitLab, review the details carefully to determine if you need to make any
changes to your code, settings, or workflow.
The `GET /groups/:id/runners?type=project_type` endpoint will be removed in GitLab 15.0. The endpoint always returned an empty collection.
The `GET /groups/:id/runners?type=project_type` endpoint will be removed in GitLab 16.0. The endpoint always returned an empty collection.
**Planned removal milestone: 15.0 (2022-05-22)**
**Planned removal milestone: 16.0 (2023-04-22)**
### REST and GraphQL API Runner usage of `active` replaced by `paused`
WARNING:
This feature will be changed or removed in 15.0
This feature will be changed or removed in 16.0
as a [breaking change](https://docs.gitlab.com/ee/development/contributing/#breaking-changes).
Before updating GitLab, review the details carefully to determine if you need to make any
changes to your code, settings, or workflow.
Occurrences of the `active` identifier in the GitLab Runner REST and GraphQL API endpoints will be
renamed to `paused` in GitLab 15.0, namely:
renamed to `paused` in GitLab 16.0, namely:
- GraphQL API:
- the `CiRunner` property;
@ -1075,11 +1075,11 @@ renamed to `paused` in GitLab 15.0, namely:
- `GET /projects/:id/runners` / `POST /projects/:id/runners`
- `GET /groups/:id/runners`
The 15.0 release of the GitLab Runner will start using the `paused` property when registering runners, and therefore
will only be compatible with GitLab 15.0 and later. Until 15.0, GitLab will accept the deprecated `active` flag from
The 16.0 release of the GitLab Runner will start using the `paused` property when registering runners, and therefore
will only be compatible with GitLab 16.0 and later. Until 16.0, GitLab will accept the deprecated `active` flag from
existing runners.
**Planned removal milestone: 15.0 (2022-05-22)**
**Planned removal milestone: 16.0 (2023-04-22)**
### Reminder: support for NFS repository storage

View File

@ -158,6 +158,7 @@ information to the cluster automatically without downtime.
## View your Agents
> The version of installed `agentk` shown on the Agent tab [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/340882) in GitLab 14.8.
If you have at least the Developer role, you can access the Agent's
configuration repository and view the Agent's list:
@ -168,6 +169,7 @@ On this page, you can view:
- All the registered Agents for the current project.
- The connection status.
- The version of `agentk` installed on your cluster.
- The path to each Agent's configuration file.
Furthermore, if you select one of the Agents on your list, you can view its

View File

@ -72,6 +72,24 @@ You can also filter epics in the Roadmap view by the epics':
Roadmaps can also be [visualized inside an epic](../epics/index.md#roadmap-in-epics).
### Roadmap settings
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/345158) in GitLab 14.8 [with a flag](../../../administration/feature_flags.md) named `roadmap_settings`. Enabled by default.
FLAG:
On self-managed GitLab, by default this feature is not available. To make it available, ask an administrator to [enable the feature flag](../../../administration/feature_flags.md) named `roadmap_settings`.
On GitLab.com, this feature is available but can be configured by GitLab.com administrators only.
When you enable the roadmap settings sidebar, you can use it to refine epics shown in the roadmap.
You can configure the following:
- Select date range.
- Show all, open, or closed epics.
- Turn progress tracking on or off and select whether it uses issue weights or counts.
The progress tracking setting is not saved in user preferences but is saved or shared using URL parameters.
## Timeline duration
> - Introduced in GitLab 11.0.

View File

@ -120,10 +120,6 @@ To remove all the time spent at once, use the `/remove_time_spent` [quick action
You can view a breakdown of time spent on an issue or merge request.
Prerequisites:
- You must have at least the Reporter role for a project.
To view a time tracking report:
1. Go to an issue or a merge request.

View File

@ -143,7 +143,7 @@ module API
authenticate_update_runner!(runner)
runner.reset_token!
present runner.token, with: Entities::Ci::ResetTokenResult
present runner.token_with_expiration, with: Entities::Ci::ResetTokenResult
end
end
@ -249,7 +249,7 @@ module API
authorize! :update_runners_registration_token
ApplicationSetting.current.reset_runners_registration_token!
present ApplicationSetting.current_without_cache.runners_registration_token, with: Entities::Ci::ResetTokenResult
present ApplicationSetting.current_without_cache.runners_registration_token_with_expiration, with: Entities::Ci::ResetTokenResult
end
end
@ -267,7 +267,7 @@ module API
authorize! :update_runners_registration_token, project
project.reset_runners_token!
present project.runners_token, with: Entities::Ci::ResetTokenResult
present project.runners_token_with_expiration, with: Entities::Ci::ResetTokenResult
end
end
@ -285,7 +285,7 @@ module API
authorize! :update_runners_registration_token, group
group.reset_runners_token!
present group.runners_token, with: Entities::Ci::ResetTokenResult
present group.runners_token_with_expiration, with: Entities::Ci::ResetTokenResult
end
end

View File

@ -4,7 +4,8 @@ module API
module Entities
module Ci
class ResetTokenResult < Grape::Entity
expose(:token) {|object| object}
expose(:token)
expose(:token_expires_at, if: -> (object, options) { object.expirable? })
end
end
end

View File

@ -4,7 +4,7 @@ module API
module Entities
module Ci
class RunnerRegistrationDetails < Grape::Entity
expose :id, :token
expose :id, :token, :token_expires_at
end
end
end

View File

@ -207,11 +207,7 @@ module API
options = serializer_options_for(merge_requests).merge(project: user_project)
options[:project] = user_project
if Feature.enabled?(:api_caching_merge_requests, user_project, type: :development, default_enabled: :yaml)
present_cached merge_requests, expires_in: 2.days, **options
else
present merge_requests, options
end
present_cached merge_requests, expires_in: 2.days, **options
end
desc 'Create a merge request' do

View File

@ -178,6 +178,9 @@ module API
optional :user_deactivation_emails_enabled, type: Boolean, desc: 'Send emails to users upon account deactivation'
optional :suggest_pipeline_enabled, type: Boolean, desc: 'Enable pipeline suggestion banner'
optional :users_get_by_id_limit, type: Integer, desc: "Maximum number of calls to the /users/:id API per 10 minutes per user. Set to 0 for unlimited requests."
optional :runner_token_expiration_interval, type: Integer, desc: 'Token expiration interval for shared runners, in seconds'
optional :group_runner_token_expiration_interval, type: Integer, desc: 'Token expiration interval for group runners, in seconds'
optional :project_runner_token_expiration_interval, type: Integer, desc: 'Token expiration interval for project runners, in seconds'
ApplicationSetting::SUPPORTED_KEY_TYPES.each do |type|
optional :"#{type}_key_restriction",

View File

@ -3,11 +3,24 @@
module Gitlab
module Audit
class CiRunnerTokenAuthor < Gitlab::Audit::NullAuthor
def initialize(token:, entity_type:, entity_path:)
super(id: -1, name: "Registration token: #{token}")
# Represents a CI Runner token (registration or authentication)
#
# @param [AuditEvent] audit_event event representing a runner registration/un-registration operation
def initialize(audit_event)
if audit_event.details.include?(:runner_authentication_token)
token = audit_event.details[:runner_authentication_token]
name = "Authentication token: #{token}"
elsif audit_event.details.include?(:runner_registration_token)
token = audit_event.details[:runner_registration_token]
name = "Registration token: #{token}"
else
raise ArgumentError, 'Runner token missing'
end
@entity_type = entity_type
@entity_path = entity_path
super(id: -1, name: name)
@entity_type = audit_event.entity_type
@entity_path = audit_event.entity_path
end
def full_path

View File

@ -18,12 +18,8 @@ module Gitlab
def self.for(id, audit_event)
name = audit_event[:author_name] || audit_event.details[:author_name]
if audit_event.details.include?(:runner_registration_token)
::Gitlab::Audit::CiRunnerTokenAuthor.new(
token: audit_event.details[:runner_registration_token],
entity_type: audit_event.entity_type || audit_event.details[:entity_type],
entity_path: audit_event.entity_path || audit_event.details[:entity_path]
)
if audit_event.target_type == ::Ci::Runner.name
Gitlab::Audit::CiRunnerTokenAuthor.new(audit_event)
elsif id == -1
Gitlab::Audit::UnauthenticatedAuthor.new(name: name)
else

View File

@ -4,7 +4,7 @@ module Gitlab
module BackgroundMigration
# rubocop: disable Style/Documentation
class FixIncorrectMaxSeatsUsed
def perform
def perform(batch = nil)
end
end
end

View File

@ -74,11 +74,24 @@ module Gitlab
# With connection re-use the primary connection can be overwritten
# to be used from different model
def primary_connection_specification_name
(@primary_model || @model).connection_specification_name
primary_model_or_model_if_enabled.connection_specification_name
end
def primary_db_config
(@primary_model || @model).connection_db_config
def primary_model_or_model_if_enabled
if force_no_sharing_primary_model?
@model
else
@primary_model || @model
end
end
def force_no_sharing_primary_model?
return false unless @primary_model # Doesn't matter since we don't have an overriding primary model
return false unless ::Gitlab::SafeRequestStore.active?
::Gitlab::SafeRequestStore.fetch(:force_no_sharing_primary_model) do
::Feature::FlipperFeature.table_exists? && ::Feature.enabled?(:force_no_sharing_primary_model, default_enabled: :yaml)
end
end
def replica_db_config

View File

@ -4243,12 +4243,15 @@ msgstr ""
msgid "ApplicationSettings|Approve users in the pending approval status?"
msgstr ""
msgid "ApplicationSettings|By making this change, you will automatically approve %d user with the pending approval status."
msgid_plural "ApplicationSettings|By making this change, you will automatically approve %d users with the pending approval status."
msgid "ApplicationSettings|Approve users who are pending approval?"
msgstr ""
msgid "ApplicationSettings|By making this change, you will automatically approve %d user who is pending approval."
msgid_plural "ApplicationSettings|By making this change, you will automatically approve %d users who are pending approval."
msgstr[0] ""
msgstr[1] ""
msgid "ApplicationSettings|By making this change, you will automatically approve all users in pending approval status."
msgid "ApplicationSettings|By making this change, you will automatically approve all users who are pending approval."
msgstr ""
msgid "ApplicationSettings|Denied domains for sign-ups"
@ -30151,9 +30154,6 @@ msgstr ""
msgid "Remove all or specific reviewer(s)"
msgstr ""
msgid "Remove approver"
msgstr ""
msgid "Remove approvers"
msgstr ""

View File

@ -3,18 +3,50 @@
require 'spec_helper'
RSpec.describe Gitlab::Audit::CiRunnerTokenAuthor do
describe '#initialize' do
it 'sets correct attributes' do
expect(described_class.new(token: 'abc1234567', entity_type: 'Project', entity_path: 'd/e'))
.to have_attributes(id: -1, name: 'Registration token: abc1234567')
describe '.initialize' do
subject { described_class.new(audit_event) }
let(:details) { }
let(:audit_event) { instance_double(AuditEvent, details: details, entity_type: 'Project', entity_path: 'd/e') }
context 'with runner_authentication_token' do
let(:details) do
{ runner_authentication_token: 'abc1234567' }
end
it 'returns CiRunnerTokenAuthor with expected attributes' do
is_expected.to have_attributes(id: -1, name: 'Authentication token: abc1234567')
end
end
context 'with runner_registration_token' do
let(:details) do
{ runner_registration_token: 'abc1234567' }
end
it 'returns CiRunnerTokenAuthor with expected attributes' do
is_expected.to have_attributes(id: -1, name: 'Registration token: abc1234567')
end
end
context 'with runner token missing' do
let(:details) do
{}
end
it 'raises ArgumentError' do
expect { subject }.to raise_error ArgumentError, 'Runner token missing'
end
end
end
describe '#full_path' do
subject { author.full_path }
let(:author) { described_class.new(audit_event) }
context 'with instance registration token' do
let(:author) { described_class.new(token: 'abc1234567', entity_type: 'User', entity_path: nil) }
let(:audit_event) { instance_double(AuditEvent, details: { runner_registration_token: 'abc1234567' }, entity_type: 'User', entity_path: nil) }
it 'returns correct url' do
is_expected.to eq('/admin/runners')
@ -22,7 +54,7 @@ RSpec.describe Gitlab::Audit::CiRunnerTokenAuthor do
end
context 'with group registration token' do
let(:author) { described_class.new(token: 'abc1234567', entity_type: 'Group', entity_path: 'a/b') }
let(:audit_event) { instance_double(AuditEvent, details: { runner_registration_token: 'abc1234567' }, entity_type: 'Group', entity_path: 'a/b') }
it 'returns correct url' do
expect(::Gitlab::Routing.url_helpers).to receive(:group_settings_ci_cd_path)
@ -35,7 +67,7 @@ RSpec.describe Gitlab::Audit::CiRunnerTokenAuthor do
end
context 'with project registration token' do
let(:author) { described_class.new(token: 'abc1234567', entity_type: 'Project', entity_path: project.full_path) }
let(:audit_event) { instance_double(AuditEvent, details: { runner_registration_token: 'abc1234567' }, entity_type: 'Project', entity_path: project.full_path) }
let(:project) { create(:project) }
it 'returns correct url' do

View File

@ -11,6 +11,7 @@ RSpec.describe Gitlab::Audit::NullAuthor do
it 'returns an DeletedAuthor' do
allow(audit_event).to receive(:[]).with(:author_name).and_return('Old Hat')
allow(audit_event).to receive(:details).and_return({})
allow(audit_event).to receive(:target_type)
expect(subject.for(666, audit_event)).to be_a(Gitlab::Audit::DeletedAuthor)
end
@ -18,6 +19,7 @@ RSpec.describe Gitlab::Audit::NullAuthor do
it 'returns an UnauthenticatedAuthor when id equals -1', :aggregate_failures do
allow(audit_event).to receive(:[]).with(:author_name).and_return('Frank')
allow(audit_event).to receive(:details).and_return({})
allow(audit_event).to receive(:target_type)
expect(subject.for(-1, audit_event)).to be_a(Gitlab::Audit::UnauthenticatedAuthor)
expect(subject.for(-1, audit_event)).to have_attributes(id: -1, name: 'Frank')
@ -27,12 +29,25 @@ RSpec.describe Gitlab::Audit::NullAuthor do
allow(audit_event).to receive(:[]).with(:author_name).and_return('cde456')
allow(audit_event).to receive(:entity_type).and_return('User')
allow(audit_event).to receive(:entity_path).and_return('/a/b')
allow(audit_event).to receive(:target_type).and_return(::Ci::Runner.name)
allow(audit_event).to receive(:details)
.and_return({ runner_registration_token: 'cde456', author_name: 'cde456', entity_type: 'User', entity_path: '/a/b' })
expect(subject.for(-1, audit_event)).to be_a(Gitlab::Audit::CiRunnerTokenAuthor)
expect(subject.for(-1, audit_event)).to have_attributes(id: -1, name: 'Registration token: cde456')
end
it 'returns a CiRunnerTokenAuthor when details contain runner authentication token', :aggregate_failures do
allow(audit_event).to receive(:[]).with(:author_name).and_return('cde456')
allow(audit_event).to receive(:entity_type).and_return('User')
allow(audit_event).to receive(:entity_path).and_return('/a/b')
allow(audit_event).to receive(:target_type).and_return(::Ci::Runner.name)
allow(audit_event).to receive(:details)
.and_return({ runner_authentication_token: 'cde456', author_name: 'cde456', entity_type: 'User', entity_path: '/a/b' })
expect(subject.for(-1, audit_event)).to be_a(Gitlab::Audit::CiRunnerTokenAuthor)
expect(subject.for(-1, audit_event)).to have_attributes(id: -1, name: 'Authentication token: cde456')
end
end
describe '#current_sign_in_ip' do

View File

@ -2,11 +2,18 @@
require 'spec_helper'
RSpec.describe Gitlab::Database::LoadBalancing::Configuration do
RSpec.describe Gitlab::Database::LoadBalancing::Configuration, :request_store do
let(:configuration_hash) { {} }
let(:db_config) { ActiveRecord::DatabaseConfigurations::HashConfig.new('test', 'ci', configuration_hash) }
let(:model) { double(:model, connection_db_config: db_config) }
before do
# It's confusing to think about these specs with this enabled by default so
# we make it disabled by default and just write the specific spec for when
# it's enabled
stub_feature_flags(force_no_sharing_primary_model: false)
end
describe '.for_model' do
context 'when load balancing is not configured' do
it 'uses the default settings' do
@ -233,11 +240,23 @@ RSpec.describe Gitlab::Database::LoadBalancing::Configuration do
end
context 'when GITLAB_LOAD_BALANCING_REUSE_PRIMARY_ci=main' do
it 'the primary connection uses main connection' do
before do
stub_env('GITLAB_LOAD_BALANCING_REUSE_PRIMARY_ci', 'main')
end
it 'the primary connection uses main connection' do
expect(config.primary_connection_specification_name).to eq('ActiveRecord::Base')
end
context 'when force_no_sharing_primary_model feature flag is enabled' do
before do
stub_feature_flags(force_no_sharing_primary_model: true)
end
it 'the primary connection uses ci connection' do
expect(config.primary_connection_specification_name).to eq('Ci::ApplicationRecord')
end
end
end
context 'when GITLAB_LOAD_BALANCING_REUSE_PRIMARY_ci=unknown' do

View File

@ -130,6 +130,7 @@ RSpec.describe Gitlab::Database::LoadBalancing::Setup do
env_GITLAB_LOAD_BALANCING_REUSE_PRIMARY_ci: nil,
request_store_active: false,
ff_use_model_load_balancing: nil,
ff_force_no_sharing_primary_model: false,
expectations: {
main: { read: 'main_replica', write: 'main' },
ci: { read: 'ci_replica', write: 'ci' }
@ -140,6 +141,7 @@ RSpec.describe Gitlab::Database::LoadBalancing::Setup do
env_GITLAB_LOAD_BALANCING_REUSE_PRIMARY_ci: 'main',
request_store_active: false,
ff_use_model_load_balancing: nil,
ff_force_no_sharing_primary_model: false,
expectations: {
main: { read: 'main_replica', write: 'main' },
ci: { read: 'ci_replica', write: 'main' }
@ -150,6 +152,7 @@ RSpec.describe Gitlab::Database::LoadBalancing::Setup do
env_GITLAB_LOAD_BALANCING_REUSE_PRIMARY_ci: nil,
request_store_active: false,
ff_use_model_load_balancing: nil,
ff_force_no_sharing_primary_model: false,
expectations: {
main: { read: 'main_replica', write: 'main' },
ci: { read: 'main_replica', write: 'main' }
@ -160,60 +163,77 @@ RSpec.describe Gitlab::Database::LoadBalancing::Setup do
env_GITLAB_LOAD_BALANCING_REUSE_PRIMARY_ci: 'main',
request_store_active: false,
ff_use_model_load_balancing: nil,
ff_force_no_sharing_primary_model: false,
expectations: {
main: { read: 'main_replica', write: 'main' },
ci: { read: 'main_replica', write: 'main' }
}
},
"with FF disabled without RequestStore it uses main" => {
"with FF use_model_load_balancing disabled without RequestStore it uses main" => {
env_GITLAB_USE_MODEL_LOAD_BALANCING: nil,
env_GITLAB_LOAD_BALANCING_REUSE_PRIMARY_ci: nil,
request_store_active: false,
ff_use_model_load_balancing: false,
ff_force_no_sharing_primary_model: false,
expectations: {
main: { read: 'main_replica', write: 'main' },
ci: { read: 'main_replica', write: 'main' }
}
},
"with FF enabled without RequestStore sticking of FF does not work, so it fallbacks to use main" => {
"with FF use_model_load_balancing enabled without RequestStore sticking of FF does not work, so it fallbacks to use main" => {
env_GITLAB_USE_MODEL_LOAD_BALANCING: nil,
env_GITLAB_LOAD_BALANCING_REUSE_PRIMARY_ci: nil,
request_store_active: false,
ff_use_model_load_balancing: true,
ff_force_no_sharing_primary_model: false,
expectations: {
main: { read: 'main_replica', write: 'main' },
ci: { read: 'main_replica', write: 'main' }
}
},
"with FF disabled with RequestStore it uses main" => {
"with FF use_model_load_balancing disabled with RequestStore it uses main" => {
env_GITLAB_USE_MODEL_LOAD_BALANCING: nil,
env_GITLAB_LOAD_BALANCING_REUSE_PRIMARY_ci: nil,
request_store_active: true,
ff_use_model_load_balancing: false,
ff_force_no_sharing_primary_model: false,
expectations: {
main: { read: 'main_replica', write: 'main' },
ci: { read: 'main_replica', write: 'main' }
}
},
"with FF enabled with RequestStore it sticks FF and uses CI connection" => {
"with FF use_model_load_balancing enabled with RequestStore it sticks FF and uses CI connection" => {
env_GITLAB_USE_MODEL_LOAD_BALANCING: nil,
env_GITLAB_LOAD_BALANCING_REUSE_PRIMARY_ci: nil,
request_store_active: true,
ff_use_model_load_balancing: true,
ff_force_no_sharing_primary_model: false,
expectations: {
main: { read: 'main_replica', write: 'main' },
ci: { read: 'ci_replica', write: 'ci' }
}
},
"with re-use and FF enabled with RequestStore it sticks FF and uses CI connection for reads" => {
"with re-use and ff_use_model_load_balancing enabled and FF force_no_sharing_primary_model disabled with RequestStore it sticks FF and uses CI connection for reads" => {
env_GITLAB_USE_MODEL_LOAD_BALANCING: nil,
env_GITLAB_LOAD_BALANCING_REUSE_PRIMARY_ci: 'main',
request_store_active: true,
ff_use_model_load_balancing: true,
ff_force_no_sharing_primary_model: false,
expectations: {
main: { read: 'main_replica', write: 'main' },
ci: { read: 'ci_replica', write: 'main' }
}
},
"with re-use and ff_use_model_load_balancing enabled and FF force_no_sharing_primary_model enabled with RequestStore it sticks FF and uses CI connection for reads" => {
env_GITLAB_USE_MODEL_LOAD_BALANCING: nil,
env_GITLAB_LOAD_BALANCING_REUSE_PRIMARY_ci: 'main',
request_store_active: true,
ff_use_model_load_balancing: true,
ff_force_no_sharing_primary_model: true,
expectations: {
main: { read: 'main_replica', write: 'main' },
ci: { read: 'ci_replica', write: 'ci' }
}
}
}
end
@ -243,6 +263,7 @@ RSpec.describe Gitlab::Database::LoadBalancing::Setup do
around do |example|
if request_store_active
Gitlab::WithRequestStore.with_request_store do
stub_feature_flags(force_no_sharing_primary_model: ff_force_no_sharing_primary_model)
RequestStore.clear!
example.run

View File

@ -0,0 +1,34 @@
# frozen_string_literal: true
require 'spec_helper'
require_migration!
RSpec.describe ScheduleFixIncorrectMaxSeatsUsed2, :migration do
let(:migration_name) { described_class::MIGRATION.to_s.demodulize }
describe '#up' do
it 'schedules a job on Gitlab.com' do
allow(Gitlab).to receive(:com?).and_return(true)
Sidekiq::Testing.fake! do
freeze_time do
migrate!
expect(migration_name).to be_scheduled_delayed_migration(1.hour, 'batch_2_for_start_date_before_02_aug_2021')
expect(BackgroundMigrationWorker.jobs.size).to eq(1)
end
end
end
it 'does not schedule any jobs when not Gitlab.com' do
allow(Gitlab).to receive(:com?).and_return(false)
Sidekiq::Testing.fake! do
migrate!
expect(migration_name).not_to be_scheduled_delayed_migration
expect(BackgroundMigrationWorker.jobs.size).to eq(0)
end
end
end
end

View File

@ -97,8 +97,8 @@ RSpec.describe AuditEvent do
describe '#author' do
subject { audit_event.author }
context "when a runner_registration_token's present" do
let(:audit_event) { build(:project_audit_event, details: { target_id: 678 }) }
context "when the target type is not Ci::Runner" do
let(:audit_event) { build(:project_audit_event, target_id: 678) }
it 'returns a NullAuthor' do
expect(::Gitlab::Audit::NullAuthor).to receive(:for)
@ -109,12 +109,12 @@ RSpec.describe AuditEvent do
end
end
context "when a runner_registration_token's present" do
let(:audit_event) { build(:project_audit_event, details: { target_id: 678, runner_registration_token: 'abc123' }) }
context 'when the target type is Ci::Runner and details contain runner_registration_token' do
let(:audit_event) { build(:project_audit_event, target_type: ::Ci::Runner.name, target_id: 678, details: { runner_registration_token: 'abc123' }) }
it 'returns a CiRunnerTokenAuthor' do
expect(::Gitlab::Audit::CiRunnerTokenAuthor).to receive(:new)
.with({ token: 'abc123', entity_type: 'Project', entity_path: audit_event.entity_path })
.with(audit_event)
.and_call_original
.once

View File

@ -62,12 +62,26 @@ RSpec.describe API::Ci::Runner, :clean_gitlab_redis_shared_state do
end
end
it 'creates runner' do
request
context 'when token_expires_at is nil' do
it 'creates runner' do
request
expect(response).to have_gitlab_http_status(:created)
expect(json_response['id']).to eq(new_runner.id)
expect(json_response['token']).to eq(new_runner.token)
expect(response).to have_gitlab_http_status(:created)
expect(json_response).to eq({ 'id' => new_runner.id, 'token' => new_runner.token, 'token_expires_at' => nil })
end
end
context 'when token_expires_at is a valid date' do
before do
new_runner.token_expires_at = DateTime.new(2022, 1, 11, 14, 39, 24)
end
it 'creates runner' do
request
expect(response).to have_gitlab_http_status(:created)
expect(json_response).to eq({ 'id' => new_runner.id, 'token' => new_runner.token, 'token_expires_at' => '2022-01-11T14:39:24.000Z' })
end
end
it_behaves_like 'storing arguments in the application context for the API' do

View File

@ -664,7 +664,7 @@ RSpec.describe API::Ci::Runners do
post api("/runners/#{shared_runner.id}/reset_authentication_token", admin)
expect(response).to have_gitlab_http_status(:success)
expect(json_response).to eq({ 'token' => shared_runner.reload.token })
expect(json_response).to eq({ 'token' => shared_runner.reload.token, 'token_expires_at' => nil })
end.to change { shared_runner.reload.token }
end
@ -688,7 +688,7 @@ RSpec.describe API::Ci::Runners do
post api("/runners/#{project_runner.id}/reset_authentication_token", user)
expect(response).to have_gitlab_http_status(:success)
expect(json_response).to eq({ 'token' => project_runner.reload.token })
expect(json_response).to eq({ 'token' => project_runner.reload.token, 'token_expires_at' => nil })
end.to change { project_runner.reload.token }
end
@ -729,7 +729,22 @@ RSpec.describe API::Ci::Runners do
post api("/runners/#{group_runner_a.id}/reset_authentication_token", user)
expect(response).to have_gitlab_http_status(:success)
expect(json_response).to eq({ 'token' => group_runner_a.reload.token })
expect(json_response).to eq({ 'token' => group_runner_a.reload.token, 'token_expires_at' => nil })
end.to change { group_runner_a.reload.token }
end
it 'resets group runner authentication token with owner access with expiration time', :freeze_time do
expect(group_runner_a.reload.token_expires_at).to be_nil
group.update!(runner_token_expiration_interval: 5.days)
expect do
post api("/runners/#{group_runner_a.id}/reset_authentication_token", user)
group_runner_a.reload
expect(response).to have_gitlab_http_status(:success)
expect(json_response).to eq({ 'token' => group_runner_a.token, 'token_expires_at' => group_runner_a.token_expires_at.iso8601(3) })
expect(group_runner_a.token_expires_at).to eq(5.days.from_now)
end.to change { group_runner_a.reload.token }
end
end

View File

@ -1005,14 +1005,6 @@ RSpec.describe API::MergeRequests do
it_behaves_like 'merge requests list'
context 'when :api_caching_merge_requests is disabled' do
before do
stub_feature_flags(api_caching_merge_requests: false)
end
it_behaves_like 'merge requests list'
end
it "returns 404 for non public projects" do
project = create(:project, :private)

View File

@ -51,6 +51,9 @@ RSpec.describe API::Settings, 'Settings', :do_not_mock_admin_mode_setting do
expect(json_response['whats_new_variant']).to eq('all_tiers')
expect(json_response['user_deactivation_emails_enabled']).to be(true)
expect(json_response['suggest_pipeline_enabled']).to be(true)
expect(json_response['runner_token_expiration_interval']).to be_nil
expect(json_response['group_runner_token_expiration_interval']).to be_nil
expect(json_response['project_runner_token_expiration_interval']).to be_nil
end
end
@ -652,5 +655,37 @@ RSpec.describe API::Settings, 'Settings', :do_not_mock_admin_mode_setting do
end
end
end
context 'runner token expiration_intervals' do
it 'updates the settings' do
put api("/application/settings", admin), params: {
runner_token_expiration_interval: 3600,
group_runner_token_expiration_interval: 3600 * 2,
project_runner_token_expiration_interval: 3600 * 3
}
expect(response).to have_gitlab_http_status(:ok)
expect(json_response).to include(
'runner_token_expiration_interval' => 3600,
'group_runner_token_expiration_interval' => 3600 * 2,
'project_runner_token_expiration_interval' => 3600 * 3
)
end
it 'updates the settings with empty values' do
put api("/application/settings", admin), params: {
runner_token_expiration_interval: nil,
group_runner_token_expiration_interval: nil,
project_runner_token_expiration_interval: nil
}
expect(response).to have_gitlab_http_status(:ok)
expect(json_response).to include(
'runner_token_expiration_interval' => nil,
'group_runner_token_expiration_interval' => nil,
'project_runner_token_expiration_interval' => nil
)
end
end
end
end

View File

@ -85,6 +85,17 @@ RSpec.describe ::Ci::RegisterRunnerService, '#execute' do
expect(subject.ip_address).to eq args[:ip_address]
end
end
context 'with runner token expiration interval', :freeze_time do
before do
stub_application_setting(runner_token_expiration_interval: 5.days)
end
it 'creates runner with token expiration' do
is_expected.to be_an_instance_of(::Ci::Runner)
expect(subject.token_expires_at).to eq(5.days.from_now)
end
end
end
context 'when project token is used' do