Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2022-06-22 00:08:23 +00:00
parent c097ca59a1
commit bd9b2731b4
36 changed files with 489 additions and 80 deletions

View File

@ -128,7 +128,7 @@ The Geo primary site needs to checksum every replicable so secondaries can verif
```ruby
# frozen_string_literal: true
class CreateCoolWidgetStates < Gitlab::Database::Migration[1.0]
class CreateCoolWidgetStates < Gitlab::Database::Migration[2.0]
VERIFICATION_STATE_INDEX_NAME = "index_cool_widget_states_on_verification_state"
PENDING_VERIFICATION_INDEX_NAME = "index_cool_widget_states_pending_verification"
FAILED_VERIFICATION_INDEX_NAME = "index_cool_widget_states_failed_verification"

View File

@ -130,7 +130,7 @@ The Geo primary site needs to checksum every replicable so secondaries can verif
```ruby
# frozen_string_literal: true
class CreateCoolWidgetStates < Gitlab::Database::Migration[1.0]
class CreateCoolWidgetStates < Gitlab::Database::Migration[2.0]
VERIFICATION_STATE_INDEX_NAME = "index_cool_widget_states_on_verification_state"
PENDING_VERIFICATION_INDEX_NAME = "index_cool_widget_states_pending_verification"
FAILED_VERIFICATION_INDEX_NAME = "index_cool_widget_states_failed_verification"

View File

@ -1,5 +1,5 @@
import $ from 'jquery';
import createFlash, { FLASH_TYPES } from '~/flash';
import { VARIANT_DANGER, VARIANT_INFO, createAlert } from '~/flash';
import axios from '~/lib/utils/axios_utils';
import { parseBoolean } from '~/lib/utils/common_utils';
import { Rails } from '~/lib/utils/rails_ujs';
@ -10,7 +10,7 @@ import TimezoneDropdown, {
export default class Profile {
constructor({ form } = {}) {
this.onSubmitForm = this.onSubmitForm.bind(this);
this.form = form || $('.edit-user');
this.form = form || $('.js-edit-user');
this.setRepoRadio();
this.bindEvents();
this.initAvatarGlCrop();
@ -84,9 +84,9 @@ export default class Profile {
this.updateHeaderAvatar();
}
createFlash({
createAlert({
message: data.message,
type: data.status === 'error' ? FLASH_TYPES.ALERT : FLASH_TYPES.NOTICE,
variant: data.status === 'error' ? VARIANT_DANGER : VARIANT_INFO,
});
})
.then(() => {
@ -95,8 +95,9 @@ export default class Profile {
self.form.find(':input[disabled]').enable();
})
.catch((error) =>
createFlash({
createAlert({
message: error.message,
variant: VARIANT_DANGER,
}),
);
}

View File

@ -173,7 +173,7 @@ export default {
:label="issuableCategoryHeaderText"
label-for="linked-issue-type-radio"
label-class="label-bold"
class="mb-2"
class="gl-mb-3"
>
<gl-form-radio-group
id="linked-issue-type-radio"
@ -216,12 +216,12 @@ export default {
:disabled="isSubmitButtonDisabled"
:loading="isSubmitting"
type="submit"
class="float-left"
class="gl-float-left"
data-qa-selector="add_issue_button"
>
{{ __('Add') }}
</gl-button>
<gl-button class="float-right" @click="onFormCancel">
<gl-button class="gl-float-right" @click="onFormCancel">
{{ __('Cancel') }}
</gl-button>
</div>

View File

@ -1,6 +1,6 @@
.application-theme {
$ui-gray-bg: #2e2e2e;
$ui-light-gray-bg: #dfdfdf;
$ui-gray-bg: #303030;
$ui-light-gray-bg: #f0f0f0;
$ui-dark-mode-bg: #1f1f1f;
.preview {

View File

@ -5,9 +5,7 @@
- availability = availability_values
- custom_emoji = show_status_emoji?(@user.status)
= gitlab_ui_form_for @user, url: profile_path, method: :put, html: { multipart: true, class: 'edit-user gl-mt-3 js-quick-submit gl-show-field-errors js-password-prompt-form', remote: true }, authenticity_token: true do |f|
= form_errors(@user)
= gitlab_ui_form_for @user, url: profile_path, method: :put, html: { multipart: true, class: 'edit-user js-edit-user gl-mt-3 js-quick-submit gl-show-field-errors js-password-prompt-form', remote: true }, authenticity_token: true do |f|
.row.js-search-settings-section
.col-lg-4.profile-settings-sidebar
%h4.gl-mt-0

View File

@ -1,8 +0,0 @@
---
name: ci_minutes_cost_factor_for_all_public_projects
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/85357
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/359094
milestone: '15.0'
type: development
group: group::pipeline execution
default_enabled: false

View File

@ -0,0 +1,35 @@
# frozen_string_literal: true
class ChangePublicProjectsCostFactor < Gitlab::Database::Migration[2.0]
# This migration updates SaaS Runner cost factors for public projects.
# Previously we had a disabled cost factor for public projects, meaning
# that no CI minutes were counted by default. With a low cost factor
# we count CI minutes consumption at a very low rate to prevent
# abuses.
disable_ddl_transaction!
restrict_gitlab_migration gitlab_schema: :gitlab_ci
DISABLED_COST_FACTOR = 0
LOW_COST_FACTOR = 0.008
class Runner < MigrationRecord
self.table_name = 'ci_runners'
scope :shared, -> { where(runner_type: 1) }
end
def up
return unless Gitlab.com?
Runner.shared.where(public_projects_minutes_cost_factor: DISABLED_COST_FACTOR)
.update_all(public_projects_minutes_cost_factor: LOW_COST_FACTOR)
end
def down
return unless Gitlab.com?
Runner.shared.where(public_projects_minutes_cost_factor: LOW_COST_FACTOR)
.update_all(public_projects_minutes_cost_factor: DISABLED_COST_FACTOR)
end
end

View File

@ -0,0 +1,21 @@
# frozen_string_literal: true
class DropTempIndexOnProjectsOnIdAndRunnersToken < Gitlab::Database::Migration[2.0]
disable_ddl_transaction!
TEMP_INDEX_NAME = 'tmp_index_projects_on_id_and_runners_token'
def up
finalize_background_migration 'ResetDuplicateCiRunnersTokenValuesOnProjects'
remove_concurrent_index_by_name :projects, TEMP_INDEX_NAME
end
def down
add_concurrent_index :projects,
[:id, :runners_token],
where: "runners_token IS NOT NULL",
unique: false,
name: TEMP_INDEX_NAME
end
end

View File

@ -0,0 +1,21 @@
# frozen_string_literal: true
class DropTempIndexOnProjectsOnIdAndRunnersTokenEncrypted < Gitlab::Database::Migration[2.0]
disable_ddl_transaction!
TEMP_INDEX_NAME = 'tmp_index_projects_on_id_and_runners_token_encrypted'
def up
finalize_background_migration 'ResetDuplicateCiRunnersTokenEncryptedValuesOnProjects'
remove_concurrent_index_by_name :projects, TEMP_INDEX_NAME
end
def down
add_concurrent_index :projects,
[:id, :runners_token_encrypted],
where: "runners_token_encrypted IS NOT NULL",
unique: false,
name: TEMP_INDEX_NAME
end
end

View File

@ -0,0 +1,18 @@
# frozen_string_literal: true
class AddUniqueIndexOnProjectsOnRunnersToken < Gitlab::Database::Migration[2.0]
disable_ddl_transaction!
INDEX_NAME = 'index_uniq_projects_on_runners_token'
def up
add_concurrent_index :projects,
:runners_token,
name: INDEX_NAME,
unique: true
end
def down
remove_concurrent_index_by_name :projects, INDEX_NAME
end
end

View File

@ -0,0 +1,18 @@
# frozen_string_literal: true
class AddUniqueIndexOnProjectsOnRunnersTokenEncrypted < Gitlab::Database::Migration[2.0]
disable_ddl_transaction!
INDEX_NAME = 'index_uniq_projects_on_runners_token_encrypted'
def up
add_concurrent_index :projects,
:runners_token_encrypted,
name: INDEX_NAME,
unique: true
end
def down
remove_concurrent_index_by_name :projects, INDEX_NAME
end
end

View File

@ -0,0 +1,17 @@
# frozen_string_literal: true
class DropIndexOnProjectsOnRunnersToken < Gitlab::Database::Migration[2.0]
disable_ddl_transaction!
INDEX_NAME = 'index_projects_on_runners_token'
def up
remove_concurrent_index_by_name :projects, INDEX_NAME
end
def down
add_concurrent_index :projects,
:runners_token,
name: INDEX_NAME
end
end

View File

@ -0,0 +1,17 @@
# frozen_string_literal: true
class DropIndexOnProjectsOnRunnersTokenEncrypted < Gitlab::Database::Migration[2.0]
disable_ddl_transaction!
INDEX_NAME = 'index_projects_on_runners_token_encrypted'
def up
remove_concurrent_index_by_name :projects, INDEX_NAME
end
def down
add_concurrent_index :projects,
:runners_token_encrypted,
name: INDEX_NAME
end
end

View File

@ -0,0 +1 @@
9c64f9fb286992b6cdad8f7f22084c2d538bccf97e9c649f47284c5828a850e3

View File

@ -0,0 +1 @@
247c6cba3cee4413a17193aeebd77eae79a7ced17a5a2b785f0ecd682e823c02

View File

@ -0,0 +1 @@
d72ffd09437a576edb2d046963e8d004c5a2b13586f7318361fea6d673f5cece

View File

@ -0,0 +1 @@
d382bfcfcf79ba38a388ac5496a194adc0c392ba6685c024d7bd55a14d57c1b8

View File

@ -0,0 +1 @@
ebe1b2db48b987720e7c561b30ce41e7542d8cd190e4b454bd28d6fdfa8bff0d

View File

@ -0,0 +1 @@
2fcb9e7ecdc387d5dd4dfe78544289026bb2626eca2650da590f6181deeaadef

View File

@ -0,0 +1 @@
5a03ed4bc5791d0feb72203553f77ed37f37127309eda6c7dc75c7ac950e28e3

View File

@ -29124,10 +29124,6 @@ CREATE UNIQUE INDEX index_projects_on_project_namespace_id ON projects USING btr
CREATE INDEX index_projects_on_repository_storage ON projects USING btree (repository_storage);
CREATE INDEX index_projects_on_runners_token ON projects USING btree (runners_token);
CREATE INDEX index_projects_on_runners_token_encrypted ON projects USING btree (runners_token_encrypted);
CREATE INDEX index_projects_on_star_count ON projects USING btree (star_count);
CREATE INDEX index_projects_on_updated_at_and_id ON projects USING btree (updated_at, id);
@ -29594,6 +29590,10 @@ CREATE INDEX index_u2f_registrations_on_user_id ON u2f_registrations USING btree
CREATE UNIQUE INDEX index_uniq_im_issuable_escalation_statuses_on_issue_id ON incident_management_issuable_escalation_statuses USING btree (issue_id);
CREATE UNIQUE INDEX index_uniq_projects_on_runners_token ON projects USING btree (runners_token);
CREATE UNIQUE INDEX index_uniq_projects_on_runners_token_encrypted ON projects USING btree (runners_token_encrypted);
CREATE UNIQUE INDEX index_unique_ci_runner_projects_on_runner_id_and_project_id ON ci_runner_projects USING btree (runner_id, project_id);
CREATE UNIQUE INDEX index_unique_issue_metrics_issue_id ON issue_metrics USING btree (issue_id);
@ -30008,10 +30008,6 @@ CREATE UNIQUE INDEX tmp_index_on_tmp_project_id_on_namespaces ON namespaces USIN
CREATE INDEX tmp_index_on_vulnerabilities_non_dismissed ON vulnerabilities USING btree (id) WHERE (state <> 2);
CREATE INDEX tmp_index_projects_on_id_and_runners_token ON projects USING btree (id, runners_token) WHERE (runners_token IS NOT NULL);
CREATE INDEX tmp_index_projects_on_id_and_runners_token_encrypted ON projects USING btree (id, runners_token_encrypted) WHERE (runners_token_encrypted IS NOT NULL);
CREATE UNIQUE INDEX uniq_pkgs_deb_grp_architectures_on_distribution_id_and_name ON packages_debian_group_architectures USING btree (distribution_id, name);
CREATE UNIQUE INDEX uniq_pkgs_deb_grp_components_on_distribution_id_and_name ON packages_debian_group_components USING btree (distribution_id, name);

View File

@ -825,7 +825,9 @@ information, see the [relevant documentation](monitoring.md#monitor-gitaly-concu
## Control groups
> Introduced in GitLab 13.10.
FLAG:
On self-managed GitLab, by default cgroups are not available. To make it available, ask an administrator to
[enable the feature flag](../feature_flags.md) named `gitaly_run_cmds_in_cgroup`.
Gitaly shells out to Git for many of its operations. Git can consume a lot of resources for certain operations,
especially for large repositories.
@ -862,10 +864,6 @@ Two ways of configuring cgroups are available.
> This method of configuring cgroups introduced in GitLab 15.1.
FLAG:
On self-managed GitLab, by default this method of configuring cgroups is not available. To make it available, ask an administrator to
[enable the feature flag](../feature_flags.md) named `gitaly_run_cmds_in_cgroup`.
Gitaly creates a pool of cgroups that are isolated based on the repository used in the Git command to be placed under one of these cgroups.
To configure cgroups in Gitaly, add `gitaly['cgroups']` to `/etc/gitlab/gitlab.rb`.

View File

@ -48,8 +48,8 @@ the Gitaly logs and Prometheus:
You can observe the status of [control groups (cgroups)](configure_gitaly.md#control-groups) using Prometheus:
- `gitaly_cgroups_memory_failed_total`, a gauge for the total number of times
the memory limit has been hit. This number resets each time a server is
- `gitaly_cgroups_reclaim_attempts_total`, a gauge for the total number of times
there has been a memory relcaim attempt. This number resets each time a server is
restarted.
- `gitaly_cgroups_cpu_usage`, a gauge that measures CPU usage per cgroup.
- `gitaly_cgroup_procs_total`, a gauge that measures the total number of

View File

@ -195,12 +195,12 @@ the duration of the pipeline if many jobs ran at the same time.
The cost factor for a job running on a shared runner is:
- `0.008` for public projects on GitLab SaaS, if [created 2021-07-17 or later](https://gitlab.com/gitlab-org/gitlab/-/issues/332708).
- `0.008` for public projects on GitLab SaaS.
(For every 125 minutes of job time, you accrue 1 CI/CD minute.)
- `0.008` for projects members of GitLab [Open Source program](../../subscriptions/index.md#gitlab-for-open-source).
(For every 125 minutes of job time, you accrue 1 CI/CD minute.)
- `0` for public projects on GitLab self-managed instances, and for GitLab SaaS public projects created before 2021-07-17.
- `1` for internal and private projects.
- `0` for public projects on GitLab self-managed instances.
- `1` for internal or private projects.
### Additional costs on GitLab SaaS

View File

@ -10,13 +10,15 @@ This tutorial guides you through writing a consumer test from scratch. To start,
## Create the skeleton
Start by creating the skeleton of a consumer test. Create a file under `spec/contracts/consumer/specs` called `discussions.spec.js`.
Start by creating the skeleton of a consumer test. Create a file under `spec/contracts/consumer/specs/project/merge_request` called `discussions.spec.js`.
Then, populate it with the following function and parameters:
- [`pactWith`](#the-pactwith-function)
- [`PactOptions`](#the-pactoptions-parameter)
- [`PactFn`](#the-pactfn-parameter)
To learn more about how the contract test directory is structured, see the contract testing [test suite folder structure](index.md#test-suite-folder-structure).
### The `pactWith` function
The Pact consumer test is defined through the `pactWith` function that takes `PactOptions` and the `PactFn`.
@ -36,15 +38,17 @@ const { pactWith } = require('jest-pact');
pactWith(
{
consumer: 'Merge Request Page',
consumer: 'MergeRequest#show',
provider: 'Merge Request Discussions Endpoint',
log: '../logs/consumer.log',
dir: '../contracts',
dir: '../contracts/project/merge_request/show',
},
PactFn
);
```
To learn more about how to name the consumers and providers, see contract testing [naming conventions](index.md#naming-conventions).
### The `PactFn` parameter
The `PactFn` is where your tests are defined. This is where you set up the mock provider and where you can use the standard Jest methods like [`Jest.describe`](https://jestjs.io/docs/api#describename-fn), [`Jest.beforeEach`](https://jestjs.io/docs/api#beforeeachfn-timeout), and [`Jest.it`](https://jestjs.io/docs/api#testname-fn-timeout). For more information, see [https://jestjs.io/docs/api](https://jestjs.io/docs/api).
@ -54,14 +58,14 @@ const { pactWith } = require('jest-pact');
pactWith(
{
consumer: 'Merge Request Page',
consumer: 'MergeRequest#show',
provider: 'Merge Request Discussions Endpoint',
log: '../logs/consumer.log',
dir: '../contracts',
},
(provider) => {
describe('Discussions Endpoint', () => {
describe('Merge Request Discussions Endpoint', () => {
beforeEach(() => {
});
@ -93,14 +97,14 @@ const { Matchers } = require('@pact-foundation/pact');
pactWith(
{
consumer: 'Merge Request Page',
consumer: 'MergeRequest#show',
provider: 'Merge Request Discussions Endpoint',
log: '../logs/consumer.log',
dir: '../contracts',
dir: '../contracts/project/merge_request/show',
},
(provider) => {
describe('Discussions Endpoint', () => {
describe('Merge Request Discussions Endpoint', () => {
beforeEach(() => {
const interaction = {
state: 'a merge request with discussions exists',
@ -144,7 +148,7 @@ Notice how we use `Matchers` in the `body` of the expected response. This allows
After the mock provider is set up, you can write the test. For this test, you make a request and expect a particular response.
First, set up the client that makes the API request. To do that, either create or find an existing file under `spec/contracts/consumer/endpoints` and add the following API request.
First, set up the client that makes the API request. To do that, create `spec/contracts/consumer/endpoints/project/merge_requests.js` and add the following API request.
```javascript
const axios = require('axios');
@ -169,18 +173,18 @@ After that's set up, import it to the test file and call it to make the request.
const { pactWith } = require('jest-pact');
const { Matchers } = require('@pact-foundation/pact');
const { getDiscussions } = require('../endpoints/merge_requests');
const { getDiscussions } = require('../endpoints/project/merge_requests');
pactWith(
{
consumer: 'Merge Request Page',
consumer: 'MergeRequest#show',
provider: 'Merge Request Discussions Endpoint',
log: '../logs/consumer.log',
dir: '../contracts',
dir: '../contracts/project/merge_request/show',
},
(provider) => {
describe('Discussions Endpoint', () => {
describe('Merge Request Discussions Endpoint', () => {
beforeEach(() => {
const interaction = {
state: 'a merge request with discussions exists',
@ -230,7 +234,7 @@ There we have it! The consumer test is now set up. You can now try [running this
As you may have noticed, the request and response definitions can get large. This results in the test being difficult to read, with a lot of scrolling to find what you want. You can make the test easier to read by extracting these out to a `fixture`.
Create a file under `spec/contracts/consumer/fixtures` called `discussions.fixture.js`. You place the `request` and `response` definitions here.
Create a file under `spec/contracts/consumer/fixtures/project/merge_request` called `discussions.fixture.js` where you will place the `request` and `response` definitions.
```javascript
const { Matchers } = require('@pact-foundation/pact');
@ -274,18 +278,18 @@ With all of that moved to the `fixture`, you can simplify the test to the follow
const { pactWith } = require('jest-pact');
const { Discussions } = require('../fixtures/discussions.fixture');
const { getDiscussions } = require('../endpoints/merge_requests');
const { getDiscussions } = require('../endpoints/project/merge_requests');
pactWith(
{
consumer: 'Merge Request Page',
consumer: 'MergeRequest#show',
provider: 'Merge Request Discussions Endpoint',
log: '../logs/consumer.log',
dir: '../contracts',
dir: '../contracts/project/merge_request/show',
},
(provider) => {
describe('Discussions Endpoint', () => {
describe('Merge Request Discussions Endpoint', () => {
beforeEach(() => {
const interaction = {
state: 'a merge request with discussions exists',

View File

@ -37,3 +37,42 @@ rake contracts:mr:pact:verify:discussions # Verify provider against the
rake contracts:mr:pact:verify:metadata # Verify provider against the consumer pacts for metadata
rake contracts:mr:test:merge_request[contract_mr] # Run all merge request contract tests
```
## Test suite folder structure and naming conventions
To keep the consumer and provider test suite organized and maintainable, it's important that tests are organized, also that consumers and providers are named consistently. Therefore, it's important to adhere to the following conventions.
### Test suite folder structure
Having an organized and sensible folder structure for the test suite makes it easier to find relevant files when reviewing, debugging, or introducing tests.
#### Consumer tests
The consumer tests are grouped according to the different pages in the application. Each file contains various types of requests found in a page. As such, the consumer test files are named using the Rails standards of how pages are referenced. For example, the project pipelines page would be the `Project::Pipeline#index` page so the equivalent consumer test would be located in `consumer/specs/project/pipelines/index.spec.js`.
When defining the location to output the contract generated by the test, we want to follow the same file structure which would be `contracts/project/pipelines/` for this example. This is the structure in `consumer/endpoints` and `consumer/fixtures` as well.
#### Provider tests
The provider tests are grouped similarly to our controllers. Each of these tests contains various tests for an API endpoint. For example, the API endpoint to get a list of pipelines for a project would be located in `provider/pact_helpers/project/pipelines/get_list_project_pipelines_helper.rb`. The provider states are structured the same way.
### Naming conventions
When writing the consumer and provider tests, there are parts where a name is required for the consumer and provider. Since there are no restrictions imposed by Pact on how these should be named, a naming convention is important to keep it easy for us to figure out which consumer and provider tests are involved during debugging. Pact also uses the consumer and provider names to generate the generated contracts in the `#{consumer_name}-#{provider_name}` format.
#### Consumer naming
As mentioned in the [folder structure section](#consumer-tests), consumer tests are grouped according to the different pages in the application. As such, consumer names should follow the same naming format using the Rails standard. For example, the consumer test for `Project::Pipeline#index` would be `ProjectPipeline#index` as the consumer name. Since Pact uses this name to name the contracts it generates, the colons (`::`) are dropped as colons are not valid characters in file names.
#### Provider naming
These are the API endpoints that provides the data to the consumer so they are simply named according to the API endpoint they pertain to. Be mindful that this name is as descriptive as possible. For example, if we're writing a test for the `GET /groups/:id/projects` endpoint, we don't want to simply name it "Projects endpoint" as there is a `GET /projects` endpoint as well that also fetches a list of projects the user has access to across all of GitLab. An easy way to name them is by checking out our [API documentation](../../../api/api_resources.md) and naming it the same way it is named in there. So the [`GET /groups/:id/projects`](../../../api/groups.md#list-a-groups-projects) would be called `List a groups projects` and [`GET /projects`](../../../api/projects.md#list-all-projects) would be called `List all projects`. Subsequently, the test files are named `list_a_groups_projects_helper.rb` and `list_all_projects_helper.rb` respectively.
There are some cases where the provider being tested may not be documented so, in those cases, fall back to choosing a name that is as descriptive as possible to ensure it's easy to tell what the provider is for.
#### Conventions summary
| Tests | Folder structure | Naming convention |
| ----- | ---------------- | ----------------- |
| Consumer Test | Follows the Rails reference standards. For example, `Project::Pipeline#index` would be `consumer/specs/project/pipelines/index.spec.js` | Follows the Rails naming standard. For example, `Project::Pipeline#index` would be `ProjectPipeline#index` |
| Provider Test | Grouped like the Rails controllers. For example, [`List project pipelines` API endpoint](../../../api/pipelines.md#list-project-pipelines) would be `provider/pact_helpers/project/pipelines/provider/pact_helpers/project/pipelines/get_list_project_pipelines_helper.rb` | Follows the API documentation naming scheme. For example, [`GET /projects/:id/pipelines`](../../../api/pipelines.md#list-project-pipelines) would be called `List project pipelines`. |

View File

@ -6,18 +6,20 @@ info: To determine the technical writer assigned to the Stage/Group associated w
# Writing provider tests
This tutorial guides you through writing a provider test from scratch. It is a continuation of the [consumer test tutorial](consumer_tests.md). To start, the provider tests are written using [`pact-ruby`](https://github.com/pact-foundation/pact-ruby). In this tutorial, you write a provider test that addresses the contract generated by `discussions.spec.js`.
This tutorial guides you through writing a provider test from scratch. It is a continuation of the [consumer test tutorial](consumer_tests.md). To start, the provider tests are written using [`pact-ruby`](https://github.com/pact-foundation/pact-ruby). In this tutorial, you write a provider test that addresses the contract generated by `discussions.spec.js`. As Pact is a consumer-driven testing tool, this tutorial assumes that there is an existing consumer test that had already generated a contract for us to work with.
## Create the skeleton
Provider tests are quite simple. The goal is to set up the test data and then link that with the corresponding contract. Start by creating a file called `discussions_helper.rb` under `spec/contracts/provider/specs`. Note that the files are called `helpers` to match how they are called by Pact in the Rake tasks, which are set up at the end of this tutorial.
Provider tests are quite simple. The goal is to set up the test data and then link that with the corresponding contract. Start by creating a file called `discussions_helper.rb` under `spec/contracts/provider/pact_helpers/project/merge_request`. Note that the files are called `helpers` to match how they are called by Pact in the Rake tasks, which are set up at the end of this tutorial.
To learn more about how the contract test directory is structured, see the contract testing [test suite folder structure](index.md#test-suite-folder-structure).
### The `service_provider` block
The `service_provider` block is where the provider test is defined. For this block, put in a description of the service provider. Name it exactly as it is called in the contracts that are derived from the consumer tests.
```ruby
require_relative '../spec_helper'
require_relative '../../../spec_helper'
module Provider
module DiscussionsHelper
@ -33,12 +35,12 @@ end
The `honours_pact_with` block describes which consumer this provider test is addressing. Similar to the `service_provider` block, name this exactly the same as it's called in the contracts that are derived from the consumer tests.
```ruby
require_relative '../spec_helper'
require_relative '../../../spec_helper'
module Provider
module DiscussionsHelper
Pact.service_provider 'Merge Request Discussions Endpoint' do
honours_pact_with 'Merge Request Page' do
honours_pact_with 'MergeRequest#show' do
end
end
@ -46,19 +48,21 @@ module Provider
end
```
To learn more about how to name the consumers and providers, see contract testing [naming conventions](index.md#naming-conventions).
## Configure the test app
For the provider tests to verify the contracts, you must hook it up to a test app that makes the actual request and return a response to verify against the contract. To do this, configure the `app` the test uses as `Environment::Test.app`, which is defined in [`spec/contracts/provider/environments/test.rb`](https://gitlab.com/gitlab-org/gitlab/-/tree/master/spec/contracts/provider/environments/test.rb).
```ruby
require_relative '../spec_helper'
require_relative '../../../spec_helper'
module Provider
module DiscussionsHelper
Pact.service_provider 'Merge Request Discussions Endpoint' do
app { Environment::Test.app }
honours_pact_with 'Merge Request Page' do
honours_pact_with 'MergeRequest#show' do
end
end
@ -71,15 +75,15 @@ end
Now that the test app is configured, all that is left is to define which contract this provider test is verifying. To do this, set the `pact_uri`.
```ruby
require_relative '../spec_helper'
require_relative '../../../spec_helper'
module Provider
module DiscussionsHelper
Pact.service_provider 'Merge Request Discussions Endpoint' do
app { Environment::Test.app }
honours_pact_with 'Merge Request Page' do
pact_uri '../contracts/merge_request_page-merge_request_discussions_endpoint.json'
honours_pact_with 'MergeRequest#show' do
pact_uri '../contracts/project/merge_request/show/mergerequest#show-merge_request_discussions_endpoint.json'
end
end
end
@ -95,8 +99,8 @@ Under the `contracts:mr` namespace, introduce the Rake task to run this new test
```ruby
Pact::VerificationTask.new(:discussions) do |pact|
pact.uri(
"#{contracts}/contracts/merge_request_page-merge_request_discussions_endpoint.json",
pact_helper: "#{provider}/specs/discussions_helper.rb"
"#{contracts}/contracts/project/merge_request/show/merge_request#show-merge_request_discussions_endpoint.json",
pact_helper: "#{provider}/pact_helpers/project/merge_request/discussions_helper.rb"
)
end
```
@ -109,7 +113,7 @@ As the last step, create the test data that allows the provider test to return t
You can read more about [provider states](https://docs.pact.io/implementation_guides/ruby/provider_states). We can do global provider states but for this tutorial, the provider state is for one specific `state`.
To create the test data, create `discussions_state.rb` under `spec/contracts/provider/states`. As a quick aside, make sure to also import this state file in the `discussions_helper.rb` file.
To create the test data, create `discussions_state.rb` under `spec/contracts/provider/states/project/merge_request`. Be sure to also import this state file in the `discussions_helper.rb` file.
### Default user in `spec/contracts/provider/spec_helper.rb`
@ -118,10 +122,13 @@ Before you create the test data, note that a default user is created in the [`sp
```ruby
RSpec.configure do |config|
config.include Devise::Test::IntegrationHelpers
config.include FactoryBot::Syntax::Methods
config.before do
user = FactoryBot.create(:user, name: "Contract Test").tap do |user|
user = create(:user, name: Provider::UsersHelper::CONTRACT_USER_NAME).tap do |user|
user.current_sign_in_at = Time.current
end
sign_in user
end
end
@ -134,7 +141,7 @@ Any further modifications to the user that's needed can be done through the indi
In the state file, you must define which consumer this provider state is for. You can do that with `provider_states_for`. Make sure that the `name` provided matches the name defined for the consumer.
```ruby
Pact.provider_states_for 'Merge Request Page' do
Pact.provider_states_for 'MergeRequest#show' do
end
```
@ -143,9 +150,9 @@ end
In the `provider_states_for` block, you then define the state the test data is for. These states are also defined in the consumer test. In this case, there is a `'a merge request with discussions exists'` state.
```ruby
Pact.provider_states_for "Merge Request Page" do
Pact.provider_states_for "MergeRequest#show" do
provider_state "a merge request with discussions exists" do
end
end
```
@ -155,7 +162,7 @@ end
This is where you define the test data creation steps. Use `FactoryBot` to create the data. As you create the test data, you can keep [running the provider test](index.md#run-the-provider-tests) to check on the status of the test and figure out what else is missing in your data setup.
```ruby
Pact.provider_states_for "Merge Request Page" do
Pact.provider_states_for "MergeRequest#show" do
provider_state "a merge request with discussions exists" do
set_up do
user = User.find_by(name: Provider::UsersHelper::CONTRACT_USER_NAME)
@ -172,6 +179,28 @@ Pact.provider_states_for "Merge Request Page" do
end
```
Note the `Provider::UsersHelper::CONTRACT_USER_NAME` here to fetch a user is a user that is from the [`spec_helper`](https://gitlab.com/gitlab-org/gitlab/-/tree/master/spec/contracts/provider/spec_helper.rb) that sets up a user before any of these tests run.
## Using the test data
And with that, the provider tests for `discussion_helper.rb` should now pass with this.
Now that the provider state file is created, you need to import the state file to the provider test.
```ruby
# frozen_string_literal: true
require_relative '../../../spec_helper'
require_relative '../../../states/project/merge_request/discussions_state'
module Provider
module DiscussionsHelper
Pact.service_provider "/merge_request/discussions" do
app { Environments::Test.app }
honours_pact_with 'Merge Request#show' do
pact_uri '../contracts/project/merge_request/show/merge_request#show-merge_request_discussions_endpoint.json'
end
end
end
end
```
And there we have it. The provider test for `discussions_helper.rb` should now pass with this.

View File

@ -458,11 +458,27 @@ NOTE:
Specific information that follow related to Ruby and Git versions do not apply to [Omnibus installations](https://docs.gitlab.com/omnibus/)
and [Helm Chart deployments](https://docs.gitlab.com/charts/). They come with appropriate Ruby and Git versions and are not using system binaries for Ruby and Git. There is no need to install Ruby or Git when utilizing these two approaches.
### 15.2.0 (unreleased)
GitLab installations that have multiple web nodes should be
[upgraded to 15.1](#1510) before upgrading to 15.2 (and later) due to a
configuration change in Rails that can result in inconsistent ETag key
generation.
### 15.1.0
- If you run external PostgreSQL, particularly AWS RDS,
[check you have a PostgreSQL bug fix](#postgresql-segmentation-fault-issue)
to avoid the database crashing.
- In GitLab 15.1.0, we are switching Rails `ActiveSupport::Digest` to use SHA256 instead of MD5.
This affects ETag key generation for resources such as raw Snippet file
downloads. In order to ensure consistent ETag key generation across multiple
web nodes when upgrading, all servers must first be upgraded to 15.1.Z before
upgrading to 15.2.0 or later:
1. Ensure all GitLab web nodes are running GitLab 15.1.Z.
1. [Enable the `active_support_hash_digest_sha256` feature flag](../administration/feature_flags.md#how-to-enable-and-disable-features-behind-flags) to switch `ActiveSupport::Digest` to use SHA256:
1. Only then, continue to upgrade to later versions of GitLab.
### 15.0.0

View File

@ -0,0 +1,30 @@
---
stage: Secure
group: Dynamic Analysis
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
---
# Content-Security-Policy violations
## Description
A `Content-Security-Policy` (CSP) was identified on the target site that is reporting violations when
attempting to load the page in a browser. This may cause disruption to your users when attempting to visit the page.
## Remediation
Review the violations to determine if any action is necessary.
## Details
| ID | Aggregated | CWE | Type | Risk |
|:---|:--------|:--------|:--------|:--------|
| 16.10 | true | 16 | Passive | Info |
## Links
- [CWE](https://cwe.mitre.org/data/definitions/16.html)
- [OWASP](https://cheatsheetseries.owasp.org/cheatsheets/Content_Security_Policy_Cheat_Sheet.html)
- [MDN](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP)
- [Content Security Policy Level 3](https://www.w3.org/TR/CSP3/)
- [CSP Evaluator](https://csp-evaluator.withgoogle.com/)

View File

@ -0,0 +1,30 @@
---
stage: Secure
group: Dynamic Analysis
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
---
# Content-Security-Policy analysis
## Description
A `Content-Security-Policy` (CSP) was identified on the target site. CSP can aid in hardening
a website against various client side attacks such as Cross-Site Scripting (XSS).
## Remediation
Follow the recommendations to determine if any actions are necessary to harden this `Content-Security-Policy`.
## Details
| ID | Aggregated | CWE | Type | Risk |
|:---|:--------|:--------|:--------|:--------|
| 16.8 | true | 16 | Passive | Info |
## Links
- [CWE](https://cwe.mitre.org/data/definitions/16.html)
- [OWASP](https://cheatsheetseries.owasp.org/cheatsheets/Content_Security_Policy_Cheat_Sheet.html)
- [MDN](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP)
- [Content Security Policy Level 3](https://www.w3.org/TR/CSP3/)
- [CSP Evaluator](https://csp-evaluator.withgoogle.com/)

View File

@ -0,0 +1,32 @@
---
stage: Secure
group: Dynamic Analysis
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
---
# Content-Security-Policy-Report-Only analysis
## Description
A `Content-Security-Policy-Report-Only` (CSPRO) was identified on the target site. CSP-Report-Only headers
aid in determining how to implement a `Content-Security-Policy` that does not disrupt normal use of the target
site.
## Remediation
Follow the recommendations to determine if any actions are necessary to harden this `Content-Security-Policy-Report-Only`.
After all alerts have been resolved, we recommended that this header be changed to `Content-Security-Policy`.
## Details
| ID | Aggregated | CWE | Type | Risk |
|:---|:--------|:--------|:--------|:--------|
| 16.9 | true | 16 | Passive | Info |
## Links
- [CWE](https://cwe.mitre.org/data/definitions/16.html)
- [OWASP](https://cheatsheetseries.owasp.org/cheatsheets/Content_Security_Policy_Cheat_Sheet.html)
- [MDN](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP)
- [Content Security Policy Level 3](https://www.w3.org/TR/CSP3/)
- [CSP Evaluator](https://csp-evaluator.withgoogle.com/)

View File

@ -12,12 +12,15 @@ The [DAST browser-based crawler](../browser_based.md) provides a number of vulne
|:---|:------|:---------|:-----|
| [1004.1](1004.1.md) | Sensitive cookie without HttpOnly attribute | Low | Passive |
| [16.1](16.1.md) | Missing Content-Type header | Low | Passive |
| [16.10](16.10.md) | Content-Security-Policy violations | Info | Passive |
| [16.2](16.2.md) | Server header exposes version information | Low | Passive |
| [16.3](16.3.md) | X-Powered-By header exposes version information | Low | Passive |
| [16.4](16.4.md) | X-Backend-Server header exposes server information | Info | Passive |
| [16.5](16.5.md) | AspNet header exposes version information | Low | Passive |
| [16.6](16.6.md) | AspNetMvc header exposes version information | Low | Passive |
| [16.7](16.7.md) | Strict-Transport-Security header missing or invalid | Low | Passive |
| [16.8](16.8.md) | Content-Security-Policy analysis | Info | Passive |
| [16.9](16.9.md) | Content-Security-Policy-Report-Only analysis | Info | Passive |
| [200.1](200.1.md) | Exposure of sensitive information to an unauthorized actor (private IP address) | Low | Passive |
| [209.1](209.1.md) | Generation of error message containing sensitive information | Low | Passive |
| [319.1](319.1.md) | Mixed Content | Info | Passive |

View File

@ -10,6 +10,8 @@ module Gitlab
text_field :activation_code
button :activate
label :terms_of_services, text: /I agree that/
link :remove_license, 'data-testid': 'license-remove-action'
button :confirm_ok_button
p :plan
p :started
p :name
@ -21,6 +23,9 @@ module Gitlab
h2 :users_over_subscription
table :subscription_history
span :no_valid_license_alert, text: /no longer has a valid license/
h3 :no_active_subscription_title, text: /do not have an active subscription/
def accept_terms
terms_of_services_element.click # workaround for hidden checkbox
end
@ -39,7 +44,7 @@ module Gitlab
# @param license_type [Hash] Type of the license
# @option license_type [String] 'license file'
# @option license_type [String] 'cloud license'
# @return [Boolean] True if record exsists, false if not
# @return [Boolean] True if record exists, false if not
def has_subscription_record?(plan, users_in_license, license_type)
# find any records that have a matching plan and seats and type
subscription_history_element.hashes.any? do |record|

View File

@ -163,6 +163,20 @@ module QA
end
end
# Get users from the API
#
# @param [Integer] per_page the number of pages to traverse (used for pagination)
# @return [Array<Hash>] parsed response body
def self.all(per_page: 100)
response = nil
Resource::User.init do |user|
response = user.get(Runtime::API::Request.new(Runtime::API::Client.as_admin,
'/users',
per_page: per_page.to_s).url)
raise ResourceQueryError unless response.code == 200
end.parse_body(response)
end
def approve!
response = post(Runtime::API::Request.new(api_client, api_approve_path).url, nil)
return if response.code == 201

View File

@ -0,0 +1,68 @@
# frozen_string_literal: true
require 'spec_helper'
require_migration!
RSpec.describe ChangePublicProjectsCostFactor, :migration do
# This is a workaround to force the migration to run against the
# `gitlab_ci` schema. Otherwise it only runs against `gitlab_main`.
around do |example| # rubocop: disable Style/MultilineIfModifier
with_reestablished_active_record_base do
reconfigure_db_connection(name: :ci)
example.run
end
end if Gitlab::Database.has_config?(:ci)
let(:runners) { table(:ci_runners) }
let!(:shared_1) { runners.create!(runner_type: 1, public_projects_minutes_cost_factor: 0) }
let!(:shared_2) { runners.create!(runner_type: 1, public_projects_minutes_cost_factor: 0) }
let!(:shared_3) { runners.create!(runner_type: 1, public_projects_minutes_cost_factor: 1) }
let!(:group_1) { runners.create!(runner_type: 2, public_projects_minutes_cost_factor: 0) }
describe '#up' do
context 'when on SaaS' do
before do
allow(Gitlab).to receive(:com?).and_return(true)
end
it 'updates the cost factor from 0 only for shared runners', :aggregate_failures do
migrate!
expect(shared_1.reload.public_projects_minutes_cost_factor).to eq(0.008)
expect(shared_2.reload.public_projects_minutes_cost_factor).to eq(0.008)
expect(shared_3.reload.public_projects_minutes_cost_factor).to eq(1)
expect(group_1.reload.public_projects_minutes_cost_factor).to eq(0)
end
end
context 'when on self-managed', :aggregate_failures do
it 'skips the migration' do
migrate!
expect(shared_1.public_projects_minutes_cost_factor).to eq(0)
expect(shared_2.public_projects_minutes_cost_factor).to eq(0)
expect(shared_3.public_projects_minutes_cost_factor).to eq(1)
expect(group_1.public_projects_minutes_cost_factor).to eq(0)
end
end
end
describe '#down' do
context 'when on SaaS' do
before do
allow(Gitlab).to receive(:com?).and_return(true)
end
it 'resets the cost factor to 0 only for shared runners that were updated', :aggregate_failures do
migrate!
schema_migrate_down!
expect(shared_1.public_projects_minutes_cost_factor).to eq(0)
expect(shared_2.public_projects_minutes_cost_factor).to eq(0)
expect(shared_3.public_projects_minutes_cost_factor).to eq(1)
expect(group_1.public_projects_minutes_cost_factor).to eq(0)
end
end
end
end