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
* [ ] Add expected impact to Reporter (20) members
* [ ] Add expected impact to Developer (30) 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
......
......@@ -139,6 +139,8 @@ proper-names:
"unicorn-worker-killer",
"URL",
"WebdriverIO",
"Workload Identity Pool",
"Workload Identity Provider",
"YAML",
"YouTrack"
]
......
......@@ -15,7 +15,6 @@ import {
FilterFields,
ListTypeTitles,
DraggableItemTypes,
active,
} from 'ee_else_ce/boards/constants';
import {
formatIssueInput,
......@@ -210,7 +209,6 @@ export default {
const variables = {
fullPath,
searchTerm,
state: active,
};
let query;
......
......@@ -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) => {
const award = state.awards.find((a) => a.name === name && a.user.id === state.currentUserId);
try {
if (award) {
await axios.delete(joinPaths(gon.relative_url_root || '', `${state.path}/${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'));
} else {
const { data } = await axios.post(joinPaths(gon.relative_url_root || '', state.path), {
name,
});
const optimisticAward = newOptimisticAward(name, state);
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);
......
......@@ -96,7 +96,7 @@ class ContainerRepository < ApplicationRecord
end
event :abort_import do
transition %i[pre_importing importing] => :import_aborted
transition ACTIVE_MIGRATION_STATES.map(&:to_sym) => :import_aborted
end
event :skip_import do
......@@ -205,6 +205,11 @@ class ContainerRepository < ApplicationRecord
super
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
def registry
@registry ||= begin
......@@ -287,10 +292,18 @@ class ContainerRepository < ApplicationRecord
update!(expiration_policy_started_at: Time.zone.now)
end
def migration_in_active_state?
migration_state.in?(ACTIVE_MIGRATION_STATES)
end
def migration_importing?
migration_state == 'importing'
end
def migration_pre_importing?
migration_state == 'pre_importing'
end
def migration_pre_import
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
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/229841
milestone: '13.2'
type: development
group: group::monitor
group: group::respond
default_enabled: false
......@@ -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
milestone: '14.6'
type: development
group: group::monitor
group: group::respond
default_enabled: false
......@@ -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
milestone: '13.4'
type: development
group: group::apm
group: group::respond
default_enabled: false
......@@ -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
milestone: '12.0'
type: development
group: group::monitor
group: group::respond
default_enabled: false
......@@ -5,4 +5,4 @@ rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/343286
milestone: '14.8'
type: development
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
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/258560
milestone: '12.8'
type: development
group: group::monitor
group: group::respond
default_enabled: false
......@@ -4,5 +4,5 @@ introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/58606
rollout_issue_url:
milestone: '13.11'
type: development
group: group::monitor
group: group::respond
default_enabled: true
......@@ -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
milestone: '14.7'
type: ops
group: group::monitor
group: group::respond
default_enabled: false
......@@ -4,6 +4,7 @@ Rails.autoloaders.each do |autoloader|
# We need to ignore these since these are non-Ruby files
# that do not define Ruby classes / modules
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?
# Mailer previews are loaded manually by Rails
......
......@@ -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. |
| `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. |
| `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. |
| `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`. |
......@@ -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).
### 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
> [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:
## Running GitLab Pages on a separate server
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:
......
......@@ -31,7 +31,7 @@ GET /projects/:id/merge_requests/:merge_request_iid/status_checks
"id": 2,
"name": "Rule 1",
"external_url": "https://gitlab.com/test-endpoint",
"status": "approved"
"status": "pass"
},
{
"id": 1,
......@@ -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 |
| `sha` | string | yes | SHA at `HEAD` of the source branch |
| `external_status_check_id` | integer | yes | ID of an external status check |
| `status` | string | no | Set to `pass` to pass the check |
NOTE:
`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
Windows runners on GitLab.com autoscale by launching virtual machines on
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).
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
......
......@@ -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
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 |
|:----------------|:-------|:---------|:------------|
......@@ -553,6 +553,14 @@ curl --request PUT --header "Gitlab-Kas-Api-Request: <JWT token>" \
}'
```
Example response:
```json
{
"uuid": "4773b2ee-5ba5-5e9f-b48c-5f7a17f0faac"
}
```
## Subscriptions
The subscriptions endpoint is used by [CustomersDot](https://gitlab.com/gitlab-org/customers-gitlab-com) (`customers.gitlab.com`)
......
......@@ -105,6 +105,8 @@ Existing:
**Expire access tokens** to enable them.
- 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
Every application you authorize with your GitLab credentials is shown
......
......@@ -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.
![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
......
......@@ -343,3 +343,30 @@ run tests:
reports:
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
joins(:findings).merge(Vulnerabilities::Finding.by_location_cluster_agent(agent_ids))
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
delegate :default_branch, :name, to: :project, prefix: true, allow_nil: true
......
......@@ -20,7 +20,7 @@ module Geo
def execute
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!
create_repository_updated_event!
end
......
......@@ -4,5 +4,5 @@ introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/40475
rollout_issue_url:
milestone: '13.4'
type: development
group: group::monitor
group: group::respond
default_enabled: true
......@@ -120,6 +120,7 @@ module EE
if result.success?
status result.http_status
{ uuid: result.payload[:vulnerability].finding_uuid }
else
render_api_error!(result.message, result.http_status)
end
......
......@@ -29,6 +29,8 @@ RSpec.describe Vulnerability do
let_it_be(:vulnerability) { create(:vulnerability, :sast, :confirmed, :low, project: project) }
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
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) }
......
......@@ -7,6 +7,8 @@ RSpec.describe Vulnerabilities::Finding do
it { is_expected.to define_enum_for(:severity) }
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])
with_them do
before do
......
......@@ -276,6 +276,12 @@ RSpec.describe API::Internal::Kubernetes do
expect(Vulnerability.all.first.finding.name).to eq(payload[:vulnerability][:name])
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
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
mount ::API::Internal::Pages
mount ::API::Internal::Kubernetes
mount ::API::Internal::MailRoom
mount ::API::Internal::ContainerRegistry::Migration
version 'v3', using: :path do
# Although the following endpoints are kept behind V3 namespace,
......
......@@ -6,7 +6,7 @@ module API
extend ActiveSupport::Concern
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.')
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
let(:package_version) { '1.3.7' }
let(:package_type) { 'maven_gradle' }
where(:authentication_token_type, :maven_header_name, :testcase) do
: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'
:project_deploy_token | 'Deploy-Token' | 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347602'
where(:case_name, :authentication_token_type, :maven_header_name, :testcase) do
'using personal access token' | :personal_access_token | 'Private-Token' | 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347601'
'using ci job token' | :ci_job_token | 'Job-Token' | 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347603'
'using project deploy token' | :project_deploy_token | 'Deploy-Token' | 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347602'
end
with_them do
......@@ -31,7 +31,7 @@ module QA
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
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)
......
......@@ -27,6 +27,7 @@ RSpec.describe 'Merge request > User awards emoji', :js do
it 'removes award from merge request' do
first('[data-testid="award-button"]').click
expect(first('[data-testid="award-button"]')).to have_content '1'
find('[data-testid="award-button"].selected').click
expect(first('[data-testid="award-button"]')).to have_content '0'
......
......@@ -315,14 +315,14 @@ describe('fetchMilestones', () => {
'project',
{
query: projectBoardMilestones,
variables: { fullPath: 'gitlab-org/gitlab', state: 'active' },
variables: { fullPath: 'gitlab-org/gitlab' },
},
],
[
'group',
{
query: groupBoardMilestones,
variables: { fullPath: 'gitlab-org/gitlab', state: 'active' },
variables: { fullPath: 'gitlab-org/gitlab' },
},
],
])(
......
......@@ -87,6 +87,26 @@ describe('Awards app actions', () => {
describe('toggleAward', () => {
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(() => {
mock = new MockAdapter(axios);
});
......@@ -110,8 +130,10 @@ describe('Awards app actions', () => {
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: [] }, [
makeOptimisticAddMutation(),
makeOptimisticRemoveMutation(),
{ type: 'ADD_NEW_AWARD', payload: { id: 1 } },
]);
});
......@@ -127,7 +149,7 @@ describe('Awards app actions', () => {
actions.toggleAward,
null,
{ path: '/awards', awards: [] },
[],
[makeOptimisticAddMutation(), makeOptimisticRemoveMutation()],
[],
() => {
expect(Sentry.captureException).toHaveBeenCalled();
......@@ -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 } };
describe('success', () => {
......@@ -160,6 +182,9 @@ describe('Awards app actions', () => {
});
describe('error', () => {
const currentUserId = 1;
const name = 'thumbsup';
beforeEach(() => {
mock.onDelete(`${relativeRootUrl || ''}/awards/1`).reply(500);
});
......@@ -167,13 +192,13 @@ describe('Awards app actions', () => {
it('calls Sentry.captureException', async () => {
await testAction(
actions.toggleAward,
'thumbsup',
name,
{
path: '/awards',
currentUserId: 1,
currentUserId,
awards: [mockData],
},
[],
[makeOptimisticRemoveMutation(1), makeOptimisticAddMutation(1, name, currentUserId)],
[],
() => {
expect(Sentry.captureException).toHaveBeenCalled();
......
......@@ -312,6 +312,21 @@ RSpec.describe ContainerRepository, :aggregate_failures do
expect { repository.skip_import }.to raise_error(ArgumentError)
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
describe '#tag' do
......@@ -819,6 +834,18 @@ RSpec.describe ContainerRepository, :aggregate_failures do
it { is_expected.to eq([repository]) }
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
subject { container_repository.migration_importing? }
......@@ -831,6 +858,18 @@ RSpec.describe ContainerRepository, :aggregate_failures do
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
let_it_be_with_reload(: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