Commit 508aea98 authored by Shinya Maeda's avatar Shinya Maeda

Merge branch 'master' into 351715-fix-agent-naming-capilization

parents f85c259b 0e8f4ae8
...@@ -63,7 +63,12 @@ Consider adding checkboxes and expectations of users with certain levels of memb ...@@ -63,7 +63,12 @@ Consider adding checkboxes and expectations of users with certain levels of memb
* [ ] Add expected impact to Reporter (20) members * [ ] Add expected impact to Reporter (20) members
* [ ] Add expected impact to Developer (30) members * [ ] Add expected impact to Developer (30) members
* [ ] Add expected impact to Maintainer (40) members * [ ] Add expected impact to Maintainer (40) members
* [ ] Add expected impact to Owner (50) members --> * [ ] Add expected impact to Owner (50) members
Please consider performing a threat model for the code changes that are introduced as part of this feature. To get started, refer to our Threat Modeling handbook page https://about.gitlab.com/handbook/security/threat_modeling/#threat-modeling.
Don't hesitate to reach out to the Application Security Team (`@gitlab-com/gl-security/appsec`) to discuss any security concerns.
-->
### Documentation ### Documentation
......
...@@ -139,6 +139,8 @@ proper-names: ...@@ -139,6 +139,8 @@ proper-names:
"unicorn-worker-killer", "unicorn-worker-killer",
"URL", "URL",
"WebdriverIO", "WebdriverIO",
"Workload Identity Pool",
"Workload Identity Provider",
"YAML", "YAML",
"YouTrack" "YouTrack"
] ]
......
...@@ -15,7 +15,6 @@ import { ...@@ -15,7 +15,6 @@ import {
FilterFields, FilterFields,
ListTypeTitles, ListTypeTitles,
DraggableItemTypes, DraggableItemTypes,
active,
} from 'ee_else_ce/boards/constants'; } from 'ee_else_ce/boards/constants';
import { import {
formatIssueInput, formatIssueInput,
...@@ -210,7 +209,6 @@ export default { ...@@ -210,7 +209,6 @@ export default {
const variables = { const variables = {
fullPath, fullPath,
searchTerm, searchTerm,
state: active,
}; };
let query; let query;
......
...@@ -33,20 +33,51 @@ export const fetchAwards = async ({ commit, dispatch, state }, page = '1') => { ...@@ -33,20 +33,51 @@ export const fetchAwards = async ({ commit, dispatch, state }, page = '1') => {
} }
}; };
/**
* Creates an intermediary award, used for display
* until the real award is loaded from the backend.
*/
const newOptimisticAward = (name, state) => {
const freeId = Math.min(...state.awards.map((a) => a.id), Number.MAX_SAFE_INTEGER) - 1;
return {
id: freeId,
name,
user: {
id: window.gon.current_user_id,
name: window.gon.current_user_fullname,
username: window.gon.current_username,
},
};
};
export const toggleAward = async ({ commit, state }, name) => { export const toggleAward = async ({ commit, state }, name) => {
const award = state.awards.find((a) => a.name === name && a.user.id === state.currentUserId); const award = state.awards.find((a) => a.name === name && a.user.id === state.currentUserId);
try { try {
if (award) { if (award) {
await axios.delete(joinPaths(gon.relative_url_root || '', `${state.path}/${award.id}`));
commit(REMOVE_AWARD, award.id); commit(REMOVE_AWARD, award.id);
await axios
.delete(joinPaths(gon.relative_url_root || '', `${state.path}/${award.id}`))
.catch((err) => {
commit(ADD_NEW_AWARD, award);
throw err;
});
showToast(__('Award removed')); showToast(__('Award removed'));
} else { } else {
const { data } = await axios.post(joinPaths(gon.relative_url_root || '', state.path), { const optimisticAward = newOptimisticAward(name, state);
name,
}); commit(ADD_NEW_AWARD, optimisticAward);
const { data } = await axios
.post(joinPaths(gon.relative_url_root || '', state.path), {
name,
})
.finally(() => {
commit(REMOVE_AWARD, optimisticAward.id);
});
commit(ADD_NEW_AWARD, data); commit(ADD_NEW_AWARD, data);
......
...@@ -96,7 +96,7 @@ class ContainerRepository < ApplicationRecord ...@@ -96,7 +96,7 @@ class ContainerRepository < ApplicationRecord
end end
event :abort_import do event :abort_import do
transition %i[pre_importing importing] => :import_aborted transition ACTIVE_MIGRATION_STATES.map(&:to_sym) => :import_aborted
end end
event :skip_import do event :skip_import do
...@@ -205,6 +205,11 @@ class ContainerRepository < ApplicationRecord ...@@ -205,6 +205,11 @@ class ContainerRepository < ApplicationRecord
super super
end end
def finish_pre_import_and_start_import
# nothing to do between those two transitions for now.
finish_pre_import && start_import
end
# rubocop: disable CodeReuse/ServiceClass # rubocop: disable CodeReuse/ServiceClass
def registry def registry
@registry ||= begin @registry ||= begin
...@@ -287,10 +292,18 @@ class ContainerRepository < ApplicationRecord ...@@ -287,10 +292,18 @@ class ContainerRepository < ApplicationRecord
update!(expiration_policy_started_at: Time.zone.now) update!(expiration_policy_started_at: Time.zone.now)
end end
def migration_in_active_state?
migration_state.in?(ACTIVE_MIGRATION_STATES)
end
def migration_importing? def migration_importing?
migration_state == 'importing' migration_state == 'importing'
end end
def migration_pre_importing?
migration_state == 'pre_importing'
end
def migration_pre_import def migration_pre_import
return :error unless gitlab_api_client.supports_gitlab_api? return :error unless gitlab_api_client.supports_gitlab_api?
......
...@@ -4,5 +4,5 @@ introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/37195 ...@@ -4,5 +4,5 @@ introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/37195
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/229841 rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/229841
milestone: '13.2' milestone: '13.2'
type: development type: development
group: group::monitor group: group::respond
default_enabled: false default_enabled: false
...@@ -4,5 +4,5 @@ introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/74337 ...@@ -4,5 +4,5 @@ introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/74337
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/345769 rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/345769
milestone: '14.6' milestone: '14.6'
type: development type: development
group: group::monitor group: group::respond
default_enabled: false default_enabled: false
...@@ -4,5 +4,5 @@ introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/40103 ...@@ -4,5 +4,5 @@ introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/40103
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/241697 rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/241697
milestone: '13.4' milestone: '13.4'
type: development type: development
group: group::apm group: group::respond
default_enabled: false default_enabled: false
...@@ -4,5 +4,5 @@ introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/13443 ...@@ -4,5 +4,5 @@ introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/13443
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/255304 rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/255304
milestone: '12.0' milestone: '12.0'
type: development type: development
group: group::monitor group: group::respond
default_enabled: false default_enabled: false
...@@ -5,4 +5,4 @@ rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/343286 ...@@ -5,4 +5,4 @@ rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/343286
milestone: '14.8' milestone: '14.8'
type: development type: development
group: group::pipeline execution group: group::pipeline execution
default_enabled: false default_enabled: true
...@@ -4,5 +4,5 @@ introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/24296 ...@@ -4,5 +4,5 @@ introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/24296
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/258560 rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/258560
milestone: '12.8' milestone: '12.8'
type: development type: development
group: group::monitor group: group::respond
default_enabled: false default_enabled: false
...@@ -4,5 +4,5 @@ introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/58606 ...@@ -4,5 +4,5 @@ introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/58606
rollout_issue_url: rollout_issue_url:
milestone: '13.11' milestone: '13.11'
type: development type: development
group: group::monitor group: group::respond
default_enabled: true default_enabled: true
...@@ -4,5 +4,5 @@ introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/77168 ...@@ -4,5 +4,5 @@ introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/77168
rollout_issue_url: https://gitlab.com/gitlab-com/gl-infra/production/-/issues/6086 rollout_issue_url: https://gitlab.com/gitlab-com/gl-infra/production/-/issues/6086
milestone: '14.7' milestone: '14.7'
type: ops type: ops
group: group::monitor group: group::respond
default_enabled: false default_enabled: false
...@@ -4,6 +4,7 @@ Rails.autoloaders.each do |autoloader| ...@@ -4,6 +4,7 @@ Rails.autoloaders.each do |autoloader|
# We need to ignore these since these are non-Ruby files # We need to ignore these since these are non-Ruby files
# that do not define Ruby classes / modules # that do not define Ruby classes / modules
autoloader.ignore(Rails.root.join('lib/support')) autoloader.ignore(Rails.root.join('lib/support'))
autoloader.ignore(Rails.root.join('lib/gitlab/ci/parsers/security/validators/schemas'))
autoloader.ignore(Rails.root.join('ee/lib/ee/gitlab/ci/parsers/security/validators/schemas')) if Gitlab.ee? autoloader.ignore(Rails.root.join('ee/lib/ee/gitlab/ci/parsers/security/validators/schemas')) if Gitlab.ee?
# Mailer previews are loaded manually by Rails # Mailer previews are loaded manually by Rails
......
...@@ -238,6 +238,7 @@ control over how the Pages daemon runs and serves content in your environment. ...@@ -238,6 +238,7 @@ control over how the Pages daemon runs and serves content in your environment.
| `artifacts_server_url` | API URL to proxy artifact requests to. Defaults to GitLab `external URL` + `/api/v4`, for example `https://gitlab.com/api/v4`. When running a [separate Pages server](#running-gitlab-pages-on-a-separate-server), this URL must point to the main GitLab server's API. | | `artifacts_server_url` | API URL to proxy artifact requests to. Defaults to GitLab `external URL` + `/api/v4`, for example `https://gitlab.com/api/v4`. When running a [separate Pages server](#running-gitlab-pages-on-a-separate-server), this URL must point to the main GitLab server's API. |
| `auth_redirect_uri` | Callback URL for authenticating with GitLab. Defaults to project's subdomain of `pages_external_url` + `/auth`. | | `auth_redirect_uri` | Callback URL for authenticating with GitLab. Defaults to project's subdomain of `pages_external_url` + `/auth`. |
| `auth_secret` | Secret key for signing authentication requests. Leave blank to pull automatically from GitLab during OAuth registration. | | `auth_secret` | Secret key for signing authentication requests. Leave blank to pull automatically from GitLab during OAuth registration. |
| `client_cert_key_pairs` | Client certificates and keys used for mutual TLS with the GitLab API. See [Support mutual TLS when calling the GitLab API](#support-mutual-tls-when-calling-the-gitlab-api) for details. |
| `dir` | Working directory for configuration and secrets files. | | `dir` | Working directory for configuration and secrets files. |
| `enable` | Enable or disable GitLab Pages on the current system. | | `enable` | Enable or disable GitLab Pages on the current system. |
| `external_http` | Configure Pages to bind to one or more secondary IP addresses, serving HTTP requests. Multiple addresses can be given as an array, along with exact ports, for example `['1.2.3.4', '1.2.3.5:8063']`. Sets value for `listen_http`. | | `external_http` | Configure Pages to bind to one or more secondary IP addresses, serving HTTP requests. Multiple addresses can be given as an array, along with exact ports, for example `['1.2.3.4', '1.2.3.5:8063']`. Sets value for `listen_http`. |
...@@ -511,6 +512,20 @@ Authority (CA) in the system certificate store. ...@@ -511,6 +512,20 @@ Authority (CA) in the system certificate store.
For Omnibus, this is fixed by [installing a custom CA in Omnibus GitLab](https://docs.gitlab.com/omnibus/settings/ssl.html#install-custom-public-certificates). For Omnibus, this is fixed by [installing a custom CA in Omnibus GitLab](https://docs.gitlab.com/omnibus/settings/ssl.html#install-custom-public-certificates).
### Support mutual TLS when calling the GitLab API
If GitLab has been [configured to require mutual TLS](https://docs.gitlab.com/omnibus/settings/nginx.html#enable-2-way-ssl-client-authentication), you need to add the client certificates to Pages:
1. Configure in `/etc/gitlab/gitlab.rb`:
```ruby
gitlab_pages['client_cert_key_pairs'] = ['</path/to/cert>:</path/to/key>']
```
Where `</path/to/cert>` and `</path/to/key>` are the file paths to the client certificate and its respective key file.
Both of these files must be encoded in PEM format.
1. To configure Pages to validate the server certificates, [add the root CA to the system trust store](#using-a-custom-certificate-authority-ca).
### ZIP serving and cache configuration ### ZIP serving and cache configuration
> [Introduced](https://gitlab.com/gitlab-org/gitlab-pages/-/merge_requests/392) in GitLab 13.7. > [Introduced](https://gitlab.com/gitlab-org/gitlab-pages/-/merge_requests/392) in GitLab 13.7.
...@@ -688,7 +703,7 @@ To override the global maximum pages size for a specific group: ...@@ -688,7 +703,7 @@ To override the global maximum pages size for a specific group:
## Running GitLab Pages on a separate server ## Running GitLab Pages on a separate server
You can run the GitLab Pages daemon on a separate server to decrease the load on You can run the GitLab Pages daemon on a separate server to decrease the load on
your main application server. This configuration does not support mutual TLS (mTLS). See the [corresponding feature proposal](https://gitlab.com/gitlab-org/gitlab-pages/-/issues/548) for more information. your main application server.
To configure GitLab Pages on a separate server: To configure GitLab Pages on a separate server:
......
...@@ -31,7 +31,7 @@ GET /projects/:id/merge_requests/:merge_request_iid/status_checks ...@@ -31,7 +31,7 @@ GET /projects/:id/merge_requests/:merge_request_iid/status_checks
"id": 2, "id": 2,
"name": "Rule 1", "name": "Rule 1",
"external_url": "https://gitlab.com/test-endpoint", "external_url": "https://gitlab.com/test-endpoint",
"status": "approved" "status": "pass"
}, },
{ {
"id": 1, "id": 1,
...@@ -60,6 +60,7 @@ POST /projects/:id/merge_requests/:merge_request_iid/status_check_responses ...@@ -60,6 +60,7 @@ POST /projects/:id/merge_requests/:merge_request_iid/status_check_responses
| `merge_request_iid` | integer | yes | IID of a merge request | | `merge_request_iid` | integer | yes | IID of a merge request |
| `sha` | string | yes | SHA at `HEAD` of the source branch | | `sha` | string | yes | SHA at `HEAD` of the source branch |
| `external_status_check_id` | integer | yes | ID of an external status check | | `external_status_check_id` | integer | yes | ID of an external status check |
| `status` | string | no | Set to `pass` to pass the check |
NOTE: NOTE:
`sha` must be the SHA at the `HEAD` of the merge request's source branch. `sha` must be the SHA at the `HEAD` of the merge request's source branch.
......
...@@ -15,7 +15,7 @@ change when the beta period ends, as discussed in this [related issue](https://g ...@@ -15,7 +15,7 @@ change when the beta period ends, as discussed in this [related issue](https://g
Windows runners on GitLab.com autoscale by launching virtual machines on Windows runners on GitLab.com autoscale by launching virtual machines on
the Google Cloud Platform. This solution uses an the Google Cloud Platform. This solution uses an
[autoscaling driver](https://gitlab.com/gitlab-org/ci-cd/custom-executor-drivers/autoscaler/tree/master/docs/readme.md) [autoscaling driver](https://gitlab.com/gitlab-org/ci-cd/custom-executor-drivers/autoscaler/-/blob/main/docs/README.md)
developed by GitLab for the [custom executor](https://docs.gitlab.com/runner/executors/custom.html). developed by GitLab for the [custom executor](https://docs.gitlab.com/runner/executors/custom.html).
Windows runners execute your CI/CD jobs on `n1-standard-2` instances with Windows runners execute your CI/CD jobs on `n1-standard-2` instances with
2 vCPUs and 7.5 GB RAM. You can find a full list of available Windows packages in 2 vCPUs and 7.5 GB RAM. You can find a full list of available Windows packages in
......
...@@ -507,7 +507,7 @@ curl --request POST --header "Gitlab-Kas-Api-Request: <JWT token>" \ ...@@ -507,7 +507,7 @@ curl --request POST --header "Gitlab-Kas-Api-Request: <JWT token>" \
Called from the GitLab Agent Server (`kas`) to create a security vulnerability Called from the GitLab Agent Server (`kas`) to create a security vulnerability
from a Starboard vulnerability report. This request is idempotent. Multiple requests with the same data from a Starboard vulnerability report. This request is idempotent. Multiple requests with the same data
create a single vulnerability. create a single vulnerability. The response contains the UUID of the created vulnerability finding.
| Attribute | Type | Required | Description | | Attribute | Type | Required | Description |
|:----------------|:-------|:---------|:------------| |:----------------|:-------|:---------|:------------|
...@@ -553,6 +553,14 @@ curl --request PUT --header "Gitlab-Kas-Api-Request: <JWT token>" \ ...@@ -553,6 +553,14 @@ curl --request PUT --header "Gitlab-Kas-Api-Request: <JWT token>" \
}' }'
``` ```
Example response:
```json
{
"uuid": "4773b2ee-5ba5-5e9f-b48c-5f7a17f0faac"
}
```
## Subscriptions ## Subscriptions
The subscriptions endpoint is used by [CustomersDot](https://gitlab.com/gitlab-org/customers-gitlab-com) (`customers.gitlab.com`) The subscriptions endpoint is used by [CustomersDot](https://gitlab.com/gitlab-org/customers-gitlab-com) (`customers.gitlab.com`)
......
...@@ -105,6 +105,8 @@ Existing: ...@@ -105,6 +105,8 @@ Existing:
**Expire access tokens** to enable them. **Expire access tokens** to enable them.
- Tokens must be [revoked](../api/oauth2.md#revoke-a-token) or they don't expire. - Tokens must be [revoked](../api/oauth2.md#revoke-a-token) or they don't expire.
When applications are deleted, all grants and tokens associated with the application are also deleted.
## Authorized applications ## Authorized applications
Every application you authorize with your GitLab credentials is shown Every application you authorize with your GitLab credentials is shown
......
...@@ -105,7 +105,7 @@ label defined in the [global settings](#configuration) is used. ...@@ -105,7 +105,7 @@ label defined in the [global settings](#configuration) is used.
The label is shown on all project pages in the upper right corner. The label is shown on all project pages in the upper right corner.
![classification label on project page](img/classification_label_on_project_page.png) ![classification label on project page](img/classification_label_on_project_page_v14_8.png)
<!-- ## Troubleshooting <!-- ## Troubleshooting
......
...@@ -343,3 +343,30 @@ run tests: ...@@ -343,3 +343,30 @@ run tests:
reports: reports:
cobertura: coverage.xml cobertura: coverage.xml
``` ```
### Ruby example
The following [`.gitlab-ci.yml`](../../../ci/yaml/index.md) example for Ruby uses
- [`rspec`](https://rspec.info/) to run tests.
- [`simplecov`](https://github.com/simplecov-ruby/simplecov) and [`simplecov-cobertura`](https://github.com/dashingrocket/simplecov-cobertura)
to record the coverage profile and create a report in the Cobertura XML format.
This example assumes:
- That [`bundler`](https://bundler.io/) is being used for dependency management.
The `rspec`, `simplecov` and `simplecov-cobertura` gems have been added to your `Gemfile`.
- The `CoberturaFormatter` has been added to your `SimpleCov.formatters`
configuration within the `spec_helper.rb` file.
```yaml
run tests:
stage: test
image: ruby:3.1
script:
- bundle install
- bundle exec rspec
artifacts:
reports:
cobertura: coverage/coverage.xml
```
...@@ -127,7 +127,7 @@ module EE ...@@ -127,7 +127,7 @@ module EE
joins(:findings).merge(Vulnerabilities::Finding.by_location_cluster_agent(agent_ids)) joins(:findings).merge(Vulnerabilities::Finding.by_location_cluster_agent(agent_ids))
end end
delegate :scanner_name, :scanner_external_id, :scanner_id, :metadata, :message, :description, :details, delegate :scanner_name, :scanner_external_id, :scanner_id, :metadata, :message, :description, :details, :uuid,
to: :finding, prefix: true, allow_nil: true to: :finding, prefix: true, allow_nil: true
delegate :default_branch, :name, to: :project, prefix: true, allow_nil: true delegate :default_branch, :name, to: :project, prefix: true, allow_nil: true
......
...@@ -20,7 +20,7 @@ module Geo ...@@ -20,7 +20,7 @@ module Geo
def execute def execute
return false unless Gitlab::Geo.primary? return false unless Gitlab::Geo.primary?
::Gitlab::Database.allow_cross_joins_across_databases(url: 'https://gitlab.com/gitlab-org/gitlab/-/issues/351271') do ::Gitlab::Database::QueryAnalyzers::PreventCrossDatabaseModification.allow_cross_database_modification_within_transaction(url: 'https://gitlab.com/gitlab-org/gitlab/-/issues/351271') do
reset_repository_checksum! reset_repository_checksum!
create_repository_updated_event! create_repository_updated_event!
end end
......
...@@ -4,5 +4,5 @@ introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/40475 ...@@ -4,5 +4,5 @@ introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/40475
rollout_issue_url: rollout_issue_url:
milestone: '13.4' milestone: '13.4'
type: development type: development
group: group::monitor group: group::respond
default_enabled: true default_enabled: true
...@@ -120,6 +120,7 @@ module EE ...@@ -120,6 +120,7 @@ module EE
if result.success? if result.success?
status result.http_status status result.http_status
{ uuid: result.payload[:vulnerability].finding_uuid }
else else
render_api_error!(result.message, result.http_status) render_api_error!(result.message, result.http_status)
end end
......
...@@ -29,6 +29,8 @@ RSpec.describe Vulnerability do ...@@ -29,6 +29,8 @@ RSpec.describe Vulnerability do
let_it_be(:vulnerability) { create(:vulnerability, :sast, :confirmed, :low, project: project) } let_it_be(:vulnerability) { create(:vulnerability, :sast, :confirmed, :low, project: project) }
let_it_be(:finding) { create(:vulnerabilities_finding, vulnerability: vulnerability) } let_it_be(:finding) { create(:vulnerabilities_finding, vulnerability: vulnerability) }
it { is_expected.to have_locked_schema('f2d19034bdc7733dad8451dbb26250298f7de27895e99a2a0932f6d728f3cc27').reference('https://gitlab.com/gitlab-org/gitlab/-/issues/349315') }
describe 'enums' do describe 'enums' do
it { is_expected.to define_enum_for(:state).with_values(state_values) } it { is_expected.to define_enum_for(:state).with_values(state_values) }
it { is_expected.to define_enum_for(:severity).with_values(severity_values).with_prefix(:severity) } it { is_expected.to define_enum_for(:severity).with_values(severity_values).with_prefix(:severity) }
......
...@@ -7,6 +7,8 @@ RSpec.describe Vulnerabilities::Finding do ...@@ -7,6 +7,8 @@ RSpec.describe Vulnerabilities::Finding do
it { is_expected.to define_enum_for(:severity) } it { is_expected.to define_enum_for(:severity) }
it { is_expected.to define_enum_for(:detection_method) } it { is_expected.to define_enum_for(:detection_method) }
it { is_expected.to have_locked_schema('be0977fb3642f60c0c04b2197fe55d109d27f9630a011f9a64fd31b9716a7c7f').reference('https://gitlab.com/gitlab-org/gitlab/-/issues/349315') }
where(vulnerability_finding_signatures: [true, false]) where(vulnerability_finding_signatures: [true, false])
with_them do with_them do
before do before do
......
...@@ -276,6 +276,12 @@ RSpec.describe API::Internal::Kubernetes do ...@@ -276,6 +276,12 @@ RSpec.describe API::Internal::Kubernetes do
expect(Vulnerability.all.first.finding.name).to eq(payload[:vulnerability][:name]) expect(Vulnerability.all.first.finding.name).to eq(payload[:vulnerability][:name])
end end
it "responds with the created vulnerability's UUID" do
send_request(params: payload)
expect(json_response).to match("uuid" => Vulnerability.last.finding.uuid)
end
context 'when payload is invalid' do context 'when payload is invalid' do
let(:payload) { { vulnerability: 'invalid' } } let(:payload) { { vulnerability: 'invalid' } }
......
# frozen_string_literal: true
class SchemaVersion # rubocop:disable Gitlab/NamespacedClass
def initialize(model)
@model = model
end
def current
(column_names + index_names).sort
.join('-')
.then { |s| Digest::SHA256.hexdigest(s) }
end
private
attr_reader :model
delegate :table_name, :connection, to: :model, private: true
def column_names
connection.columns(table_name).map(&:name)
end
def index_names
connection.indexes(table_name).map(&:name)
end
end
RSpec::Matchers.define :have_locked_schema do |locked_version|
LOCKED_SCHEMA_ERROR_MESSAGE = <<~TEXT
Schema of the "%<table_name>s" table has been locked and does not match
with the current version "%<current_schema_version>s".
Please see "%<reference>s" for more details.
TEXT
chain(:reference) do |value|
@reference = value
end
match do
@table_name = described_class.table_name
@current_schema_version = SchemaVersion.new(described_class).current
@current_schema_version == locked_version
end
failure_message do
format(LOCKED_SCHEMA_ERROR_MESSAGE, table_name: @table_name, current_schema_version: @current_schema_version, reference: @reference)
end
end
...@@ -305,6 +305,7 @@ module API ...@@ -305,6 +305,7 @@ module API
mount ::API::Internal::Pages mount ::API::Internal::Pages
mount ::API::Internal::Kubernetes mount ::API::Internal::Kubernetes
mount ::API::Internal::MailRoom mount ::API::Internal::MailRoom
mount ::API::Internal::ContainerRegistry::Migration
version 'v3', using: :path do version 'v3', using: :path do
# Although the following endpoints are kept behind V3 namespace, # Although the following endpoints are kept behind V3 namespace,
......
...@@ -6,7 +6,7 @@ module API ...@@ -6,7 +6,7 @@ module API
extend ActiveSupport::Concern extend ActiveSupport::Concern
included do included do
rescue_from Faraday::Error, ContainerRegistry::Path::InvalidRegistryPathError do |e| rescue_from Faraday::Error, ::ContainerRegistry::Path::InvalidRegistryPathError do |e|
service_unavailable!('We are having trouble connecting to the Container Registry. If this error persists, please review the troubleshooting documentation.') service_unavailable!('We are having trouble connecting to the Container Registry. If this error persists, please review the troubleshooting documentation.')
end end
end end
......
# frozen_string_literal: true
module API
module Internal
module ContainerRegistry
class Migration < ::API::Base
feature_category :container_registry
STATUS_PRE_IMPORT_COMPLETE = 'pre_import_complete'
STATUS_PRE_IMPORT_FAILED = 'pre_import_failed'
STATUS_IMPORT_COMPLETE = 'import_complete'
STATUS_IMPORT_FAILED = 'import_failed'
POSSIBLE_VALUES = [
STATUS_PRE_IMPORT_COMPLETE,
STATUS_PRE_IMPORT_FAILED,
STATUS_IMPORT_COMPLETE,
STATUS_IMPORT_FAILED
].freeze
before { authenticate! }
helpers do
def authenticate!
secret_token = Gitlab.config.registry.notification_secret
unauthorized! unless Devise.secure_compare(secret_token, headers['Authorization'])
end
def find_repository!(path)
::ContainerRepository.find_by_path!(::ContainerRegistry::Path.new(path))
end
end
params do
requires :repository_path, type: String, desc: 'The container repository path'
requires :status, type: String, values: POSSIBLE_VALUES, desc: 'The migration step status'
end
put 'internal/registry/repositories/*repository_path/migration/status' do
repository = find_repository!(declared_params[:repository_path])
unless repository.migration_in_active_state?
bad_request!("Wrong migration state (#{repository.migration_state})")
end
case declared_params[:status]
when STATUS_PRE_IMPORT_COMPLETE
unless repository.finish_pre_import_and_start_import
bad_request!("Couldn't transition from pre_importing to importing")
end
when STATUS_IMPORT_COMPLETE
unless repository.finish_import
bad_request!("Couldn't transition from importing to import_done")
end
when STATUS_IMPORT_FAILED, STATUS_PRE_IMPORT_FAILED
repository.abort_import
end
status 200
end
end
end
end
end
../../../../../../../ee/lib/ee/gitlab/ci/parsers/security/validators/schemas/14.0.0
\ No newline at end of file
../../../../../../../ee/lib/ee/gitlab/ci/parsers/security/validators/schemas/14.0.1
\ No newline at end of file
../../../../../../../ee/lib/ee/gitlab/ci/parsers/security/validators/schemas/14.0.2
\ No newline at end of file
../../../../../../../ee/lib/ee/gitlab/ci/parsers/security/validators/schemas/14.0.3
\ No newline at end of file
../../../../../../../ee/lib/ee/gitlab/ci/parsers/security/validators/schemas/14.0.4
\ No newline at end of file
../../../../../../../ee/lib/ee/gitlab/ci/parsers/security/validators/schemas/14.0.5
\ No newline at end of file
../../../../../../../ee/lib/ee/gitlab/ci/parsers/security/validators/schemas/14.0.6
\ No newline at end of file
../../../../../../../ee/lib/ee/gitlab/ci/parsers/security/validators/schemas/14.1.0
\ No newline at end of file
...@@ -13,10 +13,10 @@ module QA ...@@ -13,10 +13,10 @@ module QA
let(:package_version) { '1.3.7' } let(:package_version) { '1.3.7' }
let(:package_type) { 'maven_gradle' } let(:package_type) { 'maven_gradle' }
where(:authentication_token_type, :maven_header_name, :testcase) do where(:case_name, :authentication_token_type, :maven_header_name, :testcase) do
:personal_access_token | 'Private-Token' | 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347601' 'using personal access token' | :personal_access_token | 'Private-Token' | 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347601'
:ci_job_token | 'Job-Token' | 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347603' 'using ci job token' | :ci_job_token | 'Job-Token' | 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347603'
:project_deploy_token | 'Deploy-Token' | 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347602' 'using project deploy token' | :project_deploy_token | 'Deploy-Token' | 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347602'
end end
with_them do with_them do
...@@ -31,7 +31,7 @@ module QA ...@@ -31,7 +31,7 @@ module QA
end end
end end
it "pushes and pulls a maven package via gradle using #{params[:authentication_token_type]}", testcase: params[:testcase] do it 'pushes and pulls a maven package via gradle', testcase: params[:testcase] do
Support::Retrier.retry_on_exception(max_attempts: 3, sleep_interval: 2) do Support::Retrier.retry_on_exception(max_attempts: 3, sleep_interval: 2) do
Resource::Repository::Commit.fabricate_via_api! do |commit| Resource::Repository::Commit.fabricate_via_api! do |commit|
gradle_upload_yaml = ERB.new(read_fixture('package_managers/maven', 'gradle_upload_package.yaml.erb')).result(binding) gradle_upload_yaml = ERB.new(read_fixture('package_managers/maven', 'gradle_upload_package.yaml.erb')).result(binding)
......
...@@ -27,6 +27,7 @@ RSpec.describe 'Merge request > User awards emoji', :js do ...@@ -27,6 +27,7 @@ RSpec.describe 'Merge request > User awards emoji', :js do
it 'removes award from merge request' do it 'removes award from merge request' do
first('[data-testid="award-button"]').click first('[data-testid="award-button"]').click
expect(first('[data-testid="award-button"]')).to have_content '1'
find('[data-testid="award-button"].selected').click find('[data-testid="award-button"].selected').click
expect(first('[data-testid="award-button"]')).to have_content '0' expect(first('[data-testid="award-button"]')).to have_content '0'
......
...@@ -315,14 +315,14 @@ describe('fetchMilestones', () => { ...@@ -315,14 +315,14 @@ describe('fetchMilestones', () => {
'project', 'project',
{ {
query: projectBoardMilestones, query: projectBoardMilestones,
variables: { fullPath: 'gitlab-org/gitlab', state: 'active' }, variables: { fullPath: 'gitlab-org/gitlab' },
}, },
], ],
[ [
'group', 'group',
{ {
query: groupBoardMilestones, query: groupBoardMilestones,
variables: { fullPath: 'gitlab-org/gitlab', state: 'active' }, variables: { fullPath: 'gitlab-org/gitlab' },
}, },
], ],
])( ])(
......
...@@ -87,6 +87,26 @@ describe('Awards app actions', () => { ...@@ -87,6 +87,26 @@ describe('Awards app actions', () => {
describe('toggleAward', () => { describe('toggleAward', () => {
let mock; let mock;
const optimisticAwardId = Number.MAX_SAFE_INTEGER - 1;
const makeOptimisticAddMutation = (
id = optimisticAwardId,
name = null,
userId = window.gon.current_user_id,
) => ({
type: 'ADD_NEW_AWARD',
payload: {
id,
name,
user: {
id: userId,
},
},
});
const makeOptimisticRemoveMutation = (id = optimisticAwardId) => ({
type: 'REMOVE_AWARD',
payload: id,
});
beforeEach(() => { beforeEach(() => {
mock = new MockAdapter(axios); mock = new MockAdapter(axios);
}); });
...@@ -110,8 +130,10 @@ describe('Awards app actions', () => { ...@@ -110,8 +130,10 @@ describe('Awards app actions', () => {
mock.onPost(`${relativeRootUrl || ''}/awards`).reply(200, { id: 1 }); mock.onPost(`${relativeRootUrl || ''}/awards`).reply(200, { id: 1 });
}); });
it('commits ADD_NEW_AWARD', async () => { it('adds an optimistic award, removes it, and then commits ADD_NEW_AWARD', async () => {
testAction(actions.toggleAward, null, { path: '/awards', awards: [] }, [ testAction(actions.toggleAward, null, { path: '/awards', awards: [] }, [
makeOptimisticAddMutation(),
makeOptimisticRemoveMutation(),
{ type: 'ADD_NEW_AWARD', payload: { id: 1 } }, { type: 'ADD_NEW_AWARD', payload: { id: 1 } },
]); ]);
}); });
...@@ -127,7 +149,7 @@ describe('Awards app actions', () => { ...@@ -127,7 +149,7 @@ describe('Awards app actions', () => {
actions.toggleAward, actions.toggleAward,
null, null,
{ path: '/awards', awards: [] }, { path: '/awards', awards: [] },
[], [makeOptimisticAddMutation(), makeOptimisticRemoveMutation()],
[], [],
() => { () => {
expect(Sentry.captureException).toHaveBeenCalled(); expect(Sentry.captureException).toHaveBeenCalled();
...@@ -137,7 +159,7 @@ describe('Awards app actions', () => { ...@@ -137,7 +159,7 @@ describe('Awards app actions', () => {
}); });
}); });
describe('removing a award', () => { describe('removing an award', () => {
const mockData = { id: 1, name: 'thumbsup', user: { id: 1 } }; const mockData = { id: 1, name: 'thumbsup', user: { id: 1 } };
describe('success', () => { describe('success', () => {
...@@ -160,6 +182,9 @@ describe('Awards app actions', () => { ...@@ -160,6 +182,9 @@ describe('Awards app actions', () => {
}); });
describe('error', () => { describe('error', () => {
const currentUserId = 1;
const name = 'thumbsup';
beforeEach(() => { beforeEach(() => {
mock.onDelete(`${relativeRootUrl || ''}/awards/1`).reply(500); mock.onDelete(`${relativeRootUrl || ''}/awards/1`).reply(500);
}); });
...@@ -167,13 +192,13 @@ describe('Awards app actions', () => { ...@@ -167,13 +192,13 @@ describe('Awards app actions', () => {
it('calls Sentry.captureException', async () => { it('calls Sentry.captureException', async () => {
await testAction( await testAction(
actions.toggleAward, actions.toggleAward,
'thumbsup', name,
{ {
path: '/awards', path: '/awards',
currentUserId: 1, currentUserId,
awards: [mockData], awards: [mockData],
}, },
[], [makeOptimisticRemoveMutation(1), makeOptimisticAddMutation(1, name, currentUserId)],
[], [],
() => { () => {
expect(Sentry.captureException).toHaveBeenCalled(); expect(Sentry.captureException).toHaveBeenCalled();
......
...@@ -312,6 +312,21 @@ RSpec.describe ContainerRepository, :aggregate_failures do ...@@ -312,6 +312,21 @@ RSpec.describe ContainerRepository, :aggregate_failures do
expect { repository.skip_import }.to raise_error(ArgumentError) expect { repository.skip_import }.to raise_error(ArgumentError)
end end
end end
describe '#finish_pre_import_and_start_import' do
let_it_be_with_reload(:repository) { create(:container_repository, :pre_importing) }
subject { repository.finish_pre_import_and_start_import }
before do |example|
unless example.metadata[:skip_import_success]
allow(repository).to receive(:migration_import).and_return(:ok)
end
end
it_behaves_like 'transitioning from allowed states', %w[pre_importing]
it_behaves_like 'transitioning to importing'
end
end end
describe '#tag' do describe '#tag' do
...@@ -819,6 +834,18 @@ RSpec.describe ContainerRepository, :aggregate_failures do ...@@ -819,6 +834,18 @@ RSpec.describe ContainerRepository, :aggregate_failures do
it { is_expected.to eq([repository]) } it { is_expected.to eq([repository]) }
end end
describe '#migration_in_active_state?' do
subject { container_repository.migration_in_active_state? }
ContainerRepository::MIGRATION_STATES.each do |state|
context "when in #{state} migration_state" do
let(:container_repository) { create(:container_repository, state.to_sym)}
it { is_expected.to eq(state == 'importing' || state == 'pre_importing') }
end
end
end
describe '#migration_importing?' do describe '#migration_importing?' do
subject { container_repository.migration_importing? } subject { container_repository.migration_importing? }
...@@ -831,6 +858,18 @@ RSpec.describe ContainerRepository, :aggregate_failures do ...@@ -831,6 +858,18 @@ RSpec.describe ContainerRepository, :aggregate_failures do
end end
end end
describe '#migration_pre_importing?' do
subject { container_repository.migration_pre_importing? }
ContainerRepository::MIGRATION_STATES.each do |state|
context "when in #{state} migration_state" do
let(:container_repository) { create(:container_repository, state.to_sym)}
it { is_expected.to eq(state == 'pre_importing') }
end
end
end
context 'with repositories' do context 'with repositories' do
let_it_be_with_reload(:repository) { create(:container_repository, :cleanup_unscheduled) } let_it_be_with_reload(:repository) { create(:container_repository, :cleanup_unscheduled) }
let_it_be(:other_repository) { create(:container_repository, :cleanup_unscheduled) } let_it_be(:other_repository) { create(:container_repository, :cleanup_unscheduled) }
......
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe API::Internal::ContainerRegistry::Migration do
let_it_be_with_reload(:repository) { create(:container_repository) }
let(:secret_token) { 'secret_token' }
let(:sent_token) { secret_token }
let(:repository_path) { repository.path }
let(:status) { 'pre_import_complete' }
let(:params) { { path: repository.path, status: status } }
before do
allow(Gitlab.config.registry).to receive(:notification_secret) { secret_token }
end
describe 'PUT /internal/registry/repositories/:path/migration/status' do
subject do
put api("/internal/registry/repositories/#{repository_path}/migration/status"),
params: params,
headers: { 'Authorization' => sent_token }
end
shared_examples 'returning an error' do |with_message: nil, returning_status: :bad_request|
it "returns bad request response" do
expect { subject }
.not_to change { repository.reload.migration_state }
expect(response).to have_gitlab_http_status(returning_status)
expect(response.body).to include(with_message) if with_message
end
end
context 'with a valid sent token' do
shared_examples 'updating the repository migration status' do |from:, to:|
it "updates the migration status from #{from} to #{to}" do
expect { subject }
.to change { repository.reload.migration_state }.from(from).to(to)
expect(response).to have_gitlab_http_status(:ok)
end
end
context 'with status pre_import_complete' do
let(:status) { 'pre_import_complete' }
it_behaves_like 'returning an error', with_message: 'Wrong migration state (default)'
context 'with repository in pre_importing migration state' do
let(:repository) { create(:container_repository, :pre_importing) }
before do
allow_next_found_instance_of(ContainerRepository) do |found_repository|
allow(found_repository).to receive(:migration_import).and_return(:ok)
end
end
it_behaves_like 'updating the repository migration status', from: 'pre_importing', to: 'importing'
context 'with a failing transition' do
before do
allow_next_found_instance_of(ContainerRepository) do |found_repository|
allow(found_repository).to receive(:finish_pre_import_and_start_import).and_return(false)
end
end
it_behaves_like 'returning an error', with_message: "Couldn't transition from pre_importing to importing"
end
end
context 'with repository in importing migration state' do
let(:repository) { create(:container_repository, :importing) }
it_behaves_like 'returning an error', with_message: "Couldn't transition from pre_importing to importing"
end
end
context 'with status import_complete' do
let(:status) { 'import_complete' }
it_behaves_like 'returning an error', with_message: 'Wrong migration state (default)'
context 'with repository in importing migration state' do
let(:repository) { create(:container_repository, :importing) }
let(:transition_result) { true }
it_behaves_like 'updating the repository migration status', from: 'importing', to: 'import_done'
context 'with a failing transition' do
before do
allow_next_found_instance_of(ContainerRepository) do |found_repository|
allow(found_repository).to receive(:finish_import).and_return(false)
end
end
it_behaves_like 'returning an error', with_message: "Couldn't transition from importing to import_done"
end
end
context 'with repository in pre_importing migration state' do
let(:repository) { create(:container_repository, :pre_importing) }
it_behaves_like 'returning an error', with_message: "Couldn't transition from importing to import_done"
end
end
%w[pre_import_failed import_failed].each do |status|
context 'with status pre_import_failed' do
let(:status) { 'pre_import_failed' }
it_behaves_like 'returning an error', with_message: 'Wrong migration state (default)'
context 'with repository in importing migration state' do
let(:repository) { create(:container_repository, :importing) }
it_behaves_like 'updating the repository migration status', from: 'importing', to: 'import_aborted'
end
context 'with repository in pre_importing migration state' do
let(:repository) { create(:container_repository, :pre_importing) }
it_behaves_like 'updating the repository migration status', from: 'pre_importing', to: 'import_aborted'
end
end
end
context 'with a non existing path' do
let(:repository_path) { 'this/does/not/exist' }
it_behaves_like 'returning an error', returning_status: :not_found
end
context 'with invalid status' do
let(:params) { super().merge(status: nil).compact }
it_behaves_like 'returning an error', returning_status: :bad_request
end
context 'with invalid path' do
let(:repository_path) { nil }
it_behaves_like 'returning an error', returning_status: :not_found
end
end
context 'with an invalid sent token' do
let(:sent_token) { 'not_valid' }
it_behaves_like 'returning an error', returning_status: :unauthorized
end
end
end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment