diff options
27 files changed, 348 insertions, 219 deletions
diff --git a/.markdownlint.yml b/.markdownlint.yml index ea7492cf208..bb4c859e197 100644 --- a/.markdownlint.yml +++ b/.markdownlint.yml @@ -1,29 +1,29 @@ --- # Base Markdownlint configuration # Extended Markdownlint configuration in doc/.markdownlint/ -"default": true -"first-header-h1": true -"header-style": - "style": "atx" -"ul-style": - "style": "dash" -"no-trailing-spaces": false -"line-length": false -"no-duplicate-header": - "allow_different_nesting": true -"no-trailing-punctuation": - "punctuation": ".,;:!。,;:!?" -"ol-prefix": - "style": "one" -"no-inline-html": false -"hr-style": - "style": "---" -"no-emphasis-as-heading": false -"first-line-h1": false -"code-block-style": - "style": "fenced" -"proper-names": - "names": [ +default: true +first-header-h1: true +header-style: + style: "atx" +ul-style: + style: "dash" +no-trailing-spaces: false +line-length: false +no-duplicate-header: + allow_different_nesting: true +no-trailing-punctuation: + punctuation: ".,;:!。,;:!?" +ol-prefix: + style: "one" +no-inline-html: false +hr-style: + style: "---" +no-emphasis-as-heading: false +first-line-h1: false +code-block-style: + style: "fenced" +proper-names: + names: [ "Akismet", "Alertmanager", "API", @@ -139,4 +139,4 @@ "YAML", "YouTrack" ] - "code_blocks": false + code_blocks: false diff --git a/app/assets/javascripts/pipeline_editor/components/drawer/pipeline_editor_drawer.vue b/app/assets/javascripts/pipeline_editor/components/drawer/pipeline_editor_drawer.vue index d7594fb318a..b493fc860fb 100644 --- a/app/assets/javascripts/pipeline_editor/components/drawer/pipeline_editor_drawer.vue +++ b/app/assets/javascripts/pipeline_editor/components/drawer/pipeline_editor_drawer.vue @@ -90,7 +90,7 @@ export default { <local-storage-sync v-model="isExpanded" :storage-key="$options.localDrawerKey" as-json> <aside aria-live="polite" - class="gl-fixed gl-right-0 gl-bg-gray-10 gl-shadow-drawer gl-transition-property-width gl-transition-duration-medium gl-border-l-solid gl-border-1 gl-border-gray-100 gl-h-full gl-z-index-3 gl-overflow-y-auto" + class="gl-fixed gl-right-0 gl-bg-gray-10 gl-shadow-drawer gl-transition-property-width gl-transition-duration-medium gl-border-l-solid gl-border-1 gl-border-gray-100 gl-h-full gl-z-index-200 gl-overflow-y-auto" :style="rootStyle" > <gl-button diff --git a/app/assets/javascripts/pipelines/components/pipelines_list/pipeline_stage.vue b/app/assets/javascripts/pipelines/components/pipelines_list/pipeline_stage.vue index 7552ddb61dc..afcb04cd7eb 100644 --- a/app/assets/javascripts/pipelines/components/pipelines_list/pipeline_stage.vue +++ b/app/assets/javascripts/pipelines/components/pipelines_list/pipeline_stage.vue @@ -15,7 +15,7 @@ import { GlDropdown, GlLoadingIcon, GlTooltipDirective, GlIcon } from '@gitlab/ui'; import createFlash from '~/flash'; import axios from '~/lib/utils/axios_utils'; -import { __ } from '~/locale'; +import { __, sprintf } from '~/locale'; import eventHub from '../../event_hub'; import JobItem from './job_item.vue'; @@ -98,6 +98,9 @@ export default { // warn the pipelines table to update this.$emit('pipelineActionRequestComplete'); }, + stageAriaLabel(title) { + return sprintf(__('View Stage: %{title}'), { title }); + }, }, }; </script> @@ -106,9 +109,10 @@ export default { <gl-dropdown ref="dropdown" v-gl-tooltip.hover.ds0 + v-gl-tooltip="stage.title" data-testid="mini-pipeline-graph-dropdown" - :title="stage.title" variant="link" + :aria-label="stageAriaLabel(stage.title)" :lazy="true" :popper-opts="{ placement: 'bottom' }" :toggle-class="['mini-pipeline-graph-dropdown-toggle', triggerButtonClass]" diff --git a/app/assets/stylesheets/pages/merge_requests.scss b/app/assets/stylesheets/pages/merge_requests.scss index 3b86750c6ca..a4b8e912614 100644 --- a/app/assets/stylesheets/pages/merge_requests.scss +++ b/app/assets/stylesheets/pages/merge_requests.scss @@ -947,19 +947,6 @@ $tabs-holder-z-index: 250; color: $gray-500; line-height: initial; } - - // GlDropdown mini pipeline (Vue) - // As the `mini-pipeline-item` mixin specificity is lower - // than the toggle of dropdown with 'variant="link"' we add - // classes ".gl-button.btn-link" to make it more specific - // and avoid having the size overriden - // - // See https://gitlab.com/gitlab-org/gitlab/-/issues/320737 - button.gl-button.btn-link.mini-pipeline-graph-dropdown-toggle, - .stage-cell button.gl-button.btn-link.mini-pipeline-graph-dropdown-toggle svg { - height: $ci-action-icon-size-lg; - width: $ci-action-icon-size-lg; - } } .merge-request-details .file-finder-overlay.diff-file-finder { diff --git a/app/assets/stylesheets/utilities.scss b/app/assets/stylesheets/utilities.scss index 7e46f16e1d0..65961edb833 100644 --- a/app/assets/stylesheets/utilities.scss +++ b/app/assets/stylesheets/utilities.scss @@ -297,3 +297,8 @@ $gl-line-height-42: px-to-rem(42px); max-width: 50%; } } + +// Will be moved to @gitlab/ui by https://gitlab.com/gitlab-org/gitlab-ui/-/merge_requests/2497 +.gl-z-index-200 { + z-index: 200; +} diff --git a/app/models/clusters/applications/runner.rb b/app/models/clusters/applications/runner.rb index 40054401873..b57a24dead0 100644 --- a/app/models/clusters/applications/runner.rb +++ b/app/models/clusters/applications/runner.rb @@ -50,34 +50,6 @@ module Clusters private - def ensure_runner - runner || create_and_assign_runner - end - - def create_and_assign_runner - transaction do - Ci::Runner.create!(runner_create_params).tap do |runner| - update!(runner_id: runner.id) - end - end - end - - def runner_create_params - attributes = { - name: 'kubernetes-cluster', - runner_type: cluster.cluster_type, - tag_list: %w[kubernetes cluster] - } - - if cluster.group_type? - attributes[:runner_namespaces] = [::Ci::RunnerNamespace.new(namespace: group)] - elsif cluster.project_type? - attributes[:runner_projects] = [::Ci::RunnerProject.new(project: project)] - end - - attributes - end - def gitlab_url Gitlab::Routing.url_helpers.root_url(only_path: false) end @@ -85,7 +57,6 @@ module Clusters def specification { "gitlabUrl" => gitlab_url, - "runnerToken" => ensure_runner.token, "runners" => { "privileged" => privileged } } end diff --git a/app/views/admin/users/show.html.haml b/app/views/admin/users/show.html.haml index 2a9b4694e7b..bdc5bdabb21 100644 --- a/app/views/admin/users/show.html.haml +++ b/app/views/admin/users/show.html.haml @@ -35,7 +35,7 @@ %span.light= _('Email:') %strong = render partial: 'shared/email_with_badge', locals: { email: mail_to(@user.email), verified: @user.confirmed? } - - @user.emails.each do |email| + - @user.emails.reject(&:user_primary_email?).each do |email| %li %span.light= _('Secondary email:') %strong diff --git a/doc/.markdownlint/markdownlint-no-trailing-spaces.yml b/doc/.markdownlint/markdownlint-no-trailing-spaces.yml index 71903ae423d..8720fbafcb3 100644 --- a/doc/.markdownlint/markdownlint-no-trailing-spaces.yml +++ b/doc/.markdownlint/markdownlint-no-trailing-spaces.yml @@ -1,4 +1,4 @@ --- # Extended Markdown configuration to enforce no-trailing-spaces rule -"extends": "../../.markdownlint.yml" -"no-trailing-spaces": true +extends: "../../.markdownlint.yml" +no-trailing-spaces: true diff --git a/doc/administration/gitaly/index.md b/doc/administration/gitaly/index.md index 8c971da2450..e8ed6ec1738 100644 --- a/doc/administration/gitaly/index.md +++ b/doc/administration/gitaly/index.md @@ -325,14 +325,23 @@ You can [monitor distribution of reads](#monitor-gitaly-cluster) using Prometheu > - From GitLab 13.6, primary-wins voting strategy and the `gitaly_reference_transactions_primary_wins` feature flag was removed. > - From GitLab 14.0, [Gitaly Cluster only supports strong consistency](https://gitlab.com/gitlab-org/gitaly/-/merge_requests/3575), and the `gitaly_reference_transactions` feature flag was removed. -Gitaly Cluster writes changes synchronously to all healthy, up to date replicas. -If a replica is outdated or unhealthy at the time of the transaction, the write is asynchronously replicated to it. +Gitaly Cluster provides strong consistency by writing changes synchronously to all healthy, up-to-date replicas. If a +replica is outdated or unhealthy at the time of the transaction, the write is asynchronously replicated to it. -In GitLab 13.12 and earlier, if Gitaly Cluster wasn't configured to use strong consistency (or didn't support it yet), Gitaly Cluster guaranteed eventual consistency by replicating all writes to secondary Gitaly nodes after the write to the primary Gitaly node has occurred. +If strong consistency is unavailable, Gitaly Cluster guarantees eventual consistency. In this case. Gitaly Cluster +replicates all writes to secondary Gitaly nodes after the write to the primary Gitaly node has occurred. -A subset of operations still use replication jobs (instead of a strong consistency transaction). -For more information, see the -[strong consistency epic](https://gitlab.com/groups/gitlab-org/-/epics/1189). +Strong consistency: + +- Is the primary replication method in GitLab 14.0 and later. A subset of operations still use replication jobs + (eventual consistency) instead of strong consistency. Refer to the + [strong consistency epic](https://gitlab.com/groups/gitlab-org/-/epics/1189) for more information. +- Must be configured in GitLab versions 13.1 to 13.12. For configuration information, refer to either: + - Documentation on your GitLab instance at `/help`. + - The [13.12 documentation](https://docs.gitlab.com/13.12/ee/administration/gitaly/praefect.html#strong-consistency). +- Is unavailable in GitLab 13.0 and earlier. + +For more information on monitoring strong consistency, see the Gitaly Cluster [Prometheus metrics documentation](#monitor-gitaly-cluster). #### Replication factor diff --git a/doc/administration/gitaly/praefect.md b/doc/administration/gitaly/praefect.md index 5f3d564ae31..1a23d175dbf 100644 --- a/doc/administration/gitaly/praefect.md +++ b/doc/administration/gitaly/praefect.md @@ -1105,17 +1105,6 @@ To get started quickly: Congratulations! You've configured an observable fault-tolerant Praefect cluster. -## Strong consistency - -[Strong consistency](index.md#strong-consistency) is the default from GitLab 14.0. -For configuration information on earlier versions, refer to documentation: - -- On your GitLab instance at `/help`. -- The [13.12 documentation](https://docs.gitlab.com/13.12/ee/administration/gitaly/praefect.html#strong-consistency). - -For information on monitoring strong consistency, see the Gitaly Cluster -[Prometheus metrics documentation](index.md#monitor-gitaly-cluster). - ## Configure replication factor WARNING: diff --git a/doc/administration/gitaly/troubleshooting.md b/doc/administration/gitaly/troubleshooting.md index fd36c8d5dd0..d31ed18d62e 100644 --- a/doc/administration/gitaly/troubleshooting.md +++ b/doc/administration/gitaly/troubleshooting.md @@ -437,12 +437,12 @@ Praefect does not require a lot of CPU or memory, and can run on small virtual m Cloud services may place other limits on the resources that small VMs can use, such as disk IO and network traffic. -Praefect nodes generate a lot of network traffic. The following symptoms can be observed -if their network bandwidth has been throttled by the cloud service. +Praefect nodes generate a lot of network traffic. The following symptoms can be observed if their network bandwidth has +been throttled by the cloud service: -- Poor performance of Git operations -- High network latency -- High memory use by Praefect +- Poor performance of Git operations. +- High network latency. +- High memory use by Praefect. Possible solutions: diff --git a/doc/ci/yaml/artifacts_reports.md b/doc/ci/yaml/artifacts_reports.md index 17901995f81..f93025a08fe 100644 --- a/doc/ci/yaml/artifacts_reports.md +++ b/doc/ci/yaml/artifacts_reports.md @@ -167,7 +167,7 @@ GitLab can display the results of one or more reports in: The `dotenv` report collects a set of environment variables as artifacts. The collected variables are registered as runtime-created variables of the job, -which is useful to [set dynamic environment URLs after a job finishes](../environments/index.md#set-dynamic-environment-urls-after-a-job-finishes). +which you can use to [set dynamic environment URLs after a job finishes](../environments/index.md#set-dynamic-environment-urls-after-a-job-finishes). The exceptions to the [original dotenv rules](https://github.com/motdotla/dotenv#rules) are: diff --git a/doc/development/testing_guide/end_to_end/resources.md b/doc/development/testing_guide/end_to_end/resources.md index 0819a2f7b54..abba1d51f07 100644 --- a/doc/development/testing_guide/end_to_end/resources.md +++ b/doc/development/testing_guide/end_to_end/resources.md @@ -4,14 +4,21 @@ group: unassigned info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments --- -# Resource class in GitLab QA +# Resource classes in GitLab QA -Resources are primarily created using Browser UI steps, but can also -be created via the API or the CLI. +Resources are primarily created using Browser UI steps, but can also be created via the API or the CLI. + +A typical resource class is used to create a new resource that can be used in a single test. However, several tests can +end up creating the same kind of resource and use it in ways that mean it could have been +used by more than one test. Creating a new resource each time is not efficient. Therefore, we can also create reusable +resources that are created once and can then be used by many tests. + +In the following section the content focuses on single-use resources, however it also applies to reusable resources. +Information specific to [reusable resources is detailed below](#reusable-resources). ## How to properly implement a resource class? -All resource classes should inherit from `Resource::Base`. +All non-reusable resource classes should inherit from `Resource::Base`. There is only one mandatory method to implement to define a resource class. This is the `#fabricate!` method, which is used to build the resource via the @@ -391,6 +398,96 @@ end In this case, the result is similar to calling `Resource::Shirt.fabricate!`. +## Reusable resources + +Reusable resources are created by the first test that needs a particular kind of resource, and then any test that needs +the same kind of resource can reuse it instead of creating a new one. + +The `ReusableProject` resource is an example of this class: + +```ruby +module QA + module Resource + class ReusableProject < Project # A reusable resource inherits from the resource class that we want to be able to reuse. + prepend Reusable # The Reusable module mixes in some methods that help implement reuse. + + def initialize + super # A ReusableProject is a Project so it should be initialized as one. + + # Some Project attributes aren't valid and need to be overridden. For example, a ReusableProject keeps its name once it's created, + # so we don't add a random string to the name specified. + @add_name_uuid = false + + # It has a default name, and a different name can be specified when a resource is first created. However, the same name must be + # provided any time that instance of the resource is used. + @name = "reusable_project" + + # Several instances of a ReusableProject can exists as long as each is identified via a unique value for `reuse_as`. + @reuse_as = :default_project + end + + # All reusable resource classes must validate that an instance meets the conditions that allow reuse. For example, + # by confirming that the name specified for the instance is valid and doesn't conflict with other instances. + def validate_reuse_preconditions + raise ResourceReuseError unless reused_name_valid? + end + end + end +end +``` + +Consider some examples of how a reusable resource is used: + +```ruby +# This will create a project. +default_project = Resource::ReusableProject.fabricate_via_api! +default_project.name # => "reusable_project" +default_project.reuse_as # => :default_project +``` + +Then in another test we could reuse the project: + +```ruby +# This will fetch the project created above rather than creating a new one. +default_project_again = Resource::ReusableProject.fabricate_via_api! +default_project_again.name # => "reusable_project" +default_project_again.reuse_as # => :default_project +``` + +We can also create another project that we want to change in a way that might not be suitable for tests using the +default project: + +```ruby +project_with_member = Resource::ReusableProject.fabricate_via_api! do |project| + project.name = "project-with-member" + project.reuse_as = :project_with_member +end + +project_with_member.add_member(user) +``` + +Another test can reuse that project: + +```ruby +project_still_has_member = Resource::ReusableProject.fabricate_via_api! do |project| + project.name = "project-with-member" + project.reuse_as = :project_with_member +end + +expect(project_still_has_member).to have_member(user) +``` + +However, if we don't provide the name again an error will be raised: + +```ruby +Resource::ReusableProject.fabricate_via_api! do |project| + project.reuse_as = :project_with_member +end + +# => ResourceReuseError will be raised because it will try to use the default name, "reusable_project", which doesn't +# match the name specified when the project was first fabricated. +``` + ## Where to ask for help? If you need more information, ask for help on `#quality` channel on Slack diff --git a/doc/user/project/settings/index.md b/doc/user/project/settings/index.md index 0a3b9d5b2a5..7b8b713cf15 100644 --- a/doc/user/project/settings/index.md +++ b/doc/user/project/settings/index.md @@ -449,17 +449,22 @@ To delete a project: This action deletes a project including all associated resources (issues, merge requests, and so on). -In [GitLab 13.2](https://gitlab.com/gitlab-org/gitlab/-/issues/220382) and later, on Premium or higher tiers, -group Owners can [configure](../../group/index.md#enable-delayed-project-deletion) projects in a group -to be deleted after a delayed period. -When enabled, actual deletion happens after number of days -specified in [instance settings](../../admin_area/settings/visibility_and_access_controls.md#default-deletion-delay). - WARNING: -The default behavior of [delayed project deletion](https://gitlab.com/gitlab-org/gitlab/-/issues/32935) in GitLab 12.6 was changed to -[Immediate deletion](https://gitlab.com/gitlab-org/gitlab/-/issues/220382) in GitLab 13.2. +The default deletion behavior for projects was changed to [delayed project deletion](https://gitlab.com/gitlab-org/gitlab/-/issues/32935) +in GitLab 12.6, and then to [immediate deletion](https://gitlab.com/gitlab-org/gitlab/-/issues/220382) in GitLab 13.2. + +#### Delayed project deletion **(PREMIUM)** + +Projects in a group (not a personal namespace) can be deleted after a delay period. Multiple settings can affect whether +delayed project deletion is enabled for a particular project: + +- Self-managed instance [settings](../../admin_area/settings/visibility_and_access_controls.md#default-delayed-project-deletion). + You can enable delayed project deletion as the default setting for new groups, and configure the number of days for the + delay. For GitLab.com, see the [GitLab.com settings](../../gitlab_com/index.md#delayed-project-deletion). +- Group [settings](../../group/index.md#enable-delayed-project-deletion) to enabled delayed project deletion for all + projects in the group. -#### Delete a project immediately **(PREMIUM)** +##### Delete a project immediately > [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/191367) in GitLab 14.1. diff --git a/doc/user/report_abuse.md b/doc/user/report_abuse.md index 810e1427e11..dcb50aec3d5 100644 --- a/doc/user/report_abuse.md +++ b/doc/user/report_abuse.md @@ -16,54 +16,51 @@ A GitLab administrator [can then choose](admin_area/review_abuse_reports.md) to: You can report a user through their: -- [Profile](#reporting-abuse-through-a-users-profile) -- [Comments](#reporting-abuse-through-a-users-comment) -- [Issues and Merge requests](#reporting-abuse-through-a-users-issue-or-merge-request) +- [Profile](#report-abuse-from-the-users-profile-page) +- [Comments](#report-abuse-from-a-users-comment) +- [Issues and Merge requests](#report-abuse-through-a-users-issue-or-merge-request) - [Snippets](snippets.md#mark-snippet-as-spam) -## Reporting abuse through a user's profile +## Report abuse from the user's profile page To report abuse from a user's profile page: -1. Click on the exclamation point report abuse button at the top right of the - user's profile. +1. In the top right corner of the user's profile, select the exclamation point report abuse button. 1. Complete an abuse report. -1. Click the **Send report** button. +1. Select **Send report**. -## Reporting abuse through a user's comment +## Report abuse from a user's comment To report abuse from a user's comment: -1. Click on the vertical ellipsis (⋮) more actions button to open the dropdown. -1. Select **Report as abuse**. +1. In the comment, select the vertical ellipsis (**{ellipsis_v}**). +1. Select **Report abuse to admin**. 1. Complete an abuse report. -1. Click the **Send report** button. +1. Select **Send report**. NOTE: A URL to the reported user's comment is pre-filled in the abuse report's **Message** field. -## Reporting abuse through a user's issue or merge request +## Report abuse through a user's issue or merge request -The **Report abuse** button is displayed at the top right of the issue or merge request: - -- When **Report abuse** is selected from the menu that appears when the - **Close issue** or **Close merge request** button is clicked, for users that - have permission to close the issue or merge request. -- When viewing the issue or merge request, for users that don't have permission - to close the issue or merge request. +The **Report abuse** button is displayed at the top right of the issue or merge request. For users +with permission to close the issue or merge request, the button is available when you select +**Close issue** or **Close merge request**. For all other users, it is available when viewing the +issue or +merge request. With the **Report abuse** button displayed, to submit an abuse report: -1. Click the **Report abuse** button. +1. Select **Report abuse**. 1. Submit an abuse report. -1. Click the **Send report** button. +1. Select **Send report**. NOTE: A URL to the reported user's issue or merge request is pre-filled in the abuse report's **Message** field. -## Managing abuse reports +## Related topics -Administrators are able to view and resolve abuse reports. -For more information, see [abuse reports administration documentation](admin_area/review_abuse_reports.md). +- Administrators can view and resolve abuse reports. + For more information, see [abuse reports administration documentation](admin_area/review_abuse_reports.md). diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 8658fd5b6d9..abad8aaae9b 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -38264,6 +38264,9 @@ msgstr "" msgid "View Documentation" msgstr "" +msgid "View Stage: %{title}" +msgstr "" + msgid "View alert details at" msgstr "" diff --git a/qa/qa/resource/api_fabricator.rb b/qa/qa/resource/api_fabricator.rb index b94fa543b48..6315ef0bd22 100644 --- a/qa/qa/resource/api_fabricator.rb +++ b/qa/qa/resource/api_fabricator.rb @@ -7,14 +7,7 @@ module QA module Resource module ApiFabricator include Capybara::DSL - - ResourceFabricationFailedError = Class.new(RuntimeError) - ResourceNotDeletedError = Class.new(RuntimeError) - ResourceNotFoundError = Class.new(RuntimeError) - ResourceQueryError = Class.new(RuntimeError) - ResourceUpdateFailedError = Class.new(RuntimeError) - ResourceURLMissingError = Class.new(RuntimeError) - InternalServerError = Class.new(RuntimeError) + include Errors attr_reader :api_resource, :api_response attr_writer :api_client diff --git a/qa/qa/resource/errors.rb b/qa/qa/resource/errors.rb new file mode 100644 index 00000000000..a6933a6c9a7 --- /dev/null +++ b/qa/qa/resource/errors.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +module QA + module Resource + module Errors + ResourceFabricationFailedError = Class.new(RuntimeError) + ResourceNotDeletedError = Class.new(RuntimeError) + ResourceNotFoundError = Class.new(RuntimeError) + ResourceQueryError = Class.new(RuntimeError) + ResourceUpdateFailedError = Class.new(RuntimeError) + ResourceURLMissingError = Class.new(RuntimeError) + InternalServerError = Class.new(RuntimeError) + end + end +end diff --git a/qa/qa/resource/file.rb b/qa/qa/resource/file.rb index 9b05c0cb456..253b63e4260 100644 --- a/qa/qa/resource/file.rb +++ b/qa/qa/resource/file.rb @@ -7,7 +7,8 @@ module QA :author_name, :content, :commit_message, - :name + :name, + :start_branch attr_writer :branch attribute :project do @@ -27,6 +28,7 @@ module QA @name = 'QA Test - File name' @content = 'QA Test - File content' @commit_message = 'QA Test - Commit message' + @start_branch = project.default_branch end def branch @@ -57,6 +59,7 @@ module QA def api_post_body { branch: branch, + start_branch: start_branch, author_email: @author_email || Runtime::User.default_email, author_name: @author_name || Runtime::User.username, content: content, diff --git a/qa/qa/resource/reusable.rb b/qa/qa/resource/reusable.rb new file mode 100644 index 00000000000..24b0a1f6bce --- /dev/null +++ b/qa/qa/resource/reusable.rb @@ -0,0 +1,63 @@ +# frozen_string_literal: true + +module QA + module Resource + # + # This module includes methods that allow resource classes to be reused safely. It should be prepended to a new + # reusable version of an existing resource class. See Resource::Project and ReusableResource::Project for an example + # + module Reusable + attr_accessor :reuse, + :reuse_as + + ResourceReuseError = Class.new(RuntimeError) + + def self.prepended(base) + base.extend(ClassMethods) + end + + # Gets an existing resource if it exists and the parameters of the new specification of the resource are valid. + # Creates a new instance of the resource if it does not exist. + # + # @return [String] The URL of the resource. + def fabricate_via_api! + validate_reuse_preconditions + + resource_web_url(api_get) + rescue Errors::ResourceNotFoundError + super + ensure + self.class.resources[reuse_as] = self + end + + # Including classes must confirm that the resource can be reused as defined. For example, a project can't be + # fabricated with a unique name. + # + # @return [nil] + def validate_reuse_preconditions + return super if defined?(super) + + raise NotImplementedError + end + + module ClassMethods + # Removes all created resources of this type. + # + # @return [Hash<Symbol, QA::Resource>] the resources that were to be removed. + def remove_all_via_api! + resources.each do |reuse_as, resource| + QA::Runtime::Logger.debug("#{self.name} - removing #{reuse_as}") + resource.method(:remove_via_api!).super_method.call + end + end + + # The resources created by this resource class. + # + # @return [Hash<Symbol, QA::Resource>] the resources created by this resource class. + def resources + @resources ||= {} + end + end + end + end +end diff --git a/qa/qa/resource/reusable_project.rb b/qa/qa/resource/reusable_project.rb new file mode 100644 index 00000000000..6b33bb9d65d --- /dev/null +++ b/qa/qa/resource/reusable_project.rb @@ -0,0 +1,45 @@ +# frozen_string_literal: true + +module QA + module Resource + class ReusableProject < Project + prepend Reusable + + def initialize + super + + @add_name_uuid = false + @name = "reusable_project" + @reuse_as = :default_project + @initialize_with_readme = true + end + + # Confirms that the project can be reused + # + # @return [nil] returns nil unless an error is raised + def validate_reuse_preconditions + unless reused_name_unique? + raise ResourceReuseError, + "Reusable projects must have the same name. The project reused as #{reuse_as} has the name '#{name}' but it should be '#{self.class.resources[reuse_as].name}'" + end + end + + # Checks if the project is being reused with the same name. + # + # @return [Boolean] true if the project's name is different from another project with the same reuse symbol (reuse_as) + def reused_name_unique? + return true unless self.class.resources.key?(reuse_as) + + self.class.resources[reuse_as].name == name + end + + # Overrides QA::Resource::Project#remove_via_api! to log a debug message stating that removal will happen after + # the suite completes rather than now. + # + # @return [nil] + def remove_via_api! + QA::Runtime::Logger.debug("#{self.class.name} - deferring removal until after suite") + end + end + end +end diff --git a/qa/qa/service/praefect_manager.rb b/qa/qa/service/praefect_manager.rb index ebc969dd37a..089e7b11554 100644 --- a/qa/qa/service/praefect_manager.rb +++ b/qa/qa/service/praefect_manager.rb @@ -386,20 +386,15 @@ module QA def list_untracked_repositories untracked_repositories = [] shell "docker exec #{@praefect} bash -c 'gitlab-ctl praefect list-untracked-repositories'" do |line| - # Results look like this depending on whether untracked items found or not - # Running list-untracked-repositories - # Done. - - # Running list-untracked-repositories + # Results look like this + # The following repositories were found on disk, but missing from the tracking database: # {"relative_path":"@hashed/aa/bb.git","storage":"gitaly1","virtual_storage":"default"} # {"relative_path":"@hashed/bb/cc.git","storage":"gitaly3","virtual_storage":"default"} - # Done. QA::Runtime::Logger.debug(line.chomp) - next if line.start_with?('Running list-untracked-repositories') - next if line.start_with?('Done.') - untracked_repositories.append(JSON.parse(line)) + rescue JSON::ParserError + # Ignore lines that can't be parsed as JSON end QA::Runtime::Logger.debug("list_untracked_repositories --- #{untracked_repositories}") diff --git a/qa/qa/specs/features/api/1_manage/project_access_token_spec.rb b/qa/qa/specs/features/api/1_manage/project_access_token_spec.rb index 47c07875257..77603c3e49e 100644 --- a/qa/qa/specs/features/api/1_manage/project_access_token_spec.rb +++ b/qa/qa/specs/features/api/1_manage/project_access_token_spec.rb @@ -4,7 +4,10 @@ module QA RSpec.describe 'Manage' do describe 'Project access token' do before(:all) do - @project_access_token = QA::Resource::ProjectAccessToken.fabricate_via_api! + @project_access_token = QA::Resource::ProjectAccessToken.fabricate_via_api! do |pat| + pat.project = Resource::ReusableProject.fabricate_via_api! + end + @user_api_client = Runtime::API::Client.new(:gitlab, personal_access_token: @project_access_token.token) end @@ -14,7 +17,7 @@ module QA Resource::File.fabricate_via_api! do |file| file.api_client = @user_api_client file.project = @project_access_token.project - file.branch = 'new_branch' + file.branch = "new_branch_#{SecureRandom.hex(8)}" file.commit_message = 'Add new file' file.name = "text-#{SecureRandom.hex(8)}.txt" file.content = 'New file' @@ -27,7 +30,7 @@ module QA Resource::Repository::Commit.fabricate_via_api! do |commit| commit.api_client = @user_api_client commit.project = @project_access_token.project - commit.branch = 'new_branch' + commit.branch = "new_branch_#{SecureRandom.hex(8)}" commit.start_branch = @project_access_token.project.default_branch commit.commit_message = 'Add new file' commit.add_files([ @@ -48,7 +51,7 @@ module QA Resource::File.fabricate_via_api! do |file| file.api_client = @user_api_client file.project = @different_project - file.branch = 'new_branch' + file.branch = "new_branch_#{SecureRandom.hex(8)}" file.commit_message = 'Add new file' file.name = "text-#{SecureRandom.hex(8)}.txt" file.content = 'New file' @@ -61,7 +64,7 @@ module QA Resource::Repository::Commit.fabricate_via_api! do |commit| commit.api_client = @user_api_client commit.project = @different_project - commit.branch = 'new_branch' + commit.branch = "new_branch_#{SecureRandom.hex(8)}" commit.start_branch = @different_project.default_branch commit.commit_message = 'Add new file' commit.add_files([ diff --git a/qa/qa/specs/features/api/3_create/gitaly/automatic_failover_and_recovery_spec.rb b/qa/qa/specs/features/api/3_create/gitaly/automatic_failover_and_recovery_spec.rb index cdc4d95b0be..1b19f62e367 100644 --- a/qa/qa/specs/features/api/3_create/gitaly/automatic_failover_and_recovery_spec.rb +++ b/qa/qa/specs/features/api/3_create/gitaly/automatic_failover_and_recovery_spec.rb @@ -71,7 +71,7 @@ module QA end context 'when recovering from dataloss after failover' do - it 'automatically reconciles', quarantine: { issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/238187', type: :stale }, testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/quality/test_cases/1266' do + it 'automatically reconciles', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/quality/test_cases/1266' do # Start the old primary node again praefect_manager.start_primary_node praefect_manager.wait_for_primary_node_health_check diff --git a/qa/qa/specs/features/api/3_create/merge_request/push_options_labels_spec.rb b/qa/qa/specs/features/api/3_create/merge_request/push_options_labels_spec.rb index 26ca6de29f7..8a834f305d3 100644 --- a/qa/qa/specs/features/api/3_create/merge_request/push_options_labels_spec.rb +++ b/qa/qa/specs/features/api/3_create/merge_request/push_options_labels_spec.rb @@ -11,12 +11,7 @@ module QA let(:title) { "MR push options test #{SecureRandom.hex(8)}" } let(:commit_message) { 'Add README.md' } - let(:project) do - Resource::Project.fabricate_via_api! do |project| - project.name = 'merge-request-push-options' - project.initialize_with_readme = true - end - end + let(:project) { Resource::ReusableProject.fabricate_via_api! } def create_new_mr_via_push Resource::Repository::ProjectPush.fabricate! do |push| diff --git a/qa/spec/spec_helper.rb b/qa/spec/spec_helper.rb index 640f2de0ca2..4372d9d2728 100644 --- a/qa/spec/spec_helper.rb +++ b/qa/spec/spec_helper.rb @@ -64,6 +64,13 @@ RSpec.configure do |config| end end + config.after(:suite) do |suite| + # If any tests failed, leave the resources behind to help troubleshoot + next if suite.reporter.failed_examples.present? + + QA::Resource::ReusableProject.remove_all_via_api! + end + config.expect_with :rspec do |expectations| expectations.include_chain_clauses_in_custom_matcher_descriptions = true end diff --git a/spec/models/clusters/applications/runner_spec.rb b/spec/models/clusters/applications/runner_spec.rb index 806c60d5aff..434d7ad4a90 100644 --- a/spec/models/clusters/applications/runner_spec.rb +++ b/spec/models/clusters/applications/runner_spec.rb @@ -69,66 +69,9 @@ RSpec.describe Clusters::Applications::Runner do expect(values).to include('privileged: true') expect(values).to include('image: ubuntu:16.04') expect(values).to include('resources') - expect(values).to match(/runnerToken: ['"]?#{Regexp.escape(ci_runner.token)}/) expect(values).to match(/gitlabUrl: ['"]?#{Regexp.escape(Gitlab::Routing.url_helpers.root_url)}/) end - context 'without a runner' do - let(:application) { create(:clusters_applications_runner, runner: nil, cluster: cluster) } - let(:runner) { application.runner } - - shared_examples 'runner creation' do - it 'creates a runner' do - expect { subject }.to change { Ci::Runner.count }.by(1) - end - - it 'uses the new runner token' do - expect(values).to match(/runnerToken: '?#{Regexp.escape(runner.token)}/) - end - end - - context 'project cluster' do - let(:project) { create(:project) } - let(:cluster) { create(:cluster, :with_installed_helm, projects: [project]) } - - include_examples 'runner creation' - - it 'creates a project runner' do - subject - - runner_projects = Project.where(id: runner.runner_projects.pluck(:project_id)) - expect(runner).to be_project_type - expect(runner_projects).to match_array [project] - end - end - - context 'group cluster' do - let(:group) { create(:group) } - let(:cluster) { create(:cluster, :with_installed_helm, cluster_type: :group_type, groups: [group]) } - - include_examples 'runner creation' - - it 'creates a group runner' do - subject - - expect(runner).to be_group_type - expect(runner.runner_namespaces.pluck(:namespace_id)).to match_array [group.id] - end - end - - context 'instance cluster' do - let(:cluster) { create(:cluster, :with_installed_helm, :instance) } - - include_examples 'runner creation' - - it 'creates an instance runner' do - subject - - expect(runner).to be_instance_type - end - end - end - context 'with duplicated values on vendor/runner/values.yaml' do let(:stub_values) do { |