From afbf001676ce96bc55d891ec45bf4e68f204a853 Mon Sep 17 00:00:00 2001 From: GitLab Bot Date: Wed, 20 Apr 2022 00:09:27 +0000 Subject: [PATCH] Add latest changes from gitlab-org/gitlab@master --- .../javascripts/google_tag_manager/index.js | 47 +++++++------ .../explorer/components/delete_button.vue | 33 +++++---- .../components/list_page/image_list_row.vue | 19 ++++-- .../explorer/constants/list.js | 4 ++ .../stylesheets/page_bundles/issues_show.scss | 30 +++++++++ app/assets/stylesheets/pages/issues.scss | 29 -------- .../get_container_repositories.query.graphql | 2 + app/graphql/types/base_object.rb | 7 ++ .../types/container_repository_type.rb | 1 + app/helpers/namespaces_helper.rb | 9 +++ app/views/projects/issues/show.html.haml | 1 + app/workers/bulk_imports/entity_worker.rb | 29 +++++--- .../bulk_imports/export_request_worker.rb | 10 +-- app/workers/bulk_imports/pipeline_worker.rb | 34 +++++----- config/application.rb | 1 + config/initializers/graphql.rb | 5 -- config/routes.rb | 2 +- db/docs/ci_namespace_mirrors.yml | 2 +- db/docs/ci_project_mirrors.yml | 2 +- .../loose_foreign_keys_deleted_records.yml | 2 +- db/docs/namespaces_sync_events.yml | 2 +- db/docs/projects_sync_events.yml | 2 +- ...ize_traversal_ids_background_migrations.rb | 12 ++++ db/schema_migrations/20220412044906 | 1 + doc/administration/gitaly/recovery.md | 12 +++- doc/api/graphql/reference/index.md | 2 + doc/development/rake_tasks.md | 4 +- .../application_security/api_fuzzing/index.md | 60 +++++++++++++++++ .../application_security/dast_api/index.md | 60 +++++++++++++++++ locale/gitlab.pot | 3 + .../explorer/components/delete_button_spec.js | 67 +++++++++++-------- .../list_page/image_list_row_spec.js | 10 +++ .../container_registry/explorer/mock_data.js | 2 + spec/graphql/types/base_object_spec.rb | 20 ++++++ .../container_repository_details_type_spec.rb | 4 +- .../types/container_repository_type_spec.rb | 4 +- spec/helpers/namespaces_helper_spec.rb | 11 +++ ...raversal_ids_background_migrations_spec.rb | 60 +++++++++++++++++ .../bulk_imports/entity_worker_spec.rb | 45 +++++++++---- .../export_request_worker_spec.rb | 18 ++--- .../bulk_imports/pipeline_worker_spec.rb | 65 ++++++++++-------- 41 files changed, 543 insertions(+), 190 deletions(-) create mode 100644 app/assets/stylesheets/page_bundles/issues_show.scss delete mode 100644 config/initializers/graphql.rb create mode 100644 db/post_migrate/20220412044906_finalize_traversal_ids_background_migrations.rb create mode 100644 db/schema_migrations/20220412044906 create mode 100644 spec/migrations/finalize_traversal_ids_background_migrations_spec.rb diff --git a/app/assets/javascripts/google_tag_manager/index.js b/app/assets/javascripts/google_tag_manager/index.js index f42152006d2..a44a5b30e1e 100644 --- a/app/assets/javascripts/google_tag_manager/index.js +++ b/app/assets/javascripts/google_tag_manager/index.js @@ -232,35 +232,40 @@ export const trackTransaction = (transactionDetails) => { pushEnhancedEcommerceEvent('EECtransactionSuccess', eventData); }; -export const trackAddToCartUsageTab = () => { +export const pushEECproductAddToCartEvent = () => { if (!isSupported()) { return; } - const getStartedButton = document.querySelector('.js-buy-additional-minutes'); - getStartedButton.addEventListener('click', () => { - window.dataLayer.push({ - event: 'EECproductAddToCart', - ecommerce: { - currencyCode: 'USD', - add: { - products: [ - { - name: 'CI/CD Minutes', - id: '0003', - price: '10', - brand: 'GitLab', - category: 'DevOps', - variant: 'add-on', - quantity: 1, - }, - ], - }, + window.dataLayer.push({ + event: 'EECproductAddToCart', + ecommerce: { + currencyCode: 'USD', + add: { + products: [ + { + name: 'CI/CD Minutes', + id: '0003', + price: '10', + brand: 'GitLab', + category: 'DevOps', + variant: 'add-on', + quantity: 1, + }, + ], }, - }); + }, }); }; +export const trackAddToCartUsageTab = () => { + const getStartedButton = document.querySelector('.js-buy-additional-minutes'); + if (!getStartedButton) { + return; + } + getStartedButton.addEventListener('click', pushEECproductAddToCartEvent); +}; + export const trackCombinedGroupProjectForm = () => { if (!isSupported()) { return; diff --git a/app/assets/javascripts/packages_and_registries/container_registry/explorer/components/delete_button.vue b/app/assets/javascripts/packages_and_registries/container_registry/explorer/components/delete_button.vue index e4a1a1a8266..bb1dac40b92 100644 --- a/app/assets/javascripts/packages_and_registries/container_registry/explorer/components/delete_button.vue +++ b/app/assets/javascripts/packages_and_registries/container_registry/explorer/components/delete_button.vue @@ -1,13 +1,13 @@ diff --git a/app/assets/javascripts/packages_and_registries/container_registry/explorer/components/list_page/image_list_row.vue b/app/assets/javascripts/packages_and_registries/container_registry/explorer/components/list_page/image_list_row.vue index c1ec523574a..484903354e8 100644 --- a/app/assets/javascripts/packages_and_registries/container_registry/explorer/components/list_page/image_list_row.vue +++ b/app/assets/javascripts/packages_and_registries/container_registry/explorer/components/list_page/image_list_row.vue @@ -8,11 +8,13 @@ import ListItem from '~/vue_shared/components/registry/list_item.vue'; import { ASYNC_DELETE_IMAGE_ERROR_MESSAGE, LIST_DELETE_BUTTON_DISABLED, + LIST_DELETE_BUTTON_DISABLED_FOR_MIGRATION, REMOVE_REPOSITORY_LABEL, ROW_SCHEDULED_FOR_DELETION, CLEANUP_TIMED_OUT_ERROR_MESSAGE, IMAGE_DELETE_SCHEDULED_STATUS, IMAGE_FAILED_DELETED_STATUS, + IMAGE_MIGRATING_STATE, ROOT_IMAGE_TEXT, } from '../../constants/index'; import DeleteButton from '../delete_button.vue'; @@ -32,6 +34,7 @@ export default { directives: { GlTooltip: GlTooltipDirective, }, + inject: ['config'], props: { item: { type: Object, @@ -44,13 +47,12 @@ export default { }, }, i18n: { - LIST_DELETE_BUTTON_DISABLED, REMOVE_REPOSITORY_LABEL, ROW_SCHEDULED_FOR_DELETION, }, computed: { disabledDelete() { - return !this.item.canDelete || this.deleting; + return !this.item.canDelete || this.deleting || this.migrating; }, id() { return getIdFromGraphQLId(this.item.id); @@ -58,6 +60,9 @@ export default { deleting() { return this.item.status === IMAGE_DELETE_SCHEDULED_STATUS; }, + migrating() { + return this.item.migrationState === IMAGE_MIGRATING_STATE; + }, failedDelete() { return this.item.status === IMAGE_FAILED_DELETED_STATUS; }, @@ -83,6 +88,11 @@ export default { routerLinkEvent() { return this.deleting ? '' : 'click'; }, + deleteButtonTooltipTitle() { + return this.migrating + ? LIST_DELETE_BUTTON_DISABLED_FOR_MIGRATION + : LIST_DELETE_BUTTON_DISABLED; + }, }, }; @@ -144,8 +154,9 @@ export default { diff --git a/app/assets/javascripts/packages_and_registries/container_registry/explorer/constants/list.js b/app/assets/javascripts/packages_and_registries/container_registry/explorer/constants/list.js index 7fa950ccfd0..c7022d6070f 100644 --- a/app/assets/javascripts/packages_and_registries/container_registry/explorer/constants/list.js +++ b/app/assets/javascripts/packages_and_registries/container_registry/explorer/constants/list.js @@ -14,6 +14,9 @@ export const LIST_INTRO_TEXT = s__( export const LIST_DELETE_BUTTON_DISABLED = s__( 'ContainerRegistry|Missing or insufficient permission, delete button disabled', ); +export const LIST_DELETE_BUTTON_DISABLED_FOR_MIGRATION = s__( + `ContainerRegistry|Image repository temporarily cannot be marked for deletion. Please try again in a few minutes. %{docLinkStart}More details%{docLinkEnd}`, +); export const REMOVE_REPOSITORY_LABEL = s__('ContainerRegistry|Remove repository'); export const REMOVE_REPOSITORY_MODAL_TEXT = s__( 'ContainerRegistry|You are about to remove repository %{title}. Once you confirm, this repository will be permanently deleted.', @@ -45,6 +48,7 @@ export const EMPTY_RESULT_MESSAGE = s__( export const IMAGE_DELETE_SCHEDULED_STATUS = 'DELETE_SCHEDULED'; export const IMAGE_FAILED_DELETED_STATUS = 'DELETE_FAILED'; +export const IMAGE_MIGRATING_STATE = 'importing'; export const GRAPHQL_PAGE_SIZE = 10; export const SORT_FIELDS = [ diff --git a/app/assets/stylesheets/page_bundles/issues_show.scss b/app/assets/stylesheets/page_bundles/issues_show.scss new file mode 100644 index 00000000000..1642e6a39f3 --- /dev/null +++ b/app/assets/stylesheets/page_bundles/issues_show.scss @@ -0,0 +1,30 @@ +@import 'mixins_and_variables_and_functions'; + +.description.work-items-enabled { + ul.task-list { + > li.task-list-item { + padding-inline-start: 2.5rem; + + .js-add-task { + svg { + visibility: hidden; + } + + &:focus svg { + visibility: visible; + } + } + + > input.task-list-item-checkbox { + left: 1.25rem; + } + + &:hover, + &:focus-within { + .js-add-task svg { + visibility: visible; + } + } + } + } +} diff --git a/app/assets/stylesheets/pages/issues.scss b/app/assets/stylesheets/pages/issues.scss index f127b0dc66c..04e0ef6631e 100644 --- a/app/assets/stylesheets/pages/issues.scss +++ b/app/assets/stylesheets/pages/issues.scss @@ -307,32 +307,3 @@ ul.related-merge-requests > li gl-emoji { .issuable-header-slide-leave-to { transform: translateY(-100%); } - -.description.work-items-enabled { - ul.task-list { - > li.task-list-item { - padding-inline-start: 2.5rem; - - .js-add-task { - svg { - visibility: hidden; - } - - &:focus svg { - visibility: visible; - } - } - - > input.task-list-item-checkbox { - left: 1.25rem; - } - - &:hover, - &:focus-within { - .js-add-task svg { - visibility: visible; - } - } - } - } -} diff --git a/app/graphql/queries/container_registry/get_container_repositories.query.graphql b/app/graphql/queries/container_registry/get_container_repositories.query.graphql index 40e2934a038..264878ccaa2 100644 --- a/app/graphql/queries/container_registry/get_container_repositories.query.graphql +++ b/app/graphql/queries/container_registry/get_container_repositories.query.graphql @@ -23,6 +23,7 @@ query getProjectContainerRepositories( __typename nodes { id + migrationState name path status @@ -57,6 +58,7 @@ query getProjectContainerRepositories( __typename nodes { id + migrationState name path status diff --git a/app/graphql/types/base_object.rb b/app/graphql/types/base_object.rb index b5797f07aa6..4ad88e43f52 100644 --- a/app/graphql/types/base_object.rb +++ b/app/graphql/types/base_object.rb @@ -9,6 +9,13 @@ module Types field_class Types::BaseField edge_type_class Types::BaseEdge + def self.authorize(*args) + raise 'Cannot redefine authorize' if @authorize_args && args.any? + + @authorize_args = args.freeze if args.any? + @authorize_args || (superclass.respond_to?(:authorize) ? superclass.authorize : nil) + end + def self.accepts(*types) @accepts ||= [] @accepts += types diff --git a/app/graphql/types/container_repository_type.rb b/app/graphql/types/container_repository_type.rb index 3cd3730010b..dddf9a3ee97 100644 --- a/app/graphql/types/container_repository_type.rb +++ b/app/graphql/types/container_repository_type.rb @@ -14,6 +14,7 @@ module Types field :expiration_policy_started_at, Types::TimeType, null: true, description: 'Timestamp when the cleanup done by the expiration policy was started on the container repository.' field :id, GraphQL::Types::ID, null: false, description: 'ID of the container repository.' field :location, GraphQL::Types::String, null: false, description: 'URL of the container repository.' + field :migration_state, GraphQL::Types::String, null: false, description: 'Migration state of the container repository.' field :name, GraphQL::Types::String, null: false, description: 'Name of the container repository.' field :path, GraphQL::Types::String, null: false, description: 'Path of the container repository.' field :project, Types::ProjectType, null: false, description: 'Project of the container registry.' diff --git a/app/helpers/namespaces_helper.rb b/app/helpers/namespaces_helper.rb index 64b58d28fc9..cf386ee398a 100644 --- a/app/helpers/namespaces_helper.rb +++ b/app/helpers/namespaces_helper.rb @@ -88,6 +88,15 @@ module NamespacesHelper }.to_json end + def pipeline_usage_quota_app_data(namespace) + { + namespace_actual_plan_name: namespace.actual_plan_name, + namespace_path: namespace.full_path, + namespace_id: namespace.id, + page_size: page_size + } + end + private # Many importers create a temporary Group, so use the real diff --git a/app/views/projects/issues/show.html.haml b/app/views/projects/issues/show.html.haml index 6c6f98e0b20..3572d1d6556 100644 --- a/app/views/projects/issues/show.html.haml +++ b/app/views/projects/issues/show.html.haml @@ -2,6 +2,7 @@ - add_to_breadcrumbs _("Issues"), project_issues_path(@project) - breadcrumb_title @issue.to_reference - page_title "#{@issue.title} (#{@issue.to_reference})", _("Issues") +- add_page_specific_style 'page_bundles/issues_show' = render 'projects/issuable/show', issuable: @issue, api_awards_path: award_emoji_issue_api_path(@issue) = render 'projects/invite_members_modal', project: @project diff --git a/app/workers/bulk_imports/entity_worker.rb b/app/workers/bulk_imports/entity_worker.rb index 902762eda2f..f6b1c693fe4 100644 --- a/app/workers/bulk_imports/entity_worker.rb +++ b/app/workers/bulk_imports/entity_worker.rb @@ -12,12 +12,24 @@ module BulkImports worker_has_external_dependencies! def perform(entity_id, current_stage = nil) - return if stage_running?(entity_id, current_stage) + if stage_running?(entity_id, current_stage) + logger.info( + structured_payload( + entity_id: entity_id, + current_stage: current_stage, + message: 'Stage running' + ) + ) + + return + end logger.info( - worker: self.class.name, - entity_id: entity_id, - current_stage: current_stage + structured_payload( + entity_id: entity_id, + current_stage: current_stage, + message: 'Stage starting' + ) ) next_pipeline_trackers_for(entity_id).each do |pipeline_tracker| @@ -29,10 +41,11 @@ module BulkImports end rescue StandardError => e logger.error( - worker: self.class.name, - entity_id: entity_id, - current_stage: current_stage, - error_message: e.message + structured_payload( + entity_id: entity_id, + current_stage: current_stage, + message: e.message + ) ) Gitlab::ErrorTracking.track_exception(e, entity_id: entity_id) diff --git a/app/workers/bulk_imports/export_request_worker.rb b/app/workers/bulk_imports/export_request_worker.rb index 21040178cee..0d3e4f013dd 100644 --- a/app/workers/bulk_imports/export_request_worker.rb +++ b/app/workers/bulk_imports/export_request_worker.rb @@ -42,10 +42,12 @@ module BulkImports correlation_id_value: Labkit::Correlation::CorrelationId.current_or_new_id } - Gitlab::Import::Logger.warn( - attributes.merge( - bulk_import_id: entity.bulk_import.id, - bulk_import_entity_type: entity.source_type + Gitlab::Import::Logger.error( + structured_payload( + attributes.merge( + bulk_import_id: entity.bulk_import.id, + bulk_import_entity_type: entity.source_type + ) ) ) diff --git a/app/workers/bulk_imports/pipeline_worker.rb b/app/workers/bulk_imports/pipeline_worker.rb index aad93eef753..1a98705c151 100644 --- a/app/workers/bulk_imports/pipeline_worker.rb +++ b/app/workers/bulk_imports/pipeline_worker.rb @@ -18,18 +18,20 @@ module BulkImports if pipeline_tracker.present? logger.info( - worker: self.class.name, - entity_id: pipeline_tracker.entity.id, - pipeline_name: pipeline_tracker.pipeline_name + structured_payload( + entity_id: pipeline_tracker.entity.id, + pipeline_name: pipeline_tracker.pipeline_name + ) ) run(pipeline_tracker) else logger.error( - worker: self.class.name, - entity_id: entity_id, - pipeline_tracker_id: pipeline_tracker_id, - message: 'Unstarted pipeline not found' + structured_payload( + entity_id: entity_id, + pipeline_tracker_id: pipeline_tracker_id, + message: 'Unstarted pipeline not found' + ) ) end @@ -63,10 +65,11 @@ module BulkImports rescue BulkImports::NetworkError => e if e.retriable?(pipeline_tracker) logger.error( - worker: self.class.name, - entity_id: pipeline_tracker.entity.id, - pipeline_name: pipeline_tracker.pipeline_name, - message: "Retrying error: #{e.message}" + structured_payload( + entity_id: pipeline_tracker.entity.id, + pipeline_name: pipeline_tracker.pipeline_name, + message: "Retrying error: #{e.message}" + ) ) pipeline_tracker.update!(status_event: 'retry', jid: jid) @@ -83,10 +86,11 @@ module BulkImports pipeline_tracker.update!(status_event: 'fail_op', jid: jid) logger.error( - worker: self.class.name, - entity_id: pipeline_tracker.entity.id, - pipeline_name: pipeline_tracker.pipeline_name, - message: exception.message + structured_payload( + entity_id: pipeline_tracker.entity.id, + pipeline_name: pipeline_tracker.pipeline_name, + message: exception.message + ) ) Gitlab::ErrorTracking.track_exception( diff --git a/config/application.rb b/config/application.rb index aa1a0b26d60..703b829edbb 100644 --- a/config/application.rb +++ b/config/application.rb @@ -272,6 +272,7 @@ module Gitlab config.assets.precompile << "page_bundles/import.css" config.assets.precompile << "page_bundles/incident_management_list.css" config.assets.precompile << "page_bundles/issues_list.css" + config.assets.precompile << "page_bundles/issues_show.css" config.assets.precompile << "page_bundles/jira_connect.css" config.assets.precompile << "page_bundles/jira_connect_users.css" config.assets.precompile << "page_bundles/learn_gitlab.css" diff --git a/config/initializers/graphql.rb b/config/initializers/graphql.rb deleted file mode 100644 index 52c26e756a5..00000000000 --- a/config/initializers/graphql.rb +++ /dev/null @@ -1,5 +0,0 @@ -# frozen_string_literal: true - -GraphQL::ObjectType.accepts_definitions(authorize: GraphQL::Define.assign_metadata_key(:authorize)) - -GraphQL::Schema::Object.accepts_definition(:authorize) diff --git a/config/routes.rb b/config/routes.rb index 972773467e6..41f06a412a3 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -66,7 +66,7 @@ Rails.application.routes.draw do end Gitlab.ee do - resources :company, only: [:new] + resource :company, only: [:new, :create], controller: 'company' resources :groups, only: [:new, :create] resources :projects, only: [:new, :create] resources :groups_projects, only: [:new, :create] do diff --git a/db/docs/ci_namespace_mirrors.yml b/db/docs/ci_namespace_mirrors.yml index f3f831602b8..31349cfa94c 100644 --- a/db/docs/ci_namespace_mirrors.yml +++ b/db/docs/ci_namespace_mirrors.yml @@ -4,6 +4,6 @@ classes: - Ci::NamespaceMirror feature_categories: - sharding -description: TODO +description: Mirrors some data from the `main` database into the `ci` database so that we can join directly in a single query introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/75621 milestone: '14.6' diff --git a/db/docs/ci_project_mirrors.yml b/db/docs/ci_project_mirrors.yml index 93233edc3b3..f04fbf80596 100644 --- a/db/docs/ci_project_mirrors.yml +++ b/db/docs/ci_project_mirrors.yml @@ -4,6 +4,6 @@ classes: - Ci::ProjectMirror feature_categories: - sharding -description: TODO +description: Mirrors some data from the `main` database into the `ci` database so that we can join directly in a single query introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/75621 milestone: '14.6' diff --git a/db/docs/loose_foreign_keys_deleted_records.yml b/db/docs/loose_foreign_keys_deleted_records.yml index d099acd5f94..df26ffaefd1 100644 --- a/db/docs/loose_foreign_keys_deleted_records.yml +++ b/db/docs/loose_foreign_keys_deleted_records.yml @@ -4,6 +4,6 @@ classes: - LooseForeignKeys::DeletedRecord feature_categories: - sharding -description: TODO +description: Used by the loose foreign keys feature as a queue of parent records whose child records (via foreign keys) need to be deleted/nullified introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/70152 milestone: '14.3' diff --git a/db/docs/namespaces_sync_events.yml b/db/docs/namespaces_sync_events.yml index efc5c69c346..f674bfcf622 100644 --- a/db/docs/namespaces_sync_events.yml +++ b/db/docs/namespaces_sync_events.yml @@ -4,6 +4,6 @@ classes: - Namespaces::SyncEvent feature_categories: - sharding -description: TODO +description: Used as a queue of data that needs to be synchronized between the `ci` and `main` database introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/75517 milestone: '14.6' diff --git a/db/docs/projects_sync_events.yml b/db/docs/projects_sync_events.yml index 4b0d13839b7..cdc27423778 100644 --- a/db/docs/projects_sync_events.yml +++ b/db/docs/projects_sync_events.yml @@ -4,6 +4,6 @@ classes: - Projects::SyncEvent feature_categories: - sharding -description: TODO +description: Used as a queue of data that needs to be synchronized between the `ci` and `main` database introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/75517 milestone: '14.6' diff --git a/db/post_migrate/20220412044906_finalize_traversal_ids_background_migrations.rb b/db/post_migrate/20220412044906_finalize_traversal_ids_background_migrations.rb new file mode 100644 index 00000000000..f2aa9c0b717 --- /dev/null +++ b/db/post_migrate/20220412044906_finalize_traversal_ids_background_migrations.rb @@ -0,0 +1,12 @@ +# frozen_string_literal: true + +class FinalizeTraversalIdsBackgroundMigrations < Gitlab::Database::Migration[1.0] + def up + finalize_background_migration('BackfillNamespaceTraversalIdsRoots') + finalize_background_migration('BackfillNamespaceTraversalIdsChildren') + end + + def down + # no-op + end +end diff --git a/db/schema_migrations/20220412044906 b/db/schema_migrations/20220412044906 new file mode 100644 index 00000000000..a86fcfead61 --- /dev/null +++ b/db/schema_migrations/20220412044906 @@ -0,0 +1 @@ +1d6ed98ad2da7be75e09d853db86905ed1fb1847d387cc6d1980ff5516db06d9 \ No newline at end of file diff --git a/doc/administration/gitaly/recovery.md b/doc/administration/gitaly/recovery.md index a7166f7e62e..4e3031ddf15 100644 --- a/doc/administration/gitaly/recovery.md +++ b/doc/administration/gitaly/recovery.md @@ -348,13 +348,23 @@ If this occurs, run `remove-repository` again. ### Manually list untracked repositories -> [Introduced](https://gitlab.com/gitlab-org/gitaly/-/merge_requests/3926) in GitLab 14.4. +> - [Introduced](https://gitlab.com/gitlab-org/gitaly/-/merge_requests/3926) in GitLab 14.4. +> - `older-than` option added in GitLab 15.0. The `list-untracked-repositories` Praefect sub-command lists repositories of the Gitaly Cluster that both: - Exist for at least one Gitaly storage. - Aren't tracked in the Praefect database. +Add the `-older-than` option to avoid showing repositories that are the process of being created and for which a record doesn't yet exist in the +Praefect database. Replace with a time duration (for example, `5s`, `10m`, or `1h`). Defaults to `6h`. + +```shell +sudo /opt/gitlab/embedded/bin/praefect -config /var/opt/gitlab/praefect/config.toml list-untracked-repositories -older-than +``` + +Only repositories with a creation time before the specified duration are considered. + The command outputs: - Result to `STDOUT` and the command's logs. diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md index d110e1994aa..7ef9897a170 100644 --- a/doc/api/graphql/reference/index.md +++ b/doc/api/graphql/reference/index.md @@ -9773,6 +9773,7 @@ A container repository. | `expirationPolicyStartedAt` | [`Time`](#time) | Timestamp when the cleanup done by the expiration policy was started on the container repository. | | `id` | [`ID!`](#id) | ID of the container repository. | | `location` | [`String!`](#string) | URL of the container repository. | +| `migrationState` | [`String!`](#string) | Migration state of the container repository. | | `name` | [`String!`](#string) | Name of the container repository. | | `path` | [`String!`](#string) | Path of the container repository. | | `project` | [`Project!`](#project) | Project of the container registry. | @@ -9794,6 +9795,7 @@ Details of a container repository. | `expirationPolicyStartedAt` | [`Time`](#time) | Timestamp when the cleanup done by the expiration policy was started on the container repository. | | `id` | [`ID!`](#id) | ID of the container repository. | | `location` | [`String!`](#string) | URL of the container repository. | +| `migrationState` | [`String!`](#string) | Migration state of the container repository. | | `name` | [`String!`](#string) | Name of the container repository. | | `path` | [`String!`](#string) | Path of the container repository. | | `project` | [`Project!`](#project) | Project of the container registry. | diff --git a/doc/development/rake_tasks.md b/doc/development/rake_tasks.md index 1e9367ecee4..0538add59b5 100644 --- a/doc/development/rake_tasks.md +++ b/doc/development/rake_tasks.md @@ -366,8 +366,8 @@ The docs generator code comes from our side giving us more flexibility, like usi To edit the content, you may need to edit the following: -- The template. You can edit the template at `lib/gitlab/graphql/docs/templates/default.md.haml`. - The actual renderer is at `Gitlab::Graphql::Docs::Renderer`. +- The template. You can edit the template at `tooling/graphql/docs/templates/default.md.haml`. + The actual renderer is at `Tooling::Graphql::Docs::Renderer`. - The applicable `description` field in the code, which [Updates machine-readable schema files](#update-machine-readable-schema-files), which is then used by the `rake` task described earlier. diff --git a/doc/user/application_security/api_fuzzing/index.md b/doc/user/application_security/api_fuzzing/index.md index ea3701b78c2..ed94686b7a3 100644 --- a/doc/user/application_security/api_fuzzing/index.md +++ b/doc/user/application_security/api_fuzzing/index.md @@ -592,6 +592,7 @@ profile increases as the number of tests increases. | `FUZZAPI_CONFIG` | [Deprecated](https://gitlab.com/gitlab-org/gitlab/-/issues/276395) in GitLab 13.12, replaced with default `.gitlab/gitlab-api-fuzzing-config.yml`. API Fuzzing configuration file. | |[`FUZZAPI_PROFILE`](#api-fuzzing-profiles) | Configuration profile to use during testing. Defaults to `Quick-10`. | |[`FUZZAPI_EXCLUDE_PATHS`](#exclude-paths) | Exclude API URL paths from testing. | +|[`FUZZAPI_EXCLUDE_URLS`](#exclude-urls) | Exclude API URL from testing. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/357195) in GitLab 14.10. | |[`FUZZAPI_EXCLUDE_PARAMETER_ENV`](#exclude-parameters) | JSON string containing excluded parameters. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/292196) in GitLab 14.10. | |[`FUZZAPI_EXCLUDE_PARAMETER_FILE`](#exclude-parameters) | Path to a JSON file containing excluded parameters. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/292196) in GitLab 14.10. | |[`FUZZAPI_OPENAPI`](#openapi-specification) | OpenAPI Specification file or URL. | @@ -1295,6 +1296,65 @@ variables: The `api-fuzzing-exclude-parameters.json` is a JSON document that follows the structure of [exclude parameters document](#exclude-parameters-using-a-json-document). +### Exclude URLS + +> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/357195) in GitLab 14.10. + +As an alternative to excluding by paths, you can filter by any other component in the URL by using the `FUZZAPI_EXCLUDE_URLS` CI/CD variable. This variable can be set in your `.gitlab-ci.yml` file. The variable can store multiple values, separated by commas (`,`). Each value is a regular expression. Because each entry is a regular expression, an entry such as `.*` excludes all URLs because it is a regular expression that matches everything. + +In your job output you can check if any URLs matched any provided regular expression from `FUZZAPI_EXCLUDE_URLS`. Matching operations are listed in the **Excluded Operations** section. Operations listed in the **Excluded Operations** should not be listed in the **Tested Operations** section. For example the following portion of a job output: + +```plaintext +2021-05-27 21:51:08 [INF] API Security: --[ Tested Operations ]------------------------- +2021-05-27 21:51:08 [INF] API Security: 201 POST http://target:7777/api/users CREATED +2021-05-27 21:51:08 [INF] API Security: ------------------------------------------------ +2021-05-27 21:51:08 [INF] API Security: --[ Excluded Operations ]----------------------- +2021-05-27 21:51:08 [INF] API Security: GET http://target:7777/api/messages +2021-05-27 21:51:08 [INF] API Security: POST http://target:7777/api/messages +2021-05-27 21:51:08 [INF] API Security: ------------------------------------------------ +``` + +NOTE: +Each value in `FUZZAPI_EXCLUDE_URLS` is a regular expression. Characters such as `.` , `*` and `$` among many others have special meanings in [regular expressions](https://en.wikipedia.org/wiki/Regular_expression#Standards). + +#### Examples + +##### Excluding a URL and child resources + +The following example excludes the URL `http://target/api/auth` and its child resources. + +```yaml +variables: + FUZZAPI_EXCLUDE_URLS: http://target/api/auth +``` + +##### Excluding two URLs and allow their child resources + +To exclude the URLs `http://target/api/buy` and `http://target/api/sell` but allowing to scan their child resources, for instance: `http://target/api/buy/toy` or `http://target/api/sell/chair`. You could use the value `http://target/api/buy/$,http://target/api/sell/$`. This value is using two regular expressions, each of them separated by a `,` character. Hence, it contains `http://target/api/buy$` and `http://target/api/sell$`. In each regular expression, the trailing `$` character points out where the matching URL should end. + +```yaml +variables: + FUZZAPI_EXCLUDE_URLS: http://target/api/buy/$,http://target/api/sell/$ +``` + +##### Excluding two URLs and their child resources + +In order to exclude the URLs: `http://target/api/buy` and `http://target/api/sell`, and their child resources. To provide multiple URLs we use the `,` character as follows: + +```yaml +variables: + FUZZAPI_EXCLUDE_URLS: http://target/api/buy,http://target/api/sell +``` + +##### Excluding URL using regular expressions + +In order to exclude exactly `https://target/api/v1/user/create` and `https://target/api/v2/user/create` or any other version (`v3`,`v4`, and more). We could use `https://target/api/v.*/user/create$`, in the previous regular expression `.` indicates any character and `*` indicates zero or more times, additionally `$` indicates that the URL should end there. + +```yaml +variables: + FUZZAPI_EXCLUDE_URLS: https://target/api/v.*/user/create$ +``` + ### Header Fuzzing Header fuzzing is disabled by default due to the high number of false positives that occur with many diff --git a/doc/user/application_security/dast_api/index.md b/doc/user/application_security/dast_api/index.md index c60f6f92c74..a4908204b60 100644 --- a/doc/user/application_security/dast_api/index.md +++ b/doc/user/application_security/dast_api/index.md @@ -544,6 +544,7 @@ can be added, removed, and modified by creating a custom configuration. |[`DAST_API_CONFIG`](#configuration-files) | DAST API configuration file. Defaults to `.gitlab-dast-api.yml`. | |[`DAST_API_PROFILE`](#configuration-files) | Configuration profile to use during testing. Defaults to `Quick`. | |[`DAST_API_EXCLUDE_PATHS`](#exclude-paths) | Exclude API URL paths from testing. | +|[`DAST_API_EXCLUDE_URLS`](#exclude-urls) | Exclude API URL from testing. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/357195) in GitLab 14.10. | |[`DAST_API_EXCLUDE_PARAMETER_ENV`](#exclude-parameters) | JSON string containing excluded parameters. | |[`DAST_API_EXCLUDE_PARAMETER_FILE`](#exclude-parameters) | Path to a JSON file containing excluded parameters. | |[`DAST_API_OPENAPI`](#openapi-specification) | OpenAPI specification file or URL. | @@ -1249,6 +1250,65 @@ variables: The `dast-api-exclude-parameters.json` is a JSON document that follows the structure of [exclude parameters document](#exclude-parameters-using-a-json-document). +### Exclude URLS + +> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/357195) in GitLab 14.10. + +As an alternative to excluding by paths, you can filter by any other component in the URL by using the `DAST_API_EXCLUDE_URLS` CI/CD variable. This variable can be set in your `.gitlab-ci.yml` file. The variable can store multiple values, separated by commas (`,`). Each value is a regular expression. Because each entry is a regular expression, an entry like `.*` will exclude all URLs because it is a regular expression that matches everything. + +In your job output you can check if any URLs matched any provided regular expression from `DAST_API_EXCLUDE_URLS`. Matching operations are listed in the **Excluded Operations** section. Operations listed in the **Excluded Operations** should not be listed in the **Tested Operations** section. For example the following portion of a job output: + +```plaintext +2021-05-27 21:51:08 [INF] API Security: --[ Tested Operations ]------------------------- +2021-05-27 21:51:08 [INF] API Security: 201 POST http://target:7777/api/users CREATED +2021-05-27 21:51:08 [INF] API Security: ------------------------------------------------ +2021-05-27 21:51:08 [INF] API Security: --[ Excluded Operations ]----------------------- +2021-05-27 21:51:08 [INF] API Security: GET http://target:7777/api/messages +2021-05-27 21:51:08 [INF] API Security: POST http://target:7777/api/messages +2021-05-27 21:51:08 [INF] API Security: ------------------------------------------------ +``` + +NOTE: +Each value in `DAST_API_EXCLUDE_URLS` is a regular expression. Characters such as `.` , `*` and `$` among many others have special meanings in [regular expressions](https://en.wikipedia.org/wiki/Regular_expression#Standards). + +#### Examples + +##### Excluding a URL and child resources + +The following example excludes the URL `http://target/api/auth` and its child resources. + +```yaml +variables: + DAST_API_EXCLUDE_URLS: http://target/api/auth +``` + +##### Excluding two URLs and allow their child resources + +To exclude the URLs `http://target/api/buy` and `http://target/api/sell` but allowing to scan their child resources, for instance: `http://target/api/buy/toy` or `http://target/api/sell/chair`. You could use the value `http://target/api/buy/$,http://target/api/sell/$`. This value is using two regular expressions, each of them separated by a `,` character. Hence, it contains `http://target/api/buy$` and `http://target/api/sell$`. In each regular expression, the trailing `$` character points out where the matching URL should end. + +```yaml +variables: + DAST_API_EXCLUDE_URLS: http://target/api/buy/$,http://target/api/sell/$ +``` + +##### Excluding two URLs and their child resources + +In order to exclude the URLs: `http://target/api/buy` and `http://target/api/sell`, and their child resources. To provide multiple URLs we use the `,` character as follows: + +```yaml +variables: + DAST_API_EXCLUDE_URLS: http://target/api/buy,http://target/api/sell +``` + +##### Excluding URL using regular expressions + +In order to exclude exactly `https://target/api/v1/user/create` and `https://target/api/v2/user/create` or any other version (`v3`,`v4`, and more). We could use `https://target/api/v.*/user/create$`, in the previous regular expression `.` indicates any character and `*` indicates zero or more times, additionally `$` indicates that the URL should end there. + +```yaml +variables: + DAST_API_EXCLUDE_URLS: https://target/api/v.*/user/create$ +``` + ## Running your first scan When configured correctly, a CI/CD pipeline contains a `dast` stage and an `dast_api` job. The job only fails when an invalid configuration is provided. During normal operation, the job always succeeds even if vulnerabilities are identified during testing. diff --git a/locale/gitlab.pot b/locale/gitlab.pot index f6e44bc8de3..b7c3378db63 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -9773,6 +9773,9 @@ msgstr "" msgid "ContainerRegistry|Image repository not found" msgstr "" +msgid "ContainerRegistry|Image repository temporarily cannot be marked for deletion. Please try again in a few minutes. %{docLinkStart}More details%{docLinkEnd}" +msgstr "" + msgid "ContainerRegistry|Image repository will be deleted" msgstr "" diff --git a/spec/frontend/packages_and_registries/container_registry/explorer/components/delete_button_spec.js b/spec/frontend/packages_and_registries/container_registry/explorer/components/delete_button_spec.js index 6d7bf528495..ad67128502a 100644 --- a/spec/frontend/packages_and_registries/container_registry/explorer/components/delete_button_spec.js +++ b/spec/frontend/packages_and_registries/container_registry/explorer/components/delete_button_spec.js @@ -1,7 +1,7 @@ -import { GlButton } from '@gitlab/ui'; +import { GlButton, GlTooltip, GlSprintf } from '@gitlab/ui'; import { shallowMount } from '@vue/test-utils'; -import { createMockDirective, getBinding } from 'helpers/vue_mock_directive'; import component from '~/packages_and_registries/container_registry/explorer/components/delete_button.vue'; +import { LIST_DELETE_BUTTON_DISABLED_FOR_MIGRATION } from '~/packages_and_registries/container_registry/explorer/constants/list'; describe('delete_button', () => { let wrapper; @@ -12,6 +12,7 @@ describe('delete_button', () => { }; const findButton = () => wrapper.find(GlButton); + const findTooltip = () => wrapper.find(GlTooltip); const mountComponent = (props) => { wrapper = shallowMount(component, { @@ -19,8 +20,9 @@ describe('delete_button', () => { ...defaultProps, ...props, }, - directives: { - GlTooltip: createMockDirective(), + stubs: { + GlTooltip, + GlSprintf, }, }); }; @@ -33,41 +35,50 @@ describe('delete_button', () => { describe('tooltip', () => { it('the title is controlled by tooltipTitle prop', () => { mountComponent(); - const tooltip = getBinding(wrapper.element, 'gl-tooltip'); + const tooltip = findTooltip(); expect(tooltip).toBeDefined(); - expect(tooltip.value.title).toBe(defaultProps.tooltipTitle); + expect(tooltip.text()).toBe(defaultProps.tooltipTitle); }); it('is disabled when tooltipTitle is disabled', () => { mountComponent({ tooltipDisabled: true }); - const tooltip = getBinding(wrapper.element, 'gl-tooltip'); - expect(tooltip.value.disabled).toBe(true); + expect(findTooltip().props('disabled')).toBe(true); }); - describe('button', () => { - it('exists', () => { - mountComponent(); - expect(findButton().exists()).toBe(true); + it('works with a link', () => { + mountComponent({ + tooltipTitle: LIST_DELETE_BUTTON_DISABLED_FOR_MIGRATION, + tooltipLink: 'foo', }); + expect(findTooltip().text()).toMatchInterpolatedText( + LIST_DELETE_BUTTON_DISABLED_FOR_MIGRATION, + ); + }); + }); - it('has the correct props/attributes bound', () => { - mountComponent({ disabled: true }); - expect(findButton().attributes()).toMatchObject({ - 'aria-label': 'Foo title', - icon: 'remove', - title: 'Foo title', - variant: 'danger', - disabled: 'true', - category: 'secondary', - }); - }); + describe('button', () => { + it('exists', () => { + mountComponent(); + expect(findButton().exists()).toBe(true); + }); - it('emits a delete event', () => { - mountComponent(); - expect(wrapper.emitted('delete')).toEqual(undefined); - findButton().vm.$emit('click'); - expect(wrapper.emitted('delete')).toEqual([[]]); + it('has the correct props/attributes bound', () => { + mountComponent({ disabled: true }); + expect(findButton().attributes()).toMatchObject({ + 'aria-label': 'Foo title', + icon: 'remove', + title: 'Foo title', + variant: 'danger', + disabled: 'true', + category: 'secondary', }); }); + + it('emits a delete event', () => { + mountComponent(); + expect(wrapper.emitted('delete')).toEqual(undefined); + findButton().vm.$emit('click'); + expect(wrapper.emitted('delete')).toEqual([[]]); + }); }); }); diff --git a/spec/frontend/packages_and_registries/container_registry/explorer/components/list_page/image_list_row_spec.js b/spec/frontend/packages_and_registries/container_registry/explorer/components/list_page/image_list_row_spec.js index 411bef54e40..690d827ec67 100644 --- a/spec/frontend/packages_and_registries/container_registry/explorer/components/list_page/image_list_row_spec.js +++ b/spec/frontend/packages_and_registries/container_registry/explorer/components/list_page/image_list_row_spec.js @@ -10,6 +10,7 @@ import { LIST_DELETE_BUTTON_DISABLED, REMOVE_REPOSITORY_LABEL, IMAGE_DELETE_SCHEDULED_STATUS, + IMAGE_MIGRATING_STATE, SCHEDULED_STATUS, ROOT_IMAGE_TEXT, } from '~/packages_and_registries/container_registry/explorer/constants'; @@ -41,6 +42,9 @@ describe('Image List Row', () => { item, ...props, }, + provide: { + config: {}, + }, directives: { GlTooltip: createMockDirective(), }, @@ -178,6 +182,12 @@ describe('Image List Row', () => { expect(findDeleteBtn().props('disabled')).toBe(state); }, ); + + it('is disabled when migrationState is importing', () => { + mountComponent({ item: { ...item, migrationState: IMAGE_MIGRATING_STATE } }); + + expect(findDeleteBtn().props('disabled')).toBe(true); + }); }); describe('tags count', () => { diff --git a/spec/frontend/packages_and_registries/container_registry/explorer/mock_data.js b/spec/frontend/packages_and_registries/container_registry/explorer/mock_data.js index fda1db4b7e1..7e6f88fe5bc 100644 --- a/spec/frontend/packages_and_registries/container_registry/explorer/mock_data.js +++ b/spec/frontend/packages_and_registries/container_registry/explorer/mock_data.js @@ -5,6 +5,7 @@ export const imagesListResponse = [ name: 'rails-12009', path: 'gitlab-org/gitlab-test/rails-12009', status: null, + migrationState: 'default', location: '0.0.0.0:5000/gitlab-org/gitlab-test/rails-12009', canDelete: true, createdAt: '2020-11-03T13:29:21Z', @@ -17,6 +18,7 @@ export const imagesListResponse = [ name: 'rails-20572', path: 'gitlab-org/gitlab-test/rails-20572', status: null, + migrationState: 'default', location: '0.0.0.0:5000/gitlab-org/gitlab-test/rails-20572', canDelete: true, createdAt: '2020-09-21T06:57:43Z', diff --git a/spec/graphql/types/base_object_spec.rb b/spec/graphql/types/base_object_spec.rb index d8f2ef58ea5..45dc885ecba 100644 --- a/spec/graphql/types/base_object_spec.rb +++ b/spec/graphql/types/base_object_spec.rb @@ -428,5 +428,25 @@ RSpec.describe Types::BaseObject do expect(result.dig('data', 'users', 'nodes')) .to contain_exactly({ 'name' => active_users.first.name }) end + + describe '.authorize' do + let_it_be(:read_only_type) do + Class.new(described_class) do + authorize :read_only + end + end + + let_it_be(:inherited_read_only_type) { Class.new(read_only_type) } + + it 'keeps track of the specified value' do + expect(described_class.authorize).to be_nil + expect(read_only_type.authorize).to match_array [:read_only] + expect(inherited_read_only_type.authorize).to match_array [:read_only] + end + + it 'can not redefine the authorize value' do + expect { read_only_type.authorize(:write_only) }.to raise_error('Cannot redefine authorize') + end + end end end diff --git a/spec/graphql/types/container_repository_details_type_spec.rb b/spec/graphql/types/container_repository_details_type_spec.rb index aa770284f89..d94516c6fce 100644 --- a/spec/graphql/types/container_repository_details_type_spec.rb +++ b/spec/graphql/types/container_repository_details_type_spec.rb @@ -3,7 +3,9 @@ require 'spec_helper' RSpec.describe GitlabSchema.types['ContainerRepositoryDetails'] do - fields = %i[id name path location created_at updated_at expiration_policy_started_at status tags_count can_delete expiration_policy_cleanup_status tags size project] + fields = %i[id name path location created_at updated_at expiration_policy_started_at + status tags_count can_delete expiration_policy_cleanup_status tags size + project migration_state] it { expect(described_class.graphql_name).to eq('ContainerRepositoryDetails') } diff --git a/spec/graphql/types/container_repository_type_spec.rb b/spec/graphql/types/container_repository_type_spec.rb index 87e1c11ce19..9815449dd68 100644 --- a/spec/graphql/types/container_repository_type_spec.rb +++ b/spec/graphql/types/container_repository_type_spec.rb @@ -3,7 +3,9 @@ require 'spec_helper' RSpec.describe GitlabSchema.types['ContainerRepository'] do - fields = %i[id name path location created_at updated_at expiration_policy_started_at status tags_count can_delete expiration_policy_cleanup_status project] + fields = %i[id name path location created_at updated_at expiration_policy_started_at + status tags_count can_delete expiration_policy_cleanup_status project + migration_state] it { expect(described_class.graphql_name).to eq('ContainerRepository') } diff --git a/spec/helpers/namespaces_helper_spec.rb b/spec/helpers/namespaces_helper_spec.rb index 00aa0fd1cba..52c1130e818 100644 --- a/spec/helpers/namespaces_helper_spec.rb +++ b/spec/helpers/namespaces_helper_spec.rb @@ -268,4 +268,15 @@ RSpec.describe NamespacesHelper do end end end + + describe '#pipeline_usage_quota_app_data' do + it 'returns a hash with necessary data for the frontend' do + expect(helper.pipeline_usage_quota_app_data(user_group)).to eql({ + namespace_actual_plan_name: user_group.actual_plan_name, + namespace_path: user_group.full_path, + namespace_id: user_group.id, + page_size: Kaminari.config.default_per_page + }) + end + end end diff --git a/spec/migrations/finalize_traversal_ids_background_migrations_spec.rb b/spec/migrations/finalize_traversal_ids_background_migrations_spec.rb new file mode 100644 index 00000000000..74d6447e6a7 --- /dev/null +++ b/spec/migrations/finalize_traversal_ids_background_migrations_spec.rb @@ -0,0 +1,60 @@ +# frozen_string_literal: true + +require 'spec_helper' +require_migration!('finalize_traversal_ids_background_migrations') + +RSpec.describe FinalizeTraversalIdsBackgroundMigrations, :migration do + shared_context 'incomplete background migration' do + before do + # Jobs enqueued in Sidekiq. + Sidekiq::Testing.disable! do + BackgroundMigrationWorker.perform_in(10, job_class_name, [1, 2, 100]) + BackgroundMigrationWorker.perform_in(20, job_class_name, [3, 4, 100]) + end + + # Jobs tracked in the database. + # table(:background_migration_jobs).create!( + Gitlab::Database::BackgroundMigrationJob.create!( + class_name: job_class_name, + arguments: [5, 6, 100], + status: Gitlab::Database::BackgroundMigrationJob.statuses['pending'] + ) + # table(:background_migration_jobs).create!( + Gitlab::Database::BackgroundMigrationJob.create!( + class_name: job_class_name, + arguments: [7, 8, 100], + status: Gitlab::Database::BackgroundMigrationJob.statuses['succeeded'] + ) + end + end + + context 'BackfillNamespaceTraversalIdsRoots background migration' do + let(:job_class_name) { 'BackfillNamespaceTraversalIdsRoots' } + + include_context 'incomplete background migration' + + before do + migrate! + end + + it_behaves_like( + 'finalized tracked background migration', + Gitlab::BackgroundMigration::BackfillNamespaceTraversalIdsRoots + ) + end + + context 'BackfillNamespaceTraversalIdsChildren background migration' do + let(:job_class_name) { 'BackfillNamespaceTraversalIdsChildren' } + + include_context 'incomplete background migration' + + before do + migrate! + end + + it_behaves_like( + 'finalized tracked background migration', + Gitlab::BackgroundMigration::BackfillNamespaceTraversalIdsChildren + ) + end +end diff --git a/spec/workers/bulk_imports/entity_worker_spec.rb b/spec/workers/bulk_imports/entity_worker_spec.rb index ce45299c7f7..ab85b587975 100644 --- a/spec/workers/bulk_imports/entity_worker_spec.rb +++ b/spec/workers/bulk_imports/entity_worker_spec.rb @@ -36,9 +36,11 @@ RSpec.describe BulkImports::EntityWorker do expect(logger) .to receive(:info).twice .with( - worker: described_class.name, - entity_id: entity.id, - current_stage: nil + hash_including( + 'entity_id' => entity.id, + 'current_stage' => nil, + 'message' => 'Stage starting' + ) ) end @@ -58,24 +60,26 @@ RSpec.describe BulkImports::EntityWorker do expect(BulkImports::PipelineWorker) .to receive(:perform_async) - .and_raise(exception) + .and_raise(exception) expect_next_instance_of(Gitlab::Import::Logger) do |logger| expect(logger) .to receive(:info).twice .with( - worker: described_class.name, - entity_id: entity.id, - current_stage: nil + hash_including( + 'entity_id' => entity.id, + 'current_stage' => nil + ) ) expect(logger) .to receive(:error) .with( - worker: described_class.name, - entity_id: entity.id, - current_stage: nil, - error_message: 'Error!' + hash_including( + 'entity_id' => entity.id, + 'current_stage' => nil, + 'message' => 'Error!' + ) ) end @@ -90,6 +94,18 @@ RSpec.describe BulkImports::EntityWorker do let(:job_args) { [entity.id, 0] } it 'do not enqueue a new pipeline job if the current stage still running' do + expect_next_instance_of(Gitlab::Import::Logger) do |logger| + expect(logger) + .to receive(:info).twice + .with( + hash_including( + 'entity_id' => entity.id, + 'current_stage' => 0, + 'message' => 'Stage running' + ) + ) + end + expect(BulkImports::PipelineWorker) .not_to receive(:perform_async) @@ -110,9 +126,10 @@ RSpec.describe BulkImports::EntityWorker do expect(logger) .to receive(:info).twice .with( - worker: described_class.name, - entity_id: entity.id, - current_stage: 0 + hash_including( + 'entity_id' => entity.id, + 'current_stage' => 0 + ) ) end diff --git a/spec/workers/bulk_imports/export_request_worker_spec.rb b/spec/workers/bulk_imports/export_request_worker_spec.rb index 4f452e3dd60..846df63a4d7 100644 --- a/spec/workers/bulk_imports/export_request_worker_spec.rb +++ b/spec/workers/bulk_imports/export_request_worker_spec.rb @@ -35,14 +35,16 @@ RSpec.describe BulkImports::ExportRequestWorker do expect(client).to receive(:post).and_raise(BulkImports::NetworkError, 'Export error').twice end - expect(Gitlab::Import::Logger).to receive(:warn).with( - bulk_import_entity_id: entity.id, - pipeline_class: 'ExportRequestWorker', - exception_class: 'BulkImports::NetworkError', - exception_message: 'Export error', - correlation_id_value: anything, - bulk_import_id: bulk_import.id, - bulk_import_entity_type: entity.source_type + expect(Gitlab::Import::Logger).to receive(:error).with( + hash_including( + 'bulk_import_entity_id' => entity.id, + 'pipeline_class' => 'ExportRequestWorker', + 'exception_class' => 'BulkImports::NetworkError', + 'exception_message' => 'Export error', + 'correlation_id_value' => anything, + 'bulk_import_id' => bulk_import.id, + 'bulk_import_entity_type' => entity.source_type + ) ).twice perform_multiple(job_args) diff --git a/spec/workers/bulk_imports/pipeline_worker_spec.rb b/spec/workers/bulk_imports/pipeline_worker_spec.rb index cb7e70a6749..3578fec5bc0 100644 --- a/spec/workers/bulk_imports/pipeline_worker_spec.rb +++ b/spec/workers/bulk_imports/pipeline_worker_spec.rb @@ -34,9 +34,10 @@ RSpec.describe BulkImports::PipelineWorker do expect(logger) .to receive(:info) .with( - worker: described_class.name, - pipeline_name: 'FakePipeline', - entity_id: entity.id + hash_including( + 'pipeline_name' => 'FakePipeline', + 'entity_id' => entity.id + ) ) end @@ -44,7 +45,7 @@ RSpec.describe BulkImports::PipelineWorker do .to receive(:perform_async) .with(entity.id, pipeline_tracker.stage) - expect(subject).to receive(:jid).and_return('jid') + allow(subject).to receive(:jid).and_return('jid') subject.perform(pipeline_tracker.id, pipeline_tracker.stage, entity.id) @@ -79,10 +80,11 @@ RSpec.describe BulkImports::PipelineWorker do expect(logger) .to receive(:error) .with( - worker: described_class.name, - pipeline_tracker_id: pipeline_tracker.id, - entity_id: entity.id, - message: 'Unstarted pipeline not found' + hash_including( + 'pipeline_tracker_id' => pipeline_tracker.id, + 'entity_id' => entity.id, + 'message' => 'Unstarted pipeline not found' + ) ) end @@ -107,10 +109,11 @@ RSpec.describe BulkImports::PipelineWorker do expect(logger) .to receive(:error) .with( - worker: described_class.name, - pipeline_name: 'InexistentPipeline', - entity_id: entity.id, - message: "'InexistentPipeline' is not a valid BulkImport Pipeline" + hash_including( + 'pipeline_name' => 'InexistentPipeline', + 'entity_id' => entity.id, + 'message' => "'InexistentPipeline' is not a valid BulkImport Pipeline" + ) ) end @@ -126,7 +129,7 @@ RSpec.describe BulkImports::PipelineWorker do .to receive(:perform_async) .with(entity.id, pipeline_tracker.stage) - expect(subject).to receive(:jid).and_return('jid') + allow(subject).to receive(:jid).and_return('jid') subject.perform(pipeline_tracker.id, pipeline_tracker.stage, entity.id) @@ -151,10 +154,11 @@ RSpec.describe BulkImports::PipelineWorker do expect(logger) .to receive(:error) .with( - worker: described_class.name, - pipeline_name: 'Pipeline', - entity_id: entity.id, - message: 'Failed entity status' + hash_including( + 'pipeline_name' => 'Pipeline', + 'entity_id' => entity.id, + 'message' => 'Failed entity status' + ) ) end @@ -183,7 +187,7 @@ RSpec.describe BulkImports::PipelineWorker do .and_raise(exception) end - expect(subject).to receive(:jid).and_return('jid').twice + allow(subject).to receive(:jid).and_return('jid') expect_any_instance_of(BulkImports::Tracker) do |tracker| expect(tracker).to receive(:retry).and_call_original @@ -193,9 +197,10 @@ RSpec.describe BulkImports::PipelineWorker do expect(logger) .to receive(:info) .with( - worker: described_class.name, - pipeline_name: 'FakePipeline', - entity_id: entity.id + hash_including( + 'pipeline_name' => 'FakePipeline', + 'entity_id' => entity.id + ) ) end @@ -292,10 +297,11 @@ RSpec.describe BulkImports::PipelineWorker do expect(logger) .to receive(:error) .with( - worker: described_class.name, - pipeline_name: 'NdjsonPipeline', - entity_id: entity.id, - message: 'Pipeline timeout' + hash_including( + 'pipeline_name' => 'NdjsonPipeline', + 'entity_id' => entity.id, + 'message' => 'Pipeline timeout' + ) ) end @@ -318,10 +324,11 @@ RSpec.describe BulkImports::PipelineWorker do expect(logger) .to receive(:error) .with( - worker: described_class.name, - pipeline_name: 'NdjsonPipeline', - entity_id: entity.id, - message: 'Error!' + hash_including( + 'pipeline_name' => 'NdjsonPipeline', + 'entity_id' => entity.id, + 'message' => 'Error!' + ) ) end