Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2022-12-12 15:09:56 +00:00
parent 77ba8f96b5
commit 22a3da26ad
57 changed files with 843 additions and 552 deletions

View File

@ -66,9 +66,12 @@ _Consider adding links to check for Sentry errors, Production logs for 5xx, 302s
## Rollout Steps
Note: Please make sure to run the chatops commands in the slack channel that gets impacted by the command.
### Rollout on non-production environments
- Ensure that the feature MRs have been deployed to non-production environments.
- [ ] Verify the MR with the feature flag is merged to master.
- Verify that the feature MRs have been deployed to non-production environments with:
- [ ] `/chatops run auto_deploy status <merge-commit-of-your-feature>`
- [ ] Enable the feature globally on non-production environments.
- [ ] `/chatops run feature set <feature-flag-name> true --dev --staging --staging-ref`
@ -79,13 +82,16 @@ _Consider adding links to check for Sentry errors, Production logs for 5xx, 302s
### Specific rollout on production
For visibility, all `/chatops` commands that target production should be executed in the `#production` slack channel and cross-posted (with the command results) to the responsible team's slack channel (`#g_TEAM_NAME`).
- Ensure that the feature MRs have been deployed to both production and canary.
- [ ] `/chatops run auto_deploy status <merge-commit-of-your-feature>`
- If you're using [project-actor](https://docs.gitlab.com/ee/development/feature_flags/#feature-actors), you must enable the feature on these entries:
- Depending on the [type of actor](https://docs.gitlab.com/ee/development/feature_flags/#feature-actors) you are using, pick one of these options:
- If you're using **project-actor**, you must enable the feature on these entries:
- [ ] `/chatops run feature set --project=gitlab-org/gitlab,gitlab-org/gitlab-foss,gitlab-com/www-gitlab-com <feature-flag-name> true`
- If you're using [group-actor](https://docs.gitlab.com/ee/development/feature_flags/#feature-actors), you must enable the feature on these entries:
- If you're using **group-actor**, you must enable the feature on these entries:
- [ ] `/chatops run feature set --group=gitlab-org,gitlab-com <feature-flag-name> true`
- If you're using [user-actor](https://docs.gitlab.com/ee/development/feature_flags/#feature-actors), you must enable the feature on these entries:
- If you're using **user-actor**, you must enable the feature on these entries:
- [ ] `/chatops run feature set --user=<your-username> <feature-flag-name> true`
- [ ] Verify that the feature works on the specific entries. Posting the QA result in this issue is preferable.
@ -124,7 +130,7 @@ To do so, follow these steps:
- [ ] Create a merge request with the following changes. Ask for review and merge it.
- [ ] Set the `default_enabled` attribute in [the feature flag definition](https://docs.gitlab.com/ee/development/feature_flags/#feature-flag-definition-and-validation) to `true`.
- [ ] Create [a changelog entry](https://docs.gitlab.com/ee/development/feature_flags/#changelog).
- [ ] Review [what warrants a changelog entry](https://docs.gitlab.com/ee/development/changelog.html#what-warrants-a-changelog-entry) and decide if [a changelog entry](https://docs.gitlab.com/ee/development/feature_flags/#changelog) is needed.
- [ ] Ensure that the default-enabling MR has been included in the release package.
If the merge request was deployed before [the monthly release was tagged](https://about.gitlab.com/handbook/engineering/releases/#self-managed-releases-1),
the feature can be officially announced in a release blog post.
@ -165,7 +171,7 @@ You can either [create a follow-up issue for Feature Flag Cleanup](https://gitla
the feature can be officially announced in a release blog post.
- [ ] `/chatops run release check <merge-request-url> <milestone>`
- [ ] Close [the feature issue][main-issue] to indicate the feature will be released in the current milestone.
- [ ] If not already done, clean up the feature flag from all environments by running these chatops command in `#production` channel:
- [ ] Clean up the feature flag from all environments by running these chatops command in `#production` channel:
- [ ] `/chatops run feature delete <feature-flag-name> --dev --staging --staging-ref --production`
- [ ] Close this rollout issue.

View File

@ -1 +1 @@
15.7.0-rc1
15.7.0

View File

@ -59,21 +59,20 @@ export default {
return [
{
title: I18N_ALL_TYPES,
runnerType: null,
},
...tabs,
];
},
},
methods: {
onTabSelected({ runnerType }) {
onTabSelected(runnerType) {
this.$emit('input', {
...this.value,
runnerType,
pagination: { page: 1 },
});
},
isTabActive({ runnerType }) {
isTabActive(runnerType = null) {
return runnerType === this.value.runnerType;
},
tabBadgeCountVariables(runnerType) {
@ -102,8 +101,8 @@ export default {
<gl-tab
v-for="tab in tabs"
:key="`${tab.runnerType}`"
:active="isTabActive(tab)"
@click="onTabSelected(tab)"
:active="isTabActive(tab.runnerType)"
@click="onTabSelected(tab.runnerType)"
>
<template #title>
{{ tab.title }}

View File

@ -16,13 +16,13 @@ import { INSTANCE_TYPE, GROUP_TYPE } from '../../constants';
* <strong/> tag.
*
* ```vue
* <runner-count-stat
* <runner-count
* #default="{ count }"
* :scope="INSTANCE_TYPE"
* :variables="{ status: 'ONLINE' }"
* >
* <strong>{{ count }}</strong>
* </runner-count-stat>
* </runner-count>
* ```
*
* Use `:skip="true"` to prevent data from being fetched and

View File

@ -1,5 +1,4 @@
<script>
import RunnerSingleStat from '~/ci/runner/components/stat/runner_single_stat.vue';
import {
I18N_STATUS_ONLINE,
I18N_STATUS_OFFLINE,
@ -8,9 +7,19 @@ import {
STATUS_OFFLINE,
STATUS_STALE,
} from '../../constants';
import RunnerSingleStat from './runner_single_stat.vue';
import RunnerCount from './runner_count.vue';
/**
* Shows general stats about the runners.
*
* First it checks if there are any runners in this context, and if so,
* shows more details for different status.
*/
export default {
components: {
RunnerCount,
RunnerSingleStat,
RunnerUpgradeStatusStats: () =>
import('ee_component/ci/runner/components/stat/runner_upgrade_status_stats.vue'),
@ -71,19 +80,21 @@ export default {
};
</script>
<template>
<div class="gl-display-flex gl-flex-wrap gl-py-6">
<runner-single-stat
v-for="stat in stats"
:key="stat.key"
:scope="scope"
v-bind="stat.props"
class="gl-px-5"
/>
<runner-count #default="{ count }" :scope="scope" :variables="variables">
<div v-if="count" class="gl-display-flex gl-flex-wrap gl-py-6">
<runner-single-stat
v-for="stat in stats"
:key="stat.key"
:scope="scope"
v-bind="stat.props"
class="gl-px-5"
/>
<runner-upgrade-status-stats
class="gl-display-contents"
:scope="scope"
:variables="variables"
/>
</div>
<runner-upgrade-status-stats
class="gl-display-contents"
:scope="scope"
:variables="variables"
/>
</div>
</runner-count>
</template>

View File

@ -20,13 +20,14 @@ export default {
role="region"
:aria-label="__('Merge request reports')"
data-testid="mr-widget-app"
class="mr-widget-section"
>
<component
:is="widget"
v-for="(widget, index) in widgets"
:key="widget.name || index"
:mr="mr"
:class="{ 'mr-widget-border-top': index === 0 }"
class="mr-widget-section"
/>
</section>
</template>

View File

@ -82,10 +82,8 @@ export default {
v-if="data.children && data.children.length > 0 && level === 2"
class="gl-m-0 gl-p-0 gl-list-style-none"
>
<li>
<li v-for="(childData, index) in data.children" :key="childData.id || index">
<dynamic-content
v-for="(childData, index) in data.children"
:key="childData.id || index"
:data="childData"
:widget-name="widgetName"
:level="3"

View File

@ -48,9 +48,9 @@ export default {
:class="{
[iconClassNameText]: !isLoading,
[`mr-widget-status-icon-level-${level}`]: !isLoading,
'gl-mr-3': level === 1,
'gl-w-6 gl-h-6 gl--flex-center': level === 1,
}"
class="gl-relative gl-w-6 gl-h-6 gl-rounded-full gl--flex-center"
class="gl-relative gl-rounded-full gl-mr-3"
>
<gl-loading-icon v-if="isLoading" size="md" inline />
<gl-icon

View File

@ -6,6 +6,7 @@ import SafeHtml from '~/vue_shared/directives/safe_html';
import { sprintf, __ } from '~/locale';
import Poll from '~/lib/utils/poll';
import HelpPopover from '~/vue_shared/components/help_popover.vue';
import { DynamicScroller, DynamicScrollerItem } from 'vendor/vue-virtual-scroller';
import { EXTENSION_ICONS } from '../../constants';
import { createTelemetryHub } from '../extensions/telemetry';
import ContentRow from './widget_content_row.vue';
@ -26,6 +27,8 @@ export default {
GlLoadingIcon,
ContentRow,
DynamicContent,
DynamicScroller,
DynamicScrollerItem,
HelpPopover,
},
directives: {
@ -305,7 +308,7 @@ export default {
<div v-if="isLoadingExpandedContent" class="report-block-container gl-text-center">
<gl-loading-icon size="sm" inline /> {{ loadingText }}
</div>
<div v-else class="gl-px-5 gl-display-flex">
<div v-else class="gl-pl-5 gl-display-flex" :class="{ 'gl-pr-5': $scopedSlots.content }">
<content-row
v-if="contentError"
:level="2"
@ -318,12 +321,25 @@ export default {
</content-row>
<div v-else class="gl-w-full">
<slot name="content">
<dynamic-content
v-for="(data, index) in content"
:key="data.id || index"
:data="data"
:widget-name="widgetName"
/>
<dynamic-scroller
v-if="content"
:items="content"
:min-item-size="32"
:style="{ maxHeight: '170px' }"
data-testid="dynamic-content-scroller"
class="gl-pr-5"
>
<template #default="{ item, index, active }">
<dynamic-scroller-item :item="item" :active="active">
<dynamic-content
:key="item.id || index"
:data="item"
:widget-name="widgetName"
:level="2"
/>
</dynamic-scroller-item>
</template>
</dynamic-scroller>
</slot>
</div>
</div>

View File

@ -79,10 +79,15 @@ export default {
</script>
<template>
<div
class="gl-w-full gl-display-flex mr-widget-content-row gl-align-items-baseline"
class="gl-w-full gl-display-flex gl-align-items-baseline"
:class="{ 'gl-border-t gl-py-3 gl-pl-7': level === 2 }"
>
<status-icon v-if="statusIconName" :level="2" :name="widgetName" :icon-name="statusIconName" />
<status-icon
v-if="statusIconName && !header"
:level="2"
:name="widgetName"
:icon-name="statusIconName"
/>
<div class="gl-w-full">
<div class="gl-display-flex">
<slot name="header">
@ -128,6 +133,12 @@ export default {
</div>
</div>
<div class="gl-display-flex gl-align-items-baseline gl-w-full">
<status-icon
v-if="statusIconName && header"
:level="2"
:name="widgetName"
:icon-name="statusIconName"
/>
<slot name="body"></slot>
</div>
</div>

View File

@ -1097,10 +1097,6 @@ $tabs-holder-z-index: 250;
}
}
.mr-widget-content-row:first-child {
border-top: 0;
}
.mr-widget-status-icon-level-1::before {
@include gl-content-empty;
@include gl-absolute;

View File

@ -4,7 +4,7 @@ module Ci
def show_secure_files_setting(project, user)
return false if user.nil?
Feature.enabled?(:ci_secure_files, project) && user.can?(:read_secure_files, project)
user.can?(:read_secure_files, project)
end
end
end

View File

@ -115,6 +115,15 @@ class GitlabUploader < CarrierWave::Uploader::Base
end
end
def multi_read(offsets)
open do |stream|
offsets.map do |start_offset, end_offset|
stream.seek(start_offset)
stream.read(end_offset - start_offset + 1)
end
end
end
# Used to replace an existing upload with another +file+ without modifying stored metadata
# Use this method only to repair/replace an existing upload, or to upload to a Geo secondary node
#

View File

@ -1,8 +0,0 @@
---
name: ci_secure_files
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/78227
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/350748
milestone: '14.8'
type: development
group: group::incubation
default_enabled: false

View File

@ -1,23 +1,23 @@
- title: "`POST /api/v4/runners` method to register runners" # (required) The name of the feature to be deprecated
- title: "Registration tokens and server-side runner arguments in `POST /api/v4/runners` endpoint" # (required) The name of the feature to be deprecated
announcement_milestone: "15.6" # (required) The milestone when this feature was first announced as deprecated.
announcement_date: "2022-11-22" # (required) The date of the milestone release when this feature was first announced as deprecated. This should almost always be the 22nd of a month (YYYY-MM-22), unless you did an out of band blog post.
removal_milestone: "16.0" # (required) The milestone when this feature is planned to be removed
removal_date: "2023-05-22" # (required) The date of the milestone release when this feature is planned to be removed. This should almost always be the 22nd of a month (YYYY-MM-22), unless you did an out of band blog post.
removal_milestone: "17.0" # (required) The milestone when this feature is planned to be removed
removal_date: "2024-04-22" # (required) The date of the milestone release when this feature is planned to be removed. This should almost always be the 22nd of a month (YYYY-MM-22), unless you did an out of band blog post.
breaking_change: true # (required) If this deprecation is a breaking change, set this value to true
reporter: pedropombeiro # (required) GitLab username of the person reporting the deprecation
stage: Verify # (required) String value of the stage that the feature was created in. e.g., Growth
issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/379743 # (required) Link to the deprecation issue in GitLab
body: | # (required) Do not modify this line, instead modify the lines below.
The `POST` method operation on the `/api/v4/runners` endpoint is deprecated.
This endpoint and method [registers](https://docs.gitlab.com/ee/api/runners.html#register-a-new-runner) a runner
with a GitLab instance at the instance, group, or project level through the API. We plan to remove this endpoint
and method in GitLab 16.0.
The support for registration tokens and certain runner configuration arguments in the `POST` method operation on the `/api/v4/runners` endpoint is deprecated.
This endpoint [registers](https://docs.gitlab.com/ee/api/runners.html#register-a-new-runner) a runner
with a GitLab instance at the instance, group, or project level through the API. We plan to remove the support for
registration tokens and certain configuration arguments in this endpoint in GitLab 17.0.
In GitLab 15.8, we plan to implement a new method to bind runners to a GitLab instance,
as part of the new [GitLab Runner token architecture](https://docs.gitlab.com/ee/architecture/blueprints/runner_tokens/).
This new architecture introduces a new method for registering runners and will eliminate the legacy
[runner registration token](https://docs.gitlab.com/ee/security/token_overview.html#runner-registration-tokens).
From GitLab 16.0 and later, the runner registration methods implemented by the new GitLab Runner token architecture will be the only supported methods.
From GitLab 17.0 and later, the runner registration methods implemented by the new GitLab Runner token architecture will be the only supported methods.
end_of_support_milestone: "16.0" # (optional) Use "XX.YY" format. The milestone when support for this feature will end.
end_of_support_date: "2023-05-22" # (optional) The date of the milestone release when support for this feature will end.
tiers: # (optional - may be required in the future) An array of tiers that the feature is available in currently. e.g., [Free, Silver, Gold, Core, Premium, Ultimate]

View File

@ -1,8 +1,8 @@
- title: "`runnerRegistrationToken` parameter for GitLab Runner Helm Chart" # (required) The name of the feature to be deprecated
announcement_milestone: "15.6" # (required) The milestone when this feature was first announced as deprecated.
announcement_date: "2022-11-22" # (required) The date of the milestone release when this feature was first announced as deprecated. This should almost always be the 22nd of a month (YYYY-MM-22), unless you did an out of band blog post.
removal_milestone: "16.0" # (required) The milestone when this feature is planned to be removed
removal_date: "2023-05-22" # (required) The date of the milestone release when this feature is planned to be removed. This should almost always be the 22nd of a month (YYYY-MM-22), unless you did an out of band blog post.
removal_milestone: "17.0" # (required) The milestone when this feature is planned to be removed
removal_date: "2024-04-22" # (required) The date of the milestone release when this feature is planned to be removed. This should almost always be the 22nd of a month (YYYY-MM-22), unless you did an out of band blog post.
breaking_change: true # (required) If this deprecation is a breaking change, set this value to true
reporter: pedropombeiro # (required) GitLab username of the person reporting the deprecation
stage: Verify # (required) String value of the stage that the feature was created in. e.g., Growth
@ -12,8 +12,8 @@
As part of the new [GitLab Runner token architecture](https://docs.gitlab.com/ee/architecture/blueprints/runner_tokens/), in GitLab 15.8 we plan to introduce:
- A new method to bind runners to a GitLab instance.
- A new method to bind runners to a GitLab instance leveraging `runnerToken`.
- A unique system ID saved to the `config.toml`, which will ensure traceability between jobs and runners.
From GitLab 16.0 and later, the methods to register runners introduced by the new GitLab Runner token architecture will be the only supported methods.
From GitLab 17.0 and later, the methods to register runners introduced by the new GitLab Runner token architecture will be the only supported methods.
end_of_support_milestone: "16.0" # (optional) Use "XX.YY" format. The milestone when support for this feature will end.
end_of_support_date: "2023-05-22" # (optional) The date of the milestone release when support for this feature will end.

View File

@ -1,18 +1,19 @@
- title: "`gitlab-runner register` command" # (required) The name of the feature to be deprecated
- title: "Registration tokens and server-side runner arguments in `gitlab-runner register` command" # (required) The name of the feature to be deprecated
announcement_milestone: "15.6" # (required) The milestone when this feature was first announced as deprecated.
announcement_date: "2022-11-22" # (required) The date of the milestone release when this feature was first announced as deprecated. This should almost always be the 22nd of a month (YYYY-MM-22), unless you did an out of band blog post.
removal_milestone: "16.0" # (required) The milestone when this feature is planned to be removed
removal_date: "2023-05-22" # (required) The date of the milestone release when this feature is planned to be removed. This should almost always be the 22nd of a month (YYYY-MM-22), unless you did an out of band blog post.
removal_milestone: "17.0" # (required) The milestone when this feature is planned to be removed
removal_date: "2024-04-22" # (required) The date of the milestone release when this feature is planned to be removed. This should almost always be the 22nd of a month (YYYY-MM-22), unless you did an out of band blog post.
breaking_change: true # (required) If this deprecation is a breaking change, set this value to true
reporter: pedropombeiro # (required) GitLab username of the person reporting the deprecation
stage: Verify # (required) String value of the stage that the feature was created in. e.g., Growth
issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/380872 # (required) Link to the deprecation issue in GitLab
body: | # (required) Do not modify this line, instead modify the lines below.
The command to [register](https://docs.gitlab.com/runner/register/) a runner, `gitlab-runner register` is deprecated.
The support for registration tokens and certain configuration arguments in the command to [register](https://docs.gitlab.com/runner/register/) a runner, `gitlab-runner register` is deprecated.
GitLab plans to introduce a new [GitLab Runner token architecture](https://docs.gitlab.com/ee/architecture/blueprints/runner_tokens/) in GitLab 15.8,
which introduces a new method for registering runners and eliminates the legacy
[runner registration token](https://docs.gitlab.com/ee/security/token_overview.html#runner-registration-tokens).
The new method will involve passing a [runner authentication token](https://docs.gitlab.com/ee/security/token_overview.html#runner-authentication-tokens-also-called-runner-tokens)
to a new `gitlab-runner deploy` command.
The new method will involve creating the runner in the GitLab UI and passing the
[runner authentication token](https://docs.gitlab.com/ee/security/token_overview.html#runner-authentication-tokens-also-called-runner-tokens)
to the `gitlab-runner register` command.
end_of_support_milestone: "16.0" # (optional) Use "XX.YY" format. The milestone when support for this feature will end.
end_of_support_date: "2023-05-22" # (optional) The date of the milestone release when support for this feature will end.

View File

@ -1,8 +1,8 @@
- title: "GitLab Runner registration token in Runner Operator" # (required) The name of the feature to be deprecated
announcement_milestone: "15.6" # (required) The milestone when this feature was first announced as deprecated.
announcement_date: "2022-11-22" # (required) The date of the milestone release when this feature was first announced as deprecated. This should almost always be the 22nd of a month (YYYY-MM-22), unless you did an out of band blog post.
removal_milestone: "16.0" # (required) The milestone when this feature is planned to be removed
removal_date: "2023-05-22" # (required) The date of the milestone release when this feature is planned to be removed. This should almost always be the 22nd of a month (YYYY-MM-22), unless you did an out of band blog post.
removal_milestone: "17.0" # (required) The milestone when this feature is planned to be removed
removal_date: "2024-04-22" # (required) The date of the milestone release when this feature is planned to be removed. This should almost always be the 22nd of a month (YYYY-MM-22), unless you did an out of band blog post.
breaking_change: true # (required) If this deprecation is a breaking change, set this value to true
reporter: ratchade # (required) GitLab username of the person reporting the deprecation
stage: Verify # (required) String value of the stage that the feature was created in. e.g., Growth

View File

@ -1859,8 +1859,8 @@ Updates to example must be made at:
# Set number of Sidekiq queue processes to the same number as available CPUs
sidekiq['queue_groups'] = ['*'] * 4
# Set number of Sidekiq threads per queue process to the recommend number of 10
sidekiq['max_concurrency'] = 10
# Set number of Sidekiq threads per queue process to the recommend number of 20
sidekiq['max_concurrency'] = 20
# Monitoring
consul['enable'] = true

View File

@ -1877,8 +1877,8 @@ Updates to example must be made at:
# Set number of Sidekiq queue processes to the same number as available CPUs
sidekiq['queue_groups'] = ['*'] * 4
# Set number of Sidekiq threads per queue process to the recommend number of 10
sidekiq['max_concurrency'] = 10
# Set number of Sidekiq threads per queue process to the recommend number of 20
sidekiq['max_concurrency'] = 20
# Monitoring
consul['enable'] = true

View File

@ -700,8 +700,8 @@ On each node perform the following:
puma['listen'] = '0.0.0.0'
sidekiq['listen_address'] = "0.0.0.0"
# Configure Sidekiq with 2 workers and 10 max concurrency
sidekiq['max_concurrency'] = 10
# Configure Sidekiq with 2 workers and 20 max concurrency
sidekiq['max_concurrency'] = 20
sidekiq['queue_groups'] = ['*'] * 2
# Add the monitoring node's IP address to the monitoring whitelist and allow it to

View File

@ -1794,8 +1794,8 @@ Updates to example must be made at:
## Set number of Sidekiq queue processes to the same number as available CPUs
sidekiq['queue_groups'] = ['*'] * 2
## Set number of Sidekiq threads per queue process to the recommend number of 10
sidekiq['max_concurrency'] = 10
## Set number of Sidekiq threads per queue process to the recommend number of 20
sidekiq['max_concurrency'] = 20
# Monitoring
consul['enable'] = true

View File

@ -1872,8 +1872,8 @@ Updates to example must be made at:
## Set number of Sidekiq queue processes to the same number as available CPUs
sidekiq['queue_groups'] = ['*'] * 4
## Set number of Sidekiq threads per queue process to the recommend number of 10
sidekiq['max_concurrency'] = 10
## Set number of Sidekiq threads per queue process to the recommend number of 20
sidekiq['max_concurrency'] = 20
# Monitoring
consul['enable'] = true

View File

@ -1790,8 +1790,8 @@ Updates to example must be made at:
## Set number of Sidekiq queue processes to the same number as available CPUs
sidekiq['queue_groups'] = ['*'] * 4
## Set number of Sidekiq threads per queue process to the recommend number of 10
sidekiq['max_concurrency'] = 10
## Set number of Sidekiq threads per queue process to the recommend number of 20
sidekiq['max_concurrency'] = 20
# Monitoring
consul['enable'] = true

View File

@ -14,8 +14,8 @@ There are two tokens to take into account when connecting a runner with GitLab.
| Token | Description |
| ----- | ----------- |
| Registration token | Token used to [register the runner](https://docs.gitlab.com/runner/register/). It can be [obtained through GitLab](../ci/runners/index.md). |
| Authentication token | Token used to authenticate the runner with the GitLab instance. It is obtained automatically when you [register a runner](https://docs.gitlab.com/runner/register/) or by the Runner API when you manually [register a runner](#register-a-new-runner-deprecated) or [reset the authentication token](#reset-runners-authentication-token-by-using-the-runner-id). |
| Registration token | Token used to [register the runner](https://docs.gitlab.com/runner/register/). It can be [obtained through GitLab](../ci/runners/index.md). |
| Authentication token | Token used to authenticate the runner with the GitLab instance. It is obtained automatically when you [register a runner](https://docs.gitlab.com/runner/register/) or by the Runner API when you manually [register a runner](#register-a-new-runner) or [reset the authentication token](#reset-runners-authentication-token-by-using-the-runner-id). |
Here's an example of how the two tokens are used in runner registration:
@ -640,12 +640,7 @@ Example response:
]
```
## Register a new runner (deprecated)
> [Deprecated](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/102579) in GitLab 15.6.
WARNING:
This feature was [deprecated](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/102579) in GitLab 15.6 and is planned for removal in 16.0. This change is a breaking change.
## Register a new runner
Register a new runner for the instance.
@ -655,18 +650,18 @@ POST /runners
| Attribute | Type | Required | Description |
|--------------------|--------------|----------|---------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `token` | string | yes | [Registration token](#registration-and-authentication-tokens) |
| `description` | string | no | Runner's description |
| `token` | string | yes | [Deprecated](https://gitlab.com/gitlab-org/gitlab/-/issues/379743): [Registration token](#registration-and-authentication-tokens). The registration token will be replaced by an authentication token in GitLab 16.0 |
| `description` | string | no | [Deprecated](https://gitlab.com/gitlab-org/gitlab/-/issues/379743): Runner's description |
| `info` | hash | no | Runner's metadata. You can include `name`, `version`, `revision`, `platform`, and `architecture`, but only `version`, `platform`, and `architecture` are displayed in the Admin Area of the UI |
| `active` | boolean | no | Deprecated: Use `paused` instead. Specifies whether the runner is allowed to receive new jobs |
| `paused` | boolean | no | Specifies whether the runner should ignore new jobs |
| `locked` | boolean | no | Specifies whether the runner should be locked for the current project |
| `run_untagged` | boolean | no | Specifies whether the runner should handle untagged jobs |
| `tag_list` | string array | no | A list of runner tags |
| `access_level` | string | no | The access level of the runner; `not_protected` or `ref_protected` |
| `maximum_timeout` | integer | no | Maximum timeout that limits the amount of time (in seconds) that runners can run jobs |
| `maintainer_note` | string | no | [Deprecated](https://gitlab.com/gitlab-org/gitlab/-/issues/350730), see `maintenance_note` |
| `maintenance_note` | string | no | Free-form maintenance notes for the runner (1024 characters) |
| `active` | boolean | no | Deprecated: Use `paused` instead. Specifies whether the runner is allowed to receive new jobs |
| `paused` | boolean | no | [Deprecated](https://gitlab.com/gitlab-org/gitlab/-/issues/379743): Specifies whether the runner should ignore new jobs |
| `locked` | boolean | no | [Deprecated](https://gitlab.com/gitlab-org/gitlab/-/issues/379743): Specifies whether the runner should be locked for the current project |
| `run_untagged` | boolean | no | [Deprecated](https://gitlab.com/gitlab-org/gitlab/-/issues/379743): Specifies whether the runner should handle untagged jobs |
| `tag_list` | string array | no | [Deprecated](https://gitlab.com/gitlab-org/gitlab/-/issues/379743): A list of runner tags |
| `access_level` | string | no | [Deprecated](https://gitlab.com/gitlab-org/gitlab/-/issues/379743): The access level of the runner; `not_protected` or `ref_protected` |
| `maximum_timeout` | integer | no | [Deprecated](https://gitlab.com/gitlab-org/gitlab/-/issues/379743): Maximum timeout that limits the amount of time (in seconds) that runners can run jobs |
| `maintainer_note` | string | no | [Deprecated](https://gitlab.com/gitlab-org/gitlab/-/issues/350730), see `maintenance_note` |
| `maintenance_note` | string | no | [Deprecated](https://gitlab.com/gitlab-org/gitlab/-/issues/379743): Free-form maintenance notes for the runner (1024 characters) |
```shell
curl --request POST "https://gitlab.example.com/api/v4/runners" \

View File

@ -14,7 +14,7 @@ CI/CD jobs in a reliable and concurrent environment. Ever since the beginnings
of the service as a Ruby program, runners are registered in a GitLab instance with
a registration token - a randomly generated string of text. The registration token is unique for its given scope
(instance, group, or project). The registration token proves that the party that registers the runner has
administrator access to the instance, group, or project to which the runner is registered.
administrative access to the instance, group, or project to which the runner is registered.
This approach has worked well in the initial years, but some major known issues started to
become apparent as the target audience grew:
@ -37,49 +37,122 @@ We call this new mechanism the "next GitLab Runner Token architecture".
The proposal addresses the issues of a _single token per scope_ and _token storage_
by eliminating the need for a registration token. Runner creation happens
in the GitLab Runners settings page for the given scope, in the context of the logged-in user
, which provides traceability. The page provides instructions to configure the newly-created
runner in supported environments.
in the GitLab Runners settings page for the given scope, in the context of the logged-in user,
which provides traceability. The page provides instructions to configure the newly-created
runner in supported environments using the existing `gitlab-runner register` command.
The runner configuration will be generated through a new `deploy` command, which will leverage
the `/runners/verify` REST endpoint to ensure the validity of the authentication token.
The remaining concerns become non-issues due to the elimination of the registration token.
The configuration can be applied across many machines by reusing the same instructions.
A unique system identifier will be generated automatically if a value is missing from
the runner entry in the `config.toml` file. This allows differentiating systems sharing the same
runner token (for example, in auto-scaling scenarios), and is crucial for the proper functioning of our
long-polling mechanism when the same authentication token is shared across two or more runner managers.
### Using the authentication token in place of the registration token
Given that the creation of runners involves user interaction, it should be possible
to eventually lower the per-plan limit of CI runners that can be registered per scope.
<!-- vale gitlab.Spelling = NO -->
In this proposal, runners created in the GitLab UI are assigned authentication tokens prefixed with
`glrt-` (**G**it**L**ab **R**unner **T**oken).
<!-- vale gitlab.Spelling = YES -->
The prefix allows the existing `register` command to use the authentication token _in lieu_
of the current registration token (`--registration-token`), requiring minimal adjustments in
existing workflows.
The authentication token is shown to the user only once - after completing the creation flow - to
discourage unintended reuse.
### Auto-scaling scenarios (for example Helm chart)
Given that the runner is pre-created through the GitLab UI, the `register` command fails if
provided with arguments that are exposed in the runner creation form.
Some examples are `--tag-list`, `--run-untagged`, `--locked`, or `--access-level` as these are
sensitive parameters that should be decided at creation time by an administrator/owner.
The runner configuration is generated through the existing `register` command, which can behave in
two different ways depending on whether it is supplied a registration token or an authentication
token in the `--registration-token` argument:
| Token type | Behavior |
| ---------- | -------- |
| Registration token | Leverages the `POST /api/v4/runners` REST endpoint to create a new runner, creating a new entry in `config.toml`. |
| Authentication token | Leverages the `POST /api/v4/runners/verify` REST endpoint to ensure the validity of the authentication token. Creates an entry in `config.toml` file and a `system_id` value in a sidecar file if missing (`.runner_system_id`). |
### Transition period
During a transition period, legacy tokens ("registration tokens") continue to be shown on the
GitLab Runners settings page and to be accepted by the `gitlab-runner register` command.
The legacy workflow is nevertheless discouraged in the UI.
Users are steered towards the new flow consisting of creating the runner in the UI and using the
resulting authentication token with the `gitlab-runner register` command as they do today.
This approach reduces disruption to users responsible for deploying runners.
### Reusing the runner authentication token across many machines
In the existing model, a new runner is created whenever a new worker is required. This
has led to many situations where runners are left behind and become stale.
In the proposed model, a `ci_runners` table entry describes a configuration,
which the runner could reuse across multiple machines. This allows differentiating the context in
which the runner is being used. In situations where we must differentiate between runners
that reuse the same configuration, we can use the unique system identifier to track all
unique "runners" that are executed in context of a single `ci_runners` model. This unique
system identifier would be present in the Runner's `config.toml` configuration file and
initially set when generating the new `[[runners]]` configuration by means of the `deploy` command.
Legacy files that miss values for unique system identifiers will get rewritten automatically with new values.
In the proposed model, a `ci_runners` table entry describes a configuration that the user can reuse
across multiple machines.
A unique system identifier is [generated automatically](#generating-a-system_id-value) whenever the
runner application starts up or the configuration is saved.
This allows differentiating the context in which the runner is being used.
The `system_id` value complements the short runner token that is currently used to identify a
runner in command line output, CI job logs, and GitLab UI.
Given that the creation of runners involves user interaction, it should be possible
to eventually lower the per-plan limit of CI runners that can be registered per scope.
#### Generating a `system_id` value
We ensure that a unique system identifier is assigned at all times to a `gitlab-runner`
installation.
The ID is derived from an existing machine identifier such as `/etc/machine-id` (on Linux) and
hashed for privacy, in which case it is prefixed with `s_`.
If an ID is not available, a random string is used instead, in which case it is prefixed with `r_`.
This unique ID identifies the `gitlab-runner` process and is sent
on `POST /api/v4/jobs` requests for all runners in the `config.toml` file.
The ID is generated and saved both at `gitlab-runner` startup and whenever the configuration is
saved to disk.
Instead of saving the ID at the root of `config.toml` though, we save it to a new file that lives
next to it - `.runner_system_id`. The goal for this new file is to make it less likely that IDs
get reused due to manual copying of the `config.toml` file
```plain
s_cpwhDr7zFz4xBJujFeEM
```
### Runner identification in CI jobs
For users to identify the machine where the job was executed, the unique identifier will need to be visible in CI job contexts.
For users to identify the machine where the job was executed, the unique identifier needs to be
visible in CI job contexts.
As a first iteration, GitLab Runner will include the unique system identifier in the build logs,
wherever it publishes the short token SHA.
Given that the runner will potentially be reused with different unique system identifiers,
we can store the unique system ID. This ensures the unique system ID maps to a GitLab Runner's `config.toml` entry with
the runner token. The `ci_runner_machines` would hold information about each unique runner machine,
with information when runner last connected, and what type of runner it was. The relevant fields
will be moved from the `ci_runners`.
The `ci_builds_runner_session` (or `ci_builds` or `ci_builds_metadata`) will reference
Given that the runner can potentially be reused with different unique system identifiers,
we should store the unique system ID in the database.
This ensures the unique system ID maps to a GitLab Runner's `system_id` value with the runner token.
A new `ci_runner_machines` table holds information about each unique runner machine,
with information regarding when the runner last connected, and what type of runner it was.
In the long term, the relevant fields are to be moved from the `ci_runners` into
`ci_runner_machines`.
Until the removal milestone though, they should be kept in the `ci_runners` as a fallback when a
matching `ci_runner_machines` record does not exist.
An expected scenario is the case when the table is created but the runner hasn't pinged the GitLab
instance (for example if the runner is offline).
In addition, we should add the following columns to `ci_runners`:
- a `user_id` column to keep track of who created a runner;
- a `registration_type` enum column to `ci_runners` to signal whether a runner has been created
using the legacy `register` method, or the new UI-based method.
Possible values are `:registration_token` and `:authenticated_user`.
This allows the stale runner cleanup service to determine which runners to clean up, and allows
future uses that may not be apparent.
```sql
CREATE TABLE ci_runner (
...
user_id bigint
registration_type int8
)
```
The `ci_builds_runner_session` (or `ci_builds` or `ci_builds_metadata`) shall reference
`ci_runner_machines`.
We might consider a more efficient way to store `contacted_at` than updating the existing record.
@ -110,7 +183,8 @@ CREATE TABLE ci_runner_machines (
- Runners can always be traced back to the user who created it, using the audit log;
- The claims of a CI runner are known at creation time, and cannot be changed from the runner
(for example, changing the `access_level`/`protected` flag). Authenticated users
may however still edit these settings through the GitLab UI.
may however still edit these settings through the GitLab UI;
- Easier cleanup of stale runners, which doesn't touch the `ci_runner` table.
## Details
@ -121,53 +195,95 @@ token to register new runners.
The new workflow looks as follows:
1. The user opens the Runners settings page;
1. The user opens the Runners settings page (instance, group, or project level);
1. The user fills in the details regarding the new desired runner, namely description,
tags, protected, locked, etc.;
1. The user clicks `Create`. That results in the following:
1. Creates a new runner in the `ci_runners` table (and corresponding authentication token);
1. Creates a new runner in the `ci_runners` table (and corresponding `glrt-` prefixed authentication token);
1. Presents the user with instructions on how to configure this new runner on a machine,
with possibilities for different supported deployment scenarios (e.g. shell, `docker-compose`, Helm chart, etc.)
This information contains a token which will only be available to the user once, and the UI
will make it clear to the user that the value will not be shown again, as registering the same runner multiple times
This information contains a token which is available to the user only once, and the UI
makes it clear to the user that the value shall not be shown again, as registering the same runner multiple times
is discouraged (though not impossible).
1. The user copies and pastes the instructions for the intended deployment scenario (a `deploy` command), leading to the following actions:
1. The user copies and pastes the instructions for the intended deployment scenario (a `register` command), leading to the following actions:
1. Upon executing the new `gitlab-runner deploy` command in the instructions, `gitlab-runner` will perform
a call to the `POST /runners/verify` with the given runner token;
1. If the `POST /runners/verify` GitLab endpoint validates the token, the `config.toml` file will be populated with the configuration.
1. Upon executing the new `gitlab-runner register` command in the instructions, `gitlab-runner` performs
a call to the `POST /api/v4/runners/verify` with the given runner token;
1. If the `POST /api/v4/runners/verify` GitLab endpoint validates the token, the `config.toml`
file is populated with the configuration;
1. Whenever a runner pings for a job, the respective `ci_runner_machines` record is
["upserted"](https://en.wiktionary.org/wiki/upsert) with the latest information about the
runner (with Redis cache in front of it like we do for Runner heartbeats).
The `gitlab-runner deploy` will also accept executor-specific arguments
currently present in the `register` command.
As part of the transition period, we provide admins and top-level group owners with an
instance/group-level setting (`allow_runner_registration_token`) to disable the legacy registration
token functionality and enforce using only the new workflow.
Any attempt by a `gitlab-runner register` command to hit the `POST /api/v4/runners` endpoint
to register a new runner with a registration token results in a `HTTP 410 Gone` status code.
As part of the transition period, we will provide admins and top-level group owners with a instance/group-level setting to disable
the legacy registration token functionality and enforce using only the new workflow.
Any attempt by a `gitlab-runner register` command to hit the `POST /runners` endpoint to register a new runner
will result in a `HTTP 410 - Gone` status code. The instance setting is inherited by the groups
, which means that if the legacy registration method is disabled at the instance method, the descendant groups/projects will also mandatorily
prevent the legacy registration method.
The instance setting is inherited by the groups. This means that if the legacy registration method
is disabled at the instance method, the descendant groups/projects mandatorily prevents the legacy
registration method.
The registration token workflow is to be deprecated (with a deprecation notice printed by the `gitlab-runner register` command)
and removed at a future major release after the concept is proven stable and customers have migrated to the new workflow.
### Handling of legacy runners
Legacy versions of GitLab Runner will not send the unique system identifier in its requests, and we
Legacy versions of GitLab Runner do not send the unique system identifier in its requests, and we
will not change logic in Workhorse to handle unique system IDs. This can be improved upon in the
future once the legacy registration system is removed, and runners have been upgraded to newer
future after the legacy registration system is removed, and runners have been upgraded to newer
versions.
Not using the unique system ID means that all connected runners with the same token will be
Job pings from such legacy runners results in a `ci_runner_machines` record containing a
`<legacy>` `machine_id` field value.
Not using the unique system ID means that all connected runners with the same token are
notified, instead of just the runner matching the exact system identifier. While not ideal, this is
not an issue per-se.
### Helm chart
### `ci_runner_machines` record lifetime
The `runnerRegistrationToken` entry in the [`values.yaml` file](https://gitlab.com/gitlab-org/charts/gitlab-runner/-/blob/a70bc29a903b79d5675bb0c45d981adf8b7a8659/values.yaml#L52)
will be retired. The `runnerRegistrationToken` entry will be replaced by the existing `runnerToken` value, which will be passed
to the new `gitlab-runner deploy` command in [`configmap.yaml`](https://gitlab.com/gitlab-org/charts/gitlab-runner/-/blob/a70bc29a903b79d5675bb0c45d981adf8b7a8659/templates/configmap.yaml#L116).
New records are created when the runner pings the GitLab instance for new jobs, if a record matching
the `token`+`system_id` does not already exist.
Due to the time-decaying nature of the `ci_runner_machines` records, they are automatically
cleaned after 7 days after the last contact from the respective runner.
### Required adaptations
#### Migration to `ci_runner_machines` table
When details from `ci_runner_machines` are needed, we need to fall back to the existing fields in
`ci_runner` if a match is not found in `ci_runner_machines`.
#### REST API
API endpoints receiving runner tokens should be changed to also take an optional
`system_id` parameter, sent alongside with the runner token (most often as a JSON parameter on the
request body).
#### GraphQL `CiRunner` type
The [`CiRunner` type](../../../api/graphql/reference/index.md#cirunner) closely reflects the
`ci_runners` model. This means that machine information such as `ipAddress`, `architectureName`,
and `executorName` among others are no longer singular values in the proposed approach.
We can live with that fact for the time being and start returning lists of unique values, separated
by commas.
The respective `CiRunner` fields must return the values for the `ci_runner_machines` entries
(falling back to `ci_runner` record if non-existent).
#### Stale runner cleanup
The functionality to
[clean up stale runners](../../../ci/runners/configure_runners.md#clean-up-stale-runners) needs
to be adapted to clean up `ci_runner_machines` records instead of `ci_runners` records.
At some point after the removal of the registration token support, we'll want to create a background
migration to clean up stale runners that have been created with a registration token (leveraging the
enum column created in the `ci_runners` table.
### Runner creation through API
@ -176,21 +292,66 @@ using PAT tokens for example - such that every runner is associated with an owne
## Implementation plan
### Stage 1 - Deprecations
| Component | Milestone | Changes |
|------------------|-----------|---------|
| GitLab Rails app | `15.x` (latest at `15.6`) | Deprecate `POST /api/v4/runners` endpoint for `16.0`. This hinges on a [proposal](https://gitlab.com/gitlab-org/gitlab/-/issues/373774) to allow deprecating REST API endpoints for security reasons. |
| GitLab Runner | `15.x` (latest at `15.8`) | Add deprecation notice for `register` command for `16.0`. |
| GitLab Runner | `15.x` | Ensure all runner entries in `config.toml` have unique system identifier values assigned. Log new system ID values with `INFO` level as they get created. |
| GitLab Runner | `15.x` | Start additionally logging unique system ID anywhere we log the runner short SHA. |
| GitLab Rails app | `15.x` | Create database migrations to add settings from `application_settings` and `namaspace_settings` tables. |
| GitLab Runner | `15.x` | Start sending `unique_id` value in `POST /jobs/request` request and other follow-up requests that require identifying the unique system. |
| GitLab Runner | `15.x` | Implement new user-authenticated API (REST and GraphQL) to create a new runner. |
| GitLab Rails app | `15.x` | Implement UI to create new runner. |
| GitLab Runner | `16.0` | Remove `register` command and support for `POST /runners` endpoint. |
| GitLab Rails app | `16.0` | Remove legacy UI showing registration with a registration token. |
| GitLab Rails app | `16.0` | Create database migrations to remove settings from `application_settings` and `namaspace_settings` tables. |
| GitLab Rails app | `16.0` | Make [`POST /api/v4/runners` endpoint](../../../api/runners.md#register-a-new-runner-deprecated) permanently return `410 Gone`. A future v5 version of the API would return `404 Not Found`. |
| GitLab Rails app | `16.0` | Start refusing job requests that don't include a unique ID. |
|------------------|----------:|---------|
| GitLab Rails app | `15.6` | Deprecate `POST /api/v4/runners` endpoint for `17.0`. This hinges on a [proposal](https://gitlab.com/gitlab-org/gitlab/-/issues/373774) to allow deprecating REST API endpoints for security reasons. |
| GitLab Runner | `15.6` | Add deprecation notice for `register` command for `17.0`. |
| GitLab Runner Helm Chart | `15.6` | Add deprecation notice for `runnerRegistrationToken` command for `17.0`. |
| GitLab Runner Operator | `15.6` | Add deprecation notice for `runner-registration-token` command for `17.0`. |
| GitLab Runner / GitLab Rails app | `15.7` | Add deprecation notice for registration token reset for `17.0`. |
### Stage 2 - Prepare `gitlab-runner` for `system_id`
| Component | Milestone | Changes |
|------------------|----------:|---------|
| GitLab Runner | `15.x` | Ensure a sidecar TOML file exists with a `system_id` value.<br/>Log new system ID values with `INFO` level as they get assigned. |
| GitLab Runner | `15.x` | Log unique system ID in the build logs. |
| GitLab Runner | `15.x` | Label Prometheus metrics with unique system ID. |
| GitLab Runner | `15.x` | Prepare `register` command to fail if runner server-side configuration options are passed together with a new `glrt-` token. |
### Stage 3 - Database changes
| Component | Milestone | Changes |
|------------------|----------:|---------|
| GitLab Rails app | | Create database migration to add columns to `ci_runners` table. |
| GitLab Rails app | | Create database migration to add `ci_runner_machines` table. |
| GitLab Rails app | | Create database migration to add `ci_runner_machines.machine_id` foreign key to `ci_builds_runner_session` table. |
| GitLab Rails app | | Create database migrations to add `allow_runner_registration_token` setting to `application_settings` and `namespace_settings` tables (default: `true`). |
| GitLab Runner | | Use runner token + `system_id` JSON parameters in `POST /jobs/request` request in the [heartbeat request](https://gitlab.com/gitlab-org/gitlab/blob/c73c96a8ffd515295842d72a3635a8ae873d688c/lib/api/ci/helpers/runner.rb#L14-20) to update the `ci_runner_machines` cache/table. |
| GitLab Runner | | Start sending `system_id` value in `POST /jobs/request` request and other follow-up requests that require identifying the unique system. |
| GitLab Rails app | | Create service similar to `StaleGroupRunnersPruneCronWorker` service to clean up `ci_runner_machines` records instead of `ci_runners` records.<br/>Existing service continues to exist but focuses only on legacy runners. |
### Stage 4 - New UI
| Component | Milestone | Changes |
|------------------|----------:|---------|
| GitLab Runner | | Implement new GraphQL user-authenticated API to create a new runner. |
| GitLab Runner | | [Add prefix to newly generated runner authentication tokens](https://gitlab.com/gitlab-org/gitlab/-/issues/383198). |
| GitLab Rails app | | Implement UI to create new runner. |
| GitLab Rails app | | GraphQL changes to `CiRunner` type. |
| GitLab Rails app | | UI changes to runner details view (listing of platform, architecture, IP address, etc.) (?) |
### Stage 5 - Optional disabling of registration token
| Component | Milestone | Changes |
|------------------|----------:|---------|
| GitLab Rails app | | Add UI to allow disabling use of registration tokens at project or group level. |
| GitLab Rails app | `16.0` | Introduce `:disable_runner_registration_tokens` feature flag (enabled by default) to control whether use of registration tokens is allowed. |
| GitLab Rails app | | Make [`POST /api/v4/runners` endpoint](../../../api/runners.md#register-a-new-runner) permanently return `HTTP 410 Gone` if either `allow_runner_registration_token` setting or `:disable_runner_registration_tokens` feature flag disables registration tokens.<br/>A future v5 version of the API should return `HTTP 404 Not Found`. |
| GitLab Rails app | | Start refusing job requests that don't include a unique ID, if either `allow_runner_registration_token` setting or `:disable_runner_registration_tokens` feature flag disables registration tokens. |
| GitLab Rails app | | Hide legacy UI showing registration with a registration token, if `:disable_runner_registration_tokens` feature flag disables registration tokens. |
### Stage 6 - Removals
| Component | Milestone | Changes |
|------------------|----------:|---------|
| GitLab Rails app | `17.0` | Remove legacy UI showing registration with a registration token. |
| GitLab Runner | `17.0` | Remove runner model arguments from `register` command (for example `--run-untagged`, `--tag-list`, etc.) |
| GitLab Rails app | `17.0` | Create database migrations to drop `allow_runner_registration_token` setting columns from `application_settings` and `namespace_settings` tables. |
| GitLab Rails app | `17.0` | Create database migrations to drop:<br/>- `runners_registration_token`/`runners_registration_token_encrypted` columns from `application_settings`;<br/>- `runners_token`/`runners_token_encrypted` from `namespaces` table;<br/>- `runners_token`/`runners_token_encrypted` from `projects` table. |
| GitLab Rails app | `17.0` | Remove `:disable_runner_registration_tokens` feature flag. |
## Status

View File

@ -2340,12 +2340,12 @@ In this example:
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/14311) in GitLab 12.7.
Use `needs:project` to download artifacts from up to five jobs in other pipelines.
The artifacts are downloaded from the latest successful pipeline for the specified ref.
The artifacts are downloaded from the latest successful specified job for the specified ref.
To specify multiple jobs, add each as separate array items under the `needs` keyword.
If there is a pipeline running for the specified ref, a job with `needs:project`
does not wait for the pipeline to complete. Instead, the job downloads the artifact
from the latest pipeline that completed successfully.
If there is a pipeline running for the ref, a job with `needs:project`
does not wait for the pipeline to complete. Instead, the artifacts are downloaded
from the latest successful run of the specified job.
`needs:project` must be used with `job`, `ref`, and `artifacts`.

View File

@ -152,6 +152,34 @@ For non-200 HTTP responses, use the provided helpers in `lib/api/helpers.rb` to
For `DELETE` requests, you should also generally use the `destroy_conditionally!` helper which by default returns a `204 No Content` response on success, or a `412 Precondition Failed` response if the given `If-Unmodified-Since` header is out of range. This helper calls `#destroy` on the passed resource, but you can also implement a custom deletion method by passing a block.
## Choosing HTTP verbs
When defining a new [API route](https://github.com/ruby-grape/grape#routes), use
the correct [HTTP request method](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods).
### Deciding between `PATCH` and `PUT`
In a Rails application, both the `PATCH` and `PUT` request methods are routed to
the `update` method in controllers. With Grape, the framework we use to write
the GitLab API, you must explicitly set the `PATCH` or `PUT` HTTP verb for an
endpoint that does updates.
If the endpoint updates *all* attributes of a given resource, use the
[`PUT`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/PUT) request
method. If the endpoint updates *some* attributes of a given resource, use the
[`PATCH`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/PATCH)
request method.
Here is a good example for `PATCH`: [`PATCH /projects/:id/protected_branches/:name`](../api/protected_branches.md#update-a-protected-branch)
Here is a good example for `PUT`: [`PUT /projects/:id/merge_requests/:merge_request_iid/approve`](../api/merge_request_approvals.md#approve-merge-request)
Often, a good `PUT` endpoint only has ids and a verb (in the example above, "approve").
Or, they only have a single value and represent a key/value pair.
The [Rails blog](https://rubyonrails.org/2012/2/26/edge-rails-patch-is-the-new-primary-http-method-for-updates)
has a detailed explanation of why `PATCH` is usually the most apt verb for web
API endpoints that perform an update.
## Using API path helpers in GitLab Rails codebase
Because we support [installing GitLab under a relative URL](../install/relative_url.md), one must take this

View File

@ -289,10 +289,47 @@ To disable a feature flag that has been enabled for a specific project you can r
/chatops run feature set --project=gitlab-org/gitlab some_feature false
```
You cannot selectively disable feature flags for a specific project/group/user without applying a [specific method of implementing](index.md#selectively-disable-by-actor) the feature flags.
You cannot selectively disable feature flags for a specific project/group/user without applying a [specific method of implementing](controls.md#selectively-disable-by-actor) the feature flags.
If a feature flag is disabled via ChatOps, that will take precedence over the `default_enabled` value in the YAML. In other words, you could have a feature enabled for on-premise installations but not for GitLab.com.
#### Selectively disable by actor
By default you cannot selectively disable a feature flag by actor.
```shell
# This will not work how you would expect.
/chatops run feature set some_feature true
/chatops run feature set --project=gitlab-org/gitlab some_feature false
```
However, if you add two feature flags, you can write your conditional statement in such a way that the equivalent selective disable is possible.
```ruby
Feature.enabled?(:a_feature, project) && Feature.disabled?(:a_feature_override, project)
```
```shell
# This will enable a feature flag globally, except for gitlab-org/gitlab
/chatops run feature set a_feature true
/chatops run feature set --project=gitlab-org/gitlab a_feature_override true
```
#### Percentage-based actor selection
When using the percentage rollout of actors on multiple feature flags, the actors for each feature flag are selected separately.
For example, the following feature flags are enabled for a certain percentage of actors:
```plaintext
/chatops run feature set feature-set-1 25 --actors
/chatops run feature set feature-set-2 25 --actors
```
If a project A has `:feature-set-1` enabled, there is no guarantee that project A also has `:feature-set-2` enabled.
For more detail, see [This is how percentages work in Flipper](https://www.hackwithpassion.com/this-is-how-percentages-work-in-flipper/).
### Feature flag change logging
#### ChatOps level

View File

@ -398,44 +398,8 @@ Feature.enabled?(:feature_flag, group)
Feature.enabled?(:feature_flag, user)
```
Please see [Feature flag controls](controls.md#process) for more details on working with feature flags.
#### Selectively disable by actor
By default you cannot selectively disable a feature flag by actor.
```shell
# This will not work how you would expect.
/chatops run feature set some_feature true
/chatops run feature set --project=gitlab-org/gitlab some_feature false
```
However, if you add two feature flags, you can write your conditional statement in such a way that the equivalent selective disable is possible.
```ruby
Feature.enabled?(:a_feature, project) && Feature.disabled?(:a_feature_override, project)
```
```shell
# This will enable a feature flag globally, except for gitlab-org/gitlab
/chatops run feature set a_feature true
/chatops run feature set --project=gitlab-org/gitlab a_feature_override true
```
#### Percentage-based actor selection
When using the percentage rollout of actors on multiple feature flags, the actors for each feature flag are selected separately.
For example, the following feature flags are enabled for a certain percentage of actors:
```plaintext
/chatops run feature set feature-set-1 25 --actors
/chatops run feature set feature-set-2 25 --actors
```
If a project A has `:feature-set-1` enabled, there is no guarantee that project A also has `:feature-set-2` enabled.
For more detail, see [This is how percentages work in Flipper](https://www.hackwithpassion.com/this-is-how-percentages-work-in-flipper/).
See [Feature flags in the development of GitLab](controls.md#process) for details on how to use ChatOps
to selectively enable or disable feature flags in GitLab-provided environments, like staging and production.
#### Use actors for verifying in production

View File

@ -207,12 +207,12 @@ From GitLab 13.6, users can [specify any runner configuration in the GitLab Runn
</div>
<div class="deprecation removal-160 breaking-change">
<div class="deprecation removal-170 breaking-change">
### GitLab Runner registration token in Runner Operator
End of Support: GitLab <span class="removal-milestone">16.0</span> (2023-05-22)<br />
Planned removal: GitLab <span class="removal-milestone">16.0</span> (2023-05-22)
Planned removal: GitLab <span class="removal-milestone">17.0</span> (2024-04-22)
WARNING:
This is a [breaking change](https://docs.gitlab.com/ee/development/deprecation_guidelines/).
@ -222,56 +222,57 @@ The [`runner-registration-token`](https://docs.gitlab.com/runner/install/operato
</div>
<div class="deprecation removal-160 breaking-change">
<div class="deprecation removal-170 breaking-change">
### `POST /api/v4/runners` method to register runners
### Registration tokens and server-side runner arguments in `POST /api/v4/runners` endpoint
End of Support: GitLab <span class="removal-milestone">16.0</span> (2023-05-22)<br />
Planned removal: GitLab <span class="removal-milestone">16.0</span> (2023-05-22)
Planned removal: GitLab <span class="removal-milestone">17.0</span> (2024-04-22)
WARNING:
This is a [breaking change](https://docs.gitlab.com/ee/development/deprecation_guidelines/).
Review the details carefully before upgrading.
The `POST` method operation on the `/api/v4/runners` endpoint is deprecated.
This endpoint and method [registers](https://docs.gitlab.com/ee/api/runners.html#register-a-new-runner) a runner
with a GitLab instance at the instance, group, or project level through the API. We plan to remove this endpoint
and method in GitLab 16.0.
The support for registration tokens and certain runner configuration arguments in the `POST` method operation on the `/api/v4/runners` endpoint is deprecated.
This endpoint [registers](https://docs.gitlab.com/ee/api/runners.html#register-a-new-runner) a runner
with a GitLab instance at the instance, group, or project level through the API. We plan to remove the support for
registration tokens and certain configuration arguments in this endpoint in GitLab 17.0.
In GitLab 15.8, we plan to implement a new method to bind runners to a GitLab instance,
as part of the new [GitLab Runner token architecture](https://docs.gitlab.com/ee/architecture/blueprints/runner_tokens/).
This new architecture introduces a new method for registering runners and will eliminate the legacy
[runner registration token](https://docs.gitlab.com/ee/security/token_overview.html#runner-registration-tokens).
From GitLab 16.0 and later, the runner registration methods implemented by the new GitLab Runner token architecture will be the only supported methods.
From GitLab 17.0 and later, the runner registration methods implemented by the new GitLab Runner token architecture will be the only supported methods.
</div>
<div class="deprecation removal-160 breaking-change">
<div class="deprecation removal-170 breaking-change">
### `gitlab-runner register` command
### Registration tokens and server-side runner arguments in `gitlab-runner register` command
End of Support: GitLab <span class="removal-milestone">16.0</span> (2023-05-22)<br />
Planned removal: GitLab <span class="removal-milestone">16.0</span> (2023-05-22)
Planned removal: GitLab <span class="removal-milestone">17.0</span> (2024-04-22)
WARNING:
This is a [breaking change](https://docs.gitlab.com/ee/development/deprecation_guidelines/).
Review the details carefully before upgrading.
The command to [register](https://docs.gitlab.com/runner/register/) a runner, `gitlab-runner register` is deprecated.
The support for registration tokens and certain configuration arguments in the command to [register](https://docs.gitlab.com/runner/register/) a runner, `gitlab-runner register` is deprecated.
GitLab plans to introduce a new [GitLab Runner token architecture](https://docs.gitlab.com/ee/architecture/blueprints/runner_tokens/) in GitLab 15.8,
which introduces a new method for registering runners and eliminates the legacy
[runner registration token](https://docs.gitlab.com/ee/security/token_overview.html#runner-registration-tokens).
The new method will involve passing a [runner authentication token](https://docs.gitlab.com/ee/security/token_overview.html#runner-authentication-tokens-also-called-runner-tokens)
to a new `gitlab-runner deploy` command.
The new method will involve creating the runner in the GitLab UI and passing the
[runner authentication token](https://docs.gitlab.com/ee/security/token_overview.html#runner-authentication-tokens-also-called-runner-tokens)
to the `gitlab-runner register` command.
</div>
<div class="deprecation removal-160 breaking-change">
<div class="deprecation removal-170 breaking-change">
### `runnerRegistrationToken` parameter for GitLab Runner Helm Chart
End of Support: GitLab <span class="removal-milestone">16.0</span> (2023-05-22)<br />
Planned removal: GitLab <span class="removal-milestone">16.0</span> (2023-05-22)
Planned removal: GitLab <span class="removal-milestone">17.0</span> (2024-04-22)
WARNING:
This is a [breaking change](https://docs.gitlab.com/ee/development/deprecation_guidelines/).
@ -281,9 +282,9 @@ The [`runnerRegistrationToken`](https://docs.gitlab.com/runner/install/kubernete
As part of the new [GitLab Runner token architecture](https://docs.gitlab.com/ee/architecture/blueprints/runner_tokens/), in GitLab 15.8 we plan to introduce:
- A new method to bind runners to a GitLab instance.
- A new method to bind runners to a GitLab instance leveraging `runnerToken`.
- A unique system ID saved to the `config.toml`, which will ensure traceability between jobs and runners.
From GitLab 16.0 and later, the methods to register runners introduced by the new GitLab Runner token architecture will be the only supported methods.
From GitLab 17.0 and later, the methods to register runners introduced by the new GitLab Runner token architecture will be the only supported methods.
</div>

View File

@ -25,7 +25,8 @@ Otherwise, to add your license:
1. Select **Add license**.
NOTE:
In GitLab 14.1.x through 14.10.x, you can access the **Add License** page directly from the URL, `<YourGitLabURL>/admin/license/new`. In GitLab 15.0 and later, the path is `<YourGitLabURL>/admin/subscription`.
In GitLab 14.7.x to 14.9.x, you can add the license file with the UI.
In GitLab 14.1.x to 14.7, if you have already activated your subscription with an activation code, you cannot access **Add License** from the Admin Area. You must access **Add License** directly from the URL, `<YourGitLabURL>/admin/license/new`.
## Add your license file during installation

View File

@ -144,7 +144,7 @@ rule in the defined policy are met.
| `site_profile` | `string` | Name of the selected [DAST site profile](../dast/proxy-based.md#site-profile). | The DAST site profile to execute the DAST scan. This field should only be set if `scan` type is `dast`. |
| `scanner_profile` | `string` or `null` | Name of the selected [DAST scanner profile](../dast/proxy-based.md#scanner-profile). | The DAST scanner profile to execute the DAST scan. This field should only be set if `scan` type is `dast`.|
| `variables` | `object` | | A set of CI variables, supplied as an array of `key: value` pairs, to apply and enforce for the selected scan. The `key` is the variable name, with its `value` provided as a string. This parameter supports any variable that the GitLab CI job supports for the specified scan. |
| `tags` | `array` of `string` | | A list of runner tags for the policy. The policy jobs will be run by runner with the specified tags. |
| `tags` | `array` of `string` | | A list of runner tags for the policy. The policy jobs will be run by runner with the specified tags. Tags are not supported for the `sast` and `dependency_scanning` scan types because they use the default template and run in a child pipeline. |
Note the following:

View File

@ -7,7 +7,6 @@ module API
before do
authenticate!
feature_flag_enabled?
authorize! :read_secure_files, user_project
end
@ -113,10 +112,6 @@ module API
end
helpers do
def feature_flag_enabled?
service_unavailable! unless Feature.enabled?(:ci_secure_files, user_project)
end
def read_only_feature_flag_enabled?
service_unavailable! if Feature.enabled?(:ci_secure_files_read_only, user_project, type: :ops)
end

View File

@ -90,7 +90,8 @@ module API
]
failure [
{ code: 403, message: 'Forbidden' },
{ code: 422, message: 'Validation failure' }
{ code: 422, message: 'Validation failure' },
{ code: 413, message: 'Request Entity Too Large' }
]
tags %w[terraform_state]
end
@ -101,6 +102,9 @@ module API
data = request.body.read
no_content! if data.empty?
max_state_size = Gitlab::CurrentSettings.max_terraform_state_size_bytes
file_too_large! if max_state_size > 0 && data.size > max_state_size
remote_state_handler.handle_with_lock do |state|
state.update_file!(CarrierWaveStringFile.new(data), version: params[:serial], build: current_authenticated_job)
end

View File

@ -10,6 +10,7 @@ module Gitlab
def initialize(logger: Gitlab::AppLogger)
@logger = logger
init_prometheus_metrics
end
def started(labels = {})
@ -21,13 +22,13 @@ module Gitlab
end
def threshold_violated(monitor_name)
counter_violations.increment(reason: monitor_name)
@counter_violations.increment(reason: monitor_name)
end
def strikes_exceeded(monitor_name, labels = {})
logger.warn(log_labels(labels))
counter_violations_handled.increment(reason: monitor_name)
@counter_violations_handled.increment(reason: monitor_name)
end
private
@ -48,24 +49,18 @@ module Gitlab
::Prometheus::PidProvider.worker_id
end
def counter_violations
strong_memoize("counter_violations") do
::Gitlab::Metrics.counter(
:gitlab_memwd_violations_total,
'Total number of times a Ruby process violated a memory threshold',
{ pid: worker_id }
)
end
end
def counter_violations_handled
strong_memoize("counter_violations_handled") do
::Gitlab::Metrics.counter(
:gitlab_memwd_violations_handled_total,
'Total number of times Ruby process memory violations were handled',
{ pid: worker_id }
)
end
def init_prometheus_metrics
default_labels = { pid: worker_id }
@counter_violations = Gitlab::Metrics.counter(
:gitlab_memwd_violations_total,
'Total number of times a Ruby process violated a memory threshold',
default_labels
)
@counter_violations_handled = Gitlab::Metrics.counter(
:gitlab_memwd_violations_handled_total,
'Total number of times Ruby process memory violations were handled',
default_labels
)
end
end
end

View File

@ -10,6 +10,7 @@ module Gitlab
def initialize(logger: ::Sidekiq.logger)
@event_reporter = EventReporter.new(logger: logger)
@sidekiq_daemon_monitor = Gitlab::SidekiqDaemon::Monitor.instance
init_prometheus_metrics
end
@ -26,16 +27,12 @@ module Gitlab
attr_reader :event_reporter
def fetch_running_jobs
running_jobs = []
Gitlab::SidekiqDaemon::Monitor.instance.with_running_jobs do |jobs|
running_jobs = jobs.map do |jid, job|
{
jid: jid,
worker_class: job[:worker_class].name
}
end
@sidekiq_daemon_monitor.jobs.map do |jid, job|
{
jid: jid,
worker_class: job[:worker_class].name
}
end
running_jobs
end
def increment_worker_counters(running_jobs)

View File

@ -35,6 +35,7 @@ module Gitlab
@enabled = true
@metrics = init_metrics
@sidekiq_daemon_monitor = Gitlab::SidekiqDaemon::Monitor.instance
end
private
@ -193,17 +194,12 @@ module Gitlab
end
def fetch_running_jobs
jobs = []
Gitlab::SidekiqDaemon::Monitor.instance.jobs_mutex.synchronize do
jobs = Gitlab::SidekiqDaemon::Monitor.instance.jobs.map do |jid, job|
{
jid: jid,
worker_class: job[:worker_class].name
}
end
@sidekiq_daemon_monitor.jobs.map do |jid, job|
{
jid: jid,
worker_class: job[:worker_class].name
}
end
jobs
end
def out_of_range_description(rss, hard_limit, soft_limit, deadline_exceeded)
@ -269,10 +265,8 @@ module Gitlab
end
def rss_increase_by_jobs
Gitlab::SidekiqDaemon::Monitor.instance.jobs_mutex.synchronize do
Gitlab::SidekiqDaemon::Monitor.instance.jobs.sum do |job|
rss_increase_by_job(job)
end
@sidekiq_daemon_monitor.jobs.sum do |_, job|
rss_increase_by_job(job)
end
end
@ -297,7 +291,7 @@ module Gitlab
end
def any_jobs?
Gitlab::SidekiqDaemon::Monitor.instance.jobs.any?
@sidekiq_daemon_monitor.jobs.any?
end
end
end

View File

@ -15,9 +15,6 @@ module Gitlab
# that should not be caught by application
CancelledError = Class.new(Exception) # rubocop:disable Lint/InheritException
attr_reader :jobs
attr_reader :jobs_mutex
def initialize
super
@ -31,8 +28,8 @@ module Gitlab
end
def within_job(worker_class, jid, queue)
jobs_mutex.synchronize do
jobs[jid] = { worker_class: worker_class, thread: Thread.current, started_at: Gitlab::Metrics::System.monotonic_time }
@jobs_mutex.synchronize do
@jobs[jid] = { worker_class: worker_class, thread: Thread.current, started_at: Gitlab::Metrics::System.monotonic_time }
end
if cancelled?(jid)
@ -48,8 +45,8 @@ module Gitlab
yield
ensure
jobs_mutex.synchronize do
jobs.delete(jid)
@jobs_mutex.synchronize do
@jobs.delete(jid)
end
end
@ -65,9 +62,9 @@ module Gitlab
end
end
def with_running_jobs
def jobs
@jobs_mutex.synchronize do
yield @jobs.dup
@jobs.dup
end
end
@ -172,14 +169,14 @@ module Gitlab
# This is why it passes thread in block,
# to ensure that we do process this thread
def find_thread_unsafe(jid)
jobs.dig(jid, :thread)
@jobs.dig(jid, :thread)
end
def find_thread_with_lock(jid)
# don't try to lock if we cannot find the thread
return unless find_thread_unsafe(jid)
jobs_mutex.synchronize do
@jobs_mutex.synchronize do
find_thread_unsafe(jid).tap do |thread|
yield(thread) if thread
end

View File

@ -31420,6 +31420,9 @@ msgstr ""
msgid "ProductAnalytics|An error occured while loading the %{widgetTitle} widget."
msgstr ""
msgid "ProductAnalytics|An error occurred while fetching data. Refresh the page to try again."
msgstr ""
msgid "ProductAnalytics|Audience"
msgstr ""

View File

@ -174,7 +174,7 @@ module ReviewApps
private
attr_reader :api_endpoint, :dry_run, :environments_not_found_count, :gitlab_token, :project_path
attr_reader :api_endpoint, :dry_run, :gitlab_token, :project_path
def fetch_environment(environment)
gitlab.environment(project_path, environment.id)
@ -190,9 +190,9 @@ module ReviewApps
rescue Gitlab::Error::NotFound
puts "Review app '#{environment.name}' / '#{environment.slug}' (##{environment.id}) was not found: ignoring it"
environments_not_found_count += 1
@environments_not_found_count += 1
if environments_not_found_count >= ENVIRONMENTS_NOT_FOUND_THRESHOLD
if @environments_not_found_count >= ENVIRONMENTS_NOT_FOUND_THRESHOLD
raise "At least #{ENVIRONMENTS_NOT_FOUND_THRESHOLD} environments were missing when we tried to delete them. Please investigate"
end
rescue Gitlab::Error::Forbidden

View File

@ -12,31 +12,10 @@ RSpec.describe 'Secure Files', :js, feature_category: :projects do
sign_in(user)
end
context 'when the :ci_secure_files feature flag is enabled' do
before do
stub_feature_flags(ci_secure_files: true)
context 'authenticated user with admin permissions' do
it 'shows the secure files settings' do
visit project_settings_ci_cd_path(project)
end
context 'authenticated user with admin permissions' do
it 'shows the secure files settings' do
expect(page).to have_content('Secure Files')
end
end
end
context 'when the :ci_secure_files feature flag is disabled' do
before do
stub_feature_flags(ci_secure_files: false)
visit project_settings_ci_cd_path(project)
end
context 'authenticated user with admin permissions' do
it 'does not shows the secure files settings' do
expect(page).not_to have_content('Secure Files')
end
expect(page).to have_content('Secure Files')
end
end

View File

@ -77,7 +77,9 @@ jest.mock('~/lib/utils/url_utility', () => ({
Vue.use(VueApollo);
Vue.use(GlToast);
const COUNT_QUERIES = 7; // 4 tabs + 3 status queries
const STATUS_COUNT_QUERIES = 3;
const TAB_COUNT_QUERIES = 4;
const COUNT_QUERIES = TAB_COUNT_QUERIES + STATUS_COUNT_QUERIES;
describe('AdminRunnersApp', () => {
let wrapper;
@ -170,6 +172,29 @@ describe('AdminRunnersApp', () => {
});
});
describe('does not show total runner counts when total is 0', () => {
beforeEach(async () => {
mockRunnersCountHandler.mockResolvedValue({
data: {
runners: {
count: 0,
...runnersCountData.runners,
},
},
});
await createComponent({ mountFn: mountExtended });
});
it('fetches only tab counts', () => {
expect(mockRunnersCountHandler).toHaveBeenCalledTimes(TAB_COUNT_QUERIES);
});
it('does not shows counters', () => {
expect(findRunnerStats().text()).toBe('');
});
});
it('shows the runners list', async () => {
await createComponent();

View File

@ -16,6 +16,23 @@ describe('RunnerStats', () => {
const findSingleStats = () => wrapper.findAllComponents(RunnerSingleStat);
const RunnerCountStub = {
props: ['variables'],
render() {
// return a count for each status
const mockCounts = {
undefined: 6, // no status returns "all"
[STATUS_ONLINE]: 3,
[STATUS_OFFLINE]: 2,
[STATUS_STALE]: 1,
};
return this.$scopedSlots.default({
count: mockCounts[this.variables.status],
});
},
};
const createComponent = ({ props = {}, mountFn = shallowMount, ...options } = {}) => {
wrapper = mountFn(RunnerStats, {
propsData: {
@ -23,6 +40,9 @@ describe('RunnerStats', () => {
variables: {},
...props,
},
stubs: {
RunnerCount: RunnerCountStub,
},
...options,
});
};
@ -32,24 +52,8 @@ describe('RunnerStats', () => {
});
it('Displays all the stats', () => {
const mockCounts = {
[STATUS_ONLINE]: 3,
[STATUS_OFFLINE]: 2,
[STATUS_STALE]: 1,
};
createComponent({
mountFn: mount,
stubs: {
RunnerCount: {
props: ['variables'],
render() {
return this.$scopedSlots.default({
count: mockCounts[this.variables.status],
});
},
},
},
});
const text = wrapper.text();
@ -78,4 +82,21 @@ describe('RunnerStats', () => {
expect(stat.props('variables')).toMatchObject(mockVariables);
});
});
it('Does not display counts when total is 0', () => {
createComponent({
mountFn: mount,
stubs: {
RunnerCount: {
render() {
return this.$scopedSlots.default({
count: 0,
});
},
},
},
});
expect(wrapper.html()).toBe('');
});
});

View File

@ -24,6 +24,7 @@ describe('~/vue_merge_request_widget/components/widget/widget.vue', () => {
const findActionButtons = () => wrapper.findComponent(ActionButtons);
const findToggleButton = () => wrapper.findByTestId('toggle-button');
const findHelpPopover = () => wrapper.findComponent(HelpPopover);
const findDynamicScroller = () => wrapper.findByTestId('dynamic-content-scroller');
const createComponent = ({ propsData, slots } = {}) => {
wrapper = shallowMountExtended(Widget, {
@ -411,4 +412,30 @@ describe('~/vue_merge_request_widget/components/widget/widget.vue', () => {
expect(wrapper.vm.telemetryHub).toBe(null);
});
});
describe('dynamic content', () => {
const content = [
{
id: 'row-id',
header: ['This is a header', 'This is a subheader'],
text: 'Main text for the row',
subtext: 'Optional: Smaller sub-text to be displayed below the main text',
},
];
beforeEach(() => {
createComponent({
propsData: {
isCollapsible: true,
content,
},
});
});
it('uses a dynamic scroller to show the items', async () => {
findToggleButton().vm.$emit('click');
await waitForPromises();
expect(findDynamicScroller().props('items')).toEqual(content);
});
});
});

View File

@ -19,58 +19,40 @@ RSpec.describe Ci::SecureFilesHelper do
subject { helper.show_secure_files_setting(project, user) }
describe '#show_secure_files_setting' do
context 'when the :ci_secure_files feature flag is enabled' do
before do
stub_feature_flags(ci_secure_files: true)
end
context 'authenticated user with admin permissions' do
let(:user) { maintainer }
context 'authenticated user with admin permissions' do
let(:user) { maintainer }
it { is_expected.to be true }
end
context 'authenticated user with read permissions' do
let(:user) { developer }
it { is_expected.to be true }
end
context 'authenticated user with guest permissions' do
let(:user) { guest }
it { is_expected.to be false }
end
context 'authenticated user with no permissions' do
let(:user) { anonymous }
it { is_expected.to be false }
end
context 'unconfirmed user' do
let(:user) { unconfirmed }
it { is_expected.to be false }
end
context 'unauthenticated user' do
let(:user) { nil }
it { is_expected.to be false }
end
it { is_expected.to be true }
end
context 'when the :ci_secure_files feature flag is disabled' do
before do
stub_feature_flags(ci_secure_files: false)
end
context 'authenticated user with read permissions' do
let(:user) { developer }
context 'authenticated user with admin permissions' do
let(:user) { maintainer }
it { is_expected.to be true }
end
it { is_expected.to be false }
end
context 'authenticated user with guest permissions' do
let(:user) { guest }
it { is_expected.to be false }
end
context 'authenticated user with no permissions' do
let(:user) { anonymous }
it { is_expected.to be false }
end
context 'unconfirmed user' do
let(:user) { unconfirmed }
it { is_expected.to be false }
end
context 'unauthenticated user' do
let(:user) { nil }
it { is_expected.to be false }
end
end
end

View File

@ -24,6 +24,7 @@ RSpec.describe Gitlab::Memory::Watchdog::SidekiqEventReporter, feature_category:
let(:queue) { 'default' }
let(:jid) { SecureRandom.hex }
let(:running_jobs) { { jid => { worker_class: DummyWorker } } }
let(:sidekiq_daemon_monitor) { instance_double(Gitlab::SidekiqDaemon::Monitor) }
let(:worker) do
Class.new do
def self.name
@ -33,14 +34,15 @@ RSpec.describe Gitlab::Memory::Watchdog::SidekiqEventReporter, feature_category:
end
before do
stub_const("DummyWorker", worker)
stub_const('DummyWorker', worker)
allow(Gitlab::SidekiqDaemon::Monitor).to receive(:instance).and_return(sidekiq_daemon_monitor)
allow(::Gitlab::Metrics).to receive(:counter)
.with(:sidekiq_watchdog_running_jobs_total, anything)
.and_return(sidekiq_watchdog_running_jobs_counter)
allow(sidekiq_watchdog_running_jobs_counter).to receive(:increment)
allow(logger).to receive(:warn)
allow(Gitlab::SidekiqDaemon::Monitor.instance).to receive(:with_running_jobs).and_yield(running_jobs)
allow(sidekiq_daemon_monitor).to receive(:jobs).and_return(running_jobs)
end
it 'delegates #strikes_exceeded with correct arguments' do
@ -49,7 +51,7 @@ RSpec.describe Gitlab::Memory::Watchdog::SidekiqEventReporter, feature_category:
:monitor_name,
{
message: 'dummy_text',
running_jobs: running_jobs
running_jobs: [jid: jid, worker_class: 'DummyWorker']
}
)
end

View File

@ -4,11 +4,23 @@ require 'spec_helper'
RSpec.describe Gitlab::SidekiqDaemon::MemoryKiller do
let(:memory_killer) { described_class.new }
let(:sidekiq_daemon_monitor) { instance_double(Gitlab::SidekiqDaemon::Monitor) }
let(:running_jobs) { {} }
let(:pid) { 12345 }
let(:worker) do
Class.new do
def self.name
'DummyWorker'
end
end
end
before do
stub_const('DummyWorker', worker)
allow(Sidekiq.logger).to receive(:info)
allow(Sidekiq.logger).to receive(:warn)
allow(Gitlab::SidekiqDaemon::Monitor).to receive(:instance).and_return(sidekiq_daemon_monitor)
allow(sidekiq_daemon_monitor).to receive(:jobs).and_return(running_jobs)
allow(memory_killer).to receive(:pid).and_return(pid)
# make sleep no-op
@ -306,31 +318,37 @@ RSpec.describe Gitlab::SidekiqDaemon::MemoryKiller do
stub_const("#{described_class}::CHECK_INTERVAL_SECONDS", check_interval_seconds)
end
it 'send signal and return when all jobs finished' do
expect(Process).to receive(:kill).with(signal, pid).ordered
expect(Gitlab::Metrics::System).to receive(:monotonic_time).and_call_original
context 'when all jobs are finished' do
let(:running_jobs) { {} }
expect(memory_killer).to receive(:enabled?).and_return(true)
expect(memory_killer).to receive(:any_jobs?).and_return(false)
it 'send signal and return when all jobs finished' do
expect(Process).to receive(:kill).with(signal, pid).ordered
expect(Gitlab::Metrics::System).to receive(:monotonic_time).and_call_original
expect(memory_killer).not_to receive(:sleep)
expect(memory_killer).to receive(:enabled?).and_return(true)
subject
expect(memory_killer).not_to receive(:sleep)
subject
end
end
it 'send signal and wait till deadline if any job not finished' do
expect(Process).to receive(:kill)
.with(signal, pid)
.ordered
context 'when there are still running jobs' do
let(:running_jobs) { { 'jid1' => { worker_class: DummyWorker } } }
expect(Gitlab::Metrics::System).to receive(:monotonic_time)
.and_call_original
.at_least(:once)
it 'send signal and wait till deadline if any job not finished' do
expect(Process).to receive(:kill)
.with(signal, pid)
.ordered
expect(memory_killer).to receive(:enabled?).and_return(true).at_least(:once)
expect(memory_killer).to receive(:any_jobs?).and_return(true).at_least(:once)
expect(Gitlab::Metrics::System).to receive(:monotonic_time)
.and_call_original
.at_least(:once)
subject
expect(memory_killer).to receive(:enabled?).and_return(true).at_least(:once)
subject
end
end
end
@ -377,21 +395,11 @@ RSpec.describe Gitlab::SidekiqDaemon::MemoryKiller do
let(:jid) { 1 }
let(:reason) { 'rss out of range reason description' }
let(:queue) { 'default' }
let(:running_jobs) { [{ jid: jid, worker_class: 'DummyWorker' }] }
let(:metrics) { memory_killer.instance_variable_get(:@metrics) }
let(:worker) do
Class.new do
def self.name
'DummyWorker'
end
include ApplicationWorker
end
end
let(:metrics) { memory_killer.instance_variable_get(:@metrics) }
let(:running_jobs) { { jid => { worker_class: DummyWorker } } }
before do
stub_const("DummyWorker", worker)
allow(memory_killer).to receive(:get_rss_kb).and_return(*current_rss)
allow(memory_killer).to receive(:get_soft_limit_rss_kb).and_return(soft_limit_rss)
allow(memory_killer).to receive(:get_hard_limit_rss_kb).and_return(hard_limit_rss)
@ -413,15 +421,13 @@ RSpec.describe Gitlab::SidekiqDaemon::MemoryKiller do
hard_limit_rss: hard_limit_rss,
soft_limit_rss: soft_limit_rss,
reason: reason,
running_jobs: running_jobs,
running_jobs: [jid: jid, worker_class: 'DummyWorker'],
memory_total_kb: memory_total)
expect(metrics[:sidekiq_memory_killer_running_jobs]).to receive(:increment)
.with({ worker_class: "DummyWorker", deadline_exceeded: true })
Gitlab::SidekiqDaemon::Monitor.instance.within_job(DummyWorker, jid, queue) do
subject
end
subject
end
end
@ -464,21 +470,24 @@ RSpec.describe Gitlab::SidekiqDaemon::MemoryKiller do
end
describe '#rss_increase_by_jobs' do
let(:running_jobs) { { id1: 'job1', id2: 'job2' } }
let(:running_jobs) { { 'job1' => { worker_class: "Job1" }, 'job2' => { worker_class: "Job2" } } }
subject { memory_killer.send(:rss_increase_by_jobs) }
before do
allow(memory_killer).to receive(:rss_increase_by_job).and_return(11, 22)
end
it 'adds up individual rss_increase_by_job' do
allow(Gitlab::SidekiqDaemon::Monitor).to receive_message_chain(:instance, :jobs_mutex, :synchronize).and_yield
expect(Gitlab::SidekiqDaemon::Monitor).to receive_message_chain(:instance, :jobs).and_return(running_jobs)
expect(memory_killer).to receive(:rss_increase_by_job).and_return(11, 22)
expect(subject).to eq(33)
end
it 'return 0 if no job' do
allow(Gitlab::SidekiqDaemon::Monitor).to receive_message_chain(:instance, :jobs_mutex, :synchronize).and_yield
expect(Gitlab::SidekiqDaemon::Monitor).to receive_message_chain(:instance, :jobs).and_return({})
expect(subject).to eq(0)
context 'when there is no running job' do
let(:running_jobs) { {} }
it 'return 0 if no job' do
expect(subject).to eq(0)
end
end
end

View File

@ -6,9 +6,15 @@ RSpec.describe Gitlab::SidekiqDaemon::Monitor do
let(:monitor) { described_class.new }
describe '#within_job' do
it 'tracks thread' do
it 'tracks thread, jid and worker_class' do
blk = proc do
expect(monitor.jobs.dig('jid', :thread)).not_to be_nil
monitor.jobs do |jobs|
jobs.each do |jid, job|
expect(job[:thread]).not_to be_nil
expect(jid).to eq('jid')
expect(job[:worker_class]).to eq('worker_class')
end
end
"OK"
end
@ -37,13 +43,13 @@ RSpec.describe Gitlab::SidekiqDaemon::Monitor do
end
end
describe '#with_running_jobs' do
it 'yields with correct jobs' do
describe '#jobs' do
it 'returns running jobs hash' do
jid = SecureRandom.hex
running_jobs = { jid => hash_including(worker_class: 'worker_class') }
monitor.within_job('worker_class', jid, 'queue') do
expect { |b| monitor.with_running_jobs(&b) }.to yield_with_args(running_jobs)
expect(monitor.jobs).to match(running_jobs)
end
end
end
@ -231,7 +237,7 @@ RSpec.describe Gitlab::SidekiqDaemon::Monitor do
let(:thread) { Thread.new { sleep 1000 } }
before do
monitor.jobs[jid] = { worker_class: 'worker_class', thread: thread, started_at: Time.now.to_i }
allow(monitor).to receive(:find_thread_unsafe).with(jid).and_return(thread)
end
after do

View File

@ -2528,20 +2528,24 @@ RSpec.describe Ci::Build, feature_category: :continuous_integration do
end
describe '#ref_slug' do
{
'master' => 'master',
'1-foo' => '1-foo',
'fix/1-foo' => 'fix-1-foo',
'fix-1-foo' => 'fix-1-foo',
'a' * 63 => 'a' * 63,
'a' * 64 => 'a' * 63,
'FOO' => 'foo',
'-' + 'a' * 61 + '-' => 'a' * 61,
'-' + 'a' * 62 + '-' => 'a' * 62,
'-' + 'a' * 63 + '-' => 'a' * 62,
'a' * 62 + ' ' => 'a' * 62
}.each do |ref, slug|
it "transforms #{ref} to #{slug}" do
using RSpec::Parameterized::TableSyntax
where(:ref, :slug) do
'master' | 'master'
'1-foo' | '1-foo'
'fix/1-foo' | 'fix-1-foo'
'fix-1-foo' | 'fix-1-foo'
'a' * 63 | 'a' * 63
'a' * 64 | 'a' * 63
'FOO' | 'foo'
'-' + 'a' * 61 + '-' | 'a' * 61
'-' + 'a' * 62 + '-' | 'a' * 62
'-' + 'a' * 63 + '-' | 'a' * 62
'a' * 62 + ' ' | 'a' * 62
end
with_them do
it "transforms ref to slug" do
build.ref = ref
expect(build.ref_slug).to eq(slug)

View File

@ -31,23 +31,6 @@ RSpec.describe API::Ci::SecureFiles, feature_category: :pipeline_authoring do
end
describe 'GET /projects/:id/secure_files' do
context 'feature flag' do
it 'returns a 503 when the feature flag is disabled' do
stub_feature_flags(ci_secure_files: false)
get api("/projects/#{project.id}/secure_files", maintainer)
expect(response).to have_gitlab_http_status(:service_unavailable)
end
it 'returns a 200 when the feature flag is enabled' do
get api("/projects/#{project.id}/secure_files", maintainer)
expect(response).to have_gitlab_http_status(:ok)
expect(json_response).to be_a(Array)
end
end
context 'ci_secure_files_read_only feature flag' do
context 'when the flag is enabled' do
before do

View File

@ -74,39 +74,39 @@ RSpec.describe 'Query.runner(id)', feature_category: :runner do
runner_data = graphql_data_at(:runner)
expect(runner_data).not_to be_nil
expect(runner_data).to match a_hash_including(
'id' => runner.to_global_id.to_s,
'description' => runner.description,
'createdAt' => runner.created_at&.iso8601,
'contactedAt' => runner.contacted_at&.iso8601,
'version' => runner.version,
'shortSha' => runner.short_sha,
'revision' => runner.revision,
'locked' => false,
'active' => runner.active,
'paused' => !runner.active,
'status' => runner.status('14.5').to_s.upcase,
'jobExecutionStatus' => runner.builds.running.any? ? 'RUNNING' : 'IDLE',
'maximumTimeout' => runner.maximum_timeout,
'accessLevel' => runner.access_level.to_s.upcase,
'runUntagged' => runner.run_untagged,
'ipAddress' => runner.ip_address,
'runnerType' => runner.instance_type? ? 'INSTANCE_TYPE' : 'PROJECT_TYPE',
'executorName' => runner.executor_type&.dasherize,
'architectureName' => runner.architecture,
'platformName' => runner.platform,
'maintenanceNote' => runner.maintenance_note,
'maintenanceNoteHtml' =>
expect(runner_data).to match a_graphql_entity_for(
runner,
description: runner.description,
created_at: runner.created_at&.iso8601,
contacted_at: runner.contacted_at&.iso8601,
version: runner.version,
short_sha: runner.short_sha,
revision: runner.revision,
locked: false,
active: runner.active,
paused: !runner.active,
status: runner.status('14.5').to_s.upcase,
job_execution_status: runner.builds.running.any? ? 'RUNNING' : 'IDLE',
maximum_timeout: runner.maximum_timeout,
access_level: runner.access_level.to_s.upcase,
run_untagged: runner.run_untagged,
ip_address: runner.ip_address,
runner_type: runner.instance_type? ? 'INSTANCE_TYPE' : 'PROJECT_TYPE',
executor_name: runner.executor_type&.dasherize,
architecture_name: runner.architecture,
platform_name: runner.platform,
maintenance_note: runner.maintenance_note,
maintenance_note_html:
runner.maintainer_note.present? ? a_string_including('<strong>Test maintenance note</strong>') : '',
'jobCount' => runner.builds.count,
'jobs' => a_hash_including(
job_count: runner.builds.count,
jobs: a_hash_including(
"count" => runner.builds.count,
"nodes" => an_instance_of(Array),
"pageInfo" => anything
),
'projectCount' => nil,
'adminUrl' => "http://localhost/admin/runners/#{runner.id}",
'userPermissions' => {
project_count: nil,
admin_url: "http://localhost/admin/runners/#{runner.id}",
user_permissions: {
'readRunner' => true,
'updateRunner' => true,
'deleteRunner' => true,
@ -134,11 +134,7 @@ RSpec.describe 'Query.runner(id)', feature_category: :runner do
runner_data = graphql_data_at(:runner)
expect(runner_data).not_to be_nil
expect(runner_data).to match a_hash_including(
'id' => runner.to_global_id.to_s,
'adminUrl' => nil
)
expect(runner_data['tagList']).to match_array runner.tag_list
expect(runner_data).to match a_graphql_entity_for(runner, :tag_list, admin_url: nil)
end
end
@ -231,10 +227,7 @@ RSpec.describe 'Query.runner(id)', feature_category: :runner do
runner_data = graphql_data_at(:runner)
expect(runner_data).to match a_hash_including(
'id' => project_runner.to_global_id.to_s,
'locked' => is_locked
)
expect(runner_data).to match a_graphql_entity_for(project_runner, locked: is_locked)
end
end
end
@ -258,18 +251,8 @@ RSpec.describe 'Query.runner(id)', feature_category: :runner do
post_graphql(query, current_user: user)
expect(graphql_data).to match a_hash_including(
'runner1' => {
'id' => runner1.to_global_id.to_s,
'ownerProject' => {
'id' => project2.to_global_id.to_s
}
},
'runner2' => {
'id' => runner2.to_global_id.to_s,
'ownerProject' => {
'id' => project1.to_global_id.to_s
}
}
'runner1' => a_graphql_entity_for(runner1, owner_project: a_graphql_entity_for(project2)),
'runner2' => a_graphql_entity_for(runner2, owner_project: a_graphql_entity_for(project1))
)
end
end
@ -299,8 +282,8 @@ RSpec.describe 'Query.runner(id)', feature_category: :runner do
it 'retrieves groups field with expected value' do
post_graphql(query, current_user: user)
runner_data = graphql_data_at(:runner, :groups)
expect(runner_data).to eq 'nodes' => [{ 'id' => group.to_global_id.to_s }]
runner_data = graphql_data_at(:runner, :groups, :nodes)
expect(runner_data).to contain_exactly(a_graphql_entity_for(group))
end
end
@ -424,13 +407,13 @@ RSpec.describe 'Query.runner(id)', feature_category: :runner do
'jobCount' => 1,
'jobs' => a_hash_including(
"count" => 1,
"nodes" => [{ "id" => job.to_global_id.to_s, "status" => job.status.upcase }]
"nodes" => [a_graphql_entity_for(job, status: job.status.upcase)]
),
'projectCount' => 2,
'projects' => {
'nodes' => [
{ 'id' => project1.to_global_id.to_s },
{ 'id' => project2.to_global_id.to_s }
a_graphql_entity_for(project1),
a_graphql_entity_for(project2)
]
})
expect(runner2_data).to match a_hash_including(
@ -557,27 +540,25 @@ RSpec.describe 'Query.runner(id)', feature_category: :runner do
expect(graphql_data.count).to eq 6
expect(graphql_data).to match(
a_hash_including(
'instance_runner1' => a_hash_including('id' => active_instance_runner.to_global_id.to_s),
'instance_runner2' => a_hash_including('id' => inactive_instance_runner.to_global_id.to_s),
'group_runner1' => a_hash_including(
'id' => active_group_runner.to_global_id.to_s,
'groups' => { 'nodes' => [a_hash_including('id' => group.to_global_id.to_s)] }
'instance_runner1' => a_graphql_entity_for(active_instance_runner),
'instance_runner2' => a_graphql_entity_for(inactive_instance_runner),
'group_runner1' => a_graphql_entity_for(
active_group_runner,
groups: { 'nodes' => contain_exactly(a_graphql_entity_for(group)) }
),
'group_runner2' => a_hash_including(
'id' => active_group_runner2.to_global_id.to_s,
'groups' => { 'nodes' => [a_hash_including('id' => active_group_runner2.groups[0].to_global_id.to_s)] }
'group_runner2' => a_graphql_entity_for(
active_group_runner2,
groups: { 'nodes' => active_group_runner2.groups.map { |g| a_graphql_entity_for(g) } }
),
'project_runner1' => a_hash_including(
'id' => active_project_runner.to_global_id.to_s,
'projects' => { 'nodes' => [a_hash_including('id' => active_project_runner.projects[0].to_global_id.to_s)] },
'ownerProject' => a_hash_including('id' => active_project_runner.projects[0].to_global_id.to_s)
'project_runner1' => a_graphql_entity_for(
active_project_runner,
projects: { 'nodes' => active_project_runner.projects.map { |p| a_graphql_entity_for(p) } },
owner_project: a_graphql_entity_for(active_project_runner.projects[0])
),
'project_runner2' => a_hash_including(
'id' => active_project_runner2.to_global_id.to_s,
'projects' => {
'nodes' => [a_hash_including('id' => active_project_runner2.projects[0].to_global_id.to_s)]
},
'ownerProject' => a_hash_including('id' => active_project_runner2.projects[0].to_global_id.to_s)
'project_runner2' => a_graphql_entity_for(
active_project_runner2,
projects: { 'nodes' => active_project_runner2.projects.map { |p| a_graphql_entity_for(p) } },
owner_project: a_graphql_entity_for(active_project_runner2.projects[0])
)
))
end

View File

@ -48,7 +48,7 @@ RSpec.describe 'Query.runners', feature_category: :runner do
it_behaves_like 'a working graphql query'
it 'returns expected runner' do
expect(runners_graphql_data['nodes'].map { |n| n['id'] }).to contain_exactly(expected_runner.to_global_id.to_s)
expect(runners_graphql_data['nodes']).to contain_exactly(a_graphql_entity_for(expected_runner))
end
end

View File

@ -298,6 +298,48 @@ RSpec.describe API::Terraform::State, :snowplow, feature_category: :infrastructu
expect(state.reload_latest_version.build).to eq(job)
end
end
describe 'response depending on the max allowed state size' do
let(:current_user) { maintainer }
before do
stub_application_setting(max_terraform_state_size_bytes: max_allowed_state_size)
request
end
context 'when the max allowed state size is unlimited (set as 0)' do
let(:max_allowed_state_size) { 0 }
it 'returns a success response' do
expect(response).to have_gitlab_http_status(:ok)
end
end
context 'when the max allowed state size is greater than the request state size' do
let(:max_allowed_state_size) { params.to_json.size + 1 }
it 'returns a success response' do
expect(response).to have_gitlab_http_status(:ok)
end
end
context 'when the max allowed state size is equal to the request state size' do
let(:max_allowed_state_size) { params.to_json.size }
it 'returns a success response' do
expect(response).to have_gitlab_http_status(:ok)
end
end
context 'when the max allowed state size is less than the request state size' do
let(:max_allowed_state_size) { params.to_json.size - 1 }
it "returns a 'payload too large' response" do
expect(response).to have_gitlab_http_status(:payload_too_large)
end
end
end
end
describe 'DELETE /projects/:id/terraform/state/:name' do

View File

@ -14,7 +14,7 @@ module Spec
submit_invites
end
wait_for_all_requests
wait_for_requests
page.refresh if refresh
end
@ -30,6 +30,8 @@ module Spec
find('.dropdown-item', text: 'Invite "new_email@gitlab.com" by email').click
submit_invites
wait_for_requests
end
end

View File

@ -63,10 +63,13 @@ RSpec.shared_examples 'shows and resets runner registration token' do
end
RSpec.shared_examples 'shows no runners registered' do
it 'shows counts with 0' do
expect(page).to have_text "#{s_('Runners|Online')} 0"
expect(page).to have_text "#{s_('Runners|Offline')} 0"
expect(page).to have_text "#{s_('Runners|Stale')} 0"
it 'shows total count with 0' do
expect(find('[data-testid="runner-type-tabs"]')).to have_text "#{s_('Runners|All')} 0"
# No stats are shown
expect(page).not_to have_text s_('Runners|Online')
expect(page).not_to have_text s_('Runners|Offline')
expect(page).not_to have_text s_('Runners|Stale')
end
it 'shows "no runners" message' do

View File

@ -6,7 +6,7 @@ require 'carrierwave/storage/fog'
RSpec.describe GitlabUploader do
let(:uploader_class) { Class.new(described_class) }
subject { uploader_class.new(double) }
subject(:uploader) { uploader_class.new(double) }
describe '#file_storage?' do
context 'when file storage is used' do
@ -161,6 +161,19 @@ RSpec.describe GitlabUploader do
end
end
describe '#multi_read' do
let(:file) { fixture_file_upload('spec/fixtures/trace/sample_trace', 'text/plain') }
let(:byte_offsets) { [[4, 10], [17, 29]] }
subject { uploader.multi_read(byte_offsets) }
before do
uploader.store!(file)
end
it { is_expected.to eq(%w[Running gitlab-runner]) }
end
describe '.version' do
subject { uploader_class.version }