diff options
48 files changed, 1638 insertions, 765 deletions
diff --git a/app/assets/javascripts/issues/show/components/incidents/timeline_events_item.vue b/app/assets/javascripts/issues/show/components/incidents/timeline_events_item.vue index bc7c7c505e0..90ee4351e39 100644 --- a/app/assets/javascripts/issues/show/components/incidents/timeline_events_item.vue +++ b/app/assets/javascripts/issues/show/components/incidents/timeline_events_item.vue @@ -1,5 +1,5 @@ <script> -import { GlDropdown, GlDropdownItem, GlIcon, GlSprintf } from '@gitlab/ui'; +import { GlDropdown, GlDropdownItem, GlIcon, GlSprintf, GlBadge } from '@gitlab/ui'; import SafeHtml from '~/vue_shared/directives/safe_html'; import { formatDate } from '~/lib/utils/datetime_utility'; import { timelineItemI18n } from './constants'; @@ -13,6 +13,7 @@ export default { GlDropdownItem, GlIcon, GlSprintf, + GlBadge, }, directives: { SafeHtml, @@ -31,6 +32,11 @@ export default { type: String, required: true, }, + eventTag: { + type: String, + required: false, + default: null, + }, }, computed: { time() { @@ -50,11 +56,16 @@ export default { <gl-icon :name="getEventIcon(action)" class="note-icon" /> </div> <div class="timeline-event-note timeline-event-border" data-testid="event-text-container"> - <strong class="gl-font-lg" data-testid="event-time"> - <gl-sprintf :message="$options.i18n.timeUTC"> - <template #time>{{ time }}</template> - </gl-sprintf> - </strong> + <div class="gl-display-flex gl-align-items-center gl-mb-3"> + <strong class="gl-font-lg" data-testid="event-time"> + <gl-sprintf :message="$options.i18n.timeUTC"> + <template #time>{{ time }}</template> + </gl-sprintf> + </strong> + <gl-badge v-if="eventTag" variant="muted" icon="tag" class="gl-ml-3"> + {{ eventTag }} + </gl-badge> + </div> <div v-safe-html="noteHtml" class="md"></div> </div> <gl-dropdown diff --git a/app/controllers/concerns/vscode_cdn_csp.rb b/app/controllers/concerns/vscode_cdn_csp.rb index 8446d237f16..dc8cea966e5 100644 --- a/app/controllers/concerns/vscode_cdn_csp.rb +++ b/app/controllers/concerns/vscode_cdn_csp.rb @@ -6,7 +6,7 @@ module VSCodeCDNCSP included do content_security_policy do |policy| - next if !Feature.enabled?(:vscode_web_ide) || policy.directives.blank? + next if policy.directives.blank? default_src = Array(policy.directives['default-src'] || []) policy.directives['frame-src'] ||= default_src diff --git a/app/serializers/issue_entity.rb b/app/serializers/issue_entity.rb index 82eef55205a..397f333008c 100644 --- a/app/serializers/issue_entity.rb +++ b/app/serializers/issue_entity.rb @@ -7,11 +7,15 @@ class IssueEntity < IssuableEntity item.try(:upcase) end + format_with(:iso8601) do |item| + item.try(:iso8601) + end + expose :state expose :milestone_id expose :updated_by_id - expose :created_at - expose :updated_at + expose :created_at, format_with: :iso8601 + expose :updated_at, format_with: :iso8601 expose :milestone, using: API::Entities::Milestone expose :labels, using: LabelEntity expose :lock_version diff --git a/app/serializers/merge_request_metrics_entity.rb b/app/serializers/merge_request_metrics_entity.rb index 1c9db08d103..ded82a9ef45 100644 --- a/app/serializers/merge_request_metrics_entity.rb +++ b/app/serializers/merge_request_metrics_entity.rb @@ -1,8 +1,12 @@ # frozen_string_literal: true class MergeRequestMetricsEntity < Grape::Entity - expose :latest_closed_at, as: :closed_at - expose :merged_at + format_with(:iso8601) do |item| + item.try(:iso8601) + end + + expose :latest_closed_at, as: :closed_at, format_with: :iso8601 + expose :merged_at, format_with: :iso8601 expose :latest_closed_by, as: :closed_by, using: UserEntity expose :merged_by, using: UserEntity end diff --git a/app/validators/json_schemas/build_report_result_data.json b/app/validators/json_schemas/build_report_result_data.json index 0a12c9c39a7..d109389a046 100644 --- a/app/validators/json_schemas/build_report_result_data.json +++ b/app/validators/json_schemas/build_report_result_data.json @@ -3,11 +3,16 @@ "description": "Build report result data", "type": "object", "properties": { - "coverage": { "type": "float" }, + "coverage": { + "type": "number", + "format": "float" + }, "tests": { "type": "object", - "items": { "$ref": "./build_report_result_data_tests.json" } + "items": { + "$ref": "./build_report_result_data_tests.json" + } } }, "additionalProperties": false -} +}
\ No newline at end of file diff --git a/app/validators/json_schemas/build_report_result_data_tests.json b/app/validators/json_schemas/build_report_result_data_tests.json index 610070fde5f..3b6a2688313 100644 --- a/app/validators/json_schemas/build_report_result_data_tests.json +++ b/app/validators/json_schemas/build_report_result_data_tests.json @@ -3,12 +3,24 @@ "description": "Build report result data tests", "type": "object", "properties": { - "name": { "type": "string" }, - "duration": { "type": "string" }, - "failed": { "type": "integer" }, - "errored": { "type": "integer" }, - "skipped": { "type": "integer" }, - "success": { "type": "integer" } + "name": { + "type": "string" + }, + "duration": { + "type": "string" + }, + "failed": { + "type": "integer" + }, + "errored": { + "type": "integer" + }, + "skipped": { + "type": "integer" + }, + "success": { + "type": "integer" + } }, "additionalProperties": false -} +}
\ No newline at end of file diff --git a/app/validators/json_schemas/daily_build_group_report_result_data.json b/app/validators/json_schemas/daily_build_group_report_result_data.json index 2b073506375..5b153b47b1e 100644 --- a/app/validators/json_schemas/daily_build_group_report_result_data.json +++ b/app/validators/json_schemas/daily_build_group_report_result_data.json @@ -3,7 +3,10 @@ "description": "Daily build group report result data", "type": "object", "properties": { - "coverage": { "type": "float" } + "coverage": { + "type": "number", + "format": "float" + } }, "additionalProperties": false -} +}
\ No newline at end of file diff --git a/app/views/invites/show.html.haml b/app/views/invites/show.html.haml index c1ee12bb6c8..5f65405c8bc 100644 --- a/app/views/invites/show.html.haml +++ b/app/views/invites/show.html.haml @@ -17,8 +17,10 @@ = html_escape(_("You have been invited by %{link_to_inviter} to join %{source_name} %{strong_open}%{link_to_source}%{strong_close} as %{role}")) % { link_to_inviter: link_to_inviter, source_name: @invite_details[:title], strong_open: '<strong>'.html_safe, link_to_source: link_to_source, strong_close: '</strong>'.html_safe, role: @member.human_access } .actions - = link_to _("Accept invitation"), accept_invite_url(@token), method: :post, class: "btn gl-button btn-confirm" - = link_to _("Decline"), decline_invite_url(@token), method: :post, class: "btn gl-button btn-danger gl-ml-3" + = render Pajamas::ButtonComponent.new(variant: :confirm, method: :post, href: accept_invite_url(@token)) do + = _("Accept invitation") + = render Pajamas::ButtonComponent.new(variant: :danger, method: :post, href: decline_invite_url(@token), button_options: { class: 'gl-ml-3' }) do + = _("Decline") - else %p diff --git a/app/views/jira_connect/users/show.html.haml b/app/views/jira_connect/users/show.html.haml index 569c4587f14..5db6cb44ff6 100644 --- a/app/views/jira_connect/users/show.html.haml +++ b/app/views/jira_connect/users/show.html.haml @@ -11,7 +11,10 @@ = s_('JiraService|You can now close this window and%{br}return to the GitLab for Jira application.').html_safe % { br: '<br>'.html_safe } - if @jira_app_link - %p= link_to s_('Integrations|Return to GitLab for Jira'), @jira_app_link, class: 'gl-button btn btn-confirm' + %p + = render Pajamas::ButtonComponent.new(href: @jira_app_link, variant: :confirm) do + = s_('Integrations|Return to GitLab for Jira') + %p= link_to _('Sign out'), destroy_user_session_path, method: :post diff --git a/config/events/schema.json b/config/events/schema.json index 3a0616e706b..6ce93e1a40e 100644 --- a/config/events/schema.json +++ b/config/events/schema.json @@ -12,59 +12,101 @@ "type": "string" }, "label_description": { - "type": ["string", "null"] + "type": [ + "string", + "null" + ] }, "property_description": { - "type": ["string", "null"] + "type": [ + "string", + "null" + ] }, "value_description": { - "type": ["string", "null"] + "type": [ + "string", + "null" + ] }, "extra_properties": { - "type": ["object", "null"] + "type": [ + "object", + "null" + ] }, "identifiers": { - "type": ["array", "null"], + "type": [ + "array", + "null" + ], "items": { "type": "string", - "enum": ["project", "user", "namespace"] + "enum": [ + "project", + "user", + "namespace" + ] } }, "iglu_schema_url": { - "type": ["string", "null"] + "type": [ + "string", + "null" + ] }, "product_section": { "type": "string" }, "product_stage": { - "type": ["string", "null"] + "type": [ + "string", + "null" + ] }, "product_group": { "type": "string" }, "product_category": { - "type": ["string", "null"] + "type": [ + "string", + "null" + ] }, "introduced_by_url": { - "type": ["uri", "null"] + "type": [ + "string", + "null" + ], + "format": "uri" }, "milestone": { - "type": ["string", "null"], + "type": [ + "string", + "null" + ], "pattern": "^[0-9]+\\.[0-9]+$" }, "distributions": { "type": "array", "items": { "type": "string", - "enum": ["ee", "ce"] + "enum": [ + "ee", + "ce" + ] } }, "tiers": { "type": "array", "items": { "type": "string", - "enum": ["free", "premium", "ultimate"] + "enum": [ + "free", + "premium", + "ultimate" + ] } } } -} +}
\ No newline at end of file diff --git a/config/initializers/1_settings.rb b/config/initializers/1_settings.rb index 51dff8a422b..af7a2090cec 100644 --- a/config/initializers/1_settings.rb +++ b/config/initializers/1_settings.rb @@ -1052,6 +1052,12 @@ Settings.prometheus['enabled'] ||= false Settings.prometheus['server_address'] ||= nil # +# Bullet settings +# +Settings['bullet'] ||= Settingslogic.new({}) +Settings.bullet['enabled'] ||= Rails.env.development? + +# # Shutdown settings # Settings['shutdown'] ||= Settingslogic.new({}) diff --git a/doc/user/application_security/dast/authentication.md b/doc/user/application_security/dast/authentication.md index e75c867cd1a..66fd9c13589 100644 --- a/doc/user/application_security/dast/authentication.md +++ b/doc/user/application_security/dast/authentication.md @@ -340,6 +340,11 @@ dast: ## Troubleshooting +The [logs](#read-the-logs) provide insight into what DAST is doing and expecting during the authentication process. For more detailed +information, configure the [authentication report](#configure-the-authentication-report). + +For more information about particular error messages or situations see [known problems](#known-problems). + ### Read the logs The console output of the DAST CI/CD job shows information about the authentication process using the `AUTH` log module. @@ -358,19 +363,7 @@ Authentication failed because a home page should be displayed after login. Inste 2022-11-16T13:43:21.000 INF AUTH login attempt failed error="authentication failed: failed to authenticate user" ``` -### When authentication succeeds and the scan doesn't crawl authenticated pages - -Verify the captured authentication tokens are correct when a scan appears to authenticate and fails to crawl the authenticated pages of the application. -Names of cookies and session tokens determined by DAST to be authentication tokens are written to the log. For example: - -```plaintext -2022-11-24T14:42:31.492 INF AUTH authentication token cookies names=["sessionID"] -2022-11-24T14:42:31.492 INF AUTH authentication token storage events keys=["token"] -``` - -See [authentication tokens](#authentication-tokens) for more information. - -### Configure the authentication debug report +### Configure the authentication report An authentication report can be saved as a CI/CD job artifact to assist with understanding the cause of an authentication failure. @@ -389,3 +382,144 @@ dast: paths: [gl-dast-debug-auth-report.html] when: always ``` + +### Known problems + +#### Login form not found + +DAST failed to find a login form when loading the login page, often because the authentication URL could not be loaded. +The log reports a fatal error such as: + +```plaintext +2022-12-07T12:44:02.838 INF AUTH loading login page LoginURL=[authentication URL] +2022-12-07T12:44:11.119 FTL MAIN authentication failed: login form not found +``` + +Suggested actions: + +- Generate the [authentication report](#configure-the-authentication-report) to inspect HTTP response. +- Check the target application authentication is deployed and running. +- Check the `DAST_AUTH_URL` is correct. +- Check the GitLab Runner can access the `DAST_AUTH_URL`. +- Check the `DAST_BROWSER_PATH_TO_LOGIN_FORM` is valid if used. + +#### Scan doesn't crawl authenticated pages + +If DAST captures the wrong [authentication tokens](#authentication-tokens) during the authentication process then +the scan can't crawl authenticated pages. Names of cookies and storage authentication tokens are written to the log. For example: + +```plaintext +2022-11-24T14:42:31.492 INF AUTH authentication token cookies names=["sessionID"] +2022-11-24T14:42:31.492 INF AUTH authentication token storage events keys=["token"] +``` + +Suggested actions: + +- Generate the [authentication report](#configure-the-authentication-report) and look at the screenshot from the `Login submit` to verify that the login worked as expected. +- Verify the logged authentication tokens are those used by your application. +- If using cookies to store authentication tokens, set the names of the authentication token cookies using `DAST_AUTH_COOKIES`. + +#### Unable to find elements with selector + +DAST failed to find the username, password, first submit button, or submit button elements. The log reports a fatal error such as: + +```plaintext +2022-12-07T13:14:11.545 FTL MAIN authentication failed: unable to find elements with selector: css:#username +``` + +Suggested actions: + +- Generate the [authentication report](#configure-the-authentication-report) to use the screenshot from the `Login page` to verify that the page loaded correctly. +- Load the login page in a browser and verify the [selectors](#finding-an-elements-selector) configured in `DAST_USERNAME_FIELD`, `DAST_PASSWORD_FIELD`, `DAST_FIRST_SUBMIT_FIELD`, and `DAST_SUBMIT_FIELD` are correct. + +#### Failed to authenticate user + +DAST failed to authenticate due to a failed login verification check. The log reports a fatal error such as: + +```plaintext +2022-12-07T06:39:49.483 INF AUTH verifying if login attempt was successful true_when="HTTP status code < 400 and has authentication token and no login form found (no element found when searching using selector css:[name=username] or css:[name=password] or css:button[type=\"submit\"])" +2022-12-07T06:39:49.484 INF AUTH requirement is satisfied, HTTP login request returned status code 303 url=http://auth-manual:8090/login want="HTTP status code < 400" +2022-12-07T06:39:49.513 INF AUTH requirement is unsatisfied, login form was found want="no login form found (no element found when searching using selector css:[name=username] or css:[name=password] or css:button[type=\"submit\"])" +2022-12-07T06:39:49.589 INF AUTH login attempt failed error="authentication failed: failed to authenticate user" +2022-12-07T06:39:53.626 FTL MAIN authentication failed: failed to authenticate user +``` + +Suggested actions: + +- Look in the log for the `requirement is unsatisfied`. Respond to the appropriate error. + +#### Requirement unsatisfied, login form was found + +Applications typically display a dashboard when the user logs in and the login form with an error message when the +username or password is incorrect. + +This error occurs when DAST detects the login form on the page displayed after authenticating the user, +indicating that the login attempt failed. + +```plaintext +2022-12-07T06:39:49.513 INF AUTH requirement is unsatisfied, login form was found want="no login form found (no element found when searching using selector css:[name=username] or css:[name=password] or css:button[type=\"submit\"])" +``` + +Suggested actions: + +- Verify that the username and password/authentication credentials used are correct. +- Generate the [authentication report](#configure-the-authentication-report) and verify the `Request` for the `Login submit` is correct. +- It's possible that the authentication report `Login submit` request and response are empty. This occurs when there is no request that would result + in a full page reload, such as a request made when submitting a HTML form. This occurs when using websockets or AJAX to submit the login form. +- If the page displayed following user authentication genuinely has elements matching the login form selectors, configure `DAST_AUTH_VERIFICATION_URL` + or `DAST_AUTH_VERIFICATION_SELECTOR` to use an alternate method of verifying the login attempt. + +#### Requirement unsatisfied, selector returned no results + +DAST cannot find an element matching the selector provided in `DAST_AUTH_VERIFICATION_SELECTOR` on the page displayed following user login. + +```plaintext +2022-12-07T06:39:33.239 INF AUTH requirement is unsatisfied, searching DOM using selector returned no results want="has element css:[name=welcome]" +``` + +Suggested actions: + +- Generate the [authentication report](#configure-the-authentication-report) and look at the screenshot from the `Login submit` to verify that the expected page is displayed. +- Ensure the `DAST_AUTH_VERIFICATION_SELECTOR` [selector](#finding-an-elements-selector) is correct. + +#### Requirement unsatisfied, browser not at URL + +DAST detected that the page displayed following user login has a URL different to what was expected according to `DAST_AUTH_VERIFICATION_URL`. + +```plaintext +2022-12-07T11:28:00.241 INF AUTH requirement is unsatisfied, browser is not at URL browser_url="https://example.com/home" want="is at url https://example.com/user/dashboard" +``` + +Suggested actions: + +- Generate the [authentication report](#configure-the-authentication-report) and look at the screenshot from the `Login submit` to verify that the expected page is displayed. +- Ensure the `DAST_AUTH_VERIFICATION_URL` is correct. + +#### Requirement unsatisfied, HTTP login request status code + +The HTTP response when loading the login form or submitting the form had a status code of 400 (client error) +or 500 (server error). + +```plaintext +2022-12-07T06:39:53.626 INF AUTH requirement is unsatisfied, HTTP login request returned status code 502 url="https://example.com/user/login" want="HTTP status code < 400" +``` + +- Verify that the username and password/authentication credentials used are correct. +- Generate the [authentication report](#configure-the-authentication-report) and verify the `Request` for the `Login submit` is correct. +- Verify the target application works as expected. + +#### Requirement unsatisfied, no authentication token + +DAST could not detect an [authentication token](#authentication-tokens) created during the authentication process. + +```plaintext +2022-12-07T11:25:29.010 INF AUTH authentication token cookies names=[] +2022-12-07T11:25:29.010 INF AUTH authentication token storage events keys=[] +2022-12-07T11:25:29.010 INF AUTH requirement is unsatisfied, no basic authentication, cookie or storage event authentication token detected want="has authentication token" +``` + +Suggestion actions: + +- Generate the [authentication report](#configure-the-authentication-report) and look at the screenshot from the `Login submit` to verify that the login worked as expected. +- Using the browser's developer tools, investigate the cookies and local/session storage objects created while logging in. Ensure there is an authentication token created with sufficiently random value. +- If using cookies to store authentication tokens, set the names of the authentication token cookies using `DAST_AUTH_COOKIES`. diff --git a/doc/user/gitlab_com/index.md b/doc/user/gitlab_com/index.md index c420d04d15c..9bb1c4e968c 100644 --- a/doc/user/gitlab_com/index.md +++ b/doc/user/gitlab_com/index.md @@ -445,11 +445,18 @@ If more than the maximum number of allowed connections occur concurrently, they are dropped and users get [an `ssh_exchange_identification` error](../../topics/git/troubleshooting_git.md#ssh_exchange_identification-error). -### Import/export +### Group and project import by uploading export files -To help avoid abuse, project and group imports, exports, and export downloads -are rate limited. See [Project import/export rate limits](../../user/project/settings/import_export.md#rate-limits) and [Group import/export rate limits](../../user/group/settings/import_export.md#rate-limits) -for details. +To help avoid abuse, the following are rate limited: + +- Project and group imports. +- Group and project exports that use files. +- Export downloads. + +For more information, see: + +- [Project import/export rate limits](../../user/project/settings/import_export.md#rate-limits). +- [Group import/export rate limits](../../user/group/import/index.md#rate-limits). ### Non-configurable limits diff --git a/doc/user/group/import/index.md b/doc/user/group/import/index.md index 39711d04c27..02921210f01 100644 --- a/doc/user/group/import/index.md +++ b/doc/user/group/import/index.md @@ -4,7 +4,21 @@ group: Import info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments --- -# Migrating groups with GitLab Migration **(FREE)** +# Migrating GitLab groups **(FREE)** + +You can migrate GitLab groups: + +- From self-managed GitLab to GitLab.com. +- From GitLab.com to self-managed GitLab. +- From one self-managed GitLab instance to another. +- Between groups in the same GitLab instance. + +You can migrate groups in two ways: + +- By direct transfer (recommended). +- By uploading an export file. + +## Migrate groups by direct transfer (recommended) > - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/249160) in GitLab 13.7 for group resources [with a flag](../../feature_flags.md) named `bulk_import`. Disabled by default. > - Group items [enabled on GitLab.com and self-managed](https://gitlab.com/gitlab-org/gitlab/-/issues/338985) in GitLab 14.3. @@ -18,86 +32,96 @@ On self-managed GitLab, by default [migrating project items](#migrated-project-i this feature, ask an administrator to [enable the feature flag](../../../administration/feature_flags.md) named `bulk_import_projects`. On GitLab.com, migration of both groups and projects is available. -Users with the Owner role on a top-level group can use GitLab Migration to migrate that top-level group to: +Prerequisites: + +- Network connection between instances or GitLab.com. Must support HTTPS. +- Owner role on the top-level group to migrate. + +You can import top-level groups to: - Another top-level group. - The subgroup of any existing top-level group. - Another GitLab instance, including GitLab.com. -Migrating groups using GitLab Migration is not the same as [migrating groups using file exports](../settings/import_export.md). -Importing and exporting groups using file exports requires you to export a group to a file and then import that file in -another GitLab instance. Migrating groups using GitLab Migration automates this step. +You can migrate: + +- By direct transfer using either the UI or the [API](../../../api/bulk_imports.md). +- Many groups at once. -## Import your groups and projects into GitLab +When migrating a top-level group to GitLab.com, all its subgroups and projects are migrated too. -When you migrate a group, you connect to your GitLab instance and then choose -groups to import. Not all the data is migrated. See: +Not all group and project resources are imported. See list of migrated resources below: - [Migrated group items](#migrated-group-items). - [Migrated project items](#migrated-project-items). -Leave feedback about group migration in [the relevant issue](https://gitlab.com/gitlab-org/gitlab/-/issues/284495). +### Preparation GitLab maps users and their contributions correctly provided: - Users already exists on the target GitLab instance. - Users have a public email on the source GitLab instance that matches their primary email on the target GitLab instance. +- When using an OmniAuth provider like SAML, GitLab and SAML accounts of users on GitLab must be linked. All users on the target GitLab instance must sign in + and verify their account on the target GitLab instance. -When using an OmniAuth provider like SAML, GitLab and SAML accounts of users on GitLab must be linked. All users on the target GitLab instance must sign in and verify -their account on the target GitLab instance. If the accounts are not linked, the user contribution mapping doesn't work correctly. - -NOTE: You might need to reconfigure your firewall to prevent blocking the connection on the self-managed instance. -### Connect to the remote GitLab instance +### Connect to the source GitLab instance -Before you begin, ensure that the target GitLab instance can communicate with the source over HTTPS -(HTTP is not supported). You might need to reconfigure your firewall to prevent blocking the connection on the self-managed -instance. +Create the group you want to import to and connect the source: -Then create the group you want to import into, and connect: +1. Create either: -1. Create a new group or subgroup: - - - On the top bar, select `+` and then **New group**. - - Or, on an existing group's page, in the top right, select **New subgroup**. - -1. Select **Import group**. -1. Enter the source URL of your GitLab instance. + - A new group. On the top bar, select **{plus-square}**, then **New group**, and select **Import group**. + - A new subgroup. On existing group's page, either: + - Select **New subgroup**. + - On the top bar, Select **{plus-square}** and then **New subgroup**. Then on the left sidebar, select the **import an existing group** link. +1. Enter the URL of your source GitLab instance. 1. Generate or copy a [personal access token](../../../user/profile/personal_access_tokens.md) - with the `api` and `read_repository` scopes on your remote GitLab instance. -1. Enter the [personal access token](../../../user/profile/personal_access_tokens.md) for your remote GitLab instance. + with the `api` scope on your source GitLab instance. Both `api` and `read_repository` scopes are required when migrating from GitLab 15.0 and earlier. +1. Enter the [personal access token](../../../user/profile/personal_access_tokens.md) for your source GitLab instance. 1. Select **Connect instance**. ### Select the groups to import -After you have authorized access to the GitLab instance, you are redirected to the GitLab Group -Migration importer page. The remote groups you have the Owner role for are listed. +After you have authorized access to the source GitLab instance, you are redirected to the GitLab group +importer page. The top-level groups on the connected source instance you have the Owner role for are listed. -1. By default, the proposed group namespaces match the names as they exist in remote instance, but based on your permissions, you can choose to edit these names before you proceed to import any of them. +1. By default, the proposed group namespaces match the names as they exist in source instance, but based on your permissions, you can choose to edit these names before you proceed to import any of them. 1. Next to the groups you want to import, select **Import**. 1. The **Status** column shows the import status of each group. If you leave the page open, it updates in real-time. 1. After a group has been imported, select its GitLab path to open its GitLab URL. ![Group Importer page](img/bulk_imports_v14_1.png) -## Automate group and project import **(PREMIUM)** +### Group import history -For information on automating user, group, and project import API calls, see -[Automate group and project import](../../project/import/index.md#automate-group-and-project-import). +You can view all groups migrated by you by direct transfer listed on the group import history page. This list includes: + +- Paths of source groups. +- Paths of destination groups. +- Start date of each import. +- Status of each import. +- Error details if any errors occurred. + +To view group import history: + +1. Sign in to GitLab. +1. On the top bar, select **Create new…** (**{plus-square}**). +1. Select **New group**. +1. Select **Import group**. +1. Select **History** in the upper right corner. +1. If there are any errors for a particular import, you can see them by selecting **Details**. -## Migrated group items +### Migrated group items The [`import_export.yml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/import_export/group/import_export.yml) -file for groups lists many of the items migrated when migrating groups using group migration. View this file in the branch +file for groups lists many of the items imported when migrating groups by direct transfer. View this file in the branch for your version of GitLab to see the list of items relevant to you. For example, [`import_export.yml` on the `14-10-stable-ee` branch](https://gitlab.com/gitlab-org/gitlab/-/blob/14-10-stable-ee/lib/gitlab/import_export/group/import_export.yml). -Migrating projects with file exports uses the same export and import mechanisms as creating projects from templates at the [group](../custom_project_templates.md) and -[instance](../../admin_area/custom_project_templates.md) levels. Therefore, the list of exported items is the same. - -Items that are migrated to the target instance include: +Group items that are migrated to the target instance include: - Badges ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/292431) in 13.11) - Board Lists @@ -122,21 +146,20 @@ Items that are migrated to the target instance include: Any other items are **not** migrated. -## Migrated project items +### Migrated project items > - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/267945) in GitLab 14.4 [with a flag](../../feature_flags.md) named `bulk_import_projects`. Disabled by default. > - [Enabled on GitLab.com](https://gitlab.com/gitlab-org/gitlab/-/issues/339941) in GitLab 15.6. FLAG: -On self-managed GitLab, migrating project resources are not available by default. To make them available, ask an administrator to [enable the feature flag](../../../administration/feature_flags.md) named `bulk_import_projects`. On GitLab.com, migration of project resources is available. +On self-managed GitLab, migrating project resources when migrating groups is not available by default. To make it available ask an administrator to [enable the feature flag](../../../administration/feature_flags.md) named `bulk_import_projects`. On GitLab.com, groups are migrated with all their projects by default. The [`import_export.yml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/import_export/project/import_export.yml) -file for projects lists many of the items migrated when migrating projects using group migration. View this file in the branch +file for projects lists many of the items imported when migrating projects using group migration. View this file in the branch for your version of GitLab to see the list of items relevant to you. For example, [`import_export.yml` on the `14-10-stable-ee` branch](https://gitlab.com/gitlab-org/gitlab/-/blob/14-10-stable-ee/lib/gitlab/import_export/project/import_export.yml). -Migrating projects with file exports uses the same export and import mechanisms as creating projects from templates at the [group](../../group/custom_project_templates.md) and -[instance](../../admin_area/custom_project_templates.md) levels. Therefore, the list of exported items is the same. +Project items that are migrated to the target instance include: - Projects ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/267945) in GitLab 14.4) - Auto DevOps ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/339410) in GitLab 14.6) @@ -183,9 +206,12 @@ Migrating projects with file exports uses the same export and import mechanisms Items excluded from migration, because they contain sensitive information: -- Pipeline Triggers +- Pipeline Triggers. + +Migrating projects with file exports uses the same export and import mechanisms as creating projects from templates at the [group](../../group/custom_project_templates.md) and +[instance](../../admin_area/custom_project_templates.md) levels. Therefore, the list of exported items is the same. -## Troubleshooting Group Migration +### Troubleshooting In a [rails console session](../../../administration/operations/rails_console.md#starting-a-rails-console-session), you can find the failure or error messages for the group import attempt using: @@ -207,7 +233,10 @@ entities.map(&:failures).flatten entities.where(status: [-1]).pluck(:destination_name, :destination_namespace, :status) ``` -### Stale imports +You can also see all migrated entities with any failures related to them using an +[API endpoint](../../../api/bulk_imports.md#list-all-gitlab-migrations-entities). + +#### Stale imports > [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/352985) in GitLab 14.10. @@ -222,16 +251,169 @@ import = BulkImports::Entity.where(namespace_id: Group.id).map(&:bulk_import) import.status #=> 3 means that the import timed out. ``` -### Error: `404 Group Not Found` +### Provide feedback + +Please leave your feedback about migrating groups by direct transfer in +[the feedback issue](https://gitlab.com/gitlab-org/gitlab/-/issues/284495). + +## Migrate groups by uploading an export file (deprecated) -If you attempt to import a group that has a path comprised of only numbers (for example, `5000`), GitLab attempts to find the group by ID instead of the -path. This causes a `404 Group Not Found` error. To solve this, the source group path must be changed to include a non-numerical character using either: +> - [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/2888) in GitLab 13.0 as an experimental feature. May change in future releases. +> - [Deprecated](https://gitlab.com/groups/gitlab-org/-/epics/4619) in GitLab 14.6. -- The GitLab UI: +WARNING: +This feature was [deprecated](https://gitlab.com/groups/gitlab-org/-/epics/4619) in GitLab 14.6 and replaced by +[migrating groups by direct transfer](#migrate-groups-by-direct-transfer-recommended). To follow progress on a solution for +[offline environments](../../application_security/offline_deployments/index.md), see +[the relevant epic](https://gitlab.com/groups/gitlab-org/-/epics/8985). - 1. On the top bar, select **Main menu > Groups** and find your group. - 1. On the left sidebar, select **Settings > General**. - 1. Expand **Advanced**. - 1. Under **Change group URL**, change the group URL to include non-numeric characters. +Prerequisites: -- The [Groups API](../../../api/groups.md#update-group). +- Owner role on the group to migrate. + +Using file exports, you can: + +- Export any group to a file and upload that file to another GitLab instance or to another location on the same instance. +- Use either the GitLab UI or the [API](../../../api/group_import_export.md). +- Migrate groups one by one, then export and import each project for the groups one by one. + +GitLab maps user contributions correctly when an admin access token is used to perform the import. GitLab does not map +user contributions correctly when you are importing from a self-managed instance to GitLab.com. Correct mapping of user +contributions when importing from a self-managed instance to GitLab.com can be preserved with paid involvement of +Professional Services team. + +Note the following: + +- Exports are stored in a temporary directory and are deleted every 24 hours by a specific worker. +- To preserve group-level relationships from imported projects, run the Group Import/Export first, to allow projects to + be imported into the desired group structure. +- Imported groups are given a `private` visibility level, unless imported into a parent group. +- If imported into a parent group, a subgroup inherits the same level of visibility unless otherwise restricted. +- You can export groups from the [Community Edition to the Enterprise Edition](https://about.gitlab.com/install/ce-or-ee/) + and vice versa. The Enterprise Edition retains some group data that isn't part of the Community Edition. If you're + exporting a group from the Enterprise Edition to the Community Edition, you may lose this data. For more information, + see [downgrading from EE to CE](../../../index.md). + +### Exported contents + +The [`import_export.yml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/import_export/group/import_export.yml) +file for groups lists items exported and imported when migrating groups using file exports. View this file in the branch +for your version of GitLab to see the list of items relevant to you. For example, +[`import_export.yml` on the `14-10-stable-ee` branch](https://gitlab.com/gitlab-org/gitlab/-/blob/14-10-stable-ee/lib/gitlab/import_export/group/import_export.yml). + +Group items that are exported include: + +- Milestones +- Labels +- Boards and Board Lists +- Badges +- Subgroups (including all the aforementioned data) +- Epics + - Epic resource state events ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/291983) in GitLab 15.4) +- Events +- [Wikis](../../project/wiki/group.md) + ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/53247) in GitLab 13.9) +- Iterations cadences ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/95372) in 15.4) + +Items that are **not** exported include: + +- Projects +- Runner tokens +- SAML discovery tokens + +### Preparation + +- To preserve the member list and their respective permissions on imported groups, review the users in these groups. Make +sure these users exist before importing the desired groups. +- Users must set a public email in the source GitLab instance that matches one of their verified emails in the target GitLab instance. + +### Enable export for a group + +Prerequisite: + +- You must have the Owner role for the group. + +To enable import and export for a group: + +1. On the top bar, select **Main menu > Admin**. +1. On the left sidebar, select **Settings > General**. +1. Expand **Visibility and access controls**. +1. In the **Import sources** section, select the checkboxes for the sources you want. + +### Export a group + +Prerequisites: + +- You must have the Owner role for the group. + +To export the contents of a group: + +1. On the top bar, select **Main menu > Groups** and find your group. +1. On the left sidebar, select **Settings > General**. +1. In the **Advanced** section, select **Export Group**. +1. After the export is generated, you should receive an email with a link to the [exported contents](#exported-contents) + in a compressed tar archive, with contents in NDJSON format. +1. Alternatively, you can download the export from the UI: + + 1. Return to your group's **Settings > General** page. + 1. In the **Advanced** section, select **Download export**. + You can also generate a new file by selecting **Regenerate export**. + +You can also use the [group import/export API](../../../api/group_import_export.md). + +### Import the group + +1. Create a new group: + - On the top bar, select **Create new…** (**{plus-square}**) and then **New group**. + - On an existing group's page, select the **New subgroup** button. +1. Select **Import group**. +1. Enter your group name. +1. Accept or modify the associated group URL. +1. Select **Choose file**. +1. Select the file that you exported in the [Export a group](#export-a-group) section. +1. To begin importing, select **Import group**. + +Your newly imported group page appears after the operation completes. + +NOTE: +The maximum import file size can be set by the administrator, default is `0` (unlimited). +As an administrator, you can modify the maximum import file size. To do so, use the `max_import_size` option in the +[Application settings API](../../../api/settings.md#change-application-settings) or the +[Admin Area](../../admin_area/settings/account_and_limit_settings.md). +Default [modified](https://gitlab.com/gitlab-org/gitlab/-/issues/251106) from 50MB to 0 in GitLab 13.8. + +### Rate limits + +To help avoid abuse, by default, users are rate limited to: + +| Request Type | Limit | +| ---------------- | ---------------------------------------- | +| Export | 6 groups per minute | +| Download export | 1 download per group per minute | +| Import | 6 groups per minute | + +### Version history + +#### 14.0+ + +In GitLab 14.0, the JSON format is no longer supported for project and group exports. To allow for a +transitional period, you can still import any JSON exports. The new format for imports and exports +is NDJSON. + +#### 13.0+ + +GitLab can import bundles that were exported from a different GitLab deployment. +This ability is limited to two previous GitLab [minor](../../../policy/maintenance.md#versioning) +releases, which is similar to our process for [Security Releases](../../../policy/maintenance.md#security-releases). + +For example: + +| Current version | Can import bundles exported from | +|-----------------|----------------------------------| +| 13.0 | 13.0, 12.10, 12.9 | +| 13.1 | 13.1, 13.0, 12.10 | + +## Automate group and project import **(PREMIUM)** + +For information on automating user, group, and project import API calls, see +[Automate group and project import](../../project/import/index.md#automate-group-and-project-import). diff --git a/doc/user/group/settings/import_export.md b/doc/user/group/settings/import_export.md index cec17688902..ff64a7dcd54 100644 --- a/doc/user/group/settings/import_export.md +++ b/doc/user/group/settings/import_export.md @@ -1,164 +1,11 @@ --- -stage: Manage -group: Import -info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments +redirect_to: '../import/index.md' +remove_date: '2023-03-08' --- -# Migrating groups using file exports (deprecated) **(FREE)** +This document was moved to [another location](../import/index.md). -> - [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/2888) in GitLab 13.0 as an experimental feature. May change in future releases. -> - [Deprecated](https://gitlab.com/groups/gitlab-org/-/epics/4619) in GitLab 14.6. - -WARNING: -This feature was [deprecated](https://gitlab.com/groups/gitlab-org/-/epics/4619) in GitLab 14.6 and replaced by -[a different migration method](../import/index.md). To follow progress on a solution for -[offline environments](../../application_security/offline_deployments/index.md), see -[the relevant issue](https://gitlab.com/gitlab-org/gitlab/-/issues/363406). - -You can export groups, with all their related data, from one GitLab instance to another. You can also: - -- [Migrate groups](../import/index.md) using the preferred method. -- [Migrate projects using file exports](../../project/settings/import_export.md). - -## Enable export for a group - -Prerequisite: - -- You must have the Owner role for the group. - -To enable import and export for a group: - -1. On the top bar, select **Main menu > Admin**. -1. On the left sidebar, select **Settings > General**. -1. Expand **Visibility and access controls**. -1. In the **Import sources** section, select the checkboxes for the sources you want. - -## Important Notes - -Note the following: - -- Exports are stored in a temporary directory and are deleted every 24 hours by a specific worker. -- To preserve group-level relationships from imported projects, run the Group Import/Export first, to allow projects to -be imported into the desired group structure. -- Imported groups are given a `private` visibility level, unless imported into a parent group. -- If imported into a parent group, a subgroup inherits the same level of visibility unless otherwise restricted. -- To preserve the member list and their respective permissions on imported groups, review the users in these groups. Make -sure these users exist before importing the desired groups. -- Users must set a public email in the source GitLab instance that matches one of their verified emails in the target GitLab instance. - -### Exported contents - -The [`import_export.yml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/import_export/group/import_export.yml) -file for groups lists many of the items exported and imported when migrating groups using file exports. View this file in the branch -for your version of GitLab to see the list of items relevant to you. For example, -[`import_export.yml` on the `14-10-stable-ee` branch](https://gitlab.com/gitlab-org/gitlab/-/blob/14-10-stable-ee/lib/gitlab/import_export/group/import_export.yml). - -Migrating projects with file exports uses the same export and import mechanisms as creating projects from templates at the [group](../custom_project_templates.md) and -[instance](../../admin_area/custom_project_templates.md) levels. Therefore, the list of exported items is the same. - -Items that are exported include: - -- Milestones -- Labels -- Boards and Board Lists -- Badges -- Subgroups (including all the aforementioned data) -- Epics - - Epic resource state events ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/291983) in GitLab 15.4) -- Events -- [Wikis](../../project/wiki/group.md) - ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/53247) in GitLab 13.9) -- Iterations cadences ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/95372) in 15.4) - -Items that are **not** exported include: - -- Projects -- Runner tokens -- SAML discovery tokens - -NOTE: -For more details on the specific data persisted in a group export, see the -[`import_export.yml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/import_export/group/import_export.yml) file. - -## Export a group - -Prerequisites: - -- You must have the Owner role for the group. - -To export the contents of a group: - -1. On the top bar, select **Main menu > Groups** and find your group. -1. On the left sidebar, select **Settings > General**. -1. In the **Advanced** section, select **Export Group**. -1. After the export is generated, you should receive an email with a link to the [exported contents](#exported-contents) - in a compressed tar archive, with contents in NDJSON format. -1. Alternatively, you can download the export from the UI: - - 1. Return to your group's **Settings > General** page. - 1. In the **Advanced** section, select **Download export**. - You can also generate a new file by selecting **Regenerate export**. - -NOTE: -The maximum import file size can be set by the Administrator, default is `0` (unlimited). -As an administrator, you can modify the maximum import file size. To do so, use the `max_import_size` option in the [Application settings API](../../../api/settings.md#change-application-settings) or the [Admin Area](../../admin_area/settings/account_and_limit_settings.md). Default [modified](https://gitlab.com/gitlab-org/gitlab/-/issues/251106) from 50MB to 0 in GitLab 13.8. - -You can also use the [group import/export API](../../../api/group_import_export.md). - -### Between CE and EE - -You can export groups from the [Community Edition to the Enterprise Edition](https://about.gitlab.com/install/ce-or-ee/) and vice versa. - -The Enterprise Edition retains some group data that isn't part of the Community Edition. If you're exporting a group from the Enterprise Edition to the Community Edition, you may lose this data. For more information, see [downgrading from EE to CE](../../../index.md). - -## Import the group - -1. Create a new group: - - On the top bar, select **New** (**{plus}**) and then **New group**. - - On an existing group's page, select the **New subgroup** button. -1. Select **Import group**. -1. Enter your group name. -1. Accept or modify the associated group URL. -1. Select **Choose file**. -1. Select the file that you exported in the [Export a group](#export-a-group) section. -1. To begin importing, select **Import group**. - -Your newly imported group page appears after the operation completes. - -## Automate group and project import **(PREMIUM)** - -For information on automating user, group, and project import API calls, see -[Automate group and project import](../../project/import/index.md#automate-group-and-project-import). - -## Version history - -### 14.0+ - -In GitLab 14.0, the JSON format is no longer supported for project and group exports. To allow for a -transitional period, you can still import any JSON exports. The new format for imports and exports -is NDJSON. - -### 13.0+ - -GitLab can import bundles that were exported from a different GitLab deployment. -This ability is limited to two previous GitLab [minor](../../../policy/maintenance.md#versioning) -releases, which is similar to our process for [Security Releases](../../../policy/maintenance.md#security-releases). - -For example: - -| Current version | Can import bundles exported from | -|-----------------|----------------------------------| -| 13.0 | 13.0, 12.10, 12.9 | -| 13.1 | 13.1, 13.0, 12.10 | - -## Rate Limits - -To help avoid abuse, by default, users are rate limited to: - -| Request Type | Limit | -| ---------------- | ---------------------------------------- | -| Export | 6 groups per minute | -| Download export | 1 download per group per minute | -| Import | 6 groups per minute | - -GitLab.com may have [different settings](../../gitlab_com/index.md#importexport) from the defaults. +<!-- This redirect file can be deleted after <2023-03-08>. --> +<!-- Redirects that point to other docs in the same project expire in three months. --> +<!-- Redirects that point to docs in a different project or site (for example, link is not relative and starts with `https:`) expire in one year. --> +<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/redirects.html --> diff --git a/doc/user/project/settings/import_export.md b/doc/user/project/settings/import_export.md index 3c12bb9b80f..803a14e9903 100644 --- a/doc/user/project/settings/import_export.md +++ b/doc/user/project/settings/import_export.md @@ -187,7 +187,7 @@ Imported users can be mapped by their public email addresses on self-managed ins For project migration imports performed over GitLab.com groups, preserving author information is possible through a [professional services engagement](https://about.gitlab.com/services/migration/). -## Rate Limits +## Rate limits To help avoid abuse, by default, users are rate limited to: @@ -197,9 +197,6 @@ To help avoid abuse, by default, users are rate limited to: | Download export | 1 download per group per minute | | Import | 6 projects per minute | -GitLab.com may have [different settings](../../gitlab_com/index.md#importexport) -from the defaults. - ## Version history ### 14.0+ diff --git a/lib/gitlab/bullet.rb b/lib/gitlab/bullet.rb index f5f8a316855..9759a82be0c 100644 --- a/lib/gitlab/bullet.rb +++ b/lib/gitlab/bullet.rb @@ -10,7 +10,7 @@ module Gitlab alias_method :extra_logging_enabled?, :enabled? def configure_bullet? - defined?(::Bullet) && (enabled? || Rails.env.development?) + defined?(::Bullet) && (enabled? || Gitlab.config.bullet.enabled) end end end diff --git a/locale/gitlab.pot b/locale/gitlab.pot index af8e1124052..51f59e4516e 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -8226,6 +8226,9 @@ msgstr "" msgid "Checkout|Country" msgstr "" +msgid "Checkout|Coupon code (optional)" +msgstr "" + msgid "Checkout|Create a new group" msgstr "" @@ -12212,6 +12215,9 @@ msgstr "" msgid "DORA4Metrics|Deploys" msgstr "" +msgid "DORA4Metrics|Failed to load charts" +msgstr "" + msgid "DORA4Metrics|Lead Time for Changes" msgstr "" @@ -12251,6 +12257,9 @@ msgstr "" msgid "DORA4Metrics|Number of incidents divided by the number of deployments to a production environment in the given time period." msgstr "" +msgid "DORA4Metrics|Past 6 Months" +msgstr "" + msgid "DORA4Metrics|Percentage of failed deployments" msgstr "" diff --git a/package.json b/package.json index f1fe4b54d93..fbaec978776 100644 --- a/package.json +++ b/package.json @@ -58,7 +58,7 @@ "@gitlab/svgs": "3.13.0", "@gitlab/ui": "52.0.0", "@gitlab/visual-review-tools": "1.7.3", - "@gitlab/web-ide": "0.0.1-dev-20221208212851", + "@gitlab/web-ide": "0.0.1-dev-20221212205235", "@rails/actioncable": "6.1.4-7", "@rails/ujs": "6.1.4-7", "@sourcegraph/code-host-integration": "0.0.84", diff --git a/qa/qa/service/praefect_manager.rb b/qa/qa/service/praefect_manager.rb index 42841e576f5..57f5310901b 100644 --- a/qa/qa/service/praefect_manager.rb +++ b/qa/qa/service/praefect_manager.rb @@ -9,6 +9,8 @@ module QA attr_accessor :gitlab + attr_reader :primary_node, :secondary_node, :tertiary_node, :postgres + PrometheusQueryError = Class.new(StandardError) def initialize @@ -21,7 +23,9 @@ module QA @virtual_storage = 'default' end - attr_reader :primary_node, :secondary_node, :tertiary_node, :postgres + def gitaly_nodes + [primary_node, secondary_node, tertiary_node] + end # Executes the praefect `dataloss` command. # @@ -50,42 +54,22 @@ module QA end end - def stop_primary_node - stop_node(@primary_node) - wait_until_node_is_removed_from_healthy_storages(@primary_node) - end - - def start_primary_node - start_node(@primary_node) - end - def start_praefect start_node(@praefect) - wait_for_praefect + QA::Runtime::Logger.info("Waiting for health check on praefect") + Support::Waiter.wait_until(max_duration: 120, sleep_interval: 1, raise_on_failure: true) do + wait_until_shell_command("docker exec #{@praefect} gitlab-ctl status praefect") do |line| + break true if line.include?('run: praefect: ') + + QA::Runtime::Logger.debug(line.chomp) + end + end end def stop_praefect stop_node(@praefect) end - def stop_secondary_node - stop_node(@secondary_node) - wait_until_node_is_removed_from_healthy_storages(@secondary_node) - end - - def start_secondary_node - start_node(@secondary_node) - end - - def stop_tertiary_node - stop_node(@tertiary_node) - wait_until_node_is_removed_from_healthy_storages(@tertiary_node) - end - - def start_tertiary_node - start_node(@tertiary_node) - end - def start_node(name) state = node_state(name) return if state == "running" @@ -111,6 +95,8 @@ module QA return if node_state(name) == 'paused' shell "docker pause #{name}" + + wait_until_node_is_removed_from_healthy_storages(name) if gitaly_nodes.include?(name) end def node_state(name) @@ -126,9 +112,9 @@ module QA QA::Runtime::Logger.info("Clearing the replication queue") shell sql_to_docker_exec_cmd( <<~SQL - delete from replication_queue_job_lock; - delete from replication_queue_lock; - delete from replication_queue; + delete from replication_queue_job_lock; + delete from replication_queue_lock; + delete from replication_queue; SQL ) end @@ -137,32 +123,16 @@ module QA QA::Runtime::Logger.info("Setting jobs in replication queue to `in_progress` and acquiring locks") shell sql_to_docker_exec_cmd( <<~SQL - update replication_queue set state = 'in_progress'; - insert into replication_queue_job_lock (job_id, lock_id, triggered_at) - select id, rq.lock_id, created_at from replication_queue rq - left join replication_queue_job_lock rqjl on rq.id = rqjl.job_id - where state = 'in_progress' and rqjl.job_id is null; - update replication_queue_lock set acquired = 't'; + update replication_queue set state = 'in_progress'; + insert into replication_queue_job_lock (job_id, lock_id, triggered_at) + select id, rq.lock_id, created_at from replication_queue rq + left join replication_queue_job_lock rqjl on rq.id = rqjl.job_id + where state = 'in_progress' and rqjl.job_id is null; + update replication_queue_lock set acquired = 't'; SQL ) end - # Reconciles the previous primary node with the current one - # I.e., it brings the previous primary node up-to-date - def reconcile_nodes - reconcile_node_with_node(@primary_node, current_primary_node) - end - - def reconcile_node_with_node(target, reference) - QA::Runtime::Logger.info("Reconcile #{target} with #{reference} on #{@virtual_storage}") - wait_until_shell_command_matches( - "docker exec #{@praefect} bash -c '/opt/gitlab/embedded/bin/praefect -config /var/opt/gitlab/praefect/config.toml reconcile -virtual #{@virtual_storage} -target #{target} -reference #{reference} -f'", - /FINISHED: \d+ repos were checked for consistency/, - sleep_interval: 5, - retry_on_exception: true - ) - end - def query_read_distribution cmd = "docker exec #{@gitlab} bash -c 'curl -s http://localhost:9090/api/v1/query?query=gitaly_praefect_read_distribution'" output = shell(cmd, stream_progress: false) do |line| @@ -204,9 +174,7 @@ module QA def start_all_nodes start_postgres - start_node(@primary_node) - start_node(@secondary_node) - start_node(@tertiary_node) + gitaly_nodes.each { |node| start_node(node) } start_praefect wait_for_health_check_all_nodes @@ -230,17 +198,6 @@ module QA destination_storage[:type] == :praefect ? verify_storage_move_to_praefect(repo_path, destination_storage[:name]) : verify_storage_move_to_gitaly(repo_path, destination_storage[:name]) end - def wait_for_praefect - QA::Runtime::Logger.info("Waiting for health check on praefect") - Support::Waiter.wait_until(max_duration: 120, sleep_interval: 1, raise_on_failure: true) do - wait_until_shell_command("docker exec #{@praefect} gitlab-ctl status praefect") do |line| - break true if line.include?('run: praefect: ') - - QA::Runtime::Logger.debug(line.chomp) - end - end - end - def praefect_sql_ping_healthy? cmd = "docker exec #{@praefect} bash -c '/opt/gitlab/embedded/bin/praefect -config /var/opt/gitlab/praefect/config.toml sql-ping'" wait_until_shell_command(cmd) do |line| @@ -249,17 +206,6 @@ module QA end end - def wait_for_sql_ping - wait_until_shell_command_matches( - "docker exec #{@praefect} bash -c '/opt/gitlab/embedded/bin/praefect -config /var/opt/gitlab/praefect/config.toml sql-ping'", - /praefect sql-ping: OK/ - ) - end - - def health_check_failure_message?(msg) - ['error when pinging healthcheck', 'failed checking node health'].include?(msg) - end - def wait_for_dial_nodes_successful Support::Waiter.repeat_until(max_attempts: 3, max_duration: 120, sleep_interval: 1) do nodes_confirmed = { @@ -316,14 +262,6 @@ module QA dataloss_info end - def praefect_dataloss_info_for_project(project_id) - dataloss_info = [] - Support::Retrier.retry_until(max_duration: 60) do - dataloss_info = praefect_dataloss_information(project_id) - dataloss_info.include?("#{Digest::SHA256.hexdigest(project_id.to_s)}.git") - end - end - def wait_for_project_synced_across_all_storages(project_id) Support::Retrier.retry_until(max_duration: 60) do praefect_dataloss_information(project_id).include?('All repositories are fully available on all assigned storages!') @@ -347,9 +285,7 @@ module QA end def wait_for_health_check_all_nodes - wait_for_gitaly_health_check(@primary_node) - wait_for_gitaly_health_check(@secondary_node) - wait_for_gitaly_health_check(@tertiary_node) + gitaly_nodes.each { |node| wait_for_gitaly_health_check(node) } end def wait_for_gitaly_health_check(node) @@ -364,35 +300,11 @@ module QA wait_until_node_is_marked_as_healthy_storage(node) end - def wait_for_primary_node_health_check - wait_for_gitaly_health_check(@primary_node) - end - - def wait_for_secondary_node_health_check - wait_for_gitaly_health_check(@secondary_node) - end - - def wait_for_tertiary_node_health_check - wait_for_gitaly_health_check(@tertiary_node) - end - def wait_for_health_check_failure(node) QA::Runtime::Logger.info("Waiting for health check failure on #{node}") wait_until_node_is_removed_from_healthy_storages(node) end - def wait_for_primary_node_health_check_failure - wait_for_health_check_failure(@primary_node) - end - - def wait_for_secondary_node_health_check_failure - wait_for_health_check_failure(@secondary_node) - end - - def wait_for_tertiary_node_health_check_failure - wait_for_health_check_failure(@tertiary_node) - end - def wait_until_node_is_removed_from_healthy_storages(node) Support::Waiter.wait_until(max_duration: 120, sleep_interval: 1, raise_on_failure: true) do result = [] @@ -459,10 +371,10 @@ module QA result = [] shell sql_to_docker_exec_cmd( <<~SQL - select job from replication_queue - where state = 'ready' - and job ->> 'change' = 'update' - and job ->> 'target_node_storage' = '#{@primary_node}'; + select job from replication_queue + where state = 'ready' + and job ->> 'change' = 'update' + and job ->> 'target_node_storage' = '#{@primary_node}'; SQL ) do |line| result << line @@ -601,20 +513,6 @@ module QA private - def current_primary_node - result = [] - shell sql_to_docker_exec_cmd("select node_name from shard_primaries where shard_name = '#{@virtual_storage}';") do |line| - result << line - end - # The result looks like: - # node_name - # ----------- - # gitaly1 - # (1 row) - - result[2].strip - end - def dataloss_command "docker exec #{@praefect} bash -c '/opt/gitlab/embedded/bin/praefect -config /var/opt/gitlab/praefect/config.toml dataloss'" end @@ -657,13 +555,6 @@ module QA end end - def with_praefect_log(**kwargs) - wait_until_shell_command("docker exec #{@praefect} bash -c 'tail -n 1 /var/log/gitlab/praefect/current'", **kwargs) do |line| - QA::Runtime::Logger.debug(line.chomp) - yield JSON.parse(line) - end - end - def repo_type(repo_path) return :snippet if repo_path.start_with?('@snippets') return :design if repo_path.end_with?('.design.git') diff --git a/qa/qa/specs/features/api/12_systems/gitaly/automatic_failover_and_recovery_spec.rb b/qa/qa/specs/features/api/12_systems/gitaly/automatic_failover_and_recovery_spec.rb index 8bbef4ae429..9ffca8d54c9 100644 --- a/qa/qa/specs/features/api/12_systems/gitaly/automatic_failover_and_recovery_spec.rb +++ b/qa/qa/specs/features/api/12_systems/gitaly/automatic_failover_and_recovery_spec.rb @@ -25,8 +25,8 @@ module QA it 'automatically fails over', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347830' do # stop other nodes, so we can control which node the commit is sent to - praefect_manager.stop_secondary_node - praefect_manager.stop_tertiary_node + praefect_manager.stop_node(praefect_manager.secondary_node) + praefect_manager.stop_node(praefect_manager.tertiary_node) Resource::Repository::ProjectPush.fabricate! do |push| push.project = project @@ -40,8 +40,8 @@ module QA # Stop the primary node to trigger failover, and then wait # for Gitaly to be ready for writes again - praefect_manager.stop_primary_node - praefect_manager.wait_for_primary_node_health_check_failure + praefect_manager.stop_node(praefect_manager.primary_node) + praefect_manager.wait_for_health_check_failure(praefect_manager.primary_node) Resource::Repository::Commit.fabricate_via_api! do |commit| commit.project = project @@ -65,8 +65,8 @@ module QA context 'when recovering from dataloss after failover' do it 'automatically reconciles', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347831' do # Start the old primary node again - praefect_manager.start_primary_node - praefect_manager.wait_for_primary_node_health_check + praefect_manager.start_node(praefect_manager.primary_node) + praefect_manager.wait_for_gitaly_health_check(praefect_manager.primary_node) # Confirm automatic reconciliation expect(praefect_manager.replicated?(project.id)).to be true diff --git a/qa/qa/specs/features/api/12_systems/gitaly/backend_node_recovery_spec.rb b/qa/qa/specs/features/api/12_systems/gitaly/backend_node_recovery_spec.rb index 1abc7b8a912..022f51205f0 100644 --- a/qa/qa/specs/features/api/12_systems/gitaly/backend_node_recovery_spec.rb +++ b/qa/qa/specs/features/api/12_systems/gitaly/backend_node_recovery_spec.rb @@ -27,8 +27,8 @@ module QA # Stop the primary node to trigger failover, and then wait # for Gitaly to be ready for writes again - praefect_manager.stop_primary_node - praefect_manager.wait_for_primary_node_health_check_failure + praefect_manager.stop_node(praefect_manager.primary_node) + praefect_manager.wait_for_health_check_failure(praefect_manager.primary_node) # Push a commit to the new primary Resource::Repository::ProjectPush.fabricate! do |push| @@ -43,7 +43,7 @@ module QA expect(praefect_manager).to be_replication_pending # Start the old primary node again - praefect_manager.start_primary_node + praefect_manager.start_node(praefect_manager.primary_node) praefect_manager.wait_for_health_check_all_nodes # Wait for automatic replication @@ -51,8 +51,8 @@ module QA # Force switch to the old primary node # This ensures that the commit was replicated - praefect_manager.stop_secondary_node - praefect_manager.stop_tertiary_node + praefect_manager.stop_node(praefect_manager.secondary_node) + praefect_manager.stop_node(praefect_manager.tertiary_node) # Confirm that both commits are available expect(project.commits.map { |commit| commit[:message].chomp }) diff --git a/qa/qa/specs/features/api/12_systems/gitaly/distributed_reads_spec.rb b/qa/qa/specs/features/api/12_systems/gitaly/distributed_reads_spec.rb index 403fe468d69..60ce2a65fd1 100644 --- a/qa/qa/specs/features/api/12_systems/gitaly/distributed_reads_spec.rb +++ b/qa/qa/specs/features/api/12_systems/gitaly/distributed_reads_spec.rb @@ -36,12 +36,12 @@ module QA context 'when a node is unhealthy' do before do - praefect_manager.stop_secondary_node + praefect_manager.stop_node(praefect_manager.secondary_node) end after do # Leave the cluster in a suitable state for subsequent tests - praefect_manager.start_secondary_node + praefect_manager.start_node(praefect_manager.secondary_node) end it 'does not read from the unhealthy node', diff --git a/qa/qa/specs/features/api/12_systems/gitaly/praefect_dataloss_spec.rb b/qa/qa/specs/features/api/12_systems/gitaly/praefect_dataloss_spec.rb index 6ba192a9dd6..cf387c14006 100644 --- a/qa/qa/specs/features/api/12_systems/gitaly/praefect_dataloss_spec.rb +++ b/qa/qa/specs/features/api/12_systems/gitaly/praefect_dataloss_spec.rb @@ -29,7 +29,7 @@ module QA praefect_manager.wait_for_project_synced_across_all_storages(project.id) # testing for gitaly2 'out of sync' - praefect_manager.stop_secondary_node + praefect_manager.stop_node(praefect_manager.secondary_node) number_of_changes = 3 1.upto(number_of_changes) do |i| @@ -47,7 +47,7 @@ module QA end # testing for gitaly3 'in sync' but marked unhealthy - praefect_manager.stop_tertiary_node + praefect_manager.stop_node(praefect_manager.tertiary_node) project_data_loss = praefect_manager.praefect_dataloss_information(project.id) aggregate_failures "validate dataloss identified" do @@ -74,7 +74,7 @@ module QA end praefect_manager.wait_for_replication_to_node(project.id, praefect_manager.primary_node) - praefect_manager.stop_primary_node + praefect_manager.stop_node(praefect_manager.primary_node) Resource::Repository::Commit.fabricate_via_api! do |commit| commit.project = project commit.commit_message = 'accept-dataloss-2' @@ -86,7 +86,7 @@ module QA end praefect_manager.wait_for_replication_to_node(project.id, praefect_manager.secondary_node) - praefect_manager.stop_secondary_node + praefect_manager.stop_node(praefect_manager.secondary_node) Resource::Repository::Commit.fabricate_via_api! do |commit| commit.project = project commit.commit_message = 'accept-dataloss-3' diff --git a/qa/qa/specs/features/api/12_systems/gitaly/praefect_replication_queue_spec.rb b/qa/qa/specs/features/api/12_systems/gitaly/praefect_replication_queue_spec.rb index 94bae38c5c8..f88372c0b59 100644 --- a/qa/qa/specs/features/api/12_systems/gitaly/praefect_replication_queue_spec.rb +++ b/qa/qa/specs/features/api/12_systems/gitaly/praefect_replication_queue_spec.rb @@ -35,7 +35,7 @@ module QA # During normal operations we avoid create a replication event # https://gitlab.com/groups/gitlab-org/-/epics/7741 - praefect_manager.stop_secondary_node + praefect_manager.stop_node(praefect_manager.secondary_node) Git::Repository.perform do |repository| repository.uri = project.repository_http_location.uri repository.use_default_credentials @@ -47,7 +47,7 @@ module QA end repository.push_all_branches end - praefect_manager.start_secondary_node + praefect_manager.start_node(praefect_manager.secondary_node) Support::Retrier.retry_until(max_duration: 60) do count = praefect_manager.replication_queue_lock_count diff --git a/qa/qa/support/run.rb b/qa/qa/support/run.rb index 90fc6733105..da82c09462d 100644 --- a/qa/qa/support/run.rb +++ b/qa/qa/support/run.rb @@ -45,3 +45,5 @@ module QA end end end + +QA::Support::Run.prepend_mod_with("Support::Run", namespace: QA) diff --git a/qa/qa/support/ssh.rb b/qa/qa/support/ssh.rb index 1b53244d1e4..eebe5e65504 100644 --- a/qa/qa/support/ssh.rb +++ b/qa/qa/support/ssh.rb @@ -70,3 +70,5 @@ module QA end end end + +QA::Support::SSH.prepend_mod_with("Support::SSH", namespace: QA) diff --git a/spec/fixtures/api/schemas/branch.json b/spec/fixtures/api/schemas/branch.json index 0bb74577010..02389a1b979 100644 --- a/spec/fixtures/api/schemas/branch.json +++ b/spec/fixtures/api/schemas/branch.json @@ -1,12 +1,17 @@ { "type": "object", - "required" : [ + "required": [ "name", "url" ], - "properties" : { - "name": { "type": "string" }, - "url": { "type": "uri" } + "properties": { + "name": { + "type": "string" + }, + "url": { + "type": "string", + "format": "uri" + } }, "additionalProperties": false -} +}
\ No newline at end of file diff --git a/spec/fixtures/api/schemas/entities/discussion.json b/spec/fixtures/api/schemas/entities/discussion.json index da2d2a83a8d..45271926547 100644 --- a/spec/fixtures/api/schemas/entities/discussion.json +++ b/spec/fixtures/api/schemas/entities/discussion.json @@ -1,34 +1,75 @@ { "type": "object", - "required" : [ + "required": [ "id", "notes", "individual_note" ], - "properties" : { - "id": { "type": "string" }, - "individual_note": { "type": "boolean" }, + "properties": { + "id": { + "type": "string" + }, + "individual_note": { + "type": "boolean" + }, "notes": { "type": "array", "items": { "type": "object", - "properties" : { - "id": { "type": "string" }, - "type": { "type": ["string", "null"] }, - "body": { "type": "string" }, - "attachment": { "type": ["string", "null"]}, - "award_emoji": { "type": "array" }, + "properties": { + "id": { + "type": "string" + }, + "type": { + "type": [ + "string", + "null" + ] + }, + "body": { + "type": "string" + }, + "attachment": { + "type": [ + "string", + "null" + ] + }, + "award_emoji": { + "type": "array" + }, "author": { "type": "object", "properties": { - "name": { "type": "string" }, - "username": { "type": "string" }, - "id": { "type": "integer" }, - "state": { "type": "string" }, - "avatar_url": { "type": "uri" }, - "web_url": { "type": "uri" }, - "status_tooltip_html": { "type": ["string", "null"] }, - "path": { "type": "string" } + "name": { + "type": "string" + }, + "username": { + "type": "string" + }, + "id": { + "type": "integer" + }, + "state": { + "type": "string" + }, + "avatar_url": { + "type": "string", + "format": "uri" + }, + "web_url": { + "type": "string", + "format": "uri" + }, + "status_tooltip_html": { + "type": [ + "string", + "null" + ] + }, + "path": { + "type": "string" + } }, "required": [ "id", @@ -39,43 +80,131 @@ "username" ] }, - "created_at": { "type": "string", "format": "date-time" }, - "updated_at": { "type": "string", "format": "date-time" }, - "system": { "type": "boolean" }, - "noteable_id": { "type": "integer" }, - "noteable_iid": { "type": ["integer", "null"] }, - "noteable_type": { "type": "string" }, - "resolved": { "type": "boolean" }, - "resolvable": { "type": "boolean" }, - "resolved_by": { "type": ["string", "null"] }, - "resolved_at": { "type": ["string", "null"], "format": "date-time" }, - "note": { "type": "string" }, - "note_html": { "type": "string" }, - "current_user": { "type": "object" }, - "suggestions": { "type": "array" }, - "discussion_id": { "type": "string" }, - "emoji_awardable": { "type": "boolean" }, - "report_abuse_path": { "type": "string" }, - "noteable_note_url": { "type": "string" }, - "resolve_path": { "type": "string" }, - "resolve_with_issue_path": { "type": "string" }, - "cached_markdown_version": { "type": "integer" }, - "human_access": { "type": ["string", "null"] }, - "is_noteable_author": { "type": "boolean" }, - "is_contributor": { "type": "boolean" }, - "project_name": { "type": "string" }, - "toggle_award_path": { "type": "string" }, - "path": { "type": "string" }, - "commands_changes": { "type": "object", "additionalProperties": true }, - "confidential": { "type": ["boolean", "null"] }, - "internal": { "type": ["boolean", "null"] } + "created_at": { + "type": "string", + "format": "date-time" + }, + "updated_at": { + "type": "string", + "format": "date-time" + }, + "system": { + "type": "boolean" + }, + "noteable_id": { + "type": "integer" + }, + "noteable_iid": { + "type": [ + "integer", + "null" + ] + }, + "noteable_type": { + "type": "string" + }, + "resolved": { + "type": "boolean" + }, + "resolvable": { + "type": "boolean" + }, + "resolved_by": { + "type": [ + "string", + "null" + ] + }, + "resolved_at": { + "type": [ + "string", + "null" + ], + "format": "date-time" + }, + "note": { + "type": "string" + }, + "note_html": { + "type": "string" + }, + "current_user": { + "type": "object" + }, + "suggestions": { + "type": "array" + }, + "discussion_id": { + "type": "string" + }, + "emoji_awardable": { + "type": "boolean" + }, + "report_abuse_path": { + "type": "string" + }, + "noteable_note_url": { + "type": "string" + }, + "resolve_path": { + "type": "string" + }, + "resolve_with_issue_path": { + "type": "string" + }, + "cached_markdown_version": { + "type": "integer" + }, + "human_access": { + "type": [ + "string", + "null" + ] + }, + "is_noteable_author": { + "type": "boolean" + }, + "is_contributor": { + "type": "boolean" + }, + "project_name": { + "type": "string" + }, + "toggle_award_path": { + "type": "string" + }, + "path": { + "type": "string" + }, + "commands_changes": { + "type": "object", + "additionalProperties": true + }, + "confidential": { + "type": [ + "boolean", + "null" + ] + }, + "internal": { + "type": [ + "boolean", + "null" + ] + } }, "required": [ - "id", "attachment", "author", "created_at", "updated_at", - "system", "noteable_id", "noteable_type" + "id", + "attachment", + "author", + "created_at", + "updated_at", + "system", + "noteable_id", + "noteable_type" ], "additionalProperties": false } } } -} +}
\ No newline at end of file diff --git a/spec/fixtures/api/schemas/entities/issue.json b/spec/fixtures/api/schemas/entities/issue.json index b4a076780d9..08937b5e68b 100644 --- a/spec/fixtures/api/schemas/entities/issue.json +++ b/spec/fixtures/api/schemas/entities/issue.json @@ -1,43 +1,142 @@ { "type": "object", - "properties" : { - "id": { "type": "integer" }, - "iid": { "type": "integer" }, - "type": { "type": "string" }, - "author_id": { "type": "integer" }, - "description": { "type": ["string", "null"] }, - "lock_version": { "type": ["integer", "null"] }, - "milestone_id": { "type": ["string", "null"] }, - "title": { "type": "string" }, - "moved_to_id": { "type": ["integer", "null"] }, - "project_id": { "type": "integer" }, - "web_url": { "type": "string" }, - "state": { "type": "string" }, - "create_note_path": { "type": "string" }, - "preview_note_path": { "type": "string" }, + "properties": { + "id": { + "type": "integer" + }, + "iid": { + "type": "integer" + }, + "type": { + "type": "string" + }, + "author_id": { + "type": "integer" + }, + "description": { + "type": [ + "string", + "null" + ] + }, + "lock_version": { + "type": [ + "integer", + "null" + ] + }, + "milestone_id": { + "type": [ + "string", + "null" + ] + }, + "title": { + "type": "string" + }, + "moved_to_id": { + "type": [ + "integer", + "null" + ] + }, + "project_id": { + "type": "integer" + }, + "web_url": { + "type": "string" + }, + "state": { + "type": "string" + }, + "create_note_path": { + "type": "string" + }, + "preview_note_path": { + "type": "string" + }, "current_user": { "type": "object", "properties": { - "can_create_note": { "type": "boolean" }, - "can_update": { "type": "boolean" } + "can_create_note": { + "type": "boolean" + }, + "can_update": { + "type": "boolean" + } } }, - "created_at": { "type": "date-time" }, - "updated_at": { "type": "date-time" }, - "branch_name": { "type": ["string", "null"] }, - "due_date": { "type": ["string", "null"], "format": "date-time" }, - "confidential": { "type": "boolean" }, - "discussion_locked": { "type": ["boolean", "null"] }, - "updated_by_id": { "type": ["integer", "null"] }, - "time_estimate": { "type": "integer" }, - "total_time_spent": { "type": "integer" }, - "human_time_estimate": { "type": ["integer", "null"] }, - "human_total_time_spent": { "type": ["integer", "null"] }, - "milestone": { "type": ["object", "null"] }, + "created_at": { + "type": "string", + "format": "date-time" + }, + "updated_at": { + "type": "string", + "format": "date-time" + }, + "branch_name": { + "type": [ + "string", + "null" + ] + }, + "due_date": { + "type": [ + "string", + "null" + ], + "format": "date-time" + }, + "confidential": { + "type": "boolean" + }, + "discussion_locked": { + "type": [ + "boolean", + "null" + ] + }, + "updated_by_id": { + "type": [ + "integer", + "null" + ] + }, + "time_estimate": { + "type": "integer" + }, + "total_time_spent": { + "type": "integer" + }, + "human_time_estimate": { + "type": [ + "integer", + "null" + ] + }, + "human_total_time_spent": { + "type": [ + "integer", + "null" + ] + }, + "milestone": { + "type": [ + "object", + "null" + ] + }, "labels": { "type": "array", - "items": { "$ref": "label.json" } + "items": { + "$ref": "label.json" + } }, - "assignees": { "type": ["array", "null"] } + "assignees": { + "type": [ + "array", + "null" + ] + } } -} +}
\ No newline at end of file diff --git a/spec/fixtures/api/schemas/entities/member.json b/spec/fixtures/api/schemas/entities/member.json index 24a4863df9b..cd8a4e0519b 100644 --- a/spec/fixtures/api/schemas/entities/member.json +++ b/spec/fixtures/api/schemas/entities/member.json @@ -14,59 +14,130 @@ "is_direct_member" ], "properties": { - "id": { "type": "integer" }, - "created_at": { "type": "date-time" }, - "expires_at": { "type": ["date-time", "null"] }, - "requested_at": { "type": ["date-time", "null"] }, - "can_update": { "type": "boolean" }, - "can_remove": { "type": "boolean" }, - "is_direct_member": { "type": "boolean" }, + "id": { + "type": "integer" + }, + "created_at": { + "type": "string", + "format": "date-time" + }, + "expires_at": { + "type": [ + "string", + "null" + ], + "format": "date-time" + }, + "requested_at": { + "type": [ + "string", + "null" + ], + "format": "date-time" + }, + "can_update": { + "type": "boolean" + }, + "can_remove": { + "type": "boolean" + }, + "is_direct_member": { + "type": "boolean" + }, "access_level": { "type": "object", - "required": ["integer_value", "string_value"], + "required": [ + "integer_value", + "string_value" + ], "properties": { - "integer_value": { "type": "integer" }, - "string_value": { "type": "string" } + "integer_value": { + "type": "integer" + }, + "string_value": { + "type": "string" + } }, "additionalProperties": false }, "source": { "type": "object", - "required": ["id", "full_name", "web_url"], + "required": [ + "id", + "full_name", + "web_url" + ], "properties": { - "id": { "type": "integer" }, - "full_name": { "type": "string" }, - "web_url": { "type": "string" } + "id": { + "type": "integer" + }, + "full_name": { + "type": "string" + }, + "web_url": { + "type": "string" + } }, "additionalProperties": false }, - "valid_roles": { "type": "object" }, - "type": { "type": "string" }, + "valid_roles": { + "type": "object" + }, + "type": { + "type": "string" + }, "created_by": { "type": "object", - "required": ["name", "web_url"], + "required": [ + "name", + "web_url" + ], "properties": { - "name": { "type": "string" }, - "web_url": { "type": "string" } + "name": { + "type": "string" + }, + "web_url": { + "type": "string" + } }, "additionalProperties": false }, "user": { "allOf": [ - { "$ref": "member_user_default.json" } + { + "$ref": "member_user_default.json" + } ] }, - "state": { "type": "integer" }, + "state": { + "type": "integer" + }, "invite": { "type": "object", - "required": ["email", "avatar_url", "can_resend", "user_state"], + "required": [ + "email", + "avatar_url", + "can_resend", + "user_state" + ], "properties": { - "email": { "type": "string" }, - "avatar_url": { "type": [ "string", "null" ] }, - "can_resend": { "type": "boolean" }, - "user_state": { "type": "string" } + "email": { + "type": "string" + }, + "avatar_url": { + "type": [ + "string", + "null" + ] + }, + "can_resend": { + "type": "boolean" + }, + "user_state": { + "type": "string" + } }, "additionalProperties": false } } -} +}
\ No newline at end of file diff --git a/spec/fixtures/api/schemas/entities/merge_request_metrics.json b/spec/fixtures/api/schemas/entities/merge_request_metrics.json index 3fa767f85df..591c5919b19 100644 --- a/spec/fixtures/api/schemas/entities/merge_request_metrics.json +++ b/spec/fixtures/api/schemas/entities/merge_request_metrics.json @@ -1,21 +1,46 @@ { "type": "object", - "required": ["closed_at", "merged_at", "closed_by", "merged_by"], - "properties" : { - "closed_at": { "type": ["datetime", "null"] }, - "merged_at": { "type": ["datetime", "null"] }, + "required": [ + "closed_at", + "merged_at", + "closed_by", + "merged_by" + ], + "properties": { + "closed_at": { + "type": [ + "string", + "null" + ], + "format": "date-time" + }, + "merged_at": { + "type": [ + "string", + "null" + ], + "format": "date-time" + }, "closed_by": { "oneOf": [ - { "type": "null" }, - { "$ref": "user.json" } + { + "type": "null" + }, + { + "$ref": "user.json" + } ] }, "merged_by": { "oneOf": [ - { "type": "null" }, - { "$ref": "user.json" } + { + "type": "null" + }, + { + "$ref": "user.json" + } ] } }, "additionalProperties": false -} +}
\ No newline at end of file diff --git a/spec/fixtures/api/schemas/entities/merge_request_poll_widget.json b/spec/fixtures/api/schemas/entities/merge_request_poll_widget.json index be2fe19b067..d7e5a80df61 100644 --- a/spec/fixtures/api/schemas/entities/merge_request_poll_widget.json +++ b/spec/fixtures/api/schemas/entities/merge_request_poll_widget.json @@ -1,38 +1,131 @@ { "type": "object", - "properties" : { - "id": { "type": "integer" }, - "iid": { "type": "integer" }, - "description": { "type": ["string", "null"] }, - "title": { "type": "string" }, - "auto_merge_strategy": { "type": ["string", "null"] }, - "available_auto_merge_strategies": { "type": "array" }, - "source_branch_protected": { "type": "boolean" }, - "allow_collaboration": { "type": "boolean"}, - "should_be_rebased": { "type": "boolean" }, - "ff_only_enabled": { "type": ["boolean", false] }, - "merge_user": { "type": ["object", "null"] }, - "pipeline": { "type": ["object", "null"] }, - "merge_pipeline": { "type": ["object", "null"] }, - "default_merge_commit_message": { "type": ["string", "null"] }, - "mergeable": { "type": "boolean" }, - "default_merge_commit_message_with_description": { "type": "string" }, - "mergeable_discussions_state": { "type": "boolean" }, - "project_archived": { "type": "boolean" }, - "only_allow_merge_if_pipeline_succeeds": { "type": "boolean" }, - "has_ci": { "type": "boolean" }, - "ci_status": { "type": ["string", "null"] }, - "pipeline_coverage_delta": { "type": ["float", "null"] }, + "properties": { + "id": { + "type": "integer" + }, + "iid": { + "type": "integer" + }, + "description": { + "type": [ + "string", + "null" + ] + }, + "title": { + "type": "string" + }, + "auto_merge_strategy": { + "type": [ + "string", + "null" + ] + }, + "available_auto_merge_strategies": { + "type": "array" + }, + "source_branch_protected": { + "type": "boolean" + }, + "allow_collaboration": { + "type": "boolean" + }, + "should_be_rebased": { + "type": "boolean" + }, + "ff_only_enabled": { + "type": [ + "boolean", + false + ] + }, + "merge_user": { + "type": [ + "object", + "null" + ] + }, + "pipeline": { + "type": [ + "object", + "null" + ] + }, + "merge_pipeline": { + "type": [ + "object", + "null" + ] + }, + "default_merge_commit_message": { + "type": [ + "string", + "null" + ] + }, + "mergeable": { + "type": "boolean" + }, + "default_merge_commit_message_with_description": { + "type": "string" + }, + "mergeable_discussions_state": { + "type": "boolean" + }, + "project_archived": { + "type": "boolean" + }, + "only_allow_merge_if_pipeline_succeeds": { + "type": "boolean" + }, + "has_ci": { + "type": "boolean" + }, + "ci_status": { + "type": [ + "string", + "null" + ] + }, + "pipeline_coverage_delta": { + "type": [ + "string", + "null" + ], + "format": "float" + }, "builds_with_coverage": { - "type": ["array", "null"], + "type": [ + "array", + "null" + ], "items": { "type": "object", - "required": ["name", "coverage"] + "required": [ + "name", + "coverage" + ] } }, - "cancel_auto_merge_path": { "type": ["string", "null"] }, - "test_reports_path": { "type": ["string", "null"] }, - "create_issue_to_resolve_discussions_path": { "type": ["string", "null"] }, + "cancel_auto_merge_path": { + "type": [ + "string", + "null" + ] + }, + "test_reports_path": { + "type": [ + "string", + "null" + ] + }, + "create_issue_to_resolve_discussions_path": { + "type": [ + "string", + "null" + ] + }, "current_user": { "type": "object", "required": [ @@ -42,20 +135,71 @@ "can_create_issue" ], "properties": { - "can_remove_source_branch": { "type": "boolean" }, - "can_revert_on_current_merge_request": { "type": ["boolean", "null"] }, - "can_cherry_pick_on_current_merge_request": { "type": ["boolean", "null"] }, - "can_create_issue": { "type": "boolean" } + "can_remove_source_branch": { + "type": "boolean" + }, + "can_revert_on_current_merge_request": { + "type": [ + "boolean", + "null" + ] + }, + "can_cherry_pick_on_current_merge_request": { + "type": [ + "boolean", + "null" + ] + }, + "can_create_issue": { + "type": "boolean" + } }, "additionalProperties": false }, - "can_push_to_source_branch": { "type": "boolean" }, - "new_blob_path": { "type": ["string", "null"] }, - "rebase_path": { "type": ["string", "null"] }, - "conflict_resolution_path": { "type": ["string", "null"] }, - "remove_wip_path": { "type": ["string", "null"] }, - "merge_path": { "type": ["string", "null"] }, - "cherry_pick_in_fork_path": { "type": ["string", "null"] }, - "revert_in_fork_path": { "type": ["string", "null"] } + "can_push_to_source_branch": { + "type": "boolean" + }, + "new_blob_path": { + "type": [ + "string", + "null" + ] + }, + "rebase_path": { + "type": [ + "string", + "null" + ] + }, + "conflict_resolution_path": { + "type": [ + "string", + "null" + ] + }, + "remove_wip_path": { + "type": [ + "string", + "null" + ] + }, + "merge_path": { + "type": [ + "string", + "null" + ] + }, + "cherry_pick_in_fork_path": { + "type": [ + "string", + "null" + ] + }, + "revert_in_fork_path": { + "type": [ + "string", + "null" + ] + } } -} +}
\ No newline at end of file diff --git a/spec/fixtures/api/schemas/entities/test_case.json b/spec/fixtures/api/schemas/entities/test_case.json index 483fa881f8c..660d40c4a9f 100644 --- a/spec/fixtures/api/schemas/entities/test_case.json +++ b/spec/fixtures/api/schemas/entities/test_case.json @@ -1,24 +1,57 @@ { "type": "object", - "required" : [ + "required": [ "status", "name" ], "properties": { - "status": { "type": "string" }, - "name": { "type": "string" }, - "classname": { "type": "string" }, - "file": { "type": ["string", "null"] }, - "execution_time": { "type": "float" }, - "system_output": { "type": ["string", "null"] }, - "stack_trace": { "type": ["string", "null"] }, - "attachment_url": { "type": ["string", "null"] }, + "status": { + "type": "string" + }, + "name": { + "type": "string" + }, + "classname": { + "type": "string" + }, + "file": { + "type": [ + "string", + "null" + ] + }, + "execution_time": { + "type": "number", + "format": "float" + }, + "system_output": { + "type": [ + "string", + "null" + ] + }, + "stack_trace": { + "type": [ + "string", + "null" + ] + }, + "attachment_url": { + "type": [ + "string", + "null" + ] + }, "recent_failures": { "oneOf": [ - { "type": "null" }, - { "$ref": "test_case/recent_failures.json" } + { + "type": "null" + }, + { + "$ref": "test_case/recent_failures.json" + } ] } }, "additionalProperties": false -} +}
\ No newline at end of file diff --git a/spec/fixtures/api/schemas/entities/trigger.json b/spec/fixtures/api/schemas/entities/trigger.json index 5c46142673f..c6ccd84dcdd 100644 --- a/spec/fixtures/api/schemas/entities/trigger.json +++ b/spec/fixtures/api/schemas/entities/trigger.json @@ -10,14 +10,21 @@ ], "properties": { "description": { - "type": ["string", "null"] + "type": [ + "string", + "null" + ] }, "owner": { "type": "object", "$ref": "user.json" }, "last_used": { - "type": ["datetime", "null"] + "type": [ + "string", + "null" + ], + "format": "date-time" }, "token": { "type": "string" @@ -36,4 +43,4 @@ } }, "additionalProperties": false -} +}
\ No newline at end of file diff --git a/spec/fixtures/api/schemas/issue.json b/spec/fixtures/api/schemas/issue.json index aefba89d9e2..ac8c05eb377 100644 --- a/spec/fixtures/api/schemas/issue.json +++ b/spec/fixtures/api/schemas/issue.json @@ -1,43 +1,104 @@ { "type": "object", - "required" : [ + "required": [ "iid", "title", "confidential" ], - "properties" : { - "id": { "type": "integer" }, - "iid": { "type": "integer" }, - "project_id": { "type": ["integer", "null"] }, - "title": { "type": "string" }, - "confidential": { "type": "boolean" }, - "due_date": { "type": ["string", "null"] }, - "relative_position": { "type": ["integer", "null"] }, - "time_estimate": { "type": "integer" }, - "type": { "type": "string", "enum": ["ISSUE", "INCIDENT", "TEST_CASE", "REQUIREMENT"] }, - "issue_sidebar_endpoint": { "type": "string" }, - "toggle_subscription_endpoint": { "type": "string" }, - "assignable_labels_endpoint": { "type": "string" }, - "reference_path": { "type": "string" }, - "real_path": { "type": "string" }, + "properties": { + "id": { + "type": "integer" + }, + "iid": { + "type": "integer" + }, + "project_id": { + "type": [ + "integer", + "null" + ] + }, + "title": { + "type": "string" + }, + "confidential": { + "type": "boolean" + }, + "due_date": { + "type": [ + "string", + "null" + ] + }, + "relative_position": { + "type": [ + "integer", + "null" + ] + }, + "time_estimate": { + "type": "integer" + }, + "type": { + "type": "string", + "enum": [ + "ISSUE", + "INCIDENT", + "TEST_CASE", + "REQUIREMENT" + ] + }, + "issue_sidebar_endpoint": { + "type": "string" + }, + "toggle_subscription_endpoint": { + "type": "string" + }, + "assignable_labels_endpoint": { + "type": "string" + }, + "reference_path": { + "type": "string" + }, + "real_path": { + "type": "string" + }, "project": { - "id": { "type": "integer" }, - "path": { "type": "string" } + "id": { + "type": "integer" + }, + "path": { + "type": "string" + } }, "labels": { "type": "array", - "items": { "$ref": "entities/label.json" } + "items": { + "$ref": "entities/label.json" + } }, "assignee": { - "id": { "type": "integer" }, - "name": { "type": "string" }, - "username": { "type": "string" }, - "avatar_url": { "type": "uri" } + "id": { + "type": "integer" + }, + "name": { + "type": "string" + }, + "username": { + "type": "string" + }, + "avatar_url": { + "type": "string", + "format": "uri" + } }, "assignees": { "type": "array", "items": { - "type": ["object", "null"], + "type": [ + "object", + "null" + ], "required": [ "id", "name", @@ -45,13 +106,27 @@ "avatar_url" ], "properties": { - "id": { "type": "integer" }, - "name": { "type": "string" }, - "username": { "type": "string" }, - "avatar_url": { "type": "uri" } + "id": { + "type": "integer" + }, + "name": { + "type": "string" + }, + "username": { + "type": "string" + }, + "avatar_url": { + "type": "string", + "format": "uri" + } } } }, - "subscribed": { "type": ["boolean", "null"] } + "subscribed": { + "type": [ + "boolean", + "null" + ] + } } -} +}
\ No newline at end of file diff --git a/spec/fixtures/api/schemas/merge_request.json b/spec/fixtures/api/schemas/merge_request.json index 36962660cd9..416391e454f 100644 --- a/spec/fixtures/api/schemas/merge_request.json +++ b/spec/fixtures/api/schemas/merge_request.json @@ -1,12 +1,17 @@ { "type": "object", - "required" : [ + "required": [ "iid", "url" ], - "properties" : { - "iid": { "type": "integer" }, - "url": { "type": "uri" } + "properties": { + "iid": { + "type": "integer" + }, + "url": { + "type": "string", + "format": "uri" + } }, "additionalProperties": false -} +}
\ No newline at end of file diff --git a/spec/fixtures/api/schemas/pipeline_schedule.json b/spec/fixtures/api/schemas/pipeline_schedule.json index ef5942b7eb3..ade5a76d934 100644 --- a/spec/fixtures/api/schemas/pipeline_schedule.json +++ b/spec/fixtures/api/schemas/pipeline_schedule.json @@ -1,53 +1,142 @@ { "type": "object", - "properties" : { - "id": { "type": "integer" }, - "description": { "type": "string" }, - "ref": { "type": "string" }, - "cron": { "type": "string" }, - "cron_timezone": { "type": "string" }, - "next_run_at": { "type": "string" }, - "active": { "type": "boolean" }, - "created_at": { "type": ["string", "null"], "format": "date-time" }, - "updated_at": { "type": ["string", "null"], "format": "date-time" }, + "properties": { + "id": { + "type": "integer" + }, + "description": { + "type": "string" + }, + "ref": { + "type": "string" + }, + "cron": { + "type": "string" + }, + "cron_timezone": { + "type": "string" + }, + "next_run_at": { + "type": "string" + }, + "active": { + "type": "boolean" + }, + "created_at": { + "type": [ + "string", + "null" + ], + "format": "date-time" + }, + "updated_at": { + "type": [ + "string", + "null" + ], + "format": "date-time" + }, "last_pipeline": { - "type": ["object", "null"], + "type": [ + "object", + "null" + ], "properties": { - "id": { "type": "integer" }, - "iid": { "type": "integer" }, - "project_id": { "type": "integer" }, - "sha": { "type": "string" }, - "ref": { "type": "string" }, - "status": { "type": "string" }, - "source": { "type": "string" }, - "web_url": { "type": ["string", "null"] }, - "created_at": { "type": ["string", "null"], "format": "date-time" }, - "updated_at": { "type": ["string", "null"], "format": "date-time" } + "id": { + "type": "integer" + }, + "iid": { + "type": "integer" + }, + "project_id": { + "type": "integer" + }, + "sha": { + "type": "string" + }, + "ref": { + "type": "string" + }, + "status": { + "type": "string" + }, + "source": { + "type": "string" + }, + "web_url": { + "type": [ + "string", + "null" + ] + }, + "created_at": { + "type": [ + "string", + "null" + ], + "format": "date-time" + }, + "updated_at": { + "type": [ + "string", + "null" + ], + "format": "date-time" + } }, "additionalProperties": false }, "owner": { "type": "object", "properties": { - "name": { "type": "string" }, - "username": { "type": "string" }, - "id": { "type": "integer" }, - "state": { "type": "string" }, - "avatar_url": { "type": "uri" }, - "web_url": { "type": "uri" } + "name": { + "type": "string" + }, + "username": { + "type": "string" + }, + "id": { + "type": "integer" + }, + "state": { + "type": "string" + }, + "avatar_url": { + "type": "string", + "format": "uri" + }, + "web_url": { + "type": "string", + "format": "uri" + } }, "required": [ - "id", "name", "username", "state", "avatar_url", "web_url" + "id", + "name", + "username", + "state", + "avatar_url", + "web_url" ] }, "variables": { "type": "array", - "items": { "$ref": "pipeline_schedule_variable.json" } + "items": { + "$ref": "pipeline_schedule_variable.json" + } } }, "required": [ - "id", "description", "ref", "cron", "cron_timezone", "next_run_at", - "active", "created_at", "updated_at", "owner" + "id", + "description", + "ref", + "cron", + "cron_timezone", + "next_run_at", + "active", + "created_at", + "updated_at", + "owner" ], "additionalProperties": false -} +}
\ No newline at end of file diff --git a/spec/fixtures/api/schemas/release.json b/spec/fixtures/api/schemas/release.json index fe4f8cd2157..11f238ffc92 100644 --- a/spec/fixtures/api/schemas/release.json +++ b/spec/fixtures/api/schemas/release.json @@ -1,22 +1,56 @@ { "type": "object", - "required": ["tag_name", "description"], + "required": [ + "tag_name", + "description" + ], "properties": { - "name": { "type": "string" }, - "tag_name": { "type": "string" }, - "ref": { "type": "string "}, - "description": { "type": "string" }, - "description_html": { "type": "string" }, - "created_at": { "type": "string", "format": "date-time" }, + "name": { + "type": "string" + }, + "tag_name": { + "type": "string" + }, + "ref": { + "type": "string" + }, + "description": { + "type": "string" + }, + "description_html": { + "type": "string" + }, + "created_at": { + "type": "string", + "format": "date-time" + }, "commit": { - "oneOf": [{ "type": "null" }, { "$ref": "public_api/v4/commit/basic.json" }] + "oneOf": [ + { + "type": "null" + }, + { + "$ref": "public_api/v4/commit/basic.json" + } + ] }, "author": { - "oneOf": [{ "type": "null" }, { "$ref": "public_api/v4/user/basic.json" }] + "oneOf": [ + { + "type": "null" + }, + { + "$ref": "public_api/v4/user/basic.json" + } + ] }, "assets": { - "count": { "type": "integer" }, - "links": { "$ref": "release/links.json" }, + "count": { + "type": "integer" + }, + "links": { + "$ref": "release/links.json" + }, "sources": { "type": "array", "items": { @@ -27,4 +61,4 @@ } }, "additionalProperties": false -} +}
\ No newline at end of file diff --git a/spec/fixtures/ce_sample_schema.json b/spec/fixtures/ce_sample_schema.json index e69de29bb2d..9e26dfeeb6e 100644 --- a/spec/fixtures/ce_sample_schema.json +++ b/spec/fixtures/ce_sample_schema.json @@ -0,0 +1 @@ +{}
\ No newline at end of file diff --git a/spec/frontend/issues/show/components/incidents/timeline_events_item_spec.js b/spec/frontend/issues/show/components/incidents/timeline_events_item_spec.js index 1bf8d68efd4..ba0527e5395 100644 --- a/spec/frontend/issues/show/components/incidents/timeline_events_item_spec.js +++ b/spec/frontend/issues/show/components/incidents/timeline_events_item_spec.js @@ -1,5 +1,5 @@ import timezoneMock from 'timezone-mock'; -import { GlIcon, GlDropdown } from '@gitlab/ui'; +import { GlIcon, GlDropdown, GlBadge } from '@gitlab/ui'; import { nextTick } from 'vue'; import { timelineItemI18n } from '~/issues/show/components/incidents/constants'; import { mountExtended } from 'helpers/vue_test_utils_helper'; @@ -27,25 +27,24 @@ describe('IncidentTimelineEventList', () => { const findCommentIcon = () => wrapper.findComponent(GlIcon); const findEventTime = () => wrapper.findByTestId('event-time'); + const findEventTag = () => wrapper.findComponent(GlBadge); const findDropdown = () => wrapper.findComponent(GlDropdown); const findDeleteButton = () => wrapper.findByText(timelineItemI18n.delete); describe('template', () => { - it('shows comment icon', () => { + beforeEach(() => { mountComponent(); + }); + it('shows comment icon', () => { expect(findCommentIcon().exists()).toBe(true); }); it('sets correct props for icon', () => { - mountComponent(); - expect(findCommentIcon().props('name')).toBe(mockEvents[0].action); }); it('displays the correct time', () => { - mountComponent(); - expect(findEventTime().text()).toBe('15:59 UTC'); }); @@ -58,8 +57,6 @@ describe('IncidentTimelineEventList', () => { describe(timezone, () => { beforeEach(() => { timezoneMock.register(timezone); - - mountComponent(); }); afterEach(() => { @@ -72,10 +69,20 @@ describe('IncidentTimelineEventList', () => { }); }); + describe('timeline event tag', () => { + it('does not show when tag is not provided', () => { + expect(findEventTag().exists()).toBe(false); + }); + + it('shows when tag is provided', () => { + mountComponent({ propsData: { eventTag: 'Start time' } }); + + expect(findEventTag().exists()).toBe(true); + }); + }); + describe('action dropdown', () => { it('does not show the action dropdown by default', () => { - mountComponent(); - expect(findDropdown().exists()).toBe(false); expect(findDeleteButton().exists()).toBe(false); }); diff --git a/spec/lib/gitlab/bullet_spec.rb b/spec/lib/gitlab/bullet_spec.rb index 1262a0b8bde..c575c656bb4 100644 --- a/spec/lib/gitlab/bullet_spec.rb +++ b/spec/lib/gitlab/bullet_spec.rb @@ -3,48 +3,55 @@ require 'spec_helper' RSpec.describe Gitlab::Bullet do - describe '#enabled?' do - it 'is enabled' do - stub_env('ENABLE_BULLET', true) - - expect(described_class.enabled?).to be(true) - end - - it 'is not enabled' do + context 'with bullet installed' do + before do stub_env('ENABLE_BULLET', nil) - - expect(described_class.enabled?).to be(false) + stub_const('::Bullet', double) end - it 'is correctly aliased for #extra_logging_enabled?' do - expect(described_class.method(:extra_logging_enabled?).original_name).to eq(:enabled?) - end - end + describe '#enabled?' do + context 'with env enabled' do + before do + stub_env('ENABLE_BULLET', true) + allow(Gitlab.config.bullet).to receive(:enabled).and_return(false) + end - describe '#configure_bullet?' do - context 'with ENABLE_BULLET true' do - before do - stub_env('ENABLE_BULLET', true) + it 'is enabled' do + expect(described_class.enabled?).to be(true) + end end - it 'is configurable' do - expect(described_class.configure_bullet?).to be(true) + context 'with env disabled' do + before do + stub_env('ENABLE_BULLET', false) + allow(Gitlab.config.bullet).to receive(:enabled).and_return(true) + end + + it 'is not enabled' do + expect(described_class.enabled?).to be(false) + end end end - context 'with ENABLE_BULLET falsey' do - before do - stub_env('ENABLE_BULLET', nil) - end + describe '#configure_bullet?' do + context 'with config enabled' do + before do + allow(Gitlab.config.bullet).to receive(:enabled).and_return(true) + end - it 'is not configurable' do - expect(described_class.configure_bullet?).to be(false) + it 'is configurable' do + expect(described_class.configure_bullet?).to be(true) + end end - it 'is configurable in development' do - allow(Rails).to receive_message_chain(:env, :development?).and_return(true) + context 'with config disabled' do + before do + allow(Gitlab.config.bullet).to receive(:enabled).and_return(false) + end - expect(described_class.configure_bullet?).to be(true) + it 'is not configurable' do + expect(described_class.configure_bullet?).to be(false) + end end end end diff --git a/spec/requests/ide_controller_spec.rb b/spec/requests/ide_controller_spec.rb index b7c57ca11f3..b287ded799d 100644 --- a/spec/requests/ide_controller_spec.rb +++ b/spec/requests/ide_controller_spec.rb @@ -290,20 +290,5 @@ RSpec.describe IdeController, feature_category: :web_ide do expect(find_csp_frame_src).to include("https://*.vscode-cdn.net/") end end - - describe 'when vscode_web_ide feature flag is disabled' do - describe 'frame-src content security policy' do - let(:route) { '/-/ide' } - - before do - stub_feature_flags(vscode_web_ide: false) - subject - end - - it 'does not add https://*.vscode-cdn.net in frame-src CSP policy' do - expect(find_csp_frame_src).not_to include("https://*.vscode-cdn.net/") - end - end - end end end diff --git a/spec/requests/web_ide/remote_ide_controller_spec.rb b/spec/requests/web_ide/remote_ide_controller_spec.rb index ee8a89200fa..367c7527f10 100644 --- a/spec/requests/web_ide/remote_ide_controller_spec.rb +++ b/spec/requests/web_ide/remote_ide_controller_spec.rb @@ -101,12 +101,6 @@ RSpec.describe WebIde::RemoteIdeController, feature_category: :remote_developmen let(:ff_vscode_web_ide) { false } it_behaves_like '404 response' - - it 'does not the content security policy with the correct frame sources' do - post_to_remote_ide - - expect(find_csp_source('frame-src')).not_to include("https://*.vscode-cdn.net/") - end end context "when the remote host is invalid" do diff --git a/spec/serializers/merge_request_poll_cached_widget_entity_spec.rb b/spec/serializers/merge_request_poll_cached_widget_entity_spec.rb index 702c6d9fe98..f883156628a 100644 --- a/spec/serializers/merge_request_poll_cached_widget_entity_spec.rb +++ b/spec/serializers/merge_request_poll_cached_widget_entity_spec.rb @@ -152,10 +152,10 @@ RSpec.describe MergeRequestPollCachedWidgetEntity do .to eq(closed_event.author_id) expect(subject.dig(:metrics, :merged_at).to_s) - .to eq(merge_event.updated_at.to_s) + .to eq(merge_event.updated_at.iso8601) expect(subject.dig(:metrics, :closed_at).to_s) - .to eq(closed_event.updated_at.to_s) + .to eq(closed_event.updated_at.iso8601) end end diff --git a/workhorse/go.mod b/workhorse/go.mod index 05e1e52a605..80c017ad1cb 100644 --- a/workhorse/go.mod +++ b/workhorse/go.mod @@ -26,7 +26,7 @@ require ( github.com/sirupsen/logrus v1.9.0 github.com/smartystreets/goconvey v1.7.2 github.com/stretchr/testify v1.8.1 - gitlab.com/gitlab-org/gitaly/v15 v15.6.1 + gitlab.com/gitlab-org/gitaly/v15 v15.6.2 gitlab.com/gitlab-org/golang-archive-zip v0.1.1 gitlab.com/gitlab-org/labkit v1.16.1 gocloud.dev v0.27.0 diff --git a/workhorse/go.sum b/workhorse/go.sum index 65c3f6485eb..5e095f5b417 100644 --- a/workhorse/go.sum +++ b/workhorse/go.sum @@ -1486,8 +1486,8 @@ github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43/go.mod h1:aX github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50/go.mod h1:NUSPSUX/bi6SeDMUh6brw0nXpxHnc96TguQh0+r/ssA= github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f/go.mod h1:GlGEuHIJweS1mbCqG+7vt2nvWLzLLnRHbXz5JKd/Qbg= github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q= -gitlab.com/gitlab-org/gitaly/v15 v15.6.1 h1:Rb1vmtNAitbZ85Cog49vUfcDrU5jWB8BG09lXZmC2sM= -gitlab.com/gitlab-org/gitaly/v15 v15.6.1/go.mod h1:RKa+3ADKfTonDb1pe8AtppdNHNeOM+ChtMmB7T0QWhY= +gitlab.com/gitlab-org/gitaly/v15 v15.6.2 h1:ivbMoXWgkDSJebuIFtPYGAIQ9/2P5ShxJoHt0cflwfo= +gitlab.com/gitlab-org/gitaly/v15 v15.6.2/go.mod h1:RKa+3ADKfTonDb1pe8AtppdNHNeOM+ChtMmB7T0QWhY= gitlab.com/gitlab-org/golang-archive-zip v0.1.1 h1:35k9giivbxwF03+8A05Cm8YoxoakU8FBCj5gysjCTCE= gitlab.com/gitlab-org/golang-archive-zip v0.1.1/go.mod h1:ZDtqpWPGPB9qBuZnZDrKQjIdJtkN7ZAoVwhT6H2o2kE= gitlab.com/gitlab-org/labkit v1.16.1 h1:J+HmNVR5bvPfrv9/fgKICFis2nmEugRXHMeRPvsVZUg= diff --git a/yarn.lock b/yarn.lock index dc500fb2d5c..46102aa75dd 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1155,10 +1155,10 @@ resolved "https://registry.yarnpkg.com/@gitlab/visual-review-tools/-/visual-review-tools-1.7.3.tgz#9ea641146436da388ffbad25d7f2abe0df52c235" integrity sha512-NMV++7Ew1FSBDN1xiZaauU9tfeSfgDHcOLpn+8bGpP+O5orUPm2Eu66R5eC5gkjBPaXosNAxNWtriee+aFk4+g== -"@gitlab/web-ide@0.0.1-dev-20221208212851": - version "0.0.1-dev-20221208212851" - resolved "https://registry.yarnpkg.com/@gitlab/web-ide/-/web-ide-0.0.1-dev-20221208212851.tgz#3de0a6186c2f5f3048978de266ee623b0dcc31af" - integrity sha512-BpSVFo2uu48h0Zn3OyTuMtsrUvkPFHwYu4YWeyMU1/QX5P7bBUGY9WOshF+m/3oci61jCS8EVXhr2fhh/lfvew== +"@gitlab/web-ide@0.0.1-dev-20221212205235": + version "0.0.1-dev-20221212205235" + resolved "https://registry.yarnpkg.com/@gitlab/web-ide/-/web-ide-0.0.1-dev-20221212205235.tgz#c35ada9c72080df5e92a6899cc334f66d673249c" + integrity sha512-F2Lod0oeRVdlgotqmTyRY6SyhaaPftbN82K94gymxOJt//HJQ2mQIQSwhCaWD9TgFdQVkYClh+VUtHQI/SoqwA== "@graphql-eslint/eslint-plugin@3.12.0": version "3.12.0" |